diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ffbe9ae6..cd4e0185 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -32,7 +32,6 @@ string(REPLACE "#PROJECT_PATH#" "${NICKEL_ENGINE_RELATIVE_PATH}" PROJECT_PATH_CO file(WRITE ${CMAKE_SOURCE_DIR}/nickel_engine_project_path.toml ${PROJECT_PATH_CONTENT}) - ## build add_subdirectory(engine) diff --git a/engine/cmake/compile_shader.cmake b/engine/cmake/compile_shader.cmake index 7cb4bfe6..ffd01d33 100644 --- a/engine/cmake/compile_shader.cmake +++ b/engine/cmake/compile_shader.cmake @@ -1,14 +1,15 @@ -macro(compile_shader shader_name output_name) +macro(compile_shader shader_files) if (GLSLC_PROG) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${output_name} - COMMAND ${GLSLC_PROG} ${CMAKE_CURRENT_SOURCE_DIR}/${shader_name} -o ${CMAKE_CURRENT_SOURCE_DIR}/${output_name} - COMMENT "compiling shader ${CMAKE_CURRENT_SOURCE_DIR}/${shader_name} -> ${output_name}" - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${shader_name} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM - ) - else() - message(WARN "don't find glslc, can't compiling shader ${CMAKE_CURRENT_SOURCE_DIR}/${shader_name}") + foreach(file ${shader_files}) + set(SHADER_COMPILED_FILE ${file}.spv) + add_custom_command( + OUTPUT ${SHADER_COMPILED_FILE} + COMMAND ${GLSLC_PROG} ${file} -o ${SHADER_COMPILED_FILE} + COMMENT "compiling shader ${file} -> ${SHADER_COMPILED_FILE}" + MAIN_DEPENDENCY ${file} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + ) + endforeach() endif() endmacro() \ No newline at end of file diff --git a/engine/cmake/target_category_helper.cmake b/engine/cmake/target_category_helper.cmake index 20e127ad..f89780b2 100644 --- a/engine/cmake/target_category_helper.cmake +++ b/engine/cmake/target_category_helper.cmake @@ -39,3 +39,9 @@ macro(mark_as_tool target_name) target_link_libraries(${target_name} PRIVATE ${NICKEL_MAIN_ENTRY_NAME}) endmacro() +macro(mark_as_tool_without_engine target_name) + set_target_properties(${target_name} PROPERTIES + FOLDER tools + VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +endmacro() + diff --git a/engine/doc/Doxyfile.inc b/engine/doc/Doxyfile.inc index cc5a86e9..64483c94 100644 --- a/engine/doc/Doxyfile.inc +++ b/engine/doc/Doxyfile.inc @@ -991,7 +991,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../engine/code_generator/parser.py ../engine/nickel ../tests/render ../tests/script @CMAKE_CURRENT_SOURCE_DIR@ +INPUT = ../engine/code_generator/parser.py ../engine/nickel ../tests/render ../tests/script ../tools/shader_compiler ../tools/vehicle_editor @CMAKE_CURRENT_SOURCE_DIR@ # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/engine/doc/engine_arch.md b/engine/doc/engine_arch.md index 28101b33..65f959b1 100644 --- a/engine/doc/engine_arch.md +++ b/engine/doc/engine_arch.md @@ -6,4 +6,5 @@ ## Engine Tools: -- @ref code_generate_page \ No newline at end of file +- @ref code_generate_page +- @ref shader_compiler_page \ No newline at end of file diff --git a/engine/engine/3rdlibs/CMakeLists.txt b/engine/engine/3rdlibs/CMakeLists.txt index fad3fec7..3702d60b 100644 --- a/engine/engine/3rdlibs/CMakeLists.txt +++ b/engine/engine/3rdlibs/CMakeLists.txt @@ -50,4 +50,11 @@ add_3rdlib(implot) set(QJS_BUILD_COMPILER ON) set(QJS_BUILD_REPL ON) set(QJS_BUILD_LIBC ON) -add_3rdlib(quickjs) \ No newline at end of file +add_3rdlib(quickjs) + +set(SPRIV_SKIP_TESTS ON) +set(SPRIV_SKIP_EXECUTABLES ON) +add_3rdlib(spirv-tools) + +set(BUILD_SHARED_LIBS OFF) +add_3rdlib(glslang) \ No newline at end of file diff --git a/engine/engine/3rdlibs/glslang/CHANGES.md b/engine/engine/3rdlibs/glslang/CHANGES.md new file mode 100644 index 00000000..59adb568 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/CHANGES.md @@ -0,0 +1,373 @@ +# Revision history for `glslang` + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](https://semver.org/). + +## 15.3.0 2024-04-24 +* Fix CMake find_package on Windows when building shared libraries +* Fix crash calling coopMatLoadTensorNV on an array element +* Avoid reusing array types with stride when no stride is requested +* Fix non-ASCII comments +* Support GL_EXT_bfloat16 +* Fix several issues with link-time implicit array size merging +* Support DebugSourceContinued +* LiveTraverser no longer traverses switch cases that are statically not accessed +* Implement optional cross-stage check for inputs with no matching outputs + +## 15.2.0 2024-02-24 +* Fix find_package on Windows when BUILD_SHARED_LIBS=ON +* Emit error if using in/out with struct pointer +* Emit SPV_EXT_opacity_micromap if GL extension is present +* Support GL_NV_linear_swept_spheres +* Support GLSL_EXT_nontemporal_keyword +* Support GL_NV_cluster_acceleration_structure +* Support GL_NV_cooperative_vector +* Check SparseTextureOffset non-const parameters +* Support GL_EXT_texture_offset_non_const +* Revert cross-stage check for missing outputs +* Support EXT_integer_dot_product +* Add support for OpTypeRayQueryKHR and OpTypeAccelerationStructureKHR to SPVRemapper + +## 15.1.0 2024-12-13 +* Add Vulkan 1.4 target and client +* Improve conversion of uniform block to push constant +* Improve cross stage error reporting by reporting proper stager rather than "unkwown stage" +* Add warning if forward declaration uses layout qualifiers +* Implement GLSL_NV_cooperative_matrix2 +* Emit OpModfStruct instead of depracated OpModf +* Add link-time cross stage optimization +* Add column to DebugLexicalBlock +* Propagate errors from symbol table initialization +* Fix nonsemantic debuginfo line attribution for cooperative matrix + +## 15.0.0 2024-09-23 +### Breaking changes +* Explicitly export all symbols that are part of the public API and hide other symbols by default + +### Other changes +* Allow building glslang without the SPIR-V backend using the new ENABLE_SPIRV build option +* Add setResourceSetBinding method to the API +* Add interface to get the GLSL IO mapper and resolver +* Allow compute derivative modes when the workgroup dimensions are spec constants +* Improve debug location of branch/return instructions +* Silence preprocessor '#' error reporting in inactive #if/#ifdef/#elif/#else blocks +* Apply GLSL memory decorations to top-level OpVariable +* Move definition of GLSLANG_EXPORT to visibility.h +* Merge ancillary libraries into main glslang library and stub originals +* Add public setSourceFile and addSourceText methods to TShader class +* Add type checks for hitObjectNV +* Add optimizerAllowExpandedIDBound to SpvOptions +* Add SpvTools.h back to public headers +* Add cross-stage check for missing outputs +* Fix HLSL offsets for non-buffers +* Add types and functions for IO mapping to API +* Add function to set preprocessed code to API +* Add set/get version functions to API +* Expose setGlobalUniform functions to API +* Don't emit debug instructions before an OpPhi +* Add command-line and API option to enable reporting column location for compiler errors +* Improve location aliasing checks +* Support constant expression calculated by matrixCompMult +* Fix crash caused by atomicCounter() use without arguments +* Fix multi-line function call line numbers +* Add line info to OpDebugDeclare for function parameters +* Fix HLSL OpDebugFunction file name +* Fix duplicate decorations +* Enable compilation of glslang without thread support for WASI + +## 14.3.0 2024-06-25 +* Generate vector constructions more efficiently when sizes match +* Skip identity conversions for 8-bit and 16-bit types +* Add cmake aliases for public libraries +* Support ARM extended matrix layout +* Emit debug info for buffer references +* Add support for OpExtInstWithForwardRefsKHR +* Generate SPV_EXT_replicated_compisites when requested by pragma +* Reuse loads generated for repeated function arguments +* Fix gl_HitT alias of gl_RayTmax +* Fix some cases where invalid SPIR-V was being generated when using separate samplers +* Add back layoutLocation to public API + +## 14.2.0 2024-05-02 +* Improve checking for location aliasing errors +* Fix undefined behavior in parser +* Add bounds check to gl_SampleMask +* Fix alignment and padding of matrices consuming one vector +* Remove duplicate SPIR-V decorations +* Check for exponent overflow in float parser +* Fix bug in relaxed verification rules +* Fix disassembly of debugBreak +* Fix bug when importing SPIR-V extended intruction set +* Fix issues with the interaction of cooperative_matrix and spirv_intrinsics +* Support SPV_QCOM_image_processing2 +* Support files with UTF8BOM character + +## 14.1.0 2024-03-08 +* Add a new --abosute-path command-line option to output absolute paths in error messages +* Support GL_EXT_control_flow_attributes2 +* Support GL_ARB_shading_language_include +* Fix HLSL built-in passthrough via inout +* Enable -Wimplicit-fallthrough and fix warnings +* Fix -Wmissing_field_initializer warnings +* Document supported dependencies in known_good.json +* Clear spirv vector before use +* Emit debug info for accelerationStructure and rayQuery variables +* Support NV_shader_atomic_fp16_vector +* Support GL_EXT_expect_assume_support +* Allow external control of whether glslang will be tested or installed +* Improve debug source and line info +* Support GL_KHR_shader_subgroup_rotate +* Add SPIRV-Tools-opt dependency if ENABLE_OPT +* Support EXT_shader_quad_control +* Add OpAssumeTrueKHR and OpExpectKHR +* Support GL_EXT_maximal_reconvergence +* Remove generation of deprecated Target.cmake files +* Fix array size of gl_SampleMask and gl_SampleMaskIn +* Support GL_ARB_texture_multisample_extension +* Emit DebugTypePointer when non-semantic debug info is enabled + +## 14.0.0 2023-12-21 + +### Breaking changes +* The legacy libraries named HLSL and OGLCompiler have been removed. To avoid future disruptions, please use cmake's find_package mechanism rather than hardcoding library dependencies. +* Only the headers that are part of glslang's public interface are included in the install target. +* Remove OVERRIDE_MSVCCRT cmake option. + +### Other changes +* Fix spv_options initialization +* Fix line number for OpDebugFunction and OpDebugScope for function +* Fix SPV_KHR_cooperative_matrix enumerants +* Fix nullptr crash +* Fix GL_ARB_shader_storage_buffer_object version +* Fix interpolant ES error +* Generate DebugValue for constant arguments +* Overflow/underflow out-of-range floats to infinity/0.0 respectively +* Support SV_ViewID keywords for HLSL +* Implement relaxed rule for opaque struct members +* Add BUILD_WERROR cmake option +* Add GLSLANG_TESTS cmake option +* Always generate OpDebugBasicType for bool type +* Fix GLSL parsing of '#' when not preceded by space or tab +* Fix GL_ARB_bindless_texture availability +* Support GL_EXT_draw_instanced extension +* Support GL_EXT_texture_array extension +* Fix conversion of 64-bit unsigned integer constants to bool +* Output 8-bit and 16-bit capabilities when appropriate for OpSpecConstant + +## 13.1.1 2023-10-16 +* Initialize compile_only field in C interface + +## 13.1.0 2023-10-13 +* Support GL_EXT_texture_shadow_lod +* Support GL_NV_displacement_micromap +* Fix ByteAddressBuffer when used a function parameter +* Add more verbose messages if SPIRV-Tools is not found +* Fix names for explicitly sized types when emitting nonsemantic debug info +* Emit error for r-value arguments in atomic memory operations +* Add --no-link option +* Beautify preprocessor output format +* Fix race condition in glslangValidator +* Only set LocalSizeId mode when necessary +* Don't emit invalid debug info for buffer references + +## 13.0.0 2023-08-23 + +### Breaking changes +* Simplify PoolAlloc via thread_local + * Remove InitializeDLL functions + * Remove OSDependent TLS functions +* Remove GLSLANG_WEB and GLSLANG_WEB_DEVEL code paths + +### Other changes +* Raise CMAKE minimum to 3.17.2 +* Support GL_KHR_cooperative_matrix +* Support GL_QCOM_image_processing_support +* Support outputting each module to a filename with spirv-remap +* Generate an error when gl_PrimitiveShaderRateEXT is used without enabling the extension +* Improve layout checking when GL_EXT_spirv_intrinsics is enabled + +## 12.3.1 2023-07-20 + +### Other changes +* Improve backward compatibility for glslangValidator rename on Windows + +## 12.3.0 2023-07-19 + +### Other changes +* Rename glslangValidator to glslang and create glslangValidator symlink +* Support HLSL binary literals +* Add missing initialization members for web +* Improve push_constant upgrading +* Fix race condition in spirv remap +* Support pre and post HLSL qualifier validation +* Force generateDebugInfo when non-semantic debug info is enabled +* Exit with error if output file cannot be written +* Fix struct member buffer reference decorations + +## 12.2.0 2023-05-17 + +### Other changes +* Support GLSL_EXT_shader_tile_image +* Support GL_EXT_ray_tracing_position_fetch +* Support custom include callbacks via the C API +* Add preamble-text command-line option +* Accept variables as parameters of spirv_decorate_id +* Fix generation of conditionals with a struct result +* Fix double expansion of macros +* Fix DebugCompilationUnit scope +* Improve line information + +## 12.1.0 2023-03-21 + +### Other changes +* Reject non-float inputs/outputs for version less than 120 +* Fix invalid BufferBlock decoration for SPIR-V 1.3 and above +* Add HLSL relaxed-precision float/int matrix expansions +* Block decorate Vulkan structs with RuntimeArrays +* Support InterlockedAdd on float types + +## 12.0.0 2023-01-18 + +### Breaking changes +* An ABI was accidentally broken in #3014. Consequently, we have incremented the major revision number. + +### Other changes +* Add support for ARB_bindless_texture. +* Add support for GL_NV_shader_invocation_reorder. +* Fix const parameter debug types when using NonSemantic.Shader.DebugInfo.100. +* Fix NonSemantic.Shader.DebugInfo.100 disassembly. +* Fix MaxDualSourceDrawBuffersEXT usage. +* Fix structure member reference crash. + +## 11.13.0 2022-12-06 + +### Other changes +* Make HelperInvocation accesses volatile for SPIR-V 1.6. +* Improve forward compatibility of ResourceLimits interface +* Remove GLSLANG_ANGLE + +## 11.12.0 2022-10-12 + +### Other changes +* Update generator version +* Add support for GL_EXT_mesh_shader +* Add support for NonSemantic.Shader.DebugInfo.100 +* Make OpEmitMeshTasksEXT a terminal instruction +* Make gl_SubGroupARB a flat in int in Vulkan +* Add support for GL_EXT_opacity_micromap +* Add preamble support to C interface + +## 11.11.0 2022-08-11 + +### Other changes +* Add OpSource support to C interface +* Deprecate samplerBuffer for spirv1.6 and later +* Add support for SPV_AMD_shader_early_and_late_fragment_tests + +## 11.10.0 2022-06-02 + +### Other changes +* Generate OpLine before OpFunction +* Add support for VK_EXT_fragment_shader_barycentric +* Add whitelist filtering for debug comments in SPIRV-Remap +* Add support for GL_EXT_ray_cull_mask + +## 11.9.0 2022-04-06 + +### Other changes +* Add GLSL version override functionality +* Add eliminate-dead-input-components to -Os +* Add enhanced-msgs option +* Explicitly use Python 3 for builds + +## 11.8.0 2022-01-27 + +### Other changes +* Add support for SPIR-V 1.6 +* Add support for Vulkan 1.3 +* Add --hlsl-dx-position-w option + +## 11.7.0 2021-11-11 + +### Other changes +* Add support for targeting Vulkan 1.2 in the C API + +## 11.6.0 2021-08-25 + +### Other changes +* Atomic memory function only for shader storage block member or shared variable +* Add support for gl_MaxVaryingVectors for ogl +* Fix loading bool arrays from interface blocks +* Generate separate stores for partially swizzled memory stores +* Allow layout(std430) uniform with GL_EXT_scalar_block_layout +* Support for pragma STDGL invariant(all) +* Support for GL_NV_ray_tracing_motion_blur + +## 11.5.0 2021-06-23 + +### Other changes +* Implement GLSL_EXT_shader_atomic_float2 +* Implement GL_EXT_spirv_intrinsics +* Fixed SPIR-V remapper not remapping OpExtInst instruction set IDs +* only declare compatibility gl_ variables in compatibility mode +* Add support for float spec const vector initialization +* Implement GL_EXT_subgroup_uniform_control_flow. +* Fix arrays dimensioned with spec constant sized gl_WorkGroupSize +* Add support for 64bit integer scalar and vector types to bitCount() builtin + +## 11.4.0 2021-04-22 + +### Other changes +* Fix to keep source compatible with CMake 3.10.2 + +## 11.3.0 2021-04-21 + +### Other changes +* Added --depfile +* Added --auto-sampled-textures +* Now supports InterpolateAt-based functions +* Supports cross-stage automatic IO mapping +* Supports GL_EXT_vulkan_glsl_relaxed (-R option) + +## 11.2.0 2021-02-18 + +### Other changes +* Removed Python requirement when not building with spirv-tools +* Add support for GL_EXT_shared_memory_block +* Implement GL_EXT_null_initializer +* Add CMake support for Fuschia + +## 11.1.0 2020-12-07 + +### Other changes +* Added ray-tracing extension support + +## 11.0.0 2020-07-20 + +### Breaking changes + +#### Visual Studio 2013 is no longer supported + +[As scheduled](https://github.com/KhronosGroup/glslang/blob/9eef54b2513ca6b40b47b07d24f453848b65c0df/README.md#planned-deprecationsremovals), +Microsoft Visual Studio 2013 is no longer officially supported. Please upgrade +to at least Visual Studio 2015. + +## 10.15.3847 2020-07-20 + +### Breaking changes + +* The following files have been removed: + * `glslang/include/revision.h` + * `glslang/include/revision.template` + +The `GLSLANG_MINOR_VERSION` and `GLSLANG_PATCH_LEVEL` defines have been removed +from the public headers. \ +Instead each build script now uses the new `build_info.py` +script along with the `build_info.h.tmpl` and this `CHANGES.md` file to generate +the glslang build-time generated header `glslang/build_info.h`. + +The new public API to obtain the `glslang` version is `glslang::GetVersion()`. + +### Other changes +* `glslang` shared objects produced by CMake are now `SONAME` versioned using + [Semantic Versioning 2.0.0](https://semver.org/). diff --git a/engine/engine/3rdlibs/glslang/CMakeLists.txt b/engine/engine/3rdlibs/glslang/CMakeLists.txt new file mode 100644 index 00000000..7e78c0b5 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/CMakeLists.txt @@ -0,0 +1,401 @@ +# Copyright (C) 2020-2025 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +cmake_minimum_required(VERSION 3.22.1) +project(glslang) + +set(GLSLANG_TESTS_DEFAULT ON) # Can be turned off, below, based on environment. +set(GLSLANG_ENABLE_INSTALL_DEFAULT ON) # Can be turned off, below, based on environment. + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) # Can be removed when min is 3.26 see policy CMP0143. + +# Adhere to GNU filesystem layout conventions +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# Needed for cmake_dependent_option macro +include(CMakeDependentOption) + +option(BUILD_SHARED_LIBS "Build Shared Libraries") +option(BUILD_EXTERNAL "Build external dependencies in /External" ON) +option(BUILD_WERROR "Enable warnings as errors (default is OFF)" OFF) + +set(LIB_TYPE STATIC) + +if(BUILD_SHARED_LIBS) + set(LIB_TYPE SHARED) +endif() + +get_cmake_property(isMultiConfig "GENERATOR_IS_MULTI_CONFIG") +if (NOT isMultiConfig AND NOT CMAKE_BUILD_TYPE) + # This logic inside SPIRV-Tools, which can upset build target dependencies + # if changed after targets are already defined. To prevent these issues, + # ensure CMAKE_BUILD_TYPE is assigned early and at the glslang root scope. + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Currently iOS and Android are very similar. +# They both have their own packaging (APP/APK). +# Which makes regular executables/testing problematic. +# +# Currently the only deliverables for these platforms are +# libraries (either STATIC or SHARED). +# +# Furthermore testing is equally problematic. +if (IOS OR ANDROID) + set(ENABLE_GLSLANG_BINARIES OFF) + set(GLSLANG_TESTS_DEFAULT OFF) +endif() + +# Simplify the default case of including this project. +# Otherwise add_subdirectory users have a harder time consuming the library. +# Since glslang will pollute the installation and add undesirable testing. +if(NOT PROJECT_IS_TOP_LEVEL) + set(GLSLANG_TESTS_DEFAULT OFF) + set(GLSLANG_ENABLE_INSTALL_DEFAULT OFF) +endif() + +# Control whether Glslang self-tests are built and tested. +# Always expose this as an option, so the defaults can be overridden. +option(GLSLANG_TESTS "Enable glslang testing" ${GLSLANG_TESTS_DEFAULT}) + +# Control whether to install Glslang. +# Always expose this as an option, so the defaults can be overridden. +option(GLSLANG_ENABLE_INSTALL "Enable glslang installation" ${GLSLANG_ENABLE_INSTALL_DEFAULT}) + +option(ENABLE_SPIRV "Enables SPIRV output support" ON) +option(ENABLE_SPVREMAPPER "Enables building of SPVRemapper" ON) + +option(ENABLE_GLSLANG_BINARIES "Builds glslang and spirv-remap" ON) + +option(ENABLE_GLSLANG_JS "If using Emscripten, build glslang.js. Otherwise, builds a sample executable for binary-size testing.") +cmake_dependent_option(ENABLE_EMSCRIPTEN_SINGLE_FILE + "If using Emscripten, enables SINGLE_FILE build" + OFF "ENABLE_GLSLANG_JS AND EMSCRIPTEN" + OFF) +cmake_dependent_option(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE + "If using Emscripten, builds to run on Node instead of Web" + OFF "ENABLE_GLSLANG_JS AND EMSCRIPTEN" + OFF) + +option(ENABLE_HLSL "Enables HLSL input support" ON) +option(ENABLE_RTTI "Enables RTTI") +option(ENABLE_EXCEPTIONS "Enables Exceptions") +cmake_dependent_option(ENABLE_OPT "Enables spirv-opt capability if present" ON "ENABLE_SPIRV" OFF) + +if(MINGW OR (APPLE AND ${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")) + # Workaround for CMake behavior on Mac OS with gcc, cmake generates -Xarch_* arguments + # which gcc rejects + set(ENABLE_PCH OFF) + message(NOTICE "Disabling PCH") +endif() + +option(ENABLE_PCH "Enables Precompiled header" ON) + +if(ENABLE_SPIRV) + add_compile_definitions(ENABLE_SPIRV) +endif() + +if(ENABLE_HLSL) + add_compile_definitions(ENABLE_HLSL) +endif() + +if(WIN32) + set(CMAKE_DEBUG_POSTFIX "d") + add_compile_definitions(GLSLANG_OSINCLUDE_WIN32) +elseif(UNIX OR ANDROID) + add_compile_definitions(GLSLANG_OSINCLUDE_UNIX) +else() + message("unknown platform") +endif() + +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + add_compile_options(-Wall -Wmaybe-uninitialized -Wuninitialized -Wunused -Wunused-local-typedefs -Wimplicit-fallthrough + -Wunused-parameter -Wunused-value -Wunused-variable -Wunused-but-set-parameter -Wunused-but-set-variable) + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() + if(NOT ENABLE_EXCEPTIONS) + add_compile_options(-fno-exceptions) + endif() + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0") + add_compile_options(-Werror=deprecated-copy) + endif() + + if(NOT (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")) + if (NOT APPLE) + # Error if there's symbols that are not found at link time. + add_link_options("-Wl,--no-undefined") + endif() + endif() +elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT MSVC) + add_compile_options(-Wall -Wuninitialized -Wunused -Wunused-local-typedefs -Wimplicit-fallthrough + -Wunused-parameter -Wunused-value -Wunused-variable) + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() + if(NOT ENABLE_EXCEPTIONS) + add_compile_options(-fno-exceptions) + endif() + + if(NOT (CMAKE_SYSTEM_NAME MATCHES "OpenBSD|Emscripten")) + # Error if there's symbols that are not found at link time. Some linkers do not support this flag. + if(NOT APPLE) + add_link_options("-Wl,--no-undefined") + endif() + endif() +elseif(MSVC) + if(NOT ENABLE_RTTI) + add_compile_options(/GR-) # Disable RTTI + endif() + if(ENABLE_EXCEPTIONS) + add_compile_options(/EHsc) # Enable Exceptions + else() + string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # Try to remove default /EHsc cxx_flag + add_compile_options(/D_HAS_EXCEPTIONS=0) + endif() +endif() + +# NOTE we could potentially replace this logic with COMPILE_WARNING_AS_ERROR if cmake minimum is bumped to >= 3.24 +if (BUILD_WERROR) + if (NOT MSVC) + add_compile_options(-Werror) + else() + add_compile_options(/WX) + endif() +endif() + +if(ENABLE_GLSLANG_JS) + if(MSVC) + add_compile_options(/Os /GR-) + else() + add_compile_options(-Os -fno-rtti -fno-exceptions) + if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT MSVC) + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-unused-variable -Wno-unused-const-variable) + endif() + endif() +endif() + +# Request C++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +function(glslang_set_link_args TARGET) + # For MinGW compiles, statically link against the GCC and C++ runtimes. + # This avoids the need to ship those runtimes as DLLs. + # This is supported by GCC and Clang. + if(MINGW) + target_link_options(${TARGET} PRIVATE -static -static-libgcc -static-libstdc++) + endif() +endfunction() + +# Root directory for build-time generated include files +set(GLSLANG_GENERATED_INCLUDEDIR "${CMAKE_BINARY_DIR}/include") + +################################################################################ +# Build version information generation +################################################################################ +include(parse_version.cmake) +set(GLSLANG_CHANGES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CHANGES.md") +set(GLSLANG_BUILD_INFO_H_TMPL "${CMAKE_CURRENT_SOURCE_DIR}/build_info.h.tmpl") +set(GLSLANG_BUILD_INFO_H "${GLSLANG_GENERATED_INCLUDEDIR}/glslang/build_info.h") + +parse_version(${GLSLANG_CHANGES_FILE} GLSLANG) + +function(configurate_version) + set(major ${GLSLANG_VERSION_MAJOR}) + set(minor ${GLSLANG_VERSION_MINOR}) + set(patch ${GLSLANG_VERSION_PATCH}) + set(flavor ${GLSLANG_VERSION_FLAVOR}) + configure_file(${GLSLANG_BUILD_INFO_H_TMPL} ${GLSLANG_BUILD_INFO_H} @ONLY) +endfunction() + +configurate_version() + +# glslang_add_build_info_dependency() adds the glslang-build-info dependency and +# generated include directories to target. +function(glslang_add_build_info_dependency target) + target_include_directories(${target} PUBLIC $) +endfunction() + +# glslang_only_export_explicit_symbols() makes the symbol visibility hidden by +# default for when building shared libraries, and sets the +# GLSLANG_IS_SHARED_LIBRARY define, and GLSLANG_EXPORTING to 1 when specifically +# building . +function(glslang_only_export_explicit_symbols target) + if(BUILD_SHARED_LIBS) + target_compile_definitions(${target} PUBLIC "GLSLANG_IS_SHARED_LIBRARY=1") + set_target_properties(${target} PROPERTIES CXX_VISIBILITY_PRESET hidden) + set_target_properties(${target} PROPERTIES C_VISIBILITY_PRESET hidden) + if(WIN32) + target_compile_definitions(${target} PRIVATE "GLSLANG_EXPORTING=1") + endif() + endif() +endfunction() + +# glslang_pch() adds precompiled header rules to for the pre-compiled +# header file . As target_precompile_headers() was added in CMake 3.16, +# this is a no-op if called on earlier versions of CMake. +function(glslang_pch target pch) + if(ENABLE_PCH) + target_precompile_headers(${target} PRIVATE ${pch}) + endif() +endfunction() + +if(BUILD_EXTERNAL AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/External) + # We depend on these for later projects, so they should come first. + add_subdirectory(External) +endif() + +option(ALLOW_EXTERNAL_SPIRV_TOOLS "Allows to build against installed SPIRV-Tools-opt. This is unsupported if the commit isn't the one in known_good.json") +if(NOT TARGET SPIRV-Tools-opt) + if(ALLOW_EXTERNAL_SPIRV_TOOLS) + # Look for external SPIR-V Tools build, if not building in-tree + message(STATUS "Trying to find local SPIR-V tools") + find_package(SPIRV-Tools-opt) + if(NOT TARGET SPIRV-Tools-opt) + if(ENABLE_OPT) + message(SEND_ERROR "ENABLE_OPT set but SPIR-V tools not found! Disabling SPIR-V optimization.") + endif() + set(ENABLE_OPT OFF) + endif() + else() + if(ENABLE_OPT) + message(SEND_ERROR "ENABLE_OPT set but SPIR-V tools not found. Please run update_glslang_sources.py, " + "set the ALLOW_EXTERNAL_SPIRV_TOOLS option to use a local install of SPIRV-Tools, or set ENABLE_OPT=0.") + endif() + endif() +endif() + +if(ENABLE_OPT) + message(STATUS "optimizer enabled") + add_compile_definitions(ENABLE_OPT=1) +else() + if(ENABLE_HLSL) + message(STATUS "spirv-tools not linked - illegal SPIRV may be generated for HLSL") + endif() + add_compile_definitions(ENABLE_OPT=0) +endif() + +if(ENABLE_SPIRV) + add_subdirectory(SPIRV) +endif() +add_subdirectory(glslang) +if(ENABLE_GLSLANG_BINARIES) + add_subdirectory(StandAlone) +endif() + +if(GLSLANG_TESTS) + enable_testing() + add_subdirectory(gtests) + + # glslang-testsuite runs a bash script on Windows. + # Make sure to use '-o igncr' flag to ignore carriage returns (\r). + set(IGNORE_CR_FLAG "") + if(WIN32) + set(IGNORE_CR_FLAG -o igncr) + endif() + + if (isMultiConfig) + set(RESULTS_PATH ${CMAKE_CURRENT_BINARY_DIR}/$/localResults) + set(VALIDATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/$/glslang) + set(REMAP_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/$/spirv-remap) + else() + set(RESULTS_PATH ${CMAKE_CURRENT_BINARY_DIR}/localResults) + set(VALIDATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/glslang) + set(REMAP_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/spirv-remap) + endif() + + # The TARGET_RUNTIME_DLL_DIRS feature requires CMake 3.27 or greater. + if(WIN32 AND BUILD_SHARED_LIBS AND CMAKE_VERSION VERSION_LESS "3.27") + message(WARNING "The Windows shared library test configuration requires CMake 3.27 or greater") + else() + add_test(NAME glslang-testsuite + COMMAND bash ${IGNORE_CR_FLAG} runtests ${RESULTS_PATH} ${VALIDATOR_PATH} ${REMAP_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Test/) + + # Prepend paths to shared libraries. + if (BUILD_SHARED_LIBS) + set_tests_properties(glslang-testsuite PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$,\;>") + set_tests_properties(glslang-testsuite PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$,\;>") + endif() + endif() +endif() + +if (GLSLANG_ENABLE_INSTALL) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/glslang-config.cmake.in" [=[ + @PACKAGE_INIT@ + include(CMakeFindDependencyMacro) + if(@ENABLE_OPT@) + find_dependency(SPIRV-Tools-opt) + endif() + @INSTALL_CONFIG_UNIX@ + include("@PACKAGE_PATH_EXPORT_TARGETS@") + ]=]) + + set(PATH_EXPORT_TARGETS "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/glslang-targets.cmake") + if(UNIX OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + set(INSTALL_CONFIG_UNIX [=[ + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_dependency(Threads) + ]=]) + endif() + configure_package_config_file( + "${CMAKE_CURRENT_BINARY_DIR}/glslang-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/glslang-config.cmake" + PATH_VARS + PATH_EXPORT_TARGETS + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) + + write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/glslang-config-version.cmake" + VERSION ${GLSLANG_VERSION} + COMPATIBILITY SameMajorVersion + ) + + install( + EXPORT glslang-targets + NAMESPACE "glslang::" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/glslang-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/glslang-config-version.cmake" + DESTINATION + "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) +endif() diff --git a/engine/engine/3rdlibs/glslang/README.md b/engine/engine/3rdlibs/glslang/README.md new file mode 100644 index 00000000..e712cc3d --- /dev/null +++ b/engine/engine/3rdlibs/glslang/README.md @@ -0,0 +1,540 @@ +![Continuous Integration](https://github.com/KhronosGroup/glslang/actions/workflows/continuous_integration.yml/badge.svg) +![Continuous Deployment](https://github.com/KhronosGroup/glslang/actions/workflows/continuous_deployment.yml/badge.svg) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/KhronosGroup/glslang/badge)](https://securityscorecards.dev/viewer/?uri=github.com/KhronosGroup/glslang) + +# News + +1. Building glslang as a DLL or shared library is now possible and supported. + +2. The `GenericCodeGen`, `MachineIndependent`, `OSDependent`, and `SPIRV` libraries have been integrated into the main `glslang` library. The old separate libraries have replaced with empty stubs for a temporary compatibility period, and they will be removed entirely in the future. + +3. A new CMake `ENABLE_SPIRV` option has been added to control whether glslang is built with SPIR-V support. Its default value is `ON`. + +4. `OGLCompiler` and `HLSL` stub libraries have been fully removed from the build. + +# Glslang Components and Status + +There are several components: + +### Reference Validator and GLSL/ESSL -> AST Front End + +An OpenGL GLSL and OpenGL|ES GLSL (ESSL) front-end for reference validation and translation of GLSL/ESSL into an internal abstract syntax tree (AST). + +**Status**: Virtually complete, with results carrying similar weight as the specifications. + +### HLSL -> AST Front End + +An HLSL front-end for translation of an approximation of HLSL to glslang's AST form. + +**Status**: Partially complete. Semantics are not reference quality and input is not validated. +This is in contrast to the [DXC project](https://github.com/Microsoft/DirectXShaderCompiler), which receives a much larger investment and attempts to have definitive/reference-level semantics. + +See [issue 362](https://github.com/KhronosGroup/glslang/issues/362) and [issue 701](https://github.com/KhronosGroup/glslang/issues/701) for current status. + +### AST -> SPIR-V Back End + +Translates glslang's AST to the Khronos-specified SPIR-V intermediate language. + +**Status**: Virtually complete. + +### Reflector + +An API for getting reflection information from the AST, reflection types/variables/etc. from the HLL source (not the SPIR-V). + +**Status**: There is a large amount of functionality present, but no specification/goal to measure completeness against. It is accurate for the input HLL and AST, but only approximate for what would later be emitted for SPIR-V. + +### Standalone Wrapper + +`glslang` is command-line tool for accessing the functionality above. + +Status: Complete. + +Tasks waiting to be done are documented as GitHub issues. + +## Other References + +Also see the Khronos landing page for glslang as a reference front end: + +https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ + +The above page, while not kept up to date, includes additional information regarding glslang as a reference validator. + +# How to Use Glslang + +## Execution of Standalone Wrapper + +To use the standalone binary form, execute `glslang`, and it will print +a usage statement. Basic operation is to give it a file containing a shader, +and it will print out warnings/errors and optionally an AST. + +The applied stage-specific rules are based on the file extension: +* `.vert` for a vertex shader +* `.tesc` for a tessellation control shader +* `.tese` for a tessellation evaluation shader +* `.geom` for a geometry shader +* `.frag` for a fragment shader +* `.comp` for a compute shader + +For ray tracing pipeline shaders: +* `.rgen` for a ray generation shader +* `.rint` for a ray intersection shader +* `.rahit` for a ray any-hit shader +* `.rchit` for a ray closest-hit shader +* `.rmiss` for a ray miss shader +* `.rcall` for a callable shader + +There is also a non-shader extension: +* `.conf` for a configuration file of limits, see usage statement for example + +## Building (CMake) + +Instead of building manually, you can also download the binaries for your +platform directly from the [main-tot release][main-tot-release] on GitHub. +Those binaries are automatically uploaded by the buildbots after successful +testing and they always reflect the current top of the tree of the main +branch. + +### Dependencies + +* A C++17 compiler. + (For MSVS: use 2019 or later.) +* [CMake][cmake]: for generating compilation targets. +* make: _Linux_, ninja is an alternative, if configured. +* [Python 3.x][python]: for executing SPIRV-Tools scripts. (Optional if not using SPIRV-Tools and the 'External' subdirectory does not exist.) +* [bison][bison]: _optional_, but needed when changing the grammar (glslang.y). +* [googletest][googletest]: _optional_, but should use if making any changes to glslang. + +### Build steps + +The following steps assume a Bash shell. On Windows, that could be the Git Bash +shell or some other shell of your choosing. + +#### 1) Check-Out this project + +```bash +cd +git clone https://github.com/KhronosGroup/glslang.git +``` + +#### 2) Check-Out External Projects + +```bash +./update_glslang_sources.py +``` + +#### 3) Configure + +Assume the source directory is `$SOURCE_DIR` and the build directory is `$BUILD_DIR`. +CMake will create the `$BUILD_DIR` for the user if it doesn't exist. + +First change your working directory: +```bash +cd $SOURCE_DIR +``` + +For building on Linux: + +```bash +cmake -B $BUILD_DIR -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" +# "Release" (for CMAKE_BUILD_TYPE) could also be "Debug" or "RelWithDebInfo" +``` + +For building on Android: +```bash +cmake -B $BUILD_DIR -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DANDROID_ABI=arm64-v8a -DCMAKE_BUILD_TYPE=Release -DANDROID_STL=c++_static -DANDROID_PLATFORM=android-24 -DCMAKE_SYSTEM_NAME=Android -DANDROID_TOOLCHAIN=clang -DANDROID_ARM_MODE=arm -DCMAKE_MAKE_PROGRAM=$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake +# If on Windows will be -DCMAKE_MAKE_PROGRAM=%ANDROID_NDK_HOME%\prebuilt\windows-x86_64\bin\make.exe +# -G is needed for building on Windows +# -DANDROID_ABI can also be armeabi-v7a for 32 bit +``` + +For building on Windows: + +```bash +cmake -B $BUILD_DIR -DCMAKE_INSTALL_PREFIX="$(pwd)/install" +# The CMAKE_INSTALL_PREFIX part is for testing (explained later). +``` + +Also, consider using `git config --global core.fileMode false` (or with `--local`) on Windows +to prevent the addition of execution permission on files. + +#### 4) Build and Install + +```bash +# for Linux: +make -j4 install + +# for Windows: +cmake --build . --config Release --target install +# "Release" (for --config) could also be "Debug", "MinSizeRel", or "RelWithDebInfo" +``` + +If using MSVC, after running CMake to configure, use the +Configuration Manager to check the `INSTALL` project. + +### Building (GN) + +glslang can also be built with the [GN build system](https://gn.googlesource.com/gn/). + +#### 1) Install `depot_tools` + +Download [depot_tools.zip](https://storage.googleapis.com/chrome-infra/depot_tools.zip), +extract to a directory, and add this directory to your `PATH`. + +#### 2) Synchronize dependencies and generate build files + +This only needs to be done once after updating `glslang`. + +With the current directory set to your `glslang` checkout, type: + +```bash +./update_glslang_sources.py +gclient sync --gclientfile=standalone.gclient +gn gen out/Default +``` + +#### 3) Build + +With the current directory set to your `glslang` checkout, type: + +```bash +cd out/Default +ninja +``` + +### If you need to change the GLSL grammar + +The grammar in `glslang/MachineIndependent/glslang.y` has to be recompiled with +bison if it changes, the output files are committed to the repo to avoid every +developer needing to have bison configured to compile the project when grammar +changes are quite infrequent. For windows you can get binaries from +[GnuWin32][bison-gnu-win32]. + +The command to rebuild is: + +```bash +bison --defines=MachineIndependent/glslang_tab.cpp.h + -t MachineIndependent/glslang.y + -o MachineIndependent/glslang_tab.cpp +``` + +The above command is also available in the bash script in `updateGrammar`, +when executed from the glslang subdirectory of the glslang repository. + +### Building to WASM for the Web and Node +### Building a standalone JS/WASM library for the Web and Node + +Use the steps in [Build Steps](#build-steps), with the following notes/exceptions: +* `emsdk` needs to be present in your executable search path, *PATH* for + Bash-like environments: + + [Instructions located here](https://emscripten.org/docs/getting_started/downloads.html#sdk-download-and-install) +* Wrap cmake call: `emcmake cmake` +* Set `-DENABLE_OPT=OFF`. +* Set `-DENABLE_HLSL=OFF` if HLSL is not needed. +* For a standalone JS/WASM library, turn on `-DENABLE_GLSLANG_JS=ON`. +* To get a fully minimized build, make sure to use `brotli` to compress the .js + and .wasm files +* Note that by default, Emscripten allocates a very small stack size, which may + cause stack overflows when compiling large shaders. Use the + [STACK_SIZE](https://emscripten.org/docs/tools_reference/settings_reference.html?highlight=environment#stack-size) + compiler setting to increase the stack size. + +Example: + +```sh +emcmake cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GLSLANG_JS=ON \ + -DENABLE_HLSL=OFF -DENABLE_OPT=OFF .. +``` + +## Building glslang - Using vcpkg + +You can download and install glslang using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install glslang + +The glslang port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Testing + +Right now, there are two test harnesses existing in glslang: one is [Google +Test](gtests/), one is the [`runtests` script](Test/runtests). The former +runs unit tests and single-shader single-threaded integration tests, while +the latter runs multiple-shader linking tests and multi-threaded tests. + +Tests may erroneously fail or pass if using `ALLOW_EXTERNAL_SPIRV_TOOLS` with +any commit other than the one specified in `known_good.json`. + +### Running tests + +The [`runtests` script](Test/runtests) requires compiled binaries to be +installed into `$BUILD_DIR/install`. Please make sure you have supplied the +correct configuration to CMake (using `-DCMAKE_INSTALL_PREFIX`) when building; +otherwise, you may want to modify the path in the `runtests` script. + +Running Google Test-backed tests: + +```bash +cd $BUILD_DIR + +# for Linux: +ctest + +# for Windows: +ctest -C {Debug|Release|RelWithDebInfo|MinSizeRel} + +# or, run the test binary directly +# (which gives more fine-grained control like filtering): +/glslangtests +``` + +Running `runtests` script-backed tests: + +```bash +cd $SOURCE_DIR/Test && ./runtests +``` + +If some tests fail with validation errors, there may be a mismatch between the +version of `spirv-val` on the system and the version of glslang. In this +case, it is necessary to run `update_glslang_sources.py`. See "Check-Out +External Projects" above for more details. + +### Contributing tests + +Test results should always be included with a pull request that modifies +functionality. + +If you are writing unit tests, please use the Google Test framework and +place the tests under the `gtests/` directory. + +Integration tests are placed in the `Test/` directory. It contains test input +and a subdirectory `baseResults/` that contains the expected results of the +tests. Both the tests and `baseResults/` are under source-code control. + +Google Test runs those integration tests by reading the test input, compiling +them, and then compare against the expected results in `baseResults/`. The +integration tests to run via Google Test is registered in various +`gtests/*.FromFile.cpp` source files. `glslangtests` provides a command-line +option `--update-mode`, which, if supplied, will overwrite the golden files +under the `baseResults/` directory with real output from that invocation. +For more information, please check `gtests/` directory's +[README](gtests/README.md). + +For the `runtests` script, it will generate current results in the +`localResults/` directory and `diff` them against the `baseResults/`. +When you want to update the tracked test results, they need to be +copied from `localResults/` to `baseResults/`. This can be done by +the `bump` shell script. + +You can add your own private list of tests, not tracked publicly, by using +`localtestlist` to list non-tracked tests. This is automatically read +by `runtests` and included in the `diff` and `bump` process. + +## Programmatic Interfaces + +Another piece of software can programmatically translate shaders to an AST +using one of two different interfaces: +* A new C++ class-oriented interface, or +* The original C functional interface + +The `main()` in `StandAlone/StandAlone.cpp` shows examples using both styles. + +### C++ Class Interface (new, preferred) + +This interface is in roughly the last 1/3 of `ShaderLang.h`. It is in the +glslang namespace and contains the following, here with suggested calls +for generating SPIR-V: + +```cxx +const char* GetEsslVersionString(); +const char* GetGlslVersionString(); +bool InitializeProcess(); +void FinalizeProcess(); + +class TShader + setStrings(...); + setEnvInput(EShSourceHlsl or EShSourceGlsl, stage, EShClientVulkan or EShClientOpenGL, 100); + setEnvClient(EShClientVulkan or EShClientOpenGL, EShTargetVulkan_1_0 or EShTargetVulkan_1_1 or EShTargetOpenGL_450); + setEnvTarget(EShTargetSpv, EShTargetSpv_1_0 or EShTargetSpv_1_3); + bool parse(...); + const char* getInfoLog(); + +class TProgram + void addShader(...); + bool link(...); + const char* getInfoLog(); + Reflection queries +``` + +For just validating (not generating code), substitute these calls: + +```cxx + setEnvInput(EShSourceHlsl or EShSourceGlsl, stage, EShClientNone, 0); + setEnvClient(EShClientNone, 0); + setEnvTarget(EShTargetNone, 0); +``` + +See `ShaderLang.h` and the usage of it in `StandAlone/StandAlone.cpp` for more +details. There is a block comment giving more detail above the calls for +`setEnvInput, setEnvClient, and setEnvTarget`. + +### C Functional Interface (original) + +This interface is in roughly the first 2/3 of `ShaderLang.h`, and referred to +as the `Sh*()` interface, as all the entry points start `Sh`. + +The `Sh*()` interface takes a "compiler" call-back object, which it calls after +building call back that is passed the AST and can then execute a back end on it. + +The following is a simplified resulting run-time call stack: + +```c +ShCompile(shader, compiler) -> compiler(AST) -> +``` + +In practice, `ShCompile()` takes shader strings, default version, and +warning/error and other options for controlling compilation. + +### C Functional Interface (new) + +This interface is located `glslang_c_interface.h` and exposes functionality similar to the C++ interface. The following snippet is a complete example showing how to compile GLSL into SPIR-V 1.5 for Vulkan 1.2. + +```c +#include + +// Required for use of glslang_default_resource +#include + +typedef struct SpirVBinary { + uint32_t *words; // SPIR-V words + int size; // number of words in SPIR-V binary +} SpirVBinary; + +SpirVBinary compileShaderToSPIRV_Vulkan(glslang_stage_t stage, const char* shaderSource, const char* fileName) { + const glslang_input_t input = { + .language = GLSLANG_SOURCE_GLSL, + .stage = stage, + .client = GLSLANG_CLIENT_VULKAN, + .client_version = GLSLANG_TARGET_VULKAN_1_2, + .target_language = GLSLANG_TARGET_SPV, + .target_language_version = GLSLANG_TARGET_SPV_1_5, + .code = shaderSource, + .default_version = 100, + .default_profile = GLSLANG_NO_PROFILE, + .force_default_version_and_profile = false, + .forward_compatible = false, + .messages = GLSLANG_MSG_DEFAULT_BIT, + .resource = glslang_default_resource(), + }; + + glslang_shader_t* shader = glslang_shader_create(&input); + + SpirVBinary bin = { + .words = NULL, + .size = 0, + }; + if (!glslang_shader_preprocess(shader, &input)) { + printf("GLSL preprocessing failed %s\n", fileName); + printf("%s\n", glslang_shader_get_info_log(shader)); + printf("%s\n", glslang_shader_get_info_debug_log(shader)); + printf("%s\n", input.code); + glslang_shader_delete(shader); + return bin; + } + + if (!glslang_shader_parse(shader, &input)) { + printf("GLSL parsing failed %s\n", fileName); + printf("%s\n", glslang_shader_get_info_log(shader)); + printf("%s\n", glslang_shader_get_info_debug_log(shader)); + printf("%s\n", glslang_shader_get_preprocessed_code(shader)); + glslang_shader_delete(shader); + return bin; + } + + glslang_program_t* program = glslang_program_create(); + glslang_program_add_shader(program, shader); + + if (!glslang_program_link(program, GLSLANG_MSG_SPV_RULES_BIT | GLSLANG_MSG_VULKAN_RULES_BIT)) { + printf("GLSL linking failed %s\n", fileName); + printf("%s\n", glslang_program_get_info_log(program)); + printf("%s\n", glslang_program_get_info_debug_log(program)); + glslang_program_delete(program); + glslang_shader_delete(shader); + return bin; + } + + glslang_program_SPIRV_generate(program, stage); + + bin.size = glslang_program_SPIRV_get_size(program); + bin.words = malloc(bin.size * sizeof(uint32_t)); + glslang_program_SPIRV_get(program, bin.words); + + const char* spirv_messages = glslang_program_SPIRV_get_messages(program); + if (spirv_messages) + printf("(%s) %s\b", fileName, spirv_messages); + + glslang_program_delete(program); + glslang_shader_delete(shader); + + return bin; +} +``` + +## Basic Internal Operation + +* Initial lexical analysis is done by the preprocessor in + `MachineIndependent/Preprocessor`, and then refined by a GLSL scanner + in `MachineIndependent/Scan.cpp`. There is currently no use of flex. + +* Code is parsed using bison on `MachineIndependent/glslang.y` with the + aid of a symbol table and an AST. The symbol table is not passed on to + the back-end; the intermediate representation stands on its own. + The tree is built by the grammar productions, many of which are + offloaded into `ParseHelper.cpp`, and by `Intermediate.cpp`. + +* The intermediate representation is very high-level, and represented + as an in-memory tree. This serves to lose no information from the + original program, and to have efficient transfer of the result from + parsing to the back-end. In the AST, constants are propagated and + folded, and a very small amount of dead code is eliminated. + + To aid linking and reflection, the last top-level branch in the AST + lists all global symbols. + +* The primary algorithm of the back-end compiler is to traverse the + tree (high-level intermediate representation), and create an internal + object code representation. There is an example of how to do this + in `MachineIndependent/intermOut.cpp`. + +* Reduction of the tree to a linear byte-code style low-level intermediate + representation is likely a good way to generate fully optimized code. + +* There is currently some dead old-style linker-type code still lying around. + +* Memory pool: parsing uses types derived from C++ `std` types, using a + custom allocator that puts them in a memory pool. This makes allocation + of individual container/contents just few cycles and deallocation free. + This pool is popped after the AST is made and processed. + + The use is simple: if you are going to call `new`, there are three cases: + + - the object comes from the pool (its base class has the macro + `POOL_ALLOCATOR_NEW_DELETE` in it) and you do not have to call `delete` + + - it is a `TString`, in which case call `NewPoolTString()`, which gets + it from the pool, and there is no corresponding `delete` + + - the object does not come from the pool, and you have to do normal + C++ memory management of what you `new` + +* Features can be protected by version/extension/stage/profile: + See the comment in `glslang/MachineIndependent/Versions.cpp`. + +[cmake]: https://cmake.org/ +[python]: https://www.python.org/ +[bison]: https://www.gnu.org/software/bison/ +[googletest]: https://github.com/google/googletest +[bison-gnu-win32]: http://gnuwin32.sourceforge.net/packages/bison.htm +[main-tot-release]: https://github.com/KhronosGroup/glslang/releases/tag/main-tot diff --git a/engine/engine/3rdlibs/glslang/SPIRV/CInterface/spirv_c_interface.cpp b/engine/engine/3rdlibs/glslang/SPIRV/CInterface/spirv_c_interface.cpp new file mode 100644 index 00000000..631d19d7 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/CInterface/spirv_c_interface.cpp @@ -0,0 +1,122 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "glslang/Include/glslang_c_interface.h" + +#include +#include "glslang/Public/ShaderLang.h" +#include "SPIRV/GlslangToSpv.h" +#include "SPIRV/Logger.h" +#include "SPIRV/SpvTools.h" + +static_assert(sizeof(glslang_spv_options_t) == sizeof(glslang::SpvOptions), ""); + +typedef struct glslang_program_s { + glslang::TProgram* program; + std::vector spirv; + std::string loggerMessages; +} glslang_program_t; + +static EShLanguage c_shader_stage(glslang_stage_t stage) +{ + switch (stage) { + case GLSLANG_STAGE_VERTEX: + return EShLangVertex; + case GLSLANG_STAGE_TESSCONTROL: + return EShLangTessControl; + case GLSLANG_STAGE_TESSEVALUATION: + return EShLangTessEvaluation; + case GLSLANG_STAGE_GEOMETRY: + return EShLangGeometry; + case GLSLANG_STAGE_FRAGMENT: + return EShLangFragment; + case GLSLANG_STAGE_COMPUTE: + return EShLangCompute; + case GLSLANG_STAGE_RAYGEN: + return EShLangRayGen; + case GLSLANG_STAGE_INTERSECT: + return EShLangIntersect; + case GLSLANG_STAGE_ANYHIT: + return EShLangAnyHit; + case GLSLANG_STAGE_CLOSESTHIT: + return EShLangClosestHit; + case GLSLANG_STAGE_MISS: + return EShLangMiss; + case GLSLANG_STAGE_CALLABLE: + return EShLangCallable; + case GLSLANG_STAGE_TASK: + return EShLangTask; + case GLSLANG_STAGE_MESH: + return EShLangMesh; + default: + break; + } + return EShLangCount; +} + +GLSLANG_EXPORT void glslang_program_SPIRV_generate(glslang_program_t* program, glslang_stage_t stage) +{ + glslang_spv_options_t spv_options {}; + spv_options.disable_optimizer = true; + spv_options.validate = true; + + glslang_program_SPIRV_generate_with_options(program, stage, &spv_options); +} + +GLSLANG_EXPORT void glslang_program_SPIRV_generate_with_options(glslang_program_t* program, glslang_stage_t stage, glslang_spv_options_t* spv_options) { + spv::SpvBuildLogger logger; + + const glslang::TIntermediate* intermediate = program->program->getIntermediate(c_shader_stage(stage)); + + program->spirv.clear(); + + glslang::GlslangToSpv(*intermediate, program->spirv, &logger, reinterpret_cast(spv_options)); + + program->loggerMessages = logger.getAllMessages(); +} + +GLSLANG_EXPORT size_t glslang_program_SPIRV_get_size(glslang_program_t* program) { return program->spirv.size(); } + +GLSLANG_EXPORT void glslang_program_SPIRV_get(glslang_program_t* program, unsigned int* out) +{ + memcpy(out, program->spirv.data(), program->spirv.size() * sizeof(unsigned int)); +} + +GLSLANG_EXPORT unsigned int* glslang_program_SPIRV_get_ptr(glslang_program_t* program) +{ + return program->spirv.data(); +} + +GLSLANG_EXPORT const char* glslang_program_SPIRV_get_messages(glslang_program_t* program) +{ + return program->loggerMessages.empty() ? nullptr : program->loggerMessages.c_str(); +} diff --git a/engine/engine/3rdlibs/glslang/SPIRV/CMakeLists.txt b/engine/engine/3rdlibs/glslang/SPIRV/CMakeLists.txt new file mode 100644 index 00000000..d746839a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/CMakeLists.txt @@ -0,0 +1,135 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(SPIRV_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/GlslangToSpv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/InReadableOrder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Logger.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SpvBuilder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SpvPostProcess.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/doc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SpvTools.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CInterface/spirv_c_interface.cpp + PARENT_SCOPE) + +set(SPVREMAP_SOURCES + SPVRemapper.cpp + doc.cpp) + +set(SPIRV_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/bitutils.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.EXT.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.KHR.h + ${CMAKE_CURRENT_SOURCE_DIR}/GlslangToSpv.h + ${CMAKE_CURRENT_SOURCE_DIR}/hex_float.h + ${CMAKE_CURRENT_SOURCE_DIR}/Logger.h + ${CMAKE_CURRENT_SOURCE_DIR}/SpvBuilder.h + ${CMAKE_CURRENT_SOURCE_DIR}/spvIR.h + ${CMAKE_CURRENT_SOURCE_DIR}/doc.h + ${CMAKE_CURRENT_SOURCE_DIR}/SpvTools.h + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.AMD.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.NV.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.ARM.h + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.ext.QCOM.h + ${CMAKE_CURRENT_SOURCE_DIR}/NonSemanticDebugPrintf.h + ${CMAKE_CURRENT_SOURCE_DIR}/NonSemanticShaderDebugInfo100.h + PARENT_SCOPE) + +set(SPVREMAP_HEADERS + SPVRemapper.h + doc.h) + +set(PUBLIC_HEADERS + GlslangToSpv.h + disassemble.h + Logger.h + spirv.hpp + SPVRemapper.h + SpvTools.h) + +add_library(SPIRV ${LIB_TYPE} ${CMAKE_CURRENT_SOURCE_DIR}/../glslang/stub.cpp) +add_library(glslang::SPIRV ALIAS SPIRV) +set_target_properties(SPIRV PROPERTIES + FOLDER glslang + POSITION_INDEPENDENT_CODE ON + VERSION "${GLSLANG_VERSION}" + SOVERSION "${GLSLANG_VERSION_MAJOR}") +target_include_directories(SPIRV PUBLIC + $ + $) +glslang_only_export_explicit_symbols(SPIRV) + +if (ENABLE_SPVREMAPPER) + add_library(SPVRemapper ${LIB_TYPE} ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) + add_library(glslang::SPVRemapper ALIAS SPVRemapper) + set_target_properties(SPVRemapper PROPERTIES + FOLDER glslang + POSITION_INDEPENDENT_CODE ON + VERSION "${GLSLANG_VERSION}" + SOVERSION "${GLSLANG_VERSION_MAJOR}") + glslang_only_export_explicit_symbols(SPVRemapper) +endif() + +if(WIN32 AND BUILD_SHARED_LIBS) + set_target_properties(SPIRV PROPERTIES PREFIX "") + if (ENABLE_SPVREMAPPER) + set_target_properties(SPVRemapper PROPERTIES PREFIX "") + endif() +endif() + +if(ENABLE_OPT) + target_link_libraries(SPIRV INTERFACE glslang PUBLIC SPIRV-Tools-opt) + target_include_directories(SPIRV PUBLIC + $) +else() + target_link_libraries(SPIRV INTERFACE glslang) +endif() + +if(WIN32) + source_group("Source" FILES ${SOURCES} ${HEADERS}) + source_group("Source" FILES ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) +endif() + +if(GLSLANG_ENABLE_INSTALL) + if (ENABLE_SPVREMAPPER) + install(TARGETS SPVRemapper EXPORT glslang-targets) + endif() + + install(TARGETS SPIRV EXPORT glslang-targets) + + install(FILES ${PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang/SPIRV/) +endif() diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.AMD.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.AMD.h new file mode 100644 index 00000000..009d2f1c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.AMD.h @@ -0,0 +1,108 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextAMD_H +#define GLSLextAMD_H + +static const int GLSLextAMDVersion = 100; +static const int GLSLextAMDRevision = 7; + +// SPV_AMD_shader_ballot +static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot"; + +enum ShaderBallotAMD { + ShaderBallotBadAMD = 0, // Don't use + + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4, + + ShaderBallotCountAMD +}; + +// SPV_AMD_shader_trinary_minmax +static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax"; + +enum ShaderTrinaryMinMaxAMD { + ShaderTrinaryMinMaxBadAMD = 0, // Don't use + + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9, + + ShaderTrinaryMinMaxCountAMD +}; + +// SPV_AMD_shader_explicit_vertex_parameter +static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter"; + +enum ShaderExplicitVertexParameterAMD { + ShaderExplicitVertexParameterBadAMD = 0, // Don't use + + InterpolateAtVertexAMD = 1, + + ShaderExplicitVertexParameterCountAMD +}; + +// SPV_AMD_gcn_shader +static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader"; + +enum GcnShaderAMD { + GcnShaderBadAMD = 0, // Don't use + + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3, + + GcnShaderCountAMD +}; + +// SPV_AMD_gpu_shader_half_float +static const char* const E_SPV_AMD_gpu_shader_half_float = "SPV_AMD_gpu_shader_half_float"; + +// SPV_AMD_texture_gather_bias_lod +static const char* const E_SPV_AMD_texture_gather_bias_lod = "SPV_AMD_texture_gather_bias_lod"; + +// SPV_AMD_gpu_shader_int16 +static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16"; + +// SPV_AMD_shader_image_load_store_lod +static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod"; + +// SPV_AMD_shader_fragment_mask +static const char* const E_SPV_AMD_shader_fragment_mask = "SPV_AMD_shader_fragment_mask"; + +// SPV_AMD_gpu_shader_half_float_fetch +static const char* const E_SPV_AMD_gpu_shader_half_float_fetch = "SPV_AMD_gpu_shader_half_float_fetch"; + +#endif // #ifndef GLSLextAMD_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.ARM.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.ARM.h new file mode 100644 index 00000000..14425be1 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.ARM.h @@ -0,0 +1,35 @@ +/* +** Copyright (c) 2022 ARM Limited +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextARM_H +#define GLSLextARM_H + +static const int GLSLextARMVersion = 100; +static const int GLSLextARMRevision = 1; + +static const char * const E_SPV_ARM_core_builtins = "SPV_ARM_core_builtins"; + +#endif // #ifndef GLSLextARM_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.EXT.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.EXT.h new file mode 100644 index 00000000..07f3c302 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.EXT.h @@ -0,0 +1,46 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextEXT_H +#define GLSLextEXT_H + +static const int GLSLextEXTVersion = 100; +static const int GLSLextEXTRevision = 2; + +static const char* const E_SPV_EXT_shader_stencil_export = "SPV_EXT_shader_stencil_export"; +static const char* const E_SPV_EXT_shader_viewport_index_layer = "SPV_EXT_shader_viewport_index_layer"; +static const char* const E_SPV_EXT_fragment_fully_covered = "SPV_EXT_fragment_fully_covered"; +static const char* const E_SPV_EXT_fragment_invocation_density = "SPV_EXT_fragment_invocation_density"; +static const char* const E_SPV_EXT_demote_to_helper_invocation = "SPV_EXT_demote_to_helper_invocation"; +static const char* const E_SPV_EXT_shader_atomic_float_add = "SPV_EXT_shader_atomic_float_add"; +static const char* const E_SPV_EXT_shader_atomic_float16_add = "SPV_EXT_shader_atomic_float16_add"; +static const char* const E_SPV_EXT_shader_atomic_float_min_max = "SPV_EXT_shader_atomic_float_min_max"; +static const char* const E_SPV_EXT_shader_image_int64 = "SPV_EXT_shader_image_int64"; +static const char* const E_SPV_EXT_shader_tile_image = "SPV_EXT_shader_tile_image"; +static const char* const E_SPV_EXT_mesh_shader = "SPV_EXT_mesh_shader"; +static const char* const E_SPV_ARM_cooperative_matrix_layouts = "SPV_ARM_cooperative_matrix_layouts"; + +#endif // #ifndef GLSLextEXT_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.KHR.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.KHR.h new file mode 100644 index 00000000..25dd88ff --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.KHR.h @@ -0,0 +1,70 @@ +/* +** Copyright (c) 2014-2020 The Khronos Group Inc. +** Copyright (C) 2022-2024 Arm Limited. +** Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextKHR_H +#define GLSLextKHR_H + +static const int GLSLextKHRVersion = 100; +static const int GLSLextKHRRevision = 3; + +static const char* const E_SPV_KHR_shader_ballot = "SPV_KHR_shader_ballot"; +static const char* const E_SPV_KHR_subgroup_vote = "SPV_KHR_subgroup_vote"; +static const char* const E_SPV_KHR_device_group = "SPV_KHR_device_group"; +static const char* const E_SPV_KHR_multiview = "SPV_KHR_multiview"; +static const char* const E_SPV_KHR_shader_draw_parameters = "SPV_KHR_shader_draw_parameters"; +static const char* const E_SPV_KHR_16bit_storage = "SPV_KHR_16bit_storage"; +static const char* const E_SPV_KHR_8bit_storage = "SPV_KHR_8bit_storage"; +static const char* const E_SPV_KHR_storage_buffer_storage_class = "SPV_KHR_storage_buffer_storage_class"; +static const char* const E_SPV_KHR_post_depth_coverage = "SPV_KHR_post_depth_coverage"; +static const char* const E_SPV_KHR_vulkan_memory_model = "SPV_KHR_vulkan_memory_model"; +static const char* const E_SPV_EXT_physical_storage_buffer = "SPV_EXT_physical_storage_buffer"; +static const char* const E_SPV_KHR_physical_storage_buffer = "SPV_KHR_physical_storage_buffer"; +static const char* const E_SPV_EXT_fragment_shader_interlock = "SPV_EXT_fragment_shader_interlock"; +static const char* const E_SPV_KHR_shader_clock = "SPV_KHR_shader_clock"; +static const char* const E_SPV_KHR_non_semantic_info = "SPV_KHR_non_semantic_info"; +static const char* const E_SPV_KHR_ray_tracing = "SPV_KHR_ray_tracing"; +static const char* const E_SPV_KHR_ray_query = "SPV_KHR_ray_query"; +static const char* const E_SPV_KHR_fragment_shading_rate = "SPV_KHR_fragment_shading_rate"; +static const char* const E_SPV_KHR_terminate_invocation = "SPV_KHR_terminate_invocation"; +static const char* const E_SPV_KHR_workgroup_memory_explicit_layout = "SPV_KHR_workgroup_memory_explicit_layout"; +static const char* const E_SPV_KHR_subgroup_uniform_control_flow = "SPV_KHR_subgroup_uniform_control_flow"; +static const char* const E_SPV_KHR_fragment_shader_barycentric = "SPV_KHR_fragment_shader_barycentric"; +static const char* const E_SPV_KHR_quad_control = "SPV_KHR_quad_control"; +static const char* const E_SPV_AMD_shader_early_and_late_fragment_tests = "SPV_AMD_shader_early_and_late_fragment_tests"; +static const char* const E_SPV_KHR_ray_tracing_position_fetch = "SPV_KHR_ray_tracing_position_fetch"; +static const char* const E_SPV_KHR_cooperative_matrix = "SPV_KHR_cooperative_matrix"; +static const char* const E_SPV_KHR_maximal_reconvergence = "SPV_KHR_maximal_reconvergence"; +static const char* const E_SPV_KHR_subgroup_rotate = "SPV_KHR_subgroup_rotate"; +static const char* const E_SPV_KHR_expect_assume = "SPV_KHR_expect_assume"; +static const char* const E_SPV_EXT_replicated_composites = "SPV_EXT_replicated_composites"; +static const char* const E_SPV_KHR_relaxed_extended_instruction = "SPV_KHR_relaxed_extended_instruction"; +static const char* const E_SPV_KHR_integer_dot_product = "SPV_KHR_integer_dot_product"; +static const char* const E_SPV_NV_cooperative_vector = "SPV_NV_cooperative_vector"; +static const char* const E_SPV_KHR_bfloat16 = "SPV_KHR_bfloat16"; + +#endif // #ifndef GLSLextKHR_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.NV.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.NV.h new file mode 100644 index 00000000..d4a97b31 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.NV.h @@ -0,0 +1,104 @@ +/* +** Copyright (c) 2014-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextNV_H +#define GLSLextNV_H + +enum BuiltIn; +enum Decoration; +enum Op; +enum Capability; + +static const int GLSLextNVVersion = 100; +static const int GLSLextNVRevision = 11; + +//SPV_NV_sample_mask_override_coverage +const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage"; + +//SPV_NV_geometry_shader_passthrough +const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough"; + +//SPV_NV_viewport_array2 +const char* const E_SPV_NV_viewport_array2 = "SPV_NV_viewport_array2"; +const char* const E_ARB_shader_viewport_layer_array = "SPV_ARB_shader_viewport_layer_array"; + +//SPV_NV_stereo_view_rendering +const char* const E_SPV_NV_stereo_view_rendering = "SPV_NV_stereo_view_rendering"; + +//SPV_NVX_multiview_per_view_attributes +const char* const E_SPV_NVX_multiview_per_view_attributes = "SPV_NVX_multiview_per_view_attributes"; + +//SPV_NV_shader_subgroup_partitioned +const char* const E_SPV_NV_shader_subgroup_partitioned = "SPV_NV_shader_subgroup_partitioned"; + +//SPV_NV_fragment_shader_barycentric +const char* const E_SPV_NV_fragment_shader_barycentric = "SPV_NV_fragment_shader_barycentric"; + +//SPV_NV_compute_shader_derivatives +const char* const E_SPV_NV_compute_shader_derivatives = "SPV_NV_compute_shader_derivatives"; + +//SPV_NV_shader_image_footprint +const char* const E_SPV_NV_shader_image_footprint = "SPV_NV_shader_image_footprint"; + +//SPV_NV_mesh_shader +const char* const E_SPV_NV_mesh_shader = "SPV_NV_mesh_shader"; + +//SPV_NV_raytracing +const char* const E_SPV_NV_ray_tracing = "SPV_NV_ray_tracing"; + +//SPV_NV_ray_tracing_motion_blur +const char* const E_SPV_NV_ray_tracing_motion_blur = "SPV_NV_ray_tracing_motion_blur"; + +//SPV_NV_shading_rate +const char* const E_SPV_NV_shading_rate = "SPV_NV_shading_rate"; + +//SPV_NV_cooperative_matrix +const char* const E_SPV_NV_cooperative_matrix = "SPV_NV_cooperative_matrix"; + +//SPV_NV_shader_sm_builtins +const char* const E_SPV_NV_shader_sm_builtins = "SPV_NV_shader_sm_builtins"; + +//SPV_NV_shader_execution_reorder +const char* const E_SPV_NV_shader_invocation_reorder = "SPV_NV_shader_invocation_reorder"; + +//SPV_NV_displacement_micromap +const char* const E_SPV_NV_displacement_micromap = "SPV_NV_displacement_micromap"; + +//SPV_NV_shader_atomic_fp16_vector +const char* const E_SPV_NV_shader_atomic_fp16_vector = "SPV_NV_shader_atomic_fp16_vector"; + +//SPV_NV_tensor_addressing +const char* const E_SPV_NV_tensor_addressing = "SPV_NV_tensor_addressing"; + +//SPV_NV_cooperative_matrix2 +const char* const E_SPV_NV_cooperative_matrix2 = "SPV_NV_cooperative_matrix2"; + +//SPV_NV_cluster_acceleration_structure +const char* const E_SPV_NV_cluster_acceleration_structure = "SPV_NV_cluster_acceleration_structure"; + +//SPV_NV_linear_swept_spheres +const char* const E_SPV_NV_linear_swept_spheres = "SPV_NV_linear_swept_spheres"; +#endif // #ifndef GLSLextNV_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.QCOM.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.QCOM.h new file mode 100644 index 00000000..b52990f0 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.ext.QCOM.h @@ -0,0 +1,43 @@ +/* +** Copyright (c) 2021 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextQCOM_H +#define GLSLextQCOM_H + +enum BuiltIn; +enum Decoration; +enum Op; +enum Capability; + +static const int GLSLextQCOMVersion = 100; +static const int GLSLextQCOMRevision = 1; + +//SPV_QCOM_image_processing +const char* const E_SPV_QCOM_image_processing = "SPV_QCOM_image_processing"; +//SPV_QCOM_image_processing2 +const char* const E_SPV_QCOM_image_processing2 = "SPV_QCOM_image_processing2"; + +#endif // #ifndef GLSLextQCOM_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GLSL.std.450.h b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.std.450.h new file mode 100644 index 00000000..86d3da80 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 1; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.cpp b/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.cpp new file mode 100644 index 00000000..ac02945c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.cpp @@ -0,0 +1,10905 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017, 2022-2025 Arm Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// translate them to SPIR-V. +// + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +#include "SpvTools.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "GLSL.ext.ARM.h" + #include "GLSL.ext.QCOM.h" + #include "NonSemanticDebugPrintf.h" +} + +// Glslang includes +#include "../glslang/MachineIndependent/localintermediate.h" +#include "../glslang/MachineIndependent/SymbolTable.h" +#include "../glslang/Include/Common.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +namespace { +class SpecConstantOpModeGuard { +public: + SpecConstantOpModeGuard(spv::Builder* builder) + : builder_(builder) { + previous_flag_ = builder->isInSpecConstCodeGenMode(); + } + ~SpecConstantOpModeGuard() { + previous_flag_ ? builder_->setToSpecConstCodeGenMode() + : builder_->setToNormalCodeGenMode(); + } + void turnOnSpecConstantOpMode() { + builder_->setToSpecConstCodeGenMode(); + } + +private: + spv::Builder* builder_; + bool previous_flag_; +}; + +struct OpDecorations { + public: + OpDecorations(spv::Decoration precision, spv::Decoration noContraction, spv::Decoration nonUniform) : + precision(precision) + , + noContraction(noContraction), + nonUniform(nonUniform) + { } + + spv::Decoration precision; + + void addNoContraction(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, noContraction); } + void addNonUniform(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, nonUniform); } + protected: + spv::Decoration noContraction; + spv::Decoration nonUniform; +}; + +} // namespace + +// +// The main holder of information for translating glslang to SPIR-V. +// +// Derives from the AST walking base class. +// +class TGlslangToSpvTraverser : public glslang::TIntermTraverser { +public: + TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger, + glslang::SpvOptions& options); + virtual ~TGlslangToSpvTraverser() { } + + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); + void visitConstantUnion(glslang::TIntermConstantUnion*); + bool visitSelection(glslang::TVisit, glslang::TIntermSelection*); + bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*); + void visitSymbol(glslang::TIntermSymbol* symbol); + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*); + bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); + bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); + + void finishSpv(bool compileOnly); + void dumpSpv(std::vector& out); + +protected: + TGlslangToSpvTraverser(TGlslangToSpvTraverser&); + TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&); + + spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateNonUniformDecoration(const spv::Builder::AccessChain::CoherentFlags& coherentFlags); + spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type); + spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); + spv::ImageFormat TranslateImageFormat(const glslang::TType& type); + spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; + spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector& operands) const; + spv::StorageClass TranslateStorageClass(const glslang::TType&); + void TranslateLiterals(const glslang::TVector&, std::vector&) const; + void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType); + spv::Id createSpvVariable(const glslang::TIntermSymbol*, spv::Id forcedType); + spv::Id getSampledType(const glslang::TSampler&); + spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); + spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult); + void convertSwizzle(const glslang::TIntermAggregate&, std::vector& swizzle); + spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false); + spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&, + bool lastBufferBlockMember, bool forwardReferenceOnly = false); + void applySpirvDecorate(const glslang::TType& type, spv::Id id, std::optional member); + bool filterMember(const glslang::TType& member); + spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, + glslang::TLayoutPacking, const glslang::TQualifier&); + spv::LinkageType convertGlslangLinkageToSpv(glslang::TLinkType glslangLinkType); + void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, + const glslang::TQualifier&, spv::Id, const std::vector& spvMembers); + spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim, bool allowZero = false, bool boolType = false); + spv::Id accessChainLoad(const glslang::TType& type); + void accessChainStore(const glslang::TType& type, spv::Id rvalue); + void multiTypeStore(const glslang::TType&, spv::Id rValue); + spv::Id convertLoadedBoolInUniformToUint(const glslang::TType& type, spv::Id nominalTypeId, spv::Id loadedId); + glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; + int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, + int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember); + + bool isShaderEntryPoint(const glslang::TIntermAggregate* node); + bool writableParam(glslang::TStorageQualifier) const; + bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam); + void makeFunctions(const glslang::TIntermSequence&); + void makeGlobalInitializers(const glslang::TIntermSequence&); + void collectRayTracingLinkerObjects(); + void visitFunctions(const glslang::TIntermSequence&); + void handleFunctionEntry(const glslang::TIntermAggregate* node); + void translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + void translateArguments(glslang::TIntermUnary& node, std::vector& arguments); + spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node); + spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); + + spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison = true); + spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right); + spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags, + const glslang::TType &opType); + spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy); + spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand, + glslang::TBasicType resultBasicType, glslang::TBasicType operandBasicType); + spv::Id createIntWidthConversion(spv::Id operand, int vectorSize, spv::Id destType, + glslang::TBasicType resultBasicType, glslang::TBasicType operandBasicType); + spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); + spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags, + const glslang::TType &opType); + spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands); + spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy); + spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId); + spv::Id getSymbolId(const glslang::TIntermSymbol* node); + void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier); + bool hasQCOMImageProceessingDecoration(spv::Id id, spv::Decoration decor); + void addImageProcessingQCOMDecoration(spv::Id id, spv::Decoration decor); + void addImageProcessing2QCOMDecoration(spv::Id id, bool isForGather); + spv::Id createSpvConstant(const glslang::TIntermTyped&); + spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, + int& nextConst, bool specConstant); + bool isTrivialLeaf(const glslang::TIntermTyped* node); + bool isTrivial(const glslang::TIntermTyped* node); + spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); + spv::Id getExtBuiltins(const char* name); + std::pair getForcedType(glslang::TBuiltInVariable builtIn, const glslang::TType&); + spv::Id translateForcedType(spv::Id object); + spv::Id createCompositeConstruct(spv::Id typeId, std::vector constituents); + + glslang::SpvOptions& options; + spv::Function* shaderEntry; + spv::Function* currentFunction; + spv::Instruction* entryPoint; + int sequenceDepth; + + spv::SpvBuildLogger* logger; + + // There is a 1:1 mapping between a spv builder and a module; this is thread safe + spv::Builder builder; + bool inEntryPoint; + bool entryPointTerminated; + bool linkageOnly; // true when visiting the set of objects in the AST present only for + // establishing interface, whether or not they were statically used + std::set iOSet; // all input/output variables from either static use or declaration of interface + const glslang::TIntermediate* glslangIntermediate; + bool nanMinMaxClamp; // true if use NMin/NMax/NClamp instead of FMin/FMax/FClamp + spv::Id stdBuiltins; + spv::Id nonSemanticDebugPrintf; + std::unordered_map extBuiltinMap; + + std::unordered_map symbolValues; + std::unordered_map builtInVariableIds; + std::unordered_set rValueParameters; // set of formal function parameters passed as rValues, + // rather than a pointer + std::unordered_map functionMap; + std::unordered_map structMap[glslang::ElpCount][glslang::ElmCount]; + // for mapping glslang block indices to spv indices (e.g., due to hidden members): + std::unordered_map> memberRemapper; + // for mapping glslang symbol struct to symbol Id + std::unordered_map glslangTypeToIdMap; + std::stack breakForLoop; // false means break for switch + std::unordered_map counterOriginator; + // Map pointee types for EbtReference to their forward pointers + std::map forwardPointers; + // Type forcing, for when SPIR-V wants a different type than the AST, + // requiring local translation to and from SPIR-V type on every access. + // Maps AST-required-type-id> + std::unordered_map forceType; + // Used by Task shader while generating opearnds for OpEmitMeshTasksEXT + spv::Id taskPayloadID; + // Used later for generating OpTraceKHR/OpExecuteCallableKHR/OpHitObjectRecordHit*/OpHitObjectGetShaderBindingTableData + std::unordered_map locationToSymbol[4]; + std::unordered_map > idToQCOMDecorations; +}; + +// +// Helper functions for translating glslang representations to SPIR-V enumerants. +// + +// Translate glslang profile to SPIR-V source language. +spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile) +{ + switch (source) { + case glslang::EShSourceGlsl: + switch (profile) { + case ENoProfile: + case ECoreProfile: + case ECompatibilityProfile: + return spv::SourceLanguageGLSL; + case EEsProfile: + return spv::SourceLanguageESSL; + default: + return spv::SourceLanguageUnknown; + } + case glslang::EShSourceHlsl: + return spv::SourceLanguageHLSL; + default: + return spv::SourceLanguageUnknown; + } +} + +// Translate glslang language (stage) to SPIR-V execution model. +spv::ExecutionModel TranslateExecutionModel(EShLanguage stage, bool isMeshShaderEXT = false) +{ + switch (stage) { + case EShLangVertex: return spv::ExecutionModelVertex; + case EShLangFragment: return spv::ExecutionModelFragment; + case EShLangCompute: return spv::ExecutionModelGLCompute; + case EShLangTessControl: return spv::ExecutionModelTessellationControl; + case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation; + case EShLangGeometry: return spv::ExecutionModelGeometry; + case EShLangRayGen: return spv::ExecutionModelRayGenerationKHR; + case EShLangIntersect: return spv::ExecutionModelIntersectionKHR; + case EShLangAnyHit: return spv::ExecutionModelAnyHitKHR; + case EShLangClosestHit: return spv::ExecutionModelClosestHitKHR; + case EShLangMiss: return spv::ExecutionModelMissKHR; + case EShLangCallable: return spv::ExecutionModelCallableKHR; + case EShLangTask: return (isMeshShaderEXT)? spv::ExecutionModelTaskEXT : spv::ExecutionModelTaskNV; + case EShLangMesh: return (isMeshShaderEXT)? spv::ExecutionModelMeshEXT: spv::ExecutionModelMeshNV; + default: + assert(0); + return spv::ExecutionModelFragment; + } +} + +// Translate glslang sampler type to SPIR-V dimensionality. +spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) +{ + switch (sampler.dim) { + case glslang::Esd1D: return spv::Dim1D; + case glslang::Esd2D: return spv::Dim2D; + case glslang::Esd3D: return spv::Dim3D; + case glslang::EsdCube: return spv::DimCube; + case glslang::EsdRect: return spv::DimRect; + case glslang::EsdBuffer: return spv::DimBuffer; + case glslang::EsdSubpass: return spv::DimSubpassData; + case glslang::EsdAttachmentEXT: return spv::DimTileImageDataEXT; + default: + assert(0); + return spv::Dim2D; + } +} + +// Translate glslang precision to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision) +{ + switch (glslangPrecision) { + case glslang::EpqLow: return spv::DecorationRelaxedPrecision; + case glslang::EpqMedium: return spv::DecorationRelaxedPrecision; + default: + return spv::NoPrecision; + } +} + +// Translate glslang type to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +{ + return TranslatePrecisionDecoration(type.getQualifier().precision); +} + +// Translate glslang type to SPIR-V block decorations. +spv::Decoration TranslateBlockDecoration(const glslang::TStorageQualifier storage, bool useStorageBuffer) +{ + switch (storage) { + case glslang::EvqUniform: return spv::DecorationBlock; + case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock; + case glslang::EvqVaryingIn: return spv::DecorationBlock; + case glslang::EvqVaryingOut: return spv::DecorationBlock; + case glslang::EvqShared: return spv::DecorationBlock; + case glslang::EvqPayload: return spv::DecorationBlock; + case glslang::EvqPayloadIn: return spv::DecorationBlock; + case glslang::EvqHitAttr: return spv::DecorationBlock; + case glslang::EvqCallableData: return spv::DecorationBlock; + case glslang::EvqCallableDataIn: return spv::DecorationBlock; + case glslang::EvqHitObjectAttrNV: return spv::DecorationBlock; + default: + assert(0); + break; + } + + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V memory decorations. +void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector& memory, + bool useVulkanMemoryModel) +{ + if (!useVulkanMemoryModel) { + if (qualifier.isVolatile()) { + memory.push_back(spv::DecorationVolatile); + memory.push_back(spv::DecorationCoherent); + } else if (qualifier.isCoherent()) { + memory.push_back(spv::DecorationCoherent); + } + } + if (qualifier.isRestrict()) + memory.push_back(spv::DecorationRestrict); + if (qualifier.isReadOnly()) + memory.push_back(spv::DecorationNonWritable); + if (qualifier.isWriteOnly()) + memory.push_back(spv::DecorationNonReadable); +} + +// Translate glslang type to SPIR-V layout decorations. +spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout) +{ + if (type.isMatrix()) { + switch (matrixLayout) { + case glslang::ElmRowMajor: + return spv::DecorationRowMajor; + case glslang::ElmColumnMajor: + return spv::DecorationColMajor; + default: + // opaque layouts don't need a majorness + return spv::DecorationMax; + } + } else { + switch (type.getBasicType()) { + default: + return spv::DecorationMax; + break; + case glslang::EbtBlock: + switch (type.getQualifier().storage) { + case glslang::EvqShared: + case glslang::EvqUniform: + case glslang::EvqBuffer: + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: + return spv::DecorationMax; + } + case glslang::EvqVaryingIn: + case glslang::EvqVaryingOut: + if (type.getQualifier().isTaskMemory()) { + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: break; + } + } else { + assert(type.getQualifier().layoutPacking == glslang::ElpNone); + } + return spv::DecorationMax; + case glslang::EvqPayload: + case glslang::EvqPayloadIn: + case glslang::EvqHitAttr: + case glslang::EvqCallableData: + case glslang::EvqCallableDataIn: + case glslang::EvqHitObjectAttrNV: + return spv::DecorationMax; + default: + assert(0); + return spv::DecorationMax; + } + } + } +} + +// Translate glslang type to SPIR-V interpolation decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.smooth) + // Smooth decoration doesn't exist in SPIR-V 1.0 + return spv::DecorationMax; + else if (qualifier.isNonPerspective()) + return spv::DecorationNoPerspective; + else if (qualifier.flat) + return spv::DecorationFlat; + else if (qualifier.isExplicitInterpolation()) { + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::DecorationExplicitInterpAMD; + } + else + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V auxiliary storage decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.centroid) + return spv::DecorationCentroid; + else if (qualifier.patch) + return spv::DecorationPatch; + else if (qualifier.sample) { + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::DecorationSample; + } + + return spv::DecorationMax; +} + +// If glslang type is invariant, return SPIR-V invariant decoration. +spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.invariant) + return spv::DecorationInvariant; + else + return spv::DecorationMax; +} + +// If glslang type is noContraction, return SPIR-V NoContraction decoration. +spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.isNoContraction()) + return spv::DecorationNoContraction; + else + return spv::DecorationMax; +} + +// If glslang type is nonUniform, return SPIR-V NonUniform decoration. +spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.isNonUniform()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderNonUniformEXT); + return spv::DecorationNonUniformEXT; + } else + return spv::DecorationMax; +} + +// If lvalue flags contains nonUniform, return SPIR-V NonUniform decoration. +spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration( + const spv::Builder::AccessChain::CoherentFlags& coherentFlags) +{ + if (coherentFlags.isNonUniform()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderNonUniformEXT); + return spv::DecorationNonUniformEXT; + } else + return spv::DecorationMax; +} + +spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone; + + if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage) + return mask; + + if (coherentFlags.isVolatile() || coherentFlags.anyCoherent()) { + mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask; + } + + if (coherentFlags.nonprivate) { + mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::MemoryAccessVolatileMask; + } + if (coherentFlags.nontemporal) { + mask = mask | spv::MemoryAccessNontemporalMask; + } + if (mask != spv::MemoryAccessMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + return mask; +} + +spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + + if (!glslangIntermediate->usingVulkanMemoryModel()) + return mask; + + if (coherentFlags.volatil || + coherentFlags.anyCoherent()) { + mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask | + spv::ImageOperandsMakeTexelVisibleKHRMask; + } + if (coherentFlags.nonprivate) { + mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::ImageOperandsVolatileTexelKHRMask; + } + if (coherentFlags.nontemporal && builder.getSpvVersion() >= spv::Spv_1_6) { + mask = mask | spv::ImageOperandsNontemporalMask; + } + if (mask != spv::ImageOperandsMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + return mask; +} + +spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type) +{ + spv::Builder::AccessChain::CoherentFlags flags = {}; + flags.coherent = type.getQualifier().coherent; + flags.devicecoherent = type.getQualifier().devicecoherent; + flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent; + // shared variables are implicitly workgroupcoherent in GLSL. + flags.workgroupcoherent = type.getQualifier().workgroupcoherent || + type.getQualifier().storage == glslang::EvqShared; + flags.subgroupcoherent = type.getQualifier().subgroupcoherent; + flags.shadercallcoherent = type.getQualifier().shadercallcoherent; + flags.volatil = type.getQualifier().volatil; + flags.nontemporal = type.getQualifier().nontemporal; + // *coherent variables are implicitly nonprivate in GLSL + flags.nonprivate = type.getQualifier().nonprivate || + flags.anyCoherent() || + flags.volatil; + flags.isImage = type.getBasicType() == glslang::EbtSampler; + flags.nonUniform = type.getQualifier().nonUniform; + return flags; +} + +spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::Scope scope = spv::ScopeMax; + + if (coherentFlags.volatil || coherentFlags.coherent) { + // coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model + scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + } else if (coherentFlags.devicecoherent) { + scope = spv::ScopeDevice; + } else if (coherentFlags.queuefamilycoherent) { + scope = spv::ScopeQueueFamilyKHR; + } else if (coherentFlags.workgroupcoherent) { + scope = spv::ScopeWorkgroup; + } else if (coherentFlags.subgroupcoherent) { + scope = spv::ScopeSubgroup; + } else if (coherentFlags.shadercallcoherent) { + scope = spv::ScopeShaderCallKHR; + } + if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + + return scope; +} + +// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate +// associated capabilities when required. For some built-in variables, a capability +// is generated only when using the variable in an executable instruction, but not when +// just declaring a struct member variable with it. This is true for PointSize, +// ClipDistance, and CullDistance. +spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, + bool memberDeclaration) +{ + switch (builtIn) { + case glslang::EbvPointSize: + // Defer adding the capability until the built-in is actually used. + if (! memberDeclaration) { + switch (glslangIntermediate->getStage()) { + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometryPointSize); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + builder.addCapability(spv::CapabilityTessellationPointSize); + break; + default: + break; + } + } + return spv::BuiltInPointSize; + + case glslang::EbvPosition: return spv::BuiltInPosition; + case glslang::EbvVertexId: return spv::BuiltInVertexId; + case glslang::EbvInstanceId: return spv::BuiltInInstanceId; + case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex; + case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex; + + case glslang::EbvFragCoord: return spv::BuiltInFragCoord; + case glslang::EbvPointCoord: return spv::BuiltInPointCoord; + case glslang::EbvFace: return spv::BuiltInFrontFacing; + case glslang::EbvFragDepth: return spv::BuiltInFragDepth; + + case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups; + case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize; + case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId; + case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId; + case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex; + case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId; + + // These *Distance capabilities logically belong here, but if the member is declared and + // then never used, consumers of SPIR-V prefer the capability not be declared. + // They are now generated when used, rather than here when declared. + // Potentially, the specification should be more clear what the minimum + // use needed is to trigger the capability. + // + case glslang::EbvClipDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityClipDistance); + return spv::BuiltInClipDistance; + + case glslang::EbvCullDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityCullDistance); + return spv::BuiltInCullDistance; + + case glslang::EbvViewportIndex: + if (glslangIntermediate->getStage() == EShLangGeometry || + glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityMultiViewport); + } + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + if (builder.getSpvVersion() < spv::Spv_1_5) { + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } + else + builder.addCapability(spv::CapabilityShaderViewportIndex); + } + return spv::BuiltInViewportIndex; + + case glslang::EbvSampleId: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSampleId; + + case glslang::EbvSamplePosition: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSamplePosition; + + case glslang::EbvSampleMask: + return spv::BuiltInSampleMask; + + case glslang::EbvLayer: + if (glslangIntermediate->getStage() == EShLangMesh) { + return spv::BuiltInLayer; + } + if (glslangIntermediate->getStage() == EShLangGeometry || + glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityGeometry); + } + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + if (builder.getSpvVersion() < spv::Spv_1_5) { + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } else + builder.addCapability(spv::CapabilityShaderLayer); + } + return spv::BuiltInLayer; + + case glslang::EbvBaseVertex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseVertex; + + case glslang::EbvBaseInstance: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseInstance; + + case glslang::EbvDrawId: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInDrawIndex; + + case glslang::EbvPrimitiveId: + if (glslangIntermediate->getStage() == EShLangFragment) + builder.addCapability(spv::CapabilityGeometry); + return spv::BuiltInPrimitiveId; + + case glslang::EbvFragStencilRef: + builder.addExtension(spv::E_SPV_EXT_shader_stencil_export); + builder.addCapability(spv::CapabilityStencilExportEXT); + return spv::BuiltInFragStencilRefEXT; + + case glslang::EbvShadingRateKHR: + builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); + builder.addCapability(spv::CapabilityFragmentShadingRateKHR); + return spv::BuiltInShadingRateKHR; + + case glslang::EbvPrimitiveShadingRateKHR: + builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); + builder.addCapability(spv::CapabilityFragmentShadingRateKHR); + return spv::BuiltInPrimitiveShadingRateKHR; + + case glslang::EbvInvocationId: return spv::BuiltInInvocationId; + case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner; + case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter; + case glslang::EbvTessCoord: return spv::BuiltInTessCoord; + case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices; + case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation; + + case glslang::EbvSubGroupSize: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubGroupInvocation: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubGroupEqMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubGroupGeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubGroupGtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubGroupLeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubGroupLtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvNumSubgroups: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInNumSubgroups; + + case glslang::EbvSubgroupID: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupId; + + case glslang::EbvSubgroupSize2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubgroupInvocation2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubgroupEqMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubgroupGeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubgroupGtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubgroupLeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubgroupLtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvBaryCoordNoPersp: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspAMD; + + case glslang::EbvBaryCoordNoPerspCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspCentroidAMD; + + case glslang::EbvBaryCoordNoPerspSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspSampleAMD; + + case glslang::EbvBaryCoordSmooth: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothAMD; + + case glslang::EbvBaryCoordSmoothCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothCentroidAMD; + + case glslang::EbvBaryCoordSmoothSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothSampleAMD; + + case glslang::EbvBaryCoordPullModel: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordPullModelAMD; + + case glslang::EbvDeviceIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_device_group, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDeviceGroup); + return spv::BuiltInDeviceIndex; + + case glslang::EbvViewIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_multiview, spv::Spv_1_3); + builder.addCapability(spv::CapabilityMultiView); + return spv::BuiltInViewIndex; + + case glslang::EbvFragSizeEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragSizeEXT; + + case glslang::EbvFragInvocationCountEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragInvocationCountEXT; + + case glslang::EbvViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_viewport_array2); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + } + return spv::BuiltInViewportMaskNV; + case glslang::EbvSecondaryPositionNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryPositionNV; + case glslang::EbvSecondaryViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryViewportMaskNV; + case glslang::EbvPositionPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInPositionPerViewNV; + case glslang::EbvViewportMaskPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInViewportMaskPerViewNV; + case glslang::EbvFragFullyCoveredNV: + builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered); + builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT); + return spv::BuiltInFullyCoveredEXT; + case glslang::EbvFragmentSizeNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInFragmentSizeNV; + case glslang::EbvInvocationsPerPixelNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInInvocationsPerPixelNV; + + // ray tracing + case glslang::EbvLaunchId: + return spv::BuiltInLaunchIdKHR; + case glslang::EbvLaunchSize: + return spv::BuiltInLaunchSizeKHR; + case glslang::EbvWorldRayOrigin: + return spv::BuiltInWorldRayOriginKHR; + case glslang::EbvWorldRayDirection: + return spv::BuiltInWorldRayDirectionKHR; + case glslang::EbvObjectRayOrigin: + return spv::BuiltInObjectRayOriginKHR; + case glslang::EbvObjectRayDirection: + return spv::BuiltInObjectRayDirectionKHR; + case glslang::EbvRayTmin: + return spv::BuiltInRayTminKHR; + case glslang::EbvRayTmax: + return spv::BuiltInRayTmaxKHR; + case glslang::EbvCullMask: + return spv::BuiltInCullMaskKHR; + case glslang::EbvPositionFetch: + return spv::BuiltInHitTriangleVertexPositionsKHR; + case glslang::EbvInstanceCustomIndex: + return spv::BuiltInInstanceCustomIndexKHR; + case glslang::EbvHitKind: + return spv::BuiltInHitKindKHR; + case glslang::EbvObjectToWorld: + case glslang::EbvObjectToWorld3x4: + return spv::BuiltInObjectToWorldKHR; + case glslang::EbvWorldToObject: + case glslang::EbvWorldToObject3x4: + return spv::BuiltInWorldToObjectKHR; + case glslang::EbvIncomingRayFlags: + return spv::BuiltInIncomingRayFlagsKHR; + case glslang::EbvGeometryIndex: + return spv::BuiltInRayGeometryIndexKHR; + case glslang::EbvCurrentRayTimeNV: + builder.addExtension(spv::E_SPV_NV_ray_tracing_motion_blur); + builder.addCapability(spv::CapabilityRayTracingMotionBlurNV); + return spv::BuiltInCurrentRayTimeNV; + case glslang::EbvMicroTrianglePositionNV: + builder.addCapability(spv::CapabilityRayTracingDisplacementMicromapNV); + builder.addExtension("SPV_NV_displacement_micromap"); + return spv::BuiltInHitMicroTriangleVertexPositionsNV; + case glslang::EbvMicroTriangleBaryNV: + builder.addCapability(spv::CapabilityRayTracingDisplacementMicromapNV); + builder.addExtension("SPV_NV_displacement_micromap"); + return spv::BuiltInHitMicroTriangleVertexBarycentricsNV; + case glslang::EbvHitKindFrontFacingMicroTriangleNV: + builder.addCapability(spv::CapabilityRayTracingDisplacementMicromapNV); + builder.addExtension("SPV_NV_displacement_micromap"); + return spv::BuiltInHitKindFrontFacingMicroTriangleNV; + case glslang::EbvHitKindBackFacingMicroTriangleNV: + builder.addCapability(spv::CapabilityRayTracingDisplacementMicromapNV); + builder.addExtension("SPV_NV_displacement_micromap"); + return spv::BuiltInHitKindBackFacingMicroTriangleNV; + case glslang::EbvClusterIDNV: + builder.addCapability(spv::CapabilityRayTracingClusterAccelerationStructureNV); + builder.addExtension("SPV_NV_cluster_acceleration_structure"); + return spv::BuiltInClusterIDNV; + case glslang::EbvHitIsSphereNV: + builder.addCapability(spv::CapabilityRayTracingSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitIsSphereNV; + case glslang::EbvHitIsLSSNV: + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitIsLSSNV; + case glslang::EbvHitSpherePositionNV: + builder.addCapability(spv::CapabilityRayTracingSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitSpherePositionNV; + case glslang::EbvHitSphereRadiusNV: + builder.addCapability(spv::CapabilityRayTracingSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitSphereRadiusNV; + case glslang::EbvHitLSSPositionsNV: + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitLSSPositionsNV; + case glslang::EbvHitLSSRadiiNV: + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + builder.addExtension("SPV_NV_linear_swept_spheres"); + return spv::BuiltInHitLSSRadiiNV; + + // barycentrics + case glslang::EbvBaryCoordNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNV; + case glslang::EbvBaryCoordNoPerspNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNoPerspNV; + + case glslang::EbvBaryCoordEXT: + builder.addExtension(spv::E_SPV_KHR_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricKHR); + return spv::BuiltInBaryCoordKHR; + case glslang::EbvBaryCoordNoPerspEXT: + builder.addExtension(spv::E_SPV_KHR_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricKHR); + return spv::BuiltInBaryCoordNoPerspKHR; + + // mesh shaders + case glslang::EbvTaskCountNV: + return spv::BuiltInTaskCountNV; + case glslang::EbvPrimitiveCountNV: + return spv::BuiltInPrimitiveCountNV; + case glslang::EbvPrimitiveIndicesNV: + return spv::BuiltInPrimitiveIndicesNV; + case glslang::EbvClipDistancePerViewNV: + return spv::BuiltInClipDistancePerViewNV; + case glslang::EbvCullDistancePerViewNV: + return spv::BuiltInCullDistancePerViewNV; + case glslang::EbvLayerPerViewNV: + return spv::BuiltInLayerPerViewNV; + case glslang::EbvMeshViewCountNV: + return spv::BuiltInMeshViewCountNV; + case glslang::EbvMeshViewIndicesNV: + return spv::BuiltInMeshViewIndicesNV; + + // SPV_EXT_mesh_shader + case glslang::EbvPrimitivePointIndicesEXT: + return spv::BuiltInPrimitivePointIndicesEXT; + case glslang::EbvPrimitiveLineIndicesEXT: + return spv::BuiltInPrimitiveLineIndicesEXT; + case glslang::EbvPrimitiveTriangleIndicesEXT: + return spv::BuiltInPrimitiveTriangleIndicesEXT; + case glslang::EbvCullPrimitiveEXT: + return spv::BuiltInCullPrimitiveEXT; + + // sm builtins + case glslang::EbvWarpsPerSM: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpsPerSMNV; + case glslang::EbvSMCount: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMCountNV; + case glslang::EbvWarpID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpIDNV; + case glslang::EbvSMID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMIDNV; + + // ARM builtins + case glslang::EbvCoreCountARM: + builder.addExtension(spv::E_SPV_ARM_core_builtins); + builder.addCapability(spv::CapabilityCoreBuiltinsARM); + return spv::BuiltInCoreCountARM; + case glslang::EbvCoreIDARM: + builder.addExtension(spv::E_SPV_ARM_core_builtins); + builder.addCapability(spv::CapabilityCoreBuiltinsARM); + return spv::BuiltInCoreIDARM; + case glslang::EbvCoreMaxIDARM: + builder.addExtension(spv::E_SPV_ARM_core_builtins); + builder.addCapability(spv::CapabilityCoreBuiltinsARM); + return spv::BuiltInCoreMaxIDARM; + case glslang::EbvWarpIDARM: + builder.addExtension(spv::E_SPV_ARM_core_builtins); + builder.addCapability(spv::CapabilityCoreBuiltinsARM); + return spv::BuiltInWarpIDARM; + case glslang::EbvWarpMaxIDARM: + builder.addExtension(spv::E_SPV_ARM_core_builtins); + builder.addCapability(spv::CapabilityCoreBuiltinsARM); + return spv::BuiltInWarpMaxIDARM; + + default: + return spv::BuiltInMax; + } +} + +// Translate glslang image layout format to SPIR-V image format. +spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type) +{ + assert(type.getBasicType() == glslang::EbtSampler); + + // Check for capabilities + switch (type.getQualifier().getFormat()) { + case glslang::ElfRg32f: + case glslang::ElfRg16f: + case glslang::ElfR11fG11fB10f: + case glslang::ElfR16f: + case glslang::ElfRgba16: + case glslang::ElfRgb10A2: + case glslang::ElfRg16: + case glslang::ElfRg8: + case glslang::ElfR16: + case glslang::ElfR8: + case glslang::ElfRgba16Snorm: + case glslang::ElfRg16Snorm: + case glslang::ElfRg8Snorm: + case glslang::ElfR16Snorm: + case glslang::ElfR8Snorm: + + case glslang::ElfRg32i: + case glslang::ElfRg16i: + case glslang::ElfRg8i: + case glslang::ElfR16i: + case glslang::ElfR8i: + + case glslang::ElfRgb10a2ui: + case glslang::ElfRg32ui: + case glslang::ElfRg16ui: + case glslang::ElfRg8ui: + case glslang::ElfR16ui: + case glslang::ElfR8ui: + builder.addCapability(spv::CapabilityStorageImageExtendedFormats); + break; + + case glslang::ElfR64ui: + case glslang::ElfR64i: + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityInt64ImageEXT); + break; + default: + break; + } + + // do the translation + switch (type.getQualifier().getFormat()) { + case glslang::ElfNone: return spv::ImageFormatUnknown; + case glslang::ElfRgba32f: return spv::ImageFormatRgba32f; + case glslang::ElfRgba16f: return spv::ImageFormatRgba16f; + case glslang::ElfR32f: return spv::ImageFormatR32f; + case glslang::ElfRgba8: return spv::ImageFormatRgba8; + case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm; + case glslang::ElfRg32f: return spv::ImageFormatRg32f; + case glslang::ElfRg16f: return spv::ImageFormatRg16f; + case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f; + case glslang::ElfR16f: return spv::ImageFormatR16f; + case glslang::ElfRgba16: return spv::ImageFormatRgba16; + case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2; + case glslang::ElfRg16: return spv::ImageFormatRg16; + case glslang::ElfRg8: return spv::ImageFormatRg8; + case glslang::ElfR16: return spv::ImageFormatR16; + case glslang::ElfR8: return spv::ImageFormatR8; + case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm; + case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm; + case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm; + case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm; + case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm; + case glslang::ElfRgba32i: return spv::ImageFormatRgba32i; + case glslang::ElfRgba16i: return spv::ImageFormatRgba16i; + case glslang::ElfRgba8i: return spv::ImageFormatRgba8i; + case glslang::ElfR32i: return spv::ImageFormatR32i; + case glslang::ElfRg32i: return spv::ImageFormatRg32i; + case glslang::ElfRg16i: return spv::ImageFormatRg16i; + case glslang::ElfRg8i: return spv::ImageFormatRg8i; + case glslang::ElfR16i: return spv::ImageFormatR16i; + case glslang::ElfR8i: return spv::ImageFormatR8i; + case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui; + case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui; + case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui; + case glslang::ElfR32ui: return spv::ImageFormatR32ui; + case glslang::ElfRg32ui: return spv::ImageFormatRg32ui; + case glslang::ElfRg16ui: return spv::ImageFormatRg16ui; + case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui; + case glslang::ElfRg8ui: return spv::ImageFormatRg8ui; + case glslang::ElfR16ui: return spv::ImageFormatR16ui; + case glslang::ElfR8ui: return spv::ImageFormatR8ui; + case glslang::ElfR64ui: return spv::ImageFormatR64ui; + case glslang::ElfR64i: return spv::ImageFormatR64i; + default: return spv::ImageFormatMax; + } +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl( + const glslang::TIntermSelection& selectionNode) const +{ + if (selectionNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (selectionNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) + const +{ + if (switchNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (switchNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +// return a non-0 dependency if the dependency argument must be set +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, + std::vector& operands) const +{ + spv::LoopControlMask control = spv::LoopControlMaskNone; + + if (loopNode.getDontUnroll()) + control = control | spv::LoopControlDontUnrollMask; + if (loopNode.getUnroll()) + control = control | spv::LoopControlUnrollMask; + if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite) + control = control | spv::LoopControlDependencyInfiniteMask; + else if (loopNode.getLoopDependency() > 0) { + control = control | spv::LoopControlDependencyLengthMask; + operands.push_back((unsigned int)loopNode.getLoopDependency()); + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + if (loopNode.getMinIterations() > 0) { + control = control | spv::LoopControlMinIterationsMask; + operands.push_back(loopNode.getMinIterations()); + } + if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) { + control = control | spv::LoopControlMaxIterationsMask; + operands.push_back(loopNode.getMaxIterations()); + } + if (loopNode.getIterationMultiple() > 1) { + control = control | spv::LoopControlIterationMultipleMask; + operands.push_back(loopNode.getIterationMultiple()); + } + if (loopNode.getPeelCount() > 0) { + control = control | spv::LoopControlPeelCountMask; + operands.push_back(loopNode.getPeelCount()); + } + if (loopNode.getPartialCount() > 0) { + control = control | spv::LoopControlPartialCountMask; + operands.push_back(loopNode.getPartialCount()); + } + } + + return control; +} + +// Translate glslang type to SPIR-V storage class. +spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type) +{ + if (type.getBasicType() == glslang::EbtRayQuery || type.getBasicType() == glslang::EbtHitObjectNV) + return spv::StorageClassPrivate; + if (type.getQualifier().isSpirvByReference()) { + if (type.getQualifier().isParamInput() || type.getQualifier().isParamOutput()) + return spv::StorageClassFunction; + } + if (type.getQualifier().isPipeInput()) + return spv::StorageClassInput; + if (type.getQualifier().isPipeOutput()) + return spv::StorageClassOutput; + if (type.getQualifier().storage == glslang::EvqTileImageEXT || type.isAttachmentEXT()) { + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + builder.addCapability(spv::CapabilityTileImageColorReadAccessEXT); + return spv::StorageClassTileImageEXT; + } + + if (glslangIntermediate->getSource() != glslang::EShSourceHlsl || + type.getQualifier().storage == glslang::EvqUniform) { + if (type.isAtomic()) + return spv::StorageClassAtomicCounter; + if (type.containsOpaque() && !glslangIntermediate->getBindlessMode()) + return spv::StorageClassUniformConstant; + } + + if (type.getQualifier().isUniformOrBuffer() && + type.getQualifier().isShaderRecord()) { + return spv::StorageClassShaderRecordBufferKHR; + } + + if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_storage_buffer_storage_class, spv::Spv_1_3); + return spv::StorageClassStorageBuffer; + } + + if (type.getQualifier().isUniformOrBuffer()) { + if (type.getQualifier().isPushConstant()) + return spv::StorageClassPushConstant; + if (type.getBasicType() == glslang::EbtBlock) + return spv::StorageClassUniform; + return spv::StorageClassUniformConstant; + } + + if (type.getQualifier().storage == glslang::EvqShared && type.getBasicType() == glslang::EbtBlock) { + builder.addExtension(spv::E_SPV_KHR_workgroup_memory_explicit_layout); + builder.addCapability(spv::CapabilityWorkgroupMemoryExplicitLayoutKHR); + return spv::StorageClassWorkgroup; + } + + switch (type.getQualifier().storage) { + case glslang::EvqGlobal: return spv::StorageClassPrivate; + case glslang::EvqConstReadOnly: return spv::StorageClassFunction; + case glslang::EvqTemporary: return spv::StorageClassFunction; + case glslang::EvqShared: return spv::StorageClassWorkgroup; + case glslang::EvqPayload: return spv::StorageClassRayPayloadKHR; + case glslang::EvqPayloadIn: return spv::StorageClassIncomingRayPayloadKHR; + case glslang::EvqHitAttr: return spv::StorageClassHitAttributeKHR; + case glslang::EvqCallableData: return spv::StorageClassCallableDataKHR; + case glslang::EvqCallableDataIn: return spv::StorageClassIncomingCallableDataKHR; + case glslang::EvqtaskPayloadSharedEXT : return spv::StorageClassTaskPayloadWorkgroupEXT; + case glslang::EvqHitObjectAttrNV: return spv::StorageClassHitObjectAttributeNV; + case glslang::EvqSpirvStorageClass: return static_cast(type.getQualifier().spirvStorageClass); + default: + assert(0); + break; + } + + return spv::StorageClassFunction; +} + +// Translate glslang constants to SPIR-V literals +void TGlslangToSpvTraverser::TranslateLiterals(const glslang::TVector& constants, + std::vector& literals) const +{ + for (auto constant : constants) { + if (constant->getBasicType() == glslang::EbtFloat) { + float floatValue = static_cast(constant->getConstArray()[0].getDConst()); + unsigned literal; + static_assert(sizeof(literal) == sizeof(floatValue), "sizeof(unsigned) != sizeof(float)"); + memcpy(&literal, &floatValue, sizeof(literal)); + literals.push_back(literal); + } else if (constant->getBasicType() == glslang::EbtInt) { + unsigned literal = constant->getConstArray()[0].getIConst(); + literals.push_back(literal); + } else if (constant->getBasicType() == glslang::EbtUint) { + unsigned literal = constant->getConstArray()[0].getUConst(); + literals.push_back(literal); + } else if (constant->getBasicType() == glslang::EbtBool) { + unsigned literal = constant->getConstArray()[0].getBConst(); + literals.push_back(literal); + } else if (constant->getBasicType() == glslang::EbtString) { + auto str = constant->getConstArray()[0].getSConst()->c_str(); + unsigned literal = 0; + char* literalPtr = reinterpret_cast(&literal); + unsigned charCount = 0; + char ch = 0; + do { + ch = *(str++); + *(literalPtr++) = ch; + ++charCount; + if (charCount == 4) { + literals.push_back(literal); + literalPtr = reinterpret_cast(&literal); + charCount = 0; + } + } while (ch != 0); + + // Partial literal is padded with 0 + if (charCount > 0) { + for (; charCount < 4; ++charCount) + *(literalPtr++) = 0; + literals.push_back(literal); + } + } else + assert(0); // Unexpected type + } +} + +// Add capabilities pertaining to how an array is indexed. +void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType, + const glslang::TType& indexType) +{ + if (indexType.getQualifier().isNonUniform()) { + // deal with an asserted non-uniform index + // SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) + builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT); + else if (baseType.isImage() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isTexture() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isImage()) + builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT); + else if (baseType.isTexture()) + builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT); + } else if (baseType.getBasicType() == glslang::EbtBlock) { + if (baseType.getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT); + else if (baseType.getQualifier().storage == glslang::EvqUniform) + builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT); + } + } else { + // assume a dynamically uniform index + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT); + } else if (baseType.isImage() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT); + } else if (baseType.isTexture() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT); + } + } + } +} + +// Return whether or not the given type is something that should be tied to a +// descriptor set. +bool IsDescriptorResource(const glslang::TType& type) +{ + // uniform and buffer blocks are included, unless it is a push_constant + if (type.getBasicType() == glslang::EbtBlock) + return type.getQualifier().isUniformOrBuffer() && + ! type.getQualifier().isShaderRecord() && + ! type.getQualifier().isPushConstant(); + + // non block... + // basically samplerXXX/subpass/sampler/texture are all included + // if they are the global-scope-class, not the function parameter + // (or local, if they ever exist) class. + if (type.getBasicType() == glslang::EbtSampler || + type.getBasicType() == glslang::EbtAccStruct) + return type.getQualifier().isUniformOrBuffer(); + + // None of the above. + return false; +} + +void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent) +{ + if (child.layoutMatrix == glslang::ElmNone) + child.layoutMatrix = parent.layoutMatrix; + + if (parent.invariant) + child.invariant = true; + if (parent.flat) + child.flat = true; + if (parent.centroid) + child.centroid = true; + if (parent.nopersp) + child.nopersp = true; + if (parent.explicitInterp) + child.explicitInterp = true; + if (parent.perPrimitiveNV) + child.perPrimitiveNV = true; + if (parent.perViewNV) + child.perViewNV = true; + if (parent.perTaskNV) + child.perTaskNV = true; + if (parent.storage == glslang::EvqtaskPayloadSharedEXT) + child.storage = glslang::EvqtaskPayloadSharedEXT; + if (parent.patch) + child.patch = true; + if (parent.sample) + child.sample = true; + if (parent.coherent) + child.coherent = true; + if (parent.devicecoherent) + child.devicecoherent = true; + if (parent.queuefamilycoherent) + child.queuefamilycoherent = true; + if (parent.workgroupcoherent) + child.workgroupcoherent = true; + if (parent.subgroupcoherent) + child.subgroupcoherent = true; + if (parent.shadercallcoherent) + child.shadercallcoherent = true; + if (parent.nonprivate) + child.nonprivate = true; + if (parent.volatil) + child.volatil = true; + if (parent.nontemporal) + child.nontemporal = true; + if (parent.restrict) + child.restrict = true; + if (parent.readonly) + child.readonly = true; + if (parent.writeonly) + child.writeonly = true; + if (parent.nonUniform) + child.nonUniform = true; +} + +bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier) +{ + // This should list qualifiers that simultaneous satisfy: + // - struct members might inherit from a struct declaration + // (note that non-block structs don't explicitly inherit, + // only implicitly, meaning no decoration involved) + // - affect decorations on the struct members + // (note smooth does not, and expecting something like volatile + // to effect the whole object) + // - are not part of the offset/st430/etc or row/column-major layout + return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock); +} + +// +// Implement the TGlslangToSpvTraverser class. +// + +TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, + const glslang::TIntermediate* glslangIntermediate, + spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) : + TIntermTraverser(true, false, true), + options(options), + shaderEntry(nullptr), currentFunction(nullptr), + sequenceDepth(0), logger(buildLogger), + builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger), + inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), + glslangIntermediate(glslangIntermediate), + nanMinMaxClamp(glslangIntermediate->getNanMinMaxClamp()), + nonSemanticDebugPrintf(0), + taskPayloadID(0) +{ + bool isMeshShaderExt = (glslangIntermediate->getRequestedExtensions().find(glslang::E_GL_EXT_mesh_shader) != + glslangIntermediate->getRequestedExtensions().end()); + spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage(), isMeshShaderExt); + + builder.clearAccessChain(); + builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), + glslangIntermediate->getVersion()); + + if (options.emitNonSemanticShaderDebugSource) + this->options.emitNonSemanticShaderDebugInfo = true; + if (options.emitNonSemanticShaderDebugInfo) + this->options.generateDebugInfo = true; + + if (this->options.generateDebugInfo) { + if (this->options.emitNonSemanticShaderDebugInfo) { + builder.setEmitNonSemanticShaderDebugInfo(this->options.emitNonSemanticShaderDebugSource); + } + else { + builder.setEmitSpirvDebugInfo(); + } + builder.setDebugMainSourceFile(glslangIntermediate->getSourceFile()); + + // Set the source shader's text. If for SPV version 1.0, include + // a preamble in comments stating the OpModuleProcessed instructions. + // Otherwise, emit those as actual instructions. + std::string text; + const std::vector& processes = glslangIntermediate->getProcesses(); + for (int p = 0; p < (int)processes.size(); ++p) { + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) { + text.append("// OpModuleProcessed "); + text.append(processes[p]); + text.append("\n"); + } else + builder.addModuleProcessed(processes[p]); + } + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0) + text.append("#line 1\n"); + text.append(glslangIntermediate->getSourceText()); + builder.setSourceText(text); + // Pass name and text for all included files + const std::map& include_txt = glslangIntermediate->getIncludeText(); + for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr) + builder.addInclude(iItr->first, iItr->second); + } + + builder.setUseReplicatedComposites(glslangIntermediate->usingReplicatedComposites()); + + stdBuiltins = builder.import("GLSL.std.450"); + + spv::AddressingModel addressingModel = spv::AddressingModelLogical; + spv::MemoryModel memoryModel = spv::MemoryModelGLSL450; + + if (glslangIntermediate->usingPhysicalStorageBuffer()) { + addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT; + builder.addIncorporatedExtension(spv::E_SPV_KHR_physical_storage_buffer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT); + } + if (glslangIntermediate->usingVulkanMemoryModel()) { + memoryModel = spv::MemoryModelVulkanKHR; + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + builder.addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); + } + builder.setMemoryModel(addressingModel, memoryModel); + + if (glslangIntermediate->usingVariablePointers()) { + builder.addCapability(spv::CapabilityVariablePointers); + } + + // If not linking, there is no entry point + if (!options.compileOnly) { + shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); + entryPoint = + builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); + } + + // Add the source extensions + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it) + builder.addSourceExtension(it->c_str()); + + // Add the top-level modes for this shader. + + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb); + } + + if (glslangIntermediate->getLayoutPrimitiveCulling()) { + builder.addCapability(spv::CapabilityRayTraversalPrimitiveCullingKHR); + } + + if (glslangIntermediate->getSubgroupUniformControlFlow()) { + builder.addExtension(spv::E_SPV_KHR_subgroup_uniform_control_flow); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeSubgroupUniformControlFlowKHR); + } + if (glslangIntermediate->getMaximallyReconverges()) { + builder.addExtension(spv::E_SPV_KHR_maximal_reconvergence); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeMaximallyReconvergesKHR); + } + + if (glslangIntermediate->getQuadDerivMode()) + { + builder.addCapability(spv::CapabilityQuadControlKHR); + builder.addExtension(spv::E_SPV_KHR_quad_control); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeQuadDerivativesKHR); + } + + if (glslangIntermediate->getReqFullQuadsMode()) + { + builder.addCapability(spv::CapabilityQuadControlKHR); + builder.addExtension(spv::E_SPV_KHR_quad_control); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeRequireFullQuadsKHR); + } + + unsigned int mode; + switch (glslangIntermediate->getStage()) { + case EShLangVertex: + builder.addCapability(spv::CapabilityShader); + break; + + case EShLangFragment: + builder.addCapability(spv::CapabilityShader); + if (glslangIntermediate->getPixelCenterInteger()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger); + + if (glslangIntermediate->getOriginUpperLeft()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft); + else + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft); + + if (glslangIntermediate->getEarlyFragmentTests()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests); + + if (glslangIntermediate->getEarlyAndLateFragmentTestsAMD()) + { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyAndLateFragmentTestsAMD); + builder.addExtension(spv::E_SPV_AMD_shader_early_and_late_fragment_tests); + } + + if (glslangIntermediate->getPostDepthCoverage()) { + builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); + builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); + builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); + } + + if (glslangIntermediate->getNonCoherentColorAttachmentReadEXT()) { + builder.addCapability(spv::CapabilityTileImageColorReadAccessEXT); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeNonCoherentColorAttachmentReadEXT); + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + } + + if (glslangIntermediate->getNonCoherentDepthAttachmentReadEXT()) { + builder.addCapability(spv::CapabilityTileImageDepthReadAccessEXT); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeNonCoherentDepthAttachmentReadEXT); + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + } + + if (glslangIntermediate->getNonCoherentStencilAttachmentReadEXT()) { + builder.addCapability(spv::CapabilityTileImageStencilReadAccessEXT); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeNonCoherentStencilAttachmentReadEXT); + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + } + + if (glslangIntermediate->isDepthReplacing()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing); + + if (glslangIntermediate->isStencilReplacing()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeStencilRefReplacingEXT); + + switch(glslangIntermediate->getDepth()) { + case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; + case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break; + case glslang::EldUnchanged: mode = spv::ExecutionModeDepthUnchanged; break; + default: mode = spv::ExecutionModeMax; break; + } + + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getStencil()) { + case glslang::ElsRefUnchangedFrontAMD: mode = spv::ExecutionModeStencilRefUnchangedFrontAMD; break; + case glslang::ElsRefGreaterFrontAMD: mode = spv::ExecutionModeStencilRefGreaterFrontAMD; break; + case glslang::ElsRefLessFrontAMD: mode = spv::ExecutionModeStencilRefLessFrontAMD; break; + case glslang::ElsRefUnchangedBackAMD: mode = spv::ExecutionModeStencilRefUnchangedBackAMD; break; + case glslang::ElsRefGreaterBackAMD: mode = spv::ExecutionModeStencilRefGreaterBackAMD; break; + case glslang::ElsRefLessBackAMD: mode = spv::ExecutionModeStencilRefLessBackAMD; break; + default: mode = spv::ExecutionModeMax; break; + } + + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + switch (glslangIntermediate->getInterlockOrdering()) { + case glslang::EioPixelInterlockOrdered: mode = spv::ExecutionModePixelInterlockOrderedEXT; + break; + case glslang::EioPixelInterlockUnordered: mode = spv::ExecutionModePixelInterlockUnorderedEXT; + break; + case glslang::EioSampleInterlockOrdered: mode = spv::ExecutionModeSampleInterlockOrderedEXT; + break; + case glslang::EioSampleInterlockUnordered: mode = spv::ExecutionModeSampleInterlockUnorderedEXT; + break; + case glslang::EioShadingRateInterlockOrdered: mode = spv::ExecutionModeShadingRateInterlockOrderedEXT; + break; + case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT; + break; + default: mode = spv::ExecutionModeMax; + break; + } + if (mode != spv::ExecutionModeMax) { + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT || + mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT); + } else if (mode == spv::ExecutionModePixelInterlockOrderedEXT || + mode == spv::ExecutionModePixelInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT); + } else { + builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT); + } + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + } + break; + + case EShLangCompute: { + builder.addCapability(spv::CapabilityShader); + bool needSizeId = false; + for (int dim = 0; dim < 3; ++dim) { + if ((glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet)) { + needSizeId = true; + break; + } + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6 && needSizeId) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) { + builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, + glslangIntermediate->getLocalSizeSpecId(dim)); + needSizeId = true; + } + } + builder.addExecutionModeId(shaderEntry, spv::ExecutionModeLocalSizeId, dimConstId); + } else { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + } + if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } + break; + } + case EShLangTessEvaluation: + case EShLangTessControl: + builder.addCapability(spv::CapabilityTessellation); + + glslang::TLayoutGeometry primitive; + + if (glslangIntermediate->getStage() == EShLangTessControl) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + primitive = glslangIntermediate->getOutputPrimitive(); + } else { + primitive = glslangIntermediate->getInputPrimitive(); + } + + switch (primitive) { + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break; + case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexSpacing()) { + case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break; + case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break; + case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexOrder()) { + case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break; + case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + if (glslangIntermediate->getPointMode()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode); + break; + + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometry); + switch (glslangIntermediate->getInputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break; + case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break; + case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); + break; + + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + { + auto& extensions = glslangIntermediate->getRequestedExtensions(); + if (extensions.find("GL_EXT_opacity_micromap") != extensions.end()) { + builder.addCapability(spv::CapabilityRayTracingOpacityMicromapEXT); + builder.addExtension("SPV_EXT_opacity_micromap"); + } + if (extensions.find("GL_NV_ray_tracing") == extensions.end()) { + builder.addCapability(spv::CapabilityRayTracingKHR); + builder.addExtension("SPV_KHR_ray_tracing"); + } + else { + builder.addCapability(spv::CapabilityRayTracingNV); + builder.addExtension("SPV_NV_ray_tracing"); + } + if (glslangIntermediate->getStage() != EShLangRayGen && glslangIntermediate->getStage() != EShLangCallable) { + if (extensions.find("GL_EXT_ray_cull_mask") != extensions.end()) { + builder.addCapability(spv::CapabilityRayCullMaskKHR); + builder.addExtension("SPV_KHR_ray_cull_mask"); + } + if (extensions.find("GL_EXT_ray_tracing_position_fetch") != extensions.end()) { + builder.addCapability(spv::CapabilityRayTracingPositionFetchKHR); + builder.addExtension("SPV_KHR_ray_tracing_position_fetch"); + } + } + break; + } + case EShLangTask: + case EShLangMesh: + if(isMeshShaderExt) { + builder.addCapability(spv::CapabilityMeshShadingEXT); + builder.addExtension(spv::E_SPV_EXT_mesh_shader); + } else { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) { + builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, + glslangIntermediate->getLocalSizeSpecId(dim)); + } + } + builder.addExecutionModeId(shaderEntry, spv::ExecutionModeLocalSizeId, dimConstId); + } else { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + } + if (glslangIntermediate->getStage() == EShLangMesh) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, + glslangIntermediate->getPrimitives()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + } + break; + + default: + break; + } + + // + // Add SPIR-V requirements (GL_EXT_spirv_intrinsics) + // + if (glslangIntermediate->hasSpirvRequirement()) { + const glslang::TSpirvRequirement& spirvRequirement = glslangIntermediate->getSpirvRequirement(); + + // Add SPIR-V extension requirement + for (auto& extension : spirvRequirement.extensions) + builder.addExtension(extension.c_str()); + + // Add SPIR-V capability requirement + for (auto capability : spirvRequirement.capabilities) + builder.addCapability(static_cast(capability)); + } + + // + // Add SPIR-V execution mode qualifiers (GL_EXT_spirv_intrinsics) + // + if (glslangIntermediate->hasSpirvExecutionMode()) { + const glslang::TSpirvExecutionMode spirvExecutionMode = glslangIntermediate->getSpirvExecutionMode(); + + // Add spirv_execution_mode + for (auto& mode : spirvExecutionMode.modes) { + if (!mode.second.empty()) { + std::vector literals; + TranslateLiterals(mode.second, literals); + builder.addExecutionMode(shaderEntry, static_cast(mode.first), literals); + } else + builder.addExecutionMode(shaderEntry, static_cast(mode.first)); + } + + // Add spirv_execution_mode_id + for (auto& modeId : spirvExecutionMode.modeIds) { + std::vector operandIds; + assert(!modeId.second.empty()); + for (auto extraOperand : modeId.second) { + if (extraOperand->getType().getQualifier().isSpecConstant()) + operandIds.push_back(getSymbolId(extraOperand->getAsSymbolNode())); + else + operandIds.push_back(createSpvConstant(*extraOperand)); + } + builder.addExecutionModeId(shaderEntry, static_cast(modeId.first), operandIds); + } + } +} + +// Finish creating SPV, after the traversal is complete. +void TGlslangToSpvTraverser::finishSpv(bool compileOnly) +{ + // If not linking, an entry point is not expected + if (!compileOnly) { + // Finish the entry point function + if (!entryPointTerminated) { + builder.setBuildPoint(shaderEntry->getLastBlock()); + builder.leaveFunction(); + } + + // finish off the entry-point SPV instruction by adding the Input/Output + entryPoint->reserveOperands(iOSet.size()); + for (auto id : iOSet) + entryPoint->addIdOperand(id); + } + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + // Note: WebGPU code generation must have the opportunity to aggressively + // prune unreachable merge blocks and continue targets. + builder.postProcess(compileOnly); +} + +// Write the SPV into 'out'. +void TGlslangToSpvTraverser::dumpSpv(std::vector& out) +{ + builder.dump(out); +} + +// +// Implement the traversal functions. +// +// Return true from interior nodes to have the external traversal +// continue on to children. Return false if children were +// already processed. +// + +// +// Symbols can turn into +// - uniform/input reads +// - output writes +// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain +// - something simple that degenerates into the last bullet +// +void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) +{ + // We update the line information even though no code might be generated here + // This is helpful to yield correct lines for control flow instructions + if (!linkageOnly) { + builder.setDebugSourceLocation(symbol->getLoc().line, symbol->getLoc().getFilename()); + } + + if (symbol->getBasicType() == glslang::EbtFunction) { + return; + } + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (symbol->getType().isStruct()) + glslangTypeToIdMap[symbol->getType().getStruct()] = symbol->getId(); + + if (symbol->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); +#ifdef ENABLE_HLSL + // Skip symbol handling if it is string-typed + if (symbol->getBasicType() == glslang::EbtString) + return; +#endif + + // getSymbolId() will set up all the IO decorations on the first call. + // Formal function parameters were mapped during makeFunctions(). + spv::Id id = getSymbolId(symbol); + + if (symbol->getType().getQualifier().isTaskPayload()) + taskPayloadID = id; // cache the taskPayloadID to be used it as operand for OpEmitMeshTasksEXT + + if (builder.isPointer(id)) { + if (!symbol->getType().getQualifier().isParamInput() && + !symbol->getType().getQualifier().isParamOutput()) { + // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction + // Consider adding to the OpEntryPoint interface list. + // Only looking at structures if they have at least one member. + if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) { + spv::StorageClass sc = builder.getStorageClass(id); + // Before SPIR-V 1.4, we only want to include Input and Output. + // Starting with SPIR-V 1.4, we want all globals. + if ((glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4 && builder.isGlobalVariable(id)) || + (sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) { + iOSet.insert(id); + } + } + } + + // If the SPIR-V type is required to be different than the AST type + // (for ex SubgroupMasks or 3x4 ObjectToWorld/WorldToObject matrices), + // translate now from the SPIR-V type to the AST type, for the consuming + // operation. + // Note this turns it from an l-value to an r-value. + // Currently, all symbols needing this are inputs; avoid the map lookup when non-input. + if (symbol->getType().getQualifier().storage == glslang::EvqVaryingIn) + id = translateForcedType(id); + } + + // Only process non-linkage-only nodes for generating actual static uses + if (! linkageOnly || symbol->getQualifier().isSpecConstant()) { + // Prepare to generate code for the access + + // L-value chains will be computed left to right. We're on the symbol now, + // which is the left-most part of the access chain, so now is "clear" time, + // followed by setting the base. + builder.clearAccessChain(); + + // For now, we consider all user variables as being in memory, so they are pointers, + // except for + // A) R-Value arguments to a function, which are an intermediate object. + // See comments in handleUserFunctionCall(). + // B) Specialization constants (normal constants don't even come in as a variable), + // These are also pure R-values. + // C) R-Values from type translation, see above call to translateForcedType() + glslang::TQualifier qualifier = symbol->getQualifier(); + if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end() || + !builder.isPointerType(builder.getTypeId(id))) + builder.setAccessChainRValue(id); + else + builder.setAccessChainLValue(id); + } + +#ifdef ENABLE_HLSL + // Process linkage-only nodes for any special additional interface work. + if (linkageOnly) { + if (glslangIntermediate->getHlslFunctionality1()) { + // Map implicit counter buffers to their originating buffers, which should have been + // seen by now, given earlier pruning of unused counters, and preservation of order + // of declaration. + if (symbol->getType().getQualifier().isUniformOrBuffer()) { + if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) { + // Save possible originating buffers for counter buffers, keyed by + // making the potential counter-buffer name. + std::string keyName = symbol->getName().c_str(); + keyName = glslangIntermediate->addCounterBufferName(keyName); + counterOriginator[keyName] = symbol; + } else { + // Handle a counter buffer, by finding the saved originating buffer. + std::string keyName = symbol->getName().c_str(); + auto it = counterOriginator.find(keyName); + if (it != counterOriginator.end()) { + id = getSymbolId(it->second); + if (id != spv::NoResult) { + spv::Id counterId = getSymbolId(symbol); + if (counterId != spv::NoResult) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId); + } + } + } + } + } + } + } +#endif +} + +bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) +{ + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + if (node->getLeft()->getAsSymbolNode() != nullptr && node->getLeft()->getType().isStruct()) { + glslangTypeToIdMap[node->getLeft()->getType().getStruct()] = node->getLeft()->getAsSymbolNode()->getId(); + } + if (node->getRight()->getAsSymbolNode() != nullptr && node->getRight()->getType().isStruct()) { + glslangTypeToIdMap[node->getRight()->getType().getStruct()] = node->getRight()->getAsSymbolNode()->getId(); + } + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + // First, handle special cases + switch (node->getOp()) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + // A bin-op assign "a += b" means the same thing as "a = a + b" + // where a is evaluated before b. For a simple assignment, GLSL + // says to evaluate the left before the right. So, always, left + // node then right node. + { + // get the left l-value, save it away + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Builder::AccessChain lValue = builder.getAccessChain(); + + // evaluate the right + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id rValue = accessChainLoad(node->getRight()->getType()); + + // reset line number for assignment + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + if (node->getOp() != glslang::EOpAssign) { + // the left is also an r-value + builder.setAccessChain(lValue); + spv::Id leftRValue = accessChainLoad(node->getLeft()->getType()); + + // do the operation + spv::Builder::AccessChain::CoherentFlags coherentFlags = TranslateCoherent(node->getLeft()->getType()); + coherentFlags |= TranslateCoherent(node->getRight()->getType()); + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(coherentFlags) }; + rValue = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), leftRValue, rValue, + node->getType().getBasicType()); + + // these all need their counterparts in createBinaryOperation() + assert(rValue != spv::NoResult); + } + + // store the result + builder.setAccessChain(lValue); + multiTypeStore(node->getLeft()->getType(), rValue); + + // assignments are expressions having an rValue after they are evaluated... + builder.clearAccessChain(); + builder.setAccessChainRValue(rValue); + } + return false; + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + { + // Structure, array, matrix, or vector indirection with statically known index. + // Get the left part of the access chain. + node->getLeft()->traverse(this); + + // Add the next element in the chain + + const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (! node->getLeft()->getType().isArray() && + node->getLeft()->getType().isVector() && + node->getOp() == glslang::EOpIndexDirect) { + // Swizzle is uniform so propagate uniform into access chain + spv::Builder::AccessChain::CoherentFlags coherentFlags = TranslateCoherent(node->getLeft()->getType()); + coherentFlags.nonUniform = 0; + // This is essentially a hard-coded vector swizzle of size 1, + // so short circuit the access-chain stuff with a swizzle. + std::vector swizzle; + swizzle.push_back(glslangIndex); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + coherentFlags, + glslangIntermediate->getBaseAlignmentScalar( + node->getLeft()->getType(), dummySize)); + } else { + + // Load through a block reference is performed with a dot operator that + // is mapped to EOpIndexDirectStruct. When we get to the actual reference, + // do a load and reset the access chain. + if (node->getLeft()->isReference() && + !node->getLeft()->getType().isArray() && + node->getOp() == glslang::EOpIndexDirectStruct) + { + spv::Id left = accessChainLoad(node->getLeft()->getType()); + builder.clearAccessChain(); + builder.setAccessChainLValue(left); + } + + int spvIndex = glslangIndex; + if (node->getLeft()->getBasicType() == glslang::EbtBlock && + node->getOp() == glslang::EOpIndexDirectStruct) + { + // This may be, e.g., an anonymous block-member selection, which generally need + // index remapping due to hidden members in anonymous blocks. + long long glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()]; + if (memberRemapper.find(glslangId) != memberRemapper.end()) { + std::vector& remapper = memberRemapper[glslangId]; + assert(remapper.size() > 0); + spvIndex = remapper[glslangIndex]; + } + } + + // Struct reference propagates uniform lvalue + spv::Builder::AccessChain::CoherentFlags coherentFlags = + TranslateCoherent(node->getLeft()->getType()); + coherentFlags.nonUniform = 0; + + // normal case for indexing array or structure or block + builder.accessChainPush(builder.makeIntConstant(spvIndex), + coherentFlags, + node->getLeft()->getType().getBufferReferenceAlignment()); + + // Add capabilities here for accessing PointSize and clip/cull distance. + // We have deferred generation of associated capabilities until now. + if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray()) + declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex); + } + } + return false; + case glslang::EOpIndexIndirect: + { + // Array, matrix, or vector indirection with variable index. + // Will use native SPIR-V access-chain for and array indirection; + // matrices are arrays of vectors, so will also work for a matrix. + // Will use the access chain's 'component' for variable index into a vector. + + // This adapter is building access chains left to right. + // Set up the access chain to the left. + node->getLeft()->traverse(this); + + // save it so that computing the right side doesn't trash it + spv::Builder::AccessChain partial = builder.getAccessChain(); + + // compute the next index in the chain + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id index = accessChainLoad(node->getRight()->getType()); + + addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType()); + + // restore the saved access chain + builder.setAccessChain(partial); + + // Only if index is nonUniform should we propagate nonUniform into access chain + spv::Builder::AccessChain::CoherentFlags index_flags = TranslateCoherent(node->getRight()->getType()); + spv::Builder::AccessChain::CoherentFlags coherent_flags = TranslateCoherent(node->getLeft()->getType()); + coherent_flags.nonUniform = index_flags.nonUniform; + + if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) { + int dummySize; + builder.accessChainPushComponent( + index, convertGlslangToSpvType(node->getLeft()->getType()), coherent_flags, + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } else + builder.accessChainPush(index, coherent_flags, + node->getLeft()->getType().getBufferReferenceAlignment()); + } + return false; + case glslang::EOpVectorSwizzle: + { + node->getLeft()->traverse(this); + std::vector swizzle; + convertSwizzle(*node->getRight()->getAsAggregate(), swizzle); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } + return false; + case glslang::EOpMatrixSwizzle: + logger->missingFunctionality("matrix swizzle"); + return true; + case glslang::EOpLogicalOr: + case glslang::EOpLogicalAnd: + { + + // These may require short circuiting, but can sometimes be done as straight + // binary operations. The right operand must be short circuited if it has + // side effects, and should probably be if it is complex. + if (isTrivial(node->getRight()->getAsTyped())) + break; // handle below as a normal binary operation + // otherwise, we need to do dynamic short circuiting on the right operand + spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), + *node->getRight()->getAsTyped()); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } + return false; + default: + break; + } + + // Assume generic binary op... + + // get right operand + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Id left = accessChainLoad(node->getLeft()->getType()); + + // get left operand + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id right = accessChainLoad(node->getRight()->getType()); + + // get result + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + spv::Id result = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), left, right, + node->getLeft()->getType().getBasicType()); + + builder.clearAccessChain(); + if (! result) { + logger->missingFunctionality("unknown glslang binary operation"); + return true; // pick up a child as the place-holder result + } else { + builder.setAccessChainRValue(result); + return false; + } +} + +spv::Id TGlslangToSpvTraverser::convertLoadedBoolInUniformToUint(const glslang::TType& type, + spv::Id nominalTypeId, + spv::Id loadedId) +{ + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) + return builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) + loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } else if (builder.isArrayType(nominalTypeId)) { + // Conversion for bool array + spv::Id boolArrayTypeId = convertGlslangToSpvType(type); + if (nominalTypeId != boolArrayTypeId) + { + // Use OpCopyLogical from SPIR-V 1.4 if available. + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) + return builder.createUnaryOp(spv::OpCopyLogical, boolArrayTypeId, loadedId); + + glslang::TType glslangElementType(type, 0); + spv::Id elementNominalTypeId = builder.getContainedTypeId(nominalTypeId); + std::vector constituents; + for (int index = 0; index < type.getOuterArraySize(); ++index) { + // get the element + spv::Id elementValue = builder.createCompositeExtract(loadedId, elementNominalTypeId, index); + + // recursively convert it + spv::Id elementConvertedValue = convertLoadedBoolInUniformToUint(glslangElementType, elementNominalTypeId, elementValue); + constituents.push_back(elementConvertedValue); + } + return builder.createCompositeConstruct(boolArrayTypeId, constituents); + } + } + + return loadedId; +} + +// Figure out what, if any, type changes are needed when accessing a specific built-in. +// Returns . +// Also see comment for 'forceType', regarding tracking SPIR-V-required types. +std::pair TGlslangToSpvTraverser::getForcedType(glslang::TBuiltInVariable glslangBuiltIn, + const glslang::TType& glslangType) +{ + switch(glslangBuiltIn) + { + case glslang::EbvSubGroupEqMask: + case glslang::EbvSubGroupGeMask: + case glslang::EbvSubGroupGtMask: + case glslang::EbvSubGroupLeMask: + case glslang::EbvSubGroupLtMask: { + // these require changing a 64-bit scaler -> a vector of 32-bit components + if (glslangType.isVector()) + break; + spv::Id ivec4_type = builder.makeVectorType(builder.makeUintType(32), 4); + spv::Id uint64_type = builder.makeUintType(64); + std::pair ret(ivec4_type, uint64_type); + return ret; + } + // There are no SPIR-V builtins defined for these and map onto original non-transposed + // builtins. During visitBinary we insert a transpose + case glslang::EbvWorldToObject3x4: + case glslang::EbvObjectToWorld3x4: { + spv::Id mat43 = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + spv::Id mat34 = builder.makeMatrixType(builder.makeFloatType(32), 3, 4); + std::pair ret(mat43, mat34); + return ret; + } + default: + break; + } + + std::pair ret(spv::NoType, spv::NoType); + return ret; +} + +// For an object previously identified (see getForcedType() and forceType) +// as needing type translations, do the translation needed for a load, turning +// an L-value into in R-value. +spv::Id TGlslangToSpvTraverser::translateForcedType(spv::Id object) +{ + const auto forceIt = forceType.find(object); + if (forceIt == forceType.end()) + return object; + + spv::Id desiredTypeId = forceIt->second; + spv::Id objectTypeId = builder.getTypeId(object); + assert(builder.isPointerType(objectTypeId)); + objectTypeId = builder.getContainedTypeId(objectTypeId); + if (builder.isVectorType(objectTypeId) && + builder.getScalarTypeWidth(builder.getContainedTypeId(objectTypeId)) == 32) { + if (builder.getScalarTypeWidth(desiredTypeId) == 64) { + // handle 32-bit v.xy* -> 64-bit + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, spv::DecorationMax, objectTypeId); + std::vector components; + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 0)); + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 1)); + + spv::Id vecType = builder.makeVectorType(builder.getContainedTypeId(objectTypeId), 2); + return builder.createUnaryOp(spv::OpBitcast, desiredTypeId, + builder.createCompositeConstruct(vecType, components)); + } else { + logger->missingFunctionality("forcing 32-bit vector type to non 64-bit scalar"); + } + } else if (builder.isMatrixType(objectTypeId)) { + // There are no SPIR-V builtins defined for 3x4 variants of ObjectToWorld/WorldToObject + // and we insert a transpose after loading the original non-transposed builtins + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, spv::DecorationMax, objectTypeId); + return builder.createUnaryOp(spv::OpTranspose, desiredTypeId, object); + + } else { + logger->missingFunctionality("forcing non 32-bit vector type"); + } + + return object; +} + +bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) +{ + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + + // try texturing first + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // Non-texturing. + + if (node->getOp() == glslang::EOpArrayLength) { + // Quite special; won't want to evaluate the operand. + + // Currently, the front-end does not allow .length() on an array until it is sized, + // except for the last block membeor of an SSBO. + // TODO: If this changes, link-time sized arrays might show up here, and need their + // size extracted. + + // Normal .length() would have been constant folded by the front-end. + // So, this has to be block.lastMember.length(). + // SPV wants "block" and member number as the operands, go get them. + + spv::Id length; + if (node->getOperand()->getType().isCoopMat()) { + spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType()); + assert(builder.isCooperativeMatrixType(typeId)); + + if (node->getOperand()->getType().isCoopMatKHR()) { + length = builder.createCooperativeMatrixLengthKHR(typeId); + } else { + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + length = builder.createCooperativeMatrixLengthNV(typeId); + } + } else if (node->getOperand()->getType().isCoopVecNV()) { + spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType()); + length = builder.getCooperativeVectorNumComponents(typeId); + } else { + glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft(); + block->traverse(this); + unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion() + ->getConstArray()[0].getUConst(); + length = builder.createArrayLength(builder.accessChainGetLValue(), member); + } + + // GLSL semantics say the result of .length() is an int, while SPIR-V says + // signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's + // AST expectation of a signed result. + if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) { + if (builder.isInSpecConstCodeGenMode()) { + length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0)); + } else { + length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length); + } + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(length); + + return false; + } + + // Force variable declaration - Debug Mode Only + if (node->getOp() == glslang::EOpDeclare) { + builder.clearAccessChain(); + node->getOperand()->traverse(this); + builder.clearAccessChain(); + return false; + } + + // Start by evaluating the operand + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + spv::Id invertedType = spv::NoType; + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? + invertedType : convertGlslangToSpvType(node->getType()); }; + if (node->getOp() == glslang::EOpInterpolateAtCentroid) + invertedType = getInvertedSwizzleType(*node->getOperand()); + + builder.clearAccessChain(); + TIntermNode *operandNode; + if (invertedType != spv::NoType) + operandNode = node->getOperand()->getAsBinaryNode()->getLeft(); + else + operandNode = node->getOperand(); + + operandNode->traverse(this); + + spv::Id operand = spv::NoResult; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + const auto hitObjectOpsWithLvalue = [](glslang::TOperator op) { + switch(op) { + case glslang::EOpReorderThreadNV: + case glslang::EOpHitObjectGetCurrentTimeNV: + case glslang::EOpHitObjectGetHitKindNV: + case glslang::EOpHitObjectGetPrimitiveIndexNV: + case glslang::EOpHitObjectGetGeometryIndexNV: + case glslang::EOpHitObjectGetInstanceIdNV: + case glslang::EOpHitObjectGetInstanceCustomIndexNV: + case glslang::EOpHitObjectGetObjectRayDirectionNV: + case glslang::EOpHitObjectGetObjectRayOriginNV: + case glslang::EOpHitObjectGetWorldRayDirectionNV: + case glslang::EOpHitObjectGetWorldRayOriginNV: + case glslang::EOpHitObjectGetWorldToObjectNV: + case glslang::EOpHitObjectGetObjectToWorldNV: + case glslang::EOpHitObjectGetRayTMaxNV: + case glslang::EOpHitObjectGetRayTMinNV: + case glslang::EOpHitObjectIsEmptyNV: + case glslang::EOpHitObjectIsHitNV: + case glslang::EOpHitObjectIsMissNV: + case glslang::EOpHitObjectRecordEmptyNV: + case glslang::EOpHitObjectGetShaderBindingTableRecordIndexNV: + case glslang::EOpHitObjectGetShaderRecordBufferHandleNV: + case glslang::EOpHitObjectGetClusterIdNV: + case glslang::EOpHitObjectGetSpherePositionNV: + case glslang::EOpHitObjectGetSphereRadiusNV: + case glslang::EOpHitObjectIsSphereHitNV: + case glslang::EOpHitObjectIsLSSHitNV: + return true; + default: + return false; + } + }; + + if (node->getOp() == glslang::EOpAtomicCounterIncrement || + node->getOp() == glslang::EOpAtomicCounterDecrement || + node->getOp() == glslang::EOpAtomicCounter || + (node->getOp() == glslang::EOpInterpolateAtCentroid && + glslangIntermediate->getSource() != glslang::EShSourceHlsl) || + node->getOp() == glslang::EOpRayQueryProceed || + node->getOp() == glslang::EOpRayQueryGetRayTMin || + node->getOp() == glslang::EOpRayQueryGetRayFlags || + node->getOp() == glslang::EOpRayQueryGetWorldRayOrigin || + node->getOp() == glslang::EOpRayQueryGetWorldRayDirection || + node->getOp() == glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque || + node->getOp() == glslang::EOpRayQueryTerminate || + node->getOp() == glslang::EOpRayQueryConfirmIntersection || + (node->getOp() == glslang::EOpSpirvInst && operandNode->getAsTyped()->getQualifier().isSpirvByReference()) || + hitObjectOpsWithLvalue(node->getOp())) { + operand = builder.accessChainGetLValue(); // Special case l-value operands + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType()); + } else if (operandNode->getAsTyped()->getQualifier().isSpirvLiteral()) { + // Will be translated to a literal value, make a placeholder here + operand = spv::NoResult; + } else { + operand = accessChainLoad(node->getOperand()->getType()); + } + + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + + // it could be a conversion + if (! result) { + result = createConversion(node->getOp(), decorations, resultType(), operand, + node->getType().getBasicType(), node->getOperand()->getBasicType()); + if (result) { + if (node->getType().isCoopMatKHR() && node->getOperand()->getAsTyped()->getType().isCoopMatKHR() && + !node->getAsTyped()->getType().sameCoopMatUse(node->getOperand()->getAsTyped()->getType())) { + // Conversions that change use need CapabilityCooperativeMatrixConversionsNV + builder.addCapability(spv::CapabilityCooperativeMatrixConversionsNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + } + } + } + + // if not, then possibly an operation + if (! result) + result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, + node->getOperand()->getBasicType(), lvalueCoherentFlags, node->getType()); + + // it could be attached to a SPIR-V intruction + if (!result) { + if (node->getOp() == glslang::EOpSpirvInst) { + const auto& spirvInst = node->getSpirvInstruction(); + if (spirvInst.set == "") { + spv::IdImmediate idImmOp = {true, operand}; + if (operandNode->getAsTyped()->getQualifier().isSpirvLiteral()) { + // Translate the constant to a literal value + std::vector literals; + glslang::TVector constants; + constants.push_back(operandNode->getAsConstantUnion()); + TranslateLiterals(constants, literals); + idImmOp = {false, literals[0]}; + } + + if (node->getBasicType() == glslang::EbtVoid) + builder.createNoResultOp(static_cast(spirvInst.id), {idImmOp}); + else + result = builder.createOp(static_cast(spirvInst.id), resultType(), {idImmOp}); + } else { + result = builder.createBuiltinCall( + resultType(), spirvInst.set == "GLSL.std.450" ? stdBuiltins : getExtBuiltins(spirvInst.set.c_str()), + spirvInst.id, {operand}); + } + + if (node->getBasicType() == glslang::EbtVoid) + return false; // done with this node + } + } + + if (result) { + if (invertedType) { + result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result); + decorations.addNonUniform(builder, result); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // it must be a special case, check... + switch (node->getOp()) { + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + { + // we need the integer value "1" or the floating point "1.0" to add/subtract + spv::Id one = 0; + if (node->getBasicType() == glslang::EbtFloat) + one = builder.makeFloatConstant(1.0F); + else if (node->getBasicType() == glslang::EbtDouble) + one = builder.makeDoubleConstant(1.0); + else if (node->getBasicType() == glslang::EbtFloat16) + one = builder.makeFloat16Constant(1.0F); + else if (node->getBasicType() == glslang::EbtBFloat16) + one = builder.makeBFloat16Constant(1.0F); + else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8) + one = builder.makeInt8Constant(1); + else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16) + one = builder.makeInt16Constant(1); + else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64) + one = builder.makeInt64Constant(1); + else + one = builder.makeIntConstant(1); + glslang::TOperator op; + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPostIncrement) + op = glslang::EOpAdd; + else + op = glslang::EOpSub; + + spv::Id result = createBinaryOperation(op, decorations, + convertGlslangToSpvType(node->getType()), operand, one, + node->getType().getBasicType()); + assert(result != spv::NoResult); + + // The result of operation is always stored, but conditionally the + // consumed result. The consumed result is always an r-value. + builder.accessChainStore(result, + TranslateNonUniformDecoration(builder.getAccessChain().coherentFlags)); + builder.clearAccessChain(); + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPreDecrement) + builder.setAccessChainRValue(result); + else + builder.setAccessChainRValue(operand); + } + + return false; + + case glslang::EOpAssumeEXT: + builder.addCapability(spv::CapabilityExpectAssumeKHR); + builder.addExtension(spv::E_SPV_KHR_expect_assume); + builder.createNoResultOp(spv::OpAssumeTrueKHR, operand); + return false; + case glslang::EOpEmitStreamVertex: + builder.createNoResultOp(spv::OpEmitStreamVertex, operand); + return false; + case glslang::EOpEndStreamPrimitive: + builder.createNoResultOp(spv::OpEndStreamPrimitive, operand); + return false; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operand); + return false; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operand); + return false; + case glslang::EOpReorderThreadNV: + builder.createNoResultOp(spv::OpReorderThreadWithHitObjectNV, operand); + return false; + case glslang::EOpHitObjectRecordEmptyNV: + builder.createNoResultOp(spv::OpHitObjectRecordEmptyNV, operand); + return false; + + case glslang::EOpCreateTensorLayoutNV: + result = builder.createOp(spv::OpCreateTensorLayoutNV, resultType(), std::vector{}); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + + case glslang::EOpCreateTensorViewNV: + result = builder.createOp(spv::OpCreateTensorViewNV, resultType(), std::vector{}); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + + default: + logger->missingFunctionality("unknown glslang unary"); + return true; // pick up operand as placeholder result + } +} + +// Construct a composite object, recursively copying members if their types don't match +spv::Id TGlslangToSpvTraverser::createCompositeConstruct(spv::Id resultTypeId, std::vector constituents) +{ + for (int c = 0; c < (int)constituents.size(); ++c) { + spv::Id& constituent = constituents[c]; + spv::Id lType = builder.getContainedTypeId(resultTypeId, c); + spv::Id rType = builder.getTypeId(constituent); + if (lType != rType) { + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + constituent = builder.createUnaryOp(spv::OpCopyLogical, lType, constituent); + } else if (builder.isStructType(rType)) { + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, + builder.getContainedTypeId(rType, i), i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } else { + assert(builder.isArrayType(rType)); + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, elementRType, i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } + } + } + return builder.createCompositeConstruct(resultTypeId, constituents); +} + +bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) +{ + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + spv::Id invertedType = spv::NoType; // to use to override the natural type of the node + std::vector complexLvalues; // for holding swizzling l-values too complex for + // SPIR-V, for an out parameter + std::vector temporaryLvalues; // temporaries to pass, as proxies for complexLValues + + auto resultType = [&invertedType, &node, this](){ + if (invertedType != spv::NoType) { + return invertedType; + } else { + auto ret = convertGlslangToSpvType(node->getType()); + // convertGlslangToSpvType may clobber the debug location, reset it + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + return ret; + } + }; + + // try texturing + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } else if (node->getOp() == glslang::EOpImageStore || + node->getOp() == glslang::EOpImageStoreLod || + node->getOp() == glslang::EOpImageAtomicStore) { + // "imageStore" is a special case, which has no result + return false; + } + + glslang::TOperator binOp = glslang::EOpNull; + bool reduceComparison = true; + bool isMatrix = false; + bool noReturnValue = false; + bool atomic = false; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + assert(node->getOp()); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); + + switch (node->getOp()) { + case glslang::EOpScope: + case glslang::EOpSequence: + { + if (visit == glslang::EvPreVisit) { + ++sequenceDepth; + if (sequenceDepth == 1) { + // If this is the parent node of all the functions, we want to see them + // early, so all call points have actual SPIR-V functions to reference. + // In all cases, still let the traverser visit the children for us. + makeFunctions(node->getAsAggregate()->getSequence()); + + // Global initializers is specific to the shader entry point, which does not exist in compile-only mode + if (!options.compileOnly) { + // Also, we want all globals initializers to go into the beginning of the entry point, before + // anything else gets there, so visit out of order, doing them all now. + makeGlobalInitializers(node->getAsAggregate()->getSequence()); + } + + //Pre process linker objects for ray tracing stages + if (glslangIntermediate->isRayTracingStage()) + collectRayTracingLinkerObjects(); + + // Initializers are done, don't want to visit again, but functions and link objects need to be processed, + // so do them manually. + visitFunctions(node->getAsAggregate()->getSequence()); + + return false; + } else { + if (node->getOp() == glslang::EOpScope) { + auto loc = node->getLoc(); + builder.enterLexicalBlock(loc.line, loc.column); + } + } + } else { + if (sequenceDepth > 1 && node->getOp() == glslang::EOpScope) + builder.leaveLexicalBlock(); + --sequenceDepth; + } + + return true; + } + case glslang::EOpLinkerObjects: + { + if (visit == glslang::EvPreVisit) + linkageOnly = true; + else + linkageOnly = false; + + return true; + } + case glslang::EOpComma: + { + // processing from left to right naturally leaves the right-most + // lying around in the access chain + glslang::TIntermSequence& glslangOperands = node->getSequence(); + for (int i = 0; i < (int)glslangOperands.size(); ++i) + glslangOperands[i]->traverse(this); + + return false; + } + case glslang::EOpFunction: + if (visit == glslang::EvPreVisit) { + if (options.generateDebugInfo) { + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + } + if (isShaderEntryPoint(node)) { + inEntryPoint = true; + builder.setBuildPoint(shaderEntry->getLastBlock()); + builder.enterFunction(shaderEntry); + currentFunction = shaderEntry; + } else { + handleFunctionEntry(node); + } + if (options.generateDebugInfo && !options.emitNonSemanticShaderDebugInfo) { + const auto& loc = node->getLoc(); + const char* sourceFileName = loc.getFilename(); + spv::Id sourceFileId = sourceFileName ? builder.getStringId(sourceFileName) : builder.getMainFileId(); + currentFunction->setDebugLineInfo(sourceFileId, loc.line, loc.column); + } + } else { + if (options.generateDebugInfo) { + if (glslangIntermediate->getSource() == glslang::EShSourceGlsl && node->getSequence().size() > 1) { + auto endLoc = node->getSequence()[1]->getAsAggregate()->getEndLoc(); + builder.setDebugSourceLocation(endLoc.line, endLoc.getFilename()); + } + } + if (inEntryPoint) + entryPointTerminated = true; + builder.leaveFunction(); + inEntryPoint = false; + } + + return true; + case glslang::EOpParameters: + // Parameters will have been consumed by EOpFunction processing, but not + // the body, so we still visited the function node's children, making this + // child redundant. + return false; + case glslang::EOpFunctionCall: + { + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + if (node->isUserDefined()) + result = handleUserFunctionCall(node); + if (result) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else + logger->missingFunctionality("missing user function; linker needs to catch that"); + + return false; + } + case glslang::EOpConstructMat2x2: + case glslang::EOpConstructMat2x3: + case glslang::EOpConstructMat2x4: + case glslang::EOpConstructMat3x2: + case glslang::EOpConstructMat3x3: + case glslang::EOpConstructMat3x4: + case glslang::EOpConstructMat4x2: + case glslang::EOpConstructMat4x3: + case glslang::EOpConstructMat4x4: + case glslang::EOpConstructDMat2x2: + case glslang::EOpConstructDMat2x3: + case glslang::EOpConstructDMat2x4: + case glslang::EOpConstructDMat3x2: + case glslang::EOpConstructDMat3x3: + case glslang::EOpConstructDMat3x4: + case glslang::EOpConstructDMat4x2: + case glslang::EOpConstructDMat4x3: + case glslang::EOpConstructDMat4x4: + case glslang::EOpConstructIMat2x2: + case glslang::EOpConstructIMat2x3: + case glslang::EOpConstructIMat2x4: + case glslang::EOpConstructIMat3x2: + case glslang::EOpConstructIMat3x3: + case glslang::EOpConstructIMat3x4: + case glslang::EOpConstructIMat4x2: + case glslang::EOpConstructIMat4x3: + case glslang::EOpConstructIMat4x4: + case glslang::EOpConstructUMat2x2: + case glslang::EOpConstructUMat2x3: + case glslang::EOpConstructUMat2x4: + case glslang::EOpConstructUMat3x2: + case glslang::EOpConstructUMat3x3: + case glslang::EOpConstructUMat3x4: + case glslang::EOpConstructUMat4x2: + case glslang::EOpConstructUMat4x3: + case glslang::EOpConstructUMat4x4: + case glslang::EOpConstructBMat2x2: + case glslang::EOpConstructBMat2x3: + case glslang::EOpConstructBMat2x4: + case glslang::EOpConstructBMat3x2: + case glslang::EOpConstructBMat3x3: + case glslang::EOpConstructBMat3x4: + case glslang::EOpConstructBMat4x2: + case glslang::EOpConstructBMat4x3: + case glslang::EOpConstructBMat4x4: + case glslang::EOpConstructF16Mat2x2: + case glslang::EOpConstructF16Mat2x3: + case glslang::EOpConstructF16Mat2x4: + case glslang::EOpConstructF16Mat3x2: + case glslang::EOpConstructF16Mat3x3: + case glslang::EOpConstructF16Mat3x4: + case glslang::EOpConstructF16Mat4x2: + case glslang::EOpConstructF16Mat4x3: + case glslang::EOpConstructF16Mat4x4: + isMatrix = true; + [[fallthrough]]; + case glslang::EOpConstructFloat: + case glslang::EOpConstructVec2: + case glslang::EOpConstructVec3: + case glslang::EOpConstructVec4: + case glslang::EOpConstructDouble: + case glslang::EOpConstructDVec2: + case glslang::EOpConstructDVec3: + case glslang::EOpConstructDVec4: + case glslang::EOpConstructFloat16: + case glslang::EOpConstructF16Vec2: + case glslang::EOpConstructF16Vec3: + case glslang::EOpConstructF16Vec4: + case glslang::EOpConstructBFloat16: + case glslang::EOpConstructBF16Vec2: + case glslang::EOpConstructBF16Vec3: + case glslang::EOpConstructBF16Vec4: + case glslang::EOpConstructBool: + case glslang::EOpConstructBVec2: + case glslang::EOpConstructBVec3: + case glslang::EOpConstructBVec4: + case glslang::EOpConstructInt8: + case glslang::EOpConstructI8Vec2: + case glslang::EOpConstructI8Vec3: + case glslang::EOpConstructI8Vec4: + case glslang::EOpConstructUint8: + case glslang::EOpConstructU8Vec2: + case glslang::EOpConstructU8Vec3: + case glslang::EOpConstructU8Vec4: + case glslang::EOpConstructInt16: + case glslang::EOpConstructI16Vec2: + case glslang::EOpConstructI16Vec3: + case glslang::EOpConstructI16Vec4: + case glslang::EOpConstructUint16: + case glslang::EOpConstructU16Vec2: + case glslang::EOpConstructU16Vec3: + case glslang::EOpConstructU16Vec4: + case glslang::EOpConstructInt: + case glslang::EOpConstructIVec2: + case glslang::EOpConstructIVec3: + case glslang::EOpConstructIVec4: + case glslang::EOpConstructUint: + case glslang::EOpConstructUVec2: + case glslang::EOpConstructUVec3: + case glslang::EOpConstructUVec4: + case glslang::EOpConstructInt64: + case glslang::EOpConstructI64Vec2: + case glslang::EOpConstructI64Vec3: + case glslang::EOpConstructI64Vec4: + case glslang::EOpConstructUint64: + case glslang::EOpConstructU64Vec2: + case glslang::EOpConstructU64Vec3: + case glslang::EOpConstructU64Vec4: + case glslang::EOpConstructStruct: + case glslang::EOpConstructTextureSampler: + case glslang::EOpConstructReference: + case glslang::EOpConstructCooperativeMatrixNV: + case glslang::EOpConstructCooperativeMatrixKHR: + case glslang::EOpConstructCooperativeVectorNV: + { + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + std::vector arguments; + translateArguments(*node, arguments, lvalueCoherentFlags); + spv::Id constructed; + if (node->getOp() == glslang::EOpConstructTextureSampler) { + const glslang::TType& texType = node->getSequence()[0]->getAsTyped()->getType(); + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6 && + texType.getSampler().isBuffer()) { + // SamplerBuffer is not supported in spirv1.6 so + // `samplerBuffer(textureBuffer, sampler)` is a no-op + // and textureBuffer is the result going forward + constructed = arguments[0]; + } else + constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments); + } else if (node->getOp() == glslang::EOpConstructCooperativeMatrixKHR && + node->getType().isCoopMatKHR() && node->getSequence()[0]->getAsTyped()->getType().isCoopMatKHR()) { + builder.addCapability(spv::CapabilityCooperativeMatrixConversionsNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + constructed = builder.createCooperativeMatrixConversion(resultType(), arguments[0]); + } else if (node->getOp() == glslang::EOpConstructCooperativeVectorNV && + arguments.size() == 1 && + builder.getTypeId(arguments[0]) == resultType()) { + constructed = arguments[0]; + } else if (node->getOp() == glslang::EOpConstructStruct || + node->getOp() == glslang::EOpConstructCooperativeMatrixNV || + node->getOp() == glslang::EOpConstructCooperativeMatrixKHR || + node->getType().isArray() || + // Handle constructing coopvec from one component here, to avoid the component + // getting smeared + (node->getOp() == glslang::EOpConstructCooperativeVectorNV && arguments.size() == 1 && builder.isScalar(arguments[0]))) { + std::vector constituents; + for (int c = 0; c < (int)arguments.size(); ++c) + constituents.push_back(arguments[c]); + constructed = createCompositeConstruct(resultType(), constituents); + } else if (isMatrix) + constructed = builder.createMatrixConstructor(precision, arguments, resultType()); + else + constructed = builder.createConstructor(precision, arguments, resultType()); + + if (node->getType().getQualifier().isNonUniform()) { + builder.addDecoration(constructed, spv::DecorationNonUniformEXT); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(constructed); + + return false; + } + + // These six are component-wise compares with component-wise results. + // Forward on to createBinaryOperation(), requesting a vector result. + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + { + // Map the operation to a binary + binOp = node->getOp(); + reduceComparison = false; + switch (node->getOp()) { + case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break; + case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break; + default: binOp = node->getOp(); break; + } + + break; + } + case glslang::EOpMul: + // component-wise matrix multiply + binOp = glslang::EOpMul; + break; + case glslang::EOpOuterProduct: + // two vectors multiplied to make a matrix + binOp = glslang::EOpOuterProduct; + break; + case glslang::EOpDot: + { + // for scalar dot product, use multiply + glslang::TIntermSequence& glslangOperands = node->getSequence(); + if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1) + binOp = glslang::EOpMul; + break; + } + case glslang::EOpMod: + // when an aggregate, this is the floating-point mod built-in function, + // which can be emitted by the one in createBinaryOperation() + binOp = glslang::EOpMod; + break; + + case glslang::EOpEmitVertex: + case glslang::EOpEndPrimitive: + case glslang::EOpBarrier: + case glslang::EOpMemoryBarrier: + case glslang::EOpMemoryBarrierAtomicCounter: + case glslang::EOpMemoryBarrierBuffer: + case glslang::EOpMemoryBarrierImage: + case glslang::EOpMemoryBarrierShared: + case glslang::EOpGroupMemoryBarrier: + case glslang::EOpDeviceMemoryBarrier: + case glslang::EOpAllMemoryBarrierWithGroupSync: + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + case glslang::EOpWorkgroupMemoryBarrier: + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + case glslang::EOpSubgroupBarrier: + case glslang::EOpSubgroupMemoryBarrier: + case glslang::EOpSubgroupMemoryBarrierBuffer: + case glslang::EOpSubgroupMemoryBarrierImage: + case glslang::EOpSubgroupMemoryBarrierShared: + noReturnValue = true; + // These all have 0 operands and will naturally finish up in the code below for 0 operands + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicSubtract: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + atomic = true; + break; + + case glslang::EOpAtomicStore: + noReturnValue = true; + [[fallthrough]]; + case glslang::EOpAtomicLoad: + atomic = true; + break; + + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + builder.addExtension("SPV_KHR_shader_atomic_counter_ops"); + builder.addCapability(spv::CapabilityAtomicStorageOps); + atomic = true; + break; + + case glslang::EOpAbsDifference: + case glslang::EOpAddSaturate: + case glslang::EOpSubSaturate: + case glslang::EOpAverage: + case glslang::EOpAverageRounded: + case glslang::EOpMul32x16: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + binOp = node->getOp(); + break; + + case glslang::EOpExpectEXT: + builder.addCapability(spv::CapabilityExpectAssumeKHR); + builder.addExtension(spv::E_SPV_KHR_expect_assume); + binOp = node->getOp(); + break; + + case glslang::EOpIgnoreIntersectionNV: + case glslang::EOpTerminateRayNV: + case glslang::EOpTraceNV: + case glslang::EOpTraceRayMotionNV: + case glslang::EOpTraceKHR: + case glslang::EOpExecuteCallableNV: + case glslang::EOpExecuteCallableKHR: + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + case glslang::EOpEmitMeshTasksEXT: + case glslang::EOpSetMeshOutputsEXT: + noReturnValue = true; + break; + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryConfirmIntersection: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryKHR); + noReturnValue = true; + break; + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetRayTMin: + case glslang::EOpRayQueryGetRayFlags: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetWorldRayDirection: + case glslang::EOpRayQueryGetWorldRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryKHR); + break; + case glslang::EOpCooperativeMatrixLoad: + case glslang::EOpCooperativeMatrixStore: + case glslang::EOpCooperativeMatrixLoadNV: + case glslang::EOpCooperativeMatrixStoreNV: + case glslang::EOpCooperativeMatrixLoadTensorNV: + case glslang::EOpCooperativeMatrixStoreTensorNV: + case glslang::EOpCooperativeMatrixReduceNV: + case glslang::EOpCooperativeMatrixPerElementOpNV: + case glslang::EOpCooperativeMatrixTransposeNV: + case glslang::EOpCooperativeVectorMatMulNV: + case glslang::EOpCooperativeVectorMatMulAddNV: + case glslang::EOpCooperativeVectorLoadNV: + case glslang::EOpCooperativeVectorStoreNV: + case glslang::EOpCooperativeVectorOuterProductAccumulateNV: + case glslang::EOpCooperativeVectorReduceSumAccumulateNV: + noReturnValue = true; + break; + case glslang::EOpBeginInvocationInterlock: + case glslang::EOpEndInvocationInterlock: + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + noReturnValue = true; + break; + + case glslang::EOpHitObjectTraceRayNV: + case glslang::EOpHitObjectTraceRayMotionNV: + case glslang::EOpHitObjectGetAttributesNV: + case glslang::EOpHitObjectExecuteShaderNV: + case glslang::EOpHitObjectRecordEmptyNV: + case glslang::EOpHitObjectRecordMissNV: + case glslang::EOpHitObjectRecordMissMotionNV: + case glslang::EOpHitObjectRecordHitNV: + case glslang::EOpHitObjectRecordHitMotionNV: + case glslang::EOpHitObjectRecordHitWithIndexNV: + case glslang::EOpHitObjectRecordHitWithIndexMotionNV: + case glslang::EOpReorderThreadNV: + noReturnValue = true; + [[fallthrough]]; + case glslang::EOpHitObjectIsEmptyNV: + case glslang::EOpHitObjectIsMissNV: + case glslang::EOpHitObjectIsHitNV: + case glslang::EOpHitObjectGetRayTMinNV: + case glslang::EOpHitObjectGetRayTMaxNV: + case glslang::EOpHitObjectGetObjectRayOriginNV: + case glslang::EOpHitObjectGetObjectRayDirectionNV: + case glslang::EOpHitObjectGetWorldRayOriginNV: + case glslang::EOpHitObjectGetWorldRayDirectionNV: + case glslang::EOpHitObjectGetObjectToWorldNV: + case glslang::EOpHitObjectGetWorldToObjectNV: + case glslang::EOpHitObjectGetInstanceCustomIndexNV: + case glslang::EOpHitObjectGetInstanceIdNV: + case glslang::EOpHitObjectGetGeometryIndexNV: + case glslang::EOpHitObjectGetPrimitiveIndexNV: + case glslang::EOpHitObjectGetHitKindNV: + case glslang::EOpHitObjectGetCurrentTimeNV: + case glslang::EOpHitObjectGetShaderBindingTableRecordIndexNV: + case glslang::EOpHitObjectGetShaderRecordBufferHandleNV: + builder.addExtension(spv::E_SPV_NV_shader_invocation_reorder); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + break; + + case glslang::EOpHitObjectGetLSSPositionsNV: + case glslang::EOpHitObjectGetLSSRadiiNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + noReturnValue = true; + break; + + case glslang::EOpHitObjectGetSpherePositionNV: + case glslang::EOpHitObjectGetSphereRadiusNV: + case glslang::EOpHitObjectIsSphereHitNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + builder.addCapability(spv::CapabilityRayTracingSpheresGeometryNV); + break; + + case glslang::EOpHitObjectIsLSSHitNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + break; + + case glslang::EOpRayQueryGetIntersectionLSSPositionsNV: + case glslang::EOpRayQueryGetIntersectionLSSRadiiNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityRayQueryKHR); + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + noReturnValue = true; + break; + + case glslang::EOpRayQueryGetIntersectionSpherePositionNV: + case glslang::EOpRayQueryGetIntersectionSphereRadiusNV: + case glslang::EOpRayQueryIsSphereHitNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityRayQueryKHR); + builder.addCapability(spv::CapabilityRayTracingSpheresGeometryNV); + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + break; + + case glslang::EOpRayQueryGetIntersectionLSSHitValueNV: + case glslang::EOpRayQueryIsLSSHitNV: + builder.addExtension(spv::E_SPV_NV_linear_swept_spheres); + builder.addCapability(spv::CapabilityRayQueryKHR); + builder.addCapability(spv::CapabilityRayTracingLinearSweptSpheresGeometryNV); + break; + + case glslang::EOpRayQueryGetIntersectionTriangleVertexPositionsEXT: + builder.addExtension(spv::E_SPV_KHR_ray_tracing_position_fetch); + builder.addCapability(spv::CapabilityRayQueryPositionFetchKHR); + noReturnValue = true; + break; + + case glslang::EOpImageSampleWeightedQCOM: + builder.addCapability(spv::CapabilityTextureSampleWeightedQCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing); + break; + case glslang::EOpImageBoxFilterQCOM: + builder.addCapability(spv::CapabilityTextureBoxFilterQCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing); + break; + case glslang::EOpImageBlockMatchSADQCOM: + case glslang::EOpImageBlockMatchSSDQCOM: + builder.addCapability(spv::CapabilityTextureBlockMatchQCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing); + break; + + case glslang::EOpImageBlockMatchWindowSSDQCOM: + case glslang::EOpImageBlockMatchWindowSADQCOM: + builder.addCapability(spv::CapabilityTextureBlockMatchQCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing); + builder.addCapability(spv::CapabilityTextureBlockMatch2QCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing2); + break; + + case glslang::EOpImageBlockMatchGatherSSDQCOM: + case glslang::EOpImageBlockMatchGatherSADQCOM: + builder.addCapability(spv::CapabilityTextureBlockMatchQCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing); + builder.addCapability(spv::CapabilityTextureBlockMatch2QCOM); + builder.addExtension(spv::E_SPV_QCOM_image_processing2); + break; + + case glslang::EOpFetchMicroTriangleVertexPositionNV: + case glslang::EOpFetchMicroTriangleVertexBarycentricNV: + builder.addExtension(spv::E_SPV_NV_displacement_micromap); + builder.addCapability(spv::CapabilityDisplacementMicromapNV); + break; + + case glslang::EOpRayQueryGetIntersectionClusterIdNV: + builder.addExtension(spv::E_SPV_NV_cluster_acceleration_structure); + builder.addCapability(spv::CapabilityRayQueryKHR); + builder.addCapability(spv::CapabilityRayTracingClusterAccelerationStructureNV); + break; + + case glslang::EOpDebugPrintf: + noReturnValue = true; + break; + + default: + break; + } + + // + // See if it maps to a regular operation. + // + if (binOp != glslang::EOpNull) { + glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); + glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); + assert(left && right); + + builder.clearAccessChain(); + left->traverse(this); + spv::Id leftId = accessChainLoad(left->getType()); + + builder.clearAccessChain(); + right->traverse(this); + spv::Id rightId = accessChainLoad(right->getType()); + + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createBinaryOperation(binOp, decorations, + resultType(), leftId, rightId, + left->getType().getBasicType(), reduceComparison); + + // code above should only make binOp that exists in createBinaryOperation + assert(result != spv::NoResult); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + + // + // Create the list of operands. + // + glslang::TIntermSequence& glslangOperands = node->getSequence(); + std::vector operands; + std::vector memoryAccessOperands; + for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { + // special case l-value operands; there are just a few + bool lvalue = false; + switch (node->getOp()) { + case glslang::EOpModf: + if (arg == 1) + lvalue = true; + break; + + + + case glslang::EOpHitObjectRecordHitNV: + case glslang::EOpHitObjectRecordHitMotionNV: + case glslang::EOpHitObjectRecordHitWithIndexNV: + case glslang::EOpHitObjectRecordHitWithIndexMotionNV: + case glslang::EOpHitObjectTraceRayNV: + case glslang::EOpHitObjectTraceRayMotionNV: + case glslang::EOpHitObjectExecuteShaderNV: + case glslang::EOpHitObjectRecordMissNV: + case glslang::EOpHitObjectRecordMissMotionNV: + case glslang::EOpHitObjectGetAttributesNV: + case glslang::EOpHitObjectGetClusterIdNV: + if (arg == 0) + lvalue = true; + break; + + case glslang::EOpHitObjectGetLSSPositionsNV: + case glslang::EOpHitObjectGetLSSRadiiNV: + lvalue = true; + break; + + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryConfirmIntersection: + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + case glslang::EOpRayQueryGetIntersectionClusterIdNV: + case glslang::EOpRayQueryGetIntersectionSpherePositionNV: + case glslang::EOpRayQueryGetIntersectionSphereRadiusNV: + case glslang::EOpRayQueryGetIntersectionLSSHitValueNV: + case glslang::EOpRayQueryIsSphereHitNV: + case glslang::EOpRayQueryIsLSSHitNV: + if (arg == 0) + lvalue = true; + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicSubtract: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + if (arg == 0) + lvalue = true; + break; + + case glslang::EOpFrexp: + if (arg == 1) + lvalue = true; + break; + case glslang::EOpInterpolateAtSample: + case glslang::EOpInterpolateAtOffset: + case glslang::EOpInterpolateAtVertex: + if (arg == 0) { + // If GLSL, use the address of the interpolant argument. + // If HLSL, use an internal version of OpInterolates that takes + // the rvalue of the interpolant. A fixup pass in spirv-opt + // legalization will remove the OpLoad and convert to an lvalue. + // Had to do this because legalization will only propagate a + // builtin into an rvalue. + lvalue = glslangIntermediate->getSource() != glslang::EShSourceHlsl; + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + // That is, we transform + // + // interpolate(v.zy) -> interpolate(v).zy + // + if (glslangOperands[0]->getAsOperator() && + glslangOperands[0]->getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + invertedType = convertGlslangToSpvType( + glslangOperands[0]->getAsBinaryNode()->getLeft()->getType()); + } + break; + case glslang::EOpAtomicLoad: + case glslang::EOpAtomicStore: + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + if (arg == 0) + lvalue = true; + break; + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + if (arg == 2) + lvalue = true; + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + if (arg >= 2) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixLoad: + case glslang::EOpCooperativeMatrixLoadNV: + case glslang::EOpCooperativeMatrixLoadTensorNV: + case glslang::EOpCooperativeVectorLoadNV: + if (arg == 0 || arg == 1) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixStore: + case glslang::EOpCooperativeMatrixStoreNV: + case glslang::EOpCooperativeMatrixStoreTensorNV: + case glslang::EOpCooperativeVectorStoreNV: + if (arg == 1) + lvalue = true; + break; + case glslang::EOpCooperativeVectorMatMulNV: + if (arg == 0 || arg == 3) + lvalue = true; + break; + case glslang::EOpCooperativeVectorMatMulAddNV: + if (arg == 0 || arg == 3 || arg == 6) + lvalue = true; + break; + case glslang::EOpCooperativeVectorOuterProductAccumulateNV: + if (arg == 2) + lvalue = true; + break; + case glslang::EOpCooperativeVectorReduceSumAccumulateNV: + if (arg == 1) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixReduceNV: + case glslang::EOpCooperativeMatrixPerElementOpNV: + case glslang::EOpCooperativeMatrixTransposeNV: + if (arg == 0) + lvalue = true; + break; + case glslang::EOpSpirvInst: + if (glslangOperands[arg]->getAsTyped()->getQualifier().isSpirvByReference()) + lvalue = true; + break; + case glslang::EOpReorderThreadNV: + //Three variants of reorderThreadNV, two of them use hitObjectNV + if (arg == 0 && glslangOperands.size() != 2) + lvalue = true; + break; + case glslang::EOpRayQueryGetIntersectionTriangleVertexPositionsEXT: + case glslang::EOpRayQueryGetIntersectionLSSPositionsNV: + case glslang::EOpRayQueryGetIntersectionLSSRadiiNV: + if (arg == 0 || arg == 2) + lvalue = true; + break; + default: + break; + } + builder.clearAccessChain(); + if (invertedType != spv::NoType && arg == 0) + glslangOperands[0]->getAsBinaryNode()->getLeft()->traverse(this); + else + glslangOperands[arg]->traverse(this); + + bool isCoopMat = node->getOp() == glslang::EOpCooperativeMatrixLoad || + node->getOp() == glslang::EOpCooperativeMatrixStore || + node->getOp() == glslang::EOpCooperativeMatrixLoadNV || + node->getOp() == glslang::EOpCooperativeMatrixStoreNV || + node->getOp() == glslang::EOpCooperativeMatrixLoadTensorNV || + node->getOp() == glslang::EOpCooperativeMatrixStoreTensorNV; + bool isCoopVec = node->getOp() == glslang::EOpCooperativeVectorLoadNV || + node->getOp() == glslang::EOpCooperativeVectorStoreNV; + if (isCoopMat || isCoopVec) { + + if (arg == 1) { + spv::Builder::AccessChain::CoherentFlags coherentFlags {}; + unsigned int alignment {}; + if (isCoopMat) { + // fold "element" parameter into the access chain + spv::Builder::AccessChain save = builder.getAccessChain(); + builder.clearAccessChain(); + glslangOperands[2]->traverse(this); + + spv::Id elementId = accessChainLoad(glslangOperands[2]->getAsTyped()->getType()); + + builder.setAccessChain(save); + + // Point to the first element of the array. + builder.accessChainPush(elementId, + TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()), + glslangOperands[arg]->getAsTyped()->getType().getBufferReferenceAlignment()); + coherentFlags = builder.getAccessChain().coherentFlags; + alignment = builder.getAccessChain().alignment; + } else { + coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()); + alignment = 16; + } + + int memoryAccess = TranslateMemoryAccess(coherentFlags); + if (node->getOp() == glslang::EOpCooperativeMatrixLoad || + node->getOp() == glslang::EOpCooperativeMatrixLoadNV || + node->getOp() == glslang::EOpCooperativeMatrixLoadTensorNV || + node->getOp() == glslang::EOpCooperativeVectorLoadNV) + memoryAccess &= ~spv::MemoryAccessMakePointerAvailableKHRMask; + if (node->getOp() == glslang::EOpCooperativeMatrixStore || + node->getOp() == glslang::EOpCooperativeMatrixStoreNV || + node->getOp() == glslang::EOpCooperativeMatrixStoreTensorNV || + node->getOp() == glslang::EOpCooperativeVectorStoreNV) + memoryAccess &= ~spv::MemoryAccessMakePointerVisibleKHRMask; + if (builder.getStorageClass(builder.getAccessChain().base) == + spv::StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + memoryAccessOperands.push_back(spv::IdImmediate(false, memoryAccess)); + + if (memoryAccess & spv::MemoryAccessAlignedMask) { + memoryAccessOperands.push_back(spv::IdImmediate(false, alignment)); + } + + if (memoryAccess & + (spv::MemoryAccessMakePointerAvailableKHRMask | spv::MemoryAccessMakePointerVisibleKHRMask)) { + memoryAccessOperands.push_back(spv::IdImmediate(true, + builder.makeUintConstant(TranslateMemoryScope(coherentFlags)))); + } + } else if (isCoopMat && arg == 2) { + continue; + } + } + + // for l-values, pass the address, for r-values, pass the value + if (lvalue) { + if (invertedType == spv::NoType && !builder.isSpvLvalue()) { + // SPIR-V cannot represent an l-value containing a swizzle that doesn't + // reduce to a simple access chain. So, we need a temporary vector to + // receive the result, and must later swizzle that into the original + // l-value. + complexLvalues.push_back(builder.getAccessChain()); + temporaryLvalues.push_back(builder.createVariable( + spv::NoPrecision, spv::StorageClassFunction, + builder.accessChainGetInferredType(), "swizzleTemp")); + operands.push_back(temporaryLvalues.back()); + } else { + operands.push_back(builder.accessChainGetLValue()); + } + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()); + } else { + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + glslang::TOperator glslangOp = node->getOp(); + if (arg == 1 && + (glslangOp == glslang::EOpRayQueryGetIntersectionType || + glslangOp == glslang::EOpRayQueryGetIntersectionT || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceCustomIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceId || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset || + glslangOp == glslang::EOpRayQueryGetIntersectionGeometryIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionPrimitiveIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionBarycentrics || + glslangOp == glslang::EOpRayQueryGetIntersectionFrontFace || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayDirection || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayOrigin || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectToWorld || + glslangOp == glslang::EOpRayQueryGetIntersectionWorldToObject || + glslangOp == glslang::EOpRayQueryGetIntersectionTriangleVertexPositionsEXT || + glslangOp == glslang::EOpRayQueryGetIntersectionClusterIdNV || + glslangOp == glslang::EOpRayQueryGetIntersectionSpherePositionNV || + glslangOp == glslang::EOpRayQueryGetIntersectionSphereRadiusNV || + glslangOp == glslang::EOpRayQueryGetIntersectionLSSHitValueNV || + glslangOp == glslang::EOpRayQueryGetIntersectionLSSPositionsNV || + glslangOp == glslang::EOpRayQueryGetIntersectionLSSRadiiNV || + glslangOp == glslang::EOpRayQueryIsLSSHitNV || + glslangOp == glslang::EOpRayQueryIsSphereHitNV + )) { + bool cond = glslangOperands[arg]->getAsConstantUnion()->getConstArray()[0].getBConst(); + operands.push_back(builder.makeIntConstant(cond ? 1 : 0)); + } else if ((arg == 10 && glslangOp == glslang::EOpTraceKHR) || + (arg == 11 && glslangOp == glslang::EOpTraceRayMotionNV) || + (arg == 1 && glslangOp == glslang::EOpExecuteCallableKHR) || + (arg == 1 && glslangOp == glslang::EOpHitObjectExecuteShaderNV) || + (arg == 11 && glslangOp == glslang::EOpHitObjectTraceRayNV) || + (arg == 12 && glslangOp == glslang::EOpHitObjectTraceRayMotionNV)) { + const int set = glslangOp == glslang::EOpExecuteCallableKHR ? 1 : 0; + const int location = glslangOperands[arg]->getAsConstantUnion()->getConstArray()[0].getUConst(); + auto itNode = locationToSymbol[set].find(location); + visitSymbol(itNode->second); + spv::Id symId = getSymbolId(itNode->second); + operands.push_back(symId); + } else if ((arg == 12 && glslangOp == glslang::EOpHitObjectRecordHitNV) || + (arg == 13 && glslangOp == glslang::EOpHitObjectRecordHitMotionNV) || + (arg == 11 && glslangOp == glslang::EOpHitObjectRecordHitWithIndexNV) || + (arg == 12 && glslangOp == glslang::EOpHitObjectRecordHitWithIndexMotionNV) || + (arg == 1 && glslangOp == glslang::EOpHitObjectGetAttributesNV)) { + const int location = glslangOperands[arg]->getAsConstantUnion()->getConstArray()[0].getUConst(); + const int set = 2; + auto itNode = locationToSymbol[set].find(location); + visitSymbol(itNode->second); + spv::Id symId = getSymbolId(itNode->second); + operands.push_back(symId); + } else if (glslangOperands[arg]->getAsTyped()->getQualifier().isSpirvLiteral()) { + // Will be translated to a literal value, make a placeholder here + operands.push_back(spv::NoResult); + } else if (glslangOperands[arg]->getAsTyped()->getBasicType() == glslang::EbtFunction) { + spv::Function* function = functionMap[glslangOperands[arg]->getAsSymbolNode()->getMangledName().c_str()]; + assert(function); + operands.push_back(function->getId()); + } else { + operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType())); + } + } + } + + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + if (node->getOp() == glslang::EOpCooperativeMatrixLoadTensorNV) { + std::vector idImmOps; + + builder.addCapability(spv::CapabilityCooperativeMatrixTensorAddressingNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + + spv::Id object = builder.createLoad(operands[0], spv::NoPrecision); + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // Pointer + idImmOps.push_back(spv::IdImmediate(true, object)); // Object + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // tensorLayout + + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); // memoryaccess + + // initialize tensor operands to zero, then OR in flags based on the operands + size_t tensorOpIdx = idImmOps.size(); + idImmOps.push_back(spv::IdImmediate(false, 0)); + + for (uint32_t i = 3; i < operands.size(); ++i) { + if (builder.isTensorView(operands[i])) { + idImmOps[tensorOpIdx].word |= spv::TensorAddressingOperandsTensorViewMask; + } else { + // must be the decode func + idImmOps[tensorOpIdx].word |= spv::TensorAddressingOperandsDecodeFuncMask; + builder.addCapability(spv::CapabilityCooperativeMatrixBlockLoadsNV); + } + idImmOps.push_back(spv::IdImmediate(true, operands[i])); // tensorView or decodeFunc + } + + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + // do the op + spv::Id result = builder.createOp(spv::OpCooperativeMatrixLoadTensorNV, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixLoad || + node->getOp() == glslang::EOpCooperativeMatrixLoadNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + if (node->getOp() == glslang::EOpCooperativeMatrixLoad) { + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // matrixLayout + auto layout = builder.getConstantScalar(operands[3]); + if (layout == spv::CooperativeMatrixLayoutRowBlockedInterleavedARM || + layout == spv::CooperativeMatrixLayoutColumnBlockedInterleavedARM) { + builder.addExtension(spv::E_SPV_ARM_cooperative_matrix_layouts); + builder.addCapability(spv::CapabilityCooperativeMatrixLayoutsARM); + } + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + } else { + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + } + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + // do the op + spv::Id result = node->getOp() == glslang::EOpCooperativeMatrixLoad + ? builder.createOp(spv::OpCooperativeMatrixLoadKHR, typeId, idImmOps) + : builder.createOp(spv::OpCooperativeMatrixLoadNV, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixStoreTensorNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object + + builder.addCapability(spv::CapabilityCooperativeMatrixTensorAddressingNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // tensorLayout + + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); // memoryaccess + + if (operands.size() > 3) { + idImmOps.push_back(spv::IdImmediate(false, spv::TensorAddressingOperandsTensorViewMask)); + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // tensorView + } else { + idImmOps.push_back(spv::IdImmediate(false, 0)); + } + + builder.createNoResultOp(spv::OpCooperativeMatrixStoreTensorNV, idImmOps); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixStore || + node->getOp() == glslang::EOpCooperativeMatrixStoreNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object + if (node->getOp() == glslang::EOpCooperativeMatrixStore) { + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // matrixLayout + auto layout = builder.getConstantScalar(operands[3]); + if (layout == spv::CooperativeMatrixLayoutRowBlockedInterleavedARM || + layout == spv::CooperativeMatrixLayoutColumnBlockedInterleavedARM) { + builder.addExtension(spv::E_SPV_ARM_cooperative_matrix_layouts); + builder.addCapability(spv::CapabilityCooperativeMatrixLayoutsARM); + } + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + } else { + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + } + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + + if (node->getOp() == glslang::EOpCooperativeMatrixStore) + builder.createNoResultOp(spv::OpCooperativeMatrixStoreKHR, idImmOps); + else + builder.createNoResultOp(spv::OpCooperativeMatrixStoreNV, idImmOps); + result = 0; + } else if (node->getOp() == glslang::EOpRayQueryGetIntersectionTriangleVertexPositionsEXT) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // q + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // committed + + spv::Id typeId = builder.makeArrayType(builder.makeVectorType(builder.makeFloatType(32), 3), + builder.makeUintConstant(3), 0); + // do the op + + spv::Op spvOp = spv::OpRayQueryGetIntersectionTriangleVertexPositionsKHR; + + spv::Id result = builder.createOp(spvOp, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[2]); + result = 0; + } else if (node->getOp() == glslang::EOpRayQueryGetIntersectionLSSPositionsNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // q + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // committed + + spv::Id typeId = builder.makeArrayType(builder.makeVectorType(builder.makeFloatType(32), 3), + builder.makeUintConstant(2), 0); + // do the op + + spv::Op spvOp = spv::OpRayQueryGetIntersectionLSSPositionsNV; + + spv::Id result = builder.createOp(spvOp, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[2]); + result = 0; + } else if (node->getOp() == glslang::EOpRayQueryGetIntersectionLSSRadiiNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // q + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // committed + + spv::Id typeId = builder.makeArrayType(builder.makeFloatType(32), + builder.makeUintConstant(2), 0); + // do the op + + spv::Op spvOp = spv::OpRayQueryGetIntersectionLSSRadiiNV; + + spv::Id result = builder.createOp(spvOp, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[2]); + result = 0; + } else if (node->getOp() == glslang::EOpHitObjectGetLSSPositionsNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // hitObject + + spv::Op spvOp = spv::OpHitObjectGetLSSPositionsNV; + spv::Id typeId = builder.makeArrayType(builder.makeVectorType(builder.makeFloatType(32), 3), + builder.makeUintConstant(2), 0); + + spv::Id result = builder.createOp(spvOp, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[1]); + result = 0; + } else if (node->getOp() == glslang::EOpHitObjectGetLSSRadiiNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // hitObject + + spv::Op spvOp = spv::OpHitObjectGetLSSRadiiNV; + spv::Id typeId = builder.makeArrayType(builder.makeFloatType(32), + builder.makeUintConstant(2), 0); + + spv::Id result = builder.createOp(spvOp, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[1]); + result = 0; + + } else if (node->getOp() == glslang::EOpCooperativeMatrixMulAdd) { + uint32_t matrixOperands = 0; + + // If the optional operand is present, initialize matrixOperands to that value. + if (glslangOperands.size() == 4 && glslangOperands[3]->getAsConstantUnion()) { + matrixOperands = glslangOperands[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + } + + // Determine Cooperative Matrix Operands bits from the signedness of the types. + if (isTypeSignedInt(glslangOperands[0]->getAsTyped()->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixASignedComponentsKHRMask; + if (isTypeSignedInt(glslangOperands[1]->getAsTyped()->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixBSignedComponentsKHRMask; + if (isTypeSignedInt(glslangOperands[2]->getAsTyped()->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixCSignedComponentsKHRMask; + if (isTypeSignedInt(node->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask; + + std::vector idImmOps; + idImmOps.push_back(spv::IdImmediate(true, operands[0])); + idImmOps.push_back(spv::IdImmediate(true, operands[1])); + idImmOps.push_back(spv::IdImmediate(true, operands[2])); + if (matrixOperands != 0) + idImmOps.push_back(spv::IdImmediate(false, matrixOperands)); + + result = builder.createOp(spv::OpCooperativeMatrixMulAddKHR, resultType(), idImmOps); + } else if (node->getOp() == glslang::EOpCooperativeMatrixReduceNV) { + builder.addCapability(spv::CapabilityCooperativeMatrixReductionsNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + + spv::Op opcode = spv::OpCooperativeMatrixReduceNV; + unsigned mask = glslangOperands[2]->getAsConstantUnion()->getConstArray()[0].getUConst(); + + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + + result = builder.createCooperativeMatrixReduce(opcode, typeId, operands[1], mask, operands[3]); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixPerElementOpNV) { + builder.addCapability(spv::CapabilityCooperativeMatrixPerElementOperationsNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + + result = builder.createCooperativeMatrixPerElementOp(typeId, operands); + // store the result to the pointer + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixTransposeNV) { + + builder.addCapability(spv::CapabilityCooperativeMatrixConversionsNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix2); + + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + + result = builder.createUnaryOp(spv::OpCooperativeMatrixTransposeNV, typeId, operands[1]); + // store the result to the pointer + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeVectorMatMulNV || + node->getOp() == glslang::EOpCooperativeVectorMatMulAddNV) { + uint32_t matrixOperands = 0; + + bool isMulAdd = node->getOp() == glslang::EOpCooperativeVectorMatMulAddNV; + + // Determine Cooperative Matrix Operands bits from the signedness of the types. + + if (isTypeSignedInt(glslangOperands[1]->getAsTyped()->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixBSignedComponentsKHRMask; + if (isTypeSignedInt(glslangOperands[0]->getAsTyped()->getBasicType())) + matrixOperands |= spv::CooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask; + + uint32_t opIdx = 1; + std::vector idImmOps; + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // Input + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // InputInterpretation + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // Matrix + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // MatrixOffset + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // MatrixInterpretation + if (isMulAdd) { + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // Bias + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // BiasOffset + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // BiasInterpretation + } + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // M + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // K + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // MemoryLayout + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // Transpose + idImmOps.push_back(spv::IdImmediate(true, operands[opIdx++])); // MatrixStride + if (matrixOperands != 0) + idImmOps.push_back(spv::IdImmediate(false, matrixOperands)); // Cooperative Matrix Operands + + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeVectorType(typeId)); + // do the op + spv::Id result = builder.createOp(isMulAdd ? spv::OpCooperativeVectorMatrixMulAddNV : spv::OpCooperativeVectorMatrixMulNV, typeId, idImmOps); + // store the result to the pointer (out param 'res') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeVectorLoadNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // offset + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeVectorType(typeId)); + // do the op + spv::Id result = builder.createOp(spv::OpCooperativeVectorLoadNV, typeId, idImmOps); + // store the result to the pointer (out param 'v') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeVectorStoreNV) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // offset + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + builder.createNoResultOp(spv::OpCooperativeVectorStoreNV, idImmOps); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeVectorOuterProductAccumulateNV) { + builder.addCapability(spv::CapabilityCooperativeVectorTrainingNV); + builder.addExtension(spv::E_SPV_NV_cooperative_vector); + + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // Matrix + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // Offset + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // A + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // B + idImmOps.push_back(spv::IdImmediate(true, operands[5])); // MemoryLayout + idImmOps.push_back(spv::IdImmediate(true, operands[6])); // MatrixInterpretation + idImmOps.push_back(spv::IdImmediate(true, operands[4])); // Stride + builder.createNoResultOp(spv::OpCooperativeVectorOuterProductAccumulateNV, idImmOps); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeVectorReduceSumAccumulateNV) { + builder.addCapability(spv::CapabilityCooperativeVectorTrainingNV); + builder.addExtension(spv::E_SPV_NV_cooperative_vector); + + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // Buf + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // Offset + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // A + builder.createNoResultOp(spv::OpCooperativeVectorReduceSumAccumulateNV, idImmOps); + result = 0; + } else if (atomic) { + // Handle all atomics + glslang::TBasicType typeProxy = (node->getOp() == glslang::EOpAtomicStore) + ? node->getSequence()[0]->getAsTyped()->getBasicType() : node->getBasicType(); + result = createAtomicOperation(node->getOp(), precision, resultType(), operands, typeProxy, + lvalueCoherentFlags, node->getType()); + } else if (node->getOp() == glslang::EOpSpirvInst) { + const auto& spirvInst = node->getSpirvInstruction(); + if (spirvInst.set == "") { + std::vector idImmOps; + for (unsigned int i = 0; i < glslangOperands.size(); ++i) { + if (glslangOperands[i]->getAsTyped()->getQualifier().isSpirvLiteral()) { + // Translate the constant to a literal value + std::vector literals; + glslang::TVector constants; + constants.push_back(glslangOperands[i]->getAsConstantUnion()); + TranslateLiterals(constants, literals); + idImmOps.push_back({false, literals[0]}); + } else + idImmOps.push_back({true, operands[i]}); + } + + if (node->getBasicType() == glslang::EbtVoid) + builder.createNoResultOp(static_cast(spirvInst.id), idImmOps); + else + result = builder.createOp(static_cast(spirvInst.id), resultType(), idImmOps); + } else { + result = builder.createBuiltinCall( + resultType(), spirvInst.set == "GLSL.std.450" ? stdBuiltins : getExtBuiltins(spirvInst.set.c_str()), + spirvInst.id, operands); + } + noReturnValue = node->getBasicType() == glslang::EbtVoid; + } else if (node->getOp() == glslang::EOpDebugPrintf) { + if (!nonSemanticDebugPrintf) { + nonSemanticDebugPrintf = builder.import("NonSemantic.DebugPrintf"); + } + result = builder.createBuiltinCall(builder.makeVoidType(), nonSemanticDebugPrintf, spv::NonSemanticDebugPrintfDebugPrintf, operands); + builder.addExtension(spv::E_SPV_KHR_non_semantic_info); + } else { + // Pass through to generic operations. + switch (glslangOperands.size()) { + case 0: + result = createNoArgOperation(node->getOp(), precision, resultType()); + break; + case 1: + { + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createUnaryOperation( + node->getOp(), decorations, + resultType(), operands.front(), + glslangOperands[0]->getAsTyped()->getBasicType(), lvalueCoherentFlags, node->getType()); + } + break; + default: + result = createMiscOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); + break; + } + + if (invertedType != spv::NoResult) + result = createInvertedSwizzle(precision, *glslangOperands[0]->getAsBinaryNode(), result); + + for (unsigned int i = 0; i < temporaryLvalues.size(); ++i) { + builder.setAccessChain(complexLvalues[i]); + builder.accessChainStore(builder.createLoad(temporaryLvalues[i], spv::NoPrecision), + TranslateNonUniformDecoration(complexLvalues[i].coherentFlags)); + } + } + + if (noReturnValue) + return false; + + if (! result) { + logger->missingFunctionality("unknown glslang aggregate"); + return true; // pick up a child as a placeholder operand + } else { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + } +} + +// This path handles both if-then-else and ?: +// The if-then-else has a node type of void, while +// ?: has either a void or a non-void node type +// +// Leaving the result, when not void: +// GLSL only has r-values as the result of a :?, but +// if we have an l-value, that can be more efficient if it will +// become the base of a complex r-value expression, because the +// next layer copies r-values into memory to use the access-chain mechanism +bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) +{ + // see if OpSelect can handle it + const auto isOpSelectable = [&]() { + if (node->getBasicType() == glslang::EbtVoid) + return false; + // OpSelect can do all other types starting with SPV 1.4 + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4) { + // pre-1.4, only scalars and vectors can be handled + if ((!node->getType().isScalar() && !node->getType().isVector())) + return false; + } + return true; + }; + + // See if it simple and safe, or required, to execute both sides. + // Crucially, side effects must be either semantically required or avoided, + // and there are performance trade-offs. + // Return true if required or a good idea (and safe) to execute both sides, + // false otherwise. + const auto bothSidesPolicy = [&]() -> bool { + // do we have both sides? + if (node->getTrueBlock() == nullptr || + node->getFalseBlock() == nullptr) + return false; + + // required? (unless we write additional code to look for side effects + // and make performance trade-offs if none are present) + if (!node->getShortCircuit()) + return true; + + // if not required to execute both, decide based on performance/practicality... + + if (!isOpSelectable()) + return false; + + assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() && + node->getType() == node->getFalseBlock()->getAsTyped()->getType()); + + // return true if a single operand to ? : is okay for OpSelect + const auto operandOkay = [](glslang::TIntermTyped* node) { + return node->getAsSymbolNode() || node->getType().getQualifier().isConstant(); + }; + + return operandOkay(node->getTrueBlock() ->getAsTyped()) && + operandOkay(node->getFalseBlock()->getAsTyped()); + }; + + spv::Id result = spv::NoResult; // upcoming result selecting between trueValue and falseValue + // emit the condition before doing anything with selection + node->getCondition()->traverse(this); + spv::Id condition = accessChainLoad(node->getCondition()->getType()); + + // Find a way of executing both sides and selecting the right result. + const auto executeBothSides = [&]() -> void { + // execute both sides + spv::Id resultType = convertGlslangToSpvType(node->getType()); + node->getTrueBlock()->traverse(this); + spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + node->getFalseBlock()->traverse(this); + spv::Id falseValue = accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()); + + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + // done if void + if (node->getBasicType() == glslang::EbtVoid) + return; + + // emit code to select between trueValue and falseValue + // see if OpSelect can handle the result type, and that the SPIR-V types + // of the inputs match the result type. + if (isOpSelectable()) { + // Emit OpSelect for this selection. + + // smear condition to vector, if necessary (AST is always scalar) + // Before 1.4, smear like for mix(), starting with 1.4, keep it scalar + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4 && builder.isVector(trueValue)) { + condition = builder.smearScalar(spv::NoPrecision, condition, + builder.makeVectorType(builder.makeBoolType(), + builder.getNumComponents(trueValue))); + } + + // If the types do not match, it is because of mismatched decorations on aggregates. + // Since isOpSelectable only lets us get here for SPIR-V >= 1.4, we can use OpCopyObject + // to get matching types. + if (builder.getTypeId(trueValue) != resultType) { + trueValue = builder.createUnaryOp(spv::OpCopyLogical, resultType, trueValue); + } + if (builder.getTypeId(falseValue) != resultType) { + falseValue = builder.createUnaryOp(spv::OpCopyLogical, resultType, falseValue); + } + + // OpSelect + result = builder.createTriOp(spv::OpSelect, resultType, condition, trueValue, falseValue); + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else { + // We need control flow to select the result. + // TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path. + result = builder.createVariable(TranslatePrecisionDecoration(node->getType()), + spv::StorageClassFunction, resultType); + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + multiTypeStore(node->getType(), trueValue); + + ifBuilder.makeBeginElse(); + // emit the "else" statement + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + multiTypeStore(node->getType(), falseValue); + + // finish off the control flow + ifBuilder.makeEndIf(); + + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Execute the one side needed, as per the condition + const auto executeOneSide = [&]() { + // Always emit control flow. + if (node->getBasicType() != glslang::EbtVoid) { + result = builder.createVariable(TranslatePrecisionDecoration(node->getType()), spv::StorageClassFunction, + convertGlslangToSpvType(node->getType())); + } + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + if (node->getTrueBlock() != nullptr) { + node->getTrueBlock()->traverse(this); + if (result != spv::NoResult) { + spv::Id load = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + multiTypeStore(node->getType(), load); + } + } + + if (node->getFalseBlock() != nullptr) { + ifBuilder.makeBeginElse(); + // emit the "else" statement + node->getFalseBlock()->traverse(this); + if (result != spv::NoResult) { + spv::Id load = accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()); + + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + multiTypeStore(node->getType(), load); + } + } + + // finish off the control flow + ifBuilder.makeEndIf(); + + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Try for OpSelect (or a requirement to execute both sides) + if (bothSidesPolicy()) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + executeBothSides(); + } else + executeOneSide(); + + return false; +} + +bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node) +{ + // emit and get the condition before doing anything with switch + node->getCondition()->traverse(this); + spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType()); + + // Selection control: + const spv::SelectionControlMask control = TranslateSwitchControl(*node); + + // browse the children to sort out code segments + int defaultSegment = -1; + std::vector codeSegments; + glslang::TIntermSequence& sequence = node->getBody()->getSequence(); + std::vector caseValues; + std::vector valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate + for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) { + TIntermNode* child = *c; + if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) + defaultSegment = (int)codeSegments.size(); + else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) { + valueIndexToSegment[caseValues.size()] = (int)codeSegments.size(); + caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion() + ->getConstArray()[0].getIConst()); + } else + codeSegments.push_back(child); + } + + // handle the case where the last code segment is missing, due to no code + // statements between the last case and the end of the switch statement + if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) || + (int)codeSegments.size() == defaultSegment) + codeSegments.push_back(nullptr); + + // make the switch statement + std::vector segmentBlocks; // returned, as the blocks allocated in the call + builder.makeSwitch(selector, control, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, + segmentBlocks); + + // emit all the code in the segments + breakForLoop.push(false); + for (unsigned int s = 0; s < codeSegments.size(); ++s) { + builder.nextSwitchSegment(segmentBlocks, s); + if (codeSegments[s]) + codeSegments[s]->traverse(this); + else + builder.addSwitchBreak(true); + } + breakForLoop.pop(); + + builder.endSwitch(segmentBlocks); + + return false; +} + +void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) +{ + if (node->getQualifier().isSpirvLiteral()) + return; // Translated to a literal value, skip further processing + + int nextConst = 0; + spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false); + + builder.clearAccessChain(); + builder.setAccessChainRValue(constant); +} + +bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node) +{ + auto blocks = builder.makeNewLoop(); + builder.createBranch(true, &blocks.head); + + // Loop control: + std::vector operands; + const spv::LoopControlMask control = TranslateLoopControl(*node, operands); + + // Spec requires back edges to target header blocks, and every header block + // must dominate its merge block. Make a header block first to ensure these + // conditions are met. By definition, it will contain OpLoopMerge, followed + // by a block-ending branch. But we don't want to put any other body/test + // instructions in it, since the body/test may have arbitrary instructions, + // including merges of its own. + builder.setBuildPoint(&blocks.head); + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, operands); + if (node->testFirst() && node->getTest()) { + spv::Block& test = builder.makeNewBlock(); + builder.createBranch(true, &test); + + builder.setBuildPoint(&test); + node->getTest()->traverse(this); + spv::Id condition = accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.body, &blocks.merge); + + builder.setBuildPoint(&blocks.body); + breakForLoop.push(true); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(true, &blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + builder.createBranch(true, &blocks.head); + } else { + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + builder.createBranch(true, &blocks.body); + + breakForLoop.push(true); + builder.setBuildPoint(&blocks.body); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(true, &blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + if (node->getTest()) { + node->getTest()->traverse(this); + spv::Id condition = + accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.head, &blocks.merge); + } else { + // TODO: unless there was a break/return/discard instruction + // somewhere in the body, this is an infinite loop, so we should + // issue a warning. + builder.createBranch(true, &blocks.head); + } + } + builder.setBuildPoint(&blocks.merge); + builder.closeLoop(); + return false; +} + +bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node) +{ + if (node->getExpression()) + node->getExpression()->traverse(this); + + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + switch (node->getFlowOp()) { + case glslang::EOpKill: + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { + if (glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + builder.addCapability(spv::CapabilityDemoteToHelperInvocation); + builder.createNoResultOp(spv::OpDemoteToHelperInvocationEXT); + } else { + builder.makeStatementTerminator(spv::OpTerminateInvocation, "post-terminate-invocation"); + } + } else { + builder.makeStatementTerminator(spv::OpKill, "post-discard"); + } + break; + case glslang::EOpTerminateInvocation: + builder.addExtension(spv::E_SPV_KHR_terminate_invocation); + builder.makeStatementTerminator(spv::OpTerminateInvocation, "post-terminate-invocation"); + break; + case glslang::EOpBreak: + if (breakForLoop.top()) + builder.createLoopExit(); + else + builder.addSwitchBreak(false); + break; + case glslang::EOpContinue: + builder.createLoopContinue(); + break; + case glslang::EOpReturn: + if (node->getExpression() != nullptr) { + const glslang::TType& glslangReturnType = node->getExpression()->getType(); + spv::Id returnId = accessChainLoad(glslangReturnType); + if (builder.getTypeId(returnId) != currentFunction->getReturnType() || + TranslatePrecisionDecoration(glslangReturnType) != currentFunction->getReturnPrecision()) { + builder.clearAccessChain(); + spv::Id copyId = builder.createVariable(currentFunction->getReturnPrecision(), + spv::StorageClassFunction, currentFunction->getReturnType()); + builder.setAccessChainLValue(copyId); + multiTypeStore(glslangReturnType, returnId); + returnId = builder.createLoad(copyId, currentFunction->getReturnPrecision()); + } + builder.makeReturn(false, returnId); + } else + builder.makeReturn(false); + + builder.clearAccessChain(); + break; + + case glslang::EOpDemote: + builder.createNoResultOp(spv::OpDemoteToHelperInvocationEXT); + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + break; + case glslang::EOpTerminateRayKHR: + builder.makeStatementTerminator(spv::OpTerminateRayKHR, "post-terminateRayKHR"); + break; + case glslang::EOpIgnoreIntersectionKHR: + builder.makeStatementTerminator(spv::OpIgnoreIntersectionKHR, "post-ignoreIntersectionKHR"); + break; + + default: + assert(0); + break; + } + + return false; +} + +spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node, spv::Id forcedType) +{ + // First, steer off constants, which are not SPIR-V variables, but + // can still have a mapping to a SPIR-V Id. + // This includes specialization constants. + if (node->getQualifier().isConstant()) { + spv::Id result = createSpvConstant(*node); + if (result != spv::NoResult) + return result; + } + + // Now, handle actual variables + spv::StorageClass storageClass = TranslateStorageClass(node->getType()); + spv::Id spvType = forcedType == spv::NoType ? convertGlslangToSpvType(node->getType()) + : forcedType; + + const bool contains16BitType = node->getType().contains16BitFloat() || + node->getType().contains16BitInt(); + if (contains16BitType) { + switch (storageClass) { + case spv::StorageClassInput: + case spv::StorageClassOutput: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageInputOutput16); + break; + case spv::StorageClassUniform: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + if (node->getType().getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + else + builder.addCapability(spv::CapabilityStorageUniform16); + break; + case spv::StorageClassPushConstant: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStoragePushConstant16); + break; + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + break; + default: + if (storageClass == spv::StorageClassWorkgroup && + node->getType().getBasicType() == glslang::EbtBlock) { + builder.addCapability(spv::CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR); + break; + } + if (node->getType().contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + if (node->getType().contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + break; + } + } + + if (node->getType().contains8BitInt()) { + if (storageClass == spv::StorageClassPushConstant) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStoragePushConstant8); + } else if (storageClass == spv::StorageClassUniform) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformAndStorageBuffer8BitAccess); + } else if (storageClass == spv::StorageClassStorageBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageBuffer8BitAccess); + } else if (storageClass == spv::StorageClassWorkgroup && + node->getType().getBasicType() == glslang::EbtBlock) { + builder.addCapability(spv::CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR); + } else { + builder.addCapability(spv::CapabilityInt8); + } + } + + const char* name = node->getName().c_str(); + if (glslang::IsAnonymous(name)) + name = ""; + + spv::Id initializer = spv::NoResult; + + if (node->getType().getQualifier().storage == glslang::EvqUniform && !node->getConstArray().empty()) { + int nextConst = 0; + initializer = createSpvConstantFromConstUnionArray(node->getType(), + node->getConstArray(), + nextConst, + false /* specConst */); + } else if (node->getType().getQualifier().isNullInit()) { + initializer = builder.makeNullConstant(spvType); + } + + spv::Id var = builder.createVariable(spv::NoPrecision, storageClass, spvType, name, initializer, false); + std::vector topLevelDecorations; + glslang::TQualifier typeQualifier = node->getType().getQualifier(); + TranslateMemoryDecoration(typeQualifier, topLevelDecorations, glslangIntermediate->usingVulkanMemoryModel()); + for (auto deco : topLevelDecorations) { + builder.addDecoration(var, deco); + } + return var; +} + +// Return type Id of the sampled type. +spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler) +{ + switch (sampler.type) { + case glslang::EbtInt: return builder.makeIntType(32); + case glslang::EbtUint: return builder.makeUintType(32); + case glslang::EbtFloat: return builder.makeFloatType(32); + case glslang::EbtFloat16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float_fetch); + builder.addCapability(spv::CapabilityFloat16ImageAMD); + return builder.makeFloatType(16); + case glslang::EbtInt64: + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityInt64ImageEXT); + return builder.makeIntType(64); + case glslang::EbtUint64: + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityInt64ImageEXT); + return builder.makeUintType(64); + default: + assert(0); + return builder.makeFloatType(32); + } +} + +// If node is a swizzle operation, return the type that should be used if +// the swizzle base is first consumed by another operation, before the swizzle +// is applied. +spv::Id TGlslangToSpvTraverser::getInvertedSwizzleType(const glslang::TIntermTyped& node) +{ + if (node.getAsOperator() && + node.getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + return convertGlslangToSpvType(node.getAsBinaryNode()->getLeft()->getType()); + else + return spv::NoType; +} + +// When inverting a swizzle with a parent op, this function +// will apply the swizzle operation to a completed parent operation. +spv::Id TGlslangToSpvTraverser::createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped& node, + spv::Id parentResult) +{ + std::vector swizzle; + convertSwizzle(*node.getAsBinaryNode()->getRight()->getAsAggregate(), swizzle); + return builder.createRvalueSwizzle(precision, convertGlslangToSpvType(node.getType()), parentResult, swizzle); +} + +// Convert a glslang AST swizzle node to a swizzle vector for building SPIR-V. +void TGlslangToSpvTraverser::convertSwizzle(const glslang::TIntermAggregate& node, std::vector& swizzle) +{ + const glslang::TIntermSequence& swizzleSequence = node.getSequence(); + for (int i = 0; i < (int)swizzleSequence.size(); ++i) + swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); +} + +// Convert from a glslang type to an SPV type, by calling into a +// recursive version of this function. This establishes the inherited +// layout state rooted from the top-level type. +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly) +{ + return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly); +} + +spv::LinkageType TGlslangToSpvTraverser::convertGlslangLinkageToSpv(glslang::TLinkType linkType) +{ + switch (linkType) { + case glslang::ELinkExport: + return spv::LinkageTypeExport; + default: + return spv::LinkageTypeMax; + } +} + +// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangStructToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, + glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier, + bool lastBufferBlockMember, bool forwardReferenceOnly) +{ + spv::Id spvType = spv::NoResult; + + switch (type.getBasicType()) { + case glslang::EbtVoid: + spvType = builder.makeVoidType(); + assert (! type.isArray()); + break; + case glslang::EbtBool: + // "transparent" bool doesn't exist in SPIR-V. The GLSL convention is + // a 32-bit int where non-0 means true. + if (explicitLayout != glslang::ElpNone) + spvType = builder.makeUintType(32); + else + spvType = builder.makeBoolType(); + break; + case glslang::EbtInt: + spvType = builder.makeIntType(32); + break; + case glslang::EbtUint: + spvType = builder.makeUintType(32); + break; + case glslang::EbtFloat: + spvType = builder.makeFloatType(32); + break; + case glslang::EbtDouble: + spvType = builder.makeFloatType(64); + break; + case glslang::EbtFloat16: + spvType = builder.makeFloatType(16); + break; + case glslang::EbtBFloat16: + spvType = builder.makeBFloat16Type(); + break; + case glslang::EbtInt8: + spvType = builder.makeIntType(8); + break; + case glslang::EbtUint8: + spvType = builder.makeUintType(8); + break; + case glslang::EbtInt16: + spvType = builder.makeIntType(16); + break; + case glslang::EbtUint16: + spvType = builder.makeUintType(16); + break; + case glslang::EbtInt64: + spvType = builder.makeIntType(64); + break; + case glslang::EbtUint64: + spvType = builder.makeUintType(64); + break; + case glslang::EbtAtomicUint: + builder.addCapability(spv::CapabilityAtomicStorage); + spvType = builder.makeUintType(32); + break; + case glslang::EbtAccStruct: + switch (glslangIntermediate->getStage()) { + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + // these all should have the RayTracingNV/KHR capability already + break; + default: + { + auto& extensions = glslangIntermediate->getRequestedExtensions(); + if (extensions.find("GL_EXT_ray_query") != extensions.end()) { + builder.addExtension(spv::E_SPV_KHR_ray_query); + builder.addCapability(spv::CapabilityRayQueryKHR); + } + } + break; + } + spvType = builder.makeAccelerationStructureType(); + break; + case glslang::EbtRayQuery: + { + auto& extensions = glslangIntermediate->getRequestedExtensions(); + if (extensions.find("GL_EXT_ray_query") != extensions.end()) { + builder.addExtension(spv::E_SPV_KHR_ray_query); + builder.addCapability(spv::CapabilityRayQueryKHR); + } + spvType = builder.makeRayQueryType(); + } + break; + case glslang::EbtReference: + { + // Make the forward pointer, then recurse to convert the structure type, then + // patch up the forward pointer with a real pointer type. + if (forwardPointers.find(type.getReferentType()) == forwardPointers.end()) { + spv::Id forwardId = builder.makeForwardPointer(spv::StorageClassPhysicalStorageBufferEXT); + forwardPointers[type.getReferentType()] = forwardId; + } + spvType = forwardPointers[type.getReferentType()]; + if (!forwardReferenceOnly) { + spv::Id referentType = convertGlslangToSpvType(*type.getReferentType()); + builder.makePointerFromForwardPointer(spv::StorageClassPhysicalStorageBufferEXT, + forwardPointers[type.getReferentType()], + referentType); + } + } + break; + case glslang::EbtSampler: + { + const glslang::TSampler& sampler = type.getSampler(); + if (sampler.isPureSampler()) { + spvType = builder.makeSamplerType(); + } else { + // an image is present, make its type + spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), + sampler.isShadow(), sampler.isArrayed(), sampler.isMultiSample(), + sampler.isImageClass() ? 2 : 1, TranslateImageFormat(type)); + if (sampler.isCombined() && + (!sampler.isBuffer() || glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_6)) { + // Already has both image and sampler, make the combined type. Only combine sampler to + // buffer if before SPIR-V 1.6. + spvType = builder.makeSampledImageType(spvType); + } + } + } + break; + case glslang::EbtStruct: + case glslang::EbtBlock: + { + // If we've seen this struct type, return it + const glslang::TTypeList* glslangMembers = type.getStruct(); + + // Try to share structs for different layouts, but not yet for other + // kinds of qualification (primarily not yet including interpolant qualification). + if (! HasNonLayoutQualifiers(type, qualifier)) + spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers]; + if (spvType != spv::NoResult) + break; + + // else, we haven't seen it... + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]].resize(glslangMembers->size()); + spvType = convertGlslangStructToSpvType(type, glslangMembers, explicitLayout, qualifier); + } + break; + case glslang::EbtString: + // no type used for OpString + return 0; + + case glslang::EbtHitObjectNV: { + builder.addExtension(spv::E_SPV_NV_shader_invocation_reorder); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + spvType = builder.makeHitObjectNVType(); + } + break; + case glslang::EbtSpirvType: { + // GL_EXT_spirv_intrinsics + const auto& spirvType = type.getSpirvType(); + const auto& spirvInst = spirvType.spirvInst; + + std::vector operands; + for (const auto& typeParam : spirvType.typeParams) { + if (typeParam.getAsConstant() != nullptr) { + // Constant expression + auto constant = typeParam.getAsConstant(); + if (constant->isLiteral()) { + if (constant->getBasicType() == glslang::EbtFloat) { + float floatValue = static_cast(constant->getConstArray()[0].getDConst()); + unsigned literal; + static_assert(sizeof(literal) == sizeof(floatValue), "sizeof(unsigned) != sizeof(float)"); + memcpy(&literal, &floatValue, sizeof(literal)); + operands.push_back({false, literal}); + } else if (constant->getBasicType() == glslang::EbtInt) { + unsigned literal = constant->getConstArray()[0].getIConst(); + operands.push_back({false, literal}); + } else if (constant->getBasicType() == glslang::EbtUint) { + unsigned literal = constant->getConstArray()[0].getUConst(); + operands.push_back({false, literal}); + } else if (constant->getBasicType() == glslang::EbtBool) { + unsigned literal = constant->getConstArray()[0].getBConst(); + operands.push_back({false, literal}); + } else if (constant->getBasicType() == glslang::EbtString) { + auto str = constant->getConstArray()[0].getSConst()->c_str(); + unsigned literal = 0; + char* literalPtr = reinterpret_cast(&literal); + unsigned charCount = 0; + char ch = 0; + do { + ch = *(str++); + *(literalPtr++) = ch; + ++charCount; + if (charCount == 4) { + operands.push_back({false, literal}); + literalPtr = reinterpret_cast(&literal); + charCount = 0; + } + } while (ch != 0); + + // Partial literal is padded with 0 + if (charCount > 0) { + for (; charCount < 4; ++charCount) + *(literalPtr++) = 0; + operands.push_back({false, literal}); + } + } else + assert(0); // Unexpected type + } else + operands.push_back({true, createSpvConstant(*constant)}); + } else { + // Type specifier + assert(typeParam.getAsType() != nullptr); + operands.push_back({true, convertGlslangToSpvType(*typeParam.getAsType())}); + } + } + + assert(spirvInst.set == ""); // Currently, couldn't be extended instructions. + spvType = builder.makeGenericType(static_cast(spirvInst.id), operands); + + break; + } + case glslang::EbtTensorLayoutNV: + { + builder.addCapability(spv::CapabilityTensorAddressingNV); + builder.addExtension(spv::E_SPV_NV_tensor_addressing); + + std::vector operands; + for (uint32_t i = 0; i < 2; ++i) { + operands.push_back({true, makeArraySizeId(*type.getTypeParameters()->arraySizes, i, true)}); + } + spvType = builder.makeGenericType(spv::OpTypeTensorLayoutNV, operands); + break; + } + case glslang::EbtTensorViewNV: + { + builder.addCapability(spv::CapabilityTensorAddressingNV); + builder.addExtension(spv::E_SPV_NV_tensor_addressing); + + uint32_t dim = type.getTypeParameters()->arraySizes->getDimSize(0); + assert(dim >= 1 && dim <= 5); + std::vector operands; + for (uint32_t i = 0; i < dim + 2; ++i) { + operands.push_back({true, makeArraySizeId(*type.getTypeParameters()->arraySizes, i, true, i==1)}); + } + spvType = builder.makeGenericType(spv::OpTypeTensorViewNV, operands); + break; + } + default: + assert(0); + break; + } + + if (type.isMatrix()) + spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows()); + else { + // If this variable has a vector element count greater than 1, create a SPIR-V vector + if (type.getVectorSize() > 1) + spvType = builder.makeVectorType(spvType, type.getVectorSize()); + } + + if (type.isCoopMatNV()) { + builder.addCapability(spv::CapabilityCooperativeMatrixNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix); + + if (type.getBasicType() == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityFloat16); + if (type.getBasicType() == glslang::EbtUint8 || + type.getBasicType() == glslang::EbtInt8) { + builder.addCapability(spv::CapabilityInt8); + } + + spv::Id scope = makeArraySizeId(*type.getTypeParameters()->arraySizes, 1); + spv::Id rows = makeArraySizeId(*type.getTypeParameters()->arraySizes, 2); + spv::Id cols = makeArraySizeId(*type.getTypeParameters()->arraySizes, 3); + + spvType = builder.makeCooperativeMatrixTypeNV(spvType, scope, rows, cols); + } + + if (type.isCoopMatKHR()) { + builder.addCapability(spv::CapabilityCooperativeMatrixKHR); + builder.addExtension(spv::E_SPV_KHR_cooperative_matrix); + + if (type.getBasicType() == glslang::EbtBFloat16) { + builder.addExtension(spv::E_SPV_KHR_bfloat16); + builder.addCapability(spv::CapabilityBFloat16CooperativeMatrixKHR); + } + + if (type.getBasicType() == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityFloat16); + if (type.getBasicType() == glslang::EbtUint8 || type.getBasicType() == glslang::EbtInt8) { + builder.addCapability(spv::CapabilityInt8); + } + + spv::Id scope = makeArraySizeId(*type.getTypeParameters()->arraySizes, 0); + spv::Id rows = makeArraySizeId(*type.getTypeParameters()->arraySizes, 1); + spv::Id cols = makeArraySizeId(*type.getTypeParameters()->arraySizes, 2); + spv::Id use = builder.makeUintConstant(type.getCoopMatKHRuse()); + + spvType = builder.makeCooperativeMatrixTypeKHR(spvType, scope, rows, cols, use); + } + + if (type.isCoopVecNV()) { + builder.addCapability(spv::CapabilityCooperativeVectorNV); + builder.addExtension(spv::E_SPV_NV_cooperative_vector); + + if (type.getBasicType() == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityFloat16); + if (type.getBasicType() == glslang::EbtUint8 || type.getBasicType() == glslang::EbtInt8) { + builder.addCapability(spv::CapabilityInt8); + } + + spv::Id components = makeArraySizeId(*type.getTypeParameters()->arraySizes, 0); + + spvType = builder.makeCooperativeVectorTypeNV(spvType, components); + } + + if (type.isArray()) { + int stride = 0; // keep this 0 unless doing an explicit layout; 0 will mean no decoration, no stride + + // Do all but the outer dimension + if (type.getArraySizes()->getNumDims() > 1) { + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) { + // Use a dummy glslang type for querying internal strides of + // arrays of arrays, but using just a one-dimensional array. + glslang::TType simpleArrayType(type, 0); // deference type of the array + while (simpleArrayType.getArraySizes()->getNumDims() > 1) + simpleArrayType.getArraySizes()->dereference(); + + // Will compute the higher-order strides here, rather than making a whole + // pile of types and doing repetitive recursion on their contents. + stride = getArrayStride(simpleArrayType, explicitLayout, qualifier.layoutMatrix); + } + + // make the arrays + for (int dim = type.getArraySizes()->getNumDims() - 1; dim > 0; --dim) { + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), dim), stride); + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + stride *= type.getArraySizes()->getDimSize(dim); + } + } else { + // single-dimensional array, and don't yet have stride + + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) + stride = getArrayStride(type, explicitLayout, qualifier.layoutMatrix); + } + + // Do the outer dimension, which might not be known for a runtime-sized array. + // (Unsized arrays that survive through linking will be runtime-sized arrays) + if (type.isSizedArray()) + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride); + else { + if (!lastBufferBlockMember) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityRuntimeDescriptorArrayEXT); + } + spvType = builder.makeRuntimeArray(spvType); + } + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + } + + return spvType; +} + +// Apply SPIR-V decorations to the SPIR-V object (provided by SPIR-V ID). If member index is provided, the +// decorations are applied to this member. +void TGlslangToSpvTraverser::applySpirvDecorate(const glslang::TType& type, spv::Id id, std::optional member) +{ + assert(type.getQualifier().hasSpirvDecorate()); + + const glslang::TSpirvDecorate& spirvDecorate = type.getQualifier().getSpirvDecorate(); + + // Add spirv_decorate + for (auto& decorate : spirvDecorate.decorates) { + if (!decorate.second.empty()) { + std::vector literals; + TranslateLiterals(decorate.second, literals); + if (member.has_value()) + builder.addMemberDecoration(id, *member, static_cast(decorate.first), literals); + else + builder.addDecoration(id, static_cast(decorate.first), literals); + } else { + if (member.has_value()) + builder.addMemberDecoration(id, *member, static_cast(decorate.first)); + else + builder.addDecoration(id, static_cast(decorate.first)); + } + } + + // Add spirv_decorate_id + if (member.has_value()) { + // spirv_decorate_id not applied to members + assert(spirvDecorate.decorateIds.empty()); + } else { + for (auto& decorateId : spirvDecorate.decorateIds) { + std::vector operandIds; + assert(!decorateId.second.empty()); + for (auto extraOperand : decorateId.second) { + if (extraOperand->getQualifier().isFrontEndConstant()) + operandIds.push_back(createSpvConstant(*extraOperand)); + else + operandIds.push_back(getSymbolId(extraOperand->getAsSymbolNode())); + } + builder.addDecorationId(id, static_cast(decorateId.first), operandIds); + } + } + + // Add spirv_decorate_string + for (auto& decorateString : spirvDecorate.decorateStrings) { + std::vector strings; + assert(!decorateString.second.empty()); + for (auto extraOperand : decorateString.second) { + const char* string = extraOperand->getConstArray()[0].getSConst()->c_str(); + strings.push_back(string); + } + if (member.has_value()) + builder.addMemberDecoration(id, *member, static_cast(decorateString.first), strings); + else + builder.addDecoration(id, static_cast(decorateString.first), strings); + } +} + +// TODO: this functionality should exist at a higher level, in creating the AST +// +// Identify interface members that don't have their required extension turned on. +// +bool TGlslangToSpvTraverser::filterMember(const glslang::TType& member) +{ + auto& extensions = glslangIntermediate->getRequestedExtensions(); + + if (member.getFieldName() == "gl_SecondaryViewportMaskNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + if (member.getFieldName() == "gl_SecondaryPositionNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + + if (glslangIntermediate->getStage() == EShLangMesh) { + if (member.getFieldName() == "gl_PrimitiveShadingRateEXT" && + extensions.find("GL_EXT_fragment_shading_rate") == extensions.end()) + return true; + } + + if (glslangIntermediate->getStage() != EShLangMesh) { + if (member.getFieldName() == "gl_ViewportMask" && + extensions.find("GL_NV_viewport_array2") == extensions.end()) + return true; + if (member.getFieldName() == "gl_PositionPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + if (member.getFieldName() == "gl_ViewportMaskPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + } + + return false; +} + +// Do full recursive conversion of a glslang structure (or block) type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangStructToSpvType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier) +{ + // Create a vector of struct types for SPIR-V to consume + std::vector spvMembers; + int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, + // except sometimes for blocks + std::vector > deferredForwardPointers; + for (int i = 0; i < (int)glslangMembers->size(); i++) { + auto& glslangMember = (*glslangMembers)[i]; + if (glslangMember.type->hiddenMember()) { + ++memberDelta; + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + } else { + if (type.getBasicType() == glslang::EbtBlock) { + if (filterMember(*glslangMember.type)) { + memberDelta++; + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + continue; + } + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = i - memberDelta; + } + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.type->getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // manually inherit location + if (! memberQualifier.hasLocation() && qualifier.hasLocation()) + memberQualifier.layoutLocation = qualifier.layoutLocation; + + // recurse + bool lastBufferBlockMember = qualifier.storage == glslang::EvqBuffer && + i == (int)glslangMembers->size() - 1; + + // Make forward pointers for any pointer members. + if (glslangMember.type->isReference() && + forwardPointers.find(glslangMember.type->getReferentType()) == forwardPointers.end()) { + deferredForwardPointers.push_back(std::make_pair(glslangMember.type, memberQualifier)); + } + + // Create the member type. + auto const spvMember = convertGlslangToSpvType(*glslangMember.type, explicitLayout, memberQualifier, lastBufferBlockMember, + glslangMember.type->isReference()); + spvMembers.push_back(spvMember); + + // Update the builder with the type's location so that we can create debug types for the structure members. + // There doesn't exist a "clean" entry point for this information to be passed along to the builder so, for now, + // it is stored in the builder and consumed during the construction of composite debug types. + // TODO: This probably warrants further investigation. This approach was decided to be the least ugly of the + // quick and dirty approaches that were tried. + // Advantages of this approach: + // + Relatively clean. No direct calls into debug type system. + // + Handles nested recursive structures. + // Disadvantages of this approach: + // + Not as clean as desired. Traverser queries/sets persistent state. This is fragile. + // + Table lookup during creation of composite debug types. This really shouldn't be necessary. + if(options.emitNonSemanticShaderDebugInfo) { + builder.debugTypeLocs[spvMember].name = glslangMember.type->getFieldName().c_str(); + builder.debugTypeLocs[spvMember].line = glslangMember.loc.line; + builder.debugTypeLocs[spvMember].column = glslangMember.loc.column; + } + } + } + + // Make the SPIR-V type + spv::Id spvType = builder.makeStructType(spvMembers, type.getTypeName().c_str(), false); + if (! HasNonLayoutQualifiers(type, qualifier)) + structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers] = spvType; + + // Decorate it + decorateStructType(type, glslangMembers, explicitLayout, qualifier, spvType, spvMembers); + + for (int i = 0; i < (int)deferredForwardPointers.size(); ++i) { + auto it = deferredForwardPointers[i]; + convertGlslangToSpvType(*it.first, explicitLayout, it.second, false); + } + + return spvType; +} + +void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier, + spv::Id spvType, + const std::vector& spvMembers) +{ + // Name and decorate the non-hidden members + int offset = -1; + bool memberLocationInvalid = type.isArrayOfArrays() || + (type.isArray() && (type.getQualifier().isArrayedIo(glslangIntermediate->getStage()) == false)); + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + int member = i; + if (type.getBasicType() == glslang::EbtBlock) { + member = memberRemapper[glslangTypeToIdMap[glslangMembers]][i]; + if (filterMember(glslangMember)) + continue; + } + + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // using -1 above to indicate a hidden member + if (member < 0) + continue; + + builder.addMemberName(spvType, member, glslangMember.getFieldName().c_str()); + builder.addMemberDecoration(spvType, member, + TranslateLayoutDecoration(glslangMember, memberQualifier.layoutMatrix)); + builder.addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangMember)); + // Add interpolation and auxiliary storage decorations only to + // top-level members of Input and Output storage classes + if (type.getQualifier().storage == glslang::EvqVaryingIn || + type.getQualifier().storage == glslang::EvqVaryingOut) { + if (type.getBasicType() == glslang::EbtBlock || + glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + builder.addMemberDecoration(spvType, member, TranslateInterpolationDecoration(memberQualifier)); + builder.addMemberDecoration(spvType, member, TranslateAuxiliaryStorageDecoration(memberQualifier)); + addMeshNVDecoration(spvType, member, memberQualifier); + } + } + builder.addMemberDecoration(spvType, member, TranslateInvariantDecoration(memberQualifier)); + + if (type.getBasicType() == glslang::EbtBlock && + qualifier.storage == glslang::EvqBuffer) { + // Add memory decorations only to top-level members of shader storage block + std::vector memory; + TranslateMemoryDecoration(memberQualifier, memory, glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addMemberDecoration(spvType, member, memory[i]); + } + + // Location assignment was already completed correctly by the front end, + // just track whether a member needs to be decorated. + // Ignore member locations if the container is an array, as that's + // ill-specified and decisions have been made to not allow this. + if (!memberLocationInvalid && memberQualifier.hasLocation()) + builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation); + + // component, XFB, others + if (glslangMember.getQualifier().hasComponent()) + builder.addMemberDecoration(spvType, member, spv::DecorationComponent, + glslangMember.getQualifier().layoutComponent); + if (glslangMember.getQualifier().hasXfbOffset()) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, + glslangMember.getQualifier().layoutXfbOffset); + else if (explicitLayout != glslang::ElpNone) { + // figure out what to do with offset, which is accumulating + int nextOffset; + updateMemberOffset(type, glslangMember, offset, nextOffset, explicitLayout, memberQualifier.layoutMatrix); + if (offset >= 0) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset); + offset = nextOffset; + } + + if (glslangMember.isMatrix() && explicitLayout != glslang::ElpNone) + builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, + getMatrixStride(glslangMember, explicitLayout, memberQualifier.layoutMatrix)); + + // built-in variable decorations + spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangMember.getQualifier().builtIn, true); + if (builtIn != spv::BuiltInMax) + builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn); + + // nonuniform + builder.addMemberDecoration(spvType, member, TranslateNonUniformDecoration(glslangMember.getQualifier())); + + if (glslangIntermediate->getHlslFunctionality1() && memberQualifier.semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + memberQualifier.semanticName); + } + + if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (glslangMember.getQualifier().layoutViewportRelative){ + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset != -2048){ + builder.addMemberDecoration(spvType, member, + (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + if (glslangMember.getQualifier().layoutPassthrough) { + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } + + // Add SPIR-V decorations (GL_EXT_spirv_intrinsics) + if (glslangMember.getQualifier().hasSpirvDecorate()) + applySpirvDecorate(glslangMember, spvType, member); + } + + // Decorate the structure + builder.addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix)); + const auto basicType = type.getBasicType(); + const auto typeStorageQualifier = type.getQualifier().storage; + if (basicType == glslang::EbtBlock) { + builder.addDecoration(spvType, TranslateBlockDecoration(typeStorageQualifier, glslangIntermediate->usingStorageBuffer())); + } else if (basicType == glslang::EbtStruct && glslangIntermediate->getSpv().vulkan > 0) { + const auto hasRuntimeArray = !spvMembers.empty() && builder.getOpCode(spvMembers.back()) == spv::OpTypeRuntimeArray; + if (hasRuntimeArray) { + builder.addDecoration(spvType, TranslateBlockDecoration(typeStorageQualifier, glslangIntermediate->usingStorageBuffer())); + } + } + + if (qualifier.hasHitObjectShaderRecordNV()) + builder.addDecoration(spvType, spv::DecorationHitObjectShaderRecordBufferNV); +} + +// Turn the expression forming the array size into an id. +// This is not quite trivial, because of specialization constants. +// Sometimes, a raw constant is turned into an Id, and sometimes +// a specialization constant expression is. +spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arraySizes, int dim, bool allowZero, bool boolType) +{ + // First, see if this is sized with a node, meaning a specialization constant: + glslang::TIntermTyped* specNode = arraySizes.getDimNode(dim); + if (specNode != nullptr) { + builder.clearAccessChain(); + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + specNode->traverse(this); + return accessChainLoad(specNode->getAsTyped()->getType()); + } + + // Otherwise, need a compile-time (front end) size, get it: + int size = arraySizes.getDimSize(dim); + + if (!allowZero) + assert(size > 0); + + if (boolType) { + return builder.makeBoolConstant(size); + } else { + return builder.makeUintConstant(size); + } +} + +// Wrap the builder's accessChainLoad to: +// - localize handling of RelaxedPrecision +// - use the SPIR-V inferred type instead of another conversion of the glslang type +// (avoids unnecessary work and possible type punning for structures) +// - do conversion of concrete to abstract type +spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type) +{ + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + spv::MemoryAccessMask accessMask = spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & ~spv::MemoryAccessMakePointerAvailableKHRMask); + // If the value being loaded is HelperInvocation, SPIR-V 1.6 is being generated (so that + // SPV_EXT_demote_to_helper_invocation is in core) and the memory model is in use, add + // the Volatile MemoryAccess semantic. + if (type.getQualifier().builtIn == glslang::EbvHelperInvocation && + glslangIntermediate->usingVulkanMemoryModel() && + glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { + accessMask = spv::MemoryAccessMask(accessMask | spv::MemoryAccessVolatileMask); + } + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type), + TranslateNonUniformDecoration(builder.getAccessChain().coherentFlags), + TranslateNonUniformDecoration(type.getQualifier()), + nominalTypeId, + accessMask, + TranslateMemoryScope(coherentFlags), + alignment); + + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + loadedId = convertLoadedBoolInUniformToUint(type, nominalTypeId, loadedId); + } + + return loadedId; +} + +// Wrap the builder's accessChainStore to: +// - do conversion of concrete to abstract type +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue) +{ + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = builder.makeUintConstant(1); + spv::Id zero = builder.makeUintConstant(0); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != boolType) + rvalue = builder.createBinOp(spv::OpINotEqual, boolType, rvalue, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = makeSmearedConstant(builder.makeUintConstant(1), vecSize); + spv::Id zero = makeSmearedConstant(builder.makeUintConstant(0), vecSize); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != bvecType) + rvalue = builder.createBinOp(spv::OpINotEqual, bvecType, rvalue, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + builder.accessChainStore(rvalue, TranslateNonUniformDecoration(builder.getAccessChain().coherentFlags), + spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & + ~spv::MemoryAccessMakePointerVisibleKHRMask), + TranslateMemoryScope(coherentFlags), alignment); +} + +// For storing when types match at the glslang level, but not might match at the +// SPIR-V level. +// +// This especially happens when a single glslang type expands to multiple +// SPIR-V types, like a struct that is used in a member-undecorated way as well +// as in a member-decorated way. +// +// NOTE: This function can handle any store request; if it's not special it +// simplifies to a simple OpStore. +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue) +{ + // we only do the complex path here if it's an aggregate + if (! type.isStruct() && ! type.isArray()) { + accessChainStore(type, rValue); + return; + } + + // and, it has to be a case of type aliasing + spv::Id rType = builder.getTypeId(rValue); + spv::Id lValue = builder.accessChainGetLValue(); + spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue)); + if (lType == rType) { + accessChainStore(type, rValue); + return; + } + + // Recursively (as needed) copy an aggregate type to a different aggregate type, + // where the two types were the same type in GLSL. This requires member + // by member copy, recursively. + + // SPIR-V 1.4 added an instruction to do help do this. + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + // However, bool in uniform space is changed to int, so + // OpCopyLogical does not work for that. + // TODO: It would be more robust to do a full recursive verification of the types satisfying SPIR-V rules. + bool rBool = builder.containsType(builder.getTypeId(rValue), spv::OpTypeBool, 0); + bool lBool = builder.containsType(lType, spv::OpTypeBool, 0); + if (lBool == rBool) { + spv::Id logicalCopy = builder.createUnaryOp(spv::OpCopyLogical, lType, rValue); + accessChainStore(type, logicalCopy); + return; + } + } + + // If an array, copy element by element. + if (type.isArray()) { + glslang::TType glslangElementType(type, 0); + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int index = 0; index < type.getOuterArraySize(); ++index) { + // get the source member + spv::Id elementRValue = builder.createCompositeExtract(rValue, elementRType, index); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(index), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangElementType, elementRValue); + } + } else { + assert(type.isStruct()); + + // loop over structure members + const glslang::TTypeList& members = *type.getStruct(); + for (int m = 0; m < (int)members.size(); ++m) { + const glslang::TType& glslangMemberType = *members[m].type; + + // get the source member + spv::Id memberRType = builder.getContainedTypeId(rType, m); + spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(m), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangMemberType, memberRValue); + } + } +} + +// Decide whether or not this type should be +// decorated with offsets and strides, and if so +// whether std140 or std430 rules should be applied. +glslang::TLayoutPacking TGlslangToSpvTraverser::getExplicitLayout(const glslang::TType& type) const +{ + // has to be a block + if (type.getBasicType() != glslang::EbtBlock) + return glslang::ElpNone; + + // has to be a uniform or buffer block or task in/out blocks + if (type.getQualifier().storage != glslang::EvqUniform && + type.getQualifier().storage != glslang::EvqBuffer && + type.getQualifier().storage != glslang::EvqShared && + !type.getQualifier().isTaskMemory()) + return glslang::ElpNone; + + // return the layout to use + switch (type.getQualifier().layoutPacking) { + case glslang::ElpStd140: + case glslang::ElpStd430: + case glslang::ElpScalar: + return type.getQualifier().layoutPacking; + default: + return glslang::ElpNone; + } +} + +// Given an array type, returns the integer stride required for that array +int TGlslangToSpvTraverser::getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + int size; + int stride; + glslangIntermediate->getMemberAlignment(arrayType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a matrix type, or array (of array) of matrixes type, returns the integer stride required for that matrix +// when used as a member of an interface block +int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + glslang::TType elementType; + elementType.shallowCopy(matrixType); + elementType.clearArraySizes(); + + int size; + int stride; + glslangIntermediate->getMemberAlignment(elementType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a member type of a struct, realign the current offset for it, and compute +// the next (not yet aligned) offset for the next member, which will get aligned +// on the next call. +// 'currentOffset' should be passed in already initialized, ready to modify, and reflecting +// the migration of data from nextOffset -> currentOffset. It should be -1 on the first call. +// -1 means a non-forced member offset (no decoration needed). +void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, + int& currentOffset, int& nextOffset, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout) +{ + // this will get a positive value when deemed necessary + nextOffset = -1; + + // override anything in currentOffset with user-set offset + if (memberType.getQualifier().hasOffset()) + currentOffset = memberType.getQualifier().layoutOffset; + + // It could be that current linker usage in glslang updated all the layoutOffset, + // in which case the following code does not matter. But, that's not quite right + // once cross-compilation unit GLSL validation is done, as the original user + // settings are needed in layoutOffset, and then the following will come into play. + + if (explicitLayout == glslang::ElpNone) { + if (! memberType.getQualifier().hasOffset()) + currentOffset = -1; + + return; + } + + // Getting this far means we need explicit offsets + if (currentOffset < 0) + currentOffset = 0; + + // Now, currentOffset is valid (either 0, or from a previous nextOffset), + // but possibly not yet correctly aligned. + + int memberSize; + int dummyStride; + int memberAlignment = glslangIntermediate->getMemberAlignment(memberType, memberSize, dummyStride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + bool isVectorLike = memberType.isVector(); + if (memberType.isMatrix()) { + if (matrixLayout == glslang::ElmRowMajor) + isVectorLike = memberType.getMatrixRows() == 1; + else + isVectorLike = memberType.getMatrixCols() == 1; + } + + // Adjust alignment for HLSL rules + // TODO: make this consistent in early phases of code: + // adjusting this late means inconsistencies with earlier code, which for reflection is an issue + // Until reflection is brought in sync with these adjustments, don't apply to $Global, + // which is the most likely to rely on reflection, and least likely to rely implicit layouts + if (glslangIntermediate->usingHlslOffsets() && + ! memberType.isStruct() && structType.getTypeName().compare("$Global") != 0) { + int componentSize; + int componentAlignment = glslangIntermediate->getBaseAlignmentScalar(memberType, componentSize); + if (! memberType.isArray() && isVectorLike && componentAlignment <= 4) + memberAlignment = componentAlignment; + + // Don't add unnecessary padding after this member + // (undo std140 bumping size to a mutliple of vec4) + if (explicitLayout == glslang::ElpStd140) { + if (memberType.isMatrix()) { + if (matrixLayout == glslang::ElmRowMajor) + memberSize -= componentSize * (4 - memberType.getMatrixCols()); + else + memberSize -= componentSize * (4 - memberType.getMatrixRows()); + } else if (memberType.isArray()) + memberSize -= componentSize * (4 - memberType.getVectorSize()); + } + } + + // Bump up to member alignment + glslang::RoundToPow2(currentOffset, memberAlignment); + + // Bump up to vec4 if there is a bad straddle + if (explicitLayout != glslang::ElpScalar && glslangIntermediate->improperStraddle(memberType, memberSize, + currentOffset, isVectorLike)) + glslang::RoundToPow2(currentOffset, 16); + + nextOffset = currentOffset + memberSize; +} + +void TGlslangToSpvTraverser::declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember) +{ + const glslang::TBuiltInVariable glslangBuiltIn = members[glslangMember].type->getQualifier().builtIn; + switch (glslangBuiltIn) + { + case glslang::EbvPointSize: + case glslang::EbvClipDistance: + case glslang::EbvCullDistance: + case glslang::EbvViewportMaskNV: + case glslang::EbvSecondaryPositionNV: + case glslang::EbvSecondaryViewportMaskNV: + case glslang::EbvPositionPerViewNV: + case glslang::EbvViewportMaskPerViewNV: + case glslang::EbvTaskCountNV: + case glslang::EbvPrimitiveCountNV: + case glslang::EbvPrimitiveIndicesNV: + case glslang::EbvClipDistancePerViewNV: + case glslang::EbvCullDistancePerViewNV: + case glslang::EbvLayerPerViewNV: + case glslang::EbvMeshViewCountNV: + case glslang::EbvMeshViewIndicesNV: + // Generate the associated capability. Delegate to TranslateBuiltInDecoration. + // Alternately, we could just call this for any glslang built-in, since the + // capability already guards against duplicates. + TranslateBuiltInDecoration(glslangBuiltIn, false); + break; + default: + // Capabilities were already generated when the struct was declared. + break; + } +} + +bool TGlslangToSpvTraverser::isShaderEntryPoint(const glslang::TIntermAggregate* node) +{ + return node->getName().compare(glslangIntermediate->getEntryPointMangledName().c_str()) == 0; +} + +// Does parameter need a place to keep writes, separate from the original? +// Assumes called after originalParam(), which filters out block/buffer/opaque-based +// qualifiers such that we should have only in/out/inout/constreadonly here. +bool TGlslangToSpvTraverser::writableParam(glslang::TStorageQualifier qualifier) const +{ + assert(qualifier == glslang::EvqIn || + qualifier == glslang::EvqOut || + qualifier == glslang::EvqInOut || + qualifier == glslang::EvqUniform || + qualifier == glslang::EvqConstReadOnly); + return qualifier != glslang::EvqConstReadOnly && + qualifier != glslang::EvqUniform; +} + +// Is parameter pass-by-original? +bool TGlslangToSpvTraverser::originalParam(glslang::TStorageQualifier qualifier, const glslang::TType& paramType, + bool implicitThisParam) +{ + if (implicitThisParam) // implicit this + return true; + if (glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return paramType.getBasicType() == glslang::EbtBlock; + return (paramType.containsOpaque() && !glslangIntermediate->getBindlessMode()) || // sampler, etc. + paramType.getQualifier().isSpirvByReference() || // spirv_by_reference + (paramType.getBasicType() == glslang::EbtBlock && qualifier == glslang::EvqBuffer); // SSBO +} + +// Make all the functions, skeletally, without actually visiting their bodies. +void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions) +{ + const auto getParamDecorations = [&](std::vector& decorations, const glslang::TType& type, + bool useVulkanMemoryModel) { + spv::Decoration paramPrecision = TranslatePrecisionDecoration(type); + if (paramPrecision != spv::NoPrecision) + decorations.push_back(paramPrecision); + TranslateMemoryDecoration(type.getQualifier(), decorations, useVulkanMemoryModel); + if (type.isReference()) { + // Original and non-writable params pass the pointer directly and + // use restrict/aliased, others are stored to a pointer in Function + // memory and use RestrictPointer/AliasedPointer. + if (originalParam(type.getQualifier().storage, type, false) || + !writableParam(type.getQualifier().storage)) { + // TranslateMemoryDecoration added Restrict decoration already. + if (!type.getQualifier().isRestrict()) { + decorations.push_back(spv::DecorationAliased); + } + } else { + decorations.push_back(type.getQualifier().isRestrict() ? spv::DecorationRestrictPointerEXT : + spv::DecorationAliasedPointerEXT); + } + } + }; + + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate(); + if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction) + continue; + + builder.setDebugSourceLocation(glslFunction->getLoc().line, glslFunction->getLoc().getFilename()); + + if (isShaderEntryPoint(glslFunction)) { + // For HLSL, the entry function is actually a compiler generated function to resolve the difference of + // entry function signature between HLSL and SPIR-V. So we don't emit debug information for that. + if (glslangIntermediate->getSource() != glslang::EShSourceHlsl) { + builder.setupFunctionDebugInfo(shaderEntry, glslangIntermediate->getEntryPointMangledName().c_str(), + std::vector(), // main function has no param + std::vector()); + } + continue; + } + // We're on a user function. Set up the basic interface for the function now, + // so that it's available to call. Translating the body will happen later. + // + // Typically (except for a "const in" parameter), an address will be passed to the + // function. What it is an address of varies: + // + // - "in" parameters not marked as "const" can be written to without modifying the calling + // argument so that write needs to be to a copy, hence the address of a copy works. + // + // - "const in" parameters can just be the r-value, as no writes need occur. + // + // - "out" and "inout" arguments can't be done as pointers to the calling argument, because + // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + + std::vector paramTypes; + std::vector paramNames; + std::vector> paramDecorations; // list of decorations per parameter + glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence(); + +#ifdef ENABLE_HLSL + bool implicitThis = (int)parameters.size() > 0 && parameters[0]->getAsSymbolNode()->getName() == + glslangIntermediate->implicitThisName; +#else + bool implicitThis = false; +#endif + + paramDecorations.resize(parameters.size()); + for (int p = 0; p < (int)parameters.size(); ++p) { + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + spv::Id typeId = convertGlslangToSpvType(paramType); + if (originalParam(paramType.getQualifier().storage, paramType, implicitThis && p == 0)) + typeId = builder.makePointer(TranslateStorageClass(paramType), typeId); + else if (writableParam(paramType.getQualifier().storage)) + typeId = builder.makePointer(spv::StorageClassFunction, typeId); + else + rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + getParamDecorations(paramDecorations[p], paramType, glslangIntermediate->usingVulkanMemoryModel()); + paramTypes.push_back(typeId); + } + + for (auto const parameter:parameters) { + paramNames.push_back(parameter->getAsSymbolNode()->getName().c_str()); + } + + spv::Block* functionBlock; + spv::Function* function = builder.makeFunctionEntry( + TranslatePrecisionDecoration(glslFunction->getType()), convertGlslangToSpvType(glslFunction->getType()), + glslFunction->getName().c_str(), convertGlslangLinkageToSpv(glslFunction->getLinkType()), paramTypes, + paramDecorations, &functionBlock); + builder.setupFunctionDebugInfo(function, glslFunction->getName().c_str(), paramTypes, paramNames); + if (implicitThis) + function->setImplicitThis(); + + // Track function to emit/call later + functionMap[glslFunction->getName().c_str()] = function; + + // Set the parameter id's + for (int p = 0; p < (int)parameters.size(); ++p) { + symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p); + // give a name too + builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str()); + + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + if (paramType.contains8BitInt()) + builder.addCapability(spv::CapabilityInt8); + if (paramType.contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + if (paramType.contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + } + } +} + +// Process all the initializers, while skipping the functions and link objects +void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers) +{ + builder.setBuildPoint(shaderEntry->getLastBlock()); + for (int i = 0; i < (int)initializers.size(); ++i) { + glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate(); + if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != + glslang::EOpLinkerObjects) { + + // We're on a top-level node that's not a function. Treat as an initializer, whose + // code goes into the beginning of the entry point. + initializer->traverse(this); + } + } +} +// Walk over all linker objects to create a map for payload and callable data linker objects +// and their location to be used during codegen for OpTraceKHR and OpExecuteCallableKHR +// This is done here since it is possible that these linker objects are not be referenced in the AST +void TGlslangToSpvTraverser::collectRayTracingLinkerObjects() +{ + glslang::TIntermAggregate* linkerObjects = glslangIntermediate->findLinkerObjects(); + for (auto& objSeq : linkerObjects->getSequence()) { + auto objNode = objSeq->getAsSymbolNode(); + if (objNode != nullptr) { + if (objNode->getQualifier().hasLocation()) { + unsigned int location = objNode->getQualifier().layoutLocation; + auto st = objNode->getQualifier().storage; + int set; + switch (st) + { + case glslang::EvqPayload: + case glslang::EvqPayloadIn: + set = 0; + break; + case glslang::EvqCallableData: + case glslang::EvqCallableDataIn: + set = 1; + break; + + case glslang::EvqHitObjectAttrNV: + set = 2; + break; + + default: + set = -1; + } + if (set != -1) + locationToSymbol[set].insert(std::make_pair(location, objNode)); + } + } + } +} +// Process all the functions, while skipping initializers. +void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions) +{ + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang::EOpLinkerObjects)) + node->traverse(this); + } +} + +void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node) +{ + // SPIR-V functions should already be in the functionMap from the prepass + // that called makeFunctions(). + currentFunction = functionMap[node->getName().c_str()]; + spv::Block* functionBlock = currentFunction->getEntryBlock(); + builder.setBuildPoint(functionBlock); + builder.enterFunction(currentFunction); +} + +void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + const glslang::TIntermSequence& glslangArguments = node.getSequence(); + + glslang::TSampler sampler = {}; + bool cubeCompare = false; + bool f16ShadowCompare = false; + if (node.isTexture() || node.isImage()) { + sampler = glslangArguments[0]->getAsTyped()->getType().getSampler(); + cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; + f16ShadowCompare = sampler.shadow && + glslangArguments[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16; + } + + for (int i = 0; i < (int)glslangArguments.size(); ++i) { + builder.clearAccessChain(); + glslangArguments[i]->traverse(this); + + // Special case l-value operands + bool lvalue = false; + switch (node.getOp()) { + case glslang::EOpImageAtomicAdd: + case glslang::EOpImageAtomicMin: + case glslang::EOpImageAtomicMax: + case glslang::EOpImageAtomicAnd: + case glslang::EOpImageAtomicOr: + case glslang::EOpImageAtomicXor: + case glslang::EOpImageAtomicExchange: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpImageAtomicLoad: + case glslang::EOpImageAtomicStore: + if (i == 0) + lvalue = true; + break; + case glslang::EOpSparseImageLoad: + if ((sampler.ms && i == 3) || (! sampler.ms && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTexture: + if (((cubeCompare || f16ShadowCompare) && i == 3) || (! (cubeCompare || f16ShadowCompare) && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureClamp: + if (((cubeCompare || f16ShadowCompare) && i == 4) || (! (cubeCompare || f16ShadowCompare) && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLod: + case glslang::EOpSparseTextureOffset: + if ((f16ShadowCompare && i == 4) || (! f16ShadowCompare && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetch: + if ((sampler.dim != glslang::EsdRect && i == 3) || (sampler.dim == glslang::EsdRect && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetchOffset: + if ((sampler.dim != glslang::EsdRect && i == 4) || (sampler.dim == glslang::EsdRect && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLodOffset: + case glslang::EOpSparseTextureGrad: + case glslang::EOpSparseTextureOffsetClamp: + if ((f16ShadowCompare && i == 5) || (! f16ShadowCompare && i == 4)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffset: + case glslang::EOpSparseTextureGradClamp: + if ((f16ShadowCompare && i == 6) || (! f16ShadowCompare && i == 5)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffsetClamp: + if ((f16ShadowCompare && i == 7) || (! f16ShadowCompare && i == 6)) + lvalue = true; + break; + case glslang::EOpSparseTextureGather: + if ((sampler.shadow && i == 3) || (! sampler.shadow && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherOffset: + case glslang::EOpSparseTextureGatherOffsets: + if ((sampler.shadow && i == 4) || (! sampler.shadow && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLodOffset: + case glslang::EOpSparseTextureGatherLodOffsets: + if (i == 4) + lvalue = true; + break; + case glslang::EOpSparseImageLoadLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintNV: + if (i == 4) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintClampNV: + case glslang::EOpImageSampleFootprintLodNV: + if (i == 5) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradNV: + if (i == 6) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradClampNV: + if (i == 7) + lvalue = true; + break; + case glslang::EOpRayQueryGetIntersectionTriangleVertexPositionsEXT: + case glslang::EOpRayQueryGetIntersectionLSSPositionsNV: + case glslang::EOpRayQueryGetIntersectionLSSRadiiNV: + if (i == 2) + lvalue = true; + break; + default: + break; + } + + if (lvalue) { + spv::Id lvalue_id = builder.accessChainGetLValue(); + arguments.push_back(lvalue_id); + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + builder.addDecoration(lvalue_id, TranslateNonUniformDecoration(lvalueCoherentFlags)); + lvalueCoherentFlags |= TranslateCoherent(glslangArguments[i]->getAsTyped()->getType()); + } else { + if (i > 0 && + glslangArguments[i]->getAsSymbolNode() && glslangArguments[i-1]->getAsSymbolNode() && + glslangArguments[i]->getAsSymbolNode()->getId() == glslangArguments[i-1]->getAsSymbolNode()->getId()) { + // Reuse the id if possible + arguments.push_back(arguments[i-1]); + } else { + arguments.push_back(accessChainLoad(glslangArguments[i]->getAsTyped()->getType())); + } + } + } +} + +void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector& arguments) +{ + builder.clearAccessChain(); + node.getOperand()->traverse(this); + arguments.push_back(accessChainLoad(node.getOperand()->getType())); +} + +spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node) +{ + if (! node->isImage() && ! node->isTexture()) + return spv::NoResult; + + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + // Process a GLSL texturing op (will be SPV image) + + const glslang::TType &imageType = node->getAsAggregate() + ? node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType() + : node->getAsUnaryNode()->getOperand()->getAsTyped()->getType(); + const glslang::TSampler sampler = imageType.getSampler(); + bool f16ShadowCompare = (sampler.shadow && node->getAsAggregate()) + ? node->getAsAggregate()->getSequence()[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16 + : false; + + const auto signExtensionMask = [&]() { + if (builder.getSpvVersion() >= spv::Spv_1_4) { + if (sampler.type == glslang::EbtUint) + return spv::ImageOperandsZeroExtendMask; + else if (sampler.type == glslang::EbtInt) + return spv::ImageOperandsSignExtendMask; + } + return spv::ImageOperandsMaskNone; + }; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + std::vector arguments; + if (node->getAsAggregate()) + translateArguments(*node->getAsAggregate(), arguments, lvalueCoherentFlags); + else + translateArguments(*node->getAsUnaryNode(), arguments); + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + spv::Builder::TextureParameters params = { }; + params.sampler = arguments[0]; + + glslang::TCrackedTextureOp cracked; + node->crackTexture(sampler, cracked); + + const bool isUnsignedResult = node->getType().getBasicType() == glslang::EbtUint; + + if (builder.isSampledImage(params.sampler) && + ((cracked.query && node->getOp() != glslang::EOpTextureQueryLod) || cracked.fragMask || cracked.fetch)) { + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + if (imageType.getQualifier().isNonUniform()) { + builder.addDecoration(params.sampler, spv::DecorationNonUniformEXT); + } + } + // Check for queries + if (cracked.query) { + switch (node->getOp()) { + case glslang::EOpImageQuerySize: + case glslang::EOpTextureQuerySize: + if (arguments.size() > 1) { + params.lod = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params, isUnsignedResult); + } else + return builder.createTextureQueryCall(spv::OpImageQuerySize, params, isUnsignedResult); + case glslang::EOpImageQuerySamples: + case glslang::EOpTextureQuerySamples: + return builder.createTextureQueryCall(spv::OpImageQuerySamples, params, isUnsignedResult); + case glslang::EOpTextureQueryLod: + params.coords = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQueryLod, params, isUnsignedResult); + case glslang::EOpTextureQueryLevels: + return builder.createTextureQueryCall(spv::OpImageQueryLevels, params, isUnsignedResult); + case glslang::EOpSparseTexelsResident: + return builder.createUnaryOp(spv::OpImageSparseTexelsResident, builder.makeBoolType(), arguments[0]); + default: + assert(0); + break; + } + } + + int components = node->getType().getVectorSize(); + + if (node->getOp() == glslang::EOpImageLoad || + node->getOp() == glslang::EOpImageLoadLod || + node->getOp() == glslang::EOpTextureFetch || + node->getOp() == glslang::EOpTextureFetchOffset) { + // These must produce 4 components, per SPIR-V spec. We'll add a conversion constructor if needed. + // This will only happen through the HLSL path for operator[], so we do not have to handle e.g. + // the EOpTexture/Proj/Lod/etc family. It would be harmless to do so, but would need more logic + // here around e.g. which ones return scalars or other types. + components = 4; + } + + glslang::TType returnType(node->getType().getBasicType(), glslang::EvqTemporary, components); + + auto resultType = [&returnType,this]{ return convertGlslangToSpvType(returnType); }; + + // Check for image functions other than queries + if (node->isImage()) { + std::vector operands; + auto opIt = arguments.begin(); + spv::IdImmediate image = { true, *(opIt++) }; + operands.push_back(image); + + // Handle subpass operations + // TODO: GLSL should change to have the "MS" only on the type rather than the + // built-in function. + if (cracked.subpass) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + spv::IdImmediate coord = { true, + builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps) }; + operands.push_back(coord); + spv::IdImmediate imageOperands = { false, spv::ImageOperandsMaskNone }; + imageOperands.word = imageOperands.word | signExtensionMask(); + if (sampler.isMultiSample()) { + imageOperands.word = imageOperands.word | spv::ImageOperandsSampleMask; + } + if (imageOperands.word != spv::ImageOperandsMaskNone) { + operands.push_back(imageOperands); + if (sampler.isMultiSample()) { + spv::IdImmediate imageOperand = { true, *(opIt++) }; + operands.push_back(imageOperand); + } + } + spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands); + builder.setPrecision(result, precision); + return result; + } + + if (cracked.attachmentEXT) { + if (opIt != arguments.end()) { + spv::IdImmediate sample = { true, *opIt }; + operands.push_back(sample); + } + spv::Id result = builder.createOp(spv::OpColorAttachmentReadEXT, resultType(), operands); + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + builder.setPrecision(result, precision); + return result; + } + + spv::IdImmediate coord = { true, *(opIt++) }; + operands.push_back(coord); + if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) { + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + std::vector result(1, builder.createOp(spv::OpImageRead, resultType(), operands)); + builder.setPrecision(result[0], precision); + + // If needed, add a conversion constructor to the proper size. + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; + } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) { + + // Push the texel value before the operands + if (sampler.isMultiSample() || cracked.lod) { + spv::IdImmediate texel = { true, *(opIt + 1) }; + operands.push_back(texel); + } else { + spv::IdImmediate texel = { true, *opIt }; + operands.push_back(texel); + } + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelVisibleKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelAvailableKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + builder.createNoResultOp(spv::OpImageWrite, operands); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat); + return spv::NoResult; + } else if (node->getOp() == glslang::EOpSparseImageLoad || + node->getOp() == glslang::EOpSparseImageLoadLod) { + builder.addCapability(spv::CapabilitySparseResidency); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope( + TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + // Create the return type that was a special structure + spv::Id texelOut = *opIt; + spv::Id typeId0 = resultType(); + spv::Id typeId1 = builder.getDerefTypeId(texelOut); + spv::Id resultTypeId = builder.makeStructResultType(typeId0, typeId1); + + spv::Id resultId = builder.createOp(spv::OpImageSparseRead, resultTypeId, operands); + + // Decode the return type + builder.createStore(builder.createCompositeExtract(resultId, typeId1, 1), texelOut); + return builder.createCompositeExtract(resultId, typeId0, 0); + } else { + // Process image atomic operations + + // GLSL "IMAGE_PARAMS" will involve in constructing an image texel pointer and this pointer, + // as the first source operand, is required by SPIR-V atomic operations. + // For non-MS, the sample value should be 0 + spv::IdImmediate sample = { true, sampler.isMultiSample() ? *(opIt++) : builder.makeUintConstant(0) }; + operands.push_back(sample); + + spv::Id resultTypeId; + glslang::TBasicType typeProxy = node->getBasicType(); + // imageAtomicStore has a void return type so base the pointer type on + // the type of the value operand. + if (node->getOp() == glslang::EOpImageAtomicStore) { + resultTypeId = builder.makePointer(spv::StorageClassImage, builder.getTypeId(*opIt)); + typeProxy = node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType().getSampler().type; + } else { + resultTypeId = builder.makePointer(spv::StorageClassImage, resultType()); + } + spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, operands); + if (imageType.getQualifier().nonUniform) { + builder.addDecoration(pointer, spv::DecorationNonUniformEXT); + } + + std::vector operands; + operands.push_back(pointer); + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + return createAtomicOperation(node->getOp(), precision, resultType(), operands, typeProxy, + lvalueCoherentFlags, node->getType()); + } + } + + // Check for fragment mask functions other than queries + if (cracked.fragMask) { + assert(sampler.ms); + + auto opIt = arguments.begin(); + std::vector operands; + + operands.push_back(params.sampler); + ++opIt; + + if (sampler.isSubpass()) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + operands.push_back(builder.makeCompositeConstant( + builder.makeVectorType(builder.makeIntType(32), 2), comps)); + } + + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + spv::Op fragMaskOp = spv::OpNop; + if (node->getOp() == glslang::EOpFragmentMaskFetch) + fragMaskOp = spv::OpFragmentMaskFetchAMD; + else if (node->getOp() == glslang::EOpFragmentFetch) + fragMaskOp = spv::OpFragmentFetchAMD; + + builder.addExtension(spv::E_SPV_AMD_shader_fragment_mask); + builder.addCapability(spv::CapabilityFragmentMaskAMD); + return builder.createOp(fragMaskOp, resultType(), operands); + } + + // Check for texture functions other than queries + bool sparse = node->isSparseTexture(); + bool imageFootprint = node->isImageFootprint(); + bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.isArrayed() && sampler.isShadow(); + + // check for bias argument + bool bias = false; + if (! cracked.lod && ! cracked.grad && ! cracked.fetch && ! cubeCompare) { + int nonBiasArgCount = 2; + if (cracked.gather) + ++nonBiasArgCount; // comp argument should be present when bias argument is present + + if (f16ShadowCompare) + ++nonBiasArgCount; + if (cracked.offset) + ++nonBiasArgCount; + else if (cracked.offsets) + ++nonBiasArgCount; + if (cracked.grad) + nonBiasArgCount += 2; + if (cracked.lodClamp) + ++nonBiasArgCount; + if (sparse) + ++nonBiasArgCount; + if (imageFootprint) + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + nonBiasArgCount += 3; + if ((int)arguments.size() > nonBiasArgCount) + bias = true; + } + + if (cracked.gather) { + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + if (bias || cracked.lod || + sourceExtensions.find(glslang::E_GL_AMD_texture_gather_bias_lod) != sourceExtensions.end()) { + builder.addExtension(spv::E_SPV_AMD_texture_gather_bias_lod); + builder.addCapability(spv::CapabilityImageGatherBiasLodAMD); + } + } + + // set the rest of the arguments + + params.coords = arguments[1]; + int extraArgs = 0; + bool noImplicitLod = false; + + // sort out where Dref is coming from + if (cubeCompare || f16ShadowCompare) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow && cracked.gather) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow) { + std::vector indexes; + int dRefComp; + if (cracked.proj) + dRefComp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref" + else + dRefComp = builder.getNumComponents(params.coords) - 1; + indexes.push_back(dRefComp); + params.Dref = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), indexes); + } + + // lod + if (cracked.lod) { + params.lod = arguments[2 + extraArgs]; + ++extraArgs; + } else if (glslangIntermediate->getStage() != EShLangFragment && + !(glslangIntermediate->getStage() == EShLangCompute && + glslangIntermediate->hasLayoutDerivativeModeNone())) { + // we need to invent the default lod for an explicit lod instruction for a non-fragment stage + noImplicitLod = true; + } + + // multisample + if (sampler.isMultiSample()) { + params.sample = arguments[2 + extraArgs]; // For MS, "sample" should be specified + ++extraArgs; + } + + // gradient + if (cracked.grad) { + params.gradX = arguments[2 + extraArgs]; + params.gradY = arguments[3 + extraArgs]; + extraArgs += 2; + } + + // offset and offsets + if (cracked.offset) { + params.offset = arguments[2 + extraArgs]; + ++extraArgs; + } else if (cracked.offsets) { + params.offsets = arguments[2 + extraArgs]; + ++extraArgs; + } + + // lod clamp + if (cracked.lodClamp) { + params.lodClamp = arguments[2 + extraArgs]; + ++extraArgs; + } + // sparse + if (sparse) { + params.texelOut = arguments[2 + extraArgs]; + ++extraArgs; + } + // gather component + if (cracked.gather && ! sampler.shadow) { + // default component is 0, if missing, otherwise an argument + if (2 + extraArgs < (int)arguments.size()) { + params.component = arguments[2 + extraArgs]; + ++extraArgs; + } else + params.component = builder.makeIntConstant(0); + } + spv::Id resultStruct = spv::NoResult; + if (imageFootprint) { + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + params.granularity = arguments[2 + extraArgs]; + params.coarse = arguments[3 + extraArgs]; + resultStruct = arguments[4 + extraArgs]; + extraArgs += 3; + } + + // bias + if (bias) { + params.bias = arguments[2 + extraArgs]; + ++extraArgs; + } + + if (imageFootprint) { + builder.addExtension(spv::E_SPV_NV_shader_image_footprint); + builder.addCapability(spv::CapabilityImageFootprintNV); + + + //resultStructType(OpenGL type) contains 5 elements: + //struct gl_TextureFootprint2DNV { + // uvec2 anchor; + // uvec2 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + //or + //struct gl_TextureFootprint3DNV { + // uvec3 anchor; + // uvec3 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + spv::Id resultStructType = builder.getContainedTypeId(builder.getTypeId(resultStruct)); + assert(builder.isStructType(resultStructType)); + + //resType (SPIR-V type) contains 6 elements: + //Member 0 must be a Boolean type scalar(LOD), + //Member 1 must be a vector of integer type, whose Signedness operand is 0(anchor), + //Member 2 must be a vector of integer type, whose Signedness operand is 0(offset), + //Member 3 must be a vector of integer type, whose Signedness operand is 0(mask), + //Member 4 must be a scalar of integer type, whose Signedness operand is 0(lod), + //Member 5 must be a scalar of integer type, whose Signedness operand is 0(granularity). + std::vector members; + members.push_back(resultType()); + for (int i = 0; i < 5; i++) { + members.push_back(builder.getContainedTypeId(resultStructType, i)); + } + spv::Id resType = builder.makeStructType(members, "ResType"); + + //call ImageFootprintNV + spv::Id res = builder.createTextureCall(precision, resType, sparse, cracked.fetch, cracked.proj, + cracked.gather, noImplicitLod, params, signExtensionMask()); + + //copy resType (SPIR-V type) to resultStructType(OpenGL type) + for (int i = 0; i < 5; i++) { + builder.clearAccessChain(); + builder.setAccessChainLValue(resultStruct); + + //Accessing to a struct we created, no coherent flag is set + spv::Builder::AccessChain::CoherentFlags flags; + flags.clear(); + + builder.accessChainPush(builder.makeIntConstant(i), flags, 0); + builder.accessChainStore(builder.createCompositeExtract(res, builder.getContainedTypeId(resType, i+1), + i+1), TranslateNonUniformDecoration(imageType.getQualifier())); + } + return builder.createCompositeExtract(res, resultType(), 0); + } + + // projective component (might not to move) + // GLSL: "The texture coordinates consumed from P, not including the last component of P, + // are divided by the last component of P." + // SPIR-V: "... (u [, v] [, w], q)... It may be a vector larger than needed, but all + // unused components will appear after all used components." + if (cracked.proj) { + int projSourceComp = builder.getNumComponents(params.coords) - 1; + int projTargetComp; + switch (sampler.dim) { + case glslang::Esd1D: projTargetComp = 1; break; + case glslang::Esd2D: projTargetComp = 2; break; + case glslang::EsdRect: projTargetComp = 2; break; + default: projTargetComp = projSourceComp; break; + } + // copy the projective coordinate if we have to + if (projTargetComp != projSourceComp) { + spv::Id projComp = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), projSourceComp); + params.coords = builder.createCompositeInsert(projComp, params.coords, + builder.getTypeId(params.coords), projTargetComp); + } + } + + // nonprivate + if (imageType.getQualifier().nonprivate) { + params.nonprivate = true; + } + + // volatile + if (imageType.getQualifier().volatil) { + params.volatil = true; + } + + if (imageType.getQualifier().nontemporal) { + params.nontemporal = true; + } + + std::vector result( 1, + builder.createTextureCall(precision, resultType(), sparse, cracked.fetch, cracked.proj, cracked.gather, + noImplicitLod, params, signExtensionMask()) + ); + + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; +} + +spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node) +{ + // Grab the function's pointer from the previously created function + spv::Function* function = functionMap[node->getName().c_str()]; + if (! function) + return 0; + + const glslang::TIntermSequence& glslangArgs = node->getSequence(); + const glslang::TQualifierList& qualifiers = node->getQualifierList(); + + // See comments in makeFunctions() for details about the semantics for parameter passing. + // + // These imply we need a four step process: + // 1. Evaluate the arguments + // 2. Allocate and make copies of in, out, and inout arguments + // 3. Make the call + // 4. Copy back the results + + // 1. Evaluate the arguments and their types + std::vector lValues; + std::vector rValues; + std::vector argTypes; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + argTypes.push_back(&glslangArgs[a]->getAsTyped()->getType()); + // build l-value + builder.clearAccessChain(); + glslangArgs[a]->traverse(this); + // keep outputs and pass-by-originals as l-values, evaluate others as r-values + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0) || + writableParam(qualifiers[a])) { + // save l-value + lValues.push_back(builder.getAccessChain()); + } else { + // process r-value + rValues.push_back(accessChainLoad(*argTypes.back())); + } + } + + // Reset source location to the function call location after argument evaluation + builder.setDebugSourceLocation(node->getLoc().line, node->getLoc().getFilename()); + + // 2. Allocate space for anything needing a copy, and if it's "in" or "inout" + // copy the original into that space. + // + // Also, build up the list of actual arguments to pass in for the call + int lValueCount = 0; + int rValueCount = 0; + std::vector spvArgs; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + spv::Id arg; + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) { + builder.setAccessChain(lValues[lValueCount]); + arg = builder.accessChainGetLValue(); + ++lValueCount; + } else if (writableParam(qualifiers[a])) { + // need space to hold the copy + arg = builder.createVariable(function->getParamPrecision(a), spv::StorageClassFunction, + builder.getContainedTypeId(function->getParamType(a)), "param"); + if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) { + // need to copy the input into output space + builder.setAccessChain(lValues[lValueCount]); + spv::Id copy = accessChainLoad(*argTypes[a]); + builder.clearAccessChain(); + builder.setAccessChainLValue(arg); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } else { + // process r-value, which involves a copy for a type mismatch + if (function->getParamType(a) != builder.getTypeId(rValues[rValueCount]) || + TranslatePrecisionDecoration(*argTypes[a]) != function->getParamPrecision(a)) + { + spv::Id argCopy = builder.createVariable(function->getParamPrecision(a), spv::StorageClassFunction, function->getParamType(a), "arg"); + builder.clearAccessChain(); + builder.setAccessChainLValue(argCopy); + multiTypeStore(*argTypes[a], rValues[rValueCount]); + arg = builder.createLoad(argCopy, function->getParamPrecision(a)); + } else + arg = rValues[rValueCount]; + ++rValueCount; + } + spvArgs.push_back(arg); + } + + // 3. Make the call. + spv::Id result = builder.createFunctionCall(function, spvArgs); + builder.setPrecision(result, TranslatePrecisionDecoration(node->getType())); + builder.addDecoration(result, TranslateNonUniformDecoration(node->getType().getQualifier())); + + // 4. Copy back out an "out" arguments. + lValueCount = 0; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) + ++lValueCount; + else if (writableParam(qualifiers[a])) { + if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { + spv::Id copy = builder.createLoad(spvArgs[a], spv::NoPrecision); + builder.addDecoration(copy, TranslateNonUniformDecoration(argTypes[a]->getQualifier())); + builder.setAccessChain(lValues[lValueCount]); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } + } + + return result; +} + +// Translate AST operation to SPV operation, already having SPV-based operands/types. +spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, OpDecorations& decorations, + spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + bool isBool = typeProxy == glslang::EbtBool; + + spv::Op binOp = spv::OpNop; + bool needMatchingVectors = true; // for non-matrix ops, would a scalar need to smear to match a vector? + bool comparison = false; + + switch (op) { + case glslang::EOpAdd: + case glslang::EOpAddAssign: + if (isFloat) + binOp = spv::OpFAdd; + else + binOp = spv::OpIAdd; + break; + case glslang::EOpSub: + case glslang::EOpSubAssign: + if (isFloat) + binOp = spv::OpFSub; + else + binOp = spv::OpISub; + break; + case glslang::EOpMul: + case glslang::EOpMulAssign: + if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesScalarAssign: + if (isFloat && (builder.isVector(left) || builder.isVector(right) || builder.isCooperativeVector(left) || builder.isCooperativeVector(right))) { + if (builder.isVector(right) || builder.isCooperativeVector(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + needMatchingVectors = false; + binOp = spv::OpVectorTimesScalar; + } else if (isFloat) { + binOp = spv::OpFMul; + } else if (builder.isCooperativeVector(left) || builder.isCooperativeVector(right)) { + if (builder.isCooperativeVector(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + // Construct a cooperative vector from the scalar + right = builder.createCompositeConstruct(builder.getTypeId(left), { right }); + binOp = spv::OpIMul; + } else { + binOp = spv::OpIMul; + } + break; + case glslang::EOpVectorTimesMatrix: + case glslang::EOpVectorTimesMatrixAssign: + binOp = spv::OpVectorTimesMatrix; + break; + case glslang::EOpMatrixTimesVector: + binOp = spv::OpMatrixTimesVector; + break; + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesScalarAssign: + binOp = spv::OpMatrixTimesScalar; + break; + case glslang::EOpMatrixTimesMatrix: + case glslang::EOpMatrixTimesMatrixAssign: + binOp = spv::OpMatrixTimesMatrix; + break; + case glslang::EOpOuterProduct: + binOp = spv::OpOuterProduct; + needMatchingVectors = false; + break; + + case glslang::EOpDiv: + case glslang::EOpDivAssign: + if (isFloat) + binOp = spv::OpFDiv; + else if (isUnsigned) + binOp = spv::OpUDiv; + else + binOp = spv::OpSDiv; + break; + case glslang::EOpMod: + case glslang::EOpModAssign: + if (isFloat) + binOp = spv::OpFMod; + else if (isUnsigned) + binOp = spv::OpUMod; + else + binOp = spv::OpSMod; + break; + case glslang::EOpRightShift: + case glslang::EOpRightShiftAssign: + if (isUnsigned) + binOp = spv::OpShiftRightLogical; + else + binOp = spv::OpShiftRightArithmetic; + break; + case glslang::EOpLeftShift: + case glslang::EOpLeftShiftAssign: + binOp = spv::OpShiftLeftLogical; + break; + case glslang::EOpAnd: + case glslang::EOpAndAssign: + binOp = spv::OpBitwiseAnd; + break; + case glslang::EOpLogicalAnd: + needMatchingVectors = false; + binOp = spv::OpLogicalAnd; + break; + case glslang::EOpInclusiveOr: + case glslang::EOpInclusiveOrAssign: + binOp = spv::OpBitwiseOr; + break; + case glslang::EOpLogicalOr: + needMatchingVectors = false; + binOp = spv::OpLogicalOr; + break; + case glslang::EOpExclusiveOr: + case glslang::EOpExclusiveOrAssign: + binOp = spv::OpBitwiseXor; + break; + case glslang::EOpLogicalXor: + needMatchingVectors = false; + binOp = spv::OpLogicalNotEqual; + break; + + case glslang::EOpAbsDifference: + binOp = isUnsigned ? spv::OpAbsUSubINTEL : spv::OpAbsISubINTEL; + break; + + case glslang::EOpAddSaturate: + binOp = isUnsigned ? spv::OpUAddSatINTEL : spv::OpIAddSatINTEL; + break; + + case glslang::EOpSubSaturate: + binOp = isUnsigned ? spv::OpUSubSatINTEL : spv::OpISubSatINTEL; + break; + + case glslang::EOpAverage: + binOp = isUnsigned ? spv::OpUAverageINTEL : spv::OpIAverageINTEL; + break; + + case glslang::EOpAverageRounded: + binOp = isUnsigned ? spv::OpUAverageRoundedINTEL : spv::OpIAverageRoundedINTEL; + break; + + case glslang::EOpMul32x16: + binOp = isUnsigned ? spv::OpUMul32x16INTEL : spv::OpIMul32x16INTEL; + break; + + case glslang::EOpExpectEXT: + binOp = spv::OpExpectKHR; + break; + + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + comparison = true; + break; + default: + break; + } + + // handle mapped binary operations (should be non-comparison) + if (binOp != spv::OpNop) { + assert(comparison == false); + if (builder.isMatrix(left) || builder.isMatrix(right) || + builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + return createBinaryMatrixOperation(binOp, decorations, typeId, left, right); + + // No matrix involved; make both operands be the same number of components, if needed + if (needMatchingVectors) + builder.promoteScalar(decorations.precision, left, right); + + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + if (! comparison) + return 0; + + // Handle comparison instructions + + if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual) + && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) { + spv::Id result = builder.createCompositeCompare(decorations.precision, left, right, op == glslang::EOpEqual); + decorations.addNonUniform(builder, result); + return result; + } + + switch (op) { + case glslang::EOpLessThan: + if (isFloat) + binOp = spv::OpFOrdLessThan; + else if (isUnsigned) + binOp = spv::OpULessThan; + else + binOp = spv::OpSLessThan; + break; + case glslang::EOpGreaterThan: + if (isFloat) + binOp = spv::OpFOrdGreaterThan; + else if (isUnsigned) + binOp = spv::OpUGreaterThan; + else + binOp = spv::OpSGreaterThan; + break; + case glslang::EOpLessThanEqual: + if (isFloat) + binOp = spv::OpFOrdLessThanEqual; + else if (isUnsigned) + binOp = spv::OpULessThanEqual; + else + binOp = spv::OpSLessThanEqual; + break; + case glslang::EOpGreaterThanEqual: + if (isFloat) + binOp = spv::OpFOrdGreaterThanEqual; + else if (isUnsigned) + binOp = spv::OpUGreaterThanEqual; + else + binOp = spv::OpSGreaterThanEqual; + break; + case glslang::EOpEqual: + case glslang::EOpVectorEqual: + if (isFloat) + binOp = spv::OpFOrdEqual; + else if (isBool) + binOp = spv::OpLogicalEqual; + else + binOp = spv::OpIEqual; + break; + case glslang::EOpNotEqual: + case glslang::EOpVectorNotEqual: + if (isFloat) + binOp = spv::OpFUnordNotEqual; + else if (isBool) + binOp = spv::OpLogicalNotEqual; + else + binOp = spv::OpINotEqual; + break; + default: + break; + } + + if (binOp != spv::OpNop) { + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + return 0; +} + +// +// Translate AST matrix operation to SPV operation, already having SPV-based operands/types. +// These can be any of: +// +// matrix * scalar +// scalar * matrix +// matrix * matrix linear algebraic +// matrix * vector +// vector * matrix +// matrix * matrix componentwise +// matrix op matrix op in {+, -, /} +// matrix op scalar op in {+, -, /} +// scalar op matrix op in {+, -, /} +// +spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id left, spv::Id right) +{ + bool firstClass = true; + + // First, handle first-class matrix operations (* and matrix/scalar) + switch (op) { + case spv::OpFDiv: + if (builder.isMatrix(left) && builder.isScalar(right)) { + // turn matrix / scalar into a multiply... + spv::Id resultType = builder.getTypeId(right); + right = builder.createBinOp(spv::OpFDiv, resultType, builder.makeFpConstant(resultType, 1.0), right); + op = spv::OpMatrixTimesScalar; + } else + firstClass = false; + break; + case spv::OpMatrixTimesScalar: + if (builder.isMatrix(right) || builder.isCooperativeMatrix(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + break; + case spv::OpVectorTimesMatrix: + assert(builder.isVector(left)); + assert(builder.isMatrix(right)); + break; + case spv::OpMatrixTimesVector: + assert(builder.isMatrix(left)); + assert(builder.isVector(right)); + break; + case spv::OpMatrixTimesMatrix: + assert(builder.isMatrix(left)); + assert(builder.isMatrix(right)); + break; + default: + firstClass = false; + break; + } + + if (builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + firstClass = true; + + if (firstClass) { + spv::Id result = builder.createBinOp(op, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + // Handle component-wise +, -, *, %, and / for all combinations of type. + // The result type of all of them is the same type as the (a) matrix operand. + // The algorithm is to: + // - break the matrix(es) into vectors + // - smear any scalar to a vector + // - do vector operations + // - make a matrix out the vector results + switch (op) { + case spv::OpFAdd: + case spv::OpFSub: + case spv::OpFDiv: + case spv::OpFMod: + case spv::OpFMul: + { + // one time set up... + bool leftMat = builder.isMatrix(left); + bool rightMat = builder.isMatrix(right); + unsigned int numCols = leftMat ? builder.getNumColumns(left) : builder.getNumColumns(right); + int numRows = leftMat ? builder.getNumRows(left) : builder.getNumRows(right); + spv::Id scalarType = builder.getScalarTypeId(typeId); + spv::Id vecType = builder.makeVectorType(scalarType, numRows); + std::vector results; + spv::Id smearVec = spv::NoResult; + if (builder.isScalar(left)) + smearVec = builder.smearScalar(decorations.precision, left, vecType); + else if (builder.isScalar(right)) + smearVec = builder.smearScalar(decorations.precision, right, vecType); + + // do each vector op + for (unsigned int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id leftVec = leftMat ? builder.createCompositeExtract( left, vecType, indexes) : smearVec; + spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec; + spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + results.push_back(builder.setPrecision(result, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; + } + default: + assert(0); + return spv::NoResult; + } +} + +spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType typeProxy, const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags, + const glslang::TType &opType) +{ + spv::Op unaryOp = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + switch (op) { + case glslang::EOpNegative: + if (isFloat) { + unaryOp = spv::OpFNegate; + if (builder.isMatrixType(typeId)) + return createUnaryMatrixOperation(unaryOp, decorations, typeId, operand, typeProxy); + } else + unaryOp = spv::OpSNegate; + break; + + case glslang::EOpLogicalNot: + case glslang::EOpVectorLogicalNot: + unaryOp = spv::OpLogicalNot; + break; + case glslang::EOpBitwiseNot: + unaryOp = spv::OpNot; + break; + + case glslang::EOpDeterminant: + libCall = spv::GLSLstd450Determinant; + break; + case glslang::EOpMatrixInverse: + libCall = spv::GLSLstd450MatrixInverse; + break; + case glslang::EOpTranspose: + unaryOp = spv::OpTranspose; + break; + + case glslang::EOpRadians: + libCall = spv::GLSLstd450Radians; + break; + case glslang::EOpDegrees: + libCall = spv::GLSLstd450Degrees; + break; + case glslang::EOpSin: + libCall = spv::GLSLstd450Sin; + break; + case glslang::EOpCos: + libCall = spv::GLSLstd450Cos; + break; + case glslang::EOpTan: + libCall = spv::GLSLstd450Tan; + break; + case glslang::EOpAcos: + libCall = spv::GLSLstd450Acos; + break; + case glslang::EOpAsin: + libCall = spv::GLSLstd450Asin; + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan; + break; + + case glslang::EOpAcosh: + libCall = spv::GLSLstd450Acosh; + break; + case glslang::EOpAsinh: + libCall = spv::GLSLstd450Asinh; + break; + case glslang::EOpAtanh: + libCall = spv::GLSLstd450Atanh; + break; + case glslang::EOpTanh: + libCall = spv::GLSLstd450Tanh; + break; + case glslang::EOpCosh: + libCall = spv::GLSLstd450Cosh; + break; + case glslang::EOpSinh: + libCall = spv::GLSLstd450Sinh; + break; + + case glslang::EOpLength: + libCall = spv::GLSLstd450Length; + break; + case glslang::EOpNormalize: + libCall = spv::GLSLstd450Normalize; + break; + + case glslang::EOpExp: + libCall = spv::GLSLstd450Exp; + break; + case glslang::EOpLog: + libCall = spv::GLSLstd450Log; + break; + case glslang::EOpExp2: + libCall = spv::GLSLstd450Exp2; + break; + case glslang::EOpLog2: + libCall = spv::GLSLstd450Log2; + break; + case glslang::EOpSqrt: + libCall = spv::GLSLstd450Sqrt; + break; + case glslang::EOpInverseSqrt: + libCall = spv::GLSLstd450InverseSqrt; + break; + + case glslang::EOpFloor: + libCall = spv::GLSLstd450Floor; + break; + case glslang::EOpTrunc: + libCall = spv::GLSLstd450Trunc; + break; + case glslang::EOpRound: + libCall = spv::GLSLstd450Round; + break; + case glslang::EOpRoundEven: + libCall = spv::GLSLstd450RoundEven; + break; + case glslang::EOpCeil: + libCall = spv::GLSLstd450Ceil; + break; + case glslang::EOpFract: + libCall = spv::GLSLstd450Fract; + break; + + case glslang::EOpIsNan: + unaryOp = spv::OpIsNan; + break; + case glslang::EOpIsInf: + unaryOp = spv::OpIsInf; + break; + case glslang::EOpIsFinite: + unaryOp = spv::OpIsFinite; + break; + + case glslang::EOpFloatBitsToInt: + case glslang::EOpFloatBitsToUint: + case glslang::EOpIntBitsToFloat: + case glslang::EOpUintBitsToFloat: + case glslang::EOpDoubleBitsToInt64: + case glslang::EOpDoubleBitsToUint64: + case glslang::EOpInt64BitsToDouble: + case glslang::EOpUint64BitsToDouble: + case glslang::EOpFloat16BitsToInt16: + case glslang::EOpFloat16BitsToUint16: + case glslang::EOpInt16BitsToFloat16: + case glslang::EOpUint16BitsToFloat16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpPackSnorm2x16: + libCall = spv::GLSLstd450PackSnorm2x16; + break; + case glslang::EOpUnpackSnorm2x16: + libCall = spv::GLSLstd450UnpackSnorm2x16; + break; + case glslang::EOpPackUnorm2x16: + libCall = spv::GLSLstd450PackUnorm2x16; + break; + case glslang::EOpUnpackUnorm2x16: + libCall = spv::GLSLstd450UnpackUnorm2x16; + break; + case glslang::EOpPackHalf2x16: + libCall = spv::GLSLstd450PackHalf2x16; + break; + case glslang::EOpUnpackHalf2x16: + libCall = spv::GLSLstd450UnpackHalf2x16; + break; + case glslang::EOpPackSnorm4x8: + libCall = spv::GLSLstd450PackSnorm4x8; + break; + case glslang::EOpUnpackSnorm4x8: + libCall = spv::GLSLstd450UnpackSnorm4x8; + break; + case glslang::EOpPackUnorm4x8: + libCall = spv::GLSLstd450PackUnorm4x8; + break; + case glslang::EOpUnpackUnorm4x8: + libCall = spv::GLSLstd450UnpackUnorm4x8; + break; + case glslang::EOpPackDouble2x32: + libCall = spv::GLSLstd450PackDouble2x32; + break; + case glslang::EOpUnpackDouble2x32: + libCall = spv::GLSLstd450UnpackDouble2x32; + break; + + case glslang::EOpPackInt2x32: + case glslang::EOpUnpackInt2x32: + case glslang::EOpPackUint2x32: + case glslang::EOpUnpackUint2x32: + case glslang::EOpPack16: + case glslang::EOpPack32: + case glslang::EOpPack64: + case glslang::EOpUnpack32: + case glslang::EOpUnpack16: + case glslang::EOpUnpack8: + case glslang::EOpPackInt2x16: + case glslang::EOpUnpackInt2x16: + case glslang::EOpPackUint2x16: + case glslang::EOpUnpackUint2x16: + case glslang::EOpPackInt4x16: + case glslang::EOpUnpackInt4x16: + case glslang::EOpPackUint4x16: + case glslang::EOpUnpackUint4x16: + case glslang::EOpPackFloat2x16: + case glslang::EOpUnpackFloat2x16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpDPdx: + unaryOp = spv::OpDPdx; + break; + case glslang::EOpDPdy: + unaryOp = spv::OpDPdy; + break; + case glslang::EOpFwidth: + unaryOp = spv::OpFwidth; + break; + + case glslang::EOpAny: + unaryOp = spv::OpAny; + break; + case glslang::EOpAll: + unaryOp = spv::OpAll; + break; + + case glslang::EOpAbs: + if (isFloat) + libCall = spv::GLSLstd450FAbs; + else + libCall = spv::GLSLstd450SAbs; + break; + case glslang::EOpSign: + if (isFloat) + libCall = spv::GLSLstd450FSign; + else + libCall = spv::GLSLstd450SSign; + break; + + case glslang::EOpDPdxFine: + unaryOp = spv::OpDPdxFine; + break; + case glslang::EOpDPdyFine: + unaryOp = spv::OpDPdyFine; + break; + case glslang::EOpFwidthFine: + unaryOp = spv::OpFwidthFine; + break; + case glslang::EOpDPdxCoarse: + unaryOp = spv::OpDPdxCoarse; + break; + case glslang::EOpDPdyCoarse: + unaryOp = spv::OpDPdyCoarse; + break; + case glslang::EOpFwidthCoarse: + unaryOp = spv::OpFwidthCoarse; + break; + case glslang::EOpRayQueryProceed: + unaryOp = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + unaryOp = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + unaryOp = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + unaryOp = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + unaryOp = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + unaryOp = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpInterpolateAtCentroid: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtCentroid; + break; + case glslang::EOpAtomicCounterIncrement: + case glslang::EOpAtomicCounterDecrement: + case glslang::EOpAtomicCounter: + { + // Handle all of the atomics in one place, in createAtomicOperation() + std::vector operands; + operands.push_back(operand); + return createAtomicOperation(op, decorations.precision, typeId, operands, typeProxy, lvalueCoherentFlags, opType); + } + + case glslang::EOpBitFieldReverse: + unaryOp = spv::OpBitReverse; + break; + case glslang::EOpBitCount: + unaryOp = spv::OpBitCount; + break; + case glslang::EOpFindLSB: + libCall = spv::GLSLstd450FindILsb; + break; + case glslang::EOpFindMSB: + if (isUnsigned) + libCall = spv::GLSLstd450FindUMsb; + else + libCall = spv::GLSLstd450FindSMsb; + break; + + case glslang::EOpCountLeadingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountLeadingZerosINTEL; + break; + + case glslang::EOpCountTrailingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountTrailingZerosINTEL; + break; + + case glslang::EOpBallot: + case glslang::EOpReadFirstInvocation: + case glslang::EOpAnyInvocation: + case glslang::EOpAllInvocations: + case glslang::EOpAllInvocationsEqual: + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + { + std::vector operands; + operands.push_back(operand); + return createInvocationsOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: + case glslang::EOpSubgroupQuadAll: + case glslang::EOpSubgroupQuadAny: { + std::vector operands; + operands.push_back(operand); + return createSubgroupOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpMbcnt: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::MbcntAMD; + break; + + case glslang::EOpCubeFaceIndex: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceIndexAMD; + break; + + case glslang::EOpCubeFaceCoord: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceCoordAMD; + break; + case glslang::EOpSubgroupPartition: + unaryOp = spv::OpGroupNonUniformPartitionNV; + break; + case glslang::EOpConstructReference: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpConvUint64ToAccStruct: + case glslang::EOpConvUvec2ToAccStruct: + unaryOp = spv::OpConvertUToAccelerationStructureKHR; + break; + + case glslang::EOpHitObjectIsEmptyNV: + unaryOp = spv::OpHitObjectIsEmptyNV; + break; + + case glslang::EOpHitObjectIsMissNV: + unaryOp = spv::OpHitObjectIsMissNV; + break; + + case glslang::EOpHitObjectIsHitNV: + unaryOp = spv::OpHitObjectIsHitNV; + break; + + case glslang::EOpHitObjectGetObjectRayOriginNV: + unaryOp = spv::OpHitObjectGetObjectRayOriginNV; + break; + + case glslang::EOpHitObjectGetObjectRayDirectionNV: + unaryOp = spv::OpHitObjectGetObjectRayDirectionNV; + break; + + case glslang::EOpHitObjectGetWorldRayOriginNV: + unaryOp = spv::OpHitObjectGetWorldRayOriginNV; + break; + + case glslang::EOpHitObjectGetWorldRayDirectionNV: + unaryOp = spv::OpHitObjectGetWorldRayDirectionNV; + break; + + case glslang::EOpHitObjectGetObjectToWorldNV: + unaryOp = spv::OpHitObjectGetObjectToWorldNV; + break; + + case glslang::EOpHitObjectGetWorldToObjectNV: + unaryOp = spv::OpHitObjectGetWorldToObjectNV; + break; + + case glslang::EOpHitObjectGetRayTMinNV: + unaryOp = spv::OpHitObjectGetRayTMinNV; + break; + + case glslang::EOpHitObjectGetRayTMaxNV: + unaryOp = spv::OpHitObjectGetRayTMaxNV; + break; + + case glslang::EOpHitObjectGetPrimitiveIndexNV: + unaryOp = spv::OpHitObjectGetPrimitiveIndexNV; + break; + + case glslang::EOpHitObjectGetInstanceIdNV: + unaryOp = spv::OpHitObjectGetInstanceIdNV; + break; + + case glslang::EOpHitObjectGetInstanceCustomIndexNV: + unaryOp = spv::OpHitObjectGetInstanceCustomIndexNV; + break; + + case glslang::EOpHitObjectGetGeometryIndexNV: + unaryOp = spv::OpHitObjectGetGeometryIndexNV; + break; + + case glslang::EOpHitObjectGetHitKindNV: + unaryOp = spv::OpHitObjectGetHitKindNV; + break; + + case glslang::EOpHitObjectGetCurrentTimeNV: + unaryOp = spv::OpHitObjectGetCurrentTimeNV; + break; + + case glslang::EOpHitObjectGetShaderBindingTableRecordIndexNV: + unaryOp = spv::OpHitObjectGetShaderBindingTableRecordIndexNV; + break; + + case glslang::EOpHitObjectGetShaderRecordBufferHandleNV: + unaryOp = spv::OpHitObjectGetShaderRecordBufferHandleNV; + break; + + case glslang::EOpHitObjectGetClusterIdNV: + unaryOp = spv::OpHitObjectGetClusterIdNV; + builder.addExtension(spv::E_SPV_NV_cluster_acceleration_structure); + builder.addCapability(spv::CapabilityShaderInvocationReorderNV); + builder.addCapability(spv::CapabilityRayTracingClusterAccelerationStructureNV); + break; + + case glslang::EOpHitObjectGetSpherePositionNV: + unaryOp = spv::OpHitObjectGetSpherePositionNV; + break; + + case glslang::EOpHitObjectGetSphereRadiusNV: + unaryOp = spv::OpHitObjectGetSphereRadiusNV; + break; + + case glslang::EOpHitObjectIsSphereHitNV: + unaryOp = spv::OpHitObjectIsSphereHitNV; + break; + + case glslang::EOpHitObjectIsLSSHitNV: + unaryOp = spv::OpHitObjectIsLSSHitNV; + break; + + case glslang::EOpFetchMicroTriangleVertexPositionNV: + unaryOp = spv::OpFetchMicroTriangleVertexPositionNV; + break; + + case glslang::EOpFetchMicroTriangleVertexBarycentricNV: + unaryOp = spv::OpFetchMicroTriangleVertexBarycentricNV; + break; + + case glslang::EOpCopyObject: + unaryOp = spv::OpCopyObject; + break; + + case glslang::EOpDepthAttachmentReadEXT: + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + builder.addCapability(spv::CapabilityTileImageDepthReadAccessEXT); + unaryOp = spv::OpDepthAttachmentReadEXT; + decorations.precision = spv::NoPrecision; + break; + case glslang::EOpStencilAttachmentReadEXT: + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + builder.addCapability(spv::CapabilityTileImageStencilReadAccessEXT); + unaryOp = spv::OpStencilAttachmentReadEXT; + decorations.precision = spv::DecorationRelaxedPrecision; + break; + + default: + return 0; + } + + spv::Id id; + if (libCall >= 0) { + std::vector args; + args.push_back(operand); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args); + } else { + id = builder.createUnaryOp(unaryOp, typeId, operand); + } + + decorations.addNoContraction(builder, id); + decorations.addNonUniform(builder, id); + return builder.setPrecision(id, decorations.precision); +} + +// Create a unary operation on a matrix +spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType /* typeProxy */) +{ + // Handle unary operations vector by vector. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the matrix into vectors + // - apply the operation to each vector + // - make a matrix out the vector results + + // get the types sorted out + int numCols = builder.getNumColumns(operand); + int numRows = builder.getNumRows(operand); + spv::Id srcVecType = builder.makeVectorType(builder.getScalarTypeId(builder.getTypeId(operand)), numRows); + spv::Id destVecType = builder.makeVectorType(builder.getScalarTypeId(typeId), numRows); + std::vector results; + + // do each vector op + for (int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id srcVec = builder.createCompositeExtract(operand, srcVecType, indexes); + spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec); + decorations.addNoContraction(builder, destVec); + decorations.addNonUniform(builder, destVec); + results.push_back(builder.setPrecision(destVec, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +// For converting integers where both the bitwidth and the signedness could +// change, but only do the width change here. The caller is still responsible +// for the signedness conversion. +// destType is the final type that will be converted to, but this function +// may only be doing part of that conversion. +spv::Id TGlslangToSpvTraverser::createIntWidthConversion(spv::Id operand, int vectorSize, spv::Id destType, + glslang::TBasicType resultBasicType, glslang::TBasicType operandBasicType) +{ + // Get the result type width, based on the type to convert to. + int width = GetNumBits(resultBasicType); + + // Get the conversion operation and result type, + // based on the target width, but the source type. + spv::Id type = spv::NoType; + spv::Op convOp = spv::OpNop; + if (isTypeSignedInt(operandBasicType)) { + convOp = spv::OpSConvert; + type = builder.makeIntType(width); + } else { + convOp = spv::OpUConvert; + type = builder.makeUintType(width); + } + + if (builder.getOpCode(destType) == spv::OpTypeCooperativeVectorNV) { + type = builder.makeCooperativeVectorTypeNV(type, builder.getCooperativeVectorNumComponents(destType)); + } else if (vectorSize > 0) + type = builder.makeVectorType(type, vectorSize); + else if (builder.getOpCode(destType) == spv::OpTypeCooperativeMatrixKHR || + builder.getOpCode(destType) == spv::OpTypeCooperativeMatrixNV) { + + type = builder.makeCooperativeMatrixTypeWithSameShape(type, destType); + } + + return builder.createUnaryOp(convOp, type, operand); +} + +spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, OpDecorations& decorations, spv::Id destType, + spv::Id operand, glslang::TBasicType resultBasicType, glslang::TBasicType operandBasicType) +{ + spv::Op convOp = spv::OpNop; + spv::Id zero = 0; + spv::Id one = 0; + + int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0; + + if (IsOpNumericConv(op)) { + if (isTypeSignedInt(operandBasicType) && isTypeFloat(resultBasicType)) { + convOp = spv::OpConvertSToF; + } + if (isTypeUnsignedInt(operandBasicType) && isTypeFloat(resultBasicType)) { + convOp = spv::OpConvertUToF; + } + if (isTypeFloat(operandBasicType) && isTypeSignedInt(resultBasicType)) { + convOp = spv::OpConvertFToS; + } + if (isTypeFloat(operandBasicType) && isTypeUnsignedInt(resultBasicType)) { + convOp = spv::OpConvertFToU; + } + if (isTypeSignedInt(operandBasicType) && isTypeSignedInt(resultBasicType)) { + convOp = spv::OpSConvert; + } + if (isTypeUnsignedInt(operandBasicType) && isTypeUnsignedInt(resultBasicType)) { + convOp = spv::OpUConvert; + } + if (isTypeFloat(operandBasicType) && isTypeFloat(resultBasicType)) { + convOp = spv::OpFConvert; + if (builder.isMatrixType(destType)) + return createUnaryMatrixOperation(convOp, decorations, destType, operand, operandBasicType); + } + if (isTypeInt(operandBasicType) && isTypeInt(resultBasicType) && + isTypeUnsignedInt(operandBasicType) != isTypeUnsignedInt(resultBasicType)) { + + if (GetNumBits(operandBasicType) != GetNumBits(resultBasicType)) { + // OpSConvert/OpUConvert + OpBitCast + operand = createIntWidthConversion(operand, vectorSize, destType, resultBasicType, operandBasicType); + } + + if (builder.isInSpecConstCodeGenMode()) { + uint32_t bits = GetNumBits(resultBasicType); + spv::Id zeroType = builder.makeUintType(bits); + if (bits == 64) { + zero = builder.makeInt64Constant(zeroType, 0, false); + } else { + zero = builder.makeIntConstant(zeroType, 0, false); + } + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + } + if (resultBasicType == glslang::EbtBool) { + uint32_t bits = GetNumBits(operandBasicType); + if (isTypeInt(operandBasicType)) { + spv::Id zeroType = builder.makeUintType(bits); + if (bits == 64) { + zero = builder.makeInt64Constant(zeroType, 0, false); + } else { + zero = builder.makeIntConstant(zeroType, 0, false); + } + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + } else { + assert(isTypeFloat(operandBasicType)); + if (bits == 64) { + zero = builder.makeDoubleConstant(0.0); + } else if (bits == 32) { + zero = builder.makeFloatConstant(0.0); + } else { + assert(bits == 16); + zero = builder.makeFloat16Constant(0.0); + } + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFUnordNotEqual, destType, operand, zero); + } + } + if (operandBasicType == glslang::EbtBool) { + uint32_t bits = GetNumBits(resultBasicType); + convOp = spv::OpSelect; + if (isTypeInt(resultBasicType)) { + spv::Id zeroType = isTypeSignedInt(resultBasicType) ? builder.makeIntType(bits) : builder.makeUintType(bits); + if (bits == 64) { + zero = builder.makeInt64Constant(zeroType, 0, false); + one = builder.makeInt64Constant(zeroType, 1, false); + } else { + zero = builder.makeIntConstant(zeroType, 0, false); + one = builder.makeIntConstant(zeroType, 1, false); + } + } else { + assert(isTypeFloat(resultBasicType)); + if (bits == 64) { + zero = builder.makeDoubleConstant(0.0); + one = builder.makeDoubleConstant(1.0); + } else if (bits == 32) { + zero = builder.makeFloatConstant(0.0); + one = builder.makeFloatConstant(1.0); + } else { + assert(bits == 16); + zero = builder.makeFloat16Constant(0.0); + one = builder.makeFloat16Constant(1.0); + } + } + } + } + + if (convOp == spv::OpNop) { + switch (op) { + case glslang::EOpConvUint64ToPtr: + convOp = spv::OpConvertUToPtr; + break; + case glslang::EOpConvPtrToUint64: + convOp = spv::OpConvertPtrToU; + break; + case glslang::EOpConvPtrToUvec2: + case glslang::EOpConvUvec2ToPtr: + convOp = spv::OpBitcast; + break; + + default: + break; + } + } + + spv::Id result = 0; + if (convOp == spv::OpNop) + return result; + + if (convOp == spv::OpSelect) { + zero = makeSmearedConstant(zero, vectorSize); + one = makeSmearedConstant(one, vectorSize); + result = builder.createTriOp(convOp, destType, operand, one, zero); + } else + result = builder.createUnaryOp(convOp, destType, operand); + + result = builder.setPrecision(result, decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize) +{ + if (vectorSize == 0) + return constant; + + spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize); + std::vector components; + for (int c = 0; c < vectorSize; ++c) + components.push_back(constant); + return builder.makeCompositeConstant(vectorTypeId, components); +} + +// For glslang ops that map to SPV atomic opCodes +spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration /*precision*/, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags, const glslang::TType &opType) +{ + spv::Op opCode = spv::OpNop; + + switch (op) { + case glslang::EOpAtomicAdd: + case glslang::EOpImageAtomicAdd: + case glslang::EOpAtomicCounterAdd: + opCode = spv::OpAtomicIAdd; + if (typeProxy == glslang::EbtFloat16 || typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble) { + opCode = spv::OpAtomicFAddEXT; + if (typeProxy == glslang::EbtFloat16 && + (opType.getVectorSize() == 2 || opType.getVectorSize() == 4)) { + builder.addExtension(spv::E_SPV_NV_shader_atomic_fp16_vector); + builder.addCapability(spv::CapabilityAtomicFloat16VectorNV); + } else { + builder.addExtension(spv::E_SPV_EXT_shader_atomic_float_add); + if (typeProxy == glslang::EbtFloat16) { + builder.addExtension(spv::E_SPV_EXT_shader_atomic_float16_add); + builder.addCapability(spv::CapabilityAtomicFloat16AddEXT); + } else if (typeProxy == glslang::EbtFloat) { + builder.addCapability(spv::CapabilityAtomicFloat32AddEXT); + } else { + builder.addCapability(spv::CapabilityAtomicFloat64AddEXT); + } + } + } + break; + case glslang::EOpAtomicSubtract: + case glslang::EOpAtomicCounterSubtract: + opCode = spv::OpAtomicISub; + break; + case glslang::EOpAtomicMin: + case glslang::EOpImageAtomicMin: + case glslang::EOpAtomicCounterMin: + if (typeProxy == glslang::EbtFloat16 || typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble) { + opCode = spv::OpAtomicFMinEXT; + if (typeProxy == glslang::EbtFloat16 && + (opType.getVectorSize() == 2 || opType.getVectorSize() == 4)) { + builder.addExtension(spv::E_SPV_NV_shader_atomic_fp16_vector); + builder.addCapability(spv::CapabilityAtomicFloat16VectorNV); + } else { + builder.addExtension(spv::E_SPV_EXT_shader_atomic_float_min_max); + if (typeProxy == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityAtomicFloat16MinMaxEXT); + else if (typeProxy == glslang::EbtFloat) + builder.addCapability(spv::CapabilityAtomicFloat32MinMaxEXT); + else + builder.addCapability(spv::CapabilityAtomicFloat64MinMaxEXT); + } + } else if (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) { + opCode = spv::OpAtomicUMin; + } else { + opCode = spv::OpAtomicSMin; + } + break; + case glslang::EOpAtomicMax: + case glslang::EOpImageAtomicMax: + case glslang::EOpAtomicCounterMax: + if (typeProxy == glslang::EbtFloat16 || typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble) { + opCode = spv::OpAtomicFMaxEXT; + if (typeProxy == glslang::EbtFloat16 && + (opType.getVectorSize() == 2 || opType.getVectorSize() == 4)) { + builder.addExtension(spv::E_SPV_NV_shader_atomic_fp16_vector); + builder.addCapability(spv::CapabilityAtomicFloat16VectorNV); + } else { + builder.addExtension(spv::E_SPV_EXT_shader_atomic_float_min_max); + if (typeProxy == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityAtomicFloat16MinMaxEXT); + else if (typeProxy == glslang::EbtFloat) + builder.addCapability(spv::CapabilityAtomicFloat32MinMaxEXT); + else + builder.addCapability(spv::CapabilityAtomicFloat64MinMaxEXT); + } + } else if (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) { + opCode = spv::OpAtomicUMax; + } else { + opCode = spv::OpAtomicSMax; + } + break; + case glslang::EOpAtomicAnd: + case glslang::EOpImageAtomicAnd: + case glslang::EOpAtomicCounterAnd: + opCode = spv::OpAtomicAnd; + break; + case glslang::EOpAtomicOr: + case glslang::EOpImageAtomicOr: + case glslang::EOpAtomicCounterOr: + opCode = spv::OpAtomicOr; + break; + case glslang::EOpAtomicXor: + case glslang::EOpImageAtomicXor: + case glslang::EOpAtomicCounterXor: + opCode = spv::OpAtomicXor; + break; + case glslang::EOpAtomicExchange: + case glslang::EOpImageAtomicExchange: + case glslang::EOpAtomicCounterExchange: + if ((typeProxy == glslang::EbtFloat16) && + (opType.getVectorSize() == 2 || opType.getVectorSize() == 4)) { + builder.addExtension(spv::E_SPV_NV_shader_atomic_fp16_vector); + builder.addCapability(spv::CapabilityAtomicFloat16VectorNV); + } + + opCode = spv::OpAtomicExchange; + break; + case glslang::EOpAtomicCompSwap: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpAtomicCounterCompSwap: + opCode = spv::OpAtomicCompareExchange; + break; + case glslang::EOpAtomicCounterIncrement: + opCode = spv::OpAtomicIIncrement; + break; + case glslang::EOpAtomicCounterDecrement: + opCode = spv::OpAtomicIDecrement; + break; + case glslang::EOpAtomicCounter: + case glslang::EOpImageAtomicLoad: + case glslang::EOpAtomicLoad: + opCode = spv::OpAtomicLoad; + break; + case glslang::EOpAtomicStore: + case glslang::EOpImageAtomicStore: + opCode = spv::OpAtomicStore; + break; + default: + assert(0); + break; + } + + if (typeProxy == glslang::EbtInt64 || typeProxy == glslang::EbtUint64) + builder.addCapability(spv::CapabilityInt64Atomics); + + // Sort out the operands + // - mapping from glslang -> SPV + // - there are extra SPV operands that are optional in glslang + // - compare-exchange swaps the value and comparator + // - compare-exchange has an extra memory semantics + // - EOpAtomicCounterDecrement needs a post decrement + spv::Id pointerId = 0, compareId = 0, valueId = 0; + // scope defaults to Device in the old model, QueueFamilyKHR in the new model + spv::Id scopeId; + if (glslangIntermediate->usingVulkanMemoryModel()) { + scopeId = builder.makeUintConstant(spv::ScopeQueueFamilyKHR); + } else { + scopeId = builder.makeUintConstant(spv::ScopeDevice); + } + // semantics default to relaxed + spv::Id semanticsId = builder.makeUintConstant(lvalueCoherentFlags.isVolatile() && + glslangIntermediate->usingVulkanMemoryModel() ? + spv::MemorySemanticsVolatileMask : + spv::MemorySemanticsMaskNone); + spv::Id semanticsId2 = semanticsId; + + pointerId = operands[0]; + if (opCode == spv::OpAtomicIIncrement || opCode == spv::OpAtomicIDecrement) { + // no additional operands + } else if (opCode == spv::OpAtomicCompareExchange) { + compareId = operands[1]; + valueId = operands[2]; + if (operands.size() > 3) { + scopeId = operands[3]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[4]) | builder.getConstantScalar(operands[5])); + semanticsId2 = builder.makeUintConstant( + builder.getConstantScalar(operands[6]) | builder.getConstantScalar(operands[7])); + } + } else if (opCode == spv::OpAtomicLoad) { + if (operands.size() > 1) { + scopeId = operands[1]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3])); + } + } else { + // atomic store or RMW + valueId = operands[1]; + if (operands.size() > 2) { + scopeId = operands[2]; + semanticsId = builder.makeUintConstant + (builder.getConstantScalar(operands[3]) | builder.getConstantScalar(operands[4])); + } + } + + // Check for capabilities + unsigned semanticsImmediate = builder.getConstantScalar(semanticsId) | builder.getConstantScalar(semanticsId2); + if (semanticsImmediate & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + if (builder.getConstantScalar(scopeId) == spv::ScopeQueueFamily) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + if (glslangIntermediate->usingVulkanMemoryModel() && builder.getConstantScalar(scopeId) == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + + std::vector spvAtomicOperands; // hold the spv operands + spvAtomicOperands.reserve(6); + spvAtomicOperands.push_back(pointerId); + spvAtomicOperands.push_back(scopeId); + spvAtomicOperands.push_back(semanticsId); + if (opCode == spv::OpAtomicCompareExchange) { + spvAtomicOperands.push_back(semanticsId2); + spvAtomicOperands.push_back(valueId); + spvAtomicOperands.push_back(compareId); + } else if (opCode != spv::OpAtomicLoad && opCode != spv::OpAtomicIIncrement && opCode != spv::OpAtomicIDecrement) { + spvAtomicOperands.push_back(valueId); + } + + if (opCode == spv::OpAtomicStore) { + builder.createNoResultOp(opCode, spvAtomicOperands); + return 0; + } else { + spv::Id resultId = builder.createOp(opCode, typeId, spvAtomicOperands); + + // GLSL and HLSL atomic-counter decrement return post-decrement value, + // while SPIR-V returns pre-decrement value. Translate between these semantics. + if (op == glslang::EOpAtomicCounterDecrement) + resultId = builder.createBinOp(spv::OpISub, typeId, resultId, builder.makeIntConstant(1)); + + return resultId; + } +} + +// Create group invocation operations. +spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + std::vector spvGroupOperands; + spv::GroupOperation groupOperation = spv::GroupOperationMax; + + if (op == glslang::EOpBallot || op == glslang::EOpReadFirstInvocation || + op == glslang::EOpReadInvocation) { + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + } else if (op == glslang::EOpAnyInvocation || + op == glslang::EOpAllInvocations || + op == glslang::EOpAllInvocationsEqual) { + builder.addExtension(spv::E_SPV_KHR_subgroup_vote); + builder.addCapability(spv::CapabilitySubgroupVoteKHR); + } else { + builder.addCapability(spv::CapabilityGroups); + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpAddInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpAddInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform || + op == glslang::EOpAddInvocationsExclusiveScanNonUniform) + builder.addExtension(spv::E_SPV_AMD_shader_ballot); + + switch (op) { + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + groupOperation = spv::GroupOperationExclusiveScan; + break; + default: + break; + } + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + } + } + + for (auto opIt = operands.begin(); opIt != operands.end(); ++opIt) { + spv::IdImmediate op = { true, *opIt }; + spvGroupOperands.push_back(op); + } + + switch (op) { + case glslang::EOpAnyInvocation: + opCode = spv::OpSubgroupAnyKHR; + break; + case glslang::EOpAllInvocations: + opCode = spv::OpSubgroupAllKHR; + break; + case glslang::EOpAllInvocationsEqual: + opCode = spv::OpSubgroupAllEqualKHR; + break; + case glslang::EOpReadInvocation: + opCode = spv::OpSubgroupReadInvocationKHR; + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + break; + case glslang::EOpReadFirstInvocation: + opCode = spv::OpSubgroupFirstInvocationKHR; + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + break; + case glslang::EOpBallot: + { + // NOTE: According to the spec, the result type of "OpSubgroupBallotKHR" must be a 4 component vector of 32 + // bit integer types. The GLSL built-in function "ballotARB()" assumes the maximum number of invocations in + // a subgroup is 64. Thus, we have to convert uvec4.xy to uint64_t as follow: + // + // result = Bitcast(SubgroupBallotKHR(Predicate).xy) + // + spv::Id uintType = builder.makeUintType(32); + spv::Id uvec4Type = builder.makeVectorType(uintType, 4); + spv::Id result = builder.createOp(spv::OpSubgroupBallotKHR, uvec4Type, spvGroupOperands); + + std::vector components; + components.push_back(builder.createCompositeExtract(result, uintType, 0)); + components.push_back(builder.createCompositeExtract(result, uintType, 1)); + + spv::Id uvec2Type = builder.makeVectorType(uintType, 2); + return builder.createUnaryOp(spv::OpBitcast, typeId, + builder.createCompositeConstruct(uvec2Type, components)); + } + + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + if (op == glslang::EOpMinInvocations || + op == glslang::EOpMinInvocationsInclusiveScan || + op == glslang::EOpMinInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMin; + else { + if (isUnsigned) + opCode = spv::OpGroupUMin; + else + opCode = spv::OpGroupSMin; + } + } else if (op == glslang::EOpMaxInvocations || + op == glslang::EOpMaxInvocationsInclusiveScan || + op == glslang::EOpMaxInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMax; + else { + if (isUnsigned) + opCode = spv::OpGroupUMax; + else + opCode = spv::OpGroupSMax; + } + } else { + if (isFloat) + opCode = spv::OpGroupFAdd; + else + opCode = spv::OpGroupIAdd; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMinNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMinNonUniformAMD; + else + opCode = spv::OpGroupSMinNonUniformAMD; + } + } + else if (op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMaxNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMaxNonUniformAMD; + else + opCode = spv::OpGroupSMaxNonUniformAMD; + } + } + else { + if (isFloat) + opCode = spv::OpGroupFAddNonUniformAMD; + else + opCode = spv::OpGroupIAddNonUniformAMD; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + default: + logger->missingFunctionality("invocation operation"); + return spv::NoResult; + } + + assert(opCode != spv::OpNop); + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +// Create group invocation operations on a vector +spv::Id TGlslangToSpvTraverser::CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands) +{ + assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin || + op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax || + op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast || + op == spv::OpSubgroupReadInvocationKHR || op == spv::OpSubgroupFirstInvocationKHR || + op == spv::OpGroupFMinNonUniformAMD || op == spv::OpGroupUMinNonUniformAMD || + op == spv::OpGroupSMinNonUniformAMD || + op == spv::OpGroupFMaxNonUniformAMD || op == spv::OpGroupUMaxNonUniformAMD || + op == spv::OpGroupSMaxNonUniformAMD || + op == spv::OpGroupFAddNonUniformAMD || op == spv::OpGroupIAddNonUniformAMD); + + // Handle group invocation operations scalar by scalar. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the vector into scalars + // - apply the operation to each scalar + // - make a vector out the scalar results + + // get the types sorted out + int numComponents = builder.getNumComponents(operands[0]); + spv::Id scalarType = builder.getScalarTypeId(builder.getTypeId(operands[0])); + std::vector results; + + // do each scalar op + for (int comp = 0; comp < numComponents; ++comp) { + std::vector indexes; + indexes.push_back(comp); + spv::IdImmediate scalar = { true, builder.createCompositeExtract(operands[0], scalarType, indexes) }; + std::vector spvGroupOperands; + if (op == spv::OpSubgroupReadInvocationKHR) { + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else if (op == spv::OpSubgroupFirstInvocationKHR) { + spvGroupOperands.push_back(scalar); + } else if (op == spv::OpGroupBroadcast) { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + spvGroupOperands.push_back(scalar); + } + + results.push_back(builder.createOp(op, scalarType, spvGroupOperands)); + } + + // put the pieces together + return builder.createCompositeConstruct(typeId, results); +} + +// Create subgroup invocation operations. +spv::Id TGlslangToSpvTraverser::createSubgroupOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + // Add the required capabilities. + switch (op) { + case glslang::EOpSubgroupElect: + builder.addCapability(spv::CapabilityGroupNonUniform); + break; + case glslang::EOpSubgroupQuadAll: + case glslang::EOpSubgroupQuadAny: + builder.addExtension(spv::E_SPV_KHR_quad_control); + builder.addCapability(spv::CapabilityQuadControlKHR); + [[fallthrough]]; + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformVote); + break; + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + break; + case glslang::EOpSubgroupRotate: + case glslang::EOpSubgroupClusteredRotate: + builder.addExtension(spv::E_SPV_KHR_subgroup_rotate); + builder.addCapability(spv::CapabilityGroupNonUniformRotateKHR); + break; + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffle); + break; + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffleRelative); + break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformArithmetic); + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformClustered); + break; + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformQuad); + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + builder.addExtension(spv::E_SPV_NV_shader_subgroup_partitioned); + builder.addCapability(spv::CapabilityGroupNonUniformPartitionedNV); + break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + + const bool isUnsigned = isTypeUnsignedInt(typeProxy); + const bool isFloat = isTypeFloat(typeProxy); + const bool isBool = typeProxy == glslang::EbtBool; + + spv::Op opCode = spv::OpNop; + + // Figure out which opcode to use. + switch (op) { + case glslang::EOpSubgroupElect: opCode = spv::OpGroupNonUniformElect; break; + case glslang::EOpSubgroupQuadAll: opCode = spv::OpGroupNonUniformQuadAllKHR; break; + case glslang::EOpSubgroupAll: opCode = spv::OpGroupNonUniformAll; break; + case glslang::EOpSubgroupQuadAny: opCode = spv::OpGroupNonUniformQuadAnyKHR; break; + case glslang::EOpSubgroupAny: opCode = spv::OpGroupNonUniformAny; break; + case glslang::EOpSubgroupAllEqual: opCode = spv::OpGroupNonUniformAllEqual; break; + case glslang::EOpSubgroupBroadcast: opCode = spv::OpGroupNonUniformBroadcast; break; + case glslang::EOpSubgroupBroadcastFirst: opCode = spv::OpGroupNonUniformBroadcastFirst; break; + case glslang::EOpSubgroupBallot: opCode = spv::OpGroupNonUniformBallot; break; + case glslang::EOpSubgroupInverseBallot: opCode = spv::OpGroupNonUniformInverseBallot; break; + case glslang::EOpSubgroupBallotBitExtract: opCode = spv::OpGroupNonUniformBallotBitExtract; break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: opCode = spv::OpGroupNonUniformBallotBitCount; break; + case glslang::EOpSubgroupBallotFindLSB: opCode = spv::OpGroupNonUniformBallotFindLSB; break; + case glslang::EOpSubgroupBallotFindMSB: opCode = spv::OpGroupNonUniformBallotFindMSB; break; + case glslang::EOpSubgroupShuffle: opCode = spv::OpGroupNonUniformShuffle; break; + case glslang::EOpSubgroupShuffleXor: opCode = spv::OpGroupNonUniformShuffleXor; break; + case glslang::EOpSubgroupShuffleUp: opCode = spv::OpGroupNonUniformShuffleUp; break; + case glslang::EOpSubgroupShuffleDown: opCode = spv::OpGroupNonUniformShuffleDown; break; + case glslang::EOpSubgroupRotate: + case glslang::EOpSubgroupClusteredRotate: opCode = spv::OpGroupNonUniformRotateKHR; break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + if (isFloat) { + opCode = spv::OpGroupNonUniformFAdd; + } else { + opCode = spv::OpGroupNonUniformIAdd; + } + break; + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMul: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMul; + } else { + opCode = spv::OpGroupNonUniformIMul; + } + break; + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMin: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMin; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMin; + } else { + opCode = spv::OpGroupNonUniformSMin; + } + break; + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveMax: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMax; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMax; + } else { + opCode = spv::OpGroupNonUniformSMax; + } + break; + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalAnd; + } else { + opCode = spv::OpGroupNonUniformBitwiseAnd; + } + break; + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveOr: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalOr; + } else { + opCode = spv::OpGroupNonUniformBitwiseOr; + } + break; + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveXor: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalXor; + } else { + opCode = spv::OpGroupNonUniformBitwiseXor; + } + break; + case glslang::EOpSubgroupQuadBroadcast: opCode = spv::OpGroupNonUniformQuadBroadcast; break; + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: opCode = spv::OpGroupNonUniformQuadSwap; break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + // get the right Group Operation + spv::GroupOperation groupOperation = spv::GroupOperationMax; + switch (op) { + default: + break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + groupOperation = spv::GroupOperationExclusiveScan; + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + groupOperation = spv::GroupOperationClusteredReduce; + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + groupOperation = spv::GroupOperationPartitionedReduceNV; + break; + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + groupOperation = spv::GroupOperationPartitionedInclusiveScanNV; + break; + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + groupOperation = spv::GroupOperationPartitionedExclusiveScanNV; + break; + } + + // build the instruction + std::vector spvGroupOperands; + + // Every operation begins with the Execution Scope operand. + spv::IdImmediate executionScope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + // All other ops need the execution scope. Quad Control Ops don't need scope, it's always Quad. + if (opCode != spv::OpGroupNonUniformQuadAllKHR && opCode != spv::OpGroupNonUniformQuadAnyKHR) { + spvGroupOperands.push_back(executionScope); + } + + // Next, for all operations that use a Group Operation, push that as an operand. + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOperand = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOperand); + } + + // Push back the operands next. + for (auto opIt = operands.cbegin(); opIt != operands.cend(); ++opIt) { + spv::IdImmediate operand = { true, *opIt }; + spvGroupOperands.push_back(operand); + } + + // Some opcodes have additional operands. + spv::Id directionId = spv::NoResult; + switch (op) { + default: break; + case glslang::EOpSubgroupQuadSwapHorizontal: directionId = builder.makeUintConstant(0); break; + case glslang::EOpSubgroupQuadSwapVertical: directionId = builder.makeUintConstant(1); break; + case glslang::EOpSubgroupQuadSwapDiagonal: directionId = builder.makeUintConstant(2); break; + } + if (directionId != spv::NoResult) { + spv::IdImmediate direction = { true, directionId }; + spvGroupOperands.push_back(direction); + } + + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + size_t consumedOperands = operands.size(); + spv::Id typeId0 = 0; + if (consumedOperands > 0) + typeId0 = builder.getTypeId(operands[0]); + spv::Id typeId1 = 0; + if (consumedOperands > 1) + typeId1 = builder.getTypeId(operands[1]); + spv::Id frexpIntType = 0; + + switch (op) { + case glslang::EOpMin: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMin : spv::GLSLstd450FMin; + else if (isUnsigned) + libCall = spv::GLSLstd450UMin; + else + libCall = spv::GLSLstd450SMin; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpModf: + { + libCall = spv::GLSLstd450ModfStruct; + assert(builder.isFloatType(builder.getScalarTypeId(typeId0))); + // The returned struct has two members of the same type as the first argument + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 1; + } + break; + case glslang::EOpMax: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMax : spv::GLSLstd450FMax; + else if (isUnsigned) + libCall = spv::GLSLstd450UMax; + else + libCall = spv::GLSLstd450SMax; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpPow: + libCall = spv::GLSLstd450Pow; + break; + case glslang::EOpDot: + case glslang::EOpDotPackedEXT: + case glslang::EOpDotAccSatEXT: + case glslang::EOpDotPackedAccSatEXT: + { + if (builder.isFloatType(builder.getScalarTypeId(typeId0)) || + // HLSL supports dot(int,int) which is just a multiply + glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + if (typeProxy == glslang::EbtBFloat16) { + builder.addExtension(spv::E_SPV_KHR_bfloat16); + builder.addCapability(spv::CapabilityBFloat16DotProductKHR); + } + opCode = spv::OpDot; + } else { + builder.addExtension(spv::E_SPV_KHR_integer_dot_product); + builder.addCapability(spv::CapabilityDotProductKHR); + const unsigned int vectorSize = builder.getNumComponents(operands[0]); + if (op == glslang::EOpDotPackedEXT || op == glslang::EOpDotPackedAccSatEXT) { + builder.addCapability(spv::CapabilityDotProductInput4x8BitPackedKHR); + } else if (vectorSize == 4 && builder.getScalarTypeWidth(typeId0) == 8) { + builder.addCapability(spv::CapabilityDotProductInput4x8BitKHR); + } else { + builder.addCapability(spv::CapabilityDotProductInputAllKHR); + } + const bool type0isSigned = builder.isIntType(builder.getScalarTypeId(typeId0)); + const bool type1isSigned = builder.isIntType(builder.getScalarTypeId(typeId1)); + const bool accSat = (op == glslang::EOpDotAccSatEXT || op == glslang::EOpDotPackedAccSatEXT); + if (!type0isSigned && !type1isSigned) { + opCode = accSat ? spv::OpUDotAccSatKHR : spv::OpUDotKHR; + } else if (type0isSigned && type1isSigned) { + opCode = accSat ? spv::OpSDotAccSatKHR : spv::OpSDotKHR; + } else { + opCode = accSat ? spv::OpSUDotAccSatKHR : spv::OpSUDotKHR; + // the spir-v opcode assumes the operands to be "signed, unsigned" in that order, so swap if needed + if (type1isSigned) { + std::swap(operands[0], operands[1]); + } + } + std::vector operands2; + for (auto &o : operands) { + operands2.push_back({true, o}); + } + if (op == glslang::EOpDotPackedEXT || op == glslang::EOpDotPackedAccSatEXT) { + operands2.push_back({false, 0}); + } + return builder.createOp(opCode, typeId, operands2); + } + } + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan2; + break; + + case glslang::EOpClamp: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NClamp : spv::GLSLstd450FClamp; + else if (isUnsigned) + libCall = spv::GLSLstd450UClamp; + else + libCall = spv::GLSLstd450SClamp; + builder.promoteScalar(precision, operands.front(), operands[1]); + builder.promoteScalar(precision, operands.front(), operands[2]); + break; + case glslang::EOpMix: + if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) { + assert(isFloat); + libCall = spv::GLSLstd450FMix; + } else { + opCode = spv::OpSelect; + std::swap(operands.front(), operands.back()); + } + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpStep: + libCall = spv::GLSLstd450Step; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpSmoothStep: + libCall = spv::GLSLstd450SmoothStep; + builder.promoteScalar(precision, operands[0], operands[2]); + builder.promoteScalar(precision, operands[1], operands[2]); + break; + + case glslang::EOpDistance: + libCall = spv::GLSLstd450Distance; + break; + case glslang::EOpCross: + libCall = spv::GLSLstd450Cross; + break; + case glslang::EOpFaceForward: + libCall = spv::GLSLstd450FaceForward; + break; + case glslang::EOpReflect: + libCall = spv::GLSLstd450Reflect; + break; + case glslang::EOpRefract: + libCall = spv::GLSLstd450Refract; + break; + case glslang::EOpBarrier: + { + // This is for the extended controlBarrier function, with four operands. + // The unextended barrier() goes through createNoArgOperation. + assert(operands.size() == 4); + unsigned int executionScope = builder.getConstantScalar(operands[0]); + unsigned int memoryScope = builder.getConstantScalar(operands[1]); + unsigned int semantics = builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3]); + builder.createControlBarrier((spv::Scope)executionScope, (spv::Scope)memoryScope, + (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && (executionScope == spv::ScopeDevice || + memoryScope == spv::ScopeDevice)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + case glslang::EOpMemoryBarrier: + { + // This is for the extended memoryBarrier function, with three operands. + // The unextended memoryBarrier() goes through createNoArgOperation. + assert(operands.size() == 3); + unsigned int memoryScope = builder.getConstantScalar(operands[0]); + unsigned int semantics = builder.getConstantScalar(operands[1]) | builder.getConstantScalar(operands[2]); + builder.createMemoryBarrier((spv::Scope)memoryScope, (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && memoryScope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + + case glslang::EOpInterpolateAtSample: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtSample; + break; + case glslang::EOpInterpolateAtOffset: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtOffset; + break; + case glslang::EOpAddCarry: + opCode = spv::OpIAddCarry; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpSubBorrow: + opCode = spv::OpISubBorrow; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpUMulExtended: + opCode = spv::OpUMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpIMulExtended: + opCode = spv::OpSMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpBitfieldExtract: + if (isUnsigned) + opCode = spv::OpBitFieldUExtract; + else + opCode = spv::OpBitFieldSExtract; + break; + case glslang::EOpBitfieldInsert: + opCode = spv::OpBitFieldInsert; + break; + + case glslang::EOpFma: + libCall = spv::GLSLstd450Fma; + break; + case glslang::EOpFrexp: + { + libCall = spv::GLSLstd450FrexpStruct; + assert(builder.isPointerType(typeId1)); + typeId1 = builder.getContainedTypeId(typeId1); + int width = builder.getScalarTypeWidth(typeId1); + if (width == 16) + // Using 16-bit exp operand, enable extension SPV_AMD_gpu_shader_int16 + builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16); + if (builder.getNumComponents(operands[0]) == 1) + frexpIntType = builder.makeIntegerType(width, true); + else + frexpIntType = builder.makeVectorType(builder.makeIntegerType(width, true), + builder.getNumComponents(operands[0])); + typeId = builder.makeStructResultType(typeId0, frexpIntType); + consumedOperands = 1; + } + break; + case glslang::EOpLdexp: + libCall = spv::GLSLstd450Ldexp; + break; + + case glslang::EOpReadInvocation: + return createInvocationsOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + case glslang::EOpSubgroupRotate: + case glslang::EOpSubgroupClusteredRotate: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + return createSubgroupOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSwizzleInvocations: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsAMD; + break; + case glslang::EOpSwizzleInvocationsMasked: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsMaskedAMD; + break; + case glslang::EOpWriteInvocation: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::WriteInvocationAMD; + break; + + case glslang::EOpMin3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMin3AMD; + else { + if (isUnsigned) + libCall = spv::UMin3AMD; + else + libCall = spv::SMin3AMD; + } + break; + case glslang::EOpMax3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMax3AMD; + else { + if (isUnsigned) + libCall = spv::UMax3AMD; + else + libCall = spv::SMax3AMD; + } + break; + case glslang::EOpMid3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMid3AMD; + else { + if (isUnsigned) + libCall = spv::UMid3AMD; + else + libCall = spv::SMid3AMD; + } + break; + + case glslang::EOpInterpolateAtVertex: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + libCall = spv::InterpolateAtVertexAMD; + break; + + case glslang::EOpReportIntersection: + typeId = builder.makeBoolType(); + opCode = spv::OpReportIntersectionKHR; + break; + case glslang::EOpTraceNV: + builder.createNoResultOp(spv::OpTraceNV, operands); + return 0; + case glslang::EOpTraceRayMotionNV: + builder.addExtension(spv::E_SPV_NV_ray_tracing_motion_blur); + builder.addCapability(spv::CapabilityRayTracingMotionBlurNV); + builder.createNoResultOp(spv::OpTraceRayMotionNV, operands); + return 0; + case glslang::EOpTraceKHR: + builder.createNoResultOp(spv::OpTraceRayKHR, operands); + return 0; + case glslang::EOpExecuteCallableNV: + builder.createNoResultOp(spv::OpExecuteCallableNV, operands); + return 0; + case glslang::EOpExecuteCallableKHR: + builder.createNoResultOp(spv::OpExecuteCallableKHR, operands); + return 0; + + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR, operands); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operands); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryProceed: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetIntersectionType: + typeId = builder.makeUintType(32); + opCode = spv::OpRayQueryGetIntersectionTypeKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetIntersectionT: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetIntersectionTKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceCustomIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceId: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceIdKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + typeId = builder.makeUintType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR; + break; + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionGeometryIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionPrimitiveIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionBarycentrics: + typeId = builder.makeVectorType(builder.makeFloatType(32), 2); + opCode = spv::OpRayQueryGetIntersectionBarycentricsKHR; + break; + case glslang::EOpRayQueryGetIntersectionFrontFace: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionFrontFaceKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionObjectToWorldKHR; + break; + case glslang::EOpRayQueryGetIntersectionClusterIdNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpRayQueryGetIntersectionClusterIdNV; + break; + case glslang::EOpRayQueryGetIntersectionWorldToObject: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionWorldToObjectKHR; + break; + case glslang::EOpRayQueryGetIntersectionSpherePositionNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionSpherePositionNV; + break; + case glslang::EOpRayQueryGetIntersectionSphereRadiusNV: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetIntersectionSphereRadiusNV; + break; + case glslang::EOpRayQueryGetIntersectionLSSHitValueNV: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetIntersectionLSSHitValueNV; + break; + case glslang::EOpRayQueryIsSphereHitNV: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryIsSphereHitNV; + break; + case glslang::EOpRayQueryIsLSSHitNV: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryIsLSSHitNV; + break; + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + builder.createNoResultOp(spv::OpWritePackedPrimitiveIndices4x8NV, operands); + return 0; + case glslang::EOpEmitMeshTasksEXT: + if (taskPayloadID) + operands.push_back(taskPayloadID); + // As per SPV_EXT_mesh_shader make it a terminating instruction in the current block + builder.makeStatementTerminator(spv::OpEmitMeshTasksEXT, operands, "post-OpEmitMeshTasksEXT"); + return 0; + case glslang::EOpSetMeshOutputsEXT: + builder.createNoResultOp(spv::OpSetMeshOutputsEXT, operands); + return 0; + case glslang::EOpCooperativeMatrixMulAddNV: + opCode = spv::OpCooperativeMatrixMulAddNV; + break; + case glslang::EOpHitObjectTraceRayNV: + builder.createNoResultOp(spv::OpHitObjectTraceRayNV, operands); + return 0; + case glslang::EOpHitObjectTraceRayMotionNV: + builder.createNoResultOp(spv::OpHitObjectTraceRayMotionNV, operands); + return 0; + case glslang::EOpHitObjectRecordHitNV: + builder.createNoResultOp(spv::OpHitObjectRecordHitNV, operands); + return 0; + case glslang::EOpHitObjectRecordHitMotionNV: + builder.createNoResultOp(spv::OpHitObjectRecordHitMotionNV, operands); + return 0; + case glslang::EOpHitObjectRecordHitWithIndexNV: + builder.createNoResultOp(spv::OpHitObjectRecordHitWithIndexNV, operands); + return 0; + case glslang::EOpHitObjectRecordHitWithIndexMotionNV: + builder.createNoResultOp(spv::OpHitObjectRecordHitWithIndexMotionNV, operands); + return 0; + case glslang::EOpHitObjectRecordMissNV: + builder.createNoResultOp(spv::OpHitObjectRecordMissNV, operands); + return 0; + case glslang::EOpHitObjectRecordMissMotionNV: + builder.createNoResultOp(spv::OpHitObjectRecordMissMotionNV, operands); + return 0; + case glslang::EOpHitObjectExecuteShaderNV: + builder.createNoResultOp(spv::OpHitObjectExecuteShaderNV, operands); + return 0; + case glslang::EOpHitObjectIsEmptyNV: + typeId = builder.makeBoolType(); + opCode = spv::OpHitObjectIsEmptyNV; + break; + case glslang::EOpHitObjectIsMissNV: + typeId = builder.makeBoolType(); + opCode = spv::OpHitObjectIsMissNV; + break; + case glslang::EOpHitObjectIsHitNV: + typeId = builder.makeBoolType(); + opCode = spv::OpHitObjectIsHitNV; + break; + case glslang::EOpHitObjectIsSphereHitNV: + typeId = builder.makeBoolType(); + opCode = spv::OpHitObjectIsSphereHitNV; + break; + case glslang::EOpHitObjectIsLSSHitNV: + typeId = builder.makeBoolType(); + opCode = spv::OpHitObjectIsLSSHitNV; + break; + case glslang::EOpHitObjectGetRayTMinNV: + typeId = builder.makeFloatType(32); + opCode = spv::OpHitObjectGetRayTMinNV; + break; + case glslang::EOpHitObjectGetRayTMaxNV: + typeId = builder.makeFloatType(32); + opCode = spv::OpHitObjectGetRayTMaxNV; + break; + case glslang::EOpHitObjectGetObjectRayOriginNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpHitObjectGetObjectRayOriginNV; + break; + case glslang::EOpHitObjectGetObjectRayDirectionNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpHitObjectGetObjectRayDirectionNV; + break; + case glslang::EOpHitObjectGetWorldRayOriginNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpHitObjectGetWorldRayOriginNV; + break; + case glslang::EOpHitObjectGetWorldRayDirectionNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpHitObjectGetWorldRayDirectionNV; + break; + case glslang::EOpHitObjectGetWorldToObjectNV: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpHitObjectGetWorldToObjectNV; + break; + case glslang::EOpHitObjectGetObjectToWorldNV: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpHitObjectGetObjectToWorldNV; + break; + case glslang::EOpHitObjectGetInstanceCustomIndexNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpHitObjectGetInstanceCustomIndexNV; + break; + case glslang::EOpHitObjectGetInstanceIdNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpHitObjectGetInstanceIdNV; + break; + case glslang::EOpHitObjectGetGeometryIndexNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpHitObjectGetGeometryIndexNV; + break; + case glslang::EOpHitObjectGetPrimitiveIndexNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpHitObjectGetPrimitiveIndexNV; + break; + case glslang::EOpHitObjectGetHitKindNV: + typeId = builder.makeIntegerType(32, 0); + opCode = spv::OpHitObjectGetHitKindNV; + break; + case glslang::EOpHitObjectGetCurrentTimeNV: + typeId = builder.makeFloatType(32); + opCode = spv::OpHitObjectGetCurrentTimeNV; + break; + case glslang::EOpHitObjectGetShaderBindingTableRecordIndexNV: + typeId = builder.makeIntegerType(32, 0); + opCode = spv::OpHitObjectGetShaderBindingTableRecordIndexNV; + return 0; + case glslang::EOpHitObjectGetAttributesNV: + builder.createNoResultOp(spv::OpHitObjectGetAttributesNV, operands); + return 0; + case glslang::EOpHitObjectGetShaderRecordBufferHandleNV: + typeId = builder.makeVectorType(builder.makeUintType(32), 2); + opCode = spv::OpHitObjectGetShaderRecordBufferHandleNV; + break; + case glslang::EOpHitObjectGetClusterIdNV: + typeId = builder.makeIntegerType(32, 1); + opCode = spv::OpHitObjectGetClusterIdNV; + break; + case glslang::EOpReorderThreadNV: { + if (operands.size() == 2) { + builder.createNoResultOp(spv::OpReorderThreadWithHintNV, operands); + } else { + builder.createNoResultOp(spv::OpReorderThreadWithHitObjectNV, operands); + } + return 0; + + } + + case glslang::EOpImageSampleWeightedQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageSampleWeightedQCOM; + addImageProcessingQCOMDecoration(operands[2], spv::DecorationWeightTextureQCOM); + break; + case glslang::EOpImageBoxFilterQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBoxFilterQCOM; + break; + case glslang::EOpImageBlockMatchSADQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchSADQCOM; + addImageProcessingQCOMDecoration(operands[0], spv::DecorationBlockMatchTextureQCOM); + addImageProcessingQCOMDecoration(operands[2], spv::DecorationBlockMatchTextureQCOM); + break; + case glslang::EOpImageBlockMatchSSDQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchSSDQCOM; + addImageProcessingQCOMDecoration(operands[0], spv::DecorationBlockMatchTextureQCOM); + addImageProcessingQCOMDecoration(operands[2], spv::DecorationBlockMatchTextureQCOM); + break; + + case glslang::EOpFetchMicroTriangleVertexBarycentricNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 2); + opCode = spv::OpFetchMicroTriangleVertexBarycentricNV; + break; + + case glslang::EOpFetchMicroTriangleVertexPositionNV: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpFetchMicroTriangleVertexPositionNV; + break; + + case glslang::EOpImageBlockMatchWindowSSDQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchWindowSSDQCOM; + addImageProcessing2QCOMDecoration(operands[0], false); + addImageProcessing2QCOMDecoration(operands[2], false); + break; + case glslang::EOpImageBlockMatchWindowSADQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchWindowSADQCOM; + addImageProcessing2QCOMDecoration(operands[0], false); + addImageProcessing2QCOMDecoration(operands[2], false); + break; + case glslang::EOpImageBlockMatchGatherSSDQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchGatherSSDQCOM; + addImageProcessing2QCOMDecoration(operands[0], true); + addImageProcessing2QCOMDecoration(operands[2], true); + break; + case glslang::EOpImageBlockMatchGatherSADQCOM: + typeId = builder.makeVectorType(builder.makeFloatType(32), 4); + opCode = spv::OpImageBlockMatchGatherSADQCOM; + addImageProcessing2QCOMDecoration(operands[0], true); + addImageProcessing2QCOMDecoration(operands[2], true); + break; + case glslang::EOpCreateTensorLayoutNV: + return builder.createOp(spv::OpCreateTensorLayoutNV, typeId, std::vector{}); + case glslang::EOpCreateTensorViewNV: + return builder.createOp(spv::OpCreateTensorViewNV, typeId, std::vector{}); + case glslang::EOpTensorLayoutSetBlockSizeNV: + opCode = spv::OpTensorLayoutSetBlockSizeNV; + break; + case glslang::EOpTensorLayoutSetDimensionNV: + opCode = spv::OpTensorLayoutSetDimensionNV; + break; + case glslang::EOpTensorLayoutSetStrideNV: + opCode = spv::OpTensorLayoutSetStrideNV; + break; + case glslang::EOpTensorLayoutSliceNV: + opCode = spv::OpTensorLayoutSliceNV; + break; + case glslang::EOpTensorLayoutSetClampValueNV: + opCode = spv::OpTensorLayoutSetClampValueNV; + break; + case glslang::EOpTensorViewSetDimensionNV: + opCode = spv::OpTensorViewSetDimensionNV; + break; + case glslang::EOpTensorViewSetStrideNV: + opCode = spv::OpTensorViewSetStrideNV; + break; + case glslang::EOpTensorViewSetClipNV: + opCode = spv::OpTensorViewSetClipNV; + break; + default: + return 0; + } + + spv::Id id = 0; + if (libCall >= 0) { + // Use an extended instruction from the standard library. + // Construct the call arguments, without modifying the original operands vector. + // We might need the remaining arguments, e.g. in the EOpFrexp case. + std::vector callArguments(operands.begin(), operands.begin() + consumedOperands); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments); + } else if (opCode == spv::OpDot && !isFloat) { + // int dot(int, int) + // NOTE: never called for scalar/vector1, this is turned into simple mul before this can be reached + const int componentCount = builder.getNumComponents(operands[0]); + spv::Id mulOp = builder.createBinOp(spv::OpIMul, builder.getTypeId(operands[0]), operands[0], operands[1]); + builder.setPrecision(mulOp, precision); + id = builder.createCompositeExtract(mulOp, typeId, 0); + for (int i = 1; i < componentCount; ++i) { + builder.setPrecision(id, precision); + id = builder.createBinOp(spv::OpIAdd, typeId, id, builder.createCompositeExtract(mulOp, typeId, i)); + } + } else { + switch (consumedOperands) { + case 0: + // should all be handled by visitAggregate and createNoArgOperation + assert(0); + return 0; + case 1: + // should all be handled by createUnaryOperation + assert(0); + return 0; + case 2: + id = builder.createBinOp(opCode, typeId, operands[0], operands[1]); + break; + default: + // anything 3 or over doesn't have l-value operands, so all should be consumed + assert(consumedOperands == operands.size()); + id = builder.createOp(opCode, typeId, operands); + break; + } + } + + // Decode the return types that were structures + switch (op) { + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + id = builder.createCompositeExtract(id, typeId0, 0); + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + builder.createStore(builder.createCompositeExtract(id, typeId0, 0), operands[3]); + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + break; + case glslang::EOpModf: + { + assert(operands.size() == 2); + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[1]); + id = builder.createCompositeExtract(id, typeId0, 0); + } + break; + case glslang::EOpFrexp: + { + assert(operands.size() == 2); + if (builder.isFloatType(builder.getScalarTypeId(typeId1))) { + // "exp" is floating-point type (from HLSL intrinsic) + spv::Id member1 = builder.createCompositeExtract(id, frexpIntType, 1); + member1 = builder.createUnaryOp(spv::OpConvertSToF, typeId1, member1); + builder.createStore(member1, operands[1]); + } else + // "exp" is integer type (from GLSL built-in function) + builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]); + id = builder.createCompositeExtract(id, typeId0, 0); + } + break; + default: + break; + } + + return builder.setPrecision(id, precision); +} + +// Intrinsics with no arguments (or no return value, and no precision). +spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId) +{ + // GLSL memory barriers use queuefamily scope in new model, device scope in old model + spv::Scope memoryBarrierScope = glslangIntermediate->usingVulkanMemoryModel() ? + spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + + switch (op) { + case glslang::EOpBarrier: + if (glslangIntermediate->getStage() == EShLangTessControl) { + if (glslangIntermediate->usingVulkanMemoryModel()) { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsAcquireReleaseMask); + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeInvocation, spv::MemorySemanticsMaskNone); + } + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + } + return 0; + case glslang::EOpMemoryBarrier: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierBuffer: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierShared: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpGroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierAtomicCounter: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAtomicCounterMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierImage: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpAllMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, + spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpSubgroupBarrier: + builder.createControlBarrier(spv::ScopeSubgroup, spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierBuffer: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierImage: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierShared: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + + case glslang::EOpEmitVertex: + builder.createNoResultOp(spv::OpEmitVertex); + return 0; + case glslang::EOpEndPrimitive: + builder.createNoResultOp(spv::OpEndPrimitive); + return 0; + + case glslang::EOpSubgroupElect: { + std::vector operands; + return createSubgroupOperation(op, typeId, operands, glslang::EbtVoid); + } + case glslang::EOpTime: + { + std::vector args; // Dummy arguments + spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args); + return builder.setPrecision(id, precision); + } + case glslang::EOpIgnoreIntersectionNV: + builder.createNoResultOp(spv::OpIgnoreIntersectionNV); + return 0; + case glslang::EOpTerminateRayNV: + builder.createNoResultOp(spv::OpTerminateRayNV); + return 0; + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR); + return 0; + case glslang::EOpBeginInvocationInterlock: + builder.createNoResultOp(spv::OpBeginInvocationInterlockEXT); + return 0; + case glslang::EOpEndInvocationInterlock: + builder.createNoResultOp(spv::OpEndInvocationInterlockEXT); + return 0; + + case glslang::EOpIsHelperInvocation: + { + std::vector args; // Dummy arguments + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + return builder.createOp(spv::OpIsHelperInvocationEXT, typeId, args); + } + + case glslang::EOpReadClockSubgroupKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } + + case glslang::EOpReadClockDeviceKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeDevice)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } + case glslang::EOpStencilAttachmentReadEXT: + case glslang::EOpDepthAttachmentReadEXT: + { + builder.addExtension(spv::E_SPV_EXT_shader_tile_image); + + spv::Decoration precision; + spv::Op spv_op; + if (op == glslang::EOpStencilAttachmentReadEXT) + { + precision = spv::DecorationRelaxedPrecision; + spv_op = spv::OpStencilAttachmentReadEXT; + builder.addCapability(spv::CapabilityTileImageStencilReadAccessEXT); + } + else + { + precision = spv::NoPrecision; + spv_op = spv::OpDepthAttachmentReadEXT; + builder.addCapability(spv::CapabilityTileImageDepthReadAccessEXT); + } + + std::vector args; // Dummy args + spv::Id result = builder.createOp(spv_op, typeId, args); + return builder.setPrecision(result, precision); + } + default: + break; + } + + logger->missingFunctionality("unknown operation with no arguments"); + + return 0; +} + +spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol) +{ + auto iter = symbolValues.find(symbol->getId()); + spv::Id id; + if (symbolValues.end() != iter) { + id = iter->second; + return id; + } + + // it was not found, create it + spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn, false); + auto forcedType = getForcedType(symbol->getQualifier().builtIn, symbol->getType()); + + // There are pairs of symbols that map to the same SPIR-V built-in: + // gl_ObjectToWorldEXT and gl_ObjectToWorld3x4EXT, and gl_WorldToObjectEXT + // and gl_WorldToObject3x4EXT. SPIR-V forbids having two OpVariables + // with the same BuiltIn in the same storage class, so we must re-use one. + const bool mayNeedToReuseBuiltIn = + builtIn == spv::BuiltInObjectToWorldKHR || + builtIn == spv::BuiltInWorldToObjectKHR; + + if (mayNeedToReuseBuiltIn) { + auto iter = builtInVariableIds.find(uint32_t(builtIn)); + if (builtInVariableIds.end() != iter) { + id = iter->second; + symbolValues[symbol->getId()] = id; + if (forcedType.second != spv::NoType) + forceType[id] = forcedType.second; + return id; + } + } + + if (symbol->getBasicType() == glslang::EbtFunction) { + return 0; + } + + id = createSpvVariable(symbol, forcedType.first); + + if (mayNeedToReuseBuiltIn) { + builtInVariableIds.insert({uint32_t(builtIn), id}); + } + + symbolValues[symbol->getId()] = id; + if (forcedType.second != spv::NoType) + forceType[id] = forcedType.second; + + if (symbol->getBasicType() != glslang::EbtBlock) { + builder.addDecoration(id, TranslatePrecisionDecoration(symbol->getType())); + builder.addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier())); + builder.addDecoration(id, TranslateAuxiliaryStorageDecoration(symbol->getType().getQualifier())); + addMeshNVDecoration(id, /*member*/ -1, symbol->getType().getQualifier()); + if (symbol->getQualifier().hasComponent()) + builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent); + if (symbol->getQualifier().hasIndex()) + builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex); + if (symbol->getType().getQualifier().hasSpecConstantId()) + builder.addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId); + // atomic counters use this: + if (symbol->getQualifier().hasOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutOffset); + } + + if (symbol->getQualifier().hasLocation()) { + if (!(glslangIntermediate->isRayTracingStage() && + (glslangIntermediate->IsRequestedExtension(glslang::E_GL_EXT_ray_tracing) || + glslangIntermediate->IsRequestedExtension(glslang::E_GL_NV_shader_invocation_reorder)) + && (builder.getStorageClass(id) == spv::StorageClassRayPayloadKHR || + builder.getStorageClass(id) == spv::StorageClassIncomingRayPayloadKHR || + builder.getStorageClass(id) == spv::StorageClassCallableDataKHR || + builder.getStorageClass(id) == spv::StorageClassIncomingCallableDataKHR || + builder.getStorageClass(id) == spv::StorageClassHitObjectAttributeNV))) { + // Location values are used to link TraceRayKHR/ExecuteCallableKHR/HitObjectGetAttributesNV + // to corresponding variables but are not valid in SPIRV since they are supported only + // for Input/Output Storage classes. + builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation); + } + } + + builder.addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier())); + if (symbol->getQualifier().hasStream() && glslangIntermediate->isMultiStream()) { + builder.addCapability(spv::CapabilityGeometryStreams); + builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream); + } + if (symbol->getQualifier().hasSet()) + builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationDescriptorSet, 0); + } + if (symbol->getQualifier().hasBinding()) + builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationBinding, 0); + } + if (symbol->getQualifier().hasAttachment()) + builder.addDecoration(id, spv::DecorationInputAttachmentIndex, symbol->getQualifier().layoutAttachment); + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + if (symbol->getQualifier().hasXfbBuffer()) { + builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + unsigned stride = glslangIntermediate->getXfbStride(symbol->getQualifier().layoutXfbBuffer); + if (stride != glslang::TQualifier::layoutXfbStrideEnd) + builder.addDecoration(id, spv::DecorationXfbStride, stride); + } + if (symbol->getQualifier().hasXfbOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset); + } + + // add built-in variable decoration + if (builtIn != spv::BuiltInMax) { + // WorkgroupSize deprecated in spirv1.6 + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_6 || + builtIn != spv::BuiltInWorkgroupSize) + builder.addDecoration(id, spv::DecorationBuiltIn, (int)builtIn); + } + + // Add volatile decoration to HelperInvocation for spirv1.6 and beyond + if (builtIn == spv::BuiltInHelperInvocation && + !glslangIntermediate->usingVulkanMemoryModel() && + glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_6) { + builder.addDecoration(id, spv::DecorationVolatile); + } + + // Subgroup builtins which have input storage class are volatile for ray tracing stages. + if (symbol->getType().isImage() || symbol->getQualifier().isPipeInput()) { + std::vector memory; + TranslateMemoryDecoration(symbol->getType().getQualifier(), memory, + glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addDecoration(id, memory[i]); + } + + if (builtIn == spv::BuiltInSampleMask) { + spv::Decoration decoration; + // GL_NV_sample_mask_override_coverage extension + if (glslangIntermediate->getLayoutOverrideCoverage()) + decoration = (spv::Decoration)spv::DecorationOverrideCoverageNV; + else + decoration = (spv::Decoration)spv::DecorationMax; + builder.addDecoration(id, decoration); + if (decoration != spv::DecorationMax) { + builder.addCapability(spv::CapabilitySampleMaskOverrideCoverageNV); + builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage); + } + } + else if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (symbol->getQualifier().layoutViewportRelative) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (symbol->getQualifier().layoutSecondaryViewportRelativeOffset != -2048) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + symbol->getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + + if (symbol->getQualifier().layoutPassthrough) { + builder.addDecoration(id, spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } + if (symbol->getQualifier().pervertexNV) { + builder.addDecoration(id, spv::DecorationPerVertexNV); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + } + + if (symbol->getQualifier().pervertexEXT) { + builder.addDecoration(id, spv::DecorationPerVertexKHR); + builder.addCapability(spv::CapabilityFragmentBarycentricKHR); + builder.addExtension(spv::E_SPV_KHR_fragment_shader_barycentric); + } + + if (glslangIntermediate->getHlslFunctionality1() && symbol->getType().getQualifier().semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecoration(id, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + symbol->getType().getQualifier().semanticName); + } + + if (symbol->isReference()) { + builder.addDecoration(id, symbol->getType().getQualifier().restrict ? + spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT); + } + + // Add SPIR-V decorations (GL_EXT_spirv_intrinsics) + if (symbol->getType().getQualifier().hasSpirvDecorate()) + applySpirvDecorate(symbol->getType(), id, {}); + + return id; +} + +// add per-primitive, per-view. per-task decorations to a struct member (member >= 0) or an object +void TGlslangToSpvTraverser::addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier& qualifier) +{ + bool isMeshShaderExt = (glslangIntermediate->getRequestedExtensions().find(glslang::E_GL_EXT_mesh_shader) != + glslangIntermediate->getRequestedExtensions().end()); + + if (member >= 0) { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + if(isMeshShaderExt) { + builder.addCapability(spv::CapabilityMeshShadingEXT); + builder.addExtension(spv::E_SPV_EXT_mesh_shader); + } else { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + } + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerTaskNV); + } else { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + if(isMeshShaderExt) { + builder.addCapability(spv::CapabilityMeshShadingEXT); + builder.addExtension(spv::E_SPV_EXT_mesh_shader); + } else { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + } + builder.addDecoration(id, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addDecoration(id, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addDecoration(id, spv::DecorationPerTaskNV); + } +} + +bool TGlslangToSpvTraverser::hasQCOMImageProceessingDecoration(spv::Id id, spv::Decoration decor) +{ + std::vector &decoVec = idToQCOMDecorations[id]; + for ( auto d : decoVec ) { + if ( d == decor ) + return true; + } + return false; +} + +void TGlslangToSpvTraverser::addImageProcessingQCOMDecoration(spv::Id id, spv::Decoration decor) +{ + spv::Op opc = builder.getOpCode(id); + if (opc == spv::OpSampledImage) { + id = builder.getIdOperand(id, 0); + opc = builder.getOpCode(id); + } + + if (opc == spv::OpLoad) { + spv::Id texid = builder.getIdOperand(id, 0); + if (!hasQCOMImageProceessingDecoration(texid, decor)) {// + builder.addDecoration(texid, decor); + idToQCOMDecorations[texid].push_back(decor); + } + } +} + +void TGlslangToSpvTraverser::addImageProcessing2QCOMDecoration(spv::Id id, bool isForGather) +{ + if (isForGather) { + return addImageProcessingQCOMDecoration(id, spv::DecorationBlockMatchTextureQCOM); + } + + auto addDecor = + [this](spv::Id id, spv::Decoration decor) { + spv::Id tsopc = this->builder.getOpCode(id); + if (tsopc == spv::OpLoad) { + spv::Id tsid = this->builder.getIdOperand(id, 0); + if (this->glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + assert(iOSet.count(tsid) > 0); + } + if (!hasQCOMImageProceessingDecoration(tsid, decor)) { + this->builder.addDecoration(tsid, decor); + idToQCOMDecorations[tsid].push_back(decor); + } + } + }; + + spv::Id opc = builder.getOpCode(id); + bool isInterfaceObject = (opc != spv::OpSampledImage); + + if (!isInterfaceObject) { + addDecor(builder.getIdOperand(id, 0), spv::DecorationBlockMatchTextureQCOM); + addDecor(builder.getIdOperand(id, 1), spv::DecorationBlockMatchSamplerQCOM); + } else { + addDecor(id, spv::DecorationBlockMatchTextureQCOM); + addDecor(id, spv::DecorationBlockMatchSamplerQCOM); + } +} + +// Make a full tree of instructions to build a SPIR-V specialization constant, +// or regular constant if possible. +// +// TBD: this is not yet done, nor verified to be the best design, it does do the leaf symbols though +// +// Recursively walk the nodes. The nodes form a tree whose leaves are +// regular constants, which themselves are trees that createSpvConstant() +// recursively walks. So, this function walks the "top" of the tree: +// - emit specialization constant-building instructions for specConstant +// - when running into a non-spec-constant, switch to createSpvConstant() +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node) +{ + assert(node.getQualifier().isConstant()); + + // Handle front-end constants first (non-specialization constants). + if (! node.getQualifier().specConstant) { + // hand off to the non-spec-constant path + assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr); + int nextConst = 0; + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? + node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + nextConst, false); + } + + // We now know we have a specialization constant to build + + // Extra capabilities may be needed. + if (node.getType().contains8BitInt()) + builder.addCapability(spv::CapabilityInt8); + if (node.getType().contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + if (node.getType().contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + if (node.getType().contains64BitInt()) + builder.addCapability(spv::CapabilityInt64); + if (node.getType().containsDouble()) + builder.addCapability(spv::CapabilityFloat64); + + // gl_WorkGroupSize is a special case until the front-end handles hierarchical specialization constants, + // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ... + if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) { + builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, + glslangIntermediate->getLocalSizeSpecId(dim)); + } + } + return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); + } + + // An AST node labelled as specialization constant should be a symbol node. + // Its initializer should either be a sub tree with constant nodes, or a constant union array. + if (auto* sn = node.getAsSymbolNode()) { + spv::Id result; + if (auto* sub_tree = sn->getConstSubtree()) { + // Traverse the constant constructor sub tree like generating normal run-time instructions. + // During the AST traversal, if the node is marked as 'specConstant', SpecConstantOpModeGuard + // will set the builder into spec constant op instruction generating mode. + sub_tree->traverse(this); + result = accessChainLoad(sub_tree->getType()); + } else if (auto* const_union_array = &sn->getConstArray()) { + int nextConst = 0; + result = createSpvConstantFromConstUnionArray(sn->getType(), *const_union_array, nextConst, true); + } else { + logger->missingFunctionality("Invalid initializer for spec onstant."); + return spv::NoResult; + } + builder.addName(result, sn->getName().c_str()); + return result; + } + + // Neither a front-end constant node, nor a specialization constant node with constant union array or + // constant sub tree as initializer. + logger->missingFunctionality("Neither a front-end constant nor a spec constant."); + return spv::NoResult; +} + +// Use 'consts' as the flattened glslang source of scalar constants to recursively +// build the aggregate SPIR-V constant. +// +// If there are not enough elements present in 'consts', 0 will be substituted; +// an empty 'consts' can be used to create a fully zeroed SPIR-V constant. +// +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, + const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) +{ + // vector of constants for SPIR-V + std::vector spvConsts; + + // Type is used for struct and array constants + spv::Id typeId = convertGlslangToSpvType(glslangType); + + if (glslangType.isArray()) { + glslang::TType elementType(glslangType, 0); + for (int i = 0; i < glslangType.getOuterArraySize(); ++i) + spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false)); + } else if (glslangType.isMatrix()) { + glslang::TType vectorType(glslangType, 0); + for (int col = 0; col < glslangType.getMatrixCols(); ++col) + spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false)); + } else if (glslangType.isCoopMat()) { + glslang::TType componentType(glslangType.getBasicType()); + spvConsts.push_back(createSpvConstantFromConstUnionArray(componentType, consts, nextConst, false)); + } else if (glslangType.isStruct()) { + glslang::TVector::const_iterator iter; + for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter) + spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false)); + } else if (glslangType.getVectorSize() > 1 || glslangType.isCoopVecNV()) { + unsigned int numComponents = glslangType.isCoopVecNV() ? glslangType.getTypeParameters()->arraySizes->getDimSize(0) : glslangType.getVectorSize(); + for (unsigned int i = 0; i < numComponents; ++i) { + bool zero = nextConst >= consts.size(); + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst())); + break; + case glslang::EbtUint: + spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst())); + break; + case glslang::EbtFloat: + spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + case glslang::EbtBool: + spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst())); + break; + case glslang::EbtInt8: + builder.addCapability(spv::CapabilityInt8); + spvConsts.push_back(builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const())); + break; + case glslang::EbtUint8: + builder.addCapability(spv::CapabilityInt8); + spvConsts.push_back(builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const())); + break; + case glslang::EbtInt16: + builder.addCapability(spv::CapabilityInt16); + spvConsts.push_back(builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const())); + break; + case glslang::EbtUint16: + builder.addCapability(spv::CapabilityInt16); + spvConsts.push_back(builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const())); + break; + case glslang::EbtInt64: + spvConsts.push_back(builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const())); + break; + case glslang::EbtUint64: + spvConsts.push_back(builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const())); + break; + case glslang::EbtDouble: + spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst())); + break; + case glslang::EbtFloat16: + builder.addCapability(spv::CapabilityFloat16); + spvConsts.push_back(builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + case glslang::EbtBFloat16: + spvConsts.push_back(builder.makeBFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + default: + assert(0); + break; + } + ++nextConst; + } + } else { + // we have a non-aggregate (scalar) constant + bool zero = nextConst >= consts.size(); + spv::Id scalar = 0; + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst(), specConstant); + break; + case glslang::EbtUint: + scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst(), specConstant); + break; + case glslang::EbtFloat: + scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtBool: + scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst(), specConstant); + break; + case glslang::EbtInt8: + builder.addCapability(spv::CapabilityInt8); + scalar = builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const(), specConstant); + break; + case glslang::EbtUint8: + builder.addCapability(spv::CapabilityInt8); + scalar = builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const(), specConstant); + break; + case glslang::EbtInt16: + builder.addCapability(spv::CapabilityInt16); + scalar = builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const(), specConstant); + break; + case glslang::EbtUint16: + builder.addCapability(spv::CapabilityInt16); + scalar = builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const(), specConstant); + break; + case glslang::EbtInt64: + scalar = builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const(), specConstant); + break; + case glslang::EbtUint64: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + break; + case glslang::EbtDouble: + scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtFloat16: + builder.addCapability(spv::CapabilityFloat16); + scalar = builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtBFloat16: + scalar = builder.makeBFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtReference: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + scalar = builder.createUnaryOp(spv::OpBitcast, typeId, scalar); + break; + case glslang::EbtString: + scalar = builder.getStringId(consts[nextConst].getSConst()->c_str()); + break; + default: + assert(0); + break; + } + ++nextConst; + return scalar; + } + + return builder.makeCompositeConstant(typeId, spvConsts); +} + +// Return true if the node is a constant or symbol whose reading has no +// non-trivial observable cost or effect. +bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node) +{ + // don't know what this is + if (node == nullptr) + return false; + + // a constant is safe + if (node->getAsConstantUnion() != nullptr) + return true; + + // not a symbol means non-trivial + if (node->getAsSymbolNode() == nullptr) + return false; + + // a symbol, depends on what's being read + switch (node->getType().getQualifier().storage) { + case glslang::EvqTemporary: + case glslang::EvqGlobal: + case glslang::EvqIn: + case glslang::EvqInOut: + case glslang::EvqConst: + case glslang::EvqConstReadOnly: + case glslang::EvqUniform: + return true; + default: + return false; + } +} + +// A node is trivial if it is a single operation with no side effects. +// HLSL (and/or vectors) are always trivial, as it does not short circuit. +// Otherwise, error on the side of saying non-trivial. +// Return true if trivial. +bool TGlslangToSpvTraverser::isTrivial(const glslang::TIntermTyped* node) +{ + if (node == nullptr) + return false; + + // count non scalars as trivial, as well as anything coming from HLSL + if (! node->getType().isScalarOrVec1() || glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return true; + + // symbols and constants are trivial + if (isTrivialLeaf(node)) + return true; + + // otherwise, it needs to be a simple operation or one or two leaf nodes + + // not a simple operation + const glslang::TIntermBinary* binaryNode = node->getAsBinaryNode(); + const glslang::TIntermUnary* unaryNode = node->getAsUnaryNode(); + if (binaryNode == nullptr && unaryNode == nullptr) + return false; + + // not on leaf nodes + if (binaryNode && (! isTrivialLeaf(binaryNode->getLeft()) || ! isTrivialLeaf(binaryNode->getRight()))) + return false; + + if (unaryNode && ! isTrivialLeaf(unaryNode->getOperand())) { + return false; + } + + if (IsOpNumericConv(node->getAsOperator()->getOp()) && + node->getType().getBasicType() == glslang::EbtBool) { + return true; + } + + switch (node->getAsOperator()->getOp()) { + case glslang::EOpLogicalNot: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpLogicalXor: + case glslang::EOpAny: + case glslang::EOpAll: + return true; + default: + return false; + } +} + +// Emit short-circuiting code, where 'right' is never evaluated unless +// the left side is true (for &&) or false (for ||). +spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslang::TIntermTyped& left, + glslang::TIntermTyped& right) +{ + spv::Id boolTypeId = builder.makeBoolType(); + + // emit left operand + builder.clearAccessChain(); + left.traverse(this); + spv::Id leftId = accessChainLoad(left.getType()); + + // Operands to accumulate OpPhi operands + std::vector phiOperands; + phiOperands.reserve(4); + // accumulate left operand's phi information + phiOperands.push_back(leftId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // Make the two kinds of operation symmetric with a "!" + // || => emit "if (! left) result = right" + // && => emit "if ( left) result = right" + // + // TODO: this runtime "not" for || could be avoided by adding functionality + // to 'builder' to have an "else" without an "then" + if (op == glslang::EOpLogicalOr) + leftId = builder.createUnaryOp(spv::OpLogicalNot, boolTypeId, leftId); + + // make an "if" based on the left value + spv::Builder::If ifBuilder(leftId, spv::SelectionControlMaskNone, builder); + + // emit right operand as the "then" part of the "if" + builder.clearAccessChain(); + right.traverse(this); + spv::Id rightId = accessChainLoad(right.getType()); + + // accumulate left operand's phi information + phiOperands.push_back(rightId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // finish the "if" + ifBuilder.makeEndIf(); + + // phi together the two results + return builder.createOp(spv::OpPhi, boolTypeId, phiOperands); +} + +// Return type Id of the imported set of extended instructions corresponds to the name. +// Import this set if it has not been imported yet. +spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name) +{ + if (extBuiltinMap.find(name) != extBuiltinMap.end()) + return extBuiltinMap[name]; + else { + spv::Id extBuiltins = builder.import(name); + extBuiltinMap[name] = extBuiltins; + return extBuiltins; + } +} + +} // end anonymous namespace + +namespace glslang { + +void GetSpirvVersion(std::string& version) +{ + const int bufSize = 100; + char buf[bufSize]; + snprintf(buf, bufSize, "0x%08x, Revision %d", spv::Version, spv::Revision); + version = buf; +} + +// For low-order part of the generator's magic number. Bump up +// when there is a change in the style (e.g., if SSA form changes, +// or a different instruction sequence to do something gets used). +int GetSpirvGeneratorVersion() +{ + // return 1; // start + // return 2; // EOpAtomicCounterDecrement gets a post decrement, to map between GLSL -> SPIR-V + // return 3; // change/correct barrier-instruction operands, to match memory model group decisions + // return 4; // some deeper access chains: for dynamic vector component, and local Boolean component + // return 5; // make OpArrayLength result type be an int with signedness of 0 + // return 6; // revert version 5 change, which makes a different (new) kind of incorrect code, + // versions 4 and 6 each generate OpArrayLength as it has long been done + // return 7; // GLSL volatile keyword maps to both SPIR-V decorations Volatile and Coherent + // return 8; // switch to new dead block eliminator; use OpUnreachable + // return 9; // don't include opaque function parameters in OpEntryPoint global's operand list + // return 10; // Generate OpFUnordNotEqual for != comparisons + return 11; // Make OpEmitMeshTasksEXT a terminal instruction +} + +// Write SPIR-V out to a binary file +bool OutputSpvBin(const std::vector& spirv, const char* baseName) +{ + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) { + printf("ERROR: Failed to open file: %s\n", baseName); + return false; + } + for (int i = 0; i < (int)spirv.size(); ++i) { + unsigned int word = spirv[i]; + out.write((const char*)&word, 4); + } + out.close(); + return true; +} + +// Write SPIR-V out to a text file with 32-bit hexadecimal words +bool OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName) +{ + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) { + printf("ERROR: Failed to open file: %s\n", baseName); + return false; + } + out << "\t// " << + GetSpirvGeneratorVersion() << + GLSLANG_VERSION_MAJOR << "." << GLSLANG_VERSION_MINOR << "." << GLSLANG_VERSION_PATCH << + GLSLANG_VERSION_FLAVOR << std::endl; + if (varName != nullptr) { + out << "\t #pragma once" << std::endl; + out << "const uint32_t " << varName << "[] = {" << std::endl; + } + const int WORDS_PER_LINE = 8; + for (int i = 0; i < (int)spirv.size(); i += WORDS_PER_LINE) { + out << "\t"; + for (int j = 0; j < WORDS_PER_LINE && i + j < (int)spirv.size(); ++j) { + const unsigned int word = spirv[i + j]; + out << "0x" << std::hex << std::setw(8) << std::setfill('0') << word; + if (i + j + 1 < (int)spirv.size()) { + out << ","; + } + } + out << std::endl; + } + if (varName != nullptr) { + out << "};"; + out << std::endl; + } + out.close(); + return true; +} + +// +// Set up the glslang traversal +// +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, SpvOptions* options) +{ + spv::SpvBuildLogger logger; + GlslangToSpv(intermediate, spirv, &logger, options); +} + +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options) +{ + TIntermNode* root = intermediate.getTreeRoot(); + + if (root == nullptr) + return; + + SpvOptions defaultOptions; + if (options == nullptr) + options = &defaultOptions; + + GetThreadPoolAllocator().push(); + + TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options); + root->traverse(&it); + it.finishSpv(options->compileOnly); + it.dumpSpv(spirv); + +#if ENABLE_OPT + // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan + // eg. forward and remove memory writes of opaque types. + bool prelegalization = intermediate.getSource() == EShSourceHlsl; + if ((prelegalization || options->optimizeSize) && !options->disableOptimizer) { + SpirvToolsTransform(intermediate, spirv, logger, options); + prelegalization = false; + } + else if (options->stripDebugInfo) { + // Strip debug info even if optimization is disabled. + SpirvToolsStripDebugInfo(intermediate, spirv, logger); + } + + if (options->validate) + SpirvToolsValidate(intermediate, spirv, logger, prelegalization); + + if (options->disassemble) + SpirvToolsDisassemble(std::cout, spirv); + +#endif + + GetThreadPoolAllocator().pop(); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.h b/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.h new file mode 100644 index 00000000..9fb4f3ff --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/GlslangToSpv.h @@ -0,0 +1,69 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +#include "Logger.h" +#include "glslang/Include/visibility.h" + +namespace glslang { +class TIntermediate; + +struct SpvOptions { + bool generateDebugInfo {false}; + bool stripDebugInfo {false}; + bool disableOptimizer {true}; + bool optimizeSize {false}; + bool disassemble {false}; + bool validate {false}; + bool emitNonSemanticShaderDebugInfo {false}; + bool emitNonSemanticShaderDebugSource{ false }; + bool compileOnly{false}; + bool optimizerAllowExpandedIDBound{false}; +}; + +GLSLANG_EXPORT void GetSpirvVersion(std::string&); +GLSLANG_EXPORT int GetSpirvGeneratorVersion(); +GLSLANG_EXPORT void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + SpvOptions* options = nullptr); +GLSLANG_EXPORT void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); +GLSLANG_EXPORT bool OutputSpvBin(const std::vector& spirv, const char* baseName); +GLSLANG_EXPORT bool OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName); + +} diff --git a/engine/engine/3rdlibs/glslang/SPIRV/InReadableOrder.cpp b/engine/engine/3rdlibs/glslang/SPIRV/InReadableOrder.cpp new file mode 100644 index 00000000..9d9410be --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/InReadableOrder.cpp @@ -0,0 +1,131 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// The SPIR-V spec requires code blocks to appear in an order satisfying the +// dominator-tree direction (ie, dominator before the dominated). This is, +// actually, easy to achieve: any pre-order CFG traversal algorithm will do it. +// Because such algorithms visit a block only after traversing some path to it +// from the root, they necessarily visit the block's idom first. +// +// But not every graph-traversal algorithm outputs blocks in an order that +// appears logical to human readers. The problem is that unrelated branches may +// be interspersed with each other, and merge blocks may come before some of the +// branches being merged. +// +// A good, human-readable order of blocks may be achieved by performing +// depth-first search but delaying merge nodes until after all their branches +// have been visited. This is implemented below by the inReadableOrder() +// function. + +#include "spvIR.h" + +#include +#include + +using spv::Block; +using spv::Id; + +namespace { +// Traverses CFG in a readable order, invoking a pre-set callback on each block. +// Use by calling visit() on the root block. +class ReadableOrderTraverser { +public: + ReadableOrderTraverser(std::function callback) + : callback_(callback) {} + // Visits the block if it hasn't been visited already and isn't currently + // being delayed. Invokes callback(block, why, header), then descends into its + // successors. Delays merge-block and continue-block processing until all + // the branches have been completed. If |block| is an unreachable merge block or + // an unreachable continue target, then |header| is the corresponding header block. + void visit(Block* block, spv::ReachReason why, Block* header) + { + assert(block); + if (why == spv::ReachViaControlFlow) { + reachableViaControlFlow_.insert(block); + } + if (visited_.count(block) || delayed_.count(block)) + return; + callback_(block, why, header); + visited_.insert(block); + Block* mergeBlock = nullptr; + Block* continueBlock = nullptr; + auto mergeInst = block->getMergeInstruction(); + if (mergeInst) { + Id mergeId = mergeInst->getIdOperand(0); + mergeBlock = block->getParent().getParent().getInstruction(mergeId)->getBlock(); + delayed_.insert(mergeBlock); + if (mergeInst->getOpCode() == spv::OpLoopMerge) { + Id continueId = mergeInst->getIdOperand(1); + continueBlock = + block->getParent().getParent().getInstruction(continueId)->getBlock(); + delayed_.insert(continueBlock); + } + } + if (why == spv::ReachViaControlFlow) { + const auto& successors = block->getSuccessors(); + for (auto it = successors.cbegin(); it != successors.cend(); ++it) + visit(*it, why, nullptr); + } + if (continueBlock) { + const spv::ReachReason continueWhy = + (reachableViaControlFlow_.count(continueBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadContinue; + delayed_.erase(continueBlock); + visit(continueBlock, continueWhy, block); + } + if (mergeBlock) { + const spv::ReachReason mergeWhy = + (reachableViaControlFlow_.count(mergeBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadMerge; + delayed_.erase(mergeBlock); + visit(mergeBlock, mergeWhy, block); + } + } + +private: + std::function callback_; + // Whether a block has already been visited or is being delayed. + std::unordered_set visited_, delayed_; + + // The set of blocks that actually are reached via control flow. + std::unordered_set reachableViaControlFlow_; +}; +} + +void spv::inReadableOrder(Block* root, std::function callback) +{ + ReadableOrderTraverser(callback).visit(root, spv::ReachViaControlFlow, nullptr); +} diff --git a/engine/engine/3rdlibs/glslang/SPIRV/Logger.cpp b/engine/engine/3rdlibs/glslang/SPIRV/Logger.cpp new file mode 100644 index 00000000..48bd4e3a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/Logger.cpp @@ -0,0 +1,68 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "Logger.h" + +#include +#include +#include + +namespace spv { + +void SpvBuildLogger::tbdFunctionality(const std::string& f) +{ + if (std::find(std::begin(tbdFeatures), std::end(tbdFeatures), f) == std::end(tbdFeatures)) + tbdFeatures.push_back(f); +} + +void SpvBuildLogger::missingFunctionality(const std::string& f) +{ + if (std::find(std::begin(missingFeatures), std::end(missingFeatures), f) == std::end(missingFeatures)) + missingFeatures.push_back(f); +} + +std::string SpvBuildLogger::getAllMessages() const { + std::ostringstream messages; + for (auto it = tbdFeatures.cbegin(); it != tbdFeatures.cend(); ++it) + messages << "TBD functionality: " << *it << "\n"; + for (auto it = missingFeatures.cbegin(); it != missingFeatures.cend(); ++it) + messages << "Missing functionality: " << *it << "\n"; + for (auto it = warnings.cbegin(); it != warnings.cend(); ++it) + messages << "warning: " << *it << "\n"; + for (auto it = errors.cbegin(); it != errors.cend(); ++it) + messages << "error: " << *it << "\n"; + return messages.str(); +} + +} // end spv namespace diff --git a/engine/engine/3rdlibs/glslang/SPIRV/Logger.h b/engine/engine/3rdlibs/glslang/SPIRV/Logger.h new file mode 100644 index 00000000..c64a3b8a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/Logger.h @@ -0,0 +1,75 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_SPIRV_LOGGER_H +#define GLSLANG_SPIRV_LOGGER_H + +#include +#include +#include "glslang/Include/visibility.h" + +namespace spv { + +// A class for holding all SPIR-V build status messages, including +// missing/TBD functionalities, warnings, and errors. +class GLSLANG_EXPORT SpvBuildLogger { +public: + SpvBuildLogger() {} + + // Registers a TBD functionality. + void tbdFunctionality(const std::string& f); + // Registers a missing functionality. + void missingFunctionality(const std::string& f); + + // Logs a warning. + void warning(const std::string& w) { warnings.push_back(w); } + // Logs an error. + void error(const std::string& e) { errors.push_back(e); } + + // Returns all messages accumulated in the order of: + // TBD functionalities, missing functionalities, warnings, errors. + std::string getAllMessages() const; + +private: + SpvBuildLogger(const SpvBuildLogger&); + + std::vector tbdFeatures; + std::vector missingFeatures; + std::vector warnings; + std::vector errors; +}; + +} // end spv namespace + +#endif // GLSLANG_SPIRV_LOGGER_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticDebugPrintf.h b/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticDebugPrintf.h new file mode 100644 index 00000000..3ca7247f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticDebugPrintf.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugPrintfRevision = 1, + NonSemanticDebugPrintfRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugPrintfInstructions { + NonSemanticDebugPrintfDebugPrintf = 1, + NonSemanticDebugPrintfInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ diff --git a/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticShaderDebugInfo100.h b/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticShaderDebugInfo100.h new file mode 100644 index 00000000..f74abcb6 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/NonSemanticShaderDebugInfo100.h @@ -0,0 +1,171 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ +#define SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticShaderDebugInfo100Version = 100, + NonSemanticShaderDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + NonSemanticShaderDebugInfo100Revision = 6, + NonSemanticShaderDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100Instructions { + NonSemanticShaderDebugInfo100DebugInfoNone = 0, + NonSemanticShaderDebugInfo100DebugCompilationUnit = 1, + NonSemanticShaderDebugInfo100DebugTypeBasic = 2, + NonSemanticShaderDebugInfo100DebugTypePointer = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifier = 4, + NonSemanticShaderDebugInfo100DebugTypeArray = 5, + NonSemanticShaderDebugInfo100DebugTypeVector = 6, + NonSemanticShaderDebugInfo100DebugTypedef = 7, + NonSemanticShaderDebugInfo100DebugTypeFunction = 8, + NonSemanticShaderDebugInfo100DebugTypeEnum = 9, + NonSemanticShaderDebugInfo100DebugTypeComposite = 10, + NonSemanticShaderDebugInfo100DebugTypeMember = 11, + NonSemanticShaderDebugInfo100DebugTypeInheritance = 12, + NonSemanticShaderDebugInfo100DebugTypePtrToMember = 13, + NonSemanticShaderDebugInfo100DebugTypeTemplate = 14, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameter = 15, + NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter = 16, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack = 17, + NonSemanticShaderDebugInfo100DebugGlobalVariable = 18, + NonSemanticShaderDebugInfo100DebugFunctionDeclaration = 19, + NonSemanticShaderDebugInfo100DebugFunction = 20, + NonSemanticShaderDebugInfo100DebugLexicalBlock = 21, + NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator = 22, + NonSemanticShaderDebugInfo100DebugScope = 23, + NonSemanticShaderDebugInfo100DebugNoScope = 24, + NonSemanticShaderDebugInfo100DebugInlinedAt = 25, + NonSemanticShaderDebugInfo100DebugLocalVariable = 26, + NonSemanticShaderDebugInfo100DebugInlinedVariable = 27, + NonSemanticShaderDebugInfo100DebugDeclare = 28, + NonSemanticShaderDebugInfo100DebugValue = 29, + NonSemanticShaderDebugInfo100DebugOperation = 30, + NonSemanticShaderDebugInfo100DebugExpression = 31, + NonSemanticShaderDebugInfo100DebugMacroDef = 32, + NonSemanticShaderDebugInfo100DebugMacroUndef = 33, + NonSemanticShaderDebugInfo100DebugImportedEntity = 34, + NonSemanticShaderDebugInfo100DebugSource = 35, + NonSemanticShaderDebugInfo100DebugFunctionDefinition = 101, + NonSemanticShaderDebugInfo100DebugSourceContinued = 102, + NonSemanticShaderDebugInfo100DebugLine = 103, + NonSemanticShaderDebugInfo100DebugNoLine = 104, + NonSemanticShaderDebugInfo100DebugBuildIdentifier = 105, + NonSemanticShaderDebugInfo100DebugStoragePath = 106, + NonSemanticShaderDebugInfo100DebugEntryPoint = 107, + NonSemanticShaderDebugInfo100DebugTypeMatrix = 108, + NonSemanticShaderDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum NonSemanticShaderDebugInfo100DebugInfoFlags { + NonSemanticShaderDebugInfo100None = 0x0000, + NonSemanticShaderDebugInfo100FlagIsProtected = 0x01, + NonSemanticShaderDebugInfo100FlagIsPrivate = 0x02, + NonSemanticShaderDebugInfo100FlagIsPublic = 0x03, + NonSemanticShaderDebugInfo100FlagIsLocal = 0x04, + NonSemanticShaderDebugInfo100FlagIsDefinition = 0x08, + NonSemanticShaderDebugInfo100FlagFwdDecl = 0x10, + NonSemanticShaderDebugInfo100FlagArtificial = 0x20, + NonSemanticShaderDebugInfo100FlagExplicit = 0x40, + NonSemanticShaderDebugInfo100FlagPrototyped = 0x80, + NonSemanticShaderDebugInfo100FlagObjectPointer = 0x100, + NonSemanticShaderDebugInfo100FlagStaticMember = 0x200, + NonSemanticShaderDebugInfo100FlagIndirectVariable = 0x400, + NonSemanticShaderDebugInfo100FlagLValueReference = 0x800, + NonSemanticShaderDebugInfo100FlagRValueReference = 0x1000, + NonSemanticShaderDebugInfo100FlagIsOptimized = 0x2000, + NonSemanticShaderDebugInfo100FlagIsEnumClass = 0x4000, + NonSemanticShaderDebugInfo100FlagTypePassByValue = 0x8000, + NonSemanticShaderDebugInfo100FlagTypePassByReference = 0x10000, + NonSemanticShaderDebugInfo100FlagUnknownPhysicalLayout = 0x20000, + NonSemanticShaderDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100BuildIdentifierFlags { + NonSemanticShaderDebugInfo100IdentifierPossibleDuplicates = 0x01, + NonSemanticShaderDebugInfo100BuildIdentifierFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncoding { + NonSemanticShaderDebugInfo100Unspecified = 0, + NonSemanticShaderDebugInfo100Address = 1, + NonSemanticShaderDebugInfo100Boolean = 2, + NonSemanticShaderDebugInfo100Float = 3, + NonSemanticShaderDebugInfo100Signed = 4, + NonSemanticShaderDebugInfo100SignedChar = 5, + NonSemanticShaderDebugInfo100Unsigned = 6, + NonSemanticShaderDebugInfo100UnsignedChar = 7, + NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugCompositeType { + NonSemanticShaderDebugInfo100Class = 0, + NonSemanticShaderDebugInfo100Structure = 1, + NonSemanticShaderDebugInfo100Union = 2, + NonSemanticShaderDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugTypeQualifier { + NonSemanticShaderDebugInfo100ConstType = 0, + NonSemanticShaderDebugInfo100VolatileType = 1, + NonSemanticShaderDebugInfo100RestrictType = 2, + NonSemanticShaderDebugInfo100AtomicType = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugOperation { + NonSemanticShaderDebugInfo100Deref = 0, + NonSemanticShaderDebugInfo100Plus = 1, + NonSemanticShaderDebugInfo100Minus = 2, + NonSemanticShaderDebugInfo100PlusUconst = 3, + NonSemanticShaderDebugInfo100BitPiece = 4, + NonSemanticShaderDebugInfo100Swap = 5, + NonSemanticShaderDebugInfo100Xderef = 6, + NonSemanticShaderDebugInfo100StackValue = 7, + NonSemanticShaderDebugInfo100Constu = 8, + NonSemanticShaderDebugInfo100Fragment = 9, + NonSemanticShaderDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugImportedEntity { + NonSemanticShaderDebugInfo100ImportedModule = 0, + NonSemanticShaderDebugInfo100ImportedDeclaration = 1, + NonSemanticShaderDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.cpp b/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.cpp new file mode 100644 index 00000000..40a2924c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.cpp @@ -0,0 +1,1562 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "SPVRemapper.h" +#include "doc.h" + +#include +#include + +namespace spv { + + // By default, just abort on error. Can be overridden via RegisterErrorHandler + spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); }; + // By default, eat log messages. Can be overridden via RegisterLogHandler + spirvbin_t::logfn_t spirvbin_t::logHandler = [](const std::string&) { }; + + // This can be overridden to provide other message behavior if needed + void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const + { + if (verbose >= minVerbosity) + logHandler(std::string(indent, ' ') + txt); + } + + // hash opcode, with special handling for OpExtInst + std::uint32_t spirvbin_t::asOpCodeHash(unsigned word) + { + const spv::Op opCode = asOpCode(word); + + std::uint32_t offset = 0; + + switch (opCode) { + case spv::OpExtInst: + offset += asId(word + 4); break; + default: + break; + } + + return opCode * 19 + offset; // 19 = small prime + } + + spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeFloat: // fall through... + case spv::OpTypePointer: return range_t(2, 3); + case spv::OpTypeInt: return range_t(2, 4); + // TODO: case spv::OpTypeImage: + // TODO: case spv::OpTypeSampledImage: + case spv::OpTypeSampler: return range_t(3, 8); + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypePipe: return range_t(3, 4); + case spv::OpConstant: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + if (isConstOp(opCode)) + return range_t(1, 2); + + switch (opCode) { + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypeSampler: // ... + case spv::OpTypeArray: // ... + case spv::OpTypeRuntimeArray: // ... + case spv::OpTypePipe: return range_t(2, 3); + case spv::OpTypeStruct: // fall through + case spv::OpTypeFunction: return range_t(2, maxCount); + case spv::OpTypePointer: return range_t(3, 4); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeArray: // fall through... + case spv::OpTypeRuntimeArray: return range_t(3, 4); + case spv::OpConstantComposite: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + // Return the size of a type in 32-bit words. This currently only + // handles ints and floats, and is only invoked by queries which must be + // integer types. If ever needed, it can be generalized. + unsigned spirvbin_t::typeSizeInWords(spv::Id id) const + { + const unsigned typeStart = idPos(id); + const spv::Op opCode = asOpCode(typeStart); + + if (errorLatch) + return 0; + + switch (opCode) { + case spv::OpTypeInt: // fall through... + case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32; + default: + return 0; + } + } + + // Looks up the type of a given const or variable ID, and + // returns its size in 32-bit words. + unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const + { + const auto tid_it = idTypeSizeMap.find(id); + if (tid_it == idTypeSizeMap.end()) { + error("type size for ID not found"); + return 0; + } + + return tid_it->second; + } + + // Is this an opcode we should remove when using --strip? + bool spirvbin_t::isStripOp(spv::Op opCode, unsigned start) const + { + switch (opCode) { + case spv::OpSource: + case spv::OpSourceExtension: + case spv::OpName: + case spv::OpMemberName: + case spv::OpLine : + { + const std::string name = literalString(start + 2); + + std::vector::const_iterator it; + for (it = stripWhiteList.begin(); it < stripWhiteList.end(); it++) + { + if (name.find(*it) != std::string::npos) { + return false; + } + } + + return true; + } + default : + return false; + } + } + + // Return true if this opcode is flow control + bool spirvbin_t::isFlowCtrl(spv::Op opCode) const + { + switch (opCode) { + case spv::OpBranchConditional: + case spv::OpBranch: + case spv::OpSwitch: + case spv::OpLoopMerge: + case spv::OpSelectionMerge: + case spv::OpLabel: + case spv::OpFunction: + case spv::OpFunctionEnd: return true; + default: return false; + } + } + + // Return true if this opcode defines a type + bool spirvbin_t::isTypeOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpTypeVoid: + case spv::OpTypeBool: + case spv::OpTypeInt: + case spv::OpTypeFloat: + case spv::OpTypeVector: + case spv::OpTypeMatrix: + case spv::OpTypeImage: + case spv::OpTypeSampler: + case spv::OpTypeArray: + case spv::OpTypeRuntimeArray: + case spv::OpTypeStruct: + case spv::OpTypeOpaque: + case spv::OpTypePointer: + case spv::OpTypeFunction: + case spv::OpTypeEvent: + case spv::OpTypeDeviceEvent: + case spv::OpTypeReserveId: + case spv::OpTypeQueue: + case spv::OpTypeSampledImage: + case spv::OpTypePipe: return true; + default: return false; + } + } + + // Return true if this opcode defines a constant + bool spirvbin_t::isConstOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpConstantSampler: + error("unimplemented constant type"); + return true; + + case spv::OpConstantNull: + case spv::OpConstantTrue: + case spv::OpConstantFalse: + case spv::OpConstantComposite: + case spv::OpConstant: + return true; + + default: + return false; + } + } + + const auto inst_fn_nop = [](spv::Op, unsigned) { return false; }; + const auto op_fn_nop = [](spv::Id&) { }; + + // g++ doesn't like these defined in the class proper in an anonymous namespace. + // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why. + // Defining them externally seems to please both compilers, so, here they are. + const spv::Id spirvbin_t::unmapped = spv::Id(-10000); + const spv::Id spirvbin_t::unused = spv::Id(-10001); + const int spirvbin_t::header_size = 5; + + spv::Id spirvbin_t::nextUnusedId(spv::Id id) + { + while (isNewIdMapped(id)) // search for an unused ID + ++id; + + return id; + } + + spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId) + { + //assert(id != spv::NoResult && newId != spv::NoResult); + + if (id > bound()) { + error(std::string("ID out of range: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (id >= idMapL.size()) + idMapL.resize(id+1, unused); + + if (newId != unmapped && newId != unused) { + if (isOldIdUnused(id)) { + error(std::string("ID unused in module: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (!isOldIdUnmapped(id)) { + error(std::string("ID already mapped: ") + std::to_string(id) + " -> " + + std::to_string(localId(id))); + + return spirvbin_t::unused; + } + + if (isNewIdMapped(newId)) { + error(std::string("ID already used in module: ") + std::to_string(newId)); + return spirvbin_t::unused; + } + + msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId)); + setMapped(newId); + largestNewId = std::max(largestNewId, newId); + } + + return idMapL[id] = newId; + } + + // Parse a literal string from the SPIR binary and return it as an std::string + // Due to C++11 RValue references, this doesn't copy the result string. + std::string spirvbin_t::literalString(unsigned word) const + { + std::string literal; + const spirword_t * pos = spv.data() + word; + + literal.reserve(16); + + do { + spirword_t word = *pos; + for (int i = 0; i < 4; i++) { + char c = word & 0xff; + if (c == '\0') + return literal; + literal += c; + word >>= 8; + } + pos++; + } while (true); + } + + void spirvbin_t::applyMap() + { + msg(3, 2, std::string("Applying map: ")); + + // Map local IDs through the ID map + process(inst_fn_nop, // ignore instructions + [this](spv::Id& id) { + id = localId(id); + + if (errorLatch) + return; + + assert(id != unused && id != unmapped); + } + ); + } + + // Find free IDs for anything we haven't mapped + void spirvbin_t::mapRemainder() + { + msg(3, 2, std::string("Remapping remainder: ")); + + spv::Id unusedId = 1; // can't use 0: that's NoResult + spirword_t maxBound = 0; + + for (spv::Id id = 0; id < idMapL.size(); ++id) { + if (isOldIdUnused(id)) + continue; + + // Find a new mapping for any used but unmapped IDs + if (isOldIdUnmapped(id)) { + localId(id, unusedId = nextUnusedId(unusedId)); + if (errorLatch) + return; + } + + if (isOldIdUnmapped(id)) { + error(std::string("old ID not mapped: ") + std::to_string(id)); + return; + } + + // Track max bound + maxBound = std::max(maxBound, localId(id) + 1); + + if (errorLatch) + return; + } + + bound(maxBound); // reset header ID bound to as big as it now needs to be + } + + // Mark debug instructions for stripping + void spirvbin_t::stripDebug() + { + // Strip instructions in the stripOp set: debug info. + process( + [&](spv::Op opCode, unsigned start) { + // remember opcodes we want to strip later + if (isStripOp(opCode, start)) + stripInst(start); + return true; + }, + op_fn_nop); + } + + // Mark instructions that refer to now-removed IDs for stripping + void spirvbin_t::stripDeadRefs() + { + process( + [&](spv::Op opCode, unsigned start) { + // strip opcodes pointing to removed data + switch (opCode) { + case spv::OpName: + case spv::OpMemberName: + case spv::OpDecorate: + case spv::OpMemberDecorate: + if (idPosR.find(asId(start+1)) == idPosR.end()) + stripInst(start); + break; + default: + break; // leave it alone + } + + return true; + }, + op_fn_nop); + + strip(); + } + + // Update local maps of ID, type, etc positions + void spirvbin_t::buildLocalMaps() + { + msg(2, 2, std::string("build local maps: ")); + + mapped.clear(); + idMapL.clear(); +// preserve nameMap, so we don't clear that. + fnPos.clear(); + fnCalls.clear(); + typeConstPos.clear(); + idPosR.clear(); + entryPoint = spv::NoResult; + largestNewId = 0; + + idMapL.resize(bound(), unused); + + int fnStart = 0; + spv::Id fnRes = spv::NoResult; + + // build local Id and name maps + process( + [&](spv::Op opCode, unsigned start) { + unsigned word = start+1; + spv::Id typeId = spv::NoResult; + + if (spv::InstructionDesc[opCode].hasType()) + typeId = asId(word++); + + // If there's a result ID, remember the size of its type + if (spv::InstructionDesc[opCode].hasResult()) { + const spv::Id resultId = asId(word++); + idPosR[resultId] = start; + + if (typeId != spv::NoResult) { + const unsigned idTypeSize = typeSizeInWords(typeId); + + if (errorLatch) + return false; + + if (idTypeSize != 0) + idTypeSizeMap[resultId] = idTypeSize; + } + } + + if (opCode == spv::Op::OpName) { + const spv::Id target = asId(start+1); + const std::string name = literalString(start+2); + nameMap[name] = target; + + } else if (opCode == spv::Op::OpFunctionCall) { + ++fnCalls[asId(start + 3)]; + } else if (opCode == spv::Op::OpEntryPoint) { + entryPoint = asId(start + 2); + } else if (opCode == spv::Op::OpFunction) { + if (fnStart != 0) { + error("nested function found"); + return false; + } + + fnStart = start; + fnRes = asId(start + 2); + } else if (opCode == spv::Op::OpFunctionEnd) { + assert(fnRes != spv::NoResult); + if (fnStart == 0) { + error("function end without function start"); + return false; + } + + fnPos[fnRes] = range_t(fnStart, start + asWordCount(start)); + fnStart = 0; + } else if (isConstOp(opCode)) { + if (errorLatch) + return false; + + assert(asId(start + 2) != spv::NoResult); + typeConstPos.insert(start); + } else if (isTypeOp(opCode)) { + assert(asId(start + 1) != spv::NoResult); + typeConstPos.insert(start); + } + + return false; + }, + + [this](spv::Id& id) { localId(id, unmapped); } + ); + } + + // Validate the SPIR header + void spirvbin_t::validate() const + { + msg(2, 2, std::string("validating: ")); + + if (spv.size() < header_size) { + error("file too short: "); + return; + } + + if (magic() != spv::MagicNumber) { + error("bad magic number"); + return; + } + + // field 1 = version + // field 2 = generator magic + // field 3 = result bound + + if (schemaNum() != 0) { + error("bad schema, must be 0"); + return; + } + } + + int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn) + { + const auto instructionStart = word; + const unsigned wordCount = asWordCount(instructionStart); + const int nextInst = word++ + wordCount; + spv::Op opCode = asOpCode(instructionStart); + + if (nextInst > int(spv.size())) { + error("spir instruction terminated too early"); + return -1; + } + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + if (instFn(opCode, instructionStart)) + return nextInst; + + // Read type and result ID from instruction desc table + if (spv::InstructionDesc[opCode].hasType()) { + idFn(asId(word++)); + --numOperands; + } + + if (spv::InstructionDesc[opCode].hasResult()) { + idFn(asId(word++)); + --numOperands; + } + + // Extended instructions: currently, assume everything is an ID. + // TODO: add whatever data we need for exceptions to that + if (opCode == spv::OpExtInst) { + + idFn(asId(word)); // Instruction set is an ID that also needs to be mapped + + word += 2; // instruction set, and instruction from set + numOperands -= 2; + + for (unsigned op=0; op < numOperands; ++op) + idFn(asId(word++)); // ID + + return nextInst; + } + + // Circular buffer so we can look back at previous unmapped values during the mapping pass. + static const unsigned idBufferSize = 4; + spv::Id idBuffer[idBufferSize]; + unsigned idBufferPos = 0; + + // Store IDs from instruction in our map + for (int op = 0; numOperands > 0; ++op, --numOperands) { + // SpecConstantOp is special: it includes the operands of another opcode which is + // given as a literal in the 3rd word. We will switch over to pretending that the + // opcode being processed is the literal opcode value of the SpecConstantOp. See the + // SPIRV spec for details. This way we will handle IDs and literals as appropriate for + // the embedded op. + if (opCode == spv::OpSpecConstantOp) { + if (op == 0) { + opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp. + --numOperands; + } + } + + switch (spv::InstructionDesc[opCode].operands.getClass(op)) { + case spv::OperandId: + case spv::OperandScope: + case spv::OperandMemorySemantics: + idBuffer[idBufferPos] = asId(word); + idBufferPos = (idBufferPos + 1) % idBufferSize; + idFn(asId(word++)); + break; + + case spv::OperandVariableIds: + for (unsigned i = 0; i < numOperands; ++i) + idFn(asId(word++)); + return nextInst; + + case spv::OperandVariableLiterals: + // for clarity + // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) { + // ++word; + // --numOperands; + // } + // word += numOperands; + return nextInst; + + case spv::OperandVariableLiteralId: { + if (opCode == OpSwitch) { + // word-2 is the position of the selector ID. OpSwitch Literals match its type. + // In case the IDs are currently being remapped, we get the word[-2] ID from + // the circular idBuffer. + const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize; + const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]); + const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize); + + if (errorLatch) + return -1; + + for (unsigned arg=0; arg instPos; + instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed. + + // Build local table of instruction start positions + process( + [&](spv::Op, unsigned start) { instPos.push_back(start); return true; }, + op_fn_nop); + + if (errorLatch) + return; + + // Window size for context-sensitive canonicalization values + // Empirical best size from a single data set. TODO: Would be a good tunable. + // We essentially perform a little convolution around each instruction, + // to capture the flavor of nearby code, to hopefully match to similar + // code in other modules. + static const unsigned windowSize = 2; + + for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) { + const unsigned start = instPos[entry]; + const spv::Op opCode = asOpCode(start); + + if (opCode == spv::OpFunction) + fnId = asId(start + 2); + + if (opCode == spv::OpFunctionEnd) + fnId = spv::NoResult; + + if (fnId != spv::NoResult) { // if inside a function + if (spv::InstructionDesc[opCode].hasResult()) { + const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1); + const spv::Id resId = asId(word); + std::uint32_t hashval = fnId * 17; // small prime + + for (unsigned i = entry-1; i >= entry-windowSize; --i) { + if (asOpCode(instPos[i]) == spv::OpFunction) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + for (unsigned i = entry; i <= entry + windowSize; ++i) { + if (asOpCode(instPos[i]) == spv::OpFunctionEnd) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + + } + } + } + + spv::Op thisOpCode(spv::OpNop); + std::unordered_map opCounter; + int idCounter(0); + fnId = spv::NoResult; + + process( + [&](spv::Op opCode, unsigned start) { + switch (opCode) { + case spv::OpFunction: + // Reset counters at each function + idCounter = 0; + opCounter.clear(); + fnId = asId(start + 2); + break; + + case spv::OpImageSampleImplicitLod: + case spv::OpImageSampleExplicitLod: + case spv::OpImageSampleDrefImplicitLod: + case spv::OpImageSampleDrefExplicitLod: + case spv::OpImageSampleProjImplicitLod: + case spv::OpImageSampleProjExplicitLod: + case spv::OpImageSampleProjDrefImplicitLod: + case spv::OpImageSampleProjDrefExplicitLod: + case spv::OpDot: + case spv::OpCompositeExtract: + case spv::OpCompositeInsert: + case spv::OpVectorShuffle: + case spv::OpLabel: + case spv::OpVariable: + + case spv::OpAccessChain: + case spv::OpLoad: + case spv::OpStore: + case spv::OpCompositeConstruct: + case spv::OpFunctionCall: + ++opCounter[opCode]; + idCounter = 0; + thisOpCode = opCode; + break; + default: + thisOpCode = spv::OpNop; + } + + return false; + }, + + [&](spv::Id& id) { + if (thisOpCode != spv::OpNop) { + ++idCounter; + const std::uint32_t hashval = + // Explicitly cast operands to unsigned int to avoid integer + // promotion to signed int followed by integer overflow, + // which would result in undefined behavior. + static_cast(opCounter[thisOpCode]) + * thisOpCode + * 50047 + + idCounter + + static_cast(fnId) * 117; + + if (isOldIdUnmapped(id)) + localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + } + }); + } + + // EXPERIMENTAL: forward IO and uniform load/stores into operands + // This produces invalid Schema-0 SPIRV + void spirvbin_t::forwardLoadStores() + { + idset_t fnLocalVars; // set of function local vars + idmap_t idMap; // Map of load result IDs to what they load + + // EXPERIMENTAL: Forward input and access chain loads into consumptions + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassUniform || + spv[start+3] == spv::StorageClassUniformConstant || + spv[start+3] == spv::StorageClassInput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + idMap[asId(start+2)] = asId(start+3); + stripInst(start); + } + + return false; + }, + + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + // EXPERIMENTAL: Implicit output stores + fnLocalVars.clear(); + idMap.clear(); + + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassOutput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + idMap[asId(start+2)] = asId(start+1); + stripInst(start); + } + + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + process( + inst_fn_nop, + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // optimize loads and stores + void spirvbin_t::optLoadStore() + { + idset_t fnLocalVars; // candidates for removal (only locals) + idmap_t idMap; // Map of load result IDs to what they load + blockmap_t blockMap; // Map of IDs to blocks they first appear in + int blockNum = 0; // block count, to avoid crossing flow control + + // Find all the function local pointers stored at most once, and not via access chains + process( + [&](spv::Op opCode, unsigned start) { + const int wordCount = asWordCount(start); + + // Count blocks, so we can avoid crossing flow control + if (isFlowCtrl(opCode)) + ++blockNum; + + // Add local variables to the map + if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) { + fnLocalVars.insert(asId(start+2)); + return true; + } + + // Ignore process vars referenced via access chain + if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + return true; + } + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + const spv::Id varId = asId(start+3); + + // Avoid loads before stores + if (idMap.find(varId) == idMap.end()) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + const spv::Id varId = asId(start+1); + + if (idMap.find(varId) == idMap.end()) { + idMap[varId] = asId(start+2); + } else { + // Remove if it has more than one store to the same pointer + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + return false; + }, + + // If local var id used anywhere else, don't eliminate + [&](spv::Id& id) { + if (fnLocalVars.count(id) > 0) { + fnLocalVars.erase(id); + idMap.erase(id); + } + } + ); + + if (errorLatch) + return; + + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) + idMap[asId(start+2)] = idMap[asId(start+3)]; + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + // Chase replacements to their origins, in case there is a chain such as: + // 2 = store 1 + // 3 = load 2 + // 4 = store 3 + // 5 = load 4 + // We want to replace uses of 5 with 1. + for (const auto& idPair : idMap) { + spv::Id id = idPair.first; + while (idMap.find(id) != idMap.end()) // Chase to end of chain + id = idMap[id]; + + idMap[idPair.first] = id; // replace with final result + } + + // Remove the load/store/variables for the ones we've discovered + process( + [&](spv::Op opCode, unsigned start) { + if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) || + (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) || + (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) { + + stripInst(start); + return true; + } + + return false; + }, + + [&](spv::Id& id) { + if (idMap.find(id) != idMap.end()) id = idMap[id]; + } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // remove bodies of uncalled functions + void spirvbin_t::dceFuncs() + { + msg(3, 2, std::string("Removing Dead Functions: ")); + + // TODO: There are more efficient ways to do this. + bool changed = true; + + while (changed) { + changed = false; + + for (auto fn = fnPos.begin(); fn != fnPos.end(); ) { + if (fn->first == entryPoint) { // don't DCE away the entry point! + ++fn; + continue; + } + + const auto call_it = fnCalls.find(fn->first); + + if (call_it == fnCalls.end() || call_it->second == 0) { + changed = true; + stripRange.push_back(fn->second); + + // decrease counts of called functions + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::Op::OpFunctionCall) { + const auto call_it = fnCalls.find(asId(start + 3)); + if (call_it != fnCalls.end()) { + if (--call_it->second <= 0) + fnCalls.erase(call_it); + } + } + + return true; + }, + op_fn_nop, + fn->second.first, + fn->second.second); + + if (errorLatch) + return; + + fn = fnPos.erase(fn); + } else ++fn; + } + } + } + + // remove unused function variables + decorations + void spirvbin_t::dceVars() + { + msg(3, 2, std::string("DCE Vars: ")); + + std::unordered_map varUseCount; + + // Count function variable use + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpVariable) { + ++varUseCount[asId(start+2)]; + return true; + } else if (opCode == spv::OpEntryPoint) { + const int wordCount = asWordCount(start); + for (int i = 4; i < wordCount; i++) { + ++varUseCount[asId(start+i)]; + } + return true; + } else + return false; + }, + + [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single-use function variables + associated decorations and names + process( + [&](spv::Op opCode, unsigned start) { + spv::Id id = spv::NoResult; + if (opCode == spv::OpVariable) + id = asId(start+2); + if (opCode == spv::OpDecorate || opCode == spv::OpName) + id = asId(start+1); + + if (id != spv::NoResult && varUseCount[id] == 1) + stripInst(start); + + return true; + }, + op_fn_nop); + } + + // remove unused types + void spirvbin_t::dceTypes() + { + std::vector isType(bound(), false); + + // for speed, make O(1) way to get to type query (map is log(n)) + for (const auto typeStart : typeConstPos) + isType[asTypeConstId(typeStart)] = true; + + std::unordered_map typeUseCount; + + // This is not the most efficient algorithm, but this is an offline tool, and + // it's easy to write this way. Can be improved opportunistically if needed. + bool changed = true; + while (changed) { + changed = false; + strip(); + typeUseCount.clear(); + + // Count total type usage + process(inst_fn_nop, + [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single reference types + for (const auto typeStart : typeConstPos) { + const spv::Id typeId = asTypeConstId(typeStart); + if (typeUseCount[typeId] == 1) { + changed = true; + --typeUseCount[typeId]; + stripInst(typeStart); + } + } + + if (errorLatch) + return; + } + } + +#ifdef NOTDEF + bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const + { + // Find the local type id "lt" and global type id "gt" + const auto lt_it = typeConstPosR.find(lt); + if (lt_it == typeConstPosR.end()) + return false; + + const auto typeStart = lt_it->second; + + // Search for entry in global table + const auto gtype = globalTypes.find(gt); + if (gtype == globalTypes.end()) + return false; + + const auto& gdata = gtype->second; + + // local wordcount and opcode + const int wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + // no type match if opcodes don't match, or operand count doesn't match + if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0])) + return false; + + const unsigned numOperands = wordCount - 2; // all types have a result + + const auto cmpIdRange = [&](range_t range) { + for (int x=range.first; xsecond; + } + + // Hash types to canonical values. This can return ID collisions (it's a bit + // inevitable): it's up to the caller to handle that gracefully. + std::uint32_t spirvbin_t::hashType(unsigned typeStart) const + { + const unsigned wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + switch (opCode) { + case spv::OpTypeVoid: return 0; + case spv::OpTypeBool: return 1; + case spv::OpTypeInt: return 3 + (spv[typeStart+3]); + case spv::OpTypeFloat: return 5; + case spv::OpTypeVector: + return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeMatrix: + return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeImage: + return 120 + hashType(idPos(spv[typeStart+2])) + + spv[typeStart+3] + // dimensionality + spv[typeStart+4] * 8 * 16 + // depth + spv[typeStart+5] * 4 * 16 + // arrayed + spv[typeStart+6] * 2 * 16 + // multisampled + spv[typeStart+7] * 1 * 16; // format + case spv::OpTypeSampler: + return 500; + case spv::OpTypeSampledImage: + return 502; + case spv::OpTypeArray: + return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3]; + case spv::OpTypeRuntimeArray: + return 5000 + hashType(idPos(spv[typeStart+2])); + case spv::OpTypeStruct: + { + std::uint32_t hash = 10000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeOpaque: return 6000 + spv[typeStart+2]; + case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3])); + case spv::OpTypeFunction: + { + std::uint32_t hash = 200000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeEvent: return 300000; + case spv::OpTypeDeviceEvent: return 300001; + case spv::OpTypeReserveId: return 300002; + case spv::OpTypeQueue: return 300003; + case spv::OpTypePipe: return 300004; + case spv::OpConstantTrue: return 300007; + case spv::OpConstantFalse: return 300008; + case spv::OpTypeRayQueryKHR: return 300009; + case spv::OpTypeAccelerationStructureKHR: return 300010; + case spv::OpConstantComposite: + { + std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + case spv::OpConstant: + { + std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + case spv::OpConstantNull: + { + std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1])); + return hash; + } + case spv::OpConstantSampler: + { + std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + + default: + error("unknown type opcode"); + return 0; + } + } + + void spirvbin_t::mapTypeConst() + { + globaltypes_t globalTypeMap; + + msg(3, 2, std::string("Remapping Consts & Types: ")); + + static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options + static const std::uint32_t firstMappedID = 8; // offset into ID space + + for (auto& typeStart : typeConstPos) { + const spv::Id resId = asTypeConstId(typeStart); + const std::uint32_t hashval = hashType(typeStart); + + if (errorLatch) + return; + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + } + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::strip() + { + if (stripRange.empty()) // nothing to do + return; + + // Sort strip ranges in order of traversal + std::sort(stripRange.begin(), stripRange.end()); + + // Allocate a new binary big enough to hold old binary + // We'll step this iterator through the strip ranges as we go through the binary + auto strip_it = stripRange.begin(); + + int strippedPos = 0; + for (unsigned word = 0; word < unsigned(spv.size()); ++word) { + while (strip_it != stripRange.end() && word >= strip_it->second) + ++strip_it; + + if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second) + spv[strippedPos++] = spv[word]; + } + + spv.resize(strippedPos); + stripRange.clear(); + + buildLocalMaps(); + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::remap(std::uint32_t opts) + { + options = opts; + + // Set up opcode tables from SpvDoc + spv::Parameterize(); + + validate(); // validate header + buildLocalMaps(); // build ID maps + + msg(3, 4, std::string("ID bound: ") + std::to_string(bound())); + + if (options & STRIP) stripDebug(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + if (options & OPT_LOADSTORE) optLoadStore(); + if (errorLatch) return; + + if (options & OPT_FWD_LS) forwardLoadStores(); + if (errorLatch) return; + + if (options & DCE_FUNCS) dceFuncs(); + if (errorLatch) return; + + if (options & DCE_VARS) dceVars(); + if (errorLatch) return; + + if (options & DCE_TYPES) dceTypes(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + stripDeadRefs(); // remove references to things we DCEed + if (errorLatch) return; + + // after the last strip, we must clean any debug info referring to now-deleted data + + if (options & MAP_TYPES) mapTypeConst(); + if (errorLatch) return; + + if (options & MAP_NAMES) mapNames(); + if (errorLatch) return; + + if (options & MAP_FUNCS) mapFnBodies(); + if (errorLatch) return; + + if (options & MAP_ALL) { + mapRemainder(); // map any unmapped IDs + if (errorLatch) return; + + applyMap(); // Now remap each shader to the new IDs we've come up with + if (errorLatch) return; + } + } + + // remap from a memory image + void spirvbin_t::remap(std::vector& in_spv, const std::vector& whiteListStrings, + std::uint32_t opts) + { + stripWhiteList = whiteListStrings; + spv.swap(in_spv); + remap(opts); + spv.swap(in_spv); + } + + // remap from a memory image - legacy interface without white list + void spirvbin_t::remap(std::vector& in_spv, std::uint32_t opts) + { + stripWhiteList.clear(); + spv.swap(in_spv); + remap(opts); + spv.swap(in_spv); + } + +} // namespace SPV + diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.h b/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.h new file mode 100644 index 00000000..e60da792 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SPVRemapper.h @@ -0,0 +1,300 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef SPIRVREMAPPER_H +#define SPIRVREMAPPER_H + +#include +#include +#include +#include + +#ifdef GLSLANG_IS_SHARED_LIBRARY + #ifdef _WIN32 + #ifdef GLSLANG_EXPORTING + #define GLSLANG_EXPORT __declspec(dllexport) + #else + #define GLSLANG_EXPORT __declspec(dllimport) + #endif + #elif __GNUC__ >= 4 + #define GLSLANG_EXPORT __attribute__((visibility("default"))) + #endif +#endif // GLSLANG_IS_SHARED_LIBRARY +#ifndef GLSLANG_EXPORT +#define GLSLANG_EXPORT +#endif + +namespace spv { + +class spirvbin_base_t +{ +public: + enum Options { + NONE = 0, + STRIP = (1<<0), + MAP_TYPES = (1<<1), + MAP_NAMES = (1<<2), + MAP_FUNCS = (1<<3), + DCE_FUNCS = (1<<4), + DCE_VARS = (1<<5), + DCE_TYPES = (1<<6), + OPT_LOADSTORE = (1<<7), + OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV + MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS), + DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES), + OPT_ALL = (OPT_LOADSTORE), + + ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL), + DO_EVERYTHING = (STRIP | ALL_BUT_STRIP) + }; +}; + +} // namespace SPV + +#include +#include +#include +#include +#include +#include +#include + +#include "spirv.hpp" + +namespace spv { + +static inline constexpr Id NoResult = 0; + +// class to hold SPIR-V binary data for remapping, DCE, and debug stripping +class GLSLANG_EXPORT spirvbin_t : public spirvbin_base_t +{ +public: + spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false) + { } + + virtual ~spirvbin_t() { } + + // remap on an existing binary in memory + void remap(std::vector& spv, const std::vector& whiteListStrings, + std::uint32_t opts = DO_EVERYTHING); + + // remap on an existing binary in memory - legacy interface without white list + void remap(std::vector& spv, std::uint32_t opts = DO_EVERYTHING); + + // Type for error/log handler functions + typedef std::function errorfn_t; + typedef std::function logfn_t; + + // Register error/log handling functions (can be lambda fn / functor / etc) + static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; } + static void registerLogHandler(logfn_t handler) { logHandler = handler; } + +protected: + // This can be overridden to provide other message behavior if needed + virtual void msg(int minVerbosity, int indent, const std::string& txt) const; + +private: + // Local to global, or global to local ID map + typedef std::unordered_map idmap_t; + typedef std::unordered_set idset_t; + typedef std::unordered_map blockmap_t; + + void remap(std::uint32_t opts = DO_EVERYTHING); + + // Map of names to IDs + typedef std::unordered_map namemap_t; + + typedef std::uint32_t spirword_t; + + typedef std::pair range_t; + typedef std::function idfn_t; + typedef std::function instfn_t; + + // Special Values for ID map: + static const spv::Id unmapped; // unchanged from default value + static const spv::Id unused; // unused ID + static const int header_size; // SPIR header = 5 words + + class id_iterator_t; + + // For mapping type entries between different shaders + typedef std::vector typeentry_t; + typedef std::map globaltypes_t; + + // A set that preserves position order, and a reverse map + typedef std::set posmap_t; + typedef std::unordered_map posmap_rev_t; + + // Maps and ID to the size of its base type, if known. + typedef std::unordered_map typesize_map_t; + + // handle error + void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); } + + bool isConstOp(spv::Op opCode) const; + bool isTypeOp(spv::Op opCode) const; + bool isStripOp(spv::Op opCode) const; + bool isFlowCtrl(spv::Op opCode) const; + range_t literalRange(spv::Op opCode) const; + range_t typeRange(spv::Op opCode) const; + range_t constRange(spv::Op opCode) const; + unsigned typeSizeInWords(spv::Id id) const; + unsigned idTypeSizeInWords(spv::Id id) const; + + bool isStripOp(spv::Op opCode, unsigned start) const; + + spv::Id& asId(unsigned word) { return spv[word]; } + const spv::Id& asId(unsigned word) const { return spv[word]; } + spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); } + std::uint32_t asOpCodeHash(unsigned word); + spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } + unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } + spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } + unsigned idPos(spv::Id id) const; + + static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } + static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } + + // Header access & set methods + spirword_t magic() const { return spv[0]; } // return magic number + spirword_t bound() const { return spv[3]; } // return Id bound from header + spirword_t bound(spirword_t b) { return spv[3] = b; } + spirword_t genmagic() const { return spv[2]; } // generator magic + spirword_t genmagic(spirword_t m) { return spv[2] = m; } + spirword_t schemaNum() const { return spv[4]; } // schema number from header + + // Mapping fns: get + spv::Id localId(spv::Id id) const { return idMapL[id]; } + + // Mapping fns: set + inline spv::Id localId(spv::Id id, spv::Id newId); + void countIds(spv::Id id); + + // Return next unused new local ID. + // NOTE: boost::dynamic_bitset would be more efficient due to find_next(), + // which std::vector doens't have. + inline spv::Id nextUnusedId(spv::Id id); + + void buildLocalMaps(); + std::string literalString(unsigned word) const; // Return literal as a std::string + int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; } + + bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); } + bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; } + bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; } + bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); } + bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); } + + // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const; + // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const; + std::uint32_t hashType(unsigned typeStart) const; + + spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0); + int processInstruction(unsigned word, instfn_t, idfn_t); + + void validate() const; + void mapTypeConst(); + void mapFnBodies(); + void optLoadStore(); + void dceFuncs(); + void dceVars(); + void dceTypes(); + void mapNames(); + void foldIds(); // fold IDs to smallest space + void forwardLoadStores(); // load store forwarding (EXPERIMENTAL) + void offsetIds(); // create relative offset IDs + + void applyMap(); // remap per local name map + void mapRemainder(); // map any IDs we haven't touched yet + void stripDebug(); // strip all debug info + void stripDeadRefs(); // strips debug info for now-dead references after DCE + void strip(); // remove debug symbols + + std::vector spv; // SPIR words + + std::vector stripWhiteList; + + namemap_t nameMap; // ID names from OpName + + // Since we want to also do binary ops, we can't use std::vector. we could use + // boost::dynamic_bitset, but we're trying to avoid a boost dependency. + typedef std::uint64_t bits_t; + std::vector mapped; // which new IDs have been mapped + static const int mBits = sizeof(bits_t) * 4; + + bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); } + void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); } + void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); } + size_t maxMappedId() const { return mapped.size() * mBits; } + + // Add a strip range for a given instruction starting at 'start' + // Note: avoiding brace initializers to please older versions os MSVC. + void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); } + + // Function start and end. use unordered_map because we'll have + // many fewer functions than IDs. + std::unordered_map fnPos; + + // Which functions are called, anywhere in the module, with a call count + std::unordered_map fnCalls; + + posmap_t typeConstPos; // word positions that define types & consts (ordered) + posmap_rev_t idPosR; // reverse map from IDs to positions + typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known. + + std::vector idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs + + spv::Id entryPoint; // module entry point + spv::Id largestNewId; // biggest new ID we have mapped anything to + + // Sections of the binary to strip, given as [begin,end) + std::vector stripRange; + + // processing options: + std::uint32_t options; + int verbose; // verbosity level + + // Error latch: this is set if the error handler is ever executed. It would be better to + // use a try/catch block and throw, but that's not desired for certain environments, so + // this is the alternative. + mutable bool errorLatch; + + static errorfn_t errorHandler; + static logfn_t logHandler; +}; + +} // namespace SPV + +#endif // SPIRVREMAPPER_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.cpp b/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.cpp new file mode 100644 index 00000000..7bb8c329 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.cpp @@ -0,0 +1,4623 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Helper for making SPIR-V IR. Generally, this is documented in the header +// SpvBuilder.h. +// + +#include +#include + +#include +#include + +#include "SpvBuilder.h" +#include "hex_float.h" + +#ifndef _WIN32 + #include +#endif + +namespace spv { + +Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : + spvVersion(spvVersion), + sourceLang(SourceLanguageUnknown), + sourceVersion(0), + addressModel(AddressingModelLogical), + memoryModel(MemoryModelGLSL450), + builderNumber(magicNumber), + buildPoint(nullptr), + uniqueId(0), + entryPointFunction(nullptr), + generatingOpCodeForSpecConst(false), + logger(buildLogger) +{ + clearAccessChain(); +} + +Builder::~Builder() +{ +} + +Id Builder::import(const char* name) +{ + Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); + import->addStringOperand(name); + module.mapInstruction(import); + + imports.push_back(std::unique_ptr(import)); + return import->getResultId(); +} + +// For creating new groupedTypes (will return old type if the requested one was already made). +Id Builder::makeVoidType() +{ + Instruction* type; + if (groupedTypes[OpTypeVoid].size() == 0) { + Id typeId = getUniqueId(); + type = new Instruction(typeId, NoType, OpTypeVoid); + groupedTypes[OpTypeVoid].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + // Core OpTypeVoid used for debug void type + if (emitNonSemanticShaderDebugInfo) + debugId[typeId] = typeId; + } else + type = groupedTypes[OpTypeVoid].back(); + + return type->getResultId(); +} + +Id Builder::makeBoolType() +{ + Instruction* type; + if (groupedTypes[OpTypeBool].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeBool); + groupedTypes[OpTypeBool].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) { + auto const debugResultId = makeBoolDebugType(32); + debugId[type->getResultId()] = debugResultId; + } + + } else + type = groupedTypes[OpTypeBool].back(); + + + return type->getResultId(); +} + +Id Builder::makeSamplerType() +{ + Instruction* type; + if (groupedTypes[OpTypeSampler].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeSampler); + groupedTypes[OpTypeSampler].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeSampler].back(); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeCompositeDebugType({}, "type.sampler", NonSemanticShaderDebugInfo100Structure, true); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makePointer(StorageClass storageClass, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypePointer); + type->reserveOperands(2); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) { + const Id debugResultId = makePointerDebugType(storageClass, pointee); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeForwardPointer(StorageClass storageClass) +{ + // Caching/uniquifying doesn't work here, because we don't know the + // pointee type and there can be multiple forward pointers of the same + // storage type. Somebody higher up in the stack must keep track. + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); + type->addImmediateOperand(storageClass); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) { + const Id debugResultId = makeForwardPointerDebugType(storageClass); + debugId[type->getResultId()] = debugResultId; + } + return type->getResultId(); +} + +Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + type = new Instruction(forwardPointerType, NoType, OpTypePointer); + type->reserveOperands(2); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // If we are emitting nonsemantic debuginfo, we need to patch the debug pointer type + // that was emitted alongside the forward pointer, now that we have a pointee debug + // type for it to point to. + if (emitNonSemanticShaderDebugInfo) { + Instruction *debugForwardPointer = module.getInstruction(debugId[forwardPointerType]); + assert(debugId[pointee]); + debugForwardPointer->setIdOperand(2, debugId[pointee]); + } + + return type->getResultId(); +} + +Id Builder::makeIntegerType(int width, bool hasSign) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { + type = groupedTypes[OpTypeInt][t]; + if (type->getImmediateOperand(0) == (unsigned)width && + type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeInt); + type->reserveOperands(2); + type->addImmediateOperand(width); + type->addImmediateOperand(hasSign ? 1 : 0); + groupedTypes[OpTypeInt].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 8: + case 16: + // these are currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityInt64); + break; + default: + break; + } + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeIntegerDebugType(width, hasSign); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeFloatType(int width) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { + type = groupedTypes[OpTypeFloat][t]; + if (type->getNumOperands() != 1) { + continue; + } + if (type->getImmediateOperand(0) == (unsigned)width) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFloat); + type->addImmediateOperand(width); + groupedTypes[OpTypeFloat].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 16: + // currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityFloat64); + break; + default: + break; + } + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeFloatDebugType(width); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeBFloat16Type() +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { + type = groupedTypes[OpTypeFloat][t]; + if (type->getNumOperands() != 2) { + continue; + } + if (type->getImmediateOperand(0) == (unsigned)16 && + type->getImmediateOperand(1) == FPEncodingBFloat16KHR) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFloat); + type->addImmediateOperand(16); + type->addImmediateOperand(FPEncodingBFloat16KHR); + groupedTypes[OpTypeFloat].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + addExtension(spv::E_SPV_KHR_bfloat16); + addCapability(CapabilityBFloat16TypeKHR); + +#if 0 + // XXX not supported + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeFloatDebugType(width); + debugId[type->getResultId()] = debugResultId; + } +#endif + + return type->getResultId(); +} + +// Make a struct without checking for duplication. +// See makeStructResultType() for non-decorated structs +// needed as the result of some instructions, which does +// check for duplicates. +Id Builder::makeStructType(const std::vector& members, const char* name, bool const compilerGenerated) +{ + // Don't look for previous one, because in the general case, + // structs can be duplicated except for decorations. + + // not found, make it + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); + for (int op = 0; op < (int)members.size(); ++op) + type->addIdOperand(members[op]); + groupedTypes[OpTypeStruct].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + addName(type->getResultId(), name); + + if (emitNonSemanticShaderDebugInfo && !compilerGenerated) + { + auto const debugResultId = makeCompositeDebugType(members, name, NonSemanticShaderDebugInfo100Structure); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +// Make a struct for the simple results of several instructions, +// checking for duplication. +Id Builder::makeStructResultType(Id type0, Id type1) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { + type = groupedTypes[OpTypeStruct][t]; + if (type->getNumOperands() != 2) + continue; + if (type->getIdOperand(0) != type0 || + type->getIdOperand(1) != type1) + continue; + return type->getResultId(); + } + + // not found, make it + std::vector members; + members.push_back(type0); + members.push_back(type1); + + return makeStructType(members, "ResType"); +} + +Id Builder::makeVectorType(Id component, int size) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { + type = groupedTypes[OpTypeVector][t]; + if (type->getIdOperand(0) == component && + type->getImmediateOperand(1) == (unsigned)size) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeVector); + type->reserveOperands(2); + type->addIdOperand(component); + type->addImmediateOperand(size); + groupedTypes[OpTypeVector].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeVectorDebugType(component, size); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeMatrixType(Id component, int cols, int rows) +{ + assert(cols <= maxMatrixSize && rows <= maxMatrixSize); + + Id column = makeVectorType(component, rows); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { + type = groupedTypes[OpTypeMatrix][t]; + if (type->getIdOperand(0) == column && + type->getImmediateOperand(1) == (unsigned)cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); + type->reserveOperands(2); + type->addIdOperand(column); + type->addImmediateOperand(cols); + groupedTypes[OpTypeMatrix].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeMatrixDebugType(column, cols); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixKHR].size(); ++t) { + type = groupedTypes[OpTypeCooperativeMatrixKHR][t]; + if (type->getIdOperand(0) == component && + type->getIdOperand(1) == scope && + type->getIdOperand(2) == rows && + type->getIdOperand(3) == cols && + type->getIdOperand(4) == use) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixKHR); + type->reserveOperands(5); + type->addIdOperand(component); + type->addIdOperand(scope); + type->addIdOperand(rows); + type->addIdOperand(cols); + type->addIdOperand(use); + groupedTypes[OpTypeCooperativeMatrixKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) + { + // Find a name for one of the parameters. It can either come from debuginfo for another + // type, or an OpName from a constant. + auto const findName = [&](Id id) { + Id id2 = debugId[id]; + for (auto &t : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic]) { + if (t->getResultId() == id2) { + for (auto &s : strings) { + if (s->getResultId() == t->getIdOperand(2)) { + return s->getNameString(); + } + } + } + } + for (auto &t : names) { + if (t->getIdOperand(0) == id) { + return t->getNameString(); + } + } + return "unknown"; + }; + std::string debugName = "coopmat<"; + debugName += std::string(findName(component)) + ", "; + if (isConstantScalar(scope)) { + debugName += std::string("gl_Scope") + std::string(spv::ScopeToString((spv::Scope)getConstantScalar(scope))) + ", "; + } else { + debugName += std::string(findName(scope)) + ", "; + } + debugName += std::string(findName(rows)) + ", "; + debugName += std::string(findName(cols)) + ">"; + // There's no nonsemantic debug info instruction for cooperative matrix types, + // use opaque composite instead. + auto const debugResultId = makeCompositeDebugType({}, debugName.c_str(), NonSemanticShaderDebugInfo100Structure, true); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { + type = groupedTypes[OpTypeCooperativeMatrixNV][t]; + if (type->getIdOperand(0) == component && type->getIdOperand(1) == scope && type->getIdOperand(2) == rows && + type->getIdOperand(3) == cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); + type->reserveOperands(4); + type->addIdOperand(component); + type->addIdOperand(scope); + type->addIdOperand(rows); + type->addIdOperand(cols); + groupedTypes[OpTypeCooperativeMatrixNV].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType) +{ + Instruction* instr = module.getInstruction(otherType); + if (instr->getOpCode() == OpTypeCooperativeMatrixNV) { + return makeCooperativeMatrixTypeNV(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3)); + } else { + assert(instr->getOpCode() == OpTypeCooperativeMatrixKHR); + return makeCooperativeMatrixTypeKHR(component, instr->getIdOperand(1), instr->getIdOperand(2), instr->getIdOperand(3), instr->getIdOperand(4)); + } +} + +Id Builder::makeCooperativeVectorTypeNV(Id componentType, Id components) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeVectorNV].size(); ++t) { + type = groupedTypes[OpTypeCooperativeVectorNV][t]; + if (type->getIdOperand(0) == componentType && + type->getIdOperand(1) == components) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeVectorNV); + type->addIdOperand(componentType); + type->addIdOperand(components); + groupedTypes[OpTypeCooperativeVectorNV].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeGenericType(spv::Op opcode, std::vector& operands) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[opcode].size(); ++t) { + type = groupedTypes[opcode][t]; + if (static_cast(type->getNumOperands()) != operands.size()) + continue; // Number mismatch, find next + + bool match = true; + for (int op = 0; match && op < (int)operands.size(); ++op) { + match = (operands[op].isId ? type->getIdOperand(op) : type->getImmediateOperand(op)) == operands[op].word; + } + if (match) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, opcode); + type->reserveOperands(operands.size()); + for (size_t op = 0; op < operands.size(); ++op) { + if (operands[op].isId) + type->addIdOperand(operands[op].word); + else + type->addImmediateOperand(operands[op].word); + } + groupedTypes[opcode].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +// TODO: performance: track arrays per stride +// If a stride is supplied (non-zero) make an array. +// If no stride (0), reuse previous array types. +// 'size' is an Id of a constant or specialization constant of the array size +Id Builder::makeArrayType(Id element, Id sizeId, int stride) +{ + Instruction* type; + if (stride == 0) { + // try to find existing type + for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { + type = groupedTypes[OpTypeArray][t]; + if (type->getIdOperand(0) == element && + type->getIdOperand(1) == sizeId && + explicitlyLaidOut.find(type->getResultId()) == explicitlyLaidOut.end()) + return type->getResultId(); + } + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeArray); + type->reserveOperands(2); + type->addIdOperand(element); + type->addIdOperand(sizeId); + groupedTypes[OpTypeArray].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (stride != 0) { + explicitlyLaidOut.insert(type->getResultId()); + } + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeArrayDebugType(element, sizeId); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeRuntimeArray(Id element) +{ + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); + type->addIdOperand(element); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeArrayDebugType(element, makeUintConstant(0)); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeFunctionType(Id returnType, const std::vector& paramTypes) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { + type = groupedTypes[OpTypeFunction][t]; + if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) + continue; + bool mismatch = false; + for (int p = 0; p < (int)paramTypes.size(); ++p) { + if (paramTypes[p] != type->getIdOperand(p + 1)) { + mismatch = true; + break; + } + } + if (! mismatch) + { + // If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void) + // function type is created for the wrapper function. However, nonsemantic shader debug information is disabled + // while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create + // the associated debug function type if it hasn't been created yet. + if(emitNonSemanticShaderDebugInfo && debugId[type->getResultId()] == 0) { + assert(sourceLang == spv::SourceLanguageHLSL); + assert(getTypeClass(returnType) == OpTypeVoid && paramTypes.size() == 0); + + Id debugTypeId = makeDebugFunctionType(returnType, {}); + debugId[type->getResultId()] = debugTypeId; + } + return type->getResultId(); + } + } + + // not found, make it + Id typeId = getUniqueId(); + type = new Instruction(typeId, NoType, OpTypeFunction); + type->reserveOperands(paramTypes.size() + 1); + type->addIdOperand(returnType); + for (int p = 0; p < (int)paramTypes.size(); ++p) + type->addIdOperand(paramTypes[p]); + groupedTypes[OpTypeFunction].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // make debug type and map it + if (emitNonSemanticShaderDebugInfo) { + Id debugTypeId = makeDebugFunctionType(returnType, paramTypes); + debugId[typeId] = debugTypeId; + } + + return type->getResultId(); +} + +Id Builder::makeDebugFunctionType(Id returnType, const std::vector& paramTypes) +{ + assert(debugId[returnType] != 0); + + Id typeId = getUniqueId(); + auto type = new Instruction(typeId, makeVoidType(), OpExtInst); + type->reserveOperands(paramTypes.size() + 4); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeFunction); + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); + type->addIdOperand(debugId[returnType]); + for (auto const paramType : paramTypes) { + if (isPointerType(paramType) || isArrayType(paramType)) { + type->addIdOperand(debugId[getContainedTypeId(paramType)]); + } + else { + type->addIdOperand(debugId[paramType]); + } + } + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + return typeId; +} + +Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, + ImageFormat format) +{ + assert(sampled == 1 || sampled == 2); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { + type = groupedTypes[OpTypeImage][t]; + if (type->getIdOperand(0) == sampledType && + type->getImmediateOperand(1) == (unsigned int)dim && + type->getImmediateOperand(2) == ( depth ? 1u : 0u) && + type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && + type->getImmediateOperand(4) == ( ms ? 1u : 0u) && + type->getImmediateOperand(5) == sampled && + type->getImmediateOperand(6) == (unsigned int)format) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeImage); + type->reserveOperands(7); + type->addIdOperand(sampledType); + type->addImmediateOperand( dim); + type->addImmediateOperand( depth ? 1 : 0); + type->addImmediateOperand(arrayed ? 1 : 0); + type->addImmediateOperand( ms ? 1 : 0); + type->addImmediateOperand(sampled); + type->addImmediateOperand((unsigned int)format); + + groupedTypes[OpTypeImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (dim) { + case DimBuffer: + if (sampled == 1) + addCapability(CapabilitySampledBuffer); + else + addCapability(CapabilityImageBuffer); + break; + case Dim1D: + if (sampled == 1) + addCapability(CapabilitySampled1D); + else + addCapability(CapabilityImage1D); + break; + case DimCube: + if (arrayed) { + if (sampled == 1) + addCapability(CapabilitySampledCubeArray); + else + addCapability(CapabilityImageCubeArray); + } + break; + case DimRect: + if (sampled == 1) + addCapability(CapabilitySampledRect); + else + addCapability(CapabilityImageRect); + break; + case DimSubpassData: + addCapability(CapabilityInputAttachment); + break; + default: + break; + } + + if (ms) { + if (sampled == 2) { + // Images used with subpass data are not storage + // images, so don't require the capability for them. + if (dim != Dim::DimSubpassData) + addCapability(CapabilityStorageImageMultisample); + if (arrayed) + addCapability(CapabilityImageMSArray); + } + } + + if (emitNonSemanticShaderDebugInfo) + { + auto TypeName = [&dim]() -> char const* { + switch (dim) { + case Dim1D: return "type.1d.image"; + case Dim2D: return "type.2d.image"; + case Dim3D: return "type.3d.image"; + case DimCube: return "type.cube.image"; + default: return "type.image"; + } + }; + + auto const debugResultId = makeCompositeDebugType({}, TypeName(), NonSemanticShaderDebugInfo100Class, true); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeSampledImageType(Id imageType) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { + type = groupedTypes[OpTypeSampledImage][t]; + if (type->getIdOperand(0) == imageType) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); + type->addIdOperand(imageType); + + groupedTypes[OpTypeSampledImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = makeCompositeDebugType({}, "type.sampled.image", NonSemanticShaderDebugInfo100Class, true); + debugId[type->getResultId()] = debugResultId; + } + + return type->getResultId(); +} + +Id Builder::makeDebugInfoNone() +{ + if (debugInfoNone != 0) + return debugInfoNone; + + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(2); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugInfoNone); + + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + + debugInfoNone = inst->getResultId(); + + return debugInfoNone; +} + +Id Builder::makeBoolDebugType(int const size) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { + type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; + if (type->getIdOperand(0) == getStringId("bool") && + type->getIdOperand(1) == static_cast(size) && + type->getIdOperand(2) == NonSemanticShaderDebugInfo100Boolean) + return type->getResultId(); + } + + type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(6); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); + + type->addIdOperand(getStringId("bool")); // name id + type->addIdOperand(makeUintConstant(size)); // size id + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Boolean)); // encoding id + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeIntegerDebugType(int const width, bool const hasSign) +{ + const char* typeName = nullptr; + switch (width) { + case 8: typeName = hasSign ? "int8_t" : "uint8_t"; break; + case 16: typeName = hasSign ? "int16_t" : "uint16_t"; break; + case 64: typeName = hasSign ? "int64_t" : "uint64_t"; break; + default: typeName = hasSign ? "int" : "uint"; + } + auto nameId = getStringId(typeName); + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { + type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; + if (type->getIdOperand(0) == nameId && + type->getIdOperand(1) == static_cast(width) && + type->getIdOperand(2) == (hasSign ? NonSemanticShaderDebugInfo100Signed : NonSemanticShaderDebugInfo100Unsigned)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(6); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); + type->addIdOperand(nameId); // name id + type->addIdOperand(makeUintConstant(width)); // size id + if(hasSign == true) { + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Signed)); // encoding id + } else { + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Unsigned)); // encoding id + } + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeFloatDebugType(int const width) +{ + const char* typeName = nullptr; + switch (width) { + case 16: typeName = "float16_t"; break; + case 64: typeName = "double"; break; + default: typeName = "float"; break; + } + auto nameId = getStringId(typeName); + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].size(); ++t) { + type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic][t]; + if (type->getIdOperand(0) == nameId && + type->getIdOperand(1) == static_cast(width) && + type->getIdOperand(2) == NonSemanticShaderDebugInfo100Float) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(6); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeBasic); + type->addIdOperand(nameId); // name id + type->addIdOperand(makeUintConstant(width)); // size id + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100Float)); // encoding id + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100None)); // flags id + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeBasic].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType) +{ + assert(sequenceType == NonSemanticShaderDebugInfo100DebugTypeArray || + sequenceType == NonSemanticShaderDebugInfo100DebugTypeVector); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedDebugTypes[sequenceType].size(); ++t) { + type = groupedDebugTypes[sequenceType][t]; + if (type->getIdOperand(0) == baseType && + type->getIdOperand(1) == makeUintConstant(componentCount)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(4); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(sequenceType); + type->addIdOperand(debugId[baseType]); // base type + type->addIdOperand(componentCount); // component count + + groupedDebugTypes[sequenceType].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeArrayDebugType(Id const baseType, Id const componentCount) +{ + return makeSequentialDebugType(baseType, componentCount, NonSemanticShaderDebugInfo100DebugTypeArray); +} + +Id Builder::makeVectorDebugType(Id const baseType, int const componentCount) +{ + return makeSequentialDebugType(baseType, makeUintConstant(componentCount), NonSemanticShaderDebugInfo100DebugTypeVector); +} + +Id Builder::makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].size(); ++t) { + type = groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix][t]; + if (type->getIdOperand(0) == vectorType && + type->getIdOperand(1) == makeUintConstant(vectorCount)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(5); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMatrix); + type->addIdOperand(debugId[vectorType]); // vector type id + type->addIdOperand(makeUintConstant(vectorCount)); // component count id + type->addIdOperand(makeBoolConstant(columnMajor)); // column-major id + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMatrix].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc) +{ + assert(debugId[memberType] != 0); + + Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(10); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeMember); + type->addIdOperand(getStringId(debugTypeLoc.name)); // name id + type->addIdOperand(debugId[memberType]); // type id + type->addIdOperand(makeDebugSource(currentFileId)); // source id + type->addIdOperand(makeUintConstant(debugTypeLoc.line)); // line id TODO: currentLine is always zero + type->addIdOperand(makeUintConstant(debugTypeLoc.column)); // TODO: column id + type->addIdOperand(makeUintConstant(0)); // TODO: offset id + type->addIdOperand(makeUintConstant(0)); // TODO: size id + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeMember].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +// Note: To represent a source language opaque type, this instruction must have no Members operands, Size operand must be +// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names. +Id Builder::makeCompositeDebugType(std::vector const& memberTypes, char const*const name, + NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType) +{ + // Create the debug member types. + std::vector memberDebugTypes; + for(auto const memberType : memberTypes) { + assert(debugTypeLocs.find(memberType) != debugTypeLocs.end()); + + // There _should_ be debug types for all the member types but currently buffer references + // do not have member debug info generated. + if (debugId[memberType]) + memberDebugTypes.emplace_back(makeMemberDebugType(memberType, debugTypeLocs[memberType])); + + // TODO: Need to rethink this method of passing location information. + // debugTypeLocs.erase(memberType); + } + + // Create The structure debug type. + Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(memberDebugTypes.size() + 11); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypeComposite); + type->addIdOperand(getStringId(name)); // name id + type->addIdOperand(makeUintConstant(tag)); // tag id + type->addIdOperand(makeDebugSource(currentFileId)); // source id + type->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? + type->addIdOperand(makeUintConstant(0)); // TODO: column id + type->addIdOperand(makeDebugCompilationUnit()); // scope id + if(isOpaqueType == true) { + // Prepend '@' to opaque types. + type->addIdOperand(getStringId('@' + std::string(name))); // linkage name id + type->addIdOperand(makeDebugInfoNone()); // size id + } else { + type->addIdOperand(getStringId(name)); // linkage name id + type->addIdOperand(makeUintConstant(0)); // TODO: size id + } + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); // flags id + assert(isOpaqueType == false || (isOpaqueType == true && memberDebugTypes.empty())); + for(auto const memberDebugType : memberDebugTypes) { + type->addIdOperand(memberDebugType); + } + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypeComposite].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makePointerDebugType(StorageClass storageClass, Id const baseType) +{ + const Id debugBaseType = debugId[baseType]; + if (!debugBaseType) { + return makeDebugInfoNone(); + } + const Id scID = makeUintConstant(storageClass); + for (Instruction* otherType : groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer]) { + if (otherType->getIdOperand(2) == debugBaseType && + otherType->getIdOperand(3) == scID) { + return otherType->getResultId(); + } + } + + Instruction* type = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + type->reserveOperands(5); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer); + type->addIdOperand(debugBaseType); + type->addIdOperand(scID); + type->addIdOperand(makeUintConstant(0)); + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +// Emit a OpExtInstWithForwardRefsKHR nonsemantic instruction for a pointer debug type +// where we don't have the pointee yet. Since we don't have the pointee yet, it just +// points to itself and we rely on patching it later. +Id Builder::makeForwardPointerDebugType(StorageClass storageClass) +{ + const Id scID = makeUintConstant(storageClass); + + this->addExtension(spv::E_SPV_KHR_relaxed_extended_instruction); + + Instruction *type = new Instruction(getUniqueId(), makeVoidType(), OpExtInstWithForwardRefsKHR); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugTypePointer); + type->addIdOperand(type->getResultId()); + type->addIdOperand(scID); + type->addIdOperand(makeUintConstant(0)); + + groupedDebugTypes[NonSemanticShaderDebugInfo100DebugTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeDebugSource(const Id fileName) { + if (debugSourceId.find(fileName) != debugSourceId.end()) + return debugSourceId[fileName]; + spv::Id resultId = getUniqueId(); + Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); + sourceInst->reserveOperands(3); + sourceInst->addIdOperand(nonSemanticShaderDebugInfo); + sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSource); + sourceInst->addIdOperand(fileName); + constantsTypesGlobals.push_back(std::unique_ptr(sourceInst)); + module.mapInstruction(sourceInst); + if (emitNonSemanticShaderDebugSource) { + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + auto processDebugSource = [&](std::string source) { + if (source.size() > 0) { + int nextByte = 0; + while ((int)source.size() - nextByte > 0) { + auto subString = source.substr(nextByte, nonNullBytesPerInstruction); + auto sourceId = getStringId(subString); + if (nextByte == 0) { + // DebugSource + sourceInst->addIdOperand(sourceId); + } else { + // DebugSourceContinued + Instruction* sourceContinuedInst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + sourceContinuedInst->reserveOperands(2); + sourceContinuedInst->addIdOperand(nonSemanticShaderDebugInfo); + sourceContinuedInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugSourceContinued); + sourceContinuedInst->addIdOperand(sourceId); + constantsTypesGlobals.push_back(std::unique_ptr(sourceContinuedInst)); + module.mapInstruction(sourceContinuedInst); + } + nextByte += nonNullBytesPerInstruction; + } + } else { + auto sourceId = getStringId(source); + sourceInst->addIdOperand(sourceId); + } + }; + if (fileName == mainFileId) { + processDebugSource(sourceText); + } else { + auto incItr = includeFiles.find(fileName); + if (incItr != includeFiles.end()) { + processDebugSource(*incItr->second); + } else { + // We omit the optional source text item if not available in glslang + } + } + } + debugSourceId[fileName] = resultId; + return resultId; +} + +Id Builder::makeDebugCompilationUnit() { + if (nonSemanticShaderCompilationUnitId != 0) + return nonSemanticShaderCompilationUnitId; + spv::Id resultId = getUniqueId(); + Instruction* sourceInst = new Instruction(resultId, makeVoidType(), OpExtInst); + sourceInst->reserveOperands(6); + sourceInst->addIdOperand(nonSemanticShaderDebugInfo); + sourceInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugCompilationUnit); + sourceInst->addIdOperand(makeUintConstant(1)); // TODO(greg-lunarg): Get rid of magic number + sourceInst->addIdOperand(makeUintConstant(4)); // TODO(greg-lunarg): Get rid of magic number + sourceInst->addIdOperand(makeDebugSource(mainFileId)); + sourceInst->addIdOperand(makeUintConstant(sourceLang)); + constantsTypesGlobals.push_back(std::unique_ptr(sourceInst)); + module.mapInstruction(sourceInst); + nonSemanticShaderCompilationUnitId = resultId; + + // We can reasonably assume that makeDebugCompilationUnit will be called before any of + // debug-scope stack. Function scopes and lexical scopes will occur afterward. + assert(currentDebugScopeId.empty()); + currentDebugScopeId.push(nonSemanticShaderCompilationUnitId); + + return resultId; +} + +Id Builder::createDebugGlobalVariable(Id const type, char const*const name, Id const variable) +{ + assert(type != 0); + + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(11); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugGlobalVariable); + inst->addIdOperand(getStringId(name)); // name id + inst->addIdOperand(type); // type id + inst->addIdOperand(makeDebugSource(currentFileId)); // source id + inst->addIdOperand(makeUintConstant(currentLine)); // line id TODO: currentLine always zero? + inst->addIdOperand(makeUintConstant(0)); // TODO: column id + inst->addIdOperand(makeDebugCompilationUnit()); // scope id + inst->addIdOperand(getStringId(name)); // linkage name id + inst->addIdOperand(variable); // variable id + inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsDefinition)); // flags id + + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + + return inst->getResultId(); +} + +Id Builder::createDebugLocalVariable(Id type, char const*const name, size_t const argNumber) +{ + assert(name != nullptr); + assert(!currentDebugScopeId.empty()); + + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(9); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLocalVariable); + inst->addIdOperand(getStringId(name)); // name id + inst->addIdOperand(type); // type id + inst->addIdOperand(makeDebugSource(currentFileId)); // source id + inst->addIdOperand(makeUintConstant(currentLine)); // line id + inst->addIdOperand(makeUintConstant(0)); // TODO: column id + inst->addIdOperand(currentDebugScopeId.top()); // scope id + inst->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsLocal)); // flags id + if(argNumber != 0) { + inst->addIdOperand(makeUintConstant(argNumber)); + } + + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + + return inst->getResultId(); +} + +Id Builder::makeDebugExpression() +{ + if (debugExpression != 0) + return debugExpression; + + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(2); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugExpression); + + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + + debugExpression = inst->getResultId(); + + return debugExpression; +} + +Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer) +{ + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(5); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare); + inst->addIdOperand(debugLocalVariable); // debug local variable id + inst->addIdOperand(pointer); // pointer to local variable id + inst->addIdOperand(makeDebugExpression()); // expression id + addInstruction(std::unique_ptr(inst)); + + return inst->getResultId(); +} + +Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value) +{ + Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst); + inst->reserveOperands(5); + inst->addIdOperand(nonSemanticShaderDebugInfo); + inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugValue); + inst->addIdOperand(debugLocalVariable); // debug local variable id + inst->addIdOperand(value); // value of local variable id + inst->addIdOperand(makeDebugExpression()); // expression id + addInstruction(std::unique_ptr(inst)); + + return inst->getResultId(); +} + +Id Builder::makeAccelerationStructureType() +{ + Instruction *type; + if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); + groupedTypes[OpTypeAccelerationStructureKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + if (emitNonSemanticShaderDebugInfo) { + spv::Id debugType = makeCompositeDebugType({}, "accelerationStructure", NonSemanticShaderDebugInfo100Structure, true); + debugId[type->getResultId()] = debugType; + } + } else { + type = groupedTypes[OpTypeAccelerationStructureKHR].back(); + } + + return type->getResultId(); +} + +Id Builder::makeRayQueryType() +{ + Instruction *type; + if (groupedTypes[OpTypeRayQueryKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryKHR); + groupedTypes[OpTypeRayQueryKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + if (emitNonSemanticShaderDebugInfo) { + spv::Id debugType = makeCompositeDebugType({}, "rayQuery", NonSemanticShaderDebugInfo100Structure, true); + debugId[type->getResultId()] = debugType; + } + } else { + type = groupedTypes[OpTypeRayQueryKHR].back(); + } + + return type->getResultId(); +} + +Id Builder::makeHitObjectNVType() +{ + Instruction *type; + if (groupedTypes[OpTypeHitObjectNV].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeHitObjectNV); + groupedTypes[OpTypeHitObjectNV].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else { + type = groupedTypes[OpTypeHitObjectNV].back(); + } + + return type->getResultId(); +} + +Id Builder::getDerefTypeId(Id resultId) const +{ + Id typeId = getTypeId(resultId); + assert(isPointerType(typeId)); + + return module.getInstruction(typeId)->getIdOperand(1); +} + +Op Builder::getMostBasicTypeClass(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return getMostBasicTypeClass(instr->getIdOperand(0)); + case OpTypePointer: + return getMostBasicTypeClass(instr->getIdOperand(1)); + default: + return typeClass; + } +} + +unsigned int Builder::getNumTypeConstituents(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + switch (instr->getOpCode()) + { + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypePointer: + return 1; + case OpTypeVector: + case OpTypeMatrix: + return instr->getImmediateOperand(1); + case OpTypeCooperativeVectorNV: + case OpTypeArray: + { + Id lengthId = instr->getIdOperand(1); + return module.getInstruction(lengthId)->getImmediateOperand(0); + } + case OpTypeStruct: + return instr->getNumOperands(); + case OpTypeCooperativeMatrixKHR: + case OpTypeCooperativeMatrixNV: + // has only one constituent when used with OpCompositeConstruct. + return 1; + default: + assert(0); + return 1; + } +} + +// Return the lowest-level type of scalar that an homogeneous composite is made out of. +// Typically, this is just to find out if something is made out of ints or floats. +// However, it includes returning a structure, if say, it is an array of structure. +Id Builder::getScalarTypeId(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVoid: + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypeStruct: + return instr->getResultId(); + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypePointer: + case OpTypeCooperativeVectorNV: + return getScalarTypeId(getContainedTypeId(typeId)); + default: + assert(0); + return NoResult; + } +} + +// Return the type of 'member' of a composite. +Id Builder::getContainedTypeId(Id typeId, int member) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypeCooperativeMatrixKHR: + case OpTypeCooperativeMatrixNV: + case OpTypeCooperativeVectorNV: + return instr->getIdOperand(0); + case OpTypePointer: + return instr->getIdOperand(1); + case OpTypeStruct: + return instr->getIdOperand(member); + default: + assert(0); + return NoResult; + } +} + +// Figure out the final resulting type of the access chain. +Id Builder::getResultingAccessChainType() const +{ + assert(accessChain.base != NoResult); + Id typeId = getTypeId(accessChain.base); + + assert(isPointerType(typeId)); + typeId = getContainedTypeId(typeId); + + for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { + if (isStructType(typeId)) { + assert(isConstantScalar(accessChain.indexChain[i])); + typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i])); + } else + typeId = getContainedTypeId(typeId, accessChain.indexChain[i]); + } + + return typeId; +} + +// Return the immediately contained type of a given composite type. +Id Builder::getContainedTypeId(Id typeId) const +{ + return getContainedTypeId(typeId, 0); +} + +// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' +// of width 'width'. The 'width' is only consumed for int and float types. +// Returns false otherwise. +bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypeInt: + case OpTypeFloat: + return typeClass == typeOp && instr.getImmediateOperand(0) == width; + case OpTypeStruct: + for (int m = 0; m < instr.getNumOperands(); ++m) { + if (containsType(instr.getIdOperand(m), typeOp, width)) + return true; + } + return false; + case OpTypePointer: + return false; + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return containsType(getContainedTypeId(typeId), typeOp, width); + default: + return typeClass == typeOp; + } +} + +// return true if the type is a pointer to PhysicalStorageBufferEXT or an +// contains such a pointer. These require restrict/aliased decorations. +bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypePointer: + return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; + case OpTypeArray: + return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); + case OpTypeStruct: + for (int m = 0; m < instr.getNumOperands(); ++m) { + if (containsPhysicalStorageBufferOrArray(instr.getIdOperand(m))) + return true; + } + return false; + default: + return false; + } +} + +// See if a scalar constant of this type has already been created, so it +// can be reused rather than duplicated. (Required by the specification). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == value) + return constant->getResultId(); + } + + return 0; +} + +// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == v1 && + constant->getImmediateOperand(1) == v2) + return constant->getResultId(); + } + + return 0; +} + +// Return true if consuming 'opcode' means consuming a constant. +// "constant" here means after final transform to executable code, +// the value consumed will be a constant, so includes specialization. +bool Builder::isConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpUndef: + case OpConstantTrue: + case OpConstantFalse: + case OpConstant: + case OpConstantComposite: + case OpConstantCompositeReplicateEXT: + case OpConstantSampler: + case OpConstantNull: + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantCompositeReplicateEXT: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + +// Return true if consuming 'opcode' means consuming a specialization constant. +bool Builder::isSpecConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + case OpSpecConstantCompositeReplicateEXT: + return true; + default: + return false; + } +} + +Id Builder::makeNullConstant(Id typeId) +{ + Instruction* constant; + + // See if we already made it. + Id existing = NoResult; + for (int i = 0; i < (int)nullConstants.size(); ++i) { + constant = nullConstants[i]; + if (constant->getTypeId() == typeId) + existing = constant->getResultId(); + } + + if (existing != NoResult) + return existing; + + // Make it + Instruction* c = new Instruction(getUniqueId(), typeId, OpConstantNull); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + nullConstants.push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeBoolConstant(bool b, bool specConstant) +{ + Id typeId = makeBoolType(); + Instruction* constant; + Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = 0; + for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { + constant = groupedConstants[OpTypeBool][i]; + if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) + existing = constant->getResultId(); + } + + if (existing) + return existing; + } + + // Make it + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeBool].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->reserveOperands(2); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeFloatConstant(float f, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(32); + union { float fl; unsigned int ui; } u; + u.fl = f; + unsigned value = u.ui; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeDoubleConstant(double d, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(64); + union { double db; unsigned long long ull; } u; + u.db = d; + unsigned long long value = u.ull; + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->reserveOperands(2); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeFloat16Constant(float f16, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(16); + + spvutils::HexFloat> fVal(f16); + spvutils::HexFloat> f16Val(0); + fVal.castTo(f16Val, spvutils::kRoundToZero); + + unsigned value = f16Val.value().getAsFloat().get_value(); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (!specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeBFloat16Constant(float bf16, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeBFloat16Type(); + + union { + float f; + uint32_t u; + } un; + un.f = bf16; + + // take high 16b of fp32 value. This is effectively round-to-zero, other than certain NaNs. + unsigned value = un.u >> 16; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (!specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeFpConstant(Id type, double d, bool specConstant) +{ + const int width = getScalarTypeWidth(type); + + assert(isFloatType(type)); + + switch (width) { + case 16: + return makeFloat16Constant((float)d, specConstant); + case 32: + return makeFloatConstant((float)d, specConstant); + case 64: + return makeDoubleConstant(d, specConstant); + default: + break; + } + + assert(false); + return NoResult; +} + +Id Builder::importNonSemanticShaderDebugInfoInstructions() +{ + assert(emitNonSemanticShaderDebugInfo == true); + + if(nonSemanticShaderDebugInfo == 0) + { + this->addExtension(spv::E_SPV_KHR_non_semantic_info); + nonSemanticShaderDebugInfo = this->import("NonSemantic.Shader.DebugInfo.100"); + } + + return nonSemanticShaderDebugInfo; +} + +Id Builder::findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std::vector& comps, size_t numMembers) +{ + Instruction* constant = nullptr; + bool found = false; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + + if (constant->getTypeId() != typeId) + continue; + + if (constant->getOpCode() != opcode) { + continue; + } + + if (constant->getNumOperands() != (int)numMembers) + continue; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +Id Builder::findStructConstant(Id typeId, const std::vector& comps) +{ + Instruction* constant = nullptr; + bool found = false; + for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { + constant = groupedStructConstants[typeId][i]; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +// Comments in header +Id Builder::makeCompositeConstant(Id typeId, const std::vector& members, bool specConstant) +{ + assert(typeId); + Op typeClass = getTypeClass(typeId); + + bool replicate = false; + size_t numMembers = members.size(); + if (useReplicatedComposites || typeClass == OpTypeCooperativeVectorNV) { + // use replicate if all members are the same + replicate = numMembers > 0 && + std::equal(members.begin() + 1, members.end(), members.begin()); + + if (replicate) { + numMembers = 1; + addCapability(spv::CapabilityReplicatedCompositesEXT); + addExtension(spv::E_SPV_EXT_replicated_composites); + } + } + + Op opcode = replicate ? + (specConstant ? OpSpecConstantCompositeReplicateEXT : OpConstantCompositeReplicateEXT) : + (specConstant ? OpSpecConstantComposite : OpConstantComposite); + + switch (typeClass) { + case OpTypeVector: + case OpTypeArray: + case OpTypeMatrix: + case OpTypeCooperativeMatrixKHR: + case OpTypeCooperativeMatrixNV: + case OpTypeCooperativeVectorNV: + if (! specConstant) { + Id existing = findCompositeConstant(typeClass, opcode, typeId, members, numMembers); + if (existing) + return existing; + } + break; + case OpTypeStruct: + if (! specConstant) { + Id existing = findStructConstant(typeId, members); + if (existing) + return existing; + } + break; + default: + assert(0); + return makeFloatConstant(0.0); + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->reserveOperands(members.size()); + for (size_t op = 0; op < numMembers; ++op) + c->addIdOperand(members[op]); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + if (typeClass == OpTypeStruct) + groupedStructConstants[typeId].push_back(c); + else + groupedConstants[typeClass].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) +{ + Instruction* entryPoint = new Instruction(OpEntryPoint); + entryPoint->reserveOperands(3); + entryPoint->addImmediateOperand(model); + entryPoint->addIdOperand(function->getId()); + entryPoint->addStringOperand(name); + + entryPoints.push_back(std::unique_ptr(entryPoint)); + + return entryPoint; +} + +// Currently relying on the fact that all 'value' of interest are small non-negative values. +void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) +{ + // entryPoint can be null if we are in compile-only mode + if (!entryPoint) + return; + + Instruction* instr = new Instruction(OpExecutionMode); + instr->reserveOperands(3); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + if (value1 >= 0) + instr->addImmediateOperand(value1); + if (value2 >= 0) + instr->addImmediateOperand(value2); + if (value3 >= 0) + instr->addImmediateOperand(value3); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector& literals) +{ + // entryPoint can be null if we are in compile-only mode + if (!entryPoint) + return; + + Instruction* instr = new Instruction(OpExecutionMode); + instr->reserveOperands(literals.size() + 2); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + for (auto literal : literals) + instr->addImmediateOperand(literal); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector& operandIds) +{ + // entryPoint can be null if we are in compile-only mode + if (!entryPoint) + return; + + Instruction* instr = new Instruction(OpExecutionModeId); + instr->reserveOperands(operandIds.size() + 2); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + for (auto operandId : operandIds) + instr->addIdOperand(operandId); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addName(Id id, const char* string) +{ + Instruction* name = new Instruction(OpName); + name->reserveOperands(2); + name->addIdOperand(id); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addMemberName(Id id, int memberNumber, const char* string) +{ + Instruction* name = new Instruction(OpMemberName); + name->reserveOperands(3); + name->addIdOperand(id); + name->addImmediateOperand(memberNumber); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addDecoration(Id id, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorate); + dec->reserveOperands(2); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const char* s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateString); + dec->reserveOperands(3); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const std::vector& literals) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorate); + dec->reserveOperands(literals.size() + 2); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + for (auto literal : literals) + dec->addImmediateOperand(literal); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const std::vector& strings) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateString); + dec->reserveOperands(strings.size() + 2); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + for (auto string : strings) + dec->addStringOperand(string); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) { + Instruction* dec = new Instruction(OpDecorate); + dec->reserveOperands(4); + dec->addIdOperand(id); + dec->addImmediateOperand(spv::DecorationLinkageAttributes); + dec->addStringOperand(name); + dec->addImmediateOperand(linkType); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateId); + dec->reserveOperands(3); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addIdOperand(idDecoration); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addDecorationId(Id id, Decoration decoration, const std::vector& operandIds) +{ + if(decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateId); + dec->reserveOperands(operandIds.size() + 2); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + + for (auto operandId : operandIds) + dec->addIdOperand(operandId); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorate); + dec->reserveOperands(3); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); + dec->reserveOperands(4); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector& literals) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorate); + dec->reserveOperands(literals.size() + 3); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + for (auto literal : literals) + dec->addImmediateOperand(literal); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector& strings) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorateString); + dec->reserveOperands(strings.size() + 3); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + for (auto string : strings) + dec->addStringOperand(string); + + decorations.insert(std::unique_ptr(dec)); +} + +void Builder::addInstruction(std::unique_ptr inst) { + // Phis must appear first in their block, don't insert line tracking instructions + // in front of them, just add the OpPhi and return. + if (inst->getOpCode() == OpPhi) { + buildPoint->addInstruction(std::move(inst)); + return; + } + // Optionally insert OpDebugScope + if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker) { + if (buildPoint->updateDebugScope(currentDebugScopeId.top())) { + auto scopeInst = std::make_unique(getUniqueId(), makeVoidType(), OpExtInst); + scopeInst->reserveOperands(3); + scopeInst->addIdOperand(nonSemanticShaderDebugInfo); + scopeInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugScope); + scopeInst->addIdOperand(currentDebugScopeId.top()); + buildPoint->addInstruction(std::move(scopeInst)); + } + + dirtyScopeTracker = false; + } + + // Insert OpLine/OpDebugLine if the debug source location has changed + if (trackDebugInfo && dirtyLineTracker) { + if (buildPoint->updateDebugSourceLocation(currentLine, 0, currentFileId)) { + if (emitSpirvDebugInfo) { + auto lineInst = std::make_unique(OpLine); + lineInst->reserveOperands(3); + lineInst->addIdOperand(currentFileId); + lineInst->addImmediateOperand(currentLine); + lineInst->addImmediateOperand(0); + buildPoint->addInstruction(std::move(lineInst)); + } + if (emitNonSemanticShaderDebugInfo) { + auto lineInst = std::make_unique(getUniqueId(), makeVoidType(), OpExtInst); + lineInst->reserveOperands(7); + lineInst->addIdOperand(nonSemanticShaderDebugInfo); + lineInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLine); + lineInst->addIdOperand(makeDebugSource(currentFileId)); + lineInst->addIdOperand(makeUintConstant(currentLine)); + lineInst->addIdOperand(makeUintConstant(currentLine)); + lineInst->addIdOperand(makeUintConstant(0)); + lineInst->addIdOperand(makeUintConstant(0)); + buildPoint->addInstruction(std::move(lineInst)); + } + } + + dirtyLineTracker = false; + } + + buildPoint->addInstruction(std::move(inst)); +} + +void Builder::addInstructionNoDebugInfo(std::unique_ptr inst) { + buildPoint->addInstruction(std::move(inst)); +} + +// Comments in header +Function* Builder::makeEntryPoint(const char* entryPoint) +{ + assert(! entryPointFunction); + + auto const returnType = makeVoidType(); + + restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; + if(sourceLang == spv::SourceLanguageHLSL) { + emitNonSemanticShaderDebugInfo = false; + } + + Block* entry = nullptr; + entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, {}, {}, &entry); + + emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; + + return entryPointFunction; +} + +// Comments in header +Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType, + const std::vector& paramTypes, + const std::vector>& decorations, Block** entry) +{ + // Make the function and initial instructions in it + Id typeId = makeFunctionType(returnType, paramTypes); + Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); + Id funcId = getUniqueId(); + Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module); + + // Set up the precisions + setPrecision(function->getId(), precision); + function->setReturnPrecision(precision); + for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { + for (int d = 0; d < (int)decorations[p].size(); ++d) { + addDecoration(firstParamId + p, decorations[p][d]); + function->addParamPrecision(p, decorations[p][d]); + } + } + + // reset last debug scope + if (emitNonSemanticShaderDebugInfo) { + dirtyScopeTracker = true; + } + + // CFG + assert(entry != nullptr); + *entry = new Block(getUniqueId(), *function); + function->addBlock(*entry); + setBuildPoint(*entry); + + if (name) + addName(function->getId(), name); + + functions.push_back(std::unique_ptr(function)); + + return function; +} + +void Builder::setupFunctionDebugInfo(Function* function, const char* name, const std::vector& paramTypes, + const std::vector& paramNames) +{ + + if (!emitNonSemanticShaderDebugInfo) + return; + + Id nameId = getStringId(unmangleFunctionName(name)); + Id funcTypeId = function->getFuncTypeId(); + assert(debugId[funcTypeId] != 0); + Id funcId = function->getId(); + + assert(funcId != 0); + + // Make the debug function instruction + Id debugFuncId = makeDebugFunction(function, nameId, funcTypeId); + debugId[funcId] = debugFuncId; + currentDebugScopeId.push(debugFuncId); + + // DebugScope and DebugLine for parameter DebugDeclares + assert(paramTypes.size() == paramNames.size()); + if ((int)paramTypes.size() > 0) { + Id firstParamId = function->getParamId(0); + + for (size_t p = 0; p < paramTypes.size(); ++p) { + bool passByRef = false; + Id paramTypeId = paramTypes[p]; + + // For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type. + if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) { + passByRef = true; + paramTypeId = getContainedTypeId(paramTypeId); + } + + auto const& paramName = paramNames[p]; + auto const debugLocalVariableId = createDebugLocalVariable(debugId[paramTypeId], paramName, p + 1); + auto const paramId = static_cast(firstParamId + p); + debugId[paramId] = debugLocalVariableId; + + if (passByRef) { + makeDebugDeclare(debugLocalVariableId, paramId); + } else { + makeDebugValue(debugLocalVariableId, paramId); + } + } + } + + // Clear debug scope stack + if (emitNonSemanticShaderDebugInfo) + currentDebugScopeId.pop(); +} + +Id Builder::makeDebugFunction([[maybe_unused]] Function* function, Id nameId, Id funcTypeId) +{ + assert(function != nullptr); + assert(nameId != 0); + assert(funcTypeId != 0); + assert(debugId[funcTypeId] != 0); + + Id funcId = getUniqueId(); + auto type = new Instruction(funcId, makeVoidType(), OpExtInst); + type->reserveOperands(11); + type->addIdOperand(nonSemanticShaderDebugInfo); + type->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunction); + type->addIdOperand(nameId); + type->addIdOperand(debugId[funcTypeId]); + type->addIdOperand(makeDebugSource(currentFileId)); // TODO: This points to file of definition instead of declaration + type->addIdOperand(makeUintConstant(currentLine)); // TODO: This points to line of definition instead of declaration + type->addIdOperand(makeUintConstant(0)); // column + type->addIdOperand(makeDebugCompilationUnit()); // scope + type->addIdOperand(nameId); // linkage name + type->addIdOperand(makeUintConstant(NonSemanticShaderDebugInfo100FlagIsPublic)); + type->addIdOperand(makeUintConstant(currentLine)); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + return funcId; +} + +Id Builder::makeDebugLexicalBlock(uint32_t line, uint32_t column) { + assert(!currentDebugScopeId.empty()); + + Id lexId = getUniqueId(); + auto lex = new Instruction(lexId, makeVoidType(), OpExtInst); + lex->reserveOperands(6); + lex->addIdOperand(nonSemanticShaderDebugInfo); + lex->addImmediateOperand(NonSemanticShaderDebugInfo100DebugLexicalBlock); + lex->addIdOperand(makeDebugSource(currentFileId)); + lex->addIdOperand(makeUintConstant(line)); + lex->addIdOperand(makeUintConstant(column)); // column + lex->addIdOperand(currentDebugScopeId.top()); // scope + constantsTypesGlobals.push_back(std::unique_ptr(lex)); + module.mapInstruction(lex); + return lexId; +} + +std::string Builder::unmangleFunctionName(std::string const& name) const +{ + assert(name.length() > 0); + + if(name.rfind('(') != std::string::npos) { + return name.substr(0, name.rfind('(')); + } else { + return name; + } +} + +// Comments in header +void Builder::makeReturn(bool implicit, Id retVal) +{ + if (retVal) { + Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); + inst->addIdOperand(retVal); + addInstruction(std::unique_ptr(inst)); + } else + addInstruction(std::unique_ptr(new Instruction(NoResult, NoType, OpReturn))); + + if (! implicit) + createAndSetNoPredecessorBlock("post-return"); +} + +// Comments in header +void Builder::enterLexicalBlock(uint32_t line, uint32_t column) +{ + if (!emitNonSemanticShaderDebugInfo) { + return; + } + + // Generate new lexical scope debug instruction + Id lexId = makeDebugLexicalBlock(line, column); + currentDebugScopeId.push(lexId); + dirtyScopeTracker = true; +} + +// Comments in header +void Builder::leaveLexicalBlock() +{ + if (!emitNonSemanticShaderDebugInfo) { + return; + } + + // Pop current scope from stack and clear current scope + currentDebugScopeId.pop(); + dirtyScopeTracker = true; +} + +// Comments in header +void Builder::enterFunction(Function const* function) +{ + // Save and disable debugInfo for HLSL entry point function. It is a wrapper + // function with no user code in it. + restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo; + if (sourceLang == spv::SourceLanguageHLSL && function == entryPointFunction) { + emitNonSemanticShaderDebugInfo = false; + } + + if (emitNonSemanticShaderDebugInfo) { + // Initialize scope state + Id funcId = function->getFuncId(); + currentDebugScopeId.push(debugId[funcId]); + // Create DebugFunctionDefinition + spv::Id resultId = getUniqueId(); + Instruction* defInst = new Instruction(resultId, makeVoidType(), OpExtInst); + defInst->reserveOperands(4); + defInst->addIdOperand(nonSemanticShaderDebugInfo); + defInst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugFunctionDefinition); + defInst->addIdOperand(debugId[funcId]); + defInst->addIdOperand(funcId); + addInstruction(std::unique_ptr(defInst)); + } + + if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) { + Id funcId = function->getFuncId(); + addCapability(CapabilityLinkage); + addLinkageDecoration(funcId, function->getExportName(), linkType); + } +} + +// Comments in header +void Builder::leaveFunction() +{ + Block* block = buildPoint; + Function& function = buildPoint->getParent(); + assert(block); + + // If our function did not contain a return, add a return void now. + if (! block->isTerminated()) { + if (function.getReturnType() == makeVoidType()) + makeReturn(true); + else { + makeReturn(true, createUndefined(function.getReturnType())); + } + } + + // Clear function scope from debug scope stack + if (emitNonSemanticShaderDebugInfo) + currentDebugScopeId.pop(); + + emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; +} + +// Comments in header +void Builder::makeStatementTerminator(spv::Op opcode, const char *name) +{ + addInstruction(std::unique_ptr(new Instruction(opcode))); + createAndSetNoPredecessorBlock(name); +} + +// Comments in header +void Builder::makeStatementTerminator(spv::Op opcode, const std::vector& operands, const char* name) +{ + // It's assumed that the terminator instruction is always of void return type + // However in future if there is a need for non void return type, new helper + // methods can be created. + createNoResultOp(opcode, operands); + createAndSetNoPredecessorBlock(name); +} + +// Comments in header +Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer, + bool const compilerGenerated) +{ + Id pointerType = makePointer(storageClass, type); + Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); + inst->addImmediateOperand(storageClass); + if (initializer != NoResult) + inst->addIdOperand(initializer); + + switch (storageClass) { + case StorageClassFunction: + // Validation rules require the declaration in the entry block + buildPoint->getParent().addLocalVariable(std::unique_ptr(inst)); + + if (emitNonSemanticShaderDebugInfo && !compilerGenerated) + { + auto const debugLocalVariableId = createDebugLocalVariable(debugId[type], name); + debugId[inst->getResultId()] = debugLocalVariableId; + + makeDebugDeclare(debugLocalVariableId, inst->getResultId()); + } + + break; + + default: + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + + if (emitNonSemanticShaderDebugInfo) + { + auto const debugResultId = createDebugGlobalVariable(debugId[type], name, inst->getResultId()); + debugId[inst->getResultId()] = debugResultId; + } + break; + } + + if (name) + addName(inst->getResultId(), name); + setPrecision(inst->getResultId(), precision); + + return inst->getResultId(); +} + +// Comments in header +Id Builder::createUndefined(Id type) +{ + Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); + addInstruction(std::unique_ptr(inst)); + return inst->getResultId(); +} + +// av/vis/nonprivate are unnecessary and illegal for some storage classes. +spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const +{ + switch (sc) { + case spv::StorageClassUniform: + case spv::StorageClassWorkgroup: + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + break; + default: + memoryAccess = spv::MemoryAccessMask(memoryAccess & + ~(spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask | + spv::MemoryAccessNonPrivatePointerKHRMask)); + break; + } + return memoryAccess; +} + +// Comments in header +void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, + unsigned int alignment) +{ + Instruction* store = new Instruction(OpStore); + store->reserveOperands(2); + store->addIdOperand(lValue); + store->addIdOperand(rValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + store->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + store->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { + store->addIdOperand(makeUintConstant(scope)); + } + } + + addInstruction(std::unique_ptr(store)); +} + +// Comments in header +Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess, + spv::Scope scope, unsigned int alignment) +{ + Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); + load->addIdOperand(lValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + load->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + load->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { + load->addIdOperand(makeUintConstant(scope)); + } + } + + addInstruction(std::unique_ptr(load)); + setPrecision(load->getResultId(), precision); + + return load->getResultId(); +} + +// Comments in header +Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector& offsets) +{ + // Figure out the final resulting type. + Id typeId = getResultingAccessChainType(); + typeId = makePointer(storageClass, typeId); + + // Make the instruction + Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); + chain->reserveOperands(offsets.size() + 1); + chain->addIdOperand(base); + for (int i = 0; i < (int)offsets.size(); ++i) + chain->addIdOperand(offsets[i]); + addInstruction(std::unique_ptr(chain)); + + return chain->getResultId(); +} + +Id Builder::createArrayLength(Id base, unsigned int member) +{ + spv::Id intType = makeUintType(32); + Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); + length->reserveOperands(2); + length->addIdOperand(base); + length->addImmediateOperand(member); + addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCooperativeMatrixLengthKHR(Id type) +{ + spv::Id intType = makeUintType(32); + + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCooperativeMatrixLengthKHR, intType, std::vector(1, type), std::vector()); + } + + Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthKHR); + length->addIdOperand(type); + addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCooperativeMatrixLengthNV(Id type) +{ + spv::Id intType = makeUintType(32); + + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector(1, type), std::vector()); + } + + Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); + length->addIdOperand(type); + addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), + std::vector(1, index)); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->reserveOperands(2); + extract->addIdOperand(composite); + extract->addImmediateOperand(index); + addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector& indexes) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), indexes); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->reserveOperands(indexes.size() + 1); + extract->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + extract->addImmediateOperand(indexes[i]); + addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->reserveOperands(3); + insert->addIdOperand(object); + insert->addIdOperand(composite); + insert->addImmediateOperand(index); + addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->reserveOperands(indexes.size() + 2); + insert->addIdOperand(object); + insert->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + insert->addImmediateOperand(indexes[i]); + addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) +{ + Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); + extract->reserveOperands(2); + extract->addIdOperand(vector); + extract->addIdOperand(componentIndex); + addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); + insert->reserveOperands(3); + insert->addIdOperand(vector); + insert->addIdOperand(component); + insert->addIdOperand(componentIndex); + addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +// An opcode that has no operands, no result id, and no type +void Builder::createNoResultOp(Op opCode) +{ + Instruction* op = new Instruction(opCode); + addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one id operand, no result id, and no type +void Builder::createNoResultOp(Op opCode, Id operand) +{ + Instruction* op = new Instruction(opCode); + op->addIdOperand(operand); + addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one or more operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + op->reserveOperands(operands.size()); + for (auto id : operands) { + op->addIdOperand(id); + } + addInstruction(std::unique_ptr(op)); +} + +// An opcode that has multiple operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + op->reserveOperands(operands.size()); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + addInstruction(std::unique_ptr(op)); +} + +void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) +{ + Instruction* op = new Instruction(OpControlBarrier); + op->reserveOperands(3); + op->addIdOperand(makeUintConstant(execution)); + op->addIdOperand(makeUintConstant(memory)); + op->addIdOperand(makeUintConstant(semantics)); + addInstruction(std::unique_ptr(op)); +} + +void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) +{ + Instruction* op = new Instruction(OpMemoryBarrier); + op->reserveOperands(2); + op->addIdOperand(makeUintConstant(executionScope)); + op->addIdOperand(makeUintConstant(memorySemantics)); + addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one operands, a result id, and a type +Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(opCode, typeId, std::vector(1, operand), std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(operand); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = left; operands[1] = right; + return createSpecConstantOp(opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->reserveOperands(2); + op->addIdOperand(left); + op->addIdOperand(right); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(3); + operands[0] = op1; + operands[1] = op2; + operands[2] = op3; + return createSpecConstantOp( + opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->reserveOperands(3); + op->addIdOperand(op1); + op->addIdOperand(op2); + op->addIdOperand(op3); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->reserveOperands(operands.size()); + for (auto id : operands) + op->addIdOperand(id); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->reserveOperands(operands.size()); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector& operands, + const std::vector& literals) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); + op->reserveOperands(operands.size() + literals.size() + 1); + op->addImmediateOperand((unsigned) opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + for (auto it = literals.cbegin(); it != literals.cend(); ++it) + op->addImmediateOperand(*it); + module.mapInstruction(op); + constantsTypesGlobals.push_back(std::unique_ptr(op)); + + // OpSpecConstantOp's using 8 or 16 bit types require the associated capability + if (containsType(typeId, OpTypeInt, 8)) + addCapability(CapabilityInt8); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + + return op->getResultId(); +} + +Id Builder::createFunctionCall(spv::Function* function, const std::vector& args) +{ + Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); + op->reserveOperands(args.size() + 1); + op->addIdOperand(function->getId()); + for (int a = 0; a < (int)args.size(); ++a) + op->addIdOperand(args[a]); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Comments in header +Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels) +{ + if (channels.size() == 1) + return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); + + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = operands[1] = source; + return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); + } + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + assert(isVector(source)); + swizzle->reserveOperands(channels.size() + 2); + swizzle->addIdOperand(source); + swizzle->addIdOperand(source); + for (int i = 0; i < (int)channels.size(); ++i) + swizzle->addImmediateOperand(channels[i]); + addInstruction(std::unique_ptr(swizzle)); + + return setPrecision(swizzle->getResultId(), precision); +} + +// Comments in header +Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels) +{ + if (channels.size() == 1 && getNumComponents(source) == 1) + return createCompositeInsert(source, target, typeId, channels.front()); + + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + + assert(isVector(target)); + swizzle->reserveOperands(2); + swizzle->addIdOperand(target); + + assert(getNumComponents(source) == channels.size()); + assert(isVector(source)); + swizzle->addIdOperand(source); + + // Set up an identity shuffle from the base value to the result value + unsigned int components[4]; + int numTargetComponents = getNumComponents(target); + for (int i = 0; i < numTargetComponents; ++i) + components[i] = i; + + // Punch in the l-value swizzle + for (int i = 0; i < (int)channels.size(); ++i) + components[channels[i]] = numTargetComponents + i; + + // finish the instruction with these components selectors + swizzle->reserveOperands(numTargetComponents); + for (int i = 0; i < numTargetComponents; ++i) + swizzle->addImmediateOperand(components[i]); + addInstruction(std::unique_ptr(swizzle)); + + return swizzle->getResultId(); +} + +// Comments in header +void Builder::promoteScalar(Decoration precision, Id& left, Id& right) +{ + int direction = getNumComponents(right) - getNumComponents(left); + + if (direction > 0) + left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); + else if (direction < 0) + right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); + + return; +} + +// Comments in header +Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) +{ + assert(getNumComponents(scalar) == 1); + assert(getTypeId(scalar) == getScalarTypeId(vectorType)); + + int numComponents = getNumTypeComponents(vectorType); + if (numComponents == 1 && !isCooperativeVectorType(vectorType)) + return scalar; + + Instruction* smear = nullptr; + if (generatingOpCodeForSpecConst) { + auto members = std::vector(numComponents, scalar); + // Sometime even in spec-constant-op mode, the temporary vector created by + // promoting a scalar might not be a spec constant. This should depend on + // the scalar. + // e.g.: + // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; + // In such cases, the temporary vector created from a_front_end_const_scalar + // is not a spec constant vector, even though the binary operation node is marked + // as 'specConstant' and we are in spec-constant-op mode. + auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); + smear = module.getInstruction(result_id); + } else { + bool replicate = (useReplicatedComposites || isCooperativeVectorType(vectorType)) && (numComponents > 0); + + if (replicate) { + numComponents = 1; + addCapability(spv::CapabilityReplicatedCompositesEXT); + addExtension(spv::E_SPV_EXT_replicated_composites); + } + + Op opcode = replicate ? OpCompositeConstructReplicateEXT : OpCompositeConstruct; + + smear = new Instruction(getUniqueId(), vectorType, opcode); + smear->reserveOperands(numComponents); + for (int c = 0; c < numComponents; ++c) + smear->addIdOperand(scalar); + addInstruction(std::unique_ptr(smear)); + } + + return setPrecision(smear->getResultId(), precision); +} + +// Comments in header +Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args) +{ + Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); + inst->reserveOperands(args.size() + 2); + inst->addIdOperand(builtins); + inst->addImmediateOperand(entryPoint); + for (int arg = 0; arg < (int)args.size(); ++arg) + inst->addIdOperand(args[arg]); + + addInstruction(std::unique_ptr(inst)); + + return inst->getResultId(); +} + +// Accept all parameters needed to create a texture instruction. +// Create the correct instruction based on the inputs, and make the call. +Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) +{ + std::vector texArgs; + + // + // Set up the fixed arguments + // + bool explicitLod = false; + texArgs.push_back(parameters.sampler); + texArgs.push_back(parameters.coords); + if (parameters.Dref != NoResult) + texArgs.push_back(parameters.Dref); + if (parameters.component != NoResult) + texArgs.push_back(parameters.component); + + if (parameters.granularity != NoResult) + texArgs.push_back(parameters.granularity); + if (parameters.coarse != NoResult) + texArgs.push_back(parameters.coarse); + + // + // Set up the optional arguments + // + size_t optArgNum = texArgs.size(); // the position of the mask for the optional arguments, if any. + ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand + if (parameters.bias) { + mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); + texArgs.push_back(parameters.bias); + } + if (parameters.lod) { + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs.push_back(parameters.lod); + explicitLod = true; + } else if (parameters.gradX) { + mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); + texArgs.push_back(parameters.gradX); + texArgs.push_back(parameters.gradY); + explicitLod = true; + } else if (noImplicitLod && ! fetch && ! gather) { + // have to explicitly use lod of 0 if not allowed to have them be implicit, and + // we would otherwise be about to issue an implicit instruction + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs.push_back(makeFloatConstant(0.0)); + explicitLod = true; + } + if (parameters.offset) { + if (isConstant(parameters.offset)) + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); + else { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); + } + texArgs.push_back(parameters.offset); + } + if (parameters.offsets) { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); + texArgs.push_back(parameters.offsets); + } + if (parameters.sample) { + mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); + texArgs.push_back(parameters.sample); + } + if (parameters.lodClamp) { + // capability if this bit is used + addCapability(CapabilityMinLod); + + mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); + texArgs.push_back(parameters.lodClamp); + } + if (parameters.nonprivate) { + mask = mask | ImageOperandsNonPrivateTexelKHRMask; + } + if (parameters.volatil) { + mask = mask | ImageOperandsVolatileTexelKHRMask; + } + if (parameters.nontemporal) { + mask = mask | ImageOperandsNontemporalMask; + } + mask = mask | signExtensionMask; + // insert the operand for the mask, if any bits were set. + if (mask != ImageOperandsMaskNone) + texArgs.insert(texArgs.begin() + optArgNum, mask); + + // + // Set up the instruction + // + Op opCode = OpNop; // All paths below need to set this + if (fetch) { + if (sparse) + opCode = OpImageSparseFetch; + else + opCode = OpImageFetch; + } else if (parameters.granularity && parameters.coarse) { + opCode = OpImageSampleFootprintNV; + } else if (gather) { + if (parameters.Dref) + if (sparse) + opCode = OpImageSparseDrefGather; + else + opCode = OpImageDrefGather; + else + if (sparse) + opCode = OpImageSparseGather; + else + opCode = OpImageGather; + } else if (explicitLod) { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefExplicitLod; + else + opCode = OpImageSampleProjDrefExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefExplicitLod; + else + opCode = OpImageSampleDrefExplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjExplicitLod; + else + opCode = OpImageSampleProjExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleExplicitLod; + else + opCode = OpImageSampleExplicitLod; + } + } else { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefImplicitLod; + else + opCode = OpImageSampleProjDrefImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefImplicitLod; + else + opCode = OpImageSampleDrefImplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjImplicitLod; + else + opCode = OpImageSampleProjImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleImplicitLod; + else + opCode = OpImageSampleImplicitLod; + } + } + + // See if the result type is expecting a smeared result. + // This happens when a legacy shadow*() call is made, which + // gets a vec4 back instead of a float. + Id smearedType = resultType; + if (! isScalarType(resultType)) { + switch (opCode) { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + resultType = getScalarTypeId(resultType); + break; + default: + break; + } + } + + Id typeId0 = 0; + Id typeId1 = 0; + + if (sparse) { + typeId0 = resultType; + typeId1 = getDerefTypeId(parameters.texelOut); + resultType = makeStructResultType(typeId0, typeId1); + } + + // Build the SPIR-V instruction + Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); + textureInst->reserveOperands(optArgNum + (texArgs.size() - (optArgNum + 1))); + for (size_t op = 0; op < optArgNum; ++op) + textureInst->addIdOperand(texArgs[op]); + if (optArgNum < texArgs.size()) + textureInst->addImmediateOperand(texArgs[optArgNum]); + for (size_t op = optArgNum + 1; op < texArgs.size(); ++op) + textureInst->addIdOperand(texArgs[op]); + setPrecision(textureInst->getResultId(), precision); + addInstruction(std::unique_ptr(textureInst)); + + Id resultId = textureInst->getResultId(); + + if (sparse) { + // set capability + addCapability(CapabilitySparseResidency); + + // Decode the return type that was a special structure + createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); + resultId = createCompositeExtract(resultId, typeId0, 0); + setPrecision(resultId, precision); + } else { + // When a smear is needed, do it, as per what was computed + // above when resultType was changed to a scalar type. + if (resultType != smearedType) + resultId = smearScalar(precision, resultId, smearedType); + } + + return resultId; +} + +// Comments in header +Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) +{ + // Figure out the result type + Id resultType = 0; + switch (opCode) { + case OpImageQuerySize: + case OpImageQuerySizeLod: + { + int numComponents = 0; + switch (getTypeDimensionality(getImageType(parameters.sampler))) { + case Dim1D: + case DimBuffer: + numComponents = 1; + break; + case Dim2D: + case DimCube: + case DimRect: + case DimSubpassData: + numComponents = 2; + break; + case Dim3D: + numComponents = 3; + break; + + default: + assert(0); + break; + } + if (isArrayedImageType(getImageType(parameters.sampler))) + ++numComponents; + + Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + if (numComponents == 1) + resultType = intType; + else + resultType = makeVectorType(intType, numComponents); + + break; + } + case OpImageQueryLod: + resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); + break; + case OpImageQueryLevels: + case OpImageQuerySamples: + resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + break; + default: + assert(0); + break; + } + + Instruction* query = new Instruction(getUniqueId(), resultType, opCode); + query->addIdOperand(parameters.sampler); + if (parameters.coords) + query->addIdOperand(parameters.coords); + if (parameters.lod) + query->addIdOperand(parameters.lod); + addInstruction(std::unique_ptr(query)); + addCapability(CapabilityImageQuery); + + return query->getResultId(); +} + +// External comments in header. +// Operates recursively to visit the composite's hierarchy. +Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) +{ + Id boolType = makeBoolType(); + Id valueType = getTypeId(value1); + + Id resultId = NoResult; + + int numConstituents = getNumTypeConstituents(valueType); + + // Scalars and Vectors + + if (isScalarType(valueType) || isVectorType(valueType)) { + assert(valueType == getTypeId(value2)); + // These just need a single comparison, just have + // to figure out what it is. + Op op; + switch (getMostBasicTypeClass(valueType)) { + case OpTypeFloat: + op = equal ? OpFOrdEqual : OpFUnordNotEqual; + break; + case OpTypeInt: + default: + op = equal ? OpIEqual : OpINotEqual; + break; + case OpTypeBool: + op = equal ? OpLogicalEqual : OpLogicalNotEqual; + precision = NoPrecision; + break; + } + + if (isScalarType(valueType)) { + // scalar + resultId = createBinOp(op, boolType, value1, value2); + } else { + // vector + resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); + setPrecision(resultId, precision); + // reduce vector compares... + resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); + } + + return setPrecision(resultId, precision); + } + + // Only structs, arrays, and matrices should be left. + // They share in common the reduction operation across their constituents. + assert(isAggregateType(valueType) || isMatrixType(valueType)); + + // Compare each pair of constituents + for (int constituent = 0; constituent < numConstituents; ++constituent) { + std::vector indexes(1, constituent); + Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); + Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); + Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); + Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); + + Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); + + if (constituent == 0) + resultId = subResultId; + else + resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), + precision); + } + + return resultId; +} + +// OpCompositeConstruct +Id Builder::createCompositeConstruct(Id typeId, const std::vector& constituents) +{ + assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && + getNumTypeConstituents(typeId) == constituents.size()) || + (isCooperativeVectorType(typeId) && constituents.size() == 1)); + + if (generatingOpCodeForSpecConst) { + // Sometime, even in spec-constant-op mode, the constant composite to be + // constructed may not be a specialization constant. + // e.g.: + // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); + // The first column vector should be a spec constant one, as a_spec_const is a spec constant. + // The second column vector should NOT be spec constant, as it does not contain any spec constants. + // To handle such cases, we check the constituents of the constant vector to determine whether this + // vector should be created as a spec constant. + return makeCompositeConstant(typeId, constituents, + std::any_of(constituents.begin(), constituents.end(), + [&](spv::Id id) { return isSpecConstant(id); })); + } + + bool replicate = false; + size_t numConstituents = constituents.size(); + + if (useReplicatedComposites || isCooperativeVectorType(typeId)) { + replicate = numConstituents > 0 && + std::equal(constituents.begin() + 1, constituents.end(), constituents.begin()); + } + + if (replicate) { + numConstituents = 1; + addCapability(spv::CapabilityReplicatedCompositesEXT); + addExtension(spv::E_SPV_EXT_replicated_composites); + } + + Op opcode = replicate ? OpCompositeConstructReplicateEXT : OpCompositeConstruct; + + Instruction* op = new Instruction(getUniqueId(), typeId, opcode); + op->reserveOperands(constituents.size()); + for (size_t c = 0; c < numConstituents; ++c) + op->addIdOperand(constituents[c]); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// coopmat conversion +Id Builder::createCooperativeMatrixConversion(Id typeId, Id source) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, OpCooperativeMatrixConvertNV); + op->addIdOperand(source); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// coopmat reduce +Id Builder::createCooperativeMatrixReduce(Op opcode, Id typeId, Id source, unsigned int mask, Id func) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opcode); + op->addIdOperand(source); + op->addImmediateOperand(mask); + op->addIdOperand(func); + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// coopmat per-element operation +Id Builder::createCooperativeMatrixPerElementOp(Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, spv::OpCooperativeMatrixPerElementOpNV); + // skip operand[0], which is where the result is stored + for (uint32_t i = 1; i < operands.size(); ++i) { + op->addIdOperand(operands[i]); + } + addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Vector or scalar constructor +Id Builder::createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id result = NoResult; + unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); + unsigned int targetComponent = 0; + + // Special case: when calling a vector constructor with a single scalar + // argument, smear the scalar + if (sources.size() == 1 && isScalar(sources[0]) && (numTargetComponents > 1 || isCooperativeVectorType(resultTypeId))) + return smearScalar(precision, sources[0], resultTypeId); + + // Special case: 2 vectors of equal size + if (sources.size() == 1 && isVector(sources[0]) && numTargetComponents == getNumComponents(sources[0])) { + assert(resultTypeId == getTypeId(sources[0])); + return sources[0]; + } + + // accumulate the arguments for OpCompositeConstruct + std::vector constituents; + Id scalarTypeId = getScalarTypeId(resultTypeId); + + // lambda to store the result of visiting an argument component + const auto latchResult = [&](Id comp) { + if (numTargetComponents > 1) + constituents.push_back(comp); + else + result = comp; + ++targetComponent; + }; + + // lambda to visit a vector argument's components + const auto accumulateVectorConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumComponents(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + for (unsigned int s = 0; s < sourcesToUse; ++s) { + std::vector swiz; + swiz.push_back(s); + latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); + } + }; + + // lambda to visit a matrix argument's components + const auto accumulateMatrixConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + unsigned int col = 0; + unsigned int row = 0; + for (unsigned int s = 0; s < sourcesToUse; ++s) { + if (row >= getNumRows(sourceArg)) { + row = 0; + col++; + } + std::vector indexes; + indexes.push_back(col); + indexes.push_back(row); + latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); + row++; + } + }; + + // Go through the source arguments, each one could have either + // a single or multiple components to contribute. + for (unsigned int i = 0; i < sources.size(); ++i) { + + if (isScalar(sources[i]) || isPointer(sources[i])) + latchResult(sources[i]); + else if (isVector(sources[i]) || isCooperativeVector(sources[i])) + accumulateVectorConstituents(sources[i]); + else if (isMatrix(sources[i])) + accumulateMatrixConstituents(sources[i]); + else + assert(0); + + if (targetComponent >= numTargetComponents) + break; + } + + // If the result is a vector, make it from the gathered constituents. + if (constituents.size() > 0) { + result = createCompositeConstruct(resultTypeId, constituents); + return setPrecision(result, precision); + } else { + // Precision was set when generating this component. + return result; + } +} + +// Comments in header +Id Builder::createMatrixConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id componentTypeId = getScalarTypeId(resultTypeId); + unsigned int numCols = getTypeNumColumns(resultTypeId); + unsigned int numRows = getTypeNumRows(resultTypeId); + + Instruction* instr = module.getInstruction(componentTypeId); + const unsigned bitCount = instr->getImmediateOperand(0); + + // Optimize matrix constructed from a bigger matrix + if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { + // To truncate the matrix to a smaller number of rows/columns, we need to: + // 1. For each column, extract the column and truncate it to the required size using shuffle + // 2. Assemble the resulting matrix from all columns + Id matrix = sources[0]; + Id columnTypeId = getContainedTypeId(resultTypeId); + Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); + + std::vector channels; + for (unsigned int row = 0; row < numRows; ++row) + channels.push_back(row); + + std::vector matrixColumns; + for (unsigned int col = 0; col < numCols; ++col) { + std::vector indexes; + indexes.push_back(col); + Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); + setPrecision(colv, precision); + + if (numRows != getNumRows(matrix)) { + matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); + } else { + matrixColumns.push_back(colv); + } + } + + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); + } + + // Detect a matrix being constructed from a repeated vector of the correct size. + // Create the composite directly from it. + if (sources.size() == numCols && isVector(sources[0]) && getNumComponents(sources[0]) == numRows && + std::equal(sources.begin() + 1, sources.end(), sources.begin())) { + return setPrecision(createCompositeConstruct(resultTypeId, sources), precision); + } + + // Otherwise, will use a two step process + // 1. make a compile-time 2D array of values + // 2. construct a matrix from that array + + // Step 1. + + // initialize the array to the identity matrix + Id ids[maxMatrixSize][maxMatrixSize]; + Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); + Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col == row) + ids[col][row] = one; + else + ids[col][row] = zero; + } + } + + // modify components as dictated by the arguments + if (sources.size() == 1 && isScalar(sources[0])) { + // a single scalar; resets the diagonals + for (int col = 0; col < 4; ++col) + ids[col][col] = sources[0]; + } else if (isMatrix(sources[0])) { + // constructing from another matrix; copy over the parts that exist in both the argument and constructee + Id matrix = sources[0]; + unsigned int minCols = std::min(numCols, getNumColumns(matrix)); + unsigned int minRows = std::min(numRows, getNumRows(matrix)); + for (unsigned int col = 0; col < minCols; ++col) { + std::vector indexes; + indexes.push_back(col); + for (unsigned int row = 0; row < minRows; ++row) { + indexes.push_back(row); + ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); + indexes.pop_back(); + setPrecision(ids[col][row], precision); + } + } + } else { + // fill in the matrix in column-major order with whatever argument components are available + unsigned int row = 0; + unsigned int col = 0; + + for (unsigned int arg = 0; arg < sources.size() && col < numCols; ++arg) { + Id argComp = sources[arg]; + for (unsigned int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { + if (getNumComponents(sources[arg]) > 1) { + argComp = createCompositeExtract(sources[arg], componentTypeId, comp); + setPrecision(argComp, precision); + } + ids[col][row++] = argComp; + if (row == numRows) { + row = 0; + col++; + } + if (col == numCols) { + // If more components are provided than fit the matrix, discard the rest. + break; + } + } + } + } + + // Step 2: Construct a matrix from that array. + // First make the column vectors, then make the matrix. + + // make the column vectors + Id columnTypeId = getContainedTypeId(resultTypeId); + std::vector matrixColumns; + for (unsigned int col = 0; col < numCols; ++col) { + std::vector vectorComponents; + for (unsigned int row = 0; row < numRows; ++row) + vectorComponents.push_back(ids[col][row]); + Id column = createCompositeConstruct(columnTypeId, vectorComponents); + setPrecision(column, precision); + matrixColumns.push_back(column); + } + + // make the matrix + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); +} + +// Comments in header +Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : + builder(gb), + condition(cond), + control(ctrl), + elseBlock(nullptr) +{ + function = &builder.getBuildPoint()->getParent(); + + // make the blocks, but only put the then-block into the function, + // the else-block and merge-block will be added later, in order, after + // earlier code is emitted + thenBlock = new Block(builder.getUniqueId(), *function); + mergeBlock = new Block(builder.getUniqueId(), *function); + + // Save the current block, so that we can add in the flow control split when + // makeEndIf is called. + headerBlock = builder.getBuildPoint(); + builder.createSelectionMerge(mergeBlock, control); + + function->addBlock(thenBlock); + builder.setBuildPoint(thenBlock); +} + +// Comments in header +void Builder::If::makeBeginElse() +{ + // Close out the "then" by having it jump to the mergeBlock + builder.createBranch(true, mergeBlock); + + // Make the first else block and add it to the function + elseBlock = new Block(builder.getUniqueId(), *function); + function->addBlock(elseBlock); + + // Start building the else block + builder.setBuildPoint(elseBlock); +} + +// Comments in header +void Builder::If::makeEndIf() +{ + // jump to the merge block + builder.createBranch(true, mergeBlock); + + // Go back to the headerBlock and make the flow control split + builder.setBuildPoint(headerBlock); + if (elseBlock) + builder.createConditionalBranch(condition, thenBlock, elseBlock); + else + builder.createConditionalBranch(condition, thenBlock, mergeBlock); + + // add the merge block to the function + function->addBlock(mergeBlock); + builder.setBuildPoint(mergeBlock); +} + +// Comments in header +void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueIndexToSegment, int defaultSegment, + std::vector& segmentBlocks) +{ + Function& function = buildPoint->getParent(); + + // make all the blocks + for (int s = 0; s < numSegments; ++s) + segmentBlocks.push_back(new Block(getUniqueId(), function)); + + Block* mergeBlock = new Block(getUniqueId(), function); + + // make and insert the switch's selection-merge instruction + createSelectionMerge(mergeBlock, control); + + // make the switch instruction + Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); + switchInst->reserveOperands((caseValues.size() * 2) + 2); + switchInst->addIdOperand(selector); + auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; + switchInst->addIdOperand(defaultOrMerge->getId()); + defaultOrMerge->addPredecessor(buildPoint); + for (int i = 0; i < (int)caseValues.size(); ++i) { + switchInst->addImmediateOperand(caseValues[i]); + switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); + segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); + } + addInstruction(std::unique_ptr(switchInst)); + + // push the merge block + switchMerges.push(mergeBlock); +} + +// Comments in header +void Builder::addSwitchBreak(bool implicit) +{ + // branch to the top of the merge block stack + createBranch(implicit, switchMerges.top()); + createAndSetNoPredecessorBlock("post-switch-break"); +} + +// Comments in header +void Builder::nextSwitchSegment(std::vector& segmentBlock, int nextSegment) +{ + int lastSegment = nextSegment - 1; + if (lastSegment >= 0) { + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + createBranch(true, segmentBlock[nextSegment]); + } + Block* block = segmentBlock[nextSegment]; + block->getParent().addBlock(block); + setBuildPoint(block); +} + +// Comments in header +void Builder::endSwitch(std::vector& /*segmentBlock*/) +{ + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + addSwitchBreak(true); + + switchMerges.top()->getParent().addBlock(switchMerges.top()); + setBuildPoint(switchMerges.top()); + + switchMerges.pop(); +} + +Block& Builder::makeNewBlock() +{ + Function& function = buildPoint->getParent(); + auto block = new Block(getUniqueId(), function); + function.addBlock(block); + return *block; +} + +Builder::LoopBlocks& Builder::makeNewLoop() +{ + // This verbosity is needed to simultaneously get the same behavior + // everywhere (id's in the same order), have a syntax that works + // across lots of versions of C++, have no warnings from pedantic + // compilation modes, and leave the rest of the code alone. + Block& head = makeNewBlock(); + Block& body = makeNewBlock(); + Block& merge = makeNewBlock(); + Block& continue_target = makeNewBlock(); + LoopBlocks blocks(head, body, merge, continue_target); + loops.push(blocks); + return loops.top(); +} + +void Builder::createLoopContinue() +{ + createBranch(false, &loops.top().continue_target); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-continue"); +} + +void Builder::createLoopExit() +{ + createBranch(false, &loops.top().merge); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-break"); +} + +void Builder::closeLoop() +{ + loops.pop(); +} + +void Builder::clearAccessChain() +{ + accessChain.base = NoResult; + accessChain.indexChain.clear(); + accessChain.instr = NoResult; + accessChain.swizzle.clear(); + accessChain.component = NoResult; + accessChain.preSwizzleBaseType = NoType; + accessChain.isRValue = false; + accessChain.coherentFlags.clear(); + accessChain.alignment = 0; +} + +// Comments in header +void Builder::accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment) +{ + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + + // swizzles can be stacked in GLSL, but simplified to a single + // one here; the base type doesn't change + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + + // if needed, propagate the swizzle for the current access chain + if (accessChain.swizzle.size() > 0) { + std::vector oldSwizzle = accessChain.swizzle; + accessChain.swizzle.resize(0); + for (unsigned int i = 0; i < swizzle.size(); ++i) { + assert(swizzle[i] < oldSwizzle.size()); + accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); + } + } else + accessChain.swizzle = swizzle; + + // determine if we need to track this swizzle anymore + simplifyAccessChainSwizzle(); +} + +// Comments in header +void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + + // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores. + if (accessChain.swizzle.size() > 0 && + getNumTypeComponents(getResultingAccessChainType()) != accessChain.swizzle.size() && + accessChain.component == NoResult) { + for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { + accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i])); + accessChain.instr = NoResult; + + Id base = collapseAccessChain(); + addDecoration(base, nonUniform); + + accessChain.indexChain.pop_back(); + accessChain.instr = NoResult; + + // dynamic component should be gone + assert(accessChain.component == NoResult); + + Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i); + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + createStore(source, base, memoryAccess, scope, alignment); + } + } + else { + Id base = collapseAccessChain(); + addDecoration(base, nonUniform); + + Id source = rvalue; + + // dynamic component should be gone + assert(accessChain.component == NoResult); + + // If swizzle still exists, it may be out-of-order, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. + if (accessChain.swizzle.size() > 0) { + Id tempBaseId = createLoad(base, spv::NoPrecision); + source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); + } + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + createStore(source, base, memoryAccess, scope, alignment); + } +} + +// Comments in header +Id Builder::accessChainLoad(Decoration precision, Decoration l_nonUniform, + Decoration r_nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, + spv::Scope scope, unsigned int alignment) +{ + Id id; + + if (accessChain.isRValue) { + // transfer access chain, but try to stay in registers + transferAccessChainSwizzle(false); + if (accessChain.indexChain.size() > 0) { + Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; + + // if all the accesses are constants, we can use OpCompositeExtract + std::vector indexes; + bool constant = true; + for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { + if (isConstantScalar(accessChain.indexChain[i])) + indexes.push_back(getConstantScalar(accessChain.indexChain[i])); + else { + constant = false; + break; + } + } + + if (constant) { + id = createCompositeExtract(accessChain.base, swizzleBase, indexes); + setPrecision(id, precision); + } else if (isCooperativeVector(accessChain.base)) { + assert(accessChain.indexChain.size() == 1); + id = createVectorExtractDynamic(accessChain.base, resultType, accessChain.indexChain[0]); + } else { + Id lValue = NoResult; + if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) { + // make a new function variable for this r-value, using an initializer, + // and mark it as NonWritable so that downstream it can be detected as a lookup + // table + lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), + "indexable", accessChain.base); + addDecoration(lValue, DecorationNonWritable); + } else { + lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), + "indexable"); + // store into it + createStore(accessChain.base, lValue); + } + // move base to the new variable + accessChain.base = lValue; + accessChain.isRValue = false; + + // load through the access chain + id = createLoad(collapseAccessChain(), precision); + } + } else + id = accessChain.base; // no precision, it was set when this was defined + } else { + transferAccessChainSwizzle(true); + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + // load through the access chain + id = collapseAccessChain(); + // Apply nonuniform both to the access chain and the loaded value. + // Buffer accesses need the access chain decorated, and this is where + // loaded image types get decorated. TODO: This should maybe move to + // createImageTextureFunctionCall. + addDecoration(id, l_nonUniform); + id = createLoad(id, precision, memoryAccess, scope, alignment); + addDecoration(id, r_nonUniform); + } + + // Done, unless there are swizzles to do + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return id; + + // Do remaining swizzling + + // Do the basic swizzle + if (accessChain.swizzle.size() > 0) { + Id swizzledType = getScalarTypeId(getTypeId(id)); + if (accessChain.swizzle.size() > 1) + swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); + id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); + } + + // Do the dynamic component + if (accessChain.component != NoResult) + id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); + + addDecoration(id, r_nonUniform); + return id; +} + +Id Builder::accessChainGetLValue() +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + Id lvalue = collapseAccessChain(); + + // If swizzle exists, it is out-of-order or not full, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. This does not + // go with getting a direct l-value pointer. + assert(accessChain.swizzle.size() == 0); + assert(accessChain.component == NoResult); + + return lvalue; +} + +// comment in header +Id Builder::accessChainGetInferredType() +{ + // anything to operate on? + if (accessChain.base == NoResult) + return NoType; + Id type = getTypeId(accessChain.base); + + // do initial dereference + if (! accessChain.isRValue) + type = getContainedTypeId(type); + + // dereference each index + for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { + if (isStructType(type)) + type = getContainedTypeId(type, getConstantScalar(*it)); + else + type = getContainedTypeId(type); + } + + // dereference swizzle + if (accessChain.swizzle.size() == 1) + type = getContainedTypeId(type); + else if (accessChain.swizzle.size() > 1) + type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); + + // dereference component selection + if (accessChain.component) + type = getContainedTypeId(type); + + return type; +} + +void Builder::dump(std::vector& out) const +{ + // Header, before first instructions: + out.push_back(MagicNumber); + out.push_back(spvVersion); + out.push_back(builderNumber); + out.push_back(uniqueId + 1); + out.push_back(0); + + // Capabilities + for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { + Instruction capInst(0, 0, OpCapability); + capInst.addImmediateOperand(*it); + capInst.dump(out); + } + + for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { + Instruction extInst(0, 0, OpExtension); + extInst.addStringOperand(it->c_str()); + extInst.dump(out); + } + + dumpInstructions(out, imports); + Instruction memInst(0, 0, OpMemoryModel); + memInst.addImmediateOperand(addressModel); + memInst.addImmediateOperand(memoryModel); + memInst.dump(out); + + // Instructions saved up while building: + dumpInstructions(out, entryPoints); + dumpInstructions(out, executionModes); + + // Debug instructions + dumpInstructions(out, strings); + dumpSourceInstructions(out); + for (int e = 0; e < (int)sourceExtensions.size(); ++e) { + Instruction sourceExtInst(0, 0, OpSourceExtension); + sourceExtInst.addStringOperand(sourceExtensions[e]); + sourceExtInst.dump(out); + } + dumpInstructions(out, names); + dumpModuleProcesses(out); + + // Annotation instructions + dumpInstructions(out, decorations); + + dumpInstructions(out, constantsTypesGlobals); + dumpInstructions(out, externals); + + // The functions + module.dump(out); +} + +// +// Protected methods. +// + +// Turn the described access chain in 'accessChain' into an instruction(s) +// computing its address. This *cannot* include complex swizzles, which must +// be handled after this is called. +// +// Can generate code. +Id Builder::collapseAccessChain() +{ + assert(accessChain.isRValue == false); + + // did we already emit an access chain for this? + if (accessChain.instr != NoResult) + return accessChain.instr; + + // If we have a dynamic component, we can still transfer + // that into a final operand to the access chain. We need to remap the + // dynamic component through the swizzle to get a new dynamic component to + // update. + // + // This was not done in transferAccessChainSwizzle() because it might + // generate code. + remapDynamicSwizzle(); + if (accessChain.component != NoResult) { + // transfer the dynamic component to the access chain + accessChain.indexChain.push_back(accessChain.component); + accessChain.component = NoResult; + } + + // note that non-trivial swizzling is left pending + + // do we have an access chain? + if (accessChain.indexChain.size() == 0) + return accessChain.base; + + // emit the access chain + StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); + accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); + + return accessChain.instr; +} + +// For a dynamic component selection of a swizzle. +// +// Turn the swizzle and dynamic component into just a dynamic component. +// +// Generates code. +void Builder::remapDynamicSwizzle() +{ + // do we have a swizzle to remap a dynamic component through? + if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { + // build a vector of the swizzle for the component to map into + std::vector components; + for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) + components.push_back(makeUintConstant(accessChain.swizzle[c])); + Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); + Id map = makeCompositeConstant(mapType, components); + + // use it + accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); + accessChain.swizzle.clear(); + } +} + +// clear out swizzle if it is redundant, that is reselecting the same components +// that would be present without the swizzle. +void Builder::simplifyAccessChainSwizzle() +{ + // If the swizzle has fewer components than the vector, it is subsetting, and must stay + // to preserve that fact. + if (getNumTypeComponents(accessChain.preSwizzleBaseType) > accessChain.swizzle.size()) + return; + + // if components are out of order, it is a swizzle + for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { + if (i != accessChain.swizzle[i]) + return; + } + + // otherwise, there is no need to track this swizzle + accessChain.swizzle.clear(); + if (accessChain.component == NoResult) + accessChain.preSwizzleBaseType = NoType; +} + +// To the extent any swizzling can become part of the chain +// of accesses instead of a post operation, make it so. +// If 'dynamic' is true, include transferring the dynamic component, +// otherwise, leave it pending. +// +// Does not generate code. just updates the access chain. +void Builder::transferAccessChainSwizzle(bool dynamic) +{ + // non existent? + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return; + + // too complex? + // (this requires either a swizzle, or generating code for a dynamic component) + if (accessChain.swizzle.size() > 1) + return; + + // single component, either in the swizzle and/or dynamic component + if (accessChain.swizzle.size() == 1) { + assert(accessChain.component == NoResult); + // handle static component selection + accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); + accessChain.swizzle.clear(); + accessChain.preSwizzleBaseType = NoType; + } else if (dynamic && accessChain.component != NoResult) { + assert(accessChain.swizzle.size() == 0); + // handle dynamic component + accessChain.indexChain.push_back(accessChain.component); + accessChain.preSwizzleBaseType = NoType; + accessChain.component = NoResult; + } +} + +// Utility method for creating a new block and setting the insert point to +// be in it. This is useful for flow-control operations that need a "dummy" +// block proceeding them (e.g. instructions after a discard, etc). +void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) +{ + Block* block = new Block(getUniqueId(), buildPoint->getParent()); + block->setUnreachable(); + buildPoint->getParent().addBlock(block); + setBuildPoint(block); + + // if (name) + // addName(block->getId(), name); +} + +// Comments in header +void Builder::createBranch(bool implicit, Block* block) +{ + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(block->getId()); + if (implicit) { + addInstructionNoDebugInfo(std::unique_ptr(branch)); + } + else { + addInstruction(std::unique_ptr(branch)); + } + block->addPredecessor(buildPoint); +} + +void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) +{ + Instruction* merge = new Instruction(OpSelectionMerge); + merge->reserveOperands(2); + merge->addIdOperand(mergeBlock->getId()); + merge->addImmediateOperand(control); + addInstruction(std::unique_ptr(merge)); +} + +void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands) +{ + Instruction* merge = new Instruction(OpLoopMerge); + merge->reserveOperands(operands.size() + 3); + merge->addIdOperand(mergeBlock->getId()); + merge->addIdOperand(continueBlock->getId()); + merge->addImmediateOperand(control); + for (int op = 0; op < (int)operands.size(); ++op) + merge->addImmediateOperand(operands[op]); + addInstruction(std::unique_ptr(merge)); +} + +void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) +{ + Instruction* branch = new Instruction(OpBranchConditional); + branch->reserveOperands(3); + branch->addIdOperand(condition); + branch->addIdOperand(thenBlock->getId()); + branch->addIdOperand(elseBlock->getId()); + + // A conditional branch is always attached to a condition expression + addInstructionNoDebugInfo(std::unique_ptr(branch)); + + thenBlock->addPredecessor(buildPoint); + elseBlock->addPredecessor(buildPoint); +} + +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, + std::vector& out) const +{ + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + + if (sourceLang != SourceLanguageUnknown) { + // OpSource Language Version File Source + Instruction sourceInst(NoResult, NoType, OpSource); + sourceInst.reserveOperands(3); + sourceInst.addImmediateOperand(sourceLang); + sourceInst.addImmediateOperand(sourceVersion); + // File operand + if (fileId != NoResult) { + sourceInst.addIdOperand(fileId); + // Source operand + if (text.size() > 0) { + int nextByte = 0; + std::string subString; + while ((int)text.size() - nextByte > 0) { + subString = text.substr(nextByte, nonNullBytesPerInstruction); + if (nextByte == 0) { + // OpSource + sourceInst.addStringOperand(subString.c_str()); + sourceInst.dump(out); + } else { + // OpSourcContinued + Instruction sourceContinuedInst(OpSourceContinued); + sourceContinuedInst.addStringOperand(subString.c_str()); + sourceContinuedInst.dump(out); + } + nextByte += nonNullBytesPerInstruction; + } + } else + sourceInst.dump(out); + } else + sourceInst.dump(out); + } +} + +// Dump an OpSource[Continued] sequence for the source and every include file +void Builder::dumpSourceInstructions(std::vector& out) const +{ + if (emitNonSemanticShaderDebugInfo) return; + dumpSourceInstructions(mainFileId, sourceText, out); + for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) + dumpSourceInstructions(iItr->first, *iItr->second, out); +} + +template void Builder::dumpInstructions(std::vector& out, const Range& instructions) const +{ + for (const auto& inst : instructions) { + inst->dump(out); + } +} + +void Builder::dumpModuleProcesses(std::vector& out) const +{ + for (int i = 0; i < (int)moduleProcesses.size(); ++i) { + Instruction moduleProcessed(OpModuleProcessed); + moduleProcessed.addStringOperand(moduleProcesses[i]); + moduleProcessed.dump(out); + } +} + +bool Builder::DecorationInstructionLessThan::operator()(const std::unique_ptr& lhs, + const std::unique_ptr& rhs) const +{ + // Order by the id to which the decoration applies first. This is more intuitive. + assert(lhs->isIdOperand(0) && rhs->isIdOperand(0)); + if (lhs->getIdOperand(0) != rhs->getIdOperand(0)) { + return lhs->getIdOperand(0) < rhs->getIdOperand(0); + } + + if (lhs->getOpCode() != rhs->getOpCode()) + return lhs->getOpCode() < rhs->getOpCode(); + + // Now compare the operands. + int minSize = std::min(lhs->getNumOperands(), rhs->getNumOperands()); + for (int i = 1; i < minSize; ++i) { + if (lhs->isIdOperand(i) != rhs->isIdOperand(i)) { + return lhs->isIdOperand(i) < rhs->isIdOperand(i); + } + + if (lhs->isIdOperand(i)) { + if (lhs->getIdOperand(i) != rhs->getIdOperand(i)) { + return lhs->getIdOperand(i) < rhs->getIdOperand(i); + } + } else { + if (lhs->getImmediateOperand(i) != rhs->getImmediateOperand(i)) { + return lhs->getImmediateOperand(i) < rhs->getImmediateOperand(i); + } + } + } + + if (lhs->getNumOperands() != rhs->getNumOperands()) + return lhs->getNumOperands() < rhs->getNumOperands(); + + // In this case they are equal. + return false; +} +} // end spv namespace diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.h b/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.h new file mode 100644 index 00000000..56c2684c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SpvBuilder.h @@ -0,0 +1,1033 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// "Builder" is an interface to fully build SPIR-V IR. Allocate one of +// these to build (a thread safe) internal SPIR-V representation (IR), +// and then dump it as a binary stream according to the SPIR-V specification. +// +// A Builder has a 1:1 relationship with a SPIR-V module. +// + +#pragma once +#ifndef SpvBuilder_H +#define SpvBuilder_H + +#include "Logger.h" +#define SPV_ENABLE_UTILITY_CODE +#include "spirv.hpp" +#include "spvIR.h" +namespace spv { + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "NonSemanticShaderDebugInfo100.h" +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spv { + +typedef enum { + Spv_1_0 = (1 << 16), + Spv_1_1 = (1 << 16) | (1 << 8), + Spv_1_2 = (1 << 16) | (2 << 8), + Spv_1_3 = (1 << 16) | (3 << 8), + Spv_1_4 = (1 << 16) | (4 << 8), + Spv_1_5 = (1 << 16) | (5 << 8), + Spv_1_6 = (1 << 16) | (6 << 8), +} SpvVersion; + +class Builder { +public: + Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); + virtual ~Builder(); + + static const int maxMatrixSize = 4; + + unsigned int getSpvVersion() const { return spvVersion; } + + void setSource(spv::SourceLanguage lang, int version) + { + sourceLang = lang; + sourceVersion = version; + } + spv::Id getStringId(const std::string& str) + { + auto sItr = stringIds.find(str); + if (sItr != stringIds.end()) + return sItr->second; + spv::Id strId = getUniqueId(); + Instruction* fileString = new Instruction(strId, NoType, OpString); + const char* file_c_str = str.c_str(); + fileString->addStringOperand(file_c_str); + strings.push_back(std::unique_ptr(fileString)); + module.mapInstruction(fileString); + stringIds[file_c_str] = strId; + return strId; + } + + spv::Id getMainFileId() const { return mainFileId; } + + // Initialize the main source file name + void setDebugMainSourceFile(const std::string& file) + { + if (trackDebugInfo) { + dirtyLineTracker = true; + mainFileId = getStringId(file); + currentFileId = mainFileId; + } + } + + // Set the debug source location tracker in the builder. + // The upcoming instructions in basic blocks will be associated to this location. + void setDebugSourceLocation(int line, const char* filename) + { + if (trackDebugInfo) { + dirtyLineTracker = true; + if (line != 0) { + // TODO: This is special handling of some AST nodes having (untracked) line 0. + // But they should have a valid line number. + currentLine = line; + if (filename) { + currentFileId = getStringId(filename); + } + } + } + } + + void setSourceText(const std::string& text) { sourceText = text; } + void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } + void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } + void setEmitSpirvDebugInfo() + { + trackDebugInfo = true; + emitSpirvDebugInfo = true; + } + void setEmitNonSemanticShaderDebugInfo(bool emitSourceText) + { + trackDebugInfo = true; + emitNonSemanticShaderDebugInfo = true; + importNonSemanticShaderDebugInfoInstructions(); + + if (emitSourceText) { + emitNonSemanticShaderDebugSource = emitSourceText; + } + } + void addExtension(const char* ext) { extensions.insert(ext); } + void removeExtension(const char* ext) + { + extensions.erase(ext); + } + void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion) + { + if (getSpvVersion() < static_cast(incorporatedVersion)) + addExtension(ext); + } + void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion) + { + removeExtension(baseExt); + addIncorporatedExtension(promoExt, incorporatedVersion); + } + void addInclude(const std::string& name, const std::string& text) + { + spv::Id incId = getStringId(name); + includeFiles[incId] = &text; + } + Id import(const char*); + void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) + { + addressModel = addr; + memoryModel = mem; + } + + void addCapability(spv::Capability cap) { capabilities.insert(cap); } + + // To get a new for anything needing a new one. + Id getUniqueId() { return ++uniqueId; } + + // To get a set of new s, e.g., for a set of function parameters + Id getUniqueIds(int numIds) + { + Id id = uniqueId + 1; + uniqueId += numIds; + return id; + } + + // For creating new types (will return old type if the requested one was already made). + Id makeVoidType(); + Id makeBoolType(); + Id makePointer(StorageClass, Id pointee); + Id makeForwardPointer(StorageClass); + Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); + Id makeIntegerType(int width, bool hasSign); // generic + Id makeIntType(int width) { return makeIntegerType(width, true); } + Id makeUintType(int width) { return makeIntegerType(width, false); } + Id makeFloatType(int width); + Id makeBFloat16Type(); + Id makeStructType(const std::vector& members, const char* name, bool const compilerGenerated = true); + Id makeStructResultType(Id type0, Id type1); + Id makeVectorType(Id component, int size); + Id makeMatrixType(Id component, int cols, int rows); + Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration + Id makeRuntimeArray(Id element); + Id makeFunctionType(Id returnType, const std::vector& paramTypes); + Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format); + Id makeSamplerType(); + Id makeSampledImageType(Id imageType); + Id makeCooperativeMatrixTypeKHR(Id component, Id scope, Id rows, Id cols, Id use); + Id makeCooperativeMatrixTypeNV(Id component, Id scope, Id rows, Id cols); + Id makeCooperativeMatrixTypeWithSameShape(Id component, Id otherType); + Id makeCooperativeVectorTypeNV(Id componentType, Id components); + Id makeGenericType(spv::Op opcode, std::vector& operands); + + // SPIR-V NonSemantic Shader DebugInfo Instructions + struct DebugTypeLoc { + std::string name {}; + int line {0}; + int column {0}; + }; + std::unordered_map debugTypeLocs; + Id makeDebugInfoNone(); + Id makeBoolDebugType(int const size); + Id makeIntegerDebugType(int const width, bool const hasSign); + Id makeFloatDebugType(int const width); + Id makeSequentialDebugType(Id const baseType, Id const componentCount, NonSemanticShaderDebugInfo100Instructions const sequenceType); + Id makeArrayDebugType(Id const baseType, Id const componentCount); + Id makeVectorDebugType(Id const baseType, int const componentCount); + Id makeMatrixDebugType(Id const vectorType, int const vectorCount, bool columnMajor = true); + Id makeMemberDebugType(Id const memberType, DebugTypeLoc const& debugTypeLoc); + Id makeCompositeDebugType(std::vector const& memberTypes, char const*const name, + NonSemanticShaderDebugInfo100DebugCompositeType const tag, bool const isOpaqueType = false); + Id makePointerDebugType(StorageClass storageClass, Id const baseType); + Id makeForwardPointerDebugType(StorageClass storageClass); + Id makeDebugSource(const Id fileName); + Id makeDebugCompilationUnit(); + Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable); + Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0); + Id makeDebugExpression(); + Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer); + Id makeDebugValue(Id const debugLocalVariable, Id const value); + Id makeDebugFunctionType(Id returnType, const std::vector& paramTypes); + Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId); + Id makeDebugLexicalBlock(uint32_t line, uint32_t column); + std::string unmangleFunctionName(std::string const& name) const; + + // Initialize non-semantic debug information for a function, including those of: + // - The function definition + // - The function parameters + void setupFunctionDebugInfo(Function* function, const char* name, const std::vector& paramTypes, + const std::vector& paramNames); + + // accelerationStructureNV type + Id makeAccelerationStructureType(); + // rayQueryEXT type + Id makeRayQueryType(); + // hitObjectNV type + Id makeHitObjectNVType(); + + // For querying about types. + Id getTypeId(Id resultId) const { return module.getTypeId(resultId); } + Id getDerefTypeId(Id resultId) const; + Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); } + Op getTypeClass(Id typeId) const { return getOpCode(typeId); } + Op getMostBasicTypeClass(Id typeId) const; + unsigned int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); } + unsigned int getNumTypeConstituents(Id typeId) const; + unsigned int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } + Id getScalarTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId, int) const; + StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } + ImageFormat getImageTypeFormat(Id typeId) const + { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } + Id getResultingAccessChainType() const; + Id getIdOperand(Id resultId, int idx) { return module.getInstruction(resultId)->getIdOperand(idx); } + Id getCooperativeVectorNumComponents(Id typeId) const { return module.getInstruction(typeId)->getIdOperand(1); } + + bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); } + bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); } + bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); } + bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); } + bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); } + bool isCooperativeVector(Id resultId)const { return isCooperativeVectorType(getTypeId(resultId)); } + bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); } + bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } + bool isTensorView(Id resultId)const { return isTensorViewType(getTypeId(resultId)); } + + bool isBoolType(Id typeId) + { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } + bool isIntType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } + bool isUintType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } + bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } + bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } + bool isScalarType(Id typeId) const + { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || + getTypeClass(typeId) == OpTypeBool; } + bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } + bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; } + bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; } + bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; } + bool isCooperativeMatrixType(Id typeId)const + { + return getTypeClass(typeId) == OpTypeCooperativeMatrixKHR || getTypeClass(typeId) == OpTypeCooperativeMatrixNV; + } + bool isTensorViewType(Id typeId) const { return getTypeClass(typeId) == OpTypeTensorViewNV; } + bool isCooperativeVectorType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeVectorNV; } + bool isAggregateType(Id typeId) const + { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); } + bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; } + bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; } + bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } + bool containsType(Id typeId, Op typeOp, unsigned int width) const; + bool containsPhysicalStorageBufferOrArray(Id typeId) const; + + bool isConstantOpCode(Op opcode) const; + bool isSpecConstantOpCode(Op opcode) const; + bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } + bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } + bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } + unsigned int getConstantScalar(Id resultId) const + { return module.getInstruction(resultId)->getImmediateOperand(0); } + StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } + + bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; } + bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); } + bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; } + bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); } + // See if a resultId is valid for use as an initializer. + bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); } + + int getScalarTypeWidth(Id typeId) const + { + Id scalarTypeId = getScalarTypeId(typeId); + assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); + return module.getInstruction(scalarTypeId)->getImmediateOperand(0); + } + + unsigned int getTypeNumColumns(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeConstituents(typeId); + } + unsigned int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); } + unsigned int getTypeNumRows(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeComponents(getContainedTypeId(typeId)); + } + unsigned int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); } + + Dim getTypeDimensionality(Id typeId) const + { + assert(isImageType(typeId)); + return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); + } + Id getImageType(Id resultId) const + { + Id typeId = getTypeId(resultId); + assert(isImageType(typeId) || isSampledImageType(typeId)); + return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; + } + bool isArrayedImageType(Id typeId) const + { + assert(isImageType(typeId)); + return module.getInstruction(typeId)->getImmediateOperand(3) != 0; + } + + // For making new constants (will return old constant if the requested one was already made). + Id makeNullConstant(Id typeId); + Id makeBoolConstant(bool b, bool specConstant = false); + Id makeIntConstant(Id typeId, unsigned value, bool specConstant); + Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); + Id makeInt8Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } + Id makeUint8Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(8), u, specConstant); } + Id makeInt16Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } + Id makeUint16Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(16), u, specConstant); } + Id makeIntConstant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } + Id makeUintConstant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(32), u, specConstant); } + Id makeInt64Constant(long long i, bool specConstant = false) + { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } + Id makeUint64Constant(unsigned long long u, bool specConstant = false) + { return makeInt64Constant(makeUintType(64), u, specConstant); } + Id makeFloatConstant(float f, bool specConstant = false); + Id makeDoubleConstant(double d, bool specConstant = false); + Id makeFloat16Constant(float f16, bool specConstant = false); + Id makeBFloat16Constant(float bf16, bool specConstant = false); + Id makeFpConstant(Id type, double d, bool specConstant = false); + + Id importNonSemanticShaderDebugInfoInstructions(); + + // Turn the array of constants into a proper spv constant of the requested type. + Id makeCompositeConstant(Id type, const std::vector& comps, bool specConst = false); + + // Methods for adding information outside the CFG. + Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); + void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); + void addExecutionMode(Function*, ExecutionMode mode, const std::vector& literals); + void addExecutionModeId(Function*, ExecutionMode mode, const std::vector& operandIds); + void addName(Id, const char* name); + void addMemberName(Id, int member, const char* name); + void addDecoration(Id, Decoration, int num = -1); + void addDecoration(Id, Decoration, const char*); + void addDecoration(Id, Decoration, const std::vector& literals); + void addDecoration(Id, Decoration, const std::vector& strings); + void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType); + void addDecorationId(Id id, Decoration, Id idDecoration); + void addDecorationId(Id id, Decoration, const std::vector& operandIds); + void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); + void addMemberDecoration(Id, unsigned int member, Decoration, const char*); + void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector& literals); + void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector& strings); + + // At the end of what block do the next create*() instructions go? + // Also reset current last DebugScope and current source line to unknown + void setBuildPoint(Block* bp) { + buildPoint = bp; + dirtyLineTracker = true; + dirtyScopeTracker = true; + } + Block* getBuildPoint() const { return buildPoint; } + + // Append an instruction to the end of the current build point. + // Optionally, additional debug info instructions may also be prepended. + void addInstruction(std::unique_ptr inst); + + // Append an instruction to the end of the current build point without prepending any debug instructions. + // This is useful for insertion of some debug info instructions themselves or some control flow instructions + // that are attached to its predecessor instruction. + void addInstructionNoDebugInfo(std::unique_ptr inst); + + // Make the entry-point function. The returned pointer is only valid + // for the lifetime of this builder. + Function* makeEntryPoint(const char*); + + // Make a shader-style function, and create its entry block if entry is non-zero. + // Return the function, pass back the entry. + // The returned pointer is only valid for the lifetime of this builder. + Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType, + const std::vector& paramTypes, + const std::vector>& precisions, Block** entry = nullptr); + + // Create a return. An 'implicit' return is one not appearing in the source + // code. In the case of an implicit return, no post-return block is inserted. + void makeReturn(bool implicit, Id retVal = 0); + + // Initialize state and generate instructions for new lexical scope + void enterLexicalBlock(uint32_t line, uint32_t column); + + // Set state and generate instructions to exit current lexical scope + void leaveLexicalBlock(); + + // Prepare builder for generation of instructions for a function. + void enterFunction(Function const* function); + + // Generate all the code needed to finish up a function. + void leaveFunction(); + + // Create block terminator instruction for certain statements like + // discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT + void makeStatementTerminator(spv::Op opcode, const char *name); + + // Create block terminator instruction for statements that have input operands + // such as OpEmitMeshTasksEXT + void makeStatementTerminator(spv::Op opcode, const std::vector& operands, const char* name); + + // Create a global or function local or IO variable. + Id createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name = nullptr, + Id initializer = NoResult, bool const compilerGenerated = true); + + // Create an intermediate with an undefined value. + Id createUndefined(Id type); + + // Store into an Id and return the l-value + void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Load from an Id and return it + Id createLoad(Id lValue, spv::Decoration precision, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Create an OpAccessChain instruction + Id createAccessChain(StorageClass, Id base, const std::vector& offsets); + + // Create an OpArrayLength instruction + Id createArrayLength(Id base, unsigned int member); + + // Create an OpCooperativeMatrixLengthKHR instruction + Id createCooperativeMatrixLengthKHR(Id type); + // Create an OpCooperativeMatrixLengthNV instruction + Id createCooperativeMatrixLengthNV(Id type); + + // Create an OpCompositeExtract instruction + Id createCompositeExtract(Id composite, Id typeId, unsigned index); + Id createCompositeExtract(Id composite, Id typeId, const std::vector& indexes); + Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); + Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes); + + Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); + Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); + + void createNoResultOp(Op); + void createNoResultOp(Op, Id operand); + void createNoResultOp(Op, const std::vector& operands); + void createNoResultOp(Op, const std::vector& operands); + void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); + void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); + Id createUnaryOp(Op, Id typeId, Id operand); + Id createBinOp(Op, Id typeId, Id operand1, Id operand2); + Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createFunctionCall(spv::Function*, const std::vector&); + Id createSpecConstantOp(Op, Id typeId, const std::vector& operands, const std::vector& literals); + + // Take an rvalue (source) and a set of channels to extract from it to + // make a new rvalue, which is returned. + Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels); + + // Take a copy of an lvalue (target) and a source of components, and set the + // source components into the lvalue where the 'channels' say to put them. + // An updated version of the target is returned. + // (No true lvalue or stores are used.) + Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels); + + // If both the id and precision are valid, the id + // gets tagged with the requested precision. + // The passed in id is always the returned id, to simplify use patterns. + Id setPrecision(Id id, Decoration precision) + { + if (precision != NoPrecision && id != NoResult) + addDecoration(id, precision); + + return id; + } + + // Can smear a scalar to a vector for the following forms: + // - promoteScalar(scalar, vector) // smear scalar to width of vector + // - promoteScalar(vector, scalar) // smear scalar to width of vector + // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to + // - promoteScalar(scalar, scalar) // do nothing + // Other forms are not allowed. + // + // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. + // The type of the created vector is a vector of components of the same type as the scalar. + // + // Note: One of the arguments will change, with the result coming back that way rather than + // through the return value. + void promoteScalar(Decoration precision, Id& left, Id& right); + + // Make a value by smearing the scalar to fill the type. + // vectorType should be the correct type for making a vector of scalarVal. + // (No conversions are done.) + Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); + + // Create a call to a built-in function. + Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args); + + // List of parameters used to create a texture operation + struct TextureParameters { + Id sampler; + Id coords; + Id bias; + Id lod; + Id Dref; + Id offset; + Id offsets; + Id gradX; + Id gradY; + Id sample; + Id component; + Id texelOut; + Id lodClamp; + Id granularity; + Id coarse; + bool nonprivate; + bool volatil; + bool nontemporal; + }; + + // Select the correct texture operation based on all inputs, and emit the correct instruction + Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicit, const TextureParameters&, ImageOperandsMask); + + // Emit the OpTextureQuery* instruction that was passed in. + // Figure out the right return value and type, and return it. + Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); + + Id createSamplePositionCall(Decoration precision, Id, Id); + + Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); + Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); + + // Reduction comparison for composites: For equal and not-equal resulting in a scalar. + Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); + + // OpCompositeConstruct + Id createCompositeConstruct(Id typeId, const std::vector& constituents); + + // vector or scalar constructor + Id createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId); + + // matrix constructor + Id createMatrixConstructor(Decoration precision, const std::vector& sources, Id constructee); + + // coopmat conversion + Id createCooperativeMatrixConversion(Id typeId, Id source); + Id createCooperativeMatrixReduce(Op opcode, Id typeId, Id source, unsigned int mask, Id func); + Id createCooperativeMatrixPerElementOp(Id typeId, const std::vector& operands); + + // Helper to use for building nested control flow with if-then-else. + class If { + public: + If(Id condition, unsigned int ctrl, Builder& builder); + ~If() {} + + void makeBeginElse(); + void makeEndIf(); + + private: + If(const If&); + If& operator=(If&); + + Builder& builder; + Id condition; + unsigned int control; + Function* function; + Block* headerBlock; + Block* thenBlock; + Block* elseBlock; + Block* mergeBlock; + }; + + // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing + // any case/default labels, all separated by one or more case/default labels. Each possible + // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this + // number space. How to compute the value is given by 'condition', as in switch(condition). + // + // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. + // + // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). + // + // Returns the right set of basic blocks to start each code segment with, so that the caller's + // recursion stack can hold the memory for it. + // + void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueToSegment, int defaultSegment, std::vector& segmentBB); + + // Add a branch to the innermost switch's merge block. + void addSwitchBreak(bool implicit); + + // Move to the next code segment, passing in the return argument in makeSwitch() + void nextSwitchSegment(std::vector& segmentBB, int segment); + + // Finish off the innermost switch. + void endSwitch(std::vector& segmentBB); + + struct LoopBlocks { + LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : + head(head), body(body), merge(merge), continue_target(continue_target) { } + Block &head, &body, &merge, &continue_target; + private: + LoopBlocks(); + LoopBlocks& operator=(const LoopBlocks&) = delete; + }; + + // Start a new loop and prepare the builder to generate code for it. Until + // closeLoop() is called for this loop, createLoopContinue() and + // createLoopExit() will target its corresponding blocks. + LoopBlocks& makeNewLoop(); + + // Create a new block in the function containing the build point. Memory is + // owned by the function object. + Block& makeNewBlock(); + + // Add a branch to the continue_target of the current (innermost) loop. + void createLoopContinue(); + + // Add an exit (e.g. "break") from the innermost loop that we're currently + // in. + void createLoopExit(); + + // Close the innermost loop that you're in + void closeLoop(); + + // + // Access chain design for an R-Value vs. L-Value: + // + // There is a single access chain the builder is building at + // any particular time. Such a chain can be used to either to a load or + // a store, when desired. + // + // Expressions can be r-values, l-values, or both, or only r-values: + // a[b.c].d = .... // l-value + // ... = a[b.c].d; // r-value, that also looks like an l-value + // ++a[b.c].d; // r-value and l-value + // (x + y)[2]; // r-value only, can't possibly be l-value + // + // Computing an r-value means generating code. Hence, + // r-values should only be computed when they are needed, not speculatively. + // + // Computing an l-value means saving away information for later use in the compiler, + // no code is generated until the l-value is later dereferenced. It is okay + // to speculatively generate an l-value, just not okay to speculatively dereference it. + // + // The base of the access chain (the left-most variable or expression + // from which everything is based) can be set either as an l-value + // or as an r-value. Most efficient would be to set an l-value if one + // is available. If an expression was evaluated, the resulting r-value + // can be set as the chain base. + // + // The users of this single access chain can save and restore if they + // want to nest or manage multiple chains. + // + + struct AccessChain { + Id base; // for l-values, pointer to the base object, for r-values, the base object + std::vector indexChain; + Id instr; // cache the instruction that generates this access chain + std::vector swizzle; // each std::vector element selects the next GLSL component number + Id component; // a dynamic component index, can coexist with a swizzle, + // done after the swizzle, NoResult if not present + Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; + // NoType unless a swizzle or component is present + bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value + unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. + // Only tracks base and (optional) component selection alignment. + + // Accumulate whether anything in the chain of structures has coherent decorations. + struct CoherentFlags { + CoherentFlags() { clear(); } + bool isVolatile() const { return volatil; } + bool isNonUniform() const { return nonUniform; } + bool anyCoherent() const { + return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || + subgroupcoherent || shadercallcoherent; + } + + unsigned coherent : 1; + unsigned devicecoherent : 1; + unsigned queuefamilycoherent : 1; + unsigned workgroupcoherent : 1; + unsigned subgroupcoherent : 1; + unsigned shadercallcoherent : 1; + unsigned nonprivate : 1; + unsigned volatil : 1; + unsigned nontemporal : 1; + unsigned isImage : 1; + unsigned nonUniform : 1; + + void clear() { + coherent = 0; + devicecoherent = 0; + queuefamilycoherent = 0; + workgroupcoherent = 0; + subgroupcoherent = 0; + shadercallcoherent = 0; + nonprivate = 0; + volatil = 0; + nontemporal = 0; + isImage = 0; + nonUniform = 0; + } + + CoherentFlags operator |=(const CoherentFlags &other) { + coherent |= other.coherent; + devicecoherent |= other.devicecoherent; + queuefamilycoherent |= other.queuefamilycoherent; + workgroupcoherent |= other.workgroupcoherent; + subgroupcoherent |= other.subgroupcoherent; + shadercallcoherent |= other.shadercallcoherent; + nonprivate |= other.nonprivate; + volatil |= other.volatil; + nontemporal = other.nontemporal; + isImage |= other.isImage; + nonUniform |= other.nonUniform; + return *this; + } + }; + CoherentFlags coherentFlags; + }; + + // + // the SPIR-V builder maintains a single active chain that + // the following methods operate on + // + + // for external save and restore + AccessChain getAccessChain() { return accessChain; } + void setAccessChain(AccessChain newChain) { accessChain = newChain; } + + // clear accessChain + void clearAccessChain(); + + // set new base as an l-value base + void setAccessChainLValue(Id lValue) + { + assert(isPointer(lValue)); + accessChain.base = lValue; + } + + // set new base value as an r-value + void setAccessChainRValue(Id rValue) + { + accessChain.isRValue = true; + accessChain.base = rValue; + } + + // push offset onto the end of the chain + void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) + { + accessChain.indexChain.push_back(offset); + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // push new swizzle onto the end of any existing swizzle, merging into a single swizzle + void accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment); + + // push a dynamic component selection onto the access chain, only applicable with a + // non-trivial swizzle or no swizzle + void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, + unsigned int alignment) + { + if (accessChain.swizzle.size() != 1) { + accessChain.component = component; + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + } + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // use accessChain and swizzle to store value + void accessChainStore(Id rvalue, Decoration nonUniform, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // use accessChain and swizzle to load an r-value + Id accessChainLoad(Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, + unsigned int alignment = 0); + + // Return whether or not the access chain can be represented in SPIR-V + // as an l-value. + // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. + bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } + + // get the direct pointer for an l-value + Id accessChainGetLValue(); + + // Get the inferred SPIR-V type of the result of the current access chain, + // based on the type of the base and the chain of dereferences. + Id accessChainGetInferredType(); + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + void postProcess(bool compileOnly); + + // Prune unreachable blocks in the CFG and remove unneeded decorations. + void postProcessCFG(); + + // Add capabilities, extensions based on instructions in the module. + void postProcessFeatures(); + // Hook to visit each instruction in a block in a function + void postProcess(Instruction&); + // Hook to visit each non-32-bit sized float/int operation in a block. + void postProcessType(const Instruction&, spv::Id typeId); + // move OpSampledImage instructions to be next to their users. + void postProcessSamplers(); + + void dump(std::vector&) const; + + // Add a branch to the target block. + // If set implicit, the branch instruction shouldn't have debug source location. + void createBranch(bool implicit, Block* block); + void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); + void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands); + + // Sets to generate opcode for specialization constants. + void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } + // Sets to generate opcode for non-specialization constants (normal mode). + void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } + // Check if the builder is generating code for spec constants. + bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } + + void setUseReplicatedComposites(bool use) { useReplicatedComposites = use; } + + protected: + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); + Id findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std::vector& comps, size_t numMembers); + Id findStructConstant(Id typeId, const std::vector& comps); + Id collapseAccessChain(); + void remapDynamicSwizzle(); + void transferAccessChainSwizzle(bool dynamic); + void simplifyAccessChainSwizzle(); + void createAndSetNoPredecessorBlock(const char*); + void createSelectionMerge(Block* mergeBlock, unsigned int control); + void dumpSourceInstructions(std::vector&) const; + void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector&) const; + template void dumpInstructions(std::vector& out, const Range& instructions) const; + void dumpModuleProcesses(std::vector&) const; + spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const; + struct DecorationInstructionLessThan { + bool operator()(const std::unique_ptr& lhs, const std::unique_ptr& rhs) const; + }; + + unsigned int spvVersion; // the version of SPIR-V to emit in the header + SourceLanguage sourceLang; + int sourceVersion; + spv::Id nonSemanticShaderCompilationUnitId {0}; + spv::Id nonSemanticShaderDebugInfo {0}; + spv::Id debugInfoNone {0}; + spv::Id debugExpression {0}; // Debug expression with zero operations. + std::string sourceText; + + // True if an new OpLine/OpDebugLine may need to be inserted. Either: + // 1. The current debug location changed + // 2. The current build point changed + bool dirtyLineTracker; + int currentLine = 0; + // OpString id of the current file name. Always 0 if debug info is off. + spv::Id currentFileId = 0; + // OpString id of the main file name. Always 0 if debug info is off. + spv::Id mainFileId = 0; + + // True if an new OpDebugScope may need to be inserted. Either: + // 1. A new lexical block is pushed + // 2. The current build point changed + bool dirtyScopeTracker; + std::stack currentDebugScopeId; + + // This flag toggles tracking of debug info while building the SPIR-V. + bool trackDebugInfo = false; + // This flag toggles emission of SPIR-V debug instructions, like OpLine and OpSource. + bool emitSpirvDebugInfo = false; + // This flag toggles emission of Non-Semantic Debug extension debug instructions. + bool emitNonSemanticShaderDebugInfo = false; + bool restoreNonSemanticShaderDebugInfo = false; + bool emitNonSemanticShaderDebugSource = false; + + std::set extensions; + std::vector sourceExtensions; + std::vector moduleProcesses; + AddressingModel addressModel; + MemoryModel memoryModel; + std::set capabilities; + int builderNumber; + Module module; + Block* buildPoint; + Id uniqueId; + Function* entryPointFunction; + bool generatingOpCodeForSpecConst; + bool useReplicatedComposites { false }; + AccessChain accessChain; + + // special blocks of instructions for output + std::vector > strings; + std::vector > imports; + std::vector > entryPoints; + std::vector > executionModes; + std::vector > names; + std::set, DecorationInstructionLessThan> decorations; + std::vector > constantsTypesGlobals; + std::vector > externals; + std::vector > functions; + + // not output, internally used for quick & dirty canonical (unique) creation + + // map type opcodes to constant inst. + std::unordered_map> groupedConstants; + // map struct-id to constant instructions + std::unordered_map> groupedStructConstants; + // map type opcodes to type instructions + std::unordered_map> groupedTypes; + // map type opcodes to debug type instructions + std::unordered_map> groupedDebugTypes; + // list of OpConstantNull instructions + std::vector nullConstants; + + // Track which types have explicit layouts, to avoid reusing in storage classes without layout. + // Currently only tracks array types. + std::unordered_set explicitlyLaidOut; + + // stack of switches + std::stack switchMerges; + + // Our loop stack. + std::stack loops; + + // map from strings to their string ids + std::unordered_map stringIds; + + // map from include file name ids to their contents + std::map includeFiles; + + // map from core id to debug id + std::map debugId; + + // map from file name string id to DebugSource id + std::unordered_map debugSourceId; + + // The stream for outputting warnings and errors. + SpvBuildLogger* logger; +}; // end Builder class + +} // end spv namespace + +#endif // SpvBuilder_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SpvPostProcess.cpp b/engine/engine/3rdlibs/glslang/SPIRV/SpvPostProcess.cpp new file mode 100644 index 00000000..4cd01102 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SpvPostProcess.cpp @@ -0,0 +1,551 @@ +// +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Post-processing for SPIR-V IR, in internal form, not standard binary form. +// + +#include +#include + +#include +#include +#include + +#include "SpvBuilder.h" +#include "spirv.hpp" + +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "GLSL.ext.ARM.h" + #include "GLSL.ext.QCOM.h" +} + +namespace spv { + +// Hook to visit each operand type and result type of an instruction. +// Will be called multiple times for one instruction, once for each typed +// operand and the result. +void Builder::postProcessType(const Instruction& inst, Id typeId) +{ + // Characterize the type being questioned + Id basicTypeOp = getMostBasicTypeClass(typeId); + int width = 0; + if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt) + width = getScalarTypeWidth(typeId); + + // Do opcode-specific checks + switch (inst.getOpCode()) { + case OpLoad: + case OpStore: + if (basicTypeOp == OpTypeStruct) { + if (containsType(typeId, OpTypeInt, 8)) + addCapability(CapabilityInt8); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + } else { + StorageClass storageClass = getStorageClass(inst.getIdOperand(0)); + if (width == 8) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + break; + default: + addCapability(CapabilityInt8); + break; + } + } else if (width == 16) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + case StorageClassInput: + case StorageClassOutput: + break; + default: + if (basicTypeOp == OpTypeInt) + addCapability(CapabilityInt16); + if (basicTypeOp == OpTypeFloat) + addCapability(CapabilityFloat16); + break; + } + } + } + break; + case OpCopyObject: + break; + case OpFConvert: + case OpSConvert: + case OpUConvert: + // Look for any 8/16-bit storage capabilities. If there are none, assume that + // the convert instruction requires the Float16/Int8/16 capability. + if (containsType(typeId, OpTypeFloat, 16) || containsType(typeId, OpTypeInt, 16)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStorageInputOutput16 || + cap == spv::CapabilityStoragePushConstant16 || + cap == spv::CapabilityStorageUniformBufferBlock16 || + cap == spv::CapabilityStorageUniform16) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + } + } + if (containsType(typeId, OpTypeInt, 8)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStoragePushConstant8 || + cap == spv::CapabilityUniformAndStorageBuffer8BitAccess || + cap == spv::CapabilityStorageBuffer8BitAccess) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + addCapability(CapabilityInt8); + } + } + break; + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450Frexp: + case GLSLstd450FrexpStruct: + if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, OpTypeInt, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_int16); + break; + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, OpTypeFloat, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + break; + default: + break; + } + break; + case OpAccessChain: + case OpPtrAccessChain: + if (isPointerType(typeId)) + break; + if (basicTypeOp == OpTypeInt) { + if (width == 16) + addCapability(CapabilityInt16); + else if (width == 8) + addCapability(CapabilityInt8); + } + break; + default: + if (basicTypeOp == OpTypeInt) { + if (width == 16) + addCapability(CapabilityInt16); + else if (width == 8) + addCapability(CapabilityInt8); + else if (width == 64) + addCapability(CapabilityInt64); + } else if (basicTypeOp == OpTypeFloat) { + if (width == 16) + addCapability(CapabilityFloat16); + else if (width == 64) + addCapability(CapabilityFloat64); + } + break; + } +} + +// Called for each instruction that resides in a block. +void Builder::postProcess(Instruction& inst) +{ + // Add capabilities based simply on the opcode. + switch (inst.getOpCode()) { + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + addCapability(CapabilityInterpolationFunction); + break; + default: + break; + } + break; + case OpDPdxFine: + case OpDPdyFine: + case OpFwidthFine: + case OpDPdxCoarse: + case OpDPdyCoarse: + case OpFwidthCoarse: + addCapability(CapabilityDerivativeControl); + break; + + case OpImageQueryLod: + case OpImageQuerySize: + case OpImageQuerySizeLod: + case OpImageQuerySamples: + case OpImageQueryLevels: + addCapability(CapabilityImageQuery); + break; + + case OpGroupNonUniformPartitionNV: + addExtension(E_SPV_NV_shader_subgroup_partitioned); + addCapability(CapabilityGroupNonUniformPartitionedNV); + break; + + case OpLoad: + case OpStore: + { + // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain + // index list to compute the misalignment. The pre-existing alignment value + // (set via Builder::AccessChain::alignment) only accounts for the base of + // the reference type and any scalar component selection in the accesschain, + // and this function computes the rest from the SPIR-V Offset decorations. + Instruction *accessChain = module.getInstruction(inst.getIdOperand(0)); + if (accessChain->getOpCode() == OpAccessChain) { + Instruction *base = module.getInstruction(accessChain->getIdOperand(0)); + // Get the type of the base of the access chain. It must be a pointer type. + Id typeId = base->getTypeId(); + Instruction *type = module.getInstruction(typeId); + assert(type->getOpCode() == OpTypePointer); + if (type->getImmediateOperand(0) != StorageClassPhysicalStorageBufferEXT) { + break; + } + // Get the pointee type. + typeId = type->getIdOperand(1); + type = module.getInstruction(typeId); + // Walk the index list for the access chain. For each index, find any + // misalignment that can apply when accessing the member/element via + // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all + // together. + int alignment = 0; + for (int i = 1; i < accessChain->getNumOperands(); ++i) { + Instruction *idx = module.getInstruction(accessChain->getIdOperand(i)); + if (type->getOpCode() == OpTypeStruct) { + assert(idx->getOpCode() == OpConstant); + unsigned int c = idx->getImmediateOperand(0); + + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpMemberDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == c && + (decoration.get()->getImmediateOperand(2) == DecorationOffset || + decoration.get()->getImmediateOperand(2) == DecorationMatrixStride)) { + alignment |= decoration.get()->getImmediateOperand(3); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // get the next member type + typeId = type->getIdOperand(c); + type = module.getInstruction(typeId); + } else if (type->getOpCode() == OpTypeArray || + type->getOpCode() == OpTypeRuntimeArray) { + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == DecorationArrayStride) { + alignment |= decoration.get()->getImmediateOperand(2); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // Get the element type + typeId = type->getIdOperand(0); + type = module.getInstruction(typeId); + } else { + // Once we get to any non-aggregate type, we're done. + break; + } + } + assert(inst.getNumOperands() >= 3); + unsigned int memoryAccess = inst.getImmediateOperand((inst.getOpCode() == OpStore) ? 2 : 1); + assert(memoryAccess & MemoryAccessAlignedMask); + static_cast(memoryAccess); + // Compute the index of the alignment operand. + int alignmentIdx = 2; + if (inst.getOpCode() == OpStore) + alignmentIdx++; + // Merge new and old (mis)alignment + alignment |= inst.getImmediateOperand(alignmentIdx); + // Pick the LSB + alignment = alignment & ~(alignment & (alignment-1)); + // update the Aligned operand + inst.setImmediateOperand(alignmentIdx, alignment); + } + break; + } + + default: + break; + } + + // Checks based on type + if (inst.getTypeId() != NoType) + postProcessType(inst, inst.getTypeId()); + for (int op = 0; op < inst.getNumOperands(); ++op) { + if (inst.isIdOperand(op)) { + // In blocks, these are always result ids, but we are relying on + // getTypeId() to return NoType for things like OpLabel. + if (getTypeId(inst.getIdOperand(op)) != NoType) + postProcessType(inst, getTypeId(inst.getIdOperand(op))); + } + } +} + +// comment in header +void Builder::postProcessCFG() +{ + // reachableBlocks is the set of blockss reached via control flow, or which are + // unreachable continue targert or unreachable merge. + std::unordered_set reachableBlocks; + std::unordered_map headerForUnreachableContinue; + std::unordered_set unreachableMerges; + std::unordered_set unreachableDefinitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, + [&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue] + (Block* b, ReachReason why, Block* header) { + reachableBlocks.insert(b); + if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header; + if (why == ReachDeadMerge) unreachableMerges.insert(b); + }); + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) { + auto ii = b->getInstructions().cbegin(); + ++ii; // Keep potential decorations on the label. + for (; ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } else if (reachableBlocks.count(b) == 0) { + // The normal case for unreachable code. All definitions are considered dead. + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } + } + } + + // Modify unreachable merge blocks and unreachable continue targets. + // Delete their contents. + for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) { + (*mergeIter)->rewriteAsCanonicalUnreachableMerge(); + } + for (auto continueIter = headerForUnreachableContinue.begin(); + continueIter != headerForUnreachableContinue.end(); + ++continueIter) { + Block* continue_target = continueIter->first; + Block* header = continueIter->second; + continue_target->rewriteAsCanonicalUnreachableContinue(header); + } + + // Remove unneeded decorations, for unreachable instructions + for (auto decorationIter = decorations.begin(); decorationIter != decorations.end();) { + Id decorationId = (*decorationIter)->getIdOperand(0); + if (unreachableDefinitions.count(decorationId) != 0) { + decorationIter = decorations.erase(decorationIter); + } else { + ++decorationIter; + } + } +} + +// comment in header +void Builder::postProcessFeatures() { + // Add per-instruction capabilities, extensions, etc., + + // Look for any 8/16 bit type in physical storage buffer class, and set the + // appropriate capability. This happens in createSpvVariable for other storage + // classes, but there isn't always a variable for physical storage buffer. + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + Instruction* type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)StorageClassPhysicalStorageBufferEXT) { + if (containsType(type->getIdOperand(1), OpTypeInt, 8)) { + addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + addCapability(spv::CapabilityStorageBuffer8BitAccess); + } + if (containsType(type->getIdOperand(1), OpTypeInt, 16) || + containsType(type->getIdOperand(1), OpTypeFloat, 16)) { + addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + addCapability(spv::CapabilityStorageBuffer16BitAccess); + } + } + } + + // process all block-contained instructions + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) + postProcess(*ii->get()); + + // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether + // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the + // default. + for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) { + const Instruction& inst = *vi->get(); + Id resultId = inst.getResultId(); + if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) { + bool foundDecoration = false; + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getIdOperand(0) == resultId && + decoration.get()->getOpCode() == OpDecorate && + (decoration.get()->getImmediateOperand(1) == spv::DecorationAliasedPointerEXT || + decoration.get()->getImmediateOperand(1) == spv::DecorationRestrictPointerEXT)) { + foundDecoration = true; + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + if (!foundDecoration) { + addDecoration(resultId, spv::DecorationAliasedPointerEXT); + } + } + } + } + } + + // If any Vulkan memory model-specific functionality is used, update the + // OpMemoryModel to match. + if (capabilities.find(spv::CapabilityVulkanMemoryModelKHR) != capabilities.end()) { + memoryModel = spv::MemoryModelVulkanKHR; + addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); + } + + // Add Aliased decoration if there's more than one Workgroup Block variable. + if (capabilities.find(spv::CapabilityWorkgroupMemoryExplicitLayoutKHR) != capabilities.end()) { + assert(entryPoints.size() == 1); + auto &ep = entryPoints[0]; + + std::vector workgroup_variables; + for (int i = 0; i < (int)ep->getNumOperands(); i++) { + if (!ep->isIdOperand(i)) + continue; + + const Id id = ep->getIdOperand(i); + const Instruction *instr = module.getInstruction(id); + if (instr->getOpCode() != spv::OpVariable) + continue; + + if (instr->getImmediateOperand(0) == spv::StorageClassWorkgroup) + workgroup_variables.push_back(id); + } + + if (workgroup_variables.size() > 1) { + for (size_t i = 0; i < workgroup_variables.size(); i++) + addDecoration(workgroup_variables[i], spv::DecorationAliased); + } + } +} + +// SPIR-V requires that any instruction consuming the result of an OpSampledImage +// be in the same block as the OpSampledImage instruction. This pass goes finds +// uses of OpSampledImage where that is not the case and duplicates the +// OpSampledImage to be immediately before the instruction that consumes it. +// The old OpSampledImage is left in place, potentially with no users. +void Builder::postProcessSamplers() +{ + // first, find all OpSampledImage instructions and store them in a map. + std::map sampledImageInstrs; + for (auto f: module.getFunctions()) { + for (auto b: f->getBlocks()) { + for (auto &i: b->getInstructions()) { + if (i->getOpCode() == spv::OpSampledImage) { + sampledImageInstrs[i->getResultId()] = i.get(); + } + } + } + } + // next find all uses of the given ids and rewrite them if needed. + for (auto f: module.getFunctions()) { + for (auto b: f->getBlocks()) { + auto &instrs = b->getInstructions(); + for (size_t idx = 0; idx < instrs.size(); idx++) { + Instruction *i = instrs[idx].get(); + for (int opnum = 0; opnum < i->getNumOperands(); opnum++) { + // Is this operand of the current instruction the result of an OpSampledImage? + if (i->isIdOperand(opnum) && + sampledImageInstrs.count(i->getIdOperand(opnum))) + { + Instruction *opSampImg = sampledImageInstrs[i->getIdOperand(opnum)]; + if (i->getBlock() != opSampImg->getBlock()) { + Instruction *newInstr = new Instruction(getUniqueId(), + opSampImg->getTypeId(), + spv::OpSampledImage); + newInstr->addIdOperand(opSampImg->getIdOperand(0)); + newInstr->addIdOperand(opSampImg->getIdOperand(1)); + newInstr->setBlock(b); + + // rewrite the user of the OpSampledImage to use the new instruction. + i->setIdOperand(opnum, newInstr->getResultId()); + // insert the new OpSampledImage right before the current instruction. + instrs.insert(instrs.begin() + idx, + std::unique_ptr(newInstr)); + idx++; + } + } + } + } + } + } +} + +// comment in header +void Builder::postProcess(bool compileOnly) +{ + // postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint + if (!compileOnly) + postProcessCFG(); + + postProcessFeatures(); + postProcessSamplers(); +} + +} // end spv namespace diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.cpp b/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.cpp new file mode 100644 index 00000000..d1b411f2 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.cpp @@ -0,0 +1,317 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#if ENABLE_OPT + +#include +#include + +#include "SpvTools.h" +#include "spirv-tools/optimizer.hpp" +#include "glslang/MachineIndependent/localintermediate.h" + +namespace glslang { + +// Translate glslang's view of target versioning to what SPIRV-Tools uses. +spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger) +{ + switch (spvVersion.vulkan) { + case glslang::EShTargetVulkan_1_0: + return spv_target_env::SPV_ENV_VULKAN_1_0; + case glslang::EShTargetVulkan_1_1: + switch (spvVersion.spv) { + case EShTargetSpv_1_0: + case EShTargetSpv_1_1: + case EShTargetSpv_1_2: + case EShTargetSpv_1_3: + return spv_target_env::SPV_ENV_VULKAN_1_1; + case EShTargetSpv_1_4: + return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4; + default: + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_VULKAN_1_1; + } + case glslang::EShTargetVulkan_1_2: + return spv_target_env::SPV_ENV_VULKAN_1_2; + case glslang::EShTargetVulkan_1_3: + return spv_target_env::SPV_ENV_VULKAN_1_3; + case glslang::EShTargetVulkan_1_4: + return spv_target_env::SPV_ENV_VULKAN_1_4; + default: + break; + } + + if (spvVersion.openGl > 0) + return spv_target_env::SPV_ENV_OPENGL_4_5; + + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_UNIVERSAL_1_0; +} + +spv_target_env MapToSpirvToolsEnv(const glslang::TIntermediate& intermediate, spv::SpvBuildLogger* logger) +{ + return MapToSpirvToolsEnv(intermediate.getSpv(), logger); +} + +// Callback passed to spvtools::Optimizer::SetMessageConsumer +void OptimizerMesssageConsumer(spv_message_level_t level, const char *source, + const spv_position_t &position, const char *message) +{ + auto &out = std::cerr; + switch (level) + { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + out << "error: "; + break; + case SPV_MSG_WARNING: + out << "warning: "; + break; + case SPV_MSG_INFO: + case SPV_MSG_DEBUG: + out << "info: "; + break; + default: + break; + } + if (source) + { + out << source << ":"; + } + out << position.line << ":" << position.column << ":" << position.index << ":"; + if (message) + { + out << " " << message; + } + out << std::endl; +} + +// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv) +{ + SpirvToolsDisassemble(out, spirv, spv_target_env::SPV_ENV_UNIVERSAL_1_3); +} + +// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv, + spv_target_env requested_context) +{ + // disassemble + spv_context context = spvContextCreate(requested_context); + spv_text text; + spv_diagnostic diagnostic = nullptr; + spvBinaryToText(context, spirv.data(), spirv.size(), + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, + &text, &diagnostic); + + // dump + if (diagnostic == nullptr) + out << text->str; + else + spvDiagnosticPrint(diagnostic); + + // teardown + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools validator to generated SPIR-V. +void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, bool prelegalization) +{ + // validate + spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spv_const_binary_t binary = { spirv.data(), spirv.size() }; + spv_diagnostic diagnostic = nullptr; + spv_validator_options options = spvValidatorOptionsCreate(); + spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets()); + spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization); + spvValidatorOptionsSetScalarBlockLayout(options, intermediate.usingScalarBlockLayout()); + spvValidatorOptionsSetWorkgroupScalarBlockLayout(options, intermediate.usingScalarBlockLayout()); + spvValidatorOptionsSetAllowOffsetTextureOperand(options, intermediate.usingTextureOffsetNonConst()); + spvValidateWithOptions(context, options, &binary, &diagnostic); + + // report + if (diagnostic != nullptr) { + logger->error("SPIRV-Tools Validation Errors"); + logger->error(diagnostic->error); + } + + // tear down + spvValidatorOptionsDestroy(options); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools optimizer to generated SPIR-V. HLSL SPIR-V is legalized in the process. +void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, const SpvOptions* options) +{ + spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + // If debug (specifically source line info) is being generated, propagate + // line information into all SPIR-V instructions. This avoids loss of + // information when instructions are deleted or moved. Later, remove + // redundant information to minimize final SPRIR-V size. + if (options->stripDebugInfo) { + optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); + } + optimizer.RegisterPass(spvtools::CreateWrapOpKillPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateMergeReturnPass()); + optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass()); + optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass()); + optimizer.RegisterPass(spvtools::CreateScalarReplacementPass()); + optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateBlockMergePass()); + optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateIfConversionPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + optimizer.RegisterPass(spvtools::CreateInterpolateFixupPass()); + if (options->optimizeSize) { + optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass()); + optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsSafePass()); + } + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateCFGCleanupPass()); + + spvtools::OptimizerOptions spvOptOptions; + if (options->optimizerAllowExpandedIDBound) + spvOptOptions.set_max_id_bound(0x3FFFFFFF); + optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); + + if (options->optimizerAllowExpandedIDBound) { + if (spirv.size() > 3 && spirv[3] > kDefaultMaxIdBound) { + spvtools::Optimizer optimizer2(target_env); + optimizer2.SetMessageConsumer(OptimizerMesssageConsumer); + optimizer2.RegisterPass(spvtools::CreateCompactIdsPass()); + optimizer2.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); + } + } +} + +bool SpirvToolsAnalyzeDeadOutputStores(spv_target_env target_env, std::vector& spirv, + std::unordered_set* live_locs, + std::unordered_set* live_builtins, + spv::SpvBuildLogger*) +{ + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + optimizer.RegisterPass(spvtools::CreateAnalyzeLiveInputPass(live_locs, live_builtins)); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(target_env); + spvOptOptions.set_run_validator(false); + return optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +void SpirvToolsEliminateDeadOutputStores(spv_target_env target_env, std::vector& spirv, + std::unordered_set* live_locs, + std::unordered_set* live_builtins, + spv::SpvBuildLogger*) +{ + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputStoresPass(live_locs, live_builtins)); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true)); + optimizer.RegisterPass(spvtools::CreateEliminateDeadOutputComponentsPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass(false, true)); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(target_env); + spvOptOptions.set_run_validator(false); + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +void SpirvToolsEliminateDeadInputComponents(spv_target_env target_env, std::vector& spirv, + spv::SpvBuildLogger*) +{ + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + optimizer.RegisterPass(spvtools::CreateEliminateDeadInputComponentsPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(target_env); + spvOptOptions.set_run_validator(false); + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by +// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if +// optimization is disabled. +void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate, + std::vector& spirv, spv::SpvBuildLogger* logger) +{ + spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +} // end namespace glslang + +#endif diff --git a/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.h b/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.h new file mode 100644 index 00000000..45585723 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/SpvTools.h @@ -0,0 +1,109 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#pragma once +#ifndef GLSLANG_SPV_TOOLS_H +#define GLSLANG_SPV_TOOLS_H + +#if ENABLE_OPT +#include +#include +#include +#include "spirv-tools/libspirv.h" +#endif + +#include "glslang/MachineIndependent/Versions.h" +#include "glslang/Include/visibility.h" +#include "GlslangToSpv.h" +#include "Logger.h" + +namespace glslang { + +#if ENABLE_OPT + +class TIntermediate; + +// Translate glslang's view of target versioning to what SPIRV-Tools uses. +GLSLANG_EXPORT spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger); +GLSLANG_EXPORT spv_target_env MapToSpirvToolsEnv(const glslang::TIntermediate& intermediate, spv::SpvBuildLogger* logger); + +// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment. +GLSLANG_EXPORT void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv); + +// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment. +GLSLANG_EXPORT void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv, + spv_target_env requested_context); + +// Apply the SPIRV-Tools validator to generated SPIR-V. +GLSLANG_EXPORT void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, bool prelegalization); + +// Apply the SPIRV-Tools optimizer to generated SPIR-V. HLSL SPIR-V is legalized in the process. +GLSLANG_EXPORT void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, const SpvOptions*); + +// Apply the SPIRV-Tools EliminateDeadInputComponents pass to generated SPIR-V. Put result in |spirv|. +GLSLANG_EXPORT void SpirvToolsEliminateDeadInputComponents(spv_target_env target_env, std::vector& spirv, + spv::SpvBuildLogger*); + +// Apply the SPIRV-Tools AnalyzeDeadOutputStores pass to generated SPIR-V. Put result in |live_locs|. +// Return true if the result is valid. +GLSLANG_EXPORT bool SpirvToolsAnalyzeDeadOutputStores(spv_target_env target_env, std::vector& spirv, + std::unordered_set* live_locs, + std::unordered_set* live_builtins, + spv::SpvBuildLogger*); + +// Apply the SPIRV-Tools EliminateDeadOutputStores and AggressiveDeadCodeElimination passes to generated SPIR-V using +// |live_locs|. Put result in |spirv|. +GLSLANG_EXPORT void SpirvToolsEliminateDeadOutputStores(spv_target_env target_env, std::vector& spirv, + std::unordered_set* live_locs, + std::unordered_set* live_builtins, + spv::SpvBuildLogger*); + +// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by +// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if +// optimization is disabled. +GLSLANG_EXPORT void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate, + std::vector& spirv, spv::SpvBuildLogger*); + +#endif + +} // end namespace glslang + +#endif // GLSLANG_SPV_TOOLS_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/bitutils.h b/engine/engine/3rdlibs/glslang/SPIRV/bitutils.h new file mode 100644 index 00000000..22e44cec --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/bitutils.h @@ -0,0 +1,81 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_BITUTILS_H_ +#define LIBSPIRV_UTIL_BITUTILS_H_ + +#include +#include + +namespace spvutils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(static_cast(&dest), &source, sizeof(dest)); + return dest; +} + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < sizeof(T) * 8, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +} // namespace spvutils + +#endif // LIBSPIRV_UTIL_BITUTILS_H_ diff --git a/engine/engine/3rdlibs/glslang/SPIRV/disassemble.cpp b/engine/engine/3rdlibs/glslang/SPIRV/disassemble.cpp new file mode 100644 index 00000000..7524ebbd --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/disassemble.cpp @@ -0,0 +1,874 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "disassemble.h" +#include "doc.h" + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.std.450.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "GLSL.ext.ARM.h" + #include "NonSemanticShaderDebugInfo100.h" + #include "GLSL.ext.QCOM.h" + } +} +const char* GlslStd450DebugNames[spv::GLSLstd450Count]; + +namespace spv { + +static const char* GLSLextAMDGetDebugNames(const char*, unsigned); +static const char* GLSLextNVGetDebugNames(const char*, unsigned); +static const char* NonSemanticShaderDebugInfo100GetDebugNames(unsigned); + +static void Kill(std::ostream& out, const char* message) +{ + out << std::endl << "Disassembly failed: " << message << std::endl; + exit(1); +} + +// used to identify the extended instruction library imported when printing +enum ExtInstSet { + GLSL450Inst, + GLSLextAMDInst, + GLSLextNVInst, + OpenCLExtInst, + NonSemanticDebugPrintfExtInst, + NonSemanticDebugBreakExtInst, + NonSemanticShaderDebugInfo100 +}; + +// Container class for a single instance of a SPIR-V stream, with methods for disassembly. +class SpirvStream { +public: + SpirvStream(std::ostream& out, const std::vector& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { } + virtual ~SpirvStream() { } + + void validate(); + void processInstructions(); + +protected: + SpirvStream(const SpirvStream&); + SpirvStream& operator=(const SpirvStream&); + Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; } + + // Output methods + void outputIndent(); + void formatId(Id id, std::stringstream&); + void outputResultId(Id id); + void outputTypeId(Id id); + void outputId(Id id); + void outputMask(OperandClass operandClass, unsigned mask); + void disassembleImmediates(int numOperands); + void disassembleIds(int numOperands); + std::pair decodeString(); + int disassembleString(); + void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands); + + // Data + std::ostream& out; // where to write the disassembly + const std::vector& stream; // the actual word stream + int size; // the size of the word stream + int word; // the next word of the stream to read + + // map each to the instruction that created it + Id bound; + std::vector idInstruction; // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter) + + std::vector idDescriptor; // the best text string known for explaining the + + // schema + unsigned int schema; + + // stack of structured-merge points + std::stack nestedControl; + Id nextNestedControl; // need a slight delay for when we are nested +}; + +void SpirvStream::validate() +{ + size = (int)stream.size(); + if (size < 4) + Kill(out, "stream is too short"); + + // Magic number + if (stream[word++] != MagicNumber) { + out << "Bad magic number"; + return; + } + + // Version + out << "// Module Version " << std::hex << stream[word++] << std::endl; + + // Generator's magic number + out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl; + + // Result bound + bound = stream[word++]; + idInstruction.resize(bound); + idDescriptor.resize(bound); + out << "// Id's are bound by " << bound << std::endl; + out << std::endl; + + // Reserved schema, must be 0 for now + schema = stream[word++]; + if (schema != 0) + Kill(out, "bad schema, must be 0"); +} + +// Loop over all the instructions, in order, processing each. +// Boiler plate for each is handled here directly, the rest is dispatched. +void SpirvStream::processInstructions() +{ + // Instructions + while (word < size) { + int instructionStart = word; + + // Instruction wordCount and opcode + unsigned int firstWord = stream[word]; + unsigned wordCount = firstWord >> WordCountShift; + Op opCode = (Op)(firstWord & OpCodeMask); + int nextInst = word + wordCount; + ++word; + + // Presence of full instruction + if (nextInst > size) + Kill(out, "stream instruction terminated too early"); + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + // Type + Id typeId = 0; + if (InstructionDesc[opCode].hasType()) { + typeId = stream[word++]; + --numOperands; + } + + // Result + Id resultId = 0; + if (InstructionDesc[opCode].hasResult()) { + resultId = stream[word++]; + --numOperands; + + // save instruction for future reference + idInstruction[resultId] = instructionStart; + } + + outputResultId(resultId); + outputTypeId(typeId); + outputIndent(); + + // Hand off the Op and all its operands + disassembleInstruction(resultId, typeId, opCode, numOperands); + if (word != nextInst) { + out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart; + word = nextInst; + } + out << std::endl; + } +} + +void SpirvStream::outputIndent() +{ + for (int i = 0; i < (int)nestedControl.size(); ++i) + out << " "; +} + +void SpirvStream::formatId(Id id, std::stringstream& idStream) +{ + if (id != 0) { + // On instructions with no IDs, this is called with "0", which does not + // have to be within ID bounds on null shaders. + if (id >= bound) + Kill(out, "Bad "); + + idStream << id; + if (idDescriptor[id].size() > 0) + idStream << "(" << idDescriptor[id] << ")"; + } +} + +void SpirvStream::outputResultId(Id id) +{ + const int width = 16; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str(); + if (id != 0) + out << ":"; + else + out << " "; + + if (nestedControl.size() && id == nestedControl.top()) + nestedControl.pop(); +} + +void SpirvStream::outputTypeId(Id id) +{ + const int width = 12; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str() << " "; +} + +void SpirvStream::outputId(Id id) +{ + if (id >= bound) + Kill(out, "Bad "); + + out << id; + if (idDescriptor[id].size() > 0) + out << "(" << idDescriptor[id] << ")"; +} + +void SpirvStream::outputMask(OperandClass operandClass, unsigned mask) +{ + if (mask == 0) + out << "None"; + else { + for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) { + if (mask & (1 << m)) + out << OperandClassParams[operandClass].getName(m) << " "; + } + } +} + +void SpirvStream::disassembleImmediates(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + out << stream[word++]; + if (i < numOperands - 1) + out << " "; + } +} + +void SpirvStream::disassembleIds(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + outputId(stream[word++]); + if (i < numOperands - 1) + out << " "; + } +} + +// decode string from words at current position (non-consuming) +std::pair SpirvStream::decodeString() +{ + std::string res; + int wordPos = word; + char c; + bool done = false; + + do { + unsigned int content = stream[wordPos]; + for (int charCount = 0; charCount < 4; ++charCount) { + c = content & 0xff; + content >>= 8; + if (c == '\0') { + done = true; + break; + } + res += c; + } + ++wordPos; + } while(! done); + + return std::make_pair(wordPos - word, res); +} + +// return the number of operands consumed by the string +int SpirvStream::disassembleString() +{ + out << " \""; + + std::pair decoderes = decodeString(); + + out << decoderes.second; + out << "\""; + + word += decoderes.first; + + return decoderes.first; +} + +static uint32_t popcount(uint32_t mask) +{ + uint32_t count = 0; + while (mask) { + if (mask & 1) { + count++; + } + mask >>= 1; + } + return count; +} + +void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands) +{ + // Process the opcode + + out << (OpcodeString(opCode) + 2); // leave out the "Op" + + if (opCode == OpLoopMerge || opCode == OpSelectionMerge) + nextNestedControl = stream[word]; + else if (opCode == OpBranchConditional || opCode == OpSwitch) { + if (nextNestedControl) { + nestedControl.push(nextNestedControl); + nextNestedControl = 0; + } + } else if (opCode == OpExtInstImport) { + idDescriptor[resultId] = decodeString().second; + } + else { + if (resultId != 0 && idDescriptor[resultId].size() == 0) { + switch (opCode) { + case OpTypeInt: + switch (stream[word]) { + case 8: idDescriptor[resultId] = "int8_t"; break; + case 16: idDescriptor[resultId] = "int16_t"; break; + default: assert(0); [[fallthrough]]; + case 32: idDescriptor[resultId] = "int"; break; + case 64: idDescriptor[resultId] = "int64_t"; break; + } + break; + case OpTypeFloat: + switch (stream[word]) { + case 16: + if (numOperands > 1 && stream[word+1] == spv::FPEncodingBFloat16KHR) { + idDescriptor[resultId] = "bfloat16_t"; + } else { + idDescriptor[resultId] = "float16_t"; + } + break; + default: assert(0); [[fallthrough]]; + case 32: idDescriptor[resultId] = "float"; break; + case 64: idDescriptor[resultId] = "float64_t"; break; + } + break; + case OpTypeBool: + idDescriptor[resultId] = "bool"; + break; + case OpTypeStruct: + idDescriptor[resultId] = "struct"; + break; + case OpTypePointer: + idDescriptor[resultId] = "ptr"; + break; + case OpTypeVector: + if (idDescriptor[stream[word]].size() > 0) { + if (idDescriptor[stream[word]].substr(0,2) == "bf") { + idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 2); + } else { + idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1); + } + if (strstr(idDescriptor[stream[word]].c_str(), "8")) { + idDescriptor[resultId].append("8"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "16")) { + idDescriptor[resultId].append("16"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "64")) { + idDescriptor[resultId].append("64"); + } + } + idDescriptor[resultId].append("vec"); + switch (stream[word + 1]) { + case 2: idDescriptor[resultId].append("2"); break; + case 3: idDescriptor[resultId].append("3"); break; + case 4: idDescriptor[resultId].append("4"); break; + case 8: idDescriptor[resultId].append("8"); break; + case 16: idDescriptor[resultId].append("16"); break; + case 32: idDescriptor[resultId].append("32"); break; + default: break; + } + break; + default: + break; + } + } + } + + // Process the operands. Note, a new context-dependent set could be + // swapped in mid-traversal. + + // Handle images specially, so can put out helpful strings. + if (opCode == OpTypeImage) { + out << " "; + disassembleIds(1); + out << " " << DimensionString((Dim)stream[word++]); + out << (stream[word++] != 0 ? " depth" : ""); + out << (stream[word++] != 0 ? " array" : ""); + out << (stream[word++] != 0 ? " multi-sampled" : ""); + switch (stream[word++]) { + case 0: out << " runtime"; break; + case 1: out << " sampled"; break; + case 2: out << " nonsampled"; break; + } + out << " format:" << ImageFormatString((ImageFormat)stream[word++]); + + if (numOperands == 8) { + out << " " << AccessQualifierString(stream[word++]); + } + return; + } + + // Handle all the parameterized operands + for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) { + out << " "; + OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op); + switch (operandClass) { + case OperandId: + case OperandScope: + case OperandMemorySemantics: + disassembleIds(1); + --numOperands; + // Get names for printing "(XXX)" for readability, *after* this id + if (opCode == OpName) + idDescriptor[stream[word - 1]] = decodeString().second; + break; + case OperandVariableIds: + disassembleIds(numOperands); + return; + case OperandImageOperands: + outputMask(OperandImageOperands, stream[word++]); + --numOperands; + disassembleIds(numOperands); + return; + case OperandOptionalLiteral: + case OperandVariableLiterals: + if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) || + (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) { + out << BuiltInString(stream[word++]); + --numOperands; + ++op; + } + disassembleImmediates(numOperands); + return; + case OperandVariableIdLiteral: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " Type "; + disassembleIds(1); + out << ", member "; + disassembleImmediates(1); + numOperands -= 2; + } + return; + case OperandVariableLiteralId: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " case "; + disassembleImmediates(1); + out << ": "; + disassembleIds(1); + numOperands -= 2; + } + return; + case OperandLiteralNumber: + disassembleImmediates(1); + --numOperands; + if (opCode == OpExtInst) { + ExtInstSet extInstSet = GLSL450Inst; + const char* name = idDescriptor[stream[word - 2]].c_str(); + if (strcmp("OpenCL.std", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) { + extInstSet = NonSemanticDebugPrintfExtInst; + } else if (strcmp("NonSemantic.DebugBreak", name) == 0) { + extInstSet = NonSemanticDebugBreakExtInst; + } else if (strcmp("NonSemantic.Shader.DebugInfo.100", name) == 0) { + extInstSet = NonSemanticShaderDebugInfo100; + } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 || + strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) { + extInstSet = GLSLextAMDInst; + } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 || + strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 || + strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 || + strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 || + strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 || + strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) { + extInstSet = GLSLextNVInst; + } + unsigned entrypoint = stream[word - 1]; + if (extInstSet == GLSL450Inst) { + if (entrypoint < GLSLstd450Count) { + out << "(" << GlslStd450DebugNames[entrypoint] << ")"; + } + } else if (extInstSet == GLSLextAMDInst) { + out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")"; + } + else if (extInstSet == GLSLextNVInst) { + out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")"; + } else if (extInstSet == NonSemanticDebugPrintfExtInst) { + out << "(DebugPrintf)"; + } else if (extInstSet == NonSemanticDebugBreakExtInst) { + out << "(DebugBreak)"; + } else if (extInstSet == NonSemanticShaderDebugInfo100) { + out << "(" << NonSemanticShaderDebugInfo100GetDebugNames(entrypoint) << ")"; + } + } + break; + case OperandOptionalLiteralString: + case OperandLiteralString: + numOperands -= disassembleString(); + break; + case OperandVariableLiteralStrings: + while (numOperands > 0) + numOperands -= disassembleString(); + return; + case OperandMemoryAccess: + { + outputMask(OperandMemoryAccess, stream[word++]); + --numOperands; + // Put a space after "None" if there are any remaining operands + if (numOperands && stream[word-1] == 0) { + out << " "; + } + uint32_t mask = stream[word-1]; + // Aligned is the only memory access operand that uses an immediate + // value, and it is also the first operand that uses a value at all. + if (mask & MemoryAccessAlignedMask) { + disassembleImmediates(1); + numOperands--; + if (numOperands) + out << " "; + } + + uint32_t bitCount = popcount(mask & (MemoryAccessMakePointerAvailableMask | MemoryAccessMakePointerVisibleMask)); + disassembleIds(bitCount); + numOperands -= bitCount; + } + break; + case OperandTensorAddressingOperands: + { + outputMask(OperandTensorAddressingOperands, stream[word++]); + --numOperands; + // Put a space after "None" if there are any remaining operands + if (numOperands && stream[word-1] == 0) { + out << " "; + } + uint32_t bitCount = popcount(stream[word-1]); + disassembleIds(bitCount); + numOperands -= bitCount; + } + break; + default: + assert(operandClass >= OperandSource && operandClass < OperandOpcode); + + if (OperandClassParams[operandClass].bitmask) + outputMask(operandClass, stream[word++]); + else + out << OperandClassParams[operandClass].getName(stream[word++]); + --numOperands; + + break; + } + } + + return; +} + +static void GLSLstd450GetDebugNames(const char** names) +{ + for (int i = 0; i < GLSLstd450Count; ++i) + names[i] = "Unknown"; + + names[GLSLstd450Round] = "Round"; + names[GLSLstd450RoundEven] = "RoundEven"; + names[GLSLstd450Trunc] = "Trunc"; + names[GLSLstd450FAbs] = "FAbs"; + names[GLSLstd450SAbs] = "SAbs"; + names[GLSLstd450FSign] = "FSign"; + names[GLSLstd450SSign] = "SSign"; + names[GLSLstd450Floor] = "Floor"; + names[GLSLstd450Ceil] = "Ceil"; + names[GLSLstd450Fract] = "Fract"; + names[GLSLstd450Radians] = "Radians"; + names[GLSLstd450Degrees] = "Degrees"; + names[GLSLstd450Sin] = "Sin"; + names[GLSLstd450Cos] = "Cos"; + names[GLSLstd450Tan] = "Tan"; + names[GLSLstd450Asin] = "Asin"; + names[GLSLstd450Acos] = "Acos"; + names[GLSLstd450Atan] = "Atan"; + names[GLSLstd450Sinh] = "Sinh"; + names[GLSLstd450Cosh] = "Cosh"; + names[GLSLstd450Tanh] = "Tanh"; + names[GLSLstd450Asinh] = "Asinh"; + names[GLSLstd450Acosh] = "Acosh"; + names[GLSLstd450Atanh] = "Atanh"; + names[GLSLstd450Atan2] = "Atan2"; + names[GLSLstd450Pow] = "Pow"; + names[GLSLstd450Exp] = "Exp"; + names[GLSLstd450Log] = "Log"; + names[GLSLstd450Exp2] = "Exp2"; + names[GLSLstd450Log2] = "Log2"; + names[GLSLstd450Sqrt] = "Sqrt"; + names[GLSLstd450InverseSqrt] = "InverseSqrt"; + names[GLSLstd450Determinant] = "Determinant"; + names[GLSLstd450MatrixInverse] = "MatrixInverse"; + names[GLSLstd450Modf] = "Modf"; + names[GLSLstd450ModfStruct] = "ModfStruct"; + names[GLSLstd450FMin] = "FMin"; + names[GLSLstd450SMin] = "SMin"; + names[GLSLstd450UMin] = "UMin"; + names[GLSLstd450FMax] = "FMax"; + names[GLSLstd450SMax] = "SMax"; + names[GLSLstd450UMax] = "UMax"; + names[GLSLstd450FClamp] = "FClamp"; + names[GLSLstd450SClamp] = "SClamp"; + names[GLSLstd450UClamp] = "UClamp"; + names[GLSLstd450FMix] = "FMix"; + names[GLSLstd450Step] = "Step"; + names[GLSLstd450SmoothStep] = "SmoothStep"; + names[GLSLstd450Fma] = "Fma"; + names[GLSLstd450Frexp] = "Frexp"; + names[GLSLstd450FrexpStruct] = "FrexpStruct"; + names[GLSLstd450Ldexp] = "Ldexp"; + names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8"; + names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8"; + names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16"; + names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16"; + names[GLSLstd450PackHalf2x16] = "PackHalf2x16"; + names[GLSLstd450PackDouble2x32] = "PackDouble2x32"; + names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16"; + names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16"; + names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16"; + names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8"; + names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8"; + names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32"; + names[GLSLstd450Length] = "Length"; + names[GLSLstd450Distance] = "Distance"; + names[GLSLstd450Cross] = "Cross"; + names[GLSLstd450Normalize] = "Normalize"; + names[GLSLstd450FaceForward] = "FaceForward"; + names[GLSLstd450Reflect] = "Reflect"; + names[GLSLstd450Refract] = "Refract"; + names[GLSLstd450FindILsb] = "FindILsb"; + names[GLSLstd450FindSMsb] = "FindSMsb"; + names[GLSLstd450FindUMsb] = "FindUMsb"; + names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid"; + names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample"; + names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset"; + names[GLSLstd450NMin] = "NMin"; + names[GLSLstd450NMax] = "NMax"; + names[GLSLstd450NClamp] = "NClamp"; +} + +static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) { + switch (entrypoint) { + case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD"; + case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD"; + case WriteInvocationAMD: return "WriteInvocationAMD"; + case MbcntAMD: return "MbcntAMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) { + switch (entrypoint) { + case FMin3AMD: return "FMin3AMD"; + case UMin3AMD: return "UMin3AMD"; + case SMin3AMD: return "SMin3AMD"; + case FMax3AMD: return "FMax3AMD"; + case UMax3AMD: return "UMax3AMD"; + case SMax3AMD: return "SMax3AMD"; + case FMid3AMD: return "FMid3AMD"; + case UMid3AMD: return "UMid3AMD"; + case SMid3AMD: return "SMid3AMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) { + switch (entrypoint) { + case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD"; + default: return "Bad"; + } + } + else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) { + switch (entrypoint) { + case CubeFaceIndexAMD: return "CubeFaceIndexAMD"; + case CubeFaceCoordAMD: return "CubeFaceCoordAMD"; + case TimeAMD: return "TimeAMD"; + default: + break; + } + } + + return "Bad"; +} + +static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 || + strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 || + strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 || + strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 || + strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 || + strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 || + strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 || + strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) { + switch (entrypoint) { + // NV builtins + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInBaryCoordNV: return "BaryCoordNV"; + case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + + // NV Capabilities + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV"; + + // NV Decorations + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerVertexNV: return "PerVertexNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + + default: return "Bad"; + } + } + return "Bad"; +} + +static const char* NonSemanticShaderDebugInfo100GetDebugNames(unsigned entrypoint) +{ + switch (entrypoint) { + case NonSemanticShaderDebugInfo100DebugInfoNone: return "DebugInfoNone"; + case NonSemanticShaderDebugInfo100DebugCompilationUnit: return "DebugCompilationUnit"; + case NonSemanticShaderDebugInfo100DebugTypeBasic: return "DebugTypeBasic"; + case NonSemanticShaderDebugInfo100DebugTypePointer: return "DebugTypePointer"; + case NonSemanticShaderDebugInfo100DebugTypeQualifier: return "DebugTypeQualifier"; + case NonSemanticShaderDebugInfo100DebugTypeArray: return "DebugTypeArray"; + case NonSemanticShaderDebugInfo100DebugTypeVector: return "DebugTypeVector"; + case NonSemanticShaderDebugInfo100DebugTypedef: return "DebugTypedef"; + case NonSemanticShaderDebugInfo100DebugTypeFunction: return "DebugTypeFunction"; + case NonSemanticShaderDebugInfo100DebugTypeEnum: return "DebugTypeEnum"; + case NonSemanticShaderDebugInfo100DebugTypeComposite: return "DebugTypeComposite"; + case NonSemanticShaderDebugInfo100DebugTypeMember: return "DebugTypeMember"; + case NonSemanticShaderDebugInfo100DebugTypeInheritance: return "DebugTypeInheritance"; + case NonSemanticShaderDebugInfo100DebugTypePtrToMember: return "DebugTypePtrToMember"; + case NonSemanticShaderDebugInfo100DebugTypeTemplate: return "DebugTypeTemplate"; + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter: return "DebugTypeTemplateParameter"; + case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter: return "DebugTypeTemplateTemplateParameter"; + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack: return "DebugTypeTemplateParameterPack"; + case NonSemanticShaderDebugInfo100DebugGlobalVariable: return "DebugGlobalVariable"; + case NonSemanticShaderDebugInfo100DebugFunctionDeclaration: return "DebugFunctionDeclaration"; + case NonSemanticShaderDebugInfo100DebugFunction: return "DebugFunction"; + case NonSemanticShaderDebugInfo100DebugLexicalBlock: return "DebugLexicalBlock"; + case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator: return "DebugLexicalBlockDiscriminator"; + case NonSemanticShaderDebugInfo100DebugScope: return "DebugScope"; + case NonSemanticShaderDebugInfo100DebugNoScope: return "DebugNoScope"; + case NonSemanticShaderDebugInfo100DebugInlinedAt: return "DebugInlinedAt"; + case NonSemanticShaderDebugInfo100DebugLocalVariable: return "DebugLocalVariable"; + case NonSemanticShaderDebugInfo100DebugInlinedVariable: return "DebugInlinedVariable"; + case NonSemanticShaderDebugInfo100DebugDeclare: return "DebugDeclare"; + case NonSemanticShaderDebugInfo100DebugValue: return "DebugValue"; + case NonSemanticShaderDebugInfo100DebugOperation: return "DebugOperation"; + case NonSemanticShaderDebugInfo100DebugExpression: return "DebugExpression"; + case NonSemanticShaderDebugInfo100DebugMacroDef: return "DebugMacroDef"; + case NonSemanticShaderDebugInfo100DebugMacroUndef: return "DebugMacroUndef"; + case NonSemanticShaderDebugInfo100DebugImportedEntity: return "DebugImportedEntity"; + case NonSemanticShaderDebugInfo100DebugSource: return "DebugSource"; + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: return "DebugFunctionDefinition"; + case NonSemanticShaderDebugInfo100DebugSourceContinued: return "DebugSourceContinued"; + case NonSemanticShaderDebugInfo100DebugLine: return "DebugLine"; + case NonSemanticShaderDebugInfo100DebugNoLine: return "DebugNoLine"; + case NonSemanticShaderDebugInfo100DebugBuildIdentifier: return "DebugBuildIdentifier"; + case NonSemanticShaderDebugInfo100DebugStoragePath: return "DebugStoragePath"; + case NonSemanticShaderDebugInfo100DebugEntryPoint: return "DebugEntryPoint"; + case NonSemanticShaderDebugInfo100DebugTypeMatrix: return "DebugTypeMatrix"; + default: return "Bad"; + } + + return "Bad"; +} + +void Disassemble(std::ostream& out, const std::vector& stream) +{ + SpirvStream SpirvStream(out, stream); + spv::Parameterize(); + GLSLstd450GetDebugNames(GlslStd450DebugNames); + SpirvStream.validate(); + SpirvStream.processInstructions(); +} + +} // end namespace spv diff --git a/engine/engine/3rdlibs/glslang/SPIRV/disassemble.h b/engine/engine/3rdlibs/glslang/SPIRV/disassemble.h new file mode 100644 index 00000000..3bded14f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/disassemble.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#pragma once +#ifndef disassembler_H +#define disassembler_H + +#include +#include + +#include "glslang/Include/visibility.h" + +namespace spv { + + // disassemble with glslang custom disassembler + GLSLANG_EXPORT void Disassemble(std::ostream& out, const std::vector&); + +} // end namespace spv + +#endif // disassembler_H diff --git a/engine/engine/3rdlibs/glslang/SPIRV/doc.cpp b/engine/engine/3rdlibs/glslang/SPIRV/doc.cpp new file mode 100644 index 00000000..788c6e69 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/doc.cpp @@ -0,0 +1,3794 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2022-2025 Arm Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// 1) Programmatically fill in instruction/operand information. +// This can be used for disassembly, printing documentation, etc. +// +// 2) Print documentation from this parameterization. +// + +#include "doc.h" + +#include +#include +#include +#include + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "GLSL.ext.ARM.h" + #include "GLSL.ext.QCOM.h" + } +} + +namespace spv { + +// +// Whole set of functions that translate enumerants to their text strings for +// the specification (or their sanitized versions for auto-generating the +// spirv headers. +// +// Also, for masks the ceilings are declared next to these, to help keep them in sync. +// Ceilings should be +// - one more than the maximum value an enumerant takes on, for non-mask enumerants +// (for non-sparse enums, this is the number of enumerants) +// - the number of bits consumed by the set of masks +// (for non-sparse mask enums, this is the number of enumerants) +// + +const char* SourceString(int source) +{ + switch (source) { + case 0: return "Unknown"; + case 1: return "ESSL"; + case 2: return "GLSL"; + case 3: return "OpenCL_C"; + case 4: return "OpenCL_CPP"; + case 5: return "HLSL"; + + default: return "Bad"; + } +} + +const char* ExecutionModelString(int model) +{ + switch (model) { + case 0: return "Vertex"; + case 1: return "TessellationControl"; + case 2: return "TessellationEvaluation"; + case 3: return "Geometry"; + case 4: return "Fragment"; + case 5: return "GLCompute"; + case 6: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + case ExecutionModelTaskEXT: return "TaskEXT"; + case ExecutionModelMeshEXT: return "MeshEXT"; + + default: return "Bad"; + + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + } +} + +const char* AddressingString(int addr) +{ + switch (addr) { + case 0: return "Logical"; + case 1: return "Physical32"; + case 2: return "Physical64"; + + case AddressingModelPhysicalStorageBuffer64EXT: return "PhysicalStorageBuffer64EXT"; + + default: return "Bad"; + } +} + +const char* MemoryString(int mem) +{ + switch (mem) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkanKHR: return "VulkanKHR"; + + default: return "Bad"; + } +} + +const int ExecutionModeCeiling = 40; + +const char* ExecutionModeString(int mode) +{ + switch (mode) { + case 0: return "Invocations"; + case 1: return "SpacingEqual"; + case 2: return "SpacingFractionalEven"; + case 3: return "SpacingFractionalOdd"; + case 4: return "VertexOrderCw"; + case 5: return "VertexOrderCcw"; + case 6: return "PixelCenterInteger"; + case 7: return "OriginUpperLeft"; + case 8: return "OriginLowerLeft"; + case 9: return "EarlyFragmentTests"; + case 10: return "PointMode"; + case 11: return "Xfb"; + case 12: return "DepthReplacing"; + case 13: return "Bad"; + case 14: return "DepthGreater"; + case 15: return "DepthLess"; + case 16: return "DepthUnchanged"; + case 17: return "LocalSize"; + case 18: return "LocalSizeHint"; + case 19: return "InputPoints"; + case 20: return "InputLines"; + case 21: return "InputLinesAdjacency"; + case 22: return "Triangles"; + case 23: return "InputTrianglesAdjacency"; + case 24: return "Quads"; + case 25: return "Isolines"; + case 26: return "OutputVertices"; + case 27: return "OutputPoints"; + case 28: return "OutputLineStrip"; + case 29: return "OutputTriangleStrip"; + case 30: return "VecTypeHint"; + case 31: return "ContractionOff"; + case 32: return "Bad"; + + case ExecutionModeInitializer: return "Initializer"; + case ExecutionModeFinalizer: return "Finalizer"; + case ExecutionModeSubgroupSize: return "SubgroupSize"; + case ExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionModeLocalSizeId: return "LocalSizeId"; + case ExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + + case ExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case ExecutionModeDenormPreserve: return "DenormPreserve"; + case ExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case ExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case ExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case ExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case ExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case ExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case ExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case ExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlow"; + case ExecutionModeMaximallyReconvergesKHR: return "MaximallyReconverges"; + + case ExecutionModeOutputLinesNV: return "OutputLinesNV"; + case ExecutionModeOutputPrimitivesNV: return "OutputPrimitivesNV"; + case ExecutionModeOutputTrianglesNV: return "OutputTrianglesNV"; + case ExecutionModeDerivativeGroupQuadsNV: return "DerivativeGroupQuadsNV"; + case ExecutionModeDerivativeGroupLinearNV: return "DerivativeGroupLinearNV"; + + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + + case ExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + + case ExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case ExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + + case ExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case ExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case ExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + + case ExecutionModeCeiling: + default: return "Bad"; + } +} + +const char* StorageClassString(int StorageClass) +{ + switch (StorageClass) { + case 0: return "UniformConstant"; + case 1: return "Input"; + case 2: return "Uniform"; + case 3: return "Output"; + case 4: return "Workgroup"; + case 5: return "CrossWorkgroup"; + case 6: return "Private"; + case 7: return "Function"; + case 8: return "Generic"; + case 9: return "PushConstant"; + case 10: return "AtomicCounter"; + case 11: return "Image"; + case 12: return "StorageBuffer"; + + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + + case StorageClassPhysicalStorageBufferEXT: return "PhysicalStorageBufferEXT"; + case StorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case StorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case StorageClassTileImageEXT: return "TileImageEXT"; + default: return "Bad"; + } +} + +const int DecorationCeiling = 45; + +const char* DecorationString(int decoration) +{ + switch (decoration) { + case 0: return "RelaxedPrecision"; + case 1: return "SpecId"; + case 2: return "Block"; + case 3: return "BufferBlock"; + case 4: return "RowMajor"; + case 5: return "ColMajor"; + case 6: return "ArrayStride"; + case 7: return "MatrixStride"; + case 8: return "GLSLShared"; + case 9: return "GLSLPacked"; + case 10: return "CPacked"; + case 11: return "BuiltIn"; + case 12: return "Bad"; + case 13: return "NoPerspective"; + case 14: return "Flat"; + case 15: return "Patch"; + case 16: return "Centroid"; + case 17: return "Sample"; + case 18: return "Invariant"; + case 19: return "Restrict"; + case 20: return "Aliased"; + case 21: return "Volatile"; + case 22: return "Constant"; + case 23: return "Coherent"; + case 24: return "NonWritable"; + case 25: return "NonReadable"; + case 26: return "Uniform"; + case 27: return "Bad"; + case 28: return "SaturatedConversion"; + case 29: return "Stream"; + case 30: return "Location"; + case 31: return "Component"; + case 32: return "Index"; + case 33: return "Binding"; + case 34: return "DescriptorSet"; + case 35: return "Offset"; + case 36: return "XfbBuffer"; + case 37: return "XfbStride"; + case 38: return "FuncParamAttr"; + case 39: return "FP Rounding Mode"; + case 40: return "FP Fast Math Mode"; + case 41: return "Linkage Attributes"; + case 42: return "NoContraction"; + case 43: return "InputAttachmentIndex"; + case 44: return "Alignment"; + + case DecorationCeiling: + default: return "Bad"; + + case DecorationWeightTextureQCOM: return "DecorationWeightTextureQCOM"; + case DecorationBlockMatchTextureQCOM: return "DecorationBlockMatchTextureQCOM"; + case DecorationBlockMatchSamplerQCOM: return "DecorationBlockMatchSamplerQCOM"; + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + + case DecorationPerVertexKHR: return "PerVertexKHR"; + + case DecorationNonUniformEXT: return "DecorationNonUniformEXT"; + case DecorationHlslCounterBufferGOOGLE: return "DecorationHlslCounterBufferGOOGLE"; + case DecorationHlslSemanticGOOGLE: return "DecorationHlslSemanticGOOGLE"; + case DecorationRestrictPointerEXT: return "DecorationRestrictPointerEXT"; + case DecorationAliasedPointerEXT: return "DecorationAliasedPointerEXT"; + + case DecorationHitObjectShaderRecordBufferNV: return "DecorationHitObjectShaderRecordBufferNV"; + } +} + +const char* BuiltInString(int builtIn) +{ + switch (builtIn) { + case 0: return "Position"; + case 1: return "PointSize"; + case 2: return "Bad"; + case 3: return "ClipDistance"; + case 4: return "CullDistance"; + case 5: return "VertexId"; + case 6: return "InstanceId"; + case 7: return "PrimitiveId"; + case 8: return "InvocationId"; + case 9: return "Layer"; + case 10: return "ViewportIndex"; + case 11: return "TessLevelOuter"; + case 12: return "TessLevelInner"; + case 13: return "TessCoord"; + case 14: return "PatchVertices"; + case 15: return "FragCoord"; + case 16: return "PointCoord"; + case 17: return "FrontFacing"; + case 18: return "SampleId"; + case 19: return "SamplePosition"; + case 20: return "SampleMask"; + case 21: return "Bad"; + case 22: return "FragDepth"; + case 23: return "HelperInvocation"; + case 24: return "NumWorkgroups"; + case 25: return "WorkgroupSize"; + case 26: return "WorkgroupId"; + case 27: return "LocalInvocationId"; + case 28: return "GlobalInvocationId"; + case 29: return "LocalInvocationIndex"; + case 30: return "WorkDim"; + case 31: return "GlobalSize"; + case 32: return "EnqueuedWorkgroupSize"; + case 33: return "GlobalOffset"; + case 34: return "GlobalLinearId"; + case 35: return "Bad"; + case 36: return "SubgroupSize"; + case 37: return "SubgroupMaxSize"; + case 38: return "NumSubgroups"; + case 39: return "NumEnqueuedSubgroups"; + case 40: return "SubgroupId"; + case 41: return "SubgroupLocalInvocationId"; + case 42: return "VertexIndex"; // TBD: put next to VertexId? + case 43: return "InstanceIndex"; // TBD: put next to InstanceId? + + case 4416: return "SubgroupEqMaskKHR"; + case 4417: return "SubgroupGeMaskKHR"; + case 4418: return "SubgroupGtMaskKHR"; + case 4419: return "SubgroupLeMaskKHR"; + case 4420: return "SubgroupLtMaskKHR"; + case 4438: return "DeviceIndex"; + case 4440: return "ViewIndex"; + case 4424: return "BaseVertex"; + case 4425: return "BaseInstance"; + case 4426: return "DrawIndex"; + case 4432: return "PrimitiveShadingRateKHR"; + case 4444: return "ShadingRateKHR"; + case 5014: return "FragStencilRefEXT"; + + case 4992: return "BaryCoordNoPerspAMD"; + case 4993: return "BaryCoordNoPerspCentroidAMD"; + case 4994: return "BaryCoordNoPerspSampleAMD"; + case 4995: return "BaryCoordSmoothAMD"; + case 4996: return "BaryCoordSmoothCentroidAMD"; + case 4997: return "BaryCoordSmoothSampleAMD"; + case 4998: return "BaryCoordPullModelAMD"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInCullMaskKHR: return "CullMaskKHR"; + case BuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case BuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case BuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case BuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case BuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case BuiltInHitIsSphereNV: return "HitIsSphereNV"; + case BuiltInHitIsLSSNV: return "HitIsLSSNV"; + case BuiltInHitSpherePositionNV: return "HitSpherePositionNV"; + case BuiltInHitSphereRadiusNV: return "HitSphereRadiusNV"; + case BuiltInHitLSSPositionsNV: return "HitLSSPositionsNV"; + case BuiltInHitLSSRadiiNV: return "HitLLSSRadiiNV"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTNV: return "HitTNV"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; +// case BuiltInFragmentSizeNV: return "FragmentSizeNV"; // superseded by BuiltInFragSizeEXT +// case BuiltInInvocationsPerPixelNV: return "InvocationsPerPixelNV"; // superseded by BuiltInFragInvocationCountEXT + case BuiltInBaryCoordKHR: return "BaryCoordKHR"; + case BuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case BuiltInClusterIDNV: return "ClusterIDNV"; + + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case 5264: return "FullyCoveredEXT"; + + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + case BuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case BuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case BuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case BuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case BuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case BuiltInCoreCountARM: return "CoreCountARM"; + case BuiltInCoreIDARM: return "CoreIDARM"; + case BuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case BuiltInWarpIDARM: return "WarpIDARM"; + case BuiltInWarpMaxIDARM: return "BuiltInWarpMaxIDARM"; + + default: return "Bad"; + } +} + +const char* DimensionString(int dim) +{ + switch (dim) { + case 0: return "1D"; + case 1: return "2D"; + case 2: return "3D"; + case 3: return "Cube"; + case 4: return "Rect"; + case 5: return "Buffer"; + case 6: return "SubpassData"; + case DimTileImageDataEXT: return "TileImageDataEXT"; + + default: return "Bad"; + } +} + +const char* SamplerAddressingModeString(int mode) +{ + switch (mode) { + case 0: return "None"; + case 1: return "ClampToEdge"; + case 2: return "Clamp"; + case 3: return "Repeat"; + case 4: return "RepeatMirrored"; + + default: return "Bad"; + } +} + +const char* SamplerFilterModeString(int mode) +{ + switch (mode) { + case 0: return "Nearest"; + case 1: return "Linear"; + + default: return "Bad"; + } +} + +const char* ImageFormatString(int format) +{ + switch (format) { + case 0: return "Unknown"; + + // ES/Desktop float + case 1: return "Rgba32f"; + case 2: return "Rgba16f"; + case 3: return "R32f"; + case 4: return "Rgba8"; + case 5: return "Rgba8Snorm"; + + // Desktop float + case 6: return "Rg32f"; + case 7: return "Rg16f"; + case 8: return "R11fG11fB10f"; + case 9: return "R16f"; + case 10: return "Rgba16"; + case 11: return "Rgb10A2"; + case 12: return "Rg16"; + case 13: return "Rg8"; + case 14: return "R16"; + case 15: return "R8"; + case 16: return "Rgba16Snorm"; + case 17: return "Rg16Snorm"; + case 18: return "Rg8Snorm"; + case 19: return "R16Snorm"; + case 20: return "R8Snorm"; + + // ES/Desktop int + case 21: return "Rgba32i"; + case 22: return "Rgba16i"; + case 23: return "Rgba8i"; + case 24: return "R32i"; + + // Desktop int + case 25: return "Rg32i"; + case 26: return "Rg16i"; + case 27: return "Rg8i"; + case 28: return "R16i"; + case 29: return "R8i"; + + // ES/Desktop uint + case 30: return "Rgba32ui"; + case 31: return "Rgba16ui"; + case 32: return "Rgba8ui"; + case 33: return "R32ui"; + + // Desktop uint + case 34: return "Rgb10a2ui"; + case 35: return "Rg32ui"; + case 36: return "Rg16ui"; + case 37: return "Rg8ui"; + case 38: return "R16ui"; + case 39: return "R8ui"; + case 40: return "R64ui"; + case 41: return "R64i"; + + default: + return "Bad"; + } +} + +const char* ImageChannelOrderString(int format) +{ + switch (format) { + case 0: return "R"; + case 1: return "A"; + case 2: return "RG"; + case 3: return "RA"; + case 4: return "RGB"; + case 5: return "RGBA"; + case 6: return "BGRA"; + case 7: return "ARGB"; + case 8: return "Intensity"; + case 9: return "Luminance"; + case 10: return "Rx"; + case 11: return "RGx"; + case 12: return "RGBx"; + case 13: return "Depth"; + case 14: return "DepthStencil"; + case 15: return "sRGB"; + case 16: return "sRGBx"; + case 17: return "sRGBA"; + case 18: return "sBGRA"; + + default: + return "Bad"; + } +} + +const char* ImageChannelDataTypeString(int type) +{ + switch (type) + { + case 0: return "SnormInt8"; + case 1: return "SnormInt16"; + case 2: return "UnormInt8"; + case 3: return "UnormInt16"; + case 4: return "UnormShort565"; + case 5: return "UnormShort555"; + case 6: return "UnormInt101010"; + case 7: return "SignedInt8"; + case 8: return "SignedInt16"; + case 9: return "SignedInt32"; + case 10: return "UnsignedInt8"; + case 11: return "UnsignedInt16"; + case 12: return "UnsignedInt32"; + case 13: return "HalfFloat"; + case 14: return "Float"; + case 15: return "UnormInt24"; + case 16: return "UnormInt101010_2"; + + default: + return "Bad"; + } +} + +const int ImageOperandsCeiling = 15; + +const char* ImageOperandsString(int format) +{ + switch (format) { + case ImageOperandsBiasShift: return "Bias"; + case ImageOperandsLodShift: return "Lod"; + case ImageOperandsGradShift: return "Grad"; + case ImageOperandsConstOffsetShift: return "ConstOffset"; + case ImageOperandsOffsetShift: return "Offset"; + case ImageOperandsConstOffsetsShift: return "ConstOffsets"; + case ImageOperandsSampleShift: return "Sample"; + case ImageOperandsMinLodShift: return "MinLod"; + case ImageOperandsMakeTexelAvailableKHRShift: return "MakeTexelAvailableKHR"; + case ImageOperandsMakeTexelVisibleKHRShift: return "MakeTexelVisibleKHR"; + case ImageOperandsNonPrivateTexelKHRShift: return "NonPrivateTexelKHR"; + case ImageOperandsVolatileTexelKHRShift: return "VolatileTexelKHR"; + case ImageOperandsSignExtendShift: return "SignExtend"; + case ImageOperandsZeroExtendShift: return "ZeroExtend"; + case ImageOperandsNontemporalShift: return "Nontemporal"; + + case ImageOperandsCeiling: + default: + return "Bad"; + } +} + +const char* FPFastMathString(int mode) +{ + switch (mode) { + case 0: return "NotNaN"; + case 1: return "NotInf"; + case 2: return "NSZ"; + case 3: return "AllowRecip"; + case 4: return "Fast"; + + default: return "Bad"; + } +} + +const char* FPRoundingModeString(int mode) +{ + switch (mode) { + case 0: return "RTE"; + case 1: return "RTZ"; + case 2: return "RTP"; + case 3: return "RTN"; + + default: return "Bad"; + } +} + +const char* LinkageTypeString(int type) +{ + switch (type) { + case 0: return "Export"; + case 1: return "Import"; + + default: return "Bad"; + } +} + +const char* FuncParamAttrString(int attr) +{ + switch (attr) { + case 0: return "Zext"; + case 1: return "Sext"; + case 2: return "ByVal"; + case 3: return "Sret"; + case 4: return "NoAlias"; + case 5: return "NoCapture"; + case 6: return "NoWrite"; + case 7: return "NoReadWrite"; + + default: return "Bad"; + } +} + +const char* AccessQualifierString(int attr) +{ + switch (attr) { + case 0: return "ReadOnly"; + case 1: return "WriteOnly"; + case 2: return "ReadWrite"; + + default: return "Bad"; + } +} + +const int SelectControlCeiling = 2; + +const char* SelectControlString(int cont) +{ + switch (cont) { + case 0: return "Flatten"; + case 1: return "DontFlatten"; + + case SelectControlCeiling: + default: return "Bad"; + } +} + +const int LoopControlCeiling = LoopControlPartialCountShift + 1; + +const char* LoopControlString(int cont) +{ + switch (cont) { + case LoopControlUnrollShift: return "Unroll"; + case LoopControlDontUnrollShift: return "DontUnroll"; + case LoopControlDependencyInfiniteShift: return "DependencyInfinite"; + case LoopControlDependencyLengthShift: return "DependencyLength"; + case LoopControlMinIterationsShift: return "MinIterations"; + case LoopControlMaxIterationsShift: return "MaxIterations"; + case LoopControlIterationMultipleShift: return "IterationMultiple"; + case LoopControlPeelCountShift: return "PeelCount"; + case LoopControlPartialCountShift: return "PartialCount"; + + case LoopControlCeiling: + default: return "Bad"; + } +} + +const int FunctionControlCeiling = 4; + +const char* FunctionControlString(int cont) +{ + switch (cont) { + case 0: return "Inline"; + case 1: return "DontInline"; + case 2: return "Pure"; + case 3: return "Const"; + + case FunctionControlCeiling: + default: return "Bad"; + } +} + +const char* MemorySemanticsString(int mem) +{ + // Note: No bits set (None) means "Relaxed" + switch (mem) { + case 0: return "Bad"; // Note: this is a placeholder for 'Consume' + case 1: return "Acquire"; + case 2: return "Release"; + case 3: return "AcquireRelease"; + case 4: return "SequentiallyConsistent"; + case 5: return "Bad"; // Note: reserved for future expansion + case 6: return "UniformMemory"; + case 7: return "SubgroupMemory"; + case 8: return "WorkgroupMemory"; + case 9: return "CrossWorkgroupMemory"; + case 10: return "AtomicCounterMemory"; + case 11: return "ImageMemory"; + + default: return "Bad"; + } +} + +const int MemoryAccessCeiling = 6; + +const char* MemoryAccessString(int mem) +{ + switch (mem) { + case MemoryAccessVolatileShift: return "Volatile"; + case MemoryAccessAlignedShift: return "Aligned"; + case MemoryAccessNontemporalShift: return "Nontemporal"; + case MemoryAccessMakePointerAvailableKHRShift: return "MakePointerAvailableKHR"; + case MemoryAccessMakePointerVisibleKHRShift: return "MakePointerVisibleKHR"; + case MemoryAccessNonPrivatePointerKHRShift: return "NonPrivatePointerKHR"; + + default: return "Bad"; + } +} + +const int CooperativeMatrixOperandsCeiling = 6; + +const char* CooperativeMatrixOperandsString(int op) +{ + switch (op) { + case CooperativeMatrixOperandsMatrixASignedComponentsKHRShift: return "ASignedComponentsKHR"; + case CooperativeMatrixOperandsMatrixBSignedComponentsKHRShift: return "BSignedComponentsKHR"; + case CooperativeMatrixOperandsMatrixCSignedComponentsKHRShift: return "CSignedComponentsKHR"; + case CooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift: return "ResultSignedComponentsKHR"; + case CooperativeMatrixOperandsSaturatingAccumulationKHRShift: return "SaturatingAccumulationKHR"; + + default: return "Bad"; + } +} + +const int TensorAddressingOperandsCeiling = 3; + +const char* TensorAddressingOperandsString(int op) +{ + switch (op) { + case TensorAddressingOperandsTensorViewShift: return "TensorView"; + case TensorAddressingOperandsDecodeFuncShift: return "DecodeFunc"; + + default: return "Bad"; + } +} + +const char* ScopeString(int mem) +{ + switch (mem) { + case 0: return "CrossDevice"; + case 1: return "Device"; + case 2: return "Workgroup"; + case 3: return "Subgroup"; + case 4: return "Invocation"; + + default: return "Bad"; + } +} + +const char* GroupOperationString(int gop) +{ + + switch (gop) + { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + + default: return "Bad"; + } +} + +const char* KernelEnqueueFlagsString(int flag) +{ + switch (flag) + { + case 0: return "NoWait"; + case 1: return "WaitKernel"; + case 2: return "WaitWorkGroup"; + + default: return "Bad"; + } +} + +const char* KernelProfilingInfoString(int info) +{ + switch (info) + { + case 0: return "CmdExecTime"; + + default: return "Bad"; + } +} + +const char* CapabilityString(int info) +{ + switch (info) + { + case 0: return "Matrix"; + case 1: return "Shader"; + case 2: return "Geometry"; + case 3: return "Tessellation"; + case 4: return "Addresses"; + case 5: return "Linkage"; + case 6: return "Kernel"; + case 7: return "Vector16"; + case 8: return "Float16Buffer"; + case 9: return "Float16"; + case 10: return "Float64"; + case 11: return "Int64"; + case 12: return "Int64Atomics"; + case 13: return "ImageBasic"; + case 14: return "ImageReadWrite"; + case 15: return "ImageMipmap"; + case 16: return "Bad"; + case 17: return "Pipes"; + case 18: return "Groups"; + case 19: return "DeviceEnqueue"; + case 20: return "LiteralSampler"; + case 21: return "AtomicStorage"; + case 22: return "Int16"; + case 23: return "TessellationPointSize"; + case 24: return "GeometryPointSize"; + case 25: return "ImageGatherExtended"; + case 26: return "Bad"; + case 27: return "StorageImageMultisample"; + case 28: return "UniformBufferArrayDynamicIndexing"; + case 29: return "SampledImageArrayDynamicIndexing"; + case 30: return "StorageBufferArrayDynamicIndexing"; + case 31: return "StorageImageArrayDynamicIndexing"; + case 32: return "ClipDistance"; + case 33: return "CullDistance"; + case 34: return "ImageCubeArray"; + case 35: return "SampleRateShading"; + case 36: return "ImageRect"; + case 37: return "SampledRect"; + case 38: return "GenericPointer"; + case 39: return "Int8"; + case 40: return "InputAttachment"; + case 41: return "SparseResidency"; + case 42: return "MinLod"; + case 43: return "Sampled1D"; + case 44: return "Image1D"; + case 45: return "SampledCubeArray"; + case 46: return "SampledBuffer"; + case 47: return "ImageBuffer"; + case 48: return "ImageMSArray"; + case 49: return "StorageImageExtendedFormats"; + case 50: return "ImageQuery"; + case 51: return "DerivativeControl"; + case 52: return "InterpolationFunction"; + case 53: return "TransformFeedback"; + case 54: return "GeometryStreams"; + case 55: return "StorageImageReadWithoutFormat"; + case 56: return "StorageImageWriteWithoutFormat"; + case 57: return "MultiViewport"; + case 61: return "GroupNonUniform"; + case 62: return "GroupNonUniformVote"; + case 63: return "GroupNonUniformArithmetic"; + case 64: return "GroupNonUniformBallot"; + case 65: return "GroupNonUniformShuffle"; + case 66: return "GroupNonUniformShuffleRelative"; + case 67: return "GroupNonUniformClustered"; + case 68: return "GroupNonUniformQuad"; + + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case CapabilityGroupNonUniformRotateKHR: return "CapabilityGroupNonUniformRotateKHR"; + + case CapabilityStorageUniformBufferBlock16: return "StorageUniformBufferBlock16"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + + case CapabilityDenormPreserve: return "DenormPreserve"; + case CapabilityDenormFlushToZero: return "DenormFlushToZero"; + case CapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case CapabilityRoundingModeRTE: return "RoundingModeRTE"; + case CapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + + case CapabilityStencilExportEXT: return "StencilExportEXT"; + + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerNV: return "ShaderViewportIndexLayerNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case CapabilityRayTracingKHR: return "RayTracingKHR"; + case CapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case CapabilityRayQueryKHR: return "RayQueryKHR"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case CapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case CapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case CapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case CapabilityRayTracingDisplacementMicromapNV: return "CapabilityRayTracingDisplacementMicromapNV"; + case CapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case CapabilityComputeDerivativeGroupQuadsNV: return "ComputeDerivativeGroupQuadsNV"; + case CapabilityComputeDerivativeGroupLinearNV: return "ComputeDerivativeGroupLinearNV"; + case CapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilityMeshShadingEXT: return "MeshShadingEXT"; +// case CapabilityShadingRateNV: return "ShadingRateNV"; // superseded by FragmentDensityEXT + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + + case CapabilityShaderNonUniformEXT: return "ShaderNonUniformEXT"; + case CapabilityRuntimeDescriptorArrayEXT: return "RuntimeDescriptorArrayEXT"; + case CapabilityInputAttachmentArrayDynamicIndexingEXT: return "InputAttachmentArrayDynamicIndexingEXT"; + case CapabilityUniformTexelBufferArrayDynamicIndexingEXT: return "UniformTexelBufferArrayDynamicIndexingEXT"; + case CapabilityStorageTexelBufferArrayDynamicIndexingEXT: return "StorageTexelBufferArrayDynamicIndexingEXT"; + case CapabilityUniformBufferArrayNonUniformIndexingEXT: return "UniformBufferArrayNonUniformIndexingEXT"; + case CapabilitySampledImageArrayNonUniformIndexingEXT: return "SampledImageArrayNonUniformIndexingEXT"; + case CapabilityStorageBufferArrayNonUniformIndexingEXT: return "StorageBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageImageArrayNonUniformIndexingEXT: return "StorageImageArrayNonUniformIndexingEXT"; + case CapabilityInputAttachmentArrayNonUniformIndexingEXT: return "InputAttachmentArrayNonUniformIndexingEXT"; + case CapabilityUniformTexelBufferArrayNonUniformIndexingEXT: return "UniformTexelBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageTexelBufferArrayNonUniformIndexingEXT: return "StorageTexelBufferArrayNonUniformIndexingEXT"; + + case CapabilityVulkanMemoryModelKHR: return "VulkanMemoryModelKHR"; + case CapabilityVulkanMemoryModelDeviceScopeKHR: return "VulkanMemoryModelDeviceScopeKHR"; + + case CapabilityPhysicalStorageBufferAddressesEXT: return "PhysicalStorageBufferAddressesEXT"; + + case CapabilityVariablePointers: return "VariablePointers"; + + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case CapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case CapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case CapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case CapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case CapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case CapabilityTensorAddressingNV: return "TensorAddressingNV"; + + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + + case CapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case CapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + + case CapabilityFragmentShaderSampleInterlockEXT: return "CapabilityFragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderPixelInterlockEXT: return "CapabilityFragmentShaderPixelInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "CapabilityFragmentShaderShadingRateInterlockEXT"; + + case CapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case CapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case CapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + + case CapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + + case CapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + + case CapabilityDemoteToHelperInvocationEXT: return "DemoteToHelperInvocationEXT"; + case CapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + case CapabilityQuadControlKHR: return "QuadControlKHR"; + case CapabilityInt64ImageEXT: return "Int64ImageEXT"; + + case CapabilityIntegerFunctions2INTEL: return "CapabilityIntegerFunctions2INTEL"; + + case CapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + + case CapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case CapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case CapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case CapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case CapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case CapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + + case CapabilityWorkgroupMemoryExplicitLayoutKHR: return "CapabilityWorkgroupMemoryExplicitLayoutKHR"; + case CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR"; + case CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR"; + case CapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + + case CapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + + case CapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case CapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case CapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case CapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + + case CapabilityReplicatedCompositesEXT: return "CapabilityReplicatedCompositesEXT"; + + case CapabilityDotProductKHR: return "DotProductKHR"; + case CapabilityDotProductInputAllKHR: return "DotProductInputAllKHR"; + case CapabilityDotProductInput4x8BitKHR: return "DotProductInput4x8BitKHR"; + case CapabilityDotProductInput4x8BitPackedKHR: return "DotProductInput4x8BitPackedKHR"; + + case CapabilityRayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + + case CapabilityRayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case CapabilityRayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + + case CapabilityBFloat16TypeKHR: return "CapabilityBFloat16TypeKHR"; + case CapabilityBFloat16DotProductKHR: return "CapabilityBFloat16DotProductKHR"; + case CapabilityBFloat16CooperativeMatrixKHR: return "CapabilityBFloat16CooperativeMatrixKHR"; + + default: return "Bad"; + } +} + +const char* OpcodeString(int op) +{ + switch (op) { + case 0: return "OpNop"; + case 1: return "OpUndef"; + case 2: return "OpSourceContinued"; + case 3: return "OpSource"; + case 4: return "OpSourceExtension"; + case 5: return "OpName"; + case 6: return "OpMemberName"; + case 7: return "OpString"; + case 8: return "OpLine"; + case 9: return "Bad"; + case 10: return "OpExtension"; + case 11: return "OpExtInstImport"; + case 12: return "OpExtInst"; + case 13: return "Bad"; + case 14: return "OpMemoryModel"; + case 15: return "OpEntryPoint"; + case 16: return "OpExecutionMode"; + case 17: return "OpCapability"; + case 18: return "Bad"; + case 19: return "OpTypeVoid"; + case 20: return "OpTypeBool"; + case 21: return "OpTypeInt"; + case 22: return "OpTypeFloat"; + case 23: return "OpTypeVector"; + case 24: return "OpTypeMatrix"; + case 25: return "OpTypeImage"; + case 26: return "OpTypeSampler"; + case 27: return "OpTypeSampledImage"; + case 28: return "OpTypeArray"; + case 29: return "OpTypeRuntimeArray"; + case 30: return "OpTypeStruct"; + case 31: return "OpTypeOpaque"; + case 32: return "OpTypePointer"; + case 33: return "OpTypeFunction"; + case 34: return "OpTypeEvent"; + case 35: return "OpTypeDeviceEvent"; + case 36: return "OpTypeReserveId"; + case 37: return "OpTypeQueue"; + case 38: return "OpTypePipe"; + case 39: return "OpTypeForwardPointer"; + case 40: return "Bad"; + case 41: return "OpConstantTrue"; + case 42: return "OpConstantFalse"; + case 43: return "OpConstant"; + case 44: return "OpConstantComposite"; + case 45: return "OpConstantSampler"; + case 46: return "OpConstantNull"; + case 47: return "Bad"; + case 48: return "OpSpecConstantTrue"; + case 49: return "OpSpecConstantFalse"; + case 50: return "OpSpecConstant"; + case 51: return "OpSpecConstantComposite"; + case 52: return "OpSpecConstantOp"; + case 53: return "Bad"; + case 54: return "OpFunction"; + case 55: return "OpFunctionParameter"; + case 56: return "OpFunctionEnd"; + case 57: return "OpFunctionCall"; + case 58: return "Bad"; + case 59: return "OpVariable"; + case 60: return "OpImageTexelPointer"; + case 61: return "OpLoad"; + case 62: return "OpStore"; + case 63: return "OpCopyMemory"; + case 64: return "OpCopyMemorySized"; + case 65: return "OpAccessChain"; + case 66: return "OpInBoundsAccessChain"; + case 67: return "OpPtrAccessChain"; + case 68: return "OpArrayLength"; + case 69: return "OpGenericPtrMemSemantics"; + case 70: return "OpInBoundsPtrAccessChain"; + case 71: return "OpDecorate"; + case 72: return "OpMemberDecorate"; + case 73: return "OpDecorationGroup"; + case 74: return "OpGroupDecorate"; + case 75: return "OpGroupMemberDecorate"; + case 76: return "Bad"; + case 77: return "OpVectorExtractDynamic"; + case 78: return "OpVectorInsertDynamic"; + case 79: return "OpVectorShuffle"; + case 80: return "OpCompositeConstruct"; + case 81: return "OpCompositeExtract"; + case 82: return "OpCompositeInsert"; + case 83: return "OpCopyObject"; + case 84: return "OpTranspose"; + case OpCopyLogical: return "OpCopyLogical"; + case 85: return "Bad"; + case 86: return "OpSampledImage"; + case 87: return "OpImageSampleImplicitLod"; + case 88: return "OpImageSampleExplicitLod"; + case 89: return "OpImageSampleDrefImplicitLod"; + case 90: return "OpImageSampleDrefExplicitLod"; + case 91: return "OpImageSampleProjImplicitLod"; + case 92: return "OpImageSampleProjExplicitLod"; + case 93: return "OpImageSampleProjDrefImplicitLod"; + case 94: return "OpImageSampleProjDrefExplicitLod"; + case 95: return "OpImageFetch"; + case 96: return "OpImageGather"; + case 97: return "OpImageDrefGather"; + case 98: return "OpImageRead"; + case 99: return "OpImageWrite"; + case 100: return "OpImage"; + case 101: return "OpImageQueryFormat"; + case 102: return "OpImageQueryOrder"; + case 103: return "OpImageQuerySizeLod"; + case 104: return "OpImageQuerySize"; + case 105: return "OpImageQueryLod"; + case 106: return "OpImageQueryLevels"; + case 107: return "OpImageQuerySamples"; + case 108: return "Bad"; + case 109: return "OpConvertFToU"; + case 110: return "OpConvertFToS"; + case 111: return "OpConvertSToF"; + case 112: return "OpConvertUToF"; + case 113: return "OpUConvert"; + case 114: return "OpSConvert"; + case 115: return "OpFConvert"; + case 116: return "OpQuantizeToF16"; + case 117: return "OpConvertPtrToU"; + case 118: return "OpSatConvertSToU"; + case 119: return "OpSatConvertUToS"; + case 120: return "OpConvertUToPtr"; + case 121: return "OpPtrCastToGeneric"; + case 122: return "OpGenericCastToPtr"; + case 123: return "OpGenericCastToPtrExplicit"; + case 124: return "OpBitcast"; + case 125: return "Bad"; + case 126: return "OpSNegate"; + case 127: return "OpFNegate"; + case 128: return "OpIAdd"; + case 129: return "OpFAdd"; + case 130: return "OpISub"; + case 131: return "OpFSub"; + case 132: return "OpIMul"; + case 133: return "OpFMul"; + case 134: return "OpUDiv"; + case 135: return "OpSDiv"; + case 136: return "OpFDiv"; + case 137: return "OpUMod"; + case 138: return "OpSRem"; + case 139: return "OpSMod"; + case 140: return "OpFRem"; + case 141: return "OpFMod"; + case 142: return "OpVectorTimesScalar"; + case 143: return "OpMatrixTimesScalar"; + case 144: return "OpVectorTimesMatrix"; + case 145: return "OpMatrixTimesVector"; + case 146: return "OpMatrixTimesMatrix"; + case 147: return "OpOuterProduct"; + case 148: return "OpDot"; + case 149: return "OpIAddCarry"; + case 150: return "OpISubBorrow"; + case 151: return "OpUMulExtended"; + case 152: return "OpSMulExtended"; + case 153: return "Bad"; + case 154: return "OpAny"; + case 155: return "OpAll"; + case 156: return "OpIsNan"; + case 157: return "OpIsInf"; + case 158: return "OpIsFinite"; + case 159: return "OpIsNormal"; + case 160: return "OpSignBitSet"; + case 161: return "OpLessOrGreater"; + case 162: return "OpOrdered"; + case 163: return "OpUnordered"; + case 164: return "OpLogicalEqual"; + case 165: return "OpLogicalNotEqual"; + case 166: return "OpLogicalOr"; + case 167: return "OpLogicalAnd"; + case 168: return "OpLogicalNot"; + case 169: return "OpSelect"; + case 170: return "OpIEqual"; + case 171: return "OpINotEqual"; + case 172: return "OpUGreaterThan"; + case 173: return "OpSGreaterThan"; + case 174: return "OpUGreaterThanEqual"; + case 175: return "OpSGreaterThanEqual"; + case 176: return "OpULessThan"; + case 177: return "OpSLessThan"; + case 178: return "OpULessThanEqual"; + case 179: return "OpSLessThanEqual"; + case 180: return "OpFOrdEqual"; + case 181: return "OpFUnordEqual"; + case 182: return "OpFOrdNotEqual"; + case 183: return "OpFUnordNotEqual"; + case 184: return "OpFOrdLessThan"; + case 185: return "OpFUnordLessThan"; + case 186: return "OpFOrdGreaterThan"; + case 187: return "OpFUnordGreaterThan"; + case 188: return "OpFOrdLessThanEqual"; + case 189: return "OpFUnordLessThanEqual"; + case 190: return "OpFOrdGreaterThanEqual"; + case 191: return "OpFUnordGreaterThanEqual"; + case 192: return "Bad"; + case 193: return "Bad"; + case 194: return "OpShiftRightLogical"; + case 195: return "OpShiftRightArithmetic"; + case 196: return "OpShiftLeftLogical"; + case 197: return "OpBitwiseOr"; + case 198: return "OpBitwiseXor"; + case 199: return "OpBitwiseAnd"; + case 200: return "OpNot"; + case 201: return "OpBitFieldInsert"; + case 202: return "OpBitFieldSExtract"; + case 203: return "OpBitFieldUExtract"; + case 204: return "OpBitReverse"; + case 205: return "OpBitCount"; + case 206: return "Bad"; + case 207: return "OpDPdx"; + case 208: return "OpDPdy"; + case 209: return "OpFwidth"; + case 210: return "OpDPdxFine"; + case 211: return "OpDPdyFine"; + case 212: return "OpFwidthFine"; + case 213: return "OpDPdxCoarse"; + case 214: return "OpDPdyCoarse"; + case 215: return "OpFwidthCoarse"; + case 216: return "Bad"; + case 217: return "Bad"; + case 218: return "OpEmitVertex"; + case 219: return "OpEndPrimitive"; + case 220: return "OpEmitStreamVertex"; + case 221: return "OpEndStreamPrimitive"; + case 222: return "Bad"; + case 223: return "Bad"; + case 224: return "OpControlBarrier"; + case 225: return "OpMemoryBarrier"; + case 226: return "Bad"; + case 227: return "OpAtomicLoad"; + case 228: return "OpAtomicStore"; + case 229: return "OpAtomicExchange"; + case 230: return "OpAtomicCompareExchange"; + case 231: return "OpAtomicCompareExchangeWeak"; + case 232: return "OpAtomicIIncrement"; + case 233: return "OpAtomicIDecrement"; + case 234: return "OpAtomicIAdd"; + case 235: return "OpAtomicISub"; + case 236: return "OpAtomicSMin"; + case 237: return "OpAtomicUMin"; + case 238: return "OpAtomicSMax"; + case 239: return "OpAtomicUMax"; + case 240: return "OpAtomicAnd"; + case 241: return "OpAtomicOr"; + case 242: return "OpAtomicXor"; + case 243: return "Bad"; + case 244: return "Bad"; + case 245: return "OpPhi"; + case 246: return "OpLoopMerge"; + case 247: return "OpSelectionMerge"; + case 248: return "OpLabel"; + case 249: return "OpBranch"; + case 250: return "OpBranchConditional"; + case 251: return "OpSwitch"; + case 252: return "OpKill"; + case 253: return "OpReturn"; + case 254: return "OpReturnValue"; + case 255: return "OpUnreachable"; + case 256: return "OpLifetimeStart"; + case 257: return "OpLifetimeStop"; + case 258: return "Bad"; + case 259: return "OpGroupAsyncCopy"; + case 260: return "OpGroupWaitEvents"; + case 261: return "OpGroupAll"; + case 262: return "OpGroupAny"; + case 263: return "OpGroupBroadcast"; + case 264: return "OpGroupIAdd"; + case 265: return "OpGroupFAdd"; + case 266: return "OpGroupFMin"; + case 267: return "OpGroupUMin"; + case 268: return "OpGroupSMin"; + case 269: return "OpGroupFMax"; + case 270: return "OpGroupUMax"; + case 271: return "OpGroupSMax"; + case 272: return "Bad"; + case 273: return "Bad"; + case 274: return "OpReadPipe"; + case 275: return "OpWritePipe"; + case 276: return "OpReservedReadPipe"; + case 277: return "OpReservedWritePipe"; + case 278: return "OpReserveReadPipePackets"; + case 279: return "OpReserveWritePipePackets"; + case 280: return "OpCommitReadPipe"; + case 281: return "OpCommitWritePipe"; + case 282: return "OpIsValidReserveId"; + case 283: return "OpGetNumPipePackets"; + case 284: return "OpGetMaxPipePackets"; + case 285: return "OpGroupReserveReadPipePackets"; + case 286: return "OpGroupReserveWritePipePackets"; + case 287: return "OpGroupCommitReadPipe"; + case 288: return "OpGroupCommitWritePipe"; + case 289: return "Bad"; + case 290: return "Bad"; + case 291: return "OpEnqueueMarker"; + case 292: return "OpEnqueueKernel"; + case 293: return "OpGetKernelNDrangeSubGroupCount"; + case 294: return "OpGetKernelNDrangeMaxSubGroupSize"; + case 295: return "OpGetKernelWorkGroupSize"; + case 296: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case 297: return "OpRetainEvent"; + case 298: return "OpReleaseEvent"; + case 299: return "OpCreateUserEvent"; + case 300: return "OpIsValidEvent"; + case 301: return "OpSetUserEventStatus"; + case 302: return "OpCaptureEventProfilingInfo"; + case 303: return "OpGetDefaultQueue"; + case 304: return "OpBuildNDRange"; + case 305: return "OpImageSparseSampleImplicitLod"; + case 306: return "OpImageSparseSampleExplicitLod"; + case 307: return "OpImageSparseSampleDrefImplicitLod"; + case 308: return "OpImageSparseSampleDrefExplicitLod"; + case 309: return "OpImageSparseSampleProjImplicitLod"; + case 310: return "OpImageSparseSampleProjExplicitLod"; + case 311: return "OpImageSparseSampleProjDrefImplicitLod"; + case 312: return "OpImageSparseSampleProjDrefExplicitLod"; + case 313: return "OpImageSparseFetch"; + case 314: return "OpImageSparseGather"; + case 315: return "OpImageSparseDrefGather"; + case 316: return "OpImageSparseTexelsResident"; + case 317: return "OpNoLine"; + case 318: return "OpAtomicFlagTestAndSet"; + case 319: return "OpAtomicFlagClear"; + case 320: return "OpImageSparseRead"; + + case OpModuleProcessed: return "OpModuleProcessed"; + case OpExecutionModeId: return "OpExecutionModeId"; + case OpDecorateId: return "OpDecorateId"; + + case 333: return "OpGroupNonUniformElect"; + case 334: return "OpGroupNonUniformAll"; + case 335: return "OpGroupNonUniformAny"; + case 336: return "OpGroupNonUniformAllEqual"; + case 337: return "OpGroupNonUniformBroadcast"; + case 338: return "OpGroupNonUniformBroadcastFirst"; + case 339: return "OpGroupNonUniformBallot"; + case 340: return "OpGroupNonUniformInverseBallot"; + case 341: return "OpGroupNonUniformBallotBitExtract"; + case 342: return "OpGroupNonUniformBallotBitCount"; + case 343: return "OpGroupNonUniformBallotFindLSB"; + case 344: return "OpGroupNonUniformBallotFindMSB"; + case 345: return "OpGroupNonUniformShuffle"; + case 346: return "OpGroupNonUniformShuffleXor"; + case 347: return "OpGroupNonUniformShuffleUp"; + case 348: return "OpGroupNonUniformShuffleDown"; + case 349: return "OpGroupNonUniformIAdd"; + case 350: return "OpGroupNonUniformFAdd"; + case 351: return "OpGroupNonUniformIMul"; + case 352: return "OpGroupNonUniformFMul"; + case 353: return "OpGroupNonUniformSMin"; + case 354: return "OpGroupNonUniformUMin"; + case 355: return "OpGroupNonUniformFMin"; + case 356: return "OpGroupNonUniformSMax"; + case 357: return "OpGroupNonUniformUMax"; + case 358: return "OpGroupNonUniformFMax"; + case 359: return "OpGroupNonUniformBitwiseAnd"; + case 360: return "OpGroupNonUniformBitwiseOr"; + case 361: return "OpGroupNonUniformBitwiseXor"; + case 362: return "OpGroupNonUniformLogicalAnd"; + case 363: return "OpGroupNonUniformLogicalOr"; + case 364: return "OpGroupNonUniformLogicalXor"; + case 365: return "OpGroupNonUniformQuadBroadcast"; + case 366: return "OpGroupNonUniformQuadSwap"; + + case OpTerminateInvocation: return "OpTerminateInvocation"; + + case 4421: return "OpSubgroupBallotKHR"; + case 4422: return "OpSubgroupFirstInvocationKHR"; + case 4428: return "OpSubgroupAllKHR"; + case 4429: return "OpSubgroupAnyKHR"; + case 4430: return "OpSubgroupAllEqualKHR"; + case 4432: return "OpSubgroupReadInvocationKHR"; + case 4433: return "OpExtInstWithForwardRefsKHR"; + + case OpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case OpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + + case OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case OpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case OpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + + case OpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case OpExpectKHR: return "OpExpectKHR"; + + case 5000: return "OpGroupIAddNonUniformAMD"; + case 5001: return "OpGroupFAddNonUniformAMD"; + case 5002: return "OpGroupFMinNonUniformAMD"; + case 5003: return "OpGroupUMinNonUniformAMD"; + case 5004: return "OpGroupSMinNonUniformAMD"; + case 5005: return "OpGroupFMaxNonUniformAMD"; + case 5006: return "OpGroupUMaxNonUniformAMD"; + case 5007: return "OpGroupSMaxNonUniformAMD"; + + case 5011: return "OpFragmentMaskFetchAMD"; + case 5012: return "OpFragmentFetchAMD"; + + case OpReadClockKHR: return "OpReadClockKHR"; + + case OpDecorateStringGOOGLE: return "OpDecorateStringGOOGLE"; + case OpMemberDecorateStringGOOGLE: return "OpMemberDecorateStringGOOGLE"; + + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayNV: return "OpTerminateRayNV"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpTraceNV: return "OpTraceNV"; + case OpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableNV: return "OpExecuteCallableNV"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + + case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case OpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case OpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + + case OpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + + case OpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case OpRayQueryGetIntersectionClusterIdNV: return "OpRayQueryGetIntersectionClusterIdNV"; + + case OpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case OpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case OpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case OpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case OpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case OpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case OpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case OpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case OpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case OpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case OpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case OpDemoteToHelperInvocationEXT: return "OpDemoteToHelperInvocationEXT"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + + case OpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case OpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case OpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case OpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case OpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case OpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case OpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case OpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case OpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case OpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case OpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case OpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case OpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case OpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case OpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case OpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case OpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case OpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + + case OpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case OpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case OpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case OpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case OpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case OpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case OpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + + case OpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case OpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case OpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case OpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case OpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case OpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case OpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case OpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case OpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case OpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case OpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case OpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case OpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case OpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case OpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case OpHitObjectGetHitKindNV: return "OpHitObjectGetFrontFaceNV"; + case OpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case OpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case OpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case OpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case OpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case OpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case OpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case OpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case OpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case OpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case OpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case OpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case OpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case OpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case OpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case OpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case OpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case OpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case OpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case OpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case OpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case OpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case OpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case OpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + + case OpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case OpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + + case OpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case OpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case OpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + + case OpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case OpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case OpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case OpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case OpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case OpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case OpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case OpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + + case OpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case OpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case OpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + + case OpSDotKHR: return "OpSDotKHR"; + case OpUDotKHR: return "OpUDotKHR"; + case OpSUDotKHR: return "OpSUDotKHR"; + case OpSDotAccSatKHR: return "OpSDotAccSatKHR"; + case OpUDotAccSatKHR: return "OpUDotAccSatKHR"; + case OpSUDotAccSatKHR: return "OpSUDotAccSatKHR"; + + default: + return "Bad"; + } +} + +// The set of objects that hold all the instruction/operand +// parameterization information. +InstructionParameters InstructionDesc[OpCodeMask + 1]; +OperandParameters ExecutionModeOperands[ExecutionModeCeiling]; +OperandParameters DecorationOperands[DecorationCeiling]; + +EnumDefinition OperandClassParams[OperandCount]; +EnumParameters ExecutionModeParams[ExecutionModeCeiling]; +EnumParameters ImageOperandsParams[ImageOperandsCeiling]; +EnumParameters DecorationParams[DecorationCeiling]; +EnumParameters LoopControlParams[FunctionControlCeiling]; +EnumParameters SelectionControlParams[SelectControlCeiling]; +EnumParameters FunctionControlParams[FunctionControlCeiling]; +EnumParameters MemoryAccessParams[MemoryAccessCeiling]; +EnumParameters CooperativeMatrixOperandsParams[CooperativeMatrixOperandsCeiling]; +EnumParameters TensorAddressingOperandsParams[TensorAddressingOperandsCeiling]; + +// Set up all the parameterizing descriptions of the opcodes, operands, etc. +void Parameterize() +{ + // only do this once. + static std::once_flag initialized; + std::call_once(initialized, [](){ + + // Exceptions to having a result and a resulting type . + // (Everything is initialized to have both). + + InstructionDesc[OpNop].setResultAndType(false, false); + InstructionDesc[OpSource].setResultAndType(false, false); + InstructionDesc[OpSourceContinued].setResultAndType(false, false); + InstructionDesc[OpSourceExtension].setResultAndType(false, false); + InstructionDesc[OpExtension].setResultAndType(false, false); + InstructionDesc[OpExtInstImport].setResultAndType(true, false); + InstructionDesc[OpCapability].setResultAndType(false, false); + InstructionDesc[OpMemoryModel].setResultAndType(false, false); + InstructionDesc[OpEntryPoint].setResultAndType(false, false); + InstructionDesc[OpExecutionMode].setResultAndType(false, false); + InstructionDesc[OpExecutionModeId].setResultAndType(false, false); + InstructionDesc[OpTypeVoid].setResultAndType(true, false); + InstructionDesc[OpTypeBool].setResultAndType(true, false); + InstructionDesc[OpTypeInt].setResultAndType(true, false); + InstructionDesc[OpTypeFloat].setResultAndType(true, false); + InstructionDesc[OpTypeVector].setResultAndType(true, false); + InstructionDesc[OpTypeMatrix].setResultAndType(true, false); + InstructionDesc[OpTypeImage].setResultAndType(true, false); + InstructionDesc[OpTypeSampler].setResultAndType(true, false); + InstructionDesc[OpTypeSampledImage].setResultAndType(true, false); + InstructionDesc[OpTypeArray].setResultAndType(true, false); + InstructionDesc[OpTypeRuntimeArray].setResultAndType(true, false); + InstructionDesc[OpTypeStruct].setResultAndType(true, false); + InstructionDesc[OpTypeOpaque].setResultAndType(true, false); + InstructionDesc[OpTypePointer].setResultAndType(true, false); + InstructionDesc[OpTypeForwardPointer].setResultAndType(false, false); + InstructionDesc[OpTypeFunction].setResultAndType(true, false); + InstructionDesc[OpTypeEvent].setResultAndType(true, false); + InstructionDesc[OpTypeDeviceEvent].setResultAndType(true, false); + InstructionDesc[OpTypeReserveId].setResultAndType(true, false); + InstructionDesc[OpTypeQueue].setResultAndType(true, false); + InstructionDesc[OpTypePipe].setResultAndType(true, false); + InstructionDesc[OpFunctionEnd].setResultAndType(false, false); + InstructionDesc[OpStore].setResultAndType(false, false); + InstructionDesc[OpImageWrite].setResultAndType(false, false); + InstructionDesc[OpDecorationGroup].setResultAndType(true, false); + InstructionDesc[OpDecorate].setResultAndType(false, false); + InstructionDesc[OpDecorateId].setResultAndType(false, false); + InstructionDesc[OpDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpMemberDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpGroupDecorate].setResultAndType(false, false); + InstructionDesc[OpGroupMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpName].setResultAndType(false, false); + InstructionDesc[OpMemberName].setResultAndType(false, false); + InstructionDesc[OpString].setResultAndType(true, false); + InstructionDesc[OpLine].setResultAndType(false, false); + InstructionDesc[OpNoLine].setResultAndType(false, false); + InstructionDesc[OpCopyMemory].setResultAndType(false, false); + InstructionDesc[OpCopyMemorySized].setResultAndType(false, false); + InstructionDesc[OpEmitVertex].setResultAndType(false, false); + InstructionDesc[OpEndPrimitive].setResultAndType(false, false); + InstructionDesc[OpEmitStreamVertex].setResultAndType(false, false); + InstructionDesc[OpEndStreamPrimitive].setResultAndType(false, false); + InstructionDesc[OpControlBarrier].setResultAndType(false, false); + InstructionDesc[OpMemoryBarrier].setResultAndType(false, false); + InstructionDesc[OpAtomicStore].setResultAndType(false, false); + InstructionDesc[OpLoopMerge].setResultAndType(false, false); + InstructionDesc[OpSelectionMerge].setResultAndType(false, false); + InstructionDesc[OpLabel].setResultAndType(true, false); + InstructionDesc[OpBranch].setResultAndType(false, false); + InstructionDesc[OpBranchConditional].setResultAndType(false, false); + InstructionDesc[OpSwitch].setResultAndType(false, false); + InstructionDesc[OpKill].setResultAndType(false, false); + InstructionDesc[OpTerminateInvocation].setResultAndType(false, false); + InstructionDesc[OpReturn].setResultAndType(false, false); + InstructionDesc[OpReturnValue].setResultAndType(false, false); + InstructionDesc[OpUnreachable].setResultAndType(false, false); + InstructionDesc[OpLifetimeStart].setResultAndType(false, false); + InstructionDesc[OpLifetimeStop].setResultAndType(false, false); + InstructionDesc[OpCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCaptureEventProfilingInfo].setResultAndType(false, false); + InstructionDesc[OpSetUserEventStatus].setResultAndType(false, false); + InstructionDesc[OpRetainEvent].setResultAndType(false, false); + InstructionDesc[OpReleaseEvent].setResultAndType(false, false); + InstructionDesc[OpGroupWaitEvents].setResultAndType(false, false); + InstructionDesc[OpAtomicFlagClear].setResultAndType(false, false); + InstructionDesc[OpModuleProcessed].setResultAndType(false, false); + InstructionDesc[OpTypeCooperativeMatrixNV].setResultAndType(true, false); + InstructionDesc[OpCooperativeMatrixStoreNV].setResultAndType(false, false); + InstructionDesc[OpTypeCooperativeMatrixKHR].setResultAndType(true, false); + InstructionDesc[OpCooperativeMatrixStoreKHR].setResultAndType(false, false); + InstructionDesc[OpBeginInvocationInterlockEXT].setResultAndType(false, false); + InstructionDesc[OpEndInvocationInterlockEXT].setResultAndType(false, false); + InstructionDesc[OpAssumeTrueKHR].setResultAndType(false, false); + InstructionDesc[OpTypeTensorLayoutNV].setResultAndType(true, false); + InstructionDesc[OpTypeTensorViewNV].setResultAndType(true, false); + InstructionDesc[OpCooperativeMatrixStoreTensorNV].setResultAndType(false, false); + InstructionDesc[OpTypeCooperativeVectorNV].setResultAndType(true, false); + InstructionDesc[OpCooperativeVectorStoreNV].setResultAndType(false, false); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].setResultAndType(false, false); + InstructionDesc[OpCooperativeVectorReduceSumAccumulateNV].setResultAndType(false, false); + + // Specific additional context-dependent operands + + ExecutionModeOperands[ExecutionModeInvocations].push(OperandLiteralNumber, "'Number of <>'"); + + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeOutputVertices].push(OperandLiteralNumber, "'Vertex count'"); + ExecutionModeOperands[ExecutionModeVecTypeHint].push(OperandLiteralNumber, "'Vector type'"); + + DecorationOperands[DecorationStream].push(OperandLiteralNumber, "'Stream Number'"); + DecorationOperands[DecorationLocation].push(OperandLiteralNumber, "'Location'"); + DecorationOperands[DecorationComponent].push(OperandLiteralNumber, "'Component'"); + DecorationOperands[DecorationIndex].push(OperandLiteralNumber, "'Index'"); + DecorationOperands[DecorationBinding].push(OperandLiteralNumber, "'Binding Point'"); + DecorationOperands[DecorationDescriptorSet].push(OperandLiteralNumber, "'Descriptor Set'"); + DecorationOperands[DecorationOffset].push(OperandLiteralNumber, "'Byte Offset'"); + DecorationOperands[DecorationXfbBuffer].push(OperandLiteralNumber, "'XFB Buffer Number'"); + DecorationOperands[DecorationXfbStride].push(OperandLiteralNumber, "'XFB Stride'"); + DecorationOperands[DecorationArrayStride].push(OperandLiteralNumber, "'Array Stride'"); + DecorationOperands[DecorationMatrixStride].push(OperandLiteralNumber, "'Matrix Stride'"); + DecorationOperands[DecorationBuiltIn].push(OperandLiteralNumber, "See <>"); + DecorationOperands[DecorationFPRoundingMode].push(OperandFPRoundingMode, "'Floating-Point Rounding Mode'"); + DecorationOperands[DecorationFPFastMathMode].push(OperandFPFastMath, "'Fast-Math Mode'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLiteralString, "'Name'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLinkageType, "'Linkage Type'"); + DecorationOperands[DecorationFuncParamAttr].push(OperandFuncParamAttr, "'Function Parameter Attribute'"); + DecorationOperands[DecorationSpecId].push(OperandLiteralNumber, "'Specialization Constant ID'"); + DecorationOperands[DecorationInputAttachmentIndex].push(OperandLiteralNumber, "'Attachment Index'"); + DecorationOperands[DecorationAlignment].push(OperandLiteralNumber, "'Alignment'"); + + OperandClassParams[OperandSource].set(0, SourceString, nullptr); + OperandClassParams[OperandExecutionModel].set(0, ExecutionModelString, nullptr); + OperandClassParams[OperandAddressing].set(0, AddressingString, nullptr); + OperandClassParams[OperandMemory].set(0, MemoryString, nullptr); + OperandClassParams[OperandExecutionMode].set(ExecutionModeCeiling, ExecutionModeString, ExecutionModeParams); + OperandClassParams[OperandExecutionMode].setOperands(ExecutionModeOperands); + OperandClassParams[OperandStorage].set(0, StorageClassString, nullptr); + OperandClassParams[OperandDimensionality].set(0, DimensionString, nullptr); + OperandClassParams[OperandSamplerAddressingMode].set(0, SamplerAddressingModeString, nullptr); + OperandClassParams[OperandSamplerFilterMode].set(0, SamplerFilterModeString, nullptr); + OperandClassParams[OperandSamplerImageFormat].set(0, ImageFormatString, nullptr); + OperandClassParams[OperandImageChannelOrder].set(0, ImageChannelOrderString, nullptr); + OperandClassParams[OperandImageChannelDataType].set(0, ImageChannelDataTypeString, nullptr); + OperandClassParams[OperandImageOperands].set(ImageOperandsCeiling, ImageOperandsString, ImageOperandsParams, true); + OperandClassParams[OperandFPFastMath].set(0, FPFastMathString, nullptr, true); + OperandClassParams[OperandFPRoundingMode].set(0, FPRoundingModeString, nullptr); + OperandClassParams[OperandLinkageType].set(0, LinkageTypeString, nullptr); + OperandClassParams[OperandFuncParamAttr].set(0, FuncParamAttrString, nullptr); + OperandClassParams[OperandAccessQualifier].set(0, AccessQualifierString, nullptr); + OperandClassParams[OperandDecoration].set(DecorationCeiling, DecorationString, DecorationParams); + OperandClassParams[OperandDecoration].setOperands(DecorationOperands); + OperandClassParams[OperandBuiltIn].set(0, BuiltInString, nullptr); + OperandClassParams[OperandSelect].set(SelectControlCeiling, SelectControlString, SelectionControlParams, true); + OperandClassParams[OperandLoop].set(LoopControlCeiling, LoopControlString, LoopControlParams, true); + OperandClassParams[OperandFunction].set(FunctionControlCeiling, FunctionControlString, FunctionControlParams, true); + OperandClassParams[OperandMemorySemantics].set(0, MemorySemanticsString, nullptr, true); + OperandClassParams[OperandMemoryAccess].set(MemoryAccessCeiling, MemoryAccessString, MemoryAccessParams, true); + OperandClassParams[OperandScope].set(0, ScopeString, nullptr); + OperandClassParams[OperandGroupOperation].set(0, GroupOperationString, nullptr); + OperandClassParams[OperandKernelEnqueueFlags].set(0, KernelEnqueueFlagsString, nullptr); + OperandClassParams[OperandKernelProfilingInfo].set(0, KernelProfilingInfoString, nullptr, true); + OperandClassParams[OperandCapability].set(0, CapabilityString, nullptr); + OperandClassParams[OperandCooperativeMatrixOperands].set(CooperativeMatrixOperandsCeiling, CooperativeMatrixOperandsString, CooperativeMatrixOperandsParams, true); + OperandClassParams[OperandTensorAddressingOperands].set(TensorAddressingOperandsCeiling, TensorAddressingOperandsString, TensorAddressingOperandsParams, true); + OperandClassParams[OperandOpcode].set(OpCodeMask + 1, OpcodeString, nullptr); + + // set name of operator, an initial set of style operands, and the description + + InstructionDesc[OpSource].operands.push(OperandSource, ""); + InstructionDesc[OpSource].operands.push(OperandLiteralNumber, "'Version'"); + InstructionDesc[OpSource].operands.push(OperandId, "'File'", true); + InstructionDesc[OpSource].operands.push(OperandLiteralString, "'Source'", true); + + InstructionDesc[OpSourceContinued].operands.push(OperandLiteralString, "'Continued Source'"); + + InstructionDesc[OpSourceExtension].operands.push(OperandLiteralString, "'Extension'"); + + InstructionDesc[OpName].operands.push(OperandId, "'Target'"); + InstructionDesc[OpName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpMemberName].operands.push(OperandId, "'Type'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpString].operands.push(OperandLiteralString, "'String'"); + + InstructionDesc[OpLine].operands.push(OperandId, "'File'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Line'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Column'"); + + InstructionDesc[OpExtension].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpExtInstImport].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpCapability].operands.push(OperandCapability, "'Capability'"); + + InstructionDesc[OpMemoryModel].operands.push(OperandAddressing, ""); + InstructionDesc[OpMemoryModel].operands.push(OperandMemory, ""); + + InstructionDesc[OpEntryPoint].operands.push(OperandExecutionModel, ""); + InstructionDesc[OpEntryPoint].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpEntryPoint].operands.push(OperandLiteralString, "'Name'"); + InstructionDesc[OpEntryPoint].operands.push(OperandVariableIds, "'Interface'"); + + InstructionDesc[OpExecutionMode].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpExecutionMode].operands.push(OperandExecutionMode, "'Mode'"); + InstructionDesc[OpExecutionMode].operands.push(OperandOptionalLiteral, "See <>"); + + InstructionDesc[OpExecutionModeId].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpExecutionModeId].operands.push(OperandExecutionMode, "'Mode'"); + InstructionDesc[OpExecutionModeId].operands.push(OperandVariableIds, "See <>"); + + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Width'"); + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Signedness'"); + + InstructionDesc[OpTypeFloat].operands.push(OperandLiteralNumber, "'Width'"); + InstructionDesc[OpTypeFloat].operands.push(OperandOptionalLiteral, "'FP Encoding'"); + + InstructionDesc[OpTypeVector].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeVector].operands.push(OperandLiteralNumber, "'Component Count'"); + + InstructionDesc[OpTypeMatrix].operands.push(OperandId, "'Column Type'"); + InstructionDesc[OpTypeMatrix].operands.push(OperandLiteralNumber, "'Column Count'"); + + InstructionDesc[OpTypeImage].operands.push(OperandId, "'Sampled Type'"); + InstructionDesc[OpTypeImage].operands.push(OperandDimensionality, ""); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Depth'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Arrayed'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'MS'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Sampled'"); + InstructionDesc[OpTypeImage].operands.push(OperandSamplerImageFormat, ""); + InstructionDesc[OpTypeImage].operands.push(OperandAccessQualifier, "", true); + + InstructionDesc[OpTypeSampledImage].operands.push(OperandId, "'Image Type'"); + + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Element Type'"); + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Length'"); + + InstructionDesc[OpTypeRuntimeArray].operands.push(OperandId, "'Element Type'"); + + InstructionDesc[OpTypeStruct].operands.push(OperandVariableIds, "'Member 0 type', +\n'member 1 type', +\n..."); + + InstructionDesc[OpTypeOpaque].operands.push(OperandLiteralString, "The name of the opaque type."); + + InstructionDesc[OpTypePointer].operands.push(OperandStorage, ""); + InstructionDesc[OpTypePointer].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpTypeForwardPointer].operands.push(OperandId, "'Pointer Type'"); + InstructionDesc[OpTypeForwardPointer].operands.push(OperandStorage, ""); + + InstructionDesc[OpTypePipe].operands.push(OperandAccessQualifier, "'Qualifier'"); + + InstructionDesc[OpTypeFunction].operands.push(OperandId, "'Return Type'"); + InstructionDesc[OpTypeFunction].operands.push(OperandVariableIds, "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..."); + + InstructionDesc[OpConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerAddressingMode, ""); + InstructionDesc[OpConstantSampler].operands.push(OperandLiteralNumber, "'Param'"); + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerFilterMode, ""); + + InstructionDesc[OpSpecConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpSpecConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpSpecConstantOp].operands.push(OperandLiteralNumber, "'Opcode'"); + InstructionDesc[OpSpecConstantOp].operands.push(OperandVariableIds, "'Operands'"); + + InstructionDesc[OpVariable].operands.push(OperandStorage, ""); + InstructionDesc[OpVariable].operands.push(OperandId, "'Initializer'", true); + + InstructionDesc[OpFunction].operands.push(OperandFunction, ""); + InstructionDesc[OpFunction].operands.push(OperandId, "'Function Type'"); + + InstructionDesc[OpFunctionCall].operands.push(OperandId, "'Function'"); + InstructionDesc[OpFunctionCall].operands.push(OperandVariableIds, "'Argument 0', +\n'Argument 1', +\n..."); + + InstructionDesc[OpExtInst].operands.push(OperandId, "'Set'"); + InstructionDesc[OpExtInst].operands.push(OperandLiteralNumber, "'Instruction'"); + InstructionDesc[OpExtInst].operands.push(OperandVariableIds, "'Operand 1', +\n'Operand 2', +\n..."); + + InstructionDesc[OpExtInstWithForwardRefsKHR].operands.push(OperandId, "'Set'"); + InstructionDesc[OpExtInstWithForwardRefsKHR].operands.push(OperandLiteralNumber, "'Instruction'"); + InstructionDesc[OpExtInstWithForwardRefsKHR].operands.push(OperandVariableIds, "'Operand 1', +\n'Operand 2', +\n..."); + + InstructionDesc[OpLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLoad].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpLoad].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpLoad].operands.push(OperandId, "", true); + + InstructionDesc[OpStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpStore].operands.push(OperandId, "'Object'"); + InstructionDesc[OpStore].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpStore].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpStore].operands.push(OperandId, "", true); + + InstructionDesc[OpPhi].operands.push(OperandVariableIds, "'Variable, Parent, ...'"); + + InstructionDesc[OpDecorate].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpDecorateId].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateId].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateId].operands.push(OperandVariableIds, "See <>."); + + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandVariableLiteralStrings, "'Literal Strings'"); + + InstructionDesc[OpMemberDecorate].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandVariableLiteralStrings, "'Literal Strings'"); + + InstructionDesc[OpGroupDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupDecorate].operands.push(OperandVariableIds, "'Targets'"); + + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandVariableIdLiteral, "'Targets'"); + + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Component'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 2'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandVariableLiterals, "'Components'"); + + InstructionDesc[OpCompositeConstruct].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpCompositeExtract].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeExtract].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCopyObject].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemory].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Size'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Image'"); + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Sampler'"); + + InstructionDesc[OpImage].operands.push(OperandId, "'Sampled Image'"); + + InstructionDesc[OpImageRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Texel'"); + InstructionDesc[OpImageWrite].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageWrite].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseTexelsResident].operands.push(OperandId, "'Resident Code'"); + + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Level of Detail'"); + + InstructionDesc[OpImageQuerySize].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpImageQueryLevels].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQuerySamples].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryFormat].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryOrder].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpSNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpFNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpAny].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpAll].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpConvertFToU].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertFToS].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertSToF].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpConvertUToF].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpUConvert].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpSConvert].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpFConvert].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpSatConvertSToU].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpSatConvertUToS].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpConvertPtrToU].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpConvertUToPtr].operands.push(OperandId, "'Integer Value'"); + + InstructionDesc[OpPtrCastToGeneric].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtr].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandStorage, "'Storage'"); + + InstructionDesc[OpGenericPtrMemSemantics].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpBitcast].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpQuantizeToF16].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpTranspose].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpCopyLogical].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpIsNan].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsInf].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsFinite].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsNormal].operands.push(OperandId, "'x'"); + + InstructionDesc[OpSignBitSet].operands.push(OperandId, "'x'"); + + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'x'"); + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'y'"); + + InstructionDesc[OpOrdered].operands.push(OperandId, "'x'"); + InstructionDesc[OpOrdered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpUnordered].operands.push(OperandId, "'x'"); + InstructionDesc[OpUnordered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpArrayLength].operands.push(OperandId, "'Structure'"); + InstructionDesc[OpArrayLength].operands.push(OperandLiteralNumber, "'Array member'"); + + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'LeftMatrix'"); + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'RightMatrix'"); + + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Insert'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitReverse].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpBitCount].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpSelect].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 1'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 2'"); + + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpDPdx].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdy].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidth].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpEmitStreamVertex].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpEndStreamPrimitive].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpControlBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpMemoryBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpMemoryBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Sample'"); + + InstructionDesc[OpAtomicLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicStore].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicStore].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicIIncrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIDecrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAssumeTrueKHR].operands.push(OperandId, "'Condition'"); + + InstructionDesc[OpExpectKHR].operands.push(OperandId, "'Value'"); + InstructionDesc[OpExpectKHR].operands.push(OperandId, "'ExpectedValue'"); + + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicISub].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicISub].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFMinEXT].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFMinEXT].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFMinEXT].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicFMinEXT].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFMaxEXT].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFMaxEXT].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFMaxEXT].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicFMaxEXT].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicOr].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicOr].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicXor].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicXor].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicFlagClear].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'"); + InstructionDesc[OpLoopMerge].operands.push(OperandLoop, ""); + InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, ""); + + InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, ""); + + InstructionDesc[OpBranch].operands.push(OperandId, "'Target Label'"); + + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'True Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'False Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandVariableLiterals, "'Branch weights'"); + + InstructionDesc[OpSwitch].operands.push(OperandId, "'Selector'"); + InstructionDesc[OpSwitch].operands.push(OperandId, "'Default'"); + InstructionDesc[OpSwitch].operands.push(OperandVariableLiteralId, "'Target'"); + + + InstructionDesc[OpReturnValue].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpLifetimeStart].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStart].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpLifetimeStop].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStop].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Destination'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Source'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Num Elements'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGroupWaitEvents].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Events List'"); + + InstructionDesc[OpGroupAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAll].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAny].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'Value'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'LocalId'"); + + InstructionDesc[OpGroupIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMin].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMax].operands.push(OperandId, "X"); + + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpIsValidReserveId].operands.push(OperandId, "'Reserve Id'"); + + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'LocalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkOffset'"); + + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Event'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Profiling Info'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Event'"); + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Status'"); + + InstructionDesc[OpIsValidEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpRetainEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpReleaseEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Flags'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Ret Event'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Align'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandVariableIds, "'Local Size'"); + + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'"); + + InstructionDesc[OpGroupNonUniformElect].operands.push(OperandScope, "'Execution'"); + + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "ID"); + + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "Bit"); + + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "Mask"); + + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandId, "'Direction'"); + + InstructionDesc[OpSubgroupBallotKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupFirstInvocationKHR].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupNonUniformRotateKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformRotateKHR].operands.push(OperandId, "'X'"); + InstructionDesc[OpGroupNonUniformRotateKHR].operands.push(OperandId, "'Delta'"); + InstructionDesc[OpGroupNonUniformRotateKHR].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Value'"); + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpModuleProcessed].operands.push(OperandLiteralString, "'process'"); + + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Fragment Index'"); + + InstructionDesc[OpGroupNonUniformPartitionNV].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformQuadAllKHR].operands.push(OperandId, "'Predicate'"); + InstructionDesc[OpGroupNonUniformQuadAnyKHR].operands.push(OperandId, "'Predicate'"); + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Flags'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Cull Mask'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Origin'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Direction'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpTraceNV].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpTraceNV].setResultAndType(false, false); + + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Ray Flags'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Cull Mask'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Ray Origin'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Ray Direction'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Time'"); + InstructionDesc[OpTraceRayMotionNV].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpTraceRayMotionNV].setResultAndType(false, false); + + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Flags'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Cull Mask'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Origin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Direction'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpTraceRayKHR].setResultAndType(false, false); + + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Parameter'"); + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Kind'"); + + InstructionDesc[OpIgnoreIntersectionNV].setResultAndType(false, false); + + InstructionDesc[OpIgnoreIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpTerminateRayNV].setResultAndType(false, false); + + InstructionDesc[OpTerminateRayKHR].setResultAndType(false, false); + + InstructionDesc[OpExecuteCallableNV].operands.push(OperandId, "SBT Record Index"); + InstructionDesc[OpExecuteCallableNV].operands.push(OperandId, "CallableData ID"); + InstructionDesc[OpExecuteCallableNV].setResultAndType(false, false); + + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "SBT Record Index"); + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "CallableData"); + InstructionDesc[OpExecuteCallableKHR].setResultAndType(false, false); + + InstructionDesc[OpConvertUToAccelerationStructureKHR].operands.push(OperandId, "Value"); + InstructionDesc[OpConvertUToAccelerationStructureKHR].setResultAndType(true, true); + + // Ray Query + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + InstructionDesc[OpTypeRayQueryKHR].setResultAndType(true, false); + + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'AccelerationS'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayFlags'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'CullMask'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmax'"); + InstructionDesc[OpRayQueryInitializeKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryTerminateKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryTerminateKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'THit'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryConfirmIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryConfirmIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryProceedKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryProceedKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayTMinKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayTMinKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayFlagsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayFlagsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTriangleVertexPositionsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTriangleVertexPositionsKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTriangleVertexPositionsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionClusterIdNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionClusterIdNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionClusterIdNV].setResultAndType(true, true); + InstructionDesc[OpRayQueryGetIntersectionSpherePositionNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionSpherePositionNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionSpherePositionNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionSphereRadiusNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionSphereRadiusNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionSphereRadiusNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionLSSHitValueNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionLSSHitValueNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionLSSHitValueNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionLSSPositionsNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionLSSPositionsNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionLSSPositionsNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionLSSRadiiNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionLSSRadiiNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionLSSRadiiNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryIsSphereHitNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryIsSphereHitNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryIsSphereHitNV].setResultAndType(true, true); + + InstructionDesc[OpRayQueryIsLSSHitNV].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryIsLSSHitNV].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryIsLSSHitNV].setResultAndType(true, true); + + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Granularity'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coarse'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Index Offset'"); + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Packed Indices'"); + + InstructionDesc[OpEmitMeshTasksEXT].operands.push(OperandId, "'groupCountX'"); + InstructionDesc[OpEmitMeshTasksEXT].operands.push(OperandId, "'groupCountY'"); + InstructionDesc[OpEmitMeshTasksEXT].operands.push(OperandId, "'groupCountZ'"); + InstructionDesc[OpEmitMeshTasksEXT].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpEmitMeshTasksEXT].setResultAndType(false, false); + + InstructionDesc[OpSetMeshOutputsEXT].operands.push(OperandId, "'vertexCount'"); + InstructionDesc[OpSetMeshOutputsEXT].operands.push(OperandId, "'primitiveCount'"); + InstructionDesc[OpSetMeshOutputsEXT].setResultAndType(false, false); + + + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Scope'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Rows'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Columns'"); + + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'A'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'B'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'C'"); + + InstructionDesc[OpCooperativeMatrixLengthNV].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpTypeCooperativeMatrixKHR].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeCooperativeMatrixKHR].operands.push(OperandId, "'Scope'"); + InstructionDesc[OpTypeCooperativeMatrixKHR].operands.push(OperandId, "'Rows'"); + InstructionDesc[OpTypeCooperativeMatrixKHR].operands.push(OperandId, "'Columns'"); + InstructionDesc[OpTypeCooperativeMatrixKHR].operands.push(OperandId, "'Use'"); + + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandId, "'Memory Layout'"); + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixLoadKHR].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandId, "'Memory Layout'"); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixStoreKHR].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixMulAddKHR].operands.push(OperandId, "'A'"); + InstructionDesc[OpCooperativeMatrixMulAddKHR].operands.push(OperandId, "'B'"); + InstructionDesc[OpCooperativeMatrixMulAddKHR].operands.push(OperandId, "'C'"); + InstructionDesc[OpCooperativeMatrixMulAddKHR].operands.push(OperandCooperativeMatrixOperands, "'Cooperative Matrix Operands'", true); + + InstructionDesc[OpCooperativeMatrixLengthKHR].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpTypeCooperativeVectorNV].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeCooperativeVectorNV].operands.push(OperandId, "'Components'"); + + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'Input'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'InputInterpretation'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'MatrixOffset'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'MatrixInterpretation'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'M'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'K'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'MemoryLayout'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'Transpose'"); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandId, "'MatrixStride'", true); + InstructionDesc[OpCooperativeVectorMatrixMulNV].operands.push(OperandCooperativeMatrixOperands, "'Cooperative Matrix Operands'", true); + + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'Input'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'InputInterpretation'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'MatrixOffset'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'MatrixInterpretation'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'Bias'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'BiasOffset'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'BiasInterpretation'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'M'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'K'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'MemoryLayout'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'Transpose'"); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandId, "'MatrixStride'", true); + InstructionDesc[OpCooperativeVectorMatrixMulAddNV].operands.push(OperandCooperativeMatrixOperands, "'Cooperative Matrix Operands'", true); + + InstructionDesc[OpCooperativeVectorLoadNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeVectorLoadNV].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpCooperativeVectorLoadNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeVectorLoadNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeVectorLoadNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeVectorStoreNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'A'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'B'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'MemoryLayout'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'MatrixInterpretation'"); + InstructionDesc[OpCooperativeVectorOuterProductAccumulateNV].operands.push(OperandId, "'MatrixStride'", true); + + InstructionDesc[OpCooperativeVectorReduceSumAccumulateNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeVectorReduceSumAccumulateNV].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpCooperativeVectorReduceSumAccumulateNV].operands.push(OperandId, "'V'"); + + InstructionDesc[OpDemoteToHelperInvocationEXT].setResultAndType(false, false); + + InstructionDesc[OpReadClockKHR].operands.push(OperandScope, "'Scope'"); + + InstructionDesc[OpTypeHitObjectNV].setResultAndType(true, false); + + InstructionDesc[OpHitObjectGetShaderRecordBufferHandleNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetShaderRecordBufferHandleNV].setResultAndType(true, true); + + InstructionDesc[OpReorderThreadWithHintNV].operands.push(OperandId, "'Hint'"); + InstructionDesc[OpReorderThreadWithHintNV].operands.push(OperandId, "'Bits'"); + InstructionDesc[OpReorderThreadWithHintNV].setResultAndType(false, false); + + InstructionDesc[OpReorderThreadWithHitObjectNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpReorderThreadWithHitObjectNV].operands.push(OperandId, "'Hint'"); + InstructionDesc[OpReorderThreadWithHitObjectNV].operands.push(OperandId, "'Bits'"); + InstructionDesc[OpReorderThreadWithHitObjectNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectGetCurrentTimeNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetCurrentTimeNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetHitKindNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetHitKindNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetPrimitiveIndexNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetPrimitiveIndexNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetGeometryIndexNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetGeometryIndexNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetInstanceIdNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetInstanceIdNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetInstanceCustomIndexNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetInstanceCustomIndexNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetObjectRayDirectionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetObjectRayDirectionNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetObjectRayOriginNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetObjectRayOriginNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetWorldRayDirectionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetWorldRayDirectionNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetWorldRayOriginNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetWorldRayOriginNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetWorldToObjectNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetWorldToObjectNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetObjectToWorldNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetObjectToWorldNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetRayTMaxNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetRayTMaxNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetRayTMinNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetRayTMinNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetShaderBindingTableRecordIndexNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetShaderBindingTableRecordIndexNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectIsEmptyNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectIsEmptyNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectIsHitNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectIsHitNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectIsMissNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectIsMissNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetAttributesNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetAttributesNV].operands.push(OperandId, "'HitObjectAttribute'"); + InstructionDesc[OpHitObjectGetAttributesNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectExecuteShaderNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectExecuteShaderNV].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpHitObjectExecuteShaderNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'InstanceId'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'PrimitiveId'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'GeometryIndex'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'HitKind'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordHitNV].operands.push(OperandId, "'HitObject Attribute'"); + InstructionDesc[OpHitObjectRecordHitNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'InstanceId'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'PrimitiveId'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'GeometryIndex'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'HitKind'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'Current Time'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].operands.push(OperandId, "'HitObject Attribute'"); + InstructionDesc[OpHitObjectRecordHitMotionNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'InstanceId'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'PrimitiveId'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'GeometryIndex'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'HitKind'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'SBT Record Index'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].operands.push(OperandId, "'HitObject Attribute'"); + InstructionDesc[OpHitObjectRecordHitWithIndexNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'InstanceId'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'PrimitiveId'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'GeometryIndex'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'HitKind'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'SBT Record Index'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'Current Time'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].operands.push(OperandId, "'HitObject Attribute'"); + InstructionDesc[OpHitObjectRecordHitWithIndexMotionNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'SBT Index'"); + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordMissNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordMissNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'SBT Index'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].operands.push(OperandId, "'Current Time'"); + InstructionDesc[OpHitObjectRecordMissMotionNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectRecordEmptyNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectRecordEmptyNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'RayFlags'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Cullmask'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectTraceRayNV].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpHitObjectTraceRayNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'RayFlags'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Cullmask'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Time'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpHitObjectTraceRayMotionNV].setResultAndType(false, false); + + InstructionDesc[OpHitObjectGetClusterIdNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetClusterIdNV].setResultAndType(true, true); + InstructionDesc[OpHitObjectGetSpherePositionNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetSpherePositionNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetSphereRadiusNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetSphereRadiusNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetLSSPositionsNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetLSSPositionsNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectGetLSSRadiiNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectGetLSSRadiiNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectIsSphereHitNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectIsSphereHitNV].setResultAndType(true, true); + + InstructionDesc[OpHitObjectIsLSSHitNV].operands.push(OperandId, "'HitObject'"); + InstructionDesc[OpHitObjectIsLSSHitNV].setResultAndType(true, true); + + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].operands.push(OperandId, "'Instance ID'"); + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].operands.push(OperandId, "'Geometry Index'"); + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].operands.push(OperandId, "'Primitive Index'"); + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].operands.push(OperandId, "'Barycentrics'"); + InstructionDesc[OpFetchMicroTriangleVertexBarycentricNV].setResultAndType(true, true); + + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].operands.push(OperandId, "'Acceleration Structure'"); + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].operands.push(OperandId, "'Instance ID'"); + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].operands.push(OperandId, "'Geometry Index'"); + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].operands.push(OperandId, "'Primitive Index'"); + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].operands.push(OperandId, "'Barycentrics'"); + InstructionDesc[OpFetchMicroTriangleVertexPositionNV].setResultAndType(true, true); + + InstructionDesc[OpColorAttachmentReadEXT].operands.push(OperandId, "'Attachment'"); + InstructionDesc[OpColorAttachmentReadEXT].operands.push(OperandId, "'Sample'", true); + InstructionDesc[OpStencilAttachmentReadEXT].operands.push(OperandId, "'Sample'", true); + InstructionDesc[OpDepthAttachmentReadEXT].operands.push(OperandId, "'Sample'", true); + + InstructionDesc[OpImageSampleWeightedQCOM].operands.push(OperandId, "'source texture'"); + InstructionDesc[OpImageSampleWeightedQCOM].operands.push(OperandId, "'texture coordinates'"); + InstructionDesc[OpImageSampleWeightedQCOM].operands.push(OperandId, "'weights texture'"); + InstructionDesc[OpImageSampleWeightedQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleWeightedQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBoxFilterQCOM].operands.push(OperandId, "'source texture'"); + InstructionDesc[OpImageBoxFilterQCOM].operands.push(OperandId, "'texture coordinates'"); + InstructionDesc[OpImageBoxFilterQCOM].operands.push(OperandId, "'box size'"); + InstructionDesc[OpImageBoxFilterQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBoxFilterQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchSADQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchSADQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchSSDQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchSSDQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchWindowSSDQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchWindowSADQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchGatherSSDQCOM].setResultAndType(true, true); + + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandId, "'target texture'"); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandId, "'target coordinates'"); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandId, "'reference texture'"); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandId, "'reference coordinates'"); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandId, "'block size'"); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageBlockMatchGatherSADQCOM].setResultAndType(true, true); + + InstructionDesc[OpConstantCompositeReplicateEXT].operands.push(OperandId, "'Value'"); + InstructionDesc[OpSpecConstantCompositeReplicateEXT].operands.push(OperandId, "'Value'"); + InstructionDesc[OpCompositeConstructReplicateEXT].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpCooperativeMatrixConvertNV].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpCooperativeMatrixTransposeNV].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpCooperativeMatrixReduceNV].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpCooperativeMatrixReduceNV].operands.push(OperandLiteralNumber, "'ReduceMask'"); + InstructionDesc[OpCooperativeMatrixReduceNV].operands.push(OperandId, "'CombineFunc'"); + + InstructionDesc[OpCooperativeMatrixPerElementOpNV].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpCooperativeMatrixPerElementOpNV].operands.push(OperandId, "'Operation'"); + InstructionDesc[OpCooperativeMatrixPerElementOpNV].operands.push(OperandVariableIds, "'Operands'"); + + InstructionDesc[OpCooperativeMatrixLoadTensorNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixLoadTensorNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixLoadTensorNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpCooperativeMatrixLoadTensorNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixLoadTensorNV].operands.push(OperandTensorAddressingOperands, "'Tensor Addressing Operands'"); + + InstructionDesc[OpCooperativeMatrixStoreTensorNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixStoreTensorNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixStoreTensorNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpCooperativeMatrixStoreTensorNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixStoreTensorNV].operands.push(OperandTensorAddressingOperands, "'Tensor Addressing Operands'"); + + InstructionDesc[OpCooperativeMatrixReduceNV].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpCooperativeMatrixReduceNV].operands.push(OperandLiteralNumber, "'ReduceMask'"); + + InstructionDesc[OpTypeTensorLayoutNV].operands.push(OperandId, "'Dim'"); + InstructionDesc[OpTypeTensorLayoutNV].operands.push(OperandId, "'ClampMode'"); + + InstructionDesc[OpTypeTensorViewNV].operands.push(OperandId, "'Dim'"); + InstructionDesc[OpTypeTensorViewNV].operands.push(OperandId, "'HasDimensions'"); + InstructionDesc[OpTypeTensorViewNV].operands.push(OperandVariableIds, "'p'"); + + InstructionDesc[OpTensorLayoutSetBlockSizeNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpTensorLayoutSetBlockSizeNV].operands.push(OperandVariableIds, "'BlockSize'"); + + InstructionDesc[OpTensorLayoutSetDimensionNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpTensorLayoutSetDimensionNV].operands.push(OperandVariableIds, "'Dim'"); + + InstructionDesc[OpTensorLayoutSetStrideNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpTensorLayoutSetStrideNV].operands.push(OperandVariableIds, "'Stride'"); + + InstructionDesc[OpTensorLayoutSliceNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpTensorLayoutSliceNV].operands.push(OperandVariableIds, "'Operands'"); + + InstructionDesc[OpTensorLayoutSetClampValueNV].operands.push(OperandId, "'TensorLayout'"); + InstructionDesc[OpTensorLayoutSetClampValueNV].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpTensorViewSetDimensionNV].operands.push(OperandId, "'TensorView'"); + InstructionDesc[OpTensorViewSetDimensionNV].operands.push(OperandVariableIds, "'Dim'"); + + InstructionDesc[OpTensorViewSetStrideNV].operands.push(OperandId, "'TensorView'"); + InstructionDesc[OpTensorViewSetStrideNV].operands.push(OperandVariableIds, "'Stride'"); + + InstructionDesc[OpTensorViewSetClipNV].operands.push(OperandId, "'TensorView'"); + InstructionDesc[OpTensorViewSetClipNV].operands.push(OperandId, "'ClipRowOffset'"); + InstructionDesc[OpTensorViewSetClipNV].operands.push(OperandId, "'ClipRowSpan'"); + InstructionDesc[OpTensorViewSetClipNV].operands.push(OperandId, "'ClipColOffset'"); + InstructionDesc[OpTensorViewSetClipNV].operands.push(OperandId, "'ClipColSpan'"); + + InstructionDesc[OpSDotKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpSDotKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpSDotKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + + InstructionDesc[OpUDotKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpUDotKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpUDotKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + + InstructionDesc[OpSUDotKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpSUDotKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpSUDotKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + + InstructionDesc[OpSDotAccSatKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpSDotAccSatKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpSDotAccSatKHR].operands.push(OperandId, "'Accumulator'"); + InstructionDesc[OpSDotAccSatKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + + InstructionDesc[OpUDotAccSatKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpUDotAccSatKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpUDotAccSatKHR].operands.push(OperandId, "'Accumulator'"); + InstructionDesc[OpUDotAccSatKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + + InstructionDesc[OpSUDotAccSatKHR].operands.push(OperandId, "'Vector1'"); + InstructionDesc[OpSUDotAccSatKHR].operands.push(OperandId, "'Vector2'"); + InstructionDesc[OpSUDotAccSatKHR].operands.push(OperandId, "'Accumulator'"); + InstructionDesc[OpSUDotAccSatKHR].operands.push(OperandLiteralNumber, "'PackedVectorFormat'"); + }); +} + +} // end spv namespace diff --git a/engine/engine/3rdlibs/glslang/SPIRV/doc.h b/engine/engine/3rdlibs/glslang/SPIRV/doc.h new file mode 100644 index 00000000..8cd388a1 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/doc.h @@ -0,0 +1,261 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Parameterize the SPIR-V enumerants. +// + +#pragma once + +#include "spirv.hpp" + +#include + +namespace spv { + +// Fill in all the parameters +void Parameterize(); + +// Return the English names of all the enums. +const char* SourceString(int); +const char* AddressingString(int); +const char* MemoryString(int); +const char* ExecutionModelString(int); +const char* ExecutionModeString(int); +const char* StorageClassString(int); +const char* DecorationString(int); +const char* BuiltInString(int); +const char* DimensionString(int); +const char* SelectControlString(int); +const char* LoopControlString(int); +const char* FunctionControlString(int); +const char* SamplerAddressingModeString(int); +const char* SamplerFilterModeString(int); +const char* ImageFormatString(int); +const char* ImageChannelOrderString(int); +const char* ImageChannelTypeString(int); +const char* ImageChannelDataTypeString(int type); +const char* ImageOperandsString(int format); +const char* ImageOperands(int); +const char* FPFastMathString(int); +const char* FPRoundingModeString(int); +const char* LinkageTypeString(int); +const char* FuncParamAttrString(int); +const char* AccessQualifierString(int); +const char* MemorySemanticsString(int); +const char* MemoryAccessString(int); +const char* ExecutionScopeString(int); +const char* GroupOperationString(int); +const char* KernelEnqueueFlagsString(int); +const char* KernelProfilingInfoString(int); +const char* CapabilityString(int); +const char* OpcodeString(int); +const char* ScopeString(int mem); + +// For grouping opcodes into subsections +enum OpcodeClass { + OpClassMisc, + OpClassDebug, + OpClassAnnotate, + OpClassExtension, + OpClassMode, + OpClassType, + OpClassConstant, + OpClassMemory, + OpClassFunction, + OpClassImage, + OpClassConvert, + OpClassComposite, + OpClassArithmetic, + OpClassBit, + OpClassRelationalLogical, + OpClassDerivative, + OpClassFlowControl, + OpClassAtomic, + OpClassPrimitive, + OpClassBarrier, + OpClassGroup, + OpClassDeviceSideEnqueue, + OpClassPipe, + + OpClassCount, + OpClassMissing // all instructions start out as missing +}; + +// For parameterizing operands. +enum OperandClass { + OperandNone, + OperandId, + OperandVariableIds, + OperandOptionalLiteral, + OperandOptionalLiteralString, + OperandVariableLiterals, + OperandVariableIdLiteral, + OperandVariableLiteralId, + OperandLiteralNumber, + OperandLiteralString, + OperandVariableLiteralStrings, + OperandSource, + OperandExecutionModel, + OperandAddressing, + OperandMemory, + OperandExecutionMode, + OperandStorage, + OperandDimensionality, + OperandSamplerAddressingMode, + OperandSamplerFilterMode, + OperandSamplerImageFormat, + OperandImageChannelOrder, + OperandImageChannelDataType, + OperandImageOperands, + OperandFPFastMath, + OperandFPRoundingMode, + OperandLinkageType, + OperandAccessQualifier, + OperandFuncParamAttr, + OperandDecoration, + OperandBuiltIn, + OperandSelect, + OperandLoop, + OperandFunction, + OperandMemorySemantics, + OperandMemoryAccess, + OperandScope, + OperandGroupOperation, + OperandKernelEnqueueFlags, + OperandKernelProfilingInfo, + OperandCapability, + OperandCooperativeMatrixOperands, + OperandTensorAddressingOperands, + + OperandOpcode, + + OperandCount +}; + +// Any specific enum can have a set of capabilities that allow it: +typedef std::vector EnumCaps; + +// Parameterize a set of operands with their OperandClass(es) and descriptions. +class OperandParameters { +public: + OperandParameters() { } + void push(OperandClass oc, const char* d, bool opt = false) + { + opClass.push_back(oc); + desc.push_back(d); + optional.push_back(opt); + } + void setOptional(); + OperandClass getClass(int op) const { return opClass[op]; } + const char* getDesc(int op) const { return desc[op]; } + bool isOptional(int op) const { return optional[op]; } + int getNum() const { return (int)opClass.size(); } + +protected: + std::vector opClass; + std::vector desc; + std::vector optional; +}; + +// Parameterize an enumerant +class EnumParameters { +public: + EnumParameters() : desc(nullptr) { } + const char* desc; +}; + +// Parameterize a set of enumerants that form an enum +class EnumDefinition : public EnumParameters { +public: + EnumDefinition() : + ceiling(0), bitmask(false), getName(nullptr), enumParams(nullptr), operandParams(nullptr) { } + void set(int ceil, const char* (*name)(int), EnumParameters* ep, bool mask = false) + { + ceiling = ceil; + getName = name; + bitmask = mask; + enumParams = ep; + } + void setOperands(OperandParameters* op) { operandParams = op; } + int ceiling; // ceiling of enumerants + bool bitmask; // true if these enumerants combine into a bitmask + const char* (*getName)(int); // a function that returns the name for each enumerant value (or shift) + EnumParameters* enumParams; // parameters for each individual enumerant + OperandParameters* operandParams; // sets of operands +}; + +// Parameterize an instruction's logical format, including its known set of operands, +// per OperandParameters above. +class InstructionParameters { +public: + InstructionParameters() : + opDesc("TBD"), + opClass(OpClassMissing), + typePresent(true), // most normal, only exceptions have to be spelled out + resultPresent(true) // most normal, only exceptions have to be spelled out + { } + + void setResultAndType(bool r, bool t) + { + resultPresent = r; + typePresent = t; + } + + bool hasResult() const { return resultPresent != 0; } + bool hasType() const { return typePresent != 0; } + + const char* opDesc; + OpcodeClass opClass; + OperandParameters operands; + +protected: + bool typePresent : 1; + bool resultPresent : 1; +}; + +// The set of objects that hold all the instruction/operand +// parameterization information. +extern InstructionParameters InstructionDesc[]; + +// These hold definitions of the enumerants used for operands +extern EnumDefinition OperandClassParams[]; + +const char* GetOperandDesc(OperandClass operand); +void PrintImmediateRow(int imm, const char* name, const EnumParameters* enumParams, bool caps, bool hex = false); +const char* AccessQualifierString(int attr); + +void PrintOperands(const OperandParameters& operands, int reservedOperands); + +} // end namespace spv diff --git a/engine/engine/3rdlibs/glslang/SPIRV/hex_float.h b/engine/engine/3rdlibs/glslang/SPIRV/hex_float.h new file mode 100644 index 00000000..785e8af1 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/hex_float.h @@ -0,0 +1,1065 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_HEX_FLOAT_H_ +#define LIBSPIRV_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "bitutils.h" + +namespace spvutils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() {} + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + typedef void uint_type; +}; + +template <> +struct FloatProxyTraits { + typedef uint32_t uint_type; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint64_t uint_type; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint16_t uint_type; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + typedef typename FloatProxyTraits::uint_type uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() {} + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = BitwiseCast(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return BitwiseCast(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store this hex-float. + typedef void uint_type; + // Signed integer type that can store this hex-float. + typedef void int_type; + // The numerical type that this HexFloat represents. + typedef void underlying_type; + // The type needed to construct the underlying type. + typedef void native_type; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint32_t uint_type; + typedef int32_t int_type; + typedef FloatProxy underlying_type; + typedef float native_type; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint64_t uint_type; + typedef int64_t int_type; + typedef FloatProxy underlying_type; + typedef double native_type; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint16_t uint_type; + typedef int16_t int_type; + typedef uint16_t underlying_type; + typedef uint16_t native_type; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum round_direction { + kRoundToZero, + kRoundToNearestEven, + kRoundToPositiveInfinity, + kRoundToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + typedef typename Traits::uint_type uint_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::underlying_type underlying_type; + typedef typename Traits::native_type native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + spvutils::SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + spvutils::SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + spvutils::SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return spvutils::BitwiseCast(value_); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(spvutils::BitwiseCast(value_) & + ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = BitwiseCast(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + + template + uint_type negatable_left_shift(int_type N, uint_type val) + { + if(N >= 0) + return val << N; + + return val >> -N; + } + + template + uint_type negatable_right_shift(int_type N, uint_type val) + { + if(N >= 0) + return val >> N; + + return val << -N; + } + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + typedef typename other_T::uint_type other_uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift(num_throwaway_bits, 1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift(num_throwaway_bits - 1, 1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + spvutils::SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case kRoundToZero: + break; + case kRoundToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case kRoundToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case kRoundToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift(num_throwaway_bits, incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift( + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits), significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == kRoundToNegativeInfinity + : round_dir == kRoundToPositiveInfinity; + typedef typename other_T::int_type other_int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + typedef HexFloat HF; + typedef typename HF::uint_type uint_type; + typedef typename HF::int_type int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = spvutils::BitwiseCast(value.value()); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type(0)); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type(0)); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() || negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, kRoundToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + uint_type fraction_index = 0; + + uint_type fraction = 0; + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + bool is_denorm = + true; // Assume denorm "representation" until we hear otherwise. + // NB: This does not mean the value is actually denorm, + // it just means that it was written 0. + bool bits_written = false; // Stays false until we write a bit. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We know this is not denormalized since we have stripped all leading + // zeroes and we are not a ".". + is_denorm = false; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + exponent = static_cast(exponent + 1); + } + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if (is_denorm && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + exponent = static_cast(exponent - 1); + } else { + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + bool seen_sign = false; + int8_t exponent_sign = 1; + int_type written_exponent = 0; + while (true) { + if ((next_char == '-' || next_char == '+')) { + if (seen_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + // Hex-floats express their exponent as decimal. + written_exponent = static_cast(written_exponent * 10); + written_exponent = + static_cast(written_exponent + (next_char - '0')); + } else { + break; + } + is.get(); + next_char = is.peek(); + } + + written_exponent = static_cast(written_exponent * exponent_sign); + exponent = static_cast(exponent + written_exponent); + + bool is_zero = is_denorm && (fraction == 0); + if (is_denorm && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle actual denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float = spvutils::BitwiseCast(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} +} + +#endif // LIBSPIRV_UTIL_HEX_FLOAT_H_ diff --git a/engine/engine/3rdlibs/glslang/SPIRV/spirv.hpp b/engine/engine/3rdlibs/glslang/SPIRV/spirv.hpp new file mode 100644 index 00000000..a5d10fed --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/spirv.hpp @@ -0,0 +1,5054 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010600; +static const unsigned int Revision = 1; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageCPP_for_OpenCL = 6, + SourceLanguageSYCL = 7, + SourceLanguageHERO_C = 8, + SourceLanguageNZSL = 9, + SourceLanguageWGSL = 10, + SourceLanguageSlang = 11, + SourceLanguageZig = 12, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelTaskEXT = 5364, + ExecutionModelMeshEXT = 5365, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + ExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + ExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + ExecutionModeSubgroupUniformControlFlowKHR = 4421, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeCoalescingAMDX = 5069, + ExecutionModeIsApiEntryAMDX = 5070, + ExecutionModeMaxNodeRecursionAMDX = 5071, + ExecutionModeStaticNumWorkgroupsAMDX = 5072, + ExecutionModeShaderIndexAMDX = 5073, + ExecutionModeMaxNumWorkgroupsAMDX = 5077, + ExecutionModeStencilRefUnchangedFrontAMD = 5079, + ExecutionModeStencilRefGreaterFrontAMD = 5080, + ExecutionModeStencilRefLessFrontAMD = 5081, + ExecutionModeStencilRefUnchangedBackAMD = 5082, + ExecutionModeStencilRefGreaterBackAMD = 5083, + ExecutionModeStencilRefLessBackAMD = 5084, + ExecutionModeQuadDerivativesKHR = 5088, + ExecutionModeRequireFullQuadsKHR = 5089, + ExecutionModeSharesInputWithAMDX = 5102, + ExecutionModeOutputLinesEXT = 5269, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesEXT = 5270, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsKHR = 5289, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearKHR = 5290, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesEXT = 5298, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeSharedLocalMemorySizeINTEL = 5618, + ExecutionModeRoundingModeRTPINTEL = 5620, + ExecutionModeRoundingModeRTNINTEL = 5621, + ExecutionModeFloatingPointModeALTINTEL = 5622, + ExecutionModeFloatingPointModeIEEEINTEL = 5623, + ExecutionModeMaxWorkgroupSizeINTEL = 5893, + ExecutionModeMaxWorkDimINTEL = 5894, + ExecutionModeNoGlobalOffsetINTEL = 5895, + ExecutionModeNumSIMDWorkitemsINTEL = 5896, + ExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + ExecutionModeMaximallyReconvergesKHR = 6023, + ExecutionModeFPFastMathDefault = 6028, + ExecutionModeStreamingInterfaceINTEL = 6154, + ExecutionModeRegisterMapInterfaceINTEL = 6160, + ExecutionModeNamedBarrierCountINTEL = 6417, + ExecutionModeMaximumRegistersINTEL = 6461, + ExecutionModeMaximumRegistersIdINTEL = 6462, + ExecutionModeNamedMaximumRegistersINTEL = 6463, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassTileImageEXT = 4172, + StorageClassNodePayloadAMDX = 5068, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassHitObjectAttributeNV = 5385, + StorageClassTaskPayloadWorkgroupEXT = 5402, + StorageClassCodeSectionINTEL = 5605, + StorageClassDeviceOnlyINTEL = 5936, + StorageClassHostOnlyINTEL = 5937, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimTileImageDataEXT = 4173, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatR64ui = 40, + ImageFormatR64i = 41, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeUnsignedIntRaw10EXT = 19, + ImageChannelDataTypeUnsignedIntRaw12EXT = 20, + ImageChannelDataTypeUnormInt2_101010EXT = 21, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsNontemporalShift = 14, + ImageOperandsOffsetsShift = 16, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask : unsigned { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, + ImageOperandsNontemporalMask = 0x00004000, + ImageOperandsOffsetsMask = 0x00010000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeAllowContractShift = 16, + FPFastMathModeAllowContractFastINTELShift = 16, + FPFastMathModeAllowReassocShift = 17, + FPFastMathModeAllowReassocINTELShift = 17, + FPFastMathModeAllowTransformShift = 18, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask : unsigned { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, + FPFastMathModeAllowContractMask = 0x00010000, + FPFastMathModeAllowContractFastINTELMask = 0x00010000, + FPFastMathModeAllowReassocMask = 0x00020000, + FPFastMathModeAllowReassocINTELMask = 0x00020000, + FPFastMathModeAllowTransformMask = 0x00040000, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeLinkOnceODR = 2, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeRuntimeAlignedINTEL = 5940, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationWeightTextureQCOM = 4487, + DecorationBlockMatchTextureQCOM = 4488, + DecorationBlockMatchSamplerQCOM = 4499, + DecorationExplicitInterpAMD = 4999, + DecorationNodeSharesPayloadLimitsWithAMDX = 5019, + DecorationNodeMaxPayloadsAMDX = 5020, + DecorationTrackFinishWritingAMDX = 5078, + DecorationPayloadNodeNameAMDX = 5091, + DecorationPayloadNodeBaseIndexAMDX = 5098, + DecorationPayloadNodeSparseArrayAMDX = 5099, + DecorationPayloadNodeArraySizeAMDX = 5100, + DecorationPayloadDispatchIndirectAMDX = 5105, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveEXT = 5271, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexKHR = 5285, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationHitObjectShaderRecordBufferNV = 5386, + DecorationBindlessSamplerNV = 5398, + DecorationBindlessImageNV = 5399, + DecorationBoundSamplerNV = 5400, + DecorationBoundImageNV = 5401, + DecorationSIMTCallINTEL = 5599, + DecorationReferencedIndirectlyINTEL = 5602, + DecorationClobberINTEL = 5607, + DecorationSideEffectsINTEL = 5608, + DecorationVectorComputeVariableINTEL = 5624, + DecorationFuncParamIOKindINTEL = 5625, + DecorationVectorComputeFunctionINTEL = 5626, + DecorationStackCallINTEL = 5627, + DecorationGlobalVariableOffsetINTEL = 5628, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationFunctionRoundingModeINTEL = 5822, + DecorationFunctionDenormModeINTEL = 5823, + DecorationRegisterINTEL = 5825, + DecorationMemoryINTEL = 5826, + DecorationNumbanksINTEL = 5827, + DecorationBankwidthINTEL = 5828, + DecorationMaxPrivateCopiesINTEL = 5829, + DecorationSinglepumpINTEL = 5830, + DecorationDoublepumpINTEL = 5831, + DecorationMaxReplicatesINTEL = 5832, + DecorationSimpleDualPortINTEL = 5833, + DecorationMergeINTEL = 5834, + DecorationBankBitsINTEL = 5835, + DecorationForcePow2DepthINTEL = 5836, + DecorationStridesizeINTEL = 5883, + DecorationWordsizeINTEL = 5884, + DecorationTrueDualPortINTEL = 5885, + DecorationBurstCoalesceINTEL = 5899, + DecorationCacheSizeINTEL = 5900, + DecorationDontStaticallyCoalesceINTEL = 5901, + DecorationPrefetchINTEL = 5902, + DecorationStallEnableINTEL = 5905, + DecorationFuseLoopsInFunctionINTEL = 5907, + DecorationMathOpDSPModeINTEL = 5909, + DecorationAliasScopeINTEL = 5914, + DecorationNoAliasINTEL = 5915, + DecorationInitiationIntervalINTEL = 5917, + DecorationMaxConcurrencyINTEL = 5918, + DecorationPipelineEnableINTEL = 5919, + DecorationBufferLocationINTEL = 5921, + DecorationIOPipeStorageINTEL = 5944, + DecorationFunctionFloatingPointModeINTEL = 6080, + DecorationSingleElementVectorINTEL = 6085, + DecorationVectorComputeCallableFunctionINTEL = 6087, + DecorationMediaBlockIOINTEL = 6140, + DecorationStallFreeINTEL = 6151, + DecorationFPMaxErrorDecorationINTEL = 6170, + DecorationLatencyControlLabelINTEL = 6172, + DecorationLatencyControlConstraintINTEL = 6173, + DecorationConduitKernelArgumentINTEL = 6175, + DecorationRegisterMapKernelArgumentINTEL = 6176, + DecorationMMHostInterfaceAddressWidthINTEL = 6177, + DecorationMMHostInterfaceDataWidthINTEL = 6178, + DecorationMMHostInterfaceLatencyINTEL = 6179, + DecorationMMHostInterfaceReadWriteModeINTEL = 6180, + DecorationMMHostInterfaceMaxBurstINTEL = 6181, + DecorationMMHostInterfaceWaitRequestINTEL = 6182, + DecorationStableKernelArgumentINTEL = 6183, + DecorationHostAccessINTEL = 6188, + DecorationInitModeINTEL = 6190, + DecorationImplementInRegisterMapINTEL = 6191, + DecorationCacheControlLoadINTEL = 6442, + DecorationCacheControlStoreINTEL = 6443, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInCoreIDARM = 4160, + BuiltInCoreCountARM = 4161, + BuiltInCoreMaxIDARM = 4162, + BuiltInWarpIDARM = 4163, + BuiltInWarpMaxIDARM = 4164, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInPrimitiveShadingRateKHR = 4432, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInShadingRateKHR = 4444, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInRemainingRecursionLevelsAMDX = 5021, + BuiltInShaderIndexAMDX = 5073, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordKHR = 5286, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspKHR = 5287, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInPrimitivePointIndicesEXT = 5294, + BuiltInPrimitiveLineIndicesEXT = 5295, + BuiltInPrimitiveTriangleIndicesEXT = 5296, + BuiltInCullPrimitiveEXT = 5299, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInCurrentRayTimeNV = 5334, + BuiltInHitTriangleVertexPositionsKHR = 5335, + BuiltInHitMicroTriangleVertexPositionsNV = 5337, + BuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInHitIsSphereNV = 5359, + BuiltInHitIsLSSNV = 5360, + BuiltInHitSpherePositionNV = 5361, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInHitLSSPositionsNV = 5396, + BuiltInHitKindFrontFacingMicroTriangleNV = 5405, + BuiltInHitKindBackFacingMicroTriangleNV = 5406, + BuiltInHitSphereRadiusNV = 5420, + BuiltInHitLSSRadiiNV = 5421, + BuiltInClusterIDNV = 5436, + BuiltInCullMaskKHR = 6021, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask : unsigned { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlInitiationIntervalINTELShift = 16, + LoopControlMaxConcurrencyINTELShift = 17, + LoopControlDependencyArrayINTELShift = 18, + LoopControlPipelineEnableINTELShift = 19, + LoopControlLoopCoalesceINTELShift = 20, + LoopControlMaxInterleavingINTELShift = 21, + LoopControlSpeculatedIterationsINTELShift = 22, + LoopControlNoFusionINTELShift = 23, + LoopControlLoopCountINTELShift = 24, + LoopControlMaxReinvocationDelayINTELShift = 25, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask : unsigned { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, + LoopControlInitiationIntervalINTELMask = 0x00010000, + LoopControlMaxConcurrencyINTELMask = 0x00020000, + LoopControlDependencyArrayINTELMask = 0x00040000, + LoopControlPipelineEnableINTELMask = 0x00080000, + LoopControlLoopCoalesceINTELMask = 0x00100000, + LoopControlMaxInterleavingINTELMask = 0x00200000, + LoopControlSpeculatedIterationsINTELMask = 0x00400000, + LoopControlNoFusionINTELMask = 0x00800000, + LoopControlLoopCountINTELMask = 0x01000000, + LoopControlMaxReinvocationDelayINTELMask = 0x02000000, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlOptNoneEXTShift = 16, + FunctionControlOptNoneINTELShift = 16, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask : unsigned { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, + FunctionControlOptNoneEXTMask = 0x00010000, + FunctionControlOptNoneINTELMask = 0x00010000, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask : unsigned { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessAliasScopeINTELMaskShift = 16, + MemoryAccessNoAliasINTELMaskShift = 17, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask : unsigned { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, + MemoryAccessAliasScopeINTELMaskMask = 0x00010000, + MemoryAccessNoAliasINTELMaskMask = 0x00020000, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask : unsigned { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilityUniformDecoration = 71, + CapabilityCoreBuiltinsARM = 4165, + CapabilityTileImageColorReadAccessEXT = 4166, + CapabilityTileImageDepthReadAccessEXT = 4167, + CapabilityTileImageStencilReadAccessEXT = 4168, + CapabilityCooperativeMatrixLayoutsARM = 4201, + CapabilityFragmentShadingRateKHR = 4422, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayQueryKHR = 4472, + CapabilityUntypedPointersKHR = 4473, + CapabilityRayTraversalPrimitiveCullingKHR = 4478, + CapabilityRayTracingKHR = 4479, + CapabilityTextureSampleWeightedQCOM = 4484, + CapabilityTextureBoxFilterQCOM = 4485, + CapabilityTextureBlockMatchQCOM = 4486, + CapabilityTextureBlockMatch2QCOM = 4498, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityInt64ImageEXT = 5016, + CapabilityShaderClockKHR = 5055, + CapabilityShaderEnqueueAMDX = 5067, + CapabilityQuadControlKHR = 5087, + CapabilityBFloat16TypeKHR = 5116, + CapabilityBFloat16DotProductKHR = 5117, + CapabilityBFloat16CooperativeMatrixKHR = 5118, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityMeshShadingEXT = 5283, + CapabilityFragmentBarycentricKHR = 5284, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsKHR = 5288, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingPositionFetchKHR = 5336, + CapabilityRayTracingNV = 5340, + CapabilityRayTracingMotionBlurNV = 5341, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearKHR = 5350, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocation = 5379, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilityDisplacementMicromapNV = 5380, + CapabilityRayTracingOpacityMicromapEXT = 5381, + CapabilityShaderInvocationReorderNV = 5383, + CapabilityBindlessTextureNV = 5390, + CapabilityRayQueryPositionFetchKHR = 5391, + CapabilityCooperativeVectorNV = 5394, + CapabilityAtomicFloat16VectorNV = 5404, + CapabilityRayTracingDisplacementMicromapNV = 5409, + CapabilityRawAccessChainsNV = 5414, + CapabilityRayTracingSpheresGeometryNV = 5418, + CapabilityRayTracingLinearSweptSpheresGeometryNV = 5419, + CapabilityCooperativeMatrixReductionsNV = 5430, + CapabilityCooperativeMatrixConversionsNV = 5431, + CapabilityCooperativeMatrixPerElementOperationsNV = 5432, + CapabilityCooperativeMatrixTensorAddressingNV = 5433, + CapabilityCooperativeMatrixBlockLoadsNV = 5434, + CapabilityCooperativeVectorTrainingNV = 5435, + CapabilityRayTracingClusterAccelerationStructureNV = 5437, + CapabilityTensorAddressingNV = 5439, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityRoundToInfinityINTEL = 5582, + CapabilityFloatingPointModeINTEL = 5583, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilityFunctionPointersINTEL = 5603, + CapabilityIndirectReferencesINTEL = 5604, + CapabilityAsmINTEL = 5606, + CapabilityAtomicFloat32MinMaxEXT = 5612, + CapabilityAtomicFloat64MinMaxEXT = 5613, + CapabilityAtomicFloat16MinMaxEXT = 5616, + CapabilityVectorComputeINTEL = 5617, + CapabilityVectorAnyINTEL = 5619, + CapabilityExpectAssumeKHR = 5629, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityVariableLengthArrayINTEL = 5817, + CapabilityFunctionFloatControlINTEL = 5821, + CapabilityFPGAMemoryAttributesINTEL = 5824, + CapabilityFPFastMathModeINTEL = 5837, + CapabilityArbitraryPrecisionIntegersINTEL = 5844, + CapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + CapabilityUnstructuredLoopControlsINTEL = 5886, + CapabilityFPGALoopControlsINTEL = 5888, + CapabilityKernelAttributesINTEL = 5892, + CapabilityFPGAKernelAttributesINTEL = 5897, + CapabilityFPGAMemoryAccessesINTEL = 5898, + CapabilityFPGAClusterAttributesINTEL = 5904, + CapabilityLoopFuseINTEL = 5906, + CapabilityFPGADSPControlINTEL = 5908, + CapabilityMemoryAccessAliasingINTEL = 5910, + CapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + CapabilityFPGABufferLocationINTEL = 5920, + CapabilityArbitraryPrecisionFixedPointINTEL = 5922, + CapabilityUSMStorageClassesINTEL = 5935, + CapabilityRuntimeAlignedAttributeINTEL = 5939, + CapabilityIOPipesINTEL = 5943, + CapabilityBlockingPipesINTEL = 5945, + CapabilityFPGARegINTEL = 5948, + CapabilityDotProductInputAll = 6016, + CapabilityDotProductInputAllKHR = 6016, + CapabilityDotProductInput4x8Bit = 6017, + CapabilityDotProductInput4x8BitKHR = 6017, + CapabilityDotProductInput4x8BitPacked = 6018, + CapabilityDotProductInput4x8BitPackedKHR = 6018, + CapabilityDotProduct = 6019, + CapabilityDotProductKHR = 6019, + CapabilityRayCullMaskKHR = 6020, + CapabilityCooperativeMatrixKHR = 6022, + CapabilityReplicatedCompositesEXT = 6024, + CapabilityBitInstructions = 6025, + CapabilityGroupNonUniformRotateKHR = 6026, + CapabilityFloatControls2 = 6029, + CapabilityAtomicFloat32AddEXT = 6033, + CapabilityAtomicFloat64AddEXT = 6034, + CapabilityLongCompositesINTEL = 6089, + CapabilityOptNoneEXT = 6094, + CapabilityOptNoneINTEL = 6094, + CapabilityAtomicFloat16AddEXT = 6095, + CapabilityDebugInfoModuleINTEL = 6114, + CapabilityBFloat16ConversionINTEL = 6115, + CapabilitySplitBarrierINTEL = 6141, + CapabilityArithmeticFenceEXT = 6144, + CapabilityFPGAClusterAttributesV2INTEL = 6150, + CapabilityFPGAKernelAttributesv2INTEL = 6161, + CapabilityFPMaxErrorINTEL = 6169, + CapabilityFPGALatencyControlINTEL = 6171, + CapabilityFPGAArgumentInterfacesINTEL = 6174, + CapabilityGlobalVariableHostAccessINTEL = 6187, + CapabilityGlobalVariableFPGADecorationsINTEL = 6189, + CapabilitySubgroupBufferPrefetchINTEL = 6220, + CapabilityGroupUniformArithmeticKHR = 6400, + CapabilityMaskedGatherScatterINTEL = 6427, + CapabilityCacheControlsINTEL = 6441, + CapabilityRegisterLimitsINTEL = 6460, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsForceOpacityMicromap2StateEXTShift = 10, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask : unsigned { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, + RayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum FragmentShadingRateShift { + FragmentShadingRateVertical2PixelsShift = 0, + FragmentShadingRateVertical4PixelsShift = 1, + FragmentShadingRateHorizontal2PixelsShift = 2, + FragmentShadingRateHorizontal4PixelsShift = 3, + FragmentShadingRateMax = 0x7fffffff, +}; + +enum FragmentShadingRateMask : unsigned { + FragmentShadingRateMaskNone = 0, + FragmentShadingRateVertical2PixelsMask = 0x00000001, + FragmentShadingRateVertical4PixelsMask = 0x00000002, + FragmentShadingRateHorizontal2PixelsMask = 0x00000004, + FragmentShadingRateHorizontal4PixelsMask = 0x00000008, +}; + +enum FPDenormMode { + FPDenormModePreserve = 0, + FPDenormModeFlushToZero = 1, + FPDenormModeMax = 0x7fffffff, +}; + +enum FPOperationMode { + FPOperationModeIEEE = 0, + FPOperationModeALT = 1, + FPOperationModeMax = 0x7fffffff, +}; + +enum QuantizationModes { + QuantizationModesTRN = 0, + QuantizationModesTRN_ZERO = 1, + QuantizationModesRND = 2, + QuantizationModesRND_ZERO = 3, + QuantizationModesRND_INF = 4, + QuantizationModesRND_MIN_INF = 5, + QuantizationModesRND_CONV = 6, + QuantizationModesRND_CONV_ODD = 7, + QuantizationModesMax = 0x7fffffff, +}; + +enum OverflowModes { + OverflowModesWRAP = 0, + OverflowModesSAT = 1, + OverflowModesSAT_ZERO = 2, + OverflowModesSAT_SYM = 3, + OverflowModesMax = 0x7fffffff, +}; + +enum PackedVectorFormat { + PackedVectorFormatPackedVectorFormat4x8Bit = 0, + PackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + PackedVectorFormatMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsShift { + CooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + CooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + CooperativeMatrixOperandsMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsMask : unsigned { + CooperativeMatrixOperandsMaskNone = 0, + CooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + CooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +}; + +enum CooperativeMatrixLayout { + CooperativeMatrixLayoutRowMajorKHR = 0, + CooperativeMatrixLayoutColumnMajorKHR = 1, + CooperativeMatrixLayoutRowBlockedInterleavedARM = 4202, + CooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203, + CooperativeMatrixLayoutMax = 0x7fffffff, +}; + +enum CooperativeMatrixUse { + CooperativeMatrixUseMatrixAKHR = 0, + CooperativeMatrixUseMatrixBKHR = 1, + CooperativeMatrixUseMatrixAccumulatorKHR = 2, + CooperativeMatrixUseMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceShift { + CooperativeMatrixReduceRowShift = 0, + CooperativeMatrixReduceColumnShift = 1, + CooperativeMatrixReduce2x2Shift = 2, + CooperativeMatrixReduceMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceMask : unsigned { + CooperativeMatrixReduceMaskNone = 0, + CooperativeMatrixReduceRowMask = 0x00000001, + CooperativeMatrixReduceColumnMask = 0x00000002, + CooperativeMatrixReduce2x2Mask = 0x00000004, +}; + +enum TensorClampMode { + TensorClampModeUndefined = 0, + TensorClampModeConstant = 1, + TensorClampModeClampToEdge = 2, + TensorClampModeRepeat = 3, + TensorClampModeRepeatMirrored = 4, + TensorClampModeMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsShift { + TensorAddressingOperandsTensorViewShift = 0, + TensorAddressingOperandsDecodeFuncShift = 1, + TensorAddressingOperandsMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsMask : unsigned { + TensorAddressingOperandsMaskNone = 0, + TensorAddressingOperandsTensorViewMask = 0x00000001, + TensorAddressingOperandsDecodeFuncMask = 0x00000002, +}; + +enum InitializationModeQualifier { + InitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + InitializationModeQualifierInitOnDeviceResetINTEL = 1, + InitializationModeQualifierMax = 0x7fffffff, +}; + +enum HostAccessQualifier { + HostAccessQualifierNoneINTEL = 0, + HostAccessQualifierReadINTEL = 1, + HostAccessQualifierWriteINTEL = 2, + HostAccessQualifierReadWriteINTEL = 3, + HostAccessQualifierMax = 0x7fffffff, +}; + +enum LoadCacheControl { + LoadCacheControlUncachedINTEL = 0, + LoadCacheControlCachedINTEL = 1, + LoadCacheControlStreamingINTEL = 2, + LoadCacheControlInvalidateAfterReadINTEL = 3, + LoadCacheControlConstCachedINTEL = 4, + LoadCacheControlMax = 0x7fffffff, +}; + +enum StoreCacheControl { + StoreCacheControlUncachedINTEL = 0, + StoreCacheControlWriteThroughINTEL = 1, + StoreCacheControlWriteBackINTEL = 2, + StoreCacheControlStreamingINTEL = 3, + StoreCacheControlMax = 0x7fffffff, +}; + +enum NamedMaximumNumberOfRegisters { + NamedMaximumNumberOfRegistersAutoINTEL = 0, + NamedMaximumNumberOfRegistersMax = 0x7fffffff, +}; + +enum RawAccessChainOperandsShift { + RawAccessChainOperandsRobustnessPerComponentNVShift = 0, + RawAccessChainOperandsRobustnessPerElementNVShift = 1, + RawAccessChainOperandsMax = 0x7fffffff, +}; + +enum RawAccessChainOperandsMask : unsigned { + RawAccessChainOperandsMaskNone = 0, + RawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + RawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +}; + +enum FPEncoding { + FPEncodingBFloat16KHR = 0, + FPEncodingMax = 0x7fffffff, +}; + +enum CooperativeVectorMatrixLayout { + CooperativeVectorMatrixLayoutRowMajorNV = 0, + CooperativeVectorMatrixLayoutColumnMajorNV = 1, + CooperativeVectorMatrixLayoutInferencingOptimalNV = 2, + CooperativeVectorMatrixLayoutTrainingOptimalNV = 3, + CooperativeVectorMatrixLayoutMax = 0x7fffffff, +}; + +enum ComponentType { + ComponentTypeFloat16NV = 0, + ComponentTypeFloat32NV = 1, + ComponentTypeFloat64NV = 2, + ComponentTypeSignedInt8NV = 3, + ComponentTypeSignedInt16NV = 4, + ComponentTypeSignedInt32NV = 5, + ComponentTypeSignedInt64NV = 6, + ComponentTypeUnsignedInt8NV = 7, + ComponentTypeUnsignedInt16NV = 8, + ComponentTypeUnsignedInt32NV = 9, + ComponentTypeUnsignedInt64NV = 10, + ComponentTypeSignedInt8PackedNV = 1000491000, + ComponentTypeUnsignedInt8PackedNV = 1000491001, + ComponentTypeFloatE4M3NV = 1000491002, + ComponentTypeFloatE5M2NV = 1000491003, + ComponentTypeMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetIntersectionClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpSubgroupBlockPrefetchINTEL = 6221, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case OpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case OpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case OpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case OpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case OpSDot: *hasResult = true; *hasResultType = true; break; + case OpUDot: *hasResult = true; *hasResultType = true; break; + case OpSUDot: *hasResult = true; *hasResultType = true; break; + case OpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case OpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case OpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case OpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case OpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case OpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case OpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case OpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case OpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeVectorNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case OpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case OpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case OpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case OpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case OpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case OpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case OpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case OpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case OpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case OpExpectKHR: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case OpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case OpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case OpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case OpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedLogINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedExpINTEL: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case OpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case OpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case OpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case OpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case OpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case OpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case OpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + } +} +inline const char* SourceLanguageToString(SourceLanguage value) { + switch (value) { + case SourceLanguageUnknown: return "Unknown"; + case SourceLanguageESSL: return "ESSL"; + case SourceLanguageGLSL: return "GLSL"; + case SourceLanguageOpenCL_C: return "OpenCL_C"; + case SourceLanguageOpenCL_CPP: return "OpenCL_CPP"; + case SourceLanguageHLSL: return "HLSL"; + case SourceLanguageCPP_for_OpenCL: return "CPP_for_OpenCL"; + case SourceLanguageSYCL: return "SYCL"; + case SourceLanguageHERO_C: return "HERO_C"; + case SourceLanguageNZSL: return "NZSL"; + case SourceLanguageWGSL: return "WGSL"; + case SourceLanguageSlang: return "Slang"; + case SourceLanguageZig: return "Zig"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModelToString(ExecutionModel value) { + switch (value) { + case ExecutionModelVertex: return "Vertex"; + case ExecutionModelTessellationControl: return "TessellationControl"; + case ExecutionModelTessellationEvaluation: return "TessellationEvaluation"; + case ExecutionModelGeometry: return "Geometry"; + case ExecutionModelFragment: return "Fragment"; + case ExecutionModelGLCompute: return "GLCompute"; + case ExecutionModelKernel: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + case ExecutionModelTaskEXT: return "TaskEXT"; + case ExecutionModelMeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* AddressingModelToString(AddressingModel value) { + switch (value) { + case AddressingModelLogical: return "Logical"; + case AddressingModelPhysical32: return "Physical32"; + case AddressingModelPhysical64: return "Physical64"; + case AddressingModelPhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* MemoryModelToString(MemoryModel value) { + switch (value) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModeToString(ExecutionMode value) { + switch (value) { + case ExecutionModeInvocations: return "Invocations"; + case ExecutionModeSpacingEqual: return "SpacingEqual"; + case ExecutionModeSpacingFractionalEven: return "SpacingFractionalEven"; + case ExecutionModeSpacingFractionalOdd: return "SpacingFractionalOdd"; + case ExecutionModeVertexOrderCw: return "VertexOrderCw"; + case ExecutionModeVertexOrderCcw: return "VertexOrderCcw"; + case ExecutionModePixelCenterInteger: return "PixelCenterInteger"; + case ExecutionModeOriginUpperLeft: return "OriginUpperLeft"; + case ExecutionModeOriginLowerLeft: return "OriginLowerLeft"; + case ExecutionModeEarlyFragmentTests: return "EarlyFragmentTests"; + case ExecutionModePointMode: return "PointMode"; + case ExecutionModeXfb: return "Xfb"; + case ExecutionModeDepthReplacing: return "DepthReplacing"; + case ExecutionModeDepthGreater: return "DepthGreater"; + case ExecutionModeDepthLess: return "DepthLess"; + case ExecutionModeDepthUnchanged: return "DepthUnchanged"; + case ExecutionModeLocalSize: return "LocalSize"; + case ExecutionModeLocalSizeHint: return "LocalSizeHint"; + case ExecutionModeInputPoints: return "InputPoints"; + case ExecutionModeInputLines: return "InputLines"; + case ExecutionModeInputLinesAdjacency: return "InputLinesAdjacency"; + case ExecutionModeTriangles: return "Triangles"; + case ExecutionModeInputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case ExecutionModeQuads: return "Quads"; + case ExecutionModeIsolines: return "Isolines"; + case ExecutionModeOutputVertices: return "OutputVertices"; + case ExecutionModeOutputPoints: return "OutputPoints"; + case ExecutionModeOutputLineStrip: return "OutputLineStrip"; + case ExecutionModeOutputTriangleStrip: return "OutputTriangleStrip"; + case ExecutionModeVecTypeHint: return "VecTypeHint"; + case ExecutionModeContractionOff: return "ContractionOff"; + case ExecutionModeInitializer: return "Initializer"; + case ExecutionModeFinalizer: return "Finalizer"; + case ExecutionModeSubgroupSize: return "SubgroupSize"; + case ExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionModeLocalSizeId: return "LocalSizeId"; + case ExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + case ExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case ExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case ExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case ExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case ExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case ExecutionModeDenormPreserve: return "DenormPreserve"; + case ExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case ExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case ExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case ExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case ExecutionModeCoalescingAMDX: return "CoalescingAMDX"; + case ExecutionModeIsApiEntryAMDX: return "IsApiEntryAMDX"; + case ExecutionModeMaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case ExecutionModeStaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case ExecutionModeShaderIndexAMDX: return "ShaderIndexAMDX"; + case ExecutionModeMaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case ExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case ExecutionModeStencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case ExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case ExecutionModeStencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case ExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case ExecutionModeStencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case ExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + case ExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case ExecutionModeSharesInputWithAMDX: return "SharesInputWithAMDX"; + case ExecutionModeOutputLinesEXT: return "OutputLinesEXT"; + case ExecutionModeOutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case ExecutionModeDerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case ExecutionModeDerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case ExecutionModeOutputTrianglesEXT: return "OutputTrianglesEXT"; + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case ExecutionModeSharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case ExecutionModeRoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case ExecutionModeRoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case ExecutionModeFloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case ExecutionModeFloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case ExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case ExecutionModeSchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case ExecutionModeMaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case ExecutionModeFPFastMathDefault: return "FPFastMathDefault"; + case ExecutionModeStreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case ExecutionModeRegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case ExecutionModeNamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case ExecutionModeMaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case ExecutionModeMaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case ExecutionModeNamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* StorageClassToString(StorageClass value) { + switch (value) { + case StorageClassUniformConstant: return "UniformConstant"; + case StorageClassInput: return "Input"; + case StorageClassUniform: return "Uniform"; + case StorageClassOutput: return "Output"; + case StorageClassWorkgroup: return "Workgroup"; + case StorageClassCrossWorkgroup: return "CrossWorkgroup"; + case StorageClassPrivate: return "Private"; + case StorageClassFunction: return "Function"; + case StorageClassGeneric: return "Generic"; + case StorageClassPushConstant: return "PushConstant"; + case StorageClassAtomicCounter: return "AtomicCounter"; + case StorageClassImage: return "Image"; + case StorageClassStorageBuffer: return "StorageBuffer"; + case StorageClassTileImageEXT: return "TileImageEXT"; + case StorageClassNodePayloadAMDX: return "NodePayloadAMDX"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassPhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case StorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case StorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case StorageClassCodeSectionINTEL: return "CodeSectionINTEL"; + case StorageClassDeviceOnlyINTEL: return "DeviceOnlyINTEL"; + case StorageClassHostOnlyINTEL: return "HostOnlyINTEL"; + default: return "Unknown"; + } +} + +inline const char* DimToString(Dim value) { + switch (value) { + case Dim1D: return "1D"; + case Dim2D: return "2D"; + case Dim3D: return "3D"; + case DimCube: return "Cube"; + case DimRect: return "Rect"; + case DimBuffer: return "Buffer"; + case DimSubpassData: return "SubpassData"; + case DimTileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SamplerAddressingModeToString(SamplerAddressingMode value) { + switch (value) { + case SamplerAddressingModeNone: return "None"; + case SamplerAddressingModeClampToEdge: return "ClampToEdge"; + case SamplerAddressingModeClamp: return "Clamp"; + case SamplerAddressingModeRepeat: return "Repeat"; + case SamplerAddressingModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SamplerFilterModeToString(SamplerFilterMode value) { + switch (value) { + case SamplerFilterModeNearest: return "Nearest"; + case SamplerFilterModeLinear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* ImageFormatToString(ImageFormat value) { + switch (value) { + case ImageFormatUnknown: return "Unknown"; + case ImageFormatRgba32f: return "Rgba32f"; + case ImageFormatRgba16f: return "Rgba16f"; + case ImageFormatR32f: return "R32f"; + case ImageFormatRgba8: return "Rgba8"; + case ImageFormatRgba8Snorm: return "Rgba8Snorm"; + case ImageFormatRg32f: return "Rg32f"; + case ImageFormatRg16f: return "Rg16f"; + case ImageFormatR11fG11fB10f: return "R11fG11fB10f"; + case ImageFormatR16f: return "R16f"; + case ImageFormatRgba16: return "Rgba16"; + case ImageFormatRgb10A2: return "Rgb10A2"; + case ImageFormatRg16: return "Rg16"; + case ImageFormatRg8: return "Rg8"; + case ImageFormatR16: return "R16"; + case ImageFormatR8: return "R8"; + case ImageFormatRgba16Snorm: return "Rgba16Snorm"; + case ImageFormatRg16Snorm: return "Rg16Snorm"; + case ImageFormatRg8Snorm: return "Rg8Snorm"; + case ImageFormatR16Snorm: return "R16Snorm"; + case ImageFormatR8Snorm: return "R8Snorm"; + case ImageFormatRgba32i: return "Rgba32i"; + case ImageFormatRgba16i: return "Rgba16i"; + case ImageFormatRgba8i: return "Rgba8i"; + case ImageFormatR32i: return "R32i"; + case ImageFormatRg32i: return "Rg32i"; + case ImageFormatRg16i: return "Rg16i"; + case ImageFormatRg8i: return "Rg8i"; + case ImageFormatR16i: return "R16i"; + case ImageFormatR8i: return "R8i"; + case ImageFormatRgba32ui: return "Rgba32ui"; + case ImageFormatRgba16ui: return "Rgba16ui"; + case ImageFormatRgba8ui: return "Rgba8ui"; + case ImageFormatR32ui: return "R32ui"; + case ImageFormatRgb10a2ui: return "Rgb10a2ui"; + case ImageFormatRg32ui: return "Rg32ui"; + case ImageFormatRg16ui: return "Rg16ui"; + case ImageFormatRg8ui: return "Rg8ui"; + case ImageFormatR16ui: return "R16ui"; + case ImageFormatR8ui: return "R8ui"; + case ImageFormatR64ui: return "R64ui"; + case ImageFormatR64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelOrderToString(ImageChannelOrder value) { + switch (value) { + case ImageChannelOrderR: return "R"; + case ImageChannelOrderA: return "A"; + case ImageChannelOrderRG: return "RG"; + case ImageChannelOrderRA: return "RA"; + case ImageChannelOrderRGB: return "RGB"; + case ImageChannelOrderRGBA: return "RGBA"; + case ImageChannelOrderBGRA: return "BGRA"; + case ImageChannelOrderARGB: return "ARGB"; + case ImageChannelOrderIntensity: return "Intensity"; + case ImageChannelOrderLuminance: return "Luminance"; + case ImageChannelOrderRx: return "Rx"; + case ImageChannelOrderRGx: return "RGx"; + case ImageChannelOrderRGBx: return "RGBx"; + case ImageChannelOrderDepth: return "Depth"; + case ImageChannelOrderDepthStencil: return "DepthStencil"; + case ImageChannelOrdersRGB: return "sRGB"; + case ImageChannelOrdersRGBx: return "sRGBx"; + case ImageChannelOrdersRGBA: return "sRGBA"; + case ImageChannelOrdersBGRA: return "sBGRA"; + case ImageChannelOrderABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelDataTypeToString(ImageChannelDataType value) { + switch (value) { + case ImageChannelDataTypeSnormInt8: return "SnormInt8"; + case ImageChannelDataTypeSnormInt16: return "SnormInt16"; + case ImageChannelDataTypeUnormInt8: return "UnormInt8"; + case ImageChannelDataTypeUnormInt16: return "UnormInt16"; + case ImageChannelDataTypeUnormShort565: return "UnormShort565"; + case ImageChannelDataTypeUnormShort555: return "UnormShort555"; + case ImageChannelDataTypeUnormInt101010: return "UnormInt101010"; + case ImageChannelDataTypeSignedInt8: return "SignedInt8"; + case ImageChannelDataTypeSignedInt16: return "SignedInt16"; + case ImageChannelDataTypeSignedInt32: return "SignedInt32"; + case ImageChannelDataTypeUnsignedInt8: return "UnsignedInt8"; + case ImageChannelDataTypeUnsignedInt16: return "UnsignedInt16"; + case ImageChannelDataTypeUnsignedInt32: return "UnsignedInt32"; + case ImageChannelDataTypeHalfFloat: return "HalfFloat"; + case ImageChannelDataTypeFloat: return "Float"; + case ImageChannelDataTypeUnormInt24: return "UnormInt24"; + case ImageChannelDataTypeUnormInt101010_2: return "UnormInt101010_2"; + case ImageChannelDataTypeUnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case ImageChannelDataTypeUnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case ImageChannelDataTypeUnormInt2_101010EXT: return "UnormInt2_101010EXT"; + default: return "Unknown"; + } +} + +inline const char* FPRoundingModeToString(FPRoundingMode value) { + switch (value) { + case FPRoundingModeRTE: return "RTE"; + case FPRoundingModeRTZ: return "RTZ"; + case FPRoundingModeRTP: return "RTP"; + case FPRoundingModeRTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* LinkageTypeToString(LinkageType value) { + switch (value) { + case LinkageTypeExport: return "Export"; + case LinkageTypeImport: return "Import"; + case LinkageTypeLinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* AccessQualifierToString(AccessQualifier value) { + switch (value) { + case AccessQualifierReadOnly: return "ReadOnly"; + case AccessQualifierWriteOnly: return "WriteOnly"; + case AccessQualifierReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* FunctionParameterAttributeToString(FunctionParameterAttribute value) { + switch (value) { + case FunctionParameterAttributeZext: return "Zext"; + case FunctionParameterAttributeSext: return "Sext"; + case FunctionParameterAttributeByVal: return "ByVal"; + case FunctionParameterAttributeSret: return "Sret"; + case FunctionParameterAttributeNoAlias: return "NoAlias"; + case FunctionParameterAttributeNoCapture: return "NoCapture"; + case FunctionParameterAttributeNoWrite: return "NoWrite"; + case FunctionParameterAttributeNoReadWrite: return "NoReadWrite"; + case FunctionParameterAttributeRuntimeAlignedINTEL: return "RuntimeAlignedINTEL"; + default: return "Unknown"; + } +} + +inline const char* DecorationToString(Decoration value) { + switch (value) { + case DecorationRelaxedPrecision: return "RelaxedPrecision"; + case DecorationSpecId: return "SpecId"; + case DecorationBlock: return "Block"; + case DecorationBufferBlock: return "BufferBlock"; + case DecorationRowMajor: return "RowMajor"; + case DecorationColMajor: return "ColMajor"; + case DecorationArrayStride: return "ArrayStride"; + case DecorationMatrixStride: return "MatrixStride"; + case DecorationGLSLShared: return "GLSLShared"; + case DecorationGLSLPacked: return "GLSLPacked"; + case DecorationCPacked: return "CPacked"; + case DecorationBuiltIn: return "BuiltIn"; + case DecorationNoPerspective: return "NoPerspective"; + case DecorationFlat: return "Flat"; + case DecorationPatch: return "Patch"; + case DecorationCentroid: return "Centroid"; + case DecorationSample: return "Sample"; + case DecorationInvariant: return "Invariant"; + case DecorationRestrict: return "Restrict"; + case DecorationAliased: return "Aliased"; + case DecorationVolatile: return "Volatile"; + case DecorationConstant: return "Constant"; + case DecorationCoherent: return "Coherent"; + case DecorationNonWritable: return "NonWritable"; + case DecorationNonReadable: return "NonReadable"; + case DecorationUniform: return "Uniform"; + case DecorationUniformId: return "UniformId"; + case DecorationSaturatedConversion: return "SaturatedConversion"; + case DecorationStream: return "Stream"; + case DecorationLocation: return "Location"; + case DecorationComponent: return "Component"; + case DecorationIndex: return "Index"; + case DecorationBinding: return "Binding"; + case DecorationDescriptorSet: return "DescriptorSet"; + case DecorationOffset: return "Offset"; + case DecorationXfbBuffer: return "XfbBuffer"; + case DecorationXfbStride: return "XfbStride"; + case DecorationFuncParamAttr: return "FuncParamAttr"; + case DecorationFPRoundingMode: return "FPRoundingMode"; + case DecorationFPFastMathMode: return "FPFastMathMode"; + case DecorationLinkageAttributes: return "LinkageAttributes"; + case DecorationNoContraction: return "NoContraction"; + case DecorationInputAttachmentIndex: return "InputAttachmentIndex"; + case DecorationAlignment: return "Alignment"; + case DecorationMaxByteOffset: return "MaxByteOffset"; + case DecorationAlignmentId: return "AlignmentId"; + case DecorationMaxByteOffsetId: return "MaxByteOffsetId"; + case DecorationNoSignedWrap: return "NoSignedWrap"; + case DecorationNoUnsignedWrap: return "NoUnsignedWrap"; + case DecorationWeightTextureQCOM: return "WeightTextureQCOM"; + case DecorationBlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case DecorationBlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationNodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case DecorationNodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case DecorationTrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case DecorationPayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case DecorationPayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case DecorationPayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case DecorationPayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case DecorationPayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveEXT: return "PerPrimitiveEXT"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + case DecorationPerVertexKHR: return "PerVertexKHR"; + case DecorationNonUniform: return "NonUniform"; + case DecorationRestrictPointer: return "RestrictPointer"; + case DecorationAliasedPointer: return "AliasedPointer"; + case DecorationHitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case DecorationBindlessSamplerNV: return "BindlessSamplerNV"; + case DecorationBindlessImageNV: return "BindlessImageNV"; + case DecorationBoundSamplerNV: return "BoundSamplerNV"; + case DecorationBoundImageNV: return "BoundImageNV"; + case DecorationSIMTCallINTEL: return "SIMTCallINTEL"; + case DecorationReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case DecorationClobberINTEL: return "ClobberINTEL"; + case DecorationSideEffectsINTEL: return "SideEffectsINTEL"; + case DecorationVectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case DecorationFuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case DecorationVectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case DecorationStackCallINTEL: return "StackCallINTEL"; + case DecorationGlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case DecorationCounterBuffer: return "CounterBuffer"; + case DecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case DecorationUserTypeGOOGLE: return "UserTypeGOOGLE"; + case DecorationFunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case DecorationFunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case DecorationRegisterINTEL: return "RegisterINTEL"; + case DecorationMemoryINTEL: return "MemoryINTEL"; + case DecorationNumbanksINTEL: return "NumbanksINTEL"; + case DecorationBankwidthINTEL: return "BankwidthINTEL"; + case DecorationMaxPrivateCopiesINTEL: return "MaxPrivateCopiesINTEL"; + case DecorationSinglepumpINTEL: return "SinglepumpINTEL"; + case DecorationDoublepumpINTEL: return "DoublepumpINTEL"; + case DecorationMaxReplicatesINTEL: return "MaxReplicatesINTEL"; + case DecorationSimpleDualPortINTEL: return "SimpleDualPortINTEL"; + case DecorationMergeINTEL: return "MergeINTEL"; + case DecorationBankBitsINTEL: return "BankBitsINTEL"; + case DecorationForcePow2DepthINTEL: return "ForcePow2DepthINTEL"; + case DecorationStridesizeINTEL: return "StridesizeINTEL"; + case DecorationWordsizeINTEL: return "WordsizeINTEL"; + case DecorationTrueDualPortINTEL: return "TrueDualPortINTEL"; + case DecorationBurstCoalesceINTEL: return "BurstCoalesceINTEL"; + case DecorationCacheSizeINTEL: return "CacheSizeINTEL"; + case DecorationDontStaticallyCoalesceINTEL: return "DontStaticallyCoalesceINTEL"; + case DecorationPrefetchINTEL: return "PrefetchINTEL"; + case DecorationStallEnableINTEL: return "StallEnableINTEL"; + case DecorationFuseLoopsInFunctionINTEL: return "FuseLoopsInFunctionINTEL"; + case DecorationMathOpDSPModeINTEL: return "MathOpDSPModeINTEL"; + case DecorationAliasScopeINTEL: return "AliasScopeINTEL"; + case DecorationNoAliasINTEL: return "NoAliasINTEL"; + case DecorationInitiationIntervalINTEL: return "InitiationIntervalINTEL"; + case DecorationMaxConcurrencyINTEL: return "MaxConcurrencyINTEL"; + case DecorationPipelineEnableINTEL: return "PipelineEnableINTEL"; + case DecorationBufferLocationINTEL: return "BufferLocationINTEL"; + case DecorationIOPipeStorageINTEL: return "IOPipeStorageINTEL"; + case DecorationFunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case DecorationSingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case DecorationVectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case DecorationMediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case DecorationStallFreeINTEL: return "StallFreeINTEL"; + case DecorationFPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case DecorationLatencyControlLabelINTEL: return "LatencyControlLabelINTEL"; + case DecorationLatencyControlConstraintINTEL: return "LatencyControlConstraintINTEL"; + case DecorationConduitKernelArgumentINTEL: return "ConduitKernelArgumentINTEL"; + case DecorationRegisterMapKernelArgumentINTEL: return "RegisterMapKernelArgumentINTEL"; + case DecorationMMHostInterfaceAddressWidthINTEL: return "MMHostInterfaceAddressWidthINTEL"; + case DecorationMMHostInterfaceDataWidthINTEL: return "MMHostInterfaceDataWidthINTEL"; + case DecorationMMHostInterfaceLatencyINTEL: return "MMHostInterfaceLatencyINTEL"; + case DecorationMMHostInterfaceReadWriteModeINTEL: return "MMHostInterfaceReadWriteModeINTEL"; + case DecorationMMHostInterfaceMaxBurstINTEL: return "MMHostInterfaceMaxBurstINTEL"; + case DecorationMMHostInterfaceWaitRequestINTEL: return "MMHostInterfaceWaitRequestINTEL"; + case DecorationStableKernelArgumentINTEL: return "StableKernelArgumentINTEL"; + case DecorationHostAccessINTEL: return "HostAccessINTEL"; + case DecorationInitModeINTEL: return "InitModeINTEL"; + case DecorationImplementInRegisterMapINTEL: return "ImplementInRegisterMapINTEL"; + case DecorationCacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case DecorationCacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* BuiltInToString(BuiltIn value) { + switch (value) { + case BuiltInPosition: return "Position"; + case BuiltInPointSize: return "PointSize"; + case BuiltInClipDistance: return "ClipDistance"; + case BuiltInCullDistance: return "CullDistance"; + case BuiltInVertexId: return "VertexId"; + case BuiltInInstanceId: return "InstanceId"; + case BuiltInPrimitiveId: return "PrimitiveId"; + case BuiltInInvocationId: return "InvocationId"; + case BuiltInLayer: return "Layer"; + case BuiltInViewportIndex: return "ViewportIndex"; + case BuiltInTessLevelOuter: return "TessLevelOuter"; + case BuiltInTessLevelInner: return "TessLevelInner"; + case BuiltInTessCoord: return "TessCoord"; + case BuiltInPatchVertices: return "PatchVertices"; + case BuiltInFragCoord: return "FragCoord"; + case BuiltInPointCoord: return "PointCoord"; + case BuiltInFrontFacing: return "FrontFacing"; + case BuiltInSampleId: return "SampleId"; + case BuiltInSamplePosition: return "SamplePosition"; + case BuiltInSampleMask: return "SampleMask"; + case BuiltInFragDepth: return "FragDepth"; + case BuiltInHelperInvocation: return "HelperInvocation"; + case BuiltInNumWorkgroups: return "NumWorkgroups"; + case BuiltInWorkgroupSize: return "WorkgroupSize"; + case BuiltInWorkgroupId: return "WorkgroupId"; + case BuiltInLocalInvocationId: return "LocalInvocationId"; + case BuiltInGlobalInvocationId: return "GlobalInvocationId"; + case BuiltInLocalInvocationIndex: return "LocalInvocationIndex"; + case BuiltInWorkDim: return "WorkDim"; + case BuiltInGlobalSize: return "GlobalSize"; + case BuiltInEnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case BuiltInGlobalOffset: return "GlobalOffset"; + case BuiltInGlobalLinearId: return "GlobalLinearId"; + case BuiltInSubgroupSize: return "SubgroupSize"; + case BuiltInSubgroupMaxSize: return "SubgroupMaxSize"; + case BuiltInNumSubgroups: return "NumSubgroups"; + case BuiltInNumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case BuiltInSubgroupId: return "SubgroupId"; + case BuiltInSubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case BuiltInVertexIndex: return "VertexIndex"; + case BuiltInInstanceIndex: return "InstanceIndex"; + case BuiltInCoreIDARM: return "CoreIDARM"; + case BuiltInCoreCountARM: return "CoreCountARM"; + case BuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case BuiltInWarpIDARM: return "WarpIDARM"; + case BuiltInWarpMaxIDARM: return "WarpMaxIDARM"; + case BuiltInSubgroupEqMask: return "SubgroupEqMask"; + case BuiltInSubgroupGeMask: return "SubgroupGeMask"; + case BuiltInSubgroupGtMask: return "SubgroupGtMask"; + case BuiltInSubgroupLeMask: return "SubgroupLeMask"; + case BuiltInSubgroupLtMask: return "SubgroupLtMask"; + case BuiltInBaseVertex: return "BaseVertex"; + case BuiltInBaseInstance: return "BaseInstance"; + case BuiltInDrawIndex: return "DrawIndex"; + case BuiltInPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case BuiltInDeviceIndex: return "DeviceIndex"; + case BuiltInViewIndex: return "ViewIndex"; + case BuiltInShadingRateKHR: return "ShadingRateKHR"; + case BuiltInBaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case BuiltInBaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case BuiltInBaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case BuiltInBaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case BuiltInBaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case BuiltInBaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case BuiltInBaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case BuiltInFragStencilRefEXT: return "FragStencilRefEXT"; + case BuiltInRemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case BuiltInShaderIndexAMDX: return "ShaderIndexAMDX"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInFullyCoveredEXT: return "FullyCoveredEXT"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInBaryCoordKHR: return "BaryCoordKHR"; + case BuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + case BuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case BuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case BuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case BuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTNV: return "HitTNV"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case BuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case BuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case BuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + case BuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case BuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case BuiltInCullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* ScopeToString(Scope value) { + switch (value) { + case ScopeCrossDevice: return "CrossDevice"; + case ScopeDevice: return "Device"; + case ScopeWorkgroup: return "Workgroup"; + case ScopeSubgroup: return "Subgroup"; + case ScopeInvocation: return "Invocation"; + case ScopeQueueFamily: return "QueueFamily"; + case ScopeShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* GroupOperationToString(GroupOperation value) { + switch (value) { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + default: return "Unknown"; + } +} + +inline const char* KernelEnqueueFlagsToString(KernelEnqueueFlags value) { + switch (value) { + case KernelEnqueueFlagsNoWait: return "NoWait"; + case KernelEnqueueFlagsWaitKernel: return "WaitKernel"; + case KernelEnqueueFlagsWaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* CapabilityToString(Capability value) { + switch (value) { + case CapabilityMatrix: return "Matrix"; + case CapabilityShader: return "Shader"; + case CapabilityGeometry: return "Geometry"; + case CapabilityTessellation: return "Tessellation"; + case CapabilityAddresses: return "Addresses"; + case CapabilityLinkage: return "Linkage"; + case CapabilityKernel: return "Kernel"; + case CapabilityVector16: return "Vector16"; + case CapabilityFloat16Buffer: return "Float16Buffer"; + case CapabilityFloat16: return "Float16"; + case CapabilityFloat64: return "Float64"; + case CapabilityInt64: return "Int64"; + case CapabilityInt64Atomics: return "Int64Atomics"; + case CapabilityImageBasic: return "ImageBasic"; + case CapabilityImageReadWrite: return "ImageReadWrite"; + case CapabilityImageMipmap: return "ImageMipmap"; + case CapabilityPipes: return "Pipes"; + case CapabilityGroups: return "Groups"; + case CapabilityDeviceEnqueue: return "DeviceEnqueue"; + case CapabilityLiteralSampler: return "LiteralSampler"; + case CapabilityAtomicStorage: return "AtomicStorage"; + case CapabilityInt16: return "Int16"; + case CapabilityTessellationPointSize: return "TessellationPointSize"; + case CapabilityGeometryPointSize: return "GeometryPointSize"; + case CapabilityImageGatherExtended: return "ImageGatherExtended"; + case CapabilityStorageImageMultisample: return "StorageImageMultisample"; + case CapabilityUniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case CapabilitySampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case CapabilityStorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case CapabilityStorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case CapabilityClipDistance: return "ClipDistance"; + case CapabilityCullDistance: return "CullDistance"; + case CapabilityImageCubeArray: return "ImageCubeArray"; + case CapabilitySampleRateShading: return "SampleRateShading"; + case CapabilityImageRect: return "ImageRect"; + case CapabilitySampledRect: return "SampledRect"; + case CapabilityGenericPointer: return "GenericPointer"; + case CapabilityInt8: return "Int8"; + case CapabilityInputAttachment: return "InputAttachment"; + case CapabilitySparseResidency: return "SparseResidency"; + case CapabilityMinLod: return "MinLod"; + case CapabilitySampled1D: return "Sampled1D"; + case CapabilityImage1D: return "Image1D"; + case CapabilitySampledCubeArray: return "SampledCubeArray"; + case CapabilitySampledBuffer: return "SampledBuffer"; + case CapabilityImageBuffer: return "ImageBuffer"; + case CapabilityImageMSArray: return "ImageMSArray"; + case CapabilityStorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case CapabilityImageQuery: return "ImageQuery"; + case CapabilityDerivativeControl: return "DerivativeControl"; + case CapabilityInterpolationFunction: return "InterpolationFunction"; + case CapabilityTransformFeedback: return "TransformFeedback"; + case CapabilityGeometryStreams: return "GeometryStreams"; + case CapabilityStorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case CapabilityStorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case CapabilityMultiViewport: return "MultiViewport"; + case CapabilitySubgroupDispatch: return "SubgroupDispatch"; + case CapabilityNamedBarrier: return "NamedBarrier"; + case CapabilityPipeStorage: return "PipeStorage"; + case CapabilityGroupNonUniform: return "GroupNonUniform"; + case CapabilityGroupNonUniformVote: return "GroupNonUniformVote"; + case CapabilityGroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case CapabilityGroupNonUniformBallot: return "GroupNonUniformBallot"; + case CapabilityGroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case CapabilityGroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case CapabilityGroupNonUniformClustered: return "GroupNonUniformClustered"; + case CapabilityGroupNonUniformQuad: return "GroupNonUniformQuad"; + case CapabilityShaderLayer: return "ShaderLayer"; + case CapabilityShaderViewportIndex: return "ShaderViewportIndex"; + case CapabilityUniformDecoration: return "UniformDecoration"; + case CapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + case CapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case CapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case CapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case CapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case CapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilityWorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case CapabilityStorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + case CapabilityVariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case CapabilityVariablePointers: return "VariablePointers"; + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + case CapabilityDenormPreserve: return "DenormPreserve"; + case CapabilityDenormFlushToZero: return "DenormFlushToZero"; + case CapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case CapabilityRoundingModeRTE: return "RoundingModeRTE"; + case CapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + case CapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case CapabilityRayQueryKHR: return "RayQueryKHR"; + case CapabilityUntypedPointersKHR: return "UntypedPointersKHR"; + case CapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case CapabilityRayTracingKHR: return "RayTracingKHR"; + case CapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case CapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case CapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case CapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityStencilExportEXT: return "StencilExportEXT"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case CapabilityInt64ImageEXT: return "Int64ImageEXT"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + case CapabilityShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case CapabilityQuadControlKHR: return "QuadControlKHR"; + case CapabilityBFloat16TypeKHR: return "BFloat16TypeKHR"; + case CapabilityBFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case CapabilityBFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilityMeshShadingEXT: return "MeshShadingEXT"; + case CapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case CapabilityComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case CapabilityShaderNonUniform: return "ShaderNonUniform"; + case CapabilityRuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case CapabilityInputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case CapabilityUniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case CapabilityStorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case CapabilityUniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case CapabilitySampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case CapabilityStorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case CapabilityStorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case CapabilityInputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case CapabilityUniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case CapabilityStorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case CapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case CapabilityVulkanMemoryModel: return "VulkanMemoryModel"; + case CapabilityVulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case CapabilityPhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case CapabilityComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityFragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case CapabilityFragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case CapabilityDemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case CapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case CapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case CapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case CapabilityBindlessTextureNV: return "BindlessTextureNV"; + case CapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case CapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case CapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case CapabilityRayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case CapabilityRawAccessChainsNV: return "RawAccessChainsNV"; + case CapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case CapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case CapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case CapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case CapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case CapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case CapabilityTensorAddressingNV: return "TensorAddressingNV"; + case CapabilitySubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case CapabilitySubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case CapabilitySubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case CapabilitySubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case CapabilityRoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case CapabilityFloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case CapabilityIntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case CapabilityFunctionPointersINTEL: return "FunctionPointersINTEL"; + case CapabilityIndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case CapabilityAsmINTEL: return "AsmINTEL"; + case CapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case CapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case CapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case CapabilityVectorComputeINTEL: return "VectorComputeINTEL"; + case CapabilityVectorAnyINTEL: return "VectorAnyINTEL"; + case CapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + case CapabilitySubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case CapabilitySubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case CapabilitySubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case CapabilityVariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case CapabilityFunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case CapabilityFPGAMemoryAttributesINTEL: return "FPGAMemoryAttributesINTEL"; + case CapabilityFPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case CapabilityArbitraryPrecisionIntegersINTEL: return "ArbitraryPrecisionIntegersINTEL"; + case CapabilityArbitraryPrecisionFloatingPointINTEL: return "ArbitraryPrecisionFloatingPointINTEL"; + case CapabilityUnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case CapabilityFPGALoopControlsINTEL: return "FPGALoopControlsINTEL"; + case CapabilityKernelAttributesINTEL: return "KernelAttributesINTEL"; + case CapabilityFPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case CapabilityFPGAMemoryAccessesINTEL: return "FPGAMemoryAccessesINTEL"; + case CapabilityFPGAClusterAttributesINTEL: return "FPGAClusterAttributesINTEL"; + case CapabilityLoopFuseINTEL: return "LoopFuseINTEL"; + case CapabilityFPGADSPControlINTEL: return "FPGADSPControlINTEL"; + case CapabilityMemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case CapabilityFPGAInvocationPipeliningAttributesINTEL: return "FPGAInvocationPipeliningAttributesINTEL"; + case CapabilityFPGABufferLocationINTEL: return "FPGABufferLocationINTEL"; + case CapabilityArbitraryPrecisionFixedPointINTEL: return "ArbitraryPrecisionFixedPointINTEL"; + case CapabilityUSMStorageClassesINTEL: return "USMStorageClassesINTEL"; + case CapabilityRuntimeAlignedAttributeINTEL: return "RuntimeAlignedAttributeINTEL"; + case CapabilityIOPipesINTEL: return "IOPipesINTEL"; + case CapabilityBlockingPipesINTEL: return "BlockingPipesINTEL"; + case CapabilityFPGARegINTEL: return "FPGARegINTEL"; + case CapabilityDotProductInputAll: return "DotProductInputAll"; + case CapabilityDotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case CapabilityDotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case CapabilityDotProduct: return "DotProduct"; + case CapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case CapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case CapabilityReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case CapabilityBitInstructions: return "BitInstructions"; + case CapabilityGroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case CapabilityFloatControls2: return "FloatControls2"; + case CapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case CapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case CapabilityLongCompositesINTEL: return "LongCompositesINTEL"; + case CapabilityOptNoneEXT: return "OptNoneEXT"; + case CapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case CapabilityDebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case CapabilityBFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case CapabilitySplitBarrierINTEL: return "SplitBarrierINTEL"; + case CapabilityArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case CapabilityFPGAClusterAttributesV2INTEL: return "FPGAClusterAttributesV2INTEL"; + case CapabilityFPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case CapabilityFPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case CapabilityFPGALatencyControlINTEL: return "FPGALatencyControlINTEL"; + case CapabilityFPGAArgumentInterfacesINTEL: return "FPGAArgumentInterfacesINTEL"; + case CapabilityGlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case CapabilityGlobalVariableFPGADecorationsINTEL: return "GlobalVariableFPGADecorationsINTEL"; + case CapabilitySubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case CapabilityGroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case CapabilityMaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case CapabilityCacheControlsINTEL: return "CacheControlsINTEL"; + case CapabilityRegisterLimitsINTEL: return "RegisterLimitsINTEL"; + default: return "Unknown"; + } +} + +inline const char* RayQueryIntersectionToString(RayQueryIntersection value) { + switch (value) { + case RayQueryIntersectionRayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case RayQueryIntersectionRayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCommittedIntersectionTypeToString(RayQueryCommittedIntersectionType value) { + switch (value) { + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCandidateIntersectionTypeToString(RayQueryCandidateIntersectionType value) { + switch (value) { + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* FPDenormModeToString(FPDenormMode value) { + switch (value) { + case FPDenormModePreserve: return "Preserve"; + case FPDenormModeFlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* FPOperationModeToString(FPOperationMode value) { + switch (value) { + case FPOperationModeIEEE: return "IEEE"; + case FPOperationModeALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* QuantizationModesToString(QuantizationModes value) { + switch (value) { + case QuantizationModesTRN: return "TRN"; + case QuantizationModesTRN_ZERO: return "TRN_ZERO"; + case QuantizationModesRND: return "RND"; + case QuantizationModesRND_ZERO: return "RND_ZERO"; + case QuantizationModesRND_INF: return "RND_INF"; + case QuantizationModesRND_MIN_INF: return "RND_MIN_INF"; + case QuantizationModesRND_CONV: return "RND_CONV"; + case QuantizationModesRND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* OverflowModesToString(OverflowModes value) { + switch (value) { + case OverflowModesWRAP: return "WRAP"; + case OverflowModesSAT: return "SAT"; + case OverflowModesSAT_ZERO: return "SAT_ZERO"; + case OverflowModesSAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* PackedVectorFormatToString(PackedVectorFormat value) { + switch (value) { + case PackedVectorFormatPackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixLayoutToString(CooperativeMatrixLayout value) { + switch (value) { + case CooperativeMatrixLayoutRowMajorKHR: return "RowMajorKHR"; + case CooperativeMatrixLayoutColumnMajorKHR: return "ColumnMajorKHR"; + case CooperativeMatrixLayoutRowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case CooperativeMatrixLayoutColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixUseToString(CooperativeMatrixUse value) { + switch (value) { + case CooperativeMatrixUseMatrixAKHR: return "MatrixAKHR"; + case CooperativeMatrixUseMatrixBKHR: return "MatrixBKHR"; + case CooperativeMatrixUseMatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* TensorClampModeToString(TensorClampMode value) { + switch (value) { + case TensorClampModeUndefined: return "Undefined"; + case TensorClampModeConstant: return "Constant"; + case TensorClampModeClampToEdge: return "ClampToEdge"; + case TensorClampModeRepeat: return "Repeat"; + case TensorClampModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* InitializationModeQualifierToString(InitializationModeQualifier value) { + switch (value) { + case InitializationModeQualifierInitOnDeviceReprogramINTEL: return "InitOnDeviceReprogramINTEL"; + case InitializationModeQualifierInitOnDeviceResetINTEL: return "InitOnDeviceResetINTEL"; + default: return "Unknown"; + } +} + +inline const char* HostAccessQualifierToString(HostAccessQualifier value) { + switch (value) { + case HostAccessQualifierNoneINTEL: return "NoneINTEL"; + case HostAccessQualifierReadINTEL: return "ReadINTEL"; + case HostAccessQualifierWriteINTEL: return "WriteINTEL"; + case HostAccessQualifierReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* LoadCacheControlToString(LoadCacheControl value) { + switch (value) { + case LoadCacheControlUncachedINTEL: return "UncachedINTEL"; + case LoadCacheControlCachedINTEL: return "CachedINTEL"; + case LoadCacheControlStreamingINTEL: return "StreamingINTEL"; + case LoadCacheControlInvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case LoadCacheControlConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* StoreCacheControlToString(StoreCacheControl value) { + switch (value) { + case StoreCacheControlUncachedINTEL: return "UncachedINTEL"; + case StoreCacheControlWriteThroughINTEL: return "WriteThroughINTEL"; + case StoreCacheControlWriteBackINTEL: return "WriteBackINTEL"; + case StoreCacheControlStreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* NamedMaximumNumberOfRegistersToString(NamedMaximumNumberOfRegisters value) { + switch (value) { + case NamedMaximumNumberOfRegistersAutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* FPEncodingToString(FPEncoding value) { + switch (value) { + case FPEncodingBFloat16KHR: return "BFloat16KHR"; + default: return "Unknown"; + } +} + +inline const char* CooperativeVectorMatrixLayoutToString(CooperativeVectorMatrixLayout value) { + switch (value) { + case CooperativeVectorMatrixLayoutRowMajorNV: return "RowMajorNV"; + case CooperativeVectorMatrixLayoutColumnMajorNV: return "ColumnMajorNV"; + case CooperativeVectorMatrixLayoutInferencingOptimalNV: return "InferencingOptimalNV"; + case CooperativeVectorMatrixLayoutTrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* ComponentTypeToString(ComponentType value) { + switch (value) { + case ComponentTypeFloat16NV: return "Float16NV"; + case ComponentTypeFloat32NV: return "Float32NV"; + case ComponentTypeFloat64NV: return "Float64NV"; + case ComponentTypeSignedInt8NV: return "SignedInt8NV"; + case ComponentTypeSignedInt16NV: return "SignedInt16NV"; + case ComponentTypeSignedInt32NV: return "SignedInt32NV"; + case ComponentTypeSignedInt64NV: return "SignedInt64NV"; + case ComponentTypeUnsignedInt8NV: return "UnsignedInt8NV"; + case ComponentTypeUnsignedInt16NV: return "UnsignedInt16NV"; + case ComponentTypeUnsignedInt32NV: return "UnsignedInt32NV"; + case ComponentTypeUnsignedInt64NV: return "UnsignedInt64NV"; + case ComponentTypeSignedInt8PackedNV: return "SignedInt8PackedNV"; + case ComponentTypeUnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case ComponentTypeFloatE4M3NV: return "FloatE4M3NV"; + case ComponentTypeFloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* OpToString(Op value) { + switch (value) { + case OpNop: return "OpNop"; + case OpUndef: return "OpUndef"; + case OpSourceContinued: return "OpSourceContinued"; + case OpSource: return "OpSource"; + case OpSourceExtension: return "OpSourceExtension"; + case OpName: return "OpName"; + case OpMemberName: return "OpMemberName"; + case OpString: return "OpString"; + case OpLine: return "OpLine"; + case OpExtension: return "OpExtension"; + case OpExtInstImport: return "OpExtInstImport"; + case OpExtInst: return "OpExtInst"; + case OpMemoryModel: return "OpMemoryModel"; + case OpEntryPoint: return "OpEntryPoint"; + case OpExecutionMode: return "OpExecutionMode"; + case OpCapability: return "OpCapability"; + case OpTypeVoid: return "OpTypeVoid"; + case OpTypeBool: return "OpTypeBool"; + case OpTypeInt: return "OpTypeInt"; + case OpTypeFloat: return "OpTypeFloat"; + case OpTypeVector: return "OpTypeVector"; + case OpTypeMatrix: return "OpTypeMatrix"; + case OpTypeImage: return "OpTypeImage"; + case OpTypeSampler: return "OpTypeSampler"; + case OpTypeSampledImage: return "OpTypeSampledImage"; + case OpTypeArray: return "OpTypeArray"; + case OpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case OpTypeStruct: return "OpTypeStruct"; + case OpTypeOpaque: return "OpTypeOpaque"; + case OpTypePointer: return "OpTypePointer"; + case OpTypeFunction: return "OpTypeFunction"; + case OpTypeEvent: return "OpTypeEvent"; + case OpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case OpTypeReserveId: return "OpTypeReserveId"; + case OpTypeQueue: return "OpTypeQueue"; + case OpTypePipe: return "OpTypePipe"; + case OpTypeForwardPointer: return "OpTypeForwardPointer"; + case OpConstantTrue: return "OpConstantTrue"; + case OpConstantFalse: return "OpConstantFalse"; + case OpConstant: return "OpConstant"; + case OpConstantComposite: return "OpConstantComposite"; + case OpConstantSampler: return "OpConstantSampler"; + case OpConstantNull: return "OpConstantNull"; + case OpSpecConstantTrue: return "OpSpecConstantTrue"; + case OpSpecConstantFalse: return "OpSpecConstantFalse"; + case OpSpecConstant: return "OpSpecConstant"; + case OpSpecConstantComposite: return "OpSpecConstantComposite"; + case OpSpecConstantOp: return "OpSpecConstantOp"; + case OpFunction: return "OpFunction"; + case OpFunctionParameter: return "OpFunctionParameter"; + case OpFunctionEnd: return "OpFunctionEnd"; + case OpFunctionCall: return "OpFunctionCall"; + case OpVariable: return "OpVariable"; + case OpImageTexelPointer: return "OpImageTexelPointer"; + case OpLoad: return "OpLoad"; + case OpStore: return "OpStore"; + case OpCopyMemory: return "OpCopyMemory"; + case OpCopyMemorySized: return "OpCopyMemorySized"; + case OpAccessChain: return "OpAccessChain"; + case OpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case OpPtrAccessChain: return "OpPtrAccessChain"; + case OpArrayLength: return "OpArrayLength"; + case OpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case OpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case OpDecorate: return "OpDecorate"; + case OpMemberDecorate: return "OpMemberDecorate"; + case OpDecorationGroup: return "OpDecorationGroup"; + case OpGroupDecorate: return "OpGroupDecorate"; + case OpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case OpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case OpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case OpVectorShuffle: return "OpVectorShuffle"; + case OpCompositeConstruct: return "OpCompositeConstruct"; + case OpCompositeExtract: return "OpCompositeExtract"; + case OpCompositeInsert: return "OpCompositeInsert"; + case OpCopyObject: return "OpCopyObject"; + case OpTranspose: return "OpTranspose"; + case OpSampledImage: return "OpSampledImage"; + case OpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case OpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case OpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case OpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case OpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case OpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case OpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case OpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case OpImageFetch: return "OpImageFetch"; + case OpImageGather: return "OpImageGather"; + case OpImageDrefGather: return "OpImageDrefGather"; + case OpImageRead: return "OpImageRead"; + case OpImageWrite: return "OpImageWrite"; + case OpImage: return "OpImage"; + case OpImageQueryFormat: return "OpImageQueryFormat"; + case OpImageQueryOrder: return "OpImageQueryOrder"; + case OpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case OpImageQuerySize: return "OpImageQuerySize"; + case OpImageQueryLod: return "OpImageQueryLod"; + case OpImageQueryLevels: return "OpImageQueryLevels"; + case OpImageQuerySamples: return "OpImageQuerySamples"; + case OpConvertFToU: return "OpConvertFToU"; + case OpConvertFToS: return "OpConvertFToS"; + case OpConvertSToF: return "OpConvertSToF"; + case OpConvertUToF: return "OpConvertUToF"; + case OpUConvert: return "OpUConvert"; + case OpSConvert: return "OpSConvert"; + case OpFConvert: return "OpFConvert"; + case OpQuantizeToF16: return "OpQuantizeToF16"; + case OpConvertPtrToU: return "OpConvertPtrToU"; + case OpSatConvertSToU: return "OpSatConvertSToU"; + case OpSatConvertUToS: return "OpSatConvertUToS"; + case OpConvertUToPtr: return "OpConvertUToPtr"; + case OpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case OpGenericCastToPtr: return "OpGenericCastToPtr"; + case OpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case OpBitcast: return "OpBitcast"; + case OpSNegate: return "OpSNegate"; + case OpFNegate: return "OpFNegate"; + case OpIAdd: return "OpIAdd"; + case OpFAdd: return "OpFAdd"; + case OpISub: return "OpISub"; + case OpFSub: return "OpFSub"; + case OpIMul: return "OpIMul"; + case OpFMul: return "OpFMul"; + case OpUDiv: return "OpUDiv"; + case OpSDiv: return "OpSDiv"; + case OpFDiv: return "OpFDiv"; + case OpUMod: return "OpUMod"; + case OpSRem: return "OpSRem"; + case OpSMod: return "OpSMod"; + case OpFRem: return "OpFRem"; + case OpFMod: return "OpFMod"; + case OpVectorTimesScalar: return "OpVectorTimesScalar"; + case OpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case OpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case OpMatrixTimesVector: return "OpMatrixTimesVector"; + case OpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case OpOuterProduct: return "OpOuterProduct"; + case OpDot: return "OpDot"; + case OpIAddCarry: return "OpIAddCarry"; + case OpISubBorrow: return "OpISubBorrow"; + case OpUMulExtended: return "OpUMulExtended"; + case OpSMulExtended: return "OpSMulExtended"; + case OpAny: return "OpAny"; + case OpAll: return "OpAll"; + case OpIsNan: return "OpIsNan"; + case OpIsInf: return "OpIsInf"; + case OpIsFinite: return "OpIsFinite"; + case OpIsNormal: return "OpIsNormal"; + case OpSignBitSet: return "OpSignBitSet"; + case OpLessOrGreater: return "OpLessOrGreater"; + case OpOrdered: return "OpOrdered"; + case OpUnordered: return "OpUnordered"; + case OpLogicalEqual: return "OpLogicalEqual"; + case OpLogicalNotEqual: return "OpLogicalNotEqual"; + case OpLogicalOr: return "OpLogicalOr"; + case OpLogicalAnd: return "OpLogicalAnd"; + case OpLogicalNot: return "OpLogicalNot"; + case OpSelect: return "OpSelect"; + case OpIEqual: return "OpIEqual"; + case OpINotEqual: return "OpINotEqual"; + case OpUGreaterThan: return "OpUGreaterThan"; + case OpSGreaterThan: return "OpSGreaterThan"; + case OpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case OpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case OpULessThan: return "OpULessThan"; + case OpSLessThan: return "OpSLessThan"; + case OpULessThanEqual: return "OpULessThanEqual"; + case OpSLessThanEqual: return "OpSLessThanEqual"; + case OpFOrdEqual: return "OpFOrdEqual"; + case OpFUnordEqual: return "OpFUnordEqual"; + case OpFOrdNotEqual: return "OpFOrdNotEqual"; + case OpFUnordNotEqual: return "OpFUnordNotEqual"; + case OpFOrdLessThan: return "OpFOrdLessThan"; + case OpFUnordLessThan: return "OpFUnordLessThan"; + case OpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case OpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case OpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case OpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case OpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case OpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case OpShiftRightLogical: return "OpShiftRightLogical"; + case OpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case OpShiftLeftLogical: return "OpShiftLeftLogical"; + case OpBitwiseOr: return "OpBitwiseOr"; + case OpBitwiseXor: return "OpBitwiseXor"; + case OpBitwiseAnd: return "OpBitwiseAnd"; + case OpNot: return "OpNot"; + case OpBitFieldInsert: return "OpBitFieldInsert"; + case OpBitFieldSExtract: return "OpBitFieldSExtract"; + case OpBitFieldUExtract: return "OpBitFieldUExtract"; + case OpBitReverse: return "OpBitReverse"; + case OpBitCount: return "OpBitCount"; + case OpDPdx: return "OpDPdx"; + case OpDPdy: return "OpDPdy"; + case OpFwidth: return "OpFwidth"; + case OpDPdxFine: return "OpDPdxFine"; + case OpDPdyFine: return "OpDPdyFine"; + case OpFwidthFine: return "OpFwidthFine"; + case OpDPdxCoarse: return "OpDPdxCoarse"; + case OpDPdyCoarse: return "OpDPdyCoarse"; + case OpFwidthCoarse: return "OpFwidthCoarse"; + case OpEmitVertex: return "OpEmitVertex"; + case OpEndPrimitive: return "OpEndPrimitive"; + case OpEmitStreamVertex: return "OpEmitStreamVertex"; + case OpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case OpControlBarrier: return "OpControlBarrier"; + case OpMemoryBarrier: return "OpMemoryBarrier"; + case OpAtomicLoad: return "OpAtomicLoad"; + case OpAtomicStore: return "OpAtomicStore"; + case OpAtomicExchange: return "OpAtomicExchange"; + case OpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case OpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case OpAtomicIIncrement: return "OpAtomicIIncrement"; + case OpAtomicIDecrement: return "OpAtomicIDecrement"; + case OpAtomicIAdd: return "OpAtomicIAdd"; + case OpAtomicISub: return "OpAtomicISub"; + case OpAtomicSMin: return "OpAtomicSMin"; + case OpAtomicUMin: return "OpAtomicUMin"; + case OpAtomicSMax: return "OpAtomicSMax"; + case OpAtomicUMax: return "OpAtomicUMax"; + case OpAtomicAnd: return "OpAtomicAnd"; + case OpAtomicOr: return "OpAtomicOr"; + case OpAtomicXor: return "OpAtomicXor"; + case OpPhi: return "OpPhi"; + case OpLoopMerge: return "OpLoopMerge"; + case OpSelectionMerge: return "OpSelectionMerge"; + case OpLabel: return "OpLabel"; + case OpBranch: return "OpBranch"; + case OpBranchConditional: return "OpBranchConditional"; + case OpSwitch: return "OpSwitch"; + case OpKill: return "OpKill"; + case OpReturn: return "OpReturn"; + case OpReturnValue: return "OpReturnValue"; + case OpUnreachable: return "OpUnreachable"; + case OpLifetimeStart: return "OpLifetimeStart"; + case OpLifetimeStop: return "OpLifetimeStop"; + case OpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case OpGroupWaitEvents: return "OpGroupWaitEvents"; + case OpGroupAll: return "OpGroupAll"; + case OpGroupAny: return "OpGroupAny"; + case OpGroupBroadcast: return "OpGroupBroadcast"; + case OpGroupIAdd: return "OpGroupIAdd"; + case OpGroupFAdd: return "OpGroupFAdd"; + case OpGroupFMin: return "OpGroupFMin"; + case OpGroupUMin: return "OpGroupUMin"; + case OpGroupSMin: return "OpGroupSMin"; + case OpGroupFMax: return "OpGroupFMax"; + case OpGroupUMax: return "OpGroupUMax"; + case OpGroupSMax: return "OpGroupSMax"; + case OpReadPipe: return "OpReadPipe"; + case OpWritePipe: return "OpWritePipe"; + case OpReservedReadPipe: return "OpReservedReadPipe"; + case OpReservedWritePipe: return "OpReservedWritePipe"; + case OpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case OpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case OpCommitReadPipe: return "OpCommitReadPipe"; + case OpCommitWritePipe: return "OpCommitWritePipe"; + case OpIsValidReserveId: return "OpIsValidReserveId"; + case OpGetNumPipePackets: return "OpGetNumPipePackets"; + case OpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case OpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case OpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case OpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case OpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case OpEnqueueMarker: return "OpEnqueueMarker"; + case OpEnqueueKernel: return "OpEnqueueKernel"; + case OpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case OpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case OpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case OpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case OpRetainEvent: return "OpRetainEvent"; + case OpReleaseEvent: return "OpReleaseEvent"; + case OpCreateUserEvent: return "OpCreateUserEvent"; + case OpIsValidEvent: return "OpIsValidEvent"; + case OpSetUserEventStatus: return "OpSetUserEventStatus"; + case OpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case OpGetDefaultQueue: return "OpGetDefaultQueue"; + case OpBuildNDRange: return "OpBuildNDRange"; + case OpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case OpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case OpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case OpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case OpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case OpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case OpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case OpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case OpImageSparseFetch: return "OpImageSparseFetch"; + case OpImageSparseGather: return "OpImageSparseGather"; + case OpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case OpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case OpNoLine: return "OpNoLine"; + case OpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case OpAtomicFlagClear: return "OpAtomicFlagClear"; + case OpImageSparseRead: return "OpImageSparseRead"; + case OpSizeOf: return "OpSizeOf"; + case OpTypePipeStorage: return "OpTypePipeStorage"; + case OpConstantPipeStorage: return "OpConstantPipeStorage"; + case OpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case OpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case OpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case OpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case OpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case OpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case OpModuleProcessed: return "OpModuleProcessed"; + case OpExecutionModeId: return "OpExecutionModeId"; + case OpDecorateId: return "OpDecorateId"; + case OpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case OpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case OpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case OpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case OpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case OpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case OpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case OpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case OpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case OpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case OpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case OpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case OpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case OpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case OpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case OpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case OpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case OpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case OpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case OpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case OpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case OpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case OpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case OpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case OpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case OpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case OpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case OpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case OpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case OpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case OpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case OpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case OpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case OpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case OpCopyLogical: return "OpCopyLogical"; + case OpPtrEqual: return "OpPtrEqual"; + case OpPtrNotEqual: return "OpPtrNotEqual"; + case OpPtrDiff: return "OpPtrDiff"; + case OpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case OpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case OpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case OpTerminateInvocation: return "OpTerminateInvocation"; + case OpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case OpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case OpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case OpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case OpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case OpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case OpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case OpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case OpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case OpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case OpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case OpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case OpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case OpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case OpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case OpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpSDot: return "OpSDot"; + case OpUDot: return "OpUDot"; + case OpSUDot: return "OpSUDot"; + case OpSDotAccSat: return "OpSDotAccSat"; + case OpUDotAccSat: return "OpUDotAccSat"; + case OpSUDotAccSat: return "OpSUDotAccSat"; + case OpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case OpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case OpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case OpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case OpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case OpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case OpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case OpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case OpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case OpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case OpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case OpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case OpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case OpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case OpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case OpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case OpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case OpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case OpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case OpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case OpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case OpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case OpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case OpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case OpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case OpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case OpReadClockKHR: return "OpReadClockKHR"; + case OpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case OpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case OpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case OpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case OpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case OpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case OpConstantStringAMDX: return "OpConstantStringAMDX"; + case OpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case OpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case OpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case OpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case OpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case OpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case OpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case OpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case OpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case OpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case OpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case OpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case OpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case OpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case OpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case OpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case OpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case OpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case OpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case OpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case OpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case OpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case OpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case OpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case OpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case OpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case OpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case OpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case OpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case OpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case OpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case OpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case OpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case OpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case OpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case OpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case OpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case OpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case OpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case OpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case OpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case OpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case OpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case OpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case OpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case OpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case OpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case OpTerminateRayNV: return "OpTerminateRayNV"; + case OpTraceNV: return "OpTraceNV"; + case OpTraceMotionNV: return "OpTraceMotionNV"; + case OpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableNV: return "OpExecuteCallableNV"; + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case OpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case OpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case OpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case OpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case OpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case OpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case OpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case OpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case OpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case OpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case OpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case OpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case OpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case OpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case OpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case OpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case OpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case OpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case OpConvertUToImageNV: return "OpConvertUToImageNV"; + case OpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case OpConvertImageToUNV: return "OpConvertImageToUNV"; + case OpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case OpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case OpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case OpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case OpRawAccessChainNV: return "OpRawAccessChainNV"; + case OpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case OpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case OpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case OpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case OpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case OpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case OpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case OpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case OpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case OpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case OpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case OpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case OpAbsISubINTEL: return "OpAbsISubINTEL"; + case OpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case OpIAddSatINTEL: return "OpIAddSatINTEL"; + case OpUAddSatINTEL: return "OpUAddSatINTEL"; + case OpIAverageINTEL: return "OpIAverageINTEL"; + case OpUAverageINTEL: return "OpUAverageINTEL"; + case OpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case OpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case OpISubSatINTEL: return "OpISubSatINTEL"; + case OpUSubSatINTEL: return "OpUSubSatINTEL"; + case OpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case OpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case OpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case OpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case OpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case OpAsmINTEL: return "OpAsmINTEL"; + case OpAsmCallINTEL: return "OpAsmCallINTEL"; + case OpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case OpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case OpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case OpExpectKHR: return "OpExpectKHR"; + case OpDecorateString: return "OpDecorateString"; + case OpMemberDecorateString: return "OpMemberDecorateString"; + case OpVmeImageINTEL: return "OpVmeImageINTEL"; + case OpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case OpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case OpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case OpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case OpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case OpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case OpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case OpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case OpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case OpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case OpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case OpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case OpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case OpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case OpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case OpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case OpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case OpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case OpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case OpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case OpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case OpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case OpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case OpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case OpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case OpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case OpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case OpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case OpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case OpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case OpArbitraryFloatSinCosPiINTEL: return "OpArbitraryFloatSinCosPiINTEL"; + case OpArbitraryFloatCastINTEL: return "OpArbitraryFloatCastINTEL"; + case OpArbitraryFloatCastFromIntINTEL: return "OpArbitraryFloatCastFromIntINTEL"; + case OpArbitraryFloatCastToIntINTEL: return "OpArbitraryFloatCastToIntINTEL"; + case OpArbitraryFloatAddINTEL: return "OpArbitraryFloatAddINTEL"; + case OpArbitraryFloatSubINTEL: return "OpArbitraryFloatSubINTEL"; + case OpArbitraryFloatMulINTEL: return "OpArbitraryFloatMulINTEL"; + case OpArbitraryFloatDivINTEL: return "OpArbitraryFloatDivINTEL"; + case OpArbitraryFloatGTINTEL: return "OpArbitraryFloatGTINTEL"; + case OpArbitraryFloatGEINTEL: return "OpArbitraryFloatGEINTEL"; + case OpArbitraryFloatLTINTEL: return "OpArbitraryFloatLTINTEL"; + case OpArbitraryFloatLEINTEL: return "OpArbitraryFloatLEINTEL"; + case OpArbitraryFloatEQINTEL: return "OpArbitraryFloatEQINTEL"; + case OpArbitraryFloatRecipINTEL: return "OpArbitraryFloatRecipINTEL"; + case OpArbitraryFloatRSqrtINTEL: return "OpArbitraryFloatRSqrtINTEL"; + case OpArbitraryFloatCbrtINTEL: return "OpArbitraryFloatCbrtINTEL"; + case OpArbitraryFloatHypotINTEL: return "OpArbitraryFloatHypotINTEL"; + case OpArbitraryFloatSqrtINTEL: return "OpArbitraryFloatSqrtINTEL"; + case OpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case OpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case OpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case OpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case OpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case OpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case OpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case OpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case OpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case OpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case OpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case OpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case OpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case OpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case OpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case OpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case OpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case OpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case OpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case OpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case OpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case OpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case OpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case OpLoopControlINTEL: return "OpLoopControlINTEL"; + case OpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case OpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case OpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case OpFixedSqrtINTEL: return "OpFixedSqrtINTEL"; + case OpFixedRecipINTEL: return "OpFixedRecipINTEL"; + case OpFixedRsqrtINTEL: return "OpFixedRsqrtINTEL"; + case OpFixedSinINTEL: return "OpFixedSinINTEL"; + case OpFixedCosINTEL: return "OpFixedCosINTEL"; + case OpFixedSinCosINTEL: return "OpFixedSinCosINTEL"; + case OpFixedSinPiINTEL: return "OpFixedSinPiINTEL"; + case OpFixedCosPiINTEL: return "OpFixedCosPiINTEL"; + case OpFixedSinCosPiINTEL: return "OpFixedSinCosPiINTEL"; + case OpFixedLogINTEL: return "OpFixedLogINTEL"; + case OpFixedExpINTEL: return "OpFixedExpINTEL"; + case OpPtrCastToCrossWorkgroupINTEL: return "OpPtrCastToCrossWorkgroupINTEL"; + case OpCrossWorkgroupCastToPtrINTEL: return "OpCrossWorkgroupCastToPtrINTEL"; + case OpReadPipeBlockingINTEL: return "OpReadPipeBlockingINTEL"; + case OpWritePipeBlockingINTEL: return "OpWritePipeBlockingINTEL"; + case OpFPGARegINTEL: return "OpFPGARegINTEL"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case OpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case OpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case OpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case OpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case OpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case OpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case OpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case OpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case OpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case OpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case OpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case OpGroupIMulKHR: return "OpGroupIMulKHR"; + case OpGroupFMulKHR: return "OpGroupFMulKHR"; + case OpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case OpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case OpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case OpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case OpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case OpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case OpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case OpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload bitwise operators for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline ImageOperandsMask operator&(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) & unsigned(b)); } +inline ImageOperandsMask operator^(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) ^ unsigned(b)); } +inline ImageOperandsMask operator~(ImageOperandsMask a) { return ImageOperandsMask(~unsigned(a)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator&(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) & unsigned(b)); } +inline FPFastMathModeMask operator^(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) ^ unsigned(b)); } +inline FPFastMathModeMask operator~(FPFastMathModeMask a) { return FPFastMathModeMask(~unsigned(a)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator&(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) & unsigned(b)); } +inline SelectionControlMask operator^(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) ^ unsigned(b)); } +inline SelectionControlMask operator~(SelectionControlMask a) { return SelectionControlMask(~unsigned(a)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator&(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) & unsigned(b)); } +inline LoopControlMask operator^(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) ^ unsigned(b)); } +inline LoopControlMask operator~(LoopControlMask a) { return LoopControlMask(~unsigned(a)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator&(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) & unsigned(b)); } +inline FunctionControlMask operator^(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) ^ unsigned(b)); } +inline FunctionControlMask operator~(FunctionControlMask a) { return FunctionControlMask(~unsigned(a)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator&(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) & unsigned(b)); } +inline MemorySemanticsMask operator^(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) ^ unsigned(b)); } +inline MemorySemanticsMask operator~(MemorySemanticsMask a) { return MemorySemanticsMask(~unsigned(a)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator&(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) & unsigned(b)); } +inline MemoryAccessMask operator^(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) ^ unsigned(b)); } +inline MemoryAccessMask operator~(MemoryAccessMask a) { return MemoryAccessMask(~unsigned(a)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator&(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) & unsigned(b)); } +inline KernelProfilingInfoMask operator^(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) ^ unsigned(b)); } +inline KernelProfilingInfoMask operator~(KernelProfilingInfoMask a) { return KernelProfilingInfoMask(~unsigned(a)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator&(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) & unsigned(b)); } +inline RayFlagsMask operator^(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) ^ unsigned(b)); } +inline RayFlagsMask operator~(RayFlagsMask a) { return RayFlagsMask(~unsigned(a)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator&(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) & unsigned(b)); } +inline FragmentShadingRateMask operator^(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) ^ unsigned(b)); } +inline FragmentShadingRateMask operator~(FragmentShadingRateMask a) { return FragmentShadingRateMask(~unsigned(a)); } +inline CooperativeMatrixOperandsMask operator|(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixOperandsMask operator&(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixOperandsMask operator^(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixOperandsMask operator~(CooperativeMatrixOperandsMask a) { return CooperativeMatrixOperandsMask(~unsigned(a)); } +inline CooperativeMatrixReduceMask operator|(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixReduceMask operator&(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixReduceMask operator^(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixReduceMask operator~(CooperativeMatrixReduceMask a) { return CooperativeMatrixReduceMask(~unsigned(a)); } +inline TensorAddressingOperandsMask operator|(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) | unsigned(b)); } +inline TensorAddressingOperandsMask operator&(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) & unsigned(b)); } +inline TensorAddressingOperandsMask operator^(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) ^ unsigned(b)); } +inline TensorAddressingOperandsMask operator~(TensorAddressingOperandsMask a) { return TensorAddressingOperandsMask(~unsigned(a)); } +inline RawAccessChainOperandsMask operator|(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) | unsigned(b)); } +inline RawAccessChainOperandsMask operator&(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) & unsigned(b)); } +inline RawAccessChainOperandsMask operator^(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) ^ unsigned(b)); } +inline RawAccessChainOperandsMask operator~(RawAccessChainOperandsMask a) { return RawAccessChainOperandsMask(~unsigned(a)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/glslang/SPIRV/spvIR.h b/engine/engine/3rdlibs/glslang/SPIRV/spvIR.h new file mode 100644 index 00000000..56b6c560 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/SPIRV/spvIR.h @@ -0,0 +1,592 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// SPIRV-IR +// +// Simple in-memory representation (IR) of SPIRV. Just for holding +// Each function's CFG of blocks. Has this hierarchy: +// - Module, which is a list of +// - Function, which is a list of +// - Block, which is a list of +// - Instruction +// + +#pragma once +#ifndef spvIR_H +#define spvIR_H + +#include "spirv.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spv { + +class Block; +class Function; +class Module; + +const Id NoResult = 0; +const Id NoType = 0; + +const Decoration NoPrecision = DecorationMax; + +#ifdef __GNUC__ +# define POTENTIALLY_UNUSED __attribute__((unused)) +#else +# define POTENTIALLY_UNUSED +#endif + +POTENTIALLY_UNUSED +const MemorySemanticsMask MemorySemanticsAllMemory = + (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask | + MemorySemanticsWorkgroupMemoryMask | + MemorySemanticsAtomicCounterMemoryMask | + MemorySemanticsImageMemoryMask); + +struct IdImmediate { + bool isId; // true if word is an Id, false if word is an immediate + unsigned word; + IdImmediate(bool i, unsigned w) : isId(i), word(w) {} +}; + +// +// SPIR-V IR instruction. +// + +class Instruction { +public: + Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { } + explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { } + virtual ~Instruction() {} + void reserveOperands(size_t count) { + operands.reserve(count); + idOperand.reserve(count); + } + void addIdOperand(Id id) { + // ids can't be 0 + assert(id); + operands.push_back(id); + idOperand.push_back(true); + } + // This method is potentially dangerous as it can break assumptions + // about SSA and lack of forward references. + void setIdOperand(unsigned idx, Id id) { + assert(id); + assert(idOperand[idx]); + operands[idx] = id; + } + + void addImmediateOperand(unsigned int immediate) { + operands.push_back(immediate); + idOperand.push_back(false); + } + void setImmediateOperand(unsigned idx, unsigned int immediate) { + assert(!idOperand[idx]); + operands[idx] = immediate; + } + + void addStringOperand(const char* str) + { + unsigned int word = 0; + unsigned int shiftAmount = 0; + unsigned char c; + + do { + c = *(str++); + word |= ((unsigned int)c) << shiftAmount; + shiftAmount += 8; + if (shiftAmount == 32) { + addImmediateOperand(word); + word = 0; + shiftAmount = 0; + } + } while (c != 0); + + // deal with partial last word + if (shiftAmount > 0) { + addImmediateOperand(word); + } + } + bool isIdOperand(int op) const { return idOperand[op]; } + void setBlock(Block* b) { block = b; } + Block* getBlock() const { return block; } + Op getOpCode() const { return opCode; } + int getNumOperands() const + { + assert(operands.size() == idOperand.size()); + return (int)operands.size(); + } + Id getResultId() const { return resultId; } + Id getTypeId() const { return typeId; } + Id getIdOperand(int op) const { + assert(idOperand[op]); + return operands[op]; + } + unsigned int getImmediateOperand(int op) const { + assert(!idOperand[op]); + return operands[op]; + } + + // Write out the binary form. + void dump(std::vector& out) const + { + // Compute the wordCount + unsigned int wordCount = 1; + if (typeId) + ++wordCount; + if (resultId) + ++wordCount; + wordCount += (unsigned int)operands.size(); + + // Write out the beginning of the instruction + out.push_back(((wordCount) << WordCountShift) | opCode); + if (typeId) + out.push_back(typeId); + if (resultId) + out.push_back(resultId); + + // Write out the operands + for (int op = 0; op < (int)operands.size(); ++op) + out.push_back(operands[op]); + } + + const char *getNameString() const { + if (opCode == OpString) { + return (const char *)&operands[0]; + } else { + assert(opCode == OpName); + return (const char *)&operands[1]; + } + } + +protected: + Instruction(const Instruction&); + Id resultId; + Id typeId; + Op opCode; + std::vector operands; // operands, both and immediates (both are unsigned int) + std::vector idOperand; // true for operands that are , false for immediates + Block* block; +}; + +// +// SPIR-V IR block. +// + +struct DebugSourceLocation { + int line; + int column; + spv::Id fileId; +}; + +class Block { +public: + Block(Id id, Function& parent); + virtual ~Block() + { + } + + Id getId() { return instructions.front()->getResultId(); } + + Function& getParent() const { return parent; } + // Returns true if the source location is actually updated. + // Note we still need the builder to insert the line marker instruction. This is just a tracker. + bool updateDebugSourceLocation(int line, int column, spv::Id fileId) { + if (currentSourceLoc && currentSourceLoc->line == line && currentSourceLoc->column == column && + currentSourceLoc->fileId == fileId) { + return false; + } + + currentSourceLoc = DebugSourceLocation{line, column, fileId}; + return true; + } + // Returns true if the scope is actually updated. + // Note we still need the builder to insert the debug scope instruction. This is just a tracker. + bool updateDebugScope(spv::Id scopeId) { + assert(scopeId); + if (currentDebugScope && *currentDebugScope == scopeId) { + return false; + } + + currentDebugScope = scopeId; + return true; + } + void addInstruction(std::unique_ptr inst); + void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);} + void addLocalVariable(std::unique_ptr inst) { localVariables.push_back(std::move(inst)); } + const std::vector& getPredecessors() const { return predecessors; } + const std::vector& getSuccessors() const { return successors; } + std::vector >& getInstructions() { + return instructions; + } + const std::vector >& getLocalVariables() const { return localVariables; } + void setUnreachable() { unreachable = true; } + bool isUnreachable() const { return unreachable; } + // Returns the block's merge instruction, if one exists (otherwise null). + const Instruction* getMergeInstruction() const { + if (instructions.size() < 2) return nullptr; + const Instruction* nextToLast = (instructions.cend() - 2)->get(); + switch (nextToLast->getOpCode()) { + case OpSelectionMerge: + case OpLoopMerge: + return nextToLast; + default: + return nullptr; + } + return nullptr; + } + + // Change this block into a canonical dead merge block. Delete instructions + // as necessary. A canonical dead merge block has only an OpLabel and an + // OpUnreachable. + void rewriteAsCanonicalUnreachableMerge() { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + addInstruction(std::unique_ptr(new Instruction(OpUnreachable))); + } + // Change this block into a canonical dead continue target branching to the + // given header ID. Delete instructions as necessary. A canonical dead continue + // target has only an OpLabel and an unconditional branch back to the corresponding + // header. + void rewriteAsCanonicalUnreachableContinue(Block* header) { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + // Add OpBranch back to the header. + assert(header != nullptr); + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(header->getId()); + addInstruction(std::unique_ptr(branch)); + successors.push_back(header); + } + + bool isTerminated() const + { + switch (instructions.back()->getOpCode()) { + case OpBranch: + case OpBranchConditional: + case OpSwitch: + case OpKill: + case OpTerminateInvocation: + case OpReturn: + case OpReturnValue: + case OpUnreachable: + return true; + default: + return false; + } + } + + void dump(std::vector& out) const + { + instructions[0]->dump(out); + for (int i = 0; i < (int)localVariables.size(); ++i) + localVariables[i]->dump(out); + for (int i = 1; i < (int)instructions.size(); ++i) + instructions[i]->dump(out); + } + +protected: + Block(const Block&); + Block& operator=(Block&); + + // To enforce keeping parent and ownership in sync: + friend Function; + + std::vector > instructions; + std::vector predecessors, successors; + std::vector > localVariables; + Function& parent; + + // Track source location of the last source location marker instruction. + std::optional currentSourceLoc; + + // Track scope of the last debug scope instruction. + std::optional currentDebugScope; + + // track whether this block is known to be uncreachable (not necessarily + // true for all unreachable blocks, but should be set at least + // for the extraneous ones introduced by the builder). + bool unreachable; +}; + +// The different reasons for reaching a block in the inReadableOrder traversal. +enum ReachReason { + // Reachable from the entry block via transfers of control, i.e. branches. + ReachViaControlFlow = 0, + // A continue target that is not reachable via control flow. + ReachDeadContinue, + // A merge block that is not reachable via control flow. + ReachDeadMerge +}; + +// Traverses the control-flow graph rooted at root in an order suited for +// readable code generation. Invokes callback at every node in the traversal +// order. The callback arguments are: +// - the block, +// - the reason we reached the block, +// - if the reason was that block is an unreachable continue or unreachable merge block +// then the last parameter is the corresponding header block. +void inReadableOrder(Block* root, std::function callback); + +// +// SPIR-V IR Function. +// + +class Function { +public: + Function(Id id, Id resultType, Id functionType, Id firstParam, LinkageType linkage, const std::string& name, Module& parent); + virtual ~Function() + { + for (int i = 0; i < (int)parameterInstructions.size(); ++i) + delete parameterInstructions[i]; + + for (int i = 0; i < (int)blocks.size(); ++i) + delete blocks[i]; + } + Id getId() const { return functionInstruction.getResultId(); } + Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); } + Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); } + + void addBlock(Block* block) { blocks.push_back(block); } + void removeBlock(Block* block) + { + auto found = find(blocks.begin(), blocks.end(), block); + assert(found != blocks.end()); + blocks.erase(found); + delete block; + } + + Module& getParent() const { return parent; } + Block* getEntryBlock() const { return blocks.front(); } + Block* getLastBlock() const { return blocks.back(); } + const std::vector& getBlocks() const { return blocks; } + void addLocalVariable(std::unique_ptr inst); + Id getReturnType() const { return functionInstruction.getTypeId(); } + Id getFuncId() const { return functionInstruction.getResultId(); } + Id getFuncTypeId() const { return functionInstruction.getIdOperand(1); } + void setReturnPrecision(Decoration precision) + { + if (precision == DecorationRelaxedPrecision) + reducedPrecisionReturn = true; + } + Decoration getReturnPrecision() const + { return reducedPrecisionReturn ? DecorationRelaxedPrecision : NoPrecision; } + + void setDebugLineInfo(Id fileName, int line, int column) { + lineInstruction = std::unique_ptr{new Instruction(OpLine)}; + lineInstruction->reserveOperands(3); + lineInstruction->addIdOperand(fileName); + lineInstruction->addImmediateOperand(line); + lineInstruction->addImmediateOperand(column); + } + bool hasDebugLineInfo() const { return lineInstruction != nullptr; } + + void setImplicitThis() { implicitThis = true; } + bool hasImplicitThis() const { return implicitThis; } + + void addParamPrecision(unsigned param, Decoration precision) + { + if (precision == DecorationRelaxedPrecision) + reducedPrecisionParams.insert(param); + } + Decoration getParamPrecision(unsigned param) const + { + return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ? + DecorationRelaxedPrecision : NoPrecision; + } + + void dump(std::vector& out) const + { + // OpLine + if (lineInstruction != nullptr) { + lineInstruction->dump(out); + } + + // OpFunction + functionInstruction.dump(out); + + // OpFunctionParameter + for (int p = 0; p < (int)parameterInstructions.size(); ++p) + parameterInstructions[p]->dump(out); + + // Blocks + inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); }); + Instruction end(0, 0, OpFunctionEnd); + end.dump(out); + } + + LinkageType getLinkType() const { return linkType; } + const char* getExportName() const { return exportName.c_str(); } + +protected: + Function(const Function&); + Function& operator=(Function&); + + Module& parent; + std::unique_ptr lineInstruction; + Instruction functionInstruction; + std::vector parameterInstructions; + std::vector blocks; + bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument + bool reducedPrecisionReturn; + std::set reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg + LinkageType linkType; + std::string exportName; +}; + +// +// SPIR-V IR Module. +// + +class Module { +public: + Module() {} + virtual ~Module() + { + // TODO delete things + } + + void addFunction(Function *fun) { functions.push_back(fun); } + + void mapInstruction(Instruction *instruction) + { + spv::Id resultId = instruction->getResultId(); + // map the instruction's result id + if (resultId >= idToInstruction.size()) + idToInstruction.resize(resultId + 16); + idToInstruction[resultId] = instruction; + } + + Instruction* getInstruction(Id id) const { return idToInstruction[id]; } + const std::vector& getFunctions() const { return functions; } + spv::Id getTypeId(Id resultId) const { + return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId(); + } + StorageClass getStorageClass(Id typeId) const + { + assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer); + return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0); + } + + void dump(std::vector& out) const + { + for (int f = 0; f < (int)functions.size(); ++f) + functions[f]->dump(out); + } + +protected: + Module(const Module&); + std::vector functions; + + // map from result id to instruction having that result id + std::vector idToInstruction; + + // map from a result id to its type id +}; + +// +// Implementation (it's here due to circular type definitions). +// + +// Add both +// - the OpFunction instruction +// - all the OpFunctionParameter instructions +__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, LinkageType linkage, const std::string& name, Module& parent) + : parent(parent), lineInstruction(nullptr), + functionInstruction(id, resultType, OpFunction), implicitThis(false), + reducedPrecisionReturn(false), + linkType(linkage) +{ + // OpFunction + functionInstruction.reserveOperands(2); + functionInstruction.addImmediateOperand(FunctionControlMaskNone); + functionInstruction.addIdOperand(functionType); + parent.mapInstruction(&functionInstruction); + parent.addFunction(this); + + // OpFunctionParameter + Instruction* typeInst = parent.getInstruction(functionType); + int numParams = typeInst->getNumOperands() - 1; + for (int p = 0; p < numParams; ++p) { + Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter); + parent.mapInstruction(param); + parameterInstructions.push_back(param); + } + + // If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration + if (linkType != LinkageTypeMax) { + exportName = name.substr(0, name.find_first_of('(')); + } +} + +__inline void Function::addLocalVariable(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + blocks[0]->addLocalVariable(std::move(inst)); + parent.mapInstruction(raw_instruction); +} + +__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false) +{ + instructions.push_back(std::unique_ptr(new Instruction(id, NoType, OpLabel))); + instructions.back()->setBlock(this); + parent.getParent().mapInstruction(instructions.back().get()); +} + +__inline void Block::addInstruction(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + instructions.push_back(std::move(inst)); + raw_instruction->setBlock(this); + if (raw_instruction->getResultId()) + parent.getParent().mapInstruction(raw_instruction); +} + +} // end spv namespace + +#endif // spvIR_H diff --git a/engine/engine/3rdlibs/glslang/StandAlone/CMakeLists.txt b/engine/engine/3rdlibs/glslang/StandAlone/CMakeLists.txt new file mode 100644 index 00000000..18a24ce3 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/StandAlone/CMakeLists.txt @@ -0,0 +1,122 @@ +# Copyright (C) 2020-2025 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +find_package(Python3 REQUIRED) + +set(GLSLANG_INTRINSIC_H "${GLSLANG_GENERATED_INCLUDEDIR}/glslang/glsl_intrinsic_header.h") +set(GLSLANG_INTRINSIC_PY "${CMAKE_CURRENT_SOURCE_DIR}/../gen_extension_headers.py") +set(GLSLANG_INTRINSIC_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../glslang/ExtensionHeaders") + +add_custom_command( + OUTPUT ${GLSLANG_INTRINSIC_H} + COMMAND Python3::Interpreter "${GLSLANG_INTRINSIC_PY}" + "-i" ${GLSLANG_INTRINSIC_HEADER_DIR} + "-o" ${GLSLANG_INTRINSIC_H} + DEPENDS ${GLSLANG_INTRINSIC_PY} + COMMENT "Generating ${GLSLANG_INTRINSIC_H}") + +set(SOURCES StandAlone.cpp DirStackFileIncluder.h ${GLSLANG_INTRINSIC_H}) + +add_executable(glslang-standalone ${SOURCES}) +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + target_compile_options(glslang-standalone PRIVATE -Wconversion) +elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT MSVC) + target_compile_options(glslang-standalone PRIVATE -Wshorten-64-to-32) +endif() +set_property(TARGET glslang-standalone PROPERTY FOLDER tools) +set_property(TARGET glslang-standalone PROPERTY OUTPUT_NAME glslang) +glslang_set_link_args(glslang-standalone) + +set(LIBRARIES + glslang + glslang-default-resource-limits + $<$,$,9.0>>:stdc++fs>) + +if(WIN32) + set(LIBRARIES ${LIBRARIES} psapi) +elseif(UNIX) + if(NOT ANDROID AND NOT QNX) + set(LIBRARIES ${LIBRARIES} pthread) + endif() +endif() + +target_link_libraries(glslang-standalone ${LIBRARIES}) +target_include_directories(glslang-standalone PUBLIC + $) + +if(ENABLE_SPIRV) + if(ENABLE_SPVREMAPPER) + set(REMAPPER_SOURCES spirv-remap.cpp) + add_executable(spirv-remap ${REMAPPER_SOURCES}) + set_property(TARGET spirv-remap PROPERTY FOLDER tools) + glslang_set_link_args(spirv-remap) + target_link_libraries(spirv-remap SPVRemapper ${LIBRARIES}) + endif() +endif() + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif() + +# Create a symbolic link to glslang named glslangValidator for backwards compatibility +set(legacy_glslang_name "glslangValidator${CMAKE_EXECUTABLE_SUFFIX}") +set(link_method create_symlink) +if(WIN32 OR MINGW) + set(link_method copy_if_different) +endif() + +add_custom_command( + TARGET glslang-standalone POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E "${link_method}" "\$" "${legacy_glslang_name}" + WORKING_DIRECTORY "\$" + VERBATIM +) + +if(GLSLANG_ENABLE_INSTALL) + install(TARGETS glslang-standalone EXPORT glslang-targets) + + # Create the same symlink at install time + install(CODE "\ + message(STATUS \"Installing (${link_method}): \$ -> \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\") + execute_process( + COMMAND \"\${CMAKE_COMMAND}\" -E ${link_method} [=[\$]=] [=[${legacy_glslang_name}]=] + WORKING_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\" + ) + ") + + if(ENABLE_SPIRV) + if(ENABLE_SPVREMAPPER) + install(TARGETS spirv-remap EXPORT glslang-targets) + endif() + endif() +endif() diff --git a/engine/engine/3rdlibs/glslang/StandAlone/DirStackFileIncluder.h b/engine/engine/3rdlibs/glslang/StandAlone/DirStackFileIncluder.h new file mode 100644 index 00000000..5a33c78f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/StandAlone/DirStackFileIncluder.h @@ -0,0 +1,149 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2017 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include +#include +#include +#include +#include + +#include "./../glslang/Public/ShaderLang.h" + +// Default include class for normal include convention of search backward +// through the stack of active include paths (for nested includes). +// Can be overridden to customize. +class DirStackFileIncluder : public glslang::TShader::Includer { +public: + DirStackFileIncluder() : externalLocalDirectoryCount(0) { } + + virtual IncludeResult* includeLocal(const char* headerName, + const char* includerName, + size_t inclusionDepth) override + { + return readLocalPath(headerName, includerName, (int)inclusionDepth); + } + + virtual IncludeResult* includeSystem(const char* headerName, + const char* /*includerName*/, + size_t /*inclusionDepth*/) override + { + return readSystemPath(headerName); + } + + // Externally set directories. E.g., from a command-line -I. + // - Most-recently pushed are checked first. + // - All these are checked after the parse-time stack of local directories + // is checked. + // - This only applies to the "local" form of #include. + // - Makes its own copy of the path. + virtual void pushExternalLocalDirectory(const std::string& dir) + { + directoryStack.push_back(dir); + externalLocalDirectoryCount = (int)directoryStack.size(); + } + + virtual void releaseInclude(IncludeResult* result) override + { + if (result != nullptr) { + delete [] static_cast(result->userData); + delete result; + } + } + + virtual std::set getIncludedFiles() + { + return includedFiles; + } + + virtual ~DirStackFileIncluder() override { } + +protected: + typedef char tUserDataElement; + std::vector directoryStack; + int externalLocalDirectoryCount; + std::set includedFiles; + + // Search for a valid "local" path based on combining the stack of include + // directories and the nominal name of the header. + virtual IncludeResult* readLocalPath(const char* headerName, const char* includerName, int depth) + { + // Discard popped include directories, and + // initialize when at parse-time first level. + directoryStack.resize(depth + externalLocalDirectoryCount); + if (depth == 1) + directoryStack.back() = getDirectory(includerName); + + // Find a directory that works, using a reverse search of the include stack. + for (auto it = directoryStack.rbegin(); it != directoryStack.rend(); ++it) { + std::string path = *it + '/' + headerName; + std::replace(path.begin(), path.end(), '\\', '/'); + std::ifstream file(path, std::ios_base::binary | std::ios_base::ate); + if (file) { + directoryStack.push_back(getDirectory(path)); + includedFiles.insert(path); + return newIncludeResult(path, file, (int)file.tellg()); + } + } + + return nullptr; + } + + // Search for a valid path. + // Not implemented yet; returning nullptr signals failure to find. + virtual IncludeResult* readSystemPath(const char* /*headerName*/) const + { + return nullptr; + } + + // Do actual reading of the file, filling in a new include result. + virtual IncludeResult* newIncludeResult(const std::string& path, std::ifstream& file, int length) const + { + char* content = new tUserDataElement [length]; + file.seekg(0, file.beg); + file.read(content, length); + return new IncludeResult(path, content, length, content); + } + + // If no path markers, return current working directory. + // Otherwise, strip file name and return path leading up to it. + virtual std::string getDirectory(const std::string path) const + { + size_t last = path.find_last_of("/\\"); + return last == std::string::npos ? "." : path.substr(0, last); + } +}; diff --git a/engine/engine/3rdlibs/glslang/StandAlone/StandAlone.cpp b/engine/engine/3rdlibs/glslang/StandAlone/StandAlone.cpp new file mode 100644 index 00000000..88ac68b7 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/StandAlone/StandAlone.cpp @@ -0,0 +1,2254 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2016-2020 Google, Inc. +// Modifications Copyright(C) 2021 Advanced Micro Devices, Inc.All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// this only applies to the standalone wrapper, not the front end in general +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "glslang/Public/ResourceLimits.h" +#include "Worklist.h" +#include "DirStackFileIncluder.h" +#include "./../glslang/Public/ShaderLang.h" +#include "../glslang/MachineIndependent/localintermediate.h" +#include "../SPIRV/GlslangToSpv.h" +#include "../SPIRV/GLSL.std.450.h" +#include "../SPIRV/disassemble.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../glslang/OSDependent/osinclude.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +#include "glslang/glsl_intrinsic_header.h" + +extern "C" { + GLSLANG_EXPORT void ShOutputHtml(); +} + +// Command-line options +enum TOptions : uint64_t { + EOptionNone = 0, + EOptionIntermediate = (1ull << 0), + EOptionSuppressInfolog = (1ull << 1), + EOptionMemoryLeakMode = (1ull << 2), + EOptionRelaxedErrors = (1ull << 3), + EOptionGiveWarnings = (1ull << 4), + EOptionLinkProgram = (1ull << 5), + EOptionMultiThreaded = (1ull << 6), + EOptionDumpConfig = (1ull << 7), + EOptionDumpReflection = (1ull << 8), + EOptionSuppressWarnings = (1ull << 9), + EOptionDumpVersions = (1ull << 10), + EOptionSpv = (1ull << 11), + EOptionHumanReadableSpv = (1ull << 12), + EOptionVulkanRules = (1ull << 13), + EOptionDefaultDesktop = (1ull << 14), + EOptionOutputPreprocessed = (1ull << 15), + EOptionOutputHexadecimal = (1ull << 16), + EOptionReadHlsl = (1ull << 17), + EOptionCascadingErrors = (1ull << 18), + EOptionAutoMapBindings = (1ull << 19), + EOptionFlattenUniformArrays = (1ull << 20), + EOptionNoStorageFormat = (1ull << 21), + EOptionKeepUncalled = (1ull << 22), + EOptionHlslOffsets = (1ull << 23), + EOptionHlslIoMapping = (1ull << 24), + EOptionAutoMapLocations = (1ull << 25), + EOptionDebug = (1ull << 26), + EOptionStdin = (1ull << 27), + EOptionOptimizeDisable = (1ull << 28), + EOptionOptimizeSize = (1ull << 29), + EOptionInvertY = (1ull << 30), + EOptionDumpBareVersion = (1ull << 31), + EOptionCompileOnly = (1ull << 32), + EOptionDisplayErrorColumn = (1ull << 33), + EOptionLinkTimeOptimization = (1ull << 34), + EOptionValidateCrossStageIO = (1ull << 35), +}; +bool targetHlslFunctionality1 = false; +bool SpvToolsDisassembler = false; +bool SpvToolsValidate = false; +bool NaNClamp = false; +bool stripDebugInfo = false; +bool emitNonSemanticShaderDebugInfo = false; +bool emitNonSemanticShaderDebugSource = false; +bool beQuiet = false; +bool VulkanRulesRelaxed = false; +bool autoSampledTextures = false; + +// +// Return codes from main/exit(). +// +enum TFailCode { + ESuccess = 0, + EFailUsage, + EFailCompile, + EFailLink, + EFailCompilerCreate, + EFailThreadCreate, + EFailLinkerCreate +}; + +// +// Forward declarations. +// +EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true); +void CompileFile(const char* fileName, ShHandle); +void usage(); +char* ReadFileData(const char* fileName); +void FreeFileData(char* data); +void InfoLogMsg(const char* msg, const char* name, const int num); + +// Globally track if any compile or link failure. +std::atomic CompileFailed{0}; +std::atomic LinkFailed{0}; +std::atomic CompileOrLinkFailed{0}; + +// array of unique places to leave the shader names and infologs for the asynchronous compiles +std::vector> WorkItems; + +std::string ConfigFile; + +// +// Parse either a .conf file provided by the user or the default from glslang::DefaultTBuiltInResource +// +void ProcessConfigFile() +{ + if (ConfigFile.size() == 0) + *GetResources() = *GetDefaultResources(); + else { + char* configString = ReadFileData(ConfigFile.c_str()); + DecodeResourceLimits(GetResources(), configString); + FreeFileData(configString); + } +} + +int ReflectOptions = EShReflectionDefault; +std::underlying_type_t Options = EOptionNone; +const char* ExecutableName = nullptr; +const char* binaryFileName = nullptr; +const char* depencyFileName = nullptr; +const char* entryPointName = nullptr; +const char* sourceEntryPointName = nullptr; +const char* shaderStageName = nullptr; +const char* variableName = nullptr; +bool HlslEnable16BitTypes = false; +bool HlslDX9compatible = false; +bool HlslDxPositionW = false; +bool EnhancedMsgs = false; +bool AbsolutePath = false; +bool DumpBuiltinSymbols = false; +std::vector IncludeDirectoryList; + +// Source environment +// (source 'Client' is currently the same as target 'Client') +int ClientInputSemanticsVersion = 100; + +// Target environment +glslang::EShClient Client = glslang::EShClientNone; // will stay EShClientNone if only validating +glslang::EShTargetClientVersion ClientVersion; // not valid until Client is set +glslang::EShTargetLanguage TargetLanguage = glslang::EShTargetNone; +glslang::EShTargetLanguageVersion TargetVersion; // not valid until TargetLanguage is set + +// GLSL version +int GlslVersion = 0; // GLSL version specified on CLI, overrides #version in shader source + +std::vector Processes; // what should be recorded by OpModuleProcessed, or equivalent + +// Per descriptor-set binding base data +typedef std::map TPerSetBaseBinding; + +std::vector> uniformLocationOverrides; +int uniformBase = 0; + +std::array, glslang::EResCount> baseBinding; +std::array, glslang::EResCount> baseBindingForSet; +std::array, EShLangCount> baseResourceSetBinding; + +std::vector> blockStorageOverrides; + +bool setGlobalUniformBlock = false; +std::string globalUniformName; +unsigned int globalUniformBinding; +unsigned int globalUniformSet; + +bool setGlobalBufferBlock = false; +std::string atomicCounterBlockName; +unsigned int atomicCounterBlockSet; + +// Add things like "#define ..." to a preamble to use in the beginning of the shader. +class TPreamble { +public: + TPreamble() { } + + bool isSet() const { return text.size() > 0; } + const char* get() const { return text.c_str(); } + + // #define... + void addDef(std::string def) + { + text.append("#define "); + fixLine(def); + + Processes.push_back("define-macro "); + Processes.back().append(def); + + // The first "=" needs to turn into a space + const size_t equal = def.find_first_of("="); + if (equal != def.npos) + def[equal] = ' '; + + text.append(def); + text.append("\n"); + } + + // #undef... + void addUndef(std::string undef) + { + text.append("#undef "); + fixLine(undef); + + Processes.push_back("undef-macro "); + Processes.back().append(undef); + + text.append(undef); + text.append("\n"); + } + + void addText(std::string preambleText) + { + fixLine(preambleText); + + Processes.push_back("preamble-text"); + Processes.back().append(preambleText); + + text.append(preambleText); + text.append("\n"); + } + +protected: + void fixLine(std::string& line) + { + // Can't go past a newline in the line + const size_t end = line.find_first_of("\n"); + if (end != line.npos) + line = line.substr(0, end); + } + + std::string text; // contents of preamble +}; + +// Track the user's #define and #undef from the command line. +TPreamble UserPreamble; +std::string PreambleString; + +// +// Create the default name for saving a binary if -o is not provided. +// +const char* GetBinaryName(EShLanguage stage) +{ + const char* name; + if (binaryFileName == nullptr) { + switch (stage) { + case EShLangVertex: name = "vert.spv"; break; + case EShLangTessControl: name = "tesc.spv"; break; + case EShLangTessEvaluation: name = "tese.spv"; break; + case EShLangGeometry: name = "geom.spv"; break; + case EShLangFragment: name = "frag.spv"; break; + case EShLangCompute: name = "comp.spv"; break; + case EShLangRayGen: name = "rgen.spv"; break; + case EShLangIntersect: name = "rint.spv"; break; + case EShLangAnyHit: name = "rahit.spv"; break; + case EShLangClosestHit: name = "rchit.spv"; break; + case EShLangMiss: name = "rmiss.spv"; break; + case EShLangCallable: name = "rcall.spv"; break; + case EShLangMesh : name = "mesh.spv"; break; + case EShLangTask : name = "task.spv"; break; + default: name = "unknown"; break; + } + } else + name = binaryFileName; + + return name; +} + +// +// *.conf => this is a config file that can set limits/resources +// +bool SetConfigFile(const std::string& name) +{ + if (name.size() < 5) + return false; + + if (name.compare(name.size() - 5, 5, ".conf") == 0) { + ConfigFile = name; + return true; + } + + return false; +} + +// +// Give error and exit with failure code. +// +void Error(const char* message, const char* detail = nullptr) +{ + fprintf(stderr, "%s: Error: ", ExecutableName); + if (detail != nullptr) + fprintf(stderr, "%s: ", detail); + fprintf(stderr, "%s (use -h for usage)\n", message); + exit(EFailUsage); +} + +// +// Process an optional binding base of one the forms: +// --argname [stage] base // base for stage (if given) or all stages (if not) +// --argname [stage] [base set]... // set/base pairs: set the base for given binding set. + +// Where stage is one of the forms accepted by FindLanguage, and base is an integer +// +void ProcessBindingBase(int& argc, char**& argv, glslang::TResourceType res) +{ + if (argc < 2) + usage(); + + EShLanguage lang = EShLangCount; + int singleBase = 0; + TPerSetBaseBinding perSetBase; + int arg = 1; + + // Parse stage, if given + if (!isdigit(argv[arg][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + lang = FindLanguage(argv[arg++], false); + } + + if ((argc - arg) >= 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) { + // Parse a per-set binding base + do { + const int baseNum = atoi(argv[arg++]); + const int setNum = atoi(argv[arg++]); + perSetBase[setNum] = baseNum; + } while ((argc - arg) >= 2 && isdigit(argv[arg + 0][0]) && isdigit(argv[arg + 1][0])); + } else { + // Parse single binding base + singleBase = atoi(argv[arg++]); + } + + argc -= (arg-1); + argv += (arg-1); + + // Set one or all languages + const int langMin = (lang < EShLangCount) ? lang+0 : 0; + const int langMax = (lang < EShLangCount) ? lang+1 : EShLangCount; + + for (int lang = langMin; lang < langMax; ++lang) { + if (!perSetBase.empty()) + baseBindingForSet[res][lang].insert(perSetBase.begin(), perSetBase.end()); + else + baseBinding[res][lang] = singleBase; + } +} + +void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array, EShLangCount>& base) +{ + if (argc < 2) + usage(); + + if (!isdigit(argv[1][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + // Parse form: --argname stage [regname set base...], or: + // --argname stage set + const EShLanguage lang = FindLanguage(argv[1], false); + + argc--; + argv++; + + while (argc > 1 && argv[1] != nullptr && argv[1][0] != '-') { + base[lang].push_back(argv[1]); + + argc--; + argv++; + } + + // Must have one arg, or a multiple of three (for [regname set binding] triples) + if (base[lang].size() != 1 && (base[lang].size() % 3) != 0) + usage(); + + } else { + // Parse form: --argname set + for (int lang=0; lang>& storage) +{ + if (argc < 3) + usage(); + + glslang::TBlockStorageClass blockStorage = glslang::EbsNone; + + std::string strBacking(argv[2]); + if (strBacking == "uniform") + blockStorage = glslang::EbsUniform; + else if (strBacking == "buffer") + blockStorage = glslang::EbsStorageBuffer; + else if (strBacking == "push_constant") + blockStorage = glslang::EbsPushConstant; + else { + printf("%s: invalid block storage\n", strBacking.c_str()); + usage(); + } + + storage.push_back(std::make_pair(std::string(argv[1]), blockStorage)); + + argc -= 2; + argv += 2; +} + +inline bool isNonDigit(char c) { + // a non-digit character valid in a glsl identifier + return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +// whether string isa valid identifier to be used in glsl +bool isValidIdentifier(const char* str) { + std::string idn(str); + + if (idn.length() == 0) { + return false; + } + + if (idn.length() >= 3 && idn.substr(0, 3) == "gl_") { + // identifiers startin with "gl_" are reserved + return false; + } + + if (!isNonDigit(idn[0])) { + return false; + } + + for (unsigned int i = 1; i < idn.length(); ++i) { + if (!(isdigit(idn[i]) || isNonDigit(idn[i]))) { + return false; + } + } + + return true; +} + +// Process settings for either the global buffer block or global unfirom block +// of the form: +// --argname name set binding +void ProcessGlobalBlockSettings(int& argc, char**& argv, std::string* name, unsigned int* set, unsigned int* binding) +{ + if (argc < 4) + usage(); + + unsigned int curArg = 1; + + assert(name || set || binding); + + if (name) { + if (!isValidIdentifier(argv[curArg])) { + printf("%s: invalid identifier\n", argv[curArg]); + usage(); + } + *name = argv[curArg]; + + curArg++; + } + + if (set) { + errno = 0; + int setVal = static_cast(::strtol(argv[curArg], nullptr, 10)); + if (errno || setVal < 0) { + printf("%s: invalid set\n", argv[curArg]); + usage(); + } + *set = setVal; + + curArg++; + } + + if (binding) { + errno = 0; + int bindingVal = static_cast(::strtol(argv[curArg], nullptr, 10)); + if (errno || bindingVal < 0) { + printf("%s: invalid binding\n", argv[curArg]); + usage(); + } + *binding = bindingVal; + + curArg++; + } + + argc -= (curArg - 1); + argv += (curArg - 1); +} + +// +// Do all command-line argument parsing. This includes building up the work-items +// to be processed later, and saving all the command-line options. +// +// Does not return (it exits) if command-line is fatally flawed. +// +void ProcessArguments(std::vector>& workItems, int argc, char* argv[]) +{ + for (int res = 0; res < glslang::EResCount; ++res) + baseBinding[res].fill(0); + + ExecutableName = argv[0]; + workItems.reserve(argc); + + const auto bumpArg = [&]() { + if (argc > 0) { + argc--; + argv++; + } + }; + + // read a string directly attached to a single-letter option + const auto getStringOperand = [&](const char* desc) { + if (argv[0][2] == 0) { + printf("%s must immediately follow option (no spaces)\n", desc); + exit(EFailUsage); + } + return argv[0] + 2; + }; + + // read a number attached to a single-letter option + const auto getAttachedNumber = [&](const char* desc) { + int num = atoi(argv[0] + 2); + if (num == 0) { + printf("%s: expected attached non-0 number\n", desc); + exit(EFailUsage); + } + return num; + }; + + // minimum needed (without overriding something else) to target Vulkan SPIR-V + const auto setVulkanSpv = []() { + if (Client == glslang::EShClientNone) + ClientVersion = glslang::EShTargetVulkan_1_0; + Client = glslang::EShClientVulkan; + Options |= EOptionSpv; + Options |= EOptionVulkanRules; + Options |= EOptionLinkProgram; + }; + + // minimum needed (without overriding something else) to target OpenGL SPIR-V + const auto setOpenGlSpv = []() { + if (Client == glslang::EShClientNone) + ClientVersion = glslang::EShTargetOpenGL_450; + Client = glslang::EShClientOpenGL; + Options |= EOptionSpv; + Options |= EOptionLinkProgram; + // undo a -H default to Vulkan + Options &= ~EOptionVulkanRules; + }; + + const auto getUniformOverride = [getStringOperand]() { + const char *arg = getStringOperand("-u:"); + const char *split = strchr(arg, ':'); + if (split == nullptr) { + printf("%s: missing location\n", arg); + exit(EFailUsage); + } + errno = 0; + int location = static_cast(::strtol(split + 1, nullptr, 10)); + if (errno) { + printf("%s: invalid location\n", arg); + exit(EFailUsage); + } + return std::make_pair(std::string(arg, split - arg), location); + }; + + for (bumpArg(); argc >= 1; bumpArg()) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case '-': + { + std::string lowerword(argv[0]+2); + std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower); + + // handle --word style options + if (lowerword == "auto-map-bindings" || // synonyms + lowerword == "auto-map-binding" || + lowerword == "amb") { + Options |= EOptionAutoMapBindings; + } else if (lowerword == "auto-map-locations" || // synonyms + lowerword == "aml") { + Options |= EOptionAutoMapLocations; + } else if (lowerword == "uniform-base") { + if (argc <= 1) + Error("no provided", lowerword.c_str()); + uniformBase = static_cast(::strtol(argv[1], nullptr, 10)); + bumpArg(); + break; + } else if (lowerword == "client") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan100") == 0) + setVulkanSpv(); + else if (strcmp(argv[1], "opengl100") == 0) + setOpenGlSpv(); + else + Error("expects vulkan100 or opengl100", lowerword.c_str()); + } else + Error("expects vulkan100 or opengl100", lowerword.c_str()); + bumpArg(); + } else if (lowerword == "define-macro" || + lowerword == "d") { + if (argc > 1) + UserPreamble.addDef(argv[1]); + else + Error("expects ", argv[0]); + bumpArg(); + } else if (lowerword == "dump-builtin-symbols") { + DumpBuiltinSymbols = true; + } else if (lowerword == "entry-point") { + entryPointName = argv[1]; + if (argc <= 1) + Error("no provided", lowerword.c_str()); + bumpArg(); + } else if (lowerword == "flatten-uniform-arrays" || // synonyms + lowerword == "flatten-uniform-array" || + lowerword == "fua") { + Options |= EOptionFlattenUniformArrays; + } else if (lowerword == "glsl-version") { + if (argc > 1) { + if (strcmp(argv[1], "100") == 0) { + GlslVersion = 100; + } else if (strcmp(argv[1], "110") == 0) { + GlslVersion = 110; + } else if (strcmp(argv[1], "120") == 0) { + GlslVersion = 120; + } else if (strcmp(argv[1], "130") == 0) { + GlslVersion = 130; + } else if (strcmp(argv[1], "140") == 0) { + GlslVersion = 140; + } else if (strcmp(argv[1], "150") == 0) { + GlslVersion = 150; + } else if (strcmp(argv[1], "300es") == 0) { + GlslVersion = 300; + } else if (strcmp(argv[1], "310es") == 0) { + GlslVersion = 310; + } else if (strcmp(argv[1], "320es") == 0) { + GlslVersion = 320; + } else if (strcmp(argv[1], "330") == 0) { + GlslVersion = 330; + } else if (strcmp(argv[1], "400") == 0) { + GlslVersion = 400; + } else if (strcmp(argv[1], "410") == 0) { + GlslVersion = 410; + } else if (strcmp(argv[1], "420") == 0) { + GlslVersion = 420; + } else if (strcmp(argv[1], "430") == 0) { + GlslVersion = 430; + } else if (strcmp(argv[1], "440") == 0) { + GlslVersion = 440; + } else if (strcmp(argv[1], "450") == 0) { + GlslVersion = 450; + } else if (strcmp(argv[1], "460") == 0) { + GlslVersion = 460; + } else + Error("--glsl-version expected one of: 100, 110, 120, 130, 140, 150,\n" + "300es, 310es, 320es, 330\n" + "400, 410, 420, 430, 440, 450, 460"); + } + bumpArg(); + } else if (lowerword == "hlsl-offsets") { + Options |= EOptionHlslOffsets; + } else if (lowerword == "hlsl-iomap" || + lowerword == "hlsl-iomapper" || + lowerword == "hlsl-iomapping") { + Options |= EOptionHlslIoMapping; + } else if (lowerword == "hlsl-enable-16bit-types") { + HlslEnable16BitTypes = true; + } else if (lowerword == "hlsl-dx9-compatible") { + HlslDX9compatible = true; + } else if (lowerword == "hlsl-dx-position-w") { + HlslDxPositionW = true; + } else if (lowerword == "enhanced-msgs") { + EnhancedMsgs = true; + } else if (lowerword == "absolute-path") { + AbsolutePath = true; + } else if (lowerword == "auto-sampled-textures") { + autoSampledTextures = true; + } else if (lowerword == "invert-y" || // synonyms + lowerword == "iy") { + Options |= EOptionInvertY; + } else if (lowerword == "keep-uncalled" || // synonyms + lowerword == "ku") { + Options |= EOptionKeepUncalled; + } else if (lowerword == "nan-clamp") { + NaNClamp = true; + } else if (lowerword == "no-storage-format" || // synonyms + lowerword == "nsf") { + Options |= EOptionNoStorageFormat; + } else if (lowerword == "preamble-text" || + lowerword == "p") { + if (argc > 1) + UserPreamble.addText(argv[1]); + else + Error("expects ", argv[0]); + bumpArg(); + } else if (lowerword == "relaxed-errors") { + Options |= EOptionRelaxedErrors; + } else if (lowerword == "reflect-strict-array-suffix") { + ReflectOptions |= EShReflectionStrictArraySuffix; + } else if (lowerword == "reflect-basic-array-suffix") { + ReflectOptions |= EShReflectionBasicArraySuffix; + } else if (lowerword == "reflect-intermediate-io") { + ReflectOptions |= EShReflectionIntermediateIO; + } else if (lowerword == "reflect-separate-buffers") { + ReflectOptions |= EShReflectionSeparateBuffers; + } else if (lowerword == "reflect-all-block-variables") { + ReflectOptions |= EShReflectionAllBlockVariables; + } else if (lowerword == "reflect-unwrap-io-blocks") { + ReflectOptions |= EShReflectionUnwrapIOBlocks; + } else if (lowerword == "reflect-all-io-variables") { + ReflectOptions |= EShReflectionAllIOVariables; + } else if (lowerword == "reflect-shared-std140-ubo") { + ReflectOptions |= EShReflectionSharedStd140UBO; + } else if (lowerword == "reflect-shared-std140-ssbo") { + ReflectOptions |= EShReflectionSharedStd140SSBO; + } else if (lowerword == "resource-set-bindings" || // synonyms + lowerword == "resource-set-binding" || + lowerword == "rsb") { + ProcessResourceSetBindingBase(argc, argv, baseResourceSetBinding); + } else if (lowerword == "set-block-storage" || + lowerword == "sbs") { + ProcessBlockStorage(argc, argv, blockStorageOverrides); + } else if (lowerword == "set-atomic-counter-block" || + lowerword == "sacb") { + ProcessGlobalBlockSettings(argc, argv, &atomicCounterBlockName, &atomicCounterBlockSet, nullptr); + setGlobalBufferBlock = true; + } else if (lowerword == "set-default-uniform-block" || + lowerword == "sdub") { + ProcessGlobalBlockSettings(argc, argv, &globalUniformName, &globalUniformSet, &globalUniformBinding); + setGlobalUniformBlock = true; + } else if (lowerword == "shift-image-bindings" || // synonyms + lowerword == "shift-image-binding" || + lowerword == "sib") { + ProcessBindingBase(argc, argv, glslang::EResImage); + } else if (lowerword == "shift-sampler-bindings" || // synonyms + lowerword == "shift-sampler-binding" || + lowerword == "ssb") { + ProcessBindingBase(argc, argv, glslang::EResSampler); + } else if (lowerword == "shift-uav-bindings" || // synonyms + lowerword == "shift-uav-binding" || + lowerword == "suavb") { + ProcessBindingBase(argc, argv, glslang::EResUav); + } else if (lowerword == "shift-texture-bindings" || // synonyms + lowerword == "shift-texture-binding" || + lowerword == "stb") { + ProcessBindingBase(argc, argv, glslang::EResTexture); + } else if (lowerword == "shift-ubo-bindings" || // synonyms + lowerword == "shift-ubo-binding" || + lowerword == "shift-cbuffer-bindings" || + lowerword == "shift-cbuffer-binding" || + lowerword == "sub" || + lowerword == "scb") { + ProcessBindingBase(argc, argv, glslang::EResUbo); + } else if (lowerword == "shift-ssbo-bindings" || // synonyms + lowerword == "shift-ssbo-binding" || + lowerword == "sbb") { + ProcessBindingBase(argc, argv, glslang::EResSsbo); + } else if (lowerword == "source-entrypoint" || // synonyms + lowerword == "sep") { + if (argc <= 1) + Error("no provided", lowerword.c_str()); + sourceEntryPointName = argv[1]; + bumpArg(); + break; + } else if (lowerword == "spirv-dis") { + SpvToolsDisassembler = true; + } else if (lowerword == "spirv-val") { + SpvToolsValidate = true; + } else if (lowerword == "stdin") { + Options |= EOptionStdin; + shaderStageName = argv[1]; + } else if (lowerword == "suppress-warnings") { + Options |= EOptionSuppressWarnings; + } else if (lowerword == "target-env") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan1.0") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_0; + } else if (strcmp(argv[1], "vulkan1.1") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_1; + } else if (strcmp(argv[1], "vulkan1.2") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_2; + } else if (strcmp(argv[1], "vulkan1.3") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_3; + } else if (strcmp(argv[1], "vulkan1.4") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_4; + } else if (strcmp(argv[1], "opengl") == 0) { + setOpenGlSpv(); + ClientVersion = glslang::EShTargetOpenGL_450; + } else if (strcmp(argv[1], "spirv1.0") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + } else if (strcmp(argv[1], "spirv1.1") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_1; + } else if (strcmp(argv[1], "spirv1.2") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_2; + } else if (strcmp(argv[1], "spirv1.3") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_3; + } else if (strcmp(argv[1], "spirv1.4") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_4; + } else if (strcmp(argv[1], "spirv1.5") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_5; + } else if (strcmp(argv[1], "spirv1.6") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_6; + } else + Error("--target-env expected one of: vulkan1.0, vulkan1.1, vulkan1.2,\n" + "vulkan1.3, opengl, spirv1.0, spirv1.1, spirv1.2, spirv1.3,\n" + "spirv1.4, spirv1.5 or spirv1.6"); + } + bumpArg(); + } else if (lowerword == "undef-macro" || + lowerword == "u") { + if (argc > 1) + UserPreamble.addUndef(argv[1]); + else + Error("expects ", argv[0]); + bumpArg(); + } else if (lowerword == "variable-name" || // synonyms + lowerword == "vn") { + Options |= EOptionOutputHexadecimal; + if (argc <= 1) + Error("no provided", lowerword.c_str()); + variableName = argv[1]; + bumpArg(); + break; + } else if (lowerword == "quiet") { + beQuiet = true; + } else if (lowerword == "depfile") { + if (argc <= 1) + Error("no provided", lowerword.c_str()); + depencyFileName = argv[1]; + bumpArg(); + } else if (lowerword == "version") { + Options |= EOptionDumpVersions; + } else if (lowerword == "no-link") { + Options |= EOptionCompileOnly; + } else if (lowerword == "error-column") { + Options |= EOptionDisplayErrorColumn; + } else if (lowerword == "lto") { + Options |= EOptionLinkTimeOptimization; + } else if (lowerword == "validate-io") { + Options |= EOptionValidateCrossStageIO; + } else if (lowerword == "help") { + usage(); + break; + } else { + Error("unrecognized command-line option", argv[0]); + } + } + break; + case 'C': + Options |= EOptionCascadingErrors; + break; + case 'D': + if (argv[0][2] == 0) + Options |= EOptionReadHlsl; + else + UserPreamble.addDef(getStringOperand("-D")); + break; + case 'u': + uniformLocationOverrides.push_back(getUniformOverride()); + break; + case 'E': + Options |= EOptionOutputPreprocessed; + break; + case 'G': + // OpenGL client + setOpenGlSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-G client input semantics"); + if (ClientInputSemanticsVersion != 100) + Error("unknown client version for -G, should be 100"); + break; + case 'H': + Options |= EOptionHumanReadableSpv; + if ((Options & EOptionSpv) == 0) { + // default to Vulkan + setVulkanSpv(); + } + break; + case 'I': + IncludeDirectoryList.push_back(getStringOperand("-I include path")); + break; + case 'O': + if (argv[0][2] == 'd') + Options |= EOptionOptimizeDisable; + else if (argv[0][2] == 's') +#if ENABLE_OPT + Options |= EOptionOptimizeSize; +#else + Error("-Os not available; optimizer not linked"); +#endif + else + Error("unknown -O option"); + break; + case 'P': + UserPreamble.addText(getStringOperand("-P")); + break; + case 'R': + VulkanRulesRelaxed = true; + break; + case 'S': + if (argc <= 1) + Error("no specified for -S"); + shaderStageName = argv[1]; + bumpArg(); + break; + case 'U': + UserPreamble.addUndef(getStringOperand("-U")); + break; + case 'V': + setVulkanSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-V client input semantics"); + if (ClientInputSemanticsVersion != 100) + Error("unknown client version for -V, should be 100"); + break; + case 'c': + Options |= EOptionDumpConfig; + break; + case 'd': + if (strncmp(&argv[0][1], "dumpversion", strlen(&argv[0][1]) + 1) == 0 || + strncmp(&argv[0][1], "dumpfullversion", strlen(&argv[0][1]) + 1) == 0) + Options |= EOptionDumpBareVersion; + else + Options |= EOptionDefaultDesktop; + break; + case 'e': + entryPointName = argv[1]; + if (argc <= 1) + Error("no provided for -e"); + bumpArg(); + break; + case 'f': + if (strcmp(&argv[0][2], "hlsl_functionality1") == 0) + targetHlslFunctionality1 = true; + else + Error("-f: expected hlsl_functionality1"); + break; + case 'g': + // Override previous -g or -g0 argument + stripDebugInfo = false; + emitNonSemanticShaderDebugInfo = false; + Options &= ~EOptionDebug; + if (argv[0][2] == '0') + stripDebugInfo = true; + else { + Options |= EOptionDebug; + if (argv[0][2] == 'V') { + emitNonSemanticShaderDebugInfo = true; + if (argv[0][3] == 'S') { + emitNonSemanticShaderDebugSource = true; + } else { + emitNonSemanticShaderDebugSource = false; + } + } + } + break; + case 'h': + usage(); + break; + case 'i': + Options |= EOptionIntermediate; + break; + case 'l': + Options |= EOptionLinkProgram; + break; + case 'm': + Options |= EOptionMemoryLeakMode; + break; + case 'o': + if (argc <= 1) + Error("no provided for -o"); + binaryFileName = argv[1]; + bumpArg(); + break; + case 'q': + Options |= EOptionDumpReflection; + break; + case 'r': + Options |= EOptionRelaxedErrors; + break; + case 's': + Options |= EOptionSuppressInfolog; + break; + case 't': + Options |= EOptionMultiThreaded; + break; + case 'v': + Options |= EOptionDumpVersions; + break; + case 'w': + Options |= EOptionSuppressWarnings; + break; + case 'x': + Options |= EOptionOutputHexadecimal; + break; + default: + Error("unrecognized command-line option", argv[0]); + break; + } + } else { + std::string name(argv[0]); + if (! SetConfigFile(name)) { + workItems.push_back(std::unique_ptr(new glslang::TWorkItem(name))); + } + } + } + + // Make sure that -S is always specified if --stdin is specified + if ((Options & EOptionStdin) && shaderStageName == nullptr) + Error("must provide -S when --stdin is given"); + + // Make sure that -E is not specified alongside linking (which includes SPV generation) + // Or things that require linking + if (Options & EOptionOutputPreprocessed) { + if (Options & EOptionLinkProgram) + Error("can't use -E when linking is selected"); + if (Options & EOptionDumpReflection) + Error("reflection requires linking, which can't be used when -E when is selected"); + } + + // reflection requires linking + if ((Options & EOptionDumpReflection) && !(Options & EOptionLinkProgram)) + Error("reflection requires -l for linking"); + + // link time optimization makes no sense unless linking + if ((Options & EOptionLinkTimeOptimization) && !(Options & EOptionLinkProgram)) + Error("link time optimization requires -l for linking"); + + // cross stage IO validation makes no sense unless linking + if ((Options & EOptionValidateCrossStageIO) && !(Options & EOptionLinkProgram)) + Error("cross stage IO validation requires -l for linking"); + + // -o or -x makes no sense if there is no target binary + if (binaryFileName && (Options & EOptionSpv) == 0) + Error("no binary generation requested (e.g., -V)"); + + if ((Options & EOptionFlattenUniformArrays) != 0 && + (Options & EOptionReadHlsl) == 0) + Error("uniform array flattening only valid when compiling HLSL source."); + + if ((Options & EOptionReadHlsl) && (Client == glslang::EShClientOpenGL)) { + Error("Using HLSL input under OpenGL semantics is not currently supported."); + } + + // rationalize client and target language + if (TargetLanguage == glslang::EShTargetNone) { + switch (ClientVersion) { + case glslang::EShTargetVulkan_1_0: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + break; + case glslang::EShTargetVulkan_1_1: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_3; + break; + case glslang::EShTargetVulkan_1_2: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_5; + break; + case glslang::EShTargetVulkan_1_3: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_6; + break; + case glslang::EShTargetVulkan_1_4: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_6; + break; + case glslang::EShTargetOpenGL_450: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + break; + default: + break; + } + } + if (TargetLanguage != glslang::EShTargetNone && Client == glslang::EShClientNone) + Error("To generate SPIR-V, also specify client semantics. See -G and -V."); +} + +// +// Translate the meaningful subset of command-line options to parser-behavior options. +// +void SetMessageOptions(EShMessages& messages) +{ + if (Options & EOptionRelaxedErrors) + messages = (EShMessages)(messages | EShMsgRelaxedErrors); + if (Options & EOptionIntermediate) + messages = (EShMessages)(messages | EShMsgAST); + if (Options & EOptionSuppressWarnings) + messages = (EShMessages)(messages | EShMsgSuppressWarnings); + if (Options & EOptionSpv) + messages = (EShMessages)(messages | EShMsgSpvRules); + if (Options & EOptionVulkanRules) + messages = (EShMessages)(messages | EShMsgVulkanRules); + if (Options & EOptionOutputPreprocessed) + messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); + if (Options & EOptionReadHlsl) + messages = (EShMessages)(messages | EShMsgReadHlsl); + if (Options & EOptionCascadingErrors) + messages = (EShMessages)(messages | EShMsgCascadingErrors); + if (Options & EOptionKeepUncalled) + messages = (EShMessages)(messages | EShMsgKeepUncalled); + if (Options & EOptionHlslOffsets) + messages = (EShMessages)(messages | EShMsgHlslOffsets); + if (Options & EOptionDebug) + messages = (EShMessages)(messages | EShMsgDebugInfo); + if (HlslEnable16BitTypes) + messages = (EShMessages)(messages | EShMsgHlslEnable16BitTypes); + if ((Options & EOptionOptimizeDisable) || !ENABLE_OPT) + messages = (EShMessages)(messages | EShMsgHlslLegalization); + if (HlslDX9compatible) + messages = (EShMessages)(messages | EShMsgHlslDX9Compatible); + if (DumpBuiltinSymbols) + messages = (EShMessages)(messages | EShMsgBuiltinSymbolTable); + if (EnhancedMsgs) + messages = (EShMessages)(messages | EShMsgEnhanced); + if (AbsolutePath) + messages = (EShMessages)(messages | EShMsgAbsolutePath); + if (Options & EOptionDisplayErrorColumn) + messages = (EShMessages)(messages | EShMsgDisplayErrorColumn); + if (Options & EOptionLinkTimeOptimization) + messages = (EShMessages)(messages | EShMsgLinkTimeOptimization); + if (Options & EOptionValidateCrossStageIO) + messages = (EShMessages)(messages | EShMsgValidateCrossStageIO); +} + +// +// Thread entry point, for non-linking asynchronous mode. +// +void CompileShaders(glslang::TWorklist& worklist) +{ + if (Options & EOptionDebug) + Error("cannot generate debug information unless linking to generate code"); + + // NOTE: TWorkList::remove is thread-safe + glslang::TWorkItem* workItem; + if (Options & EOptionStdin) { + if (worklist.remove(workItem)) { + ShHandle compiler = ShConstructCompiler(FindLanguage("stdin"), 0); + if (compiler == nullptr) + return; + + CompileFile("stdin", compiler); + + if (! (Options & EOptionSuppressInfolog)) + workItem->results = ShGetInfoLog(compiler); + + ShDestruct(compiler); + } + } else { + while (worklist.remove(workItem)) { + ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), 0); + if (compiler == nullptr) + return; + + + CompileFile(workItem->name.c_str(), compiler); + + if (! (Options & EOptionSuppressInfolog)) + workItem->results = ShGetInfoLog(compiler); + + ShDestruct(compiler); + } + } +} + +// Outputs the given string, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void PutsIfNonEmpty(const char* str) +{ + if (str && str[0]) { + puts(str); + } +} + +// Outputs the given string to stderr, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void StderrIfNonEmpty(const char* str) +{ + if (str && str[0]) + fprintf(stderr, "%s\n", str); +} + +// Simple bundling of what makes a compilation unit for ease in passing around, +// and separation of handling file IO versus API (programmatic) compilation. +struct ShaderCompUnit { + EShLanguage stage; + static const int maxCount = 1; + int count; // live number of strings/names + const char* text[maxCount]; // memory owned/managed externally + std::string fileName[maxCount]; // hold's the memory, but... + const char* fileNameList[maxCount]; // downstream interface wants pointers + + ShaderCompUnit(EShLanguage stage) : stage(stage), count(0) { } + + ShaderCompUnit(const ShaderCompUnit& rhs) + { + stage = rhs.stage; + count = rhs.count; + for (int i = 0; i < count; ++i) { + fileName[i] = rhs.fileName[i]; + text[i] = rhs.text[i]; + fileNameList[i] = rhs.fileName[i].c_str(); + } + } + + void addString(std::string& ifileName, const char* itext) + { + assert(count < maxCount); + fileName[count] = ifileName; + text[count] = itext; + fileNameList[count] = fileName[count].c_str(); + ++count; + } +}; + +// Writes a string into a depfile, escaping some special characters following the Makefile rules. +static void writeEscapedDepString(std::ofstream& file, const std::string& str) +{ + for (char c : str) { + switch (c) { + case ' ': + case ':': + case '#': + case '[': + case ']': + case '\\': + file << '\\'; + break; + case '$': + file << '$'; + break; + } + file << c; + } +} + +// Writes a depfile similar to gcc -MMD foo.c +bool writeDepFile(std::string depfile, std::vector& binaryFiles, const std::vector& sources) +{ + std::ofstream file(depfile); + if (file.fail()) + return false; + + for (auto binaryFile = binaryFiles.begin(); binaryFile != binaryFiles.end(); binaryFile++) { + writeEscapedDepString(file, *binaryFile); + file << ":"; + for (auto sourceFile = sources.begin(); sourceFile != sources.end(); sourceFile++) { + file << " "; + writeEscapedDepString(file, *sourceFile); + } + file << std::endl; + } + return true; +} + +// +// For linking mode: Will independently parse each compilation unit, but then put them +// in the same program and link them together, making at most one linked module per +// pipeline stage. +// +// Uses the new C++ interface instead of the old handle-based interface. +// + +void CompileAndLinkShaderUnits(std::vector compUnits) +{ + // keep track of what to free + std::list shaders; + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + DirStackFileIncluder includer; + std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) { + includer.pushExternalLocalDirectory(dir); }); + + std::vector sources; + + // + // Per-shader processing... + // + + glslang::TProgram& program = *new glslang::TProgram; + const bool compileOnly = (Options & EOptionCompileOnly) != 0; + for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { + const auto &compUnit = *it; + for (int i = 0; i < compUnit.count; i++) { + sources.push_back(compUnit.fileNameList[i]); + } + glslang::TShader* shader = new glslang::TShader(compUnit.stage); + shader->setStringsWithLengthsAndNames(compUnit.text, nullptr, compUnit.fileNameList, compUnit.count); + if (entryPointName) + shader->setEntryPoint(entryPointName); + if (sourceEntryPointName) { + if (entryPointName == nullptr) + printf("Warning: Changing source entry point name without setting an entry-point name.\n" + "Use '-e '.\n"); + shader->setSourceEntryPoint(sourceEntryPointName); + } + + if (compileOnly) + shader->setCompileOnly(); + + shader->setOverrideVersion(GlslVersion); + + std::string intrinsicString = getIntrinsic(compUnit.text, compUnit.count); + + PreambleString = ""; + if (UserPreamble.isSet()) + PreambleString.append(UserPreamble.get()); + + if (!intrinsicString.empty()) + PreambleString.append(intrinsicString); + + shader->setPreamble(PreambleString.c_str()); + shader->addProcesses(Processes); + + // Set IO mapper binding shift values + for (int r = 0; r < glslang::EResCount; ++r) { + const glslang::TResourceType res = glslang::TResourceType(r); + + // Set base bindings + shader->setShiftBinding(res, baseBinding[res][compUnit.stage]); + + // Set bindings for particular resource sets + // TODO: use a range based for loop here, when available in all environments. + for (auto i = baseBindingForSet[res][compUnit.stage].begin(); + i != baseBindingForSet[res][compUnit.stage].end(); ++i) + shader->setShiftBindingForSet(res, i->second, i->first); + } + shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); + shader->setResourceSetBinding(baseResourceSetBinding[compUnit.stage]); + + if (autoSampledTextures) + shader->setTextureSamplerTransformMode(EShTexSampTransUpgradeTextureRemoveSampler); + + if (Options & EOptionAutoMapBindings) + shader->setAutoMapBindings(true); + + if (Options & EOptionAutoMapLocations) + shader->setAutoMapLocations(true); + + for (auto& uniOverride : uniformLocationOverrides) { + shader->addUniformLocationOverride(uniOverride.first.c_str(), + uniOverride.second); + } + + shader->setUniformLocationBase(uniformBase); + + if (VulkanRulesRelaxed) { + for (auto& storageOverride : blockStorageOverrides) { + shader->addBlockStorageOverride(storageOverride.first.c_str(), + storageOverride.second); + } + + if (setGlobalBufferBlock) { + shader->setAtomicCounterBlockName(atomicCounterBlockName.c_str()); + shader->setAtomicCounterBlockSet(atomicCounterBlockSet); + } + + if (setGlobalUniformBlock) { + shader->setGlobalUniformBlockName(globalUniformName.c_str()); + shader->setGlobalUniformSet(globalUniformSet); + shader->setGlobalUniformBinding(globalUniformBinding); + } + } + + shader->setNanMinMaxClamp(NaNClamp); + +#ifdef ENABLE_HLSL + shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); + if (Options & EOptionHlslIoMapping) + shader->setHlslIoMapping(true); +#endif + + if (Options & EOptionInvertY) + shader->setInvertY(true); + + if (HlslDxPositionW) + shader->setDxPositionW(true); + + if (EnhancedMsgs) + shader->setEnhancedMsgs(); + + if (emitNonSemanticShaderDebugInfo) + shader->setDebugInfo(true); + + // Set up the environment, some subsettings take precedence over earlier + // ways of setting things. + if (Options & EOptionSpv) { + shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + compUnit.stage, Client, ClientInputSemanticsVersion); + shader->setEnvClient(Client, ClientVersion); + shader->setEnvTarget(TargetLanguage, TargetVersion); +#ifdef ENABLE_HLSL + if (targetHlslFunctionality1) + shader->setEnvTargetHlslFunctionality1(); +#endif + if (VulkanRulesRelaxed) + shader->setEnvInputVulkanRulesRelaxed(); + } + + shaders.push_back(shader); + + const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; + + if (Options & EOptionOutputPreprocessed) { + std::string str; + if (shader->preprocess(GetResources(), defaultVersion, ENoProfile, false, false, messages, &str, includer)) { + PutsIfNonEmpty(str.c_str()); + } else { + CompileFailed = 1; + } + StderrIfNonEmpty(shader->getInfoLog()); + StderrIfNonEmpty(shader->getInfoDebugLog()); + continue; + } + + if (! shader->parse(GetResources(), defaultVersion, false, messages, includer)) + CompileFailed = 1; + + if (!compileOnly) + program.addShader(shader); + + if (! (Options & EOptionSuppressInfolog) && + ! (Options & EOptionMemoryLeakMode)) { + if (!beQuiet) + PutsIfNonEmpty(compUnit.fileName[0].c_str()); + PutsIfNonEmpty(shader->getInfoLog()); + PutsIfNonEmpty(shader->getInfoDebugLog()); + } + } + + // + // Program-level processing... + // + + if (!compileOnly) { + // Link + if (!(Options & EOptionOutputPreprocessed) && !program.link(messages)) + LinkFailed = true; + + // Map IO + if (Options & EOptionSpv) { + if (!program.mapIO()) + LinkFailed = true; + } + + // Report + if (!(Options & EOptionSuppressInfolog) && !(Options & EOptionMemoryLeakMode)) { + PutsIfNonEmpty(program.getInfoLog()); + PutsIfNonEmpty(program.getInfoDebugLog()); + } + + // Reflect + if (Options & EOptionDumpReflection) { + program.buildReflection(ReflectOptions); + program.dumpReflection(); + } + } + + std::vector outputFiles; + + // Dump SPIR-V + if (Options & EOptionSpv) { +#ifdef ENABLE_SPIRV + CompileOrLinkFailed.fetch_or(CompileFailed); + CompileOrLinkFailed.fetch_or(LinkFailed); + if (static_cast(CompileOrLinkFailed.load())) + printf("SPIR-V is not generated for failed compile or link\n"); + else { + std::vector intermediates; + if (!compileOnly) { + for (int stage = 0; stage < EShLangCount; ++stage) { + if (auto* i = program.getIntermediate((EShLanguage)stage)) { + intermediates.emplace_back(i); + } + } + } else { + for (const auto* shader : shaders) { + if (auto* i = shader->getIntermediate()) { + intermediates.emplace_back(i); + } + } + } + for (auto* intermediate : intermediates) { + std::vector spirv; + spv::SpvBuildLogger logger; + glslang::SpvOptions spvOptions; + if (Options & EOptionDebug) { + spvOptions.generateDebugInfo = true; + if (emitNonSemanticShaderDebugInfo) { + spvOptions.emitNonSemanticShaderDebugInfo = true; + if (emitNonSemanticShaderDebugSource) { + spvOptions.emitNonSemanticShaderDebugSource = true; + } + } + } else if (stripDebugInfo) + spvOptions.stripDebugInfo = true; + spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0; + spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0; + spvOptions.disassemble = SpvToolsDisassembler; + spvOptions.validate = SpvToolsValidate; + spvOptions.compileOnly = compileOnly; + glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions); + + // Dump the spv to a file or stdout, etc., but only if not doing + // memory/perf testing, as it's not internal to programmatic use. + if (!(Options & EOptionMemoryLeakMode)) { + printf("%s", logger.getAllMessages().c_str()); + const auto filename = GetBinaryName(intermediate->getStage()); + if (Options & EOptionOutputHexadecimal) { + if (!glslang::OutputSpvHex(spirv, filename, variableName)) + exit(EFailUsage); + } else { + if (!glslang::OutputSpvBin(spirv, filename)) + exit(EFailUsage); + } + + outputFiles.push_back(filename); + if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv)) + spv::Disassemble(std::cout, spirv); + } + } + } +#else + Error("This configuration of glslang does not have SPIR-V support"); +#endif + } + + CompileOrLinkFailed.fetch_or(CompileFailed); + CompileOrLinkFailed.fetch_or(LinkFailed); + if (depencyFileName && !static_cast(CompileOrLinkFailed.load())) { + std::set includedFiles = includer.getIncludedFiles(); + sources.insert(sources.end(), includedFiles.begin(), includedFiles.end()); + + writeDepFile(depencyFileName, outputFiles, sources); + } + + // Free everything up, program has to go before the shaders + // because it might have merged stuff from the shaders, and + // the stuff from the shaders has to have its destructors called + // before the pools holding the memory in the shaders is freed. + delete &program; + while (shaders.size() > 0) { + delete shaders.back(); + shaders.pop_back(); + } +} + +// +// Do file IO part of compile and link, handing off the pure +// API/programmatic mode to CompileAndLinkShaderUnits(), which can +// be put in a loop for testing memory footprint and performance. +// +// This is just for linking mode: meaning all the shaders will be put into the +// the same program linked together. +// +// This means there are a limited number of work items (not multi-threading mode) +// and that the point is testing at the linking level. Hence, to enable +// performance and memory testing, the actual compile/link can be put in +// a loop, independent of processing the work items and file IO. +// +void CompileAndLinkShaderFiles(glslang::TWorklist& Worklist) +{ + std::vector compUnits; + + // If this is using stdin, we can't really detect multiple different file + // units by input type. We need to assume that we're just being given one + // file of a certain type. + if ((Options & EOptionStdin) != 0) { + ShaderCompUnit compUnit(FindLanguage("stdin")); + std::istreambuf_iterator begin(std::cin), end; + std::string tempString(begin, end); + char* fileText = strdup(tempString.c_str()); + std::string fileName = "stdin"; + compUnit.addString(fileName, fileText); + compUnits.push_back(compUnit); + } else { + // Transfer all the work items from to a simple list of + // of compilation units. (We don't care about the thread + // work-item distribution properties in this path, which + // is okay due to the limited number of shaders, know since + // they are all getting linked together.) + glslang::TWorkItem* workItem; + while (Worklist.remove(workItem)) { + ShaderCompUnit compUnit(FindLanguage(workItem->name)); + char* fileText = ReadFileData(workItem->name.c_str()); + if (fileText == nullptr) + usage(); + compUnit.addString(workItem->name, fileText); + compUnits.push_back(compUnit); + } + } + + // Actual call to programmatic processing of compile and link, + // in a loop for testing memory and performance. This part contains + // all the perf/memory that a programmatic consumer will care about. + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) + CompileAndLinkShaderUnits(compUnits); + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + // free memory from ReadFileData, which got stored in a const char* + // as the first string above + for (auto it = compUnits.begin(); it != compUnits.end(); ++it) + FreeFileData(const_cast(it->text[0])); +} + +int singleMain() +{ + glslang::TWorklist workList; + std::for_each(WorkItems.begin(), WorkItems.end(), [&workList](std::unique_ptr& item) { + assert(item); + workList.add(item.get()); + }); + + if (Options & EOptionDumpConfig) { + printf("%s", GetDefaultTBuiltInResourceString().c_str()); + if (workList.empty()) + return ESuccess; + } + + if (Options & EOptionDumpBareVersion) { + int spirvGeneratorVersion = 0; +#ifdef ENABLE_SPIRV + spirvGeneratorVersion = glslang::GetSpirvGeneratorVersion(); +#endif + printf("%d:%d.%d.%d%s\n", spirvGeneratorVersion, GLSLANG_VERSION_MAJOR, GLSLANG_VERSION_MINOR, + GLSLANG_VERSION_PATCH, GLSLANG_VERSION_FLAVOR); + if (workList.empty()) + return ESuccess; + } else if (Options & EOptionDumpVersions) { + int spirvGeneratorVersion = 0; +#ifdef ENABLE_SPIRV + spirvGeneratorVersion = glslang::GetSpirvGeneratorVersion(); +#endif + printf("Glslang Version: %d:%d.%d.%d%s\n", spirvGeneratorVersion, GLSLANG_VERSION_MAJOR, + GLSLANG_VERSION_MINOR, GLSLANG_VERSION_PATCH, GLSLANG_VERSION_FLAVOR); + printf("ESSL Version: %s\n", glslang::GetEsslVersionString()); + printf("GLSL Version: %s\n", glslang::GetGlslVersionString()); + std::string spirvVersion; +#if ENABLE_SPIRV + glslang::GetSpirvVersion(spirvVersion); +#endif + printf("SPIR-V Version %s\n", spirvVersion.c_str()); + printf("GLSL.std.450 Version %d, Revision %d\n", GLSLstd450Version, GLSLstd450Revision); + printf("Khronos Tool ID %d\n", glslang::GetKhronosToolId()); + printf("SPIR-V Generator Version %d\n", spirvGeneratorVersion); + printf("GL_KHR_vulkan_glsl version %d\n", 100); + printf("ARB_GL_gl_spirv version %d\n", 100); + if (workList.empty()) + return ESuccess; + } + + if (workList.empty() && ((Options & EOptionStdin) == 0)) { + usage(); + } + + if (Options & EOptionStdin) { + WorkItems.push_back(std::unique_ptr{new glslang::TWorkItem("stdin")}); + workList.add(WorkItems.back().get()); + } + + ProcessConfigFile(); + + if ((Options & EOptionReadHlsl) && !((Options & EOptionOutputPreprocessed) || (Options & EOptionSpv))) + Error("HLSL requires SPIR-V code generation (or preprocessing only)"); + + // + // Two modes: + // 1) linking all arguments together, single-threaded, new C++ interface + // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface + // + if (Options & (EOptionLinkProgram | EOptionOutputPreprocessed)) { + glslang::InitializeProcess(); + glslang::InitializeProcess(); // also test reference counting of users + glslang::InitializeProcess(); // also test reference counting of users + glslang::FinalizeProcess(); // also test reference counting of users + glslang::FinalizeProcess(); // also test reference counting of users + CompileAndLinkShaderFiles(workList); + glslang::FinalizeProcess(); + } else { + ShInitialize(); + ShInitialize(); // also test reference counting of users + ShFinalize(); // also test reference counting of users + + bool printShaderNames = workList.size() > 1; + + if (Options & EOptionMultiThreaded) { + std::array threads; + for (unsigned int t = 0; t < threads.size(); ++t) { + threads[t] = std::thread(CompileShaders, std::ref(workList)); + if (threads[t].get_id() == std::thread::id()) { + fprintf(stderr, "Failed to create thread\n"); + return EFailThreadCreate; + } + } + + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + } else + CompileShaders(workList); + + // Print out all the resulting infologs + for (size_t w = 0; w < WorkItems.size(); ++w) { + if (WorkItems[w]) { + if (printShaderNames || WorkItems[w]->results.size() > 0) + PutsIfNonEmpty(WorkItems[w]->name.c_str()); + PutsIfNonEmpty(WorkItems[w]->results.c_str()); + } + } + + ShFinalize(); + } + + if (CompileFailed.load()) + return EFailCompile; + if (LinkFailed.load()) + return EFailLink; + + return 0; +} + +int C_DECL main(int argc, char* argv[]) +{ + ProcessArguments(WorkItems, argc, argv); + + int ret = 0; + + // Loop over the entire init/finalize cycle to watch memory changes + const int iterations = 1; + if (iterations > 1) + glslang::OS_DumpMemoryCounters(); + for (int i = 0; i < iterations; ++i) { + ret = singleMain(); + if (iterations > 1) + glslang::OS_DumpMemoryCounters(); + } + + return ret; +} + +// +// Deduce the language from the filename. Files must end in one of the +// following extensions: +// +// .vert = vertex +// .tesc = tessellation control +// .tese = tessellation evaluation +// .geom = geometry +// .frag = fragment +// .comp = compute +// .rgen = ray generation +// .rint = ray intersection +// .rahit = ray any hit +// .rchit = ray closest hit +// .rmiss = ray miss +// .rcall = ray callable +// .mesh = mesh +// .task = task +// Additionally, the file names may end in ..glsl and ..hlsl +// where is one of the stages listed above. +// +EShLanguage FindLanguage(const std::string& name, bool parseStageName) +{ + std::string stageName; + if (shaderStageName) + stageName = shaderStageName; + else if (parseStageName) { + // Note: "first" extension means "first from the end", i.e. + // if the file is named foo.vert.glsl, then "glsl" is first, + // "vert" is second. + size_t firstExtStart = name.find_last_of("."); + bool hasFirstExt = firstExtStart != std::string::npos; + size_t secondExtStart = hasFirstExt ? name.find_last_of(".", firstExtStart - 1) : std::string::npos; + bool hasSecondExt = secondExtStart != std::string::npos; + std::string firstExt = name.substr(firstExtStart + 1, std::string::npos); + bool usesUnifiedExt = hasFirstExt && (firstExt == "glsl" || firstExt == "hlsl"); + if (usesUnifiedExt && firstExt == "hlsl") + Options |= EOptionReadHlsl; + if (hasFirstExt && !usesUnifiedExt) + stageName = firstExt; + else if (usesUnifiedExt && hasSecondExt) + stageName = name.substr(secondExtStart + 1, firstExtStart - secondExtStart - 1); + else { + usage(); + return EShLangVertex; + } + } else + stageName = name; + + if (stageName == "vert") + return EShLangVertex; + else if (stageName == "tesc") + return EShLangTessControl; + else if (stageName == "tese") + return EShLangTessEvaluation; + else if (stageName == "geom") + return EShLangGeometry; + else if (stageName == "frag") + return EShLangFragment; + else if (stageName == "comp") + return EShLangCompute; + else if (stageName == "rgen") + return EShLangRayGen; + else if (stageName == "rint") + return EShLangIntersect; + else if (stageName == "rahit") + return EShLangAnyHit; + else if (stageName == "rchit") + return EShLangClosestHit; + else if (stageName == "rmiss") + return EShLangMiss; + else if (stageName == "rcall") + return EShLangCallable; + else if (stageName == "mesh") + return EShLangMesh; + else if (stageName == "task") + return EShLangTask; + + usage(); + return EShLangVertex; +} + +// +// Read a file's data into a string, and compile it using the old interface ShCompile, +// for non-linkable results. +// +void CompileFile(const char* fileName, ShHandle compiler) +{ + int ret = 0; + char* shaderString; + if ((Options & EOptionStdin) != 0) { + std::istreambuf_iterator begin(std::cin), end; + std::string tempString(begin, end); + shaderString = strdup(tempString.c_str()); + } else { + shaderString = ReadFileData(fileName); + } + + // move to length-based strings, rather than null-terminated strings + int* lengths = new int[1]; + lengths[0] = (int)strlen(shaderString); + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + if (UserPreamble.isSet()) + Error("-D, -U and -P options require -l (linking)\n"); + + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { + // ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + ret = ShCompile(compiler, &shaderString, 1, nullptr, EShOptNone, GetResources(), 0, + (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages, fileName); + // const char* multi[12] = { "# ve", "rsion", " 300 e", "s", "\n#err", + // "or should be l", "ine 1", "string 5\n", "float glo", "bal", + // ";\n#error should be line 2\n void main() {", "global = 2.3;}" }; + // const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" }; + // ret = ShCompile(compiler, multi, 7, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + } + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + delete [] lengths; + FreeFileData(shaderString); + + if (ret == 0) + CompileFailed = true; +} + +// +// print usage to stdout +// +void usage() +{ + printf("Usage: glslang [option]... [file]...\n" + "\n" + "'file' can end in . for auto-stage classification, where is:\n" + " .conf to provide a config file that replaces the default configuration\n" + " (see -c option below for generating a template)\n" + " .vert for a vertex shader\n" + " .tesc for a tessellation control shader\n" + " .tese for a tessellation evaluation shader\n" + " .geom for a geometry shader\n" + " .frag for a fragment shader\n" + " .comp for a compute shader\n" + " .mesh for a mesh shader\n" + " .task for a task shader\n" + " .rgen for a ray generation shader\n" + " .rint for a ray intersection shader\n" + " .rahit for a ray any hit shader\n" + " .rchit for a ray closest hit shader\n" + " .rmiss for a ray miss shader\n" + " .rcall for a ray callable shader\n" + " .glsl for .vert.glsl, .tesc.glsl, ..., .comp.glsl compound suffixes\n" + " .hlsl for .vert.hlsl, .tesc.hlsl, ..., .comp.hlsl compound suffixes\n" + "\n" + "Options:\n" + " -C cascading errors; risk crash from accumulation of error recoveries\n" + " -D input is HLSL (this is the default when any suffix is .hlsl)\n" + " -D | --define-macro | --D \n" + " define a pre-processor macro\n" + " -E print pre-processed GLSL; cannot be used with -l;\n" + " errors will appear on stderr\n" + " -G[ver] create SPIR-V binary, under OpenGL semantics; turns on -l;\n" + " default file name is .spv (-o overrides this);\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define GL_SPIRV ver;\n" + " '--client opengl100' is the same as -G100;\n" + " a '--target-env' for OpenGL will also imply '-G';\n" + " currently only supports GLSL\n" + " -H print human readable form of SPIR-V; turns on -V\n" + " -I add dir to the include search path; includer's directory\n" + " is searched first, followed by left-to-right order of -I\n" + " -Od disables optimization; may cause illegal SPIR-V for HLSL\n" + " -Os optimizes SPIR-V to minimize size\n" + " -P | --preamble-text | --P \n" + " inject custom preamble text, which is treated as if it\n" + " appeared immediately after the version declaration (if any).\n" + " -R use relaxed verification rules for generating Vulkan SPIR-V,\n" + " allowing the use of default uniforms, atomic_uints, and\n" + " gl_VertexID and gl_InstanceID keywords.\n" + " -S uses specified stage rather than parsing the file extension\n" + " choices for are vert, tesc, tese, geom, frag, or comp\n" + " -U | --undef-macro | --U \n" + " undefine a pre-processor macro\n" + " -V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;\n" + " default file name is .spv (-o overrides this)\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define VULKAN ver\n" + " '--client vulkan100' is the same as -V100\n" + " a '--target-env' for Vulkan will also imply '-V'\n" + " -c configuration dump;\n" + " creates the default configuration file (redirect to a .conf file)\n" + " -d default to desktop (#version 110) when there is no shader #version\n" + " (default is ES version 100)\n" + " -e | --entry-point \n" + " specify as the entry-point function name\n" + " -f{hlsl_functionality1}\n" + " 'hlsl_functionality1' enables use of the\n" + " SPV_GOOGLE_hlsl_functionality1 extension\n" + " -g generate debug information\n" + " -g0 strip debug information\n" + " -gV generate nonsemantic shader debug information\n" + " -gVS generate nonsemantic shader debug information with source\n" + " -h print this usage message\n" + " -i intermediate tree (glslang AST) is printed out\n" + " -l link all input files together to form a single module\n" + " -m memory leak mode\n" + " -o save binary to , requires a binary option (e.g., -V)\n" + " -q dump reflection query database; requires -l for linking\n" + " -r | --relaxed-errors" + " relaxed GLSL semantic error-checking mode\n" + " -s silence syntax and semantic error reporting\n" + " -t multi-threaded mode\n" + " -v | --version\n" + " print version strings\n" + " -w | --suppress-warnings\n" + " suppress GLSL warnings, except as required by \"#extension : warn\"\n" + " -x save binary output as text-based 32-bit hexadecimal numbers\n" + " -u: specify a uniform location override for --aml\n" + " --uniform-base set a base to use for generated uniform locations\n" + " --auto-map-bindings | --amb automatically bind uniform variables\n" + " without explicit bindings\n" + " --auto-map-locations | --aml automatically locate input/output lacking\n" + " 'location' (fragile, not cross stage)\n" + " --absolute-path Prints absolute path for messages\n" + " --auto-sampled-textures Removes sampler variables and converts\n" + " existing textures to sampled textures\n" + " --client {vulkan|opengl} see -V and -G\n" + " --depfile writes depfile for build systems\n" + " --dump-builtin-symbols prints builtin symbol table prior each compile\n" + " -dumpfullversion | -dumpversion print bare major.minor.patchlevel\n" + " --flatten-uniform-arrays | --fua flatten uniform texture/sampler arrays to\n" + " scalars\n" + " --glsl-version {100 | 110 | 120 | 130 | 140 | 150 |\n" + " 300es | 310es | 320es | 330\n" + " 400 | 410 | 420 | 430 | 440 | 450 | 460}\n" + " set GLSL version, overrides #version\n" + " in shader sourcen\n" + " --hlsl-offsets allow block offsets to follow HLSL rules\n" + " works independently of source language\n" + " --hlsl-iomap perform IO mapping in HLSL register space\n" + " --hlsl-enable-16bit-types allow 16-bit types in SPIR-V for HLSL\n" + " --hlsl-dx9-compatible interprets sampler declarations as a\n" + " texture/sampler combo like DirectX9 would,\n" + " and recognizes DirectX9-specific semantics\n" + " --hlsl-dx-position-w W component of SV_Position in HLSL fragment\n" + " shaders compatible with DirectX\n" + " --invert-y | --iy invert position.Y output in vertex shader\n" + " --enhanced-msgs print more readable error messages (GLSL only)\n" + " --error-column display the column of the error along the line\n" + " --keep-uncalled | --ku don't eliminate uncalled functions\n" + " --nan-clamp favor non-NaN operand in min, max, and clamp\n" + " --no-storage-format | --nsf use Unknown image format\n" + " --quiet do not print anything to stdout, unless\n" + " requested by another option\n" + " --reflect-strict-array-suffix use strict array suffix rules when\n" + " reflecting\n" + " --reflect-basic-array-suffix arrays of basic types will have trailing [0]\n" + " --reflect-intermediate-io reflection includes inputs/outputs of linked\n" + " shaders rather than just vertex/fragment\n" + " --reflect-separate-buffers reflect buffer variables and blocks\n" + " separately to uniforms\n" + " --reflect-all-block-variables reflect all variables in blocks, whether\n" + " inactive or active\n" + " --reflect-unwrap-io-blocks unwrap input/output blocks the same as\n" + " uniform blocks\n" + " --resource-set-binding [stage] name set binding\n" + " set descriptor set and binding for\n" + " individual resources\n" + " --resource-set-binding [stage] set\n" + " set descriptor set for all resources\n" + " --rsb synonym for --resource-set-binding\n" + " --set-block-backing name {uniform|buffer|push_constant}\n" + " changes the backing type of a uniform, buffer,\n" + " or push_constant block declared in\n" + " in the program, when using -R option.\n" + " This can be used to change the backing\n" + " for existing blocks as well as implicit ones\n" + " such as 'gl_DefaultUniformBlock'.\n" + " --sbs synonym for set-block-storage\n" + " --set-atomic-counter-block name set\n" + " set name, and descriptor set for\n" + " atomic counter blocks, with -R opt\n" + " --sacb synonym for set-atomic-counter-block\n" + " --set-default-uniform-block name set binding\n" + " set name, descriptor set, and binding for\n" + " global default-uniform-block, with -R opt\n" + " --sdub synonym for set-default-uniform-block\n" + " --shift-image-binding [stage] num\n" + " base binding number for images (uav)\n" + " --shift-image-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sib synonym for --shift-image-binding\n" + " --shift-sampler-binding [stage] num\n" + " base binding number for samplers\n" + " --shift-sampler-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --ssb synonym for --shift-sampler-binding\n" + " --shift-ssbo-binding [stage] num base binding number for SSBOs\n" + " --shift-ssbo-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sbb synonym for --shift-ssbo-binding\n" + " --shift-texture-binding [stage] num\n" + " base binding number for textures\n" + " --shift-texture-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --stb synonym for --shift-texture-binding\n" + " --shift-uav-binding [stage] num base binding number for UAVs\n" + " --shift-uav-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --suavb synonym for --shift-uav-binding\n" + " --shift-UBO-binding [stage] num base binding number for UBOs\n" + " --shift-UBO-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sub synonym for --shift-UBO-binding\n" + " --shift-cbuffer-binding | --scb synonyms for --shift-UBO-binding\n" + " --spirv-dis output standard-form disassembly; works only\n" + " when a SPIR-V generation option is also used\n" + " --spirv-val execute the SPIRV-Tools validator\n" + " --source-entrypoint the given shader source function is\n" + " renamed to be the given in -e\n" + " --sep synonym for --source-entrypoint\n" + " --stdin read from stdin instead of from a file;\n" + " requires providing the shader stage using -S\n" + " --target-env {vulkan1.0 | vulkan1.1 | vulkan1.2 | vulkan1.3 | opengl |\n" + " spirv1.0 | spirv1.1 | spirv1.2 | spirv1.3 | spirv1.4 |\n" + " spirv1.5 | spirv1.6}\n" + " Set the execution environment that the\n" + " generated code will be executed in.\n" + " Defaults to:\n" + " * vulkan1.0 under --client vulkan\n" + " * opengl under --client opengl\n" + " * spirv1.0 under --target-env vulkan1.0\n" + " * spirv1.3 under --target-env vulkan1.1\n" + " * spirv1.5 under --target-env vulkan1.2\n" + " * spirv1.6 under --target-env vulkan1.3\n" + " Multiple --target-env can be specified.\n" + " --variable-name \n" + " --vn creates a C header file that contains a\n" + " uint32_t array named \n" + " initialized with the shader binary code\n" + " --no-link Only compile shader; do not link (GLSL-only)\n" + " NOTE: this option will set the export linkage\n" + " attribute on all functions\n" + " --lto perform link time optimization\n" + " --validate-io validate cross stage IO\n"); + + exit(EFailUsage); +} + +#if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API + +#include + +int fopen_s( + FILE** pFile, + const char* filename, + const char* mode +) +{ + if (!pFile || !filename || !mode) { + return EINVAL; + } + + FILE* f = fopen(filename, mode); + if (! f) { + if (errno != 0) { + return errno; + } else { + return ENOENT; + } + } + *pFile = f; + + return 0; +} + +#endif + +// +// Malloc a string of sufficient size and read a string into it. +// +char* ReadFileData(const char* fileName) +{ + FILE *in = nullptr; + int errorCode = fopen_s(&in, fileName, "r"); + if (errorCode || in == nullptr) + Error("unable to open input file"); + + int count = 0; + while (fgetc(in) != EOF) + count++; + + fseek(in, 0, SEEK_SET); + + if (count > 3) { + unsigned char head[3]; + if (fread(head, 1, 3, in) == 3) { + if (head[0] == 0xef && head[1] == 0xbb && head[2] == 0xbf) { + // skip BOM + count -= 3; + } else { + fseek(in, 0, SEEK_SET); + } + } else { + Error("can't read input file"); + } + } + + char* return_data = (char*)malloc(count + 1); // freed in FreeFileData() + if ((int)fread(return_data, 1, count, in) != count) { + free(return_data); + Error("can't read input file"); + } + + return_data[count] = '\0'; + fclose(in); + + return return_data; +} + +void FreeFileData(char* data) +{ + free(data); +} + +void InfoLogMsg(const char* msg, const char* name, const int num) +{ + if (num >= 0 ) + printf("#### %s %s %d INFO LOG ####\n", msg, name, num); + else + printf("#### %s %s INFO LOG ####\n", msg, name); +} diff --git a/engine/engine/3rdlibs/glslang/StandAlone/Worklist.h b/engine/engine/3rdlibs/glslang/StandAlone/Worklist.h new file mode 100644 index 00000000..dc726270 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/StandAlone/Worklist.h @@ -0,0 +1,94 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef WORKLIST_H_INCLUDED +#define WORKLIST_H_INCLUDED + +#include +#include +#include + +namespace glslang { + + class TWorkItem { + public: + TWorkItem() { } + explicit TWorkItem(const std::string& s) : + name(s) { } + std::string name; + std::string results; + std::string resultsIndex; + }; + + class TWorklist { + public: + TWorklist() { } + virtual ~TWorklist() { } + + void add(TWorkItem* item) + { + std::lock_guard guard(mutex); + worklist.push_back(item); + } + + bool remove(TWorkItem*& item) + { + std::lock_guard guard(mutex); + + if (worklist.empty()) + return false; + item = worklist.front(); + worklist.pop_front(); + + return true; + } + + int size() + { + return (int)worklist.size(); + } + + bool empty() + { + return worklist.empty(); + } + + protected: + std::mutex mutex; + std::list worklist; + }; + +} // end namespace glslang + +#endif // WORKLIST_H_INCLUDED diff --git a/engine/engine/3rdlibs/glslang/StandAlone/spirv-remap.cpp b/engine/engine/3rdlibs/glslang/StandAlone/spirv-remap.cpp new file mode 100644 index 00000000..b1f03266 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/StandAlone/spirv-remap.cpp @@ -0,0 +1,402 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +#include + +// +// Include remapper +// +#include "../SPIRV/SPVRemapper.h" + +namespace { + + typedef unsigned int SpvWord; + + // Poor man's basename: given a complete path, return file portion. + // E.g: + // Linux: /foo/bar/test -> test + // Win: c:\foo\bar\test -> test + // It's not very efficient, but that doesn't matter for our minimal-duty use. + // Using boost::filesystem would be better in many ways, but want to avoid that dependency. + + // OS dependent path separator (avoiding boost::filesystem dependency) +#if defined(_WIN32) + char path_sep_char() { return '\\'; } +#else + char path_sep_char() { return '/'; } +#endif + + std::string basename(const std::string filename) + { + const size_t sepLoc = filename.find_last_of(path_sep_char()); + + return (sepLoc == filename.npos) ? filename : filename.substr(sepLoc+1); + } + + void errHandler(const std::string& str) { + std::cout << str << std::endl; + exit(5); + } + + void logHandler(const std::string& str) { + std::cout << str << std::endl; + } + + // Read word stream from disk + void read(std::vector& spv, const std::string& inFilename, int verbosity) + { + std::ifstream fp; + + if (verbosity > 0) + logHandler(std::string(" reading: ") + inFilename); + + spv.clear(); + fp.open(inFilename, std::fstream::in | std::fstream::binary); + + if (fp.fail()) + errHandler("error opening file for read: "); + + // Reserve space (for efficiency, not for correctness) + fp.seekg(0, fp.end); + spv.reserve(size_t(fp.tellg()) / sizeof(SpvWord)); + fp.seekg(0, fp.beg); + + while (!fp.eof()) { + SpvWord inWord; + fp.read((char *)&inWord, sizeof(inWord)); + + if (!fp.eof()) { + spv.push_back(inWord); + if (fp.fail()) + errHandler(std::string("error reading file: ") + inFilename); + } + } + } + + // Read strings from a file + void read(std::vector& strings, const std::string& inFilename, int verbosity) + { + std::ifstream fp; + + if (verbosity > 0) + logHandler(std::string(" reading: ") + inFilename); + + strings.clear(); + fp.open(inFilename, std::fstream::in); + + if (fp.fail()) + errHandler("error opening file for read: "); + + std::string line; + while (std::getline(fp, line)) + { + // Ignore empty lines and lines starting with the comment marker '#'. + if (line.length() == 0 || line[0] == '#') { + continue; + } + + strings.push_back(line); + } + } + + void write(std::vector& spv, const std::string& outFile, int verbosity) + { + if (outFile.empty()) + errHandler("missing output filename."); + + std::ofstream fp; + + if (verbosity > 0) + logHandler(std::string(" writing: ") + outFile); + + fp.open(outFile, std::fstream::out | std::fstream::binary); + + if (fp.fail()) + errHandler(std::string("error opening file for write: ") + outFile); + + for (auto it = spv.cbegin(); it != spv.cend(); ++it) { + SpvWord word = *it; + fp.write((char *)&word, sizeof(word)); + if (fp.fail()) + errHandler(std::string("error writing file: ") + outFile); + } + + // file is closed by destructor + } + + // Print helpful usage message to stdout, and exit + void usage(const char* const name, const char* const msg = nullptr) + { + if (msg) + std::cout << msg << std::endl << std::endl; + + std::cout << "Usage: " << std::endl; + + std::cout << " " << basename(name) + << " [-v[v[...]] | --verbose [int]]" + << " [--map (all|types|names|funcs)]" + << " [--dce (all|types|funcs)]" + << " [--opt (all|loadstore)]" + << " [--strip-all | --strip all | -s]" + << " [--strip-white-list]" + << " [--do-everything]" + << " --input | -i file1 [file2...] --output|-o DESTDIR | destfile1 [destfile2...]" + << std::endl; + + std::cout << " " << basename(name) << " [--version | -V]" << std::endl; + std::cout << " " << basename(name) << " [--help | -?]" << std::endl; + + exit(0); + } + + // grind through each SPIR in turn + void execute(const std::vector& inputFiles, + const std::vector& outputDirOrFiles, + const bool isSingleOutputDir, + const std::string& whiteListFile, + int opts, + int verbosity) + { + std::vector whiteListStrings; + if (!whiteListFile.empty()) + read(whiteListStrings, whiteListFile, verbosity); + + for (std::size_t ii=0; ii spv; + read(spv, inputFiles[ii], verbosity); + + spv::spirvbin_t(verbosity).remap(spv, whiteListStrings, opts); + + if (isSingleOutputDir) { + // write all outputs to same directory + const std::string outFile = outputDirOrFiles[0] + path_sep_char() + basename(inputFiles[ii]); + write(spv, outFile, verbosity); + } else { + // write each input to its associated output + write(spv, outputDirOrFiles[ii], verbosity); + } + } + + if (verbosity > 0) + std::cout << "Done: " << inputFiles.size() << " file(s) processed" << std::endl; + } + + // Parse command line options + void parseCmdLine(int argc, + char** argv, + std::vector& inputFiles, + std::vector& outputDirOrFiles, + std::string& stripWhiteListFile, + int& options, + int& verbosity) + { + if (argc < 2) + usage(argv[0]); + + verbosity = 0; + options = spv::spirvbin_t::NONE; + + // Parse command line. + // boost::program_options would be quite a bit nicer, but we don't want to + // introduce a dependency on boost. + for (int a=1; a inputFiles; + std::vector outputDirOrFiles; + std::string whiteListFile; + int opts; + int verbosity; + + // handle errors by exiting + spv::spirvbin_t::registerErrorHandler(errHandler); + + // Log messages to std::cout + spv::spirvbin_t::registerLogHandler(logHandler); + + if (argc < 2) + usage(argv[0]); + + parseCmdLine(argc, argv, inputFiles, outputDirOrFiles, whiteListFile, opts, verbosity); + + if (outputDirOrFiles.empty()) + usage(argv[0], "Output directory or file(s) required."); + + const bool isMultiInput = inputFiles.size() > 1; + const bool isMultiOutput = outputDirOrFiles.size() > 1; + const bool isSingleOutputDir = !isMultiOutput && std::filesystem::is_directory(outputDirOrFiles[0]); + + if (isMultiInput && !isMultiOutput && !isSingleOutputDir) + usage(argv[0], "Output is not a directory."); + + + if (isMultiInput && isMultiOutput && (outputDirOrFiles.size() != inputFiles.size())) + usage(argv[0], "Output must be either a single directory or one output file per input."); + + // Main operations: read, remap, and write. + execute(inputFiles, outputDirOrFiles, isSingleOutputDir, whiteListFile, opts, verbosity); + + // If we get here, everything went OK! Nothing more to be done. +} diff --git a/engine/engine/3rdlibs/glslang/build_info.h.tmpl b/engine/engine/3rdlibs/glslang/build_info.h.tmpl new file mode 100644 index 00000000..60cb5bbe --- /dev/null +++ b/engine/engine/3rdlibs/glslang/build_info.h.tmpl @@ -0,0 +1,62 @@ +// Copyright (C) 2020 The Khronos Group Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of The Khronos Group Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_BUILD_INFO +#define GLSLANG_BUILD_INFO + +#define GLSLANG_VERSION_MAJOR @major@ +#define GLSLANG_VERSION_MINOR @minor@ +#define GLSLANG_VERSION_PATCH @patch@ +#define GLSLANG_VERSION_FLAVOR "@flavor@" + +#define GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \ + ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \ + ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ + (GLSLANG_VERSION_PATCH) > (patch))))) + +#define GLSLANG_VERSION_GREATER_OR_EQUAL_TO(major, minor, patch) \ + ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \ + ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ + (GLSLANG_VERSION_PATCH >= (patch)))))) + +#define GLSLANG_VERSION_LESS_THAN(major, minor, patch) \ + ((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \ + ((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ + (GLSLANG_VERSION_PATCH) < (patch))))) + +#define GLSLANG_VERSION_LESS_OR_EQUAL_TO(major, minor, patch) \ + ((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \ + ((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ + (GLSLANG_VERSION_PATCH <= (patch)))))) + +#endif // GLSLANG_BUILD_INFO diff --git a/engine/engine/3rdlibs/glslang/gen_extension_headers.py b/engine/engine/3rdlibs/glslang/gen_extension_headers.py new file mode 100644 index 00000000..dc195edb --- /dev/null +++ b/engine/engine/3rdlibs/glslang/gen_extension_headers.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import glob +import sys +import os + +def generate_main(glsl_files, output_header_file): + # Write commit ID to output header file + with open(output_header_file, "w") as header_file: + # Copyright Notice + header_string = '/***************************************************************************\n' + header_string += ' *\n' + header_string += ' * Copyright (c) 2015-2021 The Khronos Group Inc.\n' + header_string += ' * Copyright (c) 2015-2021 Valve Corporation\n' + header_string += ' * Copyright (c) 2015-2021 LunarG, Inc.\n' + header_string += ' * Copyright (c) 2015-2021 Google Inc.\n' + header_string += ' * Copyright (c) 2021 Advanced Micro Devices, Inc.All rights reserved.\n' + header_string += ' *\n' + header_string += ' ****************************************************************************/\n' + header_string += '#pragma once\n\n' + header_string += '#ifndef _INTRINSIC_EXTENSION_HEADER_H_\n' + header_string += '#define _INTRINSIC_EXTENSION_HEADER_H_\n\n' + header_file.write(header_string) + + symbol_name_list = [] + + for i in glsl_files: + glsl_contents = open(i,"r").read() + + filename = os.path.basename(i) + symbol_name = filename.split(".")[0] + symbol_name_list.append(symbol_name) + header_name = symbol_name + ".h" + header_str = 'std::string %s_GLSL = R"(\n%s\n)";\n' % (symbol_name, glsl_contents) + header_str += '\n' + header_file.write(header_str) + + contents = '' + contents += '\n' + contents += 'std::string getIntrinsic(const char* const* shaders, int n) {\n' + contents += '\tstd::string shaderString = "";\n'; + + contents += '\tfor (int i = 0; i < n; i++) {\n' + + for symbol_name in symbol_name_list: + contents += '\t\tif (strstr(shaders[i], "%s") != nullptr) {\n' % (symbol_name) + contents += '\t\t shaderString.append(%s_GLSL);\n' % (symbol_name) + contents += '\t\t}\n' + + contents += '\t}\n' + contents += '\treturn shaderString;\n'; + contents += '}\n' + + contents += '\n#endif\n' + header_file.write(contents) + +def main(): + if len(sys.argv) < 2: + raise Exception("Invalid number of arguments") + + i = 0 + while i < len(sys.argv): + opt = sys.argv[i] + i = i + 1 + + if opt == "-i" or opt == "-o": + if i == len(sys.argv): + raise Exception("Expected path after {}".format(opt)) + val = sys.argv[i] + i = i + 1 + if (opt == "-i"): + input_dir = val + elif (opt == "-o"): + output_file = val + else: + raise Exception("Unknown flag {}".format(opt)) + + glsl_files = glob.glob(input_dir + '/*.glsl') + + # Generate main header + generate_main(glsl_files, output_file) + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/glslang/glslang/CInterface/glslang_c_interface.cpp b/engine/engine/3rdlibs/glslang/glslang/CInterface/glslang_c_interface.cpp new file mode 100644 index 00000000..d391c1d0 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/CInterface/glslang_c_interface.cpp @@ -0,0 +1,541 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "glslang/Include/glslang_c_interface.h" + +#include "StandAlone/DirStackFileIncluder.h" +#include "glslang/Public/ResourceLimits.h" +#include "glslang/Public/ShaderLang.h" +#include "glslang/Include/ShHandle.h" + +#include "glslang/Include/BaseTypes.h" +#include "glslang/Include/ResourceLimits.h" +#include "glslang/Include/Types.h" +#include "glslang/MachineIndependent/iomapper.h" +#include "glslang/MachineIndependent/Versions.h" +#include "glslang/MachineIndependent/localintermediate.h" + +static_assert(int(GLSLANG_STAGE_COUNT) == EShLangCount, ""); +static_assert(int(GLSLANG_STAGE_MASK_COUNT) == EShLanguageMaskCount, ""); +static_assert(int(GLSLANG_SOURCE_COUNT) == glslang::EShSourceCount, ""); +static_assert(int(GLSLANG_CLIENT_COUNT) == glslang::EShClientCount, ""); +static_assert(int(GLSLANG_TARGET_COUNT) == glslang::EShTargetCount, ""); +static_assert(int(GLSLANG_TARGET_CLIENT_VERSION_COUNT) == glslang::EShTargetClientVersionCount, ""); +static_assert(int(GLSLANG_TARGET_LANGUAGE_VERSION_COUNT) == glslang::EShTargetLanguageVersionCount, ""); +static_assert(int(GLSLANG_OPT_LEVEL_COUNT) == EshOptLevelCount, ""); +static_assert(int(GLSLANG_TEX_SAMP_TRANS_COUNT) == EShTexSampTransCount, ""); +static_assert(int(GLSLANG_MSG_COUNT) == EShMsgCount, ""); +static_assert(int(GLSLANG_REFLECTION_COUNT) == EShReflectionCount, ""); +static_assert(int(GLSLANG_PROFILE_COUNT) == EProfileCount, ""); +static_assert(sizeof(glslang_limits_t) == sizeof(TLimits), ""); +static_assert(sizeof(glslang_resource_t) == sizeof(TBuiltInResource), ""); +static_assert(sizeof(glslang_version_t) == sizeof(glslang::Version), ""); + +typedef struct glslang_shader_s { + glslang::TShader* shader; + std::string preprocessedGLSL; + std::vector baseResourceSetBinding; +} glslang_shader_t; + +typedef struct glslang_program_s { + glslang::TProgram* program; + std::vector spirv; + std::string loggerMessages; +} glslang_program_t; + +/* Wrapper/Adapter for C glsl_include_callbacks_t functions + + This class contains a 'glsl_include_callbacks_t' structure + with C include_local/include_system callback pointers. + + This class implement TShader::Includer interface + by redirecting C++ virtual methods to C callbacks. + + The 'IncludeResult' instances produced by this Includer + contain a reference to glsl_include_result_t C structure + to allow its lifetime management by another C callback + (CallbackIncluder::callbacks::free_include_result) +*/ +class CallbackIncluder : public glslang::TShader::Includer { +public: + CallbackIncluder(glsl_include_callbacks_t _callbacks, void* _context) : callbacks(_callbacks), context(_context) {} + + virtual ~CallbackIncluder() {} + + virtual IncludeResult* includeSystem(const char* headerName, const char* includerName, + size_t inclusionDepth) override + { + if (this->callbacks.include_system) { + glsl_include_result_t* result = + this->callbacks.include_system(this->context, headerName, includerName, inclusionDepth); + return makeIncludeResult(result); + } + + return glslang::TShader::Includer::includeSystem(headerName, includerName, inclusionDepth); + } + + virtual IncludeResult* includeLocal(const char* headerName, const char* includerName, + size_t inclusionDepth) override + { + if (this->callbacks.include_local) { + glsl_include_result_t* result = + this->callbacks.include_local(this->context, headerName, includerName, inclusionDepth); + return makeIncludeResult(result); + } + + return glslang::TShader::Includer::includeLocal(headerName, includerName, inclusionDepth); + } + + /* This function only calls free_include_result callback + when the IncludeResult instance is allocated by a C function */ + virtual void releaseInclude(IncludeResult* result) override + { + if (result == nullptr) + return; + + if (this->callbacks.free_include_result) { + this->callbacks.free_include_result(this->context, static_cast(result->userData)); + } + + delete result; + } + +private: + CallbackIncluder() {} + + IncludeResult* makeIncludeResult(glsl_include_result_t* result) { + if (!result) { + return nullptr; + } + + return new glslang::TShader::Includer::IncludeResult( + std::string(result->header_name), result->header_data, result->header_length, result); + } + + /* C callback pointers */ + glsl_include_callbacks_t callbacks; + /* User-defined context */ + void* context; +}; + +GLSLANG_EXPORT void glslang_get_version(glslang_version_t* version) +{ + *reinterpret_cast(version) = glslang::GetVersion(); +} + +GLSLANG_EXPORT int glslang_initialize_process() { return static_cast(glslang::InitializeProcess()); } + +GLSLANG_EXPORT void glslang_finalize_process() { glslang::FinalizeProcess(); } + +static EShLanguage c_shader_stage(glslang_stage_t stage) +{ + switch (stage) { + case GLSLANG_STAGE_VERTEX: + return EShLangVertex; + case GLSLANG_STAGE_TESSCONTROL: + return EShLangTessControl; + case GLSLANG_STAGE_TESSEVALUATION: + return EShLangTessEvaluation; + case GLSLANG_STAGE_GEOMETRY: + return EShLangGeometry; + case GLSLANG_STAGE_FRAGMENT: + return EShLangFragment; + case GLSLANG_STAGE_COMPUTE: + return EShLangCompute; + case GLSLANG_STAGE_RAYGEN_NV: + return EShLangRayGen; + case GLSLANG_STAGE_INTERSECT_NV: + return EShLangIntersect; + case GLSLANG_STAGE_ANYHIT_NV: + return EShLangAnyHit; + case GLSLANG_STAGE_CLOSESTHIT_NV: + return EShLangClosestHit; + case GLSLANG_STAGE_MISS_NV: + return EShLangMiss; + case GLSLANG_STAGE_CALLABLE_NV: + return EShLangCallable; + case GLSLANG_STAGE_TASK: + return EShLangTask; + case GLSLANG_STAGE_MESH: + return EShLangMesh; + default: + break; + } + return EShLangCount; +} + +static int c_shader_messages(glslang_messages_t messages) +{ +#define CONVERT_MSG(in, out) \ + if ((messages & in) == in) \ + res |= out; + + int res = 0; + + CONVERT_MSG(GLSLANG_MSG_RELAXED_ERRORS_BIT, EShMsgRelaxedErrors); + CONVERT_MSG(GLSLANG_MSG_SUPPRESS_WARNINGS_BIT, EShMsgSuppressWarnings); + CONVERT_MSG(GLSLANG_MSG_AST_BIT, EShMsgAST); + CONVERT_MSG(GLSLANG_MSG_SPV_RULES_BIT, EShMsgSpvRules); + CONVERT_MSG(GLSLANG_MSG_VULKAN_RULES_BIT, EShMsgVulkanRules); + CONVERT_MSG(GLSLANG_MSG_ONLY_PREPROCESSOR_BIT, EShMsgOnlyPreprocessor); + CONVERT_MSG(GLSLANG_MSG_READ_HLSL_BIT, EShMsgReadHlsl); + CONVERT_MSG(GLSLANG_MSG_CASCADING_ERRORS_BIT, EShMsgCascadingErrors); + CONVERT_MSG(GLSLANG_MSG_KEEP_UNCALLED_BIT, EShMsgKeepUncalled); + CONVERT_MSG(GLSLANG_MSG_HLSL_OFFSETS_BIT, EShMsgHlslOffsets); + CONVERT_MSG(GLSLANG_MSG_DEBUG_INFO_BIT, EShMsgDebugInfo); + CONVERT_MSG(GLSLANG_MSG_HLSL_ENABLE_16BIT_TYPES_BIT, EShMsgHlslEnable16BitTypes); + CONVERT_MSG(GLSLANG_MSG_HLSL_LEGALIZATION_BIT, EShMsgHlslLegalization); + CONVERT_MSG(GLSLANG_MSG_HLSL_DX9_COMPATIBLE_BIT, EShMsgHlslDX9Compatible); + CONVERT_MSG(GLSLANG_MSG_BUILTIN_SYMBOL_TABLE_BIT, EShMsgBuiltinSymbolTable); + CONVERT_MSG(GLSLANG_MSG_ENHANCED, EShMsgEnhanced); + CONVERT_MSG(GLSLANG_MSG_ABSOLUTE_PATH, EShMsgAbsolutePath); + CONVERT_MSG(GLSLANG_MSG_DISPLAY_ERROR_COLUMN, EShMsgDisplayErrorColumn); + return res; +#undef CONVERT_MSG +} + +static glslang::EShTargetLanguageVersion +c_shader_target_language_version(glslang_target_language_version_t target_language_version) +{ + switch (target_language_version) { + case GLSLANG_TARGET_SPV_1_0: + return glslang::EShTargetSpv_1_0; + case GLSLANG_TARGET_SPV_1_1: + return glslang::EShTargetSpv_1_1; + case GLSLANG_TARGET_SPV_1_2: + return glslang::EShTargetSpv_1_2; + case GLSLANG_TARGET_SPV_1_3: + return glslang::EShTargetSpv_1_3; + case GLSLANG_TARGET_SPV_1_4: + return glslang::EShTargetSpv_1_4; + case GLSLANG_TARGET_SPV_1_5: + return glslang::EShTargetSpv_1_5; + case GLSLANG_TARGET_SPV_1_6: + return glslang::EShTargetSpv_1_6; + default: + break; + } + return glslang::EShTargetSpv_1_0; +} + +static glslang::EShClient c_shader_client(glslang_client_t client) +{ + switch (client) { + case GLSLANG_CLIENT_VULKAN: + return glslang::EShClientVulkan; + case GLSLANG_CLIENT_OPENGL: + return glslang::EShClientOpenGL; + default: + break; + } + + return glslang::EShClientNone; +} + +static glslang::EShTargetClientVersion c_shader_client_version(glslang_target_client_version_t client_version) +{ + switch (client_version) { + case GLSLANG_TARGET_VULKAN_1_1: + return glslang::EShTargetVulkan_1_1; + case GLSLANG_TARGET_VULKAN_1_2: + return glslang::EShTargetVulkan_1_2; + case GLSLANG_TARGET_VULKAN_1_3: + return glslang::EShTargetVulkan_1_3; + case GLSLANG_TARGET_VULKAN_1_4: + return glslang::EShTargetVulkan_1_4; + case GLSLANG_TARGET_OPENGL_450: + return glslang::EShTargetOpenGL_450; + default: + break; + } + + return glslang::EShTargetVulkan_1_0; +} + +static glslang::EShTargetLanguage c_shader_target_language(glslang_target_language_t target_language) +{ + if (target_language == GLSLANG_TARGET_NONE) + return glslang::EShTargetNone; + + return glslang::EShTargetSpv; +} + +static glslang::EShSource c_shader_source(glslang_source_t source) +{ + switch (source) { + case GLSLANG_SOURCE_GLSL: + return glslang::EShSourceGlsl; + case GLSLANG_SOURCE_HLSL: + return glslang::EShSourceHlsl; + default: + break; + } + + return glslang::EShSourceNone; +} + +static EProfile c_shader_profile(glslang_profile_t profile) +{ + switch (profile) { + case GLSLANG_BAD_PROFILE: + return EBadProfile; + case GLSLANG_NO_PROFILE: + return ENoProfile; + case GLSLANG_CORE_PROFILE: + return ECoreProfile; + case GLSLANG_COMPATIBILITY_PROFILE: + return ECompatibilityProfile; + case GLSLANG_ES_PROFILE: + return EEsProfile; + case GLSLANG_PROFILE_COUNT: // Should not use this + break; + } + + return EProfile(); +} + +GLSLANG_EXPORT glslang_shader_t* glslang_shader_create(const glslang_input_t* input) +{ + if (!input || !input->code) { + printf("Error creating shader: null input(%p)/input->code\n", (void*)input); + + if (input) + printf("input->code = %p\n", input->code); + + return nullptr; + } + + glslang_shader_t* shader = new glslang_shader_t(); + + shader->shader = new glslang::TShader(c_shader_stage(input->stage)); + shader->shader->setStrings(&input->code, 1); + shader->shader->setEnvInput(c_shader_source(input->language), c_shader_stage(input->stage), + c_shader_client(input->client), input->default_version); + shader->shader->setEnvClient(c_shader_client(input->client), c_shader_client_version(input->client_version)); + shader->shader->setEnvTarget(c_shader_target_language(input->target_language), + c_shader_target_language_version(input->target_language_version)); + + return shader; +} + +GLSLANG_EXPORT void glslang_shader_set_preamble(glslang_shader_t* shader, const char* s) { + shader->shader->setPreamble(s); +} + +GLSLANG_EXPORT void glslang_shader_shift_binding(glslang_shader_t* shader, glslang_resource_type_t res, unsigned int base) +{ + const glslang::TResourceType res_type = glslang::TResourceType(res); + shader->shader->setShiftBinding(res_type, base); +} + +GLSLANG_EXPORT void glslang_shader_shift_binding_for_set(glslang_shader_t* shader, glslang_resource_type_t res, unsigned int base, unsigned int set) +{ + const glslang::TResourceType res_type = glslang::TResourceType(res); + shader->shader->setShiftBindingForSet(res_type, base, set); +} + +GLSLANG_EXPORT void glslang_shader_set_options(glslang_shader_t* shader, int options) +{ + if (options & GLSLANG_SHADER_AUTO_MAP_BINDINGS) { + shader->shader->setAutoMapBindings(true); + } + + if (options & GLSLANG_SHADER_AUTO_MAP_LOCATIONS) { + shader->shader->setAutoMapLocations(true); + } + + if (options & GLSLANG_SHADER_VULKAN_RULES_RELAXED) { + shader->shader->setEnvInputVulkanRulesRelaxed(); + } +} + +GLSLANG_EXPORT void glslang_shader_set_glsl_version(glslang_shader_t* shader, int version) +{ + shader->shader->setOverrideVersion(version); +} + +GLSLANG_EXPORT void glslang_shader_set_default_uniform_block_set_and_binding(glslang_shader_t* shader, unsigned int set, unsigned int binding) { + shader->shader->setGlobalUniformSet(set); + shader->shader->setGlobalUniformBinding(binding); +} + +GLSLANG_EXPORT void glslang_shader_set_default_uniform_block_name(glslang_shader_t* shader, const char *name) { + shader->shader->setGlobalUniformBlockName(name); +} + +GLSLANG_EXPORT void glslang_shader_set_resource_set_binding(glslang_shader_t* shader, const char *const *bindings, unsigned int num_bindings) { + shader->baseResourceSetBinding.clear(); + + for (unsigned int i = 0; i < num_bindings; ++i) { + shader->baseResourceSetBinding.push_back(std::string(bindings[i])); + } + + shader->shader->setResourceSetBinding(shader->baseResourceSetBinding); +} + +GLSLANG_EXPORT const char* glslang_shader_get_preprocessed_code(glslang_shader_t* shader) +{ + return shader->preprocessedGLSL.c_str(); +} + +GLSLANG_EXPORT void glslang_shader_set_preprocessed_code(glslang_shader_t* shader, const char* code) +{ + shader->preprocessedGLSL.assign(code); +} + +GLSLANG_EXPORT int glslang_shader_preprocess(glslang_shader_t* shader, const glslang_input_t* input) +{ + DirStackFileIncluder dirStackFileIncluder; + CallbackIncluder callbackIncluder(input->callbacks, input->callbacks_ctx); + glslang::TShader::Includer& Includer = (input->callbacks.include_local||input->callbacks.include_system) + ? static_cast(callbackIncluder) + : static_cast(dirStackFileIncluder); + return shader->shader->preprocess( + reinterpret_cast(input->resource), + input->default_version, + c_shader_profile(input->default_profile), + input->force_default_version_and_profile != 0, + input->forward_compatible != 0, + (EShMessages)c_shader_messages(input->messages), + &shader->preprocessedGLSL, + Includer + ); +} + +GLSLANG_EXPORT int glslang_shader_parse(glslang_shader_t* shader, const glslang_input_t* input) +{ + const char* preprocessedCStr = shader->preprocessedGLSL.c_str(); + shader->shader->setStrings(&preprocessedCStr, 1); + + return shader->shader->parse( + reinterpret_cast(input->resource), + input->default_version, + input->forward_compatible != 0, + (EShMessages)c_shader_messages(input->messages) + ); +} + +GLSLANG_EXPORT const char* glslang_shader_get_info_log(glslang_shader_t* shader) { return shader->shader->getInfoLog(); } + +GLSLANG_EXPORT const char* glslang_shader_get_info_debug_log(glslang_shader_t* shader) { return shader->shader->getInfoDebugLog(); } + +GLSLANG_EXPORT void glslang_shader_delete(glslang_shader_t* shader) +{ + if (!shader) + return; + + delete (shader->shader); + delete (shader); +} + +GLSLANG_EXPORT glslang_program_t* glslang_program_create() +{ + glslang_program_t* p = new glslang_program_t(); + p->program = new glslang::TProgram(); + return p; +} + +GLSLANG_EXPORT void glslang_program_delete(glslang_program_t* program) +{ + if (!program) + return; + + delete (program->program); + delete (program); +} + +GLSLANG_EXPORT void glslang_program_add_shader(glslang_program_t* program, glslang_shader_t* shader) +{ + program->program->addShader(shader->shader); +} + +GLSLANG_EXPORT int glslang_program_link(glslang_program_t* program, int messages) +{ + return (int)program->program->link((EShMessages)messages); +} + +GLSLANG_EXPORT void glslang_program_add_source_text(glslang_program_t* program, glslang_stage_t stage, const char* text, size_t len) { + glslang::TIntermediate* intermediate = program->program->getIntermediate(c_shader_stage(stage)); + intermediate->addSourceText(text, len); +} + +GLSLANG_EXPORT void glslang_program_set_source_file(glslang_program_t* program, glslang_stage_t stage, const char* file) { + glslang::TIntermediate* intermediate = program->program->getIntermediate(c_shader_stage(stage)); + intermediate->setSourceFile(file); +} + +GLSLANG_EXPORT int glslang_program_map_io(glslang_program_t* program) +{ + return (int)program->program->mapIO(); +} + +GLSLANG_EXPORT int glslang_program_map_io_with_resolver_and_mapper(glslang_program_t* program, glslang_resolver_t* resolver, glslang_mapper_t* mapper) +{ + return (int)program->program->mapIO(reinterpret_cast(resolver), reinterpret_cast(mapper)); +} + +GLSLANG_EXPORT const char* glslang_program_get_info_log(glslang_program_t* program) +{ + return program->program->getInfoLog(); +} + +GLSLANG_EXPORT const char* glslang_program_get_info_debug_log(glslang_program_t* program) +{ + return program->program->getInfoDebugLog(); +} + +GLSLANG_EXPORT glslang_mapper_t* glslang_glsl_mapper_create() +{ + return reinterpret_cast(new glslang::TGlslIoMapper()); +} + +GLSLANG_EXPORT void glslang_glsl_mapper_delete(glslang_mapper_t* mapper) +{ + if (!mapper) + return; + + delete reinterpret_cast(mapper); +} + +GLSLANG_EXPORT glslang_resolver_t* glslang_glsl_resolver_create(glslang_program_t* program, glslang_stage_t stage) +{ + glslang::TIntermediate* intermediate = program->program->getIntermediate(c_shader_stage(stage)); + return reinterpret_cast(new glslang::TDefaultGlslIoResolver(reinterpret_cast(*intermediate))); +} + +GLSLANG_EXPORT void glslang_glsl_resolver_delete(glslang_resolver_t* resolver) +{ + if (!resolver) + return; + + delete reinterpret_cast(resolver); +} diff --git a/engine/engine/3rdlibs/glslang/glslang/CMakeLists.txt b/engine/engine/3rdlibs/glslang/glslang/CMakeLists.txt new file mode 100644 index 00000000..eb1dc68e --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/CMakeLists.txt @@ -0,0 +1,277 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(WIN32) + add_subdirectory(OSDependent/Windows) +elseif(UNIX OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia" OR ANDROID) + add_subdirectory(OSDependent/Unix) +else() + message("unknown platform") +endif() + +if(EMSCRIPTEN OR ENABLE_GLSLANG_JS) + # May be enabled on non-Emscripten builds for binary-size testing. + add_subdirectory(OSDependent/Web) +endif() + +################################################################################ +# GenericCodeGen +################################################################################ +set(GENERICCODEGEN_SOURCES + GenericCodeGen/CodeGen.cpp + GenericCodeGen/Link.cpp) + +add_library(GenericCodeGen STATIC + stub.cpp) +set_property(TARGET GenericCodeGen PROPERTY POSITION_INDEPENDENT_CODE ON) +set_property(TARGET GenericCodeGen PROPERTY FOLDER glslang) + +################################################################################ +# MachineIndependent +################################################################################ +set(MACHINEINDEPENDENT_SOURCES + MachineIndependent/glslang.y + MachineIndependent/glslang_tab.cpp + MachineIndependent/attribute.cpp + MachineIndependent/Constant.cpp + MachineIndependent/iomapper.cpp + MachineIndependent/InfoSink.cpp + MachineIndependent/Initialize.cpp + MachineIndependent/IntermTraverse.cpp + MachineIndependent/Intermediate.cpp + MachineIndependent/ParseContextBase.cpp + MachineIndependent/ParseHelper.cpp + MachineIndependent/PoolAlloc.cpp + MachineIndependent/RemoveTree.cpp + MachineIndependent/Scan.cpp + MachineIndependent/ShaderLang.cpp + MachineIndependent/SpirvIntrinsics.cpp + MachineIndependent/SymbolTable.cpp + MachineIndependent/Versions.cpp + MachineIndependent/intermOut.cpp + MachineIndependent/limits.cpp + MachineIndependent/linkValidate.cpp + MachineIndependent/parseConst.cpp + MachineIndependent/reflection.cpp + MachineIndependent/preprocessor/Pp.cpp + MachineIndependent/preprocessor/PpAtom.cpp + MachineIndependent/preprocessor/PpContext.cpp + MachineIndependent/preprocessor/PpScanner.cpp + MachineIndependent/preprocessor/PpTokens.cpp + MachineIndependent/propagateNoContraction.cpp +) + +set(MACHINEINDEPENDENT_HEADERS + MachineIndependent/attribute.h + MachineIndependent/glslang_tab.cpp.h + MachineIndependent/gl_types.h + MachineIndependent/Initialize.h + MachineIndependent/iomapper.h + MachineIndependent/LiveTraverser.h + MachineIndependent/localintermediate.h + MachineIndependent/ParseHelper.h + MachineIndependent/reflection.h + MachineIndependent/RemoveTree.h + MachineIndependent/Scan.h + MachineIndependent/ScanContext.h + MachineIndependent/SymbolTable.h + MachineIndependent/Versions.h + MachineIndependent/parseVersions.h + MachineIndependent/propagateNoContraction.h + MachineIndependent/preprocessor/PpContext.h + MachineIndependent/preprocessor/PpTokens.h +) + +if(ENABLE_HLSL) + list(APPEND MACHINEINDEPENDENT_SOURCES + HLSL/hlslAttributes.cpp + HLSL/hlslParseHelper.cpp + HLSL/hlslScanContext.cpp + HLSL/hlslOpMap.cpp + HLSL/hlslTokenStream.cpp + HLSL/hlslGrammar.cpp + HLSL/hlslParseables.cpp) + + list(APPEND MACHINEINDEPENDENT_HEADERS + HLSL/hlslAttributes.h + HLSL/hlslParseHelper.h + HLSL/hlslTokens.h + HLSL/hlslScanContext.h + HLSL/hlslOpMap.h + HLSL/hlslTokenStream.h + HLSL/hlslGrammar.h + HLSL/hlslParseables.h) +endif() + +add_library(MachineIndependent STATIC stub.cpp) +set_property(TARGET MachineIndependent PROPERTY POSITION_INDEPENDENT_CODE ON) +set_property(TARGET MachineIndependent PROPERTY FOLDER glslang) +target_link_libraries(MachineIndependent INTERFACE glslang) + +if (NOT MSVC) + # -Wunused-but-set-variable is triggered in code generated by bison that we do not control. Turn this warning off, but only for the generated. + set_source_files_properties(MachineIndependent/glslang_tab.cpp PROPERTIES COMPILE_FLAGS -Wno-unused-but-set-variable) +endif() + +################################################################################ +# glslang +################################################################################ +set(GLSLANG_SOURCES + CInterface/glslang_c_interface.cpp) + +set(GLSLANG_HEADERS + Public/ShaderLang.h + Include/arrays.h + Include/BaseTypes.h + Include/Common.h + Include/ConstantUnion.h + Include/glslang_c_interface.h + Include/glslang_c_shader_types.h + Include/InfoSink.h + Include/InitializeGlobals.h + Include/intermediate.h + Include/PoolAlloc.h + Include/ResourceLimits.h + Include/ShHandle.h + Include/SpirvIntrinsics.h + Include/Types.h + Include/visibility.h) + +add_library(glslang ${LIB_TYPE} ${GLSLANG_SOURCES} ${GLSLANG_HEADERS} ${GENERICCODEGEN_SOURCES} ${GENERICCODEGEN_HEADERS} ${OSDEPENDENT_SOURCES} ${OSDEPENDENT_HEADERS} ${MACHINEINDEPENDENT_SOURCES} ${MACHINEINDEPENDENT_HEADERS} ${SPIRV_SOURCES} ${SPIRV_HEADERS}) +add_library(glslang::glslang ALIAS glslang) +set_target_properties(glslang PROPERTIES + FOLDER glslang + POSITION_INDEPENDENT_CODE ON + VERSION "${GLSLANG_VERSION}" + SOVERSION "${GLSLANG_VERSION_MAJOR}") +target_include_directories(glslang PUBLIC + $ + $) + +glslang_add_build_info_dependency(glslang) + +glslang_pch(glslang MachineIndependent/pch.h) + +glslang_only_export_explicit_symbols(glslang) + +if(WIN32 AND BUILD_SHARED_LIBS) + set_target_properties(glslang PROPERTIES PREFIX "") +endif() + +if(ENABLE_SPIRV) + if(ENABLE_OPT) + target_include_directories(glslang PUBLIC + $) + target_link_libraries(glslang SPIRV-Tools-opt) + endif() +endif() + +# Link pthread +if(UNIX OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia" OR ANDROID) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + target_link_libraries(glslang Threads::Threads) +endif() + +################################################################################ +# ResourceLimits +################################################################################ +set(RESOURCELIMITS_SOURCES + ResourceLimits/ResourceLimits.cpp + ResourceLimits/resource_limits_c.cpp +) + +set(RESOURCELIMITS_HEADERS + Public/ResourceLimits.h + Public/resource_limits_c.h +) + +add_library(glslang-default-resource-limits ${RESOURCELIMITS_SOURCES} ${RESOURCELIMITS_HEADERS}) +add_library(glslang::glslang-default-resource-limits ALIAS glslang-default-resource-limits) +set_target_properties(glslang-default-resource-limits PROPERTIES + VERSION "${GLSLANG_VERSION}" + SOVERSION "${GLSLANG_VERSION_MAJOR}" + FOLDER glslang + POSITION_INDEPENDENT_CODE ON) + +target_include_directories(glslang-default-resource-limits PUBLIC + $ + $) +glslang_only_export_explicit_symbols(glslang-default-resource-limits) + +################################################################################ +# source_groups +################################################################################ +if(WIN32) + source_group("OSDependent" REGULAR_EXPRESSION "OSDependent/[^/]*") + source_group("OSDependent\\Windows" REGULAR_EXPRESSION "OSDependent/Windows/*") + source_group("Public" REGULAR_EXPRESSION "Public/*") + source_group("MachineIndependent" REGULAR_EXPRESSION "MachineIndependent/[^/]*") + source_group("Include" REGULAR_EXPRESSION "Include/[^/]*") + source_group("GenericCodeGen" REGULAR_EXPRESSION "GenericCodeGen/*") + source_group("MachineIndependent\\Preprocessor" REGULAR_EXPRESSION "MachineIndependent/preprocessor/*") + source_group("HLSL" REGULAR_EXPRESSION "HLSL/*") + source_group("CInterface" REGULAR_EXPRESSION "CInterface/*") + source_group("SPIRV" REGULAR_EXPRESSION "SPIRV/[^/]*") + source_group("SPIRV\\CInterface" REGULAR_EXPRESSION "SPIRV/CInterface/*") +endif() + +################################################################################ +# install +################################################################################ +if(GLSLANG_ENABLE_INSTALL) + install(TARGETS glslang EXPORT glslang-targets) + if(NOT BUILD_SHARED_LIBS) + install(TARGETS MachineIndependent EXPORT glslang-targets) + install(TARGETS GenericCodeGen EXPORT glslang-targets) + endif() + + set(PUBLIC_HEADERS + Public/ResourceLimits.h + Public/ShaderLang.h + Public/resource_limits_c.h + Include/glslang_c_interface.h + Include/glslang_c_shader_types.h + Include/ResourceLimits.h + Include/visibility.h + MachineIndependent/Versions.h) + + foreach(file ${PUBLIC_HEADERS}) + get_filename_component(dir ${file} DIRECTORY) + install(FILES ${file} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang/${dir}) + endforeach() + + install(FILES ${GLSLANG_BUILD_INFO_H} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang) + + install(TARGETS glslang-default-resource-limits EXPORT glslang-targets) +endif() diff --git a/engine/engine/3rdlibs/glslang/glslang/ExtensionHeaders/GL_EXT_shader_realtime_clock.glsl b/engine/engine/3rdlibs/glslang/glslang/ExtensionHeaders/GL_EXT_shader_realtime_clock.glsl new file mode 100644 index 00000000..7cf545d9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/ExtensionHeaders/GL_EXT_shader_realtime_clock.glsl @@ -0,0 +1,38 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2016-2020 Google, Inc. +// Modifications Copyright(C) 2021 Advanced Micro Devices, Inc.All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + diff --git a/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/CodeGen.cpp b/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/CodeGen.cpp new file mode 100644 index 00000000..1ef24496 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/CodeGen.cpp @@ -0,0 +1,72 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "../MachineIndependent/Versions.h" + +// +// Here is where real machine specific high-level data would be defined. +// +class TGenericCompiler : public TCompiler { +public: + TGenericCompiler(EShLanguage l) : TCompiler(l, infoSink) {} + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile); + TInfoSink infoSink; +}; + +// +// This function must be provided to create the actual +// compile object used by higher level code. It returns +// a subclass of TCompiler. +// +TCompiler* ConstructCompiler(EShLanguage language, int) { return new TGenericCompiler(language); } + +// +// Delete the compiler made by ConstructCompiler +// +void DeleteCompiler(TCompiler* compiler) +{ + delete compiler; +} + +// +// Generate code from the given parse tree +// +bool TGenericCompiler::compile(TIntermNode* /*root*/, int /*version*/, EProfile /*profile*/) +{ + haveValidObjectCode = true; + + return haveValidObjectCode; +} diff --git a/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/Link.cpp b/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/Link.cpp new file mode 100644 index 00000000..5df39b81 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/GenericCodeGen/Link.cpp @@ -0,0 +1,87 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// The top level algorithms for linking multiple +// shaders together. +// +#include "../Include/Common.h" +#include "../Include/ShHandle.h" + +// +// Actual link object, derived from the shader handle base classes. +// +class TGenericLinker : public TLinker { +public: + TGenericLinker(EShExecutable e) : TLinker(e, infoSink) {} + bool link(TCompilerList&, TUniformMap*) { return true; } + void getAttributeBindings(ShBindingTable const **) const { } + TInfoSink infoSink; +}; + +// +// The internal view of a uniform/float object exchanged with the driver. +// +class TUniformLinkedMap : public TUniformMap { +public: + TUniformLinkedMap() { } + virtual int getLocation(const char*) { return 0; } +}; + +TShHandleBase* ConstructLinker(EShExecutable executable, int) { return new TGenericLinker(executable); } + +void DeleteLinker(TShHandleBase* linker) +{ + delete linker; +} + +TUniformMap* ConstructUniformMap() +{ + return new TUniformLinkedMap(); +} + +void DeleteUniformMap(TUniformMap* map) +{ + delete map; +} + +TShHandleBase* ConstructBindings() +{ + return nullptr; +} + +void DeleteBindingList(TShHandleBase* bindingList) +{ + delete bindingList; +} diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.cpp new file mode 100644 index 00000000..97305493 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.cpp @@ -0,0 +1,151 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslAttributes.h" +#include "hlslParseHelper.h" + +namespace glslang { + // Map the given string to an attribute enum from TAttributeType, + // or EatNone if invalid. + TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const + { + // handle names within a namespace + + if (nameSpace == "vk") { + if (name == "input_attachment_index") + return EatInputAttachment; + else if (name == "location") + return EatLocation; + else if (name == "binding") + return EatBinding; + else if (name == "global_cbuffer_binding") + return EatGlobalBinding; + else if (name == "builtin") + return EatBuiltIn; + else if (name == "constant_id") + return EatConstantId; + else if (name == "push_constant") + return EatPushConstant; + } else if (nameSpace == "spv") { + if (name == "format_rgba32f") return EatFormatRgba32f; + if (name == "format_rgba16f") return EatFormatRgba16f; + if (name == "format_r32f") return EatFormatR32f; + if (name == "format_rgba8") return EatFormatRgba8; + if (name == "format_rgba8snorm") return EatFormatRgba8Snorm; + if (name == "format_rg32f") return EatFormatRg32f; + if (name == "format_rg16f") return EatFormatRg16f; + if (name == "format_r11fg11fb10f") return EatFormatR11fG11fB10f; + if (name == "format_r16f") return EatFormatR16f; + if (name == "format_rgba16") return EatFormatRgba16; + if (name == "format_rgb10a2") return EatFormatRgb10A2; + if (name == "format_rg16") return EatFormatRg16; + if (name == "format_rg8") return EatFormatRg8; + if (name == "format_r16") return EatFormatR16; + if (name == "format_r8") return EatFormatR8; + if (name == "format_rgba16snorm") return EatFormatRgba16Snorm; + if (name == "format_rg16snorm") return EatFormatRg16Snorm; + if (name == "format_rg8snorm") return EatFormatRg8Snorm; + if (name == "format_r16snorm") return EatFormatR16Snorm; + if (name == "format_r8snorm") return EatFormatR8Snorm; + if (name == "format_rgba32i") return EatFormatRgba32i; + if (name == "format_rgba16i") return EatFormatRgba16i; + if (name == "format_rgba8i") return EatFormatRgba8i; + if (name == "format_r32i") return EatFormatR32i; + if (name == "format_rg32i") return EatFormatRg32i; + if (name == "format_rg16i") return EatFormatRg16i; + if (name == "format_rg8i") return EatFormatRg8i; + if (name == "format_r16i") return EatFormatR16i; + if (name == "format_r8i") return EatFormatR8i; + if (name == "format_rgba32ui") return EatFormatRgba32ui; + if (name == "format_rgba16ui") return EatFormatRgba16ui; + if (name == "format_rgba8ui") return EatFormatRgba8ui; + if (name == "format_r32ui") return EatFormatR32ui; + if (name == "format_rgb10a2ui") return EatFormatRgb10a2ui; + if (name == "format_rg32ui") return EatFormatRg32ui; + if (name == "format_rg16ui") return EatFormatRg16ui; + if (name == "format_rg8ui") return EatFormatRg8ui; + if (name == "format_r16ui") return EatFormatR16ui; + if (name == "format_r8ui") return EatFormatR8ui; + + if (name == "nonwritable") return EatNonWritable; + if (name == "nonreadable") return EatNonReadable; + + if (name == "export") return EatExport; + } else if (nameSpace.size() > 0) + return EatNone; + + // handle names with no namespace + + if (name == "allow_uav_condition") + return EatAllow_uav_condition; + else if (name == "branch") + return EatBranch; + else if (name == "call") + return EatCall; + else if (name == "domain") + return EatDomain; + else if (name == "earlydepthstencil") + return EatEarlyDepthStencil; + else if (name == "fastopt") + return EatFastOpt; + else if (name == "flatten") + return EatFlatten; + else if (name == "forcecase") + return EatForceCase; + else if (name == "instance") + return EatInstance; + else if (name == "maxtessfactor") + return EatMaxTessFactor; + else if (name == "maxvertexcount") + return EatMaxVertexCount; + else if (name == "numthreads") + return EatNumThreads; + else if (name == "outputcontrolpoints") + return EatOutputControlPoints; + else if (name == "outputtopology") + return EatOutputTopology; + else if (name == "partitioning") + return EatPartitioning; + else if (name == "patchconstantfunc") + return EatPatchConstantFunc; + else if (name == "unroll") + return EatUnroll; + else if (name == "loop") + return EatLoop; + else + return EatNone; + } + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.h new file mode 100644 index 00000000..62faa5b8 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslAttributes.h @@ -0,0 +1,59 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLATTRIBUTES_H_ +#define HLSLATTRIBUTES_H_ + +#include +#include + +#include "../MachineIndependent/attribute.h" +#include "../MachineIndependent/SymbolTable.h" +#include "hlslScanContext.h" + +namespace glslang { + + class TFunctionDeclarator { + public: + TFunctionDeclarator() : function(nullptr), body(nullptr) { } + TSourceLoc loc; + TFunction* function; + TAttributes attributes; + TVector* body; + }; + +} // end namespace glslang + +#endif // HLSLATTRIBUTES_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.cpp new file mode 100644 index 00000000..ed32ff0c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.cpp @@ -0,0 +1,4484 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// Copyright (C) 2023 Mobica Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This is a set of mutually recursive methods implementing the HLSL grammar. +// Generally, each returns +// - through an argument: a type specifically appropriate to which rule it +// recognized +// - through the return value: true/false to indicate whether or not it +// recognized its rule +// +// As much as possible, only grammar recognition should happen in this file, +// with all other work being farmed out to hlslParseHelper.cpp, which in turn +// will build the AST. +// +// The next token, yet to be "accepted" is always sitting in 'token'. +// When a method says it accepts a rule, that means all tokens involved +// in the rule will have been consumed, and none left in 'token'. +// + +#include "hlslTokens.h" +#include "hlslGrammar.h" +#include "hlslAttributes.h" + +namespace glslang { + +// Root entry point to this recursive decent parser. +// Return true if compilation unit was successfully accepted. +bool HlslGrammar::parse() +{ + advanceToken(); + return acceptCompilationUnit(); +} + +void HlslGrammar::expected(const char* syntax) +{ + parseContext.error(token.loc, "Expected", syntax, ""); +} + +void HlslGrammar::unimplemented(const char* error) +{ + parseContext.error(token.loc, "Unimplemented", error, ""); +} + +// IDENTIFIER +// THIS +// type that can be used as IDENTIFIER +// +// Only process the next token if it is an identifier. +// Return true if it was an identifier. +bool HlslGrammar::acceptIdentifier(HlslToken& idToken) +{ + // IDENTIFIER + if (peekTokenClass(EHTokIdentifier)) { + idToken = token; + advanceToken(); + return true; + } + + // THIS + // -> maps to the IDENTIFIER spelled with the internal special name for 'this' + if (peekTokenClass(EHTokThis)) { + idToken = token; + advanceToken(); + idToken.tokenClass = EHTokIdentifier; + idToken.string = NewPoolTString(intermediate.implicitThisName); + return true; + } + + // type that can be used as IDENTIFIER + + // Even though "sample", "bool", "float", etc keywords (for types, interpolation modifiers), + // they ARE still accepted as identifiers. This is not a dense space: e.g, "void" is not a + // valid identifier, nor is "linear". This code special cases the known instances of this, so + // e.g, "int sample;" or "float float;" is accepted. Other cases can be added here if needed. + + const char* idString = getTypeString(peek()); + if (idString == nullptr) + return false; + + token.string = NewPoolTString(idString); + token.tokenClass = EHTokIdentifier; + idToken = token; + typeIdentifiers = true; + + advanceToken(); + + return true; +} + +// compilationUnit +// : declaration_list EOF +// +bool HlslGrammar::acceptCompilationUnit() +{ + if (! acceptDeclarationList(unitNode)) + return false; + + if (! peekTokenClass(EHTokNone)) + return false; + + // set root of AST + if (unitNode && !unitNode->getAsAggregate()) + unitNode = intermediate.growAggregate(nullptr, unitNode); + intermediate.setTreeRoot(unitNode); + + return true; +} + +// Recognize the following, but with the extra condition that it can be +// successfully terminated by EOF or '}'. +// +// declaration_list +// : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE +// +// declaration_or_semicolon +// : declaration +// : SEMICOLON +// +bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList) +{ + do { + // HLSL allows extra semicolons between global declarations + do { } while (acceptTokenClass(EHTokSemicolon)); + + // EOF or RIGHT_BRACE + if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace)) + return true; + + // declaration + if (! acceptDeclaration(nodeList)) { + expected("declaration"); + return false; + } + } while (true); + + return true; +} + +// sampler_state +// : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE +// +// sampler_state_assignment +// : sampler_state_identifier EQUAL value SEMICOLON +// +// sampler_state_identifier +// : ADDRESSU +// | ADDRESSV +// | ADDRESSW +// | BORDERCOLOR +// | FILTER +// | MAXANISOTROPY +// | MAXLOD +// | MINLOD +// | MIPLODBIAS +// +bool HlslGrammar::acceptSamplerState() +{ + // TODO: this should be genericized to accept a list of valid tokens and + // return token/value pairs. Presently it is specific to texture values. + + if (! acceptTokenClass(EHTokLeftBrace)) + return true; + + parseContext.warn(token.loc, "unimplemented", "immediate sampler state", ""); + + do { + // read state name + HlslToken state; + if (! acceptIdentifier(state)) + break; // end of list + + // FXC accepts any case + TString stateName = *state.string; + std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::tolower); + + if (! acceptTokenClass(EHTokAssign)) { + expected("assign"); + return false; + } + + if (stateName == "minlod" || stateName == "maxlod") { + if (! peekTokenClass(EHTokIntConstant)) { + expected("integer"); + return false; + } + + TIntermTyped* lod = nullptr; + if (! acceptLiteral(lod)) // should never fail, since we just looked for an integer + return false; + } else if (stateName == "maxanisotropy") { + if (! peekTokenClass(EHTokIntConstant)) { + expected("integer"); + return false; + } + + TIntermTyped* maxAnisotropy = nullptr; + if (! acceptLiteral(maxAnisotropy)) // should never fail, since we just looked for an integer + return false; + } else if (stateName == "filter") { + HlslToken filterMode; + if (! acceptIdentifier(filterMode)) { + expected("filter mode"); + return false; + } + } else if (stateName == "addressu" || stateName == "addressv" || stateName == "addressw") { + HlslToken addrMode; + if (! acceptIdentifier(addrMode)) { + expected("texture address mode"); + return false; + } + } else if (stateName == "miplodbias") { + TIntermTyped* lodBias = nullptr; + if (! acceptLiteral(lodBias)) { + expected("lod bias"); + return false; + } + } else if (stateName == "bordercolor") { + return false; + } else { + expected("texture state"); + return false; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) { + expected("semicolon"); + return false; + } + } while (true); + + if (! acceptTokenClass(EHTokRightBrace)) + return false; + + return true; +} + +// sampler_declaration_dx9 +// : SAMPLER identifier EQUAL sampler_type sampler_state +// +bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) +{ + if (! acceptTokenClass(EHTokSampler)) + return false; + + // TODO: remove this when DX9 style declarations are implemented. + unimplemented("Direct3D 9 sampler declaration"); + + // read sampler name + HlslToken name; + if (! acceptIdentifier(name)) { + expected("sampler name"); + return false; + } + + if (! acceptTokenClass(EHTokAssign)) { + expected("="); + return false; + } + + return false; +} + +// declaration +// : attributes attributed_declaration +// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE +// +// attributed_declaration +// : sampler_declaration_dx9 post_decls SEMICOLON +// | fully_specified_type // for cbuffer/tbuffer +// | fully_specified_type declarator_list SEMICOLON // for non cbuffer/tbuffer +// | fully_specified_type identifier function_parameters post_decls compound_statement // function definition +// | fully_specified_type identifier sampler_state post_decls compound_statement // sampler definition +// | typedef declaration +// +// declarator_list +// : declarator COMMA declarator COMMA declarator... // zero or more declarators +// +// declarator +// : identifier array_specifier post_decls +// | identifier array_specifier post_decls EQUAL assignment_expression +// | identifier function_parameters post_decls // function prototype +// +// Parsing has to go pretty far in to know whether it's a variable, prototype, or +// function definition, so the implementation below doesn't perfectly divide up the grammar +// as above. (The 'identifier' in the first item in init_declarator list is the +// same as 'identifier' for function declarations.) +// +// This can generate more than one subtree, one per initializer or a function body. +// All initializer subtrees are put in their own aggregate node, making one top-level +// node for all the initializers. Each function created is a top-level node to grow +// into the passed-in nodeList. +// +// If 'nodeList' is passed in as non-null, it must be an aggregate to extend for +// each top-level node the declaration creates. Otherwise, if only one top-level +// node in generated here, that is want is returned in nodeList. +// +bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) +{ + // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE + if (acceptTokenClass(EHTokNamespace)) { + HlslToken namespaceToken; + if (!acceptIdentifier(namespaceToken)) { + expected("namespace name"); + return false; + } + parseContext.pushNamespace(*namespaceToken.string); + if (!acceptTokenClass(EHTokLeftBrace)) { + expected("{"); + return false; + } + if (!acceptDeclarationList(nodeList)) { + expected("declaration list"); + return false; + } + if (!acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + parseContext.popNamespace(); + return true; + } + + bool declarator_list = false; // true when processing comma separation + + // attributes + TFunctionDeclarator declarator; + acceptAttributes(declarator.attributes); + + // typedef + bool typedefDecl = acceptTokenClass(EHTokTypedef); + + TType declaredType; + + // DX9 sampler declaration use a different syntax + // DX9 shaders need to run through HLSL compiler (fxc) via a back compat mode, it isn't going to + // be possible to simultaneously compile D3D10+ style shaders and DX9 shaders. If we want to compile DX9 + // HLSL shaders, this will have to be a master level switch + // As such, the sampler keyword in D3D10+ turns into an automatic sampler type, and is commonly used + // For that reason, this line is commented out + // if (acceptSamplerDeclarationDX9(declaredType)) + // return true; + + bool forbidDeclarators = (peekTokenClass(EHTokCBuffer) || peekTokenClass(EHTokTBuffer)); + // fully_specified_type + if (! acceptFullySpecifiedType(declaredType, nodeList, declarator.attributes, forbidDeclarators)) + return false; + + // cbuffer and tbuffer end with the closing '}'. + // No semicolon is included. + if (forbidDeclarators) + return true; + + // Check if there are invalid in/out qualifiers + switch (declaredType.getQualifier().storage) { + case EvqIn: + case EvqOut: + case EvqInOut: + parseContext.error(token.loc, "in/out qualifiers are only valid on parameters", token.string->c_str(), ""); + break; + default: + break; + } + + // declarator_list + // : declarator + // : identifier + HlslToken idToken; + TIntermAggregate* initializers = nullptr; + while (acceptIdentifier(idToken)) { + TString *fullName = idToken.string; + if (parseContext.symbolTable.atGlobalLevel()) + parseContext.getFullNamespaceName(fullName); + if (peekTokenClass(EHTokLeftParen)) { + // looks like function parameters + + // merge in the attributes into the return type + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction(fullName); + + // function_parameters + declarator.function = new TFunction(fullName, declaredType); + if (!acceptFunctionParameters(*declarator.function)) { + expected("function parameter list"); + return false; + } + + // post_decls + acceptPostDecls(declarator.function->getWritableType().getQualifier()); + + // compound_statement (function body definition) or just a prototype? + declarator.loc = token.loc; + if (peekTokenClass(EHTokLeftBrace)) { + if (declarator_list) + parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", ""); + if (typedefDecl) + parseContext.error(idToken.loc, "function body can't be in a typedef", "{", ""); + return acceptFunctionDefinition(declarator, nodeList, nullptr); + } else { + if (typedefDecl) + parseContext.error(idToken.loc, "function typedefs not implemented", "{", ""); + parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true); + } + } else { + // A variable declaration. + + // merge in the attributes, the first time around, into the shared type + if (! declarator_list) + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType); + + // Fix the storage qualifier if it's a global. + if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel()) + declaredType.getQualifier().storage = EvqUniform; + + // recognize array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + + // We can handle multiple variables per type declaration, so + // the number of types can expand when arrayness is different. + TType variableType; + variableType.shallowCopy(declaredType); + + // In the most general case, arrayness is potentially coming both from the + // declared type and from the variable: "int[] a[];" or just one or the other. + // Merge it all to the variableType, so all arrayness is part of the variableType. + variableType.transferArraySizes(arraySizes); + variableType.copyArrayInnerSizes(declaredType.getArraySizes()); + + // samplers accept immediate sampler state + if (variableType.getBasicType() == EbtSampler) { + if (! acceptSamplerState()) + return false; + } + + // post_decls + acceptPostDecls(variableType.getQualifier()); + + // EQUAL assignment_expression + TIntermTyped* expressionNode = nullptr; + if (acceptTokenClass(EHTokAssign)) { + if (typedefDecl) + parseContext.error(idToken.loc, "can't have an initializer", "typedef", ""); + if (! acceptAssignmentExpression(expressionNode)) { + expected("initializer"); + return false; + } + } + + // TODO: things scoped within an annotation need their own name space; + // TODO: non-constant strings are not yet handled. + if (!(variableType.getBasicType() == EbtString && !variableType.getQualifier().isConstant()) && + parseContext.getAnnotationNestingLevel() == 0) { + if (typedefDecl) + parseContext.declareTypedef(idToken.loc, *fullName, variableType); + else if (variableType.getBasicType() == EbtBlock) { + if (expressionNode) + parseContext.error(idToken.loc, "buffer aliasing not yet supported", "block initializer", ""); + parseContext.declareBlock(idToken.loc, variableType, fullName); + parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName); + } else { + if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) { + // this isn't really an individual variable, but a member of the $Global buffer + parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName); + } else { + // Declare the variable and add any initializer code to the AST. + // The top-level node is always made into an aggregate, as that's + // historically how the AST has been. + initializers = intermediate.growAggregate(initializers, + parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode), + idToken.loc); + } + } + } + } + + // COMMA + if (acceptTokenClass(EHTokComma)) + declarator_list = true; + } + + // The top-level initializer node is a sequence. + if (initializers != nullptr) + initializers->setOperator(EOpSequence); + + // if we have a locally scoped static, it needs a globally scoped initializer + if (declaredType.getQualifier().storage == EvqGlobal && !parseContext.symbolTable.atGlobalLevel()) { + unitNode = intermediate.growAggregate(unitNode, initializers, idToken.loc); + } else { + // Add the initializers' aggregate to the nodeList we were handed. + if (nodeList) + nodeList = intermediate.growAggregate(nodeList, initializers); + else + nodeList = initializers; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) { + // This may have been a false detection of what appeared to be a declaration, but + // was actually an assignment such as "float = 4", where "float" is an identifier. + // We put the token back to let further parsing happen for cases where that may + // happen. This errors on the side of caution, and mostly triggers the error. + if (peek() == EHTokAssign || peek() == EHTokLeftBracket || peek() == EHTokDot || peek() == EHTokComma) + recedeToken(); + else + expected(";"); + return false; + } + + return true; +} + +// control_declaration +// : fully_specified_type identifier EQUAL expression +// +bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node) +{ + node = nullptr; + TAttributes attributes; + + // fully_specified_type + TType type; + if (! acceptFullySpecifiedType(type, attributes)) + return false; + + if (attributes.size() > 0) + parseContext.warn(token.loc, "attributes don't apply to control declaration", "", ""); + + // filter out type casts + if (peekTokenClass(EHTokLeftParen)) { + recedeToken(); + return false; + } + + // identifier + HlslToken idToken; + if (! acceptIdentifier(idToken)) { + expected("identifier"); + return false; + } + + // EQUAL + TIntermTyped* expressionNode = nullptr; + if (! acceptTokenClass(EHTokAssign)) { + expected("="); + return false; + } + + // expression + if (! acceptExpression(expressionNode)) { + expected("initializer"); + return false; + } + + node = parseContext.declareVariable(idToken.loc, *idToken.string, type, expressionNode); + + return true; +} + +// fully_specified_type +// : type_specifier +// | type_qualifier type_specifier +// | type_specifier type_qualifier +// +bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes) +{ + TIntermNode* nodeList = nullptr; + return acceptFullySpecifiedType(type, nodeList, attributes); +} +bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators) +{ + // type_qualifier + TQualifier qualifier; + qualifier.clear(); + if (! acceptPreQualifier(qualifier)) + return false; + TSourceLoc loc = token.loc; + + // type_specifier + if (! acceptType(type, nodeList)) { + // If this is not a type, we may have inadvertently gone down a wrong path + // by parsing "sample", which can be treated like either an identifier or a + // qualifier. Back it out, if we did. + if (qualifier.sample) + recedeToken(); + + return false; + } + + // type_qualifier + if (! acceptPostQualifier(qualifier)) + return false; + + if (type.getBasicType() == EbtBlock) { + // the type was a block, which set some parts of the qualifier + parseContext.mergeQualifiers(type.getQualifier(), qualifier); + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, type); + + // further, it can create an anonymous instance of the block + // (cbuffer and tbuffer don't consume the next identifier, and + // should set forbidDeclarators) + if (forbidDeclarators || peek() != EHTokIdentifier) + parseContext.declareBlock(loc, type); + } else { + // Some qualifiers are set when parsing the type. Merge those with + // whatever comes from acceptPreQualifier and acceptPostQualifier. + assert(qualifier.layoutFormat == ElfNone); + + qualifier.layoutFormat = type.getQualifier().layoutFormat; + qualifier.precision = type.getQualifier().precision; + + if (type.getQualifier().storage == EvqOut || + type.getQualifier().storage == EvqBuffer) { + qualifier.storage = type.getQualifier().storage; + qualifier.readonly = type.getQualifier().readonly; + } + + if (type.isBuiltIn()) + qualifier.builtIn = type.getQualifier().builtIn; + + type.getQualifier() = qualifier; + } + + return true; +} + +// type_qualifier +// : qualifier qualifier ... +// +// Zero or more of these, so this can't return false. +// +bool HlslGrammar::acceptPreQualifier(TQualifier& qualifier) +{ + do { + switch (peek()) { + case EHTokStatic: + qualifier.storage = EvqGlobal; + break; + case EHTokExtern: + // TODO: no meaning in glslang? + break; + case EHTokShared: + // TODO: hint + break; + case EHTokGroupShared: + qualifier.storage = EvqShared; + break; + case EHTokUniform: + qualifier.storage = EvqUniform; + break; + case EHTokConst: + qualifier.storage = EvqConst; + break; + case EHTokVolatile: + qualifier.volatil = true; + break; + case EHTokLinear: + qualifier.smooth = true; + break; + case EHTokCentroid: + qualifier.centroid = true; + break; + case EHTokNointerpolation: + qualifier.flat = true; + break; + case EHTokNoperspective: + qualifier.nopersp = true; + break; + case EHTokSample: + qualifier.sample = true; + break; + case EHTokRowMajor: + qualifier.layoutMatrix = ElmColumnMajor; + break; + case EHTokColumnMajor: + qualifier.layoutMatrix = ElmRowMajor; + break; + case EHTokPrecise: + qualifier.noContraction = true; + break; + case EHTokIn: + if (qualifier.storage != EvqUniform) { + qualifier.storage = (qualifier.storage == EvqOut) ? EvqInOut : EvqIn; + } + break; + case EHTokOut: + qualifier.storage = (qualifier.storage == EvqIn) ? EvqInOut : EvqOut; + break; + case EHTokInOut: + qualifier.storage = EvqInOut; + break; + case EHTokLayout: + if (! acceptLayoutQualifierList(qualifier)) + return false; + continue; + case EHTokGloballyCoherent: + qualifier.coherent = true; + break; + case EHTokInline: + // TODO: map this to SPIR-V function control + break; + + // GS geometries: these are specified on stage input variables, and are an error (not verified here) + // for output variables. + case EHTokPoint: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgPoints)) + return false; + break; + case EHTokLine: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgLines)) + return false; + break; + case EHTokTriangle: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgTriangles)) + return false; + break; + case EHTokLineAdj: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgLinesAdjacency)) + return false; + break; + case EHTokTriangleAdj: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgTrianglesAdjacency)) + return false; + break; + + default: + return true; + } + advanceToken(); + } while (true); +} + +// type_qualifier +// : qualifier qualifier ... +// +// Zero or more of these, so this can't return false. +// +bool HlslGrammar::acceptPostQualifier(TQualifier& qualifier) +{ + do { + switch (peek()) { + case EHTokConst: + qualifier.storage = EvqConst; + break; + default: + return true; + } + advanceToken(); + } while (true); +} + +// layout_qualifier_list +// : LAYOUT LEFT_PAREN layout_qualifier COMMA layout_qualifier ... RIGHT_PAREN +// +// layout_qualifier +// : identifier +// | identifier EQUAL expression +// +// Zero or more of these, so this can't return false. +// +bool HlslGrammar::acceptLayoutQualifierList(TQualifier& qualifier) +{ + if (! acceptTokenClass(EHTokLayout)) + return false; + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + do { + // identifier + HlslToken idToken; + if (! acceptIdentifier(idToken)) + break; + + // EQUAL expression + if (acceptTokenClass(EHTokAssign)) { + TIntermTyped* expr; + if (! acceptConditionalExpression(expr)) { + expected("expression"); + return false; + } + parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string, expr); + } else + parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string); + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +// template_type +// : FLOAT +// | DOUBLE +// | INT +// | DWORD +// | UINT +// | BOOL +// +bool HlslGrammar::acceptTemplateVecMatBasicType(TBasicType& basicType, + TPrecisionQualifier& precision) +{ + precision = EpqNone; + switch (peek()) { + case EHTokFloat: + basicType = EbtFloat; + break; + case EHTokDouble: + basicType = EbtDouble; + break; + case EHTokInt: + case EHTokDword: + basicType = EbtInt; + break; + case EHTokUint: + basicType = EbtUint; + break; + case EHTokBool: + basicType = EbtBool; + break; + case EHTokHalf: + basicType = parseContext.hlslEnable16BitTypes() ? EbtFloat16 : EbtFloat; + break; + case EHTokMin16float: + case EHTokMin10float: + basicType = parseContext.hlslEnable16BitTypes() ? EbtFloat16 : EbtFloat; + precision = EpqMedium; + break; + case EHTokMin16int: + case EHTokMin12int: + basicType = parseContext.hlslEnable16BitTypes() ? EbtInt16 : EbtInt; + precision = EpqMedium; + break; + case EHTokMin16uint: + basicType = parseContext.hlslEnable16BitTypes() ? EbtUint16 : EbtUint; + precision = EpqMedium; + break; + default: + return false; + } + + advanceToken(); + + return true; +} + +// vector_template_type +// : VECTOR +// | VECTOR LEFT_ANGLE template_type COMMA integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptVectorTemplateType(TType& type) +{ + if (! acceptTokenClass(EHTokVector)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + // in HLSL, 'vector' alone means float4. + new(&type) TType(EbtFloat, EvqTemporary, 4); + return true; + } + + TBasicType basicType; + TPrecisionQualifier precision; + if (! acceptTemplateVecMatBasicType(basicType, precision)) { + expected("scalar type"); + return false; + } + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* vecSize; + if (! acceptLiteral(vecSize)) + return false; + + const int vecSizeI = vecSize->getAsConstantUnion()->getConstArray()[0].getIConst(); + + new(&type) TType(basicType, EvqTemporary, precision, vecSizeI); + + if (vecSizeI == 1) + type.makeVector(); + + if (!acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// matrix_template_type +// : MATRIX +// | MATRIX LEFT_ANGLE template_type COMMA integer_literal COMMA integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptMatrixTemplateType(TType& type) +{ + if (! acceptTokenClass(EHTokMatrix)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + // in HLSL, 'matrix' alone means float4x4. + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); + return true; + } + + TBasicType basicType; + TPrecisionQualifier precision; + if (! acceptTemplateVecMatBasicType(basicType, precision)) { + expected("scalar type"); + return false; + } + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer rows + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* rows; + if (! acceptLiteral(rows)) + return false; + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer cols + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* cols; + if (! acceptLiteral(cols)) + return false; + + new(&type) TType(basicType, EvqTemporary, precision, 0, + rows->getAsConstantUnion()->getConstArray()[0].getIConst(), + cols->getAsConstantUnion()->getConstArray()[0].getIConst()); + + if (!acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// layout_geometry +// : LINESTREAM +// | POINTSTREAM +// | TRIANGLESTREAM +// +bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry) +{ + // read geometry type + const EHlslTokenClass geometryType = peek(); + + switch (geometryType) { + case EHTokPointStream: geometry = ElgPoints; break; + case EHTokLineStream: geometry = ElgLineStrip; break; + case EHTokTriangleStream: geometry = ElgTriangleStrip; break; + default: + return false; // not a layout geometry + } + + advanceToken(); // consume the layout keyword + return true; +} + +// tessellation_decl_type +// : INPUTPATCH +// | OUTPUTPATCH +// +bool HlslGrammar::acceptTessellationDeclType(TBuiltInVariable& patchType) +{ + // read geometry type + const EHlslTokenClass tessType = peek(); + + switch (tessType) { + case EHTokInputPatch: patchType = EbvInputPatch; break; + case EHTokOutputPatch: patchType = EbvOutputPatch; break; + default: + return false; // not a tessellation decl + } + + advanceToken(); // consume the keyword + return true; +} + +// tessellation_patch_template_type +// : tessellation_decl_type LEFT_ANGLE type comma integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type) +{ + TBuiltInVariable patchType; + + if (! acceptTessellationDeclType(patchType)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + if (! acceptType(type)) { + expected("tessellation patch type"); + return false; + } + + if (! acceptTokenClass(EHTokComma)) + return false; + + // integer size + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* size; + if (! acceptLiteral(size)) + return false; + + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst()); + type.transferArraySizes(arraySizes); + type.getQualifier().builtIn = patchType; + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// stream_out_template_type +// : output_primitive_geometry_type LEFT_ANGLE type RIGHT_ANGLE +// +bool HlslGrammar::acceptStreamOutTemplateType(TType& type, TLayoutGeometry& geometry) +{ + geometry = ElgNone; + + if (! acceptOutputPrimitiveGeometry(geometry)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + if (! acceptType(type)) { + expected("stream output type"); + return false; + } + + type.getQualifier().storage = EvqOut; + type.getQualifier().builtIn = EbvGsOutputStream; + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// annotations +// : LEFT_ANGLE declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE +// +bool HlslGrammar::acceptAnnotations(TQualifier&) +{ + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + // note that we are nesting a name space + parseContext.nestAnnotations(); + + // declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE + do { + // eat any extra SEMI_COLON; don't know if the grammar calls for this or not + while (acceptTokenClass(EHTokSemicolon)) + ; + + if (acceptTokenClass(EHTokRightAngle)) + break; + + // declaration + TIntermNode* node = nullptr; + if (! acceptDeclaration(node)) { + expected("declaration in annotation"); + return false; + } + } while (true); + + parseContext.unnestAnnotations(); + return true; +} + +// subpass input type +// : SUBPASSINPUT +// | SUBPASSINPUT VECTOR LEFT_ANGLE template_type RIGHT_ANGLE +// | SUBPASSINPUTMS +// | SUBPASSINPUTMS VECTOR LEFT_ANGLE template_type RIGHT_ANGLE +bool HlslGrammar::acceptSubpassInputType(TType& type) +{ + // read subpass type + const EHlslTokenClass subpassInputType = peek(); + + bool multisample; + + switch (subpassInputType) { + case EHTokSubpassInput: multisample = false; break; + case EHTokSubpassInputMS: multisample = true; break; + default: + return false; // not a subpass input declaration + } + + advanceToken(); // consume the sampler type keyword + + TType subpassType(EbtFloat, EvqUniform, 4); // default type is float4 + + if (acceptTokenClass(EHTokLeftAngle)) { + if (! acceptType(subpassType)) { + expected("scalar or vector type"); + return false; + } + + const TBasicType basicRetType = subpassType.getBasicType() ; + + switch (basicRetType) { + case EbtFloat: + case EbtUint: + case EbtInt: + case EbtStruct: + break; + default: + unimplemented("basic type in subpass input"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } + + const TBasicType subpassBasicType = subpassType.isStruct() ? (*subpassType.getStruct())[0].type->getBasicType() + : subpassType.getBasicType(); + + TSampler sampler; + sampler.setSubpass(subpassBasicType, multisample); + + // Remember the declared return type. Function returns false on error. + if (!parseContext.setTextureReturnType(sampler, subpassType, token.loc)) + return false; + + type.shallowCopy(TType(sampler, EvqUniform)); + + return true; +} + +// sampler_type for DX9 compatibility +// : SAMPLER +// | SAMPLER1D +// | SAMPLER2D +// | SAMPLER3D +// | SAMPLERCUBE +bool HlslGrammar::acceptSamplerTypeDX9(TType &type) +{ + // read sampler type + const EHlslTokenClass samplerType = peek(); + + TSamplerDim dim = EsdNone; + TType txType(EbtFloat, EvqUniform, 4); // default type is float4 + + bool isShadow = false; + + switch (samplerType) + { + case EHTokSampler: dim = Esd2D; break; + case EHTokSampler1d: dim = Esd1D; break; + case EHTokSampler2d: dim = Esd2D; break; + case EHTokSampler3d: dim = Esd3D; break; + case EHTokSamplerCube: dim = EsdCube; break; + default: + return false; // not a dx9 sampler declaration + } + + advanceToken(); // consume the sampler type keyword + + TArraySizes *arraySizes = nullptr; // TODO: array + + TSampler sampler; + sampler.set(txType.getBasicType(), dim, false, isShadow, false); + + if (!parseContext.setTextureReturnType(sampler, txType, token.loc)) + return false; + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + type.getQualifier().layoutFormat = ElfNone; + + return true; +} + +// sampler_type +// : SAMPLER +// | SAMPLER1D +// | SAMPLER2D +// | SAMPLER3D +// | SAMPLERCUBE +// | SAMPLERSTATE +// | SAMPLERCOMPARISONSTATE +bool HlslGrammar::acceptSamplerType(TType& type) +{ + // read sampler type + const EHlslTokenClass samplerType = peek(); + + // TODO: for DX9 + // TSamplerDim dim = EsdNone; + + bool isShadow = false; + + switch (samplerType) { + case EHTokSampler: break; + case EHTokSampler1d: /*dim = Esd1D*/; break; + case EHTokSampler2d: /*dim = Esd2D*/; break; + case EHTokSampler3d: /*dim = Esd3D*/; break; + case EHTokSamplerCube: /*dim = EsdCube*/; break; + case EHTokSamplerState: break; + case EHTokSamplerComparisonState: isShadow = true; break; + default: + return false; // not a sampler declaration + } + + advanceToken(); // consume the sampler type keyword + + TArraySizes* arraySizes = nullptr; // TODO: array + + TSampler sampler; + sampler.setPureSampler(isShadow); + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + + return true; +} + +// texture_type +// | BUFFER +// | TEXTURE1D +// | TEXTURE1DARRAY +// | TEXTURE2D +// | TEXTURE2DARRAY +// | TEXTURE3D +// | TEXTURECUBE +// | TEXTURECUBEARRAY +// | TEXTURE2DMS +// | TEXTURE2DMSARRAY +// | RWBUFFER +// | RWTEXTURE1D +// | RWTEXTURE1DARRAY +// | RWTEXTURE2D +// | RWTEXTURE2DARRAY +// | RWTEXTURE3D + +bool HlslGrammar::acceptTextureType(TType& type) +{ + const EHlslTokenClass textureType = peek(); + + TSamplerDim dim = EsdNone; + bool array = false; + bool ms = false; + bool image = false; + bool combined = true; + + switch (textureType) { + case EHTokBuffer: dim = EsdBuffer; combined = false; break; + case EHTokTexture1d: dim = Esd1D; break; + case EHTokTexture1darray: dim = Esd1D; array = true; break; + case EHTokTexture2d: dim = Esd2D; break; + case EHTokTexture2darray: dim = Esd2D; array = true; break; + case EHTokTexture3d: dim = Esd3D; break; + case EHTokTextureCube: dim = EsdCube; break; + case EHTokTextureCubearray: dim = EsdCube; array = true; break; + case EHTokTexture2DMS: dim = Esd2D; ms = true; break; + case EHTokTexture2DMSarray: dim = Esd2D; array = true; ms = true; break; + case EHTokRWBuffer: dim = EsdBuffer; image=true; break; + case EHTokRWTexture1d: dim = Esd1D; array=false; image=true; break; + case EHTokRWTexture1darray: dim = Esd1D; array=true; image=true; break; + case EHTokRWTexture2d: dim = Esd2D; array=false; image=true; break; + case EHTokRWTexture2darray: dim = Esd2D; array=true; image=true; break; + case EHTokRWTexture3d: dim = Esd3D; array=false; image=true; break; + default: + return false; // not a texture declaration + } + + advanceToken(); // consume the texture object keyword + + TType txType(EbtFloat, EvqUniform, 4); // default type is float4 + + TIntermTyped* msCount = nullptr; + + // texture type: required for multisample types and RWBuffer/RWTextures! + if (acceptTokenClass(EHTokLeftAngle)) { + if (! acceptType(txType)) { + expected("scalar or vector type"); + return false; + } + + const TBasicType basicRetType = txType.getBasicType() ; + + switch (basicRetType) { + case EbtFloat: + case EbtUint: + case EbtInt: + case EbtStruct: + break; + default: + unimplemented("basic type in texture"); + return false; + } + + // Buffers can handle small mats if they fit in 4 components + if (dim == EsdBuffer && txType.isMatrix()) { + if ((txType.getMatrixCols() * txType.getMatrixRows()) > 4) { + expected("components < 4 in matrix buffer type"); + return false; + } + + // TODO: except we don't handle it yet... + unimplemented("matrix type in buffer"); + return false; + } + + if (!txType.isScalar() && !txType.isVector() && !txType.isStruct()) { + expected("scalar, vector, or struct type"); + return false; + } + + if (ms && acceptTokenClass(EHTokComma)) { + // read sample count for multisample types, if given + if (! peekTokenClass(EHTokIntConstant)) { + expected("multisample count"); + return false; + } + + if (! acceptLiteral(msCount)) // should never fail, since we just found an integer + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } else if (ms) { + expected("texture type for multisample"); + return false; + } else if (image) { + expected("type for RWTexture/RWBuffer"); + return false; + } + + TArraySizes* arraySizes = nullptr; + const bool shadow = false; // declared on the sampler + + TSampler sampler; + TLayoutFormat format = ElfNone; + + // Buffer, RWBuffer and RWTexture (images) require a TLayoutFormat. We handle only a limit set. + if (image || dim == EsdBuffer) + format = parseContext.getLayoutFromTxType(token.loc, txType); + + const TBasicType txBasicType = txType.isStruct() ? (*txType.getStruct())[0].type->getBasicType() + : txType.getBasicType(); + + // Non-image Buffers are combined + if (dim == EsdBuffer && !image) { + sampler.set(txType.getBasicType(), dim, array); + } else { + // DX10 textures are separated. TODO: DX9. + if (image) { + sampler.setImage(txBasicType, dim, array, shadow, ms); + } else { + sampler.setTexture(txBasicType, dim, array, shadow, ms); + } + } + + // Remember the declared return type. Function returns false on error. + if (!parseContext.setTextureReturnType(sampler, txType, token.loc)) + return false; + + // Force uncombined, if necessary + if (!combined) + sampler.combined = false; + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + type.getQualifier().layoutFormat = format; + + return true; +} + +// If token is for a type, update 'type' with the type information, +// and return true and advance. +// Otherwise, return false, and don't advance +bool HlslGrammar::acceptType(TType& type) +{ + TIntermNode* nodeList = nullptr; + return acceptType(type, nodeList); +} +bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList) +{ + // Basic types for min* types, use native halfs if the option allows them. + bool enable16BitTypes = parseContext.hlslEnable16BitTypes(); + + const TBasicType min16float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType min10float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType half_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType min16int_bt = enable16BitTypes ? EbtInt16 : EbtInt; + const TBasicType min12int_bt = enable16BitTypes ? EbtInt16 : EbtInt; + const TBasicType min16uint_bt = enable16BitTypes ? EbtUint16 : EbtUint; + + // Some types might have turned into identifiers. Take the hit for checking + // when this has happened. + if (typeIdentifiers) { + const char* identifierString = getTypeString(peek()); + if (identifierString != nullptr) { + TString name = identifierString; + // if it's an identifier, it's not a type + if (parseContext.symbolTable.find(name) != nullptr) + return false; + } + } + + bool isUnorm = false; + bool isSnorm = false; + + // Accept snorm and unorm. Presently, this is ignored, save for an error check below. + switch (peek()) { + case EHTokUnorm: + isUnorm = true; + advanceToken(); // eat the token + break; + case EHTokSNorm: + isSnorm = true; + advanceToken(); // eat the token + break; + default: + break; + } + + switch (peek()) { + case EHTokVector: + return acceptVectorTemplateType(type); + break; + + case EHTokMatrix: + return acceptMatrixTemplateType(type); + break; + + case EHTokPointStream: // fall through + case EHTokLineStream: // ... + case EHTokTriangleStream: // ... + { + TLayoutGeometry geometry; + if (! acceptStreamOutTemplateType(type, geometry)) + return false; + + if (! parseContext.handleOutputGeometry(token.loc, geometry)) + return false; + + return true; + } + + case EHTokInputPatch: // fall through + case EHTokOutputPatch: // ... + { + if (! acceptTessellationPatchTemplateType(type)) + return false; + + return true; + } + + case EHTokSampler: // fall through + case EHTokSampler1d: // ... + case EHTokSampler2d: // ... + case EHTokSampler3d: // ... + case EHTokSamplerCube: // ... + if (parseContext.hlslDX9Compatible()) + return acceptSamplerTypeDX9(type); + else + return acceptSamplerType(type); + break; + + case EHTokSamplerState: // fall through + case EHTokSamplerComparisonState: // ... + return acceptSamplerType(type); + break; + + case EHTokSubpassInput: // fall through + case EHTokSubpassInputMS: // ... + return acceptSubpassInputType(type); + break; + + case EHTokBuffer: // fall through + case EHTokTexture1d: // ... + case EHTokTexture1darray: // ... + case EHTokTexture2d: // ... + case EHTokTexture2darray: // ... + case EHTokTexture3d: // ... + case EHTokTextureCube: // ... + case EHTokTextureCubearray: // ... + case EHTokTexture2DMS: // ... + case EHTokTexture2DMSarray: // ... + case EHTokRWTexture1d: // ... + case EHTokRWTexture1darray: // ... + case EHTokRWTexture2d: // ... + case EHTokRWTexture2darray: // ... + case EHTokRWTexture3d: // ... + case EHTokRWBuffer: // ... + return acceptTextureType(type); + break; + + case EHTokAppendStructuredBuffer: + case EHTokByteAddressBuffer: + case EHTokConsumeStructuredBuffer: + case EHTokRWByteAddressBuffer: + case EHTokRWStructuredBuffer: + case EHTokStructuredBuffer: + return acceptStructBufferType(type); + break; + + case EHTokTextureBuffer: + return acceptTextureBufferType(type); + break; + + case EHTokConstantBuffer: + return acceptConstantBufferType(type); + + case EHTokClass: + case EHTokStruct: + case EHTokCBuffer: + case EHTokTBuffer: + return acceptStruct(type, nodeList); + + case EHTokIdentifier: + // An identifier could be for a user-defined type. + // Note we cache the symbol table lookup, to save for a later rule + // when this is not a type. + if (parseContext.lookupUserType(*token.string, type) != nullptr) { + advanceToken(); + return true; + } else + return false; + + case EHTokVoid: + new(&type) TType(EbtVoid); + break; + + case EHTokString: + new(&type) TType(EbtString); + break; + + case EHTokFloat: + new(&type) TType(EbtFloat); + break; + case EHTokFloat1: + new(&type) TType(EbtFloat); + type.makeVector(); + break; + case EHTokFloat2: + new(&type) TType(EbtFloat, EvqTemporary, 2); + break; + case EHTokFloat3: + new(&type) TType(EbtFloat, EvqTemporary, 3); + break; + case EHTokFloat4: + new(&type) TType(EbtFloat, EvqTemporary, 4); + break; + + case EHTokDouble: + new(&type) TType(EbtDouble); + break; + case EHTokDouble1: + new(&type) TType(EbtDouble); + type.makeVector(); + break; + case EHTokDouble2: + new(&type) TType(EbtDouble, EvqTemporary, 2); + break; + case EHTokDouble3: + new(&type) TType(EbtDouble, EvqTemporary, 3); + break; + case EHTokDouble4: + new(&type) TType(EbtDouble, EvqTemporary, 4); + break; + + case EHTokInt: + case EHTokDword: + new(&type) TType(EbtInt); + break; + case EHTokInt1: + new(&type) TType(EbtInt); + type.makeVector(); + break; + case EHTokInt2: + new(&type) TType(EbtInt, EvqTemporary, 2); + break; + case EHTokInt3: + new(&type) TType(EbtInt, EvqTemporary, 3); + break; + case EHTokInt4: + new(&type) TType(EbtInt, EvqTemporary, 4); + break; + + case EHTokUint: + new(&type) TType(EbtUint); + break; + case EHTokUint1: + new(&type) TType(EbtUint); + type.makeVector(); + break; + case EHTokUint2: + new(&type) TType(EbtUint, EvqTemporary, 2); + break; + case EHTokUint3: + new(&type) TType(EbtUint, EvqTemporary, 3); + break; + case EHTokUint4: + new(&type) TType(EbtUint, EvqTemporary, 4); + break; + + case EHTokUint64: + new(&type) TType(EbtUint64); + break; + + case EHTokBool: + new(&type) TType(EbtBool); + break; + case EHTokBool1: + new(&type) TType(EbtBool); + type.makeVector(); + break; + case EHTokBool2: + new(&type) TType(EbtBool, EvqTemporary, 2); + break; + case EHTokBool3: + new(&type) TType(EbtBool, EvqTemporary, 3); + break; + case EHTokBool4: + new(&type) TType(EbtBool, EvqTemporary, 4); + break; + + case EHTokHalf: + new(&type) TType(half_bt, EvqTemporary); + break; + case EHTokHalf1: + new(&type) TType(half_bt, EvqTemporary); + type.makeVector(); + break; + case EHTokHalf2: + new(&type) TType(half_bt, EvqTemporary, 2); + break; + case EHTokHalf3: + new(&type) TType(half_bt, EvqTemporary, 3); + break; + case EHTokHalf4: + new(&type) TType(half_bt, EvqTemporary, 4); + break; + + case EHTokMin16float: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16float1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16float2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16float3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16float4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin10float: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin10float1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin10float2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin10float3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin10float4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin16int: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16int1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16int2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16int3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16int4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin12int: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin12int1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin12int2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin12int3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin12int4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin16uint: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16uint1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16uint2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16uint3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16uint4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokInt1x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 1); + break; + case EHTokInt1x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 2); + break; + case EHTokInt1x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 3); + break; + case EHTokInt1x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 4); + break; + case EHTokInt2x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 1); + break; + case EHTokInt2x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 2); + break; + case EHTokInt2x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 3); + break; + case EHTokInt2x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 4); + break; + case EHTokInt3x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 1); + break; + case EHTokInt3x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 2); + break; + case EHTokInt3x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 3); + break; + case EHTokInt3x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 4); + break; + case EHTokInt4x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 1); + break; + case EHTokInt4x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 2); + break; + case EHTokInt4x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 3); + break; + case EHTokInt4x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 4); + break; + + case EHTokUint1x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 1); + break; + case EHTokUint1x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 2); + break; + case EHTokUint1x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 3); + break; + case EHTokUint1x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 4); + break; + case EHTokUint2x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 1); + break; + case EHTokUint2x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 2); + break; + case EHTokUint2x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 3); + break; + case EHTokUint2x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 4); + break; + case EHTokUint3x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 1); + break; + case EHTokUint3x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 2); + break; + case EHTokUint3x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 3); + break; + case EHTokUint3x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 4); + break; + case EHTokUint4x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 1); + break; + case EHTokUint4x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 2); + break; + case EHTokUint4x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 3); + break; + case EHTokUint4x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 4); + break; + + case EHTokBool1x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 1); + break; + case EHTokBool1x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 2); + break; + case EHTokBool1x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 3); + break; + case EHTokBool1x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 4); + break; + case EHTokBool2x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 1); + break; + case EHTokBool2x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 2); + break; + case EHTokBool2x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 3); + break; + case EHTokBool2x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 4); + break; + case EHTokBool3x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 1); + break; + case EHTokBool3x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 2); + break; + case EHTokBool3x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 3); + break; + case EHTokBool3x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 4); + break; + case EHTokBool4x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 1); + break; + case EHTokBool4x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 2); + break; + case EHTokBool4x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 3); + break; + case EHTokBool4x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 4); + break; + + case EHTokFloat1x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 1); + break; + case EHTokFloat1x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 2); + break; + case EHTokFloat1x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 3); + break; + case EHTokFloat1x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 4); + break; + case EHTokFloat2x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 1); + break; + case EHTokFloat2x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2); + break; + case EHTokFloat2x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3); + break; + case EHTokFloat2x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4); + break; + case EHTokFloat3x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 1); + break; + case EHTokFloat3x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2); + break; + case EHTokFloat3x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3); + break; + case EHTokFloat3x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4); + break; + case EHTokFloat4x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 1); + break; + case EHTokFloat4x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2); + break; + case EHTokFloat4x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3); + break; + case EHTokFloat4x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); + break; + + case EHTokHalf1x1: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 1); + break; + case EHTokHalf1x2: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 2); + break; + case EHTokHalf1x3: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 3); + break; + case EHTokHalf1x4: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 4); + break; + case EHTokHalf2x1: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 1); + break; + case EHTokHalf2x2: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 2); + break; + case EHTokHalf2x3: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 3); + break; + case EHTokHalf2x4: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 4); + break; + case EHTokHalf3x1: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 1); + break; + case EHTokHalf3x2: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 2); + break; + case EHTokHalf3x3: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 3); + break; + case EHTokHalf3x4: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 4); + break; + case EHTokHalf4x1: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 1); + break; + case EHTokHalf4x2: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 2); + break; + case EHTokHalf4x3: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 3); + break; + case EHTokHalf4x4: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 4); + break; + + case EHTokDouble1x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 1); + break; + case EHTokDouble1x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 2); + break; + case EHTokDouble1x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 3); + break; + case EHTokDouble1x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 4); + break; + case EHTokDouble2x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 1); + break; + case EHTokDouble2x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 2); + break; + case EHTokDouble2x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 3); + break; + case EHTokDouble2x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 4); + break; + case EHTokDouble3x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 1); + break; + case EHTokDouble3x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 2); + break; + case EHTokDouble3x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 3); + break; + case EHTokDouble3x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 4); + break; + case EHTokDouble4x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 1); + break; + case EHTokDouble4x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 2); + break; + case EHTokDouble4x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 3); + break; + case EHTokDouble4x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 4); + break; + + case EHTokMin16float1x1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 1, 1); + break; + case EHTokMin16float1x2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 1, 2); + break; + case EHTokMin16float1x3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 1, 3); + break; + case EHTokMin16float1x4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 1, 4); + break; + case EHTokMin16float2x1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 2, 1); + break; + case EHTokMin16float2x2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 2, 2); + break; + case EHTokMin16float2x3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 2, 3); + break; + case EHTokMin16float2x4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 2, 4); + break; + case EHTokMin16float3x1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 3, 1); + break; + case EHTokMin16float3x2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 3, 2); + break; + case EHTokMin16float3x3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 3, 3); + break; + case EHTokMin16float3x4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 3, 4); + break; + case EHTokMin16float4x1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 4, 1); + break; + case EHTokMin16float4x2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 4, 2); + break; + case EHTokMin16float4x3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 4, 3); + break; + case EHTokMin16float4x4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 0, 4, 4); + break; + + case EHTokMin10float1x1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 1, 1); + break; + case EHTokMin10float1x2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 1, 2); + break; + case EHTokMin10float1x3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 1, 3); + break; + case EHTokMin10float1x4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 1, 4); + break; + case EHTokMin10float2x1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 2, 1); + break; + case EHTokMin10float2x2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 2, 2); + break; + case EHTokMin10float2x3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 2, 3); + break; + case EHTokMin10float2x4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 2, 4); + break; + case EHTokMin10float3x1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 3, 1); + break; + case EHTokMin10float3x2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 3, 2); + break; + case EHTokMin10float3x3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 3, 3); + break; + case EHTokMin10float3x4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 3, 4); + break; + case EHTokMin10float4x1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 4, 1); + break; + case EHTokMin10float4x2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 4, 2); + break; + case EHTokMin10float4x3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 4, 3); + break; + case EHTokMin10float4x4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 0, 4, 4); + break; + + case EHTokMin16int1x1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 1, 1); + break; + case EHTokMin16int1x2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 1, 2); + break; + case EHTokMin16int1x3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 1, 3); + break; + case EHTokMin16int1x4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 1, 4); + break; + case EHTokMin16int2x1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 2, 1); + break; + case EHTokMin16int2x2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 2, 2); + break; + case EHTokMin16int2x3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 2, 3); + break; + case EHTokMin16int2x4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 2, 4); + break; + case EHTokMin16int3x1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 3, 1); + break; + case EHTokMin16int3x2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 3, 2); + break; + case EHTokMin16int3x3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 3, 3); + break; + case EHTokMin16int3x4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 3, 4); + break; + case EHTokMin16int4x1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 4, 1); + break; + case EHTokMin16int4x2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 4, 2); + break; + case EHTokMin16int4x3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 4, 3); + break; + case EHTokMin16int4x4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 0, 4, 4); + break; + + case EHTokMin12int1x1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 1, 1); + break; + case EHTokMin12int1x2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 1, 2); + break; + case EHTokMin12int1x3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 1, 3); + break; + case EHTokMin12int1x4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 1, 4); + break; + case EHTokMin12int2x1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 2, 1); + break; + case EHTokMin12int2x2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 2, 2); + break; + case EHTokMin12int2x3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 2, 3); + break; + case EHTokMin12int2x4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 2, 4); + break; + case EHTokMin12int3x1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 3, 1); + break; + case EHTokMin12int3x2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 3, 2); + break; + case EHTokMin12int3x3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 3, 3); + break; + case EHTokMin12int3x4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 3, 4); + break; + case EHTokMin12int4x1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 4, 1); + break; + case EHTokMin12int4x2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 4, 2); + break; + case EHTokMin12int4x3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 4, 3); + break; + case EHTokMin12int4x4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 0, 4, 4); + break; + + case EHTokMin16uint1x1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 1, 1); + break; + case EHTokMin16uint1x2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 1, 2); + break; + case EHTokMin16uint1x3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 1, 3); + break; + case EHTokMin16uint1x4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 1, 4); + break; + case EHTokMin16uint2x1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 2, 1); + break; + case EHTokMin16uint2x2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 2, 2); + break; + case EHTokMin16uint2x3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 2, 3); + break; + case EHTokMin16uint2x4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 2, 4); + break; + case EHTokMin16uint3x1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 3, 1); + break; + case EHTokMin16uint3x2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 3, 2); + break; + case EHTokMin16uint3x3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 3, 3); + break; + case EHTokMin16uint3x4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 3, 4); + break; + case EHTokMin16uint4x1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 4, 1); + break; + case EHTokMin16uint4x2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 4, 2); + break; + case EHTokMin16uint4x3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 4, 3); + break; + case EHTokMin16uint4x4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 0, 4, 4); + break; + + default: + return false; + } + + advanceToken(); + + if ((isUnorm || isSnorm) && !type.isFloatingDomain()) { + parseContext.error(token.loc, "unorm and snorm only valid in floating point domain", "", ""); + return false; + } + + return true; +} + +// struct +// : struct_type IDENTIFIER post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE +// | struct_type post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE +// | struct_type IDENTIFIER // use of previously declared struct type +// +// struct_type +// : STRUCT +// | CLASS +// | CBUFFER +// | TBUFFER +// +bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList) +{ + // This storage qualifier will tell us whether it's an AST + // block type or just a generic structure type. + TStorageQualifier storageQualifier = EvqTemporary; + bool readonly = false; + + if (acceptTokenClass(EHTokCBuffer)) { + // CBUFFER + storageQualifier = EvqUniform; + } else if (acceptTokenClass(EHTokTBuffer)) { + // TBUFFER + storageQualifier = EvqBuffer; + readonly = true; + } else if (! acceptTokenClass(EHTokClass) && ! acceptTokenClass(EHTokStruct)) { + // Neither CLASS nor STRUCT + return false; + } + + // Now known to be one of CBUFFER, TBUFFER, CLASS, or STRUCT + + + // IDENTIFIER. It might also be a keyword which can double as an identifier. + // For example: 'cbuffer ConstantBuffer' or 'struct ConstantBuffer' is legal. + // 'cbuffer int' is also legal, and 'struct int' appears rejected only because + // it attempts to redefine the 'int' type. + const char* idString = getTypeString(peek()); + TString structName = ""; + if (peekTokenClass(EHTokIdentifier) || idString != nullptr) { + if (idString != nullptr) + structName = *idString; + else + structName = *token.string; + advanceToken(); + } + + // post_decls + TQualifier postDeclQualifier; + postDeclQualifier.clear(); + bool postDeclsFound = acceptPostDecls(postDeclQualifier); + + // LEFT_BRACE, or + // struct_type IDENTIFIER + if (! acceptTokenClass(EHTokLeftBrace)) { + if (structName.size() > 0 && !postDeclsFound && parseContext.lookupUserType(structName, type) != nullptr) { + // struct_type IDENTIFIER + return true; + } else { + expected("{"); + return false; + } + } + + + // struct_declaration_list + TTypeList* typeList; + // Save each member function so they can be processed after we have a fully formed 'this'. + TVector functionDeclarators; + + parseContext.pushNamespace(structName); + bool acceptedList = acceptStructDeclarationList(typeList, nodeList, functionDeclarators); + parseContext.popNamespace(); + + if (! acceptedList) { + expected("struct member declarations"); + return false; + } + + // RIGHT_BRACE + if (! acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + + // create the user-defined type + if (storageQualifier == EvqTemporary) + new(&type) TType(typeList, structName); + else { + postDeclQualifier.storage = storageQualifier; + postDeclQualifier.readonly = readonly; + new(&type) TType(typeList, structName, postDeclQualifier); // sets EbtBlock + } + + parseContext.declareStruct(token.loc, structName, type); + + // For member functions: now that we know the type of 'this', go back and + // - add their implicit argument with 'this' (not to the mangling, just the argument list) + // - parse the functions, their tokens were saved for deferred parsing (now) + for (int b = 0; b < (int)functionDeclarators.size(); ++b) { + // update signature + if (functionDeclarators[b].function->hasImplicitThis()) + functionDeclarators[b].function->addThisParameter(type, intermediate.implicitThisName); + } + + // All member functions get parsed inside the class/struct namespace and with the + // class/struct members in a symbol-table level. + parseContext.pushNamespace(structName); + parseContext.pushThisScope(type, functionDeclarators); + bool deferredSuccess = true; + for (int b = 0; b < (int)functionDeclarators.size() && deferredSuccess; ++b) { + // parse body + pushTokenStream(functionDeclarators[b].body); + if (! acceptFunctionBody(functionDeclarators[b], nodeList)) + deferredSuccess = false; + popTokenStream(); + } + parseContext.popThisScope(); + parseContext.popNamespace(); + + return deferredSuccess; +} + +// constantbuffer +// : CONSTANTBUFFER LEFT_ANGLE type RIGHT_ANGLE +bool HlslGrammar::acceptConstantBufferType(TType& type) +{ + if (! acceptTokenClass(EHTokConstantBuffer)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + TType templateType; + if (! acceptType(templateType)) { + expected("type"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + TQualifier postDeclQualifier; + postDeclQualifier.clear(); + postDeclQualifier.storage = EvqUniform; + + if (templateType.isStruct()) { + // Make a block from the type parsed as the template argument + TTypeList* typeList = templateType.getWritableStruct(); + new(&type) TType(typeList, "", postDeclQualifier); // sets EbtBlock + + type.getQualifier().storage = EvqUniform; + + return true; + } else { + parseContext.error(token.loc, "non-structure type in ConstantBuffer", "", ""); + return false; + } +} + +// texture_buffer +// : TEXTUREBUFFER LEFT_ANGLE type RIGHT_ANGLE +bool HlslGrammar::acceptTextureBufferType(TType& type) +{ + if (! acceptTokenClass(EHTokTextureBuffer)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + TType templateType; + if (! acceptType(templateType)) { + expected("type"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + templateType.getQualifier().storage = EvqBuffer; + templateType.getQualifier().readonly = true; + + TType blockType(templateType.getWritableStruct(), "", templateType.getQualifier()); + + blockType.getQualifier().storage = EvqBuffer; + blockType.getQualifier().readonly = true; + + type.shallowCopy(blockType); + + return true; +} + + +// struct_buffer +// : APPENDSTRUCTUREDBUFFER +// | BYTEADDRESSBUFFER +// | CONSUMESTRUCTUREDBUFFER +// | RWBYTEADDRESSBUFFER +// | RWSTRUCTUREDBUFFER +// | STRUCTUREDBUFFER +bool HlslGrammar::acceptStructBufferType(TType& type) +{ + const EHlslTokenClass structBuffType = peek(); + + // TODO: globallycoherent + bool hasTemplateType = true; + bool readonly = false; + + TStorageQualifier storage = EvqBuffer; + TBuiltInVariable builtinType = EbvNone; + + switch (structBuffType) { + case EHTokAppendStructuredBuffer: + builtinType = EbvAppendConsume; + break; + case EHTokByteAddressBuffer: + hasTemplateType = false; + readonly = true; + builtinType = EbvByteAddressBuffer; + break; + case EHTokConsumeStructuredBuffer: + builtinType = EbvAppendConsume; + break; + case EHTokRWByteAddressBuffer: + hasTemplateType = false; + builtinType = EbvRWByteAddressBuffer; + break; + case EHTokRWStructuredBuffer: + builtinType = EbvRWStructuredBuffer; + break; + case EHTokStructuredBuffer: + builtinType = EbvStructuredBuffer; + readonly = true; + break; + default: + return false; // not a structure buffer type + } + + advanceToken(); // consume the structure keyword + + // type on which this StructedBuffer is templatized. E.g, StructedBuffer ==> MyStruct + TType* templateType = new TType; + + if (hasTemplateType) { + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + if (! acceptType(*templateType)) { + expected("type"); + return false; + } + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } else { + // byte address buffers have no explicit type. + TType uintType(EbtUint, storage); + templateType->shallowCopy(uintType); + } + + // Create an unsized array out of that type. + // TODO: does this work if it's already an array type? + TArraySizes* unsizedArray = new TArraySizes; + unsizedArray->addInnerSize(UnsizedArraySize); + templateType->transferArraySizes(unsizedArray); + templateType->getQualifier().storage = storage; + + // field name is canonical for all structbuffers + templateType->setFieldName("@data"); + + TTypeList* blockStruct = new TTypeList; + TTypeLoc member = { templateType, token.loc }; + blockStruct->push_back(member); + + // This is the type of the buffer block (SSBO) + TType blockType(blockStruct, "", templateType->getQualifier()); + + blockType.getQualifier().storage = storage; + blockType.getQualifier().readonly = readonly; + blockType.getQualifier().builtIn = builtinType; + + // We may have created an equivalent type before, in which case we should use its + // deep structure. + parseContext.shareStructBufferType(blockType); + + type.shallowCopy(blockType); + + return true; +} + +// struct_declaration_list +// : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ... +// +// struct_declaration +// : attributes fully_specified_type struct_declarator COMMA struct_declarator ... +// | attributes fully_specified_type IDENTIFIER function_parameters post_decls compound_statement // member-function definition +// +// struct_declarator +// : IDENTIFIER post_decls +// | IDENTIFIER array_specifier post_decls +// | IDENTIFIER function_parameters post_decls // member-function prototype +// +bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*& nodeList, + TVector& declarators) +{ + typeList = new TTypeList(); + HlslToken idToken; + + do { + // success on seeing the RIGHT_BRACE coming up + if (peekTokenClass(EHTokRightBrace)) + break; + + // struct_declaration + + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + bool declarator_list = false; + + // fully_specified_type + TType memberType; + if (! acceptFullySpecifiedType(memberType, nodeList, attributes)) { + expected("member type"); + return false; + } + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, memberType); + + // struct_declarator COMMA struct_declarator ... + bool functionDefinitionAccepted = false; + do { + if (! acceptIdentifier(idToken)) { + expected("member name"); + return false; + } + + if (peekTokenClass(EHTokLeftParen)) { + // function_parameters + if (!declarator_list) { + declarators.resize(declarators.size() + 1); + // request a token stream for deferred processing + functionDefinitionAccepted = acceptMemberFunctionDefinition(nodeList, memberType, *idToken.string, + declarators.back()); + if (functionDefinitionAccepted) + break; + } + expected("member-function definition"); + return false; + } else { + // add it to the list of members + TTypeLoc member = { new TType(EbtVoid), token.loc }; + member.type->shallowCopy(memberType); + member.type->setFieldName(*idToken.string); + typeList->push_back(member); + + // array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes) + typeList->back().type->transferArraySizes(arraySizes); + + acceptPostDecls(member.type->getQualifier()); + + // EQUAL assignment_expression + if (acceptTokenClass(EHTokAssign)) { + parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", ""); + TIntermTyped* expressionNode = nullptr; + if (! acceptAssignmentExpression(expressionNode)) { + expected("initializer"); + return false; + } + } + } + // success on seeing the SEMICOLON coming up + if (peekTokenClass(EHTokSemicolon)) + break; + + // COMMA + if (acceptTokenClass(EHTokComma)) + declarator_list = true; + else { + expected(","); + return false; + } + + } while (true); + + // SEMI_COLON + if (! functionDefinitionAccepted && ! acceptTokenClass(EHTokSemicolon)) { + expected(";"); + return false; + } + + } while (true); + + return true; +} + +// member_function_definition +// | function_parameters post_decls compound_statement +// +// Expects type to have EvqGlobal for a static member and +// EvqTemporary for non-static member. +bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType& type, TString& memberName, + TFunctionDeclarator& declarator) +{ + bool accepted = false; + + TString* functionName = &memberName; + parseContext.getFullNamespaceName(functionName); + declarator.function = new TFunction(functionName, type); + if (type.getQualifier().storage == EvqTemporary) + declarator.function->setImplicitThis(); + else + declarator.function->setIllegalImplicitThis(); + + // function_parameters + if (acceptFunctionParameters(*declarator.function)) { + // post_decls + acceptPostDecls(declarator.function->getWritableType().getQualifier()); + + // compound_statement (function body definition) + if (peekTokenClass(EHTokLeftBrace)) { + declarator.loc = token.loc; + declarator.body = new TVector; + accepted = acceptFunctionDefinition(declarator, nodeList, declarator.body); + } + } else + expected("function parameter list"); + + return accepted; +} + +// function_parameters +// : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN +// | LEFT_PAREN VOID RIGHT_PAREN +// +bool HlslGrammar::acceptFunctionParameters(TFunction& function) +{ + parseContext.beginParameterParsing(function); + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + // VOID RIGHT_PAREN + if (! acceptTokenClass(EHTokVoid)) { + do { + // parameter_declaration + if (! acceptParameterDeclaration(function)) + break; + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +// default_parameter_declaration +// : EQUAL conditional_expression +// : EQUAL initializer +bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTyped*& node) +{ + node = nullptr; + + // Valid not to have a default_parameter_declaration + if (!acceptTokenClass(EHTokAssign)) + return true; + + if (!acceptConditionalExpression(node)) { + if (!acceptInitializer(node)) + return false; + + // For initializer lists, we have to const-fold into a constructor for the type, so build + // that. + TFunction* constructor = parseContext.makeConstructorCall(token.loc, type); + if (constructor == nullptr) // cannot construct + return false; + + TIntermTyped* arguments = nullptr; + for (int i = 0; i < int(node->getAsAggregate()->getSequence().size()); i++) + parseContext.handleFunctionArgument(constructor, arguments, node->getAsAggregate()->getSequence()[i]->getAsTyped()); + + node = parseContext.handleFunctionCall(token.loc, constructor, node); + } + + if (node == nullptr) + return false; + + // If this is simply a constant, we can use it directly. + if (node->getAsConstantUnion()) + return true; + + // Otherwise, it has to be const-foldable. + TIntermTyped* origNode = node; + + node = intermediate.fold(node->getAsAggregate()); + + if (node != nullptr && origNode != node) + return true; + + parseContext.error(token.loc, "invalid default parameter value", "", ""); + + return false; +} + +// parameter_declaration +// : attributes attributed_declaration +// +// attributed_declaration +// : fully_specified_type post_decls [ = default_parameter_declaration ] +// | fully_specified_type identifier array_specifier post_decls [ = default_parameter_declaration ] +// +bool HlslGrammar::acceptParameterDeclaration(TFunction& function) +{ + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + // fully_specified_type + TType* type = new TType; + if (! acceptFullySpecifiedType(*type, attributes)) + return false; + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, *type); + + // identifier + HlslToken idToken; + acceptIdentifier(idToken); + + // array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes) { + if (arraySizes->hasUnsized()) { + parseContext.error(token.loc, "function parameter requires array size", "[]", ""); + return false; + } + + type->transferArraySizes(arraySizes); + } + + // post_decls + acceptPostDecls(type->getQualifier()); + + TIntermTyped* defaultValue; + if (!acceptDefaultParameterDeclaration(*type, defaultValue)) + return false; + + parseContext.paramFix(*type); + + // If any prior parameters have default values, all the parameters after that must as well. + if (defaultValue == nullptr && function.getDefaultParamCount() > 0) { + parseContext.error(idToken.loc, "invalid parameter after default value parameters", idToken.string->c_str(), ""); + return false; + } + + TParameter param = { idToken.string, type, defaultValue }; + function.addParameter(param); + + return true; +} + +// Do the work to create the function definition in addition to +// parsing the body (compound_statement). +// +// If 'deferredTokens' are passed in, just get the token stream, +// don't process. +// +bool HlslGrammar::acceptFunctionDefinition(TFunctionDeclarator& declarator, TIntermNode*& nodeList, + TVector* deferredTokens) +{ + parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, false /* not prototype */); + + if (deferredTokens) + return captureBlockTokens(*deferredTokens); + else + return acceptFunctionBody(declarator, nodeList); +} + +bool HlslGrammar::acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList) +{ + // we might get back an entry-point + TIntermNode* entryPointNode = nullptr; + + // This does a pushScope() + TIntermNode* functionNode = parseContext.handleFunctionDefinition(declarator.loc, *declarator.function, + declarator.attributes, entryPointNode); + + // compound_statement + TIntermNode* functionBody = nullptr; + if (! acceptCompoundStatement(functionBody)) + return false; + + // this does a popScope() + parseContext.handleFunctionBody(declarator.loc, *declarator.function, functionBody, functionNode); + + // Hook up the 1 or 2 function definitions. + nodeList = intermediate.growAggregate(nodeList, functionNode); + nodeList = intermediate.growAggregate(nodeList, entryPointNode); + + return true; +} + +// Accept an expression with parenthesis around it, where +// the parenthesis ARE NOT expression parenthesis, but the +// syntactically required ones like in "if ( expression )". +// +// Also accepts a declaration expression; "if (int a = expression)". +// +// Note this one is not set up to be speculative; as it gives +// errors if not found. +// +bool HlslGrammar::acceptParenExpression(TIntermTyped*& expression) +{ + expression = nullptr; + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + bool decl = false; + TIntermNode* declNode = nullptr; + decl = acceptControlDeclaration(declNode); + if (decl) { + if (declNode == nullptr || declNode->getAsTyped() == nullptr) { + expected("initialized declaration"); + return false; + } else + expression = declNode->getAsTyped(); + } else { + // no declaration + if (! acceptExpression(expression)) { + expected("expression"); + return false; + } + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + return true; +} + +// The top-level full expression recognizer. +// +// expression +// : assignment_expression COMMA assignment_expression COMMA assignment_expression ... +// +bool HlslGrammar::acceptExpression(TIntermTyped*& node) +{ + node = nullptr; + + // assignment_expression + if (! acceptAssignmentExpression(node)) + return false; + + if (! peekTokenClass(EHTokComma)) + return true; + + do { + // ... COMMA + TSourceLoc loc = token.loc; + advanceToken(); + + // ... assignment_expression + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = intermediate.addComma(node, rightNode, loc); + + if (! peekTokenClass(EHTokComma)) + return true; + } while (true); +} + +// initializer +// : LEFT_BRACE RIGHT_BRACE +// | LEFT_BRACE initializer_list RIGHT_BRACE +// +// initializer_list +// : assignment_expression COMMA assignment_expression COMMA ... +// +bool HlslGrammar::acceptInitializer(TIntermTyped*& node) +{ + // LEFT_BRACE + if (! acceptTokenClass(EHTokLeftBrace)) + return false; + + // RIGHT_BRACE + TSourceLoc loc = token.loc; + if (acceptTokenClass(EHTokRightBrace)) { + // a zero-length initializer list + node = intermediate.makeAggregate(loc); + return true; + } + + // initializer_list + node = nullptr; + do { + // assignment_expression + TIntermTyped* expr; + if (! acceptAssignmentExpression(expr)) { + expected("assignment expression in initializer list"); + return false; + } + + const bool firstNode = (node == nullptr); + + node = intermediate.growAggregate(node, expr, loc); + + // If every sub-node in the list has qualifier EvqConst, the returned node becomes + // EvqConst. Otherwise, it becomes EvqTemporary. That doesn't happen with e.g. + // EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are. + if (firstNode && expr->getQualifier().storage == EvqConst) + node->getQualifier().storage = EvqConst; + else if (expr->getQualifier().storage != EvqConst) + node->getQualifier().storage = EvqTemporary; + + // COMMA + if (acceptTokenClass(EHTokComma)) { + if (acceptTokenClass(EHTokRightBrace)) // allow trailing comma + return true; + continue; + } + + // RIGHT_BRACE + if (acceptTokenClass(EHTokRightBrace)) + return true; + + expected(", or }"); + return false; + } while (true); +} + +// Accept an assignment expression, where assignment operations +// associate right-to-left. That is, it is implicit, for example +// +// a op (b op (c op d)) +// +// assigment_expression +// : initializer +// | conditional_expression +// | conditional_expression assign_op conditional_expression assign_op conditional_expression ... +// +bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node) +{ + // initializer + if (peekTokenClass(EHTokLeftBrace)) { + if (acceptInitializer(node)) + return true; + + expected("initializer"); + return false; + } + + // conditional_expression + if (! acceptConditionalExpression(node)) + return false; + + // assignment operation? + TOperator assignOp = HlslOpMap::assignment(peek()); + if (assignOp == EOpNull) + return true; + + // assign_op + TSourceLoc loc = token.loc; + advanceToken(); + + // conditional_expression assign_op conditional_expression ... + // Done by recursing this function, which automatically + // gets the right-to-left associativity. + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = parseContext.handleAssign(loc, assignOp, node, rightNode); + node = parseContext.handleLvalue(loc, "assign", node); + + if (node == nullptr) { + parseContext.error(loc, "could not create assignment", "", ""); + return false; + } + + if (! peekTokenClass(EHTokComma)) + return true; + + return true; +} + +// Accept a conditional expression, which associates right-to-left, +// accomplished by the "true" expression calling down to lower +// precedence levels than this level. +// +// conditional_expression +// : binary_expression +// | binary_expression QUESTION expression COLON assignment_expression +// +bool HlslGrammar::acceptConditionalExpression(TIntermTyped*& node) +{ + // binary_expression + if (! acceptBinaryExpression(node, PlLogicalOr)) + return false; + + if (! acceptTokenClass(EHTokQuestion)) + return true; + + node = parseContext.convertConditionalExpression(token.loc, node, false); + if (node == nullptr) + return false; + + ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors + + TIntermTyped* trueNode = nullptr; + if (! acceptExpression(trueNode)) { + expected("expression after ?"); + return false; + } + TSourceLoc loc = token.loc; + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + TIntermTyped* falseNode = nullptr; + if (! acceptAssignmentExpression(falseNode)) { + expected("expression after :"); + return false; + } + + --parseContext.controlFlowNestingLevel; + + node = intermediate.addSelection(node, trueNode, falseNode, loc); + + return true; +} + +// Accept a binary expression, for binary operations that +// associate left-to-right. This is, it is implicit, for example +// +// ((a op b) op c) op d +// +// binary_expression +// : expression op expression op expression ... +// +// where 'expression' is the next higher level in precedence. +// +bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel precedenceLevel) +{ + if (precedenceLevel > PlMul) + return acceptUnaryExpression(node); + + // assignment_expression + if (! acceptBinaryExpression(node, (PrecedenceLevel)(precedenceLevel + 1))) + return false; + + do { + TOperator op = HlslOpMap::binary(peek()); + PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op); + if (tokenLevel < precedenceLevel) + return true; + + // ... op + TSourceLoc loc = token.loc; + advanceToken(); + + // ... expression + TIntermTyped* rightNode = nullptr; + if (! acceptBinaryExpression(rightNode, (PrecedenceLevel)(precedenceLevel + 1))) { + expected("expression"); + return false; + } + + node = intermediate.addBinaryMath(op, node, rightNode, loc); + if (node == nullptr) { + parseContext.error(loc, "Could not perform requested binary operation", "", ""); + return false; + } + } while (true); +} + +// unary_expression +// : (type) unary_expression +// | + unary_expression +// | - unary_expression +// | ! unary_expression +// | ~ unary_expression +// | ++ unary_expression +// | -- unary_expression +// | postfix_expression +// +bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) +{ + // (type) unary_expression + // Have to look two steps ahead, because this could be, e.g., a + // postfix_expression instead, since that also starts with at "(". + if (acceptTokenClass(EHTokLeftParen)) { + TType castType; + if (acceptType(castType)) { + // recognize any array_specifier as part of the type + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes != nullptr) + castType.transferArraySizes(arraySizes); + TSourceLoc loc = token.loc; + if (acceptTokenClass(EHTokRightParen)) { + // We've matched "(type)" now, get the expression to cast + if (! acceptUnaryExpression(node)) + return false; + + // Hook it up like a constructor + TFunction* constructorFunction = parseContext.makeConstructorCall(loc, castType); + if (constructorFunction == nullptr) { + expected("type that can be constructed"); + return false; + } + TIntermTyped* arguments = nullptr; + parseContext.handleFunctionArgument(constructorFunction, arguments, node); + node = parseContext.handleFunctionCall(loc, constructorFunction, arguments); + + return node != nullptr; + } else { + // This could be a parenthesized constructor, ala (int(3)), and we just accepted + // the '(int' part. We must back up twice. + recedeToken(); + recedeToken(); + + // Note, there are no array constructors like + // (float[2](...)) + if (arraySizes != nullptr) + parseContext.error(loc, "parenthesized array constructor not allowed", "([]())", "", ""); + } + } else { + // This isn't a type cast, but it still started "(", so if it is a + // unary expression, it can only be a postfix_expression, so try that. + // Back it up first. + recedeToken(); + return acceptPostfixExpression(node); + } + } + + // peek for "op unary_expression" + TOperator unaryOp = HlslOpMap::preUnary(peek()); + + // postfix_expression (if no unary operator) + if (unaryOp == EOpNull) + return acceptPostfixExpression(node); + + // op unary_expression + TSourceLoc loc = token.loc; + advanceToken(); + if (! acceptUnaryExpression(node)) + return false; + + // + is a no-op + if (unaryOp == EOpAdd) + return true; + + node = intermediate.addUnaryMath(unaryOp, node, loc); + + // These unary ops require lvalues + if (unaryOp == EOpPreIncrement || unaryOp == EOpPreDecrement) + node = parseContext.handleLvalue(loc, "unary operator", node); + + return node != nullptr; +} + +// postfix_expression +// : LEFT_PAREN expression RIGHT_PAREN +// | literal +// | constructor +// | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ] +// | function_call +// | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET +// | postfix_expression DOT IDENTIFIER +// | postfix_expression DOT IDENTIFIER arguments +// | postfix_expression arguments +// | postfix_expression INC_OP +// | postfix_expression DEC_OP +// +bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) +{ + // Not implemented as self-recursive: + // The logical "right recursion" is done with a loop at the end + + // idToken will pick up either a variable or a function name in a function call + HlslToken idToken; + + // Find something before the postfix operations, as they can't operate + // on nothing. So, no "return true", they fall through, only "return false". + if (acceptTokenClass(EHTokLeftParen)) { + // LEFT_PAREN expression RIGHT_PAREN + if (! acceptExpression(node)) { + expected("expression"); + return false; + } + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + } else if (acceptLiteral(node)) { + // literal (nothing else to do yet) + } else if (acceptConstructor(node)) { + // constructor (nothing else to do yet) + } else if (acceptIdentifier(idToken)) { + // user-type, namespace name, variable, or function name + TString* fullName = idToken.string; + while (acceptTokenClass(EHTokColonColon)) { + // user-type or namespace name + fullName = NewPoolTString(fullName->c_str()); + fullName->append(parseContext.scopeMangler); + if (acceptIdentifier(idToken)) + fullName->append(*idToken.string); + else { + expected("identifier after ::"); + return false; + } + } + if (! peekTokenClass(EHTokLeftParen)) { + node = parseContext.handleVariable(idToken.loc, fullName); + if (node == nullptr) + return false; + } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) { + // function_call (nothing else to do yet) + } else { + expected("function call arguments"); + return false; + } + } else { + // nothing found, can't post operate + return false; + } + + // Something was found, chain as many postfix operations as exist. + do { + TSourceLoc loc = token.loc; + TOperator postOp = HlslOpMap::postUnary(peek()); + + // Consume only a valid post-unary operator, otherwise we are done. + switch (postOp) { + case EOpIndexDirectStruct: + case EOpIndexIndirect: + case EOpPostIncrement: + case EOpPostDecrement: + case EOpScoping: + advanceToken(); + break; + default: + return true; + } + + // We have a valid post-unary operator, process it. + switch (postOp) { + case EOpScoping: + case EOpIndexDirectStruct: + { + // DOT IDENTIFIER + // includes swizzles, member variables, and member functions + HlslToken field; + if (! acceptIdentifier(field)) { + expected("swizzle or member"); + return false; + } + + if (peekTokenClass(EHTokLeftParen)) { + // member function + TIntermTyped* thisNode = node; + + // arguments + if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) { + expected("function parameters"); + return false; + } + } else + node = parseContext.handleDotDereference(field.loc, node, *field.string); + + break; + } + case EOpIndexIndirect: + { + // LEFT_BRACKET integer_expression RIGHT_BRACKET + TIntermTyped* indexNode = nullptr; + if (! acceptExpression(indexNode) || + ! peekTokenClass(EHTokRightBracket)) { + expected("expression followed by ']'"); + return false; + } + advanceToken(); + node = parseContext.handleBracketDereference(indexNode->getLoc(), node, indexNode); + if (node == nullptr) + return false; + break; + } + case EOpPostIncrement: + // INC_OP + // fall through + case EOpPostDecrement: + // DEC_OP + node = intermediate.addUnaryMath(postOp, node, loc); + node = parseContext.handleLvalue(loc, "unary operator", node); + break; + default: + assert(0); + break; + } + } while (true); +} + +// constructor +// : type argument_list +// +bool HlslGrammar::acceptConstructor(TIntermTyped*& node) +{ + // type + TType type; + if (acceptType(type)) { + TFunction* constructorFunction = parseContext.makeConstructorCall(token.loc, type); + if (constructorFunction == nullptr) + return false; + + // arguments + TIntermTyped* arguments = nullptr; + if (! acceptArguments(constructorFunction, arguments)) { + // It's possible this is a type keyword used as an identifier. Put the token back + // for later use. + recedeToken(); + return false; + } + + if (arguments == nullptr) { + expected("one or more arguments"); + return false; + } + + // hook it up + node = parseContext.handleFunctionCall(token.loc, constructorFunction, arguments); + + return node != nullptr; + } + + return false; +} + +// The function_call identifier was already recognized, and passed in as idToken. +// +// function_call +// : [idToken] arguments +// +bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject) +{ + // name + TString* functionName = nullptr; + if (baseObject == nullptr) { + functionName = &name; + } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) { + // Built-in methods are not in the symbol table as methods, but as global functions + // taking an explicit 'this' as the first argument. + functionName = NewPoolTString(BUILTIN_PREFIX); + functionName->append(name); + } else { + if (! baseObject->getType().isStruct()) { + expected("structure"); + return false; + } + functionName = NewPoolTString(""); + functionName->append(baseObject->getType().getTypeName()); + parseContext.addScopeMangler(*functionName); + functionName->append(name); + } + + // function + TFunction* function = new TFunction(functionName, TType(EbtVoid)); + + // arguments + TIntermTyped* arguments = nullptr; + if (baseObject != nullptr) { + // Non-static member functions have an implicit first argument of the base object. + parseContext.handleFunctionArgument(function, arguments, baseObject); + } + if (! acceptArguments(function, arguments)) + return false; + + // call + node = parseContext.handleFunctionCall(loc, function, arguments); + + return node != nullptr; +} + +// arguments +// : LEFT_PAREN expression COMMA expression COMMA ... RIGHT_PAREN +// +// The arguments are pushed onto the 'function' argument list and +// onto the 'arguments' aggregate. +// +bool HlslGrammar::acceptArguments(TFunction* function, TIntermTyped*& arguments) +{ + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + // RIGHT_PAREN + if (acceptTokenClass(EHTokRightParen)) + return true; + + // must now be at least one expression... + do { + // expression + TIntermTyped* arg; + if (! acceptAssignmentExpression(arg)) + return false; + + // hook it up + parseContext.handleFunctionArgument(function, arguments, arg); + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +bool HlslGrammar::acceptLiteral(TIntermTyped*& node) +{ + switch (token.tokenClass) { + case EHTokIntConstant: + node = intermediate.addConstantUnion(token.i, token.loc, true); + break; + case EHTokUintConstant: + node = intermediate.addConstantUnion(token.u, token.loc, true); + break; + case EHTokFloat16Constant: + node = intermediate.addConstantUnion(token.d, EbtFloat16, token.loc, true); + break; + case EHTokFloatConstant: + node = intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true); + break; + case EHTokDoubleConstant: + node = intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true); + break; + case EHTokBoolConstant: + node = intermediate.addConstantUnion(token.b, token.loc, true); + break; + case EHTokStringConstant: + node = intermediate.addConstantUnion(token.string, token.loc, true); + break; + + default: + return false; + } + + advanceToken(); + + return true; +} + +// simple_statement +// : SEMICOLON +// | declaration_statement +// | expression SEMICOLON +// +bool HlslGrammar::acceptSimpleStatement(TIntermNode*& statement) +{ + // SEMICOLON + if (acceptTokenClass(EHTokSemicolon)) + return true; + + // declaration + if (acceptDeclaration(statement)) + return true; + + // expression + TIntermTyped* node; + if (acceptExpression(node)) + statement = node; + else + return false; + + // SEMICOLON (following an expression) + if (acceptTokenClass(EHTokSemicolon)) + return true; + else { + expected(";"); + return false; + } +} + +// compound_statement +// : LEFT_CURLY statement statement ... RIGHT_CURLY +// +bool HlslGrammar::acceptCompoundStatement(TIntermNode*& retStatement) +{ + TIntermAggregate* compoundStatement = nullptr; + + // LEFT_CURLY + if (! acceptTokenClass(EHTokLeftBrace)) + return false; + + // statement statement ... + TIntermNode* statement = nullptr; + while (acceptStatement(statement)) { + TIntermBranch* branch = statement ? statement->getAsBranchNode() : nullptr; + if (branch != nullptr && (branch->getFlowOp() == EOpCase || + branch->getFlowOp() == EOpDefault)) { + // hook up individual subsequences within a switch statement + parseContext.wrapupSwitchSubsequence(compoundStatement, statement); + compoundStatement = nullptr; + } else { + // hook it up to the growing compound statement + compoundStatement = intermediate.growAggregate(compoundStatement, statement); + } + } + if (compoundStatement) + compoundStatement->setOperator(intermediate.getDebugInfo() ? EOpScope : EOpSequence); + + retStatement = compoundStatement; + + // RIGHT_CURLY + return acceptTokenClass(EHTokRightBrace); +} + +bool HlslGrammar::acceptScopedStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptStatement(statement); + parseContext.popScope(); + + return result; +} + +bool HlslGrammar::acceptScopedCompoundStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptCompoundStatement(statement); + parseContext.popScope(); + + return result; +} + +// statement +// : attributes attributed_statement +// +// attributed_statement +// : compound_statement +// | simple_statement +// | selection_statement +// | switch_statement +// | case_label +// | default_label +// | iteration_statement +// | jump_statement +// +bool HlslGrammar::acceptStatement(TIntermNode*& statement) +{ + statement = nullptr; + + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + // attributed_statement + switch (peek()) { + case EHTokLeftBrace: + return acceptScopedCompoundStatement(statement); + + case EHTokIf: + return acceptSelectionStatement(statement, attributes); + + case EHTokSwitch: + return acceptSwitchStatement(statement, attributes); + + case EHTokFor: + case EHTokDo: + case EHTokWhile: + return acceptIterationStatement(statement, attributes); + + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + case EHTokReturn: + return acceptJumpStatement(statement); + + case EHTokCase: + return acceptCaseLabel(statement); + case EHTokDefault: + return acceptDefaultLabel(statement); + + case EHTokRightBrace: + // Performance: not strictly necessary, but stops a bunch of hunting early, + // and is how sequences of statements end. + return false; + + default: + return acceptSimpleStatement(statement); + } + + return true; +} + +// attributes +// : [zero or more:] bracketed-attribute +// +// bracketed-attribute: +// : LEFT_BRACKET scoped-attribute RIGHT_BRACKET +// : LEFT_BRACKET LEFT_BRACKET scoped-attribute RIGHT_BRACKET RIGHT_BRACKET +// +// scoped-attribute: +// : attribute +// | namespace COLON COLON attribute +// +// attribute: +// : UNROLL +// | UNROLL LEFT_PAREN literal RIGHT_PAREN +// | FASTOPT +// | ALLOW_UAV_CONDITION +// | BRANCH +// | FLATTEN +// | FORCECASE +// | CALL +// | DOMAIN +// | EARLYDEPTHSTENCIL +// | INSTANCE +// | MAXTESSFACTOR +// | OUTPUTCONTROLPOINTS +// | OUTPUTTOPOLOGY +// | PARTITIONING +// | PATCHCONSTANTFUNC +// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN +// +void HlslGrammar::acceptAttributes(TAttributes& attributes) +{ + // For now, accept the [ XXX(X) ] syntax, but drop all but + // numthreads, which is used to set the CS local size. + // TODO: subset to correct set? Pass on? + do { + HlslToken attributeToken; + + // LEFT_BRACKET? + if (! acceptTokenClass(EHTokLeftBracket)) + return; + // another LEFT_BRACKET? + bool doubleBrackets = false; + if (acceptTokenClass(EHTokLeftBracket)) + doubleBrackets = true; + + // attribute? (could be namespace; will adjust later) + if (!acceptIdentifier(attributeToken)) { + if (!peekTokenClass(EHTokRightBracket)) { + expected("namespace or attribute identifier"); + advanceToken(); + } + } + + TString nameSpace; + if (acceptTokenClass(EHTokColonColon)) { + // namespace COLON COLON + nameSpace = *attributeToken.string; + // attribute + if (!acceptIdentifier(attributeToken)) { + expected("attribute identifier"); + return; + } + } + + TIntermAggregate* expressions = nullptr; + + // (x, ...) + if (acceptTokenClass(EHTokLeftParen)) { + expressions = new TIntermAggregate; + + TIntermTyped* node; + bool expectingExpression = false; + + while (acceptAssignmentExpression(node)) { + expectingExpression = false; + expressions->getSequence().push_back(node); + if (acceptTokenClass(EHTokComma)) + expectingExpression = true; + } + + // 'expressions' is an aggregate with the expressions in it + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + // Error for partial or missing expression + if (expectingExpression || expressions->getSequence().empty()) + expected("expression"); + } + + // RIGHT_BRACKET + if (!acceptTokenClass(EHTokRightBracket)) { + expected("]"); + return; + } + // another RIGHT_BRACKET? + if (doubleBrackets && !acceptTokenClass(EHTokRightBracket)) { + expected("]]"); + return; + } + + // Add any values we found into the attribute map. + if (attributeToken.string != nullptr) { + TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string); + if (attributeType == EatNone) + parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), ""); + else { + TAttributeArgs attributeArgs = { attributeType, expressions }; + attributes.push_back(attributeArgs); + } + } + } while (true); +} + +// selection_statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement +// +bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + TSourceLoc loc = token.loc; + + // IF + if (! acceptTokenClass(EHTokIf)) + return false; + + // so that something declared in the condition is scoped to the lifetimes + // of the then-else statements + parseContext.pushScope(); + + // LEFT_PAREN expression RIGHT_PAREN + TIntermTyped* condition; + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + // create the child statements + TIntermNodePair thenElse = { nullptr, nullptr }; + + ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors + + // then statement + if (! acceptScopedStatement(thenElse.node1)) { + expected("then statement"); + return false; + } + + // ELSE + if (acceptTokenClass(EHTokElse)) { + // else statement + if (! acceptScopedStatement(thenElse.node2)) { + expected("else statement"); + return false; + } + } + + // Put the pieces together + statement = intermediate.addSelection(condition, thenElse, loc); + parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes); + + parseContext.popScope(); + --parseContext.controlFlowNestingLevel; + + return true; +} + +// switch_statement +// : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement +// +bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + // SWITCH + TSourceLoc loc = token.loc; + + if (! acceptTokenClass(EHTokSwitch)) + return false; + + // LEFT_PAREN expression RIGHT_PAREN + parseContext.pushScope(); + TIntermTyped* switchExpression; + if (! acceptParenExpression(switchExpression)) { + parseContext.popScope(); + return false; + } + + // compound_statement + parseContext.pushSwitchSequence(new TIntermSequence); + + ++parseContext.controlFlowNestingLevel; + bool statementOkay = acceptCompoundStatement(statement); + --parseContext.controlFlowNestingLevel; + + if (statementOkay) + statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, + attributes); + + parseContext.popSwitchSequence(); + parseContext.popScope(); + + return statementOkay; +} + +// iteration_statement +// : WHILE LEFT_PAREN condition RIGHT_PAREN statement +// | DO LEFT_BRACE statement RIGHT_BRACE WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON +// | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement +// +// Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen. +bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + TSourceLoc loc = token.loc; + TIntermTyped* condition = nullptr; + + EHlslTokenClass loop = peek(); + assert(loop == EHTokDo || loop == EHTokFor || loop == EHTokWhile); + + // WHILE or DO or FOR + advanceToken(); + + TIntermLoop* loopNode = nullptr; + switch (loop) { + case EHTokWhile: + // so that something declared in the condition is scoped to the lifetime + // of the while sub-statement + parseContext.pushScope(); // this only needs to work right if no errors + parseContext.nestLooping(); + ++parseContext.controlFlowNestingLevel; + + // LEFT_PAREN condition RIGHT_PAREN + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + // statement + if (! acceptScopedStatement(statement)) { + expected("while sub-statement"); + return false; + } + + parseContext.unnestLooping(); + parseContext.popScope(); + --parseContext.controlFlowNestingLevel; + + loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc); + statement = loopNode; + break; + + case EHTokDo: + parseContext.nestLooping(); // this only needs to work right if no errors + ++parseContext.controlFlowNestingLevel; + + // statement + if (! acceptScopedStatement(statement)) { + expected("do sub-statement"); + return false; + } + + // WHILE + if (! acceptTokenClass(EHTokWhile)) { + expected("while"); + return false; + } + + // LEFT_PAREN condition RIGHT_PAREN + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + parseContext.unnestLooping(); + --parseContext.controlFlowNestingLevel; + + loopNode = intermediate.addLoop(statement, condition, nullptr, false, loc); + statement = loopNode; + break; + + case EHTokFor: + { + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + // so that something declared in the condition is scoped to the lifetime + // of the for sub-statement + parseContext.pushScope(); + + // initializer + TIntermNode* initNode = nullptr; + if (! acceptSimpleStatement(initNode)) + expected("for-loop initializer statement"); + + parseContext.nestLooping(); // this only needs to work right if no errors + ++parseContext.controlFlowNestingLevel; + + // condition SEMI_COLON + acceptExpression(condition); + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + if (condition != nullptr) { + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + } + + // iterator SEMI_COLON + TIntermTyped* iterator = nullptr; + acceptExpression(iterator); + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + // statement + if (! acceptScopedStatement(statement)) { + expected("for sub-statement"); + return false; + } + + statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode); + + parseContext.popScope(); + parseContext.unnestLooping(); + --parseContext.controlFlowNestingLevel; + + break; + } + + default: + return false; + } + + parseContext.handleLoopAttributes(loc, loopNode, attributes); + return true; +} + +// jump_statement +// : CONTINUE SEMICOLON +// | BREAK SEMICOLON +// | DISCARD SEMICOLON +// | RETURN SEMICOLON +// | RETURN expression SEMICOLON +// +bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement) +{ + EHlslTokenClass jump = peek(); + switch (jump) { + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + case EHTokReturn: + advanceToken(); + break; + default: + // not something we handle in this function + return false; + } + + switch (jump) { + case EHTokContinue: + statement = intermediate.addBranch(EOpContinue, token.loc); + if (parseContext.loopNestingLevel == 0) { + expected("loop"); + return false; + } + break; + case EHTokBreak: + statement = intermediate.addBranch(EOpBreak, token.loc); + if (parseContext.loopNestingLevel == 0 && parseContext.switchSequenceStack.size() == 0) { + expected("loop or switch"); + return false; + } + break; + case EHTokDiscard: + statement = intermediate.addBranch(EOpKill, token.loc); + break; + + case EHTokReturn: + { + // expression + TIntermTyped* node; + if (acceptExpression(node)) { + // hook it up + statement = parseContext.handleReturnValue(token.loc, node); + } else + statement = intermediate.addBranch(EOpReturn, token.loc); + break; + } + + default: + assert(0); + return false; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + return true; +} + +// case_label +// : CASE expression COLON +// +bool HlslGrammar::acceptCaseLabel(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + if (! acceptTokenClass(EHTokCase)) + return false; + + TIntermTyped* expression; + if (! acceptExpression(expression)) { + expected("case expression"); + return false; + } + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + statement = parseContext.intermediate.addBranch(EOpCase, expression, loc); + + return true; +} + +// default_label +// : DEFAULT COLON +// +bool HlslGrammar::acceptDefaultLabel(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + if (! acceptTokenClass(EHTokDefault)) + return false; + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + statement = parseContext.intermediate.addBranch(EOpDefault, loc); + + return true; +} + +// array_specifier +// : LEFT_BRACKET integer_expression RGHT_BRACKET ... // optional +// : LEFT_BRACKET RGHT_BRACKET // optional +// +void HlslGrammar::acceptArraySpecifier(TArraySizes*& arraySizes) +{ + arraySizes = nullptr; + + // Early-out if there aren't any array dimensions + if (!peekTokenClass(EHTokLeftBracket)) + return; + + // If we get here, we have at least one array dimension. This will track the sizes we find. + arraySizes = new TArraySizes; + + // Collect each array dimension. + while (acceptTokenClass(EHTokLeftBracket)) { + TSourceLoc loc = token.loc; + TIntermTyped* sizeExpr = nullptr; + + // Array sizing expression is optional. If omitted, array will be later sized by initializer list. + const bool hasArraySize = acceptAssignmentExpression(sizeExpr); + + if (! acceptTokenClass(EHTokRightBracket)) { + expected("]"); + return; + } + + if (hasArraySize) { + TArraySize arraySize; + parseContext.arraySizeCheck(loc, sizeExpr, arraySize); + arraySizes->addInnerSize(arraySize); + } else { + arraySizes->addInnerSize(0); // sized by initializers. + } + } +} + +// post_decls +// : COLON semantic // optional +// COLON PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN // optional +// COLON REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN // optional +// COLON LAYOUT layout_qualifier_list +// annotations // optional +// +// Return true if any tokens were accepted. That is, +// false can be returned on successfully recognizing nothing, +// not necessarily meaning bad syntax. +// +bool HlslGrammar::acceptPostDecls(TQualifier& qualifier) +{ + bool found = false; + + do { + // COLON + if (acceptTokenClass(EHTokColon)) { + found = true; + HlslToken idToken; + if (peekTokenClass(EHTokLayout)) + acceptLayoutQualifierList(qualifier); + else if (acceptTokenClass(EHTokPackOffset)) { + // PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return false; + } + HlslToken locationToken; + if (! acceptIdentifier(locationToken)) { + expected("c[subcomponent][.component]"); + return false; + } + HlslToken componentToken; + if (acceptTokenClass(EHTokDot)) { + if (! acceptIdentifier(componentToken)) { + expected("component"); + return false; + } + } + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + parseContext.handlePackOffset(locationToken.loc, qualifier, *locationToken.string, componentToken.string); + } else if (! acceptIdentifier(idToken)) { + expected("layout, semantic, packoffset, or register"); + return false; + } else if (*idToken.string == "register") { + // REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return false; + } + HlslToken registerDesc; // for Type# + HlslToken profile; + if (! acceptIdentifier(registerDesc)) { + expected("register number description"); + return false; + } + if (registerDesc.string->size() > 1 && !isdigit((*registerDesc.string)[1]) && + acceptTokenClass(EHTokComma)) { + // Then we didn't really see the registerDesc yet, it was + // actually the profile. Adjust... + profile = registerDesc; + if (! acceptIdentifier(registerDesc)) { + expected("register number description"); + return false; + } + } + int subComponent = 0; + if (acceptTokenClass(EHTokLeftBracket)) { + // LEFT_BRACKET subcomponent RIGHT_BRACKET + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + subComponent = token.i; + advanceToken(); + if (! acceptTokenClass(EHTokRightBracket)) { + expected("]"); + break; + } + } + // (COMMA SPACEN)opt + HlslToken spaceDesc; + if (acceptTokenClass(EHTokComma)) { + if (! acceptIdentifier(spaceDesc)) { + expected ("space identifier"); + return false; + } + } + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + parseContext.handleRegister(registerDesc.loc, qualifier, profile.string, *registerDesc.string, subComponent, spaceDesc.string); + } else { + // semantic, in idToken.string + TString semanticUpperCase = *idToken.string; + std::transform(semanticUpperCase.begin(), semanticUpperCase.end(), semanticUpperCase.begin(), ::toupper); + parseContext.handleSemantic(idToken.loc, qualifier, mapSemantic(semanticUpperCase.c_str()), semanticUpperCase); + } + } else if (peekTokenClass(EHTokLeftAngle)) { + found = true; + acceptAnnotations(qualifier); + } else + break; + + } while (true); + + return found; +} + +// +// Get the stream of tokens from the scanner, but skip all syntactic/semantic +// processing. +// +bool HlslGrammar::captureBlockTokens(TVector& tokens) +{ + if (! peekTokenClass(EHTokLeftBrace)) + return false; + + int braceCount = 0; + + do { + switch (peek()) { + case EHTokLeftBrace: + ++braceCount; + break; + case EHTokRightBrace: + --braceCount; + break; + case EHTokNone: + // End of input before balance { } is bad... + return false; + default: + break; + } + + tokens.push_back(token); + advanceToken(); + } while (braceCount > 0); + + return true; +} + +// Return a string for just the types that can also be declared as an identifier. +const char* HlslGrammar::getTypeString(EHlslTokenClass tokenClass) const +{ + switch (tokenClass) { + case EHTokSample: return "sample"; + case EHTokHalf: return "half"; + case EHTokHalf1x1: return "half1x1"; + case EHTokHalf1x2: return "half1x2"; + case EHTokHalf1x3: return "half1x3"; + case EHTokHalf1x4: return "half1x4"; + case EHTokHalf2x1: return "half2x1"; + case EHTokHalf2x2: return "half2x2"; + case EHTokHalf2x3: return "half2x3"; + case EHTokHalf2x4: return "half2x4"; + case EHTokHalf3x1: return "half3x1"; + case EHTokHalf3x2: return "half3x2"; + case EHTokHalf3x3: return "half3x3"; + case EHTokHalf3x4: return "half3x4"; + case EHTokHalf4x1: return "half4x1"; + case EHTokHalf4x2: return "half4x2"; + case EHTokHalf4x3: return "half4x3"; + case EHTokHalf4x4: return "half4x4"; + case EHTokBool: return "bool"; + case EHTokFloat: return "float"; + case EHTokDouble: return "double"; + case EHTokInt: return "int"; + case EHTokUint: return "uint"; + case EHTokMin16float: return "min16float"; + case EHTokMin10float: return "min10float"; + case EHTokMin16int: return "min16int"; + case EHTokMin12int: return "min12int"; + case EHTokConstantBuffer: return "ConstantBuffer"; + case EHTokLayout: return "layout"; + default: + return nullptr; + } +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.h new file mode 100644 index 00000000..cfe294bc --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslGrammar.h @@ -0,0 +1,144 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// Copyright (C) 2023 Mobica Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLGRAMMAR_H_ +#define HLSLGRAMMAR_H_ + +#include "hlslParseHelper.h" +#include "hlslOpMap.h" +#include "hlslTokenStream.h" + +namespace glslang { + + class TFunctionDeclarator; + + // Should just be the grammar aspect of HLSL. + // Described in more detail in hlslGrammar.cpp. + + class HlslGrammar : public HlslTokenStream { + public: + HlslGrammar(HlslScanContext& scanner, HlslParseContext& parseContext) + : HlslTokenStream(scanner), parseContext(parseContext), intermediate(parseContext.intermediate), + typeIdentifiers(false), unitNode(nullptr) { } + virtual ~HlslGrammar() { } + + bool parse(); + + protected: + HlslGrammar(); + HlslGrammar& operator=(const HlslGrammar&); + + void expected(const char*); + void unimplemented(const char*); + bool acceptIdentifier(HlslToken&); + bool acceptCompilationUnit(); + bool acceptDeclarationList(TIntermNode*&); + bool acceptDeclaration(TIntermNode*&); + bool acceptControlDeclaration(TIntermNode*& node); + bool acceptSamplerDeclarationDX9(TType&); + bool acceptSamplerState(); + bool acceptFullySpecifiedType(TType&, const TAttributes&); + bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false); + bool acceptPreQualifier(TQualifier&); + bool acceptPostQualifier(TQualifier&); + bool acceptLayoutQualifierList(TQualifier&); + bool acceptType(TType&); + bool acceptType(TType&, TIntermNode*& nodeList); + bool acceptTemplateVecMatBasicType(TBasicType&, TPrecisionQualifier&); + bool acceptVectorTemplateType(TType&); + bool acceptMatrixTemplateType(TType&); + bool acceptTessellationDeclType(TBuiltInVariable&); + bool acceptTessellationPatchTemplateType(TType&); + bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&); + bool acceptOutputPrimitiveGeometry(TLayoutGeometry&); + bool acceptAnnotations(TQualifier&); + bool acceptSamplerTypeDX9(TType &); + bool acceptSamplerType(TType&); + bool acceptTextureType(TType&); + bool acceptSubpassInputType(TType&); + bool acceptStructBufferType(TType&); + bool acceptTextureBufferType(TType&); + bool acceptConstantBufferType(TType&); + bool acceptStruct(TType&, TIntermNode*& nodeList); + bool acceptStructDeclarationList(TTypeList*&, TIntermNode*& nodeList, TVector&); + bool acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType&, TString& memberName, + TFunctionDeclarator&); + bool acceptFunctionParameters(TFunction&); + bool acceptParameterDeclaration(TFunction&); + bool acceptFunctionDefinition(TFunctionDeclarator&, TIntermNode*& nodeList, TVector* deferredTokens); + bool acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList); + bool acceptParenExpression(TIntermTyped*&); + bool acceptExpression(TIntermTyped*&); + bool acceptInitializer(TIntermTyped*&); + bool acceptAssignmentExpression(TIntermTyped*&); + bool acceptConditionalExpression(TIntermTyped*&); + bool acceptBinaryExpression(TIntermTyped*&, PrecedenceLevel); + bool acceptUnaryExpression(TIntermTyped*&); + bool acceptPostfixExpression(TIntermTyped*&); + bool acceptConstructor(TIntermTyped*&); + bool acceptFunctionCall(const TSourceLoc&, TString& name, TIntermTyped*&, TIntermTyped* objectBase); + bool acceptArguments(TFunction*, TIntermTyped*&); + bool acceptLiteral(TIntermTyped*&); + bool acceptSimpleStatement(TIntermNode*&); + bool acceptCompoundStatement(TIntermNode*&); + bool acceptScopedStatement(TIntermNode*&); + bool acceptScopedCompoundStatement(TIntermNode*&); + bool acceptStatement(TIntermNode*&); + bool acceptNestedStatement(TIntermNode*&); + void acceptAttributes(TAttributes&); + bool acceptSelectionStatement(TIntermNode*&, const TAttributes&); + bool acceptSwitchStatement(TIntermNode*&, const TAttributes&); + bool acceptIterationStatement(TIntermNode*&, const TAttributes&); + bool acceptJumpStatement(TIntermNode*&); + bool acceptCaseLabel(TIntermNode*&); + bool acceptDefaultLabel(TIntermNode*&); + void acceptArraySpecifier(TArraySizes*&); + bool acceptPostDecls(TQualifier&); + bool acceptDefaultParameterDeclaration(const TType&, TIntermTyped*&); + + bool captureBlockTokens(TVector& tokens); + const char* getTypeString(EHlslTokenClass tokenClass) const; + + HlslParseContext& parseContext; // state of parsing and helper functions for building the intermediate + TIntermediate& intermediate; // the final product, the intermediate representation, includes the AST + bool typeIdentifiers; // shader uses some types as identifiers + TIntermNode* unitNode; + }; + +} // end namespace glslang + +#endif // HLSLGRAMMAR_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.cpp new file mode 100644 index 00000000..ebe6fbd9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.cpp @@ -0,0 +1,173 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Map from physical token form (e.g. '-') to logical operator +// form (e.g., binary subtract or unary negate). + +#include "hlslOpMap.h" + +namespace glslang { + +// Map parsing tokens that could be assignments into assignment operators. +TOperator HlslOpMap::assignment(EHlslTokenClass op) +{ + switch (op) { + case EHTokAssign: return EOpAssign; + case EHTokMulAssign: return EOpMulAssign; + case EHTokDivAssign: return EOpDivAssign; + case EHTokAddAssign: return EOpAddAssign; + case EHTokModAssign: return EOpModAssign; + case EHTokLeftAssign: return EOpLeftShiftAssign; + case EHTokRightAssign: return EOpRightShiftAssign; + case EHTokAndAssign: return EOpAndAssign; + case EHTokXorAssign: return EOpExclusiveOrAssign; + case EHTokOrAssign: return EOpInclusiveOrAssign; + case EHTokSubAssign: return EOpSubAssign; + + default: + return EOpNull; + } +} + +// Map parsing tokens that could be binary operations into binary operators. +TOperator HlslOpMap::binary(EHlslTokenClass op) +{ + switch (op) { + case EHTokPlus: return EOpAdd; + case EHTokDash: return EOpSub; + case EHTokStar: return EOpMul; + case EHTokSlash: return EOpDiv; + case EHTokPercent: return EOpMod; + case EHTokRightOp: return EOpRightShift; + case EHTokLeftOp: return EOpLeftShift; + case EHTokAmpersand: return EOpAnd; + case EHTokVerticalBar: return EOpInclusiveOr; + case EHTokCaret: return EOpExclusiveOr; + case EHTokEqOp: return EOpEqual; + case EHTokNeOp: return EOpNotEqual; + case EHTokLeftAngle: return EOpLessThan; + case EHTokRightAngle: return EOpGreaterThan; + case EHTokLeOp: return EOpLessThanEqual; + case EHTokGeOp: return EOpGreaterThanEqual; + case EHTokOrOp: return EOpLogicalOr; + case EHTokXorOp: return EOpLogicalXor; + case EHTokAndOp: return EOpLogicalAnd; + + default: + return EOpNull; + } +} + +// Map parsing tokens that could be unary operations into unary operators. +// These are just the ones that can appear in front of its operand. +TOperator HlslOpMap::preUnary(EHlslTokenClass op) +{ + switch (op) { + case EHTokPlus: return EOpAdd; // means no-op, but still a unary op was present + case EHTokDash: return EOpNegative; + case EHTokBang: return EOpLogicalNot; + case EHTokTilde: return EOpBitwiseNot; + + case EHTokIncOp: return EOpPreIncrement; + case EHTokDecOp: return EOpPreDecrement; + + default: return EOpNull; // means not a pre-unary op + } +} + +// Map parsing tokens that could be unary operations into unary operators. +// These are just the ones that can appear behind its operand. +TOperator HlslOpMap::postUnary(EHlslTokenClass op) +{ + switch (op) { + case EHTokDot: return EOpIndexDirectStruct; + case EHTokLeftBracket: return EOpIndexIndirect; + + case EHTokIncOp: return EOpPostIncrement; + case EHTokDecOp: return EOpPostDecrement; + + case EHTokColonColon: return EOpScoping; + + default: return EOpNull; // means not a post-unary op + } +} + +// Map operators into their level of precedence. +PrecedenceLevel HlslOpMap::precedenceLevel(TOperator op) +{ + switch (op) { + case EOpLogicalOr: + return PlLogicalOr; + case EOpLogicalXor: + return PlLogicalXor; + case EOpLogicalAnd: + return PlLogicalAnd; + + case EOpInclusiveOr: + return PlBitwiseOr; + case EOpExclusiveOr: + return PlBitwiseXor; + case EOpAnd: + return PlBitwiseAnd; + + case EOpEqual: + case EOpNotEqual: + return PlEquality; + + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + return PlRelational; + + case EOpRightShift: + case EOpLeftShift: + return PlShift; + + case EOpAdd: + case EOpSub: + return PlAdd; + + case EOpMul: + case EOpDiv: + case EOpMod: + return PlMul; + + default: + return PlBad; + } +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.h new file mode 100644 index 00000000..4e783f3f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslOpMap.h @@ -0,0 +1,69 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLOPMAP_H_ +#define HLSLOPMAP_H_ + +#include "hlslScanContext.h" + +namespace glslang { + + enum PrecedenceLevel { + PlBad, + PlLogicalOr, + PlLogicalXor, + PlLogicalAnd, + PlBitwiseOr, + PlBitwiseXor, + PlBitwiseAnd, + PlEquality, + PlRelational, + PlShift, + PlAdd, + PlMul + }; + + class HlslOpMap { + public: + static TOperator assignment(EHlslTokenClass op); + static TOperator binary(EHlslTokenClass op); + static TOperator preUnary(EHlslTokenClass op); + static TOperator postUnary(EHlslTokenClass op); + static PrecedenceLevel precedenceLevel(TOperator); + }; + +} // end namespace glslang + +#endif // HLSLOPMAP_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.cpp new file mode 100644 index 00000000..07323340 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.cpp @@ -0,0 +1,10287 @@ +// +// Copyright (C) 2017-2018 Google, Inc. +// Copyright (C) 2017 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslParseHelper.h" +#include "hlslScanContext.h" +#include "hlslGrammar.h" +#include "hlslAttributes.h" + +#include "../Include/Common.h" +#include "../MachineIndependent/Scan.h" +#include "../MachineIndependent/preprocessor/PpContext.h" + +#include +#include +#include +#include +#include + +namespace glslang { + +HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, + int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, + const TString sourceEntryPointName, + bool forwardCompatible, EShMessages messages) : + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, + forwardCompatible, messages, &sourceEntryPointName), + annotationNestingLevel(0), + inputPatch(nullptr), + nextInLocation(0), nextOutLocation(0), + entryPointFunction(nullptr), + entryPointFunctionBody(nullptr), + gsStreamOutput(nullptr), + clipDistanceOutput(nullptr), + cullDistanceOutput(nullptr), + clipDistanceInput(nullptr), + cullDistanceInput(nullptr), + parsingEntrypointParameters(false) +{ + globalUniformDefaults.clear(); + globalUniformDefaults.layoutMatrix = ElmRowMajor; + globalUniformDefaults.layoutPacking = ElpStd140; + + globalBufferDefaults.clear(); + globalBufferDefaults.layoutMatrix = ElmRowMajor; + globalBufferDefaults.layoutPacking = ElpStd430; + + globalInputDefaults.clear(); + globalOutputDefaults.clear(); + + clipSemanticNSizeIn.fill(0); + cullSemanticNSizeIn.fill(0); + clipSemanticNSizeOut.fill(0); + cullSemanticNSizeOut.fill(0); + + // "Shaders in the transform + // feedback capturing mode have an initial global default of + // layout(xfb_buffer = 0) out;" + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) + globalOutputDefaults.layoutXfbBuffer = 0; + + if (language == EShLangGeometry) + globalOutputDefaults.layoutStream = 0; +} + +HlslParseContext::~HlslParseContext() +{ +} + +void HlslParseContext::initializeExtensionBehavior() +{ + TParseContextBase::initializeExtensionBehavior(); + + // HLSL allows #line by default. + extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable; +} + +void HlslParseContext::setLimits(const TBuiltInResource& r) +{ + resources = r; + intermediate.setLimits(resources); +} + +// +// Parse an array of strings using the parser in HlslRules. +// +// Returns true for successful acceptance of the shader, false if any errors. +// +bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) +{ + currentScanner = &input; + ppContext.setInput(input, versionWillBeError); + + HlslScanContext scanContext(*this, ppContext); + HlslGrammar grammar(scanContext, *this); + if (!grammar.parse()) { + // Print a message formated such that if you click on the message it will take you right to + // the line through most UIs. + const glslang::TSourceLoc& sourceLoc = input.getSourceLoc(); + infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column + << ", HLSL parsing failed.\n"; + ++numErrors; + return false; + } + + finish(); + + return numErrors == 0; +} + +// +// Return true if this l-value node should be converted in some manner. +// For instance: turning a load aggregate into a store in an l-value. +// +bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const +{ + if (node == nullptr || node->getAsTyped() == nullptr) + return false; + + const TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); + const TIntermBinary* lhsAsBinary = node->getAsBinaryNode(); + + // If it's a swizzled/indexed aggregate, look at the left node instead. + if (lhsAsBinary != nullptr && + (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) + lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); + if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad) + return true; + + return false; +} + +void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, + TTypeList* newTypeList) +{ + newTypeList = nullptr; + correctUniform(memberType.getQualifier()); + if (memberType.isStruct()) { + auto it = ioTypeMap.find(memberType.getStruct()); + if (it != ioTypeMap.end() && it->second.uniform) + newTypeList = it->second.uniform; + } + TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList); +} + +// +// Return a TLayoutFormat corresponding to the given texture type. +// +TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType) +{ + if (txType.isStruct()) { + // TODO: implement. + error(loc, "unimplemented: structure type in image or buffer", "", ""); + return ElfNone; + } + + const int components = txType.getVectorSize(); + const TBasicType txBasicType = txType.getBasicType(); + + const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat { + if (intermediate.getNoStorageFormat()) + return ElfNone; + + return components == 1 ? v1 : + components == 2 ? v2 : v4; + }; + + switch (txBasicType) { + case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f); + case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i); + case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui); + default: + error(loc, "unknown basic type in image format", "", ""); + return ElfNone; + } +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + if (shouldConvertLValue(node)) { + // if we're writing to a texture, it must be an RW form. + + TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); + TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); + + if (!object->getType().getSampler().isImage()) { + error(loc, "operator[] on a non-RW texture must be an r-value", "", ""); + return true; + } + } + + // We tolerate samplers as l-values, even though they are nominally + // illegal, because we expect a later optimization to eliminate them. + if (node->getType().getBasicType() == EbtSampler) { + intermediate.setNeedsLegalization(); + return false; + } + + // Let the base class check errors + return TParseContextBase::lValueErrorCheck(loc, op, node); +} + +// +// This function handles l-value conversions and verifications. It uses, but is not synonymous +// with lValueErrorCheck. That function accepts an l-value directly, while this one must be +// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a +// series of other image operations. +// +// Most things are passed through unmodified, except for error checking. +// +TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) +{ + if (node == nullptr) + return nullptr; + + TIntermBinary* nodeAsBinary = node->getAsBinaryNode(); + TIntermUnary* nodeAsUnary = node->getAsUnaryNode(); + TIntermAggregate* sequence = nullptr; + + TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() : + nodeAsBinary ? nodeAsBinary->getLeft() : + nullptr; + + // Early bail out if there is no conversion to apply + if (!shouldConvertLValue(lhs)) { + if (lhs != nullptr) + if (lValueErrorCheck(loc, op, lhs)) + return nullptr; + return node; + } + + // *** If we get here, we're going to apply some conversion to an l-value. + + // Helper to create a load. + const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) { + TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad); + loadOp->setLoc(loc); + loadOp->getSequence().push_back(object); + loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode())); + loadOp->setType(derefType); + + sequence = intermediate.growAggregate(sequence, + intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc), + loc); + }; + + // Helper to create a store. + const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) { + TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore); + storeOp->getSequence().push_back(object); + storeOp->getSequence().push_back(coord); + storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp)); + storeOp->setLoc(loc); + storeOp->setType(TType(EbtVoid)); + + sequence = intermediate.growAggregate(sequence, storeOp); + }; + + // Helper to create an assign. + const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) { + sequence = intermediate.growAggregate(sequence, + intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()), + loc); + }; + + // Helper to complete sequence by adding trailing variable, so we evaluate to the right value. + const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* { + // Add a trailing use of the temp, so the sequence returns the proper value. + sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp)); + sequence->setOperator(EOpSequence); + sequence->setLoc(loc); + sequence->setType(derefType); + + return sequence; + }; + + // Helper to add unary op + const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) { + sequence = intermediate.growAggregate(sequence, + intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc, + rhsTmp->getType()), + loc); + }; + + // Return true if swizzle or index writes all components of the given variable. + const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool { + if (swizzle == nullptr) // not a swizzle or index + return true; + + // Track which components are being set. + std::array compIsSet; + compIsSet.fill(false); + + const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion(); + const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate(); + + // This could be either a direct index, or a swizzle. + if (asConst) { + compIsSet[asConst->getConstArray()[0].getIConst()] = true; + } else if (asAggregate) { + const TIntermSequence& seq = asAggregate->getSequence(); + for (int comp=0; compgetAsConstantUnion()->getConstArray()[0].getIConst()] = true; + } else { + assert(0); + } + + // Return true if all components are being set by the index or swizzle + return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(), + [](bool isSet) { return isSet; } ); + }; + + // Create swizzle matching input swizzle + const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* { + if (swizzle) + return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType()); + else + return var; + }; + + TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode(); + TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate(); + bool lhsIsSwizzle = false; + + // If it's a swizzled L-value, remember the swizzle, and use the LHS. + if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) { + lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); + lhsIsSwizzle = true; + } + + TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped(); + + const TSampler& texSampler = object->getType().getSampler(); + + TType objDerefType; + getTextureReturnType(texSampler, objDerefType); + + if (nodeAsBinary) { + TIntermTyped* rhs = nodeAsBinary->getRight(); + const TOperator assignOp = nodeAsBinary->getOp(); + + bool isModifyOp = false; + + switch (assignOp) { + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesMatrixAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpMatrixTimesMatrixAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + isModifyOp = true; + [[fallthrough]]; + case EOpAssign: + { + // Since this is an lvalue, we'll convert an image load to a sequence like this + // (to still provide the value): + // OpSequence + // OpImageStore(object, lhs, rhs) + // rhs + // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS, + // so we'll convert instead to this: + // OpSequence + // rhsTmp = rhs + // OpImageStore(object, coord, rhsTmp) + // rhsTmp + // If this is a read-modify-write op, like +=, we issue: + // OpSequence + // coordtmp = load's param1 + // rhsTmp = OpImageLoad(object, coordTmp) + // rhsTmp op= rhs + // OpImageStore(object, coordTmp, rhsTmp) + // rhsTmp + // + // If the lvalue is swizzled, we apply that when writing the temp variable, like so: + // ... + // rhsTmp.some_swizzle = ... + // For partial writes, an error is generated. + + TIntermSymbol* rhsTmp = rhs->getAsSymbolNode(); + TIntermTyped* coordTmp = coord; + + if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) { + rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); + + // Partial updates not yet supported + if (!writesAllComponents(rhsTmp, lhsAsBinary)) { + error(loc, "unimplemented: partial image updates", "", ""); + } + + // Assign storeTemp = rhs + if (isModifyOp) { + // We have to make a temp var for the coordinate, to avoid evaluating it twice. + coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) + } + + // rhsTmp op= rhs. + makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs); + } + + makeStore(object, coordTmp, rhsTmp); // add a store + return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence + } + + default: + break; + } + } + + if (nodeAsUnary) { + const TOperator assignOp = nodeAsUnary->getOp(); + + switch (assignOp) { + case EOpPreIncrement: + case EOpPreDecrement: + { + // We turn this into: + // OpSequence + // coordtmp = load's param1 + // rhsTmp = OpImageLoad(object, coordTmp) + // rhsTmp op + // OpImageStore(object, coordTmp, rhsTmp) + // rhsTmp + + TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); + TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) + makeUnary(assignOp, rhsTmp); // op rhsTmp + makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp) + return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence + } + + case EOpPostIncrement: + case EOpPostDecrement: + { + // We turn this into: + // OpSequence + // coordtmp = load's param1 + // rhsTmp1 = OpImageLoad(object, coordTmp) + // rhsTmp2 = rhsTmp1 + // rhsTmp2 op + // OpImageStore(object, coordTmp, rhsTmp2) + // rhsTmp1 (pre-op value) + TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType); + TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType); + TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp) + makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1 + makeUnary(assignOp, rhsTmp2); // rhsTmp op + makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2) + return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence + } + + default: + break; + } + } + + if (lhs) + if (lValueErrorCheck(loc, op, lhs)) + return nullptr; + + return node; +} + +void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) +{ + if (pragmaCallback) + pragmaCallback(loc.line, tokens); + + if (tokens.size() == 0) + return; + + // These pragmas are case insensitive in HLSL, so we'll compare in lower case. + TVector lowerTokens = tokens; + + for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it) + std::transform(it->begin(), it->end(), it->begin(), ::tolower); + + // Handle pack_matrix + if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") { + // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense. + // Row major becomes column major and vice versa. + + if (lowerTokens[2] == "row_major") { + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor; + } else if (lowerTokens[2] == "column_major") { + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; + } else { + // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major) + warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), ""); + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; + } + return; + } + + // Handle once + if (lowerTokens[0] == "once") { + warn(loc, "not implemented", "#pragma once", ""); + return; + } +} + +// +// Look at a '.' matrix selector string and change it into components +// for a matrix. There are two types: +// +// _21 second row, first column (one based) +// _m21 third row, second column (zero based) +// +// Returns true if there is no error. +// +bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows, + TSwizzleSelectors& components) +{ + int startPos[MaxSwizzleSelectors]; + int numComps = 0; + TString compString = fields; + + // Find where each component starts, + // recording the first character position after the '_'. + for (size_t c = 0; c < compString.size(); ++c) { + if (compString[c] == '_') { + if (numComps >= MaxSwizzleSelectors) { + error(loc, "matrix component swizzle has too many components", compString.c_str(), ""); + return false; + } + if (c > compString.size() - 3 || + ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) { + error(loc, "matrix component swizzle missing", compString.c_str(), ""); + return false; + } + startPos[numComps++] = (int)c + 1; + } + } + + // Process each component + for (int i = 0; i < numComps; ++i) { + int pos = startPos[i]; + int bias = -1; + if (compString[pos] == 'm' || compString[pos] == 'M') { + bias = 0; + ++pos; + } + TMatrixSelector comp; + comp.coord1 = compString[pos+0] - '0' + bias; + comp.coord2 = compString[pos+1] - '0' + bias; + if (comp.coord1 < 0 || comp.coord1 >= cols) { + error(loc, "matrix row component out of range", compString.c_str(), ""); + return false; + } + if (comp.coord2 < 0 || comp.coord2 >= rows) { + error(loc, "matrix column component out of range", compString.c_str(), ""); + return false; + } + components.push_back(comp); + } + + return true; +} + +// If the 'comps' express a column of a matrix, +// return the column. Column means the first coords all match. +// +// Otherwise, return -1. +// +int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors& selector) +{ + int col = -1; + + // right number of comps? + if (selector.size() != rows) + return -1; + + // all comps in the same column? + // rows in order? + col = selector[0].coord1; + for (int i = 0; i < rows; ++i) { + if (col != selector[i].coord1) + return -1; + if (i != selector[i].coord2) + return -1; + } + + return col; +} + +// +// Handle seeing a variable identifier in the grammar. +// +TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string) +{ + int thisDepth; + TSymbol* symbol = symbolTable.find(*string, thisDepth); + if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { + error(loc, "expected symbol, not user-defined type", string->c_str(), ""); + return nullptr; + } + + const TVariable* variable = nullptr; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; + TIntermTyped* node = nullptr; + if (anon) { + // It was a member of an anonymous container, which could be a 'this' structure. + + // Create a subtree for its dereference. + if (thisDepth > 0) { + variable = getImplicitThis(thisDepth); + if (variable == nullptr) + error(loc, "cannot access member variables (static member function?)", "this", ""); + } + if (variable == nullptr) + variable = anon->getAnonContainer().getAsVariable(); + + TIntermTyped* container = intermediate.addSymbol(*variable, loc); + TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); + node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); + + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().hiddenMember()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); + } else { + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : nullptr; + if (variable) { + if ((variable->getType().getBasicType() == EbtBlock || + variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) { + error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); + variable = nullptr; + } + } else { + if (symbol) + error(loc, "variable name expected", string->c_str(), ""); + } + + // Recovery, if it wasn't found or was not a variable. + if (variable == nullptr) { + error(loc, "unknown variable", string->c_str(), ""); + variable = new TVariable(string, TType(EbtVoid)); + } + + if (variable->getType().getQualifier().isFrontEndConstant()) + node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); + else + node = intermediate.addSymbol(*variable, loc); + } + + if (variable->getType().getQualifier().isIo()) + intermediate.addIoAccessed(*string); + + return node; +} + +// +// Handle operator[] on any objects it applies to. Currently: +// Textures +// Buffers +// +TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + // handle r-value operator[] on textures and images. l-values will be processed later. + if (base->getType().getBasicType() == EbtSampler && !base->isArray()) { + const TSampler& sampler = base->getType().getSampler(); + if (sampler.isImage() || sampler.isTexture()) { + if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) { + // The first operator[] to a .mips[] sequence is the mip level. We'll remember it. + mipsOperatorMipArg.back().mipLevel = index; + return base; // next [] index is to the same base. + } else { + TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch); + + TType sampReturnType; + getTextureReturnType(sampler, sampReturnType); + + load->setType(sampReturnType); + load->setLoc(loc); + load->getSequence().push_back(base); + load->getSequence().push_back(index); + + // Textures need a MIP. If we saw one go by, use it. Otherwise, use zero. + if (sampler.isTexture()) { + if (! mipsOperatorMipArg.empty()) { + load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel); + mipsOperatorMipArg.pop_back(); + } else { + load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true)); + } + } + + return load; + } + } + } + + // Handle operator[] on structured buffers: this indexes into the array element of the buffer. + // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO). + TIntermTyped* sbArray = indexStructBufferContent(loc, base); + if (sbArray != nullptr) { + // Now we'll apply the [] index to that array + const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc); + const TType derefType(sbArray->getType(), 0); + element->setType(derefType); + return element; + } + + return nullptr; +} + +// +// Cast index value to a uint if it isn't already (for operator[], load indexes, etc) +TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index) +{ + const TBasicType indexBasicType = index->getType().getBasicType(); + const int vecSize = index->getType().getVectorSize(); + + // We can use int types directly as the index + if (indexBasicType == EbtInt || indexBasicType == EbtUint || + indexBasicType == EbtInt64 || indexBasicType == EbtUint64) + return index; + + // Cast index to unsigned integer if it isn't one. + return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index); +} + +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + index = makeIntegerIndex(index); + + if (index == nullptr) { + error(loc, " unknown index type ", "", ""); + return nullptr; + } + + TIntermTyped* result = handleBracketOperator(loc, base, index); + + if (result != nullptr) + return result; // it was handled as an operator[] + + bool flattened = false; + int indexValue = 0; + if (index->getQualifier().isFrontEndConstant()) + indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); + + variableCheck(base); + if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", + base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + } else if (base->getType().getQualifier().isFrontEndConstant() && + index->getQualifier().isFrontEndConstant()) { + // both base and index are front-end constants + checkIndex(loc, base->getType(), indexValue); + return intermediate.foldDereference(base, indexValue, loc); + } else { + // at least one of base and index is variable... + + if (index->getQualifier().isFrontEndConstant()) + checkIndex(loc, base->getType(), indexValue); + + if (base->getType().isScalarOrVec1()) + result = base; + else if (base->getAsSymbolNode() && wasFlattened(base)) { + if (index->getQualifier().storage != EvqConst) + error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), ""); + + result = flattenAccess(base, indexValue); + flattened = (result != base); + } else { + if (index->getQualifier().isFrontEndConstant()) { + if (base->getType().isUnsizedArray()) + base->getWritableType().updateImplicitArraySize(indexValue + 1); + else + checkIndex(loc, base->getType(), indexValue); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + } + + if (result == nullptr) { + // Insert dummy error-recovery result + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + } else { + // If the array reference was flattened, it has the correct type. E.g, if it was + // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps. + // In that case, we preserve the qualifiers. + if (!flattened) { + // Insert valid dereferenced result + TType newType(base->getType(), 0); // dereferenced type + if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) + newType.getQualifier().storage = EvqConst; + else + newType.getQualifier().storage = EvqTemporary; + result->setType(newType); + } + } + + return result; +} + +// Handle seeing a binary node with a math operation. +TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, + TIntermTyped* left, TIntermTyped* right) +{ + TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc); + if (result == nullptr) + binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); + + return result; +} + +// Handle seeing a unary node with a math operation. +TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, + TIntermTyped* childNode) +{ + TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc); + + if (result) + return result; + else + unaryOpError(loc, str, childNode->getCompleteString()); + + return childNode; +} +// +// Return true if the name is a struct buffer method +// +bool HlslParseContext::isStructBufferMethod(const TString& name) const +{ + return + name == "GetDimensions" || + name == "Load" || + name == "Load2" || + name == "Load3" || + name == "Load4" || + name == "Store" || + name == "Store2" || + name == "Store3" || + name == "Store4" || + name == "InterlockedAdd" || + name == "InterlockedAnd" || + name == "InterlockedCompareExchange" || + name == "InterlockedCompareStore" || + name == "InterlockedExchange" || + name == "InterlockedMax" || + name == "InterlockedMin" || + name == "InterlockedOr" || + name == "InterlockedXor" || + name == "IncrementCounter" || + name == "DecrementCounter" || + name == "Append" || + name == "Consume"; +} + +// +// Handle seeing a base.field dereference in the grammar, where 'field' is a +// swizzle or member variable. +// +TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + variableCheck(base); + + if (base->isArray()) { + error(loc, "cannot apply to an array:", ".", field.c_str()); + return base; + } + + TIntermTyped* result = base; + + if (base->getType().getBasicType() == EbtSampler) { + // Handle .mips[mipid][pos] operation on textures + const TSampler& sampler = base->getType().getSampler(); + if (sampler.isTexture() && field == "mips") { + // Push a null to signify that we expect a mip level under operator[] next. + mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr)); + // Keep 'result' pointing to 'base', since we expect an operator[] to go by next. + } else { + if (field == "mips") + error(loc, "unexpected texture type for .mips[][] operator:", + base->getType().getCompleteString().c_str(), ""); + else + error(loc, "unexpected operator on texture type:", field.c_str(), + base->getType().getCompleteString().c_str()); + } + } else if (base->isVector() || base->isScalar()) { + TSwizzleSelectors selectors; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + + if (base->isScalar()) { + if (selectors.size() == 1) + return result; + else { + TType type(base->getBasicType(), EvqTemporary, selectors.size()); + return addConstructor(loc, base, type); + } + } + // Use EOpIndexDirect (below) with vec1.x so that it remains l-value (Test/hlsl.swizzle.vec1.comp) + if (base->getVectorSize() == 1 && selectors.size() > 1) { + TType scalarType(base->getBasicType(), EvqTemporary, 1); + TType vectorType(base->getBasicType(), EvqTemporary, selectors.size()); + return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType); + } + + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldSwizzle(base, selectors, loc); + else { + if (selectors.size() == 1) { + TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary)); + } else { + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, + selectors.size())); + } + } + } else if (base->isMatrix()) { + TSwizzleSelectors selectors; + if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors)) + return result; + + if (selectors.size() == 1) { + // Representable by m[c][r] + if (base->getType().getQualifier().isFrontEndConstant()) { + result = intermediate.foldDereference(base, selectors[0].coord1, loc); + result = intermediate.foldDereference(result, selectors[0].coord2, loc); + } else { + result = intermediate.addIndex(EOpIndexDirect, base, + intermediate.addConstantUnion(selectors[0].coord1, loc), + loc); + TType dereferencedCol(base->getType(), 0); + result->setType(dereferencedCol); + result = intermediate.addIndex(EOpIndexDirect, result, + intermediate.addConstantUnion(selectors[0].coord2, loc), + loc); + TType dereferenced(dereferencedCol, 0); + result->setType(dereferenced); + } + } else { + int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors); + if (column >= 0) { + // Representable by m[c] + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldDereference(base, column, loc); + else { + result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc), + loc); + TType dereferenced(base->getType(), 0); + result->setType(dereferenced); + } + } else { + // general case, not a column, not a single component + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, + selectors.size())); + } + } + } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { + const TTypeList* fields = base->getType().getStruct(); + bool fieldFound = false; + int member; + for (member = 0; member < (int)fields->size(); ++member) { + if ((*fields)[member].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + if (fieldFound) { + if (base->getAsSymbolNode() && wasFlattened(base)) { + result = flattenAccess(base, member); + } else { + if (base->getType().getQualifier().storage == EvqConst) + result = intermediate.foldDereference(base, member, loc); + else { + TIntermTyped* index = intermediate.addConstantUnion(member, loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[member].type); + } + } + } else + error(loc, "no such field in structure", field.c_str(), ""); + } else + error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + return result; +} + +// +// Return true if the field should be treated as a built-in method. +// Return false otherwise. +// +bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field) +{ + if (base == nullptr) + return false; + + variableCheck(base); + + if (base->getType().getBasicType() == EbtSampler) { + return true; + } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) { + return true; + } else if (field == "Append" || + field == "RestartStrip") { + // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but + // the code is around in the shader source. + return true; + } else + return false; +} + +// Independently establish a built-in that is a member of a structure. +// 'arraySizes' are what's desired for the independent built-in, whatever +// the higher-level source/expression of them was. +void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes, + const TQualifier& outerQualifier) +{ + // Because of arrays of structs, we might be asked more than once, + // but the arraySizes passed in should have captured the whole thing + // the first time. + // However, clip/cull rely on multiple updates. + if (!isClipOrCullDistance(memberType)) + if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) != + splitBuiltIns.end()) + return; + + TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType); + + if (arraySizes != nullptr && !memberType.isArray()) + ioVar->getWritableType().copyArraySizes(*arraySizes); + + splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar; + if (!isClipOrCullDistance(ioVar->getType())) + trackLinkage(*ioVar); + + // Merge qualifier from the user structure + mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier); + + // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the + // shader declared them). This is done after mergeQualifiers(), in case fixBuiltInIoType looks + // at the qualifier to determine e.g, in or out qualifications. + fixBuiltInIoType(ioVar->getWritableType()); + + // But, not location, we're losing that + ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; +} + +// Split a type into +// 1. a struct of non-I/O members +// 2. a collection of independent I/O variables +void HlslParseContext::split(const TVariable& variable) +{ + // Create a new variable: + const TType& clonedType = *variable.getType().clone(); + const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier()); + splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType); +} + +// Recursive implementation of split(). +// Returns reference to the modified type. +const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier) +{ + if (type.isStruct()) { + TTypeList* userStructure = type.getWritableStruct(); + for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) { + if (ioType->type->isBuiltIn()) { + // move out the built-in + splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier); + ioType = userStructure->erase(ioType); + } else { + split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier); + ++ioType; + } + } + } + + return type; +} + +// Is this an aggregate that should be flattened? +// Can be applied to intermediate levels of type in a hierarchy. +// Some things like flattening uniform arrays are only about the top level +// of the aggregate, triggered on 'topLevel'. +bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const +{ + switch (qualifier) { + case EvqVaryingIn: + case EvqVaryingOut: + return type.isStruct() || type.isArray(); + case EvqUniform: + return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) || + (type.isStruct() && type.containsOpaque()); + default: + return false; + }; +} + +// Top level variable flattening: construct data +void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed) +{ + const TType& type = variable.getType(); + + // If it's a standalone built-in, there is nothing to flatten + if (type.isBuiltIn() && !type.isStruct()) + return; + + + auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(), + TFlattenData(type.getQualifier().layoutBinding, + type.getQualifier().layoutLocation))); + + if (type.isStruct() && type.getStruct()->size()==0) + return; + // if flattening arrayed io struct, array each member of dereferenced type + if (arrayed) { + const TType dereferencedType(type, 0); + flatten(variable, dereferencedType, entry.first->second, variable.getName(), linkage, + type.getQualifier(), type.getArraySizes()); + } else { + flatten(variable, type, entry.first->second, variable.getName(), linkage, + type.getQualifier(), nullptr); + } +} + +// Recursively flatten the given variable at the provided type, building the flattenData as we go. +// +// This is mutually recursive with flattenStruct and flattenArray. +// We are going to flatten an arbitrarily nested composite structure into a linear sequence of +// members, and later on, we want to turn a path through the tree structure into a final +// location in this linear sequence. +// +// If the tree was N-ary, that can be directly calculated. However, we are dealing with +// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must +// build a data structure to allow the sequence of bracket and dot operators on arrays and +// structs to arrive at the proper member. +// +// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers. +// The leaves are the indexes into the flattened member array. +// Each level will have the next location for the Nth item stored sequentially, so for instance: +// +// struct { float2 a[2]; int b; float4 c[3] }; +// +// This will produce the following flattened tree: +// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 +// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5} +// +// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse: +// (0+2) = 8 --> (8+1) = 12 --> 12 = 4 +// +// so the 4th flattened member in traversal order is ours. +// +int HlslParseContext::flatten(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + // If something is an arrayed struct, the array flattener will recursively call flatten() + // to then flatten the struct, so this is an "if else": we don't do both. + if (type.isArray()) + return flattenArray(variable, type, flattenData, name, linkage, outerQualifier); + else if (type.isStruct()) + return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes); + else { + assert(0); // should never happen + return -1; + } +} + +// Add a single flattened member to the flattened data being tracked for the composite +// Returns true for the final flattening level. +int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData, + const TString& memberName, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + if (!shouldFlatten(type, outerQualifier.storage, false)) { + // This is as far as we flatten. Insert the variable. + TVariable* memberVariable = makeInternalVariable(memberName, type); + mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier()); + + if (flattenData.nextBinding != TQualifier::layoutBindingEnd) + memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++; + + if (memberVariable->getType().isBuiltIn()) { + // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number) + memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + } else { + // inherited locations must be auto bumped, not replicated + if (flattenData.nextLocation != TQualifier::layoutLocationEnd) { + memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation; + flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language); + nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation); + } + } + + // Only propagate arraysizes here for arrayed io + if (variable.getType().getQualifier().isArrayedIo(language) && builtInArraySizes != nullptr) + memberVariable->getWritableType().copyArraySizes(*builtInArraySizes); + + flattenData.offsets.push_back(static_cast(flattenData.members.size())); + flattenData.members.push_back(memberVariable); + + if (linkage) + trackLinkage(*memberVariable); + + return static_cast(flattenData.offsets.size()) - 1; // location of the member reference + } else { + // Further recursion required + return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes); + } +} + +// Figure out the mapping between an aggregate's top members and an +// equivalent set of individual variables. +// +// Assumes shouldFlatten() or equivalent was called first. +int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + assert(type.isStruct()); + + auto members = *type.getStruct(); + + // Reserve space for this tree level. + int start = static_cast(flattenData.offsets.size()); + int pos = start; + flattenData.offsets.resize(int(pos + members.size()), -1); + + for (int member = 0; member < (int)members.size(); ++member) { + TType& dereferencedType = *members[member].type; + if (dereferencedType.isBuiltIn()) + splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier); + else { + const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, + name + "." + dereferencedType.getFieldName(), + linkage, outerQualifier, + builtInArraySizes == nullptr && dereferencedType.isArray() + ? dereferencedType.getArraySizes() + : builtInArraySizes); + flattenData.offsets[pos++] = mpos; + } + } + + return start; +} + +// Figure out mapping between an array's members and an +// equivalent set of individual variables. +// +// Assumes shouldFlatten() or equivalent was called first. +int HlslParseContext::flattenArray(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier) +{ + assert(type.isSizedArray()); + + const int size = type.getOuterArraySize(); + const TType dereferencedType(type, 0); + + if (name.empty()) + name = variable.getName(); + + // Reserve space for this tree level. + int start = static_cast(flattenData.offsets.size()); + int pos = start; + flattenData.offsets.resize(int(pos + size), -1); + + for (int element=0; element < size; ++element) { + char elementNumBuf[20]; // sufficient for MAXINT + snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element); + const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, + name + elementNumBuf, linkage, outerQualifier, + type.getArraySizes()); + + flattenData.offsets[pos++] = mpos; + } + + return start; +} + +// Return true if we have flattened this node. +bool HlslParseContext::wasFlattened(const TIntermTyped* node) const +{ + return node != nullptr && node->getAsSymbolNode() != nullptr && + wasFlattened(node->getAsSymbolNode()->getId()); +} + +// Return true if we have split this structure +bool HlslParseContext::wasSplit(const TIntermTyped* node) const +{ + return node != nullptr && node->getAsSymbolNode() != nullptr && + wasSplit(node->getAsSymbolNode()->getId()); +} + +// Turn an access into an aggregate that was flattened to instead be +// an access to the individual variable the member was flattened to. +// Assumes wasFlattened() or equivalent was called first. +TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member) +{ + const TType dereferencedType(base->getType(), member); // dereferenced type + const TIntermSymbol& symbolNode = *base->getAsSymbolNode(); + TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage, + dereferencedType, symbolNode.getFlattenSubset()); + + return flattened ? flattened : base; +} +TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage, + const TType& dereferencedType, int subset) +{ + const auto flattenData = flattenMap.find(uniqueId); + + if (flattenData == flattenMap.end()) + return nullptr; + + // Calculate new cumulative offset from the packed tree + int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member]; + + TIntermSymbol* subsetSymbol; + if (!shouldFlatten(dereferencedType, outerStorage, false)) { + // Finished flattening: create symbol for variable + member = flattenData->second.offsets[newSubset]; + const TVariable* memberVariable = flattenData->second.members[member]; + subsetSymbol = intermediate.addSymbol(*memberVariable); + subsetSymbol->setFlattenSubset(-1); + } else { + + // If this is not the final flattening, accumulate the position and return + // an object of the partially dereferenced type. + subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", getLanguage(), dereferencedType); + subsetSymbol->setFlattenSubset(newSubset); + } + + return subsetSymbol; +} + +// For finding where the first leaf is in a subtree of a multi-level aggregate +// that is just getting a subset assigned. Follows the same logic as flattenAccess, +// but logically going down the "left-most" tree branch each step of the way. +// +// Returns the offset into the first leaf of the subset. +int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const +{ + const TIntermSymbol* sym = node.getAsSymbolNode(); + if (sym == nullptr) + return 0; + if (!sym->isArray() && !sym->isStruct()) + return 0; + int subset = sym->getFlattenSubset(); + if (subset == -1) + return 0; + + // Getting this far means a partial aggregate is identified by the flatten subset. + // Find the first leaf of the subset. + + const auto flattenData = flattenMap.find(sym->getId()); + if (flattenData == flattenMap.end()) + return 0; + + return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets); + + do { + subset = flattenData->second.offsets[subset]; + } while (true); +} +// Recursively do the desent +int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector& offsets) const +{ + if (!type.isArray() && !type.isStruct()) + return offsets[subset]; + TType derefType(type, 0); + return findSubtreeOffset(derefType, offsets[subset], offsets); +} + +// Find and return the split IO TVariable for id, or nullptr if none. +TVariable* HlslParseContext::getSplitNonIoVar(long long id) const +{ + const auto splitNonIoVar = splitNonIoVars.find(id); + if (splitNonIoVar == splitNonIoVars.end()) + return nullptr; + + return splitNonIoVar->second; +} + +// Pass through to base class after remembering built-in mappings. +void HlslParseContext::trackLinkage(TSymbol& symbol) +{ + TBuiltInVariable biType = symbol.getType().getQualifier().builtIn; + + if (biType != EbvNone) + builtInTessLinkageSymbols[biType] = symbol.clone(); + + TParseContextBase::trackLinkage(symbol); +} + + +// Returns true if the built-in is a clip or cull distance variable. +bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn) +{ + return builtIn == EbvClipDistance || builtIn == EbvCullDistance; +} + +// Some types require fixed array sizes in SPIR-V, but can be scalars or +// arrays of sizes SPIR-V doesn't allow. For example, tessellation factors. +// This creates the right size. A conversion is performed when the internal +// type is copied to or from the external type. This corrects the externally +// facing input or output type to abide downstream semantics. +void HlslParseContext::fixBuiltInIoType(TType& type) +{ + int requiredArraySize = 0; + int requiredVectorSize = 0; + + switch (type.getQualifier().builtIn) { + case EbvTessLevelOuter: requiredArraySize = 4; break; + case EbvTessLevelInner: requiredArraySize = 2; break; + + case EbvSampleMask: + { + // Promote scalar to array of size 1. Leave existing arrays alone. + if (!type.isArray()) + requiredArraySize = 1; + break; + } + + case EbvWorkGroupId: requiredVectorSize = 3; break; + case EbvGlobalInvocationId: requiredVectorSize = 3; break; + case EbvLocalInvocationId: requiredVectorSize = 3; break; + case EbvTessCoord: requiredVectorSize = 3; break; + + default: + if (isClipOrCullDistance(type)) { + const int loc = type.getQualifier().layoutLocation; + + if (type.getQualifier().builtIn == EbvClipDistance) { + if (type.getQualifier().storage == EvqVaryingIn) + clipSemanticNSizeIn[loc] = type.getVectorSize(); + else + clipSemanticNSizeOut[loc] = type.getVectorSize(); + } else { + if (type.getQualifier().storage == EvqVaryingIn) + cullSemanticNSizeIn[loc] = type.getVectorSize(); + else + cullSemanticNSizeOut[loc] = type.getVectorSize(); + } + } + + return; + } + + // Alter or set vector size as needed. + if (requiredVectorSize > 0) { + TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize); + newType.getQualifier() = type.getQualifier(); + + type.shallowCopy(newType); + } + + // Alter or set array size as needed. + if (requiredArraySize > 0) { + if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) { + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(requiredArraySize); + type.transferArraySizes(arraySizes); + } + } +} + +// Variables that correspond to the user-interface in and out of a stage +// (not the built-in interface) are +// - assigned locations +// - registered as a linkage node (part of the stage's external interface). +// Assumes it is called in the order in which locations should be assigned. +void HlslParseContext::assignToInterface(TVariable& variable) +{ + const auto assignLocation = [&](TVariable& variable) { + TType& type = variable.getWritableType(); + if (!type.isStruct() || type.getStruct()->size() > 0) { + TQualifier& qualifier = type.getQualifier(); + if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) { + if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) { + // Strip off the outer array dimension for those having an extra one. + int size; + if (type.isArray() && qualifier.isArrayedIo(language)) { + TType elementType(type, 0); + size = intermediate.computeTypeLocationSize(elementType, language); + } else + size = intermediate.computeTypeLocationSize(type, language); + + if (qualifier.storage == EvqVaryingIn) { + variable.getWritableType().getQualifier().layoutLocation = nextInLocation; + nextInLocation += size; + } else { + variable.getWritableType().getQualifier().layoutLocation = nextOutLocation; + nextOutLocation += size; + } + } + trackLinkage(variable); + } + } + }; + + if (wasFlattened(variable.getUniqueId())) { + auto& memberList = flattenMap[variable.getUniqueId()].members; + for (auto member = memberList.begin(); member != memberList.end(); ++member) + assignLocation(**member); + } else if (wasSplit(variable.getUniqueId())) { + TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId()); + assignLocation(*splitIoVar); + } else { + assignLocation(variable); + } +} + +// +// Handle seeing a function declarator in the grammar. This is the precursor +// to recognizing a function prototype or function definition. +// +void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) +{ + // + // Multiple declarations of the same function name are allowed. + // + // If this is a definition, the definition production code will check for redefinitions + // (we don't know at this point if it's a definition or not). + // + bool builtIn; + TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); + const TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (prototype) { + // All built-in functions are defined, even though they don't have a body. + // Count their prototype as a definition instead. + if (symbolTable.atBuiltInLevel()) + function.setDefined(); + else { + if (prevDec && ! builtIn) + symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const + function.setPrototyped(); + } + } + + // This insert won't actually insert it if it's a duplicate signature, but it will still check for + // other forms of name collisions. + if (! symbolTable.insert(function)) + error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); +} + +// For struct buffers with counters, we must pass the counter buffer as hidden parameter. +// This adds the hidden parameter to the parameter list in 'paramNodes' if needed. +// Otherwise, it's a no-op +void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param, + TIntermAggregate*& paramNodes) +{ + if (! hasStructBuffCounter(*param.type)) + return; + + const TString counterBlockName(intermediate.addCounterBufferName(*param.name)); + + TType counterType; + counterBufferType(loc, counterType); + TVariable *variable = makeInternalVariable(counterBlockName, counterType); + + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); +} + +// +// Handle seeing the function prototype in front of a function definition in the grammar. +// The body is handled after this function returns. +// +// Returns an aggregate of parameter-symbol nodes. +// +TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function, + const TAttributes& attributes, + TIntermNode*& entryPointTree) +{ + currentCaller = function.getMangledName(); + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (prevDec == nullptr) + error(loc, "can't find function", function.getName().c_str(), ""); + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurrence. + + if (prevDec && prevDec->isDefined()) { + // Then this function already has a body. + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec && ! prevDec->isDefined()) { + prevDec->setDefined(); + + // Remember the return type for later checking for RETURN statements. + currentFunctionType = &(prevDec->getType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + // Entry points need different I/O and other handling, transform it so the + // rest of this function doesn't care. + entryPointTree = transformEntryPoint(loc, function, attributes); + + // + // New symbol table scope for body of function plus its arguments + // + pushScope(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the AST, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != nullptr) { + TVariable *variable = new TVariable(param.name, *param.type); + + if (i == 0 && function.hasImplicitThis()) { + // Anonymous 'this' members are already in a symbol-table level, + // and we need to know what function parameter to map them to. + symbolTable.makeInternalVariable(*variable); + pushImplicitThis(variable); + } + + // Insert the parameters with name in the symbol table. + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + + // Add parameters to the AST list. + if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) { + // Expand the AST parameter nodes (but not the name mangling or symbol table view) + // for structures that need to be flattened. + flatten(*variable, false); + const TTypeList* structure = variable->getType().getStruct(); + for (int mem = 0; mem < (int)structure->size(); ++mem) { + paramNodes = intermediate.growAggregate(paramNodes, + flattenAccess(variable->getUniqueId(), mem, + variable->getType().getQualifier().storage, + *(*structure)[mem].type), + loc); + } + } else { + // Add the parameter to the AST + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); + } + + // Add hidden AST parameter for struct buffer counters, if needed. + addStructBufferHiddenCounterParam(loc, param, paramNodes); + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); + } + if (function.hasIllegalImplicitThis()) + pushImplicitThis(nullptr); + + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + controlFlowNestingLevel = 0; + postEntryPointReturn = false; + + return paramNodes; +} + +// Handle all [attrib] attribute for the shader entry point +void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes) +{ + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatNumThreads: + { + const TIntermSequence& sequence = it->args->getSequence(); + for (int lid = 0; lid < int(sequence.size()); ++lid) + intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst()); + break; + } + case EatInstance: + { + int invocations; + + if (!it->getInt(invocations)) { + error(loc, "invalid instance", "", ""); + } else { + if (!intermediate.setInvocations(invocations)) + error(loc, "cannot change previously set instance attribute", "", ""); + } + break; + } + case EatMaxVertexCount: + { + int maxVertexCount; + + if (! it->getInt(maxVertexCount)) { + error(loc, "invalid maxvertexcount", "", ""); + } else { + if (! intermediate.setVertices(maxVertexCount)) + error(loc, "cannot change previously set maxvertexcount attribute", "", ""); + } + break; + } + case EatPatchConstantFunc: + { + TString pcfName; + if (! it->getString(pcfName, 0, false)) { + error(loc, "invalid patch constant function", "", ""); + } else { + patchConstantFunctionName = pcfName; + } + break; + } + case EatDomain: + { + // Handle [domain("...")] + TString domainStr; + if (! it->getString(domainStr)) { + error(loc, "invalid domain", "", ""); + } else { + TLayoutGeometry domain = ElgNone; + + if (domainStr == "tri") { + domain = ElgTriangles; + } else if (domainStr == "quad") { + domain = ElgQuads; + } else if (domainStr == "isoline") { + domain = ElgIsolines; + } else { + error(loc, "unsupported domain type", domainStr.c_str(), ""); + } + + if (language == EShLangTessEvaluation) { + if (! intermediate.setInputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + } else { + if (! intermediate.setOutputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + } + } + break; + } + case EatOutputTopology: + { + // Handle [outputtopology("...")] + TString topologyStr; + if (! it->getString(topologyStr)) { + error(loc, "invalid outputtopology", "", ""); + } else { + TVertexOrder vertexOrder = EvoNone; + TLayoutGeometry primitive = ElgNone; + + if (topologyStr == "point") { + intermediate.setPointMode(); + } else if (topologyStr == "line") { + primitive = ElgIsolines; + } else if (topologyStr == "triangle_cw") { + vertexOrder = EvoCw; + primitive = ElgTriangles; + } else if (topologyStr == "triangle_ccw") { + vertexOrder = EvoCcw; + primitive = ElgTriangles; + } else { + error(loc, "unsupported outputtopology type", topologyStr.c_str(), ""); + } + + if (vertexOrder != EvoNone) { + if (! intermediate.setVertexOrder(vertexOrder)) { + error(loc, "cannot change previously set outputtopology", + TQualifier::getVertexOrderString(vertexOrder), ""); + } + } + if (primitive != ElgNone) + intermediate.setOutputPrimitive(primitive); + } + break; + } + case EatPartitioning: + { + // Handle [partitioning("...")] + TString partitionStr; + if (! it->getString(partitionStr)) { + error(loc, "invalid partitioning", "", ""); + } else { + TVertexSpacing partitioning = EvsNone; + + if (partitionStr == "integer") { + partitioning = EvsEqual; + } else if (partitionStr == "fractional_even") { + partitioning = EvsFractionalEven; + } else if (partitionStr == "fractional_odd") { + partitioning = EvsFractionalOdd; + //} else if (partition == "pow2") { // TODO: currently nothing to map this to. + } else { + error(loc, "unsupported partitioning type", partitionStr.c_str(), ""); + } + + if (! intermediate.setVertexSpacing(partitioning)) + error(loc, "cannot change previously set partitioning", + TQualifier::getVertexSpacingString(partitioning), ""); + } + break; + } + case EatOutputControlPoints: + { + // Handle [outputcontrolpoints("...")] + int ctrlPoints; + if (! it->getInt(ctrlPoints)) { + error(loc, "invalid outputcontrolpoints", "", ""); + } else { + if (! intermediate.setVertices(ctrlPoints)) { + error(loc, "cannot change previously set outputcontrolpoints attribute", "", ""); + } + } + break; + } + case EatEarlyDepthStencil: + intermediate.setEarlyFragmentTests(); + break; + case EatBuiltIn: + case EatLocation: + // tolerate these because of dual use of entrypoint and type attributes + break; + default: + warn(loc, "attribute does not apply to entry point", "", ""); + break; + } + } +} + +// Update the given type with any type-like attribute information in the +// attributes. +void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type, + bool allowEntry) +{ + if (attributes.size() == 0) + return; + + int value; + TString builtInString; + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatLocation: + // location + if (it->getInt(value)) + type.getQualifier().layoutLocation = value; + else + error(loc, "needs a literal integer", "location", ""); + break; + case EatBinding: + // binding + if (it->getInt(value)) { + type.getQualifier().layoutBinding = value; + type.getQualifier().layoutSet = 0; + } else + error(loc, "needs a literal integer", "binding", ""); + // set + if (it->getInt(value, 1)) + type.getQualifier().layoutSet = value; + break; + case EatGlobalBinding: + // global cbuffer binding + if (it->getInt(value)) + globalUniformBinding = value; + else + error(loc, "needs a literal integer", "global binding", ""); + // global cbuffer set + if (it->getInt(value, 1)) + globalUniformSet = value; + break; + case EatInputAttachment: + // input attachment + if (it->getInt(value)) + type.getQualifier().layoutAttachment = value; + else + error(loc, "needs a literal integer", "input attachment", ""); + break; + case EatBuiltIn: + // PointSize built-in + if (it->getString(builtInString, 0, false)) { + if (builtInString == "PointSize") + type.getQualifier().builtIn = EbvPointSize; + } + break; + case EatPushConstant: + // push_constant + type.getQualifier().layoutPushConstant = true; + break; + case EatConstantId: + // specialization constant + if (type.getQualifier().storage != EvqConst) { + error(loc, "needs a const type", "constant_id", ""); + break; + } + if (it->getInt(value)) { + TSourceLoc loc; + loc.init(); + setSpecConstantId(loc, type.getQualifier(), value); + } + break; + + // image formats + case EatFormatRgba32f: type.getQualifier().layoutFormat = ElfRgba32f; break; + case EatFormatRgba16f: type.getQualifier().layoutFormat = ElfRgba16f; break; + case EatFormatR32f: type.getQualifier().layoutFormat = ElfR32f; break; + case EatFormatRgba8: type.getQualifier().layoutFormat = ElfRgba8; break; + case EatFormatRgba8Snorm: type.getQualifier().layoutFormat = ElfRgba8Snorm; break; + case EatFormatRg32f: type.getQualifier().layoutFormat = ElfRg32f; break; + case EatFormatRg16f: type.getQualifier().layoutFormat = ElfRg16f; break; + case EatFormatR11fG11fB10f: type.getQualifier().layoutFormat = ElfR11fG11fB10f; break; + case EatFormatR16f: type.getQualifier().layoutFormat = ElfR16f; break; + case EatFormatRgba16: type.getQualifier().layoutFormat = ElfRgba16; break; + case EatFormatRgb10A2: type.getQualifier().layoutFormat = ElfRgb10A2; break; + case EatFormatRg16: type.getQualifier().layoutFormat = ElfRg16; break; + case EatFormatRg8: type.getQualifier().layoutFormat = ElfRg8; break; + case EatFormatR16: type.getQualifier().layoutFormat = ElfR16; break; + case EatFormatR8: type.getQualifier().layoutFormat = ElfR8; break; + case EatFormatRgba16Snorm: type.getQualifier().layoutFormat = ElfRgba16Snorm; break; + case EatFormatRg16Snorm: type.getQualifier().layoutFormat = ElfRg16Snorm; break; + case EatFormatRg8Snorm: type.getQualifier().layoutFormat = ElfRg8Snorm; break; + case EatFormatR16Snorm: type.getQualifier().layoutFormat = ElfR16Snorm; break; + case EatFormatR8Snorm: type.getQualifier().layoutFormat = ElfR8Snorm; break; + case EatFormatRgba32i: type.getQualifier().layoutFormat = ElfRgba32i; break; + case EatFormatRgba16i: type.getQualifier().layoutFormat = ElfRgba16i; break; + case EatFormatRgba8i: type.getQualifier().layoutFormat = ElfRgba8i; break; + case EatFormatR32i: type.getQualifier().layoutFormat = ElfR32i; break; + case EatFormatRg32i: type.getQualifier().layoutFormat = ElfRg32i; break; + case EatFormatRg16i: type.getQualifier().layoutFormat = ElfRg16i; break; + case EatFormatRg8i: type.getQualifier().layoutFormat = ElfRg8i; break; + case EatFormatR16i: type.getQualifier().layoutFormat = ElfR16i; break; + case EatFormatR8i: type.getQualifier().layoutFormat = ElfR8i; break; + case EatFormatRgba32ui: type.getQualifier().layoutFormat = ElfRgba32ui; break; + case EatFormatRgba16ui: type.getQualifier().layoutFormat = ElfRgba16ui; break; + case EatFormatRgba8ui: type.getQualifier().layoutFormat = ElfRgba8ui; break; + case EatFormatR32ui: type.getQualifier().layoutFormat = ElfR32ui; break; + case EatFormatRgb10a2ui: type.getQualifier().layoutFormat = ElfRgb10a2ui; break; + case EatFormatRg32ui: type.getQualifier().layoutFormat = ElfRg32ui; break; + case EatFormatRg16ui: type.getQualifier().layoutFormat = ElfRg16ui; break; + case EatFormatRg8ui: type.getQualifier().layoutFormat = ElfRg8ui; break; + case EatFormatR16ui: type.getQualifier().layoutFormat = ElfR16ui; break; + case EatFormatR8ui: type.getQualifier().layoutFormat = ElfR8ui; break; + case EatFormatUnknown: type.getQualifier().layoutFormat = ElfNone; break; + + case EatNonWritable: type.getQualifier().readonly = true; break; + case EatNonReadable: type.getQualifier().writeonly = true; break; + + default: + if (! allowEntry) + warn(loc, "attribute does not apply to a type", "", ""); + break; + } + } +} + +// +// Do all special handling for the entry point, including wrapping +// the shader's entry point with the official entry point that will call it. +// +// The following: +// +// retType shaderEntryPoint(args...) // shader declared entry point +// { body } +// +// Becomes +// +// out retType ret; +// in iargs...; +// out oargs ...; +// +// void shaderEntryPoint() // synthesized, but official, entry point +// { +// args = iargs...; +// ret = @shaderEntryPoint(args...); +// oargs = args...; +// } +// retType @shaderEntryPoint(args...) +// { body } +// +// The symbol table will still map the original entry point name to the +// the modified function and its new name: +// +// symbol table: shaderEntryPoint -> @shaderEntryPoint +// +// Returns nullptr if no entry-point tree was built, otherwise, returns +// a subtree that creates the entry point. +// +TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, + const TAttributes& attributes) +{ + // Return true if this is a tessellation patch constant function input to a domain shader. + const auto isDsPcfInput = [this](const TType& type) { + return language == EShLangTessEvaluation && + type.contains([](const TType* t) { + return t->getQualifier().builtIn == EbvTessLevelOuter || + t->getQualifier().builtIn == EbvTessLevelInner; + }); + }; + + // if we aren't in the entry point, fix the IO as such and exit + if (! isEntrypointName(userFunction.getName())) { + remapNonEntryPointIO(userFunction); + return nullptr; + } + + entryPointFunction = &userFunction; // needed in finish() + + // Handle entry point attributes + handleEntryPointAttributes(loc, attributes); + + // entry point logic... + + // Move parameters and return value to shader in/out + TVariable* entryPointOutput; // gets created in remapEntryPointIO + TVector inputs; + TVector outputs; + remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs); + + // Further this return/in/out transform by flattening, splitting, and assigning locations + const auto makeVariableInOut = [&](TVariable& variable) { + if (variable.getType().isStruct()) { + bool arrayed = variable.getType().getQualifier().isArrayedIo(language); + flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */, arrayed); + } + // TODO: flatten arrays too + // TODO: flatten everything in I/O + // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed + + // For clip and cull distance, multiple output variables potentially get merged + // into one in assignClipCullDistance. That code in assignClipCullDistance + // handles the interface logic, so we avoid it here in that case. + if (!isClipOrCullDistance(variable.getType())) + assignToInterface(variable); + }; + if (entryPointOutput != nullptr) + makeVariableInOut(*entryPointOutput); + for (auto it = inputs.begin(); it != inputs.end(); ++it) + if (!isDsPcfInput((*it)->getType())) // wait until the end for PCF input (see comment below) + makeVariableInOut(*(*it)); + for (auto it = outputs.begin(); it != outputs.end(); ++it) + makeVariableInOut(*(*it)); + + // In the domain shader, PCF input must be at the end of the linkage. That's because in the + // hull shader there is no ordering: the output comes from the separate PCF, which does not + // participate in the argument list. That is always put at the end of the HS linkage, so the + // input side of the DS must match. The argument may be in any position in the DS argument list + // however, so this ensures the linkage is built in the correct order regardless of argument order. + if (language == EShLangTessEvaluation) { + for (auto it = inputs.begin(); it != inputs.end(); ++it) + if (isDsPcfInput((*it)->getType())) + makeVariableInOut(*(*it)); + } + + // Add uniform parameters to the $Global uniform block. + TVector opaque_uniforms; + for (int i = 0; i < userFunction.getParamCount(); i++) { + TType& paramType = *userFunction[i].type; + TString& paramName = *userFunction[i].name; + if (paramType.getQualifier().storage == EvqUniform) { + if (!paramType.containsOpaque()) { + // Add it to the global uniform block. + growGlobalUniformBlock(loc, paramType, paramName); + } else { + // Declare it as a separate variable. + TVariable *var = makeInternalVariable(paramName.c_str(), paramType); + opaque_uniforms.push_back(var); + } + } + } + + // Synthesize the call + + pushScope(); // matches the one in handleFunctionBody() + + // new signature + TType voidType(EbtVoid); + TFunction synthEntryPoint(&userFunction.getName(), voidType); + TIntermAggregate* synthParams = new TIntermAggregate(); + intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc); + intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str()); + intermediate.incrementEntryPointCount(); + TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table + + // change original name + userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table + + // Copy inputs (shader-in -> calling arg), while building up the call node + TVector argVars; + TIntermAggregate* synthBody = new TIntermAggregate(); + auto inputIt = inputs.begin(); + auto opaqueUniformIt = opaque_uniforms.begin(); + TIntermTyped* callingArgs = nullptr; + + for (int i = 0; i < userFunction.getParamCount(); i++) { + TParameter& param = userFunction[i]; + argVars.push_back(makeInternalVariable(*param.name, *param.type)); + argVars.back()->getWritableType().getQualifier().makeTemporary(); + + // Track the input patch, which is the only non-builtin supported by hull shader PCF. + if (param.getDeclaredBuiltIn() == EbvInputPatch) + inputPatch = argVars.back(); + + TIntermSymbol* arg = intermediate.addSymbol(*argVars.back()); + handleFunctionArgument(&callee, callingArgs, arg); + if (param.type->getQualifier().isParamInput()) { + TIntermTyped* input = intermediate.addSymbol(**inputIt); + if (input->getType().getQualifier().builtIn == EbvFragCoord && intermediate.getDxPositionW()) { + // Replace FragCoord W with reciprocal + auto pos_xyz = handleDotDereference(loc, input, "xyz"); + auto pos_w = handleDotDereference(loc, input, "w"); + auto one = intermediate.addConstantUnion(1.0, EbtFloat, loc); + auto recip_w = intermediate.addBinaryMath(EOpDiv, one, pos_w, loc); + TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); + dst->getSequence().push_back(pos_xyz); + dst->getSequence().push_back(recip_w); + dst->setType(TType(EbtFloat, EvqTemporary, 4)); + dst->setLoc(loc); + input = dst; + } + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, input)); + inputIt++; + } + if (param.type->getQualifier().storage == EvqUniform) { + if (!param.type->containsOpaque()) { + // Look it up in the $Global uniform block. + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, + handleVariable(loc, param.name))); + } else { + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, + intermediate.addSymbol(**opaqueUniformIt))); + ++opaqueUniformIt; + } + } + } + + // Call + currentCaller = synthEntryPoint.getMangledName(); + TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); + currentCaller = userFunction.getMangledName(); + + // Return value + if (entryPointOutput) { + TIntermTyped* returnAssign; + + // For hull shaders, the wrapped entry point return value is written to + // an array element as indexed by invocation ID, which we might have to make up. + // This is required to match SPIR-V semantics. + if (language == EShLangTessControl) { + TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); + + // If there is no user declared invocation ID, we must make one. + if (invocationIdSym == nullptr) { + TType invocationIdType(EbtUint, EvqIn, 1); + TString* invocationIdName = NewPoolTString("InvocationId"); + invocationIdType.getQualifier().builtIn = EbvInvocationId; + + TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType); + + globalQualifierFix(loc, variable->getWritableType().getQualifier()); + trackLinkage(*variable); + + invocationIdSym = intermediate.addSymbol(*variable); + } + + TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput), + invocationIdSym, loc); + + // Set the type of the array element being dereferenced + const TType derefElementType(entryPointOutput->getType(), 0); + element->setType(derefElementType); + + returnAssign = handleAssign(loc, EOpAssign, element, callReturn); + } else { + returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn); + } + intermediate.growAggregate(synthBody, returnAssign); + } else + intermediate.growAggregate(synthBody, callReturn); + + // Output copies + auto outputIt = outputs.begin(); + for (int i = 0; i < userFunction.getParamCount(); i++) { + TParameter& param = userFunction[i]; + + // GS outputs are via emit, so we do not copy them here. + if (param.type->getQualifier().isParamOutput()) { + if (param.getDeclaredBuiltIn() == EbvGsOutputStream) { + // GS output stream does not assign outputs here: it's the Append() method + // which writes to the output, probably multiple times separated by Emit. + // We merely remember the output to use, here. + gsStreamOutput = *outputIt; + } else { + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, + intermediate.addSymbol(**outputIt), + intermediate.addSymbol(*argVars[i]))); + } + + outputIt++; + } + } + + // Put the pieces together to form a full function subtree + // for the synthesized entry point. + synthBody->setOperator(EOpSequence); + TIntermNode* synthFunctionDef = synthParams; + handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef); + + entryPointFunctionBody = synthBody; + + return synthFunctionDef; +} + +void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, + TIntermNode*& node) +{ + node = intermediate.growAggregate(node, functionBody); + intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc); + node->getAsAggregate()->setName(function.getMangledName().c_str()); + + popScope(); + if (function.hasImplicitThis()) + popImplicitThis(); + + if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue) + error(loc, "function does not return a value:", "", function.getName().c_str()); +} + +// AST I/O is done through shader globals declared in the 'in' or 'out' +// storage class. An HLSL entry point has a return value, input parameters +// and output parameters. These need to get remapped to the AST I/O. +void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue, + TVector& inputs, TVector& outputs) +{ + // We might have in input structure type with no decorations that caused it + // to look like an input type, yet it has (e.g.) interpolation types that + // must be modified that turn it into an input type. + // Hence, a missing ioTypeMap for 'input' might need to be synthesized. + const auto synthesizeEditedInput = [this](TType& type) { + // True if a type needs to be 'flat' + const auto needsFlat = [](const TType& type) { + return type.containsBasicType(EbtInt) || + type.containsBasicType(EbtUint) || + type.containsBasicType(EbtInt64) || + type.containsBasicType(EbtUint64) || + type.containsBasicType(EbtBool) || + type.containsBasicType(EbtDouble); + }; + + if (language == EShLangFragment && needsFlat(type)) { + if (type.isStruct()) { + TTypeList* finalList = nullptr; + auto it = ioTypeMap.find(type.getStruct()); + if (it == ioTypeMap.end() || it->second.input == nullptr) { + // Getting here means we have no input struct, but we need one. + auto list = new TTypeList; + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + TType* newType = new TType; + newType->shallowCopy(*member->type); + TTypeLoc typeLoc = { newType, member->loc }; + list->push_back(typeLoc); + } + // install the new input type + if (it == ioTypeMap.end()) { + tIoKinds newLists = { list, nullptr, nullptr }; + ioTypeMap[type.getStruct()] = newLists; + } else + it->second.input = list; + finalList = list; + } else + finalList = it->second.input; + // edit for 'flat' + for (auto member = finalList->begin(); member != finalList->end(); ++member) { + if (needsFlat(*member->type)) { + member->type->getQualifier().clearInterpolation(); + member->type->getQualifier().flat = true; + } + } + } else { + type.getQualifier().clearInterpolation(); + type.getQualifier().flat = true; + } + } + }; + + // Do the actual work to make a type be a shader input or output variable, + // and clear the original to be non-IO (for use as a normal function parameter/return). + const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* { + TVariable* ioVariable = makeInternalVariable(name, type); + clearUniformInputOutput(type.getQualifier()); + if (type.isStruct()) { + auto newLists = ioTypeMap.find(ioVariable->getType().getStruct()); + if (newLists != ioTypeMap.end()) { + if (storage == EvqVaryingIn && newLists->second.input) + ioVariable->getWritableType().setStruct(newLists->second.input); + else if (storage == EvqVaryingOut && newLists->second.output) + ioVariable->getWritableType().setStruct(newLists->second.output); + } + } + if (storage == EvqVaryingIn) { + correctInput(ioVariable->getWritableType().getQualifier()); + if (language == EShLangTessEvaluation) + if (!ioVariable->getType().isArray()) + ioVariable->getWritableType().getQualifier().patch = true; + } else { + correctOutput(ioVariable->getWritableType().getQualifier()); + } + ioVariable->getWritableType().getQualifier().storage = storage; + + fixBuiltInIoType(ioVariable->getWritableType()); + + return ioVariable; + }; + + // return value is actually a shader-scoped output (out) + if (function.getType().getBasicType() == EbtVoid) { + returnValue = nullptr; + } else { + if (language == EShLangTessControl) { + // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an + // array in SPIR-V semantics. We'll write to it indexed by invocation ID. + + returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); + + TType outputType; + outputType.shallowCopy(function.getType()); + + // vertices has necessarily already been set when handling entry point attributes. + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(intermediate.getVertices()); + outputType.transferArraySizes(arraySizes); + + clearUniformInputOutput(function.getWritableType().getQualifier()); + returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut); + } else { + returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); + } + } + + // parameters are actually shader-scoped inputs and outputs (in or out) + for (int i = 0; i < function.getParamCount(); i++) { + TType& paramType = *function[i].type; + if (paramType.getQualifier().isParamInput()) { + synthesizeEditedInput(paramType); + TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn); + inputs.push_back(argAsGlobal); + } + if (paramType.getQualifier().isParamOutput()) { + TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut); + outputs.push_back(argAsGlobal); + } + } +} + +// An HLSL function that looks like an entry point, but is not, +// declares entry point IO built-ins, but these have to be undone. +void HlslParseContext::remapNonEntryPointIO(TFunction& function) +{ + // return value + if (function.getType().getBasicType() != EbtVoid) + clearUniformInputOutput(function.getWritableType().getQualifier()); + + // parameters. + // References to structuredbuffer types are left unmodified + for (int i = 0; i < function.getParamCount(); i++) + if (!isReference(*function[i].type)) + clearUniformInputOutput(function[i].type->getQualifier()); +} + +TIntermNode* HlslParseContext::handleDeclare(const TSourceLoc& loc, TIntermTyped* var) +{ + return intermediate.addUnaryNode(EOpDeclare, var, loc, TType(EbtVoid)); +} + +// Handle function returns, including type conversions to the function return type +// if necessary. +TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) +{ + functionReturnsValue = true; + + if (currentFunctionType->getBasicType() == EbtVoid) { + error(loc, "void function cannot return a value", "return", ""); + return intermediate.addBranch(EOpReturn, loc); + } else if (*currentFunctionType != value->getType()) { + value = intermediate.addConversion(EOpReturn, *currentFunctionType, value); + if (value && *currentFunctionType != value->getType()) + value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value); + if (value == nullptr || *currentFunctionType != value->getType()) { + error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); + return value; + } + } + + return intermediate.addBranch(EOpReturn, value, loc); +} + +void HlslParseContext::handleFunctionArgument(TFunction* function, + TIntermTyped*& arguments, TIntermTyped* newArg) +{ + TParameter param = { nullptr, new TType, nullptr }; + param.type->shallowCopy(newArg->getType()); + + function->addParameter(param); + if (arguments) + arguments = intermediate.growAggregate(arguments, newArg); + else + arguments = newArg; +} + +// FragCoord may require special loading: we can optionally reciprocate W. +TIntermTyped* HlslParseContext::assignFromFragCoord(const TSourceLoc& loc, TOperator op, + TIntermTyped* left, TIntermTyped* right) +{ + // If we are not asked for reciprocal W, use a plain old assign. + if (!intermediate.getDxPositionW()) + return intermediate.addAssign(op, left, right, loc); + + // If we get here, we should reciprocate W. + TIntermAggregate* assignList = nullptr; + + // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary. + TVariable* rhsTempVar = nullptr; + rhsTempVar = makeInternalVariable("@fragcoord", right->getType()); + rhsTempVar->getWritableType().getQualifier().makeTemporary(); + + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc); + } + + // tmp.w = 1.0 / tmp.w + { + const int W = 3; + + TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* index = intermediate.addConstantUnion(W, loc); + + TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc); + TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc); + + const TType derefType(right->getType(), 0); + + lhsElement->setType(derefType); + rhsElement->setType(derefType); + + auto one = intermediate.addConstantUnion(1.0, EbtFloat, loc); + auto recip_w = intermediate.addBinaryMath(EOpDiv, one, rhsElement, loc); + + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, recip_w, loc)); + } + + // Assign the rhs temp (now with W reciprocal) to the final output + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc)); + } + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// Position may require special handling: we can optionally invert Y. +// See: https://github.com/KhronosGroup/glslang/issues/1173 +// https://github.com/KhronosGroup/glslang/issues/494 +TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op, + TIntermTyped* left, TIntermTyped* right) +{ + // If we are not asked for Y inversion, use a plain old assign. + if (!intermediate.getInvertY()) + return intermediate.addAssign(op, left, right, loc); + + // If we get here, we should invert Y. + TIntermAggregate* assignList = nullptr; + + // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary. + TVariable* rhsTempVar = nullptr; + rhsTempVar = makeInternalVariable("@position", right->getType()); + rhsTempVar->getWritableType().getQualifier().makeTemporary(); + + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc); + } + + // pos.y = -pos.y + { + const int Y = 1; + + TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* index = intermediate.addConstantUnion(Y, loc); + + TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc); + TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc); + + const TType derefType(right->getType(), 0); + + lhsElement->setType(derefType); + rhsElement->setType(derefType); + + TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc); + + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc)); + } + + // Assign the rhs temp (now with Y inversion) to the final output + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc)); + } + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// Clip and cull distance require special handling due to a semantic mismatch. In HLSL, +// these can be float scalar, float vector, or arrays of float scalar or float vector. +// In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components +// (e.g, both x and y components of a float2) out into the destination float array. +// +// The values are assigned to sequential members of the output array. The inner dimension +// is vector components. The outer dimension is array elements. +TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId, + TIntermTyped* left, TIntermTyped* right) +{ + switch (language) { + case EShLangFragment: + case EShLangVertex: + case EShLangGeometry: + break; + default: + error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", ""); + return nullptr; + } + + TVariable** clipCullVar = nullptr; + + // Figure out if we are assigning to, or from, clip or cull distance. + const bool isOutput = isClipOrCullDistance(left->getType()); + + // This is the rvalue or lvalue holding the clip or cull distance. + TIntermTyped* clipCullNode = isOutput ? left : right; + // This is the value going into or out of the clip or cull distance. + TIntermTyped* internalNode = isOutput ? right : left; + + const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn; + + decltype(clipSemanticNSizeIn)* semanticNSize = nullptr; + + // Refer to either the clip or the cull distance, depending on semantic. + switch (builtInType) { + case EbvClipDistance: + clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput; + semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn; + break; + case EbvCullDistance: + clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput; + semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn; + break; + + // called invalidly: we expected a clip or a cull distance. + // static compile time problem: should not happen. + default: assert(0); return nullptr; + } + + // This is the offset in the destination array of a given semantic's data + std::array semanticOffset; + + // Calculate offset of variable of semantic N in destination array + int arrayLoc = 0; + int vecItems = 0; + + for (int x = 0; x < maxClipCullRegs; ++x) { + // See if we overflowed the vec4 packing + if ((vecItems + (*semanticNSize)[x]) > 4) { + arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4 + vecItems = 0; + } + + semanticOffset[x] = arrayLoc; + vecItems += (*semanticNSize)[x]; + arrayLoc += (*semanticNSize)[x]; + } + + + // It can have up to 2 array dimensions (in the case of geometry shader inputs) + const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes(); + const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0; + // vector sizes: + const int internalVectorSize = internalNode->getType().getVectorSize(); + // array sizes, or 1 if it's not an array: + const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1); + const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1); + + // The created type may be an array of arrays, e.g, for geometry shader inputs. + const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput); + + // If we haven't created the output already, create it now. + if (*clipCullVar == nullptr) { + // ClipDistance and CullDistance are handled specially in the entry point input/output copy + // algorithm, because they may need to be unpacked from components of vectors (or a scalar) + // into a float array, or vice versa. Here, we make the array the right size and type, + // which depends on the incoming data, which has several potential dimensions: + // * Semantic ID + // * vector size + // * array size + // Of those, semantic ID and array size cannot appear simultaneously. + // + // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two + // array dimensions. The shader's declaration may have one or two array dimensions. One is always + // the geometry's dimension. + + const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed; + + const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1); + const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1; + + TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1); + clipCullType.getQualifier() = clipCullNode->getType().getQualifier(); + + // Create required array dimension + TArraySizes* arraySizes = new TArraySizes; + if (isImplicitlyArrayed) + arraySizes->addInnerSize(requiredOuterArraySize); + arraySizes->addInnerSize(requiredInnerArraySize); + clipCullType.transferArraySizes(arraySizes); + + // Obtain symbol name: we'll use that for the symbol we introduce. + TIntermSymbol* sym = clipCullNode->getAsSymbolNode(); + assert(sym != nullptr); + + // We are moving the semantic ID from the layout location, so it is no longer needed or + // desired there. + clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + + // Create variable and track its linkage + *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType); + + trackLinkage(**clipCullVar); + } + + // Create symbol for the clip or cull variable. + TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar); + + // vector sizes: + const int clipCullVectorSize = clipCullSym->getType().getVectorSize(); + + // array sizes, or 1 if it's not an array: + const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes(); + const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1; + const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0); + + // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics. + // fixBuiltInIoType() should have handled that upstream. + assert(clipCullSym->getType().isArray()); + assert(clipCullSym->getType().getVectorSize() == 1); + assert(clipCullSym->getType().getBasicType() == EbtFloat); + + // We may be creating multiple sub-assignments. This is an aggregate to hold them. + // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed. + TIntermAggregate* assignList = nullptr; + + // Holds individual component assignments as we make them. + TIntermTyped* clipCullAssign = nullptr; + + // If the types are homomorphic, use a simple assign. No need to mess about with + // individual components. + if (clipCullSym->getType().isArray() == internalNode->getType().isArray() && + clipCullInnerArraySize == internalInnerArraySize && + clipCullOuterArraySize == internalOuterArraySize && + clipCullVectorSize == internalVectorSize) { + + if (isOutput) + clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc); + else + clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc); + + assignList = intermediate.growAggregate(assignList, clipCullAssign); + assignList->setOperator(EOpSequence); + + return assignList; + } + + // We are going to copy each component of the internal (per array element if indicated) to sequential + // array elements of the clipCullSym. This tracks the lhs element we're writing to as we go along. + // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above. + int clipCullInnerArrayPos = semanticOffset[semanticId]; + int clipCullOuterArrayPos = 0; + + // Lambda to add an index to a node, set the type of the result, and return the new node. + const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* { + const TType derefType(node->getType(), 0); + node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc); + node->setType(derefType); + return node; + }; + + // Loop through every component of every element of the internal, and copy to or from the matching external. + for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) { + for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) { + for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) { + // clip/cull array member to read from / write to: + TIntermTyped* clipCullMember = clipCullSym; + + // If implicitly arrayed, there is an outer array dimension involved + if (isImplicitlyArrayed) + clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos); + + // Index into proper array position for clip cull member + clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++); + + // if needed, start over with next outer array slice. + if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) { + clipCullInnerArrayPos = semanticOffset[semanticId]; + ++clipCullOuterArrayPos; + } + + // internal member to read from / write to: + TIntermTyped* internalMember = internalNode; + + // If internal node has outer array dimension, index appropriately. + if (internalArrayDims > 1) + internalMember = addIndex(internalMember, internalOuterArrayPos); + + // If internal node has inner array dimension, index appropriately. + if (internalArrayDims > 0) + internalMember = addIndex(internalMember, internalInnerArrayPos); + + // If internal node is a vector, extract the component of interest. + if (internalNode->getType().isVector()) + internalMember = addIndex(internalMember, internalComponent); + + // Create an assignment: output from internal to clip cull, or input from clip cull to internal. + if (isOutput) + clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc); + else + clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc); + + // Track assignment in the sequence. + assignList = intermediate.growAggregate(assignList, clipCullAssign); + } + } + } + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// Some simple source assignments need to be flattened to a sequence +// of AST assignments. Catch these and flatten, otherwise, pass through +// to intermediate.addAssign(). +// +// Also, assignment to matrix swizzles requires multiple component assignments, +// intercept those as well. +TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, + TIntermTyped* right) +{ + if (left == nullptr || right == nullptr) + return nullptr; + + // writing to opaques will require fixing transforms + if (left->getType().containsOpaque()) + intermediate.setNeedsLegalization(); + + if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle) + return handleAssignToMatrixSwizzle(loc, op, left, right); + + // Return true if the given node is an index operation into a split variable. + const auto indexesSplit = [this](const TIntermTyped* node) -> bool { + const TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode == nullptr) + return false; + + return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) && + wasSplit(binaryNode->getLeft()); + }; + + // Return symbol if node is symbol or index ref + const auto getSymbol = [](const TIntermTyped* node) -> const TIntermSymbol* { + const TIntermSymbol* symbolNode = node->getAsSymbolNode(); + if (symbolNode != nullptr) + return symbolNode; + + const TIntermBinary* binaryNode = node->getAsBinaryNode(); + if (binaryNode != nullptr && (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect)) + return binaryNode->getLeft()->getAsSymbolNode(); + + return nullptr; + }; + + // Return true if this stage assigns clip position with potentially inverted Y + const auto assignsClipPos = [this](const TIntermTyped* node) -> bool { + return node->getType().getQualifier().builtIn == EbvPosition && + (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation); + }; + + const TIntermSymbol* leftSymbol = getSymbol(left); + const TIntermSymbol* rightSymbol = getSymbol(right); + + const bool isSplitLeft = wasSplit(left) || indexesSplit(left); + const bool isSplitRight = wasSplit(right) || indexesSplit(right); + + const bool isFlattenLeft = wasFlattened(leftSymbol); + const bool isFlattenRight = wasFlattened(rightSymbol); + + // OK to do a single assign if neither side is split or flattened. Otherwise, + // fall through to a member-wise copy. + if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) { + // Clip and cull distance requires more processing. See comment above assignClipCullDistance. + if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) { + const bool isOutput = isClipOrCullDistance(left->getType()); + + const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation; + return assignClipCullDistance(loc, op, semanticId, left, right); + } else if (assignsClipPos(left)) { + // Position can require special handling: see comment above assignPosition + return assignPosition(loc, op, left, right); + } else if (left->getQualifier().builtIn == EbvSampleMask) { + // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars + // in the shader. Copy the scalar RHS into the LHS array element zero, if that happens. + if (left->isArray() && !right->isArray()) { + const TType derefType(left->getType(), 0); + left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc); + left->setType(derefType); + // Fall through to add assign. + } + } + + return intermediate.addAssign(op, left, right, loc); + } + + TIntermAggregate* assignList = nullptr; + const TVector* leftVariables = nullptr; + const TVector* rightVariables = nullptr; + + // A temporary to store the right node's value, so we don't keep indirecting into it + // if it's not a simple symbol. + TVariable* rhsTempVar = nullptr; + + // If the RHS is a simple symbol node, we'll copy it for each member. + TIntermSymbol* cloneSymNode = nullptr; + + int memberCount = 0; + + // Track how many items there are to copy. + if (left->getType().isStruct()) + memberCount = (int)left->getType().getStruct()->size(); + if (left->getType().isArray()) + memberCount = left->getType().getCumulativeArraySize(); + + if (isFlattenLeft) + leftVariables = &flattenMap.find(leftSymbol->getId())->second.members; + + if (isFlattenRight) { + rightVariables = &flattenMap.find(rightSymbol->getId())->second.members; + } else { + // The RHS is not flattened. There are several cases: + // 1. 1 item to copy: Use the RHS directly. + // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp. + // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member. + + if (memberCount <= 1) { + // case 1: we'll use the symbol directly below. Nothing to do. + } else { + if (right->getAsSymbolNode() != nullptr) { + // case 2: we'll copy the symbol per iteration below. + cloneSymNode = right->getAsSymbolNode(); + } else { + // case 3: assign to a temp, and indirect into that. + rhsTempVar = makeInternalVariable("flattenTemp", right->getType()); + rhsTempVar->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc); + + // Add this to the aggregate being built. + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(op, noFlattenRHS, right, loc), loc); + } + } + } + + // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in + // variables, which is awkward when copying between split and unsplit structures. This variable tracks + // array indirections so they can be percolated from outer structs to inner variables. + std::vector arrayElement; + + TStorageQualifier leftStorage = left->getType().getQualifier().storage; + TStorageQualifier rightStorage = right->getType().getQualifier().storage; + + int leftOffsetStart = findSubtreeOffset(*left); + int rightOffsetStart = findSubtreeOffset(*right); + int leftOffset = leftOffsetStart; + int rightOffset = rightOffsetStart; + + const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember, + bool flattened) + -> TIntermTyped * { + const bool split = isLeft ? isSplitLeft : isSplitRight; + + TIntermTyped* subTree; + const TType derefType(type, member); + const TVariable* builtInVar = nullptr; + if ((flattened || split) && derefType.isBuiltIn()) { + auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData( + derefType.getQualifier().builtIn, + isLeft ? leftStorage : rightStorage)); + if (splitPair != splitBuiltIns.end()) + builtInVar = splitPair->second; + } + if (builtInVar != nullptr) { + // copy from interstage IO built-in if needed + subTree = intermediate.addSymbol(*builtInVar); + + if (subTree->getType().isArray()) { + // Arrayness of builtIn symbols isn't handled by the normal recursion: + // it's been extracted and moved to the built-in. + if (!arrayElement.empty()) { + const TType splitDerefType(subTree->getType(), arrayElement.back()); + subTree = intermediate.addIndex(EOpIndexDirect, subTree, + intermediate.addConstantUnion(arrayElement.back(), loc), loc); + subTree->setType(splitDerefType); + } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) { + // This might also be a stage with arrayed outputs, in which case there's an index + // operation we should transfer to the output builtin. + + const TType splitDerefType(subTree->getType(), 0); + subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree, + splitNode->getAsBinaryNode()->getRight(), loc); + subTree->setType(splitDerefType); + } + } + } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) { + if (isLeft) { + // offset will cycle through variables for arrayed io + if (leftOffset >= static_cast(leftVariables->size())) + leftOffset = leftOffsetStart; + subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]); + } else { + // offset will cycle through variables for arrayed io + if (rightOffset >= static_cast(rightVariables->size())) + rightOffset = rightOffsetStart; + subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]); + } + + // arrayed io + if (subTree->getType().isArray()) { + if (!arrayElement.empty()) { + const TType derefType(subTree->getType(), arrayElement.front()); + subTree = intermediate.addIndex(EOpIndexDirect, subTree, + intermediate.addConstantUnion(arrayElement.front(), loc), loc); + subTree->setType(derefType); + } else { + // There's an index operation we should transfer to the output builtin. + assert(splitNode->getAsOperator() != nullptr && + splitNode->getAsOperator()->getOp() == EOpIndexIndirect); + const TType splitDerefType(subTree->getType(), 0); + subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree, + splitNode->getAsBinaryNode()->getRight(), loc); + subTree->setType(splitDerefType); + } + } + } else { + // Index operator if it's an aggregate, else EOpNull + const TOperator accessOp = type.isArray() ? EOpIndexDirect + : type.isStruct() ? EOpIndexDirectStruct + : EOpNull; + if (accessOp == EOpNull) { + subTree = splitNode; + } else { + subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc), + loc); + const TType splitDerefType(splitNode->getType(), splitMember); + subTree->setType(splitDerefType); + } + } + + return subTree; + }; + + // Use the proper RHS node: a new symbol from a TVariable, copy + // of an TIntermSymbol node, or sometimes the right node directly. + right = rhsTempVar != nullptr ? intermediate.addSymbol(*rhsTempVar, loc) : + cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) : + right; + + // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the + // whole thing. So, we'll resort to an explicit type via std::function. + const std::function + traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight, + bool topLevel) -> void { + // If we get here, we are assigning to or from a whole array or struct that must be + // flattened, so have to do member-by-member assignment: + + bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel); + bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel); + + if ((left->getType().isArray() || right->getType().isArray()) && + (shouldFlattenSubsetLeft || isSplitLeft || + shouldFlattenSubsetRight || isSplitRight)) { + const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1; + const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1; + + // The arrays might not be the same size, + // e.g., if the size has been forced for EbvTessLevelInner/Outer. + const int elementsToCopy = std::min(elementsL, elementsR); + + // array case + for (int element = 0; element < elementsToCopy; ++element) { + arrayElement.push_back(element); + + // Add a new AST symbol node if we have a temp variable holding a complex RHS. + TIntermTyped* subLeft = getMember(true, left->getType(), element, left, element, + shouldFlattenSubsetLeft); + TIntermTyped* subRight = getMember(false, right->getType(), element, right, element, + shouldFlattenSubsetRight); + + TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), element, splitLeft, + element, shouldFlattenSubsetLeft) + : subLeft; + TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight, + element, shouldFlattenSubsetRight) + : subRight; + + traverse(subLeft, subRight, subSplitLeft, subSplitRight, false); + + arrayElement.pop_back(); + } + } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft || isSplitLeft || + shouldFlattenSubsetRight || isSplitRight)) { + // struct case + const auto& membersL = *left->getType().getStruct(); + const auto& membersR = *right->getType().getStruct(); + + // These track the members in the split structures corresponding to the same in the unsplit structures, + // which we traverse in parallel. + int memberL = 0; + int memberR = 0; + + // Handle empty structure assignment + if (int(membersL.size()) == 0 && int(membersR.size()) == 0) + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); + + for (int member = 0; member < int(membersL.size()); ++member) { + const TType& typeL = *membersL[member].type; + const TType& typeR = *membersR[member].type; + + TIntermTyped* subLeft = getMember(true, left->getType(), member, left, member, + shouldFlattenSubsetLeft); + TIntermTyped* subRight = getMember(false, right->getType(), member, right, member, + shouldFlattenSubsetRight); + + // If there is no splitting, use the same values to avoid inefficiency. + TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), member, splitLeft, + memberL, shouldFlattenSubsetLeft) + : subLeft; + TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight, + memberR, shouldFlattenSubsetRight) + : subRight; + + if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) { + // Clip and cull distance built-in assignment is complex in its own right, and is handled in + // a separate function dedicated to that task. See comment above assignClipCullDistance; + + const bool isOutput = isClipOrCullDistance(subSplitLeft->getType()); + + // Since all clip/cull semantics boil down to the same built-in type, we need to get the + // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping. + const TType derefType((isOutput ? left : right)->getType(), member); + const int semanticId = derefType.getQualifier().layoutLocation; + + TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId, + subSplitLeft, subSplitRight); + + assignList = intermediate.growAggregate(assignList, clipCullAssign, loc); + } else if (subSplitRight->getType().getQualifier().builtIn == EbvFragCoord) { + // FragCoord can require special handling: see comment above assignFromFragCoord + TIntermTyped* fragCoordAssign = assignFromFragCoord(loc, op, subSplitLeft, subSplitRight); + assignList = intermediate.growAggregate(assignList, fragCoordAssign, loc); + } else if (assignsClipPos(subSplitLeft)) { + // Position can require special handling: see comment above assignPosition + TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight); + assignList = intermediate.growAggregate(assignList, positionAssign, loc); + } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight && + !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) { + // If this is the final flattening (no nested types below to flatten) + // we'll copy the member, else recurse into the type hierarchy. + // However, if splitting the struct, that means we can copy a whole + // subtree here IFF it does not itself contain any interstage built-in + // IO variables, so we only have to recurse into it if there's something + // for splitting to do. That can save a lot of AST verbosity for + // a bunch of memberwise copies. + + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), + loc); + } else { + traverse(subLeft, subRight, subSplitLeft, subSplitRight, false); + } + + memberL += (typeL.isBuiltIn() ? 0 : 1); + memberR += (typeR.isBuiltIn() ? 0 : 1); + } + } else { + // Member copy + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); + } + + }; + + TIntermTyped* splitLeft = left; + TIntermTyped* splitRight = right; + + // If either left or right was a split structure, we must read or write it, but still have to + // parallel-recurse through the unsplit structure to identify the built-in IO vars. + // The left can be either a symbol, or an index into a symbol (e.g, array reference) + if (isSplitLeft) { + if (indexesSplit(left)) { + // Index case: Refer to the indexed symbol, if the left is an index operator. + const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + + TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc); + + splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo, + left->getAsBinaryNode()->getRight(), loc); + + const TType derefType(splitLeftNonIo->getType(), 0); + splitLeft->setType(derefType); + } else { + // Symbol case: otherwise, if not indexed, we have the symbol directly. + const TIntermSymbol* symNode = left->getAsSymbolNode(); + splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc); + } + } + + if (isSplitRight) + splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc); + + // This makes the whole assignment, recursing through subtypes as needed. + traverse(left, right, splitLeft, splitRight, true); + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// An assignment to matrix swizzle must be decomposed into individual assignments. +// These must be selected component-wise from the RHS and stored component-wise +// into the LHS. +TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, + TIntermTyped* right) +{ + assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle); + + if (op != EOpAssign) + error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", ""); + + // isolate the matrix and swizzle nodes + TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped(); + const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence(); + + // if the RHS isn't already a simple vector, let's store into one + TIntermSymbol* vector = right->getAsSymbolNode(); + TIntermTyped* vectorAssign = nullptr; + if (vector == nullptr) { + // create a new intermediate vector variable to assign to + TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2); + vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc); + + // assign the right to the new vector + vectorAssign = handleAssign(loc, op, vector, right); + } + + // Assign the vector components to the matrix components. + // Store this as a sequence, so a single aggregate node represents this + // entire operation. + TIntermAggregate* result = intermediate.makeAggregate(vectorAssign); + TType columnType(matrix->getType(), 0); + TType componentType(columnType, 0); + TType indexType(EbtInt); + for (int i = 0; i < (int)swizzle.size(); i += 2) { + // the right component, single index into the RHS vector + TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector, + intermediate.addConstantUnion(i/2, loc), loc); + + // the left component, double index into the LHS matrix + TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix, + intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(), + indexType, loc), + loc); + leftComp->setType(columnType); + leftComp = intermediate.addIndex(EOpIndexDirect, leftComp, + intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(), + indexType, loc), + loc); + leftComp->setType(componentType); + + // Add the assignment to the aggregate + result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc)); + } + + result->setOp(EOpSequence); + + return result; +} + +// +// HLSL atomic operations have slightly different arguments than +// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic. +// This provides the post-decomposition equivalent opcode. +// +TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage) +{ + switch (op) { + case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd; + case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd; + case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap; + case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax; + case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin; + case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr; + case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor; + case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange; + case EOpInterlockedCompareStore: // TODO: ... + default: + error(loc, "unknown atomic operation", "unknown op", ""); + return EOpNull; + } +} + +// +// Create a combined sampler/texture from separate sampler and texture. +// +TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, + TIntermTyped* argSampler) +{ + TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler); + + txcombine->getSequence().push_back(argTex); + txcombine->getSequence().push_back(argSampler); + + TSampler samplerType = argTex->getType().getSampler(); + samplerType.combined = true; + + // TODO: + // This block exists until the spec no longer requires shadow modes on texture objects. + // It can be deleted after that, along with the shadowTextureVariant member. + { + const bool shadowMode = argSampler->getType().getSampler().shadow; + + TIntermSymbol* texSymbol = argTex->getAsSymbolNode(); + + if (texSymbol == nullptr) + texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + + if (texSymbol == nullptr) { + error(loc, "unable to find texture symbol", "", ""); + return nullptr; + } + + // This forces the texture's shadow state to be the sampler's + // shadow state. This depends on downstream optimization to + // DCE one variant in [shadow, nonshadow] if both are present, + // or the SPIR-V module would be invalid. + long long newId = texSymbol->getId(); + + // Check to see if this texture has been given a shadow mode already. + // If so, look up the one we already have. + const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId()); + + if (textureShadowEntry != textureShadowVariant.end()) + newId = textureShadowEntry->second->get(shadowMode); + else + textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1); + + // Sometimes we have to create another symbol (if this texture has been seen before, + // and we haven't created the form for this shadow mode). + if (newId == -1) { + TType texType; + texType.shallowCopy(argTex->getType()); + texType.getSampler().shadow = shadowMode; // set appropriate shadow mode. + globalQualifierFix(loc, texType.getQualifier()); + + TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType); + + trackLinkage(*newTexture); + + newId = newTexture->getUniqueId(); + } + + assert(newId != -1); + + if (textureShadowVariant.find(newId) == textureShadowVariant.end()) + textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()]; + + textureShadowVariant[newId]->set(shadowMode, newId); + + // Remember this shadow mode in the texture and the merged type. + argTex->getWritableType().getSampler().shadow = shadowMode; + samplerType.shadow = shadowMode; + + texSymbol->switchId(newId); + } + + txcombine->setType(TType(samplerType, EvqTemporary)); + txcombine->setLoc(loc); + + return txcombine; +} + +// Return true if this a buffer type that has an associated counter buffer. +bool HlslParseContext::hasStructBuffCounter(const TType& type) const +{ + switch (type.getQualifier().declaredBuiltIn) { + case EbvAppendConsume: // fall through... + case EbvRWStructuredBuffer: // ... + return true; + default: + return false; // the other structuredbuffer types do not have a counter. + } +} + +void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type) +{ + // Counter type + TType* counterType = new TType(EbtUint, EvqBuffer); + counterType->setFieldName(intermediate.implicitCounterName); + + TTypeList* blockStruct = new TTypeList; + TTypeLoc member = { counterType, loc }; + blockStruct->push_back(member); + + TType blockType(blockStruct, "", counterType->getQualifier()); + blockType.getQualifier().storage = EvqBuffer; + + type.shallowCopy(blockType); + shareStructBufferType(type); +} + +// declare counter for a structured buffer type +void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name) +{ + // Bail out if not a struct buffer + if (! isStructBufferType(bufferType)) + return; + + if (! hasStructBuffCounter(bufferType)) + return; + + TType blockType; + counterBufferType(loc, blockType); + + TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str()); + + // Counter buffer is not yet in use + structBufferCounter[*blockName] = false; + + shareStructBufferType(blockType); + declareBlock(loc, blockType, blockName); +} + +// return the counter that goes with a given structuredbuffer +TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer) +{ + // Bail out if not a struct buffer + if (buffer == nullptr || ! isStructBufferType(buffer->getType())) + return nullptr; + + const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName())); + + // Mark the counter as being used + structBufferCounter[counterBlockName] = true; + + TIntermTyped* counterVar = handleVariable(loc, &counterBlockName); // find the block structure + TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct + + TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc); + counterMember->setType(TType(EbtUint)); + return counterMember; +} + +// +// Decompose structure buffer methods into AST +// +void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr) + return; + + const TOperator op = node->getAsOperator()->getOp(); + TIntermAggregate* argAggregate = arguments->getAsAggregate(); + + // Buffer is the object upon which method is called, so always arg 0 + TIntermTyped* bufferObj = nullptr; + + // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params. + if (argAggregate) { + if (argAggregate->getSequence().empty()) + return; + if (argAggregate->getSequence()[0]) + bufferObj = argAggregate->getSequence()[0]->getAsTyped(); + } else { + bufferObj = arguments->getAsSymbolNode(); + } + + if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr) + return; + + // Some methods require a hidden internal counter, obtained via getStructBufferCounter(). + // This lambda adds something to it and returns the old value. + const auto incDecCounter = [&](int incval) -> TIntermTyped* { + TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast(incval), loc, true); + TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member + + if (counter == nullptr) + return nullptr; + + TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd); + counterIncrement->setType(TType(EbtUint, EvqTemporary)); + counterIncrement->setLoc(loc); + counterIncrement->getSequence().push_back(counter); + counterIncrement->getSequence().push_back(incrementValue); + + return counterIncrement; + }; + + // Index to obtain the runtime sized array out of the buffer. + TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj); + if (argArray == nullptr) + return; // It might not be a struct buffer method. + + switch (op) { + case EOpMethodLoad: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + + const TType& bufferType = bufferObj->getType(); + + const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn; + + // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address + // buffer then, but that's what it calls itself. + const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer || + builtInType == EbvRWByteAddressBuffer); + + + if (isByteAddressBuffer) + argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + // Index into the array to find the item being loaded. + const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + node = intermediate.addIndex(idxOp, argArray, argIndex, loc); + + const TType derefType(argArray->getType(), 0); + node->setType(derefType); + } + + break; + + case EOpMethodLoad2: + case EOpMethodLoad3: + case EOpMethodLoad4: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + + TOperator constructOp = EOpNull; + int size = 0; + + switch (op) { + case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break; + case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break; + case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break; + default: assert(0); + } + + TIntermTyped* body = nullptr; + + // First, we'll store the address in a variable to avoid multiple shifts + // (we must convert the byte address to an item address) + TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); + TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); + + body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); + + TIntermTyped* vec = nullptr; + + // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2 + // address conversion. + for (int idx=0; idxgetQualifier().storage == EvqConst) ? EOpIndexDirect + : EOpIndexIndirect; + + TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc); + + TType derefType(argArray->getType(), 0); + derefType.getQualifier().makeTemporary(); + indexVal->setType(derefType); + + vec = intermediate.growAggregate(vec, indexVal); + } + + vec->setType(TType(argArray->getBasicType(), EvqTemporary, size)); + vec->getAsAggregate()->setOperator(constructOp); + + body = intermediate.growAggregate(body, vec); + body->setType(vec->getType()); + body->getAsAggregate()->setOperator(EOpSequence); + + node = body; + } + + break; + + case EOpMethodStore: + case EOpMethodStore2: + case EOpMethodStore3: + case EOpMethodStore4: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value + + // Index into the array to find the item being loaded. + // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address + // buffer then, but that's what it calls itself). + + int size = 0; + + switch (op) { + case EOpMethodStore: size = 1; break; + case EOpMethodStore2: size = 2; break; + case EOpMethodStore3: size = 3; break; + case EOpMethodStore4: size = 4; break; + default: assert(0); + } + + TIntermAggregate* body = nullptr; + + // First, we'll store the address in a variable to avoid multiple shifts + // (we must convert the byte address to an item address) + TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt)); + + TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); + TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); + + body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); + + for (int idx=0; idxgetQualifier().storage == EvqConst) ? EOpIndexDirect + : EOpIndexIndirect; + + TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc); + const TType derefType(argArray->getType(), 0); + lValue->setType(derefType); + + TIntermTyped* rValue; + if (size == 1) { + rValue = argValue; + } else { + rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc); + const TType indexType(argValue->getType(), 0); + rValue->setType(indexType); + } + + TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc); + + body = intermediate.growAggregate(body, assign); + } + + body->setOperator(EOpSequence); + node = body; + } + + break; + + case EOpMethodGetDimensions: + { + const int numArgs = (int)argAggregate->getSequence().size(); + TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items + TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride + + TIntermAggregate* body = nullptr; + + // Length output: + if (argArray->getType().isSizedArray()) { + const int length = argArray->getType().getOuterArraySize(); + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, + intermediate.addConstantUnion(length, loc, true), loc); + body = intermediate.growAggregate(body, assign, loc); + } else { + TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray, + argNumItems->getType()); + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc); + body = intermediate.growAggregate(body, assign, loc); + } + + // Stride output: + if (argStride != nullptr) { + int size; + int stride; + intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking, + argArray->getType().getQualifier().layoutMatrix == ElmRowMajor); + + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride, + intermediate.addConstantUnion(stride, loc, true), loc); + + body = intermediate.growAggregate(body, assign); + } + + body->setOperator(EOpSequence); + node = body; + } + + break; + + case EOpInterlockedAdd: + case EOpInterlockedAnd: + case EOpInterlockedExchange: + case EOpInterlockedMax: + case EOpInterlockedMin: + case EOpInterlockedOr: + case EOpInterlockedXor: + case EOpInterlockedCompareExchange: + case EOpInterlockedCompareStore: + { + // We'll replace the first argument with the block dereference, and let + // downstream decomposition handle the rest. + + TIntermSequence& sequence = argAggregate->getSequence(); + + TIntermTyped* argIndex = makeIntegerIndex(sequence[1]->getAsTyped()); // index + argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc); + + const TType derefType(argArray->getType(), 0); + element->setType(derefType); + + // Replace the numeric byte offset parameter with array reference. + sequence[1] = element; + sequence.erase(sequence.begin(), sequence.begin()+1); + } + break; + + case EOpMethodIncrementCounter: + { + node = incDecCounter(1); + break; + } + + case EOpMethodDecrementCounter: + { + TIntermTyped* preIncValue = incDecCounter(-1); // result is original value + node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc, + preIncValue->getType()); + break; + } + + case EOpMethodAppend: + { + TIntermTyped* oldCounter = incDecCounter(1); + + TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc); + TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped(); + + const TType derefType(argArray->getType(), 0); + lValue->setType(derefType); + + node = intermediate.addAssign(EOpAssign, lValue, rValue, loc); + + break; + } + + case EOpMethodConsume: + { + TIntermTyped* oldCounter = incDecCounter(-1); + + TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter, + intermediate.addConstantUnion(-1, loc, true), loc, + oldCounter->getType()); + + node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc); + + const TType derefType(argArray->getType(), 0); + node->setType(derefType); + + break; + } + + default: + break; // most pass through unchanged + } +} + +// Create array of standard sample positions for given sample count. +// TODO: remove when a real method to query sample pos exists in SPIR-V. +TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count) +{ + struct tSamplePos { float x, y; }; + + static const tSamplePos pos1[] = { + { 0.0/16.0, 0.0/16.0 }, + }; + + // standard sample positions for 2, 4, 8, and 16 samples. + static const tSamplePos pos2[] = { + { 4.0/16.0, 4.0/16.0 }, {-4.0/16.0, -4.0/16.0 }, + }; + + static const tSamplePos pos4[] = { + {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0, 2.0/16.0 }, { 2.0/16.0, 6.0/16.0 }, + }; + + static const tSamplePos pos8[] = { + { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0, 3.0/16.0 }, { 5.0/16.0, 1.0/16.0 }, {-3.0/16.0, -5.0/16.0 }, + {-5.0/16.0, 5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0, 7.0/16.0 }, { 7.0/16.0, -7.0/16.0 }, + }; + + static const tSamplePos pos16[] = { + { 1.0/16.0, 1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0, 2.0/16.0 }, { 4.0/16.0, -1.0/16.0 }, + {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0, 5.0/16.0 }, { 5.0/16.0, 3.0/16.0 }, { 3.0/16.0, -5.0/16.0 }, + {-2.0/16.0, 6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0, 4.0/16.0 }, + {-8.0/16.0, 0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0, 7.0/16.0 }, {-7.0/16.0, -8.0/16.0 }, + }; + + const tSamplePos* sampleLoc = nullptr; + int numSamples = count; + + switch (count) { + case 2: sampleLoc = pos2; break; + case 4: sampleLoc = pos4; break; + case 8: sampleLoc = pos8; break; + case 16: sampleLoc = pos16; break; + default: + sampleLoc = pos1; + numSamples = 1; + } + + TConstUnionArray* values = new TConstUnionArray(numSamples*2); + + for (int pos=0; posaddInnerSize(numSamples); + retType.transferArraySizes(arraySizes); + } + + return new TIntermConstantUnion(*values, retType); +} + +// +// Decompose DX9 and DX10 sample intrinsics & object methods into AST +// +void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || !node->getAsOperator()) + return; + + // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it. + const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* { + result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize())); + + TIntermTyped* convertedResult = nullptr; + + TType retType; + getTextureReturnType(sampler, retType); + + if (retType.isStruct()) { + // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here) + TIntermAggregate* conversionAggregate = new TIntermAggregate; + convertedResult = conversionAggregate; + + // Convert vector output to return structure. We will need a temp symbol to copy the results to. + TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType); + + // We also need a temp symbol to hold the result of the texture. We don't want to re-fetch the + // sample each time we'll index into the result, so we'll copy to this, and index into the copy. + TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType()); + + // Initial copy from texture to our sample result shadow. + TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc), + result, loc); + + conversionAggregate->getSequence().push_back(shadowCopy); + + unsigned vec4Pos = 0; + + for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) { + const TType memberType(retType, m); // dereferenced type of the member we're about to assign. + + // Check for bad struct members. This should have been caught upstream. Complain, because + // wwe don't know what to do with it. This algorithm could be generalized to handle + // other things, e.g, sub-structures, but HLSL doesn't allow them. + if (!memberType.isVector() && !memberType.isScalar()) { + error(loc, "expected: scalar or vector type in texture structure", "", ""); + return nullptr; + } + + // Index into the struct variable to find the member to assign. + TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct, + intermediate.addSymbol(*structVar, loc), + intermediate.addConstantUnion(m, loc), loc); + + structMember->setType(memberType); + + // Assign each component of (possible) vector in struct member. + for (int component = 0; component < memberType.getVectorSize(); ++component) { + TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect, + intermediate.addSymbol(*sampleShadow, loc), + intermediate.addConstantUnion(vec4Pos++, loc), loc); + vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1)); + + TIntermTyped* memberAssign = nullptr; + + if (memberType.isVector()) { + // Vector member: we need to create an access chain to the vector component. + + TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember, + intermediate.addConstantUnion(component, loc), loc); + + memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc); + } else { + // Scalar member: we can assign to it directly. + memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc); + } + + + conversionAggregate->getSequence().push_back(memberAssign); + } + } + + // Add completed variable so the expression results in the whole struct value we just built. + conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc)); + + // Make it a sequence. + intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc); + } else { + // vector clamp the output if template vector type is smaller than sample result. + if (retType.getVectorSize() < node->getVectorSize()) { + // Too many components. Construct shorter vector from it. + const TOperator op = intermediate.mapTypeToConstructorOp(retType); + + convertedResult = constructBuiltIn(retType, op, result, loc, false); + } else { + // Enough components. Use directly. + convertedResult = result; + } + } + + convertedResult->setLoc(loc); + return convertedResult; + }; + + const TOperator op = node->getAsOperator()->getOp(); + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + + // Bail out if not a sampler method. + // Note though this is odd to do before checking the op, because the op + // could be something that takes the arguments, and the function in question + // takes the result of the op. So, this is not the final word. + if (arguments != nullptr) { + if (argAggregate == nullptr) { + if (arguments->getAsTyped()->getBasicType() != EbtSampler) + return; + } else { + if (argAggregate->getSequence().size() == 0 || + argAggregate->getSequence()[0] == nullptr || + argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler) + return; + } + } + + switch (op) { + // **** DX9 intrinsics: **** + case EOpTexture: + { + // Texture with ddx & ddy is really gradient form in HLSL + if (argAggregate->getSequence().size() == 4) + node->getAsAggregate()->setOperator(EOpTextureGrad); + + break; + } + case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different) + { + TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped(); // sampler + TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped(); // coord + + assert(argCoord->getVectorSize() == 4); + TIntermTyped *w = intermediate.addConstantUnion(3, loc, true); + TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc); + + TOperator constructOp = EOpNull; + const TSampler &sampler = argSamp->getType().getSampler(); + int coordSize = 0; + + switch (sampler.dim) + { + case Esd1D: constructOp = EOpConstructFloat; coordSize = 1; break; // 1D + case Esd2D: constructOp = EOpConstructVec2; coordSize = 2; break; // 2D + case Esd3D: constructOp = EOpConstructVec3; coordSize = 3; break; // 3D + case EsdCube: constructOp = EOpConstructVec3; coordSize = 3; break; // also 3D + default: + error(loc, "unhandled DX9 texture LoD dimension", "", ""); + break; + } + + TIntermAggregate *constructCoord = new TIntermAggregate(constructOp); + constructCoord->getSequence().push_back(argCoord); + constructCoord->setLoc(loc); + constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize)); + + TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod); + tex->getSequence().push_back(argSamp); // sampler + tex->getSequence().push_back(constructCoord); // coordinate + tex->getSequence().push_back(argLod); // lod + + node = convertReturn(tex, sampler); + + break; + } + + case EOpTextureBias: + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord + + // HLSL puts bias in W component of coordinate. We extract it and add it to + // the argument list, instead + TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); + TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); + + TOperator constructOp = EOpNull; + const TSampler& sampler = arg0->getType().getSampler(); + + switch (sampler.dim) { + case Esd1D: constructOp = EOpConstructFloat; break; // 1D + case Esd2D: constructOp = EOpConstructVec2; break; // 2D + case Esd3D: constructOp = EOpConstructVec3; break; // 3D + case EsdCube: constructOp = EOpConstructVec3; break; // also 3D + default: + error(loc, "unhandled DX9 texture bias dimension", "", ""); + break; + } + + TIntermAggregate* constructCoord = new TIntermAggregate(constructOp); + constructCoord->getSequence().push_back(arg1); + constructCoord->setLoc(loc); + + // The input vector should never be less than 2, since there's always a bias. + // The max is for safety, and should be a no-op. + constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0))); + + TIntermAggregate* tex = new TIntermAggregate(EOpTexture); + tex->getSequence().push_back(arg0); // sampler + tex->getSequence().push_back(constructCoord); // coordinate + tex->getSequence().push_back(bias); // bias + + node = convertReturn(tex, sampler); + + break; + } + + // **** DX10 methods: **** + case EOpMethodSample: // fall through + case EOpMethodSampleBias: // ... + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argBias = nullptr; + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + int nextArg = 3; + + if (op == EOpMethodSampleBias) // SampleBias has a bias arg + argBias = argAggregate->getSequence()[nextArg++]->getAsTyped(); + + TOperator textureOp = EOpTexture; + + if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form + textureOp = EOpTextureOffset; + argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped(); + } + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + if (argBias != nullptr) + txsample->getSequence().push_back(argBias); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodSampleGrad: // ... + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + TOperator textureOp = EOpTextureGrad; + + if (argAggregate->getSequence().size() == 6) { // last parameter is offset form + textureOp = EOpTextureGradOffset; + argOffset = argAggregate->getSequence()[5]->getAsTyped(); + } + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + txsample->getSequence().push_back(argDDX); + txsample->getSequence().push_back(argDDY); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodGetDimensions: + { + // AST returns a vector of results, which we break apart component-wise into + // separate values to assign to the HLSL method's outputs, ala: + // tx . GetDimensions(width, height); + // float2 sizeQueryTemp = EOpTextureQuerySize + // width = sizeQueryTemp.X; + // height = sizeQueryTemp.Y; + + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + const TType& texType = argTex->getType(); + + assert(texType.getBasicType() == EbtSampler); + + const TSampler& sampler = texType.getSampler(); + const TSamplerDim dim = sampler.dim; + const bool isImage = sampler.isImage(); + const bool isMs = sampler.isMultiSample(); + const int numArgs = (int)argAggregate->getSequence().size(); + + int numDims = 0; + + switch (dim) { + case Esd1D: numDims = 1; break; // W + case Esd2D: numDims = 2; break; // W, H + case Esd3D: numDims = 3; break; // W, H, D + case EsdCube: numDims = 2; break; // W, H (cube) + case EsdBuffer: numDims = 1; break; // W (buffers) + case EsdRect: numDims = 2; break; // W, H (rect) + default: + error(loc, "unhandled DX10 MethodGet dimension", "", ""); + break; + } + + // Arrayed adds another dimension for the number of array elements + if (sampler.isArrayed()) + ++numDims; + + // Establish whether the method itself is querying mip levels. This can be false even + // if the underlying query requires a MIP level, due to the available HLSL method overloads. + const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0))); + + // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query). + // True if: + // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query) + // or, + // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec) + const bool mipRequired = + ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1... + mipQuery; // 2... + + // AST assumes integer return. Will be converted to float if required. + TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize); + sizeQuery->getSequence().push_back(argTex); + + // If we're building an LOD query, add the LOD. + if (mipRequired) { + // If the base HLSL query had no MIP level given, use level 0. + TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() : + intermediate.addConstantUnion(0, loc, true); + sizeQuery->getSequence().push_back(queryLod); + } + + sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims)); + sizeQuery->setLoc(loc); + + // Return value from size query + TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType()); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign, + intermediate.addSymbol(*tempArg, loc), + sizeQuery, loc); + + // Compound statement for assigning outputs + TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc); + // Index of first output parameter + const int outParamBase = mipQuery ? 2 : 1; + + for (int compNum = 0; compNum < numDims; ++compNum) { + TIntermTyped* indexedOut = nullptr; + TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc); + + if (numDims > 1) { + TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true); + indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc); + indexedOut->setType(TType(EbtUint, EvqTemporary, 1)); + indexedOut->setLoc(loc); + } else { + indexedOut = sizeQueryReturn; + } + + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped(); + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc); + + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + // handle mip level parameter + if (mipQuery) { + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); + + TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels); + levelsQuery->getSequence().push_back(argTex); + levelsQuery->setType(TType(EbtUint, EvqTemporary, 1)); + levelsQuery->setLoc(loc); + + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + // 2DMS formats query # samples, which needs a different query op + if (sampler.isMultiSample()) { + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); + + TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); + samplesQuery->getSequence().push_back(argTex); + samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); + samplesQuery->setLoc(loc); + + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtVoid)); + + node = compoundStatement; + + break; + } + + case EOpMethodSampleCmp: // fall through... + case EOpMethodSampleCmpLevelZero: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + + // Sampler argument should be a sampler. + if (argSamp->getType().getBasicType() != EbtSampler) { + error(loc, "expected: sampler type", "", ""); + return; + } + + // Sampler should be a SamplerComparisonState + if (! argSamp->getType().getSampler().isShadow()) { + error(loc, "expected: SamplerComparisonState", "", ""); + return; + } + + // optional offset value + if (argAggregate->getSequence().size() > 4) + argOffset = argAggregate->getSequence()[4]->getAsTyped(); + + const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp + + // AST wants comparison value as one of the texture coordinates + TOperator constructOp = EOpNull; + switch (coordDimWithCmpVal) { + // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val + case 2: constructOp = EOpConstructVec2; break; + case 3: constructOp = EOpConstructVec3; break; + case 4: constructOp = EOpConstructVec4; break; + case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg. + default: + error(loc, "unhandled DX10 MethodSample dimension", "", ""); + break; + } + + TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp); + coordWithCmp->getSequence().push_back(argCoord); + if (coordDimWithCmpVal != 5) // cube array shadow is special. + coordWithCmp->getSequence().push_back(argCmpVal); + coordWithCmp->setLoc(loc); + coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4))); + + TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture); + if (argOffset != nullptr) + textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset); + + // Create combined sampler & texture op + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(coordWithCmp); + + if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord. + txsample->getSequence().push_back(argCmpVal); + + // the LevelZero form uses 0 as an explicit LOD + if (op == EOpMethodSampleCmpLevelZero) + txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true)); + + // Add offset if present + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + txsample->setType(node->getType()); + txsample->setLoc(loc); + node = txsample; + + break; + } + + case EOpMethodLoad: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + TIntermTyped* lodComponent = nullptr; + TIntermTyped* coordSwizzle = nullptr; + + const TSampler& sampler = argTex->getType().getSampler(); + const bool isMS = sampler.isMultiSample(); + const bool isBuffer = sampler.dim == EsdBuffer; + const bool isImage = sampler.isImage(); + const TBasicType coordBaseType = argCoord->getType().getBasicType(); + + // Last component of coordinate is the mip level, for non-MS. we separate them here: + if (isMS || isBuffer || isImage) { + // MS, Buffer, and Image have no LOD + coordSwizzle = argCoord; + } else { + // Extract coordinate + int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1); + TSwizzleSelectors coordFields; + for (int i = 0; i < swizzleSize; ++i) + coordFields.push_back(i); + TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc); + coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc); + coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size())); + + // Extract LOD + TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true); + lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc); + lodComponent->setType(TType(coordBaseType, EvqTemporary, 1)); + } + + const int numArgs = (int)argAggregate->getSequence().size(); + const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4)); + + // Create texel fetch + const TOperator fetchOp = (isImage ? EOpImageLoad : + hasOffset ? EOpTextureFetchOffset : + EOpTextureFetch); + TIntermAggregate* txfetch = new TIntermAggregate(fetchOp); + + // Build up the fetch + txfetch->getSequence().push_back(argTex); + txfetch->getSequence().push_back(coordSwizzle); + + if (isMS) { + // add 2DMS sample index + TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped(); + txfetch->getSequence().push_back(argSampleIdx); + } else if (isBuffer) { + // Nothing else to do for buffers. + } else if (isImage) { + // Nothing else to do for images. + } else { + // 2DMS and buffer have no LOD, but everything else does. + txfetch->getSequence().push_back(lodComponent); + } + + // Obtain offset arg, if there is one. + if (hasOffset) { + const int offsetPos = (isMS ? 3 : 2); + argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped(); + txfetch->getSequence().push_back(argOffset); + } + + node = convertReturn(txfetch, sampler); + + break; + } + + case EOpMethodSampleLevel: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + const int numArgs = (int)argAggregate->getSequence().size(); + + if (numArgs == 5) // offset, if present + argOffset = argAggregate->getSequence()[4]->getAsTyped(); + + const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset); + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + txsample->getSequence().push_back(argLod); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodGather: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + + // Offset is optional + if (argAggregate->getSequence().size() > 3) + argOffset = argAggregate->getSequence()[3]->getAsTyped(); + + const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset); + TIntermAggregate* txgather = new TIntermAggregate(textureOp); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + txgather->getSequence().push_back(txcombine); + txgather->getSequence().push_back(argCoord); + // Offset if not given is implicitly channel 0 (red) + + if (argOffset != nullptr) + txgather->getSequence().push_back(argOffset); + + txgather->setType(node->getType()); + txgather->setLoc(loc); + node = txgather; + + break; + } + + case EOpMethodGatherRed: // fall through... + case EOpMethodGatherGreen: // ... + case EOpMethodGatherBlue: // ... + case EOpMethodGatherAlpha: // ... + case EOpMethodGatherCmpRed: // ... + case EOpMethodGatherCmpGreen: // ... + case EOpMethodGatherCmpBlue: // ... + case EOpMethodGatherCmpAlpha: // ... + { + int channel = 0; // the channel we are gathering + int cmpValues = 0; // 1 if there is a compare value (handier than a bool below) + + switch (op) { + case EOpMethodGatherCmpRed: cmpValues = 1; [[fallthrough]]; + case EOpMethodGatherRed: channel = 0; break; + case EOpMethodGatherCmpGreen: cmpValues = 1; [[fallthrough]]; + case EOpMethodGatherGreen: channel = 1; break; + case EOpMethodGatherCmpBlue: cmpValues = 1; [[fallthrough]]; + case EOpMethodGatherBlue: channel = 2; break; + case EOpMethodGatherCmpAlpha: cmpValues = 1; [[fallthrough]]; + case EOpMethodGatherAlpha: channel = 3; break; + default: assert(0); break; + } + + // For now, we have nothing to map the component-wise comparison forms + // to, because neither GLSL nor SPIR-V has such an opcode. Issue an + // unimplemented error instead. Most of the machinery is here if that + // should ever become available. However, red can be passed through + // to OpImageDrefGather. G/B/A cannot, because that opcode does not + // accept a component. + if (cmpValues != 0 && op != EOpMethodGatherCmpRed) { + error(loc, "unimplemented: component-level gather compare", "", ""); + return; + } + + int arg = 0; + + TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr }; + // TIntermTyped* argStatus = nullptr; // TODO: residency + TIntermTyped* argCmp = nullptr; + + const TSamplerDim dim = argTex->getType().getSampler().dim; + + const int argSize = (int)argAggregate->getSequence().size(); + bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues)); + bool hasOffset1 = false; + bool hasOffset4 = false; + + // Sampler argument should be a sampler. + if (argSamp->getType().getBasicType() != EbtSampler) { + error(loc, "expected: sampler type", "", ""); + return; + } + + // Cmp forms require SamplerComparisonState + if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) { + error(loc, "expected: SamplerComparisonState", "", ""); + return; + } + + // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets. + if (dim == Esd2D) { + hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues)); + hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues)); + } + + assert(!(hasOffset1 && hasOffset4)); + + TOperator textureOp = EOpTextureGather; + + // Compare forms have compare value + if (cmpValues != 0) + argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); + + // Some forms have single offset + if (hasOffset1) { + textureOp = EOpTextureGatherOffset; // single offset form + argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); + } + + // Some forms have 4 gather offsets + if (hasOffset4) { + textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form + for (int offsetNum = 0; offsetNum < 4; ++offsetNum) + argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped(); + } + + // Residency status + if (hasStatus) { + // argStatus = argAggregate->getSequence()[arg++]->getAsTyped(); + error(loc, "unimplemented: residency status", "", ""); + return; + } + + TIntermAggregate* txgather = new TIntermAggregate(textureOp); + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true); + + txgather->getSequence().push_back(txcombine); + txgather->getSequence().push_back(argCoord); + + // AST wants an array of 4 offsets, where HLSL has separate args. Here + // we construct an array from the separate args. + if (hasOffset4) { + TType arrayType(EbtInt, EvqTemporary, 2); + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(4); + arrayType.transferArraySizes(arraySizes); + + TIntermAggregate* initList = new TIntermAggregate(EOpNull); + + for (int offsetNum = 0; offsetNum < 4; ++offsetNum) + initList->getSequence().push_back(argOffsets[offsetNum]); + + argOffset = addConstructor(loc, initList, arrayType); + } + + // Add comparison value if we have one + if (argCmp != nullptr) + txgather->getSequence().push_back(argCmp); + + // Add offset (either 1, or an array of 4) if we have one + if (argOffset != nullptr) + txgather->getSequence().push_back(argOffset); + + // Add channel value if the sampler is not shadow + if (! argSamp->getType().getSampler().isShadow()) + txgather->getSequence().push_back(argChannel); + + txgather->setType(node->getType()); + txgather->setLoc(loc); + node = txgather; + + break; + } + + case EOpMethodCalculateLevelOfDetail: + case EOpMethodCalculateLevelOfDetailUnclamped: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + txquerylod->getSequence().push_back(txcombine); + txquerylod->getSequence().push_back(argCoord); + + TIntermTyped* lodComponent = intermediate.addConstantUnion( + op == EOpMethodCalculateLevelOfDetail ? 0 : 1, + loc, true); + TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc); + lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1)); + node = lodComponentIdx; + + break; + } + + case EOpMethodGetSamplePosition: + { + // TODO: this entire decomposition exists because there is not yet a way to query + // the sample position directly through SPIR-V. Instead, we return fixed sample + // positions for common cases. *** If the sample positions are set differently, + // this will be wrong. *** + + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped(); + + TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); + samplesQuery->getSequence().push_back(argTex); + samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); + samplesQuery->setLoc(loc); + + TIntermAggregate* compoundStatement = nullptr; + + TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint)); + outSampleCount->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc), + samplesQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + + TIntermTyped* idxtest[4]; + + // Create tests against 2, 4, 8, and 16 sample values + int count = 0; + for (int val = 2; val <= 16; val *= 2) + idxtest[count++] = + intermediate.addBinaryNode(EOpEqual, + intermediate.addSymbol(*outSampleCount, loc), + intermediate.addConstantUnion(val, loc), + loc, TType(EbtBool)); + + const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + // Create index ops into position arrays given sample index. + // TODO: should it be clamped? + TIntermTyped* index[4]; + count = 0; + for (int val = 2; val <= 16; val *= 2) { + index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc); + index[count++]->setType(TType(EbtFloat, EvqTemporary, 2)); + } + + // Create expression as: + // (sampleCount == 2) ? pos2[idx] : + // (sampleCount == 4) ? pos4[idx] : + // (sampleCount == 8) ? pos8[idx] : + // (sampleCount == 16) ? pos16[idx] : float2(0,0); + TIntermTyped* test = + intermediate.addSelection(idxtest[0], index[0], + intermediate.addSelection(idxtest[1], index[1], + intermediate.addSelection(idxtest[2], index[2], + intermediate.addSelection(idxtest[3], index[3], + getSamplePosArray(1), loc), loc), loc), loc); + + compoundStatement = intermediate.growAggregate(compoundStatement, test); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2)); + + node = compoundStatement; + + break; + } + + case EOpSubpassLoad: + { + const TIntermTyped* argSubpass = + argAggregate ? argAggregate->getSequence()[0]->getAsTyped() : + arguments->getAsTyped(); + + const TSampler& sampler = argSubpass->getType().getSampler(); + + // subpass load: the multisample form is overloaded. Here, we convert that to + // the EOpSubpassLoadMS opcode. + if (argAggregate != nullptr && argAggregate->getSequence().size() > 1) + node->getAsOperator()->setOp(EOpSubpassLoadMS); + + node = convertReturn(node, sampler); + + break; + } + + + default: + break; // most pass through unchanged + } +} + +// +// Decompose geometry shader methods +// +void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || !node->getAsOperator()) + return; + + const TOperator op = node->getAsOperator()->getOp(); + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + + switch (op) { + case EOpMethodAppend: + if (argAggregate) { + // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. + if (language != EShLangGeometry) { + node = nullptr; + return; + } + + TIntermAggregate* sequence = nullptr; + TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex); + + emit->setLoc(loc); + emit->setType(TType(EbtVoid)); + + TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped(); + + // This will be patched in finalization during finalizeAppendMethods() + sequence = intermediate.growAggregate(sequence, data, loc); + sequence = intermediate.growAggregate(sequence, emit); + + sequence->setOperator(EOpSequence); + sequence->setLoc(loc); + sequence->setType(TType(EbtVoid)); + + gsAppends.push_back({sequence, loc}); + + node = sequence; + } + break; + + case EOpMethodRestartStrip: + { + // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. + if (language != EShLangGeometry) { + node = nullptr; + return; + } + + TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive); + cut->setLoc(loc); + cut->setType(TType(EbtVoid)); + node = cut; + } + break; + + default: + break; // most pass through unchanged + } +} + +// +// Optionally decompose intrinsics to AST opcodes. +// +void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + // Helper to find image data for image atomics: + // OpImageLoad(image[idx]) + // We take the image load apart and add its params to the atomic op aggregate node + const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) { + TIntermAggregate* loadOp = load->getAsAggregate(); + if (loadOp == nullptr) { + error(loc, "unknown image type in atomic operation", "", ""); + node = nullptr; + return; + } + + atomic->getSequence().push_back(loadOp->getSequence()[0]); + atomic->getSequence().push_back(loadOp->getSequence()[1]); + }; + + // Return true if this is an imageLoad, which we will change to an image atomic. + const auto isImageParam = [](TIntermTyped* image) -> bool { + TIntermAggregate* imageAggregate = image->getAsAggregate(); + return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad; + }; + + const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* { + TSymbol* symbol = symbolTable.find(name); + if (nullptr == symbol) { + type.getQualifier().builtIn = builtin; + + TVariable* variable = new TVariable(NewPoolTString(name), type); + + symbolTable.insert(*variable); + + symbol = symbolTable.find(name); + assert(symbol && "Inserted symbol could not be found!"); + } + + return intermediate.addSymbol(*(symbol->getAsVariable()), loc); + }; + + // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST + // opcodes for compatibility with existing software stacks. + static const bool decomposeHlslIntrinsics = true; + + if (!decomposeHlslIntrinsics || !node || !node->getAsOperator()) + return; + + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + TIntermUnary* fnUnary = node->getAsUnaryNode(); + const TOperator op = node->getAsOperator()->getOp(); + + switch (op) { + case EOpGenMul: + { + // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul + // Since we are treating HLSL rows like GLSL columns (the first matrix indirection), + // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc. + TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped(); + + if (arg0->isVector() && arg1->isVector()) { // vec * vec + node->getAsAggregate()->setOperator(EOpDot); + } else { + node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1); + } + + break; + } + + case EOpRcp: + { + // rcp(a) -> 1 / a + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true); + node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0); + + break; + } + + case EOpAny: // fall through + case EOpAll: + { + TIntermTyped* typedArg = arguments->getAsTyped(); + + // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool. + // We'll convert here. Note that for efficiency, we could add a smarter + // decomposition for some type cases, e.g, maybe by decomposing a dot product. + if (typedArg->getType().getBasicType() != EbtBool) { + const TType boolType(EbtBool, EvqTemporary, + typedArg->getVectorSize(), + typedArg->getMatrixCols(), + typedArg->getMatrixRows(), + typedArg->isVector()); + + typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg); + node->getAsUnaryNode()->setOperand(typedArg); + } + + break; + } + + case EOpSaturate: + { + // saturate(a) -> clamp(a,0,1) + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermAggregate* clamp = new TIntermAggregate(EOpClamp); + + clamp->getSequence().push_back(arg0); + clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true)); + clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true)); + clamp->setLoc(loc); + clamp->setType(node->getType()); + clamp->getWritableType().getQualifier().makeTemporary(); + node = clamp; + + break; + } + + case EOpSinCos: + { + // sincos(a,b,c) -> b = sin(a), c = cos(a) + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0); + TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0); + TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc); + TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc); + + TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtVoid)); + + node = compoundStatement; + + break; + } + + case EOpClip: + { + // clip(a) -> if (any(a<0)) discard; + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermTyped* compareNode = nullptr; + + // For non-scalars: per experiment with FXC compiler, discard if any component < 0. + if (!arg0->isScalar()) { + // component-wise compare: a < 0 + TIntermAggregate* less = new TIntermAggregate(EOpLessThan); + less->getSequence().push_back(arg0); + less->setLoc(loc); + + // make vec or mat of bool matching dimensions of input + less->setType(TType(EbtBool, EvqTemporary, + arg0->getType().getVectorSize(), + arg0->getType().getMatrixCols(), + arg0->getType().getMatrixRows(), + arg0->getType().isVector())); + + // calculate # of components for comparison const + const int constComponentCount = + std::max(arg0->getType().getVectorSize(), 1) * + std::max(arg0->getType().getMatrixCols(), 1) * + std::max(arg0->getType().getMatrixRows(), 1); + + TConstUnion zero; + if (arg0->getType().isIntegerDomain()) + zero.setDConst(0); + else + zero.setDConst(0.0); + TConstUnionArray zeros(constComponentCount, zero); + + less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true)); + + compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool)); + } else { + TIntermTyped* zero; + if (arg0->getType().isIntegerDomain()) + zero = intermediate.addConstantUnion(0, loc, true); + else + zero = intermediate.addConstantUnion(0.0, type0, loc, true); + compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero); + } + + TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc); + + node = new TIntermSelection(compareNode, killNode, nullptr); + node->setLoc(loc); + + break; + } + + case EOpLog10: + { + // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10)) + TIntermTyped* arg0 = fnUnary->getOperand(); + TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0); + TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true); + + node = handleBinaryMath(loc, "mul", EOpMul, log2, base); + + break; + } + + case EOpDst: + { + // dest.x = 1; + // dest.y = src0.y * src1.y; + // dest.z = src0.z; + // dest.w = src1.w; + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + TIntermTyped* y = intermediate.addConstantUnion(1, loc, true); + TIntermTyped* z = intermediate.addConstantUnion(2, loc, true); + TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); + + TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc); + TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc); + TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc); + TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); + + TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); + + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y)); + dst->getSequence().push_back(src0z); + dst->getSequence().push_back(src1w); + dst->setType(TType(EbtFloat, EvqTemporary, 4)); + dst->setLoc(loc); + node = dst; + + break; + } + + case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value + case EOpInterlockedMin: // ... + case EOpInterlockedMax: // ... + case EOpInterlockedAnd: // ... + case EOpInterlockedOr: // ... + case EOpInterlockedXor: // ... + case EOpInterlockedExchange: // always has output arg + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value + TIntermTyped* arg2 = nullptr; + + if (argAggregate->getSequence().size() > 2) + arg2 = argAggregate->getSequence()[2]->getAsTyped(); + + const bool isImage = isImageParam(arg0); + const TOperator atomicOp = mapAtomicOp(loc, op, isImage); + TIntermAggregate* atomic = new TIntermAggregate(atomicOp); + atomic->setType(arg0->getType()); + atomic->getWritableType().getQualifier().makeTemporary(); + atomic->setLoc(loc); + + if (isImage) { + // orig_value = imageAtomicOp(image, loc, data) + imageAtomicParams(atomic, arg0); + atomic->getSequence().push_back(arg1); + + if (argAggregate->getSequence().size() > 2) { + node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); + } else { + node = atomic; // no assignment needed, as there was no out var. + } + } else { + // Normal memory variable: + // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value + if (argAggregate->getSequence().size() > 2) { + // optional output param is present. return value goes to arg2. + atomic->getSequence().push_back(arg0); + atomic->getSequence().push_back(arg1); + + node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); + } else { + // Set the matching operator. Since output is absent, this is all we need to do. + node->getAsAggregate()->setOperator(atomicOp); + node->setType(atomic->getType()); + } + } + + break; + } + + case EOpInterlockedCompareExchange: + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp + TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value + TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig + + const bool isImage = isImageParam(arg0); + TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage)); + atomic->setLoc(loc); + atomic->setType(arg2->getType()); + atomic->getWritableType().getQualifier().makeTemporary(); + + if (isImage) { + imageAtomicParams(atomic, arg0); + } else { + atomic->getSequence().push_back(arg0); + } + + atomic->getSequence().push_back(arg1); + atomic->getSequence().push_back(arg2); + node = intermediate.addAssign(EOpAssign, arg3, atomic, loc); + + break; + } + + case EOpEvaluateAttributeSnapped: + { + // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels + // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y: + // iU = (iU<<28)>>28 + // fU = ((float)iU)/16 + // Targets might handle this natively, in which case they can disable + // decompositions. + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset + + TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true); + TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift, + handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28), + i28); + + TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true); + TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul, + intermediate.addConversion(EOpConstructFloat, + TType(EbtFloat, EvqTemporary, 2), iU), + recip16); + + TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset); + interp->getSequence().push_back(arg0); + interp->getSequence().push_back(floatOffset); + interp->setLoc(loc); + interp->setType(arg0->getType()); + interp->getWritableType().getQualifier().makeTemporary(); + + node = interp; + + break; + } + + case EOpLit: + { + TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); + + // Ambient + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + + // Diffuse: + TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); + TIntermAggregate* diffuse = new TIntermAggregate(EOpMax); + diffuse->getSequence().push_back(n_dot_l); + diffuse->getSequence().push_back(zero); + diffuse->setLoc(loc); + diffuse->setType(TType(EbtFloat)); + dst->getSequence().push_back(diffuse); + + // Specular: + TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin); + min_ndot->getSequence().push_back(n_dot_l); + min_ndot->getSequence().push_back(n_dot_h); + min_ndot->setLoc(loc); + min_ndot->setType(TType(EbtFloat)); + + TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero); + TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m + + dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc)); + + // One: + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + + dst->setLoc(loc); + dst->setType(TType(EbtFloat, EvqTemporary, 4)); + node = dst; + break; + } + + case EOpAsDouble: + { + // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must + // first construct a uint64. + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + if (arg0->getType().isVector()) { // TODO: ... + error(loc, "double2 conversion not implemented", "asdouble", ""); + break; + } + + TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2); + + uint64->getSequence().push_back(arg0); + uint64->getSequence().push_back(arg1); + uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2 + uint64->setLoc(loc); + + // bitcast uint2 to a double + TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble); + convert->getAsUnaryNode()->setOperand(uint64); + convert->setLoc(loc); + convert->setType(TType(EbtDouble, EvqTemporary)); + node = convert; + + break; + } + + case EOpF16tof32: + { + // input uvecN with low 16 bits of each component holding a float16. convert to float32. + TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); + TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); + const int vecSize = argValue->getType().getVectorSize(); + + TOperator constructOp = EOpNull; + switch (vecSize) { + case 1: constructOp = EOpNull; break; // direct use, no construct needed + case 2: constructOp = EOpConstructVec2; break; + case 3: constructOp = EOpConstructVec3; break; + case 4: constructOp = EOpConstructVec4; break; + default: assert(0); break; + } + + // For scalar case, we don't need to construct another type. + TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; + + if (result) { + result->setType(TType(EbtFloat, EvqTemporary, vecSize)); + result->setLoc(loc); + } + + for (int idx = 0; idx < vecSize; ++idx) { + TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); + TIntermTyped* component = argValue->getType().isVector() ? + intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; + + if (component != argValue) + component->setType(TType(argValue->getBasicType(), EvqTemporary)); + + TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16); + unpackOp->setType(TType(EbtFloat, EvqTemporary, 2)); + unpackOp->getAsUnaryNode()->setOperand(component); + unpackOp->setLoc(loc); + + TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc); + + if (result != nullptr) { + result->getSequence().push_back(lowOrder); + node = result; + } else { + node = lowOrder; + } + } + + break; + } + + case EOpF32tof16: + { + // input floatN converted to 16 bit float in low order bits of each component of uintN + TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); + + TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); + const int vecSize = argValue->getType().getVectorSize(); + + TOperator constructOp = EOpNull; + switch (vecSize) { + case 1: constructOp = EOpNull; break; // direct use, no construct needed + case 2: constructOp = EOpConstructUVec2; break; + case 3: constructOp = EOpConstructUVec3; break; + case 4: constructOp = EOpConstructUVec4; break; + default: assert(0); break; + } + + // For scalar case, we don't need to construct another type. + TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; + + if (result) { + result->setType(TType(EbtUint, EvqTemporary, vecSize)); + result->setLoc(loc); + } + + for (int idx = 0; idx < vecSize; ++idx) { + TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); + TIntermTyped* component = argValue->getType().isVector() ? + intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; + + if (component != argValue) + component->setType(TType(argValue->getBasicType(), EvqTemporary)); + + TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2); + vec2ComponentAndZero->getSequence().push_back(component); + vec2ComponentAndZero->getSequence().push_back(zero); + vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2)); + vec2ComponentAndZero->setLoc(loc); + + TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16); + packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero); + packOp->setLoc(loc); + packOp->setType(TType(EbtUint, EvqTemporary)); + + if (result != nullptr) { + result->getSequence().push_back(packOp); + node = result; + } else { + node = packOp; + } + } + + break; + } + + case EOpD3DCOLORtoUBYTE4: + { + // ivec4 ( x.zyxw * 255.001953 ); + TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); + TSwizzleSelectors selectors; + selectors.push_back(2); + selectors.push_back(1); + selectors.push_back(0); + selectors.push_back(3); + TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc); + TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc); + swizzled->setType(arg0->getType()); + swizzled->getWritableType().getQualifier().makeTemporary(); + + TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true); + TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled); + rangeConverted->setType(arg0->getType()); + rangeConverted->getWritableType().getQualifier().makeTemporary(); + + node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted); + node->setLoc(loc); + node->setType(TType(EbtInt, EvqTemporary, 4)); + break; + } + + case EOpIsFinite: + { + // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate + // it to !isnan && !isinf + + TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); + + // We'll make a temporary in case the RHS is cmoplex + TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType()); + tempArg->getWritableType().getQualifier().makeTemporary(); + + TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign, + intermediate.addSymbol(*tempArg, loc), + arg0, loc); + + TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc); + + const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(), + arg0->getMatrixRows()); + + TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc)); + isnan->setType(boolType); + + TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan); + notnan->setType(boolType); + + TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc)); + isinf->setType(boolType); + + TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf); + notinf->setType(boolType); + + TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf); + andNode->setType(boolType); + + compoundStatement = intermediate.growAggregate(compoundStatement, andNode); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(boolType); + + node = compoundStatement; + + break; + } + case EOpWaveGetLaneCount: + { + // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol + // so that it inhabits the symbol table, but has a user-invalid name + // in-case some source HLSL defined the symbol also). + TType type(EbtUint, EvqVaryingIn); + node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type); + break; + } + case EOpWaveGetLaneIndex: + { + // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the + // symbol so that it inhabits the symbol table, but has a + // user-invalid name in-case some source HLSL defined the symbol + // also). + TType type(EbtUint, EvqVaryingIn); + node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type); + break; + } + case EOpWaveActiveCountBits: + { + // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin + + // uvec4 type. + TType uvec4Type(EbtUint, EvqTemporary, 4); + + // Get the uvec4 return from subgroupBallot(). + TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallot, true, arguments, uvec4Type); + + // uint type. + TType uintType(EbtUint, EvqTemporary); + + node = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallotBitCount, true, res, uintType); + + break; + } + case EOpWavePrefixCountBits: + { + // Mapped to subgroupBallotExclusiveBitCount(subgroupBallot()) + // builtin + + // uvec4 type. + TType uvec4Type(EbtUint, EvqTemporary, 4); + + // Get the uvec4 return from subgroupBallot(). + TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallot, true, arguments, uvec4Type); + + // uint type. + TType uintType(EbtUint, EvqTemporary); + + node = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallotExclusiveBitCount, true, res, uintType); + + break; + } + + default: + break; // most pass through unchanged + } +} + +// +// Handle seeing function call syntax in the grammar, which could be any of +// - .length() method +// - constructor +// - a call to a built-in function mapped to an operator +// - a call to a built-in function that will remain a function call (e.g., texturing) +// - user function +// - subroutine call (not implemented yet) +// +TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments) +{ + TIntermTyped* result = nullptr; + + TOperator op = function->getBuiltInOp(); + if (op != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (! constructorError(loc, arguments, *function, op, type)) { + // + // It's a constructor, of type 'type'. + // + result = handleConstructor(loc, arguments, type); + if (result == nullptr) { + error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); + return nullptr; + } + } + } else { + // + // Find it in the symbol table. + // + const TFunction* fnCandidate = nullptr; + bool builtIn = false; + int thisDepth = 0; + + // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes, + // and clamp the opposite arg. Since that's complex, we farm it off to a separate method. + // It doesn't naturally fall out of processing an argument at a time in isolation. + if (function->getName() == "mul") + addGenMulArgumentConversion(loc, *function, arguments); + + TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr; + + // TODO: this needs improvement: there's no way at present to look up a signature in + // the symbol table for an arbitrary type. This is a temporary hack until that ability exists. + // It will have false positives, since it doesn't check arg counts or types. + if (arguments) { + // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we + // look for either case. + + TIntermTyped* arg0 = nullptr; + + if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0]) + arg0 = aggregate->getSequence()[0]->getAsTyped(); + else if (arguments->getAsSymbolNode()) + arg0 = arguments->getAsSymbolNode(); + + if (arg0 != nullptr && isStructBufferType(arg0->getType())) { + static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1; + + if (function->getName().length() > methodPrefixSize && + isStructBufferMethod(function->getName().substr(methodPrefixSize))) { + const TString mangle = function->getName() + "("; + TSymbol* symbol = symbolTable.find(mangle, &builtIn); + + if (symbol) + fnCandidate = symbol->getAsFunction(); + } + } + } + + if (fnCandidate == nullptr) + fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments); + + if (fnCandidate) { + // This is a declared function that might map to + // - a built-in operator, + // - a built-in function not mapped to an operator, or + // - a user function. + + // turn an implicit member-function resolution into an explicit call + TString callerName; + if (thisDepth == 0) + callerName = fnCandidate->getMangledName(); + else { + // get the explicit (full) name of the function + callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth]; + callerName += fnCandidate->getMangledName(); + // insert the implicit calling argument + pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments); + } + + // Convert 'in' arguments, so that types match. + // However, skip those that need expansion, that is covered next. + if (arguments) + addInputArgumentConversions(*fnCandidate, arguments); + + // Expand arguments. Some arguments must physically expand to a different set + // than what the shader declared and passes. + if (arguments && !builtIn) + expandArguments(loc, *fnCandidate, arguments); + + // Expansion may have changed the form of arguments + aggregate = arguments ? arguments->getAsAggregate() : nullptr; + + op = fnCandidate->getBuiltInOp(); + if (builtIn && op != EOpNull) { + // SM 4.0 and above guarantees roundEven semantics for round() + if (!hlslDX9Compatible() && op == EOpRound) + op = EOpRoundEven; + + // A function call mapped to a built-in operation. + result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, + fnCandidate->getType()); + if (result == nullptr) { + error(arguments->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(arguments)->getCompleteString().c_str()); + } else if (result->getAsOperator()) { + builtInOpCheck(loc, *fnCandidate, *result->getAsOperator()); + } + } else { + // This is a function call not mapped to built-in operator. + // It could still be a built-in function, but only if PureOperatorBuiltins == false. + result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(callerName); + + // this is how we know whether the given function is a built-in function or a user-defined function + // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also + // if builtIn == true, it's definitely a built-in function with EOpNull + if (! builtIn) { + call->setUserDefined(); + intermediate.addToCallGraph(infoSink, currentCaller, callerName); + } + } + + // for decompositions, since we want to operate on the function node, not the aggregate holding + // output conversions. + const TIntermTyped* fnNode = result; + + decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions + decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions + decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions + decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions + + // Create the qualifier list, carried in the AST for the call. + // Because some arguments expand to multiple arguments, the qualifier list will + // be longer than the formal parameter list. + if (result == fnNode && result->getAsAggregate()) { + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; + if (hasStructBuffCounter(*(*fnCandidate)[i].type)) { + // add buffer and counter buffer argument qualifier + qualifierList.push_back(qual); + qualifierList.push_back(qual); + } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage, + true)) { + // add structure member expansion + for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb) + qualifierList.push_back(qual); + } else { + // Normal 1:1 case + qualifierList.push_back(qual); + } + } + } + + // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. + // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. + // Also, build the qualifier list for user function calls, which are always called with an aggregate. + // We don't do this is if there has been a decomposition, which will have added its own conversions + // for output parameters. + if (result == fnNode && result->getAsAggregate()) + result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator()); + } + } + + // generic error recovery + // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to + // reduce cascades + if (result == nullptr) + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + + return result; +} + +// An initial argument list is difficult: it can be null, or a single node, +// or an aggregate if more than one argument. Add one to the front, maintaining +// this lack of uniformity. +void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments) +{ + if (arguments == nullptr) + arguments = front; + else if (arguments->getAsAggregate() != nullptr) + arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front); + else + arguments = intermediate.growAggregate(front, arguments); +} + +// +// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a +// situation not well suited to resolution in intrinsic selection, but we can do so here, since we +// can look at both arguments insert explicit shape changes if required. +// +void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args) +{ + TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr; + + if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) { + // It really ought to have two arguments. + error(loc, "expected: mul arguments", "", ""); + return; + } + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + if (arg0->isVector() && arg1->isVector()) { + // For: + // vec * vec: it's handled during intrinsic selection, so while we could do it here, + // we can also ignore it, which is easier. + } else if (arg0->isVector() && arg1->isMatrix()) { + // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col. + if (arg0->getVectorSize() < arg1->getMatrixCols()) { + // vec is smaller, so truncate larger mat dimension + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + 0, arg0->getVectorSize(), arg1->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } else if (arg0->getVectorSize() > arg1->getMatrixCols()) { + // vec is larger, so truncate vec to mat size + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + arg1->getMatrixCols()); + arg0 = addConstructor(loc, arg0, truncType); + } + } else if (arg0->isMatrix() && arg1->isVector()) { + // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col. + if (arg1->getVectorSize() < arg0->getMatrixRows()) { + // vec is smaller, so truncate larger mat dimension + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + 0, arg0->getMatrixCols(), arg1->getVectorSize()); + arg0 = addConstructor(loc, arg0, truncType); + } else if (arg1->getVectorSize() > arg0->getMatrixRows()) { + // vec is larger, so truncate vec to mat size + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + arg0->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } + } else if (arg0->isMatrix() && arg1->isMatrix()) { + // mat * mat: we clamp the smaller inner dimension to match the other matrix size. + // Remember, HLSL Mrc = GLSL/SPIRV Mcr. + if (arg0->getMatrixRows() > arg1->getMatrixCols()) { + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + 0, arg0->getMatrixCols(), arg1->getMatrixCols()); + arg0 = addConstructor(loc, arg0, truncType); + } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) { + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + 0, arg0->getMatrixRows(), arg1->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } + } else { + // It's something with scalars: we'll just leave it alone. Function selection will handle it + // downstream. + } + + // Warn if we altered one of the arguments + if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1]) + warn(loc, "mul() matrix size mismatch", "", ""); + + // Put arguments back. (They might be unchanged, in which case this is harmless). + argAggregate->getSequence()[0] = arg0; + argAggregate->getSequence()[1] = arg1; + + call[0].type = &arg0->getWritableType(); + call[1].type = &arg1->getWritableType(); +} + +// +// Add any needed implicit conversions for function-call arguments to input parameters. +// +void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments) +{ + TIntermAggregate* aggregate = arguments->getAsAggregate(); + + // Replace a single argument with a single argument. + const auto setArg = [&](int paramNum, TIntermTyped* arg) { + if (function.getParamCount() == 1) + arguments = arg; + else { + if (aggregate == nullptr) + arguments = arg; + else + aggregate->getSequence()[paramNum] = arg; + } + }; + + // Process each argument's conversion + for (int param = 0; param < function.getParamCount(); ++param) { + if (! function[param].type->getQualifier().isParamInput()) + continue; + + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 + ? arguments->getAsTyped() + : (aggregate ? + aggregate->getSequence()[param]->getAsTyped() : + arguments->getAsTyped()); + if (*function[param].type != arg->getType()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg); + if (convArg != nullptr) + convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg); + if (convArg != nullptr) + setArg(param, convArg); + else + error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param); + } else { + if (wasFlattened(arg)) { + // If both formal and calling arg are to be flattened, leave that to argument + // expansion, not conversion. + if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) { + // Will make a two-level subtree. + // The deepest will copy member-by-member to build the structure to pass. + // The level above that will be a two-operand EOpComma sequence that follows the copy by the + // object itself. + TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type); + internalAggregate->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(), + internalAggregate->getName(), + getLanguage(), + internalAggregate->getType()); + internalSymbolNode->setLoc(arg->getLoc()); + // This makes the deepest level, the member-wise copy + TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign, + internalSymbolNode, arg)->getAsAggregate(); + + // Now, pair that with the resulting aggregate. + assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc()); + assignAgg->setOperator(EOpComma); + assignAgg->setType(internalAggregate->getType()); + setArg(param, assignAgg); + } + } + } + } +} + +// +// Add any needed implicit expansion of calling arguments from what the shader listed to what's +// internally needed for the AST (given the constraints downstream). +// +void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments) +{ + TIntermAggregate* aggregate = arguments->getAsAggregate(); + int functionParamNumberOffset = 0; + + // Replace a single argument with a single argument. + const auto setArg = [&](int paramNum, TIntermTyped* arg) { + if (function.getParamCount() + functionParamNumberOffset == 1) + arguments = arg; + else { + if (aggregate == nullptr) + arguments = arg; + else + aggregate->getSequence()[paramNum] = arg; + } + }; + + // Replace a single argument with a list of arguments + const auto setArgList = [&](int paramNum, const TVector& args) { + if (args.size() == 1) + setArg(paramNum, args.front()); + else if (args.size() > 1) { + if (function.getParamCount() + functionParamNumberOffset == 1) { + arguments = intermediate.makeAggregate(args.front()); + std::for_each(args.begin() + 1, args.end(), + [&](TIntermTyped* arg) { + arguments = intermediate.growAggregate(arguments, arg); + }); + } else { + auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum); + aggregate->getSequence().insert(it, args.begin(), args.end()); + } + functionParamNumberOffset += (int)(args.size() - 1); + } + }; + + // Process each argument's conversion + for (int param = 0; param < function.getParamCount(); ++param) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 + ? arguments->getAsTyped() + : (aggregate ? + aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() : + arguments->getAsTyped()); + + if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) { + // Need to pass the structure members instead of the structure. + TVector memberArgs; + for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb) + memberArgs.push_back(flattenAccess(arg, memb)); + setArgList(param + functionParamNumberOffset, memberArgs); + } + } + + // TODO: if we need both hidden counter args (below) and struct expansion (above) + // the two algorithms need to be merged: Each assumes the list starts out 1:1 between + // parameters and arguments. + + // If any argument is a pass-by-reference struct buffer with an associated counter + // buffer, we have to add another hidden parameter for that counter. + if (aggregate) + addStructBuffArguments(loc, aggregate); +} + +// +// Add any needed implicit output conversions for function-call arguments. This +// can require a new tree topology, complicated further by whether the function +// has a return value. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode) +{ + assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr); + + const TSourceLoc& loc = intermNode.getLoc(); + + TIntermSequence argSequence; // temp sequence for unary node args + + if (intermNode.getAsUnaryNode()) + argSequence.push_back(intermNode.getAsUnaryNode()->getOperand()); + + TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence; + + const auto needsConversion = [&](int argNum) { + return function[argNum].type->getQualifier().isParamOutput() && + (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() || + shouldConvertLValue(arguments[argNum]) || + wasFlattened(arguments[argNum]->getAsTyped())); + }; + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (needsConversion(i)) { + outputConversions = true; + break; + } + } + + if (! outputConversions) + return &intermNode; + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = nullptr; + TVariable* tempRet = nullptr; + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (needsConversion(i)) { + // Out-qualified arguments needing conversion need to use the topology setup above. + // Do the " ...(tempArg, ...), arg = tempArg" bit from above. + + // Make a temporary for what the function expects the argument to look like. + TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc); + + // This makes the deepest level, the member-wise copy + TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(), + tempArgNode); + tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, loc); + } + } + + // Finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc); + } + + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc); + + return conversionTree; +} + +// +// Add any needed "hidden" counter buffer arguments for function calls. +// +// Modifies the 'aggregate' argument if needed. Otherwise, is no-op. +// +void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate) +{ + // See if there are any SB types with counters. + const bool hasStructBuffArg = + std::any_of(aggregate->getSequence().begin(), + aggregate->getSequence().end(), + [this](const TIntermNode* node) { + return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType()); + }); + + // Nothing to do, if we didn't find one. + if (! hasStructBuffArg) + return; + + TIntermSequence argsWithCounterBuffers; + + for (int param = 0; param < int(aggregate->getSequence().size()); ++param) { + argsWithCounterBuffers.push_back(aggregate->getSequence()[param]); + + if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) { + const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode(); + if (blockSym != nullptr) { + TType counterType; + counterBufferType(loc, counterType); + + const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName())); + + TVariable* variable = makeInternalVariable(counterBlockName, counterType); + + // Mark this buffer's counter block as being in use + structBufferCounter[counterBlockName] = true; + + TIntermSymbol* sym = intermediate.addSymbol(*variable, loc); + argsWithCounterBuffers.push_back(sym); + } + } + } + + // Swap with the temp list we've built up. + aggregate->getSequence().swap(argsWithCounterBuffers); +} + + +// +// Do additional checking of built-in function calls that is not caught +// by normal semantic checks on argument type, extension tagging, etc. +// +// Assumes there has been a semantically correct match to a built-in function prototype. +// +void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) +{ + // Set up convenience accessors to the argument(s). There is almost always + // multiple arguments for the cases below, but when there might be one, + // check the unaryArg first. + const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference + const TIntermTyped* unaryArg = nullptr; + const TIntermTyped* arg0 = nullptr; + if (callNode.getAsAggregate()) { + argp = &callNode.getAsAggregate()->getSequence(); + if (argp->size() > 0) + arg0 = (*argp)[0]->getAsTyped(); + } else { + assert(callNode.getAsUnaryNode()); + unaryArg = callNode.getAsUnaryNode()->getOperand(); + arg0 = unaryArg; + } + const TIntermSequence& aggArgs = argp ? *argp : TIntermSequence(); // only valid when unaryArg is nullptr + + switch (callNode.getOp()) { + case EOpTextureGather: + case EOpTextureGatherOffset: + case EOpTextureGatherOffsets: + { + // Figure out which variants are allowed by what extensions, + // and what arguments must be constant for which situations. + + TString featureString = fnCandidate.getName() + "(...)"; + const char* feature = featureString.c_str(); + int compArg = -1; // track which argument, if any, is the constant component argument + switch (callNode.getOp()) { + case EOpTextureGather: + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || + fnCandidate[0].type->getSampler().shadow) { + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } + break; + case EOpTextureGatherOffset: + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + case EOpTextureGatherOffsets: + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + default: + break; + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if (aggArgs[compArg]->getAsConstantUnion()) { + int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + + break; + } + + case EOpTextureOffset: + case EOpTextureFetchOffset: + case EOpTextureProjOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + { + // Handle texture-offset limits checking + // Pick which argument has to hold constant offsets + int arg = -1; + switch (callNode.getOp()) { + case EOpTextureOffset: arg = 2; break; + case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break; + case EOpTextureProjOffset: arg = 2; break; + case EOpTextureLodOffset: arg = 3; break; + case EOpTextureProjLodOffset: arg = 3; break; + case EOpTextureGradOffset: arg = 4; break; + case EOpTextureProjGradOffset: arg = 4; break; + default: + assert(0); + break; + } + + if (arg > 0) { + if (aggArgs[arg]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else { + const TType& type = aggArgs[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", + "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + + break; + } + + case EOpTextureQuerySamples: + case EOpImageQuerySamples: + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicCompSwap: + break; + + case EOpInterpolateAtCentroid: + case EOpInterpolateAtSample: + case EOpInterpolateAtOffset: + // TODO(greg-lunarg): Re-enable this check. It currently gives false errors for builtins + // defined and passed as members of a struct. In this case the storage class is showing to be + // Function. See glslang #2584 + + // Make sure the first argument is an interpolant, or an array element of an interpolant + // if (arg0->getType().getQualifier().storage != EvqVaryingIn) { + // It might still be an array element. + // + // We could check more, but the semantics of the first argument are already met; the + // only way to turn an array into a float/vec* is array dereference and swizzle. + // + // ES and desktop 4.3 and earlier: swizzles may not be used + // desktop 4.4 and later: swizzles may be used + // const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true); + // if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) + // error(loc, "first argument must be an interpolant, or interpolant-array element", + // fnCandidate.getName().c_str(), ""); + // } + break; + + default: + break; + } +} + +// +// Handle seeing something in a grammar production that can be done by calling +// a constructor. +// +// The constructor still must be "handled" by handleFunctionCall(), which will +// then call handleConstructor(). +// +TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type) +{ + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpNull) { + error(loc, "cannot construct this type", type.getBasicString(), ""); + return nullptr; + } + + TString empty(""); + + return new TFunction(&empty, type, op); +} + +// +// Handle seeing a "COLON semantic" at the end of a type declaration, +// by updating the type according to the semantic. +// +void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn, + const TString& upperCase) +{ + // Parse and return semantic number. If limit is 0, it will be ignored. Otherwise, if the parsed + // semantic number is >= limit, errorMsg is issued and 0 is returned. + // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet + // accept those in lambda functions. + const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int { + size_t pos = semantic.find_last_not_of("0123456789"); + if (pos == std::string::npos) + return 0u; + + unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1); + + if (limit != 0 && semanticNum >= limit) { + error(loc, errorMsg, semantic.c_str(), ""); + return 0u; + } + + return semanticNum; + }; + + if (builtIn == EbvNone && hlslDX9Compatible()) { + if (language == EShLangVertex) { + if (qualifier.isParamOutput()) { + if (upperCase == "POSITION") { + builtIn = EbvPosition; + } + if (upperCase == "PSIZE") { + builtIn = EbvPointSize; + } + } + } else if (language == EShLangFragment) { + if (qualifier.isParamInput() && upperCase == "VPOS") { + builtIn = EbvFragCoord; + } + if (qualifier.isParamOutput()) { + if (upperCase.compare(0, 5, "COLOR") == 0) { + qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr); + nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u); + } + if (upperCase == "DEPTH") { + builtIn = EbvFragDepth; + } + } + } + } + + switch(builtIn) { + case EbvNone: + // Get location numbers from fragment outputs, instead of + // auto-assigning them. + if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) { + qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr); + nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u); + } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) { + builtIn = EbvClipDistance; + qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic"); + } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) { + builtIn = EbvCullDistance; + qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic"); + } + break; + case EbvPosition: + // adjust for stage in/out + if (language == EShLangFragment) + builtIn = EbvFragCoord; + break; + case EbvFragStencilRef: + error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", ""); + break; + case EbvTessLevelInner: + case EbvTessLevelOuter: + qualifier.patch = true; + break; + default: + break; + } + + if (qualifier.builtIn == EbvNone) + qualifier.builtIn = builtIn; + qualifier.semanticName = intermediate.addSemanticName(upperCase); +} + +// +// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN" +// +// 'location' has the "c[Subcomponent]" part. +// 'component' points to the "component" part, or nullptr if not present. +// +void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location, + const glslang::TString* component) +{ + if (location.size() == 0 || location[0] != 'c') { + error(loc, "expected 'c'", "packoffset", ""); + return; + } + if (location.size() == 1) + return; + if (! isdigit(location[1])) { + error(loc, "expected number after 'c'", "packoffset", ""); + return; + } + + qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str()); + if (component != nullptr) { + int componentOffset = 0; + switch ((*component)[0]) { + case 'x': componentOffset = 0; break; + case 'y': componentOffset = 4; break; + case 'z': componentOffset = 8; break; + case 'w': componentOffset = 12; break; + default: + componentOffset = -1; + break; + } + if (componentOffset < 0 || component->size() > 1) { + error(loc, "expected {x, y, z, w} for component", "packoffset", ""); + return; + } + qualifier.layoutOffset += componentOffset; + } +} + +// +// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN" +// +// 'profile' points to the shader_profile part, or nullptr if not present. +// 'desc' is the type# part. +// +void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile, + const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc) +{ + if (profile != nullptr) + warn(loc, "ignoring shader_profile", "register", ""); + + if (desc.size() < 1) { + error(loc, "expected register type", "register", ""); + return; + } + + int regNumber = 0; + if (desc.size() > 1) { + if (isdigit(desc[1])) + regNumber = atoi(desc.substr(1, desc.size()).c_str()); + else { + error(loc, "expected register number after register type", "register", ""); + return; + } + } + + // more information about register types see + // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register + const std::vector& resourceInfo = intermediate.getResourceSetBinding(); + switch (std::tolower(desc[0])) { + case 'c': + // c register is the register slot in the global const buffer + // each slot is a vector of 4 32 bit components + qualifier.layoutOffset = regNumber * 4 * 4; + break; + // const buffer register slot + case 'b': + // textrues and structured buffers + case 't': + // samplers + case 's': + // uav resources + case 'u': + // if nothing else has set the binding, do so now + // (other mechanisms override this one) + if (!qualifier.hasBinding()) + qualifier.layoutBinding = regNumber + subComponent; + + // This handles per-register layout sets numbers. For the global mode which sets + // every symbol to the same value, see setLinkageLayoutSets(). + if ((resourceInfo.size() % 3) == 0) { + // Apply per-symbol resource set and binding. + for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) { + if (strcmp(desc.c_str(), it[0].c_str()) == 0) { + qualifier.layoutSet = atoi(it[1].c_str()); + qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent; + break; + } + } + } + break; + default: + warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]); + break; + } + + // space + unsigned int setNumber; + const auto crackSpace = [&]() -> bool { + const int spaceLen = 5; + if (spaceDesc->size() < spaceLen + 1) + return false; + if (spaceDesc->compare(0, spaceLen, "space") != 0) + return false; + if (! isdigit((*spaceDesc)[spaceLen])) + return false; + setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str()); + return true; + }; + + // if nothing else has set the set, do so now + // (other mechanisms override this one) + if (spaceDesc && !qualifier.hasSet()) { + if (! crackSpace()) { + error(loc, "expected spaceN", "register", ""); + return; + } + qualifier.layoutSet = setNumber; + } +} + +// Convert to a scalar boolean, or if not allowed by HLSL semantics, +// report an error and return nullptr. +TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition, + bool mustBeScalar) +{ + if (mustBeScalar && !condition->getType().isScalarOrVec1()) { + error(loc, "requires a scalar", "conditional expression", ""); + return nullptr; + } + + return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()), + condition); +} + +// +// Same error message for all places assignments don't work. +// +void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, "", op, "cannot convert from '%s' to '%s'", + right.c_str(), left.c_str()); +} + +// +// Same error message for all places unary operations don't work. +// +void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) +{ + error(loc, " wrong operand type", op, + "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", + op, operand.c_str()); +} + +// +// Same error message for all binary operations don't work. +// +void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, " wrong operand types:", op, + "no operation '%s' exists that takes a left-hand operand of type '%s' and " + "a right operand of type '%s' (or there is no acceptable conversion)", + op, left.c_str(), right.c_str()); +} + +// +// A basic type of EbtVoid is a key that the name string was seen in the source, but +// it was not found as a variable in the symbol table. If so, give the error +// message and insert a dummy variable in the symbol table to prevent future errors. +// +void HlslParseContext::variableCheck(TIntermTyped*& nodePtr) +{ + TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); + if (! symbol) + return; + + if (symbol->getType().getBasicType() == EbtVoid) { + error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), ""); + + // Add to symbol table to prevent future error messages on the same name + if (symbol->getName().size() > 0) { + TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); + symbolTable.insert(*fakeVariable); + + // substitute a symbol node for this new variable + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); + } + } +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a constant. +// +void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token) +{ + if (node->getQualifier().storage != EvqConst) + error(node->getLoc(), "constant expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// an integer. +// +void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token) +{ + if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) + return; + + error(node->getLoc(), "scalar integer expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if we are currently +// globally scoped. +// +void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token) +{ + if (! symbolTable.atGlobalLevel()) + error(loc, "not allowed in nested scope", token, ""); +} + +bool HlslParseContext::builtInName(const TString& /*identifier*/) +{ + return false; +} + +// +// Make sure there is enough data and not too many arguments provided to the +// constructor to build something of the type of the constructor. Also returns +// the type of the constructor. +// +// Returns true if there was an error in construction. +// +bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, + TOperator op, TType& type) +{ + type.shallowCopy(function.getType()); + + bool constructingMatrix = false; + switch (op) { + case EOpConstructTextureSampler: + error(loc, "unhandled texture constructor", "constructor", ""); + return true; + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructIMat2x2: + case EOpConstructIMat2x3: + case EOpConstructIMat2x4: + case EOpConstructIMat3x2: + case EOpConstructIMat3x3: + case EOpConstructIMat3x4: + case EOpConstructIMat4x2: + case EOpConstructIMat4x3: + case EOpConstructIMat4x4: + case EOpConstructUMat2x2: + case EOpConstructUMat2x3: + case EOpConstructUMat2x4: + case EOpConstructUMat3x2: + case EOpConstructUMat3x3: + case EOpConstructUMat3x4: + case EOpConstructUMat4x2: + case EOpConstructUMat4x3: + case EOpConstructUMat4x4: + case EOpConstructBMat2x2: + case EOpConstructBMat2x3: + case EOpConstructBMat2x4: + case EOpConstructBMat3x2: + case EOpConstructBMat3x3: + case EOpConstructBMat3x4: + case EOpConstructBMat4x2: + case EOpConstructBMat4x3: + case EOpConstructBMat4x4: + constructingMatrix = true; + break; + default: + break; + } + + // + // Walk the arguments for first-pass checks and collection of information. + // + + int size = 0; + bool constType = true; + bool full = false; + bool overFull = false; + bool matrixInMatrix = false; + bool arrayArg = false; + for (int arg = 0; arg < function.getParamCount(); ++arg) { + if (function[arg].type->isArray()) { + if (function[arg].type->isUnsizedArray()) { + // Can't construct from an unsized array. + error(loc, "array argument must be sized", "constructor", ""); + return true; + } + arrayArg = true; + } + if (constructingMatrix && function[arg].type->isMatrix()) + matrixInMatrix = true; + + // 'full' will go to true when enough args have been seen. If we loop + // again, there is an extra argument. + if (full) { + // For vectors and matrices, it's okay to have too many components + // available, but not okay to have unused arguments. + overFull = true; + } + + size += function[arg].type->computeNumComponents(); + if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) + full = true; + + if (function[arg].type->getQualifier().storage != EvqConst) + constType = false; + } + + if (constType) + type.getQualifier().storage = EvqConst; + + if (type.isArray()) { + if (function.getParamCount() == 0) { + error(loc, "array constructor must have at least one argument", "constructor", ""); + return true; + } + + if (type.isUnsizedArray()) { + // auto adapt the constructor type to the number of arguments + type.changeOuterArraySize(function.getParamCount()); + } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) { + error(loc, "array constructor needs one argument per array element", "constructor", ""); + return true; + } + + if (type.isArrayOfArrays()) { + // Types have to match, but we're still making the type. + // Finish making the type, and the comparison is done later + // when checking for conversion. + TArraySizes& arraySizes = *type.getArraySizes(); + + // At least the dimensionalities have to match. + if (! function[0].type->isArray() || + arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) { + error(loc, "array constructor argument not correct type to construct array element", "constructor", ""); + return true; + } + + if (arraySizes.isInnerUnsized()) { + // "Arrays of arrays ..., and the size for any dimension is optional" + // That means we need to adopt (from the first argument) the other array sizes into the type. + for (int d = 1; d < arraySizes.getNumDims(); ++d) { + if (arraySizes.getDimSize(d) == UnsizedArraySize) { + arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1)); + } + } + } + } + } + + // Some array -> array type casts are okay + if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() && + !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() && + type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1) + return false; + + if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { + error(loc, "constructing non-array constituent from array argument", "constructor", ""); + return true; + } + + if (matrixInMatrix && ! type.isArray()) { + return false; + } + + if (overFull) { + error(loc, "too many arguments", "constructor", ""); + return true; + } + + if (op == EOpConstructStruct && ! type.isArray()) { + if (isScalarConstructor(node)) + return false; + + // Self-type construction: e.g, we can construct a struct from a single identically typed object. + if (function.getParamCount() == 1 && type == *function[0].type) + return false; + + if ((int)type.getStruct()->size() != function.getParamCount()) { + error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); + return true; + } + } + + if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || + (op == EOpConstructStruct && size < type.computeNumComponents())) { + error(loc, "not enough data provided for construction", "constructor", ""); + return true; + } + + return false; +} + +// See if 'node', in the context of constructing aggregates, is a scalar argument +// to a constructor. +// +bool HlslParseContext::isScalarConstructor(const TIntermNode* node) +{ + // Obviously, it must be a scalar, but an aggregate node might not be fully + // completed yet: holding a sequence of initializers under an aggregate + // would not yet be typed, so don't check it's type. This corresponds to + // the aggregate operator also not being set yet. (An aggregate operation + // that legitimately yields a scalar will have a getOp() of that operator, + // not EOpNull.) + + return node->getAsTyped() != nullptr && + node->getAsTyped()->isScalar() && + (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull); +} + +// Checks to see if a void variable has been declared and raise an error message for such a case +// +// returns true in case of an error +// +bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) +{ + if (basicType == EbtVoid) { + error(loc, "illegal use of type 'void'", identifier.c_str(), ""); + return true; + } + + return false; +} + +// +// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. +// +void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier) +{ + // move from parameter/unknown qualifiers to pipeline in/out qualifiers + switch (qualifier.storage) { + case EvqIn: + qualifier.storage = EvqVaryingIn; + break; + case EvqOut: + qualifier.storage = EvqVaryingOut; + break; + default: + break; + } +} + +// +// Merge characteristics of the 'src' qualifier into the 'dst'. +// +void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src) +{ + // Storage qualification + if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) + dst.storage = src.storage; + else if ((dst.storage == EvqIn && src.storage == EvqOut) || + (dst.storage == EvqOut && src.storage == EvqIn)) + dst.storage = EvqInOut; + else if ((dst.storage == EvqIn && src.storage == EvqConst) || + (dst.storage == EvqConst && src.storage == EvqIn)) + dst.storage = EvqConstReadOnly; + + // Layout qualifiers + mergeObjectLayoutQualifiers(dst, src, false); + + // individual qualifiers +#define MERGE_SINGLETON(field) dst.field |= src.field; + MERGE_SINGLETON(invariant); + MERGE_SINGLETON(noContraction); + MERGE_SINGLETON(centroid); + MERGE_SINGLETON(smooth); + MERGE_SINGLETON(flat); + MERGE_SINGLETON(nopersp); + MERGE_SINGLETON(patch); + MERGE_SINGLETON(sample); + MERGE_SINGLETON(coherent); + MERGE_SINGLETON(volatil); + MERGE_SINGLETON(restrict); + MERGE_SINGLETON(readonly); + MERGE_SINGLETON(writeonly); + MERGE_SINGLETON(specConstant); + MERGE_SINGLETON(nonUniform); +} + +// used to flatten the sampler type space into a single dimension +// correlates with the declaration of defaultSamplerPrecision[] +int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler) +{ + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.external ? 1 : 0; + + return EsdNumDims * + (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim; +} + +// +// Do size checking for an array type's size. +// +void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair) +{ + bool isConst = false; + sizePair.size = 1; + sizePair.node = nullptr; + + TIntermConstantUnion* constant = expr->getAsConstantUnion(); + if (constant) { + // handle true (non-specialization) constant + sizePair.size = constant->getConstArray()[0].getIConst(); + isConst = true; + } else { + // see if it's a specialization constant instead + if (expr->getQualifier().isSpecConstant()) { + isConst = true; + sizePair.node = expr; + TIntermSymbol* symbol = expr->getAsSymbolNode(); + if (symbol && symbol->getConstArray().size() > 0) + sizePair.size = symbol->getConstArray()[0].getIConst(); + } + } + + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, "array size must be a constant integer expression", "", ""); + return; + } + + if (sizePair.size <= 0) { + error(loc, "array size must be a positive integer", "", ""); + return; + } +} + +// +// Require array to be completely sized +// +void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) +{ + if (arraySizes.hasUnsized()) + error(loc, "array size required", "", ""); +} + +void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) +{ + const TTypeList& structure = *type.getStruct(); + for (int m = 0; m < (int)structure.size(); ++m) { + const TType& member = *structure[m].type; + if (member.isArray()) + arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); + } +} + +// +// Do all the semantic checking for declaring or redeclaring an array, with and +// without a size, and make the right changes to the symbol table. +// +void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, + TSymbol*& symbol, bool track) +{ + if (symbol == nullptr) { + bool currentScope; + symbol = symbolTable.find(identifier, nullptr, ¤tScope); + + if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { + // bad shader (errors already reported) trying to redeclare a built-in name as an array + return; + } + if (symbol == nullptr || ! currentScope) { + // + // Successfully process a new definition. + // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) + // + symbol = new TVariable(&identifier, type); + symbolTable.insert(*symbol); + if (track && symbolTable.atGlobalLevel()) + trackLinkage(*symbol); + + return; + } + if (symbol->getAsAnonMember()) { + error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); + symbol = nullptr; + return; + } + } + + // + // Process a redeclaration. + // + + if (symbol == nullptr) { + error(loc, "array variable name expected", identifier.c_str(), ""); + return; + } + + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); + + if (existingType.isSizedArray()) { + // be more lenient for input arrays to geometry shaders and tessellation control outputs, + // where the redeclaration is the same size + return; + } + + existingType.updateArraySizes(type); +} + +// +// Enforce non-initializer type/qualifier rules. +// +void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type, + TIntermTyped*& initializer) +{ + // + // Make the qualifier make sense, given that there is an initializer. + // + if (initializer == nullptr) { + if (type.getQualifier().storage == EvqConst || + type.getQualifier().storage == EvqConstReadOnly) { + initializer = intermediate.makeAggregate(loc); + warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), ""); + } + } +} + +// +// See if the identifier is a built-in symbol that can be redeclared, and if so, +// copy the symbol table's read-only built-in variable to the current +// global level, where it can be modified based on the passed in type. +// +// Returns nullptr if no redeclaration took place; meaning a normal declaration still +// needs to occur for it, not necessarily an error. +// +// Returns a redeclared and type-modified variable if a redeclared occurred. +// +TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier, + const TQualifier& /*qualifier*/, + const TShaderQualifiers& /*publicType*/) +{ + if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) + return nullptr; + + return nullptr; +} + +// +// Generate index to the array element in a structure buffer (SSBO) +// +TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const +{ + // Bail out if not a struct buffer + if (buffer == nullptr || ! isStructBufferType(buffer->getType())) + return nullptr; + + // Runtime sized array is always the last element. + const TTypeList* bufferStruct = buffer->getType().getStruct(); + TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc); + + TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc); + argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type); + + return argArray; +} + +// +// IFF type is a structuredbuffer/byteaddressbuffer type, return the content +// (template) type. E.g, StructuredBuffer -> MyType. Else return nullptr. +// +TType* HlslParseContext::getStructBufferContentType(const TType& type) const +{ + if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer) + return nullptr; + + const int memberCount = (int)type.getStruct()->size(); + assert(memberCount > 0); + + TType* contentType = (*type.getStruct())[memberCount-1].type; + + return contentType->isUnsizedArray() ? contentType : nullptr; +} + +// +// If an existing struct buffer has a sharable type, then share it. +// +void HlslParseContext::shareStructBufferType(TType& type) +{ + // PackOffset must be equivalent to share types on a per-member basis. + // Note: cannot use auto type due to recursion. Thus, this is a std::function. + const std::function + compareQualifiers = [&](TType& lhs, TType& rhs) -> bool { + if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset) + return false; + + if (lhs.isStruct() != rhs.isStruct()) + return false; + + if (lhs.getQualifier().builtIn != rhs.getQualifier().builtIn) + return false; + + if (lhs.isStruct() && rhs.isStruct()) { + if (lhs.getStruct()->size() != rhs.getStruct()->size()) + return false; + + for (int i = 0; i < int(lhs.getStruct()->size()); ++i) + if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type)) + return false; + } + + return true; + }; + + // We need to compare certain qualifiers in addition to the type. + const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool { + if (lhs.getQualifier().readonly != rhs.getQualifier().readonly) + return false; + + // If both are structures, recursively look for packOffset equality + // as well as type equality. + return compareQualifiers(lhs, rhs) && lhs == rhs; + }; + + // This is an exhaustive O(N) search, but real world shaders have + // only a small number of these. + for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) { + // If the deep structure matches, modulo qualifiers, use it + if (typeEqual(*structBufferTypes[idx], type)) { + type.shallowCopy(*structBufferTypes[idx]); + return; + } + } + + // Otherwise, remember it: + TType* typeCopy = new TType; + typeCopy->shallowCopy(type); + structBufferTypes.push_back(typeCopy); +} + +void HlslParseContext::paramFix(TType& type) +{ + switch (type.getQualifier().storage) { + case EvqConst: + type.getQualifier().storage = EvqConstReadOnly; + break; + case EvqGlobal: + case EvqTemporary: + type.getQualifier().storage = EvqIn; + break; + case EvqBuffer: + { + // SSBO parameter. These do not go through the declareBlock path since they are fn parameters. + correctUniform(type.getQualifier()); + TQualifier bufferQualifier = globalBufferDefaults; + mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true); + bufferQualifier.storage = type.getQualifier().storage; + bufferQualifier.readonly = type.getQualifier().readonly; + bufferQualifier.coherent = type.getQualifier().coherent; + bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn; + type.getQualifier() = bufferQualifier; + break; + } + default: + break; + } +} + +void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.containsSpecializationSize()) + error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); +} + +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type, for qualifiers not having a number set. +// This is before we know any type information for error checking. +void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { + qualifier.layoutMatrix = ElmRowMajor; + return; + } + if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { + qualifier.layoutMatrix = ElmColumnMajor; + return; + } + if (id == "push_constant") { + requireVulkan(loc, "push_constant"); + qualifier.layoutPushConstant = true; + return; + } + if (language == EShLangGeometry || language == EShLangTessEvaluation) { + if (id == TQualifier::getGeometryString(ElgTriangles)) { + // publicType.shaderQualifiers.geometry = ElgTriangles; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (language == EShLangGeometry) { + if (id == TQualifier::getGeometryString(ElgPoints)) { + // publicType.shaderQualifiers.geometry = ElgPoints; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLineStrip)) { + // publicType.shaderQualifiers.geometry = ElgLineStrip; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLines)) { + // publicType.shaderQualifiers.geometry = ElgLines; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { + // publicType.shaderQualifiers.geometry = ElgLinesAdjacency; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { + // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { + // publicType.shaderQualifiers.geometry = ElgTriangleStrip; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } else { + assert(language == EShLangTessEvaluation); + + // input primitive + if (id == TQualifier::getGeometryString(ElgTriangles)) { + // publicType.shaderQualifiers.geometry = ElgTriangles; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgQuads)) { + // publicType.shaderQualifiers.geometry = ElgQuads; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgIsolines)) { + // publicType.shaderQualifiers.geometry = ElgIsolines; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // vertex spacing + if (id == TQualifier::getVertexSpacingString(EvsEqual)) { + // publicType.shaderQualifiers.spacing = EvsEqual; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { + // publicType.shaderQualifiers.spacing = EvsFractionalEven; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { + // publicType.shaderQualifiers.spacing = EvsFractionalOdd; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // triangle order + if (id == TQualifier::getVertexOrderString(EvoCw)) { + // publicType.shaderQualifiers.order = EvoCw; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexOrderString(EvoCcw)) { + // publicType.shaderQualifiers.order = EvoCcw; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // point mode + if (id == "point_mode") { + // publicType.shaderQualifiers.pointMode = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + } + if (language == EShLangFragment) { + if (id == "origin_upper_left") { + // publicType.shaderQualifiers.originUpperLeft = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "pixel_center_integer") { + // publicType.shaderQualifiers.pixelCenterInteger = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "early_fragment_tests") { + // publicType.shaderQualifiers.earlyFragmentTests = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) { + if (id == TQualifier::getLayoutDepthString(depth)) { + // publicType.shaderQualifiers.layoutDepth = depth; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + if (id.compare(0, 13, "blend_support") == 0) { + bool found = false; + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (id == TQualifier::getBlendEquationString(be)) { + requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation"); + intermediate.addBlendEquation(be); + // publicType.shaderQualifiers.blendEquation = true; + warn(loc, "ignored", id.c_str(), ""); + found = true; + break; + } + } + if (! found) + error(loc, "unknown blend equation", "blend_support", ""); + return; + } + } + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type, for qualifiers having a number set. +// This is before we know any type information for error checking. +void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, + const TIntermTyped* node) +{ + const char* feature = "layout-id value"; + // const char* nonLiteralFeature = "non-literal layout-id value"; + + integerCheck(node, feature); + const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); + int value = 0; + if (constUnion) { + value = constUnion->getConstArray()[0].getIConst(); + } + + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == "offset") { + qualifier.layoutOffset = value; + return; + } else if (id == "align") { + // "The specified alignment must be a power of 2, or a compile-time error results." + if (! IsPow2(value)) + error(loc, "must be a power of 2", "align", ""); + else + qualifier.layoutAlign = value; + return; + } else if (id == "location") { + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(loc, "location is too large", id.c_str(), ""); + else + qualifier.layoutLocation = value; + return; + } else if (id == "set") { + if ((unsigned int)value >= TQualifier::layoutSetEnd) + error(loc, "set is too large", id.c_str(), ""); + else + qualifier.layoutSet = value; + return; + } else if (id == "binding") { + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + qualifier.layoutBinding = value; + return; + } else if (id == "component") { + if ((unsigned)value >= TQualifier::layoutComponentEnd) + error(loc, "component is too large", id.c_str(), ""); + else + qualifier.layoutComponent = value; + return; + } else if (id.compare(0, 4, "xfb_") == 0) { + // "Any shader making any static use (after preprocessing) of any of these + // *xfb_* qualifiers will cause the shader to be in a transform feedback + // capturing mode and hence responsible for describing the transform feedback + // setup." + intermediate.setXfbMode(); + if (id == "xfb_buffer") { + // "It is a compile-time error to specify an *xfb_buffer* that is greater than + // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." + if (value >= resources.maxTransformFeedbackBuffers) + error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", + resources.maxTransformFeedbackBuffers); + if (value >= (int)TQualifier::layoutXfbBufferEnd) + error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1); + else + qualifier.layoutXfbBuffer = value; + return; + } else if (id == "xfb_offset") { + if (value >= (int)TQualifier::layoutXfbOffsetEnd) + error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1); + else + qualifier.layoutXfbOffset = value; + return; + } else if (id == "xfb_stride") { + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) + error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", + resources.maxTransformFeedbackInterleavedComponents); + else if (value >= (int)TQualifier::layoutXfbStrideEnd) + error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1); + if (value < (int)TQualifier::layoutXfbStrideEnd) + qualifier.layoutXfbStride = value; + return; + } + } + + if (id == "input_attachment_index") { + requireVulkan(loc, "input_attachment_index"); + if (value >= (int)TQualifier::layoutAttachmentEnd) + error(loc, "attachment index is too large", id.c_str(), ""); + else + qualifier.layoutAttachment = value; + return; + } + if (id == "constant_id") { + setSpecConstantId(loc, qualifier, value); + return; + } + + switch (language) { + case EShLangVertex: + break; + + case EShLangTessControl: + if (id == "vertices") { + if (value == 0) + error(loc, "must be greater than 0", "vertices", ""); + else + // publicType.shaderQualifiers.vertices = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + break; + + case EShLangTessEvaluation: + break; + + case EShLangGeometry: + if (id == "invocations") { + if (value == 0) + error(loc, "must be at least 1", "invocations", ""); + else + // publicType.shaderQualifiers.invocations = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "max_vertices") { + // publicType.shaderQualifiers.vertices = value; + warn(loc, "ignored", id.c_str(), ""); + if (value > resources.maxGeometryOutputVertices) + error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); + return; + } + if (id == "stream") { + qualifier.layoutStream = value; + return; + } + break; + + case EShLangFragment: + if (id == "index") { + qualifier.layoutIndex = value; + return; + } + break; + + case EShLangCompute: + if (id.compare(0, 11, "local_size_") == 0) { + if (id == "local_size_x") { + // publicType.shaderQualifiers.localSize[0] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_y") { + // publicType.shaderQualifiers.localSize[1] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_z") { + // publicType.shaderQualifiers.localSize[2] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (spvVersion.spv != 0) { + if (id == "local_size_x_id") { + // publicType.shaderQualifiers.localSizeSpecId[0] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_y_id") { + // publicType.shaderQualifiers.localSizeSpecId[1] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_z_id") { + // publicType.shaderQualifiers.localSizeSpecId[2] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + } + break; + + default: + break; + } + + error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); +} + +void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value) +{ + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", "constant_id", ""); + } else { + qualifier.layoutSpecConstantId = value; + qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", "constant_id", ""); + } + return; +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +// +// "More than one layout qualifier may appear in a single declaration. +// Additionally, the same layout-qualifier-name can occur multiple times +// within a layout qualifier or across multiple layout qualifiers in the +// same declaration. When the same layout-qualifier-name occurs +// multiple times, in a single declaration, the last occurrence overrides +// the former occurrence(s). Further, if such a layout-qualifier-name +// will effect subsequent declarations or other observable behavior, it +// is only the last occurrence that will have any effect, behaving as if +// the earlier occurrence(s) within the declaration are not present. +// This is also true for overriding layout-qualifier-names, where one +// overrides the other (e.g., row_major vs. column_major); only the last +// occurrence has any effect." +// +void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) +{ + if (src.hasMatrix()) + dst.layoutMatrix = src.layoutMatrix; + if (src.hasPacking()) + dst.layoutPacking = src.layoutPacking; + + if (src.hasStream()) + dst.layoutStream = src.layoutStream; + + if (src.hasFormat()) + dst.layoutFormat = src.layoutFormat; + + if (src.hasXfbBuffer()) + dst.layoutXfbBuffer = src.layoutXfbBuffer; + + if (src.hasAlign()) + dst.layoutAlign = src.layoutAlign; + + if (! inheritOnly) { + if (src.hasLocation()) + dst.layoutLocation = src.layoutLocation; + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; + + if (src.hasOffset()) + dst.layoutOffset = src.layoutOffset; + + if (src.hasSet()) + dst.layoutSet = src.layoutSet; + if (src.layoutBinding != TQualifier::layoutBindingEnd) + dst.layoutBinding = src.layoutBinding; + + if (src.hasXfbStride()) + dst.layoutXfbStride = src.layoutXfbStride; + if (src.hasXfbOffset()) + dst.layoutXfbOffset = src.layoutXfbOffset; + if (src.hasAttachment()) + dst.layoutAttachment = src.layoutAttachment; + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + + if (src.layoutPushConstant) + dst.layoutPushConstant = true; + } +} + + +// +// Look up a function name in the symbol table, and make sure it is a function. +// +// First, look for an exact match. If there is none, use the generic selector +// TParseContextBase::selectFunction() to find one, parameterized by the +// convertible() and better() predicates defined below. +// +// Return the function symbol if found, otherwise nullptr. +// +const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, + TIntermTyped*& args) +{ + if (symbolTable.isFunctionNameVariable(call.getName())) { + error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); + return nullptr; + } + + // first, look for an exact match + bool dummyScope; + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // These built-in ops can accept any type, so we bypass the argument selection + if (candidateList.size() == 1 && builtIn && + (candidateList[0]->getBuiltInOp() == EOpMethodAppend || + candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip || + candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter || + candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter || + candidateList[0]->getBuiltInOp() == EOpMethodConsume)) { + return candidateList[0]; + } + + bool allowOnlyUpConversions = true; + + // can 'from' convert to 'to'? + const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool { + if (from == to) + return true; + + // no aggregate conversions + if (from.isArray() || to.isArray() || + from.isStruct() || to.isStruct()) + return false; + + switch (op) { + case EOpInterlockedAdd: + case EOpInterlockedAnd: + case EOpInterlockedCompareExchange: + case EOpInterlockedCompareStore: + case EOpInterlockedExchange: + case EOpInterlockedMax: + case EOpInterlockedMin: + case EOpInterlockedOr: + case EOpInterlockedXor: + // We do not promote the texture or image type for these ocodes. Normally that would not + // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this + // stage it's merely e.g, a basic integer type. + // + // Instead, we want to promote other arguments, but stay within the same family. In other + // words, InterlockedAdd(RWBuffer, ...) will always use the int flavor, never the uint flavor, + // but it is allowed to promote its other arguments. + if (arg == 0) + return false; + break; + case EOpMethodSample: + case EOpMethodSampleBias: + case EOpMethodSampleCmp: + case EOpMethodSampleCmpLevelZero: + case EOpMethodSampleGrad: + case EOpMethodSampleLevel: + case EOpMethodLoad: + case EOpMethodGetDimensions: + case EOpMethodGetSamplePosition: + case EOpMethodGather: + case EOpMethodCalculateLevelOfDetail: + case EOpMethodCalculateLevelOfDetailUnclamped: + case EOpMethodGatherRed: + case EOpMethodGatherGreen: + case EOpMethodGatherBlue: + case EOpMethodGatherAlpha: + case EOpMethodGatherCmp: + case EOpMethodGatherCmpRed: + case EOpMethodGatherCmpGreen: + case EOpMethodGatherCmpBlue: + case EOpMethodGatherCmpAlpha: + case EOpMethodAppend: + case EOpMethodRestartStrip: + // those are method calls, the object type can not be changed + // they are equal if the dim and type match (is dim sufficient?) + if (arg == 0) + return from.getSampler().type == to.getSampler().type && + from.getSampler().arrayed == to.getSampler().arrayed && + from.getSampler().shadow == to.getSampler().shadow && + from.getSampler().ms == to.getSampler().ms && + from.getSampler().dim == to.getSampler().dim; + break; + default: + break; + } + + // basic types have to be convertible + if (allowOnlyUpConversions) + if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall)) + return false; + + // shapes have to be convertible + if ((from.isScalarOrVec1() && to.isScalarOrVec1()) || + (from.isScalarOrVec1() && to.isVector()) || + (from.isScalarOrVec1() && to.isMatrix()) || + (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize())) + return true; + + // TODO: what are the matrix rules? they go here + + return false; + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { + // exact match is always better than mismatch + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // shape changes are always worse + if (from.isScalar() || from.isVector()) { + if (from.getVectorSize() == to2.getVectorSize() && + from.getVectorSize() != to1.getVectorSize()) + return true; + if (from.getVectorSize() == to1.getVectorSize() && + from.getVectorSize() != to2.getVectorSize()) + return false; + } + + // Handle sampler betterness: An exact sampler match beats a non-exact match. + // (If we just looked at basic type, all EbtSamplers would look the same). + // If any type is not a sampler, just use the linearize function below. + if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) { + // We can ignore the vector size in the comparison. + TSampler to1Sampler = to1.getSampler(); + TSampler to2Sampler = to2.getSampler(); + + to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize; + + if (from.getSampler() == to2Sampler) + return from.getSampler() != to1Sampler; + if (from.getSampler() == to1Sampler) + return false; + } + + // Might or might not be changing shape, which means basic type might + // or might not match, so within that, the question is how big a + // basic-type conversion is being done. + // + // Use a hierarchy of domains, translated to order of magnitude + // in a linearized view: + // - floating-point vs. integer + // - 32 vs. 64 bit (or width in general) + // - bool vs. non bool + // - signed vs. not signed + const auto linearize = [](const TBasicType& basicType) -> int { + switch (basicType) { + case EbtBool: return 1; + case EbtInt: return 10; + case EbtUint: return 11; + case EbtInt64: return 20; + case EbtUint64: return 21; + case EbtFloat: return 100; + case EbtDouble: return 110; + default: return 0; + } + }; + + return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) < + abs(linearize(to1.getBasicType()) - linearize(from.getBasicType())); + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = nullptr; + + // printf has var args and is in the symbol table as "printf()", + // mangled to "printf(" + if (call.getName() == "printf") { + TSymbol* symbol = symbolTable.find("printf(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) { + // If there is nothing selected by allowing only up-conversions (to a larger linearize() value), + // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any + // upconversions possible. + allowOnlyUpConversions = false; + bestMatch = selectFunction(candidateList, call, convertible, better, tie); + } + + if (bestMatch == nullptr) { + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + return nullptr; + } + + // For built-ins, we can convert across the arguments. This will happen in several steps: + // Step 1: If there's an exact match, use it. + // Step 2a: Otherwise, get the operator from the best match and promote arguments: + // Step 2b: reconstruct the TFunction based on the new arg types + // Step 3: Re-select after type promotion is applied, to find proper candidate. + if (builtIn) { + // Step 1: If there's an exact match, use it. + if (call.getMangledName() == bestMatch->getMangledName()) + return bestMatch; + + // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we + // are that kind of operator. + if (args != nullptr) { + // The arg list can be a unary node, or an aggregate. We have to handle both. + // We will use the normal promote() facilities, which require an interm node. + TIntermOperator* promote = nullptr; + + if (call.getParamCount() == 1) { + promote = new TIntermUnary(bestMatch->getBuiltInOp()); + promote->getAsUnaryNode()->setOperand(args->getAsTyped()); + } else { + promote = new TIntermAggregate(bestMatch->getBuiltInOp()); + promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); + } + + if (! intermediate.promote(promote)) + return nullptr; + + // Obtain the promoted arg list. + if (call.getParamCount() == 1) { + args = promote->getAsUnaryNode()->getOperand(); + } else { + promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); + } + } + + // Step 2b: reconstruct the TFunction based on the new arg types + TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp()); + + if (args->getAsAggregate()) { + // Handle aggregates: put all args into the new function call + for (int arg = 0; arg < int(args->getAsAggregate()->getSequence().size()); ++arg) { + // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly. + TParameter param = { nullptr, new TType, nullptr }; + param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType()); + convertedCall.addParameter(param); + } + } else if (args->getAsUnaryNode()) { + // Handle unaries: put all args into the new function call + TParameter param = { nullptr, new TType, nullptr }; + param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType()); + convertedCall.addParameter(param); + } else if (args->getAsTyped()) { + // Handle bare e.g, floats, not in an aggregate. + TParameter param = { nullptr, new TType, nullptr }; + param.type->shallowCopy(args->getAsTyped()->getType()); + convertedCall.addParameter(param); + } else { + assert(0); // unknown argument list. + return nullptr; + } + + // Step 3: Re-select after type promotion, to find proper candidate + // send to the generic selector + bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie); + + // At this point, there should be no tie. + } + + if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + // Append default parameter values if needed + if (!tie && bestMatch != nullptr) { + for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) { + handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue); + } + } + + return bestMatch; +} + +// +// Do everything necessary to handle a typedef declaration, for a single symbol. +// +// 'parseType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType) +{ + TVariable* typeSymbol = new TVariable(&identifier, parseType, true); + if (! symbolTable.insert(*typeSymbol)) + error(loc, "name already defined", "typedef", identifier.c_str()); +} + +// Do everything necessary to handle a struct declaration, including +// making IO aliases because HLSL allows mixed IO in a struct that specializes +// based on the usage (input, output, uniform, none). +void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type) +{ + // If it was named, which means the type can be reused later, add + // it to the symbol table. (Unless it's a block, in which + // case the name is not a type.) + if (type.getBasicType() == EbtBlock || structName.size() == 0) + return; + + TVariable* userTypeDef = new TVariable(&structName, type, true); + if (! symbolTable.insert(*userTypeDef)) { + error(loc, "redefinition", structName.c_str(), "struct"); + return; + } + + // See if we need IO aliases for the structure typeList + + const auto condAlloc = [](bool pred, TTypeList*& list) { + if (pred && list == nullptr) + list = new TTypeList; + }; + + tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform); + condAlloc( hasInput(member->type->getQualifier()), newLists.input); + condAlloc( hasOutput(member->type->getQualifier()), newLists.output); + + if (member->type->isStruct()) { + auto it = ioTypeMap.find(member->type->getStruct()); + if (it != ioTypeMap.end()) { + condAlloc(it->second.uniform != nullptr, newLists.uniform); + condAlloc(it->second.input != nullptr, newLists.input); + condAlloc(it->second.output != nullptr, newLists.output); + } + } + } + if (newLists.uniform == nullptr && + newLists.input == nullptr && + newLists.output == nullptr) { + // Won't do any IO caching, clear up the type and get out now. + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) + clearUniformInputOutput(member->type->getQualifier()); + return; + } + + // We have IO involved. + + // Make a pure typeList for the symbol table, and cache side copies of IO versions. + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) { + if (s != nullptr) { + ioMember.type = new TType; + ioMember.type->shallowCopy(*member->type); + ioMember.type->setStruct(s); + } + }; + const auto newMember = [&](TTypeLoc& m) { + if (m.type == nullptr) { + m.type = new TType; + m.type->shallowCopy(*member->type); + } + }; + + TTypeLoc newUniformMember = { nullptr, member->loc }; + TTypeLoc newInputMember = { nullptr, member->loc }; + TTypeLoc newOutputMember = { nullptr, member->loc }; + if (member->type->isStruct()) { + // swap in an IO child if there is one + auto it = ioTypeMap.find(member->type->getStruct()); + if (it != ioTypeMap.end()) { + inheritStruct(it->second.uniform, newUniformMember); + inheritStruct(it->second.input, newInputMember); + inheritStruct(it->second.output, newOutputMember); + } + } + if (newLists.uniform) { + newMember(newUniformMember); + + // inherit default matrix layout (changeable via #pragma pack_matrix), if none given. + if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone) + newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix; + + correctUniform(newUniformMember.type->getQualifier()); + newLists.uniform->push_back(newUniformMember); + } + if (newLists.input) { + newMember(newInputMember); + correctInput(newInputMember.type->getQualifier()); + newLists.input->push_back(newInputMember); + } + if (newLists.output) { + newMember(newOutputMember); + correctOutput(newOutputMember.type->getQualifier()); + newLists.output->push_back(newOutputMember); + } + + // make original pure + clearUniformInputOutput(member->type->getQualifier()); + } + ioTypeMap[type.getStruct()] = newLists; +} + +// Lookup a user-type by name. +// If found, fill in the type and return the defining symbol. +// If not found, return nullptr. +TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type) +{ + TSymbol* symbol = symbolTable.find(typeName); + if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { + type.shallowCopy(symbol->getType()); + return symbol; + } else + return nullptr; +} + +// +// Do everything necessary to handle a variable (non-block) declaration. +// Either redeclaring a variable, or making a new one, updating the symbol +// table, and all error checking. +// +// Returns a subtree node that computes an initializer, if needed. +// Returns nullptr if there is no code to execute for initialization. +// +// 'parseType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type, + TIntermTyped* initializer) +{ + if (voidErrorCheck(loc, identifier, type.getBasicType())) + return nullptr; + + // Global consts with initializers that are non-const act like EvqGlobal in HLSL. + // This test is implicitly recursive, because initializers propagate constness + // up the aggregate node tree during creation. E.g, for: + // { { 1, 2 }, { 3, 4 } } + // the initializer list is marked EvqConst at the top node, and remains so here. However: + // { 1, { myvar, 2 }, 3 } + // is not a const intializer, and still becomes EvqGlobal here. + + const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst); + + if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) { + // Force to global + type.getQualifier().storage = EvqGlobal; + } + + // make const and initialization consistent + fixConstInit(loc, identifier, type, initializer); + + // Check for redeclaration of built-ins and/or attempting to declare a reserved name + TSymbol* symbol = nullptr; + + inheritGlobalDefaults(type.getQualifier()); + + const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true); + + // correct IO in the type + switch (type.getQualifier().storage) { + case EvqGlobal: + case EvqTemporary: + clearUniformInputOutput(type.getQualifier()); + break; + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + if (type.isStruct()) { + auto it = ioTypeMap.find(type.getStruct()); + if (it != ioTypeMap.end()) + type.setStruct(it->second.uniform); + } + + break; + default: + break; + } + + // Declare the variable + if (type.isArray()) { + // array case + declareArray(loc, identifier, type, symbol, !flattenVar); + } else { + // non-array case + if (symbol == nullptr) + symbol = declareNonArray(loc, identifier, type, !flattenVar); + else if (type != symbol->getType()) + error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); + } + + if (symbol == nullptr) + return nullptr; + + if (flattenVar) + flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel()); + + TVariable* variable = symbol->getAsVariable(); + + if (initializer == nullptr) { + if (intermediate.getDebugInfo()) + return executeDeclaration(loc, variable); + else + return nullptr; + } + + // Deal with initializer + if (variable == nullptr) { + error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); + return nullptr; + } + return executeInitializer(loc, initializer, variable); +} + +// Pick up global defaults from the provide global defaults into dst. +void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const +{ + if (dst.storage == EvqVaryingOut) { + if (! dst.hasStream() && language == EShLangGeometry) + dst.layoutStream = globalOutputDefaults.layoutStream; + if (! dst.hasXfbBuffer()) + dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +} + +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = NewPoolTString(name); + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + +// Make a symbol node holding a new internal temporary variable. +TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, + const TType& type) const +{ + TVariable* tmpVar = makeInternalVariable(name, type); + tmpVar->getWritableType().getQualifier().makeTemporary(); + + return intermediate.addSymbol(*tmpVar, loc); +} + +// +// Declare a non-array variable, the main point being there is no redeclaration +// for resizing allowed. +// +// Return the successfully declared variable. +// +TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type, + bool track) +{ + // make a new variable + TVariable* variable = new TVariable(&identifier, type); + + // add variable to symbol table + if (symbolTable.insert(*variable)) { + if (track && symbolTable.atGlobalLevel()) + trackLinkage(*variable); + return variable; + } + + error(loc, "redefinition", variable->getName().c_str(), ""); + return nullptr; +} + +// Return a declaration of a temporary variable +// +// This is used to force a variable to be declared in the correct scope +// when debug information is being generated. + +TIntermNode* HlslParseContext::executeDeclaration(const TSourceLoc& loc, TVariable* variable) +{ + // + // Identifier must be of type temporary. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + if (qualifier != EvqTemporary) + return nullptr; + + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + return handleDeclare(loc, intermSymbol); +} + +// +// Handle all types of initializers from the grammar. +// +// Returning nullptr just means there is no code to execute to handle the +// initializer, which will, for example, be the case for constant initializers. +// +// Returns a subtree that accomplished the initialization. +// +TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) +{ + // + // Identifier must be of type constant, a global, or a temporary, and + // starting at version 120, desktop allows uniforms to have initializers. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + + // + // If the initializer was from braces { ... }, we convert the whole subtree to a + // constructor-style subtree, allowing the rest of the code to operate + // identically for both kinds of initializers. + // + // + // Type can't be deduced from the initializer list, so a skeletal type to + // follow has to be passed in. Constness and specialization-constness + // should be deduced bottom up, not dictated by the skeletal type. + // + TType skeletalType; + skeletalType.shallowCopy(variable->getType()); + skeletalType.getQualifier().makeTemporary(); + if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull) + initializer = convertInitializerList(loc, skeletalType, initializer, nullptr); + if (initializer == nullptr) { + // error recovery; don't leave const without constant values + if (qualifier == EvqConst) + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + // Fix outer arrayness if variable is unsized, getting size from the initializer + if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray()) + variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); + + // Inner arrayness can also get set by an initializer + if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && + initializer->getType().getArraySizes()->getNumDims() == + variable->getType().getArraySizes()->getNumDims()) { + // adopt unsized sizes from the initializer's sizes + for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { + if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) { + variable->getWritableType().getArraySizes()->setDimSize(d, + initializer->getType().getArraySizes()->getDimSize(d)); + } + } + } + + // Uniform and global consts require a constant initializer + if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) { + error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + // Const variables require a constant initializer + if (qualifier == EvqConst) { + if (initializer->getType().getQualifier().storage != EvqConst) { + variable->getWritableType().getQualifier().storage = EvqConstReadOnly; + qualifier = EvqConstReadOnly; + } + } + + if (qualifier == EvqConst || qualifier == EvqUniform) { + // Compile-time tagging of the variable with its constant value... + + initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); + if (initializer != nullptr && variable->getType() != initializer->getType()) + initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer); + if (initializer == nullptr || !initializer->getAsConstantUnion() || + variable->getType() != initializer->getType()) { + error(loc, "non-matching or non-convertible constant type for const initializer", + variable->getType().getStorageQualifierString(), ""); + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + } else { + // normal assigning of a value to a variable... + specializationCheck(loc, initializer->getType(), "initializer"); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer); + if (initNode == nullptr) + assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + return initNode; + } + + return nullptr; +} + +// +// Reprocess any initializer-list { ... } parts of the initializer. +// Need to hierarchically assign correct types and implicit +// conversions. Will do this mimicking the same process used for +// creating a constructor-style initializer, ensuring we get the +// same form. +// +// Returns a node representing an expression for the initializer list expressed +// as the correct type. +// +// Returns nullptr if there is an error. +// +TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, + TIntermTyped* initializer, TIntermTyped* scalarInit) +{ + // Will operate recursively. Once a subtree is found that is constructor style, + // everything below it is already good: Only the "top part" of the initializer + // can be an initializer list, where "top part" can extend for several (or all) levels. + + // see if we have bottomed out in the tree within the initializer-list part + TIntermAggregate* initList = initializer->getAsAggregate(); + if (initList == nullptr || initList->getOp() != EOpNull) { + // We don't have a list, but if it's a scalar and the 'type' is a + // composite, we need to lengthen below to make it useful. + // Otherwise, this is an already formed object to initialize with. + if (type.isScalar() || !initializer->getType().isScalar()) + return initializer; + else + initList = intermediate.makeAggregate(initializer); + } + + // Of the initializer-list set of nodes, need to process bottom up, + // so recurse deep, then process on the way up. + + // Go down the tree here... + if (type.isArray()) { + // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. + // Later on, initializer execution code will deal with array size logic. + TType arrayType; + arrayType.shallowCopy(type); // sharing struct stuff is fine + arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below + + // edit array sizes to fill in unsized dimensions + if (type.isUnsizedArray()) + arrayType.changeOuterArraySize((int)initList->getSequence().size()); + + // set unsized array dimensions that can be derived from the initializer's first element + if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) { + TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); + if (firstInit->getType().isArray() && + arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { + for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) { + if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize) + arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); + } + } + } + + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit); + + // recursively process each element + TType elementType(arrayType, 0); // dereferenced type + for (int i = 0; i < arrayType.getOuterArraySize(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, elementType, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + + return addConstructor(loc, initList, arrayType); + } else if (type.isStruct()) { + // do we have implicit assignments to opaques? + for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) { + if ((*type.getStruct())[i].type->containsOpaque()) { + error(loc, "cannot implicitly initialize opaque members", "initializer list", ""); + return nullptr; + } + } + + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), static_cast(type.getStruct()->size()), scalarInit); + + if (type.getStruct()->size() != initList->getSequence().size()) { + error(loc, "wrong number of structure members", "initializer list", ""); + return nullptr; + } + for (size_t i = 0; i < type.getStruct()->size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isMatrix()) { + if (type.computeNumComponents() == (int)initList->getSequence().size()) { + // This means the matrix is initialized component-wise, rather than as + // a series of rows and columns. We can just use the list directly as + // a constructor; no further processing needed. + } else { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit); + + if (type.getMatrixCols() != (int)initList->getSequence().size()) { + error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + TType vectorType(type, 0); // dereferenced type + for (int i = 0; i < type.getMatrixCols(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, vectorType, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } + } else if (type.isVector()) { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit); + + // error check; we're at bottom, so work is finished below + if (type.getVectorSize() != (int)initList->getSequence().size()) { + error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", + type.getCompleteString().c_str()); + return nullptr; + } + } else if (type.isScalar()) { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), 1, scalarInit); + + if ((int)initList->getSequence().size() != 1) { + error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + } else { + error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + + // Now that the subtree is processed, process this node as if the + // initializer list is a set of arguments to a constructor. + TIntermTyped* emulatedConstructorArguments; + if (initList->getSequence().size() == 1) + emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped(); + else + emulatedConstructorArguments = initList; + + return addConstructor(loc, emulatedConstructorArguments, type); +} + +// Lengthen list to be long enough to cover any gap from the current list size +// to 'size'. If the list is longer, do nothing. +// The value to lengthen with is the default for short lists. +// +// By default, lists that are too short due to lack of initializers initialize to zero. +// Alternatively, it could be a scalar initializer for a structure. Both cases are handled, +// based on whether something is passed in as 'scalarInit'. +// +// 'scalarInit' must be safe to use each time this is called (no side effects replication). +// +void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit) +{ + for (int c = (int)list.size(); c < size; ++c) { + if (scalarInit == nullptr) + list.push_back(intermediate.addConstantUnion(0, loc)); + else + list.push_back(scalarInit); + } +} + +// +// Test for the correctness of the parameters passed to various constructor functions +// and also convert them to the right data type, if allowed and required. +// +// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. +// +TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) +{ + if (node == nullptr) + return nullptr; + + // Construct identical type + if (type == node->getType()) + return node; + + // Handle the idiom "(struct type)" + if (type.isStruct() && isScalarConstructor(node)) { + // 'node' will almost always get used multiple times, so should not be used directly, + // it would create a DAG instead of a tree, which might be okay (would + // like to formalize that for constants and symbols), but if it has + // side effects, they would get executed multiple times, which is not okay. + if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) { + TIntermAggregate* seq = intermediate.makeAggregate(loc); + TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType()); + seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc)); + seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy)); + seq->setOp(EOpComma); + seq->setType(type); + return seq; + } else + return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node); + } + + return addConstructor(loc, node, type); +} + +// Add a constructor, either from the grammar, or other programmatic reasons. +// +// 'node' is what to construct from. +// 'type' is what type to construct. +// +// Returns the constructed object. +// Return nullptr if it can't be done. +// +TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) +{ + TIntermAggregate* aggrNode = node->getAsAggregate(); + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpConstructTextureSampler) + return intermediate.setAggregateOperator(aggrNode, op, type, loc); + + TTypeList::const_iterator memberTypes; + if (op == EOpConstructStruct) + memberTypes = type.getStruct()->begin(); + + TType elementType; + if (type.isArray()) { + TType dereferenced(type, 0); + elementType.shallowCopy(dereferenced); + } else + elementType.shallowCopy(type); + + bool singleArg; + if (aggrNode != nullptr) { + if (aggrNode->getOp() != EOpNull) + singleArg = true; + else + singleArg = false; + } else + singleArg = true; + + TIntermTyped *newNode; + if (singleArg) { + // Handle array -> array conversion + // Constructing an array of one type from an array of another type is allowed, + // assuming there are enough components available (semantic-checked earlier). + if (type.isArray() && node->isArray()) + newNode = convertArray(node, type); + + // If structure constructor or array constructor is being called + // for only one parameter inside the aggregate, we need to call constructAggregate function once. + else if (type.isArray()) + newNode = constructAggregate(node, elementType, 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); + else { + // shape conversion for matrix constructor from scalar. HLSL semantics are: scalar + // is replicated into every element of the matrix (not just the diagnonal), so + // that is handled specially here. + if (type.isMatrix() && node->getType().isScalarOrVec1()) + node = intermediate.addShapeConversion(type, node); + + newNode = constructBuiltIn(type, op, node, node->getLoc(), false); + } + + if (newNode && (type.isArray() || op == EOpConstructStruct)) + newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); + + return newNode; + } + + // + // Handle list of arguments. + // + TIntermSequence& sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor + // if the structure constructor contains more than one parameter, then construct + // each parameter + + int paramCount = 0; // keeps a track of the constructor parameter number being checked + + // for each parameter to the constructor call, check to see if the right type is passed or convert them + // to the right type if possible (and allowed). + // for structure constructors, just check if the right type is passed, no conversion is allowed. + + for (TIntermSequence::iterator p = sequenceVector.begin(); + p != sequenceVector.end(); p++, paramCount++) { + if (type.isArray()) + newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); + + if (newNode) + *p = newNode; + else + return nullptr; + } + + TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); + + return constructor; +} + +// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value +// for the parameter to the constructor (passed to this function). Essentially, it converts +// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a +// float, then float is converted to int. +// +// Returns nullptr for an error or the constructed node. +// +TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, + const TSourceLoc& loc, bool subset) +{ + TIntermTyped* newNode; + TOperator basicOp; + + // + // First, convert types as needed. + // + switch (op) { + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + case EOpConstructFloat16: + basicOp = EOpConstructFloat16; + break; + + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructFloat: + basicOp = EOpConstructFloat; + break; + + case EOpConstructDVec2: + case EOpConstructDVec3: + case EOpConstructDVec4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructDouble: + basicOp = EOpConstructDouble; + break; + + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructInt16: + basicOp = EOpConstructInt16; + break; + + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructIMat2x2: + case EOpConstructIMat2x3: + case EOpConstructIMat2x4: + case EOpConstructIMat3x2: + case EOpConstructIMat3x3: + case EOpConstructIMat3x4: + case EOpConstructIMat4x2: + case EOpConstructIMat4x3: + case EOpConstructIMat4x4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructUint16: + basicOp = EOpConstructUint16; + break; + + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUMat2x2: + case EOpConstructUMat2x3: + case EOpConstructUMat2x4: + case EOpConstructUMat3x2: + case EOpConstructUMat3x3: + case EOpConstructUMat3x4: + case EOpConstructUMat4x2: + case EOpConstructUMat4x3: + case EOpConstructUMat4x4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBMat2x2: + case EOpConstructBMat2x3: + case EOpConstructBMat2x4: + case EOpConstructBMat3x2: + case EOpConstructBMat3x3: + case EOpConstructBMat3x4: + case EOpConstructBMat4x2: + case EOpConstructBMat4x3: + case EOpConstructBMat4x4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + + default: + error(loc, "unsupported construction", "", ""); + + return nullptr; + } + newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); + if (newNode == nullptr) { + error(loc, "can't convert", "constructor", ""); + return nullptr; + } + + // + // Now, if there still isn't an operation to do the construction, and we need one, add one. + // + + // Otherwise, skip out early. + if (subset || (newNode != node && newNode->getType() == type)) + return newNode; + + // setAggregateOperator will insert a new node for the constructor, as needed. + return intermediate.setAggregateOperator(newNode, op, type, loc); +} + +// Convert the array in node to the requested type, which is also an array. +// Returns nullptr on failure, otherwise returns aggregate holding the list of +// elements needed to construct the array. +TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type) +{ + assert(node->isArray() && type.isArray()); + if (node->getType().computeNumComponents() < type.computeNumComponents()) + return nullptr; + + // TODO: write an argument replicator, for the case the argument should not be + // executed multiple times, yet multiple copies are needed. + + TIntermTyped* constructee = node->getAsTyped(); + // track where we are in consuming the argument + int constructeeElement = 0; + int constructeeComponent = 0; + + // bump up to the next component to consume + const auto getNextComponent = [&]() { + TIntermTyped* component; + component = handleBracketDereference(node->getLoc(), constructee, + intermediate.addConstantUnion(constructeeElement, node->getLoc())); + if (component->isVector()) + component = handleBracketDereference(node->getLoc(), component, + intermediate.addConstantUnion(constructeeComponent, node->getLoc())); + // bump component pointer up + ++constructeeComponent; + if (constructeeComponent == constructee->getVectorSize()) { + constructeeComponent = 0; + ++constructeeElement; + } + return component; + }; + + // make one subnode per constructed array element + TIntermAggregate* constructor = nullptr; + TType derefType(type, 0); + TType speculativeComponentType(derefType, 0); + TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType; + TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType); + TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize()); + for (int e = 0; e < type.getOuterArraySize(); ++e) { + // construct an element + TIntermTyped* elementArg; + if (type.getVectorSize() == constructee->getVectorSize()) { + // same element shape + elementArg = handleBracketDereference(node->getLoc(), constructee, + intermediate.addConstantUnion(e, node->getLoc())); + } else { + // mismatched element shapes + if (type.getVectorSize() == 1) + elementArg = getNextComponent(); + else { + // make a vector + TIntermAggregate* elementConstructee = nullptr; + for (int c = 0; c < type.getVectorSize(); ++c) + elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent()); + elementArg = addConstructor(node->getLoc(), elementConstructee, crossType); + } + } + // convert basic types + elementArg = intermediate.addConversion(componentOp, derefType, elementArg); + if (elementArg == nullptr) + return nullptr; + // combine with top-level constructor + constructor = intermediate.growAggregate(constructor, elementArg); + } + + return constructor; +} + +// This function tests for the type of the parameters to the structure or array constructor. Raises +// an error message if the expected type does not match the parameter passed to the constructor. +// +// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. +// +TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, + const TSourceLoc& loc) +{ + // Handle cases that map more 1:1 between constructor arguments and constructed. + TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); + if (converted == nullptr || converted->getType() != type) { + error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, + node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); + + return nullptr; + } + + return converted; +} + +// +// Do everything needed to add an interface block. +// +void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName) +{ + assert(type.getWritableStruct() != nullptr); + + // Clean up top-level decorations that don't belong. + switch (type.getQualifier().storage) { + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + break; + case EvqVaryingIn: + correctInput(type.getQualifier()); + break; + case EvqVaryingOut: + correctOutput(type.getQualifier()); + break; + default: + break; + } + + TTypeList& typeList = *type.getWritableStruct(); + // fix and check for member storage qualifiers and types that don't belong within a block + for (unsigned int member = 0; member < typeList.size(); ++member) { + TType& memberType = *typeList[member].type; + TQualifier& memberQualifier = memberType.getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + globalQualifierFix(memberLoc, memberQualifier); + memberQualifier.storage = type.getQualifier().storage; + + if (memberType.isStruct()) { + // clean up and pick up the right set of decorations + auto it = ioTypeMap.find(memberType.getStruct()); + switch (type.getQualifier().storage) { + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.uniform) + memberType.setStruct(it->second.uniform); + break; + case EvqVaryingIn: + correctInput(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.input) + memberType.setStruct(it->second.input); + break; + case EvqVaryingOut: + correctOutput(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.output) + memberType.setStruct(it->second.output); + break; + default: + break; + } + } + } + + // Make default block qualification, and adjust the member qualifications + + TQualifier defaultQualification; + switch (type.getQualifier().storage) { + case EvqUniform: defaultQualification = globalUniformDefaults; break; + case EvqBuffer: defaultQualification = globalBufferDefaults; break; + case EvqVaryingIn: defaultQualification = globalInputDefaults; break; + case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; + default: defaultQualification.clear(); break; + } + + // Special case for "push_constant uniform", which has a default of std430, + // contrary to normal uniform defaults, and can't have a default tracked for it. + if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking()) + type.getQualifier().layoutPacking = ElpStd430; + + // fix and check for member layout qualifiers + + mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true); + + bool memberWithLocation = false; + bool memberWithoutLocation = false; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.hasStream()) { + if (defaultQualification.layoutStream != memberQualifier.layoutStream) + error(memberLoc, "member cannot contradict block", "stream", ""); + } + + // "This includes a block's inheritance of the + // current global default buffer, a block member's inheritance of the block's + // buffer, and the requirement that any *xfb_buffer* declared on a block + // member must match the buffer inherited from the block." + if (memberQualifier.hasXfbBuffer()) { + if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + } + + if (memberQualifier.hasLocation()) { + switch (type.getQualifier().storage) { + case EvqVaryingIn: + case EvqVaryingOut: + memberWithLocation = true; + break; + default: + break; + } + } else + memberWithoutLocation = true; + + TQualifier newMemberQualification = defaultQualification; + mergeQualifiers(newMemberQualification, memberQualifier); + memberQualifier = newMemberQualification; + } + + // Process the members + fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation); + fixXfbOffsets(type.getQualifier(), typeList); + fixBlockUniformOffsets(type.getQualifier(), typeList); + + // reverse merge, so that currentBlockQualifier now has all layout information + // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) + mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true); + + // + // Build and add the interface block as a new type named 'blockName' + // + + // Use the instance name as the interface name if one exists, else the block name. + const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName(); + + TType blockType(&typeList, interfaceName, type.getQualifier()); + if (type.isArray()) + blockType.transferArraySizes(type.getArraySizes()); + + // Add the variable, as anonymous or named instanceName. + // Make an anonymous variable if no name was provided. + if (instanceName == nullptr) + instanceName = NewPoolTString(""); + + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { + if (*instanceName == "") + error(loc, "nameless block contains a member that already has a name at global scope", + "" /* blockName->c_str() */, ""); + else + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); + + return; + } + + // Save it in the AST for linker use. + if (symbolTable.atGlobalLevel()) + trackLinkage(variable); +} + +// +// "For a block, this process applies to the entire block, or until the first member +// is reached that has a location layout qualifier. When a block member is declared with a location +// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level +// declaration. Subsequent members are again assigned consecutive locations, based on the newest location, +// until the next member declared with a location qualifier. The values used for locations do not have to be +// declared in increasing order." +void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) +{ + // "If a block has no block-level location layout qualifier, it is required that either all or none of its members + // have a location layout qualifier, or a compile-time error results." + if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) + error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); + else { + if (memberWithLocation) { + // remove any block-level location and make it per *every* member + int nextLocation = 0; // by the rule above, initial value is not relevant + if (qualifier.hasAnyLocation()) { + nextLocation = qualifier.layoutLocation; + qualifier.layoutLocation = TQualifier::layoutLocationEnd; + if (qualifier.hasComponent()) { + // "It is a compile-time error to apply the *component* qualifier to a ... block" + error(loc, "cannot apply to a block", "component", ""); + } + if (qualifier.hasIndex()) { + error(loc, "cannot apply to a block", "index", ""); + } + } + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (! memberQualifier.hasLocation()) { + if (nextLocation >= (int)TQualifier::layoutLocationEnd) + error(memberLoc, "location is too large", "location", ""); + memberQualifier.layoutLocation = nextLocation; + memberQualifier.layoutComponent = 0; + } + nextLocation = memberQualifier.layoutLocation + + intermediate.computeTypeLocationSize(*typeList[member].type, language); + } + } + } +} + +void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + // "If a block is qualified with xfb_offset, all its + // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any + // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer + // offsets." + + if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) + return; + + int nextOffset = qualifier.layoutXfbOffset; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + bool contains64BitType = false; + bool contains32BitType = false; + bool contains16BitType = false; + int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); + // see if we need to auto-assign an offset to this member + if (! memberQualifier.hasXfbOffset()) { + // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" + if (contains64BitType) + RoundToPow2(nextOffset, 8); + else if (contains32BitType) + RoundToPow2(nextOffset, 4); + // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2" + else if (contains16BitType) + RoundToPow2(nextOffset, 2); + memberQualifier.layoutXfbOffset = nextOffset; + } else + nextOffset = memberQualifier.layoutXfbOffset; + nextOffset += memberSize; + } + + // The above gave all block members an offset, so we can take it off the block now, + // which will avoid double counting the offset usage. + qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +} + +// Calculate and save the offset of each block member, using the recursively +// defined block offset rules and the user-provided offset and align. +// +// Also, compute and save the total size of the block. For the block's size, arrayness +// is not taken into account, as each element is backed by a separate buffer. +// +void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList) +{ + if (! qualifier.isUniformOrBuffer()) + return; + if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar) + return; + + int offset = 0; + int memberSize; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + + // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; + int dummyStride; + int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, + qualifier.layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : qualifier.layoutMatrix == ElmRowMajor); + if (memberQualifier.hasOffset()) { + // "The specified offset must be a multiple + // of the base alignment of the type of the block member it qualifies, or a compile-time error results." + if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) + error(memberLoc, "must be a multiple of the member's alignment", "offset", + "(layout offset = %d | member alignment = %d)", memberQualifier.layoutOffset, memberAlignment); + + // "The offset qualifier forces the qualified member to start at or after the specified + // integral-constant expression, which will be its byte offset from the beginning of the buffer. + // "The actual offset of a member is computed as + // follows: If offset was declared, start with that offset, otherwise start with the next available offset." + offset = std::max(offset, memberQualifier.layoutOffset); + } + + // "The actual alignment of a member will be the greater of the specified align alignment and the standard + // (e.g., std140) base alignment for the member's type." + if (memberQualifier.hasAlign()) + memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); + + // "If the resulting offset is not a multiple of the actual alignment, + // increase it to the first offset that is a multiple of + // the actual alignment." + RoundToPow2(offset, memberAlignment); + typeList[member].type->getQualifier().layoutOffset = offset; + offset += memberSize; + } +} + +// For an identifier that is already declared, add more qualification to it. +void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) +{ + TSymbol* symbol = symbolTable.find(identifier); + if (symbol == nullptr) { + error(loc, "identifier not previously declared", identifier.c_str(), ""); + return; + } + if (symbol->getAsFunction()) { + error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); + return; + } + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.hasLayout() || + qualifier.storage != EvqTemporary || + qualifier.precision != EpqNone) { + error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); + return; + } + + // For read-only built-ins, add a new symbol for holding the modified qualifier. + // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) + if (symbol->isReadOnly()) + symbol = symbolTable.copyUp(symbol); + + if (qualifier.invariant) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "invariant", ""); + symbol->getWritableType().getQualifier().invariant = true; + } else if (qualifier.noContraction) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "precise", ""); + symbol->getWritableType().getQualifier().noContraction = true; + } else if (qualifier.specConstant) { + symbol->getWritableType().getQualifier().makeSpecConstant(); + if (qualifier.hasSpecConstantId()) + symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; + } else + warn(loc, "unknown requalification", "", ""); +} + +void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) +{ + for (unsigned int i = 0; i < identifiers.size(); ++i) + addQualifierToExisting(loc, qualifier, *identifiers[i]); +} + +// +// Update the intermediate for the given input geometry +// +bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) +{ + // these can be declared on non-entry-points, in which case they lose their meaning + if (! parsingEntrypointParameters) + return true; + + switch (geometry) { + case ElgPoints: // fall through + case ElgLines: // ... + case ElgTriangles: // ... + case ElgLinesAdjacency: // ... + case ElgTrianglesAdjacency: // ... + if (! intermediate.setInputPrimitive(geometry)) { + error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); + return false; + } + break; + + default: + error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), ""); + return false; + } + + return true; +} + +// +// Update the intermediate for the given output geometry +// +bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) +{ + // If this is not a geometry shader, ignore. It might be a mixed shader including several stages. + // Since that's an OK situation, return true for success. + if (language != EShLangGeometry) + return true; + + // these can be declared on non-entry-points, in which case they lose their meaning + if (! parsingEntrypointParameters) + return true; + + switch (geometry) { + case ElgPoints: + case ElgLineStrip: + case ElgTriangleStrip: + if (! intermediate.setOutputPrimitive(geometry)) { + error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); + return false; + } + break; + default: + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), ""); + return false; + } + + return true; +} + +// +// Selection attributes +// +void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection, + const TAttributes& attributes) +{ + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection, + const TAttributes& attributes) +{ + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a switch", "", ""); + break; + } + } +} + +// +// Loop attributes +// +void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop, + const TAttributes& attributes) +{ + if (loop == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatUnroll: + loop->setUnroll(); + break; + case EatLoop: + loop->setDontUnroll(); + break; + default: + warn(loc, "attribute does not apply to a loop", "", ""); + break; + } + } +} + +// +// Updating default qualifier for the case of a declaration with just a qualifier, +// no type, block, or identifier. +// +void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ + if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { + assert(language == EShLangTessControl || language == EShLangGeometry); + // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; + } + if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { + if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) + error(loc, "cannot change previously set layout value", "invocations", ""); + } + if (publicType.shaderQualifiers.geometry != ElgNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + switch (publicType.shaderQualifiers.geometry) { + case ElgPoints: + case ElgLines: + case ElgLinesAdjacency: + case ElgTriangles: + case ElgTrianglesAdjacency: + case ElgQuads: + case ElgIsolines: + break; + default: + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), + ""); + } + } else if (publicType.qualifier.storage == EvqVaryingOut) { + handleOutputGeometry(loc, publicType.shaderQualifiers.geometry); + } else + error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), + GetStorageQualifierString(publicType.qualifier.storage)); + } + if (publicType.shaderQualifiers.spacing != EvsNone) + intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing); + if (publicType.shaderQualifiers.order != EvoNone) + intermediate.setVertexOrder(publicType.shaderQualifiers.order); + if (publicType.shaderQualifiers.pointMode) + intermediate.setPointMode(); + for (int i = 0; i < 3; ++i) { + if (publicType.shaderQualifiers.localSize[i] > 1) { + int max = 0; + switch (i) { + case 0: max = resources.maxComputeWorkGroupSizeX; break; + case 1: max = resources.maxComputeWorkGroupSizeY; break; + case 2: max = resources.maxComputeWorkGroupSizeZ; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); + + // Fix the existing constant gl_WorkGroupSize with this new information. + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); + } + if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { + intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]); + // Set the workgroup built-in variable as a specialization constant + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + workGroupSize->getWritableType().getQualifier().specConstant = true; + } + } + if (publicType.shaderQualifiers.earlyFragmentTests) + intermediate.setEarlyFragmentTests(); + + const TQualifier& qualifier = publicType.qualifier; + + switch (qualifier.storage) { + case EvqUniform: + if (qualifier.hasMatrix()) + globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalUniformDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqBuffer: + if (qualifier.hasMatrix()) + globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalBufferDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqVaryingIn: + break; + case EvqVaryingOut: + if (qualifier.hasStream()) + globalOutputDefaults.layoutStream = qualifier.layoutStream; + if (qualifier.hasXfbBuffer()) + globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; + if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { + if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", + qualifier.layoutXfbBuffer); + } + break; + default: + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); + return; + } +} + +// +// Take the sequence of statements that has been built up since the last case/default, +// put it on the list of top-level nodes for the current (inner-most) switch statement, +// and follow that by the case/default we are on now. (See switch topology comment on +// TIntermSwitch.) +// +void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + TIntermSequence* switchSequence = switchSequenceStack.back(); + + if (statements) { + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) { + // check all previous cases for the same label (or both are 'default') + for (unsigned int s = 0; s < switchSequence->size(); ++s) { + TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); + if (prevBranch) { + TIntermTyped* prevExpression = prevBranch->getExpression(); + TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); + if (prevExpression == nullptr && newExpression == nullptr) + error(branchNode->getLoc(), "duplicate label", "default", ""); + else if (prevExpression != nullptr && + newExpression != nullptr && + prevExpression->getAsConstantUnion() && + newExpression->getAsConstantUnion() && + prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == + newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) + error(branchNode->getLoc(), "duplicated value", "case", ""); + } + } + switchSequence->push_back(branchNode); + } +} + +// +// Turn the top-level node sequence built up of wrapupSwitchSubsequence +// into a switch node. +// +TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, + TIntermAggregate* lastStatements, const TAttributes& attributes) +{ + wrapupSwitchSubsequence(lastStatements, nullptr); + + if (expression == nullptr || + (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) + error(loc, "condition must be a scalar integer expression", "switch", ""); + + // If there is nothing to do, drop the switch but still execute the expression + TIntermSequence* switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == nullptr) { + // emulate a break for error recovery + lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); + lastStatements->setOperator(EOpSequence); + switchSequence->push_back(lastStatements); + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLoc(loc); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLoc(loc); + handleSwitchAttributes(loc, switchNode, attributes); + + return switchNode; +} + +// Make a new symbol-table level that is made out of the members of a structure. +// This should be done as an anonymous struct (name is "") so that the symbol table +// finds the members with no explicit reference to a 'this' variable. +void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector& functionDeclarators) +{ + // member variables + TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct); + symbolTable.pushThis(thisVariable); + + // member functions + for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) { + // member should have a prefix matching currentTypePrefix.back() + // but, symbol lookup within the class scope will just use the + // unprefixed name. Hence, there are two: one fully prefixed and + // one with no prefix. + TFunction& member = *it->function->clone(); + member.removePrefix(currentTypePrefix.back()); + symbolTable.insert(member); + } +} + +// Track levels of class/struct/namespace nesting with a prefix string using +// the type names separated by the scoping operator. E.g., two levels +// would look like: +// +// outer::inner +// +// The string is empty when at normal global level. +// +void HlslParseContext::pushNamespace(const TString& typeName) +{ + // make new type prefix + TString newPrefix; + if (currentTypePrefix.size() > 0) + newPrefix = currentTypePrefix.back(); + newPrefix.append(typeName); + newPrefix.append(scopeMangler); + currentTypePrefix.push_back(newPrefix); +} + +// Opposite of pushNamespace(), see above +void HlslParseContext::popNamespace() +{ + currentTypePrefix.pop_back(); +} + +// Use the class/struct nesting string to create a global name for +// a member of a class/struct. +void HlslParseContext::getFullNamespaceName(TString*& name) const +{ + if (currentTypePrefix.size() == 0) + return; + + TString* fullName = NewPoolTString(currentTypePrefix.back().c_str()); + fullName->append(*name); + name = fullName; +} + +// Helper function to add the namespace scope mangling syntax to a string. +void HlslParseContext::addScopeMangler(TString& name) +{ + name.append(scopeMangler); +} + +// Return true if this has uniform-interface like decorations. +bool HlslParseContext::hasUniform(const TQualifier& qualifier) const +{ + return qualifier.hasUniformLayout() || + qualifier.layoutPushConstant; +} + +// Potentially not the opposite of hasUniform(), as if some characteristic is +// ever used for more than one thing (e.g., uniform or input), hasUniform() should +// say it exists, but clearUniform() should leave it in place. +void HlslParseContext::clearUniform(TQualifier& qualifier) +{ + qualifier.clearUniformLayout(); + qualifier.layoutPushConstant = false; +} + +// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier. +bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const +{ + switch (qualifier.builtIn) { + case EbvPosition: + case EbvPointSize: + return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment; + case EbvClipDistance: + case EbvCullDistance: + return language != EShLangVertex && language != EShLangCompute; + case EbvFragCoord: + case EbvFace: + case EbvHelperInvocation: + case EbvLayer: + case EbvPointCoord: + case EbvSampleId: + case EbvSampleMask: + case EbvSamplePosition: + case EbvViewportIndex: + return language == EShLangFragment; + case EbvGlobalInvocationId: + case EbvLocalInvocationIndex: + case EbvLocalInvocationId: + case EbvNumWorkGroups: + case EbvWorkGroupId: + case EbvWorkGroupSize: + return language == EShLangCompute; + case EbvInvocationId: + return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry; + case EbvPatchVertices: + return language == EShLangTessControl || language == EShLangTessEvaluation; + case EbvInstanceId: + case EbvInstanceIndex: + case EbvVertexId: + case EbvVertexIndex: + return language == EShLangVertex; + case EbvPrimitiveId: + return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl; + case EbvTessLevelInner: + case EbvTessLevelOuter: + return language == EShLangTessEvaluation; + case EbvTessCoord: + return language == EShLangTessEvaluation; + case EbvViewIndex: + return language != EShLangCompute; + default: + return false; + } +} + +// Return true if there are decorations to preserve for input-like storage. +bool HlslParseContext::hasInput(const TQualifier& qualifier) const +{ + if (qualifier.hasAnyLocation()) + return true; + + if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample)) + return true; + + if (language == EShLangTessEvaluation && qualifier.patch) + return true; + + if (isInputBuiltIn(qualifier)) + return true; + + return false; +} + +// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier. +bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const +{ + switch (qualifier.builtIn) { + case EbvPosition: + case EbvPointSize: + case EbvClipVertex: + case EbvClipDistance: + case EbvCullDistance: + return language != EShLangFragment && language != EShLangCompute; + case EbvFragDepth: + case EbvFragDepthGreater: + case EbvFragDepthLesser: + case EbvSampleMask: + return language == EShLangFragment; + case EbvLayer: + case EbvViewportIndex: + return language == EShLangGeometry || language == EShLangVertex; + case EbvPrimitiveId: + return language == EShLangGeometry; + case EbvTessLevelInner: + case EbvTessLevelOuter: + return language == EShLangTessControl; + default: + return false; + } +} + +// Return true if there are decorations to preserve for output-like storage. +bool HlslParseContext::hasOutput(const TQualifier& qualifier) const +{ + if (qualifier.hasAnyLocation()) + return true; + + if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb()) + return true; + + if (language == EShLangTessControl && qualifier.patch) + return true; + + if (language == EShLangGeometry && qualifier.hasStream()) + return true; + + if (isOutputBuiltIn(qualifier)) + return true; + + return false; +} + +// Make the IO decorations etc. be appropriate only for an input interface. +void HlslParseContext::correctInput(TQualifier& qualifier) +{ + clearUniform(qualifier); + if (language == EShLangVertex) + qualifier.clearInterstage(); + if (language != EShLangTessEvaluation) + qualifier.patch = false; + if (language != EShLangFragment) { + qualifier.clearInterpolation(); + qualifier.sample = false; + } + + qualifier.clearStreamLayout(); + qualifier.clearXfbLayout(); + + if (! isInputBuiltIn(qualifier)) + qualifier.builtIn = EbvNone; +} + +// Make the IO decorations etc. be appropriate only for an output interface. +void HlslParseContext::correctOutput(TQualifier& qualifier) +{ + clearUniform(qualifier); + if (language == EShLangFragment) + qualifier.clearInterstage(); + if (language != EShLangGeometry) + qualifier.clearStreamLayout(); + if (language == EShLangFragment) + qualifier.clearXfbLayout(); + if (language != EShLangTessControl) + qualifier.patch = false; + + // Fixes Test/hlsl.entry-inout.vert (SV_Position will not become a varying). + if (qualifier.builtIn == EbvNone) + qualifier.builtIn = qualifier.declaredBuiltIn; + + switch (qualifier.builtIn) { + case EbvFragDepth: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldAny); + break; + case EbvFragDepthGreater: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldGreater); + qualifier.builtIn = EbvFragDepth; + break; + case EbvFragDepthLesser: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldLess); + qualifier.builtIn = EbvFragDepth; + break; + default: + break; + } + + if (! isOutputBuiltIn(qualifier)) + qualifier.builtIn = EbvNone; +} + +// Make the IO decorations etc. be appropriate only for uniform type interfaces. +void HlslParseContext::correctUniform(TQualifier& qualifier) +{ + if (qualifier.declaredBuiltIn == EbvNone) + qualifier.declaredBuiltIn = qualifier.builtIn; + + qualifier.builtIn = EbvNone; + qualifier.clearInterstage(); + qualifier.clearInterstageLayout(); +} + +// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface. +void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier) +{ + clearUniform(qualifier); + correctUniform(qualifier); +} + + +// Set texture return type. Returns success (not all types are valid). +bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc) +{ + // Seed the output with an invalid index. We will set it to a valid one if we can. + sampler.structReturnIndex = TSampler::noReturnStruct; + + // Arrays aren't supported. + if (retType.isArray()) { + error(loc, "Arrays not supported in texture template types", "", ""); + return false; + } + + // If return type is a vector, remember the vector size in the sampler, and return. + if (retType.isVector() || retType.isScalar()) { + sampler.vectorSize = retType.getVectorSize(); + return true; + } + + // If it wasn't a vector, it must be a struct meeting certain requirements. The requirements + // are checked below: just check for struct-ness here. + if (!retType.isStruct()) { + error(loc, "Invalid texture template type", "", ""); + return false; + } + + // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading. + if (sampler.isSubpass()) { + error(loc, "Unimplemented: structure template type in subpass input", "", ""); + return false; + } + + TTypeList* members = retType.getWritableStruct(); + + // Check for too many or not enough structure members. + if (members->size() > 4 || members->size() == 0) { + error(loc, "Invalid member count in texture template structure", "", ""); + return false; + } + + // Error checking: We must have <= 4 total components, all of the same basic type. + unsigned totalComponents = 0; + for (unsigned m = 0; m < members->size(); ++m) { + // Check for bad member types + if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) { + error(loc, "Invalid texture template struct member type", "", ""); + return false; + } + + const unsigned memberVectorSize = (*members)[m].type->getVectorSize(); + totalComponents += memberVectorSize; + + // too many total member components + if (totalComponents > 4) { + error(loc, "Too many components in texture template structure type", "", ""); + return false; + } + + // All members must be of a common basic type + if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) { + error(loc, "Texture template structure members must same basic type", "", ""); + return false; + } + } + + // If the structure in the return type already exists in the table, we'll use it. Otherwise, we'll make + // a new entry. This is a linear search, but it hardly ever happens, and the list cannot be very large. + for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) { + if (textureReturnStruct[idx] == members) { + sampler.structReturnIndex = idx; + return true; + } + } + + // It wasn't found as an existing entry. See if we have room for a new one. + if (textureReturnStruct.size() >= TSampler::structReturnSlots) { + error(loc, "Texture template struct return slots exceeded", "", ""); + return false; + } + + // Insert it in the vector that tracks struct return types. + sampler.structReturnIndex = unsigned(textureReturnStruct.size()); + textureReturnStruct.push_back(members); + + // Success! + return true; +} + +// Return the sampler return type in retType. +void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const +{ + if (sampler.hasReturnStruct()) { + assert(textureReturnStruct.size() >= sampler.structReturnIndex); + + // We land here if the texture return is a structure. + TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex]; + + const TType resultType(blockStruct, ""); + retType.shallowCopy(resultType); + } else { + // We land here if the texture return is a vector or scalar. + const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize()); + retType.shallowCopy(resultType); + } +} + + +// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type +TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const +{ + const auto it = builtInTessLinkageSymbols.find(biType); + if (it == builtInTessLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr + return nullptr; + + return intermediate.addSymbol(*it->second->getAsVariable()); +} + +// Find the patch constant function (issues error, returns nullptr if not found) +const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc) +{ + if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) { + error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + const TString mangledName = patchConstantFunctionName + "("; + + // create list of PCF candidates + TVector candidateList; + bool builtIn; + symbolTable.findFunctionNameList(mangledName, candidateList, builtIn); + + // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not + // allow any disambiguation of overloads. + if (candidateList.empty()) { + error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions, + // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error + // out if there is more than one candidate. + if (candidateList.size() > 1) { + error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + return candidateList[0]; +} + +// Finalization step: Add patch constant function invocation +void HlslParseContext::addPatchConstantInvocation() +{ + TSourceLoc loc; + loc.init(); + + // If there's no patch constant function, or we're not a HS, do nothing. + if (patchConstantFunctionName.empty() || language != EShLangTessControl) + return; + + // Look for built-in variables in a function's parameter list. + const auto findBuiltIns = [&](const TFunction& function, std::set& builtIns) { + for (int p=0; pgetQualifier().storage; + + if (storage == EvqConstReadOnly) // treated identically to input + storage = EvqIn; + + if (function[p].getDeclaredBuiltIn() != EbvNone) + builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage)); + else + builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage)); + } + }; + + // If we synthesize a built-in interface variable, we must add it to the linkage. + const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) { + if (name == nullptr) { + error(loc, "unable to locate patch function parameter name", "", ""); + return; + } else { + TVariable& variable = *new TVariable(name, type); + if (! symbolTable.insert(variable)) { + error(loc, "unable to declare patch constant function interface variable", name->c_str(), ""); + return; + } + + globalQualifierFix(loc, variable.getWritableType().getQualifier()); + + if (symbolNode != nullptr) + *symbolNode = intermediate.addSymbol(variable); + + trackLinkage(variable); + } + }; + + const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) { + const TType& type = *patchConstantFunction[param].type; + const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn(); + + return type.isSizedArray() && biType == EbvOutputPatch; + }; + + // We will perform these steps. Each is in a scoped block for separation: they could + // become separate functions to make addPatchConstantInvocation shorter. + // + // 1. Union the interfaces, and create built-ins for anything present in the PCF and + // declared as a built-in variable that isn't present in the entry point's signature. + // + // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main, + // or the ones we created. Matching is based on built-in type. We may use synthesized + // variables from (1) above. + // + // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them. + // + // 3. Create a return sequence: copy the return value (if any) from the PCF to a + // (non-sanitized) output variable. In case this may involve multiple copies, such as for + // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple + // indirections into a complex R-value coming from the call to the PCF. + // + // 4. Create a barrier. + // + // 5/5B. Call the PCF inside an if test for (invocation id == 0). + + TFunction* patchConstantFunctionPtr = const_cast(findPatchConstantFunction(loc)); + + if (patchConstantFunctionPtr == nullptr) + return; + + TFunction& patchConstantFunction = *patchConstantFunctionPtr; + + const int pcfParamCount = patchConstantFunction.getParamCount(); + TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); + TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence(); + + int outPatchParam = -1; // -1 means there isn't one. + + // ================ Step 1A: Union Interfaces ================ + // Our patch constant function. + { + std::set pcfBuiltIns; // patch constant function built-ins + std::set epfBuiltIns; // entry point function built-ins + + assert(entryPointFunction); + assert(entryPointFunctionBody); + + findBuiltIns(patchConstantFunction, pcfBuiltIns); + findBuiltIns(*entryPointFunction, epfBuiltIns); + + // Find the set of built-ins in the PCF that are not present in the entry point. + std::set notInEntryPoint; + + notInEntryPoint = pcfBuiltIns; + + // std::set_difference not usable on unordered containers + for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi) + notInEntryPoint.erase(*bi); + + // Now we'll add those to the entry and to the linkage. + for (int p=0; pgetQualifier().storage; + + // Track whether there is an output patch param + if (isOutputPatch(patchConstantFunction, p)) { + if (outPatchParam >= 0) { + // Presently we only support one per ctrl pt input. + error(loc, "unimplemented: multiple output patches in patch constant function", "", ""); + return; + } + outPatchParam = p; + } + + if (biType != EbvNone) { + TType* paramType = patchConstantFunction[p].type->clone(); + + if (storage == EvqConstReadOnly) // treated identically to input + storage = EvqIn; + + // Presently, the only non-built-in we support is InputPatch, which is treated as + // a pseudo-built-in. + if (biType == EbvInputPatch) { + builtInTessLinkageSymbols[biType] = inputPatch; + } else if (biType == EbvOutputPatch) { + // Nothing... + } else { + // Use the original declaration type for the linkage + paramType->getQualifier().builtIn = biType; + if (biType == EbvTessLevelInner || biType == EbvTessLevelOuter) + paramType->getQualifier().patch = true; + + if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1) + addToLinkage(*paramType, patchConstantFunction[p].name, nullptr); + } + } + } + + // If we didn't find it because the shader made one, add our own. + if (invocationIdSym == nullptr) { + TType invocationIdType(EbtUint, EvqIn, 1); + TString* invocationIdName = NewPoolTString("InvocationId"); + invocationIdType.getQualifier().builtIn = EbvInvocationId; + addToLinkage(invocationIdType, invocationIdName, &invocationIdSym); + } + + assert(invocationIdSym); + } + + TIntermTyped* pcfArguments = nullptr; + TVariable* perCtrlPtVar = nullptr; + + // ================ Step 1B: Argument synthesis ================ + // Create pcfArguments for synthesis of patchconstantfunction invocation + { + for (int p=0; pgetWritableType().getQualifier().makeTemporary(); + } + inputArg = intermediate.addSymbol(*perCtrlPtVar, loc); + } else { + // find which built-in it is + const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn(); + + if (biType == EbvInputPatch && inputPatch == nullptr) { + error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", ""); + return; + } + + inputArg = findTessLinkageSymbol(biType); + + if (inputArg == nullptr) { + error(loc, "unable to find patch constant function built-in variable", "", ""); + return; + } + } + + if (pcfParamCount == 1) + pcfArguments = inputArg; + else + pcfArguments = intermediate.growAggregate(pcfArguments, inputArg); + } + } + + // ================ Step 2: Synthesize call to PCF ================ + TIntermAggregate* pcfCallSequence = nullptr; + TIntermTyped* pcfCall = nullptr; + + { + // Create a function call to the patchconstantfunction + if (pcfArguments) + addInputArgumentConversions(patchConstantFunction, pcfArguments); + + // Synthetic call. + pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc); + pcfCall->getAsAggregate()->setUserDefined(); + pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName()); + intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(), + patchConstantFunction.getMangledName()); + + if (pcfCall->getAsAggregate()) { + TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList(); + for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) { + TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage; + qualifierList.push_back(qual); + } + pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator()); + } + } + + // ================ Step 2B: Per Control Point synthesis ================ + // If there is per control point data, we must either emulate that with multiple + // invocations of the entry point to build up an array, or (TODO:) use a yet + // unavailable extension to look across the SIMD lanes. This is the former + // as a placeholder for the latter. + if (outPatchParam >= 0) { + // We must introduce a local temp variable of the type wanted by the PCF input. + const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize(); + + if (entryPointFunction->getType().getBasicType() == EbtVoid) { + error(loc, "entry point must return a value for use with patch constant function", "", ""); + return; + } + + // Create calls to wrapped main to fill in the array. We will substitute fixed values + // of invocation ID when calling the wrapped main. + + // This is the type of the each member of the per ctrl point array. + const TType derefType(perCtrlPtVar->getType(), 0); + + for (int cpt = 0; cpt < arraySize; ++cpt) { + // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab + // for this function. + const TString origName = entryPointFunction->getName().substr(1); + TFunction callee(&origName, TType(EbtVoid)); + TIntermTyped* callingArgs = nullptr; + + for (int i = 0; i < entryPointFunction->getParamCount(); i++) { + TParameter& param = (*entryPointFunction)[i]; + TType& paramType = *param.type; + + if (paramType.getQualifier().isParamOutput()) { + error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", ""); + return; + } + + if (paramType.getQualifier().isParamInput()) { + TIntermTyped* arg = nullptr; + if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) { + // substitute invocation ID with the array element ID + arg = intermediate.addConstantUnion(cpt, loc); + } else { + TVariable* argVar = makeInternalVariable(*param.name, *param.type); + argVar->getWritableType().getQualifier().makeTemporary(); + arg = intermediate.addSymbol(*argVar); + } + + handleFunctionArgument(&callee, callingArgs, arg); + } + } + + // Call and assign to per ctrl point variable + currentCaller = intermediate.getEntryPointMangledName().c_str(); + TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); + TIntermTyped* index = intermediate.addConstantUnion(cpt, loc); + TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc); + TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc); + element->setType(derefType); + element->setLoc(loc); + + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, + handleAssign(loc, EOpAssign, element, callReturn)); + } + } + + // ================ Step 3: Create return Sequence ================ + // Return sequence: copy PCF result to a temporary, then to shader output variable. + if (pcfCall->getBasicType() != EbtVoid) { + const TType* retType = &patchConstantFunction.getType(); // return type from the PCF + TType outType; // output type that goes with the return type. + outType.shallowCopy(*retType); + + // substitute the output type + const auto newLists = ioTypeMap.find(retType->getStruct()); + if (newLists != ioTypeMap.end()) + outType.setStruct(newLists->second.output); + + // Substitute the top level type's built-in type + if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone) + outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType(); + + outType.getQualifier().patch = true; // make it a per-patch variable + + TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType); + pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut; + + if (pcfOutput->getType().isStruct()) + flatten(*pcfOutput, false); + + assignToInterface(*pcfOutput); + + TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc); + + // The call to the PCF is a complex R-value: we want to store it in a temp to avoid + // repeated calls to the PCF: + TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType); + pcfCallResult->getWritableType().getQualifier().makeTemporary(); + + TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc); + TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall); + TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym, + intermediate.addSymbol(*pcfCallResult, loc)); + + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign); + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut); + } else { + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall); + } + + // ================ Step 4: Barrier ================ + TIntermTyped* barrier = new TIntermAggregate(EOpBarrier); + barrier->setLoc(loc); + barrier->setType(TType(EbtVoid)); + epBodySeq.insert(epBodySeq.end(), barrier); + + // ================ Step 5: Test on invocation ID ================ + TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); + TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool)); + + + // ================ Step 5B: Create if statement on Invocation ID == 0 ================ + intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc); + TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr); + invocationIdTest->setLoc(loc); + + // add our test sequence before the return. + epBodySeq.insert(epBodySeq.end(), invocationIdTest); +} + +// Finalization step: remove unused buffer blocks from linkage (we don't know until the +// shader is entirely compiled). +// Preserve order of remaining symbols. +void HlslParseContext::removeUnusedStructBufferCounters() +{ + const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(), + [this](const TSymbol* sym) { + const auto sbcIt = structBufferCounter.find(sym->getName()); + return sbcIt != structBufferCounter.end() && !sbcIt->second; + }); + + linkageSymbols.erase(endIt, linkageSymbols.end()); +} + +// Finalization step: patch texture shadow modes to match samplers they were combined with +void HlslParseContext::fixTextureShadowModes() +{ + for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) { + TSampler& sampler = (*symbol)->getWritableType().getSampler(); + + if (sampler.isTexture()) { + const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId()); + if (shadowMode != textureShadowVariant.end()) { + + if (shadowMode->second->overloaded()) + // Texture needs legalization if it's been seen with both shadow and non-shadow modes. + intermediate.setNeedsLegalization(); + + sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId()); + } + } + } +} + +// Finalization step: patch append methods to use proper stream output, which isn't known until +// main is parsed, which could happen after the append method is parsed. +void HlslParseContext::finalizeAppendMethods() +{ + TSourceLoc loc; + loc.init(); + + // Nothing to do: bypass test for valid stream output. + if (gsAppends.empty()) + return; + + if (gsStreamOutput == nullptr) { + error(loc, "unable to find output symbol for Append()", "", ""); + return; + } + + // Patch append sequences, now that we know the stream output symbol. + for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) { + append->node->getSequence()[0] = + handleAssign(append->loc, EOpAssign, + intermediate.addSymbol(*gsStreamOutput, append->loc), + append->node->getSequence()[0]->getAsTyped()); + } +} + +// post-processing +void HlslParseContext::finish() +{ + // Error check: There was a dangling .mips operator. These are not nested constructs in the grammar, so + // cannot be detected there. This is not strictly needed in a non-validating parser; it's just helpful. + if (! mipsOperatorMipArg.empty()) { + error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", ""); + } + + removeUnusedStructBufferCounters(); + addPatchConstantInvocation(); + fixTextureShadowModes(); + finalizeAppendMethods(); + + // Communicate out (esp. for command line) that we formed AST that will make + // illegal AST SPIR-V and it needs transforms to legalize it. + if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization)) + infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize"; + + TParseContextBase::finish(); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.h new file mode 100644 index 00000000..97c52d45 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseHelper.h @@ -0,0 +1,517 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef HLSL_PARSE_INCLUDED_ +#define HLSL_PARSE_INCLUDED_ + +#include "../MachineIndependent/parseVersions.h" +#include "../MachineIndependent/ParseHelper.h" +#include "../MachineIndependent/attribute.h" + +#include + +namespace glslang { + +class TFunctionDeclarator; + +class HlslParseContext : public TParseContextBase { +public: + HlslParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, + int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&, + const TString sourceEntryPointName, + bool forwardCompatible = false, EShMessages messages = EShMsgDefault); + virtual ~HlslParseContext(); + void initializeExtensionBehavior() override; + + void setLimits(const TBuiltInResource&) override; + bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; + virtual const char* getGlobalUniformBlockName() const override { return "$Global"; } + virtual void setUniformBlockDefaults(TType& block) const override + { + block.getQualifier().layoutPacking = globalUniformDefaults.layoutPacking; + block.getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix; + } + + void reservedPpErrorCheck(const TSourceLoc&, const char* /*name*/, const char* /*op*/) override { } + bool lineContinuationCheck(const TSourceLoc&, bool /*endOfComment*/) override { return true; } + bool lineDirectiveShouldSetNextLine() const override { return true; } + bool builtInName(const TString&); + + void handlePragma(const TSourceLoc&, const TVector&) override; + TIntermTyped* handleVariable(const TSourceLoc&, const TString* string); + TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + TIntermTyped* handleBracketOperator(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + + TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode); + TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field); + bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field); + void assignToInterface(TVariable& variable); + void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree); + TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&); + void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&); + void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false); + void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node); + void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector& inputs, TVector& outputs); + void remapNonEntryPointIO(TFunction& function); + TIntermNode* handleDeclare(const TSourceLoc&, TIntermTyped*); + TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*); + void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg); + TIntermTyped* handleAssign(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleAssignToMatrixSwizzle(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermTyped*); + TIntermAggregate* assignClipCullDistance(const TSourceLoc&, TOperator, int semanticId, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* assignPosition(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* assignFromFragCoord(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeStructBufferMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments); + void addInputArgumentConversions(const TFunction&, TIntermTyped*&); + void expandArguments(const TSourceLoc&, const TFunction&, TIntermTyped*&); + TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermOperator&); + void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); + TFunction* makeConstructorCall(const TSourceLoc&, const TType&); + void handleSemantic(TSourceLoc, TQualifier&, TBuiltInVariable, const TString& upperCase); + void handlePackOffset(const TSourceLoc&, TQualifier&, const glslang::TString& location, + const glslang::TString* component); + void handleRegister(const TSourceLoc&, TQualifier&, const glslang::TString* profile, const glslang::TString& desc, + int subComponent, const glslang::TString*); + TIntermTyped* convertConditionalExpression(const TSourceLoc&, TIntermTyped*, bool mustBeScalar = true); + TIntermAggregate* handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler); + + bool parseMatrixSwizzleSelector(const TSourceLoc&, const TString&, int cols, int rows, TSwizzleSelectors&); + int getMatrixComponentsColumn(int rows, const TSwizzleSelectors&); + void assignError(const TSourceLoc&, const char* op, TString left, TString right); + void unaryOpError(const TSourceLoc&, const char* op, TString operand); + void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); + void variableCheck(TIntermTyped*& nodePtr); + void constantValueCheck(TIntermTyped* node, const char* token); + void integerCheck(const TIntermTyped* node, const char* token); + void globalCheck(const TSourceLoc&, const char* token); + bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&); + void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&); + void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&); + void structArrayCheck(const TSourceLoc&, const TType& structure); + bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType); + void globalQualifierFix(const TSourceLoc&, TQualifier&); + bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType); + void mergeQualifiers(TQualifier& dst, const TQualifier& src); + int computeSamplerTypeIndex(TSampler&); + TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&); + void paramFix(TType& type); + void specializationCheck(const TSourceLoc&, const TType&, const char* op); + + void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&); + void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&, const TIntermTyped*); + void setSpecConstantId(const TSourceLoc&, TQualifier&, int value); + void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); + void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); + + const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args); + void addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args); + void declareTypedef(const TSourceLoc&, const TString& identifier, const TType&); + void declareStruct(const TSourceLoc&, TString& structName, TType&); + TSymbol* lookupUserType(const TString&, TType&); + TIntermNode* declareVariable(const TSourceLoc&, const TString& identifier, TType&, TIntermTyped* initializer = nullptr); + void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit); + TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&); + TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&); + TIntermTyped* convertArray(TIntermTyped*, const TType&); + TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); + TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); + void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = nullptr); + void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name); + void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); + void fixXfbOffsets(TQualifier&, TTypeList&); + void fixBlockUniformOffsets(const TQualifier&, TTypeList&); + void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier); + void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); + void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&); + + void nestLooping() { ++loopNestingLevel; } + void unnestLooping() { --loopNestingLevel; } + void nestAnnotations() { ++annotationNestingLevel; } + void unnestAnnotations() { --annotationNestingLevel; } + int getAnnotationNestingLevel() { return annotationNestingLevel; } + void pushScope() { symbolTable.push(); } + void popScope() { symbolTable.pop(nullptr); } + + void pushThisScope(const TType&, const TVector&); + void popThisScope() { symbolTable.pop(nullptr); } + + void pushImplicitThis(TVariable* thisParameter) { implicitThisStack.push_back(thisParameter); } + void popImplicitThis() { implicitThisStack.pop_back(); } + TVariable* getImplicitThis(int thisDepth) const { return implicitThisStack[implicitThisStack.size() - thisDepth]; } + + void pushNamespace(const TString& name); + void popNamespace(); + void getFullNamespaceName(TString*&) const; + void addScopeMangler(TString&); + + void beginParameterParsing(TFunction& function) + { + parsingEntrypointParameters = isEntrypointName(function.getName()); + } + + void pushSwitchSequence(TIntermSequence* sequence) { switchSequenceStack.push_back(sequence); } + void popSwitchSequence() { switchSequenceStack.pop_back(); } + + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, + TTypeList* typeList = nullptr) override; + + // Apply L-value conversions. E.g, turning a write to a RWTexture into an ImageStore. + TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node); + bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + + TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&); + + bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); + bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); + + // Determine selection control from attributes + void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes); + void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes); + + // Determine loop control from attributes + void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes); + + // Share struct buffer deep types + void shareStructBufferType(TType&); + + // Set texture return type of the given sampler. Returns success (not all types are valid). + bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc); + + // Obtain the sampler return type of the given sampler in retType. + void getTextureReturnType(const TSampler& sampler, TType& retType) const; + + TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const; + +protected: + struct TFlattenData { + TFlattenData() : nextBinding(TQualifier::layoutBindingEnd), + nextLocation(TQualifier::layoutLocationEnd) { } + TFlattenData(int nb, int nl) : nextBinding(nb), nextLocation(nl) { } + + TVector members; // individual flattened variables + TVector offsets; // offset to next tree level + unsigned int nextBinding; // next binding to use. + unsigned int nextLocation; // next location to use + }; + + void fixConstInit(const TSourceLoc&, const TString& identifier, TType& type, TIntermTyped*& initializer); + void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; + TVariable* makeInternalVariable(const TString& name, const TType& type) const { + return makeInternalVariable(name.c_str(), type); + } + TIntermSymbol* makeInternalVariableNode(const TSourceLoc&, const char* name, const TType&) const; + TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&, bool track); + void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&, bool track); + TIntermNode* executeDeclaration(const TSourceLoc&, TVariable* variable); + TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); + TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer, TIntermTyped* scalarInit); + bool isScalarConstructor(const TIntermNode*); + TOperator mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage); + bool isEntrypointName(const TString& name) { return name.compare(intermediate.getEntryPointName().c_str()) == 0; } + + // Return true if this node requires L-value conversion (e.g, to an imageStore). + bool shouldConvertLValue(const TIntermNode*) const; + + // Array and struct flattening + TIntermTyped* flattenAccess(TIntermTyped* base, int member); + TIntermTyped* flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1); + int findSubtreeOffset(const TIntermNode&) const; + int findSubtreeOffset(const TType&, int subset, const TVector& offsets) const; + bool shouldFlatten(const TType&, TStorageQualifier, bool topLevel) const; + bool wasFlattened(const TIntermTyped* node) const; + bool wasFlattened(long long id) const { return flattenMap.find(id) != flattenMap.end(); } + int addFlattenedMember(const TVariable&, const TType&, TFlattenData&, const TString& name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + + // Structure splitting (splits interstage built-in types into its own struct) + void split(const TVariable&); + void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&); + const TType& split(const TType& type, const TString& name, const TQualifier&); + bool wasSplit(const TIntermTyped* node) const; + bool wasSplit(long long id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); } + TVariable* getSplitNonIoVar(long long id) const; + void addPatchConstantInvocation(); + void fixTextureShadowModes(); + void finalizeAppendMethods(); + TIntermTyped* makeIntegerIndex(TIntermTyped*); + + void fixBuiltInIoType(TType&); + + void flatten(const TVariable& variable, bool linkage, bool arrayed = false); + int flatten(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + int flattenStruct(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + int flattenArray(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier); + + bool hasUniform(const TQualifier& qualifier) const; + void clearUniform(TQualifier& qualifier); + bool isInputBuiltIn(const TQualifier& qualifier) const; + bool hasInput(const TQualifier& qualifier) const; + void correctOutput(TQualifier& qualifier); + bool isOutputBuiltIn(const TQualifier& qualifier) const; + bool hasOutput(const TQualifier& qualifier) const; + void correctInput(TQualifier& qualifier); + void correctUniform(TQualifier& qualifier); + void clearUniformInputOutput(TQualifier& qualifier); + + // Test method names + bool isStructBufferMethod(const TString& name) const; + void counterBufferType(const TSourceLoc& loc, TType& type); + + // Return standard sample position array + TIntermConstantUnion* getSamplePosArray(int count); + + TType* getStructBufferContentType(const TType& type) const; + bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; } + TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const; + TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer); + TString getStructBuffCounterName(const TString&) const; + void addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*&); + void addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter&, TIntermAggregate*&); + + // Return true if this type is a reference. This is not currently a type method in case that's + // a language specific answer. + bool isReference(const TType& type) const { return isStructBufferType(type); } + + // Return true if this a buffer type that has an associated counter buffer. + bool hasStructBuffCounter(const TType&) const; + + // Finalization step: remove unused buffer blocks from linkage (we don't know until the + // shader is entirely compiled) + void removeUnusedStructBufferCounters(); + + static bool isClipOrCullDistance(TBuiltInVariable); + static bool isClipOrCullDistance(const TQualifier& qual) { return isClipOrCullDistance(qual.builtIn); } + static bool isClipOrCullDistance(const TType& type) { return isClipOrCullDistance(type.getQualifier()); } + + // Find the patch constant function (issues error, returns nullptr if not found) + const TFunction* findPatchConstantFunction(const TSourceLoc& loc); + + // Pass through to base class after remembering built-in mappings. + using TParseContextBase::trackLinkage; + void trackLinkage(TSymbol& variable) override; + + void finish() override; // post-processing + + // Linkage symbol helpers + TIntermSymbol* findTessLinkageSymbol(TBuiltInVariable biType) const; + + // Current state of parsing + int annotationNestingLevel; // 0 if outside all annotations + + HlslParseContext(HlslParseContext&); + HlslParseContext& operator=(HlslParseContext&); + + static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2)); // see computeSamplerTypeIndex() + TQualifier globalBufferDefaults; + TQualifier globalUniformDefaults; + TQualifier globalInputDefaults; + TQualifier globalOutputDefaults; + TString currentCaller; // name of last function body entered (not valid when at global scope) + TIdSetType inductiveLoopIds; + TVector needsIndexLimitationChecking; + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of symbols whose array sizes + // can be fixed + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in array use: copyUp() symbol and add it to the resize-list + // + // - on user array declaration: add it to the resize-list + // + // - on block redeclaration: copyUp() symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing a layout declaration that sizes the array, fix everything in the + // resize-list, giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // array-sizing declarations + // + TVector ioArraySymbolResizeList; + + TMap flattenMap; + + // IO-type map. Maps a pure symbol-table form of a structure-member list into + // each of the (up to) three kinds of IO, as each as different allowed decorations, + // but HLSL allows mixing all in the same structure. + struct tIoKinds { + TTypeList* input; + TTypeList* output; + TTypeList* uniform; + }; + TMap ioTypeMap; + + // Structure splitting data: + TMap splitNonIoVars; // variables with the built-in interstage IO removed, indexed by unique ID. + + // Structuredbuffer shared types. Typically there are only a few. + TVector structBufferTypes; + + // This tracks texture sample user structure return types. Only a limited number are supported, as + // may fit in TSampler::structReturnIndex. + TVector textureReturnStruct; + + TMap structBufferCounter; // true if counter buffer is in use + + // The built-in interstage IO map considers e.g, EvqPosition on input and output separately, so that we + // can build the linkage correctly if position appears on both sides. Otherwise, multiple positions + // are considered identical. + struct tInterstageIoData { + tInterstageIoData(TBuiltInVariable bi, TStorageQualifier q) : + builtIn(bi), storage(q) { } + + TBuiltInVariable builtIn; + TStorageQualifier storage; + + // ordering for maps + bool operator<(const tInterstageIoData d) const { + return (builtIn != d.builtIn) ? (builtIn < d.builtIn) : (storage < d.storage); + } + }; + + TMap splitBuiltIns; // split built-ins, indexed by built-in type. + TVariable* inputPatch; // input patch is special for PCF: it's the only non-builtin PCF input, + // and is handled as a pseudo-builtin. + + unsigned int nextInLocation; + unsigned int nextOutLocation; + + TFunction* entryPointFunction; + TIntermNode* entryPointFunctionBody; + + TString patchConstantFunctionName; // hull shader patch constant function name, from function level attribute. + TMap builtInTessLinkageSymbols; // used for tessellation, finding declared built-ins + + TVector currentTypePrefix; // current scoping prefix for nested structures + TVector implicitThisStack; // currently active 'this' variables for nested structures + + TVariable* gsStreamOutput; // geometry shader stream outputs, for emit (Append method) + + TVariable* clipDistanceOutput; // synthesized clip distance out variable (shader might have >1) + TVariable* cullDistanceOutput; // synthesized cull distance out variable (shader might have >1) + TVariable* clipDistanceInput; // synthesized clip distance in variable (shader might have >1) + TVariable* cullDistanceInput; // synthesized cull distance in variable (shader might have >1) + + static const int maxClipCullRegs = 2; + std::array clipSemanticNSizeIn; // vector, indexed by clip semantic ID + std::array cullSemanticNSizeIn; // vector, indexed by cull semantic ID + std::array clipSemanticNSizeOut; // vector, indexed by clip semantic ID + std::array cullSemanticNSizeOut; // vector, indexed by cull semantic ID + + // This tracks the first (mip level) argument to the .mips[][] operator. Since this can be nested as + // in tx.mips[tx.mips[0][1].x][2], we need a stack. We also track the TSourceLoc for error reporting + // purposes. + struct tMipsOperatorData { + tMipsOperatorData(TSourceLoc l, TIntermTyped* m) : loc(l), mipLevel(m) { } + TSourceLoc loc; + TIntermTyped* mipLevel; + }; + + TVector mipsOperatorMipArg; + + // The geometry output stream is not copied out from the entry point as a typical output variable + // is. It's written via EmitVertex (hlsl=Append), which may happen in arbitrary control flow. + // For this we need the real output symbol. Since it may not be known at the time and Append() + // method is parsed, the sequence will be patched during finalization. + struct tGsAppendData { + TIntermAggregate* node; + TSourceLoc loc; + }; + + TVector gsAppends; + + // A texture object may be used with shadow and non-shadow samplers, but both may not be + // alive post-DCE in the same shader. We do not know at compilation time which are alive: that's + // only known post-DCE. If a texture is used both ways, we create two textures, and + // leave the elimiation of one to the optimizer. This maps the shader variant to + // the shadow variant. + // + // This can be removed if and when the texture shadow code in + // HlslParseContext::handleSamplerTextureCombine is removed. + struct tShadowTextureSymbols { + tShadowTextureSymbols() { symId.fill(-1); } + + void set(bool shadow, long long id) { symId[int(shadow)] = id; } + long long get(bool shadow) const { return symId[int(shadow)]; } + + // True if this texture has been seen with both shadow and non-shadow modes + bool overloaded() const { return symId[0] != -1 && symId[1] != -1; } + bool isShadowId(long long id) const { return symId[1] == id; } + + private: + std::array symId; + }; + + TMap textureShadowVariant; + bool parsingEntrypointParameters; +}; + +// This is the prefix we use for built-in methods to avoid namespace collisions with +// global scope user functions. +// TODO: this would be better as a nonparseable character, but that would +// require changing the scanner. +#define BUILTIN_PREFIX "__BI_" + +} // end namespace glslang + +#endif // HLSL_PARSE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.cpp new file mode 100644 index 00000000..8fb1d290 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.cpp @@ -0,0 +1,1259 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Create strings that declare built-in definitions, add built-ins programmatically +// that cannot be expressed in the strings, and establish mappings between +// built-in functions and operators. +// +// Where to put a built-in: +// TBuiltInParseablesHlsl::initialize(version,profile) context-independent textual built-ins; add them to the right string +// TBuiltInParseablesHlsl::initialize(resources,...) context-dependent textual built-ins; add them to the right string +// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the +// symbol table, including identifying what extensions are needed if a version does +// not allow a symbol +// + +#include "hlslParseables.h" +#include "hlslParseHelper.h" +#include +#include +#include + +namespace { // anonymous namespace functions + +// arg order queries +bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; } +bool IsArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&' || argOrder == '#'; } +bool IsTextureNonMS(const char argOrder) { return argOrder == '%'; } +bool IsSubpassInput(const char argOrder) { return argOrder == '[' || argOrder == ']'; } +bool IsArrayedTexture(const char argOrder) { return argOrder == '@'; } +bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; } +bool IsMS(const char argOrder) { return IsTextureMS(argOrder) || argOrder == ']'; } +bool IsBuffer(const char argOrder) { return argOrder == '*' || argOrder == '~'; } +bool IsImage(const char argOrder) { return argOrder == '!' || argOrder == '#' || argOrder == '~'; } + +bool IsTextureType(const char argOrder) +{ + return IsTextureNonMS(argOrder) || IsArrayedTexture(argOrder) || + IsTextureMS(argOrder) || IsBuffer(argOrder) || IsImage(argOrder); +} + +// Reject certain combinations that are illegal sample methods. For example, +// 3D arrays. +bool IsIllegalSample(const glslang::TString& name, const char* argOrder, int dim0) +{ + const bool isArrayed = IsArrayed(*argOrder); + const bool isMS = IsTextureMS(*argOrder); + const bool isBuffer = IsBuffer(*argOrder); + + // there are no 3D arrayed textures, or 3D SampleCmp(LevelZero) + if (dim0 == 3 && (isArrayed || name == "SampleCmp" || name == "SampleCmpLevelZero")) + return true; + + const int numArgs = int(std::count(argOrder, argOrder + strlen(argOrder), ',')) + 1; + + // Reject invalid offset forms with cubemaps + if (dim0 == 4) { + if ((name == "Sample" && numArgs >= 4) || + (name == "SampleBias" && numArgs >= 5) || + (name == "SampleCmp" && numArgs >= 5) || + (name == "SampleCmpLevelZero" && numArgs >= 5) || + (name == "SampleGrad" && numArgs >= 6) || + (name == "SampleLevel" && numArgs >= 5)) + return true; + } + + const bool isGather = + (name == "Gather" || + name == "GatherRed" || + name == "GatherGreen" || + name == "GatherBlue" || + name == "GatherAlpha"); + + const bool isGatherCmp = + (name == "GatherCmp" || + name == "GatherCmpRed" || + name == "GatherCmpGreen" || + name == "GatherCmpBlue" || + name == "GatherCmpAlpha"); + + // Reject invalid Gathers + if (isGather || isGatherCmp) { + if (dim0 == 1 || dim0 == 3) // there are no 1D or 3D gathers + return true; + + // no offset on cube or cube array gathers + if (dim0 == 4) { + if ((isGather && numArgs > 3) || (isGatherCmp && numArgs > 4)) + return true; + } + } + + // Reject invalid Loads + if (name == "Load" && dim0 == 4) + return true; // Load does not support any cubemaps, arrayed or not. + + // Multisample formats are only 2D and 2Darray + if (isMS && dim0 != 2) + return true; + + // Buffer are only 1D + if (isBuffer && dim0 != 1) + return true; + + return false; +} + +// Return the number of the coordinate arg, if any +int CoordinateArgPos(const glslang::TString& name, bool isTexture) +{ + if (!isTexture || (name == "GetDimensions")) + return -1; // has none + else if (name == "Load") + return 1; + else + return 2; // other texture methods are 2 +} + +// Some texture methods use an addition coordinate dimension for the mip +bool HasMipInCoord(const glslang::TString& name, bool isMS, bool isBuffer, bool isImage) +{ + return name == "Load" && !isMS && !isBuffer && !isImage; +} + +// LOD calculations don't pass the array level in the coordinate. +bool NoArrayCoord(const glslang::TString& name) +{ + return name == "CalculateLevelOfDetail" || name == "CalculateLevelOfDetailUnclamped"; +} + +// Handle IO params marked with > or < +const char* IoParam(glslang::TString& s, const char* nthArgOrder) +{ + if (*nthArgOrder == '>') { // output params + ++nthArgOrder; + s.append("out "); + } else if (*nthArgOrder == '<') { // input params + ++nthArgOrder; + s.append("in "); + } + + return nthArgOrder; +} + +// Handle repeated args +void HandleRepeatArg(const char*& arg, const char*& prev, const char* current) +{ + if (*arg == ',' || *arg == '\0') + arg = prev; + else + prev = current; +} + +// Return true for the end of a single argument key, which can be the end of the string, or +// the comma separator. +inline bool IsEndOfArg(const char* arg) +{ + return arg == nullptr || *arg == '\0' || *arg == ','; +} + +// If this is a fixed vector size, such as V3, return the size. Else return 0. +int FixedVecSize(const char* arg) +{ + while (!IsEndOfArg(arg)) { + if (isdigit(*arg)) + return *arg - '0'; + ++arg; + } + + return 0; // none found. +} + +// Create and return a type name, using HLSL type conventions. +// +// order: S = scalar, V = vector, M = matrix +// argType: F = float, D = double, I = int, U = uint, B = bool, S = sampler +// dim0 = vector dimension, or matrix 1st dimension +// dim1 = matrix 2nd dimension +glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, const char* argType, int dim0, int dim1) +{ + const bool isTranspose = (argOrder[0] == '^'); + const bool isTexture = IsTextureType(argOrder[0]); + const bool isArrayed = IsArrayed(argOrder[0]); + const bool isSampler = IsSamplerType(argType[0]); + const bool isMS = IsMS(argOrder[0]); + const bool isBuffer = IsBuffer(argOrder[0]); + const bool isImage = IsImage(argOrder[0]); + const bool isSubpass = IsSubpassInput(argOrder[0]); + + char type = *argType; + + if (isTranspose) { // Take transpose of matrix dimensions + std::swap(dim0, dim1); + } else if (isTexture || isSubpass) { + if (type == 'F') // map base type to texture of that type. + type = 'T'; // e.g, int -> itexture, uint -> utexture, etc. + else if (type == 'I') + type = 'i'; + else if (type == 'U') + type = 'u'; + } + + if (isTranspose) + ++argOrder; + + char order = *argOrder; + + switch (type) { + case '-': s += "void"; break; + case 'F': s += "float"; break; + case 'D': s += "double"; break; + case 'I': s += "int"; break; + case 'U': s += "uint"; break; + case 'L': s += "int64_t"; break; + case 'M': s += "uint64_t"; break; + case 'B': s += "bool"; break; + case 'S': s += "sampler"; break; + case 's': s += "SamplerComparisonState"; break; + case 'T': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + case 'i': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + case 'u': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + default: s += "UNKNOWN_TYPE"; break; + } + + if (isSubpass && isMS) + s += "MS"; + + // handle fixed vector sizes, such as float3, and only ever 3. + const int fixedVecSize = FixedVecSize(argOrder); + if (fixedVecSize != 0) + dim0 = dim1 = fixedVecSize; + + const char dim0Char = ('0' + char(dim0)); + const char dim1Char = ('0' + char(dim1)); + + // Add sampler dimensions + if (isSampler || isTexture) { + if ((order == 'V' || isTexture) && !isBuffer) { + switch (dim0) { + case 1: s += "1D"; break; + case 2: s += (isMS ? "2DMS" : "2D"); break; + case 3: s += "3D"; break; + case 4: s += (type == 'S'? "CUBE" : "Cube"); break; + default: s += "UNKNOWN_SAMPLER"; break; + } + } + } else { + // Non-sampler type: + // verify dimensions + if (((order == 'V' || order == 'M') && (dim0 < 1 || dim0 > 4)) || + (order == 'M' && (dim1 < 1 || dim1 > 4))) { + s += "UNKNOWN_DIMENSION"; + return s; + } + + switch (order) { + case '-': break; // no dimensions for voids + case 'S': break; // no dimensions on scalars + case 'V': + s += dim0Char; + break; + case 'M': + s += dim0Char; + s += 'x'; + s += dim1Char; + break; + default: + break; + } + } + + // handle arrayed textures + if (isArrayed) + s += "Array"; + + switch (type) { + case 'i': s += " 0) // handle fixed sized vectors + dim0Min = dim0Max = fixedVecSize; +} + +} // end anonymous namespace + +namespace glslang { + +TBuiltInParseablesHlsl::TBuiltInParseablesHlsl() +{ +} + +// +// Handle creation of mat*mat specially, since it doesn't fall conveniently out of +// the generic prototype creation code below. +// +void TBuiltInParseablesHlsl::createMatTimesMat() +{ + TString& s = commonBuiltins; + + for (int xRows = 1; xRows <=4; xRows++) { + for (int xCols = 1; xCols <=4; xCols++) { + const int yRows = xCols; + for (int yCols = 1; yCols <=4; yCols++) { + const int retRows = xRows; + const int retCols = yCols; + + // Create a mat * mat of the appropriate dimensions + AppendTypeName(s, "M", "F", retRows, retCols); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + s.append(", "); + AppendTypeName(s, "M", "F", yRows, yCols); // add Y input + + s.append(");\n"); // close paren + } + + // Create M*V + AppendTypeName(s, "V", "F", xRows, 1); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + s.append(", "); + AppendTypeName(s, "V", "F", xCols, 1); // add Y input + + s.append(");\n"); // close paren + + // Create V*M + AppendTypeName(s, "V", "F", xCols, 1); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "V", "F", xRows, 1); // add Y input + s.append(", "); + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + + s.append(");\n"); // close paren + } + } +} + +// +// Add all context-independent built-in functions and variables that are present +// for the given version and profile. Share common ones across stages, otherwise +// make stage-specific entries. +// +// Most built-ins variables can be added as simple text strings. Some need to +// be added programmatically, which is done later in IdentifyBuiltIns() below. +// +void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/) +{ + static const EShLanguageMask EShLangAll = EShLanguageMask(EShLangCount - 1); + + // These are the actual stage masks defined in the documentation, in case they are + // needed for future validation. For now, they are commented out, and set below + // to EShLangAll, to allow any intrinsic to be used in any shader, which is legal + // if it is not called. + // + // static const EShLanguageMask EShLangPSCS = EShLanguageMask(EShLangFragmentMask | EShLangComputeMask); + // static const EShLanguageMask EShLangVSPSGS = EShLanguageMask(EShLangVertexMask | EShLangFragmentMask | EShLangGeometryMask); + // static const EShLanguageMask EShLangCS = EShLangComputeMask; + // static const EShLanguageMask EShLangPS = EShLangFragmentMask; + // static const EShLanguageMask EShLangHS = EShLangTessControlMask; + + // This set uses EShLangAll for everything. + static const EShLanguageMask EShLangPSCS = EShLangAll; + static const EShLanguageMask EShLangVSPSGS = EShLangAll; + static const EShLanguageMask EShLangCS = EShLangAll; + static const EShLanguageMask EShLangPS = EShLangAll; + static const EShLanguageMask EShLangHS = EShLangAll; + static const EShLanguageMask EShLangGS = EShLangAll; + + // This structure encodes the prototype information for each HLSL intrinsic. + // Because explicit enumeration would be cumbersome, it's procedurally generated. + // orderKey can be: + // S = scalar, V = vector, M = matrix, - = void + // typekey can be: + // D = double, F = float, U = uint, I = int, B = bool, S = sampler, s = shadowSampler, M = uint64_t, L = int64_t + // An empty order or type key repeats the first one. E.g: SVM,, means 3 args each of SVM. + // '>' as first letter of order creates an output parameter + // '<' as first letter of order creates an input parameter + // '^' as first letter of order takes transpose dimensions + // '%' as first letter of order creates texture of given F/I/U type (texture, itexture, etc) + // '@' as first letter of order creates arrayed texture of given type + // '$' / '&' as first letter of order creates 2DMS / 2DMSArray textures + // '*' as first letter of order creates buffer object + // '!' as first letter of order creates image object + // '#' as first letter of order creates arrayed image object + // '~' as first letter of order creates an image buffer object + // '[' / ']' as first letter of order creates a SubpassInput/SubpassInputMS object + + static const struct { + const char* name; // intrinsic name + const char* retOrder; // return type key: empty matches order of 1st argument + const char* retType; // return type key: empty matches type of 1st argument + const char* argOrder; // argument order key + const char* argType; // argument type key + unsigned int stage; // stage mask + bool method; // true if it's a method. + } hlslIntrinsics[] = { + // name retOrd retType argOrder argType stage mask method + // ---------------------------------------------------------------------------------------------------------------- + { "abort", nullptr, nullptr, "-", "-", EShLangAll, false }, + { "abs", nullptr, nullptr, "SVM", "DFUI", EShLangAll, false }, + { "acos", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "all", "S", "B", "SVM", "BFIU", EShLangAll, false }, + { "AllMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "AllMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "any", "S", "B", "SVM", "BFIU", EShLangAll, false }, + { "asdouble", "S", "D", "S,", "UI,", EShLangAll, false }, + { "asdouble", "V2", "D", "V2,", "UI,", EShLangAll, false }, + { "asfloat", nullptr, "F", "SVM", "BFIU", EShLangAll, false }, + { "asin", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "asint", nullptr, "I", "SVM", "FIU", EShLangAll, false }, + { "asuint", nullptr, "U", "SVM", "FIU", EShLangAll, false }, + { "atan", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "atan2", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "ceil", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "CheckAccessFullyMapped", "S", "B" , "S", "U", EShLangPSCS, false }, + { "clamp", nullptr, nullptr, "SVM,,", "FUI,,", EShLangAll, false }, + { "clip", "-", "-", "SVM", "FUI", EShLangPS, false }, + { "cos", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "cosh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "countbits", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "cross", nullptr, nullptr, "V3,", "F,", EShLangAll, false }, + { "D3DCOLORtoUBYTE4", "V4", "I", "V4", "F", EShLangAll, false }, + { "ddx", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddx_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddx_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "degrees", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "determinant", "S", "F", "M", "F", EShLangAll, false }, + { "DeviceMemoryBarrier", nullptr, nullptr, "-", "-", EShLangPSCS, false }, + { "DeviceMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "distance", "S", "F", "SV,", "F,", EShLangAll, false }, + { "dot", "S", nullptr, "SV,", "FI,", EShLangAll, false }, + { "dst", nullptr, nullptr, "V4,", "F,", EShLangAll, false }, + // { "errorf", "-", "-", "", "", EShLangAll, false }, TODO: varargs + { "EvaluateAttributeAtCentroid", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "EvaluateAttributeAtSample", nullptr, nullptr, "SVM,S", "F,U", EShLangPS, false }, + { "EvaluateAttributeSnapped", nullptr, nullptr, "SVM,V2", "F,I", EShLangPS, false }, + { "exp", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "exp2", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "f16tof32", nullptr, "F", "SV", "U", EShLangAll, false }, + { "f32tof16", nullptr, "U", "SV", "F", EShLangAll, false }, + { "faceforward", nullptr, nullptr, "V,,", "F,,", EShLangAll, false }, + { "firstbithigh", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "firstbitlow", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "floor", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "fma", nullptr, nullptr, "SVM,,", "D,,", EShLangAll, false }, + { "fmod", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "frac", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "frexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "fwidth", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "GetRenderTargetSampleCount", "S", "U", "-", "-", EShLangAll, false }, + { "GetRenderTargetSamplePosition", "V2", "F", "V1", "I", EShLangAll, false }, + { "GroupMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "GroupMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "InterlockedAdd", "-", "-", "SVM,,>", "FUI,,", EShLangPSCS, false }, + { "InterlockedAdd", "-", "-", "SVM,", "FUI,", EShLangPSCS, false }, + { "InterlockedAnd", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedAnd", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedCompareExchange", "-", "-", "SVM,,,>", "UI,,,", EShLangPSCS, false }, + { "InterlockedCompareStore", "-", "-", "SVM,,", "UI,,", EShLangPSCS, false }, + { "InterlockedExchange", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMax", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMax", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedMin", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMin", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedOr", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedOr", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedXor", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedXor", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "isfinite", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "isinf", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "isnan", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "ldexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "length", "S", "F", "SV", "F", EShLangAll, false }, + { "lerp", nullptr, nullptr, "VM,,", "F,,", EShLangAll, false }, + { "lerp", nullptr, nullptr, "SVM,,S", "F,,", EShLangAll, false }, + { "lit", "V4", "F", "S,,", "F,,", EShLangAll, false }, + { "log", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "log10", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "log2", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "mad", nullptr, nullptr, "SVM,,", "DFUI,,", EShLangAll, false }, + { "max", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false }, + { "min", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false }, + { "modf", nullptr, nullptr, "SVM,>", "FIU,", EShLangAll, false }, + { "msad4", "V4", "U", "S,V2,V4", "U,,", EShLangAll, false }, + { "mul", "S", nullptr, "S,S", "FI,", EShLangAll, false }, + { "mul", "V", nullptr, "S,V", "FI,", EShLangAll, false }, + { "mul", "M", nullptr, "S,M", "FI,", EShLangAll, false }, + { "mul", "V", nullptr, "V,S", "FI,", EShLangAll, false }, + { "mul", "S", nullptr, "V,V", "FI,", EShLangAll, false }, + { "mul", "M", nullptr, "M,S", "FI,", EShLangAll, false }, + // mat*mat form of mul is handled in createMatTimesMat() + { "noise", "S", "F", "V", "F", EShLangPS, false }, + { "normalize", nullptr, nullptr, "V", "F", EShLangAll, false }, + { "pow", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "printf", nullptr, nullptr, "-", "-", EShLangAll, false }, + { "Process2DQuadTessFactorsAvg", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "Process2DQuadTessFactorsMax", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "Process2DQuadTessFactorsMin", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessIsolineTessFactors", "-", "-", "S,,>,>", "F,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsAvg", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsMax", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsMin", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsAvg", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsMax", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsMin", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "radians", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "rcp", nullptr, nullptr, "SVM", "FD", EShLangAll, false }, + { "reflect", nullptr, nullptr, "V,", "F,", EShLangAll, false }, + { "refract", nullptr, nullptr, "V,V,S", "F,,", EShLangAll, false }, + { "reversebits", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "round", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "rsqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "saturate", nullptr, nullptr , "SVM", "F", EShLangAll, false }, + { "sign", nullptr, nullptr, "SVM", "FI", EShLangAll, false }, + { "sin", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "sincos", "-", "-", "SVM,>,>", "F,,", EShLangAll, false }, + { "sinh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "smoothstep", nullptr, nullptr, "SVM,,", "F,,", EShLangAll, false }, + { "sqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "step", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "tan", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "tanh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "tex1D", "V4", "F", "S,S", "S,F", EShLangPS, false }, + { "tex1D", "V4", "F", "S,S,V1,", "S,F,,", EShLangPS, false }, + { "tex1Dbias", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex1Dgrad", "V4", "F", "S,,,", "S,F,,", EShLangPS, false }, + { "tex1Dlod", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex1Dproj", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex2D", "V4", "F", "V2,", "S,F", EShLangPS, false }, + { "tex2D", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false }, + { "tex2Dbias", "V4", "F", "V2,V4", "S,F", EShLangPS, false }, + { "tex2Dgrad", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false }, + { "tex2Dlod", "V4", "F", "V2,V4", "S,F", EShLangAll, false }, + { "tex2Dproj", "V4", "F", "V2,V4", "S,F", EShLangPS, false }, + { "tex3D", "V4", "F", "V3,", "S,F", EShLangPS, false }, + { "tex3D", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false }, + { "tex3Dbias", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "tex3Dgrad", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false }, + { "tex3Dlod", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "tex3Dproj", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "texCUBE", "V4", "F", "V4,V3", "S,F", EShLangPS, false }, + { "texCUBE", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false }, + { "texCUBEbias", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "texCUBEgrad", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false }, + { "texCUBElod", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "texCUBEproj", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "transpose", "^M", nullptr, "M", "FUIB", EShLangAll, false }, + { "trunc", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + + // Texture object methods. Return type can be overridden by shader declaration. + // !O = no offset, O = offset + { "Sample", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangPS, true }, + { "Sample", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangPS, true }, + + { "SampleBias", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,F", EShLangPS, true }, + { "SampleBias", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,F,I", EShLangPS, true }, + + // TODO: FXC accepts int/uint samplers here. unclear what that means. + { "SampleCmp", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,", EShLangPS, true }, + { "SampleCmp", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,,I", EShLangPS, true }, + + // TODO: FXC accepts int/uint samplers here. unclear what that means. + { "SampleCmpLevelZero", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,F", EShLangPS, true }, + { "SampleCmpLevelZero", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,F,I", EShLangPS, true }, + + { "SampleGrad", /*!O*/ "V4", nullptr, "%@,S,V,,", "FIU,S,F,,", EShLangAll, true }, + { "SampleGrad", /* O*/ "V4", nullptr, "%@,S,V,,,", "FIU,S,F,,,I", EShLangAll, true }, + + { "SampleLevel", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,", EShLangAll, true }, + { "SampleLevel", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,,I", EShLangAll, true }, + + { "Load", /*!O*/ "V4", nullptr, "%@,V", "FIU,I", EShLangAll, true }, + { "Load", /* O*/ "V4", nullptr, "%@,V,V", "FIU,I,I", EShLangAll, true }, + { "Load", /* +sampleidex*/ "V4", nullptr, "$&,V,S", "FIU,I,I", EShLangAll, true }, + { "Load", /* +samplindex, offset*/ "V4", nullptr, "$&,V,S,V", "FIU,I,I,I", EShLangAll, true }, + + // RWTexture loads + { "Load", "V4", nullptr, "!#,V", "FIU,I", EShLangAll, true }, + // (RW)Buffer loads + { "Load", "V4", nullptr, "~*1,V", "FIU,I", EShLangAll, true }, + + { "Gather", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "Gather", /* O*/ "V4", nullptr, "%@,S,V,V", "FIU,S,F,I", EShLangAll, true }, + + { "CalculateLevelOfDetail", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true }, + { "CalculateLevelOfDetailUnclamped", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true }, + + { "GetSamplePosition", "V2", "F", "$&2,S", "FUI,I", EShLangVSPSGS,true }, + + // + // UINT Width + // UINT MipLevel, UINT Width, UINT NumberOfLevels + { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,U", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,F", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,F,", EShLangAll, true }, + + // UINT Width, UINT Elements + // UINT MipLevel, UINT Width, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height + // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels + { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Elements + // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,F,F,F", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Depth + // UINT MipLevel, UINT Width, UINT Height, UINT Depth, UINT NumberOfLevels + { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,F,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height + // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels + { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Elements + // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,F,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Samples + // UINT Width, UINT Height, UINT Elements, UINT Samples + { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true }, + + // SM5 texture methods + { "GatherRed", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherRed", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherRed", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherRed", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherRed", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherGreen", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherGreen", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherGreen", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherGreen", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherGreen", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherBlue", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherBlue", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherBlue", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherBlue", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherBlue", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherAlpha", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherAlpha", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherCmp", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmp", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmp", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmp", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmp", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpRed", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpRed", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpRed", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpRed", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpRed", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpGreen", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpGreen", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpGreen", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpGreen", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpGreen", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpBlue", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpBlue", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpBlue", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpBlue", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpBlue", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpAlpha", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpAlpha", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + // geometry methods + { "Append", "-", "-", "-", "-", EShLangGS , true }, + { "RestartStrip", "-", "-", "-", "-", EShLangGS , true }, + + // Methods for structurebuffers. TODO: wildcard type matching. + { "Load", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load2", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load3", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load4", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store2", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store3", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store4", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "GetDimensions", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedAdd", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedAnd", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedCompareExchange", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedCompareStore", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedExchange", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedMax", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "IncrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Consume", nullptr, nullptr, "-", "-", EShLangAll, true }, + + // SM 6.0 + + { "WaveIsFirstLane", "S", "B", "-", "-", EShLangPSCS, false}, + { "WaveGetLaneCount", "S", "U", "-", "-", EShLangPSCS, false}, + { "WaveGetLaneIndex", "S", "U", "-", "-", EShLangPSCS, false}, + { "WaveActiveAnyTrue", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveAllTrue", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveBallot", "V4", "U", "S", "B", EShLangPSCS, false}, + { "WaveReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false}, + { "WaveReadLaneFirst", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveAllEqual", "S", "B", "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveAllEqualBool", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveCountBits", "S", "U", "S", "B", EShLangPSCS, false}, + + { "WaveActiveSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitAnd", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitOr", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitXor", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveMin", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveMax", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixCountBits", "S", "U", "S", "B", EShLangPSCS, false}, + { "QuadReadAcrossX", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadAcrossY", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadAcrossDiagonal", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false}, + + // Methods for subpass input objects + { "SubpassLoad", "V4", nullptr, "[", "FIU", EShLangPS, true }, + { "SubpassLoad", "V4", nullptr, "],S", "FIU,I", EShLangPS, true }, + + // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet. + { nullptr, nullptr, nullptr, nullptr, nullptr, 0, false }, + }; + + // Create prototypes for the intrinsics. TODO: Avoid ranged based for until all compilers can handle it. + for (int icount = 0; hlslIntrinsics[icount].name; ++icount) { + const auto& intrinsic = hlslIntrinsics[icount]; + + for (int stage = 0; stage < EShLangCount; ++stage) { // for each stage... + if ((intrinsic.stage & (1< 0 ? std::min(dim0, 3) : dim0; + + s.append(arg > 0 ? ", ": ""); // comma separator if needed + + const char* orderBegin = nthArgOrder; + nthArgOrder = IoParam(s, nthArgOrder); + + // Comma means use the previous argument order and type. + HandleRepeatArg(nthArgOrder, prevArgOrder, orderBegin); + HandleRepeatArg(nthArgType, prevArgType, nthArgType); + + // In case the repeated arg has its own I/O marker + nthArgOrder = IoParam(s, nthArgOrder); + + // arrayed textures have one extra coordinate dimension, except for + // the CalculateLevelOfDetail family. + if (isArrayed && arg == coordArg && !NoArrayCoord(intrinsic.name)) + argDim0++; + + // Some texture methods use an addition arg dimension to hold mip + if (arg == coordArg && mipInCoord) + argDim0++; + + // For textures, the 1D case isn't a 1-vector, but a scalar. + if (isTexture && argDim0 == 1 && arg > 0 && *nthArgOrder == 'V') + nthArgOrder = "S"; + + AppendTypeName(s, nthArgOrder, nthArgType, argDim0, dim1); // Add arguments + } + + s.append(");\n"); // close paren and trailing semicolon + } // dim 1 loop + } // dim 0 loop + } // arg type loop + + // skip over special characters + if (isTexture && isalpha(argOrder[1])) + ++argOrder; + if (isdigit(argOrder[1])) + ++argOrder; + } // arg order loop + + if (intrinsic.stage == EShLangAll) // common builtins are only added once. + break; + } + } + + createMatTimesMat(); // handle this case separately, for convenience + + // printf("Common:\n%s\n", getCommonString().c_str()); + // printf("Frag:\n%s\n", getStageString(EShLangFragment).c_str()); + // printf("Vertex:\n%s\n", getStageString(EShLangVertex).c_str()); + // printf("Geo:\n%s\n", getStageString(EShLangGeometry).c_str()); + // printf("TessCtrl:\n%s\n", getStageString(EShLangTessControl).c_str()); + // printf("TessEval:\n%s\n", getStageString(EShLangTessEvaluation).c_str()); + // printf("Compute:\n%s\n", getStageString(EShLangCompute).c_str()); +} + +// +// Add context-dependent built-in functions and variables that are present +// for the given version and profile. All the results are put into just the +// commonBuiltins, because it is called for just a specific stage. So, +// add stage-specific entries to the commonBuiltins, and only if that stage +// was requested. +// +void TBuiltInParseablesHlsl::initialize(const TBuiltInResource& /*resources*/, int /*version*/, EProfile /*profile*/, + const SpvVersion& /*spvVersion*/, EShLanguage /*language*/) +{ +} + +// +// Finish adding/processing context-independent built-in symbols. +// 1) Programmatically add symbols that could not be added by simple text strings above. +// 2) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 3) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/, + TSymbolTable& symbolTable) +{ + // symbolTable.relateToOperator("abort", EOpAbort); + symbolTable.relateToOperator("abs", EOpAbs); + symbolTable.relateToOperator("acos", EOpAcos); + symbolTable.relateToOperator("all", EOpAll); + symbolTable.relateToOperator("AllMemoryBarrier", EOpMemoryBarrier); + symbolTable.relateToOperator("AllMemoryBarrierWithGroupSync", EOpAllMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("any", EOpAny); + symbolTable.relateToOperator("asdouble", EOpAsDouble); + symbolTable.relateToOperator("asfloat", EOpIntBitsToFloat); + symbolTable.relateToOperator("asin", EOpAsin); + symbolTable.relateToOperator("asint", EOpFloatBitsToInt); + symbolTable.relateToOperator("asuint", EOpFloatBitsToUint); + symbolTable.relateToOperator("atan", EOpAtan); + symbolTable.relateToOperator("atan2", EOpAtan); + symbolTable.relateToOperator("ceil", EOpCeil); + // symbolTable.relateToOperator("CheckAccessFullyMapped"); + symbolTable.relateToOperator("clamp", EOpClamp); + symbolTable.relateToOperator("clip", EOpClip); + symbolTable.relateToOperator("cos", EOpCos); + symbolTable.relateToOperator("cosh", EOpCosh); + symbolTable.relateToOperator("countbits", EOpBitCount); + symbolTable.relateToOperator("cross", EOpCross); + symbolTable.relateToOperator("D3DCOLORtoUBYTE4", EOpD3DCOLORtoUBYTE4); + symbolTable.relateToOperator("ddx", EOpDPdx); + symbolTable.relateToOperator("ddx_coarse", EOpDPdxCoarse); + symbolTable.relateToOperator("ddx_fine", EOpDPdxFine); + symbolTable.relateToOperator("ddy", EOpDPdy); + symbolTable.relateToOperator("ddy_coarse", EOpDPdyCoarse); + symbolTable.relateToOperator("ddy_fine", EOpDPdyFine); + symbolTable.relateToOperator("degrees", EOpDegrees); + symbolTable.relateToOperator("determinant", EOpDeterminant); + symbolTable.relateToOperator("DeviceMemoryBarrier", EOpDeviceMemoryBarrier); + symbolTable.relateToOperator("DeviceMemoryBarrierWithGroupSync", EOpDeviceMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("distance", EOpDistance); + symbolTable.relateToOperator("dot", EOpDot); + symbolTable.relateToOperator("dst", EOpDst); + // symbolTable.relateToOperator("errorf", EOpErrorf); + symbolTable.relateToOperator("EvaluateAttributeAtCentroid", EOpInterpolateAtCentroid); + symbolTable.relateToOperator("EvaluateAttributeAtSample", EOpInterpolateAtSample); + symbolTable.relateToOperator("EvaluateAttributeSnapped", EOpEvaluateAttributeSnapped); + symbolTable.relateToOperator("exp", EOpExp); + symbolTable.relateToOperator("exp2", EOpExp2); + symbolTable.relateToOperator("f16tof32", EOpF16tof32); + symbolTable.relateToOperator("f32tof16", EOpF32tof16); + symbolTable.relateToOperator("faceforward", EOpFaceForward); + symbolTable.relateToOperator("firstbithigh", EOpFindMSB); + symbolTable.relateToOperator("firstbitlow", EOpFindLSB); + symbolTable.relateToOperator("floor", EOpFloor); + symbolTable.relateToOperator("fma", EOpFma); + symbolTable.relateToOperator("fmod", EOpMod); + symbolTable.relateToOperator("frac", EOpFract); + symbolTable.relateToOperator("frexp", EOpFrexp); + symbolTable.relateToOperator("fwidth", EOpFwidth); + // symbolTable.relateToOperator("GetRenderTargetSampleCount"); + // symbolTable.relateToOperator("GetRenderTargetSamplePosition"); + symbolTable.relateToOperator("GroupMemoryBarrier", EOpWorkgroupMemoryBarrier); + symbolTable.relateToOperator("GroupMemoryBarrierWithGroupSync", EOpWorkgroupMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("InterlockedAdd", EOpInterlockedAdd); + symbolTable.relateToOperator("InterlockedAnd", EOpInterlockedAnd); + symbolTable.relateToOperator("InterlockedCompareExchange", EOpInterlockedCompareExchange); + symbolTable.relateToOperator("InterlockedCompareStore", EOpInterlockedCompareStore); + symbolTable.relateToOperator("InterlockedExchange", EOpInterlockedExchange); + symbolTable.relateToOperator("InterlockedMax", EOpInterlockedMax); + symbolTable.relateToOperator("InterlockedMin", EOpInterlockedMin); + symbolTable.relateToOperator("InterlockedOr", EOpInterlockedOr); + symbolTable.relateToOperator("InterlockedXor", EOpInterlockedXor); + symbolTable.relateToOperator("isfinite", EOpIsFinite); + symbolTable.relateToOperator("isinf", EOpIsInf); + symbolTable.relateToOperator("isnan", EOpIsNan); + symbolTable.relateToOperator("ldexp", EOpLdexp); + symbolTable.relateToOperator("length", EOpLength); + symbolTable.relateToOperator("lerp", EOpMix); + symbolTable.relateToOperator("lit", EOpLit); + symbolTable.relateToOperator("log", EOpLog); + symbolTable.relateToOperator("log10", EOpLog10); + symbolTable.relateToOperator("log2", EOpLog2); + symbolTable.relateToOperator("mad", EOpFma); + symbolTable.relateToOperator("max", EOpMax); + symbolTable.relateToOperator("min", EOpMin); + symbolTable.relateToOperator("modf", EOpModf); + // symbolTable.relateToOperator("msad4", EOpMsad4); + symbolTable.relateToOperator("mul", EOpGenMul); + // symbolTable.relateToOperator("noise", EOpNoise); // TODO: check return type + symbolTable.relateToOperator("normalize", EOpNormalize); + symbolTable.relateToOperator("pow", EOpPow); + symbolTable.relateToOperator("printf", EOpDebugPrintf); + // symbolTable.relateToOperator("Process2DQuadTessFactorsAvg"); + // symbolTable.relateToOperator("Process2DQuadTessFactorsMax"); + // symbolTable.relateToOperator("Process2DQuadTessFactorsMin"); + // symbolTable.relateToOperator("ProcessIsolineTessFactors"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsAvg"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsMax"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsMin"); + // symbolTable.relateToOperator("ProcessTriTessFactorsAvg"); + // symbolTable.relateToOperator("ProcessTriTessFactorsMax"); + // symbolTable.relateToOperator("ProcessTriTessFactorsMin"); + symbolTable.relateToOperator("radians", EOpRadians); + symbolTable.relateToOperator("rcp", EOpRcp); + symbolTable.relateToOperator("reflect", EOpReflect); + symbolTable.relateToOperator("refract", EOpRefract); + symbolTable.relateToOperator("reversebits", EOpBitFieldReverse); + symbolTable.relateToOperator("round", EOpRound); + symbolTable.relateToOperator("rsqrt", EOpInverseSqrt); + symbolTable.relateToOperator("saturate", EOpSaturate); + symbolTable.relateToOperator("sign", EOpSign); + symbolTable.relateToOperator("sin", EOpSin); + symbolTable.relateToOperator("sincos", EOpSinCos); + symbolTable.relateToOperator("sinh", EOpSinh); + symbolTable.relateToOperator("smoothstep", EOpSmoothStep); + symbolTable.relateToOperator("sqrt", EOpSqrt); + symbolTable.relateToOperator("step", EOpStep); + symbolTable.relateToOperator("tan", EOpTan); + symbolTable.relateToOperator("tanh", EOpTanh); + symbolTable.relateToOperator("tex1D", EOpTexture); + symbolTable.relateToOperator("tex1Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex1Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex1Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex1Dproj", EOpTextureProj); + symbolTable.relateToOperator("tex2D", EOpTexture); + symbolTable.relateToOperator("tex2Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex2Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex2Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex2Dproj", EOpTextureProj); + symbolTable.relateToOperator("tex3D", EOpTexture); + symbolTable.relateToOperator("tex3Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex3Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex3Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex3Dproj", EOpTextureProj); + symbolTable.relateToOperator("texCUBE", EOpTexture); + symbolTable.relateToOperator("texCUBEbias", EOpTextureBias); + symbolTable.relateToOperator("texCUBEgrad", EOpTextureGrad); + symbolTable.relateToOperator("texCUBElod", EOpTextureLod); + symbolTable.relateToOperator("texCUBEproj", EOpTextureProj); + symbolTable.relateToOperator("transpose", EOpTranspose); + symbolTable.relateToOperator("trunc", EOpTrunc); + + // Texture methods + symbolTable.relateToOperator(BUILTIN_PREFIX "Sample", EOpMethodSample); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleBias", EOpMethodSampleBias); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmp", EOpMethodSampleCmp); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmpLevelZero", EOpMethodSampleCmpLevelZero); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleGrad", EOpMethodSampleGrad); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleLevel", EOpMethodSampleLevel); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load", EOpMethodLoad); + symbolTable.relateToOperator(BUILTIN_PREFIX "GetDimensions", EOpMethodGetDimensions); + symbolTable.relateToOperator(BUILTIN_PREFIX "GetSamplePosition", EOpMethodGetSamplePosition); + symbolTable.relateToOperator(BUILTIN_PREFIX "Gather", EOpMethodGather); + symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetail", EOpMethodCalculateLevelOfDetail); + symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetailUnclamped", EOpMethodCalculateLevelOfDetailUnclamped); + + // Structure buffer methods (excluding associations already made above for texture methods w/ same name) + symbolTable.relateToOperator(BUILTIN_PREFIX "Load2", EOpMethodLoad2); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load3", EOpMethodLoad3); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load4", EOpMethodLoad4); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store", EOpMethodStore); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4); + symbolTable.relateToOperator(BUILTIN_PREFIX "IncrementCounter", EOpMethodIncrementCounter); + symbolTable.relateToOperator(BUILTIN_PREFIX "DecrementCounter", EOpMethodDecrementCounter); + // Append is also a GS method: we don't add it twice + symbolTable.relateToOperator(BUILTIN_PREFIX "Consume", EOpMethodConsume); + + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareExchange", EOpInterlockedCompareExchange); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareStore", EOpInterlockedCompareStore); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedExchange", EOpInterlockedExchange); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMax", EOpInterlockedMax); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMin", EOpInterlockedMin); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedOr", EOpInterlockedOr); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedXor", EOpInterlockedXor); + + // SM5 Texture methods + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherRed", EOpMethodGatherRed); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherGreen", EOpMethodGatherGreen); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherBlue", EOpMethodGatherBlue); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherAlpha", EOpMethodGatherAlpha); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmp", EOpMethodGatherCmpRed); // alias + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpRed", EOpMethodGatherCmpRed); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpGreen", EOpMethodGatherCmpGreen); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpBlue", EOpMethodGatherCmpBlue); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpAlpha", EOpMethodGatherCmpAlpha); + + // GS methods + symbolTable.relateToOperator(BUILTIN_PREFIX "Append", EOpMethodAppend); + symbolTable.relateToOperator(BUILTIN_PREFIX "RestartStrip", EOpMethodRestartStrip); + + // Wave ops + symbolTable.relateToOperator("WaveIsFirstLane", EOpSubgroupElect); + symbolTable.relateToOperator("WaveGetLaneCount", EOpWaveGetLaneCount); + symbolTable.relateToOperator("WaveGetLaneIndex", EOpWaveGetLaneIndex); + symbolTable.relateToOperator("WaveActiveAnyTrue", EOpSubgroupAny); + symbolTable.relateToOperator("WaveActiveAllTrue", EOpSubgroupAll); + symbolTable.relateToOperator("WaveActiveBallot", EOpSubgroupBallot); + symbolTable.relateToOperator("WaveReadLaneFirst", EOpSubgroupBroadcastFirst); + symbolTable.relateToOperator("WaveReadLaneAt", EOpSubgroupShuffle); + symbolTable.relateToOperator("WaveActiveAllEqual", EOpSubgroupAllEqual); + symbolTable.relateToOperator("WaveActiveAllEqualBool", EOpSubgroupAllEqual); + symbolTable.relateToOperator("WaveActiveCountBits", EOpWaveActiveCountBits); + symbolTable.relateToOperator("WaveActiveSum", EOpSubgroupAdd); + symbolTable.relateToOperator("WaveActiveProduct", EOpSubgroupMul); + symbolTable.relateToOperator("WaveActiveBitAnd", EOpSubgroupAnd); + symbolTable.relateToOperator("WaveActiveBitOr", EOpSubgroupOr); + symbolTable.relateToOperator("WaveActiveBitXor", EOpSubgroupXor); + symbolTable.relateToOperator("WaveActiveMin", EOpSubgroupMin); + symbolTable.relateToOperator("WaveActiveMax", EOpSubgroupMax); + symbolTable.relateToOperator("WavePrefixSum", EOpSubgroupInclusiveAdd); + symbolTable.relateToOperator("WavePrefixProduct", EOpSubgroupInclusiveMul); + symbolTable.relateToOperator("WavePrefixCountBits", EOpWavePrefixCountBits); + symbolTable.relateToOperator("QuadReadAcrossX", EOpSubgroupQuadSwapHorizontal); + symbolTable.relateToOperator("QuadReadAcrossY", EOpSubgroupQuadSwapVertical); + symbolTable.relateToOperator("QuadReadAcrossDiagonal", EOpSubgroupQuadSwapDiagonal); + symbolTable.relateToOperator("QuadReadLaneAt", EOpSubgroupQuadBroadcast); + + // Subpass input methods + symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoad", EOpSubpassLoad); + symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoadMS", EOpSubpassLoadMS); +} + +// +// Add context-dependent (resource-specific) built-ins not handled by the above. These +// would be ones that need to be programmatically added because they cannot +// be added by simple text strings. For these, also +// 1) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 2) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/, + TSymbolTable& /*symbolTable*/, const TBuiltInResource& /*resources*/) +{ +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.h new file mode 100644 index 00000000..a4aef6c3 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslParseables.h @@ -0,0 +1,64 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _HLSLPARSEABLES_INCLUDED_ +#define _HLSLPARSEABLES_INCLUDED_ + +#include "../MachineIndependent/Initialize.h" + +namespace glslang { + +// +// This is an HLSL specific derivation of TBuiltInParseables. See comment +// above TBuiltInParseables for details. +// +class TBuiltInParseablesHlsl : public TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltInParseablesHlsl(); + void initialize(int version, EProfile, const SpvVersion& spvVersion); + void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources); + +private: + void createMatTimesMat(); +}; + +} // end namespace glslang + +#endif // _HLSLPARSEABLES_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.cpp new file mode 100644 index 00000000..e45643e9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.cpp @@ -0,0 +1,1038 @@ +// +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// HLSL scanning, leveraging the scanning done by the preprocessor. +// + +#include +#include +#include + +#include "../Include/Types.h" +#include "../MachineIndependent/SymbolTable.h" +#include "../MachineIndependent/ParseHelper.h" +#include "hlslScanContext.h" +#include "hlslTokens.h" + +// preprocessor includes +#include "../MachineIndependent/preprocessor/PpContext.h" +#include "../MachineIndependent/preprocessor/PpTokens.h" + +namespace { + +struct str_eq +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + +struct str_hash +{ + size_t operator()(const char* str) const + { + // djb2 + unsigned long hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash; + } +}; + +// A single global usable by all threads, by all versions, by all languages. +const std::unordered_map KeywordMap { + {"static",glslang::EHTokStatic}, + {"const",glslang::EHTokConst}, + {"unorm",glslang::EHTokUnorm}, + {"snorm",glslang::EHTokSNorm}, + {"extern",glslang::EHTokExtern}, + {"uniform",glslang::EHTokUniform}, + {"volatile",glslang::EHTokVolatile}, + {"precise",glslang::EHTokPrecise}, + {"shared",glslang::EHTokShared}, + {"groupshared",glslang::EHTokGroupShared}, + {"linear",glslang::EHTokLinear}, + {"centroid",glslang::EHTokCentroid}, + {"nointerpolation",glslang::EHTokNointerpolation}, + {"noperspective",glslang::EHTokNoperspective}, + {"sample",glslang::EHTokSample}, + {"row_major",glslang::EHTokRowMajor}, + {"column_major",glslang::EHTokColumnMajor}, + {"packoffset",glslang::EHTokPackOffset}, + {"in",glslang::EHTokIn}, + {"out",glslang::EHTokOut}, + {"inout",glslang::EHTokInOut}, + {"layout",glslang::EHTokLayout}, + {"globallycoherent",glslang::EHTokGloballyCoherent}, + {"inline",glslang::EHTokInline}, + + {"point",glslang::EHTokPoint}, + {"line",glslang::EHTokLine}, + {"triangle",glslang::EHTokTriangle}, + {"lineadj",glslang::EHTokLineAdj}, + {"triangleadj",glslang::EHTokTriangleAdj}, + + {"PointStream",glslang::EHTokPointStream}, + {"LineStream",glslang::EHTokLineStream}, + {"TriangleStream",glslang::EHTokTriangleStream}, + + {"InputPatch",glslang::EHTokInputPatch}, + {"OutputPatch",glslang::EHTokOutputPatch}, + + {"Buffer",glslang::EHTokBuffer}, + {"vector",glslang::EHTokVector}, + {"matrix",glslang::EHTokMatrix}, + + {"void",glslang::EHTokVoid}, + {"string",glslang::EHTokString}, + {"bool",glslang::EHTokBool}, + {"int",glslang::EHTokInt}, + {"uint",glslang::EHTokUint}, + {"uint64_t",glslang::EHTokUint64}, + {"dword",glslang::EHTokDword}, + {"half",glslang::EHTokHalf}, + {"float",glslang::EHTokFloat}, + {"double",glslang::EHTokDouble}, + {"min16float",glslang::EHTokMin16float}, + {"min10float",glslang::EHTokMin10float}, + {"min16int",glslang::EHTokMin16int}, + {"min12int",glslang::EHTokMin12int}, + {"min16uint",glslang::EHTokMin16uint}, + + {"bool1",glslang::EHTokBool1}, + {"bool2",glslang::EHTokBool2}, + {"bool3",glslang::EHTokBool3}, + {"bool4",glslang::EHTokBool4}, + {"float1",glslang::EHTokFloat1}, + {"float2",glslang::EHTokFloat2}, + {"float3",glslang::EHTokFloat3}, + {"float4",glslang::EHTokFloat4}, + {"int1",glslang::EHTokInt1}, + {"int2",glslang::EHTokInt2}, + {"int3",glslang::EHTokInt3}, + {"int4",glslang::EHTokInt4}, + {"double1",glslang::EHTokDouble1}, + {"double2",glslang::EHTokDouble2}, + {"double3",glslang::EHTokDouble3}, + {"double4",glslang::EHTokDouble4}, + {"uint1",glslang::EHTokUint1}, + {"uint2",glslang::EHTokUint2}, + {"uint3",glslang::EHTokUint3}, + {"uint4",glslang::EHTokUint4}, + + {"half1",glslang::EHTokHalf1}, + {"half2",glslang::EHTokHalf2}, + {"half3",glslang::EHTokHalf3}, + {"half4",glslang::EHTokHalf4}, + {"min16float1",glslang::EHTokMin16float1}, + {"min16float2",glslang::EHTokMin16float2}, + {"min16float3",glslang::EHTokMin16float3}, + {"min16float4",glslang::EHTokMin16float4}, + {"min10float1",glslang::EHTokMin10float1}, + {"min10float2",glslang::EHTokMin10float2}, + {"min10float3",glslang::EHTokMin10float3}, + {"min10float4",glslang::EHTokMin10float4}, + {"min16int1",glslang::EHTokMin16int1}, + {"min16int2",glslang::EHTokMin16int2}, + {"min16int3",glslang::EHTokMin16int3}, + {"min16int4",glslang::EHTokMin16int4}, + {"min12int1",glslang::EHTokMin12int1}, + {"min12int2",glslang::EHTokMin12int2}, + {"min12int3",glslang::EHTokMin12int3}, + {"min12int4",glslang::EHTokMin12int4}, + {"min16uint1",glslang::EHTokMin16uint1}, + {"min16uint2",glslang::EHTokMin16uint2}, + {"min16uint3",glslang::EHTokMin16uint3}, + {"min16uint4",glslang::EHTokMin16uint4}, + + {"bool1x1",glslang::EHTokBool1x1}, + {"bool1x2",glslang::EHTokBool1x2}, + {"bool1x3",glslang::EHTokBool1x3}, + {"bool1x4",glslang::EHTokBool1x4}, + {"bool2x1",glslang::EHTokBool2x1}, + {"bool2x2",glslang::EHTokBool2x2}, + {"bool2x3",glslang::EHTokBool2x3}, + {"bool2x4",glslang::EHTokBool2x4}, + {"bool3x1",glslang::EHTokBool3x1}, + {"bool3x2",glslang::EHTokBool3x2}, + {"bool3x3",glslang::EHTokBool3x3}, + {"bool3x4",glslang::EHTokBool3x4}, + {"bool4x1",glslang::EHTokBool4x1}, + {"bool4x2",glslang::EHTokBool4x2}, + {"bool4x3",glslang::EHTokBool4x3}, + {"bool4x4",glslang::EHTokBool4x4}, + {"int1x1",glslang::EHTokInt1x1}, + {"int1x2",glslang::EHTokInt1x2}, + {"int1x3",glslang::EHTokInt1x3}, + {"int1x4",glslang::EHTokInt1x4}, + {"int2x1",glslang::EHTokInt2x1}, + {"int2x2",glslang::EHTokInt2x2}, + {"int2x3",glslang::EHTokInt2x3}, + {"int2x4",glslang::EHTokInt2x4}, + {"int3x1",glslang::EHTokInt3x1}, + {"int3x2",glslang::EHTokInt3x2}, + {"int3x3",glslang::EHTokInt3x3}, + {"int3x4",glslang::EHTokInt3x4}, + {"int4x1",glslang::EHTokInt4x1}, + {"int4x2",glslang::EHTokInt4x2}, + {"int4x3",glslang::EHTokInt4x3}, + {"int4x4",glslang::EHTokInt4x4}, + {"uint1x1",glslang::EHTokUint1x1}, + {"uint1x2",glslang::EHTokUint1x2}, + {"uint1x3",glslang::EHTokUint1x3}, + {"uint1x4",glslang::EHTokUint1x4}, + {"uint2x1",glslang::EHTokUint2x1}, + {"uint2x2",glslang::EHTokUint2x2}, + {"uint2x3",glslang::EHTokUint2x3}, + {"uint2x4",glslang::EHTokUint2x4}, + {"uint3x1",glslang::EHTokUint3x1}, + {"uint3x2",glslang::EHTokUint3x2}, + {"uint3x3",glslang::EHTokUint3x3}, + {"uint3x4",glslang::EHTokUint3x4}, + {"uint4x1",glslang::EHTokUint4x1}, + {"uint4x2",glslang::EHTokUint4x2}, + {"uint4x3",glslang::EHTokUint4x3}, + {"uint4x4",glslang::EHTokUint4x4}, + {"bool1x1",glslang::EHTokBool1x1}, + {"bool1x2",glslang::EHTokBool1x2}, + {"bool1x3",glslang::EHTokBool1x3}, + {"bool1x4",glslang::EHTokBool1x4}, + {"bool2x1",glslang::EHTokBool2x1}, + {"bool2x2",glslang::EHTokBool2x2}, + {"bool2x3",glslang::EHTokBool2x3}, + {"bool2x4",glslang::EHTokBool2x4}, + {"bool3x1",glslang::EHTokBool3x1}, + {"bool3x2",glslang::EHTokBool3x2}, + {"bool3x3",glslang::EHTokBool3x3}, + {"bool3x4",glslang::EHTokBool3x4}, + {"bool4x1",glslang::EHTokBool4x1}, + {"bool4x2",glslang::EHTokBool4x2}, + {"bool4x3",glslang::EHTokBool4x3}, + {"bool4x4",glslang::EHTokBool4x4}, + {"float1x1",glslang::EHTokFloat1x1}, + {"float1x2",glslang::EHTokFloat1x2}, + {"float1x3",glslang::EHTokFloat1x3}, + {"float1x4",glslang::EHTokFloat1x4}, + {"float2x1",glslang::EHTokFloat2x1}, + {"float2x2",glslang::EHTokFloat2x2}, + {"float2x3",glslang::EHTokFloat2x3}, + {"float2x4",glslang::EHTokFloat2x4}, + {"float3x1",glslang::EHTokFloat3x1}, + {"float3x2",glslang::EHTokFloat3x2}, + {"float3x3",glslang::EHTokFloat3x3}, + {"float3x4",glslang::EHTokFloat3x4}, + {"float4x1",glslang::EHTokFloat4x1}, + {"float4x2",glslang::EHTokFloat4x2}, + {"float4x3",glslang::EHTokFloat4x3}, + {"float4x4",glslang::EHTokFloat4x4}, + {"half1x1",glslang::EHTokHalf1x1}, + {"half1x2",glslang::EHTokHalf1x2}, + {"half1x3",glslang::EHTokHalf1x3}, + {"half1x4",glslang::EHTokHalf1x4}, + {"half2x1",glslang::EHTokHalf2x1}, + {"half2x2",glslang::EHTokHalf2x2}, + {"half2x3",glslang::EHTokHalf2x3}, + {"half2x4",glslang::EHTokHalf2x4}, + {"half3x1",glslang::EHTokHalf3x1}, + {"half3x2",glslang::EHTokHalf3x2}, + {"half3x3",glslang::EHTokHalf3x3}, + {"half3x4",glslang::EHTokHalf3x4}, + {"half4x1",glslang::EHTokHalf4x1}, + {"half4x2",glslang::EHTokHalf4x2}, + {"half4x3",glslang::EHTokHalf4x3}, + {"half4x4",glslang::EHTokHalf4x4}, + {"double1x1",glslang::EHTokDouble1x1}, + {"double1x2",glslang::EHTokDouble1x2}, + {"double1x3",glslang::EHTokDouble1x3}, + {"double1x4",glslang::EHTokDouble1x4}, + {"double2x1",glslang::EHTokDouble2x1}, + {"double2x2",glslang::EHTokDouble2x2}, + {"double2x3",glslang::EHTokDouble2x3}, + {"double2x4",glslang::EHTokDouble2x4}, + {"double3x1",glslang::EHTokDouble3x1}, + {"double3x2",glslang::EHTokDouble3x2}, + {"double3x3",glslang::EHTokDouble3x3}, + {"double3x4",glslang::EHTokDouble3x4}, + {"double4x1",glslang::EHTokDouble4x1}, + {"double4x2",glslang::EHTokDouble4x2}, + {"double4x3",glslang::EHTokDouble4x3}, + {"double4x4",glslang::EHTokDouble4x4}, + {"min16float1x1",glslang::EHTokMin16float1x1}, + {"min16float1x2",glslang::EHTokMin16float1x2}, + {"min16float1x3",glslang::EHTokMin16float1x3}, + {"min16float1x4",glslang::EHTokMin16float1x4}, + {"min16float2x1",glslang::EHTokMin16float2x1}, + {"min16float2x2",glslang::EHTokMin16float2x2}, + {"min16float2x3",glslang::EHTokMin16float2x3}, + {"min16float2x4",glslang::EHTokMin16float2x4}, + {"min16float3x1",glslang::EHTokMin16float3x1}, + {"min16float3x2",glslang::EHTokMin16float3x2}, + {"min16float3x3",glslang::EHTokMin16float3x3}, + {"min16float3x4",glslang::EHTokMin16float3x4}, + {"min16float4x1",glslang::EHTokMin16float4x1}, + {"min16float4x2",glslang::EHTokMin16float4x2}, + {"min16float4x3",glslang::EHTokMin16float4x3}, + {"min16float4x4",glslang::EHTokMin16float4x4}, + {"min10float1x1",glslang::EHTokMin10float1x1}, + {"min10float1x2",glslang::EHTokMin10float1x2}, + {"min10float1x3",glslang::EHTokMin10float1x3}, + {"min10float1x4",glslang::EHTokMin10float1x4}, + {"min10float2x1",glslang::EHTokMin10float2x1}, + {"min10float2x2",glslang::EHTokMin10float2x2}, + {"min10float2x3",glslang::EHTokMin10float2x3}, + {"min10float2x4",glslang::EHTokMin10float2x4}, + {"min10float3x1",glslang::EHTokMin10float3x1}, + {"min10float3x2",glslang::EHTokMin10float3x2}, + {"min10float3x3",glslang::EHTokMin10float3x3}, + {"min10float3x4",glslang::EHTokMin10float3x4}, + {"min10float4x1",glslang::EHTokMin10float4x1}, + {"min10float4x2",glslang::EHTokMin10float4x2}, + {"min10float4x3",glslang::EHTokMin10float4x3}, + {"min10float4x4",glslang::EHTokMin10float4x4}, + {"min16int1x1",glslang::EHTokMin16int1x1}, + {"min16int1x2",glslang::EHTokMin16int1x2}, + {"min16int1x3",glslang::EHTokMin16int1x3}, + {"min16int1x4",glslang::EHTokMin16int1x4}, + {"min16int2x1",glslang::EHTokMin16int2x1}, + {"min16int2x2",glslang::EHTokMin16int2x2}, + {"min16int2x3",glslang::EHTokMin16int2x3}, + {"min16int2x4",glslang::EHTokMin16int2x4}, + {"min16int3x1",glslang::EHTokMin16int3x1}, + {"min16int3x2",glslang::EHTokMin16int3x2}, + {"min16int3x3",glslang::EHTokMin16int3x3}, + {"min16int3x4",glslang::EHTokMin16int3x4}, + {"min16int4x1",glslang::EHTokMin16int4x1}, + {"min16int4x2",glslang::EHTokMin16int4x2}, + {"min16int4x3",glslang::EHTokMin16int4x3}, + {"min16int4x4",glslang::EHTokMin16int4x4}, + {"min12int1x1",glslang::EHTokMin12int1x1}, + {"min12int1x2",glslang::EHTokMin12int1x2}, + {"min12int1x3",glslang::EHTokMin12int1x3}, + {"min12int1x4",glslang::EHTokMin12int1x4}, + {"min12int2x1",glslang::EHTokMin12int2x1}, + {"min12int2x2",glslang::EHTokMin12int2x2}, + {"min12int2x3",glslang::EHTokMin12int2x3}, + {"min12int2x4",glslang::EHTokMin12int2x4}, + {"min12int3x1",glslang::EHTokMin12int3x1}, + {"min12int3x2",glslang::EHTokMin12int3x2}, + {"min12int3x3",glslang::EHTokMin12int3x3}, + {"min12int3x4",glslang::EHTokMin12int3x4}, + {"min12int4x1",glslang::EHTokMin12int4x1}, + {"min12int4x2",glslang::EHTokMin12int4x2}, + {"min12int4x3",glslang::EHTokMin12int4x3}, + {"min12int4x4",glslang::EHTokMin12int4x4}, + {"min16uint1x1",glslang::EHTokMin16uint1x1}, + {"min16uint1x2",glslang::EHTokMin16uint1x2}, + {"min16uint1x3",glslang::EHTokMin16uint1x3}, + {"min16uint1x4",glslang::EHTokMin16uint1x4}, + {"min16uint2x1",glslang::EHTokMin16uint2x1}, + {"min16uint2x2",glslang::EHTokMin16uint2x2}, + {"min16uint2x3",glslang::EHTokMin16uint2x3}, + {"min16uint2x4",glslang::EHTokMin16uint2x4}, + {"min16uint3x1",glslang::EHTokMin16uint3x1}, + {"min16uint3x2",glslang::EHTokMin16uint3x2}, + {"min16uint3x3",glslang::EHTokMin16uint3x3}, + {"min16uint3x4",glslang::EHTokMin16uint3x4}, + {"min16uint4x1",glslang::EHTokMin16uint4x1}, + {"min16uint4x2",glslang::EHTokMin16uint4x2}, + {"min16uint4x3",glslang::EHTokMin16uint4x3}, + {"min16uint4x4",glslang::EHTokMin16uint4x4}, + + {"sampler",glslang::EHTokSampler}, + {"sampler1D",glslang::EHTokSampler1d}, + {"sampler2D",glslang::EHTokSampler2d}, + {"sampler3D",glslang::EHTokSampler3d}, + {"samplerCUBE",glslang::EHTokSamplerCube}, + {"sampler_state",glslang::EHTokSamplerState}, + {"SamplerState",glslang::EHTokSamplerState}, + {"SamplerComparisonState",glslang::EHTokSamplerComparisonState}, + {"texture",glslang::EHTokTexture}, + {"Texture1D",glslang::EHTokTexture1d}, + {"Texture1DArray",glslang::EHTokTexture1darray}, + {"Texture2D",glslang::EHTokTexture2d}, + {"Texture2DArray",glslang::EHTokTexture2darray}, + {"Texture3D",glslang::EHTokTexture3d}, + {"TextureCube",glslang::EHTokTextureCube}, + {"TextureCubeArray",glslang::EHTokTextureCubearray}, + {"Texture2DMS",glslang::EHTokTexture2DMS}, + {"Texture2DMSArray",glslang::EHTokTexture2DMSarray}, + {"RWTexture1D",glslang::EHTokRWTexture1d}, + {"RWTexture1DArray",glslang::EHTokRWTexture1darray}, + {"RWTexture2D",glslang::EHTokRWTexture2d}, + {"RWTexture2DArray",glslang::EHTokRWTexture2darray}, + {"RWTexture3D",glslang::EHTokRWTexture3d}, + {"RWBuffer",glslang::EHTokRWBuffer}, + {"SubpassInput",glslang::EHTokSubpassInput}, + {"SubpassInputMS",glslang::EHTokSubpassInputMS}, + + {"AppendStructuredBuffer",glslang::EHTokAppendStructuredBuffer}, + {"ByteAddressBuffer",glslang::EHTokByteAddressBuffer}, + {"ConsumeStructuredBuffer",glslang::EHTokConsumeStructuredBuffer}, + {"RWByteAddressBuffer",glslang::EHTokRWByteAddressBuffer}, + {"RWStructuredBuffer",glslang::EHTokRWStructuredBuffer}, + {"StructuredBuffer",glslang::EHTokStructuredBuffer}, + {"TextureBuffer",glslang::EHTokTextureBuffer}, + + {"class",glslang::EHTokClass}, + {"struct",glslang::EHTokStruct}, + {"cbuffer",glslang::EHTokCBuffer}, + {"ConstantBuffer",glslang::EHTokConstantBuffer}, + {"tbuffer",glslang::EHTokTBuffer}, + {"typedef",glslang::EHTokTypedef}, + {"this",glslang::EHTokThis}, + {"namespace",glslang::EHTokNamespace}, + + {"true",glslang::EHTokBoolConstant}, + {"false",glslang::EHTokBoolConstant}, + + {"for",glslang::EHTokFor}, + {"do",glslang::EHTokDo}, + {"while",glslang::EHTokWhile}, + {"break",glslang::EHTokBreak}, + {"continue",glslang::EHTokContinue}, + {"if",glslang::EHTokIf}, + {"else",glslang::EHTokElse}, + {"discard",glslang::EHTokDiscard}, + {"return",glslang::EHTokReturn}, + {"switch",glslang::EHTokSwitch}, + {"case",glslang::EHTokCase}, + {"default",glslang::EHTokDefault}, +}; + +const std::unordered_set ReservedSet { + "auto", + "catch", + "char", + "const_cast", + "enum", + "explicit", + "friend", + "goto", + "long", + "mutable", + "new", + "operator", + "private", + "protected", + "public", + "reinterpret_cast", + "short", + "signed", + "sizeof", + "static_cast", + "template", + "throw", + "try", + "typename", + "union", + "unsigned", + "using", + "virtual", +}; +std::unordered_map SemanticMap { + + // in DX9, all outputs had to have a semantic associated with them, that was either consumed + // by the system or was a specific register assignment + // in DX10+, only semantics with the SV_ prefix have any meaning beyond decoration + // Fxc will only accept DX9 style semantics in compat mode + // Also, in DX10 if a SV value is present as the input of a stage, but isn't appropriate for that + // stage, it would just be ignored as it is likely there as part of an output struct from one stage + // to the next +#if 0 + (*SemanticMap)["PSIZE"] = EbvPointSize; + (*SemanticMap)["FOG"] = EbvFogFragCoord; + (*SemanticMap)["DEPTH"] = EbvFragDepth; + (*SemanticMap)["VFACE"] = EbvFace; + (*SemanticMap)["VPOS"] = EbvFragCoord; +#endif + + {"SV_POSITION",glslang::EbvPosition}, + {"SV_VERTEXID",glslang::EbvVertexIndex}, + {"SV_VIEWPORTARRAYINDEX",glslang::EbvViewportIndex}, + {"SV_TESSFACTOR",glslang::EbvTessLevelOuter}, + {"SV_SAMPLEINDEX",glslang::EbvSampleId}, + {"SV_RENDERTARGETARRAYINDEX",glslang::EbvLayer}, + {"SV_PRIMITIVEID",glslang::EbvPrimitiveId}, + {"SV_OUTPUTCONTROLPOINTID",glslang::EbvInvocationId}, + {"SV_ISFRONTFACE",glslang::EbvFace}, + {"SV_VIEWID",glslang::EbvViewIndex}, + {"SV_INSTANCEID",glslang::EbvInstanceIndex}, + {"SV_INSIDETESSFACTOR",glslang::EbvTessLevelInner}, + {"SV_GSINSTANCEID",glslang::EbvInvocationId}, + {"SV_DISPATCHTHREADID",glslang::EbvGlobalInvocationId}, + {"SV_GROUPTHREADID",glslang::EbvLocalInvocationId}, + {"SV_GROUPINDEX",glslang::EbvLocalInvocationIndex}, + {"SV_GROUPID",glslang::EbvWorkGroupId}, + {"SV_DOMAINLOCATION",glslang::EbvTessCoord}, + {"SV_DEPTH",glslang::EbvFragDepth}, + {"SV_COVERAGE",glslang::EbvSampleMask}, + {"SV_DEPTHGREATEREQUAL",glslang::EbvFragDepthGreater}, + {"SV_DEPTHLESSEQUAL",glslang::EbvFragDepthLesser}, + {"SV_STENCILREF", glslang::EbvFragStencilRef}, +}; +} + +namespace glslang { + +// Wrapper for tokenizeClass() to get everything inside the token. +void HlslScanContext::tokenize(HlslToken& token) +{ + EHlslTokenClass tokenClass = tokenizeClass(token); + token.tokenClass = tokenClass; +} + +glslang::TBuiltInVariable HlslScanContext::mapSemantic(const char* upperCase) +{ + auto it = SemanticMap.find(upperCase); + if (it != SemanticMap.end()) + return it->second; + else + return glslang::EbvNone; +} + +// +// Fill in token information for the next token, except for the token class. +// Returns the enum value of the token class of the next token found. +// Return 0 (EndOfTokens) on end of input. +// +EHlslTokenClass HlslScanContext::tokenizeClass(HlslToken& token) +{ + do { + parserToken = &token; + TPpToken ppToken; + int token = ppContext.tokenize(ppToken); + if (token == EndOfInput) + return EHTokNone; + + tokenText = ppToken.name; + loc = ppToken.loc; + parserToken->loc = loc; + switch (token) { + case ';': return EHTokSemicolon; + case ',': return EHTokComma; + case ':': return EHTokColon; + case '=': return EHTokAssign; + case '(': return EHTokLeftParen; + case ')': return EHTokRightParen; + case '.': return EHTokDot; + case '!': return EHTokBang; + case '-': return EHTokDash; + case '~': return EHTokTilde; + case '+': return EHTokPlus; + case '*': return EHTokStar; + case '/': return EHTokSlash; + case '%': return EHTokPercent; + case '<': return EHTokLeftAngle; + case '>': return EHTokRightAngle; + case '|': return EHTokVerticalBar; + case '^': return EHTokCaret; + case '&': return EHTokAmpersand; + case '?': return EHTokQuestion; + case '[': return EHTokLeftBracket; + case ']': return EHTokRightBracket; + case '{': return EHTokLeftBrace; + case '}': return EHTokRightBrace; + case '\\': + parseContext.error(loc, "illegal use of escape character", "\\", ""); + break; + + case PPAtomAddAssign: return EHTokAddAssign; + case PPAtomSubAssign: return EHTokSubAssign; + case PPAtomMulAssign: return EHTokMulAssign; + case PPAtomDivAssign: return EHTokDivAssign; + case PPAtomModAssign: return EHTokModAssign; + + case PpAtomRight: return EHTokRightOp; + case PpAtomLeft: return EHTokLeftOp; + + case PpAtomRightAssign: return EHTokRightAssign; + case PpAtomLeftAssign: return EHTokLeftAssign; + case PpAtomAndAssign: return EHTokAndAssign; + case PpAtomOrAssign: return EHTokOrAssign; + case PpAtomXorAssign: return EHTokXorAssign; + + case PpAtomAnd: return EHTokAndOp; + case PpAtomOr: return EHTokOrOp; + case PpAtomXor: return EHTokXorOp; + + case PpAtomEQ: return EHTokEqOp; + case PpAtomGE: return EHTokGeOp; + case PpAtomNE: return EHTokNeOp; + case PpAtomLE: return EHTokLeOp; + + case PpAtomDecrement: return EHTokDecOp; + case PpAtomIncrement: return EHTokIncOp; + + case PpAtomColonColon: return EHTokColonColon; + + case PpAtomConstInt: parserToken->i = ppToken.ival; return EHTokIntConstant; + case PpAtomConstUint: parserToken->i = ppToken.ival; return EHTokUintConstant; + case PpAtomConstFloat16: parserToken->d = ppToken.dval; return EHTokFloat16Constant; + case PpAtomConstFloat: parserToken->d = ppToken.dval; return EHTokFloatConstant; + case PpAtomConstDouble: parserToken->d = ppToken.dval; return EHTokDoubleConstant; + case PpAtomIdentifier: + { + EHlslTokenClass token = tokenizeIdentifier(); + return token; + } + + case PpAtomConstString: { + parserToken->string = NewPoolTString(tokenText); + return EHTokStringConstant; + } + + case EndOfInput: return EHTokNone; + + default: + if (token < PpAtomMaxSingle) { + char buf[2]; + buf[0] = (char)token; + buf[1] = 0; + parseContext.error(loc, "unexpected token", buf, ""); + } else if (tokenText[0] != 0) + parseContext.error(loc, "unexpected token", tokenText, ""); + else + parseContext.error(loc, "unexpected token", "", ""); + break; + } + } while (true); +} + +EHlslTokenClass HlslScanContext::tokenizeIdentifier() +{ + if (ReservedSet.find(tokenText) != ReservedSet.end()) + return reservedWord(); + + auto it = KeywordMap.find(tokenText); + if (it == KeywordMap.end()) { + // Should have an identifier of some sort + return identifierOrType(); + } + keyword = it->second; + + switch (keyword) { + + // qualifiers + case EHTokStatic: + case EHTokConst: + case EHTokSNorm: + case EHTokUnorm: + case EHTokExtern: + case EHTokUniform: + case EHTokVolatile: + case EHTokShared: + case EHTokGroupShared: + case EHTokLinear: + case EHTokCentroid: + case EHTokNointerpolation: + case EHTokNoperspective: + case EHTokSample: + case EHTokRowMajor: + case EHTokColumnMajor: + case EHTokPackOffset: + case EHTokIn: + case EHTokOut: + case EHTokInOut: + case EHTokPrecise: + case EHTokLayout: + case EHTokGloballyCoherent: + case EHTokInline: + return keyword; + + // primitive types + case EHTokPoint: + case EHTokLine: + case EHTokTriangle: + case EHTokLineAdj: + case EHTokTriangleAdj: + return keyword; + + // stream out types + case EHTokPointStream: + case EHTokLineStream: + case EHTokTriangleStream: + return keyword; + + // Tessellation patches + case EHTokInputPatch: + case EHTokOutputPatch: + return keyword; + + case EHTokBuffer: + case EHTokVector: + case EHTokMatrix: + return keyword; + + // scalar types + case EHTokVoid: + case EHTokString: + case EHTokBool: + case EHTokInt: + case EHTokUint: + case EHTokUint64: + case EHTokDword: + case EHTokHalf: + case EHTokFloat: + case EHTokDouble: + case EHTokMin16float: + case EHTokMin10float: + case EHTokMin16int: + case EHTokMin12int: + case EHTokMin16uint: + + // vector types + case EHTokBool1: + case EHTokBool2: + case EHTokBool3: + case EHTokBool4: + case EHTokFloat1: + case EHTokFloat2: + case EHTokFloat3: + case EHTokFloat4: + case EHTokInt1: + case EHTokInt2: + case EHTokInt3: + case EHTokInt4: + case EHTokDouble1: + case EHTokDouble2: + case EHTokDouble3: + case EHTokDouble4: + case EHTokUint1: + case EHTokUint2: + case EHTokUint3: + case EHTokUint4: + case EHTokHalf1: + case EHTokHalf2: + case EHTokHalf3: + case EHTokHalf4: + case EHTokMin16float1: + case EHTokMin16float2: + case EHTokMin16float3: + case EHTokMin16float4: + case EHTokMin10float1: + case EHTokMin10float2: + case EHTokMin10float3: + case EHTokMin10float4: + case EHTokMin16int1: + case EHTokMin16int2: + case EHTokMin16int3: + case EHTokMin16int4: + case EHTokMin12int1: + case EHTokMin12int2: + case EHTokMin12int3: + case EHTokMin12int4: + case EHTokMin16uint1: + case EHTokMin16uint2: + case EHTokMin16uint3: + case EHTokMin16uint4: + + // matrix types + case EHTokBool1x1: + case EHTokBool1x2: + case EHTokBool1x3: + case EHTokBool1x4: + case EHTokBool2x1: + case EHTokBool2x2: + case EHTokBool2x3: + case EHTokBool2x4: + case EHTokBool3x1: + case EHTokBool3x2: + case EHTokBool3x3: + case EHTokBool3x4: + case EHTokBool4x1: + case EHTokBool4x2: + case EHTokBool4x3: + case EHTokBool4x4: + case EHTokInt1x1: + case EHTokInt1x2: + case EHTokInt1x3: + case EHTokInt1x4: + case EHTokInt2x1: + case EHTokInt2x2: + case EHTokInt2x3: + case EHTokInt2x4: + case EHTokInt3x1: + case EHTokInt3x2: + case EHTokInt3x3: + case EHTokInt3x4: + case EHTokInt4x1: + case EHTokInt4x2: + case EHTokInt4x3: + case EHTokInt4x4: + case EHTokUint1x1: + case EHTokUint1x2: + case EHTokUint1x3: + case EHTokUint1x4: + case EHTokUint2x1: + case EHTokUint2x2: + case EHTokUint2x3: + case EHTokUint2x4: + case EHTokUint3x1: + case EHTokUint3x2: + case EHTokUint3x3: + case EHTokUint3x4: + case EHTokUint4x1: + case EHTokUint4x2: + case EHTokUint4x3: + case EHTokUint4x4: + case EHTokFloat1x1: + case EHTokFloat1x2: + case EHTokFloat1x3: + case EHTokFloat1x4: + case EHTokFloat2x1: + case EHTokFloat2x2: + case EHTokFloat2x3: + case EHTokFloat2x4: + case EHTokFloat3x1: + case EHTokFloat3x2: + case EHTokFloat3x3: + case EHTokFloat3x4: + case EHTokFloat4x1: + case EHTokFloat4x2: + case EHTokFloat4x3: + case EHTokFloat4x4: + case EHTokHalf1x1: + case EHTokHalf1x2: + case EHTokHalf1x3: + case EHTokHalf1x4: + case EHTokHalf2x1: + case EHTokHalf2x2: + case EHTokHalf2x3: + case EHTokHalf2x4: + case EHTokHalf3x1: + case EHTokHalf3x2: + case EHTokHalf3x3: + case EHTokHalf3x4: + case EHTokHalf4x1: + case EHTokHalf4x2: + case EHTokHalf4x3: + case EHTokHalf4x4: + case EHTokDouble1x1: + case EHTokDouble1x2: + case EHTokDouble1x3: + case EHTokDouble1x4: + case EHTokDouble2x1: + case EHTokDouble2x2: + case EHTokDouble2x3: + case EHTokDouble2x4: + case EHTokDouble3x1: + case EHTokDouble3x2: + case EHTokDouble3x3: + case EHTokDouble3x4: + case EHTokDouble4x1: + case EHTokDouble4x2: + case EHTokDouble4x3: + case EHTokDouble4x4: + case EHTokMin16float1x1: + case EHTokMin16float1x2: + case EHTokMin16float1x3: + case EHTokMin16float1x4: + case EHTokMin16float2x1: + case EHTokMin16float2x2: + case EHTokMin16float2x3: + case EHTokMin16float2x4: + case EHTokMin16float3x1: + case EHTokMin16float3x2: + case EHTokMin16float3x3: + case EHTokMin16float3x4: + case EHTokMin16float4x1: + case EHTokMin16float4x2: + case EHTokMin16float4x3: + case EHTokMin16float4x4: + case EHTokMin10float1x1: + case EHTokMin10float1x2: + case EHTokMin10float1x3: + case EHTokMin10float1x4: + case EHTokMin10float2x1: + case EHTokMin10float2x2: + case EHTokMin10float2x3: + case EHTokMin10float2x4: + case EHTokMin10float3x1: + case EHTokMin10float3x2: + case EHTokMin10float3x3: + case EHTokMin10float3x4: + case EHTokMin10float4x1: + case EHTokMin10float4x2: + case EHTokMin10float4x3: + case EHTokMin10float4x4: + case EHTokMin16int1x1: + case EHTokMin16int1x2: + case EHTokMin16int1x3: + case EHTokMin16int1x4: + case EHTokMin16int2x1: + case EHTokMin16int2x2: + case EHTokMin16int2x3: + case EHTokMin16int2x4: + case EHTokMin16int3x1: + case EHTokMin16int3x2: + case EHTokMin16int3x3: + case EHTokMin16int3x4: + case EHTokMin16int4x1: + case EHTokMin16int4x2: + case EHTokMin16int4x3: + case EHTokMin16int4x4: + case EHTokMin12int1x1: + case EHTokMin12int1x2: + case EHTokMin12int1x3: + case EHTokMin12int1x4: + case EHTokMin12int2x1: + case EHTokMin12int2x2: + case EHTokMin12int2x3: + case EHTokMin12int2x4: + case EHTokMin12int3x1: + case EHTokMin12int3x2: + case EHTokMin12int3x3: + case EHTokMin12int3x4: + case EHTokMin12int4x1: + case EHTokMin12int4x2: + case EHTokMin12int4x3: + case EHTokMin12int4x4: + case EHTokMin16uint1x1: + case EHTokMin16uint1x2: + case EHTokMin16uint1x3: + case EHTokMin16uint1x4: + case EHTokMin16uint2x1: + case EHTokMin16uint2x2: + case EHTokMin16uint2x3: + case EHTokMin16uint2x4: + case EHTokMin16uint3x1: + case EHTokMin16uint3x2: + case EHTokMin16uint3x3: + case EHTokMin16uint3x4: + case EHTokMin16uint4x1: + case EHTokMin16uint4x2: + case EHTokMin16uint4x3: + case EHTokMin16uint4x4: + return keyword; + + // texturing types + case EHTokSampler: + case EHTokSampler1d: + case EHTokSampler2d: + case EHTokSampler3d: + case EHTokSamplerCube: + case EHTokSamplerState: + case EHTokSamplerComparisonState: + case EHTokTexture: + case EHTokTexture1d: + case EHTokTexture1darray: + case EHTokTexture2d: + case EHTokTexture2darray: + case EHTokTexture3d: + case EHTokTextureCube: + case EHTokTextureCubearray: + case EHTokTexture2DMS: + case EHTokTexture2DMSarray: + case EHTokRWTexture1d: + case EHTokRWTexture1darray: + case EHTokRWTexture2d: + case EHTokRWTexture2darray: + case EHTokRWTexture3d: + case EHTokRWBuffer: + case EHTokAppendStructuredBuffer: + case EHTokByteAddressBuffer: + case EHTokConsumeStructuredBuffer: + case EHTokRWByteAddressBuffer: + case EHTokRWStructuredBuffer: + case EHTokStructuredBuffer: + case EHTokTextureBuffer: + case EHTokSubpassInput: + case EHTokSubpassInputMS: + return keyword; + + // variable, user type, ... + case EHTokClass: + case EHTokStruct: + case EHTokTypedef: + case EHTokCBuffer: + case EHTokConstantBuffer: + case EHTokTBuffer: + case EHTokThis: + case EHTokNamespace: + return keyword; + + case EHTokBoolConstant: + if (strcmp("true", tokenText) == 0) + parserToken->b = true; + else + parserToken->b = false; + return keyword; + + // control flow + case EHTokFor: + case EHTokDo: + case EHTokWhile: + case EHTokBreak: + case EHTokContinue: + case EHTokIf: + case EHTokElse: + case EHTokDiscard: + case EHTokReturn: + case EHTokCase: + case EHTokSwitch: + case EHTokDefault: + return keyword; + + default: + parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); + return EHTokNone; + } +} + +EHlslTokenClass HlslScanContext::identifierOrType() +{ + parserToken->string = NewPoolTString(tokenText); + + return EHTokIdentifier; +} + +// Give an error for use of a reserved symbol. +// However, allow built-in declarations to use reserved words, to allow +// extension support before the extension is enabled. +EHlslTokenClass HlslScanContext::reservedWord() +{ + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.error(loc, "Reserved word.", tokenText, "", ""); + + return EHTokNone; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.h new file mode 100644 index 00000000..3b191e48 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslScanContext.h @@ -0,0 +1,109 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This holds context specific to the HLSL scanner, which +// sits between the preprocessor scanner and HLSL parser. +// + +#ifndef HLSLSCANCONTEXT_H_ +#define HLSLSCANCONTEXT_H_ + +#include "../MachineIndependent/ParseHelper.h" +#include "hlslTokens.h" + +namespace glslang { + +class TPpContext; +class TPpToken; + + +// +// Everything needed to fully describe a token. +// +struct HlslToken { + HlslToken() : string(nullptr) { loc.init(); } + TSourceLoc loc; // location of token in the source + EHlslTokenClass tokenClass; // what kind of token it is + union { // what data the token holds + glslang::TString *string; // for identifiers + int i; // for literals + unsigned int u; + bool b; + double d; + }; +}; + +// +// The state of scanning and translating raw tokens to slightly richer +// semantics, like knowing if an identifier is an existing symbol, or +// user-defined type. +// +class HlslScanContext { +public: + HlslScanContext(TParseContextBase& parseContext, TPpContext& ppContext) + : parseContext(parseContext), ppContext(ppContext) { } + virtual ~HlslScanContext() { } + + static void fillInKeywordMap(); + static void deleteKeywordMap(); + + void tokenize(HlslToken&); + glslang::TBuiltInVariable mapSemantic(const char*); + +protected: + HlslScanContext(HlslScanContext&); + HlslScanContext& operator=(HlslScanContext&); + + EHlslTokenClass tokenizeClass(HlslToken&); + EHlslTokenClass tokenizeIdentifier(); + EHlslTokenClass identifierOrType(); + EHlslTokenClass reservedWord(); + EHlslTokenClass identifierOrReserved(bool reserved); + EHlslTokenClass nonreservedKeyword(int version); + + TParseContextBase& parseContext; + TPpContext& ppContext; + TSourceLoc loc; + TPpToken* ppToken; + HlslToken* parserToken; + + const char* tokenText; + EHlslTokenClass keyword; +}; + +} // end namespace glslang + +#endif // HLSLSCANCONTEXT_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.cpp b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.cpp new file mode 100644 index 00000000..5d9311cf --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.cpp @@ -0,0 +1,150 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslTokenStream.h" + +namespace glslang { + +void HlslTokenStream::pushPreToken(const HlslToken& tok) +{ + assert(preTokenStackSize < tokenBufferSize); + preTokenStack[preTokenStackSize++] = tok; +} + +HlslToken HlslTokenStream::popPreToken() +{ + assert(preTokenStackSize > 0); + + return preTokenStack[--preTokenStackSize]; +} + +void HlslTokenStream::pushTokenBuffer(const HlslToken& tok) +{ + tokenBuffer[tokenBufferPos] = tok; + tokenBufferPos = (tokenBufferPos+1) % tokenBufferSize; +} + +HlslToken HlslTokenStream::popTokenBuffer() +{ + // Back up + tokenBufferPos = (tokenBufferPos+tokenBufferSize-1) % tokenBufferSize; + + return tokenBuffer[tokenBufferPos]; +} + +// +// Make a new source of tokens, not from the source, but from an +// already pre-processed token stream. +// +// This interrupts current token processing which must be restored +// later. Some simplifying assumptions are made (and asserted). +// +void HlslTokenStream::pushTokenStream(const TVector* tokens) +{ + // not yet setup to interrupt a stream that has been receded + // and not yet reconsumed + assert(preTokenStackSize == 0); + + // save current state + currentTokenStack.push_back(token); + + // set up new token stream + tokenStreamStack.push_back(tokens); + + // start position at first token: + token = (*tokens)[0]; + tokenPosition.push_back(0); +} + +// Undo pushTokenStream(), see above +void HlslTokenStream::popTokenStream() +{ + tokenStreamStack.pop_back(); + tokenPosition.pop_back(); + token = currentTokenStack.back(); + currentTokenStack.pop_back(); +} + +// Load 'token' with the next token in the stream of tokens. +void HlslTokenStream::advanceToken() +{ + pushTokenBuffer(token); + if (preTokenStackSize > 0) + token = popPreToken(); + else { + if (tokenStreamStack.size() == 0) + scanner.tokenize(token); + else { + ++tokenPosition.back(); + if (tokenPosition.back() >= (int)tokenStreamStack.back()->size()) + token.tokenClass = EHTokNone; + else + token = (*tokenStreamStack.back())[tokenPosition.back()]; + } + } +} + +void HlslTokenStream::recedeToken() +{ + pushPreToken(token); + token = popTokenBuffer(); +} + +// Return the current token class. +EHlslTokenClass HlslTokenStream::peek() const +{ + return token.tokenClass; +} + +// Return true, without advancing to the next token, if the current token is +// the expected (passed in) token class. +bool HlslTokenStream::peekTokenClass(EHlslTokenClass tokenClass) const +{ + return peek() == tokenClass; +} + +// Return true and advance to the next token if the current token is the +// expected (passed in) token class. +bool HlslTokenStream::acceptTokenClass(EHlslTokenClass tokenClass) +{ + if (peekTokenClass(tokenClass)) { + advanceToken(); + return true; + } + + return false; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.h new file mode 100644 index 00000000..cb6c9e72 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokenStream.h @@ -0,0 +1,96 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLTOKENSTREAM_H_ +#define HLSLTOKENSTREAM_H_ + +#include "hlslScanContext.h" + +namespace glslang { + + class HlslTokenStream { + public: + explicit HlslTokenStream(HlslScanContext& scanner) + : scanner(scanner), preTokenStackSize(0), tokenBufferPos(0) { } + virtual ~HlslTokenStream() { } + + public: + void advanceToken(); + void recedeToken(); + bool acceptTokenClass(EHlslTokenClass); + EHlslTokenClass peek() const; + bool peekTokenClass(EHlslTokenClass) const; + glslang::TBuiltInVariable mapSemantic(const char* upperCase) { return scanner.mapSemantic(upperCase); } + + void pushTokenStream(const TVector* tokens); + void popTokenStream(); + + protected: + HlslToken token; // the token we are currently looking at, but have not yet accepted + + private: + HlslTokenStream(); + HlslTokenStream& operator=(const HlslTokenStream&); + + HlslScanContext& scanner; // lexical scanner, to get next token from source file + TVector*> tokenStreamStack; // for getting the next token from an existing vector of tokens + TVector tokenPosition; + TVector currentTokenStack; + + // This is the number of tokens we can recedeToken() over. + static const int tokenBufferSize = 2; + + // Previously scanned tokens, returned for future advances, + // so logically in front of the token stream. + // Is logically a stack; needs last in last out semantics. + // Currently implemented as a stack of size 2. + HlslToken preTokenStack[tokenBufferSize]; + int preTokenStackSize; + void pushPreToken(const HlslToken&); + HlslToken popPreToken(); + + // Previously scanned tokens, not yet returned for future advances, + // but available for that. + // Is logically a fifo for normal advances, and a stack for recession. + // Currently implemented with an intrinsic size of 2. + HlslToken tokenBuffer[tokenBufferSize]; + int tokenBufferPos; + void pushTokenBuffer(const HlslToken&); + HlslToken popTokenBuffer(); + }; + +} // end namespace glslang + +#endif // HLSLTOKENSTREAM_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokens.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokens.h new file mode 100644 index 00000000..a7c12990 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/hlslTokens.h @@ -0,0 +1,454 @@ +// +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef EHLSLTOKENS_H_ +#define EHLSLTOKENS_H_ + +namespace glslang { + +enum EHlslTokenClass { + EHTokNone = 0, + + // qualifiers + EHTokStatic, + EHTokConst, + EHTokSNorm, + EHTokUnorm, + EHTokExtern, + EHTokUniform, + EHTokVolatile, + EHTokPrecise, + EHTokShared, + EHTokGroupShared, + EHTokLinear, + EHTokCentroid, + EHTokNointerpolation, + EHTokNoperspective, + EHTokSample, + EHTokRowMajor, + EHTokColumnMajor, + EHTokPackOffset, + EHTokIn, + EHTokOut, + EHTokInOut, + EHTokLayout, + EHTokGloballyCoherent, + EHTokInline, + + // primitive types + EHTokPoint, + EHTokLine, + EHTokTriangle, + EHTokLineAdj, + EHTokTriangleAdj, + + // stream out types + EHTokPointStream, + EHTokLineStream, + EHTokTriangleStream, + + // Tessellation patches + EHTokInputPatch, + EHTokOutputPatch, + + // template types + EHTokBuffer, + EHTokVector, + EHTokMatrix, + + // scalar types + EHTokVoid, + EHTokString, + EHTokBool, + EHTokInt, + EHTokUint, + EHTokUint64, + EHTokDword, + EHTokHalf, + EHTokFloat, + EHTokDouble, + EHTokMin16float, + EHTokMin10float, + EHTokMin16int, + EHTokMin12int, + EHTokMin16uint, + + // vector types + EHTokBool1, + EHTokBool2, + EHTokBool3, + EHTokBool4, + EHTokFloat1, + EHTokFloat2, + EHTokFloat3, + EHTokFloat4, + EHTokInt1, + EHTokInt2, + EHTokInt3, + EHTokInt4, + EHTokDouble1, + EHTokDouble2, + EHTokDouble3, + EHTokDouble4, + EHTokUint1, + EHTokUint2, + EHTokUint3, + EHTokUint4, + EHTokHalf1, + EHTokHalf2, + EHTokHalf3, + EHTokHalf4, + EHTokMin16float1, + EHTokMin16float2, + EHTokMin16float3, + EHTokMin16float4, + EHTokMin10float1, + EHTokMin10float2, + EHTokMin10float3, + EHTokMin10float4, + EHTokMin16int1, + EHTokMin16int2, + EHTokMin16int3, + EHTokMin16int4, + EHTokMin12int1, + EHTokMin12int2, + EHTokMin12int3, + EHTokMin12int4, + EHTokMin16uint1, + EHTokMin16uint2, + EHTokMin16uint3, + EHTokMin16uint4, + + // matrix types + EHTokInt1x1, + EHTokInt1x2, + EHTokInt1x3, + EHTokInt1x4, + EHTokInt2x1, + EHTokInt2x2, + EHTokInt2x3, + EHTokInt2x4, + EHTokInt3x1, + EHTokInt3x2, + EHTokInt3x3, + EHTokInt3x4, + EHTokInt4x1, + EHTokInt4x2, + EHTokInt4x3, + EHTokInt4x4, + EHTokUint1x1, + EHTokUint1x2, + EHTokUint1x3, + EHTokUint1x4, + EHTokUint2x1, + EHTokUint2x2, + EHTokUint2x3, + EHTokUint2x4, + EHTokUint3x1, + EHTokUint3x2, + EHTokUint3x3, + EHTokUint3x4, + EHTokUint4x1, + EHTokUint4x2, + EHTokUint4x3, + EHTokUint4x4, + EHTokBool1x1, + EHTokBool1x2, + EHTokBool1x3, + EHTokBool1x4, + EHTokBool2x1, + EHTokBool2x2, + EHTokBool2x3, + EHTokBool2x4, + EHTokBool3x1, + EHTokBool3x2, + EHTokBool3x3, + EHTokBool3x4, + EHTokBool4x1, + EHTokBool4x2, + EHTokBool4x3, + EHTokBool4x4, + EHTokFloat1x1, + EHTokFloat1x2, + EHTokFloat1x3, + EHTokFloat1x4, + EHTokFloat2x1, + EHTokFloat2x2, + EHTokFloat2x3, + EHTokFloat2x4, + EHTokFloat3x1, + EHTokFloat3x2, + EHTokFloat3x3, + EHTokFloat3x4, + EHTokFloat4x1, + EHTokFloat4x2, + EHTokFloat4x3, + EHTokFloat4x4, + EHTokHalf1x1, + EHTokHalf1x2, + EHTokHalf1x3, + EHTokHalf1x4, + EHTokHalf2x1, + EHTokHalf2x2, + EHTokHalf2x3, + EHTokHalf2x4, + EHTokHalf3x1, + EHTokHalf3x2, + EHTokHalf3x3, + EHTokHalf3x4, + EHTokHalf4x1, + EHTokHalf4x2, + EHTokHalf4x3, + EHTokHalf4x4, + EHTokDouble1x1, + EHTokDouble1x2, + EHTokDouble1x3, + EHTokDouble1x4, + EHTokDouble2x1, + EHTokDouble2x2, + EHTokDouble2x3, + EHTokDouble2x4, + EHTokDouble3x1, + EHTokDouble3x2, + EHTokDouble3x3, + EHTokDouble3x4, + EHTokDouble4x1, + EHTokDouble4x2, + EHTokDouble4x3, + EHTokDouble4x4, + EHTokMin16float1x1, + EHTokMin16float1x2, + EHTokMin16float1x3, + EHTokMin16float1x4, + EHTokMin16float2x1, + EHTokMin16float2x2, + EHTokMin16float2x3, + EHTokMin16float2x4, + EHTokMin16float3x1, + EHTokMin16float3x2, + EHTokMin16float3x3, + EHTokMin16float3x4, + EHTokMin16float4x1, + EHTokMin16float4x2, + EHTokMin16float4x3, + EHTokMin16float4x4, + EHTokMin10float1x1, + EHTokMin10float1x2, + EHTokMin10float1x3, + EHTokMin10float1x4, + EHTokMin10float2x1, + EHTokMin10float2x2, + EHTokMin10float2x3, + EHTokMin10float2x4, + EHTokMin10float3x1, + EHTokMin10float3x2, + EHTokMin10float3x3, + EHTokMin10float3x4, + EHTokMin10float4x1, + EHTokMin10float4x2, + EHTokMin10float4x3, + EHTokMin10float4x4, + EHTokMin16int1x1, + EHTokMin16int1x2, + EHTokMin16int1x3, + EHTokMin16int1x4, + EHTokMin16int2x1, + EHTokMin16int2x2, + EHTokMin16int2x3, + EHTokMin16int2x4, + EHTokMin16int3x1, + EHTokMin16int3x2, + EHTokMin16int3x3, + EHTokMin16int3x4, + EHTokMin16int4x1, + EHTokMin16int4x2, + EHTokMin16int4x3, + EHTokMin16int4x4, + EHTokMin12int1x1, + EHTokMin12int1x2, + EHTokMin12int1x3, + EHTokMin12int1x4, + EHTokMin12int2x1, + EHTokMin12int2x2, + EHTokMin12int2x3, + EHTokMin12int2x4, + EHTokMin12int3x1, + EHTokMin12int3x2, + EHTokMin12int3x3, + EHTokMin12int3x4, + EHTokMin12int4x1, + EHTokMin12int4x2, + EHTokMin12int4x3, + EHTokMin12int4x4, + EHTokMin16uint1x1, + EHTokMin16uint1x2, + EHTokMin16uint1x3, + EHTokMin16uint1x4, + EHTokMin16uint2x1, + EHTokMin16uint2x2, + EHTokMin16uint2x3, + EHTokMin16uint2x4, + EHTokMin16uint3x1, + EHTokMin16uint3x2, + EHTokMin16uint3x3, + EHTokMin16uint3x4, + EHTokMin16uint4x1, + EHTokMin16uint4x2, + EHTokMin16uint4x3, + EHTokMin16uint4x4, + + // texturing types + EHTokSampler, + EHTokSampler1d, + EHTokSampler2d, + EHTokSampler3d, + EHTokSamplerCube, + EHTokSamplerState, + EHTokSamplerComparisonState, + EHTokTexture, + EHTokTexture1d, + EHTokTexture1darray, + EHTokTexture2d, + EHTokTexture2darray, + EHTokTexture3d, + EHTokTextureCube, + EHTokTextureCubearray, + EHTokTexture2DMS, + EHTokTexture2DMSarray, + EHTokRWTexture1d, + EHTokRWTexture1darray, + EHTokRWTexture2d, + EHTokRWTexture2darray, + EHTokRWTexture3d, + EHTokRWBuffer, + EHTokSubpassInput, + EHTokSubpassInputMS, + + // Structure buffer variants + EHTokAppendStructuredBuffer, + EHTokByteAddressBuffer, + EHTokConsumeStructuredBuffer, + EHTokRWByteAddressBuffer, + EHTokRWStructuredBuffer, + EHTokStructuredBuffer, + EHTokTextureBuffer, + + // variable, user type, ... + EHTokIdentifier, + EHTokClass, + EHTokStruct, + EHTokCBuffer, + EHTokTBuffer, + EHTokTypedef, + EHTokThis, + EHTokNamespace, + EHTokConstantBuffer, + + // constant + EHTokFloat16Constant, + EHTokFloatConstant, + EHTokDoubleConstant, + EHTokIntConstant, + EHTokUintConstant, + EHTokBoolConstant, + EHTokStringConstant, + + // control flow + EHTokFor, + EHTokDo, + EHTokWhile, + EHTokBreak, + EHTokContinue, + EHTokIf, + EHTokElse, + EHTokDiscard, + EHTokReturn, + EHTokSwitch, + EHTokCase, + EHTokDefault, + + // expressions + EHTokLeftOp, + EHTokRightOp, + EHTokIncOp, + EHTokDecOp, + EHTokLeOp, + EHTokGeOp, + EHTokEqOp, + EHTokNeOp, + EHTokAndOp, + EHTokOrOp, + EHTokXorOp, + EHTokAssign, + EHTokMulAssign, + EHTokDivAssign, + EHTokAddAssign, + EHTokModAssign, + EHTokLeftAssign, + EHTokRightAssign, + EHTokAndAssign, + EHTokXorAssign, + EHTokOrAssign, + EHTokSubAssign, + EHTokLeftParen, + EHTokRightParen, + EHTokLeftBracket, + EHTokRightBracket, + EHTokLeftBrace, + EHTokRightBrace, + EHTokDot, + EHTokComma, + EHTokColon, + EHTokColonColon, + EHTokSemicolon, + EHTokBang, + EHTokDash, + EHTokTilde, + EHTokPlus, + EHTokStar, + EHTokSlash, + EHTokPercent, + EHTokLeftAngle, + EHTokRightAngle, + EHTokVerticalBar, + EHTokCaret, + EHTokAmpersand, + EHTokQuestion, +}; + +} // end namespace glslang + +#endif // EHLSLTOKENS_H_ diff --git a/engine/engine/3rdlibs/glslang/glslang/HLSL/pch.h b/engine/engine/3rdlibs/glslang/glslang/HLSL/pch.h new file mode 100644 index 00000000..f51efacc --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/HLSL/pch.h @@ -0,0 +1,51 @@ +#ifndef _PCH_H +#define _PCH_H +// +// Copyright (C) 2018 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslAttributes.h" +#include "hlslGrammar.h" +#include "hlslParseHelper.h" +#include "hlslScanContext.h" + +#include "../MachineIndependent/Scan.h" +#include "../MachineIndependent/preprocessor/PpContext.h" + +#include +#include +#include +#include +#include + +#endif /* _PCH_H */ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/BaseTypes.h b/engine/engine/3rdlibs/glslang/glslang/Include/BaseTypes.h new file mode 100644 index 00000000..83eef226 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/BaseTypes.h @@ -0,0 +1,667 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _BASICTYPES_INCLUDED_ +#define _BASICTYPES_INCLUDED_ + +namespace glslang { + +// +// Basic type. Arrays, vectors, sampler details, etc., are orthogonal to this. +// +enum TBasicType { + EbtVoid, + EbtFloat, + EbtDouble, + EbtFloat16, + EbtBFloat16, + EbtInt8, + EbtUint8, + EbtInt16, + EbtUint16, + EbtInt, + EbtUint, + EbtInt64, + EbtUint64, + EbtBool, + EbtAtomicUint, + EbtSampler, + EbtStruct, + EbtBlock, + EbtAccStruct, + EbtReference, + EbtRayQuery, + EbtHitObjectNV, + EbtCoopmat, + EbtFunction, + EbtTensorLayoutNV, + EbtTensorViewNV, + EbtCoopvecNV, + // SPIR-V type defined by spirv_type + EbtSpirvType, + + // HLSL types that live only temporarily. + EbtString, + + EbtNumTypes +}; + +// +// Storage qualifiers. Should align with different kinds of storage or +// resource or GLSL storage qualifier. Expansion is deprecated. +// +// N.B.: You probably DON'T want to add anything here, but rather just add it +// to the built-in variables. See the comment above TBuiltInVariable. +// +// A new built-in variable will normally be an existing qualifier, like 'in', 'out', etc. +// DO NOT follow the design pattern of, say EvqInstanceId, etc. +// +enum TStorageQualifier { + EvqTemporary, // For temporaries (within a function), read/write + EvqGlobal, // For globals read/write + EvqConst, // User-defined constant values, will be semantically constant and constant folded + EvqVaryingIn, // pipeline input, read only, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqVaryingOut, // pipeline output, read/write, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqUniform, // read only, shared with app + EvqBuffer, // read/write, shared with app + EvqShared, // compute shader's read/write 'shared' qualifier + EvqSpirvStorageClass, // spirv_storage_class + + EvqPayload, + EvqPayloadIn, + EvqHitAttr, + EvqCallableData, + EvqCallableDataIn, + EvqHitObjectAttrNV, + + EvqtaskPayloadSharedEXT, + + // parameters + EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter + EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter + EvqInOut, + EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics + + // built-ins read by vertex shader + EvqVertexId, + EvqInstanceId, + + // built-ins written by vertex shader + EvqPosition, + EvqPointSize, + EvqClipVertex, + + // built-ins read by fragment shader + EvqFace, + EvqFragCoord, + EvqPointCoord, + + // built-ins written by fragment shader + EvqFragColor, + EvqFragDepth, + EvqFragStencil, + + EvqTileImageEXT, + + // end of list + EvqLast +}; + +// +// Subcategories of the TStorageQualifier, simply to give a direct mapping +// between built-in variable names and an numerical value (the enum). +// +// For backward compatibility, there is some redundancy between the +// TStorageQualifier and these. Existing members should both be maintained accurately. +// However, any new built-in variable (and any existing non-redundant one) +// must follow the pattern that the specific built-in is here, and only its +// general qualifier is in TStorageQualifier. +// +// Something like gl_Position, which is sometimes 'in' and sometimes 'out' +// shows up as two different built-in variables in a single stage, but +// only has a single enum in TBuiltInVariable, so both the +// TStorageQualifier and the TBuitinVariable are needed to distinguish +// between them. +// +enum TBuiltInVariable { + EbvNone, + EbvNumWorkGroups, + EbvWorkGroupSize, + EbvWorkGroupId, + EbvLocalInvocationId, + EbvGlobalInvocationId, + EbvLocalInvocationIndex, + EbvNumSubgroups, + EbvSubgroupID, + EbvSubGroupSize, + EbvSubGroupInvocation, + EbvSubGroupEqMask, + EbvSubGroupGeMask, + EbvSubGroupGtMask, + EbvSubGroupLeMask, + EbvSubGroupLtMask, + EbvSubgroupSize2, + EbvSubgroupInvocation2, + EbvSubgroupEqMask2, + EbvSubgroupGeMask2, + EbvSubgroupGtMask2, + EbvSubgroupLeMask2, + EbvSubgroupLtMask2, + EbvVertexId, + EbvInstanceId, + EbvVertexIndex, + EbvInstanceIndex, + EbvBaseVertex, + EbvBaseInstance, + EbvDrawId, + EbvPosition, + EbvPointSize, + EbvClipVertex, + EbvClipDistance, + EbvCullDistance, + EbvNormal, + EbvVertex, + EbvMultiTexCoord0, + EbvMultiTexCoord1, + EbvMultiTexCoord2, + EbvMultiTexCoord3, + EbvMultiTexCoord4, + EbvMultiTexCoord5, + EbvMultiTexCoord6, + EbvMultiTexCoord7, + EbvFrontColor, + EbvBackColor, + EbvFrontSecondaryColor, + EbvBackSecondaryColor, + EbvTexCoord, + EbvFogFragCoord, + EbvInvocationId, + EbvPrimitiveId, + EbvLayer, + EbvViewportIndex, + EbvPatchVertices, + EbvTessLevelOuter, + EbvTessLevelInner, + EbvBoundingBox, + EbvTessCoord, + EbvColor, + EbvSecondaryColor, + EbvFace, + EbvFragCoord, + EbvPointCoord, + EbvFragColor, + EbvFragData, + EbvFragDepth, + EbvFragStencilRef, + EbvSampleId, + EbvSamplePosition, + EbvSampleMask, + EbvHelperInvocation, + + EbvBaryCoordNoPersp, + EbvBaryCoordNoPerspCentroid, + EbvBaryCoordNoPerspSample, + EbvBaryCoordSmooth, + EbvBaryCoordSmoothCentroid, + EbvBaryCoordSmoothSample, + EbvBaryCoordPullModel, + + EbvViewIndex, + EbvDeviceIndex, + + EbvShadingRateKHR, + EbvPrimitiveShadingRateKHR, + + EbvFragSizeEXT, + EbvFragInvocationCountEXT, + + EbvSecondaryFragDataEXT, + EbvSecondaryFragColorEXT, + + EbvViewportMaskNV, + EbvSecondaryPositionNV, + EbvSecondaryViewportMaskNV, + EbvPositionPerViewNV, + EbvViewportMaskPerViewNV, + EbvFragFullyCoveredNV, + EbvFragmentSizeNV, + EbvInvocationsPerPixelNV, + // ray tracing + EbvLaunchId, + EbvLaunchSize, + EbvInstanceCustomIndex, + EbvGeometryIndex, + EbvWorldRayOrigin, + EbvWorldRayDirection, + EbvObjectRayOrigin, + EbvObjectRayDirection, + EbvRayTmin, + EbvRayTmax, + EbvCullMask, + EbvHitKind, + EbvObjectToWorld, + EbvObjectToWorld3x4, + EbvWorldToObject, + EbvWorldToObject3x4, + EbvIncomingRayFlags, + EbvCurrentRayTimeNV, + EbvClusterIDNV, + // barycentrics + EbvBaryCoordNV, + EbvBaryCoordNoPerspNV, + EbvBaryCoordEXT, + EbvBaryCoordNoPerspEXT, + // mesh shaders + EbvTaskCountNV, + EbvPrimitiveCountNV, + EbvPrimitiveIndicesNV, + EbvClipDistancePerViewNV, + EbvCullDistancePerViewNV, + EbvLayerPerViewNV, + EbvMeshViewCountNV, + EbvMeshViewIndicesNV, + + EbvMicroTrianglePositionNV, + EbvMicroTriangleBaryNV, + EbvHitKindFrontFacingMicroTriangleNV, + EbvHitKindBackFacingMicroTriangleNV, + + EbvHitIsSphereNV, + EbvHitIsLSSNV, + EbvHitSpherePositionNV, + EbvHitSphereRadiusNV, + EbvHitLSSPositionsNV, + EbvHitLSSRadiiNV, + + //GL_EXT_mesh_shader + EbvPrimitivePointIndicesEXT, + EbvPrimitiveLineIndicesEXT, + EbvPrimitiveTriangleIndicesEXT, + EbvCullPrimitiveEXT, + + // sm builtins + EbvWarpsPerSM, + EbvSMCount, + EbvWarpID, + EbvSMID, + + // HLSL built-ins that live only temporarily, until they get remapped + // to one of the above. + EbvFragDepthGreater, + EbvFragDepthLesser, + EbvGsOutputStream, + EbvOutputPatch, + EbvInputPatch, + + // structbuffer types + EbvAppendConsume, // no need to differentiate append and consume + EbvRWStructuredBuffer, + EbvStructuredBuffer, + EbvByteAddressBuffer, + EbvRWByteAddressBuffer, + + // ARM specific core builtins + EbvCoreCountARM, + EbvCoreIDARM, + EbvCoreMaxIDARM, + EbvWarpIDARM, + EbvWarpMaxIDARM, + + EbvPositionFetch, + + EbvLast +}; + +// In this enum, order matters; users can assume higher precision is a bigger value +// and EpqNone is 0. +enum TPrecisionQualifier { + EpqNone = 0, + EpqLow, + EpqMedium, + EpqHigh +}; + +// These will show up in error messages +__inline const char* GetStorageQualifierString(TStorageQualifier q) +{ + switch (q) { + case EvqTemporary: return "temp"; break; + case EvqGlobal: return "global"; break; + case EvqConst: return "const"; break; + case EvqConstReadOnly: return "const (read only)"; break; + case EvqSpirvStorageClass: return "spirv_storage_class"; break; + case EvqVaryingIn: return "in"; break; + case EvqVaryingOut: return "out"; break; + case EvqUniform: return "uniform"; break; + case EvqBuffer: return "buffer"; break; + case EvqShared: return "shared"; break; + case EvqIn: return "in"; break; + case EvqOut: return "out"; break; + case EvqInOut: return "inout"; break; + case EvqVertexId: return "gl_VertexId"; break; + case EvqInstanceId: return "gl_InstanceId"; break; + case EvqPosition: return "gl_Position"; break; + case EvqPointSize: return "gl_PointSize"; break; + case EvqClipVertex: return "gl_ClipVertex"; break; + case EvqFace: return "gl_FrontFacing"; break; + case EvqFragCoord: return "gl_FragCoord"; break; + case EvqPointCoord: return "gl_PointCoord"; break; + case EvqFragColor: return "fragColor"; break; + case EvqFragDepth: return "gl_FragDepth"; break; + case EvqFragStencil: return "gl_FragStencilRefARB"; break; + case EvqPayload: return "rayPayloadNV"; break; + case EvqPayloadIn: return "rayPayloadInNV"; break; + case EvqHitAttr: return "hitAttributeNV"; break; + case EvqCallableData: return "callableDataNV"; break; + case EvqCallableDataIn: return "callableDataInNV"; break; + case EvqtaskPayloadSharedEXT: return "taskPayloadSharedEXT"; break; + case EvqHitObjectAttrNV:return "hitObjectAttributeNV"; break; + default: return "unknown qualifier"; + } +} + +__inline const char* GetBuiltInVariableString(TBuiltInVariable v) +{ + switch (v) { + case EbvNone: return ""; + case EbvNumWorkGroups: return "NumWorkGroups"; + case EbvWorkGroupSize: return "WorkGroupSize"; + case EbvWorkGroupId: return "WorkGroupID"; + case EbvLocalInvocationId: return "LocalInvocationID"; + case EbvGlobalInvocationId: return "GlobalInvocationID"; + case EbvLocalInvocationIndex: return "LocalInvocationIndex"; + case EbvNumSubgroups: return "NumSubgroups"; + case EbvSubgroupID: return "SubgroupID"; + case EbvSubGroupSize: return "SubGroupSize"; + case EbvSubGroupInvocation: return "SubGroupInvocation"; + case EbvSubGroupEqMask: return "SubGroupEqMask"; + case EbvSubGroupGeMask: return "SubGroupGeMask"; + case EbvSubGroupGtMask: return "SubGroupGtMask"; + case EbvSubGroupLeMask: return "SubGroupLeMask"; + case EbvSubGroupLtMask: return "SubGroupLtMask"; + case EbvSubgroupSize2: return "SubgroupSize"; + case EbvSubgroupInvocation2: return "SubgroupInvocationID"; + case EbvSubgroupEqMask2: return "SubgroupEqMask"; + case EbvSubgroupGeMask2: return "SubgroupGeMask"; + case EbvSubgroupGtMask2: return "SubgroupGtMask"; + case EbvSubgroupLeMask2: return "SubgroupLeMask"; + case EbvSubgroupLtMask2: return "SubgroupLtMask"; + case EbvVertexId: return "VertexId"; + case EbvInstanceId: return "InstanceId"; + case EbvVertexIndex: return "VertexIndex"; + case EbvInstanceIndex: return "InstanceIndex"; + case EbvBaseVertex: return "BaseVertex"; + case EbvBaseInstance: return "BaseInstance"; + case EbvDrawId: return "DrawId"; + case EbvPosition: return "Position"; + case EbvPointSize: return "PointSize"; + case EbvClipVertex: return "ClipVertex"; + case EbvClipDistance: return "ClipDistance"; + case EbvCullDistance: return "CullDistance"; + case EbvNormal: return "Normal"; + case EbvVertex: return "Vertex"; + case EbvMultiTexCoord0: return "MultiTexCoord0"; + case EbvMultiTexCoord1: return "MultiTexCoord1"; + case EbvMultiTexCoord2: return "MultiTexCoord2"; + case EbvMultiTexCoord3: return "MultiTexCoord3"; + case EbvMultiTexCoord4: return "MultiTexCoord4"; + case EbvMultiTexCoord5: return "MultiTexCoord5"; + case EbvMultiTexCoord6: return "MultiTexCoord6"; + case EbvMultiTexCoord7: return "MultiTexCoord7"; + case EbvFrontColor: return "FrontColor"; + case EbvBackColor: return "BackColor"; + case EbvFrontSecondaryColor: return "FrontSecondaryColor"; + case EbvBackSecondaryColor: return "BackSecondaryColor"; + case EbvTexCoord: return "TexCoord"; + case EbvFogFragCoord: return "FogFragCoord"; + case EbvInvocationId: return "InvocationID"; + case EbvPrimitiveId: return "PrimitiveID"; + case EbvLayer: return "Layer"; + case EbvViewportIndex: return "ViewportIndex"; + case EbvPatchVertices: return "PatchVertices"; + case EbvTessLevelOuter: return "TessLevelOuter"; + case EbvTessLevelInner: return "TessLevelInner"; + case EbvBoundingBox: return "BoundingBox"; + case EbvTessCoord: return "TessCoord"; + case EbvColor: return "Color"; + case EbvSecondaryColor: return "SecondaryColor"; + case EbvFace: return "Face"; + case EbvFragCoord: return "FragCoord"; + case EbvPointCoord: return "PointCoord"; + case EbvFragColor: return "FragColor"; + case EbvFragData: return "FragData"; + case EbvFragDepth: return "FragDepth"; + case EbvFragStencilRef: return "FragStencilRef"; + case EbvSampleId: return "SampleId"; + case EbvSamplePosition: return "SamplePosition"; + case EbvSampleMask: return "SampleMaskIn"; + case EbvHelperInvocation: return "HelperInvocation"; + + case EbvBaryCoordNoPersp: return "BaryCoordNoPersp"; + case EbvBaryCoordNoPerspCentroid: return "BaryCoordNoPerspCentroid"; + case EbvBaryCoordNoPerspSample: return "BaryCoordNoPerspSample"; + case EbvBaryCoordSmooth: return "BaryCoordSmooth"; + case EbvBaryCoordSmoothCentroid: return "BaryCoordSmoothCentroid"; + case EbvBaryCoordSmoothSample: return "BaryCoordSmoothSample"; + case EbvBaryCoordPullModel: return "BaryCoordPullModel"; + + case EbvViewIndex: return "ViewIndex"; + case EbvDeviceIndex: return "DeviceIndex"; + + case EbvFragSizeEXT: return "FragSizeEXT"; + case EbvFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case EbvSecondaryFragDataEXT: return "SecondaryFragDataEXT"; + case EbvSecondaryFragColorEXT: return "SecondaryFragColorEXT"; + + case EbvViewportMaskNV: return "ViewportMaskNV"; + case EbvSecondaryPositionNV: return "SecondaryPositionNV"; + case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case EbvPositionPerViewNV: return "PositionPerViewNV"; + case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case EbvFragFullyCoveredNV: return "FragFullyCoveredNV"; + case EbvFragmentSizeNV: return "FragmentSizeNV"; + case EbvInvocationsPerPixelNV: return "InvocationsPerPixelNV"; + case EbvLaunchId: return "LaunchIdNV"; + case EbvLaunchSize: return "LaunchSizeNV"; + case EbvInstanceCustomIndex: return "InstanceCustomIndexNV"; + case EbvGeometryIndex: return "GeometryIndexEXT"; + case EbvWorldRayOrigin: return "WorldRayOriginNV"; + case EbvWorldRayDirection: return "WorldRayDirectionNV"; + case EbvObjectRayOrigin: return "ObjectRayOriginNV"; + case EbvObjectRayDirection: return "ObjectRayDirectionNV"; + case EbvRayTmin: return "ObjectRayTminNV"; + case EbvRayTmax: return "ObjectRayTmaxNV"; + case EbvHitKind: return "HitKindNV"; + case EbvIncomingRayFlags: return "IncomingRayFlagsNV"; + case EbvObjectToWorld: return "ObjectToWorldNV"; + case EbvWorldToObject: return "WorldToObjectNV"; + case EbvCurrentRayTimeNV: return "CurrentRayTimeNV"; + case EbvClusterIDNV: return "ClusterIDNV"; + + case EbvBaryCoordEXT: + case EbvBaryCoordNV: return "BaryCoordKHR"; + case EbvBaryCoordNoPerspEXT: + case EbvBaryCoordNoPerspNV: return "BaryCoordNoPerspKHR"; + + case EbvTaskCountNV: return "TaskCountNV"; + case EbvPrimitiveCountNV: return "PrimitiveCountNV"; + case EbvPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case EbvClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case EbvCullDistancePerViewNV: return "CullDistancePerViewNV"; + case EbvLayerPerViewNV: return "LayerPerViewNV"; + case EbvMeshViewCountNV: return "MeshViewCountNV"; + case EbvMeshViewIndicesNV: return "MeshViewIndicesNV"; + // GL_EXT_mesh_shader + case EbvPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case EbvPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case EbvPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case EbvCullPrimitiveEXT: return "CullPrimitiveEXT"; + + case EbvWarpsPerSM: return "WarpsPerSMNV"; + case EbvSMCount: return "SMCountNV"; + case EbvWarpID: return "WarpIDNV"; + case EbvSMID: return "SMIDNV"; + + case EbvShadingRateKHR: return "ShadingRateKHR"; + case EbvPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + + case EbvHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case EbvHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + + case EbvHitIsSphereNV: return "HitIsSphereNV"; + case EbvHitIsLSSNV: return "HitIsLSSNV"; + case EbvHitSpherePositionNV: return "HitSpherePositionNV"; + case EbvHitSphereRadiusNV: return "HitSphereRadiusNV"; + case EbvHitLSSPositionsNV: return "HitSpherePositionsNV"; + case EbvHitLSSRadiiNV: return "HitLSSRadiiNV"; + + default: return "unknown built-in variable"; + } +} + +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) +{ + switch (p) { + case EpqNone: return ""; break; + case EpqLow: return "lowp"; break; + case EpqMedium: return "mediump"; break; + case EpqHigh: return "highp"; break; + default: return "unknown precision qualifier"; + } +} + +__inline bool isTypeSignedInt(TBasicType type) +{ + switch (type) { + case EbtInt8: + case EbtInt16: + case EbtInt: + case EbtInt64: + return true; + default: + return false; + } +} + +__inline bool isTypeUnsignedInt(TBasicType type) +{ + switch (type) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return true; + default: + return false; + } +} + +__inline bool isTypeInt(TBasicType type) +{ + return isTypeSignedInt(type) || isTypeUnsignedInt(type); +} + +__inline bool isTypeFloat(TBasicType type) +{ + switch (type) { + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + return true; + default: + return false; + } +} + +__inline uint32_t GetNumBits(TBasicType type) +{ + switch (type) { + case EbtInt8: + case EbtUint8: + return 8; + case EbtBFloat16: + case EbtFloat16: + case EbtInt16: + case EbtUint16: + return 16; + case EbtInt: + case EbtUint: + case EbtFloat: + return 32; + case EbtDouble: + case EbtInt64: + case EbtUint64: + return 64; + default: + assert(false); + return 0; + } +} + +__inline int getTypeRank(TBasicType type) +{ + int res = -1; + switch(type) { + case EbtInt8: + case EbtUint8: + res = 0; + break; + case EbtInt16: + case EbtUint16: + res = 1; + break; + case EbtInt: + case EbtUint: + res = 2; + break; + case EbtInt64: + case EbtUint64: + res = 3; + break; + default: + assert(false); + break; + } + return res; +} + +} // end namespace glslang + +#endif // _BASICTYPES_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/Common.h b/engine/engine/3rdlibs/glslang/glslang/Include/Common.h new file mode 100644 index 00000000..0c142605 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/Common.h @@ -0,0 +1,307 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _COMMON_INCLUDED_ +#define _COMMON_INCLUDED_ + +#include +#include +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__ANDROID__) +#include +namespace std { +template +std::string to_string(const T& val) { + std::ostringstream os; + os << val; + return os.str(); +} +} +#endif + +#if defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API + #include + #ifndef snprintf + #define snprintf sprintf_s + #endif + #define safe_vsprintf(buf,max,format,args) vsnprintf_s((buf), (max), (max), (format), (args)) +#elif defined (solaris) + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#else + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#endif + +#if defined(_MSC_VER) +#define strdup _strdup +#endif + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4786) // Don't warn about too long identifiers + #pragma warning(disable : 4514) // unused inline method + #pragma warning(disable : 4201) // nameless union +#endif + +// Allow compilation to WASI which does not support threads yet. +#ifdef __wasi__ +#define DISABLE_THREAD_SUPPORT +#endif + +#include "PoolAlloc.h" + +// +// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme. +// +#define POOL_ALLOCATOR_NEW_DELETE(A) \ + void* operator new(size_t s) { return (A).allocate(s); } \ + void* operator new(size_t, void *_Where) { return (_Where); } \ + void operator delete(void*) { } \ + void operator delete(void *, void *) { } \ + void* operator new[](size_t s) { return (A).allocate(s); } \ + void* operator new[](size_t, void *_Where) { return (_Where); } \ + void operator delete[](void*) { } \ + void operator delete[](void *, void *) { } + +namespace glslang { + + // + // Pool version of string. + // + typedef pool_allocator TStringAllocator; + typedef std::basic_string , TStringAllocator> TString; + +} // end namespace glslang + +// Repackage the std::hash for use by unordered map/set with a TString key. +namespace std { + + template<> struct hash { + std::size_t operator()(const glslang::TString& s) const + { + const unsigned _FNV_offset_basis = 2166136261U; + const unsigned _FNV_prime = 16777619U; + unsigned _Val = _FNV_offset_basis; + size_t _Count = s.size(); + const char* _First = s.c_str(); + for (size_t _Next = 0; _Next < _Count; ++_Next) + { + _Val ^= (unsigned)_First[_Next]; + _Val *= _FNV_prime; + } + + return _Val; + } + }; +} + +namespace glslang { + +inline TString* NewPoolTString(const char* s) +{ + void* memory = GetThreadPoolAllocator().allocate(sizeof(TString)); + return new(memory) TString(s); +} + +template inline T* NewPoolObject(T*) +{ + return new(GetThreadPoolAllocator().allocate(sizeof(T))) T; +} + +template inline T* NewPoolObject(T, int instances) +{ + return new(GetThreadPoolAllocator().allocate(instances * sizeof(T))) T[instances]; +} + +inline bool StartsWith(TString const &str, const char *prefix) +{ + return str.compare(0, strlen(prefix), prefix) == 0; +} + +// +// Pool allocator versions of vectors, lists, and maps +// +template class TVector : public std::vector > { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + typedef typename std::vector >::size_type size_type; + TVector() : std::vector >() {} + TVector(const pool_allocator& a) : std::vector >(a) {} + TVector(size_type i) : std::vector >(i) {} + TVector(size_type i, const T& val) : std::vector >(i, val) {} +}; + +template class TList : public std::list > { +}; + +template > +class TMap : public std::map > > { +}; + +template , class PRED = std::equal_to > +class TUnorderedMap : public std::unordered_map > > { +}; + +template > +class TSet : public std::set > { +}; + +// +// Persistent string memory. Should only be used for strings that survive +// across compiles/links. +// +typedef std::basic_string TPersistString; + +// +// templatized min and max functions. +// +template T Min(const T a, const T b) { return a < b ? a : b; } +template T Max(const T a, const T b) { return a > b ? a : b; } + +// +// Create a TString object from an integer. +// +#if defined(_MSC_VER) || (defined(MINGW_HAS_SECURE_API) && MINGW_HAS_SECURE_API) +inline const TString String(const int i, const int base = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + _itoa_s(i, text, sizeof(text), base); + return text; +} +#else +inline const TString String(const int i, const int /*base*/ = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + + // we assume base 10 for all cases + snprintf(text, sizeof(text), "%d", i); + + return text; +} +#endif + +struct TSourceLoc { + void init() + { + name = nullptr; string = 0; line = 0; column = 0; + } + void init(int stringNum) { init(); string = stringNum; } + // Returns the name if it exists. Otherwise, returns the string number. + std::string getStringNameOrNum(bool quoteStringName = true) const + { + if (name != nullptr) { + TString qstr = quoteStringName ? ("\"" + *name + "\"") : *name; + std::string ret_str(qstr.c_str()); + return ret_str; + } + return std::to_string((long long)string); + } + const char* getFilename() const + { + if (name == nullptr) + return nullptr; + return name->c_str(); + } + const char* getFilenameStr() const { return name == nullptr ? "" : name->c_str(); } + TString* name; // descriptive name for this string, when a textual name is available, otherwise nullptr + int string; + int line; + int column; +}; + +class TPragmaTable : public TMap { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) +}; + +const int MaxTokenLength = 1024; + +template bool IsPow2(T powerOf2) +{ + if (powerOf2 <= 0) + return false; + + return (powerOf2 & (powerOf2 - 1)) == 0; +} + +// Round number up to a multiple of the given powerOf2, which is not +// a power, just a number that must be a power of 2. +template void RoundToPow2(T& number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + number = (number + powerOf2 - 1) & ~(powerOf2 - 1); +} + +template bool IsMultipleOfPow2(T number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + return ! (number & (powerOf2 - 1)); +} + +// Returns log2 of an integer power of 2. +// T should be integral. +template int IntLog2(T n) +{ + assert(IsPow2(n)); + int result = 0; + while ((T(1) << result) != n) { + result++; + } + return result; +} + +} // end namespace glslang + +#endif // _COMMON_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/ConstantUnion.h b/engine/engine/3rdlibs/glslang/glslang/Include/ConstantUnion.h new file mode 100644 index 00000000..da4737b6 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/ConstantUnion.h @@ -0,0 +1,938 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _CONSTANT_UNION_INCLUDED_ +#define _CONSTANT_UNION_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" + +namespace glslang { + +class TConstUnion { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnion() : iConst(0), type(EbtInt) { } + + void setI8Const(signed char i) + { + i8Const = i; + type = EbtInt8; + } + + void setU8Const(unsigned char u) + { + u8Const = u; + type = EbtUint8; + } + + void setI16Const(signed short i) + { + i16Const = i; + type = EbtInt16; + } + + void setU16Const(unsigned short u) + { + u16Const = u; + type = EbtUint16; + } + + void setIConst(int i) + { + iConst = i; + type = EbtInt; + } + + void setUConst(unsigned int u) + { + uConst = u; + type = EbtUint; + } + + void setI64Const(long long i64) + { + i64Const = i64; + type = EbtInt64; + } + + void setU64Const(unsigned long long u64) + { + u64Const = u64; + type = EbtUint64; + } + + void setDConst(double d) + { + dConst = d; + type = EbtDouble; + } + + void setBConst(bool b) + { + bConst = b; + type = EbtBool; + } + + void setSConst(const TString* s) + { + sConst = s; + type = EbtString; + } + + signed char getI8Const() const { return i8Const; } + unsigned char getU8Const() const { return u8Const; } + signed short getI16Const() const { return i16Const; } + unsigned short getU16Const() const { return u16Const; } + int getIConst() const { return iConst; } + unsigned int getUConst() const { return uConst; } + long long getI64Const() const { return i64Const; } + unsigned long long getU64Const() const { return u64Const; } + double getDConst() const { return dConst; } + bool getBConst() const { return bConst; } + const TString* getSConst() const { return sConst; } + + bool operator==(const signed char i) const + { + if (i == i8Const) + return true; + + return false; + } + + bool operator==(const unsigned char u) const + { + if (u == u8Const) + return true; + + return false; + } + + bool operator==(const signed short i) const + { + if (i == i16Const) + return true; + + return false; + } + + bool operator==(const unsigned short u) const + { + if (u == u16Const) + return true; + + return false; + } + + bool operator==(const int i) const + { + if (i == iConst) + return true; + + return false; + } + + bool operator==(const unsigned int u) const + { + if (u == uConst) + return true; + + return false; + } + + bool operator==(const long long i64) const + { + if (i64 == i64Const) + return true; + + return false; + } + + bool operator==(const unsigned long long u64) const + { + if (u64 == u64Const) + return true; + + return false; + } + + bool operator==(const double d) const + { + if (d == dConst) + return true; + + return false; + } + + bool operator==(const bool b) const + { + if (b == bConst) + return true; + + return false; + } + + bool operator==(const TConstUnion& constant) const + { + if (constant.type != type) + return false; + + switch (type) { + case EbtInt: + if (constant.iConst == iConst) + return true; + + break; + case EbtUint: + if (constant.uConst == uConst) + return true; + + break; + case EbtBool: + if (constant.bConst == bConst) + return true; + + break; + case EbtDouble: + if (constant.dConst == dConst) + return true; + + break; + + case EbtInt16: + if (constant.i16Const == i16Const) + return true; + + break; + case EbtUint16: + if (constant.u16Const == u16Const) + return true; + + break; + case EbtInt8: + if (constant.i8Const == i8Const) + return true; + + break; + case EbtUint8: + if (constant.u8Const == u8Const) + return true; + + break; + case EbtInt64: + if (constant.i64Const == i64Const) + return true; + + break; + case EbtUint64: + if (constant.u64Const == u64Const) + return true; + + break; + default: + assert(false && "Default missing"); + } + + return false; + } + + bool operator!=(const signed char i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned char u) const + { + return !operator==(u); + } + + bool operator!=(const signed short i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned short u) const + { + return !operator==(u); + } + + bool operator!=(const int i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned int u) const + { + return !operator==(u); + } + + bool operator!=(const long long i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned long long u) const + { + return !operator==(u); + } + + bool operator!=(const float f) const + { + return !operator==(f); + } + + bool operator!=(const bool b) const + { + return !operator==(b); + } + + bool operator!=(const TConstUnion& constant) const + { + return !operator==(constant); + } + + bool operator>(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt: + if (iConst > constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst > constant.uConst) + return true; + + return false; + case EbtDouble: + if (dConst > constant.dConst) + return true; + + return false; + case EbtInt8: + if (i8Const > constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const > constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const > constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const > constant.u16Const) + return true; + + return false; + case EbtInt64: + if (i64Const > constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const > constant.u64Const) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + bool operator<(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt8: + if (i8Const < constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const < constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const < constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const < constant.u16Const) + return true; + return false; + case EbtInt64: + if (i64Const < constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const < constant.u64Const) + return true; + + return false; + case EbtDouble: + if (dConst < constant.dConst) + return true; + + return false; + case EbtInt: + if (iConst < constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst < constant.uConst) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + TConstUnion operator+(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst + constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst + constant.dConst); break; + case EbtInt8: returnValue.setI8Const(i8Const + constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const + constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const + constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const + constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const + constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const + constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator-(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst - constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst - constant.dConst); break; + case EbtInt8: returnValue.setI8Const(i8Const - constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const - constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const - constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const - constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const - constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const - constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator*(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst * constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst * constant.dConst); break; + case EbtInt8: returnValue.setI8Const(i8Const * constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const * constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const * constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const * constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const * constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const * constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator%(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst % constant.uConst); break; + case EbtInt8: returnValue.setI8Const(i8Const % constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const % constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const % constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const % constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const % constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const % constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator>>(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const >> constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const >> constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const >> constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const >> constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const >> constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const >> constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const >> constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const >> constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const >> constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const >> constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const >> constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const >> constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst >> constant.uConst); break; + case EbtInt8: returnValue.setIConst(iConst >> constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst >> constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst >> constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst >> constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst >> constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst >> constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst >> constant.uConst); break; + case EbtInt8: returnValue.setUConst(uConst >> constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst >> constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst >> constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst >> constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst >> constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const >> constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const >> constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const >> constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const >> constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const >> constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const >> constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator<<(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const << constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const << constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const << constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const << constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const << constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const << constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const << constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const << constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const << constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const << constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const << constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const << constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const << constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const << constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const << constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const << constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const << constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const << constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const << constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const << constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const << constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const << constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const << constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const << constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const << constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const << constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const << constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const << constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const << constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const << constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const << constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const << constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const << constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const << constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const << constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const << constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const << constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const << constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const << constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const << constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const << constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const << constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst << constant.uConst); break; + case EbtInt8: returnValue.setIConst(iConst << constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst << constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst << constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst << constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst << constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst << constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst << constant.uConst); break; + case EbtInt8: returnValue.setUConst(uConst << constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst << constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst << constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst << constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst << constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst & constant.uConst); break; + case EbtInt8: returnValue.setI8Const(i8Const & constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const & constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const & constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const & constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const & constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const & constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator|(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst | constant.uConst); break; + case EbtInt8: returnValue.setI8Const(i8Const | constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const | constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const | constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const | constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const | constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const | constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator^(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst ^ constant.uConst); break; + case EbtInt8: returnValue.setI8Const(i8Const ^ constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const ^ constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const ^ constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const ^ constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const ^ constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const ^ constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator~() const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: returnValue.setIConst(~iConst); break; + case EbtUint: returnValue.setUConst(~uConst); break; + case EbtInt8: returnValue.setI8Const(~i8Const); break; + case EbtUint8: returnValue.setU8Const(~u8Const); break; + case EbtInt16: returnValue.setI16Const(~i16Const); break; + case EbtUint16: returnValue.setU16Const(~u16Const); break; + case EbtInt64: returnValue.setI64Const(~i64Const); break; + case EbtUint64: returnValue.setU64Const(~u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator||(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TBasicType getType() const { return type; } + +private: + union { + signed char i8Const; // used for i8vec, scalar int8s + unsigned char u8Const; // used for u8vec, scalar uint8s + signed short i16Const; // used for i16vec, scalar int16s + unsigned short u16Const; // used for u16vec, scalar uint16s + int iConst; // used for ivec, scalar ints + unsigned int uConst; // used for uvec, scalar uints + long long i64Const; // used for i64vec, scalar int64s + unsigned long long u64Const; // used for u64vec, scalar uint64s + bool bConst; // used for bvec, scalar bools + double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles + const TString* sConst; // string constant + }; + + TBasicType type; +}; + +// Encapsulate having a pointer to an array of TConstUnion, +// which only needs to be allocated if its size is going to be +// bigger than 0. +// +// One convenience is being able to use [] to go inside the array, instead +// of C++ assuming it as an array of pointers to vectors. +// +// General usage is that the size is known up front, and it is +// created once with the proper size. +// +class TConstUnionArray { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnionArray() : unionArray(nullptr) { } + virtual ~TConstUnionArray() { } + + explicit TConstUnionArray(int size) + { + if (size == 0) + unionArray = nullptr; + else + unionArray = new TConstUnionVector(size); + } + TConstUnionArray(const TConstUnionArray& a) = default; + TConstUnionArray(const TConstUnionArray& a, int start, int size) + { + unionArray = new TConstUnionVector(size); + for (int i = 0; i < size; ++i) + (*unionArray)[i] = a[start + i]; + } + + // Use this constructor for a smear operation + TConstUnionArray(int size, const TConstUnion& val) + { + unionArray = new TConstUnionVector(size, val); + } + + int size() const { return unionArray ? (int)unionArray->size() : 0; } + TConstUnion& operator[](size_t index) { return (*unionArray)[index]; } + const TConstUnion& operator[](size_t index) const { return (*unionArray)[index]; } + bool operator==(const TConstUnionArray& rhs) const + { + // this includes the case that both are unallocated + if (unionArray == rhs.unionArray) + return true; + + if (! unionArray || ! rhs.unionArray) + return false; + + return *unionArray == *rhs.unionArray; + } + bool operator!=(const TConstUnionArray& rhs) const { return ! operator==(rhs); } + + double dot(const TConstUnionArray& rhs) + { + assert(rhs.unionArray->size() == unionArray->size()); + double sum = 0.0; + + for (size_t comp = 0; comp < unionArray->size(); ++comp) + sum += (*this)[comp].getDConst() * rhs[comp].getDConst(); + + return sum; + } + + bool empty() const { return unionArray == nullptr; } + +protected: + typedef TVector TConstUnionVector; + TConstUnionVector* unionArray; +}; + +} // end namespace glslang + +#endif // _CONSTANT_UNION_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/InfoSink.h b/engine/engine/3rdlibs/glslang/glslang/Include/InfoSink.h new file mode 100644 index 00000000..26293394 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/InfoSink.h @@ -0,0 +1,168 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INFOSINK_INCLUDED_ +#define _INFOSINK_INCLUDED_ + +#include "../Include/Common.h" +#include +#include + +namespace glslang { + +// +// TPrefixType is used to centralize how info log messages start. +// See below. +// +enum TPrefixType { + EPrefixNone, + EPrefixWarning, + EPrefixError, + EPrefixInternalError, + EPrefixUnimplemented, + EPrefixNote +}; + +enum TOutputStream { + ENull = 0, + EDebugger = 0x01, + EStdOut = 0x02, + EString = 0x04, +}; +// +// Encapsulate info logs for all objects that have them. +// +// The methods are a general set of tools for getting a variety of +// messages and types inserted into the log. +// +class TInfoSinkBase { +public: + TInfoSinkBase() : outputStream(4), shaderFileName(nullptr) {} + void erase() { sink.erase(); } + TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(char c) { append(1, c); return *this; } + TInfoSinkBase& operator<<(const char* s) { append(s); return *this; } + TInfoSinkBase& operator<<(int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(unsigned int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(float n) { const int size = 40; char buf[size]; + snprintf(buf, size, (fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g", n); + append(buf); + return *this; } + TInfoSinkBase& operator+(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const char* s) { append(s); return *this; } + const char* c_str() const { return sink.c_str(); } + void prefix(TPrefixType message) { + switch(message) { + case EPrefixNone: break; + case EPrefixWarning: append("WARNING: "); break; + case EPrefixError: append("ERROR: "); break; + case EPrefixInternalError: append("INTERNAL ERROR: "); break; + case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break; + case EPrefixNote: append("NOTE: "); break; + default: append("UNKNOWN ERROR: "); break; + } + } + void location(const TSourceLoc& loc, bool absolute = false, bool displayColumn = false) { + const int maxSize = 24; + char locText[maxSize]; + if (displayColumn) { + snprintf(locText, maxSize, ":%d:%d", loc.line, loc.column); + } else { + snprintf(locText, maxSize, ":%d", loc.line); + } + + if(loc.getFilename() == nullptr && shaderFileName != nullptr && absolute) { + append(std::filesystem::absolute(shaderFileName).string()); + } else { + std::string location = loc.getStringNameOrNum(false); + if (absolute) { + append(std::filesystem::absolute(location).string()); + } else { + append(location); + } + } + + append(locText); + append(": "); + } + void message(TPrefixType message, const char* s) { + prefix(message); + append(s); + append("\n"); + } + void message(TPrefixType message, const char* s, const TSourceLoc& loc, bool absolute = false, + bool displayColumn = false) + { + prefix(message); + location(loc, absolute, displayColumn); + append(s); + append("\n"); + } + + void setOutputStream(int output = 4) + { + outputStream = output; + } + + void setShaderFileName(const char* file = nullptr) + { + shaderFileName = file; + } + +protected: + void append(const char* s); + + void append(int count, char c); + void append(const TPersistString& t); + void append(const TString& t); + + void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2) + sink.reserve(sink.capacity() + sink.capacity() / 2); } + void appendToStream(const char* s); + TPersistString sink; + int outputStream; + const char* shaderFileName; +}; + +} // end namespace glslang + +class TInfoSink { +public: + glslang::TInfoSinkBase info; + glslang::TInfoSinkBase debug; +}; + +#endif // _INFOSINK_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/InitializeGlobals.h b/engine/engine/3rdlibs/glslang/glslang/Include/InitializeGlobals.h new file mode 100644 index 00000000..b7fdd7aa --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/InitializeGlobals.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __INITIALIZE_GLOBALS_INCLUDED_ +#define __INITIALIZE_GLOBALS_INCLUDED_ + +namespace glslang { + +inline bool InitializePoolIndex() { return true; } // DEPRECATED: No need to call + +} // end namespace glslang + +#endif // __INITIALIZE_GLOBALS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/PoolAlloc.h b/engine/engine/3rdlibs/glslang/glslang/Include/PoolAlloc.h new file mode 100644 index 00000000..8a928414 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/PoolAlloc.h @@ -0,0 +1,328 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _POOLALLOC_INCLUDED_ +#define _POOLALLOC_INCLUDED_ + +#ifndef NDEBUG +# define GUARD_BLOCKS // define to enable guard block sanity checking +#endif + +// +// This header defines an allocator that can be used to efficiently +// allocate a large number of small requests for heap memory, with the +// intention that they are not individually deallocated, but rather +// collectively deallocated at one time. +// +// This simultaneously +// +// * Makes each individual allocation much more efficient; the +// typical allocation is trivial. +// * Completely avoids the cost of doing individual deallocation. +// * Saves the trouble of tracking down and plugging a large class of leaks. +// +// Individual classes can use this allocator by supplying their own +// new and delete methods. +// +// STL containers can use this allocator by using the pool_allocator +// class as the allocator (second) template argument. +// + +#include "visibility.h" + +#include +#include +#include + +namespace glslang { + +// If we are using guard blocks, we must track each individual +// allocation. If we aren't using guard blocks, these +// never get instantiated, so won't have any impact. +// + +class TAllocation { +public: + TAllocation(size_t size, unsigned char* mem, TAllocation* prev = nullptr) : + size(size), mem(mem), prevAlloc(prev) { + // Allocations are bracketed: + // [allocationHeader][initialGuardBlock][userData][finalGuardBlock] + // This would be cleaner with if (guardBlockSize)..., but that + // makes the compiler print warnings about 0 length memsets, + // even with the if() protecting them. +# ifdef GUARD_BLOCKS + memset(preGuard(), guardBlockBeginVal, guardBlockSize); + memset(data(), userDataFill, size); + memset(postGuard(), guardBlockEndVal, guardBlockSize); +# endif + } + + void check() const { + checkGuardBlock(preGuard(), guardBlockBeginVal, "before"); + checkGuardBlock(postGuard(), guardBlockEndVal, "after"); + } + + void checkAllocList() const; + + // Return total size needed to accommodate user buffer of 'size', + // plus our tracking data. + inline static size_t allocationSize(size_t size) { + return size + 2 * guardBlockSize + headerSize(); + } + + // Offset from surrounding buffer to get to user data buffer. + inline static unsigned char* offsetAllocation(unsigned char* m) { + return m + guardBlockSize + headerSize(); + } + +private: + void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const; + + // Find offsets to pre and post guard blocks, and user data buffer + unsigned char* preGuard() const { return mem + headerSize(); } + unsigned char* data() const { return preGuard() + guardBlockSize; } + unsigned char* postGuard() const { return data() + size; } + + size_t size; // size of the user data area + unsigned char* mem; // beginning of our allocation (pts to header) + TAllocation* prevAlloc; // prior allocation in the chain + + static inline constexpr unsigned char guardBlockBeginVal = 0xfb; + static inline constexpr unsigned char guardBlockEndVal = 0xfe; + static inline constexpr unsigned char userDataFill = 0xcd; + +# ifdef GUARD_BLOCKS + static inline constexpr size_t guardBlockSize = 16; +# else + static inline constexpr size_t guardBlockSize = 0; +# endif + +# ifdef GUARD_BLOCKS + inline static size_t headerSize() { return sizeof(TAllocation); } +# else + inline static size_t headerSize() { return 0; } +# endif +}; + +// +// There are several stacks. One is to track the pushing and popping +// of the user, and not yet implemented. The others are simply a +// repositories of free pages or used pages. +// +// Page stacks are linked together with a simple header at the beginning +// of each allocation obtained from the underlying OS. Multi-page allocations +// are returned to the OS. Individual page allocations are kept for future +// re-use. +// +// The "page size" used is not, nor must it match, the underlying OS +// page size. But, having it be about that size or equal to a set of +// pages is likely most optimal. +// +class TPoolAllocator { +public: + TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16); + + // + // Don't call the destructor just to free up the memory, call pop() + // + ~TPoolAllocator(); + + // + // Call push() to establish a new place to pop memory too. Does not + // have to be called to get things started. + // + void push(); + + // + // Call pop() to free all memory allocated since the last call to push(), + // or if no last call to push, frees all memory since first allocation. + // + void pop(); + + // + // Call popAll() to free all memory allocated. + // + void popAll(); + + // + // Call allocate() to actually acquire memory. Returns nullptr if no memory + // available, otherwise a properly aligned pointer to 'numBytes' of memory. + // + GLSLANG_EXPORT_FOR_TESTS + void* allocate(size_t numBytes); + + // + // There is no deallocate. The point of this class is that + // deallocation can be skipped by the user of it, as the model + // of use is to simultaneously deallocate everything at once + // by calling pop(), and to not have to solve memory leak problems. + // + +protected: + friend struct tHeader; + + struct tHeader { + tHeader(tHeader* nextPage, size_t pageCount) : +#ifdef GUARD_BLOCKS + lastAllocation(nullptr), +#endif + nextPage(nextPage), pageCount(pageCount) { } + + ~tHeader() { +#ifdef GUARD_BLOCKS + if (lastAllocation) + lastAllocation->checkAllocList(); +#endif + } + +#ifdef GUARD_BLOCKS + TAllocation* lastAllocation; +#endif + tHeader* nextPage; + size_t pageCount; + }; + + struct tAllocState { + size_t offset; + tHeader* page; + }; + typedef std::vector tAllocStack; + + // Track allocations if and only if we're using guard blocks +#ifndef GUARD_BLOCKS + void* initializeAllocation(tHeader*, unsigned char* memory, size_t) { +#else + void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) { + new(memory) TAllocation(numBytes, memory, block->lastAllocation); + block->lastAllocation = reinterpret_cast(memory); +#endif + + // This is optimized entirely away if GUARD_BLOCKS is not defined. + return TAllocation::offsetAllocation(memory); + } + + size_t pageSize; // granularity of allocation from the OS + size_t alignment; // all returned allocations will be aligned at + // this granularity, which will be a power of 2 + size_t alignmentMask; + size_t headerSkip; // amount of memory to skip to make room for the + // header (basically, size of header, rounded + // up to make it aligned + size_t currentPageOffset; // next offset in top of inUseList to allocate from + tHeader* freeList; // list of popped memory + tHeader* inUseList; // list of all memory currently being used + tAllocStack stack; // stack of where to allocate from, to partition pool + + int numCalls; // just an interesting statistic + size_t totalBytes; // just an interesting statistic +private: + TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator + TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor +}; + +// +// There could potentially be many pools with pops happening at +// different times. But a simple use is to have a global pop +// with everyone using the same global allocator. +// +GLSLANG_EXPORT_FOR_TESTS +extern TPoolAllocator& GetThreadPoolAllocator(); +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator); + +// +// This STL compatible allocator is intended to be used as the allocator +// parameter to templatized STL containers, like vector and map. +// +// It will use the pools for allocation, and not +// do any deallocation, but will still do destruction. +// +template +class pool_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T *pointer; + typedef const T *const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template + struct rebind { + typedef pool_allocator other; + }; + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pool_allocator() : allocator(GetThreadPoolAllocator()) { } + pool_allocator(TPoolAllocator& a) : allocator(a) { } + pool_allocator(const pool_allocator& p) : allocator(p.allocator) { } + + template + pool_allocator(const pool_allocator& p) : allocator(p.getAllocator()) { } + + GLSLANG_EXPORT_FOR_TESTS + pointer allocate(size_type n) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + pointer allocate(size_type n, const void*) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + + void deallocate(void*, size_type) { } + void deallocate(pointer, size_type) { } + + pointer _Charalloc(size_t n) { + return reinterpret_cast(getAllocator().allocate(n)); } + + void construct(pointer p, const T& val) { new ((void *)p) T(val); } + void destroy(pointer p) { p->T::~T(); } + + bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); } + bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + + size_type max_size() const { return static_cast(-1) / sizeof(T); } + size_type max_size(int size) const { return static_cast(-1) / size; } + + TPoolAllocator& getAllocator() const { return allocator; } + + pool_allocator select_on_container_copy_construction() const { return pool_allocator{}; } + +protected: + pool_allocator& operator=(const pool_allocator&) { return *this; } + TPoolAllocator& allocator; +}; + +} // end namespace glslang + +#endif // _POOLALLOC_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/ResourceLimits.h b/engine/engine/3rdlibs/glslang/glslang/Include/ResourceLimits.h new file mode 100644 index 00000000..b36c8d62 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/ResourceLimits.h @@ -0,0 +1,159 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _RESOURCE_LIMITS_INCLUDED_ +#define _RESOURCE_LIMITS_INCLUDED_ + +struct TLimits { + bool nonInductiveForLoops; + bool whileLoops; + bool doWhileLoops; + bool generalUniformIndexing; + bool generalAttributeMatrixVectorIndexing; + bool generalVaryingIndexing; + bool generalSamplerIndexing; + bool generalVariableIndexing; + bool generalConstantMatrixVectorIndexing; +}; + +struct TBuiltInResource { + int maxLights; + int maxClipPlanes; + int maxTextureUnits; + int maxTextureCoords; + int maxVertexAttribs; + int maxVertexUniformComponents; + int maxVaryingFloats; + int maxVertexTextureImageUnits; + int maxCombinedTextureImageUnits; + int maxTextureImageUnits; + int maxFragmentUniformComponents; + int maxDrawBuffers; + int maxVertexUniformVectors; + int maxVaryingVectors; + int maxFragmentUniformVectors; + int maxVertexOutputVectors; + int maxFragmentInputVectors; + int minProgramTexelOffset; + int maxProgramTexelOffset; + int maxClipDistances; + int maxComputeWorkGroupCountX; + int maxComputeWorkGroupCountY; + int maxComputeWorkGroupCountZ; + int maxComputeWorkGroupSizeX; + int maxComputeWorkGroupSizeY; + int maxComputeWorkGroupSizeZ; + int maxComputeUniformComponents; + int maxComputeTextureImageUnits; + int maxComputeImageUniforms; + int maxComputeAtomicCounters; + int maxComputeAtomicCounterBuffers; + int maxVaryingComponents; + int maxVertexOutputComponents; + int maxGeometryInputComponents; + int maxGeometryOutputComponents; + int maxFragmentInputComponents; + int maxImageUnits; + int maxCombinedImageUnitsAndFragmentOutputs; + int maxCombinedShaderOutputResources; + int maxImageSamples; + int maxVertexImageUniforms; + int maxTessControlImageUniforms; + int maxTessEvaluationImageUniforms; + int maxGeometryImageUniforms; + int maxFragmentImageUniforms; + int maxCombinedImageUniforms; + int maxGeometryTextureImageUnits; + int maxGeometryOutputVertices; + int maxGeometryTotalOutputComponents; + int maxGeometryUniformComponents; + int maxGeometryVaryingComponents; + int maxTessControlInputComponents; + int maxTessControlOutputComponents; + int maxTessControlTextureImageUnits; + int maxTessControlUniformComponents; + int maxTessControlTotalOutputComponents; + int maxTessEvaluationInputComponents; + int maxTessEvaluationOutputComponents; + int maxTessEvaluationTextureImageUnits; + int maxTessEvaluationUniformComponents; + int maxTessPatchComponents; + int maxPatchVertices; + int maxTessGenLevel; + int maxViewports; + int maxVertexAtomicCounters; + int maxTessControlAtomicCounters; + int maxTessEvaluationAtomicCounters; + int maxGeometryAtomicCounters; + int maxFragmentAtomicCounters; + int maxCombinedAtomicCounters; + int maxAtomicCounterBindings; + int maxVertexAtomicCounterBuffers; + int maxTessControlAtomicCounterBuffers; + int maxTessEvaluationAtomicCounterBuffers; + int maxGeometryAtomicCounterBuffers; + int maxFragmentAtomicCounterBuffers; + int maxCombinedAtomicCounterBuffers; + int maxAtomicCounterBufferSize; + int maxTransformFeedbackBuffers; + int maxTransformFeedbackInterleavedComponents; + int maxCullDistances; + int maxCombinedClipAndCullDistances; + int maxSamples; + int maxMeshOutputVerticesNV; + int maxMeshOutputPrimitivesNV; + int maxMeshWorkGroupSizeX_NV; + int maxMeshWorkGroupSizeY_NV; + int maxMeshWorkGroupSizeZ_NV; + int maxTaskWorkGroupSizeX_NV; + int maxTaskWorkGroupSizeY_NV; + int maxTaskWorkGroupSizeZ_NV; + int maxMeshViewCountNV; + int maxMeshOutputVerticesEXT; + int maxMeshOutputPrimitivesEXT; + int maxMeshWorkGroupSizeX_EXT; + int maxMeshWorkGroupSizeY_EXT; + int maxMeshWorkGroupSizeZ_EXT; + int maxTaskWorkGroupSizeX_EXT; + int maxTaskWorkGroupSizeY_EXT; + int maxTaskWorkGroupSizeZ_EXT; + int maxMeshViewCountEXT; + int maxDualSourceDrawBuffersEXT; + + TLimits limits; +}; + +#endif // _RESOURCE_LIMITS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/ShHandle.h b/engine/engine/3rdlibs/glslang/glslang/Include/ShHandle.h new file mode 100644 index 00000000..dee47c0d --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/ShHandle.h @@ -0,0 +1,176 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SHHANDLE_INCLUDED_ +#define _SHHANDLE_INCLUDED_ + +// +// Machine independent part of the compiler private objects +// sent as ShHandle to the driver. +// +// This should not be included by driver code. +// + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "../MachineIndependent/Versions.h" +#include "InfoSink.h" + +class TCompiler; +class TLinker; +class TUniformMap; + +// +// The base class used to back handles returned to the driver. +// +class TShHandleBase { +public: + TShHandleBase() { pool = new glslang::TPoolAllocator; } + virtual ~TShHandleBase() { delete pool; } + virtual TCompiler* getAsCompiler() { return nullptr; } + virtual TLinker* getAsLinker() { return nullptr; } + virtual TUniformMap* getAsUniformMap() { return nullptr; } + virtual glslang::TPoolAllocator* getPool() const { return pool; } +private: + glslang::TPoolAllocator* pool; +}; + +// +// The base class for the machine dependent linker to derive from +// for managing where uniforms live. +// +class TUniformMap : public TShHandleBase { +public: + TUniformMap() { } + virtual ~TUniformMap() { } + virtual TUniformMap* getAsUniformMap() { return this; } + virtual int getLocation(const char* name) = 0; + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink infoSink; +}; + +class TIntermNode; + +// +// The base class for the machine dependent compiler to derive from +// for managing object code from the compile. +// +class TCompiler : public TShHandleBase { +public: + TCompiler(EShLanguage l, TInfoSink& sink) : infoSink(sink) , language(l), haveValidObjectCode(false) { } + virtual ~TCompiler() { } + EShLanguage getLanguage() { return language; } + virtual TInfoSink& getInfoSink() { return infoSink; } + + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) = 0; + + virtual TCompiler* getAsCompiler() { return this; } + virtual bool linkable() { return haveValidObjectCode; } + + TInfoSink& infoSink; +protected: + TCompiler& operator=(TCompiler&); + + EShLanguage language; + bool haveValidObjectCode; +}; + +// +// Link operations are based on a list of compile results... +// +typedef glslang::TVector TCompilerList; +typedef glslang::TVector THandleList; + +// +// The base class for the machine dependent linker to derive from +// to manage the resulting executable. +// + +class TLinker : public TShHandleBase { +public: + TLinker(EShExecutable e, TInfoSink& iSink) : + infoSink(iSink), + executable(e), + haveReturnableObjectCode(false), + appAttributeBindings(nullptr), + fixedAttributeBindings(nullptr), + excludedAttributes(nullptr), + excludedCount(0), + uniformBindings(nullptr) { } + virtual TLinker* getAsLinker() { return this; } + virtual ~TLinker() { } + virtual bool link(TCompilerList&, TUniformMap*) = 0; + virtual bool link(THandleList&) { return false; } + virtual void setAppAttributeBindings(const ShBindingTable* t) { appAttributeBindings = t; } + virtual void setFixedAttributeBindings(const ShBindingTable* t) { fixedAttributeBindings = t; } + virtual void getAttributeBindings(ShBindingTable const **t) const = 0; + virtual void setExcludedAttributes(const int* attributes, int count) { excludedAttributes = attributes; excludedCount = count; } + virtual ShBindingTable* getUniformBindings() const { return uniformBindings; } + virtual const void* getObjectCode() const { return nullptr; } // a real compiler would be returning object code here + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink& infoSink; +protected: + TLinker& operator=(TLinker&); + EShExecutable executable; + bool haveReturnableObjectCode; // true when objectCode is acceptable to send to driver + + const ShBindingTable* appAttributeBindings; + const ShBindingTable* fixedAttributeBindings; + const int* excludedAttributes; + int excludedCount; + ShBindingTable* uniformBindings; // created by the linker +}; + +// +// This is the interface between the machine independent code +// and the machine dependent code. +// +// The machine dependent code should derive from the classes +// above. Then Construct*() and Delete*() will create and +// destroy the machine dependent objects, which contain the +// above machine independent information. +// +TCompiler* ConstructCompiler(EShLanguage, int); + +TShHandleBase* ConstructLinker(EShExecutable, int); +TShHandleBase* ConstructBindings(); +void DeleteLinker(TShHandleBase*); +void DeleteBindingList(TShHandleBase* bindingList); + +TUniformMap* ConstructUniformMap(); +void DeleteCompiler(TCompiler*); + +void DeleteUniformMap(TUniformMap*); + +#endif // _SHHANDLE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/SpirvIntrinsics.h b/engine/engine/3rdlibs/glslang/glslang/Include/SpirvIntrinsics.h new file mode 100644 index 00000000..0082a4d4 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/SpirvIntrinsics.h @@ -0,0 +1,140 @@ +// +// Copyright(C) 2021 Advanced Micro Devices, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +// +// GL_EXT_spirv_intrinsics +// +#include "Common.h" +#include + +namespace glslang { + +class TIntermTyped; +class TIntermConstantUnion; +class TType; + +// SPIR-V requirements +struct TSpirvRequirement { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // capability = [..] + TSet extensions; + // extension = [..] + TSet capabilities; +}; + +// SPIR-V execution modes +struct TSpirvExecutionMode { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // spirv_execution_mode + TMap> modes; + // spirv_execution_mode_id + TMap > modeIds; +}; + +// SPIR-V decorations +struct TSpirvDecorate { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // spirv_decorate + TMap > decorates; + // spirv_decorate_id + TMap> decorateIds; + // spirv_decorate_string + TMap > decorateStrings; +}; + +// SPIR-V instruction +struct TSpirvInstruction { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSpirvInstruction() { set = ""; id = -1; } + + bool operator==(const TSpirvInstruction& rhs) const { return set == rhs.set && id == rhs.id; } + bool operator!=(const TSpirvInstruction& rhs) const { return !operator==(rhs); } + + // spirv_instruction + TString set; + int id; +}; + +// SPIR-V type parameter +struct TSpirvTypeParameter { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSpirvTypeParameter(const TIntermConstantUnion* arg) { value = arg; } + TSpirvTypeParameter(const TType* arg) { value = arg; } + + const TIntermConstantUnion* getAsConstant() const + { + if (value.index() == 0) + return std::get(value); + return nullptr; + } + const TType* getAsType() const + { + if (value.index() == 1) + return std::get(value); + return nullptr; + } + + bool operator==(const TSpirvTypeParameter& rhs) const; + bool operator!=(const TSpirvTypeParameter& rhs) const { return !operator==(rhs); } + + // Parameter value: constant expression or type specifier + std::variant value; +}; + +typedef TVector TSpirvTypeParameters; + +// SPIR-V type +struct TSpirvType { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + bool operator==(const TSpirvType& rhs) const + { + return spirvInst == rhs.spirvInst && typeParams == rhs.typeParams; + } + bool operator!=(const TSpirvType& rhs) const { return !operator==(rhs); } + + // spirv_type + TSpirvInstruction spirvInst; + TSpirvTypeParameters typeParams; +}; + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/Types.h b/engine/engine/3rdlibs/glslang/glslang/Include/Types.h new file mode 100644 index 00000000..1f5abf2e --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/Types.h @@ -0,0 +1,2988 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _TYPES_INCLUDED +#define _TYPES_INCLUDED + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" +#include "../Public/ShaderLang.h" +#include "arrays.h" +#include "SpirvIntrinsics.h" + +#include + +namespace glslang { + +class TIntermAggregate; + +const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded + +const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed +inline bool IsAnonymous(const TString& name) +{ + return name.compare(0, 5, AnonymousPrefix) == 0; +} + +// +// Details within a sampler type +// +enum TSamplerDim { + EsdNone, + Esd1D, + Esd2D, + Esd3D, + EsdCube, + EsdRect, + EsdBuffer, + EsdSubpass, // goes only with non-sampled image (image is true) + EsdAttachmentEXT, + EsdNumDims +}; + +struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler + TBasicType type : 8; // type returned by sampler + TSamplerDim dim : 8; + bool arrayed : 1; + bool shadow : 1; + bool ms : 1; + bool image : 1; // image, combined should be false + bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler + bool sampler : 1; // true means a pure sampler, other fields should be clear() + + unsigned int vectorSize : 3; // vector return type size. + // Some languages support structures as sample results. Storing the whole structure in the + // TSampler is too large, so there is an index to a separate table. + static const unsigned structReturnIndexBits = 4; // number of index bits to use. + static const unsigned structReturnSlots = (1< TTypeList; + +typedef TVector TIdentifierList; + +enum TLayoutMatrix { + ElmNone, + ElmRowMajor, + ElmColumnMajor, // default, but different than saying nothing + ElmCount // If expanding, see bitfield width below +}; + +// Union of geometry shader and tessellation shader geometry types. +// They don't go into TType, but rather have current state per shader or +// active parser type (TPublicType). +enum TLayoutGeometry { + ElgNone, + ElgPoints, + ElgLines, + ElgLinesAdjacency, + ElgLineStrip, + ElgTriangles, + ElgTrianglesAdjacency, + ElgTriangleStrip, + ElgQuads, + ElgIsolines, +}; + +enum TVertexSpacing { + EvsNone, + EvsEqual, + EvsFractionalEven, + EvsFractionalOdd +}; + +enum TVertexOrder { + EvoNone, + EvoCw, + EvoCcw +}; + +// Note: order matters, as type of format is done by comparison. +enum TLayoutFormat { + ElfNone, + + // Float image + ElfRgba32f, + ElfRgba16f, + ElfR32f, + ElfRgba8, + ElfRgba8Snorm, + + ElfEsFloatGuard, // to help with comparisons + + ElfRg32f, + ElfRg16f, + ElfR11fG11fB10f, + ElfR16f, + ElfRgba16, + ElfRgb10A2, + ElfRg16, + ElfRg8, + ElfR16, + ElfR8, + ElfRgba16Snorm, + ElfRg16Snorm, + ElfRg8Snorm, + ElfR16Snorm, + ElfR8Snorm, + + ElfFloatGuard, // to help with comparisons + + // Int image + ElfRgba32i, + ElfRgba16i, + ElfRgba8i, + ElfR32i, + + ElfEsIntGuard, // to help with comparisons + + ElfRg32i, + ElfRg16i, + ElfRg8i, + ElfR16i, + ElfR8i, + ElfR64i, + + ElfIntGuard, // to help with comparisons + + // Uint image + ElfRgba32ui, + ElfRgba16ui, + ElfRgba8ui, + ElfR32ui, + + ElfEsUintGuard, // to help with comparisons + + ElfRg32ui, + ElfRg16ui, + ElfRgb10a2ui, + ElfRg8ui, + ElfR16ui, + ElfR8ui, + ElfR64ui, + ElfExtSizeGuard, // to help with comparisons + ElfSize1x8, + ElfSize1x16, + ElfSize1x32, + ElfSize2x32, + ElfSize4x32, + + ElfCount +}; + +enum TLayoutDepth { + EldNone, + EldAny, + EldGreater, + EldLess, + EldUnchanged, + + EldCount +}; + +enum TLayoutStencil { + ElsNone, + ElsRefUnchangedFrontAMD, + ElsRefGreaterFrontAMD, + ElsRefLessFrontAMD, + ElsRefUnchangedBackAMD, + ElsRefGreaterBackAMD, + ElsRefLessBackAMD, + + ElsCount +}; + +enum TBlendEquationShift { + // No 'EBlendNone': + // These are used as bit-shift amounts. A mask of such shifts will have type 'int', + // and in that space, 0 means no bits set, or none. In this enum, 0 means (1 << 0), a bit is set. + EBlendMultiply, + EBlendScreen, + EBlendOverlay, + EBlendDarken, + EBlendLighten, + EBlendColordodge, + EBlendColorburn, + EBlendHardlight, + EBlendSoftlight, + EBlendDifference, + EBlendExclusion, + EBlendHslHue, + EBlendHslSaturation, + EBlendHslColor, + EBlendHslLuminosity, + EBlendAllEquations, + + EBlendCount +}; + +enum TInterlockOrdering { + EioNone, + EioPixelInterlockOrdered, + EioPixelInterlockUnordered, + EioSampleInterlockOrdered, + EioSampleInterlockUnordered, + EioShadingRateInterlockOrdered, + EioShadingRateInterlockUnordered, + + EioCount, +}; + +enum TShaderInterface +{ + // Includes both uniform blocks and buffer blocks + EsiUniform = 0, + EsiInput, + EsiOutput, + EsiNone, + + EsiCount +}; + +class TQualifier { +public: + static const int layoutNotSet = -1; + + void clear() + { + precision = EpqNone; + invariant = false; + makeTemporary(); + declaredBuiltIn = EbvNone; + noContraction = false; + nullInit = false; + spirvByReference = false; + spirvLiteral = false; + defaultBlock = false; + } + + // drop qualifiers that don't belong in a temporary variable + void makeTemporary() + { + semanticName = nullptr; + storage = EvqTemporary; + builtIn = EbvNone; + clearInterstage(); + clearMemory(); + specConstant = false; + nonUniform = false; + nullInit = false; + defaultBlock = false; + clearLayout(); + spirvStorageClass = -1; + spirvDecorate = nullptr; + spirvByReference = false; + spirvLiteral = false; + } + + void clearInterstage() + { + clearInterpolation(); + patch = false; + sample = false; + } + + void clearInterpolation() + { + centroid = false; + smooth = false; + flat = false; + nopersp = false; + explicitInterp = false; + pervertexNV = false; + perPrimitiveNV = false; + perViewNV = false; + perTaskNV = false; + pervertexEXT = false; + } + + void clearMemory() + { + coherent = false; + devicecoherent = false; + queuefamilycoherent = false; + workgroupcoherent = false; + subgroupcoherent = false; + shadercallcoherent = false; + nonprivate = false; + volatil = false; + nontemporal = false; + restrict = false; + readonly = false; + writeonly = false; + } + + const char* semanticName; + TStorageQualifier storage : 7; + static_assert(EvqLast < 64, "need to increase size of TStorageQualifier bitfields!"); + TBuiltInVariable builtIn : 9; + TBuiltInVariable declaredBuiltIn : 9; + static_assert(EbvLast < 256, "need to increase size of TBuiltInVariable bitfields!"); + TPrecisionQualifier precision : 3; + bool invariant : 1; // require canonical treatment for cross-shader invariance + bool centroid : 1; + bool smooth : 1; + bool flat : 1; + // having a constant_id is not sufficient: expressions have no id, but are still specConstant + bool specConstant : 1; + bool nonUniform : 1; + bool explicitOffset : 1; + bool defaultBlock : 1; // default blocks with matching names have structures merged when linking + + bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects + bool nopersp : 1; + bool explicitInterp : 1; + bool pervertexNV : 1; + bool pervertexEXT : 1; + bool perPrimitiveNV : 1; + bool perViewNV : 1; + bool perTaskNV : 1; + bool patch : 1; + bool sample : 1; + bool restrict : 1; + bool readonly : 1; + bool writeonly : 1; + bool coherent : 1; + bool volatil : 1; + bool nontemporal : 1; + bool devicecoherent : 1; + bool queuefamilycoherent : 1; + bool workgroupcoherent : 1; + bool subgroupcoherent : 1; + bool shadercallcoherent : 1; + bool nonprivate : 1; + bool nullInit : 1; + bool spirvByReference : 1; + bool spirvLiteral : 1; + bool isWriteOnly() const { return writeonly; } + bool isReadOnly() const { return readonly; } + bool isRestrict() const { return restrict; } + bool isCoherent() const { return coherent; } + bool isVolatile() const { return volatil; } + bool isNonTemporal() const { return nontemporal; } + bool isSample() const { return sample; } + bool isMemory() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || nontemporal || restrict || readonly || writeonly || nonprivate; + } + bool isMemoryQualifierImageAndSSBOOnly() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || nontemporal || restrict || readonly || writeonly; + } + bool bufferReferenceNeedsVulkanMemoryModel() const + { + // include qualifiers that map to load/store availability/visibility/nonprivate memory access operands + return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || nonprivate; + } + bool isInterpolation() const + { + return flat || smooth || nopersp || explicitInterp; + } + bool isExplicitInterpolation() const + { + return explicitInterp; + } + bool isAuxiliary() const + { + return centroid || patch || sample || pervertexNV || pervertexEXT; + } + bool isPatch() const { return patch; } + bool isNoContraction() const { return noContraction; } + void setNoContraction() { noContraction = true; } + bool isPervertexNV() const { return pervertexNV; } + bool isPervertexEXT() const { return pervertexEXT; } + void setNullInit() { nullInit = true; } + bool isNullInit() const { return nullInit; } + void setSpirvByReference() { spirvByReference = true; } + bool isSpirvByReference() const { return spirvByReference; } + void setSpirvLiteral() { spirvLiteral = true; } + bool isSpirvLiteral() const { return spirvLiteral; } + + bool isPipeInput() const + { + switch (storage) { + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + return true; + default: + return false; + } + } + + bool isPipeOutput() const + { + switch (storage) { + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + case EvqFragStencil: + return true; + default: + return false; + } + } + + bool isParamInput() const + { + switch (storage) { + case EvqIn: + case EvqInOut: + case EvqConstReadOnly: + return true; + default: + return false; + } + } + + bool isParamOutput() const + { + switch (storage) { + case EvqOut: + case EvqInOut: + return true; + default: + return false; + } + } + + bool isUniformOrBuffer() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + return true; + default: + return false; + } + } + + bool isUniform() const + { + switch (storage) { + case EvqUniform: + return true; + default: + return false; + } + } + + bool isIo() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + case EvqFragStencil: + return true; + default: + return false; + } + } + + // non-built-in symbols that might link between compilation units + bool isLinkable() const + { + switch (storage) { + case EvqGlobal: + case EvqVaryingIn: + case EvqVaryingOut: + case EvqUniform: + case EvqBuffer: + case EvqShared: + return true; + default: + return false; + } + } + + TBlockStorageClass getBlockStorage() const { + if (storage == EvqUniform && !isPushConstant()) { + return EbsUniform; + } + else if (storage == EvqUniform) { + return EbsPushConstant; + } + else if (storage == EvqBuffer) { + return EbsStorageBuffer; + } + return EbsNone; + } + + void setBlockStorage(TBlockStorageClass newBacking) { + layoutPushConstant = (newBacking == EbsPushConstant); + switch (newBacking) { + case EbsUniform : + if (layoutPacking == ElpStd430) { + // std430 would not be valid + layoutPacking = ElpStd140; + } + storage = EvqUniform; + break; + case EbsStorageBuffer : + storage = EvqBuffer; + break; + case EbsPushConstant : + storage = EvqUniform; + layoutSet = TQualifier::layoutSetEnd; + layoutBinding = TQualifier::layoutBindingEnd; + break; + default: + break; + } + } + + bool isPerPrimitive() const { return perPrimitiveNV; } + bool isPerView() const { return perViewNV; } + bool isTaskMemory() const { return perTaskNV; } + bool isTaskPayload() const { return storage == EvqtaskPayloadSharedEXT; } + bool isAnyPayload() const { + return storage == EvqPayload || storage == EvqPayloadIn; + } + bool isAnyCallable() const { + return storage == EvqCallableData || storage == EvqCallableDataIn; + } + bool isHitObjectAttrNV() const { + return storage == EvqHitObjectAttrNV; + } + + // True if this type of IO is supposed to be arrayed with extra level for per-vertex data + bool isArrayedIo(EShLanguage language) const + { + switch (language) { + case EShLangGeometry: + return isPipeInput(); + case EShLangTessControl: + return ! patch && (isPipeInput() || isPipeOutput()); + case EShLangTessEvaluation: + return ! patch && isPipeInput(); + case EShLangFragment: + return (pervertexNV || pervertexEXT) && isPipeInput(); + case EShLangMesh: + return ! perTaskNV && isPipeOutput(); + + default: + return false; + } + } + + // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield + void clearLayout() // all layout + { + clearUniformLayout(); + + layoutPushConstant = false; + layoutBufferReference = false; + layoutPassthrough = false; + layoutViewportRelative = false; + // -2048 as the default value indicating layoutSecondaryViewportRelative is not set + layoutSecondaryViewportRelativeOffset = -2048; + layoutShaderRecord = false; + layoutFullQuads = false; + layoutQuadDeriv = false; + layoutHitObjectShaderRecordNV = false; + layoutBindlessSampler = false; + layoutBindlessImage = false; + layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd; + layoutFormat = ElfNone; + + clearInterstageLayout(); + + layoutSpecConstantId = layoutSpecConstantIdEnd; + } + void clearInterstageLayout() + { + layoutLocation = layoutLocationEnd; + layoutComponent = layoutComponentEnd; + layoutIndex = layoutIndexEnd; + clearStreamLayout(); + clearXfbLayout(); + } + + void clearStreamLayout() + { + layoutStream = layoutStreamEnd; + } + void clearXfbLayout() + { + layoutXfbBuffer = layoutXfbBufferEnd; + layoutXfbStride = layoutXfbStrideEnd; + layoutXfbOffset = layoutXfbOffsetEnd; + } + + bool hasNonXfbLayout() const + { + return hasUniformLayout() || + hasAnyLocation() || + hasStream() || + hasFormat() || + isShaderRecord() || + isPushConstant() || + hasBufferReference(); + } + bool hasLayout() const + { + return hasNonXfbLayout() || + hasXfb(); + } + + TLayoutMatrix layoutMatrix : 3; + TLayoutPacking layoutPacking : 4; + int layoutOffset; + int layoutAlign; + + unsigned int layoutLocation : 12; + static const unsigned int layoutLocationEnd = 0xFFF; + + unsigned int layoutComponent : 3; + static const unsigned int layoutComponentEnd = 4; + + unsigned int layoutSet : 7; + static const unsigned int layoutSetEnd = 0x3F; + + unsigned int layoutBinding : 16; + static const unsigned int layoutBindingEnd = 0xFFFF; + + unsigned int layoutIndex : 8; + static const unsigned int layoutIndexEnd = 0xFF; + + unsigned int layoutStream : 8; + static const unsigned int layoutStreamEnd = 0xFF; + + unsigned int layoutXfbBuffer : 4; + static const unsigned int layoutXfbBufferEnd = 0xF; + + unsigned int layoutXfbStride : 14; + static const unsigned int layoutXfbStrideEnd = 0x3FFF; + + unsigned int layoutXfbOffset : 13; + static const unsigned int layoutXfbOffsetEnd = 0x1FFF; + + unsigned int layoutAttachment : 8; // for input_attachment_index + static const unsigned int layoutAttachmentEnd = 0XFF; + + unsigned int layoutSpecConstantId : 11; + static const unsigned int layoutSpecConstantIdEnd = 0x7FF; + + // stored as log2 of the actual alignment value + unsigned int layoutBufferReferenceAlign : 6; + static const unsigned int layoutBufferReferenceAlignEnd = 0x3F; + + TLayoutFormat layoutFormat : 8; + + bool layoutPushConstant; + bool layoutBufferReference; + bool layoutPassthrough; + bool layoutViewportRelative; + int layoutSecondaryViewportRelativeOffset; + bool layoutShaderRecord; + bool layoutFullQuads; + bool layoutQuadDeriv; + bool layoutHitObjectShaderRecordNV; + + // GL_EXT_spirv_intrinsics + int spirvStorageClass; + TSpirvDecorate* spirvDecorate; + + bool layoutBindlessSampler; + bool layoutBindlessImage; + + bool hasUniformLayout() const + { + return hasMatrix() || + hasPacking() || + hasOffset() || + hasBinding() || + hasSet() || + hasAlign(); + } + void clearUniformLayout() // only uniform specific + { + layoutMatrix = ElmNone; + layoutPacking = ElpNone; + layoutOffset = layoutNotSet; + layoutAlign = layoutNotSet; + + layoutSet = layoutSetEnd; + layoutBinding = layoutBindingEnd; + layoutAttachment = layoutAttachmentEnd; + } + + bool hasMatrix() const + { + return layoutMatrix != ElmNone; + } + bool hasPacking() const + { + return layoutPacking != ElpNone; + } + bool hasAlign() const + { + return layoutAlign != layoutNotSet; + } + bool hasAnyLocation() const + { + return hasLocation() || + hasComponent() || + hasIndex(); + } + bool hasLocation() const + { + return layoutLocation != layoutLocationEnd; + } + bool hasSet() const + { + return layoutSet != layoutSetEnd; + } + bool hasBinding() const + { + return layoutBinding != layoutBindingEnd; + } + bool hasOffset() const + { + return layoutOffset != layoutNotSet; + } + bool isNonPerspective() const { return nopersp; } + bool hasIndex() const + { + return layoutIndex != layoutIndexEnd; + } + unsigned getIndex() const { return layoutIndex; } + bool hasComponent() const + { + return layoutComponent != layoutComponentEnd; + } + bool hasStream() const + { + return layoutStream != layoutStreamEnd; + } + bool hasFormat() const + { + return layoutFormat != ElfNone; + } + bool hasXfb() const + { + return hasXfbBuffer() || + hasXfbStride() || + hasXfbOffset(); + } + bool hasXfbBuffer() const + { + return layoutXfbBuffer != layoutXfbBufferEnd; + } + bool hasXfbStride() const + { + return layoutXfbStride != layoutXfbStrideEnd; + } + bool hasXfbOffset() const + { + return layoutXfbOffset != layoutXfbOffsetEnd; + } + bool hasAttachment() const + { + return layoutAttachment != layoutAttachmentEnd; + } + TLayoutFormat getFormat() const { return layoutFormat; } + bool isPushConstant() const { return layoutPushConstant; } + bool isShaderRecord() const { return layoutShaderRecord; } + bool isFullQuads() const { return layoutFullQuads; } + bool isQuadDeriv() const { return layoutQuadDeriv; } + bool hasHitObjectShaderRecordNV() const { return layoutHitObjectShaderRecordNV; } + bool hasBufferReference() const { return layoutBufferReference; } + bool hasBufferReferenceAlign() const + { + return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd; + } + bool isNonUniform() const + { + return nonUniform; + } + bool isBindlessSampler() const + { + return layoutBindlessSampler; + } + bool isBindlessImage() const + { + return layoutBindlessImage; + } + + // GL_EXT_spirv_intrinsics + bool hasSpirvDecorate() const { return spirvDecorate != nullptr; } + void setSpirvDecorate(int decoration, const TIntermAggregate* args = nullptr); + void setSpirvDecorateId(int decoration, const TIntermAggregate* args); + void setSpirvDecorateString(int decoration, const TIntermAggregate* args); + const TSpirvDecorate& getSpirvDecorate() const { assert(spirvDecorate); return *spirvDecorate; } + TSpirvDecorate& getSpirvDecorate() { assert(spirvDecorate); return *spirvDecorate; } + TString getSpirvDecorateQualifierString() const; + + bool hasSpecConstantId() const + { + // Not the same thing as being a specialization constant, this + // is just whether or not it was declared with an ID. + return layoutSpecConstantId != layoutSpecConstantIdEnd; + } + bool isSpecConstant() const + { + // True if type is a specialization constant, whether or not it + // had a specialization-constant ID, and false if it is not a + // true front-end constant. + return specConstant; + } + bool isFrontEndConstant() const + { + // True if the front-end knows the final constant value. + // This allows front-end constant folding. + return storage == EvqConst && ! specConstant; + } + bool isConstant() const + { + // True if is either kind of constant; specialization or regular. + return isFrontEndConstant() || isSpecConstant(); + } + void makeSpecConstant() + { + storage = EvqConst; + specConstant = true; + } + static const char* getLayoutPackingString(TLayoutPacking packing) + { + switch (packing) { + case ElpStd140: return "std140"; + case ElpPacked: return "packed"; + case ElpShared: return "shared"; + case ElpStd430: return "std430"; + case ElpScalar: return "scalar"; + default: return "none"; + } + } + static const char* getLayoutMatrixString(TLayoutMatrix m) + { + switch (m) { + case ElmColumnMajor: return "column_major"; + case ElmRowMajor: return "row_major"; + default: return "none"; + } + } + static const char* getLayoutFormatString(TLayoutFormat f) + { + switch (f) { + case ElfRgba32f: return "rgba32f"; + case ElfRgba16f: return "rgba16f"; + case ElfRg32f: return "rg32f"; + case ElfRg16f: return "rg16f"; + case ElfR11fG11fB10f: return "r11f_g11f_b10f"; + case ElfR32f: return "r32f"; + case ElfR16f: return "r16f"; + case ElfRgba16: return "rgba16"; + case ElfRgb10A2: return "rgb10_a2"; + case ElfRgba8: return "rgba8"; + case ElfRg16: return "rg16"; + case ElfRg8: return "rg8"; + case ElfR16: return "r16"; + case ElfR8: return "r8"; + case ElfRgba16Snorm: return "rgba16_snorm"; + case ElfRgba8Snorm: return "rgba8_snorm"; + case ElfRg16Snorm: return "rg16_snorm"; + case ElfRg8Snorm: return "rg8_snorm"; + case ElfR16Snorm: return "r16_snorm"; + case ElfR8Snorm: return "r8_snorm"; + + case ElfRgba32i: return "rgba32i"; + case ElfRgba16i: return "rgba16i"; + case ElfRgba8i: return "rgba8i"; + case ElfRg32i: return "rg32i"; + case ElfRg16i: return "rg16i"; + case ElfRg8i: return "rg8i"; + case ElfR32i: return "r32i"; + case ElfR16i: return "r16i"; + case ElfR8i: return "r8i"; + + case ElfRgba32ui: return "rgba32ui"; + case ElfRgba16ui: return "rgba16ui"; + case ElfRgba8ui: return "rgba8ui"; + case ElfRg32ui: return "rg32ui"; + case ElfRg16ui: return "rg16ui"; + case ElfRgb10a2ui: return "rgb10_a2ui"; + case ElfRg8ui: return "rg8ui"; + case ElfR32ui: return "r32ui"; + case ElfR16ui: return "r16ui"; + case ElfR8ui: return "r8ui"; + case ElfR64ui: return "r64ui"; + case ElfR64i: return "r64i"; + case ElfSize1x8: return "size1x8"; + case ElfSize1x16: return "size1x16"; + case ElfSize1x32: return "size1x32"; + case ElfSize2x32: return "size2x32"; + case ElfSize4x32: return "size4x32"; + default: return "none"; + } + } + static const char* getLayoutDepthString(TLayoutDepth d) + { + switch (d) { + case EldAny: return "depth_any"; + case EldGreater: return "depth_greater"; + case EldLess: return "depth_less"; + case EldUnchanged: return "depth_unchanged"; + default: return "none"; + } + } + static const char* getLayoutStencilString(TLayoutStencil s) + { + switch (s) { + case ElsRefUnchangedFrontAMD: return "stencil_ref_unchanged_front_amd"; + case ElsRefGreaterFrontAMD: return "stencil_ref_greater_front_amd"; + case ElsRefLessFrontAMD: return "stencil_ref_less_front_amd"; + case ElsRefUnchangedBackAMD: return "stencil_ref_unchanged_back_amd"; + case ElsRefGreaterBackAMD: return "stencil_ref_greater_back_amd"; + case ElsRefLessBackAMD: return "stencil_ref_less_back_amd"; + default: return "none"; + } + } + static const char* getBlendEquationString(TBlendEquationShift e) + { + switch (e) { + case EBlendMultiply: return "blend_support_multiply"; + case EBlendScreen: return "blend_support_screen"; + case EBlendOverlay: return "blend_support_overlay"; + case EBlendDarken: return "blend_support_darken"; + case EBlendLighten: return "blend_support_lighten"; + case EBlendColordodge: return "blend_support_colordodge"; + case EBlendColorburn: return "blend_support_colorburn"; + case EBlendHardlight: return "blend_support_hardlight"; + case EBlendSoftlight: return "blend_support_softlight"; + case EBlendDifference: return "blend_support_difference"; + case EBlendExclusion: return "blend_support_exclusion"; + case EBlendHslHue: return "blend_support_hsl_hue"; + case EBlendHslSaturation: return "blend_support_hsl_saturation"; + case EBlendHslColor: return "blend_support_hsl_color"; + case EBlendHslLuminosity: return "blend_support_hsl_luminosity"; + case EBlendAllEquations: return "blend_support_all_equations"; + default: return "unknown"; + } + } + static const char* getGeometryString(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return "points"; + case ElgLines: return "lines"; + case ElgLinesAdjacency: return "lines_adjacency"; + case ElgLineStrip: return "line_strip"; + case ElgTriangles: return "triangles"; + case ElgTrianglesAdjacency: return "triangles_adjacency"; + case ElgTriangleStrip: return "triangle_strip"; + case ElgQuads: return "quads"; + case ElgIsolines: return "isolines"; + default: return "none"; + } + } + static const char* getVertexSpacingString(TVertexSpacing spacing) + { + switch (spacing) { + case EvsEqual: return "equal_spacing"; + case EvsFractionalEven: return "fractional_even_spacing"; + case EvsFractionalOdd: return "fractional_odd_spacing"; + default: return "none"; + } + } + static const char* getVertexOrderString(TVertexOrder order) + { + switch (order) { + case EvoCw: return "cw"; + case EvoCcw: return "ccw"; + default: return "none"; + } + } + static int mapGeometryToSize(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return 1; + case ElgLines: return 2; + case ElgLinesAdjacency: return 4; + case ElgTriangles: return 3; + case ElgTrianglesAdjacency: return 6; + default: return 0; + } + } + static const char* getInterlockOrderingString(TInterlockOrdering order) + { + switch (order) { + case EioPixelInterlockOrdered: return "pixel_interlock_ordered"; + case EioPixelInterlockUnordered: return "pixel_interlock_unordered"; + case EioSampleInterlockOrdered: return "sample_interlock_ordered"; + case EioSampleInterlockUnordered: return "sample_interlock_unordered"; + case EioShadingRateInterlockOrdered: return "shading_rate_interlock_ordered"; + case EioShadingRateInterlockUnordered: return "shading_rate_interlock_unordered"; + default: return "none"; + } + } +}; + +// Qualifiers that don't need to be keep per object. They have shader scope, not object scope. +// So, they will not be part of TType, TQualifier, etc. +struct TShaderQualifiers { + TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives + bool pixelCenterInteger; // fragment shader + bool originUpperLeft; // fragment shader + int invocations; + int vertices; // for tessellation "vertices", geometry & mesh "max_vertices" + TVertexSpacing spacing; + TVertexOrder order; + bool pointMode; + int localSize[3]; // compute shader + bool localSizeNotDefault[3]; // compute shader + int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize + bool earlyFragmentTests; // fragment input + bool postDepthCoverage; // fragment input + bool earlyAndLateFragmentTestsAMD; //fragment input + bool nonCoherentColorAttachmentReadEXT; // fragment input + bool nonCoherentDepthAttachmentReadEXT; // fragment input + bool nonCoherentStencilAttachmentReadEXT; // fragment input + TLayoutDepth layoutDepth; + TLayoutStencil layoutStencil; + bool blendEquation; // true if any blend equation was specified + int numViews; // multiview extenstions + TInterlockOrdering interlockOrdering; + bool layoutOverrideCoverage; // true if layout override_coverage set + bool layoutDerivativeGroupQuads; // true if layout derivative_group_quadsNV set + bool layoutDerivativeGroupLinear; // true if layout derivative_group_linearNV set + int primitives; // mesh shader "max_primitives"DerivativeGroupLinear; // true if layout derivative_group_linearNV set + bool layoutPrimitiveCulling; // true if layout primitive_culling set + TLayoutDepth getDepth() const { return layoutDepth; } + TLayoutStencil getStencil() const { return layoutStencil; } + + void init() + { + geometry = ElgNone; + originUpperLeft = false; + pixelCenterInteger = false; + invocations = TQualifier::layoutNotSet; + vertices = TQualifier::layoutNotSet; + spacing = EvsNone; + order = EvoNone; + pointMode = false; + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; + earlyFragmentTests = false; + earlyAndLateFragmentTestsAMD = false; + postDepthCoverage = false; + nonCoherentColorAttachmentReadEXT = false; + nonCoherentDepthAttachmentReadEXT = false; + nonCoherentStencilAttachmentReadEXT = false; + layoutDepth = EldNone; + layoutStencil = ElsNone; + blendEquation = false; + numViews = TQualifier::layoutNotSet; + layoutOverrideCoverage = false; + layoutDerivativeGroupQuads = false; + layoutDerivativeGroupLinear = false; + layoutPrimitiveCulling = false; + primitives = TQualifier::layoutNotSet; + interlockOrdering = EioNone; + } + + bool hasBlendEquation() const { return blendEquation; } + + // Merge in characteristics from the 'src' qualifier. They can override when + // set, but never erase when not set. + void merge(const TShaderQualifiers& src) + { + if (src.geometry != ElgNone) + geometry = src.geometry; + if (src.pixelCenterInteger) + pixelCenterInteger = src.pixelCenterInteger; + if (src.originUpperLeft) + originUpperLeft = src.originUpperLeft; + if (src.invocations != TQualifier::layoutNotSet) + invocations = src.invocations; + if (src.vertices != TQualifier::layoutNotSet) + vertices = src.vertices; + if (src.spacing != EvsNone) + spacing = src.spacing; + if (src.order != EvoNone) + order = src.order; + if (src.pointMode) + pointMode = true; + for (int i = 0; i < 3; ++i) { + if (src.localSize[i] > 1) + localSize[i] = src.localSize[i]; + } + for (int i = 0; i < 3; ++i) { + localSizeNotDefault[i] = src.localSizeNotDefault[i] || localSizeNotDefault[i]; + } + for (int i = 0; i < 3; ++i) { + if (src.localSizeSpecId[i] != TQualifier::layoutNotSet) + localSizeSpecId[i] = src.localSizeSpecId[i]; + } + if (src.earlyFragmentTests) + earlyFragmentTests = true; + if (src.earlyAndLateFragmentTestsAMD) + earlyAndLateFragmentTestsAMD = true; + if (src.postDepthCoverage) + postDepthCoverage = true; + if (src.nonCoherentColorAttachmentReadEXT) + nonCoherentColorAttachmentReadEXT = true; + if (src.nonCoherentDepthAttachmentReadEXT) + nonCoherentDepthAttachmentReadEXT = true; + if (src.nonCoherentStencilAttachmentReadEXT) + nonCoherentStencilAttachmentReadEXT = true; + if (src.layoutDepth) + layoutDepth = src.layoutDepth; + if (src.layoutStencil) + layoutStencil = src.layoutStencil; + if (src.blendEquation) + blendEquation = src.blendEquation; + if (src.numViews != TQualifier::layoutNotSet) + numViews = src.numViews; + if (src.layoutOverrideCoverage) + layoutOverrideCoverage = src.layoutOverrideCoverage; + if (src.layoutDerivativeGroupQuads) + layoutDerivativeGroupQuads = src.layoutDerivativeGroupQuads; + if (src.layoutDerivativeGroupLinear) + layoutDerivativeGroupLinear = src.layoutDerivativeGroupLinear; + if (src.primitives != TQualifier::layoutNotSet) + primitives = src.primitives; + if (src.interlockOrdering != EioNone) + interlockOrdering = src.interlockOrdering; + if (src.layoutPrimitiveCulling) + layoutPrimitiveCulling = src.layoutPrimitiveCulling; + } +}; + +class TTypeParameters { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TTypeParameters() : basicType(EbtVoid), arraySizes(nullptr), spirvType(nullptr) {} + + TBasicType basicType; + TArraySizes *arraySizes; + TSpirvType *spirvType; + + bool operator==(const TTypeParameters& rhs) const + { + bool same = basicType == rhs.basicType && *arraySizes == *rhs.arraySizes; + if (same && basicType == EbtSpirvType) { + assert(spirvType && rhs.spirvType); + return *spirvType == *rhs.spirvType; + } + return same; + } + bool operator!=(const TTypeParameters& rhs) const + { + return !(*this == rhs); + } +}; + +// +// TPublicType is just temporarily used while parsing and not quite the same +// information kept per node in TType. Due to the bison stack, it can't have +// types that it thinks have non-trivial constructors. It should +// just be used while recognizing the grammar, not anything else. +// Once enough is known about the situation, the proper information +// moved into a TType, or the parse context, etc. +// +class TPublicType { +public: + TBasicType basicType; + TSampler sampler; + TQualifier qualifier; + TShaderQualifiers shaderQualifiers; + uint32_t vectorSize : 4; + uint32_t matrixCols : 4; + uint32_t matrixRows : 4; + bool coopmatNV : 1; + bool coopmatKHR : 1; + bool coopvecNV : 1; + TArraySizes* arraySizes; + const TType* userDef; + TSourceLoc loc; + TTypeParameters* typeParameters; + // SPIR-V type defined by spirv_type directive + TSpirvType* spirvType; + + bool isCoopmat() const { return coopmatNV || coopmatKHR; } + bool isCoopmatNV() const { return coopmatNV; } + bool isCoopmatKHR() const { return coopmatKHR; } + bool isCoopvecNV() const { return coopvecNV; } + bool isCoopmatOrvec() const { return isCoopmat() || isCoopvecNV(); } + + bool isTensorLayoutNV() const { return basicType == EbtTensorLayoutNV; } + bool isTensorViewNV() const { return basicType == EbtTensorViewNV; } + + void initType(const TSourceLoc& l) + { + basicType = EbtVoid; + vectorSize = 1u; + matrixRows = 0; + matrixCols = 0; + arraySizes = nullptr; + userDef = nullptr; + loc = l; + typeParameters = nullptr; + coopmatNV = false; + coopmatKHR = false; + coopvecNV = false; + spirvType = nullptr; + } + + void initQualifiers(bool global = false) + { + qualifier.clear(); + if (global) + qualifier.storage = EvqGlobal; + } + + void init(const TSourceLoc& l, bool global = false) + { + initType(l); + sampler.clear(); + initQualifiers(global); + shaderQualifiers.init(); + } + + void setVector(int s) + { + matrixRows = 0; + matrixCols = 0; + assert(s >= 0); + vectorSize = static_cast(s) & 0b1111; + } + + void setMatrix(int c, int r) + { + assert(r >= 0); + matrixRows = static_cast(r) & 0b1111; + assert(c >= 0); + matrixCols = static_cast(c) & 0b1111; + vectorSize = 0; + } + + bool isScalar() const + { + return matrixCols == 0u && vectorSize == 1u && arraySizes == nullptr && userDef == nullptr; + } + + // GL_EXT_spirv_intrinsics + void setSpirvType(const TSpirvInstruction& spirvInst, const TSpirvTypeParameters* typeParams = nullptr); + + // "Image" is a superset of "Subpass" + bool isImage() const { return basicType == EbtSampler && sampler.isImage(); } + bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); } + bool isAttachmentEXT() const { return basicType == EbtSampler && sampler.isAttachmentEXT(); } +}; + +// +// Base class for things that have a type. +// +class TType { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // for "empty" type (no args) or simple scalar/vector/matrix + explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(static_cast(vs) & 0b1111), matrixCols(static_cast(mc) & 0b1111), matrixRows(static_cast(mr) & 0b1111), vector1(isVector && vs == 1), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr), + spirvType(nullptr) + { + assert(vs >= 0); + assert(mc >= 0); + assert(mr >= 0); + + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for explicit precision qualifier + TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(static_cast(vs) & 0b1111), matrixCols(static_cast(mc) & 0b1111), matrixRows(static_cast(mr) & 0b1111), vector1(isVector && vs == 1), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr), + spirvType(nullptr) + { + assert(vs >= 0); + assert(mc >= 0); + assert(mr >= 0); + + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + qualifier.precision = p; + assert(p >= EpqNone && p <= EpqHigh); + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for turning a TPublicType into a TType, using a shallow copy + explicit TType(const TPublicType& p) : + basicType(p.basicType), + vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), vector1(false), coopmatNV(p.coopmatNV), coopmatKHR(p.coopmatKHR), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(p.coopvecNV), + arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(p.typeParameters), + spirvType(p.spirvType) + { + if (basicType == EbtSampler) + sampler = p.sampler; + else + sampler.clear(); + qualifier = p.qualifier; + if (p.userDef) { + if (p.userDef->basicType == EbtReference) { + basicType = EbtReference; + referentType = p.userDef->referentType; + } else { + structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues + } + typeName = NewPoolTString(p.userDef->getTypeName().c_str()); + } + if (p.isCoopmatNV() && p.typeParameters && p.typeParameters->arraySizes->getNumDims() > 0) { + int numBits = p.typeParameters->arraySizes->getDimSize(0); + if (p.basicType == EbtFloat && numBits == 16) { + basicType = EbtFloat16; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtUint && numBits == 8) { + basicType = EbtUint8; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtUint && numBits == 16) { + basicType = EbtUint16; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtInt && numBits == 8) { + basicType = EbtInt8; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtInt && numBits == 16) { + basicType = EbtInt16; + qualifier.precision = EpqNone; + } + } + if (p.isCoopmatKHR() && p.typeParameters && p.typeParameters->arraySizes->getNumDims() > 0) { + basicType = p.typeParameters->basicType; + if (isSpirvType()) { + assert(p.typeParameters->spirvType); + spirvType = p.typeParameters->spirvType; + } + + if (p.typeParameters->arraySizes->getNumDims() == 4) { + const int dimSize = p.typeParameters->arraySizes->getDimSize(3); + assert(dimSize >= 0); + coopmatKHRuse = static_cast(dimSize) & 0b111; + coopmatKHRUseValid = true; + } + } + if (p.isCoopvecNV() && p.typeParameters) { + basicType = p.typeParameters->basicType; + } + } + // for construction of sampler types + TType(const TSampler& sampler, TStorageQualifier q = EvqUniform, TArraySizes* as = nullptr) : + basicType(EbtSampler), vectorSize(1u), matrixCols(0u), matrixRows(0u), vector1(false), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(false), + arraySizes(as), structure(nullptr), fieldName(nullptr), typeName(nullptr), + sampler(sampler), typeParameters(nullptr), spirvType(nullptr) + { + qualifier.clear(); + qualifier.storage = q; + } + // to efficiently make a dereferenced type + // without ever duplicating the outer structure that will be thrown away + // and using only shallow copy + TType(const TType& type, int derefIndex, bool rowMajor = false) + { + if (type.isArray()) { + shallowCopy(type); + if (type.getArraySizes()->getNumDims() == 1) { + arraySizes = nullptr; + } else { + // want our own copy of the array, so we can edit it + arraySizes = new TArraySizes; + arraySizes->copyDereferenced(*type.arraySizes); + } + } else if (type.basicType == EbtStruct || type.basicType == EbtBlock) { + // do a structure dereference + const TTypeList& memberList = *type.getStruct(); + shallowCopy(*memberList[derefIndex].type); + return; + } else { + // do a vector/matrix dereference + shallowCopy(type); + if (matrixCols > 0) { + // dereference from matrix to vector + if (rowMajor) + vectorSize = matrixCols; + else + vectorSize = matrixRows; + matrixCols = 0; + matrixRows = 0; + if (vectorSize == 1) + vector1 = true; + } else if (isVector()) { + // dereference from vector to scalar + vectorSize = 1; + vector1 = false; + } else if (isCoopMat() || isCoopVecNV()) { + coopmatNV = false; + coopmatKHR = false; + coopmatKHRuse = 0; + coopmatKHRUseValid = false; + coopvecNV = false; + typeParameters = nullptr; + } + } + } + // for making structures, ... + TType(TTypeList* userDef, const TString& n) : + basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(false), + arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr), + spirvType(nullptr) + { + sampler.clear(); + qualifier.clear(); + typeName = NewPoolTString(n.c_str()); + } + // For interface blocks + TType(TTypeList* userDef, const TString& n, const TQualifier& q) : + basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), coopvecNV(false), + qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr), + spirvType(nullptr) + { + sampler.clear(); + typeName = NewPoolTString(n.c_str()); + } + // for block reference (first parameter must be EbtReference) + explicit TType(TBasicType t, const TType &p, const TString& n) : + basicType(t), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmatNV(false), coopmatKHR(false), coopmatKHRuse(0), coopmatKHRUseValid(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr), + spirvType(nullptr) + { + assert(t == EbtReference); + typeName = NewPoolTString(n.c_str()); + sampler.clear(); + qualifier.clear(); + qualifier.storage = p.qualifier.storage; + referentType = p.clone(); + } + virtual ~TType() {} + + // Not for use across pool pops; it will cause multiple instances of TType to point to the same information. + // This only works if that information (like a structure's list of types) does not change and + // the instances are sharing the same pool. + void shallowCopy(const TType& copyOf) + { + basicType = copyOf.basicType; + sampler = copyOf.sampler; + qualifier = copyOf.qualifier; + vectorSize = copyOf.vectorSize; + matrixCols = copyOf.matrixCols; + matrixRows = copyOf.matrixRows; + vector1 = copyOf.vector1; + arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents + fieldName = copyOf.fieldName; + typeName = copyOf.typeName; + if (isStruct()) { + structure = copyOf.structure; + } else { + referentType = copyOf.referentType; + } + typeParameters = copyOf.typeParameters; + spirvType = copyOf.spirvType; + coopmatNV = copyOf.isCoopMatNV(); + coopmatKHR = copyOf.isCoopMatKHR(); + coopmatKHRuse = copyOf.coopmatKHRuse; + coopmatKHRUseValid = copyOf.coopmatKHRUseValid; + coopvecNV = copyOf.isCoopVecNV(); + } + + // Make complete copy of the whole type graph rooted at 'copyOf'. + void deepCopy(const TType& copyOf) + { + TMap copied; // to enable copying a type graph as a graph, not a tree + deepCopy(copyOf, copied); + } + + // Recursively make temporary + void makeTemporary() + { + getQualifier().makeTemporary(); + + if (isStruct()) + for (unsigned int i = 0; i < structure->size(); ++i) + (*structure)[i].type->makeTemporary(); + } + + TType* clone() const + { + TType *newType = new TType(); + newType->deepCopy(*this); + + return newType; + } + + void makeVector() { vector1 = true; } + + virtual void hideMember() { basicType = EbtVoid; vectorSize = 1u; } + virtual bool hiddenMember() const { return basicType == EbtVoid; } + + virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); } + virtual const TString& getTypeName() const + { + assert(typeName); + return *typeName; + } + + virtual const TString& getFieldName() const + { + assert(fieldName); + return *fieldName; + } + TShaderInterface getShaderInterface() const + { + if (basicType != EbtBlock) + return EsiNone; + + switch (qualifier.storage) { + default: + return EsiNone; + case EvqVaryingIn: + return EsiInput; + case EvqVaryingOut: + return EsiOutput; + case EvqUniform: + case EvqBuffer: + return EsiUniform; + } + } + + virtual TBasicType getBasicType() const { return basicType; } + virtual const TSampler& getSampler() const { return sampler; } + virtual TSampler& getSampler() { return sampler; } + + virtual TQualifier& getQualifier() { return qualifier; } + virtual const TQualifier& getQualifier() const { return qualifier; } + + virtual int getVectorSize() const { return static_cast(vectorSize); } // returns 1 for either scalar or vector of size 1, valid for both + virtual int getMatrixCols() const { return static_cast(matrixCols); } + virtual int getMatrixRows() const { return static_cast(matrixRows); } + virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } + virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); } + virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); } + bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } + virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); } + virtual const TArraySizes* getArraySizes() const { return arraySizes; } + virtual TArraySizes* getArraySizes() { return arraySizes; } + virtual TType* getReferentType() const { return referentType; } + virtual const TTypeParameters* getTypeParameters() const { return typeParameters; } + virtual TTypeParameters* getTypeParameters() { return typeParameters; } + + virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray() && ! isCoopVecNV(); } + virtual bool isScalarOrVec1() const { return isScalar() || vector1; } + virtual bool isScalarOrVector() const { return !isMatrix() && !isStruct() && !isArray(); } + virtual bool isVector() const { return vectorSize > 1u || vector1; } + virtual bool isMatrix() const { return matrixCols ? true : false; } + virtual bool isArray() const { return arraySizes != nullptr; } + virtual bool isSizedArray() const { return isArray() && arraySizes->isSized(); } + virtual bool isUnsizedArray() const { return isArray() && !arraySizes->isSized(); } + virtual bool isImplicitlySizedArray() const { return isArray() && arraySizes->isImplicitlySized(); } + virtual bool isArrayVariablyIndexed() const { assert(isArray()); return arraySizes->isVariablyIndexed(); } + virtual void setArrayVariablyIndexed() { assert(isArray()); arraySizes->setVariablyIndexed(); } + virtual void updateImplicitArraySize(int size) { assert(isArray()); arraySizes->updateImplicitSize(size); } + virtual void setImplicitlySized(bool isImplicitSized) { arraySizes->setImplicitlySized(isImplicitSized); } + virtual bool isStruct() const { return basicType == EbtStruct || basicType == EbtBlock; } + virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16 || basicType == EbtBFloat16; } + virtual bool isIntegerDomain() const + { + switch (basicType) { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtAtomicUint: + return true; + default: + break; + } + return false; + } + virtual bool isOpaque() const { return basicType == EbtSampler + || basicType == EbtAtomicUint || basicType == EbtAccStruct || basicType == EbtRayQuery + || basicType == EbtHitObjectNV; } + virtual bool isBuiltIn() const { return getQualifier().builtIn != EbvNone; } + + virtual bool isAttachmentEXT() const { return basicType == EbtSampler && getSampler().isAttachmentEXT(); } + virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); } + virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); } + virtual bool isTexture() const { return basicType == EbtSampler && getSampler().isTexture(); } + virtual bool isBindlessImage() const { return isImage() && qualifier.layoutBindlessImage; } + virtual bool isBindlessTexture() const { return isTexture() && qualifier.layoutBindlessSampler; } + // Check the block-name convention of creating a block without populating it's members: + virtual bool isUnusableName() const { return isStruct() && structure == nullptr; } + virtual bool isParameterized() const { return typeParameters != nullptr; } + bool isAtomic() const { return basicType == EbtAtomicUint; } + bool isCoopMat() const { return coopmatNV || coopmatKHR; } + bool isCoopMatNV() const { return coopmatNV; } + bool isCoopMatKHR() const { return coopmatKHR; } + bool isCoopVecNV() const { return coopvecNV; } + bool isCoopMatOrVec() const { return isCoopMat() || isCoopVecNV(); } + bool isReference() const { return getBasicType() == EbtReference; } + bool isSpirvType() const { return getBasicType() == EbtSpirvType; } + int getCoopMatKHRuse() const { return static_cast(coopmatKHRuse); } + + bool isTensorLayoutNV() const { return getBasicType() == EbtTensorLayoutNV; } + bool isTensorViewNV() const { return getBasicType() == EbtTensorViewNV; } + + // return true if this type contains any subtype which satisfies the given predicate. + template + bool contains(P predicate) const + { + if (predicate(this)) + return true; + + const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); }; + + return isStruct() && std::any_of(structure->begin(), structure->end(), hasa); + } + + // Recursively checks if the type contains the given basic type + virtual bool containsBasicType(TBasicType checkType) const + { + return contains([checkType](const TType* t) { return t->basicType == checkType; } ); + } + + // Recursively check the structure for any arrays, needed for some error checks + virtual bool containsArray() const + { + return contains([](const TType* t) { return t->isArray(); } ); + } + + // Check the structure for any structures, needed for some error checks + virtual bool containsStructure() const + { + return contains([this](const TType* t) { return t != this && t->isStruct(); } ); + } + + // Recursively check the structure for any unsized arrays, needed for triggering a copyUp(). + virtual bool containsUnsizedArray() const + { + return contains([](const TType* t) { return t->isUnsizedArray(); } ); + } + + virtual bool containsOpaque() const + { + return contains([](const TType* t) { return t->isOpaque(); } ); + } + + virtual bool containsSampler() const + { + return contains([](const TType* t) { return t->isTexture() || t->isImage(); }); + } + + // Recursively checks if the type contains a built-in variable + virtual bool containsBuiltIn() const + { + return contains([](const TType* t) { return t->isBuiltIn(); } ); + } + + virtual bool containsNonOpaque() const + { + const auto nonOpaque = [](const TType* t) { + switch (t->basicType) { + case EbtVoid: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtReference: + return true; + default: + return false; + } + }; + + return contains(nonOpaque); + } + + virtual bool containsSpecializationSize() const + { + return contains([](const TType* t) { return t->isArray() && t->arraySizes->isOuterSpecialization(); } ); + } + + bool containsDouble() const + { + return containsBasicType(EbtDouble); + } + bool contains16BitFloat() const + { + return containsBasicType(EbtFloat16); + } + bool containsBFloat16() const + { + return containsBasicType(EbtBFloat16); + } + bool contains64BitInt() const + { + return containsBasicType(EbtInt64) || containsBasicType(EbtUint64); + } + bool contains16BitInt() const + { + return containsBasicType(EbtInt16) || containsBasicType(EbtUint16); + } + bool contains8BitInt() const + { + return containsBasicType(EbtInt8) || containsBasicType(EbtUint8); + } + bool containsCoopMat() const + { + return contains([](const TType* t) { return t->coopmatNV || t->coopmatKHR; } ); + } + bool containsCoopVec() const + { + return contains([](const TType* t) { return t->coopvecNV; } ); + } + bool containsReference() const + { + return containsBasicType(EbtReference); + } + + // Array editing methods. Array descriptors can be shared across + // type instances. This allows all uses of the same array + // to be updated at once. E.g., all nodes can be explicitly sized + // by tracking and correcting one implicit size. Or, all nodes + // can get the explicit size on a redeclaration that gives size. + // + // N.B.: Don't share with the shared symbol tables (symbols are + // marked as isReadOnly(). Such symbols with arrays that will be + // edited need to copyUp() on first use, so that + // A) the edits don't effect the shared symbol table, and + // B) the edits are shared across all users. + void updateArraySizes(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(arraySizes != nullptr); + assert(type.arraySizes != nullptr); + *arraySizes = *type.arraySizes; + } + void copyArraySizes(const TArraySizes& s) + { + // For setting a fresh new set of array sizes, not yet worrying about sharing. + arraySizes = new TArraySizes; + *arraySizes = s; + } + void transferArraySizes(TArraySizes* s) + { + // For setting an already allocated set of sizes that this type can use + // (no copy made). + arraySizes = s; + } + void clearArraySizes() + { + arraySizes = nullptr; + } + + // Add inner array sizes, to any existing sizes, via copy; the + // sizes passed in can still be reused for other purposes. + void copyArrayInnerSizes(const TArraySizes* s) + { + if (s != nullptr) { + if (arraySizes == nullptr) + copyArraySizes(*s); + else + arraySizes->addInnerSizes(*s); + } + } + void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); } + + // Recursively make the implicit array size the explicit array size. + // Expicit arrays are compile-time or link-time sized, never run-time sized. + // Sometimes, policy calls for an array to be run-time sized even if it was + // never variably indexed: Don't turn a 'skipNonvariablyIndexed' array into + // an explicit array. + void adoptImplicitArraySizes(bool skipNonvariablyIndexed) + { + if (isUnsizedArray() && + (qualifier.builtIn == EbvSampleMask || + !(skipNonvariablyIndexed || isArrayVariablyIndexed()))) { + changeOuterArraySize(getImplicitArraySize()); + setImplicitlySized(true); + } + // For multi-dim per-view arrays, set unsized inner dimension size to 1 + if (qualifier.isPerView() && arraySizes && arraySizes->isInnerUnsized()) + arraySizes->clearInnerUnsized(); + if (isStruct() && structure->size() > 0) { + int lastMember = (int)structure->size() - 1; + for (int i = 0; i < lastMember; ++i) + (*structure)[i].type->adoptImplicitArraySizes(false); + // implement the "last member of an SSBO" policy + (*structure)[lastMember].type->adoptImplicitArraySizes(getQualifier().storage == EvqBuffer); + } + } + + void copyTypeParameters(const TTypeParameters& s) + { + // For setting a fresh new set of type parameters, not yet worrying about sharing. + typeParameters = new TTypeParameters; + *typeParameters = s; + } + + const char* getBasicString() const + { + return TType::getBasicString(basicType); + } + + static const char* getBasicString(TBasicType t) + { + switch (t) { + case EbtFloat: return "float"; + case EbtInt: return "int"; + case EbtUint: return "uint"; + case EbtSampler: return "sampler/image"; + case EbtVoid: return "void"; + case EbtDouble: return "double"; + case EbtFloat16: return "float16_t"; + case EbtBFloat16: return "bfloat16_t"; + case EbtInt8: return "int8_t"; + case EbtUint8: return "uint8_t"; + case EbtInt16: return "int16_t"; + case EbtUint16: return "uint16_t"; + case EbtInt64: return "int64_t"; + case EbtUint64: return "uint64_t"; + case EbtBool: return "bool"; + case EbtAtomicUint: return "atomic_uint"; + case EbtStruct: return "structure"; + case EbtBlock: return "block"; + case EbtAccStruct: return "accelerationStructureNV"; + case EbtRayQuery: return "rayQueryEXT"; + case EbtReference: return "reference"; + case EbtString: return "string"; + case EbtSpirvType: return "spirv_type"; + case EbtCoopmat: return "coopmat"; + case EbtTensorLayoutNV: return "tensorLayoutNV"; + case EbtTensorViewNV: return "tensorViewNV"; + case EbtCoopvecNV: return "coopvecNV"; + default: return "unknown type"; + } + } + + TString getCompleteString(bool syntactic = false, bool getQualifiers = true, bool getPrecision = true, + bool getType = true, TString name = "", TString structName = "") const + { + TString typeString; + + const auto appendStr = [&](const char* s) { typeString.append(s); }; + const auto appendUint = [&](unsigned int u) { typeString.append(std::to_string(u).c_str()); }; + const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); }; + + if (getQualifiers) { + if (qualifier.hasSpirvDecorate()) + appendStr(qualifier.getSpirvDecorateQualifierString().c_str()); + + if (qualifier.hasLayout()) { + // To reduce noise, skip this if the only layout is an xfb_buffer + // with no triggering xfb_offset. + TQualifier noXfbBuffer = qualifier; + noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd; + if (noXfbBuffer.hasLayout()) { + appendStr("layout("); + if (qualifier.hasAnyLocation()) { + appendStr(" location="); + appendUint(qualifier.layoutLocation); + if (qualifier.hasComponent()) { + appendStr(" component="); + appendUint(qualifier.layoutComponent); + } + if (qualifier.hasIndex()) { + appendStr(" index="); + appendUint(qualifier.layoutIndex); + } + } + if (qualifier.hasSet()) { + appendStr(" set="); + appendUint(qualifier.layoutSet); + } + if (qualifier.hasBinding()) { + appendStr(" binding="); + appendUint(qualifier.layoutBinding); + } + if (qualifier.hasStream()) { + appendStr(" stream="); + appendUint(qualifier.layoutStream); + } + if (qualifier.hasMatrix()) { + appendStr(" "); + appendStr(TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); + } + if (qualifier.hasPacking()) { + appendStr(" "); + appendStr(TQualifier::getLayoutPackingString(qualifier.layoutPacking)); + } + if (qualifier.hasOffset()) { + appendStr(" offset="); + appendInt(qualifier.layoutOffset); + } + if (qualifier.hasAlign()) { + appendStr(" align="); + appendInt(qualifier.layoutAlign); + } + if (qualifier.hasFormat()) { + appendStr(" "); + appendStr(TQualifier::getLayoutFormatString(qualifier.layoutFormat)); + } + if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) { + appendStr(" xfb_buffer="); + appendUint(qualifier.layoutXfbBuffer); + } + if (qualifier.hasXfbOffset()) { + appendStr(" xfb_offset="); + appendUint(qualifier.layoutXfbOffset); + } + if (qualifier.hasXfbStride()) { + appendStr(" xfb_stride="); + appendUint(qualifier.layoutXfbStride); + } + if (qualifier.hasAttachment()) { + appendStr(" input_attachment_index="); + appendUint(qualifier.layoutAttachment); + } + if (qualifier.hasSpecConstantId()) { + appendStr(" constant_id="); + appendUint(qualifier.layoutSpecConstantId); + } + if (qualifier.layoutPushConstant) + appendStr(" push_constant"); + if (qualifier.layoutBufferReference) + appendStr(" buffer_reference"); + if (qualifier.hasBufferReferenceAlign()) { + appendStr(" buffer_reference_align="); + appendUint(1u << qualifier.layoutBufferReferenceAlign); + } + + if (qualifier.layoutPassthrough) + appendStr(" passthrough"); + if (qualifier.layoutViewportRelative) + appendStr(" layoutViewportRelative"); + if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) { + appendStr(" layoutSecondaryViewportRelativeOffset="); + appendInt(qualifier.layoutSecondaryViewportRelativeOffset); + } + + if (qualifier.layoutShaderRecord) + appendStr(" shaderRecordNV"); + if (qualifier.layoutFullQuads) + appendStr(" full_quads"); + if (qualifier.layoutQuadDeriv) + appendStr(" quad_derivatives"); + if (qualifier.layoutHitObjectShaderRecordNV) + appendStr(" hitobjectshaderrecordnv"); + + if (qualifier.layoutBindlessSampler) + appendStr(" layoutBindlessSampler"); + if (qualifier.layoutBindlessImage) + appendStr(" layoutBindlessImage"); + + appendStr(")"); + } + } + + if (qualifier.invariant) + appendStr(" invariant"); + if (qualifier.noContraction) + appendStr(" noContraction"); + if (qualifier.centroid) + appendStr(" centroid"); + if (qualifier.smooth) + appendStr(" smooth"); + if (qualifier.flat) + appendStr(" flat"); + if (qualifier.nopersp) + appendStr(" noperspective"); + if (qualifier.explicitInterp) + appendStr(" __explicitInterpAMD"); + if (qualifier.pervertexNV) + appendStr(" pervertexNV"); + if (qualifier.pervertexEXT) + appendStr(" pervertexEXT"); + if (qualifier.perPrimitiveNV) + appendStr(" perprimitiveNV"); + if (qualifier.perViewNV) + appendStr(" perviewNV"); + if (qualifier.perTaskNV) + appendStr(" taskNV"); + if (qualifier.patch) + appendStr(" patch"); + if (qualifier.sample) + appendStr(" sample"); + if (qualifier.coherent) + appendStr(" coherent"); + if (qualifier.devicecoherent) + appendStr(" devicecoherent"); + if (qualifier.queuefamilycoherent) + appendStr(" queuefamilycoherent"); + if (qualifier.workgroupcoherent) + appendStr(" workgroupcoherent"); + if (qualifier.subgroupcoherent) + appendStr(" subgroupcoherent"); + if (qualifier.shadercallcoherent) + appendStr(" shadercallcoherent"); + if (qualifier.nonprivate) + appendStr(" nonprivate"); + if (qualifier.volatil) + appendStr(" volatile"); + if (qualifier.nontemporal) + appendStr(" nontemporal"); + if (qualifier.restrict) + appendStr(" restrict"); + if (qualifier.readonly) + appendStr(" readonly"); + if (qualifier.writeonly) + appendStr(" writeonly"); + if (qualifier.specConstant) + appendStr(" specialization-constant"); + if (qualifier.nonUniform) + appendStr(" nonuniform"); + if (qualifier.isNullInit()) + appendStr(" null-init"); + if (qualifier.isSpirvByReference()) + appendStr(" spirv_by_reference"); + if (qualifier.isSpirvLiteral()) + appendStr(" spirv_literal"); + appendStr(" "); + appendStr(getStorageQualifierString()); + } + if (getType) { + if (syntactic) { + if (getPrecision && qualifier.precision != EpqNone) { + appendStr(" "); + appendStr(getPrecisionQualifierString()); + } + if (isVector() || isMatrix()) { + appendStr(" "); + switch (basicType) { + case EbtDouble: + appendStr("d"); + break; + case EbtInt: + appendStr("i"); + break; + case EbtUint: + appendStr("u"); + break; + case EbtBool: + appendStr("b"); + break; + case EbtFloat: + default: + break; + } + if (isVector()) { + appendStr("vec"); + appendInt(vectorSize); + } else { + appendStr("mat"); + appendInt(matrixCols); + appendStr("x"); + appendInt(matrixRows); + } + } else if (isStruct() && structure) { + appendStr(" "); + appendStr(structName.c_str()); + appendStr("{"); + bool hasHiddenMember = true; + for (size_t i = 0; i < structure->size(); ++i) { + if (!(*structure)[i].type->hiddenMember()) { + if (!hasHiddenMember) + appendStr(", "); + typeString.append((*structure)[i].type->getCompleteString(syntactic, getQualifiers, getPrecision, getType, (*structure)[i].type->getFieldName())); + hasHiddenMember = false; + } + } + appendStr("}"); + } else { + appendStr(" "); + switch (basicType) { + case EbtDouble: + appendStr("double"); + break; + case EbtInt: + appendStr("int"); + break; + case EbtUint: + appendStr("uint"); + break; + case EbtBool: + appendStr("bool"); + break; + case EbtFloat: + appendStr("float"); + break; + default: + appendStr("unexpected"); + break; + } + } + if (name.length() > 0) { + appendStr(" "); + appendStr(name.c_str()); + } + if (isArray()) { + for (int i = 0; i < (int)arraySizes->getNumDims(); ++i) { + int size = arraySizes->getDimSize(i); + if (size == UnsizedArraySize && i == 0 && arraySizes->isVariablyIndexed()) + appendStr("[]"); + else { + if (size == UnsizedArraySize) { + appendStr("["); + if (i == 0) + appendInt(arraySizes->getImplicitSize()); + appendStr("]"); + } + else { + appendStr("["); + appendInt(arraySizes->getDimSize(i)); + appendStr("]"); + } + } + } + } + } + else { + if (isArray()) { + for (int i = 0; i < (int)arraySizes->getNumDims(); ++i) { + int size = arraySizes->getDimSize(i); + if (size == UnsizedArraySize && i == 0 && arraySizes->isVariablyIndexed()) + appendStr(" runtime-sized array of"); + else { + if (size == UnsizedArraySize) { + appendStr(" unsized"); + if (i == 0) { + appendStr(" "); + appendInt(arraySizes->getImplicitSize()); + } + } + else { + appendStr(" "); + appendInt(arraySizes->getDimSize(i)); + } + appendStr("-element array of"); + } + } + } + if (isParameterized()) { + if (isCoopMatKHR()) { + appendStr(" "); + appendStr("coopmat"); + } + if (isTensorLayoutNV()) { + appendStr(" "); + appendStr("tensorLayoutNV"); + } + if (isTensorViewNV()) { + appendStr(" "); + appendStr("tensorViewNV"); + } + if (isCoopVecNV()) { + appendStr(" "); + appendStr("coopvecNV"); + } + + appendStr("<"); + for (int i = 0; i < (int)typeParameters->arraySizes->getNumDims(); ++i) { + appendInt(typeParameters->arraySizes->getDimSize(i)); + if (i != (int)typeParameters->arraySizes->getNumDims() - 1) + appendStr(", "); + } + appendStr(">"); + } + if (getPrecision && qualifier.precision != EpqNone) { + appendStr(" "); + appendStr(getPrecisionQualifierString()); + } + if (isMatrix()) { + appendStr(" "); + appendInt(matrixCols); + appendStr("X"); + appendInt(matrixRows); + appendStr(" matrix of"); + } + else if (isVector()) { + appendStr(" "); + appendInt(vectorSize); + appendStr("-component vector of"); + } + + appendStr(" "); + typeString.append(getBasicTypeString()); + + if (qualifier.builtIn != EbvNone) { + appendStr(" "); + appendStr(getBuiltInVariableString()); + } + + // Add struct/block members + if (isStruct() && structure) { + appendStr("{"); + bool hasHiddenMember = true; + for (size_t i = 0; i < structure->size(); ++i) { + if (!(*structure)[i].type->hiddenMember()) { + if (!hasHiddenMember) + appendStr(", "); + typeString.append((*structure)[i].type->getCompleteString()); + typeString.append(" "); + typeString.append((*structure)[i].type->getFieldName()); + hasHiddenMember = false; + } + } + appendStr("}"); + } + } + } + + return typeString; + } + + TString getBasicTypeString() const + { + if (basicType == EbtSampler) + return sampler.getString(); + else + return getBasicString(); + } + + const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); } + const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); } + const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } + + const TTypeList* getStruct() const { assert(isStruct()); return structure; } + void setStruct(TTypeList* s) { assert(isStruct()); structure = s; } + TTypeList* getWritableStruct() const { assert(isStruct()); return structure; } // This should only be used when known to not be sharing with other threads + void setBasicType(const TBasicType& t) { basicType = t; } + void setVectorSize(int s) { + assert(s >= 0); + vectorSize = static_cast(s) & 0b1111; + } + + int computeNumComponents() const + { + uint32_t components = 0; + + if (isCoopVecNV()) { + components = typeParameters->arraySizes->getDimSize(0); + } else if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) { + for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) + components += ((*tl).type)->computeNumComponents(); + } else if (matrixCols) + components = matrixCols * matrixRows; + else + components = vectorSize; + + if (arraySizes != nullptr) { + components *= arraySizes->getCumulativeSize(); + } + + return static_cast(components); + } + + // append this type's mangled name to the passed in 'name' + void appendMangledName(TString& name) const + { + buildMangledName(name); + name += ';' ; + } + + // These variables are inconsistently declared inside and outside of gl_PerVertex in glslang right now. + // They are declared inside of 'in gl_PerVertex', but sitting as standalone when they are 'out'puts. + bool isInconsistentGLPerVertexMember(const TString& name) const + { + if (name == "gl_SecondaryPositionNV" || + name == "gl_PositionPerViewNV") + return true; + return false; + } + + + // Do two structure types match? They could be declared independently, + // in different places, but still might satisfy the definition of matching. + // From the spec: + // + // "Structures must have the same name, sequence of type names, and + // type definitions, and member names to be considered the same type. + // This rule applies recursively for nested or embedded types." + // + // If type mismatch in structure, return member indices through lpidx and rpidx. + // If matching members for either block are exhausted, return -1 for exhausted + // block and the index of the unmatched member. Otherwise return {-1,-1}. + // + bool sameStructType(const TType& right, int* lpidx = nullptr, int* rpidx = nullptr) const + { + // Initialize error to general type mismatch. + if (lpidx != nullptr) { + *lpidx = -1; + *rpidx = -1; + } + + // Most commonly, they are both nullptr, or the same pointer to the same actual structure + // TODO: Why return true when neither types are structures? + if ((!isStruct() && !right.isStruct()) || + (isStruct() && right.isStruct() && structure == right.structure)) + return true; + + if (!isStruct() || !right.isStruct()) + return false; + + // Structure names have to match + if (*typeName != *right.typeName) + return false; + + // There are inconsistencies with how gl_PerVertex is setup. For now ignore those as errors if they + // are known inconsistencies. + bool isGLPerVertex = *typeName == "gl_PerVertex"; + + // Both being nullptr was caught above, now they both have to be structures of the same number of elements + if (lpidx == nullptr && + (structure->size() != right.structure->size() && !isGLPerVertex)) { + return false; + } + + // Compare the names and types of all the members, which have to match + for (size_t li = 0, ri = 0; li < structure->size() || ri < right.structure->size(); ++li, ++ri) { + if (lpidx != nullptr) { + *lpidx = static_cast(li); + *rpidx = static_cast(ri); + } + if (li < structure->size() && ri < right.structure->size()) { + if ((*structure)[li].type->getFieldName() == (*right.structure)[ri].type->getFieldName()) { + if (*(*structure)[li].type != *(*right.structure)[ri].type) + return false; + } else { + // Skip hidden members + if ((*structure)[li].type->hiddenMember()) { + ri--; + continue; + } else if ((*right.structure)[ri].type->hiddenMember()) { + li--; + continue; + } + // If one of the members is something that's inconsistently declared, skip over it + // for now. + if (isGLPerVertex) { + if (isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName())) { + ri--; + continue; + } else if (isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName())) { + li--; + continue; + } + } else { + return false; + } + } + // If we get here, then there should only be inconsistently declared members left + } else if (li < structure->size()) { + if (!(*structure)[li].type->hiddenMember() && !isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName())) { + if (lpidx != nullptr) { + *rpidx = -1; + } + return false; + } + } else { + if (!(*right.structure)[ri].type->hiddenMember() && !isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName())) { + if (lpidx != nullptr) { + *lpidx = -1; + } + return false; + } + } + } + + return true; + } + + bool sameReferenceType(const TType& right) const + { + if (isReference() != right.isReference()) + return false; + + if (!isReference() && !right.isReference()) + return true; + + assert(referentType != nullptr); + assert(right.referentType != nullptr); + + if (referentType == right.referentType) + return true; + + return *referentType == *right.referentType; + } + + // See if two types match, in all aspects except arrayness + // If mismatch in structure members, return member indices in lpidx and rpidx. + bool sameElementType(const TType& right, int* lpidx = nullptr, int* rpidx = nullptr) const + { + if (lpidx != nullptr) { + *lpidx = -1; + *rpidx = -1; + } + return basicType == right.basicType && sameElementShape(right, lpidx, rpidx); + } + + // See if two type's arrayness match + bool sameArrayness(const TType& right) const + { + return ((arraySizes == nullptr && right.arraySizes == nullptr) || + (arraySizes != nullptr && right.arraySizes != nullptr && + (*arraySizes == *right.arraySizes || + (arraySizes->isImplicitlySized() && right.arraySizes->isDefaultImplicitlySized()) || + (right.arraySizes->isImplicitlySized() && arraySizes->isDefaultImplicitlySized())))); + } + + // See if two type's arrayness match in everything except their outer dimension + bool sameInnerArrayness(const TType& right) const + { + assert(arraySizes != nullptr && right.arraySizes != nullptr); + return arraySizes->sameInnerArrayness(*right.arraySizes); + } + + // See if two type's parameters match + bool sameTypeParameters(const TType& right) const + { + return ((typeParameters == nullptr && right.typeParameters == nullptr) || + (typeParameters != nullptr && right.typeParameters != nullptr && *typeParameters == *right.typeParameters)); + } + + // See if two type's SPIR-V type contents match + bool sameSpirvType(const TType& right) const + { + return ((spirvType == nullptr && right.spirvType == nullptr) || + (spirvType != nullptr && right.spirvType != nullptr && *spirvType == *right.spirvType)); + } + + // See if two type's elements match in all ways except basic type + // If mismatch in structure members, return member indices in lpidx and rpidx. + bool sameElementShape(const TType& right, int* lpidx = nullptr, int* rpidx = nullptr) const + { + if (lpidx != nullptr) { + *lpidx = -1; + *rpidx = -1; + } + return ((basicType != EbtSampler && right.basicType != EbtSampler) || sampler == right.sampler) && + vectorSize == right.vectorSize && + matrixCols == right.matrixCols && + matrixRows == right.matrixRows && + vector1 == right.vector1 && + isCoopMatNV() == right.isCoopMatNV() && + isCoopMatKHR() == right.isCoopMatKHR() && + isCoopVecNV() == right.isCoopVecNV() && + sameStructType(right, lpidx, rpidx) && + sameReferenceType(right); + } + + // See if a cooperative matrix type parameter with unspecified parameters is + // an OK function parameter + bool coopMatParameterOK(const TType& right) const + { + if (isCoopMatNV()) { + return right.isCoopMatNV() && (getBasicType() == right.getBasicType()) && typeParameters == nullptr && + right.typeParameters != nullptr; + } + if (isCoopMatKHR() && right.isCoopMatKHR()) { + return ((getBasicType() == right.getBasicType()) || (getBasicType() == EbtCoopmat) || + (right.getBasicType() == EbtCoopmat)) && + ((typeParameters == nullptr && right.typeParameters != nullptr) || + (typeParameters != nullptr && right.typeParameters == nullptr)); + } + return false; + } + + // See if a cooperative vector type parameter with unspecified parameters is + // an OK function parameter + bool coopVecParameterOK(const TType& right) const + { + if (isCoopVecNV() && right.isCoopVecNV()) { + return ((getBasicType() == right.getBasicType()) || (getBasicType() == EbtCoopvecNV) || + (right.getBasicType() == EbtCoopvecNV)) && + typeParameters == nullptr && right.typeParameters != nullptr; + } + return false; + } + + bool sameCoopMatBaseType(const TType &right) const { + bool rv = false; + + if (isCoopMatNV()) { + rv = isCoopMatNV() && right.isCoopMatNV(); + if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16) + rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16; + else if (getBasicType() == EbtUint || getBasicType() == EbtUint8 || getBasicType() == EbtUint16) + rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8 || right.getBasicType() == EbtUint16; + else if (getBasicType() == EbtInt || getBasicType() == EbtInt8 || getBasicType() == EbtInt16) + rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8 || right.getBasicType() == EbtInt16; + else + rv = false; + } else if (isCoopMatKHR() && right.isCoopMatKHR()) { + if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16 || getBasicType() == EbtBFloat16) + rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16 || right.getBasicType() == EbtBFloat16 || right.getBasicType() == EbtCoopmat; + else if (getBasicType() == EbtUint || getBasicType() == EbtUint8 || getBasicType() == EbtUint16) + rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8 || right.getBasicType() == EbtUint16 || right.getBasicType() == EbtCoopmat; + else if (getBasicType() == EbtInt || getBasicType() == EbtInt8 || getBasicType() == EbtInt16) + rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8 || right.getBasicType() == EbtInt16 || right.getBasicType() == EbtCoopmat; + else + rv = false; + } + return rv; + } + + bool tensorParameterOK(const TType& right) const + { + if (isTensorLayoutNV()) { + return right.isTensorLayoutNV() && right.typeParameters == nullptr && typeParameters != nullptr; + } + if (isTensorViewNV()) { + return right.isTensorViewNV() && right.typeParameters == nullptr && typeParameters != nullptr; + } + return false; + } + + bool sameCoopVecBaseType(const TType &right) const { + bool rv = false; + + if (isCoopVecNV() && right.isCoopVecNV()) { + if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16) + rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16 || right.getBasicType() == EbtCoopvecNV; + else if (getBasicType() == EbtUint || getBasicType() == EbtUint8 || getBasicType() == EbtUint16) + rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8 || right.getBasicType() == EbtUint16 || right.getBasicType() == EbtCoopvecNV; + else if (getBasicType() == EbtInt || getBasicType() == EbtInt8 || getBasicType() == EbtInt16) + rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8 || right.getBasicType() == EbtInt16 || right.getBasicType() == EbtCoopvecNV; + else + rv = false; + } + return rv; + } + + bool sameCoopMatUse(const TType &right) const { + return coopmatKHRuse == right.coopmatKHRuse; + } + + bool sameCoopMatShape(const TType &right) const + { + if (!isCoopMat() || !right.isCoopMat() || isCoopMatKHR() != right.isCoopMatKHR()) + return false; + + // Skip bit width type parameter (first array size) for coopmatNV + int firstArrayDimToCompare = isCoopMatNV() ? 1 : 0; + int lastArrayDimToCompare = typeParameters->arraySizes->getNumDims() - (isCoopMatKHR() ? 1 : 0); + for (int i = firstArrayDimToCompare; i < lastArrayDimToCompare; ++i) { + if (typeParameters->arraySizes->getDimSize(i) != right.typeParameters->arraySizes->getDimSize(i)) + return false; + } + return true; + } + + bool sameCoopMatShapeAndUse(const TType &right) const + { + if (!sameCoopMatShape(right)) + return false; + + if (coopmatKHRuse != right.coopmatKHRuse) + return false; + + return true; + } + + // See if two types match in all ways (just the actual type, not qualification) + bool operator==(const TType& right) const + { + return sameElementType(right) && sameArrayness(right) && sameTypeParameters(right) && sameCoopMatUse(right) && sameSpirvType(right); + } + + bool operator!=(const TType& right) const + { + return ! operator==(right); + } + + unsigned int getBufferReferenceAlignment() const + { + if (getBasicType() == glslang::EbtReference) { + return getReferentType()->getQualifier().hasBufferReferenceAlign() ? + (1u << getReferentType()->getQualifier().layoutBufferReferenceAlign) : 16u; + } + return 0; + } + + const TSpirvType& getSpirvType() const { assert(spirvType); return *spirvType; } + +protected: + // Require consumer to pick between deep copy and shallow copy. + TType(const TType& type); + TType& operator=(const TType& type); + + // Recursively copy a type graph, while preserving the graph-like + // quality. That is, don't make more than one copy of a structure that + // gets reused multiple times in the type graph. + void deepCopy(const TType& copyOf, TMap& copiedMap) + { + shallowCopy(copyOf); + + // GL_EXT_spirv_intrinsics + if (copyOf.qualifier.spirvDecorate) { + qualifier.spirvDecorate = new TSpirvDecorate; + *qualifier.spirvDecorate = *copyOf.qualifier.spirvDecorate; + } + + if (copyOf.spirvType) { + spirvType = new TSpirvType; + *spirvType = *copyOf.spirvType; + } + + if (copyOf.arraySizes) { + arraySizes = new TArraySizes; + *arraySizes = *copyOf.arraySizes; + } + + if (copyOf.typeParameters) { + typeParameters = new TTypeParameters; + typeParameters->arraySizes = new TArraySizes; + *typeParameters->arraySizes = *copyOf.typeParameters->arraySizes; + if (copyOf.typeParameters->spirvType) { + *typeParameters->spirvType = *copyOf.typeParameters->spirvType; + } + typeParameters->basicType = copyOf.basicType; + } + + if (copyOf.isStruct() && copyOf.structure) { + auto prevCopy = copiedMap.find(copyOf.structure); + if (prevCopy != copiedMap.end()) + structure = prevCopy->second; + else { + structure = new TTypeList; + copiedMap[copyOf.structure] = structure; + for (unsigned int i = 0; i < copyOf.structure->size(); ++i) { + TTypeLoc typeLoc; + typeLoc.loc = (*copyOf.structure)[i].loc; + typeLoc.type = new TType(); + typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap); + structure->push_back(typeLoc); + } + } + } + + if (copyOf.fieldName) + fieldName = NewPoolTString(copyOf.fieldName->c_str()); + if (copyOf.typeName) + typeName = NewPoolTString(copyOf.typeName->c_str()); + } + + + void buildMangledName(TString&) const; + + TBasicType basicType : 8; + uint32_t vectorSize : 4; // 1 means either scalar or 1-component vector; see vector1 to disambiguate. + uint32_t matrixCols : 4; + uint32_t matrixRows : 4; + bool vector1 : 1; // Backward-compatible tracking of a 1-component vector distinguished from a scalar. + // GLSL 4.5 never has a 1-component vector; so this will always be false until such + // functionality is added. + // HLSL does have a 1-component vectors, so this will be true to disambiguate + // from a scalar. + bool coopmatNV : 1; + bool coopmatKHR : 1; + uint32_t coopmatKHRuse : 3; // Accepts one of three values: 0, 1, 2 (gl_MatrixUseA, gl_MatrixUseB, gl_MatrixUseAccumulator) + bool coopmatKHRUseValid : 1; // True if coopmatKHRuse has been set + bool coopvecNV : 1; + TQualifier qualifier; + + TArraySizes* arraySizes; // nullptr unless an array; can be shared across types + // A type can't be both a structure (EbtStruct/EbtBlock) and a reference (EbtReference), so + // conserve space by making these a union + union { + TTypeList* structure; // invalid unless this is a struct; can be shared across types + TType *referentType; // invalid unless this is an EbtReference + }; + TString *fieldName; // for structure field names + TString *typeName; // for structure type name + TSampler sampler; + TTypeParameters *typeParameters;// nullptr unless a parameterized type; can be shared across types + TSpirvType* spirvType; // SPIR-V type defined by spirv_type directive +}; + +} // end namespace glslang + +#endif // _TYPES_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/arrays.h b/engine/engine/3rdlibs/glslang/glslang/Include/arrays.h new file mode 100644 index 00000000..91e19083 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/arrays.h @@ -0,0 +1,362 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement types for tracking GLSL arrays, arrays of arrays, etc. +// + +#ifndef _ARRAYS_INCLUDED +#define _ARRAYS_INCLUDED + +#include + +namespace glslang { + +// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else. +const int UnsizedArraySize = 0; + +class TIntermTyped; +extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*); + +// Specialization constants need both a nominal size and a node that defines +// the specialization constant being used. Array types are the same when their +// size and specialization constant nodes are the same. +struct TArraySize { + unsigned int size; + TIntermTyped* node; // nullptr means no specialization constant node + bool operator==(const TArraySize& rhs) const + { + if (size != rhs.size) + return false; + if (node == nullptr || rhs.node == nullptr) + return node == rhs.node; + + return SameSpecializationConstants(node, rhs.node); + } +}; + +// +// TSmallArrayVector is used as the container for the set of sizes in TArraySizes. +// It has generic-container semantics, while TArraySizes has array-of-array semantics. +// That is, TSmallArrayVector should be more focused on mechanism and TArraySizes on policy. +// +struct TSmallArrayVector { + // + // TODO: memory: TSmallArrayVector is intended to be smaller. + // Almost all arrays could be handled by two sizes each fitting + // in 16 bits, needing a real vector only in the cases where there + // are more than 3 sizes or a size needing more than 16 bits. + // + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSmallArrayVector() : sizes(nullptr) { } + virtual ~TSmallArrayVector() { dealloc(); } + + // For breaking into two non-shared copies, independently modifiable. + TSmallArrayVector& operator=(const TSmallArrayVector& from) + { + if (from.sizes == nullptr) + sizes = nullptr; + else { + alloc(); + *sizes = *from.sizes; + } + + return *this; + } + + int size() const + { + if (sizes == nullptr) + return 0; + return (int)sizes->size(); + } + + unsigned int frontSize() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().size; + } + + TIntermTyped* frontNode() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().node; + } + + void changeFront(unsigned int s) + { + assert(sizes != nullptr); + // this should only happen for implicitly sized arrays, not specialization constants + assert(sizes->front().node == nullptr); + sizes->front().size = s; + } + + void push_back(unsigned int e, TIntermTyped* n) + { + alloc(); + TArraySize pair = { e, n }; + sizes->push_back(pair); + } + + void push_back(const TSmallArrayVector& newDims) + { + alloc(); + sizes->insert(sizes->end(), newDims.sizes->begin(), newDims.sizes->end()); + } + + void pop_front() + { + assert(sizes != nullptr && sizes->size() > 0); + if (sizes->size() == 1) + dealloc(); + else + sizes->erase(sizes->begin()); + } + + void pop_back() + { + assert(sizes != nullptr && sizes->size() > 0); + if (sizes->size() == 1) + dealloc(); + else + sizes->resize(sizes->size() - 1); + } + + // 'this' should currently not be holding anything, and copyNonFront + // will make it hold a copy of all but the first element of rhs. + // (This would be useful for making a type that is dereferenced by + // one dimension.) + void copyNonFront(const TSmallArrayVector& rhs) + { + assert(sizes == nullptr); + if (rhs.size() > 1) { + alloc(); + sizes->insert(sizes->begin(), rhs.sizes->begin() + 1, rhs.sizes->end()); + } + } + + unsigned int getDimSize(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].size; + } + + void setDimSize(int i, unsigned int size) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + assert((*sizes)[i].node == nullptr); + (*sizes)[i].size = size; + } + + TIntermTyped* getDimNode(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].node; + } + + bool operator==(const TSmallArrayVector& rhs) const + { + if (sizes == nullptr && rhs.sizes == nullptr) + return true; + if (sizes == nullptr || rhs.sizes == nullptr) + return false; + return *sizes == *rhs.sizes; + } + bool operator!=(const TSmallArrayVector& rhs) const { return ! operator==(rhs); } + +protected: + TSmallArrayVector(const TSmallArrayVector&); + + void alloc() + { + if (sizes == nullptr) + sizes = new TVector; + } + void dealloc() + { + delete sizes; + sizes = nullptr; + } + + TVector* sizes; // will either hold such a pointer, or in the future, hold the two array sizes +}; + +// +// Represent an array, or array of arrays, to arbitrary depth. This is not +// done through a hierarchy of types in a type tree, rather all contiguous arrayness +// in the type hierarchy is localized into this single cumulative object. +// +// The arrayness in TTtype is a pointer, so that it can be non-allocated and zero +// for the vast majority of types that are non-array types. +// +// Order Policy: these are all identical: +// - left to right order within a contiguous set of ...[..][..][..]... in the source language +// - index order 0, 1, 2, ... within the 'sizes' member below +// - outer-most to inner-most +// +struct TArraySizes { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TArraySizes() : implicitArraySize(0), implicitlySized(true), variablyIndexed(false){ } + + // For breaking into two non-shared copies, independently modifiable. + TArraySizes& operator=(const TArraySizes& from) + { + implicitArraySize = from.implicitArraySize; + variablyIndexed = from.variablyIndexed; + sizes = from.sizes; + implicitlySized = from.implicitlySized; + + return *this; + } + + // translate from array-of-array semantics to container semantics + int getNumDims() const { return sizes.size(); } + int getDimSize(int dim) const { return sizes.getDimSize(dim); } + TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); } + void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); } + int getOuterSize() const { return sizes.frontSize(); } + TIntermTyped* getOuterNode() const { return sizes.frontNode(); } + int getCumulativeSize() const + { + int size = 1; + for (int d = 0; d < sizes.size(); ++d) { + // this only makes sense in paths that have a known array size + assert(sizes.getDimSize(d) != UnsizedArraySize); + size *= sizes.getDimSize(d); + } + return size; + } + void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); } + void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); } + void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); } + void addInnerSize(TArraySize pair) { + sizes.push_back(pair.size, pair.node); + implicitlySized = false; + } + void addInnerSizes(const TArraySizes& s) { sizes.push_back(s.sizes); } + void changeOuterSize(int s) { + sizes.changeFront((unsigned)s); + implicitlySized = false; + } + int getImplicitSize() const { return implicitArraySize > 0 ? implicitArraySize : 1; } + void updateImplicitSize(int s) { + implicitArraySize = (std::max)(implicitArraySize, s); + } + bool isInnerUnsized() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + return true; + } + + return false; + } + bool clearInnerUnsized() + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + setDimSize(d, 1); + } + + return false; + } + bool isInnerSpecialization() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimNode(d) != nullptr) + return true; + } + + return false; + } + bool isOuterSpecialization() + { + return sizes.getDimNode(0) != nullptr; + } + + bool hasUnsized() const { return getOuterSize() == UnsizedArraySize || isInnerUnsized(); } + bool isSized() const { return getOuterSize() != UnsizedArraySize; } + bool isImplicitlySized() const { return implicitlySized; } + bool isDefaultImplicitlySized() const { return implicitlySized && implicitArraySize == 0; } + void setImplicitlySized(bool isImplicitSizing) { implicitlySized = isImplicitSizing; } + void dereference() { sizes.pop_front(); } + void removeLastSize() { sizes.pop_back(); } + void copyDereferenced(const TArraySizes& rhs) + { + assert(sizes.size() == 0); + if (rhs.sizes.size() > 1) + sizes.copyNonFront(rhs.sizes); + } + + bool sameInnerArrayness(const TArraySizes& rhs) const + { + if (sizes.size() != rhs.sizes.size()) + return false; + + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) || + sizes.getDimNode(d) != rhs.sizes.getDimNode(d)) + return false; + } + + return true; + } + + void setVariablyIndexed() { variablyIndexed = true; } + bool isVariablyIndexed() const { return variablyIndexed; } + + bool operator==(const TArraySizes& rhs) const { return sizes == rhs.sizes; } + bool operator!=(const TArraySizes& rhs) const { return sizes != rhs.sizes; } + +protected: + TSmallArrayVector sizes; + + TArraySizes(const TArraySizes&); + + // For tracking maximum referenced compile-time constant index. + // Applies only to the outer-most dimension. Potentially becomes + // the implicit size of the array, if not variably indexed and + // otherwise legal. + int implicitArraySize; + bool implicitlySized; + bool variablyIndexed; // true if array is indexed with a non compile-time constant +}; + +} // end namespace glslang + +#endif // _ARRAYS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_interface.h b/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_interface.h new file mode 100644 index 00000000..06120219 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_interface.h @@ -0,0 +1,297 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef GLSLANG_C_IFACE_H_INCLUDED +#define GLSLANG_C_IFACE_H_INCLUDED + +#include +#include + +#include "glslang_c_shader_types.h" +#include "visibility.h" + +typedef struct glslang_shader_s glslang_shader_t; +typedef struct glslang_program_s glslang_program_t; +typedef struct glslang_mapper_s glslang_mapper_t; +typedef struct glslang_resolver_s glslang_resolver_t; + +/* Version counterpart */ +typedef struct glslang_version_s { + int major; + int minor; + int patch; + const char* flavor; +} glslang_version_t; + +/* TLimits counterpart */ +typedef struct glslang_limits_s { + bool non_inductive_for_loops; + bool while_loops; + bool do_while_loops; + bool general_uniform_indexing; + bool general_attribute_matrix_vector_indexing; + bool general_varying_indexing; + bool general_sampler_indexing; + bool general_variable_indexing; + bool general_constant_matrix_vector_indexing; +} glslang_limits_t; + +/* TBuiltInResource counterpart */ +typedef struct glslang_resource_s { + int max_lights; + int max_clip_planes; + int max_texture_units; + int max_texture_coords; + int max_vertex_attribs; + int max_vertex_uniform_components; + int max_varying_floats; + int max_vertex_texture_image_units; + int max_combined_texture_image_units; + int max_texture_image_units; + int max_fragment_uniform_components; + int max_draw_buffers; + int max_vertex_uniform_vectors; + int max_varying_vectors; + int max_fragment_uniform_vectors; + int max_vertex_output_vectors; + int max_fragment_input_vectors; + int min_program_texel_offset; + int max_program_texel_offset; + int max_clip_distances; + int max_compute_work_group_count_x; + int max_compute_work_group_count_y; + int max_compute_work_group_count_z; + int max_compute_work_group_size_x; + int max_compute_work_group_size_y; + int max_compute_work_group_size_z; + int max_compute_uniform_components; + int max_compute_texture_image_units; + int max_compute_image_uniforms; + int max_compute_atomic_counters; + int max_compute_atomic_counter_buffers; + int max_varying_components; + int max_vertex_output_components; + int max_geometry_input_components; + int max_geometry_output_components; + int max_fragment_input_components; + int max_image_units; + int max_combined_image_units_and_fragment_outputs; + int max_combined_shader_output_resources; + int max_image_samples; + int max_vertex_image_uniforms; + int max_tess_control_image_uniforms; + int max_tess_evaluation_image_uniforms; + int max_geometry_image_uniforms; + int max_fragment_image_uniforms; + int max_combined_image_uniforms; + int max_geometry_texture_image_units; + int max_geometry_output_vertices; + int max_geometry_total_output_components; + int max_geometry_uniform_components; + int max_geometry_varying_components; + int max_tess_control_input_components; + int max_tess_control_output_components; + int max_tess_control_texture_image_units; + int max_tess_control_uniform_components; + int max_tess_control_total_output_components; + int max_tess_evaluation_input_components; + int max_tess_evaluation_output_components; + int max_tess_evaluation_texture_image_units; + int max_tess_evaluation_uniform_components; + int max_tess_patch_components; + int max_patch_vertices; + int max_tess_gen_level; + int max_viewports; + int max_vertex_atomic_counters; + int max_tess_control_atomic_counters; + int max_tess_evaluation_atomic_counters; + int max_geometry_atomic_counters; + int max_fragment_atomic_counters; + int max_combined_atomic_counters; + int max_atomic_counter_bindings; + int max_vertex_atomic_counter_buffers; + int max_tess_control_atomic_counter_buffers; + int max_tess_evaluation_atomic_counter_buffers; + int max_geometry_atomic_counter_buffers; + int max_fragment_atomic_counter_buffers; + int max_combined_atomic_counter_buffers; + int max_atomic_counter_buffer_size; + int max_transform_feedback_buffers; + int max_transform_feedback_interleaved_components; + int max_cull_distances; + int max_combined_clip_and_cull_distances; + int max_samples; + int max_mesh_output_vertices_nv; + int max_mesh_output_primitives_nv; + int max_mesh_work_group_size_x_nv; + int max_mesh_work_group_size_y_nv; + int max_mesh_work_group_size_z_nv; + int max_task_work_group_size_x_nv; + int max_task_work_group_size_y_nv; + int max_task_work_group_size_z_nv; + int max_mesh_view_count_nv; + int max_mesh_output_vertices_ext; + int max_mesh_output_primitives_ext; + int max_mesh_work_group_size_x_ext; + int max_mesh_work_group_size_y_ext; + int max_mesh_work_group_size_z_ext; + int max_task_work_group_size_x_ext; + int max_task_work_group_size_y_ext; + int max_task_work_group_size_z_ext; + int max_mesh_view_count_ext; + union + { + int max_dual_source_draw_buffers_ext; + + /* Incorrectly capitalized name retained for backward compatibility */ + int maxDualSourceDrawBuffersEXT; + }; + + glslang_limits_t limits; +} glslang_resource_t; + +/* Inclusion result structure allocated by C include_local/include_system callbacks */ +typedef struct glsl_include_result_s { + /* Header file name or NULL if inclusion failed */ + const char* header_name; + + /* Header contents or NULL */ + const char* header_data; + size_t header_length; + +} glsl_include_result_t; + +/* Callback for local file inclusion */ +typedef glsl_include_result_t* (*glsl_include_local_func)(void* ctx, const char* header_name, const char* includer_name, + size_t include_depth); + +/* Callback for system file inclusion */ +typedef glsl_include_result_t* (*glsl_include_system_func)(void* ctx, const char* header_name, + const char* includer_name, size_t include_depth); + +/* Callback for include result destruction */ +typedef int (*glsl_free_include_result_func)(void* ctx, glsl_include_result_t* result); + +/* Collection of callbacks for GLSL preprocessor */ +typedef struct glsl_include_callbacks_s { + glsl_include_system_func include_system; + glsl_include_local_func include_local; + glsl_free_include_result_func free_include_result; +} glsl_include_callbacks_t; + +typedef struct glslang_input_s { + glslang_source_t language; + glslang_stage_t stage; + glslang_client_t client; + glslang_target_client_version_t client_version; + glslang_target_language_t target_language; + glslang_target_language_version_t target_language_version; + /** Shader source code */ + const char* code; + int default_version; + glslang_profile_t default_profile; + int force_default_version_and_profile; + int forward_compatible; + glslang_messages_t messages; + const glslang_resource_t* resource; + glsl_include_callbacks_t callbacks; + void* callbacks_ctx; +} glslang_input_t; + +/* SpvOptions counterpart */ +typedef struct glslang_spv_options_s { + bool generate_debug_info; + bool strip_debug_info; + bool disable_optimizer; + bool optimize_size; + bool disassemble; + bool validate; + bool emit_nonsemantic_shader_debug_info; + bool emit_nonsemantic_shader_debug_source; + bool compile_only; + bool optimize_allow_expanded_id_bound; +} glslang_spv_options_t; + +#ifdef __cplusplus +extern "C" { +#endif + +GLSLANG_EXPORT void glslang_get_version(glslang_version_t* version); + +GLSLANG_EXPORT int glslang_initialize_process(void); +GLSLANG_EXPORT void glslang_finalize_process(void); + +GLSLANG_EXPORT glslang_shader_t* glslang_shader_create(const glslang_input_t* input); +GLSLANG_EXPORT void glslang_shader_delete(glslang_shader_t* shader); +GLSLANG_EXPORT void glslang_shader_set_preamble(glslang_shader_t* shader, const char* s); +GLSLANG_EXPORT void glslang_shader_shift_binding(glslang_shader_t* shader, glslang_resource_type_t res, unsigned int base); +GLSLANG_EXPORT void glslang_shader_shift_binding_for_set(glslang_shader_t* shader, glslang_resource_type_t res, unsigned int base, unsigned int set); +GLSLANG_EXPORT void glslang_shader_set_options(glslang_shader_t* shader, int options); // glslang_shader_options_t +GLSLANG_EXPORT void glslang_shader_set_glsl_version(glslang_shader_t* shader, int version); +GLSLANG_EXPORT void glslang_shader_set_default_uniform_block_set_and_binding(glslang_shader_t* shader, unsigned int set, unsigned int binding); +GLSLANG_EXPORT void glslang_shader_set_default_uniform_block_name(glslang_shader_t* shader, const char *name); +GLSLANG_EXPORT void glslang_shader_set_resource_set_binding(glslang_shader_t* shader, const char *const *bindings, unsigned int num_bindings); +GLSLANG_EXPORT int glslang_shader_preprocess(glslang_shader_t* shader, const glslang_input_t* input); +GLSLANG_EXPORT int glslang_shader_parse(glslang_shader_t* shader, const glslang_input_t* input); +GLSLANG_EXPORT const char* glslang_shader_get_preprocessed_code(glslang_shader_t* shader); +GLSLANG_EXPORT void glslang_shader_set_preprocessed_code(glslang_shader_t* shader, const char* code); +GLSLANG_EXPORT const char* glslang_shader_get_info_log(glslang_shader_t* shader); +GLSLANG_EXPORT const char* glslang_shader_get_info_debug_log(glslang_shader_t* shader); + +GLSLANG_EXPORT glslang_program_t* glslang_program_create(void); +GLSLANG_EXPORT void glslang_program_delete(glslang_program_t* program); +GLSLANG_EXPORT void glslang_program_add_shader(glslang_program_t* program, glslang_shader_t* shader); +GLSLANG_EXPORT int glslang_program_link(glslang_program_t* program, int messages); // glslang_messages_t +GLSLANG_EXPORT void glslang_program_add_source_text(glslang_program_t* program, glslang_stage_t stage, const char* text, size_t len); +GLSLANG_EXPORT void glslang_program_set_source_file(glslang_program_t* program, glslang_stage_t stage, const char* file); +GLSLANG_EXPORT int glslang_program_map_io(glslang_program_t* program); +GLSLANG_EXPORT int glslang_program_map_io_with_resolver_and_mapper(glslang_program_t* program, glslang_resolver_t* resolver, glslang_mapper_t* mapper); +GLSLANG_EXPORT void glslang_program_SPIRV_generate(glslang_program_t* program, glslang_stage_t stage); +GLSLANG_EXPORT void glslang_program_SPIRV_generate_with_options(glslang_program_t* program, glslang_stage_t stage, glslang_spv_options_t* spv_options); +GLSLANG_EXPORT size_t glslang_program_SPIRV_get_size(glslang_program_t* program); +GLSLANG_EXPORT void glslang_program_SPIRV_get(glslang_program_t* program, unsigned int*); +GLSLANG_EXPORT unsigned int* glslang_program_SPIRV_get_ptr(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_SPIRV_get_messages(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_get_info_log(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_get_info_debug_log(glslang_program_t* program); + +GLSLANG_EXPORT glslang_mapper_t* glslang_glsl_mapper_create(void); +GLSLANG_EXPORT void glslang_glsl_mapper_delete(glslang_mapper_t* mapper); + +GLSLANG_EXPORT glslang_resolver_t* glslang_glsl_resolver_create(glslang_program_t* program, glslang_stage_t stage); +GLSLANG_EXPORT void glslang_glsl_resolver_delete(glslang_resolver_t* resolver); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifdef GLSLANG_C_IFACE_INCLUDED */ diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_shader_types.h b/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_shader_types.h new file mode 100644 index 00000000..34bfbdb9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/glslang_c_shader_types.h @@ -0,0 +1,232 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef C_SHADER_TYPES_H_INCLUDED +#define C_SHADER_TYPES_H_INCLUDED + +#define LAST_ELEMENT_MARKER(x) x + +/* EShLanguage counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX, + GLSLANG_STAGE_TESSCONTROL, + GLSLANG_STAGE_TESSEVALUATION, + GLSLANG_STAGE_GEOMETRY, + GLSLANG_STAGE_FRAGMENT, + GLSLANG_STAGE_COMPUTE, + GLSLANG_STAGE_RAYGEN, + GLSLANG_STAGE_RAYGEN_NV = GLSLANG_STAGE_RAYGEN, + GLSLANG_STAGE_INTERSECT, + GLSLANG_STAGE_INTERSECT_NV = GLSLANG_STAGE_INTERSECT, + GLSLANG_STAGE_ANYHIT, + GLSLANG_STAGE_ANYHIT_NV = GLSLANG_STAGE_ANYHIT, + GLSLANG_STAGE_CLOSESTHIT, + GLSLANG_STAGE_CLOSESTHIT_NV = GLSLANG_STAGE_CLOSESTHIT, + GLSLANG_STAGE_MISS, + GLSLANG_STAGE_MISS_NV = GLSLANG_STAGE_MISS, + GLSLANG_STAGE_CALLABLE, + GLSLANG_STAGE_CALLABLE_NV = GLSLANG_STAGE_CALLABLE, + GLSLANG_STAGE_TASK, + GLSLANG_STAGE_TASK_NV = GLSLANG_STAGE_TASK, + GLSLANG_STAGE_MESH, + GLSLANG_STAGE_MESH_NV = GLSLANG_STAGE_MESH, + LAST_ELEMENT_MARKER(GLSLANG_STAGE_COUNT), +} glslang_stage_t; // would be better as stage, but this is ancient now + +/* EShLanguageMask counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX_MASK = (1 << GLSLANG_STAGE_VERTEX), + GLSLANG_STAGE_TESSCONTROL_MASK = (1 << GLSLANG_STAGE_TESSCONTROL), + GLSLANG_STAGE_TESSEVALUATION_MASK = (1 << GLSLANG_STAGE_TESSEVALUATION), + GLSLANG_STAGE_GEOMETRY_MASK = (1 << GLSLANG_STAGE_GEOMETRY), + GLSLANG_STAGE_FRAGMENT_MASK = (1 << GLSLANG_STAGE_FRAGMENT), + GLSLANG_STAGE_COMPUTE_MASK = (1 << GLSLANG_STAGE_COMPUTE), + GLSLANG_STAGE_RAYGEN_MASK = (1 << GLSLANG_STAGE_RAYGEN), + GLSLANG_STAGE_RAYGEN_NV_MASK = GLSLANG_STAGE_RAYGEN_MASK, + GLSLANG_STAGE_INTERSECT_MASK = (1 << GLSLANG_STAGE_INTERSECT), + GLSLANG_STAGE_INTERSECT_NV_MASK = GLSLANG_STAGE_INTERSECT_MASK, + GLSLANG_STAGE_ANYHIT_MASK = (1 << GLSLANG_STAGE_ANYHIT), + GLSLANG_STAGE_ANYHIT_NV_MASK = GLSLANG_STAGE_ANYHIT_MASK, + GLSLANG_STAGE_CLOSESTHIT_MASK = (1 << GLSLANG_STAGE_CLOSESTHIT), + GLSLANG_STAGE_CLOSESTHIT_NV_MASK = GLSLANG_STAGE_CLOSESTHIT_MASK, + GLSLANG_STAGE_MISS_MASK = (1 << GLSLANG_STAGE_MISS), + GLSLANG_STAGE_MISS_NV_MASK = GLSLANG_STAGE_MISS_MASK, + GLSLANG_STAGE_CALLABLE_MASK = (1 << GLSLANG_STAGE_CALLABLE), + GLSLANG_STAGE_CALLABLE_NV_MASK = GLSLANG_STAGE_CALLABLE_MASK, + GLSLANG_STAGE_TASK_MASK = (1 << GLSLANG_STAGE_TASK), + GLSLANG_STAGE_TASK_NV_MASK = GLSLANG_STAGE_TASK_MASK, + GLSLANG_STAGE_MESH_MASK = (1 << GLSLANG_STAGE_MESH), + GLSLANG_STAGE_MESH_NV_MASK = GLSLANG_STAGE_MESH_MASK, + LAST_ELEMENT_MARKER(GLSLANG_STAGE_MASK_COUNT), +} glslang_stage_mask_t; + +/* EShSource counterpart */ +typedef enum { + GLSLANG_SOURCE_NONE, + GLSLANG_SOURCE_GLSL, + GLSLANG_SOURCE_HLSL, + LAST_ELEMENT_MARKER(GLSLANG_SOURCE_COUNT), +} glslang_source_t; + +/* EShClient counterpart */ +typedef enum { + GLSLANG_CLIENT_NONE, + GLSLANG_CLIENT_VULKAN, + GLSLANG_CLIENT_OPENGL, + LAST_ELEMENT_MARKER(GLSLANG_CLIENT_COUNT), +} glslang_client_t; + +/* EShTargetLanguage counterpart */ +typedef enum { + GLSLANG_TARGET_NONE, + GLSLANG_TARGET_SPV, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_COUNT), +} glslang_target_language_t; + +/* SH_TARGET_ClientVersion counterpart */ +typedef enum { + GLSLANG_TARGET_VULKAN_1_0 = (1 << 22), + GLSLANG_TARGET_VULKAN_1_1 = (1 << 22) | (1 << 12), + GLSLANG_TARGET_VULKAN_1_2 = (1 << 22) | (2 << 12), + GLSLANG_TARGET_VULKAN_1_3 = (1 << 22) | (3 << 12), + GLSLANG_TARGET_VULKAN_1_4 = (1 << 22) | (4 << 12), + GLSLANG_TARGET_OPENGL_450 = 450, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_CLIENT_VERSION_COUNT = 6), +} glslang_target_client_version_t; + +/* SH_TARGET_LanguageVersion counterpart */ +typedef enum { + GLSLANG_TARGET_SPV_1_0 = (1 << 16), + GLSLANG_TARGET_SPV_1_1 = (1 << 16) | (1 << 8), + GLSLANG_TARGET_SPV_1_2 = (1 << 16) | (2 << 8), + GLSLANG_TARGET_SPV_1_3 = (1 << 16) | (3 << 8), + GLSLANG_TARGET_SPV_1_4 = (1 << 16) | (4 << 8), + GLSLANG_TARGET_SPV_1_5 = (1 << 16) | (5 << 8), + GLSLANG_TARGET_SPV_1_6 = (1 << 16) | (6 << 8), + LAST_ELEMENT_MARKER(GLSLANG_TARGET_LANGUAGE_VERSION_COUNT = 7), +} glslang_target_language_version_t; + +/* EShExecutable counterpart */ +typedef enum { GLSLANG_EX_VERTEX_FRAGMENT, GLSLANG_EX_FRAGMENT } glslang_executable_t; + +// EShOptimizationLevel counterpart +// This enum is not used in the current C interface, but could be added at a later date. +// GLSLANG_OPT_NONE is the current default. +typedef enum { + GLSLANG_OPT_NO_GENERATION, + GLSLANG_OPT_NONE, + GLSLANG_OPT_SIMPLE, + GLSLANG_OPT_FULL, + LAST_ELEMENT_MARKER(GLSLANG_OPT_LEVEL_COUNT), +} glslang_optimization_level_t; + +/* EShTextureSamplerTransformMode counterpart */ +typedef enum { + GLSLANG_TEX_SAMP_TRANS_KEEP, + GLSLANG_TEX_SAMP_TRANS_UPGRADE_TEXTURE_REMOVE_SAMPLER, + LAST_ELEMENT_MARKER(GLSLANG_TEX_SAMP_TRANS_COUNT), +} glslang_texture_sampler_transform_mode_t; + +/* EShMessages counterpart */ +typedef enum { + GLSLANG_MSG_DEFAULT_BIT = 0, + GLSLANG_MSG_RELAXED_ERRORS_BIT = (1 << 0), + GLSLANG_MSG_SUPPRESS_WARNINGS_BIT = (1 << 1), + GLSLANG_MSG_AST_BIT = (1 << 2), + GLSLANG_MSG_SPV_RULES_BIT = (1 << 3), + GLSLANG_MSG_VULKAN_RULES_BIT = (1 << 4), + GLSLANG_MSG_ONLY_PREPROCESSOR_BIT = (1 << 5), + GLSLANG_MSG_READ_HLSL_BIT = (1 << 6), + GLSLANG_MSG_CASCADING_ERRORS_BIT = (1 << 7), + GLSLANG_MSG_KEEP_UNCALLED_BIT = (1 << 8), + GLSLANG_MSG_HLSL_OFFSETS_BIT = (1 << 9), + GLSLANG_MSG_DEBUG_INFO_BIT = (1 << 10), + GLSLANG_MSG_HLSL_ENABLE_16BIT_TYPES_BIT = (1 << 11), + GLSLANG_MSG_HLSL_LEGALIZATION_BIT = (1 << 12), + GLSLANG_MSG_HLSL_DX9_COMPATIBLE_BIT = (1 << 13), + GLSLANG_MSG_BUILTIN_SYMBOL_TABLE_BIT = (1 << 14), + GLSLANG_MSG_ENHANCED = (1 << 15), + GLSLANG_MSG_ABSOLUTE_PATH = (1 << 16), + GLSLANG_MSG_DISPLAY_ERROR_COLUMN = (1 << 17), + GLSLANG_MSG_LINK_TIME_OPTIMIZATION_BIT = (1 << 18), + GLSLANG_MSG_VALIDATE_CROSS_STAGE_IO_BIT = (1 << 19), + LAST_ELEMENT_MARKER(GLSLANG_MSG_COUNT), +} glslang_messages_t; + +/* EShReflectionOptions counterpart */ +typedef enum { + GLSLANG_REFLECTION_DEFAULT_BIT = 0, + GLSLANG_REFLECTION_STRICT_ARRAY_SUFFIX_BIT = (1 << 0), + GLSLANG_REFLECTION_BASIC_ARRAY_SUFFIX_BIT = (1 << 1), + GLSLANG_REFLECTION_INTERMEDIATE_IOO_BIT = (1 << 2), + GLSLANG_REFLECTION_SEPARATE_BUFFERS_BIT = (1 << 3), + GLSLANG_REFLECTION_ALL_BLOCK_VARIABLES_BIT = (1 << 4), + GLSLANG_REFLECTION_UNWRAP_IO_BLOCKS_BIT = (1 << 5), + GLSLANG_REFLECTION_ALL_IO_VARIABLES_BIT = (1 << 6), + GLSLANG_REFLECTION_SHARED_STD140_SSBO_BIT = (1 << 7), + GLSLANG_REFLECTION_SHARED_STD140_UBO_BIT = (1 << 8), + LAST_ELEMENT_MARKER(GLSLANG_REFLECTION_COUNT), +} glslang_reflection_options_t; + +/* EProfile counterpart (from Versions.h) */ +typedef enum { + GLSLANG_BAD_PROFILE = 0, + GLSLANG_NO_PROFILE = (1 << 0), + GLSLANG_CORE_PROFILE = (1 << 1), + GLSLANG_COMPATIBILITY_PROFILE = (1 << 2), + GLSLANG_ES_PROFILE = (1 << 3), + LAST_ELEMENT_MARKER(GLSLANG_PROFILE_COUNT), +} glslang_profile_t; + +/* Shader options */ +typedef enum { + GLSLANG_SHADER_DEFAULT_BIT = 0, + GLSLANG_SHADER_AUTO_MAP_BINDINGS = (1 << 0), + GLSLANG_SHADER_AUTO_MAP_LOCATIONS = (1 << 1), + GLSLANG_SHADER_VULKAN_RULES_RELAXED = (1 << 2), + LAST_ELEMENT_MARKER(GLSLANG_SHADER_COUNT), +} glslang_shader_options_t; + +/* TResourceType counterpart */ +typedef enum { + GLSLANG_RESOURCE_TYPE_SAMPLER, + GLSLANG_RESOURCE_TYPE_TEXTURE, + GLSLANG_RESOURCE_TYPE_IMAGE, + GLSLANG_RESOURCE_TYPE_UBO, + GLSLANG_RESOURCE_TYPE_SSBO, + GLSLANG_RESOURCE_TYPE_UAV, + LAST_ELEMENT_MARKER(GLSLANG_RESOURCE_TYPE_COUNT), +} glslang_resource_type_t; + +#undef LAST_ELEMENT_MARKER + +#endif diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/intermediate.h b/engine/engine/3rdlibs/glslang/glslang/Include/intermediate.h new file mode 100644 index 00000000..2595bb12 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/intermediate.h @@ -0,0 +1,1800 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017, 2022-2024 Arm Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Definition of the in-memory high-level intermediate representation +// of shaders. This is a tree that parser creates. +// +// Nodes in the tree are defined as a hierarchy of classes derived from +// TIntermNode. Each is a node in a tree. There is no preset branching factor; +// each node can have it's own type of list of children. +// + +#ifndef __INTERMEDIATE_H +#define __INTERMEDIATE_H + +#include "Common.h" +#include "Types.h" +#include "ConstantUnion.h" + +namespace glslang { + +class TIntermediate; + +// +// Operators used by the high-level (parse tree) representation. +// +enum TOperator { + EOpNull, // if in a node, should only mean a node is still being built + EOpSequence, // denotes a list of statements, or parameters, etc. + EOpScope, // Used by debugging to denote a scoped list of statements + EOpLinkerObjects, // for aggregate node of objects the linker may need, if not reference by the rest of the AST + EOpFunctionCall, + EOpFunction, // For function definition + EOpParameters, // an aggregate listing the parameters to a function + EOpSpirvInst, + + // + // Unary operators + // + + EOpNegative, + EOpLogicalNot, + EOpVectorLogicalNot, + EOpBitwiseNot, + + EOpPostIncrement, + EOpPostDecrement, + EOpPreIncrement, + EOpPreDecrement, + + EOpCopyObject, + + EOpDeclare, // Used by debugging to force declaration of variable in correct scope + + // Operator used to represent all conversions between int, float, and bool. + // The specific types are inferred from TBasicType. + EOpConvNumeric, + + // uint64_t <-> pointer + EOpConvUint64ToPtr, + EOpConvPtrToUint64, + + // uvec2 <-> pointer + EOpConvUvec2ToPtr, + EOpConvPtrToUvec2, + + // uint64_t -> accelerationStructureEXT + EOpConvUint64ToAccStruct, + + // uvec2 -> accelerationStructureEXT + EOpConvUvec2ToAccStruct, + + // + // binary operations + // + + EOpAdd, + EOpSub, + EOpMul, + EOpDiv, + EOpMod, + EOpRightShift, + EOpLeftShift, + EOpAnd, + EOpInclusiveOr, + EOpExclusiveOr, + EOpEqual, + EOpNotEqual, + EOpVectorEqual, + EOpVectorNotEqual, + EOpLessThan, + EOpGreaterThan, + EOpLessThanEqual, + EOpGreaterThanEqual, + EOpComma, + + EOpVectorTimesScalar, + EOpVectorTimesMatrix, + EOpMatrixTimesVector, + EOpMatrixTimesScalar, + + EOpLogicalOr, + EOpLogicalXor, + EOpLogicalAnd, + + EOpIndexDirect, + EOpIndexIndirect, + EOpIndexDirectStruct, + + EOpVectorSwizzle, + + EOpMethod, + EOpScoping, + + // + // Built-in functions mapped to operators + // + + EOpRadians, + EOpDegrees, + EOpSin, + EOpCos, + EOpTan, + EOpAsin, + EOpAcos, + EOpAtan, + EOpSinh, + EOpCosh, + EOpTanh, + EOpAsinh, + EOpAcosh, + EOpAtanh, + + EOpPow, + EOpExp, + EOpLog, + EOpExp2, + EOpLog2, + EOpSqrt, + EOpInverseSqrt, + + EOpAbs, + EOpSign, + EOpFloor, + EOpTrunc, + EOpRound, + EOpRoundEven, + EOpCeil, + EOpFract, + EOpModf, + EOpMin, + EOpMax, + EOpClamp, + EOpMix, + EOpStep, + EOpSmoothStep, + + EOpIsNan, + EOpIsInf, + + EOpFma, + + EOpFrexp, + EOpLdexp, + + EOpFloatBitsToInt, + EOpFloatBitsToUint, + EOpIntBitsToFloat, + EOpUintBitsToFloat, + EOpDoubleBitsToInt64, + EOpDoubleBitsToUint64, + EOpInt64BitsToDouble, + EOpUint64BitsToDouble, + EOpFloat16BitsToInt16, + EOpFloat16BitsToUint16, + EOpInt16BitsToFloat16, + EOpUint16BitsToFloat16, + EOpPackSnorm2x16, + EOpUnpackSnorm2x16, + EOpPackUnorm2x16, + EOpUnpackUnorm2x16, + EOpPackSnorm4x8, + EOpUnpackSnorm4x8, + EOpPackUnorm4x8, + EOpUnpackUnorm4x8, + EOpPackHalf2x16, + EOpUnpackHalf2x16, + EOpPackDouble2x32, + EOpUnpackDouble2x32, + EOpPackInt2x32, + EOpUnpackInt2x32, + EOpPackUint2x32, + EOpUnpackUint2x32, + EOpPackFloat2x16, + EOpUnpackFloat2x16, + EOpPackInt2x16, + EOpUnpackInt2x16, + EOpPackUint2x16, + EOpUnpackUint2x16, + EOpPackInt4x16, + EOpUnpackInt4x16, + EOpPackUint4x16, + EOpUnpackUint4x16, + EOpPack16, + EOpPack32, + EOpPack64, + EOpUnpack32, + EOpUnpack16, + EOpUnpack8, + + EOpLength, + EOpDistance, + EOpDot, + EOpCross, + EOpNormalize, + EOpFaceForward, + EOpReflect, + EOpRefract, + + EOpMin3, + EOpMax3, + EOpMid3, + + EOpDPdx, // Fragment only + EOpDPdy, // Fragment only + EOpFwidth, // Fragment only + EOpDPdxFine, // Fragment only + EOpDPdyFine, // Fragment only + EOpFwidthFine, // Fragment only + EOpDPdxCoarse, // Fragment only + EOpDPdyCoarse, // Fragment only + EOpFwidthCoarse, // Fragment only + + EOpInterpolateAtCentroid, // Fragment only + EOpInterpolateAtSample, // Fragment only + EOpInterpolateAtOffset, // Fragment only + EOpInterpolateAtVertex, + + EOpMatrixTimesMatrix, + EOpOuterProduct, + EOpDeterminant, + EOpMatrixInverse, + EOpTranspose, + + EOpFtransform, + + EOpNoise, + + EOpEmitVertex, // geometry only + EOpEndPrimitive, // geometry only + EOpEmitStreamVertex, // geometry only + EOpEndStreamPrimitive, // geometry only + + EOpBarrier, + EOpMemoryBarrier, + EOpMemoryBarrierAtomicCounter, + EOpMemoryBarrierBuffer, + EOpMemoryBarrierImage, + EOpMemoryBarrierShared, // compute only + EOpGroupMemoryBarrier, // compute only + + EOpBallot, + EOpReadInvocation, + EOpReadFirstInvocation, + + EOpAnyInvocation, + EOpAllInvocations, + EOpAllInvocationsEqual, + + EOpSubgroupGuardStart, + EOpSubgroupBarrier, + EOpSubgroupMemoryBarrier, + EOpSubgroupMemoryBarrierBuffer, + EOpSubgroupMemoryBarrierImage, + EOpSubgroupMemoryBarrierShared, // compute only + EOpSubgroupElect, + EOpSubgroupAll, + EOpSubgroupAny, + EOpSubgroupAllEqual, + EOpSubgroupBroadcast, + EOpSubgroupBroadcastFirst, + EOpSubgroupBallot, + EOpSubgroupInverseBallot, + EOpSubgroupBallotBitExtract, + EOpSubgroupBallotBitCount, + EOpSubgroupBallotInclusiveBitCount, + EOpSubgroupBallotExclusiveBitCount, + EOpSubgroupBallotFindLSB, + EOpSubgroupBallotFindMSB, + EOpSubgroupShuffle, + EOpSubgroupShuffleXor, + EOpSubgroupShuffleUp, + EOpSubgroupShuffleDown, + EOpSubgroupRotate, + EOpSubgroupClusteredRotate, + EOpSubgroupAdd, + EOpSubgroupMul, + EOpSubgroupMin, + EOpSubgroupMax, + EOpSubgroupAnd, + EOpSubgroupOr, + EOpSubgroupXor, + EOpSubgroupInclusiveAdd, + EOpSubgroupInclusiveMul, + EOpSubgroupInclusiveMin, + EOpSubgroupInclusiveMax, + EOpSubgroupInclusiveAnd, + EOpSubgroupInclusiveOr, + EOpSubgroupInclusiveXor, + EOpSubgroupExclusiveAdd, + EOpSubgroupExclusiveMul, + EOpSubgroupExclusiveMin, + EOpSubgroupExclusiveMax, + EOpSubgroupExclusiveAnd, + EOpSubgroupExclusiveOr, + EOpSubgroupExclusiveXor, + EOpSubgroupClusteredAdd, + EOpSubgroupClusteredMul, + EOpSubgroupClusteredMin, + EOpSubgroupClusteredMax, + EOpSubgroupClusteredAnd, + EOpSubgroupClusteredOr, + EOpSubgroupClusteredXor, + EOpSubgroupQuadBroadcast, + EOpSubgroupQuadSwapHorizontal, + EOpSubgroupQuadSwapVertical, + EOpSubgroupQuadSwapDiagonal, + EOpSubgroupQuadAll, + EOpSubgroupQuadAny, + + EOpSubgroupPartition, + EOpSubgroupPartitionedAdd, + EOpSubgroupPartitionedMul, + EOpSubgroupPartitionedMin, + EOpSubgroupPartitionedMax, + EOpSubgroupPartitionedAnd, + EOpSubgroupPartitionedOr, + EOpSubgroupPartitionedXor, + EOpSubgroupPartitionedInclusiveAdd, + EOpSubgroupPartitionedInclusiveMul, + EOpSubgroupPartitionedInclusiveMin, + EOpSubgroupPartitionedInclusiveMax, + EOpSubgroupPartitionedInclusiveAnd, + EOpSubgroupPartitionedInclusiveOr, + EOpSubgroupPartitionedInclusiveXor, + EOpSubgroupPartitionedExclusiveAdd, + EOpSubgroupPartitionedExclusiveMul, + EOpSubgroupPartitionedExclusiveMin, + EOpSubgroupPartitionedExclusiveMax, + EOpSubgroupPartitionedExclusiveAnd, + EOpSubgroupPartitionedExclusiveOr, + EOpSubgroupPartitionedExclusiveXor, + + EOpSubgroupGuardStop, + + // Integer dot product + EOpDotPackedEXT, + EOpDotAccSatEXT, + EOpDotPackedAccSatEXT, + + EOpMinInvocations, + EOpMaxInvocations, + EOpAddInvocations, + EOpMinInvocationsNonUniform, + EOpMaxInvocationsNonUniform, + EOpAddInvocationsNonUniform, + EOpMinInvocationsInclusiveScan, + EOpMaxInvocationsInclusiveScan, + EOpAddInvocationsInclusiveScan, + EOpMinInvocationsInclusiveScanNonUniform, + EOpMaxInvocationsInclusiveScanNonUniform, + EOpAddInvocationsInclusiveScanNonUniform, + EOpMinInvocationsExclusiveScan, + EOpMaxInvocationsExclusiveScan, + EOpAddInvocationsExclusiveScan, + EOpMinInvocationsExclusiveScanNonUniform, + EOpMaxInvocationsExclusiveScanNonUniform, + EOpAddInvocationsExclusiveScanNonUniform, + EOpSwizzleInvocations, + EOpSwizzleInvocationsMasked, + EOpWriteInvocation, + EOpMbcnt, + + EOpCubeFaceIndex, + EOpCubeFaceCoord, + EOpTime, + + EOpAtomicAdd, + EOpAtomicSubtract, + EOpAtomicMin, + EOpAtomicMax, + EOpAtomicAnd, + EOpAtomicOr, + EOpAtomicXor, + EOpAtomicExchange, + EOpAtomicCompSwap, + EOpAtomicLoad, + EOpAtomicStore, + + EOpAtomicCounterIncrement, // results in pre-increment value + EOpAtomicCounterDecrement, // results in post-decrement value + EOpAtomicCounter, + EOpAtomicCounterAdd, + EOpAtomicCounterSubtract, + EOpAtomicCounterMin, + EOpAtomicCounterMax, + EOpAtomicCounterAnd, + EOpAtomicCounterOr, + EOpAtomicCounterXor, + EOpAtomicCounterExchange, + EOpAtomicCounterCompSwap, + + EOpAny, + EOpAll, + + EOpCooperativeMatrixLoad, + EOpCooperativeMatrixStore, + EOpCooperativeMatrixMulAdd, + EOpCooperativeMatrixLoadNV, + EOpCooperativeMatrixStoreNV, + EOpCooperativeMatrixLoadTensorNV, + EOpCooperativeMatrixStoreTensorNV, + EOpCooperativeMatrixMulAddNV, + EOpCooperativeMatrixReduceNV, + EOpCooperativeMatrixPerElementOpNV, + EOpCooperativeMatrixTransposeNV, + + EOpCreateTensorLayoutNV, + EOpTensorLayoutSetBlockSizeNV, + EOpTensorLayoutSetDimensionNV, + EOpTensorLayoutSetStrideNV, + EOpTensorLayoutSliceNV, + EOpTensorLayoutSetClampValueNV, + + EOpCreateTensorViewNV, + EOpTensorViewSetDimensionNV, + EOpTensorViewSetStrideNV, + EOpTensorViewSetClipNV, + + EOpCooperativeVectorMatMulNV, + EOpCooperativeVectorMatMulAddNV, + EOpCooperativeVectorLoadNV, + EOpCooperativeVectorStoreNV, + EOpCooperativeVectorOuterProductAccumulateNV, + EOpCooperativeVectorReduceSumAccumulateNV, + + EOpBeginInvocationInterlock, // Fragment only + EOpEndInvocationInterlock, // Fragment only + + EOpIsHelperInvocation, + + EOpDebugPrintf, + + // + // Branch + // + + EOpKill, // Fragment only + EOpTerminateInvocation, // Fragment only + EOpDemote, // Fragment only + EOpTerminateRayKHR, // Any-hit only + EOpIgnoreIntersectionKHR, // Any-hit only + EOpReturn, + EOpBreak, + EOpContinue, + EOpCase, + EOpDefault, + + // + // Constructors + // + + EOpConstructGuardStart, + EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed + EOpConstructUint, + EOpConstructInt8, + EOpConstructUint8, + EOpConstructInt16, + EOpConstructUint16, + EOpConstructInt64, + EOpConstructUint64, + EOpConstructBool, + EOpConstructFloat, + EOpConstructDouble, + // Keep vector and matrix constructors in a consistent relative order for + // TParseContext::constructBuiltIn, which converts between 8/16/32 bit + // vector constructors + EOpConstructVec2, + EOpConstructVec3, + EOpConstructVec4, + EOpConstructMat2x2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, + EOpConstructMat3x3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, + EOpConstructMat4x4, + EOpConstructDVec2, + EOpConstructDVec3, + EOpConstructDVec4, + EOpConstructBVec2, + EOpConstructBVec3, + EOpConstructBVec4, + EOpConstructI8Vec2, + EOpConstructI8Vec3, + EOpConstructI8Vec4, + EOpConstructU8Vec2, + EOpConstructU8Vec3, + EOpConstructU8Vec4, + EOpConstructI16Vec2, + EOpConstructI16Vec3, + EOpConstructI16Vec4, + EOpConstructU16Vec2, + EOpConstructU16Vec3, + EOpConstructU16Vec4, + EOpConstructIVec2, + EOpConstructIVec3, + EOpConstructIVec4, + EOpConstructUVec2, + EOpConstructUVec3, + EOpConstructUVec4, + EOpConstructI64Vec2, + EOpConstructI64Vec3, + EOpConstructI64Vec4, + EOpConstructU64Vec2, + EOpConstructU64Vec3, + EOpConstructU64Vec4, + EOpConstructDMat2x2, + EOpConstructDMat2x3, + EOpConstructDMat2x4, + EOpConstructDMat3x2, + EOpConstructDMat3x3, + EOpConstructDMat3x4, + EOpConstructDMat4x2, + EOpConstructDMat4x3, + EOpConstructDMat4x4, + EOpConstructIMat2x2, + EOpConstructIMat2x3, + EOpConstructIMat2x4, + EOpConstructIMat3x2, + EOpConstructIMat3x3, + EOpConstructIMat3x4, + EOpConstructIMat4x2, + EOpConstructIMat4x3, + EOpConstructIMat4x4, + EOpConstructUMat2x2, + EOpConstructUMat2x3, + EOpConstructUMat2x4, + EOpConstructUMat3x2, + EOpConstructUMat3x3, + EOpConstructUMat3x4, + EOpConstructUMat4x2, + EOpConstructUMat4x3, + EOpConstructUMat4x4, + EOpConstructBMat2x2, + EOpConstructBMat2x3, + EOpConstructBMat2x4, + EOpConstructBMat3x2, + EOpConstructBMat3x3, + EOpConstructBMat3x4, + EOpConstructBMat4x2, + EOpConstructBMat4x3, + EOpConstructBMat4x4, + EOpConstructFloat16, + EOpConstructF16Vec2, + EOpConstructF16Vec3, + EOpConstructF16Vec4, + EOpConstructF16Mat2x2, + EOpConstructF16Mat2x3, + EOpConstructF16Mat2x4, + EOpConstructF16Mat3x2, + EOpConstructF16Mat3x3, + EOpConstructF16Mat3x4, + EOpConstructF16Mat4x2, + EOpConstructF16Mat4x3, + EOpConstructF16Mat4x4, + EOpConstructBFloat16, + EOpConstructBF16Vec2, + EOpConstructBF16Vec3, + EOpConstructBF16Vec4, + EOpConstructStruct, + EOpConstructTextureSampler, + EOpConstructNonuniform, // expected to be transformed away, not present in final AST + EOpConstructReference, + EOpConstructCooperativeMatrixNV, + EOpConstructCooperativeMatrixKHR, + EOpConstructCooperativeVectorNV, + EOpConstructAccStruct, + EOpConstructGuardEnd, + + // + // moves + // + + EOpAssign, + EOpAddAssign, + EOpSubAssign, + EOpMulAssign, + EOpVectorTimesMatrixAssign, + EOpVectorTimesScalarAssign, + EOpMatrixTimesScalarAssign, + EOpMatrixTimesMatrixAssign, + EOpDivAssign, + EOpModAssign, + EOpAndAssign, + EOpInclusiveOrAssign, + EOpExclusiveOrAssign, + EOpLeftShiftAssign, + EOpRightShiftAssign, + + // + // Array operators + // + + // Can apply to arrays, vectors, or matrices. + // Can be decomposed to a constant at compile time, but this does not always happen, + // due to link-time effects. So, consumer can expect either a link-time sized or + // run-time sized array. + EOpArrayLength, + + // + // Image operations + // + + EOpImageGuardBegin, + + EOpImageQuerySize, + EOpImageQuerySamples, + EOpImageLoad, + EOpImageStore, + EOpImageLoadLod, + EOpImageStoreLod, + EOpImageAtomicAdd, + EOpImageAtomicMin, + EOpImageAtomicMax, + EOpImageAtomicAnd, + EOpImageAtomicOr, + EOpImageAtomicXor, + EOpImageAtomicExchange, + EOpImageAtomicCompSwap, + EOpImageAtomicLoad, + EOpImageAtomicStore, + + EOpSubpassLoad, + EOpSubpassLoadMS, + EOpSparseImageLoad, + EOpSparseImageLoadLod, + EOpColorAttachmentReadEXT, // Fragment only + + EOpImageGuardEnd, + + // + // Texture operations + // + + EOpTextureGuardBegin, + + EOpTextureQuerySize, + EOpTextureQueryLod, + EOpTextureQueryLevels, + EOpTextureQuerySamples, + + EOpSamplingGuardBegin, + + EOpTexture, + EOpTextureProj, + EOpTextureLod, + EOpTextureOffset, + EOpTextureFetch, + EOpTextureFetchOffset, + EOpTextureProjOffset, + EOpTextureLodOffset, + EOpTextureProjLod, + EOpTextureProjLodOffset, + EOpTextureGrad, + EOpTextureGradOffset, + EOpTextureProjGrad, + EOpTextureProjGradOffset, + EOpTextureGather, + EOpTextureGatherOffset, + EOpTextureGatherOffsets, + EOpTextureClamp, + EOpTextureOffsetClamp, + EOpTextureGradClamp, + EOpTextureGradOffsetClamp, + EOpTextureGatherLod, + EOpTextureGatherLodOffset, + EOpTextureGatherLodOffsets, + EOpFragmentMaskFetch, + EOpFragmentFetch, + + EOpSparseTextureGuardBegin, + + EOpSparseTexture, + EOpSparseTextureLod, + EOpSparseTextureOffset, + EOpSparseTextureFetch, + EOpSparseTextureFetchOffset, + EOpSparseTextureLodOffset, + EOpSparseTextureGrad, + EOpSparseTextureGradOffset, + EOpSparseTextureGather, + EOpSparseTextureGatherOffset, + EOpSparseTextureGatherOffsets, + EOpSparseTexelsResident, + EOpSparseTextureClamp, + EOpSparseTextureOffsetClamp, + EOpSparseTextureGradClamp, + EOpSparseTextureGradOffsetClamp, + EOpSparseTextureGatherLod, + EOpSparseTextureGatherLodOffset, + EOpSparseTextureGatherLodOffsets, + + EOpSparseTextureGuardEnd, + + EOpImageFootprintGuardBegin, + EOpImageSampleFootprintNV, + EOpImageSampleFootprintClampNV, + EOpImageSampleFootprintLodNV, + EOpImageSampleFootprintGradNV, + EOpImageSampleFootprintGradClampNV, + EOpImageFootprintGuardEnd, + EOpSamplingGuardEnd, + EOpTextureGuardEnd, + + // + // Integer operations + // + + EOpAddCarry, + EOpSubBorrow, + EOpUMulExtended, + EOpIMulExtended, + EOpBitfieldExtract, + EOpBitfieldInsert, + EOpBitFieldReverse, + EOpBitCount, + EOpFindLSB, + EOpFindMSB, + + EOpCountLeadingZeros, + EOpCountTrailingZeros, + EOpAbsDifference, + EOpAddSaturate, + EOpSubSaturate, + EOpAverage, + EOpAverageRounded, + EOpMul32x16, + + EOpTraceNV, + EOpTraceRayMotionNV, + EOpTraceKHR, + EOpReportIntersection, + EOpIgnoreIntersectionNV, + EOpTerminateRayNV, + EOpExecuteCallableNV, + EOpExecuteCallableKHR, + EOpWritePackedPrimitiveIndices4x8NV, + EOpEmitMeshTasksEXT, + EOpSetMeshOutputsEXT, + + // + // GL_EXT_ray_query operations + // + + EOpRayQueryInitialize, + EOpRayQueryTerminate, + EOpRayQueryGenerateIntersection, + EOpRayQueryConfirmIntersection, + EOpRayQueryProceed, + EOpRayQueryGetIntersectionType, + EOpRayQueryGetRayTMin, + EOpRayQueryGetRayFlags, + EOpRayQueryGetIntersectionT, + EOpRayQueryGetIntersectionInstanceCustomIndex, + EOpRayQueryGetIntersectionInstanceId, + EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset, + EOpRayQueryGetIntersectionGeometryIndex, + EOpRayQueryGetIntersectionPrimitiveIndex, + EOpRayQueryGetIntersectionBarycentrics, + EOpRayQueryGetIntersectionFrontFace, + EOpRayQueryGetIntersectionCandidateAABBOpaque, + EOpRayQueryGetIntersectionObjectRayDirection, + EOpRayQueryGetIntersectionObjectRayOrigin, + EOpRayQueryGetWorldRayDirection, + EOpRayQueryGetWorldRayOrigin, + EOpRayQueryGetIntersectionObjectToWorld, + EOpRayQueryGetIntersectionWorldToObject, + + // + // GL_NV_shader_invocation_reorder + // + + EOpHitObjectTraceRayNV, + EOpHitObjectTraceRayMotionNV, + EOpHitObjectRecordHitNV, + EOpHitObjectRecordHitMotionNV, + EOpHitObjectRecordHitWithIndexNV, + EOpHitObjectRecordHitWithIndexMotionNV, + EOpHitObjectRecordMissNV, + EOpHitObjectRecordMissMotionNV, + EOpHitObjectRecordEmptyNV, + EOpHitObjectExecuteShaderNV, + EOpHitObjectIsEmptyNV, + EOpHitObjectIsMissNV, + EOpHitObjectIsHitNV, + EOpHitObjectGetRayTMinNV, + EOpHitObjectGetRayTMaxNV, + EOpHitObjectGetObjectRayOriginNV, + EOpHitObjectGetObjectRayDirectionNV, + EOpHitObjectGetWorldRayOriginNV, + EOpHitObjectGetWorldRayDirectionNV, + EOpHitObjectGetWorldToObjectNV, + EOpHitObjectGetObjectToWorldNV, + EOpHitObjectGetInstanceCustomIndexNV, + EOpHitObjectGetInstanceIdNV, + EOpHitObjectGetGeometryIndexNV, + EOpHitObjectGetPrimitiveIndexNV, + EOpHitObjectGetHitKindNV, + EOpHitObjectGetShaderBindingTableRecordIndexNV, + EOpHitObjectGetShaderRecordBufferHandleNV, + EOpHitObjectGetAttributesNV, + EOpHitObjectGetCurrentTimeNV, + EOpReorderThreadNV, + EOpFetchMicroTriangleVertexPositionNV, + EOpFetchMicroTriangleVertexBarycentricNV, + + // HLSL operations + // + + EOpClip, // discard if input value < 0 + EOpIsFinite, + EOpLog10, // base 10 log + EOpRcp, // 1/x + EOpSaturate, // clamp from 0 to 1 + EOpSinCos, // sin and cos in out parameters + EOpGenMul, // mul(x,y) on any of mat/vec/scalars + EOpDst, // x = 1, y=src0.y * src1.y, z=src0.z, w=src1.w + EOpInterlockedAdd, // atomic ops, but uses [optional] out arg instead of return + EOpInterlockedAnd, // ... + EOpInterlockedCompareExchange, // ... + EOpInterlockedCompareStore, // ... + EOpInterlockedExchange, // ... + EOpInterlockedMax, // ... + EOpInterlockedMin, // ... + EOpInterlockedOr, // ... + EOpInterlockedXor, // ... + EOpAllMemoryBarrierWithGroupSync, // memory barriers without non-hlsl AST equivalents + EOpDeviceMemoryBarrier, // ... + EOpDeviceMemoryBarrierWithGroupSync, // ... + EOpWorkgroupMemoryBarrier, // ... + EOpWorkgroupMemoryBarrierWithGroupSync, // ... + EOpEvaluateAttributeSnapped, // InterpolateAtOffset with int position on 16x16 grid + EOpF32tof16, // HLSL conversion: half of a PackHalf2x16 + EOpF16tof32, // HLSL conversion: half of an UnpackHalf2x16 + EOpLit, // HLSL lighting coefficient vector + EOpTextureBias, // HLSL texture bias: will be lowered to EOpTexture + EOpAsDouble, // slightly different from EOpUint64BitsToDouble + EOpD3DCOLORtoUBYTE4, // convert and swizzle 4-component color to UBYTE4 range + + EOpMethodSample, // Texture object methods. These are translated to existing + EOpMethodSampleBias, // AST methods, and exist to represent HLSL semantics until that + EOpMethodSampleCmp, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodSampleCmpLevelZero, // ... + EOpMethodSampleGrad, // ... + EOpMethodSampleLevel, // ... + EOpMethodLoad, // ... + EOpMethodGetDimensions, // ... + EOpMethodGetSamplePosition, // ... + EOpMethodGather, // ... + EOpMethodCalculateLevelOfDetail, // ... + EOpMethodCalculateLevelOfDetailUnclamped, // ... + + // Load already defined above for textures + EOpMethodLoad2, // Structure buffer object methods. These are translated to existing + EOpMethodLoad3, // AST methods, and exist to represent HLSL semantics until that + EOpMethodLoad4, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodStore, // ... + EOpMethodStore2, // ... + EOpMethodStore3, // ... + EOpMethodStore4, // ... + EOpMethodIncrementCounter, // ... + EOpMethodDecrementCounter, // ... + // EOpMethodAppend is defined for geo shaders below + EOpMethodConsume, + + // SM5 texture methods + EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about + EOpMethodGatherGreen, // translation to existing AST opcodes. They exist temporarily + EOpMethodGatherBlue, // because HLSL arguments are slightly different. + EOpMethodGatherAlpha, // ... + EOpMethodGatherCmp, // ... + EOpMethodGatherCmpRed, // ... + EOpMethodGatherCmpGreen, // ... + EOpMethodGatherCmpBlue, // ... + EOpMethodGatherCmpAlpha, // ... + + // geometry methods + EOpMethodAppend, // Geometry shader methods + EOpMethodRestartStrip, // ... + + // matrix + EOpMatrixSwizzle, // select multiple matrix components (non-column) + + // SM6 wave ops + EOpWaveGetLaneCount, // Will decompose to gl_SubgroupSize. + EOpWaveGetLaneIndex, // Will decompose to gl_SubgroupInvocationID. + EOpWaveActiveCountBits, // Will decompose to subgroupBallotBitCount(subgroupBallot()). + EOpWavePrefixCountBits, // Will decompose to subgroupBallotInclusiveBitCount(subgroupBallot()). + + // GL_EXT_expect_assume + EOpAssumeEXT, + EOpExpectEXT, + + // Shader Clock Ops + EOpReadClockSubgroupKHR, + EOpReadClockDeviceKHR, + + // GL_EXT_ray_tracing_position_fetch + EOpRayQueryGetIntersectionTriangleVertexPositionsEXT, + + // Shader tile image ops + EOpStencilAttachmentReadEXT, // Fragment only + EOpDepthAttachmentReadEXT, // Fragment only + + // Image processing + EOpImageSampleWeightedQCOM, + EOpImageBoxFilterQCOM, + EOpImageBlockMatchSADQCOM, + EOpImageBlockMatchSSDQCOM, + + // Image processing2 + EOpImageBlockMatchWindowSSDQCOM, + EOpImageBlockMatchWindowSADQCOM, + EOpImageBlockMatchGatherSSDQCOM, + EOpImageBlockMatchGatherSADQCOM, + + // GL_NV_cluster_acceleration_structure + EOpRayQueryGetIntersectionClusterIdNV, + EOpHitObjectGetClusterIdNV, + + // GL_NV_linear_swept_spheres + EOpRayQueryGetIntersectionSpherePositionNV, + EOpRayQueryGetIntersectionSphereRadiusNV, + EOpRayQueryGetIntersectionLSSHitValueNV, + EOpRayQueryGetIntersectionLSSPositionsNV, + EOpRayQueryGetIntersectionLSSRadiiNV, + EOpRayQueryIsSphereHitNV, + EOpRayQueryIsLSSHitNV, + EOpHitObjectGetSpherePositionNV, + EOpHitObjectGetSphereRadiusNV, + EOpHitObjectGetLSSPositionsNV, + EOpHitObjectGetLSSRadiiNV, + EOpHitObjectIsSphereHitNV, + EOpHitObjectIsLSSHitNV, +}; + +inline bool IsOpNumericConv(const TOperator op) { + return op == EOpConvNumeric; +} + +enum TLinkType { + ELinkNone, + ELinkExport, +}; + +class TIntermTraverser; +class TIntermOperator; +class TIntermAggregate; +class TIntermUnary; +class TIntermBinary; +class TIntermConstantUnion; +class TIntermSelection; +class TIntermSwitch; +class TIntermBranch; +class TIntermTyped; +class TIntermMethod; +class TIntermSymbol; +class TIntermLoop; + +} // end namespace glslang + +// +// Base class for the tree nodes +// +// (Put outside the glslang namespace, as it's used as part of the external interface.) +// +class TIntermNode { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + + TIntermNode() { loc.init(); } + virtual const glslang::TSourceLoc& getLoc() const { return loc; } + virtual void setLoc(const glslang::TSourceLoc& l) { loc = l; } + virtual void traverse(glslang::TIntermTraverser*) = 0; + virtual glslang::TIntermTyped* getAsTyped() { return nullptr; } + virtual glslang::TIntermOperator* getAsOperator() { return nullptr; } + virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return nullptr; } + virtual glslang::TIntermAggregate* getAsAggregate() { return nullptr; } + virtual glslang::TIntermUnary* getAsUnaryNode() { return nullptr; } + virtual glslang::TIntermBinary* getAsBinaryNode() { return nullptr; } + virtual glslang::TIntermSelection* getAsSelectionNode() { return nullptr; } + virtual glslang::TIntermSwitch* getAsSwitchNode() { return nullptr; } + virtual glslang::TIntermMethod* getAsMethodNode() { return nullptr; } + virtual glslang::TIntermSymbol* getAsSymbolNode() { return nullptr; } + virtual glslang::TIntermBranch* getAsBranchNode() { return nullptr; } + virtual glslang::TIntermLoop* getAsLoopNode() { return nullptr; } + + virtual const glslang::TIntermTyped* getAsTyped() const { return nullptr; } + virtual const glslang::TIntermOperator* getAsOperator() const { return nullptr; } + virtual const glslang::TIntermConstantUnion* getAsConstantUnion() const { return nullptr; } + virtual const glslang::TIntermAggregate* getAsAggregate() const { return nullptr; } + virtual const glslang::TIntermUnary* getAsUnaryNode() const { return nullptr; } + virtual const glslang::TIntermBinary* getAsBinaryNode() const { return nullptr; } + virtual const glslang::TIntermSelection* getAsSelectionNode() const { return nullptr; } + virtual const glslang::TIntermSwitch* getAsSwitchNode() const { return nullptr; } + virtual const glslang::TIntermMethod* getAsMethodNode() const { return nullptr; } + virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return nullptr; } + virtual const glslang::TIntermBranch* getAsBranchNode() const { return nullptr; } + virtual const glslang::TIntermLoop* getAsLoopNode() const { return nullptr; } + virtual ~TIntermNode() { } + +protected: + TIntermNode(const TIntermNode&); + TIntermNode& operator=(const TIntermNode&); + glslang::TSourceLoc loc; +}; + +namespace glslang { + +// +// This is just to help yacc. +// +struct TIntermNodePair { + TIntermNode* node1; + TIntermNode* node2; +}; + +// +// Intermediate class for nodes that have a type. +// +class TIntermTyped : public TIntermNode { +public: + TIntermTyped(const TType& t) { type.shallowCopy(t); } + TIntermTyped(TBasicType basicType) { TType bt(basicType); type.shallowCopy(bt); } + virtual TIntermTyped* getAsTyped() { return this; } + virtual const TIntermTyped* getAsTyped() const { return this; } + virtual void setType(const TType& t) { type.shallowCopy(t); } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { return type; } + + virtual TBasicType getBasicType() const { return type.getBasicType(); } + virtual TQualifier& getQualifier() { return type.getQualifier(); } + virtual const TQualifier& getQualifier() const { return type.getQualifier(); } + virtual TArraySizes* getArraySizes() { return type.getArraySizes(); } + virtual const TArraySizes* getArraySizes() const { return type.getArraySizes(); } + virtual void propagatePrecision(TPrecisionQualifier); + virtual int getVectorSize() const { return type.getVectorSize(); } + virtual int getMatrixCols() const { return type.getMatrixCols(); } + virtual int getMatrixRows() const { return type.getMatrixRows(); } + virtual bool isMatrix() const { return type.isMatrix(); } + virtual bool isArray() const { return type.isArray(); } + virtual bool isVector() const { return type.isVector(); } + virtual bool isScalar() const { return type.isScalar(); } + virtual bool isStruct() const { return type.isStruct(); } + virtual bool isFloatingDomain() const { return type.isFloatingDomain(); } + virtual bool isIntegerDomain() const { return type.isIntegerDomain(); } + bool isAtomic() const { return type.isAtomic(); } + bool isReference() const { return type.isReference(); } + TString getCompleteString(bool enhanced = false) const { return type.getCompleteString(enhanced); } + +protected: + TIntermTyped& operator=(const TIntermTyped&); + TType type; +}; + +// +// Handle for, do-while, and while loops. +// +class TIntermLoop : public TIntermNode { +public: + TIntermLoop(TIntermNode* aBody, TIntermTyped* aTest, TIntermTyped* aTerminal, bool testFirst) : + body(aBody), + test(aTest), + terminal(aTerminal), + first(testFirst), + unroll(false), + dontUnroll(false), + dependency(0), + minIterations(0), + maxIterations(iterationsInfinite), + iterationMultiple(1), + peelCount(0), + partialCount(0) + { } + + virtual TIntermLoop* getAsLoopNode() { return this; } + virtual const TIntermLoop* getAsLoopNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TIntermNode* getBody() const { return body; } + TIntermTyped* getTest() const { return test; } + TIntermTyped* getTerminal() const { return terminal; } + bool testFirst() const { return first; } + + void setUnroll() { unroll = true; } + void setDontUnroll() { + dontUnroll = true; + peelCount = 0; + partialCount = 0; + } + bool getUnroll() const { return unroll; } + bool getDontUnroll() const { return dontUnroll; } + + static const unsigned int dependencyInfinite = 0xFFFFFFFF; + static const unsigned int iterationsInfinite = 0xFFFFFFFF; + void setLoopDependency(int d) { dependency = d; } + int getLoopDependency() const { return dependency; } + + void setMinIterations(unsigned int v) { minIterations = v; } + unsigned int getMinIterations() const { return minIterations; } + void setMaxIterations(unsigned int v) { maxIterations = v; } + unsigned int getMaxIterations() const { return maxIterations; } + void setIterationMultiple(unsigned int v) { iterationMultiple = v; } + unsigned int getIterationMultiple() const { return iterationMultiple; } + void setPeelCount(unsigned int v) { + peelCount = v; + dontUnroll = false; + } + unsigned int getPeelCount() const { return peelCount; } + void setPartialCount(unsigned int v) { + partialCount = v; + dontUnroll = false; + } + unsigned int getPartialCount() const { return partialCount; } + +protected: + TIntermNode* body; // code to loop over + TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops + TIntermTyped* terminal; // exists for for-loops + bool first; // true for while and for, not for do-while + bool unroll; // true if unroll requested + bool dontUnroll; // true if request to not unroll + unsigned int dependency; // loop dependency hint; 0 means not set or unknown + unsigned int minIterations; // as per the SPIR-V specification + unsigned int maxIterations; // as per the SPIR-V specification + unsigned int iterationMultiple; // as per the SPIR-V specification + unsigned int peelCount; // as per the SPIR-V specification + unsigned int partialCount; // as per the SPIR-V specification +}; + +// +// Handle case, break, continue, return, and kill. +// +class TIntermBranch : public TIntermNode { +public: + TIntermBranch(TOperator op, TIntermTyped* e) : + flowOp(op), + expression(e) { } + virtual TIntermBranch* getAsBranchNode() { return this; } + virtual const TIntermBranch* getAsBranchNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TOperator getFlowOp() const { return flowOp; } + TIntermTyped* getExpression() const { return expression; } + void setExpression(TIntermTyped* pExpression) { expression = pExpression; } + void updatePrecision(TPrecisionQualifier parentPrecision); +protected: + TOperator flowOp; + TIntermTyped* expression; +}; + +// +// Represent method names before seeing their calling signature +// or resolving them to operations. Just an expression as the base object +// and a textural name. +// +class TIntermMethod : public TIntermTyped { +public: + TIntermMethod(TIntermTyped* o, const TType& t, const TString& m) : TIntermTyped(t), object(o), method(m) { } + virtual TIntermMethod* getAsMethodNode() { return this; } + virtual const TIntermMethod* getAsMethodNode() const { return this; } + virtual const TString& getMethodName() const { return method; } + virtual TIntermTyped* getObject() const { return object; } + virtual void traverse(TIntermTraverser*); + void setExport() { linkType = ELinkExport; } +protected: + TIntermTyped* object; + TString method; + TLinkType linkType; +}; + +// +// Nodes that correspond to symbols or constants in the source code. +// +class TIntermSymbol : public TIntermTyped { +public: + // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from + // per process threadPoolAllocator, then it causes increased memory usage per compile + // it is essential to use "symbol = sym" to assign to symbol + TIntermSymbol(long long i, const TString& n, EShLanguage s, const TType& t, const TString* mn = nullptr) + : TIntermTyped(t), id(i), flattenSubset(-1), stage(s), constSubtree(nullptr) { + name = n; + if (mn) { + mangledName = *mn; + } else { + mangledName = n; + } + } + virtual long long getId() const { return id; } + virtual void changeId(long long i) { id = i; } + virtual const TString& getName() const { return name; } + virtual const TString& getMangledName() const { return mangledName; } + virtual void traverse(TIntermTraverser*); + virtual TIntermSymbol* getAsSymbolNode() { return this; } + virtual const TIntermSymbol* getAsSymbolNode() const { return this; } + void setConstArray(const TConstUnionArray& c) { constArray = c; } + const TConstUnionArray& getConstArray() const { return constArray; } + void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + TIntermTyped* getConstSubtree() const { return constSubtree; } + void setFlattenSubset(int subset) { flattenSubset = subset; } + virtual const TString& getAccessName() const; + + int getFlattenSubset() const { return flattenSubset; } // -1 means full object + + // This is meant for cases where a node has already been constructed, and + // later on, it becomes necessary to switch to a different symbol. + virtual void switchId(long long newId) { id = newId; } + EShLanguage getStage() const { return stage; } + +protected: + long long id; // the unique id of the symbol this node represents + int flattenSubset; // how deeply the flattened object rooted at id has been dereferenced + TString name; // the name of the symbol this node represents + EShLanguage stage; + TString mangledName; // mangled function name, or a copy of name if not a function + TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value + TIntermTyped* constSubtree; +}; + +class TIntermConstantUnion : public TIntermTyped { +public: + TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { } + const TConstUnionArray& getConstArray() const { return constArray; } + virtual TIntermConstantUnion* getAsConstantUnion() { return this; } + virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; } + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* fold(TOperator, const TIntermTyped*) const; + virtual TIntermTyped* fold(TOperator, const TType&) const; + void setLiteral() { literal = true; } + void setExpression() { literal = false; } + bool isLiteral() const { return literal; } + +protected: + TIntermConstantUnion& operator=(const TIntermConstantUnion&); + + const TConstUnionArray constArray; + bool literal; // true if node represents a literal in the source code +}; + +// Represent the independent aspects of a texturing TOperator +struct TCrackedTextureOp { + bool query; + bool proj; + bool lod; + bool fetch; + bool offset; + bool offsets; + bool gather; + bool grad; + bool subpass; + bool lodClamp; + bool fragMask; + bool attachmentEXT; +}; + +// +// Intermediate class for node types that hold operators. +// +class TIntermOperator : public TIntermTyped { +public: + virtual TIntermOperator* getAsOperator() { return this; } + virtual const TIntermOperator* getAsOperator() const { return this; } + TOperator getOp() const { return op; } + void setOp(TOperator newOp) { op = newOp; } + bool modifiesState() const; + bool isConstructor() const; + bool isTexture() const { return op > EOpTextureGuardBegin && op < EOpTextureGuardEnd; } + bool isSampling() const { return op > EOpSamplingGuardBegin && op < EOpSamplingGuardEnd; } + bool isImage() const { return op > EOpImageGuardBegin && op < EOpImageGuardEnd; } + bool isSparseTexture() const { return op > EOpSparseTextureGuardBegin && op < EOpSparseTextureGuardEnd; } + bool isImageFootprint() const { return op > EOpImageFootprintGuardBegin && op < EOpImageFootprintGuardEnd; } + bool isSparseImage() const { return op == EOpSparseImageLoad; } + bool isSubgroup() const { return op > EOpSubgroupGuardStart && op < EOpSubgroupGuardStop; } + + void setOperationPrecision(TPrecisionQualifier p) { operationPrecision = p; } + TPrecisionQualifier getOperationPrecision() const { return operationPrecision != EpqNone ? + operationPrecision : + type.getQualifier().precision; } + TString getCompleteString() const + { + TString cs = type.getCompleteString(); + if (getOperationPrecision() != type.getQualifier().precision) { + cs += ", operation at "; + cs += GetPrecisionQualifierString(getOperationPrecision()); + } + + return cs; + } + + // Crack the op into the individual dimensions of texturing operation. + void crackTexture(TSampler sampler, TCrackedTextureOp& cracked) const + { + cracked.query = false; + cracked.proj = false; + cracked.lod = false; + cracked.fetch = false; + cracked.offset = false; + cracked.offsets = false; + cracked.gather = false; + cracked.grad = false; + cracked.subpass = false; + cracked.attachmentEXT = false; + cracked.lodClamp = false; + cracked.fragMask = false; + + switch (op) { + case EOpImageQuerySize: + case EOpImageQuerySamples: + case EOpTextureQuerySize: + case EOpTextureQueryLod: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpSparseTexelsResident: + cracked.query = true; + break; + case EOpTexture: + case EOpSparseTexture: + break; + case EOpTextureProj: + cracked.proj = true; + break; + case EOpTextureLod: + case EOpSparseTextureLod: + cracked.lod = true; + break; + case EOpTextureOffset: + case EOpSparseTextureOffset: + cracked.offset = true; + break; + case EOpTextureFetch: + case EOpSparseTextureFetch: + cracked.fetch = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureFetchOffset: + case EOpSparseTextureFetchOffset: + cracked.fetch = true; + cracked.offset = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureProjOffset: + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureLodOffset: + case EOpSparseTextureLodOffset: + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureProjLod: + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureProjLodOffset: + cracked.offset = true; + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureGrad: + case EOpSparseTextureGrad: + cracked.grad = true; + break; + case EOpTextureGradOffset: + case EOpSparseTextureGradOffset: + cracked.grad = true; + cracked.offset = true; + break; + case EOpTextureProjGrad: + cracked.grad = true; + cracked.proj = true; + break; + case EOpTextureProjGradOffset: + cracked.grad = true; + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureClamp: + case EOpSparseTextureClamp: + cracked.lodClamp = true; + break; + case EOpTextureOffsetClamp: + case EOpSparseTextureOffsetClamp: + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGradClamp: + case EOpSparseTextureGradClamp: + cracked.grad = true; + cracked.lodClamp = true; + break; + case EOpTextureGradOffsetClamp: + case EOpSparseTextureGradOffsetClamp: + cracked.grad = true; + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGather: + case EOpSparseTextureGather: + cracked.gather = true; + break; + case EOpTextureGatherOffset: + case EOpSparseTextureGatherOffset: + cracked.gather = true; + cracked.offset = true; + break; + case EOpTextureGatherOffsets: + case EOpSparseTextureGatherOffsets: + cracked.gather = true; + cracked.offsets = true; + break; + case EOpTextureGatherLod: + case EOpSparseTextureGatherLod: + cracked.gather = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffset: + cracked.gather = true; + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffsets: + case EOpSparseTextureGatherLodOffsets: + cracked.gather = true; + cracked.offsets = true; + cracked.lod = true; + break; + case EOpImageLoadLod: + case EOpImageStoreLod: + case EOpSparseImageLoadLod: + cracked.lod = true; + break; + case EOpFragmentMaskFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpFragmentFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpImageSampleFootprintNV: + break; + case EOpImageSampleFootprintClampNV: + cracked.lodClamp = true; + break; + case EOpImageSampleFootprintLodNV: + cracked.lod = true; + break; + case EOpImageSampleFootprintGradNV: + cracked.grad = true; + break; + case EOpImageSampleFootprintGradClampNV: + cracked.lodClamp = true; + cracked.grad = true; + break; + case EOpSubpassLoad: + case EOpSubpassLoadMS: + cracked.subpass = true; + break; + case EOpColorAttachmentReadEXT: + cracked.attachmentEXT = true; + break; + default: + break; + } + } + +protected: + TIntermOperator(TOperator o) : TIntermTyped(EbtFloat), op(o), operationPrecision(EpqNone) {} + TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o), operationPrecision(EpqNone) {} + TOperator op; + // The result precision is in the inherited TType, and is usually meant to be both + // the operation precision and the result precision. However, some more complex things, + // like built-in function calls, distinguish between the two, in which case non-EqpNone + // 'operationPrecision' overrides the result precision as far as operation precision + // is concerned. + TPrecisionQualifier operationPrecision; +}; + +// +// Nodes for all the basic binary math operators. +// +class TIntermBinary : public TIntermOperator { +public: + TIntermBinary(TOperator o) : TIntermOperator(o) {} + virtual void traverse(TIntermTraverser*); + virtual void setLeft(TIntermTyped* n) { left = n; } + virtual void setRight(TIntermTyped* n) { right = n; } + virtual TIntermTyped* getLeft() const { return left; } + virtual TIntermTyped* getRight() const { return right; } + virtual TIntermBinary* getAsBinaryNode() { return this; } + virtual const TIntermBinary* getAsBinaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* left; + TIntermTyped* right; +}; + +// +// Nodes for unary math operators. +// +class TIntermUnary : public TIntermOperator { +public: + TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(nullptr) {} + TIntermUnary(TOperator o) : TIntermOperator(o), operand(nullptr) {} + virtual void traverse(TIntermTraverser*); + virtual void setOperand(TIntermTyped* o) { operand = o; } + virtual TIntermTyped* getOperand() { return operand; } + virtual const TIntermTyped* getOperand() const { return operand; } + virtual TIntermUnary* getAsUnaryNode() { return this; } + virtual const TIntermUnary* getAsUnaryNode() const { return this; } + virtual void updatePrecision(); + void setSpirvInstruction(const TSpirvInstruction& inst) { spirvInst = inst; } + const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; } +protected: + TIntermTyped* operand; + TSpirvInstruction spirvInst; +}; + +typedef TVector TIntermSequence; +typedef TVector TQualifierList; +// +// Nodes that operate on an arbitrary sized set of children. +// +class TIntermAggregate : public TIntermOperator { +public: + TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), pragmaTable(nullptr) { + endLoc.init(); + } + TIntermAggregate(TOperator o) : TIntermOperator(o), pragmaTable(nullptr) { + endLoc.init(); + } + ~TIntermAggregate() { delete pragmaTable; } + virtual TIntermAggregate* getAsAggregate() { return this; } + virtual const TIntermAggregate* getAsAggregate() const { return this; } + virtual void updatePrecision(); + virtual void setOperator(TOperator o) { op = o; } + virtual TIntermSequence& getSequence() { return sequence; } + virtual const TIntermSequence& getSequence() const { return sequence; } + virtual void setName(const TString& n) { name = n; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual void setUserDefined() { userDefined = true; } + virtual bool isUserDefined() { return userDefined; } + virtual TQualifierList& getQualifierList() { return qualifier; } + virtual const TQualifierList& getQualifierList() const { return qualifier; } + void setOptimize(bool o) { optimize = o; } + void setDebug(bool d) { debug = d; } + bool getOptimize() const { return optimize; } + bool getDebug() const { return debug; } + void setPragmaTable(const TPragmaTable& pTable); + const TPragmaTable& getPragmaTable() const { return *pragmaTable; } + void setSpirvInstruction(const TSpirvInstruction& inst) { spirvInst = inst; } + const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; } + + void setEndLoc(TSourceLoc loc) { endLoc = loc; } + TSourceLoc getEndLoc() const { return endLoc; } + + void setLinkType(TLinkType l) { linkType = l; } + TLinkType getLinkType() const { return linkType; } +protected: + TIntermAggregate(const TIntermAggregate&); // disallow copy constructor + TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator + TIntermSequence sequence; + TQualifierList qualifier; + TString name; + bool userDefined; // used for user defined function names + bool optimize; + bool debug; + TPragmaTable* pragmaTable; + TSpirvInstruction spirvInst; + TLinkType linkType = ELinkNone; + + // Marking the end source location of the aggregate. + // This is currently only set for a compound statement or a function body, pointing to '}'. + TSourceLoc endLoc; +}; + +// +// For if tests. +// +class TIntermSelection : public TIntermTyped { +public: + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : + TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : + TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* getCondition() const { return condition; } + virtual void setCondition(TIntermTyped* c) { condition = c; } + virtual TIntermNode* getTrueBlock() const { return trueBlock; } + virtual void setTrueBlock(TIntermTyped* tb) { trueBlock = tb; } + virtual TIntermNode* getFalseBlock() const { return falseBlock; } + virtual void setFalseBlock(TIntermTyped* fb) { falseBlock = fb; } + virtual TIntermSelection* getAsSelectionNode() { return this; } + virtual const TIntermSelection* getAsSelectionNode() const { return this; } + + void setNoShortCircuit() { shortCircuit = false; } + bool getShortCircuit() const { return shortCircuit; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermNode* trueBlock; + TIntermNode* falseBlock; + bool shortCircuit; // normally all if-then-else and all GLSL ?: short-circuit, but HLSL ?: does not + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +// +// For switch statements. Designed use is that a switch will have sequence of nodes +// that are either case/default nodes or a *single* node that represents all the code +// in between (if any) consecutive case/defaults. So, a traversal need only deal with +// 0 or 1 nodes per case/default statement. +// +class TIntermSwitch : public TIntermNode { +public: + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermNode* getCondition() const { return condition; } + virtual TIntermAggregate* getBody() const { return body; } + virtual TIntermSwitch* getAsSwitchNode() { return this; } + virtual const TIntermSwitch* getAsSwitchNode() const { return this; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermAggregate* body; + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +enum TVisit +{ + EvPreVisit, + EvInVisit, + EvPostVisit +}; + +// +// For traversing the tree. User should derive from this, +// put their traversal specific data in it, and then pass +// it to a Traverse method. +// +// When using this, just fill in the methods for nodes you want visited. +// Return false from a pre-visit to skip visiting that node's subtree. +// +// Explicitly set postVisit to true if you want post visiting, otherwise, +// filled in methods will only be called at pre-visit time (before processing +// the subtree). Similarly for inVisit for in-order visiting of nodes with +// multiple children. +// +// If you only want post-visits, explicitly turn off preVisit (and inVisit) +// and turn on postVisit. +// +// In general, for the visit*() methods, return true from interior nodes +// to have the traversal continue on to children. +// +// If you process children yourself, or don't want them processed, return false. +// +class TIntermTraverser { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) : + preVisit(preVisit), + inVisit(inVisit), + postVisit(postVisit), + rightToLeft(rightToLeft), + depth(0), + maxDepth(0) { } + virtual ~TIntermTraverser() { } + + virtual void visitSymbol(TIntermSymbol*) { } + virtual void visitConstantUnion(TIntermConstantUnion*) { } + virtual bool visitBinary(TVisit, TIntermBinary*) { return true; } + virtual bool visitUnary(TVisit, TIntermUnary*) { return true; } + virtual bool visitSelection(TVisit, TIntermSelection*) { return true; } + virtual bool visitAggregate(TVisit, TIntermAggregate*) { return true; } + virtual bool visitLoop(TVisit, TIntermLoop*) { return true; } + virtual bool visitBranch(TVisit, TIntermBranch*) { return true; } + virtual bool visitSwitch(TVisit, TIntermSwitch*) { return true; } + + int getMaxDepth() const { return maxDepth; } + + void incrementDepth(TIntermNode *current) + { + depth++; + maxDepth = (std::max)(maxDepth, depth); + path.push_back(current); + } + + void decrementDepth() + { + depth--; + path.pop_back(); + } + + TIntermNode *getParentNode() + { + return path.size() == 0 ? nullptr : path.back(); + } + + const bool preVisit; + const bool inVisit; + const bool postVisit; + const bool rightToLeft; + +protected: + TIntermTraverser& operator=(TIntermTraverser&); + + int depth; + int maxDepth; + + // All the nodes from root to the current node's parent during traversing. + TVector path; +}; + +// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if +// sized with the same symbol, involving no operations" +inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2) +{ + return node1->getAsSymbolNode() && node2->getAsSymbolNode() && + node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId(); +} + +} // end namespace glslang + +#endif // __INTERMEDIATE_H diff --git a/engine/engine/3rdlibs/glslang/glslang/Include/visibility.h b/engine/engine/3rdlibs/glslang/glslang/Include/visibility.h new file mode 100644 index 00000000..9bb8f3fa --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Include/visibility.h @@ -0,0 +1,54 @@ +// +// Copyright (C) 2023 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifdef GLSLANG_IS_SHARED_LIBRARY + #ifdef _WIN32 + #ifdef GLSLANG_EXPORTING + #define GLSLANG_EXPORT __declspec(dllexport) + #else + #define GLSLANG_EXPORT __declspec(dllimport) + #endif + #elif __GNUC__ >= 4 + #define GLSLANG_EXPORT __attribute__((visibility("default"))) + #endif +#endif // GLSLANG_IS_SHARED_LIBRARY + +#ifndef GLSLANG_EXPORT +#define GLSLANG_EXPORT +#endif + +// Symbols marked with this macro are only meant for public use by the test suite +// and do not appear in publicly installed headers. They are not considered to be +// part of the glslang library ABI. +#define GLSLANG_EXPORT_FOR_TESTS GLSLANG_EXPORT diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Constant.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Constant.cpp new file mode 100644 index 00000000..b0767815 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Constant.cpp @@ -0,0 +1,1291 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "localintermediate.h" +#include +#include +#include +#include + +namespace { + +using namespace glslang; + +const double pi = 3.1415926535897932384626433832795; + +} // end anonymous namespace + + +namespace glslang { + +// +// The fold functions see if an operation on a constant can be done in place, +// without generating run-time code. +// +// Returns the node to keep using, which may or may not be the node passed in. +// +// Note: As of version 1.2, all constant operations must be folded. It is +// not opportunistic, but rather a semantic requirement. +// + +// +// Do folding between a pair of nodes. +// 'this' is the left-hand operand and 'rightConstantNode' is the right-hand operand. +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* rightConstantNode) const +{ + // For most cases, the return type matches the argument type, so set that + // up and just code to exceptions below. + TType returnType; + returnType.shallowCopy(getType()); + + // + // A pair of nodes is to be folded together + // + + const TIntermConstantUnion *rightNode = rightConstantNode->getAsConstantUnion(); + TConstUnionArray leftUnionArray = getConstArray(); + TConstUnionArray rightUnionArray = rightNode->getConstArray(); + + // Figure out the size of the result + int newComps; + int constComps; + switch(op) { + case EOpMatrixTimesMatrix: + newComps = rightNode->getMatrixCols() * getMatrixRows(); + break; + case EOpMatrixTimesVector: + newComps = getMatrixRows(); + break; + case EOpVectorTimesMatrix: + newComps = rightNode->getMatrixCols(); + break; + default: + newComps = getType().computeNumComponents(); + constComps = rightConstantNode->getType().computeNumComponents(); + if (constComps == 1 && newComps > 1) { + // for a case like vec4 f = vec4(2,3,4,5) + 1.2; + TConstUnionArray smearedArray(newComps, rightNode->getConstArray()[0]); + rightUnionArray = smearedArray; + } else if (constComps > 1 && newComps == 1) { + // for a case like vec4 f = 1.2 + vec4(2,3,4,5); + newComps = constComps; + rightUnionArray = rightNode->getConstArray(); + TConstUnionArray smearedArray(newComps, getConstArray()[0]); + leftUnionArray = smearedArray; + returnType.shallowCopy(rightNode->getType()); + } + break; + } + + TConstUnionArray newConstArray(newComps); + TType constBool(EbtBool, EvqConst); + + switch(op) { + case EOpAdd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] + rightUnionArray[i]; + break; + case EOpSub: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] - rightUnionArray[i]; + break; + + case EOpMul: + case EOpVectorTimesScalar: + case EOpMatrixTimesScalar: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] * rightUnionArray[i]; + break; + case EOpMatrixTimesMatrix: + for (int row = 0; row < getMatrixRows(); row++) { + for (int column = 0; column < rightNode->getMatrixCols(); column++) { + double sum = 0.0f; + for (int i = 0; i < rightNode->getMatrixRows(); i++) + sum += leftUnionArray[i * getMatrixRows() + row].getDConst() * rightUnionArray[column * rightNode->getMatrixRows() + i].getDConst(); + newConstArray[column * getMatrixRows() + row].setDConst(sum); + } + } + returnType.shallowCopy(TType(getType().getBasicType(), EvqConst, 0, rightNode->getMatrixCols(), getMatrixRows())); + break; + case EOpDiv: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat: + case EbtFloat16: + case EbtBFloat16: + if (rightUnionArray[i].getDConst() != 0.0) + newConstArray[i].setDConst(leftUnionArray[i].getDConst() / rightUnionArray[i].getDConst()); + else if (leftUnionArray[i].getDConst() > 0.0) + newConstArray[i].setDConst((double)INFINITY); + else if (leftUnionArray[i].getDConst() < 0.0) + newConstArray[i].setDConst(-(double)INFINITY); + else + newConstArray[i].setDConst((double)NAN); + break; + + case EbtInt: + if (rightUnionArray[i] == 0) + newConstArray[i].setIConst(0x7FFFFFFF); + else if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == (int)-0x80000000ll) + newConstArray[i].setIConst((int)-0x80000000ll); + else + newConstArray[i].setIConst(leftUnionArray[i].getIConst() / rightUnionArray[i].getIConst()); + break; + + case EbtUint: + if (rightUnionArray[i] == 0u) + newConstArray[i].setUConst(0xFFFFFFFFu); + else + newConstArray[i].setUConst(leftUnionArray[i].getUConst() / rightUnionArray[i].getUConst()); + break; + + case EbtInt8: + if (rightUnionArray[i] == (signed char)0) + newConstArray[i].setI8Const((signed char)0x7F); + else if (rightUnionArray[i].getI8Const() == (signed char)-1 && leftUnionArray[i].getI8Const() == (signed char)-0x80) + newConstArray[i].setI8Const((signed char)-0x80); + else + newConstArray[i].setI8Const(leftUnionArray[i].getI8Const() / rightUnionArray[i].getI8Const()); + break; + + case EbtUint8: + if (rightUnionArray[i] == (unsigned char)0u) + newConstArray[i].setU8Const((unsigned char)0xFFu); + else + newConstArray[i].setU8Const(leftUnionArray[i].getU8Const() / rightUnionArray[i].getU8Const()); + break; + + case EbtInt16: + if (rightUnionArray[i] == (signed short)0) + newConstArray[i].setI16Const((signed short)0x7FFF); + else if (rightUnionArray[i].getI16Const() == (signed short)-1 && leftUnionArray[i].getI16Const() == (signed short)-0x8000) + newConstArray[i].setI16Const((signed short)-0x8000); + else + newConstArray[i].setI16Const(leftUnionArray[i].getI16Const() / rightUnionArray[i].getI16Const()); + break; + + case EbtUint16: + if (rightUnionArray[i] == (unsigned short)0u) + newConstArray[i].setU16Const((unsigned short)0xFFFFu); + else + newConstArray[i].setU16Const(leftUnionArray[i].getU16Const() / rightUnionArray[i].getU16Const()); + break; + + case EbtInt64: + if (rightUnionArray[i] == 0ll) + newConstArray[i].setI64Const(LLONG_MAX); + else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == LLONG_MIN) + newConstArray[i].setI64Const(LLONG_MIN); + else + newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const()); + break; + + case EbtUint64: + if (rightUnionArray[i] == 0ull) + newConstArray[i].setU64Const(0xFFFFFFFFFFFFFFFFull); + else + newConstArray[i].setU64Const(leftUnionArray[i].getU64Const() / rightUnionArray[i].getU64Const()); + break; + default: + return nullptr; + } + } + break; + + case EOpMatrixTimesVector: + for (int i = 0; i < getMatrixRows(); i++) { + double sum = 0.0f; + for (int j = 0; j < rightNode->getVectorSize(); j++) { + sum += leftUnionArray[j*getMatrixRows() + i].getDConst() * rightUnionArray[j].getDConst(); + } + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, getMatrixRows())); + break; + + case EOpVectorTimesMatrix: + for (int i = 0; i < rightNode->getMatrixCols(); i++) { + double sum = 0.0f; + for (int j = 0; j < getVectorSize(); j++) + sum += leftUnionArray[j].getDConst() * rightUnionArray[i*rightNode->getMatrixRows() + j].getDConst(); + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, rightNode->getMatrixCols())); + break; + + case EOpMod: + for (int i = 0; i < newComps; i++) { + if (rightUnionArray[i] == 0) + newConstArray[i] = leftUnionArray[i]; + else { + switch (getType().getBasicType()) { + case EbtInt: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == INT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; + case EbtInt64: + if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == LLONG_MIN) { + newConstArray[i].setI64Const(0); + break; + } else goto modulo_default; + case EbtInt16: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == SHRT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; + default: + modulo_default: + newConstArray[i] = leftUnionArray[i] % rightUnionArray[i]; + } + } + } + break; + + case EOpRightShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] >> rightUnionArray[i]; + break; + + case EOpLeftShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] << rightUnionArray[i]; + break; + + case EOpAnd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] & rightUnionArray[i]; + break; + case EOpInclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] | rightUnionArray[i]; + break; + case EOpExclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] ^ rightUnionArray[i]; + break; + + case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] && rightUnionArray[i]; + break; + + case EOpLogicalOr: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] || rightUnionArray[i]; + break; + + case EOpLogicalXor: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst((leftUnionArray[i] == rightUnionArray[i]) ? false : true); break; + default: assert(false && "Default missing"); + } + } + break; + + case EOpLessThan: + newConstArray[0].setBConst(leftUnionArray[0] < rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThan: + newConstArray[0].setBConst(leftUnionArray[0] > rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpLessThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] > rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] < rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpEqual: + newConstArray[0].setBConst(rightNode->getConstArray() == leftUnionArray); + returnType.shallowCopy(constBool); + break; + case EOpNotEqual: + newConstArray[0].setBConst(rightNode->getConstArray() != leftUnionArray); + returnType.shallowCopy(constBool); + break; + + default: + return nullptr; + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do single unary node folding +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType) const +{ + // First, size the result, which is mostly the same as the argument's size, + // but not always, and classify what is componentwise. + // Also, eliminate cases that can't be compile-time constant. + int resultSize; + bool componentWise = true; + + int objectSize = getType().computeNumComponents(); + switch (op) { + case EOpDeterminant: + case EOpAny: + case EOpAll: + case EOpLength: + componentWise = false; + resultSize = 1; + break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + // These don't fold + return nullptr; + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + componentWise = false; + resultSize = 1; + break; + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + componentWise = false; + resultSize = 2; + break; + + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + case EOpNormalize: + componentWise = false; + resultSize = objectSize; + break; + + default: + resultSize = objectSize; + break; + } + + // Set up for processing + TConstUnionArray newConstArray(resultSize); + const TConstUnionArray& unionArray = getConstArray(); + + // Process non-component-wise operations + switch (op) { + case EOpLength: + case EOpNormalize: + { + double sum = 0; + for (int i = 0; i < objectSize; i++) + sum += unionArray[i].getDConst() * unionArray[i].getDConst(); + double length = sqrt(sum); + if (op == EOpLength) + newConstArray[0].setDConst(length); + else { + for (int i = 0; i < objectSize; i++) + newConstArray[i].setDConst(unionArray[i].getDConst() / length); + } + break; + } + + case EOpAny: + { + bool result = false; + for (int i = 0; i < objectSize; i++) { + if (unionArray[i].getBConst()) + result = true; + } + newConstArray[0].setBConst(result); + break; + } + case EOpAll: + { + bool result = true; + for (int i = 0; i < objectSize; i++) { + if (! unionArray[i].getBConst()) + result = false; + } + newConstArray[0].setBConst(result); + break; + } + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + + case EOpDeterminant: + case EOpMatrixInverse: + case EOpTranspose: + return nullptr; + + default: + assert(componentWise); + break; + } + + // Turn off the componentwise loop + if (! componentWise) + objectSize = 0; + + // Process component-wise operations + for (int i = 0; i < objectSize; i++) { + // First read the value and convert to i64/u64/f64/bool, then convert + // to the destination type (still 64b), then convert down to the + // destination size. + if (IsOpNumericConv(op)) { + enum ConvType { CONV_FLOAT, CONV_INT, CONV_UINT, CONV_BOOL }; + ConvType srcType = CONV_UINT, dstType = CONV_UINT; + double valf = 0.0; + uint64_t valu = 0; + int64_t vali = 0; + bool valb = false; + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + valf = unionArray[i].getDConst(); + srcType = CONV_FLOAT; + break; + case EbtInt8: + vali = unionArray[i].getI8Const(); + srcType = CONV_INT; + break; + case EbtInt16: + vali = unionArray[i].getI16Const(); + srcType = CONV_INT; + break; + case EbtInt: + vali = unionArray[i].getIConst(); + srcType = CONV_INT; + break; + case EbtInt64: + vali = unionArray[i].getI64Const(); + srcType = CONV_INT; + break; + case EbtUint8: + valu = unionArray[i].getU8Const(); + srcType = CONV_UINT; + break; + case EbtUint16: + valu = unionArray[i].getU16Const(); + srcType = CONV_UINT; + break; + case EbtUint: + valu = unionArray[i].getUConst(); + srcType = CONV_UINT; + break; + case EbtUint64: + valu = unionArray[i].getU64Const(); + srcType = CONV_UINT; + break; + case EbtBool: + valb = unionArray[i].getBConst(); + srcType = CONV_BOOL; + break; + default: + assert(0); + break; + } + + switch (returnType.getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + dstType = CONV_FLOAT; + break; + case EbtInt8: + case EbtInt16: + case EbtInt: + case EbtInt64: + dstType = CONV_INT; + break; + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + dstType = CONV_UINT; + break; + case EbtBool: + dstType = CONV_BOOL; + break; + default: + assert(0); + break; + } + if (dstType == CONV_BOOL) { + switch (srcType) { + case CONV_FLOAT: + valb = (valf != 0.0); break; + case CONV_INT: + valb = (vali != 0.0); break; + case CONV_UINT: + valb = (valu != 0.0); break; + default: + break; + } + } else if (dstType == CONV_FLOAT) { + switch (srcType) { + case CONV_BOOL: + valf = (double)valb; break; + case CONV_INT: + valf = (double)vali; break; + case CONV_UINT: + valf = (double)valu; break; + default: + break; + } + } else if (dstType == CONV_INT) { + switch (srcType) { + case CONV_BOOL: + vali = (int64_t)valb; break; + case CONV_FLOAT: + vali = (int64_t)valf; break; + case CONV_UINT: + vali = (int64_t)valu; break; + default: + break; + } + } else if (dstType == CONV_UINT) { + switch (srcType) { + case CONV_BOOL: + valu = (uint64_t)valb; break; + case CONV_FLOAT: + valu = (uint64_t)valf; break; + case CONV_INT: + valu = (uint64_t)vali; break; + default: + break; + } + } + switch (returnType.getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + newConstArray[i].setDConst(valf); break; + case EbtInt8: + newConstArray[i].setI8Const(static_cast(vali)); break; + case EbtInt16: + newConstArray[i].setI16Const(static_cast(vali)); break; + case EbtInt: + newConstArray[i].setIConst(static_cast(vali)); break; + case EbtInt64: + newConstArray[i].setI64Const(vali); break; + case EbtUint8: + newConstArray[i].setU8Const(static_cast(valu)); break; + case EbtUint16: + newConstArray[i].setU16Const(static_cast(valu)); break; + case EbtUint: + newConstArray[i].setUConst(static_cast(valu)); break; + case EbtUint64: + newConstArray[i].setU64Const(valu); break; + case EbtBool: + newConstArray[i].setBConst(valb); break; + default: + assert(0); + break; + } + continue; + } + switch (op) { + case EOpNegative: + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: newConstArray[i].setDConst(-unionArray[i].getDConst()); break; + // Note: avoid UBSAN error regarding negating 0x80000000 + case EbtInt: newConstArray[i].setIConst( + static_cast(unionArray[i].getIConst()) == 0x80000000 + ? -0x7FFFFFFF - 1 + : -unionArray[i].getIConst()); + break; + case EbtUint: newConstArray[i].setUConst(static_cast(-static_cast(unionArray[i].getUConst()))); break; + case EbtInt8: newConstArray[i].setI8Const(-unionArray[i].getI8Const()); break; + case EbtUint8: newConstArray[i].setU8Const(static_cast(-static_cast(unionArray[i].getU8Const()))); break; + case EbtInt16: newConstArray[i].setI16Const(-unionArray[i].getI16Const()); break; + case EbtUint16:newConstArray[i].setU16Const(static_cast(-static_cast(unionArray[i].getU16Const()))); break; + case EbtInt64: { + int64_t i64val = unionArray[i].getI64Const(); + newConstArray[i].setI64Const(i64val == INT64_MIN ? INT64_MIN : -i64val); + break; + } + case EbtUint64: newConstArray[i].setU64Const(static_cast(-static_cast(unionArray[i].getU64Const()))); break; + default: + return nullptr; + } + break; + case EOpLogicalNot: + case EOpVectorLogicalNot: + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst(!unionArray[i].getBConst()); break; + default: + return nullptr; + } + break; + case EOpBitwiseNot: + newConstArray[i] = ~unionArray[i]; + break; + case EOpRadians: + newConstArray[i].setDConst(unionArray[i].getDConst() * pi / 180.0); + break; + case EOpDegrees: + newConstArray[i].setDConst(unionArray[i].getDConst() * 180.0 / pi); + break; + case EOpSin: + newConstArray[i].setDConst(sin(unionArray[i].getDConst())); + break; + case EOpCos: + newConstArray[i].setDConst(cos(unionArray[i].getDConst())); + break; + case EOpTan: + newConstArray[i].setDConst(tan(unionArray[i].getDConst())); + break; + case EOpAsin: + newConstArray[i].setDConst(asin(unionArray[i].getDConst())); + break; + case EOpAcos: + newConstArray[i].setDConst(acos(unionArray[i].getDConst())); + break; + case EOpAtan: + newConstArray[i].setDConst(atan(unionArray[i].getDConst())); + break; + + case EOpDPdx: + case EOpDPdy: + case EOpFwidth: + case EOpDPdxFine: + case EOpDPdyFine: + case EOpFwidthFine: + case EOpDPdxCoarse: + case EOpDPdyCoarse: + case EOpFwidthCoarse: + // The derivatives are all mandated to create a constant 0. + newConstArray[i].setDConst(0.0); + break; + + case EOpExp: + newConstArray[i].setDConst(exp(unionArray[i].getDConst())); + break; + case EOpLog: + newConstArray[i].setDConst(log(unionArray[i].getDConst())); + break; + case EOpExp2: + newConstArray[i].setDConst(exp2(unionArray[i].getDConst())); + break; + case EOpLog2: + newConstArray[i].setDConst(log2(unionArray[i].getDConst())); + break; + case EOpSqrt: + newConstArray[i].setDConst(sqrt(unionArray[i].getDConst())); + break; + case EOpInverseSqrt: + newConstArray[i].setDConst(1.0 / sqrt(unionArray[i].getDConst())); + break; + + case EOpAbs: + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(fabs(unionArray[i].getDConst())); + else if (unionArray[i].getType() == EbtInt) + newConstArray[i].setIConst(abs(unionArray[i].getIConst())); + else + newConstArray[i] = unionArray[i]; + break; + case EOpSign: + #define SIGN(X) (X == 0 ? 0 : (X < 0 ? -1 : 1)) + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(SIGN(unionArray[i].getDConst())); + else + newConstArray[i].setIConst(SIGN(unionArray[i].getIConst())); + break; + case EOpFloor: + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + break; + case EOpTrunc: + if (unionArray[i].getDConst() > 0) + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + else + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpRound: + newConstArray[i].setDConst(floor(0.5 + unionArray[i].getDConst())); + break; + case EOpRoundEven: + { + double flr = floor(unionArray[i].getDConst()); + bool even = flr / 2.0 == floor(flr / 2.0); + double rounded = even ? ceil(unionArray[i].getDConst() - 0.5) : floor(unionArray[i].getDConst() + 0.5); + newConstArray[i].setDConst(rounded); + break; + } + case EOpCeil: + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpFract: + { + double x = unionArray[i].getDConst(); + newConstArray[i].setDConst(x - floor(x)); + break; + } + + case EOpIsNan: + { + newConstArray[i].setBConst(std::isnan(unionArray[i].getDConst())); + break; + } + case EOpIsInf: + { + newConstArray[i].setBConst(std::isinf(unionArray[i].getDConst())); + break; + } + + case EOpConvPtrToUint64: + case EOpConvUint64ToPtr: + case EOpConstructReference: + newConstArray[i].setU64Const(unionArray[i].getU64Const()); break; + + // TODO: 3.0 Functionality: unary constant folding: the rest of the ops have to be fleshed out + + case EOpSinh: + case EOpCosh: + case EOpTanh: + case EOpAsinh: + case EOpAcosh: + case EOpAtanh: + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + case EOpDoubleBitsToInt64: + case EOpDoubleBitsToUint64: + case EOpInt64BitsToDouble: + case EOpUint64BitsToDouble: + case EOpFloat16BitsToInt16: + case EOpFloat16BitsToUint16: + case EOpInt16BitsToFloat16: + case EOpUint16BitsToFloat16: + default: + return nullptr; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do constant folding for an aggregate node that has all its children +// as constants and an operator that requires constant folding. +// +TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode) +{ + if (aggrNode == nullptr) + return aggrNode; + + if (! areAllChildConst(aggrNode)) + return aggrNode; + + if (aggrNode->isConstructor()) + return foldConstructor(aggrNode); + + TIntermSequence& children = aggrNode->getSequence(); + + // First, see if this is an operation to constant fold, kick out if not, + // see what size the result is if so. + + bool componentwise = false; // will also say componentwise if a scalar argument gets repeated to make per-component results + int objectSize; + switch (aggrNode->getOp()) { + case EOpAtan: + case EOpPow: + case EOpMin: + case EOpMax: + case EOpMix: + case EOpMod: + case EOpClamp: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + componentwise = true; + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpCross: + case EOpReflect: + case EOpRefract: + case EOpFaceForward: + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpDistance: + case EOpDot: + objectSize = 1; + break; + case EOpOuterProduct: + objectSize = children[0]->getAsTyped()->getType().getVectorSize() * + children[1]->getAsTyped()->getType().getVectorSize(); + break; + case EOpStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[1]->getAsTyped()->getType().getVectorSize()); + break; + case EOpSmoothStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[2]->getAsTyped()->getType().getVectorSize()); + break; + case EOpMul: + { + TIntermConstantUnion* left = children[0]->getAsConstantUnion(); + TIntermConstantUnion* right = children[1]->getAsConstantUnion(); + return left->fold(EOpMul, right); + } + default: + return aggrNode; + } + TConstUnionArray newConstArray(objectSize); + + TVector childConstUnions; + for (unsigned int arg = 0; arg < children.size(); ++arg) + childConstUnions.push_back(children[arg]->getAsConstantUnion()->getConstArray()); + + if (componentwise) { + for (int comp = 0; comp < objectSize; comp++) { + + // some arguments are scalars instead of matching vectors; simulate a smear + int arg0comp = std::min(comp, children[0]->getAsTyped()->getType().getVectorSize() - 1); + int arg1comp = 0; + if (children.size() > 1) + arg1comp = std::min(comp, children[1]->getAsTyped()->getType().getVectorSize() - 1); + int arg2comp = 0; + if (children.size() > 2) + arg2comp = std::min(comp, children[2]->getAsTyped()->getType().getVectorSize() - 1); + + switch (aggrNode->getOp()) { + case EOpAtan: + newConstArray[comp].setDConst(atan2(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpPow: + newConstArray[comp].setDConst(pow(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpMod: + { + double arg0 = childConstUnions[0][arg0comp].getDConst(); + double arg1 = childConstUnions[1][arg1comp].getDConst(); + double result = arg0 - arg1 * floor(arg0 / arg1); + newConstArray[comp].setDConst(result); + break; + } + case EOpMin: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; + case EbtInt8: + newConstArray[comp].setI8Const(std::min(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; + default: assert(false && "Default missing"); + } + break; + case EOpMax: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; + case EbtInt8: + newConstArray[comp].setI8Const(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; + default: assert(false && "Default missing"); + } + break; + case EOpClamp: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtBFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()), + childConstUnions[2][arg2comp].getDConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()), + childConstUnions[2][arg2comp].getUConst())); + break; + case EbtInt8: + newConstArray[comp].setI8Const(std::min(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()), + childConstUnions[2][arg2comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()), + childConstUnions[2][arg2comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()), + childConstUnions[2][arg2comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()), + childConstUnions[2][arg2comp].getU16Const())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()), + childConstUnions[2][arg2comp].getIConst())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()), + childConstUnions[2][arg2comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()), + childConstUnions[2][arg2comp].getU64Const())); + break; + default: assert(false && "Default missing"); + } + break; + case EOpLessThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp]); + break; + case EOpGreaterThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp]); + break; + case EOpLessThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp])); + break; + case EOpGreaterThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp])); + break; + case EOpVectorEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] == childConstUnions[1][arg1comp]); + break; + case EOpVectorNotEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] != childConstUnions[1][arg1comp]); + break; + case EOpMix: + if (!children[0]->getAsTyped()->isFloatingDomain()) + return aggrNode; + if (children[2]->getAsTyped()->getBasicType() == EbtBool) { + newConstArray[comp].setDConst(childConstUnions[2][arg2comp].getBConst() + ? childConstUnions[1][arg1comp].getDConst() + : childConstUnions[0][arg0comp].getDConst()); + } else { + newConstArray[comp].setDConst( + childConstUnions[0][arg0comp].getDConst() * (1.0 - childConstUnions[2][arg2comp].getDConst()) + + childConstUnions[1][arg1comp].getDConst() * childConstUnions[2][arg2comp].getDConst()); + } + break; + case EOpStep: + newConstArray[comp].setDConst(childConstUnions[1][arg1comp].getDConst() < childConstUnions[0][arg0comp].getDConst() ? 0.0 : 1.0); + break; + case EOpSmoothStep: + { + double t = (childConstUnions[2][arg2comp].getDConst() - childConstUnions[0][arg0comp].getDConst()) / + (childConstUnions[1][arg1comp].getDConst() - childConstUnions[0][arg0comp].getDConst()); + if (t < 0.0) + t = 0.0; + if (t > 1.0) + t = 1.0; + newConstArray[comp].setDConst(t * t * (3.0 - 2.0 * t)); + break; + } + default: + return aggrNode; + } + } + } else { + // Non-componentwise... + + int numComps = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + double dot; + + switch (aggrNode->getOp()) { + case EOpDistance: + { + double sum = 0.0; + for (int comp = 0; comp < numComps; ++comp) { + double diff = childConstUnions[1][comp].getDConst() - childConstUnions[0][comp].getDConst(); + sum += diff * diff; + } + newConstArray[0].setDConst(sqrt(sum)); + break; + } + case EOpDot: + if (!children[0]->getAsTyped()->isFloatingDomain()) { + return aggrNode; + } + newConstArray[0].setDConst(childConstUnions[0].dot(childConstUnions[1])); + break; + case EOpCross: + newConstArray[0] = childConstUnions[0][1] * childConstUnions[1][2] - childConstUnions[0][2] * childConstUnions[1][1]; + newConstArray[1] = childConstUnions[0][2] * childConstUnions[1][0] - childConstUnions[0][0] * childConstUnions[1][2]; + newConstArray[2] = childConstUnions[0][0] * childConstUnions[1][1] - childConstUnions[0][1] * childConstUnions[1][0]; + break; + case EOpFaceForward: + // If dot(Nref, I) < 0 return N, otherwise return -N: Arguments are (N, I, Nref). + dot = childConstUnions[1].dot(childConstUnions[2]); + for (int comp = 0; comp < numComps; ++comp) { + if (dot < 0.0) + newConstArray[comp] = childConstUnions[0][comp]; + else + newConstArray[comp].setDConst(-childConstUnions[0][comp].getDConst()); + } + break; + case EOpReflect: + // I - 2 * dot(N, I) * N: Arguments are (I, N). + dot = childConstUnions[0].dot(childConstUnions[1]); + dot *= 2.0; + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(childConstUnions[0][comp].getDConst() - dot * childConstUnions[1][comp].getDConst()); + break; + case EOpRefract: + { + // Arguments are (I, N, eta). + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + // if (k < 0.0) + // return dvec(0.0) + // else + // return eta * I - (eta * dot(N, I) + sqrt(k)) * N + dot = childConstUnions[0].dot(childConstUnions[1]); + double eta = childConstUnions[2][0].getDConst(); + double k = 1.0 - eta * eta * (1.0 - dot * dot); + if (k < 0.0) { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(0.0); + } else { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(eta * childConstUnions[0][comp].getDConst() - (eta * dot + sqrt(k)) * childConstUnions[1][comp].getDConst()); + } + break; + } + case EOpOuterProduct: + { + int numRows = numComps; + int numCols = children[1]->getAsConstantUnion()->getType().computeNumComponents(); + for (int row = 0; row < numRows; ++row) + for (int col = 0; col < numCols; ++col) + newConstArray[col * numRows + row] = childConstUnions[0][row] * childConstUnions[1][col]; + break; + } + default: + return aggrNode; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, aggrNode->getType()); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(aggrNode->getLoc()); + + return newNode; +} + +bool TIntermediate::areAllChildConst(TIntermAggregate* aggrNode) +{ + bool allConstant = true; + + // check if all the child nodes are constants so that they can be inserted into + // the parent node + if (aggrNode) { + TIntermSequence& childSequenceVector = aggrNode->getSequence(); + for (TIntermSequence::iterator p = childSequenceVector.begin(); + p != childSequenceVector.end(); p++) { + if (!(*p)->getAsTyped()->getAsConstantUnion()) + return false; + } + } + + return allConstant; +} + +TIntermTyped* TIntermediate::foldConstructor(TIntermAggregate* aggrNode) +{ + bool error = false; + + TConstUnionArray unionArray(aggrNode->getType().computeNumComponents()); + if (aggrNode->getSequence().size() == 1) + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true); + else + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType()); + + if (error) + return aggrNode; + + return addConstantUnion(unionArray, aggrNode->getType(), aggrNode->getLoc()); +} + +// +// Constant folding of a bracket (array-style) dereference or struct-like dot +// dereference. Can handle anything except a multi-character swizzle, though +// all swizzles may go to foldSwizzle(). +// +TIntermTyped* TIntermediate::foldDereference(TIntermTyped* node, int index, const TSourceLoc& loc) +{ + TType dereferencedType(node->getType(), index); + dereferencedType.getQualifier().storage = EvqConst; + TIntermTyped* result = nullptr; + int size = dereferencedType.computeNumComponents(); + + // arrays, vectors, matrices, all use simple multiplicative math + // while structures need to add up heterogeneous members + int start; + if (node->getType().isCoopMat()) + start = 0; + else if (node->isArray() || ! node->isStruct()) + start = size * index; + else { + // it is a structure + assert(node->isStruct()); + start = 0; + for (int i = 0; i < index; ++i) + start += (*node->getType().getStruct())[i].type->computeNumComponents(); + } + + result = addConstantUnion(TConstUnionArray(node->getAsConstantUnion()->getConstArray(), start, size), node->getType(), loc); + + if (result == nullptr) + result = node; + else + result->setType(dereferencedType); + + return result; +} + +// +// Make a constant vector node or constant scalar node, representing a given +// constant vector and constant swizzle into it. +// +TIntermTyped* TIntermediate::foldSwizzle(TIntermTyped* node, TSwizzleSelectors& selectors, const TSourceLoc& loc) +{ + const TConstUnionArray& unionArray = node->getAsConstantUnion()->getConstArray(); + TConstUnionArray constArray(selectors.size()); + + for (int i = 0; i < selectors.size(); i++) + constArray[i] = unionArray[selectors[i]]; + + TIntermTyped* result = addConstantUnion(constArray, node->getType(), loc); + + if (result == nullptr) + result = node; + else + result->setType(TType(node->getBasicType(), EvqConst, selectors.size())); + + return result; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/InfoSink.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/InfoSink.cpp new file mode 100644 index 00000000..d00c4225 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/InfoSink.cpp @@ -0,0 +1,113 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/InfoSink.h" + +#include + +namespace glslang { + +void TInfoSinkBase::append(const char* s) +{ + if (outputStream & EString) { + if (s == nullptr) + sink.append("(null)"); + else { + checkMem(strlen(s)); + sink.append(s); + } + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(s); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", s); +} + +void TInfoSinkBase::append(int count, char c) +{ + if (outputStream & EString) { + checkMem(count); + sink.append(count, c); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) { +// char str[2]; +// str[0] = c; +// str[1] = '\0'; +// OutputDebugString(str); +// } +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%c", c); +} + +void TInfoSinkBase::append(const TPersistString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +void TInfoSinkBase::append(const TString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t.c_str()); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.cpp new file mode 100644 index 00000000..ad0f0ab9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.cpp @@ -0,0 +1,10954 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017, 2022-2024 Arm Limited. +// Modifications Copyright (C) 2020-2021 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Create strings that declare built-in definitions, add built-ins programmatically +// that cannot be expressed in the strings, and establish mappings between +// built-in functions and operators. +// +// Where to put a built-in: +// TBuiltIns::initialize(version,profile) context-independent textual built-ins; add them to the right string +// TBuiltIns::initialize(resources,...) context-dependent textual built-ins; add them to the right string +// TBuiltIns::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// TBuiltIns::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// + +#include +#include +#include "Initialize.h" +#include "span.h" + +namespace glslang { + +// TODO: ARB_Compatability: do full extension support +const bool ARBCompatibility = true; + +const bool ForwardCompatibility = false; + +namespace { + +// +// A set of definitions for tabling of the built-in functions. +// + +// Order matters here, as does correlation with the subsequent +// "const int ..." declarations and the ArgType enumerants. +const char* TypeString[] = { + "bool", "bvec2", "bvec3", "bvec4", + "float", "vec2", "vec3", "vec4", + "int", "ivec2", "ivec3", "ivec4", + "uint", "uvec2", "uvec3", "uvec4", +}; +const int TypeStringCount = sizeof(TypeString) / sizeof(char*); // number of entries in 'TypeString' +const int TypeStringRowShift = 2; // shift amount to go downe one row in 'TypeString' +const int TypeStringColumnMask = (1 << TypeStringRowShift) - 1; // reduce type to its column number in 'TypeString' +const int TypeStringScalarMask = ~TypeStringColumnMask; // take type to its scalar column in 'TypeString' + +enum ArgType { + // numbers hardcoded to correspond to 'TypeString'; order and value matter + TypeB = 1 << 0, // Boolean + TypeF = 1 << 1, // float 32 + TypeI = 1 << 2, // int 32 + TypeU = 1 << 3, // uint 32 + TypeF16 = 1 << 4, // float 16 + TypeF64 = 1 << 5, // float 64 + TypeI8 = 1 << 6, // int 8 + TypeI16 = 1 << 7, // int 16 + TypeI64 = 1 << 8, // int 64 + TypeU8 = 1 << 9, // uint 8 + TypeU16 = 1 << 10, // uint 16 + TypeU64 = 1 << 11, // uint 64 +}; +// Mixtures of the above, to help the function tables +const ArgType TypeFI = static_cast(TypeF | TypeI); +const ArgType TypeFIB = static_cast(TypeF | TypeI | TypeB); +const ArgType TypeIU = static_cast(TypeI | TypeU); + +// The relationships between arguments and return type, whether anything is +// output, or other unusual situations. +enum ArgClass { + ClassRegular = 0, // nothing special, just all vector widths with matching return type; traditional arithmetic + ClassLS = 1 << 0, // the last argument is also held fixed as a (type-matched) scalar while the others cycle + ClassXLS = 1 << 1, // the last argument is exclusively a (type-matched) scalar while the others cycle + ClassLS2 = 1 << 2, // the last two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassFS = 1 << 3, // the first argument is held fixed as a (type-matched) scalar while the others cycle + ClassFS2 = 1 << 4, // the first two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassLO = 1 << 5, // the last argument is an output + ClassB = 1 << 6, // return type cycles through only bool/bvec, matching vector width of args + ClassLB = 1 << 7, // last argument cycles through only bool/bvec, matching vector width of args + ClassV1 = 1 << 8, // scalar only + ClassFIO = 1 << 9, // first argument is inout + ClassRS = 1 << 10, // the return is held scalar as the arguments cycle + ClassNS = 1 << 11, // no scalar prototype + ClassCVN = 1 << 12, // first argument is 'coherent volatile nontemporal' + ClassFO = 1 << 13, // first argument is output + ClassV3 = 1 << 14, // vec3 only +}; +// Mixtures of the above, to help the function tables +const ArgClass ClassV1FIOCVN = (ArgClass)(ClassV1 | ClassFIO | ClassCVN); +const ArgClass ClassBNS = (ArgClass)(ClassB | ClassNS); +const ArgClass ClassRSNS = (ArgClass)(ClassRS | ClassNS); + +// A descriptor, for a single profile, of when something is available. +// If the current profile does not match 'profile' mask below, the other fields +// do not apply (nor validate). +// profiles == EBadProfile is the end of an array of these +struct Versioning { + EProfile profiles; // the profile(s) (mask) that the following fields are valid for + int minExtendedVersion; // earliest version when extensions are enabled; ignored if numExtensions is 0 + int minCoreVersion; // earliest version function is in core; 0 means never + int numExtensions; // how many extensions are in the 'extensions' list + const char** extensions; // list of extension names enabling the function +}; + +EProfile EDesktopProfile = static_cast(ENoProfile | ECoreProfile | ECompatibilityProfile); + +// Declare pointers to put into the table for versioning. + const std::array Es300Desktop130Version = { Versioning{ EEsProfile, 0, 300, 0, nullptr }, + Versioning{ EDesktopProfile, 0, 130, 0, nullptr }, + }; + + const std::array Es310Desktop400Version = { Versioning{ EEsProfile, 0, 310, 0, nullptr }, + Versioning{ EDesktopProfile, 0, 400, 0, nullptr }, + }; + + const std::array Es310Desktop450Version = { Versioning{ EEsProfile, 0, 310, 0, nullptr }, + Versioning{ EDesktopProfile, 0, 450, 0, nullptr }, + }; + +// The main descriptor of what a set of function prototypes can look like, and +// a pointer to extra versioning information, when needed. +struct BuiltInFunction { + TOperator op; // operator to map the name to + const char* name; // function name + int numArguments; // number of arguments (overloads with varying arguments need different entries) + ArgType types; // ArgType mask + ArgClass classes; // the ways this particular function entry manifests + const span versioning; // An empty span means always a valid version +}; + +// The tables can have the same built-in function name more than one time, +// but the exact same prototype must be indicated at most once. +// The prototypes that get declared are the union of all those indicated. +// This is important when different releases add new prototypes for the same name. +// It also also congnitively simpler tiling of the prototype space. +// In practice, most names can be fully represented with one entry. +// +// Table is terminated by an OpNull TOperator. + +const std::array BaseFunctions = { +// TOperator, name, arg-count, ArgType, ArgClass, versioning +// --------- ---- --------- ------- -------- ---------- + BuiltInFunction{ EOpRadians, "radians", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpDegrees, "degrees", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpSin, "sin", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpCos, "cos", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpTan, "tan", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpAsin, "asin", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpAcos, "acos", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpAtan, "atan", 2, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpAtan, "atan", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpPow, "pow", 2, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpExp, "exp", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpLog, "log", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpExp2, "exp2", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpLog2, "log2", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpSqrt, "sqrt", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpInverseSqrt, "inversesqrt", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpAbs, "abs", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpSign, "sign", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpFloor, "floor", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpCeil, "ceil", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpFract, "fract", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpMod, "mod", 2, TypeF, ClassLS, {} }, + BuiltInFunction{ EOpMin, "min", 2, TypeF, ClassLS, {} }, + BuiltInFunction{ EOpMax, "max", 2, TypeF, ClassLS, {} }, + BuiltInFunction{ EOpClamp, "clamp", 3, TypeF, ClassLS2, {} }, + BuiltInFunction{ EOpMix, "mix", 3, TypeF, ClassLS, {} }, + BuiltInFunction{ EOpStep, "step", 2, TypeF, ClassFS, {} }, + BuiltInFunction{ EOpSmoothStep, "smoothstep", 3, TypeF, ClassFS2, {} }, + BuiltInFunction{ EOpNormalize, "normalize", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpFaceForward, "faceforward", 3, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpReflect, "reflect", 2, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpRefract, "refract", 3, TypeF, ClassXLS, {} }, + BuiltInFunction{ EOpLength, "length", 1, TypeF, ClassRS, {} }, + BuiltInFunction{ EOpDistance, "distance", 2, TypeF, ClassRS, {} }, + BuiltInFunction{ EOpDot, "dot", 2, TypeF, ClassRS, {} }, + BuiltInFunction{ EOpCross, "cross", 2, TypeF, ClassV3, {} }, + BuiltInFunction{ EOpLessThan, "lessThan", 2, TypeFI, ClassBNS, {} }, + BuiltInFunction{ EOpLessThanEqual, "lessThanEqual", 2, TypeFI, ClassBNS, {} }, + BuiltInFunction{ EOpGreaterThan, "greaterThan", 2, TypeFI, ClassBNS, {} }, + BuiltInFunction{ EOpGreaterThanEqual, "greaterThanEqual", 2, TypeFI, ClassBNS, {} }, + BuiltInFunction{ EOpVectorEqual, "equal", 2, TypeFIB, ClassBNS, {} }, + BuiltInFunction{ EOpVectorNotEqual, "notEqual", 2, TypeFIB, ClassBNS, {} }, + BuiltInFunction{ EOpAny, "any", 1, TypeB, ClassRSNS, {} }, + BuiltInFunction{ EOpAll, "all", 1, TypeB, ClassRSNS, {} }, + BuiltInFunction{ EOpVectorLogicalNot, "not", 1, TypeB, ClassNS, {} }, + BuiltInFunction{ EOpSinh, "sinh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpCosh, "cosh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpTanh, "tanh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpAsinh, "asinh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpAcosh, "acosh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpAtanh, "atanh", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpAbs, "abs", 1, TypeI, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpSign, "sign", 1, TypeI, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpTrunc, "trunc", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpRound, "round", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpRoundEven, "roundEven", 1, TypeF, ClassRegular, {Es300Desktop130Version} }, + BuiltInFunction{ EOpModf, "modf", 2, TypeF, ClassLO, {Es300Desktop130Version} }, + BuiltInFunction{ EOpMin, "min", 2, TypeIU, ClassLS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpMax, "max", 2, TypeIU, ClassLS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpClamp, "clamp", 3, TypeIU, ClassLS2, {Es300Desktop130Version} }, + BuiltInFunction{ EOpMix, "mix", 3, TypeF, ClassLB, {Es300Desktop130Version} }, + BuiltInFunction{ EOpIsInf, "isinf", 1, TypeF, ClassB, {Es300Desktop130Version} }, + BuiltInFunction{ EOpIsNan, "isnan", 1, TypeF, ClassB, {Es300Desktop130Version} }, + BuiltInFunction{ EOpLessThan, "lessThan", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpLessThanEqual, "lessThanEqual", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpGreaterThan, "greaterThan", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpGreaterThanEqual, "greaterThanEqual", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpVectorEqual, "equal", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpVectorNotEqual, "notEqual", 2, TypeU, ClassBNS, {Es300Desktop130Version} }, + BuiltInFunction{ EOpAtomicAdd, "atomicAdd", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicMin, "atomicMin", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicMax, "atomicMax", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicAnd, "atomicAnd", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicOr, "atomicOr", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicXor, "atomicXor", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicExchange, "atomicExchange", 2, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpAtomicCompSwap, "atomicCompSwap", 3, TypeIU, ClassV1FIOCVN, {Es310Desktop400Version} }, + BuiltInFunction{ EOpMix, "mix", 3, TypeB, ClassRegular, {Es310Desktop450Version} }, + BuiltInFunction{ EOpMix, "mix", 3, TypeIU, ClassLB, {Es310Desktop450Version} }, +}; + +const std::array DerivativeFunctions = { + BuiltInFunction{ EOpDPdx, "dFdx", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpDPdy, "dFdy", 1, TypeF, ClassRegular, {} }, + BuiltInFunction{ EOpFwidth, "fwidth", 1, TypeF, ClassRegular, {} }, +}; + +// For functions declared some other way, but still use the table to relate to operator. +struct CustomFunction { + TOperator op; // operator to map the name to + const char* name; // function name + const span versioning; // An empty span means always a valid version +}; + +const CustomFunction CustomFunctions[] = { + { EOpBarrier, "barrier", {} }, + { EOpMemoryBarrierShared, "memoryBarrierShared", {} }, + { EOpGroupMemoryBarrier, "groupMemoryBarrier", {} }, + { EOpMemoryBarrier, "memoryBarrier", {} }, + { EOpMemoryBarrierBuffer, "memoryBarrierBuffer", {} }, + + { EOpPackSnorm2x16, "packSnorm2x16", {} }, + { EOpUnpackSnorm2x16, "unpackSnorm2x16", {} }, + { EOpPackUnorm2x16, "packUnorm2x16", {} }, + { EOpUnpackUnorm2x16, "unpackUnorm2x16", {} }, + { EOpPackHalf2x16, "packHalf2x16", {} }, + { EOpUnpackHalf2x16, "unpackHalf2x16", {} }, + + { EOpMul, "matrixCompMult", {} }, + { EOpOuterProduct, "outerProduct", {} }, + { EOpTranspose, "transpose", {} }, + { EOpDeterminant, "determinant", {} }, + { EOpMatrixInverse, "inverse", {} }, + { EOpFloatBitsToInt, "floatBitsToInt", {} }, + { EOpFloatBitsToUint, "floatBitsToUint", {} }, + { EOpIntBitsToFloat, "intBitsToFloat", {} }, + { EOpUintBitsToFloat, "uintBitsToFloat", {} }, + + { EOpTextureQuerySize, "textureSize", {} }, + { EOpTextureQueryLod, "textureQueryLod", {} }, + { EOpTextureQueryLod, "textureQueryLOD", {} }, // extension GL_ARB_texture_query_lod + { EOpTextureQueryLevels, "textureQueryLevels", {} }, + { EOpTextureQuerySamples, "textureSamples", {} }, + { EOpTexture, "texture", {} }, + { EOpTextureProj, "textureProj", {} }, + { EOpTextureLod, "textureLod", {} }, + { EOpTextureOffset, "textureOffset", {} }, + { EOpTextureFetch, "texelFetch", {} }, + { EOpTextureFetchOffset, "texelFetchOffset", {} }, + { EOpTextureProjOffset, "textureProjOffset", {} }, + { EOpTextureLodOffset, "textureLodOffset", {} }, + { EOpTextureProjLod, "textureProjLod", {} }, + { EOpTextureProjLodOffset, "textureProjLodOffset", {} }, + { EOpTextureGrad, "textureGrad", {} }, + { EOpTextureGradOffset, "textureGradOffset", {} }, + { EOpTextureProjGrad, "textureProjGrad", {} }, + { EOpTextureProjGradOffset, "textureProjGradOffset", {} }, +}; + +// For the given table of functions, add all the indicated prototypes for each +// one, to be returned in the passed in decls. +void AddTabledBuiltin(TString& decls, const BuiltInFunction& function) +{ + const auto isScalarType = [](int type) { return (type & TypeStringColumnMask) == 0; }; + + // loop across these two: + // 0: the varying arg set, and + // 1: the fixed scalar args + const ArgClass ClassFixed = (ArgClass)(ClassLS | ClassXLS | ClassLS2 | ClassFS | ClassFS2); + for (int fixed = 0; fixed < ((function.classes & ClassFixed) > 0 ? 2 : 1); ++fixed) { + + if (fixed == 0 && (function.classes & ClassXLS)) + continue; + + // walk the type strings in TypeString[] + for (int type = 0; type < TypeStringCount; ++type) { + // skip types not selected: go from type to row number to type bit + if ((function.types & (1 << (type >> TypeStringRowShift))) == 0) + continue; + + // if we aren't on a scalar, and should be, skip + if ((function.classes & ClassV1) && !isScalarType(type)) + continue; + + // if we aren't on a 3-vector, and should be, skip + if ((function.classes & ClassV3) && (type & TypeStringColumnMask) != 2) + continue; + + // skip replication of all arg scalars between the varying arg set and the fixed args + if (fixed == 1 && type == (type & TypeStringScalarMask) && (function.classes & ClassXLS) == 0) + continue; + + // skip scalars when we are told to + if ((function.classes & ClassNS) && isScalarType(type)) + continue; + + // return type + if (function.classes & ClassB) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (function.classes & ClassRS) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + decls.append(" "); + decls.append(function.name); + decls.append("("); + + // arguments + for (int arg = 0; arg < function.numArguments; ++arg) { + if (arg == function.numArguments - 1 && (function.classes & ClassLO)) + decls.append("out "); + if (arg == 0) { + if (function.classes & ClassCVN) + decls.append("coherent volatile nontemporal "); + if (function.classes & ClassFIO) + decls.append("inout "); + if (function.classes & ClassFO) + decls.append("out "); + } + if ((function.classes & ClassLB) && arg == function.numArguments - 1) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (fixed && ((arg == function.numArguments - 1 && (function.classes & (ClassLS | ClassXLS | + ClassLS2))) || + (arg == function.numArguments - 2 && (function.classes & ClassLS2)) || + (arg == 0 && (function.classes & (ClassFS | ClassFS2))) || + (arg == 1 && (function.classes & ClassFS2)))) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + if (arg < function.numArguments - 1) + decls.append(","); + } + decls.append(");\n"); + } + } +} + +// See if the tabled versioning information allows the current version. +bool ValidVersion(const BuiltInFunction& function, int version, EProfile profile, const SpvVersion& /* spVersion */) +{ + // nullptr means always valid + if (function.versioning.empty()) + return true; + + // check for what is said about our current profile + for (const auto& v : function.versioning) { + if ((v.profiles & profile) != 0) { + if (v.minCoreVersion <= version || (v.numExtensions > 0 && v.minExtendedVersion <= version)) + return true; + } + } + + return false; +} + +// Relate a single table of built-ins to their AST operator. +// This can get called redundantly (especially for the common built-ins, when +// called once per stage). This is a performance issue only, not a correctness +// concern. It is done for quality arising from simplicity, as there are subtleties +// to get correct if instead trying to do it surgically. +template +void RelateTabledBuiltins(const FunctionContainer& functions, TSymbolTable& symbolTable) +{ + for (const auto& fn : functions) { + symbolTable.relateToOperator(fn.name, fn.op); + } +} + +} // end anonymous namespace + +// Add declarations for all tables of built-in functions. +void TBuiltIns::addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion) +{ + const auto forEachFunction = [&](TString& decls, const span& functions) { + for (const auto& fn : functions) { + if (ValidVersion(fn, version, profile, spvVersion)) + AddTabledBuiltin(decls, fn); + } + }; + + forEachFunction(commonBuiltins, BaseFunctions); + forEachFunction(stageBuiltins[EShLangFragment], DerivativeFunctions); + + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) + forEachFunction(stageBuiltins[EShLangCompute], DerivativeFunctions); +} + +// Relate all tables of built-ins to the AST operators. +void TBuiltIns::relateTabledBuiltins(int /* version */, EProfile /* profile */, const SpvVersion& /* spvVersion */, EShLanguage /* stage */, TSymbolTable& symbolTable) +{ + RelateTabledBuiltins(BaseFunctions, symbolTable); + RelateTabledBuiltins(DerivativeFunctions, symbolTable); + RelateTabledBuiltins(CustomFunctions, symbolTable); +} + +inline bool IncludeLegacy(int version, EProfile profile, const SpvVersion& spvVersion) +{ + return profile != EEsProfile && (version <= 130 || (spvVersion.spv == 0 && version == 140 && ARBCompatibility) || + profile == ECompatibilityProfile); +} + +// Construct TBuiltInParseables base class. This can be used for language-common constructs. +TBuiltInParseables::TBuiltInParseables() +{ +} + +// Destroy TBuiltInParseables. +TBuiltInParseables::~TBuiltInParseables() +{ +} + +TBuiltIns::TBuiltIns() +{ + // Set up textual representations for making all the permutations + // of texturing/imaging functions. + prefixes[EbtFloat] = ""; + prefixes[EbtInt] = "i"; + prefixes[EbtUint] = "u"; + prefixes[EbtFloat16] = "f16"; + prefixes[EbtInt8] = "i8"; + prefixes[EbtUint8] = "u8"; + prefixes[EbtInt16] = "i16"; + prefixes[EbtUint16] = "u16"; + prefixes[EbtInt64] = "i64"; + prefixes[EbtUint64] = "u64"; + + postfixes[2] = "2"; + postfixes[3] = "3"; + postfixes[4] = "4"; + + // Map from symbolic class of texturing dimension to numeric dimensions. + dimMap[Esd2D] = 2; + dimMap[Esd3D] = 3; + dimMap[EsdCube] = 3; + dimMap[Esd1D] = 1; + dimMap[EsdRect] = 2; + dimMap[EsdBuffer] = 1; + dimMap[EsdSubpass] = 2; // potentially unused for now + dimMap[EsdAttachmentEXT] = 2; // potentially unused for now +} + +TBuiltIns::~TBuiltIns() +{ +} + + +// +// Add all context-independent built-in functions and variables that are present +// for the given version and profile. Share common ones across stages, otherwise +// make stage-specific entries. +// +// Most built-ins variables can be added as simple text strings. Some need to +// be added programmatically, which is done later in IdentifyBuiltIns() below. +// +void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvVersion) +{ + addTabledBuiltins(version, profile, spvVersion); + + //============================================================================ + // + // Prototypes for built-in functions used repeatly by different shaders + // + //============================================================================ + + // + // Derivatives Functions. + // + TString derivativeControls ( + "float dFdxFine(float p);" + "vec2 dFdxFine(vec2 p);" + "vec3 dFdxFine(vec3 p);" + "vec4 dFdxFine(vec4 p);" + + "float dFdyFine(float p);" + "vec2 dFdyFine(vec2 p);" + "vec3 dFdyFine(vec3 p);" + "vec4 dFdyFine(vec4 p);" + + "float fwidthFine(float p);" + "vec2 fwidthFine(vec2 p);" + "vec3 fwidthFine(vec3 p);" + "vec4 fwidthFine(vec4 p);" + + "float dFdxCoarse(float p);" + "vec2 dFdxCoarse(vec2 p);" + "vec3 dFdxCoarse(vec3 p);" + "vec4 dFdxCoarse(vec4 p);" + + "float dFdyCoarse(float p);" + "vec2 dFdyCoarse(vec2 p);" + "vec3 dFdyCoarse(vec3 p);" + "vec4 dFdyCoarse(vec4 p);" + + "float fwidthCoarse(float p);" + "vec2 fwidthCoarse(vec2 p);" + "vec3 fwidthCoarse(vec3 p);" + "vec4 fwidthCoarse(vec4 p);" + ); + + TString derivativesAndControl16bits ( + "float16_t dFdx(float16_t);" + "f16vec2 dFdx(f16vec2);" + "f16vec3 dFdx(f16vec3);" + "f16vec4 dFdx(f16vec4);" + + "float16_t dFdy(float16_t);" + "f16vec2 dFdy(f16vec2);" + "f16vec3 dFdy(f16vec3);" + "f16vec4 dFdy(f16vec4);" + + "float16_t dFdxFine(float16_t);" + "f16vec2 dFdxFine(f16vec2);" + "f16vec3 dFdxFine(f16vec3);" + "f16vec4 dFdxFine(f16vec4);" + + "float16_t dFdyFine(float16_t);" + "f16vec2 dFdyFine(f16vec2);" + "f16vec3 dFdyFine(f16vec3);" + "f16vec4 dFdyFine(f16vec4);" + + "float16_t dFdxCoarse(float16_t);" + "f16vec2 dFdxCoarse(f16vec2);" + "f16vec3 dFdxCoarse(f16vec3);" + "f16vec4 dFdxCoarse(f16vec4);" + + "float16_t dFdyCoarse(float16_t);" + "f16vec2 dFdyCoarse(f16vec2);" + "f16vec3 dFdyCoarse(f16vec3);" + "f16vec4 dFdyCoarse(f16vec4);" + + "float16_t fwidth(float16_t);" + "f16vec2 fwidth(f16vec2);" + "f16vec3 fwidth(f16vec3);" + "f16vec4 fwidth(f16vec4);" + + "float16_t fwidthFine(float16_t);" + "f16vec2 fwidthFine(f16vec2);" + "f16vec3 fwidthFine(f16vec3);" + "f16vec4 fwidthFine(f16vec4);" + + "float16_t fwidthCoarse(float16_t);" + "f16vec2 fwidthCoarse(f16vec2);" + "f16vec3 fwidthCoarse(f16vec3);" + "f16vec4 fwidthCoarse(f16vec4);" + ); + + TString derivativesAndControl64bits ( + "float64_t dFdx(float64_t);" + "f64vec2 dFdx(f64vec2);" + "f64vec3 dFdx(f64vec3);" + "f64vec4 dFdx(f64vec4);" + + "float64_t dFdy(float64_t);" + "f64vec2 dFdy(f64vec2);" + "f64vec3 dFdy(f64vec3);" + "f64vec4 dFdy(f64vec4);" + + "float64_t dFdxFine(float64_t);" + "f64vec2 dFdxFine(f64vec2);" + "f64vec3 dFdxFine(f64vec3);" + "f64vec4 dFdxFine(f64vec4);" + + "float64_t dFdyFine(float64_t);" + "f64vec2 dFdyFine(f64vec2);" + "f64vec3 dFdyFine(f64vec3);" + "f64vec4 dFdyFine(f64vec4);" + + "float64_t dFdxCoarse(float64_t);" + "f64vec2 dFdxCoarse(f64vec2);" + "f64vec3 dFdxCoarse(f64vec3);" + "f64vec4 dFdxCoarse(f64vec4);" + + "float64_t dFdyCoarse(float64_t);" + "f64vec2 dFdyCoarse(f64vec2);" + "f64vec3 dFdyCoarse(f64vec3);" + "f64vec4 dFdyCoarse(f64vec4);" + + "float64_t fwidth(float64_t);" + "f64vec2 fwidth(f64vec2);" + "f64vec3 fwidth(f64vec3);" + "f64vec4 fwidth(f64vec4);" + + "float64_t fwidthFine(float64_t);" + "f64vec2 fwidthFine(f64vec2);" + "f64vec3 fwidthFine(f64vec3);" + "f64vec4 fwidthFine(f64vec4);" + + "float64_t fwidthCoarse(float64_t);" + "f64vec2 fwidthCoarse(f64vec2);" + "f64vec3 fwidthCoarse(f64vec3);" + "f64vec4 fwidthCoarse(f64vec4);" + ); + + //============================================================================ + // + // Prototypes for built-in functions seen by both vertex and fragment shaders. + // + //============================================================================ + + // + // double functions added to desktop 4.00, but not fma, frexp, ldexp, or pack/unpack + // + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + + "double sqrt(double);" + "dvec2 sqrt(dvec2);" + "dvec3 sqrt(dvec3);" + "dvec4 sqrt(dvec4);" + + "double inversesqrt(double);" + "dvec2 inversesqrt(dvec2);" + "dvec3 inversesqrt(dvec3);" + "dvec4 inversesqrt(dvec4);" + + "double abs(double);" + "dvec2 abs(dvec2);" + "dvec3 abs(dvec3);" + "dvec4 abs(dvec4);" + + "double sign(double);" + "dvec2 sign(dvec2);" + "dvec3 sign(dvec3);" + "dvec4 sign(dvec4);" + + "double floor(double);" + "dvec2 floor(dvec2);" + "dvec3 floor(dvec3);" + "dvec4 floor(dvec4);" + + "double trunc(double);" + "dvec2 trunc(dvec2);" + "dvec3 trunc(dvec3);" + "dvec4 trunc(dvec4);" + + "double round(double);" + "dvec2 round(dvec2);" + "dvec3 round(dvec3);" + "dvec4 round(dvec4);" + + "double roundEven(double);" + "dvec2 roundEven(dvec2);" + "dvec3 roundEven(dvec3);" + "dvec4 roundEven(dvec4);" + + "double ceil(double);" + "dvec2 ceil(dvec2);" + "dvec3 ceil(dvec3);" + "dvec4 ceil(dvec4);" + + "double fract(double);" + "dvec2 fract(dvec2);" + "dvec3 fract(dvec3);" + "dvec4 fract(dvec4);" + + "double mod(double, double);" + "dvec2 mod(dvec2 , double);" + "dvec3 mod(dvec3 , double);" + "dvec4 mod(dvec4 , double);" + "dvec2 mod(dvec2 , dvec2);" + "dvec3 mod(dvec3 , dvec3);" + "dvec4 mod(dvec4 , dvec4);" + + "double modf(double, out double);" + "dvec2 modf(dvec2, out dvec2);" + "dvec3 modf(dvec3, out dvec3);" + "dvec4 modf(dvec4, out dvec4);" + + "double min(double, double);" + "dvec2 min(dvec2, double);" + "dvec3 min(dvec3, double);" + "dvec4 min(dvec4, double);" + "dvec2 min(dvec2, dvec2);" + "dvec3 min(dvec3, dvec3);" + "dvec4 min(dvec4, dvec4);" + + "double max(double, double);" + "dvec2 max(dvec2 , double);" + "dvec3 max(dvec3 , double);" + "dvec4 max(dvec4 , double);" + "dvec2 max(dvec2 , dvec2);" + "dvec3 max(dvec3 , dvec3);" + "dvec4 max(dvec4 , dvec4);" + + "double clamp(double, double, double);" + "dvec2 clamp(dvec2 , double, double);" + "dvec3 clamp(dvec3 , double, double);" + "dvec4 clamp(dvec4 , double, double);" + "dvec2 clamp(dvec2 , dvec2 , dvec2);" + "dvec3 clamp(dvec3 , dvec3 , dvec3);" + "dvec4 clamp(dvec4 , dvec4 , dvec4);" + + "double mix(double, double, double);" + "dvec2 mix(dvec2, dvec2, double);" + "dvec3 mix(dvec3, dvec3, double);" + "dvec4 mix(dvec4, dvec4, double);" + "dvec2 mix(dvec2, dvec2, dvec2);" + "dvec3 mix(dvec3, dvec3, dvec3);" + "dvec4 mix(dvec4, dvec4, dvec4);" + "double mix(double, double, bool);" + "dvec2 mix(dvec2, dvec2, bvec2);" + "dvec3 mix(dvec3, dvec3, bvec3);" + "dvec4 mix(dvec4, dvec4, bvec4);" + + "double step(double, double);" + "dvec2 step(dvec2 , dvec2);" + "dvec3 step(dvec3 , dvec3);" + "dvec4 step(dvec4 , dvec4);" + "dvec2 step(double, dvec2);" + "dvec3 step(double, dvec3);" + "dvec4 step(double, dvec4);" + + "double smoothstep(double, double, double);" + "dvec2 smoothstep(dvec2 , dvec2 , dvec2);" + "dvec3 smoothstep(dvec3 , dvec3 , dvec3);" + "dvec4 smoothstep(dvec4 , dvec4 , dvec4);" + "dvec2 smoothstep(double, double, dvec2);" + "dvec3 smoothstep(double, double, dvec3);" + "dvec4 smoothstep(double, double, dvec4);" + + "bool isnan(double);" + "bvec2 isnan(dvec2);" + "bvec3 isnan(dvec3);" + "bvec4 isnan(dvec4);" + + "bool isinf(double);" + "bvec2 isinf(dvec2);" + "bvec3 isinf(dvec3);" + "bvec4 isinf(dvec4);" + + "double length(double);" + "double length(dvec2);" + "double length(dvec3);" + "double length(dvec4);" + + "double distance(double, double);" + "double distance(dvec2 , dvec2);" + "double distance(dvec3 , dvec3);" + "double distance(dvec4 , dvec4);" + + "double dot(double, double);" + "double dot(dvec2 , dvec2);" + "double dot(dvec3 , dvec3);" + "double dot(dvec4 , dvec4);" + + "dvec3 cross(dvec3, dvec3);" + + "double normalize(double);" + "dvec2 normalize(dvec2);" + "dvec3 normalize(dvec3);" + "dvec4 normalize(dvec4);" + + "double faceforward(double, double, double);" + "dvec2 faceforward(dvec2, dvec2, dvec2);" + "dvec3 faceforward(dvec3, dvec3, dvec3);" + "dvec4 faceforward(dvec4, dvec4, dvec4);" + + "double reflect(double, double);" + "dvec2 reflect(dvec2 , dvec2 );" + "dvec3 reflect(dvec3 , dvec3 );" + "dvec4 reflect(dvec4 , dvec4 );" + + "double refract(double, double, double);" + "dvec2 refract(dvec2 , dvec2 , double);" + "dvec3 refract(dvec3 , dvec3 , double);" + "dvec4 refract(dvec4 , dvec4 , double);" + + "dmat2 matrixCompMult(dmat2, dmat2);" + "dmat3 matrixCompMult(dmat3, dmat3);" + "dmat4 matrixCompMult(dmat4, dmat4);" + "dmat2x3 matrixCompMult(dmat2x3, dmat2x3);" + "dmat2x4 matrixCompMult(dmat2x4, dmat2x4);" + "dmat3x2 matrixCompMult(dmat3x2, dmat3x2);" + "dmat3x4 matrixCompMult(dmat3x4, dmat3x4);" + "dmat4x2 matrixCompMult(dmat4x2, dmat4x2);" + "dmat4x3 matrixCompMult(dmat4x3, dmat4x3);" + + "dmat2 outerProduct(dvec2, dvec2);" + "dmat3 outerProduct(dvec3, dvec3);" + "dmat4 outerProduct(dvec4, dvec4);" + "dmat2x3 outerProduct(dvec3, dvec2);" + "dmat3x2 outerProduct(dvec2, dvec3);" + "dmat2x4 outerProduct(dvec4, dvec2);" + "dmat4x2 outerProduct(dvec2, dvec4);" + "dmat3x4 outerProduct(dvec4, dvec3);" + "dmat4x3 outerProduct(dvec3, dvec4);" + + "dmat2 transpose(dmat2);" + "dmat3 transpose(dmat3);" + "dmat4 transpose(dmat4);" + "dmat2x3 transpose(dmat3x2);" + "dmat3x2 transpose(dmat2x3);" + "dmat2x4 transpose(dmat4x2);" + "dmat4x2 transpose(dmat2x4);" + "dmat3x4 transpose(dmat4x3);" + "dmat4x3 transpose(dmat3x4);" + + "double determinant(dmat2);" + "double determinant(dmat3);" + "double determinant(dmat4);" + + "dmat2 inverse(dmat2);" + "dmat3 inverse(dmat3);" + "dmat4 inverse(dmat4);" + + "bvec2 lessThan(dvec2, dvec2);" + "bvec3 lessThan(dvec3, dvec3);" + "bvec4 lessThan(dvec4, dvec4);" + + "bvec2 lessThanEqual(dvec2, dvec2);" + "bvec3 lessThanEqual(dvec3, dvec3);" + "bvec4 lessThanEqual(dvec4, dvec4);" + + "bvec2 greaterThan(dvec2, dvec2);" + "bvec3 greaterThan(dvec3, dvec3);" + "bvec4 greaterThan(dvec4, dvec4);" + + "bvec2 greaterThanEqual(dvec2, dvec2);" + "bvec3 greaterThanEqual(dvec3, dvec3);" + "bvec4 greaterThanEqual(dvec4, dvec4);" + + "bvec2 equal(dvec2, dvec2);" + "bvec3 equal(dvec3, dvec3);" + "bvec4 equal(dvec4, dvec4);" + + "bvec2 notEqual(dvec2, dvec2);" + "bvec3 notEqual(dvec3, dvec3);" + "bvec4 notEqual(dvec4, dvec4);" + + "\n"); + } + + if (profile == EEsProfile && version >= 310) { // Explicit Types + commonBuiltins.append( + + "float64_t sqrt(float64_t);" + "f64vec2 sqrt(f64vec2);" + "f64vec3 sqrt(f64vec3);" + "f64vec4 sqrt(f64vec4);" + + "float64_t inversesqrt(float64_t);" + "f64vec2 inversesqrt(f64vec2);" + "f64vec3 inversesqrt(f64vec3);" + "f64vec4 inversesqrt(f64vec4);" + + "float64_t abs(float64_t);" + "f64vec2 abs(f64vec2);" + "f64vec3 abs(f64vec3);" + "f64vec4 abs(f64vec4);" + + "float64_t sign(float64_t);" + "f64vec2 sign(f64vec2);" + "f64vec3 sign(f64vec3);" + "f64vec4 sign(f64vec4);" + + "float64_t floor(float64_t);" + "f64vec2 floor(f64vec2);" + "f64vec3 floor(f64vec3);" + "f64vec4 floor(f64vec4);" + + "float64_t trunc(float64_t);" + "f64vec2 trunc(f64vec2);" + "f64vec3 trunc(f64vec3);" + "f64vec4 trunc(f64vec4);" + + "float64_t round(float64_t);" + "f64vec2 round(f64vec2);" + "f64vec3 round(f64vec3);" + "f64vec4 round(f64vec4);" + + "float64_t roundEven(float64_t);" + "f64vec2 roundEven(f64vec2);" + "f64vec3 roundEven(f64vec3);" + "f64vec4 roundEven(f64vec4);" + + "float64_t ceil(float64_t);" + "f64vec2 ceil(f64vec2);" + "f64vec3 ceil(f64vec3);" + "f64vec4 ceil(f64vec4);" + + "float64_t fract(float64_t);" + "f64vec2 fract(f64vec2);" + "f64vec3 fract(f64vec3);" + "f64vec4 fract(f64vec4);" + + "float64_t mod(float64_t, float64_t);" + "f64vec2 mod(f64vec2 , float64_t);" + "f64vec3 mod(f64vec3 , float64_t);" + "f64vec4 mod(f64vec4 , float64_t);" + "f64vec2 mod(f64vec2 , f64vec2);" + "f64vec3 mod(f64vec3 , f64vec3);" + "f64vec4 mod(f64vec4 , f64vec4);" + + "float64_t modf(float64_t, out float64_t);" + "f64vec2 modf(f64vec2, out f64vec2);" + "f64vec3 modf(f64vec3, out f64vec3);" + "f64vec4 modf(f64vec4, out f64vec4);" + + "float64_t min(float64_t, float64_t);" + "f64vec2 min(f64vec2, float64_t);" + "f64vec3 min(f64vec3, float64_t);" + "f64vec4 min(f64vec4, float64_t);" + "f64vec2 min(f64vec2, f64vec2);" + "f64vec3 min(f64vec3, f64vec3);" + "f64vec4 min(f64vec4, f64vec4);" + + "float64_t max(float64_t, float64_t);" + "f64vec2 max(f64vec2 , float64_t);" + "f64vec3 max(f64vec3 , float64_t);" + "f64vec4 max(f64vec4 , float64_t);" + "f64vec2 max(f64vec2 , f64vec2);" + "f64vec3 max(f64vec3 , f64vec3);" + "f64vec4 max(f64vec4 , f64vec4);" + + "float64_t clamp(float64_t, float64_t, float64_t);" + "f64vec2 clamp(f64vec2 , float64_t, float64_t);" + "f64vec3 clamp(f64vec3 , float64_t, float64_t);" + "f64vec4 clamp(f64vec4 , float64_t, float64_t);" + "f64vec2 clamp(f64vec2 , f64vec2 , f64vec2);" + "f64vec3 clamp(f64vec3 , f64vec3 , f64vec3);" + "f64vec4 clamp(f64vec4 , f64vec4 , f64vec4);" + + "float64_t mix(float64_t, float64_t, float64_t);" + "f64vec2 mix(f64vec2, f64vec2, float64_t);" + "f64vec3 mix(f64vec3, f64vec3, float64_t);" + "f64vec4 mix(f64vec4, f64vec4, float64_t);" + "f64vec2 mix(f64vec2, f64vec2, f64vec2);" + "f64vec3 mix(f64vec3, f64vec3, f64vec3);" + "f64vec4 mix(f64vec4, f64vec4, f64vec4);" + "float64_t mix(float64_t, float64_t, bool);" + "f64vec2 mix(f64vec2, f64vec2, bvec2);" + "f64vec3 mix(f64vec3, f64vec3, bvec3);" + "f64vec4 mix(f64vec4, f64vec4, bvec4);" + + "float64_t step(float64_t, float64_t);" + "f64vec2 step(f64vec2 , f64vec2);" + "f64vec3 step(f64vec3 , f64vec3);" + "f64vec4 step(f64vec4 , f64vec4);" + "f64vec2 step(float64_t, f64vec2);" + "f64vec3 step(float64_t, f64vec3);" + "f64vec4 step(float64_t, f64vec4);" + + "float64_t smoothstep(float64_t, float64_t, float64_t);" + "f64vec2 smoothstep(f64vec2 , f64vec2 , f64vec2);" + "f64vec3 smoothstep(f64vec3 , f64vec3 , f64vec3);" + "f64vec4 smoothstep(f64vec4 , f64vec4 , f64vec4);" + "f64vec2 smoothstep(float64_t, float64_t, f64vec2);" + "f64vec3 smoothstep(float64_t, float64_t, f64vec3);" + "f64vec4 smoothstep(float64_t, float64_t, f64vec4);" + + "float64_t length(float64_t);" + "float64_t length(f64vec2);" + "float64_t length(f64vec3);" + "float64_t length(f64vec4);" + + "float64_t distance(float64_t, float64_t);" + "float64_t distance(f64vec2 , f64vec2);" + "float64_t distance(f64vec3 , f64vec3);" + "float64_t distance(f64vec4 , f64vec4);" + + "float64_t dot(float64_t, float64_t);" + "float64_t dot(f64vec2 , f64vec2);" + "float64_t dot(f64vec3 , f64vec3);" + "float64_t dot(f64vec4 , f64vec4);" + + "f64vec3 cross(f64vec3, f64vec3);" + + "float64_t normalize(float64_t);" + "f64vec2 normalize(f64vec2);" + "f64vec3 normalize(f64vec3);" + "f64vec4 normalize(f64vec4);" + + "float64_t faceforward(float64_t, float64_t, float64_t);" + "f64vec2 faceforward(f64vec2, f64vec2, f64vec2);" + "f64vec3 faceforward(f64vec3, f64vec3, f64vec3);" + "f64vec4 faceforward(f64vec4, f64vec4, f64vec4);" + + "float64_t reflect(float64_t, float64_t);" + "f64vec2 reflect(f64vec2 , f64vec2 );" + "f64vec3 reflect(f64vec3 , f64vec3 );" + "f64vec4 reflect(f64vec4 , f64vec4 );" + + "float64_t refract(float64_t, float64_t, float64_t);" + "f64vec2 refract(f64vec2 , f64vec2 , float64_t);" + "f64vec3 refract(f64vec3 , f64vec3 , float64_t);" + "f64vec4 refract(f64vec4 , f64vec4 , float64_t);" + + "f64mat2 matrixCompMult(f64mat2, f64mat2);" + "f64mat3 matrixCompMult(f64mat3, f64mat3);" + "f64mat4 matrixCompMult(f64mat4, f64mat4);" + "f64mat2x3 matrixCompMult(f64mat2x3, f64mat2x3);" + "f64mat2x4 matrixCompMult(f64mat2x4, f64mat2x4);" + "f64mat3x2 matrixCompMult(f64mat3x2, f64mat3x2);" + "f64mat3x4 matrixCompMult(f64mat3x4, f64mat3x4);" + "f64mat4x2 matrixCompMult(f64mat4x2, f64mat4x2);" + "f64mat4x3 matrixCompMult(f64mat4x3, f64mat4x3);" + + "f64mat2 outerProduct(f64vec2, f64vec2);" + "f64mat3 outerProduct(f64vec3, f64vec3);" + "f64mat4 outerProduct(f64vec4, f64vec4);" + "f64mat2x3 outerProduct(f64vec3, f64vec2);" + "f64mat3x2 outerProduct(f64vec2, f64vec3);" + "f64mat2x4 outerProduct(f64vec4, f64vec2);" + "f64mat4x2 outerProduct(f64vec2, f64vec4);" + "f64mat3x4 outerProduct(f64vec4, f64vec3);" + "f64mat4x3 outerProduct(f64vec3, f64vec4);" + + "f64mat2 transpose(f64mat2);" + "f64mat3 transpose(f64mat3);" + "f64mat4 transpose(f64mat4);" + "f64mat2x3 transpose(f64mat3x2);" + "f64mat3x2 transpose(f64mat2x3);" + "f64mat2x4 transpose(f64mat4x2);" + "f64mat4x2 transpose(f64mat2x4);" + "f64mat3x4 transpose(f64mat4x3);" + "f64mat4x3 transpose(f64mat3x4);" + + "float64_t determinant(f64mat2);" + "float64_t determinant(f64mat3);" + "float64_t determinant(f64mat4);" + + "f64mat2 inverse(f64mat2);" + "f64mat3 inverse(f64mat3);" + "f64mat4 inverse(f64mat4);" + + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + + "int64_t abs(int64_t);" + "i64vec2 abs(i64vec2);" + "i64vec3 abs(i64vec3);" + "i64vec4 abs(i64vec4);" + + "int64_t sign(int64_t);" + "i64vec2 sign(i64vec2);" + "i64vec3 sign(i64vec3);" + "i64vec4 sign(i64vec4);" + + "int64_t min(int64_t, int64_t);" + "i64vec2 min(i64vec2, int64_t);" + "i64vec3 min(i64vec3, int64_t);" + "i64vec4 min(i64vec4, int64_t);" + "i64vec2 min(i64vec2, i64vec2);" + "i64vec3 min(i64vec3, i64vec3);" + "i64vec4 min(i64vec4, i64vec4);" + "uint64_t min(uint64_t, uint64_t);" + "u64vec2 min(u64vec2, uint64_t);" + "u64vec3 min(u64vec3, uint64_t);" + "u64vec4 min(u64vec4, uint64_t);" + "u64vec2 min(u64vec2, u64vec2);" + "u64vec3 min(u64vec3, u64vec3);" + "u64vec4 min(u64vec4, u64vec4);" + + "int64_t max(int64_t, int64_t);" + "i64vec2 max(i64vec2, int64_t);" + "i64vec3 max(i64vec3, int64_t);" + "i64vec4 max(i64vec4, int64_t);" + "i64vec2 max(i64vec2, i64vec2);" + "i64vec3 max(i64vec3, i64vec3);" + "i64vec4 max(i64vec4, i64vec4);" + "uint64_t max(uint64_t, uint64_t);" + "u64vec2 max(u64vec2, uint64_t);" + "u64vec3 max(u64vec3, uint64_t);" + "u64vec4 max(u64vec4, uint64_t);" + "u64vec2 max(u64vec2, u64vec2);" + "u64vec3 max(u64vec3, u64vec3);" + "u64vec4 max(u64vec4, u64vec4);" + + "int64_t clamp(int64_t, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, int64_t, int64_t);" + "i64vec3 clamp(i64vec3, int64_t, int64_t);" + "i64vec4 clamp(i64vec4, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, i64vec2, i64vec2);" + "i64vec3 clamp(i64vec3, i64vec3, i64vec3);" + "i64vec4 clamp(i64vec4, i64vec4, i64vec4);" + "uint64_t clamp(uint64_t, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, uint64_t, uint64_t);" + "u64vec3 clamp(u64vec3, uint64_t, uint64_t);" + "u64vec4 clamp(u64vec4, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, u64vec2, u64vec2);" + "u64vec3 clamp(u64vec3, u64vec3, u64vec3);" + "u64vec4 clamp(u64vec4, u64vec4, u64vec4);" + + "int64_t mix(int64_t, int64_t, bool);" + "i64vec2 mix(i64vec2, i64vec2, bvec2);" + "i64vec3 mix(i64vec3, i64vec3, bvec3);" + "i64vec4 mix(i64vec4, i64vec4, bvec4);" + "uint64_t mix(uint64_t, uint64_t, bool);" + "u64vec2 mix(u64vec2, u64vec2, bvec2);" + "u64vec3 mix(u64vec3, u64vec3, bvec3);" + "u64vec4 mix(u64vec4, u64vec4, bvec4);" + + "int64_t doubleBitsToInt64(float64_t);" + "i64vec2 doubleBitsToInt64(f64vec2);" + "i64vec3 doubleBitsToInt64(f64vec3);" + "i64vec4 doubleBitsToInt64(f64vec4);" + + "uint64_t doubleBitsToUint64(float64_t);" + "u64vec2 doubleBitsToUint64(f64vec2);" + "u64vec3 doubleBitsToUint64(f64vec3);" + "u64vec4 doubleBitsToUint64(f64vec4);" + + "float64_t int64BitsToDouble(int64_t);" + "f64vec2 int64BitsToDouble(i64vec2);" + "f64vec3 int64BitsToDouble(i64vec3);" + "f64vec4 int64BitsToDouble(i64vec4);" + + "float64_t uint64BitsToDouble(uint64_t);" + "f64vec2 uint64BitsToDouble(u64vec2);" + "f64vec3 uint64BitsToDouble(u64vec3);" + "f64vec4 uint64BitsToDouble(u64vec4);" + + "int64_t packInt2x32(ivec2);" + "uint64_t packUint2x32(uvec2);" + "ivec2 unpackInt2x32(int64_t);" + "uvec2 unpackUint2x32(uint64_t);" + + "bvec2 lessThan(i64vec2, i64vec2);" + "bvec3 lessThan(i64vec3, i64vec3);" + "bvec4 lessThan(i64vec4, i64vec4);" + "bvec2 lessThan(u64vec2, u64vec2);" + "bvec3 lessThan(u64vec3, u64vec3);" + "bvec4 lessThan(u64vec4, u64vec4);" + + "bvec2 lessThanEqual(i64vec2, i64vec2);" + "bvec3 lessThanEqual(i64vec3, i64vec3);" + "bvec4 lessThanEqual(i64vec4, i64vec4);" + "bvec2 lessThanEqual(u64vec2, u64vec2);" + "bvec3 lessThanEqual(u64vec3, u64vec3);" + "bvec4 lessThanEqual(u64vec4, u64vec4);" + + "bvec2 greaterThan(i64vec2, i64vec2);" + "bvec3 greaterThan(i64vec3, i64vec3);" + "bvec4 greaterThan(i64vec4, i64vec4);" + "bvec2 greaterThan(u64vec2, u64vec2);" + "bvec3 greaterThan(u64vec3, u64vec3);" + "bvec4 greaterThan(u64vec4, u64vec4);" + + "bvec2 greaterThanEqual(i64vec2, i64vec2);" + "bvec3 greaterThanEqual(i64vec3, i64vec3);" + "bvec4 greaterThanEqual(i64vec4, i64vec4);" + "bvec2 greaterThanEqual(u64vec2, u64vec2);" + "bvec3 greaterThanEqual(u64vec3, u64vec3);" + "bvec4 greaterThanEqual(u64vec4, u64vec4);" + + "bvec2 equal(i64vec2, i64vec2);" + "bvec3 equal(i64vec3, i64vec3);" + "bvec4 equal(i64vec4, i64vec4);" + "bvec2 equal(u64vec2, u64vec2);" + "bvec3 equal(u64vec3, u64vec3);" + "bvec4 equal(u64vec4, u64vec4);" + + "bvec2 notEqual(i64vec2, i64vec2);" + "bvec3 notEqual(i64vec3, i64vec3);" + "bvec4 notEqual(i64vec4, i64vec4);" + "bvec2 notEqual(u64vec2, u64vec2);" + "bvec3 notEqual(u64vec3, u64vec3);" + "bvec4 notEqual(u64vec4, u64vec4);" + + "int64_t bitCount(int64_t);" + "i64vec2 bitCount(i64vec2);" + "i64vec3 bitCount(i64vec3);" + "i64vec4 bitCount(i64vec4);" + + "int64_t bitCount(uint64_t);" + "i64vec2 bitCount(u64vec2);" + "i64vec3 bitCount(u64vec3);" + "i64vec4 bitCount(u64vec4);" + + "int64_t findLSB(int64_t);" + "i64vec2 findLSB(i64vec2);" + "i64vec3 findLSB(i64vec3);" + "i64vec4 findLSB(i64vec4);" + + "int64_t findLSB(uint64_t);" + "i64vec2 findLSB(u64vec2);" + "i64vec3 findLSB(u64vec3);" + "i64vec4 findLSB(u64vec4);" + + "int64_t findMSB(int64_t);" + "i64vec2 findMSB(i64vec2);" + "i64vec3 findMSB(i64vec3);" + "i64vec4 findMSB(i64vec4);" + + "int64_t findMSB(uint64_t);" + "i64vec2 findMSB(u64vec2);" + "i64vec3 findMSB(u64vec3);" + "i64vec4 findMSB(u64vec4);" + + "\n" + ); + } + + // GL_AMD_shader_trinary_minmax + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "float min3(float, float, float);" + "vec2 min3(vec2, vec2, vec2);" + "vec3 min3(vec3, vec3, vec3);" + "vec4 min3(vec4, vec4, vec4);" + + "int min3(int, int, int);" + "ivec2 min3(ivec2, ivec2, ivec2);" + "ivec3 min3(ivec3, ivec3, ivec3);" + "ivec4 min3(ivec4, ivec4, ivec4);" + + "uint min3(uint, uint, uint);" + "uvec2 min3(uvec2, uvec2, uvec2);" + "uvec3 min3(uvec3, uvec3, uvec3);" + "uvec4 min3(uvec4, uvec4, uvec4);" + + "float max3(float, float, float);" + "vec2 max3(vec2, vec2, vec2);" + "vec3 max3(vec3, vec3, vec3);" + "vec4 max3(vec4, vec4, vec4);" + + "int max3(int, int, int);" + "ivec2 max3(ivec2, ivec2, ivec2);" + "ivec3 max3(ivec3, ivec3, ivec3);" + "ivec4 max3(ivec4, ivec4, ivec4);" + + "uint max3(uint, uint, uint);" + "uvec2 max3(uvec2, uvec2, uvec2);" + "uvec3 max3(uvec3, uvec3, uvec3);" + "uvec4 max3(uvec4, uvec4, uvec4);" + + "float mid3(float, float, float);" + "vec2 mid3(vec2, vec2, vec2);" + "vec3 mid3(vec3, vec3, vec3);" + "vec4 mid3(vec4, vec4, vec4);" + + "int mid3(int, int, int);" + "ivec2 mid3(ivec2, ivec2, ivec2);" + "ivec3 mid3(ivec3, ivec3, ivec3);" + "ivec4 mid3(ivec4, ivec4, ivec4);" + + "uint mid3(uint, uint, uint);" + "uvec2 mid3(uvec2, uvec2, uvec2);" + "uvec3 mid3(uvec3, uvec3, uvec3);" + "uvec4 mid3(uvec4, uvec4, uvec4);" + + "float16_t min3(float16_t, float16_t, float16_t);" + "f16vec2 min3(f16vec2, f16vec2, f16vec2);" + "f16vec3 min3(f16vec3, f16vec3, f16vec3);" + "f16vec4 min3(f16vec4, f16vec4, f16vec4);" + + "float16_t max3(float16_t, float16_t, float16_t);" + "f16vec2 max3(f16vec2, f16vec2, f16vec2);" + "f16vec3 max3(f16vec3, f16vec3, f16vec3);" + "f16vec4 max3(f16vec4, f16vec4, f16vec4);" + + "float16_t mid3(float16_t, float16_t, float16_t);" + "f16vec2 mid3(f16vec2, f16vec2, f16vec2);" + "f16vec3 mid3(f16vec3, f16vec3, f16vec3);" + "f16vec4 mid3(f16vec4, f16vec4, f16vec4);" + + "int16_t min3(int16_t, int16_t, int16_t);" + "i16vec2 min3(i16vec2, i16vec2, i16vec2);" + "i16vec3 min3(i16vec3, i16vec3, i16vec3);" + "i16vec4 min3(i16vec4, i16vec4, i16vec4);" + + "int16_t max3(int16_t, int16_t, int16_t);" + "i16vec2 max3(i16vec2, i16vec2, i16vec2);" + "i16vec3 max3(i16vec3, i16vec3, i16vec3);" + "i16vec4 max3(i16vec4, i16vec4, i16vec4);" + + "int16_t mid3(int16_t, int16_t, int16_t);" + "i16vec2 mid3(i16vec2, i16vec2, i16vec2);" + "i16vec3 mid3(i16vec3, i16vec3, i16vec3);" + "i16vec4 mid3(i16vec4, i16vec4, i16vec4);" + + "uint16_t min3(uint16_t, uint16_t, uint16_t);" + "u16vec2 min3(u16vec2, u16vec2, u16vec2);" + "u16vec3 min3(u16vec3, u16vec3, u16vec3);" + "u16vec4 min3(u16vec4, u16vec4, u16vec4);" + + "uint16_t max3(uint16_t, uint16_t, uint16_t);" + "u16vec2 max3(u16vec2, u16vec2, u16vec2);" + "u16vec3 max3(u16vec3, u16vec3, u16vec3);" + "u16vec4 max3(u16vec4, u16vec4, u16vec4);" + + "uint16_t mid3(uint16_t, uint16_t, uint16_t);" + "u16vec2 mid3(u16vec2, u16vec2, u16vec2);" + "u16vec3 mid3(u16vec3, u16vec3, u16vec3);" + "u16vec4 mid3(u16vec4, u16vec4, u16vec4);" + + "\n" + ); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 430)) { + commonBuiltins.append( + "uint atomicAdd(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicAdd(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicMin(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicMin(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicMax(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicMax(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicAnd(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicAnd(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicOr (coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicOr (coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicXor(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicXor(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicExchange(coherent volatile nontemporal inout uint, uint, int, int, int);" + " int atomicExchange(coherent volatile nontemporal inout int, int, int, int, int);" + + "uint atomicCompSwap(coherent volatile nontemporal inout uint, uint, uint, int, int, int, int, int);" + " int atomicCompSwap(coherent volatile nontemporal inout int, int, int, int, int, int, int, int);" + + "uint atomicLoad(coherent volatile nontemporal in uint, int, int, int);" + " int atomicLoad(coherent volatile nontemporal in int, int, int, int);" + + "void atomicStore(coherent volatile nontemporal out uint, uint, int, int, int);" + "void atomicStore(coherent volatile nontemporal out int, int, int, int, int);" + + "\n"); + } + + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "uint64_t atomicMin(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicMin(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicMin(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMin(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + "float16_t atomicMin(coherent volatile nontemporal inout float16_t, float16_t);" + "float16_t atomicMin(coherent volatile nontemporal inout float16_t, float16_t, int, int, int);" + " float atomicMin(coherent volatile nontemporal inout float, float);" + " float atomicMin(coherent volatile nontemporal inout float, float, int, int, int);" + " double atomicMin(coherent volatile nontemporal inout double, double);" + " double atomicMin(coherent volatile nontemporal inout double, double, int, int, int);" + + "uint64_t atomicMax(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicMax(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicMax(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMax(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + "float16_t atomicMax(coherent volatile nontemporal inout float16_t, float16_t);" + "float16_t atomicMax(coherent volatile nontemporal inout float16_t, float16_t, int, int, int);" + " float atomicMax(coherent volatile nontemporal inout float, float);" + " float atomicMax(coherent volatile nontemporal inout float, float, int, int, int);" + " double atomicMax(coherent volatile nontemporal inout double, double);" + " double atomicMax(coherent volatile nontemporal inout double, double, int, int, int);" + + "uint64_t atomicAnd(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicAnd(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicAnd(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAnd(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicOr (coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicOr (coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicOr (coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicOr (coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicXor(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicXor(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicXor(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicXor(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicAdd(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicAdd(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicAdd(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAdd(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + "float16_t atomicAdd(coherent volatile nontemporal inout float16_t, float16_t);" + "float16_t atomicAdd(coherent volatile nontemporal inout float16_t, float16_t, int, int, int);" + " float atomicAdd(coherent volatile nontemporal inout float, float);" + " float atomicAdd(coherent volatile nontemporal inout float, float, int, int, int);" + " double atomicAdd(coherent volatile nontemporal inout double, double);" + " double atomicAdd(coherent volatile nontemporal inout double, double, int, int, int);" + + "uint64_t atomicExchange(coherent volatile nontemporal inout uint64_t, uint64_t);" + " int64_t atomicExchange(coherent volatile nontemporal inout int64_t, int64_t);" + "uint64_t atomicExchange(coherent volatile nontemporal inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicExchange(coherent volatile nontemporal inout int64_t, int64_t, int, int, int);" + "float16_t atomicExchange(coherent volatile nontemporal inout float16_t, float16_t);" + "float16_t atomicExchange(coherent volatile nontemporal inout float16_t, float16_t, int, int, int);" + " float atomicExchange(coherent volatile nontemporal inout float, float);" + " float atomicExchange(coherent volatile nontemporal inout float, float, int, int, int);" + " double atomicExchange(coherent volatile nontemporal inout double, double);" + " double atomicExchange(coherent volatile nontemporal inout double, double, int, int, int);" + + "uint64_t atomicCompSwap(coherent volatile nontemporal inout uint64_t, uint64_t, uint64_t);" + " int64_t atomicCompSwap(coherent volatile nontemporal inout int64_t, int64_t, int64_t);" + "uint64_t atomicCompSwap(coherent volatile nontemporal inout uint64_t, uint64_t, uint64_t, int, int, int, int, int);" + " int64_t atomicCompSwap(coherent volatile nontemporal inout int64_t, int64_t, int64_t, int, int, int, int, int);" + + "uint64_t atomicLoad(coherent volatile nontemporal in uint64_t, int, int, int);" + " int64_t atomicLoad(coherent volatile nontemporal in int64_t, int, int, int);" + "float16_t atomicLoad(coherent volatile nontemporal in float16_t, int, int, int);" + " float atomicLoad(coherent volatile nontemporal in float, int, int, int);" + " double atomicLoad(coherent volatile nontemporal in double, int, int, int);" + + "void atomicStore(coherent volatile nontemporal out uint64_t, uint64_t, int, int, int);" + "void atomicStore(coherent volatile nontemporal out int64_t, int64_t, int, int, int);" + "void atomicStore(coherent volatile nontemporal out float16_t, float16_t, int, int, int);" + "void atomicStore(coherent volatile nontemporal out float, float, int, int, int);" + "void atomicStore(coherent volatile nontemporal out double, double, int, int, int);" + "\n"); + } + + // NV_shader_atomic_fp16_vector + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "f16vec2 atomicAdd(coherent volatile nontemporal inout f16vec2, f16vec2);" + "f16vec4 atomicAdd(coherent volatile nontemporal inout f16vec4, f16vec4);" + "f16vec2 atomicMin(coherent volatile nontemporal inout f16vec2, f16vec2);" + "f16vec4 atomicMin(coherent volatile nontemporal inout f16vec4, f16vec4);" + "f16vec2 atomicMax(coherent volatile nontemporal inout f16vec2, f16vec2);" + "f16vec4 atomicMax(coherent volatile nontemporal inout f16vec4, f16vec4);" + "f16vec2 atomicExchange(coherent volatile nontemporal inout f16vec2, f16vec2);" + "f16vec4 atomicExchange(coherent volatile nontemporal inout f16vec4, f16vec4);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { // GL_ARB_shader_bit_encoding + commonBuiltins.append( + "int floatBitsToInt(highp float value);" + "ivec2 floatBitsToInt(highp vec2 value);" + "ivec3 floatBitsToInt(highp vec3 value);" + "ivec4 floatBitsToInt(highp vec4 value);" + + "uint floatBitsToUint(highp float value);" + "uvec2 floatBitsToUint(highp vec2 value);" + "uvec3 floatBitsToUint(highp vec3 value);" + "uvec4 floatBitsToUint(highp vec4 value);" + + "float intBitsToFloat(highp int value);" + "vec2 intBitsToFloat(highp ivec2 value);" + "vec3 intBitsToFloat(highp ivec3 value);" + "vec4 intBitsToFloat(highp ivec4 value);" + + "float uintBitsToFloat(highp uint value);" + "vec2 uintBitsToFloat(highp uvec2 value);" + "vec3 uintBitsToFloat(highp uvec3 value);" + "vec4 uintBitsToFloat(highp uvec4 value);" + + "\n"); + } + + if ((profile != EEsProfile && version >= 400) || + (profile == EEsProfile && version >= 310)) { // GL_OES_gpu_shader5 + + commonBuiltins.append( + "float fma(float, float, float );" + "vec2 fma(vec2, vec2, vec2 );" + "vec3 fma(vec3, vec3, vec3 );" + "vec4 fma(vec4, vec4, vec4 );" + "\n"); + } + + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double fma(double, double, double);" + "dvec2 fma(dvec2, dvec2, dvec2 );" + "dvec3 fma(dvec3, dvec3, dvec3 );" + "dvec4 fma(dvec4, dvec4, dvec4 );" + "\n"); + } + + if (profile == EEsProfile && version >= 310) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "float64_t fma(float64_t, float64_t, float64_t);" + "f64vec2 fma(f64vec2, f64vec2, f64vec2 );" + "f64vec3 fma(f64vec3, f64vec3, f64vec3 );" + "f64vec4 fma(f64vec4, f64vec4, f64vec4 );" + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + "float frexp(highp float, out highp int);" + "vec2 frexp(highp vec2, out highp ivec2);" + "vec3 frexp(highp vec3, out highp ivec3);" + "vec4 frexp(highp vec4, out highp ivec4);" + + "float ldexp(highp float, highp int);" + "vec2 ldexp(highp vec2, highp ivec2);" + "vec3 ldexp(highp vec3, highp ivec3);" + "vec4 ldexp(highp vec4, highp ivec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double frexp(double, out int);" + "dvec2 frexp( dvec2, out ivec2);" + "dvec3 frexp( dvec3, out ivec3);" + "dvec4 frexp( dvec4, out ivec4);" + + "double ldexp(double, int);" + "dvec2 ldexp( dvec2, ivec2);" + "dvec3 ldexp( dvec3, ivec3);" + "dvec4 ldexp( dvec4, ivec4);" + + "double packDouble2x32(uvec2);" + "uvec2 unpackDouble2x32(double);" + + "\n"); + } + + if (profile == EEsProfile && version >= 310) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "float64_t frexp(float64_t, out int);" + "f64vec2 frexp( f64vec2, out ivec2);" + "f64vec3 frexp( f64vec3, out ivec3);" + "f64vec4 frexp( f64vec4, out ivec4);" + + "float64_t ldexp(float64_t, int);" + "f64vec2 ldexp( f64vec2, ivec2);" + "f64vec3 ldexp( f64vec3, ivec3);" + "f64vec4 ldexp( f64vec4, ivec4);" + + "\n"); + } + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packUnorm2x16(vec2);" + "vec2 unpackUnorm2x16(highp uint);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm2x16(vec2);" + " vec2 unpackSnorm2x16(highp uint);" + "highp uint packHalf2x16(vec2);" + "\n"); + } + + if (profile == EEsProfile && version >= 300) { + commonBuiltins.append( + "mediump vec2 unpackHalf2x16(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + " vec2 unpackHalf2x16(highp uint);" + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm4x8(vec4);" + "highp uint packUnorm4x8(vec4);" + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "mediump vec4 unpackSnorm4x8(highp uint);" + "mediump vec4 unpackUnorm4x8(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + "vec4 unpackSnorm4x8(highp uint);" + "vec4 unpackUnorm4x8(highp uint);" + "\n"); + } + + // + // Matrix Functions. + // + commonBuiltins.append( + "mat2 matrixCompMult(mat2 x, mat2 y);" + "mat3 matrixCompMult(mat3 x, mat3 y);" + "mat4 matrixCompMult(mat4 x, mat4 y);" + + "\n"); + + // 120 is correct for both ES and desktop + if (version >= 120) { + commonBuiltins.append( + "mat2 outerProduct(vec2 c, vec2 r);" + "mat3 outerProduct(vec3 c, vec3 r);" + "mat4 outerProduct(vec4 c, vec4 r);" + "mat2x3 outerProduct(vec3 c, vec2 r);" + "mat3x2 outerProduct(vec2 c, vec3 r);" + "mat2x4 outerProduct(vec4 c, vec2 r);" + "mat4x2 outerProduct(vec2 c, vec4 r);" + "mat3x4 outerProduct(vec4 c, vec3 r);" + "mat4x3 outerProduct(vec3 c, vec4 r);" + + "mat2 transpose(mat2 m);" + "mat3 transpose(mat3 m);" + "mat4 transpose(mat4 m);" + "mat2x3 transpose(mat3x2 m);" + "mat3x2 transpose(mat2x3 m);" + "mat2x4 transpose(mat4x2 m);" + "mat4x2 transpose(mat2x4 m);" + "mat3x4 transpose(mat4x3 m);" + "mat4x3 transpose(mat3x4 m);" + + "mat2x3 matrixCompMult(mat2x3, mat2x3);" + "mat2x4 matrixCompMult(mat2x4, mat2x4);" + "mat3x2 matrixCompMult(mat3x2, mat3x2);" + "mat3x4 matrixCompMult(mat3x4, mat3x4);" + "mat4x2 matrixCompMult(mat4x2, mat4x2);" + "mat4x3 matrixCompMult(mat4x3, mat4x3);" + + "\n"); + + // 150 is correct for both ES and desktop + if (version >= 150) { + commonBuiltins.append( + "float determinant(mat2 m);" + "float determinant(mat3 m);" + "float determinant(mat4 m);" + + "mat2 inverse(mat2 m);" + "mat3 inverse(mat3 m);" + "mat4 inverse(mat4 m);" + + "\n"); + } + } + + // + // Original-style texture functions existing in all stages. + // (Per-stage functions below.) + // + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture2D(sampler2D, vec2);" + + "vec4 texture2DProj(sampler2D, vec3);" + "vec4 texture2DProj(sampler2D, vec4);" + + "vec4 texture3D(sampler3D, vec3);" // OES_texture_3D, but caught by keyword check + "vec4 texture3DProj(sampler3D, vec4);" // OES_texture_3D, but caught by keyword check + + "vec4 textureCube(samplerCube, vec3);" + + "\n"); + } + } + + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture1D(sampler1D, float);" + + "vec4 texture1DProj(sampler1D, vec2);" + "vec4 texture1DProj(sampler1D, vec4);" + + "vec4 shadow1D(sampler1DShadow, vec3);" + "vec4 shadow2D(sampler2DShadow, vec3);" + "vec4 shadow1DProj(sampler1DShadow, vec4);" + "vec4 shadow2DProj(sampler2DShadow, vec4);" + + "vec4 texture2DRect(sampler2DRect, vec2);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + + "vec4 texture1DArray(sampler1DArray, vec2);" // GL_EXT_texture_array + "vec4 texture2DArray(sampler2DArray, vec3);" // GL_EXT_texture_array + "vec4 shadow1DArray(sampler1DArrayShadow, vec3);" // GL_EXT_texture_array + "vec4 shadow2DArray(sampler2DArrayShadow, vec4);" // GL_EXT_texture_array + "vec4 texture1DArray(sampler1DArray, vec2, float);" // GL_EXT_texture_array + "vec4 texture2DArray(sampler2DArray, vec3, float);" // GL_EXT_texture_array + "vec4 shadow1DArray(sampler1DArrayShadow, vec3, float);" // GL_EXT_texture_array + "vec4 texture1DArrayLod(sampler1DArray, vec2, float);" // GL_EXT_texture_array + "vec4 texture2DArrayLod(sampler2DArray, vec3, float);" // GL_EXT_texture_array + "vec4 shadow1DArrayLod(sampler1DArrayShadow, vec3, float);" // GL_EXT_texture_array + "\n"); + } + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + if (version < 300) { + commonBuiltins.append( + "vec4 texture2D(samplerExternalOES, vec2 coord);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external + "\n"); + } else { + commonBuiltins.append( + "highp ivec2 textureSize(samplerExternalOES, int lod);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 texelFetch(samplerExternalOES, ivec2, int lod);" // GL_OES_EGL_image_external_essl3 + "\n"); + } + commonBuiltins.append( + "highp ivec2 textureSize(__samplerExternal2DY2YEXT, int lod);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4, float bias);" // GL_EXT_YUV_target + "vec4 texelFetch(__samplerExternal2DY2YEXT sampler, ivec2, int lod);" // GL_EXT_YUV_target + "\n"); + commonBuiltins.append( + "vec4 texture2DGradEXT(sampler2D, vec2, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec3, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec4, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 textureCubeGradEXT(samplerCube, vec3, vec3, vec3);" // GL_EXT_shader_texture_lod + + "float shadow2DEXT(sampler2DShadow, vec3);" // GL_EXT_shadow_samplers + "float shadow2DProjEXT(sampler2DShadow, vec4);" // GL_EXT_shadow_samplers + + "\n"); + } + } + + // + // Noise functions. + // + if (spvVersion.spv == 0 && profile != EEsProfile) { + commonBuiltins.append( + "float noise1(float x);" + "float noise1(vec2 x);" + "float noise1(vec3 x);" + "float noise1(vec4 x);" + + "vec2 noise2(float x);" + "vec2 noise2(vec2 x);" + "vec2 noise2(vec3 x);" + "vec2 noise2(vec4 x);" + + "vec3 noise3(float x);" + "vec3 noise3(vec2 x);" + "vec3 noise3(vec3 x);" + "vec3 noise3(vec4 x);" + + "vec4 noise4(float x);" + "vec4 noise4(vec2 x);" + "vec4 noise4(vec3 x);" + "vec4 noise4(vec4 x);" + + "\n"); + } + + if (spvVersion.vulkan == 0) { + // + // Atomic counter functions. + // + if ((profile != EEsProfile && version >= 300) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "uint atomicCounterIncrement(atomic_uint);" + "uint atomicCounterDecrement(atomic_uint);" + "uint atomicCounter(atomic_uint);" + + "\n"); + } + if (profile != EEsProfile && version == 450) { + commonBuiltins.append( + "uint atomicCounterAddARB(atomic_uint, uint);" + "uint atomicCounterSubtractARB(atomic_uint, uint);" + "uint atomicCounterMinARB(atomic_uint, uint);" + "uint atomicCounterMaxARB(atomic_uint, uint);" + "uint atomicCounterAndARB(atomic_uint, uint);" + "uint atomicCounterOrARB(atomic_uint, uint);" + "uint atomicCounterXorARB(atomic_uint, uint);" + "uint atomicCounterExchangeARB(atomic_uint, uint);" + "uint atomicCounterCompSwapARB(atomic_uint, uint, uint);" + + "\n"); + } + + + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "uint atomicCounterAdd(atomic_uint, uint);" + "uint atomicCounterSubtract(atomic_uint, uint);" + "uint atomicCounterMin(atomic_uint, uint);" + "uint atomicCounterMax(atomic_uint, uint);" + "uint atomicCounterAnd(atomic_uint, uint);" + "uint atomicCounterOr(atomic_uint, uint);" + "uint atomicCounterXor(atomic_uint, uint);" + "uint atomicCounterExchange(atomic_uint, uint);" + "uint atomicCounterCompSwap(atomic_uint, uint, uint);" + + "\n"); + } + } + else if (spvVersion.vulkanRelaxed) { + // + // Atomic counter functions act as aliases to normal atomic functions. + // replace definitions to take 'volatile coherent nontemporal uint' instead of 'atomic_uint' + // and map to equivalent non-counter atomic op + // + if ((profile != EEsProfile && version >= 300) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "uint atomicCounterIncrement(volatile coherent nontemporal uint);" + "uint atomicCounterDecrement(volatile coherent nontemporal uint);" + "uint atomicCounter(volatile coherent nontemporal uint);" + + "\n"); + } + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "uint atomicCounterAdd(volatile coherent nontemporal uint, uint);" + "uint atomicCounterSubtract(volatile coherent nontemporal uint, uint);" + "uint atomicCounterMin(volatile coherent nontemporal uint, uint);" + "uint atomicCounterMax(volatile coherent nontemporal uint, uint);" + "uint atomicCounterAnd(volatile coherent nontemporal uint, uint);" + "uint atomicCounterOr(volatile coherent nontemporal uint, uint);" + "uint atomicCounterXor(volatile coherent nontemporal uint, uint);" + "uint atomicCounterExchange(volatile coherent nontemporal uint, uint);" + "uint atomicCounterCompSwap(volatile coherent nontemporal uint, uint, uint);" + + "\n"); + } + } + + // Bitfield + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " int bitfieldExtract( int, int, int);" + "ivec2 bitfieldExtract(ivec2, int, int);" + "ivec3 bitfieldExtract(ivec3, int, int);" + "ivec4 bitfieldExtract(ivec4, int, int);" + + " uint bitfieldExtract( uint, int, int);" + "uvec2 bitfieldExtract(uvec2, int, int);" + "uvec3 bitfieldExtract(uvec3, int, int);" + "uvec4 bitfieldExtract(uvec4, int, int);" + + " int bitfieldInsert( int base, int, int, int);" + "ivec2 bitfieldInsert(ivec2 base, ivec2, int, int);" + "ivec3 bitfieldInsert(ivec3 base, ivec3, int, int);" + "ivec4 bitfieldInsert(ivec4 base, ivec4, int, int);" + + " uint bitfieldInsert( uint base, uint, int, int);" + "uvec2 bitfieldInsert(uvec2 base, uvec2, int, int);" + "uvec3 bitfieldInsert(uvec3 base, uvec3, int, int);" + "uvec4 bitfieldInsert(uvec4 base, uvec4, int, int);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int findLSB( int);" + "ivec2 findLSB(ivec2);" + "ivec3 findLSB(ivec3);" + "ivec4 findLSB(ivec4);" + + " int findLSB( uint);" + "ivec2 findLSB(uvec2);" + "ivec3 findLSB(uvec3);" + "ivec4 findLSB(uvec4);" + + "\n"); + } else if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int findLSB( int);" + "lowp ivec2 findLSB(ivec2);" + "lowp ivec3 findLSB(ivec3);" + "lowp ivec4 findLSB(ivec4);" + + "lowp int findLSB( uint);" + "lowp ivec2 findLSB(uvec2);" + "lowp ivec3 findLSB(uvec3);" + "lowp ivec4 findLSB(uvec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int bitCount( int);" + "ivec2 bitCount(ivec2);" + "ivec3 bitCount(ivec3);" + "ivec4 bitCount(ivec4);" + + " int bitCount( uint);" + "ivec2 bitCount(uvec2);" + "ivec3 bitCount(uvec3);" + "ivec4 bitCount(uvec4);" + + " int findMSB(highp int);" + "ivec2 findMSB(highp ivec2);" + "ivec3 findMSB(highp ivec3);" + "ivec4 findMSB(highp ivec4);" + + " int findMSB(highp uint);" + "ivec2 findMSB(highp uvec2);" + "ivec3 findMSB(highp uvec3);" + "ivec4 findMSB(highp uvec4);" + + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " uint uaddCarry(highp uint, highp uint, out lowp uint carry);" + "uvec2 uaddCarry(highp uvec2, highp uvec2, out lowp uvec2 carry);" + "uvec3 uaddCarry(highp uvec3, highp uvec3, out lowp uvec3 carry);" + "uvec4 uaddCarry(highp uvec4, highp uvec4, out lowp uvec4 carry);" + + " uint usubBorrow(highp uint, highp uint, out lowp uint borrow);" + "uvec2 usubBorrow(highp uvec2, highp uvec2, out lowp uvec2 borrow);" + "uvec3 usubBorrow(highp uvec3, highp uvec3, out lowp uvec3 borrow);" + "uvec4 usubBorrow(highp uvec4, highp uvec4, out lowp uvec4 borrow);" + + "void umulExtended(highp uint, highp uint, out highp uint, out highp uint lsb);" + "void umulExtended(highp uvec2, highp uvec2, out highp uvec2, out highp uvec2 lsb);" + "void umulExtended(highp uvec3, highp uvec3, out highp uvec3, out highp uvec3 lsb);" + "void umulExtended(highp uvec4, highp uvec4, out highp uvec4, out highp uvec4 lsb);" + + "void imulExtended(highp int, highp int, out highp int, out highp int lsb);" + "void imulExtended(highp ivec2, highp ivec2, out highp ivec2, out highp ivec2 lsb);" + "void imulExtended(highp ivec3, highp ivec3, out highp ivec3, out highp ivec3 lsb);" + "void imulExtended(highp ivec4, highp ivec4, out highp ivec4, out highp ivec4 lsb);" + + " int bitfieldReverse(highp int);" + "ivec2 bitfieldReverse(highp ivec2);" + "ivec3 bitfieldReverse(highp ivec3);" + "ivec4 bitfieldReverse(highp ivec4);" + + " uint bitfieldReverse(highp uint);" + "uvec2 bitfieldReverse(highp uvec2);" + "uvec3 bitfieldReverse(highp uvec3);" + "uvec4 bitfieldReverse(highp uvec4);" + + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int bitCount( int);" + "lowp ivec2 bitCount(ivec2);" + "lowp ivec3 bitCount(ivec3);" + "lowp ivec4 bitCount(ivec4);" + + "lowp int bitCount( uint);" + "lowp ivec2 bitCount(uvec2);" + "lowp ivec3 bitCount(uvec3);" + "lowp ivec4 bitCount(uvec4);" + + "lowp int findMSB(highp int);" + "lowp ivec2 findMSB(highp ivec2);" + "lowp ivec3 findMSB(highp ivec3);" + "lowp ivec4 findMSB(highp ivec4);" + + "lowp int findMSB(highp uint);" + "lowp ivec2 findMSB(highp uvec2);" + "lowp ivec3 findMSB(highp uvec3);" + "lowp ivec4 findMSB(highp uvec4);" + + "\n"); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint64_t ballotARB(bool);" + + "float readInvocationARB(float, uint);" + "vec2 readInvocationARB(vec2, uint);" + "vec3 readInvocationARB(vec3, uint);" + "vec4 readInvocationARB(vec4, uint);" + + "int readInvocationARB(int, uint);" + "ivec2 readInvocationARB(ivec2, uint);" + "ivec3 readInvocationARB(ivec3, uint);" + "ivec4 readInvocationARB(ivec4, uint);" + + "uint readInvocationARB(uint, uint);" + "uvec2 readInvocationARB(uvec2, uint);" + "uvec3 readInvocationARB(uvec3, uint);" + "uvec4 readInvocationARB(uvec4, uint);" + + "float readFirstInvocationARB(float);" + "vec2 readFirstInvocationARB(vec2);" + "vec3 readFirstInvocationARB(vec3);" + "vec4 readFirstInvocationARB(vec4);" + + "int readFirstInvocationARB(int);" + "ivec2 readFirstInvocationARB(ivec2);" + "ivec3 readFirstInvocationARB(ivec3);" + "ivec4 readFirstInvocationARB(ivec4);" + + "uint readFirstInvocationARB(uint);" + "uvec2 readFirstInvocationARB(uvec2);" + "uvec3 readFirstInvocationARB(uvec3);" + "uvec4 readFirstInvocationARB(uvec4);" + + "\n"); + } + + // GL_ARB_shader_group_vote + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "bool anyInvocationARB(bool);" + "bool allInvocationsARB(bool);" + "bool allInvocationsEqualARB(bool);" + + "\n"); + } + + // GL_EXT_integer_dot_product + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 450)) { + commonBuiltins.append( + + "uint dotEXT(uvec2 a, uvec2 b);" + "int dotEXT(ivec2 a, ivec2 b);" + "int dotEXT(ivec2 a, uvec2 b);" + "int dotEXT(uvec2 a, ivec2 b);" + + "uint dotEXT(uvec3 a, uvec3 b);" + "int dotEXT(ivec3 a, ivec3 b);" + "int dotEXT(ivec3 a, uvec3 b);" + "int dotEXT(uvec3 a, ivec3 b);" + + "uint dotEXT(uvec4 a, uvec4 b);" + "int dotEXT(ivec4 a, ivec4 b);" + "int dotEXT(ivec4 a, uvec4 b);" + "int dotEXT(uvec4 a, ivec4 b);" + + "uint dotPacked4x8EXT(uint a, uint b);" + "int dotPacked4x8EXT(int a, uint b);" + "int dotPacked4x8EXT(uint a, int b);" + "int dotPacked4x8EXT(int a, int b);" + + "uint dotEXT(u8vec2 a, u8vec2 b);" + "int dotEXT(i8vec2 a, u8vec2 b);" + "int dotEXT(u8vec2 a, i8vec2 b);" + "int dotEXT(i8vec2 a, i8vec2 b);" + + "uint dotEXT(u8vec3 a, u8vec3 b);" + "int dotEXT(i8vec3 a, u8vec3 b);" + "int dotEXT(u8vec3 a, i8vec3 b);" + "int dotEXT(i8vec3 a, i8vec3 b);" + + "uint dotEXT(u8vec4 a, u8vec4 b);" + "int dotEXT(i8vec4 a, u8vec4 b);" + "int dotEXT(u8vec4 a, i8vec4 b);" + "int dotEXT(i8vec4 a, i8vec4 b);" + + "uint dotEXT(u16vec2 a, u16vec2 b);" + "int dotEXT(i16vec2 a, u16vec2 b);" + "int dotEXT(u16vec2 a, i16vec2 b);" + "int dotEXT(i16vec2 a, i16vec2 b);" + + "uint dotEXT(u16vec3 a, u16vec3 b);" + "int dotEXT(i16vec3 a, u16vec3 b);" + "int dotEXT(u16vec3 a, i16vec3 b);" + "int dotEXT(i16vec3 a, i16vec3 b);" + + "uint dotEXT(u16vec4 a, u16vec4 b);" + "int dotEXT(i16vec4 a, u16vec4 b);" + "int dotEXT(u16vec4 a, i16vec4 b);" + "int dotEXT(i16vec4 a, i16vec4 b);" + + "uint64_t dotEXT(u64vec2 a, u64vec2 b);" + "int64_t dotEXT(i64vec2 a, u64vec2 b);" + "int64_t dotEXT(u64vec2 a, i64vec2 b);" + "int64_t dotEXT(i64vec2 a, i64vec2 b);" + + "uint64_t dotEXT(u64vec3 a, u64vec3 b);" + "int64_t dotEXT(i64vec3 a, u64vec3 b);" + "int64_t dotEXT(u64vec3 a, i64vec3 b);" + "int64_t dotEXT(i64vec3 a, i64vec3 b);" + + "uint64_t dotEXT(u64vec4 a, u64vec4 b);" + "int64_t dotEXT(i64vec4 a, u64vec4 b);" + "int64_t dotEXT(u64vec4 a, i64vec4 b);" + "int64_t dotEXT(i64vec4 a, i64vec4 b);" + + "uint dotAccSatEXT(uvec2 a, uvec2 b, uint c);" + "int dotAccSatEXT(ivec2 a, uvec2 b, int c);" + "int dotAccSatEXT(uvec2 a, ivec2 b, int c);" + "int dotAccSatEXT(ivec2 a, ivec2 b, int c);" + + "uint dotAccSatEXT(uvec3 a, uvec3 b, uint c);" + "int dotAccSatEXT(ivec3 a, uvec3 b, int c);" + "int dotAccSatEXT(uvec3 a, ivec3 b, int c);" + "int dotAccSatEXT(ivec3 a, ivec3 b, int c);" + + "uint dotAccSatEXT(uvec4 a, uvec4 b, uint c);" + "int dotAccSatEXT(ivec4 a, uvec4 b, int c);" + "int dotAccSatEXT(uvec4 a, ivec4 b, int c);" + "int dotAccSatEXT(ivec4 a, ivec4 b, int c);" + + "uint dotPacked4x8AccSatEXT(uint a, uint b, uint c);" + "int dotPacked4x8AccSatEXT(int a, uint b, int c);" + "int dotPacked4x8AccSatEXT(uint a, int b, int c);" + "int dotPacked4x8AccSatEXT(int a, int b, int c);" + + "uint dotAccSatEXT(u8vec2 a, u8vec2 b, uint c);" + "int dotAccSatEXT(i8vec2 a, u8vec2 b, int c);" + "int dotAccSatEXT(u8vec2 a, i8vec2 b, int c);" + "int dotAccSatEXT(i8vec2 a, i8vec2 b, int c);" + + "uint dotAccSatEXT(u8vec3 a, u8vec3 b, uint c);" + "int dotAccSatEXT(i8vec3 a, u8vec3 b, int c);" + "int dotAccSatEXT(u8vec3 a, i8vec3 b, int c);" + "int dotAccSatEXT(i8vec3 a, i8vec3 b, int c);" + + "uint dotAccSatEXT(u8vec4 a, u8vec4 b, uint c);" + "int dotAccSatEXT(i8vec4 a, u8vec4 b, int c);" + "int dotAccSatEXT(u8vec4 a, i8vec4 b, int c);" + "int dotAccSatEXT(i8vec4 a, i8vec4 b, int c);" + + "uint dotAccSatEXT(u16vec2 a, u16vec2 b, uint c);" + "int dotAccSatEXT(i16vec2 a, u16vec2 b, int c);" + "int dotAccSatEXT(u16vec2 a, i16vec2 b, int c);" + "int dotAccSatEXT(i16vec2 a, i16vec2 b, int c);" + + "uint dotAccSatEXT(u16vec3 a, u16vec3 b, uint c);" + "int dotAccSatEXT(i16vec3 a, u16vec3 b, int c);" + "int dotAccSatEXT(u16vec3 a, i16vec3 b, int c);" + "int dotAccSatEXT(i16vec3 a, i16vec3 b, int c);" + + "uint dotAccSatEXT(u16vec4 a, u16vec4 b, uint c);" + "int dotAccSatEXT(i16vec4 a, u16vec4 b, int c);" + "int dotAccSatEXT(u16vec4 a, i16vec4 b, int c);" + "int dotAccSatEXT(i16vec4 a, i16vec4 b, int c);" + + "uint64_t dotAccSatEXT(u64vec2 a, u64vec2 b, uint64_t c);" + "int64_t dotAccSatEXT(i64vec2 a, u64vec2 b, int64_t c);" + "int64_t dotAccSatEXT(u64vec2 a, i64vec2 b, int64_t c);" + "int64_t dotAccSatEXT(i64vec2 a, i64vec2 b, int64_t c);" + + "uint64_t dotAccSatEXT(u64vec3 a, u64vec3 b, uint64_t c);" + "int64_t dotAccSatEXT(i64vec3 a, u64vec3 b, int64_t c);" + "int64_t dotAccSatEXT(u64vec3 a, i64vec3 b, int64_t c);" + "int64_t dotAccSatEXT(i64vec3 a, i64vec3 b, int64_t c);" + + "uint64_t dotAccSatEXT(u64vec4 a, u64vec4 b, uint64_t c);" + "int64_t dotAccSatEXT(i64vec4 a, u64vec4 b, int64_t c);" + "int64_t dotAccSatEXT(u64vec4 a, i64vec4 b, int64_t c);" + "int64_t dotAccSatEXT(i64vec4 a, i64vec4 b, int64_t c);" + "\n"); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + commonBuiltins.append( + "void subgroupBarrier();" + "void subgroupMemoryBarrier();" + "void subgroupMemoryBarrierBuffer();" + "void subgroupMemoryBarrierImage();" + "bool subgroupElect();" + + "bool subgroupAll(bool);\n" + "bool subgroupAny(bool);\n" + "uvec4 subgroupBallot(bool);\n" + "bool subgroupInverseBallot(uvec4);\n" + "bool subgroupBallotBitExtract(uvec4, uint);\n" + "uint subgroupBallotBitCount(uvec4);\n" + "uint subgroupBallotInclusiveBitCount(uvec4);\n" + "uint subgroupBallotExclusiveBitCount(uvec4);\n" + "uint subgroupBallotFindLSB(uvec4);\n" + "uint subgroupBallotFindMSB(uvec4);\n" + ); + + // Generate all flavors of subgroup ops. + static const char *subgroupOps[] = + { + "bool subgroupAllEqual(%s);\n", + "%s subgroupBroadcast(%s, uint);\n", + "%s subgroupBroadcastFirst(%s);\n", + "%s subgroupShuffle(%s, uint);\n", + "%s subgroupShuffleXor(%s, uint);\n", + "%s subgroupShuffleUp(%s, uint delta);\n", + "%s subgroupShuffleDown(%s, uint delta);\n", + "%s subgroupRotate(%s, uint);\n", + "%s subgroupClusteredRotate(%s, uint, uint);\n", + "%s subgroupAdd(%s);\n", + "%s subgroupMul(%s);\n", + "%s subgroupMin(%s);\n", + "%s subgroupMax(%s);\n", + "%s subgroupAnd(%s);\n", + "%s subgroupOr(%s);\n", + "%s subgroupXor(%s);\n", + "%s subgroupInclusiveAdd(%s);\n", + "%s subgroupInclusiveMul(%s);\n", + "%s subgroupInclusiveMin(%s);\n", + "%s subgroupInclusiveMax(%s);\n", + "%s subgroupInclusiveAnd(%s);\n", + "%s subgroupInclusiveOr(%s);\n", + "%s subgroupInclusiveXor(%s);\n", + "%s subgroupExclusiveAdd(%s);\n", + "%s subgroupExclusiveMul(%s);\n", + "%s subgroupExclusiveMin(%s);\n", + "%s subgroupExclusiveMax(%s);\n", + "%s subgroupExclusiveAnd(%s);\n", + "%s subgroupExclusiveOr(%s);\n", + "%s subgroupExclusiveXor(%s);\n", + "%s subgroupClusteredAdd(%s, uint);\n", + "%s subgroupClusteredMul(%s, uint);\n", + "%s subgroupClusteredMin(%s, uint);\n", + "%s subgroupClusteredMax(%s, uint);\n", + "%s subgroupClusteredAnd(%s, uint);\n", + "%s subgroupClusteredOr(%s, uint);\n", + "%s subgroupClusteredXor(%s, uint);\n", + "%s subgroupQuadBroadcast(%s, uint);\n", + "%s subgroupQuadSwapHorizontal(%s);\n", + "%s subgroupQuadSwapVertical(%s);\n", + "%s subgroupQuadSwapDiagonal(%s);\n", + "uvec4 subgroupPartitionNV(%s);\n", + "%s subgroupPartitionedAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveXorNV(%s, uvec4 ballot);\n", + }; + + static const char *floatTypes[] = { + "float", "vec2", "vec3", "vec4", + "float16_t", "f16vec2", "f16vec3", "f16vec4", + }; + static const char *doubleTypes[] = { + "double", "dvec2", "dvec3", "dvec4", + }; + static const char *intTypes[] = { + "int8_t", "i8vec2", "i8vec3", "i8vec4", + "int16_t", "i16vec2", "i16vec3", "i16vec4", + "int", "ivec2", "ivec3", "ivec4", + "int64_t", "i64vec2", "i64vec3", "i64vec4", + "uint8_t", "u8vec2", "u8vec3", "u8vec4", + "uint16_t", "u16vec2", "u16vec3", "u16vec4", + "uint", "uvec2", "uvec3", "uvec4", + "uint64_t", "u64vec2", "u64vec3", "u64vec4", + }; + static const char *boolTypes[] = { + "bool", "bvec2", "bvec3", "bvec4", + }; + + for (size_t i = 0; i < sizeof(subgroupOps)/sizeof(subgroupOps[0]); ++i) { + const char *op = subgroupOps[i]; + + // Logical operations don't support float + bool logicalOp = strstr(op, "Or") || strstr(op, "And") || + (strstr(op, "Xor") && !strstr(op, "ShuffleXor")); + // Math operations don't support bool + bool mathOp = strstr(op, "Add") || strstr(op, "Mul") || strstr(op, "Min") || strstr(op, "Max"); + + const int bufSize = 256; + char buf[bufSize]; + + if (!logicalOp) { + for (size_t j = 0; j < sizeof(floatTypes)/sizeof(floatTypes[0]); ++j) { + snprintf(buf, bufSize, op, floatTypes[j], floatTypes[j]); + commonBuiltins.append(buf); + } + if (profile != EEsProfile && version >= 400) { + for (size_t j = 0; j < sizeof(doubleTypes)/sizeof(doubleTypes[0]); ++j) { + snprintf(buf, bufSize, op, doubleTypes[j], doubleTypes[j]); + commonBuiltins.append(buf); + } + } + } + if (!mathOp) { + for (size_t j = 0; j < sizeof(boolTypes)/sizeof(boolTypes[0]); ++j) { + snprintf(buf, bufSize, op, boolTypes[j], boolTypes[j]); + commonBuiltins.append(buf); + } + } + for (size_t j = 0; j < sizeof(intTypes)/sizeof(intTypes[0]); ++j) { + snprintf(buf, bufSize, op, intTypes[j], intTypes[j]); + commonBuiltins.append(buf); + } + } + + stageBuiltins[EShLangCompute].append( + "void subgroupMemoryBarrierShared();" + + "\n" + ); + stageBuiltins[EShLangMesh].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + stageBuiltins[EShLangTask].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + } + + // GL_EXT_shader_quad_control + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + commonBuiltins.append( + "bool subgroupQuadAll(bool);\n" + "bool subgroupQuadAny(bool);\n" + ); + } + + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "bool anyInvocation(bool);" + "bool allInvocations(bool);" + "bool allInvocationsEqual(bool);" + + "\n"); + } + + // GL_AMD_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "float minInvocationsAMD(float);" + "vec2 minInvocationsAMD(vec2);" + "vec3 minInvocationsAMD(vec3);" + "vec4 minInvocationsAMD(vec4);" + + "int minInvocationsAMD(int);" + "ivec2 minInvocationsAMD(ivec2);" + "ivec3 minInvocationsAMD(ivec3);" + "ivec4 minInvocationsAMD(ivec4);" + + "uint minInvocationsAMD(uint);" + "uvec2 minInvocationsAMD(uvec2);" + "uvec3 minInvocationsAMD(uvec3);" + "uvec4 minInvocationsAMD(uvec4);" + + "double minInvocationsAMD(double);" + "dvec2 minInvocationsAMD(dvec2);" + "dvec3 minInvocationsAMD(dvec3);" + "dvec4 minInvocationsAMD(dvec4);" + + "int64_t minInvocationsAMD(int64_t);" + "i64vec2 minInvocationsAMD(i64vec2);" + "i64vec3 minInvocationsAMD(i64vec3);" + "i64vec4 minInvocationsAMD(i64vec4);" + + "uint64_t minInvocationsAMD(uint64_t);" + "u64vec2 minInvocationsAMD(u64vec2);" + "u64vec3 minInvocationsAMD(u64vec3);" + "u64vec4 minInvocationsAMD(u64vec4);" + + "float16_t minInvocationsAMD(float16_t);" + "f16vec2 minInvocationsAMD(f16vec2);" + "f16vec3 minInvocationsAMD(f16vec3);" + "f16vec4 minInvocationsAMD(f16vec4);" + + "int16_t minInvocationsAMD(int16_t);" + "i16vec2 minInvocationsAMD(i16vec2);" + "i16vec3 minInvocationsAMD(i16vec3);" + "i16vec4 minInvocationsAMD(i16vec4);" + + "uint16_t minInvocationsAMD(uint16_t);" + "u16vec2 minInvocationsAMD(u16vec2);" + "u16vec3 minInvocationsAMD(u16vec3);" + "u16vec4 minInvocationsAMD(u16vec4);" + + "float minInvocationsInclusiveScanAMD(float);" + "vec2 minInvocationsInclusiveScanAMD(vec2);" + "vec3 minInvocationsInclusiveScanAMD(vec3);" + "vec4 minInvocationsInclusiveScanAMD(vec4);" + + "int minInvocationsInclusiveScanAMD(int);" + "ivec2 minInvocationsInclusiveScanAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanAMD(ivec4);" + + "uint minInvocationsInclusiveScanAMD(uint);" + "uvec2 minInvocationsInclusiveScanAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanAMD(uvec4);" + + "double minInvocationsInclusiveScanAMD(double);" + "dvec2 minInvocationsInclusiveScanAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanAMD(u16vec4);" + + "float minInvocationsExclusiveScanAMD(float);" + "vec2 minInvocationsExclusiveScanAMD(vec2);" + "vec3 minInvocationsExclusiveScanAMD(vec3);" + "vec4 minInvocationsExclusiveScanAMD(vec4);" + + "int minInvocationsExclusiveScanAMD(int);" + "ivec2 minInvocationsExclusiveScanAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanAMD(ivec4);" + + "uint minInvocationsExclusiveScanAMD(uint);" + "uvec2 minInvocationsExclusiveScanAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanAMD(uvec4);" + + "double minInvocationsExclusiveScanAMD(double);" + "dvec2 minInvocationsExclusiveScanAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanAMD(u16vec4);" + + "float maxInvocationsAMD(float);" + "vec2 maxInvocationsAMD(vec2);" + "vec3 maxInvocationsAMD(vec3);" + "vec4 maxInvocationsAMD(vec4);" + + "int maxInvocationsAMD(int);" + "ivec2 maxInvocationsAMD(ivec2);" + "ivec3 maxInvocationsAMD(ivec3);" + "ivec4 maxInvocationsAMD(ivec4);" + + "uint maxInvocationsAMD(uint);" + "uvec2 maxInvocationsAMD(uvec2);" + "uvec3 maxInvocationsAMD(uvec3);" + "uvec4 maxInvocationsAMD(uvec4);" + + "double maxInvocationsAMD(double);" + "dvec2 maxInvocationsAMD(dvec2);" + "dvec3 maxInvocationsAMD(dvec3);" + "dvec4 maxInvocationsAMD(dvec4);" + + "int64_t maxInvocationsAMD(int64_t);" + "i64vec2 maxInvocationsAMD(i64vec2);" + "i64vec3 maxInvocationsAMD(i64vec3);" + "i64vec4 maxInvocationsAMD(i64vec4);" + + "uint64_t maxInvocationsAMD(uint64_t);" + "u64vec2 maxInvocationsAMD(u64vec2);" + "u64vec3 maxInvocationsAMD(u64vec3);" + "u64vec4 maxInvocationsAMD(u64vec4);" + + "float16_t maxInvocationsAMD(float16_t);" + "f16vec2 maxInvocationsAMD(f16vec2);" + "f16vec3 maxInvocationsAMD(f16vec3);" + "f16vec4 maxInvocationsAMD(f16vec4);" + + "int16_t maxInvocationsAMD(int16_t);" + "i16vec2 maxInvocationsAMD(i16vec2);" + "i16vec3 maxInvocationsAMD(i16vec3);" + "i16vec4 maxInvocationsAMD(i16vec4);" + + "uint16_t maxInvocationsAMD(uint16_t);" + "u16vec2 maxInvocationsAMD(u16vec2);" + "u16vec3 maxInvocationsAMD(u16vec3);" + "u16vec4 maxInvocationsAMD(u16vec4);" + + "float maxInvocationsInclusiveScanAMD(float);" + "vec2 maxInvocationsInclusiveScanAMD(vec2);" + "vec3 maxInvocationsInclusiveScanAMD(vec3);" + "vec4 maxInvocationsInclusiveScanAMD(vec4);" + + "int maxInvocationsInclusiveScanAMD(int);" + "ivec2 maxInvocationsInclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanAMD(ivec4);" + + "uint maxInvocationsInclusiveScanAMD(uint);" + "uvec2 maxInvocationsInclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanAMD(uvec4);" + + "double maxInvocationsInclusiveScanAMD(double);" + "dvec2 maxInvocationsInclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanAMD(u16vec4);" + + "float maxInvocationsExclusiveScanAMD(float);" + "vec2 maxInvocationsExclusiveScanAMD(vec2);" + "vec3 maxInvocationsExclusiveScanAMD(vec3);" + "vec4 maxInvocationsExclusiveScanAMD(vec4);" + + "int maxInvocationsExclusiveScanAMD(int);" + "ivec2 maxInvocationsExclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanAMD(ivec4);" + + "uint maxInvocationsExclusiveScanAMD(uint);" + "uvec2 maxInvocationsExclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanAMD(uvec4);" + + "double maxInvocationsExclusiveScanAMD(double);" + "dvec2 maxInvocationsExclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanAMD(u16vec4);" + + "float addInvocationsAMD(float);" + "vec2 addInvocationsAMD(vec2);" + "vec3 addInvocationsAMD(vec3);" + "vec4 addInvocationsAMD(vec4);" + + "int addInvocationsAMD(int);" + "ivec2 addInvocationsAMD(ivec2);" + "ivec3 addInvocationsAMD(ivec3);" + "ivec4 addInvocationsAMD(ivec4);" + + "uint addInvocationsAMD(uint);" + "uvec2 addInvocationsAMD(uvec2);" + "uvec3 addInvocationsAMD(uvec3);" + "uvec4 addInvocationsAMD(uvec4);" + + "double addInvocationsAMD(double);" + "dvec2 addInvocationsAMD(dvec2);" + "dvec3 addInvocationsAMD(dvec3);" + "dvec4 addInvocationsAMD(dvec4);" + + "int64_t addInvocationsAMD(int64_t);" + "i64vec2 addInvocationsAMD(i64vec2);" + "i64vec3 addInvocationsAMD(i64vec3);" + "i64vec4 addInvocationsAMD(i64vec4);" + + "uint64_t addInvocationsAMD(uint64_t);" + "u64vec2 addInvocationsAMD(u64vec2);" + "u64vec3 addInvocationsAMD(u64vec3);" + "u64vec4 addInvocationsAMD(u64vec4);" + + "float16_t addInvocationsAMD(float16_t);" + "f16vec2 addInvocationsAMD(f16vec2);" + "f16vec3 addInvocationsAMD(f16vec3);" + "f16vec4 addInvocationsAMD(f16vec4);" + + "int16_t addInvocationsAMD(int16_t);" + "i16vec2 addInvocationsAMD(i16vec2);" + "i16vec3 addInvocationsAMD(i16vec3);" + "i16vec4 addInvocationsAMD(i16vec4);" + + "uint16_t addInvocationsAMD(uint16_t);" + "u16vec2 addInvocationsAMD(u16vec2);" + "u16vec3 addInvocationsAMD(u16vec3);" + "u16vec4 addInvocationsAMD(u16vec4);" + + "float addInvocationsInclusiveScanAMD(float);" + "vec2 addInvocationsInclusiveScanAMD(vec2);" + "vec3 addInvocationsInclusiveScanAMD(vec3);" + "vec4 addInvocationsInclusiveScanAMD(vec4);" + + "int addInvocationsInclusiveScanAMD(int);" + "ivec2 addInvocationsInclusiveScanAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanAMD(ivec4);" + + "uint addInvocationsInclusiveScanAMD(uint);" + "uvec2 addInvocationsInclusiveScanAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanAMD(uvec4);" + + "double addInvocationsInclusiveScanAMD(double);" + "dvec2 addInvocationsInclusiveScanAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanAMD(u16vec4);" + + "float addInvocationsExclusiveScanAMD(float);" + "vec2 addInvocationsExclusiveScanAMD(vec2);" + "vec3 addInvocationsExclusiveScanAMD(vec3);" + "vec4 addInvocationsExclusiveScanAMD(vec4);" + + "int addInvocationsExclusiveScanAMD(int);" + "ivec2 addInvocationsExclusiveScanAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanAMD(ivec4);" + + "uint addInvocationsExclusiveScanAMD(uint);" + "uvec2 addInvocationsExclusiveScanAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanAMD(uvec4);" + + "double addInvocationsExclusiveScanAMD(double);" + "dvec2 addInvocationsExclusiveScanAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanAMD(u16vec4);" + + "float minInvocationsNonUniformAMD(float);" + "vec2 minInvocationsNonUniformAMD(vec2);" + "vec3 minInvocationsNonUniformAMD(vec3);" + "vec4 minInvocationsNonUniformAMD(vec4);" + + "int minInvocationsNonUniformAMD(int);" + "ivec2 minInvocationsNonUniformAMD(ivec2);" + "ivec3 minInvocationsNonUniformAMD(ivec3);" + "ivec4 minInvocationsNonUniformAMD(ivec4);" + + "uint minInvocationsNonUniformAMD(uint);" + "uvec2 minInvocationsNonUniformAMD(uvec2);" + "uvec3 minInvocationsNonUniformAMD(uvec3);" + "uvec4 minInvocationsNonUniformAMD(uvec4);" + + "double minInvocationsNonUniformAMD(double);" + "dvec2 minInvocationsNonUniformAMD(dvec2);" + "dvec3 minInvocationsNonUniformAMD(dvec3);" + "dvec4 minInvocationsNonUniformAMD(dvec4);" + + "int64_t minInvocationsNonUniformAMD(int64_t);" + "i64vec2 minInvocationsNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsNonUniformAMD(u64vec4);" + + "float16_t minInvocationsNonUniformAMD(float16_t);" + "f16vec2 minInvocationsNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsNonUniformAMD(f16vec4);" + + "int16_t minInvocationsNonUniformAMD(int16_t);" + "i16vec2 minInvocationsNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsNonUniformAMD(u16vec4);" + + "float minInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float minInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsNonUniformAMD(float);" + "vec2 maxInvocationsNonUniformAMD(vec2);" + "vec3 maxInvocationsNonUniformAMD(vec3);" + "vec4 maxInvocationsNonUniformAMD(vec4);" + + "int maxInvocationsNonUniformAMD(int);" + "ivec2 maxInvocationsNonUniformAMD(ivec2);" + "ivec3 maxInvocationsNonUniformAMD(ivec3);" + "ivec4 maxInvocationsNonUniformAMD(ivec4);" + + "uint maxInvocationsNonUniformAMD(uint);" + "uvec2 maxInvocationsNonUniformAMD(uvec2);" + "uvec3 maxInvocationsNonUniformAMD(uvec3);" + "uvec4 maxInvocationsNonUniformAMD(uvec4);" + + "double maxInvocationsNonUniformAMD(double);" + "dvec2 maxInvocationsNonUniformAMD(dvec2);" + "dvec3 maxInvocationsNonUniformAMD(dvec3);" + "dvec4 maxInvocationsNonUniformAMD(dvec4);" + + "int64_t maxInvocationsNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsNonUniformAMD(u16vec4);" + + "float maxInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsNonUniformAMD(float);" + "vec2 addInvocationsNonUniformAMD(vec2);" + "vec3 addInvocationsNonUniformAMD(vec3);" + "vec4 addInvocationsNonUniformAMD(vec4);" + + "int addInvocationsNonUniformAMD(int);" + "ivec2 addInvocationsNonUniformAMD(ivec2);" + "ivec3 addInvocationsNonUniformAMD(ivec3);" + "ivec4 addInvocationsNonUniformAMD(ivec4);" + + "uint addInvocationsNonUniformAMD(uint);" + "uvec2 addInvocationsNonUniformAMD(uvec2);" + "uvec3 addInvocationsNonUniformAMD(uvec3);" + "uvec4 addInvocationsNonUniformAMD(uvec4);" + + "double addInvocationsNonUniformAMD(double);" + "dvec2 addInvocationsNonUniformAMD(dvec2);" + "dvec3 addInvocationsNonUniformAMD(dvec3);" + "dvec4 addInvocationsNonUniformAMD(dvec4);" + + "int64_t addInvocationsNonUniformAMD(int64_t);" + "i64vec2 addInvocationsNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsNonUniformAMD(u64vec4);" + + "float16_t addInvocationsNonUniformAMD(float16_t);" + "f16vec2 addInvocationsNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsNonUniformAMD(f16vec4);" + + "int16_t addInvocationsNonUniformAMD(int16_t);" + "i16vec2 addInvocationsNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsNonUniformAMD(u16vec4);" + + "float addInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float swizzleInvocationsAMD(float, uvec4);" + "vec2 swizzleInvocationsAMD(vec2, uvec4);" + "vec3 swizzleInvocationsAMD(vec3, uvec4);" + "vec4 swizzleInvocationsAMD(vec4, uvec4);" + + "int swizzleInvocationsAMD(int, uvec4);" + "ivec2 swizzleInvocationsAMD(ivec2, uvec4);" + "ivec3 swizzleInvocationsAMD(ivec3, uvec4);" + "ivec4 swizzleInvocationsAMD(ivec4, uvec4);" + + "uint swizzleInvocationsAMD(uint, uvec4);" + "uvec2 swizzleInvocationsAMD(uvec2, uvec4);" + "uvec3 swizzleInvocationsAMD(uvec3, uvec4);" + "uvec4 swizzleInvocationsAMD(uvec4, uvec4);" + + "float swizzleInvocationsMaskedAMD(float, uvec3);" + "vec2 swizzleInvocationsMaskedAMD(vec2, uvec3);" + "vec3 swizzleInvocationsMaskedAMD(vec3, uvec3);" + "vec4 swizzleInvocationsMaskedAMD(vec4, uvec3);" + + "int swizzleInvocationsMaskedAMD(int, uvec3);" + "ivec2 swizzleInvocationsMaskedAMD(ivec2, uvec3);" + "ivec3 swizzleInvocationsMaskedAMD(ivec3, uvec3);" + "ivec4 swizzleInvocationsMaskedAMD(ivec4, uvec3);" + + "uint swizzleInvocationsMaskedAMD(uint, uvec3);" + "uvec2 swizzleInvocationsMaskedAMD(uvec2, uvec3);" + "uvec3 swizzleInvocationsMaskedAMD(uvec3, uvec3);" + "uvec4 swizzleInvocationsMaskedAMD(uvec4, uvec3);" + + "float writeInvocationAMD(float, float, uint);" + "vec2 writeInvocationAMD(vec2, vec2, uint);" + "vec3 writeInvocationAMD(vec3, vec3, uint);" + "vec4 writeInvocationAMD(vec4, vec4, uint);" + + "int writeInvocationAMD(int, int, uint);" + "ivec2 writeInvocationAMD(ivec2, ivec2, uint);" + "ivec3 writeInvocationAMD(ivec3, ivec3, uint);" + "ivec4 writeInvocationAMD(ivec4, ivec4, uint);" + + "uint writeInvocationAMD(uint, uint, uint);" + "uvec2 writeInvocationAMD(uvec2, uvec2, uint);" + "uvec3 writeInvocationAMD(uvec3, uvec3, uint);" + "uvec4 writeInvocationAMD(uvec4, uvec4, uint);" + + "uint mbcntAMD(uint64_t);" + + "\n"); + } + + // GL_AMD_gcn_shader + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "float cubeFaceIndexAMD(vec3);" + "vec2 cubeFaceCoordAMD(vec3);" + "uint64_t timeAMD();" + + "in int gl_SIMDGroupSizeAMD;" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint fragmentMaskFetchAMD(sampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(isampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(usampler2DMS, ivec2);" + + "uint fragmentMaskFetchAMD(sampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(isampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(usampler2DMSArray, ivec3);" + + "vec4 fragmentFetchAMD(sampler2DMS, ivec2, uint);" + "ivec4 fragmentFetchAMD(isampler2DMS, ivec2, uint);" + "uvec4 fragmentFetchAMD(usampler2DMS, ivec2, uint);" + + "vec4 fragmentFetchAMD(sampler2DMSArray, ivec3, uint);" + "ivec4 fragmentFetchAMD(isampler2DMSArray, ivec3, uint);" + "uvec4 fragmentFetchAMD(usampler2DMSArray, ivec3, uint);" + + "\n"); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 300)) { + commonBuiltins.append( + "uint countLeadingZeros(uint);" + "uvec2 countLeadingZeros(uvec2);" + "uvec3 countLeadingZeros(uvec3);" + "uvec4 countLeadingZeros(uvec4);" + + "uint countTrailingZeros(uint);" + "uvec2 countTrailingZeros(uvec2);" + "uvec3 countTrailingZeros(uvec3);" + "uvec4 countTrailingZeros(uvec4);" + + "uint absoluteDifference(int, int);" + "uvec2 absoluteDifference(ivec2, ivec2);" + "uvec3 absoluteDifference(ivec3, ivec3);" + "uvec4 absoluteDifference(ivec4, ivec4);" + + "uint16_t absoluteDifference(int16_t, int16_t);" + "u16vec2 absoluteDifference(i16vec2, i16vec2);" + "u16vec3 absoluteDifference(i16vec3, i16vec3);" + "u16vec4 absoluteDifference(i16vec4, i16vec4);" + + "uint64_t absoluteDifference(int64_t, int64_t);" + "u64vec2 absoluteDifference(i64vec2, i64vec2);" + "u64vec3 absoluteDifference(i64vec3, i64vec3);" + "u64vec4 absoluteDifference(i64vec4, i64vec4);" + + "uint absoluteDifference(uint, uint);" + "uvec2 absoluteDifference(uvec2, uvec2);" + "uvec3 absoluteDifference(uvec3, uvec3);" + "uvec4 absoluteDifference(uvec4, uvec4);" + + "uint16_t absoluteDifference(uint16_t, uint16_t);" + "u16vec2 absoluteDifference(u16vec2, u16vec2);" + "u16vec3 absoluteDifference(u16vec3, u16vec3);" + "u16vec4 absoluteDifference(u16vec4, u16vec4);" + + "uint64_t absoluteDifference(uint64_t, uint64_t);" + "u64vec2 absoluteDifference(u64vec2, u64vec2);" + "u64vec3 absoluteDifference(u64vec3, u64vec3);" + "u64vec4 absoluteDifference(u64vec4, u64vec4);" + + "int addSaturate(int, int);" + "ivec2 addSaturate(ivec2, ivec2);" + "ivec3 addSaturate(ivec3, ivec3);" + "ivec4 addSaturate(ivec4, ivec4);" + + "int16_t addSaturate(int16_t, int16_t);" + "i16vec2 addSaturate(i16vec2, i16vec2);" + "i16vec3 addSaturate(i16vec3, i16vec3);" + "i16vec4 addSaturate(i16vec4, i16vec4);" + + "int64_t addSaturate(int64_t, int64_t);" + "i64vec2 addSaturate(i64vec2, i64vec2);" + "i64vec3 addSaturate(i64vec3, i64vec3);" + "i64vec4 addSaturate(i64vec4, i64vec4);" + + "uint addSaturate(uint, uint);" + "uvec2 addSaturate(uvec2, uvec2);" + "uvec3 addSaturate(uvec3, uvec3);" + "uvec4 addSaturate(uvec4, uvec4);" + + "uint16_t addSaturate(uint16_t, uint16_t);" + "u16vec2 addSaturate(u16vec2, u16vec2);" + "u16vec3 addSaturate(u16vec3, u16vec3);" + "u16vec4 addSaturate(u16vec4, u16vec4);" + + "uint64_t addSaturate(uint64_t, uint64_t);" + "u64vec2 addSaturate(u64vec2, u64vec2);" + "u64vec3 addSaturate(u64vec3, u64vec3);" + "u64vec4 addSaturate(u64vec4, u64vec4);" + + "int subtractSaturate(int, int);" + "ivec2 subtractSaturate(ivec2, ivec2);" + "ivec3 subtractSaturate(ivec3, ivec3);" + "ivec4 subtractSaturate(ivec4, ivec4);" + + "int16_t subtractSaturate(int16_t, int16_t);" + "i16vec2 subtractSaturate(i16vec2, i16vec2);" + "i16vec3 subtractSaturate(i16vec3, i16vec3);" + "i16vec4 subtractSaturate(i16vec4, i16vec4);" + + "int64_t subtractSaturate(int64_t, int64_t);" + "i64vec2 subtractSaturate(i64vec2, i64vec2);" + "i64vec3 subtractSaturate(i64vec3, i64vec3);" + "i64vec4 subtractSaturate(i64vec4, i64vec4);" + + "uint subtractSaturate(uint, uint);" + "uvec2 subtractSaturate(uvec2, uvec2);" + "uvec3 subtractSaturate(uvec3, uvec3);" + "uvec4 subtractSaturate(uvec4, uvec4);" + + "uint16_t subtractSaturate(uint16_t, uint16_t);" + "u16vec2 subtractSaturate(u16vec2, u16vec2);" + "u16vec3 subtractSaturate(u16vec3, u16vec3);" + "u16vec4 subtractSaturate(u16vec4, u16vec4);" + + "uint64_t subtractSaturate(uint64_t, uint64_t);" + "u64vec2 subtractSaturate(u64vec2, u64vec2);" + "u64vec3 subtractSaturate(u64vec3, u64vec3);" + "u64vec4 subtractSaturate(u64vec4, u64vec4);" + + "int average(int, int);" + "ivec2 average(ivec2, ivec2);" + "ivec3 average(ivec3, ivec3);" + "ivec4 average(ivec4, ivec4);" + + "int16_t average(int16_t, int16_t);" + "i16vec2 average(i16vec2, i16vec2);" + "i16vec3 average(i16vec3, i16vec3);" + "i16vec4 average(i16vec4, i16vec4);" + + "int64_t average(int64_t, int64_t);" + "i64vec2 average(i64vec2, i64vec2);" + "i64vec3 average(i64vec3, i64vec3);" + "i64vec4 average(i64vec4, i64vec4);" + + "uint average(uint, uint);" + "uvec2 average(uvec2, uvec2);" + "uvec3 average(uvec3, uvec3);" + "uvec4 average(uvec4, uvec4);" + + "uint16_t average(uint16_t, uint16_t);" + "u16vec2 average(u16vec2, u16vec2);" + "u16vec3 average(u16vec3, u16vec3);" + "u16vec4 average(u16vec4, u16vec4);" + + "uint64_t average(uint64_t, uint64_t);" + "u64vec2 average(u64vec2, u64vec2);" + "u64vec3 average(u64vec3, u64vec3);" + "u64vec4 average(u64vec4, u64vec4);" + + "int averageRounded(int, int);" + "ivec2 averageRounded(ivec2, ivec2);" + "ivec3 averageRounded(ivec3, ivec3);" + "ivec4 averageRounded(ivec4, ivec4);" + + "int16_t averageRounded(int16_t, int16_t);" + "i16vec2 averageRounded(i16vec2, i16vec2);" + "i16vec3 averageRounded(i16vec3, i16vec3);" + "i16vec4 averageRounded(i16vec4, i16vec4);" + + "int64_t averageRounded(int64_t, int64_t);" + "i64vec2 averageRounded(i64vec2, i64vec2);" + "i64vec3 averageRounded(i64vec3, i64vec3);" + "i64vec4 averageRounded(i64vec4, i64vec4);" + + "uint averageRounded(uint, uint);" + "uvec2 averageRounded(uvec2, uvec2);" + "uvec3 averageRounded(uvec3, uvec3);" + "uvec4 averageRounded(uvec4, uvec4);" + + "uint16_t averageRounded(uint16_t, uint16_t);" + "u16vec2 averageRounded(u16vec2, u16vec2);" + "u16vec3 averageRounded(u16vec3, u16vec3);" + "u16vec4 averageRounded(u16vec4, u16vec4);" + + "uint64_t averageRounded(uint64_t, uint64_t);" + "u64vec2 averageRounded(u64vec2, u64vec2);" + "u64vec3 averageRounded(u64vec3, u64vec3);" + "u64vec4 averageRounded(u64vec4, u64vec4);" + + "int multiply32x16(int, int);" + "ivec2 multiply32x16(ivec2, ivec2);" + "ivec3 multiply32x16(ivec3, ivec3);" + "ivec4 multiply32x16(ivec4, ivec4);" + + "uint multiply32x16(uint, uint);" + "uvec2 multiply32x16(uvec2, uvec2);" + "uvec3 multiply32x16(uvec3, uvec3);" + "uvec4 multiply32x16(uvec4, uvec4);" + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + commonBuiltins.append( + "struct gl_TextureFootprint2DNV {" + "uvec2 anchor;" + "uvec2 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + + "struct gl_TextureFootprint3DNV {" + "uvec3 anchor;" + "uvec3 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintLodNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintLodNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintGradNV(sampler2D, vec2, vec2, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintGradClampNV(sampler2D, vec2, vec2, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300 && version < 310) || + (profile != EEsProfile && version >= 150 && version < 450)) { // GL_EXT_shader_integer_mix + commonBuiltins.append("int mix(int, int, bool);" + "ivec2 mix(ivec2, ivec2, bvec2);" + "ivec3 mix(ivec3, ivec3, bvec3);" + "ivec4 mix(ivec4, ivec4, bvec4);" + "uint mix(uint, uint, bool );" + "uvec2 mix(uvec2, uvec2, bvec2);" + "uvec3 mix(uvec3, uvec3, bvec3);" + "uvec4 mix(uvec4, uvec4, bvec4);" + "bool mix(bool, bool, bool );" + "bvec2 mix(bvec2, bvec2, bvec2);" + "bvec3 mix(bvec3, bvec3, bvec3);" + "bvec4 mix(bvec4, bvec4, bvec4);" + + "\n"); + } + + // GL_AMD_gpu_shader_half_float/Explicit types + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "float16_t radians(float16_t);" + "f16vec2 radians(f16vec2);" + "f16vec3 radians(f16vec3);" + "f16vec4 radians(f16vec4);" + + "float16_t degrees(float16_t);" + "f16vec2 degrees(f16vec2);" + "f16vec3 degrees(f16vec3);" + "f16vec4 degrees(f16vec4);" + + "float16_t sin(float16_t);" + "f16vec2 sin(f16vec2);" + "f16vec3 sin(f16vec3);" + "f16vec4 sin(f16vec4);" + + "float16_t cos(float16_t);" + "f16vec2 cos(f16vec2);" + "f16vec3 cos(f16vec3);" + "f16vec4 cos(f16vec4);" + + "float16_t tan(float16_t);" + "f16vec2 tan(f16vec2);" + "f16vec3 tan(f16vec3);" + "f16vec4 tan(f16vec4);" + + "float16_t asin(float16_t);" + "f16vec2 asin(f16vec2);" + "f16vec3 asin(f16vec3);" + "f16vec4 asin(f16vec4);" + + "float16_t acos(float16_t);" + "f16vec2 acos(f16vec2);" + "f16vec3 acos(f16vec3);" + "f16vec4 acos(f16vec4);" + + "float16_t atan(float16_t, float16_t);" + "f16vec2 atan(f16vec2, f16vec2);" + "f16vec3 atan(f16vec3, f16vec3);" + "f16vec4 atan(f16vec4, f16vec4);" + + "float16_t atan(float16_t);" + "f16vec2 atan(f16vec2);" + "f16vec3 atan(f16vec3);" + "f16vec4 atan(f16vec4);" + + "float16_t sinh(float16_t);" + "f16vec2 sinh(f16vec2);" + "f16vec3 sinh(f16vec3);" + "f16vec4 sinh(f16vec4);" + + "float16_t cosh(float16_t);" + "f16vec2 cosh(f16vec2);" + "f16vec3 cosh(f16vec3);" + "f16vec4 cosh(f16vec4);" + + "float16_t tanh(float16_t);" + "f16vec2 tanh(f16vec2);" + "f16vec3 tanh(f16vec3);" + "f16vec4 tanh(f16vec4);" + + "float16_t asinh(float16_t);" + "f16vec2 asinh(f16vec2);" + "f16vec3 asinh(f16vec3);" + "f16vec4 asinh(f16vec4);" + + "float16_t acosh(float16_t);" + "f16vec2 acosh(f16vec2);" + "f16vec3 acosh(f16vec3);" + "f16vec4 acosh(f16vec4);" + + "float16_t atanh(float16_t);" + "f16vec2 atanh(f16vec2);" + "f16vec3 atanh(f16vec3);" + "f16vec4 atanh(f16vec4);" + + "float16_t pow(float16_t, float16_t);" + "f16vec2 pow(f16vec2, f16vec2);" + "f16vec3 pow(f16vec3, f16vec3);" + "f16vec4 pow(f16vec4, f16vec4);" + + "float16_t exp(float16_t);" + "f16vec2 exp(f16vec2);" + "f16vec3 exp(f16vec3);" + "f16vec4 exp(f16vec4);" + + "float16_t log(float16_t);" + "f16vec2 log(f16vec2);" + "f16vec3 log(f16vec3);" + "f16vec4 log(f16vec4);" + + "float16_t exp2(float16_t);" + "f16vec2 exp2(f16vec2);" + "f16vec3 exp2(f16vec3);" + "f16vec4 exp2(f16vec4);" + + "float16_t log2(float16_t);" + "f16vec2 log2(f16vec2);" + "f16vec3 log2(f16vec3);" + "f16vec4 log2(f16vec4);" + + "float16_t sqrt(float16_t);" + "f16vec2 sqrt(f16vec2);" + "f16vec3 sqrt(f16vec3);" + "f16vec4 sqrt(f16vec4);" + + "float16_t inversesqrt(float16_t);" + "f16vec2 inversesqrt(f16vec2);" + "f16vec3 inversesqrt(f16vec3);" + "f16vec4 inversesqrt(f16vec4);" + + "float16_t abs(float16_t);" + "f16vec2 abs(f16vec2);" + "f16vec3 abs(f16vec3);" + "f16vec4 abs(f16vec4);" + + "float16_t sign(float16_t);" + "f16vec2 sign(f16vec2);" + "f16vec3 sign(f16vec3);" + "f16vec4 sign(f16vec4);" + + "float16_t floor(float16_t);" + "f16vec2 floor(f16vec2);" + "f16vec3 floor(f16vec3);" + "f16vec4 floor(f16vec4);" + + "float16_t trunc(float16_t);" + "f16vec2 trunc(f16vec2);" + "f16vec3 trunc(f16vec3);" + "f16vec4 trunc(f16vec4);" + + "float16_t round(float16_t);" + "f16vec2 round(f16vec2);" + "f16vec3 round(f16vec3);" + "f16vec4 round(f16vec4);" + + "float16_t roundEven(float16_t);" + "f16vec2 roundEven(f16vec2);" + "f16vec3 roundEven(f16vec3);" + "f16vec4 roundEven(f16vec4);" + + "float16_t ceil(float16_t);" + "f16vec2 ceil(f16vec2);" + "f16vec3 ceil(f16vec3);" + "f16vec4 ceil(f16vec4);" + + "float16_t fract(float16_t);" + "f16vec2 fract(f16vec2);" + "f16vec3 fract(f16vec3);" + "f16vec4 fract(f16vec4);" + + "float16_t mod(float16_t, float16_t);" + "f16vec2 mod(f16vec2, float16_t);" + "f16vec3 mod(f16vec3, float16_t);" + "f16vec4 mod(f16vec4, float16_t);" + "f16vec2 mod(f16vec2, f16vec2);" + "f16vec3 mod(f16vec3, f16vec3);" + "f16vec4 mod(f16vec4, f16vec4);" + + "float16_t modf(float16_t, out float16_t);" + "f16vec2 modf(f16vec2, out f16vec2);" + "f16vec3 modf(f16vec3, out f16vec3);" + "f16vec4 modf(f16vec4, out f16vec4);" + + "float16_t min(float16_t, float16_t);" + "f16vec2 min(f16vec2, float16_t);" + "f16vec3 min(f16vec3, float16_t);" + "f16vec4 min(f16vec4, float16_t);" + "f16vec2 min(f16vec2, f16vec2);" + "f16vec3 min(f16vec3, f16vec3);" + "f16vec4 min(f16vec4, f16vec4);" + + "float16_t max(float16_t, float16_t);" + "f16vec2 max(f16vec2, float16_t);" + "f16vec3 max(f16vec3, float16_t);" + "f16vec4 max(f16vec4, float16_t);" + "f16vec2 max(f16vec2, f16vec2);" + "f16vec3 max(f16vec3, f16vec3);" + "f16vec4 max(f16vec4, f16vec4);" + + "float16_t clamp(float16_t, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, float16_t, float16_t);" + "f16vec3 clamp(f16vec3, float16_t, float16_t);" + "f16vec4 clamp(f16vec4, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, f16vec2, f16vec2);" + "f16vec3 clamp(f16vec3, f16vec3, f16vec3);" + "f16vec4 clamp(f16vec4, f16vec4, f16vec4);" + + "float16_t mix(float16_t, float16_t, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, float16_t);" + "f16vec3 mix(f16vec3, f16vec3, float16_t);" + "f16vec4 mix(f16vec4, f16vec4, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, f16vec2);" + "f16vec3 mix(f16vec3, f16vec3, f16vec3);" + "f16vec4 mix(f16vec4, f16vec4, f16vec4);" + "float16_t mix(float16_t, float16_t, bool);" + "f16vec2 mix(f16vec2, f16vec2, bvec2);" + "f16vec3 mix(f16vec3, f16vec3, bvec3);" + "f16vec4 mix(f16vec4, f16vec4, bvec4);" + + "float16_t step(float16_t, float16_t);" + "f16vec2 step(f16vec2, f16vec2);" + "f16vec3 step(f16vec3, f16vec3);" + "f16vec4 step(f16vec4, f16vec4);" + "f16vec2 step(float16_t, f16vec2);" + "f16vec3 step(float16_t, f16vec3);" + "f16vec4 step(float16_t, f16vec4);" + + "float16_t smoothstep(float16_t, float16_t, float16_t);" + "f16vec2 smoothstep(f16vec2, f16vec2, f16vec2);" + "f16vec3 smoothstep(f16vec3, f16vec3, f16vec3);" + "f16vec4 smoothstep(f16vec4, f16vec4, f16vec4);" + "f16vec2 smoothstep(float16_t, float16_t, f16vec2);" + "f16vec3 smoothstep(float16_t, float16_t, f16vec3);" + "f16vec4 smoothstep(float16_t, float16_t, f16vec4);" + + "bool isnan(float16_t);" + "bvec2 isnan(f16vec2);" + "bvec3 isnan(f16vec3);" + "bvec4 isnan(f16vec4);" + + "bool isinf(float16_t);" + "bvec2 isinf(f16vec2);" + "bvec3 isinf(f16vec3);" + "bvec4 isinf(f16vec4);" + + "float16_t fma(float16_t, float16_t, float16_t);" + "f16vec2 fma(f16vec2, f16vec2, f16vec2);" + "f16vec3 fma(f16vec3, f16vec3, f16vec3);" + "f16vec4 fma(f16vec4, f16vec4, f16vec4);" + + "float16_t frexp(float16_t, out int);" + "f16vec2 frexp(f16vec2, out ivec2);" + "f16vec3 frexp(f16vec3, out ivec3);" + "f16vec4 frexp(f16vec4, out ivec4);" + + "float16_t ldexp(float16_t, in int);" + "f16vec2 ldexp(f16vec2, in ivec2);" + "f16vec3 ldexp(f16vec3, in ivec3);" + "f16vec4 ldexp(f16vec4, in ivec4);" + + "uint packFloat2x16(f16vec2);" + "f16vec2 unpackFloat2x16(uint);" + + "float16_t length(float16_t);" + "float16_t length(f16vec2);" + "float16_t length(f16vec3);" + "float16_t length(f16vec4);" + + "float16_t distance(float16_t, float16_t);" + "float16_t distance(f16vec2, f16vec2);" + "float16_t distance(f16vec3, f16vec3);" + "float16_t distance(f16vec4, f16vec4);" + + "float16_t dot(float16_t, float16_t);" + "float16_t dot(f16vec2, f16vec2);" + "float16_t dot(f16vec3, f16vec3);" + "float16_t dot(f16vec4, f16vec4);" + + "f16vec3 cross(f16vec3, f16vec3);" + + "float16_t normalize(float16_t);" + "f16vec2 normalize(f16vec2);" + "f16vec3 normalize(f16vec3);" + "f16vec4 normalize(f16vec4);" + + "float16_t faceforward(float16_t, float16_t, float16_t);" + "f16vec2 faceforward(f16vec2, f16vec2, f16vec2);" + "f16vec3 faceforward(f16vec3, f16vec3, f16vec3);" + "f16vec4 faceforward(f16vec4, f16vec4, f16vec4);" + + "float16_t reflect(float16_t, float16_t);" + "f16vec2 reflect(f16vec2, f16vec2);" + "f16vec3 reflect(f16vec3, f16vec3);" + "f16vec4 reflect(f16vec4, f16vec4);" + + "float16_t refract(float16_t, float16_t, float16_t);" + "f16vec2 refract(f16vec2, f16vec2, float16_t);" + "f16vec3 refract(f16vec3, f16vec3, float16_t);" + "f16vec4 refract(f16vec4, f16vec4, float16_t);" + + "f16mat2 matrixCompMult(f16mat2, f16mat2);" + "f16mat3 matrixCompMult(f16mat3, f16mat3);" + "f16mat4 matrixCompMult(f16mat4, f16mat4);" + "f16mat2x3 matrixCompMult(f16mat2x3, f16mat2x3);" + "f16mat2x4 matrixCompMult(f16mat2x4, f16mat2x4);" + "f16mat3x2 matrixCompMult(f16mat3x2, f16mat3x2);" + "f16mat3x4 matrixCompMult(f16mat3x4, f16mat3x4);" + "f16mat4x2 matrixCompMult(f16mat4x2, f16mat4x2);" + "f16mat4x3 matrixCompMult(f16mat4x3, f16mat4x3);" + + "f16mat2 outerProduct(f16vec2, f16vec2);" + "f16mat3 outerProduct(f16vec3, f16vec3);" + "f16mat4 outerProduct(f16vec4, f16vec4);" + "f16mat2x3 outerProduct(f16vec3, f16vec2);" + "f16mat3x2 outerProduct(f16vec2, f16vec3);" + "f16mat2x4 outerProduct(f16vec4, f16vec2);" + "f16mat4x2 outerProduct(f16vec2, f16vec4);" + "f16mat3x4 outerProduct(f16vec4, f16vec3);" + "f16mat4x3 outerProduct(f16vec3, f16vec4);" + + "f16mat2 transpose(f16mat2);" + "f16mat3 transpose(f16mat3);" + "f16mat4 transpose(f16mat4);" + "f16mat2x3 transpose(f16mat3x2);" + "f16mat3x2 transpose(f16mat2x3);" + "f16mat2x4 transpose(f16mat4x2);" + "f16mat4x2 transpose(f16mat2x4);" + "f16mat3x4 transpose(f16mat4x3);" + "f16mat4x3 transpose(f16mat3x4);" + + "float16_t determinant(f16mat2);" + "float16_t determinant(f16mat3);" + "float16_t determinant(f16mat4);" + + "f16mat2 inverse(f16mat2);" + "f16mat3 inverse(f16mat3);" + "f16mat4 inverse(f16mat4);" + + "bvec2 lessThan(f16vec2, f16vec2);" + "bvec3 lessThan(f16vec3, f16vec3);" + "bvec4 lessThan(f16vec4, f16vec4);" + + "bvec2 lessThanEqual(f16vec2, f16vec2);" + "bvec3 lessThanEqual(f16vec3, f16vec3);" + "bvec4 lessThanEqual(f16vec4, f16vec4);" + + "bvec2 greaterThan(f16vec2, f16vec2);" + "bvec3 greaterThan(f16vec3, f16vec3);" + "bvec4 greaterThan(f16vec4, f16vec4);" + + "bvec2 greaterThanEqual(f16vec2, f16vec2);" + "bvec3 greaterThanEqual(f16vec3, f16vec3);" + "bvec4 greaterThanEqual(f16vec4, f16vec4);" + + "bvec2 equal(f16vec2, f16vec2);" + "bvec3 equal(f16vec3, f16vec3);" + "bvec4 equal(f16vec4, f16vec4);" + + "bvec2 notEqual(f16vec2, f16vec2);" + "bvec3 notEqual(f16vec3, f16vec3);" + "bvec4 notEqual(f16vec4, f16vec4);" + + "bfloat16_t dot(bfloat16_t, bfloat16_t);" + "bfloat16_t dot(bf16vec2, bf16vec2);" + "bfloat16_t dot(bf16vec3, bf16vec3);" + "bfloat16_t dot(bf16vec4, bf16vec4);" + + "int16_t bfloat16BitsToIntEXT(bfloat16_t value);" + "i16vec2 bfloat16BitsToIntEXT(bf16vec2 value);" + "i16vec3 bfloat16BitsToIntEXT(bf16vec3 value);" + "i16vec4 bfloat16BitsToIntEXT(bf16vec4 value);" + + "uint16_t bfloat16BitsToUintEXT(bfloat16_t value);" + "u16vec2 bfloat16BitsToUintEXT(bf16vec2 value);" + "u16vec3 bfloat16BitsToUintEXT(bf16vec3 value);" + "u16vec4 bfloat16BitsToUintEXT(bf16vec4 value);" + + "bfloat16_t intBitsToBFloat16EXT(int16_t value);" + "bf16vec2 intBitsToBFloat16EXT(i16vec2 value);" + "bf16vec3 intBitsToBFloat16EXT(i16vec3 value);" + "bf16vec4 intBitsToBFloat16EXT(i16vec4 value);" + + "bfloat16_t uintBitsToBFloat16EXT(uint16_t value);" + "bf16vec2 uintBitsToBFloat16EXT(u16vec2 value);" + "bf16vec3 uintBitsToBFloat16EXT(u16vec3 value);" + "bf16vec4 uintBitsToBFloat16EXT(u16vec4 value);" + + "\n"); + } + + // Explicit types + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "int8_t abs(int8_t);" + "i8vec2 abs(i8vec2);" + "i8vec3 abs(i8vec3);" + "i8vec4 abs(i8vec4);" + + "int8_t sign(int8_t);" + "i8vec2 sign(i8vec2);" + "i8vec3 sign(i8vec3);" + "i8vec4 sign(i8vec4);" + + "int8_t min(int8_t x, int8_t y);" + "i8vec2 min(i8vec2 x, int8_t y);" + "i8vec3 min(i8vec3 x, int8_t y);" + "i8vec4 min(i8vec4 x, int8_t y);" + "i8vec2 min(i8vec2 x, i8vec2 y);" + "i8vec3 min(i8vec3 x, i8vec3 y);" + "i8vec4 min(i8vec4 x, i8vec4 y);" + + "uint8_t min(uint8_t x, uint8_t y);" + "u8vec2 min(u8vec2 x, uint8_t y);" + "u8vec3 min(u8vec3 x, uint8_t y);" + "u8vec4 min(u8vec4 x, uint8_t y);" + "u8vec2 min(u8vec2 x, u8vec2 y);" + "u8vec3 min(u8vec3 x, u8vec3 y);" + "u8vec4 min(u8vec4 x, u8vec4 y);" + + "int8_t max(int8_t x, int8_t y);" + "i8vec2 max(i8vec2 x, int8_t y);" + "i8vec3 max(i8vec3 x, int8_t y);" + "i8vec4 max(i8vec4 x, int8_t y);" + "i8vec2 max(i8vec2 x, i8vec2 y);" + "i8vec3 max(i8vec3 x, i8vec3 y);" + "i8vec4 max(i8vec4 x, i8vec4 y);" + + "uint8_t max(uint8_t x, uint8_t y);" + "u8vec2 max(u8vec2 x, uint8_t y);" + "u8vec3 max(u8vec3 x, uint8_t y);" + "u8vec4 max(u8vec4 x, uint8_t y);" + "u8vec2 max(u8vec2 x, u8vec2 y);" + "u8vec3 max(u8vec3 x, u8vec3 y);" + "u8vec4 max(u8vec4 x, u8vec4 y);" + + "int8_t clamp(int8_t x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, int8_t minVal, int8_t maxVal);" + "i8vec3 clamp(i8vec3 x, int8_t minVal, int8_t maxVal);" + "i8vec4 clamp(i8vec4 x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, i8vec2 minVal, i8vec2 maxVal);" + "i8vec3 clamp(i8vec3 x, i8vec3 minVal, i8vec3 maxVal);" + "i8vec4 clamp(i8vec4 x, i8vec4 minVal, i8vec4 maxVal);" + + "uint8_t clamp(uint8_t x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, uint8_t minVal, uint8_t maxVal);" + "u8vec3 clamp(u8vec3 x, uint8_t minVal, uint8_t maxVal);" + "u8vec4 clamp(u8vec4 x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, u8vec2 minVal, u8vec2 maxVal);" + "u8vec3 clamp(u8vec3 x, u8vec3 minVal, u8vec3 maxVal);" + "u8vec4 clamp(u8vec4 x, u8vec4 minVal, u8vec4 maxVal);" + + "int8_t mix(int8_t, int8_t, bool);" + "i8vec2 mix(i8vec2, i8vec2, bvec2);" + "i8vec3 mix(i8vec3, i8vec3, bvec3);" + "i8vec4 mix(i8vec4, i8vec4, bvec4);" + "uint8_t mix(uint8_t, uint8_t, bool);" + "u8vec2 mix(u8vec2, u8vec2, bvec2);" + "u8vec3 mix(u8vec3, u8vec3, bvec3);" + "u8vec4 mix(u8vec4, u8vec4, bvec4);" + + "bvec2 lessThan(i8vec2, i8vec2);" + "bvec3 lessThan(i8vec3, i8vec3);" + "bvec4 lessThan(i8vec4, i8vec4);" + "bvec2 lessThan(u8vec2, u8vec2);" + "bvec3 lessThan(u8vec3, u8vec3);" + "bvec4 lessThan(u8vec4, u8vec4);" + + "bvec2 lessThanEqual(i8vec2, i8vec2);" + "bvec3 lessThanEqual(i8vec3, i8vec3);" + "bvec4 lessThanEqual(i8vec4, i8vec4);" + "bvec2 lessThanEqual(u8vec2, u8vec2);" + "bvec3 lessThanEqual(u8vec3, u8vec3);" + "bvec4 lessThanEqual(u8vec4, u8vec4);" + + "bvec2 greaterThan(i8vec2, i8vec2);" + "bvec3 greaterThan(i8vec3, i8vec3);" + "bvec4 greaterThan(i8vec4, i8vec4);" + "bvec2 greaterThan(u8vec2, u8vec2);" + "bvec3 greaterThan(u8vec3, u8vec3);" + "bvec4 greaterThan(u8vec4, u8vec4);" + + "bvec2 greaterThanEqual(i8vec2, i8vec2);" + "bvec3 greaterThanEqual(i8vec3, i8vec3);" + "bvec4 greaterThanEqual(i8vec4, i8vec4);" + "bvec2 greaterThanEqual(u8vec2, u8vec2);" + "bvec3 greaterThanEqual(u8vec3, u8vec3);" + "bvec4 greaterThanEqual(u8vec4, u8vec4);" + + "bvec2 equal(i8vec2, i8vec2);" + "bvec3 equal(i8vec3, i8vec3);" + "bvec4 equal(i8vec4, i8vec4);" + "bvec2 equal(u8vec2, u8vec2);" + "bvec3 equal(u8vec3, u8vec3);" + "bvec4 equal(u8vec4, u8vec4);" + + "bvec2 notEqual(i8vec2, i8vec2);" + "bvec3 notEqual(i8vec3, i8vec3);" + "bvec4 notEqual(i8vec4, i8vec4);" + "bvec2 notEqual(u8vec2, u8vec2);" + "bvec3 notEqual(u8vec3, u8vec3);" + "bvec4 notEqual(u8vec4, u8vec4);" + + " int8_t bitfieldExtract( int8_t, int8_t, int8_t);" + "i8vec2 bitfieldExtract(i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldExtract(i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldExtract(i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldExtract( uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldExtract(u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldExtract(u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldExtract(u8vec4, int8_t, int8_t);" + + " int8_t bitfieldInsert( int8_t base, int8_t, int8_t, int8_t);" + "i8vec2 bitfieldInsert(i8vec2 base, i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldInsert(i8vec3 base, i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldInsert(i8vec4 base, i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldInsert( uint8_t base, uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldInsert(u8vec2 base, u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldInsert(u8vec3 base, u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldInsert(u8vec4 base, u8vec4, int8_t, int8_t);" + + " int8_t bitCount( int8_t);" + "i8vec2 bitCount(i8vec2);" + "i8vec3 bitCount(i8vec3);" + "i8vec4 bitCount(i8vec4);" + + " int8_t bitCount( uint8_t);" + "i8vec2 bitCount(u8vec2);" + "i8vec3 bitCount(u8vec3);" + "i8vec4 bitCount(u8vec4);" + + " int8_t findLSB( int8_t);" + "i8vec2 findLSB(i8vec2);" + "i8vec3 findLSB(i8vec3);" + "i8vec4 findLSB(i8vec4);" + + " int8_t findLSB( uint8_t);" + "i8vec2 findLSB(u8vec2);" + "i8vec3 findLSB(u8vec3);" + "i8vec4 findLSB(u8vec4);" + + " int8_t findMSB( int8_t);" + "i8vec2 findMSB(i8vec2);" + "i8vec3 findMSB(i8vec3);" + "i8vec4 findMSB(i8vec4);" + + " int8_t findMSB( uint8_t);" + "i8vec2 findMSB(u8vec2);" + "i8vec3 findMSB(u8vec3);" + "i8vec4 findMSB(u8vec4);" + + "int16_t abs(int16_t);" + "i16vec2 abs(i16vec2);" + "i16vec3 abs(i16vec3);" + "i16vec4 abs(i16vec4);" + + "int16_t sign(int16_t);" + "i16vec2 sign(i16vec2);" + "i16vec3 sign(i16vec3);" + "i16vec4 sign(i16vec4);" + + "int16_t min(int16_t x, int16_t y);" + "i16vec2 min(i16vec2 x, int16_t y);" + "i16vec3 min(i16vec3 x, int16_t y);" + "i16vec4 min(i16vec4 x, int16_t y);" + "i16vec2 min(i16vec2 x, i16vec2 y);" + "i16vec3 min(i16vec3 x, i16vec3 y);" + "i16vec4 min(i16vec4 x, i16vec4 y);" + + "uint16_t min(uint16_t x, uint16_t y);" + "u16vec2 min(u16vec2 x, uint16_t y);" + "u16vec3 min(u16vec3 x, uint16_t y);" + "u16vec4 min(u16vec4 x, uint16_t y);" + "u16vec2 min(u16vec2 x, u16vec2 y);" + "u16vec3 min(u16vec3 x, u16vec3 y);" + "u16vec4 min(u16vec4 x, u16vec4 y);" + + "int16_t max(int16_t x, int16_t y);" + "i16vec2 max(i16vec2 x, int16_t y);" + "i16vec3 max(i16vec3 x, int16_t y);" + "i16vec4 max(i16vec4 x, int16_t y);" + "i16vec2 max(i16vec2 x, i16vec2 y);" + "i16vec3 max(i16vec3 x, i16vec3 y);" + "i16vec4 max(i16vec4 x, i16vec4 y);" + + "uint16_t max(uint16_t x, uint16_t y);" + "u16vec2 max(u16vec2 x, uint16_t y);" + "u16vec3 max(u16vec3 x, uint16_t y);" + "u16vec4 max(u16vec4 x, uint16_t y);" + "u16vec2 max(u16vec2 x, u16vec2 y);" + "u16vec3 max(u16vec3 x, u16vec3 y);" + "u16vec4 max(u16vec4 x, u16vec4 y);" + + "int16_t clamp(int16_t x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, int16_t minVal, int16_t maxVal);" + "i16vec3 clamp(i16vec3 x, int16_t minVal, int16_t maxVal);" + "i16vec4 clamp(i16vec4 x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, i16vec2 minVal, i16vec2 maxVal);" + "i16vec3 clamp(i16vec3 x, i16vec3 minVal, i16vec3 maxVal);" + "i16vec4 clamp(i16vec4 x, i16vec4 minVal, i16vec4 maxVal);" + + "uint16_t clamp(uint16_t x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, uint16_t minVal, uint16_t maxVal);" + "u16vec3 clamp(u16vec3 x, uint16_t minVal, uint16_t maxVal);" + "u16vec4 clamp(u16vec4 x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, u16vec2 minVal, u16vec2 maxVal);" + "u16vec3 clamp(u16vec3 x, u16vec3 minVal, u16vec3 maxVal);" + "u16vec4 clamp(u16vec4 x, u16vec4 minVal, u16vec4 maxVal);" + + "int16_t mix(int16_t, int16_t, bool);" + "i16vec2 mix(i16vec2, i16vec2, bvec2);" + "i16vec3 mix(i16vec3, i16vec3, bvec3);" + "i16vec4 mix(i16vec4, i16vec4, bvec4);" + "uint16_t mix(uint16_t, uint16_t, bool);" + "u16vec2 mix(u16vec2, u16vec2, bvec2);" + "u16vec3 mix(u16vec3, u16vec3, bvec3);" + "u16vec4 mix(u16vec4, u16vec4, bvec4);" + + "float16_t frexp(float16_t, out int16_t);" + "f16vec2 frexp(f16vec2, out i16vec2);" + "f16vec3 frexp(f16vec3, out i16vec3);" + "f16vec4 frexp(f16vec4, out i16vec4);" + + "float16_t ldexp(float16_t, int16_t);" + "f16vec2 ldexp(f16vec2, i16vec2);" + "f16vec3 ldexp(f16vec3, i16vec3);" + "f16vec4 ldexp(f16vec4, i16vec4);" + + "int16_t halfBitsToInt16(float16_t);" + "i16vec2 halfBitsToInt16(f16vec2);" + "i16vec3 halhBitsToInt16(f16vec3);" + "i16vec4 halfBitsToInt16(f16vec4);" + + "uint16_t halfBitsToUint16(float16_t);" + "u16vec2 halfBitsToUint16(f16vec2);" + "u16vec3 halfBitsToUint16(f16vec3);" + "u16vec4 halfBitsToUint16(f16vec4);" + + "int16_t float16BitsToInt16(float16_t);" + "i16vec2 float16BitsToInt16(f16vec2);" + "i16vec3 float16BitsToInt16(f16vec3);" + "i16vec4 float16BitsToInt16(f16vec4);" + + "uint16_t float16BitsToUint16(float16_t);" + "u16vec2 float16BitsToUint16(f16vec2);" + "u16vec3 float16BitsToUint16(f16vec3);" + "u16vec4 float16BitsToUint16(f16vec4);" + + "float16_t int16BitsToFloat16(int16_t);" + "f16vec2 int16BitsToFloat16(i16vec2);" + "f16vec3 int16BitsToFloat16(i16vec3);" + "f16vec4 int16BitsToFloat16(i16vec4);" + + "float16_t uint16BitsToFloat16(uint16_t);" + "f16vec2 uint16BitsToFloat16(u16vec2);" + "f16vec3 uint16BitsToFloat16(u16vec3);" + "f16vec4 uint16BitsToFloat16(u16vec4);" + + "float16_t int16BitsToHalf(int16_t);" + "f16vec2 int16BitsToHalf(i16vec2);" + "f16vec3 int16BitsToHalf(i16vec3);" + "f16vec4 int16BitsToHalf(i16vec4);" + + "float16_t uint16BitsToHalf(uint16_t);" + "f16vec2 uint16BitsToHalf(u16vec2);" + "f16vec3 uint16BitsToHalf(u16vec3);" + "f16vec4 uint16BitsToHalf(u16vec4);" + + "int packInt2x16(i16vec2);" + "uint packUint2x16(u16vec2);" + "int64_t packInt4x16(i16vec4);" + "uint64_t packUint4x16(u16vec4);" + "i16vec2 unpackInt2x16(int);" + "u16vec2 unpackUint2x16(uint);" + "i16vec4 unpackInt4x16(int64_t);" + "u16vec4 unpackUint4x16(uint64_t);" + + "bvec2 lessThan(i16vec2, i16vec2);" + "bvec3 lessThan(i16vec3, i16vec3);" + "bvec4 lessThan(i16vec4, i16vec4);" + "bvec2 lessThan(u16vec2, u16vec2);" + "bvec3 lessThan(u16vec3, u16vec3);" + "bvec4 lessThan(u16vec4, u16vec4);" + + "bvec2 lessThanEqual(i16vec2, i16vec2);" + "bvec3 lessThanEqual(i16vec3, i16vec3);" + "bvec4 lessThanEqual(i16vec4, i16vec4);" + "bvec2 lessThanEqual(u16vec2, u16vec2);" + "bvec3 lessThanEqual(u16vec3, u16vec3);" + "bvec4 lessThanEqual(u16vec4, u16vec4);" + + "bvec2 greaterThan(i16vec2, i16vec2);" + "bvec3 greaterThan(i16vec3, i16vec3);" + "bvec4 greaterThan(i16vec4, i16vec4);" + "bvec2 greaterThan(u16vec2, u16vec2);" + "bvec3 greaterThan(u16vec3, u16vec3);" + "bvec4 greaterThan(u16vec4, u16vec4);" + + "bvec2 greaterThanEqual(i16vec2, i16vec2);" + "bvec3 greaterThanEqual(i16vec3, i16vec3);" + "bvec4 greaterThanEqual(i16vec4, i16vec4);" + "bvec2 greaterThanEqual(u16vec2, u16vec2);" + "bvec3 greaterThanEqual(u16vec3, u16vec3);" + "bvec4 greaterThanEqual(u16vec4, u16vec4);" + + "bvec2 equal(i16vec2, i16vec2);" + "bvec3 equal(i16vec3, i16vec3);" + "bvec4 equal(i16vec4, i16vec4);" + "bvec2 equal(u16vec2, u16vec2);" + "bvec3 equal(u16vec3, u16vec3);" + "bvec4 equal(u16vec4, u16vec4);" + + "bvec2 notEqual(i16vec2, i16vec2);" + "bvec3 notEqual(i16vec3, i16vec3);" + "bvec4 notEqual(i16vec4, i16vec4);" + "bvec2 notEqual(u16vec2, u16vec2);" + "bvec3 notEqual(u16vec3, u16vec3);" + "bvec4 notEqual(u16vec4, u16vec4);" + + " int16_t bitfieldExtract( int16_t, int16_t, int16_t);" + "i16vec2 bitfieldExtract(i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldExtract(i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldExtract(i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldExtract( uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldExtract(u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldExtract(u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldExtract(u16vec4, int16_t, int16_t);" + + " int16_t bitfieldInsert( int16_t base, int16_t, int16_t, int16_t);" + "i16vec2 bitfieldInsert(i16vec2 base, i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldInsert(i16vec3 base, i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldInsert(i16vec4 base, i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldInsert( uint16_t base, uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldInsert(u16vec2 base, u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldInsert(u16vec3 base, u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldInsert(u16vec4 base, u16vec4, int16_t, int16_t);" + + " int16_t bitCount( int16_t);" + "i16vec2 bitCount(i16vec2);" + "i16vec3 bitCount(i16vec3);" + "i16vec4 bitCount(i16vec4);" + + " int16_t bitCount( uint16_t);" + "i16vec2 bitCount(u16vec2);" + "i16vec3 bitCount(u16vec3);" + "i16vec4 bitCount(u16vec4);" + + " int16_t findLSB( int16_t);" + "i16vec2 findLSB(i16vec2);" + "i16vec3 findLSB(i16vec3);" + "i16vec4 findLSB(i16vec4);" + + " int16_t findLSB( uint16_t);" + "i16vec2 findLSB(u16vec2);" + "i16vec3 findLSB(u16vec3);" + "i16vec4 findLSB(u16vec4);" + + " int16_t findMSB( int16_t);" + "i16vec2 findMSB(i16vec2);" + "i16vec3 findMSB(i16vec3);" + "i16vec4 findMSB(i16vec4);" + + " int16_t findMSB( uint16_t);" + "i16vec2 findMSB(u16vec2);" + "i16vec3 findMSB(u16vec3);" + "i16vec4 findMSB(u16vec4);" + + "int16_t pack16(i8vec2);" + "uint16_t pack16(u8vec2);" + "int32_t pack32(i8vec4);" + "uint32_t pack32(u8vec4);" + "int32_t pack32(i16vec2);" + "uint32_t pack32(u16vec2);" + "int64_t pack64(i16vec4);" + "uint64_t pack64(u16vec4);" + "int64_t pack64(i32vec2);" + "uint64_t pack64(u32vec2);" + + "i8vec2 unpack8(int16_t);" + "u8vec2 unpack8(uint16_t);" + "i8vec4 unpack8(int32_t);" + "u8vec4 unpack8(uint32_t);" + "i16vec2 unpack16(int32_t);" + "u16vec2 unpack16(uint32_t);" + "i16vec4 unpack16(int64_t);" + "u16vec4 unpack16(uint64_t);" + "i32vec2 unpack32(int64_t);" + "u32vec2 unpack32(uint64_t);" + + // GL_EXT_expect_assume + "int8_t expectEXT(int8_t, int8_t);" + "i8vec2 expectEXT(i8vec2, i8vec2);" + "i8vec3 expectEXT(i8vec3, i8vec3);" + "i8vec4 expectEXT(i8vec4, i8vec4);" + + "uint8_t expectEXT(uint8_t, uint8_t);" + "u8vec2 expectEXT(u8vec2, u8vec2);" + "u8vec3 expectEXT(u8vec3, u8vec3);" + "u8vec4 expectEXT(u8vec4, u8vec4);" + + "int16_t expectEXT(int16_t, int16_t);" + "i16vec2 expectEXT(i16vec2, i16vec2);" + "i16vec3 expectEXT(i16vec3, i16vec3);" + "i16vec4 expectEXT(i16vec4, i16vec4);" + + "uint16_t expectEXT(uint16_t, uint16_t);" + "u16vec2 expectEXT(u16vec2, u16vec2);" + "u16vec3 expectEXT(u16vec3, u16vec3);" + "u16vec4 expectEXT(u16vec4, u16vec4);" + + "int64_t expectEXT(int64_t, int64_t);" + "i64vec2 expectEXT(i64vec2, i64vec2);" + "i64vec3 expectEXT(i64vec3, i64vec3);" + "i64vec4 expectEXT(i64vec4, i64vec4);" + + "uint64_t expectEXT(uint64_t, uint64_t);" + "u64vec2 expectEXT(u64vec2, u64vec2);" + "u64vec3 expectEXT(u64vec3, u64vec3);" + "u64vec4 expectEXT(u64vec4, u64vec4);" + "\n"); + } + + // Builtins for GL_EXT_texture_shadow_lod + if ((profile == EEsProfile && version >= 300) || ((profile != EEsProfile && version >= 130))) { + commonBuiltins.append( + "float texture(sampler2DArrayShadow, vec4, float);" + "float texture(samplerCubeArrayShadow, vec4, float, float);" + "float textureLod(sampler2DArrayShadow, vec4, float);" + "float textureLod(samplerCubeShadow, vec4, float);" + "float textureLod(samplerCubeArrayShadow, vec4, float, float);" + "float textureLodOffset(sampler2DArrayShadow, vec4, float, ivec2);" + "float textureOffset(sampler2DArrayShadow, vec4 , ivec2, float);" + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl64bits); + stageBuiltins[EShLangFragment].append( + "float64_t interpolateAtCentroid(float64_t);" + "f64vec2 interpolateAtCentroid(f64vec2);" + "f64vec3 interpolateAtCentroid(f64vec3);" + "f64vec4 interpolateAtCentroid(f64vec4);" + + "float64_t interpolateAtSample(float64_t, int);" + "f64vec2 interpolateAtSample(f64vec2, int);" + "f64vec3 interpolateAtSample(f64vec3, int);" + "f64vec4 interpolateAtSample(f64vec4, int);" + + "float64_t interpolateAtOffset(float64_t, f64vec2);" + "f64vec2 interpolateAtOffset(f64vec2, f64vec2);" + "f64vec3 interpolateAtOffset(f64vec3, f64vec2);" + "f64vec4 interpolateAtOffset(f64vec4, f64vec2);" + + "\n"); + + } + + // GL_EXT_expect_assume + if ((profile == EEsProfile && version >= 310) || + ((profile != EEsProfile && version >= 140))) { + commonBuiltins.append( + "void assumeEXT(bool);" + + "bool expectEXT(bool, bool);" + "bvec2 expectEXT(bvec2, bvec2);" + "bvec3 expectEXT(bvec3, bvec3);" + "bvec4 expectEXT(bvec4, bvec4);" + + "int expectEXT(int, int);" + "ivec2 expectEXT(ivec2, ivec2);" + "ivec3 expectEXT(ivec3, ivec3);" + "ivec4 expectEXT(ivec4, ivec4);" + + "uint expectEXT(uint, uint);" + "uvec2 expectEXT(uvec2, uvec2);" + "uvec3 expectEXT(uvec3, uvec3);" + "uvec4 expectEXT(uvec4, uvec4);" + "\n"); + } + + // QCOM_image_processing + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + commonBuiltins.append( + "vec4 textureWeightedQCOM(sampler2D, vec2, sampler2DArray);" + "vec4 textureWeightedQCOM(sampler2D, vec2, sampler1DArray);" + "vec4 textureBoxFilterQCOM(sampler2D, vec2, vec2);" + "vec4 textureBlockMatchSADQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + "vec4 textureBlockMatchSSDQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + + "vec4 textureBlockMatchWindowSSDQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + "vec4 textureBlockMatchWindowSADQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + "vec4 textureBlockMatchGatherSSDQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + "vec4 textureBlockMatchGatherSADQCOM(sampler2D, uvec2, sampler2D, uvec2, uvec2);" + "\n"); + } + + //============================================================================ + // + // Prototypes for built-in functions seen by vertex shaders only. + // (Except legacy lod functions, where it depends which release they are + // vertex only.) + // + //============================================================================ + + // + // Geometric Functions. + // + if (spvVersion.vulkan == 0 && IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append("vec4 ftransform();"); + + // + // Original-style texture Functions with lod. + // + TString* s; + if (version == 100) + s = &stageBuiltins[EShLangVertex]; + else + s = &commonBuiltins; + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture2DLod(sampler2D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 texture3DLod(sampler3D, vec3, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod + + "\n"); + } + } + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture1DLod(sampler1D, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DLod(sampler1DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DLod(sampler2DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjLod(sampler1DShadow, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjLod(sampler2DShadow, vec4, float);" // GL_ARB_shader_texture_lod + + "vec4 texture1DGradARB(sampler1D, float, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec2, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DGradARB(sampler2D, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture3DGradARB(sampler3D, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 texture3DProjGradARB(sampler3D, vec4, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 textureCubeGradARB(samplerCube, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 shadow1DGradARB(sampler1DShadow, vec3, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjGradARB( sampler1DShadow, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DGradARB(sampler2DShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjGradARB( sampler2DShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectGradARB(sampler2DRect, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + + "\n"); + } + } + + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + //============================================================================ + // + // Prototypes for built-in functions seen by geometry shaders only. + // + //============================================================================ + + if (profile != EEsProfile && (version >= 400 || version == 150)) { + stageBuiltins[EShLangGeometry].append( + "void EmitStreamVertex(int);" + "void EndStreamPrimitive(int);" + ); + } + stageBuiltins[EShLangGeometry].append( + "void EmitVertex();" + "void EndPrimitive();" + "\n"); + } + + //============================================================================ + // + // Prototypes for all control functions. + // + //============================================================================ + bool esBarrier = (profile == EEsProfile && version >= 310); + if ((profile != EEsProfile && version >= 150) || esBarrier) + stageBuiltins[EShLangTessControl].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) + stageBuiltins[EShLangCompute].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMesh].append( + "void barrier();" + ); + stageBuiltins[EShLangTask].append( + "void barrier();" + ); + } + if ((profile != EEsProfile && version >= 130) || esBarrier) + commonBuiltins.append( + "void memoryBarrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) { + commonBuiltins.append( + "void memoryBarrierBuffer();" + ); + stageBuiltins[EShLangCompute].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } + if ((profile != EEsProfile && version >= 420) || esBarrier) { + if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) { + commonBuiltins.append("void memoryBarrierAtomicCounter();"); + } + commonBuiltins.append("void memoryBarrierImage();"); + } + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMesh].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + stageBuiltins[EShLangTask].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } + + commonBuiltins.append("void controlBarrier(int, int, int, int);\n" + "void memoryBarrier(int, int, int);\n"); + + commonBuiltins.append("void debugPrintfEXT();\n"); + + if (profile != EEsProfile && version >= 450) { + // coopMatStoreNV perhaps ought to have "out" on the buf parameter, but + // adding it introduces undesirable tempArgs on the stack. What we want + // is more like "buf" thought of as a pointer value being an in parameter. + stageBuiltins[EShLangCompute].append( + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal float64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "fcoopmatNV coopMatMulAddNV(fcoopmatNV A, fcoopmatNV B, fcoopmatNV C);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent nontemporal uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "icoopmatNV coopMatMulAddNV(icoopmatNV A, icoopmatNV B, icoopmatNV C);\n" + "ucoopmatNV coopMatMulAddNV(ucoopmatNV A, ucoopmatNV B, ucoopmatNV C);\n" + ); + + std::stringstream cooperativeMatrixFuncs; + + { + static const char *allTypes[] = + { + "float", "vec2", "vec4", + "float16_t", "f16vec2", "f16vec4", + "bfloat16_t", "bf16vec2", "bf16vec4", + "double", "dvec2", "dvec4", + "int8_t", "i8vec2", "i8vec4", + "int16_t", "i16vec2", "i16vec4", + "int", "ivec2", "ivec4", + "int64_t", "i64vec2", "i64vec4", + "uint8_t", "u8vec2", "u8vec4", + "uint16_t", "u16vec2", "u16vec4", + "uint", "uvec2", "uvec4", + "uint64_t", "u64vec2", "u64vec4", + }; + for (auto t : allTypes) { + cooperativeMatrixFuncs << "void coopMatLoad(out coopmat m, volatile coherent nontemporal " << t << "[] buf, uint element, uint stride, int matrixLayout);\n"; + cooperativeMatrixFuncs << "void coopMatStore(coopmat m, volatile coherent nontemporal " << t << "[] buf, uint element, uint stride, int matrixLayout);\n"; + } + // Just use uint8_t for buffer type, we have special matching rules to allow any conversion + cooperativeMatrixFuncs << "void coopMatLoadTensorNV(inout coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t);\n"; + cooperativeMatrixFuncs << "void coopMatLoadTensorNV(inout coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t, tensorViewNV v);\n"; + cooperativeMatrixFuncs << "void coopMatLoadTensorNV(inout coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t, __function f);\n"; + cooperativeMatrixFuncs << "void coopMatLoadTensorNV(inout coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t, tensorViewNV v, __function f);\n"; + cooperativeMatrixFuncs << "void coopMatStoreTensorNV(coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t);\n"; + cooperativeMatrixFuncs << "void coopMatStoreTensorNV(coopmat m, volatile coherent nontemporal uint8_t[] buf, uint element, tensorLayoutNV t, tensorViewNV v);\n"; + } + + cooperativeMatrixFuncs << + "coopmat coopMatMulAdd(coopmat A, coopmat B, coopmat C);\n" + "coopmat coopMatMulAdd(coopmat A, coopmat B, coopmat C, int matrixOperands);\n"; + + commonBuiltins.append(cooperativeMatrixFuncs.str().c_str()); + + commonBuiltins.append( + "const int gl_MatrixUseA = 0;\n" + "const int gl_MatrixUseB = 1;\n" + "const int gl_MatrixUseAccumulator = 2;\n" + "const int gl_MatrixOperandsSaturatingAccumulation = 0x10;\n" + "const int gl_CooperativeMatrixLayoutRowMajor = 0;\n" + "const int gl_CooperativeMatrixLayoutColumnMajor = 1;\n" + "const int gl_CooperativeMatrixLayoutRowBlockedInterleavedARM = 4202;\n" + "const int gl_CooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203;\n" + "\n" + ); + + commonBuiltins.append( + "void coopMatTransposeNV(out coopmat, coopmat);" + "void coopMatReduceNV(out coopmat, coopmat, int, __function);" + "void coopMatPerElementNV();" + ); + + commonBuiltins.append( + "const int gl_CooperativeMatrixReduceRowNV = 0x1;\n" + "const int gl_CooperativeMatrixReduceColumnNV = 0x2;\n" + "const int gl_CooperativeMatrixReduceRowAndColumnNV = 0x3;\n" + "const int gl_CooperativeMatrixReduce2x2NV = 0x4;\n" + "\n" + ); + + commonBuiltins.append( + "const int gl_CooperativeMatrixClampModeUndefinedNV = 0x0;\n" + "const int gl_CooperativeMatrixClampModeConstantNV = 0x1;\n" + "const int gl_CooperativeMatrixClampModeClampToEdgeNV = 0x2;\n" + "const int gl_CooperativeMatrixClampModeRepeatNV = 0x3;\n" + "const int gl_CooperativeMatrixClampModeMirrorRepeatNV = 0x4;\n" + "\n" + ); + + commonBuiltins.append( + "tensorLayoutNV createTensorLayoutNV(uint Dim);\n" + "tensorLayoutNV createTensorLayoutNV(uint Dim, uint Mode);\n" + + "tensorLayoutNV setTensorLayoutBlockSizeNV(tensorLayoutNV t, uint blockSize0);\n" + "tensorLayoutNV setTensorLayoutBlockSizeNV(tensorLayoutNV t, uint blockSize0, uint blockSize1);\n" + "tensorLayoutNV setTensorLayoutBlockSizeNV(tensorLayoutNV t, uint blockSize0, uint blockSize1, uint blockSize2);\n" + "tensorLayoutNV setTensorLayoutBlockSizeNV(tensorLayoutNV t, uint blockSize0, uint blockSize1, uint blockSize2, uint blockSize3);\n" + "tensorLayoutNV setTensorLayoutBlockSizeNV(tensorLayoutNV t, uint blockSize0, uint blockSize1, uint blockSize2, uint blockSize3, uint blockSize4);\n" + + "tensorLayoutNV setTensorLayoutDimensionNV(tensorLayoutNV t, uint dim0);\n" + "tensorLayoutNV setTensorLayoutDimensionNV(tensorLayoutNV t, uint dim0, uint dim1);\n" + "tensorLayoutNV setTensorLayoutDimensionNV(tensorLayoutNV t, uint dim0, uint dim1, uint dim2);\n" + "tensorLayoutNV setTensorLayoutDimensionNV(tensorLayoutNV t, uint dim0, uint dim1, uint dim2, uint dim3);\n" + "tensorLayoutNV setTensorLayoutDimensionNV(tensorLayoutNV t, uint dim0, uint dim1, uint dim2, uint dim3, uint dim4);\n" + + "tensorLayoutNV setTensorLayoutStrideNV(tensorLayoutNV t, uint stride0);\n" + "tensorLayoutNV setTensorLayoutStrideNV(tensorLayoutNV t, uint stride0, uint stride1);\n" + "tensorLayoutNV setTensorLayoutStrideNV(tensorLayoutNV t, uint stride0, uint stride1, uint stride2);\n" + "tensorLayoutNV setTensorLayoutStrideNV(tensorLayoutNV t, uint stride0, uint stride1, uint stride2, uint stride3);\n" + "tensorLayoutNV setTensorLayoutStrideNV(tensorLayoutNV t, uint stride0, uint stride1, uint stride2, uint stride3, uint stride4);\n" + + "tensorLayoutNV sliceTensorLayoutNV(tensorLayoutNV t, uint offset0, uint span0);\n" + "tensorLayoutNV sliceTensorLayoutNV(tensorLayoutNV t, uint offset0, uint span0, uint offset1, uint span1);\n" + "tensorLayoutNV sliceTensorLayoutNV(tensorLayoutNV t, uint offset0, uint span0, uint offset1, uint span1, uint offset2, uint span2);\n" + "tensorLayoutNV sliceTensorLayoutNV(tensorLayoutNV t, uint offset0, uint span0, uint offset1, uint span1, uint offset2, uint span2, uint offset3, uint span3);\n" + "tensorLayoutNV sliceTensorLayoutNV(tensorLayoutNV t, uint offset0, uint span0, uint offset1, uint span1, uint offset2, uint span2, uint offset3, uint span3, uint offset4, uint span4);\n" + + "tensorLayoutNV setTensorLayoutClampValueNV(tensorLayoutNV t, uint value);\n" + + "tensorViewNV createTensorViewNV(uint Dim);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions, uint p0);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions, uint p0, uint p1);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions, uint p0, uint p1, uint p2);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions, uint p0, uint p1, uint p2, uint p3);\n" + "tensorViewNV createTensorViewNV(uint Dim, bool HasDimensions, uint p0, uint p1, uint p2, uint p3, uint p4);\n" + + "tensorViewNV setTensorViewDimensionsNV(tensorViewNV v, uint dim0);\n" + "tensorViewNV setTensorViewDimensionsNV(tensorViewNV v, uint dim0, uint dim1);\n" + "tensorViewNV setTensorViewDimensionsNV(tensorViewNV v, uint dim0, uint dim1, uint dim2);\n" + "tensorViewNV setTensorViewDimensionsNV(tensorViewNV v, uint dim0, uint dim1, uint dim2, uint dim3);\n" + "tensorViewNV setTensorViewDimensionsNV(tensorViewNV v, uint dim0, uint dim1, uint dim2, uint dim3, uint dim4);\n" + + "tensorViewNV setTensorViewStrideNV(tensorViewNV v, uint stride0);\n" + "tensorViewNV setTensorViewStrideNV(tensorViewNV v, uint stride0, uint stride1);\n" + "tensorViewNV setTensorViewStrideNV(tensorViewNV v, uint stride0, uint stride1, uint stride2);\n" + "tensorViewNV setTensorViewStrideNV(tensorViewNV v, uint stride0, uint stride1, uint stride2, uint stride3);\n" + "tensorViewNV setTensorViewStrideNV(tensorViewNV v, uint stride0, uint stride1, uint stride2, uint stride3, uint stride4);\n" + + "tensorViewNV setTensorViewClipNV(tensorViewNV v, uint clipRowOffset, uint clipRowSpan, uint clipColOffset, uint clipColSpan);\n" + "\n" + ); + } + + if (profile != EEsProfile && version >= 450) { + const char *basicTypes[] = { + "int8_t", + "int16_t", + "int32_t", + "int64_t", + "uint8_t", + "uint16_t", + "uint32_t", + "uint64_t", + "float16_t", + "float32_t", + "float64_t", + }; + for (uint32_t i = 0; i < sizeof(basicTypes)/sizeof(basicTypes[0]); ++i) { + std::string func = std::string("void coopVecMatMulNV(out coopvecNV result, ") + + std::string("coopvecNV v, ") + + std::string("int inputInterpretation, ") + + std::string(basicTypes[i]) + std::string("[] matrix, ") + + std::string("uint matrixOffset, ") + + std::string("int matrixInterpretation, ") + + std::string("uint M, ") + + std::string("uint K, ") + + std::string("int matrixLayout, ") + + std::string("bool transpose, ") + + std::string("uint matrixStride);\n"); + commonBuiltins.append(func.c_str()); + + for (uint32_t j = 0; j < sizeof(basicTypes)/sizeof(basicTypes[0]); ++j) { + func = std::string("void coopVecMatMulAddNV(out coopvecNV result, ") + + std::string("coopvecNV v, ") + + std::string("int inputInterpretation, ") + + std::string(basicTypes[i]) + std::string("[] matrix, ") + + std::string("uint matrixOffset, ") + + std::string("int matrixInterpretation, ") + + std::string(basicTypes[j]) + std::string("[] bias, ") + + std::string("uint biasOffset, ") + + std::string("int biasInterpretation, ") + + std::string("uint M, ") + + std::string("uint K, ") + + std::string("int matrixLayout, ") + + std::string("bool transpose, ") + + std::string("uint matrixStride);\n"); + commonBuiltins.append(func.c_str()); + } + + func = std::string("void coopVecOuterProductAccumulateNV(coopvecNV v1, coopvecNV v2, ") + + std::string(basicTypes[i]) + + std::string("[] buf, uint offset, uint stride, int matrixLayout, int matrixInterpretation);\n"); + commonBuiltins.append(func.c_str()); + + func = std::string("void coopVecReduceSumAccumulateNV(coopvecNV v, ") + + std::string(basicTypes[i]) + + std::string("[] buf, uint offset);\n"); + commonBuiltins.append(func.c_str()); + } + + std::string cooperativeVectorFuncs = + "coopvecNV fma(coopvecNV, coopvecNV, coopvecNV);\n" + "coopvecNV min(coopvecNV, coopvecNV);\n" + "coopvecNV max(coopvecNV, coopvecNV);\n" + "coopvecNV step(coopvecNV, coopvecNV);\n" + "coopvecNV exp(coopvecNV);\n" + "coopvecNV log(coopvecNV);\n" + "coopvecNV tanh(coopvecNV);\n" + "coopvecNV atan(coopvecNV);\n" + "coopvecNV clamp(coopvecNV, coopvecNV, coopvecNV);\n" + "\n" + ; + + commonBuiltins.append(cooperativeVectorFuncs.c_str()); + + const char *scalarAndVectorTypes[] = { + "int8_t", + "int16_t", + "int32_t", + "int64_t", + "uint8_t", + "uint16_t", + "uint32_t", + "uint64_t", + "float16_t", + "float32_t", + "float64_t", + "i8vec2", + "i16vec2", + "i32vec2", + "i64vec2", + "u8vec2", + "u16vec2", + "u32vec2", + "u64vec2", + "f16vec2", + "f32vec2", + "f64vec2", + "i8vec3", + "i16vec3", + "i32vec3", + "i64vec3", + "u8vec3", + "u16vec3", + "u32vec3", + "u64vec3", + "f16vec3", + "f32vec3", + "f64vec3", + "i8vec4", + "i16vec4", + "i32vec4", + "i64vec4", + "u8vec4", + "u16vec4", + "u32vec4", + "u64vec4", + "f16vec4", + "f32vec4", + "f64vec4", + }; + for (uint32_t i = 0; i < sizeof(scalarAndVectorTypes)/sizeof(scalarAndVectorTypes[0]); ++i) { + std::string load = std::string("void coopVecLoadNV(out coopvecNV v, volatile coherent ") + + std::string(scalarAndVectorTypes[i]) + std::string("[] buf, uint offset);"); + std::string store = std::string("void coopVecStoreNV(coopvecNV v, volatile coherent ") + + std::string(scalarAndVectorTypes[i]) + std::string("[] buf, uint offset);"); + commonBuiltins.append(load.c_str()); + commonBuiltins.append(store.c_str()); + } + + commonBuiltins.append( + "const int gl_CooperativeVectorMatrixLayoutRowMajorNV = 0;\n" + "const int gl_CooperativeVectorMatrixLayoutColumnMajorNV = 1;\n" + "const int gl_CooperativeVectorMatrixLayoutInferencingOptimalNV = 2;\n" + "const int gl_CooperativeVectorMatrixLayoutTrainingOptimalNV = 3;\n" + "\n" + ); + + commonBuiltins.append( + "const int gl_ComponentTypeFloat16NV = 0;\n" + "const int gl_ComponentTypeFloat32NV = 1;\n" + "const int gl_ComponentTypeFloat64NV = 2;\n" + "const int gl_ComponentTypeSignedInt8NV = 3;\n" + "const int gl_ComponentTypeSignedInt16NV = 4;\n" + "const int gl_ComponentTypeSignedInt32NV = 5;\n" + "const int gl_ComponentTypeSignedInt64NV = 6;\n" + "const int gl_ComponentTypeUnsignedInt8NV = 7;\n" + "const int gl_ComponentTypeUnsignedInt16NV = 8;\n" + "const int gl_ComponentTypeUnsignedInt32NV = 9;\n" + "const int gl_ComponentTypeUnsignedInt64NV = 10;\n" + "const int gl_ComponentTypeSignedInt8PackedNV = 1000491000;\n" + "const int gl_ComponentTypeUnsignedInt8PackedNV = 1000491001;\n" + "const int gl_ComponentTypeFloatE4M3NV = 1000491002;\n" + "const int gl_ComponentTypeFloatE5M2NV = 1000491003;\n" + "\n" + ); + } + + //============================================================================ + // + // Prototypes for built-in functions seen by fragment shaders only. + // + //============================================================================ + + // + // Original-style texture Functions with bias. + // + if (spvVersion.spv == 0 && (profile != EEsProfile || version == 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2D(sampler2D, vec2, float);" + "vec4 texture2DProj(sampler2D, vec3, float);" + "vec4 texture2DProj(sampler2D, vec4, float);" + "vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D + "vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D + "vec4 textureCube(samplerCube, vec3, float);" + + "\n"); + } + if (spvVersion.spv == 0 && (profile != EEsProfile && version > 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture1D(sampler1D, float, float);" + "vec4 texture1DProj(sampler1D, vec2, float);" + "vec4 texture1DProj(sampler1D, vec4, float);" + "vec4 shadow1D(sampler1DShadow, vec3, float);" + "vec4 shadow2D(sampler2DShadow, vec3, float);" + "vec4 shadow1DProj(sampler1DShadow, vec4, float);" + "vec4 shadow2DProj(sampler2DShadow, vec4, float);" + + "\n"); + } + if (spvVersion.spv == 0 && profile == EEsProfile) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2DLodEXT(sampler2D, vec2, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec3, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec4, float);" // GL_EXT_shader_texture_lod + "vec4 textureCubeLodEXT(samplerCube, vec3, float);" // GL_EXT_shader_texture_lod + + "\n"); + } + + // GL_EXT_shader_tile_image + if (spvVersion.vulkan > 0) { + stageBuiltins[EShLangFragment].append( + "lowp uint stencilAttachmentReadEXT();" + "lowp uint stencilAttachmentReadEXT(int);" + "highp float depthAttachmentReadEXT();" + "highp float depthAttachmentReadEXT(int);" + "\n"); + stageBuiltins[EShLangFragment].append( + "vec4 colorAttachmentReadEXT(attachmentEXT);" + "vec4 colorAttachmentReadEXT(attachmentEXT, int);" + "ivec4 colorAttachmentReadEXT(iattachmentEXT);" + "ivec4 colorAttachmentReadEXT(iattachmentEXT, int);" + "uvec4 colorAttachmentReadEXT(uattachmentEXT);" + "uvec4 colorAttachmentReadEXT(uattachmentEXT, int);" + "\n"); + } + + // GL_ARB_derivative_control + if (profile != EEsProfile && version >= 400) { + stageBuiltins[EShLangFragment].append(derivativeControls); + stageBuiltins[EShLangFragment].append("\n"); + } + + // GL_OES_shader_multisample_interpolation + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtCentroid(float);" + "vec2 interpolateAtCentroid(vec2);" + "vec3 interpolateAtCentroid(vec3);" + "vec4 interpolateAtCentroid(vec4);" + + "float interpolateAtSample(float, int);" + "vec2 interpolateAtSample(vec2, int);" + "vec3 interpolateAtSample(vec3, int);" + "vec4 interpolateAtSample(vec4, int);" + + "float interpolateAtOffset(float, vec2);" + "vec2 interpolateAtOffset(vec2, vec2);" + "vec3 interpolateAtOffset(vec3, vec2);" + "vec4 interpolateAtOffset(vec4, vec2);" + + "\n"); + } + + stageBuiltins[EShLangFragment].append( + "void beginInvocationInterlockARB(void);" + "void endInvocationInterlockARB(void);"); + + stageBuiltins[EShLangFragment].append( + "bool helperInvocationEXT();" + "\n"); + + // GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtVertexAMD(float, uint);" + "vec2 interpolateAtVertexAMD(vec2, uint);" + "vec3 interpolateAtVertexAMD(vec3, uint);" + "vec4 interpolateAtVertexAMD(vec4, uint);" + + "int interpolateAtVertexAMD(int, uint);" + "ivec2 interpolateAtVertexAMD(ivec2, uint);" + "ivec3 interpolateAtVertexAMD(ivec3, uint);" + "ivec4 interpolateAtVertexAMD(ivec4, uint);" + + "uint interpolateAtVertexAMD(uint, uint);" + "uvec2 interpolateAtVertexAMD(uvec2, uint);" + "uvec3 interpolateAtVertexAMD(uvec3, uint);" + "uvec4 interpolateAtVertexAMD(uvec4, uint);" + + "float16_t interpolateAtVertexAMD(float16_t, uint);" + "f16vec2 interpolateAtVertexAMD(f16vec2, uint);" + "f16vec3 interpolateAtVertexAMD(f16vec3, uint);" + "f16vec4 interpolateAtVertexAMD(f16vec4, uint);" + + "\n"); + } + + // GL_AMD_gpu_shader_half_float + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl16bits); + stageBuiltins[EShLangFragment].append("\n"); + + stageBuiltins[EShLangFragment].append( + "float16_t interpolateAtCentroid(float16_t);" + "f16vec2 interpolateAtCentroid(f16vec2);" + "f16vec3 interpolateAtCentroid(f16vec3);" + "f16vec4 interpolateAtCentroid(f16vec4);" + + "float16_t interpolateAtSample(float16_t, int);" + "f16vec2 interpolateAtSample(f16vec2, int);" + "f16vec3 interpolateAtSample(f16vec3, int);" + "f16vec4 interpolateAtSample(f16vec4, int);" + + "float16_t interpolateAtOffset(float16_t, f16vec2);" + "f16vec2 interpolateAtOffset(f16vec2, f16vec2);" + "f16vec3 interpolateAtOffset(f16vec3, f16vec2);" + "f16vec4 interpolateAtOffset(f16vec4, f16vec2);" + + "\n"); + } + + // GL_ARB_shader_clock& GL_EXT_shader_realtime_clock + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uvec2 clock2x32ARB();" + "uint64_t clockARB();" + "uvec2 clockRealtime2x32EXT();" + "uint64_t clockRealtimeEXT();" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450 && spvVersion.vulkan > 0) { + stageBuiltins[EShLangFragment].append( + "uint fragmentMaskFetchAMD(subpassInputMS);" + "uint fragmentMaskFetchAMD(isubpassInputMS);" + "uint fragmentMaskFetchAMD(usubpassInputMS);" + + "vec4 fragmentFetchAMD(subpassInputMS, uint);" + "ivec4 fragmentFetchAMD(isubpassInputMS, uint);" + "uvec4 fragmentFetchAMD(usubpassInputMS, uint);" + + "\n"); + } + + // Builtins for GL_NV_ray_tracing/GL_NV_ray_tracing_motion_blur/GL_EXT_ray_tracing/GL_EXT_ray_query/ + // GL_NV_shader_invocation_reorder/GL_KHR_ray_tracing_position_Fetch + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append("void rayQueryInitializeEXT(rayQueryEXT, accelerationStructureEXT, uint, uint, vec3, float, vec3, float);" + "void rayQueryTerminateEXT(rayQueryEXT);" + "void rayQueryGenerateIntersectionEXT(rayQueryEXT, float);" + "void rayQueryConfirmIntersectionEXT(rayQueryEXT);" + "bool rayQueryProceedEXT(rayQueryEXT);" + "uint rayQueryGetIntersectionTypeEXT(rayQueryEXT, bool);" + "float rayQueryGetRayTMinEXT(rayQueryEXT);" + "uint rayQueryGetRayFlagsEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayOriginEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayDirectionEXT(rayQueryEXT);" + "float rayQueryGetIntersectionTEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceCustomIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceIdEXT(rayQueryEXT, bool);" + "uint rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionGeometryIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionPrimitiveIndexEXT(rayQueryEXT, bool);" + "vec2 rayQueryGetIntersectionBarycentricsEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionFrontFaceEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionCandidateAABBOpaqueEXT(rayQueryEXT);" + "vec3 rayQueryGetIntersectionObjectRayDirectionEXT(rayQueryEXT, bool);" + "vec3 rayQueryGetIntersectionObjectRayOriginEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionObjectToWorldEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionWorldToObjectEXT(rayQueryEXT, bool);" + "void rayQueryGetIntersectionTriangleVertexPositionsEXT(rayQueryEXT, bool, out vec3[3]);" + "int rayQueryGetIntersectionClusterIdNV(rayQueryEXT, bool);" + "vec3 rayQueryGetIntersectionSpherePositionNV(rayQueryEXT, bool);" + "float rayQueryGetIntersectionSphereRadiusNV(rayQueryEXT, bool);" + "float rayQueryGetIntersectionLSSHitValueNV(rayQueryEXT, bool);" + "void rayQueryGetIntersectionLSSPositionsNV(rayQueryEXT, bool, out vec3[2]);" + "void rayQueryGetIntersectionLSSRadiiNV(rayQueryEXT, bool, out float[2]);" + "bool rayQueryIsSphereHitNV(rayQueryEXT, bool);" + "bool rayQueryIsLSSHitNV(rayQueryEXT, bool);" + "\n"); + + stageBuiltins[EShLangRayGen].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayMotionNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "void hitObjectTraceRayNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectTraceRayMotionNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitMotionNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitWithIndexNV(hitObjectNV, accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitWithIndexMotionNV(hitObjectNV, accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordMissNV(hitObjectNV,uint,vec3,float,vec3,float);" + "void hitObjectRecordMissMotionNV(hitObjectNV,uint,vec3,float,vec3,float,float);" + "void hitObjectRecordEmptyNV(hitObjectNV);" + "void hitObjectExecuteShaderNV(hitObjectNV,int);" + "bool hitObjectIsEmptyNV(hitObjectNV);" + "bool hitObjectIsMissNV(hitObjectNV);" + "bool hitObjectIsHitNV(hitObjectNV);" + "float hitObjectGetRayTMinNV(hitObjectNV);" + "float hitObjectGetRayTMaxNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayDirectionNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayDirectionNV(hitObjectNV);" + "mat4x3 hitObjectGetWorldToObjectNV(hitObjectNV);" + "mat4x3 hitObjectGetObjectToWorldNV(hitObjectNV);" + "int hitObjectGetInstanceCustomIndexNV(hitObjectNV);" + "int hitObjectGetInstanceIdNV(hitObjectNV);" + "int hitObjectGetGeometryIndexNV(hitObjectNV);" + "int hitObjectGetPrimitiveIndexNV(hitObjectNV);" + "uint hitObjectGetHitKindNV(hitObjectNV);" + "void hitObjectGetAttributesNV(hitObjectNV,int);" + "float hitObjectGetCurrentTimeNV(hitObjectNV);" + "uint hitObjectGetShaderBindingTableRecordIndexNV(hitObjectNV);" + "uvec2 hitObjectGetShaderRecordBufferHandleNV(hitObjectNV);" + "int hitObjectGetClusterIdNV(hitObjectNV);" + "void reorderThreadNV(uint, uint);" + "void reorderThreadNV(hitObjectNV);" + "void reorderThreadNV(hitObjectNV, uint, uint);" + "vec3 fetchMicroTriangleVertexPositionNV(accelerationStructureEXT, int, int, int, ivec2);" + "vec2 fetchMicroTriangleVertexBarycentricNV(accelerationStructureEXT, int, int, int, ivec2);" + "vec3 hitObjectGetSpherePositionNV(hitObjectNV);" + "float hitObjectGetSphereRadiusNV(hitObjectNV);" + "void hitObjectGetLSSPositionsNV(hitObjectNV, out vec3[2]);" + "void hitObjectGetLSSRadiiNV(hitObjectNV, out float[2]);" + "bool hitObjectIsSphereHitNV(hitObjectNV);" + "bool hitObjectIsLSSHitNV(hitObjectNV);" + "\n"); + stageBuiltins[EShLangIntersect].append( + "bool reportIntersectionNV(float, uint);" + "bool reportIntersectionEXT(float, uint);" + "\n"); + stageBuiltins[EShLangAnyHit].append( + "void ignoreIntersectionNV();" + "void terminateRayNV();" + "\n"); + stageBuiltins[EShLangClosestHit].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayMotionNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "void hitObjectTraceRayNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectTraceRayMotionNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitMotionNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitWithIndexNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitWithIndexMotionNV(hitObjectNV, accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordMissNV(hitObjectNV, uint, vec3, float, vec3, float);" + "void hitObjectRecordMissMotionNV(hitObjectNV,uint,vec3,float,vec3,float,float);" + "void hitObjectRecordEmptyNV(hitObjectNV);" + "void hitObjectExecuteShaderNV(hitObjectNV, int);" + "bool hitObjectIsEmptyNV(hitObjectNV);" + "bool hitObjectIsMissNV(hitObjectNV);" + "bool hitObjectIsHitNV(hitObjectNV);" + "float hitObjectGetRayTMinNV(hitObjectNV);" + "float hitObjectGetRayTMaxNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayDirectionNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayDirectionNV(hitObjectNV);" + "mat4x3 hitObjectGetWorldToObjectNV(hitObjectNV);" + "mat4x3 hitObjectGetObjectToWorldNV(hitObjectNV);" + "int hitObjectGetInstanceCustomIndexNV(hitObjectNV);" + "int hitObjectGetInstanceIdNV(hitObjectNV);" + "int hitObjectGetGeometryIndexNV(hitObjectNV);" + "int hitObjectGetPrimitiveIndexNV(hitObjectNV);" + "uint hitObjectGetHitKindNV(hitObjectNV);" + "void hitObjectGetAttributesNV(hitObjectNV,int);" + "float hitObjectGetCurrentTimeNV(hitObjectNV);" + "uint hitObjectGetShaderBindingTableRecordIndexNV(hitObjectNV);" + "uvec2 hitObjectGetShaderRecordBufferHandleNV(hitObjectNV);" + "int hitObjectGetClusterIdNV(hitObjectNV);" + "vec3 hitObjectGetSpherePositionNV(hitObjectNV);" + "float hitObjectGetSphereRadiusNV(hitObjectNV);" + "void hitObjectGetLSSPositionsNV(hitObjectNV, out vec3[2]);" + "void hitObjectGetLSSRadiiNV(hitObjectNV, out float[2]);" + "bool hitObjectIsSphereHitNV(hitObjectNV);" + "bool hitObjectIsLSSHitNV(hitObjectNV);" + "\n"); + stageBuiltins[EShLangMiss].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayMotionNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "void hitObjectTraceRayNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectTraceRayMotionNV(hitObjectNV,accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitMotionNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordHitWithIndexNV(hitObjectNV,accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,int);" + "void hitObjectRecordHitWithIndexMotionNV(hitObjectNV, accelerationStructureEXT,int,int,int,uint,uint,vec3,float,vec3,float,float,int);" + "void hitObjectRecordMissNV(hitObjectNV, uint, vec3, float, vec3, float);" + "void hitObjectRecordMissMotionNV(hitObjectNV,uint,vec3,float,vec3,float,float);" + "void hitObjectRecordEmptyNV(hitObjectNV);" + "void hitObjectExecuteShaderNV(hitObjectNV, int);" + "bool hitObjectIsEmptyNV(hitObjectNV);" + "bool hitObjectIsMissNV(hitObjectNV);" + "bool hitObjectIsHitNV(hitObjectNV);" + "float hitObjectGetRayTMinNV(hitObjectNV);" + "float hitObjectGetRayTMaxNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetWorldRayDirectionNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayOriginNV(hitObjectNV);" + "vec3 hitObjectGetObjectRayDirectionNV(hitObjectNV);" + "mat4x3 hitObjectGetWorldToObjectNV(hitObjectNV);" + "mat4x3 hitObjectGetObjectToWorldNV(hitObjectNV);" + "int hitObjectGetInstanceCustomIndexNV(hitObjectNV);" + "int hitObjectGetInstanceIdNV(hitObjectNV);" + "int hitObjectGetGeometryIndexNV(hitObjectNV);" + "int hitObjectGetPrimitiveIndexNV(hitObjectNV);" + "uint hitObjectGetHitKindNV(hitObjectNV);" + "void hitObjectGetAttributesNV(hitObjectNV,int);" + "float hitObjectGetCurrentTimeNV(hitObjectNV);" + "uint hitObjectGetShaderBindingTableRecordIndexNV(hitObjectNV);" + "uvec2 hitObjectGetShaderRecordBufferHandleNV(hitObjectNV);" + "int hitObjectGetClusterIdNV(hitObjectNV);" + "vec3 hitObjectGetSpherePositionNV(hitObjectNV);" + "float hitObjectGetSphereRadiusNV(hitObjectNV);" + "void hitObjectGetLSSPositionsNV(hitObjectNV, out vec3[2]);" + "void hitObjectGetLSSRadiiNV(hitObjectNV, out float[2]);" + "bool hitObjectIsSphereHitNV(hitObjectNV);" + "bool hitObjectIsLSSHitNV(hitObjectNV);" + "\n"); + stageBuiltins[EShLangCallable].append( + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + } + + //E_SPV_NV_compute_shader_derivatives + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) { + stageBuiltins[EShLangCompute].append(derivativeControls); + stageBuiltins[EShLangCompute].append("\n"); + } + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangCompute].append(derivativesAndControl16bits); + stageBuiltins[EShLangCompute].append(derivativesAndControl64bits); + stageBuiltins[EShLangCompute].append("\n"); + } + + // Builtins for GL_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMesh].append( + "void writePackedPrimitiveIndices4x8NV(uint, uint);" + "\n"); + } + // Builtins for GL_EXT_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // Builtins for GL_EXT_mesh_shader + stageBuiltins[EShLangTask].append( + "void EmitMeshTasksEXT(uint, uint, uint);" + "\n"); + + stageBuiltins[EShLangMesh].append( + "void SetMeshOutputsEXT(uint, uint);" + "\n"); + } + // Builtins for GL_NV_displacement_micromap + if ((profile != EEsProfile && version >= 460) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMesh].append( + "vec3 fetchMicroTriangleVertexPositionNV(accelerationStructureEXT, int, int, int, ivec2);" + "vec2 fetchMicroTriangleVertexBarycentricNV(accelerationStructureEXT, int, int, int, ivec2);" + "\n"); + + stageBuiltins[EShLangCompute].append( + "vec3 fetchMicroTriangleVertexPositionNV(accelerationStructureEXT, int, int, int, ivec2);" + "vec2 fetchMicroTriangleVertexBarycentricNV(accelerationStructureEXT, int, int, int, ivec2);" + "\n"); + + } + + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + // + // Depth range in window coordinates, p. 33 + // + if (spvVersion.spv == 0) { + commonBuiltins.append( + "struct gl_DepthRangeParameters {" + ); + if (profile == EEsProfile) { + commonBuiltins.append( + "highp float near;" // n + "highp float far;" // f + "highp float diff;" // f - n + ); + } else { + commonBuiltins.append( + "float near;" // n + "float far;" // f + "float diff;" // f - n + ); + } + + commonBuiltins.append( + "};" + "uniform gl_DepthRangeParameters gl_DepthRange;" + "\n"); + } + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + commonBuiltins.append( + "uniform mat4 gl_ModelViewMatrix;" + "uniform mat4 gl_ProjectionMatrix;" + "uniform mat4 gl_ModelViewProjectionMatrix;" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat3 gl_NormalMatrix;" + + "uniform mat4 gl_ModelViewMatrixInverse;" + "uniform mat4 gl_ProjectionMatrixInverse;" + "uniform mat4 gl_ModelViewProjectionMatrixInverse;" + + "uniform mat4 gl_ModelViewMatrixTranspose;" + "uniform mat4 gl_ProjectionMatrixTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixTranspose;" + + "uniform mat4 gl_ModelViewMatrixInverseTranspose;" + "uniform mat4 gl_ProjectionMatrixInverseTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;" + + // + // Normal scaling p. 39. + // + "uniform float gl_NormalScale;" + + // + // Point Size, p. 66, 67. + // + "struct gl_PointParameters {" + "float size;" + "float sizeMin;" + "float sizeMax;" + "float fadeThresholdSize;" + "float distanceConstantAttenuation;" + "float distanceLinearAttenuation;" + "float distanceQuadraticAttenuation;" + "};" + + "uniform gl_PointParameters gl_Point;" + + // + // Material State p. 50, 55. + // + "struct gl_MaterialParameters {" + "vec4 emission;" // Ecm + "vec4 ambient;" // Acm + "vec4 diffuse;" // Dcm + "vec4 specular;" // Scm + "float shininess;" // Srm + "};" + "uniform gl_MaterialParameters gl_FrontMaterial;" + "uniform gl_MaterialParameters gl_BackMaterial;" + + // + // Light State p 50, 53, 55. + // + "struct gl_LightSourceParameters {" + "vec4 ambient;" // Acli + "vec4 diffuse;" // Dcli + "vec4 specular;" // Scli + "vec4 position;" // Ppli + "vec4 halfVector;" // Derived: Hi + "vec3 spotDirection;" // Sdli + "float spotExponent;" // Srli + "float spotCutoff;" // Crli + // (range: [0.0,90.0], 180.0) + "float spotCosCutoff;" // Derived: cos(Crli) + // (range: [1.0,0.0],-1.0) + "float constantAttenuation;" // K0 + "float linearAttenuation;" // K1 + "float quadraticAttenuation;"// K2 + "};" + + "struct gl_LightModelParameters {" + "vec4 ambient;" // Acs + "};" + + "uniform gl_LightModelParameters gl_LightModel;" + + // + // Derived state from products of light and material. + // + "struct gl_LightModelProducts {" + "vec4 sceneColor;" // Derived. Ecm + Acm * Acs + "};" + + "uniform gl_LightModelProducts gl_FrontLightModelProduct;" + "uniform gl_LightModelProducts gl_BackLightModelProduct;" + + "struct gl_LightProducts {" + "vec4 ambient;" // Acm * Acli + "vec4 diffuse;" // Dcm * Dcli + "vec4 specular;" // Scm * Scli + "};" + + // + // Fog p. 161 + // + "struct gl_FogParameters {" + "vec4 color;" + "float density;" + "float start;" + "float end;" + "float scale;" // 1 / (gl_FogEnd - gl_FogStart) + "};" + + "uniform gl_FogParameters gl_Fog;" + + "\n"); + } + + //============================================================================ + // + // Define the interface to the compute shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp uvec3 gl_NumWorkGroups;" + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"); + } + + //============================================================================ + // + // Define the interface to the mesh/task shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex attributes + stageBuiltins[EShLangMesh].append( + "out gl_MeshPerVertexNV {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "float gl_CullDistance[];" + "perviewNV vec4 gl_PositionPerViewNV[];" + "perviewNV float gl_ClipDistancePerViewNV[][];" + "perviewNV float gl_CullDistancePerViewNV[][];" + "} gl_MeshVerticesNV[];" + ); + + // per-primitive attributes + stageBuiltins[EShLangMesh].append( + "perprimitiveNV out gl_MeshPerPrimitiveNV {" + "int gl_PrimitiveID;" + "int gl_Layer;" + "int gl_ViewportIndex;" + "int gl_ViewportMask[];" + "perviewNV int gl_LayerPerViewNV[];" + "perviewNV int gl_ViewportMaskPerViewNV[][];" + "} gl_MeshPrimitivesNV[];" + ); + + stageBuiltins[EShLangMesh].append( + "out uint gl_PrimitiveCountNV;" + "out uint gl_PrimitiveIndicesNV[];" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + "\n"); + + // GL_EXT_mesh_shader + stageBuiltins[EShLangMesh].append( + "out uint gl_PrimitivePointIndicesEXT[];" + "out uvec2 gl_PrimitiveLineIndicesEXT[];" + "out uvec3 gl_PrimitiveTriangleIndicesEXT[];" + "in highp uvec3 gl_NumWorkGroups;" + "\n"); + + // per-vertex attributes + stageBuiltins[EShLangMesh].append( + "out gl_MeshPerVertexEXT {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "float gl_CullDistance[];" + "} gl_MeshVerticesEXT[];" + ); + + // per-primitive attributes + stageBuiltins[EShLangMesh].append( + "perprimitiveEXT out gl_MeshPerPrimitiveEXT {" + "int gl_PrimitiveID;" + "int gl_Layer;" + "int gl_ViewportIndex;" + "bool gl_CullPrimitiveEXT;" + "int gl_PrimitiveShadingRateEXT;" + "} gl_MeshPrimitivesEXT[];" + ); + + stageBuiltins[EShLangTask].append( + "out uint gl_TaskCountNV;" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + "in highp uvec3 gl_NumWorkGroups;" + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangMesh].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "in int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + + stageBuiltins[EShLangTask].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "\n"); + + if (version >= 460) { + stageBuiltins[EShLangMesh].append( + "in int gl_DrawID;" + "\n"); + + stageBuiltins[EShLangTask].append( + "in int gl_DrawID;" + "\n"); + } + } + + //============================================================================ + // + // Define the interface to the vertex shader. + // + //============================================================================ + + if (profile != EEsProfile) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + "attribute vec4 gl_Color;" + "attribute vec4 gl_SecondaryColor;" + "attribute vec3 gl_Normal;" + "attribute vec4 gl_Vertex;" + "attribute vec4 gl_MultiTexCoord0;" + "attribute vec4 gl_MultiTexCoord1;" + "attribute vec4 gl_MultiTexCoord2;" + "attribute vec4 gl_MultiTexCoord3;" + "attribute vec4 gl_MultiTexCoord4;" + "attribute vec4 gl_MultiTexCoord5;" + "attribute vec4 gl_MultiTexCoord6;" + "attribute vec4 gl_MultiTexCoord7;" + "attribute float gl_FogCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "in vec3 gl_Normal;" + "in vec4 gl_Vertex;" + "in vec4 gl_MultiTexCoord0;" + "in vec4 gl_MultiTexCoord1;" + "in vec4 gl_MultiTexCoord2;" + "in vec4 gl_MultiTexCoord3;" + "in vec4 gl_MultiTexCoord4;" + "in vec4 gl_MultiTexCoord5;" + "in vec4 gl_MultiTexCoord6;" + "in vec4 gl_MultiTexCoord7;" + "in float gl_FogCoord;" + "\n"); + } + + if (version < 150) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "varying vec4 gl_FrontColor;" + "varying vec4 gl_BackColor;" + "varying vec4 gl_FrontSecondaryColor;" + "varying vec4 gl_BackSecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "out vec4 gl_FrontColor;" + "out vec4 gl_BackColor;" + "out vec4 gl_FrontSecondaryColor;" + "out vec4 gl_BackSecondaryColor;" + "out vec4 gl_TexCoord[];" + "out float gl_FogFragCoord;" + "\n"); + } + stageBuiltins[EShLangVertex].append( + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + ); + + if (version == 130 || version == 140) + stageBuiltins[EShLangVertex].append( + "out float gl_ClipDistance[];" + ); + } else { + // version >= 150 + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + "float gl_ClipDistance[];" + ); + if (IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append( + "vec4 gl_ClipVertex;" // needs qualifier fixed later + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangVertex].append( + "};" + "\n"); + } + if (version >= 130 && spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_VertexID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0 && version >= 140) + stageBuiltins[EShLangVertex].append( + "in int gl_VertexIndex;" + "in int gl_InstanceIndex;" + ); + + if (spvVersion.vulkan > 0 && version >= 140 && spvVersion.vulkanRelaxed) + stageBuiltins[EShLangVertex].append( + "in int gl_VertexID;" // declare with 'in' qualifier + "in int gl_InstanceID;" + ); + + if (version >= 440) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertexARB;" + "in int gl_BaseInstanceARB;" + "in int gl_DrawIDARB;" + ); + } + if (version >= 410) { + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + ); + } + if (version >= 460) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertex;" + "in int gl_BaseInstance;" + "in int gl_DrawID;" + ); + } + + if (version >= 430) + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + ); + + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + } else { + // ES profile + if (version == 100) { + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "mediump float gl_PointSize;" // needs qualifier fixed later + "highp int gl_InstanceID;" // needs qualifier fixed later + ); + } else { + if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexID;" // needs qualifier fixed later + "in highp int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0) + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexIndex;" + "in highp int gl_InstanceIndex;" + ); + if (version < 310) + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + ); + else + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + "};" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangVertex].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangVertex].append( + "in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangVertex].append( + "out highp int gl_PrimitiveShadingRateEXT;" // GL_EXT_fragment_shading_rate + "\n"); + } + + //============================================================================ + // + // Define the interface to the geometry shader. + // + //============================================================================ + + if (profile == ECoreProfile || profile == ECompatibilityProfile) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangGeometry].append( + "} gl_in[];" + + "in int gl_PrimitiveIDIn;" + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "\n"); + if (profile == ECompatibilityProfile && version >= 400) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangGeometry].append( + "};" + + "out int gl_PrimitiveID;" + "out int gl_Layer;"); + + if (version >= 150) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportIndex;" + ); + + if (profile == ECompatibilityProfile && version < 400) + stageBuiltins[EShLangGeometry].append( + "out vec4 gl_ClipVertex;" + ); + + if (version >= 400) + stageBuiltins[EShLangGeometry].append( + "in int gl_InvocationID;" + ); + + if (version >= 430) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + ); + + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + stageBuiltins[EShLangGeometry].append("\n"); + } else if (profile == EEsProfile && version >= 310) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "} gl_in[];" + "\n" + "in highp int gl_PrimitiveIDIn;" + "in highp int gl_InvocationID;" + "\n" + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "};" + "\n" + "out highp int gl_PrimitiveID;" + "out highp int gl_Layer;" + "\n" + ); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangGeometry].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangGeometry].append( + "out highp int gl_PrimitiveShadingRateEXT;" // GL_EXT_fragment_shading_rate + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation control shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in int gl_InvocationID;" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangTessControl].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessControl].append( + "float gl_CullDistance[];" + ); + if (version >= 430) + stageBuiltins[EShLangTessControl].append( + "int gl_ViewportMask[];" // GL_NV_viewport_array2 + ); + if (version >= 450) + stageBuiltins[EShLangTessControl].append( + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out float gl_TessLevelOuter[4];" + "patch out float gl_TessLevelInner[2];" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessControl].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + } else { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp int gl_InvocationID;" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out highp float gl_TessLevelOuter[4];" + "patch out highp float gl_TessLevelInner[2];" + "patch out highp vec4 gl_BoundingBoxOES[2];" + "patch out highp vec4 gl_BoundingBoxEXT[2];" + "\n"); + if (profile == EEsProfile && version >= 320) { + stageBuiltins[EShLangTessControl].append( + "patch out highp vec4 gl_BoundingBox[2];" + "\n" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessControl].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation evaluation shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in vec3 gl_TessCoord;" + + "patch in float gl_TessLevelOuter[4];" + "patch in float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (version >= 400 && profile == ECompatibilityProfile) + stageBuiltins[EShLangTessEvaluation].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + if (version >= 430) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + ); + + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + } else if (profile == EEsProfile && version >= 310) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp vec3 gl_TessCoord;" + + "patch in highp float gl_TessLevelOuter[4];" + "patch in highp float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the fragment shader. + // + //============================================================================ + + if (profile != EEsProfile) { + + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragCoord;" // needs qualifier fixed later + "bool gl_FrontFacing;" // needs qualifier fixed later + "float gl_FragDepth;" // needs qualifier fixed later + ); + if (version >= 120) + stageBuiltins[EShLangFragment].append( + "vec2 gl_PointCoord;" // needs qualifier fixed later + ); + if (version >= 140) + stageBuiltins[EShLangFragment].append( + "out int gl_FragStencilRefARB;" + ); + if (IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && version < 420)) + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragColor;" // needs qualifier fixed later + ); + + if (version < 130) { + stageBuiltins[EShLangFragment].append( + "varying vec4 gl_Color;" + "varying vec4 gl_SecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + ); + } else { + stageBuiltins[EShLangFragment].append( + "in float gl_ClipDistance[];" + ); + + if (IncludeLegacy(version, profile, spvVersion)) { + if (version < 150) + stageBuiltins[EShLangFragment].append( + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + ); + else + stageBuiltins[EShLangFragment].append( + "in gl_PerFragment {" + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "};" + ); + } + } + + if (version >= 150) + stageBuiltins[EShLangFragment].append( + "flat in int gl_PrimitiveID;" + ); + + if (version >= 130) { // ARB_sample_shading + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleID;" + " in vec2 gl_SamplePosition;" + " out int gl_SampleMask[];" + ); + + if (spvVersion.spv == 0) { + stageBuiltins[EShLangFragment].append( + "uniform int gl_NumSamples;" + ); + } + } + + if (version >= 400) + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleMaskIn[];" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "flat in int gl_Layer;" + "flat in int gl_ViewportIndex;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in float gl_CullDistance[];" + "bool gl_HelperInvocation;" // needs qualifier fixed later + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in vec2 gl_BaryCoordNoPerspAMD;" + "in vec2 gl_BaryCoordNoPerspCentroidAMD;" + "in vec2 gl_BaryCoordNoPerspSampleAMD;" + "in vec2 gl_BaryCoordSmoothAMD;" + "in vec2 gl_BaryCoordSmoothCentroidAMD;" + "in vec2 gl_BaryCoordSmoothSampleAMD;" + "in vec3 gl_BaryCoordPullModelAMD;" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "in bool gl_FragFullyCoveredNV;" + ); + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "flat in ivec2 gl_FragmentSizeNV;" // GL_NV_shading_rate_image + "flat in int gl_InvocationsPerPixelNV;" + "in vec3 gl_BaryCoordNV;" // GL_NV_fragment_shader_barycentric + "in vec3 gl_BaryCoordNoPerspNV;" + "in vec3 gl_BaryCoordEXT;" // GL_EXT_fragment_shader_barycentric + "in vec3 gl_BaryCoordNoPerspEXT;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "flat in int gl_ShadingRateEXT;" // GL_EXT_fragment_shading_rate + ); + + } else { + // ES profile + + if (version == 100) { + stageBuiltins[EShLangFragment].append( + "mediump vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec4 gl_FragColor;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + ); + } + if (version >= 300) { + stageBuiltins[EShLangFragment].append( + "highp vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + "highp float gl_FragDepth;" // needs qualifier fixed later + ); + } + if (version >= 310) { + stageBuiltins[EShLangFragment].append( + "bool gl_HelperInvocation;" // needs qualifier fixed later + "flat in highp int gl_PrimitiveID;" // needs qualifier fixed later + "flat in highp int gl_Layer;" // needs qualifier fixed later + ); + + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "flat in lowp int gl_SampleID;" + " in mediump vec2 gl_SamplePosition;" + "flat in highp int gl_SampleMaskIn[];" + " out highp int gl_SampleMask[];" + ); + if (spvVersion.spv == 0) + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "uniform lowp int gl_NumSamples;" + ); + } + stageBuiltins[EShLangFragment].append( + "highp float gl_FragDepthEXT;" // GL_EXT_frag_depth + ); + + if (version >= 310) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( // GL_NV_shading_rate_image + "flat in ivec2 gl_FragmentSizeNV;" + "flat in int gl_InvocationsPerPixelNV;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( + "in vec3 gl_BaryCoordNV;" + "in vec3 gl_BaryCoordNoPerspNV;" + "in vec3 gl_BaryCoordEXT;" + "in vec3 gl_BaryCoordNoPerspEXT;" + ); + if (version >= 310) + stageBuiltins[EShLangFragment].append( + "flat in highp int gl_ShadingRateEXT;" // GL_EXT_fragment_shading_rate + ); + } + + stageBuiltins[EShLangFragment].append("\n"); + + if (version >= 130) + add2ndGenerationSamplingImaging(version, profile, spvVersion); + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangFragment].append( + "flat in highp int gl_DeviceIndex;" // GL_EXT_device_group + "flat in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangFragment].append( + "flat in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + const char* ballotDecls = + "uniform uint gl_SubGroupSizeARB;" + "in uint gl_SubGroupInvocationARB;" + "in uint64_t gl_SubGroupEqMaskARB;" + "in uint64_t gl_SubGroupGeMaskARB;" + "in uint64_t gl_SubGroupGtMaskARB;" + "in uint64_t gl_SubGroupLeMaskARB;" + "in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + const char* rtBallotDecls = + "uniform volatile uint gl_SubGroupSizeARB;" + "in volatile uint gl_SubGroupInvocationARB;" + "in volatile uint64_t gl_SubGroupEqMaskARB;" + "in volatile uint64_t gl_SubGroupGeMaskARB;" + "in volatile uint64_t gl_SubGroupGtMaskARB;" + "in volatile uint64_t gl_SubGroupLeMaskARB;" + "in volatile uint64_t gl_SubGroupLtMaskARB;" + "\n"; + const char* fragmentBallotDecls = + "uniform uint gl_SubGroupSizeARB;" + "flat in uint gl_SubGroupInvocationARB;" + "flat in uint64_t gl_SubGroupEqMaskARB;" + "flat in uint64_t gl_SubGroupGeMaskARB;" + "flat in uint64_t gl_SubGroupGtMaskARB;" + "flat in uint64_t gl_SubGroupLeMaskARB;" + "flat in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + stageBuiltins[EShLangVertex] .append(ballotDecls); + stageBuiltins[EShLangTessControl] .append(ballotDecls); + stageBuiltins[EShLangTessEvaluation].append(ballotDecls); + stageBuiltins[EShLangGeometry] .append(ballotDecls); + stageBuiltins[EShLangCompute] .append(ballotDecls); + stageBuiltins[EShLangFragment] .append(fragmentBallotDecls); + stageBuiltins[EShLangMesh] .append(ballotDecls); + stageBuiltins[EShLangTask] .append(ballotDecls); + stageBuiltins[EShLangRayGen] .append(rtBallotDecls); + stageBuiltins[EShLangIntersect] .append(rtBallotDecls); + // No volatile qualifier on these builtins in any-hit + stageBuiltins[EShLangAnyHit] .append(ballotDecls); + stageBuiltins[EShLangClosestHit] .append(rtBallotDecls); + stageBuiltins[EShLangMiss] .append(rtBallotDecls); + stageBuiltins[EShLangCallable] .append(rtBallotDecls); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + const char* subgroupDecls = + "in mediump uint gl_SubgroupSize;" + "in mediump uint gl_SubgroupInvocationID;" + "in highp uvec4 gl_SubgroupEqMask;" + "in highp uvec4 gl_SubgroupGeMask;" + "in highp uvec4 gl_SubgroupGtMask;" + "in highp uvec4 gl_SubgroupLeMask;" + "in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "in highp uint gl_WarpsPerSMNV;" + "in highp uint gl_SMCountNV;" + "in highp uint gl_WarpIDNV;" + "in highp uint gl_SMIDNV;" + // GL_ARM_shader_core_builtins + "in highp uint gl_CoreIDARM;" + "in highp uint gl_CoreCountARM;" + "in highp uint gl_CoreMaxIDARM;" + "in highp uint gl_WarpIDARM;" + "in highp uint gl_WarpMaxIDARM;" + "\n"; + const char* fragmentSubgroupDecls = + "flat in mediump uint gl_SubgroupSize;" + "flat in mediump uint gl_SubgroupInvocationID;" + "flat in highp uvec4 gl_SubgroupEqMask;" + "flat in highp uvec4 gl_SubgroupGeMask;" + "flat in highp uvec4 gl_SubgroupGtMask;" + "flat in highp uvec4 gl_SubgroupLeMask;" + "flat in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "flat in highp uint gl_WarpsPerSMNV;" + "flat in highp uint gl_SMCountNV;" + "flat in highp uint gl_WarpIDNV;" + "flat in highp uint gl_SMIDNV;" + // GL_ARM_shader_core_builtins + "flat in highp uint gl_CoreIDARM;" + "flat in highp uint gl_CoreCountARM;" + "flat in highp uint gl_CoreMaxIDARM;" + "flat in highp uint gl_WarpIDARM;" + "flat in highp uint gl_WarpMaxIDARM;" + "\n"; + const char* computeSubgroupDecls = + "in highp uint gl_NumSubgroups;" + "in highp uint gl_SubgroupID;" + "\n"; + // These builtins are volatile for RT stages + const char* rtSubgroupDecls = + "in mediump volatile uint gl_SubgroupSize;" + "in mediump volatile uint gl_SubgroupInvocationID;" + "in highp volatile uvec4 gl_SubgroupEqMask;" + "in highp volatile uvec4 gl_SubgroupGeMask;" + "in highp volatile uvec4 gl_SubgroupGtMask;" + "in highp volatile uvec4 gl_SubgroupLeMask;" + "in highp volatile uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "in highp uint gl_WarpsPerSMNV;" + "in highp uint gl_SMCountNV;" + "in highp volatile uint gl_WarpIDNV;" + "in highp volatile uint gl_SMIDNV;" + // GL_ARM_shader_core_builtins + "in highp uint gl_CoreIDARM;" + "in highp uint gl_CoreCountARM;" + "in highp uint gl_CoreMaxIDARM;" + "in highp uint gl_WarpIDARM;" + "in highp uint gl_WarpMaxIDARM;" + "\n"; + + stageBuiltins[EShLangVertex] .append(subgroupDecls); + stageBuiltins[EShLangTessControl] .append(subgroupDecls); + stageBuiltins[EShLangTessEvaluation].append(subgroupDecls); + stageBuiltins[EShLangGeometry] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(computeSubgroupDecls); + stageBuiltins[EShLangFragment] .append(fragmentSubgroupDecls); + stageBuiltins[EShLangMesh] .append(subgroupDecls); + stageBuiltins[EShLangMesh] .append(computeSubgroupDecls); + stageBuiltins[EShLangTask] .append(subgroupDecls); + stageBuiltins[EShLangTask] .append(computeSubgroupDecls); + stageBuiltins[EShLangRayGen] .append(rtSubgroupDecls); + stageBuiltins[EShLangIntersect] .append(rtSubgroupDecls); + // No volatile qualifier on these builtins in any-hit + stageBuiltins[EShLangAnyHit] .append(subgroupDecls); + stageBuiltins[EShLangClosestHit] .append(rtSubgroupDecls); + stageBuiltins[EShLangMiss] .append(rtSubgroupDecls); + stageBuiltins[EShLangCallable] .append(rtSubgroupDecls); + } + + // GL_NV_ray_tracing/GL_EXT_ray_tracing + if (profile != EEsProfile && version >= 460) { + + const char *constRayFlags = + "const uint gl_RayFlagsNoneNV = 0U;" + "const uint gl_RayFlagsNoneEXT = 0U;" + "const uint gl_RayFlagsOpaqueNV = 1U;" + "const uint gl_RayFlagsOpaqueEXT = 1U;" + "const uint gl_RayFlagsNoOpaqueNV = 2U;" + "const uint gl_RayFlagsNoOpaqueEXT = 2U;" + "const uint gl_RayFlagsTerminateOnFirstHitNV = 4U;" + "const uint gl_RayFlagsTerminateOnFirstHitEXT = 4U;" + "const uint gl_RayFlagsSkipClosestHitShaderNV = 8U;" + "const uint gl_RayFlagsSkipClosestHitShaderEXT = 8U;" + "const uint gl_RayFlagsCullBackFacingTrianglesNV = 16U;" + "const uint gl_RayFlagsCullBackFacingTrianglesEXT = 16U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesNV = 32U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesEXT = 32U;" + "const uint gl_RayFlagsCullOpaqueNV = 64U;" + "const uint gl_RayFlagsCullOpaqueEXT = 64U;" + "const uint gl_RayFlagsCullNoOpaqueNV = 128U;" + "const uint gl_RayFlagsCullNoOpaqueEXT = 128U;" + "const uint gl_RayFlagsSkipTrianglesEXT = 256U;" + "const uint gl_RayFlagsSkipBuiltinPrimitivesNV = 256U;" + "const uint gl_RayFlagsSkipAABBEXT = 512U;" + "const uint gl_RayFlagsForceOpacityMicromap2StateEXT = 1024U;" + "const uint gl_HitKindFrontFacingTriangleEXT = 254U;" + "const uint gl_HitKindBackFacingTriangleEXT = 255U;" + "in uint gl_HitKindFrontFacingMicroTriangleNV;" + "in uint gl_HitKindBackFacingMicroTriangleNV;" + "const int gl_ClusterIDNoneNV = -1;" + "\n"; + + const char *constRayQueryIntersection = + "const uint gl_RayQueryCandidateIntersectionEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionNoneEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionTriangleEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionGeneratedEXT = 2U;" + "const uint gl_RayQueryCandidateIntersectionTriangleEXT = 0U;" + "const uint gl_RayQueryCandidateIntersectionAABBEXT = 1U;" + "\n"; + + const char *rayGenDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + const char *intersectDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in volatile float gl_RayTmaxEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "in float gl_CurrentRayTimeNV;" + "in uint gl_CullMaskEXT;" + "\n"; + const char *hitDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in float gl_HitTNV;" + "in float gl_HitTEXT;" + "in uint gl_HitKindNV;" + "in uint gl_HitKindEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "in float gl_CurrentRayTimeNV;" + "in uint gl_CullMaskEXT;" + "in vec3 gl_HitTriangleVertexPositionsEXT[3];" + "in vec3 gl_HitMicroTriangleVertexPositionsNV[3];" + "in vec2 gl_HitMicroTriangleVertexBarycentricsNV[3];" + "in int gl_ClusterIDNV;" + "in bool gl_HitIsSphereNV;" + "in bool gl_HitIsLSSNV;" + "in vec3 gl_HitSpherePositionNV;" + "in float gl_HitSphereRadiusNV;" + "in vec3 gl_HitLSSPositionsNV[2];" + "in float gl_HitLSSRadiiNV[2];" + "\n"; + + const char *missDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayDirectionNV;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "in float gl_CurrentRayTimeNV;" + "in uint gl_CullMaskEXT;" + "\n"; + + const char *callableDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + + + commonBuiltins.append(constRayQueryIntersection); + commonBuiltins.append(constRayFlags); + + stageBuiltins[EShLangRayGen].append(rayGenDecls); + stageBuiltins[EShLangIntersect].append(intersectDecls); + stageBuiltins[EShLangAnyHit].append(hitDecls); + stageBuiltins[EShLangClosestHit].append(hitDecls); + stageBuiltins[EShLangMiss].append(missDecls); + stageBuiltins[EShLangCallable].append(callableDecls); + + } + + if ((profile != EEsProfile && version >= 140)) { + const char *deviceIndex = + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"; + + stageBuiltins[EShLangRayGen].append(deviceIndex); + stageBuiltins[EShLangIntersect].append(deviceIndex); + stageBuiltins[EShLangAnyHit].append(deviceIndex); + stageBuiltins[EShLangClosestHit].append(deviceIndex); + stageBuiltins[EShLangMiss].append(deviceIndex); + } + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append("const int gl_ScopeDevice = 1;\n"); + commonBuiltins.append("const int gl_ScopeWorkgroup = 2;\n"); + commonBuiltins.append("const int gl_ScopeSubgroup = 3;\n"); + commonBuiltins.append("const int gl_ScopeInvocation = 4;\n"); + commonBuiltins.append("const int gl_ScopeQueueFamily = 5;\n"); + commonBuiltins.append("const int gl_ScopeShaderCallEXT = 6;\n"); + + commonBuiltins.append("const int gl_SemanticsRelaxed = 0x0;\n"); + commonBuiltins.append("const int gl_SemanticsAcquire = 0x2;\n"); + commonBuiltins.append("const int gl_SemanticsRelease = 0x4;\n"); + commonBuiltins.append("const int gl_SemanticsAcquireRelease = 0x8;\n"); + commonBuiltins.append("const int gl_SemanticsMakeAvailable = 0x2000;\n"); + commonBuiltins.append("const int gl_SemanticsMakeVisible = 0x4000;\n"); + commonBuiltins.append("const int gl_SemanticsVolatile = 0x8000;\n"); + + commonBuiltins.append("const int gl_StorageSemanticsNone = 0x0;\n"); + commonBuiltins.append("const int gl_StorageSemanticsBuffer = 0x40;\n"); + commonBuiltins.append("const int gl_StorageSemanticsShared = 0x100;\n"); + commonBuiltins.append("const int gl_StorageSemanticsImage = 0x800;\n"); + commonBuiltins.append("const int gl_StorageSemanticsOutput = 0x1000;\n"); + } + + // Adding these to common built-ins triggers an assert due to a memory corruption in related code when testing + // So instead add to each stage individually, avoiding the GLSLang bug + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + for (int stage=EShLangVertex; stage(stage)].append("const highp int gl_ShadingRateFlag2VerticalPixelsEXT = 1;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag4VerticalPixelsEXT = 2;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag2HorizontalPixelsEXT = 4;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag4HorizontalPixelsEXT = 8;\n"); + } + } + + // GL_EXT_shader_image_int64 + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + + const TBasicType bTypes[] = { EbtInt64, EbtUint64 }; + for (int ms = 0; ms <= 1; ++ms) { // loop over "bool" multisample or not + for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not + for (int dim = Esd1D; dim < EsdSubpass; ++dim) { // 1D, ..., buffer + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + + if ((dim == Esd3D || dim == EsdRect || dim == EsdBuffer) && arrayed) + continue; + + if (dim != Esd2D && ms) + continue; + + // Loop over the bTypes + for (size_t bType = 0; bType < sizeof(bTypes)/sizeof(TBasicType); ++bType) { + // + // Now, make all the function prototypes for the type we just built... + // + TSampler sampler; + + sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + false, + ms ? true : false); + + TString typeName = sampler.getString(); + + addQueryFunctions(sampler, typeName, version, profile); + addImageFunctions(sampler, typeName, version, profile); + } + } + } + } + } + + // printf("%s\n", commonBuiltins.c_str()); + // printf("%s\n", stageBuiltins[EShLangFragment].c_str()); +} + +// +// Helper function for initialize(), to add the second set of names for texturing, +// when adding context-independent built-in functions. +// +void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion) +{ + // + // In this function proper, enumerate the types, then calls the next set of functions + // to enumerate all the uses for that type. + // + + // enumerate all the types + const TBasicType bTypes[] = { EbtFloat, EbtInt, EbtUint, + EbtFloat16 + }; + bool skipBuffer = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 140); + bool skipCubeArrayed = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 130); + for (int image = 0; image <= 1; ++image) // loop over "bool" image vs sampler + { + for (int shadow = 0; shadow <= 1; ++shadow) { // loop over "bool" shadow or not + for (int ms = 0; ms <= 1; ++ms) // loop over "bool" multisample or not + { + if ((ms || image) && shadow) + continue; + if (ms && profile != EEsProfile && version < 140) + continue; + if (ms && image && profile == EEsProfile) + continue; + if (ms && profile == EEsProfile && version < 310) + continue; + + for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not + for (int dim = Esd1D; dim < EsdNumDims; ++dim) { // 1D, ..., buffer, subpass + if (dim == EsdAttachmentEXT) + continue; + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim != Esd2D && dim != EsdSubpass && ms) + continue; + if (dim == EsdBuffer && skipBuffer) + continue; + if (dim == EsdBuffer && (shadow || arrayed || ms)) + continue; + if (ms && arrayed && profile == EEsProfile && version < 310) + continue; + if (dim == Esd3D && shadow) + continue; + if (dim == EsdCube && arrayed && skipCubeArrayed) + continue; + if ((dim == Esd3D || dim == EsdRect) && arrayed) + continue; + + // Loop over the bTypes + for (size_t bType = 0; bType < sizeof(bTypes)/sizeof(TBasicType); ++bType) { + if (bTypes[bType] == EbtFloat16 && (profile == EEsProfile || version < 450)) + continue; + if (dim == EsdRect && version < 140 && bType > 0) + continue; + if (shadow && (bTypes[bType] == EbtInt || bTypes[bType] == EbtUint)) + continue; + // + // Now, make all the function prototypes for the type we just built... + // + TSampler sampler; + if (dim == EsdSubpass) { + sampler.setSubpass(bTypes[bType], ms ? true : false); + } else if (dim == EsdAttachmentEXT) { + sampler.setAttachmentEXT(bTypes[bType]); + } else + if (image) { + sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } else { + sampler.set(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } + + TString typeName = sampler.getString(); + + if (dim == EsdSubpass) { + addSubpassSampling(sampler, typeName, version, profile); + continue; + } + + addQueryFunctions(sampler, typeName, version, profile); + + if (image) + addImageFunctions(sampler, typeName, version, profile); + else { + addSamplingFunctions(sampler, typeName, version, profile); + addGatherFunctions(sampler, typeName, version, profile); + if (spvVersion.vulkan > 0 && sampler.isCombined() && !sampler.shadow) { + // Base Vulkan allows texelFetch() for + // textureBuffer (i.e. without sampler). + // + // GL_EXT_samplerless_texture_functions + // allows texelFetch() and query functions + // (other than textureQueryLod()) for all + // texture types. + sampler.setTexture(sampler.type, sampler.dim, sampler.arrayed, sampler.shadow, + sampler.ms); + TString textureTypeName = sampler.getString(); + addSamplingFunctions(sampler, textureTypeName, version, profile); + addQueryFunctions(sampler, textureTypeName, version, profile); + } + } + } + } + } + } + } + } + + // + // sparseTexelsResidentARB() + // + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append("bool sparseTexelsResidentARB(int code);\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the query functions for the given type. +// +void TBuiltIns::addQueryFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + // + // textureSize() and imageSize() + // + + int sizeDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0) - (sampler.dim == EsdCube ? 1 : 0); + + if (sampler.isImage() && ((profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 420))) + return; + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + if (sizeDims == 1) + commonBuiltins.append("int"); + else { + commonBuiltins.append("ivec"); + commonBuiltins.append(postfixes[sizeDims]); + } + if (sampler.isImage()) + commonBuiltins.append(" imageSize(readonly writeonly volatile coherent nontemporal "); + else + commonBuiltins.append(" textureSize("); + commonBuiltins.append(typeName); + if (! sampler.isImage() && ! sampler.isRect() && ! sampler.isBuffer() && ! sampler.isMultiSample()) + commonBuiltins.append(",int);\n"); + else + commonBuiltins.append(");\n"); + + // + // textureSamples() and imageSamples() + // + + // GL_ARB_shader_texture_image_samples + // TODO: spec issue? there are no memory qualifiers; how to query a writeonly/readonly image, etc? + if (profile != EEsProfile && version >= 430 && sampler.isMultiSample()) { + commonBuiltins.append("int "); + if (sampler.isImage()) + commonBuiltins.append("imageSamples(readonly writeonly volatile coherent nontemporal "); + else + commonBuiltins.append("textureSamples("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } + + // + // textureQueryLod(), fragment stage only + // Also enabled with extension GL_ARB_texture_query_lod + // Extension GL_ARB_texture_query_lod says that textureQueryLOD() also exist at extension. + + if (profile != EEsProfile && version >= 150 && sampler.isCombined() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + + const TString funcName[2] = {"vec2 textureQueryLod(", "vec2 textureQueryLOD("}; + + for (int i = 0; i < 2; ++i){ + for (int f16TexAddr = 0; f16TexAddr < 2; ++f16TexAddr) { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + stageBuiltins[EShLangFragment].append(funcName[i]); + stageBuiltins[EShLangFragment].append(typeName); + if (dimMap[sampler.dim] == 1) + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", float16_t"); + else + stageBuiltins[EShLangFragment].append(", float"); + else { + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", f16vec"); + else + stageBuiltins[EShLangFragment].append(", vec"); + stageBuiltins[EShLangFragment].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangFragment].append(");\n"); + } + + stageBuiltins[EShLangCompute].append(funcName[i]); + stageBuiltins[EShLangCompute].append(typeName); + if (dimMap[sampler.dim] == 1) + stageBuiltins[EShLangCompute].append(", float"); + else { + stageBuiltins[EShLangCompute].append(", vec"); + stageBuiltins[EShLangCompute].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangCompute].append(");\n"); + } + } + + // + // textureQueryLevels() + // + + if (profile != EEsProfile && version >= 430 && ! sampler.isImage() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + commonBuiltins.append("int textureQueryLevels("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the image access functions for the given type. +// +void TBuiltIns::addImageFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + int dims = dimMap[sampler.dim]; + // most things with an array add a dimension, except for cubemaps + if (sampler.arrayed && sampler.dim != EsdCube) + ++dims; + + TString imageParams = typeName; + if (dims == 1) + imageParams.append(", int"); + else { + imageParams.append(", ivec"); + imageParams.append(postfixes[dims]); + } + if (sampler.isMultiSample()) + imageParams.append(", int"); + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoad(readonly volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStore(writeonly volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D() && ! sampler.isBuffer() && profile != EEsProfile && version >= 450) { + commonBuiltins.append("int sparseImageLoadARB(readonly volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } + + if ( profile != EEsProfile || + (profile == EEsProfile && version >= 310)) { + if (sampler.type == EbtInt || sampler.type == EbtUint || sampler.type == EbtInt64 || sampler.type == EbtUint64 ) { + + const char* dataType; + switch (sampler.type) { + case(EbtInt): dataType = "highp int"; break; + case(EbtUint): dataType = "highp uint"; break; + case(EbtInt64): dataType = "highp int64_t"; break; + case(EbtUint64): dataType = "highp uint64_t"; break; + default: dataType = ""; + } + + const int numBuiltins = 7; + + static const char* atomicFunc[numBuiltins] = { + " imageAtomicAdd(volatile coherent nontemporal ", + " imageAtomicMin(volatile coherent nontemporal ", + " imageAtomicMax(volatile coherent nontemporal ", + " imageAtomicAnd(volatile coherent nontemporal ", + " imageAtomicOr(volatile coherent nontemporal ", + " imageAtomicXor(volatile coherent nontemporal ", + " imageAtomicExchange(volatile coherent nontemporal " + }; + + // Loop twice to add prototypes with/without scope/semantics + for (int j = 0; j < 2; ++j) { + for (size_t i = 0; i < numBuiltins; ++i) { + commonBuiltins.append(dataType); + commonBuiltins.append(atomicFunc[i]); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicCompSwap(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicLoad(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("void imageAtomicStore(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", int, int, int);\n"); + + } else { + // not int or uint + // GL_ARB_ES3_1_compatibility + // TODO: spec issue: are there restrictions on the kind of layout() that can be used? what about dropping memory qualifiers? + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append("float imageAtomicExchange(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + } + + // GL_NV_shader_atomic_fp16_vector + if (profile != EEsProfile && version >= 430) { + const int numFp16Builtins = 4; + const char* atomicFp16Func[numFp16Builtins] = { + " imageAtomicAdd(volatile coherent nontemporal ", + " imageAtomicMin(volatile coherent nontemporal ", + " imageAtomicMax(volatile coherent nontemporal ", + " imageAtomicExchange(volatile coherent nontemporal " + }; + const int numFp16DataTypes = 2; + const char* atomicFp16DataTypes[numFp16DataTypes] = { + "f16vec2", + "f16vec4" + }; + // Loop twice to add prototypes with/without scope/semantics + for (int j = 0; j < numFp16DataTypes; ++j) { + for (int i = 0; i < numFp16Builtins; ++i) { + commonBuiltins.append(atomicFp16DataTypes[j]); + commonBuiltins.append(atomicFp16Func[i]); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(atomicFp16DataTypes[j]); + commonBuiltins.append(");\n"); + } + } + } + + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append("float imageAtomicAdd(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicAdd(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicExchange(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicExchange(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicLoad(readonly volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("void imageAtomicStore(writeonly volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicMin(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicMin(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicMax(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicMax(volatile coherent nontemporal "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + } + } + } + + if (sampler.dim == EsdRect || sampler.dim == EsdBuffer || sampler.shadow || sampler.isMultiSample()) + return; + + if (profile == EEsProfile || version < 450) + return; + + TString imageLodParams = typeName; + if (dims == 1) + imageLodParams.append(", int"); + else { + imageLodParams.append(", ivec"); + imageLodParams.append(postfixes[dims]); + } + imageLodParams.append(", int"); + + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoadLodAMD(readonly volatile coherent nontemporal "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStoreLodAMD(writeonly volatile coherent nontemporal "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D()) { + commonBuiltins.append("int sparseImageLoadLodAMD(readonly volatile coherent nontemporal "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for initialize(), +// when adding context-independent built-in functions. +// +// Add all the subpass access functions for the given type. +// +void TBuiltIns::addSubpassSampling(TSampler sampler, const TString& typeName, int /*version*/, EProfile /*profile*/) +{ + stageBuiltins[EShLangFragment].append(prefixes[sampler.type]); + stageBuiltins[EShLangFragment].append("vec4 subpassLoad"); + stageBuiltins[EShLangFragment].append("("); + stageBuiltins[EShLangFragment].append(typeName.c_str()); + if (sampler.isMultiSample()) + stageBuiltins[EShLangFragment].append(", int"); + stageBuiltins[EShLangFragment].append(");\n"); +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture lookup functions for the given type. +// +void TBuiltIns::addSamplingFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + // + // texturing + // + for (int proj = 0; proj <= 1; ++proj) { // loop over "bool" projective or not + + if (proj && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.arrayed || sampler.isMultiSample() + || !sampler.isCombined())) + continue; + + for (int lod = 0; lod <= 1; ++lod) { + + if (lod && (sampler.isBuffer() || sampler.isRect() || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (lod && sampler.dim == Esd2D && sampler.arrayed && sampler.shadow) + continue; + if (lod && sampler.dim == EsdCube && sampler.shadow) + continue; + + for (int bias = 0; bias <= 1; ++bias) { + + if (bias && (lod || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (bias && (sampler.dim == Esd2D || sampler.dim == EsdCube) && sampler.shadow && sampler.arrayed) + continue; + if (bias && (sampler.isRect() || sampler.isBuffer())) + continue; + + for (int offset = 0; offset <= 1; ++offset) { // loop over "bool" offset or not + + if (proj + offset + bias + lod > 3) + continue; + if (offset && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.isMultiSample())) + continue; + + for (int fetch = 0; fetch <= 1; ++fetch) { // loop over "bool" fetch or not + + if (proj + offset + fetch + bias + lod > 3) + continue; + if (fetch && (lod || bias)) + continue; + if (fetch && (sampler.shadow || sampler.dim == EsdCube)) + continue; + if (fetch == 0 && (sampler.isMultiSample() || sampler.isBuffer() + || !sampler.isCombined())) + continue; + + for (int grad = 0; grad <= 1; ++grad) { // loop over "bool" grad or not + + if (grad && (lod || bias || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (grad && sampler.isBuffer()) + continue; + if (proj + offset + fetch + grad + bias + lod > 3) + continue; + + for (int extraProj = 0; extraProj <= 1; ++extraProj) { + bool compare = false; + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + // skip dummy unused second component for 1D non-array shadows + if (sampler.shadow && totalDims < 2) + totalDims = 2; + totalDims += (sampler.shadow ? 1 : 0) + proj; + if (totalDims > 4 && sampler.shadow) { + compare = true; + totalDims = 4; + } + assert(totalDims <= 4); + + if (extraProj && ! proj) + continue; + if (extraProj && (sampler.dim == Esd3D || sampler.shadow || !sampler.isCombined())) + continue; + + // loop over 16-bit floating-point texel addressing + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) + { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + if (f16TexAddr && sampler.shadow && ! compare) { + compare = true; // compare argument is always present + totalDims--; + } + // loop over "bool" lod clamp + for (int lodClamp = 0; lodClamp <= 1 ;++lodClamp) + { + if (lodClamp && (profile == EEsProfile || version < 450)) + continue; + if (lodClamp && (proj || lod || fetch)) + continue; + + // loop over "bool" sparse or not + for (int sparse = 0; sparse <= 1; ++sparse) + { + if (sparse && (profile == EEsProfile || version < 450)) + continue; + // Sparse sampling is not for 1D/1D array texture, buffer texture, and + // projective texture + if (sparse && (sampler.is1D() || sampler.isBuffer() || proj)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t "); + else + s.append("float "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + } + + // name + if (sparse) { + if (fetch) + s.append("sparseTexel"); + else + s.append("sparseTexture"); + } + else { + if (fetch) + s.append("texel"); + else + s.append("texture"); + } + if (proj) + s.append("Proj"); + if (lod) + s.append("Lod"); + if (grad) + s.append("Grad"); + if (fetch) + s.append("Fetch"); + if (offset) + s.append("Offset"); + if (lodClamp) + s.append("Clamp"); + if (lodClamp != 0 || sparse) + s.append("ARB"); + s.append("("); + + // sampler type + s.append(typeName); + // P coordinate + if (extraProj) { + if (f16TexAddr) + s.append(",f16vec4"); + else + s.append(",vec4"); + } else { + s.append(","); + TBasicType t = fetch ? EbtInt : (f16TexAddr ? EbtFloat16 : EbtFloat); + if (totalDims == 1) + s.append(TType::getBasicString(t)); + else { + s.append(prefixes[t]); + s.append("vec"); + s.append(postfixes[totalDims]); + } + } + // non-optional compare + if (compare) + s.append(",float"); + + // non-optional lod argument (lod that's not driven by lod loop) or sample + if ((fetch && !sampler.isBuffer() && + !sampler.isRect() && !sampler.isMultiSample()) + || (sampler.isMultiSample() && fetch)) + s.append(",int"); + // non-optional lod + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // gradient arguments + if (grad) { + if (dimMap[sampler.dim] == 1) { + if (f16TexAddr) + s.append(",float16_t,float16_t"); + else + s.append(",float,float"); + } else { + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + // offset + if (offset) { + if (dimMap[sampler.dim] == 1) + s.append(",int"); + else { + s.append(",ivec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + + // lod clamp + if (lodClamp) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t"); + else + s.append("float"); + else { + s.append(prefixes[sampler.type]); + s.append("vec4"); + } + } + // optional bias + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + s.append(");\n"); + + // Add to the per-language set of built-ins + if (!grad && (bias || lodClamp != 0)) { + stageBuiltins[EShLangFragment].append(s); + stageBuiltins[EShLangCompute].append(s); + } else + commonBuiltins.append(s); + + } + } + } + } + } + } + } + } + } + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture gather functions for the given type. +// +void TBuiltIns::addGatherFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + switch (sampler.dim) { + case Esd2D: + case EsdRect: + case EsdCube: + break; + default: + return; + } + + if (sampler.isMultiSample()) + return; + + if (version < 140 && sampler.dim == EsdRect && sampler.type != EbtFloat) + return; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp > 0 && sampler.shadow) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + if (sparse) + s.append("ARB"); + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // refZ argument + if (sampler.shadow) + s.append(",float"); + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + s.append(");\n"); + commonBuiltins.append(s); + } + } + } + } + + if (sampler.dim == EsdRect || sampler.shadow) + return; + + if (profile == EEsProfile || version < 450) + return; + + for (int bias = 0; bias < 2; ++bias) { // loop over presence of bias argument + + for (int lod = 0; lod < 2; ++lod) { // loop over presence of lod argument + + if ((lod && bias) || (lod == 0 && bias == 0)) + continue; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp == 0 && bias) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + + if (lod) + s.append("Lod"); + + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + + if (lod) + s.append("AMD"); + else if (sparse) + s.append("ARB"); + + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // lod argument + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + // bias argument + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + s.append(");\n"); + if (bias) + stageBuiltins[EShLangFragment].append(s); + else + commonBuiltins.append(s); + } + } + } + } + } + } +} + +// +// Add context-dependent built-in functions and variables that are present +// for the given version and profile. All the results are put into just the +// commonBuiltins, because it is called for just a specific stage. So, +// add stage-specific entries to the commonBuiltins, and only if that stage +// was requested. +// +void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language) +{ + // + // Initialize the context-dependent (resource-dependent) built-in strings for parsing. + // + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + TString& s = commonBuiltins; + const int maxSize = 200; + char builtInConstant[maxSize]; + + // + // Build string of implementation dependent constants. + // + + if (profile == EEsProfile) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + if (version == 100) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors); + s.append(builtInConstant); + } else { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexOutputVectors = %d;", resources.maxVertexOutputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentInputVectors = %d;", resources.maxFragmentInputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + + if (version >= 310) { + // geometry + + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources.maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources.maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "highp vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "highp vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 320) { + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources.maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources.maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources.maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources.maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + } + + if (version >= 100) { + // GL_EXT_blend_func_extended + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDualSourceDrawBuffersEXT = %d;", resources.maxDualSourceDrawBuffersEXT); + s.append(builtInConstant); + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxDualSourceDrawBuffersEXT + if (language == EShLangFragment) { + s.append( + "mediump vec4 gl_SecondaryFragColorEXT;" + "mediump vec4 gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT];" + "\n"); + } + } + } else { + // non-ES profile + + if (version > 400) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxLights = %d;", resources.maxLights); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxClipPlanes = %d;", resources.maxClipPlanes); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureUnits = %d;", resources.maxTextureUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureCoords = %d;", resources.maxTextureCoords); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformComponents = %d;", resources.maxVertexUniformComponents); + s.append(builtInConstant); + + // Moved from just being deprecated into compatibility profile only as of 4.20 + if (version < 420 || profile == ECompatibilityProfile) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingFloats = %d;", resources.maxVaryingFloats); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformComponents = %d;", resources.maxFragmentUniformComponents); + s.append(builtInConstant); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // OpenGL'uniform' state. Page numbers are in reference to version + // 1.4 of the OpenGL specification. + // + + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + s.append("uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];" + + // + // Clip planes p. 42. + // + "uniform vec4 gl_ClipPlane[gl_MaxClipPlanes];" + + // + // Light State p 50, 53, 55. + // + "uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];" + + // + // Derived state from products of light. + // + "uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];" + "uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];" + + // + // Texture Environment and Generation, p. 152, p. 40-42. + // + "uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];" + "uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneQ[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneQ[gl_MaxTextureCoords];"); + } + + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxClipDistances = %d;", resources.maxClipDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingComponents = %d;", resources.maxVaryingComponents); + s.append(builtInConstant); + + // GL_ARB_shading_language_420pack + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + + // geometry + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryVaryingComponents = %d;", resources.maxGeometryVaryingComponents); + s.append(builtInConstant); + + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexOutputComponents = %d;", resources.maxVertexOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentInputComponents = %d;", resources.maxFragmentInputComponents); + s.append(builtInConstant); + } + + // tessellation + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + s.append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (profile != EEsProfile && version >= 450) + s.append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + s.append( + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxViewports = %d;", resources.maxViewports); + s.append(builtInConstant); + } + + // images + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUnitsAndFragmentOutputs = %d;", resources.maxCombinedImageUnitsAndFragmentOutputs); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxImageSamples = %d;", resources.maxImageSamples); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + } + + // enhanced layouts + if (version >= 430) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackBuffers = %d;", resources.maxTransformFeedbackBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackInterleavedComponents = %d;", resources.maxTransformFeedbackInterleavedComponents); + s.append(builtInConstant); + } + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupCount = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupCountX, + resources.maxComputeWorkGroupCountY, + resources.maxComputeWorkGroupCountZ); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupSize = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupSizeX, + resources.maxComputeWorkGroupSizeY, + resources.maxComputeWorkGroupSizeZ); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeUniformComponents = %d;", resources.maxComputeUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeTextureImageUnits = %d;", resources.maxComputeTextureImageUnits); + s.append(builtInConstant); + + s.append("\n"); + } + + // images (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 130)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxImageUnits = %d;", resources.maxImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedShaderOutputResources = %d;", resources.maxCombinedShaderOutputResources); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexImageUniforms = %d;", resources.maxVertexImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentImageUniforms = %d;", resources.maxFragmentImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUniforms = %d;", resources.maxCombinedImageUniforms); + s.append(builtInConstant); + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeImageUniforms = %d;", resources.maxComputeImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounters = %d;", resources.maxComputeAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounterBuffers = %d;", resources.maxComputeAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } + + // atomic counters (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounters = %d;", resources. maxVertexAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounters = %d;", resources. maxFragmentAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounters = %d;", resources. maxCombinedAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBindings = %d;", resources. maxAtomicCounterBindings); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounterBuffers = %d;", resources. maxVertexAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounterBuffers = %d;", resources. maxFragmentAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounterBuffers = %d;", resources. maxCombinedAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBufferSize = %d;", resources. maxAtomicCounterBufferSize); + s.append(builtInConstant); + } + if (profile != EEsProfile && version >= 420) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources. maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources. maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources. maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources. maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources. maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources. maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } + + // GL_ARB_cull_distance + if (profile != EEsProfile && version >= 450) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCullDistances = %d;", resources.maxCullDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedClipAndCullDistances = %d;", resources.maxCombinedClipAndCullDistances); + s.append(builtInConstant); + } + + // GL_ARB_ES3_1_compatibility + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxSamples = %d;", resources.maxSamples); + s.append(builtInConstant); + } + + // SPV_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputVerticesNV = %d;", resources.maxMeshOutputVerticesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputPrimitivesNV = %d;", resources.maxMeshOutputPrimitivesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxMeshWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxMeshWorkGroupSizeX_NV, + resources.maxMeshWorkGroupSizeY_NV, + resources.maxMeshWorkGroupSizeZ_NV); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxTaskWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxTaskWorkGroupSizeX_NV, + resources.maxTaskWorkGroupSizeY_NV, + resources.maxTaskWorkGroupSizeZ_NV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshViewCountNV = %d;", resources.maxMeshViewCountNV); + s.append(builtInConstant); + + s.append("\n"); + } + + s.append("\n"); +} + +// +// To support special built-ins that have a special qualifier that cannot be declared textually +// in a shader, like gl_Position. +// +// This lets the type of the built-in be declared textually, and then have just its qualifier be +// updated afterward. +// +// Safe to call even if name is not present. +// +// Only use this for built-in variables that have a special qualifier in TStorageQualifier. +// New built-in variables should use a generic (textually declarable) qualifier in +// TStoraregQualifier and only call BuiltInVariable(). +// +static void SpecialQualifier(const char* name, TStorageQualifier qualifier, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.storage = qualifier; + symQualifier.builtIn = builtIn; +} + +// +// Modify the symbol's flat decoration. +// +// Safe to call even if name is not present. +// +// Originally written to transform gl_SubGroupSizeARB from uniform to fragment input in Vulkan. +// +static void ModifyFlatDecoration(const char* name, bool flat, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.flat = flat; +} + +// +// To tag built-in variables with their TBuiltInVariable enum. Use this when the +// normal declaration text already gets the qualifier right, and all that's needed +// is setting the builtIn field. This should be the normal way for all new +// built-in variables. +// +// If SpecialQualifier() was called, this does not need to be called. +// +// Safe to call even if name is not present. +// +static void BuiltInVariable(const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.builtIn = builtIn; +} + +static void RetargetVariable(const char* from, const char* to, TSymbolTable& symbolTable) +{ + symbolTable.retargetSymbol(from, to); +} + +// +// For built-in variables inside a named block. +// SpecialQualifier() won't ever go inside a block; their member's qualifier come +// from the qualification of the block. +// +// See comments above for other detail. +// +static void BuiltInVariable(const char* blockName, const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(blockName); + if (symbol == nullptr) + return; + + TTypeList& structure = *symbol->getWritableType().getWritableStruct(); + for (int i = 0; i < (int)structure.size(); ++i) { + if (structure[i].type->getFieldName().compare(name) == 0) { + structure[i].type->getQualifier().builtIn = builtIn; + return; + } + } +} + +// +// Finish adding/processing context-independent built-in symbols. +// 1) Programmatically add symbols that could not be added by simple text strings above. +// 2) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 3) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) +{ + // + // Tag built-in variables and functions with additional qualifier and extension information + // that cannot be declared with the text strings. + // + + // N.B.: a symbol should only be tagged once, and this function is called multiple times, once + // per stage that's used for this profile. So + // - generally, stick common ones in the fragment stage to ensure they are tagged exactly once + // - for ES, which has different precisions for different stages, the coarsest-grained tagging + // for a built-in used in many stages needs to be once for the fragment stage and once for + // the vertex stage + + switch(language) { + case EShLangVertex: + if (spvVersion.vulkan > 0) { + BuiltInVariable("gl_VertexIndex", EbvVertexIndex, symbolTable); + BuiltInVariable("gl_InstanceIndex", EbvInstanceIndex, symbolTable); + } + + if (spvVersion.vulkan == 0) { + SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable); + SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable); + if (version < 140) + symbolTable.setVariableExtensions("gl_InstanceID", 1, &E_GL_EXT_draw_instanced); + } + + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // treat these built-ins as aliases of VertexIndex and InstanceIndex + RetargetVariable("gl_InstanceID", "gl_InstanceIndex", symbolTable); + RetargetVariable("gl_VertexID", "gl_VertexIndex", symbolTable); + } + + if (profile != EEsProfile) { + if (version >= 440) { + symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_BaseInstanceARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_BaseVertexARB", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstanceARB", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + } + if (version >= 460) { + BuiltInVariable("gl_BaseVertex", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstance", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + symbolTable.setFunctionExtensions("ballotARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readFirstInvocationARB", 1, &E_GL_ARB_shader_ballot); + + if (version >= 430) { + symbolTable.setFunctionExtensions("anyInvocationARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsEqualARB", 1, &E_GL_ARB_shader_group_vote); + } + } + + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("minInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsWithPatternAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("writeInvocationAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("mbcntAMD", 1, &E_GL_AMD_shader_ballot); + + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("min3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("max3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("mid3", 1, &E_GL_AMD_shader_trinary_minmax); + } + + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SIMDGroupSizeAMD", 1, &E_GL_AMD_gcn_shader); + SpecialQualifier("gl_SIMDGroupSizeAMD", EvqVaryingIn, EbvSubGroupSize, symbolTable); + + symbolTable.setFunctionExtensions("cubeFaceIndexAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("cubeFaceCoordAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("timeAMD", 1, &E_GL_AMD_gcn_shader); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("fragmentMaskFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + symbolTable.setFunctionExtensions("fragmentFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + } + + symbolTable.setFunctionExtensions("countLeadingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("countTrailingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("absoluteDifference", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("addSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("subtractSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("average", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("averageRounded", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("multiply32x16", 1, &E_GL_INTEL_shader_integer_functions2); + + symbolTable.setFunctionExtensions("textureFootprintNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintClampNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintLodNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradClampNV", 1, &E_GL_NV_shader_texture_footprint); + // Compatibility variables, vertex only + if (spvVersion.spv == 0) { + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + BuiltInVariable("gl_Normal", EbvNormal, symbolTable); + BuiltInVariable("gl_Vertex", EbvVertex, symbolTable); + BuiltInVariable("gl_MultiTexCoord0", EbvMultiTexCoord0, symbolTable); + BuiltInVariable("gl_MultiTexCoord1", EbvMultiTexCoord1, symbolTable); + BuiltInVariable("gl_MultiTexCoord2", EbvMultiTexCoord2, symbolTable); + BuiltInVariable("gl_MultiTexCoord3", EbvMultiTexCoord3, symbolTable); + BuiltInVariable("gl_MultiTexCoord4", EbvMultiTexCoord4, symbolTable); + BuiltInVariable("gl_MultiTexCoord5", EbvMultiTexCoord5, symbolTable); + BuiltInVariable("gl_MultiTexCoord6", EbvMultiTexCoord6, symbolTable); + BuiltInVariable("gl_MultiTexCoord7", EbvMultiTexCoord7, symbolTable); + BuiltInVariable("gl_FogCoord", EbvFogFragCoord, symbolTable); + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version == 310) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 310) + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + + // E_GL_EXT_texture_array + if (profile != EEsProfile && spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DArray", 1, &E_GL_EXT_texture_array); + symbolTable.setFunctionExtensions("texture2DArray", 1, &E_GL_EXT_texture_array); + symbolTable.setFunctionExtensions("shadow1DArray", 1, &E_GL_EXT_texture_array); + symbolTable.setFunctionExtensions("shadow2DArray", 1, &E_GL_EXT_texture_array); + + symbolTable.setFunctionExtensions("texture1DArrayLod", 1, &E_GL_EXT_texture_array); + symbolTable.setFunctionExtensions("texture2DArrayLod", 1, &E_GL_EXT_texture_array); + symbolTable.setFunctionExtensions("shadow1DArrayLod", 1, &E_GL_EXT_texture_array); + } + [[fallthrough]]; + + case EShLangTessControl: + if (profile == EEsProfile && version >= 310) { + BuiltInVariable("gl_BoundingBoxEXT", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxEXT", 1, + &E_GL_EXT_primitive_bounding_box); + BuiltInVariable("gl_BoundingBoxOES", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxOES", 1, + &E_GL_OES_primitive_bounding_box); + + if (version >= 320) { + BuiltInVariable("gl_BoundingBox", EbvBoundingBox, symbolTable); + } + } + [[fallthrough]]; + + case EShLangTessEvaluation: + case EShLangGeometry: + SpecialQualifier("gl_Position", EvqPosition, EbvPosition, symbolTable); + SpecialQualifier("gl_PointSize", EvqPointSize, EbvPointSize, symbolTable); + + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + + BuiltInVariable("gl_out", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_out", "gl_PointSize", EbvPointSize, symbolTable); + + SpecialQualifier("gl_ClipVertex", EvqClipVertex, EbvClipVertex, symbolTable); + + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_out", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveIDIn", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InvocationID", EbvInvocationId, symbolTable); + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + if (language != EShLangGeometry) { + symbolTable.setVariableExtensions("gl_Layer", Num_viewportEXTs, viewportEXTs); + symbolTable.setVariableExtensions("gl_ViewportIndex", Num_viewportEXTs, viewportEXTs); + } + symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + if (language == EShLangVertex || language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + } + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_out", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable); + BuiltInVariable("gl_TessLevelOuter", EbvTessLevelOuter, symbolTable); + BuiltInVariable("gl_TessLevelInner", EbvTessLevelInner, symbolTable); + BuiltInVariable("gl_TessCoord", EbvTessCoord, symbolTable); + + if (version < 410) + symbolTable.setVariableExtensions("gl_ViewportIndex", 1, &E_GL_ARB_viewport_array); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_out", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_out", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_out", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + // gl_PointSize, when it needs to be tied to an extension, is always a member of a block. + // (Sometimes with an instance name, sometimes anonymous). + if (profile == EEsProfile) { + if (language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + } else if (language == EShLangTessEvaluation || language == EShLangTessControl) { + // gl_in tessellation settings of gl_PointSize are in the context-dependent paths + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + symbolTable.setVariableExtensions("gl_out", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + } + + if (profile != EEsProfile) { + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + + if (language == EShLangGeometry || language == EShLangVertex) { + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_PrimitiveShadingRateEXT", 1, &E_GL_EXT_fragment_shading_rate); + BuiltInVariable("gl_PrimitiveShadingRateEXT", EbvPrimitiveShadingRateKHR, symbolTable); + + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + } + break; + + case EShLangFragment: + SpecialQualifier("gl_FrontFacing", EvqFace, EbvFace, symbolTable); + SpecialQualifier("gl_FragCoord", EvqFragCoord, EbvFragCoord, symbolTable); + SpecialQualifier("gl_PointCoord", EvqPointCoord, EbvPointCoord, symbolTable); + if (spvVersion.spv == 0) + SpecialQualifier("gl_FragColor", EvqFragColor, EbvFragColor, symbolTable); + else { + TSymbol* symbol = symbolTable.find("gl_FragColor"); + if (symbol) { + symbol->getWritableType().getQualifier().storage = EvqVaryingOut; + symbol->getWritableType().getQualifier().layoutLocation = 0; + } + } + SpecialQualifier("gl_FragDepth", EvqFragDepth, EbvFragDepth, symbolTable); + SpecialQualifier("gl_FragDepthEXT", EvqFragDepth, EbvFragDepth, symbolTable); + SpecialQualifier("gl_FragStencilRefARB", EvqFragStencil, EbvFragStencilRef, symbolTable); + SpecialQualifier("gl_HelperInvocation", EvqVaryingIn, EbvHelperInvocation, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + + if (profile != EEsProfile && version >= 140) { + symbolTable.setVariableExtensions("gl_FragStencilRefARB", 1, &E_GL_ARB_shader_stencil_export); + BuiltInVariable("gl_FragStencilRefARB", EbvFragStencilRef, symbolTable); + } + + if (profile != EEsProfile && version < 400) { + symbolTable.setFunctionExtensions("textureQueryLOD", 1, &E_GL_ARB_texture_query_lod); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.setFunctionExtensions("rayQueryInitializeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryTerminateEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGenerateIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryConfirmIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryProceedEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTypeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayFlagsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayTMinEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceCustomIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceIdEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionGeometryIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionPrimitiveIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionBarycentricsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionFrontFaceEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionCandidateAABBOpaqueEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectToWorldEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionWorldToObjectEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTriangleVertexPositionsEXT", 1, &E_GL_EXT_ray_tracing_position_fetch); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionClusterIdNV", 1, &E_GL_NV_cluster_acceleration_structure); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionSpherePositionNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionSphereRadiusNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionLSSHitValueNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionLSSPositionsNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionLSSRadiiNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryIsSphereHitNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("rayQueryIsLSSHitNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_RayFlagsSkipAABBEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + symbolTable.setVariableExtensions("gl_RayFlagsSkipTrianglesEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + symbolTable.setVariableExtensions("gl_RayFlagsForceOpacityMicromap2StateEXT", 1, &E_GL_EXT_opacity_micromap); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 310)) { + BuiltInVariable("gl_SampleID", EbvSampleId, symbolTable); + BuiltInVariable("gl_SamplePosition", EbvSamplePosition, symbolTable); + BuiltInVariable("gl_SampleMask", EbvSampleMask, symbolTable); + + if (profile != EEsProfile && version < 400) { + BuiltInVariable("gl_NumSamples", EbvSampleMask, symbolTable); + + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_ARB_sample_shading); + } else { + BuiltInVariable("gl_SampleMaskIn", EbvSampleMask, symbolTable); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMaskIn", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_OES_sample_variables); + } + } + } + + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_in", "gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + // built-in functions + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version < 320) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 100) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_OES_standard_derivatives); + } + if (version == 310) { + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + symbolTable.setFunctionExtensions("interpolateAtCentroid", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtSample", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtOffset", 1, &E_GL_OES_shader_multisample_interpolation); + } + } else if (version < 130) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + } + } + + // E_GL_ARB_shader_texture_lod functions usable only with the extension enabled + if (profile != EEsProfile && spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + } + + // E_GL_ARB_shader_image_load_store + if (profile != EEsProfile && version < 420) + symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store); + // All the image access functions are protected by checks on the type of the first argument. + + // E_GL_ARB_shader_atomic_counters + if (profile != EEsProfile && version < 420) { + symbolTable.setFunctionExtensions("atomicCounterIncrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounterDecrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounter" , 1, &E_GL_ARB_shader_atomic_counters); + } + + // E_GL_ARB_shader_atomic_counter_ops + if (profile != EEsProfile && version == 450) { + symbolTable.setFunctionExtensions("atomicCounterAddARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterSubtractARB", 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterMinARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterMaxARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterAndARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterOrARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterXorARB" , 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterExchangeARB", 1, &E_GL_ARB_shader_atomic_counter_ops); + symbolTable.setFunctionExtensions("atomicCounterCompSwapARB", 1, &E_GL_ARB_shader_atomic_counter_ops); + } + + // E_GL_ARB_derivative_control + if (profile != EEsProfile && version < 450) { + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_ARB_derivative_control); + } + + // E_GL_ARB_sparse_texture2 + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetsARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseImageLoadARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelsResident", 1, &E_GL_ARB_sparse_texture2); + } + + // E_GL_ARB_sparse_texture_clamp + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + } + + // E_GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordPullModelAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + symbolTable.setFunctionExtensions("interpolateAtVertexAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + BuiltInVariable("gl_BaryCoordNoPerspAMD", EbvBaryCoordNoPersp, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspCentroidAMD", EbvBaryCoordNoPerspCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspSampleAMD", EbvBaryCoordNoPerspSample, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothAMD", EbvBaryCoordSmooth, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothCentroidAMD", EbvBaryCoordSmoothCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothSampleAMD", EbvBaryCoordSmoothSample, symbolTable); + BuiltInVariable("gl_BaryCoordPullModelAMD", EbvBaryCoordPullModel, symbolTable); + } + + // E_GL_AMD_texture_gather_bias_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("textureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + } + + // E_GL_AMD_shader_image_load_store_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("imageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("imageStoreLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("sparseImageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + } + if (profile != EEsProfile && version >= 430) { + symbolTable.setVariableExtensions("gl_FragFullyCoveredNV", 1, &E_GL_NV_conservative_raster_underestimation); + BuiltInVariable("gl_FragFullyCoveredNV", EbvFragFullyCoveredNV, symbolTable); + } + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_FragmentSizeNV", 1, &E_GL_NV_shading_rate_image); + symbolTable.setVariableExtensions("gl_InvocationsPerPixelNV", 1, &E_GL_NV_shading_rate_image); + BuiltInVariable("gl_FragmentSizeNV", EbvFragmentSizeNV, symbolTable); + BuiltInVariable("gl_InvocationsPerPixelNV", EbvInvocationsPerPixelNV, symbolTable); + symbolTable.setVariableExtensions("gl_BaryCoordNV", 1, &E_GL_NV_fragment_shader_barycentric); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspNV", 1, &E_GL_NV_fragment_shader_barycentric); + BuiltInVariable("gl_BaryCoordNV", EbvBaryCoordNV, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspNV", EbvBaryCoordNoPerspNV, symbolTable); + symbolTable.setVariableExtensions("gl_BaryCoordEXT", 1, &E_GL_EXT_fragment_shader_barycentric); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspEXT", 1, &E_GL_EXT_fragment_shader_barycentric); + BuiltInVariable("gl_BaryCoordEXT", EbvBaryCoordEXT, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspEXT", EbvBaryCoordNoPerspEXT, symbolTable); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_FragSizeEXT", 1, &E_GL_EXT_fragment_invocation_density); + symbolTable.setVariableExtensions("gl_FragInvocationCountEXT", 1, &E_GL_EXT_fragment_invocation_density); + BuiltInVariable("gl_FragSizeEXT", EbvFragSizeEXT, symbolTable); + BuiltInVariable("gl_FragInvocationCountEXT", EbvFragInvocationCountEXT, symbolTable); + } + + symbolTable.setVariableExtensions("gl_FragDepthEXT", 1, &E_GL_EXT_frag_depth); + + symbolTable.setFunctionExtensions("clockARB", 1, &E_GL_ARB_shader_clock); + symbolTable.setFunctionExtensions("clock2x32ARB", 1, &E_GL_ARB_shader_clock); + + symbolTable.setFunctionExtensions("clockRealtimeEXT", 1, &E_GL_EXT_shader_realtime_clock); + symbolTable.setFunctionExtensions("clockRealtime2x32EXT", 1, &E_GL_EXT_shader_realtime_clock); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_PrimitiveID", Num_AEP_geometry_shader, AEP_geometry_shader); + symbolTable.setVariableExtensions("gl_Layer", Num_AEP_geometry_shader, AEP_geometry_shader); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (profile != EEsProfile && version < 330 ) { + const char* bitsConvertExt[2] = {E_GL_ARB_shader_bit_encoding, E_GL_ARB_gpu_shader5}; + symbolTable.setFunctionExtensions("floatBitsToInt", 2, bitsConvertExt); + symbolTable.setFunctionExtensions("floatBitsToUint", 2, bitsConvertExt); + symbolTable.setFunctionExtensions("intBitsToFloat", 2, bitsConvertExt); + symbolTable.setFunctionExtensions("uintBitsToFloat", 2, bitsConvertExt); + } + + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("imageSize", 1, &E_GL_ARB_shader_image_size); + } + + // GL_ARB_shader_storage_buffer_object + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("atomicAdd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMin", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMax", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicAnd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicOr", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicXor", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicExchange", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicCompSwap", 1, &E_GL_ARB_shader_storage_buffer_object); + } + + // GL_ARB_shading_language_packing + if (profile != EEsProfile && version < 400 ) { + symbolTable.setFunctionExtensions("packUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + } + if (profile != EEsProfile && version < 420 ) { + symbolTable.setFunctionExtensions("packSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackHalf2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packHalf2x16", 1, &E_GL_ARB_shading_language_packing); + } + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_EXT_expect_assume + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setFunctionExtensions("assumeEXT", 1, &E_GL_EXT_expect_assume); + symbolTable.setFunctionExtensions("expectEXT", 1, &E_GL_EXT_expect_assume); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierBuffer", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierImage", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupElect", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupAll", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAny", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAllEqual", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupBroadcast", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBroadcastFirst", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupInverseBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitExtract", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotInclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotExclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindLSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindMSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupShuffle", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleXor", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleUp", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupShuffleDown", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupRotate", 1, &E_GL_KHR_shader_subgroup_rotate); + symbolTable.setFunctionExtensions("subgroupClusteredRotate", 1, &E_GL_KHR_shader_subgroup_rotate); + symbolTable.setFunctionExtensions("subgroupAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupClusteredAdd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMul", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMin", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMax", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredAnd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredOr", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredXor", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupQuadBroadcast", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapHorizontal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapVertical", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapDiagonal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupPartitionNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + + if (spvVersion.vulkan > 0) { + symbolTable.setVariableExtensions("gl_ScopeDevice", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeWorkgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeSubgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeInvocation", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_SemanticsRelaxed", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquire", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquireRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeAvailable", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeVisible", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsVolatile", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_StorageSemanticsNone", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsBuffer", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsShared", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsImage", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsOutput", 1, &E_GL_KHR_memory_scope_semantics); + } + + symbolTable.setFunctionExtensions("helperInvocationEXT", 1, &E_GL_EXT_demote_to_helper_invocation); + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateEXT", 1, &E_GL_EXT_fragment_shading_rate); + BuiltInVariable("gl_ShadingRateEXT", EbvShadingRateKHR, symbolTable); + + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + + // GL_EXT_shader_quad_control + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setFunctionExtensions("subgroupQuadAll", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupQuadAny", 1, &E_GL_KHR_shader_subgroup_vote); + } + + // GL_EXT_shader_tile_image + symbolTable.setFunctionExtensions("stencilAttachmentReadEXT", 1, &E_GL_EXT_shader_tile_image); + symbolTable.setFunctionExtensions("depthAttachmentReadEXT", 1, &E_GL_EXT_shader_tile_image); + symbolTable.setFunctionExtensions("colorAttachmentReadEXT", 1, &E_GL_EXT_shader_tile_image); + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + + symbolTable.setFunctionExtensions("textureWeightedQCOM", 1, &E_GL_QCOM_image_processing); + symbolTable.setFunctionExtensions("textureBoxFilterQCOM", 1, &E_GL_QCOM_image_processing); + symbolTable.setFunctionExtensions("textureBlockMatchSADQCOM", 1, &E_GL_QCOM_image_processing); + symbolTable.setFunctionExtensions("textureBlockMatchSSDQCOM", 1, &E_GL_QCOM_image_processing); + + symbolTable.setFunctionExtensions("textureBlockMatchWindowSSDQCOM", 1, &E_GL_QCOM_image_processing2); + symbolTable.setFunctionExtensions("textureBlockMatchWindowSADQCOM", 1, &E_GL_QCOM_image_processing2); + symbolTable.setFunctionExtensions("textureBlockMatchGatherSSDQCOM", 1, &E_GL_QCOM_image_processing2); + symbolTable.setFunctionExtensions("textureBlockMatchGatherSADQCOM", 1, &E_GL_QCOM_image_processing2); + } + break; + + case EShLangCompute: + BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + } + + if (profile != EEsProfile && version < 430) { + symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_ARB_compute_shader); + + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupCount", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeUniformComponents", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeTextureImageUnits", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeImageUniforms", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounters", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounterBuffers", 1, &E_GL_ARB_compute_shader); + + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierAtomicCounter", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierBuffer", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierImage", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_ARB_compute_shader); + } + + + symbolTable.setFunctionExtensions("controlBarrier", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setFunctionExtensions("debugPrintfEXT", 1, &E_GL_EXT_debug_printf); + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + } + + { + const char *coopExt[2] = { E_GL_NV_cooperative_matrix, E_GL_NV_integer_cooperative_matrix }; + symbolTable.setFunctionExtensions("coopMatLoadNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatStoreNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatMulAddNV", 2, coopExt); + } + + { + symbolTable.setFunctionExtensions("coopMatLoad", 1, &E_GL_KHR_cooperative_matrix); + symbolTable.setFunctionExtensions("coopMatStore", 1, &E_GL_KHR_cooperative_matrix); + symbolTable.setFunctionExtensions("coopMatMulAdd", 1, &E_GL_KHR_cooperative_matrix); + + symbolTable.setFunctionExtensions("coopMatReduceNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("coopMatPerElementNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("coopMatTransposeNV", 1, &E_GL_NV_cooperative_matrix2); + + symbolTable.setFunctionExtensions("createTensorLayoutNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorLayoutBlockSizeNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorLayoutDimensionNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorLayoutStrideNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("sliceTensorLayoutNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorLayoutClampValueNV", 1, &E_GL_NV_cooperative_matrix2); + + symbolTable.setFunctionExtensions("createTensorViewNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorViewDimensionsNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorViewStrideNV", 1, &E_GL_NV_cooperative_matrix2); + symbolTable.setFunctionExtensions("setTensorViewClipNV", 1, &E_GL_NV_cooperative_matrix2); + } + + { + symbolTable.setFunctionExtensions("coopVecMatMulNV", 1, &E_GL_NV_cooperative_vector); + symbolTable.setFunctionExtensions("coopVecMatMulAddNV", 1, &E_GL_NV_cooperative_vector); + symbolTable.setFunctionExtensions("coopVecOuterProductAccumulateNV", 1, &E_GL_NV_cooperative_vector); + symbolTable.setFunctionExtensions("coopVecReduceSumAccumulateNV", 1, &E_GL_NV_cooperative_vector); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_NV_compute_shader_derivatives); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + + if ((profile != EEsProfile && version >= 460)) { + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexPositionNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexBarycentricNV", 1, &E_GL_NV_displacement_micromap); + } + + // GL_EXT_integer_dot_product + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setFunctionExtensions("dotEXT", 1, &E_GL_EXT_integer_dot_product); + symbolTable.setFunctionExtensions("dotPacked4x8EXT", 1, &E_GL_EXT_integer_dot_product); + symbolTable.setFunctionExtensions("dotAccSatEXT", 1, &E_GL_EXT_integer_dot_product); + symbolTable.setFunctionExtensions("dotPacked4x8AccSatEXT", 1, &E_GL_EXT_integer_dot_product); + } + + { + symbolTable.setFunctionExtensions("bfloat16BitsToIntEXT", 1, &E_GL_EXT_bfloat16); + symbolTable.setFunctionExtensions("bfloat16BitsToUintEXT", 1, &E_GL_EXT_bfloat16); + symbolTable.setFunctionExtensions("intBitsToBFloat16EXT", 1, &E_GL_EXT_bfloat16); + symbolTable.setFunctionExtensions("uintBitsToBFloat16EXT", 1, &E_GL_EXT_bfloat16); + } + + break; + + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + const char *rtexts[] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + symbolTable.setVariableExtensions("gl_LaunchIDNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchIDEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_PrimitiveID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_GeometryIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_CullMaskEXT", 1, &E_GL_EXT_ray_cull_mask); + symbolTable.setVariableExtensions("gl_HitTNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitTEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorld3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObject3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_CurrentRayTimeNV", 1, &E_GL_NV_ray_tracing_motion_blur); + symbolTable.setVariableExtensions("gl_HitTriangleVertexPositionsEXT", 1, &E_GL_EXT_ray_tracing_position_fetch); + symbolTable.setVariableExtensions("gl_HitMicroTriangleVertexPositionsNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setVariableExtensions("gl_HitMicroTriangleVertexBarycentricsNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setVariableExtensions("gl_ClusterIDNV", 1, &E_GL_NV_cluster_acceleration_structure); + symbolTable.setVariableExtensions("gl_HitKindSphereNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_HitKindLSSNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_HitSpherePositionNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_HitSphereRadiusNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_HitLSSPositionNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setVariableExtensions("gl_HitLSSRadiiNV", 1, &E_GL_NV_linear_swept_spheres); + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + + + symbolTable.setFunctionExtensions("traceNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("traceRayMotionNV", 1, &E_GL_NV_ray_tracing_motion_blur); + symbolTable.setFunctionExtensions("traceRayEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("ignoreIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("terminateRayNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableEXT", 1, &E_GL_EXT_ray_tracing); + + symbolTable.setFunctionExtensions("hitObjectTraceRayNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectTraceRayMotionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordHitNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordHitMotionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordHitWithIndexNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordHitWithIndexMotionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordMissNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordMissMotionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectRecordEmptyNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectExecuteShaderNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectIsEmptyNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectIsMissNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectIsHitNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetRayTMinNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetRayTMaxNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetObjectRayOriginNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetObjectRayDirectionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetWorldRayOriginNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetWorldRayDirectionNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetWorldToObjectNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetbjectToWorldNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetInstanceCustomIndexNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetInstanceIdNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetGeometryIndexNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetPrimitiveIndexNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetHitKindNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetAttributesNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetCurrentTimeNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetShaderBindingTableRecordIndexNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetShaderRecordBufferHandleNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("hitObjectGetClusterIdNV", 1, &E_GL_NV_cluster_acceleration_structure); + symbolTable.setFunctionExtensions("reorderThreadNV", 1, &E_GL_NV_shader_invocation_reorder); + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexPositionNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexBarycentricNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setFunctionExtensions("hitObjectGetSpherePositionNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("hitObjectGetSphereRadiusNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("hitObjectGetLSSPositionsNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("hitObjectGetLSSRadiiNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("hitObjectIsSphereHitNV", 1, &E_GL_NV_linear_swept_spheres); + symbolTable.setFunctionExtensions("hitObjectIsLSSHitNV", 1, &E_GL_NV_linear_swept_spheres); + + + BuiltInVariable("gl_LaunchIDNV", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchIDEXT", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchSizeNV", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_LaunchSizeEXT", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InstanceID", EbvInstanceId, symbolTable); + BuiltInVariable("gl_InstanceCustomIndexNV", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_InstanceCustomIndexEXT", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_GeometryIndexEXT", EbvGeometryIndex, symbolTable); + BuiltInVariable("gl_WorldRayOriginNV", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayOriginEXT", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayDirectionNV", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_WorldRayDirectionEXT", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayOriginNV", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayOriginEXT", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionNV", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionEXT", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_RayTminNV", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTminEXT", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTmaxNV", EbvRayTmax, symbolTable); + BuiltInVariable("gl_RayTmaxEXT", EbvRayTmax, symbolTable); + BuiltInVariable("gl_CullMaskEXT", EbvCullMask, symbolTable); + BuiltInVariable("gl_HitKindNV", EbvHitKind, symbolTable); + BuiltInVariable("gl_HitKindEXT", EbvHitKind, symbolTable); + BuiltInVariable("gl_ObjectToWorldNV", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorldEXT", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorld3x4EXT", EbvObjectToWorld3x4, symbolTable); + BuiltInVariable("gl_WorldToObjectNV", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObjectEXT", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObject3x4EXT", EbvWorldToObject3x4, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsNV", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsEXT", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + BuiltInVariable("gl_CurrentRayTimeNV", EbvCurrentRayTimeNV, symbolTable); + BuiltInVariable("gl_HitTriangleVertexPositionsEXT", EbvPositionFetch, symbolTable); + BuiltInVariable("gl_HitMicroTriangleVertexPositionsNV", EbvMicroTrianglePositionNV, symbolTable); + BuiltInVariable("gl_HitMicroTriangleVertexBarycentricsNV", EbvMicroTriangleBaryNV, symbolTable); + BuiltInVariable("gl_HitKindFrontFacingMicroTriangleNV", EbvHitKindFrontFacingMicroTriangleNV, symbolTable); + BuiltInVariable("gl_HitKindBackFacingMicroTriangleNV", EbvHitKindBackFacingMicroTriangleNV, symbolTable); + BuiltInVariable("gl_ClusterIDNV", EbvClusterIDNV, symbolTable); + BuiltInVariable("gl_HitIsSphereNV", EbvHitIsSphereNV, symbolTable); + BuiltInVariable("gl_HitIsLSSNV", EbvHitIsLSSNV, symbolTable); + BuiltInVariable("gl_HitSpherePositionNV", EbvHitSpherePositionNV, symbolTable); + BuiltInVariable("gl_HitSphereRadiusNV", EbvHitSphereRadiusNV, symbolTable); + BuiltInVariable("gl_HitLSSPositionsNV", EbvHitLSSPositionsNV, symbolTable); + BuiltInVariable("gl_HitLSSRadiiNV", EbvHitLSSRadiiNV, symbolTable); + + // gl_HitT variables are aliases of their gl_RayTmax counterparts. + RetargetVariable("gl_HitTNV", "gl_RayTmaxNV", symbolTable); + RetargetVariable("gl_HitTEXT", "gl_RayTmaxEXT", symbolTable); + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + + // GL_KHR_shader_subgroup + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + break; + + case EShLangMesh: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex builtins + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_Position", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PointSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistance", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistance", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistance", EbvCullDistance, symbolTable); + + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PositionPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", EbvClipDistancePerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", EbvCullDistancePerViewNV, symbolTable); + + // per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_PrimitiveID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_Layer", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportIndex", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMask", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportIndex", EbvViewportIndex, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + + // per-view per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", EbvLayerPerViewNV, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + // other builtins + symbolTable.setVariableExtensions("gl_PrimitiveCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_PrimitiveIndicesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_WorkGroupSize", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", Num_AEP_mesh_shader, AEP_mesh_shader); + } else { + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + } + BuiltInVariable("gl_PrimitiveCountNV", EbvPrimitiveCountNV, symbolTable); + BuiltInVariable("gl_PrimitiveIndicesNV", EbvPrimitiveIndicesNV, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + + // builtin constants + symbolTable.setVariableExtensions("gl_MaxMeshOutputVerticesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshOutputPrimitivesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + // builtin functions + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("barrier", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", Num_AEP_mesh_shader, AEP_mesh_shader); + } else { + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + symbolTable.setFunctionExtensions("writePackedPrimitiveIndices4x8NV", 1, &E_GL_NV_mesh_shader); + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_Mesh_shader + symbolTable.setVariableExtensions("gl_PrimitivePointIndicesEXT", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_PrimitiveLineIndicesEXT", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_PrimitiveTriangleIndicesEXT", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_EXT_mesh_shader); + + BuiltInVariable("gl_PrimitivePointIndicesEXT", EbvPrimitivePointIndicesEXT, symbolTable); + BuiltInVariable("gl_PrimitiveLineIndicesEXT", EbvPrimitiveLineIndicesEXT, symbolTable); + BuiltInVariable("gl_PrimitiveTriangleIndicesEXT", EbvPrimitiveTriangleIndicesEXT, symbolTable); + BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable); + + symbolTable.setVariableExtensions("gl_MeshVerticesEXT", "gl_Position", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesEXT", "gl_PointSize", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesEXT", "gl_ClipDistance", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesEXT", "gl_CullDistance", 1, &E_GL_EXT_mesh_shader); + + BuiltInVariable("gl_MeshVerticesEXT", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_MeshVerticesEXT", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_MeshVerticesEXT", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_MeshVerticesEXT", "gl_CullDistance", EbvCullDistance, symbolTable); + + symbolTable.setVariableExtensions("gl_MeshPrimitivesEXT", "gl_PrimitiveID", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesEXT", "gl_Layer", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesEXT", "gl_ViewportIndex", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesEXT", "gl_CullPrimitiveEXT", 1, &E_GL_EXT_mesh_shader); + + // note: technically this member requires both GL_EXT_mesh_shader and GL_EXT_fragment_shading_rate + // since setVariableExtensions only needs *one of* the extensions to validate, it's more useful to specify EXT_fragment_shading_rate + // GL_EXT_mesh_shader will be required in practice by use of other fields of gl_MeshPrimitivesEXT + symbolTable.setVariableExtensions("gl_MeshPrimitivesEXT", "gl_PrimitiveShadingRateEXT", 1, &E_GL_EXT_fragment_shading_rate); + + BuiltInVariable("gl_MeshPrimitivesEXT", "gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_MeshPrimitivesEXT", "gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_MeshPrimitivesEXT", "gl_ViewportIndex", EbvViewportIndex, symbolTable); + BuiltInVariable("gl_MeshPrimitivesEXT", "gl_CullPrimitiveEXT", EbvCullPrimitiveEXT, symbolTable); + BuiltInVariable("gl_MeshPrimitivesEXT", "gl_PrimitiveShadingRateEXT", EbvPrimitiveShadingRateKHR, symbolTable); + + symbolTable.setFunctionExtensions("SetMeshOutputsEXT", 1, &E_GL_EXT_mesh_shader); + + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + // GL_EXT_multiview + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + + // Builtins for GL_NV_displacment_micromap + if ((profile != EEsProfile && version >= 460)) { + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexPositionNV", 1, &E_GL_NV_displacement_micromap); + symbolTable.setFunctionExtensions("fetchMicroTriangleVertexBarycentricNV", 1, &E_GL_NV_displacement_micromap); + } + + break; + + case EShLangTask: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_TaskCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_WorkGroupSize", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", Num_AEP_mesh_shader, AEP_mesh_shader); + } else { + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + } + + BuiltInVariable("gl_TaskCountNV", EbvTaskCountNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + + symbolTable.setVariableExtensions("gl_MaxTaskWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("barrier", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", Num_AEP_mesh_shader, AEP_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", Num_AEP_mesh_shader, AEP_mesh_shader); + } else { + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_mesh_shader + symbolTable.setFunctionExtensions("EmitMeshTasksEXT", 1, &E_GL_EXT_mesh_shader); + symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_EXT_mesh_shader); + BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable); + + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) { + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + if (language == EShLangFragment) + ModifyFlatDecoration("gl_SubGroupSizeARB", true, symbolTable); + } + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + + // GL_ARM_shader_core_builtins + symbolTable.setVariableExtensions("gl_CoreCountARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_CoreMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpIDARM", 1, &E_GL_ARM_shader_core_builtins); + symbolTable.setVariableExtensions("gl_WarpMaxIDARM", 1, &E_GL_ARM_shader_core_builtins); + + BuiltInVariable("gl_CoreCountARM", EbvCoreCountARM, symbolTable); + BuiltInVariable("gl_CoreIDARM", EbvCoreIDARM, symbolTable); + BuiltInVariable("gl_CoreMaxIDARM", EbvCoreMaxIDARM, symbolTable); + BuiltInVariable("gl_WarpIDARM", EbvWarpIDARM, symbolTable); + BuiltInVariable("gl_WarpMaxIDARM", EbvWarpMaxIDARM, symbolTable); + } + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + break; + + default: + assert(false && "Language not supported"); + break; + } + + // + // Next, identify which built-ins have a mapping to an operator. + // If PureOperatorBuiltins is false, those that are not identified as such are + // expected to be resolved through a library of functions, versus as + // operations. + // + + relateTabledBuiltins(version, profile, spvVersion, language, symbolTable); + + symbolTable.relateToOperator("doubleBitsToInt64", EOpDoubleBitsToInt64); + symbolTable.relateToOperator("doubleBitsToUint64", EOpDoubleBitsToUint64); + symbolTable.relateToOperator("int64BitsToDouble", EOpInt64BitsToDouble); + symbolTable.relateToOperator("uint64BitsToDouble", EOpUint64BitsToDouble); + symbolTable.relateToOperator("halfBitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("halfBitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("float16BitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("float16BitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("int16BitsToFloat16", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToFloat16", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("int16BitsToHalf", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToHalf", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("packSnorm4x8", EOpPackSnorm4x8); + symbolTable.relateToOperator("unpackSnorm4x8", EOpUnpackSnorm4x8); + symbolTable.relateToOperator("packUnorm4x8", EOpPackUnorm4x8); + symbolTable.relateToOperator("unpackUnorm4x8", EOpUnpackUnorm4x8); + + symbolTable.relateToOperator("packDouble2x32", EOpPackDouble2x32); + symbolTable.relateToOperator("unpackDouble2x32", EOpUnpackDouble2x32); + + symbolTable.relateToOperator("packInt2x32", EOpPackInt2x32); + symbolTable.relateToOperator("unpackInt2x32", EOpUnpackInt2x32); + symbolTable.relateToOperator("packUint2x32", EOpPackUint2x32); + symbolTable.relateToOperator("unpackUint2x32", EOpUnpackUint2x32); + + symbolTable.relateToOperator("packInt2x16", EOpPackInt2x16); + symbolTable.relateToOperator("unpackInt2x16", EOpUnpackInt2x16); + symbolTable.relateToOperator("packUint2x16", EOpPackUint2x16); + symbolTable.relateToOperator("unpackUint2x16", EOpUnpackUint2x16); + + symbolTable.relateToOperator("packInt4x16", EOpPackInt4x16); + symbolTable.relateToOperator("unpackInt4x16", EOpUnpackInt4x16); + symbolTable.relateToOperator("packUint4x16", EOpPackUint4x16); + symbolTable.relateToOperator("unpackUint4x16", EOpUnpackUint4x16); + symbolTable.relateToOperator("packFloat2x16", EOpPackFloat2x16); + symbolTable.relateToOperator("unpackFloat2x16", EOpUnpackFloat2x16); + + symbolTable.relateToOperator("pack16", EOpPack16); + symbolTable.relateToOperator("pack32", EOpPack32); + symbolTable.relateToOperator("pack64", EOpPack64); + + symbolTable.relateToOperator("unpack32", EOpUnpack32); + symbolTable.relateToOperator("unpack16", EOpUnpack16); + symbolTable.relateToOperator("unpack8", EOpUnpack8); + + symbolTable.relateToOperator("controlBarrier", EOpBarrier); + symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter); + symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage); + + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take uint operations on buffer variables + // remap atomic counter functions to atomic operations + // + symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierBuffer); + } + + symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad); + symbolTable.relateToOperator("atomicStore", EOpAtomicStore); + + symbolTable.relateToOperator("atomicCounterIncrement", EOpAtomicCounterIncrement); + symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement); + symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter); + + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take uint operations + // remap atomic counter functions to atomic operations + // + // these atomic counter functions do not match signatures of glsl + // atomic functions, so they will be remapped to semantically + // equivalent functions in the parser + // + symbolTable.relateToOperator("atomicCounterIncrement", EOpNull); + symbolTable.relateToOperator("atomicCounterDecrement", EOpNull); + symbolTable.relateToOperator("atomicCounter", EOpNull); + } + + symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR); + symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR); + + symbolTable.relateToOperator("clockRealtimeEXT", EOpReadClockDeviceKHR); + symbolTable.relateToOperator("clockRealtime2x32EXT", EOpReadClockDeviceKHR); + + if (profile != EEsProfile && version == 450) { + symbolTable.relateToOperator("atomicCounterAddARB", EOpAtomicCounterAdd); + symbolTable.relateToOperator("atomicCounterSubtractARB", EOpAtomicCounterSubtract); + symbolTable.relateToOperator("atomicCounterMinARB", EOpAtomicCounterMin); + symbolTable.relateToOperator("atomicCounterMaxARB", EOpAtomicCounterMax); + symbolTable.relateToOperator("atomicCounterAndARB", EOpAtomicCounterAnd); + symbolTable.relateToOperator("atomicCounterOrARB", EOpAtomicCounterOr); + symbolTable.relateToOperator("atomicCounterXorARB", EOpAtomicCounterXor); + symbolTable.relateToOperator("atomicCounterExchangeARB", EOpAtomicCounterExchange); + symbolTable.relateToOperator("atomicCounterCompSwapARB", EOpAtomicCounterCompSwap); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicCounterAdd); + symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicCounterSubtract); + symbolTable.relateToOperator("atomicCounterMin", EOpAtomicCounterMin); + symbolTable.relateToOperator("atomicCounterMax", EOpAtomicCounterMax); + symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicCounterAnd); + symbolTable.relateToOperator("atomicCounterOr", EOpAtomicCounterOr); + symbolTable.relateToOperator("atomicCounterXor", EOpAtomicCounterXor); + symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicCounterExchange); + symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap); + } + + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take 'uint' instead of 'atomic_uint' + // remap atomic counter functions to non-counter atomic ops so + // functions act as aliases to non-counter atomic ops + // + symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicAdd); + symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicSubtract); + symbolTable.relateToOperator("atomicCounterMin", EOpAtomicMin); + symbolTable.relateToOperator("atomicCounterMax", EOpAtomicMax); + symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicAnd); + symbolTable.relateToOperator("atomicCounterOr", EOpAtomicOr); + symbolTable.relateToOperator("atomicCounterXor", EOpAtomicXor); + symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicExchange); + symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCompSwap); + } + + symbolTable.relateToOperator("fma", EOpFma); + symbolTable.relateToOperator("frexp", EOpFrexp); + symbolTable.relateToOperator("ldexp", EOpLdexp); + symbolTable.relateToOperator("uaddCarry", EOpAddCarry); + symbolTable.relateToOperator("usubBorrow", EOpSubBorrow); + symbolTable.relateToOperator("umulExtended", EOpUMulExtended); + symbolTable.relateToOperator("imulExtended", EOpIMulExtended); + symbolTable.relateToOperator("bitfieldExtract", EOpBitfieldExtract); + symbolTable.relateToOperator("bitfieldInsert", EOpBitfieldInsert); + symbolTable.relateToOperator("bitfieldReverse", EOpBitFieldReverse); + symbolTable.relateToOperator("bitCount", EOpBitCount); + symbolTable.relateToOperator("findLSB", EOpFindLSB); + symbolTable.relateToOperator("findMSB", EOpFindMSB); + + symbolTable.relateToOperator("helperInvocationEXT", EOpIsHelperInvocation); + + symbolTable.relateToOperator("countLeadingZeros", EOpCountLeadingZeros); + symbolTable.relateToOperator("countTrailingZeros", EOpCountTrailingZeros); + symbolTable.relateToOperator("absoluteDifference", EOpAbsDifference); + symbolTable.relateToOperator("addSaturate", EOpAddSaturate); + symbolTable.relateToOperator("subtractSaturate", EOpSubSaturate); + symbolTable.relateToOperator("average", EOpAverage); + symbolTable.relateToOperator("averageRounded", EOpAverageRounded); + symbolTable.relateToOperator("multiply32x16", EOpMul32x16); + symbolTable.relateToOperator("debugPrintfEXT", EOpDebugPrintf); + symbolTable.relateToOperator("assumeEXT", EOpAssumeEXT); + symbolTable.relateToOperator("expectEXT", EOpExpectEXT); + + + if (PureOperatorBuiltins) { + symbolTable.relateToOperator("imageSize", EOpImageQuerySize); + symbolTable.relateToOperator("imageSamples", EOpImageQuerySamples); + symbolTable.relateToOperator("imageLoad", EOpImageLoad); + symbolTable.relateToOperator("imageStore", EOpImageStore); + symbolTable.relateToOperator("imageAtomicAdd", EOpImageAtomicAdd); + symbolTable.relateToOperator("imageAtomicMin", EOpImageAtomicMin); + symbolTable.relateToOperator("imageAtomicMax", EOpImageAtomicMax); + symbolTable.relateToOperator("imageAtomicAnd", EOpImageAtomicAnd); + symbolTable.relateToOperator("imageAtomicOr", EOpImageAtomicOr); + symbolTable.relateToOperator("imageAtomicXor", EOpImageAtomicXor); + symbolTable.relateToOperator("imageAtomicExchange", EOpImageAtomicExchange); + symbolTable.relateToOperator("imageAtomicCompSwap", EOpImageAtomicCompSwap); + symbolTable.relateToOperator("imageAtomicLoad", EOpImageAtomicLoad); + symbolTable.relateToOperator("imageAtomicStore", EOpImageAtomicStore); + + symbolTable.relateToOperator("subpassLoad", EOpSubpassLoad); + symbolTable.relateToOperator("subpassLoadMS", EOpSubpassLoadMS); + + symbolTable.relateToOperator("textureGather", EOpTextureGather); + symbolTable.relateToOperator("textureGatherOffset", EOpTextureGatherOffset); + symbolTable.relateToOperator("textureGatherOffsets", EOpTextureGatherOffsets); + + symbolTable.relateToOperator("noise1", EOpNoise); + symbolTable.relateToOperator("noise2", EOpNoise); + symbolTable.relateToOperator("noise3", EOpNoise); + symbolTable.relateToOperator("noise4", EOpNoise); + + symbolTable.relateToOperator("textureFootprintNV", EOpImageSampleFootprintNV); + symbolTable.relateToOperator("textureFootprintClampNV", EOpImageSampleFootprintClampNV); + symbolTable.relateToOperator("textureFootprintLodNV", EOpImageSampleFootprintLodNV); + symbolTable.relateToOperator("textureFootprintGradNV", EOpImageSampleFootprintGradNV); + symbolTable.relateToOperator("textureFootprintGradClampNV", EOpImageSampleFootprintGradClampNV); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) + symbolTable.relateToOperator("ftransform", EOpFtransform); + + if (spvVersion.spv == 0 && (IncludeLegacy(version, profile, spvVersion) || + (profile == EEsProfile && version == 100))) { + + symbolTable.relateToOperator("texture1D", EOpTexture); + symbolTable.relateToOperator("texture1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture1DProj", EOpTextureProj); + symbolTable.relateToOperator("texture1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture1DLod", EOpTextureLod); + symbolTable.relateToOperator("texture1DProjLod", EOpTextureProjLod); + + symbolTable.relateToOperator("texture2DRect", EOpTexture); + symbolTable.relateToOperator("texture2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DRectProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DRect", EOpTexture); + symbolTable.relateToOperator("shadow2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2DRectProjGradARB", EOpTextureProjGrad); + + symbolTable.relateToOperator("texture2D", EOpTexture); + symbolTable.relateToOperator("texture2DProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("texture2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DProjGradEXT", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DLod", EOpTextureLod); + symbolTable.relateToOperator("texture2DLodEXT", EOpTextureLod); + symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod); + + symbolTable.relateToOperator("texture3D", EOpTexture); + symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture3DProj", EOpTextureProj); + symbolTable.relateToOperator("texture3DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture3DLod", EOpTextureLod); + symbolTable.relateToOperator("texture3DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("textureCube", EOpTexture); + symbolTable.relateToOperator("textureCubeGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeGradARB", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeLod", EOpTextureLod); + symbolTable.relateToOperator("textureCubeLodEXT", EOpTextureLod); + symbolTable.relateToOperator("shadow1D", EOpTexture); + symbolTable.relateToOperator("shadow1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2D", EOpTexture); + symbolTable.relateToOperator("shadow2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow1DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow1DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow2DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow1DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("shadow2DProjLod", EOpTextureProjLod); + } + + if (profile != EEsProfile) { + symbolTable.relateToOperator("sparseTextureARB", EOpSparseTexture); + symbolTable.relateToOperator("sparseTextureLodARB", EOpSparseTextureLod); + symbolTable.relateToOperator("sparseTextureOffsetARB", EOpSparseTextureOffset); + symbolTable.relateToOperator("sparseTexelFetchARB", EOpSparseTextureFetch); + symbolTable.relateToOperator("sparseTexelFetchOffsetARB", EOpSparseTextureFetchOffset); + symbolTable.relateToOperator("sparseTextureLodOffsetARB", EOpSparseTextureLodOffset); + symbolTable.relateToOperator("sparseTextureGradARB", EOpSparseTextureGrad); + symbolTable.relateToOperator("sparseTextureGradOffsetARB", EOpSparseTextureGradOffset); + symbolTable.relateToOperator("sparseTextureGatherARB", EOpSparseTextureGather); + symbolTable.relateToOperator("sparseTextureGatherOffsetARB", EOpSparseTextureGatherOffset); + symbolTable.relateToOperator("sparseTextureGatherOffsetsARB", EOpSparseTextureGatherOffsets); + symbolTable.relateToOperator("sparseImageLoadARB", EOpSparseImageLoad); + symbolTable.relateToOperator("sparseTexelsResidentARB", EOpSparseTexelsResident); + + symbolTable.relateToOperator("sparseTextureClampARB", EOpSparseTextureClamp); + symbolTable.relateToOperator("sparseTextureOffsetClampARB", EOpSparseTextureOffsetClamp); + symbolTable.relateToOperator("sparseTextureGradClampARB", EOpSparseTextureGradClamp); + symbolTable.relateToOperator("sparseTextureGradOffsetClampARB", EOpSparseTextureGradOffsetClamp); + symbolTable.relateToOperator("textureClampARB", EOpTextureClamp); + symbolTable.relateToOperator("textureOffsetClampARB", EOpTextureOffsetClamp); + symbolTable.relateToOperator("textureGradClampARB", EOpTextureGradClamp); + symbolTable.relateToOperator("textureGradOffsetClampARB", EOpTextureGradOffsetClamp); + + symbolTable.relateToOperator("ballotARB", EOpBallot); + symbolTable.relateToOperator("readInvocationARB", EOpReadInvocation); + symbolTable.relateToOperator("readFirstInvocationARB", EOpReadFirstInvocation); + + if (version >= 430) { + symbolTable.relateToOperator("anyInvocationARB", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocationsARB", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqualARB", EOpAllInvocationsEqual); + } + if (version >= 460) { + symbolTable.relateToOperator("anyInvocation", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocations", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqual", EOpAllInvocationsEqual); + } + symbolTable.relateToOperator("minInvocationsAMD", EOpMinInvocations); + symbolTable.relateToOperator("maxInvocationsAMD", EOpMaxInvocations); + symbolTable.relateToOperator("addInvocationsAMD", EOpAddInvocations); + symbolTable.relateToOperator("minInvocationsNonUniformAMD", EOpMinInvocationsNonUniform); + symbolTable.relateToOperator("maxInvocationsNonUniformAMD", EOpMaxInvocationsNonUniform); + symbolTable.relateToOperator("addInvocationsNonUniformAMD", EOpAddInvocationsNonUniform); + symbolTable.relateToOperator("minInvocationsInclusiveScanAMD", EOpMinInvocationsInclusiveScan); + symbolTable.relateToOperator("maxInvocationsInclusiveScanAMD", EOpMaxInvocationsInclusiveScan); + symbolTable.relateToOperator("addInvocationsInclusiveScanAMD", EOpAddInvocationsInclusiveScan); + symbolTable.relateToOperator("minInvocationsInclusiveScanNonUniformAMD", EOpMinInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsInclusiveScanNonUniformAMD", EOpMaxInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsInclusiveScanNonUniformAMD", EOpAddInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("minInvocationsExclusiveScanAMD", EOpMinInvocationsExclusiveScan); + symbolTable.relateToOperator("maxInvocationsExclusiveScanAMD", EOpMaxInvocationsExclusiveScan); + symbolTable.relateToOperator("addInvocationsExclusiveScanAMD", EOpAddInvocationsExclusiveScan); + symbolTable.relateToOperator("minInvocationsExclusiveScanNonUniformAMD", EOpMinInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsExclusiveScanNonUniformAMD", EOpMaxInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsExclusiveScanNonUniformAMD", EOpAddInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("swizzleInvocationsAMD", EOpSwizzleInvocations); + symbolTable.relateToOperator("swizzleInvocationsMaskedAMD", EOpSwizzleInvocationsMasked); + symbolTable.relateToOperator("writeInvocationAMD", EOpWriteInvocation); + symbolTable.relateToOperator("mbcntAMD", EOpMbcnt); + + symbolTable.relateToOperator("min3", EOpMin3); + symbolTable.relateToOperator("max3", EOpMax3); + symbolTable.relateToOperator("mid3", EOpMid3); + + symbolTable.relateToOperator("cubeFaceIndexAMD", EOpCubeFaceIndex); + symbolTable.relateToOperator("cubeFaceCoordAMD", EOpCubeFaceCoord); + symbolTable.relateToOperator("timeAMD", EOpTime); + + symbolTable.relateToOperator("textureGatherLodAMD", EOpTextureGatherLod); + symbolTable.relateToOperator("textureGatherLodOffsetAMD", EOpTextureGatherLodOffset); + symbolTable.relateToOperator("textureGatherLodOffsetsAMD", EOpTextureGatherLodOffsets); + symbolTable.relateToOperator("sparseTextureGatherLodAMD", EOpSparseTextureGatherLod); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetAMD", EOpSparseTextureGatherLodOffset); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetsAMD", EOpSparseTextureGatherLodOffsets); + + symbolTable.relateToOperator("imageLoadLodAMD", EOpImageLoadLod); + symbolTable.relateToOperator("imageStoreLodAMD", EOpImageStoreLod); + symbolTable.relateToOperator("sparseImageLoadLodAMD", EOpSparseImageLoadLod); + + symbolTable.relateToOperator("fragmentMaskFetchAMD", EOpFragmentMaskFetch); + symbolTable.relateToOperator("fragmentFetchAMD", EOpFragmentFetch); + } + + // GL_EXT_integer_dot_product + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 450)) { + symbolTable.relateToOperator("dotEXT", EOpDot); + symbolTable.relateToOperator("dotPacked4x8EXT", EOpDotPackedEXT); + symbolTable.relateToOperator("dotAccSatEXT", EOpDotAccSatEXT); + symbolTable.relateToOperator("dotPacked4x8AccSatEXT", EOpDotPackedAccSatEXT); + } + + // GL_EXT_bfloat16 + if ((profile == EEsProfile && version >= 320) || + (profile != EEsProfile && version >= 450)) { + symbolTable.relateToOperator("bfloat16BitsToIntEXT", EOpFloatBitsToInt); + symbolTable.relateToOperator("bfloat16BitsToUintEXT", EOpFloatBitsToUint); + symbolTable.relateToOperator("intBitsToBFloat16EXT", EOpIntBitsToFloat); + symbolTable.relateToOperator("uintBitsToBFloat16EXT", EOpUintBitsToFloat); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.relateToOperator("subgroupBarrier", EOpSubgroupBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrier", EOpSubgroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierBuffer", EOpSubgroupMemoryBarrierBuffer); + symbolTable.relateToOperator("subgroupMemoryBarrierImage", EOpSubgroupMemoryBarrierImage); + symbolTable.relateToOperator("subgroupElect", EOpSubgroupElect); + symbolTable.relateToOperator("subgroupAll", EOpSubgroupAll); + symbolTable.relateToOperator("subgroupAny", EOpSubgroupAny); + symbolTable.relateToOperator("subgroupAllEqual", EOpSubgroupAllEqual); + symbolTable.relateToOperator("subgroupBroadcast", EOpSubgroupBroadcast); + symbolTable.relateToOperator("subgroupBroadcastFirst", EOpSubgroupBroadcastFirst); + symbolTable.relateToOperator("subgroupBallot", EOpSubgroupBallot); + symbolTable.relateToOperator("subgroupInverseBallot", EOpSubgroupInverseBallot); + symbolTable.relateToOperator("subgroupBallotBitExtract", EOpSubgroupBallotBitExtract); + symbolTable.relateToOperator("subgroupBallotBitCount", EOpSubgroupBallotBitCount); + symbolTable.relateToOperator("subgroupBallotInclusiveBitCount", EOpSubgroupBallotInclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotExclusiveBitCount", EOpSubgroupBallotExclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotFindLSB", EOpSubgroupBallotFindLSB); + symbolTable.relateToOperator("subgroupBallotFindMSB", EOpSubgroupBallotFindMSB); + symbolTable.relateToOperator("subgroupShuffle", EOpSubgroupShuffle); + symbolTable.relateToOperator("subgroupShuffleXor", EOpSubgroupShuffleXor); + symbolTable.relateToOperator("subgroupShuffleUp", EOpSubgroupShuffleUp); + symbolTable.relateToOperator("subgroupShuffleDown", EOpSubgroupShuffleDown); + symbolTable.relateToOperator("subgroupRotate", EOpSubgroupRotate); + symbolTable.relateToOperator("subgroupClusteredRotate", EOpSubgroupClusteredRotate); + symbolTable.relateToOperator("subgroupAdd", EOpSubgroupAdd); + symbolTable.relateToOperator("subgroupMul", EOpSubgroupMul); + symbolTable.relateToOperator("subgroupMin", EOpSubgroupMin); + symbolTable.relateToOperator("subgroupMax", EOpSubgroupMax); + symbolTable.relateToOperator("subgroupAnd", EOpSubgroupAnd); + symbolTable.relateToOperator("subgroupOr", EOpSubgroupOr); + symbolTable.relateToOperator("subgroupXor", EOpSubgroupXor); + symbolTable.relateToOperator("subgroupInclusiveAdd", EOpSubgroupInclusiveAdd); + symbolTable.relateToOperator("subgroupInclusiveMul", EOpSubgroupInclusiveMul); + symbolTable.relateToOperator("subgroupInclusiveMin", EOpSubgroupInclusiveMin); + symbolTable.relateToOperator("subgroupInclusiveMax", EOpSubgroupInclusiveMax); + symbolTable.relateToOperator("subgroupInclusiveAnd", EOpSubgroupInclusiveAnd); + symbolTable.relateToOperator("subgroupInclusiveOr", EOpSubgroupInclusiveOr); + symbolTable.relateToOperator("subgroupInclusiveXor", EOpSubgroupInclusiveXor); + symbolTable.relateToOperator("subgroupExclusiveAdd", EOpSubgroupExclusiveAdd); + symbolTable.relateToOperator("subgroupExclusiveMul", EOpSubgroupExclusiveMul); + symbolTable.relateToOperator("subgroupExclusiveMin", EOpSubgroupExclusiveMin); + symbolTable.relateToOperator("subgroupExclusiveMax", EOpSubgroupExclusiveMax); + symbolTable.relateToOperator("subgroupExclusiveAnd", EOpSubgroupExclusiveAnd); + symbolTable.relateToOperator("subgroupExclusiveOr", EOpSubgroupExclusiveOr); + symbolTable.relateToOperator("subgroupExclusiveXor", EOpSubgroupExclusiveXor); + symbolTable.relateToOperator("subgroupClusteredAdd", EOpSubgroupClusteredAdd); + symbolTable.relateToOperator("subgroupClusteredMul", EOpSubgroupClusteredMul); + symbolTable.relateToOperator("subgroupClusteredMin", EOpSubgroupClusteredMin); + symbolTable.relateToOperator("subgroupClusteredMax", EOpSubgroupClusteredMax); + symbolTable.relateToOperator("subgroupClusteredAnd", EOpSubgroupClusteredAnd); + symbolTable.relateToOperator("subgroupClusteredOr", EOpSubgroupClusteredOr); + symbolTable.relateToOperator("subgroupClusteredXor", EOpSubgroupClusteredXor); + symbolTable.relateToOperator("subgroupQuadBroadcast", EOpSubgroupQuadBroadcast); + symbolTable.relateToOperator("subgroupQuadSwapHorizontal", EOpSubgroupQuadSwapHorizontal); + symbolTable.relateToOperator("subgroupQuadSwapVertical", EOpSubgroupQuadSwapVertical); + symbolTable.relateToOperator("subgroupQuadSwapDiagonal", EOpSubgroupQuadSwapDiagonal); + + symbolTable.relateToOperator("subgroupPartitionNV", EOpSubgroupPartition); + symbolTable.relateToOperator("subgroupPartitionedAddNV", EOpSubgroupPartitionedAdd); + symbolTable.relateToOperator("subgroupPartitionedMulNV", EOpSubgroupPartitionedMul); + symbolTable.relateToOperator("subgroupPartitionedMinNV", EOpSubgroupPartitionedMin); + symbolTable.relateToOperator("subgroupPartitionedMaxNV", EOpSubgroupPartitionedMax); + symbolTable.relateToOperator("subgroupPartitionedAndNV", EOpSubgroupPartitionedAnd); + symbolTable.relateToOperator("subgroupPartitionedOrNV", EOpSubgroupPartitionedOr); + symbolTable.relateToOperator("subgroupPartitionedXorNV", EOpSubgroupPartitionedXor); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAddNV", EOpSubgroupPartitionedInclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMulNV", EOpSubgroupPartitionedInclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMinNV", EOpSubgroupPartitionedInclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMaxNV", EOpSubgroupPartitionedInclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAndNV", EOpSubgroupPartitionedInclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveOrNV", EOpSubgroupPartitionedInclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedInclusiveXorNV", EOpSubgroupPartitionedInclusiveXor); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAddNV", EOpSubgroupPartitionedExclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMulNV", EOpSubgroupPartitionedExclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMinNV", EOpSubgroupPartitionedExclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMaxNV", EOpSubgroupPartitionedExclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAndNV", EOpSubgroupPartitionedExclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveOrNV", EOpSubgroupPartitionedExclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedExclusiveXorNV", EOpSubgroupPartitionedExclusiveXor); + } + + if (profile == EEsProfile) { + symbolTable.relateToOperator("shadow2DEXT", EOpTexture); + symbolTable.relateToOperator("shadow2DProjEXT", EOpTextureProj); + } + + // GL_EXT_shader_quad_control + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.relateToOperator("subgroupQuadAll", EOpSubgroupQuadAll); + symbolTable.relateToOperator("subgroupQuadAny", EOpSubgroupQuadAny); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.relateToOperator("textureWeightedQCOM", EOpImageSampleWeightedQCOM); + symbolTable.relateToOperator("textureBoxFilterQCOM", EOpImageBoxFilterQCOM); + symbolTable.relateToOperator("textureBlockMatchSADQCOM", EOpImageBlockMatchSADQCOM); + symbolTable.relateToOperator("textureBlockMatchSSDQCOM", EOpImageBlockMatchSSDQCOM); + + symbolTable.relateToOperator("textureBlockMatchWindowSSDQCOM", EOpImageBlockMatchWindowSSDQCOM); + symbolTable.relateToOperator("textureBlockMatchWindowSADQCOM", EOpImageBlockMatchWindowSADQCOM); + symbolTable.relateToOperator("textureBlockMatchGatherSSDQCOM", EOpImageBlockMatchGatherSSDQCOM); + symbolTable.relateToOperator("textureBlockMatchGatherSADQCOM", EOpImageBlockMatchGatherSADQCOM); + } + + if (profile != EEsProfile && spvVersion.spv == 0) { + symbolTable.relateToOperator("texture1DArray", EOpTexture); + symbolTable.relateToOperator("texture2DArray", EOpTexture); + symbolTable.relateToOperator("shadow1DArray", EOpTexture); + symbolTable.relateToOperator("shadow2DArray", EOpTexture); + + symbolTable.relateToOperator("texture1DArrayLod", EOpTextureLod); + symbolTable.relateToOperator("texture2DArrayLod", EOpTextureLod); + symbolTable.relateToOperator("shadow1DArrayLod", EOpTextureLod); + } + + symbolTable.relateToOperator("coopVecMatMulNV", EOpCooperativeVectorMatMulNV); + symbolTable.relateToOperator("coopVecMatMulAddNV", EOpCooperativeVectorMatMulAddNV); + symbolTable.relateToOperator("coopVecLoadNV", EOpCooperativeVectorLoadNV); + symbolTable.relateToOperator("coopVecStoreNV", EOpCooperativeVectorStoreNV); + symbolTable.relateToOperator("coopVecOuterProductAccumulateNV", EOpCooperativeVectorOuterProductAccumulateNV); + symbolTable.relateToOperator("coopVecReduceSumAccumulateNV", EOpCooperativeVectorReduceSumAccumulateNV); + } + + switch(language) { + case EShLangVertex: + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + break; + + case EShLangGeometry: + symbolTable.relateToOperator("EmitStreamVertex", EOpEmitStreamVertex); + symbolTable.relateToOperator("EndStreamPrimitive", EOpEndStreamPrimitive); + symbolTable.relateToOperator("EmitVertex", EOpEmitVertex); + symbolTable.relateToOperator("EndPrimitive", EOpEndPrimitive); + break; + + case EShLangFragment: + if (profile != EEsProfile && version >= 400) { + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse", EOpFwidthCoarse); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("rayQueryInitializeEXT", EOpRayQueryInitialize); + symbolTable.relateToOperator("rayQueryTerminateEXT", EOpRayQueryTerminate); + symbolTable.relateToOperator("rayQueryGenerateIntersectionEXT", EOpRayQueryGenerateIntersection); + symbolTable.relateToOperator("rayQueryConfirmIntersectionEXT", EOpRayQueryConfirmIntersection); + symbolTable.relateToOperator("rayQueryProceedEXT", EOpRayQueryProceed); + symbolTable.relateToOperator("rayQueryGetIntersectionTypeEXT", EOpRayQueryGetIntersectionType); + symbolTable.relateToOperator("rayQueryGetRayTMinEXT", EOpRayQueryGetRayTMin); + symbolTable.relateToOperator("rayQueryGetRayFlagsEXT", EOpRayQueryGetRayFlags); + symbolTable.relateToOperator("rayQueryGetIntersectionTEXT", EOpRayQueryGetIntersectionT); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceCustomIndexEXT", EOpRayQueryGetIntersectionInstanceCustomIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceIdEXT", EOpRayQueryGetIntersectionInstanceId); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset); + symbolTable.relateToOperator("rayQueryGetIntersectionGeometryIndexEXT", EOpRayQueryGetIntersectionGeometryIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionPrimitiveIndexEXT", EOpRayQueryGetIntersectionPrimitiveIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionBarycentricsEXT", EOpRayQueryGetIntersectionBarycentrics); + symbolTable.relateToOperator("rayQueryGetIntersectionFrontFaceEXT", EOpRayQueryGetIntersectionFrontFace); + symbolTable.relateToOperator("rayQueryGetIntersectionCandidateAABBOpaqueEXT", EOpRayQueryGetIntersectionCandidateAABBOpaque); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayDirectionEXT", EOpRayQueryGetIntersectionObjectRayDirection); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayOriginEXT", EOpRayQueryGetIntersectionObjectRayOrigin); + symbolTable.relateToOperator("rayQueryGetWorldRayDirectionEXT", EOpRayQueryGetWorldRayDirection); + symbolTable.relateToOperator("rayQueryGetWorldRayOriginEXT", EOpRayQueryGetWorldRayOrigin); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectToWorldEXT", EOpRayQueryGetIntersectionObjectToWorld); + symbolTable.relateToOperator("rayQueryGetIntersectionWorldToObjectEXT", EOpRayQueryGetIntersectionWorldToObject); + symbolTable.relateToOperator("rayQueryGetIntersectionTriangleVertexPositionsEXT", EOpRayQueryGetIntersectionTriangleVertexPositionsEXT); + symbolTable.relateToOperator("rayQueryGetIntersectionClusterIdNV", EOpRayQueryGetIntersectionClusterIdNV); + symbolTable.relateToOperator("rayQueryGetIntersectionSpherePositionNV", EOpRayQueryGetIntersectionSpherePositionNV); + symbolTable.relateToOperator("rayQueryGetIntersectionSphereRadiusNV", EOpRayQueryGetIntersectionSphereRadiusNV); + symbolTable.relateToOperator("rayQueryGetIntersectionLSSHitValueNV", EOpRayQueryGetIntersectionLSSHitValueNV); + symbolTable.relateToOperator("rayQueryGetIntersectionLSSPositionsNV", EOpRayQueryGetIntersectionLSSPositionsNV); + symbolTable.relateToOperator("rayQueryGetIntersectionLSSRadiiNV", EOpRayQueryGetIntersectionLSSRadiiNV); + symbolTable.relateToOperator("rayQueryIsSphereHitNV", EOpRayQueryIsSphereHitNV); + symbolTable.relateToOperator("rayQueryIsLSSHitNV", EOpRayQueryIsLSSHitNV); + } + + symbolTable.relateToOperator("interpolateAtCentroid", EOpInterpolateAtCentroid); + symbolTable.relateToOperator("interpolateAtSample", EOpInterpolateAtSample); + symbolTable.relateToOperator("interpolateAtOffset", EOpInterpolateAtOffset); + + if (profile != EEsProfile) + symbolTable.relateToOperator("interpolateAtVertexAMD", EOpInterpolateAtVertex); + + symbolTable.relateToOperator("beginInvocationInterlockARB", EOpBeginInvocationInterlock); + symbolTable.relateToOperator("endInvocationInterlockARB", EOpEndInvocationInterlock); + + symbolTable.relateToOperator("stencilAttachmentReadEXT", EOpStencilAttachmentReadEXT); + symbolTable.relateToOperator("depthAttachmentReadEXT", EOpDepthAttachmentReadEXT); + symbolTable.relateToOperator("colorAttachmentReadEXT", EOpColorAttachmentReadEXT); + + break; + + case EShLangCompute: + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("dFdx", EOpDPdx); + symbolTable.relateToOperator("dFdy", EOpDPdy); + symbolTable.relateToOperator("fwidth", EOpFwidth); + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse",EOpFwidthCoarse); + } + symbolTable.relateToOperator("coopMatLoadNV", EOpCooperativeMatrixLoadNV); + symbolTable.relateToOperator("coopMatStoreNV", EOpCooperativeMatrixStoreNV); + symbolTable.relateToOperator("coopMatMulAddNV", EOpCooperativeMatrixMulAddNV); + + symbolTable.relateToOperator("coopMatLoad", EOpCooperativeMatrixLoad); + symbolTable.relateToOperator("coopMatStore", EOpCooperativeMatrixStore); + symbolTable.relateToOperator("coopMatMulAdd", EOpCooperativeMatrixMulAdd); + + symbolTable.relateToOperator("coopMatLoadTensorNV", EOpCooperativeMatrixLoadTensorNV); + symbolTable.relateToOperator("coopMatStoreTensorNV", EOpCooperativeMatrixStoreTensorNV); + + symbolTable.relateToOperator("coopMatReduceNV", EOpCooperativeMatrixReduceNV); + symbolTable.relateToOperator("coopMatPerElementNV", EOpCooperativeMatrixPerElementOpNV); + symbolTable.relateToOperator("coopMatTransposeNV", EOpCooperativeMatrixTransposeNV); + + symbolTable.relateToOperator("createTensorLayoutNV", EOpCreateTensorLayoutNV); + symbolTable.relateToOperator("setTensorLayoutBlockSizeNV", EOpTensorLayoutSetBlockSizeNV); + symbolTable.relateToOperator("setTensorLayoutDimensionNV", EOpTensorLayoutSetDimensionNV); + symbolTable.relateToOperator("setTensorLayoutStrideNV", EOpTensorLayoutSetStrideNV); + symbolTable.relateToOperator("sliceTensorLayoutNV", EOpTensorLayoutSliceNV); + symbolTable.relateToOperator("setTensorLayoutClampValueNV", EOpTensorLayoutSetClampValueNV); + + symbolTable.relateToOperator("createTensorViewNV", EOpCreateTensorViewNV); + symbolTable.relateToOperator("setTensorViewDimensionsNV", EOpTensorViewSetDimensionNV); + symbolTable.relateToOperator("setTensorViewStrideNV", EOpTensorViewSetStrideNV); + symbolTable.relateToOperator("setTensorViewClipNV", EOpTensorViewSetClipNV); + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("fetchMicroTriangleVertexPositionNV", EOpFetchMicroTriangleVertexPositionNV); + symbolTable.relateToOperator("fetchMicroTriangleVertexBarycentricNV", EOpFetchMicroTriangleVertexBarycentricNV); + } + break; + + case EShLangRayGen: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("fetchMicroTriangleVertexPositionNV", EOpFetchMicroTriangleVertexPositionNV); + symbolTable.relateToOperator("fetchMicroTriangleVertexBarycentricNV", EOpFetchMicroTriangleVertexBarycentricNV); + } + [[fallthrough]]; + case EShLangClosestHit: + case EShLangMiss: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("traceNV", EOpTraceNV); + symbolTable.relateToOperator("traceRayMotionNV", EOpTraceRayMotionNV); + symbolTable.relateToOperator("traceRayEXT", EOpTraceKHR); + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallableNV); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallableKHR); + + symbolTable.relateToOperator("hitObjectTraceRayNV", EOpHitObjectTraceRayNV); + symbolTable.relateToOperator("hitObjectTraceRayMotionNV", EOpHitObjectTraceRayMotionNV); + symbolTable.relateToOperator("hitObjectRecordHitNV", EOpHitObjectRecordHitNV); + symbolTable.relateToOperator("hitObjectRecordHitMotionNV", EOpHitObjectRecordHitMotionNV); + symbolTable.relateToOperator("hitObjectRecordHitWithIndexNV", EOpHitObjectRecordHitWithIndexNV); + symbolTable.relateToOperator("hitObjectRecordHitWithIndexMotionNV", EOpHitObjectRecordHitWithIndexMotionNV); + symbolTable.relateToOperator("hitObjectRecordMissNV", EOpHitObjectRecordMissNV); + symbolTable.relateToOperator("hitObjectRecordMissMotionNV", EOpHitObjectRecordMissMotionNV); + symbolTable.relateToOperator("hitObjectRecordEmptyNV", EOpHitObjectRecordEmptyNV); + symbolTable.relateToOperator("hitObjectExecuteShaderNV", EOpHitObjectExecuteShaderNV); + symbolTable.relateToOperator("hitObjectIsEmptyNV", EOpHitObjectIsEmptyNV); + symbolTable.relateToOperator("hitObjectIsMissNV", EOpHitObjectIsMissNV); + symbolTable.relateToOperator("hitObjectIsHitNV", EOpHitObjectIsHitNV); + symbolTable.relateToOperator("hitObjectGetRayTMinNV", EOpHitObjectGetRayTMinNV); + symbolTable.relateToOperator("hitObjectGetRayTMaxNV", EOpHitObjectGetRayTMaxNV); + symbolTable.relateToOperator("hitObjectGetObjectRayOriginNV", EOpHitObjectGetObjectRayOriginNV); + symbolTable.relateToOperator("hitObjectGetObjectRayDirectionNV", EOpHitObjectGetObjectRayDirectionNV); + symbolTable.relateToOperator("hitObjectGetWorldRayOriginNV", EOpHitObjectGetWorldRayOriginNV); + symbolTable.relateToOperator("hitObjectGetWorldRayDirectionNV", EOpHitObjectGetWorldRayDirectionNV); + symbolTable.relateToOperator("hitObjectGetWorldToObjectNV", EOpHitObjectGetWorldToObjectNV); + symbolTable.relateToOperator("hitObjectGetObjectToWorldNV", EOpHitObjectGetObjectToWorldNV); + symbolTable.relateToOperator("hitObjectGetInstanceCustomIndexNV", EOpHitObjectGetInstanceCustomIndexNV); + symbolTable.relateToOperator("hitObjectGetInstanceIdNV", EOpHitObjectGetInstanceIdNV); + symbolTable.relateToOperator("hitObjectGetGeometryIndexNV", EOpHitObjectGetGeometryIndexNV); + symbolTable.relateToOperator("hitObjectGetPrimitiveIndexNV", EOpHitObjectGetPrimitiveIndexNV); + symbolTable.relateToOperator("hitObjectGetHitKindNV", EOpHitObjectGetHitKindNV); + symbolTable.relateToOperator("hitObjectGetAttributesNV", EOpHitObjectGetAttributesNV); + symbolTable.relateToOperator("hitObjectGetCurrentTimeNV", EOpHitObjectGetCurrentTimeNV); + symbolTable.relateToOperator("hitObjectGetShaderBindingTableRecordIndexNV", EOpHitObjectGetShaderBindingTableRecordIndexNV); + symbolTable.relateToOperator("hitObjectGetShaderRecordBufferHandleNV", EOpHitObjectGetShaderRecordBufferHandleNV); + symbolTable.relateToOperator("hitObjectGetClusterIdNV", EOpHitObjectGetClusterIdNV); + symbolTable.relateToOperator("reorderThreadNV", EOpReorderThreadNV); + symbolTable.relateToOperator("hitObjectGetSpherePositionNV", EOpHitObjectGetSpherePositionNV); + symbolTable.relateToOperator("hitObjectGetSphereRadiusNV", EOpHitObjectGetSphereRadiusNV); + symbolTable.relateToOperator("hitObjectGetLSSPositionsNV", EOpHitObjectGetLSSPositionsNV); + symbolTable.relateToOperator("hitObjectGetLSSRadiiNV", EOpHitObjectGetLSSRadiiNV); + symbolTable.relateToOperator("hitObjectIsSphereHitNV", EOpHitObjectIsSphereHitNV); + symbolTable.relateToOperator("hitObjectIsLSSHitNV", EOpHitObjectIsLSSHitNV); + } + break; + case EShLangIntersect: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("reportIntersectionNV", EOpReportIntersection); + symbolTable.relateToOperator("reportIntersectionEXT", EOpReportIntersection); + } + break; + case EShLangAnyHit: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("ignoreIntersectionNV", EOpIgnoreIntersectionNV); + symbolTable.relateToOperator("terminateRayNV", EOpTerminateRayNV); + } + break; + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallableNV); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallableKHR); + } + break; + case EShLangMesh: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("writePackedPrimitiveIndices4x8NV", EOpWritePackedPrimitiveIndices4x8NV); + symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared); + symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + } + + if (profile != EEsProfile && version >= 450) { + symbolTable.relateToOperator("SetMeshOutputsEXT", EOpSetMeshOutputsEXT); + } + + if (profile != EEsProfile && version >= 460) { + // Builtins for GL_NV_displacement_micromap. + symbolTable.relateToOperator("fetchMicroTriangleVertexPositionNV", EOpFetchMicroTriangleVertexPositionNV); + symbolTable.relateToOperator("fetchMicroTriangleVertexBarycentricNV", EOpFetchMicroTriangleVertexBarycentricNV); + } + break; + case EShLangTask: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared); + symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + } + if (profile != EEsProfile && version >= 450) { + symbolTable.relateToOperator("EmitMeshTasksEXT", EOpEmitMeshTasksEXT); + } + break; + + default: + assert(false && "Language not supported"); + } +} + +// +// Add context-dependent (resource-specific) built-ins not handled by the above. These +// would be ones that need to be programmatically added because they cannot +// be added by simple text strings. For these, also +// 1) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 2) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) +{ + if (profile != EEsProfile && version >= 430 && version < 440) { + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackBuffers", 1, &E_GL_ARB_enhanced_layouts); + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackInterleavedComponents", 1, &E_GL_ARB_enhanced_layouts); + } + if (profile != EEsProfile && version >= 130 && version < 420) { + symbolTable.setVariableExtensions("gl_MinProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + symbolTable.setVariableExtensions("gl_MaxProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + } + if (profile != EEsProfile && version >= 150 && version < 410) + symbolTable.setVariableExtensions("gl_MaxViewports", 1, &E_GL_ARB_viewport_array); + + switch(language) { + case EShLangFragment: + // Set up gl_FragData based on current array size. + if (version == 100 || IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && profile != EEsProfile && version < 420)) { + TPrecisionQualifier pq = profile == EEsProfile ? EpqMedium : EpqNone; + TType fragData(EbtFloat, EvqFragColor, pq, 4); + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(resources.maxDrawBuffers); + fragData.transferArraySizes(arraySizes); + symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); + SpecialQualifier("gl_FragData", EvqFragColor, EbvFragData, symbolTable); + } + + // GL_EXT_blend_func_extended + if (profile == EEsProfile && version >= 100) { + symbolTable.setVariableExtensions("gl_MaxDualSourceDrawBuffersEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragColorEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragDataEXT", 1, &E_GL_EXT_blend_func_extended); + SpecialQualifier("gl_SecondaryFragColorEXT", EvqVaryingOut, EbvSecondaryFragColorEXT, symbolTable); + SpecialQualifier("gl_SecondaryFragDataEXT", EvqVaryingOut, EbvSecondaryFragDataEXT, symbolTable); + } + + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + // Because of the context-dependent array size (gl_MaxPatchVertices), + // these variables were added later than the others and need to be mapped now. + + // standard members + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + // compatibility members + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + + // extension requirements + if (profile == EEsProfile) { + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + + break; + + default: + break; + } +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.h new file mode 100644 index 00000000..42c32ddb --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Initialize.h @@ -0,0 +1,115 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INITIALIZE_INCLUDED_ +#define _INITIALIZE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "Versions.h" + +namespace glslang { + +// +// This is made to hold parseable strings for almost all the built-in +// functions and variables for one specific combination of version +// and profile. (Some still need to be added programmatically.) +// This is a base class for language-specific derivations, which +// can be used for language independent builtins. +// +// The strings are organized by +// commonBuiltins: intersection of all stages' built-ins, processed just once +// stageBuiltins[]: anything a stage needs that's not in commonBuiltins +// +class TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltInParseables(); + virtual ~TBuiltInParseables(); + virtual void initialize(int version, EProfile, const SpvVersion& spvVersion) = 0; + virtual void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage) = 0; + virtual const TString& getCommonString() const { return commonBuiltins; } + virtual const TString& getStageString(EShLanguage language) const { return stageBuiltins[language]; } + + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) = 0; + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) = 0; + +protected: + TString commonBuiltins; + TString stageBuiltins[EShLangCount]; +}; + +// +// This is a GLSL specific derivation of TBuiltInParseables. To present a stable +// interface and match other similar code, it is called TBuiltIns, rather +// than TBuiltInParseablesGlsl. +// +class TBuiltIns : public TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltIns(); + virtual ~TBuiltIns(); + void initialize(int version, EProfile, const SpvVersion& spvVersion); + void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable); + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources); + +protected: + void addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion); + void relateTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage, TSymbolTable&); + void add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion); + void addSubpassSampling(TSampler, const TString& typeName, int version, EProfile profile); + void addQueryFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addImageFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addSamplingFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addGatherFunctions(TSampler, const TString& typeName, int version, EProfile profile); + + // Helpers for making textual representations of the permutations + // of texturing/imaging functions. + const char* postfixes[5]; + const char* prefixes[EbtNumTypes]; + int dimMap[EsdNumDims]; +}; + +// change this back to false if depending on textual spellings of texturing calls when consuming the AST +// Using PureOperatorBuiltins=false is deprecated. +constexpr bool PureOperatorBuiltins = true; +} // end namespace glslang + +#endif // _INITIALIZE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/IntermTraverse.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/IntermTraverse.cpp new file mode 100644 index 00000000..553b1b5f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/IntermTraverse.cpp @@ -0,0 +1,309 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (c) 2002-2010 The ANGLE Project Authors. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" + +namespace glslang { + +// +// Traverse the intermediate representation tree, and +// call a node type specific function for each node. +// Done recursively through the member function Traverse(). +// Node types can be skipped if their function to call is 0, +// but their subtree will still be traversed. +// Nodes with children can have their whole subtree skipped +// if preVisit is turned on and the type specific function +// returns false. +// +// preVisit, postVisit, and rightToLeft control what order +// nodes are visited in. +// + +// +// Traversal functions for terminals are straightforward.... +// +void TIntermMethod::traverse(TIntermTraverser*) +{ + // Tree should always resolve all methods as a non-method. +} + +void TIntermSymbol::traverse(TIntermTraverser *it) +{ + it->visitSymbol(this); +} + +void TIntermConstantUnion::traverse(TIntermTraverser *it) +{ + it->visitConstantUnion(this); +} + +const TString& TIntermSymbol::getAccessName() const { + if (getBasicType() == EbtBlock) + return getType().getTypeName(); + else + return getName(); +} + +// +// Traverse a binary node. +// +void TIntermBinary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + // + // visit the node before children if pre-visiting. + // + if (it->preVisit) + visit = it->visitBinary(EvPreVisit, this); + + // + // Visit the children, in the right order. + // + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (right) + right->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && left) + left->traverse(it); + } else { + if (left) + left->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && right) + right->traverse(it); + } + + it->decrementDepth(); + } + + // + // Visit the node after the children, if requested and the traversal + // hasn't been canceled yet. + // + if (visit && it->postVisit) + it->visitBinary(EvPostVisit, this); +} + +// +// Traverse a unary node. Same comments in binary node apply here. +// +void TIntermUnary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitUnary(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + operand->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitUnary(EvPostVisit, this); +} + +// +// Traverse an aggregate node. Same comments in binary node apply here. +// +void TIntermAggregate::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitAggregate(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + for (TIntermSequence::reverse_iterator sit = sequence.rbegin(); sit != sequence.rend(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.front()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } else { + for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.back()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitAggregate(EvPostVisit, this); +} + +// +// Traverse a selection node. Same comments in binary node apply here. +// +void TIntermSelection::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSelection(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + if (falseBlock) + falseBlock->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + if (falseBlock) + falseBlock->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSelection(EvPostVisit, this); +} + +// +// Traverse a loop node. Same comments in binary node apply here. +// +void TIntermLoop::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitLoop(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (terminal) + terminal->traverse(it); + + if (body) + body->traverse(it); + + if (test) + test->traverse(it); + } else { + if (test) + test->traverse(it); + + if (body) + body->traverse(it); + + if (terminal) + terminal->traverse(it); + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitLoop(EvPostVisit, this); +} + +// +// Traverse a branch node. Same comments in binary node apply here. +// +void TIntermBranch::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitBranch(EvPreVisit, this); + + if (visit && expression) { + it->incrementDepth(this); + expression->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitBranch(EvPostVisit, this); +} + +// +// Traverse a switch node. +// +void TIntermSwitch::traverse(TIntermTraverser* it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSwitch(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + body->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + body->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSwitch(EvPostVisit, this); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Intermediate.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Intermediate.cpp new file mode 100644 index 00000000..d162f32c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Intermediate.cpp @@ -0,0 +1,3841 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Build the intermediate representation. +// + +#include "localintermediate.h" +#include "RemoveTree.h" +#include "SymbolTable.h" +#include "propagateNoContraction.h" + +#include +#include +#include +#include + +namespace glslang { + +//////////////////////////////////////////////////////////////////////////// +// +// First set of functions are to help build the intermediate representation. +// These functions are not member functions of the nodes. +// They are called from parser productions. +// +///////////////////////////////////////////////////////////////////////////// + +// +// Add a terminal node for an identifier in an expression. +// +// Returns the added node. +// + +TIntermSymbol* TIntermediate::addSymbol(long long id, const TString& name, const TString& mangledName, const TType& type, const TConstUnionArray& constArray, + TIntermTyped* constSubtree, const TSourceLoc& loc) +{ + TIntermSymbol* node = new TIntermSymbol(id, name, getStage(), type, &mangledName); + node->setLoc(loc); + node->setConstArray(constArray); + node->setConstSubtree(constSubtree); + + return node; +} + +TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol) +{ + return addSymbol(intermSymbol.getId(), + intermSymbol.getName(), + intermSymbol.getMangledName(), + intermSymbol.getType(), + intermSymbol.getConstArray(), + intermSymbol.getConstSubtree(), + intermSymbol.getLoc()); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) +{ + glslang::TSourceLoc loc; // just a null location + loc.init(); + + return addSymbol(variable, loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) +{ + return addSymbol(variable.getUniqueId(), variable.getName(), variable.getMangledName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) +{ + TConstUnionArray unionArray; // just a null constant + + return addSymbol(0, "", "", type, unionArray, nullptr, loc); +} + +// +// Connect two nodes with a new parent that does a binary operation on the nodes. +// +// Returns the added node. +// +// Returns nullptr if the working conversions and promotions could not be found. +// +TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) +{ + // No operations work on blocks + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference +/- int" and "reference - reference" to integer math + if (op == EOpAdd || op == EOpSub) { + + // No addressing math on struct with unsized array. + if ((left->isReference() && left->getType().getReferentType()->containsUnsizedArray()) || + (right->isReference() && right->getType().getReferentType()->containsUnsizedArray())) { + return nullptr; + } + + if (left->isReference() && isTypeInt(right->getBasicType())) { + const TType& referenceType = left->getType(); + TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + + right = createConversion(EbtInt64, right); + right = addBinaryMath(EOpMul, right, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + } + + if (op == EOpAdd && right->isReference() && isTypeInt(left->getBasicType())) { + const TType& referenceType = right->getType(); + TIntermConstantUnion* size = + addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(right->getType()), loc, true); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = createConversion(EbtInt64, left); + left = addBinaryMath(EOpMul, left, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + + if (op == EOpSub && left->isReference() && right->isReference()) { + TIntermConstantUnion* size = + addConstantUnion((long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = addBuiltInFunctionCall(loc, EOpConvNumeric, true, left, TType(EbtInt64)); + right = addBuiltInFunctionCall(loc, EOpConvNumeric, true, right, TType(EbtInt64)); + + left = addBinaryMath(EOpSub, left, right, loc); + + TIntermTyped *node = addBinaryMath(EOpDiv, left, size, loc); + return node; + } + + // No other math operators supported on references + if (left->isReference() || right->isReference()) + return nullptr; + + // Try converting the children's base types to compatible types. + auto children = addPairConversion(op, left, right); + left = std::get<0>(children); + right = std::get<1>(children); + + if (left == nullptr || right == nullptr) + return nullptr; + + // Convert the children's type shape to be compatible. + addBiShapeConversion(op, left, right); + if (left == nullptr || right == nullptr) + return nullptr; + + // + // Need a new node holding things together. Make + // one and promote it to the right type. + // + TIntermBinary* node = addBinaryNode(op, left, right, loc); + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // + // If they are both (non-specialization) constants, they must be folded. + // (Unless it's the sequence (comma) operator, but that's handled in addComma().) + // + TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion(); + TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion(); + if (leftTempConstant && rightTempConstant) { + TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant); + if (folded) + return folded; + } + + // If can propagate spec-constantness and if the operation is an allowed + // specialization-constant operation, make a spec-constant. + if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if ((node->getLeft()->getQualifier().isNonUniform() || node->getRight()->getQualifier().isNonUniform()) && + isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +// +// Low level: add binary node (no promotions or other argument modifications) +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc) const +{ + // build the node + TIntermBinary* node = new TIntermBinary(op); + node->setLoc(loc.line != 0 ? loc : left->getLoc()); + node->setLeft(left); + node->setRight(right); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc, const TType& type) const +{ + TIntermBinary* node = addBinaryNode(op, left, right, loc); + node->setType(type); + return node; +} + +// +// Low level: add unary node (no promotions or other argument modifications) +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc) const +{ + TIntermUnary* node = new TIntermUnary(op); + node->setLoc(loc.line != 0 ? loc : child->getLoc()); + node->setOperand(child); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc, const TType& type) + const +{ + TIntermUnary* node = addUnaryNode(op, child, loc); + node->setType(type); + return node; +} + +// +// Connect two nodes through an assignment. +// +// Returns the added node. +// +// Returns nullptr if the 'right' type could not be converted to match the 'left' type, +// or the resulting operation cannot be properly promoted. +// +TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc) +{ + // No block assignment + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference += int" to "reference = reference + int". We need this because the + // "reference + int" calculation involves a cast back to the original type, which makes it + // not an lvalue. + if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) { + if (!(right->getType().isScalar() && right->getType().isIntegerDomain())) + return nullptr; + + TIntermTyped* node = addBinaryMath(op == EOpAddAssign ? EOpAdd : EOpSub, left, right, loc); + if (!node) + return nullptr; + + TIntermSymbol* symbol = left->getAsSymbolNode(); + left = addSymbol(*symbol); + + node = addAssign(EOpAssign, left, node, loc); + return node; + } + + // + // Like adding binary math, except the conversion can only go + // from right to left. + // + + // convert base types, nullptr return means not possible + right = addConversion(op, left->getType(), right); + if (right == nullptr) + return nullptr; + + // convert shape + right = addUniShapeConversion(op, left->getType(), right); + + // build the node + TIntermBinary* node = addBinaryNode(op, left, right, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + return node; +} + +// +// Connect two nodes through an index operator, where the left node is the base +// of an array or struct, and the right node is a direct or indirect offset. +// +// Returns the added node. +// The caller should set the type of the returned node. +// +TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, + const TSourceLoc& loc) +{ + // caller should set the type + return addBinaryNode(op, base, index, loc); +} + +// +// Add one node as the parent of another that it operates on. +// +// Returns the added node. +// +TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, + const TSourceLoc& loc) +{ + if (child == nullptr) + return nullptr; + + if (child->getType().getBasicType() == EbtBlock) + return nullptr; + + switch (op) { + case EOpLogicalNot: + if (getSource() == EShSourceHlsl) { + break; // HLSL can promote logical not + } + + if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { + return nullptr; + } + break; + + case EOpPostIncrement: + case EOpPreIncrement: + case EOpPostDecrement: + case EOpPreDecrement: + case EOpNegative: + if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) + return nullptr; + break; + default: break; // some compilers want this + } + + // + // Do we need to promote the operand? + // + TBasicType newType = EbtVoid; + switch (op) { + case EOpConstructBool: newType = EbtBool; break; + case EOpConstructFloat: newType = EbtFloat; break; + case EOpConstructInt: newType = EbtInt; break; + case EOpConstructUint: newType = EbtUint; break; + case EOpConstructInt8: newType = EbtInt8; break; + case EOpConstructUint8: newType = EbtUint8; break; + case EOpConstructInt16: newType = EbtInt16; break; + case EOpConstructUint16: newType = EbtUint16; break; + case EOpConstructInt64: newType = EbtInt64; break; + case EOpConstructUint64: newType = EbtUint64; break; + case EOpConstructDouble: newType = EbtDouble; break; + case EOpConstructFloat16: newType = EbtFloat16; break; + case EOpConstructBFloat16: newType = EbtBFloat16; break; + default: break; // some compilers want this + } + + if (newType != EbtVoid) { + child = addConversion(op, TType(newType, EvqTemporary, child->getVectorSize(), + child->getMatrixCols(), + child->getMatrixRows(), + child->isVector()), + child); + if (child == nullptr) + return nullptr; + } + + // + // For constructors, we are now done, it was all in the conversion. + // TODO: but, did this bypass constant folding? + // + switch (op) { + case EOpConstructInt8: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructBool: + case EOpConstructFloat: + case EOpConstructDouble: + case EOpConstructFloat16: + case EOpConstructBFloat16: { + TIntermUnary* unary_node = child->getAsUnaryNode(); + if (unary_node != nullptr) + unary_node->updatePrecision(); + return child; + } + default: break; // some compilers want this + } + + // + // Make a new node for the operator. + // + TIntermUnary* node = addUnaryNode(op, child, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // If it's a (non-specialization) constant, it must be folded. + if (node->getOperand()->getAsConstantUnion()) + return node->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + + // If it's a specialization constant, the result is too, + // if the operation is allowed for specialization constants. + if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if (node->getOperand()->getQualifier().isNonUniform() && isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary, + TIntermNode* childNode, const TType& returnType) +{ + if (unary) { + // + // Treat it like a unary operator. + // addUnaryMath() should get the type correct on its own; + // including constness (which would differ from the prototype). + // + TIntermTyped* child = childNode->getAsTyped(); + if (child == nullptr) + return nullptr; + + if (child->getAsConstantUnion()) { + TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType); + if (folded) + return folded; + } + + return addUnaryNode(op, child, child->getLoc(), returnType); + } else { + // setAggregateOperater() calls fold() for constant folding + TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc); + + return node; + } +} + +// +// This is the safe way to change the operator on an aggregate, as it +// does lots of error checking and fixing. Especially for establishing +// a function call's operation on its set of parameters. Sequences +// of instructions are also aggregates, but they just directly set +// their operator to EOpSequence. +// +// Returns an aggregate node, which could be the one passed in if +// it was already an aggregate. +// +TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, + const TSourceLoc& loc) +{ + TIntermAggregate* aggNode; + + // + // Make sure we have an aggregate. If not turn it into one. + // + if (node != nullptr) { + aggNode = node->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + // + // Make an aggregate containing this node. + // + aggNode = new TIntermAggregate(); + aggNode->getSequence().push_back(node); + } + } else + aggNode = new TIntermAggregate(); + + // + // Set the operator. + // + aggNode->setOperator(op); + if (loc.line != 0 || node != nullptr) + aggNode->setLoc(loc.line != 0 ? loc : node->getLoc()); + + aggNode->setType(type); + + return fold(aggNode); +} + +bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const +{ + // + // Does the base type even allow the operation? + // + switch (node->getBasicType()) { + case EbtVoid: + return false; + case EbtAtomicUint: + case EbtSampler: + case EbtAccStruct: + // opaque types can be passed to functions + if (op == EOpFunction) + break; + + // HLSL can assign samplers directly (no constructor) + if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler) + break; + + // samplers can get assigned via a sampler constructor + // (well, not yet, but code in the rest of this function is ready for it) + if (node->getBasicType() == EbtSampler && op == EOpAssign && + node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + break; + + // otherwise, opaque types can't even be operated on, let alone converted + return false; + default: + break; + } + + return true; +} + +bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const +{ + // bfloat16_t <-> bool not supported + if ((src == EbtBFloat16 && dst == EbtBool) || + (dst == EbtBFloat16 && src == EbtBool)) { + return false; + } + + if ((isTypeInt(dst) || isTypeFloat(dst) || dst == EbtBool) && + (isTypeInt(src) || isTypeFloat(src) || src == EbtBool)) { + newOp = EOpConvNumeric; + return true; + } + return false; +} + +// This is 'mechanism' here, it does any conversion told. +// It is about basic type, not about shape. +// The policy comes from the shader or the calling code. +TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const +{ + // + // Add a new newNode for the conversion. + // + + bool convertToIntTypes = (convertTo == EbtInt8 || convertTo == EbtUint8 || + convertTo == EbtInt16 || convertTo == EbtUint16 || + convertTo == EbtInt || convertTo == EbtUint || + convertTo == EbtInt64 || convertTo == EbtUint64); + + bool convertFromIntTypes = (node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8 || + node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16 || + node->getBasicType() == EbtInt || node->getBasicType() == EbtUint || + node->getBasicType() == EbtInt64 || node->getBasicType() == EbtUint64); + + bool convertToFloatTypes = (convertTo == EbtFloat16 || convertTo == EbtBFloat16 || convertTo == EbtFloat || convertTo == EbtDouble); + + bool convertFromFloatTypes = (node->getBasicType() == EbtFloat16 || + node->getBasicType() == EbtBFloat16 || + node->getBasicType() == EbtFloat || + node->getBasicType() == EbtDouble); + + if (((convertTo == EbtInt8 || convertTo == EbtUint8) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8) && ! convertToIntTypes)) { + if (! getArithemeticInt8Enabled()) { + return nullptr; + } + } + + if (((convertTo == EbtInt16 || convertTo == EbtUint16) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16) && ! convertToIntTypes)) { + if (! getArithemeticInt16Enabled()) { + return nullptr; + } + } + + if ((convertTo == EbtFloat16 && ! convertFromFloatTypes) || + (node->getBasicType() == EbtFloat16 && ! convertToFloatTypes)) { + if (! getArithemeticFloat16Enabled()) { + return nullptr; + } + } + + TIntermUnary* newNode = nullptr; + TOperator newOp = EOpNull; + if (!buildConvertOp(convertTo, node->getBasicType(), newOp)) { + return nullptr; + } + + TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); + newNode = addUnaryNode(newOp, node, node->getLoc(), newType); + + if (node->getAsConstantUnion()) { + // 8/16-bit storage extensions don't support 8/16-bit constants, so don't fold conversions + // to those types + if ((getArithemeticInt8Enabled() || !(convertTo == EbtInt8 || convertTo == EbtUint8)) && + (getArithemeticInt16Enabled() || !(convertTo == EbtInt16 || convertTo == EbtUint16)) && + (getArithemeticFloat16Enabled() || !(convertTo == EbtFloat16))) + { + TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType); + if (folded) + return folded; + } + } + + // Propagate specialization-constant-ness, if allowed + if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode)) + newNode->getWritableType().getQualifier().makeSpecConstant(); + + return newNode; +} + +TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const +{ + return createConversion(convertTo, node); +} + +// For converting a pair of operands to a binary operation to compatible +// types with each other, relative to the operation in 'op'. +// This does not cover assignment operations, which is asymmetric in that the +// left type is not changeable. +// See addConversion(op, type, node) for assignments and unary operation +// conversions. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Returns the converted pair of nodes. +// Returns when there is no conversion. +std::tuple +TIntermediate::addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1) +{ + if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1)) + return std::make_tuple(nullptr, nullptr); + + if (node0->getType() != node1->getType()) { + // If differing structure, then no conversions. + if (node0->isStruct() || node1->isStruct()) + return std::make_tuple(nullptr, nullptr); + + // If differing arrays, then no conversions. + if (node0->getType().isArray() || node1->getType().isArray()) + return std::make_tuple(nullptr, nullptr); + + // No implicit conversions for operations involving cooperative matrices + if (node0->getType().isCoopMat() || node1->getType().isCoopMat()) + return std::make_tuple(node0, node1); + } + + auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes); + + switch (op) { + // + // List all the binary ops that can implicitly convert one operand to the other's type; + // This implements the 'policy' for implicit type conversion. + // + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpEqual: + case EOpNotEqual: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpSequence: // used by ?: + + if (node0->getBasicType() == node1->getBasicType()) + return std::make_tuple(node0, node1); + + promoteTo = getConversionDestinationType(node0->getBasicType(), node1->getBasicType(), op); + if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes) + return std::make_tuple(nullptr, nullptr); + + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + if (getSource() == EShSourceHlsl) + promoteTo = std::make_tuple(EbtBool, EbtBool); + else + return std::make_tuple(node0, node1); + break; + + // There are no conversions needed for GLSL; the shift amount just needs to be an + // integer type, as does the base. + // HLSL can promote bools to ints to make this work. + case EOpLeftShift: + case EOpRightShift: + if (getSource() == EShSourceHlsl) { + TBasicType node0BasicType = node0->getBasicType(); + if (node0BasicType == EbtBool) + node0BasicType = EbtInt; + if (node1->getBasicType() == EbtBool) + promoteTo = std::make_tuple(node0BasicType, EbtInt); + else + promoteTo = std::make_tuple(node0BasicType, node1->getBasicType()); + } else { + if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType())) + return std::make_tuple(node0, node1); + else + return std::make_tuple(nullptr, nullptr); + } + break; + + default: + if (node0->getType() == node1->getType()) + return std::make_tuple(node0, node1); + + return std::make_tuple(nullptr, nullptr); + } + + TIntermTyped* newNode0; + TIntermTyped* newNode1; + + if (std::get<0>(promoteTo) != node0->getType().getBasicType()) { + if (node0->getAsConstantUnion()) + newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion()); + else + newNode0 = createConversion(std::get<0>(promoteTo), node0); + } else + newNode0 = node0; + + if (std::get<1>(promoteTo) != node1->getType().getBasicType()) { + if (node1->getAsConstantUnion()) + newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion()); + else + newNode1 = createConversion(std::get<1>(promoteTo), node1); + } else + newNode1 = node1; + + return std::make_tuple(newNode0, newNode1); +} + +// +// Convert the node's type to the given type, as allowed by the operation involved: 'op'. +// For implicit conversions, 'op' is not the requested conversion, it is the explicit +// operation requiring the implicit conversion. +// +// Binary operation conversions should be handled by addConversion(op, node, node), not here. +// +// Returns a node representing the conversion, which could be the same +// node passed in if no conversion was needed. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Return nullptr if a conversion can't be done. +// +TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + if (!isConversionAllowed(op, node)) + return nullptr; + + // Otherwise, if types are identical, no problem + if (type == node->getType()) + return node; + + // If one's a structure, then no conversions. + if (type.isStruct() || node->isStruct()) + return nullptr; + + // If one's an array, then no conversions. + if (type.isArray() || node->getType().isArray()) + return nullptr; + + // Reject implicit conversions to cooperative matrix types + if (node->getType().isCoopMat() && + op != EOpConstructCooperativeMatrixNV && + op != EOpConstructCooperativeMatrixKHR) + return nullptr; + + if (node->getType().isTensorLayoutNV() || + node->getType().isTensorViewNV()) + return nullptr; + + // Reject implicit conversions to cooperative vector types + if (node->getType().isCoopVecNV() && + op != EOpConstructCooperativeVectorNV) + return nullptr; + + // Note: callers are responsible for other aspects of shape, + // like vector and matrix sizes. + + switch (op) { + // + // Explicit conversions (unary operations) + // + case EOpConstructBool: + case EOpConstructFloat: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructDouble: + case EOpConstructFloat16: + case EOpConstructBFloat16: + case EOpConstructInt8: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: + break; + + // + // Implicit conversions + // + case EOpLogicalNot: + + case EOpFunctionCall: + + case EOpReturn: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + case EOpFma: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpMod: + case EOpModf: + case EOpPow: + case EOpReflect: + case EOpRefract: + case EOpSmoothStep: + case EOpStep: + + case EOpSequence: + case EOpConstructStruct: + case EOpConstructCooperativeMatrixNV: + case EOpConstructCooperativeMatrixKHR: + case EOpConstructCooperativeVectorNV: + + if (type.isReference() || node->getType().isReference()) { + // types must match to assign a reference + if (type == node->getType()) + return node; + else + return nullptr; + } + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + + if (! canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op)) + return nullptr; + break; + + // For GLSL, there are no conversions needed; the shift amount just needs to be an + // integer type, as do the base/result. + // HLSL can convert the shift from a bool to an int. + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + { + if (!(getSource() == EShSourceHlsl && node->getType().getBasicType() == EbtBool)) { + if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType())) + return node; + else + return nullptr; + } + break; + } + + default: + // default is to require a match; all exceptions should have case statements above + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + else + return nullptr; + } + + bool canPromoteConstant = true; + // GL_EXT_shader_16bit_storage can't do OpConstantComposite with + // 16-bit types, so disable promotion for those types. + // Many issues with this, from JohnK: + // - this isn't really right to discuss SPIR-V here + // - this could easily be entirely about scalars, so is overstepping + // - we should be looking at what the shader asked for, and saying whether or + // not it can be done, in the parser, by calling requireExtensions(), not + // changing language sementics on the fly by asking what extensions are in use + // - at the time of this writing (14-Aug-2020), no test results are changed by this. + switch (op) { + case EOpConstructBFloat16: + canPromoteConstant = true; + break; + case EOpConstructFloat16: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); + break; + case EOpConstructInt8: + case EOpConstructUint8: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); + break; + case EOpConstructInt16: + case EOpConstructUint16: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); + break; + default: + break; + } + + if (canPromoteConstant && node->getAsConstantUnion()) + return promoteConstantUnion(type.getBasicType(), node->getAsConstantUnion()); + + // + // Add a new newNode for the conversion. + // + TIntermTyped* newNode = createConversion(type.getBasicType(), node); + + return newNode; +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. This is for situations where there is only one +// direction to consider doing the shape conversion. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return node; + } + + // some operations don't do this + switch (op) { + case EOpFunctionCall: + case EOpReturn: + break; + + case EOpMulAssign: + // want to support vector *= scalar native ops in AST and lower, not smear, similarly for + // matrix *= scalar, etc. + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (node->getVectorSize() == 1) + return node; + break; + + case EOpAssign: + break; + + case EOpMix: + break; + + default: + return node; + } + + return addShapeConversion(type, node); +} + +// Convert the nodes' shapes to be compatible for the operation 'op'. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return; + } + + // some operations don't do this + // 'break' will mean attempt bidirectional conversion + switch (op) { + case EOpMulAssign: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + // switch to unidirectional conversion (the lhs can't change) + rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode); + return; + + case EOpMul: + // matrix multiply does not change shapes + if (lhsNode->isMatrix() && rhsNode->isMatrix()) + return; + [[fallthrough]]; + case EOpAdd: + case EOpSub: + case EOpDiv: + // want to support vector * scalar native ops in AST and lower, not smear, similarly for + // matrix * vector, etc. + if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1) + return; + break; + + case EOpRightShift: + case EOpLeftShift: + // can natively support the right operand being a scalar and the left a vector, + // but not the reverse + if (rhsNode->getVectorSize() == 1) + return; + break; + + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpMix: + break; + + default: + return; + } + + // Do bidirectional conversions + if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) { + if (lhsNode->getType().isScalarOrVec1()) + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + else + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); + } + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node) +{ + // no conversion needed + if (node->getType() == type) + return node; + + // structures and arrays don't change shape, either to or from + if (node->getType().isStruct() || node->getType().isArray() || + type.isStruct() || type.isArray()) + return node; + + // The new node that handles the conversion + TOperator constructorOp = mapTypeToConstructorOp(type); + + if (getSource() == EShSourceHlsl) { + // HLSL rules for scalar, vector and matrix conversions: + // 1) scalar can become anything, initializing every component with its value + // 2) vector and matrix can become scalar, first element is used (warning: truncation) + // 3) matrix can become matrix with less rows and/or columns (warning: truncation) + // 4) vector can become vector with less rows size (warning: truncation) + // 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret) + // 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret) + + const TType &sourceType = node->getType(); + + // rule 1 for scalar to matrix is special + if (sourceType.isScalarOrVec1() && type.isMatrix()) { + + // HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its + // own devices, the constructor from a scalar would populate the diagonal. This forces replication + // to every matrix element. + + // Note that if the node is complex (e.g, a function call), we don't want to duplicate it here + // repeatedly, so we copy it to a temp, then use the temp. + const int matSize = type.computeNumComponents(); + TIntermAggregate* rhsAggregate = new TIntermAggregate(); + + const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr); + + if (!isSimple) { + assert(0); // TODO: use node replicator service when available. + } + + for (int x = 0; x < matSize; ++x) + rhsAggregate->getSequence().push_back(node); + + return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc()); + } + + // rule 1 and 2 + if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + // rule 3 and 5b + if (sourceType.isMatrix()) { + // rule 3 + if (type.isMatrix()) { + if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) && + sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5b + } else if (type.isVector()) { + if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + + // rule 4 and 5a + if (sourceType.isVector()) { + // rule 4 + if (type.isVector()) + { + if (sourceType.getVectorSize() > type.getVectorSize()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5a + } else if (type.isMatrix()) { + if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + } + + // scalar -> vector or vec1 -> vector or + // vector -> scalar or + // bigger vector -> smaller vector + if ((node->getType().isScalarOrVec1() && type.isVector()) || + (node->getType().isVector() && type.isScalar()) || + (node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + return node; +} + +bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const +{ + // integral promotions + if (to == EbtInt) { + switch(from) { + case EbtInt8: + case EbtInt16: + case EbtUint8: + case EbtUint16: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const +{ + // floating-point promotions + if (to == EbtDouble) { + switch(from) { + case EbtBFloat16: + case EbtFloat16: + case EbtFloat: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const +{ + switch (from) { + case EbtInt: + switch(to) { + case EbtUint: + return version >= 400 || getSource() == EShSourceHlsl; + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint: + switch(to) { + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt8: + switch (to) { + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint8: + switch (to) { + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt16: + switch(to) { + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint16: + switch(to) { + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt64: + if (to == EbtUint64) { + return true; + } + break; + default: + break; + } + return false; +} + +bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const +{ + if (to == EbtFloat && (from == EbtFloat16 || from == EbtBFloat16)) { + return true; + } else { + return false; + } +} + +bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const +{ + switch (from) { + case EbtInt: + case EbtUint: + switch(to) { + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + switch (to) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; + case EbtInt64: + case EbtUint64: + if (to == EbtDouble) { + return true; + } + break; + default: + break; + } + return false; +} + +// +// See if the 'from' type is allowed to be implicitly converted to the +// 'to' type. This is not about vector/array/struct, only about basic type. +// +bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const +{ + if ((isEsProfile() && version < 310 ) || version == 110) + return false; + + if (from == to) + return true; + + // TODO: Move more policies into language-specific handlers. + // Some languages allow more general (or potentially, more specific) conversions under some conditions. + if (getSource() == EShSourceHlsl) { + const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool); + const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool); + + if (fromConvertable && toConvertable) { + switch (op) { + case EOpAndAssign: // assignments can perform arbitrary conversions + case EOpInclusiveOrAssign: // ... + case EOpExclusiveOrAssign: // ... + case EOpAssign: // ... + case EOpAddAssign: // ... + case EOpSubAssign: // ... + case EOpMulAssign: // ... + case EOpVectorTimesScalarAssign: // ... + case EOpMatrixTimesScalarAssign: // ... + case EOpDivAssign: // ... + case EOpModAssign: // ... + case EOpReturn: // function returns can also perform arbitrary conversions + case EOpFunctionCall: // conversion of a calling parameter + case EOpLogicalNot: + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpConstructStruct: + return true; + default: + break; + } + } + } + + if (getSource() == EShSourceHlsl) { + // HLSL + if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat)) + return true; + } else { + // GLSL + if (isIntegralPromotion(from, to) || + isFPPromotion(from, to) || + isIntegralConversion(from, to) || + isFPConversion(from, to) || + isFPIntegralConversion(from, to)) { + + if (numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int32) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int64) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float32) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float64)) { + return true; + } + } + } + + if (isEsProfile()) { + switch (to) { + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); + default: + return false; + } + default: + return false; + } + } else { + switch (to) { + case EbtDouble: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtFloat: + return version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64); + case EbtInt16: + case EbtUint16: + return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && + numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + case EbtFloat16: + return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && + numericFeatures.contains(TNumericFeatures::gpu_shader_half_float); + case EbtBFloat16: + return true; + default: + return false; + } + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + return true; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + case EbtFloat16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || + getSource() == EShSourceHlsl; + case EbtBFloat16: + return true; + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return version >= 400 || getSource() == EShSourceHlsl || IsRequestedExtension(E_GL_ARB_gpu_shader5); + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtInt: + switch (from) { + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtUint64: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + return true; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtInt64: + switch (from) { + case EbtInt: + return true; + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtFloat16: + switch (from) { + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + break; + } + return false; + case EbtUint16: + switch (from) { + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + break; + } + return false; + default: + return false; + } + } + + return false; +} + +static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) +{ + switch(sintType) { + case EbtInt8: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt16: + switch(uintType) { + case EbtUint8: + return true; + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt: + switch(uintType) { + case EbtUint8: + case EbtUint16: + return true; + case EbtUint: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt64: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + return true; + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + default: + assert(false); + return false; + } +} + + +static TBasicType getCorrespondingUnsignedType(TBasicType type) +{ + switch(type) { + case EbtInt8: + return EbtUint8; + case EbtInt16: + return EbtUint16; + case EbtInt: + return EbtUint; + case EbtInt64: + return EbtUint64; + default: + assert(false); + return EbtNumTypes; + } +} + +// Implements the following rules +// - If either operand has type float64_t or derived from float64_t, +// the other shall be converted to float64_t or derived type. +// - Otherwise, if either operand has type float32_t or derived from +// float32_t, the other shall be converted to float32_t or derived type. +// - Otherwise, if either operand has type float16_t or derived from +// float16_t, the other shall be converted to float16_t or derived type. +// - Otherwise, if both operands have integer types the following rules +// shall be applied to the operands: +// - If both operands have the same type, no further conversion +// is needed. +// - Otherwise, if both operands have signed integer types or both +// have unsigned integer types, the operand with the type of lesser +// integer conversion rank shall be converted to the type of the +// operand with greater rank. +// - Otherwise, if the operand that has unsigned integer type has rank +// greater than or equal to the rank of the type of the other +// operand, the operand with signed integer type shall be converted +// to the type of the operand with unsigned integer type. +// - Otherwise, if the type of the operand with signed integer type can +// represent all of the values of the type of the operand with +// unsigned integer type, the operand with unsigned integer type +// shall be converted to the type of the operand with signed +// integer type. +// - Otherwise, both operands shall be converted to the unsigned +// integer type corresponding to the type of the operand with signed +// integer type. + +std::tuple TIntermediate::getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const +{ + TBasicType res0 = EbtNumTypes; + TBasicType res1 = EbtNumTypes; + + if ((isEsProfile() && + (version < 310 || !numericFeatures.contains(TNumericFeatures::shader_implicit_conversions))) || + version == 110) + return std::make_tuple(res0, res1); + + if (getSource() == EShSourceHlsl) { + if (canImplicitlyPromote(type1, type0, op)) { + res0 = type0; + res1 = type0; + } else if (canImplicitlyPromote(type0, type1, op)) { + res0 = type1; + res1 = type1; + } + return std::make_tuple(res0, res1); + } + + if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) || + (type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) { + res0 = EbtDouble; + res1 = EbtDouble; + } else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) || + (type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) { + res0 = EbtFloat; + res1 = EbtFloat; + } else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) || + (type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) { + res0 = EbtFloat16; + res1 = EbtFloat16; + } else if ((type0 == EbtBFloat16 && canImplicitlyPromote(type1, EbtBFloat16, op)) || + (type1 == EbtBFloat16 && canImplicitlyPromote(type0, EbtBFloat16, op)) ) { + res0 = EbtBFloat16; + res1 = EbtBFloat16; + } else if (isTypeInt(type0) && isTypeInt(type1) && + (canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) { + if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) || + (isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) { + if (getTypeRank(type0) < getTypeRank(type1)) { + res0 = type1; + res1 = type1; + } else { + res0 = type0; + res1 = type0; + } + } else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) { + res0 = type0; + res1 = type0; + } else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) { + res0 = type1; + res1 = type1; + } else if (isTypeSignedInt(type0)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) { + res0 = type0; + res1 = type0; + } else { + res0 = getCorrespondingUnsignedType(type0); + res1 = getCorrespondingUnsignedType(type0); + } + } else if (isTypeSignedInt(type1)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) { + res0 = type1; + res1 = type1; + } else { + res0 = getCorrespondingUnsignedType(type1); + res1 = getCorrespondingUnsignedType(type1); + } + } + } + + return std::make_tuple(res0, res1); +} + +// +// Given a type, find what operation would fully construct it. +// +TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const +{ + TOperator op = EOpNull; + + if (type.getQualifier().isNonUniform()) + return EOpConstructNonuniform; + + if (type.isCoopMatNV()) + return EOpConstructCooperativeMatrixNV; + + if (type.isCoopMatKHR()) + return EOpConstructCooperativeMatrixKHR; + + if (type.isCoopVecNV()) + return EOpConstructCooperativeVectorNV; + + switch (type.getBasicType()) { + case EbtStruct: + op = EOpConstructStruct; + break; + case EbtSampler: + if (type.getSampler().isCombined()) + op = EOpConstructTextureSampler; + break; + case EbtFloat: + if (type.isMatrix()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat2x2; break; + case 3: op = EOpConstructMat2x3; break; + case 4: op = EOpConstructMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat3x2; break; + case 3: op = EOpConstructMat3x3; break; + case 4: op = EOpConstructMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat4x2; break; + case 3: op = EOpConstructMat4x3; break; + case 4: op = EOpConstructMat4x4; break; + default: break; // some compilers want this + } + break; + default: break; // some compilers want this + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructFloat; break; + case 2: op = EOpConstructVec2; break; + case 3: op = EOpConstructVec3; break; + case 4: op = EOpConstructVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat2x2; break; + case 3: op = EOpConstructIMat2x3; break; + case 4: op = EOpConstructIMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat3x2; break; + case 3: op = EOpConstructIMat3x3; break; + case 4: op = EOpConstructIMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat4x2; break; + case 3: op = EOpConstructIMat4x3; break; + case 4: op = EOpConstructIMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt; break; + case 2: op = EOpConstructIVec2; break; + case 3: op = EOpConstructIVec3; break; + case 4: op = EOpConstructIVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtUint: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat2x2; break; + case 3: op = EOpConstructUMat2x3; break; + case 4: op = EOpConstructUMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat3x2; break; + case 3: op = EOpConstructUMat3x3; break; + case 4: op = EOpConstructUMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat4x2; break; + case 3: op = EOpConstructUMat4x3; break; + case 4: op = EOpConstructUMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint; break; + case 2: op = EOpConstructUVec2; break; + case 3: op = EOpConstructUVec3; break; + case 4: op = EOpConstructUVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtBool: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat2x2; break; + case 3: op = EOpConstructBMat2x3; break; + case 4: op = EOpConstructBMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat3x2; break; + case 3: op = EOpConstructBMat3x3; break; + case 4: op = EOpConstructBMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat4x2; break; + case 3: op = EOpConstructBMat4x3; break; + case 4: op = EOpConstructBMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructBool; break; + case 2: op = EOpConstructBVec2; break; + case 3: op = EOpConstructBVec3; break; + case 4: op = EOpConstructBVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtDouble: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat2x2; break; + case 3: op = EOpConstructDMat2x3; break; + case 4: op = EOpConstructDMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat3x2; break; + case 3: op = EOpConstructDMat3x3; break; + case 4: op = EOpConstructDMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat4x2; break; + case 3: op = EOpConstructDMat4x3; break; + case 4: op = EOpConstructDMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructDouble; break; + case 2: op = EOpConstructDVec2; break; + case 3: op = EOpConstructDVec3; break; + case 4: op = EOpConstructDVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtFloat16: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat2x2; break; + case 3: op = EOpConstructF16Mat2x3; break; + case 4: op = EOpConstructF16Mat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat3x2; break; + case 3: op = EOpConstructF16Mat3x3; break; + case 4: op = EOpConstructF16Mat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat4x2; break; + case 3: op = EOpConstructF16Mat4x3; break; + case 4: op = EOpConstructF16Mat4x4; break; + default: break; // some compilers want this + } + break; + } + } + else { + switch (type.getVectorSize()) { + case 1: op = EOpConstructFloat16; break; + case 2: op = EOpConstructF16Vec2; break; + case 3: op = EOpConstructF16Vec3; break; + case 4: op = EOpConstructF16Vec4; break; + default: break; // some compilers want this + } + } + break; + case EbtBFloat16: + switch (type.getVectorSize()) { + case 1: op = EOpConstructBFloat16; break; + case 2: op = EOpConstructBF16Vec2; break; + case 3: op = EOpConstructBF16Vec3; break; + case 4: op = EOpConstructBF16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt8; break; + case 2: op = EOpConstructI8Vec2; break; + case 3: op = EOpConstructI8Vec3; break; + case 4: op = EOpConstructI8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint8; break; + case 2: op = EOpConstructU8Vec2; break; + case 3: op = EOpConstructU8Vec3; break; + case 4: op = EOpConstructU8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt16; break; + case 2: op = EOpConstructI16Vec2; break; + case 3: op = EOpConstructI16Vec3; break; + case 4: op = EOpConstructI16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint16; break; + case 2: op = EOpConstructU16Vec2; break; + case 3: op = EOpConstructU16Vec3; break; + case 4: op = EOpConstructU16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt64; break; + case 2: op = EOpConstructI64Vec2; break; + case 3: op = EOpConstructI64Vec3; break; + case 4: op = EOpConstructI64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint64; break; + case 2: op = EOpConstructU64Vec2; break; + case 3: op = EOpConstructU64Vec3; break; + case 4: op = EOpConstructU64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtReference: + op = EOpConstructReference; + break; + + case EbtAccStruct: + op = EOpConstructAccStruct; + break; + default: + break; + } + + return op; +} + +// +// Safe way to combine two nodes into an aggregate. Works with null pointers, +// a node that's not a aggregate yet, etc. +// +// Returns the resulting aggregate, unless nullptr was passed in for +// both existing nodes. +// +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right) +{ + if (left == nullptr && right == nullptr) + return nullptr; + + TIntermAggregate* aggNode = nullptr; + if (left != nullptr) + aggNode = left->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + aggNode = new TIntermAggregate; + if (left != nullptr) + aggNode->getSequence().push_back(left); + } + + if (right != nullptr) + aggNode->getSequence().push_back(right); + + return aggNode; +} + +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = growAggregate(left, right); + if (aggNode) + aggNode->setLoc(loc); + + return aggNode; +} + +TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right) +{ + if (left == nullptr && right == nullptr) + return nullptr; + + TIntermAggregate* aggNode = nullptr; + if (left != nullptr) + aggNode = left->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + aggNode = new TIntermAggregate; + if (left != nullptr) + aggNode->getSequence().push_back(left); + } + + TIntermAggregate* rhsagg = right->getAsAggregate(); + if (rhsagg == nullptr || rhsagg->getOp() != EOpNull) + aggNode->getSequence().push_back(right); + else + aggNode->getSequence().insert(aggNode->getSequence().end(), + rhsagg->getSequence().begin(), + rhsagg->getSequence().end()); + + return aggNode; +} + +TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = mergeAggregate(left, right); + if (aggNode) + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Turn an existing node into an aggregate. +// +// Returns an aggregate, unless nullptr was passed in for the existing node. +// +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(node->getLoc()); + + return aggNode; +} + +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Make an aggregate with an empty sequence. +// +TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->setLoc(loc); + + return aggNode; +} + +// +// For "if" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are in the +// nodePair. +// +// Returns the selection node created. +// +TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) +{ + // + // Don't prune the false path for compile-time constants; it's needed + // for static access analysis. + // + + TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); + node->setLoc(loc); + + return node; +} + +TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) +{ + // However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators + // ... are not included in the operators that can create a constant expression. + // + // if (left->getType().getQualifier().storage == EvqConst && + // right->getType().getQualifier().storage == EvqConst) { + + // return right; + //} + + TIntermTyped *commaAggregate = growAggregate(left, right, loc); + commaAggregate->getAsAggregate()->setOperator(EOpComma); + commaAggregate->setType(right->getType()); + commaAggregate->getWritableType().getQualifier().makeTemporary(); + + return commaAggregate; +} + +TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc) +{ + TIntermMethod* method = new TIntermMethod(object, type, *name); + method->setLoc(loc); + + return method; +} + +// +// For "?:" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are specified +// as separate parameters. For vector 'cond', the true and false +// are not paths, but vectors to mix. +// +// Specialization constant operations include +// - The ternary operator ( ? : ) +// +// Returns the selection node created, or nullptr if one could not be. +// +TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, + const TSourceLoc& loc) +{ + // If it's void, go to the if-then-else selection() + if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) { + TIntermNodePair pair = { trueBlock, falseBlock }; + TIntermSelection* selection = addSelection(cond, pair, loc); + if (getSource() == EShSourceHlsl) + selection->setNoShortCircuit(); + + return selection; + } + + // + // Get compatible types. + // + auto children = addPairConversion(EOpSequence, trueBlock, falseBlock); + trueBlock = std::get<0>(children); + falseBlock = std::get<1>(children); + + if (trueBlock == nullptr || falseBlock == nullptr) + return nullptr; + + // Handle a vector condition as a mix + if (!cond->getType().isScalarOrVec1()) { + TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary, + cond->getType().getVectorSize()); + // smear true/false operands as needed + trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock); + falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // make the mix operation + TIntermAggregate* mix = makeAggregate(loc); + mix = growAggregate(mix, falseBlock); + mix = growAggregate(mix, trueBlock); + mix = growAggregate(mix, cond); + mix->setType(targetVectorType); + mix->setOp(EOpMix); + + return mix; + } + + // Now have a scalar condition... + + // Convert true and false expressions to matching types + addBiShapeConversion(EOpMix, trueBlock, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // Eliminate the selection when the condition is a scalar and all operands are constant. + if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) { + if (cond->getAsConstantUnion()->getConstArray()[0].getBConst()) + return trueBlock; + else + return falseBlock; + } + + // + // Make a selection node. + // + TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); + node->setLoc(loc); + node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision); + + if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) || + (cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() && + falseBlock->getQualifier().isConstant())) + node->getQualifier().makeSpecConstant(); + else + node->getQualifier().makeTemporary(); + + if (getSource() == EShSourceHlsl) + node->setNoShortCircuit(); + + return node; +} + +// +// Constant terminal nodes. Has a union that contains bool, float or int constants +// +// Returns the constant union node created. +// + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const +{ + TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t); + node->getQualifier().storage = EvqConst; + node->setLoc(loc); + if (literal) + node->setLiteral(); + + return node; +} +TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI8Const(i8); + + return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u8); + + return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI16Const(i16); + + return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU16Const(u16); + + return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setIConst(i); + + return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u); + + return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI64Const(i64); + + return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU64Const(u64); + + return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setBConst(b); + + return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const +{ + assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16 || baseType == EbtBFloat16); + + if (isEsProfile() && (baseType == EbtFloat || baseType == EbtFloat16)) { + int exponent = 0; + frexp(d, &exponent); + int minExp = baseType == EbtFloat ? -126 : -14; + int maxExp = baseType == EbtFloat ? 127 : 15; + if (exponent > maxExp) { //overflow, d = inf + d = std::numeric_limits::infinity(); + } else if (exponent < minExp) { //underflow, d = 0.0; + d = 0.0; + } + } + + TConstUnionArray unionArray(1); + unionArray[0].setDConst(d); + + return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setSConst(s); + + return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal); +} + +// Put vector swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc); + sequence.push_back(constIntNode); +} + +// Put matrix swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc); + sequence.push_back(constIntNode); + constIntNode = addConstantUnion(selector.coord2, loc); + sequence.push_back(constIntNode); +} + +// Make an aggregate node that has a sequence of all selectors. +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template +TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc) +{ + TIntermAggregate* node = new TIntermAggregate(EOpSequence); + + node->setLoc(loc); + TIntermSequence &sequenceVector = node->getSequence(); + + for (int i = 0; i < selector.size(); i++) + pushSelector(sequenceVector, selector[i], loc); + + return node; +} + +// +// Follow the left branches down to the root of an l-value +// expression (just "." and []). +// +// Return the base of the l-value (where following indexing quits working). +// Return nullptr if a chain following dereferences cannot be followed. +// +// 'swizzleOkay' says whether or not it is okay to consider a swizzle +// a valid part of the dereference chain. +// +// 'bufferReferenceOk' says if type is buffer_reference, the routine will stop to find the most left node. +// +// 'proc' is an optional function to run on each node that is processed during the traversal. 'proc' must +// return true to continue the traversal, or false to end the traversal early. +// + +const TIntermTyped* TIntermediate::traverseLValueBase(const TIntermTyped* node, bool swizzleOkay, + bool bufferReferenceOk, + std::function proc) +{ + do { + const TIntermBinary* binary = node->getAsBinaryNode(); + if (binary == nullptr) { + if (proc) { + proc(*node); + } + return node; + } + TOperator op = binary->getOp(); + if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && + op != EOpMatrixSwizzle) + return nullptr; + if (!swizzleOkay) { + if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle) + return nullptr; + if ((op == EOpIndexDirect || op == EOpIndexIndirect) && + (binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) && + !binary->getLeft()->getType().isArray()) + return nullptr; + } + if (proc) { + if (!proc(*node)) { + return node; + } + } + node = binary->getLeft(); + if (bufferReferenceOk && node->isReference()) + return node; + } while (true); +} + +// +// Create while and do-while loop nodes. +// +TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, + const TSourceLoc& loc) +{ + TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + return node; +} + +// +// Create a for-loop sequence. +// +TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, + TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) +{ + node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + // make a sequence of the initializer and statement, but try to reuse the + // aggregate already created for whatever is in the initializer, if there is one + TIntermAggregate* loopSequence = (initializer == nullptr || + initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc) + : initializer->getAsAggregate(); + if (loopSequence != nullptr && (loopSequence->getOp() == EOpSequence || loopSequence->getOp() == EOpScope)) + loopSequence->setOp(EOpNull); + loopSequence = growAggregate(loopSequence, node); + loopSequence->setOperator(getDebugInfo() ? EOpScope : EOpSequence); + + return loopSequence; +} + +// +// Add branches. +// +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc) +{ + return addBranch(branchOp, nullptr, loc); +} + +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc) +{ + TIntermBranch* node = new TIntermBranch(branchOp, expression); + node->setLoc(loc); + + return node; +} + +// Propagate precision from formal function return type to actual return type, +// and on to its subtree. +void TIntermBranch::updatePrecision(TPrecisionQualifier parentPrecision) +{ + TIntermTyped* exp = getExpression(); + if (exp == nullptr) + return; + + if (exp->getBasicType() == EbtInt || exp->getBasicType() == EbtUint || + exp->getBasicType() == EbtFloat) { + if (parentPrecision != EpqNone && exp->getQualifier().precision == EpqNone) { + exp->propagatePrecision(parentPrecision); + } + } +} + +// +// This is to be executed after the final root is put on top by the parsing +// process. +// +bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) +{ + if (root == nullptr) + return true; + + // Finish off the top-level sequence + TIntermAggregate* aggRoot = root->getAsAggregate(); + if (aggRoot && aggRoot->getOp() == EOpNull) + aggRoot->setOperator(EOpSequence); + + // Propagate 'noContraction' label in backward from 'precise' variables. + glslang::PropagateNoContraction(*this); + + switch (textureSamplerTransformMode) { + case EShTexSampTransKeep: + break; + case EShTexSampTransUpgradeTextureRemoveSampler: + performTextureUpgradeAndSamplerRemovalTransformation(root); + break; + case EShTexSampTransCount: + assert(0); + break; + } + + return true; +} + +void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable) +{ + // Add top-level nodes for declarations that must be checked cross + // compilation unit by a linker, yet might not have been referenced + // by the AST. + // + // Almost entirely, translation of symbols is driven by what's present + // in the AST traversal, not by translating the symbol table. + // + // However, there are some special cases: + // - From the specification: "Special built-in inputs gl_VertexID and + // gl_InstanceID are also considered active vertex attributes." + // - Linker-based type mismatch error reporting needs to see all + // uniforms/ins/outs variables and blocks. + // - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active. + // + + // if (ftransformUsed) { + // TODO: 1.1 lowering functionality: track ftransform() usage + // addSymbolLinkageNode(root, symbolTable, "gl_Vertex"); + // addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix"); + //} + + if (language == EShLangVertex) { + addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID"); + if ((version < 140 && requestedExtensions.find(E_GL_EXT_draw_instanced) != requestedExtensions.end()) || version >= 140) + addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID"); + } + + // Add a child to the root node for the linker objects + linkage->setOperator(EOpLinkerObjects); + treeRoot = growAggregate(treeRoot, linkage); +} + +// +// Add the given name or symbol to the list of nodes at the end of the tree used +// for link-time checking and external linkage. +// + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol) + addSymbolLinkageNode(linkage, *symbol->getAsVariable()); +} + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol) +{ + const TVariable* variable = symbol.getAsVariable(); + if (! variable) { + // This must be a member of an anonymous block, and we need to add the whole block + const TAnonMember* anon = symbol.getAsAnonMember(); + variable = &anon->getAnonContainer(); + } + TIntermSymbol* node = addSymbol(*variable); + linkage = growAggregate(linkage, node); +} + +// +// Add a caller->callee relationship to the call graph. +// Assumes the strings are unique per signature. +// +void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee) +{ + // Duplicates are okay, but faster to not keep them, and they come grouped by caller, + // as long as new ones are push on the same end we check on for duplicates + for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller != caller) + break; + if (call->callee == callee) + return; + } + + callGraph.emplace_front(caller, callee); +} + +// +// This deletes the tree. +// +void TIntermediate::removeTree() +{ + if (treeRoot) + RemoveAllTreeNodes(treeRoot); +} + +// +// Implement the part of KHR_vulkan_glsl that lists the set of operations +// that can result in a specialization constant operation. +// +// "5.x Specialization Constant Operations" +// +// Only some operations discussed in this section may be applied to a +// specialization constant and still yield a result that is as +// specialization constant. The operations allowed are listed below. +// When a specialization constant is operated on with one of these +// operators and with another constant or specialization constant, the +// result is implicitly a specialization constant. +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// - allowed implicit conversions of the above +// - swizzles (e.g., foo.yx) +// - The following when applied to integer or unsigned integer types: +// * unary negative ( - ) +// * binary operations ( + , - , * , / , % ) +// * shift ( <<, >> ) +// * bitwise operations ( & , | , ^ ) +// - The following when applied to integer or unsigned integer scalar types: +// * comparison ( == , != , > , >= , < , <= ) +// - The following when applied to the Boolean scalar type: +// * not ( ! ) +// * logical operations ( && , || , ^^ ) +// * comparison ( == , != )" +// +// This function just handles binary and unary nodes. Construction +// rules are handled in construction paths that are not covered by the unary +// and binary paths, while required conversions will still show up here +// as unary converters in the from a construction operator. +// +bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const +{ + // The operations resulting in floating point are quite limited + // (However, some floating-point operations result in bool, like ">", + // so are handled later.) + if (node.getType().isFloatingDomain()) { + if (IsOpNumericConv(node.getOp()) && + isTypeFloat(node.getType().getBasicType()) && + isTypeFloat(node.getAsUnaryNode()->getOperand()->getAsTyped()->getType().getBasicType())) { + return true; + } + switch (node.getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + return true; + default: + return false; + } + } + + // Check for floating-point arguments + if (const TIntermBinary* bin = node.getAsBinaryNode()) + if (bin->getLeft() ->getType().isFloatingDomain() || + bin->getRight()->getType().isFloatingDomain()) + return false; + + // So, for now, we can assume everything left is non-floating-point... + + if (IsOpNumericConv(node.getOp())) { + TBasicType srcType = node.getAsUnaryNode()->getOperand()->getAsTyped()->getType().getBasicType(); + TBasicType dstType = node.getType().getBasicType(); + if ((isTypeInt(srcType) || srcType == EbtBool) && + (isTypeInt(dstType) || dstType == EbtBool)) { + return true; + } + } + + // Now check for integer/bool-based operations + switch (node.getOp()) { + + // dereference/swizzle + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + + // unary operations + case EOpNegative: + case EOpLogicalNot: + case EOpBitwiseNot: + + // binary operations + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpVectorTimesScalar: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + return true; + default: + return false; + } +} + +// Is the operation one that must propagate nonuniform? +bool TIntermediate::isNonuniformPropagating(TOperator op) const +{ + // "* All Operators in Section 5.1 (Operators), except for assignment, + // arithmetic assignment, and sequence + // * Component selection in Section 5.5 + // * Matrix components in Section 5.6 + // * Structure and Array Operations in Section 5.7, except for the length + // method." + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + + case EOpNegative: + case EOpLogicalNot: + case EOpVectorLogicalNot: + case EOpBitwiseNot: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + return true; + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////// +// +// Member functions of the nodes used for building the tree. +// +//////////////////////////////////////////////////////////////// + +// +// Say whether or not an operation node changes the value of a variable. +// +// Returns true if state is modified. +// +bool TIntermOperator::modifiesState() const +{ + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesMatrixAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpMatrixTimesMatrixAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + return true; + default: + return false; + } +} + +// +// returns true if the operator is for one of the constructors +// +bool TIntermOperator::isConstructor() const +{ + return op > EOpConstructGuardStart && op < EOpConstructGuardEnd; +} + +// +// Make sure the type of an operator is appropriate for its +// combination of operation and operand type. This will invoke +// promoteUnary, promoteBinary, etc as needed. +// +// Returns false if nothing makes sense. +// +bool TIntermediate::promote(TIntermOperator* node) +{ + if (node == nullptr) + return false; + + if (node->getAsUnaryNode()) + return promoteUnary(*node->getAsUnaryNode()); + + if (node->getAsBinaryNode()) + return promoteBinary(*node->getAsBinaryNode()); + + if (node->getAsAggregate()) + return promoteAggregate(*node->getAsAggregate()); + + return false; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteUnary(TIntermUnary& node) +{ + const TOperator op = node.getOp(); + TIntermTyped* operand = node.getOperand(); + + switch (op) { + case EOpLogicalNot: + // Convert operand to a boolean type + if (operand->getBasicType() != EbtBool) { + // Add constructor to boolean type. If that fails, we can't do it, so return false. + TIntermTyped* converted = addConversion(op, TType(EbtBool), operand); + if (converted == nullptr) + return false; + + // Use the result of converting the node to a bool. + node.setOperand(operand = converted); // also updates stack variable + } + break; + case EOpBitwiseNot: + if (!isTypeInt(operand->getBasicType())) + return false; + break; + case EOpNegative: + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + if (!isTypeInt(operand->getBasicType()) && + operand->getBasicType() != EbtFloat && + operand->getBasicType() != EbtFloat16 && + operand->getBasicType() != EbtDouble) + + return false; + break; + default: + // HLSL uses this path for initial function signature finding for built-ins + // taking a single argument, which generally don't participate in + // operator-based type promotion (type conversion will occur later). + // For now, scalar argument cases are relying on the setType() call below. + if (getSource() == EShSourceHlsl) + break; + + // GLSL only allows integer arguments for the cases identified above in the + // case statements. + if (operand->getBasicType() != EbtFloat) + return false; + } + + node.setType(operand->getType()); + node.getWritableType().getQualifier().makeTemporary(); + + return true; +} + +// Propagate precision qualifiers *up* from children to parent. +void TIntermUnary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || + getBasicType() == EbtFloat) { + if (operand->getQualifier().precision > getQualifier().precision) + getQualifier().precision = operand->getQualifier().precision; + } +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteBinary(TIntermBinary& node) +{ + TOperator op = node.getOp(); + TIntermTyped* left = node.getLeft(); + TIntermTyped* right = node.getRight(); + + // Arrays and structures have to be exact matches. + if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct) + && left->getType() != right->getType()) + return false; + + // Base assumption: just make the type the same as the left + // operand. Only deviations from this will be coded. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + // Composite and opaque types don't having pending operator changes, e.g., + // array, structure, and samplers. Just establish final type and correctness. + if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) { + switch (op) { + case EOpEqual: + case EOpNotEqual: + if (left->getBasicType() == EbtSampler) { + // can't compare samplers + return false; + } else { + // Promote to conditional + node.setType(TType(EbtBool)); + } + + return true; + + case EOpAssign: + // Keep type from above + + return true; + + default: + return false; + } + } + + // + // We now have only scalars, vectors, and matrices to worry about. + // + + // HLSL implicitly promotes bool -> int for numeric operations. + // (Implicit conversions to make the operands match each other's types were already done.) + if (getSource() == EShSourceHlsl && + (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) { + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpRightShift: + case EOpLeftShift: + + case EOpMod: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + if (left->getBasicType() == EbtBool) + left = createConversion(EbtInt, left); + if (right->getBasicType() == EbtBool) + right = createConversion(EbtInt, right); + if (left == nullptr || right == nullptr) + return false; + node.setLeft(left); + node.setRight(right); + + // Update the original base assumption on result type.. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + break; + + default: + break; + } + } + + // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that) + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + // Relational comparisons need numeric types and will promote to scalar Boolean. + if (left->getBasicType() == EbtBool) + return false; + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpEqual: + case EOpNotEqual: + if (getSource() == EShSourceHlsl) { + const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize()); + + // In HLSL, == or != on vectors means component-wise comparison. + if (resultWidth > 1) { + op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual; + node.setOp(op); + } + + node.setType(TType(EbtBool, EvqTemporary, resultWidth)); + } else { + // All the above comparisons result in a bool (but not the vector compares) + node.setType(TType(EbtBool)); + } + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + // logical ops operate only on Booleans or vectors of Booleans. + if (left->getBasicType() != EbtBool || left->isMatrix()) + return false; + + if (getSource() == EShSourceGlsl) { + // logical ops operate only on scalar Booleans and will promote to scalar Boolean. + if (left->isVector()) + return false; + } + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + if (getSource() == EShSourceHlsl) + break; + + // Check for integer-only operands. + if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType())) + return false; + if (left->isMatrix() || right->isMatrix()) + return false; + + break; + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + // check for non-Boolean operands + if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool) + return false; + break; + + default: + break; + } + + // Compare left and right, and finish with the cases where the operand types must match + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + return left->getType() == right->getType(); + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAdd: + case EOpSub: + case EOpDiv: + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + // Quick out in case the types do match + if (left->getType() == right->getType()) + return true; + + [[fallthrough]]; + + case EOpMul: + case EOpMulAssign: + // At least the basic type has to match + if (left->getBasicType() != right->getBasicType()) + return false; + break; + + default: + break; + } + + if (left->getType().isCoopMat() || right->getType().isCoopMat()) { + // Operations on two cooperative matrices must have identical types + if (left->getType().isCoopMat() && right->getType().isCoopMat() && + left->getType() != right->getType()) { + return false; + } + switch (op) { + case EOpMul: + case EOpMulAssign: + // Mul not supported in NV_cooperative_matrix + if (left->getType().isCoopMatNV() && right->getType().isCoopMatNV()) { + return false; + } + // NV_cooperative_matrix supports MulAssign is for mat*=scalar only. + // KHR_cooperative_matrix supports it for mat*=mat as well. + if (op == EOpMulAssign && right->getType().isCoopMatNV()) { + return false; + } + // Use MatrixTimesScalar if either operand is not a matrix. Otherwise use Mul. + if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { + node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar); + } + // In case of scalar*matrix, take the result type from the matrix. + if (right->getType().isCoopMat()) { + node.setType(right->getType()); + } + return true; + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpAssign: + // These require both to be cooperative matrices + if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { + return false; + } + return true; + default: + break; + } + return false; + } + + if (left->getType().isCoopVecNV() || right->getType().isCoopVecNV()) { + // Operations on two cooperative vectors must have identical types + if (left->getType().isCoopVecNV() && right->getType().isCoopVecNV() && + left->getType() != right->getType()) { + return false; + } + switch (op) { + case EOpMul: + case EOpMulAssign: + // Use VectorTimesScalar if either operand is not a vector. Otherwise use Mul. + if (!left->getType().isCoopVecNV() || !right->getType().isCoopVecNV()) { + node.setOp(op == EOpMulAssign ? EOpVectorTimesScalarAssign : EOpVectorTimesScalar); + } + // In case of scalar*vector, take the result type from the vector. + if (right->getType().isCoopVecNV()) { + node.setType(right->getType()); + } + return true; + case EOpLeftShift: + case EOpLeftShiftAssign: + case EOpRightShift: + case EOpRightShiftAssign: + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpAssign: + // These require both to be cooperative vectors + if (!left->getType().isCoopVecNV() || !right->getType().isCoopVecNV()) { + return false; + } + return true; + default: + break; + } + return false; + } + + // Finish handling the case, for all ops, where both operands are scalars. + if (left->isScalar() && right->isScalar()) + return true; + + // Finish handling the case, for all ops, where there are two vectors of different sizes + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1) + return false; + + // + // We now have a mix of scalars, vectors, or matrices, for non-relational operations. + // + + // Can these two operands be combined, what is the resulting type? + TBasicType basicType = left->getBasicType(); + switch (op) { + case EOpMul: + if (!left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows()) + return false; + node.setOp(op = EOpVectorTimesMatrix); + node.setType(TType(basicType, EvqTemporary, right->getMatrixCols())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows())); + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + if (left->getMatrixCols() != right->getVectorSize()) + return false; + node.setOp(op = EOpMatrixTimesVector); + node.setType(TType(basicType, EvqTemporary, left->getMatrixRows())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrix); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows())); + } else if (! left->isMatrix() && ! right->isMatrix()) { + if (left->isVector() && right->isVector()) { + ; // leave as component product + } else if (left->isVector() || right->isVector()) { + node.setOp(op = EOpVectorTimesScalar); + if (right->isVector()) + node.setType(TType(basicType, EvqTemporary, right->getVectorSize())); + } + } else { + return false; + } + break; + case EOpMulAssign: + if (! left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols()) + return false; + node.setOp(op = EOpVectorTimesMatrixAssign); + } else { + return false; + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + return false; + } else { + node.setOp(op = EOpMatrixTimesScalarAssign); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrixAssign); + } else if (!left->isMatrix() && !right->isMatrix()) { + if (left->isVector() && right->isVector()) { + // leave as component product + } else if (left->isVector() || right->isVector()) { + if (! left->isVector()) + return false; + node.setOp(op = EOpVectorTimesScalarAssign); + } + } else { + return false; + } + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize())) + return false; + break; + + case EOpAssign: + if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()) + return false; + [[fallthrough]]; + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMod: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + if ((left->isMatrix() && right->isVector()) || + (left->isVector() && right->isMatrix()) || + left->getBasicType() != right->getBasicType()) + return false; + if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())) + return false; + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize()) + return false; + if (right->isVector() || right->isMatrix()) { + node.getWritableType().shallowCopy(right->getType()); + node.getWritableType().getQualifier().makeTemporary(); + } + break; + + default: + return false; + } + + // + // One more check for assignment. + // + switch (op) { + // The resulting type has to match the left operand. + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + if (node.getType() != left->getType()) + return false; + break; + default: + break; + } + + return true; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteAggregate(TIntermAggregate& node) +{ + TOperator op = node.getOp(); + TIntermSequence& args = node.getSequence(); + const int numArgs = static_cast(args.size()); + + // Presently, only hlsl does intrinsic promotions. + if (getSource() != EShSourceHlsl) + return true; + + // set of opcodes that can be promoted in this manner. + switch (op) { + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + // case EOpFindMSB: TODO: + // case EOpFindLSB: TODO: + case EOpFma: + case EOpMod: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpModf: + // case EOpGenMul: TODO: + case EOpPow: + case EOpReflect: + case EOpRefract: + // case EOpSinCos: TODO: + case EOpSmoothStep: + case EOpStep: + break; + default: + return true; + } + + // TODO: array and struct behavior + + // Try converting all nodes to the given node's type + TIntermSequence convertedArgs(numArgs, nullptr); + + // Try to convert all types to the nonConvArg type. + for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) { + // Try converting all args to this arg's type + for (int convArg = 0; convArg < numArgs; ++convArg) { + convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(), + args[convArg]->getAsTyped()); + } + + // If we successfully converted all the args, use the result. + if (std::all_of(convertedArgs.begin(), convertedArgs.end(), + [](const TIntermNode* node) { return node != nullptr; })) { + + std::swap(args, convertedArgs); + return true; + } + } + + return false; +} + +// Propagate precision qualifiers *up* from children to parent, and then +// back *down* again to the children's subtrees. +void TIntermAggregate::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || + getBasicType() == EbtFloat) { + TPrecisionQualifier maxPrecision = EpqNone; + TIntermSequence operands = getSequence(); + for (unsigned int i = 0; i < operands.size(); ++i) { + TIntermTyped* typedNode = operands[i]->getAsTyped(); + assert(typedNode); + maxPrecision = std::max(maxPrecision, typedNode->getQualifier().precision); + } + getQualifier().precision = maxPrecision; + for (unsigned int i = 0; i < operands.size(); ++i) { + TIntermTyped* typedNode = operands[i]->getAsTyped(); + assert(typedNode); + typedNode->propagatePrecision(maxPrecision); + } + } +} + +// Propagate precision qualifiers *up* from children to parent, and then +// back *down* again to the children's subtrees. +void TIntermBinary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || + getBasicType() == EbtFloat) { + if (op == EOpRightShift || op == EOpLeftShift) { + // For shifts get precision from left side only and thus no need to propagate + getQualifier().precision = left->getQualifier().precision; + } else { + getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision); + if (getQualifier().precision != EpqNone) { + left->propagatePrecision(getQualifier().precision); + right->propagatePrecision(getQualifier().precision); + } + } + } +} + +// Recursively propagate precision qualifiers *down* the subtree of the current node, +// until reaching a node that already has a precision qualifier or otherwise does +// not participate in precision propagation. +void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) +{ + if (getQualifier().precision != EpqNone || + (getBasicType() != EbtInt && getBasicType() != EbtUint && + getBasicType() != EbtFloat && getBasicType() != EbtFloat16)) + return; + + getQualifier().precision = newPrecision; + + TIntermBinary* binaryNode = getAsBinaryNode(); + if (binaryNode) { + binaryNode->getLeft()->propagatePrecision(newPrecision); + binaryNode->getRight()->propagatePrecision(newPrecision); + + return; + } + + TIntermUnary* unaryNode = getAsUnaryNode(); + if (unaryNode) { + unaryNode->getOperand()->propagatePrecision(newPrecision); + + return; + } + + TIntermAggregate* aggregateNode = getAsAggregate(); + if (aggregateNode) { + TIntermSequence operands = aggregateNode->getSequence(); + for (unsigned int i = 0; i < operands.size(); ++i) { + TIntermTyped* typedNode = operands[i]->getAsTyped(); + if (! typedNode) + break; + typedNode->propagatePrecision(newPrecision); + } + + return; + } + + TIntermSelection* selectionNode = getAsSelectionNode(); + if (selectionNode) { + TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped(); + if (typedNode) { + typedNode->propagatePrecision(newPrecision); + typedNode = selectionNode->getFalseBlock()->getAsTyped(); + if (typedNode) + typedNode->propagatePrecision(newPrecision); + } + + return; + } +} + +TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const +{ + const TConstUnionArray& rightUnionArray = node->getConstArray(); + int size = node->getType().computeNumComponents(); + + TConstUnionArray leftUnionArray(size); + + for (int i=0; i < size; i++) { + +#define PROMOTE(Set, CType, Get) leftUnionArray[i].Set(static_cast(rightUnionArray[i].Get())) +#define PROMOTE_TO_BOOL(Get) leftUnionArray[i].setBConst(rightUnionArray[i].Get() != 0) + +#define TO_ALL(Get) \ + switch (promoteTo) { \ + case EbtBFloat16: PROMOTE(setDConst, double, Get); break; \ + case EbtFloat16: PROMOTE(setDConst, double, Get); break; \ + case EbtFloat: PROMOTE(setDConst, double, Get); break; \ + case EbtDouble: PROMOTE(setDConst, double, Get); break; \ + case EbtInt8: PROMOTE(setI8Const, signed char, Get); break; \ + case EbtInt16: PROMOTE(setI16Const, short, Get); break; \ + case EbtInt: PROMOTE(setIConst, int, Get); break; \ + case EbtInt64: PROMOTE(setI64Const, long long, Get); break; \ + case EbtUint8: PROMOTE(setU8Const, unsigned char, Get); break; \ + case EbtUint16: PROMOTE(setU16Const, unsigned short, Get); break; \ + case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ + case EbtUint64: PROMOTE(setU64Const, unsigned long long, Get); break; \ + case EbtBool: PROMOTE_TO_BOOL(Get); break; \ + default: return node; \ + } + + switch (node->getType().getBasicType()) { + case EbtFloat: TO_ALL(getDConst); break; + case EbtInt: TO_ALL(getIConst); break; + case EbtUint: TO_ALL(getUConst); break; + case EbtBool: TO_ALL(getBConst); break; + case EbtFloat16: TO_ALL(getDConst); break; + case EbtBFloat16: TO_ALL(getDConst); break; + case EbtDouble: TO_ALL(getDConst); break; + case EbtInt8: TO_ALL(getI8Const); break; + case EbtInt16: TO_ALL(getI16Const); break; + case EbtInt64: TO_ALL(getI64Const); break; + case EbtUint8: TO_ALL(getU8Const); break; + case EbtUint16: TO_ALL(getU16Const); break; + case EbtUint64: TO_ALL(getU64Const); break; + default: return node; + } + } + + const TType& t = node->getType(); + + return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()), + node->getLoc()); +} + +void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable) +{ + assert(pragmaTable == nullptr); + pragmaTable = new TPragmaTable; + *pragmaTable = pTable; +} + +// If either node is a specialization constant, while the other is +// a constant (or specialization constant), the result is still +// a specialization constant. +bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2) +{ + return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) || + (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant()); +} + +struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { + void visitSymbol(TIntermSymbol* symbol) override { + if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) { + symbol->getWritableType().getSampler().setCombined(true); + } + } + bool visitAggregate(TVisit, TIntermAggregate* ag) override { + using namespace std; + TIntermSequence& seq = ag->getSequence(); + TQualifierList& qual = ag->getQualifierList(); + + // qual and seq are indexed using the same indices, so we have to modify both in lock-step + assert(seq.size() == qual.size() || qual.empty()); + + size_t write = 0; + for (size_t i = 0; i < seq.size(); ++i) { + TIntermSymbol* symbol = seq[i]->getAsSymbolNode(); + if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) { + // remove pure sampler variables + continue; + } + + TIntermNode* result = seq[i]; + + // replace constructors with sampler/textures + TIntermAggregate *constructor = seq[i]->getAsAggregate(); + if (constructor && constructor->getOp() == EOpConstructTextureSampler) { + if (!constructor->getSequence().empty()) + result = constructor->getSequence()[0]; + } + + // write new node & qualifier + seq[write] = result; + if (!qual.empty()) + qual[write] = qual[i]; + write++; + } + + seq.resize(write); + if (!qual.empty()) + qual.resize(write); + + return true; + } +}; + +void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) +{ + TextureUpgradeAndSamplerRemovalTransform transform; + root->traverse(&transform); +} + +const char* TIntermediate::getResourceName(TResourceType res) +{ + switch (res) { + case EResSampler: return "shift-sampler-binding"; + case EResTexture: return "shift-texture-binding"; + case EResImage: return "shift-image-binding"; + case EResUbo: return "shift-UBO-binding"; + case EResSsbo: return "shift-ssbo-binding"; + case EResUav: return "shift-uav-binding"; + default: + assert(0); // internal error: should only be called with valid resource types. + return nullptr; + } +} + + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/LiveTraverser.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/LiveTraverser.h new file mode 100644 index 00000000..0e9d748c --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/LiveTraverser.h @@ -0,0 +1,209 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include "../Include/Common.h" +#include "reflection.h" +#include "localintermediate.h" + +#include "gl_types.h" + +#include +#include + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing function-call nodes to push live functions onto the stack of functions to process +// - processing selection nodes to trim semantically dead code +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// This can be derived from to implement reflection database traversers or +// binding mappers: anything that wants to traverse the live subset of the tree. +// + +class TLiveTraverser : public TIntermTraverser { +public: + TLiveTraverser(const TIntermediate& i, bool traverseAll = false, + bool preVisit = true, bool inVisit = false, bool postVisit = false) : + TIntermTraverser(preVisit, inVisit, postVisit), + intermediate(i), traverseAll(traverseAll) + { } + + // + // Given a function name, find its subroot in the tree, and push it onto the stack of + // functions left to process. + // + void pushFunction(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) { + destinations.push_back(candidate); + break; + } + } + } + + void pushGlobalReference(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpSequence && + candidate->getSequence().size() == 1 && + candidate->getSequence()[0]->getAsBinaryNode()) { + TIntermBinary* binary = candidate->getSequence()[0]->getAsBinaryNode(); + TIntermSymbol* symbol = binary->getLeft()->getAsSymbolNode(); + if (symbol && symbol->getQualifier().storage == EvqGlobal && + symbol->getName() == name) { + destinations.push_back(candidate); + break; + } + } + } + } + + typedef std::list TDestinationStack; + TDestinationStack destinations; + +protected: + // To catch which function calls are not dead, and hence which functions must be visited. + virtual bool visitAggregate(TVisit, TIntermAggregate* node) + { + if (!traverseAll) + if (node->getOp() == EOpFunctionCall) + addFunctionCall(node); + + return true; // traverse this subtree + } + + // To prune semantically dead paths. + virtual bool visitSelection(TVisit /* visit */, TIntermSelection* node) + { + if (traverseAll) + return true; // traverse all code + + TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); + if (constant) { + // cull the path that is dead + if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + + return false; // don't traverse any more, we did it all above + } else + return true; // traverse the whole subtree + } + + // To prune semantically dead paths in switch statements with constant expressions. + virtual bool visitSwitch(TVisit /* visit */, TIntermSwitch* node) + { + if (traverseAll) + return true; // traverse all code + + TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); + if (constant) { + TConstUnion switchValue = constant->getConstArray()[0]; + int liveBranch = -1; + const auto& body = node->getBody()->getSequence(); + for (unsigned int i = 0; i < body.size(); ++i) { + if (body[i]->getAsBranchNode()) { + if (body[i]->getAsBranchNode()->getFlowOp() == glslang::EOpCase) { + TConstUnion caseValue = + body[i]->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0]; + if (switchValue == caseValue.getIConst()) { + liveBranch = (int)i; + break; + } + } else if (body[i]->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) { + liveBranch = (int)i; + } + } + } + if (liveBranch != -1) { + for (int i = liveBranch; i < (int)body.size(); ++i) { + if (body[i]->getAsAggregate()) { + for (auto* inst : body[i]->getAsAggregate()->getSequence()) { + if (inst->getAsBranchNode() && (inst->getAsBranchNode()->getFlowOp() == glslang::EOpBreak)) + return false; // found and traversed the live case(s) + inst->traverse(this); + } + } + } + } + return false; // finished traversing all cases + } else + return true; // traverse the whole subtree + } + + // Track live functions as well as uniforms, so that we don't visit dead functions + // and only visit each function once. + void addFunctionCall(TIntermAggregate* call) + { + // just use the map to ensure we process each function at most once + if (liveFunctions.find(call->getName()) == liveFunctions.end()) { + liveFunctions.insert(call->getName()); + pushFunction(call->getName()); + } + } + + void addGlobalReference(const TString& name) + { + // just use the map to ensure we process each global at most once + if (liveGlobals.find(name) == liveGlobals.end()) { + liveGlobals.insert(name); + pushGlobalReference(name); + } + } + + const TIntermediate& intermediate; + typedef std::unordered_set TLiveFunctions; + TLiveFunctions liveFunctions; + typedef std::unordered_set TLiveGlobals; + TLiveGlobals liveGlobals; + bool traverseAll; + +private: + // prevent copy & copy construct + TLiveTraverser(TLiveTraverser&); + TLiveTraverser& operator=(TLiveTraverser&); +}; + +} // namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseContextBase.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseContextBase.cpp new file mode 100644 index 00000000..986b9419 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseContextBase.cpp @@ -0,0 +1,756 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Implement the TParseContextBase class. + +#include + +#include "ParseHelper.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +// +// Used to output syntax, parsing, and semantic errors. +// + +void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason, + const char* szToken, + const char* szExtraInfoFormat, + TPrefixType prefix, va_list args) +{ + const int maxSize = MaxTokenLength + 200; + char szExtraInfo[maxSize]; + + safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args); + + infoSink.info.prefix(prefix); + infoSink.info.location(loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n"; + + if (prefix == EPrefixError) { + ++numErrors; + } +} + +void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (messages & EShMsgOnlyPreprocessor) + return; + // If enhanced msg readability, only print one error + if (messages & EShMsgEnhanced && numErrors > 0) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (suppressWarnings()) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqConst: message = "can't modify a const"; break; + case EvqConstReadOnly: message = "can't modify a const"; break; + case EvqUniform: message = "can't modify a uniform"; break; + case EvqBuffer: + if (node->getQualifier().isReadOnly()) + message = "can't modify a readonly buffer"; + if (node->getQualifier().isShaderRecord()) + message = "can't modify a shaderrecordnv qualified buffer"; + break; + case EvqHitAttr: + if (language != EShLangIntersect) + message = "cannot modify hitAttributeNV in this stage"; + break; + + default: + // + // Type that can't be written to? + // + switch (node->getBasicType()) { + case EbtSampler: + if (extensionTurnedOn(E_GL_ARB_bindless_texture) == false) + message = "can't modify a sampler"; + break; + case EbtVoid: + message = "can't modify void"; + break; + case EbtAtomicUint: + message = "can't modify an atomic_uint"; + break; + case EbtAccStruct: + message = "can't modify accelerationStructureNV"; + break; + case EbtRayQuery: + message = "can't modify rayQueryEXT"; + break; + case EbtHitObjectNV: + message = "can't modify hitObjectNV"; + break; + default: + break; + } + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + { + if (binaryNode) { + switch (binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: // fall through + case EOpIndexDirectStruct: // fall through + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + return lValueErrorCheck(loc, op, binaryNode->getLeft()); + default: + break; + } + error(loc, " l-value required", op, "", ""); + + return true; + } + return false; + } + + // + // If we get here, we have an error and a message. + // + const TIntermTyped* leftMostTypeNode = TIntermediate::traverseLValueBase(node, true); + + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + if (binaryNode && binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct) + if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName())) + error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str(), message); + else + error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getName().c_str(), message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + if (! node) + return; + + TIntermBinary* binaryNode = node->getAsBinaryNode(); + const TIntermSymbol* symNode = node->getAsSymbolNode(); + + if (node->getQualifier().isWriteOnly()) { + const TIntermTyped* leftMostTypeNode = TIntermediate::traverseLValueBase(node, true); + + if (symNode != nullptr) + error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str()); + else if (binaryNode && + (binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct || + binaryNode->getAsOperator()->getOp() == EOpIndexDirect)) + if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName())) + error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str()); + else + error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getName().c_str()); + else + error(loc, "can't read from writeonly object: ", op, ""); + + } else { + if (binaryNode) { + switch (binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + rValueErrorCheck(loc, op, binaryNode->getLeft()); + break; + default: + break; + } + } + } +} + +// Add 'symbol' to the list of deferred linkage symbols, which +// are later processed in finish(), at which point the symbol +// must still be valid. +// It is okay if the symbol's type will be subsequently edited; +// the modifications will be tracked. +// Order is preserved, to avoid creating novel forward references. +void TParseContextBase::trackLinkage(TSymbol& symbol) +{ + if (!parsingBuiltins) + linkageSymbols.push_back(&symbol); +} + +// Ensure index is in bounds, correct if necessary. +// Give an error if not. +void TParseContextBase::checkIndex(const TSourceLoc& loc, const TType& type, int& index) +{ + const auto sizeIsSpecializationExpression = [&type]() { + return type.containsSpecializationSize() && + type.getArraySizes()->getOuterNode() != nullptr && + type.getArraySizes()->getOuterNode()->getAsSymbolNode() == nullptr; }; + + if (index < 0) { + error(loc, "", "[", "index out of range '%d'", index); + index = 0; + } else if (type.isArray()) { + if (type.isSizedArray() && !sizeIsSpecializationExpression() && + index >= type.getOuterArraySize()) { + error(loc, "", "[", "array index out of range '%d'", index); + index = type.getOuterArraySize() - 1; + } + } else if (type.isVector()) { + if (index >= type.getVectorSize()) { + error(loc, "", "[", "vector index out of range '%d'", index); + index = type.getVectorSize() - 1; + } + } else if (type.isMatrix()) { + if (index >= type.getMatrixCols()) { + error(loc, "", "[", "matrix index out of range '%d'", index); + index = type.getMatrixCols() - 1; + } + } else if (type.isCoopVecNV()) { + if (index >= type.computeNumComponents()) { + error(loc, "", "[", "cooperative vector index out of range '%d'", index); + index = type.computeNumComponents() - 1; + } + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes already sharing it (non-shallow type), +// or adopting its full type after being edited (shallow type). +void TParseContextBase::makeEditable(TSymbol*& symbol) +{ + // copyUp() does a deep copy of the type. + symbol = symbolTable.copyUp(symbol); + + // Save it (deferred, so it can be edited first) in the AST for linker use. + if (symbol) + trackLinkage(*symbol); +} + +// Return a writable version of the variable 'name'. +// +// Return nullptr if 'name' is not found. This should mean +// something is seriously wrong (e.g., compiler asking self for +// built-in that doesn't exist). +TVariable* TParseContextBase::getEditableVariable(const char* name) +{ + bool builtIn; + TSymbol* symbol = symbolTable.find(name, &builtIn); + + assert(symbol != nullptr); + if (symbol == nullptr) + return nullptr; + + if (builtIn) + makeEditable(symbol); + + return symbol->getAsVariable(); +} + +// Select the best matching function for 'call' from 'candidateList'. +// +// Assumptions +// +// There is no exact match, so a selection algorithm needs to run. That is, the +// language-specific handler should check for exact match first, to +// decide what to do, before calling this selector. +// +// Input +// +// * list of candidate signatures to select from +// * the call +// * a predicate function convertible(from, to) that says whether or not type +// 'from' can implicitly convert to type 'to' (it includes the case of what +// the calling language would consider a matching type with no conversion +// needed) +// * a predicate function better(from1, from2, to1, to2) that says whether or +// not a conversion from <-> to2 is considered better than a conversion +// from <-> to1 (both in and out directions need testing, as declared by the +// formal parameter) +// +// Output +// +// * best matching candidate (or none, if no viable candidates found) +// * whether there was a tie for the best match (ambiguous overload selection, +// caller's choice for how to report) +// +const TFunction* TParseContextBase::selectFunction( + const TVector candidateList, + const TFunction& call, + std::function convertible, + std::function better, + /* output */ bool& tie) +{ +// +// Operation +// +// 1. Prune the input list of candidates down to a list of viable candidates, +// where each viable candidate has +// +// * at least as many parameters as there are calling arguments, with any +// remaining parameters being optional or having default values +// * each parameter is true under convertible(A, B), where A is the calling +// type for in and B is the formal type, and in addition, for out B is the +// calling type and A is the formal type +// +// 2. If there are no viable candidates, return with no match. +// +// 3. If there is only one viable candidate, it is the best match. +// +// 4. If there are multiple viable candidates, select the first viable candidate +// as the incumbent. Compare the incumbent to the next viable candidate, and if +// that candidate is better (bullets below), make it the incumbent. Repeat, with +// a linear walk through the viable candidate list. The final incumbent will be +// returned as the best match. A viable candidate is better than the incumbent if +// +// * it has a function argument with a better(...) conversion than the incumbent, +// for all directions needed by in and out +// * the incumbent has no argument with a better(...) conversion then the +// candidate, for either in or out (as needed) +// +// 5. Check for ambiguity by comparing the best match against all other viable +// candidates. If any other viable candidate has a function argument with a +// better(...) conversion than the best candidate (for either in or out +// directions), return that there was a tie for best. +// + + tie = false; + + // 1. prune to viable... + TVector viableCandidates; + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& candidate = *(*it); + + // to even be a potential match, number of arguments must be >= the number of + // fixed (non-default) parameters, and <= the total (including parameter with defaults). + if (call.getParamCount() < candidate.getFixedParamCount() || + call.getParamCount() > candidate.getParamCount()) + continue; + + // see if arguments are convertible + bool viable = true; + + // The call can have fewer parameters than the candidate, if some have defaults. + const int paramCount = std::min(call.getParamCount(), candidate.getParamCount()); + for (int param = 0; param < paramCount; ++param) { + if (candidate[param].type->getQualifier().isParamInput()) { + if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + if (candidate[param].type->getQualifier().isParamOutput()) { + if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + } + + if (viable) + viableCandidates.push_back(&candidate); + } + + // 2. none viable... + if (viableCandidates.size() == 0) + return nullptr; + + // 3. only one viable... + if (viableCandidates.size() == 1) + return viableCandidates.front(); + + // 4. find best... + const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 better than call -> can1 for any parameter + bool hasBetterParam = false; + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type)) { + hasBetterParam = true; + break; + } + } + return hasBetterParam; + }; + + const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 equivalent to call -> can1 for all the call parameters? + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type) || + better(*call[param].type, *can2[param].type, *can1[param].type)) + return false; + } + return true; + }; + + const TFunction* incumbent = viableCandidates.front(); + for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) { + const TFunction& candidate = *(*it); + if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent)) + incumbent = &candidate; + } + + // 5. ambiguity... + for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) { + if (incumbent == *it) + continue; + const TFunction& candidate = *(*it); + + // In the case of default parameters, it may have an identical initial set, which is + // also ambiguous + if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate)) + tie = true; + } + + return incumbent; +} + +// +// Look at a '.' field selector string and change it into numerical selectors +// for a vector or scalar. +// +// Always return some form of swizzle, so the result is always usable. +// +void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize, + TSwizzleSelectors& selector) +{ + // Too long? + if (compString.size() > MaxSwizzleSelectors) + error(loc, "vector swizzle too long", compString.c_str(), ""); + + // Use this to test that all swizzle characters are from the same swizzle-namespace-set + enum { + exyzw, + ergba, + estpq, + } fieldSet[MaxSwizzleSelectors]; + + // Decode the swizzle string. + int size = std::min(MaxSwizzleSelectors, (int)compString.size()); + for (int i = 0; i < size; ++i) { + switch (compString[i]) { + case 'x': + selector.push_back(0); + fieldSet[i] = exyzw; + break; + case 'r': + selector.push_back(0); + fieldSet[i] = ergba; + break; + case 's': + selector.push_back(0); + fieldSet[i] = estpq; + break; + + case 'y': + selector.push_back(1); + fieldSet[i] = exyzw; + break; + case 'g': + selector.push_back(1); + fieldSet[i] = ergba; + break; + case 't': + selector.push_back(1); + fieldSet[i] = estpq; + break; + + case 'z': + selector.push_back(2); + fieldSet[i] = exyzw; + break; + case 'b': + selector.push_back(2); + fieldSet[i] = ergba; + break; + case 'p': + selector.push_back(2); + fieldSet[i] = estpq; + break; + + case 'w': + selector.push_back(3); + fieldSet[i] = exyzw; + break; + case 'a': + selector.push_back(3); + fieldSet[i] = ergba; + break; + case 'q': + selector.push_back(3); + fieldSet[i] = estpq; + break; + + default: + error(loc, "unknown swizzle selection", compString.c_str(), ""); + break; + } + } + + // Additional error checking. + for (int i = 0; i < selector.size(); ++i) { + if (selector[i] >= vecSize) { + error(loc, "vector swizzle selection out of range", compString.c_str(), ""); + selector.resize(i); + break; + } + + if (i > 0 && fieldSet[i] != fieldSet[i-1]) { + error(loc, "vector swizzle selectors not from the same set", compString.c_str(), ""); + selector.resize(i); + break; + } + } + + // Ensure it is valid. + if (selector.size() == 0) + selector.push_back(0); +} + +// +// Make the passed-in variable information become a member of the +// global uniform block. If this doesn't exist yet, make it. +// +void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + // Make the global block, if not yet made. + if (globalUniformBlock == nullptr) { + TQualifier blockQualifier; + blockQualifier.clear(); + blockQualifier.storage = EvqUniform; + TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier); + setUniformBlockDefaults(blockType); + globalUniformBlock = new TVariable(NewPoolTString(""), blockType, true); + firstNewMember = 0; + } + + // Update with binding and set + globalUniformBlock->getWritableType().getQualifier().layoutBinding = globalUniformBinding; + globalUniformBlock->getWritableType().getQualifier().layoutSet = globalUniformSet; + + // Check for declarations of this default uniform that already exist due to other compilation units. + TSymbol* symbol = symbolTable.find(memberName); + if (symbol) { + if (memberType != symbol->getType()) { + TString err; + err += "Redeclaration: already declared as \"" + symbol->getType().getCompleteString() + "\""; + error(loc, "", memberName.c_str(), err.c_str()); + } + return; + } + + // Add the requested member as a member to the global block. + TType* type = new TType; + type->shallowCopy(memberType); + type->setFieldName(memberName); + if (typeList) + type->setStruct(typeList); + TTypeLoc typeLoc = {type, loc}; + globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc); + + // Insert into the symbol table. + if (firstNewMember == 0) { + // This is the first request; we need a normal symbol table insert + if (symbolTable.insert(*globalUniformBlock)) + trackLinkage(*globalUniformBlock); + else + error(loc, "failed to insert the global constant buffer", "uniform", ""); + } else { + // This is a follow-on request; we need to amend the first insert + symbolTable.amend(*globalUniformBlock, firstNewMember); + } + + ++firstNewMember; +} + +void TParseContextBase::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) { + // Make the atomic counter block, if not yet made. + const auto &at = atomicCounterBuffers.find(binding); + if (at == atomicCounterBuffers.end()) { + atomicCounterBuffers.insert({binding, (TVariable*)nullptr }); + atomicCounterBlockFirstNewMember.insert({binding, 0}); + } + + TVariable*& atomicCounterBuffer = atomicCounterBuffers[binding]; + int& bufferNewMember = atomicCounterBlockFirstNewMember[binding]; + + if (atomicCounterBuffer == nullptr) { + TQualifier blockQualifier; + blockQualifier.clear(); + blockQualifier.storage = EvqBuffer; + + char charBuffer[512]; + if (binding != TQualifier::layoutBindingEnd) { + snprintf(charBuffer, 512, "%s_%d", getAtomicCounterBlockName(), binding); + } else { + snprintf(charBuffer, 512, "%s_0", getAtomicCounterBlockName()); + } + + TType blockType(new TTypeList, *NewPoolTString(charBuffer), blockQualifier); + setUniformBlockDefaults(blockType); + blockType.getQualifier().layoutPacking = ElpStd430; + atomicCounterBuffer = new TVariable(NewPoolTString(""), blockType, true); + // If we arn't auto mapping bindings then set the block to use the same + // binding as what the atomic was set to use + if (!intermediate.getAutoMapBindings()) { + atomicCounterBuffer->getWritableType().getQualifier().layoutBinding = binding; + } + bufferNewMember = 0; + + atomicCounterBuffer->getWritableType().getQualifier().layoutSet = atomicCounterBlockSet; + } + + // Add the requested member as a member to the global block. + TType* type = new TType; + type->shallowCopy(memberType); + type->setFieldName(memberName); + if (typeList) + type->setStruct(typeList); + TTypeLoc typeLoc = {type, loc}; + atomicCounterBuffer->getType().getWritableStruct()->push_back(typeLoc); + + // Insert into the symbol table. + if (bufferNewMember == 0) { + // This is the first request; we need a normal symbol table insert + if (symbolTable.insert(*atomicCounterBuffer)) + trackLinkage(*atomicCounterBuffer); + else + error(loc, "failed to insert the global constant buffer", "buffer", ""); + } else { + // This is a follow-on request; we need to amend the first insert + symbolTable.amend(*atomicCounterBuffer, bufferNewMember); + } + + ++bufferNewMember; +} + +void TParseContextBase::finish() +{ + if (parsingBuiltins) + return; + + for (const TString& relaxedSymbol : relaxedSymbols) + { + TSymbol* symbol = symbolTable.find(relaxedSymbol); + TType& type = symbol->getWritableType(); + for (const TTypeLoc& typeLoc : *type.getStruct()) + { + if (typeLoc.type->isOpaque()) + { + typeLoc.type->getSampler() = TSampler{}; + typeLoc.type->setBasicType(EbtInt); + TString fieldName("/*"); + fieldName.append(typeLoc.type->getFieldName()); + fieldName.append("*/"); + typeLoc.type->setFieldName(fieldName); + } + } + } + + // Transfer the linkage symbols to AST nodes, preserving order. + TIntermAggregate* linkage = new TIntermAggregate; + for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i) + intermediate.addSymbolLinkageNode(linkage, **i); + intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.cpp new file mode 100644 index 00000000..ee3bad93 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.cpp @@ -0,0 +1,10568 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017, 2019 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// Modifications Copyright (C) 2024 Ravi Prakash Singh. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "ParseHelper.h" +#include "Initialize.h" +#include "Scan.h" + +#include + +#include "Versions.h" +#include "preprocessor/PpContext.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, + int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint) : + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, + infoSink, forwardCompatible, messages, entryPoint), + inMain(false), + blockName(nullptr), + limits(resources.limits), + atomicUintOffsets(nullptr), anyIndexLimits(false) +{ + // decide whether precision qualifiers should be ignored or respected + if (isEsProfile() || spvVersion.vulkan > 0) { + precisionManager.respectPrecisionQualifiers(); + if (! parsingBuiltins && language == EShLangFragment && !isEsProfile() && spvVersion.vulkan > 0) + precisionManager.warnAboutDefaults(); + } + + setPrecisionDefaults(); + + globalUniformDefaults.clear(); + globalUniformDefaults.layoutMatrix = ElmColumnMajor; + globalUniformDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd140 : ElpShared; + + globalBufferDefaults.clear(); + globalBufferDefaults.layoutMatrix = ElmColumnMajor; + globalBufferDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd430 : ElpShared; + + globalInputDefaults.clear(); + globalOutputDefaults.clear(); + + globalSharedDefaults.clear(); + globalSharedDefaults.layoutMatrix = ElmColumnMajor; + globalSharedDefaults.layoutPacking = ElpStd430; + + // "Shaders in the transform + // feedback capturing mode have an initial global default of + // layout(xfb_buffer = 0) out;" + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) + globalOutputDefaults.layoutXfbBuffer = 0; + + if (language == EShLangGeometry) + globalOutputDefaults.layoutStream = 0; + + if (entryPoint != nullptr && entryPoint->size() > 0 && *entryPoint != "main") + infoSink.info.message(EPrefixError, "Source entry point must be \"main\""); +} + +TParseContext::~TParseContext() +{ + delete [] atomicUintOffsets; +} + +// Set up all default precisions as needed by the current environment. +// Intended just as a TParseContext constructor helper. +void TParseContext::setPrecisionDefaults() +{ + // Set all precision defaults to EpqNone, which is correct for all types + // when not obeying precision qualifiers, and correct for types that don't + // have defaults (thus getting an error on use) when obeying precision + // qualifiers. + + for (int type = 0; type < EbtNumTypes; ++type) + defaultPrecision[type] = EpqNone; + + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqNone; + + // replace with real precision defaults for those that have them + if (obeyPrecisionQualifiers()) { + if (isEsProfile()) { + // Most don't have defaults, a few default to lowp. + TSampler sampler; + sampler.set(EbtFloat, Esd2D); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, EsdCube); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, Esd2D); + sampler.setExternal(true); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + } + + // If we are parsing built-in computational variables/functions, it is meaningful to record + // whether the built-in has no precision qualifier, as that ambiguity + // is used to resolve the precision from the supplied arguments/operands instead. + // So, we don't actually want to replace EpqNone with a default precision for built-ins. + if (! parsingBuiltins) { + if (isEsProfile() && language == EShLangFragment) { + defaultPrecision[EbtInt] = EpqMedium; + defaultPrecision[EbtUint] = EpqMedium; + } else { + defaultPrecision[EbtInt] = EpqHigh; + defaultPrecision[EbtUint] = EpqHigh; + defaultPrecision[EbtFloat] = EpqHigh; + } + + if (!isEsProfile()) { + // Non-ES profile + // All sampler precisions default to highp. + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqHigh; + } + } + + defaultPrecision[EbtSampler] = EpqLow; + defaultPrecision[EbtAtomicUint] = EpqHigh; + } +} + +void TParseContext::setLimits(const TBuiltInResource& r) +{ + resources = r; + intermediate.setLimits(r); + + anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing || + ! limits.generalConstantMatrixVectorIndexing || + ! limits.generalSamplerIndexing || + ! limits.generalUniformIndexing || + ! limits.generalVariableIndexing || + ! limits.generalVaryingIndexing; + + + // "Each binding point tracks its own current default offset for + // inheritance of subsequent variables using the same binding. The initial state of compilation is that all + // binding points have an offset of 0." + atomicUintOffsets = new int[resources.maxAtomicCounterBindings]; + for (int b = 0; b < resources.maxAtomicCounterBindings; ++b) + atomicUintOffsets[b] = 0; +} + +// +// Parse an array of strings using yyparse, going through the +// preprocessor to tokenize the shader strings, then through +// the GLSL scanner. +// +// Returns true for successful acceptance of the shader, false if any errors. +// +bool TParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) +{ + currentScanner = &input; + ppContext.setInput(input, versionWillBeError); + yyparse(this); + + finish(); + + return numErrors == 0; +} + +// This is called from bison when it has a parse (syntax) error +// Note though that to stop cascading errors, we set EOF, which +// will usually cause a syntax error, so be more accurate that +// compilation is terminating. +void TParseContext::parserError(const char* s) +{ + if (! getScanner()->atEndOfInput() || numErrors == 0) + error(getCurrentLoc(), "", "", s, ""); + else + error(getCurrentLoc(), "compilation terminated", "", ""); +} + +void TParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + bool createBlock = globalUniformBlock == nullptr; + + if (createBlock) { + globalUniformBinding = intermediate.getGlobalUniformBinding(); + globalUniformSet = intermediate.getGlobalUniformSet(); + } + + // use base class function to create/expand block + TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, typeList); + + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // check for a block storage override + TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getGlobalUniformBlockName()); + TQualifier& qualifier = globalUniformBlock->getWritableType().getQualifier(); + qualifier.defaultBlock = true; + + if (storageOverride != EbsNone) { + if (createBlock) { + // Remap block storage + qualifier.setBlockStorage(storageOverride); + + // check that the change didn't create errors + blockQualifierCheck(loc, qualifier, false); + } + + // remap meber storage as well + memberType.getQualifier().setBlockStorage(storageOverride); + } + } +} + +void TParseContext::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + bool createBlock = atomicCounterBuffers.find(binding) == atomicCounterBuffers.end(); + + if (createBlock) { + atomicCounterBlockSet = intermediate.getAtomicCounterBlockSet(); + } + + // use base class function to create/expand block + TParseContextBase::growAtomicCounterBlock(binding, loc, memberType, memberName, typeList); + TQualifier& qualifier = atomicCounterBuffers[binding]->getWritableType().getQualifier(); + qualifier.defaultBlock = true; + + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // check for a Block storage override + TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getAtomicCounterBlockName()); + + if (storageOverride != EbsNone) { + if (createBlock) { + // Remap block storage + + qualifier.setBlockStorage(storageOverride); + + // check that the change didn't create errors + blockQualifierCheck(loc, qualifier, false); + } + + // remap meber storage as well + memberType.getQualifier().setBlockStorage(storageOverride); + } + } +} + +const char* TParseContext::getGlobalUniformBlockName() const +{ + const char* name = intermediate.getGlobalUniformBlockName(); + if (std::string(name) == "") + return "gl_DefaultUniformBlock"; + else + return name; +} +void TParseContext::finalizeGlobalUniformBlockLayout(TVariable&) +{ +} +void TParseContext::setUniformBlockDefaults(TType& block) const +{ + block.getQualifier().layoutPacking = ElpStd140; + block.getQualifier().layoutMatrix = ElmColumnMajor; +} + + +const char* TParseContext::getAtomicCounterBlockName() const +{ + const char* name = intermediate.getAtomicCounterBlockName(); + if (std::string(name) == "") + return "gl_AtomicCounterBlock"; + else + return name; +} +void TParseContext::finalizeAtomicCounterBlockLayout(TVariable&) +{ +} + +void TParseContext::setAtomicCounterBlockDefaults(TType& block) const +{ + block.getQualifier().layoutPacking = ElpStd430; + block.getQualifier().layoutMatrix = ElmRowMajor; +} + +void TParseContext::setInvariant(const TSourceLoc& loc, const char* builtin) { + TSymbol* symbol = symbolTable.find(builtin); + if (symbol && symbol->getType().getQualifier().isPipeOutput()) { + if (intermediate.inIoAccessed(builtin)) + warn(loc, "changing qualification after use", "invariant", builtin); + TSymbol* csymbol = symbolTable.copyUp(symbol); + csymbol->getWritableType().getQualifier().invariant = true; + } +} + +void TParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) +{ + if (pragmaCallback) + pragmaCallback(loc.line, tokens); + + if (tokens.size() == 0) + return; + + if (tokens[0].compare("optimize") == 0) { + if (tokens.size() != 4) { + error(loc, "optimize pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'optimize' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.optimize = true; + else if (tokens[2].compare("off") == 0) + contextPragma.optimize = false; + else { + if(relaxedErrors()) + // If an implementation does not recognize the tokens following #pragma, then it will ignore that pragma. + warn(loc, "\"on\" or \"off\" expected after '(' for 'optimize' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'optimize' pragma", "#pragma", ""); + return; + } + } else if (tokens[0].compare("debug") == 0) { + if (tokens.size() != 4) { + error(loc, "debug pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'debug' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.debug = true; + else if (tokens[2].compare("off") == 0) + contextPragma.debug = false; + else { + if(relaxedErrors()) + // If an implementation does not recognize the tokens following #pragma, then it will ignore that pragma. + warn(loc, "\"on\" or \"off\" expected after '(' for 'debug' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'debug' pragma", "#pragma", ""); + return; + } + } else if (spvVersion.spv > 0 && tokens[0].compare("use_storage_buffer") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseStorageBuffer(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_vulkan_memory_model") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseVulkanMemoryModel(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_variable_pointers") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + if (spvVersion.spv < glslang::EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "#pragma use_variable_pointers", ""); + intermediate.setUseVariablePointers(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_replicated_composites") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setReplicatedComposites(); + } else if (tokens[0].compare("once") == 0) { + warn(loc, "not implemented", "#pragma once", ""); + } else if (tokens[0].compare("glslang_binary_double_output") == 0) { + intermediate.setBinaryDoubleOutput(); + } else if (spvVersion.spv > 0 && tokens[0].compare("STDGL") == 0 && + tokens[1].compare("invariant") == 0 && tokens[3].compare("all") == 0) { + intermediate.setInvariantAll(); + // Set all builtin out variables invariant if declared + setInvariant(loc, "gl_Position"); + setInvariant(loc, "gl_PointSize"); + setInvariant(loc, "gl_ClipDistance"); + setInvariant(loc, "gl_CullDistance"); + setInvariant(loc, "gl_TessLevelOuter"); + setInvariant(loc, "gl_TessLevelInner"); + setInvariant(loc, "gl_PrimitiveID"); + setInvariant(loc, "gl_Layer"); + setInvariant(loc, "gl_ViewportIndex"); + setInvariant(loc, "gl_FragDepth"); + setInvariant(loc, "gl_SampleMask"); + setInvariant(loc, "gl_ClipVertex"); + setInvariant(loc, "gl_FrontColor"); + setInvariant(loc, "gl_BackColor"); + setInvariant(loc, "gl_FrontSecondaryColor"); + setInvariant(loc, "gl_BackSecondaryColor"); + setInvariant(loc, "gl_TexCoord"); + setInvariant(loc, "gl_FogFragCoord"); + setInvariant(loc, "gl_FragColor"); + setInvariant(loc, "gl_FragData"); + } +} + +// +// Handle seeing a variable identifier in the grammar. +// +TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string) +{ + TIntermTyped* node = nullptr; + + // Error check for requiring specific extensions present. + if (symbol && symbol->getNumExtensions()) + requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); + + if (symbol && symbol->isReadOnly()) { + // All shared things containing an unsized array must be copied up + // on first use, so that all future references will share its array structure, + // so that editing the implicit size will effect all nodes consuming it, + // and so that editing the implicit size won't change the shared one. + // + // If this is a variable or a block, check it and all it contains, but if this + // is a member of an anonymous block, check the whole block, as the whole block + // will need to be copied up if it contains an unsized array. + // + // This check is being done before the block-name check further down, so guard + // for that too. + if (!symbol->getType().isUnusableName()) { + if (symbol->getType().containsUnsizedArray() || + (symbol->getAsAnonMember() && + symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray())) + makeEditable(symbol); + } + } + + const TVariable* variable; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; + if (anon) { + // It was a member of an anonymous container. + + // Create a subtree for its dereference. + variable = anon->getAnonContainer().getAsVariable(); + TIntermTyped* container = intermediate.addSymbol(*variable, loc); + TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); + node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); + + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().hiddenMember()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); + } else { + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : nullptr; + if (variable) { + if (variable->getType().isUnusableName()) { + error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); + variable = nullptr; + } + + if (language == EShLangMesh && variable) { + TLayoutGeometry primitiveType = intermediate.getOutputPrimitive(); + if ((variable->getMangledName() == "gl_PrimitiveTriangleIndicesEXT" && primitiveType != ElgTriangles) || + (variable->getMangledName() == "gl_PrimitiveLineIndicesEXT" && primitiveType != ElgLines) || + (variable->getMangledName() == "gl_PrimitivePointIndicesEXT" && primitiveType != ElgPoints)) { + error(loc, "cannot be used (output primitive type mismatch)", string->c_str(), ""); + variable = nullptr; + } + } + } else { + if (symbol) + error(loc, "variable name expected", string->c_str(), ""); + } + + // Recovery, if it wasn't found or was not a variable. + if (! variable) { + bool builtIn = false; + TVector candidateList; + symbolTable.findFunctionNameList(*string + "(", candidateList, builtIn); + + // If it's a function, pass the name/mangledName + if (!candidateList.empty() && !builtIn) { + variable = new TVariable(&candidateList[0]->getName(), &candidateList[0]->getMangledName(), TType(EbtFunction)); + } else { + variable = new TVariable(string, TType(EbtVoid)); + } + } + + if (variable->getType().getQualifier().isFrontEndConstant()) + node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); + else + node = intermediate.addSymbol(*variable, loc); + } + + if (variable->getType().getQualifier().isIo()) + intermediate.addIoAccessed(*string); + + if (variable->getType().isReference() && + variable->getType().getQualifier().bufferReferenceNeedsVulkanMemoryModel()) { + intermediate.setUseVulkanMemoryModel(); + } + + return node; +} + +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + int indexValue = 0; + if (index->getQualifier().isFrontEndConstant()) + indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); + + // basic type checks... + variableCheck(base); + + if (! base->isArray() && ! base->isMatrix() && ! base->isVector() && ! base->getType().isCoopMat() && + ! base->isReference() && ! base->getType().isCoopVecNV()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + + // Insert dummy error-recovery result + return intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + + if (!base->isArray() && base->isVector()) { + if (base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "[", "does not operate on types containing float16"); + if (base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "[", "does not operate on types containing (u)int16"); + if (base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "[", "does not operate on types containing (u)int8"); + } + + // check for constant folding + if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) { + // both base and index are front-end constants + checkIndex(loc, base->getType(), indexValue); + return intermediate.foldDereference(base, indexValue, loc); + } + + // at least one of base and index is not a front-end constant variable... + TIntermTyped* result = nullptr; + + if (base->isReference() && ! base->isArray()) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference indexing"); + if (base->getType().getReferentType()->containsUnsizedArray()) { + error(loc, "cannot index reference to buffer containing an unsized array", "", ""); + result = nullptr; + } else { + result = intermediate.addBinaryMath(EOpAdd, base, index, loc); + if (result != nullptr) + result->setType(base->getType()); + } + if (result == nullptr) { + error(loc, "cannot index buffer reference", "", ""); + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + return result; + } + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + handleIoResizeArrayAccess(loc, base); + + if (index->getQualifier().isFrontEndConstant()) + checkIndex(loc, base->getType(), indexValue); + + if (index->getQualifier().isFrontEndConstant()) { + if (base->getType().isUnsizedArray()) { + base->getWritableType().updateImplicitArraySize(indexValue + 1); + base->getWritableType().setImplicitlySized(true); + if (base->getQualifier().builtIn == EbvClipDistance && + indexValue >= resources.maxClipDistances) { + error(loc, "gl_ClipDistance", "[", "array index out of range '%d'", indexValue); + } + else if (base->getQualifier().builtIn == EbvCullDistance && + indexValue >= resources.maxCullDistances) { + error(loc, "gl_CullDistance", "[", "array index out of range '%d'", indexValue); + } + else if (base->getQualifier().builtIn == EbvSampleMask && + indexValue >= (resources.maxSamples + 31) / 32) { + error(loc, "gl_SampleMask", "[", "array index out of range '%d'", indexValue); + } + // For 2D per-view builtin arrays, update the inner dimension size in parent type + if (base->getQualifier().isPerView() && base->getQualifier().builtIn != EbvNone) { + TIntermBinary* binaryNode = base->getAsBinaryNode(); + if (binaryNode) { + TType& leftType = binaryNode->getLeft()->getWritableType(); + TArraySizes& arraySizes = *leftType.getArraySizes(); + assert(arraySizes.getNumDims() == 2); + arraySizes.setDimSize(1, std::max(arraySizes.getDimSize(1), indexValue + 1)); + } + } + } else + checkIndex(loc, base->getType(), indexValue); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else { + if (base->getType().isUnsizedArray()) { + // we have a variable index into an unsized array, which is okay, + // depending on the situation + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable"); + else { + // it is okay for a run-time sized array + checkRuntimeSizable(loc, *base); + } + base->getWritableType().setArrayVariablyIndexed(); + } + if (base->getBasicType() == EbtBlock) { + if (base->getQualifier().storage == EvqBuffer) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing buffer block array"); + else if (base->getQualifier().storage == EvqUniform) + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "variable indexing uniform block array"); + else { + // input/output blocks either don't exist or can't be variably indexed + } + } else if (language == EShLangFragment && base->getQualifier().isPipeOutput() && base->getQualifier().builtIn != EbvSampleMask) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader output array"); + else if (base->getBasicType() == EbtSampler && version >= 130) { + const char* explanation = "variable indexing sampler array"; + requireProfile(base->getLoc(), EEsProfile | ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, explanation); + profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, nullptr, explanation); + } + + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + + // Insert valid dereferenced result type + TType newType(base->getType(), 0); + if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { + newType.getQualifier().storage = EvqConst; + // If base or index is a specialization constant, the result should also be a specialization constant. + if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) { + newType.getQualifier().makeSpecConstant(); + } + } else { + newType.getQualifier().storage = EvqTemporary; + newType.getQualifier().specConstant = false; + } + result->setType(newType); + + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform() || index->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + if (anyIndexLimits) + handleIndexLimits(loc, base, index); + + return result; +} + +// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms +void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index) +{ + if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) || + (! limits.generalUniformIndexing && base->getQualifier().isUniformOrBuffer() && language != EShLangVertex) || + (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) || + (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) || + (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() && + ! base->getType().getQualifier().isPipeInput() && + ! base->getType().getQualifier().isPipeOutput() && + ! base->getType().getQualifier().isConstant()) || + (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || + base->getType().getQualifier().isPipeOutput()))) { + // it's too early to know what the inductive variables are, save it for post processing + needsIndexLimitationChecking.push_back(index); + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes sharing it. +void TParseContext::makeEditable(TSymbol*& symbol) +{ + TParseContextBase::makeEditable(symbol); + + // See if it's tied to IO resizing + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); +} + +// Return true if this is a geometry shader input array or tessellation control output array +// or mesh shader output array. +bool TParseContext::isIoResizeArray(const TType& type) const +{ + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && + ! type.getQualifier().patch) || + (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && + (type.getQualifier().pervertexNV || type.getQualifier().pervertexEXT)) || + (language == EShLangMesh && type.getQualifier().storage == EvqVaryingOut && + !type.getQualifier().perTaskNV)); +} + +// If an array is not isIoResizeArray() but is an io array, make sure it has the right size +void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) +{ + if (! type.isArray() || type.getQualifier().patch || symbolTable.atBuiltInLevel()) + return; + + assert(! isIoResizeArray(type)); + + if (type.getQualifier().storage != EvqVaryingIn || type.getQualifier().patch) + return; + + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + if (type.getOuterArraySize() != resources.maxPatchVertices) { + if (type.isSizedArray()) + error(loc, "tessellation input array size must be gl_MaxPatchVertices or implicitly sized", "[]", ""); + type.changeOuterArraySize(resources.maxPatchVertices); + } + } +} + +// Issue any errors if the non-array object is missing arrayness WRT +// shader I/O that has array requirements. +// All arrayness checking is handled in array paths, this is for +void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (! type.isArray() && ! symbolTable.atBuiltInLevel()) { + if (type.getQualifier().isArrayedIo(language) && !type.getQualifier().layoutPassthrough) + error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str()); + } +} + +// Handle a dereference of a geometry shader input array or tessellation control output array. +// See ioArraySymbolResizeList comment in ParseHelper.h. +// +void TParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base) +{ + TIntermSymbol* symbolNode = base->getAsSymbolNode(); + assert(symbolNode); + if (! symbolNode) + return; + + // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing) + if (symbolNode->getType().isUnsizedArray()) { + int newSize = getIoArrayImplicitSize(symbolNode->getType().getQualifier()); + if (newSize > 0) + symbolNode->getWritableType().changeOuterArraySize(newSize); + } +} + +// If there has been an input primitive declaration (geometry shader) or an output +// number of vertices declaration(tessellation shader), make sure all input array types +// match it in size. Types come either from nodes in the AST or symbols in the +// symbol table. +// +// Types without an array size will be given one. +// Types already having a size that is wrong will get an error. +// +void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnly) +{ + int requiredSize = 0; + TString featureString; + size_t listSize = ioArraySymbolResizeList.size(); + size_t i = 0; + + // If tailOnly = true, only check the last array symbol in the list. + if (tailOnly) { + i = listSize - 1; + } + for (bool firstIteration = true; i < listSize; ++i) { + TType &type = ioArraySymbolResizeList[i]->getWritableType(); + + // As I/O array sizes don't change, fetch requiredSize only once, + // except for mesh shaders which could have different I/O array sizes based on type qualifiers. + if (firstIteration || (language == EShLangMesh)) { + requiredSize = getIoArrayImplicitSize(type.getQualifier(), &featureString); + if (requiredSize == 0) + break; + firstIteration = false; + } + + checkIoArrayConsistency(loc, requiredSize, featureString.c_str(), type, + ioArraySymbolResizeList[i]->getName()); + } +} + +int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString *featureString) const +{ + int expectedSize = 0; + TString str = "unknown"; + unsigned int maxVertices = intermediate.getVertices() != TQualifier::layoutNotSet ? intermediate.getVertices() : 0; + + if (language == EShLangGeometry) { + expectedSize = TQualifier::mapGeometryToSize(intermediate.getInputPrimitive()); + str = TQualifier::getGeometryString(intermediate.getInputPrimitive()); + } + else if (language == EShLangTessControl) { + expectedSize = maxVertices; + str = "vertices"; + } else if (language == EShLangFragment) { + // Number of vertices for Fragment shader is always three. + expectedSize = 3; + str = "vertices"; + } else if (language == EShLangMesh) { + unsigned int maxPrimitives = + intermediate.getPrimitives() != TQualifier::layoutNotSet ? intermediate.getPrimitives() : 0; + if (qualifier.builtIn == EbvPrimitiveIndicesNV) { + expectedSize = maxPrimitives * TQualifier::mapGeometryToSize(intermediate.getOutputPrimitive()); + str = "max_primitives*"; + str += TQualifier::getGeometryString(intermediate.getOutputPrimitive()); + } + else if (qualifier.builtIn == EbvPrimitiveTriangleIndicesEXT || qualifier.builtIn == EbvPrimitiveLineIndicesEXT || + qualifier.builtIn == EbvPrimitivePointIndicesEXT) { + expectedSize = maxPrimitives; + str = "max_primitives"; + } + else if (qualifier.isPerPrimitive()) { + expectedSize = maxPrimitives; + str = "max_primitives"; + } + else { + expectedSize = maxVertices; + str = "max_vertices"; + } + } + if (featureString) + *featureString = str; + return expectedSize; +} + +void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredSize, const char* feature, TType& type, const TString& name) +{ + if (type.isUnsizedArray()) + type.changeOuterArraySize(requiredSize); + else if (type.getOuterArraySize() != requiredSize) { + if (language == EShLangGeometry) + error(loc, "inconsistent input primitive for array size of", feature, name.c_str()); + else if (language == EShLangTessControl) + error(loc, "inconsistent output number of vertices for array size of", feature, name.c_str()); + else if (language == EShLangFragment) { + if (type.getOuterArraySize() > requiredSize) + error(loc, " cannot be greater than 3 for pervertexEXT", feature, name.c_str()); + } + else if (language == EShLangMesh) + error(loc, "inconsistent output array size of", feature, name.c_str()); + else + assert(0); + } +} + +// Handle seeing a binary node with a math operation. +// Returns nullptr if not semantically allowed. +TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) +{ + rValueErrorCheck(loc, str, left->getAsTyped()); + rValueErrorCheck(loc, str, right->getAsTyped()); + + bool allowed = true; + switch (op) { + // TODO: Bring more source language-specific checks up from intermediate.cpp + // to the specific parse helpers for that source language. + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + if (! left->isScalar() || ! right->isScalar()) + allowed = false; + break; + default: + break; + } + + if (((left->getType().contains16BitFloat() || right->getType().contains16BitFloat()) && !float16Arithmetic()) || + ((left->getType().contains16BitInt() || right->getType().contains16BitInt()) && !int16Arithmetic()) || + ((left->getType().contains8BitInt() || right->getType().contains8BitInt()) && !int8Arithmetic()) || + (left->getType().containsBFloat16() || right->getType().containsBFloat16())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) { + if ((left->isReference() || right->isReference())) + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference math"); + result = intermediate.addBinaryMath(op, left, right, loc); + } + + if (result == nullptr) { + bool enhanced = intermediate.getEnhancedMsgs(); + binaryOpError(loc, str, left->getCompleteString(enhanced), right->getCompleteString(enhanced)); + } + + return result; +} + +// Handle seeing a unary node with a math operation. +TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) +{ + rValueErrorCheck(loc, str, childNode); + + bool allowed = true; + if ((childNode->getType().contains16BitFloat() && !float16Arithmetic()) || + (childNode->getType().contains16BitInt() && !int16Arithmetic()) || + (childNode->getType().contains8BitInt() && !int8Arithmetic()) || + (childNode->getType().containsBFloat16())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) + result = intermediate.addUnaryMath(op, childNode, loc); + + if (result) + return result; + else { + bool enhanced = intermediate.getEnhancedMsgs(); + unaryOpError(loc, str, childNode->getCompleteString(enhanced)); + } + + return childNode; +} + +// +// Handle seeing a base.field dereference in the grammar. +// +TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + variableCheck(base); + + // + // .length() can't be resolved until we later see the function-calling syntax. + // Save away the name in the AST for now. Processing is completed in + // handleLengthMethod(). + // + if (field == "length") { + if (base->isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, ".length"); + profileRequires(loc, EEsProfile, 300, nullptr, ".length"); + } else if (base->isVector() || base->isMatrix()) { + const char* feature = ".length() on vectors and matrices"; + requireProfile(loc, ~EEsProfile, feature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, feature); + } else if (!base->getType().isCoopMat() && !base->getType().isCoopVecNV()) { + bool enhanced = intermediate.getEnhancedMsgs(); + error(loc, "does not operate on this type:", field.c_str(), base->getType().getCompleteString(enhanced).c_str()); + return base; + } + + return intermediate.addMethod(base, TType(EbtInt), &field, loc); + } + + // It's not .length() if we get to here. + + if (base->isArray()) { + error(loc, "cannot apply to an array:", ".", field.c_str()); + + return base; + } + + if (base->getType().isCoopMat()) { + error(loc, "cannot apply to a cooperative matrix type:", ".", field.c_str()); + return base; + } + + // It's neither an array nor .length() if we get here, + // leaving swizzles and struct/block dereferences. + + TIntermTyped* result = base; + if ((base->isVector() || base->isScalar()) && + (base->isFloatingDomain() || base->isIntegerDomain() || base->getBasicType() == EbtBool)) { + result = handleDotSwizzle(loc, base, field); + } else if (base->isStruct() || base->isReference()) { + const TTypeList* fields = base->isReference() ? + base->getType().getReferentType()->getStruct() : + base->getType().getStruct(); + bool fieldFound = false; + int member; + for (member = 0; member < (int)fields->size(); ++member) { + if ((*fields)[member].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + + if (fieldFound) { + if (spvVersion.vulkan != 0 && spvVersion.vulkanRelaxed) + result = vkRelaxedRemapDotDereference(loc, *base, *(*fields)[member].type, field); + + if (result == base) + { + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldDereference(base, member, loc); + else { + blockMemberExtensionCheck(loc, base, member, field); + TIntermTyped* index = intermediate.addConstantUnion(member, loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[member].type); + if ((*fields)[member].type->getQualifier().isIo()) + intermediate.addIoAccessed(field); + } + } + + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + } else { + auto baseSymbol = base; + while (baseSymbol->getAsSymbolNode() == nullptr) { + auto binaryNode = baseSymbol->getAsBinaryNode(); + if (binaryNode == nullptr) break; + baseSymbol = binaryNode->getLeft(); + } + if (baseSymbol->getAsSymbolNode() != nullptr) { + TString structName; + structName.append("\'").append(baseSymbol->getAsSymbolNode()->getName().c_str()).append("\'"); + error(loc, "no such field in structure", field.c_str(), structName.c_str()); + } else { + error(loc, "no such field in structure", field.c_str(), ""); + } + } + } else + error(loc, "does not apply to this type:", field.c_str(), + base->getType().getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + + // Propagate noContraction up the dereference chain + if (base->getQualifier().isNoContraction()) + result->getWritableType().getQualifier().setNoContraction(); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + return result; +} + +// +// Handle seeing a base.swizzle, a subset of base.identifier in the grammar. +// +TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + TIntermTyped* result = base; + if (base->isScalar()) { + const char* dotFeature = "scalar swizzle"; + requireProfile(loc, ~EEsProfile, dotFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, dotFeature); + } + + TSwizzleSelectors selectors; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, ".", "can't swizzle types containing (u)int16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8"); + + if (base->isScalar()) { + if (selectors.size() == 1) + return result; + else { + TType type(base->getBasicType(), EvqTemporary, selectors.size()); + // Swizzle operations propagate specialization-constantness + if (base->getQualifier().isSpecConstant()) + type.getQualifier().makeSpecConstant(); + return addConstructor(loc, base, type); + } + } + + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldSwizzle(base, selectors, loc); + else { + if (selectors.size() == 1) { + TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision)); + } else { + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size())); + } + // Swizzle operations propagate specialization-constantness + if (base->getType().getQualifier().isSpecConstant()) + result->getWritableType().getQualifier().makeSpecConstant(); + } + + return result; +} + +void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName) +{ + // a block that needs extension checking is either 'base', or if arrayed, + // one level removed to the left + const TIntermSymbol* baseSymbol = nullptr; + if (base->getAsBinaryNode() == nullptr) + baseSymbol = base->getAsSymbolNode(); + else + baseSymbol = base->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + if (baseSymbol == nullptr) + return; + const TSymbol* symbol = symbolTable.find(baseSymbol->getName()); + if (symbol == nullptr) + return; + const TVariable* variable = symbol->getAsVariable(); + if (variable == nullptr) + return; + if (!variable->hasMemberExtensions()) + return; + + // We now have a variable that is the base of a dot reference + // with members that need extension checking. + if (variable->getNumMemberExtensions(member) > 0) + requireExtensions(loc, variable->getNumMemberExtensions(member), variable->getMemberExtensions(member), memberName.c_str()); +} + +// +// Handle seeing a function declarator in the grammar. This is the precursor +// to recognizing a function prototype or function definition. +// +TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) +{ + // ES can't declare prototypes inside functions + if (! symbolTable.atGlobalLevel()) + requireProfile(loc, ~EEsProfile, "local function declaration"); + + // + // Multiple declarations of the same function name are allowed. + // + // If this is a definition, the definition production code will check for redefinitions + // (we don't know at this point if it's a definition or not). + // + // Redeclarations (full signature match) are allowed. But, return types and parameter qualifiers must also match. + // - except ES 100, which only allows a single prototype + // + // ES 100 does not allow redefining, but does allow overloading of built-in functions. + // ES 300 does not allow redefining or overloading of built-in functions. + // + bool builtIn; + TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); + if (symbol && symbol->getAsFunction() && builtIn) + requireProfile(loc, ~EEsProfile, "redefinition of built-in function"); + // Check the validity of using spirv_literal qualifier + for (int i = 0; i < function.getParamCount(); ++i) { + if (function[i].type->getQualifier().isSpirvLiteral() && function.getBuiltInOp() != EOpSpirvInst) + error(loc, "'spirv_literal' can only be used on functions defined with 'spirv_instruction' for argument", + function.getName().c_str(), "%d", i + 1); + } + + // For function declaration with SPIR-V instruction qualifier, always ignore the built-in function and + // respect this redeclared one. + if (symbol && builtIn && function.getBuiltInOp() == EOpSpirvInst) + symbol = nullptr; + const TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + if (prevDec) { + if (prevDec->isPrototyped() && prototype) + profileRequires(loc, EEsProfile, 300, nullptr, "multiple prototypes for same function"); + if (prevDec->getSpirvInstruction() != function.getSpirvInstruction()) { + error(loc, "overloaded functions must have the same qualifiers", function.getName().c_str(), + "spirv_instruction"); + } + bool parameterTypesDiffer = false; + for (int i = 0; i < prevDec->getParamCount(); ++i) { + if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage) + error(loc, "overloaded functions must have the same parameter storage qualifiers for argument", function[i].type->getStorageQualifierString(), "%d", i+1); + + if ((*prevDec)[i].type->getQualifier().precision != function[i].type->getQualifier().precision) + error(loc, "overloaded functions must have the same parameter precision qualifiers for argument", function[i].type->getPrecisionQualifierString(), "%d", i+1); + + if (*(*prevDec)[i].type != *function[i].type) + parameterTypesDiffer = true; + } + if (!parameterTypesDiffer && prevDec->getType() != function.getType()) + error(loc, "overloaded functions must have the same return type", function.getName().c_str(), ""); + } + + arrayObjectCheck(loc, function.getType(), "array in function return type"); + + if (prototype) { + // All built-in functions are defined, even though they don't have a body. + // Count their prototype as a definition instead. + if (symbolTable.atBuiltInLevel()) + function.setDefined(); + else { + if (prevDec && ! builtIn) + symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const + function.setPrototyped(); + } + } + + // This insert won't actually insert it if it's a duplicate signature, but it will still check for + // other forms of name collisions. + if (! symbolTable.insert(function)) + error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); + + // + // If this is a redeclaration, it could also be a definition, + // in which case, we need to use the parameter names from this one, and not the one that's + // being redeclared. So, pass back this declaration, not the one in the symbol table. + // + return &function; +} + +// +// Handle seeing the function prototype in front of a function definition in the grammar. +// The body is handled after this function returns. +// +TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function) +{ + currentCaller = function.getMangledName(); + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (! prevDec) + error(loc, "can't find function", function.getName().c_str(), ""); + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurrence. + + if (prevDec && prevDec->isDefined()) { + // Then this function already has a body. + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec && ! prevDec->isDefined()) { + prevDec->setDefined(); + + // Remember the return type for later checking for RETURN statements. + currentFunctionType = &(prevDec->getType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + // Check for entry point + if (function.getName().compare(intermediate.getEntryPointName().c_str()) == 0) { + intermediate.setEntryPointMangledName(function.getMangledName().c_str()); + intermediate.incrementEntryPointCount(); + inMain = true; + } else + inMain = false; + + // + // Raise error message if main function takes any parameters or returns anything other than void + // + if (inMain) { + if (function.getParamCount() > 0) + error(loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); + if (function.getType().getBasicType() != EbtVoid) + error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value"); + if (function.getLinkType() != ELinkNone) + error(loc, "main function cannot be exported", "", ""); + } + + // + // New symbol table scope for body of function plus its arguments + // + symbolTable.push(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the HIL, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != nullptr) { + TVariable *variable = new TVariable(param.name, *param.type); + + // Insert the parameters with name in the symbol table. + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + else { + // Transfer ownership of name pointer to symbol table. + param.name = nullptr; + + // Add the parameter to the HIL + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); + } + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); + } + paramNodes->setLinkType(function.getLinkType()); + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + statementNestingLevel = 0; + controlFlowNestingLevel = 0; + postEntryPointReturn = false; + + return paramNodes; +} + +// +// Handle seeing function call syntax in the grammar, which could be any of +// - .length() method +// - constructor +// - a call to a built-in function mapped to an operator +// - a call to a built-in function that will remain a function call (e.g., texturing) +// - user function +// - subroutine call (not implemented yet) +// +TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) +{ + TIntermTyped* result = nullptr; + + if (spvVersion.vulkan != 0 && spvVersion.vulkanRelaxed) { + // allow calls that are invalid in Vulkan Semantics to be invisibily + // remapped to equivalent valid functions + result = vkRelaxedRemapFunctionCall(loc, function, arguments); + if (result) + return result; + } + + if (function->getBuiltInOp() == EOpArrayLength) + result = handleLengthMethod(loc, function, arguments); + else if (function->getBuiltInOp() != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (! constructorError(loc, arguments, *function, function->getBuiltInOp(), type)) { + // + // It's a constructor, of type 'type'. + // + result = addConstructor(loc, arguments, type); + if (result == nullptr) + error(loc, "cannot construct with these arguments", type.getCompleteString(intermediate.getEnhancedMsgs()).c_str(), ""); + } + } else { + // + // Find it in the symbol table. + // + const TFunction* fnCandidate; + bool builtIn {false}; + fnCandidate = findFunction(loc, *function, builtIn); + if (fnCandidate) { + // This is a declared function that might map to + // - a built-in operator, + // - a built-in function not mapped to an operator, or + // - a user function. + + // Error check for a function requiring specific extensions present. + if (builtIn && + (fnCandidate->getBuiltInOp() == EOpSubgroupQuadAll || fnCandidate->getBuiltInOp() == EOpSubgroupQuadAny)) + requireExtensions(loc, 1, &E_GL_EXT_shader_quad_control, fnCandidate->getName().c_str()); + + if (builtIn && fnCandidate->getNumExtensions()) + requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); + + if (builtIn && fnCandidate->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + if (builtIn && (fnCandidate->getBuiltInOp() == EOpTextureFetch || fnCandidate->getBuiltInOp() == EOpTextureQuerySize)) { + if ((*fnCandidate)[0].type->getSampler().isMultiSample() && version <= 140) + requireExtensions(loc, 1, &E_GL_ARB_texture_multisample, fnCandidate->getName().c_str()); + } + if (arguments != nullptr) { + // Make sure qualifications work for these arguments. + TIntermAggregate* aggregate = arguments->getAsAggregate(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermNode* arg = fnCandidate->getParamCount() == 1 ? arguments : (aggregate ? aggregate->getSequence()[i] : arguments); + TQualifier& formalQualifier = (*fnCandidate)[i].type->getQualifier(); + if (formalQualifier.isParamOutput()) { + if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped())) + error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", ""); + } + if (formalQualifier.isSpirvLiteral()) { + if (!arg->getAsTyped()->getQualifier().isFrontEndConstant()) { + error(arguments->getLoc(), + "Non front-end constant expressions cannot be passed for 'spirv_literal' parameters.", + "spirv_literal", ""); + } + } + const TType& argType = arg->getAsTyped()->getType(); + const TQualifier& argQualifier = argType.getQualifier(); + bool containsBindlessSampler = intermediate.getBindlessMode() && argType.containsSampler(); + if (argQualifier.isMemory() && !containsBindlessSampler && (argType.containsOpaque() || argType.isReference())) { + const char* message = "argument cannot drop memory qualifier when passed to formal parameter"; + if (argQualifier.volatil && ! formalQualifier.volatil) + error(arguments->getLoc(), message, "volatile", ""); + if (argQualifier.coherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "coherent", ""); + if (argQualifier.devicecoherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "devicecoherent", ""); + if (argQualifier.queuefamilycoherent && ! (formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "queuefamilycoherent", ""); + if (argQualifier.workgroupcoherent && ! (formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "workgroupcoherent", ""); + if (argQualifier.subgroupcoherent && ! (formalQualifier.subgroupcoherent || formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "subgroupcoherent", ""); + if (argQualifier.readonly && ! formalQualifier.readonly) + error(arguments->getLoc(), message, "readonly", ""); + if (argQualifier.writeonly && ! formalQualifier.writeonly) + error(arguments->getLoc(), message, "writeonly", ""); + if (argQualifier.nontemporal && ! formalQualifier.nontemporal) + error(arguments->getLoc(), message, "nontemporal", ""); + // Don't check 'restrict', it is different than the rest: + // "...but only restrict can be taken away from a calling argument, by a formal parameter that + // lacks the restrict qualifier..." + } + if (!builtIn && argQualifier.getFormat() != formalQualifier.getFormat()) { + // we have mismatched formats, which should only be allowed if writeonly + // and at least one format is unknown + if (!formalQualifier.isWriteOnly() || (formalQualifier.getFormat() != ElfNone && + argQualifier.getFormat() != ElfNone)) + error(arguments->getLoc(), "image formats must match", "format", ""); + } + if (builtIn && arg->getAsTyped()->getType().contains16BitFloat()) + requireFloat16Arithmetic(arguments->getLoc(), "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains16BitInt()) + requireInt16Arithmetic(arguments->getLoc(), "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains8BitInt()) + requireInt8Arithmetic(arguments->getLoc(), "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + + // Check that coopVecOuterProductAccumulateNV vector component types match + if (builtIn && fnCandidate->getBuiltInOp() == EOpCooperativeVectorOuterProductAccumulateNV && + i == 1 && arg->getAsTyped()->getType().getBasicType() != aggregate->getSequence()[0]->getAsTyped()->getType().getBasicType()) + error(arguments->getLoc(), "cooperative vector basic types must match", fnCandidate->getName().c_str(), ""); + + // TODO 4.5 functionality: A shader will fail to compile + // if the value passed to the memargument of an atomic memory function does not correspond to a buffer or + // shared variable. It is acceptable to pass an element of an array or a single component of a vector to the + // memargument of an atomic memory function, as long as the underlying array or vector is a buffer or + // shared variable. + } + + // Convert 'in' arguments + addInputArgumentConversions(*fnCandidate, arguments); // arguments may be modified if it's just a single argument node + } + + if (builtIn && fnCandidate->getBuiltInOp() != EOpNull) { + // A function call mapped to a built-in operation. + result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate); + } else if (fnCandidate->getBuiltInOp() == EOpSpirvInst) { + // When SPIR-V instruction qualifier is specified, the function call is still mapped to a built-in operation. + result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate); + } else { + // This is a function call not mapped to built-in operator. + // It could still be a built-in function, but only if PureOperatorBuiltins == false. + result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(fnCandidate->getMangledName()); + + // this is how we know whether the given function is a built-in function or a user-defined function + // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also + // if builtIn == true, it's definitely a built-in function with EOpNull + if (! builtIn) { + call->setUserDefined(); + if (symbolTable.atGlobalLevel()) { + requireProfile(loc, ~EEsProfile, "calling user function from global scope"); + intermediate.addToCallGraph(infoSink, "main(", fnCandidate->getMangledName()); + } else + intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); + } + + if (builtIn) + nonOpBuiltInCheck(loc, *fnCandidate, *call); + else + userFunctionCallCheck(loc, *call); + } + + // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. + // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. + // Also, build the qualifier list for user function calls, which are always called with an aggregate. + if (result->getAsAggregate()) { + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; + qualifierList.push_back(qual); + } + result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate()); + } + + handleCoopMat2FunctionCall(loc, fnCandidate, result, arguments); + + if (result->getAsTyped()->getType().isCoopVecNV() && + !result->getAsTyped()->getType().isParameterized()) { + if (auto unaryNode = result->getAsUnaryNode()) + result->setType(unaryNode->getOperand()->getAsTyped()->getType()); + else + result->setType(result->getAsAggregate()->getSequence()[0]->getAsTyped()->getType()); + } + } + } + + // generic error recovery + // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades + if (result == nullptr) + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + + return result; +} + +void TParseContext::handleCoopMat2FunctionCall(const TSourceLoc& loc, const TFunction* fnCandidate, TIntermTyped* result, TIntermNode* arguments) +{ + if (arguments && arguments->getAsAggregate()) { + auto &sequence = arguments->getAsAggregate()->getSequence(); + for (uint32_t i = 0; i < sequence.size(); ++i) { + auto param = sequence[i]; + if (param->getAsTyped()->getBasicType() == EbtFunction) { + // Add the function to the callgraph + intermediate.addToCallGraph(infoSink, currentCaller, param->getAsSymbolNode()->getMangledName()); + + // error checking that all parameters are 'const in' + if (fnCandidate->getBuiltInOp() == EOpCooperativeMatrixLoadTensorNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixReduceNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixPerElementOpNV) { + const TFunction* func = symbolTable.find(param->getAsSymbolNode()->getMangledName())->getAsFunction(); + for (int i = 0; i < func->getParamCount(); ++i) { + const TParameter& arg = (*func)[i]; + const TQualifier& formalQualifier = arg.type->getQualifier(); + if (formalQualifier.storage != EvqConstReadOnly) { + error(loc, "function parameters must all be qualified 'const in'", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + } + + // error checking decodeFunc parameters are (reference, uint32_t[], uint32_t[]) + if (fnCandidate->getBuiltInOp() == EOpCooperativeMatrixLoadTensorNV) { + const TFunction* decodeFunc = symbolTable.find(param->getAsSymbolNode()->getMangledName())->getAsFunction(); + + if (decodeFunc->getParamCount() != 3) { + error(loc, "must have three parameters", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + + if ((*decodeFunc)[0].type->getBasicType() != EbtReference) { + error(loc, "first parameter must be buffer reference type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + if ((*decodeFunc)[1].type->getBasicType() != EbtUint || (*decodeFunc)[2].type->getBasicType() != EbtUint) { + error(loc, "coordinate parameters must be uint32_t", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + if (!(*decodeFunc)[1].type->isArray() || !(*decodeFunc)[2].type->isArray()) { + error(loc, "coordinate parameters must be uint32_t", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + + // error checking reduce function has matching parameters + if (fnCandidate->getBuiltInOp() == EOpCooperativeMatrixReduceNV) { + const TFunction* combineOp = symbolTable.find(param->getAsSymbolNode()->getMangledName())->getAsFunction(); + + if (combineOp->getParamCount() != 2) { + error(loc, "must have two parameters", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + + for (int i = 0; i < combineOp->getParamCount(); ++i) { + const TParameter& arg = (*combineOp)[i]; + if (sequence[1]->getAsTyped()->getType().getBasicType() != arg.type->getBasicType()) { + error(loc, "parameter types must match cooperative matrix component type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + if (sequence[1]->getAsTyped()->getType().getBasicType() != combineOp->getType().getBasicType()) { + error(loc, "return type must match cooperative matrix component type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + + // error checking perelement op has correct parameters + if (fnCandidate->getBuiltInOp() == EOpCooperativeMatrixPerElementOpNV) { + const TFunction* elemOp = symbolTable.find(param->getAsSymbolNode()->getMangledName())->getAsFunction(); + + if (sequence[1]->getAsTyped()->getType() != sequence[0]->getAsTyped()->getType()) { + error(loc, "cooperative matrix input and result types must match", "", ""); + } + + if (elemOp->getParamCount() < 3) { + error(loc, "not enough parameters", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } else if (elemOp->getParamCount() != (int)sequence.size()) { + error(loc, "number of parameters must match call to coopMatPerElementNV", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } else { + if ((*elemOp)[0].type->getBasicType() != EbtUint || (*elemOp)[1].type->getBasicType() != EbtUint) { + error(loc, "row/column parameters must be uint32_t", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + + const TParameter& matArg = (*elemOp)[2]; + if (sequence[1]->getAsTyped()->getType().getBasicType() != matArg.type->getBasicType()) { + error(loc, "third parameter must match cooperative matrix component type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + + for (int i = 3; i < elemOp->getParamCount(); ++i) { + const TParameter& arg = (*elemOp)[i]; + if (sequence[i]->getAsTyped()->getType().getBasicType() != arg.type->getBasicType()) { + error(loc, "parameter types must match or be cooperative matrix component type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + if (sequence[1]->getAsTyped()->getType().getBasicType() != elemOp->getType().getBasicType()) { + error(loc, "return type must match cooperative matrix component type", param->getAsSymbolNode()->getMangledName().c_str(), ""); + } + } + } + } + } + } + if ((result->getAsTyped()->getType().isCoopMat() || + result->getAsTyped()->getType().isTensorLayoutNV() || + result->getAsTyped()->getType().isTensorViewNV()) && + !result->getAsTyped()->getType().isParameterized()) { + assert(fnCandidate->getBuiltInOp() == EOpCooperativeMatrixMulAdd || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixMulAddNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixReduceNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixPerElementOpNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixTransposeNV || + fnCandidate->getBuiltInOp() == EOpCreateTensorLayoutNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetDimensionNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetBlockSizeNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetStrideNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSliceNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetClampValueNV || + fnCandidate->getBuiltInOp() == EOpCreateTensorViewNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetDimensionNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetStrideNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetClipNV); + + if (fnCandidate->getBuiltInOp() == EOpCreateTensorLayoutNV) { + + // Convert template parameters to arraySizes/typeParameters + TArraySizes *arraySizes = new TArraySizes; + for (uint32_t i = 0; i < 2; ++i) { + TIntermNode *param {}; + if (arguments->getAsConstantUnion()) { + if (i == 0) { + param = arguments; + } + } else { + assert(arguments->getAsAggregate()); + auto &sequence = arguments->getAsAggregate()->getSequence(); + if (i < sequence.size()) { + param = sequence[i]; + } + } + if (param) { + if (param->getAsTyped()->getType().getQualifier().isSpecConstant()) { + uint32_t value = param->getAsSymbolNode()->getConstArray()[0].getIConst(); + arraySizes->addInnerSize(value, param->getAsTyped()); + } else { + uint32_t value = param->getAsConstantUnion()->getConstArray()[0].getIConst(); + arraySizes->addInnerSize(value); + } + } else { + // gl_CooperativeMatrixClampModeUndefined + arraySizes->addInnerSize(0); + } + } + TTypeParameters typeParameters; + typeParameters.arraySizes = arraySizes; + + TType resultType; + resultType.deepCopy(result->getAsTyped()->getType()); + + resultType.copyTypeParameters(typeParameters); + result->setType(resultType); + } else if (fnCandidate->getBuiltInOp() == EOpCreateTensorViewNV) { + + // Convert template parameters to arraySizes/typeParameters + TArraySizes *arraySizes = new TArraySizes; + for (uint32_t i = 0; i < 7; ++i) { + TIntermNode *param {}; + if (arguments->getAsConstantUnion()) { + if (i == 0) { + param = arguments; + } + } else { + assert(arguments->getAsAggregate()); + auto &sequence = arguments->getAsAggregate()->getSequence(); + if (i < sequence.size()) { + param = sequence[i]; + } + } + if (param) { + if (param->getAsTyped()->getType().getQualifier().isSpecConstant()) { + uint32_t value = param->getAsSymbolNode()->getConstArray()[0].getIConst(); + arraySizes->addInnerSize(value, param->getAsTyped()); + } else { + uint32_t value = param->getAsConstantUnion()->getConstArray()[0].getIConst(); + arraySizes->addInnerSize(value); + } + } else { + uint32_t value = 0; + if (i >= 2) { + // default permutation values are an increasing sequence + value = i - 2; + } + arraySizes->addInnerSize(value); + } + } + TTypeParameters typeParameters; + typeParameters.arraySizes = arraySizes; + + TType resultType; + resultType.deepCopy(result->getAsTyped()->getType()); + + resultType.copyTypeParameters(typeParameters); + result->setType(resultType); + } else if (fnCandidate->getBuiltInOp() == EOpCooperativeMatrixReduceNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixPerElementOpNV || + fnCandidate->getBuiltInOp() == EOpCooperativeMatrixTransposeNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetDimensionNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetBlockSizeNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetStrideNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSliceNV || + fnCandidate->getBuiltInOp() == EOpTensorLayoutSetClampValueNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetDimensionNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetStrideNV || + fnCandidate->getBuiltInOp() == EOpTensorViewSetClipNV) { + // Set result type to match type of first parameter + result->setType(result->getAsAggregate()->getSequence()[0]->getAsTyped()->getType()); + } else { + // For MulAdd, set result type to match type of C parameter + result->setType(result->getAsAggregate()->getSequence()[2]->getAsTyped()->getType()); + } + } +} + +TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNode* arguments, + const TFunction& function) +{ + checkLocation(loc, function.getBuiltInOp()); + TIntermTyped *result = intermediate.addBuiltInFunctionCall(loc, function.getBuiltInOp(), + function.getParamCount() == 1, + arguments, function.getType()); + if (result != nullptr && obeyPrecisionQualifiers()) + computeBuiltinPrecisions(*result, function); + + if (result == nullptr) { + if (arguments == nullptr) + error(loc, " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", ""); + else + error(arguments->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(arguments)->getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + } else if (result->getAsOperator()) + builtInOpCheck(loc, function, *result->getAsOperator()); + + // Special handling for function call with SPIR-V instruction qualifier specified + if (function.getBuiltInOp() == EOpSpirvInst) { + if (auto agg = result->getAsAggregate()) { + // Propogate spirv_by_reference/spirv_literal from parameters to arguments + auto& sequence = agg->getSequence(); + for (unsigned i = 0; i < sequence.size(); ++i) { + if (function[i].type->getQualifier().isSpirvByReference()) + sequence[i]->getAsTyped()->getQualifier().setSpirvByReference(); + if (function[i].type->getQualifier().isSpirvLiteral()) + sequence[i]->getAsTyped()->getQualifier().setSpirvLiteral(); + } + + // Attach the function call to SPIR-V intruction + agg->setSpirvInstruction(function.getSpirvInstruction()); + } else if (auto unaryNode = result->getAsUnaryNode()) { + // Propogate spirv_by_reference/spirv_literal from parameters to arguments + if (function[0].type->getQualifier().isSpirvByReference()) + unaryNode->getOperand()->getQualifier().setSpirvByReference(); + if (function[0].type->getQualifier().isSpirvLiteral()) + unaryNode->getOperand()->getQualifier().setSpirvLiteral(); + + // Attach the function call to SPIR-V intruction + unaryNode->setSpirvInstruction(function.getSpirvInstruction()); + } else + assert(0); + } + + return result; +} + +// "The operation of a built-in function can have a different precision +// qualification than the precision qualification of the resulting value. +// These two precision qualifications are established as follows. +// +// The precision qualification of the operation of a built-in function is +// based on the precision qualification of its input arguments and formal +// parameters: When a formal parameter specifies a precision qualifier, +// that is used, otherwise, the precision qualification of the calling +// argument is used. The highest precision of these will be the precision +// qualification of the operation of the built-in function. Generally, +// this is applied across all arguments to a built-in function, with the +// exceptions being: +// - bitfieldExtract and bitfieldInsert ignore the 'offset' and 'bits' +// arguments. +// - interpolateAt* functions only look at the 'interpolant' argument. +// +// The precision qualification of the result of a built-in function is +// determined in one of the following ways: +// +// - For the texture sampling, image load, and image store functions, +// the precision of the return type matches the precision of the +// sampler type +// +// Otherwise: +// +// - For prototypes that do not specify a resulting precision qualifier, +// the precision will be the same as the precision of the operation. +// +// - For prototypes that do specify a resulting precision qualifier, +// the specified precision qualifier is the precision qualification of +// the result." +// +void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction& function) +{ + TPrecisionQualifier operationPrecision = EpqNone; + TPrecisionQualifier resultPrecision = EpqNone; + + TIntermOperator* opNode = node.getAsOperator(); + if (opNode == nullptr) + return; + + if (TIntermUnary* unaryNode = node.getAsUnaryNode()) { + operationPrecision = std::max(function[0].type->getQualifier().precision, + unaryNode->getOperand()->getType().getQualifier().precision); + if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } else if (TIntermAggregate* agg = node.getAsAggregate()) { + TIntermSequence& sequence = agg->getSequence(); + unsigned int numArgs = (unsigned int)sequence.size(); + switch (agg->getOp()) { + case EOpBitfieldExtract: + numArgs = 1; + break; + case EOpBitfieldInsert: + numArgs = 2; + break; + case EOpInterpolateAtCentroid: + case EOpInterpolateAtOffset: + case EOpInterpolateAtSample: + numArgs = 1; + break; + case EOpDebugPrintf: + case EOpCooperativeMatrixPerElementOpNV: + case EOpCooperativeMatrixReduceNV: + numArgs = 0; + break; + default: + break; + } + // find the maximum precision from the arguments and parameters + for (unsigned int arg = 0; arg < numArgs; ++arg) { + operationPrecision = std::max(operationPrecision, sequence[arg]->getAsTyped()->getQualifier().precision); + operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision); + } + // compute the result precision + if (agg->isSampling() || + agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore || + agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod) + resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision; + else if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } + + // Propagate precision through this node and its children. That algorithm stops + // when a precision is found, so start by clearing this subroot precision + opNode->getQualifier().precision = EpqNone; + if (operationPrecision != EpqNone) { + opNode->propagatePrecision(operationPrecision); + opNode->setOperationPrecision(operationPrecision); + } + // Now, set the result precision, which might not match + opNode->getQualifier().precision = resultPrecision; +} + +TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) +{ + storage16BitAssignmentCheck(loc, value->getType(), "return"); + + functionReturnsValue = true; + TIntermBranch* branch = nullptr; + if (currentFunctionType->getBasicType() == EbtVoid) { + error(loc, "void function cannot return a value", "return", ""); + branch = intermediate.addBranch(EOpReturn, loc); + } else if (*currentFunctionType != value->getType()) { + TIntermTyped* converted = intermediate.addConversion(EOpReturn, *currentFunctionType, value); + if (converted) { + if (*currentFunctionType != converted->getType()) + error(loc, "cannot convert return value to function return type", "return", ""); + if (version < 420) + warn(loc, "type conversion on return values was not explicitly allowed until version 420", + "return", ""); + branch = intermediate.addBranch(EOpReturn, converted, loc); + } else { + error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); + branch = intermediate.addBranch(EOpReturn, value, loc); + } + } else { + if (value->getType().isTexture() || value->getType().isImage()) { + if (spvVersion.spv != 0) + error(loc, "sampler or image cannot be used as return type when generating SPIR-V", "return", ""); + else if (!extensionTurnedOn(E_GL_ARB_bindless_texture)) + error(loc, "sampler or image can be used as return type only when the extension GL_ARB_bindless_texture enabled", "return", ""); + } + branch = intermediate.addBranch(EOpReturn, value, loc); + } + branch->updatePrecision(currentFunctionType->getQualifier().precision); + return branch; +} + +// See if the operation is being done in an illegal location. +void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) +{ + switch (op) { + case EOpBarrier: + if (language == EShLangTessControl) { + if (controlFlowNestingLevel > 0) + error(loc, "tessellation control barrier() cannot be placed within flow control", "", ""); + if (! inMain) + error(loc, "tessellation control barrier() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", ""); + } + break; + case EOpBeginInvocationInterlock: + if (language != EShLangFragment) + error(loc, "beginInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "beginInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "beginInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "beginInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (beginInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must only be called once", "", ""); + if (endInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + beginInvocationInterlockCount++; + + // default to pixel_interlock_ordered + if (intermediate.getInterlockOrdering() == EioNone) + intermediate.setInterlockOrdering(EioPixelInterlockOrdered); + break; + case EOpEndInvocationInterlock: + if (language != EShLangFragment) + error(loc, "endInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "endInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "endInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "endInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (endInvocationInterlockCount > 0) + error(loc, "endInvocationInterlockARB() must only be called once", "", ""); + if (beginInvocationInterlockCount == 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + endInvocationInterlockCount++; + break; + default: + break; + } +} + +// Finish processing object.length(). This started earlier in handleDotDereference(), where +// the ".length" part was recognized and semantically checked, and finished here where the +// function syntax "()" is recognized. +// +// Return resulting tree node. +TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode) +{ + int length = 0; + + if (function->getParamCount() > 0) + error(loc, "method does not accept any arguments", function->getName().c_str(), ""); + else { + const TType& type = intermNode->getAsTyped()->getType(); + if (type.isArray()) { + if (type.isUnsizedArray()) { + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) { + // We could be between a layout declaration that gives a built-in io array implicit size and + // a user redeclaration of that array, meaning we have to substitute its implicit size here + // without actually redeclaring the array. (It is an error to use a member before the + // redeclaration, but not an error to use the array name itself.) + const TString& name = intermNode->getAsSymbolNode()->getName(); + if (name == "gl_in" || name == "gl_out" || name == "gl_MeshVerticesNV" || + name == "gl_MeshPrimitivesNV") { + length = getIoArrayImplicitSize(type.getQualifier()); + } + } else if (const auto typed = intermNode->getAsTyped()) { + if (typed->getQualifier().builtIn == EbvSampleMask) { + requireProfile(loc, EEsProfile, "the array size of gl_SampleMask and gl_SampleMaskIn is ceil(gl_MaxSamples/32)"); + length = (resources.maxSamples + 31) / 32; + } + } + if (length == 0) { + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) + error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); + else if (isRuntimeLength(*intermNode->getAsTyped())) { + // Create a unary op and let the back end handle it + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + } else + error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); + } + } else if (type.getOuterArrayNode()) { + // If the array's outer size is specified by an intermediate node, it means the array's length + // was specified by a specialization constant. In such a case, we should return the node of the + // specialization constants to represent the length. + return type.getOuterArrayNode(); + } else + length = type.getOuterArraySize(); + } else if (type.isMatrix()) + length = type.getMatrixCols(); + else if (type.isVector()) + length = type.getVectorSize(); + else if (type.isCoopMat() || type.isCoopVecNV()) + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + else { + // we should not get here, because earlier semantic checking should have prevented this path + error(loc, ".length()", "unexpected use of .length()", ""); + } + } + + if (length == 0) + length = 1; + + return intermediate.addConstantUnion(length, loc); +} + +// +// Add any needed implicit conversions for function-call arguments to input parameters. +// +void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const +{ + TIntermAggregate* aggregate = arguments->getAsAggregate(); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 ? arguments->getAsTyped() : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped()); + if (*function[i].type != arg->getType()) { + if (function[i].type->getQualifier().isParamInput() && + !function[i].type->isCoopMat()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + arg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg); + if (arg) { + if (function.getParamCount() == 1) + arguments = arg; + else { + if (aggregate) + aggregate->getSequence()[i] = arg; + else + arguments = arg; + } + } + } + } + } +} + +// +// Add any needed implicit output conversions for function-call arguments. This +// can require a new tree topology, complicated further by whether the function +// has a return value. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const +{ + TIntermSequence& arguments = intermNode.getSequence(); + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().isParamOutput() && + !function[i].type->isCoopMat()) { + outputConversions = true; + break; + } + } + + if (! outputConversions) + return &intermNode; + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = nullptr; + TVariable* tempRet = nullptr; + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc()); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType()) { + if (function[i].type->getQualifier().isParamOutput()) { + // Out-qualified arguments need to use the topology set up above. + // do the " ...(tempArg, ...), arg = tempArg" bit from above + TType paramType; + paramType.shallowCopy(*function[i].type); + if (arguments[i]->getAsTyped()->getType().isParameterized() && + !paramType.isParameterized()) { + paramType.shallowCopy(arguments[i]->getAsTyped()->getType()); + paramType.copyTypeParameters(*arguments[i]->getAsTyped()->getType().getTypeParameters()); + } + TVariable* tempArg = makeInternalVariable("tempArg", paramType); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + } + } + } + + // Finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc()); + } + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); + + return conversionTree; +} + +TIntermTyped* TParseContext::addAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) +{ + if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "+= and -= on a buffer reference"); + + if (op == EOpAssign && left->getBasicType() == EbtSampler && right->getBasicType() == EbtSampler) + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "sampler assignment for bindless texture"); + + return intermediate.addAssign(op, left, right, loc); +} + +void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode) +{ + const TIntermSequence* argp = &callNode.getAsAggregate()->getSequence(); + + //const int gl_SemanticsRelaxed = 0x0; + const int gl_SemanticsAcquire = 0x2; + const int gl_SemanticsRelease = 0x4; + const int gl_SemanticsAcquireRelease = 0x8; + const int gl_SemanticsMakeAvailable = 0x2000; + const int gl_SemanticsMakeVisible = 0x4000; + const int gl_SemanticsVolatile = 0x8000; + + //const int gl_StorageSemanticsNone = 0x0; + const int gl_StorageSemanticsBuffer = 0x40; + const int gl_StorageSemanticsShared = 0x100; + const int gl_StorageSemanticsImage = 0x800; + const int gl_StorageSemanticsOutput = 0x1000; + + + unsigned int semantics = 0, storageClassSemantics = 0; + unsigned int semantics2 = 0, storageClassSemantics2 = 0; + + const TIntermTyped* arg0 = (*argp)[0]->getAsTyped(); + const bool isMS = arg0->getBasicType() == EbtSampler && arg0->getType().getSampler().isMultiSample(); + + // Grab the semantics and storage class semantics from the operands, based on opcode + switch (callNode.getOp()) { + case EOpAtomicAdd: + case EOpAtomicSubtract: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicStore: + storageClassSemantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicLoad: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicCompSwap: + storageClassSemantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicStore: + storageClassSemantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicLoad: + storageClassSemantics = (*argp)[isMS ? 4 : 3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicCompSwap: + storageClassSemantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 7 : 6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[isMS ? 8 : 7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[isMS ? 9 : 8]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpBarrier: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpMemoryBarrier: + storageClassSemantics = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + default: + break; + } + + if ((semantics & gl_SemanticsAcquire) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore)) { + error(loc, "gl_SemanticsAcquire must not be used with (image) atomic store", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsRelease) && + (callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsRelease must not be used with (image) atomic load", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsAcquireRelease) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsAcquireRelease must not be used with (image) atomic load/store", + fnCandidate.getName().c_str(), ""); + } + if (((semantics | semantics2) & ~(gl_SemanticsAcquire | + gl_SemanticsRelease | + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible | + gl_SemanticsVolatile))) { + error(loc, "Invalid semantics value", fnCandidate.getName().c_str(), ""); + } + if (((storageClassSemantics | storageClassSemantics2) & ~(gl_StorageSemanticsBuffer | + gl_StorageSemanticsShared | + gl_StorageSemanticsImage | + gl_StorageSemanticsOutput))) { + error(loc, "Invalid storage class semantics value", fnCandidate.getName().c_str(), ""); + } + + if (callNode.getOp() == EOpMemoryBarrier) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must include exactly one of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } else { + if (semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + if (semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + } + if (callNode.getOp() == EOpMemoryBarrier) { + if (storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + } + if (callNode.getOp() == EOpBarrier && semantics != 0 && storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + (semantics2 & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not be gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeAvailable) && + !(semantics & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeAvailable requires gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeVisible) && + !(semantics & (gl_SemanticsAcquire | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeVisible requires gl_SemanticsAcquire or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsVolatile) && + (callNode.getOp() == EOpMemoryBarrier || callNode.getOp() == EOpBarrier)) { + error(loc, "gl_SemanticsVolatile must not be used with memoryBarrier or controlBarrier", + fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + ((semantics ^ semantics2) & gl_SemanticsVolatile)) { + error(loc, "semEqual and semUnequal must either both include gl_SemanticsVolatile or neither", + fnCandidate.getName().c_str(), ""); + } +} + +// +// Do additional checking of built-in function calls that is not caught +// by normal semantic checks on argument type, extension tagging, etc. +// +// Assumes there has been a semantically correct match to a built-in function prototype. +// +void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) +{ + // Set up convenience accessors to the argument(s). There is almost always + // multiple arguments for the cases below, but when there might be one, + // check the unaryArg first. + const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference + const TIntermTyped* unaryArg = nullptr; + const TIntermTyped* arg0 = nullptr; + if (callNode.getAsAggregate()) { + argp = &callNode.getAsAggregate()->getSequence(); + if (argp->size() > 0) + arg0 = (*argp)[0]->getAsTyped(); + } else { + assert(callNode.getAsUnaryNode()); + unaryArg = callNode.getAsUnaryNode()->getOperand(); + arg0 = unaryArg; + } + + TString featureString; + const char* feature = nullptr; + switch (callNode.getOp()) { + case EOpTextureGather: + case EOpTextureGatherOffset: + case EOpTextureGatherOffsets: + { + // Figure out which variants are allowed by what extensions, + // and what arguments must be constant for which situations. + + featureString = fnCandidate.getName(); + featureString += "(...)"; + feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + int compArg = -1; // track which argument, if any, is the constant component argument + switch (callNode.getOp()) { + case EOpTextureGather: + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + break; + case EOpTextureGatherOffset: + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + case EOpTextureGatherOffsets: + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + break; + default: + break; + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if ((*argp)[compArg]->getAsConstantUnion()) { + int value = (*argp)[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + + bool bias = false; + if (callNode.getOp() == EOpTextureGather) + bias = fnCandidate.getParamCount() > 3; + else if (callNode.getOp() == EOpTextureGatherOffset || + callNode.getOp() == EOpTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 4; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + break; + } + + case EOpTexture: + case EOpTextureLod: + { + if ((fnCandidate.getParamCount() > 2) && ((*argp)[1]->getAsTyped()->getType().getBasicType() == EbtFloat) && + ((*argp)[1]->getAsTyped()->getType().getVectorSize() == 4) && fnCandidate[0].type->getSampler().shadow) { + featureString = fnCandidate.getName(); + if (callNode.getOp() == EOpTexture) + featureString += "(..., float bias)"; + else + featureString += "(..., float lod)"; + feature = featureString.c_str(); + + if ((fnCandidate[0].type->getSampler().dim == Esd2D && fnCandidate[0].type->getSampler().arrayed) || //2D Array Shadow + (fnCandidate[0].type->getSampler().dim == EsdCube && fnCandidate[0].type->getSampler().arrayed && fnCandidate.getParamCount() > 3) || // Cube Array Shadow + (fnCandidate[0].type->getSampler().dim == EsdCube && callNode.getOp() == EOpTextureLod)) { // Cube Shadow + requireExtensions(loc, 1, &E_GL_EXT_texture_shadow_lod, feature); + if (isEsProfile()) { + if (version < 320 && + !extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + error(loc, "GL_EXT_texture_shadow_lod not supported for this ES version", feature, ""); + else + profileRequires(loc, EEsProfile, 320, nullptr, feature); + } else { // Desktop + profileRequires(loc, ~EEsProfile, 130, nullptr, feature); + } + } + } + break; + } + + case EOpSparseTextureGather: + case EOpSparseTextureGatherOffset: + case EOpSparseTextureGatherOffsets: + { + bool bias = false; + if (callNode.getOp() == EOpSparseTextureGather) + bias = fnCandidate.getParamCount() > 4; + else if (callNode.getOp() == EOpSparseTextureGatherOffset || + callNode.getOp() == EOpSparseTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 5; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + // As per GL_ARB_sparse_texture2 extension "Offsets" parameter must be constant integral expression + // for sparseTextureGatherOffsetsARB just as textureGatherOffsets + if (callNode.getOp() == EOpSparseTextureGatherOffsets) { + int offsetsArg = arg0->getType().getSampler().shadow ? 3 : 2; + if (!(*argp)[offsetsArg]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "offsets", ""); + } + break; + } + + case EOpSparseTextureGatherLod: + case EOpSparseTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffsets: + { + requireExtensions(loc, 1, &E_GL_ARB_sparse_texture2, fnCandidate.getName().c_str()); + break; + } + + case EOpSwizzleInvocations: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "offset", ""); + else { + unsigned offset[4] = {}; + offset[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + offset[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + offset[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + offset[3] = (*argp)[1]->getAsConstantUnion()->getConstArray()[3].getUConst(); + if (offset[0] > 3 || offset[1] > 3 || offset[2] > 3 || offset[3] > 3) + error(loc, "components must be in the range [0, 3]", "offset", ""); + } + + break; + } + + case EOpSwizzleInvocationsMasked: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "mask", ""); + else { + unsigned mask[3] = {}; + mask[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + mask[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + mask[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + if (mask[0] > 31 || mask[1] > 31 || mask[2] > 31) + error(loc, "components must be in the range [0, 31]", "mask", ""); + } + + break; + } + + case EOpTextureOffset: + case EOpTextureFetchOffset: + case EOpTextureProjOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + case EOpSparseTextureOffset: + case EOpSparseTextureFetchOffset: + case EOpSparseTextureLodOffset: + case EOpSparseTextureGradOffset: + { + // Handle texture-offset limits checking + // Pick which argument has to hold constant offsets + int arg = -1; + switch (callNode.getOp()) { + case EOpSparseTextureOffset: + case EOpTextureOffset: + case EOpTextureProjOffset: + arg = 2; + break; + case EOpSparseTextureLodOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + arg = 3; + break; + case EOpSparseTextureGradOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + arg = 4; + break; + case EOpSparseTextureFetchOffset: + case EOpTextureFetchOffset: + arg = (arg0->getType().getSampler().isRect()) ? 2 : 3; + break; + default: + assert(0); + break; + } + + if (arg > 0) { + + bool f16ShadowCompare = (*argp)[1]->getAsTyped()->getBasicType() == EbtFloat16 && + arg0->getType().getSampler().shadow; + if (f16ShadowCompare) + ++arg; + if (! (*argp)[arg]->getAsTyped()->getQualifier().isConstant()) { + if (!extensionTurnedOn(E_GL_EXT_texture_offset_non_const)) + error(loc, "argument must be compile-time constant", "texel offset", ""); + } + else if ((*argp)[arg]->getAsConstantUnion()) { + const TType& type = (*argp)[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = (*argp)[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", + "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + + // This check does not apply to sparse because + // GL_ARB_sparse_texture2 always includes this function. + if (callNode.getOp() == EOpTextureOffset) { + TSampler s = arg0->getType().getSampler(); + if (s.is2D() && s.isArrayed() && s.isShadow()) { + if ( + ((*argp)[1]->getAsTyped()->getType().getBasicType() == EbtFloat) && + ((*argp)[1]->getAsTyped()->getType().getVectorSize() == 4) && + (fnCandidate.getParamCount() == 4)) { + featureString = fnCandidate.getName() + " for sampler2DArrayShadow"; + feature = featureString.c_str(); + requireExtensions(loc, 1, &E_GL_EXT_texture_shadow_lod, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + profileRequires(loc, ~EEsProfile, 130, nullptr, feature); + } + else if (isEsProfile()) + error(loc, "TextureOffset does not support sampler2DArrayShadow : ", "sampler", "ES Profile"); + else if (version <= 420) + error(loc, "TextureOffset does not support sampler2DArrayShadow : ", "sampler", "version <= 420"); + } + } + + // This check does not apply to sparse because + // GL_ARB_sparse_texture2 does not define sparseTextureLodOffsetARB + // with a sampler2DArrayShadow. + if (callNode.getOp() == EOpTextureLodOffset) { + TSampler s = arg0->getType().getSampler(); + if (s.is2D() && s.isArrayed() && s.isShadow() && + ((*argp)[1]->getAsTyped()->getType().getBasicType() == EbtFloat) && + ((*argp)[1]->getAsTyped()->getType().getVectorSize() == 4) && + (fnCandidate.getParamCount() == 4)) { + featureString = fnCandidate.getName() + " for sampler2DArrayShadow"; + feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + profileRequires(loc, ~EEsProfile, 130, nullptr, feature); + requireExtensions(loc, 1, &E_GL_EXT_texture_shadow_lod, feature); + } + } + } + + break; + } + + case EOpTraceNV: + if (!(*argp)[10]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", "a"); + break; + case EOpTraceRayMotionNV: + if (!(*argp)[11]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", "a"); + break; + case EOpTraceKHR: + if (!(*argp)[10]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", "a"); + else { + unsigned int location = (*argp)[10]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(0, location) < 0) + error(loc, "with layout(location =", "no rayPayloadEXT/rayPayloadInEXT declared", "%d)", location); + } + break; + case EOpExecuteCallableNV: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "callable data number", ""); + break; + case EOpExecuteCallableKHR: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "callable data number", ""); + else { + unsigned int location = (*argp)[1]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(1, location) < 0) + error(loc, "with layout(location =", "no callableDataEXT/callableDataInEXT declared", "%d)", location); + } + break; + + case EOpHitObjectTraceRayNV: + if (!(*argp)[11]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", ""); + else { + unsigned int location = (*argp)[11]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(0, location) < 0) + error(loc, "with layout(location =", "no rayPayloadEXT/rayPayloadInEXT declared", "%d)", location); + } + break; + case EOpHitObjectTraceRayMotionNV: + if (!(*argp)[12]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", ""); + else { + unsigned int location = (*argp)[12]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(0, location) < 0) + error(loc, "with layout(location =", "no rayPayloadEXT/rayPayloadInEXT declared", "%d)", location); + } + break; + case EOpHitObjectExecuteShaderNV: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", ""); + else { + unsigned int location = (*argp)[1]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(0, location) < 0) + error(loc, "with layout(location =", "no rayPayloadEXT/rayPayloadInEXT declared", "%d)", location); + } + break; + case EOpHitObjectRecordHitNV: + if (!(*argp)[12]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "hitobjectattribute number", ""); + else { + unsigned int location = (*argp)[12]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(2, location) < 0) + error(loc, "with layout(location =", "no hitObjectAttributeNV declared", "%d)", location); + } + break; + case EOpHitObjectRecordHitMotionNV: + if (!(*argp)[13]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "hitobjectattribute number", ""); + else { + unsigned int location = (*argp)[13]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(2, location) < 0) + error(loc, "with layout(location =", "no hitObjectAttributeNV declared", "%d)", location); + } + break; + case EOpHitObjectRecordHitWithIndexNV: + if (!(*argp)[11]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "hitobjectattribute number", ""); + else { + unsigned int location = (*argp)[11]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(2, location) < 0) + error(loc, "with layout(location =", "no hitObjectAttributeNV declared", "%d)", location); + } + break; + case EOpHitObjectRecordHitWithIndexMotionNV: + if (!(*argp)[12]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "hitobjectattribute number", ""); + else { + unsigned int location = (*argp)[12]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(2, location) < 0) + error(loc, "with layout(location =", "no hitObjectAttributeNV declared", "%d)", location); + } + break; + case EOpHitObjectGetAttributesNV: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "hitobjectattribute number", ""); + else { + unsigned int location = (*argp)[1]->getAsConstantUnion()->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (!extensionTurnedOn(E_GL_EXT_spirv_intrinsics) && intermediate.checkLocationRT(2, location) < 0) + error(loc, "with layout(location =", "no hitObjectAttributeNV declared", "%d)", location); + } + break; + + case EOpRayQueryGetIntersectionType: + case EOpRayQueryGetIntersectionT: + case EOpRayQueryGetIntersectionInstanceCustomIndex: + case EOpRayQueryGetIntersectionInstanceId: + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case EOpRayQueryGetIntersectionGeometryIndex: + case EOpRayQueryGetIntersectionPrimitiveIndex: + case EOpRayQueryGetIntersectionBarycentrics: + case EOpRayQueryGetIntersectionFrontFace: + case EOpRayQueryGetIntersectionObjectRayDirection: + case EOpRayQueryGetIntersectionObjectRayOrigin: + case EOpRayQueryGetIntersectionObjectToWorld: + case EOpRayQueryGetIntersectionWorldToObject: + case EOpRayQueryGetIntersectionTriangleVertexPositionsEXT: + case EOpRayQueryGetIntersectionClusterIdNV: + case EOpRayQueryGetIntersectionSpherePositionNV: + case EOpRayQueryGetIntersectionSphereRadiusNV: + case EOpRayQueryGetIntersectionLSSHitValueNV: + case EOpRayQueryGetIntersectionLSSPositionsNV: + case EOpRayQueryGetIntersectionLSSRadiiNV: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "committed", ""); + break; + + case EOpTextureQuerySamples: + case EOpImageQuerySamples: + // GL_ARB_shader_texture_image_samples + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicCompSwap: + case EOpImageAtomicLoad: + case EOpImageAtomicStore: + { + // Make sure the image types have the correct layout() format and correct argument types + const TType& imageType = arg0->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint || + imageType.getSampler().type == EbtInt64 || imageType.getSampler().type == EbtUint64) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui && + imageType.getQualifier().getFormat() != ElfR64i && imageType.getQualifier().getFormat() != ElfR64ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + if (callNode.getType().getBasicType() == EbtInt64 && imageType.getQualifier().getFormat() != ElfR64i) + error(loc, "only supported on image with format r64i", fnCandidate.getName().c_str(), ""); + else if (callNode.getType().getBasicType() == EbtUint64 && imageType.getQualifier().getFormat() != ElfR64ui) + error(loc, "only supported on image with format r64ui", fnCandidate.getName().c_str(), ""); + } else if(callNode.getType().getBasicType() == EbtFloat16 && + ((callNode.getType().getVectorSize() == 2 && arg0->getType().getQualifier().getFormat() == ElfRg16f) || + (callNode.getType().getVectorSize() == 4 && arg0->getType().getQualifier().getFormat() == ElfRgba16f))) { + if (StartsWith(fnCandidate.getName(), "imageAtomicAdd") || + StartsWith(fnCandidate.getName(), "imageAtomicExchange") || + StartsWith(fnCandidate.getName(), "imageAtomicMin") || + StartsWith(fnCandidate.getName(), "imageAtomicMax")) { + requireExtensions(loc, 1, &E_GL_NV_shader_atomic_fp16_vector, fnCandidate.getName().c_str()); + } else { + error(loc, "f16vec2/4 operation not supported on: ", fnCandidate.getName().c_str(), ""); + } + } else if (imageType.getSampler().type == EbtFloat) { + if (StartsWith(fnCandidate.getName(), "imageAtomicExchange")) { + // imageAtomicExchange doesn't require an extension + } else if (StartsWith(fnCandidate.getName(), "imageAtomicAdd") || + StartsWith(fnCandidate.getName(), "imageAtomicLoad") || + StartsWith(fnCandidate.getName(), "imageAtomicStore")) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + } else if (StartsWith(fnCandidate.getName(), "imageAtomicMin") || + StartsWith(fnCandidate.getName(), "imageAtomicMax")) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str()); + } else { + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + } + if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } else { + error(loc, "not supported on this image type", fnCandidate.getName().c_str(), ""); + } + + const size_t maxArgs = imageType.getSampler().isMultiSample() ? 5 : 4; + if (argp->size() > maxArgs) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + + break; + } + + case EOpAtomicAdd: + case EOpAtomicSubtract: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicCompSwap: + case EOpAtomicLoad: + case EOpAtomicStore: + { + if (argp->size() > 3) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpAtomicStore) && + (arg0->getType().getBasicType() == EbtFloat || + arg0->getType().getBasicType() == EbtDouble)) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + } else if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpAtomicStore || + callNode.getOp() == EOpAtomicMin || callNode.getOp() == EOpAtomicMax) && + arg0->getType().isFloatingDomain()) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str()); + } + } else if (arg0->getType().getBasicType() == EbtInt64 || arg0->getType().getBasicType() == EbtUint64) { + const char* const extensions[2] = { E_GL_NV_shader_atomic_int64, + E_GL_EXT_shader_atomic_int64 }; + requireExtensions(loc, 2, extensions, fnCandidate.getName().c_str()); + } else if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange || + callNode.getOp() == EOpAtomicMin || callNode.getOp() == EOpAtomicMax) && + arg0->getType().getBasicType() == EbtFloat16 && + (arg0->getType().getVectorSize() == 2 || arg0->getType().getVectorSize() == 4 )) { + requireExtensions(loc, 1, &E_GL_NV_shader_atomic_fp16_vector, fnCandidate.getName().c_str()); + } else if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange) && + (arg0->getType().getBasicType() == EbtFloat || + arg0->getType().getBasicType() == EbtDouble)) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + } else if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpAtomicStore || + callNode.getOp() == EOpAtomicMin || callNode.getOp() == EOpAtomicMax) && + arg0->getType().isFloatingDomain()) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str()); + } + + const TIntermTyped* base = TIntermediate::traverseLValueBase(arg0, true, true); + const char* errMsg = "Only l-values corresponding to shader block storage or shared variables can be used with " + "atomic memory functions."; + if (base) { + const TType* refType = (base->getType().isReference()) ? base->getType().getReferentType() : nullptr; + const TQualifier& qualifier = + (refType != nullptr) ? refType->getQualifier() : base->getType().getQualifier(); + if (qualifier.storage != EvqShared && qualifier.storage != EvqBuffer && + qualifier.storage != EvqtaskPayloadSharedEXT) + error(loc, errMsg, fnCandidate.getName().c_str(), ""); + } else { + error(loc, errMsg, fnCandidate.getName().c_str(), ""); + } + + break; + } + + case EOpInterpolateAtCentroid: + case EOpInterpolateAtSample: + case EOpInterpolateAtOffset: + case EOpInterpolateAtVertex: { + if (arg0->getType().getQualifier().storage != EvqVaryingIn) { + // Traverse down the left branch of arg0 to ensure this argument is a valid interpolant. + // + // For desktop GL >4.3 we effectively only need to ensure that arg0 represents an l-value from an + // input declaration. + // + // For desktop GL <= 4.3 and ES, we must also ensure that swizzling is not used + // + // For ES, we must also ensure that a field selection operator (i.e., '.') is not used on a named + // struct. + + const bool esProfile = isEsProfile(); + const bool swizzleOkay = !esProfile && (version >= 440); + + std::string interpolantErrorMsg = "first argument must be an interpolant, or interpolant-array element"; + bool isValid = true; // Assume that the interpolant is valid until we find a condition making it invalid + bool isIn = false; // Checks whether or not the interpolant is a shader input + bool structAccessOp = false; // Whether or not the previous node in the chain is a struct accessor + TIntermediate::traverseLValueBase( + arg0, swizzleOkay, false, + [&isValid, &isIn, &interpolantErrorMsg, esProfile, &structAccessOp](const TIntermNode& n) -> bool { + auto* type = n.getAsTyped(); + if (type) { + if (type->getType().getQualifier().storage == EvqVaryingIn) { + isIn = true; + } + // If a field accessor was used, it can only be used to access a field with an input block, not a struct. + if (structAccessOp && (type->getType().getBasicType() != EbtBlock)) { + interpolantErrorMsg += + ". Using the field of a named struct as an interpolant argument is not " + "allowed (ES-only)."; + isValid = false; + } + } + + // ES has different requirements for interpolants than GL + if (esProfile) { + // Swizzling will be taken care of by the 'swizzleOkay' argument passsed to traverseLValueBase, + // so we only ned to check whether or not a field accessor has been used with a named struct. + auto* binary = n.getAsBinaryNode(); + if (binary && (binary->getOp() == EOpIndexDirectStruct)) { + structAccessOp = true; + } + } + // Don't continue traversing if we know we have an invalid interpolant at this point. + return isValid; + }); + if (!isIn || !isValid) { + error(loc, interpolantErrorMsg.c_str(), fnCandidate.getName().c_str(), ""); + } + } + + if (callNode.getOp() == EOpInterpolateAtVertex) { + if (!arg0->getType().getQualifier().isExplicitInterpolation()) + error(loc, "argument must be qualified as __explicitInterpAMD in", "interpolant", ""); + else { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "vertex index", ""); + else { + unsigned vertexIdx = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (vertexIdx > 2) + error(loc, "must be in the range [0, 2]", "vertex index", ""); + } + } + } + } break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + if (version == 150) + requireExtensions(loc, 1, &E_GL_ARB_gpu_shader5, "if the version is 150 , the EmitStreamVertex and EndStreamPrimitive only support at extension GL_ARB_gpu_shader5"); + intermediate.setMultiStream(); + break; + + case EOpSubgroupClusteredAdd: + case EOpSubgroupClusteredMul: + case EOpSubgroupClusteredMin: + case EOpSubgroupClusteredMax: + case EOpSubgroupClusteredAnd: + case EOpSubgroupClusteredOr: + case EOpSubgroupClusteredXor: + // The as used in the subgroupClustered() operations must be: + // - An integral constant expression. + // - At least 1. + // - A power of 2. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "cluster size", ""); + else { + int size = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (size < 1) + error(loc, "argument must be at least 1", "cluster size", ""); + else if (!IsPow2(size)) + error(loc, "argument must be a power of 2", "cluster size", ""); + } + break; + + case EOpSubgroupBroadcast: + case EOpSubgroupQuadBroadcast: + if (spvVersion.spv < EShTargetSpv_1_5) { + // must be an integral constant expression. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "id", ""); + } + break; + + case EOpBarrier: + case EOpMemoryBarrier: + if (argp->size() > 0) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + break; + + case EOpMix: + if (profile == EEsProfile && version < 310) { + // Look for specific signatures + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, "specific signature of builtin mix"); + } + } + + if (profile != EEsProfile && version < 450) { + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[0]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, fnCandidate.getName().c_str()); + } + } + + break; + case EOpCooperativeVectorMatMulNV: + case EOpCooperativeVectorMatMulAddNV: + { + int inputInterpIdx = 2; + int matrixInterpIdx = 5; + int biasInterpIdx = 8; + int MIdx = callNode.getOp() == EOpCooperativeVectorMatMulAddNV ? 9 : 6; + int KIdx = callNode.getOp() == EOpCooperativeVectorMatMulAddNV ? 10 : 7; + int matrixLayoutIdx = callNode.getOp() == EOpCooperativeVectorMatMulAddNV ? 11 : 8; + int transposeIdx = callNode.getOp() == EOpCooperativeVectorMatMulAddNV ? 12 : 9; + + if (!(*argp)[inputInterpIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "inputInterpretation", ""); + if (!(*argp)[matrixInterpIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "matrixInterpretation", ""); + if (callNode.getOp() == EOpCooperativeVectorMatMulAddNV) { + if (!(*argp)[biasInterpIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "biasInterpretation", ""); + } + if (!(*argp)[MIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "M", ""); + if (!(*argp)[KIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "K", ""); + if (!(*argp)[matrixLayoutIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "matrixLayout", ""); + if (!(*argp)[transposeIdx]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "transpose", ""); + } + break; + case EOpCooperativeVectorOuterProductAccumulateNV: + if (!(*argp)[5]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "matrixLayout", ""); + if (!(*argp)[6]->getAsTyped()->getType().getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "matrixInterpretation", ""); + break; + default: + break; + } + + // Texture operations on texture objects (aside from texelFetch on a + // textureBuffer) require EXT_samplerless_texture_functions. + switch (callNode.getOp()) { + case EOpTextureQuerySize: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpTextureFetch: + case EOpTextureFetchOffset: + { + const TSampler& sampler = fnCandidate[0].type->getSampler(); + + const bool isTexture = sampler.isTexture() && !sampler.isCombined(); + const bool isBuffer = sampler.isBuffer(); + const bool isFetch = callNode.getOp() == EOpTextureFetch || callNode.getOp() == EOpTextureFetchOffset; + + if (isTexture && (!isBuffer || !isFetch)) + requireExtensions(loc, 1, &E_GL_EXT_samplerless_texture_functions, fnCandidate.getName().c_str()); + + break; + } + + default: + break; + } + + if (callNode.isSubgroup()) { + // these require SPIR-V 1.3 + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "subgroup op", ""); + + // Check that if extended types are being used that the correct extensions are enabled. + if (arg0 != nullptr) { + const TType& type = arg0->getType(); + bool enhanced = intermediate.getEnhancedMsgs(); + switch (type.getBasicType()) { + default: + break; + case EbtInt8: + case EbtUint8: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int8, type.getCompleteString(enhanced).c_str()); + break; + case EbtInt16: + case EbtUint16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int16, type.getCompleteString(enhanced).c_str()); + break; + case EbtInt64: + case EbtUint64: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int64, type.getCompleteString(enhanced).c_str()); + break; + case EbtFloat16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_float16, type.getCompleteString(enhanced).c_str()); + break; + } + } + } +} + + +// Deprecated! Use PureOperatorBuiltins == true instead, in which case this +// functionality is handled in builtInOpCheck() instead of here. +// +// Do additional checking of built-in function calls that were not mapped +// to built-in operations (e.g., texturing functions). +// +// Assumes there has been a semantically correct match to a built-in function. +// +void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermAggregate& callNode) +{ + // Further maintenance of this function is deprecated, because the "correct" + // future-oriented design is to not have to do string compares on function names. + + // If PureOperatorBuiltins == true, then all built-ins should be mapped + // to a TOperator, and this function would then never get called. + + assert(PureOperatorBuiltins == false); + + // built-in texturing functions get their return value precision from the precision of the sampler + if (fnCandidate.getType().getQualifier().precision == EpqNone && + fnCandidate.getParamCount() > 0 && fnCandidate[0].type->getBasicType() == EbtSampler) + callNode.getQualifier().precision = callNode.getSequence()[0]->getAsTyped()->getQualifier().precision; + + if (fnCandidate.getName().compare(0, 7, "texture") == 0) { + if (fnCandidate.getName().compare(0, 13, "textureGather") == 0) { + TString featureString = fnCandidate.getName() + "(...)"; + const char* feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + + int compArg = -1; // track which argument, if any, is the constant component argument + if (fnCandidate.getName().compare("textureGatherOffset") == 0) { + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + } else if (fnCandidate.getName().compare("textureGatherOffsets") == 0) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + } else if (fnCandidate.getName().compare("textureGather") == 0) { + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if (callNode.getSequence()[compArg]->getAsConstantUnion()) { + int value = callNode.getSequence()[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + } else { + // this is only for functions not starting "textureGather"... + if (fnCandidate.getName().find("Offset") != TString::npos) { + + // Handle texture-offset limits checking + int arg = -1; + if (fnCandidate.getName().compare("textureOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("texelFetchOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("textureLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureGradOffset") == 0) + arg = 4; + else if (fnCandidate.getName().compare("textureProjGradOffset") == 0) + arg = 4; + + if (arg > 0) { + if (! callNode.getSequence()[arg]->getAsConstantUnion()) { + if (!extensionTurnedOn(E_GL_EXT_texture_offset_non_const)) + error(loc, "argument must be compile-time constant", "texel offset", ""); + } + else { + const TType& type = callNode.getSequence()[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = callNode.getSequence()[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + } + } + } + + // GL_ARB_shader_texture_image_samples + if (fnCandidate.getName().compare(0, 14, "textureSamples") == 0 || fnCandidate.getName().compare(0, 12, "imageSamples") == 0) + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + + if (fnCandidate.getName().compare(0, 11, "imageAtomic") == 0) { + const TType& imageType = callNode.getSequence()[0]->getAsTyped()->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + } else { + if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } + } +} + +// +// Do any extra checking for a user function call. +// +void TParseContext::userFunctionCallCheck(const TSourceLoc& loc, TIntermAggregate& callNode) +{ + TIntermSequence& arguments = callNode.getSequence(); + + for (int i = 0; i < (int)arguments.size(); ++i) + samplerConstructorLocationCheck(loc, "call argument", arguments[i]); +} + +// +// Emit an error if this is a sampler constructor +// +void TParseContext::samplerConstructorLocationCheck(const TSourceLoc& loc, const char* token, TIntermNode* node) +{ + if (node->getAsOperator() && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + error(loc, "sampler constructor must appear at point of use", token, ""); +} + +// +// Handle seeing a built-in constructor in a grammar production. +// +TFunction* TParseContext::handleConstructorCall(const TSourceLoc& loc, const TPublicType& publicType) +{ + TType type(publicType); + type.getQualifier().precision = EpqNone; + + if (type.isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed constructor"); + profileRequires(loc, EEsProfile, 300, nullptr, "arrayed constructor"); + } + + // Reuse EOpConstructTextureSampler for bindless image constructor + // uvec2 imgHandle; + // imageLoad(image1D(imgHandle), 0); + if (type.isImage() && extensionTurnedOn(E_GL_ARB_bindless_texture)) + { + intermediate.setBindlessImageMode(currentCaller, AstRefTypeFunc); + } + + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpNull) { + if (intermediate.getEnhancedMsgs() && type.getBasicType() == EbtSampler) + error(loc, "function not supported in this version; use texture() instead", "texture*D*", ""); + else + error(loc, "cannot construct this type", type.getBasicString(), ""); + op = EOpConstructFloat; + TType errorType(EbtFloat); + type.shallowCopy(errorType); + } + + TString empty(""); + + return new TFunction(&empty, type, op); +} + +// Handle seeing a precision qualifier in the grammar. +void TParseContext::handlePrecisionQualifier(const TSourceLoc& /*loc*/, TQualifier& qualifier, TPrecisionQualifier precision) +{ + if (obeyPrecisionQualifiers()) + qualifier.precision = precision; +} + +// Check for messages to give on seeing a precision qualifier used in a +// declaration in the grammar. +void TParseContext::checkPrecisionQualifier(const TSourceLoc& loc, TPrecisionQualifier) +{ + if (precisionManager.shouldWarnAboutDefaults()) { + warn(loc, "all default precisions are highp; use precision statements to quiet warning, e.g.:\n" + " \"precision mediump int; precision highp float;\"", "", ""); + precisionManager.defaultWarningGiven(); + } +} + +// +// Same error message for all places assignments don't work. +// +void TParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, "", op, "cannot convert from '%s' to '%s'", + right.c_str(), left.c_str()); +} + +// +// Same error message for all places unary operations don't work. +// +void TParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) +{ + error(loc, " wrong operand type", op, + "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", + op, operand.c_str()); +} + +// +// Same error message for all binary operations don't work. +// +void TParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, " wrong operand types:", op, + "no operation '%s' exists that takes a left-hand operand of type '%s' and " + "a right operand of type '%s' (or there is no acceptable conversion)", + op, left.c_str(), right.c_str()); +} + +// +// A basic type of EbtVoid is a key that the name string was seen in the source, but +// it was not found as a variable in the symbol table. If so, give the error +// message and insert a dummy variable in the symbol table to prevent future errors. +// +void TParseContext::variableCheck(TIntermTyped*& nodePtr) +{ + TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); + if (! symbol) + return; + + if (symbol->getType().getBasicType() == EbtVoid) { + const char *extraInfoFormat = ""; + if (spvVersion.vulkan != 0 && symbol->getName() == "gl_VertexID") { + extraInfoFormat = "(Did you mean gl_VertexIndex?)"; + } else if (spvVersion.vulkan != 0 && symbol->getName() == "gl_InstanceID") { + extraInfoFormat = "(Did you mean gl_InstanceIndex?)"; + } + error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), extraInfoFormat); + + // Add to symbol table to prevent future error messages on the same name + if (symbol->getName().size() > 0) { + TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); + symbolTable.insert(*fakeVariable); + + // substitute a symbol node for this new variable + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); + } + } else { + switch (symbol->getQualifier().storage) { + case EvqPointCoord: + profileRequires(symbol->getLoc(), ENoProfile, 120, nullptr, "gl_PointCoord"); + break; + default: break; // some compilers want this + } + } +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode) { + bool errorReturn = false; + + switch(binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + // ... tessellation control shader ... + // If a per-vertex output variable is used as an l-value, it is a + // compile-time or link-time error if the expression indicating the + // vertex index is not the identifier gl_InvocationID. + if (language == EShLangTessControl) { + const TType& leftType = binaryNode->getLeft()->getType(); + if (leftType.getQualifier().storage == EvqVaryingOut && ! leftType.getQualifier().patch && binaryNode->getLeft()->getAsSymbolNode()) { + // we have a per-vertex output + const TIntermSymbol* rightSymbol = binaryNode->getRight()->getAsSymbolNode(); + if (! rightSymbol || rightSymbol->getQualifier().builtIn != EbvInvocationId) + error(loc, "tessellation-control per-vertex output l-value must be indexed with gl_InvocationID", "[]", ""); + } + } + break; // left node is checked by base class + case EOpVectorSwizzle: + errorReturn = lValueErrorCheck(loc, op, binaryNode->getLeft()); + if (!errorReturn) { + int offset[4] = {0,0,0,0}; + + TIntermTyped* rightNode = binaryNode->getRight(); + TIntermAggregate *aggrNode = rightNode->getAsAggregate(); + + for (TIntermSequence::iterator p = aggrNode->getSequence().begin(); + p != aggrNode->getSequence().end(); p++) { + int value = (*p)->getAsTyped()->getAsConstantUnion()->getConstArray()[0].getIConst(); + offset[value]++; + if (offset[value] > 1) { + error(loc, " l-value of swizzle cannot have duplicate components", op, "", ""); + + return true; + } + } + } + + return errorReturn; + default: + break; + } + + if (errorReturn) { + error(loc, " l-value required", op, "", ""); + return true; + } + } + + if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct && binaryNode->getLeft()->isReference()) + return false; + + // Let the base class check errors + if (TParseContextBase::lValueErrorCheck(loc, op, node)) + return true; + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqVaryingIn: message = "can't modify shader input"; break; + case EvqInstanceId: message = "can't modify gl_InstanceID"; break; + case EvqVertexId: message = "can't modify gl_VertexID"; break; + case EvqFace: message = "can't modify gl_FrontFace"; break; + case EvqFragCoord: message = "can't modify gl_FragCoord"; break; + case EvqPointCoord: message = "can't modify gl_PointCoord"; break; + case EvqFragDepth: + intermediate.setDepthReplacing(); + // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader." + if (isEsProfile() && intermediate.getEarlyFragmentTests()) + message = "can't modify gl_FragDepth if using early_fragment_tests"; + break; + case EvqFragStencil: + intermediate.setStencilReplacing(); + // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader." + if (isEsProfile() && intermediate.getEarlyFragmentTests()) + message = "can't modify EvqFragStencil if using early_fragment_tests"; + break; + + case EvqtaskPayloadSharedEXT: + if (language == EShLangMesh) + message = "can't modify variable with storage qualifier taskPayloadSharedEXT in mesh shaders"; + break; + default: + break; + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + return false; + + // + // If we get here, we have an error and a message. + // + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + // Let the base class check errors + TParseContextBase::rValueErrorCheck(loc, op, node); + + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (!(symNode && symNode->getQualifier().isWriteOnly())) // base class checks + if (symNode && symNode->getQualifier().isExplicitInterpolation()) + error(loc, "can't read from explicitly-interpolated object: ", op, symNode->getName().c_str()); + + // local_size_{xyz} must be assigned or specialized before gl_WorkGroupSize can be assigned. + if(node->getQualifier().builtIn == EbvWorkGroupSize && + !(intermediate.isLocalSizeSet() || intermediate.isLocalSizeSpecialized())) + error(loc, "can't read from gl_WorkGroupSize before a fixed workgroup size has been declared", op, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a constant. +// +void TParseContext::constantValueCheck(TIntermTyped* node, const char* token) +{ + if (! node->getQualifier().isConstant()) + error(node->getLoc(), "constant expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a 32-bit integer or can implicitly convert to one. +// +void TParseContext::integerCheck(const TIntermTyped* node, const char* token) +{ + auto from_type = node->getBasicType(); + if ((from_type == EbtInt || from_type == EbtUint || + intermediate.canImplicitlyPromote(from_type, EbtInt, EOpNull) || + intermediate.canImplicitlyPromote(from_type, EbtUint, EOpNull)) && node->isScalar()) + return; + + error(node->getLoc(), "scalar integer expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if we are currently +// globally scoped. +// +void TParseContext::globalCheck(const TSourceLoc& loc, const char* token) +{ + if (! symbolTable.atGlobalLevel()) + error(loc, "not allowed in nested scope", token, ""); +} + +// +// Reserved errors for GLSL. +// +void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& identifier) +{ + // "Identifiers starting with "gl_" are reserved for use by OpenGL, and may not be + // declared in a shader; this results in a compile-time error." + if (! symbolTable.atBuiltInLevel()) { + if (builtInName(identifier) && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) + // The extension GL_EXT_spirv_intrinsics allows us to declare identifiers starting with "gl_". + error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), ""); + + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "In addition, all identifiers containing two consecutive underscores (__) are + // reserved; using such a name does not itself result in an error, but may result + // in undefined behavior." + // however, before that, ES tests required an error. + if (identifier.find("__") != TString::npos && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) { + // The extension GL_EXT_spirv_intrinsics allows us to declare identifiers starting with "__". + if (isEsProfile() && version < 300) + error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version < 300", identifier.c_str(), ""); + else + warn(loc, "identifiers containing consecutive underscores (\"__\") are reserved", identifier.c_str(), ""); + } + } +} + +// +// Reserved errors for the preprocessor. +// +void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op) +{ + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "All macro names containing two consecutive underscores ( __ ) are reserved; + // defining such a name does not itself result in an error, but may result in + // undefined behavior. All macro names prefixed with "GL_" ("GL" followed by a + // single underscore) are also reserved, and defining such a name results in a + // compile-time error." + // however, before that, ES tests required an error. + if (strncmp(identifier, "GL_", 3) == 0 && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) + // The extension GL_EXT_spirv_intrinsics allows us to declare macros prefixed with "GL_". + ppError(loc, "names beginning with \"GL_\" can't be (un)defined:", op, identifier); + else if (strncmp(identifier, "defined", 8) == 0) + if (relaxedErrors()) + ppWarn(loc, "\"defined\" is (un)defined:", op, identifier); + else + ppError(loc, "\"defined\" can't be (un)defined:", op, identifier); + else if (strstr(identifier, "__") != nullptr && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) { + // The extension GL_EXT_spirv_intrinsics allows us to declare macros prefixed with "__". + if (isEsProfile() && version >= 300 && + (strcmp(identifier, "__LINE__") == 0 || + strcmp(identifier, "__FILE__") == 0 || + strcmp(identifier, "__VERSION__") == 0)) + ppError(loc, "predefined names can't be (un)defined:", op, identifier); + else { + if (isEsProfile() && version < 300 && !relaxedErrors()) + ppError(loc, "names containing consecutive underscores are reserved, and an error if version < 300:", op, identifier); + else + ppWarn(loc, "names containing consecutive underscores are reserved:", op, identifier); + } + } +} + +// +// See if this version/profile allows use of the line-continuation character '\'. +// +// Returns true if a line continuation should be done. +// +bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment) +{ + const char* message = "line continuation"; + + bool lineContinuationAllowed = (isEsProfile() && version >= 300) || + (!isEsProfile() && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack))); + + if (endOfComment) { + if (lineContinuationAllowed) + warn(loc, "used at end of comment; the following line is still part of the comment", message, ""); + else + warn(loc, "used at end of comment, but this version does not provide line continuation", message, ""); + + return lineContinuationAllowed; + } + + if (relaxedErrors()) { + if (! lineContinuationAllowed) + warn(loc, "not allowed in this version", message, ""); + return true; + } else { + profileRequires(loc, EEsProfile, 300, nullptr, message); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, message); + } + + return lineContinuationAllowed; +} + +bool TParseContext::builtInName(const TString& identifier) +{ + return identifier.compare(0, 3, "gl_") == 0; +} + +// +// Make sure there is enough data and not too many arguments provided to the +// constructor to build something of the type of the constructor. Also returns +// the type of the constructor. +// +// Part of establishing type is establishing specialization-constness. +// We don't yet know "top down" whether type is a specialization constant, +// but a const constructor can becomes a specialization constant if any of +// its children are, subject to KHR_vulkan_glsl rules: +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// +// Returns true if there was an error in construction. +// +bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) +{ + // See if the constructor does not establish the main type, only requalifies + // it, in which case the type comes from the argument instead of from the + // constructor function. + switch (op) { + case EOpConstructNonuniform: + if (node != nullptr && node->getAsTyped() != nullptr) { + type.shallowCopy(node->getAsTyped()->getType()); + type.getQualifier().makeTemporary(); + type.getQualifier().nonUniform = true; + } + break; + default: + type.shallowCopy(function.getType()); + break; + } + + TString constructorString; + if (intermediate.getEnhancedMsgs()) + constructorString.append(type.getCompleteString(true, false, false, true)).append(" constructor"); + else + constructorString.append("constructor"); + + // See if it's a matrix + bool constructingMatrix = false; + switch (op) { + case EOpConstructTextureSampler: + return constructorTextureSamplerError(loc, function); + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + constructingMatrix = true; + break; + default: + break; + } + + // + // Walk the arguments for first-pass checks and collection of information. + // + + int size = 0; + bool constType = true; + bool specConstType = false; // value is only valid if constType is true + bool full = false; + bool overFull = false; + bool matrixInMatrix = false; + bool arrayArg = false; + bool floatArgument = false; + bool intArgument = false; + for (int arg = 0; arg < function.getParamCount(); ++arg) { + if (function[arg].type->isArray()) { + if (function[arg].type->isUnsizedArray()) { + // Can't construct from an unsized array. + error(loc, "array argument must be sized", constructorString.c_str(), ""); + return true; + } + arrayArg = true; + } + if (constructingMatrix && function[arg].type->isMatrix()) + matrixInMatrix = true; + + // 'full' will go to true when enough args have been seen. If we loop + // again, there is an extra argument. + if (full) { + // For vectors and matrices, it's okay to have too many components + // available, but not okay to have unused arguments. + overFull = true; + } + + size += function[arg].type->computeNumComponents(); + if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) + full = true; + + if (! function[arg].type->getQualifier().isConstant()) + constType = false; + if (function[arg].type->getQualifier().isSpecConstant()) + specConstType = true; + if (function[arg].type->isFloatingDomain()) + floatArgument = true; + if (function[arg].type->isIntegerDomain()) + intArgument = true; + if (type.isStruct()) { + if (function[arg].type->contains16BitFloat()) { + requireFloat16Arithmetic(loc, constructorString.c_str(), "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains16BitInt()) { + requireInt16Arithmetic(loc, constructorString.c_str(), "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains8BitInt()) { + requireInt8Arithmetic(loc, constructorString.c_str(), "can't construct structure containing 8-bit type"); + } + } + } + if (op == EOpConstructNonuniform) + constType = false; + + switch (op) { + case EOpConstructFloat16: + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + if (type.isArray()) + requireFloat16Arithmetic(loc, constructorString.c_str(), "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireFloat16Arithmetic(loc, constructorString.c_str(), "16-bit vectors only take vector types"); + break; + case EOpConstructUint16: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructInt16: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + if (type.isArray()) + requireInt16Arithmetic(loc, constructorString.c_str(), "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt16Arithmetic(loc, constructorString.c_str(), "16-bit vectors only take vector types"); + break; + case EOpConstructUint8: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructInt8: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + if (type.isArray()) + requireInt8Arithmetic(loc, constructorString.c_str(), "8-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt8Arithmetic(loc, constructorString.c_str(), "8-bit vectors only take vector types"); + break; + default: + break; + } + + // inherit constness from children + if (constType) { + bool makeSpecConst; + // Finish pinning down spec-const semantics + if (specConstType) { + switch (op) { + case EOpConstructInt8: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructBool: + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: + // This was the list of valid ones, if they aren't converting from float + // and aren't making an array. + makeSpecConst = ! floatArgument && ! type.isArray(); + break; + + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + // This was the list of valid ones, if they aren't converting from int + // and aren't making an array. + makeSpecConst = ! intArgument && !type.isArray(); + break; + + case EOpConstructCooperativeMatrixNV: + case EOpConstructCooperativeMatrixKHR: + case EOpConstructStruct: + { + const char *specConstantCompositeExt[] = { E_GL_EXT_spec_constant_composites }; + if (checkExtensionsRequested(loc, 1, specConstantCompositeExt, "spec constant aggregate constructor")) { + makeSpecConst = true; + } else { + makeSpecConst = false; + } + } + break; + + default: + // anything else wasn't white-listed in the spec as a conversion + makeSpecConst = false; + break; + } + } else + makeSpecConst = false; + + if (makeSpecConst) + type.getQualifier().makeSpecConstant(); + else if (specConstType) + type.getQualifier().makeTemporary(); + else + type.getQualifier().storage = EvqConst; + } + + if (type.isArray()) { + if (function.getParamCount() == 0) { + error(loc, "array constructor must have at least one argument", constructorString.c_str(), ""); + return true; + } + + if (type.isUnsizedArray()) { + // auto adapt the constructor type to the number of arguments + type.changeOuterArraySize(function.getParamCount()); + } else if (type.getOuterArraySize() != function.getParamCount()) { + error(loc, "array constructor needs one argument per array element", constructorString.c_str(), ""); + return true; + } + + if (type.isArrayOfArrays()) { + // Types have to match, but we're still making the type. + // Finish making the type, and the comparison is done later + // when checking for conversion. + TArraySizes& arraySizes = *type.getArraySizes(); + + // At least the dimensionalities have to match. + if (! function[0].type->isArray() || + arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) { + error(loc, "array constructor argument not correct type to construct array element", constructorString.c_str(), ""); + return true; + } + + if (arraySizes.isInnerUnsized()) { + // "Arrays of arrays ..., and the size for any dimension is optional" + // That means we need to adopt (from the first argument) the other array sizes into the type. + for (int d = 1; d < arraySizes.getNumDims(); ++d) { + if (arraySizes.getDimSize(d) == UnsizedArraySize) { + arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1)); + } + } + } + } + } + + if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { + error(loc, "constructing non-array constituent from array argument", constructorString.c_str(), ""); + return true; + } + + if (matrixInMatrix && ! type.isArray()) { + profileRequires(loc, ENoProfile, 120, nullptr, "constructing matrix from matrix"); + + // "If a matrix argument is given to a matrix constructor, + // it is a compile-time error to have any other arguments." + if (function.getParamCount() != 1) + error(loc, "matrix constructed from matrix can only have one argument", constructorString.c_str(), ""); + return false; + } + + if (overFull) { + error(loc, "too many arguments", constructorString.c_str(), ""); + return true; + } + + if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) { + error(loc, "Number of constructor parameters does not match the number of structure fields", constructorString.c_str(), ""); + return true; + } + + if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || + (op == EOpConstructStruct && size < type.computeNumComponents())) { + error(loc, "not enough data provided for construction", constructorString.c_str(), ""); + return true; + } + + if (type.isCoopMat() && function.getParamCount() != 1) { + error(loc, "wrong number of arguments", constructorString.c_str(), ""); + return true; + } + if (type.isCoopMat() && + !(function[0].type->isScalar() || function[0].type->isCoopMat())) { + error(loc, "Cooperative matrix constructor argument must be scalar or cooperative matrix", constructorString.c_str(), ""); + return true; + } + + TIntermTyped* typed = node->getAsTyped(); + if (type.isCoopMat() && typed->getType().isCoopMat() && + ((extensionTurnedOn(E_GL_NV_cooperative_matrix2) && !type.sameCoopMatShape(typed->getType())) || + (!extensionTurnedOn(E_GL_NV_cooperative_matrix2) && !type.sameCoopMatShapeAndUse(typed->getType())))) { + error(loc, "Cooperative matrix type parameters mismatch", constructorString.c_str(), ""); + return true; + } + + if (typed == nullptr) { + error(loc, "constructor argument does not have a type", constructorString.c_str(), ""); + return true; + } + if (op != EOpConstructStruct && op != EOpConstructNonuniform && typed->getBasicType() == EbtSampler) { + if (op == EOpConstructUVec2 && extensionTurnedOn(E_GL_ARB_bindless_texture)) { + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeFunc); + } + else { + error(loc, "cannot convert a sampler", constructorString.c_str(), ""); + return true; + } + } + if (op != EOpConstructStruct && typed->isAtomic()) { + error(loc, "cannot convert an atomic_uint", constructorString.c_str(), ""); + return true; + } + if (typed->getBasicType() == EbtVoid) { + error(loc, "cannot convert a void", constructorString.c_str(), ""); + return true; + } + + return false; +} + +// Verify all the correct semantics for constructing a combined texture/sampler. +// Return true if the semantics are incorrect. +bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) +{ + TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change + const char* token = constructorName.c_str(); + // verify the constructor for bindless texture, the input must be ivec2 or uvec2 + if (function.getParamCount() == 1) { + TType* pType = function[0].type; + TBasicType basicType = pType->getBasicType(); + bool isIntegerVec2 = ((basicType == EbtUint || basicType == EbtInt) && pType->getVectorSize() == 2); + bool bindlessMode = extensionTurnedOn(E_GL_ARB_bindless_texture); + if (isIntegerVec2 && bindlessMode) { + if (pType->getSampler().isImage()) + intermediate.setBindlessImageMode(currentCaller, AstRefTypeFunc); + else + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeFunc); + return false; + } else { + if (!bindlessMode) + error(loc, "sampler-constructor requires the extension GL_ARB_bindless_texture enabled", token, ""); + else + error(loc, "sampler-constructor requires the input to be ivec2 or uvec2", token, ""); + return true; + } + } + + // exactly two arguments needed + if (function.getParamCount() != 2) { + error(loc, "sampler-constructor requires two arguments", token, ""); + return true; + } + + // For now, not allowing arrayed constructors, the rest of this function + // is set up to allow them, if this test is removed: + if (function.getType().isArray()) { + error(loc, "sampler-constructor cannot make an array of samplers", token, ""); + return true; + } + + // first argument + // * the constructor's first argument must be a texture type + // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array) + // of the texture type must match that of the constructed sampler type + // (that is, the suffixes of the type of the first argument and the + // type of the constructor will be spelled the same way) + if (function[0].type->getBasicType() != EbtSampler || + ! function[0].type->getSampler().isTexture() || + function[0].type->isArray()) { + error(loc, "sampler-constructor first argument must be a scalar *texture* type", token, ""); + return true; + } + // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() + TSampler texture = function.getType().getSampler(); + texture.setCombined(false); + texture.shadow = false; + if (texture != function[0].type->getSampler()) { + error(loc, "sampler-constructor first argument must be a *texture* type" + " matching the dimensionality and sampled type of the constructor", token, ""); + return true; + } + + // second argument + // * the constructor's second argument must be a scalar of type + // *sampler* or *samplerShadow* + if ( function[1].type->getBasicType() != EbtSampler || + ! function[1].type->getSampler().isPureSampler() || + function[1].type->isArray()) { + error(loc, "sampler-constructor second argument must be a scalar sampler or samplerShadow", token, ""); + return true; + } + + return false; +} + +// Checks to see if a void variable has been declared and raise an error message for such a case +// +// returns true in case of an error +// +bool TParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) +{ + if (basicType == EbtVoid) { + error(loc, "illegal use of type 'void'", identifier.c_str(), ""); + return true; + } + + return false; +} + +// Checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type) +{ + if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) + error(loc, "boolean expression expected", "", ""); +} + +// This function checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType) +{ + if (pType.basicType != EbtBool || pType.arraySizes || pType.matrixCols > 1 || (pType.vectorSize > 1)) + error(loc, "boolean expression expected", "", ""); +} + +void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/) +{ + // Check that the appropriate extension is enabled if external sampler is used. + // There are two extensions. The correct one must be used based on GLSL version. + if (type.getBasicType() == EbtSampler && type.getSampler().isExternal()) { + if (version < 300) { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external, "samplerExternalOES"); + } else { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external_essl3, "samplerExternalOES"); + } + } + if (type.getSampler().isYuv()) { + requireExtensions(loc, 1, &E_GL_EXT_YUV_target, "__samplerExternal2DY2YEXT"); + } + + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtSampler)) { + // For bindless texture, sampler can be declared as an struct member + if (extensionTurnedOn(E_GL_ARB_bindless_texture)) { + if (type.getSampler().isImage()) + intermediate.setBindlessImageMode(currentCaller, AstRefTypeVar); + else + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeVar); + } + else { + error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str()); + } + } + else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform) { + // For bindless texture, sampler can be declared as an input/output/block member + if (extensionTurnedOn(E_GL_ARB_bindless_texture)) { + if (type.getSampler().isImage()) + intermediate.setBindlessImageMode(currentCaller, AstRefTypeVar); + else + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeVar); + } + else { + // non-uniform sampler + // not yet: okay if it has an initializer + // if (! initializer) + if (type.getSampler().isAttachmentEXT() && type.getQualifier().storage != EvqTileImageEXT) + error(loc, "can only be used in tileImageEXT variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getQualifier().storage != EvqTileImageEXT) + error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); + } + } +} + +void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAtomicUint)) + error(loc, "non-uniform struct contains an atomic_uint:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAtomicUint && type.getQualifier().storage != EvqUniform) + error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); +} + +void TParseContext::accStructCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAccStruct)) + error(loc, "non-uniform struct contains an accelerationStructureNV:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAccStruct && type.getQualifier().storage != EvqUniform) + error(loc, "accelerationStructureNV can only be used in uniform variables or function parameters:", + type.getBasicTypeString().c_str(), identifier.c_str()); + +} + +void TParseContext::hitObjectNVCheck(const TSourceLoc & loc, const TType & type, const TString & identifier) +{ + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtHitObjectNV)) { + error(loc, "struct is not allowed to contain hitObjectNV:", type.getTypeName().c_str(), identifier.c_str()); + } else if (type.getBasicType() == EbtHitObjectNV) { + TStorageQualifier qualifier = type.getQualifier().storage; + if (qualifier != EvqGlobal && qualifier != EvqTemporary) { + error(loc, "hitObjectNV can only be declared in global or function scope with no storage qualifier:", "hitObjectNV", identifier.c_str()); + } + } +} + +void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (parsingBuiltins) + return; + + if (type.getQualifier().storage != EvqUniform) + return; + + if (type.containsNonOpaque()) { + // Vulkan doesn't allow transparent uniforms outside of blocks + if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed) + vulkanRemoved(loc, "non-opaque uniforms outside a block"); + // OpenGL wants locations on these (unless they are getting automapped) + if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations()) + error(loc, "non-opaque uniform variables need a layout(location=L)", identifier.c_str(), ""); + } +} + +// +// Qualifier checks knowing the qualifier and that it is a member of a struct/block. +// +void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType) +{ + globalQualifierFixCheck(publicType.loc, publicType.qualifier, true); + checkNoShaderLayouts(publicType.loc, publicType.shaderQualifiers); + if (publicType.qualifier.isNonUniform()) { + error(publicType.loc, "not allowed on block or structure members", "nonuniformEXT", ""); + publicType.qualifier.nonUniform = false; + } +} + +// +// Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. +// +void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier, bool isMemberCheck, const TPublicType* publicType) +{ + bool nonuniformOkay = false; + + // move from parameter/unknown qualifiers to pipeline in/out qualifiers + switch (qualifier.storage) { + case EvqIn: + profileRequires(loc, ENoProfile, 130, nullptr, "in for stage inputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "in for stage inputs"); + qualifier.storage = EvqVaryingIn; + nonuniformOkay = true; + break; + case EvqOut: + profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs"); + qualifier.storage = EvqVaryingOut; + if (intermediate.isInvariantAll()) + qualifier.invariant = true; + break; + case EvqInOut: + qualifier.storage = EvqVaryingIn; + error(loc, "cannot use 'inout' at global scope", "", ""); + break; + case EvqGlobal: + case EvqTemporary: + nonuniformOkay = true; + break; + case EvqUniform: + // According to GLSL spec: The std430 qualifier is supported only for shader storage blocks; a shader using + // the std430 qualifier on a uniform block will fail to compile. + // Only check the global declaration: layout(std430) uniform; + if (blockName == nullptr && + qualifier.layoutPacking == ElpStd430) + { + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "default std430 layout for uniform"); + } + + if (publicType != nullptr && publicType->isImage() && + (qualifier.layoutFormat > ElfExtSizeGuard && qualifier.layoutFormat < ElfCount)) + qualifier.layoutFormat = mapLegacyLayoutFormat(qualifier.layoutFormat, publicType->sampler.getBasicType()); + + break; + default: + break; + } + + if (!nonuniformOkay && qualifier.isNonUniform()) + error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", ""); + + if (qualifier.isSpirvByReference()) + error(loc, "can only apply to parameter", "spirv_by_reference", ""); + + if (qualifier.isSpirvLiteral()) + error(loc, "can only apply to parameter", "spirv_literal", ""); + + // Storage qualifier isn't ready for memberQualifierCheck, we should skip invariantCheck for it. + if (!isMemberCheck || structNestingLevel > 0) + invariantCheck(loc, qualifier); + + if (qualifier.isFullQuads()) { + if (qualifier.storage != EvqVaryingIn) + error(loc, "can only apply to input layout", "full_quads ", ""); + intermediate.setReqFullQuadsMode(); + } + + if (qualifier.isQuadDeriv()) { + if (qualifier.storage != EvqVaryingIn) + error(loc, "can only apply to input layout", "quad_derivatives", ""); + intermediate.setQuadDerivMode(); + } +} + +// +// Check a full qualifier and type (no variable yet) at global level. +// +void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQualifier& qualifier, const TPublicType& publicType) +{ + if (! symbolTable.atGlobalLevel()) + return; + + if (!(publicType.userDef && publicType.userDef->isReference()) && !parsingBuiltins) { + if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } + } + + if (qualifier.storage == EvqBuffer && + publicType.basicType != EbtBlock && + !qualifier.hasBufferReference()) + error(loc, "buffers can be declared only as blocks", "buffer", ""); + + if (qualifier.storage != EvqVaryingIn && publicType.basicType == EbtDouble && + extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) && language == EShLangVertex && + version < 400) { + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, E_GL_ARB_gpu_shader_fp64, "vertex-shader `double` type"); + } + if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut) + return; + + if (publicType.shaderQualifiers.hasBlendEquation()) + error(loc, "can only be applied to a standalone 'out'", "blend equation", ""); + + // now, knowing it is a shader in/out, do all the in/out semantic checks + + if (publicType.basicType == EbtBool && !parsingBuiltins) { + error(loc, "cannot be bool", GetStorageQualifierString(qualifier.storage), ""); + return; + } + + if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble) { + profileRequires(loc, EEsProfile, 300, nullptr, "non-float shader input/output"); + profileRequires(loc, ~EEsProfile, 130, nullptr, "non-float shader input/output"); + } + + if (!qualifier.flat && !qualifier.isExplicitInterpolation() && !qualifier.isPervertexNV() && !qualifier.isPervertexEXT()) { + if (isTypeInt(publicType.basicType) || + publicType.basicType == EbtDouble || + (publicType.userDef && ( publicType.userDef->containsBasicType(EbtInt) + || publicType.userDef->containsBasicType(EbtUint) + || publicType.userDef->contains16BitInt() + || publicType.userDef->contains8BitInt() + || publicType.userDef->contains64BitInt() + || publicType.userDef->containsDouble()))) { + if (qualifier.storage == EvqVaryingIn && language == EShLangFragment) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + else if (qualifier.storage == EvqVaryingOut && language == EShLangVertex && version == 300) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + } + } + + if (qualifier.isPatch() && qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers with patch", "patch", ""); + + if (qualifier.isTaskPayload() && publicType.basicType == EbtBlock) + error(loc, "taskPayloadSharedEXT variables should not be declared as interface blocks", "taskPayloadSharedEXT", ""); + + if (qualifier.isTaskMemory() && publicType.basicType != EbtBlock) + error(loc, "taskNV variables can be declared only as blocks", "taskNV", ""); + + if (qualifier.storage == EvqVaryingIn) { + switch (language) { + case EShLangVertex: + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.arraySizes) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + if (publicType.basicType == EbtDouble) + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_vertex_attrib_64bit, "vertex-shader `double` type input"); + if (qualifier.isAuxiliary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant) + error(loc, "vertex input cannot be further qualified", "", ""); + break; + case EShLangFragment: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "fragment-shader struct input"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "fragment-shader struct input"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing an array"); + } + break; + case EShLangCompute: + if (! symbolTable.atBuiltInLevel()) + error(loc, "global storage input qualifier cannot be used in a compute shader", "in", ""); + break; + case EShLangTessControl: + if (qualifier.patch) + error(loc, "can only use on output in tessellation-control shader", "patch", ""); + break; + default: + break; + } + } else { + // qualifier.storage == EvqVaryingOut + switch (language) { + case EShLangVertex: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "vertex-shader struct output"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "vertex-shader struct output"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing an array"); + } + + break; + case EShLangFragment: + profileRequires(loc, EEsProfile, 300, nullptr, "fragment shader output"); + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.matrixRows > 0) { + error(loc, "cannot be a matrix", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (qualifier.isAuxiliary()) + error(loc, "can't use auxiliary qualifier on a fragment output", "centroid/sample/patch", ""); + if (qualifier.isInterpolation()) + error(loc, "can't use interpolation qualifier on a fragment output", "flat/smooth/noperspective", ""); + if (publicType.basicType == EbtDouble || publicType.basicType == EbtInt64 || publicType.basicType == EbtUint64) + error(loc, "cannot contain a double, int64, or uint64", GetStorageQualifierString(qualifier.storage), ""); + break; + + case EShLangCompute: + error(loc, "global storage output qualifier cannot be used in a compute shader", "out", ""); + break; + case EShLangTessEvaluation: + if (qualifier.patch) + error(loc, "can only use on input in tessellation-evaluation shader", "patch", ""); + break; + default: + break; + } + } +} + +// +// Merge characteristics of the 'src' qualifier into the 'dst'. +// If there is duplication, issue error messages, unless 'force' +// is specified, which means to just override default settings. +// +// Also, when force is false, it will be assumed that 'src' follows +// 'dst', for the purpose of error checking order for versions +// that require specific orderings of qualifiers. +// +void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force) +{ + // Multiple auxiliary qualifiers (mostly done later by 'individual qualifiers') + if (src.isAuxiliary() && dst.isAuxiliary()) + error(loc, "can only have one auxiliary qualifier (centroid, patch, and sample)", "", ""); + + // Multiple interpolation qualifiers (mostly done later by 'individual qualifiers') + if (src.isInterpolation() && dst.isInterpolation()) + error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective, __explicitInterpAMD)", "", ""); + + // Ordering + if (! force && ((!isEsProfile() && version < 420) || + (isEsProfile() && version < 310)) + && ! extensionTurnedOn(E_GL_ARB_shading_language_420pack)) { + // non-function parameters + if (src.isNoContraction() && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.invariant && (dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "invariant qualifier must appear before interpolation, storage, and precision qualifiers ", "", ""); + else if (src.isInterpolation() && (dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "interpolation qualifiers must appear before storage and precision qualifiers", "", ""); + else if (src.isAuxiliary() && (dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "Auxiliary qualifiers (centroid, patch, and sample) must appear before storage and precision qualifiers", "", ""); + else if (src.storage != EvqTemporary && (dst.precision != EpqNone)) + error(loc, "precision qualifier must appear as last qualifier", "", ""); + + // function parameters + if (src.isNoContraction() && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.storage == EvqConst && (dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "in/out must appear before const", "", ""); + } + + // Storage qualification + if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) + dst.storage = src.storage; + else if ((dst.storage == EvqIn && src.storage == EvqOut) || + (dst.storage == EvqOut && src.storage == EvqIn)) + dst.storage = EvqInOut; + else if ((dst.storage == EvqIn && src.storage == EvqConst) || + (dst.storage == EvqConst && src.storage == EvqIn)) + dst.storage = EvqConstReadOnly; + else if (src.storage != EvqTemporary && + src.storage != EvqGlobal) + error(loc, "too many storage qualifiers", GetStorageQualifierString(src.storage), ""); + + // Precision qualifiers + if (! force && src.precision != EpqNone && dst.precision != EpqNone) + error(loc, "only one precision qualifier allowed", GetPrecisionQualifierString(src.precision), ""); + if (dst.precision == EpqNone || (force && src.precision != EpqNone)) + dst.precision = src.precision; + + if (!force && ((src.coherent && (dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.devicecoherent && (dst.coherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.queuefamilycoherent && (dst.coherent || dst.devicecoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.workgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.subgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.shadercallcoherent)) || + (src.shadercallcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)))) { + error(loc, "only one coherent/devicecoherent/queuefamilycoherent/workgroupcoherent/subgroupcoherent/shadercallcoherent qualifier allowed", + GetPrecisionQualifierString(src.precision), ""); + } + + // Layout qualifiers + mergeObjectLayoutQualifiers(dst, src, false); + + // individual qualifiers + bool repeated = false; + #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; + MERGE_SINGLETON(invariant); + MERGE_SINGLETON(centroid); + MERGE_SINGLETON(smooth); + MERGE_SINGLETON(flat); + MERGE_SINGLETON(specConstant); + MERGE_SINGLETON(noContraction); + MERGE_SINGLETON(nopersp); + MERGE_SINGLETON(explicitInterp); + MERGE_SINGLETON(perPrimitiveNV); + MERGE_SINGLETON(perViewNV); + MERGE_SINGLETON(perTaskNV); + MERGE_SINGLETON(patch); + MERGE_SINGLETON(sample); + MERGE_SINGLETON(coherent); + MERGE_SINGLETON(devicecoherent); + MERGE_SINGLETON(queuefamilycoherent); + MERGE_SINGLETON(workgroupcoherent); + MERGE_SINGLETON(subgroupcoherent); + MERGE_SINGLETON(shadercallcoherent); + MERGE_SINGLETON(nonprivate); + MERGE_SINGLETON(volatil); + MERGE_SINGLETON(nontemporal); + MERGE_SINGLETON(restrict); + MERGE_SINGLETON(readonly); + MERGE_SINGLETON(writeonly); + MERGE_SINGLETON(nonUniform); + + // SPIR-V storage class qualifier (GL_EXT_spirv_intrinsics) + dst.spirvStorageClass = src.spirvStorageClass; + + // SPIR-V decorate qualifiers (GL_EXT_spirv_intrinsics) + if (src.hasSpirvDecorate()) { + if (dst.hasSpirvDecorate()) { + const TSpirvDecorate& srcSpirvDecorate = src.getSpirvDecorate(); + TSpirvDecorate& dstSpirvDecorate = dst.getSpirvDecorate(); + for (auto& decorate : srcSpirvDecorate.decorates) { + if (dstSpirvDecorate.decorates.find(decorate.first) != dstSpirvDecorate.decorates.end()) + error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate", "(decoration=%u)", decorate.first); + else + dstSpirvDecorate.decorates.insert(decorate); + } + + for (auto& decorateId : srcSpirvDecorate.decorateIds) { + if (dstSpirvDecorate.decorateIds.find(decorateId.first) != dstSpirvDecorate.decorateIds.end()) + error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate_id", "(decoration=%u)", decorateId.first); + else + dstSpirvDecorate.decorateIds.insert(decorateId); + } + + for (auto& decorateString : srcSpirvDecorate.decorateStrings) { + if (dstSpirvDecorate.decorates.find(decorateString.first) != dstSpirvDecorate.decorates.end()) + error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate_string", "(decoration=%u)", decorateString.first); + else + dstSpirvDecorate.decorateStrings.insert(decorateString); + } + } else { + dst.spirvDecorate = src.spirvDecorate; + } + } + + if (repeated) + error(loc, "replicated qualifiers", "", ""); +} + +void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publicType, TPrecisionQualifier qualifier) +{ + TBasicType basicType = publicType.basicType; + + if (basicType == EbtSampler) { + defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)] = qualifier; + + return; // all is well + } + + if (basicType == EbtInt || basicType == EbtFloat) { + if (publicType.isScalar()) { + defaultPrecision[basicType] = qualifier; + if (basicType == EbtInt) { + defaultPrecision[EbtUint] = qualifier; + precisionManager.explicitIntDefaultSeen(); + } else + precisionManager.explicitFloatDefaultSeen(); + + return; // all is well + } + } + + if (basicType == EbtAtomicUint) { + if (qualifier != EpqHigh) + error(loc, "can only apply highp to atomic_uint", "precision", ""); + + return; + } + + error(loc, "cannot apply precision statement to this type; use 'float', 'int' or a sampler type", TType::getBasicString(basicType), ""); +} + +// used to flatten the sampler type space into a single dimension +// correlates with the declaration of defaultSamplerPrecision[] +int TParseContext::computeSamplerTypeIndex(TSampler& sampler) +{ + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.isExternal() ? 1 : 0; + int imageIndex = sampler.isImageClass() ? 1 : 0; + int msIndex = sampler.isMultiSample() ? 1 : 0; + + int flattened = EsdNumDims * (EbtNumTypes * (2 * (2 * (2 * (2 * arrayIndex + msIndex) + imageIndex) + shadowIndex) + + externalIndex) + sampler.type) + sampler.dim; + assert(flattened < maxSamplerIndex); + + return flattened; +} + +TPrecisionQualifier TParseContext::getDefaultPrecision(TPublicType& publicType) +{ + if (publicType.basicType == EbtSampler) + return defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)]; + else + return defaultPrecision[publicType.basicType]; +} + +void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType baseType, TQualifier& qualifier, bool isCoopMatOrVec) +{ + // Built-in symbols are allowed some ambiguous precisions, to be pinned down + // later by context. + if (! obeyPrecisionQualifiers() || parsingBuiltins) + return; + + if (baseType == EbtAtomicUint && qualifier.precision != EpqNone && qualifier.precision != EpqHigh) + error(loc, "atomic counters can only be highp", "atomic_uint", ""); + + if (isCoopMatOrVec) + return; + + if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) { + if (qualifier.precision == EpqNone) { + if (relaxedErrors()) + warn(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "substituting 'mediump'"); + else + error(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), ""); + qualifier.precision = EpqMedium; + defaultPrecision[baseType] = EpqMedium; + } + } else if (qualifier.precision != EpqNone) + error(loc, "type cannot have precision qualifier", TType::getBasicString(baseType), ""); +} + +void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier qualifier, const TType& type) +{ + if ((qualifier == EvqOut || qualifier == EvqInOut) && type.isOpaque() && !intermediate.getBindlessMode()) + error(loc, "samplers and atomic_uints cannot be output parameters", type.getBasicTypeString().c_str(), ""); + if (!parsingBuiltins && type.contains16BitFloat()) + requireFloat16Arithmetic(loc, type.getBasicTypeString().c_str(), "float16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains16BitInt()) + requireInt16Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains8BitInt()) + requireInt8Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int8 types can only be in uniform block or buffer storage"); +} + +bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType basicType) +{ + if (type.getBasicType() == basicType) + return true; + + if (type.getBasicType() == EbtStruct) { + const TTypeList& structure = *type.getStruct(); + for (unsigned int i = 0; i < structure.size(); ++i) { + if (containsFieldWithBasicType(*structure[i].type, basicType)) + return true; + } + } + + return false; +} + +// +// Do size checking for an array type's size. +// +void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair, + const char* sizeType, const bool isTypeParameter) +{ + bool isConst = false; + sizePair.node = nullptr; + + int size = 1; + + TIntermConstantUnion* constant = expr->getAsConstantUnion(); + if (constant) { + // handle true (non-specialization) constant + size = constant->getConstArray()[0].getIConst(); + isConst = true; + } else { + // see if it's a specialization constant instead + if (expr->getQualifier().isSpecConstant()) { + isConst = true; + sizePair.node = expr; + TIntermSymbol* symbol = expr->getAsSymbolNode(); + if (symbol && symbol->getConstArray().size() > 0) + size = symbol->getConstArray()[0].getIConst(); + } else if (expr->getAsUnaryNode() && expr->getAsUnaryNode()->getOp() == glslang::EOpArrayLength && + expr->getAsUnaryNode()->getOperand()->getType().isCoopMatNV()) { + isConst = true; + size = 1; + sizePair.node = expr->getAsUnaryNode(); + } + } + + sizePair.size = size; + + if (isTypeParameter) { + if (extensionTurnedOn(E_GL_NV_cooperative_matrix2)) { + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint && expr->getBasicType() != EbtBool)) { + error(loc, sizeType, "", "must be a constant integer or boolean expression"); + return; + } + } else { + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, sizeType, "", "must be a constant integer expression"); + return; + } + } + if (size < 0) { + error(loc, sizeType, "", "must be a non-negative integer"); + return; + } + } else { + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, sizeType, "", "must be a constant integer expression"); + return; + } + if (size <= 0) { + error(loc, sizeType, "", "must be a positive integer"); + return; + } + } +} + +// +// See if this qualifier can be an array. +// +// Returns true if there is an error. +// +bool TParseContext::arrayQualifierError(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqConst) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "const array"); + profileRequires(loc, EEsProfile, 300, nullptr, "const array"); + } + + if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + + return false; +} + +// +// See if this qualifier and type combination can be an array. +// Assumes arrayQualifierError() was also called to catch the type-invariant tests. +// +// Returns true if there is an error. +// +bool TParseContext::arrayError(const TSourceLoc& loc, const TType& type) +{ + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangVertex) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-array output"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-struct output"); + } + if (type.getQualifier().storage == EvqVaryingIn && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array input"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-struct input"); + } + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array output"); + } + + return false; +} + +// +// Require array to be completely sized +// +void TParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) +{ + if (!parsingBuiltins && arraySizes.hasUnsized()) + error(loc, "array size required", "", ""); +} + +void TParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) +{ + const TTypeList& structure = *type.getStruct(); + for (int m = 0; m < (int)structure.size(); ++m) { + const TType& member = *structure[m].type; + if (member.isArray()) + arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); + } +} + +void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qualifier, TArraySizes* arraySizes, + const TIntermTyped* initializer, bool lastMember) +{ + assert(arraySizes); + + // always allow special built-in ins/outs sized to topologies + if (parsingBuiltins) + return; + + // initializer must be a sized array, in which case + // allow the initializer to set any unknown array sizes + if (initializer != nullptr) { + if (initializer->getType().isUnsizedArray()) + error(loc, "array initializer must be sized", "[]", ""); + return; + } + + // No environment allows any non-outer-dimension to be implicitly sized + if (arraySizes->isInnerUnsized()) { + error(loc, "only outermost dimension of an array of arrays can be implicitly sized", "[]", ""); + arraySizes->clearInnerUnsized(); + } + + if (arraySizes->isInnerSpecialization() && + (qualifier.storage != EvqTemporary && qualifier.storage != EvqGlobal && qualifier.storage != EvqShared && qualifier.storage != EvqConst)) + error(loc, "only outermost dimension of an array of arrays can be a specialization constant", "[]", ""); + + // desktop always allows outer-dimension-unsized variable arrays, + if (!isEsProfile()) + return; + + // for ES, if size isn't coming from an initializer, it has to be explicitly declared now, + // with very few exceptions + + // implicitly-sized io exceptions: + switch (language) { + case EShLangGeometry: + if (qualifier.storage == EvqVaryingIn) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_geometry_shader, AEP_geometry_shader)) + return; + break; + case EShLangTessControl: + if ( qualifier.storage == EvqVaryingIn || + (qualifier.storage == EvqVaryingOut && ! qualifier.isPatch())) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangTessEvaluation: + if ((qualifier.storage == EvqVaryingIn && ! qualifier.isPatch()) || + qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangMesh: + if (qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_mesh_shader, AEP_mesh_shader)) + return; + break; + default: + break; + } + + // last member of ssbo block exception: + if (qualifier.storage == EvqBuffer && lastMember) + return; + + arraySizeRequiredCheck(loc, *arraySizes); +} + +void TParseContext::arrayOfArrayVersionCheck(const TSourceLoc& loc, const TArraySizes* sizes) +{ + if (sizes == nullptr || sizes->getNumDims() == 1) + return; + + const char* feature = "arrays of arrays"; + + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature); +} + +// +// Do all the semantic checking for declaring or redeclaring an array, with and +// without a size, and make the right changes to the symbol table. +// +void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol) +{ + if (symbol == nullptr) { + bool currentScope; + symbol = symbolTable.find(identifier, nullptr, ¤tScope); + + if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { + // bad shader (errors already reported) trying to redeclare a built-in name as an array + symbol = nullptr; + return; + } + if (symbol == nullptr || ! currentScope) { + // + // Successfully process a new definition. + // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) + // + symbol = new TVariable(&identifier, type); + symbolTable.insert(*symbol); + if (symbolTable.atGlobalLevel()) + trackLinkage(*symbol); + + if (! symbolTable.atBuiltInLevel()) { + if (isIoResizeArray(type)) { + ioArraySymbolResizeList.push_back(symbol); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, symbol->getWritableType()); + } + + return; + } + if (symbol->getAsAnonMember()) { + error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); + symbol = nullptr; + return; + } + } + + // + // Process a redeclaration. + // + + if (symbol == nullptr) { + error(loc, "array variable name expected", identifier.c_str(), ""); + return; + } + + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); + + if (! existingType.isArray()) { + error(loc, "redeclaring non-array as array", identifier.c_str(), ""); + return; + } + + if (! existingType.sameElementType(type)) { + error(loc, "redeclaration of array with a different element type", identifier.c_str(), ""); + return; + } + + if (! existingType.sameInnerArrayness(type)) { + error(loc, "redeclaration of array with a different array dimensions or sizes", identifier.c_str(), ""); + return; + } + + if (existingType.isSizedArray()) { + // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size + if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize())) + error(loc, "redeclaration of array with size", identifier.c_str(), ""); + return; + } + + arrayLimitCheck(loc, identifier, type.getOuterArraySize()); + + existingType.updateArraySizes(type); + + if (isIoResizeArray(type)) + checkIoArraysConsistency(loc); +} + +// Policy and error check for needing a runtime sized array. +void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base) +{ + // runtime length implies runtime sizeable, so no problem + if (isRuntimeLength(base)) + return; + + if (base.getType().getQualifier().builtIn == EbvSampleMask) + return; + + // Check for last member of a bufferreference type, which is runtime sizeable + // but doesn't support runtime length + if (base.getType().getQualifier().storage == EvqBuffer) { + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && + binary->getOp() == EOpIndexDirectStruct && + binary->getLeft()->isReference()) { + + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size(); + if (index == memberCount - 1) + return; + } + } + + // check for additional things allowed by GL_EXT_nonuniform_qualifier + if (base.getBasicType() == EbtSampler || base.getBasicType() == EbtAccStruct || base.getBasicType() == EbtRayQuery || + base.getBasicType() == EbtHitObjectNV || (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer())) + requireExtensions(loc, 1, &E_GL_EXT_nonuniform_qualifier, "variable index"); + else + error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); +} + +// Policy decision for whether a run-time .length() is allowed. +bool TParseContext::isRuntimeLength(const TIntermTyped& base) const +{ + if (base.getType().getQualifier().storage == EvqBuffer) { + // in a buffer block + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) { + // is it the last member? + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + + if (binary->getLeft()->isReference()) + return false; + + const int memberCount = (int)binary->getLeft()->getType().getStruct()->size(); + if (index == memberCount - 1) + return true; + } + } + + return false; +} + +// Check if mesh perviewNV attributes have a view dimension +// and resize it to gl_MaxMeshViewCountNV when implicitly sized. +void TParseContext::checkAndResizeMeshViewDim(const TSourceLoc& loc, TType& type, bool isBlockMember) +{ + // see if member is a per-view attribute + if (!type.getQualifier().isPerView()) + return; + + if ((isBlockMember && type.isArray()) || (!isBlockMember && type.isArrayOfArrays())) { + // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value. + int maxViewCount = parsingBuiltins ? 4 : resources.maxMeshViewCountNV; + // For block members, outermost array dimension is the view dimension. + // For non-block members, outermost array dimension is the vertex/primitive dimension + // and 2nd outermost is the view dimension. + int viewDim = isBlockMember ? 0 : 1; + int viewDimSize = type.getArraySizes()->getDimSize(viewDim); + + if (viewDimSize != UnsizedArraySize && viewDimSize != maxViewCount) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (viewDimSize == UnsizedArraySize) + type.getArraySizes()->setDimSize(viewDim, maxViewCount); + } + else { + error(loc, "requires a view array dimension", "perviewNV", ""); + } +} + +// Returns true if the first argument to the #line directive is the line number for the next line. +// +// Desktop, pre-version 3.30: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line+1 and +// source string number source-string-number." +// +// Desktop, version 3.30 and later, and ES: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line and +// source string number source-string-number. +bool TParseContext::lineDirectiveShouldSetNextLine() const +{ + return isEsProfile() || version >= 330; +} + +// +// Enforce non-initializer type/qualifier rules. +// +void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier, TType& type) +{ + // + // Make the qualifier make sense, given that there is not an initializer. + // + if (type.getQualifier().storage == EvqConst || + type.getQualifier().storage == EvqConstReadOnly) { + type.getQualifier().makeTemporary(); + error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), ""); + } +} + +// +// See if the identifier is a built-in symbol that can be redeclared, and if so, +// copy the symbol table's read-only built-in variable to the current +// global level, where it can be modified based on the passed in type. +// +// Returns nullptr if no redeclaration took place; meaning a normal declaration still +// needs to occur for it, not necessarily an error. +// +// Returns a redeclared and type-modified variable if a redeclarated occurred. +// +TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier, + const TQualifier& qualifier, const TShaderQualifiers& publicType) +{ + if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) + return nullptr; + + bool nonEsRedecls = (!isEsProfile() && (version >= 130 || identifier == "gl_TexCoord")); + bool esRedecls = (isEsProfile() && + (version >= 320 || extensionsTurnedOn(Num_AEP_shader_io_blocks, AEP_shader_io_blocks))); + if (! esRedecls && ! nonEsRedecls) + return nullptr; + + // Special case when using GL_ARB_separate_shader_objects + bool ssoPre150 = false; // means the only reason this variable is redeclared is due to this combination + if (!isEsProfile() && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) { + if (identifier == "gl_Position" || + identifier == "gl_PointSize" || + identifier == "gl_ClipVertex" || + identifier == "gl_FogFragCoord") + ssoPre150 = true; + } + + // Potentially redeclaring a built-in variable... + + if (ssoPre150 || + (identifier == "gl_FragDepth" && ((nonEsRedecls && version >= 420) || esRedecls)) || + (identifier == "gl_FragCoord" && ((nonEsRedecls && version >= 140) || esRedecls)) || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance" || + identifier == "gl_ShadingRateEXT" || + identifier == "gl_PrimitiveShadingRateEXT" || + identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + (identifier == "gl_Color" && language == EShLangFragment) || + (identifier == "gl_FragStencilRefARB" && (nonEsRedecls && version >= 140) + && language == EShLangFragment) || + identifier == "gl_SampleMask" || + identifier == "gl_Layer" || + identifier == "gl_PrimitiveIndicesNV" || + identifier == "gl_PrimitivePointIndicesEXT" || + identifier == "gl_PrimitiveLineIndicesEXT" || + identifier == "gl_PrimitiveTriangleIndicesEXT" || + identifier == "gl_TexCoord") { + + // Find the existing symbol, if any. + bool builtIn; + TSymbol* symbol = symbolTable.find(identifier, &builtIn); + + // If the symbol was not found, this must be a version/profile/stage + // that doesn't have it. + if (! symbol) + return nullptr; + + // If it wasn't at a built-in level, then it's already been redeclared; + // that is, this is a redeclaration of a redeclaration; reuse that initial + // redeclaration. Otherwise, make the new one. + if (builtIn) { + makeEditable(symbol); + symbolTable.amendSymbolIdLevel(*symbol); + } + + // Now, modify the type of the copy, as per the type of the current redeclaration. + + TQualifier& symbolQualifier = symbol->getWritableType().getQualifier(); + if (ssoPre150) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot redeclare after use", identifier.c_str(), ""); + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || (language == EShLangVertex && qualifier.storage != EvqVaryingOut) || + (language == EShLangFragment && qualifier.storage != EvqVaryingIn)) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + if (! qualifier.smooth) + error(loc, "cannot change interpolation qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + identifier == "gl_Color") { + symbolQualifier.flat = qualifier.flat; + symbolQualifier.smooth = qualifier.smooth; + symbolQualifier.nopersp = qualifier.nopersp; + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || symbol->getType().getQualifier().storage != qualifier.storage) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_TexCoord" || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance") { + if (qualifier.hasLayout() || qualifier.isMemory() || qualifier.isAuxiliary() || + qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + symbolQualifier.storage != qualifier.storage) + error(loc, "cannot change qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FragCoord") { + if (!intermediate.getTexCoordRedeclared() && intermediate.inIoAccessed("gl_FragCoord")) + error(loc, "cannot redeclare after use", "gl_FragCoord", ""); + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingIn) + error(loc, "cannot change input storage qualification of", "redeclaration", symbol->getName().c_str()); + if (! builtIn && (publicType.pixelCenterInteger != intermediate.getPixelCenterInteger() || + publicType.originUpperLeft != intermediate.getOriginUpperLeft())) + error(loc, "cannot redeclare with different qualification:", "redeclaration", symbol->getName().c_str()); + + + intermediate.setTexCoordRedeclared(); + if (publicType.pixelCenterInteger) + intermediate.setPixelCenterInteger(); + if (publicType.originUpperLeft) + intermediate.setOriginUpperLeft(); + } else if (identifier == "gl_FragDepth") { + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + if (publicType.layoutDepth != EldNone) { + if (intermediate.inIoAccessed("gl_FragDepth")) + error(loc, "cannot redeclare after use", "gl_FragDepth", ""); + if (! intermediate.setDepth(publicType.layoutDepth)) + error(loc, "all redeclarations must use the same depth layout on", "redeclaration", symbol->getName().c_str()); + } + } else if (identifier == "gl_FragStencilRefARB") { + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + if (publicType.layoutStencil != ElsNone) { + if (intermediate.inIoAccessed("gl_FragStencilRefARB")) + error(loc, "cannot redeclare after use", "gl_FragStencilRefARB", ""); + if (!intermediate.setStencil(publicType.layoutStencil)) + error(loc, "all redeclarations must use the same stencil layout on", "redeclaration", + symbol->getName().c_str()); + } + } + else if ( + identifier == "gl_PrimitiveIndicesNV") { + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + } + else if (identifier == "gl_SampleMask") { + if (!publicType.layoutOverrideCoverage) { + error(loc, "redeclaration only allowed for override_coverage layout", "redeclaration", symbol->getName().c_str()); + } + intermediate.setLayoutOverrideCoverage(); + } + else if (identifier == "gl_Layer") { + if (!qualifier.layoutViewportRelative && qualifier.layoutSecondaryViewportRelativeOffset == -2048) + error(loc, "redeclaration only allowed for viewport_relative or secondary_view_offset layout", "redeclaration", symbol->getName().c_str()); + symbolQualifier.layoutViewportRelative = qualifier.layoutViewportRelative; + symbolQualifier.layoutSecondaryViewportRelativeOffset = qualifier.layoutSecondaryViewportRelativeOffset; + } + + // TODO: semantics quality: separate smooth from nothing declared, then use IsInterpolation for several tests above + + return symbol; + } + + return nullptr; +} + +// +// Either redeclare the requested block, or give an error message why it can't be done. +// +// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size +void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, + const TString* instanceName, TArraySizes* arraySizes) +{ + const char* feature = "built-in block redeclaration"; + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + + if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment" && + blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV" && + blockName != "gl_MeshPerVertexEXT" && blockName != "gl_MeshPerPrimitiveEXT") { + error(loc, "cannot redeclare block: ", "block declaration", blockName.c_str()); + return; + } + + // Redeclaring a built-in block... + + if (instanceName && ! builtInName(*instanceName)) { + error(loc, "cannot redeclare a built-in block with a user name", instanceName->c_str(), ""); + return; + } + + // Blocks with instance names are easy to find, lookup the instance name, + // Anonymous blocks need to be found via a member. + bool builtIn; + TSymbol* block; + if (instanceName) + block = symbolTable.find(*instanceName, &builtIn); + else + block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn); + + // If the block was not found, this must be a version/profile/stage + // that doesn't have it, or the instance name is wrong. + const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str(); + if (! block) { + error(loc, "no declaration found for redeclaration", errorName, ""); + return; + } + // Built-in blocks cannot be redeclared more than once, which if happened, + // we'd be finding the already redeclared one here, rather than the built in. + if (! builtIn) { + error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), ""); + return; + } + + // Copy the block to make a writable version, to insert into the block table after editing. + block = symbolTable.copyUpDeferredInsert(block); + + if (block->getType().getBasicType() != EbtBlock) { + error(loc, "cannot redeclare a non block as a block", errorName, ""); + return; + } + + // Fix XFB stuff up, it applies to the order of the redeclaration, not + // the order of the original members. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + if (!currentBlockQualifier.hasStream()) + currentBlockQualifier.layoutStream = globalOutputDefaults.layoutStream; + fixXfbOffsets(currentBlockQualifier, newTypeList); + } + + // Edit and error check the container against the redeclaration + // - remove unused members + // - ensure remaining qualifiers/types match + + TType& type = block->getWritableType(); + + // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position" + // for passthrough purpose, the redeclared block should have the same qualifers as + // the current one + if (currentBlockQualifier.layoutPassthrough) { + type.getQualifier().layoutPassthrough = currentBlockQualifier.layoutPassthrough; + type.getQualifier().storage = currentBlockQualifier.storage; + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + + TTypeList::iterator member = type.getWritableStruct()->begin(); + size_t numOriginalMembersFound = 0; + while (member != type.getStruct()->end()) { + // look for match + bool found = false; + TTypeList::const_iterator newMember; + TSourceLoc memberLoc; + memberLoc.init(); + for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) { + if (member->type->getFieldName() == newMember->type->getFieldName()) { + found = true; + memberLoc = newMember->loc; + break; + } + } + + if (found) { + ++numOriginalMembersFound; + // - ensure match between redeclared members' types + // - check for things that can't be changed + // - update things that can be changed + TType& oldType = *member->type; + const TType& newType = *newMember->type; + if (! newType.sameElementType(oldType)) + error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), ""); + if (oldType.isArray() != newType.isArray()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && ! oldType.sameArrayness(newType) && oldType.isSizedArray()) + error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.isArray()) + arrayLimitCheck(loc, member->type->getFieldName(), newType.getOuterArraySize()); + if (oldType.getQualifier().isPerView() && ! newType.getQualifier().isPerView()) + error(memberLoc, "missing perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.getQualifier().isPerView()) + error(memberLoc, "cannot add perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (newType.getQualifier().isPerView()) { + if (oldType.getArraySizes()->getNumDims() != newType.getArraySizes()->getNumDims()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! newType.isUnsizedArray() && newType.getOuterArraySize() != resources.maxMeshViewCountNV) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (newType.getArraySizes()->getNumDims() == 2) { + int innerDimSize = newType.getArraySizes()->getDimSize(1); + arrayLimitCheck(memberLoc, member->type->getFieldName(), innerDimSize); + oldType.getArraySizes()->setDimSize(1, innerDimSize); + } + } + if (oldType.getQualifier().isPerPrimitive() && ! newType.getQualifier().isPerPrimitive()) + error(memberLoc, "missing perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerPrimitive() && newType.getQualifier().isPerPrimitive()) + error(memberLoc, "cannot add perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().isMemory()) + error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasNonXfbLayout()) + error(memberLoc, "cannot add non-XFB layout to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().patch) + error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasXfbBuffer() && + newType.getQualifier().layoutXfbBuffer != currentBlockQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + if (newType.getQualifier().hasStream() && + newType.getQualifier().layoutStream != currentBlockQualifier.layoutStream) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_stream", ""); + oldType.getQualifier().centroid = newType.getQualifier().centroid; + oldType.getQualifier().sample = newType.getQualifier().sample; + oldType.getQualifier().invariant = newType.getQualifier().invariant; + oldType.getQualifier().noContraction = newType.getQualifier().noContraction; + oldType.getQualifier().smooth = newType.getQualifier().smooth; + oldType.getQualifier().flat = newType.getQualifier().flat; + oldType.getQualifier().nopersp = newType.getQualifier().nopersp; + oldType.getQualifier().layoutXfbOffset = newType.getQualifier().layoutXfbOffset; + oldType.getQualifier().layoutXfbBuffer = newType.getQualifier().layoutXfbBuffer; + oldType.getQualifier().layoutXfbStride = newType.getQualifier().layoutXfbStride; + if (oldType.getQualifier().layoutXfbOffset != TQualifier::layoutXfbBufferEnd) { + // If any member has an xfb_offset, then the block's xfb_buffer inherents current xfb_buffer, + // and for xfb processing, the member needs it as well, along with xfb_stride. + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + oldType.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + if (oldType.isUnsizedArray() && newType.isSizedArray()) + oldType.changeOuterArraySize(newType.getOuterArraySize()); + + // check and process the member's type, which will include managing xfb information + layoutTypeCheck(loc, oldType); + + // go to next member + ++member; + } else { + // For missing members of anonymous blocks that have been redeclared, + // hide the original (shared) declaration. + // Instance-named blocks can just have the member removed. + if (instanceName) + member = type.getWritableStruct()->erase(member); + else { + member->type->hideMember(); + ++member; + } + } + } + + if (spvVersion.vulkan > 0) { + // ...then streams apply to built-in blocks, instead of them being only on stream 0 + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + } + + if (numOriginalMembersFound < newTypeList.size()) + error(loc, "block redeclaration has extra members", blockName.c_str(), ""); + if (type.isArray() != (arraySizes != nullptr) || + (type.isArray() && arraySizes != nullptr && type.getArraySizes()->getNumDims() != arraySizes->getNumDims())) + error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), ""); + else if (type.isArray()) { + // At this point, we know both are arrays and both have the same number of dimensions. + + // It is okay for a built-in block redeclaration to be unsized, and keep the size of the + // original block declaration. + if (!arraySizes->isSized() && type.isSizedArray()) + arraySizes->changeOuterSize(type.getOuterArraySize()); + + // And, okay to be giving a size to the array, by the redeclaration + if (!type.isSizedArray() && arraySizes->isSized()) + type.changeOuterArraySize(arraySizes->getOuterSize()); + + // Now, they must match in all dimensions. + if (type.isSizedArray() && *type.getArraySizes() != *arraySizes) + error(loc, "cannot change array size of redeclared block", blockName.c_str(), ""); + } + + symbolTable.insert(*block); + + // Check for general layout qualifier errors + layoutObjectCheck(loc, *block); + + // Tracking for implicit sizing of array + if (isIoResizeArray(block->getType())) { + ioArraySymbolResizeList.push_back(block); + checkIoArraysConsistency(loc, true); + } else if (block->getType().isArray()) + fixIoArraySize(loc, block->getWritableType()); + + // Save it in the AST for linker use. + trackLinkage(*block); +} + +void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type) +{ + switch (qualifier) { + case EvqConst: + case EvqConstReadOnly: + type.getQualifier().storage = EvqConstReadOnly; + break; + case EvqIn: + case EvqOut: + case EvqInOut: + case EvqTileImageEXT: + type.getQualifier().storage = qualifier; + break; + case EvqGlobal: + case EvqTemporary: + type.getQualifier().storage = EvqIn; + break; + default: + type.getQualifier().storage = EvqIn; + error(loc, "storage qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), ""); + break; + } +} + +void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type) +{ + if (qualifier.isMemory()) { + type.getQualifier().volatil = qualifier.volatil; + type.getQualifier().nontemporal = qualifier.nontemporal; + type.getQualifier().coherent = qualifier.coherent; + type.getQualifier().devicecoherent = qualifier.devicecoherent ; + type.getQualifier().queuefamilycoherent = qualifier.queuefamilycoherent; + type.getQualifier().workgroupcoherent = qualifier.workgroupcoherent; + type.getQualifier().subgroupcoherent = qualifier.subgroupcoherent; + type.getQualifier().shadercallcoherent = qualifier.shadercallcoherent; + type.getQualifier().nonprivate = qualifier.nonprivate; + type.getQualifier().readonly = qualifier.readonly; + type.getQualifier().writeonly = qualifier.writeonly; + type.getQualifier().restrict = qualifier.restrict; + } + + if (qualifier.isAuxiliary() || + qualifier.isInterpolation()) + error(loc, "cannot use auxiliary or interpolation qualifiers on a function parameter", "", ""); + if (qualifier.hasLayout()) + error(loc, "cannot use layout qualifiers on a function parameter", "", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on a function parameter", "", ""); + if (qualifier.isNoContraction()) { + if (qualifier.isParamOutput()) + type.getQualifier().setNoContraction(); + else + warn(loc, "qualifier has no effect on non-output parameters", "precise", ""); + } + if (qualifier.isNonUniform()) + type.getQualifier().nonUniform = qualifier.nonUniform; + if (qualifier.isSpirvByReference()) + type.getQualifier().setSpirvByReference(); + if (qualifier.isSpirvLiteral()) { + if (type.getBasicType() == EbtFloat || type.getBasicType() == EbtInt || type.getBasicType() == EbtUint || + type.getBasicType() == EbtBool) + type.getQualifier().setSpirvLiteral(); + else + error(loc, "cannot use spirv_literal qualifier", type.getBasicTypeString().c_str(), ""); + } + + paramCheckFixStorage(loc, qualifier.storage, type); +} + +void TParseContext::nestedBlockCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0 || blockNestingLevel > 0) + error(loc, "cannot nest a block definition inside a structure or block", "", ""); + ++blockNestingLevel; +} + +void TParseContext::nestedStructCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0 || blockNestingLevel > 0) + error(loc, "cannot nest a structure definition inside a structure or block", "", ""); + ++structNestingLevel; +} + +void TParseContext::arrayObjectCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + // Some versions don't allow comparing arrays or structures containing arrays + if (type.containsArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); + } +} + +void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (containsFieldWithBasicType(type, EbtSampler) && !extensionTurnedOn(E_GL_ARB_bindless_texture)) + error(loc, "can't use with samplers or structs containing samplers", op, ""); +} + +void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (containsFieldWithBasicType(type, EbtReference)) + error(loc, "can't use with reference types", op, ""); +} + +void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtFloat16)) + requireFloat16Arithmetic(loc, op, "can't use with structs containing float16"); + + if (type.isArray() && type.getBasicType() == EbtFloat16) + requireFloat16Arithmetic(loc, op, "can't use with arrays containing float16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing int16"); + + if (type.isArray() && type.getBasicType() == EbtInt16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing int16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing uint16"); + + if (type.isArray() && type.getBasicType() == EbtUint16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing uint16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing int8"); + + if (type.isArray() && type.getBasicType() == EbtInt8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing int8"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing uint8"); + + if (type.isArray() && type.getBasicType() == EbtUint8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing uint8"); +} + +void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.containsSpecializationSize()) + error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); +} + +void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType) +{ + const TTypeList& typeList = *publicType.userDef->getStruct(); + + // fix and check for member storage qualifiers and types that don't belong within a structure + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.isAuxiliary() || + memberQualifier.isInterpolation() || + (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal)) + error(memberLoc, "cannot use storage or interpolation qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.isMemory()) + error(memberLoc, "cannot use memory qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLayout()) { + error(memberLoc, "cannot use layout qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + memberQualifier.clearLayout(); + } + if (memberQualifier.invariant) + error(memberLoc, "cannot use invariant qualifier on structure members", typeList[member].type->getFieldName().c_str(), ""); + } +} + +// +// See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A: +// +// "The loop index has type int or float. +// +// "The for statement has the form: +// for ( init-declaration ; condition ; expression ) +// init-declaration has the form: type-specifier identifier = constant-expression +// condition has the form: loop-index relational_operator constant-expression +// where relational_operator is one of: > >= < <= == or != +// expression [sic] has one of the following forms: +// loop-index++ +// loop-index-- +// loop-index += constant-expression +// loop-index -= constant-expression +// +// The body is handled in an AST traversal. +// +void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop) +{ + // loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration + bool badInit = false; + if (! init || ! init->getAsAggregate() || init->getAsAggregate()->getSequence().size() != 1) + badInit = true; + TIntermBinary* binaryInit = nullptr; + if (! badInit) { + // get the declaration assignment + binaryInit = init->getAsAggregate()->getSequence()[0]->getAsBinaryNode(); + if (! binaryInit) + badInit = true; + } + if (badInit) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // loop index must be type int or float + if (! binaryInit->getType().isScalar() || (binaryInit->getBasicType() != EbtInt && binaryInit->getBasicType() != EbtFloat)) { + error(loc, "inductive loop requires a scalar 'int' or 'float' loop index", "limitations", ""); + return; + } + + // init is the form "loop-index = constant" + if (binaryInit->getOp() != EOpAssign || ! binaryInit->getLeft()->getAsSymbolNode() || ! binaryInit->getRight()->getAsConstantUnion()) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // get the unique id of the loop index + long long loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId(); + inductiveLoopIds.insert(loopIndex); + + // condition's form must be "loop-index relational-operator constant-expression" + bool badCond = ! loop->getTest(); + if (! badCond) { + TIntermBinary* binaryCond = loop->getTest()->getAsBinaryNode(); + badCond = ! binaryCond; + if (! badCond) { + switch (binaryCond->getOp()) { + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpLessThan: + case EOpLessThanEqual: + case EOpEqual: + case EOpNotEqual: + break; + default: + badCond = true; + } + } + if (binaryCond && (! binaryCond->getLeft()->getAsSymbolNode() || + binaryCond->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryCond->getRight()->getAsConstantUnion())) + badCond = true; + } + if (badCond) { + error(loc, "inductive-loop condition requires the form \"loop-index constant-expression\"", "limitations", ""); + return; + } + + // loop-index++ + // loop-index-- + // loop-index += constant-expression + // loop-index -= constant-expression + bool badTerminal = ! loop->getTerminal(); + if (! badTerminal) { + TIntermUnary* unaryTerminal = loop->getTerminal()->getAsUnaryNode(); + TIntermBinary* binaryTerminal = loop->getTerminal()->getAsBinaryNode(); + if (unaryTerminal || binaryTerminal) { + switch(loop->getTerminal()->getAsOperator()->getOp()) { + case EOpPostDecrement: + case EOpPostIncrement: + case EOpAddAssign: + case EOpSubAssign: + break; + default: + badTerminal = true; + } + } else + badTerminal = true; + if (binaryTerminal && (! binaryTerminal->getLeft()->getAsSymbolNode() || + binaryTerminal->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryTerminal->getRight()->getAsConstantUnion())) + badTerminal = true; + if (unaryTerminal && (! unaryTerminal->getOperand()->getAsSymbolNode() || + unaryTerminal->getOperand()->getAsSymbolNode()->getId() != loopIndex)) + badTerminal = true; + } + if (badTerminal) { + error(loc, "inductive-loop termination requires the form \"loop-index++, loop-index--, loop-index += constant-expression, or loop-index -= constant-expression\"", "limitations", ""); + return; + } + + // the body + inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable); +} + +// Do limit checks for built-in arrays. +void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size) +{ + if (identifier.compare("gl_TexCoord") == 0) + limitCheck(loc, size, "gl_MaxTextureCoords", "gl_TexCoord array size"); + else if (identifier.compare("gl_ClipDistance") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistance array size"); + else if (identifier.compare("gl_CullDistance") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistance array size"); + else if (identifier.compare("gl_ClipDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistancePerViewNV array size"); + else if (identifier.compare("gl_CullDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistancePerViewNV array size"); +} + +// See if the provided value is less than or equal to the symbol indicated by limit, +// which should be a constant in the symbol table. +void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* limit, const char* feature) +{ + TSymbol* symbol = symbolTable.find(limit); + assert(symbol->getAsVariable()); + const TConstUnionArray& constArray = symbol->getAsVariable()->getConstArray(); + assert(! constArray.empty()); + if (value > constArray[0].getIConst()) + error(loc, "must be less than or equal to", feature, "%s (%d)", limit, constArray[0].getIConst()); +} + +// +// Do any additional error checking, etc., once we know the parsing is done. +// +void TParseContext::finish() +{ + TParseContextBase::finish(); + + if (parsingBuiltins) + return; + + // Check on array indexes for ES 2.0 (version 100) limitations. + for (size_t i = 0; i < needsIndexLimitationChecking.size(); ++i) + constantIndexExpressionCheck(needsIndexLimitationChecking[i]); + + // Check for stages that are enabled by extension. + // Can't do this at the beginning, it is chicken and egg to add a stage by + // extension. + // Stage-specific features were correctly tested for already, this is just + // about the stage itself. + switch (language) { + case EShLangGeometry: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_geometry_shader, AEP_geometry_shader, "geometry shaders"); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_tessellation_shader, AEP_tessellation_shader, "tessellation shaders"); + else if (!isEsProfile() && version < 400) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_tessellation_shader, "tessellation shaders"); + break; + case EShLangCompute: + if (!isEsProfile() && version < 430) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders"); + break; + case EShLangTask: + requireExtensions(getCurrentLoc(), Num_AEP_mesh_shader, AEP_mesh_shader, "task shaders"); + break; + case EShLangMesh: + requireExtensions(getCurrentLoc(), Num_AEP_mesh_shader, AEP_mesh_shader, "mesh shaders"); + break; + default: + break; + } + + // Set default outputs for GL_NV_geometry_shader_passthrough + if (language == EShLangGeometry && extensionTurnedOn(E_SPV_NV_geometry_shader_passthrough)) { + if (intermediate.getOutputPrimitive() == ElgNone) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setOutputPrimitive(ElgPoints); break; + case ElgLines: intermediate.setOutputPrimitive(ElgLineStrip); break; + case ElgTriangles: intermediate.setOutputPrimitive(ElgTriangleStrip); break; + default: break; + } + } + if (intermediate.getVertices() == TQualifier::layoutNotSet) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setVertices(1); break; + case ElgLines: intermediate.setVertices(2); break; + case ElgTriangles: intermediate.setVertices(3); break; + default: break; + } + } + } +} + +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type, for qualifiers not having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { + publicType.qualifier.layoutMatrix = ElmColumnMajor; + return; + } + if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { + publicType.qualifier.layoutMatrix = ElmRowMajor; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpPacked)) { + if (spvVersion.spv != 0) { + if (spvVersion.vulkanRelaxed) + return; // silently ignore qualifier + else + spvRemoved(loc, "packed"); + } + publicType.qualifier.layoutPacking = ElpPacked; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpShared)) { + if (spvVersion.spv != 0) { + if (spvVersion.vulkanRelaxed) + return; // silently ignore qualifier + else + spvRemoved(loc, "shared"); + } + publicType.qualifier.layoutPacking = ElpShared; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpStd140)) { + publicType.qualifier.layoutPacking = ElpStd140; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpStd430)) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "std430"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "std430"); + profileRequires(loc, EEsProfile, 310, nullptr, "std430"); + publicType.qualifier.layoutPacking = ElpStd430; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpScalar)) { + requireVulkan(loc, "scalar"); + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "scalar block layout"); + publicType.qualifier.layoutPacking = ElpScalar; + return; + } + // TODO: compile-time performance: may need to stop doing linear searches + for (TLayoutFormat format = (TLayoutFormat)(ElfNone + 1); format < ElfCount; format = (TLayoutFormat)(format + 1)) { + if (id == TQualifier::getLayoutFormatString(format)) { + if ((format > ElfEsFloatGuard && format < ElfFloatGuard) || + (format > ElfEsIntGuard && format < ElfIntGuard) || + (format > ElfEsUintGuard && format < ElfCount)) + requireProfile(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, "image load-store format"); + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "image load store"); + profileRequires(loc, EEsProfile, 310, E_GL_ARB_shader_image_load_store, "image load store"); + publicType.qualifier.layoutFormat = format; + return; + } + } + if (id == "push_constant") { + requireVulkan(loc, "push_constant"); + publicType.qualifier.layoutPushConstant = true; + return; + } + if (id == "buffer_reference") { + requireVulkan(loc, "buffer_reference"); + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference"); + publicType.qualifier.layoutBufferReference = true; + intermediate.setUseStorageBuffer(); + intermediate.setUsePhysicalStorageBuffer(); + return; + } + if (id == "bindless_sampler") { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "bindless_sampler"); + publicType.qualifier.layoutBindlessSampler = true; + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeLayout); + return; + } + if (id == "bindless_image") { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "bindless_image"); + publicType.qualifier.layoutBindlessImage = true; + intermediate.setBindlessImageMode(currentCaller, AstRefTypeLayout); + return; + } + if (id == "bound_sampler") { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "bound_sampler"); + publicType.qualifier.layoutBindlessSampler = false; + return; + } + if (id == "bound_image") { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "bound_image"); + publicType.qualifier.layoutBindlessImage = false; + return; + } + if (language == EShLangGeometry || language == EShLangTessEvaluation || language == EShLangMesh) { + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (language == EShLangGeometry || language == EShLangMesh) { + if (id == TQualifier::getGeometryString(ElgPoints)) { + publicType.shaderQualifiers.geometry = ElgPoints; + return; + } + if (id == TQualifier::getGeometryString(ElgLines)) { + publicType.shaderQualifiers.geometry = ElgLines; + return; + } + if (language == EShLangGeometry) { + if (id == TQualifier::getGeometryString(ElgLineStrip)) { + publicType.shaderQualifiers.geometry = ElgLineStrip; + return; + } + if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgLinesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { + publicType.shaderQualifiers.geometry = ElgTriangleStrip; + return; + } + if (id == "passthrough") { + requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough"); + publicType.qualifier.layoutPassthrough = true; + intermediate.setGeoPassthroughEXT(); + return; + } + } + } else { + assert(language == EShLangTessEvaluation); + + // input primitive + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (id == TQualifier::getGeometryString(ElgQuads)) { + publicType.shaderQualifiers.geometry = ElgQuads; + return; + } + if (id == TQualifier::getGeometryString(ElgIsolines)) { + publicType.shaderQualifiers.geometry = ElgIsolines; + return; + } + + // vertex spacing + if (id == TQualifier::getVertexSpacingString(EvsEqual)) { + publicType.shaderQualifiers.spacing = EvsEqual; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { + publicType.shaderQualifiers.spacing = EvsFractionalEven; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { + publicType.shaderQualifiers.spacing = EvsFractionalOdd; + return; + } + + // triangle order + if (id == TQualifier::getVertexOrderString(EvoCw)) { + publicType.shaderQualifiers.order = EvoCw; + return; + } + if (id == TQualifier::getVertexOrderString(EvoCcw)) { + publicType.shaderQualifiers.order = EvoCcw; + return; + } + + // point mode + if (id == "point_mode") { + publicType.shaderQualifiers.pointMode = true; + return; + } + } + } + if (language == EShLangFragment) { + if (id == "origin_upper_left") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile | ENoProfile, "origin_upper_left"); + if (profile == ENoProfile) { + profileRequires(loc,ECoreProfile | ECompatibilityProfile, 140, E_GL_ARB_fragment_coord_conventions, "origin_upper_left"); + } + + publicType.shaderQualifiers.originUpperLeft = true; + return; + } + if (id == "pixel_center_integer") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile | ENoProfile, "pixel_center_integer"); + if (profile == ENoProfile) { + profileRequires(loc,ECoreProfile | ECompatibilityProfile, 140, E_GL_ARB_fragment_coord_conventions, "pixel_center_integer"); + } + publicType.shaderQualifiers.pixelCenterInteger = true; + return; + } + if (id == "early_fragment_tests") { + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "early_fragment_tests"); + profileRequires(loc, EEsProfile, 310, nullptr, "early_fragment_tests"); + publicType.shaderQualifiers.earlyFragmentTests = true; + return; + } + if (id == "early_and_late_fragment_tests_amd") { + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_AMD_shader_early_and_late_fragment_tests, "early_and_late_fragment_tests_amd"); + profileRequires(loc, EEsProfile, 310, nullptr, "early_and_late_fragment_tests_amd"); + publicType.shaderQualifiers.earlyAndLateFragmentTestsAMD = true; + return; + } + if (id == "post_depth_coverage") { + requireExtensions(loc, Num_post_depth_coverageEXTs, post_depth_coverageEXTs, "post depth coverage"); + if (extensionTurnedOn(E_GL_ARB_post_depth_coverage)) { + publicType.shaderQualifiers.earlyFragmentTests = true; + } + publicType.shaderQualifiers.postDepthCoverage = true; + return; + } + /* id is transformed into lower case in the beginning of this function. */ + if (id == "non_coherent_color_attachment_readext") { + requireExtensions(loc, 1, &E_GL_EXT_shader_tile_image, "non_coherent_color_attachment_readEXT"); + publicType.shaderQualifiers.nonCoherentColorAttachmentReadEXT = true; + return; + } + if (id == "non_coherent_depth_attachment_readext") { + requireExtensions(loc, 1, &E_GL_EXT_shader_tile_image, "non_coherent_depth_attachment_readEXT"); + publicType.shaderQualifiers.nonCoherentDepthAttachmentReadEXT = true; + return; + } + if (id == "non_coherent_stencil_attachment_readext") { + requireExtensions(loc, 1, &E_GL_EXT_shader_tile_image, "non_coherent_stencil_attachment_readEXT"); + publicType.shaderQualifiers.nonCoherentStencilAttachmentReadEXT = true; + return; + } + for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth+1)) { + if (id == TQualifier::getLayoutDepthString(depth)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "depth layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, nullptr, "depth layout qualifier"); + publicType.shaderQualifiers.layoutDepth = depth; + return; + } + } + for (TLayoutStencil stencil = (TLayoutStencil)(ElsNone + 1); stencil < ElsCount; stencil = (TLayoutStencil)(stencil+1)) { + if (id == TQualifier::getLayoutStencilString(stencil)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "stencil layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, nullptr, "stencil layout qualifier"); + publicType.shaderQualifiers.layoutStencil = stencil; + return; + } + } + for (TInterlockOrdering order = (TInterlockOrdering)(EioNone + 1); order < EioCount; order = (TInterlockOrdering)(order+1)) { + if (id == TQualifier::getInterlockOrderingString(order)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "fragment shader interlock layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 450, nullptr, "fragment shader interlock layout qualifier"); + requireExtensions(loc, 1, &E_GL_ARB_fragment_shader_interlock, TQualifier::getInterlockOrderingString(order)); + if (order == EioShadingRateInterlockOrdered || order == EioShadingRateInterlockUnordered) + requireExtensions(loc, 1, &E_GL_NV_shading_rate_image, TQualifier::getInterlockOrderingString(order)); + publicType.shaderQualifiers.interlockOrdering = order; + return; + } + } + if (id.compare(0, 13, "blend_support") == 0) { + bool found = false; + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (id == TQualifier::getBlendEquationString(be)) { + profileRequires(loc, EEsProfile, 320, E_GL_KHR_blend_equation_advanced, "blend equation"); + profileRequires(loc, ~EEsProfile, 0, E_GL_KHR_blend_equation_advanced, "blend equation"); + intermediate.addBlendEquation(be); + publicType.shaderQualifiers.blendEquation = true; + found = true; + break; + } + } + if (! found) + error(loc, "unknown blend equation", "blend_support", ""); + return; + } + if (id == "override_coverage") { + requireExtensions(loc, 1, &E_GL_NV_sample_mask_override_coverage, "sample mask override coverage"); + publicType.shaderQualifiers.layoutOverrideCoverage = true; + return; + } + if (id == "full_quads") + { + const char* feature = "full_quads qualifier"; + requireProfile(loc, ECompatibilityProfile | ECoreProfile | EEsProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 140, E_GL_EXT_shader_quad_control, feature); + profileRequires(loc, EEsProfile, 310, E_GL_EXT_shader_quad_control, feature); + publicType.qualifier.layoutFullQuads = true; + return; + } + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry ) { + if (id == "viewport_relative") { + requireExtensions(loc, 1, &E_GL_NV_viewport_array2, "view port array2"); + publicType.qualifier.layoutViewportRelative = true; + return; + } + } else { + if (language == EShLangRayGen || language == EShLangIntersect || + language == EShLangAnyHit || language == EShLangClosestHit || + language == EShLangMiss || language == EShLangCallable) { + if (id == "shaderrecordnv" || id == "shaderrecordext") { + if (id == "shaderrecordnv") { + requireExtensions(loc, 1, &E_GL_NV_ray_tracing, "shader record NV"); + } else { + requireExtensions(loc, 1, &E_GL_EXT_ray_tracing, "shader record EXT"); + } + publicType.qualifier.layoutShaderRecord = true; + return; + } else if (id == "hitobjectshaderrecordnv") { + requireExtensions(loc, 1, &E_GL_NV_shader_invocation_reorder, "hitobject shader record NV"); + publicType.qualifier.layoutHitObjectShaderRecordNV = true; + return; + } + + } + } + if (language == EShLangCompute) { + if (id.compare(0, 17, "derivative_group_") == 0) { + requireExtensions(loc, 1, &E_GL_NV_compute_shader_derivatives, "compute shader derivatives"); + if (id == "derivative_group_quadsnv") { + publicType.shaderQualifiers.layoutDerivativeGroupQuads = true; + return; + } else if (id == "derivative_group_linearnv") { + publicType.shaderQualifiers.layoutDerivativeGroupLinear = true; + return; + } + } + } + + if (id == "primitive_culling") { + requireExtensions(loc, 1, &E_GL_EXT_ray_flags_primitive_culling, "primitive culling"); + publicType.shaderQualifiers.layoutPrimitiveCulling = true; + return; + } + + if (id == "quad_derivatives") + { + const char* feature = "quad_derivatives qualifier"; + requireProfile(loc, ECompatibilityProfile | ECoreProfile | EEsProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 140, E_GL_EXT_shader_quad_control, feature); + profileRequires(loc, EEsProfile, 310, E_GL_EXT_shader_quad_control, feature); + publicType.qualifier.layoutQuadDeriv = true; + return; + } + + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type, for qualifiers having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node) +{ + const char* feature = "layout-id value"; + const char* nonLiteralFeature = "non-literal layout-id value"; + + integerCheck(node, feature); + const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); + int value; + bool nonLiteral = false; + if (constUnion) { + value = constUnion->getConstArray()[0].getIConst(); + if (! constUnion->isLiteral()) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, nonLiteralFeature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, nonLiteralFeature); + } + } else { + // grammar should have give out the error message + value = 0; + nonLiteral = true; + } + + if (value < 0) { + error(loc, "cannot be negative", feature, ""); + return; + } + + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == "offset") { + // "offset" can be for either + // - uniform offsets + // - atomic_uint offsets + const char* feature = "offset"; + if (spvVersion.spv == 0) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters }; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + publicType.qualifier.layoutOffset = value; + publicType.qualifier.explicitOffset = true; + if (nonLiteral) + error(loc, "needs a literal integer", "offset", ""); + return; + } else if (id == "align") { + const char* feature = "uniform buffer-member align"; + if (spvVersion.spv == 0) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + } + // "The specified alignment must be a power of 2, or a compile-time error results." + if (! IsPow2(value)) + error(loc, "must be a power of 2", "align", ""); + else + publicType.qualifier.layoutAlign = value; + if (nonLiteral) + error(loc, "needs a literal integer", "align", ""); + return; + } else if (id == "location") { + profileRequires(loc, EEsProfile, 300, nullptr, "location"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + // GL_ARB_explicit_uniform_location requires 330 or GL_ARB_explicit_attrib_location we do not need to add it here + profileRequires(loc, ~EEsProfile, 330, 2, exts, "location"); + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(loc, "location is too large", id.c_str(), ""); + else + publicType.qualifier.layoutLocation = value; + if (nonLiteral) + error(loc, "needs a literal integer", "location", ""); + return; + } else if (id == "set") { + if ((unsigned int)value >= TQualifier::layoutSetEnd) + error(loc, "set is too large", id.c_str(), ""); + else + publicType.qualifier.layoutSet = value; + if (value != 0) + requireVulkan(loc, "descriptor set"); + if (nonLiteral) + error(loc, "needs a literal integer", "set", ""); + return; + } else if (id == "binding") { + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, "binding"); + profileRequires(loc, EEsProfile, 310, nullptr, "binding"); + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + publicType.qualifier.layoutBinding = value; + if (nonLiteral) + error(loc, "needs a literal integer", "binding", ""); + return; + } + if (id == "constant_id") { + requireSpv(loc, "constant_id"); + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", id.c_str(), ""); + } else { + publicType.qualifier.layoutSpecConstantId = value; + publicType.qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", id.c_str(), ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "constant_id", ""); + return; + } + if (id == "component") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "component"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "component"); + if ((unsigned)value >= TQualifier::layoutComponentEnd) + error(loc, "component is too large", id.c_str(), ""); + else + publicType.qualifier.layoutComponent = value; + if (nonLiteral) + error(loc, "needs a literal integer", "component", ""); + return; + } + if (id.compare(0, 4, "xfb_") == 0) { + // "Any shader making any static use (after preprocessing) of any of these + // *xfb_* qualifiers will cause the shader to be in a transform feedback + // capturing mode and hence responsible for describing the transform feedback + // setup." + intermediate.setXfbMode(); + const char* feature = "transform feedback qualifier"; + requireStage(loc, (EShLanguageMask)(EShLangVertexMask | EShLangGeometryMask | EShLangTessControlMask | EShLangTessEvaluationMask), feature); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + if (id == "xfb_buffer") { + // "It is a compile-time error to specify an *xfb_buffer* that is greater than + // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." + if (value >= resources.maxTransformFeedbackBuffers) + error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers); + if (value >= (int)TQualifier::layoutXfbBufferEnd) + error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd-1); + else + publicType.qualifier.layoutXfbBuffer = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_buffer", ""); + return; + } else if (id == "xfb_offset") { + if (value >= (int)TQualifier::layoutXfbOffsetEnd) + error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd-1); + else + publicType.qualifier.layoutXfbOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_offset", ""); + return; + } else if (id == "xfb_stride") { + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) { + error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", + resources.maxTransformFeedbackInterleavedComponents); + } + if (value >= (int)TQualifier::layoutXfbStrideEnd) + error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd-1); + else + publicType.qualifier.layoutXfbStride = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_stride", ""); + return; + } + } + if (id == "input_attachment_index") { + requireVulkan(loc, "input_attachment_index"); + if (value >= (int)TQualifier::layoutAttachmentEnd) + error(loc, "attachment index is too large", id.c_str(), ""); + else + publicType.qualifier.layoutAttachment = value; + if (nonLiteral) + error(loc, "needs a literal integer", "input_attachment_index", ""); + return; + } + if (id == "num_views") { + requireExtensions(loc, Num_OVR_multiview_EXTs, OVR_multiview_EXTs, "num_views"); + publicType.shaderQualifiers.numViews = value; + if (nonLiteral) + error(loc, "needs a literal integer", "num_views", ""); + return; + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) { + if (id == "secondary_view_offset") { + requireExtensions(loc, 1, &E_GL_NV_stereo_view_rendering, "stereo view rendering"); + publicType.qualifier.layoutSecondaryViewportRelativeOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "secondary_view_offset", ""); + return; + } + } + + if (id == "buffer_reference_align") { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align"); + if (! IsPow2(value)) + error(loc, "must be a power of 2", "buffer_reference_align", ""); + else + publicType.qualifier.layoutBufferReferenceAlign = IntLog2(value); + if (nonLiteral) + error(loc, "needs a literal integer", "buffer_reference_align", ""); + return; + } + + switch (language) { + case EShLangTessControl: + if (id == "vertices") { + if (value == 0) + error(loc, "must be greater than 0", "vertices", ""); + else + publicType.shaderQualifiers.vertices = value; + if (nonLiteral) + error(loc, "needs a literal integer", "vertices", ""); + return; + } + break; + + case EShLangGeometry: + if (id == "invocations") { + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, nullptr, "invocations"); + if (value == 0) + error(loc, "must be at least 1", "invocations", ""); + else + publicType.shaderQualifiers.invocations = value; + if (nonLiteral) + error(loc, "needs a literal integer", "invocations", ""); + return; + } + if (id == "max_vertices") { + publicType.shaderQualifiers.vertices = value; + if (value > resources.maxGeometryOutputVertices) + error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "stream") { + requireProfile(loc, ~EEsProfile, "selecting output stream"); + publicType.qualifier.layoutStream = value; + if (value > 0) + intermediate.setMultiStream(); + if (nonLiteral) + error(loc, "needs a literal integer", "stream", ""); + return; + } + break; + + case EShLangFragment: + if (id == "index") { + requireProfile(loc, ECompatibilityProfile | ECoreProfile | EEsProfile, "index layout qualifier on fragment output"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 330, 2, exts, "index layout qualifier on fragment output"); + profileRequires(loc, EEsProfile ,310, E_GL_EXT_blend_func_extended, "index layout qualifier on fragment output"); + // "It is also a compile-time error if a fragment shader sets a layout index to less than 0 or greater than 1." + if (value < 0 || value > 1) { + value = 0; + error(loc, "value must be 0 or 1", "index", ""); + } + + publicType.qualifier.layoutIndex = value; + if (nonLiteral) + error(loc, "needs a literal integer", "index", ""); + return; + } + break; + + case EShLangMesh: + if (id == "max_vertices") { + requireExtensions(loc, Num_AEP_mesh_shader, AEP_mesh_shader, "max_vertices"); + publicType.shaderQualifiers.vertices = value; + int max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? resources.maxMeshOutputVerticesEXT + : resources.maxMeshOutputVerticesNV; + if (value > max) { + TString maxsErrtring = "too large, must be less than "; + maxsErrtring.append(extensionTurnedOn(E_GL_EXT_mesh_shader) ? "gl_MaxMeshOutputVerticesEXT" + : "gl_MaxMeshOutputVerticesNV"); + error(loc, maxsErrtring.c_str(), "max_vertices", ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "max_primitives") { + requireExtensions(loc, Num_AEP_mesh_shader, AEP_mesh_shader, "max_primitives"); + publicType.shaderQualifiers.primitives = value; + int max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? resources.maxMeshOutputPrimitivesEXT + : resources.maxMeshOutputPrimitivesNV; + if (value > max) { + TString maxsErrtring = "too large, must be less than "; + maxsErrtring.append(extensionTurnedOn(E_GL_EXT_mesh_shader) ? "gl_MaxMeshOutputPrimitivesEXT" + : "gl_MaxMeshOutputPrimitivesNV"); + error(loc, maxsErrtring.c_str(), "max_primitives", ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "max_primitives", ""); + return; + } + [[fallthrough]]; + + case EShLangTask: + // Fall through + case EShLangCompute: + if (id.compare(0, 11, "local_size_") == 0) { + if (language == EShLangMesh || language == EShLangTask) { + requireExtensions(loc, Num_AEP_mesh_shader, AEP_mesh_shader, "gl_WorkGroupSize"); + } else { + profileRequires(loc, EEsProfile, 310, nullptr, "gl_WorkGroupSize"); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkGroupSize"); + } + if (nonLiteral) + error(loc, "needs a literal integer", "local_size", ""); + if (id.size() == 12 && value == 0) { + error(loc, "must be at least 1", id.c_str(), ""); + return; + } + if (id == "local_size_x") { + publicType.shaderQualifiers.localSize[0] = value; + publicType.shaderQualifiers.localSizeNotDefault[0] = true; + return; + } + if (id == "local_size_y") { + publicType.shaderQualifiers.localSize[1] = value; + publicType.shaderQualifiers.localSizeNotDefault[1] = true; + return; + } + if (id == "local_size_z") { + publicType.shaderQualifiers.localSize[2] = value; + publicType.shaderQualifiers.localSizeNotDefault[2] = true; + return; + } + if (spvVersion.spv != 0) { + if (id == "local_size_x_id") { + publicType.shaderQualifiers.localSizeSpecId[0] = value; + return; + } + if (id == "local_size_y_id") { + publicType.shaderQualifiers.localSizeSpecId[1] = value; + return; + } + if (id == "local_size_z_id") { + publicType.shaderQualifiers.localSizeSpecId[2] = value; + return; + } + } + } + break; + + default: + break; + } + + error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +// +// "More than one layout qualifier may appear in a single declaration. +// Additionally, the same layout-qualifier-name can occur multiple times +// within a layout qualifier or across multiple layout qualifiers in the +// same declaration. When the same layout-qualifier-name occurs +// multiple times, in a single declaration, the last occurrence overrides +// the former occurrence(s). Further, if such a layout-qualifier-name +// will effect subsequent declarations or other observable behavior, it +// is only the last occurrence that will have any effect, behaving as if +// the earlier occurrence(s) within the declaration are not present. +// This is also true for overriding layout-qualifier-names, where one +// overrides the other (e.g., row_major vs. column_major); only the last +// occurrence has any effect." +void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) +{ + if (src.hasMatrix()) + dst.layoutMatrix = src.layoutMatrix; + if (src.hasPacking()) + dst.layoutPacking = src.layoutPacking; + + if (src.hasStream()) + dst.layoutStream = src.layoutStream; + if (src.hasFormat()) + dst.layoutFormat = src.layoutFormat; + if (src.hasXfbBuffer()) + dst.layoutXfbBuffer = src.layoutXfbBuffer; + if (src.hasBufferReferenceAlign()) + dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign; + + if (src.hasAlign()) + dst.layoutAlign = src.layoutAlign; + + if (! inheritOnly) { + if (src.hasLocation()) + dst.layoutLocation = src.layoutLocation; + if (src.hasOffset()) + dst.layoutOffset = src.layoutOffset; + if (src.hasSet()) + dst.layoutSet = src.layoutSet; + if (src.layoutBinding != TQualifier::layoutBindingEnd) + dst.layoutBinding = src.layoutBinding; + + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; + if (src.hasXfbStride()) + dst.layoutXfbStride = src.layoutXfbStride; + if (src.hasXfbOffset()) + dst.layoutXfbOffset = src.layoutXfbOffset; + if (src.hasAttachment()) + dst.layoutAttachment = src.layoutAttachment; + if (src.layoutPushConstant) + dst.layoutPushConstant = true; + + if (src.layoutBufferReference) + dst.layoutBufferReference = true; + + if (src.layoutPassthrough) + dst.layoutPassthrough = true; + if (src.layoutViewportRelative) + dst.layoutViewportRelative = true; + if (src.layoutSecondaryViewportRelativeOffset != -2048) + dst.layoutSecondaryViewportRelativeOffset = src.layoutSecondaryViewportRelativeOffset; + if (src.layoutShaderRecord) + dst.layoutShaderRecord = true; + if (src.layoutFullQuads) + dst.layoutFullQuads = true; + if (src.layoutQuadDeriv) + dst.layoutQuadDeriv = true; + if (src.layoutBindlessSampler) + dst.layoutBindlessSampler = true; + if (src.layoutBindlessImage) + dst.layoutBindlessImage = true; + if (src.pervertexNV) + dst.pervertexNV = true; + if (src.pervertexEXT) + dst.pervertexEXT = true; + if (src.layoutHitObjectShaderRecordNV) + dst.layoutHitObjectShaderRecordNV = true; + } +} + +// Do error layout error checking given a full variable/block declaration. +void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symbol) +{ + const TType& type = symbol.getType(); + const TQualifier& qualifier = type.getQualifier(); + + // first, cross check WRT to just the type + layoutTypeCheck(loc, type); + + // now, any remaining error checking based on the object itself + + if (qualifier.hasAnyLocation()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (symbol.getAsVariable() == nullptr) + error(loc, "can only be used on variable declaration", "location", ""); + break; + default: + break; + } + } + + // user-variable location check, which are required for SPIR-V in/out: + // - variables have it directly, + // - blocks have it on each member (already enforced), so check first one + if (spvVersion.spv > 0 && !parsingBuiltins && qualifier.builtIn == EbvNone && + !qualifier.hasLocation() && !intermediate.getAutoMapLocations()) { + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + if (!type.getQualifier().isTaskMemory() && !type.getQualifier().hasSpirvDecorate() && + (type.getBasicType() != EbtBlock || + (!(*type.getStruct())[0].type->getQualifier().hasLocation() && + (*type.getStruct())[0].type->getQualifier().builtIn == EbvNone))) + error(loc, "SPIR-V requires location for user input/output", "location", ""); + break; + default: + break; + } + } + + // Check packing and matrix + if (qualifier.hasUniformLayout()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() != EbtBlock) { + if (qualifier.hasMatrix()) + error(loc, "cannot specify matrix layout on a variable declaration", "layout", ""); + if (qualifier.hasPacking()) + error(loc, "cannot specify packing on a variable declaration", "layout", ""); + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset() && !type.isAtomic()) + error(loc, "cannot specify on a variable declaration", "offset", ""); + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasAlign()) + error(loc, "cannot specify on a variable declaration", "align", ""); + if (qualifier.isPushConstant()) + error(loc, "can only specify on a uniform block", "push_constant", ""); + if (qualifier.isShaderRecord()) + error(loc, "can only specify on a buffer block", "shaderRecordNV", ""); + if (qualifier.hasLocation() && type.isAtomic()) + error(loc, "cannot specify on atomic counter", "location", ""); + } + break; + default: + // these were already filtered by layoutTypeCheck() (or its callees) + break; + } + } +} + +// "For some blocks declared as arrays, the location can only be applied at the block level: +// When a block is declared as an array where additional locations are needed for each member +// for each block array element, it is a compile-time error to specify locations on the block +// members. That is, when locations would be under specified by applying them on block members, +// they are not allowed on block members. For arrayed interfaces (those generally having an +// extra level of arrayness due to interface expansion), the outer array is stripped before +// applying this rule." +void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool memberWithLocation, + TArraySizes* arraySizes) +{ + if (memberWithLocation && arraySizes != nullptr) { + if (arraySizes->getNumDims() > (currentBlockQualifier.isArrayedIo(language) ? 1 : 0)) + error(loc, "cannot use in a block array where new locations are needed for each block element", + "location", ""); + } +} + +// Do layout error checking with respect to a type. +void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + // first, intra-layout qualifier-only error checking + layoutQualifierCheck(loc, qualifier); + + // now, error checking combining type and qualifier + + if (qualifier.hasAnyLocation()) { + if (qualifier.hasLocation()) { + if (qualifier.storage == EvqVaryingOut && language == EShLangFragment) { + if (qualifier.layoutLocation >= (unsigned int)resources.maxDrawBuffers) + error(loc, "too large for fragment output", "location", ""); + } + } + if (qualifier.hasComponent()) { + // "It is a compile-time error if this sequence of components gets larger than 3." + if (qualifier.layoutComponent + type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1) > 4) + error(loc, "type overflows the available 4 components", "component", ""); + + // "It is a compile-time error to apply the component qualifier to a matrix, a structure, a block, or an array containing any of these." + if (type.isMatrix() || type.getBasicType() == EbtBlock || type.getBasicType() == EbtStruct) + error(loc, "cannot apply to a matrix, structure, or block", "component", ""); + + // " It is a compile-time error to use component 1 or 3 as the beginning of a double or dvec2." + if (type.getBasicType() == EbtDouble) + if (qualifier.layoutComponent & 1) + error(loc, "doubles cannot start on an odd-numbered component", "component", ""); + } + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + if (type.getBasicType() == EbtBlock) + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "location qualifier on in/out block"); + if (type.getQualifier().isTaskMemory()) + error(loc, "cannot apply to taskNV in/out blocks", "location", ""); + break; + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() == EbtBlock) + error(loc, "cannot apply to uniform or buffer block", "location", ""); + else if (type.getBasicType() == EbtSampler && type.getSampler().isAttachmentEXT()) + error(loc, "only applies to", "location", "%s with storage tileImageEXT", type.getBasicTypeString().c_str()); + break; + case EvqtaskPayloadSharedEXT: + error(loc, "cannot apply to taskPayloadSharedEXT", "location", ""); + break; + case EvqPayload: + case EvqPayloadIn: + case EvqHitAttr: + case EvqCallableData: + case EvqCallableDataIn: + case EvqHitObjectAttrNV: + case EvqSpirvStorageClass: + break; + case EvqTileImageEXT: + break; + default: + error(loc, "can only apply to uniform, buffer, in, or out storage qualifiers", "location", ""); + break; + } + + bool typeCollision; + int repeated = intermediate.addUsedLocation(qualifier, type, typeCollision); + if (repeated >= 0 && ! typeCollision) + error(loc, "overlapping use of location", "location", "%d", repeated); + // When location aliasing, the aliases sharing the location must have the same underlying numerical type and bit width( + // floating - point or integer, 32 - bit versus 64 - bit,etc.) + if (typeCollision && (qualifier.isPipeInput() || qualifier.isPipeOutput() || qualifier.storage == EvqTileImageEXT)) + error(loc, "the aliases sharing the location", "location", "%d must be the same basic type and interpolation qualification", repeated); + } + + if (qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()) { + if (type.isUnsizedArray()) { + error(loc, "unsized array", "xfb_offset", "in buffer %d", qualifier.layoutXfbBuffer); + } else { + int repeated = intermediate.addXfbBufferOffset(type); + if (repeated >= 0) + error(loc, "overlapping offsets at", "xfb_offset", "offset %d in buffer %d", repeated, qualifier.layoutXfbBuffer); + } + + // "The offset must be a multiple of the size of the first component of the first + // qualified variable or block member, or a compile-time error results. Further, if applied to an aggregate + // containing a double or 64-bit integer, the offset must also be a multiple of 8..." + if ((type.containsBasicType(EbtDouble) || type.containsBasicType(EbtInt64) || type.containsBasicType(EbtUint64)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 8)) + error(loc, "type contains double or 64-bit integer; xfb_offset must be a multiple of 8", "xfb_offset", ""); + else if ((type.containsBasicType(EbtBool) || type.containsBasicType(EbtFloat) || + type.containsBasicType(EbtInt) || type.containsBasicType(EbtUint)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4)) + error(loc, "must be a multiple of size of first component", "xfb_offset", ""); + // ..., if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2..." + else if ((type.contains16BitFloat() || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) && + !IsMultipleOfPow2(qualifier.layoutXfbOffset, 2)) + error(loc, "type contains half float or 16-bit integer; xfb_offset must be a multiple of 2", "xfb_offset", ""); + } + if (qualifier.hasXfbStride() && qualifier.hasXfbBuffer()) { + if (! intermediate.setXfbBufferStride(qualifier.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } + + if (qualifier.hasBinding()) { + // Binding checking, from the spec: + // + // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or + // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time + // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as + // an array of size N, all elements of the array from binding through binding + N - 1 must be within this + // range." + // + if (!type.isOpaque() && type.getBasicType() != EbtBlock && type.getBasicType() != EbtSpirvType) + error(loc, "requires block, or sampler/image, or atomic-counter type", "binding", ""); + if (type.getBasicType() == EbtSampler) { + int lastBinding = qualifier.layoutBinding; + if (type.isArray()) { + if (spvVersion.vulkan == 0) { + if (type.isSizedArray()) + lastBinding += (type.getCumulativeArraySize() - 1); + else { + warn(loc, "assuming binding count of one for compile-time checking of binding numbers for unsized array", "[]", ""); + } + } + } + if (spvVersion.vulkan == 0 && lastBinding >= resources.maxCombinedTextureImageUnits) + error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); + } + if (type.isAtomic() && !spvVersion.vulkanRelaxed) { + if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); + return; + } + } + } else if (!intermediate.getAutoMapBindings()) { + // some types require bindings + + // atomic_uint + if (type.isAtomic()) + error(loc, "layout(binding=X) is required", "atomic_uint", ""); + + // SPIR-V + if (spvVersion.spv > 0) { + if (qualifier.isUniformOrBuffer()) { + if (type.getBasicType() == EbtBlock && !qualifier.isPushConstant() && + !qualifier.isShaderRecord() && + !qualifier.hasAttachment() && + !qualifier.hasBufferReference()) + error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", ""); + else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler && !type.getSampler().isAttachmentEXT()) + error(loc, "sampler/texture/image requires layout(binding=X)", "binding", ""); + } + } + } + + // some things can't have arrays of arrays + if (type.isArrayOfArrays()) { + if (spvVersion.vulkan > 0) { + if (type.isOpaque() || (type.getQualifier().isUniformOrBuffer() && type.getBasicType() == EbtBlock)) + warn(loc, "Generating SPIR-V array-of-arrays, but Vulkan only supports single array level for this resource", "[][]", ""); + } + } + + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset()) { + if (type.getBasicType() == EbtBlock) + error(loc, "only applies to block members, not blocks", "offset", ""); + } + + // Image format + if (qualifier.hasFormat()) { + if (! type.isImage() && !intermediate.getBindlessImageMode()) + error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + else { + if (type.getSampler().type == EbtFloat && qualifier.getFormat() > ElfFloatGuard) + error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtInt && (qualifier.getFormat() < ElfFloatGuard || qualifier.getFormat() > ElfIntGuard)) + error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtUint && qualifier.getFormat() < ElfIntGuard) + error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + + if (isEsProfile()) { + // "Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, image variables must + // specify either memory qualifier readonly or the memory qualifier writeonly." + if (! (qualifier.getFormat() == ElfR32f || qualifier.getFormat() == ElfR32i || qualifier.getFormat() == ElfR32ui)) { + if (! qualifier.isReadOnly() && ! qualifier.isWriteOnly()) + error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + } + } + } + } else if (type.isImage() && ! qualifier.isWriteOnly() && !intermediate.getBindlessImageMode()) { + const char *explanation = "image variables not declared 'writeonly' and without a format layout qualifier"; + requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation); + } + + if (qualifier.isPushConstant()) { + if (type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "push_constant", ""); + if (type.isArray()) + error(loc, "Push constants blocks can't be an array", "push_constant", ""); + } + + if (type.getBasicType() == EbtReference) { + if (qualifier.isPipeInput()) + error(loc, "cannot contain any structs with buffer_reference.", "in", + "If you want to interface shader stages with a buffer_reference cast to a uint64 or uvec2 instead."); + if (qualifier.isPipeOutput()) + error(loc, "cannot contain any structs with buffer_reference.", "out", + "If you want to interface shader stages with a buffer_reference cast to a uint64 or uvec2 instead."); + } + + if (qualifier.hasBufferReference() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "buffer_reference", ""); + + if (qualifier.isShaderRecord() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "shaderRecordNV", ""); + + // input attachment + if (type.isSubpass()) { + if (extensionTurnedOn(E_GL_EXT_shader_tile_image)) + error(loc, "cannot be used with GL_EXT_shader_tile_image enabled", type.getSampler().getString().c_str(), + ""); + if (! qualifier.hasAttachment()) + error(loc, "requires an input_attachment_index layout qualifier", "subpass", ""); + } else { + if (qualifier.hasAttachment()) + error(loc, "can only be used with a subpass", "input_attachment_index", ""); + } + + // specialization-constant id + if (qualifier.hasSpecConstantId()) { + if (type.getQualifier().storage != EvqConst) + error(loc, "can only be applied to 'const'-qualified scalar", "constant_id", ""); + if (! type.isScalar()) + error(loc, "can only be applied to a scalar", "constant_id", ""); + switch (type.getBasicType()) + { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + break; + default: + error(loc, "cannot be applied to this type", "constant_id", ""); + break; + } + } +} + +static bool storageCanHaveLayoutInBlock(const enum TStorageQualifier storage) +{ + switch (storage) { + case EvqUniform: + case EvqBuffer: + case EvqShared: + return true; + default: + return false; + } +} + +// Do layout error checking that can be done within a layout qualifier proper, not needing to know +// if there are blocks, atomic counters, variables, etc. +void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqShared && qualifier.hasLayout()) { + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) { + error(loc, "shared block requires at least SPIR-V 1.4", "shared block", ""); + } + profileRequires(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shared_memory_block, "shared block"); + } + + // "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)." + if (qualifier.hasComponent() && ! qualifier.hasLocation()) + error(loc, "must specify 'location' to use 'component'", "component", ""); + + if (qualifier.hasAnyLocation()) { + + // "As with input layout qualifiers, all shaders except compute shaders + // allow *location* layout qualifiers on output variable declarations, + // output block declarations, and output block member declarations." + + switch (qualifier.storage) { + case EvqVaryingIn: + { + const char* feature = "location qualifier on input"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangVertex, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangVertex) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } + case EvqVaryingOut: + { + const char* feature = "location qualifier on output"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangFragment, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangFragment) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } + case EvqUniform: + case EvqBuffer: + { + const char* feature = "location qualifier on uniform or buffer"; + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile | ENoProfile, feature); + profileRequires(loc, ~EEsProfile, 330, E_GL_ARB_explicit_attrib_location, feature); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_explicit_uniform_location, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + break; + } + default: + break; + } + if (qualifier.hasIndex()) { + if (qualifier.storage != EvqVaryingOut) + error(loc, "can only be used on an output", "index", ""); + if (! qualifier.hasLocation()) + error(loc, "can only be used with an explicit location", "index", ""); + } + } + + if (qualifier.hasBinding()) { + if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) + error(loc, "requires uniform or buffer storage qualifier", "binding", ""); + } + if (qualifier.hasStream()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "stream", ""); + } + if (qualifier.hasXfb()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "xfb layout qualifier", ""); + } + if (qualifier.hasUniformLayout()) { + if (!storageCanHaveLayoutInBlock(qualifier.storage) && !qualifier.isTaskMemory()) { + if (qualifier.hasMatrix() || qualifier.hasPacking()) + error(loc, "matrix or packing qualifiers can only be used on a uniform or buffer", "layout", ""); + if (qualifier.hasOffset() || qualifier.hasAlign()) + error(loc, "offset/align can only be used on a uniform or buffer", "layout", ""); + } + } + if (qualifier.isPushConstant()) { + if (qualifier.storage != EvqUniform) + error(loc, "can only be used with a uniform", "push_constant", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with push_constant", "set", ""); + if (qualifier.hasBinding()) + error(loc, "cannot be used with push_constant", "binding", ""); + } + if (qualifier.hasBufferReference()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + } + if (qualifier.isShaderRecord()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with a buffer", "shaderRecordNV", ""); + if (qualifier.hasBinding()) + error(loc, "cannot be used with shaderRecordNV", "binding", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with shaderRecordNV", "set", ""); + + } + + if (qualifier.storage == EvqTileImageEXT) { + if (qualifier.hasSet()) + error(loc, "cannot be used with tileImageEXT", "set", ""); + if (!qualifier.hasLocation()) + error(loc, "can only be used with an explicit location", "tileImageEXT", ""); + } + + if (qualifier.storage == EvqHitAttr && qualifier.hasLayout()) { + error(loc, "cannot apply layout qualifiers to hitAttributeNV variable", "hitAttributeNV", ""); + } +} + +// For places that can't have shader-level layout qualifiers +void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers) +{ + const char* message = "can only apply to a standalone qualifier"; + + if (shaderQualifiers.geometry != ElgNone) + error(loc, message, TQualifier::getGeometryString(shaderQualifiers.geometry), ""); + if (shaderQualifiers.spacing != EvsNone) + error(loc, message, TQualifier::getVertexSpacingString(shaderQualifiers.spacing), ""); + if (shaderQualifiers.order != EvoNone) + error(loc, message, TQualifier::getVertexOrderString(shaderQualifiers.order), ""); + if (shaderQualifiers.pointMode) + error(loc, message, "point_mode", ""); + if (shaderQualifiers.invocations != TQualifier::layoutNotSet) + error(loc, message, "invocations", ""); + for (int i = 0; i < 3; ++i) { + if (shaderQualifiers.localSize[i] > 1) + error(loc, message, "local_size", ""); + if (shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) + error(loc, message, "local_size id", ""); + } + if (shaderQualifiers.vertices != TQualifier::layoutNotSet) { + if (language == EShLangGeometry || language == EShLangMesh) + error(loc, message, "max_vertices", ""); + else if (language == EShLangTessControl) + error(loc, message, "vertices", ""); + else + assert(0); + } + if (shaderQualifiers.earlyFragmentTests) + error(loc, message, "early_fragment_tests", ""); + if (shaderQualifiers.postDepthCoverage) + error(loc, message, "post_depth_coverage", ""); + if (shaderQualifiers.nonCoherentColorAttachmentReadEXT) + error(loc, message, "non_coherent_color_attachment_readEXT", ""); + if (shaderQualifiers.nonCoherentDepthAttachmentReadEXT) + error(loc, message, "non_coherent_depth_attachment_readEXT", ""); + if (shaderQualifiers.nonCoherentStencilAttachmentReadEXT) + error(loc, message, "non_coherent_stencil_attachment_readEXT", ""); + if (shaderQualifiers.primitives != TQualifier::layoutNotSet) { + if (language == EShLangMesh) + error(loc, message, "max_primitives", ""); + else + assert(0); + } + if (shaderQualifiers.hasBlendEquation()) + error(loc, message, "blend equation", ""); + if (shaderQualifiers.numViews != TQualifier::layoutNotSet) + error(loc, message, "num_views", ""); + if (shaderQualifiers.interlockOrdering != EioNone) + error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), ""); + if (shaderQualifiers.layoutPrimitiveCulling) + error(loc, "can only be applied as standalone", "primitive_culling", ""); +} + +// Correct and/or advance an object's offset layout qualifier. +void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) +{ + const TQualifier& qualifier = symbol.getType().getQualifier(); + if (symbol.getType().isAtomic()) { + if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) { + + // Set the offset + int offset; + if (qualifier.hasOffset()) + offset = qualifier.layoutOffset; + else + offset = atomicUintOffsets[qualifier.layoutBinding]; + + if (offset % 4 != 0) + error(loc, "atomic counters offset should align based on 4:", "offset", "%d", offset); + + symbol.getWritableType().getQualifier().layoutOffset = offset; + + // Check for overlap + int numOffsets = 4; + if (symbol.getType().isArray()) { + if (symbol.getType().isSizedArray() && !symbol.getType().getArraySizes()->isInnerUnsized()) + numOffsets *= symbol.getType().getCumulativeArraySize(); + else { + // "It is a compile-time error to declare an unsized array of atomic_uint." + error(loc, "array must be explicitly sized", "atomic_uint", ""); + } + } + int repeated = intermediate.addUsedOffsets(qualifier.layoutBinding, offset, numOffsets); + if (repeated >= 0) + error(loc, "atomic counters sharing the same offset:", "offset", "%d", repeated); + + // Bump the default offset + atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets; + } + } +} + +// +// Look up a function name in the symbol table, and make sure it is a function. +// +// Return the function symbol if found, otherwise nullptr. +// +const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + if (symbolTable.isFunctionNameVariable(call.getName())) { + error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); + return nullptr; + } + + const TFunction* function = nullptr; + + // debugPrintfEXT has var args and is in the symbol table as "debugPrintfEXT()", + // mangled to "debugPrintfEXT(" + if (call.getName() == "debugPrintfEXT") { + TSymbol* symbol = symbolTable.find("debugPrintfEXT(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + // coopMatPerElementNV is variadic. There is some function signature error + // checking in handleCoopMat2FunctionCall. + if (call.getName() == "coopMatPerElementNV") { + TSymbol* symbol = symbolTable.find("coopMatPerElementNV(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + bool explicitTypesEnabled = extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64); + + if (isEsProfile()) + function = (explicitTypesEnabled && version >= 310) + ? findFunctionExplicitTypes(loc, call, builtIn) + : ((extensionTurnedOn(E_GL_EXT_shader_implicit_conversions) && version >= 310) + ? findFunction120(loc, call, builtIn) + : findFunctionExact(loc, call, builtIn)); + else if (version < 120) + function = findFunctionExact(loc, call, builtIn); + else if (version < 400) { + bool needfindFunction400 = extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) || extensionTurnedOn(E_GL_ARB_gpu_shader5); + function = needfindFunction400 ? findFunction400(loc, call, builtIn) : findFunction120(loc, call, builtIn); + } + else if (explicitTypesEnabled) + function = findFunctionExplicitTypes(loc, call, builtIn); + else + function = findFunction400(loc, call, builtIn); + + return function; +} + +// Function finding algorithm for ES and desktop 110. +const TFunction* TParseContext::findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol == nullptr) { + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return nullptr; + } + + return symbol->getAsFunction(); +} + +// Function finding algorithm for desktop versions 120 through 330. +const TFunction* TParseContext::findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // exact match not found, look through a list of overloaded functions of the same name + + // "If no exact match is found, then [implicit conversions] will be applied to find a match. Mismatched types + // on input parameters (in or inout or default) must have a conversion from the calling argument type to the + // formal parameter type. Mismatched types on output parameters (out or inout) must have a conversion + // from the formal parameter type to the calling argument type. When argument conversions are used to find + // a match, it is a semantic error if there are multiple ways to apply these conversions to make the call match + // more than one function." + + const TFunction* candidate = nullptr; + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& function = *(*it); + + // to even be a potential match, number of arguments has to match + if (call.getParamCount() != function.getParamCount()) + continue; + + bool possibleMatch = true; + for (int i = 0; i < function.getParamCount(); ++i) { + // same types is easy + if (*function[i].type == *call[i].type) + continue; + + // We have a mismatch in type, see if it is implicitly convertible + + if (function[i].type->isArray() || call[i].type->isArray() || + ! function[i].type->sameElementShape(*call[i].type)) + possibleMatch = false; + else { + // do direction-specific checks for conversion of basic type + if (function[i].type->getQualifier().isParamInput()) { + if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType())) + possibleMatch = false; + } + if (function[i].type->getQualifier().isParamOutput()) { + if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType())) + possibleMatch = false; + } + } + if (! possibleMatch) + break; + } + if (possibleMatch) { + if (candidate) { + // our second match, meaning ambiguity + error(loc, "ambiguous function signature match: multiple signatures match under implicit type conversion", call.getName().c_str(), ""); + } else + candidate = &function; + } + } + + if (candidate == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return candidate; +} + +// Function finding algorithm for desktop version 400 and above. +// +// "When function calls are resolved, an exact type match for all the arguments +// is sought. If an exact match is found, all other functions are ignored, and +// the exact match is used. If no exact match is found, then the implicit +// conversions in section 4.1.10 Implicit Conversions will be applied to find +// a match. Mismatched types on input parameters (in or inout or default) must +// have a conversion from the calling argument type to the formal parameter type. +// Mismatched types on output parameters (out or inout) must have a conversion +// from the formal parameter type to the calling argument type. +// +// "If implicit conversions can be used to find more than one matching function, +// a single best-matching function is sought. To determine a best match, the +// conversions between calling argument and formal parameter types are compared +// for each function argument and pair of matching functions. After these +// comparisons are performed, each pair of matching functions are compared. +// A function declaration A is considered a better match than function +// declaration B if +// +// * for at least one function argument, the conversion for that argument in A +// is better than the corresponding conversion in B; and +// * there is no function argument for which the conversion in B is better than +// the corresponding conversion in A. +// +// "If a single function declaration is considered a better match than every +// other matching function declaration, it will be used. Otherwise, a +// compile-time semantic error for an ambiguous overloaded function call occurs. +// +// "To determine whether the conversion for a single argument in one match is +// better than that for another match, the following rules are applied, in order: +// +// 1. An exact match is better than a match involving any implicit conversion. +// 2. A match involving an implicit conversion from float to double is better +// than a match involving any other implicit conversion. +// 3. A match involving an implicit conversion from either int or uint to float +// is better than a match involving an implicit conversion from either int +// or uint to double. +// +// "If none of the rules above apply to a particular pair of conversions, neither +// conversion is considered better than the other." +// +const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator op, int param) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + if (from.tensorParameterOK(to)) + return true; + if (from.getBasicType() == EbtFunction && to.getBasicType() == EbtFunction) + return true; + if (from.coopVecParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + // Load/store tensor functions allow any element type for the pointer + if ((op == EOpCooperativeMatrixLoadTensorNV || op == EOpCooperativeMatrixStoreTensorNV) && + param == 1 && + (from.getQualifier().storage == EvqBuffer || from.getQualifier().storage == EvqShared)) { + return true; + } + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + if (from.isCoopVecNV() && to.isCoopVecNV()) + return from.sameCoopVecBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. float -> double is better + if (from.getBasicType() == EbtFloat) { + if (to2.getBasicType() == EbtDouble && to1.getBasicType() != EbtDouble) + return true; + } + + // 3. -> float is better than -> double + return to2.getBasicType() == EbtFloat && to1.getBasicType() == EbtDouble; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// "To determine whether the conversion for a single argument in one match +// is better than that for another match, the conversion is assigned of the +// three ranks ordered from best to worst: +// 1. Exact match: no conversion. +// 2. Promotion: integral or floating-point promotion. +// 3. Conversion: integral conversion, floating-point conversion, +// floating-integral conversion. +// A conversion C1 is better than a conversion C2 if the rank of C1 is +// better than the rank of C2." +const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator op, int param) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + if (from.tensorParameterOK(to)) + return true; + if (from.getBasicType() == EbtFunction && to.getBasicType() == EbtFunction) + return true; + if (from.coopVecParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + // Load/store tensor functions allow any element type for the pointer + if ((op == EOpCooperativeMatrixLoadTensorNV || op == EOpCooperativeMatrixStoreTensorNV) && + param == 1 && + (from.getQualifier().storage == EvqBuffer || from.getQualifier().storage == EvqShared)) { + return true; + } + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + if (from.isCoopVecNV() && to.isCoopVecNV()) + return from.sameCoopVecBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [this](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. Promotion (integral, floating-point) is better + TBasicType from_type = from.getBasicType(); + TBasicType to1_type = to1.getBasicType(); + TBasicType to2_type = to2.getBasicType(); + bool isPromotion1 = (intermediate.isIntegralPromotion(from_type, to1_type) || + intermediate.isFPPromotion(from_type, to1_type)); + bool isPromotion2 = (intermediate.isIntegralPromotion(from_type, to2_type) || + intermediate.isFPPromotion(from_type, to2_type)); + if (isPromotion2) + return !isPromotion1; + if(isPromotion1) + return false; + + // 3. Conversion (integral, floating-point , floating-integral) + bool isConversion1 = (intermediate.isIntegralConversion(from_type, to1_type) || + intermediate.isFPConversion(from_type, to1_type) || + intermediate.isFPIntegralConversion(from_type, to1_type)); + bool isConversion2 = (intermediate.isIntegralConversion(from_type, to2_type) || + intermediate.isFPConversion(from_type, to2_type) || + intermediate.isFPIntegralConversion(from_type, to2_type)); + + return isConversion2 && !isConversion1; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// +// Adjust function calls that aren't declared in Vulkan to a +// calls with equivalent effects +// +TIntermTyped* TParseContext::vkRelaxedRemapFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) +{ + TIntermTyped* result = nullptr; + + if (function->getBuiltInOp() != EOpNull) { + return nullptr; + } + + if (function->getName() == "atomicCounterIncrement") { + // change atomicCounterIncrement into an atomicAdd of 1 + TString name("atomicAdd"); + TType uintType(EbtUint); + + TFunction realFunc(&name, function->getType()); + + // Use copyParam to avoid shared ownership of the 'type' field + // of the parameter. + for (int i = 0; i < function->getParamCount(); ++i) { + realFunc.addParameter(TParameter().copyParam((*function)[i])); + } + + TParameter tmpP = { nullptr, &uintType, {} }; + realFunc.addParameter(TParameter().copyParam(tmpP)); + arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(1, loc, true)); + + result = handleFunctionCall(loc, &realFunc, arguments); + } else if (function->getName() == "atomicCounterDecrement") { + // change atomicCounterDecrement into an atomicAdd with -1 + // and subtract 1 from result, to return post-decrement value + TString name("atomicAdd"); + TType uintType(EbtUint); + + TFunction realFunc(&name, function->getType()); + + for (int i = 0; i < function->getParamCount(); ++i) { + realFunc.addParameter(TParameter().copyParam((*function)[i])); + } + + TParameter tmpP = { nullptr, &uintType, {} }; + realFunc.addParameter(TParameter().copyParam(tmpP)); + arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(-1, loc, true)); + + result = handleFunctionCall(loc, &realFunc, arguments); + + // post decrement, so that it matches AtomicCounterDecrement semantics + if (result) { + result = handleBinaryMath(loc, "-", EOpSub, result, intermediate.addConstantUnion(1, loc, true)); + } + } else if (function->getName() == "atomicCounter") { + // change atomicCounter into a direct read of the variable + if (arguments && arguments->getAsTyped()) { + result = arguments->getAsTyped(); + } + } + + return result; +} + +// When a declaration includes a type, but not a variable name, it can be used +// to establish defaults. +void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ + if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding()) { + if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large", "binding", ""); + return; + } + if (publicType.qualifier.hasOffset()) + atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + return; + } + + if (publicType.arraySizes) { + error(loc, "expect an array name", "", ""); + } + + if (publicType.qualifier.hasLayout() && !publicType.qualifier.hasBufferReference()) + warn(loc, "useless application of layout qualifier", "layout", ""); +} + +void TParseContext::typeParametersCheck(const TSourceLoc& loc, const TPublicType& publicType) +{ + if (parsingBuiltins) + return; + if (publicType.isCoopmatKHR()) { + if (publicType.typeParameters == nullptr) { + error(loc, "coopmat missing type parameters", "", ""); + return; + } + switch (publicType.typeParameters->basicType) { + case EbtFloat: + case EbtFloat16: + case EbtBFloat16: + case EbtInt: + case EbtInt8: + case EbtInt16: + case EbtUint: + case EbtUint8: + case EbtUint16: + case EbtSpirvType: + break; + default: + error(loc, "coopmat invalid basic type", TType::getBasicString(publicType.typeParameters->basicType), ""); + break; + } + if (publicType.typeParameters->arraySizes->getNumDims() != 4) { + error(loc, "coopmat incorrect number of type parameters", "", ""); + return; + } + int use = publicType.typeParameters->arraySizes->getDimSize(3); + if (use < 0 || use > 2) { + error(loc, "coopmat invalid matrix Use", "", ""); + return; + } + } + if (publicType.isTensorLayoutNV()) { + if (publicType.typeParameters == nullptr) { + error(loc, "tensorLayout missing type parameters", "", ""); + return; + } + if (publicType.typeParameters->arraySizes->getNumDims() > 2) { + error(loc, "tensorLayout incorrect number of type parameters", "", ""); + return; + } + if (publicType.typeParameters && publicType.typeParameters->arraySizes->getNumDims() < 2) { + while (publicType.typeParameters->arraySizes->getNumDims() < 2) { + publicType.typeParameters->arraySizes->addInnerSize(0); + } + } + } + if (publicType.isTensorViewNV()) { + if (publicType.typeParameters == nullptr) { + error(loc, "tensorView missing type parameters", "", ""); + return; + } + if (publicType.typeParameters->arraySizes->getNumDims() < 1 || + publicType.typeParameters->arraySizes->getNumDims() > 7) { + error(loc, "tensorView incorrect number of type parameters", "", ""); + return; + } + if (publicType.typeParameters && publicType.typeParameters->arraySizes->getNumDims() < 7) { + uint32_t numDims = publicType.typeParameters->arraySizes->getNumDims(); + while (numDims < 7) { + uint32_t dim = (numDims == 1) ? 0 : (numDims - 2); + publicType.typeParameters->arraySizes->addInnerSize(dim); + numDims++; + } + } + } +} + +bool TParseContext::vkRelaxedRemapUniformVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, + TArraySizes*, TIntermTyped* initializer, TType& type) +{ + vkRelaxedRemapUniformMembers(loc, publicType, type, identifier); + + if (parsingBuiltins || symbolTable.atBuiltInLevel() || !symbolTable.atGlobalLevel() || + type.getQualifier().storage != EvqUniform || + !(type.containsNonOpaque() || type.getBasicType() == EbtAtomicUint || (type.containsSampler() && type.isStruct()))) { + return false; + } + + if (type.getQualifier().hasLocation()) { + warn(loc, "ignoring layout qualifier for uniform", identifier.c_str(), "location"); + type.getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + } + + if (initializer) { + warn(loc, "Ignoring initializer for uniform", identifier.c_str(), ""); + initializer = nullptr; + } + + if (type.isArray()) { + // do array size checks here + arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false); + + if (arrayQualifierError(loc, type.getQualifier()) || arrayError(loc, type)) { + error(loc, "array param error", identifier.c_str(), ""); + } + } + + // do some checking on the type as it was declared + layoutTypeCheck(loc, type); + + int bufferBinding = TQualifier::layoutBindingEnd; + TVariable* updatedBlock = nullptr; + + // Convert atomic_uint into members of a buffer block + if (type.isAtomic()) { + type.setBasicType(EbtUint); + type.getQualifier().storage = EvqBuffer; + + type.getQualifier().volatil = true; + type.getQualifier().coherent = true; + + // xxTODO: use logic from fixOffset() to apply explicit member offset + bufferBinding = type.getQualifier().layoutBinding; + type.getQualifier().layoutBinding = TQualifier::layoutBindingEnd; + type.getQualifier().explicitOffset = false; + growAtomicCounterBlock(bufferBinding, loc, type, identifier, nullptr); + updatedBlock = atomicCounterBuffers[bufferBinding]; + } + + if (!updatedBlock) { + growGlobalUniformBlock(loc, type, identifier, nullptr); + updatedBlock = globalUniformBlock; + } + + // + // don't assign explicit member offsets here + // if any are assigned, need to be updated here and in the merge/link step + // fixBlockUniformOffsets(updatedBlock->getWritableType().getQualifier(), *updatedBlock->getWritableType().getWritableStruct()); + + // checks on update buffer object + layoutObjectCheck(loc, *updatedBlock); + + TSymbol* symbol = symbolTable.find(identifier); + + if (!symbol) { + if (updatedBlock == globalUniformBlock) + error(loc, "error adding uniform to default uniform block", identifier.c_str(), ""); + else + error(loc, "error adding atomic counter to atomic counter block", identifier.c_str(), ""); + return false; + } + + // merge qualifiers + mergeObjectLayoutQualifiers(updatedBlock->getWritableType().getQualifier(), type.getQualifier(), true); + + return true; +} + +template +static void ForEachOpaque(const TType& type, const TString& path, Function callback) +{ + auto recursion = [&callback](const TType& type, const TString& path, bool skipArray, auto& recursion) -> void { + if (!skipArray && type.isArray()) + { + std::vector indices(type.getArraySizes()->getNumDims()); + for (int flatIndex = 0; + flatIndex < type.getArraySizes()->getCumulativeSize(); + ++flatIndex) + { + TString subscriptPath = path; + for (size_t dimIndex = 0; dimIndex < indices.size(); ++dimIndex) + { + int index = indices[dimIndex]; + subscriptPath.append("["); + subscriptPath.append(String(index)); + subscriptPath.append("]"); + } + + recursion(type, subscriptPath, true, recursion); + + for (size_t dimIndex = 0; dimIndex < indices.size(); ++dimIndex) + { + ++indices[dimIndex]; + if (indices[dimIndex] < type.getArraySizes()->getDimSize(dimIndex)) + break; + else + indices[dimIndex] = 0; + } + } + } + + else if (type.isStruct() && type.containsOpaque()) + { + const TTypeList& types = *type.getStruct(); + for (const TTypeLoc& typeLoc : types) + { + TString nextPath = path; + nextPath.append("."); + nextPath.append(typeLoc.type->getFieldName()); + + recursion(*(typeLoc.type), nextPath, false, recursion); + } + } + + else if (type.isOpaque()) + { + callback(type, path); + } + }; + + recursion(type, path, false, recursion); +} + +void TParseContext::vkRelaxedRemapUniformMembers(const TSourceLoc& loc, const TPublicType& publicType, const TType& type, + const TString& identifier) +{ + if (!type.isStruct() || !type.containsOpaque()) + return; + + ForEachOpaque(type, identifier, + [&publicType, &loc, this](const TType& type, const TString& path) { + TArraySizes arraySizes = {}; + if (type.getArraySizes()) arraySizes = *type.getArraySizes(); + TTypeParameters typeParameters = {}; + if (type.getTypeParameters()) typeParameters = *type.getTypeParameters(); + + TPublicType memberType{}; + memberType.basicType = type.getBasicType(); + memberType.sampler = type.getSampler(); + memberType.qualifier = type.getQualifier(); + memberType.vectorSize = type.getVectorSize(); + memberType.matrixCols = type.getMatrixCols(); + memberType.matrixRows = type.getMatrixRows(); + memberType.coopmatNV = type.isCoopMatNV(); + memberType.coopmatKHR = type.isCoopMatKHR(); + memberType.arraySizes = nullptr; + memberType.userDef = nullptr; + memberType.loc = loc; + memberType.typeParameters = (type.getTypeParameters() ? &typeParameters : nullptr); + memberType.spirvType = nullptr; + + memberType.qualifier.storage = publicType.qualifier.storage; + memberType.shaderQualifiers = publicType.shaderQualifiers; + + TString& structMemberName = *NewPoolTString(path.c_str()); // A copy is required due to declareVariable() signature. + declareVariable(loc, structMemberName, memberType, nullptr, nullptr); + }); +} + +void TParseContext::vkRelaxedRemapFunctionParameter(TFunction* function, TParameter& param, std::vector* newParams) +{ + function->addParameter(param); + + if (!param.type->isStruct() || !param.type->containsOpaque()) + return; + + ForEachOpaque(*param.type, (param.name ? *param.name : param.type->getFieldName()), + [function, param, newParams](const TType& type, const TString& path) { + TString* memberName = NewPoolTString(path.c_str()); + + TType* memberType = new TType(); + memberType->shallowCopy(type); + memberType->getQualifier().storage = param.type->getQualifier().storage; + memberType->clearArraySizes(); + + TParameter memberParam = {}; + memberParam.name = memberName; + memberParam.type = memberType; + memberParam.defaultValue = nullptr; + function->addParameter(memberParam); + if (newParams) + newParams->push_back(function->getParamCount()-1); + }); +} + +// +// Generates a valid GLSL dereferencing string for the input TIntermNode +// +struct AccessChainTraverser : public TIntermTraverser { + AccessChainTraverser() : TIntermTraverser(false, false, true) + {} + + TString path = ""; + TStorageQualifier topLevelStorageQualifier = TStorageQualifier::EvqLast; + + bool visitBinary(TVisit, TIntermBinary* binary) override { + if (binary->getOp() == EOpIndexDirectStruct) + { + const TTypeList& members = *binary->getLeft()->getType().getStruct(); + const TTypeLoc& member = + members[binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()]; + TString memberName = member.type->getFieldName(); + + if (path != "") + path.append("."); + + path.append(memberName); + } + + if (binary->getOp() == EOpIndexDirect) + { + const TConstUnionArray& indices = binary->getRight()->getAsConstantUnion()->getConstArray(); + for (int index = 0; index < indices.size(); ++index) + { + path.append("["); + path.append(String(indices[index].getIConst())); + path.append("]"); + } + } + + return true; + } + + void visitSymbol(TIntermSymbol* symbol) override { + if (symbol->getType().isOpaque()) + topLevelStorageQualifier = symbol->getQualifier().storage; + if (!IsAnonymous(symbol->getName())) + path.append(symbol->getName()); + } +}; + +TIntermNode* TParseContext::vkRelaxedRemapFunctionArgument(const TSourceLoc& loc, TFunction* function, TIntermTyped* intermTyped) +{ + AccessChainTraverser accessChainTraverser{}; + intermTyped->traverse(&accessChainTraverser); + + if (accessChainTraverser.topLevelStorageQualifier == TStorageQualifier::EvqUniform) + { + TParameter param = { 0, new TType, {} }; + param.type->shallowCopy(intermTyped->getType()); + + function->addParameter(param); + return intermTyped; + } + + TParameter param = { NewPoolTString(accessChainTraverser.path.c_str()), new TType, {} }; + param.type->shallowCopy(intermTyped->getType()); + + std::vector newParams = {}; + vkRelaxedRemapFunctionParameter(function, param, &newParams); + + if (intermTyped->getType().isOpaque()) + { + TIntermNode* remappedArgument = intermTyped; + { + TIntermSymbol* intermSymbol = nullptr; + TSymbol* symbol = symbolTable.find(*param.name); + if (symbol && symbol->getAsVariable()) + intermSymbol = intermediate.addSymbol(*symbol->getAsVariable(), loc); + else + { + TVariable* variable = new TVariable(param.name, *param.type); + intermSymbol = intermediate.addSymbol(*variable, loc); + } + + remappedArgument = intermSymbol; + } + + return remappedArgument; + } + else if (!(intermTyped->isStruct() && intermTyped->getType().containsOpaque())) + return intermTyped; + else + { + TIntermNode* remappedArgument = intermTyped; + { + TSymbol* symbol = symbolTable.find(*param.name); + if (symbol && symbol->getAsVariable()) + remappedArgument = intermediate.addSymbol(*symbol->getAsVariable(), loc); + } + + if (!newParams.empty()) + remappedArgument = intermediate.makeAggregate(remappedArgument, loc); + + for (int paramIndex : newParams) + { + TParameter& newParam = function->operator[](paramIndex); + TIntermSymbol* intermSymbol = nullptr; + TSymbol* symbol = symbolTable.find(*newParam.name); + if (symbol && symbol->getAsVariable()) + intermSymbol = intermediate.addSymbol(*symbol->getAsVariable(), loc); + else + { + TVariable* variable = new TVariable(newParam.name, *newParam.type); + intermSymbol = intermediate.addSymbol(*variable, loc); + } + + remappedArgument = intermediate.growAggregate(remappedArgument, intermSymbol); + } + + return remappedArgument; + } +} + +TIntermTyped* TParseContext::vkRelaxedRemapDotDereference(const TSourceLoc&, TIntermTyped& base, const TType& member, + const TString& identifier) +{ + if (!member.isOpaque()) + return &base; + + AccessChainTraverser traverser{}; + base.traverse(&traverser); + if (!traverser.path.empty()) + traverser.path.append("."); + traverser.path.append(identifier); + + const TSymbol* symbol = symbolTable.find(traverser.path); + if (!symbol) + return &base; + + TIntermTyped* result = intermediate.addSymbol(*symbol->getAsVariable()); + result->setType(symbol->getType()); + return result; +} + +// +// Do everything necessary to handle a variable (non-block) declaration. +// Either redeclaring a variable, or making a new one, updating the symbol +// table, and all error checking. +// +// Returns a subtree node that computes an initializer, if needed. +// Returns nullptr if there is no code to execute for initialization. +// +// 'publicType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, + TArraySizes* arraySizes, TIntermTyped* initializer) +{ + // Make a fresh type that combines the characteristics from the individual + // identifier syntax and the declaration-type syntax. + TType type(publicType); + type.transferArraySizes(arraySizes); + type.copyArrayInnerSizes(publicType.arraySizes); + arrayOfArrayVersionCheck(loc, type.getArraySizes()); + + if (initializer) { + if (type.getBasicType() == EbtRayQuery) { + error(loc, "ray queries can only be initialized by using the rayQueryInitializeEXT intrinsic:", "=", identifier.c_str()); + } else if (type.getBasicType() == EbtHitObjectNV) { + error(loc, "hit objects cannot be initialized using initializers", "=", identifier.c_str()); + } + + } + + if (type.isCoopMatKHR()) { + intermediate.setUseVulkanMemoryModel(); + intermediate.setUseStorageBuffer(); + + if (!publicType.typeParameters || !publicType.typeParameters->arraySizes || + publicType.typeParameters->arraySizes->getNumDims() != 4) { + error(loc, "unexpected number type parameters", identifier.c_str(), ""); + } + if (publicType.typeParameters) { + if (!isTypeFloat(publicType.typeParameters->basicType) && + !isTypeInt(publicType.typeParameters->basicType) && publicType.typeParameters->basicType != EbtSpirvType) { + error(loc, "expected 8, 16, 32, or 64 bit signed or unsigned integer or 16, 32, or 64 bit float type", identifier.c_str(), ""); + } + } + } + else if (type.isCoopMatNV()) { + intermediate.setUseVulkanMemoryModel(); + intermediate.setUseStorageBuffer(); + + if (!publicType.typeParameters || !publicType.typeParameters->arraySizes || publicType.typeParameters->arraySizes->getNumDims() != 4) { + error(loc, "expected four type parameters", identifier.c_str(), ""); + } else { + if (isTypeFloat(publicType.basicType) && + publicType.typeParameters->arraySizes->getDimSize(0) != 16 && + publicType.typeParameters->arraySizes->getDimSize(0) != 32 && + publicType.typeParameters->arraySizes->getDimSize(0) != 64) { + error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), ""); + } + if (isTypeInt(publicType.basicType) && + publicType.typeParameters->arraySizes->getDimSize(0) != 8 && + publicType.typeParameters->arraySizes->getDimSize(0) != 16 && + publicType.typeParameters->arraySizes->getDimSize(0) != 32) { + error(loc, "expected 8, 16, or 32 bits for first type parameter", identifier.c_str(), ""); + } + } + } else if (type.isTensorLayoutNV()) { + if (!publicType.typeParameters || publicType.typeParameters->arraySizes->getNumDims() > 2) { + error(loc, "expected 1-2 type parameters", identifier.c_str(), ""); + } + } else if (type.isTensorViewNV()) { + if (!publicType.typeParameters || publicType.typeParameters->arraySizes->getNumDims() > 7) { + error(loc, "expected 1-7 type parameters", identifier.c_str(), ""); + } + } else if (type.isCoopVecNV()) { + intermediate.setUseVulkanMemoryModel(); + intermediate.setUseStorageBuffer(); + + if (!publicType.typeParameters || !publicType.typeParameters->arraySizes || publicType.typeParameters->arraySizes->getNumDims() != 1) { + error(loc, "expected two type parameters", identifier.c_str(), ""); + } else if (publicType.typeParameters->arraySizes->getDimSize(0) <= 0) { + error(loc, "expected positive number of components", identifier.c_str(), ""); + } + } else { + if (publicType.typeParameters && publicType.typeParameters->arraySizes->getNumDims() != 0) { + error(loc, "unexpected type parameters", identifier.c_str(), ""); + } + } + + if (voidErrorCheck(loc, identifier, type.getBasicType())) + return nullptr; + + if (initializer) + rValueErrorCheck(loc, "initializer", initializer); + else + nonInitConstCheck(loc, identifier, type); + + samplerCheck(loc, type, identifier, initializer); + transparentOpaqueCheck(loc, type, identifier); + atomicUintCheck(loc, type, identifier); + accStructCheck(loc, type, identifier); + hitObjectNVCheck(loc, type, identifier); + checkAndResizeMeshViewDim(loc, type, /*isBlockMember*/ false); + if (type.getQualifier().storage == EvqConst && type.containsReference()) { + error(loc, "variables with reference type can't have qualifier 'const'", "qualifier", ""); + } + + if (type.getQualifier().storage != EvqUniform && type.getQualifier().storage != EvqBuffer) { + if (type.contains16BitFloat()) + requireFloat16Arithmetic(loc, "qualifier", "float16 types can only be in uniform block or buffer storage"); + if (type.contains16BitInt()) + requireInt16Arithmetic(loc, "qualifier", "(u)int16 types can only be in uniform block or buffer storage"); + if (type.contains8BitInt()) + requireInt8Arithmetic(loc, "qualifier", "(u)int8 types can only be in uniform block or buffer storage"); + } + if (type.getBasicType() == EbtBFloat16 && + (type.getQualifier().storage == EvqVaryingIn || type.getQualifier().storage == EvqVaryingOut)) + error(loc, "qualifier", "bfloat16 types not allowed as input/output", ""); + + if (type.getQualifier().storage == EvqtaskPayloadSharedEXT) + intermediate.addTaskPayloadEXTCount(); + if (type.getQualifier().storage == EvqShared && type.containsCoopMat()) + error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", ""); + + if (profile == EEsProfile) { + if (type.getQualifier().isPipeInput() && type.getBasicType() == EbtStruct) { + if (type.getQualifier().isArrayedIo(language)) { + TType perVertexType(type, 0); + if (perVertexType.containsArray() && perVertexType.containsBuiltIn() == false) { + error(loc, "A per vertex structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + } + else if (type.containsArray() && type.containsBuiltIn() == false) { + error(loc, "A structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + if (type.containsStructure()) + error(loc, "A structure containing an struct is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + } + + if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger)) + error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", ""); + if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.getDepth() != EldNone) + error(loc, "can only apply depth layout to gl_FragDepth", "layout qualifier", ""); + if (identifier != "gl_FragStencilRefARB" && publicType.shaderQualifiers.getStencil() != ElsNone) + error(loc, "can only apply depth layout to gl_FragStencilRefARB", "layout qualifier", ""); + + // Check for redeclaration of built-ins and/or attempting to declare a reserved name + TSymbol* symbol = redeclareBuiltinVariable(loc, identifier, type.getQualifier(), publicType.shaderQualifiers); + if (symbol == nullptr) + reservedErrorCheck(loc, identifier); + + if (symbol == nullptr && spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + bool remapped = vkRelaxedRemapUniformVariable(loc, identifier, publicType, arraySizes, initializer, type); + + if (remapped) { + return nullptr; + } + } + + inheritGlobalDefaults(type.getQualifier()); + + // Declare the variable + if (type.isArray()) { + // Check that implicit sizing is only where allowed. + arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false); + + if (! arrayQualifierError(loc, type.getQualifier()) && ! arrayError(loc, type)) + declareArray(loc, identifier, type, symbol); + + if (initializer) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "initializer"); + profileRequires(loc, EEsProfile, 300, nullptr, "initializer"); + } + } else { + // non-array case + if (symbol == nullptr) + symbol = declareNonArray(loc, identifier, type); + else if (type != symbol->getType()) + error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); + } + + if (symbol == nullptr) + return nullptr; + + // Deal with initializer + TIntermNode* initNode = nullptr; + if (symbol != nullptr && initializer) { + TVariable* variable = symbol->getAsVariable(); + if (! variable) { + error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); + return nullptr; + } + initNode = executeInitializer(loc, initializer, variable); + } + + // look for errors in layout qualifier use + layoutObjectCheck(loc, *symbol); + + // fix up + fixOffset(loc, *symbol); + + return initNode; +} + +// Pick up global defaults from the provide global defaults into dst. +void TParseContext::inheritGlobalDefaults(TQualifier& dst) const +{ + if (dst.storage == EvqVaryingOut) { + if (! dst.hasStream() && language == EShLangGeometry) + dst.layoutStream = globalOutputDefaults.layoutStream; + if (! dst.hasXfbBuffer()) + dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +} + +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = NewPoolTString(name); + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + +// +// Declare a non-array variable, the main point being there is no redeclaration +// for resizing allowed. +// +// Return the successfully declared variable. +// +TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type) +{ + // make a new variable + TVariable* variable = new TVariable(&identifier, type); + + ioArrayCheck(loc, type, identifier); + + // add variable to symbol table + if (symbolTable.insert(*variable)) { + if (symbolTable.atGlobalLevel()) + trackLinkage(*variable); + return variable; + } + + error(loc, "redefinition", variable->getName().c_str(), ""); + return nullptr; +} + +// +// Handle all types of initializers from the grammar. +// +// Returning nullptr just means there is no code to execute to handle the +// initializer, which will, for example, be the case for constant initializers. +// +TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) +{ + // A null initializer is an aggregate that hasn't had an op assigned yet + // (still EOpNull, no relation to nullInit), and has no children. + bool nullInit = initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull && + initializer->getAsAggregate()->getSequence().size() == 0; + + // + // Identifier must be of type constant, a global, or a temporary, and + // starting at version 120, desktop allows uniforms to have initializers. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + if (! (qualifier == EvqTemporary || qualifier == EvqGlobal || qualifier == EvqConst || + (qualifier == EvqUniform && !isEsProfile() && version >= 120))) { + if (qualifier == EvqShared) { + // GL_EXT_null_initializer allows this for shared, if it's a null initializer + if (nullInit) { + const char* feature = "initialization with shared qualifier"; + profileRequires(loc, EEsProfile, 0, E_GL_EXT_null_initializer, feature); + profileRequires(loc, ~EEsProfile, 0, E_GL_EXT_null_initializer, feature); + } else { + error(loc, "initializer can only be a null initializer ('{}')", "shared", ""); + } + } else { + error(loc, " cannot initialize this type of qualifier ", + variable->getType().getStorageQualifierString(), ""); + return nullptr; + } + } + + if (nullInit) { + // only some types can be null initialized + if (variable->getType().containsUnsizedArray()) { + error(loc, "null initializers can't size unsized arrays", "{}", ""); + return nullptr; + } + if (variable->getType().containsOpaque()) { + error(loc, "null initializers can't be used on opaque values", "{}", ""); + return nullptr; + } + variable->getWritableType().getQualifier().setNullInit(); + return nullptr; + } + + arrayObjectCheck(loc, variable->getType(), "array initializer"); + + // + // If the initializer was from braces { ... }, we convert the whole subtree to a + // constructor-style subtree, allowing the rest of the code to operate + // identically for both kinds of initializers. + // + // Type can't be deduced from the initializer list, so a skeletal type to + // follow has to be passed in. Constness and specialization-constness + // should be deduced bottom up, not dictated by the skeletal type. + // + TType skeletalType; + skeletalType.shallowCopy(variable->getType()); + skeletalType.getQualifier().makeTemporary(); + initializer = convertInitializerList(loc, skeletalType, initializer); + if (! initializer) { + // error recovery; don't leave const without constant values + if (qualifier == EvqConst) + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Fix outer arrayness if variable is unsized, getting size from the initializer + if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray()) + variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); + + // Inner arrayness can also get set by an initializer + if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && + initializer->getType().getArraySizes()->getNumDims() == + variable->getType().getArraySizes()->getNumDims()) { + // adopt unsized sizes from the initializer's sizes + for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { + if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) { + variable->getWritableType().getArraySizes()->setDimSize(d, + initializer->getType().getArraySizes()->getDimSize(d)); + } + } + } + + // Uniforms require a compile-time constant initializer + if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) { + error(loc, "uniform initializers must be constant", "=", "'%s'", + variable->getType().getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + // Global consts require a constant initializer (specialization constant is okay) + if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + error(loc, "global const initializers must be constant", "=", "'%s'", + variable->getType().getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Const variables require a constant initializer, depending on version + if (qualifier == EvqConst) { + if (! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = "non-constant initializer"; + requireProfile(loc, ~EEsProfile, initFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + variable->getWritableType().getQualifier().storage = EvqConstReadOnly; + qualifier = EvqConstReadOnly; + } + } else { + // Non-const global variables in ES need a const initializer. + // + // "In declarations of global variables with no storage qualifier or with a const + // qualifier any initializer must be a constant expression." + if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = + "non-constant global initializer (needs GL_EXT_shader_non_constant_global_initializers)"; + if (isEsProfile()) { + if (relaxedErrors() && ! extensionTurnedOn(E_GL_EXT_shader_non_constant_global_initializers)) + warn(loc, "not allowed in this version", initFeature, ""); + else + profileRequires(loc, EEsProfile, 0, E_GL_EXT_shader_non_constant_global_initializers, initFeature); + } + } + } + + if (qualifier == EvqConst || qualifier == EvqUniform) { + // Compile-time tagging of the variable with its constant value... + + initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); + if (! initializer || ! initializer->getType().getQualifier().isConstant() || + variable->getType() != initializer->getType()) { + error(loc, "non-matching or non-convertible constant type for const initializer", + variable->getType().getStorageQualifierString(), ""); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // We either have a folded constant in getAsConstantUnion, or we have to use + // the initializer's subtree in the AST to represent the computation of a + // specialization constant. + assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant()); + if (initializer->getAsConstantUnion()) + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + else { + // It's a specialization constant. + variable->getWritableType().getQualifier().makeSpecConstant(); + + // Keep the subtree that computes the specialization constant with the variable. + // Later, a symbol node will adopt the subtree from the variable. + variable->setConstSubtree(initializer); + } + } else { + // normal assigning of a value to a variable... + specializationCheck(loc, initializer->getType(), "initializer"); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + TIntermTyped* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc); + if (! initNode) + assignError(loc, "=", intermSymbol->getCompleteString(intermediate.getEnhancedMsgs()), initializer->getCompleteString(intermediate.getEnhancedMsgs())); + + return initNode; + } + + return nullptr; +} + +// +// Reprocess any initializer-list (the "{ ... }" syntax) parts of the +// initializer. +// +// Need to hierarchically assign correct types and implicit +// conversions. Will do this mimicking the same process used for +// creating a constructor-style initializer, ensuring we get the +// same form. However, it has to in parallel walk the 'type' +// passed in, as type cannot be deduced from an initializer list. +// +TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer) +{ + // Will operate recursively. Once a subtree is found that is constructor style, + // everything below it is already good: Only the "top part" of the initializer + // can be an initializer list, where "top part" can extend for several (or all) levels. + + // see if we have bottomed out in the tree within the initializer-list part + TIntermAggregate* initList = initializer->getAsAggregate(); + if (! initList || initList->getOp() != EOpNull) + return initializer; + + // Of the initializer-list set of nodes, need to process bottom up, + // so recurse deep, then process on the way up. + + // Go down the tree here... + if (type.isArray()) { + // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. + // Later on, initializer execution code will deal with array size logic. + TType arrayType; + arrayType.shallowCopy(type); // sharing struct stuff is fine + arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below + + // edit array sizes to fill in unsized dimensions + arrayType.changeOuterArraySize((int)initList->getSequence().size()); + TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); + if (arrayType.isArrayOfArrays() && firstInit->getType().isArray() && + arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { + for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) { + if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize) + arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); + } + } + + TType elementType(arrayType, 0); // dereferenced type + for (size_t i = 0; i < initList->getSequence().size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + + return addConstructor(loc, initList, arrayType); + } else if (type.isStruct()) { + if (type.getStruct()->size() != initList->getSequence().size()) { + error(loc, "wrong number of structure members", "initializer list", ""); + return nullptr; + } + for (size_t i = 0; i < type.getStruct()->size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isMatrix()) { + if (type.getMatrixCols() != (int)initList->getSequence().size()) { + error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + return nullptr; + } + TType vectorType(type, 0); // dereferenced type + for (int i = 0; i < type.getMatrixCols(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isVector()) { + if (type.getVectorSize() != (int)initList->getSequence().size()) { + error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + return nullptr; + } + TBasicType destType = type.getBasicType(); + for (int i = 0; i < type.getVectorSize(); ++i) { + TBasicType initType = initList->getSequence()[i]->getAsTyped()->getBasicType(); + if (destType != initType && !intermediate.canImplicitlyPromote(initType, destType)) { + error(loc, "type mismatch in initializer list", "initializer list", type.getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + return nullptr; + } + + } + } else { + error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString(intermediate.getEnhancedMsgs()).c_str()); + return nullptr; + } + + // Now that the subtree is processed, process this node as if the + // initializer list is a set of arguments to a constructor. + TIntermNode* emulatedConstructorArguments; + if (initList->getSequence().size() == 1) + emulatedConstructorArguments = initList->getSequence()[0]; + else + emulatedConstructorArguments = initList; + return addConstructor(loc, emulatedConstructorArguments, type); +} + +// +// Test for the correctness of the parameters passed to various constructor functions +// and also convert them to the right data type, if allowed and required. +// +// 'node' is what to construct from. +// 'type' is what type to construct. +// +// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. +// +TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type) +{ + if (node == nullptr || node->getAsTyped() == nullptr) + return nullptr; + rValueErrorCheck(loc, "constructor", node->getAsTyped()); + + TIntermAggregate* aggrNode = node->getAsAggregate(); + TOperator op = intermediate.mapTypeToConstructorOp(type); + + // Combined texture-sampler constructors are completely semantic checked + // in constructorTextureSamplerError() + if (op == EOpConstructTextureSampler) { + if (aggrNode != nullptr) { + if (aggrNode->getSequence()[1]->getAsTyped()->getType().getSampler().shadow) { + // Transfer depth into the texture (SPIR-V image) type, as a hint + // for tools to know this texture/image is a depth image. + aggrNode->getSequence()[0]->getAsTyped()->getWritableType().getSampler().shadow = true; + } + return intermediate.setAggregateOperator(aggrNode, op, type, loc); + } + } + + TTypeList::const_iterator memberTypes; + if (op == EOpConstructStruct) + memberTypes = type.getStruct()->begin(); + + TType elementType; + if (type.isArray()) { + TType dereferenced(type, 0); + elementType.shallowCopy(dereferenced); + } else + elementType.shallowCopy(type); + + bool singleArg; + if (aggrNode) { + if (aggrNode->getOp() != EOpNull) + singleArg = true; + else + singleArg = false; + } else + singleArg = true; + + TIntermTyped *newNode; + if (singleArg) { + // If structure constructor or array constructor is being called + // for only one parameter inside the structure, we need to call constructAggregate function once. + if (type.isArray()) + newNode = constructAggregate(node, elementType, 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false); + + if (newNode && (type.isArray() || op == EOpConstructStruct)) + newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); + + return newNode; + } + + // + // Handle list of arguments. + // + TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor + // if the structure constructor contains more than one parameter, then construct + // each parameter + + int paramCount = 0; // keeps track of the constructor parameter number being checked + + // We don't know "top down" whether type is a specialization constant, + // but a const becomes a specialization constant if any of its children are. + bool hasSpecConst = false; + bool isConstConstructor = true; + + // for each parameter to the constructor call, check to see if the right type is passed or convert them + // to the right type if possible (and allowed). + // for structure constructors, just check if the right type is passed, no conversion is allowed. + for (TIntermSequence::iterator p = sequenceVector.begin(); + p != sequenceVector.end(); p++, paramCount++) { + if (type.isArray()) + newNode = constructAggregate(*p, elementType, paramCount+1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount+1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); + + if (newNode) { + *p = newNode; + if (!newNode->getType().getQualifier().isConstant()) + isConstConstructor = false; + if (newNode->getType().getQualifier().isSpecConstant()) + hasSpecConst = true; + } else + return nullptr; + } + + TIntermTyped* ret_node = intermediate.setAggregateOperator(aggrNode, op, type, loc); + + const char *specConstantCompositeExt[] = { E_GL_EXT_spec_constant_composites }; + if (checkExtensionsRequested(loc, 1, specConstantCompositeExt, "spec constant aggregate constructor")) { + if (isConstConstructor && hasSpecConst) { + ret_node->getWritableType().getQualifier().makeSpecConstant(); + } + } + + TIntermAggregate *agg_node = ret_node->getAsAggregate(); + if (agg_node && (agg_node->isVector() || agg_node->isArray() || agg_node->isMatrix())) + agg_node->updatePrecision(); + + return ret_node; +} + +// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value +// for the parameter to the constructor (passed to this function). Essentially, it converts +// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a +// float, then float is converted to int. +// +// Returns nullptr for an error or the constructed node. +// +TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, + bool subset) +{ + // If we are changing a matrix in both domain of basic type and to a non matrix, + // do the shape change first (by default, below, basic type is changed before shape). + // This avoids requesting a matrix of a new type that is going to be discarded anyway. + // TODO: This could be generalized to more type combinations, but that would require + // more extensive testing and full algorithm rework. For now, the need to do two changes makes + // the recursive call work, and avoids the most egregious case of creating integer matrices. + if (node->getType().isMatrix() && (type.isScalar() || type.isVector()) && + type.isFloatingDomain() != node->getType().isFloatingDomain()) { + TType transitionType(node->getBasicType(), glslang::EvqTemporary, type.getVectorSize(), 0, 0, node->isVector()); + TOperator transitionOp = intermediate.mapTypeToConstructorOp(transitionType); + node = constructBuiltIn(transitionType, transitionOp, node, loc, false); + } + + TIntermTyped* newNode; + TOperator basicOp; + + // + // First, convert types as needed. + // + switch (op) { + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructFloat: + basicOp = EOpConstructFloat; + break; + + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructUVec2: + if (node->getType().getBasicType() == EbtReference) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "reference conversion to uvec2"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUvec2, true, node, + type); + return newNode; + } else if (node->getType().getBasicType() == EbtSampler) { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "sampler conversion to uvec2"); + // force the basic type of the constructor param to uvec2, otherwise spv builder will + // report some errors + TIntermTyped* newSrcNode = intermediate.createConversion(EbtUint, node); + newSrcNode->getAsTyped()->getWritableType().setVectorSize(2); + + TIntermTyped* newNode = + intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructUVec2, false, newSrcNode, type); + return newNode; + } + [[fallthrough]]; + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + case EOpConstructTextureSampler: + if ((node->getType().getBasicType() == EbtUint || node->getType().getBasicType() == EbtInt) && + node->getType().getVectorSize() == 2) { + requireExtensions(loc, 1, &E_GL_ARB_bindless_texture, "ivec2/uvec2 convert to texture handle"); + // No matter ivec2 or uvec2, Set EOpPackUint2x32 just to generate an opBitcast op code + TIntermTyped* newNode = + intermediate.addBuiltInFunctionCall(node->getLoc(), EOpPackUint2x32, true, node, type); + return newNode; + } + [[fallthrough]]; + case EOpConstructDVec2: + case EOpConstructDVec3: + case EOpConstructDVec4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructDouble: + basicOp = EOpConstructDouble; + break; + + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + case EOpConstructFloat16: + basicOp = EOpConstructFloat16; + // 8/16-bit storage extensions don't support direct constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + // and do not generate any conversion if it is an identity conversion, i.e. float16_t( var) + if (!intermediate.getArithemeticFloat16Enabled() && (node->getBasicType() != EbtFloat16)) { + TType tempType(EbtFloat, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructFloat16) + aggregateOp = EOpConstructFloat; + else + aggregateOp = (TOperator)(EOpConstructVec2 + op - EOpConstructF16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtFloat16, newNode); + return newNode; + } + break; + + case EOpConstructBF16Vec2: + case EOpConstructBF16Vec3: + case EOpConstructBF16Vec4: + case EOpConstructBFloat16: + basicOp = EOpConstructBFloat16; + break; + + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructInt8: + basicOp = EOpConstructInt8; + // 8/16-bit storage extensions don't support direct constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + // and do not generate any conversion if it is an identity conversion, i.e. int8_t( var) + if (!intermediate.getArithemeticInt8Enabled() && (node->getBasicType() != EbtInt8)) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt8) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt8, newNode); + return newNode; + } + break; + + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructUint8: + basicOp = EOpConstructUint8; + // 8/16-bit storage extensions don't support direct constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + // and do not generate any conversion if it is an identity conversion, i.e. uint8_t( var) + if (!intermediate.getArithemeticInt8Enabled() && (node->getBasicType() != EbtUint8)) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint8) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint8, newNode); + return newNode; + } + break; + + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructInt16: + basicOp = EOpConstructInt16; + // 8/16-bit storage extensions don't support direct constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + // and do not generate any conversion if it is an identity conversion, i.e. int16_t( var) + if (!intermediate.getArithemeticInt16Enabled() && (node->getBasicType() != EbtInt16)) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt16) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt16, newNode); + return newNode; + } + break; + + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructUint16: + basicOp = EOpConstructUint16; + // 8/16-bit storage extensions don't support direct constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + // and do not generate any conversion if it is an identity conversion, i.e. uint16_t( var) + if (!intermediate.getArithemeticInt16Enabled() && (node->getBasicType() != EbtUint16)) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint16) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint16, newNode); + return newNode; + } + break; + + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructInt64: + basicOp = EOpConstructInt64; + break; + + case EOpConstructUint64: + if (type.isScalar() && node->getType().isReference()) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUint64, true, node, type); + return newNode; + } + [[fallthrough]]; + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: + basicOp = EOpConstructUint64; + break; + + case EOpConstructNonuniform: + // Make a nonuniform copy of node + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpCopyObject, true, node, type); + return newNode; + + case EOpConstructReference: + // construct reference from reference + if (node->getType().isReference()) { + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructReference, true, node, type); + return newNode; + // construct reference from uint64 + } else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, + type); + return newNode; + // construct reference from uvec2 + } else if (node->getType().isVector() && node->getType().getBasicType() == EbtUint && + node->getVectorSize() == 2) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "uvec2 conversion to reference"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUvec2ToPtr, true, node, + type); + return newNode; + } else { + return nullptr; + } + + case EOpConstructCooperativeVectorNV: + if (!node->getType().isCoopVecNV()) { + if (type.getBasicType() != node->getType().getBasicType()) { + node = intermediate.addConversion(type.getBasicType(), node); + if (node == nullptr) + return nullptr; + } + } + if (type.getBasicType() != node->getType().getBasicType()) { + intermediate.buildConvertOp(type.getBasicType(), node->getType().getBasicType(), op); + node = intermediate.addUnaryNode(op, node, node->getLoc(), type); + return node; + } + if (subset) { + return node; + } + + node = intermediate.setAggregateOperator(node, op, type, node->getLoc()); + + return node; + + case EOpConstructCooperativeMatrixNV: + case EOpConstructCooperativeMatrixKHR: + if (node->getType() == type) { + return node; + } + if (!node->getType().isCoopMat()) { + if (type.getBasicType() != node->getType().getBasicType()) { + node = intermediate.addConversion(type.getBasicType(), node); + if (node == nullptr) + return nullptr; + } + node = intermediate.setAggregateOperator(node, op, type, node->getLoc()); + } else if (type.sameCoopMatShape(node->getType()) && !type.sameCoopMatUse(node->getType()) && + type.getBasicType() == node->getType().getBasicType()) { + node = intermediate.setAggregateOperator(node, op, type, node->getLoc()); + } else { + TOperator op = EOpConvNumeric; + + node = intermediate.addUnaryNode(op, node, node->getLoc(), type); + // If it's a (non-specialization) constant, it must be folded. + if (node->getAsUnaryNode()->getOperand()->getAsConstantUnion()) + return node->getAsUnaryNode()->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + } + + return node; + + case EOpConstructAccStruct: + if ((node->getType().isScalar() && node->getType().getBasicType() == EbtUint64)) { + // construct acceleration structure from uint64 + requireExtensions(loc, Num_ray_tracing_EXTs, ray_tracing_EXTs, "uint64_t conversion to acclerationStructureEXT"); + return intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToAccStruct, true, node, + type); + } else if (node->getType().isVector() && node->getType().getBasicType() == EbtUint && node->getVectorSize() == 2) { + // construct acceleration structure from uint64 + requireExtensions(loc, Num_ray_tracing_EXTs, ray_tracing_EXTs, "uvec2 conversion to accelerationStructureEXT"); + return intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUvec2ToAccStruct, true, node, + type); + } else + return nullptr; + + default: + error(loc, "unsupported construction", "", ""); + + return nullptr; + } + newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); + if (newNode == nullptr) { + error(loc, "can't convert", "constructor", ""); + return nullptr; + } + + // + // Now, if there still isn't an operation to do the construction, and we need one, add one. + // + + // Otherwise, skip out early. + if (subset || (newNode != node && newNode->getType() == type)) + return newNode; + + // setAggregateOperator will insert a new node for the constructor, as needed. + return intermediate.setAggregateOperator(newNode, op, type, loc); +} + +// This function tests for the type of the parameters to the structure or array constructor. Raises +// an error message if the expected type does not match the parameter passed to the constructor. +// +// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. +// +TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) +{ + TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); + if (! converted || converted->getType() != type) { + bool enhanced = intermediate.getEnhancedMsgs(); + error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, + node->getAsTyped()->getType().getCompleteString(enhanced).c_str(), type.getCompleteString(enhanced).c_str()); + + return nullptr; + } + + return converted; +} + +// If a memory qualifier is present in 'to', also make it present in 'from'. +void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& to) +{ + if (from.isReadOnly()) + to.readonly = from.readonly; + if (from.isWriteOnly()) + to.writeonly = from.writeonly; + if (from.coherent) + to.coherent = from.coherent; + if (from.volatil) + to.volatil = from.volatil; + if (from.nontemporal) + to.nontemporal = from.nontemporal; + if (from.restrict) + to.restrict = from.restrict; +} + +// +// Update qualifier layoutBindlessImage & layoutBindlessSampler on block member +// +void TParseContext::updateBindlessQualifier(TType& memberType) +{ + if (memberType.containsSampler()) { + if (memberType.isStruct()) { + TTypeList* typeList = memberType.getWritableStruct(); + for (unsigned int member = 0; member < typeList->size(); ++member) { + TType* subMemberType = (*typeList)[member].type; + updateBindlessQualifier(*subMemberType); + } + } + else if (memberType.getSampler().isImage()) { + intermediate.setBindlessImageMode(currentCaller, AstRefTypeLayout); + memberType.getQualifier().layoutBindlessImage = true; + } + else { + intermediate.setBindlessTextureMode(currentCaller, AstRefTypeLayout); + memberType.getQualifier().layoutBindlessSampler = true; + } + } +} + +// +// Do everything needed to add an interface block. +// +void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, + TArraySizes* arraySizes) +{ + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) + blockStorageRemap(loc, blockName, currentBlockQualifier); + blockStageIoCheck(loc, currentBlockQualifier); + blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr); + if (arraySizes != nullptr) { + arraySizesCheck(loc, currentBlockQualifier, arraySizes, nullptr, false); + arrayOfArrayVersionCheck(loc, arraySizes); + if (arraySizes->getNumDims() > 1) + requireProfile(loc, ~EEsProfile, "array-of-array of block"); + } + + // Inherit and check member storage qualifiers WRT to the block-level qualifier. + for (unsigned int member = 0; member < typeList.size(); ++member) { + TType& memberType = *typeList[member].type; + TQualifier& memberQualifier = memberType.getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage) + error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), ""); + memberQualifier.storage = currentBlockQualifier.storage; + globalQualifierFixCheck(memberLoc, memberQualifier); + inheritMemoryQualifiers(currentBlockQualifier, memberQualifier); + if (currentBlockQualifier.perPrimitiveNV) + memberQualifier.perPrimitiveNV = currentBlockQualifier.perPrimitiveNV; + if (currentBlockQualifier.perViewNV) + memberQualifier.perViewNV = currentBlockQualifier.perViewNV; + if (currentBlockQualifier.perTaskNV) + memberQualifier.perTaskNV = currentBlockQualifier.perTaskNV; + if (currentBlockQualifier.storage == EvqtaskPayloadSharedEXT) + memberQualifier.storage = EvqtaskPayloadSharedEXT; + if (memberQualifier.storage == EvqSpirvStorageClass) + error(memberLoc, "member cannot have a spirv_storage_class qualifier", memberType.getFieldName().c_str(), ""); + if (memberQualifier.hasSpirvDecorate() && !memberQualifier.getSpirvDecorate().decorateIds.empty()) + error(memberLoc, "member cannot have a spirv_decorate_id qualifier", memberType.getFieldName().c_str(), ""); + if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary())) + error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), ""); + if (memberType.isArray()) + arraySizesCheck(memberLoc, currentBlockQualifier, memberType.getArraySizes(), nullptr, member == typeList.size() - 1); + if (memberQualifier.hasOffset()) { + if (spvVersion.spv == 0) { + profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "\"offset\" on block member"); + profileRequires(memberLoc, EEsProfile, 300, E_GL_ARB_enhanced_layouts, "\"offset\" on block member"); + } + } + + // For bindless texture, sampler can be declared as uniform/storage block member, + if (memberType.containsOpaque()) { + if (memberType.containsSampler() && extensionTurnedOn(E_GL_ARB_bindless_texture)) + updateBindlessQualifier(memberType); + else + error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), ""); + } + + if (memberType.containsCoopMat()) + error(memberLoc, "member of block cannot be or contain a cooperative matrix type", typeList[member].type->getFieldName().c_str(), ""); + + if (memberType.containsCoopVec()) + error(memberLoc, "member of block cannot be or contain a cooperative vector type", typeList[member].type->getFieldName().c_str(), ""); + } + + // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will + // do all the rest. + if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) { + redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes); + return; + } + + // Not a redeclaration of a built-in; check that all names are user names. + reservedErrorCheck(loc, *blockName); + if (instanceName) + reservedErrorCheck(loc, *instanceName); + for (unsigned int member = 0; member < typeList.size(); ++member) + reservedErrorCheck(typeList[member].loc, typeList[member].type->getFieldName()); + + // Make default block qualification, and adjust the member qualifications + + TQualifier defaultQualification; + switch (currentBlockQualifier.storage) { + case EvqUniform: defaultQualification = globalUniformDefaults; break; + case EvqBuffer: defaultQualification = globalBufferDefaults; break; + case EvqVaryingIn: defaultQualification = globalInputDefaults; break; + case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; + case EvqShared: defaultQualification = globalSharedDefaults; break; + default: defaultQualification.clear(); break; + } + + // Special case for "push_constant uniform", which has a default of std430, + // contrary to normal uniform defaults, and can't have a default tracked for it. + if ((currentBlockQualifier.isPushConstant() && !currentBlockQualifier.hasPacking()) || + (currentBlockQualifier.isShaderRecord() && !currentBlockQualifier.hasPacking())) + currentBlockQualifier.layoutPacking = ElpStd430; + + // Special case for "taskNV in/out", which has a default of std430, + if (currentBlockQualifier.isTaskMemory() && !currentBlockQualifier.hasPacking()) + currentBlockQualifier.layoutPacking = ElpStd430; + + // fix and check for member layout qualifiers + + mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true); + + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (currentBlockQualifier.hasAlign()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) { + error(loc, "can only be used with std140, std430, or scalar layout packing", "align", ""); + defaultQualification.layoutAlign = -1; + } + } + + bool memberWithLocation = false; + bool memberWithoutLocation = false; + bool memberWithPerViewQualifier = false; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.hasStream()) { + if (defaultQualification.layoutStream != memberQualifier.layoutStream) + error(memberLoc, "member cannot contradict block", "stream", ""); + } + + // "This includes a block's inheritance of the + // current global default buffer, a block member's inheritance of the block's + // buffer, and the requirement that any *xfb_buffer* declared on a block + // member must match the buffer inherited from the block." + if (memberQualifier.hasXfbBuffer()) { + if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + } + + if (memberQualifier.hasPacking()) + error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLocation()) { + const char* feature = "location on block member"; + switch (currentBlockQualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + requireProfile(memberLoc, ECoreProfile | ECompatibilityProfile | EEsProfile, feature); + profileRequires(memberLoc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + profileRequires(memberLoc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + memberWithLocation = true; + break; + default: + error(memberLoc, "can only use in an in/out block", feature, ""); + break; + } + } else + memberWithoutLocation = true; + + // "The offset qualifier can only be used on block members of blocks declared with std140 or std430 layouts." + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (memberQualifier.hasAlign() || memberQualifier.hasOffset()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) + error(memberLoc, "can only be used with std140, std430, or scalar layout packing", "offset/align", ""); + } + + if (memberQualifier.isPerView()) { + memberWithPerViewQualifier = true; + } + + TQualifier newMemberQualification = defaultQualification; + mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false); + memberQualifier = newMemberQualification; + } + + layoutMemberLocationArrayCheck(loc, memberWithLocation, arraySizes); + + // Ensure that the block has an XfbBuffer assigned. This is needed + // because if the block has a XfbOffset assigned, then it is + // assumed that it has implicitly assigned the current global + // XfbBuffer, and because it's members need to be assigned a + // XfbOffset if they lack it. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer() && currentBlockQualifier.hasXfbOffset()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } + + // Process the members + fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation); + fixXfbOffsets(currentBlockQualifier, typeList); + fixBlockUniformOffsets(currentBlockQualifier, typeList); + fixBlockUniformLayoutMatrix(currentBlockQualifier, &typeList, nullptr); + fixBlockUniformLayoutPacking(currentBlockQualifier, &typeList, nullptr); + for (unsigned int member = 0; member < typeList.size(); ++member) + layoutTypeCheck(typeList[member].loc, *typeList[member].type); + + if (memberWithPerViewQualifier) { + for (unsigned int member = 0; member < typeList.size(); ++member) { + checkAndResizeMeshViewDim(typeList[member].loc, *typeList[member].type, /*isBlockMember*/ true); + } + } + + // reverse merge, so that currentBlockQualifier now has all layout information + // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) + mergeObjectLayoutQualifiers(currentBlockQualifier, defaultQualification, true); + + // + // Build and add the interface block as a new type named 'blockName' + // + + TType blockType(&typeList, *blockName, currentBlockQualifier); + if (arraySizes != nullptr) + blockType.transferArraySizes(arraySizes); + + if (arraySizes == nullptr) + ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName); + if (currentBlockQualifier.hasBufferReference()) { + + if (currentBlockQualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + + // Create the block reference type. If it was forward-declared, detect that + // as a referent struct type with no members. Replace the referent type with + // blockType. + TType blockNameType(EbtReference, blockType, *blockName); + TVariable* blockNameVar = new TVariable(blockName, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().isReference() && + existingName->getType().getReferentType()->getStruct() && + existingName->getType().getReferentType()->getStruct()->size() == 0 && + existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + existingName->getType().getReferentType()->deepCopy(blockType); + } else { + error(loc, "block name cannot be redefined", blockName->c_str(), ""); + } + } + if (!instanceName) { + return; + } + } else { + // + // Don't make a user-defined type out of block name; that will cause an error + // if the same block name gets reused in a different interface. + // + // "Block names have no other use within a shader + // beyond interface matching; it is a compile-time error to use a block name at global scope for anything + // other than as a block name (e.g., use of a block name for a global variable name or function name is + // currently reserved)." + // + // Use the symbol table to prevent normal reuse of the block's name, as a variable entry, + // whose type is EbtBlock, but without all the structure; that will come from the type + // the instances point to. + // + TType blockNameType(EbtBlock, blockType.getQualifier().storage); + TVariable* blockNameVar = new TVariable(blockName, blockNameType); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().getBasicType() == EbtBlock) { + if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString()); + return; + } + } else { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + return; + } + } + } + + // Add the variable, as anonymous or named instanceName. + // Make an anonymous variable if no name was provided. + if (! instanceName) + instanceName = NewPoolTString(""); + + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { + if (*instanceName == "") + error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), ""); + else + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); + + return; + } + + // Check for general layout qualifier errors + layoutObjectCheck(loc, variable); + + // fix up + if (isIoResizeArray(blockType)) { + ioArraySymbolResizeList.push_back(&variable); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, variable.getWritableType()); + + // Save it in the AST for linker use. + trackLinkage(variable); +} + +// +// allow storage type of block to be remapped at compile time +// +void TParseContext::blockStorageRemap(const TSourceLoc&, const TString* instanceName, TQualifier& qualifier) +{ + TBlockStorageClass type = intermediate.getBlockStorageOverride(instanceName->c_str()); + if (type != EbsNone) { + qualifier.setBlockStorage(type); + } +} + +// Do all block-declaration checking regarding the combination of in/out/uniform/buffer +// with a particular stage. +void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + const char *extsrt[2] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + switch (qualifier.storage) { + case EvqUniform: + profileRequires(loc, EEsProfile, 300, nullptr, "uniform block"); + profileRequires(loc, ENoProfile, 140, E_GL_ARB_uniform_buffer_object, "uniform block"); + if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.isPushConstant()) + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "std430 requires the buffer storage qualifier"); + break; + case EvqBuffer: + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "buffer block"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "buffer block"); + profileRequires(loc, EEsProfile, 310, nullptr, "buffer block"); + break; + case EvqVaryingIn: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "input block"); + // It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader + // "Compute shaders do not permit user-defined input variables..." + requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask| + EShLangFragmentMask|EShLangMeshMask), "input block"); + if (language == EShLangFragment) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "fragment input block"); + } else if (language == EShLangMesh && ! qualifier.isTaskMemory()) { + error(loc, "input blocks cannot be used in a mesh shader", "out", ""); + } + break; + case EvqVaryingOut: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "output block"); + requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask| + EShLangGeometryMask|EShLangMeshMask|EShLangTaskMask), "output block"); + // ES 310 can have a block before shader_io is turned on, so skip this test for built-ins + if (language == EShLangVertex && ! parsingBuiltins) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "vertex output block"); + } else if (language == EShLangMesh && qualifier.isTaskMemory()) { + error(loc, "can only use on input blocks in mesh shader", "taskNV", ""); + } else if (language == EShLangTask && ! qualifier.isTaskMemory()) { + error(loc, "output blocks cannot be used in a task shader", "out", ""); + } + break; + case EvqShared: + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) { + error(loc, "shared block requires at least SPIR-V 1.4", "shared block", ""); + } + profileRequires(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shared_memory_block, "shared block"); + break; + case EvqPayload: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadNV block"); + break; + case EvqPayloadIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadInNV block"); + break; + case EvqHitAttr: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "hitAttributeNV block"); + requireStage(loc, (EShLanguageMask)(EShLangIntersectMask | EShLangAnyHitMask | EShLangClosestHitMask), "hitAttributeNV block"); + break; + case EvqCallableData: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), + "callableDataNV block"); + break; + case EvqCallableDataIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV block"); + break; + case EvqHitObjectAttrNV: + profileRequires(loc, ~EEsProfile, 460, E_GL_NV_shader_invocation_reorder, "hitObjectAttributeNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | EShLangMissMask), "hitObjectAttributeNV block"); + break; + default: + error(loc, "only uniform, buffer, in, or out blocks are supported", blockName->c_str(), ""); + break; + } +} + +// Do all block-declaration checking regarding its qualifiers. +void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool /*instanceName*/) +{ + // The 4.5 specification says: + // + // interface-block : + // layout-qualifieropt interface-qualifier block-name { member-list } instance-nameopt ; + // + // interface-qualifier : + // in + // out + // patch in + // patch out + // uniform + // buffer + // + // Note however memory qualifiers aren't included, yet the specification also says + // + // "...memory qualifiers may also be used in the declaration of shader storage blocks..." + + if (qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers on an interface block", "flat/smooth/noperspective", ""); + if (qualifier.centroid) + error(loc, "cannot use centroid qualifier on an interface block", "centroid", ""); + if (qualifier.isSample()) + error(loc, "cannot use sample qualifier on an interface block", "sample", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on an interface block", "invariant", ""); + if (qualifier.isPushConstant()) + intermediate.addPushConstantCount(); + if (qualifier.isShaderRecord()) + intermediate.addShaderRecordCount(); + if (qualifier.isTaskMemory()) + intermediate.addTaskNVCount(); +} + +// +// "For a block, this process applies to the entire block, or until the first member +// is reached that has a location layout qualifier. When a block member is declared with a location +// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level +// declaration. Subsequent members are again assigned consecutive locations, based on the newest location, +// until the next member declared with a location qualifier. The values used for locations do not have to be +// declared in increasing order." +void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) +{ + // "If a block has no block-level location layout qualifier, it is required that either all or none of its members + // have a location layout qualifier, or a compile-time error results." + if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) + error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); + else { + if (memberWithLocation) { + // remove any block-level location and make it per *every* member + int nextLocation = 0; // by the rule above, initial value is not relevant + if (qualifier.hasAnyLocation()) { + nextLocation = qualifier.layoutLocation; + qualifier.layoutLocation = TQualifier::layoutLocationEnd; + if (qualifier.hasComponent()) { + // "It is a compile-time error to apply the *component* qualifier to a ... block" + error(loc, "cannot apply to a block", "component", ""); + } + if (qualifier.hasIndex()) { + error(loc, "cannot apply to a block", "index", ""); + } + } + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (! memberQualifier.hasLocation()) { + if (nextLocation >= (int)TQualifier::layoutLocationEnd) + error(memberLoc, "location is too large", "location", ""); + memberQualifier.layoutLocation = nextLocation; + memberQualifier.layoutComponent = TQualifier::layoutComponentEnd; + } + nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize( + *typeList[member].type, language); + } + } + } +} + +void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + // "If a block is qualified with xfb_offset, all its + // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any + // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer + // offsets." + + if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) + return; + + int nextOffset = qualifier.layoutXfbOffset; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + bool contains64BitType = false; + bool contains32BitType = false; + bool contains16BitType = false; + int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); + // see if we need to auto-assign an offset to this member + if (! memberQualifier.hasXfbOffset()) { + // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" + if (contains64BitType) + RoundToPow2(nextOffset, 8); + else if (contains32BitType) + RoundToPow2(nextOffset, 4); + else if (contains16BitType) + RoundToPow2(nextOffset, 2); + memberQualifier.layoutXfbOffset = nextOffset; + } else + nextOffset = memberQualifier.layoutXfbOffset; + nextOffset += memberSize; + } + + // The above gave all block members an offset, so we can take it off the block now, + // which will avoid double counting the offset usage. + qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +} + +// Calculate and save the offset of each block member, using the recursively +// defined block offset rules and the user-provided offset and align. +// +// Also, compute and save the total size of the block. For the block's size, arrayness +// is not taken into account, as each element is backed by a separate buffer. +// +void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + if (!storageCanHaveLayoutInBlock(qualifier.storage) && !qualifier.isTaskMemory()) + return; + if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar) + return; + + int offset = 0; + int memberSize; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + + // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; + int dummyStride; + int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking, + subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor); + if (memberQualifier.hasOffset()) { + // "The specified offset must be a multiple + // of the base alignment of the type of the block member it qualifies, or a compile-time error results." + if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) + error(memberLoc, "must be a multiple of the member's alignment", "offset", + "(layout offset = %d | member alignment = %d)", memberQualifier.layoutOffset, memberAlignment); + + // GLSL: "It is a compile-time error to specify an offset that is smaller than the offset of the previous + // member in the block or that lies within the previous member of the block" + if (spvVersion.spv == 0) { + if (memberQualifier.layoutOffset < offset) + error(memberLoc, "cannot lie in previous members", "offset", ""); + + // "The offset qualifier forces the qualified member to start at or after the specified + // integral-constant expression, which will be its byte offset from the beginning of the buffer. + // "The actual offset of a member is computed as + // follows: If offset was declared, start with that offset, otherwise start with the next available offset." + offset = std::max(offset, memberQualifier.layoutOffset); + } else { + // TODO: Vulkan: "It is a compile-time error to have any offset, explicit or assigned, + // that lies within another member of the block." + + offset = memberQualifier.layoutOffset; + } + } + + // "The actual alignment of a member will be the greater of the specified align alignment and the standard + // (e.g., std140) base alignment for the member's type." + if (memberQualifier.hasAlign()) + memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); + + // "If the resulting offset is not a multiple of the actual alignment, + // increase it to the first offset that is a multiple of + // the actual alignment." + RoundToPow2(offset, memberAlignment); + typeList[member].type->getQualifier().layoutOffset = offset; + offset += memberSize; + } +} + +// +// Spread LayoutMatrix to uniform block member, if a uniform block member is a struct, +// we need spread LayoutMatrix to this struct member too. and keep this rule for recursive. +// +void TParseContext::fixBlockUniformLayoutMatrix(TQualifier& qualifier, TTypeList* originTypeList, + TTypeList* tmpTypeList) +{ + assert(tmpTypeList == nullptr || originTypeList->size() == tmpTypeList->size()); + for (unsigned int member = 0; member < originTypeList->size(); ++member) { + if (qualifier.layoutPacking != ElpNone) { + if (tmpTypeList == nullptr) { + if (((*originTypeList)[member].type->isMatrix() || + (*originTypeList)[member].type->getBasicType() == EbtStruct) && + (*originTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + (*originTypeList)[member].type->getQualifier().layoutMatrix = qualifier.layoutMatrix; + } + } else { + if (((*tmpTypeList)[member].type->isMatrix() || + (*tmpTypeList)[member].type->getBasicType() == EbtStruct) && + (*tmpTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + (*tmpTypeList)[member].type->getQualifier().layoutMatrix = qualifier.layoutMatrix; + } + } + } + + if ((*originTypeList)[member].type->getBasicType() == EbtStruct) { + TQualifier* memberQualifier = nullptr; + // block member can be declare a matrix style, so it should be update to the member's style + if ((*originTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + memberQualifier = &qualifier; + } else { + memberQualifier = &((*originTypeList)[member].type->getQualifier()); + } + + const TType* tmpType = tmpTypeList == nullptr ? + (*originTypeList)[member].type->clone() : (*tmpTypeList)[member].type; + + fixBlockUniformLayoutMatrix(*memberQualifier, (*originTypeList)[member].type->getWritableStruct(), + tmpType->getWritableStruct()); + + const TTypeList* structure = recordStructCopy(matrixFixRecord, (*originTypeList)[member].type, tmpType); + + if (tmpTypeList == nullptr) { + (*originTypeList)[member].type->setStruct(const_cast(structure)); + } + if (tmpTypeList != nullptr) { + (*tmpTypeList)[member].type->setStruct(const_cast(structure)); + } + } + } +} + +// +// Spread LayoutPacking to matrix or aggregate block members. If a block member is a struct or +// array of struct, spread LayoutPacking recursively to its matrix or aggregate members. +// +void TParseContext::fixBlockUniformLayoutPacking(TQualifier& qualifier, TTypeList* originTypeList, + TTypeList* tmpTypeList) +{ + assert(tmpTypeList == nullptr || originTypeList->size() == tmpTypeList->size()); + for (unsigned int member = 0; member < originTypeList->size(); ++member) { + if (qualifier.layoutPacking != ElpNone) { + if (tmpTypeList == nullptr) { + if ((*originTypeList)[member].type->getQualifier().layoutPacking == ElpNone && + !(*originTypeList)[member].type->isScalarOrVector()) { + (*originTypeList)[member].type->getQualifier().layoutPacking = qualifier.layoutPacking; + } + } else { + if ((*tmpTypeList)[member].type->getQualifier().layoutPacking == ElpNone && + !(*tmpTypeList)[member].type->isScalarOrVector()) { + (*tmpTypeList)[member].type->getQualifier().layoutPacking = qualifier.layoutPacking; + } + } + } + + if ((*originTypeList)[member].type->getBasicType() == EbtStruct) { + // Deep copy the type in pool. + // Because, struct use in different block may have different layout qualifier. + // We have to new a object to distinguish between them. + const TType* tmpType = tmpTypeList == nullptr ? + (*originTypeList)[member].type->clone() : (*tmpTypeList)[member].type; + + fixBlockUniformLayoutPacking(qualifier, (*originTypeList)[member].type->getWritableStruct(), + tmpType->getWritableStruct()); + + const TTypeList* structure = recordStructCopy(packingFixRecord, (*originTypeList)[member].type, tmpType); + + if (tmpTypeList == nullptr) { + (*originTypeList)[member].type->setStruct(const_cast(structure)); + } + if (tmpTypeList != nullptr) { + (*tmpTypeList)[member].type->setStruct(const_cast(structure)); + } + } + } +} + +// For an identifier that is already declared, add more qualification to it. +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) +{ + TSymbol* symbol = symbolTable.find(identifier); + + // A forward declaration of a block reference looks to the grammar like adding + // a qualifier to an existing symbol. Detect this and create the block reference + // type with an empty type list, which will be filled in later in + // TParseContext::declareBlock. + if (!symbol && qualifier.hasBufferReference()) { + // The layout qualifiers are ignored in forward declaration, give warning for the most probable to be seen + if (qualifier.hasBufferReferenceAlign()) { + warn(loc, "the buffer_reference_align layout is ignored when defined in forward declaration", + identifier.c_str(), ""); + } + if (qualifier.hasPacking()) { + warn(loc, "the packing layout (scalar, std430, etc) is ignored when defined in forward declaration", + identifier.c_str(), ""); + } + TTypeList typeList; + TType blockType(&typeList, identifier, qualifier); + TType blockNameType(EbtReference, blockType, identifier); + TVariable* blockNameVar = new TVariable(&identifier, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + } + return; + } + + if (! symbol) { + error(loc, "identifier not previously declared", identifier.c_str(), ""); + return; + } + if (symbol->getAsFunction()) { + error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); + return; + } + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.hasLayout() || + qualifier.storage != EvqTemporary || + qualifier.precision != EpqNone) { + error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); + return; + } + + // For read-only built-ins, add a new symbol for holding the modified qualifier. + // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) + if (symbol->isReadOnly()) + symbol = symbolTable.copyUp(symbol); + + if (qualifier.invariant) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "invariant", ""); + symbol->getWritableType().getQualifier().invariant = true; + invariantCheck(loc, symbol->getType().getQualifier()); + } else if (qualifier.isNoContraction()) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "precise", ""); + symbol->getWritableType().getQualifier().setNoContraction(); + } else if (qualifier.specConstant) { + symbol->getWritableType().getQualifier().makeSpecConstant(); + if (qualifier.hasSpecConstantId()) + symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; + } else + warn(loc, "unknown requalification", "", ""); +} + +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) +{ + for (unsigned int i = 0; i < identifiers.size(); ++i) + addQualifierToExisting(loc, qualifier, *identifiers[i]); +} + +// Make sure 'invariant' isn't being applied to a non-allowed object. +void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (! qualifier.invariant) + return; + + bool pipeOut = qualifier.isPipeOutput(); + bool pipeIn = qualifier.isPipeInput(); + if ((version >= 300 && isEsProfile()) || (!isEsProfile() && version >= 420)) { + if (! pipeOut) + error(loc, "can only apply to an output", "invariant", ""); + } else { + if ((language == EShLangVertex && pipeIn) || (! pipeOut && ! pipeIn)) + error(loc, "can only apply to an output, or to an input in a non-vertex stage\n", "invariant", ""); + } +} + +// +// Updating default qualifier for the case of a declaration with just a qualifier, +// no type, block, or identifier. +// +void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ + if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { + assert(language == EShLangTessControl || language == EShLangGeometry || language == EShLangMesh); + const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setVertices(publicType.shaderQualifiers.vertices)) + error(loc, "cannot change previously set layout value", id, ""); + + if (language == EShLangTessControl) + checkIoArraysConsistency(loc); + } + if (publicType.shaderQualifiers.primitives != TQualifier::layoutNotSet) { + assert(language == EShLangMesh); + const char* id = "max_primitives"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setPrimitives(publicType.shaderQualifiers.primitives)) + error(loc, "cannot change previously set layout value", id, ""); + } + if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage != EvqVaryingIn) + error(loc, "can only apply to 'in'", "invocations", ""); + if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) + error(loc, "cannot change previously set layout value", "invocations", ""); + } + if (publicType.shaderQualifiers.geometry != ElgNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + switch (publicType.shaderQualifiers.geometry) { + case ElgPoints: + case ElgLines: + case ElgLinesAdjacency: + case ElgTriangles: + case ElgTrianglesAdjacency: + case ElgQuads: + case ElgIsolines: + if (language == EShLangMesh) { + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { + if (language == EShLangGeometry) + checkIoArraysConsistency(loc); + } else + error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else if (publicType.qualifier.storage == EvqVaryingOut) { + switch (publicType.shaderQualifiers.geometry) { + case ElgLines: + case ElgTriangles: + if (language != EShLangMesh) { + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + [[fallthrough]]; + case ElgPoints: + case ElgLineStrip: + case ElgTriangleStrip: + if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry)) + error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else + error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage)); + } + if (publicType.shaderQualifiers.spacing != EvsNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing)) + error(loc, "cannot change previously set vertex spacing", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } + if (publicType.shaderQualifiers.order != EvoNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexOrder(publicType.shaderQualifiers.order)) + error(loc, "cannot change previously set vertex order", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } + if (publicType.shaderQualifiers.pointMode) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPointMode(); + else + error(loc, "can only apply to 'in'", "point_mode", ""); + } + + for (int i = 0; i < 3; ++i) { + if (publicType.shaderQualifiers.localSizeNotDefault[i]) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i])) + error(loc, "cannot change previously set size", "local_size", ""); + else { + int max = 0; + if (language == EShLangCompute) { + switch (i) { + case 0: max = resources.maxComputeWorkGroupSizeX; break; + case 1: max = resources.maxComputeWorkGroupSizeY; break; + case 2: max = resources.maxComputeWorkGroupSizeZ; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); + } else if (language == EShLangMesh) { + switch (i) { + case 0: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxMeshWorkGroupSizeX_EXT : + resources.maxMeshWorkGroupSizeX_NV; + break; + case 1: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxMeshWorkGroupSizeY_EXT : + resources.maxMeshWorkGroupSizeY_NV ; + break; + case 2: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxMeshWorkGroupSizeZ_EXT : + resources.maxMeshWorkGroupSizeZ_NV ; + break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) { + TString maxsErrtring = "too large, see "; + maxsErrtring.append(extensionTurnedOn(E_GL_EXT_mesh_shader) ? + "gl_MaxMeshWorkGroupSizeEXT" : "gl_MaxMeshWorkGroupSizeNV"); + error(loc, maxsErrtring.c_str(), "local_size", ""); + } + } else if (language == EShLangTask) { + switch (i) { + case 0: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxTaskWorkGroupSizeX_EXT : + resources.maxTaskWorkGroupSizeX_NV; + break; + case 1: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxTaskWorkGroupSizeY_EXT: + resources.maxTaskWorkGroupSizeY_NV; + break; + case 2: + max = extensionTurnedOn(E_GL_EXT_mesh_shader) ? + resources.maxTaskWorkGroupSizeZ_EXT: + resources.maxTaskWorkGroupSizeZ_NV; + break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) { + TString maxsErrtring = "too large, see "; + maxsErrtring.append(extensionTurnedOn(E_GL_EXT_mesh_shader) ? + "gl_MaxTaskWorkGroupSizeEXT" : "gl_MaxTaskWorkGroupSizeNV"); + error(loc, maxsErrtring.c_str(), "local_size", ""); + } + } else { + assert(0); + } + + // Fix the existing constant gl_WorkGroupSize with this new information. + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); + } + } else + error(loc, "can only apply to 'in'", "local_size", ""); + } + if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i])) + error(loc, "cannot change previously set size", "local_size", ""); + } else + error(loc, "can only apply to 'in'", "local_size id", ""); + // Set the workgroup built-in variable as a specialization constant + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableType().getQualifier().specConstant = true; + } + } + + if (publicType.shaderQualifiers.earlyFragmentTests) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setEarlyFragmentTests(); + else + error(loc, "can only apply to 'in'", "early_fragment_tests", ""); + } + if (publicType.shaderQualifiers.earlyAndLateFragmentTestsAMD) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setEarlyAndLateFragmentTestsAMD(); + else + error(loc, "can only apply to 'in'", "early_and_late_fragment_tests_amd", ""); + } + if (publicType.shaderQualifiers.postDepthCoverage) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPostDepthCoverage(); + else + error(loc, "can only apply to 'in'", "post_coverage_coverage", ""); + } + if (publicType.shaderQualifiers.nonCoherentColorAttachmentReadEXT) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setNonCoherentColorAttachmentReadEXT(); + else + error(loc, "can only apply to 'in'", "non_coherent_color_attachment_readEXT", ""); + } + if (publicType.shaderQualifiers.nonCoherentDepthAttachmentReadEXT) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setNonCoherentDepthAttachmentReadEXT(); + else + error(loc, "can only apply to 'in'", "non_coherent_depth_attachment_readEXT", ""); + } + if (publicType.shaderQualifiers.nonCoherentStencilAttachmentReadEXT) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setNonCoherentStencilAttachmentReadEXT(); + else + error(loc, "can only apply to 'in'", "non_coherent_stencil_attachment_readEXT", ""); + } + if (publicType.shaderQualifiers.hasBlendEquation()) { + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", "blend equation", ""); + } + if (publicType.shaderQualifiers.interlockOrdering) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (!intermediate.setInterlockOrdering(publicType.shaderQualifiers.interlockOrdering)) + error(loc, "cannot change previously set fragment shader interlock ordering", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + else + error(loc, "can only apply to 'in'", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads && + publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + error(loc, "cannot be both specified", "derivative_group_quadsNV and derivative_group_linearNV", ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if ((intermediate.getLocalSizeSpecId(0) == TQualifier::layoutNotSet && (intermediate.getLocalSize(0) & 1)) || + (intermediate.getLocalSizeSpecId(1) == TQualifier::layoutNotSet && (intermediate.getLocalSize(1) & 1))) + error(loc, "requires local_size_x and local_size_y to be multiple of two", "derivative_group_quadsNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupQuads); + } + else + error(loc, "can only apply to 'in'", "derivative_group_quadsNV", ""); + } + if (publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (intermediate.getLocalSizeSpecId(0) == TQualifier::layoutNotSet && + intermediate.getLocalSizeSpecId(1) == TQualifier::layoutNotSet && + intermediate.getLocalSizeSpecId(2) == TQualifier::layoutNotSet && + (intermediate.getLocalSize(0) * + intermediate.getLocalSize(1) * + intermediate.getLocalSize(2)) % 4 != 0) + error(loc, "requires total group size to be multiple of four", "derivative_group_linearNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupLinear); + } + else + error(loc, "can only apply to 'in'", "derivative_group_linearNV", ""); + } + // Check mesh out array sizes, once all the necessary out qualifiers are defined. + if ((language == EShLangMesh) && + (intermediate.getVertices() != TQualifier::layoutNotSet) && + (intermediate.getPrimitives() != TQualifier::layoutNotSet) && + (intermediate.getOutputPrimitive() != ElgNone)) + { + checkIoArraysConsistency(loc); + } + + if (publicType.shaderQualifiers.layoutPrimitiveCulling) { + if (publicType.qualifier.storage != EvqTemporary) + error(loc, "layout qualifier cannot have storage qualifiers", "primitive_culling", "", ""); + else { + intermediate.setLayoutPrimitiveCulling(); + } + // Exit early as further checks are not valid + return; + } + + const TQualifier& qualifier = publicType.qualifier; + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.precision != EpqNone) + error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "qualifier", ""); + + // "The offset qualifier can only be used on block members of blocks..." + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasOffset() || + qualifier.hasAlign()) + error(loc, "cannot use offset or align qualifiers in a default qualifier declaration (declaration with no type)", "layout qualifier", ""); + + layoutQualifierCheck(loc, qualifier); + + switch (qualifier.storage) { + case EvqUniform: + if (qualifier.hasMatrix()) + globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalUniformDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqBuffer: + if (qualifier.hasMatrix()) + globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalBufferDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqVaryingIn: + break; + case EvqVaryingOut: + if (qualifier.hasStream()) + globalOutputDefaults.layoutStream = qualifier.layoutStream; + if (qualifier.hasXfbBuffer()) + globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; + if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { + if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } + break; + case EvqShared: + if (qualifier.hasMatrix()) + globalSharedDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalSharedDefaults.layoutPacking = qualifier.layoutPacking; + break; + default: + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', 'out' or 'shared' storage qualification", "", ""); + return; + } + + if (qualifier.hasBinding()) + error(loc, "cannot declare a default, include a type or full declaration", "binding", ""); + if (qualifier.hasAnyLocation()) + error(loc, "cannot declare a default, use a full declaration", "location/component/index", ""); + if (qualifier.hasXfbOffset()) + error(loc, "cannot declare a default, use a full declaration", "xfb_offset", ""); + if (qualifier.isPushConstant()) + error(loc, "cannot declare a default, can only be used on a block", "push_constant", ""); + if (qualifier.hasBufferReference()) + error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", ""); + if (qualifier.hasSpecConstantId()) + error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", ""); + if (qualifier.isShaderRecord()) + error(loc, "cannot declare a default, can only be used on a block", "shaderRecordNV", ""); +} + +// +// Take the sequence of statements that has been built up since the last case/default, +// put it on the list of top-level nodes for the current (inner-most) switch statement, +// and follow that by the case/default we are on now. (See switch topology comment on +// TIntermSwitch.) +// +void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + TIntermSequence* switchSequence = switchSequenceStack.back(); + + if (statements) { + if (switchSequence->size() == 0) + error(statements->getLoc(), "cannot have statements before first case/default label", "switch", ""); + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) { + // check all previous cases for the same label (or both are 'default') + for (unsigned int s = 0; s < switchSequence->size(); ++s) { + TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); + if (prevBranch) { + TIntermTyped* prevExpression = prevBranch->getExpression(); + TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); + if (prevExpression == nullptr && newExpression == nullptr) + error(branchNode->getLoc(), "duplicate label", "default", ""); + else if (prevExpression != nullptr && + newExpression != nullptr && + prevExpression->getAsConstantUnion() && + newExpression->getAsConstantUnion() && + prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == + newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) + error(branchNode->getLoc(), "duplicated value", "case", ""); + } + } + switchSequence->push_back(branchNode); + } +} + +// +// Turn the top-level node sequence built up of wrapupSwitchSubsequence9) +// into a switch node. +// +TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements) +{ + profileRequires(loc, EEsProfile, 300, nullptr, "switch statements"); + profileRequires(loc, ENoProfile, 130, nullptr, "switch statements"); + + wrapupSwitchSubsequence(lastStatements, nullptr); + + if (expression == nullptr || + (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) + error(loc, "condition must be a scalar integer expression", "switch", ""); + + // If there is nothing to do, drop the switch but still execute the expression + TIntermSequence* switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == nullptr) { + // This was originally an ERRROR, because early versions of the specification said + // "it is an error to have no statement between a label and the end of the switch statement." + // The specifications were updated to remove this (being ill-defined what a "statement" was), + // so, this became a warning. However, 3.0 tests still check for the error. + if (isEsProfile() && (version <= 300 || version >= 320) && ! relaxedErrors()) + error(loc, "last case/default label not followed by statements", "switch", ""); + else if (!isEsProfile() && (version <= 430 || version >= 460)) + error(loc, "last case/default label not followed by statements", "switch", ""); + else + warn(loc, "last case/default label not followed by statements", "switch", ""); + + + // emulate a break for error recovery + lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); + lastStatements->setOperator(EOpSequence); + switchSequence->push_back(lastStatements); + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLoc(loc); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLoc(loc); + + return switchNode; +} + +// +// When a struct used in block, and has it's own layout packing, layout matrix, +// record the origin structure of a struct to map, and Record the structure copy to the copy table, +// +const TTypeList* TParseContext::recordStructCopy(TStructRecord& record, const TType* originType, const TType* tmpType) +{ + size_t memberCount = tmpType->getStruct()->size(); + size_t originHash = 0, tmpHash = 0; + std::hash hasher; + for (size_t i = 0; i < memberCount; i++) { + size_t originMemberHash = hasher(originType->getStruct()->at(i).type->getQualifier().layoutPacking + + originType->getStruct()->at(i).type->getQualifier().layoutMatrix); + size_t tmpMemberHash = hasher(tmpType->getStruct()->at(i).type->getQualifier().layoutPacking + + tmpType->getStruct()->at(i).type->getQualifier().layoutMatrix); + originHash = hasher((originHash ^ originMemberHash) << 1); + tmpHash = hasher((tmpHash ^ tmpMemberHash) << 1); + } + const TTypeList* originStruct = originType->getStruct(); + const TTypeList* tmpStruct = tmpType->getStruct(); + if (originHash != tmpHash) { + auto fixRecords = record.find(originStruct); + if (fixRecords != record.end()) { + auto fixRecord = fixRecords->second.find(tmpHash); + if (fixRecord != fixRecords->second.end()) { + return fixRecord->second; + } else { + record[originStruct][tmpHash] = tmpStruct; + return tmpStruct; + } + } else { + record[originStruct] = std::map(); + record[originStruct][tmpHash] = tmpStruct; + return tmpStruct; + } + } + return originStruct; +} + +TLayoutFormat TParseContext::mapLegacyLayoutFormat(TLayoutFormat legacyLayoutFormat, TBasicType imageType) +{ + TLayoutFormat layoutFormat = ElfNone; + if (imageType == EbtFloat) { + switch (legacyLayoutFormat) { + case ElfSize1x16: layoutFormat = ElfR16f; break; + case ElfSize1x32: layoutFormat = ElfR32f; break; + case ElfSize2x32: layoutFormat = ElfRg32f; break; + case ElfSize4x32: layoutFormat = ElfRgba32f; break; + default: break; + } + } else if (imageType == EbtUint) { + switch (legacyLayoutFormat) { + case ElfSize1x8: layoutFormat = ElfR8ui; break; + case ElfSize1x16: layoutFormat = ElfR16ui; break; + case ElfSize1x32: layoutFormat = ElfR32ui; break; + case ElfSize2x32: layoutFormat = ElfRg32ui; break; + case ElfSize4x32: layoutFormat = ElfRgba32ui; break; + default: break; + } + } else if (imageType == EbtInt) { + switch (legacyLayoutFormat) { + case ElfSize1x8: layoutFormat = ElfR8i; break; + case ElfSize1x16: layoutFormat = ElfR16i; break; + case ElfSize1x32: layoutFormat = ElfR32i; break; + case ElfSize2x32: layoutFormat = ElfRg32i; break; + case ElfSize4x32: layoutFormat = ElfRgba32i; break; + default: break; + } + } + + return layoutFormat; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.h new file mode 100644 index 00000000..47a72999 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ParseHelper.h @@ -0,0 +1,593 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This header defines a two-level parse-helper hierarchy, derived from +// TParseVersions: +// - TParseContextBase: sharable across multiple parsers +// - TParseContext: GLSL specific helper +// + +#ifndef _PARSER_HELPER_INCLUDED_ +#define _PARSER_HELPER_INCLUDED_ + +#include +#include + +#include "parseVersions.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "localintermediate.h" +#include "Scan.h" +#include "attribute.h" + +namespace glslang { + +struct TPragma { + TPragma(bool o, bool d) : optimize(o), debug(d) { } + bool optimize; + bool debug; + TPragmaTable pragmaTable; +}; + +class TScanContext; +class TPpContext; + +typedef std::set TIdSetType; +typedef std::map> TStructRecord; + +// +// Sharable code (as well as what's in TParseVersions) across +// parse helpers. +// +class TParseContextBase : public TParseVersions { +public: + TParseContextBase(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint = nullptr) + : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages), + scopeMangler("::"), + symbolTable(symbolTable), + statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), blockNestingLevel(0), controlFlowNestingLevel(0), + currentFunctionType(nullptr), + postEntryPointReturn(false), + contextPragma(true, false), + beginInvocationInterlockCount(0), endInvocationInterlockCount(0), + parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr), + limits(resources.limits), + globalUniformBlock(nullptr), + globalUniformBinding(TQualifier::layoutBindingEnd), + globalUniformSet(TQualifier::layoutSetEnd), + atomicCounterBlockSet(TQualifier::layoutSetEnd) + { + // use storage buffer on SPIR-V 1.3 and up + if (spvVersion.spv >= EShTargetSpv_1_3) + intermediate.setUseStorageBuffer(); + + if (entryPoint != nullptr) + sourceEntryPointName = *entryPoint; + } + virtual ~TParseContextBase() { } + + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + + virtual void setLimits(const TBuiltInResource&) = 0; + + void checkIndex(const TSourceLoc&, const TType&, int& index); + + EShLanguage getLanguage() const { return language; } + void setScanContext(TScanContext* c) { scanContext = c; } + TScanContext* getScanContext() const { return scanContext; } + void setPpContext(TPpContext* c) { ppContext = c; } + TPpContext* getPpContext() const { return ppContext; } + + virtual void setLineCallback(const std::function& func) { lineCallback = func; } + virtual void setExtensionCallback(const std::function& func) { extensionCallback = func; } + virtual void setVersionCallback(const std::function& func) { versionCallback = func; } + virtual void setPragmaCallback(const std::function&)>& func) { pragmaCallback = func; } + virtual void setErrorCallback(const std::function& func) { errorCallback = func; } + + virtual void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) = 0; + virtual bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) = 0; + virtual bool lineDirectiveShouldSetNextLine() const = 0; + virtual void handlePragma(const TSourceLoc&, const TVector&) = 0; + + virtual bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) = 0; + + virtual void notifyVersion(int line, int version, const char* type_string) + { + if (versionCallback) + versionCallback(line, version, type_string); + } + virtual void notifyErrorDirective(int line, const char* error_message) + { + if (errorCallback) + errorCallback(line, error_message); + } + virtual void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName) + { + if (lineCallback) + lineCallback(curLineNo, newLineNo, hasSource, sourceNum, sourceName); + } + virtual void notifyExtensionDirective(int line, const char* extension, const char* behavior) + { + if (extensionCallback) + extensionCallback(line, extension, behavior); + } + + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); + + // Manage global buffer (used for backing atomic counters in GLSL when using relaxed Vulkan semantics) + virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); + + // Potentially rename shader entry point function + void renameShaderFunction(TString*& name) const + { + // Replace the entry point name given in the shader with the real entry point name, + // if there is a substitution. + if (name != nullptr && *name == sourceEntryPointName && intermediate.getEntryPointName().size() > 0) + name = NewPoolTString(intermediate.getEntryPointName().c_str()); + } + + virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + virtual void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + + const char* const scopeMangler; + + // Basic parsing state, easily accessible to the grammar + + TSymbolTable& symbolTable; // symbol table that goes with the current language, version, and profile + TVector relaxedSymbols; + int statementNestingLevel; // 0 if outside all flow control or compound statements + int loopNestingLevel; // 0 if outside all loops + int structNestingLevel; // 0 if outside structures + int blockNestingLevel; // 0 if outside blocks + int controlFlowNestingLevel; // 0 if outside all flow control + const TType* currentFunctionType; // the return type of the function that's currently being parsed + bool functionReturnsValue; // true if a non-void function has a return + // if inside a function, true if the function is the entry point and this is after a return statement + bool postEntryPointReturn; + // case, node, case, case, node, ...; ensure only one node between cases; stack of them for nesting + TList switchSequenceStack; + // the statementNestingLevel the current switch statement is at, which must match the level of its case statements + TList switchLevel; + struct TPragma contextPragma; + int beginInvocationInterlockCount; + int endInvocationInterlockCount; + bool compileOnly = false; + +protected: + TParseContextBase(TParseContextBase&); + TParseContextBase& operator=(TParseContextBase&); + + const bool parsingBuiltins; // true if parsing built-in symbols/functions + TVector linkageSymbols; // will be transferred to 'linkage', after all editing is done, order preserving + TScanContext* scanContext; + TPpContext* ppContext; + TBuiltInResource resources; + TLimits& limits; + TString sourceEntryPointName; + + // These, if set, will be called when a line, pragma ... is preprocessed. + // They will be called with any parameters to the original directive. + std::function lineCallback; + std::function&)> pragmaCallback; + std::function versionCallback; + std::function extensionCallback; + std::function errorCallback; + + // see implementation for detail + const TFunction* selectFunction(const TVector, const TFunction&, + std::function, + std::function, + /* output */ bool& tie); + + virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size, + TSwizzleSelectors&); + + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + TVariable* globalUniformBlock; // the actual block, inserted into the symbol table + unsigned int globalUniformBinding; // the block's binding number + unsigned int globalUniformSet; // the block's set number + int firstNewMember; // the index of the first member not yet inserted into the symbol table + // override this to set the language-specific name + virtual const char* getGlobalUniformBlockName() const { return ""; } + virtual void setUniformBlockDefaults(TType&) const { } + virtual void finalizeGlobalUniformBlockLayout(TVariable&) {} + + // Manage the atomic counter block (used for atomic_uints with Vulkan-Relaxed) + TMap atomicCounterBuffers; + unsigned int atomicCounterBlockSet; + TMap atomicCounterBlockFirstNewMember; + // override this to set the language-specific name + virtual const char* getAtomicCounterBlockName() const { return ""; } + virtual void setAtomicCounterBlockDefaults(TType&) const {} + virtual void setInvariant(const TSourceLoc&, const char*) {} + virtual void finalizeAtomicCounterBlockLayout(TVariable&) {} + bool isAtomicCounterBlock(const TSymbol& symbol) { + const TVariable* var = symbol.getAsVariable(); + if (!var) + return false; + const auto& at = atomicCounterBuffers.find(var->getType().getQualifier().layoutBinding); + return (at != atomicCounterBuffers.end() && (*at).second->getType() == var->getType()); + } + + virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, TPrefixType prefix, + va_list args); + virtual void trackLinkage(TSymbol& symbol); + virtual void makeEditable(TSymbol*&); + virtual TVariable* getEditableVariable(const char* name); + virtual void finish(); +}; + +// +// Manage the state for when to respect precision qualifiers and when to warn about +// the defaults being different than might be expected. +// +class TPrecisionManager { +public: + TPrecisionManager() : obey(false), warn(false), explicitIntDefault(false), explicitFloatDefault(false){ } + virtual ~TPrecisionManager() {} + + void respectPrecisionQualifiers() { obey = true; } + bool respectingPrecisionQualifiers() const { return obey; } + bool shouldWarnAboutDefaults() const { return warn; } + void defaultWarningGiven() { warn = false; } + void warnAboutDefaults() { warn = true; } + void explicitIntDefaultSeen() + { + explicitIntDefault = true; + if (explicitFloatDefault) + warn = false; + } + void explicitFloatDefaultSeen() + { + explicitFloatDefault = true; + if (explicitIntDefault) + warn = false; + } + +protected: + bool obey; // respect precision qualifiers + bool warn; // need to give a warning about the defaults + bool explicitIntDefault; // user set the default for int/uint + bool explicitFloatDefault; // user set the default for float +}; + +// +// GLSL-specific parse helper. Should have GLSL in the name, but that's +// too big of a change for comparing branches at the moment, and perhaps +// impacts downstream consumers as well. +// +class TParseContext : public TParseContextBase { +public: + TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&, + bool forwardCompatible = false, EShMessages messages = EShMsgDefault, + const TString* entryPoint = nullptr); + virtual ~TParseContext(); + + bool obeyPrecisionQualifiers() const { return precisionManager.respectingPrecisionQualifiers(); } + void setPrecisionDefaults(); + + void setLimits(const TBuiltInResource&) override; + bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; + void parserError(const char* s); // for bison's yyerror + + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override; + virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override; + + void reservedErrorCheck(const TSourceLoc&, const TString&); + void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override; + bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override; + bool lineDirectiveShouldSetNextLine() const override; + bool builtInName(const TString&); + + void handlePragma(const TSourceLoc&, const TVector&) override; + TIntermTyped* handleVariable(const TSourceLoc&, TSymbol* symbol, const TString* string); + TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + void handleIndexLimits(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + + void makeEditable(TSymbol*&) override; + void ioArrayCheck(const TSourceLoc&, const TType&, const TString& identifier); + bool isIoResizeArray(const TType&) const; + void fixIoArraySize(const TSourceLoc&, TType&); + void handleIoResizeArrayAccess(const TSourceLoc&, TIntermTyped* base); + void checkIoArraysConsistency(const TSourceLoc&, bool tailOnly = false); + int getIoArrayImplicitSize(const TQualifier&, TString* featureString = nullptr) const; + void checkIoArrayConsistency(const TSourceLoc&, int requiredSize, const char* feature, TType&, const TString&); + + TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode); + TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field); + TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field); + void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName); + TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&); + TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*); + TIntermTyped* handleBuiltInFunctionCall(TSourceLoc, TIntermNode* arguments, const TFunction& function); + void computeBuiltinPrecisions(TIntermTyped&, const TFunction&); + TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*); + void checkLocation(const TSourceLoc&, TOperator); + TIntermTyped* handleLengthMethod(const TSourceLoc&, TFunction*, TIntermNode*); + void addInputArgumentConversions(const TFunction&, TIntermNode*&) const; + TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermAggregate&) const; + TIntermTyped* addAssign(const TSourceLoc&, TOperator op, TIntermTyped* left, TIntermTyped* right); + void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); + void nonOpBuiltInCheck(const TSourceLoc&, const TFunction&, TIntermAggregate&); + void userFunctionCallCheck(const TSourceLoc&, TIntermAggregate&); + void samplerConstructorLocationCheck(const TSourceLoc&, const char* token, TIntermNode*); + TFunction* handleConstructorCall(const TSourceLoc&, const TPublicType&); + void handlePrecisionQualifier(const TSourceLoc&, TQualifier&, TPrecisionQualifier); + void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier); + void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode); + + TIntermTyped* vkRelaxedRemapFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*); + // returns true if the variable was remapped to something else + bool vkRelaxedRemapUniformVariable(const TSourceLoc&, TString&, const TPublicType&, TArraySizes*, TIntermTyped*, TType&); + void vkRelaxedRemapUniformMembers(const TSourceLoc&, const TPublicType&, const TType&, const TString&); + void vkRelaxedRemapFunctionParameter(TFunction*, TParameter&, std::vector* newParams = nullptr); + TIntermNode* vkRelaxedRemapFunctionArgument(const TSourceLoc&, TFunction*, TIntermTyped*); + TIntermTyped* vkRelaxedRemapDotDereference(const TSourceLoc&, TIntermTyped&, const TType&, const TString&); + + void assignError(const TSourceLoc&, const char* op, TString left, TString right); + void unaryOpError(const TSourceLoc&, const char* op, TString operand); + void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); + void variableCheck(TIntermTyped*& nodePtr); + bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void constantValueCheck(TIntermTyped* node, const char* token); + void integerCheck(const TIntermTyped* node, const char* token); + void globalCheck(const TSourceLoc&, const char* token); + bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&); + bool constructorTextureSamplerError(const TSourceLoc&, const TFunction&); + void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&, const char *sizeType, const bool isTypeParameter = false); + bool arrayQualifierError(const TSourceLoc&, const TQualifier&); + bool arrayError(const TSourceLoc&, const TType&); + void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&); + void structArrayCheck(const TSourceLoc&, const TType& structure); + void arraySizesCheck(const TSourceLoc&, const TQualifier&, TArraySizes*, const TIntermTyped* initializer, bool lastMember); + void arrayOfArrayVersionCheck(const TSourceLoc&, const TArraySizes*); + bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType); + void boolCheck(const TSourceLoc&, const TIntermTyped*); + void boolCheck(const TSourceLoc&, const TPublicType&); + void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier, TIntermTyped* initializer); + void atomicUintCheck(const TSourceLoc&, const TType&, const TString& identifier); + void accStructCheck(const TSourceLoc & loc, const TType & type, const TString & identifier); + void hitObjectNVCheck(const TSourceLoc & loc, const TType & type, const TString & identifier); + void transparentOpaqueCheck(const TSourceLoc&, const TType&, const TString& identifier); + void memberQualifierCheck(glslang::TPublicType&); + void globalQualifierFixCheck(const TSourceLoc&, TQualifier&, bool isMemberCheck = false, const TPublicType* publicType = nullptr); + void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&); + bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType); + void mergeQualifiers(const TSourceLoc&, TQualifier& dst, const TQualifier& src, bool force); + void setDefaultPrecision(const TSourceLoc&, TPublicType&, TPrecisionQualifier); + int computeSamplerTypeIndex(TSampler&); + TPrecisionQualifier getDefaultPrecision(TPublicType&); + void precisionQualifierCheck(const TSourceLoc&, TBasicType, TQualifier&, bool isCoopMatOrVec); + void parameterTypeCheck(const TSourceLoc&, TStorageQualifier qualifier, const TType& type); + bool containsFieldWithBasicType(const TType& type ,TBasicType basicType); + TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&); + void redeclareBuiltinBlock(const TSourceLoc&, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes); + void paramCheckFixStorage(const TSourceLoc&, const TStorageQualifier&, TType& type); + void paramCheckFix(const TSourceLoc&, const TQualifier&, TType& type); + void nestedBlockCheck(const TSourceLoc&); + void nestedStructCheck(const TSourceLoc&); + void arrayObjectCheck(const TSourceLoc&, const TType&, const char* op); + void opaqueCheck(const TSourceLoc&, const TType&, const char* op); + void referenceCheck(const TSourceLoc&, const TType&, const char* op); + void storage16BitAssignmentCheck(const TSourceLoc&, const TType&, const char* op); + void specializationCheck(const TSourceLoc&, const TType&, const char* op); + void structTypeCheck(const TSourceLoc&, TPublicType&); + void inductiveLoopCheck(const TSourceLoc&, TIntermNode* init, TIntermLoop* loop); + void arrayLimitCheck(const TSourceLoc&, const TString&, int size); + void limitCheck(const TSourceLoc&, int value, const char* limit, const char* feature); + void typeParametersCheck(const TSourceLoc&, const TPublicType&); + + void inductiveLoopBodyCheck(TIntermNode*, long long loopIndexId, TSymbolTable&); + void constantIndexExpressionCheck(TIntermNode*); + + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&); + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&, const TIntermTyped*); + void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); + void layoutObjectCheck(const TSourceLoc&, const TSymbol&); + void layoutMemberLocationArrayCheck(const TSourceLoc&, bool memberWithLocation, TArraySizes* arraySizes); + void layoutTypeCheck(const TSourceLoc&, const TType&); + void layoutQualifierCheck(const TSourceLoc&, const TQualifier&); + void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); + void fixOffset(const TSourceLoc&, TSymbol&); + + const TFunction* findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + void declareTypeDefaults(const TSourceLoc&, const TPublicType&); + TIntermNode* declareVariable(const TSourceLoc&, TString& identifier, const TPublicType&, TArraySizes* typeArray = nullptr, TIntermTyped* initializer = nullptr); + TIntermTyped* addConstructor(const TSourceLoc&, TIntermNode*, const TType&); + TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); + TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); + void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to); + void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = nullptr, TArraySizes* arraySizes = nullptr); + void blockStorageRemap(const TSourceLoc&, const TString*, TQualifier&); + void blockStageIoCheck(const TSourceLoc&, const TQualifier&); + void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName); + void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); + void fixXfbOffsets(TQualifier&, TTypeList&); + void fixBlockUniformOffsets(TQualifier&, TTypeList&); + void fixBlockUniformLayoutMatrix(TQualifier&, TTypeList*, TTypeList*); + void fixBlockUniformLayoutPacking(TQualifier&, TTypeList*, TTypeList*); + void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier); + void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); + void invariantCheck(const TSourceLoc&, const TQualifier&); + void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); + void updateBindlessQualifier(TType& memberType); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body); + const TTypeList* recordStructCopy(TStructRecord&, const TType*, const TType*); + TLayoutFormat mapLegacyLayoutFormat(TLayoutFormat legacyLayoutFormat, TBasicType imageType); + + TAttributeType attributeFromName(const TString& name) const; + TAttributes* makeAttributes(const TString& identifier) const; + TAttributes* makeAttributes(const TString& identifier, TIntermNode* node) const; + TAttributes* mergeAttributes(TAttributes*, TAttributes*) const; + + // Determine selection control from attributes + void handleSelectionAttributes(const TAttributes& attributes, TIntermNode*); + void handleSwitchAttributes(const TAttributes& attributes, TIntermNode*); + // Determine loop control from attributes + void handleLoopAttributes(const TAttributes& attributes, TIntermNode*); + // Function attributes + void handleFunctionAttributes(const TSourceLoc&, const TAttributes&); + + // GL_EXT_spirv_intrinsics + TSpirvRequirement* makeSpirvRequirement(const TSourceLoc& loc, const TString& name, + const TIntermAggregate* extensions, const TIntermAggregate* capabilities); + TSpirvRequirement* mergeSpirvRequirements(const TSourceLoc& loc, TSpirvRequirement* spirvReq1, + TSpirvRequirement* spirvReq2); + TSpirvTypeParameters* makeSpirvTypeParameters(const TSourceLoc& loc, const TIntermConstantUnion* constant); + TSpirvTypeParameters* makeSpirvTypeParameters(const TSourceLoc& loc, const TPublicType& type); + TSpirvTypeParameters* mergeSpirvTypeParameters(TSpirvTypeParameters* spirvTypeParams1, + TSpirvTypeParameters* spirvTypeParams2); + TSpirvInstruction* makeSpirvInstruction(const TSourceLoc& loc, const TString& name, const TString& value); + TSpirvInstruction* makeSpirvInstruction(const TSourceLoc& loc, const TString& name, int value); + TSpirvInstruction* mergeSpirvInstruction(const TSourceLoc& loc, TSpirvInstruction* spirvInst1, + TSpirvInstruction* spirvInst2); + void checkAndResizeMeshViewDim(const TSourceLoc&, TType&, bool isBlockMember); + +protected: + void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); + void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; + TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&); + void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&); + void checkRuntimeSizable(const TSourceLoc&, const TIntermTyped&); + bool isRuntimeLength(const TIntermTyped&) const; + TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); + TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); + void finish() override; + void handleCoopMat2FunctionCall(const TSourceLoc& loc, const TFunction* fnCandidate, TIntermTyped* result, TIntermNode* arguments); + + virtual const char* getGlobalUniformBlockName() const override; + virtual void finalizeGlobalUniformBlockLayout(TVariable&) override; + virtual void setUniformBlockDefaults(TType& block) const override; + + virtual const char* getAtomicCounterBlockName() const override; + virtual void finalizeAtomicCounterBlockLayout(TVariable&) override; + virtual void setAtomicCounterBlockDefaults(TType& block) const override; + virtual void setInvariant(const TSourceLoc& loc, const char* builtin) override; + +public: + // + // Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access + // + + // Current state of parsing + bool inMain; // if inside a function, true if the function is main + const TString* blockName; + TQualifier currentBlockQualifier; + TPrecisionQualifier defaultPrecision[EbtNumTypes]; + TBuiltInResource resources; + TLimits& limits; + +protected: + TParseContext(TParseContext&); + TParseContext& operator=(TParseContext&); + + static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2 * 2 * 2)); // see computeSamplerTypeIndex() + TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex]; + TPrecisionManager precisionManager; + TQualifier globalBufferDefaults; + TQualifier globalUniformDefaults; + TQualifier globalInputDefaults; + TQualifier globalOutputDefaults; + TQualifier globalSharedDefaults; + TString currentCaller; // name of last function body entered (not valid when at global scope) + int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point + bool anyIndexLimits; + TIdSetType inductiveLoopIds; + TVector needsIndexLimitationChecking; + TStructRecord matrixFixRecord; + TStructRecord packingFixRecord; + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of symbols whose array sizes + // can be fixed + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in array use: copyUp() symbol and add it to the resize-list + // + // - on user array declaration: add it to the resize-list + // + // - on block redeclaration: copyUp() symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing a layout declaration that sizes the array, fix everything in the + // resize-list, giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // array-sizing declarations + // + TVector ioArraySymbolResizeList; +}; + +} // end namespace glslang + +#endif // _PARSER_HELPER_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/PoolAlloc.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/PoolAlloc.cpp new file mode 100644 index 00000000..93a3b0d1 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/PoolAlloc.cpp @@ -0,0 +1,306 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/PoolAlloc.h" + +// Mostly here for target that do not support threads such as WASI. +#ifdef DISABLE_THREAD_SUPPORT +#define THREAD_LOCAL +#else +#define THREAD_LOCAL thread_local +#endif + +namespace glslang { + +namespace { +THREAD_LOCAL TPoolAllocator* threadPoolAllocator = nullptr; + +TPoolAllocator* GetDefaultThreadPoolAllocator() +{ + THREAD_LOCAL TPoolAllocator defaultAllocator; + return &defaultAllocator; +} +} // anonymous namespace + +// Return the thread-specific current pool. +TPoolAllocator& GetThreadPoolAllocator() +{ + return *(threadPoolAllocator ? threadPoolAllocator : GetDefaultThreadPoolAllocator()); +} + +// Set the thread-specific current pool. +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator) +{ + threadPoolAllocator = poolAllocator; +} + +// +// Implement the functionality of the TPoolAllocator class, which +// is documented in PoolAlloc.h. +// +TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : + pageSize(growthIncrement), + alignment(allocationAlignment), + freeList(nullptr), + inUseList(nullptr), + numCalls(0) +{ + // + // Don't allow page sizes we know are smaller than all common + // OS page sizes. + // + if (pageSize < 4*1024) + pageSize = 4*1024; + + // + // A large currentPageOffset indicates a new page needs to + // be obtained to allocate memory. + // + currentPageOffset = pageSize; + + // + // Adjust alignment to be at least pointer aligned and + // power of 2. + // + size_t minAlign = sizeof(void*); + alignment &= ~(minAlign - 1); + if (alignment < minAlign) + alignment = minAlign; + size_t a = 1; + while (a < alignment) + a <<= 1; + alignment = a; + alignmentMask = a - 1; + + // + // Align header skip + // + headerSkip = minAlign; + if (headerSkip < sizeof(tHeader)) { + headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; + } + + push(); +} + +TPoolAllocator::~TPoolAllocator() +{ + while (inUseList) { + tHeader* next = inUseList->nextPage; + inUseList->~tHeader(); + delete [] reinterpret_cast(inUseList); + inUseList = next; + } + + // + // Always delete the free list memory - it can't be being + // (correctly) referenced, whether the pool allocator was + // global or not. We should not check the guard blocks + // here, because we did it already when the block was + // placed into the free list. + // + while (freeList) { + tHeader* next = freeList->nextPage; + delete [] reinterpret_cast(freeList); + freeList = next; + } +} + +// +// Check a single guard block for damage +// +#ifdef GUARD_BLOCKS +void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const +#else +void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const +#endif +{ +#ifdef GUARD_BLOCKS + for (size_t x = 0; x < guardBlockSize; x++) { + if (blockMem[x] != val) { + const int maxSize = 80; + char assertMsg[maxSize]; + + // We don't print the assert message. It's here just to be helpful. + snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", + locText, size, data()); + assert(0 && "PoolAlloc: Damage in guard block"); + } + } +#else + assert(guardBlockSize == 0); +#endif +} + +void TPoolAllocator::push() +{ + tAllocState state = { currentPageOffset, inUseList }; + + stack.push_back(state); + + // + // Indicate there is no current page to allocate from. + // + currentPageOffset = pageSize; +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred since the last push(), or since the +// last pop(), or since the object's creation. +// +// The deallocated pages are saved for future allocations. +// +void TPoolAllocator::pop() +{ + if (stack.size() < 1) + return; + + tHeader* page = stack.back().page; + currentPageOffset = stack.back().offset; + + while (inUseList != page) { + tHeader* nextInUse = inUseList->nextPage; + size_t pageCount = inUseList->pageCount; + + // This technically ends the lifetime of the header as C++ object, + // but we will still control the memory and reuse it. + inUseList->~tHeader(); // currently, just a debug allocation checker + + if (pageCount > 1) { + delete [] reinterpret_cast(inUseList); + } else { + inUseList->nextPage = freeList; + freeList = inUseList; + } + inUseList = nextInUse; + } + + stack.pop_back(); +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred. +// +void TPoolAllocator::popAll() +{ + while (stack.size() > 0) + pop(); +} + +void* TPoolAllocator::allocate(size_t numBytes) +{ + // If we are using guard blocks, all allocations are bracketed by + // them: [guardblock][allocation][guardblock]. numBytes is how + // much memory the caller asked for. allocationSize is the total + // size including guard blocks. In release build, + // guardBlockSize=0 and this all gets optimized away. + size_t allocationSize = TAllocation::allocationSize(numBytes); + + // + // Just keep some interesting statistics. + // + ++numCalls; + totalBytes += numBytes; + + // + // Do the allocation, most likely case first, for efficiency. + // This step could be moved to be inline sometime. + // + if (currentPageOffset + allocationSize <= pageSize) { + // + // Safe to allocate from currentPageOffset. + // + unsigned char* memory = reinterpret_cast(inUseList) + currentPageOffset; + currentPageOffset += allocationSize; + currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, memory, numBytes); + } + + if (allocationSize + headerSkip > pageSize) { + // + // Do a multi-page allocation. Don't mix these with the others. + // The OS is efficient and allocating and free-ing multiple pages. + // + size_t numBytesToAlloc = allocationSize + headerSkip; + tHeader* memory = reinterpret_cast(::new char[numBytesToAlloc]); + if (memory == nullptr) + return nullptr; + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); + inUseList = memory; + + currentPageOffset = pageSize; // make next allocation come from a new page + + // No guard blocks for multi-page allocations (yet) + return reinterpret_cast(reinterpret_cast(memory) + headerSkip); + } + + // + // Need a simple page to allocate from. + // + tHeader* memory; + if (freeList) { + memory = freeList; + freeList = freeList->nextPage; + } else { + memory = reinterpret_cast(::new char[pageSize]); + if (memory == nullptr) + return nullptr; + } + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, 1); + inUseList = memory; + + unsigned char* ret = reinterpret_cast(inUseList) + headerSkip; + currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, ret, numBytes); +} + +// +// Check all allocations in a list for damage by calling check on each. +// +void TAllocation::checkAllocList() const +{ + for (const TAllocation* alloc = this; alloc != nullptr; alloc = alloc->prevAlloc) + alloc->check(); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.cpp new file mode 100644 index 00000000..1d33bfd2 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.cpp @@ -0,0 +1,118 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" +#include "RemoveTree.h" + +namespace glslang { + +// +// Code to recursively delete the intermediate tree. +// +struct TRemoveTraverser : TIntermTraverser { + TRemoveTraverser() : TIntermTraverser(false, false, true, false) {} + + virtual void visitSymbol(TIntermSymbol* node) + { + delete node; + } + + virtual bool visitBinary(TVisit /* visit*/ , TIntermBinary* node) + { + delete node; + + return true; + } + + virtual bool visitUnary(TVisit /* visit */, TIntermUnary* node) + { + delete node; + + return true; + } + + virtual bool visitAggregate(TVisit /* visit*/ , TIntermAggregate* node) + { + delete node; + + return true; + } + + virtual bool visitSelection(TVisit /* visit*/ , TIntermSelection* node) + { + delete node; + + return true; + } + + virtual bool visitSwitch(TVisit /* visit*/ , TIntermSwitch* node) + { + delete node; + + return true; + } + + virtual void visitConstantUnion(TIntermConstantUnion* node) + { + delete node; + } + + virtual bool visitLoop(TVisit /* visit*/ , TIntermLoop* node) + { + delete node; + + return true; + } + + virtual bool visitBranch(TVisit /* visit*/ , TIntermBranch* node) + { + delete node; + + return true; + } +}; + +// +// Entry point. +// +void RemoveAllTreeNodes(TIntermNode* root) +{ + TRemoveTraverser it; + + root->traverse(&it); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.h new file mode 100644 index 00000000..1ed01562 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/RemoveTree.h @@ -0,0 +1,41 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +namespace glslang { + +void RemoveAllTreeNodes(TIntermNode*); + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.cpp new file mode 100644 index 00000000..d5778d72 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.cpp @@ -0,0 +1,2031 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// GLSL scanning, leveraging the scanning done by the preprocessor. +// + +#include +#include +#include + +#include "../Include/Types.h" +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "attribute.h" +#include "glslang_tab.cpp.h" +#include "ScanContext.h" +#include "Scan.h" + +// preprocessor includes +#include "preprocessor/PpContext.h" +#include "preprocessor/PpTokens.h" + +// Required to avoid missing prototype warnings for some compilers +int yylex(YYSTYPE*, glslang::TParseContext&); + +namespace glslang { + +// read past any white space +void TInputScanner::consumeWhiteSpace(bool& foundNonSpaceTab) +{ + int c = peek(); // don't accidentally consume anything other than whitespace + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if (c == '\r' || c == '\n') + foundNonSpaceTab = true; + get(); + c = peek(); + } +} + +// return true if a comment was actually consumed +bool TInputScanner::consumeComment() +{ + if (peek() != '/') + return false; + + get(); // consume the '/' + int c = peek(); + if (c == '/') { + + // a '//' style comment + get(); // consume the second '/' + c = get(); + do { + while (c != EndOfInput && c != '\\' && c != '\r' && c != '\n') + c = get(); + + if (c == EndOfInput || c == '\r' || c == '\n') { + while (c == '\r' || c == '\n') + c = get(); + + // we reached the end of the comment + break; + } else { + // it's a '\', so we need to keep going, after skipping what's escaped + + // read the skipped character + c = get(); + + // if it's a two-character newline, skip both characters + if (c == '\r' && peek() == '\n') + get(); + c = get(); + } + } while (true); + + // put back the last non-comment character + if (c != EndOfInput) + unget(); + + return true; + } else if (c == '*') { + + // a '/*' style comment + get(); // consume the '*' + c = get(); + do { + while (c != EndOfInput && c != '*') + c = get(); + if (c == '*') { + c = get(); + if (c == '/') + break; // end of comment + // not end of comment + } else // end of input + break; + } while (true); + + return true; + } else { + // it's not a comment, put the '/' back + unget(); + + return false; + } +} + +// skip whitespace, then skip a comment, rinse, repeat +void TInputScanner::consumeWhitespaceComment(bool& foundNonSpaceTab) +{ + do { + consumeWhiteSpace(foundNonSpaceTab); + + // if not starting a comment now, then done + int c = peek(); + if (c != '/' || c == EndOfInput) + return; + + // skip potential comment + foundNonSpaceTab = true; + if (! consumeComment()) + return; + + } while (true); +} + +// Returns true if there was non-white space (e.g., a comment, newline) before the #version +// or no #version was found; otherwise, returns false. There is no error case, it always +// succeeds, but will leave version == 0 if no #version was found. +// +// Sets notFirstToken based on whether tokens (beyond white space and comments) +// appeared before the #version. +// +// N.B. does not attempt to leave input in any particular known state. The assumption +// is that scanning will start anew, following the rules for the chosen version/profile, +// and with a corresponding parsing context. +// +bool TInputScanner::scanVersion(int& version, EProfile& profile, bool& notFirstToken) +{ + // This function doesn't have to get all the semantics correct, + // just find the #version if there is a correct one present. + // The preprocessor will have the responsibility of getting all the semantics right. + + bool versionNotFirst = false; // means not first WRT comments and white space, nothing more + notFirstToken = false; // means not first WRT to real tokens + version = 0; // means not found + profile = ENoProfile; + + bool foundNonSpaceTab = false; + bool lookingInMiddle = false; + int c; + do { + if (lookingInMiddle) { + notFirstToken = true; + // make forward progress by finishing off the current line plus extra new lines + if (peek() != '\n' && peek() != '\r') { + do { + c = get(); + } while (c != EndOfInput && c != '\n' && c != '\r'); + } + while (peek() == '\n' || peek() == '\r') + get(); + if (peek() == EndOfInput) + return true; + } + lookingInMiddle = true; + + // Nominal start, skipping the desktop allowed comments and white space, but tracking if + // something else was found for ES: + consumeWhitespaceComment(foundNonSpaceTab); + if (foundNonSpaceTab) + versionNotFirst = true; + + // "#" + if (get() != '#') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // "version" + if ( c != 'v' || + get() != 'e' || + get() != 'r' || + get() != 's' || + get() != 'i' || + get() != 'o' || + get() != 'n') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // version number + while (c >= '0' && c <= '9') { + version = 10 * version + (c - '0'); + c = get(); + } + if (version == 0) { + versionNotFirst = true; + continue; + } + + // whitespace + while (c == ' ' || c == '\t') + c = get(); + + // profile + const int maxProfileLength = 13; // not including any 0 + char profileString[maxProfileLength]; + int profileLength; + for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { + if (c == EndOfInput || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + profileString[profileLength] = (char)c; + c = get(); + } + if (c != EndOfInput && c != ' ' && c != '\t' && c != '\n' && c != '\r') { + versionNotFirst = true; + continue; + } + + if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) + profile = EEsProfile; + else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) + profile = ECoreProfile; + else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) + profile = ECompatibilityProfile; + + return versionNotFirst; + } while (true); +} + +// Fill this in when doing glslang-level scanning, to hand back to the parser. +class TParserToken { +public: + explicit TParserToken(YYSTYPE& b) : sType(b) { } + + YYSTYPE& sType; +protected: + TParserToken(TParserToken&); + TParserToken& operator=(TParserToken&); +}; + +} // end namespace glslang + +// This is the function the glslang parser (i.e., bison) calls to get its next token +int yylex(YYSTYPE* glslangTokenDesc, glslang::TParseContext& parseContext) +{ + glslang::TParserToken token(*glslangTokenDesc); + + return parseContext.getScanContext()->tokenize(parseContext.getPpContext(), token); +} + +namespace { + +struct str_eq +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + +struct str_hash +{ + size_t operator()(const char* str) const + { + // djb2 + unsigned long hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash; + } +}; + +// A single global usable by all threads, by all versions, by all languages. +const std::unordered_map KeywordMap { + {"const",CONST}, + {"uniform",UNIFORM}, + {"tileImageEXT",TILEIMAGEEXT}, + {"buffer",BUFFER}, + {"in",IN}, + {"out",OUT}, + {"smooth",SMOOTH}, + {"flat",FLAT}, + {"centroid",CENTROID}, + {"invariant",INVARIANT}, + {"packed",PACKED}, + {"resource",RESOURCE}, + {"inout",INOUT}, + {"struct",STRUCT}, + {"break",BREAK}, + {"continue",CONTINUE}, + {"do",DO}, + {"for",FOR}, + {"while",WHILE}, + {"switch",SWITCH}, + {"case",CASE}, + {"default",DEFAULT}, + {"if",IF}, + {"else",ELSE}, + {"discard",DISCARD}, + {"terminateInvocation",TERMINATE_INVOCATION}, + {"terminateRayEXT",TERMINATE_RAY}, + {"ignoreIntersectionEXT",IGNORE_INTERSECTION}, + {"return",RETURN}, + {"void",VOID}, + {"bool",BOOL}, + {"float",FLOAT}, + {"int",INT}, + {"bvec2",BVEC2}, + {"bvec3",BVEC3}, + {"bvec4",BVEC4}, + {"vec2",VEC2}, + {"vec3",VEC3}, + {"vec4",VEC4}, + {"ivec2",IVEC2}, + {"ivec3",IVEC3}, + {"ivec4",IVEC4}, + {"mat2",MAT2}, + {"mat3",MAT3}, + {"mat4",MAT4}, + {"true",BOOLCONSTANT}, + {"false",BOOLCONSTANT}, + {"layout",LAYOUT}, + {"shared",SHARED}, + {"highp",HIGH_PRECISION}, + {"mediump",MEDIUM_PRECISION}, + {"lowp",LOW_PRECISION}, + {"superp",SUPERP}, + {"precision",PRECISION}, + {"mat2x2",MAT2X2}, + {"mat2x3",MAT2X3}, + {"mat2x4",MAT2X4}, + {"mat3x2",MAT3X2}, + {"mat3x3",MAT3X3}, + {"mat3x4",MAT3X4}, + {"mat4x2",MAT4X2}, + {"mat4x3",MAT4X3}, + {"mat4x4",MAT4X4}, + {"uint",UINT}, + {"uvec2",UVEC2}, + {"uvec3",UVEC3}, + {"uvec4",UVEC4}, + + {"nonuniformEXT",NONUNIFORM}, + {"demote",DEMOTE}, + {"attribute",ATTRIBUTE}, + {"varying",VARYING}, + {"noperspective",NOPERSPECTIVE}, + {"coherent",COHERENT}, + {"devicecoherent",DEVICECOHERENT}, + {"queuefamilycoherent",QUEUEFAMILYCOHERENT}, + {"workgroupcoherent",WORKGROUPCOHERENT}, + {"subgroupcoherent",SUBGROUPCOHERENT}, + {"shadercallcoherent",SHADERCALLCOHERENT}, + {"nonprivate",NONPRIVATE}, + {"restrict",RESTRICT}, + {"readonly",READONLY}, + {"writeonly",WRITEONLY}, + {"atomic_uint",ATOMIC_UINT}, + {"volatile",VOLATILE}, + {"nontemporal",NONTEMPORAL}, + {"patch",PATCH}, + {"sample",SAMPLE}, + {"subroutine",SUBROUTINE}, + {"dmat2",DMAT2}, + {"dmat3",DMAT3}, + {"dmat4",DMAT4}, + {"dmat2x2",DMAT2X2}, + {"dmat2x3",DMAT2X3}, + {"dmat2x4",DMAT2X4}, + {"dmat3x2",DMAT3X2}, + {"dmat3x3",DMAT3X3}, + {"dmat3x4",DMAT3X4}, + {"dmat4x2",DMAT4X2}, + {"dmat4x3",DMAT4X3}, + {"dmat4x4",DMAT4X4}, + {"image1D",IMAGE1D}, + {"iimage1D",IIMAGE1D}, + {"uimage1D",UIMAGE1D}, + {"image2D",IMAGE2D}, + {"iimage2D",IIMAGE2D}, + {"uimage2D",UIMAGE2D}, + {"image3D",IMAGE3D}, + {"iimage3D",IIMAGE3D}, + {"uimage3D",UIMAGE3D}, + {"image2DRect",IMAGE2DRECT}, + {"iimage2DRect",IIMAGE2DRECT}, + {"uimage2DRect",UIMAGE2DRECT}, + {"imageCube",IMAGECUBE}, + {"iimageCube",IIMAGECUBE}, + {"uimageCube",UIMAGECUBE}, + {"imageBuffer",IMAGEBUFFER}, + {"iimageBuffer",IIMAGEBUFFER}, + {"uimageBuffer",UIMAGEBUFFER}, + {"image1DArray",IMAGE1DARRAY}, + {"iimage1DArray",IIMAGE1DARRAY}, + {"uimage1DArray",UIMAGE1DARRAY}, + {"image2DArray",IMAGE2DARRAY}, + {"iimage2DArray",IIMAGE2DARRAY}, + {"uimage2DArray",UIMAGE2DARRAY}, + {"imageCubeArray",IMAGECUBEARRAY}, + {"iimageCubeArray",IIMAGECUBEARRAY}, + {"uimageCubeArray",UIMAGECUBEARRAY}, + {"image2DMS",IMAGE2DMS}, + {"iimage2DMS",IIMAGE2DMS}, + {"uimage2DMS",UIMAGE2DMS}, + {"image2DMSArray",IMAGE2DMSARRAY}, + {"iimage2DMSArray",IIMAGE2DMSARRAY}, + {"uimage2DMSArray",UIMAGE2DMSARRAY}, + {"i64image1D",I64IMAGE1D}, + {"u64image1D",U64IMAGE1D}, + {"i64image2D",I64IMAGE2D}, + {"u64image2D",U64IMAGE2D}, + {"i64image3D",I64IMAGE3D}, + {"u64image3D",U64IMAGE3D}, + {"i64image2DRect",I64IMAGE2DRECT}, + {"u64image2DRect",U64IMAGE2DRECT}, + {"i64imageCube",I64IMAGECUBE}, + {"u64imageCube",U64IMAGECUBE}, + {"i64imageBuffer",I64IMAGEBUFFER}, + {"u64imageBuffer",U64IMAGEBUFFER}, + {"i64image1DArray",I64IMAGE1DARRAY}, + {"u64image1DArray",U64IMAGE1DARRAY}, + {"i64image2DArray",I64IMAGE2DARRAY}, + {"u64image2DArray",U64IMAGE2DARRAY}, + {"i64imageCubeArray",I64IMAGECUBEARRAY}, + {"u64imageCubeArray",U64IMAGECUBEARRAY}, + {"i64image2DMS",I64IMAGE2DMS}, + {"u64image2DMS",U64IMAGE2DMS}, + {"i64image2DMSArray",I64IMAGE2DMSARRAY}, + {"u64image2DMSArray",U64IMAGE2DMSARRAY}, + {"double",DOUBLE}, + {"dvec2",DVEC2}, + {"dvec3",DVEC3}, + {"dvec4",DVEC4}, + {"int64_t",INT64_T}, + {"uint64_t",UINT64_T}, + {"i64vec2",I64VEC2}, + {"i64vec3",I64VEC3}, + {"i64vec4",I64VEC4}, + {"u64vec2",U64VEC2}, + {"u64vec3",U64VEC3}, + {"u64vec4",U64VEC4}, + + // GL_EXT_shader_explicit_arithmetic_types + {"int8_t",INT8_T}, + {"i8vec2",I8VEC2}, + {"i8vec3",I8VEC3}, + {"i8vec4",I8VEC4}, + {"uint8_t",UINT8_T}, + {"u8vec2",U8VEC2}, + {"u8vec3",U8VEC3}, + {"u8vec4",U8VEC4}, + + {"int16_t",INT16_T}, + {"i16vec2",I16VEC2}, + {"i16vec3",I16VEC3}, + {"i16vec4",I16VEC4}, + {"uint16_t",UINT16_T}, + {"u16vec2",U16VEC2}, + {"u16vec3",U16VEC3}, + {"u16vec4",U16VEC4}, + + {"int32_t",INT32_T}, + {"i32vec2",I32VEC2}, + {"i32vec3",I32VEC3}, + {"i32vec4",I32VEC4}, + {"uint32_t",UINT32_T}, + {"u32vec2",U32VEC2}, + {"u32vec3",U32VEC3}, + {"u32vec4",U32VEC4}, + + {"float16_t",FLOAT16_T}, + {"f16vec2",F16VEC2}, + {"f16vec3",F16VEC3}, + {"f16vec4",F16VEC4}, + {"f16mat2",F16MAT2}, + {"f16mat3",F16MAT3}, + {"f16mat4",F16MAT4}, + {"f16mat2x2",F16MAT2X2}, + {"f16mat2x3",F16MAT2X3}, + {"f16mat2x4",F16MAT2X4}, + {"f16mat3x2",F16MAT3X2}, + {"f16mat3x3",F16MAT3X3}, + {"f16mat3x4",F16MAT3X4}, + {"f16mat4x2",F16MAT4X2}, + {"f16mat4x3",F16MAT4X3}, + {"f16mat4x4",F16MAT4X4}, + + {"bfloat16_t",BFLOAT16_T}, + {"bf16vec2",BF16VEC2}, + {"bf16vec3",BF16VEC3}, + {"bf16vec4",BF16VEC4}, + + {"float32_t",FLOAT32_T}, + {"f32vec2",F32VEC2}, + {"f32vec3",F32VEC3}, + {"f32vec4",F32VEC4}, + {"f32mat2",F32MAT2}, + {"f32mat3",F32MAT3}, + {"f32mat4",F32MAT4}, + {"f32mat2x2",F32MAT2X2}, + {"f32mat2x3",F32MAT2X3}, + {"f32mat2x4",F32MAT2X4}, + {"f32mat3x2",F32MAT3X2}, + {"f32mat3x3",F32MAT3X3}, + {"f32mat3x4",F32MAT3X4}, + {"f32mat4x2",F32MAT4X2}, + {"f32mat4x3",F32MAT4X3}, + {"f32mat4x4",F32MAT4X4}, + {"float64_t",FLOAT64_T}, + {"f64vec2",F64VEC2}, + {"f64vec3",F64VEC3}, + {"f64vec4",F64VEC4}, + {"f64mat2",F64MAT2}, + {"f64mat3",F64MAT3}, + {"f64mat4",F64MAT4}, + {"f64mat2x2",F64MAT2X2}, + {"f64mat2x3",F64MAT2X3}, + {"f64mat2x4",F64MAT2X4}, + {"f64mat3x2",F64MAT3X2}, + {"f64mat3x3",F64MAT3X3}, + {"f64mat3x4",F64MAT3X4}, + {"f64mat4x2",F64MAT4X2}, + {"f64mat4x3",F64MAT4X3}, + {"f64mat4x4",F64MAT4X4}, + + // GL_EXT_spirv_intrinsics + {"spirv_instruction",SPIRV_INSTRUCTION}, + {"spirv_execution_mode",SPIRV_EXECUTION_MODE}, + {"spirv_execution_mode_id",SPIRV_EXECUTION_MODE_ID}, + {"spirv_decorate",SPIRV_DECORATE}, + {"spirv_decorate_id",SPIRV_DECORATE_ID}, + {"spirv_decorate_string",SPIRV_DECORATE_STRING}, + {"spirv_type",SPIRV_TYPE}, + {"spirv_storage_class",SPIRV_STORAGE_CLASS}, + {"spirv_by_reference",SPIRV_BY_REFERENCE}, + {"spirv_literal",SPIRV_LITERAL}, + + {"sampler2D",SAMPLER2D}, + {"samplerCube",SAMPLERCUBE}, + {"samplerCubeShadow",SAMPLERCUBESHADOW}, + {"sampler2DArray",SAMPLER2DARRAY}, + {"sampler2DArrayShadow",SAMPLER2DARRAYSHADOW}, + {"isampler2D",ISAMPLER2D}, + {"isampler3D",ISAMPLER3D}, + {"isamplerCube",ISAMPLERCUBE}, + {"isampler2DArray",ISAMPLER2DARRAY}, + {"usampler2D",USAMPLER2D}, + {"usampler3D",USAMPLER3D}, + {"usamplerCube",USAMPLERCUBE}, + {"usampler2DArray",USAMPLER2DARRAY}, + {"sampler3D",SAMPLER3D}, + {"sampler2DShadow",SAMPLER2DSHADOW}, + + {"texture2D",TEXTURE2D}, + {"textureCube",TEXTURECUBE}, + {"texture2DArray",TEXTURE2DARRAY}, + {"itexture2D",ITEXTURE2D}, + {"itexture3D",ITEXTURE3D}, + {"itextureCube",ITEXTURECUBE}, + {"itexture2DArray",ITEXTURE2DARRAY}, + {"utexture2D",UTEXTURE2D}, + {"utexture3D",UTEXTURE3D}, + {"utextureCube",UTEXTURECUBE}, + {"utexture2DArray",UTEXTURE2DARRAY}, + {"texture3D",TEXTURE3D}, + + {"sampler",SAMPLER}, + {"samplerShadow",SAMPLERSHADOW}, + + {"textureCubeArray",TEXTURECUBEARRAY}, + {"itextureCubeArray",ITEXTURECUBEARRAY}, + {"utextureCubeArray",UTEXTURECUBEARRAY}, + {"samplerCubeArray",SAMPLERCUBEARRAY}, + {"samplerCubeArrayShadow",SAMPLERCUBEARRAYSHADOW}, + {"isamplerCubeArray",ISAMPLERCUBEARRAY}, + {"usamplerCubeArray",USAMPLERCUBEARRAY}, + {"sampler1DArrayShadow",SAMPLER1DARRAYSHADOW}, + {"isampler1DArray",ISAMPLER1DARRAY}, + {"usampler1D",USAMPLER1D}, + {"isampler1D",ISAMPLER1D}, + {"usampler1DArray",USAMPLER1DARRAY}, + {"samplerBuffer",SAMPLERBUFFER}, + {"isampler2DRect",ISAMPLER2DRECT}, + {"usampler2DRect",USAMPLER2DRECT}, + {"isamplerBuffer",ISAMPLERBUFFER}, + {"usamplerBuffer",USAMPLERBUFFER}, + {"sampler2DMS",SAMPLER2DMS}, + {"isampler2DMS",ISAMPLER2DMS}, + {"usampler2DMS",USAMPLER2DMS}, + {"sampler2DMSArray",SAMPLER2DMSARRAY}, + {"isampler2DMSArray",ISAMPLER2DMSARRAY}, + {"usampler2DMSArray",USAMPLER2DMSARRAY}, + {"sampler1D",SAMPLER1D}, + {"sampler1DShadow",SAMPLER1DSHADOW}, + {"sampler2DRect",SAMPLER2DRECT}, + {"sampler2DRectShadow",SAMPLER2DRECTSHADOW}, + {"sampler1DArray",SAMPLER1DARRAY}, + + {"samplerExternalOES", SAMPLEREXTERNALOES}, // GL_OES_EGL_image_external + {"__samplerExternal2DY2YEXT", SAMPLEREXTERNAL2DY2YEXT}, // GL_EXT_YUV_target + + {"itexture1DArray",ITEXTURE1DARRAY}, + {"utexture1D",UTEXTURE1D}, + {"itexture1D",ITEXTURE1D}, + {"utexture1DArray",UTEXTURE1DARRAY}, + {"textureBuffer",TEXTUREBUFFER}, + {"itexture2DRect",ITEXTURE2DRECT}, + {"utexture2DRect",UTEXTURE2DRECT}, + {"itextureBuffer",ITEXTUREBUFFER}, + {"utextureBuffer",UTEXTUREBUFFER}, + {"texture2DMS",TEXTURE2DMS}, + {"itexture2DMS",ITEXTURE2DMS}, + {"utexture2DMS",UTEXTURE2DMS}, + {"texture2DMSArray",TEXTURE2DMSARRAY}, + {"itexture2DMSArray",ITEXTURE2DMSARRAY}, + {"utexture2DMSArray",UTEXTURE2DMSARRAY}, + {"texture1D",TEXTURE1D}, + {"texture2DRect",TEXTURE2DRECT}, + {"texture1DArray",TEXTURE1DARRAY}, + + {"attachmentEXT",ATTACHMENTEXT}, + {"iattachmentEXT",IATTACHMENTEXT}, + {"uattachmentEXT",UATTACHMENTEXT}, + + {"subpassInput",SUBPASSINPUT}, + {"subpassInputMS",SUBPASSINPUTMS}, + {"isubpassInput",ISUBPASSINPUT}, + {"isubpassInputMS",ISUBPASSINPUTMS}, + {"usubpassInput",USUBPASSINPUT}, + {"usubpassInputMS",USUBPASSINPUTMS}, + + {"f16sampler1D",F16SAMPLER1D}, + {"f16sampler2D",F16SAMPLER2D}, + {"f16sampler3D",F16SAMPLER3D}, + {"f16sampler2DRect",F16SAMPLER2DRECT}, + {"f16samplerCube",F16SAMPLERCUBE}, + {"f16sampler1DArray",F16SAMPLER1DARRAY}, + {"f16sampler2DArray",F16SAMPLER2DARRAY}, + {"f16samplerCubeArray",F16SAMPLERCUBEARRAY}, + {"f16samplerBuffer",F16SAMPLERBUFFER}, + {"f16sampler2DMS",F16SAMPLER2DMS}, + {"f16sampler2DMSArray",F16SAMPLER2DMSARRAY}, + {"f16sampler1DShadow",F16SAMPLER1DSHADOW}, + {"f16sampler2DShadow",F16SAMPLER2DSHADOW}, + {"f16sampler2DRectShadow",F16SAMPLER2DRECTSHADOW}, + {"f16samplerCubeShadow",F16SAMPLERCUBESHADOW}, + {"f16sampler1DArrayShadow",F16SAMPLER1DARRAYSHADOW}, + {"f16sampler2DArrayShadow",F16SAMPLER2DARRAYSHADOW}, + {"f16samplerCubeArrayShadow",F16SAMPLERCUBEARRAYSHADOW}, + + {"f16image1D",F16IMAGE1D}, + {"f16image2D",F16IMAGE2D}, + {"f16image3D",F16IMAGE3D}, + {"f16image2DRect",F16IMAGE2DRECT}, + {"f16imageCube",F16IMAGECUBE}, + {"f16image1DArray",F16IMAGE1DARRAY}, + {"f16image2DArray",F16IMAGE2DARRAY}, + {"f16imageCubeArray",F16IMAGECUBEARRAY}, + {"f16imageBuffer",F16IMAGEBUFFER}, + {"f16image2DMS",F16IMAGE2DMS}, + {"f16image2DMSArray",F16IMAGE2DMSARRAY}, + + {"f16texture1D",F16TEXTURE1D}, + {"f16texture2D",F16TEXTURE2D}, + {"f16texture3D",F16TEXTURE3D}, + {"f16texture2DRect",F16TEXTURE2DRECT}, + {"f16textureCube",F16TEXTURECUBE}, + {"f16texture1DArray",F16TEXTURE1DARRAY}, + {"f16texture2DArray",F16TEXTURE2DARRAY}, + {"f16textureCubeArray",F16TEXTURECUBEARRAY}, + {"f16textureBuffer",F16TEXTUREBUFFER}, + {"f16texture2DMS",F16TEXTURE2DMS}, + {"f16texture2DMSArray",F16TEXTURE2DMSARRAY}, + + {"f16subpassInput",F16SUBPASSINPUT}, + {"f16subpassInputMS",F16SUBPASSINPUTMS}, + {"__explicitInterpAMD",EXPLICITINTERPAMD}, + {"pervertexNV",PERVERTEXNV}, + {"pervertexEXT",PERVERTEXEXT}, + {"precise",PRECISE}, + + {"rayPayloadNV",PAYLOADNV}, + {"rayPayloadEXT",PAYLOADEXT}, + {"rayPayloadInNV",PAYLOADINNV}, + {"rayPayloadInEXT",PAYLOADINEXT}, + {"hitAttributeNV",HITATTRNV}, + {"hitAttributeEXT",HITATTREXT}, + {"callableDataNV",CALLDATANV}, + {"callableDataEXT",CALLDATAEXT}, + {"callableDataInNV",CALLDATAINNV}, + {"callableDataInEXT",CALLDATAINEXT}, + {"accelerationStructureNV",ACCSTRUCTNV}, + {"accelerationStructureEXT",ACCSTRUCTEXT}, + {"rayQueryEXT",RAYQUERYEXT}, + {"perprimitiveNV",PERPRIMITIVENV}, + {"perviewNV",PERVIEWNV}, + {"taskNV",PERTASKNV}, + {"perprimitiveEXT",PERPRIMITIVEEXT}, + {"taskPayloadSharedEXT",TASKPAYLOADWORKGROUPEXT}, + + {"fcoopmatNV",FCOOPMATNV}, + {"icoopmatNV",ICOOPMATNV}, + {"ucoopmatNV",UCOOPMATNV}, + + {"coopmat",COOPMAT}, + + {"hitObjectNV",HITOBJECTNV}, + {"hitObjectAttributeNV",HITOBJECTATTRNV}, + + {"__function",FUNCTION}, + {"tensorLayoutNV",TENSORLAYOUTNV}, + {"tensorViewNV",TENSORVIEWNV}, + + {"coopvecNV",COOPVECNV}, +}; +const std::unordered_set ReservedSet { + "common", + "partition", + "active", + "asm", + "class", + "union", + "enum", + "typedef", + "template", + "this", + "goto", + "inline", + "noinline", + "public", + "static", + "extern", + "external", + "interface", + "long", + "short", + "half", + "fixed", + "unsigned", + "input", + "output", + "hvec2", + "hvec3", + "hvec4", + "fvec2", + "fvec3", + "fvec4", + "sampler3DRect", + "filter", + "sizeof", + "cast", + "namespace", + "using", +}; + +} + +namespace glslang { + +// Called by yylex to get the next token. +// Returning 0 implies end of input. +int TScanContext::tokenize(TPpContext* pp, TParserToken& token) +{ + do { + parserToken = &token; + TPpToken ppToken; + int token = pp->tokenize(ppToken); + if (token == EndOfInput) + return 0; + + tokenText = ppToken.name; + loc = ppToken.loc; + parserToken->sType.lex.loc = loc; + switch (token) { + case ';': afterType = false; afterBuffer = false; return SEMICOLON; + case ',': afterType = false; return COMMA; + case ':': return COLON; + case '=': afterType = false; return EQUAL; + case '(': afterType = false; return LEFT_PAREN; + case ')': afterType = false; return RIGHT_PAREN; + case '.': field = true; return DOT; + case '!': return BANG; + case '-': return DASH; + case '~': return TILDE; + case '+': return PLUS; + case '*': return STAR; + case '/': return SLASH; + case '%': return PERCENT; + case '<': return LEFT_ANGLE; + case '>': return RIGHT_ANGLE; + case '|': return VERTICAL_BAR; + case '^': return CARET; + case '&': return AMPERSAND; + case '?': return QUESTION; + case '[': return LEFT_BRACKET; + case ']': return RIGHT_BRACKET; + case '{': afterStruct = false; afterBuffer = false; return LEFT_BRACE; + case '}': return RIGHT_BRACE; + case '\\': + parseContext.error(loc, "illegal use of escape character", "\\", ""); + break; + + case PPAtomAddAssign: return ADD_ASSIGN; + case PPAtomSubAssign: return SUB_ASSIGN; + case PPAtomMulAssign: return MUL_ASSIGN; + case PPAtomDivAssign: return DIV_ASSIGN; + case PPAtomModAssign: return MOD_ASSIGN; + + case PpAtomRight: return RIGHT_OP; + case PpAtomLeft: return LEFT_OP; + + case PpAtomRightAssign: return RIGHT_ASSIGN; + case PpAtomLeftAssign: return LEFT_ASSIGN; + case PpAtomAndAssign: return AND_ASSIGN; + case PpAtomOrAssign: return OR_ASSIGN; + case PpAtomXorAssign: return XOR_ASSIGN; + + case PpAtomAnd: return AND_OP; + case PpAtomOr: return OR_OP; + case PpAtomXor: return XOR_OP; + + case PpAtomEQ: return EQ_OP; + case PpAtomGE: return GE_OP; + case PpAtomNE: return NE_OP; + case PpAtomLE: return LE_OP; + + case PpAtomDecrement: return DEC_OP; + case PpAtomIncrement: return INC_OP; + + case PpAtomColonColon: + parseContext.error(loc, "not supported", "::", ""); + break; + + case PpAtomConstString: parserToken->sType.lex.string = NewPoolTString(tokenText); return STRING_LITERAL; + case PpAtomConstInt: parserToken->sType.lex.i = ppToken.ival; return INTCONSTANT; + case PpAtomConstUint: parserToken->sType.lex.i = ppToken.ival; return UINTCONSTANT; + case PpAtomConstFloat: parserToken->sType.lex.d = ppToken.dval; return FLOATCONSTANT; + case PpAtomConstInt16: parserToken->sType.lex.i = ppToken.ival; return INT16CONSTANT; + case PpAtomConstUint16: parserToken->sType.lex.i = ppToken.ival; return UINT16CONSTANT; + case PpAtomConstInt64: parserToken->sType.lex.i64 = ppToken.i64val; return INT64CONSTANT; + case PpAtomConstUint64: parserToken->sType.lex.i64 = ppToken.i64val; return UINT64CONSTANT; + case PpAtomConstDouble: parserToken->sType.lex.d = ppToken.dval; return DOUBLECONSTANT; + case PpAtomConstFloat16: parserToken->sType.lex.d = ppToken.dval; return FLOAT16CONSTANT; + case PpAtomIdentifier: + { + int token = tokenizeIdentifier(); + field = false; + return token; + } + + case EndOfInput: return 0; + + default: + char buf[2]; + buf[0] = (char)token; + buf[1] = 0; + parseContext.error(loc, "unexpected token", buf, ""); + break; + } + } while (true); +} + +int TScanContext::tokenizeIdentifier() +{ + if (ReservedSet.find(tokenText) != ReservedSet.end()) + return reservedWord(); + + auto it = KeywordMap.find(tokenText); + if (it == KeywordMap.end()) { + // Should have an identifier of some sort + return identifierOrType(); + } + keyword = it->second; + + switch (keyword) { + case CONST: + case UNIFORM: + case TILEIMAGEEXT: + case IN: + case OUT: + case INOUT: + case BREAK: + case CONTINUE: + case DO: + case FOR: + case WHILE: + case IF: + case ELSE: + case DISCARD: + case RETURN: + case CASE: + return keyword; + + case TERMINATE_INVOCATION: + if (!parseContext.extensionTurnedOn(E_GL_EXT_terminate_invocation)) + return identifierOrType(); + return keyword; + + case TERMINATE_RAY: + case IGNORE_INTERSECTION: + if (!parseContext.extensionTurnedOn(E_GL_EXT_ray_tracing)) + return identifierOrType(); + return keyword; + + case BUFFER: + afterBuffer = true; + if ((parseContext.isEsProfile() && parseContext.version < 310) || + (!parseContext.isEsProfile() && (parseContext.version < 430 && + !parseContext.extensionTurnedOn(E_GL_ARB_shader_storage_buffer_object)))) + return identifierOrType(); + return keyword; + + case STRUCT: + afterStruct = true; + return keyword; + + case SWITCH: + case DEFAULT: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + reservedWord(); + return keyword; + + case VOID: + case BOOL: + case FLOAT: + case INT: + case BVEC2: + case BVEC3: + case BVEC4: + case VEC2: + case VEC3: + case VEC4: + case IVEC2: + case IVEC3: + case IVEC4: + case MAT2: + case MAT3: + case MAT4: + case SAMPLER2D: + case SAMPLERCUBE: + afterType = true; + return keyword; + + case BOOLCONSTANT: + if (strcmp("true", tokenText) == 0) + parserToken->sType.lex.b = true; + else + parserToken->sType.lex.b = false; + return keyword; + + case SMOOTH: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + return identifierOrType(); + return keyword; + case FLAT: + if (parseContext.isEsProfile() && parseContext.version < 300) + reservedWord(); + else if (!parseContext.isEsProfile() && parseContext.version < 130) + return identifierOrType(); + return keyword; + case CENTROID: + if (parseContext.version < 120) + return identifierOrType(); + return keyword; + case INVARIANT: + if (!parseContext.isEsProfile() && parseContext.version < 120) + return identifierOrType(); + return keyword; + case PACKED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140)) + return reservedWord(); + return identifierOrType(); + + case RESOURCE: + { + bool reserved = (parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 420); + return identifierOrReserved(reserved); + } + case SUPERP: + { + bool reserved = parseContext.isEsProfile() || parseContext.version >= 130; + return identifierOrReserved(reserved); + } + + case NOPERSPECTIVE: + if (parseContext.extensionTurnedOn(E_GL_NV_shader_noperspective_interpolation)) + return keyword; + return es30ReservedFromGLSL(130); + + case NONUNIFORM: + if (parseContext.extensionTurnedOn(E_GL_EXT_nonuniform_qualifier)) + return keyword; + else + return identifierOrType(); + case ATTRIBUTE: + case VARYING: + if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + return keyword; + case PAYLOADNV: + case PAYLOADINNV: + case HITATTRNV: + case CALLDATANV: + case CALLDATAINNV: + case ACCSTRUCTNV: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_ray_tracing)) + return keyword; + return identifierOrType(); + case ACCSTRUCTEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_ray_tracing) || + parseContext.extensionTurnedOn(E_GL_EXT_ray_query) || + parseContext.extensionTurnedOn(E_GL_NV_displacement_micromap)) + return keyword; + return identifierOrType(); + case PAYLOADEXT: + case PAYLOADINEXT: + case HITATTREXT: + case CALLDATAEXT: + case CALLDATAINEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_ray_tracing) || + parseContext.extensionTurnedOn(E_GL_EXT_ray_query)) + return keyword; + return identifierOrType(); + case RAYQUERYEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && parseContext.version >= 460 + && parseContext.extensionTurnedOn(E_GL_EXT_ray_query))) + return keyword; + return identifierOrType(); + case ATOMIC_UINT: + if ((parseContext.isEsProfile() && parseContext.version >= 310) || + parseContext.extensionTurnedOn(E_GL_ARB_shader_atomic_counters)) + return keyword; + return es30ReservedFromGLSL(420); + + case COHERENT: + case DEVICECOHERENT: + case QUEUEFAMILYCOHERENT: + case WORKGROUPCOHERENT: + case SUBGROUPCOHERENT: + case SHADERCALLCOHERENT: + case NONPRIVATE: + case RESTRICT: + case READONLY: + case WRITEONLY: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + return es30ReservedFromGLSL(parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store) ? 130 : 420); + case VOLATILE: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (! parseContext.symbolTable.atBuiltInLevel() && (parseContext.isEsProfile() || + (parseContext.version < 420 && ! parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + reservedWord(); + return keyword; + case NONTEMPORAL: + if (parseContext.symbolTable.atBuiltInLevel()) + return keyword; + if (parseContext.extensionTurnedOn(E_GL_EXT_nontemporal_keyword)) { + if (!parseContext.intermediate.usingVulkanMemoryModel()) + parseContext.warn(loc, "Nontemporal without the Vulkan Memory Model is ignored", tokenText, ""); + return keyword; + } + return identifierOrType(); + case PATCH: + if (parseContext.symbolTable.atBuiltInLevel() || + (parseContext.isEsProfile() && + (parseContext.version >= 320 || + parseContext.extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))) || + (!parseContext.isEsProfile() && parseContext.extensionTurnedOn(E_GL_ARB_tessellation_shader))) + return keyword; + + return es30ReservedFromGLSL(400); + + case SAMPLE: + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_shader_multisample_interpolation)) + return keyword; + return es30ReservedFromGLSL(400); + + case SUBROUTINE: + return es30ReservedFromGLSL(400); + + case SHARED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140)) + return identifierOrType(); + return keyword; + case LAYOUT: + { + const int numLayoutExts = 2; + const char* layoutExts[numLayoutExts] = { E_GL_ARB_shading_language_420pack, + E_GL_ARB_explicit_attrib_location }; + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140 && + ! parseContext.extensionsTurnedOn(numLayoutExts, layoutExts))) + return identifierOrType(); + return keyword; + } + + case HIGH_PRECISION: + case MEDIUM_PRECISION: + case LOW_PRECISION: + case PRECISION: + return precisionKeyword(); + + case MAT2X2: + case MAT2X3: + case MAT2X4: + case MAT3X2: + case MAT3X3: + case MAT3X4: + case MAT4X2: + case MAT4X3: + case MAT4X4: + return matNxM(); + + case DMAT2: + case DMAT3: + case DMAT4: + case DMAT2X2: + case DMAT2X3: + case DMAT2X4: + case DMAT3X2: + case DMAT3X3: + case DMAT3X4: + case DMAT4X2: + case DMAT4X3: + case DMAT4X4: + return dMat(); + + case IMAGE1D: + case IIMAGE1D: + case UIMAGE1D: + case IMAGE1DARRAY: + case IIMAGE1DARRAY: + case UIMAGE1DARRAY: + case IMAGE2DRECT: + case IIMAGE2DRECT: + case UIMAGE2DRECT: + afterType = true; + return firstGenerationImage(false); + + case I64IMAGE1D: + case U64IMAGE1D: + case I64IMAGE1DARRAY: + case U64IMAGE1DARRAY: + case I64IMAGE2DRECT: + case U64IMAGE2DRECT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + return firstGenerationImage(false); + } + return identifierOrType(); + + case IMAGEBUFFER: + case IIMAGEBUFFER: + case UIMAGEBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return firstGenerationImage(false); + + case I64IMAGEBUFFER: + case U64IMAGEBUFFER: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return firstGenerationImage(false); + } + return identifierOrType(); + + case IMAGE2D: + case IIMAGE2D: + case UIMAGE2D: + case IMAGE3D: + case IIMAGE3D: + case UIMAGE3D: + case IMAGECUBE: + case IIMAGECUBE: + case UIMAGECUBE: + case IMAGE2DARRAY: + case IIMAGE2DARRAY: + case UIMAGE2DARRAY: + afterType = true; + return firstGenerationImage(true); + + case I64IMAGE2D: + case U64IMAGE2D: + case I64IMAGE3D: + case U64IMAGE3D: + case I64IMAGECUBE: + case U64IMAGECUBE: + case I64IMAGE2DARRAY: + case U64IMAGE2DARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) + return firstGenerationImage(true); + return identifierOrType(); + + case IMAGECUBEARRAY: + case IIMAGECUBEARRAY: + case UIMAGECUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + return secondGenerationImage(); + + case I64IMAGECUBEARRAY: + case U64IMAGECUBEARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + return secondGenerationImage(); + } + return identifierOrType(); + + case IMAGE2DMS: + case IIMAGE2DMS: + case UIMAGE2DMS: + case IMAGE2DMSARRAY: + case IIMAGE2DMSARRAY: + case UIMAGE2DMSARRAY: + afterType = true; + return secondGenerationImage(); + + case I64IMAGE2DMS: + case U64IMAGE2DMS: + case I64IMAGE2DMSARRAY: + case U64IMAGE2DMSARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + return secondGenerationImage(); + } + return identifierOrType(); + + case DOUBLE: + case DVEC2: + case DVEC3: + case DVEC4: + afterType = true; + if (parseContext.isEsProfile() || parseContext.version < 150 || + (!parseContext.symbolTable.atBuiltInLevel() && + (parseContext.version < 400 && !parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) && + (parseContext.version < 410 && !parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit))))) + reservedWord(); + return keyword; + + case INT64_T: + case UINT64_T: + case I64VEC2: + case I64VEC3: + case I64VEC4: + case U64VEC2: + case U64VEC3: + case U64VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_int64) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64)) + return keyword; + return identifierOrType(); + + case INT8_T: + case UINT8_T: + case I8VEC2: + case I8VEC3: + case I8VEC4: + case U8VEC2: + case U8VEC3: + case U8VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_8bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8)) + return keyword; + return identifierOrType(); + + case INT16_T: + case UINT16_T: + case I16VEC2: + case I16VEC3: + case I16VEC4: + case U16VEC2: + case U16VEC3: + case U16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_int16) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16)) + return keyword; + return identifierOrType(); + case INT32_T: + case UINT32_T: + case I32VEC2: + case I32VEC3: + case I32VEC4: + case U32VEC2: + case U32VEC3: + case U32VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32)) + return keyword; + return identifierOrType(); + case FLOAT32_T: + case F32VEC2: + case F32VEC3: + case F32VEC4: + case F32MAT2: + case F32MAT3: + case F32MAT4: + case F32MAT2X2: + case F32MAT2X3: + case F32MAT2X4: + case F32MAT3X2: + case F32MAT3X3: + case F32MAT3X4: + case F32MAT4X2: + case F32MAT4X3: + case F32MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32)) + return keyword; + return identifierOrType(); + + case FLOAT64_T: + case F64VEC2: + case F64VEC3: + case F64VEC4: + case F64MAT2: + case F64MAT3: + case F64MAT4: + case F64MAT2X2: + case F64MAT2X3: + case F64MAT2X4: + case F64MAT3X2: + case F64MAT3X3: + case F64MAT3X4: + case F64MAT4X2: + case F64MAT4X3: + case F64MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64)) + return keyword; + return identifierOrType(); + + case FLOAT16_T: + case F16VEC2: + case F16VEC3: + case F16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case F16MAT2: + case F16MAT3: + case F16MAT4: + case F16MAT2X2: + case F16MAT2X3: + case F16MAT2X4: + case F16MAT3X2: + case F16MAT3X3: + case F16MAT3X4: + case F16MAT4X2: + case F16MAT4X3: + case F16MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case BFLOAT16_T: + case BF16VEC2: + case BF16VEC3: + case BF16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_bfloat16)) + return keyword; + + return identifierOrType(); + + case SAMPLERCUBEARRAY: + case SAMPLERCUBEARRAYSHADOW: + case ISAMPLERCUBEARRAY: + case USAMPLERCUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + if (parseContext.isEsProfile() || (parseContext.version < 400 && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_cube_map_array))) + reservedWord(); + return keyword; + + case TEXTURECUBEARRAY: + case ITEXTURECUBEARRAY: + case UTEXTURECUBEARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case UINT: + case UVEC2: + case UVEC3: + case UVEC4: + case SAMPLERCUBESHADOW: + case SAMPLER2DARRAY: + case SAMPLER2DARRAYSHADOW: + case ISAMPLER2D: + case ISAMPLER3D: + case ISAMPLERCUBE: + case ISAMPLER2DARRAY: + case USAMPLER2D: + case USAMPLER3D: + case USAMPLERCUBE: + case USAMPLER2DARRAY: + afterType = true; + if (keyword == SAMPLER2DARRAY || keyword == SAMPLER2DARRAYSHADOW) { + if (!parseContext.isEsProfile() && + (parseContext.extensionTurnedOn(E_GL_EXT_texture_array) || parseContext.symbolTable.atBuiltInLevel())) { + return keyword; + } + } + return nonreservedKeyword(300, 130); + + case SAMPLER3D: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_OES_texture_3D)) + reservedWord(); + } + return keyword; + + case SAMPLER2DSHADOW: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_EXT_shadow_samplers)) + reservedWord(); + } + return keyword; + + case TEXTURE2D: + case TEXTURECUBE: + case TEXTURE2DARRAY: + case ITEXTURE2D: + case ITEXTURE3D: + case ITEXTURECUBE: + case ITEXTURE2DARRAY: + case UTEXTURE2D: + case UTEXTURE3D: + case UTEXTURECUBE: + case UTEXTURE2DARRAY: + case TEXTURE3D: + case SAMPLER: + case SAMPLERSHADOW: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case ISAMPLER1D: + case ISAMPLER1DARRAY: + case SAMPLER1DARRAYSHADOW: + case USAMPLER1D: + case USAMPLER1DARRAY: + afterType = true; + if (keyword == SAMPLER1DARRAYSHADOW) { + if (!parseContext.isEsProfile() && + (parseContext.extensionTurnedOn(E_GL_EXT_texture_array) || parseContext.symbolTable.atBuiltInLevel())) { + return keyword; + } + } + return es30ReservedFromGLSL(130); + case ISAMPLER2DRECT: + case USAMPLER2DRECT: + afterType = true; + return es30ReservedFromGLSL(140); + + case SAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(130); + + case ISAMPLERBUFFER: + case USAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(140); + + case SAMPLER2DMS: + case ISAMPLER2DMS: + case USAMPLER2DMS: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER2DMSARRAY: + case ISAMPLER2DMSARRAY: + case USAMPLER2DMSARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_texture_storage_multisample_2d_array)) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER1D: + case SAMPLER1DSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + return keyword; + + case SAMPLER2DRECT: + case SAMPLER2DRECTSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + else if (parseContext.version < 140 && ! parseContext.symbolTable.atBuiltInLevel() && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_rectangle)) { + if (parseContext.relaxedErrors()) + parseContext.requireExtensions(loc, 1, &E_GL_ARB_texture_rectangle, "texture-rectangle sampler keyword"); + else + reservedWord(); + } + return keyword; + + case SAMPLER1DARRAY: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version == 300) + reservedWord(); + else if ((parseContext.isEsProfile() && parseContext.version < 300) || + ((!parseContext.isEsProfile() && parseContext.version < 130) && + !parseContext.symbolTable.atBuiltInLevel() && + !parseContext.extensionTurnedOn(E_GL_EXT_texture_array))) + return identifierOrType(); + return keyword; + + case SAMPLEREXTERNALOES: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external) || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external_essl3)) + return keyword; + return identifierOrType(); + + case SAMPLEREXTERNAL2DY2YEXT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_YUV_target)) + return keyword; + return identifierOrType(); + + case ITEXTURE1DARRAY: + case UTEXTURE1D: + case ITEXTURE1D: + case UTEXTURE1DARRAY: + case TEXTUREBUFFER: + case ITEXTURE2DRECT: + case UTEXTURE2DRECT: + case ITEXTUREBUFFER: + case UTEXTUREBUFFER: + case TEXTURE2DMS: + case ITEXTURE2DMS: + case UTEXTURE2DMS: + case TEXTURE2DMSARRAY: + case ITEXTURE2DMSARRAY: + case UTEXTURE2DMSARRAY: + case TEXTURE1D: + case TEXTURE2DRECT: + case TEXTURE1DARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case SUBPASSINPUT: + case SUBPASSINPUTMS: + case ISUBPASSINPUT: + case ISUBPASSINPUTMS: + case USUBPASSINPUT: + case USUBPASSINPUTMS: + case ATTACHMENTEXT: + case IATTACHMENTEXT: + case UATTACHMENTEXT: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case F16SAMPLER1D: + case F16SAMPLER2D: + case F16SAMPLER3D: + case F16SAMPLER2DRECT: + case F16SAMPLERCUBE: + case F16SAMPLER1DARRAY: + case F16SAMPLER2DARRAY: + case F16SAMPLERCUBEARRAY: + case F16SAMPLERBUFFER: + case F16SAMPLER2DMS: + case F16SAMPLER2DMSARRAY: + case F16SAMPLER1DSHADOW: + case F16SAMPLER2DSHADOW: + case F16SAMPLER1DARRAYSHADOW: + case F16SAMPLER2DARRAYSHADOW: + case F16SAMPLER2DRECTSHADOW: + case F16SAMPLERCUBESHADOW: + case F16SAMPLERCUBEARRAYSHADOW: + + case F16IMAGE1D: + case F16IMAGE2D: + case F16IMAGE3D: + case F16IMAGE2DRECT: + case F16IMAGECUBE: + case F16IMAGE1DARRAY: + case F16IMAGE2DARRAY: + case F16IMAGECUBEARRAY: + case F16IMAGEBUFFER: + case F16IMAGE2DMS: + case F16IMAGE2DMSARRAY: + + case F16TEXTURE1D: + case F16TEXTURE2D: + case F16TEXTURE3D: + case F16TEXTURE2DRECT: + case F16TEXTURECUBE: + case F16TEXTURE1DARRAY: + case F16TEXTURE2DARRAY: + case F16TEXTURECUBEARRAY: + case F16TEXTUREBUFFER: + case F16TEXTURE2DMS: + case F16TEXTURE2DMSARRAY: + + case F16SUBPASSINPUT: + case F16SUBPASSINPUTMS: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float_fetch)) + return keyword; + return identifierOrType(); + + case EXPLICITINTERPAMD: + if (parseContext.extensionTurnedOn(E_GL_AMD_shader_explicit_vertex_parameter)) + return keyword; + return identifierOrType(); + + case PERVERTEXNV: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + parseContext.extensionTurnedOn(E_GL_NV_fragment_shader_barycentric)) + return keyword; + return identifierOrType(); + + case PERVERTEXEXT: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + parseContext.extensionTurnedOn(E_GL_EXT_fragment_shader_barycentric)) + return keyword; + return identifierOrType(); + + case PRECISE: + if ((parseContext.isEsProfile() && + (parseContext.version >= 320 || parseContext.extensionsTurnedOn(Num_AEP_gpu_shader5, AEP_gpu_shader5))) || + (!parseContext.isEsProfile() && parseContext.version >= 400)) + return keyword; + if (parseContext.isEsProfile() && parseContext.version == 310) { + reservedWord(); + return keyword; + } + return identifierOrType(); + + case PERPRIMITIVENV: + case PERVIEWNV: + case PERTASKNV: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_mesh_shader)) + return keyword; + return identifierOrType(); + + case PERPRIMITIVEEXT: + case TASKPAYLOADWORKGROUPEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_mesh_shader)) + return keyword; + return identifierOrType(); + + case FCOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case UCOOPMATNV: + case ICOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_integer_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case COOPMAT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_KHR_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case COOPVECNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_cooperative_vector)) + return keyword; + return identifierOrType(); + + case DEMOTE: + if (parseContext.extensionTurnedOn(E_GL_EXT_demote_to_helper_invocation)) + return keyword; + else + return identifierOrType(); + + case SPIRV_INSTRUCTION: + case SPIRV_EXECUTION_MODE: + case SPIRV_EXECUTION_MODE_ID: + case SPIRV_DECORATE: + case SPIRV_DECORATE_ID: + case SPIRV_DECORATE_STRING: + case SPIRV_TYPE: + case SPIRV_STORAGE_CLASS: + case SPIRV_BY_REFERENCE: + case SPIRV_LITERAL: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) + return keyword; + return identifierOrType(); + + case HITOBJECTNV: + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && parseContext.version >= 460 + && parseContext.extensionTurnedOn(E_GL_NV_shader_invocation_reorder))) + return keyword; + return identifierOrType(); + + case HITOBJECTATTRNV: + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && parseContext.version >= 460 + && parseContext.extensionTurnedOn(E_GL_NV_shader_invocation_reorder))) + return keyword; + return identifierOrType(); + + case FUNCTION: + case TENSORLAYOUTNV: + case TENSORVIEWNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_cooperative_matrix2)) + return keyword; + return identifierOrType(); + + default: + parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); + return 0; + } +} + +int TScanContext::identifierOrType() +{ + parserToken->sType.lex.string = NewPoolTString(tokenText); + if (field) + return IDENTIFIER; + + parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string); + if ((afterType == false && afterStruct == false) && parserToken->sType.lex.symbol != nullptr) { + if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) { + if (variable->isUserType() && + // treat redeclaration of forward-declared buffer/uniform reference as an identifier + !(variable->getType().isReference() && afterBuffer)) { + afterType = true; + + return TYPE_NAME; + } + } + } + + return IDENTIFIER; +} + +// Give an error for use of a reserved symbol. +// However, allow built-in declarations to use reserved words, to allow +// extension support before the extension is enabled. +int TScanContext::reservedWord() +{ + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.error(loc, "Reserved word.", tokenText, "", ""); + + return 0; +} + +int TScanContext::identifierOrReserved(bool reserved) +{ + if (reserved) { + reservedWord(); + + return 0; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future reserved keyword", tokenText, ""); + + return identifierOrType(); +} + +// For keywords that suddenly showed up on non-ES (not previously reserved) +// but then got reserved by ES 3.0. +int TScanContext::es30ReservedFromGLSL(int version) +{ + if (parseContext.symbolTable.atBuiltInLevel()) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < version)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "future reserved word in ES 300 and keyword in GLSL", tokenText, ""); + + return identifierOrType(); + } else if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + + return keyword; +} + +// For a keyword that was never reserved, until it suddenly +// showed up, both in an es version and a non-ES version. +int TScanContext::nonreservedKeyword(int esVersion, int nonEsVersion) +{ + if ((parseContext.isEsProfile() && parseContext.version < esVersion) || + (!parseContext.isEsProfile() && parseContext.version < nonEsVersion)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future keyword", tokenText, ""); + + return identifierOrType(); + } + + return keyword; +} + +int TScanContext::precisionKeyword() +{ + if (parseContext.isEsProfile() || parseContext.version >= 130) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using ES precision qualifier keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::matNxM() +{ + afterType = true; + + if (parseContext.version > 110) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future non-square matrix type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::dMat() +{ + afterType = true; + + if (parseContext.isEsProfile() && parseContext.version >= 300) { + reservedWord(); + + return keyword; + } + + if (!parseContext.isEsProfile() && (parseContext.version >= 400 || + parseContext.symbolTable.atBuiltInLevel() || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64)) || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) + && parseContext.language == EShLangVertex))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::firstGenerationImage(bool inEs310) +{ + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && (parseContext.version >= 420 || + parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))) || + (inEs310 && parseContext.isEsProfile() && parseContext.version >= 310)) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 130)) { + reservedWord(); + + return keyword; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::secondGenerationImage() +{ + if (parseContext.isEsProfile() && parseContext.version >= 310) { + reservedWord(); + return keyword; + } + + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && + (parseContext.version >= 420 || parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.h new file mode 100644 index 00000000..24b75cf7 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Scan.h @@ -0,0 +1,276 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _GLSLANG_SCAN_INCLUDED_ +#define _GLSLANG_SCAN_INCLUDED_ + +#include "Versions.h" + +namespace glslang { + +// Use a global end-of-input character, so no translation is needed across +// layers of encapsulation. Characters are all 8 bit, and positive, so there is +// no aliasing of character 255 onto -1, for example. +const int EndOfInput = -1; + +// +// A character scanner that seamlessly, on read-only strings, reads across an +// array of strings without assuming null termination. +// +class TInputScanner { +public: + TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, + int b = 0, int f = 0, bool single = false) : + numSources(n), + // up to this point, common usage is "char*", but now we need positive 8-bit characters + sources(reinterpret_cast(s)), + lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single), + endOfFileReached(false) + { + loc = new TSourceLoc[numSources]; + for (int i = 0; i < numSources; ++i) { + loc[i].init(i - stringBias); + } + if (names != nullptr) { + for (int i = 0; i < numSources; ++i) + loc[i].name = names[i] != nullptr ? NewPoolTString(names[i]) : nullptr; + } + loc[currentSource].line = 1; + logicalSourceLoc.init(1); + logicalSourceLoc.name = loc[0].name; + } + + virtual ~TInputScanner() + { + delete [] loc; + } + + // retrieve the next character and advance one character + int get() + { + int ret = peek(); + if (ret == EndOfInput) + return ret; + ++loc[currentSource].column; + ++logicalSourceLoc.column; + if (ret == '\n') { + ++loc[currentSource].line; + ++logicalSourceLoc.line; + logicalSourceLoc.column = 0; + loc[currentSource].column = 0; + } + advance(); + + return ret; + } + + // retrieve the next character, no advance + int peek() + { + if (currentSource >= numSources) { + endOfFileReached = true; + return EndOfInput; + } + // Make sure we do not read off the end of a string. + // N.B. Sources can have a length of 0. + int sourceToRead = currentSource; + size_t charToRead = currentChar; + while(charToRead >= lengths[sourceToRead]) { + charToRead = 0; + sourceToRead += 1; + if (sourceToRead >= numSources) { + return EndOfInput; + } + } + + // Here, we care about making negative valued characters positive + return sources[sourceToRead][charToRead]; + } + + // go back one character + void unget() + { + // Do not roll back once we've reached the end of the file. + if (endOfFileReached) + return; + + if (currentChar > 0) { + --currentChar; + --loc[currentSource].column; + --logicalSourceLoc.column; + if (loc[currentSource].column < 0) { + // We've moved back past a new line. Find the + // previous newline (or start of the file) to compute + // the column count on the now current line. + size_t chIndex = currentChar; + while (chIndex > 0) { + if (sources[currentSource][chIndex] == '\n') { + break; + } + --chIndex; + } + logicalSourceLoc.column = (int)(currentChar - chIndex); + loc[currentSource].column = (int)(currentChar - chIndex); + } + } else { + do { + --currentSource; + } while (currentSource > 0 && lengths[currentSource] == 0); + if (lengths[currentSource] == 0) { + // set to 0 if we've backed up to the start of an empty string + currentChar = 0; + } else + currentChar = lengths[currentSource] - 1; + } + if (peek() == '\n') { + --loc[currentSource].line; + --logicalSourceLoc.line; + } + } + + // for #line override + void setLine(int newLine) + { + logicalSourceLoc.line = newLine; + loc[getLastValidSourceIndex()].line = newLine; + } + + // for #line override in filename based parsing + void setFile(const char* filename) + { + TString* fn_tstr = NewPoolTString(filename); + logicalSourceLoc.name = fn_tstr; + loc[getLastValidSourceIndex()].name = fn_tstr; + } + + void setFile(const char* filename, int i) + { + TString* fn_tstr = NewPoolTString(filename); + if (i == getLastValidSourceIndex()) { + logicalSourceLoc.name = fn_tstr; + } + loc[i].name = fn_tstr; + } + + void setString(int newString) + { + logicalSourceLoc.string = newString; + loc[getLastValidSourceIndex()].string = newString; + logicalSourceLoc.name = nullptr; + loc[getLastValidSourceIndex()].name = nullptr; + } + + // for #include content indentation + void setColumn(int col) + { + logicalSourceLoc.column = col; + loc[getLastValidSourceIndex()].column = col; + } + + void setEndOfInput() + { + endOfFileReached = true; + currentSource = numSources; + } + + bool atEndOfInput() const { return endOfFileReached; } + + const TSourceLoc& getSourceLoc() const + { + if (singleLogical) { + return logicalSourceLoc; + } else { + return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; + } + } + // Returns the index (starting from 0) of the most recent valid source string we are reading from. + int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } + + void consumeWhiteSpace(bool& foundNonSpaceTab); + bool consumeComment(); + void consumeWhitespaceComment(bool& foundNonSpaceTab); + bool scanVersion(int& version, EProfile& profile, bool& notFirstToken); + +protected: + + // advance one character + void advance() + { + ++currentChar; + if (currentChar >= lengths[currentSource]) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + while (currentSource < numSources && lengths[currentSource] == 0) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + } + currentChar = 0; + } + } + + int numSources; // number of strings in source + const unsigned char* const *sources; // array of strings; must be converted to positive values on use, to avoid aliasing with -1 as EndOfInput + const size_t *lengths; // length of each string + int currentSource; + size_t currentChar; + + // This is for reporting what string/line an error occurred on, and can be overridden by #line. + // It remembers the last state of each source string as it is left for the next one, so unget() + // can restore that state. + TSourceLoc* loc; // an array + + int stringBias; // the first string that is the user's string number 0 + int finale; // number of internal strings after user's last string + + TSourceLoc logicalSourceLoc; + bool singleLogical; // treats the strings as a single logical string. + // locations will be reported from the first string. + + // Set to true once peek() returns EndOfFile, so that we won't roll back + // once we've reached EndOfFile. + bool endOfFileReached; +}; + +} // end namespace glslang + +#endif // _GLSLANG_SCAN_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ScanContext.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ScanContext.h new file mode 100644 index 00000000..74b2b3c7 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ScanContext.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This holds context specific to the GLSL scanner, which +// sits between the preprocessor scanner and parser. +// + +#pragma once + +#include "ParseHelper.h" + +namespace glslang { + +class TPpContext; +class TPpToken; +class TParserToken; + +class TScanContext { +public: + explicit TScanContext(TParseContextBase& pc) : + parseContext(pc), + afterType(false), afterStruct(false), + field(false), afterBuffer(false) { } + virtual ~TScanContext() { } + + static void fillInKeywordMap(); + static void deleteKeywordMap(); + + int tokenize(TPpContext*, TParserToken&); + +protected: + TScanContext(TScanContext&); + TScanContext& operator=(TScanContext&); + + int tokenizeIdentifier(); + int identifierOrType(); + int reservedWord(); + int identifierOrReserved(bool reserved); + int es30ReservedFromGLSL(int version); + int nonreservedKeyword(int esVersion, int nonEsVersion); + int precisionKeyword(); + int matNxM(); + int dMat(); + int firstGenerationImage(bool inEs310); + int secondGenerationImage(); + + TParseContextBase& parseContext; + bool afterType; // true if we've recognized a type, so can only be looking for an identifier + bool afterStruct; // true if we've recognized the STRUCT keyword, so can only be looking for an identifier + bool field; // true if we're on a field, right after a '.' + bool afterBuffer; // true if we've recognized the BUFFER keyword + TSourceLoc loc; + TParserToken* parserToken; + TPpToken* ppToken; + + const char* tokenText; + int keyword; +}; + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ShaderLang.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ShaderLang.cpp new file mode 100644 index 00000000..3cb6687f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/ShaderLang.cpp @@ -0,0 +1,2255 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement the top-level of interface to the compiler/linker, +// as defined in ShaderLang.h +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// +#include +#include +#include +#include +#include +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "Scan.h" +#include "ScanContext.h" + +#ifdef ENABLE_HLSL +#include "../HLSL/hlslParseHelper.h" +#include "../HLSL/hlslParseables.h" +#include "../HLSL/hlslScanContext.h" +#endif + +#include "../Include/ShHandle.h" + +#include "preprocessor/PpContext.h" + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "reflection.h" +#include "iomapper.h" +#include "Initialize.h" + +// TODO: this really shouldn't be here, it is only because of the trial addition +// of printing pre-processed tokens, which requires knowing the string literal +// token to print ", but none of that seems appropriate for this file. +#include "preprocessor/PpTokens.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +namespace { // anonymous namespace for file-local functions and symbols + +// Total number of successful initializers of glslang: a refcount +// Shared global; access should be protected by a global mutex/critical section. +int NumberOfClients = 0; + +// global initialization lock +#ifndef DISABLE_THREAD_SUPPORT +std::mutex init_lock; +#endif + + +using namespace glslang; + +// Create a language specific version of parseables. +TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source) +{ + switch (source) { + case EShSourceGlsl: return new TBuiltIns(); // GLSL builtIns +#ifdef ENABLE_HLSL + case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics +#endif + + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Create a language specific version of a parse context. +TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate, + int version, EProfile profile, EShSource source, + EShLanguage language, TInfoSink& infoSink, + SpvVersion spvVersion, bool forwardCompatible, EShMessages messages, + bool parsingBuiltIns, std::string sourceEntryPointName = "") +{ + switch (source) { + case EShSourceGlsl: { + if (sourceEntryPointName.size() == 0) + intermediate.setEntryPointName("main"); + TString entryPoint = sourceEntryPointName.c_str(); + return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, forwardCompatible, messages, &entryPoint); + } +#ifdef ENABLE_HLSL + case EShSourceHlsl: + return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages); +#endif + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Local mapping functions for making arrays of symbol tables.... + +const int VersionCount = 17; // index range in MapVersionToIndex + +int MapVersionToIndex(int version) +{ + int index = 0; + + switch (version) { + case 100: index = 0; break; + case 110: index = 1; break; + case 120: index = 2; break; + case 130: index = 3; break; + case 140: index = 4; break; + case 150: index = 5; break; + case 300: index = 6; break; + case 330: index = 7; break; + case 400: index = 8; break; + case 410: index = 9; break; + case 420: index = 10; break; + case 430: index = 11; break; + case 440: index = 12; break; + case 310: index = 13; break; + case 450: index = 14; break; + case 500: index = 0; break; // HLSL + case 320: index = 15; break; + case 460: index = 16; break; + default: assert(0); break; + } + + assert(index < VersionCount); + + return index; +} + +const int SpvVersionCount = 4; // index range in MapSpvVersionToIndex + +int MapSpvVersionToIndex(const SpvVersion& spvVersion) +{ + int index = 0; + + if (spvVersion.openGl > 0) + index = 1; + else if (spvVersion.vulkan > 0) { + if (!spvVersion.vulkanRelaxed) + index = 2; + else + index = 3; + } + + assert(index < SpvVersionCount); + + return index; +} + +const int ProfileCount = 4; // index range in MapProfileToIndex + +int MapProfileToIndex(EProfile profile) +{ + int index = 0; + + switch (profile) { + case ENoProfile: index = 0; break; + case ECoreProfile: index = 1; break; + case ECompatibilityProfile: index = 2; break; + case EEsProfile: index = 3; break; + default: break; + } + + assert(index < ProfileCount); + + return index; +} + +const int SourceCount = 2; + +int MapSourceToIndex(EShSource source) +{ + int index = 0; + + switch (source) { + case EShSourceGlsl: index = 0; break; + case EShSourceHlsl: index = 1; break; + default: break; + } + + assert(index < SourceCount); + + return index; +} + +// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins +enum EPrecisionClass { + EPcGeneral, + EPcFragment, + EPcCount +}; + +// A process-global symbol table per version per profile for built-ins common +// to multiple stages (languages), and a process-global symbol table per version +// per profile per stage for built-ins unique to each stage. They will be sparsely +// populated, so they will only be generated as needed. +// +// Each has a different set of built-ins, and we want to preserve that from +// compile to compile. +// +TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {}; +TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {}; + +TPoolAllocator* PerProcessGPA = nullptr; + +// +// Parse and add to the given symbol table the content of the given shader string. +// +bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable) +{ + TIntermediate intermediate(language, version, profile); + + intermediate.setSource(source); + + std::unique_ptr parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source, + language, infoSink, spvVersion, true, EShMsgDefault, + true)); + + TShader::ForbidIncluder includer; + TPpContext ppContext(*parseContext, "", includer); + TScanContext scanContext(*parseContext); + parseContext->setScanContext(&scanContext); + parseContext->setPpContext(&ppContext); + + // + // Push the symbol table to give it an initial scope. This + // push should not have a corresponding pop, so that built-ins + // are preserved, and the test for an empty table fails. + // + + symbolTable.push(); + + const char* builtInShaders[2]; + size_t builtInLengths[2]; + builtInShaders[0] = builtIns.c_str(); + builtInLengths[0] = builtIns.size(); + + if (builtInLengths[0] == 0) + return true; + + TInputScanner input(1, builtInShaders, builtInLengths); + if (! parseContext->parseShaderStrings(ppContext, input) != 0) { + infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); + printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str()); + printf("%s\n", builtInShaders[0]); + + return false; + } + + return true; +} + +int CommonIndex(EProfile profile, EShLanguage language) +{ + return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral; +} + +// +// To initialize per-stage shared tables, with the common table already complete. +// +bool InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion, + EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable, + TSymbolTable** symbolTables) +{ + (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]); + if (!InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source, + infoSink, *symbolTables[language])) + return false; + builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]); + if (profile == EEsProfile && version >= 300) + (*symbolTables[language]).setNoBuiltInRedeclarations(); + if (version == 110) + (*symbolTables[language]).setSeparateNameSpaces(); + + return true; +} + +// +// Initialize the full set of shareable symbol tables; +// The common (cross-stage) and those shareable per-stage. +// +bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ + bool success = true; + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(version, profile, spvVersion); + + // do the common tables + success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source, + infoSink, *commonTable[EPcGeneral]); + if (profile == EEsProfile) + success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source, + infoSink, *commonTable[EPcFragment]); + + // do the per-stage tables + + // always have vertex and fragment + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source, + infoSink, commonTable, symbolTables); + + // check for tessellation + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source, + infoSink, commonTable, symbolTables); + } + + // check for geometry + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source, + infoSink, commonTable, symbolTables); + + // check for compute + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source, + infoSink, commonTable, symbolTables); + + // check for ray tracing stages + if (profile != EEsProfile && version >= 450) { + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source, + infoSink, commonTable, symbolTables); + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source, + infoSink, commonTable, symbolTables); + } + + // check for mesh + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMesh, source, + infoSink, commonTable, symbolTables); + + // check for task + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTask, source, + infoSink, commonTable, symbolTables); + + return success; +} + +bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source) +{ + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(*resources, version, profile, spvVersion, language); + if (!InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable)) + return false; + builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources); + + return true; +} + +// +// To do this on the fly, we want to leave the current state of our thread's +// pool allocator intact, so: +// - Switch to a new pool for parsing the built-ins +// - Do the parsing, which builds the symbol table, using the new pool +// - Switch to the process-global pool to save a copy of the resulting symbol table +// - Free up the new pool used to parse the built-ins +// - Switch back to the original thread's pool +// +// This only gets done the first time any thread needs a particular symbol table +// (lazy evaluation). +// +bool SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ + TInfoSink infoSink; + bool success; + + // Make sure only one thread tries to do this at a time +#ifndef DISABLE_THREAD_SUPPORT + const std::lock_guard lock(init_lock); +#endif + + // See if it's already been done for this version/profile combination + int versionIndex = MapVersionToIndex(version); + int spvVersionIndex = MapSpvVersionToIndex(spvVersion); + int profileIndex = MapProfileToIndex(profile); + int sourceIndex = MapSourceToIndex(source); + if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) { + return true; + } + + // Switch to a new pool + TPoolAllocator& previousAllocator = GetThreadPoolAllocator(); + TPoolAllocator* builtInPoolAllocator = new TPoolAllocator; + SetThreadPoolAllocator(builtInPoolAllocator); + + // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped. + TSymbolTable* commonTable[EPcCount]; + TSymbolTable* stageTables[EShLangCount]; + for (int precClass = 0; precClass < EPcCount; ++precClass) + commonTable[precClass] = new TSymbolTable; + for (int stage = 0; stage < EShLangCount; ++stage) + stageTables[stage] = new TSymbolTable; + + // Generate the local symbol tables using the new pool + if (!InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source)) { + success = false; + goto cleanup; + } + + // Switch to the process-global pool + SetThreadPoolAllocator(PerProcessGPA); + + // Copy the local symbol tables from the new pool to the global tables using the process-global pool + for (int precClass = 0; precClass < EPcCount; ++precClass) { + if (! commonTable[precClass]->isEmpty()) { + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable; + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]); + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly(); + } + } + for (int stage = 0; stage < EShLangCount; ++stage) { + if (! stageTables[stage]->isEmpty()) { + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable; + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable + [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly(); + } + } + success = true; + +cleanup: + // Clean up the local tables before deleting the pool they used. + for (int precClass = 0; precClass < EPcCount; ++precClass) + delete commonTable[precClass]; + for (int stage = 0; stage < EShLangCount; ++stage) + delete stageTables[stage]; + + delete builtInPoolAllocator; + SetThreadPoolAllocator(&previousAllocator); + + return success; +} + +// Function to Print all builtins +void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable) +{ + infoSink.debug << "BuiltinSymbolTable {\n"; + + symbolTable.dump(infoSink, true); + + infoSink.debug << "}\n"; +} + +// Return true if the shader was correctly specified for version/profile/stage. +bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion, + EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion) +{ + const int FirstProfileVersion = 150; + bool correct = true; + + if (source == EShSourceHlsl) { + version = 500; // shader model; currently a characteristic of glslang, not the input + profile = ECoreProfile; // allow doubles in prototype parsing + return correct; + } + + // Get a version... + if (version == 0) { + version = defaultVersion; + // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader"); + } + + // Get a good profile... + if (profile == ENoProfile) { + if (version == 300 || version == 310 || version == 320) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile"); + profile = EEsProfile; + } else if (version == 100) + profile = EEsProfile; + else if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } else { + // a profile was provided... + if (version < 150) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token"); + if (version == 100) + profile = EEsProfile; + else + profile = ENoProfile; + } else if (version == 300 || version == 310 || version == 320) { + if (profile != EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile"); + } + profile = EEsProfile; + } else { + if (profile == EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile"); + if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } + // else: typical desktop case... e.g., "#version 410 core" + } + } + + // Fix version... + switch (version) { + // ES versions + case 100: break; + case 300: break; + case 310: break; + case 320: break; + + // desktop versions + case 110: break; + case 120: break; + case 130: break; + case 140: break; + case 150: break; + case 330: break; + case 400: break; + case 410: break; + case 420: break; + case 430: break; + case 440: break; + case 450: break; + case 460: break; + + // unknown version + default: + correct = false; + infoSink.info.message(EPrefixError, "version not supported"); + if (profile == EEsProfile) + version = 310; + else { + version = 450; + profile = ECoreProfile; + } + break; + } + + // Correct for stage type... + switch (stage) { + case EShLangGeometry: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 150; + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangCompute: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 420)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above"); + version = profile == EEsProfile ? 310 : 420; + } + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile == EEsProfile || version < 460) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above"); + version = 460; + } + break; + case EShLangMesh: + case EShLangTask: + if ((profile == EEsProfile && version < 320) || + (profile != EEsProfile && version < 450)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above"); + version = profile == EEsProfile ? 320 : 450; + } + break; + default: + break; + } + + if (profile == EEsProfile && version >= 300 && versionNotFirst) { + correct = false; + infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines"); + } + + // Check for SPIR-V compatibility + if (spvVersion.spv != 0) { + switch (profile) { + case EEsProfile: + if (version < 310) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher"); + version = 310; + } + break; + case ECompatibilityProfile: + infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile"); + break; + default: + if (spvVersion.vulkan > 0 && version < 140) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher"); + version = 140; + } + if (spvVersion.openGl >= 100 && version < 330) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher"); + version = 330; + } + break; + } + } + + return correct; +} + +// There are multiple paths in for setting environment stuff. +// TEnvironment takes precedence, for what it sets, so sort all this out. +// Ideally, the internal code could be made to use TEnvironment, but for +// now, translate it to the historically used parameters. +void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source, + EShLanguage& stage, SpvVersion& spvVersion) +{ + // Set up environmental defaults, first ignoring 'environment'. + if (messages & EShMsgSpvRules) + spvVersion.spv = EShTargetSpv_1_0; + if (messages & EShMsgVulkanRules) { + spvVersion.vulkan = EShTargetVulkan_1_0; + spvVersion.vulkanGlsl = 100; + } else if (spvVersion.spv != 0) + spvVersion.openGl = 100; + + // Now, override, based on any content set in 'environment'. + // 'environment' must be cleared to ESh*None settings when items + // are not being set. + if (environment != nullptr) { + // input language + if (environment->input.languageFamily != EShSourceNone) { + stage = environment->input.stage; + switch (environment->input.dialect) { + case EShClientNone: + break; + case EShClientVulkan: + spvVersion.vulkanGlsl = environment->input.dialectVersion; + spvVersion.vulkanRelaxed = environment->input.vulkanRulesRelaxed; + break; + case EShClientOpenGL: + spvVersion.openGl = environment->input.dialectVersion; + break; + case EShClientCount: + assert(0); + break; + } + switch (environment->input.languageFamily) { + case EShSourceNone: + break; + case EShSourceGlsl: + source = EShSourceGlsl; + messages = static_cast(messages & ~EShMsgReadHlsl); + break; + case EShSourceHlsl: + source = EShSourceHlsl; + messages = static_cast(messages | EShMsgReadHlsl); + break; + case EShSourceCount: + assert(0); + break; + } + } + + // client + switch (environment->client.client) { + case EShClientVulkan: + spvVersion.vulkan = environment->client.version; + break; + default: + break; + } + + // generated code + switch (environment->target.language) { + case EshTargetSpv: + spvVersion.spv = environment->target.version; + break; + default: + break; + } + } +} + +// Most processes are recorded when set in the intermediate representation, +// These are the few that are not. +void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName) +{ + if ((messages & EShMsgRelaxedErrors) != 0) + intermediate.addProcess("relaxed-errors"); + if ((messages & EShMsgSuppressWarnings) != 0) + intermediate.addProcess("suppress-warnings"); + if ((messages & EShMsgKeepUncalled) != 0) + intermediate.addProcess("keep-uncalled"); + if (sourceEntryPointName.size() > 0) { + intermediate.addProcess("source-entrypoint"); + intermediate.addProcessArgument(sourceEntryPointName); + } +} + +// This is the common setup and cleanup code for PreprocessDeferred and +// CompileDeferred. +// It takes any callable with a signature of +// bool (TParseContextBase& parseContext, TPpContext& ppContext, +// TInputScanner& input, bool versionWillBeError, +// TSymbolTable& , TIntermediate& , +// EShOptimizationLevel , EShMessages ); +// Which returns false if a failure was detected and true otherwise. +// +template +bool ProcessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* customPreamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan + EProfile defaultProfile, + // set version/profile to defaultVersion/defaultProfile regardless of the #version + // directive in the source code + bool forceDefaultVersionAndProfile, + int overrideVersion, // overrides version specified by #version or default version + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate, // returned tree, etc. + ProcessingContext& processingContext, + bool requireNonempty, + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + const TEnvironment* environment = nullptr, // optional way of fully setting all versions, overriding the above + bool compileOnly = false) +{ + // This must be undone (.pop()) by the caller, after it finishes consuming the created tree. + GetThreadPoolAllocator().push(); + + if (numStrings == 0) + return true; + + // Move to length-based strings, rather than null-terminated strings. + // Also, add strings to include the preamble and to ensure the shader is not null, + // which lets the grammar accept what was a null (post preprocessing) shader. + // + // Shader will look like + // string 0: system preamble + // string 1: custom preamble + // string 2...numStrings+1: user's shader + // string numStrings+2: "int;" + const int numPre = 2; + const int numPost = requireNonempty? 1 : 0; + const int numTotal = numPre + numStrings + numPost; + std::unique_ptr lengths(new size_t[numTotal]); + std::unique_ptr strings(new const char*[numTotal]); + std::unique_ptr names(new const char*[numTotal]); + for (int s = 0; s < numStrings; ++s) { + strings[s + numPre] = shaderStrings[s]; + if (inputLengths == nullptr || inputLengths[s] < 0) + lengths[s + numPre] = strlen(shaderStrings[s]); + else + lengths[s + numPre] = inputLengths[s]; + } + if (stringNames != nullptr) { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = stringNames[s]; + } else { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = nullptr; + } + + // Get all the stages, languages, clients, and other environment + // stuff sorted out. + EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl; + SpvVersion spvVersion; + EShLanguage stage = compiler->getLanguage(); + TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion); +#ifdef ENABLE_HLSL + EShSource source = sourceGuess; + if (environment != nullptr && environment->target.hlslFunctionality1) + intermediate.setHlslFunctionality1(); +#else + const EShSource source = EShSourceGlsl; +#endif + // First, without using the preprocessor or parser, find the #version, so we know what + // symbol tables, processing rules, etc. to set up. This does not need the extra strings + // outlined above, just the user shader, after the system and user preambles. + glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); + int version = 0; + EProfile profile = ENoProfile; + bool versionNotFirstToken = false; + bool versionNotFirst = (source == EShSourceHlsl) + ? true + : userInput.scanVersion(version, profile, versionNotFirstToken); + bool versionNotFound = version == 0; + if (forceDefaultVersionAndProfile && source == EShSourceGlsl) { + if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && + (version != defaultVersion || profile != defaultProfile)) { + compiler->infoSink.info << "Warning, (version, profile) forced to be (" + << defaultVersion << ", " << ProfileName(defaultProfile) + << "), while in source code it is (" + << version << ", " << ProfileName(profile) << ")\n"; + } + + if (versionNotFound) { + versionNotFirstToken = false; + versionNotFirst = false; + versionNotFound = false; + } + version = defaultVersion; + profile = defaultProfile; + } + if (source == EShSourceGlsl && overrideVersion != 0) { + version = overrideVersion; + } + + bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage, + versionNotFirst, defaultVersion, source, version, profile, spvVersion); + bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); + bool warnVersionNotFirst = false; + if (! versionWillBeError && versionNotFirstToken) { + if (messages & EShMsgRelaxedErrors) + warnVersionNotFirst = true; + else + versionWillBeError = true; + } + + intermediate.setSource(source); + intermediate.setVersion(version); + intermediate.setProfile(profile); + intermediate.setSpv(spvVersion); + RecordProcesses(intermediate, messages, sourceEntryPointName); + if (spvVersion.vulkan > 0) + intermediate.setOriginUpperLeft(); +#ifdef ENABLE_HLSL + if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl) + intermediate.setHlslOffsets(); +#endif + if (messages & EShMsgDebugInfo) { + intermediate.setSourceFile(names[numPre]); + for (int s = 0; s < numStrings; ++s) { + // The string may not be null-terminated, so make sure we provide + // the length along with the string. + intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]); + } + } + if (!SetupBuiltinSymbolTable(version, profile, spvVersion, source)) { + return false; + } + + TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] + [MapSpvVersionToIndex(spvVersion)] + [MapProfileToIndex(profile)] + [MapSourceToIndex(source)] + [stage]; + + // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. + std::unique_ptr symbolTable(new TSymbolTable); + if (cachedTable) + symbolTable->adoptLevels(*cachedTable); + + if (intermediate.getUniqueId() != 0) + symbolTable->overwriteUniqueId(intermediate.getUniqueId()); + + // Add built-in symbols that are potentially context dependent; + // they get popped again further down. + if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion, + stage, source)) { + return false; + } + + if (messages & EShMsgBuiltinSymbolTable) + DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable); + + // + // Now we can process the full shader under proper symbols and rules. + // + + std::unique_ptr parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source, + stage, compiler->infoSink, + spvVersion, forwardCompatible, messages, false, sourceEntryPointName)); + parseContext->compileOnly = compileOnly; + TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer); + + // only GLSL (bison triggered, really) needs an externally set scan context + glslang::TScanContext scanContext(*parseContext); + if (source == EShSourceGlsl) + parseContext->setScanContext(&scanContext); + + parseContext->setPpContext(&ppContext); + parseContext->setLimits(*resources); + if (! goodVersion) + parseContext->addError(); + if (warnVersionNotFirst) { + TSourceLoc loc; + loc.init(); + parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", ""); + } + + parseContext->initializeExtensionBehavior(); + + // Fill in the strings as outlined above. + std::string preamble; + parseContext->getPreamble(preamble); + strings[0] = preamble.c_str(); + lengths[0] = strlen(strings[0]); + names[0] = nullptr; + strings[1] = customPreamble; + lengths[1] = strlen(strings[1]); + names[1] = nullptr; + assert(2 == numPre); + if (requireNonempty) { + const int postIndex = numStrings + numPre; + strings[postIndex] = "\n int;"; + lengths[postIndex] = strlen(strings[numStrings + numPre]); + names[postIndex] = nullptr; + } + TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost); + + // Push a new symbol allocation scope that will get used for the shader's globals. + symbolTable->push(); + + bool success = processingContext(*parseContext, ppContext, fullInput, + versionWillBeError, *symbolTable, + intermediate, optLevel, messages); + intermediate.setUniqueId(symbolTable->getMaxSymbolId()); + return success; +} + +// Responsible for keeping track of the most recent source string and line in +// the preprocessor and outputting newlines appropriately if the source string +// or line changes. +class SourceLineSynchronizer { +public: + SourceLineSynchronizer(const std::function& lastSourceIndex, + std::string* output) + : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {} +// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete; +// SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete; + + // Sets the internally tracked source string index to that of the most + // recently read token. If we switched to a new source string, returns + // true and inserts a newline. Otherwise, returns false and outputs nothing. + bool syncToMostRecentString() { + if (getLastSourceIndex() != lastSource) { + // After switching to a new source string, we need to reset lastLine + // because line number resets every time a new source string is + // used. We also need to output a newline to separate the output + // from the previous source string (if there is one). + if (lastSource != -1 || lastLine != 0) + *output += '\n'; + lastSource = getLastSourceIndex(); + lastLine = -1; + return true; + } + return false; + } + + // Calls syncToMostRecentString() and then sets the internally tracked line + // number to tokenLine. If we switched to a new line, returns true and inserts + // newlines appropriately. Otherwise, returns false and outputs nothing. + bool syncToLine(int tokenLine) { + syncToMostRecentString(); + const bool newLineStarted = lastLine < tokenLine; + for (; lastLine < tokenLine; ++lastLine) { + if (lastLine > 0) *output += '\n'; + } + return newLineStarted; + } + + // Sets the internally tracked line number to newLineNum. + void setLineNum(int newLineNum) { lastLine = newLineNum; } + +private: + SourceLineSynchronizer& operator=(const SourceLineSynchronizer&); + + // A function for getting the index of the last valid source string we've + // read tokens from. + const std::function getLastSourceIndex; + // output string for newlines. + std::string* output; + // lastSource is the source string index (starting from 0) of the last token + // processed. It is tracked in order for newlines to be inserted when a new + // source string starts. -1 means we haven't started processing any source + // string. + int lastSource; + // lastLine is the line number (starting from 1) of the last token processed. + // It is tracked in order for newlines to be inserted when a token appears + // on a new line. 0 means we haven't started processing any line in the + // current source string. + int lastLine; +}; + +// DoPreprocessing is a valid ProcessingContext template argument, +// which only performs the preprocessing step of compilation. +// It places the result in the "string" argument to its constructor. +// +// This is not an officially supported or fully working path. +struct DoPreprocessing { + explicit DoPreprocessing(std::string* string): outputString(string) {} + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& input, bool versionWillBeError, + TSymbolTable&, TIntermediate&, + EShOptimizationLevel, EShMessages) + { + // This is a list of tokens that do not require a space before or after. + static const std::string noNeededSpaceBeforeTokens = ";)[].,"; + static const std::string noNeededSpaceAfterTokens = ".(["; + glslang::TPpToken ppToken; + + parseContext.setScanner(&input); + ppContext.setInput(input, versionWillBeError); + + std::string outputBuffer; + SourceLineSynchronizer lineSync( + std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer); + + parseContext.setExtensionCallback([&lineSync, &outputBuffer]( + int line, const char* extension, const char* behavior) { + lineSync.syncToLine(line); + outputBuffer += "#extension "; + outputBuffer += extension; + outputBuffer += " : "; + outputBuffer += behavior; + }); + + parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext]( + int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) { + // SourceNum is the number of the source-string that is being parsed. + lineSync.syncToLine(curLineNum); + outputBuffer += "#line "; + outputBuffer += std::to_string(newLineNum); + if (hasSource) { + outputBuffer += ' '; + if (sourceName != nullptr) { + outputBuffer += '\"'; + outputBuffer += sourceName; + outputBuffer += '\"'; + } else { + outputBuffer += std::to_string(sourceNum); + } + } + if (parseContext.lineDirectiveShouldSetNextLine()) { + // newLineNum is the new line number for the line following the #line + // directive. So the new line number for the current line is + newLineNum -= 1; + } + outputBuffer += '\n'; + // And we are at the next line of the #line directive now. + lineSync.setLineNum(newLineNum + 1); + }); + + parseContext.setVersionCallback( + [&lineSync, &outputBuffer](int line, int version, const char* str) { + lineSync.syncToLine(line); + outputBuffer += "#version "; + outputBuffer += std::to_string(version); + if (str) { + outputBuffer += ' '; + outputBuffer += str; + } + }); + + parseContext.setPragmaCallback([&lineSync, &outputBuffer]( + int line, const glslang::TVector& ops) { + lineSync.syncToLine(line); + outputBuffer += "#pragma "; + for(size_t i = 0; i < ops.size(); ++i) { + outputBuffer += ops[i].c_str(); + } + }); + + parseContext.setErrorCallback([&lineSync, &outputBuffer]( + int line, const char* errorMessage) { + lineSync.syncToLine(line); + outputBuffer += "#error "; + outputBuffer += errorMessage; + }); + + int lastToken = EndOfInput; // lastToken records the last token processed. + std::string lastTokenName; + do { + int token = ppContext.tokenize(ppToken); + if (token == EndOfInput) + break; + + bool isNewString = lineSync.syncToMostRecentString(); + bool isNewLine = lineSync.syncToLine(ppToken.loc.line); + + if (isNewLine) { + // Don't emit whitespace onto empty lines. + // Copy any whitespace characters at the start of a line + // from the input to the output. + outputBuffer += std::string(ppToken.loc.column - 1, ' '); + } + + // Output a space in between tokens, but not at the start of a line, + // and also not around special tokens. This helps with readability + // and consistency. + if (!isNewString && !isNewLine && lastToken != EndOfInput) { + // left parenthesis need a leading space, except it is in a function-call-like context. + // examples: `for (xxx)`, `a * (b + c)`, `vec(2.0)`, `foo(x, y, z)` + if (token == '(') { + if (lastToken != PpAtomIdentifier || + lastTokenName == "if" || + lastTokenName == "for" || + lastTokenName == "while" || + lastTokenName == "switch") + outputBuffer += ' '; + } else if ((noNeededSpaceBeforeTokens.find((char)token) == std::string::npos) && + (noNeededSpaceAfterTokens.find((char)lastToken) == std::string::npos)) { + outputBuffer += ' '; + } + } + if (token == PpAtomIdentifier) + lastTokenName = ppToken.name; + lastToken = token; + if (token == PpAtomConstString) + outputBuffer += "\""; + outputBuffer += ppToken.name; + if (token == PpAtomConstString) + outputBuffer += "\""; + } while (true); + outputBuffer += '\n'; + *outputString = std::move(outputBuffer); + + bool success = true; + if (parseContext.getNumErrors() > 0) { + success = false; + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + return success; + } + std::string* outputString; +}; + +// DoFullParse is a valid ProcessingConext template argument for fully +// parsing the shader. It populates the "intermediate" with the AST. +struct DoFullParse{ + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& fullInput, bool versionWillBeError, + TSymbolTable&, TIntermediate& intermediate, + EShOptimizationLevel optLevel, EShMessages messages) + { + bool success = true; + // Parse the full shader. + if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError)) + success = false; + + if (success && intermediate.getTreeRoot()) { + if (optLevel == EShOptNoGeneration) + parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested."); + else + success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage()); + } else if (! success) { + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + + if (messages & EShMsgAST) + intermediate.output(parseContext.infoSink, true); + + return success; + } +}; + +// Take a single compilation unit, and run the preprocessor on it. +// Return: True if there were no issues found in preprocessing, +// False if during preprocessing any unknown version, pragmas or +// extensions were found. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool PreprocessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + int overrideVersion, // use 0 if not overriding GLSL version + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TShader::Includer& includer, + TIntermediate& intermediate, // returned tree, etc. + std::string* outputString, + TEnvironment* environment = nullptr) +{ + DoPreprocessing parser(outputString); + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, overrideVersion, + forwardCompatible, messages, intermediate, parser, + false, includer, "", environment); +} + +// +// do a partial compile on the given strings for a single compilation unit +// for a potential deferred link into a single stage (and deferred full compile of that +// stage through machine-dependent compilation). +// +// all preprocessing, parsing, semantic checks, etc. for a single compilation unit +// are done here. +// +// return: the tree and other information is filled into the intermediate argument, +// and true is returned by the function for success. +// +bool CompileDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + int overrideVersion, // use 0 if not overriding GLSL version + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate,// returned tree, etc. + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + TEnvironment* environment = nullptr, + bool compileOnly = false) +{ + DoFullParse parser; + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, overrideVersion, + forwardCompatible, messages, intermediate, parser, + true, includer, sourceEntryPointName, environment, compileOnly); +} + +} // end anonymous namespace for local functions + +// +// ShInitialize() should be called exactly once per process, not per thread. +// +int ShInitialize() +{ +#ifndef DISABLE_THREAD_SUPPORT + const std::lock_guard lock(init_lock); +#endif + ++NumberOfClients; + + if (PerProcessGPA == nullptr) + PerProcessGPA = new TPoolAllocator(); + + return 1; +} + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// + +ShHandle ShConstructCompiler(const EShLanguage language, int /*debugOptions unused*/) +{ + TShHandleBase* base = static_cast(ConstructCompiler(language, 0)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructLinker(const EShExecutable executable, int /*debugOptions unused*/) +{ + TShHandleBase* base = static_cast(ConstructLinker(executable, 0)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructUniformMap() +{ + TShHandleBase* base = static_cast(ConstructUniformMap()); + + return reinterpret_cast(base); +} + +void ShDestruct(ShHandle handle) +{ + if (handle == nullptr) + return; + + TShHandleBase* base = static_cast(handle); + + if (base->getAsCompiler()) + DeleteCompiler(base->getAsCompiler()); + else if (base->getAsLinker()) + DeleteLinker(base->getAsLinker()); + else if (base->getAsUniformMap()) + DeleteUniformMap(base->getAsUniformMap()); +} + +// +// Cleanup symbol tables +// +int ShFinalize() +{ +#ifndef DISABLE_THREAD_SUPPORT + const std::lock_guard lock(init_lock); +#endif + --NumberOfClients; + assert(NumberOfClients >= 0); + if (NumberOfClients > 0) + return 1; + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int stage = 0; stage < EShLangCount; ++stage) { + delete SharedSymbolTables[version][spvVersion][p][source][stage]; + SharedSymbolTables[version][spvVersion][p][source][stage] = nullptr; + } + } + } + } + } + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int pc = 0; pc < EPcCount; ++pc) { + delete CommonSymbolTable[version][spvVersion][p][source][pc]; + CommonSymbolTable[version][spvVersion][p][source][pc] = nullptr; + } + } + } + } + } + + if (PerProcessGPA != nullptr) { + delete PerProcessGPA; + PerProcessGPA = nullptr; + } + + return 1; +} + +// +// Do a full compile on the given strings for a single compilation unit +// forming a complete stage. The result of the machine dependent compilation +// is left in the provided compile object. +// +// Return: The return value is really boolean, indicating +// success (1) or failure (0). +// +int ShCompile( + const ShHandle handle, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int /*debugOptions*/, + int defaultVersion, // use 100 for ES environment, 110 for desktop + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out, + const char *shaderFileName // the filename + ) +{ + // Map the generic handle to the C++ object + if (handle == nullptr) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TCompiler* compiler = base->getAsCompiler(); + if (compiler == nullptr) + return 0; + + SetThreadPoolAllocator(compiler->getPool()); + + compiler->infoSink.info.erase(); + compiler->infoSink.debug.erase(); + compiler->infoSink.info.setShaderFileName(shaderFileName); + compiler->infoSink.debug.setShaderFileName(shaderFileName); + + + TIntermediate intermediate(compiler->getLanguage()); + TShader::ForbidIncluder includer; + bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, + "", optLevel, resources, defaultVersion, ENoProfile, false, 0, + forwardCompatible, messages, intermediate, includer); + + // + // Call the machine dependent compiler + // + if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration) + success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile()); + + intermediate.removeTree(); + + // Throw away all the temporary memory used by the compilation process. + // The push was done in the CompileDeferred() call above. + GetThreadPoolAllocator().pop(); + + return success ? 1 : 0; +} + +// +// Link the given compile objects. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShLinkExt( + const ShHandle linkHandle, + const ShHandle compHandles[], + const int numHandles) +{ + if (linkHandle == nullptr || numHandles == 0) + return 0; + + THandleList cObjects; + + for (int i = 0; i < numHandles; ++i) { + if (compHandles[i] == nullptr) + return 0; + TShHandleBase* base = reinterpret_cast(compHandles[i]); + if (base->getAsLinker()) { + cObjects.push_back(base->getAsLinker()); + } + if (base->getAsCompiler()) + cObjects.push_back(base->getAsCompiler()); + + if (cObjects[i] == nullptr) + return 0; + } + + TShHandleBase* base = reinterpret_cast(linkHandle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == nullptr) + return 0; + + SetThreadPoolAllocator(linker->getPool()); + linker->infoSink.info.erase(); + + for (int i = 0; i < numHandles; ++i) { + if (cObjects[i]->getAsCompiler()) { + if (! cObjects[i]->getAsCompiler()->linkable()) { + linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code."); + return 0; + } + } + } + + bool ret = linker->link(cObjects); + + return ret ? 1 : 0; +} + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +void ShSetEncryptionMethod(ShHandle handle) +{ + if (handle == nullptr) + return; +} + +// +// Return any compiler/linker/uniformmap log of messages for the application. +// +const char* ShGetInfoLog(const ShHandle handle) +{ + if (handle == nullptr) + return nullptr; + + TShHandleBase* base = static_cast(handle); + TInfoSink* infoSink; + + if (base->getAsCompiler()) + infoSink = &(base->getAsCompiler()->getInfoSink()); + else if (base->getAsLinker()) + infoSink = &(base->getAsLinker()->getInfoSink()); + else + return nullptr; + + infoSink->info << infoSink->debug.c_str(); + return infoSink->info.c_str(); +} + +// +// Return the resulting binary code from the link process. Structure +// is machine dependent. +// +const void* ShGetExecutable(const ShHandle handle) +{ + if (handle == nullptr) + return nullptr; + + TShHandleBase* base = reinterpret_cast(handle); + + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == nullptr) + return nullptr; + + return linker->getObjectCode(); +} + +// +// Let the linker know where the application said it's attributes are bound. +// The linker does not use these values, they are remapped by the ICD or +// hardware. It just needs them to know what's aliased. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == nullptr) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == nullptr) + return 0; + + linker->setAppAttributeBindings(table); + + return 1; +} + +// +// Let the linker know where the predefined attributes have to live. +// +int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == nullptr) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == nullptr) + return 0; + + linker->setFixedAttributeBindings(table); + return 1; +} + +// +// Some attribute locations are off-limits to the linker... +// +int ShExcludeAttributes(const ShHandle handle, int *attributes, int count) +{ + if (handle == nullptr) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == nullptr) + return 0; + + linker->setExcludedAttributes(attributes, count); + + return 1; +} + +// +// Return the index for OpenGL to use for knowing where a uniform lives. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShGetUniformLocation(const ShHandle handle, const char* name) +{ + if (handle == nullptr) + return -1; + + TShHandleBase* base = reinterpret_cast(handle); + TUniformMap* uniformMap= base->getAsUniformMap(); + if (uniformMap == nullptr) + return -1; + + return uniformMap->getLocation(name); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface that might potentially replace the above +// opaque handle-based interface. +// +// See more detailed comment in ShaderLang.h +// + +namespace glslang { + +Version GetVersion() +{ + Version version; + version.major = GLSLANG_VERSION_MAJOR; + version.minor = GLSLANG_VERSION_MINOR; + version.patch = GLSLANG_VERSION_PATCH; + version.flavor = GLSLANG_VERSION_FLAVOR; + return version; +} + +#define QUOTE(s) #s +#define STR(n) QUOTE(n) + +const char* GetEsslVersionString() +{ + return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR( + GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR; +} + +const char* GetGlslVersionString() +{ + return "4.60 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR( + GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR; +} + +int GetKhronosToolId() +{ + return 8; +} + +bool InitializeProcess() +{ + return ShInitialize() != 0; +} + +void FinalizeProcess() +{ + ShFinalize(); +} + +class TDeferredCompiler : public TCompiler { +public: + TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { } + virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; } +}; + +TIoMapper* GetGlslIoMapper() { + return static_cast(new TGlslIoMapper()); +} + +TShader::TShader(EShLanguage s) + : stage(s), lengths(nullptr), stringNames(nullptr), preamble(""), overrideVersion(0) +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + compiler = new TDeferredCompiler(stage, *infoSink); + intermediate = new TIntermediate(s); + + // clear environment (avoid constructors in them for use in a C interface) + environment.input.languageFamily = EShSourceNone; + environment.input.dialect = EShClientNone; + environment.input.vulkanRulesRelaxed = false; + environment.client.client = EShClientNone; + environment.target.language = EShTargetNone; + environment.target.hlslFunctionality1 = false; +} + +TShader::~TShader() +{ + delete infoSink; + delete compiler; + delete intermediate; + delete pool; +} + +void TShader::setStrings(const char* const* s, int n) +{ + strings = s; + numStrings = n; + lengths = nullptr; +} + +void TShader::setStringsWithLengths(const char* const* s, const int* l, int n) +{ + strings = s; + numStrings = n; + lengths = l; +} + +void TShader::setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n) +{ + strings = s; + numStrings = n; + lengths = l; + stringNames = names; +} + +void TShader::setEntryPoint(const char* entryPoint) +{ + intermediate->setEntryPointName(entryPoint); +} + +void TShader::setSourceEntryPoint(const char* name) +{ + sourceEntryPointName = name; +} + +// Log initial settings and transforms. +// See comment for class TProcesses. +void TShader::addProcesses(const std::vector& p) +{ + intermediate->addProcesses(p); +} + +void TShader::setUniqueId(unsigned long long id) +{ + intermediate->setUniqueId(id); +} + +void TShader::setOverrideVersion(int version) +{ + overrideVersion = version; +} + +void TShader::setDebugInfo(bool debugInfo) { intermediate->setDebugInfo(debugInfo); } +void TShader::setInvertY(bool invert) { intermediate->setInvertY(invert); } +void TShader::setDxPositionW(bool invert) { intermediate->setDxPositionW(invert); } +void TShader::setEnhancedMsgs() { intermediate->setEnhancedMsgs(); } +void TShader::setNanMinMaxClamp(bool useNonNan) { intermediate->setNanMinMaxClamp(useNonNan); } + +// Set binding base for given resource type +void TShader::setShiftBinding(TResourceType res, unsigned int base) { + intermediate->setShiftBinding(res, base); +} + +// Set binding base for given resource type for a given binding set. +void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) { + intermediate->setShiftBindingForSet(res, base, set); +} + +// Set binding base for sampler types +void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); } +// Set binding base for texture types (SRV) +void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); } +// Set binding base for image types +void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); } +// Set binding base for uniform buffer objects (CBV) +void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Synonym for setShiftUboBinding, to match HLSL language. +void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Set binding base for UAV (unordered access view) +void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); } +// Set binding base for SSBOs +void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); } +// Enables binding automapping using TIoMapper +void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); } +// Enables position.Y output negation in vertex shader + +// Fragile: currently within one stage: simple auto-assignment of location +void TShader::setAutoMapLocations(bool map) { intermediate->setAutoMapLocations(map); } +void TShader::addUniformLocationOverride(const char* name, int loc) +{ + intermediate->addUniformLocationOverride(name, loc); +} +void TShader::setUniformLocationBase(int base) +{ + intermediate->setUniformLocationBase(base); +} +void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); } +void TShader::setResourceSetBinding(const std::vector& base) { intermediate->setResourceSetBinding(base); } +void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); } + +void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); } + +void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); } +void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); } +void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); } + +void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); } +void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); } + +void TShader::addSourceText(const char* text, size_t len) { intermediate->addSourceText(text, len); } +void TShader::setSourceFile(const char* file) { intermediate->setSourceFile(file); } + +#ifdef ENABLE_HLSL +// See comment above TDefaultHlslIoMapper in iomapper.cpp: +void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); } +void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); } +#endif + +// +// Turn the shader strings into a parse tree in the TIntermediate. +// +// Returns true for success. +// +bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages, Includer& includer) +{ + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, + preamble, EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, overrideVersion, + forwardCompatible, messages, *intermediate, includer, sourceEntryPointName, + &environment, compileOnly); +} + +// Fill in a string with the result of preprocessing ShaderStrings +// Returns true if all extensions, pragmas and version strings were valid. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool TShader::preprocess(const TBuiltInResource* builtInResources, + int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, + std::string* output_string, + Includer& includer) +{ + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, + EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, overrideVersion, + forwardCompatible, message, includer, *intermediate, output_string, + &environment); +} + +const char* TShader::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TShader::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +TProgram::TProgram() : reflection(nullptr), linked(false) +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + for (int s = 0; s < EShLangCount; ++s) { + intermediate[s] = nullptr; + newedIntermediate[s] = false; + } +} + +TProgram::~TProgram() +{ + delete infoSink; + delete reflection; + + for (int s = 0; s < EShLangCount; ++s) + if (newedIntermediate[s]) + delete intermediate[s]; + + delete pool; +} + +// +// Merge the compilation units within each stage into a single TIntermediate. +// All starting compilation units need to be the result of calling TShader::parse(). +// +// Return true for success. +// +bool TProgram::link(EShMessages messages) +{ + if (linked) + return false; + linked = true; + + bool error = false; + + SetThreadPoolAllocator(pool); + + for (int s = 0; s < EShLangCount; ++s) { + if (! linkStage((EShLanguage)s, messages)) + error = true; + } + + if (!error) { + if (! crossStageCheck(messages)) + error = true; + } + + if (messages & EShMsgAST) { + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s] == nullptr) + continue; + intermediate[s]->output(*infoSink, true); + } + } + + return ! error; +} + +// +// Merge the compilation units within the given stage into a single TIntermediate. +// +// Return true for success. +// +bool TProgram::linkStage(EShLanguage stage, EShMessages messages) +{ + if (stages[stage].size() == 0) + return true; + + int numEsShaders = 0, numNonEsShaders = 0; + for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) { + if ((*it)->intermediate->getProfile() == EEsProfile) { + numEsShaders++; + } else { + numNonEsShaders++; + } + } + + if (numEsShaders > 0 && numNonEsShaders > 0) { + infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders"); + return false; + } else if (numEsShaders > 1) { + infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program"); + return false; + } + + // + // Be efficient for the common single compilation unit per stage case, + // reusing it's TIntermediate instead of merging into a new one. + // + TIntermediate *firstIntermediate = stages[stage].front()->intermediate; + if (stages[stage].size() == 1) + intermediate[stage] = firstIntermediate; + else { + intermediate[stage] = new TIntermediate(stage, + firstIntermediate->getVersion(), + firstIntermediate->getProfile()); + intermediate[stage]->setLimits(firstIntermediate->getLimits()); + if (firstIntermediate->getEnhancedMsgs()) + intermediate[stage]->setEnhancedMsgs(); + + // The new TIntermediate must use the same origin as the original TIntermediates. + // Otherwise linking will fail due to different coordinate systems. + if (firstIntermediate->getOriginUpperLeft()) { + intermediate[stage]->setOriginUpperLeft(); + } + intermediate[stage]->setSpv(firstIntermediate->getSpv()); + + newedIntermediate[stage] = true; + } + + if (messages & EShMsgAST) + infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n"; + + if (stages[stage].size() > 1) { + std::list::const_iterator it; + for (it = stages[stage].begin(); it != stages[stage].end(); ++it) + intermediate[stage]->merge(*infoSink, *(*it)->intermediate); + } + intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0); + + return intermediate[stage]->getNumErrors() == 0; +} + +// +// Check that there are no errors in linker objects accross stages +// +// Return true if no errors. +// +bool TProgram::crossStageCheck(EShMessages messages) { + + // make temporary intermediates to hold the linkage symbols for each linking interface + // while we do the checks + // Independent interfaces are: + // all uniform variables and blocks + // all buffer blocks + // all in/out on a stage boundary + + TVector activeStages; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) + activeStages.push_back(intermediate[s]); + } + + class TFinalLinkTraverser : public TIntermTraverser { + public: + TFinalLinkTraverser() { } + virtual ~TFinalLinkTraverser() { } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + // Implicitly size arrays. + // If an unsized array is left as unsized, it effectively + // becomes run-time sized. + symbol->getWritableType().adoptImplicitArraySizes(false); + } + } finalLinkTraverser; + + // no extra linking if there is only one stage + if (! (activeStages.size() > 1)) { + if (activeStages.size() == 1 && activeStages[0]->getTreeRoot()) { + activeStages[0]->getTreeRoot()->traverse(&finalLinkTraverser); + } + return true; + } + + // setup temporary tree to hold unfirom objects from different stages + TIntermediate* firstIntermediate = activeStages.front(); + TIntermediate uniforms(EShLangCount, + firstIntermediate->getVersion(), + firstIntermediate->getProfile()); + uniforms.setSpv(firstIntermediate->getSpv()); + + TIntermAggregate uniformObjects(EOpLinkerObjects); + TIntermAggregate root(EOpSequence); + root.getSequence().push_back(&uniformObjects); + uniforms.setTreeRoot(&root); + + bool error = false; + + // merge uniforms from all stages into a single intermediate + for (unsigned int i = 0; i < activeStages.size(); ++i) { + uniforms.mergeUniformObjects(*infoSink, *activeStages[i]); + } + error |= uniforms.getNumErrors() != 0; + + // update implicit array sizes across shader stages + for (unsigned int i = 0; i < activeStages.size(); ++i) { + activeStages[i]->mergeImplicitArraySizes(*infoSink, uniforms); + activeStages[i]->getTreeRoot()->traverse(&finalLinkTraverser); + } + + // copy final definition of global block back into each stage + for (unsigned int i = 0; i < activeStages.size(); ++i) { + // We only want to merge into already existing global uniform blocks. + // A stage that doesn't already know about the global doesn't care about it's content. + // Otherwise we end up pointing to the same object between different stages + // and that will break binding/set remappings + bool mergeExistingOnly = true; + activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms, mergeExistingOnly); + } + + // compare cross stage symbols for each stage boundary + for (unsigned int i = 1; i < activeStages.size(); ++i) { + activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i], messages); + error |= (activeStages[i - 1]->getNumErrors() != 0 || activeStages[i]->getNumErrors() != 0); + } + + // if requested, optimize cross stage IO + if (messages & EShMsgLinkTimeOptimization) { + for (unsigned int i = 1; i < activeStages.size(); ++i) { + activeStages[i - 1]->optimizeStageIO(*infoSink, *activeStages[i]); + } + } + + return !error; +} + +const char* TProgram::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TProgram::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +// +// Reflection implementation. +// + +unsigned int TObjectReflection::layoutLocation() const { return type->getQualifier().layoutLocation; } + +bool TProgram::buildReflection(int opts) +{ + if (! linked || reflection != nullptr) + return false; + + SetThreadPoolAllocator(pool); + + int firstStage = EShLangVertex, lastStage = EShLangFragment; + + if (opts & EShReflectionIntermediateIO) { + // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the + // boundaries for which stages generate pipeline inputs/outputs + firstStage = EShLangCount; + lastStage = 0; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + firstStage = std::min(firstStage, s); + lastStage = std::max(lastStage, s); + } + } + } + + reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage); + + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! reflection->addStage((EShLanguage)s, *intermediate[s])) + return false; + } + } + + return true; +} + +unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); } +int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); } +int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const + { return reflection->getPipeIOIndex(name, inOrOut); } + +int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); } +const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } +int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } +const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } +int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } +const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } +int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } +const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } +int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); } +const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); } +int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); } +const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); } +int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); } +const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); } +void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); } + +TIoMapResolver* TProgram::getGlslIoResolver(EShLanguage stage) { + auto *intermediate = getIntermediate(stage); + if (!intermediate) + return NULL; + return static_cast(new TDefaultGlslIoResolver(*intermediate)); +} +// +// I/O mapping implementation. +// +bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper) +{ + if (! linked) + return false; + + SetThreadPoolAllocator(pool); + + TIoMapper* ioMapper = nullptr; + TIoMapper defaultIOMapper; + if (pIoMapper == nullptr) + ioMapper = &defaultIOMapper; + else + ioMapper = pIoMapper; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver)) + return false; + } + } + + return ioMapper->doMap(pResolver, *infoSink); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp new file mode 100644 index 00000000..1d08797a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp @@ -0,0 +1,360 @@ +// +// Copyright(C) 2021 Advanced Micro Devices, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// GL_EXT_spirv_intrinsics +// +#include "../Include/intermediate.h" +#include "../Include/SpirvIntrinsics.h" +#include "../Include/Types.h" +#include "ParseHelper.h" + +namespace glslang { + +bool TSpirvTypeParameter::operator==(const TSpirvTypeParameter& rhs) const +{ + if (getAsConstant() != nullptr) + return getAsConstant()->getConstArray() == rhs.getAsConstant()->getConstArray(); + + assert(getAsType() != nullptr); + return *getAsType() == *rhs.getAsType(); +} + +// +// Handle SPIR-V requirements +// +TSpirvRequirement* TParseContext::makeSpirvRequirement(const TSourceLoc& loc, const TString& name, + const TIntermAggregate* extensions, + const TIntermAggregate* capabilities) +{ + TSpirvRequirement* spirvReq = new TSpirvRequirement; + + if (name == "extensions") { + assert(extensions); + for (auto extension : extensions->getSequence()) { + assert(extension->getAsConstantUnion()); + spirvReq->extensions.insert(*extension->getAsConstantUnion()->getConstArray()[0].getSConst()); + } + } else if (name == "capabilities") { + assert(capabilities); + for (auto capability : capabilities->getSequence()) { + assert(capability->getAsConstantUnion()); + spirvReq->capabilities.insert(capability->getAsConstantUnion()->getConstArray()[0].getIConst()); + } + } else + error(loc, "unknown SPIR-V requirement", name.c_str(), ""); + + return spirvReq; +} + +TSpirvRequirement* TParseContext::mergeSpirvRequirements(const TSourceLoc& loc, TSpirvRequirement* spirvReq1, + TSpirvRequirement* spirvReq2) +{ + // Merge the second SPIR-V requirement to the first one + if (!spirvReq2->extensions.empty()) { + if (spirvReq1->extensions.empty()) + spirvReq1->extensions = spirvReq2->extensions; + else + error(loc, "too many SPIR-V requirements", "extensions", ""); + } + + if (!spirvReq2->capabilities.empty()) { + if (spirvReq1->capabilities.empty()) + spirvReq1->capabilities = spirvReq2->capabilities; + else + error(loc, "too many SPIR-V requirements", "capabilities", ""); + } + + return spirvReq1; +} + +void TIntermediate::insertSpirvRequirement(const TSpirvRequirement* spirvReq) +{ + if (!spirvRequirement) + spirvRequirement = new TSpirvRequirement; + + for (auto extension : spirvReq->extensions) + spirvRequirement->extensions.insert(extension); + + for (auto capability : spirvReq->capabilities) + spirvRequirement->capabilities.insert(capability); +} + +// +// Handle SPIR-V execution modes +// +void TIntermediate::insertSpirvExecutionMode(int executionMode, const TIntermAggregate* args) +{ + if (!spirvExecutionMode) + spirvExecutionMode = new TSpirvExecutionMode; + + TVector extraOperands; + if (args) { + for (auto arg : args->getSequence()) { + auto extraOperand = arg->getAsConstantUnion(); + assert(extraOperand != nullptr); + extraOperands.push_back(extraOperand); + } + } + spirvExecutionMode->modes[executionMode] = extraOperands; +} + +void TIntermediate::insertSpirvExecutionModeId(int executionMode, const TIntermAggregate* args) +{ + if (!spirvExecutionMode) + spirvExecutionMode = new TSpirvExecutionMode; + + assert(args); + TVector extraOperands; + + for (auto arg : args->getSequence()) { + auto extraOperand = arg->getAsTyped(); + assert(extraOperand != nullptr && extraOperand->getQualifier().isConstant()); + extraOperands.push_back(extraOperand); + } + spirvExecutionMode->modeIds[executionMode] = extraOperands; +} + +// +// Handle SPIR-V decorate qualifiers +// +void TQualifier::setSpirvDecorate(int decoration, const TIntermAggregate* args) +{ + if (!spirvDecorate) + spirvDecorate = new TSpirvDecorate; + + TVector extraOperands; + if (args) { + for (auto arg : args->getSequence()) { + auto extraOperand = arg->getAsConstantUnion(); + assert(extraOperand != nullptr); + extraOperands.push_back(extraOperand); + } + } + spirvDecorate->decorates[decoration] = extraOperands; +} + +void TQualifier::setSpirvDecorateId(int decoration, const TIntermAggregate* args) +{ + if (!spirvDecorate) + spirvDecorate = new TSpirvDecorate; + + assert(args); + TVector extraOperands; + for (auto arg : args->getSequence()) { + auto extraOperand = arg->getAsTyped(); + assert(extraOperand != nullptr); + extraOperands.push_back(extraOperand); + } + spirvDecorate->decorateIds[decoration] = extraOperands; +} + +void TQualifier::setSpirvDecorateString(int decoration, const TIntermAggregate* args) +{ + if (!spirvDecorate) + spirvDecorate = new TSpirvDecorate; + + assert(args); + TVector extraOperands; + for (auto arg : args->getSequence()) { + auto extraOperand = arg->getAsConstantUnion(); + assert(extraOperand != nullptr); + extraOperands.push_back(extraOperand); + } + spirvDecorate->decorateStrings[decoration] = extraOperands; +} + +TString TQualifier::getSpirvDecorateQualifierString() const +{ + assert(spirvDecorate); + + TString qualifierString; + + const auto appendFloat = [&](float f) { qualifierString.append(std::to_string(f).c_str()); }; + const auto appendInt = [&](int i) { qualifierString.append(std::to_string(i).c_str()); }; + const auto appendUint = [&](unsigned int u) { qualifierString.append(std::to_string(u).c_str()); }; + const auto appendBool = [&](bool b) { qualifierString.append(std::to_string(b).c_str()); }; + const auto appendStr = [&](const char* s) { qualifierString.append(s); }; + + const auto appendDecorate = [&](const TIntermTyped* constant) { + if (constant->getAsConstantUnion()) { + auto& constArray = constant->getAsConstantUnion()->getConstArray(); + if (constant->getBasicType() == EbtFloat) { + float value = static_cast(constArray[0].getDConst()); + appendFloat(value); + } else if (constant->getBasicType() == EbtInt) { + int value = constArray[0].getIConst(); + appendInt(value); + } else if (constant->getBasicType() == EbtUint) { + unsigned value = constArray[0].getUConst(); + appendUint(value); + } else if (constant->getBasicType() == EbtBool) { + bool value = constArray[0].getBConst(); + appendBool(value); + } else if (constant->getBasicType() == EbtString) { + const TString* value = constArray[0].getSConst(); + appendStr(value->c_str()); + } else + assert(0); + } else { + assert(constant->getAsSymbolNode()); + appendStr(constant->getAsSymbolNode()->getName().c_str()); + } + }; + + for (auto& decorate : spirvDecorate->decorates) { + appendStr("spirv_decorate("); + appendInt(decorate.first); + for (auto extraOperand : decorate.second) { + appendStr(", "); + appendDecorate(extraOperand); + } + appendStr(") "); + } + + for (auto& decorateId : spirvDecorate->decorateIds) { + appendStr("spirv_decorate_id("); + appendInt(decorateId.first); + for (auto extraOperand : decorateId.second) { + appendStr(", "); + appendDecorate(extraOperand); + } + appendStr(") "); + } + + for (auto& decorateString : spirvDecorate->decorateStrings) { + appendStr("spirv_decorate_string("); + appendInt(decorateString.first); + for (auto extraOperand : decorateString.second) { + appendStr(", "); + appendDecorate(extraOperand); + } + appendStr(") "); + } + + return qualifierString; +} + +// +// Handle SPIR-V type specifiers +// +void TPublicType::setSpirvType(const TSpirvInstruction& spirvInst, const TSpirvTypeParameters* typeParams) +{ + if (!spirvType) + spirvType = new TSpirvType; + + basicType = EbtSpirvType; + spirvType->spirvInst = spirvInst; + if (typeParams) + spirvType->typeParams = *typeParams; +} + +TSpirvTypeParameters* TParseContext::makeSpirvTypeParameters(const TSourceLoc& loc, const TIntermConstantUnion* constant) +{ + TSpirvTypeParameters* spirvTypeParams = new TSpirvTypeParameters; + if (constant->getBasicType() != EbtFloat && + constant->getBasicType() != EbtInt && + constant->getBasicType() != EbtUint && + constant->getBasicType() != EbtBool && + constant->getBasicType() != EbtString) + error(loc, "this type not allowed", constant->getType().getBasicString(), ""); + else + spirvTypeParams->push_back(TSpirvTypeParameter(constant)); + + return spirvTypeParams; +} + +TSpirvTypeParameters* TParseContext::makeSpirvTypeParameters(const TSourceLoc& /* loc */, + const TPublicType& type) +{ + TSpirvTypeParameters* spirvTypeParams = new TSpirvTypeParameters; + spirvTypeParams->push_back(TSpirvTypeParameter(new TType(type))); + return spirvTypeParams; +} + +TSpirvTypeParameters* TParseContext::mergeSpirvTypeParameters(TSpirvTypeParameters* spirvTypeParams1, TSpirvTypeParameters* spirvTypeParams2) +{ + // Merge SPIR-V type parameters of the second one to the first one + for (const auto& spirvTypeParam : *spirvTypeParams2) + spirvTypeParams1->push_back(spirvTypeParam); + return spirvTypeParams1; +} + +// +// Handle SPIR-V instruction qualifiers +// +TSpirvInstruction* TParseContext::makeSpirvInstruction(const TSourceLoc& loc, const TString& name, const TString& value) +{ + TSpirvInstruction* spirvInst = new TSpirvInstruction; + if (name == "set") + spirvInst->set = value; + else + error(loc, "unknown SPIR-V instruction qualifier", name.c_str(), ""); + + return spirvInst; +} + +TSpirvInstruction* TParseContext::makeSpirvInstruction(const TSourceLoc& loc, const TString& name, int value) +{ + TSpirvInstruction* spirvInstuction = new TSpirvInstruction; + if (name == "id") + spirvInstuction->id = value; + else + error(loc, "unknown SPIR-V instruction qualifier", name.c_str(), ""); + + return spirvInstuction; +} + +TSpirvInstruction* TParseContext::mergeSpirvInstruction(const TSourceLoc& loc, TSpirvInstruction* spirvInst1, TSpirvInstruction* spirvInst2) +{ + // Merge qualifiers of the second SPIR-V instruction to those of the first one + if (!spirvInst2->set.empty()) { + if (spirvInst1->set.empty()) + spirvInst1->set = spirvInst2->set; + else + error(loc, "too many SPIR-V instruction qualifiers", "spirv_instruction", "(set)"); + } + + if (spirvInst2->id != -1) { + if (spirvInst1->id == -1) + spirvInst1->id = spirvInst2->id; + else + error(loc, "too many SPIR-V instruction qualifiers", "spirv_instruction", "(id)"); + } + + return spirvInst1; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.cpp new file mode 100644 index 00000000..fcfa5df0 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.cpp @@ -0,0 +1,501 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Symbol table for parsing. Most functionality and main ideas +// are documented in the header file. +// + +#include "SymbolTable.h" + +namespace glslang { + +// +// TType helper function needs a place to live. +// + +// +// Recursively generate mangled names. +// +void TType::buildMangledName(TString& mangledName) const +{ + if (isMatrix()) + mangledName += 'm'; + else if (isVector()) + mangledName += 'v'; + + if (isCoopVecNV()) + mangledName += "coopvec"; + + switch (basicType) { + case EbtFloat: mangledName += 'f'; break; + case EbtInt: mangledName += 'i'; break; + case EbtUint: mangledName += 'u'; break; + case EbtBool: mangledName += 'b'; break; + case EbtDouble: mangledName += 'd'; break; + case EbtFloat16: mangledName += "f16"; break; + case EbtBFloat16: mangledName += "bf16"; break; + case EbtInt8: mangledName += "i8"; break; + case EbtUint8: mangledName += "u8"; break; + case EbtInt16: mangledName += "i16"; break; + case EbtUint16: mangledName += "u16"; break; + case EbtInt64: mangledName += "i64"; break; + case EbtUint64: mangledName += "u64"; break; + case EbtAtomicUint: mangledName += "au"; break; + case EbtAccStruct: mangledName += "as"; break; + case EbtRayQuery: mangledName += "rq"; break; + case EbtSpirvType: mangledName += "spv-t"; break; + case EbtHitObjectNV: mangledName += "ho"; break; + case EbtTensorLayoutNV: mangledName += "tl"; break; + case EbtTensorViewNV: mangledName += "tv"; break; + case EbtSampler: + switch (sampler.type) { + case EbtFloat16: mangledName += "f16"; break; + case EbtInt: mangledName += "i"; break; + case EbtUint: mangledName += "u"; break; + case EbtInt64: mangledName += "i64"; break; + case EbtUint64: mangledName += "u64"; break; + default: break; // some compilers want this + } + if (sampler.isImageClass()) + mangledName += "I"; // a normal image or subpass + else if (sampler.isPureSampler()) + mangledName += "p"; // a "pure" sampler + else if (!sampler.isCombined()) + mangledName += "t"; // a "pure" texture + else + mangledName += "s"; // traditional combined sampler + if (sampler.isArrayed()) + mangledName += "A"; + if (sampler.isShadow()) + mangledName += "S"; + if (sampler.isExternal()) + mangledName += "E"; + if (sampler.isYuv()) + mangledName += "Y"; + switch (sampler.dim) { + case Esd2D: mangledName += "2"; break; + case Esd3D: mangledName += "3"; break; + case EsdCube: mangledName += "C"; break; + case Esd1D: mangledName += "1"; break; + case EsdRect: mangledName += "R2"; break; + case EsdBuffer: mangledName += "B"; break; + case EsdSubpass: mangledName += "P"; break; + default: break; // some compilers want this + } + +#ifdef ENABLE_HLSL + if (sampler.hasReturnStruct()) { + // Name mangle for sampler return struct uses struct table index. + mangledName += "-tx-struct"; + + char text[16]; // plenty enough space for the small integers. + snprintf(text, sizeof(text), "%u-", sampler.getStructReturnIndex()); + mangledName += text; + } else { + switch (sampler.getVectorSize()) { + case 1: mangledName += "1"; break; + case 2: mangledName += "2"; break; + case 3: mangledName += "3"; break; + case 4: break; // default to prior name mangle behavior + } + } +#endif + + if (sampler.isMultiSample()) + mangledName += "M"; + break; + case EbtStruct: + case EbtBlock: + if (basicType == EbtStruct) + mangledName += "struct-"; + else + mangledName += "block-"; + if (typeName) + mangledName += *typeName; + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->getBasicType() == EbtVoid) + continue; + mangledName += '-'; + (*structure)[i].type->buildMangledName(mangledName); + } + break; + default: + break; + } + + if (getVectorSize() > 0) + mangledName += static_cast('0' + getVectorSize()); + else { + mangledName += static_cast('0' + getMatrixCols()); + mangledName += static_cast('0' + getMatrixRows()); + } + + if (typeParameters) { + const int maxSize = 11; + char buf[maxSize]; + for (int i = 0; i < typeParameters->arraySizes->getNumDims(); ++i) { + if (typeParameters->arraySizes->getDimNode(i)) { + if (typeParameters->arraySizes->getDimNode(i)->getAsSymbolNode()) + snprintf(buf, maxSize, "s%lld", typeParameters->arraySizes->getDimNode(i)->getAsSymbolNode()->getId()); + else + snprintf(buf, maxSize, "s%p", typeParameters->arraySizes->getDimNode(i)); + } else + snprintf(buf, maxSize, "%d", typeParameters->arraySizes->getDimSize(i)); + mangledName += '<'; + mangledName += buf; + mangledName += '>'; + } + } + + if (arraySizes) { + const int maxSize = 11; + char buf[maxSize]; + for (int i = 0; i < arraySizes->getNumDims(); ++i) { + if (arraySizes->getDimNode(i)) { + if (arraySizes->getDimNode(i)->getAsSymbolNode()) + snprintf(buf, maxSize, "s%lld", arraySizes->getDimNode(i)->getAsSymbolNode()->getId()); + else + snprintf(buf, maxSize, "s%p", (void*)(arraySizes->getDimNode(i))); + } else + snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i)); + mangledName += '['; + mangledName += buf; + mangledName += ']'; + } + } +} + +// +// Dump functions. +// + +void TSymbol::dumpExtensions(TInfoSink& infoSink) const +{ + int numExtensions = getNumExtensions(); + if (numExtensions) { + infoSink.debug << " <"; + + for (int i = 0; i < numExtensions; i++) + infoSink.debug << getExtensions()[i] << ","; + + infoSink.debug << ">"; + } +} + +void TVariable::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << type.getCompleteString(); + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " + << type.getBasicTypeString(); + + if (type.isArray()) + infoSink.debug << "[0]"; + } + + infoSink.debug << "\n"; +} + +void TFunction::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << returnType.getCompleteString() << " " << getName().c_str() + << "("; + + int numParams = getParamCount(); + for (int i = 0; i < numParams; i++) { + const TParameter ¶m = parameters[i]; + infoSink.debug << param.type->getCompleteString() << " " + << (param.type->isStruct() ? "of " + param.type->getTypeName() + " " : "") + << (param.name ? *param.name : "") << (i < numParams - 1 ? "," : ""); + } + + infoSink.debug << ")"; + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << returnType.getBasicTypeString() << " " + << getMangledName().c_str() << "n"; + } + + infoSink.debug << "\n"; +} + +void TAnonMember::dump(TInfoSink& TInfoSink, bool) const +{ + TInfoSink.debug << "anonymous member " << getMemberNumber() << " of " << getAnonContainer().getName().c_str() + << "\n"; +} + +void TSymbolTableLevel::dump(TInfoSink& infoSink, bool complete) const +{ + tLevel::const_iterator it; + for (it = level.begin(); it != level.end(); ++it) + (*it).second->dump(infoSink, complete); +} + +void TSymbolTable::dump(TInfoSink& infoSink, bool complete) const +{ + for (int level = currentLevel(); level >= 0; --level) { + infoSink.debug << "LEVEL " << level << "\n"; + table[level]->dump(infoSink, complete); + } +} + +// +// Functions have buried pointers to delete. +// +TFunction::~TFunction() +{ + for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i) + delete (*i).type; +} + +// +// Symbol table levels are a map of pointers to symbols that have to be deleted. +// +TSymbolTableLevel::~TSymbolTableLevel() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) { + const TString& name = it->first; + auto retargetIter = std::find_if(retargetedSymbols.begin(), retargetedSymbols.end(), + [&name](const std::pair& i) { return i.first == name; }); + if (retargetIter == retargetedSymbols.end()) + delete (*it).second; + } + + + delete [] defaultPrecision; +} + +// +// Change all function entries in the table with the non-mangled name +// to be related to the provided built-in operation. +// +void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TFunction* function = (*candidate).second->getAsFunction(); + function->relateToOperator(op); + } else + break; + ++candidate; + } +} + +// Make all function overloads of the given name require an extension(s). +// Should only be used for a version/profile that actually needs the extension(s). +void TSymbolTableLevel::setFunctionExtensions(const char* name, int num, const char* const extensions[]) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TSymbol* symbol = candidate->second; + symbol->setExtensions(num, extensions); + } else + break; + ++candidate; + } +} + +// Make a single function require an extension(s). i.e., this will only set the extensions for the symbol that matches 'name' exactly. +// This is different from setFunctionExtensions, which uses std::map::lower_bound to effectively set all symbols that start with 'name'. +// Should only be used for a version/profile that actually needs the extension(s). +void TSymbolTableLevel::setSingleFunctionExtensions(const char* name, int num, const char* const extensions[]) +{ + if (auto candidate = level.find(name); candidate != level.end()) { + candidate->second->setExtensions(num, extensions); + } +} + +// +// Make all symbols in this table level read only. +// +void TSymbolTableLevel::readOnly() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + (*it).second->makeReadOnly(); +} + +// +// Copy a symbol, but the copy is writable; call readOnly() afterward if that's not desired. +// +TSymbol::TSymbol(const TSymbol& copyOf) +{ + name = NewPoolTString(copyOf.name->c_str()); + mangledName = NewPoolTString(copyOf.mangledName->c_str()); + uniqueId = copyOf.uniqueId; + writable = true; +} + +TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf) +{ + type.deepCopy(copyOf.type); + userType = copyOf.userType; + + // we don't support specialization-constant subtrees in cloned tables, only extensions + constSubtree = nullptr; + extensions = nullptr; + memberExtensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + if (copyOf.hasMemberExtensions()) { + for (int m = 0; m < (int)copyOf.type.getStruct()->size(); ++m) { + if (copyOf.getNumMemberExtensions(m) > 0) + setMemberExtensions(m, copyOf.getNumMemberExtensions(m), copyOf.getMemberExtensions(m)); + } + } + + if (! copyOf.constArray.empty()) { + assert(! copyOf.type.isStruct()); + TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size()); + constArray = newArray; + } +} + +TVariable* TVariable::clone() const +{ + TVariable *variable = new TVariable(*this); + + return variable; +} + +TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf) +{ + for (unsigned int i = 0; i < copyOf.parameters.size(); ++i) { + TParameter param{}; + parameters.push_back(param); + (void)parameters.back().copyParam(copyOf.parameters[i]); + } + + extensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + returnType.deepCopy(copyOf.returnType); + mangledName = copyOf.mangledName; + op = copyOf.op; + defined = copyOf.defined; + prototyped = copyOf.prototyped; + implicitThis = copyOf.implicitThis; + illegalImplicitThis = copyOf.illegalImplicitThis; + defaultParamCount = copyOf.defaultParamCount; + spirvInst = copyOf.spirvInst; +} + +TFunction* TFunction::clone() const +{ + TFunction *function = new TFunction(*this); + + return function; +} + +TAnonMember* TAnonMember::clone() const +{ + // Anonymous members of a given block should be cloned at a higher level, + // where they can all be assured to still end up pointing to a single + // copy of the original container. + assert(0); + + return nullptr; +} + +TSymbolTableLevel* TSymbolTableLevel::clone() const +{ + TSymbolTableLevel *symTableLevel = new TSymbolTableLevel(); + symTableLevel->anonId = anonId; + symTableLevel->thisLevel = thisLevel; + symTableLevel->retargetedSymbols.clear(); + for (auto &s : retargetedSymbols) { + symTableLevel->retargetedSymbols.push_back({s.first, s.second}); + } + std::vector containerCopied(anonId, false); + tLevel::const_iterator iter; + for (iter = level.begin(); iter != level.end(); ++iter) { + const TAnonMember* anon = iter->second->getAsAnonMember(); + if (anon) { + // Insert all the anonymous members of this same container at once, + // avoid inserting the remaining members in the future, once this has been done, + // allowing them to all be part of the same new container. + if (! containerCopied[anon->getAnonId()]) { + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + // insert the container and all its members + symTableLevel->insert(*container, false); + containerCopied[anon->getAnonId()] = true; + } + } else { + const TString& name = iter->first; + auto retargetIter = std::find_if(retargetedSymbols.begin(), retargetedSymbols.end(), + [&name](const std::pair& i) { return i.first == name; }); + if (retargetIter != retargetedSymbols.end()) + continue; + symTableLevel->insert(*iter->second->clone(), false); + } + } + // Now point retargeted symbols to the newly created versions of them + for (auto &s : retargetedSymbols) { + TSymbol* sym = symTableLevel->find(s.second); + if (!sym) + continue; + symTableLevel->insert(s.first, sym); + } + + return symTableLevel; +} + +void TSymbolTable::copyTable(const TSymbolTable& copyOf) +{ + assert(adoptedLevels == copyOf.adoptedLevels); + + uniqueId = copyOf.uniqueId; + noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations; + separateNameSpaces = copyOf.separateNameSpaces; + for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i) + table.push_back(copyOf.table[i]->clone()); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.h new file mode 100644 index 00000000..6d1a0c8e --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/SymbolTable.h @@ -0,0 +1,957 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SYMBOL_TABLE_INCLUDED_ +#define _SYMBOL_TABLE_INCLUDED_ + +// +// Symbol table for parsing. Has these design characteristics: +// +// * Same symbol table can be used to compile many shaders, to preserve +// effort of creating and loading with the large numbers of built-in +// symbols. +// +// --> This requires a copy mechanism, so initial pools used to create +// the shared information can be popped. Done through "clone" +// methods. +// +// * Name mangling will be used to give each function a unique name +// so that symbol table lookups are never ambiguous. This allows +// a simpler symbol table structure. +// +// * Pushing and popping of scope, so symbol table will really be a stack +// of symbol tables. Searched from the top, with new inserts going into +// the top. +// +// * Constants: Compile time constant symbols will keep their values +// in the symbol table. The parser can substitute constants at parse +// time, including doing constant folding and constant propagation. +// +// * No temporaries: Temporaries made from operations (+, --, .xy, etc.) +// are tracked in the intermediate representation, not the symbol table. +// + +#include "../Include/Common.h" +#include "../Include/intermediate.h" +#include "../Include/InfoSink.h" + +namespace glslang { + +// +// Symbol base class. (Can build functions or variables out of these...) +// + +class TVariable; +class TFunction; +class TAnonMember; + +typedef TVector TExtensionList; + +class TSymbol { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + explicit TSymbol(const TString *n, const TString *mn) : name(n), mangledName(mn), uniqueId(0), extensions(nullptr), writable(true) { } + explicit TSymbol(const TString *n) : TSymbol(n, n) { } + virtual TSymbol* clone() const = 0; + virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool + + virtual const TString& getName() const { return *name; } + virtual void changeName(const TString* newName) { name = newName; } + virtual void addPrefix(const char* prefix) + { + TString newName(prefix); + newName.append(*name); + changeName(NewPoolTString(newName.c_str())); + } + virtual const TString& getMangledName() const { return *mangledName; } + virtual TFunction* getAsFunction() { return nullptr; } + virtual const TFunction* getAsFunction() const { return nullptr; } + virtual TVariable* getAsVariable() { return nullptr; } + virtual const TVariable* getAsVariable() const { return nullptr; } + virtual const TAnonMember* getAsAnonMember() const { return nullptr; } + virtual const TType& getType() const = 0; + virtual TType& getWritableType() = 0; + virtual void setUniqueId(long long id) { uniqueId = id; } + virtual long long getUniqueId() const { return uniqueId; } + virtual void setExtensions(int numExts, const char* const exts[]) + { + assert(extensions == nullptr); + assert(numExts > 0); + extensions = NewPoolObject(extensions); + for (int e = 0; e < numExts; ++e) + extensions->push_back(exts[e]); + } + virtual int getNumExtensions() const { return extensions == nullptr ? 0 : (int)extensions->size(); } + virtual const char** getExtensions() const { return extensions->data(); } + + virtual void dump(TInfoSink& infoSink, bool complete = false) const = 0; + void dumpExtensions(TInfoSink& infoSink) const; + + virtual bool isReadOnly() const { return ! writable; } + virtual void makeReadOnly() { writable = false; } + +protected: + explicit TSymbol(const TSymbol&); + TSymbol& operator=(const TSymbol&); + + const TString *name; + const TString *mangledName; + unsigned long long uniqueId; // For cross-scope comparing during code generation + + // For tracking what extensions must be present + // (don't use if correct version/profile is present). + TExtensionList* extensions; // an array of pointers to existing constant char strings + + // + // N.B.: Non-const functions that will be generally used should assert on this, + // to avoid overwriting shared symbol-table information. + // + bool writable; +}; + +// +// Variable class, meaning a symbol that's not a function. +// +// There could be a separate class hierarchy for Constant variables; +// Only one of int, bool, or float, (or none) is correct for +// any particular use, but it's easy to do this way, and doesn't +// seem worth having separate classes, and "getConst" can't simply return +// different values for different types polymorphically, so this is +// just simple and pragmatic. +// +class TVariable : public TSymbol { +public: + TVariable(const TString *name, const TType& t, bool uT = false ) + : TVariable(name, name, t, uT) {} + TVariable(const TString *name, const TString *mangledName, const TType& t, bool uT = false ) + : TSymbol(name, mangledName), + userType(uT), + constSubtree(nullptr), + memberExtensions(nullptr), + anonId(-1) + { type.shallowCopy(t); } + virtual TVariable* clone() const; + virtual ~TVariable() { } + + virtual TVariable* getAsVariable() { return this; } + virtual const TVariable* getAsVariable() const { return this; } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { assert(writable); return type; } + virtual bool isUserType() const { return userType; } + virtual const TConstUnionArray& getConstArray() const { return constArray; } + virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } + virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } + virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + virtual TIntermTyped* getConstSubtree() const { return constSubtree; } + virtual void setAnonId(int i) { anonId = i; } + virtual int getAnonId() const { return anonId; } + + virtual void setMemberExtensions(int member, int numExts, const char* const exts[]) + { + assert(type.isStruct()); + assert(numExts > 0); + if (memberExtensions == nullptr) { + memberExtensions = NewPoolObject(memberExtensions); + memberExtensions->resize(type.getStruct()->size()); + } + for (int e = 0; e < numExts; ++e) + (*memberExtensions)[member].push_back(exts[e]); + } + virtual bool hasMemberExtensions() const { return memberExtensions != nullptr; } + virtual int getNumMemberExtensions(int member) const + { + return memberExtensions == nullptr ? 0 : (int)(*memberExtensions)[member].size(); + } + virtual const char** getMemberExtensions(int member) const { return (*memberExtensions)[member].data(); } + + virtual void dump(TInfoSink& infoSink, bool complete = false) const; + +protected: + explicit TVariable(const TVariable&); + TVariable& operator=(const TVariable&); + + TType type; + bool userType; + + // we are assuming that Pool Allocator will free the memory allocated to unionArray + // when this object is destroyed + + TConstUnionArray constArray; // for compile-time constant value + TIntermTyped* constSubtree; // for specialization constant computation + TVector* memberExtensions; // per-member extension list, allocated only when needed + int anonId; // the ID used for anonymous blocks: TODO: see if uniqueId could serve a dual purpose +}; + +// +// The function sub-class of symbols and the parser will need to +// share this definition of a function parameter. +// +struct TParameter { + TString *name; + TType* type; + TIntermTyped* defaultValue; + TParameter& copyParam(const TParameter& param) + { + if (param.name) + name = NewPoolTString(param.name->c_str()); + else + name = nullptr; + type = param.type->clone(); + defaultValue = param.defaultValue; + return *this; + } + TBuiltInVariable getDeclaredBuiltIn() const { return type->getQualifier().declaredBuiltIn; } +}; + +// +// The function sub-class of a symbol. +// +class TFunction : public TSymbol { +public: + explicit TFunction(TOperator o) : + TSymbol(nullptr), + op(o), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) { } + TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) : + TSymbol(name), + mangledName(*name + '('), + op(tOp), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0), + linkType(ELinkNone) + { + returnType.shallowCopy(retType); + declaredBuiltIn = retType.getQualifier().builtIn; + } + virtual TFunction* clone() const override; + virtual ~TFunction(); + + virtual TFunction* getAsFunction() override { return this; } + virtual const TFunction* getAsFunction() const override { return this; } + + // Install 'p' as the (non-'this') last parameter. + // Non-'this' parameters are reflected in both the list of parameters and the + // mangled name. + virtual void addParameter(TParameter& p) + { + assert(writable); + parameters.push_back(p); + p.type->appendMangledName(mangledName); + + if (p.defaultValue != nullptr) + defaultParamCount++; + } + + // Install 'this' as the first parameter. + // 'this' is reflected in the list of parameters, but not the mangled name. + virtual void addThisParameter(TType& type, const char* name) + { + TParameter p = { NewPoolTString(name), new TType, nullptr }; + p.type->shallowCopy(type); + parameters.insert(parameters.begin(), p); + } + + virtual void addPrefix(const char* prefix) override + { + TSymbol::addPrefix(prefix); + mangledName.insert(0, prefix); + } + + virtual void removePrefix(const TString& prefix) + { + assert(mangledName.compare(0, prefix.size(), prefix) == 0); + mangledName.erase(0, prefix.size()); + } + + virtual const TString& getMangledName() const override { return mangledName; } + virtual const TType& getType() const override { return returnType; } + virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; } + virtual TType& getWritableType() override { return returnType; } + virtual void relateToOperator(TOperator o) { assert(writable); op = o; } + virtual TOperator getBuiltInOp() const { return op; } + virtual void setDefined() { assert(writable); defined = true; } + virtual bool isDefined() const { return defined; } + virtual void setPrototyped() { assert(writable); prototyped = true; } + virtual bool isPrototyped() const { return prototyped; } + virtual void setImplicitThis() { assert(writable); implicitThis = true; } + virtual bool hasImplicitThis() const { return implicitThis; } + virtual void setIllegalImplicitThis() { assert(writable); illegalImplicitThis = true; } + virtual bool hasIllegalImplicitThis() const { return illegalImplicitThis; } + + // Return total number of parameters + virtual int getParamCount() const { return static_cast(parameters.size()); } + // Return number of parameters with default values. + virtual int getDefaultParamCount() const { return defaultParamCount; } + // Return number of fixed parameters (without default values) + virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); } + + virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; } + virtual const TParameter& operator[](int i) const { return parameters[i]; } + const TQualifier& getQualifier() const { return returnType.getQualifier(); } + + virtual void setSpirvInstruction(const TSpirvInstruction& inst) + { + relateToOperator(EOpSpirvInst); + spirvInst = inst; + } + virtual const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; } + + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; + + void setExport() { linkType = ELinkExport; } + TLinkType getLinkType() const { return linkType; } + +protected: + explicit TFunction(const TFunction&); + TFunction& operator=(const TFunction&); + + typedef TVector TParamList; + TParamList parameters; + TType returnType; + TBuiltInVariable declaredBuiltIn; + + TString mangledName; + TOperator op; + bool defined; + bool prototyped; + bool implicitThis; // True if this function is allowed to see all members of 'this' + bool illegalImplicitThis; // True if this function is not supposed to have access to dynamic members of 'this', + // even if it finds member variables in the symbol table. + // This is important for a static member function that has member variables in scope, + // but is not allowed to use them, or see hidden symbols instead. + int defaultParamCount; + + TSpirvInstruction spirvInst; // SPIR-V instruction qualifiers + TLinkType linkType; +}; + +// +// Members of anonymous blocks are a kind of TSymbol. They are not hidden in +// the symbol table behind a container; rather they are visible and point to +// their anonymous container. (The anonymous container is found through the +// member, not the other way around.) +// +class TAnonMember : public TSymbol { +public: + TAnonMember(const TString* n, unsigned int m, TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { } + virtual TAnonMember* clone() const override; + virtual ~TAnonMember() { } + + virtual const TAnonMember* getAsAnonMember() const override { return this; } + virtual const TVariable& getAnonContainer() const { return anonContainer; } + virtual unsigned int getMemberNumber() const { return memberNumber; } + + virtual const TType& getType() const override + { + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual TType& getWritableType() override + { + assert(writable); + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual void setExtensions(int numExts, const char* const exts[]) override + { + anonContainer.setMemberExtensions(memberNumber, numExts, exts); + } + virtual int getNumExtensions() const override { return anonContainer.getNumMemberExtensions(memberNumber); } + virtual const char** getExtensions() const override { return anonContainer.getMemberExtensions(memberNumber); } + + virtual int getAnonId() const { return anonId; } + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; + +protected: + explicit TAnonMember(const TAnonMember&); + TAnonMember& operator=(const TAnonMember&); + + TVariable& anonContainer; + unsigned int memberNumber; + int anonId; +}; + +class TSymbolTableLevel { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TSymbolTableLevel() : defaultPrecision(nullptr), anonId(0), thisLevel(false) { } + ~TSymbolTableLevel(); + + bool insert(const TString& name, TSymbol* symbol) { + return level.insert(tLevelPair(name, symbol)).second; + } + + bool insert(TSymbol& symbol, bool separateNameSpaces, const TString& forcedKeyName = TString()) + { + // + // returning true means symbol was added to the table with no semantic errors + // + const TString& name = symbol.getName(); + if (forcedKeyName.length()) { + return level.insert(tLevelPair(forcedKeyName, &symbol)).second; + } + else if (name == "") { + symbol.getAsVariable()->setAnonId(anonId++); + // An empty name means an anonymous container, exposing its members to the external scope. + // Give it a name and insert its members in the symbol table, pointing to the container. + char buf[20]; + snprintf(buf, 20, "%s%d", AnonymousPrefix, symbol.getAsVariable()->getAnonId()); + symbol.changeName(NewPoolTString(buf)); + + return insertAnonymousMembers(symbol, 0); + } else { + // Check for redefinition errors: + // - STL itself will tell us if there is a direct name collision, with name mangling, at this level + // - additionally, check for function-redefining-variable name collisions + const TString& insertName = symbol.getMangledName(); + if (symbol.getAsFunction()) { + // make sure there isn't a variable of this name + if (! separateNameSpaces && level.find(name) != level.end()) + return false; + + // insert, and whatever happens is okay + level.insert(tLevelPair(insertName, &symbol)); + + return true; + } else + return level.insert(tLevelPair(insertName, &symbol)).second; + } + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + // Only supporting amend of anonymous blocks so far. + if (IsAnonymous(symbol.getName())) + return insertAnonymousMembers(symbol, firstNewMember); + else + return false; + } + + bool insertAnonymousMembers(TSymbol& symbol, int firstMember) + { + const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); + for (unsigned int m = firstMember; m < types.size(); ++m) { + TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), symbol.getAsVariable()->getAnonId()); + if (! level.insert(tLevelPair(member->getMangledName(), member)).second) + return false; + } + + return true; + } + + void retargetSymbol(const TString& from, const TString& to) { + tLevel::const_iterator fromIt = level.find(from); + tLevel::const_iterator toIt = level.find(to); + if (fromIt == level.end() || toIt == level.end()) + return; + delete fromIt->second; + level[from] = toIt->second; + retargetedSymbols.push_back({from, to}); + } + + TSymbol* find(const TString& name) const + { + tLevel::const_iterator it = level.find(name); + if (it == level.end()) + return nullptr; + else + return (*it).second; + } + + void findFunctionNameList(const TString& name, TVector& list) + { + size_t parenAt = name.find_first_of('('); + TString base(name, 0, parenAt + 1); + + tLevel::const_iterator begin = level.lower_bound(base); + base[parenAt] = ')'; // assume ')' is lexically after '(' + tLevel::const_iterator end = level.upper_bound(base); + for (tLevel::const_iterator it = begin; it != end; ++it) + list.push_back(it->second->getAsFunction()); + } + + // See if there is already a function in the table having the given non-function-style name. + bool hasFunctionName(const TString& name) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) + + return true; + } + + return false; + } + + // See if there is a variable at this level having the given non-function-style name. + // Return true if name is found, and set variable to true if the name was a variable. + bool findFunctionVariableName(const TString& name, bool& variable) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt == candidateName.npos) { + // not a mangled name + if (candidateName == name) { + // found a variable name match + variable = true; + return true; + } + } else { + // a mangled name + if (candidateName.compare(0, parenAt, name) == 0) { + // found a function name match + variable = false; + return true; + } + } + } + + return false; + } + + // Use this to do a lazy 'push' of precision defaults the first time + // a precision statement is seen in a new scope. Leave it at 0 for + // when no push was needed. Thus, it is not the current defaults, + // it is what to restore the defaults to when popping a level. + void setPreviousDefaultPrecisions(const TPrecisionQualifier *p) + { + // can call multiple times at one scope, will only latch on first call, + // as we're tracking the previous scope's values, not the current values + if (defaultPrecision != nullptr) + return; + + defaultPrecision = new TPrecisionQualifier[EbtNumTypes]; + for (int t = 0; t < EbtNumTypes; ++t) + defaultPrecision[t] = p[t]; + } + + void getPreviousDefaultPrecisions(TPrecisionQualifier *p) + { + // can be called for table level pops that didn't set the + // defaults + if (defaultPrecision == nullptr || p == nullptr) + return; + + for (int t = 0; t < EbtNumTypes; ++t) + p[t] = defaultPrecision[t]; + } + + void relateToOperator(const char* name, TOperator op); + void setFunctionExtensions(const char* name, int num, const char* const extensions[]); + void setSingleFunctionExtensions(const char* name, int num, const char* const extensions[]); + void dump(TInfoSink& infoSink, bool complete = false) const; + TSymbolTableLevel* clone() const; + void readOnly(); + + void setThisLevel() { thisLevel = true; } + bool isThisLevel() const { return thisLevel; } + +protected: + explicit TSymbolTableLevel(TSymbolTableLevel&); + TSymbolTableLevel& operator=(TSymbolTableLevel&); + + typedef std::map, pool_allocator > > tLevel; + typedef const tLevel::value_type tLevelPair; + typedef std::pair tInsertResult; + + tLevel level; // named mappings + TPrecisionQualifier *defaultPrecision; + // pair + TVector> retargetedSymbols; + int anonId; + bool thisLevel; // True if this level of the symbol table is a structure scope containing member function + // that are supposed to see anonymous access to member variables. +}; + +class TSymbolTable { +public: + TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0) + { + // + // This symbol table cannot be used until push() is called. + // + } + ~TSymbolTable() + { + // this can be called explicitly; safest to code it so it can be called multiple times + + // don't deallocate levels passed in from elsewhere + while (table.size() > adoptedLevels) + pop(nullptr); + } + + void adoptLevels(TSymbolTable& symTable) + { + for (unsigned int level = 0; level < symTable.table.size(); ++level) { + table.push_back(symTable.table[level]); + ++adoptedLevels; + } + uniqueId = symTable.uniqueId; + noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; + separateNameSpaces = symTable.separateNameSpaces; + } + + // + // While level adopting is generic, the methods below enact a the following + // convention for levels: + // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables + // 1: per-stage built-ins, shared across all compiles, but a different copy per stage + // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins + // 3: user-shader globals + // +protected: + static const uint32_t LevelFlagBitOffset = 56; + static const int globalLevel = 3; + static bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels + static bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals + static bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals +public: + bool isEmpty() { return table.size() == 0; } + bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } + bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } + static bool isBuiltInSymbol(long long uniqueId) { + int level = static_cast(uniqueId >> LevelFlagBitOffset); + return isBuiltInLevel(level); + } + static constexpr uint64_t uniqueIdMask = (1LL << LevelFlagBitOffset) - 1; + static const uint32_t MaxLevelInUniqueID = 127; + void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } + void setSeparateNameSpaces() { separateNameSpaces = true; } + + void push() + { + table.push_back(new TSymbolTableLevel); + updateUniqueIdLevelFlag(); + } + + // Make a new symbol-table level to represent the scope introduced by a structure + // containing member functions, such that the member functions can find anonymous + // references to member variables. + // + // 'thisSymbol' should have a name of "" to trigger anonymous structure-member + // symbol finds. + void pushThis(TSymbol& thisSymbol) + { + assert(thisSymbol.getName().size() == 0); + table.push_back(new TSymbolTableLevel); + updateUniqueIdLevelFlag(); + table.back()->setThisLevel(); + insert(thisSymbol); + } + + void pop(TPrecisionQualifier *p) + { + table[currentLevel()]->getPreviousDefaultPrecisions(p); + delete table.back(); + table.pop_back(); + updateUniqueIdLevelFlag(); + } + + // + // Insert a visible symbol into the symbol table so it can + // be found later by name. + // + // Returns false if the was a name collision. + // + bool insert(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + + // make sure there isn't a function of this variable name + if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName())) + return false; + + // check for not overloading or redefining a built-in function + if (noBuiltInRedeclarations) { + if (atGlobalLevel() && currentLevel() > 0) { + if (table[0]->hasFunctionName(symbol.getName())) + return false; + if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName())) + return false; + } + } + + return table[currentLevel()]->insert(symbol, separateNameSpaces); + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + return table[currentLevel()]->amend(symbol, firstNewMember); + } + + // Update the level info in symbol's unique ID to current level + void amendSymbolIdLevel(TSymbol& symbol) + { + // clamp level to avoid overflow + uint64_t level = (uint32_t)currentLevel() > MaxLevelInUniqueID ? MaxLevelInUniqueID : currentLevel(); + uint64_t symbolId = symbol.getUniqueId(); + symbolId &= uniqueIdMask; + symbolId |= (level << LevelFlagBitOffset); + symbol.setUniqueId(symbolId); + } + // + // To allocate an internal temporary, which will need to be uniquely + // identified by the consumer of the AST, but never need to + // found by doing a symbol table search by name, hence allowed an + // arbitrary name in the symbol with no worry of collision. + // + void makeInternalVariable(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + } + + // + // Copy a variable or anonymous member's structure from a shared level so that + // it can be added (soon after return) to the symbol table where it can be + // modified without impacting other users of the shared table. + // + TSymbol* copyUpDeferredInsert(TSymbol* shared) + { + if (shared->getAsVariable()) { + TSymbol* copy = shared->clone(); + copy->setUniqueId(shared->getUniqueId()); + return copy; + } else { + const TAnonMember* anon = shared->getAsAnonMember(); + assert(anon); + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + container->setUniqueId(anon->getAnonContainer().getUniqueId()); + return container; + } + } + + TSymbol* copyUp(TSymbol* shared) + { + TSymbol* copy = copyUpDeferredInsert(shared); + table[globalLevel]->insert(*copy, separateNameSpaces); + if (shared->getAsVariable()) + return copy; + else { + // return the copy of the anonymous member + return table[globalLevel]->find(shared->getName()); + } + } + + // Normal find of a symbol, that can optionally say whether the symbol was found + // at a built-in level or the current top-scope level. + TSymbol* find(const TString& name, bool* builtIn = nullptr, bool* currentScope = nullptr, int* thisDepthP = nullptr) + { + int level = currentLevel(); + TSymbol* symbol; + int thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == nullptr && level >= 0); + level++; + if (builtIn) + *builtIn = isBuiltInLevel(level); + if (currentScope) + *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals + if (thisDepthP != nullptr) { + if (! table[level]->isThisLevel()) + thisDepth = 0; + *thisDepthP = thisDepth; + } + + return symbol; + } + + void retargetSymbol(const TString& from, const TString& to) { + int level = currentLevel(); + table[level]->retargetSymbol(from, to); + } + + + // Find of a symbol that returns how many layers deep of nested + // structures-with-member-functions ('this' scopes) deep the symbol was + // found in. + TSymbol* find(const TString& name, int& thisDepth) + { + int level = currentLevel(); + TSymbol* symbol; + thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == nullptr && level >= 0); + + if (! table[level + 1]->isThisLevel()) + thisDepth = 0; + + return symbol; + } + + bool isFunctionNameVariable(const TString& name) const + { + if (separateNameSpaces) + return false; + + int level = currentLevel(); + do { + bool variable; + bool found = table[level]->findFunctionVariableName(name, variable); + if (found) + return variable; + --level; + } while (level >= 0); + + return false; + } + + void findFunctionNameList(const TString& name, TVector& list, bool& builtIn) + { + // For user levels, return the set found in the first scope with a match + builtIn = false; + int level = currentLevel(); + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (list.empty() && level >= globalLevel); + + if (! list.empty()) + return; + + // Gather across all built-in levels; they don't hide each other + builtIn = true; + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (level >= 0); + } + + void relateToOperator(const char* name, TOperator op) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->relateToOperator(name, op); + } + + void setFunctionExtensions(const char* name, int num, const char* const extensions[]) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->setFunctionExtensions(name, num, extensions); + } + + void setSingleFunctionExtensions(const char* name, int num, const char* const extensions[]) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->setSingleFunctionExtensions(name, num, extensions); + } + + void setVariableExtensions(const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(name)); + if (symbol == nullptr) + return; + + symbol->setExtensions(numExts, extensions); + } + + void setVariableExtensions(const char* blockName, const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(blockName)); + if (symbol == nullptr) + return; + TVariable* variable = symbol->getAsVariable(); + assert(variable != nullptr); + + const TTypeList& structure = *variable->getAsVariable()->getType().getStruct(); + for (int member = 0; member < (int)structure.size(); ++member) { + if (structure[member].type->getFieldName().compare(name) == 0) { + variable->setMemberExtensions(member, numExts, extensions); + return; + } + } + } + + long long getMaxSymbolId() { return uniqueId; } + void dump(TInfoSink& infoSink, bool complete = false) const; + void copyTable(const TSymbolTable& copyOf); + + void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } + + void readOnly() + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->readOnly(); + } + + // Add current level in the high-bits of unique id + void updateUniqueIdLevelFlag() { + // clamp level to avoid overflow + uint64_t level = (uint32_t)currentLevel() > MaxLevelInUniqueID ? MaxLevelInUniqueID : currentLevel(); + uniqueId &= uniqueIdMask; + uniqueId |= (level << LevelFlagBitOffset); + } + + void overwriteUniqueId(long long id) + { + uniqueId = id; + updateUniqueIdLevelFlag(); + } + +protected: + TSymbolTable(TSymbolTable&); + TSymbolTable& operator=(TSymbolTableLevel&); + + int currentLevel() const { return static_cast(table.size()) - 1; } + std::vector table; + long long uniqueId; // for unique identification in code generation + bool noBuiltInRedeclarations; + bool separateNameSpaces; + unsigned int adoptedLevels; +}; + +} // end namespace glslang + +#endif // _SYMBOL_TABLE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.cpp new file mode 100644 index 00000000..e20d8083 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.cpp @@ -0,0 +1,1464 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017, 2022-2024 Arm Limited. +// Copyright (C) 2015-2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Help manage multiple profiles, versions, extensions etc. +// +// These don't return error codes, as the presumption is parsing will +// always continue as if the tested feature were enabled, and thus there +// is no error recovery needed. +// + +// +// HOW TO add a feature enabled by an extension. +// +// To add a new hypothetical "Feature F" to the front end, where an extension +// "XXX_extension_X" can be used to enable the feature, do the following. +// +// OVERVIEW: Specific features are what are error-checked for, not +// extensions: A specific Feature F might be enabled by an extension, or a +// particular version in a particular profile, or a stage, or combinations, etc. +// +// The basic mechanism is to use the following to "declare" all the things that +// enable/disable Feature F, in a code path that implements Feature F: +// +// requireProfile() +// profileRequires() +// requireStage() +// checkDeprecated() +// requireNotRemoved() +// requireExtensions() +// extensionRequires() +// +// Typically, only the first two calls are needed. They go into a code path that +// implements Feature F, and will log the proper error/warning messages. Parsing +// will then always continue as if the tested feature was enabled. +// +// There is typically no if-testing or conditional parsing, just insertion of the calls above. +// However, if symbols specific to the extension are added (step 5), they will +// only be added under tests that the minimum version and profile are present. +// +// 1) Add a symbol name for the extension string at the bottom of Versions.h: +// +// const char* const XXX_extension_X = "XXX_extension_X"; +// +// 2) Add extension initialization to TParseVersions::initializeExtensionBehavior(), +// the first function below and optionally a entry to extensionData for additional +// error checks: +// +// extensionBehavior[XXX_extension_X] = EBhDisable; +// (Optional) exts[] = {XXX_extension_X, EShTargetSpv_1_4} +// +// 3) Add any preprocessor directives etc. in the next function, TParseVersions::getPreamble(): +// +// "#define XXX_extension_X 1\n" +// +// The new-line is important, as that ends preprocess tokens. +// +// 4) Insert a profile check in the feature's path (unless all profiles support the feature, +// for some version level). That is, call requireProfile() to constrain the profiles, e.g.: +// +// // ... in a path specific to Feature F... +// requireProfile(loc, +// ECoreProfile | ECompatibilityProfile, +// "Feature F"); +// +// 5) For each profile that supports the feature, insert version/extension checks: +// +// The mostly likely scenario is that Feature F can only be used with a +// particular profile if XXX_extension_X is present or the version is +// high enough that the core specification already incorporated it. +// +// // following the requireProfile() call... +// profileRequires(loc, +// ECoreProfile | ECompatibilityProfile, +// 420, // 0 if no version incorporated the feature into the core spec. +// XXX_extension_X, // can be a list of extensions that all add the feature +// "Feature F Description"); +// +// This allows the feature if either A) one of the extensions is enabled or +// B) the version is high enough. If no version yet incorporates the feature +// into core, pass in 0. +// +// This can be called multiple times, if different profiles support the +// feature starting at different version numbers or with different +// extensions. +// +// This must be called for each profile allowed by the initial call to requireProfile(). +// +// Profiles are all masks, which can be "or"-ed together. +// +// ENoProfile +// ECoreProfile +// ECompatibilityProfile +// EEsProfile +// +// The ENoProfile profile is only for desktop, before profiles showed up in version 150; +// All other #version with no profile default to either es or core, and so have profiles. +// +// You can select all but a particular profile using ~. The following basically means "desktop": +// +// ~EEsProfile +// +// 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use +// will be automatically error checked against the extensions enabled at that moment. +// see the comment at the top of Initialize.cpp for where to put them. Establish them at +// the earliest release that supports the extension. Then, tag them with the +// set of extensions that both enable them and are necessary, given the version of the symbol +// table. (There is a different symbol table for each version.) +// +// 7) If the extension has additional requirements like minimum SPIR-V version required, add them +// to extensionRequires() + +#include "parseVersions.h" +#include "localintermediate.h" + +namespace glslang { + +// +// Initialize all extensions, almost always to 'disable', as once their features +// are incorporated into a core version, their features are supported through allowing that +// core version, not through a pseudo-enablement of the extension. +// +void TParseVersions::initializeExtensionBehavior() +{ + typedef struct { + const char *const extensionName; + EShTargetLanguageVersion minSpvVersion; + } extensionData; + + const extensionData exts[] = { {E_GL_EXT_ray_tracing, EShTargetSpv_1_4}, + {E_GL_NV_ray_tracing_motion_blur, EShTargetSpv_1_4}, + {E_GL_EXT_mesh_shader, EShTargetSpv_1_4}, + {E_GL_NV_cooperative_matrix2, EShTargetSpv_1_6} + }; + + for (size_t ii = 0; ii < sizeof(exts) / sizeof(exts[0]); ii++) { + // Add only extensions which require > spv1.0 to save space in map + if (exts[ii].minSpvVersion > EShTargetSpv_1_0) { + extensionMinSpv[exts[ii].extensionName] = exts[ii].minSpvVersion; + } + } + + extensionBehavior[E_GL_OES_texture_3D] = EBhDisable; + extensionBehavior[E_GL_OES_standard_derivatives] = EBhDisable; + extensionBehavior[E_GL_EXT_frag_depth] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external_essl3] = EBhDisable; + extensionBehavior[E_GL_EXT_YUV_target] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_EXT_shadow_samplers] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable; + extensionBehavior[E_GL_3DL_array_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_gather] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader5] = EBhDisablePartial; + extensionBehavior[E_GL_ARB_separate_shader_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_compute_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_enhanced_layouts] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_multisample] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_attrib_location] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_uniform_location] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_load_store] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_atomic_counters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_atomic_counter_ops] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_draw_parameters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_group_vote] = EBhDisable; + extensionBehavior[E_GL_ARB_derivative_control] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_image_samples] = EBhDisable; + extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_int64] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_fp64] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture2] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture_clamp] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_stencil_export] = EBhDisable; +// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members + extensionBehavior[E_GL_ARB_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable; + extensionBehavior[E_GL_ARB_fragment_shader_interlock] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_clock] = EBhDisable; + extensionBehavior[E_GL_ARB_uniform_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_sample_shading] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_bit_encoding] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_size] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_storage_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_packing] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_query_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_vertex_attrib_64bit] = EBhDisable; + extensionBehavior[E_GL_ARB_draw_instanced] = EBhDisable; + extensionBehavior[E_GL_ARB_bindless_texture] = EBhDisable; + extensionBehavior[E_GL_ARB_fragment_coord_conventions] = EBhDisable; + + + extensionBehavior[E_GL_KHR_shader_subgroup_basic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_vote] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_arithmetic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_ballot] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle_relative] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_rotate] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_clustered] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_quad] = EBhDisable; + extensionBehavior[E_GL_KHR_memory_scope_semantics] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_atomic_int64] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable; + extensionBehavior[E_GL_EXT_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_EXT_control_flow_attributes] = EBhDisable; + extensionBehavior[E_GL_EXT_nonuniform_qualifier] = EBhDisable; + extensionBehavior[E_GL_EXT_samplerless_texture_functions] = EBhDisable; + extensionBehavior[E_GL_EXT_scalar_block_layout] = EBhDisable; + extensionBehavior[E_GL_EXT_fragment_invocation_density] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference2] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference_uvec2] = EBhDisable; + extensionBehavior[E_GL_EXT_demote_to_helper_invocation] = EBhDisable; + extensionBehavior[E_GL_EXT_debug_printf] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_16bit_storage] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_8bit_storage] = EBhDisable; + extensionBehavior[E_GL_EXT_subgroup_uniform_control_flow] = EBhDisable; + extensionBehavior[E_GL_EXT_maximal_reconvergence] = EBhDisable; + + extensionBehavior[E_GL_EXT_fragment_shader_barycentric] = EBhDisable; + extensionBehavior[E_GL_EXT_expect_assume] = EBhDisable; + + extensionBehavior[E_GL_EXT_control_flow_attributes2] = EBhDisable; + extensionBehavior[E_GL_EXT_spec_constant_composites] = EBhDisable; + + extensionBehavior[E_GL_KHR_cooperative_matrix] = EBhDisable; + extensionBehavior[E_GL_NV_cooperative_vector] = EBhDisable; + + // #line and #include + extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable; + extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_include] = EBhDisable; + + extensionBehavior[E_GL_AMD_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_trinary_minmax] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_explicit_vertex_parameter] = EBhDisable; + extensionBehavior[E_GL_AMD_gcn_shader] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float] = EBhDisable; + extensionBehavior[E_GL_AMD_texture_gather_bias_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_int16] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_image_load_store_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_fragment_mask] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float_fetch] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_early_and_late_fragment_tests] = EBhDisable; + + extensionBehavior[E_GL_INTEL_shader_integer_functions2] = EBhDisable; + + extensionBehavior[E_GL_NV_sample_mask_override_coverage] = EBhDisable; + extensionBehavior[E_SPV_NV_geometry_shader_passthrough] = EBhDisable; + extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable; + extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable; + extensionBehavior[E_GL_NVX_multiview_per_view_attributes] = EBhDisable; + extensionBehavior[E_GL_NV_shader_atomic_int64] = EBhDisable; + extensionBehavior[E_GL_NV_conservative_raster_underestimation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_noperspective_interpolation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_subgroup_partitioned] = EBhDisable; + extensionBehavior[E_GL_NV_shading_rate_image] = EBhDisable; + extensionBehavior[E_GL_NV_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_NV_ray_tracing_motion_blur] = EBhDisable; + extensionBehavior[E_GL_NV_fragment_shader_barycentric] = EBhDisable; + extensionBehavior[E_GL_NV_compute_shader_derivatives] = EBhDisable; + extensionBehavior[E_GL_NV_shader_texture_footprint] = EBhDisable; + extensionBehavior[E_GL_NV_mesh_shader] = EBhDisable; + extensionBehavior[E_GL_NV_cooperative_matrix] = EBhDisable; + extensionBehavior[E_GL_NV_shader_sm_builtins] = EBhDisable; + extensionBehavior[E_GL_NV_integer_cooperative_matrix] = EBhDisable; + extensionBehavior[E_GL_NV_shader_invocation_reorder] = EBhDisable; + extensionBehavior[E_GL_NV_displacement_micromap] = EBhDisable; + extensionBehavior[E_GL_NV_shader_atomic_fp16_vector] = EBhDisable; + extensionBehavior[E_GL_NV_cooperative_matrix2] = EBhDisable; + extensionBehavior[E_GL_NV_cluster_acceleration_structure] = EBhDisable; + extensionBehavior[E_GL_NV_linear_swept_spheres] = EBhDisable; + + // ARM + extensionBehavior[E_GL_ARM_shader_core_builtins] = EBhDisable; + + // QCOM + extensionBehavior[E_GL_QCOM_image_processing] = EBhDisable; + extensionBehavior[E_GL_QCOM_image_processing2] = EBhDisable; + + // AEP + extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisable; + extensionBehavior[E_GL_KHR_blend_equation_advanced] = EBhDisable; + extensionBehavior[E_GL_OES_sample_variables] = EBhDisable; + extensionBehavior[E_GL_OES_shader_image_atomic] = EBhDisable; + extensionBehavior[E_GL_OES_shader_multisample_interpolation] = EBhDisable; + extensionBehavior[E_GL_OES_texture_storage_multisample_2d_array] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_EXT_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_EXT_null_initializer] = EBhDisable; + + // OES matching AEP + extensionBehavior[E_GL_OES_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_OES_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_OES_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_OES_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_OES_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_integer_mix] = EBhDisable; + + // EXT extensions + extensionBehavior[E_GL_EXT_device_group] = EBhDisable; + extensionBehavior[E_GL_EXT_multiview] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_realtime_clock] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_query] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_flags_primitive_culling] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_cull_mask] = EBhDisable; + extensionBehavior[E_GL_EXT_blend_func_extended] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_implicit_conversions] = EBhDisable; + extensionBehavior[E_GL_EXT_fragment_shading_rate] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_image_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_terminate_invocation] = EBhDisable; + extensionBehavior[E_GL_EXT_shared_memory_block] = EBhDisable; + extensionBehavior[E_GL_EXT_spirv_intrinsics] = EBhDisable; + extensionBehavior[E_GL_EXT_mesh_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_opacity_micromap] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_quad_control] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_tracing_position_fetch] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_tile_image] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_shadow_lod] = EBhDisable; + extensionBehavior[E_GL_EXT_draw_instanced] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_array] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_offset_non_const] = EBhDisable; + extensionBehavior[E_GL_EXT_nontemporal_keyword] = EBhDisable; + extensionBehavior[E_GL_EXT_bfloat16] = EBhDisable; + + // OVR extensions + extensionBehavior[E_GL_OVR_multiview] = EBhDisable; + extensionBehavior[E_GL_OVR_multiview2] = EBhDisable; + + // explicit types + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float64] = EBhDisable; + + // subgroup extended types + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_float16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_atomic_float] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_atomic_float2] = EBhDisable; + + extensionBehavior[E_GL_EXT_integer_dot_product] = EBhDisable; + + // Record extensions not for spv. + spvUnsupportedExt.push_back(E_GL_ARB_bindless_texture); +} + +// Get code that is not part of a shared symbol table, is specific to this shader, +// or needed by the preprocessor (which does not use a shared symbol table). +void TParseVersions::getPreamble(std::string& preamble) +{ + if (isEsProfile()) { + preamble = + "#define GL_ES 1\n" + "#define GL_FRAGMENT_PRECISION_HIGH 1\n" + "#define GL_OES_texture_3D 1\n" + "#define GL_OES_standard_derivatives 1\n" + "#define GL_EXT_frag_depth 1\n" + "#define GL_OES_EGL_image_external 1\n" + "#define GL_OES_EGL_image_external_essl3 1\n" + "#define GL_EXT_YUV_target 1\n" + "#define GL_EXT_shader_texture_lod 1\n" + "#define GL_EXT_shadow_samplers 1\n" + "#define GL_EXT_fragment_shading_rate 1\n" + + // AEP + "#define GL_ANDROID_extension_pack_es31a 1\n" + "#define GL_OES_sample_variables 1\n" + "#define GL_OES_shader_image_atomic 1\n" + "#define GL_OES_shader_multisample_interpolation 1\n" + "#define GL_OES_texture_storage_multisample_2d_array 1\n" + "#define GL_EXT_geometry_shader 1\n" + "#define GL_EXT_geometry_point_size 1\n" + "#define GL_EXT_gpu_shader5 1\n" + "#define GL_EXT_primitive_bounding_box 1\n" + "#define GL_EXT_shader_io_blocks 1\n" + "#define GL_EXT_tessellation_shader 1\n" + "#define GL_EXT_tessellation_point_size 1\n" + "#define GL_EXT_texture_buffer 1\n" + "#define GL_EXT_texture_cube_map_array 1\n" + "#define GL_EXT_shader_implicit_conversions 1\n" + "#define GL_EXT_shader_integer_mix 1\n" + "#define GL_EXT_blend_func_extended 1\n" + + // OES matching AEP + "#define GL_OES_geometry_shader 1\n" + "#define GL_OES_geometry_point_size 1\n" + "#define GL_OES_gpu_shader5 1\n" + "#define GL_OES_primitive_bounding_box 1\n" + "#define GL_OES_shader_io_blocks 1\n" + "#define GL_OES_tessellation_shader 1\n" + "#define GL_OES_tessellation_point_size 1\n" + "#define GL_OES_texture_buffer 1\n" + "#define GL_OES_texture_cube_map_array 1\n" + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + + "#define GL_QCOM_image_processing 1\n" + "#define GL_QCOM_image_processing2 1\n" + ; + + if (version >= 300) { + preamble += "#define GL_NV_shader_noperspective_interpolation 1\n"; + } + if (version >= 310) { + preamble += "#define GL_EXT_null_initializer 1\n"; + preamble += "#define GL_EXT_subgroup_uniform_control_flow 1\n"; + preamble += "#define GL_EXT_maximal_reconvergence 1\n"; + } + + } else { // !isEsProfile() + preamble = + "#define GL_ARB_texture_rectangle 1\n" + "#define GL_ARB_shading_language_420pack 1\n" + "#define GL_ARB_texture_gather 1\n" + "#define GL_ARB_gpu_shader5 1\n" + "#define GL_ARB_separate_shader_objects 1\n" + "#define GL_ARB_compute_shader 1\n" + "#define GL_ARB_tessellation_shader 1\n" + "#define GL_ARB_enhanced_layouts 1\n" + "#define GL_ARB_texture_cube_map_array 1\n" + "#define GL_ARB_texture_multisample 1\n" + "#define GL_ARB_shader_texture_lod 1\n" + "#define GL_ARB_explicit_attrib_location 1\n" + "#define GL_ARB_explicit_uniform_location 1\n" + "#define GL_ARB_shader_image_load_store 1\n" + "#define GL_ARB_shader_atomic_counters 1\n" + "#define GL_ARB_shader_draw_parameters 1\n" + "#define GL_ARB_shader_group_vote 1\n" + "#define GL_ARB_derivative_control 1\n" + "#define GL_ARB_shader_texture_image_samples 1\n" + "#define GL_ARB_viewport_array 1\n" + "#define GL_ARB_gpu_shader_int64 1\n" + "#define GL_ARB_gpu_shader_fp64 1\n" + "#define GL_ARB_shader_ballot 1\n" + "#define GL_ARB_sparse_texture2 1\n" + "#define GL_ARB_sparse_texture_clamp 1\n" + "#define GL_ARB_shader_stencil_export 1\n" + "#define GL_ARB_sample_shading 1\n" + "#define GL_ARB_shader_image_size 1\n" + "#define GL_ARB_shading_language_packing 1\n" +// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members + "#define GL_ARB_post_depth_coverage 1\n" + "#define GL_ARB_fragment_shader_interlock 1\n" + "#define GL_ARB_uniform_buffer_object 1\n" + "#define GL_ARB_shader_bit_encoding 1\n" + "#define GL_ARB_shader_storage_buffer_object 1\n" + "#define GL_ARB_texture_query_lod 1\n" + "#define GL_ARB_vertex_attrib_64bit 1\n" + "#define GL_ARB_draw_instanced 1\n" + "#define GL_ARB_fragment_coord_conventions 1\n" + + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + "#define GL_EXT_shader_image_load_formatted 1\n" + "#define GL_EXT_post_depth_coverage 1\n" + "#define GL_EXT_control_flow_attributes 1\n" + "#define GL_EXT_nonuniform_qualifier 1\n" + "#define GL_EXT_shader_16bit_storage 1\n" + "#define GL_EXT_shader_8bit_storage 1\n" + "#define GL_EXT_samplerless_texture_functions 1\n" + "#define GL_EXT_scalar_block_layout 1\n" + "#define GL_EXT_fragment_invocation_density 1\n" + "#define GL_EXT_buffer_reference 1\n" + "#define GL_EXT_buffer_reference2 1\n" + "#define GL_EXT_buffer_reference_uvec2 1\n" + "#define GL_EXT_demote_to_helper_invocation 1\n" + "#define GL_EXT_debug_printf 1\n" + "#define GL_EXT_fragment_shading_rate 1\n" + "#define GL_EXT_shared_memory_block 1\n" + "#define GL_EXT_shader_integer_mix 1\n" + "#define GL_EXT_spec_constant_composites 1\n" + + // GL_KHR_shader_subgroup + "#define GL_KHR_shader_subgroup_basic 1\n" + "#define GL_KHR_shader_subgroup_vote 1\n" + "#define GL_KHR_shader_subgroup_arithmetic 1\n" + "#define GL_KHR_shader_subgroup_ballot 1\n" + "#define GL_KHR_shader_subgroup_shuffle 1\n" + "#define GL_KHR_shader_subgroup_shuffle_relative 1\n" + "#define GL_KHR_shader_subgroup_clustered 1\n" + "#define GL_KHR_shader_subgroup_quad 1\n" + + "#define GL_KHR_cooperative_matrix 1\n" + + "#define GL_EXT_shader_image_int64 1\n" + "#define GL_EXT_shader_atomic_int64 1\n" + "#define GL_EXT_shader_realtime_clock 1\n" + "#define GL_EXT_ray_tracing 1\n" + "#define GL_EXT_ray_query 1\n" + "#define GL_EXT_ray_flags_primitive_culling 1\n" + "#define GL_EXT_ray_cull_mask 1\n" + "#define GL_EXT_ray_tracing_position_fetch 1\n" + "#define GL_EXT_spirv_intrinsics 1\n" + "#define GL_EXT_mesh_shader 1\n" + + "#define GL_AMD_shader_ballot 1\n" + "#define GL_AMD_shader_trinary_minmax 1\n" + "#define GL_AMD_shader_explicit_vertex_parameter 1\n" + "#define GL_AMD_gcn_shader 1\n" + "#define GL_AMD_gpu_shader_half_float 1\n" + "#define GL_AMD_texture_gather_bias_lod 1\n" + "#define GL_AMD_gpu_shader_int16 1\n" + "#define GL_AMD_shader_image_load_store_lod 1\n" + "#define GL_AMD_shader_fragment_mask 1\n" + "#define GL_AMD_gpu_shader_half_float_fetch 1\n" + + "#define GL_INTEL_shader_integer_functions2 1\n" + + "#define GL_NV_sample_mask_override_coverage 1\n" + "#define GL_NV_geometry_shader_passthrough 1\n" + "#define GL_NV_viewport_array2 1\n" + "#define GL_NV_shader_atomic_int64 1\n" + "#define GL_NV_conservative_raster_underestimation 1\n" + "#define GL_NV_shader_subgroup_partitioned 1\n" + "#define GL_NV_shading_rate_image 1\n" + "#define GL_NV_ray_tracing 1\n" + "#define GL_NV_ray_tracing_motion_blur 1\n" + "#define GL_NV_fragment_shader_barycentric 1\n" + "#define GL_NV_compute_shader_derivatives 1\n" + "#define GL_NV_shader_texture_footprint 1\n" + "#define GL_NV_mesh_shader 1\n" + "#define GL_NV_cooperative_matrix 1\n" + "#define GL_NV_integer_cooperative_matrix 1\n" + "#define GL_NV_shader_invocation_reorder 1\n" + "#define GL_NV_cooperative_matrix2 1\n" + + "#define GL_QCOM_image_processing 1\n" + "#define GL_QCOM_image_processing2 1\n" + + "#define GL_EXT_shader_explicit_arithmetic_types 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int8 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int64 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float64 1\n" + + "#define GL_EXT_shader_subgroup_extended_types_int8 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int16 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int64 1\n" + "#define GL_EXT_shader_subgroup_extended_types_float16 1\n" + + "#define GL_EXT_shader_atomic_float 1\n" + "#define GL_EXT_shader_atomic_float2 1\n" + + "#define GL_EXT_fragment_shader_barycentric 1\n" + "#define GL_EXT_shader_quad_control 1\n" + "#define GL_EXT_texture_array 1\n" + + "#define GL_EXT_control_flow_attributes2 1\n" + + "#define GL_EXT_integer_dot_product 1\n" + "#define GL_EXT_bfloat16 1\n" + ; + + if (spvVersion.spv == 0) { + preamble += "#define GL_ARB_bindless_texture 1\n"; + } + + if (version >= 150) { + // define GL_core_profile and GL_compatibility_profile + preamble += "#define GL_core_profile 1\n"; + + if (profile == ECompatibilityProfile) + preamble += "#define GL_compatibility_profile 1\n"; + } + if (version >= 140) { + preamble += "#define GL_EXT_null_initializer 1\n"; + preamble += "#define GL_EXT_subgroup_uniform_control_flow 1\n"; + preamble += "#define GL_EXT_maximal_reconvergence 1\n"; + } + if (version >= 130) { + preamble +="#define GL_FRAGMENT_PRECISION_HIGH 1\n"; + } + } + + if ((!isEsProfile() && version >= 140) || + (isEsProfile() && version >= 310)) { + preamble += + "#define GL_EXT_device_group 1\n" + "#define GL_EXT_multiview 1\n" + "#define GL_NV_shader_sm_builtins 1\n" + ; + } + + if ((!isEsProfile() && version >= 130) || + (isEsProfile() && version >= 300)) { + preamble += "#define GL_EXT_texture_offset_non_const 1\n"; + } + + if (version >= 300 /* both ES and non-ES */) { + preamble += + "#define GL_OVR_multiview 1\n" + "#define GL_OVR_multiview2 1\n" + ; + } + + // #line and #include + preamble += + "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" + "#define GL_KHR_blend_equation_advanced 1\n" + ; + + // other general extensions + preamble += + "#define GL_EXT_terminate_invocation 1\n" + ; + + // #define VULKAN XXXX + const int numberBufSize = 12; + char numberBuf[numberBufSize]; + if (spvVersion.vulkanGlsl > 0) { + preamble += "#define VULKAN "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl); + preamble += numberBuf; + preamble += "\n"; + } + + // #define GL_SPIRV XXXX + if (spvVersion.openGl > 0) { + preamble += "#define GL_SPIRV "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.openGl); + preamble += numberBuf; + preamble += "\n"; + } + + // GL_EXT_spirv_intrinsics + if (!isEsProfile()) { + switch (language) { + case EShLangVertex: preamble += "#define GL_VERTEX_SHADER 1 \n"; break; + case EShLangTessControl: preamble += "#define GL_TESSELLATION_CONTROL_SHADER 1 \n"; break; + case EShLangTessEvaluation: preamble += "#define GL_TESSELLATION_EVALUATION_SHADER 1 \n"; break; + case EShLangGeometry: preamble += "#define GL_GEOMETRY_SHADER 1 \n"; break; + case EShLangFragment: preamble += "#define GL_FRAGMENT_SHADER 1 \n"; break; + case EShLangCompute: preamble += "#define GL_COMPUTE_SHADER 1 \n"; break; + case EShLangRayGen: preamble += "#define GL_RAY_GENERATION_SHADER_EXT 1 \n"; break; + case EShLangIntersect: preamble += "#define GL_INTERSECTION_SHADER_EXT 1 \n"; break; + case EShLangAnyHit: preamble += "#define GL_ANY_HIT_SHADER_EXT 1 \n"; break; + case EShLangClosestHit: preamble += "#define GL_CLOSEST_HIT_SHADER_EXT 1 \n"; break; + case EShLangMiss: preamble += "#define GL_MISS_SHADER_EXT 1 \n"; break; + case EShLangCallable: preamble += "#define GL_CALLABLE_SHADER_EXT 1 \n"; break; + case EShLangTask: preamble += "#define GL_TASK_SHADER_NV 1 \n"; break; + case EShLangMesh: preamble += "#define GL_MESH_SHADER_NV 1 \n"; break; + default: break; + } + } +} + +// +// Map from stage enum to externally readable text name. +// +const char* StageName(EShLanguage stage) +{ + switch(stage) { + case EShLangVertex: return "vertex"; + case EShLangFragment: return "fragment"; + case EShLangCompute: return "compute"; + case EShLangTessControl: return "tessellation control"; + case EShLangTessEvaluation: return "tessellation evaluation"; + case EShLangGeometry: return "geometry"; + case EShLangRayGen: return "ray-generation"; + case EShLangIntersect: return "intersection"; + case EShLangAnyHit: return "any-hit"; + case EShLangClosestHit: return "closest-hit"; + case EShLangMiss: return "miss"; + case EShLangCallable: return "callable"; + case EShLangMesh: return "mesh"; + case EShLangTask: return "task"; + default: return "unknown stage"; + } +} + +// +// When to use requireStage() +// +// If only some stages support a feature. +// +// Operation: If the current stage is not present, give an error message. +// +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguageMask languageMask, const char* featureDesc) +{ + if (((1 << language) & languageMask) == 0) + error(loc, "not supported in this stage:", featureDesc, StageName(language)); +} + +// If only one stage supports a feature, this can be called. But, all supporting stages +// must be specified with one call. +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguage stage, const char* featureDesc) +{ + requireStage(loc, static_cast(1 << stage), featureDesc); +} + +// +// When to use requireProfile(): +// +// Use if only some profiles support a feature. However, if within a profile the feature +// is version or extension specific, follow this call with calls to profileRequires(). +// +// Operation: If the current profile is not one of the profileMask, +// give an error message. +// +void TParseVersions::requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) +{ + if (! (profile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); +} + +// +// When to use profileRequires(): +// +// If a set of profiles have the same requirements for what version or extensions +// are needed to support a feature. +// +// It must be called for each profile that needs protection. Use requireProfile() first +// to reduce that set of profiles. +// +// Operation: Will issue warnings/errors based on the current profile, version, and extension +// behaviors. It only checks extensions when the current profile is one of the profileMask. +// +// A minVersion of 0 means no version of the profileMask support this in core, +// the extension must be present. +// + +// entry point that takes multiple extensions +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc) +{ + if (profile & profileMask) { + bool okay = minVersion > 0 && version >= minVersion; + for (int i = 0; i < numExtensions; ++i) { + switch (getExtensionBehavior(extensions[i])) { + case EBhWarn: + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + [[fallthrough]]; + case EBhRequire: + case EBhEnable: + okay = true; + break; + default: break; // some compilers want this + } + } + if (! okay) + error(loc, "not supported for this version or the enabled extensions", featureDesc, ""); + } +} + +// entry point for the above that takes a single extension +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc) +{ + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); +} + +void TParseVersions::unimplemented(const TSourceLoc& loc, const char* featureDesc) +{ + error(loc, "feature not yet implemented", featureDesc, ""); +} + +// +// Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether +// a future compatibility context is being use. +// +void TParseVersions::checkDeprecated(const TSourceLoc& loc, int profileMask, int depVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= depVersion) { + if (forwardCompatible) + error(loc, "deprecated, may be removed in future release", featureDesc, ""); + else if (! suppressWarnings()) + infoSink.info.message(EPrefixWarning, (TString(featureDesc) + " deprecated in version " + + String(depVersion) + "; may be removed in future release").c_str(), + loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + } + } +} + +// +// Within a set of profiles, see if a feature has now been removed and if so, give an error. +// The version argument is the first version no longer having the feature. +// +void TParseVersions::requireNotRemoved(const TSourceLoc& loc, int profileMask, int removedVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= removedVersion) { + const int maxSize = 60; + char buf[maxSize]; + snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName(profile), removedVersion); + error(loc, "no longer supported in", featureDesc, buf); + } + } +} + +// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. +// Warns appropriately if the requested behavior of an extension is "warn". +bool TParseVersions::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + // First, see if any of the extensions are enabled + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhEnable || behavior == EBhRequire) + return true; + } + + // See if any extensions want to give a warning on use; give warnings for all such extensions + bool warned = false; + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhDisable && relaxedErrors()) { + infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc, + messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + behavior = EBhWarn; + } + if (behavior == EBhWarn) { + infoSink.info.message(EPrefixWarning, + ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), + loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + warned = true; + } + } + if (warned) + return true; + return false; +} + +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], + const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + error(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +// +// Use by preprocessor when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], + const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +TExtensionBehavior TParseVersions::getExtensionBehavior(const char* extension) +{ + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) + return EBhMissing; + else + return iter->second; +} + +// Returns true if the given extension is set to enable, require, or warn. +bool TParseVersions::extensionTurnedOn(const char* const extension) +{ + switch (getExtensionBehavior(extension)) { + case EBhEnable: + case EBhRequire: + case EBhWarn: + return true; + default: + break; + } + return false; +} +// See if any of the extensions are set to enable, require, or warn. +bool TParseVersions::extensionsTurnedOn(int numExtensions, const char* const extensions[]) +{ + for (int i = 0; i < numExtensions; ++i) { + if (extensionTurnedOn(extensions[i])) + return true; + } + return false; +} + +// +// Change the current state of an extension's behavior. +// +void TParseVersions::updateExtensionBehavior(int line, const char* extension, const char* behaviorString) +{ + // Translate from text string of extension's behavior to an enum. + TExtensionBehavior behavior = EBhDisable; + if (! strcmp("require", behaviorString)) + behavior = EBhRequire; + else if (! strcmp("enable", behaviorString)) + behavior = EBhEnable; + else if (! strcmp("disable", behaviorString)) + behavior = EBhDisable; + else if (! strcmp("warn", behaviorString)) + behavior = EBhWarn; + else { + error(getCurrentLoc(), "behavior not supported:", "#extension", behaviorString); + return; + } + bool on = behavior != EBhDisable; + + // check if extension is used with correct shader stage + checkExtensionStage(getCurrentLoc(), extension); + + // check if extension has additional requirements + extensionRequires(getCurrentLoc(), extension, behaviorString); + + // update the requested extension + updateExtensionBehavior(extension, behavior); + + // see if need to propagate to implicitly modified things + if (strcmp(extension, "GL_ANDROID_extension_pack_es31a") == 0) { + // to everything in AEP + updateExtensionBehavior(line, "GL_KHR_blend_equation_advanced", behaviorString); + updateExtensionBehavior(line, "GL_OES_sample_variables", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_image_atomic", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_multisample_interpolation", behaviorString); + updateExtensionBehavior(line, "GL_OES_texture_storage_multisample_2d_array", behaviorString); + updateExtensionBehavior(line, "GL_EXT_geometry_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_gpu_shader5", behaviorString); + updateExtensionBehavior(line, "GL_EXT_primitive_bounding_box", behaviorString); + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + updateExtensionBehavior(line, "GL_EXT_tessellation_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_buffer", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_cube_map_array", behaviorString); + } + // geometry to io_blocks + else if (strcmp(extension, "GL_EXT_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + // tessellation to io_blocks + else if (strcmp(extension, "GL_EXT_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); + else if (strcmp(extension, "GL_ARB_shading_language_include") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); + // subgroup_* to subgroup_basic + else if (strcmp(extension, "GL_KHR_shader_subgroup_vote") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_arithmetic") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_ballot") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle_relative") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_clustered") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_quad") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_NV_shader_subgroup_partitioned") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_EXT_buffer_reference2") == 0 || + strcmp(extension, "GL_EXT_buffer_reference_uvec2") == 0) + updateExtensionBehavior(line, "GL_EXT_buffer_reference", behaviorString); + else if (strcmp(extension, "GL_NV_integer_cooperative_matrix") == 0) + updateExtensionBehavior(line, "GL_NV_cooperative_matrix", behaviorString); + else if (strcmp(extension, "GL_NV_cooperative_matrix2") == 0) + updateExtensionBehavior(line, "GL_KHR_cooperative_matrix", behaviorString); + // subgroup extended types to explicit types + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int8") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int8", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int16", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int64") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int64", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_float16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_float16", behaviorString); + + // see if we need to update the numeric features + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int8") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int8, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int16, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int32") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int32, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int64, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float16, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float32") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float32, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float64, on); + else if (strcmp(extension, "GL_EXT_shader_implicit_conversions") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_implicit_conversions, on); + else if (strcmp(extension, "GL_ARB_gpu_shader_fp64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_fp64, on); + else if (strcmp(extension, "GL_AMD_gpu_shader_int16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_int16, on); + else if (strcmp(extension, "GL_AMD_gpu_shader_half_float") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_half_float, on); +} + +void TParseVersions::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) +{ + // Update the current behavior + if (strcmp(extension, "all") == 0) { + // special case for the 'all' extension; apply it to every extension present + if (behavior == EBhRequire || behavior == EBhEnable) { + error(getCurrentLoc(), "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", ""); + return; + } else { + for (auto iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter) + iter->second = behavior; + } + } else { + // Do the update for this single extension + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) { + switch (behavior) { + case EBhRequire: + error(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + case EBhEnable: + case EBhWarn: + case EBhDisable: + warn(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + default: + assert(0 && "unexpected behavior"); + } + + return; + } else { + if (iter->second == EBhDisablePartial) + warn(getCurrentLoc(), "extension is only partially supported:", "#extension", extension); + if (behavior != EBhDisable) + intermediate.addRequestedExtension(extension); + iter->second = behavior; + } + } +} + +// Check if extension is used with correct shader stage. +void TParseVersions::checkExtensionStage(const TSourceLoc& loc, const char * const extension) +{ + // GL_NV_mesh_shader extension is only allowed in task/mesh shaders + if (strcmp(extension, "GL_NV_mesh_shader") == 0) { + requireStage(loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask | EShLangFragmentMask), + "#extension GL_NV_mesh_shader"); + profileRequires(loc, ECoreProfile, 450, nullptr, "#extension GL_NV_mesh_shader"); + profileRequires(loc, EEsProfile, 320, nullptr, "#extension GL_NV_mesh_shader"); + if (extensionTurnedOn(E_GL_EXT_mesh_shader)) { + error(loc, "GL_EXT_mesh_shader is already turned on, and not allowed with", "#extension", extension); + } + } + else if (strcmp(extension, "GL_EXT_mesh_shader") == 0) { + requireStage(loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask | EShLangFragmentMask), + "#extension GL_EXT_mesh_shader"); + profileRequires(loc, ECoreProfile, 450, nullptr, "#extension GL_EXT_mesh_shader"); + profileRequires(loc, EEsProfile, 320, nullptr, "#extension GL_EXT_mesh_shader"); + if (extensionTurnedOn(E_GL_NV_mesh_shader)) { + error(loc, "GL_NV_mesh_shader is already turned on, and not allowed with", "#extension", extension); + } + } +} + +// Check if extension has additional requirements +void TParseVersions::extensionRequires(const TSourceLoc &loc, const char * const extension, const char *behaviorString) +{ + bool isEnabled = false; + if (!strcmp("require", behaviorString)) + isEnabled = true; + else if (!strcmp("enable", behaviorString)) + isEnabled = true; + + if (isEnabled) { + unsigned int minSpvVersion = 0; + auto iter = extensionMinSpv.find(TString(extension)); + if (iter != extensionMinSpv.end()) + minSpvVersion = iter->second; + requireSpv(loc, extension, minSpvVersion); + } + + if (spvVersion.spv != 0){ + for (auto ext : spvUnsupportedExt){ + if (strcmp(extension, ext.c_str()) == 0) + error(loc, "not allowed when using generating SPIR-V codes", extension, ""); + } + } +} + +// Call for any operation needing full GLSL integer data-type support. +void TParseVersions::fullIntegerCheck(const TSourceLoc& loc, const char* op) +{ + profileRequires(loc, ENoProfile, 130, nullptr, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); +} + +// Call for any operation needing GLSL double data-type support. +void TParseVersions::doubleCheck(const TSourceLoc& loc, const char* op) +{ + + //requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + if (language == EShLangVertex) { + const char* const f64_Extensions[] = {E_GL_ARB_gpu_shader_fp64, E_GL_ARB_vertex_attrib_64bit}; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, 2, f64_Extensions, op); + } else + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader_fp64, op); +} + +// Call for any operation needing GLSL float16 data-type support. +void TParseVersions::float16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +bool TParseVersions::float16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int8Arithmetic() +{ + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +void TParseVersions::requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::float16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::bfloat16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_EXT_bfloat16, + }; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL float32 data-type support. +void TParseVersions::explicitFloat32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float64 data-type support. +void TParseVersions::explicitFloat64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float64}; + requireExtensions(loc, 2, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int8 data-type support. +void TParseVersions::explicitInt8Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float16 opaque-type support +void TParseVersions::float16OpaqueCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + requireExtensions(loc, 1, &E_GL_AMD_gpu_shader_half_float_fetch, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int16 data-type support. +void TParseVersions::explicitInt16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int8ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_EXT_shader_8bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL explicit int32 data-type support. +void TParseVersions::explicitInt32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL 64-bit integer data-type support. +void TParseVersions::int64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[3] = {E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64}; + requireExtensions(loc, 3, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +void TParseVersions::fcoopmatCheckNV(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::intcoopmatCheckNV(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_integer_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::coopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_KHR_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::tensorLayoutViewCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_cooperative_matrix2}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::coopvecCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_cooperative_vector}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation removed because SPIR-V is in use. +void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.spv != 0) + error(loc, "not allowed when generating SPIR-V", op, ""); +} + +// Call for any operation removed because Vulkan SPIR-V is being generated. +void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed) + error(loc, "not allowed when using GLSL for Vulkan", op, ""); +} + +// Call for any operation that requires Vulkan. +void TParseVersions::requireVulkan(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.vulkan == 0) + error(loc, "only allowed when using GLSL for Vulkan", op, ""); +} + +// Call for any operation that requires SPIR-V. +void TParseVersions::requireSpv(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.spv == 0) + error(loc, "only allowed when generating SPIR-V", op, ""); +} +void TParseVersions::requireSpv(const TSourceLoc& loc, const char *op, unsigned int version) +{ + if (spvVersion.spv < version) + error(loc, "not supported for current targeted SPIR-V version", op, ""); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.h new file mode 100644 index 00000000..bd74a0f9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/Versions.h @@ -0,0 +1,395 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017, 2022-2024 Arm Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// Modifications Copyright (C) 2024 Valve Corporation. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _VERSIONS_INCLUDED_ +#define _VERSIONS_INCLUDED_ + +#define LAST_ELEMENT_MARKER(x) x + +// +// Help manage multiple profiles, versions, extensions etc. +// + +// +// Profiles are set up for masking operations, so queries can be done on multiple +// profiles at the same time. +// +// Don't maintain an ordinal set of enums (0,1,2,3...) to avoid all possible +// defects from mixing the two different forms. +// +typedef enum : unsigned { + EBadProfile = 0, + ENoProfile = (1 << 0), // only for desktop, before profiles showed up + ECoreProfile = (1 << 1), + ECompatibilityProfile = (1 << 2), + EEsProfile = (1 << 3), + LAST_ELEMENT_MARKER(EProfileCount), +} EProfile; + +namespace glslang { + +// +// Map from profile enum to externally readable text name. +// +inline const char* ProfileName(EProfile profile) +{ + switch (profile) { + case ENoProfile: return "none"; + case ECoreProfile: return "core"; + case ECompatibilityProfile: return "compatibility"; + case EEsProfile: return "es"; + default: return "unknown profile"; + } +} + +// +// What source rules, validation rules, target language, etc. are needed or +// desired for SPIR-V? +// +// 0 means a target or rule set is not enabled (ignore rules from that entity). +// Non-0 means to apply semantic rules arising from that version of its rule set. +// The union of all requested rule sets will be applied. +// +struct SpvVersion { + SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0), vulkanRelaxed(false) {} + unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header + int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX" + int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use + int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX" + bool vulkanRelaxed; // relax changes to GLSL for Vulkan, allowing some GL-specific to be compiled to Vulkan SPIR-V target +}; + +// +// The behaviors from the GLSL "#extension extension_name : behavior" +// +typedef enum { + EBhMissing = 0, + EBhRequire, + EBhEnable, + EBhWarn, + EBhDisable, + EBhDisablePartial // use as initial state of an extension that is only partially implemented +} TExtensionBehavior; + +// +// Symbolic names for extensions. Strings may be directly used when calling the +// functions, but better to have the compiler do spelling checks. +// +const char* const E_GL_OES_texture_3D = "GL_OES_texture_3D"; +const char* const E_GL_OES_standard_derivatives = "GL_OES_standard_derivatives"; +const char* const E_GL_EXT_frag_depth = "GL_EXT_frag_depth"; +const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external"; +const char* const E_GL_OES_EGL_image_external_essl3 = "GL_OES_EGL_image_external_essl3"; +const char* const E_GL_EXT_YUV_target = "GL_EXT_YUV_target"; +const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod"; +const char* const E_GL_EXT_shadow_samplers = "GL_EXT_shadow_samplers"; + +const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle"; +const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects"; +const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack"; +const char* const E_GL_ARB_texture_gather = "GL_ARB_texture_gather"; +const char* const E_GL_ARB_gpu_shader5 = "GL_ARB_gpu_shader5"; +const char* const E_GL_ARB_separate_shader_objects = "GL_ARB_separate_shader_objects"; +const char* const E_GL_ARB_compute_shader = "GL_ARB_compute_shader"; +const char* const E_GL_ARB_tessellation_shader = "GL_ARB_tessellation_shader"; +const char* const E_GL_ARB_enhanced_layouts = "GL_ARB_enhanced_layouts"; +const char* const E_GL_ARB_texture_cube_map_array = "GL_ARB_texture_cube_map_array"; +const char* const E_GL_ARB_texture_multisample = "GL_ARB_texture_multisample"; +const char* const E_GL_ARB_shader_texture_lod = "GL_ARB_shader_texture_lod"; +const char* const E_GL_ARB_explicit_attrib_location = "GL_ARB_explicit_attrib_location"; +const char* const E_GL_ARB_explicit_uniform_location = "GL_ARB_explicit_uniform_location"; +const char* const E_GL_ARB_shader_image_load_store = "GL_ARB_shader_image_load_store"; +const char* const E_GL_ARB_shader_atomic_counters = "GL_ARB_shader_atomic_counters"; +const char* const E_GL_ARB_shader_atomic_counter_ops = "GL_ARB_shader_atomic_counter_ops"; +const char* const E_GL_ARB_shader_draw_parameters = "GL_ARB_shader_draw_parameters"; +const char* const E_GL_ARB_shader_group_vote = "GL_ARB_shader_group_vote"; +const char* const E_GL_ARB_derivative_control = "GL_ARB_derivative_control"; +const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture_image_samples"; +const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; +const char* const E_GL_ARB_gpu_shader_int64 = "GL_ARB_gpu_shader_int64"; +const char* const E_GL_ARB_gpu_shader_fp64 = "GL_ARB_gpu_shader_fp64"; +const char* const E_GL_ARB_shader_ballot = "GL_ARB_shader_ballot"; +const char* const E_GL_ARB_sparse_texture2 = "GL_ARB_sparse_texture2"; +const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture_clamp"; +const char* const E_GL_ARB_shader_stencil_export = "GL_ARB_shader_stencil_export"; +// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members +const char* const E_GL_ARB_post_depth_coverage = "GL_ARB_post_depth_coverage"; +const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array"; +const char* const E_GL_ARB_fragment_shader_interlock = "GL_ARB_fragment_shader_interlock"; +const char* const E_GL_ARB_shader_clock = "GL_ARB_shader_clock"; +const char* const E_GL_ARB_uniform_buffer_object = "GL_ARB_uniform_buffer_object"; +const char* const E_GL_ARB_sample_shading = "GL_ARB_sample_shading"; +const char* const E_GL_ARB_shader_bit_encoding = "GL_ARB_shader_bit_encoding"; +const char* const E_GL_ARB_shader_image_size = "GL_ARB_shader_image_size"; +const char* const E_GL_ARB_shader_storage_buffer_object = "GL_ARB_shader_storage_buffer_object"; +const char* const E_GL_ARB_shading_language_packing = "GL_ARB_shading_language_packing"; +const char* const E_GL_ARB_texture_query_lod = "GL_ARB_texture_query_lod"; +const char* const E_GL_ARB_vertex_attrib_64bit = "GL_ARB_vertex_attrib_64bit"; +const char* const E_GL_ARB_draw_instanced = "GL_ARB_draw_instanced"; +const char* const E_GL_ARB_fragment_coord_conventions = "GL_ARB_fragment_coord_conventions"; +const char* const E_GL_ARB_bindless_texture = "GL_ARB_bindless_texture"; + +const char* const E_GL_KHR_shader_subgroup_basic = "GL_KHR_shader_subgroup_basic"; +const char* const E_GL_KHR_shader_subgroup_vote = "GL_KHR_shader_subgroup_vote"; +const char* const E_GL_KHR_shader_subgroup_arithmetic = "GL_KHR_shader_subgroup_arithmetic"; +const char* const E_GL_KHR_shader_subgroup_ballot = "GL_KHR_shader_subgroup_ballot"; +const char* const E_GL_KHR_shader_subgroup_shuffle = "GL_KHR_shader_subgroup_shuffle"; +const char* const E_GL_KHR_shader_subgroup_shuffle_relative = "GL_KHR_shader_subgroup_shuffle_relative"; +const char* const E_GL_KHR_shader_subgroup_rotate = "GL_KHR_shader_subgroup_rotate"; +const char* const E_GL_KHR_shader_subgroup_clustered = "GL_KHR_shader_subgroup_clustered"; +const char* const E_GL_KHR_shader_subgroup_quad = "GL_KHR_shader_subgroup_quad"; +const char* const E_GL_KHR_memory_scope_semantics = "GL_KHR_memory_scope_semantics"; +const char* const E_GL_KHR_cooperative_matrix = "GL_KHR_cooperative_matrix"; + +const char* const E_GL_EXT_shader_atomic_int64 = "GL_EXT_shader_atomic_int64"; + +const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers"; +const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_load_formatted"; + +const char* const E_GL_EXT_shader_16bit_storage = "GL_EXT_shader_16bit_storage"; +const char* const E_GL_EXT_shader_8bit_storage = "GL_EXT_shader_8bit_storage"; + + +// EXT extensions +const char* const E_GL_EXT_device_group = "GL_EXT_device_group"; +const char* const E_GL_EXT_multiview = "GL_EXT_multiview"; +const char* const E_GL_EXT_post_depth_coverage = "GL_EXT_post_depth_coverage"; +const char* const E_GL_EXT_control_flow_attributes = "GL_EXT_control_flow_attributes"; +const char* const E_GL_EXT_nonuniform_qualifier = "GL_EXT_nonuniform_qualifier"; +const char* const E_GL_EXT_samplerless_texture_functions = "GL_EXT_samplerless_texture_functions"; +const char* const E_GL_EXT_scalar_block_layout = "GL_EXT_scalar_block_layout"; +const char* const E_GL_EXT_fragment_invocation_density = "GL_EXT_fragment_invocation_density"; +const char* const E_GL_EXT_buffer_reference = "GL_EXT_buffer_reference"; +const char* const E_GL_EXT_buffer_reference2 = "GL_EXT_buffer_reference2"; +const char* const E_GL_EXT_buffer_reference_uvec2 = "GL_EXT_buffer_reference_uvec2"; +const char* const E_GL_EXT_demote_to_helper_invocation = "GL_EXT_demote_to_helper_invocation"; +const char* const E_GL_EXT_shader_realtime_clock = "GL_EXT_shader_realtime_clock"; +const char* const E_GL_EXT_debug_printf = "GL_EXT_debug_printf"; +const char* const E_GL_EXT_ray_tracing = "GL_EXT_ray_tracing"; +const char* const E_GL_EXT_ray_query = "GL_EXT_ray_query"; +const char* const E_GL_EXT_ray_flags_primitive_culling = "GL_EXT_ray_flags_primitive_culling"; +const char* const E_GL_EXT_ray_cull_mask = "GL_EXT_ray_cull_mask"; +const char* const E_GL_EXT_blend_func_extended = "GL_EXT_blend_func_extended"; +const char* const E_GL_EXT_shader_implicit_conversions = "GL_EXT_shader_implicit_conversions"; +const char* const E_GL_EXT_fragment_shading_rate = "GL_EXT_fragment_shading_rate"; +const char* const E_GL_EXT_shader_image_int64 = "GL_EXT_shader_image_int64"; +const char* const E_GL_EXT_null_initializer = "GL_EXT_null_initializer"; +const char* const E_GL_EXT_shared_memory_block = "GL_EXT_shared_memory_block"; +const char* const E_GL_EXT_subgroup_uniform_control_flow = "GL_EXT_subgroup_uniform_control_flow"; +const char* const E_GL_EXT_spirv_intrinsics = "GL_EXT_spirv_intrinsics"; +const char* const E_GL_EXT_fragment_shader_barycentric = "GL_EXT_fragment_shader_barycentric"; +const char* const E_GL_EXT_mesh_shader = "GL_EXT_mesh_shader"; +const char* const E_GL_EXT_opacity_micromap = "GL_EXT_opacity_micromap"; +const char* const E_GL_EXT_shader_quad_control = "GL_EXT_shader_quad_control"; +const char* const E_GL_EXT_draw_instanced = "GL_EXT_draw_instanced"; +const char* const E_GL_EXT_texture_array = "GL_EXT_texture_array"; +const char* const E_GL_EXT_maximal_reconvergence = "GL_EXT_maximal_reconvergence"; +const char* const E_GL_EXT_expect_assume = "GL_EXT_expect_assume"; +const char* const E_GL_EXT_control_flow_attributes2 = "GL_EXT_control_flow_attributes2"; +const char* const E_GL_EXT_spec_constant_composites = "GL_EXT_spec_constant_composites"; +const char* const E_GL_EXT_texture_offset_non_const = "GL_EXT_texture_offset_non_const"; +const char* const E_GL_EXT_nontemporal_keyword = "GL_EXT_nontemporal_keyword"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const post_depth_coverageEXTs[] = { E_GL_ARB_post_depth_coverage, E_GL_EXT_post_depth_coverage }; +const int Num_post_depth_coverageEXTs = sizeof(post_depth_coverageEXTs) / sizeof(post_depth_coverageEXTs[0]); + +// Array of extensions to cover both extensions providing ray tracing capabilities. +const char* const ray_tracing_EXTs[] = { E_GL_EXT_ray_query, E_GL_EXT_ray_tracing }; +const int Num_ray_tracing_EXTs = sizeof(ray_tracing_EXTs) / sizeof(ray_tracing_EXTs[0]); + +// OVR extensions +const char* const E_GL_OVR_multiview = "GL_OVR_multiview"; +const char* const E_GL_OVR_multiview2 = "GL_OVR_multiview2"; + +const char* const OVR_multiview_EXTs[] = { E_GL_OVR_multiview, E_GL_OVR_multiview2 }; +const int Num_OVR_multiview_EXTs = sizeof(OVR_multiview_EXTs) / sizeof(OVR_multiview_EXTs[0]); + +// #line and #include +const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; +const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive"; +const char* const E_GL_ARB_shading_language_include = "GL_ARB_shading_language_include"; + +const char* const E_GL_AMD_shader_ballot = "GL_AMD_shader_ballot"; +const char* const E_GL_AMD_shader_trinary_minmax = "GL_AMD_shader_trinary_minmax"; +const char* const E_GL_AMD_shader_explicit_vertex_parameter = "GL_AMD_shader_explicit_vertex_parameter"; +const char* const E_GL_AMD_gcn_shader = "GL_AMD_gcn_shader"; +const char* const E_GL_AMD_gpu_shader_half_float = "GL_AMD_gpu_shader_half_float"; +const char* const E_GL_AMD_texture_gather_bias_lod = "GL_AMD_texture_gather_bias_lod"; +const char* const E_GL_AMD_gpu_shader_int16 = "GL_AMD_gpu_shader_int16"; +const char* const E_GL_AMD_shader_image_load_store_lod = "GL_AMD_shader_image_load_store_lod"; +const char* const E_GL_AMD_shader_fragment_mask = "GL_AMD_shader_fragment_mask"; +const char* const E_GL_AMD_gpu_shader_half_float_fetch = "GL_AMD_gpu_shader_half_float_fetch"; +const char* const E_GL_AMD_shader_early_and_late_fragment_tests = "GL_AMD_shader_early_and_late_fragment_tests"; + +const char* const E_GL_INTEL_shader_integer_functions2 = "GL_INTEL_shader_integer_functions2"; + +const char* const E_GL_NV_sample_mask_override_coverage = "GL_NV_sample_mask_override_coverage"; +const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometry_shader_passthrough"; +const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2"; +const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering"; +const char* const E_GL_NVX_multiview_per_view_attributes = "GL_NVX_multiview_per_view_attributes"; +const char* const E_GL_NV_shader_atomic_int64 = "GL_NV_shader_atomic_int64"; +const char* const E_GL_NV_conservative_raster_underestimation = "GL_NV_conservative_raster_underestimation"; +const char* const E_GL_NV_shader_noperspective_interpolation = "GL_NV_shader_noperspective_interpolation"; +const char* const E_GL_NV_shader_subgroup_partitioned = "GL_NV_shader_subgroup_partitioned"; +const char* const E_GL_NV_shading_rate_image = "GL_NV_shading_rate_image"; +const char* const E_GL_NV_ray_tracing = "GL_NV_ray_tracing"; +const char* const E_GL_NV_ray_tracing_motion_blur = "GL_NV_ray_tracing_motion_blur"; +const char* const E_GL_NV_fragment_shader_barycentric = "GL_NV_fragment_shader_barycentric"; +const char* const E_GL_NV_compute_shader_derivatives = "GL_NV_compute_shader_derivatives"; +const char* const E_GL_NV_shader_texture_footprint = "GL_NV_shader_texture_footprint"; +const char* const E_GL_NV_mesh_shader = "GL_NV_mesh_shader"; +const char* const E_GL_NV_cooperative_matrix = "GL_NV_cooperative_matrix"; +const char* const E_GL_NV_shader_sm_builtins = "GL_NV_shader_sm_builtins"; +const char* const E_GL_NV_integer_cooperative_matrix = "GL_NV_integer_cooperative_matrix"; +const char* const E_GL_NV_shader_invocation_reorder = "GL_NV_shader_invocation_reorder"; +const char* const E_GL_EXT_ray_tracing_position_fetch = "GL_EXT_ray_tracing_position_fetch"; +const char* const E_GL_NV_displacement_micromap = "GL_NV_displacement_micromap"; +const char* const E_GL_NV_shader_atomic_fp16_vector = "GL_NV_shader_atomic_fp16_vector"; +const char* const E_GL_NV_cooperative_matrix2 = "GL_NV_cooperative_matrix2"; +const char* const E_GL_NV_cooperative_vector = "GL_NV_cooperative_vector"; +const char* const E_GL_NV_cluster_acceleration_structure = "GL_NV_cluster_acceleration_structure"; +const char* const E_GL_NV_linear_swept_spheres = "GL_NV_linear_swept_spheres"; + +// ARM +const char* const E_GL_ARM_shader_core_builtins = "GL_ARM_shader_core_builtins"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const viewportEXTs[] = { E_GL_ARB_shader_viewport_layer_array, E_GL_NV_viewport_array2 }; +const int Num_viewportEXTs = sizeof(viewportEXTs) / sizeof(viewportEXTs[0]); + + +const char* const E_GL_QCOM_image_processing = "GL_QCOM_image_processing"; +const char* const E_GL_QCOM_image_processing2 = "GL_QCOM_image_processing2"; + +// AEP +const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; +const char* const E_GL_KHR_blend_equation_advanced = "GL_KHR_blend_equation_advanced"; +const char* const E_GL_OES_sample_variables = "GL_OES_sample_variables"; +const char* const E_GL_OES_shader_image_atomic = "GL_OES_shader_image_atomic"; +const char* const E_GL_OES_shader_multisample_interpolation = "GL_OES_shader_multisample_interpolation"; +const char* const E_GL_OES_texture_storage_multisample_2d_array = "GL_OES_texture_storage_multisample_2d_array"; +const char* const E_GL_EXT_geometry_shader = "GL_EXT_geometry_shader"; +const char* const E_GL_EXT_geometry_point_size = "GL_EXT_geometry_point_size"; +const char* const E_GL_EXT_gpu_shader5 = "GL_EXT_gpu_shader5"; +const char* const E_GL_EXT_primitive_bounding_box = "GL_EXT_primitive_bounding_box"; +const char* const E_GL_EXT_shader_io_blocks = "GL_EXT_shader_io_blocks"; +const char* const E_GL_EXT_tessellation_shader = "GL_EXT_tessellation_shader"; +const char* const E_GL_EXT_tessellation_point_size = "GL_EXT_tessellation_point_size"; +const char* const E_GL_EXT_texture_buffer = "GL_EXT_texture_buffer"; +const char* const E_GL_EXT_texture_cube_map_array = "GL_EXT_texture_cube_map_array"; +const char* const E_GL_EXT_shader_integer_mix = "GL_EXT_shader_integer_mix"; + +// OES matching AEP +const char* const E_GL_OES_geometry_shader = "GL_OES_geometry_shader"; +const char* const E_GL_OES_geometry_point_size = "GL_OES_geometry_point_size"; +const char* const E_GL_OES_gpu_shader5 = "GL_OES_gpu_shader5"; +const char* const E_GL_OES_primitive_bounding_box = "GL_OES_primitive_bounding_box"; +const char* const E_GL_OES_shader_io_blocks = "GL_OES_shader_io_blocks"; +const char* const E_GL_OES_tessellation_shader = "GL_OES_tessellation_shader"; +const char* const E_GL_OES_tessellation_point_size = "GL_OES_tessellation_point_size"; +const char* const E_GL_OES_texture_buffer = "GL_OES_texture_buffer"; +const char* const E_GL_OES_texture_cube_map_array = "GL_OES_texture_cube_map_array"; + +// EXT +const char* const E_GL_EXT_shader_explicit_arithmetic_types = "GL_EXT_shader_explicit_arithmetic_types"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int8 = "GL_EXT_shader_explicit_arithmetic_types_int8"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int16 = "GL_EXT_shader_explicit_arithmetic_types_int16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int32 = "GL_EXT_shader_explicit_arithmetic_types_int32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int64 = "GL_EXT_shader_explicit_arithmetic_types_int64"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float16 = "GL_EXT_shader_explicit_arithmetic_types_float16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float32 = "GL_EXT_shader_explicit_arithmetic_types_float32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float64 = "GL_EXT_shader_explicit_arithmetic_types_float64"; + +const char* const E_GL_EXT_shader_subgroup_extended_types_int8 = "GL_EXT_shader_subgroup_extended_types_int8"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int16 = "GL_EXT_shader_subgroup_extended_types_int16"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int64 = "GL_EXT_shader_subgroup_extended_types_int64"; +const char* const E_GL_EXT_shader_subgroup_extended_types_float16 = "GL_EXT_shader_subgroup_extended_types_float16"; +const char* const E_GL_EXT_terminate_invocation = "GL_EXT_terminate_invocation"; + +const char* const E_GL_EXT_shader_atomic_float = "GL_EXT_shader_atomic_float"; +const char* const E_GL_EXT_shader_atomic_float2 = "GL_EXT_shader_atomic_float2"; + +const char* const E_GL_EXT_shader_tile_image = "GL_EXT_shader_tile_image"; + +const char* const E_GL_EXT_texture_shadow_lod = "GL_EXT_texture_shadow_lod"; + +const char* const E_GL_EXT_integer_dot_product = "GL_EXT_integer_dot_product"; + +const char* const E_GL_EXT_bfloat16 = "GL_EXT_bfloat16"; + +// Arrays of extensions for the above AEP duplications + +const char* const AEP_geometry_shader[] = { E_GL_EXT_geometry_shader, E_GL_OES_geometry_shader }; +const int Num_AEP_geometry_shader = sizeof(AEP_geometry_shader)/sizeof(AEP_geometry_shader[0]); + +const char* const AEP_geometry_point_size[] = { E_GL_EXT_geometry_point_size, E_GL_OES_geometry_point_size }; +const int Num_AEP_geometry_point_size = sizeof(AEP_geometry_point_size)/sizeof(AEP_geometry_point_size[0]); + +const char* const AEP_gpu_shader5[] = { E_GL_EXT_gpu_shader5, E_GL_OES_gpu_shader5 }; +const int Num_AEP_gpu_shader5 = sizeof(AEP_gpu_shader5)/sizeof(AEP_gpu_shader5[0]); + +const char* const AEP_primitive_bounding_box[] = { E_GL_EXT_primitive_bounding_box, E_GL_OES_primitive_bounding_box }; +const int Num_AEP_primitive_bounding_box = sizeof(AEP_primitive_bounding_box)/sizeof(AEP_primitive_bounding_box[0]); + +const char* const AEP_shader_io_blocks[] = { E_GL_EXT_shader_io_blocks, E_GL_OES_shader_io_blocks }; +const int Num_AEP_shader_io_blocks = sizeof(AEP_shader_io_blocks)/sizeof(AEP_shader_io_blocks[0]); + +const char* const AEP_tessellation_shader[] = { E_GL_EXT_tessellation_shader, E_GL_OES_tessellation_shader }; +const int Num_AEP_tessellation_shader = sizeof(AEP_tessellation_shader)/sizeof(AEP_tessellation_shader[0]); + +const char* const AEP_tessellation_point_size[] = { E_GL_EXT_tessellation_point_size, E_GL_OES_tessellation_point_size }; +const int Num_AEP_tessellation_point_size = sizeof(AEP_tessellation_point_size)/sizeof(AEP_tessellation_point_size[0]); + +const char* const AEP_texture_buffer[] = { E_GL_EXT_texture_buffer, E_GL_OES_texture_buffer }; +const int Num_AEP_texture_buffer = sizeof(AEP_texture_buffer)/sizeof(AEP_texture_buffer[0]); + +const char* const AEP_texture_cube_map_array[] = { E_GL_EXT_texture_cube_map_array, E_GL_OES_texture_cube_map_array }; +const int Num_AEP_texture_cube_map_array = sizeof(AEP_texture_cube_map_array)/sizeof(AEP_texture_cube_map_array[0]); + +const char* const AEP_mesh_shader[] = { E_GL_NV_mesh_shader, E_GL_EXT_mesh_shader }; +const int Num_AEP_mesh_shader = sizeof(AEP_mesh_shader)/sizeof(AEP_mesh_shader[0]); + +} // end namespace glslang + +#endif // _VERSIONS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.cpp new file mode 100644 index 00000000..19e8faac --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.cpp @@ -0,0 +1,376 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "attribute.h" +#include "../Include/intermediate.h" +#include "ParseHelper.h" + +namespace glslang { + +// extract integers out of attribute arguments stored in attribute aggregate +bool TAttributeArgs::getInt(int& value, int argNum) const +{ + const TConstUnion* intConst = getConstUnion(EbtInt, argNum); + + if (intConst == nullptr) + return false; + + value = intConst->getIConst(); + return true; +} + + +// extract strings out of attribute arguments stored in attribute aggregate. +// convert to lower case if converToLower is true (for case-insensitive compare convenience) +bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const +{ + const TConstUnion* stringConst = getConstUnion(EbtString, argNum); + + if (stringConst == nullptr) + return false; + + value = *stringConst->getSConst(); + + // Convenience. + if (convertToLower) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + return true; +} + +// How many arguments were supplied? +int TAttributeArgs::size() const +{ + return args == nullptr ? 0 : (int)args->getSequence().size(); +} + +// Helper to get attribute const union. Returns nullptr on failure. +const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const +{ + if (args == nullptr) + return nullptr; + + if (argNum >= (int)args->getSequence().size()) + return nullptr; + + if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr) + return nullptr; + + const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; + if (constVal == nullptr || constVal->getType() != basicType) + return nullptr; + + return constVal; +} + +// Implementation of TParseContext parts of attributes +TAttributeType TParseContext::attributeFromName(const TString& name) const +{ + if (name == "branch" || name == "dont_flatten") + return EatBranch; + else if (name == "flatten") + return EatFlatten; + else if (name == "unroll") + return EatUnroll; + else if (name == "loop" || name == "dont_unroll") + return EatLoop; + else if (name == "dependency_infinite") + return EatDependencyInfinite; + else if (name == "dependency_length") + return EatDependencyLength; + else if (name == "min_iterations") + return EatMinIterations; + else if (name == "max_iterations") + return EatMaxIterations; + else if (name == "iteration_multiple") + return EatIterationMultiple; + else if (name == "peel_count") + return EatPeelCount; + else if (name == "partial_count") + return EatPartialCount; + else if (name == "subgroup_uniform_control_flow") + return EatSubgroupUniformControlFlow; + else if (name == "export") + return EatExport; + else if (name == "maximally_reconverges") + return EatMaximallyReconverges; + else + return EatNone; +} + +// Make an initial leaf for the grammar from a no-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + TAttributeArgs args = { attributeFromName(identifier), nullptr }; + attributes->push_back(args); + return attributes; +} + +// Make an initial leaf for the grammar from a one-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + + // for now, node is always a simple single expression, but other code expects + // a list, so make it so + TIntermAggregate* agg = intermediate.makeAggregate(node); + TAttributeArgs args = { attributeFromName(identifier), agg }; + attributes->push_back(args); + return attributes; +} + +// Merge two sets of attributes into a single set. +// The second argument is destructively consumed. +TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const +{ + attr1->splice(attr1->end(), *attr2); + return attr1; +} + +// +// Selection attributes +// +void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSelection* selection = node->getAsSelectionNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSwitch* selection = node->getAsSwitchNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a switch", "", ""); + break; + } + } +} + +// +// Loop attributes +// +void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermLoop* loop = node->getAsLoopNode(); + if (loop == nullptr) { + // the actual loop might be part of a sequence + TIntermAggregate* agg = node->getAsAggregate(); + if (agg == nullptr) + return; + for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) { + loop = (*it)->getAsLoopNode(); + if (loop != nullptr) + break; + } + if (loop == nullptr) + return; + } + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + + const auto noArgument = [&](const char* feature) { + if (it->size() > 0) { + warn(node->getLoc(), "expected no arguments", feature, ""); + return false; + } + return true; + }; + + const auto positiveSignedArgument = [&](const char* feature, int& value) { + if (it->size() == 1 && it->getInt(value)) { + if (value <= 0) { + error(node->getLoc(), "must be positive", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + return true; + }; + + const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (!(it->size() == 1 && it->getInt(value))) { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (it->size() == 1 && it->getInt(value)) { + if (value == 0) { + error(node->getLoc(), "must be greater than or equal to 1", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto spirv14 = [&](const char* feature) { + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) + warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, ""); + }; + + int value = 0; + unsigned uiValue = 0; + switch (it->name) { + case EatUnroll: + if (noArgument("unroll")) + loop->setUnroll(); + break; + case EatLoop: + if (noArgument("dont_unroll")) + loop->setDontUnroll(); + break; + case EatDependencyInfinite: + if (noArgument("dependency_infinite")) + loop->setLoopDependency(TIntermLoop::dependencyInfinite); + break; + case EatDependencyLength: + if (positiveSignedArgument("dependency_length", value)) + loop->setLoopDependency(value); + break; + case EatMinIterations: + spirv14("min_iterations"); + if (unsignedArgument("min_iterations", uiValue)) + loop->setMinIterations(uiValue); + break; + case EatMaxIterations: + spirv14("max_iterations"); + if (unsignedArgument("max_iterations", uiValue)) + loop->setMaxIterations(uiValue); + break; + case EatIterationMultiple: + spirv14("iteration_multiple"); + if (positiveUnsignedArgument("iteration_multiple", uiValue)) + loop->setIterationMultiple(uiValue); + break; + case EatPeelCount: + spirv14("peel_count"); + if (unsignedArgument("peel_count", uiValue)) + loop->setPeelCount(uiValue); + break; + case EatPartialCount: + spirv14("partial_count"); + if (unsignedArgument("partial_count", uiValue)) + loop->setPartialCount(uiValue); + break; + default: + warn(node->getLoc(), "attribute does not apply to a loop", "", ""); + break; + } + } +} + + +// +// Function attributes +// +void TParseContext::handleFunctionAttributes(const TSourceLoc& loc, const TAttributes& attributes) +{ + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(loc, "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatSubgroupUniformControlFlow: + requireExtensions(loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute"); + intermediate.setSubgroupUniformControlFlow(); + break; + case EatMaximallyReconverges: + requireExtensions(loc, 1, &E_GL_EXT_maximal_reconvergence, "attribute"); + intermediate.setMaximallyReconverges(); + break; + default: + warn(loc, "attribute does not apply to a function", "", ""); + break; + } + } +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.h new file mode 100644 index 00000000..3b480c6f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/attribute.h @@ -0,0 +1,152 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _ATTRIBUTE_INCLUDED_ +#define _ATTRIBUTE_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + + enum TAttributeType { + EatNone, + EatAllow_uav_condition, + EatBranch, + EatCall, + EatDomain, + EatEarlyDepthStencil, + EatFastOpt, + EatFlatten, + EatForceCase, + EatInstance, + EatMaxTessFactor, + EatNumThreads, + EatMaxVertexCount, + EatOutputControlPoints, + EatOutputTopology, + EatPartitioning, + EatPatchConstantFunc, + EatPatchSize, + EatUnroll, + EatLoop, + EatBinding, + EatGlobalBinding, + EatLocation, + EatInputAttachment, + EatBuiltIn, + EatPushConstant, + EatConstantId, + EatDependencyInfinite, + EatDependencyLength, + EatMinIterations, + EatMaxIterations, + EatIterationMultiple, + EatPeelCount, + EatPartialCount, + EatFormatRgba32f, + EatFormatRgba16f, + EatFormatR32f, + EatFormatRgba8, + EatFormatRgba8Snorm, + EatFormatRg32f, + EatFormatRg16f, + EatFormatR11fG11fB10f, + EatFormatR16f, + EatFormatRgba16, + EatFormatRgb10A2, + EatFormatRg16, + EatFormatRg8, + EatFormatR16, + EatFormatR8, + EatFormatRgba16Snorm, + EatFormatRg16Snorm, + EatFormatRg8Snorm, + EatFormatR16Snorm, + EatFormatR8Snorm, + EatFormatRgba32i, + EatFormatRgba16i, + EatFormatRgba8i, + EatFormatR32i, + EatFormatRg32i, + EatFormatRg16i, + EatFormatRg8i, + EatFormatR16i, + EatFormatR8i, + EatFormatRgba32ui, + EatFormatRgba16ui, + EatFormatRgba8ui, + EatFormatR32ui, + EatFormatRgb10a2ui, + EatFormatRg32ui, + EatFormatRg16ui, + EatFormatRg8ui, + EatFormatR16ui, + EatFormatR8ui, + EatFormatUnknown, + EatNonWritable, + EatNonReadable, + EatSubgroupUniformControlFlow, + EatExport, + EatMaximallyReconverges, + }; + + class TIntermAggregate; + + struct TAttributeArgs { + TAttributeType name; + const TIntermAggregate* args; + + // Obtain attribute as integer + // Return false if it cannot be obtained + bool getInt(int& value, int argNum = 0) const; + + // Obtain attribute as string, with optional to-lower transform + // Return false if it cannot be obtained + bool getString(TString& value, int argNum = 0, bool convertToLower = true) const; + + // How many arguments were provided to the attribute? + int size() const; + + protected: + const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const; + }; + + typedef TList TAttributes; + +} // end namespace glslang + +#endif // _ATTRIBUTE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/gl_types.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/gl_types.h new file mode 100644 index 00000000..d6c93937 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/gl_types.h @@ -0,0 +1,218 @@ +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#pragma once + +#define GL_FLOAT 0x1406 +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 + +#define GL_DOUBLE 0x140A +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE + +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 + +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 + +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB + +#define GL_UNSIGNED_INT64_ARB 0x140F +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 + +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 + +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 + +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A + +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E + +// Those constants are borrowed from extension NV_gpu_shader5 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB + +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD + +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D + +#define GL_FLOAT16_SAMPLER_1D_AMD 0x91CE +#define GL_FLOAT16_SAMPLER_2D_AMD 0x91CF +#define GL_FLOAT16_SAMPLER_3D_AMD 0x91D0 +#define GL_FLOAT16_SAMPLER_CUBE_AMD 0x91D1 +#define GL_FLOAT16_SAMPLER_2D_RECT_AMD 0x91D2 +#define GL_FLOAT16_SAMPLER_1D_ARRAY_AMD 0x91D3 +#define GL_FLOAT16_SAMPLER_2D_ARRAY_AMD 0x91D4 +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD 0x91D5 +#define GL_FLOAT16_SAMPLER_BUFFER_AMD 0x91D6 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD 0x91D7 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD 0x91D8 + +#define GL_FLOAT16_SAMPLER_1D_SHADOW_AMD 0x91D9 +#define GL_FLOAT16_SAMPLER_2D_SHADOW_AMD 0x91DA +#define GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD 0x91DB +#define GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD 0x91DC +#define GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD 0x91DD +#define GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD 0x91DE +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD 0x91DF + +#define GL_FLOAT16_IMAGE_1D_AMD 0x91E0 +#define GL_FLOAT16_IMAGE_2D_AMD 0x91E1 +#define GL_FLOAT16_IMAGE_3D_AMD 0x91E2 +#define GL_FLOAT16_IMAGE_2D_RECT_AMD 0x91E3 +#define GL_FLOAT16_IMAGE_CUBE_AMD 0x91E4 +#define GL_FLOAT16_IMAGE_1D_ARRAY_AMD 0x91E5 +#define GL_FLOAT16_IMAGE_2D_ARRAY_AMD 0x91E6 +#define GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD 0x91E7 +#define GL_FLOAT16_IMAGE_BUFFER_AMD 0x91E8 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD 0x91E9 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD 0x91EA + +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E + +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A + +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C + +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang.y b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang.y new file mode 100644 index 00000000..006ca164 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang.y @@ -0,0 +1,4508 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2019 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +/** + * This is bison grammar and productions for parsing all versions of the + * GLSL shading languages. + */ +%{ + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + +%} + +%define parse.error verbose + +%union { + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + glslang::TSpirvRequirement* spirvReq; + glslang::TSpirvInstruction* spirvInst; + glslang::TSpirvTypeParameters* spirvTypeParams; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TTypeParameters* typeParameters; + } interm; +} + +%{ + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + +%} + +%parse-param {glslang::TParseContext* pParseContext} +%lex-param {parseContext} +%pure-parser // enable thread safety +%expect 1 // One shift reduce conflict because of if | else + +%token CONST BOOL INT UINT FLOAT +%token BVEC2 BVEC3 BVEC4 +%token IVEC2 IVEC3 IVEC4 +%token UVEC2 UVEC3 UVEC4 +%token VEC2 VEC3 VEC4 +%token MAT2 MAT3 MAT4 +%token MAT2X2 MAT2X3 MAT2X4 +%token MAT3X2 MAT3X3 MAT3X4 +%token MAT4X2 MAT4X3 MAT4X4 + +// combined image/sampler +%token SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER2DSHADOW +%token SAMPLERCUBESHADOW SAMPLER2DARRAY +%token SAMPLER2DARRAYSHADOW ISAMPLER2D ISAMPLER3D ISAMPLERCUBE +%token ISAMPLER2DARRAY USAMPLER2D USAMPLER3D +%token USAMPLERCUBE USAMPLER2DARRAY + +// separate image/sampler +%token SAMPLER SAMPLERSHADOW +%token TEXTURE2D TEXTURE3D TEXTURECUBE TEXTURE2DARRAY +%token ITEXTURE2D ITEXTURE3D ITEXTURECUBE ITEXTURE2DARRAY +%token UTEXTURE2D UTEXTURE3D UTEXTURECUBE UTEXTURE2DARRAY + +%token ATTRIBUTE VARYING +%token BFLOAT16_T FLOAT16_T FLOAT32_T DOUBLE FLOAT64_T +%token INT64_T UINT64_T INT32_T UINT32_T INT16_T UINT16_T INT8_T UINT8_T +%token I64VEC2 I64VEC3 I64VEC4 +%token U64VEC2 U64VEC3 U64VEC4 +%token I32VEC2 I32VEC3 I32VEC4 +%token U32VEC2 U32VEC3 U32VEC4 +%token I16VEC2 I16VEC3 I16VEC4 +%token U16VEC2 U16VEC3 U16VEC4 +%token I8VEC2 I8VEC3 I8VEC4 +%token U8VEC2 U8VEC3 U8VEC4 +%token DVEC2 DVEC3 DVEC4 DMAT2 DMAT3 DMAT4 +%token BF16VEC2 BF16VEC3 BF16VEC4 +%token F16VEC2 F16VEC3 F16VEC4 F16MAT2 F16MAT3 F16MAT4 +%token F32VEC2 F32VEC3 F32VEC4 F32MAT2 F32MAT3 F32MAT4 +%token F64VEC2 F64VEC3 F64VEC4 F64MAT2 F64MAT3 F64MAT4 +%token DMAT2X2 DMAT2X3 DMAT2X4 +%token DMAT3X2 DMAT3X3 DMAT3X4 +%token DMAT4X2 DMAT4X3 DMAT4X4 +%token F16MAT2X2 F16MAT2X3 F16MAT2X4 +%token F16MAT3X2 F16MAT3X3 F16MAT3X4 +%token F16MAT4X2 F16MAT4X3 F16MAT4X4 +%token F32MAT2X2 F32MAT2X3 F32MAT2X4 +%token F32MAT3X2 F32MAT3X3 F32MAT3X4 +%token F32MAT4X2 F32MAT4X3 F32MAT4X4 +%token F64MAT2X2 F64MAT2X3 F64MAT2X4 +%token F64MAT3X2 F64MAT3X3 F64MAT3X4 +%token F64MAT4X2 F64MAT4X3 F64MAT4X4 +%token ATOMIC_UINT +%token ACCSTRUCTNV +%token ACCSTRUCTEXT +%token RAYQUERYEXT +%token FCOOPMATNV ICOOPMATNV UCOOPMATNV +%token COOPMAT +%token COOPVECNV +%token HITOBJECTNV HITOBJECTATTRNV +%token TENSORLAYOUTNV TENSORVIEWNV + +// combined image/sampler +%token SAMPLERCUBEARRAY SAMPLERCUBEARRAYSHADOW +%token ISAMPLERCUBEARRAY USAMPLERCUBEARRAY +%token SAMPLER1D SAMPLER1DARRAY SAMPLER1DARRAYSHADOW ISAMPLER1D SAMPLER1DSHADOW +%token SAMPLER2DRECT SAMPLER2DRECTSHADOW ISAMPLER2DRECT USAMPLER2DRECT +%token SAMPLERBUFFER ISAMPLERBUFFER USAMPLERBUFFER +%token SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS +%token SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY +%token SAMPLEREXTERNALOES +%token SAMPLEREXTERNAL2DY2YEXT +%token ISAMPLER1DARRAY USAMPLER1D USAMPLER1DARRAY +%token F16SAMPLER1D F16SAMPLER2D F16SAMPLER3D F16SAMPLER2DRECT F16SAMPLERCUBE +%token F16SAMPLER1DARRAY F16SAMPLER2DARRAY F16SAMPLERCUBEARRAY +%token F16SAMPLERBUFFER F16SAMPLER2DMS F16SAMPLER2DMSARRAY +%token F16SAMPLER1DSHADOW F16SAMPLER2DSHADOW F16SAMPLER1DARRAYSHADOW F16SAMPLER2DARRAYSHADOW +%token F16SAMPLER2DRECTSHADOW F16SAMPLERCUBESHADOW F16SAMPLERCUBEARRAYSHADOW + +// images +%token IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D +%token UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D +%token IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT +%token IMAGECUBE IIMAGECUBE UIMAGECUBE +%token IMAGEBUFFER IIMAGEBUFFER UIMAGEBUFFER +%token IMAGE1DARRAY IIMAGE1DARRAY UIMAGE1DARRAY +%token IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token IMAGECUBEARRAY IIMAGECUBEARRAY UIMAGECUBEARRAY +%token IMAGE2DMS IIMAGE2DMS UIMAGE2DMS +%token IMAGE2DMSARRAY IIMAGE2DMSARRAY UIMAGE2DMSARRAY + +%token F16IMAGE1D F16IMAGE2D F16IMAGE3D F16IMAGE2DRECT +%token F16IMAGECUBE F16IMAGE1DARRAY F16IMAGE2DARRAY F16IMAGECUBEARRAY +%token F16IMAGEBUFFER F16IMAGE2DMS F16IMAGE2DMSARRAY + +%token I64IMAGE1D U64IMAGE1D +%token I64IMAGE2D U64IMAGE2D +%token I64IMAGE3D U64IMAGE3D +%token I64IMAGE2DRECT U64IMAGE2DRECT +%token I64IMAGECUBE U64IMAGECUBE +%token I64IMAGEBUFFER U64IMAGEBUFFER +%token I64IMAGE1DARRAY U64IMAGE1DARRAY +%token I64IMAGE2DARRAY U64IMAGE2DARRAY +%token I64IMAGECUBEARRAY U64IMAGECUBEARRAY +%token I64IMAGE2DMS U64IMAGE2DMS +%token I64IMAGE2DMSARRAY U64IMAGE2DMSARRAY + +// texture without sampler +%token TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY +%token TEXTURE1D ITEXTURE1D UTEXTURE1D +%token TEXTURE1DARRAY ITEXTURE1DARRAY UTEXTURE1DARRAY +%token TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT +%token TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER +%token TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS +%token TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY + +%token F16TEXTURE1D F16TEXTURE2D F16TEXTURE3D F16TEXTURE2DRECT F16TEXTURECUBE +%token F16TEXTURE1DARRAY F16TEXTURE2DARRAY F16TEXTURECUBEARRAY +%token F16TEXTUREBUFFER F16TEXTURE2DMS F16TEXTURE2DMSARRAY + +// input attachments +%token SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS +%token F16SUBPASSINPUT F16SUBPASSINPUTMS + +// spirv intrinsics +%token SPIRV_INSTRUCTION SPIRV_EXECUTION_MODE SPIRV_EXECUTION_MODE_ID +%token SPIRV_DECORATE SPIRV_DECORATE_ID SPIRV_DECORATE_STRING +%token SPIRV_TYPE SPIRV_STORAGE_CLASS SPIRV_BY_REFERENCE SPIRV_LITERAL +%token ATTACHMENTEXT IATTACHMENTEXT UATTACHMENTEXT + +%token LEFT_OP RIGHT_OP +%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN +%token MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN +%token SUB_ASSIGN +%token STRING_LITERAL + +%token LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT +%token COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT +%token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION + +%token INVARIANT +%token HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION +%token PACKED RESOURCE SUPERP + +%token FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT +%token IDENTIFIER TYPE_NAME +%token CENTROID IN OUT INOUT +%token STRUCT VOID WHILE +%token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT +%token TERMINATE_INVOCATION +%token TERMINATE_RAY IGNORE_INTERSECTION +%token UNIFORM SHARED BUFFER TILEIMAGEEXT +%token FLAT SMOOTH LAYOUT + +%token DOUBLECONSTANT INT16CONSTANT UINT16CONSTANT FLOAT16CONSTANT INT32CONSTANT UINT32CONSTANT +%token INT64CONSTANT UINT64CONSTANT +%token SUBROUTINE DEMOTE FUNCTION +%token PAYLOADNV PAYLOADINNV HITATTRNV CALLDATANV CALLDATAINNV +%token PAYLOADEXT PAYLOADINEXT HITATTREXT CALLDATAEXT CALLDATAINEXT +%token PATCH SAMPLE NONUNIFORM +%token COHERENT VOLATILE RESTRICT READONLY WRITEONLY NONTEMPORAL DEVICECOHERENT QUEUEFAMILYCOHERENT WORKGROUPCOHERENT +%token SUBGROUPCOHERENT NONPRIVATE SHADERCALLCOHERENT +%token NOPERSPECTIVE EXPLICITINTERPAMD PERVERTEXEXT PERVERTEXNV PERPRIMITIVENV PERVIEWNV PERTASKNV PERPRIMITIVEEXT TASKPAYLOADWORKGROUPEXT +%token PRECISE + +%type assignment_operator unary_operator +%type variable_identifier primary_expression postfix_expression +%type expression integer_expression assignment_expression +%type unary_expression multiplicative_expression additive_expression +%type relational_expression equality_expression +%type conditional_expression constant_expression +%type logical_or_expression logical_xor_expression logical_and_expression +%type shift_expression and_expression exclusive_or_expression inclusive_or_expression +%type function_call initializer condition conditionopt + +%type translation_unit function_definition +%type statement simple_statement +%type statement_list switch_statement_list compound_statement +%type declaration_statement selection_statement selection_statement_nonattributed expression_statement +%type switch_statement switch_statement_nonattributed case_label +%type declaration external_declaration +%type for_init_statement compound_statement_no_new_scope +%type selection_rest_statement for_rest_statement +%type iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped +%type single_declaration init_declarator_list + +%type parameter_declaration parameter_declarator parameter_type_specifier + +%type array_specifier +%type invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier +%type layout_qualifier layout_qualifier_id_list layout_qualifier_id + +%type type_parameter_specifier +%type type_parameter_specifier_opt +%type type_parameter_specifier_list + +%type type_qualifier fully_specified_type type_specifier +%type single_type_qualifier +%type type_specifier_nonarray +%type struct_specifier +%type struct_declarator +%type struct_declarator_list struct_declaration struct_declaration_list +%type block_structure +%type function_header function_declarator +%type function_header_with_parameters +%type function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype +%type function_call_or_method function_identifier function_call_header + +%type identifier_list + +%type precise_qualifier non_uniform_qualifier +%type type_name_list +%type attribute attribute_list single_attribute +%type demote_statement +%type initializer_list +%type spirv_requirements_list spirv_requirements_parameter +%type spirv_extension_list spirv_capability_list +%type spirv_execution_mode_qualifier +%type spirv_execution_mode_parameter_list spirv_execution_mode_parameter spirv_execution_mode_id_parameter_list +%type spirv_storage_class_qualifier +%type spirv_decorate_qualifier +%type spirv_decorate_parameter_list spirv_decorate_parameter +%type spirv_decorate_id_parameter_list spirv_decorate_id_parameter +%type spirv_decorate_string_parameter_list +%type spirv_type_specifier +%type spirv_type_parameter_list spirv_type_parameter +%type spirv_instruction_qualifier +%type spirv_instruction_qualifier_list spirv_instruction_qualifier_id + +%start translation_unit +%% + +variable_identifier + : IDENTIFIER { + $$ = parseContext.handleVariable($1.loc, $1.symbol, $1.string); + } + ; + +primary_expression + : variable_identifier { + $$ = $1; + } + | LEFT_PAREN expression RIGHT_PAREN { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + | FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + parseContext.fullIntegerCheck($1.loc, "unsigned literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + | STRING_LITERAL { + $$ = parseContext.intermediate.addConstantUnion($1.string, $1.loc, true); + } + | INT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | INT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i64, $1.loc, true); + } + | UINT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u64, $1.loc, true); + } + | INT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion((short)$1.i, $1.loc, true); + } + | UINT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion((unsigned short)$1.u, $1.loc, true); + } + | DOUBLECONSTANT { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtDouble, $1.loc, true); + } + | FLOAT16CONSTANT { + parseContext.float16Check($1.loc, "half float literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat16, $1.loc, true); + } + ; + +postfix_expression + : primary_expression { + $$ = $1; + } + | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { + $$ = parseContext.handleBracketDereference($2.loc, $1, $3); + } + | function_call { + $$ = $1; + } + | postfix_expression DOT IDENTIFIER { + $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string); + } + | postfix_expression INC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "++", $1); + $$ = parseContext.handleUnaryMath($2.loc, "++", EOpPostIncrement, $1); + } + | postfix_expression DEC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "--", $1); + $$ = parseContext.handleUnaryMath($2.loc, "--", EOpPostDecrement, $1); + } + ; + +integer_expression + : expression { + parseContext.integerCheck($1, "[]"); + $$ = $1; + } + ; + +function_call + : function_call_or_method { + $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode); + delete $1.function; + } + ; + +function_call_or_method + : function_call_generic { + $$ = $1; + } + ; + +function_call_generic + : function_call_header_with_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + | function_call_header_no_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + ; + +function_call_header_no_parameters + : function_call_header VOID { + $$ = $1; + } + | function_call_header { + $$ = $1; + } + ; + +function_call_header_with_parameters + : function_call_header assignment_expression { + if (parseContext.spvVersion.vulkan > 0 + && parseContext.spvVersion.vulkanRelaxed + && $2->getType().containsOpaque()) + { + $$.intermNode = parseContext.vkRelaxedRemapFunctionArgument($$.loc, $1.function, $2); + $$.function = $1.function; + } + else + { + TParameter param = { 0, new TType, {} }; + param.type->shallowCopy($2->getType()); + + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = $2; + } + } + | function_call_header_with_parameters COMMA assignment_expression { + if (parseContext.spvVersion.vulkan > 0 + && parseContext.spvVersion.vulkanRelaxed + && $3->getType().containsOpaque()) + { + TIntermNode* remappedNode = parseContext.vkRelaxedRemapFunctionArgument($2.loc, $1.function, $3); + if (remappedNode == $3) + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc); + else + $$.intermNode = parseContext.intermediate.mergeAggregate($1.intermNode, remappedNode, $2.loc); + $$.function = $1.function; + } + else + { + TParameter param = { 0, new TType, {} }; + param.type->shallowCopy($3->getType()); + + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc); + } + } + ; + +function_call_header + : function_identifier LEFT_PAREN { + $$ = $1; + } + ; + +// Grammar Note: Constructors look like functions, but are recognized as types. + +function_identifier + : type_specifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + | postfix_expression { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + $$.function = 0; + $$.intermNode = 0; + + TIntermMethod* method = $1->getAsMethodNode(); + if (method) { + $$.function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + $$.intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = $1->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + $$.function = function; + } else + parseContext.error($1->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ($$.function == 0) { + // error recover + TString* empty = NewPoolTString(""); + $$.function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } + | non_uniform_qualifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + ; + +unary_expression + : postfix_expression { + parseContext.variableCheck($1); + $$ = $1; + if (TIntermMethod* method = $1->getAsMethodNode()) + parseContext.error($1->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } + | INC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "++", $2); + $$ = parseContext.handleUnaryMath($1.loc, "++", EOpPreIncrement, $2); + } + | DEC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "--", $2); + $$ = parseContext.handleUnaryMath($1.loc, "--", EOpPreDecrement, $2); + } + | unary_operator unary_expression { + if ($1.op != EOpNull) { + char errorOp[2] = {0, 0}; + switch($1.op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + $$ = parseContext.handleUnaryMath($1.loc, errorOp, $1.op, $2); + } else { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + } + ; +// Grammar Note: No traditional style type casts. + +unary_operator + : PLUS { $$.loc = $1.loc; $$.op = EOpNull; } + | DASH { $$.loc = $1.loc; $$.op = EOpNegative; } + | BANG { $$.loc = $1.loc; $$.op = EOpLogicalNot; } + | TILDE { $$.loc = $1.loc; $$.op = EOpBitwiseNot; + parseContext.fullIntegerCheck($1.loc, "bitwise not"); } + ; +// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. + +multiplicative_expression + : unary_expression { $$ = $1; } + | multiplicative_expression STAR unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "*", EOpMul, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression SLASH unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "/", EOpDiv, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression PERCENT unary_expression { + parseContext.fullIntegerCheck($2.loc, "%"); + $$ = parseContext.handleBinaryMath($2.loc, "%", EOpMod, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression PLUS multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "+", EOpAdd, $1, $3); + if ($$ == 0) + $$ = $1; + } + | additive_expression DASH multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "-", EOpSub, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift left"); + $$ = parseContext.handleBinaryMath($2.loc, "<<", EOpLeftShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + | shift_expression RIGHT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift right"); + $$ = parseContext.handleBinaryMath($2.loc, ">>", EOpRightShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression LEFT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<", EOpLessThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression RIGHT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">", EOpGreaterThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression LE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<=", EOpLessThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression GE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">=", EOpGreaterThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "=="); + parseContext.specializationCheck($2.loc, $1->getType(), "=="); + parseContext.referenceCheck($2.loc, $1->getType(), "=="); + $$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | equality_expression NE_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "!="); + parseContext.specializationCheck($2.loc, $1->getType(), "!="); + parseContext.referenceCheck($2.loc, $1->getType(), "!="); + $$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression AMPERSAND equality_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise and"); + $$ = parseContext.handleBinaryMath($2.loc, "&", EOpAnd, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression CARET and_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise exclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "^", EOpExclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression VERTICAL_BAR exclusive_or_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise inclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "|", EOpInclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { + $$ = parseContext.handleBinaryMath($2.loc, "&&", EOpLogicalAnd, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_xor_expression + : logical_and_expression { $$ = $1; } + | logical_xor_expression XOR_OP logical_and_expression { + $$ = parseContext.handleBinaryMath($2.loc, "^^", EOpLogicalXor, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_or_expression + : logical_xor_expression { $$ = $1; } + | logical_or_expression OR_OP logical_xor_expression { + $$ = parseContext.handleBinaryMath($2.loc, "||", EOpLogicalOr, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression QUESTION { + ++parseContext.controlFlowNestingLevel; + } + expression COLON assignment_expression { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck($2.loc, $1); + parseContext.rValueErrorCheck($2.loc, "?", $1); + parseContext.rValueErrorCheck($5.loc, ":", $4); + parseContext.rValueErrorCheck($5.loc, ":", $6); + $$ = parseContext.intermediate.addSelection($1, $4, $6, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ":", $4->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), $6->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + $$ = $6; + } + } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment"); + parseContext.opaqueCheck($2.loc, $1->getType(), "="); + parseContext.storage16BitAssignmentCheck($2.loc, $1->getType(), "="); + parseContext.specializationCheck($2.loc, $1->getType(), "="); + parseContext.lValueErrorCheck($2.loc, "assign", $1); + parseContext.rValueErrorCheck($2.loc, "assign", $3); + $$ = parseContext.addAssign($2.loc, $2.op, $1, $3); + if ($$ == 0) { + parseContext.assignError($2.loc, "assign", $1->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), $3->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + $$ = $1; + } + } + ; + +assignment_operator + : EQUAL { + $$.loc = $1.loc; + $$.op = EOpAssign; + } + | MUL_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpMulAssign; + } + | DIV_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpDivAssign; + } + | MOD_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "%="); + $$.loc = $1.loc; + $$.op = EOpModAssign; + } + | ADD_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpAddAssign; + } + | SUB_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpSubAssign; + } + | LEFT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift left assign"); + $$.loc = $1.loc; $$.op = EOpLeftShiftAssign; + } + | RIGHT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift right assign"); + $$.loc = $1.loc; $$.op = EOpRightShiftAssign; + } + | AND_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-and assign"); + $$.loc = $1.loc; $$.op = EOpAndAssign; + } + | XOR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-xor assign"); + $$.loc = $1.loc; $$.op = EOpExclusiveOrAssign; + } + | OR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-or assign"); + $$.loc = $1.loc; $$.op = EOpInclusiveOrAssign; + } + ; + +expression + : assignment_expression { + $$ = $1; + } + | expression COMMA assignment_expression { + parseContext.samplerConstructorLocationCheck($2.loc, ",", $3); + $$ = parseContext.intermediate.addComma($1, $3, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ",", $1->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), $3->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + $$ = $3; + } + } + ; + +constant_expression + : conditional_expression { + parseContext.constantValueCheck($1, ""); + $$ = $1; + } + ; + +declaration + : function_prototype SEMICOLON { + parseContext.handleFunctionDeclarator($1.loc, *$1.function, true /* prototype */); + $$ = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } + | spirv_instruction_qualifier function_prototype SEMICOLON { + parseContext.requireExtensions($2.loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V instruction qualifier"); + $2.function->setSpirvInstruction(*$1); // Attach SPIR-V intruction qualifier + parseContext.handleFunctionDeclarator($2.loc, *$2.function, true /* prototype */); + $$ = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } + | spirv_execution_mode_qualifier SEMICOLON { + parseContext.globalCheck($2.loc, "SPIR-V execution mode qualifier"); + parseContext.requireExtensions($2.loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V execution mode qualifier"); + $$ = 0; + } + | init_declarator_list SEMICOLON { + if ($1.intermNode && $1.intermNode->getAsAggregate()) + $1.intermNode->getAsAggregate()->setOperator(EOpSequence); + $$ = $1.intermNode; + } + | PRECISION precision_qualifier type_specifier SEMICOLON { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision); + $$ = 0; + } + | block_structure SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList); + $$ = 0; + } + | block_structure IDENTIFIER SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string); + $$ = 0; + } + | block_structure IDENTIFIER array_specifier SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string, $3.arraySizes); + $$ = 0; + } + | type_qualifier SEMICOLON { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.updateStandaloneQualifierDefaults($1.loc, $1); + $$ = 0; + } + | type_qualifier IDENTIFIER SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string); + $$ = 0; + } + | type_qualifier IDENTIFIER identifier_list SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + $3->push_back($2.string); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3); + $$ = 0; + } + ; + +block_structure + : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + --parseContext.blockNestingLevel; + parseContext.blockName = $2.string; + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.currentBlockQualifier = $1.qualifier; + $$.loc = $1.loc; + $$.typeList = $5; + } + +identifier_list + : COMMA IDENTIFIER { + $$ = new TIdentifierList; + $$->push_back($2.string); + } + | identifier_list COMMA IDENTIFIER { + $$ = $1; + $$->push_back($3.string); + } + ; + +function_prototype + : function_declarator RIGHT_PAREN { + $$.function = $1; + if (parseContext.compileOnly) $$.function->setExport(); + $$.loc = $2.loc; + } + | function_declarator RIGHT_PAREN attribute { + $$.function = $1; + if (parseContext.compileOnly) $$.function->setExport(); + $$.loc = $2.loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions($2.loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes($2.loc, *$3); + } + | attribute function_declarator RIGHT_PAREN { + $$.function = $2; + if (parseContext.compileOnly) $$.function->setExport(); + $$.loc = $3.loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions($3.loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes($3.loc, *$1); + } + | attribute function_declarator RIGHT_PAREN attribute { + $$.function = $2; + if (parseContext.compileOnly) $$.function->setExport(); + $$.loc = $3.loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions($3.loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes($3.loc, *$1); + parseContext.handleFunctionAttributes($3.loc, *$4); + } + ; + +function_declarator + : function_header { + $$ = $1; + } + | function_header_with_parameters { + $$ = $1; + } + ; + + +function_header_with_parameters + : function_header parameter_declaration { + // Add the parameter + $$ = $1; + if ($2.param.type->getBasicType() != EbtVoid) + { + if (!(parseContext.spvVersion.vulkan > 0 && parseContext.spvVersion.vulkanRelaxed)) + $1->addParameter($2.param); + else + parseContext.vkRelaxedRemapFunctionParameter($1, $2.param); + } + else + delete $2.param.type; + } + | function_header_with_parameters COMMA parameter_declaration { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ($3.param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error($2.loc, "cannot be an argument type except for '(void)'", "void", ""); + delete $3.param.type; + } else { + // Add the parameter + $$ = $1; + if (!(parseContext.spvVersion.vulkan > 0 && parseContext.spvVersion.vulkanRelaxed)) + $1->addParameter($3.param); + else + parseContext.vkRelaxedRemapFunctionParameter($1, $3.param); + } + } + ; + +function_header + : fully_specified_type IDENTIFIER LEFT_PAREN { + if ($1.qualifier.storage != EvqGlobal && $1.qualifier.storage != EvqTemporary) { + parseContext.error($2.loc, "no qualifiers allowed for function return", + GetStorageQualifierString($1.qualifier.storage), ""); + } + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type($1); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction($2.string); + + // Make the function + function = new TFunction($2.string, type); + $$ = function; + } + ; + +parameter_declarator + // Type + name + : type_specifier IDENTIFIER { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + if ($1.basicType == EbtVoid) { + parseContext.error($2.loc, "illegal use of type 'void'", $2.string->c_str(), ""); + } + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = {$2.string, new TType($1), {}}; + $$.loc = $2.loc; + $$.param = param; + } + | type_specifier IDENTIFIER array_specifier { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + TType* type = new TType($1); + type->transferArraySizes($3.arraySizes); + type->copyArrayInnerSizes($1.arraySizes); + + parseContext.arrayOfArrayVersionCheck($2.loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck($3.loc, *$3.arraySizes); + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = { $2.string, type, {} }; + + $$.loc = $2.loc; + $$.param = param; + } + ; + +parameter_declaration + // + // With name + // + : type_qualifier parameter_declarator { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier(), $$.param.type->isCoopMatOrVec()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + + } + | parameter_declarator { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier(), $$.param.type->isCoopMatOrVec()); + } + // + // Without name + // + | type_qualifier parameter_type_specifier { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($1.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier(), $$.param.type->isCoopMatOrVec()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + } + | parameter_type_specifier { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier(), $$.param.type->isCoopMatOrVec()); + } + ; + +parameter_type_specifier + : type_specifier { + TParameter param = { 0, new TType($1), {} }; + $$.param = param; + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + ; + +init_declarator_list + : single_declaration { + $$ = $1; + } + | init_declarator_list COMMA IDENTIFIER { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type); + } + | init_declarator_list COMMA IDENTIFIER array_specifier { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes); + } + | init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes, $6); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $5.loc); + } + | init_declarator_list COMMA IDENTIFIER EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, 0, $5); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $4.loc); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareTypeDefaults($$.loc, $$.type); + } + | fully_specified_type IDENTIFIER { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1); + } + | fully_specified_type IDENTIFIER array_specifier { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes); + } + | fully_specified_type IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc); + } + +// Grammar Note: No 'enum', or 'typedef'. + +fully_specified_type + : type_specifier { + $$ = $1; + + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $$); + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck($$.loc, $$.basicType, $$.qualifier, $$.isCoopmatOrvec()); + } + | type_qualifier type_specifier { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier, false, &$2); + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $2); + + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier)) + $2.arraySizes = nullptr; + + parseContext.checkNoShaderLayouts($2.loc, $1.shaderQualifiers); + $2.shaderQualifiers.merge($1.shaderQualifiers); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier, $2.isCoopmatOrvec()); + + $$ = $2; + + if (! $$.qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && $$.qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && $$.qualifier.storage == EvqVaryingIn))) + $$.qualifier.smooth = true; + } + ; + +invariant_qualifier + : INVARIANT { + parseContext.globalCheck($1.loc, "invariant"); + parseContext.profileRequires($$.loc, ENoProfile, 120, 0, "invariant"); + $$.init($1.loc); + $$.qualifier.invariant = true; + } + ; + +interpolation_qualifier + : SMOOTH { + parseContext.globalCheck($1.loc, "smooth"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "smooth"); + $$.init($1.loc); + $$.qualifier.smooth = true; + } + | FLAT { + parseContext.globalCheck($1.loc, "flat"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "flat"); + $$.init($1.loc); + $$.qualifier.flat = true; + } + | NOPERSPECTIVE { + parseContext.globalCheck($1.loc, "noperspective"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); + $$.init($1.loc); + $$.qualifier.nopersp = true; + } + | EXPLICITINTERPAMD { + parseContext.globalCheck($1.loc, "__explicitInterpAMD"); + parseContext.profileRequires($1.loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + $$.init($1.loc); + $$.qualifier.explicitInterp = true; + } + | PERVERTEXNV { + parseContext.globalCheck($1.loc, "pervertexNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + $$.init($1.loc); + $$.qualifier.pervertexNV = true; + } + | PERVERTEXEXT { + parseContext.globalCheck($1.loc, "pervertexEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + $$.init($1.loc); + $$.qualifier.pervertexEXT = true; + } + | PERPRIMITIVENV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perprimitiveNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions($1.loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + $$.init($1.loc); + $$.qualifier.perPrimitiveNV = true; + } + | PERPRIMITIVEEXT { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perprimitiveEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshMask), "perprimitiveEXT"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_mesh_shader, "perprimitiveEXT"); + $$.init($1.loc); + $$.qualifier.perPrimitiveNV = true; + } + | PERVIEWNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perviewNV"); + parseContext.requireStage($1.loc, EShLangMesh, "perviewNV"); + $$.init($1.loc); + $$.qualifier.perViewNV = true; + } + | PERTASKNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "taskNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask), "taskNV"); + $$.init($1.loc); + $$.qualifier.perTaskNV = true; + } + ; + +layout_qualifier + : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { + $$ = $3; + } + ; + +layout_qualifier_id_list + : layout_qualifier_id { + $$ = $1; + } + | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = $1; + $$.shaderQualifiers.merge($3.shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers($$.qualifier, $3.qualifier, false); + } + +layout_qualifier_id + : IDENTIFIER { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string); + } + | IDENTIFIER EQUAL constant_expression { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string, $3); + } + | SHARED { // because "shared" is both an identifier and a keyword + $$.init($1.loc); + TString strShared("shared"); + parseContext.setLayoutQualifier($1.loc, $$, strShared); + } + ; + +precise_qualifier + : PRECISE { + parseContext.profileRequires($$.loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires($1.loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + $$.init($1.loc); + $$.qualifier.noContraction = true; + } + ; + +type_qualifier + : single_type_qualifier { + $$ = $1; + } + | type_qualifier single_type_qualifier { + $$ = $1; + if ($$.basicType == EbtVoid) + $$.basicType = $2.basicType; + + $$.shaderQualifiers.merge($2.shaderQualifiers); + parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false); + } + ; + +single_type_qualifier + : storage_qualifier { + $$ = $1; + } + | layout_qualifier { + $$ = $1; + } + | precision_qualifier { + parseContext.checkPrecisionQualifier($1.loc, $1.qualifier.precision); + $$ = $1; + } + | interpolation_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | invariant_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | precise_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | non_uniform_qualifier { + $$ = $1; + } + | spirv_storage_class_qualifier { + parseContext.globalCheck($1.loc, "spirv_storage_class"); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V storage class qualifier"); + $$ = $1; + } + | spirv_decorate_qualifier { + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V decorate qualifier"); + $$ = $1; + } + | SPIRV_BY_REFERENCE { + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_spirv_intrinsics, "spirv_by_reference"); + $$.init($1.loc); + $$.qualifier.setSpirvByReference(); + } + | SPIRV_LITERAL { + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_spirv_intrinsics, "spirv_by_literal"); + $$.init($1.loc); + $$.qualifier.setSpirvLiteral(); + } + ; + +storage_qualifier + : CONST { + $$.init($1.loc); + $$.qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } + | INOUT { + parseContext.globalCheck($1.loc, "inout"); + $$.init($1.loc); + $$.qualifier.storage = EvqInOut; + } + | IN { + parseContext.globalCheck($1.loc, "in"); + $$.init($1.loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + $$.qualifier.storage = EvqIn; + } + | OUT { + parseContext.globalCheck($1.loc, "out"); + $$.init($1.loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + $$.qualifier.storage = EvqOut; + } + | CENTROID { + parseContext.profileRequires($1.loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck($1.loc, "centroid"); + $$.init($1.loc); + $$.qualifier.centroid = true; + } + | UNIFORM { + parseContext.globalCheck($1.loc, "uniform"); + $$.init($1.loc); + $$.qualifier.storage = EvqUniform; + } + | TILEIMAGEEXT { + parseContext.globalCheck($1.loc, "tileImageEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqTileImageEXT; + } + | SHARED { + parseContext.globalCheck($1.loc, "shared"); + parseContext.profileRequires($1.loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires($1.loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshMask | EShLangTaskMask), "shared"); + $$.init($1.loc); + $$.qualifier.storage = EvqShared; + } + | BUFFER { + parseContext.globalCheck($1.loc, "buffer"); + $$.init($1.loc); + $$.qualifier.storage = EvqBuffer; + } + | ATTRIBUTE { + parseContext.requireStage($1.loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck($1.loc, "attribute"); + + $$.init($1.loc); + $$.qualifier.storage = EvqVaryingIn; + } + | VARYING { + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck($1.loc, "varying"); + + $$.init($1.loc); + if (parseContext.language == EShLangVertex) + $$.qualifier.storage = EvqVaryingOut; + else + $$.qualifier.storage = EvqVaryingIn; + } + | PATCH { + parseContext.globalCheck($1.loc, "patch"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + $$.init($1.loc); + $$.qualifier.patch = true; + } + | SAMPLE { + parseContext.globalCheck($1.loc, "sample"); + $$.init($1.loc); + $$.qualifier.sample = true; + } + | HITATTRNV { + parseContext.globalCheck($1.loc, "hitAttributeNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | HITOBJECTATTRNV { + parseContext.globalCheck($1.loc, "hitAttributeNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask + | EShLangMissMask), "hitObjectAttributeNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_shader_invocation_reorder, "hitObjectAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitObjectAttrNV; + } + | HITATTREXT { + parseContext.globalCheck($1.loc, "hitAttributeEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | PAYLOADNV { + parseContext.globalCheck($1.loc, "rayPayloadNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADEXT { + parseContext.globalCheck($1.loc, "rayPayloadEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADINNV { + parseContext.globalCheck($1.loc, "rayPayloadInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | PAYLOADINEXT { + parseContext.globalCheck($1.loc, "rayPayloadInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | CALLDATANV { + parseContext.globalCheck($1.loc, "callableDataNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAEXT { + parseContext.globalCheck($1.loc, "callableDataEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAINNV { + parseContext.globalCheck($1.loc, "callableDataInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | CALLDATAINEXT { + parseContext.globalCheck($1.loc, "callableDataInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | COHERENT { + $$.init($1.loc); + $$.qualifier.coherent = true; + } + | DEVICECOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + $$.qualifier.devicecoherent = true; + } + | QUEUEFAMILYCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + $$.qualifier.queuefamilycoherent = true; + } + | WORKGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + $$.qualifier.workgroupcoherent = true; + } + | SUBGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + $$.qualifier.subgroupcoherent = true; + } + | NONPRIVATE { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + $$.qualifier.nonprivate = true; + } + | SHADERCALLCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + $$.qualifier.shadercallcoherent = true; + } + | VOLATILE { + $$.init($1.loc); + $$.qualifier.volatil = true; + } + | RESTRICT { + $$.init($1.loc); + $$.qualifier.restrict = true; + } + | READONLY { + $$.init($1.loc); + $$.qualifier.readonly = true; + } + | WRITEONLY { + $$.init($1.loc); + $$.qualifier.writeonly = true; + } + | NONTEMPORAL { + $$.init($1.loc); + $$.qualifier.nontemporal = true; + } + | SUBROUTINE { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + | TASKPAYLOADWORKGROUPEXT { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "taskPayloadSharedEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask), "taskPayloadSharedEXT "); + $$.init($1.loc); + $$.qualifier.storage = EvqtaskPayloadSharedEXT; + } + ; + +non_uniform_qualifier + : NONUNIFORM { + $$.init($1.loc); + $$.qualifier.nonUniform = true; + } + ; + +type_name_list + : IDENTIFIER { + // TODO + } + | type_name_list COMMA IDENTIFIER { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } + ; + +type_specifier + : type_specifier_nonarray type_parameter_specifier_opt { + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + parseContext.typeParametersCheck($1.loc, $$); + + } + | type_specifier_nonarray type_parameter_specifier_opt array_specifier { + parseContext.arrayOfArrayVersionCheck($3.loc, $3.arraySizes); + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + $$.arraySizes = $3.arraySizes; + parseContext.typeParametersCheck($1.loc, $$); + } + ; + +array_specifier + : LEFT_BRACKET RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + $$.arraySizes->addInnerSize(); + } + | LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($2->getLoc(), $2, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + | array_specifier LEFT_BRACKET RIGHT_BRACKET { + $$ = $1; + $$.arraySizes->addInnerSize(); + } + | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + ; + +type_parameter_specifier_opt + : type_parameter_specifier { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +type_parameter_specifier + : LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE { + $$ = $2; + } + ; + +type_parameter_specifier_list + : type_specifier { + $$ = new TTypeParameters; + $$->arraySizes = new TArraySizes; + $$->spirvType = $1.spirvType; + $$->basicType = $1.basicType; + } + | unary_expression { + $$ = new TTypeParameters; + $$->arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($1->getLoc(), $1, size, "type parameter", true); + $$->arraySizes->addInnerSize(size); + } + | type_parameter_specifier_list COMMA unary_expression { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "type parameter", true); + $$->arraySizes->addInnerSize(size); + } + ; + +type_specifier_nonarray + : VOID { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtVoid; + } + | FLOAT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | INT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT { + parseContext.fullIntegerCheck($1.loc, "unsigned integer"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | BOOL { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + } + | VEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | VEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | VEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | BVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(2); + } + | BVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(3); + } + | BVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(4); + } + | IVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | IVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | IVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | UVEC2 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | UVEC3 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | UVEC4 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | MAT2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | MAT2X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT2X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | MAT2X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | MAT3X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | MAT3X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT3X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | MAT4X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | MAT4X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | MAT4X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | DOUBLE { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | BFLOAT16_T { + parseContext.bfloat16ScalarVectorCheck($1.loc, "bfloat16_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBFloat16; + } + | FLOAT16_T { + parseContext.float16ScalarVectorCheck($1.loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + } + | FLOAT32_T { + parseContext.explicitFloat32Check($1.loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | FLOAT64_T { + parseContext.explicitFloat64Check($1.loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | INT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + } + | UINT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + } + | INT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + } + | UINT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + } + | INT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | INT64_T { + parseContext.int64Check($1.loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + } + | UINT64_T { + parseContext.int64Check($1.loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + } + | DVEC2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | DVEC3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | DVEC4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | BF16VEC2 { + parseContext.bfloat16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBFloat16; + $$.setVector(2); + } + | BF16VEC3 { + parseContext.bfloat16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBFloat16; + $$.setVector(3); + } + | BF16VEC4 { + parseContext.bfloat16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBFloat16; + $$.setVector(4); + } + | F16VEC2 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(2); + } + | F16VEC3 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(3); + } + | F16VEC4 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(4); + } + | F32VEC2 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | F32VEC3 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | F32VEC4 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | F64VEC2 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | F64VEC3 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | F64VEC4 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | I8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(2); + } + | I8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(3); + } + | I8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(4); + } + | I16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(2); + } + | I16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(3); + } + | I16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(4); + } + | I32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | I32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | I32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | I64VEC2 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(2); + } + | I64VEC3 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(3); + } + | I64VEC4 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(4); + } + | U8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(2); + } + | U8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(3); + } + | U8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(4); + } + | U16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(2); + } + | U16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(3); + } + | U16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(4); + } + | U32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | U32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | U32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | U64VEC2 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(2); + } + | U64VEC3 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(3); + } + | U64VEC4 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(4); + } + | DMAT2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | DMAT2X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT2X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | DMAT2X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | DMAT3X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | DMAT3X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT3X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | DMAT4X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | DMAT4X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | DMAT4X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F16MAT2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F16MAT2X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT2X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 3); + } + | F16MAT2X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 4); + } + | F16MAT3X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 2); + } + | F16MAT3X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT3X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 4); + } + | F16MAT4X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 2); + } + | F16MAT4X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 3); + } + | F16MAT4X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F32MAT2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F32MAT2X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT2X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | F32MAT2X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | F32MAT3X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | F32MAT3X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT3X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | F32MAT4X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | F32MAT4X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | F32MAT4X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F64MAT2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F64MAT2X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT2X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | F64MAT2X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | F64MAT3X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | F64MAT3X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT3X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | F64MAT4X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | F64MAT4X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | F64MAT4X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | ACCSTRUCTNV { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | ACCSTRUCTEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | RAYQUERYEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtRayQuery; + } + | ATOMIC_UINT { + parseContext.vulkanRemoved($1.loc, "atomic counter types"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAtomicUint; + } + | SAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D); + } + | SAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + } + | SAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd3D); + } + | SAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube); + } + | SAMPLER2DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, true); + } + | SAMPLERCUBESHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, false, true); + } + | SAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true); + } + | SAMPLER2DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, true); + } + | SAMPLER1DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, false, true); + } + | SAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true); + } + | SAMPLER1DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true, true); + } + | SAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true); + } + | SAMPLERCUBEARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true, true); + } + | F16SAMPLER1D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D); + } + | F16SAMPLER2D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D); + } + | F16SAMPLER3D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd3D); + } + | F16SAMPLERCUBE { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube); + } + | F16SAMPLER1DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, false, true); + } + | F16SAMPLER2DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, true); + } + | F16SAMPLERCUBESHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, false, true); + } + | F16SAMPLER1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true); + } + | F16SAMPLER2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true); + } + | F16SAMPLER1DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true, true); + } + | F16SAMPLER2DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, true); + } + | F16SAMPLERCUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true); + } + | F16SAMPLERCUBEARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true, true); + } + | ISAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D); + } + | ISAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D); + } + | ISAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd3D); + } + | ISAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube); + } + | ISAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true); + } + | USAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D); + } + | USAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd3D); + } + | USAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube); + } + | ISAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D, true); + } + | ISAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube, true); + } + | USAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D); + } + | USAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D, true); + } + | USAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube, true); + } + | TEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube, true); + } + | ITEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube, true); + } + | UTEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube, true); + } + | USAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true); + } + | TEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D); + } + | TEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd3D); + } + | TEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true); + } + | TEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube); + } + | ITEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D); + } + | ITEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd3D); + } + | ITEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube); + } + | ITEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true); + } + | UTEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D); + } + | UTEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd3D); + } + | UTEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube); + } + | UTEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true); + } + | SAMPLER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(false); + } + | SAMPLERSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(true); + } + | SAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect); + } + | SAMPLER2DRECTSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect, false, true); + } + | F16SAMPLER2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect); + } + | F16SAMPLER2DRECTSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect, false, true); + } + | ISAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdRect); + } + | USAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdRect); + } + | SAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdBuffer); + } + | F16SAMPLERBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdBuffer); + } + | ISAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdBuffer); + } + | USAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdBuffer); + } + | SAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, false, true); + } + | F16SAMPLER2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, false, true); + } + | ISAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, false, false, true); + } + | USAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, false, false, true); + } + | SAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, false, true); + } + | F16SAMPLER2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, false, true); + } + | ISAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true, false, true); + } + | USAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true, false, true); + } + | TEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D); + } + | F16TEXTURE1D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D); + } + | F16TEXTURE2D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D); + } + | F16TEXTURE3D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd3D); + } + | F16TEXTURECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube); + } + | TEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D, true); + } + | F16TEXTURE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D, true); + } + | F16TEXTURE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true); + } + | F16TEXTURECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube, true); + } + | ITEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D); + } + | ITEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D, true); + } + | UTEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D); + } + | UTEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D, true); + } + | TEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdRect); + } + | F16TEXTURE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdRect); + } + | ITEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdRect); + } + | UTEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdRect); + } + | TEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdBuffer); + } + | F16TEXTUREBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdBuffer); + } + | ITEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdBuffer); + } + | UTEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdBuffer); + } + | TEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } + | F16TEXTURE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } + | ITEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, false, false, true); + } + | UTEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, false, false, true); + } + | TEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } + | F16TEXTURE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } + | ITEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true, false, true); + } + | UTEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true, false, true); + } + | IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D); + } + | F16IMAGE1D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D); + } + | IIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D); + } + | UIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D); + } + | IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D); + } + | F16IMAGE2D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D); + } + | IIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D); + } + | UIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D); + } + | IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd3D); + } + | F16IMAGE3D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd3D); + } + | IIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd3D); + } + | UIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd3D); + } + | IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdRect); + } + | F16IMAGE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdRect); + } + | IIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdRect); + } + | UIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdRect); + } + | IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube); + } + | F16IMAGECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube); + } + | IIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube); + } + | UIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube); + } + | IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdBuffer); + } + | F16IMAGEBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdBuffer); + } + | IIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdBuffer); + } + | UIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdBuffer); + } + | IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D, true); + } + | F16IMAGE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D, true); + } + | IIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D, true); + } + | UIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D, true); + } + | IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true); + } + | F16IMAGE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true); + } + | IIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true); + } + | UIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true); + } + | IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube, true); + } + | F16IMAGECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube, true); + } + | IIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube, true); + } + | UIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube, true); + } + | IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, false, false, true); + } + | F16IMAGE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } + | IIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, false, false, true); + } + | UIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, false, false, true); + } + | IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true, false, true); + } + | F16IMAGE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } + | IIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true, false, true); + } + | UIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true, false, true); + } + | I64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D); + } + | U64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D); + } + | I64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D); + } + | U64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D); + } + | I64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd3D); + } + | U64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd3D); + } + | I64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdRect); + } + | U64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdRect); + } + | I64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube); + } + | U64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube); + } + | I64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdBuffer); + } + | U64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdBuffer); + } + | I64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D, true); + } + | U64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D, true); + } + | I64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true); + } + | U64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true); + } + | I64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube, true); + } + | U64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube, true); + } + | I64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, false, false, true); + } + | U64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, false, false, true); + } + | I64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true, false, true); + } + | U64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true, false, true); + } + | SAMPLEREXTERNALOES { // GL_OES_EGL_image_external + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.external = true; + } + | SAMPLEREXTERNAL2DY2YEXT { // GL_EXT_YUV_target + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.yuv = true; + } + | ATTACHMENTEXT { + parseContext.requireStage($1.loc, EShLangFragment, "attachmentEXT input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setAttachmentEXT(EbtFloat); + } + | IATTACHMENTEXT { + parseContext.requireStage($1.loc, EShLangFragment, "attachmentEXT input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setAttachmentEXT(EbtInt); + } + | UATTACHMENTEXT { + parseContext.requireStage($1.loc, EShLangFragment, "attachmentEXT input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setAttachmentEXT(EbtUint); + } + | SUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat); + } + | SUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat, true); + } + | F16SUBPASSINPUT { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16); + } + | F16SUBPASSINPUTMS { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16, true); + } + | ISUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt); + } + | ISUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt, true); + } + | USUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint); + } + | USUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint, true); + } + | FCOOPMATNV { + parseContext.fcoopmatCheckNV($1.loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.coopmatNV = true; + $$.coopmatKHR = false; + } + | ICOOPMATNV { + parseContext.intcoopmatCheckNV($1.loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.coopmatNV = true; + $$.coopmatKHR = false; + } + | UCOOPMATNV { + parseContext.intcoopmatCheckNV($1.loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.coopmatNV = true; + $$.coopmatKHR = false; + } + | COOPMAT { + parseContext.coopmatCheck($1.loc, "coopmat", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtCoopmat; + $$.coopmatNV = false; + $$.coopmatKHR = true; + } + | TENSORLAYOUTNV { + parseContext.tensorLayoutViewCheck($1.loc, "tensorLayoutNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtTensorLayoutNV; + } + | TENSORVIEWNV { + parseContext.tensorLayoutViewCheck($1.loc, "tensorViewNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtTensorViewNV; + } + | FUNCTION { + $$.init($1.loc); + $$.basicType = EbtFunction; + } + | COOPVECNV { + parseContext.coopvecCheck($1.loc, "coopvecNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtCoopvecNV; + $$.coopvecNV = true; + } + | spirv_type_specifier { + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V type specifier"); + $$ = $1; + } + | HITOBJECTNV { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtHitObjectNV; + } + | struct_specifier { + $$ = $1; + $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck($$.loc, $$); + } + | TYPE_NAME { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ($1.symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtStruct; + $$.userDef = &structure; + } else + parseContext.error($1.loc, "expected type name", $1.string->c_str(), ""); + } + ; + +precision_qualifier + : HIGH_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "highp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqHigh); + } + | MEDIUM_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "mediump precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqMedium); + } + | LOW_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "lowp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqLow); + } + ; + +struct_specifier + : STRUCT IDENTIFIER LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + + TType* structure = new TType($5, *$2.string); + parseContext.structArrayCheck($2.loc, *structure); + + TVariable* userTypeDef = new TVariable($2.string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error($2.loc, "redefinition", $2.string->c_str(), "struct"); + else if (parseContext.spvVersion.vulkanRelaxed + && structure->containsOpaque()) + parseContext.relaxedSymbols.push_back(structure->getTypeName()); + + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + | STRUCT LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($4, TString("")); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + ; + +struct_declaration_list + : struct_declaration { + $$ = $1; + } + | struct_declaration_list struct_declaration { + $$ = $1; + for (unsigned int i = 0; i < $2->size(); ++i) { + for (unsigned int j = 0; j < $$->size(); ++j) { + if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName()) + parseContext.error((*$2)[i].loc, "duplicate member name:", "", (*$2)[i].type->getFieldName().c_str()); + } + $$->push_back((*$2)[i]); + } + } + ; + +struct_declaration + : type_specifier struct_declarator_list SEMICOLON { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + + $$ = $2; + + parseContext.voidErrorCheck($1.loc, (*$2)[0].type->getFieldName(), $1.basicType); + parseContext.precisionQualifierCheck($1.loc, $1.basicType, $1.qualifier, $1.isCoopmatOrvec()); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($1); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($1.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + | type_qualifier type_specifier struct_declarator_list SEMICOLON { + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($2.loc, *$2.arraySizes); + } + + $$ = $3; + + parseContext.memberQualifierCheck($1); + parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier, $2.isCoopmatOrvec()); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($2); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($2.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + ; + +struct_declarator_list + : struct_declarator { + $$ = new TTypeList; + $$->push_back($1); + } + | struct_declarator_list COMMA struct_declarator { + $$->push_back($3); + } + ; + +struct_declarator + : IDENTIFIER { + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + } + | IDENTIFIER array_specifier { + parseContext.arrayOfArrayVersionCheck($1.loc, $2.arraySizes); + + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + $$.type->transferArraySizes($2.arraySizes); + } + ; + +initializer + : assignment_expression { + $$ = $1; + } + | LEFT_BRACE initializer_list RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + | LEFT_BRACE initializer_list COMMA RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + | LEFT_BRACE RIGHT_BRACE { + const char* initFeature = "empty { } initializer"; + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_EXT_null_initializer, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 0, E_GL_EXT_null_initializer, initFeature); + $$ = parseContext.intermediate.makeAggregate($1.loc); + } + ; + +initializer_list + : initializer { + $$ = parseContext.intermediate.growAggregate(0, $1, $1->getLoc()); + } + | initializer_list COMMA initializer { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + ; + +declaration_statement + : declaration { $$ = $1; } + ; + +statement + : compound_statement { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +// Grammar Note: labeled statements for switch statements only; 'goto' is not supported. + +simple_statement + : declaration_statement { $$ = $1; } + | expression_statement { $$ = $1; } + | selection_statement { $$ = $1; } + | switch_statement { $$ = $1; } + | case_label { $$ = $1; } + | iteration_statement { $$ = $1; } + | jump_statement { $$ = $1; } + | demote_statement { $$ = $1; } + ; + +demote_statement + : DEMOTE SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "demote"); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + $$ = parseContext.intermediate.addBranch(EOpDemote, $1.loc); + } + ; + +compound_statement + : LEFT_BRACE RIGHT_BRACE { $$ = 0; } + | LEFT_BRACE { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + statement_list { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + RIGHT_BRACE { + if ($3 && $3->getAsAggregate()) { + $3->getAsAggregate()->setOperator(parseContext.intermediate.getDebugInfo() ? EOpScope : EOpSequence); + $3->getAsAggregate()->setEndLoc($5.loc); + } + $$ = $3; + } + ; + +statement_no_new_scope + : compound_statement_no_new_scope { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +statement_scoped + : { + ++parseContext.controlFlowNestingLevel; + } + compound_statement { + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + | { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + simple_statement { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + +compound_statement_no_new_scope + // Statement that doesn't create a new scope, for selection_statement, iteration_statement + : LEFT_BRACE RIGHT_BRACE { + $$ = 0; + } + | LEFT_BRACE statement_list RIGHT_BRACE { + if ($2 && $2->getAsAggregate()) { + $2->getAsAggregate()->setOperator(EOpSequence); + $2->getAsAggregate()->setEndLoc($3.loc); + } + $$ = $2; + } + ; + +statement_list + : statement { + $$ = parseContext.intermediate.makeAggregate($1); + if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase || + $1->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, $1); + $$ = 0; // start a fresh subsequence for what's after this case + } + } + | statement_list statement { + if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || + $2->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence($1 ? $1->getAsAggregate() : 0, $2); + $$ = 0; // start a fresh subsequence for what's after this case + } else + $$ = parseContext.intermediate.growAggregate($1, $2); + } + ; + +expression_statement + : SEMICOLON { $$ = 0; } + | expression SEMICOLON { $$ = static_cast($1); } + ; + +selection_statement + : selection_statement_nonattributed { + $$ = $1; + } + | attribute selection_statement_nonattributed { + parseContext.requireExtensions($2->getLoc(), 1, &E_GL_EXT_control_flow_attributes, "attribute"); + parseContext.handleSelectionAttributes(*$1, $2); + $$ = $2; + } + +selection_statement_nonattributed + : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { + parseContext.boolCheck($1.loc, $3); + $$ = parseContext.intermediate.addSelection($3, $5, $1.loc); + } + ; + +selection_rest_statement + : statement_scoped ELSE statement_scoped { + $$.node1 = $1; + $$.node2 = $3; + } + | statement_scoped { + $$.node1 = $1; + $$.node2 = 0; + } + ; + +condition + // In 1996 c++ draft, conditions can include single declarations + : expression { + $$ = $1; + parseContext.boolCheck($1->getLoc(), $1); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + parseContext.boolCheck($2.loc, $1); + + TType type($1); + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + if (initNode) + $$ = initNode->getAsTyped(); + else + $$ = 0; + } + ; + +switch_statement + : switch_statement_nonattributed { + $$ = $1; + } + | attribute switch_statement_nonattributed { + parseContext.requireExtensions($2->getLoc(), 1, &E_GL_EXT_control_flow_attributes, "attribute"); + parseContext.handleSwitchAttributes(*$1, $2); + $$ = $2; + } + +switch_statement_nonattributed + : SWITCH LEFT_PAREN expression RIGHT_PAREN { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } + LEFT_BRACE switch_statement_list RIGHT_BRACE { + $$ = parseContext.addSwitch($1.loc, $3, $7 ? $7->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +switch_statement_list + : /* nothing */ { + $$ = 0; + } + | statement_list { + $$ = $1; + } + ; + +case_label + : CASE expression COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck($2, "case"); + parseContext.integerCheck($2, "case"); + $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.loc); + } + } + | DEFAULT COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "default", ""); + else + $$ = parseContext.intermediate.addBranch(EOpDefault, $1.loc); + } + ; + +iteration_statement + : iteration_statement_nonattributed { + $$ = $1; + } + | attribute iteration_statement_nonattributed { + const char * extensions[2] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2 }; + parseContext.requireExtensions($2->getLoc(), 2, extensions, "attribute"); + parseContext.handleLoopAttributes(*$1, $2); + $$ = $2; + } + +iteration_statement_nonattributed + : WHILE LEFT_PAREN { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + condition RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.addLoop($6, $4, 0, true, $1.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | DO { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck($8.loc, $6); + + $$ = parseContext.intermediate.addLoop($3, $6, 0, false, $4.loc); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | FOR LEFT_PAREN { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.makeAggregate($4, $2.loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast($5.node1), reinterpret_cast($5.node2), true, $1.loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck($1.loc, $4, forLoop); + $$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc); + $$->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +for_init_statement + : expression_statement { + $$ = $1; + } + | declaration_statement { + $$ = $1; + } + ; + +conditionopt + : condition { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +for_rest_statement + : conditionopt SEMICOLON { + $$.node1 = $1; + $$.node2 = 0; + } + | conditionopt SEMICOLON expression { + $$.node1 = $1; + $$.node2 = $3; + } + ; + +jump_statement + : CONTINUE SEMICOLON { + if (parseContext.loopNestingLevel <= 0) + parseContext.error($1.loc, "continue statement only allowed in loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpContinue, $1.loc); + } + | BREAK SEMICOLON { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error($1.loc, "break statement only allowed in switch and loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpBreak, $1.loc); + } + | RETURN SEMICOLON { + $$ = parseContext.intermediate.addBranch(EOpReturn, $1.loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error($1.loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } + | RETURN expression SEMICOLON { + $$ = parseContext.handleReturnValue($1.loc, $2); + } + | DISCARD SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "discard"); + $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); + } + | TERMINATE_INVOCATION SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "terminateInvocation"); + $$ = parseContext.intermediate.addBranch(EOpTerminateInvocation, $1.loc); + } + | TERMINATE_RAY SEMICOLON { + parseContext.requireStage($1.loc, EShLangAnyHit, "terminateRayEXT"); + $$ = parseContext.intermediate.addBranch(EOpTerminateRayKHR, $1.loc); + } + | IGNORE_INTERSECTION SEMICOLON { + parseContext.requireStage($1.loc, EShLangAnyHit, "ignoreIntersectionEXT"); + $$ = parseContext.intermediate.addBranch(EOpIgnoreIntersectionKHR, $1.loc); + } + ; + +// Grammar Note: No 'goto'. Gotos are not supported. + +translation_unit + : external_declaration { + $$ = $1; + parseContext.intermediate.setTreeRoot($$); + } + | translation_unit external_declaration { + if ($2 != nullptr) { + $$ = parseContext.intermediate.growAggregate($1, $2); + parseContext.intermediate.setTreeRoot($$); + } + } + ; + +external_declaration + : function_definition { + $$ = $1; + } + | declaration { + $$ = $1; + } + | SEMICOLON { + parseContext.requireProfile($1.loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires($1.loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + $$ = nullptr; + } + ; + +function_definition + : function_prototype { + $1.function = parseContext.handleFunctionDeclarator($1.loc, *$1.function, false /* not prototype */); + $1.intermNode = parseContext.handleFunctionDefinition($1.loc, *$1.function); + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + } + compound_statement_no_new_scope { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.growAggregate($1.intermNode, $3); + $$->getAsAggregate()->setLinkType($1.function->getLinkType()); + parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc); + $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + $$->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + $$->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + $$->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + + // Set currentFunctionType to empty pointer when goes outside of the function + parseContext.currentFunctionType = nullptr; + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + } + ; + +attribute + : LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET { + $$ = $3; + } + +attribute_list + : single_attribute { + $$ = $1; + } + | attribute_list COMMA single_attribute { + $$ = parseContext.mergeAttributes($1, $3); + } + +single_attribute + : IDENTIFIER { + $$ = parseContext.makeAttributes(*$1.string); + } + | IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN { + $$ = parseContext.makeAttributes(*$1.string, $3); + } + +spirv_requirements_list + : spirv_requirements_parameter { + $$ = $1; + } + | spirv_requirements_list COMMA spirv_requirements_parameter { + $$ = parseContext.mergeSpirvRequirements($2.loc, $1, $3); + } + +spirv_requirements_parameter + : IDENTIFIER EQUAL LEFT_BRACKET spirv_extension_list RIGHT_BRACKET { + $$ = parseContext.makeSpirvRequirement($2.loc, *$1.string, $4->getAsAggregate(), nullptr); + } + | IDENTIFIER EQUAL LEFT_BRACKET spirv_capability_list RIGHT_BRACKET { + $$ = parseContext.makeSpirvRequirement($2.loc, *$1.string, nullptr, $4->getAsAggregate()); + } + +spirv_extension_list + : STRING_LITERAL { + $$ = parseContext.intermediate.makeAggregate(parseContext.intermediate.addConstantUnion($1.string, $1.loc, true)); + } + | spirv_extension_list COMMA STRING_LITERAL { + $$ = parseContext.intermediate.growAggregate($1, parseContext.intermediate.addConstantUnion($3.string, $3.loc, true)); + } + +spirv_capability_list + : INTCONSTANT { + $$ = parseContext.intermediate.makeAggregate(parseContext.intermediate.addConstantUnion($1.i, $1.loc, true)); + } + | spirv_capability_list COMMA INTCONSTANT { + $$ = parseContext.intermediate.growAggregate($1, parseContext.intermediate.addConstantUnion($3.i, $3.loc, true)); + } + +spirv_execution_mode_qualifier + : SPIRV_EXECUTION_MODE LEFT_PAREN INTCONSTANT RIGHT_PAREN { + parseContext.intermediate.insertSpirvExecutionMode($3.i); + $$ = 0; + } + | SPIRV_EXECUTION_MODE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN { + parseContext.intermediate.insertSpirvRequirement($3); + parseContext.intermediate.insertSpirvExecutionMode($5.i); + $$ = 0; + } + | SPIRV_EXECUTION_MODE LEFT_PAREN INTCONSTANT COMMA spirv_execution_mode_parameter_list RIGHT_PAREN { + parseContext.intermediate.insertSpirvExecutionMode($3.i, $5->getAsAggregate()); + $$ = 0; + } + | SPIRV_EXECUTION_MODE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_execution_mode_parameter_list RIGHT_PAREN { + parseContext.intermediate.insertSpirvRequirement($3); + parseContext.intermediate.insertSpirvExecutionMode($5.i, $7->getAsAggregate()); + $$ = 0; + } + | SPIRV_EXECUTION_MODE_ID LEFT_PAREN INTCONSTANT COMMA spirv_execution_mode_id_parameter_list RIGHT_PAREN { + parseContext.intermediate.insertSpirvExecutionModeId($3.i, $5->getAsAggregate()); + $$ = 0; + } + | SPIRV_EXECUTION_MODE_ID LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_execution_mode_id_parameter_list RIGHT_PAREN { + parseContext.intermediate.insertSpirvRequirement($3); + parseContext.intermediate.insertSpirvExecutionModeId($5.i, $7->getAsAggregate()); + $$ = 0; + } + +spirv_execution_mode_parameter_list + : spirv_execution_mode_parameter { + $$ = parseContext.intermediate.makeAggregate($1); + } + | spirv_execution_mode_parameter_list COMMA spirv_execution_mode_parameter { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + +spirv_execution_mode_parameter + : FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + | STRING_LITERAL { + $$ = parseContext.intermediate.addConstantUnion($1.string, $1.loc, true); + } + +spirv_execution_mode_id_parameter_list + : constant_expression { + if ($1->getBasicType() != EbtFloat && + $1->getBasicType() != EbtInt && + $1->getBasicType() != EbtUint && + $1->getBasicType() != EbtBool && + $1->getBasicType() != EbtString) + parseContext.error($1->getLoc(), "this type not allowed", $1->getType().getBasicString(), ""); + $$ = parseContext.intermediate.makeAggregate($1); + } + | spirv_execution_mode_id_parameter_list COMMA constant_expression { + if ($3->getBasicType() != EbtFloat && + $3->getBasicType() != EbtInt && + $3->getBasicType() != EbtUint && + $3->getBasicType() != EbtBool && + $3->getBasicType() != EbtString) + parseContext.error($3->getLoc(), "this type not allowed", $3->getType().getBasicString(), ""); + $$ = parseContext.intermediate.growAggregate($1, $3); + } + +spirv_storage_class_qualifier + : SPIRV_STORAGE_CLASS LEFT_PAREN INTCONSTANT RIGHT_PAREN { + $$.init($1.loc); + $$.qualifier.storage = EvqSpirvStorageClass; + $$.qualifier.spirvStorageClass = $3.i; + } + | SPIRV_STORAGE_CLASS LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN { + $$.init($1.loc); + parseContext.intermediate.insertSpirvRequirement($3); + $$.qualifier.storage = EvqSpirvStorageClass; + $$.qualifier.spirvStorageClass = $5.i; + } + +spirv_decorate_qualifier + : SPIRV_DECORATE LEFT_PAREN INTCONSTANT RIGHT_PAREN{ + $$.init($1.loc); + $$.qualifier.setSpirvDecorate($3.i); + } + | SPIRV_DECORATE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN{ + $$.init($1.loc); + parseContext.intermediate.insertSpirvRequirement($3); + $$.qualifier.setSpirvDecorate($5.i); + } + | SPIRV_DECORATE LEFT_PAREN INTCONSTANT COMMA spirv_decorate_parameter_list RIGHT_PAREN { + $$.init($1.loc); + $$.qualifier.setSpirvDecorate($3.i, $5->getAsAggregate()); + } + | SPIRV_DECORATE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_parameter_list RIGHT_PAREN { + $$.init($1.loc); + parseContext.intermediate.insertSpirvRequirement($3); + $$.qualifier.setSpirvDecorate($5.i, $7->getAsAggregate()); + } + | SPIRV_DECORATE_ID LEFT_PAREN INTCONSTANT COMMA spirv_decorate_id_parameter_list RIGHT_PAREN { + $$.init($1.loc); + $$.qualifier.setSpirvDecorateId($3.i, $5->getAsAggregate()); + } + | SPIRV_DECORATE_ID LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_id_parameter_list RIGHT_PAREN { + $$.init($1.loc); + parseContext.intermediate.insertSpirvRequirement($3); + $$.qualifier.setSpirvDecorateId($5.i, $7->getAsAggregate()); + } + | SPIRV_DECORATE_STRING LEFT_PAREN INTCONSTANT COMMA spirv_decorate_string_parameter_list RIGHT_PAREN { + $$.init($1.loc); + $$.qualifier.setSpirvDecorateString($3.i, $5->getAsAggregate()); + } + | SPIRV_DECORATE_STRING LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_string_parameter_list RIGHT_PAREN { + $$.init($1.loc); + parseContext.intermediate.insertSpirvRequirement($3); + $$.qualifier.setSpirvDecorateString($5.i, $7->getAsAggregate()); + } + +spirv_decorate_parameter_list + : spirv_decorate_parameter { + $$ = parseContext.intermediate.makeAggregate($1); + } + | spirv_decorate_parameter_list COMMA spirv_decorate_parameter { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + +spirv_decorate_parameter + : FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + +spirv_decorate_id_parameter_list + : spirv_decorate_id_parameter { + $$ = parseContext.intermediate.makeAggregate($1); + } + | spirv_decorate_id_parameter_list COMMA spirv_decorate_id_parameter { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + +spirv_decorate_id_parameter + : variable_identifier { + if ($1->getAsConstantUnion() || $1->getAsSymbolNode()) + $$ = $1; + else + parseContext.error($1->getLoc(), "only allow constants or variables which are not elements of a composite", "", ""); + } + | FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + +spirv_decorate_string_parameter_list + : STRING_LITERAL { + $$ = parseContext.intermediate.makeAggregate( + parseContext.intermediate.addConstantUnion($1.string, $1.loc, true)); + } + | spirv_decorate_string_parameter_list COMMA STRING_LITERAL { + $$ = parseContext.intermediate.growAggregate($1, parseContext.intermediate.addConstantUnion($3.string, $3.loc, true)); + } + +spirv_type_specifier + : SPIRV_TYPE LEFT_PAREN spirv_instruction_qualifier_list COMMA spirv_type_parameter_list RIGHT_PAREN { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.setSpirvType(*$3, $5); + } + | SPIRV_TYPE LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list COMMA spirv_type_parameter_list RIGHT_PAREN { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.intermediate.insertSpirvRequirement($3); + $$.setSpirvType(*$5, $7); + } + | SPIRV_TYPE LEFT_PAREN spirv_instruction_qualifier_list RIGHT_PAREN { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.setSpirvType(*$3); + } + | SPIRV_TYPE LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list RIGHT_PAREN { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.intermediate.insertSpirvRequirement($3); + $$.setSpirvType(*$5); + } + +spirv_type_parameter_list + : spirv_type_parameter { + $$ = $1; + } + | spirv_type_parameter_list COMMA spirv_type_parameter { + $$ = parseContext.mergeSpirvTypeParameters($1, $3); + } + +spirv_type_parameter + : constant_expression { + $$ = parseContext.makeSpirvTypeParameters($1->getLoc(), $1->getAsConstantUnion()); + } + | type_specifier_nonarray { + $$ = parseContext.makeSpirvTypeParameters($1.loc, $1); + } + +spirv_instruction_qualifier + : SPIRV_INSTRUCTION LEFT_PAREN spirv_instruction_qualifier_list RIGHT_PAREN { + $$ = $3; + } + | SPIRV_INSTRUCTION LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list RIGHT_PAREN { + parseContext.intermediate.insertSpirvRequirement($3); + $$ = $5; + } + +spirv_instruction_qualifier_list + : spirv_instruction_qualifier_id { + $$ = $1; + } + | spirv_instruction_qualifier_list COMMA spirv_instruction_qualifier_id { + $$ = parseContext.mergeSpirvInstruction($2.loc, $1, $3); + } + +spirv_instruction_qualifier_id + : IDENTIFIER EQUAL STRING_LITERAL { + $$ = parseContext.makeSpirvInstruction($2.loc, *$1.string, *$3.string); + } + | IDENTIFIER EQUAL INTCONSTANT { + $$ = parseContext.makeSpirvInstruction($2.loc, *$1.string, $3.i); + } + +%% diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp new file mode 100644 index 00000000..9cc9ac98 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp @@ -0,0 +1,12849 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 44 "MachineIndependent/glslang.y" + + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + + +#line 97 "MachineIndependent/glslang_tab.cpp" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "glslang_tab.cpp.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_CONST = 3, /* CONST */ + YYSYMBOL_BOOL = 4, /* BOOL */ + YYSYMBOL_INT = 5, /* INT */ + YYSYMBOL_UINT = 6, /* UINT */ + YYSYMBOL_FLOAT = 7, /* FLOAT */ + YYSYMBOL_BVEC2 = 8, /* BVEC2 */ + YYSYMBOL_BVEC3 = 9, /* BVEC3 */ + YYSYMBOL_BVEC4 = 10, /* BVEC4 */ + YYSYMBOL_IVEC2 = 11, /* IVEC2 */ + YYSYMBOL_IVEC3 = 12, /* IVEC3 */ + YYSYMBOL_IVEC4 = 13, /* IVEC4 */ + YYSYMBOL_UVEC2 = 14, /* UVEC2 */ + YYSYMBOL_UVEC3 = 15, /* UVEC3 */ + YYSYMBOL_UVEC4 = 16, /* UVEC4 */ + YYSYMBOL_VEC2 = 17, /* VEC2 */ + YYSYMBOL_VEC3 = 18, /* VEC3 */ + YYSYMBOL_VEC4 = 19, /* VEC4 */ + YYSYMBOL_MAT2 = 20, /* MAT2 */ + YYSYMBOL_MAT3 = 21, /* MAT3 */ + YYSYMBOL_MAT4 = 22, /* MAT4 */ + YYSYMBOL_MAT2X2 = 23, /* MAT2X2 */ + YYSYMBOL_MAT2X3 = 24, /* MAT2X3 */ + YYSYMBOL_MAT2X4 = 25, /* MAT2X4 */ + YYSYMBOL_MAT3X2 = 26, /* MAT3X2 */ + YYSYMBOL_MAT3X3 = 27, /* MAT3X3 */ + YYSYMBOL_MAT3X4 = 28, /* MAT3X4 */ + YYSYMBOL_MAT4X2 = 29, /* MAT4X2 */ + YYSYMBOL_MAT4X3 = 30, /* MAT4X3 */ + YYSYMBOL_MAT4X4 = 31, /* MAT4X4 */ + YYSYMBOL_SAMPLER2D = 32, /* SAMPLER2D */ + YYSYMBOL_SAMPLER3D = 33, /* SAMPLER3D */ + YYSYMBOL_SAMPLERCUBE = 34, /* SAMPLERCUBE */ + YYSYMBOL_SAMPLER2DSHADOW = 35, /* SAMPLER2DSHADOW */ + YYSYMBOL_SAMPLERCUBESHADOW = 36, /* SAMPLERCUBESHADOW */ + YYSYMBOL_SAMPLER2DARRAY = 37, /* SAMPLER2DARRAY */ + YYSYMBOL_SAMPLER2DARRAYSHADOW = 38, /* SAMPLER2DARRAYSHADOW */ + YYSYMBOL_ISAMPLER2D = 39, /* ISAMPLER2D */ + YYSYMBOL_ISAMPLER3D = 40, /* ISAMPLER3D */ + YYSYMBOL_ISAMPLERCUBE = 41, /* ISAMPLERCUBE */ + YYSYMBOL_ISAMPLER2DARRAY = 42, /* ISAMPLER2DARRAY */ + YYSYMBOL_USAMPLER2D = 43, /* USAMPLER2D */ + YYSYMBOL_USAMPLER3D = 44, /* USAMPLER3D */ + YYSYMBOL_USAMPLERCUBE = 45, /* USAMPLERCUBE */ + YYSYMBOL_USAMPLER2DARRAY = 46, /* USAMPLER2DARRAY */ + YYSYMBOL_SAMPLER = 47, /* SAMPLER */ + YYSYMBOL_SAMPLERSHADOW = 48, /* SAMPLERSHADOW */ + YYSYMBOL_TEXTURE2D = 49, /* TEXTURE2D */ + YYSYMBOL_TEXTURE3D = 50, /* TEXTURE3D */ + YYSYMBOL_TEXTURECUBE = 51, /* TEXTURECUBE */ + YYSYMBOL_TEXTURE2DARRAY = 52, /* TEXTURE2DARRAY */ + YYSYMBOL_ITEXTURE2D = 53, /* ITEXTURE2D */ + YYSYMBOL_ITEXTURE3D = 54, /* ITEXTURE3D */ + YYSYMBOL_ITEXTURECUBE = 55, /* ITEXTURECUBE */ + YYSYMBOL_ITEXTURE2DARRAY = 56, /* ITEXTURE2DARRAY */ + YYSYMBOL_UTEXTURE2D = 57, /* UTEXTURE2D */ + YYSYMBOL_UTEXTURE3D = 58, /* UTEXTURE3D */ + YYSYMBOL_UTEXTURECUBE = 59, /* UTEXTURECUBE */ + YYSYMBOL_UTEXTURE2DARRAY = 60, /* UTEXTURE2DARRAY */ + YYSYMBOL_ATTRIBUTE = 61, /* ATTRIBUTE */ + YYSYMBOL_VARYING = 62, /* VARYING */ + YYSYMBOL_BFLOAT16_T = 63, /* BFLOAT16_T */ + YYSYMBOL_FLOAT16_T = 64, /* FLOAT16_T */ + YYSYMBOL_FLOAT32_T = 65, /* FLOAT32_T */ + YYSYMBOL_DOUBLE = 66, /* DOUBLE */ + YYSYMBOL_FLOAT64_T = 67, /* FLOAT64_T */ + YYSYMBOL_INT64_T = 68, /* INT64_T */ + YYSYMBOL_UINT64_T = 69, /* UINT64_T */ + YYSYMBOL_INT32_T = 70, /* INT32_T */ + YYSYMBOL_UINT32_T = 71, /* UINT32_T */ + YYSYMBOL_INT16_T = 72, /* INT16_T */ + YYSYMBOL_UINT16_T = 73, /* UINT16_T */ + YYSYMBOL_INT8_T = 74, /* INT8_T */ + YYSYMBOL_UINT8_T = 75, /* UINT8_T */ + YYSYMBOL_I64VEC2 = 76, /* I64VEC2 */ + YYSYMBOL_I64VEC3 = 77, /* I64VEC3 */ + YYSYMBOL_I64VEC4 = 78, /* I64VEC4 */ + YYSYMBOL_U64VEC2 = 79, /* U64VEC2 */ + YYSYMBOL_U64VEC3 = 80, /* U64VEC3 */ + YYSYMBOL_U64VEC4 = 81, /* U64VEC4 */ + YYSYMBOL_I32VEC2 = 82, /* I32VEC2 */ + YYSYMBOL_I32VEC3 = 83, /* I32VEC3 */ + YYSYMBOL_I32VEC4 = 84, /* I32VEC4 */ + YYSYMBOL_U32VEC2 = 85, /* U32VEC2 */ + YYSYMBOL_U32VEC3 = 86, /* U32VEC3 */ + YYSYMBOL_U32VEC4 = 87, /* U32VEC4 */ + YYSYMBOL_I16VEC2 = 88, /* I16VEC2 */ + YYSYMBOL_I16VEC3 = 89, /* I16VEC3 */ + YYSYMBOL_I16VEC4 = 90, /* I16VEC4 */ + YYSYMBOL_U16VEC2 = 91, /* U16VEC2 */ + YYSYMBOL_U16VEC3 = 92, /* U16VEC3 */ + YYSYMBOL_U16VEC4 = 93, /* U16VEC4 */ + YYSYMBOL_I8VEC2 = 94, /* I8VEC2 */ + YYSYMBOL_I8VEC3 = 95, /* I8VEC3 */ + YYSYMBOL_I8VEC4 = 96, /* I8VEC4 */ + YYSYMBOL_U8VEC2 = 97, /* U8VEC2 */ + YYSYMBOL_U8VEC3 = 98, /* U8VEC3 */ + YYSYMBOL_U8VEC4 = 99, /* U8VEC4 */ + YYSYMBOL_DVEC2 = 100, /* DVEC2 */ + YYSYMBOL_DVEC3 = 101, /* DVEC3 */ + YYSYMBOL_DVEC4 = 102, /* DVEC4 */ + YYSYMBOL_DMAT2 = 103, /* DMAT2 */ + YYSYMBOL_DMAT3 = 104, /* DMAT3 */ + YYSYMBOL_DMAT4 = 105, /* DMAT4 */ + YYSYMBOL_BF16VEC2 = 106, /* BF16VEC2 */ + YYSYMBOL_BF16VEC3 = 107, /* BF16VEC3 */ + YYSYMBOL_BF16VEC4 = 108, /* BF16VEC4 */ + YYSYMBOL_F16VEC2 = 109, /* F16VEC2 */ + YYSYMBOL_F16VEC3 = 110, /* F16VEC3 */ + YYSYMBOL_F16VEC4 = 111, /* F16VEC4 */ + YYSYMBOL_F16MAT2 = 112, /* F16MAT2 */ + YYSYMBOL_F16MAT3 = 113, /* F16MAT3 */ + YYSYMBOL_F16MAT4 = 114, /* F16MAT4 */ + YYSYMBOL_F32VEC2 = 115, /* F32VEC2 */ + YYSYMBOL_F32VEC3 = 116, /* F32VEC3 */ + YYSYMBOL_F32VEC4 = 117, /* F32VEC4 */ + YYSYMBOL_F32MAT2 = 118, /* F32MAT2 */ + YYSYMBOL_F32MAT3 = 119, /* F32MAT3 */ + YYSYMBOL_F32MAT4 = 120, /* F32MAT4 */ + YYSYMBOL_F64VEC2 = 121, /* F64VEC2 */ + YYSYMBOL_F64VEC3 = 122, /* F64VEC3 */ + YYSYMBOL_F64VEC4 = 123, /* F64VEC4 */ + YYSYMBOL_F64MAT2 = 124, /* F64MAT2 */ + YYSYMBOL_F64MAT3 = 125, /* F64MAT3 */ + YYSYMBOL_F64MAT4 = 126, /* F64MAT4 */ + YYSYMBOL_DMAT2X2 = 127, /* DMAT2X2 */ + YYSYMBOL_DMAT2X3 = 128, /* DMAT2X3 */ + YYSYMBOL_DMAT2X4 = 129, /* DMAT2X4 */ + YYSYMBOL_DMAT3X2 = 130, /* DMAT3X2 */ + YYSYMBOL_DMAT3X3 = 131, /* DMAT3X3 */ + YYSYMBOL_DMAT3X4 = 132, /* DMAT3X4 */ + YYSYMBOL_DMAT4X2 = 133, /* DMAT4X2 */ + YYSYMBOL_DMAT4X3 = 134, /* DMAT4X3 */ + YYSYMBOL_DMAT4X4 = 135, /* DMAT4X4 */ + YYSYMBOL_F16MAT2X2 = 136, /* F16MAT2X2 */ + YYSYMBOL_F16MAT2X3 = 137, /* F16MAT2X3 */ + YYSYMBOL_F16MAT2X4 = 138, /* F16MAT2X4 */ + YYSYMBOL_F16MAT3X2 = 139, /* F16MAT3X2 */ + YYSYMBOL_F16MAT3X3 = 140, /* F16MAT3X3 */ + YYSYMBOL_F16MAT3X4 = 141, /* F16MAT3X4 */ + YYSYMBOL_F16MAT4X2 = 142, /* F16MAT4X2 */ + YYSYMBOL_F16MAT4X3 = 143, /* F16MAT4X3 */ + YYSYMBOL_F16MAT4X4 = 144, /* F16MAT4X4 */ + YYSYMBOL_F32MAT2X2 = 145, /* F32MAT2X2 */ + YYSYMBOL_F32MAT2X3 = 146, /* F32MAT2X3 */ + YYSYMBOL_F32MAT2X4 = 147, /* F32MAT2X4 */ + YYSYMBOL_F32MAT3X2 = 148, /* F32MAT3X2 */ + YYSYMBOL_F32MAT3X3 = 149, /* F32MAT3X3 */ + YYSYMBOL_F32MAT3X4 = 150, /* F32MAT3X4 */ + YYSYMBOL_F32MAT4X2 = 151, /* F32MAT4X2 */ + YYSYMBOL_F32MAT4X3 = 152, /* F32MAT4X3 */ + YYSYMBOL_F32MAT4X4 = 153, /* F32MAT4X4 */ + YYSYMBOL_F64MAT2X2 = 154, /* F64MAT2X2 */ + YYSYMBOL_F64MAT2X3 = 155, /* F64MAT2X3 */ + YYSYMBOL_F64MAT2X4 = 156, /* F64MAT2X4 */ + YYSYMBOL_F64MAT3X2 = 157, /* F64MAT3X2 */ + YYSYMBOL_F64MAT3X3 = 158, /* F64MAT3X3 */ + YYSYMBOL_F64MAT3X4 = 159, /* F64MAT3X4 */ + YYSYMBOL_F64MAT4X2 = 160, /* F64MAT4X2 */ + YYSYMBOL_F64MAT4X3 = 161, /* F64MAT4X3 */ + YYSYMBOL_F64MAT4X4 = 162, /* F64MAT4X4 */ + YYSYMBOL_ATOMIC_UINT = 163, /* ATOMIC_UINT */ + YYSYMBOL_ACCSTRUCTNV = 164, /* ACCSTRUCTNV */ + YYSYMBOL_ACCSTRUCTEXT = 165, /* ACCSTRUCTEXT */ + YYSYMBOL_RAYQUERYEXT = 166, /* RAYQUERYEXT */ + YYSYMBOL_FCOOPMATNV = 167, /* FCOOPMATNV */ + YYSYMBOL_ICOOPMATNV = 168, /* ICOOPMATNV */ + YYSYMBOL_UCOOPMATNV = 169, /* UCOOPMATNV */ + YYSYMBOL_COOPMAT = 170, /* COOPMAT */ + YYSYMBOL_COOPVECNV = 171, /* COOPVECNV */ + YYSYMBOL_HITOBJECTNV = 172, /* HITOBJECTNV */ + YYSYMBOL_HITOBJECTATTRNV = 173, /* HITOBJECTATTRNV */ + YYSYMBOL_TENSORLAYOUTNV = 174, /* TENSORLAYOUTNV */ + YYSYMBOL_TENSORVIEWNV = 175, /* TENSORVIEWNV */ + YYSYMBOL_SAMPLERCUBEARRAY = 176, /* SAMPLERCUBEARRAY */ + YYSYMBOL_SAMPLERCUBEARRAYSHADOW = 177, /* SAMPLERCUBEARRAYSHADOW */ + YYSYMBOL_ISAMPLERCUBEARRAY = 178, /* ISAMPLERCUBEARRAY */ + YYSYMBOL_USAMPLERCUBEARRAY = 179, /* USAMPLERCUBEARRAY */ + YYSYMBOL_SAMPLER1D = 180, /* SAMPLER1D */ + YYSYMBOL_SAMPLER1DARRAY = 181, /* SAMPLER1DARRAY */ + YYSYMBOL_SAMPLER1DARRAYSHADOW = 182, /* SAMPLER1DARRAYSHADOW */ + YYSYMBOL_ISAMPLER1D = 183, /* ISAMPLER1D */ + YYSYMBOL_SAMPLER1DSHADOW = 184, /* SAMPLER1DSHADOW */ + YYSYMBOL_SAMPLER2DRECT = 185, /* SAMPLER2DRECT */ + YYSYMBOL_SAMPLER2DRECTSHADOW = 186, /* SAMPLER2DRECTSHADOW */ + YYSYMBOL_ISAMPLER2DRECT = 187, /* ISAMPLER2DRECT */ + YYSYMBOL_USAMPLER2DRECT = 188, /* USAMPLER2DRECT */ + YYSYMBOL_SAMPLERBUFFER = 189, /* SAMPLERBUFFER */ + YYSYMBOL_ISAMPLERBUFFER = 190, /* ISAMPLERBUFFER */ + YYSYMBOL_USAMPLERBUFFER = 191, /* USAMPLERBUFFER */ + YYSYMBOL_SAMPLER2DMS = 192, /* SAMPLER2DMS */ + YYSYMBOL_ISAMPLER2DMS = 193, /* ISAMPLER2DMS */ + YYSYMBOL_USAMPLER2DMS = 194, /* USAMPLER2DMS */ + YYSYMBOL_SAMPLER2DMSARRAY = 195, /* SAMPLER2DMSARRAY */ + YYSYMBOL_ISAMPLER2DMSARRAY = 196, /* ISAMPLER2DMSARRAY */ + YYSYMBOL_USAMPLER2DMSARRAY = 197, /* USAMPLER2DMSARRAY */ + YYSYMBOL_SAMPLEREXTERNALOES = 198, /* SAMPLEREXTERNALOES */ + YYSYMBOL_SAMPLEREXTERNAL2DY2YEXT = 199, /* SAMPLEREXTERNAL2DY2YEXT */ + YYSYMBOL_ISAMPLER1DARRAY = 200, /* ISAMPLER1DARRAY */ + YYSYMBOL_USAMPLER1D = 201, /* USAMPLER1D */ + YYSYMBOL_USAMPLER1DARRAY = 202, /* USAMPLER1DARRAY */ + YYSYMBOL_F16SAMPLER1D = 203, /* F16SAMPLER1D */ + YYSYMBOL_F16SAMPLER2D = 204, /* F16SAMPLER2D */ + YYSYMBOL_F16SAMPLER3D = 205, /* F16SAMPLER3D */ + YYSYMBOL_F16SAMPLER2DRECT = 206, /* F16SAMPLER2DRECT */ + YYSYMBOL_F16SAMPLERCUBE = 207, /* F16SAMPLERCUBE */ + YYSYMBOL_F16SAMPLER1DARRAY = 208, /* F16SAMPLER1DARRAY */ + YYSYMBOL_F16SAMPLER2DARRAY = 209, /* F16SAMPLER2DARRAY */ + YYSYMBOL_F16SAMPLERCUBEARRAY = 210, /* F16SAMPLERCUBEARRAY */ + YYSYMBOL_F16SAMPLERBUFFER = 211, /* F16SAMPLERBUFFER */ + YYSYMBOL_F16SAMPLER2DMS = 212, /* F16SAMPLER2DMS */ + YYSYMBOL_F16SAMPLER2DMSARRAY = 213, /* F16SAMPLER2DMSARRAY */ + YYSYMBOL_F16SAMPLER1DSHADOW = 214, /* F16SAMPLER1DSHADOW */ + YYSYMBOL_F16SAMPLER2DSHADOW = 215, /* F16SAMPLER2DSHADOW */ + YYSYMBOL_F16SAMPLER1DARRAYSHADOW = 216, /* F16SAMPLER1DARRAYSHADOW */ + YYSYMBOL_F16SAMPLER2DARRAYSHADOW = 217, /* F16SAMPLER2DARRAYSHADOW */ + YYSYMBOL_F16SAMPLER2DRECTSHADOW = 218, /* F16SAMPLER2DRECTSHADOW */ + YYSYMBOL_F16SAMPLERCUBESHADOW = 219, /* F16SAMPLERCUBESHADOW */ + YYSYMBOL_F16SAMPLERCUBEARRAYSHADOW = 220, /* F16SAMPLERCUBEARRAYSHADOW */ + YYSYMBOL_IMAGE1D = 221, /* IMAGE1D */ + YYSYMBOL_IIMAGE1D = 222, /* IIMAGE1D */ + YYSYMBOL_UIMAGE1D = 223, /* UIMAGE1D */ + YYSYMBOL_IMAGE2D = 224, /* IMAGE2D */ + YYSYMBOL_IIMAGE2D = 225, /* IIMAGE2D */ + YYSYMBOL_UIMAGE2D = 226, /* UIMAGE2D */ + YYSYMBOL_IMAGE3D = 227, /* IMAGE3D */ + YYSYMBOL_IIMAGE3D = 228, /* IIMAGE3D */ + YYSYMBOL_UIMAGE3D = 229, /* UIMAGE3D */ + YYSYMBOL_IMAGE2DRECT = 230, /* IMAGE2DRECT */ + YYSYMBOL_IIMAGE2DRECT = 231, /* IIMAGE2DRECT */ + YYSYMBOL_UIMAGE2DRECT = 232, /* UIMAGE2DRECT */ + YYSYMBOL_IMAGECUBE = 233, /* IMAGECUBE */ + YYSYMBOL_IIMAGECUBE = 234, /* IIMAGECUBE */ + YYSYMBOL_UIMAGECUBE = 235, /* UIMAGECUBE */ + YYSYMBOL_IMAGEBUFFER = 236, /* IMAGEBUFFER */ + YYSYMBOL_IIMAGEBUFFER = 237, /* IIMAGEBUFFER */ + YYSYMBOL_UIMAGEBUFFER = 238, /* UIMAGEBUFFER */ + YYSYMBOL_IMAGE1DARRAY = 239, /* IMAGE1DARRAY */ + YYSYMBOL_IIMAGE1DARRAY = 240, /* IIMAGE1DARRAY */ + YYSYMBOL_UIMAGE1DARRAY = 241, /* UIMAGE1DARRAY */ + YYSYMBOL_IMAGE2DARRAY = 242, /* IMAGE2DARRAY */ + YYSYMBOL_IIMAGE2DARRAY = 243, /* IIMAGE2DARRAY */ + YYSYMBOL_UIMAGE2DARRAY = 244, /* UIMAGE2DARRAY */ + YYSYMBOL_IMAGECUBEARRAY = 245, /* IMAGECUBEARRAY */ + YYSYMBOL_IIMAGECUBEARRAY = 246, /* IIMAGECUBEARRAY */ + YYSYMBOL_UIMAGECUBEARRAY = 247, /* UIMAGECUBEARRAY */ + YYSYMBOL_IMAGE2DMS = 248, /* IMAGE2DMS */ + YYSYMBOL_IIMAGE2DMS = 249, /* IIMAGE2DMS */ + YYSYMBOL_UIMAGE2DMS = 250, /* UIMAGE2DMS */ + YYSYMBOL_IMAGE2DMSARRAY = 251, /* IMAGE2DMSARRAY */ + YYSYMBOL_IIMAGE2DMSARRAY = 252, /* IIMAGE2DMSARRAY */ + YYSYMBOL_UIMAGE2DMSARRAY = 253, /* UIMAGE2DMSARRAY */ + YYSYMBOL_F16IMAGE1D = 254, /* F16IMAGE1D */ + YYSYMBOL_F16IMAGE2D = 255, /* F16IMAGE2D */ + YYSYMBOL_F16IMAGE3D = 256, /* F16IMAGE3D */ + YYSYMBOL_F16IMAGE2DRECT = 257, /* F16IMAGE2DRECT */ + YYSYMBOL_F16IMAGECUBE = 258, /* F16IMAGECUBE */ + YYSYMBOL_F16IMAGE1DARRAY = 259, /* F16IMAGE1DARRAY */ + YYSYMBOL_F16IMAGE2DARRAY = 260, /* F16IMAGE2DARRAY */ + YYSYMBOL_F16IMAGECUBEARRAY = 261, /* F16IMAGECUBEARRAY */ + YYSYMBOL_F16IMAGEBUFFER = 262, /* F16IMAGEBUFFER */ + YYSYMBOL_F16IMAGE2DMS = 263, /* F16IMAGE2DMS */ + YYSYMBOL_F16IMAGE2DMSARRAY = 264, /* F16IMAGE2DMSARRAY */ + YYSYMBOL_I64IMAGE1D = 265, /* I64IMAGE1D */ + YYSYMBOL_U64IMAGE1D = 266, /* U64IMAGE1D */ + YYSYMBOL_I64IMAGE2D = 267, /* I64IMAGE2D */ + YYSYMBOL_U64IMAGE2D = 268, /* U64IMAGE2D */ + YYSYMBOL_I64IMAGE3D = 269, /* I64IMAGE3D */ + YYSYMBOL_U64IMAGE3D = 270, /* U64IMAGE3D */ + YYSYMBOL_I64IMAGE2DRECT = 271, /* I64IMAGE2DRECT */ + YYSYMBOL_U64IMAGE2DRECT = 272, /* U64IMAGE2DRECT */ + YYSYMBOL_I64IMAGECUBE = 273, /* I64IMAGECUBE */ + YYSYMBOL_U64IMAGECUBE = 274, /* U64IMAGECUBE */ + YYSYMBOL_I64IMAGEBUFFER = 275, /* I64IMAGEBUFFER */ + YYSYMBOL_U64IMAGEBUFFER = 276, /* U64IMAGEBUFFER */ + YYSYMBOL_I64IMAGE1DARRAY = 277, /* I64IMAGE1DARRAY */ + YYSYMBOL_U64IMAGE1DARRAY = 278, /* U64IMAGE1DARRAY */ + YYSYMBOL_I64IMAGE2DARRAY = 279, /* I64IMAGE2DARRAY */ + YYSYMBOL_U64IMAGE2DARRAY = 280, /* U64IMAGE2DARRAY */ + YYSYMBOL_I64IMAGECUBEARRAY = 281, /* I64IMAGECUBEARRAY */ + YYSYMBOL_U64IMAGECUBEARRAY = 282, /* U64IMAGECUBEARRAY */ + YYSYMBOL_I64IMAGE2DMS = 283, /* I64IMAGE2DMS */ + YYSYMBOL_U64IMAGE2DMS = 284, /* U64IMAGE2DMS */ + YYSYMBOL_I64IMAGE2DMSARRAY = 285, /* I64IMAGE2DMSARRAY */ + YYSYMBOL_U64IMAGE2DMSARRAY = 286, /* U64IMAGE2DMSARRAY */ + YYSYMBOL_TEXTURECUBEARRAY = 287, /* TEXTURECUBEARRAY */ + YYSYMBOL_ITEXTURECUBEARRAY = 288, /* ITEXTURECUBEARRAY */ + YYSYMBOL_UTEXTURECUBEARRAY = 289, /* UTEXTURECUBEARRAY */ + YYSYMBOL_TEXTURE1D = 290, /* TEXTURE1D */ + YYSYMBOL_ITEXTURE1D = 291, /* ITEXTURE1D */ + YYSYMBOL_UTEXTURE1D = 292, /* UTEXTURE1D */ + YYSYMBOL_TEXTURE1DARRAY = 293, /* TEXTURE1DARRAY */ + YYSYMBOL_ITEXTURE1DARRAY = 294, /* ITEXTURE1DARRAY */ + YYSYMBOL_UTEXTURE1DARRAY = 295, /* UTEXTURE1DARRAY */ + YYSYMBOL_TEXTURE2DRECT = 296, /* TEXTURE2DRECT */ + YYSYMBOL_ITEXTURE2DRECT = 297, /* ITEXTURE2DRECT */ + YYSYMBOL_UTEXTURE2DRECT = 298, /* UTEXTURE2DRECT */ + YYSYMBOL_TEXTUREBUFFER = 299, /* TEXTUREBUFFER */ + YYSYMBOL_ITEXTUREBUFFER = 300, /* ITEXTUREBUFFER */ + YYSYMBOL_UTEXTUREBUFFER = 301, /* UTEXTUREBUFFER */ + YYSYMBOL_TEXTURE2DMS = 302, /* TEXTURE2DMS */ + YYSYMBOL_ITEXTURE2DMS = 303, /* ITEXTURE2DMS */ + YYSYMBOL_UTEXTURE2DMS = 304, /* UTEXTURE2DMS */ + YYSYMBOL_TEXTURE2DMSARRAY = 305, /* TEXTURE2DMSARRAY */ + YYSYMBOL_ITEXTURE2DMSARRAY = 306, /* ITEXTURE2DMSARRAY */ + YYSYMBOL_UTEXTURE2DMSARRAY = 307, /* UTEXTURE2DMSARRAY */ + YYSYMBOL_F16TEXTURE1D = 308, /* F16TEXTURE1D */ + YYSYMBOL_F16TEXTURE2D = 309, /* F16TEXTURE2D */ + YYSYMBOL_F16TEXTURE3D = 310, /* F16TEXTURE3D */ + YYSYMBOL_F16TEXTURE2DRECT = 311, /* F16TEXTURE2DRECT */ + YYSYMBOL_F16TEXTURECUBE = 312, /* F16TEXTURECUBE */ + YYSYMBOL_F16TEXTURE1DARRAY = 313, /* F16TEXTURE1DARRAY */ + YYSYMBOL_F16TEXTURE2DARRAY = 314, /* F16TEXTURE2DARRAY */ + YYSYMBOL_F16TEXTURECUBEARRAY = 315, /* F16TEXTURECUBEARRAY */ + YYSYMBOL_F16TEXTUREBUFFER = 316, /* F16TEXTUREBUFFER */ + YYSYMBOL_F16TEXTURE2DMS = 317, /* F16TEXTURE2DMS */ + YYSYMBOL_F16TEXTURE2DMSARRAY = 318, /* F16TEXTURE2DMSARRAY */ + YYSYMBOL_SUBPASSINPUT = 319, /* SUBPASSINPUT */ + YYSYMBOL_SUBPASSINPUTMS = 320, /* SUBPASSINPUTMS */ + YYSYMBOL_ISUBPASSINPUT = 321, /* ISUBPASSINPUT */ + YYSYMBOL_ISUBPASSINPUTMS = 322, /* ISUBPASSINPUTMS */ + YYSYMBOL_USUBPASSINPUT = 323, /* USUBPASSINPUT */ + YYSYMBOL_USUBPASSINPUTMS = 324, /* USUBPASSINPUTMS */ + YYSYMBOL_F16SUBPASSINPUT = 325, /* F16SUBPASSINPUT */ + YYSYMBOL_F16SUBPASSINPUTMS = 326, /* F16SUBPASSINPUTMS */ + YYSYMBOL_SPIRV_INSTRUCTION = 327, /* SPIRV_INSTRUCTION */ + YYSYMBOL_SPIRV_EXECUTION_MODE = 328, /* SPIRV_EXECUTION_MODE */ + YYSYMBOL_SPIRV_EXECUTION_MODE_ID = 329, /* SPIRV_EXECUTION_MODE_ID */ + YYSYMBOL_SPIRV_DECORATE = 330, /* SPIRV_DECORATE */ + YYSYMBOL_SPIRV_DECORATE_ID = 331, /* SPIRV_DECORATE_ID */ + YYSYMBOL_SPIRV_DECORATE_STRING = 332, /* SPIRV_DECORATE_STRING */ + YYSYMBOL_SPIRV_TYPE = 333, /* SPIRV_TYPE */ + YYSYMBOL_SPIRV_STORAGE_CLASS = 334, /* SPIRV_STORAGE_CLASS */ + YYSYMBOL_SPIRV_BY_REFERENCE = 335, /* SPIRV_BY_REFERENCE */ + YYSYMBOL_SPIRV_LITERAL = 336, /* SPIRV_LITERAL */ + YYSYMBOL_ATTACHMENTEXT = 337, /* ATTACHMENTEXT */ + YYSYMBOL_IATTACHMENTEXT = 338, /* IATTACHMENTEXT */ + YYSYMBOL_UATTACHMENTEXT = 339, /* UATTACHMENTEXT */ + YYSYMBOL_LEFT_OP = 340, /* LEFT_OP */ + YYSYMBOL_RIGHT_OP = 341, /* RIGHT_OP */ + YYSYMBOL_INC_OP = 342, /* INC_OP */ + YYSYMBOL_DEC_OP = 343, /* DEC_OP */ + YYSYMBOL_LE_OP = 344, /* LE_OP */ + YYSYMBOL_GE_OP = 345, /* GE_OP */ + YYSYMBOL_EQ_OP = 346, /* EQ_OP */ + YYSYMBOL_NE_OP = 347, /* NE_OP */ + YYSYMBOL_AND_OP = 348, /* AND_OP */ + YYSYMBOL_OR_OP = 349, /* OR_OP */ + YYSYMBOL_XOR_OP = 350, /* XOR_OP */ + YYSYMBOL_MUL_ASSIGN = 351, /* MUL_ASSIGN */ + YYSYMBOL_DIV_ASSIGN = 352, /* DIV_ASSIGN */ + YYSYMBOL_ADD_ASSIGN = 353, /* ADD_ASSIGN */ + YYSYMBOL_MOD_ASSIGN = 354, /* MOD_ASSIGN */ + YYSYMBOL_LEFT_ASSIGN = 355, /* LEFT_ASSIGN */ + YYSYMBOL_RIGHT_ASSIGN = 356, /* RIGHT_ASSIGN */ + YYSYMBOL_AND_ASSIGN = 357, /* AND_ASSIGN */ + YYSYMBOL_XOR_ASSIGN = 358, /* XOR_ASSIGN */ + YYSYMBOL_OR_ASSIGN = 359, /* OR_ASSIGN */ + YYSYMBOL_SUB_ASSIGN = 360, /* SUB_ASSIGN */ + YYSYMBOL_STRING_LITERAL = 361, /* STRING_LITERAL */ + YYSYMBOL_LEFT_PAREN = 362, /* LEFT_PAREN */ + YYSYMBOL_RIGHT_PAREN = 363, /* RIGHT_PAREN */ + YYSYMBOL_LEFT_BRACKET = 364, /* LEFT_BRACKET */ + YYSYMBOL_RIGHT_BRACKET = 365, /* RIGHT_BRACKET */ + YYSYMBOL_LEFT_BRACE = 366, /* LEFT_BRACE */ + YYSYMBOL_RIGHT_BRACE = 367, /* RIGHT_BRACE */ + YYSYMBOL_DOT = 368, /* DOT */ + YYSYMBOL_COMMA = 369, /* COMMA */ + YYSYMBOL_COLON = 370, /* COLON */ + YYSYMBOL_EQUAL = 371, /* EQUAL */ + YYSYMBOL_SEMICOLON = 372, /* SEMICOLON */ + YYSYMBOL_BANG = 373, /* BANG */ + YYSYMBOL_DASH = 374, /* DASH */ + YYSYMBOL_TILDE = 375, /* TILDE */ + YYSYMBOL_PLUS = 376, /* PLUS */ + YYSYMBOL_STAR = 377, /* STAR */ + YYSYMBOL_SLASH = 378, /* SLASH */ + YYSYMBOL_PERCENT = 379, /* PERCENT */ + YYSYMBOL_LEFT_ANGLE = 380, /* LEFT_ANGLE */ + YYSYMBOL_RIGHT_ANGLE = 381, /* RIGHT_ANGLE */ + YYSYMBOL_VERTICAL_BAR = 382, /* VERTICAL_BAR */ + YYSYMBOL_CARET = 383, /* CARET */ + YYSYMBOL_AMPERSAND = 384, /* AMPERSAND */ + YYSYMBOL_QUESTION = 385, /* QUESTION */ + YYSYMBOL_INVARIANT = 386, /* INVARIANT */ + YYSYMBOL_HIGH_PRECISION = 387, /* HIGH_PRECISION */ + YYSYMBOL_MEDIUM_PRECISION = 388, /* MEDIUM_PRECISION */ + YYSYMBOL_LOW_PRECISION = 389, /* LOW_PRECISION */ + YYSYMBOL_PRECISION = 390, /* PRECISION */ + YYSYMBOL_PACKED = 391, /* PACKED */ + YYSYMBOL_RESOURCE = 392, /* RESOURCE */ + YYSYMBOL_SUPERP = 393, /* SUPERP */ + YYSYMBOL_FLOATCONSTANT = 394, /* FLOATCONSTANT */ + YYSYMBOL_INTCONSTANT = 395, /* INTCONSTANT */ + YYSYMBOL_UINTCONSTANT = 396, /* UINTCONSTANT */ + YYSYMBOL_BOOLCONSTANT = 397, /* BOOLCONSTANT */ + YYSYMBOL_IDENTIFIER = 398, /* IDENTIFIER */ + YYSYMBOL_TYPE_NAME = 399, /* TYPE_NAME */ + YYSYMBOL_CENTROID = 400, /* CENTROID */ + YYSYMBOL_IN = 401, /* IN */ + YYSYMBOL_OUT = 402, /* OUT */ + YYSYMBOL_INOUT = 403, /* INOUT */ + YYSYMBOL_STRUCT = 404, /* STRUCT */ + YYSYMBOL_VOID = 405, /* VOID */ + YYSYMBOL_WHILE = 406, /* WHILE */ + YYSYMBOL_BREAK = 407, /* BREAK */ + YYSYMBOL_CONTINUE = 408, /* CONTINUE */ + YYSYMBOL_DO = 409, /* DO */ + YYSYMBOL_ELSE = 410, /* ELSE */ + YYSYMBOL_FOR = 411, /* FOR */ + YYSYMBOL_IF = 412, /* IF */ + YYSYMBOL_DISCARD = 413, /* DISCARD */ + YYSYMBOL_RETURN = 414, /* RETURN */ + YYSYMBOL_SWITCH = 415, /* SWITCH */ + YYSYMBOL_CASE = 416, /* CASE */ + YYSYMBOL_DEFAULT = 417, /* DEFAULT */ + YYSYMBOL_TERMINATE_INVOCATION = 418, /* TERMINATE_INVOCATION */ + YYSYMBOL_TERMINATE_RAY = 419, /* TERMINATE_RAY */ + YYSYMBOL_IGNORE_INTERSECTION = 420, /* IGNORE_INTERSECTION */ + YYSYMBOL_UNIFORM = 421, /* UNIFORM */ + YYSYMBOL_SHARED = 422, /* SHARED */ + YYSYMBOL_BUFFER = 423, /* BUFFER */ + YYSYMBOL_TILEIMAGEEXT = 424, /* TILEIMAGEEXT */ + YYSYMBOL_FLAT = 425, /* FLAT */ + YYSYMBOL_SMOOTH = 426, /* SMOOTH */ + YYSYMBOL_LAYOUT = 427, /* LAYOUT */ + YYSYMBOL_DOUBLECONSTANT = 428, /* DOUBLECONSTANT */ + YYSYMBOL_INT16CONSTANT = 429, /* INT16CONSTANT */ + YYSYMBOL_UINT16CONSTANT = 430, /* UINT16CONSTANT */ + YYSYMBOL_FLOAT16CONSTANT = 431, /* FLOAT16CONSTANT */ + YYSYMBOL_INT32CONSTANT = 432, /* INT32CONSTANT */ + YYSYMBOL_UINT32CONSTANT = 433, /* UINT32CONSTANT */ + YYSYMBOL_INT64CONSTANT = 434, /* INT64CONSTANT */ + YYSYMBOL_UINT64CONSTANT = 435, /* UINT64CONSTANT */ + YYSYMBOL_SUBROUTINE = 436, /* SUBROUTINE */ + YYSYMBOL_DEMOTE = 437, /* DEMOTE */ + YYSYMBOL_FUNCTION = 438, /* FUNCTION */ + YYSYMBOL_PAYLOADNV = 439, /* PAYLOADNV */ + YYSYMBOL_PAYLOADINNV = 440, /* PAYLOADINNV */ + YYSYMBOL_HITATTRNV = 441, /* HITATTRNV */ + YYSYMBOL_CALLDATANV = 442, /* CALLDATANV */ + YYSYMBOL_CALLDATAINNV = 443, /* CALLDATAINNV */ + YYSYMBOL_PAYLOADEXT = 444, /* PAYLOADEXT */ + YYSYMBOL_PAYLOADINEXT = 445, /* PAYLOADINEXT */ + YYSYMBOL_HITATTREXT = 446, /* HITATTREXT */ + YYSYMBOL_CALLDATAEXT = 447, /* CALLDATAEXT */ + YYSYMBOL_CALLDATAINEXT = 448, /* CALLDATAINEXT */ + YYSYMBOL_PATCH = 449, /* PATCH */ + YYSYMBOL_SAMPLE = 450, /* SAMPLE */ + YYSYMBOL_NONUNIFORM = 451, /* NONUNIFORM */ + YYSYMBOL_COHERENT = 452, /* COHERENT */ + YYSYMBOL_VOLATILE = 453, /* VOLATILE */ + YYSYMBOL_RESTRICT = 454, /* RESTRICT */ + YYSYMBOL_READONLY = 455, /* READONLY */ + YYSYMBOL_WRITEONLY = 456, /* WRITEONLY */ + YYSYMBOL_NONTEMPORAL = 457, /* NONTEMPORAL */ + YYSYMBOL_DEVICECOHERENT = 458, /* DEVICECOHERENT */ + YYSYMBOL_QUEUEFAMILYCOHERENT = 459, /* QUEUEFAMILYCOHERENT */ + YYSYMBOL_WORKGROUPCOHERENT = 460, /* WORKGROUPCOHERENT */ + YYSYMBOL_SUBGROUPCOHERENT = 461, /* SUBGROUPCOHERENT */ + YYSYMBOL_NONPRIVATE = 462, /* NONPRIVATE */ + YYSYMBOL_SHADERCALLCOHERENT = 463, /* SHADERCALLCOHERENT */ + YYSYMBOL_NOPERSPECTIVE = 464, /* NOPERSPECTIVE */ + YYSYMBOL_EXPLICITINTERPAMD = 465, /* EXPLICITINTERPAMD */ + YYSYMBOL_PERVERTEXEXT = 466, /* PERVERTEXEXT */ + YYSYMBOL_PERVERTEXNV = 467, /* PERVERTEXNV */ + YYSYMBOL_PERPRIMITIVENV = 468, /* PERPRIMITIVENV */ + YYSYMBOL_PERVIEWNV = 469, /* PERVIEWNV */ + YYSYMBOL_PERTASKNV = 470, /* PERTASKNV */ + YYSYMBOL_PERPRIMITIVEEXT = 471, /* PERPRIMITIVEEXT */ + YYSYMBOL_TASKPAYLOADWORKGROUPEXT = 472, /* TASKPAYLOADWORKGROUPEXT */ + YYSYMBOL_PRECISE = 473, /* PRECISE */ + YYSYMBOL_YYACCEPT = 474, /* $accept */ + YYSYMBOL_variable_identifier = 475, /* variable_identifier */ + YYSYMBOL_primary_expression = 476, /* primary_expression */ + YYSYMBOL_postfix_expression = 477, /* postfix_expression */ + YYSYMBOL_integer_expression = 478, /* integer_expression */ + YYSYMBOL_function_call = 479, /* function_call */ + YYSYMBOL_function_call_or_method = 480, /* function_call_or_method */ + YYSYMBOL_function_call_generic = 481, /* function_call_generic */ + YYSYMBOL_function_call_header_no_parameters = 482, /* function_call_header_no_parameters */ + YYSYMBOL_function_call_header_with_parameters = 483, /* function_call_header_with_parameters */ + YYSYMBOL_function_call_header = 484, /* function_call_header */ + YYSYMBOL_function_identifier = 485, /* function_identifier */ + YYSYMBOL_unary_expression = 486, /* unary_expression */ + YYSYMBOL_unary_operator = 487, /* unary_operator */ + YYSYMBOL_multiplicative_expression = 488, /* multiplicative_expression */ + YYSYMBOL_additive_expression = 489, /* additive_expression */ + YYSYMBOL_shift_expression = 490, /* shift_expression */ + YYSYMBOL_relational_expression = 491, /* relational_expression */ + YYSYMBOL_equality_expression = 492, /* equality_expression */ + YYSYMBOL_and_expression = 493, /* and_expression */ + YYSYMBOL_exclusive_or_expression = 494, /* exclusive_or_expression */ + YYSYMBOL_inclusive_or_expression = 495, /* inclusive_or_expression */ + YYSYMBOL_logical_and_expression = 496, /* logical_and_expression */ + YYSYMBOL_logical_xor_expression = 497, /* logical_xor_expression */ + YYSYMBOL_logical_or_expression = 498, /* logical_or_expression */ + YYSYMBOL_conditional_expression = 499, /* conditional_expression */ + YYSYMBOL_500_1 = 500, /* $@1 */ + YYSYMBOL_assignment_expression = 501, /* assignment_expression */ + YYSYMBOL_assignment_operator = 502, /* assignment_operator */ + YYSYMBOL_expression = 503, /* expression */ + YYSYMBOL_constant_expression = 504, /* constant_expression */ + YYSYMBOL_declaration = 505, /* declaration */ + YYSYMBOL_block_structure = 506, /* block_structure */ + YYSYMBOL_507_2 = 507, /* $@2 */ + YYSYMBOL_identifier_list = 508, /* identifier_list */ + YYSYMBOL_function_prototype = 509, /* function_prototype */ + YYSYMBOL_function_declarator = 510, /* function_declarator */ + YYSYMBOL_function_header_with_parameters = 511, /* function_header_with_parameters */ + YYSYMBOL_function_header = 512, /* function_header */ + YYSYMBOL_parameter_declarator = 513, /* parameter_declarator */ + YYSYMBOL_parameter_declaration = 514, /* parameter_declaration */ + YYSYMBOL_parameter_type_specifier = 515, /* parameter_type_specifier */ + YYSYMBOL_init_declarator_list = 516, /* init_declarator_list */ + YYSYMBOL_single_declaration = 517, /* single_declaration */ + YYSYMBOL_fully_specified_type = 518, /* fully_specified_type */ + YYSYMBOL_invariant_qualifier = 519, /* invariant_qualifier */ + YYSYMBOL_interpolation_qualifier = 520, /* interpolation_qualifier */ + YYSYMBOL_layout_qualifier = 521, /* layout_qualifier */ + YYSYMBOL_layout_qualifier_id_list = 522, /* layout_qualifier_id_list */ + YYSYMBOL_layout_qualifier_id = 523, /* layout_qualifier_id */ + YYSYMBOL_precise_qualifier = 524, /* precise_qualifier */ + YYSYMBOL_type_qualifier = 525, /* type_qualifier */ + YYSYMBOL_single_type_qualifier = 526, /* single_type_qualifier */ + YYSYMBOL_storage_qualifier = 527, /* storage_qualifier */ + YYSYMBOL_non_uniform_qualifier = 528, /* non_uniform_qualifier */ + YYSYMBOL_type_name_list = 529, /* type_name_list */ + YYSYMBOL_type_specifier = 530, /* type_specifier */ + YYSYMBOL_array_specifier = 531, /* array_specifier */ + YYSYMBOL_type_parameter_specifier_opt = 532, /* type_parameter_specifier_opt */ + YYSYMBOL_type_parameter_specifier = 533, /* type_parameter_specifier */ + YYSYMBOL_type_parameter_specifier_list = 534, /* type_parameter_specifier_list */ + YYSYMBOL_type_specifier_nonarray = 535, /* type_specifier_nonarray */ + YYSYMBOL_precision_qualifier = 536, /* precision_qualifier */ + YYSYMBOL_struct_specifier = 537, /* struct_specifier */ + YYSYMBOL_538_3 = 538, /* $@3 */ + YYSYMBOL_539_4 = 539, /* $@4 */ + YYSYMBOL_struct_declaration_list = 540, /* struct_declaration_list */ + YYSYMBOL_struct_declaration = 541, /* struct_declaration */ + YYSYMBOL_struct_declarator_list = 542, /* struct_declarator_list */ + YYSYMBOL_struct_declarator = 543, /* struct_declarator */ + YYSYMBOL_initializer = 544, /* initializer */ + YYSYMBOL_initializer_list = 545, /* initializer_list */ + YYSYMBOL_declaration_statement = 546, /* declaration_statement */ + YYSYMBOL_statement = 547, /* statement */ + YYSYMBOL_simple_statement = 548, /* simple_statement */ + YYSYMBOL_demote_statement = 549, /* demote_statement */ + YYSYMBOL_compound_statement = 550, /* compound_statement */ + YYSYMBOL_551_5 = 551, /* $@5 */ + YYSYMBOL_552_6 = 552, /* $@6 */ + YYSYMBOL_statement_no_new_scope = 553, /* statement_no_new_scope */ + YYSYMBOL_statement_scoped = 554, /* statement_scoped */ + YYSYMBOL_555_7 = 555, /* $@7 */ + YYSYMBOL_556_8 = 556, /* $@8 */ + YYSYMBOL_compound_statement_no_new_scope = 557, /* compound_statement_no_new_scope */ + YYSYMBOL_statement_list = 558, /* statement_list */ + YYSYMBOL_expression_statement = 559, /* expression_statement */ + YYSYMBOL_selection_statement = 560, /* selection_statement */ + YYSYMBOL_selection_statement_nonattributed = 561, /* selection_statement_nonattributed */ + YYSYMBOL_selection_rest_statement = 562, /* selection_rest_statement */ + YYSYMBOL_condition = 563, /* condition */ + YYSYMBOL_switch_statement = 564, /* switch_statement */ + YYSYMBOL_switch_statement_nonattributed = 565, /* switch_statement_nonattributed */ + YYSYMBOL_566_9 = 566, /* $@9 */ + YYSYMBOL_switch_statement_list = 567, /* switch_statement_list */ + YYSYMBOL_case_label = 568, /* case_label */ + YYSYMBOL_iteration_statement = 569, /* iteration_statement */ + YYSYMBOL_iteration_statement_nonattributed = 570, /* iteration_statement_nonattributed */ + YYSYMBOL_571_10 = 571, /* $@10 */ + YYSYMBOL_572_11 = 572, /* $@11 */ + YYSYMBOL_573_12 = 573, /* $@12 */ + YYSYMBOL_for_init_statement = 574, /* for_init_statement */ + YYSYMBOL_conditionopt = 575, /* conditionopt */ + YYSYMBOL_for_rest_statement = 576, /* for_rest_statement */ + YYSYMBOL_jump_statement = 577, /* jump_statement */ + YYSYMBOL_translation_unit = 578, /* translation_unit */ + YYSYMBOL_external_declaration = 579, /* external_declaration */ + YYSYMBOL_function_definition = 580, /* function_definition */ + YYSYMBOL_581_13 = 581, /* $@13 */ + YYSYMBOL_attribute = 582, /* attribute */ + YYSYMBOL_attribute_list = 583, /* attribute_list */ + YYSYMBOL_single_attribute = 584, /* single_attribute */ + YYSYMBOL_spirv_requirements_list = 585, /* spirv_requirements_list */ + YYSYMBOL_spirv_requirements_parameter = 586, /* spirv_requirements_parameter */ + YYSYMBOL_spirv_extension_list = 587, /* spirv_extension_list */ + YYSYMBOL_spirv_capability_list = 588, /* spirv_capability_list */ + YYSYMBOL_spirv_execution_mode_qualifier = 589, /* spirv_execution_mode_qualifier */ + YYSYMBOL_spirv_execution_mode_parameter_list = 590, /* spirv_execution_mode_parameter_list */ + YYSYMBOL_spirv_execution_mode_parameter = 591, /* spirv_execution_mode_parameter */ + YYSYMBOL_spirv_execution_mode_id_parameter_list = 592, /* spirv_execution_mode_id_parameter_list */ + YYSYMBOL_spirv_storage_class_qualifier = 593, /* spirv_storage_class_qualifier */ + YYSYMBOL_spirv_decorate_qualifier = 594, /* spirv_decorate_qualifier */ + YYSYMBOL_spirv_decorate_parameter_list = 595, /* spirv_decorate_parameter_list */ + YYSYMBOL_spirv_decorate_parameter = 596, /* spirv_decorate_parameter */ + YYSYMBOL_spirv_decorate_id_parameter_list = 597, /* spirv_decorate_id_parameter_list */ + YYSYMBOL_spirv_decorate_id_parameter = 598, /* spirv_decorate_id_parameter */ + YYSYMBOL_spirv_decorate_string_parameter_list = 599, /* spirv_decorate_string_parameter_list */ + YYSYMBOL_spirv_type_specifier = 600, /* spirv_type_specifier */ + YYSYMBOL_spirv_type_parameter_list = 601, /* spirv_type_parameter_list */ + YYSYMBOL_spirv_type_parameter = 602, /* spirv_type_parameter */ + YYSYMBOL_spirv_instruction_qualifier = 603, /* spirv_instruction_qualifier */ + YYSYMBOL_spirv_instruction_qualifier_list = 604, /* spirv_instruction_qualifier_list */ + YYSYMBOL_spirv_instruction_qualifier_id = 605 /* spirv_instruction_qualifier_id */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + +/* Second part of user prologue. */ +#line 111 "MachineIndependent/glslang.y" + + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + + +#line 745 "MachineIndependent/glslang_tab.cpp" + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 461 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 12966 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 474 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 132 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 709 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 955 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 728 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int16 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 358, 358, 364, 367, 372, 375, 378, 382, 385, + 388, 392, 396, 400, 404, 408, 412, 418, 425, 428, + 431, 434, 437, 442, 450, 457, 464, 470, 474, 481, + 484, 490, 508, 533, 541, 546, 573, 581, 587, 591, + 595, 615, 616, 617, 618, 624, 625, 630, 635, 644, + 645, 650, 658, 659, 665, 674, 675, 680, 685, 690, + 698, 699, 708, 720, 721, 730, 731, 740, 741, 750, + 751, 759, 760, 768, 769, 777, 778, 778, 796, 797, + 813, 817, 821, 825, 830, 834, 838, 842, 846, 850, + 854, 861, 864, 875, 882, 887, 894, 899, 904, 911, + 915, 919, 923, 928, 933, 942, 942, 953, 957, 964, + 969, 977, 985, 997, 1000, 1007, 1020, 1043, 1066, 1081, + 1106, 1117, 1127, 1137, 1147, 1156, 1159, 1163, 1167, 1172, + 1180, 1185, 1190, 1195, 1200, 1209, 1219, 1246, 1255, 1262, + 1269, 1276, 1283, 1291, 1299, 1309, 1319, 1326, 1336, 1342, + 1345, 1352, 1356, 1360, 1368, 1377, 1380, 1391, 1394, 1397, + 1401, 1405, 1409, 1413, 1416, 1421, 1425, 1430, 1438, 1442, + 1447, 1453, 1459, 1466, 1471, 1476, 1484, 1489, 1501, 1515, + 1521, 1526, 1534, 1542, 1550, 1558, 1566, 1574, 1582, 1590, + 1598, 1605, 1612, 1616, 1621, 1626, 1631, 1636, 1641, 1646, + 1650, 1654, 1658, 1662, 1666, 1672, 1678, 1688, 1695, 1698, + 1706, 1713, 1724, 1729, 1737, 1741, 1751, 1754, 1760, 1766, + 1772, 1780, 1790, 1794, 1798, 1802, 1807, 1811, 1816, 1821, + 1826, 1831, 1836, 1841, 1846, 1851, 1856, 1862, 1868, 1874, + 1879, 1884, 1889, 1894, 1899, 1904, 1909, 1914, 1919, 1924, + 1929, 1934, 1941, 1946, 1951, 1956, 1961, 1966, 1971, 1976, + 1981, 1986, 1991, 1996, 2001, 2009, 2017, 2025, 2031, 2037, + 2043, 2049, 2055, 2061, 2067, 2073, 2079, 2085, 2091, 2097, + 2103, 2109, 2115, 2121, 2127, 2133, 2139, 2145, 2151, 2157, + 2163, 2169, 2175, 2181, 2187, 2193, 2199, 2205, 2211, 2217, + 2223, 2229, 2235, 2241, 2249, 2257, 2265, 2273, 2281, 2289, + 2297, 2305, 2313, 2321, 2329, 2337, 2343, 2349, 2355, 2361, + 2367, 2373, 2379, 2385, 2391, 2397, 2403, 2409, 2415, 2421, + 2427, 2433, 2439, 2445, 2451, 2457, 2463, 2469, 2475, 2481, + 2487, 2493, 2499, 2505, 2511, 2517, 2523, 2529, 2535, 2541, + 2547, 2553, 2557, 2561, 2565, 2570, 2575, 2580, 2585, 2590, + 2595, 2600, 2605, 2610, 2615, 2620, 2625, 2630, 2635, 2641, + 2647, 2653, 2659, 2665, 2671, 2677, 2683, 2689, 2695, 2701, + 2707, 2713, 2718, 2723, 2728, 2733, 2738, 2743, 2748, 2753, + 2758, 2763, 2768, 2773, 2778, 2783, 2788, 2793, 2798, 2803, + 2808, 2813, 2818, 2823, 2828, 2833, 2838, 2843, 2848, 2853, + 2858, 2863, 2868, 2873, 2878, 2884, 2890, 2895, 2900, 2905, + 2911, 2916, 2921, 2926, 2932, 2937, 2942, 2947, 2953, 2958, + 2963, 2968, 2974, 2980, 2986, 2992, 2997, 3003, 3009, 3015, + 3020, 3025, 3030, 3035, 3040, 3046, 3051, 3056, 3061, 3067, + 3072, 3077, 3082, 3088, 3093, 3098, 3103, 3109, 3114, 3119, + 3124, 3130, 3135, 3140, 3145, 3151, 3156, 3161, 3166, 3172, + 3177, 3182, 3187, 3193, 3198, 3203, 3208, 3214, 3219, 3224, + 3229, 3235, 3240, 3245, 3250, 3256, 3261, 3266, 3271, 3277, + 3282, 3287, 3292, 3298, 3303, 3308, 3313, 3319, 3324, 3329, + 3334, 3340, 3345, 3350, 3355, 3360, 3365, 3370, 3375, 3380, + 3385, 3390, 3395, 3400, 3405, 3410, 3415, 3420, 3425, 3430, + 3435, 3440, 3445, 3450, 3455, 3460, 3466, 3472, 3478, 3484, + 3490, 3496, 3502, 3509, 3516, 3522, 3528, 3534, 3540, 3547, + 3554, 3561, 3568, 3573, 3578, 3582, 3588, 3592, 3596, 3601, + 3617, 3622, 3627, 3635, 3635, 3652, 3652, 3662, 3665, 3678, + 3700, 3727, 3731, 3737, 3742, 3753, 3756, 3762, 3768, 3777, + 3780, 3786, 3790, 3791, 3797, 3798, 3799, 3800, 3801, 3802, + 3803, 3804, 3808, 3816, 3817, 3821, 3817, 3835, 3836, 3840, + 3840, 3847, 3847, 3861, 3864, 3874, 3882, 3893, 3894, 3898, + 3901, 3908, 3915, 3919, 3927, 3931, 3944, 3947, 3954, 3954, + 3974, 3977, 3983, 3995, 4007, 4010, 4018, 4018, 4033, 4033, + 4051, 4051, 4072, 4075, 4081, 4084, 4090, 4094, 4101, 4106, + 4111, 4118, 4121, 4125, 4129, 4133, 4142, 4146, 4155, 4158, + 4161, 4169, 4169, 4211, 4216, 4219, 4224, 4227, 4232, 4235, + 4240, 4243, 4248, 4251, 4256, 4259, 4264, 4268, 4273, 4277, + 4282, 4286, 4293, 4296, 4301, 4304, 4307, 4310, 4313, 4318, + 4327, 4338, 4343, 4351, 4355, 4360, 4364, 4369, 4373, 4378, + 4382, 4389, 4392, 4397, 4400, 4403, 4406, 4411, 4414, 4419, + 4425, 4428, 4431, 4434, 4439, 4443, 4448, 4452, 4457, 4461, + 4468, 4471, 4476, 4479, 4484, 4487, 4493, 4496, 4501, 4504 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "CONST", "BOOL", "INT", + "UINT", "FLOAT", "BVEC2", "BVEC3", "BVEC4", "IVEC2", "IVEC3", "IVEC4", + "UVEC2", "UVEC3", "UVEC4", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", + "MAT4", "MAT2X2", "MAT2X3", "MAT2X4", "MAT3X2", "MAT3X3", "MAT3X4", + "MAT4X2", "MAT4X3", "MAT4X4", "SAMPLER2D", "SAMPLER3D", "SAMPLERCUBE", + "SAMPLER2DSHADOW", "SAMPLERCUBESHADOW", "SAMPLER2DARRAY", + "SAMPLER2DARRAYSHADOW", "ISAMPLER2D", "ISAMPLER3D", "ISAMPLERCUBE", + "ISAMPLER2DARRAY", "USAMPLER2D", "USAMPLER3D", "USAMPLERCUBE", + "USAMPLER2DARRAY", "SAMPLER", "SAMPLERSHADOW", "TEXTURE2D", "TEXTURE3D", + "TEXTURECUBE", "TEXTURE2DARRAY", "ITEXTURE2D", "ITEXTURE3D", + "ITEXTURECUBE", "ITEXTURE2DARRAY", "UTEXTURE2D", "UTEXTURE3D", + "UTEXTURECUBE", "UTEXTURE2DARRAY", "ATTRIBUTE", "VARYING", "BFLOAT16_T", + "FLOAT16_T", "FLOAT32_T", "DOUBLE", "FLOAT64_T", "INT64_T", "UINT64_T", + "INT32_T", "UINT32_T", "INT16_T", "UINT16_T", "INT8_T", "UINT8_T", + "I64VEC2", "I64VEC3", "I64VEC4", "U64VEC2", "U64VEC3", "U64VEC4", + "I32VEC2", "I32VEC3", "I32VEC4", "U32VEC2", "U32VEC3", "U32VEC4", + "I16VEC2", "I16VEC3", "I16VEC4", "U16VEC2", "U16VEC3", "U16VEC4", + "I8VEC2", "I8VEC3", "I8VEC4", "U8VEC2", "U8VEC3", "U8VEC4", "DVEC2", + "DVEC3", "DVEC4", "DMAT2", "DMAT3", "DMAT4", "BF16VEC2", "BF16VEC3", + "BF16VEC4", "F16VEC2", "F16VEC3", "F16VEC4", "F16MAT2", "F16MAT3", + "F16MAT4", "F32VEC2", "F32VEC3", "F32VEC4", "F32MAT2", "F32MAT3", + "F32MAT4", "F64VEC2", "F64VEC3", "F64VEC4", "F64MAT2", "F64MAT3", + "F64MAT4", "DMAT2X2", "DMAT2X3", "DMAT2X4", "DMAT3X2", "DMAT3X3", + "DMAT3X4", "DMAT4X2", "DMAT4X3", "DMAT4X4", "F16MAT2X2", "F16MAT2X3", + "F16MAT2X4", "F16MAT3X2", "F16MAT3X3", "F16MAT3X4", "F16MAT4X2", + "F16MAT4X3", "F16MAT4X4", "F32MAT2X2", "F32MAT2X3", "F32MAT2X4", + "F32MAT3X2", "F32MAT3X3", "F32MAT3X4", "F32MAT4X2", "F32MAT4X3", + "F32MAT4X4", "F64MAT2X2", "F64MAT2X3", "F64MAT2X4", "F64MAT3X2", + "F64MAT3X3", "F64MAT3X4", "F64MAT4X2", "F64MAT4X3", "F64MAT4X4", + "ATOMIC_UINT", "ACCSTRUCTNV", "ACCSTRUCTEXT", "RAYQUERYEXT", + "FCOOPMATNV", "ICOOPMATNV", "UCOOPMATNV", "COOPMAT", "COOPVECNV", + "HITOBJECTNV", "HITOBJECTATTRNV", "TENSORLAYOUTNV", "TENSORVIEWNV", + "SAMPLERCUBEARRAY", "SAMPLERCUBEARRAYSHADOW", "ISAMPLERCUBEARRAY", + "USAMPLERCUBEARRAY", "SAMPLER1D", "SAMPLER1DARRAY", + "SAMPLER1DARRAYSHADOW", "ISAMPLER1D", "SAMPLER1DSHADOW", "SAMPLER2DRECT", + "SAMPLER2DRECTSHADOW", "ISAMPLER2DRECT", "USAMPLER2DRECT", + "SAMPLERBUFFER", "ISAMPLERBUFFER", "USAMPLERBUFFER", "SAMPLER2DMS", + "ISAMPLER2DMS", "USAMPLER2DMS", "SAMPLER2DMSARRAY", "ISAMPLER2DMSARRAY", + "USAMPLER2DMSARRAY", "SAMPLEREXTERNALOES", "SAMPLEREXTERNAL2DY2YEXT", + "ISAMPLER1DARRAY", "USAMPLER1D", "USAMPLER1DARRAY", "F16SAMPLER1D", + "F16SAMPLER2D", "F16SAMPLER3D", "F16SAMPLER2DRECT", "F16SAMPLERCUBE", + "F16SAMPLER1DARRAY", "F16SAMPLER2DARRAY", "F16SAMPLERCUBEARRAY", + "F16SAMPLERBUFFER", "F16SAMPLER2DMS", "F16SAMPLER2DMSARRAY", + "F16SAMPLER1DSHADOW", "F16SAMPLER2DSHADOW", "F16SAMPLER1DARRAYSHADOW", + "F16SAMPLER2DARRAYSHADOW", "F16SAMPLER2DRECTSHADOW", + "F16SAMPLERCUBESHADOW", "F16SAMPLERCUBEARRAYSHADOW", "IMAGE1D", + "IIMAGE1D", "UIMAGE1D", "IMAGE2D", "IIMAGE2D", "UIMAGE2D", "IMAGE3D", + "IIMAGE3D", "UIMAGE3D", "IMAGE2DRECT", "IIMAGE2DRECT", "UIMAGE2DRECT", + "IMAGECUBE", "IIMAGECUBE", "UIMAGECUBE", "IMAGEBUFFER", "IIMAGEBUFFER", + "UIMAGEBUFFER", "IMAGE1DARRAY", "IIMAGE1DARRAY", "UIMAGE1DARRAY", + "IMAGE2DARRAY", "IIMAGE2DARRAY", "UIMAGE2DARRAY", "IMAGECUBEARRAY", + "IIMAGECUBEARRAY", "UIMAGECUBEARRAY", "IMAGE2DMS", "IIMAGE2DMS", + "UIMAGE2DMS", "IMAGE2DMSARRAY", "IIMAGE2DMSARRAY", "UIMAGE2DMSARRAY", + "F16IMAGE1D", "F16IMAGE2D", "F16IMAGE3D", "F16IMAGE2DRECT", + "F16IMAGECUBE", "F16IMAGE1DARRAY", "F16IMAGE2DARRAY", + "F16IMAGECUBEARRAY", "F16IMAGEBUFFER", "F16IMAGE2DMS", + "F16IMAGE2DMSARRAY", "I64IMAGE1D", "U64IMAGE1D", "I64IMAGE2D", + "U64IMAGE2D", "I64IMAGE3D", "U64IMAGE3D", "I64IMAGE2DRECT", + "U64IMAGE2DRECT", "I64IMAGECUBE", "U64IMAGECUBE", "I64IMAGEBUFFER", + "U64IMAGEBUFFER", "I64IMAGE1DARRAY", "U64IMAGE1DARRAY", + "I64IMAGE2DARRAY", "U64IMAGE2DARRAY", "I64IMAGECUBEARRAY", + "U64IMAGECUBEARRAY", "I64IMAGE2DMS", "U64IMAGE2DMS", "I64IMAGE2DMSARRAY", + "U64IMAGE2DMSARRAY", "TEXTURECUBEARRAY", "ITEXTURECUBEARRAY", + "UTEXTURECUBEARRAY", "TEXTURE1D", "ITEXTURE1D", "UTEXTURE1D", + "TEXTURE1DARRAY", "ITEXTURE1DARRAY", "UTEXTURE1DARRAY", "TEXTURE2DRECT", + "ITEXTURE2DRECT", "UTEXTURE2DRECT", "TEXTUREBUFFER", "ITEXTUREBUFFER", + "UTEXTUREBUFFER", "TEXTURE2DMS", "ITEXTURE2DMS", "UTEXTURE2DMS", + "TEXTURE2DMSARRAY", "ITEXTURE2DMSARRAY", "UTEXTURE2DMSARRAY", + "F16TEXTURE1D", "F16TEXTURE2D", "F16TEXTURE3D", "F16TEXTURE2DRECT", + "F16TEXTURECUBE", "F16TEXTURE1DARRAY", "F16TEXTURE2DARRAY", + "F16TEXTURECUBEARRAY", "F16TEXTUREBUFFER", "F16TEXTURE2DMS", + "F16TEXTURE2DMSARRAY", "SUBPASSINPUT", "SUBPASSINPUTMS", "ISUBPASSINPUT", + "ISUBPASSINPUTMS", "USUBPASSINPUT", "USUBPASSINPUTMS", "F16SUBPASSINPUT", + "F16SUBPASSINPUTMS", "SPIRV_INSTRUCTION", "SPIRV_EXECUTION_MODE", + "SPIRV_EXECUTION_MODE_ID", "SPIRV_DECORATE", "SPIRV_DECORATE_ID", + "SPIRV_DECORATE_STRING", "SPIRV_TYPE", "SPIRV_STORAGE_CLASS", + "SPIRV_BY_REFERENCE", "SPIRV_LITERAL", "ATTACHMENTEXT", "IATTACHMENTEXT", + "UATTACHMENTEXT", "LEFT_OP", "RIGHT_OP", "INC_OP", "DEC_OP", "LE_OP", + "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "XOR_OP", "MUL_ASSIGN", + "DIV_ASSIGN", "ADD_ASSIGN", "MOD_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN", + "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "SUB_ASSIGN", "STRING_LITERAL", + "LEFT_PAREN", "RIGHT_PAREN", "LEFT_BRACKET", "RIGHT_BRACKET", + "LEFT_BRACE", "RIGHT_BRACE", "DOT", "COMMA", "COLON", "EQUAL", + "SEMICOLON", "BANG", "DASH", "TILDE", "PLUS", "STAR", "SLASH", "PERCENT", + "LEFT_ANGLE", "RIGHT_ANGLE", "VERTICAL_BAR", "CARET", "AMPERSAND", + "QUESTION", "INVARIANT", "HIGH_PRECISION", "MEDIUM_PRECISION", + "LOW_PRECISION", "PRECISION", "PACKED", "RESOURCE", "SUPERP", + "FLOATCONSTANT", "INTCONSTANT", "UINTCONSTANT", "BOOLCONSTANT", + "IDENTIFIER", "TYPE_NAME", "CENTROID", "IN", "OUT", "INOUT", "STRUCT", + "VOID", "WHILE", "BREAK", "CONTINUE", "DO", "ELSE", "FOR", "IF", + "DISCARD", "RETURN", "SWITCH", "CASE", "DEFAULT", "TERMINATE_INVOCATION", + "TERMINATE_RAY", "IGNORE_INTERSECTION", "UNIFORM", "SHARED", "BUFFER", + "TILEIMAGEEXT", "FLAT", "SMOOTH", "LAYOUT", "DOUBLECONSTANT", + "INT16CONSTANT", "UINT16CONSTANT", "FLOAT16CONSTANT", "INT32CONSTANT", + "UINT32CONSTANT", "INT64CONSTANT", "UINT64CONSTANT", "SUBROUTINE", + "DEMOTE", "FUNCTION", "PAYLOADNV", "PAYLOADINNV", "HITATTRNV", + "CALLDATANV", "CALLDATAINNV", "PAYLOADEXT", "PAYLOADINEXT", "HITATTREXT", + "CALLDATAEXT", "CALLDATAINEXT", "PATCH", "SAMPLE", "NONUNIFORM", + "COHERENT", "VOLATILE", "RESTRICT", "READONLY", "WRITEONLY", + "NONTEMPORAL", "DEVICECOHERENT", "QUEUEFAMILYCOHERENT", + "WORKGROUPCOHERENT", "SUBGROUPCOHERENT", "NONPRIVATE", + "SHADERCALLCOHERENT", "NOPERSPECTIVE", "EXPLICITINTERPAMD", + "PERVERTEXEXT", "PERVERTEXNV", "PERPRIMITIVENV", "PERVIEWNV", + "PERTASKNV", "PERPRIMITIVEEXT", "TASKPAYLOADWORKGROUPEXT", "PRECISE", + "$accept", "variable_identifier", "primary_expression", + "postfix_expression", "integer_expression", "function_call", + "function_call_or_method", "function_call_generic", + "function_call_header_no_parameters", + "function_call_header_with_parameters", "function_call_header", + "function_identifier", "unary_expression", "unary_operator", + "multiplicative_expression", "additive_expression", "shift_expression", + "relational_expression", "equality_expression", "and_expression", + "exclusive_or_expression", "inclusive_or_expression", + "logical_and_expression", "logical_xor_expression", + "logical_or_expression", "conditional_expression", "$@1", + "assignment_expression", "assignment_operator", "expression", + "constant_expression", "declaration", "block_structure", "$@2", + "identifier_list", "function_prototype", "function_declarator", + "function_header_with_parameters", "function_header", + "parameter_declarator", "parameter_declaration", + "parameter_type_specifier", "init_declarator_list", "single_declaration", + "fully_specified_type", "invariant_qualifier", "interpolation_qualifier", + "layout_qualifier", "layout_qualifier_id_list", "layout_qualifier_id", + "precise_qualifier", "type_qualifier", "single_type_qualifier", + "storage_qualifier", "non_uniform_qualifier", "type_name_list", + "type_specifier", "array_specifier", "type_parameter_specifier_opt", + "type_parameter_specifier", "type_parameter_specifier_list", + "type_specifier_nonarray", "precision_qualifier", "struct_specifier", + "$@3", "$@4", "struct_declaration_list", "struct_declaration", + "struct_declarator_list", "struct_declarator", "initializer", + "initializer_list", "declaration_statement", "statement", + "simple_statement", "demote_statement", "compound_statement", "$@5", + "$@6", "statement_no_new_scope", "statement_scoped", "$@7", "$@8", + "compound_statement_no_new_scope", "statement_list", + "expression_statement", "selection_statement", + "selection_statement_nonattributed", "selection_rest_statement", + "condition", "switch_statement", "switch_statement_nonattributed", "$@9", + "switch_statement_list", "case_label", "iteration_statement", + "iteration_statement_nonattributed", "$@10", "$@11", "$@12", + "for_init_statement", "conditionopt", "for_rest_statement", + "jump_statement", "translation_unit", "external_declaration", + "function_definition", "$@13", "attribute", "attribute_list", + "single_attribute", "spirv_requirements_list", + "spirv_requirements_parameter", "spirv_extension_list", + "spirv_capability_list", "spirv_execution_mode_qualifier", + "spirv_execution_mode_parameter_list", "spirv_execution_mode_parameter", + "spirv_execution_mode_id_parameter_list", + "spirv_storage_class_qualifier", "spirv_decorate_qualifier", + "spirv_decorate_parameter_list", "spirv_decorate_parameter", + "spirv_decorate_id_parameter_list", "spirv_decorate_id_parameter", + "spirv_decorate_string_parameter_list", "spirv_type_specifier", + "spirv_type_parameter_list", "spirv_type_parameter", + "spirv_instruction_qualifier", "spirv_instruction_qualifier_list", + "spirv_instruction_qualifier_id", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-878) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-704) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 4729, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -305, -273, -212, -207, -194, + -121, -113, -108, -878, -878, -878, -878, -878, -320, -878, + -878, -878, -878, -878, -196, -878, -878, -878, -878, -878, + -333, -878, -878, -878, -878, -878, -878, -878, -99, -91, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -337, -240, -67, + -64, 8026, -278, -878, -71, -878, -878, -878, -878, 5671, + -878, -878, -878, -878, -47, -878, -878, 961, -878, -878, + 8026, -29, -878, -878, -878, 6142, -53, -134, -133, -128, + -126, -118, -53, -117, -49, 12528, -878, -16, -360, -46, + -878, -316, -878, -13, -10, 8026, -878, -878, -878, 8026, + -43, -42, -878, -313, -878, -258, -878, -878, 11184, -7, + -878, -878, -878, -5, -39, 8026, -878, -12, -9, -8, + -878, -264, -878, -253, -6, -3, -2, -1, -251, 1, + 3, 4, 6, 7, 8, -233, 11, 10, 16, -310, + -878, 13, 8026, -878, 15, -878, -230, -878, -878, -226, + 9392, -878, -274, 1432, -878, -878, -878, -878, -878, -7, + -311, -878, 9840, -271, -878, -35, -878, -238, 11184, 11184, + -878, 11184, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -261, -878, -878, -878, 17, -225, 11632, 19, -878, + 11184, -878, 21, -284, 20, -10, 25, -878, -325, -53, + -878, -34, -878, -321, 24, -116, 11184, -112, -878, -151, + -111, -168, -107, 28, -100, -53, -878, 12080, -878, -98, + 11184, 29, -49, -878, 8026, -27, 6613, -878, 8026, 11184, + -878, -360, -878, 14, -878, -878, -146, -153, -101, -314, + -163, -15, 23, 18, 42, 45, -307, 31, -878, 10288, + -878, 30, -878, -878, 36, 27, 38, -878, 40, 41, + 39, 10736, 52, 11184, 46, 43, 56, 59, 61, -231, + -878, -878, -68, -878, -240, 72, 21, -878, -878, -878, + -878, -878, 1903, -878, -878, -878, -878, -878, -878, -878, + -878, -878, 5200, 20, 9840, -262, 8496, -878, -878, 9840, + 8026, -878, 44, -878, -878, -878, -221, -878, -878, 11184, + 49, -878, -878, 11184, 55, -878, -878, -878, 11184, -878, + -878, -878, -324, -878, -878, -218, 65, -878, -878, -878, + -878, -878, -878, -216, -878, -209, -878, -878, -200, 70, + -878, -878, -878, -878, -199, -878, -198, -878, -878, -878, + -878, -878, -197, -878, 71, -878, -190, 74, -188, 65, + -878, -317, -161, -878, 78, 81, -878, -878, -27, -7, + -66, -878, -878, -878, 7084, -878, -878, -878, 11184, 11184, + 11184, 11184, 11184, 11184, 11184, 11184, 11184, 11184, 11184, 11184, + 11184, 11184, 11184, 11184, 11184, 11184, 11184, -878, -878, -878, + 83, -878, 2374, -878, -878, -878, 2374, -878, 11184, -878, + -878, -41, 11184, -80, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + 11184, 11184, -878, -878, -878, -878, -878, -878, -878, 9840, + -878, -878, -119, -878, 7555, -878, -878, 85, 84, -878, + -878, -878, -878, -878, -252, -131, -878, -318, -878, -321, + -878, -321, -878, 11184, 11184, -878, -151, -878, -151, -878, + -168, -168, -878, 91, 28, -878, 12080, -878, 11184, -878, + -878, -37, 20, -27, -878, -878, -878, -878, -878, -146, + -146, -153, -153, -101, -101, -101, -101, -314, -314, -163, + -15, 23, 18, 42, 45, 11184, -878, 2374, 4258, 48, + 3787, -159, -878, -157, -878, -878, -878, -878, -878, 8944, + -878, -878, -878, 94, -878, 64, -878, -156, -878, -154, + -878, -152, -878, -149, -878, -147, -145, -878, -878, -878, + -33, 89, 84, 62, 99, 101, -878, -878, 4258, 98, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, 11184, -878, 95, 2845, 11184, -878, 97, 104, 60, + 105, 3316, -878, 106, -878, 9840, -878, -878, -878, -144, + 11184, 2845, 98, -878, -878, 2374, -878, 102, 84, -878, + -878, 2374, 108, -878, -878 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int16 yydefact[] = +{ + 0, 168, 226, 224, 225, 223, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 227, 228, 229, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 356, 357, 358, 359, 360, 361, 362, 382, 383, 384, + 385, 386, 387, 388, 397, 410, 411, 398, 399, 401, + 400, 402, 403, 404, 405, 406, 407, 408, 409, 177, + 178, 252, 253, 254, 251, 255, 262, 263, 260, 261, + 258, 259, 256, 257, 288, 289, 290, 300, 301, 302, + 285, 286, 287, 297, 298, 299, 282, 283, 284, 294, + 295, 296, 279, 280, 281, 291, 292, 293, 264, 265, + 266, 303, 304, 305, 267, 268, 269, 270, 271, 272, + 315, 316, 317, 273, 274, 275, 327, 328, 329, 276, + 277, 278, 339, 340, 341, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 330, 331, 332, 333, 334, 335, 336, + 337, 338, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 354, 351, 352, 353, 538, 539, 540, 541, 545, + 547, 182, 542, 543, 366, 367, 390, 393, 355, 364, + 365, 381, 363, 412, 413, 416, 417, 418, 420, 421, + 422, 424, 425, 426, 428, 429, 525, 526, 389, 391, + 392, 368, 369, 370, 414, 371, 375, 376, 379, 419, + 423, 427, 372, 373, 377, 378, 415, 374, 380, 459, + 461, 462, 463, 465, 466, 467, 469, 470, 471, 473, + 474, 475, 477, 478, 479, 481, 482, 483, 485, 486, + 487, 489, 490, 491, 493, 494, 495, 497, 498, 499, + 501, 502, 460, 464, 468, 472, 476, 484, 488, 492, + 480, 496, 500, 503, 504, 505, 506, 507, 508, 509, + 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, + 520, 521, 522, 523, 524, 394, 395, 396, 430, 439, + 441, 435, 440, 442, 443, 445, 446, 447, 449, 450, + 451, 453, 454, 455, 457, 458, 431, 432, 433, 444, + 434, 436, 437, 438, 448, 452, 456, 530, 531, 534, + 535, 536, 537, 532, 533, 0, 0, 0, 0, 0, + 0, 0, 0, 166, 167, 527, 528, 529, 0, 640, + 137, 550, 551, 552, 0, 549, 172, 170, 171, 169, + 0, 222, 173, 175, 176, 174, 139, 138, 0, 204, + 544, 184, 186, 181, 188, 190, 185, 187, 183, 189, + 191, 179, 180, 207, 192, 199, 200, 201, 202, 203, + 193, 194, 195, 196, 197, 198, 140, 141, 143, 142, + 144, 146, 147, 145, 206, 154, 639, 0, 641, 0, + 114, 113, 0, 125, 130, 161, 160, 158, 162, 0, + 155, 157, 163, 135, 217, 159, 548, 0, 636, 638, + 0, 0, 164, 165, 546, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 555, 0, 0, 0, + 99, 0, 94, 0, 109, 0, 121, 115, 123, 0, + 124, 0, 97, 131, 102, 0, 156, 136, 0, 210, + 216, 1, 637, 0, 0, 0, 96, 0, 0, 0, + 648, 0, 706, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 646, 0, + 644, 0, 0, 553, 151, 153, 0, 149, 208, 0, + 0, 100, 0, 0, 642, 110, 116, 120, 122, 118, + 126, 117, 0, 132, 105, 0, 103, 0, 0, 0, + 9, 0, 43, 42, 44, 41, 5, 6, 7, 8, + 2, 16, 14, 15, 17, 10, 11, 12, 13, 3, + 18, 37, 20, 25, 26, 0, 0, 30, 0, 220, + 0, 36, 219, 0, 211, 111, 0, 95, 0, 0, + 704, 0, 656, 0, 0, 0, 0, 0, 673, 0, + 0, 0, 0, 0, 0, 0, 698, 0, 671, 0, + 0, 0, 0, 98, 0, 0, 0, 557, 0, 0, + 148, 0, 205, 0, 212, 45, 49, 52, 55, 60, + 63, 65, 67, 69, 71, 73, 75, 0, 34, 0, + 101, 584, 593, 597, 0, 0, 0, 618, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, + 78, 91, 0, 571, 0, 163, 135, 574, 595, 573, + 581, 572, 0, 575, 576, 599, 577, 606, 578, 579, + 614, 580, 0, 119, 0, 127, 0, 565, 134, 0, + 0, 107, 0, 104, 38, 39, 0, 22, 23, 0, + 0, 28, 27, 0, 222, 31, 33, 40, 0, 218, + 112, 708, 0, 709, 649, 0, 0, 707, 668, 664, + 665, 666, 667, 0, 662, 0, 93, 669, 0, 0, + 683, 684, 685, 686, 0, 681, 0, 690, 691, 692, + 693, 689, 0, 687, 0, 694, 0, 0, 0, 2, + 702, 217, 0, 700, 0, 0, 643, 645, 0, 563, + 0, 561, 556, 558, 0, 152, 150, 209, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 76, 213, 214, + 0, 583, 0, 616, 629, 628, 0, 620, 0, 632, + 630, 0, 0, 0, 613, 633, 634, 635, 582, 81, + 82, 84, 83, 86, 87, 88, 89, 90, 85, 80, + 0, 0, 598, 594, 596, 600, 607, 615, 129, 0, + 568, 569, 0, 133, 0, 108, 4, 0, 24, 21, + 32, 221, 652, 654, 0, 0, 705, 0, 658, 0, + 657, 0, 660, 0, 0, 675, 0, 674, 0, 677, + 0, 0, 679, 0, 0, 699, 0, 696, 0, 672, + 647, 0, 564, 0, 559, 554, 46, 47, 48, 51, + 50, 53, 54, 58, 59, 56, 57, 61, 62, 64, + 66, 68, 70, 72, 74, 0, 215, 585, 0, 0, + 0, 0, 631, 0, 612, 79, 92, 128, 566, 0, + 106, 19, 650, 0, 651, 0, 663, 0, 670, 0, + 682, 0, 688, 0, 695, 0, 0, 701, 560, 562, + 0, 0, 604, 0, 0, 0, 623, 622, 625, 591, + 608, 567, 570, 653, 655, 659, 661, 676, 678, 680, + 697, 0, 586, 0, 0, 0, 624, 0, 0, 603, + 0, 0, 601, 0, 77, 0, 588, 617, 587, 0, + 626, 0, 591, 590, 592, 610, 605, 0, 627, 621, + 602, 611, 0, 619, 609 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -878, -557, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -431, -878, -403, -397, -489, -409, -275, -279, + -272, -276, -270, -277, -878, -494, -878, -497, -878, -517, + -548, 9, -878, -878, -878, 5, -399, -878, -878, 33, + 35, 34, -878, -878, -417, -878, -878, -878, -878, -105, + -878, -400, -381, -878, 12, -878, 0, -430, -878, -878, + -878, -570, 140, -878, -878, -878, -564, -567, -241, -355, + -643, -878, -380, -625, -877, -878, -441, -878, -878, -448, + -447, -878, -878, 51, -740, -374, -878, -155, -878, -410, + -878, -150, -878, -878, -878, -878, -143, -878, -878, -878, + -878, -878, -878, -878, -878, 82, -878, -878, 2, -878, + -82, -232, -385, -878, -878, -878, -315, -312, -323, -878, + -878, -309, -308, -326, -319, -322, -878, -306, -328, -878, + -398, -551 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + 0, 539, 540, 541, 807, 542, 543, 544, 545, 546, + 547, 548, 629, 550, 596, 597, 598, 599, 600, 601, + 602, 603, 604, 605, 606, 630, 865, 631, 790, 632, + 720, 633, 397, 660, 517, 634, 399, 400, 401, 446, + 447, 448, 402, 403, 404, 405, 406, 407, 496, 497, + 408, 409, 410, 411, 551, 499, 608, 502, 459, 460, + 553, 414, 415, 416, 588, 492, 586, 587, 730, 731, + 658, 802, 637, 638, 639, 640, 641, 762, 901, 937, + 929, 930, 931, 938, 642, 643, 644, 645, 932, 904, + 646, 647, 933, 952, 648, 649, 650, 868, 766, 870, + 908, 927, 928, 651, 417, 418, 419, 443, 652, 489, + 490, 469, 470, 814, 815, 421, 693, 694, 698, 422, + 423, 704, 705, 712, 713, 716, 424, 722, 723, 425, + 471, 472 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 413, 449, 420, 464, 666, 398, 607, 721, 464, 396, + 687, 798, 412, 801, 711, 657, 803, 794, 697, 733, + 465, 463, 867, 513, 734, 465, 687, 549, 456, 554, + 745, 746, 725, 436, 485, 440, 681, 812, 494, 682, + 688, 735, 756, 681, 434, 449, -703, 936, 500, 511, + 675, 500, -703, 500, 944, 581, 501, 426, 512, 582, + 654, 441, 495, 458, 936, 437, 747, 748, 456, 595, + 683, 813, 696, 689, 690, 691, 692, 683, 757, 653, + 655, 667, 668, 696, 456, 678, 696, 664, 665, 427, + 609, 451, 584, 609, 452, 696, 804, 679, 610, 560, + 659, -35, 609, 669, 771, 561, 773, 670, 514, 799, + 562, 515, 568, 882, 516, 760, 563, 883, 569, 677, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, + 576, 662, 442, 590, 663, 595, 577, 592, 672, 591, + 789, 869, 806, 593, 673, 816, 595, 818, 791, 595, + 428, 561, 808, 819, 820, 429, 877, 657, 595, 657, + 821, 685, 657, 822, 825, 827, 829, 733, 430, 823, + 826, 828, 830, 832, 684, 835, 810, 718, 595, 833, + 684, 836, 684, 749, 750, 684, 584, 684, 584, 684, + 684, 341, 342, 343, 684, 475, 477, 479, 481, 483, + 484, 487, 837, 456, 909, 951, 910, 915, 838, 916, + 791, 917, 791, 819, 918, 823, 919, 826, 920, 947, + 830, 741, 833, 742, 838, 791, 707, 708, 709, 710, + 530, 738, 739, 740, 884, 464, 912, 733, 885, 743, + 744, 431, 794, 700, 701, 702, 703, 811, 878, 432, + 879, 871, 465, 463, 433, 873, 853, 854, 855, 856, + 584, 473, 476, 438, 474, 474, 721, 478, 721, 480, + 474, 439, 474, 711, 711, 888, 697, 482, 486, 695, + 474, 474, 474, 699, 706, 687, 474, 474, 714, 791, + 874, 474, 946, 875, 876, 717, 444, 724, 474, 842, + 474, 791, 657, 843, 792, 445, 844, 846, 847, 848, + 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, + 595, 595, 595, 595, 595, 595, 794, 453, 791, 696, + 696, 872, 843, 458, 584, 898, 791, 921, 849, 850, + 857, 858, 696, 466, 696, 468, 851, 852, 900, 488, + 493, 902, 498, 503, 338, 509, 510, 500, 555, 556, + 557, 559, 558, 661, 686, 564, 565, 566, 567, 751, + 570, 729, 571, 572, 578, 573, 574, 575, 580, 579, + 671, 676, 657, -34, 609, 583, 589, 511, 682, 715, + 754, 902, 595, 595, 726, 755, 758, 761, 763, 764, + 753, 450, 767, 768, 584, 595, 752, 595, 939, 457, + 765, 769, 737, 412, 772, 775, 774, 413, -29, 420, + 413, 412, 398, 948, 934, 413, 396, 420, 776, 412, + 467, 777, 412, 778, -36, 491, 817, 412, 657, 824, + 831, 839, 805, 834, 840, 450, 505, 809, 866, 450, + 881, 903, 894, 791, 905, 913, 922, 412, 552, 914, + 923, 412, 924, 925, -589, 457, 935, 941, 465, 940, + 942, 611, 945, 860, 953, 954, 859, 412, 862, 864, + 506, 861, 507, 508, 435, 863, 736, 841, 899, 943, + 906, 903, 585, 949, 504, 950, 907, 795, 926, 462, + 727, 889, 796, 636, 412, 893, 887, 886, 465, 797, + 897, 892, 895, 0, 0, 635, 0, 0, 890, 891, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 896, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 680, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 728, 0, 585, 0, 585, 0, + 0, 0, 0, 0, 0, 0, 412, 0, 412, 0, + 412, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 636, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 413, 0, 635, 0, 0, 0, 0, 0, + 585, 0, 0, 0, 412, 0, 0, 0, 0, 0, + 0, 0, 412, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 585, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 412, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 636, 0, 0, 0, 636, 0, 0, 0, + 0, 0, 0, 0, 635, 0, 0, 0, 635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 585, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 412, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 636, 636, 0, + 636, 0, 420, 0, 0, 0, 0, 0, 0, 635, + 635, 0, 635, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 636, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 635, 0, 0, 0, 636, 0, 0, 0, 0, 0, + 0, 636, 0, 0, 0, 0, 635, 0, 0, 0, + 0, 636, 0, 635, 0, 636, 0, 0, 0, 0, + 0, 636, 0, 635, 0, 0, 0, 635, 0, 0, + 0, 461, 0, 635, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 338, 0, 0, 0, 0, + 0, 0, 0, 339, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 340, 341, 342, + 343, 344, 0, 0, 0, 0, 0, 0, 0, 0, + 345, 346, 347, 348, 349, 350, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 352, 353, 354, 355, 356, 357, 358, 0, + 0, 0, 0, 0, 0, 0, 0, 359, 0, 360, + 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, + 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, + 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 0, 0, 518, 519, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 520, 521, 0, 338, 0, 611, 612, + 0, 0, 0, 0, 613, 522, 523, 524, 525, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 340, 341, + 342, 343, 344, 0, 0, 0, 526, 527, 528, 529, + 530, 345, 346, 347, 348, 349, 350, 351, 614, 615, + 616, 617, 0, 618, 619, 620, 621, 622, 623, 624, + 625, 626, 627, 352, 353, 354, 355, 356, 357, 358, + 531, 532, 533, 534, 535, 536, 537, 538, 359, 628, + 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, 392, 393, 394, 395, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 0, 0, 518, 519, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 520, 521, 0, 338, 0, 611, + 793, 0, 0, 0, 0, 613, 522, 523, 524, 525, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 340, + 341, 342, 343, 344, 0, 0, 0, 526, 527, 528, + 529, 530, 345, 346, 347, 348, 349, 350, 351, 614, + 615, 616, 617, 0, 618, 619, 620, 621, 622, 623, + 624, 625, 626, 627, 352, 353, 354, 355, 356, 357, + 358, 531, 532, 533, 534, 535, 536, 537, 538, 359, + 628, 360, 361, 362, 363, 364, 365, 366, 367, 368, + 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, + 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, + 389, 390, 391, 392, 393, 394, 395, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 0, 0, 518, 519, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 520, 521, 0, 338, 0, + 611, 0, 0, 0, 0, 0, 613, 522, 523, 524, + 525, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 340, 341, 342, 343, 344, 0, 0, 0, 526, 527, + 528, 529, 530, 345, 346, 347, 348, 349, 350, 351, + 614, 615, 616, 617, 0, 618, 619, 620, 621, 622, + 623, 624, 625, 626, 627, 352, 353, 354, 355, 356, + 357, 358, 531, 532, 533, 534, 535, 536, 537, 538, + 359, 628, 360, 361, 362, 363, 364, 365, 366, 367, + 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 0, 0, 518, 519, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 520, 521, 0, 338, + 0, 503, 0, 0, 0, 0, 0, 613, 522, 523, + 524, 525, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 340, 341, 342, 343, 344, 0, 0, 0, 526, + 527, 528, 529, 530, 345, 346, 347, 348, 349, 350, + 351, 614, 615, 616, 617, 0, 618, 619, 620, 621, + 622, 623, 624, 625, 626, 627, 352, 353, 354, 355, + 356, 357, 358, 531, 532, 533, 534, 535, 536, 537, + 538, 359, 628, 360, 361, 362, 363, 364, 365, 366, + 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + 387, 388, 389, 390, 391, 392, 393, 394, 395, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 0, 0, 518, 519, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 520, 521, 0, + 338, 0, 0, 0, 0, 0, 0, 0, 613, 522, + 523, 524, 525, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 340, 341, 342, 343, 344, 0, 0, 0, + 526, 527, 528, 529, 530, 345, 346, 347, 348, 349, + 350, 351, 614, 615, 616, 617, 0, 618, 619, 620, + 621, 622, 623, 624, 625, 626, 627, 352, 353, 354, + 355, 356, 357, 358, 531, 532, 533, 534, 535, 536, + 537, 538, 359, 628, 360, 361, 362, 363, 364, 365, + 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 0, 0, 518, + 519, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 520, 521, + 0, 338, 0, 0, 0, 0, 0, 0, 0, 613, + 522, 523, 524, 525, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 340, 341, 342, 343, 344, 0, 0, + 0, 526, 527, 528, 529, 530, 345, 346, 347, 348, + 349, 350, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 352, 353, + 354, 355, 356, 357, 358, 531, 532, 533, 534, 535, + 536, 537, 538, 359, 0, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 0, 0, + 518, 519, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 520, + 521, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 522, 523, 524, 525, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 340, 341, 342, 343, 0, 0, + 0, 0, 526, 527, 528, 529, 530, 345, 346, 347, + 348, 349, 350, 351, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, + 353, 354, 355, 356, 357, 358, 531, 532, 533, 534, + 535, 536, 537, 538, 359, 0, 360, 361, 362, 363, + 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, + 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, + 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, + 329, 330, 331, 332, 333, 334, 335, 336, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 338, 0, 0, 0, 0, 0, 0, + 0, 339, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 340, 341, 342, 343, 344, + 0, 0, 0, 0, 0, 0, 0, 0, 345, 346, + 347, 348, 349, 350, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 352, 353, 354, 355, 356, 357, 358, 0, 0, 0, + 0, 0, 0, 0, 0, 359, 0, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, + 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, + 393, 394, 395, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 340, 341, 342, 343, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 345, + 346, 347, 348, 349, 350, 351, 614, 0, 0, 617, + 0, 618, 619, 0, 0, 622, 0, 0, 0, 0, + 0, 352, 353, 354, 355, 356, 357, 358, 0, 0, + 0, 0, 0, 0, 0, 0, 359, 0, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 392, 393, 394, 395, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 0, 0, + 0, 328, 329, 330, 331, 332, 333, 334, 335, 336, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 454, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 340, 341, 342, + 343, 0, 0, 0, 0, 0, 0, 0, 0, 455, + 345, 346, 347, 348, 349, 350, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 352, 353, 354, 355, 356, 357, 358, 0, + 0, 0, 0, 0, 0, 0, 0, 359, 0, 360, + 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, + 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, + 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 338, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 340, 341, + 342, 343, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 345, 346, 347, 348, 349, 350, 351, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 352, 353, 354, 355, 356, 357, 358, + 0, 0, 0, 0, 0, 0, 0, 0, 359, 0, + 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, 392, 393, 394, 395, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 0, 0, 0, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 732, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 340, + 341, 342, 343, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 345, 346, 347, 348, 349, 350, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 352, 353, 354, 355, 356, 357, + 358, 0, 0, 0, 0, 0, 0, 0, 0, 359, + 0, 360, 361, 362, 363, 364, 365, 366, 367, 368, + 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, + 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, + 389, 390, 391, 392, 393, 394, 395, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 845, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 340, 341, 342, 343, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 345, 346, 347, 348, 349, 350, 351, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 352, 353, 354, 355, 356, + 357, 358, 0, 0, 0, 0, 0, 0, 0, 0, + 359, 0, 360, 361, 362, 363, 364, 365, 366, 367, + 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 0, 0, 0, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 880, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 340, 341, 342, 343, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 345, 346, 347, 348, 349, 350, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 352, 353, 354, 355, + 356, 357, 358, 0, 0, 0, 0, 0, 0, 0, + 0, 359, 0, 360, 361, 362, 363, 364, 365, 366, + 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + 387, 388, 389, 390, 391, 392, 393, 394, 395, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 340, 341, 342, 343, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 345, 346, 347, 348, 349, + 350, 351, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 352, 353, 354, + 355, 356, 357, 358, 0, 0, 0, 0, 0, 0, + 0, 0, 359, 0, 360, 361, 362, 363, 364, 365, + 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 0, 0, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 0, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 0, 0, 331, + 0, 0, 0, 335, 336, 337, 0, 0, 518, 519, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 520, 521, 0, + 0, 0, 656, 800, 0, 0, 0, 0, 0, 522, + 523, 524, 525, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 526, 527, 528, 529, 530, 345, 0, 0, 0, 0, + 350, 351, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 531, 532, 533, 534, 535, 536, + 537, 538, 0, 0, 360, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 373, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 0, 0, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 0, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 0, 0, 331, 0, 0, + 0, 335, 336, 337, 0, 0, 518, 519, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 520, 521, 0, 0, 0, + 656, 911, 0, 0, 0, 0, 0, 522, 523, 524, + 525, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 526, 527, + 528, 529, 530, 345, 0, 0, 0, 0, 350, 351, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 531, 532, 533, 534, 535, 536, 537, 538, + 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 373, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 0, 0, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 0, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 0, 0, 331, 0, 0, 0, 335, + 336, 337, 0, 0, 518, 519, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 520, 521, 0, 0, 594, 0, 0, + 0, 0, 0, 0, 0, 522, 523, 524, 525, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 526, 527, 528, 529, + 530, 345, 0, 0, 0, 0, 350, 351, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 531, 532, 533, 534, 535, 536, 537, 538, 0, 0, + 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 373, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 0, 0, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 0, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 0, 0, 331, 0, 0, 0, 335, 336, 337, + 0, 0, 518, 519, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 520, 521, 0, 0, 0, 656, 0, 0, 0, + 0, 0, 0, 522, 523, 524, 525, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 526, 527, 528, 529, 530, 345, + 0, 0, 0, 0, 350, 351, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 531, 532, + 533, 534, 535, 536, 537, 538, 0, 0, 360, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 373, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 0, + 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 0, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 0, + 0, 331, 0, 0, 0, 335, 336, 337, 0, 0, + 518, 519, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 520, + 521, 0, 0, 759, 0, 0, 0, 0, 0, 0, + 0, 522, 523, 524, 525, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 526, 527, 528, 529, 530, 345, 0, 0, + 0, 0, 350, 351, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 531, 532, 533, 534, + 535, 536, 537, 538, 0, 0, 360, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 373, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 0, 0, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 0, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 0, 0, 0, 0, 0, 0, 331, + 0, 0, 0, 335, 336, 337, 0, 0, 518, 519, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 520, 521, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 770, 522, + 523, 524, 525, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 526, 527, 528, 529, 530, 345, 0, 0, 0, 0, + 350, 351, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 531, 532, 533, 534, 535, 536, + 537, 538, 0, 0, 360, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 373, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 0, 0, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 0, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 0, 0, 0, 0, 0, 0, 331, 0, 0, + 0, 335, 336, 337, 0, 0, 518, 519, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 520, 521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 522, 523, 524, + 525, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 526, 527, + 528, 529, 530, 345, 0, 0, 0, 0, 350, 351, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 531, 532, 533, 534, 535, 536, 537, 538, + 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 373, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 0, 0, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 0, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 0, + 0, 0, 0, 0, 0, 331, 0, 0, 0, 335, + 336, 337, 0, 0, 518, 519, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 520, 521, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 522, 523, 524, 525, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 526, 527, 528, 529, + 530, 345, 0, 0, 0, 0, 350, 674, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 531, 532, 533, 534, 535, 536, 537, 538, 0, 0, + 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 373, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 0, 0, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 0, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 0, 0, 0, + 0, 0, 0, 331, 0, 0, 0, 335, 336, 337, + 0, 0, 518, 519, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 520, 521, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 522, 523, 524, 525, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 526, 527, 528, 529, 719, 345, + 0, 0, 0, 0, 350, 351, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 531, 532, + 533, 534, 535, 536, 537, 538, 0, 0, 360, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 373, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 0, + 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 0, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 0, 0, 0, 0, 0, + 0, 331, 0, 0, 0, 335, 336, 337, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 345, 0, 0, + 0, 0, 350, 351, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 360 +}; + +static const yytype_int16 yycheck[] = +{ + 0, 401, 0, 420, 521, 0, 500, 577, 425, 0, + 561, 654, 0, 656, 571, 512, 659, 642, 566, 586, + 420, 420, 762, 453, 588, 425, 577, 458, 409, 459, + 344, 345, 580, 366, 432, 372, 361, 361, 398, 364, + 361, 589, 349, 361, 364, 445, 363, 924, 364, 362, + 547, 364, 369, 364, 931, 365, 372, 362, 371, 369, + 371, 398, 422, 380, 941, 398, 380, 381, 449, 500, + 395, 395, 566, 394, 395, 396, 397, 395, 385, 509, + 510, 342, 343, 577, 465, 369, 580, 518, 519, 362, + 364, 369, 492, 364, 372, 589, 660, 381, 372, 363, + 371, 362, 364, 364, 621, 369, 623, 368, 366, 371, + 363, 369, 363, 365, 372, 609, 369, 369, 369, 550, + 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, + 363, 369, 372, 363, 372, 566, 369, 363, 363, 369, + 371, 766, 363, 369, 369, 363, 577, 363, 369, 580, + 362, 369, 669, 369, 363, 362, 799, 654, 589, 656, + 369, 559, 659, 363, 363, 363, 363, 734, 362, 369, + 369, 369, 369, 363, 559, 363, 673, 575, 609, 369, + 565, 369, 567, 346, 347, 570, 586, 572, 588, 574, + 575, 387, 388, 389, 579, 427, 428, 429, 430, 431, + 432, 433, 363, 584, 363, 945, 363, 363, 369, 363, + 369, 363, 369, 369, 363, 369, 363, 369, 363, 363, + 369, 374, 369, 376, 369, 369, 394, 395, 396, 397, + 398, 377, 378, 379, 365, 652, 879, 804, 369, 340, + 341, 362, 867, 394, 395, 396, 397, 678, 367, 362, + 369, 768, 652, 652, 362, 772, 745, 746, 747, 748, + 660, 395, 395, 362, 398, 398, 836, 395, 838, 395, + 398, 362, 398, 830, 831, 823, 824, 395, 395, 395, + 398, 398, 398, 395, 395, 836, 398, 398, 395, 369, + 370, 398, 935, 790, 791, 395, 363, 395, 398, 729, + 398, 369, 799, 369, 372, 369, 372, 738, 739, 740, + 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, + 751, 752, 753, 754, 755, 756, 951, 398, 369, 823, + 824, 372, 369, 380, 734, 372, 369, 370, 741, 742, + 749, 750, 836, 372, 838, 398, 743, 744, 865, 398, + 366, 868, 398, 366, 364, 398, 398, 364, 363, 398, + 372, 369, 371, 398, 398, 371, 369, 369, 369, 384, + 369, 398, 369, 369, 363, 369, 369, 369, 362, 369, + 363, 362, 879, 362, 364, 372, 371, 362, 364, 361, + 348, 908, 823, 824, 365, 350, 365, 367, 362, 372, + 382, 401, 362, 362, 804, 836, 383, 838, 925, 409, + 372, 372, 398, 401, 362, 372, 370, 417, 363, 417, + 420, 409, 417, 940, 921, 425, 417, 425, 372, 417, + 425, 372, 420, 372, 362, 435, 371, 425, 935, 369, + 369, 363, 398, 369, 363, 445, 444, 398, 365, 449, + 365, 868, 361, 369, 406, 361, 367, 445, 458, 395, + 398, 449, 363, 362, 366, 465, 371, 363, 868, 372, + 410, 366, 366, 752, 372, 367, 751, 465, 754, 756, + 445, 753, 449, 449, 344, 755, 591, 728, 843, 930, + 870, 908, 492, 941, 443, 942, 870, 652, 908, 417, + 582, 824, 652, 503, 492, 831, 821, 819, 908, 652, + 838, 830, 834, -1, -1, 503, -1, -1, 826, 828, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 836, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 555, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 584, -1, 586, -1, 588, -1, + -1, -1, -1, -1, -1, -1, 584, -1, 586, -1, + 588, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 642, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 652, -1, 642, -1, -1, -1, -1, -1, + 660, -1, -1, -1, 652, -1, -1, -1, -1, -1, + -1, -1, 660, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 734, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 734, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 762, -1, -1, -1, 766, -1, -1, -1, + -1, -1, -1, -1, 762, -1, -1, -1, 766, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 804, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 804, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 867, 868, -1, + 870, -1, 870, -1, -1, -1, -1, -1, -1, 867, + 868, -1, 870, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 908, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 908, -1, -1, -1, 924, -1, -1, -1, -1, -1, + -1, 931, -1, -1, -1, -1, 924, -1, -1, -1, + -1, 941, -1, 931, -1, 945, -1, -1, -1, -1, + -1, 951, -1, 941, -1, -1, -1, 945, -1, -1, + -1, 0, -1, 951, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, + 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 364, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 386, 387, 388, + 389, 390, -1, -1, -1, -1, -1, -1, -1, -1, + 399, 400, 401, 402, 403, 404, 405, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 421, 422, 423, 424, 425, 426, 427, -1, + -1, -1, -1, -1, -1, -1, -1, 436, -1, 438, + 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, + 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, + 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, + 469, 470, 471, 472, 473, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, -1, -1, 342, 343, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 361, 362, -1, 364, -1, 366, 367, + -1, -1, -1, -1, 372, 373, 374, 375, 376, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 386, 387, + 388, 389, 390, -1, -1, -1, 394, 395, 396, 397, + 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, -1, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, + 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, + 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, + 337, 338, 339, -1, -1, 342, 343, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 361, 362, -1, 364, -1, 366, + 367, -1, -1, -1, -1, 372, 373, 374, 375, 376, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 386, + 387, 388, 389, 390, -1, -1, -1, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, -1, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, + 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, + 467, 468, 469, 470, 471, 472, 473, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, -1, -1, 342, 343, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 361, 362, -1, 364, -1, + 366, -1, -1, -1, -1, -1, 372, 373, 374, 375, + 376, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 386, 387, 388, 389, 390, -1, -1, -1, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, -1, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, + 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, + 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, + 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, + 466, 467, 468, 469, 470, 471, 472, 473, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, -1, -1, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 361, 362, -1, 364, + -1, 366, -1, -1, -1, -1, -1, 372, 373, 374, + 375, 376, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 386, 387, 388, 389, 390, -1, -1, -1, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, -1, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 361, 362, -1, + 364, -1, -1, -1, -1, -1, -1, -1, 372, 373, + 374, 375, 376, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 386, 387, 388, 389, 390, -1, -1, -1, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, -1, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, + 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, + 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, + 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, + 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, -1, -1, 342, + 343, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 361, 362, + -1, 364, -1, -1, -1, -1, -1, -1, -1, 372, + 373, 374, 375, 376, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 386, 387, 388, 389, 390, -1, -1, + -1, 394, 395, 396, 397, 398, 399, 400, 401, 402, + 403, 404, 405, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 421, 422, + 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, + 433, 434, 435, 436, -1, 438, 439, 440, 441, 442, + 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, + 473, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, -1, -1, -1, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, -1, -1, + 342, 343, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 361, + 362, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 373, 374, 375, 376, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 386, 387, 388, 389, -1, -1, + -1, -1, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 421, + 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, + 432, 433, 434, 435, 436, -1, 438, 439, 440, 441, + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, + 472, 473, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 338, 339, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 364, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 386, 387, 388, 389, 390, + -1, -1, -1, -1, -1, -1, -1, -1, 399, 400, + 401, 402, 403, 404, 405, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 421, 422, 423, 424, 425, 426, 427, -1, -1, -1, + -1, -1, -1, -1, -1, 436, -1, 438, 439, 440, + 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, + 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, + 471, 472, 473, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, -1, -1, -1, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 386, 387, 388, 389, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 399, + 400, 401, 402, 403, 404, 405, 406, -1, -1, 409, + -1, 411, 412, -1, -1, 415, -1, -1, -1, -1, + -1, 421, 422, 423, 424, 425, 426, 427, -1, -1, + -1, -1, -1, -1, -1, -1, 436, -1, 438, 439, + 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, + 470, 471, 472, 473, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 325, 326, -1, -1, + -1, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 386, 387, 388, + 389, -1, -1, -1, -1, -1, -1, -1, -1, 398, + 399, 400, 401, 402, 403, 404, 405, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 421, 422, 423, 424, 425, 426, 427, -1, + -1, -1, -1, -1, -1, -1, -1, 436, -1, 438, + 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, + 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, + 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, + 469, 470, 471, 472, 473, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, -1, + -1, -1, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 364, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 386, 387, + 388, 389, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 399, 400, 401, 402, 403, 404, 405, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 421, 422, 423, 424, 425, 426, 427, + -1, -1, -1, -1, -1, -1, -1, -1, 436, -1, + 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, + 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, + -1, -1, -1, 330, 331, 332, 333, 334, 335, 336, + 337, 338, 339, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 367, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 386, + 387, 388, 389, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 399, 400, 401, 402, 403, 404, 405, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 421, 422, 423, 424, 425, 426, + 427, -1, -1, -1, -1, -1, -1, -1, -1, 436, + -1, 438, 439, 440, 441, 442, 443, 444, 445, 446, + 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, + 467, 468, 469, 470, 471, 472, 473, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, -1, -1, -1, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 367, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 386, 387, 388, 389, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 399, 400, 401, 402, 403, 404, 405, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 421, 422, 423, 424, 425, + 426, 427, -1, -1, -1, -1, -1, -1, -1, -1, + 436, -1, 438, 439, 440, 441, 442, 443, 444, 445, + 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, + 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, + 466, 467, 468, 469, 470, 471, 472, 473, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, -1, -1, -1, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 367, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 386, 387, 388, 389, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 399, 400, 401, 402, 403, 404, + 405, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 421, 422, 423, 424, + 425, 426, 427, -1, -1, -1, -1, -1, -1, -1, + -1, 436, -1, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, -1, -1, -1, 330, 331, 332, 333, + 334, 335, 336, 337, 338, 339, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 386, 387, 388, 389, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 399, 400, 401, 402, 403, + 404, 405, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 421, 422, 423, + 424, 425, 426, 427, -1, -1, -1, -1, -1, -1, + -1, -1, 436, -1, 438, 439, 440, 441, 442, 443, + 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, + 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, + 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, -1, -1, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, -1, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, -1, -1, -1, -1, -1, -1, 333, + -1, -1, -1, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 361, 362, -1, + -1, -1, 366, 367, -1, -1, -1, -1, -1, 373, + 374, 375, 376, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 394, 395, 396, 397, 398, 399, -1, -1, -1, -1, + 404, 405, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 428, 429, 430, 431, 432, 433, + 434, 435, -1, -1, 438, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 451, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, -1, -1, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, -1, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, -1, -1, -1, -1, -1, -1, 333, -1, -1, + -1, 337, 338, 339, -1, -1, 342, 343, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 361, 362, -1, -1, -1, + 366, 367, -1, -1, -1, -1, -1, 373, 374, 375, + 376, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 394, 395, + 396, 397, 398, 399, -1, -1, -1, -1, 404, 405, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 428, 429, 430, 431, 432, 433, 434, 435, + -1, -1, 438, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 451, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, -1, -1, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, -1, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, -1, + -1, -1, -1, -1, -1, 333, -1, -1, -1, 337, + 338, 339, -1, -1, 342, 343, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 361, 362, -1, -1, 365, -1, -1, + -1, -1, -1, -1, -1, 373, 374, 375, 376, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 394, 395, 396, 397, + 398, 399, -1, -1, -1, -1, 404, 405, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 428, 429, 430, 431, 432, 433, 434, 435, -1, -1, + 438, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 451, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, -1, -1, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, -1, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, -1, -1, -1, + -1, -1, -1, 333, -1, -1, -1, 337, 338, 339, + -1, -1, 342, 343, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 361, 362, -1, -1, -1, 366, -1, -1, -1, + -1, -1, -1, 373, 374, 375, 376, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 394, 395, 396, 397, 398, 399, + -1, -1, -1, -1, 404, 405, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 428, 429, + 430, 431, 432, 433, 434, 435, -1, -1, 438, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 451, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, -1, + -1, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, -1, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, -1, -1, -1, -1, -1, + -1, 333, -1, -1, -1, 337, 338, 339, -1, -1, + 342, 343, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 361, + 362, -1, -1, 365, -1, -1, -1, -1, -1, -1, + -1, 373, 374, 375, 376, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 394, 395, 396, 397, 398, 399, -1, -1, + -1, -1, 404, 405, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 428, 429, 430, 431, + 432, 433, 434, 435, -1, -1, 438, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 451, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, -1, -1, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, -1, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, -1, -1, -1, -1, -1, -1, 333, + -1, -1, -1, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 361, 362, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 372, 373, + 374, 375, 376, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 394, 395, 396, 397, 398, 399, -1, -1, -1, -1, + 404, 405, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 428, 429, 430, 431, 432, 433, + 434, 435, -1, -1, 438, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 451, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, -1, -1, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, -1, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, -1, -1, -1, -1, -1, -1, 333, -1, -1, + -1, 337, 338, 339, -1, -1, 342, 343, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 361, 362, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 373, 374, 375, + 376, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 394, 395, + 396, 397, 398, 399, -1, -1, -1, -1, 404, 405, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 428, 429, 430, 431, 432, 433, 434, 435, + -1, -1, 438, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 451, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, -1, -1, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, -1, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, -1, + -1, -1, -1, -1, -1, 333, -1, -1, -1, 337, + 338, 339, -1, -1, 342, 343, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 361, 362, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 373, 374, 375, 376, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 394, 395, 396, 397, + 398, 399, -1, -1, -1, -1, 404, 405, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 428, 429, 430, 431, 432, 433, 434, 435, -1, -1, + 438, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 451, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, -1, -1, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, -1, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, -1, -1, -1, + -1, -1, -1, 333, -1, -1, -1, 337, 338, 339, + -1, -1, 342, 343, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 361, 362, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 373, 374, 375, 376, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 394, 395, 396, 397, 398, 399, + -1, -1, -1, -1, 404, 405, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 428, 429, + 430, 431, 432, 433, 434, 435, -1, -1, 438, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 451, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, -1, + -1, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, -1, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, -1, -1, -1, -1, -1, + -1, 333, -1, -1, -1, 337, 338, 339, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 399, -1, -1, + -1, -1, 404, 405, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 438 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int16 yystos[] = +{ + 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, 364, 372, + 386, 387, 388, 389, 390, 399, 400, 401, 402, 403, + 404, 405, 421, 422, 423, 424, 425, 426, 427, 436, + 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, + 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 505, 506, 509, 510, + 511, 512, 516, 517, 518, 519, 520, 521, 524, 525, + 526, 527, 528, 530, 535, 536, 537, 578, 579, 580, + 582, 589, 593, 594, 600, 603, 362, 362, 362, 362, + 362, 362, 362, 362, 364, 536, 366, 398, 362, 362, + 372, 398, 372, 581, 363, 369, 513, 514, 515, 525, + 530, 369, 372, 398, 372, 398, 526, 530, 380, 532, + 533, 0, 579, 510, 518, 525, 372, 509, 398, 585, + 586, 604, 605, 395, 398, 585, 395, 585, 395, 585, + 395, 585, 395, 585, 585, 604, 395, 585, 398, 583, + 584, 530, 539, 366, 398, 422, 522, 523, 398, 529, + 364, 372, 531, 366, 557, 582, 514, 513, 515, 398, + 398, 362, 371, 531, 366, 369, 372, 508, 342, 343, + 361, 362, 373, 374, 375, 376, 394, 395, 396, 397, + 398, 428, 429, 430, 431, 432, 433, 434, 435, 475, + 476, 477, 479, 480, 481, 482, 483, 484, 485, 486, + 487, 528, 530, 534, 531, 363, 398, 372, 371, 369, + 363, 369, 363, 369, 371, 369, 369, 369, 363, 369, + 369, 369, 369, 369, 369, 369, 363, 369, 363, 369, + 362, 365, 369, 372, 525, 530, 540, 541, 538, 371, + 363, 369, 363, 369, 365, 486, 488, 489, 490, 491, + 492, 493, 494, 495, 496, 497, 498, 499, 530, 364, + 372, 366, 367, 372, 406, 407, 408, 409, 411, 412, + 413, 414, 415, 416, 417, 418, 419, 420, 437, 486, + 499, 501, 503, 505, 509, 528, 530, 546, 547, 548, + 549, 550, 558, 559, 560, 561, 564, 565, 568, 569, + 570, 577, 582, 531, 371, 531, 366, 501, 544, 371, + 507, 398, 369, 372, 486, 486, 503, 342, 343, 364, + 368, 363, 363, 369, 405, 501, 362, 486, 369, 381, + 582, 361, 364, 395, 586, 604, 398, 605, 361, 394, + 395, 396, 397, 590, 591, 395, 499, 504, 592, 395, + 394, 395, 396, 397, 595, 596, 395, 394, 395, 396, + 397, 475, 597, 598, 395, 361, 599, 395, 604, 398, + 504, 535, 601, 602, 395, 504, 365, 584, 530, 398, + 542, 543, 367, 541, 540, 504, 523, 398, 377, 378, + 379, 374, 376, 340, 341, 344, 345, 380, 381, 346, + 347, 384, 383, 382, 348, 350, 349, 385, 365, 365, + 499, 367, 551, 362, 372, 372, 572, 362, 362, 372, + 372, 503, 362, 503, 370, 372, 372, 372, 372, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 371, + 502, 369, 372, 367, 547, 561, 565, 570, 544, 371, + 367, 544, 545, 544, 540, 398, 363, 478, 503, 398, + 501, 486, 361, 395, 587, 588, 363, 371, 363, 369, + 363, 369, 363, 369, 369, 363, 369, 363, 369, 363, + 369, 369, 363, 369, 369, 363, 369, 363, 369, 363, + 363, 542, 531, 369, 372, 367, 486, 486, 486, 488, + 488, 489, 489, 490, 490, 490, 490, 491, 491, 492, + 493, 494, 495, 496, 497, 500, 365, 558, 571, 547, + 573, 503, 372, 503, 370, 501, 501, 544, 367, 369, + 367, 365, 365, 369, 365, 369, 591, 590, 504, 592, + 596, 595, 598, 597, 361, 599, 601, 602, 372, 543, + 503, 552, 503, 518, 563, 406, 546, 559, 574, 363, + 363, 367, 544, 361, 395, 363, 363, 363, 363, 363, + 363, 370, 367, 398, 363, 362, 563, 575, 576, 554, + 555, 556, 562, 566, 501, 371, 548, 553, 557, 503, + 372, 363, 410, 550, 548, 366, 544, 363, 503, 553, + 554, 558, 567, 372, 367 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int16 yyr1[] = +{ + 0, 474, 475, 476, 476, 476, 476, 476, 476, 476, + 476, 476, 476, 476, 476, 476, 476, 476, 477, 477, + 477, 477, 477, 477, 478, 479, 480, 481, 481, 482, + 482, 483, 483, 484, 485, 485, 485, 486, 486, 486, + 486, 487, 487, 487, 487, 488, 488, 488, 488, 489, + 489, 489, 490, 490, 490, 491, 491, 491, 491, 491, + 492, 492, 492, 493, 493, 494, 494, 495, 495, 496, + 496, 497, 497, 498, 498, 499, 500, 499, 501, 501, + 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, + 502, 503, 503, 504, 505, 505, 505, 505, 505, 505, + 505, 505, 505, 505, 505, 507, 506, 508, 508, 509, + 509, 509, 509, 510, 510, 511, 511, 512, 513, 513, + 514, 514, 514, 514, 515, 516, 516, 516, 516, 516, + 517, 517, 517, 517, 517, 518, 518, 519, 520, 520, + 520, 520, 520, 520, 520, 520, 520, 520, 521, 522, + 522, 523, 523, 523, 524, 525, 525, 526, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 528, 529, 529, + 530, 530, 531, 531, 531, 531, 532, 532, 533, 534, + 534, 534, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, + 536, 536, 536, 538, 537, 539, 537, 540, 540, 541, + 541, 542, 542, 543, 543, 544, 544, 544, 544, 545, + 545, 546, 547, 547, 548, 548, 548, 548, 548, 548, + 548, 548, 549, 550, 551, 552, 550, 553, 553, 555, + 554, 556, 554, 557, 557, 558, 558, 559, 559, 560, + 560, 561, 562, 562, 563, 563, 564, 564, 566, 565, + 567, 567, 568, 568, 569, 569, 571, 570, 572, 570, + 573, 570, 574, 574, 575, 575, 576, 576, 577, 577, + 577, 577, 577, 577, 577, 577, 578, 578, 579, 579, + 579, 581, 580, 582, 583, 583, 584, 584, 585, 585, + 586, 586, 587, 587, 588, 588, 589, 589, 589, 589, + 589, 589, 590, 590, 591, 591, 591, 591, 591, 592, + 592, 593, 593, 594, 594, 594, 594, 594, 594, 594, + 594, 595, 595, 596, 596, 596, 596, 597, 597, 598, + 598, 598, 598, 598, 599, 599, 600, 600, 600, 600, + 601, 601, 602, 602, 603, 603, 604, 604, 605, 605 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 3, 2, 2, 1, 1, 1, 2, 2, 2, + 1, 2, 3, 2, 1, 1, 1, 1, 2, 2, + 2, 1, 1, 1, 1, 1, 3, 3, 3, 1, + 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, + 1, 3, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 0, 6, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 2, 3, 2, 2, 4, 2, + 3, 4, 2, 3, 4, 0, 6, 2, 3, 2, + 3, 3, 4, 1, 1, 2, 3, 3, 2, 3, + 2, 1, 2, 1, 1, 1, 3, 4, 6, 5, + 1, 2, 3, 5, 4, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, + 3, 1, 3, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 1, 1, 1, 3, + 2, 3, 2, 3, 3, 4, 1, 0, 3, 1, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 6, 0, 5, 1, 2, 3, + 4, 1, 3, 1, 2, 1, 3, 4, 2, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 0, 0, 5, 1, 1, 0, + 2, 0, 2, 2, 3, 1, 2, 1, 2, 1, + 2, 5, 3, 1, 1, 4, 1, 2, 0, 8, + 0, 1, 3, 2, 1, 2, 0, 6, 0, 8, + 0, 7, 1, 1, 1, 0, 2, 3, 2, 2, + 2, 3, 2, 2, 2, 2, 1, 2, 1, 1, + 1, 0, 3, 5, 1, 3, 1, 4, 1, 3, + 5, 5, 1, 3, 1, 3, 4, 6, 6, 8, + 6, 8, 1, 3, 1, 1, 1, 1, 1, 1, + 3, 4, 6, 4, 6, 6, 8, 6, 8, 6, + 8, 1, 3, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 3, 6, 8, 4, 6, + 1, 3, 1, 1, 4, 6, 1, 3, 3, 3 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (pParseContext, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, pParseContext); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (pParseContext); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, pParseContext); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, glslang::TParseContext* pParseContext) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], pParseContext); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, pParseContext); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, glslang::TParseContext* pParseContext) +{ + YY_USE (yyvaluep); + YY_USE (pParseContext); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (glslang::TParseContext* pParseContext) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, parseContext); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* variable_identifier: IDENTIFIER */ +#line 358 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleVariable((yyvsp[0].lex).loc, (yyvsp[0].lex).symbol, (yyvsp[0].lex).string); + } +#line 5278 "MachineIndependent/glslang_tab.cpp" + break; + + case 3: /* primary_expression: variable_identifier */ +#line 364 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5286 "MachineIndependent/glslang_tab.cpp" + break; + + case 4: /* primary_expression: LEFT_PAREN expression RIGHT_PAREN */ +#line 367 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } +#line 5296 "MachineIndependent/glslang_tab.cpp" + break; + + case 5: /* primary_expression: FLOATCONSTANT */ +#line 372 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 5304 "MachineIndependent/glslang_tab.cpp" + break; + + case 6: /* primary_expression: INTCONSTANT */ +#line 375 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 5312 "MachineIndependent/glslang_tab.cpp" + break; + + case 7: /* primary_expression: UINTCONSTANT */ +#line 378 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 5321 "MachineIndependent/glslang_tab.cpp" + break; + + case 8: /* primary_expression: BOOLCONSTANT */ +#line 382 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 5329 "MachineIndependent/glslang_tab.cpp" + break; + + case 9: /* primary_expression: STRING_LITERAL */ +#line 385 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true); + } +#line 5337 "MachineIndependent/glslang_tab.cpp" + break; + + case 10: /* primary_expression: INT32CONSTANT */ +#line 388 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 5346 "MachineIndependent/glslang_tab.cpp" + break; + + case 11: /* primary_expression: UINT32CONSTANT */ +#line 392 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 5355 "MachineIndependent/glslang_tab.cpp" + break; + + case 12: /* primary_expression: INT64CONSTANT */ +#line 396 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i64, (yyvsp[0].lex).loc, true); + } +#line 5364 "MachineIndependent/glslang_tab.cpp" + break; + + case 13: /* primary_expression: UINT64CONSTANT */ +#line 400 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u64, (yyvsp[0].lex).loc, true); + } +#line 5373 "MachineIndependent/glslang_tab.cpp" + break; + + case 14: /* primary_expression: INT16CONSTANT */ +#line 404 "MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((short)(yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 5382 "MachineIndependent/glslang_tab.cpp" + break; + + case 15: /* primary_expression: UINT16CONSTANT */ +#line 408 "MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((unsigned short)(yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 5391 "MachineIndependent/glslang_tab.cpp" + break; + + case 16: /* primary_expression: DOUBLECONSTANT */ +#line 412 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtDouble, (yyvsp[0].lex).loc, true); + } +#line 5402 "MachineIndependent/glslang_tab.cpp" + break; + + case 17: /* primary_expression: FLOAT16CONSTANT */ +#line 418 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat16, (yyvsp[0].lex).loc, true); + } +#line 5411 "MachineIndependent/glslang_tab.cpp" + break; + + case 18: /* postfix_expression: primary_expression */ +#line 425 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5419 "MachineIndependent/glslang_tab.cpp" + break; + + case 19: /* postfix_expression: postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET */ +#line 428 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBracketDereference((yyvsp[-2].lex).loc, (yyvsp[-3].interm.intermTypedNode), (yyvsp[-1].interm.intermTypedNode)); + } +#line 5427 "MachineIndependent/glslang_tab.cpp" + break; + + case 20: /* postfix_expression: function_call */ +#line 431 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5435 "MachineIndependent/glslang_tab.cpp" + break; + + case 21: /* postfix_expression: postfix_expression DOT IDENTIFIER */ +#line 434 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleDotDereference((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode), *(yyvsp[0].lex).string); + } +#line 5443 "MachineIndependent/glslang_tab.cpp" + break; + + case 22: /* postfix_expression: postfix_expression INC_OP */ +#line 437 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "++", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "++", EOpPostIncrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 5453 "MachineIndependent/glslang_tab.cpp" + break; + + case 23: /* postfix_expression: postfix_expression DEC_OP */ +#line 442 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "--", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "--", EOpPostDecrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 5463 "MachineIndependent/glslang_tab.cpp" + break; + + case 24: /* integer_expression: expression */ +#line 450 "MachineIndependent/glslang.y" + { + parseContext.integerCheck((yyvsp[0].interm.intermTypedNode), "[]"); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5472 "MachineIndependent/glslang_tab.cpp" + break; + + case 25: /* function_call: function_call_or_method */ +#line 457 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleFunctionCall((yyvsp[0].interm).loc, (yyvsp[0].interm).function, (yyvsp[0].interm).intermNode); + delete (yyvsp[0].interm).function; + } +#line 5481 "MachineIndependent/glslang_tab.cpp" + break; + + case 26: /* function_call_or_method: function_call_generic */ +#line 464 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 5489 "MachineIndependent/glslang_tab.cpp" + break; + + case 27: /* function_call_generic: function_call_header_with_parameters RIGHT_PAREN */ +#line 470 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 5498 "MachineIndependent/glslang_tab.cpp" + break; + + case 28: /* function_call_generic: function_call_header_no_parameters RIGHT_PAREN */ +#line 474 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 5507 "MachineIndependent/glslang_tab.cpp" + break; + + case 29: /* function_call_header_no_parameters: function_call_header VOID */ +#line 481 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 5515 "MachineIndependent/glslang_tab.cpp" + break; + + case 30: /* function_call_header_no_parameters: function_call_header */ +#line 484 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 5523 "MachineIndependent/glslang_tab.cpp" + break; + + case 31: /* function_call_header_with_parameters: function_call_header assignment_expression */ +#line 490 "MachineIndependent/glslang.y" + { + if (parseContext.spvVersion.vulkan > 0 + && parseContext.spvVersion.vulkanRelaxed + && (yyvsp[0].interm.intermTypedNode)->getType().containsOpaque()) + { + (yyval.interm).intermNode = parseContext.vkRelaxedRemapFunctionArgument((yyval.interm).loc, (yyvsp[-1].interm).function, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).function = (yyvsp[-1].interm).function; + } + else + { + TParameter param = { 0, new TType, {} }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + + (yyvsp[-1].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-1].interm).function; + (yyval.interm).intermNode = (yyvsp[0].interm.intermTypedNode); + } + } +#line 5546 "MachineIndependent/glslang_tab.cpp" + break; + + case 32: /* function_call_header_with_parameters: function_call_header_with_parameters COMMA assignment_expression */ +#line 508 "MachineIndependent/glslang.y" + { + if (parseContext.spvVersion.vulkan > 0 + && parseContext.spvVersion.vulkanRelaxed + && (yyvsp[0].interm.intermTypedNode)->getType().containsOpaque()) + { + TIntermNode* remappedNode = parseContext.vkRelaxedRemapFunctionArgument((yyvsp[-1].lex).loc, (yyvsp[-2].interm).function, (yyvsp[0].interm.intermTypedNode)); + if (remappedNode == (yyvsp[0].interm.intermTypedNode)) + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + else + (yyval.interm).intermNode = parseContext.intermediate.mergeAggregate((yyvsp[-2].interm).intermNode, remappedNode, (yyvsp[-1].lex).loc); + (yyval.interm).function = (yyvsp[-2].interm).function; + } + else + { + TParameter param = { 0, new TType, {} }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + + (yyvsp[-2].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-2].interm).function; + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + } + } +#line 5573 "MachineIndependent/glslang_tab.cpp" + break; + + case 33: /* function_call_header: function_identifier LEFT_PAREN */ +#line 533 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 5581 "MachineIndependent/glslang_tab.cpp" + break; + + case 34: /* function_identifier: type_specifier */ +#line 541 "MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 5591 "MachineIndependent/glslang_tab.cpp" + break; + + case 35: /* function_identifier: postfix_expression */ +#line 546 "MachineIndependent/glslang.y" + { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + (yyval.interm).function = 0; + (yyval.interm).intermNode = 0; + + TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode(); + if (method) { + (yyval.interm).function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + (yyval.interm).intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = (yyvsp[0].interm.intermTypedNode)->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + (yyval.interm).function = function; + } else + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ((yyval.interm).function == 0) { + // error recover + TString* empty = NewPoolTString(""); + (yyval.interm).function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } +#line 5623 "MachineIndependent/glslang_tab.cpp" + break; + + case 36: /* function_identifier: non_uniform_qualifier */ +#line 573 "MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 5633 "MachineIndependent/glslang_tab.cpp" + break; + + case 37: /* unary_expression: postfix_expression */ +#line 581 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if (TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode()) + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } +#line 5644 "MachineIndependent/glslang_tab.cpp" + break; + + case 38: /* unary_expression: INC_OP unary_expression */ +#line 587 "MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "++", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "++", EOpPreIncrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 5653 "MachineIndependent/glslang_tab.cpp" + break; + + case 39: /* unary_expression: DEC_OP unary_expression */ +#line 591 "MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "--", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "--", EOpPreDecrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 5662 "MachineIndependent/glslang_tab.cpp" + break; + + case 40: /* unary_expression: unary_operator unary_expression */ +#line 595 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).op != EOpNull) { + char errorOp[2] = {0, 0}; + switch((yyvsp[-1].interm).op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].interm).loc, errorOp, (yyvsp[-1].interm).op, (yyvsp[0].interm.intermTypedNode)); + } else { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } + } +#line 5683 "MachineIndependent/glslang_tab.cpp" + break; + + case 41: /* unary_operator: PLUS */ +#line 615 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNull; } +#line 5689 "MachineIndependent/glslang_tab.cpp" + break; + + case 42: /* unary_operator: DASH */ +#line 616 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNegative; } +#line 5695 "MachineIndependent/glslang_tab.cpp" + break; + + case 43: /* unary_operator: BANG */ +#line 617 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLogicalNot; } +#line 5701 "MachineIndependent/glslang_tab.cpp" + break; + + case 44: /* unary_operator: TILDE */ +#line 618 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpBitwiseNot; + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise not"); } +#line 5708 "MachineIndependent/glslang_tab.cpp" + break; + + case 45: /* multiplicative_expression: unary_expression */ +#line 624 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5714 "MachineIndependent/glslang_tab.cpp" + break; + + case 46: /* multiplicative_expression: multiplicative_expression STAR unary_expression */ +#line 625 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "*", EOpMul, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5724 "MachineIndependent/glslang_tab.cpp" + break; + + case 47: /* multiplicative_expression: multiplicative_expression SLASH unary_expression */ +#line 630 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "/", EOpDiv, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5734 "MachineIndependent/glslang_tab.cpp" + break; + + case 48: /* multiplicative_expression: multiplicative_expression PERCENT unary_expression */ +#line 635 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "%"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "%", EOpMod, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5745 "MachineIndependent/glslang_tab.cpp" + break; + + case 49: /* additive_expression: multiplicative_expression */ +#line 644 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5751 "MachineIndependent/glslang_tab.cpp" + break; + + case 50: /* additive_expression: additive_expression PLUS multiplicative_expression */ +#line 645 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "+", EOpAdd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5761 "MachineIndependent/glslang_tab.cpp" + break; + + case 51: /* additive_expression: additive_expression DASH multiplicative_expression */ +#line 650 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "-", EOpSub, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5771 "MachineIndependent/glslang_tab.cpp" + break; + + case 52: /* shift_expression: additive_expression */ +#line 658 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5777 "MachineIndependent/glslang_tab.cpp" + break; + + case 53: /* shift_expression: shift_expression LEFT_OP additive_expression */ +#line 659 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift left"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<<", EOpLeftShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5788 "MachineIndependent/glslang_tab.cpp" + break; + + case 54: /* shift_expression: shift_expression RIGHT_OP additive_expression */ +#line 665 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift right"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">>", EOpRightShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5799 "MachineIndependent/glslang_tab.cpp" + break; + + case 55: /* relational_expression: shift_expression */ +#line 674 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5805 "MachineIndependent/glslang_tab.cpp" + break; + + case 56: /* relational_expression: relational_expression LEFT_ANGLE shift_expression */ +#line 675 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<", EOpLessThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5815 "MachineIndependent/glslang_tab.cpp" + break; + + case 57: /* relational_expression: relational_expression RIGHT_ANGLE shift_expression */ +#line 680 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">", EOpGreaterThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5825 "MachineIndependent/glslang_tab.cpp" + break; + + case 58: /* relational_expression: relational_expression LE_OP shift_expression */ +#line 685 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<=", EOpLessThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5835 "MachineIndependent/glslang_tab.cpp" + break; + + case 59: /* relational_expression: relational_expression GE_OP shift_expression */ +#line 690 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">=", EOpGreaterThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5845 "MachineIndependent/glslang_tab.cpp" + break; + + case 60: /* equality_expression: relational_expression */ +#line 698 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5851 "MachineIndependent/glslang_tab.cpp" + break; + + case 61: /* equality_expression: equality_expression EQ_OP relational_expression */ +#line 699 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "==", EOpEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5865 "MachineIndependent/glslang_tab.cpp" + break; + + case 62: /* equality_expression: equality_expression NE_OP relational_expression */ +#line 708 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "!=", EOpNotEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5879 "MachineIndependent/glslang_tab.cpp" + break; + + case 63: /* and_expression: equality_expression */ +#line 720 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5885 "MachineIndependent/glslang_tab.cpp" + break; + + case 64: /* and_expression: and_expression AMPERSAND equality_expression */ +#line 721 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise and"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&", EOpAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5896 "MachineIndependent/glslang_tab.cpp" + break; + + case 65: /* exclusive_or_expression: and_expression */ +#line 730 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5902 "MachineIndependent/glslang_tab.cpp" + break; + + case 66: /* exclusive_or_expression: exclusive_or_expression CARET and_expression */ +#line 731 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise exclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^", EOpExclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5913 "MachineIndependent/glslang_tab.cpp" + break; + + case 67: /* inclusive_or_expression: exclusive_or_expression */ +#line 740 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5919 "MachineIndependent/glslang_tab.cpp" + break; + + case 68: /* inclusive_or_expression: inclusive_or_expression VERTICAL_BAR exclusive_or_expression */ +#line 741 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise inclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "|", EOpInclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5930 "MachineIndependent/glslang_tab.cpp" + break; + + case 69: /* logical_and_expression: inclusive_or_expression */ +#line 750 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5936 "MachineIndependent/glslang_tab.cpp" + break; + + case 70: /* logical_and_expression: logical_and_expression AND_OP inclusive_or_expression */ +#line 751 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&&", EOpLogicalAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5946 "MachineIndependent/glslang_tab.cpp" + break; + + case 71: /* logical_xor_expression: logical_and_expression */ +#line 759 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5952 "MachineIndependent/glslang_tab.cpp" + break; + + case 72: /* logical_xor_expression: logical_xor_expression XOR_OP logical_and_expression */ +#line 760 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^^", EOpLogicalXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5962 "MachineIndependent/glslang_tab.cpp" + break; + + case 73: /* logical_or_expression: logical_xor_expression */ +#line 768 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5968 "MachineIndependent/glslang_tab.cpp" + break; + + case 74: /* logical_or_expression: logical_or_expression OR_OP logical_xor_expression */ +#line 769 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "||", EOpLogicalOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5978 "MachineIndependent/glslang_tab.cpp" + break; + + case 75: /* conditional_expression: logical_or_expression */ +#line 777 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5984 "MachineIndependent/glslang_tab.cpp" + break; + + case 76: /* $@1: %empty */ +#line 778 "MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 5992 "MachineIndependent/glslang_tab.cpp" + break; + + case 77: /* conditional_expression: logical_or_expression QUESTION $@1 expression COLON assignment_expression */ +#line 781 "MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-4].lex).loc, "?", (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addSelection((yyvsp[-5].interm.intermTypedNode), (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-4].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-4].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), (yyvsp[0].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 6009 "MachineIndependent/glslang_tab.cpp" + break; + + case 78: /* assignment_expression: conditional_expression */ +#line 796 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 6015 "MachineIndependent/glslang_tab.cpp" + break; + + case 79: /* assignment_expression: unary_expression assignment_operator assignment_expression */ +#line 797 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array assignment"); + parseContext.opaqueCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.storage16BitAssignmentCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.specializationCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.lValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.addAssign((yyvsp[-1].interm).loc, (yyvsp[-1].interm).op, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.assignError((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), (yyvsp[0].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } + } +#line 6033 "MachineIndependent/glslang_tab.cpp" + break; + + case 80: /* assignment_operator: EQUAL */ +#line 813 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAssign; + } +#line 6042 "MachineIndependent/glslang_tab.cpp" + break; + + case 81: /* assignment_operator: MUL_ASSIGN */ +#line 817 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpMulAssign; + } +#line 6051 "MachineIndependent/glslang_tab.cpp" + break; + + case 82: /* assignment_operator: DIV_ASSIGN */ +#line 821 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpDivAssign; + } +#line 6060 "MachineIndependent/glslang_tab.cpp" + break; + + case 83: /* assignment_operator: MOD_ASSIGN */ +#line 825 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "%="); + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpModAssign; + } +#line 6070 "MachineIndependent/glslang_tab.cpp" + break; + + case 84: /* assignment_operator: ADD_ASSIGN */ +#line 830 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAddAssign; + } +#line 6079 "MachineIndependent/glslang_tab.cpp" + break; + + case 85: /* assignment_operator: SUB_ASSIGN */ +#line 834 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpSubAssign; + } +#line 6088 "MachineIndependent/glslang_tab.cpp" + break; + + case 86: /* assignment_operator: LEFT_ASSIGN */ +#line 838 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift left assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLeftShiftAssign; + } +#line 6097 "MachineIndependent/glslang_tab.cpp" + break; + + case 87: /* assignment_operator: RIGHT_ASSIGN */ +#line 842 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift right assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpRightShiftAssign; + } +#line 6106 "MachineIndependent/glslang_tab.cpp" + break; + + case 88: /* assignment_operator: AND_ASSIGN */ +#line 846 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-and assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpAndAssign; + } +#line 6115 "MachineIndependent/glslang_tab.cpp" + break; + + case 89: /* assignment_operator: XOR_ASSIGN */ +#line 850 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-xor assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpExclusiveOrAssign; + } +#line 6124 "MachineIndependent/glslang_tab.cpp" + break; + + case 90: /* assignment_operator: OR_ASSIGN */ +#line 854 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-or assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpInclusiveOrAssign; + } +#line 6133 "MachineIndependent/glslang_tab.cpp" + break; + + case 91: /* expression: assignment_expression */ +#line 861 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 6141 "MachineIndependent/glslang_tab.cpp" + break; + + case 92: /* expression: expression COMMA assignment_expression */ +#line 864 "MachineIndependent/glslang.y" + { + parseContext.samplerConstructorLocationCheck((yyvsp[-1].lex).loc, ",", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-1].lex).loc, ",", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs()), (yyvsp[0].interm.intermTypedNode)->getCompleteString(parseContext.intermediate.getEnhancedMsgs())); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 6154 "MachineIndependent/glslang_tab.cpp" + break; + + case 93: /* constant_expression: conditional_expression */ +#line 875 "MachineIndependent/glslang.y" + { + parseContext.constantValueCheck((yyvsp[0].interm.intermTypedNode), ""); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 6163 "MachineIndependent/glslang_tab.cpp" + break; + + case 94: /* declaration: function_prototype SEMICOLON */ +#line 882 "MachineIndependent/glslang.y" + { + parseContext.handleFunctionDeclarator((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).function, true /* prototype */); + (yyval.interm.intermNode) = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } +#line 6173 "MachineIndependent/glslang_tab.cpp" + break; + + case 95: /* declaration: spirv_instruction_qualifier function_prototype SEMICOLON */ +#line 887 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[-1].interm).loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V instruction qualifier"); + (yyvsp[-1].interm).function->setSpirvInstruction(*(yyvsp[-2].interm.spirvInst)); // Attach SPIR-V intruction qualifier + parseContext.handleFunctionDeclarator((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).function, true /* prototype */); + (yyval.interm.intermNode) = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } +#line 6185 "MachineIndependent/glslang_tab.cpp" + break; + + case 96: /* declaration: spirv_execution_mode_qualifier SEMICOLON */ +#line 894 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "SPIR-V execution mode qualifier"); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V execution mode qualifier"); + (yyval.interm.intermNode) = 0; + } +#line 6195 "MachineIndependent/glslang_tab.cpp" + break; + + case 97: /* declaration: init_declarator_list SEMICOLON */ +#line 899 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).intermNode && (yyvsp[-1].interm).intermNode->getAsAggregate()) + (yyvsp[-1].interm).intermNode->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-1].interm).intermNode; + } +#line 6205 "MachineIndependent/glslang_tab.cpp" + break; + + case 98: /* declaration: PRECISION precision_qualifier type_specifier SEMICOLON */ +#line 904 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[-3].lex).loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision((yyvsp[-3].lex).loc, (yyvsp[-1].interm.type), (yyvsp[-2].interm.type).qualifier.precision); + (yyval.interm.intermNode) = 0; + } +#line 6217 "MachineIndependent/glslang_tab.cpp" + break; + + case 99: /* declaration: block_structure SEMICOLON */ +#line 911 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).typeList); + (yyval.interm.intermNode) = 0; + } +#line 6226 "MachineIndependent/glslang_tab.cpp" + break; + + case 100: /* declaration: block_structure IDENTIFIER SEMICOLON */ +#line 915 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-2].interm).loc, *(yyvsp[-2].interm).typeList, (yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 6235 "MachineIndependent/glslang_tab.cpp" + break; + + case 101: /* declaration: block_structure IDENTIFIER array_specifier SEMICOLON */ +#line 919 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-3].interm).loc, *(yyvsp[-3].interm).typeList, (yyvsp[-2].lex).string, (yyvsp[-1].interm).arraySizes); + (yyval.interm.intermNode) = 0; + } +#line 6244 "MachineIndependent/glslang_tab.cpp" + break; + + case 102: /* declaration: type_qualifier SEMICOLON */ +#line 923 "MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier); + parseContext.updateStandaloneQualifierDefaults((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type)); + (yyval.interm.intermNode) = 0; + } +#line 6254 "MachineIndependent/glslang_tab.cpp" + break; + + case 103: /* declaration: type_qualifier IDENTIFIER SEMICOLON */ +#line 928 "MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).shaderQualifiers); + parseContext.addQualifierToExisting((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, *(yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 6264 "MachineIndependent/glslang_tab.cpp" + break; + + case 104: /* declaration: type_qualifier IDENTIFIER identifier_list SEMICOLON */ +#line 933 "MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).shaderQualifiers); + (yyvsp[-1].interm.identifierList)->push_back((yyvsp[-2].lex).string); + parseContext.addQualifierToExisting((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).qualifier, *(yyvsp[-1].interm.identifierList)); + (yyval.interm.intermNode) = 0; + } +#line 6275 "MachineIndependent/glslang_tab.cpp" + break; + + case 105: /* $@2: %empty */ +#line 942 "MachineIndependent/glslang.y" + { parseContext.nestedBlockCheck((yyvsp[-2].interm.type).loc); } +#line 6281 "MachineIndependent/glslang_tab.cpp" + break; + + case 106: /* block_structure: type_qualifier IDENTIFIER LEFT_BRACE $@2 struct_declaration_list RIGHT_BRACE */ +#line 942 "MachineIndependent/glslang.y" + { + --parseContext.blockNestingLevel; + parseContext.blockName = (yyvsp[-4].lex).string; + parseContext.globalQualifierFixCheck((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).qualifier); + parseContext.checkNoShaderLayouts((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).shaderQualifiers); + parseContext.currentBlockQualifier = (yyvsp[-5].interm.type).qualifier; + (yyval.interm).loc = (yyvsp[-5].interm.type).loc; + (yyval.interm).typeList = (yyvsp[-1].interm.typeList); + } +#line 6295 "MachineIndependent/glslang_tab.cpp" + break; + + case 107: /* identifier_list: COMMA IDENTIFIER */ +#line 953 "MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = new TIdentifierList; + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 6304 "MachineIndependent/glslang_tab.cpp" + break; + + case 108: /* identifier_list: identifier_list COMMA IDENTIFIER */ +#line 957 "MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = (yyvsp[-2].interm.identifierList); + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 6313 "MachineIndependent/glslang_tab.cpp" + break; + + case 109: /* function_prototype: function_declarator RIGHT_PAREN */ +#line 964 "MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-1].interm.function); + if (parseContext.compileOnly) (yyval.interm).function->setExport(); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 6323 "MachineIndependent/glslang_tab.cpp" + break; + + case 110: /* function_prototype: function_declarator RIGHT_PAREN attribute */ +#line 969 "MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-2].interm.function); + if (parseContext.compileOnly) (yyval.interm).function->setExport(); + (yyval.interm).loc = (yyvsp[-1].lex).loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions((yyvsp[-1].lex).loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes((yyvsp[-1].lex).loc, *(yyvsp[0].interm.attributes)); + } +#line 6336 "MachineIndependent/glslang_tab.cpp" + break; + + case 111: /* function_prototype: attribute function_declarator RIGHT_PAREN */ +#line 977 "MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-1].interm.function); + if (parseContext.compileOnly) (yyval.interm).function->setExport(); + (yyval.interm).loc = (yyvsp[0].lex).loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions((yyvsp[0].lex).loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes((yyvsp[0].lex).loc, *(yyvsp[-2].interm.attributes)); + } +#line 6349 "MachineIndependent/glslang_tab.cpp" + break; + + case 112: /* function_prototype: attribute function_declarator RIGHT_PAREN attribute */ +#line 985 "MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-2].interm.function); + if (parseContext.compileOnly) (yyval.interm).function->setExport(); + (yyval.interm).loc = (yyvsp[-1].lex).loc; + const char * extensions[2] = { E_GL_EXT_subgroup_uniform_control_flow, E_GL_EXT_maximal_reconvergence }; + parseContext.requireExtensions((yyvsp[-1].lex).loc, 2, extensions, "attribute"); + parseContext.handleFunctionAttributes((yyvsp[-1].lex).loc, *(yyvsp[-3].interm.attributes)); + parseContext.handleFunctionAttributes((yyvsp[-1].lex).loc, *(yyvsp[0].interm.attributes)); + } +#line 6363 "MachineIndependent/glslang_tab.cpp" + break; + + case 113: /* function_declarator: function_header */ +#line 997 "MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 6371 "MachineIndependent/glslang_tab.cpp" + break; + + case 114: /* function_declarator: function_header_with_parameters */ +#line 1000 "MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 6379 "MachineIndependent/glslang_tab.cpp" + break; + + case 115: /* function_header_with_parameters: function_header parameter_declaration */ +#line 1007 "MachineIndependent/glslang.y" + { + // Add the parameter + (yyval.interm.function) = (yyvsp[-1].interm.function); + if ((yyvsp[0].interm).param.type->getBasicType() != EbtVoid) + { + if (!(parseContext.spvVersion.vulkan > 0 && parseContext.spvVersion.vulkanRelaxed)) + (yyvsp[-1].interm.function)->addParameter((yyvsp[0].interm).param); + else + parseContext.vkRelaxedRemapFunctionParameter((yyvsp[-1].interm.function), (yyvsp[0].interm).param); + } + else + delete (yyvsp[0].interm).param.type; + } +#line 6397 "MachineIndependent/glslang_tab.cpp" + break; + + case 116: /* function_header_with_parameters: function_header_with_parameters COMMA parameter_declaration */ +#line 1020 "MachineIndependent/glslang.y" + { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ((yyvsp[0].interm).param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error((yyvsp[-1].lex).loc, "cannot be an argument type except for '(void)'", "void", ""); + delete (yyvsp[0].interm).param.type; + } else { + // Add the parameter + (yyval.interm.function) = (yyvsp[-2].interm.function); + if (!(parseContext.spvVersion.vulkan > 0 && parseContext.spvVersion.vulkanRelaxed)) + (yyvsp[-2].interm.function)->addParameter((yyvsp[0].interm).param); + else + parseContext.vkRelaxedRemapFunctionParameter((yyvsp[-2].interm.function), (yyvsp[0].interm).param); + } + } +#line 6422 "MachineIndependent/glslang_tab.cpp" + break; + + case 117: /* function_header: fully_specified_type IDENTIFIER LEFT_PAREN */ +#line 1043 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).qualifier.storage != EvqGlobal && (yyvsp[-2].interm.type).qualifier.storage != EvqTemporary) { + parseContext.error((yyvsp[-1].lex).loc, "no qualifiers allowed for function return", + GetStorageQualifierString((yyvsp[-2].interm.type).qualifier.storage), ""); + } + if ((yyvsp[-2].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type((yyvsp[-2].interm.type)); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction((yyvsp[-1].lex).string); + + // Make the function + function = new TFunction((yyvsp[-1].lex).string, type); + (yyval.interm.function) = function; + } +#line 6446 "MachineIndependent/glslang_tab.cpp" + break; + + case 118: /* parameter_declarator: type_specifier IDENTIFIER */ +#line 1066 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-1].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-1].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-1].interm.type).loc, *(yyvsp[-1].interm.type).arraySizes); + } + if ((yyvsp[-1].interm.type).basicType == EbtVoid) { + parseContext.error((yyvsp[0].lex).loc, "illegal use of type 'void'", (yyvsp[0].lex).string->c_str(), ""); + } + parseContext.reservedErrorCheck((yyvsp[0].lex).loc, *(yyvsp[0].lex).string); + + TParameter param = {(yyvsp[0].lex).string, new TType((yyvsp[-1].interm.type)), {}}; + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).param = param; + } +#line 6466 "MachineIndependent/glslang_tab.cpp" + break; + + case 119: /* parameter_declarator: type_specifier IDENTIFIER array_specifier */ +#line 1081 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + TType* type = new TType((yyvsp[-2].interm.type)); + type->transferArraySizes((yyvsp[0].interm).arraySizes); + type->copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck((yyvsp[0].interm).loc, *(yyvsp[0].interm).arraySizes); + parseContext.reservedErrorCheck((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string); + + TParameter param = { (yyvsp[-1].lex).string, type, {} }; + + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).param = param; + } +#line 6490 "MachineIndependent/glslang_tab.cpp" + break; + + case 120: /* parameter_declaration: type_qualifier parameter_declarator */ +#line 1106 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier(), (yyval.interm).param.type->isCoopMatOrVec()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + + } +#line 6506 "MachineIndependent/glslang_tab.cpp" + break; + + case 121: /* parameter_declaration: parameter_declarator */ +#line 1117 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier(), (yyval.interm).param.type->isCoopMatOrVec()); + } +#line 6518 "MachineIndependent/glslang_tab.cpp" + break; + + case 122: /* parameter_declaration: type_qualifier parameter_type_specifier */ +#line 1127 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyvsp[-1].interm.type).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier(), (yyval.interm).param.type->isCoopMatOrVec()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + } +#line 6533 "MachineIndependent/glslang_tab.cpp" + break; + + case 123: /* parameter_declaration: parameter_type_specifier */ +#line 1137 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier(), (yyval.interm).param.type->isCoopMatOrVec()); + } +#line 6545 "MachineIndependent/glslang_tab.cpp" + break; + + case 124: /* parameter_type_specifier: type_specifier */ +#line 1147 "MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType((yyvsp[0].interm.type)), {} }; + (yyval.interm).param = param; + if ((yyvsp[0].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[0].interm.type).loc, *(yyvsp[0].interm.type).arraySizes); + } +#line 6556 "MachineIndependent/glslang_tab.cpp" + break; + + case 125: /* init_declarator_list: single_declaration */ +#line 1156 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 6564 "MachineIndependent/glslang_tab.cpp" + break; + + case 126: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER */ +#line 1159 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-2].interm).type); + } +#line 6573 "MachineIndependent/glslang_tab.cpp" + break; + + case 127: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER array_specifier */ +#line 1163 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-3].interm).type, (yyvsp[0].interm).arraySizes); + } +#line 6582 "MachineIndependent/glslang_tab.cpp" + break; + + case 128: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer */ +#line 1167 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-5].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-5].interm).type, (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-5].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 6592 "MachineIndependent/glslang_tab.cpp" + break; + + case 129: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER EQUAL initializer */ +#line 1172 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-4].interm).type, 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-4].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 6602 "MachineIndependent/glslang_tab.cpp" + break; + + case 130: /* single_declaration: fully_specified_type */ +#line 1180 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[0].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareTypeDefaults((yyval.interm).loc, (yyval.interm).type); + } +#line 6612 "MachineIndependent/glslang_tab.cpp" + break; + + case 131: /* single_declaration: fully_specified_type IDENTIFIER */ +#line 1185 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-1].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-1].interm.type)); + } +#line 6622 "MachineIndependent/glslang_tab.cpp" + break; + + case 132: /* single_declaration: fully_specified_type IDENTIFIER array_specifier */ +#line 1190 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-2].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-2].interm.type), (yyvsp[0].interm).arraySizes); + } +#line 6632 "MachineIndependent/glslang_tab.cpp" + break; + + case 133: /* single_declaration: fully_specified_type IDENTIFIER array_specifier EQUAL initializer */ +#line 1195 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-4].interm.type), (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 6642 "MachineIndependent/glslang_tab.cpp" + break; + + case 134: /* single_declaration: fully_specified_type IDENTIFIER EQUAL initializer */ +#line 1200 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-3].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 6652 "MachineIndependent/glslang_tab.cpp" + break; + + case 135: /* fully_specified_type: type_specifier */ +#line 1209 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + + parseContext.globalQualifierTypeCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyval.interm.type)); + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck((yyval.interm.type).loc, (yyval.interm.type).basicType, (yyval.interm.type).qualifier, (yyval.interm.type).isCoopmatOrvec()); + } +#line 6667 "MachineIndependent/glslang_tab.cpp" + break; + + case 136: /* fully_specified_type: type_qualifier type_specifier */ +#line 1219 "MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, false, &(yyvsp[0].interm.type)); + parseContext.globalQualifierTypeCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, (yyvsp[0].interm.type)); + + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ((yyvsp[0].interm.type).arraySizes && parseContext.arrayQualifierError((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).qualifier)) + (yyvsp[0].interm.type).arraySizes = nullptr; + + parseContext.checkNoShaderLayouts((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + (yyvsp[0].interm.type).shaderQualifiers.merge((yyvsp[-1].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyvsp[-1].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).basicType, (yyvsp[0].interm.type).qualifier, (yyvsp[0].interm.type).isCoopmatOrvec()); + + (yyval.interm.type) = (yyvsp[0].interm.type); + + if (! (yyval.interm.type).qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && (yyval.interm.type).qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && (yyval.interm.type).qualifier.storage == EvqVaryingIn))) + (yyval.interm.type).qualifier.smooth = true; + } +#line 6696 "MachineIndependent/glslang_tab.cpp" + break; + + case 137: /* invariant_qualifier: INVARIANT */ +#line 1246 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "invariant"); + parseContext.profileRequires((yyval.interm.type).loc, ENoProfile, 120, 0, "invariant"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.invariant = true; + } +#line 6707 "MachineIndependent/glslang_tab.cpp" + break; + + case 138: /* interpolation_qualifier: SMOOTH */ +#line 1255 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "smooth"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.smooth = true; + } +#line 6719 "MachineIndependent/glslang_tab.cpp" + break; + + case 139: /* interpolation_qualifier: FLAT */ +#line 1262 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "flat"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.flat = true; + } +#line 6731 "MachineIndependent/glslang_tab.cpp" + break; + + case 140: /* interpolation_qualifier: NOPERSPECTIVE */ +#line 1269 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "noperspective"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nopersp = true; + } +#line 6743 "MachineIndependent/glslang_tab.cpp" + break; + + case 141: /* interpolation_qualifier: EXPLICITINTERPAMD */ +#line 1276 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "__explicitInterpAMD"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.explicitInterp = true; + } +#line 6755 "MachineIndependent/glslang_tab.cpp" + break; + + case 142: /* interpolation_qualifier: PERVERTEXNV */ +#line 1283 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "pervertexNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.pervertexNV = true; + } +#line 6768 "MachineIndependent/glslang_tab.cpp" + break; + + case 143: /* interpolation_qualifier: PERVERTEXEXT */ +#line 1291 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "pervertexEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_EXT_fragment_shader_barycentric, "fragment shader barycentric"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.pervertexEXT = true; + } +#line 6781 "MachineIndependent/glslang_tab.cpp" + break; + + case 144: /* interpolation_qualifier: PERPRIMITIVENV */ +#line 1299 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perprimitiveNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perPrimitiveNV = true; + } +#line 6796 "MachineIndependent/glslang_tab.cpp" + break; + + case 145: /* interpolation_qualifier: PERPRIMITIVEEXT */ +#line 1309 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perprimitiveEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshMask), "perprimitiveEXT"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_mesh_shader, "perprimitiveEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perPrimitiveNV = true; + } +#line 6811 "MachineIndependent/glslang_tab.cpp" + break; + + case 146: /* interpolation_qualifier: PERVIEWNV */ +#line 1319 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perviewNV"); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangMesh, "perviewNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perViewNV = true; + } +#line 6823 "MachineIndependent/glslang_tab.cpp" + break; + + case 147: /* interpolation_qualifier: PERTASKNV */ +#line 1326 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "taskNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask), "taskNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perTaskNV = true; + } +#line 6835 "MachineIndependent/glslang_tab.cpp" + break; + + case 148: /* layout_qualifier: LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN */ +#line 1336 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + } +#line 6843 "MachineIndependent/glslang_tab.cpp" + break; + + case 149: /* layout_qualifier_id_list: layout_qualifier_id */ +#line 1342 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6851 "MachineIndependent/glslang_tab.cpp" + break; + + case 150: /* layout_qualifier_id_list: layout_qualifier_id_list COMMA layout_qualifier_id */ +#line 1345 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers((yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 6861 "MachineIndependent/glslang_tab.cpp" + break; + + case 151: /* layout_qualifier_id: IDENTIFIER */ +#line 1352 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), *(yyvsp[0].lex).string); + } +#line 6870 "MachineIndependent/glslang_tab.cpp" + break; + + case 152: /* layout_qualifier_id: IDENTIFIER EQUAL constant_expression */ +#line 1356 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-2].lex).loc); + parseContext.setLayoutQualifier((yyvsp[-2].lex).loc, (yyval.interm.type), *(yyvsp[-2].lex).string, (yyvsp[0].interm.intermTypedNode)); + } +#line 6879 "MachineIndependent/glslang_tab.cpp" + break; + + case 153: /* layout_qualifier_id: SHARED */ +#line 1360 "MachineIndependent/glslang.y" + { // because "shared" is both an identifier and a keyword + (yyval.interm.type).init((yyvsp[0].lex).loc); + TString strShared("shared"); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), strShared); + } +#line 6889 "MachineIndependent/glslang_tab.cpp" + break; + + case 154: /* precise_qualifier: PRECISE */ +#line 1368 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyval.interm.type).loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.noContraction = true; + } +#line 6900 "MachineIndependent/glslang_tab.cpp" + break; + + case 155: /* type_qualifier: single_type_qualifier */ +#line 1377 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6908 "MachineIndependent/glslang_tab.cpp" + break; + + case 156: /* type_qualifier: type_qualifier single_type_qualifier */ +#line 1380 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + if ((yyval.interm.type).basicType == EbtVoid) + (yyval.interm.type).basicType = (yyvsp[0].interm.type).basicType; + + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyval.interm.type).loc, (yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 6921 "MachineIndependent/glslang_tab.cpp" + break; + + case 157: /* single_type_qualifier: storage_qualifier */ +#line 1391 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6929 "MachineIndependent/glslang_tab.cpp" + break; + + case 158: /* single_type_qualifier: layout_qualifier */ +#line 1394 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6937 "MachineIndependent/glslang_tab.cpp" + break; + + case 159: /* single_type_qualifier: precision_qualifier */ +#line 1397 "MachineIndependent/glslang.y" + { + parseContext.checkPrecisionQualifier((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier.precision); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6946 "MachineIndependent/glslang_tab.cpp" + break; + + case 160: /* single_type_qualifier: interpolation_qualifier */ +#line 1401 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6955 "MachineIndependent/glslang_tab.cpp" + break; + + case 161: /* single_type_qualifier: invariant_qualifier */ +#line 1405 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6964 "MachineIndependent/glslang_tab.cpp" + break; + + case 162: /* single_type_qualifier: precise_qualifier */ +#line 1409 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6973 "MachineIndependent/glslang_tab.cpp" + break; + + case 163: /* single_type_qualifier: non_uniform_qualifier */ +#line 1413 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6981 "MachineIndependent/glslang_tab.cpp" + break; + + case 164: /* single_type_qualifier: spirv_storage_class_qualifier */ +#line 1416 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].interm.type).loc, "spirv_storage_class"); + parseContext.requireExtensions((yyvsp[0].interm.type).loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V storage class qualifier"); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6991 "MachineIndependent/glslang_tab.cpp" + break; + + case 165: /* single_type_qualifier: spirv_decorate_qualifier */ +#line 1421 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].interm.type).loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V decorate qualifier"); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 7000 "MachineIndependent/glslang_tab.cpp" + break; + + case 166: /* single_type_qualifier: SPIRV_BY_REFERENCE */ +#line 1425 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_spirv_intrinsics, "spirv_by_reference"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.setSpirvByReference(); + } +#line 7010 "MachineIndependent/glslang_tab.cpp" + break; + + case 167: /* single_type_qualifier: SPIRV_LITERAL */ +#line 1430 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_spirv_intrinsics, "spirv_by_literal"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.setSpirvLiteral(); + } +#line 7020 "MachineIndependent/glslang_tab.cpp" + break; + + case 168: /* storage_qualifier: CONST */ +#line 1438 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } +#line 7029 "MachineIndependent/glslang_tab.cpp" + break; + + case 169: /* storage_qualifier: INOUT */ +#line 1442 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "inout"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqInOut; + } +#line 7039 "MachineIndependent/glslang_tab.cpp" + break; + + case 170: /* storage_qualifier: IN */ +#line 1447 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "in"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqIn; + } +#line 7050 "MachineIndependent/glslang_tab.cpp" + break; + + case 171: /* storage_qualifier: OUT */ +#line 1453 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "out"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqOut; + } +#line 7061 "MachineIndependent/glslang_tab.cpp" + break; + + case 172: /* storage_qualifier: CENTROID */ +#line 1459 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck((yyvsp[0].lex).loc, "centroid"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.centroid = true; + } +#line 7073 "MachineIndependent/glslang_tab.cpp" + break; + + case 173: /* storage_qualifier: UNIFORM */ +#line 1466 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "uniform"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqUniform; + } +#line 7083 "MachineIndependent/glslang_tab.cpp" + break; + + case 174: /* storage_qualifier: TILEIMAGEEXT */ +#line 1471 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "tileImageEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqTileImageEXT; + } +#line 7093 "MachineIndependent/glslang_tab.cpp" + break; + + case 175: /* storage_qualifier: SHARED */ +#line 1476 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshMask | EShLangTaskMask), "shared"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqShared; + } +#line 7106 "MachineIndependent/glslang_tab.cpp" + break; + + case 176: /* storage_qualifier: BUFFER */ +#line 1484 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "buffer"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqBuffer; + } +#line 7116 "MachineIndependent/glslang_tab.cpp" + break; + + case 177: /* storage_qualifier: ATTRIBUTE */ +#line 1489 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "attribute"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 7133 "MachineIndependent/glslang_tab.cpp" + break; + + case 178: /* storage_qualifier: VARYING */ +#line 1501 "MachineIndependent/glslang.y" + { + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "varying"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + if (parseContext.language == EShLangVertex) + (yyval.interm.type).qualifier.storage = EvqVaryingOut; + else + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 7152 "MachineIndependent/glslang_tab.cpp" + break; + + case 179: /* storage_qualifier: PATCH */ +#line 1515 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "patch"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.patch = true; + } +#line 7163 "MachineIndependent/glslang_tab.cpp" + break; + + case 180: /* storage_qualifier: SAMPLE */ +#line 1521 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "sample"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.sample = true; + } +#line 7173 "MachineIndependent/glslang_tab.cpp" + break; + + case 181: /* storage_qualifier: HITATTRNV */ +#line 1526 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 7186 "MachineIndependent/glslang_tab.cpp" + break; + + case 182: /* storage_qualifier: HITOBJECTATTRNV */ +#line 1534 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask + | EShLangMissMask), "hitObjectAttributeNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_shader_invocation_reorder, "hitObjectAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitObjectAttrNV; + } +#line 7199 "MachineIndependent/glslang_tab.cpp" + break; + + case 183: /* storage_qualifier: HITATTREXT */ +#line 1542 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 7212 "MachineIndependent/glslang_tab.cpp" + break; + + case 184: /* storage_qualifier: PAYLOADNV */ +#line 1550 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 7225 "MachineIndependent/glslang_tab.cpp" + break; + + case 185: /* storage_qualifier: PAYLOADEXT */ +#line 1558 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 7238 "MachineIndependent/glslang_tab.cpp" + break; + + case 186: /* storage_qualifier: PAYLOADINNV */ +#line 1566 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 7251 "MachineIndependent/glslang_tab.cpp" + break; + + case 187: /* storage_qualifier: PAYLOADINEXT */ +#line 1574 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 7264 "MachineIndependent/glslang_tab.cpp" + break; + + case 188: /* storage_qualifier: CALLDATANV */ +#line 1582 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 7277 "MachineIndependent/glslang_tab.cpp" + break; + + case 189: /* storage_qualifier: CALLDATAEXT */ +#line 1590 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 7290 "MachineIndependent/glslang_tab.cpp" + break; + + case 190: /* storage_qualifier: CALLDATAINNV */ +#line 1598 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 7302 "MachineIndependent/glslang_tab.cpp" + break; + + case 191: /* storage_qualifier: CALLDATAINEXT */ +#line 1605 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 7314 "MachineIndependent/glslang_tab.cpp" + break; + + case 192: /* storage_qualifier: COHERENT */ +#line 1612 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.coherent = true; + } +#line 7323 "MachineIndependent/glslang_tab.cpp" + break; + + case 193: /* storage_qualifier: DEVICECOHERENT */ +#line 1616 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + (yyval.interm.type).qualifier.devicecoherent = true; + } +#line 7333 "MachineIndependent/glslang_tab.cpp" + break; + + case 194: /* storage_qualifier: QUEUEFAMILYCOHERENT */ +#line 1621 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + (yyval.interm.type).qualifier.queuefamilycoherent = true; + } +#line 7343 "MachineIndependent/glslang_tab.cpp" + break; + + case 195: /* storage_qualifier: WORKGROUPCOHERENT */ +#line 1626 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + (yyval.interm.type).qualifier.workgroupcoherent = true; + } +#line 7353 "MachineIndependent/glslang_tab.cpp" + break; + + case 196: /* storage_qualifier: SUBGROUPCOHERENT */ +#line 1631 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + (yyval.interm.type).qualifier.subgroupcoherent = true; + } +#line 7363 "MachineIndependent/glslang_tab.cpp" + break; + + case 197: /* storage_qualifier: NONPRIVATE */ +#line 1636 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + (yyval.interm.type).qualifier.nonprivate = true; + } +#line 7373 "MachineIndependent/glslang_tab.cpp" + break; + + case 198: /* storage_qualifier: SHADERCALLCOHERENT */ +#line 1641 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + (yyval.interm.type).qualifier.shadercallcoherent = true; + } +#line 7383 "MachineIndependent/glslang_tab.cpp" + break; + + case 199: /* storage_qualifier: VOLATILE */ +#line 1646 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.volatil = true; + } +#line 7392 "MachineIndependent/glslang_tab.cpp" + break; + + case 200: /* storage_qualifier: RESTRICT */ +#line 1650 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.restrict = true; + } +#line 7401 "MachineIndependent/glslang_tab.cpp" + break; + + case 201: /* storage_qualifier: READONLY */ +#line 1654 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.readonly = true; + } +#line 7410 "MachineIndependent/glslang_tab.cpp" + break; + + case 202: /* storage_qualifier: WRITEONLY */ +#line 1658 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.writeonly = true; + } +#line 7419 "MachineIndependent/glslang_tab.cpp" + break; + + case 203: /* storage_qualifier: NONTEMPORAL */ +#line 1662 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nontemporal = true; + } +#line 7428 "MachineIndependent/glslang_tab.cpp" + break; + + case 204: /* storage_qualifier: SUBROUTINE */ +#line 1666 "MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[0].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[0].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[0].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + } +#line 7439 "MachineIndependent/glslang_tab.cpp" + break; + + case 205: /* storage_qualifier: SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN */ +#line 1672 "MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[-3].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[-3].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[-3].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[-3].lex).loc); + } +#line 7450 "MachineIndependent/glslang_tab.cpp" + break; + + case 206: /* storage_qualifier: TASKPAYLOADWORKGROUPEXT */ +#line 1678 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "taskPayloadSharedEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTaskMask | EShLangMeshMask), "taskPayloadSharedEXT "); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqtaskPayloadSharedEXT; + } +#line 7462 "MachineIndependent/glslang_tab.cpp" + break; + + case 207: /* non_uniform_qualifier: NONUNIFORM */ +#line 1688 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nonUniform = true; + } +#line 7471 "MachineIndependent/glslang_tab.cpp" + break; + + case 208: /* type_name_list: IDENTIFIER */ +#line 1695 "MachineIndependent/glslang.y" + { + // TODO + } +#line 7479 "MachineIndependent/glslang_tab.cpp" + break; + + case 209: /* type_name_list: type_name_list COMMA IDENTIFIER */ +#line 1698 "MachineIndependent/glslang.y" + { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } +#line 7489 "MachineIndependent/glslang_tab.cpp" + break; + + case 210: /* type_specifier: type_specifier_nonarray type_parameter_specifier_opt */ +#line 1706 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[0].interm.typeParameters); + parseContext.typeParametersCheck((yyvsp[-1].interm.type).loc, (yyval.interm.type)); + + } +#line 7501 "MachineIndependent/glslang_tab.cpp" + break; + + case 211: /* type_specifier: type_specifier_nonarray type_parameter_specifier_opt array_specifier */ +#line 1713 "MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[0].interm).loc, (yyvsp[0].interm).arraySizes); + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[-1].interm.typeParameters); + (yyval.interm.type).arraySizes = (yyvsp[0].interm).arraySizes; + parseContext.typeParametersCheck((yyvsp[-2].interm.type).loc, (yyval.interm.type)); + } +#line 7514 "MachineIndependent/glslang_tab.cpp" + break; + + case 212: /* array_specifier: LEFT_BRACKET RIGHT_BRACKET */ +#line 1724 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + (yyval.interm).arraySizes->addInnerSize(); + } +#line 7524 "MachineIndependent/glslang_tab.cpp" + break; + + case 213: /* array_specifier: LEFT_BRACKET conditional_expression RIGHT_BRACKET */ +#line 1729 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-2].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 7537 "MachineIndependent/glslang_tab.cpp" + break; + + case 214: /* array_specifier: array_specifier LEFT_BRACKET RIGHT_BRACKET */ +#line 1737 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + (yyval.interm).arraySizes->addInnerSize(); + } +#line 7546 "MachineIndependent/glslang_tab.cpp" + break; + + case 215: /* array_specifier: array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET */ +#line 1741 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 7558 "MachineIndependent/glslang_tab.cpp" + break; + + case 216: /* type_parameter_specifier_opt: type_parameter_specifier */ +#line 1751 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[0].interm.typeParameters); + } +#line 7566 "MachineIndependent/glslang_tab.cpp" + break; + + case 217: /* type_parameter_specifier_opt: %empty */ +#line 1754 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = 0; + } +#line 7574 "MachineIndependent/glslang_tab.cpp" + break; + + case 218: /* type_parameter_specifier: LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE */ +#line 1760 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-1].interm.typeParameters); + } +#line 7582 "MachineIndependent/glslang_tab.cpp" + break; + + case 219: /* type_parameter_specifier_list: type_specifier */ +#line 1766 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = new TTypeParameters; + (yyval.interm.typeParameters)->arraySizes = new TArraySizes; + (yyval.interm.typeParameters)->spirvType = (yyvsp[0].interm.type).spirvType; + (yyval.interm.typeParameters)->basicType = (yyvsp[0].interm.type).basicType; + } +#line 7593 "MachineIndependent/glslang_tab.cpp" + break; + + case 220: /* type_parameter_specifier_list: unary_expression */ +#line 1772 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = new TTypeParameters; + (yyval.interm.typeParameters)->arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter", true); + (yyval.interm.typeParameters)->arraySizes->addInnerSize(size); + } +#line 7606 "MachineIndependent/glslang_tab.cpp" + break; + + case 221: /* type_parameter_specifier_list: type_parameter_specifier_list COMMA unary_expression */ +#line 1780 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-2].interm.typeParameters); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter", true); + (yyval.interm.typeParameters)->arraySizes->addInnerSize(size); + } +#line 7618 "MachineIndependent/glslang_tab.cpp" + break; + + case 222: /* type_specifier_nonarray: VOID */ +#line 1790 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtVoid; + } +#line 7627 "MachineIndependent/glslang_tab.cpp" + break; + + case 223: /* type_specifier_nonarray: FLOAT */ +#line 1794 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 7636 "MachineIndependent/glslang_tab.cpp" + break; + + case 224: /* type_specifier_nonarray: INT */ +#line 1798 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 7645 "MachineIndependent/glslang_tab.cpp" + break; + + case 225: /* type_specifier_nonarray: UINT */ +#line 1802 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 7655 "MachineIndependent/glslang_tab.cpp" + break; + + case 226: /* type_specifier_nonarray: BOOL */ +#line 1807 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + } +#line 7664 "MachineIndependent/glslang_tab.cpp" + break; + + case 227: /* type_specifier_nonarray: VEC2 */ +#line 1811 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 7674 "MachineIndependent/glslang_tab.cpp" + break; + + case 228: /* type_specifier_nonarray: VEC3 */ +#line 1816 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 7684 "MachineIndependent/glslang_tab.cpp" + break; + + case 229: /* type_specifier_nonarray: VEC4 */ +#line 1821 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 7694 "MachineIndependent/glslang_tab.cpp" + break; + + case 230: /* type_specifier_nonarray: BVEC2 */ +#line 1826 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(2); + } +#line 7704 "MachineIndependent/glslang_tab.cpp" + break; + + case 231: /* type_specifier_nonarray: BVEC3 */ +#line 1831 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(3); + } +#line 7714 "MachineIndependent/glslang_tab.cpp" + break; + + case 232: /* type_specifier_nonarray: BVEC4 */ +#line 1836 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(4); + } +#line 7724 "MachineIndependent/glslang_tab.cpp" + break; + + case 233: /* type_specifier_nonarray: IVEC2 */ +#line 1841 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 7734 "MachineIndependent/glslang_tab.cpp" + break; + + case 234: /* type_specifier_nonarray: IVEC3 */ +#line 1846 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 7744 "MachineIndependent/glslang_tab.cpp" + break; + + case 235: /* type_specifier_nonarray: IVEC4 */ +#line 1851 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 7754 "MachineIndependent/glslang_tab.cpp" + break; + + case 236: /* type_specifier_nonarray: UVEC2 */ +#line 1856 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 7765 "MachineIndependent/glslang_tab.cpp" + break; + + case 237: /* type_specifier_nonarray: UVEC3 */ +#line 1862 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 7776 "MachineIndependent/glslang_tab.cpp" + break; + + case 238: /* type_specifier_nonarray: UVEC4 */ +#line 1868 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 7787 "MachineIndependent/glslang_tab.cpp" + break; + + case 239: /* type_specifier_nonarray: MAT2 */ +#line 1874 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7797 "MachineIndependent/glslang_tab.cpp" + break; + + case 240: /* type_specifier_nonarray: MAT3 */ +#line 1879 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7807 "MachineIndependent/glslang_tab.cpp" + break; + + case 241: /* type_specifier_nonarray: MAT4 */ +#line 1884 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7817 "MachineIndependent/glslang_tab.cpp" + break; + + case 242: /* type_specifier_nonarray: MAT2X2 */ +#line 1889 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7827 "MachineIndependent/glslang_tab.cpp" + break; + + case 243: /* type_specifier_nonarray: MAT2X3 */ +#line 1894 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7837 "MachineIndependent/glslang_tab.cpp" + break; + + case 244: /* type_specifier_nonarray: MAT2X4 */ +#line 1899 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7847 "MachineIndependent/glslang_tab.cpp" + break; + + case 245: /* type_specifier_nonarray: MAT3X2 */ +#line 1904 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7857 "MachineIndependent/glslang_tab.cpp" + break; + + case 246: /* type_specifier_nonarray: MAT3X3 */ +#line 1909 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7867 "MachineIndependent/glslang_tab.cpp" + break; + + case 247: /* type_specifier_nonarray: MAT3X4 */ +#line 1914 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7877 "MachineIndependent/glslang_tab.cpp" + break; + + case 248: /* type_specifier_nonarray: MAT4X2 */ +#line 1919 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7887 "MachineIndependent/glslang_tab.cpp" + break; + + case 249: /* type_specifier_nonarray: MAT4X3 */ +#line 1924 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7897 "MachineIndependent/glslang_tab.cpp" + break; + + case 250: /* type_specifier_nonarray: MAT4X4 */ +#line 1929 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7907 "MachineIndependent/glslang_tab.cpp" + break; + + case 251: /* type_specifier_nonarray: DOUBLE */ +#line 1934 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 7919 "MachineIndependent/glslang_tab.cpp" + break; + + case 252: /* type_specifier_nonarray: BFLOAT16_T */ +#line 1941 "MachineIndependent/glslang.y" + { + parseContext.bfloat16ScalarVectorCheck((yyvsp[0].lex).loc, "bfloat16_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBFloat16; + } +#line 7929 "MachineIndependent/glslang_tab.cpp" + break; + + case 253: /* type_specifier_nonarray: FLOAT16_T */ +#line 1946 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + } +#line 7939 "MachineIndependent/glslang_tab.cpp" + break; + + case 254: /* type_specifier_nonarray: FLOAT32_T */ +#line 1951 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 7949 "MachineIndependent/glslang_tab.cpp" + break; + + case 255: /* type_specifier_nonarray: FLOAT64_T */ +#line 1956 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 7959 "MachineIndependent/glslang_tab.cpp" + break; + + case 256: /* type_specifier_nonarray: INT8_T */ +#line 1961 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + } +#line 7969 "MachineIndependent/glslang_tab.cpp" + break; + + case 257: /* type_specifier_nonarray: UINT8_T */ +#line 1966 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + } +#line 7979 "MachineIndependent/glslang_tab.cpp" + break; + + case 258: /* type_specifier_nonarray: INT16_T */ +#line 1971 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + } +#line 7989 "MachineIndependent/glslang_tab.cpp" + break; + + case 259: /* type_specifier_nonarray: UINT16_T */ +#line 1976 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + } +#line 7999 "MachineIndependent/glslang_tab.cpp" + break; + + case 260: /* type_specifier_nonarray: INT32_T */ +#line 1981 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 8009 "MachineIndependent/glslang_tab.cpp" + break; + + case 261: /* type_specifier_nonarray: UINT32_T */ +#line 1986 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 8019 "MachineIndependent/glslang_tab.cpp" + break; + + case 262: /* type_specifier_nonarray: INT64_T */ +#line 1991 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + } +#line 8029 "MachineIndependent/glslang_tab.cpp" + break; + + case 263: /* type_specifier_nonarray: UINT64_T */ +#line 1996 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + } +#line 8039 "MachineIndependent/glslang_tab.cpp" + break; + + case 264: /* type_specifier_nonarray: DVEC2 */ +#line 2001 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 8052 "MachineIndependent/glslang_tab.cpp" + break; + + case 265: /* type_specifier_nonarray: DVEC3 */ +#line 2009 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 8065 "MachineIndependent/glslang_tab.cpp" + break; + + case 266: /* type_specifier_nonarray: DVEC4 */ +#line 2017 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 8078 "MachineIndependent/glslang_tab.cpp" + break; + + case 267: /* type_specifier_nonarray: BF16VEC2 */ +#line 2025 "MachineIndependent/glslang.y" + { + parseContext.bfloat16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBFloat16; + (yyval.interm.type).setVector(2); + } +#line 8089 "MachineIndependent/glslang_tab.cpp" + break; + + case 268: /* type_specifier_nonarray: BF16VEC3 */ +#line 2031 "MachineIndependent/glslang.y" + { + parseContext.bfloat16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBFloat16; + (yyval.interm.type).setVector(3); + } +#line 8100 "MachineIndependent/glslang_tab.cpp" + break; + + case 269: /* type_specifier_nonarray: BF16VEC4 */ +#line 2037 "MachineIndependent/glslang.y" + { + parseContext.bfloat16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBFloat16; + (yyval.interm.type).setVector(4); + } +#line 8111 "MachineIndependent/glslang_tab.cpp" + break; + + case 270: /* type_specifier_nonarray: F16VEC2 */ +#line 2043 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(2); + } +#line 8122 "MachineIndependent/glslang_tab.cpp" + break; + + case 271: /* type_specifier_nonarray: F16VEC3 */ +#line 2049 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(3); + } +#line 8133 "MachineIndependent/glslang_tab.cpp" + break; + + case 272: /* type_specifier_nonarray: F16VEC4 */ +#line 2055 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(4); + } +#line 8144 "MachineIndependent/glslang_tab.cpp" + break; + + case 273: /* type_specifier_nonarray: F32VEC2 */ +#line 2061 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 8155 "MachineIndependent/glslang_tab.cpp" + break; + + case 274: /* type_specifier_nonarray: F32VEC3 */ +#line 2067 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 8166 "MachineIndependent/glslang_tab.cpp" + break; + + case 275: /* type_specifier_nonarray: F32VEC4 */ +#line 2073 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 8177 "MachineIndependent/glslang_tab.cpp" + break; + + case 276: /* type_specifier_nonarray: F64VEC2 */ +#line 2079 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 8188 "MachineIndependent/glslang_tab.cpp" + break; + + case 277: /* type_specifier_nonarray: F64VEC3 */ +#line 2085 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 8199 "MachineIndependent/glslang_tab.cpp" + break; + + case 278: /* type_specifier_nonarray: F64VEC4 */ +#line 2091 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 8210 "MachineIndependent/glslang_tab.cpp" + break; + + case 279: /* type_specifier_nonarray: I8VEC2 */ +#line 2097 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(2); + } +#line 8221 "MachineIndependent/glslang_tab.cpp" + break; + + case 280: /* type_specifier_nonarray: I8VEC3 */ +#line 2103 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(3); + } +#line 8232 "MachineIndependent/glslang_tab.cpp" + break; + + case 281: /* type_specifier_nonarray: I8VEC4 */ +#line 2109 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(4); + } +#line 8243 "MachineIndependent/glslang_tab.cpp" + break; + + case 282: /* type_specifier_nonarray: I16VEC2 */ +#line 2115 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(2); + } +#line 8254 "MachineIndependent/glslang_tab.cpp" + break; + + case 283: /* type_specifier_nonarray: I16VEC3 */ +#line 2121 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(3); + } +#line 8265 "MachineIndependent/glslang_tab.cpp" + break; + + case 284: /* type_specifier_nonarray: I16VEC4 */ +#line 2127 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(4); + } +#line 8276 "MachineIndependent/glslang_tab.cpp" + break; + + case 285: /* type_specifier_nonarray: I32VEC2 */ +#line 2133 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 8287 "MachineIndependent/glslang_tab.cpp" + break; + + case 286: /* type_specifier_nonarray: I32VEC3 */ +#line 2139 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 8298 "MachineIndependent/glslang_tab.cpp" + break; + + case 287: /* type_specifier_nonarray: I32VEC4 */ +#line 2145 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 8309 "MachineIndependent/glslang_tab.cpp" + break; + + case 288: /* type_specifier_nonarray: I64VEC2 */ +#line 2151 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(2); + } +#line 8320 "MachineIndependent/glslang_tab.cpp" + break; + + case 289: /* type_specifier_nonarray: I64VEC3 */ +#line 2157 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(3); + } +#line 8331 "MachineIndependent/glslang_tab.cpp" + break; + + case 290: /* type_specifier_nonarray: I64VEC4 */ +#line 2163 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(4); + } +#line 8342 "MachineIndependent/glslang_tab.cpp" + break; + + case 291: /* type_specifier_nonarray: U8VEC2 */ +#line 2169 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(2); + } +#line 8353 "MachineIndependent/glslang_tab.cpp" + break; + + case 292: /* type_specifier_nonarray: U8VEC3 */ +#line 2175 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(3); + } +#line 8364 "MachineIndependent/glslang_tab.cpp" + break; + + case 293: /* type_specifier_nonarray: U8VEC4 */ +#line 2181 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(4); + } +#line 8375 "MachineIndependent/glslang_tab.cpp" + break; + + case 294: /* type_specifier_nonarray: U16VEC2 */ +#line 2187 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(2); + } +#line 8386 "MachineIndependent/glslang_tab.cpp" + break; + + case 295: /* type_specifier_nonarray: U16VEC3 */ +#line 2193 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(3); + } +#line 8397 "MachineIndependent/glslang_tab.cpp" + break; + + case 296: /* type_specifier_nonarray: U16VEC4 */ +#line 2199 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(4); + } +#line 8408 "MachineIndependent/glslang_tab.cpp" + break; + + case 297: /* type_specifier_nonarray: U32VEC2 */ +#line 2205 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 8419 "MachineIndependent/glslang_tab.cpp" + break; + + case 298: /* type_specifier_nonarray: U32VEC3 */ +#line 2211 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 8430 "MachineIndependent/glslang_tab.cpp" + break; + + case 299: /* type_specifier_nonarray: U32VEC4 */ +#line 2217 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 8441 "MachineIndependent/glslang_tab.cpp" + break; + + case 300: /* type_specifier_nonarray: U64VEC2 */ +#line 2223 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(2); + } +#line 8452 "MachineIndependent/glslang_tab.cpp" + break; + + case 301: /* type_specifier_nonarray: U64VEC3 */ +#line 2229 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(3); + } +#line 8463 "MachineIndependent/glslang_tab.cpp" + break; + + case 302: /* type_specifier_nonarray: U64VEC4 */ +#line 2235 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(4); + } +#line 8474 "MachineIndependent/glslang_tab.cpp" + break; + + case 303: /* type_specifier_nonarray: DMAT2 */ +#line 2241 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8487 "MachineIndependent/glslang_tab.cpp" + break; + + case 304: /* type_specifier_nonarray: DMAT3 */ +#line 2249 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8500 "MachineIndependent/glslang_tab.cpp" + break; + + case 305: /* type_specifier_nonarray: DMAT4 */ +#line 2257 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8513 "MachineIndependent/glslang_tab.cpp" + break; + + case 306: /* type_specifier_nonarray: DMAT2X2 */ +#line 2265 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8526 "MachineIndependent/glslang_tab.cpp" + break; + + case 307: /* type_specifier_nonarray: DMAT2X3 */ +#line 2273 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 8539 "MachineIndependent/glslang_tab.cpp" + break; + + case 308: /* type_specifier_nonarray: DMAT2X4 */ +#line 2281 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 8552 "MachineIndependent/glslang_tab.cpp" + break; + + case 309: /* type_specifier_nonarray: DMAT3X2 */ +#line 2289 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 8565 "MachineIndependent/glslang_tab.cpp" + break; + + case 310: /* type_specifier_nonarray: DMAT3X3 */ +#line 2297 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8578 "MachineIndependent/glslang_tab.cpp" + break; + + case 311: /* type_specifier_nonarray: DMAT3X4 */ +#line 2305 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 8591 "MachineIndependent/glslang_tab.cpp" + break; + + case 312: /* type_specifier_nonarray: DMAT4X2 */ +#line 2313 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 8604 "MachineIndependent/glslang_tab.cpp" + break; + + case 313: /* type_specifier_nonarray: DMAT4X3 */ +#line 2321 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 8617 "MachineIndependent/glslang_tab.cpp" + break; + + case 314: /* type_specifier_nonarray: DMAT4X4 */ +#line 2329 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8630 "MachineIndependent/glslang_tab.cpp" + break; + + case 315: /* type_specifier_nonarray: F16MAT2 */ +#line 2337 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8641 "MachineIndependent/glslang_tab.cpp" + break; + + case 316: /* type_specifier_nonarray: F16MAT3 */ +#line 2343 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8652 "MachineIndependent/glslang_tab.cpp" + break; + + case 317: /* type_specifier_nonarray: F16MAT4 */ +#line 2349 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8663 "MachineIndependent/glslang_tab.cpp" + break; + + case 318: /* type_specifier_nonarray: F16MAT2X2 */ +#line 2355 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8674 "MachineIndependent/glslang_tab.cpp" + break; + + case 319: /* type_specifier_nonarray: F16MAT2X3 */ +#line 2361 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 3); + } +#line 8685 "MachineIndependent/glslang_tab.cpp" + break; + + case 320: /* type_specifier_nonarray: F16MAT2X4 */ +#line 2367 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 4); + } +#line 8696 "MachineIndependent/glslang_tab.cpp" + break; + + case 321: /* type_specifier_nonarray: F16MAT3X2 */ +#line 2373 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 2); + } +#line 8707 "MachineIndependent/glslang_tab.cpp" + break; + + case 322: /* type_specifier_nonarray: F16MAT3X3 */ +#line 2379 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8718 "MachineIndependent/glslang_tab.cpp" + break; + + case 323: /* type_specifier_nonarray: F16MAT3X4 */ +#line 2385 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 4); + } +#line 8729 "MachineIndependent/glslang_tab.cpp" + break; + + case 324: /* type_specifier_nonarray: F16MAT4X2 */ +#line 2391 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 2); + } +#line 8740 "MachineIndependent/glslang_tab.cpp" + break; + + case 325: /* type_specifier_nonarray: F16MAT4X3 */ +#line 2397 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 3); + } +#line 8751 "MachineIndependent/glslang_tab.cpp" + break; + + case 326: /* type_specifier_nonarray: F16MAT4X4 */ +#line 2403 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8762 "MachineIndependent/glslang_tab.cpp" + break; + + case 327: /* type_specifier_nonarray: F32MAT2 */ +#line 2409 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8773 "MachineIndependent/glslang_tab.cpp" + break; + + case 328: /* type_specifier_nonarray: F32MAT3 */ +#line 2415 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8784 "MachineIndependent/glslang_tab.cpp" + break; + + case 329: /* type_specifier_nonarray: F32MAT4 */ +#line 2421 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8795 "MachineIndependent/glslang_tab.cpp" + break; + + case 330: /* type_specifier_nonarray: F32MAT2X2 */ +#line 2427 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8806 "MachineIndependent/glslang_tab.cpp" + break; + + case 331: /* type_specifier_nonarray: F32MAT2X3 */ +#line 2433 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 8817 "MachineIndependent/glslang_tab.cpp" + break; + + case 332: /* type_specifier_nonarray: F32MAT2X4 */ +#line 2439 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 8828 "MachineIndependent/glslang_tab.cpp" + break; + + case 333: /* type_specifier_nonarray: F32MAT3X2 */ +#line 2445 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 8839 "MachineIndependent/glslang_tab.cpp" + break; + + case 334: /* type_specifier_nonarray: F32MAT3X3 */ +#line 2451 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8850 "MachineIndependent/glslang_tab.cpp" + break; + + case 335: /* type_specifier_nonarray: F32MAT3X4 */ +#line 2457 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 8861 "MachineIndependent/glslang_tab.cpp" + break; + + case 336: /* type_specifier_nonarray: F32MAT4X2 */ +#line 2463 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 8872 "MachineIndependent/glslang_tab.cpp" + break; + + case 337: /* type_specifier_nonarray: F32MAT4X3 */ +#line 2469 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 8883 "MachineIndependent/glslang_tab.cpp" + break; + + case 338: /* type_specifier_nonarray: F32MAT4X4 */ +#line 2475 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8894 "MachineIndependent/glslang_tab.cpp" + break; + + case 339: /* type_specifier_nonarray: F64MAT2 */ +#line 2481 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8905 "MachineIndependent/glslang_tab.cpp" + break; + + case 340: /* type_specifier_nonarray: F64MAT3 */ +#line 2487 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8916 "MachineIndependent/glslang_tab.cpp" + break; + + case 341: /* type_specifier_nonarray: F64MAT4 */ +#line 2493 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8927 "MachineIndependent/glslang_tab.cpp" + break; + + case 342: /* type_specifier_nonarray: F64MAT2X2 */ +#line 2499 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 8938 "MachineIndependent/glslang_tab.cpp" + break; + + case 343: /* type_specifier_nonarray: F64MAT2X3 */ +#line 2505 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 8949 "MachineIndependent/glslang_tab.cpp" + break; + + case 344: /* type_specifier_nonarray: F64MAT2X4 */ +#line 2511 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 8960 "MachineIndependent/glslang_tab.cpp" + break; + + case 345: /* type_specifier_nonarray: F64MAT3X2 */ +#line 2517 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 8971 "MachineIndependent/glslang_tab.cpp" + break; + + case 346: /* type_specifier_nonarray: F64MAT3X3 */ +#line 2523 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8982 "MachineIndependent/glslang_tab.cpp" + break; + + case 347: /* type_specifier_nonarray: F64MAT3X4 */ +#line 2529 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 8993 "MachineIndependent/glslang_tab.cpp" + break; + + case 348: /* type_specifier_nonarray: F64MAT4X2 */ +#line 2535 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 9004 "MachineIndependent/glslang_tab.cpp" + break; + + case 349: /* type_specifier_nonarray: F64MAT4X3 */ +#line 2541 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 9015 "MachineIndependent/glslang_tab.cpp" + break; + + case 350: /* type_specifier_nonarray: F64MAT4X4 */ +#line 2547 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 9026 "MachineIndependent/glslang_tab.cpp" + break; + + case 351: /* type_specifier_nonarray: ACCSTRUCTNV */ +#line 2553 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 9035 "MachineIndependent/glslang_tab.cpp" + break; + + case 352: /* type_specifier_nonarray: ACCSTRUCTEXT */ +#line 2557 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 9044 "MachineIndependent/glslang_tab.cpp" + break; + + case 353: /* type_specifier_nonarray: RAYQUERYEXT */ +#line 2561 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtRayQuery; + } +#line 9053 "MachineIndependent/glslang_tab.cpp" + break; + + case 354: /* type_specifier_nonarray: ATOMIC_UINT */ +#line 2565 "MachineIndependent/glslang.y" + { + parseContext.vulkanRemoved((yyvsp[0].lex).loc, "atomic counter types"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAtomicUint; + } +#line 9063 "MachineIndependent/glslang_tab.cpp" + break; + + case 355: /* type_specifier_nonarray: SAMPLER1D */ +#line 2570 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D); + } +#line 9073 "MachineIndependent/glslang_tab.cpp" + break; + + case 356: /* type_specifier_nonarray: SAMPLER2D */ +#line 2575 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + } +#line 9083 "MachineIndependent/glslang_tab.cpp" + break; + + case 357: /* type_specifier_nonarray: SAMPLER3D */ +#line 2580 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd3D); + } +#line 9093 "MachineIndependent/glslang_tab.cpp" + break; + + case 358: /* type_specifier_nonarray: SAMPLERCUBE */ +#line 2585 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube); + } +#line 9103 "MachineIndependent/glslang_tab.cpp" + break; + + case 359: /* type_specifier_nonarray: SAMPLER2DSHADOW */ +#line 2590 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, true); + } +#line 9113 "MachineIndependent/glslang_tab.cpp" + break; + + case 360: /* type_specifier_nonarray: SAMPLERCUBESHADOW */ +#line 2595 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, false, true); + } +#line 9123 "MachineIndependent/glslang_tab.cpp" + break; + + case 361: /* type_specifier_nonarray: SAMPLER2DARRAY */ +#line 2600 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true); + } +#line 9133 "MachineIndependent/glslang_tab.cpp" + break; + + case 362: /* type_specifier_nonarray: SAMPLER2DARRAYSHADOW */ +#line 2605 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, true); + } +#line 9143 "MachineIndependent/glslang_tab.cpp" + break; + + case 363: /* type_specifier_nonarray: SAMPLER1DSHADOW */ +#line 2610 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, false, true); + } +#line 9153 "MachineIndependent/glslang_tab.cpp" + break; + + case 364: /* type_specifier_nonarray: SAMPLER1DARRAY */ +#line 2615 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true); + } +#line 9163 "MachineIndependent/glslang_tab.cpp" + break; + + case 365: /* type_specifier_nonarray: SAMPLER1DARRAYSHADOW */ +#line 2620 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true, true); + } +#line 9173 "MachineIndependent/glslang_tab.cpp" + break; + + case 366: /* type_specifier_nonarray: SAMPLERCUBEARRAY */ +#line 2625 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true); + } +#line 9183 "MachineIndependent/glslang_tab.cpp" + break; + + case 367: /* type_specifier_nonarray: SAMPLERCUBEARRAYSHADOW */ +#line 2630 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true, true); + } +#line 9193 "MachineIndependent/glslang_tab.cpp" + break; + + case 368: /* type_specifier_nonarray: F16SAMPLER1D */ +#line 2635 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D); + } +#line 9204 "MachineIndependent/glslang_tab.cpp" + break; + + case 369: /* type_specifier_nonarray: F16SAMPLER2D */ +#line 2641 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D); + } +#line 9215 "MachineIndependent/glslang_tab.cpp" + break; + + case 370: /* type_specifier_nonarray: F16SAMPLER3D */ +#line 2647 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd3D); + } +#line 9226 "MachineIndependent/glslang_tab.cpp" + break; + + case 371: /* type_specifier_nonarray: F16SAMPLERCUBE */ +#line 2653 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube); + } +#line 9237 "MachineIndependent/glslang_tab.cpp" + break; + + case 372: /* type_specifier_nonarray: F16SAMPLER1DSHADOW */ +#line 2659 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, false, true); + } +#line 9248 "MachineIndependent/glslang_tab.cpp" + break; + + case 373: /* type_specifier_nonarray: F16SAMPLER2DSHADOW */ +#line 2665 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, true); + } +#line 9259 "MachineIndependent/glslang_tab.cpp" + break; + + case 374: /* type_specifier_nonarray: F16SAMPLERCUBESHADOW */ +#line 2671 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, false, true); + } +#line 9270 "MachineIndependent/glslang_tab.cpp" + break; + + case 375: /* type_specifier_nonarray: F16SAMPLER1DARRAY */ +#line 2677 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true); + } +#line 9281 "MachineIndependent/glslang_tab.cpp" + break; + + case 376: /* type_specifier_nonarray: F16SAMPLER2DARRAY */ +#line 2683 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true); + } +#line 9292 "MachineIndependent/glslang_tab.cpp" + break; + + case 377: /* type_specifier_nonarray: F16SAMPLER1DARRAYSHADOW */ +#line 2689 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true, true); + } +#line 9303 "MachineIndependent/glslang_tab.cpp" + break; + + case 378: /* type_specifier_nonarray: F16SAMPLER2DARRAYSHADOW */ +#line 2695 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, true); + } +#line 9314 "MachineIndependent/glslang_tab.cpp" + break; + + case 379: /* type_specifier_nonarray: F16SAMPLERCUBEARRAY */ +#line 2701 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true); + } +#line 9325 "MachineIndependent/glslang_tab.cpp" + break; + + case 380: /* type_specifier_nonarray: F16SAMPLERCUBEARRAYSHADOW */ +#line 2707 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true, true); + } +#line 9336 "MachineIndependent/glslang_tab.cpp" + break; + + case 381: /* type_specifier_nonarray: ISAMPLER1D */ +#line 2713 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D); + } +#line 9346 "MachineIndependent/glslang_tab.cpp" + break; + + case 382: /* type_specifier_nonarray: ISAMPLER2D */ +#line 2718 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D); + } +#line 9356 "MachineIndependent/glslang_tab.cpp" + break; + + case 383: /* type_specifier_nonarray: ISAMPLER3D */ +#line 2723 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd3D); + } +#line 9366 "MachineIndependent/glslang_tab.cpp" + break; + + case 384: /* type_specifier_nonarray: ISAMPLERCUBE */ +#line 2728 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube); + } +#line 9376 "MachineIndependent/glslang_tab.cpp" + break; + + case 385: /* type_specifier_nonarray: ISAMPLER2DARRAY */ +#line 2733 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true); + } +#line 9386 "MachineIndependent/glslang_tab.cpp" + break; + + case 386: /* type_specifier_nonarray: USAMPLER2D */ +#line 2738 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D); + } +#line 9396 "MachineIndependent/glslang_tab.cpp" + break; + + case 387: /* type_specifier_nonarray: USAMPLER3D */ +#line 2743 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd3D); + } +#line 9406 "MachineIndependent/glslang_tab.cpp" + break; + + case 388: /* type_specifier_nonarray: USAMPLERCUBE */ +#line 2748 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube); + } +#line 9416 "MachineIndependent/glslang_tab.cpp" + break; + + case 389: /* type_specifier_nonarray: ISAMPLER1DARRAY */ +#line 2753 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D, true); + } +#line 9426 "MachineIndependent/glslang_tab.cpp" + break; + + case 390: /* type_specifier_nonarray: ISAMPLERCUBEARRAY */ +#line 2758 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube, true); + } +#line 9436 "MachineIndependent/glslang_tab.cpp" + break; + + case 391: /* type_specifier_nonarray: USAMPLER1D */ +#line 2763 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D); + } +#line 9446 "MachineIndependent/glslang_tab.cpp" + break; + + case 392: /* type_specifier_nonarray: USAMPLER1DARRAY */ +#line 2768 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D, true); + } +#line 9456 "MachineIndependent/glslang_tab.cpp" + break; + + case 393: /* type_specifier_nonarray: USAMPLERCUBEARRAY */ +#line 2773 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube, true); + } +#line 9466 "MachineIndependent/glslang_tab.cpp" + break; + + case 394: /* type_specifier_nonarray: TEXTURECUBEARRAY */ +#line 2778 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube, true); + } +#line 9476 "MachineIndependent/glslang_tab.cpp" + break; + + case 395: /* type_specifier_nonarray: ITEXTURECUBEARRAY */ +#line 2783 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube, true); + } +#line 9486 "MachineIndependent/glslang_tab.cpp" + break; + + case 396: /* type_specifier_nonarray: UTEXTURECUBEARRAY */ +#line 2788 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube, true); + } +#line 9496 "MachineIndependent/glslang_tab.cpp" + break; + + case 397: /* type_specifier_nonarray: USAMPLER2DARRAY */ +#line 2793 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true); + } +#line 9506 "MachineIndependent/glslang_tab.cpp" + break; + + case 398: /* type_specifier_nonarray: TEXTURE2D */ +#line 2798 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D); + } +#line 9516 "MachineIndependent/glslang_tab.cpp" + break; + + case 399: /* type_specifier_nonarray: TEXTURE3D */ +#line 2803 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd3D); + } +#line 9526 "MachineIndependent/glslang_tab.cpp" + break; + + case 400: /* type_specifier_nonarray: TEXTURE2DARRAY */ +#line 2808 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true); + } +#line 9536 "MachineIndependent/glslang_tab.cpp" + break; + + case 401: /* type_specifier_nonarray: TEXTURECUBE */ +#line 2813 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube); + } +#line 9546 "MachineIndependent/glslang_tab.cpp" + break; + + case 402: /* type_specifier_nonarray: ITEXTURE2D */ +#line 2818 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D); + } +#line 9556 "MachineIndependent/glslang_tab.cpp" + break; + + case 403: /* type_specifier_nonarray: ITEXTURE3D */ +#line 2823 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd3D); + } +#line 9566 "MachineIndependent/glslang_tab.cpp" + break; + + case 404: /* type_specifier_nonarray: ITEXTURECUBE */ +#line 2828 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube); + } +#line 9576 "MachineIndependent/glslang_tab.cpp" + break; + + case 405: /* type_specifier_nonarray: ITEXTURE2DARRAY */ +#line 2833 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true); + } +#line 9586 "MachineIndependent/glslang_tab.cpp" + break; + + case 406: /* type_specifier_nonarray: UTEXTURE2D */ +#line 2838 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D); + } +#line 9596 "MachineIndependent/glslang_tab.cpp" + break; + + case 407: /* type_specifier_nonarray: UTEXTURE3D */ +#line 2843 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd3D); + } +#line 9606 "MachineIndependent/glslang_tab.cpp" + break; + + case 408: /* type_specifier_nonarray: UTEXTURECUBE */ +#line 2848 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube); + } +#line 9616 "MachineIndependent/glslang_tab.cpp" + break; + + case 409: /* type_specifier_nonarray: UTEXTURE2DARRAY */ +#line 2853 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true); + } +#line 9626 "MachineIndependent/glslang_tab.cpp" + break; + + case 410: /* type_specifier_nonarray: SAMPLER */ +#line 2858 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(false); + } +#line 9636 "MachineIndependent/glslang_tab.cpp" + break; + + case 411: /* type_specifier_nonarray: SAMPLERSHADOW */ +#line 2863 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(true); + } +#line 9646 "MachineIndependent/glslang_tab.cpp" + break; + + case 412: /* type_specifier_nonarray: SAMPLER2DRECT */ +#line 2868 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect); + } +#line 9656 "MachineIndependent/glslang_tab.cpp" + break; + + case 413: /* type_specifier_nonarray: SAMPLER2DRECTSHADOW */ +#line 2873 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect, false, true); + } +#line 9666 "MachineIndependent/glslang_tab.cpp" + break; + + case 414: /* type_specifier_nonarray: F16SAMPLER2DRECT */ +#line 2878 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect); + } +#line 9677 "MachineIndependent/glslang_tab.cpp" + break; + + case 415: /* type_specifier_nonarray: F16SAMPLER2DRECTSHADOW */ +#line 2884 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect, false, true); + } +#line 9688 "MachineIndependent/glslang_tab.cpp" + break; + + case 416: /* type_specifier_nonarray: ISAMPLER2DRECT */ +#line 2890 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdRect); + } +#line 9698 "MachineIndependent/glslang_tab.cpp" + break; + + case 417: /* type_specifier_nonarray: USAMPLER2DRECT */ +#line 2895 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdRect); + } +#line 9708 "MachineIndependent/glslang_tab.cpp" + break; + + case 418: /* type_specifier_nonarray: SAMPLERBUFFER */ +#line 2900 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdBuffer); + } +#line 9718 "MachineIndependent/glslang_tab.cpp" + break; + + case 419: /* type_specifier_nonarray: F16SAMPLERBUFFER */ +#line 2905 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdBuffer); + } +#line 9729 "MachineIndependent/glslang_tab.cpp" + break; + + case 420: /* type_specifier_nonarray: ISAMPLERBUFFER */ +#line 2911 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdBuffer); + } +#line 9739 "MachineIndependent/glslang_tab.cpp" + break; + + case 421: /* type_specifier_nonarray: USAMPLERBUFFER */ +#line 2916 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdBuffer); + } +#line 9749 "MachineIndependent/glslang_tab.cpp" + break; + + case 422: /* type_specifier_nonarray: SAMPLER2DMS */ +#line 2921 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, false, true); + } +#line 9759 "MachineIndependent/glslang_tab.cpp" + break; + + case 423: /* type_specifier_nonarray: F16SAMPLER2DMS */ +#line 2926 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, false, true); + } +#line 9770 "MachineIndependent/glslang_tab.cpp" + break; + + case 424: /* type_specifier_nonarray: ISAMPLER2DMS */ +#line 2932 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, false, false, true); + } +#line 9780 "MachineIndependent/glslang_tab.cpp" + break; + + case 425: /* type_specifier_nonarray: USAMPLER2DMS */ +#line 2937 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, false, false, true); + } +#line 9790 "MachineIndependent/glslang_tab.cpp" + break; + + case 426: /* type_specifier_nonarray: SAMPLER2DMSARRAY */ +#line 2942 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, false, true); + } +#line 9800 "MachineIndependent/glslang_tab.cpp" + break; + + case 427: /* type_specifier_nonarray: F16SAMPLER2DMSARRAY */ +#line 2947 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, false, true); + } +#line 9811 "MachineIndependent/glslang_tab.cpp" + break; + + case 428: /* type_specifier_nonarray: ISAMPLER2DMSARRAY */ +#line 2953 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true, false, true); + } +#line 9821 "MachineIndependent/glslang_tab.cpp" + break; + + case 429: /* type_specifier_nonarray: USAMPLER2DMSARRAY */ +#line 2958 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true, false, true); + } +#line 9831 "MachineIndependent/glslang_tab.cpp" + break; + + case 430: /* type_specifier_nonarray: TEXTURE1D */ +#line 2963 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D); + } +#line 9841 "MachineIndependent/glslang_tab.cpp" + break; + + case 431: /* type_specifier_nonarray: F16TEXTURE1D */ +#line 2968 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D); + } +#line 9852 "MachineIndependent/glslang_tab.cpp" + break; + + case 432: /* type_specifier_nonarray: F16TEXTURE2D */ +#line 2974 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D); + } +#line 9863 "MachineIndependent/glslang_tab.cpp" + break; + + case 433: /* type_specifier_nonarray: F16TEXTURE3D */ +#line 2980 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd3D); + } +#line 9874 "MachineIndependent/glslang_tab.cpp" + break; + + case 434: /* type_specifier_nonarray: F16TEXTURECUBE */ +#line 2986 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube); + } +#line 9885 "MachineIndependent/glslang_tab.cpp" + break; + + case 435: /* type_specifier_nonarray: TEXTURE1DARRAY */ +#line 2992 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D, true); + } +#line 9895 "MachineIndependent/glslang_tab.cpp" + break; + + case 436: /* type_specifier_nonarray: F16TEXTURE1DARRAY */ +#line 2997 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D, true); + } +#line 9906 "MachineIndependent/glslang_tab.cpp" + break; + + case 437: /* type_specifier_nonarray: F16TEXTURE2DARRAY */ +#line 3003 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true); + } +#line 9917 "MachineIndependent/glslang_tab.cpp" + break; + + case 438: /* type_specifier_nonarray: F16TEXTURECUBEARRAY */ +#line 3009 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube, true); + } +#line 9928 "MachineIndependent/glslang_tab.cpp" + break; + + case 439: /* type_specifier_nonarray: ITEXTURE1D */ +#line 3015 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D); + } +#line 9938 "MachineIndependent/glslang_tab.cpp" + break; + + case 440: /* type_specifier_nonarray: ITEXTURE1DARRAY */ +#line 3020 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D, true); + } +#line 9948 "MachineIndependent/glslang_tab.cpp" + break; + + case 441: /* type_specifier_nonarray: UTEXTURE1D */ +#line 3025 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D); + } +#line 9958 "MachineIndependent/glslang_tab.cpp" + break; + + case 442: /* type_specifier_nonarray: UTEXTURE1DARRAY */ +#line 3030 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D, true); + } +#line 9968 "MachineIndependent/glslang_tab.cpp" + break; + + case 443: /* type_specifier_nonarray: TEXTURE2DRECT */ +#line 3035 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdRect); + } +#line 9978 "MachineIndependent/glslang_tab.cpp" + break; + + case 444: /* type_specifier_nonarray: F16TEXTURE2DRECT */ +#line 3040 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdRect); + } +#line 9989 "MachineIndependent/glslang_tab.cpp" + break; + + case 445: /* type_specifier_nonarray: ITEXTURE2DRECT */ +#line 3046 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdRect); + } +#line 9999 "MachineIndependent/glslang_tab.cpp" + break; + + case 446: /* type_specifier_nonarray: UTEXTURE2DRECT */ +#line 3051 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdRect); + } +#line 10009 "MachineIndependent/glslang_tab.cpp" + break; + + case 447: /* type_specifier_nonarray: TEXTUREBUFFER */ +#line 3056 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdBuffer); + } +#line 10019 "MachineIndependent/glslang_tab.cpp" + break; + + case 448: /* type_specifier_nonarray: F16TEXTUREBUFFER */ +#line 3061 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdBuffer); + } +#line 10030 "MachineIndependent/glslang_tab.cpp" + break; + + case 449: /* type_specifier_nonarray: ITEXTUREBUFFER */ +#line 3067 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdBuffer); + } +#line 10040 "MachineIndependent/glslang_tab.cpp" + break; + + case 450: /* type_specifier_nonarray: UTEXTUREBUFFER */ +#line 3072 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdBuffer); + } +#line 10050 "MachineIndependent/glslang_tab.cpp" + break; + + case 451: /* type_specifier_nonarray: TEXTURE2DMS */ +#line 3077 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } +#line 10060 "MachineIndependent/glslang_tab.cpp" + break; + + case 452: /* type_specifier_nonarray: F16TEXTURE2DMS */ +#line 3082 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } +#line 10071 "MachineIndependent/glslang_tab.cpp" + break; + + case 453: /* type_specifier_nonarray: ITEXTURE2DMS */ +#line 3088 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, false, false, true); + } +#line 10081 "MachineIndependent/glslang_tab.cpp" + break; + + case 454: /* type_specifier_nonarray: UTEXTURE2DMS */ +#line 3093 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, false, false, true); + } +#line 10091 "MachineIndependent/glslang_tab.cpp" + break; + + case 455: /* type_specifier_nonarray: TEXTURE2DMSARRAY */ +#line 3098 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } +#line 10101 "MachineIndependent/glslang_tab.cpp" + break; + + case 456: /* type_specifier_nonarray: F16TEXTURE2DMSARRAY */ +#line 3103 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } +#line 10112 "MachineIndependent/glslang_tab.cpp" + break; + + case 457: /* type_specifier_nonarray: ITEXTURE2DMSARRAY */ +#line 3109 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true, false, true); + } +#line 10122 "MachineIndependent/glslang_tab.cpp" + break; + + case 458: /* type_specifier_nonarray: UTEXTURE2DMSARRAY */ +#line 3114 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true, false, true); + } +#line 10132 "MachineIndependent/glslang_tab.cpp" + break; + + case 459: /* type_specifier_nonarray: IMAGE1D */ +#line 3119 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D); + } +#line 10142 "MachineIndependent/glslang_tab.cpp" + break; + + case 460: /* type_specifier_nonarray: F16IMAGE1D */ +#line 3124 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D); + } +#line 10153 "MachineIndependent/glslang_tab.cpp" + break; + + case 461: /* type_specifier_nonarray: IIMAGE1D */ +#line 3130 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D); + } +#line 10163 "MachineIndependent/glslang_tab.cpp" + break; + + case 462: /* type_specifier_nonarray: UIMAGE1D */ +#line 3135 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D); + } +#line 10173 "MachineIndependent/glslang_tab.cpp" + break; + + case 463: /* type_specifier_nonarray: IMAGE2D */ +#line 3140 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D); + } +#line 10183 "MachineIndependent/glslang_tab.cpp" + break; + + case 464: /* type_specifier_nonarray: F16IMAGE2D */ +#line 3145 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D); + } +#line 10194 "MachineIndependent/glslang_tab.cpp" + break; + + case 465: /* type_specifier_nonarray: IIMAGE2D */ +#line 3151 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D); + } +#line 10204 "MachineIndependent/glslang_tab.cpp" + break; + + case 466: /* type_specifier_nonarray: UIMAGE2D */ +#line 3156 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D); + } +#line 10214 "MachineIndependent/glslang_tab.cpp" + break; + + case 467: /* type_specifier_nonarray: IMAGE3D */ +#line 3161 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd3D); + } +#line 10224 "MachineIndependent/glslang_tab.cpp" + break; + + case 468: /* type_specifier_nonarray: F16IMAGE3D */ +#line 3166 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd3D); + } +#line 10235 "MachineIndependent/glslang_tab.cpp" + break; + + case 469: /* type_specifier_nonarray: IIMAGE3D */ +#line 3172 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd3D); + } +#line 10245 "MachineIndependent/glslang_tab.cpp" + break; + + case 470: /* type_specifier_nonarray: UIMAGE3D */ +#line 3177 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd3D); + } +#line 10255 "MachineIndependent/glslang_tab.cpp" + break; + + case 471: /* type_specifier_nonarray: IMAGE2DRECT */ +#line 3182 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdRect); + } +#line 10265 "MachineIndependent/glslang_tab.cpp" + break; + + case 472: /* type_specifier_nonarray: F16IMAGE2DRECT */ +#line 3187 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdRect); + } +#line 10276 "MachineIndependent/glslang_tab.cpp" + break; + + case 473: /* type_specifier_nonarray: IIMAGE2DRECT */ +#line 3193 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdRect); + } +#line 10286 "MachineIndependent/glslang_tab.cpp" + break; + + case 474: /* type_specifier_nonarray: UIMAGE2DRECT */ +#line 3198 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdRect); + } +#line 10296 "MachineIndependent/glslang_tab.cpp" + break; + + case 475: /* type_specifier_nonarray: IMAGECUBE */ +#line 3203 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube); + } +#line 10306 "MachineIndependent/glslang_tab.cpp" + break; + + case 476: /* type_specifier_nonarray: F16IMAGECUBE */ +#line 3208 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube); + } +#line 10317 "MachineIndependent/glslang_tab.cpp" + break; + + case 477: /* type_specifier_nonarray: IIMAGECUBE */ +#line 3214 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube); + } +#line 10327 "MachineIndependent/glslang_tab.cpp" + break; + + case 478: /* type_specifier_nonarray: UIMAGECUBE */ +#line 3219 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube); + } +#line 10337 "MachineIndependent/glslang_tab.cpp" + break; + + case 479: /* type_specifier_nonarray: IMAGEBUFFER */ +#line 3224 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdBuffer); + } +#line 10347 "MachineIndependent/glslang_tab.cpp" + break; + + case 480: /* type_specifier_nonarray: F16IMAGEBUFFER */ +#line 3229 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdBuffer); + } +#line 10358 "MachineIndependent/glslang_tab.cpp" + break; + + case 481: /* type_specifier_nonarray: IIMAGEBUFFER */ +#line 3235 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdBuffer); + } +#line 10368 "MachineIndependent/glslang_tab.cpp" + break; + + case 482: /* type_specifier_nonarray: UIMAGEBUFFER */ +#line 3240 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdBuffer); + } +#line 10378 "MachineIndependent/glslang_tab.cpp" + break; + + case 483: /* type_specifier_nonarray: IMAGE1DARRAY */ +#line 3245 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D, true); + } +#line 10388 "MachineIndependent/glslang_tab.cpp" + break; + + case 484: /* type_specifier_nonarray: F16IMAGE1DARRAY */ +#line 3250 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D, true); + } +#line 10399 "MachineIndependent/glslang_tab.cpp" + break; + + case 485: /* type_specifier_nonarray: IIMAGE1DARRAY */ +#line 3256 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D, true); + } +#line 10409 "MachineIndependent/glslang_tab.cpp" + break; + + case 486: /* type_specifier_nonarray: UIMAGE1DARRAY */ +#line 3261 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D, true); + } +#line 10419 "MachineIndependent/glslang_tab.cpp" + break; + + case 487: /* type_specifier_nonarray: IMAGE2DARRAY */ +#line 3266 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true); + } +#line 10429 "MachineIndependent/glslang_tab.cpp" + break; + + case 488: /* type_specifier_nonarray: F16IMAGE2DARRAY */ +#line 3271 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true); + } +#line 10440 "MachineIndependent/glslang_tab.cpp" + break; + + case 489: /* type_specifier_nonarray: IIMAGE2DARRAY */ +#line 3277 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true); + } +#line 10450 "MachineIndependent/glslang_tab.cpp" + break; + + case 490: /* type_specifier_nonarray: UIMAGE2DARRAY */ +#line 3282 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true); + } +#line 10460 "MachineIndependent/glslang_tab.cpp" + break; + + case 491: /* type_specifier_nonarray: IMAGECUBEARRAY */ +#line 3287 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube, true); + } +#line 10470 "MachineIndependent/glslang_tab.cpp" + break; + + case 492: /* type_specifier_nonarray: F16IMAGECUBEARRAY */ +#line 3292 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube, true); + } +#line 10481 "MachineIndependent/glslang_tab.cpp" + break; + + case 493: /* type_specifier_nonarray: IIMAGECUBEARRAY */ +#line 3298 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube, true); + } +#line 10491 "MachineIndependent/glslang_tab.cpp" + break; + + case 494: /* type_specifier_nonarray: UIMAGECUBEARRAY */ +#line 3303 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube, true); + } +#line 10501 "MachineIndependent/glslang_tab.cpp" + break; + + case 495: /* type_specifier_nonarray: IMAGE2DMS */ +#line 3308 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, false, false, true); + } +#line 10511 "MachineIndependent/glslang_tab.cpp" + break; + + case 496: /* type_specifier_nonarray: F16IMAGE2DMS */ +#line 3313 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } +#line 10522 "MachineIndependent/glslang_tab.cpp" + break; + + case 497: /* type_specifier_nonarray: IIMAGE2DMS */ +#line 3319 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, false, false, true); + } +#line 10532 "MachineIndependent/glslang_tab.cpp" + break; + + case 498: /* type_specifier_nonarray: UIMAGE2DMS */ +#line 3324 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, false, false, true); + } +#line 10542 "MachineIndependent/glslang_tab.cpp" + break; + + case 499: /* type_specifier_nonarray: IMAGE2DMSARRAY */ +#line 3329 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true, false, true); + } +#line 10552 "MachineIndependent/glslang_tab.cpp" + break; + + case 500: /* type_specifier_nonarray: F16IMAGE2DMSARRAY */ +#line 3334 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } +#line 10563 "MachineIndependent/glslang_tab.cpp" + break; + + case 501: /* type_specifier_nonarray: IIMAGE2DMSARRAY */ +#line 3340 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true, false, true); + } +#line 10573 "MachineIndependent/glslang_tab.cpp" + break; + + case 502: /* type_specifier_nonarray: UIMAGE2DMSARRAY */ +#line 3345 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true, false, true); + } +#line 10583 "MachineIndependent/glslang_tab.cpp" + break; + + case 503: /* type_specifier_nonarray: I64IMAGE1D */ +#line 3350 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd1D); + } +#line 10593 "MachineIndependent/glslang_tab.cpp" + break; + + case 504: /* type_specifier_nonarray: U64IMAGE1D */ +#line 3355 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd1D); + } +#line 10603 "MachineIndependent/glslang_tab.cpp" + break; + + case 505: /* type_specifier_nonarray: I64IMAGE2D */ +#line 3360 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D); + } +#line 10613 "MachineIndependent/glslang_tab.cpp" + break; + + case 506: /* type_specifier_nonarray: U64IMAGE2D */ +#line 3365 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D); + } +#line 10623 "MachineIndependent/glslang_tab.cpp" + break; + + case 507: /* type_specifier_nonarray: I64IMAGE3D */ +#line 3370 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd3D); + } +#line 10633 "MachineIndependent/glslang_tab.cpp" + break; + + case 508: /* type_specifier_nonarray: U64IMAGE3D */ +#line 3375 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd3D); + } +#line 10643 "MachineIndependent/glslang_tab.cpp" + break; + + case 509: /* type_specifier_nonarray: I64IMAGE2DRECT */ +#line 3380 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdRect); + } +#line 10653 "MachineIndependent/glslang_tab.cpp" + break; + + case 510: /* type_specifier_nonarray: U64IMAGE2DRECT */ +#line 3385 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdRect); + } +#line 10663 "MachineIndependent/glslang_tab.cpp" + break; + + case 511: /* type_specifier_nonarray: I64IMAGECUBE */ +#line 3390 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdCube); + } +#line 10673 "MachineIndependent/glslang_tab.cpp" + break; + + case 512: /* type_specifier_nonarray: U64IMAGECUBE */ +#line 3395 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdCube); + } +#line 10683 "MachineIndependent/glslang_tab.cpp" + break; + + case 513: /* type_specifier_nonarray: I64IMAGEBUFFER */ +#line 3400 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdBuffer); + } +#line 10693 "MachineIndependent/glslang_tab.cpp" + break; + + case 514: /* type_specifier_nonarray: U64IMAGEBUFFER */ +#line 3405 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdBuffer); + } +#line 10703 "MachineIndependent/glslang_tab.cpp" + break; + + case 515: /* type_specifier_nonarray: I64IMAGE1DARRAY */ +#line 3410 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd1D, true); + } +#line 10713 "MachineIndependent/glslang_tab.cpp" + break; + + case 516: /* type_specifier_nonarray: U64IMAGE1DARRAY */ +#line 3415 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd1D, true); + } +#line 10723 "MachineIndependent/glslang_tab.cpp" + break; + + case 517: /* type_specifier_nonarray: I64IMAGE2DARRAY */ +#line 3420 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, true); + } +#line 10733 "MachineIndependent/glslang_tab.cpp" + break; + + case 518: /* type_specifier_nonarray: U64IMAGE2DARRAY */ +#line 3425 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, true); + } +#line 10743 "MachineIndependent/glslang_tab.cpp" + break; + + case 519: /* type_specifier_nonarray: I64IMAGECUBEARRAY */ +#line 3430 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdCube, true); + } +#line 10753 "MachineIndependent/glslang_tab.cpp" + break; + + case 520: /* type_specifier_nonarray: U64IMAGECUBEARRAY */ +#line 3435 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdCube, true); + } +#line 10763 "MachineIndependent/glslang_tab.cpp" + break; + + case 521: /* type_specifier_nonarray: I64IMAGE2DMS */ +#line 3440 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, false, false, true); + } +#line 10773 "MachineIndependent/glslang_tab.cpp" + break; + + case 522: /* type_specifier_nonarray: U64IMAGE2DMS */ +#line 3445 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, false, false, true); + } +#line 10783 "MachineIndependent/glslang_tab.cpp" + break; + + case 523: /* type_specifier_nonarray: I64IMAGE2DMSARRAY */ +#line 3450 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, true, false, true); + } +#line 10793 "MachineIndependent/glslang_tab.cpp" + break; + + case 524: /* type_specifier_nonarray: U64IMAGE2DMSARRAY */ +#line 3455 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, true, false, true); + } +#line 10803 "MachineIndependent/glslang_tab.cpp" + break; + + case 525: /* type_specifier_nonarray: SAMPLEREXTERNALOES */ +#line 3460 "MachineIndependent/glslang.y" + { // GL_OES_EGL_image_external + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.external = true; + } +#line 10814 "MachineIndependent/glslang_tab.cpp" + break; + + case 526: /* type_specifier_nonarray: SAMPLEREXTERNAL2DY2YEXT */ +#line 3466 "MachineIndependent/glslang.y" + { // GL_EXT_YUV_target + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.yuv = true; + } +#line 10825 "MachineIndependent/glslang_tab.cpp" + break; + + case 527: /* type_specifier_nonarray: ATTACHMENTEXT */ +#line 3472 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "attachmentEXT input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setAttachmentEXT(EbtFloat); + } +#line 10836 "MachineIndependent/glslang_tab.cpp" + break; + + case 528: /* type_specifier_nonarray: IATTACHMENTEXT */ +#line 3478 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "attachmentEXT input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setAttachmentEXT(EbtInt); + } +#line 10847 "MachineIndependent/glslang_tab.cpp" + break; + + case 529: /* type_specifier_nonarray: UATTACHMENTEXT */ +#line 3484 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "attachmentEXT input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setAttachmentEXT(EbtUint); + } +#line 10858 "MachineIndependent/glslang_tab.cpp" + break; + + case 530: /* type_specifier_nonarray: SUBPASSINPUT */ +#line 3490 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat); + } +#line 10869 "MachineIndependent/glslang_tab.cpp" + break; + + case 531: /* type_specifier_nonarray: SUBPASSINPUTMS */ +#line 3496 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat, true); + } +#line 10880 "MachineIndependent/glslang_tab.cpp" + break; + + case 532: /* type_specifier_nonarray: F16SUBPASSINPUT */ +#line 3502 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16); + } +#line 10892 "MachineIndependent/glslang_tab.cpp" + break; + + case 533: /* type_specifier_nonarray: F16SUBPASSINPUTMS */ +#line 3509 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16, true); + } +#line 10904 "MachineIndependent/glslang_tab.cpp" + break; + + case 534: /* type_specifier_nonarray: ISUBPASSINPUT */ +#line 3516 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt); + } +#line 10915 "MachineIndependent/glslang_tab.cpp" + break; + + case 535: /* type_specifier_nonarray: ISUBPASSINPUTMS */ +#line 3522 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt, true); + } +#line 10926 "MachineIndependent/glslang_tab.cpp" + break; + + case 536: /* type_specifier_nonarray: USUBPASSINPUT */ +#line 3528 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint); + } +#line 10937 "MachineIndependent/glslang_tab.cpp" + break; + + case 537: /* type_specifier_nonarray: USUBPASSINPUTMS */ +#line 3534 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint, true); + } +#line 10948 "MachineIndependent/glslang_tab.cpp" + break; + + case 538: /* type_specifier_nonarray: FCOOPMATNV */ +#line 3540 "MachineIndependent/glslang.y" + { + parseContext.fcoopmatCheckNV((yyvsp[0].lex).loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).coopmatNV = true; + (yyval.interm.type).coopmatKHR = false; + } +#line 10960 "MachineIndependent/glslang_tab.cpp" + break; + + case 539: /* type_specifier_nonarray: ICOOPMATNV */ +#line 3547 "MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheckNV((yyvsp[0].lex).loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).coopmatNV = true; + (yyval.interm.type).coopmatKHR = false; + } +#line 10972 "MachineIndependent/glslang_tab.cpp" + break; + + case 540: /* type_specifier_nonarray: UCOOPMATNV */ +#line 3554 "MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheckNV((yyvsp[0].lex).loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).coopmatNV = true; + (yyval.interm.type).coopmatKHR = false; + } +#line 10984 "MachineIndependent/glslang_tab.cpp" + break; + + case 541: /* type_specifier_nonarray: COOPMAT */ +#line 3561 "MachineIndependent/glslang.y" + { + parseContext.coopmatCheck((yyvsp[0].lex).loc, "coopmat", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtCoopmat; + (yyval.interm.type).coopmatNV = false; + (yyval.interm.type).coopmatKHR = true; + } +#line 10996 "MachineIndependent/glslang_tab.cpp" + break; + + case 542: /* type_specifier_nonarray: TENSORLAYOUTNV */ +#line 3568 "MachineIndependent/glslang.y" + { + parseContext.tensorLayoutViewCheck((yyvsp[0].lex).loc, "tensorLayoutNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtTensorLayoutNV; + } +#line 11006 "MachineIndependent/glslang_tab.cpp" + break; + + case 543: /* type_specifier_nonarray: TENSORVIEWNV */ +#line 3573 "MachineIndependent/glslang.y" + { + parseContext.tensorLayoutViewCheck((yyvsp[0].lex).loc, "tensorViewNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtTensorViewNV; + } +#line 11016 "MachineIndependent/glslang_tab.cpp" + break; + + case 544: /* type_specifier_nonarray: FUNCTION */ +#line 3578 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).basicType = EbtFunction; + } +#line 11025 "MachineIndependent/glslang_tab.cpp" + break; + + case 545: /* type_specifier_nonarray: COOPVECNV */ +#line 3582 "MachineIndependent/glslang.y" + { + parseContext.coopvecCheck((yyvsp[0].lex).loc, "coopvecNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtCoopvecNV; + (yyval.interm.type).coopvecNV = true; + } +#line 11036 "MachineIndependent/glslang_tab.cpp" + break; + + case 546: /* type_specifier_nonarray: spirv_type_specifier */ +#line 3588 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].interm.type).loc, 1, &E_GL_EXT_spirv_intrinsics, "SPIR-V type specifier"); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 11045 "MachineIndependent/glslang_tab.cpp" + break; + + case 547: /* type_specifier_nonarray: HITOBJECTNV */ +#line 3592 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtHitObjectNV; + } +#line 11054 "MachineIndependent/glslang_tab.cpp" + break; + + case 548: /* type_specifier_nonarray: struct_specifier */ +#line 3596 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + (yyval.interm.type).qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck((yyval.interm.type).loc, (yyval.interm.type)); + } +#line 11064 "MachineIndependent/glslang_tab.cpp" + break; + + case 549: /* type_specifier_nonarray: TYPE_NAME */ +#line 3601 "MachineIndependent/glslang.y" + { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ((yyvsp[0].lex).symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = &structure; + } else + parseContext.error((yyvsp[0].lex).loc, "expected type name", (yyvsp[0].lex).string->c_str(), ""); + } +#line 11082 "MachineIndependent/glslang_tab.cpp" + break; + + case 550: /* precision_qualifier: HIGH_PRECISION */ +#line 3617 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "highp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqHigh); + } +#line 11092 "MachineIndependent/glslang_tab.cpp" + break; + + case 551: /* precision_qualifier: MEDIUM_PRECISION */ +#line 3622 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "mediump precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqMedium); + } +#line 11102 "MachineIndependent/glslang_tab.cpp" + break; + + case 552: /* precision_qualifier: LOW_PRECISION */ +#line 3627 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "lowp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqLow); + } +#line 11112 "MachineIndependent/glslang_tab.cpp" + break; + + case 553: /* $@3: %empty */ +#line 3635 "MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-2].lex).loc); } +#line 11118 "MachineIndependent/glslang_tab.cpp" + break; + + case 554: /* struct_specifier: STRUCT IDENTIFIER LEFT_BRACE $@3 struct_declaration_list RIGHT_BRACE */ +#line 3635 "MachineIndependent/glslang.y" + { + + TType* structure = new TType((yyvsp[-1].interm.typeList), *(yyvsp[-4].lex).string); + parseContext.structArrayCheck((yyvsp[-4].lex).loc, *structure); + + TVariable* userTypeDef = new TVariable((yyvsp[-4].lex).string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error((yyvsp[-4].lex).loc, "redefinition", (yyvsp[-4].lex).string->c_str(), "struct"); + else if (parseContext.spvVersion.vulkanRelaxed + && structure->containsOpaque()) + parseContext.relaxedSymbols.push_back(structure->getTypeName()); + + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 11140 "MachineIndependent/glslang_tab.cpp" + break; + + case 555: /* $@4: %empty */ +#line 3652 "MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-1].lex).loc); } +#line 11146 "MachineIndependent/glslang_tab.cpp" + break; + + case 556: /* struct_specifier: STRUCT LEFT_BRACE $@4 struct_declaration_list RIGHT_BRACE */ +#line 3652 "MachineIndependent/glslang.y" + { + TType* structure = new TType((yyvsp[-1].interm.typeList), TString("")); + (yyval.interm.type).init((yyvsp[-4].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 11158 "MachineIndependent/glslang_tab.cpp" + break; + + case 557: /* struct_declaration_list: struct_declaration */ +#line 3662 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[0].interm.typeList); + } +#line 11166 "MachineIndependent/glslang_tab.cpp" + break; + + case 558: /* struct_declaration_list: struct_declaration_list struct_declaration */ +#line 3665 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + for (unsigned int i = 0; i < (yyvsp[0].interm.typeList)->size(); ++i) { + for (unsigned int j = 0; j < (yyval.interm.typeList)->size(); ++j) { + if ((*(yyval.interm.typeList))[j].type->getFieldName() == (*(yyvsp[0].interm.typeList))[i].type->getFieldName()) + parseContext.error((*(yyvsp[0].interm.typeList))[i].loc, "duplicate member name:", "", (*(yyvsp[0].interm.typeList))[i].type->getFieldName().c_str()); + } + (yyval.interm.typeList)->push_back((*(yyvsp[0].interm.typeList))[i]); + } + } +#line 11181 "MachineIndependent/glslang_tab.cpp" + break; + + case 559: /* struct_declaration: type_specifier struct_declarator_list SEMICOLON */ +#line 3678 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier, (yyvsp[-2].interm.type).isCoopmatOrvec()); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 11208 "MachineIndependent/glslang_tab.cpp" + break; + + case 560: /* struct_declaration: type_qualifier type_specifier struct_declarator_list SEMICOLON */ +#line 3700 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.memberQualifierCheck((yyvsp[-3].interm.type)); + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.mergeQualifiers((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, (yyvsp[-3].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier, (yyvsp[-2].interm.type).isCoopmatOrvec()); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 11237 "MachineIndependent/glslang_tab.cpp" + break; + + case 561: /* struct_declarator_list: struct_declarator */ +#line 3727 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = new TTypeList; + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 11246 "MachineIndependent/glslang_tab.cpp" + break; + + case 562: /* struct_declarator_list: struct_declarator_list COMMA struct_declarator */ +#line 3731 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 11254 "MachineIndependent/glslang_tab.cpp" + break; + + case 563: /* struct_declarator: IDENTIFIER */ +#line 3737 "MachineIndependent/glslang.y" + { + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[0].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[0].lex).string); + } +#line 11264 "MachineIndependent/glslang_tab.cpp" + break; + + case 564: /* struct_declarator: IDENTIFIER array_specifier */ +#line 3742 "MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, (yyvsp[0].interm).arraySizes); + + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[-1].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[-1].lex).string); + (yyval.interm.typeLine).type->transferArraySizes((yyvsp[0].interm).arraySizes); + } +#line 11277 "MachineIndependent/glslang_tab.cpp" + break; + + case 565: /* initializer: assignment_expression */ +#line 3753 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 11285 "MachineIndependent/glslang_tab.cpp" + break; + + case 566: /* initializer: LEFT_BRACE initializer_list RIGHT_BRACE */ +#line 3756 "MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-2].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-2].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + } +#line 11296 "MachineIndependent/glslang_tab.cpp" + break; + + case 567: /* initializer: LEFT_BRACE initializer_list COMMA RIGHT_BRACE */ +#line 3762 "MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-3].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-3].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 11307 "MachineIndependent/glslang_tab.cpp" + break; + + case 568: /* initializer: LEFT_BRACE RIGHT_BRACE */ +#line 3768 "MachineIndependent/glslang.y" + { + const char* initFeature = "empty { } initializer"; + parseContext.profileRequires((yyvsp[-1].lex).loc, EEsProfile, 0, E_GL_EXT_null_initializer, initFeature); + parseContext.profileRequires((yyvsp[-1].lex).loc, ~EEsProfile, 0, E_GL_EXT_null_initializer, initFeature); + (yyval.interm.intermTypedNode) = parseContext.intermediate.makeAggregate((yyvsp[-1].lex).loc); + } +#line 11318 "MachineIndependent/glslang_tab.cpp" + break; + + case 569: /* initializer_list: initializer */ +#line 3777 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate(0, (yyvsp[0].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)->getLoc()); + } +#line 11326 "MachineIndependent/glslang_tab.cpp" + break; + + case 570: /* initializer_list: initializer_list COMMA initializer */ +#line 3780 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + } +#line 11334 "MachineIndependent/glslang_tab.cpp" + break; + + case 571: /* declaration_statement: declaration */ +#line 3786 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11340 "MachineIndependent/glslang_tab.cpp" + break; + + case 572: /* statement: compound_statement */ +#line 3790 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11346 "MachineIndependent/glslang_tab.cpp" + break; + + case 573: /* statement: simple_statement */ +#line 3791 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11352 "MachineIndependent/glslang_tab.cpp" + break; + + case 574: /* simple_statement: declaration_statement */ +#line 3797 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11358 "MachineIndependent/glslang_tab.cpp" + break; + + case 575: /* simple_statement: expression_statement */ +#line 3798 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11364 "MachineIndependent/glslang_tab.cpp" + break; + + case 576: /* simple_statement: selection_statement */ +#line 3799 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11370 "MachineIndependent/glslang_tab.cpp" + break; + + case 577: /* simple_statement: switch_statement */ +#line 3800 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11376 "MachineIndependent/glslang_tab.cpp" + break; + + case 578: /* simple_statement: case_label */ +#line 3801 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11382 "MachineIndependent/glslang_tab.cpp" + break; + + case 579: /* simple_statement: iteration_statement */ +#line 3802 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11388 "MachineIndependent/glslang_tab.cpp" + break; + + case 580: /* simple_statement: jump_statement */ +#line 3803 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11394 "MachineIndependent/glslang_tab.cpp" + break; + + case 581: /* simple_statement: demote_statement */ +#line 3804 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11400 "MachineIndependent/glslang_tab.cpp" + break; + + case 582: /* demote_statement: DEMOTE SEMICOLON */ +#line 3808 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "demote"); + parseContext.requireExtensions((yyvsp[-1].lex).loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDemote, (yyvsp[-1].lex).loc); + } +#line 11410 "MachineIndependent/glslang_tab.cpp" + break; + + case 583: /* compound_statement: LEFT_BRACE RIGHT_BRACE */ +#line 3816 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 11416 "MachineIndependent/glslang_tab.cpp" + break; + + case 584: /* $@5: %empty */ +#line 3817 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } +#line 11425 "MachineIndependent/glslang_tab.cpp" + break; + + case 585: /* $@6: %empty */ +#line 3821 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } +#line 11434 "MachineIndependent/glslang_tab.cpp" + break; + + case 586: /* compound_statement: LEFT_BRACE $@5 statement_list $@6 RIGHT_BRACE */ +#line 3825 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.intermNode) && (yyvsp[-2].interm.intermNode)->getAsAggregate()) { + (yyvsp[-2].interm.intermNode)->getAsAggregate()->setOperator(parseContext.intermediate.getDebugInfo() ? EOpScope : EOpSequence); + (yyvsp[-2].interm.intermNode)->getAsAggregate()->setEndLoc((yyvsp[0].lex).loc); + } + (yyval.interm.intermNode) = (yyvsp[-2].interm.intermNode); + } +#line 11446 "MachineIndependent/glslang_tab.cpp" + break; + + case 587: /* statement_no_new_scope: compound_statement_no_new_scope */ +#line 3835 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11452 "MachineIndependent/glslang_tab.cpp" + break; + + case 588: /* statement_no_new_scope: simple_statement */ +#line 3836 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 11458 "MachineIndependent/glslang_tab.cpp" + break; + + case 589: /* $@7: %empty */ +#line 3840 "MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 11466 "MachineIndependent/glslang_tab.cpp" + break; + + case 590: /* statement_scoped: $@7 compound_statement */ +#line 3843 "MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11475 "MachineIndependent/glslang_tab.cpp" + break; + + case 591: /* $@8: %empty */ +#line 3847 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 11485 "MachineIndependent/glslang_tab.cpp" + break; + + case 592: /* statement_scoped: $@8 simple_statement */ +#line 3852 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11496 "MachineIndependent/glslang_tab.cpp" + break; + + case 593: /* compound_statement_no_new_scope: LEFT_BRACE RIGHT_BRACE */ +#line 3861 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 11504 "MachineIndependent/glslang_tab.cpp" + break; + + case 594: /* compound_statement_no_new_scope: LEFT_BRACE statement_list RIGHT_BRACE */ +#line 3864 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.intermNode) && (yyvsp[-1].interm.intermNode)->getAsAggregate()) { + (yyvsp[-1].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + (yyvsp[-1].interm.intermNode)->getAsAggregate()->setEndLoc((yyvsp[0].lex).loc); + } + (yyval.interm.intermNode) = (yyvsp[-1].interm.intermNode); + } +#line 11516 "MachineIndependent/glslang_tab.cpp" + break; + + case 595: /* statement_list: statement */ +#line 3874 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } + } +#line 11529 "MachineIndependent/glslang_tab.cpp" + break; + + case 596: /* statement_list: statement_list statement */ +#line 3882 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence((yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } else + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 11542 "MachineIndependent/glslang_tab.cpp" + break; + + case 597: /* expression_statement: SEMICOLON */ +#line 3893 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 11548 "MachineIndependent/glslang_tab.cpp" + break; + + case 598: /* expression_statement: expression SEMICOLON */ +#line 3894 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = static_cast((yyvsp[-1].interm.intermTypedNode)); } +#line 11554 "MachineIndependent/glslang_tab.cpp" + break; + + case 599: /* selection_statement: selection_statement_nonattributed */ +#line 3898 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11562 "MachineIndependent/glslang_tab.cpp" + break; + + case 600: /* selection_statement: attribute selection_statement_nonattributed */ +#line 3901 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].interm.intermNode)->getLoc(), 1, &E_GL_EXT_control_flow_attributes, "attribute"); + parseContext.handleSelectionAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11572 "MachineIndependent/glslang_tab.cpp" + break; + + case 601: /* selection_statement_nonattributed: IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement */ +#line 3908 "MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + (yyval.interm.intermNode) = parseContext.intermediate.addSelection((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.nodePair), (yyvsp[-4].lex).loc); + } +#line 11581 "MachineIndependent/glslang_tab.cpp" + break; + + case 602: /* selection_rest_statement: statement_scoped ELSE statement_scoped */ +#line 3915 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode); + } +#line 11590 "MachineIndependent/glslang_tab.cpp" + break; + + case 603: /* selection_rest_statement: statement_scoped */ +#line 3919 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 11599 "MachineIndependent/glslang_tab.cpp" + break; + + case 604: /* condition: expression */ +#line 3927 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + parseContext.boolCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode)); + } +#line 11608 "MachineIndependent/glslang_tab.cpp" + break; + + case 605: /* condition: fully_specified_type IDENTIFIER EQUAL initializer */ +#line 3931 "MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-2].lex).loc, (yyvsp[-3].interm.type)); + + TType type((yyvsp[-3].interm.type)); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + if (initNode) + (yyval.interm.intermTypedNode) = initNode->getAsTyped(); + else + (yyval.interm.intermTypedNode) = 0; + } +#line 11623 "MachineIndependent/glslang_tab.cpp" + break; + + case 606: /* switch_statement: switch_statement_nonattributed */ +#line 3944 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11631 "MachineIndependent/glslang_tab.cpp" + break; + + case 607: /* switch_statement: attribute switch_statement_nonattributed */ +#line 3947 "MachineIndependent/glslang.y" + { + parseContext.requireExtensions((yyvsp[0].interm.intermNode)->getLoc(), 1, &E_GL_EXT_control_flow_attributes, "attribute"); + parseContext.handleSwitchAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11641 "MachineIndependent/glslang_tab.cpp" + break; + + case 608: /* $@9: %empty */ +#line 3954 "MachineIndependent/glslang.y" + { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } +#line 11654 "MachineIndependent/glslang_tab.cpp" + break; + + case 609: /* switch_statement_nonattributed: SWITCH LEFT_PAREN expression RIGHT_PAREN $@9 LEFT_BRACE switch_statement_list RIGHT_BRACE */ +#line 3962 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.addSwitch((yyvsp[-7].lex).loc, (yyvsp[-5].interm.intermTypedNode), (yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 11668 "MachineIndependent/glslang_tab.cpp" + break; + + case 610: /* switch_statement_list: %empty */ +#line 3974 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 11676 "MachineIndependent/glslang_tab.cpp" + break; + + case 611: /* switch_statement_list: statement_list */ +#line 3977 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11684 "MachineIndependent/glslang_tab.cpp" + break; + + case 612: /* case_label: CASE expression COLON */ +#line 3983 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-2].lex).loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-2].lex).loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck((yyvsp[-1].interm.intermTypedNode), "case"); + parseContext.integerCheck((yyvsp[-1].interm.intermTypedNode), "case"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpCase, (yyvsp[-1].interm.intermTypedNode), (yyvsp[-2].lex).loc); + } + } +#line 11701 "MachineIndependent/glslang_tab.cpp" + break; + + case 613: /* case_label: DEFAULT COLON */ +#line 3995 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-1].lex).loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-1].lex).loc, "cannot be nested inside control flow", "default", ""); + else + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDefault, (yyvsp[-1].lex).loc); + } +#line 11715 "MachineIndependent/glslang_tab.cpp" + break; + + case 614: /* iteration_statement: iteration_statement_nonattributed */ +#line 4007 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11723 "MachineIndependent/glslang_tab.cpp" + break; + + case 615: /* iteration_statement: attribute iteration_statement_nonattributed */ +#line 4010 "MachineIndependent/glslang.y" + { + const char * extensions[2] = { E_GL_EXT_control_flow_attributes, E_GL_EXT_control_flow_attributes2 }; + parseContext.requireExtensions((yyvsp[0].interm.intermNode)->getLoc(), 2, extensions, "attribute"); + parseContext.handleLoopAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11734 "MachineIndependent/glslang_tab.cpp" + break; + + case 616: /* $@10: %empty */ +#line 4018 "MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-1].lex).loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 11747 "MachineIndependent/glslang_tab.cpp" + break; + + case 617: /* iteration_statement_nonattributed: WHILE LEFT_PAREN $@10 condition RIGHT_PAREN statement_no_new_scope */ +#line 4026 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, true, (yyvsp[-5].lex).loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 11759 "MachineIndependent/glslang_tab.cpp" + break; + + case 618: /* $@11: %empty */ +#line 4033 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 11770 "MachineIndependent/glslang_tab.cpp" + break; + + case 619: /* iteration_statement_nonattributed: DO $@11 statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON */ +#line 4039 "MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-7].lex).loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[-5].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, false, (yyvsp[-4].lex).loc); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 11787 "MachineIndependent/glslang_tab.cpp" + break; + + case 620: /* $@12: %empty */ +#line 4051 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 11798 "MachineIndependent/glslang_tab.cpp" + break; + + case 621: /* iteration_statement_nonattributed: FOR LEFT_PAREN $@12 for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope */ +#line 4057 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[-3].interm.intermNode), (yyvsp[-5].lex).loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), reinterpret_cast((yyvsp[-2].interm.nodePair).node1), reinterpret_cast((yyvsp[-2].interm.nodePair).node2), true, (yyvsp[-6].lex).loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck((yyvsp[-6].lex).loc, (yyvsp[-3].interm.intermNode), forLoop); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyval.interm.intermNode), forLoop, (yyvsp[-6].lex).loc); + (yyval.interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 11815 "MachineIndependent/glslang_tab.cpp" + break; + + case 622: /* for_init_statement: expression_statement */ +#line 4072 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11823 "MachineIndependent/glslang_tab.cpp" + break; + + case 623: /* for_init_statement: declaration_statement */ +#line 4075 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11831 "MachineIndependent/glslang_tab.cpp" + break; + + case 624: /* conditionopt: condition */ +#line 4081 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 11839 "MachineIndependent/glslang_tab.cpp" + break; + + case 625: /* conditionopt: %empty */ +#line 4084 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = 0; + } +#line 11847 "MachineIndependent/glslang_tab.cpp" + break; + + case 626: /* for_rest_statement: conditionopt SEMICOLON */ +#line 4090 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 11856 "MachineIndependent/glslang_tab.cpp" + break; + + case 627: /* for_rest_statement: conditionopt SEMICOLON expression */ +#line 4094 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode); + } +#line 11865 "MachineIndependent/glslang_tab.cpp" + break; + + case 628: /* jump_statement: CONTINUE SEMICOLON */ +#line 4101 "MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel <= 0) + parseContext.error((yyvsp[-1].lex).loc, "continue statement only allowed in loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpContinue, (yyvsp[-1].lex).loc); + } +#line 11875 "MachineIndependent/glslang_tab.cpp" + break; + + case 629: /* jump_statement: BREAK SEMICOLON */ +#line 4106 "MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error((yyvsp[-1].lex).loc, "break statement only allowed in switch and loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpBreak, (yyvsp[-1].lex).loc); + } +#line 11885 "MachineIndependent/glslang_tab.cpp" + break; + + case 630: /* jump_statement: RETURN SEMICOLON */ +#line 4111 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpReturn, (yyvsp[-1].lex).loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error((yyvsp[-1].lex).loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } +#line 11897 "MachineIndependent/glslang_tab.cpp" + break; + + case 631: /* jump_statement: RETURN expression SEMICOLON */ +#line 4118 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.handleReturnValue((yyvsp[-2].lex).loc, (yyvsp[-1].interm.intermTypedNode)); + } +#line 11905 "MachineIndependent/glslang_tab.cpp" + break; + + case 632: /* jump_statement: DISCARD SEMICOLON */ +#line 4121 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "discard"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpKill, (yyvsp[-1].lex).loc); + } +#line 11914 "MachineIndependent/glslang_tab.cpp" + break; + + case 633: /* jump_statement: TERMINATE_INVOCATION SEMICOLON */ +#line 4125 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "terminateInvocation"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpTerminateInvocation, (yyvsp[-1].lex).loc); + } +#line 11923 "MachineIndependent/glslang_tab.cpp" + break; + + case 634: /* jump_statement: TERMINATE_RAY SEMICOLON */ +#line 4129 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangAnyHit, "terminateRayEXT"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpTerminateRayKHR, (yyvsp[-1].lex).loc); + } +#line 11932 "MachineIndependent/glslang_tab.cpp" + break; + + case 635: /* jump_statement: IGNORE_INTERSECTION SEMICOLON */ +#line 4133 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangAnyHit, "ignoreIntersectionEXT"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpIgnoreIntersectionKHR, (yyvsp[-1].lex).loc); + } +#line 11941 "MachineIndependent/glslang_tab.cpp" + break; + + case 636: /* translation_unit: external_declaration */ +#line 4142 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } +#line 11950 "MachineIndependent/glslang_tab.cpp" + break; + + case 637: /* translation_unit: translation_unit external_declaration */ +#line 4146 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) != nullptr) { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } + } +#line 11961 "MachineIndependent/glslang_tab.cpp" + break; + + case 638: /* external_declaration: function_definition */ +#line 4155 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11969 "MachineIndependent/glslang_tab.cpp" + break; + + case 639: /* external_declaration: declaration */ +#line 4158 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 11977 "MachineIndependent/glslang_tab.cpp" + break; + + case 640: /* external_declaration: SEMICOLON */ +#line 4161 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires((yyvsp[0].lex).loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + (yyval.interm.intermNode) = nullptr; + } +#line 11987 "MachineIndependent/glslang_tab.cpp" + break; + + case 641: /* $@13: %empty */ +#line 4169 "MachineIndependent/glslang.y" + { + (yyvsp[0].interm).function = parseContext.handleFunctionDeclarator((yyvsp[0].interm).loc, *(yyvsp[0].interm).function, false /* not prototype */); + (yyvsp[0].interm).intermNode = parseContext.handleFunctionDefinition((yyvsp[0].interm).loc, *(yyvsp[0].interm).function); + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + } +#line 12004 "MachineIndependent/glslang_tab.cpp" + break; + + case 642: /* function_definition: function_prototype $@13 compound_statement_no_new_scope */ +#line 4181 "MachineIndependent/glslang.y" + { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error((yyvsp[-2].interm).loc, "function does not return a value:", "", (yyvsp[-2].interm).function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode)->getAsAggregate()->setLinkType((yyvsp[-2].interm).function->getLinkType()); + parseContext.intermediate.setAggregateOperator((yyval.interm.intermNode), EOpFunction, (yyvsp[-2].interm).function->getType(), (yyvsp[-2].interm).loc); + (yyval.interm.intermNode)->getAsAggregate()->setName((yyvsp[-2].interm).function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + (yyval.interm.intermNode)->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + (yyval.interm.intermNode)->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + (yyval.interm.intermNode)->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + + // Set currentFunctionType to empty pointer when goes outside of the function + parseContext.currentFunctionType = nullptr; + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + } +#line 12036 "MachineIndependent/glslang_tab.cpp" + break; + + case 643: /* attribute: LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET */ +#line 4211 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[-2].interm.attributes); + } +#line 12044 "MachineIndependent/glslang_tab.cpp" + break; + + case 644: /* attribute_list: single_attribute */ +#line 4216 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[0].interm.attributes); + } +#line 12052 "MachineIndependent/glslang_tab.cpp" + break; + + case 645: /* attribute_list: attribute_list COMMA single_attribute */ +#line 4219 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.mergeAttributes((yyvsp[-2].interm.attributes), (yyvsp[0].interm.attributes)); + } +#line 12060 "MachineIndependent/glslang_tab.cpp" + break; + + case 646: /* single_attribute: IDENTIFIER */ +#line 4224 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[0].lex).string); + } +#line 12068 "MachineIndependent/glslang_tab.cpp" + break; + + case 647: /* single_attribute: IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN */ +#line 4227 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[-3].lex).string, (yyvsp[-1].interm.intermTypedNode)); + } +#line 12076 "MachineIndependent/glslang_tab.cpp" + break; + + case 648: /* spirv_requirements_list: spirv_requirements_parameter */ +#line 4232 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvReq) = (yyvsp[0].interm.spirvReq); + } +#line 12084 "MachineIndependent/glslang_tab.cpp" + break; + + case 649: /* spirv_requirements_list: spirv_requirements_list COMMA spirv_requirements_parameter */ +#line 4235 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvReq) = parseContext.mergeSpirvRequirements((yyvsp[-1].lex).loc, (yyvsp[-2].interm.spirvReq), (yyvsp[0].interm.spirvReq)); + } +#line 12092 "MachineIndependent/glslang_tab.cpp" + break; + + case 650: /* spirv_requirements_parameter: IDENTIFIER EQUAL LEFT_BRACKET spirv_extension_list RIGHT_BRACKET */ +#line 4240 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvReq) = parseContext.makeSpirvRequirement((yyvsp[-3].lex).loc, *(yyvsp[-4].lex).string, (yyvsp[-1].interm.intermNode)->getAsAggregate(), nullptr); + } +#line 12100 "MachineIndependent/glslang_tab.cpp" + break; + + case 651: /* spirv_requirements_parameter: IDENTIFIER EQUAL LEFT_BRACKET spirv_capability_list RIGHT_BRACKET */ +#line 4243 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvReq) = parseContext.makeSpirvRequirement((yyvsp[-3].lex).loc, *(yyvsp[-4].lex).string, nullptr, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12108 "MachineIndependent/glslang_tab.cpp" + break; + + case 652: /* spirv_extension_list: STRING_LITERAL */ +#line 4248 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate(parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true)); + } +#line 12116 "MachineIndependent/glslang_tab.cpp" + break; + + case 653: /* spirv_extension_list: spirv_extension_list COMMA STRING_LITERAL */ +#line 4251 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true)); + } +#line 12124 "MachineIndependent/glslang_tab.cpp" + break; + + case 654: /* spirv_capability_list: INTCONSTANT */ +#line 4256 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate(parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true)); + } +#line 12132 "MachineIndependent/glslang_tab.cpp" + break; + + case 655: /* spirv_capability_list: spirv_capability_list COMMA INTCONSTANT */ +#line 4259 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true)); + } +#line 12140 "MachineIndependent/glslang_tab.cpp" + break; + + case 656: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE LEFT_PAREN INTCONSTANT RIGHT_PAREN */ +#line 4264 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvExecutionMode((yyvsp[-1].lex).i); + (yyval.interm.intermNode) = 0; + } +#line 12149 "MachineIndependent/glslang_tab.cpp" + break; + + case 657: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN */ +#line 4268 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvRequirement((yyvsp[-3].interm.spirvReq)); + parseContext.intermediate.insertSpirvExecutionMode((yyvsp[-1].lex).i); + (yyval.interm.intermNode) = 0; + } +#line 12159 "MachineIndependent/glslang_tab.cpp" + break; + + case 658: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE LEFT_PAREN INTCONSTANT COMMA spirv_execution_mode_parameter_list RIGHT_PAREN */ +#line 4273 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvExecutionMode((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + (yyval.interm.intermNode) = 0; + } +#line 12168 "MachineIndependent/glslang_tab.cpp" + break; + + case 659: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_execution_mode_parameter_list RIGHT_PAREN */ +#line 4277 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + parseContext.intermediate.insertSpirvExecutionMode((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + (yyval.interm.intermNode) = 0; + } +#line 12178 "MachineIndependent/glslang_tab.cpp" + break; + + case 660: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE_ID LEFT_PAREN INTCONSTANT COMMA spirv_execution_mode_id_parameter_list RIGHT_PAREN */ +#line 4282 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvExecutionModeId((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + (yyval.interm.intermNode) = 0; + } +#line 12187 "MachineIndependent/glslang_tab.cpp" + break; + + case 661: /* spirv_execution_mode_qualifier: SPIRV_EXECUTION_MODE_ID LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_execution_mode_id_parameter_list RIGHT_PAREN */ +#line 4286 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + parseContext.intermediate.insertSpirvExecutionModeId((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + (yyval.interm.intermNode) = 0; + } +#line 12197 "MachineIndependent/glslang_tab.cpp" + break; + + case 662: /* spirv_execution_mode_parameter_list: spirv_execution_mode_parameter */ +#line 4293 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + } +#line 12205 "MachineIndependent/glslang_tab.cpp" + break; + + case 663: /* spirv_execution_mode_parameter_list: spirv_execution_mode_parameter_list COMMA spirv_execution_mode_parameter */ +#line 4296 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 12213 "MachineIndependent/glslang_tab.cpp" + break; + + case 664: /* spirv_execution_mode_parameter: FLOATCONSTANT */ +#line 4301 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 12221 "MachineIndependent/glslang_tab.cpp" + break; + + case 665: /* spirv_execution_mode_parameter: INTCONSTANT */ +#line 4304 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 12229 "MachineIndependent/glslang_tab.cpp" + break; + + case 666: /* spirv_execution_mode_parameter: UINTCONSTANT */ +#line 4307 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 12237 "MachineIndependent/glslang_tab.cpp" + break; + + case 667: /* spirv_execution_mode_parameter: BOOLCONSTANT */ +#line 4310 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 12245 "MachineIndependent/glslang_tab.cpp" + break; + + case 668: /* spirv_execution_mode_parameter: STRING_LITERAL */ +#line 4313 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true); + } +#line 12253 "MachineIndependent/glslang_tab.cpp" + break; + + case 669: /* spirv_execution_mode_id_parameter_list: constant_expression */ +#line 4318 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtFloat && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtInt && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtUint && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtBool && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtString) + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "this type not allowed", (yyvsp[0].interm.intermTypedNode)->getType().getBasicString(), ""); + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermTypedNode)); + } +#line 12267 "MachineIndependent/glslang_tab.cpp" + break; + + case 670: /* spirv_execution_mode_id_parameter_list: spirv_execution_mode_id_parameter_list COMMA constant_expression */ +#line 4327 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtFloat && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtInt && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtUint && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtBool && + (yyvsp[0].interm.intermTypedNode)->getBasicType() != EbtString) + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "this type not allowed", (yyvsp[0].interm.intermTypedNode)->getType().getBasicString(), ""); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), (yyvsp[0].interm.intermTypedNode)); + } +#line 12281 "MachineIndependent/glslang_tab.cpp" + break; + + case 671: /* spirv_storage_class_qualifier: SPIRV_STORAGE_CLASS LEFT_PAREN INTCONSTANT RIGHT_PAREN */ +#line 4338 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-3].lex).loc); + (yyval.interm.type).qualifier.storage = EvqSpirvStorageClass; + (yyval.interm.type).qualifier.spirvStorageClass = (yyvsp[-1].lex).i; + } +#line 12291 "MachineIndependent/glslang_tab.cpp" + break; + + case 672: /* spirv_storage_class_qualifier: SPIRV_STORAGE_CLASS LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN */ +#line 4343 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-3].interm.spirvReq)); + (yyval.interm.type).qualifier.storage = EvqSpirvStorageClass; + (yyval.interm.type).qualifier.spirvStorageClass = (yyvsp[-1].lex).i; + } +#line 12302 "MachineIndependent/glslang_tab.cpp" + break; + + case 673: /* spirv_decorate_qualifier: SPIRV_DECORATE LEFT_PAREN INTCONSTANT RIGHT_PAREN */ +#line 4351 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-3].lex).loc); + (yyval.interm.type).qualifier.setSpirvDecorate((yyvsp[-1].lex).i); + } +#line 12311 "MachineIndependent/glslang_tab.cpp" + break; + + case 674: /* spirv_decorate_qualifier: SPIRV_DECORATE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT RIGHT_PAREN */ +#line 4355 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-3].interm.spirvReq)); + (yyval.interm.type).qualifier.setSpirvDecorate((yyvsp[-1].lex).i); + } +#line 12321 "MachineIndependent/glslang_tab.cpp" + break; + + case 675: /* spirv_decorate_qualifier: SPIRV_DECORATE LEFT_PAREN INTCONSTANT COMMA spirv_decorate_parameter_list RIGHT_PAREN */ +#line 4360 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).qualifier.setSpirvDecorate((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12330 "MachineIndependent/glslang_tab.cpp" + break; + + case 676: /* spirv_decorate_qualifier: SPIRV_DECORATE LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_parameter_list RIGHT_PAREN */ +#line 4364 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-7].lex).loc); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + (yyval.interm.type).qualifier.setSpirvDecorate((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12340 "MachineIndependent/glslang_tab.cpp" + break; + + case 677: /* spirv_decorate_qualifier: SPIRV_DECORATE_ID LEFT_PAREN INTCONSTANT COMMA spirv_decorate_id_parameter_list RIGHT_PAREN */ +#line 4369 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).qualifier.setSpirvDecorateId((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12349 "MachineIndependent/glslang_tab.cpp" + break; + + case 678: /* spirv_decorate_qualifier: SPIRV_DECORATE_ID LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_id_parameter_list RIGHT_PAREN */ +#line 4373 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-7].lex).loc); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + (yyval.interm.type).qualifier.setSpirvDecorateId((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12359 "MachineIndependent/glslang_tab.cpp" + break; + + case 679: /* spirv_decorate_qualifier: SPIRV_DECORATE_STRING LEFT_PAREN INTCONSTANT COMMA spirv_decorate_string_parameter_list RIGHT_PAREN */ +#line 4378 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).qualifier.setSpirvDecorateString((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12368 "MachineIndependent/glslang_tab.cpp" + break; + + case 680: /* spirv_decorate_qualifier: SPIRV_DECORATE_STRING LEFT_PAREN spirv_requirements_list COMMA INTCONSTANT COMMA spirv_decorate_string_parameter_list RIGHT_PAREN */ +#line 4382 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-7].lex).loc); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + (yyval.interm.type).qualifier.setSpirvDecorateString((yyvsp[-3].lex).i, (yyvsp[-1].interm.intermNode)->getAsAggregate()); + } +#line 12378 "MachineIndependent/glslang_tab.cpp" + break; + + case 681: /* spirv_decorate_parameter_list: spirv_decorate_parameter */ +#line 4389 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + } +#line 12386 "MachineIndependent/glslang_tab.cpp" + break; + + case 682: /* spirv_decorate_parameter_list: spirv_decorate_parameter_list COMMA spirv_decorate_parameter */ +#line 4392 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 12394 "MachineIndependent/glslang_tab.cpp" + break; + + case 683: /* spirv_decorate_parameter: FLOATCONSTANT */ +#line 4397 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 12402 "MachineIndependent/glslang_tab.cpp" + break; + + case 684: /* spirv_decorate_parameter: INTCONSTANT */ +#line 4400 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 12410 "MachineIndependent/glslang_tab.cpp" + break; + + case 685: /* spirv_decorate_parameter: UINTCONSTANT */ +#line 4403 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 12418 "MachineIndependent/glslang_tab.cpp" + break; + + case 686: /* spirv_decorate_parameter: BOOLCONSTANT */ +#line 4406 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 12426 "MachineIndependent/glslang_tab.cpp" + break; + + case 687: /* spirv_decorate_id_parameter_list: spirv_decorate_id_parameter */ +#line 4411 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + } +#line 12434 "MachineIndependent/glslang_tab.cpp" + break; + + case 688: /* spirv_decorate_id_parameter_list: spirv_decorate_id_parameter_list COMMA spirv_decorate_id_parameter */ +#line 4414 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 12442 "MachineIndependent/glslang_tab.cpp" + break; + + case 689: /* spirv_decorate_id_parameter: variable_identifier */ +#line 4419 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermTypedNode)->getAsConstantUnion() || (yyvsp[0].interm.intermTypedNode)->getAsSymbolNode()) + (yyval.interm.intermNode) = (yyvsp[0].interm.intermTypedNode); + else + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "only allow constants or variables which are not elements of a composite", "", ""); + } +#line 12453 "MachineIndependent/glslang_tab.cpp" + break; + + case 690: /* spirv_decorate_id_parameter: FLOATCONSTANT */ +#line 4425 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 12461 "MachineIndependent/glslang_tab.cpp" + break; + + case 691: /* spirv_decorate_id_parameter: INTCONSTANT */ +#line 4428 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 12469 "MachineIndependent/glslang_tab.cpp" + break; + + case 692: /* spirv_decorate_id_parameter: UINTCONSTANT */ +#line 4431 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 12477 "MachineIndependent/glslang_tab.cpp" + break; + + case 693: /* spirv_decorate_id_parameter: BOOLCONSTANT */ +#line 4434 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 12485 "MachineIndependent/glslang_tab.cpp" + break; + + case 694: /* spirv_decorate_string_parameter_list: STRING_LITERAL */ +#line 4439 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate( + parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true)); + } +#line 12494 "MachineIndependent/glslang_tab.cpp" + break; + + case 695: /* spirv_decorate_string_parameter_list: spirv_decorate_string_parameter_list COMMA STRING_LITERAL */ +#line 4443 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermNode), parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true)); + } +#line 12502 "MachineIndependent/glslang_tab.cpp" + break; + + case 696: /* spirv_type_specifier: SPIRV_TYPE LEFT_PAREN spirv_instruction_qualifier_list COMMA spirv_type_parameter_list RIGHT_PAREN */ +#line 4448 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).setSpirvType(*(yyvsp[-3].interm.spirvInst), (yyvsp[-1].interm.spirvTypeParams)); + } +#line 12511 "MachineIndependent/glslang_tab.cpp" + break; + + case 697: /* spirv_type_specifier: SPIRV_TYPE LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list COMMA spirv_type_parameter_list RIGHT_PAREN */ +#line 4452 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-7].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-5].interm.spirvReq)); + (yyval.interm.type).setSpirvType(*(yyvsp[-3].interm.spirvInst), (yyvsp[-1].interm.spirvTypeParams)); + } +#line 12521 "MachineIndependent/glslang_tab.cpp" + break; + + case 698: /* spirv_type_specifier: SPIRV_TYPE LEFT_PAREN spirv_instruction_qualifier_list RIGHT_PAREN */ +#line 4457 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-3].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).setSpirvType(*(yyvsp[-1].interm.spirvInst)); + } +#line 12530 "MachineIndependent/glslang_tab.cpp" + break; + + case 699: /* spirv_type_specifier: SPIRV_TYPE LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list RIGHT_PAREN */ +#line 4461 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-5].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.intermediate.insertSpirvRequirement((yyvsp[-3].interm.spirvReq)); + (yyval.interm.type).setSpirvType(*(yyvsp[-1].interm.spirvInst)); + } +#line 12540 "MachineIndependent/glslang_tab.cpp" + break; + + case 700: /* spirv_type_parameter_list: spirv_type_parameter */ +#line 4468 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvTypeParams) = (yyvsp[0].interm.spirvTypeParams); + } +#line 12548 "MachineIndependent/glslang_tab.cpp" + break; + + case 701: /* spirv_type_parameter_list: spirv_type_parameter_list COMMA spirv_type_parameter */ +#line 4471 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvTypeParams) = parseContext.mergeSpirvTypeParameters((yyvsp[-2].interm.spirvTypeParams), (yyvsp[0].interm.spirvTypeParams)); + } +#line 12556 "MachineIndependent/glslang_tab.cpp" + break; + + case 702: /* spirv_type_parameter: constant_expression */ +#line 4476 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvTypeParams) = parseContext.makeSpirvTypeParameters((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode)->getAsConstantUnion()); + } +#line 12564 "MachineIndependent/glslang_tab.cpp" + break; + + case 703: /* spirv_type_parameter: type_specifier_nonarray */ +#line 4479 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvTypeParams) = parseContext.makeSpirvTypeParameters((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 12572 "MachineIndependent/glslang_tab.cpp" + break; + + case 704: /* spirv_instruction_qualifier: SPIRV_INSTRUCTION LEFT_PAREN spirv_instruction_qualifier_list RIGHT_PAREN */ +#line 4484 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvInst) = (yyvsp[-1].interm.spirvInst); + } +#line 12580 "MachineIndependent/glslang_tab.cpp" + break; + + case 705: /* spirv_instruction_qualifier: SPIRV_INSTRUCTION LEFT_PAREN spirv_requirements_list COMMA spirv_instruction_qualifier_list RIGHT_PAREN */ +#line 4487 "MachineIndependent/glslang.y" + { + parseContext.intermediate.insertSpirvRequirement((yyvsp[-3].interm.spirvReq)); + (yyval.interm.spirvInst) = (yyvsp[-1].interm.spirvInst); + } +#line 12589 "MachineIndependent/glslang_tab.cpp" + break; + + case 706: /* spirv_instruction_qualifier_list: spirv_instruction_qualifier_id */ +#line 4493 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvInst) = (yyvsp[0].interm.spirvInst); + } +#line 12597 "MachineIndependent/glslang_tab.cpp" + break; + + case 707: /* spirv_instruction_qualifier_list: spirv_instruction_qualifier_list COMMA spirv_instruction_qualifier_id */ +#line 4496 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvInst) = parseContext.mergeSpirvInstruction((yyvsp[-1].lex).loc, (yyvsp[-2].interm.spirvInst), (yyvsp[0].interm.spirvInst)); + } +#line 12605 "MachineIndependent/glslang_tab.cpp" + break; + + case 708: /* spirv_instruction_qualifier_id: IDENTIFIER EQUAL STRING_LITERAL */ +#line 4501 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvInst) = parseContext.makeSpirvInstruction((yyvsp[-1].lex).loc, *(yyvsp[-2].lex).string, *(yyvsp[0].lex).string); + } +#line 12613 "MachineIndependent/glslang_tab.cpp" + break; + + case 709: /* spirv_instruction_qualifier_id: IDENTIFIER EQUAL INTCONSTANT */ +#line 4504 "MachineIndependent/glslang.y" + { + (yyval.interm.spirvInst) = parseContext.makeSpirvInstruction((yyvsp[-1].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[0].lex).i); + } +#line 12621 "MachineIndependent/glslang_tab.cpp" + break; + + +#line 12625 "MachineIndependent/glslang_tab.cpp" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (pParseContext, yymsgp); + if (yysyntax_error_status == YYENOMEM) + YYNOMEM; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, pParseContext); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, pParseContext); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (pParseContext, YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, pParseContext); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, pParseContext); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 4508 "MachineIndependent/glslang.y" + diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp.h new file mode 100644 index 00000000..2ff6f78a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/glslang_tab.cpp.h @@ -0,0 +1,589 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +# define YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + CONST = 258, /* CONST */ + BOOL = 259, /* BOOL */ + INT = 260, /* INT */ + UINT = 261, /* UINT */ + FLOAT = 262, /* FLOAT */ + BVEC2 = 263, /* BVEC2 */ + BVEC3 = 264, /* BVEC3 */ + BVEC4 = 265, /* BVEC4 */ + IVEC2 = 266, /* IVEC2 */ + IVEC3 = 267, /* IVEC3 */ + IVEC4 = 268, /* IVEC4 */ + UVEC2 = 269, /* UVEC2 */ + UVEC3 = 270, /* UVEC3 */ + UVEC4 = 271, /* UVEC4 */ + VEC2 = 272, /* VEC2 */ + VEC3 = 273, /* VEC3 */ + VEC4 = 274, /* VEC4 */ + MAT2 = 275, /* MAT2 */ + MAT3 = 276, /* MAT3 */ + MAT4 = 277, /* MAT4 */ + MAT2X2 = 278, /* MAT2X2 */ + MAT2X3 = 279, /* MAT2X3 */ + MAT2X4 = 280, /* MAT2X4 */ + MAT3X2 = 281, /* MAT3X2 */ + MAT3X3 = 282, /* MAT3X3 */ + MAT3X4 = 283, /* MAT3X4 */ + MAT4X2 = 284, /* MAT4X2 */ + MAT4X3 = 285, /* MAT4X3 */ + MAT4X4 = 286, /* MAT4X4 */ + SAMPLER2D = 287, /* SAMPLER2D */ + SAMPLER3D = 288, /* SAMPLER3D */ + SAMPLERCUBE = 289, /* SAMPLERCUBE */ + SAMPLER2DSHADOW = 290, /* SAMPLER2DSHADOW */ + SAMPLERCUBESHADOW = 291, /* SAMPLERCUBESHADOW */ + SAMPLER2DARRAY = 292, /* SAMPLER2DARRAY */ + SAMPLER2DARRAYSHADOW = 293, /* SAMPLER2DARRAYSHADOW */ + ISAMPLER2D = 294, /* ISAMPLER2D */ + ISAMPLER3D = 295, /* ISAMPLER3D */ + ISAMPLERCUBE = 296, /* ISAMPLERCUBE */ + ISAMPLER2DARRAY = 297, /* ISAMPLER2DARRAY */ + USAMPLER2D = 298, /* USAMPLER2D */ + USAMPLER3D = 299, /* USAMPLER3D */ + USAMPLERCUBE = 300, /* USAMPLERCUBE */ + USAMPLER2DARRAY = 301, /* USAMPLER2DARRAY */ + SAMPLER = 302, /* SAMPLER */ + SAMPLERSHADOW = 303, /* SAMPLERSHADOW */ + TEXTURE2D = 304, /* TEXTURE2D */ + TEXTURE3D = 305, /* TEXTURE3D */ + TEXTURECUBE = 306, /* TEXTURECUBE */ + TEXTURE2DARRAY = 307, /* TEXTURE2DARRAY */ + ITEXTURE2D = 308, /* ITEXTURE2D */ + ITEXTURE3D = 309, /* ITEXTURE3D */ + ITEXTURECUBE = 310, /* ITEXTURECUBE */ + ITEXTURE2DARRAY = 311, /* ITEXTURE2DARRAY */ + UTEXTURE2D = 312, /* UTEXTURE2D */ + UTEXTURE3D = 313, /* UTEXTURE3D */ + UTEXTURECUBE = 314, /* UTEXTURECUBE */ + UTEXTURE2DARRAY = 315, /* UTEXTURE2DARRAY */ + ATTRIBUTE = 316, /* ATTRIBUTE */ + VARYING = 317, /* VARYING */ + BFLOAT16_T = 318, /* BFLOAT16_T */ + FLOAT16_T = 319, /* FLOAT16_T */ + FLOAT32_T = 320, /* FLOAT32_T */ + DOUBLE = 321, /* DOUBLE */ + FLOAT64_T = 322, /* FLOAT64_T */ + INT64_T = 323, /* INT64_T */ + UINT64_T = 324, /* UINT64_T */ + INT32_T = 325, /* INT32_T */ + UINT32_T = 326, /* UINT32_T */ + INT16_T = 327, /* INT16_T */ + UINT16_T = 328, /* UINT16_T */ + INT8_T = 329, /* INT8_T */ + UINT8_T = 330, /* UINT8_T */ + I64VEC2 = 331, /* I64VEC2 */ + I64VEC3 = 332, /* I64VEC3 */ + I64VEC4 = 333, /* I64VEC4 */ + U64VEC2 = 334, /* U64VEC2 */ + U64VEC3 = 335, /* U64VEC3 */ + U64VEC4 = 336, /* U64VEC4 */ + I32VEC2 = 337, /* I32VEC2 */ + I32VEC3 = 338, /* I32VEC3 */ + I32VEC4 = 339, /* I32VEC4 */ + U32VEC2 = 340, /* U32VEC2 */ + U32VEC3 = 341, /* U32VEC3 */ + U32VEC4 = 342, /* U32VEC4 */ + I16VEC2 = 343, /* I16VEC2 */ + I16VEC3 = 344, /* I16VEC3 */ + I16VEC4 = 345, /* I16VEC4 */ + U16VEC2 = 346, /* U16VEC2 */ + U16VEC3 = 347, /* U16VEC3 */ + U16VEC4 = 348, /* U16VEC4 */ + I8VEC2 = 349, /* I8VEC2 */ + I8VEC3 = 350, /* I8VEC3 */ + I8VEC4 = 351, /* I8VEC4 */ + U8VEC2 = 352, /* U8VEC2 */ + U8VEC3 = 353, /* U8VEC3 */ + U8VEC4 = 354, /* U8VEC4 */ + DVEC2 = 355, /* DVEC2 */ + DVEC3 = 356, /* DVEC3 */ + DVEC4 = 357, /* DVEC4 */ + DMAT2 = 358, /* DMAT2 */ + DMAT3 = 359, /* DMAT3 */ + DMAT4 = 360, /* DMAT4 */ + BF16VEC2 = 361, /* BF16VEC2 */ + BF16VEC3 = 362, /* BF16VEC3 */ + BF16VEC4 = 363, /* BF16VEC4 */ + F16VEC2 = 364, /* F16VEC2 */ + F16VEC3 = 365, /* F16VEC3 */ + F16VEC4 = 366, /* F16VEC4 */ + F16MAT2 = 367, /* F16MAT2 */ + F16MAT3 = 368, /* F16MAT3 */ + F16MAT4 = 369, /* F16MAT4 */ + F32VEC2 = 370, /* F32VEC2 */ + F32VEC3 = 371, /* F32VEC3 */ + F32VEC4 = 372, /* F32VEC4 */ + F32MAT2 = 373, /* F32MAT2 */ + F32MAT3 = 374, /* F32MAT3 */ + F32MAT4 = 375, /* F32MAT4 */ + F64VEC2 = 376, /* F64VEC2 */ + F64VEC3 = 377, /* F64VEC3 */ + F64VEC4 = 378, /* F64VEC4 */ + F64MAT2 = 379, /* F64MAT2 */ + F64MAT3 = 380, /* F64MAT3 */ + F64MAT4 = 381, /* F64MAT4 */ + DMAT2X2 = 382, /* DMAT2X2 */ + DMAT2X3 = 383, /* DMAT2X3 */ + DMAT2X4 = 384, /* DMAT2X4 */ + DMAT3X2 = 385, /* DMAT3X2 */ + DMAT3X3 = 386, /* DMAT3X3 */ + DMAT3X4 = 387, /* DMAT3X4 */ + DMAT4X2 = 388, /* DMAT4X2 */ + DMAT4X3 = 389, /* DMAT4X3 */ + DMAT4X4 = 390, /* DMAT4X4 */ + F16MAT2X2 = 391, /* F16MAT2X2 */ + F16MAT2X3 = 392, /* F16MAT2X3 */ + F16MAT2X4 = 393, /* F16MAT2X4 */ + F16MAT3X2 = 394, /* F16MAT3X2 */ + F16MAT3X3 = 395, /* F16MAT3X3 */ + F16MAT3X4 = 396, /* F16MAT3X4 */ + F16MAT4X2 = 397, /* F16MAT4X2 */ + F16MAT4X3 = 398, /* F16MAT4X3 */ + F16MAT4X4 = 399, /* F16MAT4X4 */ + F32MAT2X2 = 400, /* F32MAT2X2 */ + F32MAT2X3 = 401, /* F32MAT2X3 */ + F32MAT2X4 = 402, /* F32MAT2X4 */ + F32MAT3X2 = 403, /* F32MAT3X2 */ + F32MAT3X3 = 404, /* F32MAT3X3 */ + F32MAT3X4 = 405, /* F32MAT3X4 */ + F32MAT4X2 = 406, /* F32MAT4X2 */ + F32MAT4X3 = 407, /* F32MAT4X3 */ + F32MAT4X4 = 408, /* F32MAT4X4 */ + F64MAT2X2 = 409, /* F64MAT2X2 */ + F64MAT2X3 = 410, /* F64MAT2X3 */ + F64MAT2X4 = 411, /* F64MAT2X4 */ + F64MAT3X2 = 412, /* F64MAT3X2 */ + F64MAT3X3 = 413, /* F64MAT3X3 */ + F64MAT3X4 = 414, /* F64MAT3X4 */ + F64MAT4X2 = 415, /* F64MAT4X2 */ + F64MAT4X3 = 416, /* F64MAT4X3 */ + F64MAT4X4 = 417, /* F64MAT4X4 */ + ATOMIC_UINT = 418, /* ATOMIC_UINT */ + ACCSTRUCTNV = 419, /* ACCSTRUCTNV */ + ACCSTRUCTEXT = 420, /* ACCSTRUCTEXT */ + RAYQUERYEXT = 421, /* RAYQUERYEXT */ + FCOOPMATNV = 422, /* FCOOPMATNV */ + ICOOPMATNV = 423, /* ICOOPMATNV */ + UCOOPMATNV = 424, /* UCOOPMATNV */ + COOPMAT = 425, /* COOPMAT */ + COOPVECNV = 426, /* COOPVECNV */ + HITOBJECTNV = 427, /* HITOBJECTNV */ + HITOBJECTATTRNV = 428, /* HITOBJECTATTRNV */ + TENSORLAYOUTNV = 429, /* TENSORLAYOUTNV */ + TENSORVIEWNV = 430, /* TENSORVIEWNV */ + SAMPLERCUBEARRAY = 431, /* SAMPLERCUBEARRAY */ + SAMPLERCUBEARRAYSHADOW = 432, /* SAMPLERCUBEARRAYSHADOW */ + ISAMPLERCUBEARRAY = 433, /* ISAMPLERCUBEARRAY */ + USAMPLERCUBEARRAY = 434, /* USAMPLERCUBEARRAY */ + SAMPLER1D = 435, /* SAMPLER1D */ + SAMPLER1DARRAY = 436, /* SAMPLER1DARRAY */ + SAMPLER1DARRAYSHADOW = 437, /* SAMPLER1DARRAYSHADOW */ + ISAMPLER1D = 438, /* ISAMPLER1D */ + SAMPLER1DSHADOW = 439, /* SAMPLER1DSHADOW */ + SAMPLER2DRECT = 440, /* SAMPLER2DRECT */ + SAMPLER2DRECTSHADOW = 441, /* SAMPLER2DRECTSHADOW */ + ISAMPLER2DRECT = 442, /* ISAMPLER2DRECT */ + USAMPLER2DRECT = 443, /* USAMPLER2DRECT */ + SAMPLERBUFFER = 444, /* SAMPLERBUFFER */ + ISAMPLERBUFFER = 445, /* ISAMPLERBUFFER */ + USAMPLERBUFFER = 446, /* USAMPLERBUFFER */ + SAMPLER2DMS = 447, /* SAMPLER2DMS */ + ISAMPLER2DMS = 448, /* ISAMPLER2DMS */ + USAMPLER2DMS = 449, /* USAMPLER2DMS */ + SAMPLER2DMSARRAY = 450, /* SAMPLER2DMSARRAY */ + ISAMPLER2DMSARRAY = 451, /* ISAMPLER2DMSARRAY */ + USAMPLER2DMSARRAY = 452, /* USAMPLER2DMSARRAY */ + SAMPLEREXTERNALOES = 453, /* SAMPLEREXTERNALOES */ + SAMPLEREXTERNAL2DY2YEXT = 454, /* SAMPLEREXTERNAL2DY2YEXT */ + ISAMPLER1DARRAY = 455, /* ISAMPLER1DARRAY */ + USAMPLER1D = 456, /* USAMPLER1D */ + USAMPLER1DARRAY = 457, /* USAMPLER1DARRAY */ + F16SAMPLER1D = 458, /* F16SAMPLER1D */ + F16SAMPLER2D = 459, /* F16SAMPLER2D */ + F16SAMPLER3D = 460, /* F16SAMPLER3D */ + F16SAMPLER2DRECT = 461, /* F16SAMPLER2DRECT */ + F16SAMPLERCUBE = 462, /* F16SAMPLERCUBE */ + F16SAMPLER1DARRAY = 463, /* F16SAMPLER1DARRAY */ + F16SAMPLER2DARRAY = 464, /* F16SAMPLER2DARRAY */ + F16SAMPLERCUBEARRAY = 465, /* F16SAMPLERCUBEARRAY */ + F16SAMPLERBUFFER = 466, /* F16SAMPLERBUFFER */ + F16SAMPLER2DMS = 467, /* F16SAMPLER2DMS */ + F16SAMPLER2DMSARRAY = 468, /* F16SAMPLER2DMSARRAY */ + F16SAMPLER1DSHADOW = 469, /* F16SAMPLER1DSHADOW */ + F16SAMPLER2DSHADOW = 470, /* F16SAMPLER2DSHADOW */ + F16SAMPLER1DARRAYSHADOW = 471, /* F16SAMPLER1DARRAYSHADOW */ + F16SAMPLER2DARRAYSHADOW = 472, /* F16SAMPLER2DARRAYSHADOW */ + F16SAMPLER2DRECTSHADOW = 473, /* F16SAMPLER2DRECTSHADOW */ + F16SAMPLERCUBESHADOW = 474, /* F16SAMPLERCUBESHADOW */ + F16SAMPLERCUBEARRAYSHADOW = 475, /* F16SAMPLERCUBEARRAYSHADOW */ + IMAGE1D = 476, /* IMAGE1D */ + IIMAGE1D = 477, /* IIMAGE1D */ + UIMAGE1D = 478, /* UIMAGE1D */ + IMAGE2D = 479, /* IMAGE2D */ + IIMAGE2D = 480, /* IIMAGE2D */ + UIMAGE2D = 481, /* UIMAGE2D */ + IMAGE3D = 482, /* IMAGE3D */ + IIMAGE3D = 483, /* IIMAGE3D */ + UIMAGE3D = 484, /* UIMAGE3D */ + IMAGE2DRECT = 485, /* IMAGE2DRECT */ + IIMAGE2DRECT = 486, /* IIMAGE2DRECT */ + UIMAGE2DRECT = 487, /* UIMAGE2DRECT */ + IMAGECUBE = 488, /* IMAGECUBE */ + IIMAGECUBE = 489, /* IIMAGECUBE */ + UIMAGECUBE = 490, /* UIMAGECUBE */ + IMAGEBUFFER = 491, /* IMAGEBUFFER */ + IIMAGEBUFFER = 492, /* IIMAGEBUFFER */ + UIMAGEBUFFER = 493, /* UIMAGEBUFFER */ + IMAGE1DARRAY = 494, /* IMAGE1DARRAY */ + IIMAGE1DARRAY = 495, /* IIMAGE1DARRAY */ + UIMAGE1DARRAY = 496, /* UIMAGE1DARRAY */ + IMAGE2DARRAY = 497, /* IMAGE2DARRAY */ + IIMAGE2DARRAY = 498, /* IIMAGE2DARRAY */ + UIMAGE2DARRAY = 499, /* UIMAGE2DARRAY */ + IMAGECUBEARRAY = 500, /* IMAGECUBEARRAY */ + IIMAGECUBEARRAY = 501, /* IIMAGECUBEARRAY */ + UIMAGECUBEARRAY = 502, /* UIMAGECUBEARRAY */ + IMAGE2DMS = 503, /* IMAGE2DMS */ + IIMAGE2DMS = 504, /* IIMAGE2DMS */ + UIMAGE2DMS = 505, /* UIMAGE2DMS */ + IMAGE2DMSARRAY = 506, /* IMAGE2DMSARRAY */ + IIMAGE2DMSARRAY = 507, /* IIMAGE2DMSARRAY */ + UIMAGE2DMSARRAY = 508, /* UIMAGE2DMSARRAY */ + F16IMAGE1D = 509, /* F16IMAGE1D */ + F16IMAGE2D = 510, /* F16IMAGE2D */ + F16IMAGE3D = 511, /* F16IMAGE3D */ + F16IMAGE2DRECT = 512, /* F16IMAGE2DRECT */ + F16IMAGECUBE = 513, /* F16IMAGECUBE */ + F16IMAGE1DARRAY = 514, /* F16IMAGE1DARRAY */ + F16IMAGE2DARRAY = 515, /* F16IMAGE2DARRAY */ + F16IMAGECUBEARRAY = 516, /* F16IMAGECUBEARRAY */ + F16IMAGEBUFFER = 517, /* F16IMAGEBUFFER */ + F16IMAGE2DMS = 518, /* F16IMAGE2DMS */ + F16IMAGE2DMSARRAY = 519, /* F16IMAGE2DMSARRAY */ + I64IMAGE1D = 520, /* I64IMAGE1D */ + U64IMAGE1D = 521, /* U64IMAGE1D */ + I64IMAGE2D = 522, /* I64IMAGE2D */ + U64IMAGE2D = 523, /* U64IMAGE2D */ + I64IMAGE3D = 524, /* I64IMAGE3D */ + U64IMAGE3D = 525, /* U64IMAGE3D */ + I64IMAGE2DRECT = 526, /* I64IMAGE2DRECT */ + U64IMAGE2DRECT = 527, /* U64IMAGE2DRECT */ + I64IMAGECUBE = 528, /* I64IMAGECUBE */ + U64IMAGECUBE = 529, /* U64IMAGECUBE */ + I64IMAGEBUFFER = 530, /* I64IMAGEBUFFER */ + U64IMAGEBUFFER = 531, /* U64IMAGEBUFFER */ + I64IMAGE1DARRAY = 532, /* I64IMAGE1DARRAY */ + U64IMAGE1DARRAY = 533, /* U64IMAGE1DARRAY */ + I64IMAGE2DARRAY = 534, /* I64IMAGE2DARRAY */ + U64IMAGE2DARRAY = 535, /* U64IMAGE2DARRAY */ + I64IMAGECUBEARRAY = 536, /* I64IMAGECUBEARRAY */ + U64IMAGECUBEARRAY = 537, /* U64IMAGECUBEARRAY */ + I64IMAGE2DMS = 538, /* I64IMAGE2DMS */ + U64IMAGE2DMS = 539, /* U64IMAGE2DMS */ + I64IMAGE2DMSARRAY = 540, /* I64IMAGE2DMSARRAY */ + U64IMAGE2DMSARRAY = 541, /* U64IMAGE2DMSARRAY */ + TEXTURECUBEARRAY = 542, /* TEXTURECUBEARRAY */ + ITEXTURECUBEARRAY = 543, /* ITEXTURECUBEARRAY */ + UTEXTURECUBEARRAY = 544, /* UTEXTURECUBEARRAY */ + TEXTURE1D = 545, /* TEXTURE1D */ + ITEXTURE1D = 546, /* ITEXTURE1D */ + UTEXTURE1D = 547, /* UTEXTURE1D */ + TEXTURE1DARRAY = 548, /* TEXTURE1DARRAY */ + ITEXTURE1DARRAY = 549, /* ITEXTURE1DARRAY */ + UTEXTURE1DARRAY = 550, /* UTEXTURE1DARRAY */ + TEXTURE2DRECT = 551, /* TEXTURE2DRECT */ + ITEXTURE2DRECT = 552, /* ITEXTURE2DRECT */ + UTEXTURE2DRECT = 553, /* UTEXTURE2DRECT */ + TEXTUREBUFFER = 554, /* TEXTUREBUFFER */ + ITEXTUREBUFFER = 555, /* ITEXTUREBUFFER */ + UTEXTUREBUFFER = 556, /* UTEXTUREBUFFER */ + TEXTURE2DMS = 557, /* TEXTURE2DMS */ + ITEXTURE2DMS = 558, /* ITEXTURE2DMS */ + UTEXTURE2DMS = 559, /* UTEXTURE2DMS */ + TEXTURE2DMSARRAY = 560, /* TEXTURE2DMSARRAY */ + ITEXTURE2DMSARRAY = 561, /* ITEXTURE2DMSARRAY */ + UTEXTURE2DMSARRAY = 562, /* UTEXTURE2DMSARRAY */ + F16TEXTURE1D = 563, /* F16TEXTURE1D */ + F16TEXTURE2D = 564, /* F16TEXTURE2D */ + F16TEXTURE3D = 565, /* F16TEXTURE3D */ + F16TEXTURE2DRECT = 566, /* F16TEXTURE2DRECT */ + F16TEXTURECUBE = 567, /* F16TEXTURECUBE */ + F16TEXTURE1DARRAY = 568, /* F16TEXTURE1DARRAY */ + F16TEXTURE2DARRAY = 569, /* F16TEXTURE2DARRAY */ + F16TEXTURECUBEARRAY = 570, /* F16TEXTURECUBEARRAY */ + F16TEXTUREBUFFER = 571, /* F16TEXTUREBUFFER */ + F16TEXTURE2DMS = 572, /* F16TEXTURE2DMS */ + F16TEXTURE2DMSARRAY = 573, /* F16TEXTURE2DMSARRAY */ + SUBPASSINPUT = 574, /* SUBPASSINPUT */ + SUBPASSINPUTMS = 575, /* SUBPASSINPUTMS */ + ISUBPASSINPUT = 576, /* ISUBPASSINPUT */ + ISUBPASSINPUTMS = 577, /* ISUBPASSINPUTMS */ + USUBPASSINPUT = 578, /* USUBPASSINPUT */ + USUBPASSINPUTMS = 579, /* USUBPASSINPUTMS */ + F16SUBPASSINPUT = 580, /* F16SUBPASSINPUT */ + F16SUBPASSINPUTMS = 581, /* F16SUBPASSINPUTMS */ + SPIRV_INSTRUCTION = 582, /* SPIRV_INSTRUCTION */ + SPIRV_EXECUTION_MODE = 583, /* SPIRV_EXECUTION_MODE */ + SPIRV_EXECUTION_MODE_ID = 584, /* SPIRV_EXECUTION_MODE_ID */ + SPIRV_DECORATE = 585, /* SPIRV_DECORATE */ + SPIRV_DECORATE_ID = 586, /* SPIRV_DECORATE_ID */ + SPIRV_DECORATE_STRING = 587, /* SPIRV_DECORATE_STRING */ + SPIRV_TYPE = 588, /* SPIRV_TYPE */ + SPIRV_STORAGE_CLASS = 589, /* SPIRV_STORAGE_CLASS */ + SPIRV_BY_REFERENCE = 590, /* SPIRV_BY_REFERENCE */ + SPIRV_LITERAL = 591, /* SPIRV_LITERAL */ + ATTACHMENTEXT = 592, /* ATTACHMENTEXT */ + IATTACHMENTEXT = 593, /* IATTACHMENTEXT */ + UATTACHMENTEXT = 594, /* UATTACHMENTEXT */ + LEFT_OP = 595, /* LEFT_OP */ + RIGHT_OP = 596, /* RIGHT_OP */ + INC_OP = 597, /* INC_OP */ + DEC_OP = 598, /* DEC_OP */ + LE_OP = 599, /* LE_OP */ + GE_OP = 600, /* GE_OP */ + EQ_OP = 601, /* EQ_OP */ + NE_OP = 602, /* NE_OP */ + AND_OP = 603, /* AND_OP */ + OR_OP = 604, /* OR_OP */ + XOR_OP = 605, /* XOR_OP */ + MUL_ASSIGN = 606, /* MUL_ASSIGN */ + DIV_ASSIGN = 607, /* DIV_ASSIGN */ + ADD_ASSIGN = 608, /* ADD_ASSIGN */ + MOD_ASSIGN = 609, /* MOD_ASSIGN */ + LEFT_ASSIGN = 610, /* LEFT_ASSIGN */ + RIGHT_ASSIGN = 611, /* RIGHT_ASSIGN */ + AND_ASSIGN = 612, /* AND_ASSIGN */ + XOR_ASSIGN = 613, /* XOR_ASSIGN */ + OR_ASSIGN = 614, /* OR_ASSIGN */ + SUB_ASSIGN = 615, /* SUB_ASSIGN */ + STRING_LITERAL = 616, /* STRING_LITERAL */ + LEFT_PAREN = 617, /* LEFT_PAREN */ + RIGHT_PAREN = 618, /* RIGHT_PAREN */ + LEFT_BRACKET = 619, /* LEFT_BRACKET */ + RIGHT_BRACKET = 620, /* RIGHT_BRACKET */ + LEFT_BRACE = 621, /* LEFT_BRACE */ + RIGHT_BRACE = 622, /* RIGHT_BRACE */ + DOT = 623, /* DOT */ + COMMA = 624, /* COMMA */ + COLON = 625, /* COLON */ + EQUAL = 626, /* EQUAL */ + SEMICOLON = 627, /* SEMICOLON */ + BANG = 628, /* BANG */ + DASH = 629, /* DASH */ + TILDE = 630, /* TILDE */ + PLUS = 631, /* PLUS */ + STAR = 632, /* STAR */ + SLASH = 633, /* SLASH */ + PERCENT = 634, /* PERCENT */ + LEFT_ANGLE = 635, /* LEFT_ANGLE */ + RIGHT_ANGLE = 636, /* RIGHT_ANGLE */ + VERTICAL_BAR = 637, /* VERTICAL_BAR */ + CARET = 638, /* CARET */ + AMPERSAND = 639, /* AMPERSAND */ + QUESTION = 640, /* QUESTION */ + INVARIANT = 641, /* INVARIANT */ + HIGH_PRECISION = 642, /* HIGH_PRECISION */ + MEDIUM_PRECISION = 643, /* MEDIUM_PRECISION */ + LOW_PRECISION = 644, /* LOW_PRECISION */ + PRECISION = 645, /* PRECISION */ + PACKED = 646, /* PACKED */ + RESOURCE = 647, /* RESOURCE */ + SUPERP = 648, /* SUPERP */ + FLOATCONSTANT = 649, /* FLOATCONSTANT */ + INTCONSTANT = 650, /* INTCONSTANT */ + UINTCONSTANT = 651, /* UINTCONSTANT */ + BOOLCONSTANT = 652, /* BOOLCONSTANT */ + IDENTIFIER = 653, /* IDENTIFIER */ + TYPE_NAME = 654, /* TYPE_NAME */ + CENTROID = 655, /* CENTROID */ + IN = 656, /* IN */ + OUT = 657, /* OUT */ + INOUT = 658, /* INOUT */ + STRUCT = 659, /* STRUCT */ + VOID = 660, /* VOID */ + WHILE = 661, /* WHILE */ + BREAK = 662, /* BREAK */ + CONTINUE = 663, /* CONTINUE */ + DO = 664, /* DO */ + ELSE = 665, /* ELSE */ + FOR = 666, /* FOR */ + IF = 667, /* IF */ + DISCARD = 668, /* DISCARD */ + RETURN = 669, /* RETURN */ + SWITCH = 670, /* SWITCH */ + CASE = 671, /* CASE */ + DEFAULT = 672, /* DEFAULT */ + TERMINATE_INVOCATION = 673, /* TERMINATE_INVOCATION */ + TERMINATE_RAY = 674, /* TERMINATE_RAY */ + IGNORE_INTERSECTION = 675, /* IGNORE_INTERSECTION */ + UNIFORM = 676, /* UNIFORM */ + SHARED = 677, /* SHARED */ + BUFFER = 678, /* BUFFER */ + TILEIMAGEEXT = 679, /* TILEIMAGEEXT */ + FLAT = 680, /* FLAT */ + SMOOTH = 681, /* SMOOTH */ + LAYOUT = 682, /* LAYOUT */ + DOUBLECONSTANT = 683, /* DOUBLECONSTANT */ + INT16CONSTANT = 684, /* INT16CONSTANT */ + UINT16CONSTANT = 685, /* UINT16CONSTANT */ + FLOAT16CONSTANT = 686, /* FLOAT16CONSTANT */ + INT32CONSTANT = 687, /* INT32CONSTANT */ + UINT32CONSTANT = 688, /* UINT32CONSTANT */ + INT64CONSTANT = 689, /* INT64CONSTANT */ + UINT64CONSTANT = 690, /* UINT64CONSTANT */ + SUBROUTINE = 691, /* SUBROUTINE */ + DEMOTE = 692, /* DEMOTE */ + FUNCTION = 693, /* FUNCTION */ + PAYLOADNV = 694, /* PAYLOADNV */ + PAYLOADINNV = 695, /* PAYLOADINNV */ + HITATTRNV = 696, /* HITATTRNV */ + CALLDATANV = 697, /* CALLDATANV */ + CALLDATAINNV = 698, /* CALLDATAINNV */ + PAYLOADEXT = 699, /* PAYLOADEXT */ + PAYLOADINEXT = 700, /* PAYLOADINEXT */ + HITATTREXT = 701, /* HITATTREXT */ + CALLDATAEXT = 702, /* CALLDATAEXT */ + CALLDATAINEXT = 703, /* CALLDATAINEXT */ + PATCH = 704, /* PATCH */ + SAMPLE = 705, /* SAMPLE */ + NONUNIFORM = 706, /* NONUNIFORM */ + COHERENT = 707, /* COHERENT */ + VOLATILE = 708, /* VOLATILE */ + RESTRICT = 709, /* RESTRICT */ + READONLY = 710, /* READONLY */ + WRITEONLY = 711, /* WRITEONLY */ + NONTEMPORAL = 712, /* NONTEMPORAL */ + DEVICECOHERENT = 713, /* DEVICECOHERENT */ + QUEUEFAMILYCOHERENT = 714, /* QUEUEFAMILYCOHERENT */ + WORKGROUPCOHERENT = 715, /* WORKGROUPCOHERENT */ + SUBGROUPCOHERENT = 716, /* SUBGROUPCOHERENT */ + NONPRIVATE = 717, /* NONPRIVATE */ + SHADERCALLCOHERENT = 718, /* SHADERCALLCOHERENT */ + NOPERSPECTIVE = 719, /* NOPERSPECTIVE */ + EXPLICITINTERPAMD = 720, /* EXPLICITINTERPAMD */ + PERVERTEXEXT = 721, /* PERVERTEXEXT */ + PERVERTEXNV = 722, /* PERVERTEXNV */ + PERPRIMITIVENV = 723, /* PERPRIMITIVENV */ + PERVIEWNV = 724, /* PERVIEWNV */ + PERTASKNV = 725, /* PERTASKNV */ + PERPRIMITIVEEXT = 726, /* PERPRIMITIVEEXT */ + TASKPAYLOADWORKGROUPEXT = 727, /* TASKPAYLOADWORKGROUPEXT */ + PRECISE = 728 /* PRECISE */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 72 "MachineIndependent/glslang.y" + + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + glslang::TSpirvRequirement* spirvReq; + glslang::TSpirvInstruction* spirvInst; + glslang::TSpirvTypeParameters* spirvTypeParams; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TTypeParameters* typeParameters; + } interm; + +#line 576 "MachineIndependent/glslang_tab.cpp.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + + +int yyparse (glslang::TParseContext* pParseContext); + + +#endif /* !YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/intermOut.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/intermOut.cpp new file mode 100644 index 00000000..866503a7 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/intermOut.cpp @@ -0,0 +1,1499 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017, 2022-2024 Arm Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "localintermediate.h" +#include "../Include/InfoSink.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + + +namespace glslang { + +// +// Two purposes: +// 1. Show an example of how to iterate tree. Functions can +// also directly call Traverse() on children themselves to +// have finer grained control over the process than shown here. +// See the last function for how to get started. +// 2. Print out a text based description of the tree. +// + +// +// Use this class to carry along data from node to node in +// the traversal +// +class TOutputTraverser : public TIntermTraverser { +public: + TOutputTraverser(TInfoSink& i) : infoSink(i), extraOutput(NoExtraOutput) { } + + enum EExtraOutput { + NoExtraOutput, + BinaryDoubleOutput + }; + void setDoubleOutput(EExtraOutput extra) { extraOutput = extra; } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + virtual bool visitSelection(TVisit, TIntermSelection* node); + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual void visitSymbol(TIntermSymbol* node); + virtual bool visitLoop(TVisit, TIntermLoop* node); + virtual bool visitBranch(TVisit, TIntermBranch* node); + virtual bool visitSwitch(TVisit, TIntermSwitch* node); + + TInfoSink& infoSink; +protected: + TOutputTraverser(TOutputTraverser&); + TOutputTraverser& operator=(TOutputTraverser&); + + EExtraOutput extraOutput; +}; + +// +// Helper functions for printing, not part of traversing. +// + +static void OutputTreeText(TInfoSink& infoSink, const TIntermNode* node, const int depth) +{ + int i; + + infoSink.debug << node->getLoc().string << ":"; + if (node->getLoc().line) + infoSink.debug << node->getLoc().line; + else + infoSink.debug << "? "; + + for (i = 0; i < depth; ++i) + infoSink.debug << " "; +} + +// +// The rest of the file are the traversal functions. The last one +// is the one that starts the traversal. +// +// Return true from interior nodes to have the external traversal +// continue on to children. If you process children yourself, +// return false. +// + +bool TOutputTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpAssign: out.debug << "move second child to first child"; break; + case EOpAddAssign: out.debug << "add second child into first child"; break; + case EOpSubAssign: out.debug << "subtract second child into first child"; break; + case EOpMulAssign: out.debug << "multiply second child into first child"; break; + case EOpVectorTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpVectorTimesScalarAssign: out.debug << "vector scale second child into first child"; break; + case EOpMatrixTimesScalarAssign: out.debug << "matrix scale second child into first child"; break; + case EOpMatrixTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpDivAssign: out.debug << "divide second child into first child"; break; + case EOpModAssign: out.debug << "mod second child into first child"; break; + case EOpAndAssign: out.debug << "and second child into first child"; break; + case EOpInclusiveOrAssign: out.debug << "or second child into first child"; break; + case EOpExclusiveOrAssign: out.debug << "exclusive or second child into first child"; break; + case EOpLeftShiftAssign: out.debug << "left shift second child into first child"; break; + case EOpRightShiftAssign: out.debug << "right shift second child into first child"; break; + + case EOpIndexDirect: out.debug << "direct index"; break; + case EOpIndexIndirect: out.debug << "indirect index"; break; + case EOpIndexDirectStruct: + { + bool reference = node->getLeft()->getType().isReference(); + const TTypeList *members = reference ? node->getLeft()->getType().getReferentType()->getStruct() : node->getLeft()->getType().getStruct(); + out.debug << (*members)[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName(); + out.debug << ": direct index for structure"; break; + } + case EOpVectorSwizzle: out.debug << "vector swizzle"; break; + case EOpMatrixSwizzle: out.debug << "matrix swizzle"; break; + + case EOpAdd: out.debug << "add"; break; + case EOpSub: out.debug << "subtract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpDiv: out.debug << "divide"; break; + case EOpMod: out.debug << "mod"; break; + case EOpRightShift: out.debug << "right-shift"; break; + case EOpLeftShift: out.debug << "left-shift"; break; + case EOpAnd: out.debug << "bitwise and"; break; + case EOpInclusiveOr: out.debug << "inclusive-or"; break; + case EOpExclusiveOr: out.debug << "exclusive-or"; break; + case EOpEqual: out.debug << "Compare Equal"; break; + case EOpNotEqual: out.debug << "Compare Not Equal"; break; + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpVectorTimesScalar: out.debug << "vector-scale"; break; + case EOpVectorTimesMatrix: out.debug << "vector-times-matrix"; break; + case EOpMatrixTimesVector: out.debug << "matrix-times-vector"; break; + case EOpMatrixTimesScalar: out.debug << "matrix-scale"; break; + case EOpMatrixTimesMatrix: out.debug << "matrix-multiply"; break; + + case EOpLogicalOr: out.debug << "logical-or"; break; + case EOpLogicalXor: out.debug << "logical-xor"; break; + case EOpLogicalAnd: out.debug << "logical-and"; break; + + case EOpAbsDifference: out.debug << "absoluteDifference"; break; + case EOpAddSaturate: out.debug << "addSaturate"; break; + case EOpSubSaturate: out.debug << "subtractSaturate"; break; + case EOpAverage: out.debug << "average"; break; + case EOpAverageRounded: out.debug << "averageRounded"; break; + case EOpMul32x16: out.debug << "multiply32x16"; break; + + default: out.debug << ""; + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + if (IsOpNumericConv(node->getAsOperator()->getOp())) { + out.debug << "Convert " << TType::getBasicString(node->getOperand()->getType().getBasicType()) << " to " << TType::getBasicString(node->getType().getBasicType()); + out.debug << " (" << node->getCompleteString() << ")"; + out.debug << "\n"; + return true; + } + + switch (node->getOp()) { + case EOpNegative: out.debug << "Negate value"; break; + case EOpVectorLogicalNot: + case EOpLogicalNot: out.debug << "Negate conditional"; break; + case EOpBitwiseNot: out.debug << "Bitwise not"; break; + + case EOpPostIncrement: out.debug << "Post-Increment"; break; + case EOpPostDecrement: out.debug << "Post-Decrement"; break; + case EOpPreIncrement: out.debug << "Pre-Increment"; break; + case EOpPreDecrement: out.debug << "Pre-Decrement"; break; + case EOpCopyObject: out.debug << "copy object"; break; + + case EOpConvUint64ToPtr: out.debug << "Convert uint64_t to pointer"; break; + case EOpConvPtrToUint64: out.debug << "Convert pointer to uint64_t"; break; + + case EOpConvUint64ToAccStruct: out.debug << "Convert uint64_t to acceleration structure"; break; + case EOpConvUvec2ToAccStruct: out.debug << "Convert uvec2 to acceleration strucuture "; break; + + case EOpRadians: out.debug << "radians"; break; + case EOpDegrees: out.debug << "degrees"; break; + case EOpSin: out.debug << "sine"; break; + case EOpCos: out.debug << "cosine"; break; + case EOpTan: out.debug << "tangent"; break; + case EOpAsin: out.debug << "arc sine"; break; + case EOpAcos: out.debug << "arc cosine"; break; + case EOpAtan: out.debug << "arc tangent"; break; + case EOpSinh: out.debug << "hyp. sine"; break; + case EOpCosh: out.debug << "hyp. cosine"; break; + case EOpTanh: out.debug << "hyp. tangent"; break; + case EOpAsinh: out.debug << "arc hyp. sine"; break; + case EOpAcosh: out.debug << "arc hyp. cosine"; break; + case EOpAtanh: out.debug << "arc hyp. tangent"; break; + + case EOpExp: out.debug << "exp"; break; + case EOpLog: out.debug << "log"; break; + case EOpExp2: out.debug << "exp2"; break; + case EOpLog2: out.debug << "log2"; break; + case EOpSqrt: out.debug << "sqrt"; break; + case EOpInverseSqrt: out.debug << "inverse sqrt"; break; + + case EOpAbs: out.debug << "Absolute value"; break; + case EOpSign: out.debug << "Sign"; break; + case EOpFloor: out.debug << "Floor"; break; + case EOpTrunc: out.debug << "trunc"; break; + case EOpRound: out.debug << "round"; break; + case EOpRoundEven: out.debug << "roundEven"; break; + case EOpCeil: out.debug << "Ceiling"; break; + case EOpFract: out.debug << "Fraction"; break; + + case EOpIsNan: out.debug << "isnan"; break; + case EOpIsInf: out.debug << "isinf"; break; + + case EOpFloatBitsToInt: out.debug << "floatBitsToInt"; break; + case EOpFloatBitsToUint:out.debug << "floatBitsToUint"; break; + case EOpIntBitsToFloat: out.debug << "intBitsToFloat"; break; + case EOpUintBitsToFloat:out.debug << "uintBitsToFloat"; break; + case EOpDoubleBitsToInt64: out.debug << "doubleBitsToInt64"; break; + case EOpDoubleBitsToUint64: out.debug << "doubleBitsToUint64"; break; + case EOpInt64BitsToDouble: out.debug << "int64BitsToDouble"; break; + case EOpUint64BitsToDouble: out.debug << "uint64BitsToDouble"; break; + case EOpFloat16BitsToInt16: out.debug << "float16BitsToInt16"; break; + case EOpFloat16BitsToUint16: out.debug << "float16BitsToUint16"; break; + case EOpInt16BitsToFloat16: out.debug << "int16BitsToFloat16"; break; + case EOpUint16BitsToFloat16: out.debug << "uint16BitsToFloat16"; break; + + case EOpPackSnorm2x16: out.debug << "packSnorm2x16"; break; + case EOpUnpackSnorm2x16:out.debug << "unpackSnorm2x16"; break; + case EOpPackUnorm2x16: out.debug << "packUnorm2x16"; break; + case EOpUnpackUnorm2x16:out.debug << "unpackUnorm2x16"; break; + case EOpPackHalf2x16: out.debug << "packHalf2x16"; break; + case EOpUnpackHalf2x16: out.debug << "unpackHalf2x16"; break; + case EOpPack16: out.debug << "pack16"; break; + case EOpPack32: out.debug << "pack32"; break; + case EOpPack64: out.debug << "pack64"; break; + case EOpUnpack32: out.debug << "unpack32"; break; + case EOpUnpack16: out.debug << "unpack16"; break; + case EOpUnpack8: out.debug << "unpack8"; break; + + case EOpPackSnorm4x8: out.debug << "PackSnorm4x8"; break; + case EOpUnpackSnorm4x8: out.debug << "UnpackSnorm4x8"; break; + case EOpPackUnorm4x8: out.debug << "PackUnorm4x8"; break; + case EOpUnpackUnorm4x8: out.debug << "UnpackUnorm4x8"; break; + case EOpPackDouble2x32: out.debug << "PackDouble2x32"; break; + case EOpUnpackDouble2x32: out.debug << "UnpackDouble2x32"; break; + + case EOpPackInt2x32: out.debug << "packInt2x32"; break; + case EOpUnpackInt2x32: out.debug << "unpackInt2x32"; break; + case EOpPackUint2x32: out.debug << "packUint2x32"; break; + case EOpUnpackUint2x32: out.debug << "unpackUint2x32"; break; + + case EOpPackInt2x16: out.debug << "packInt2x16"; break; + case EOpUnpackInt2x16: out.debug << "unpackInt2x16"; break; + case EOpPackUint2x16: out.debug << "packUint2x16"; break; + case EOpUnpackUint2x16: out.debug << "unpackUint2x16"; break; + + case EOpPackInt4x16: out.debug << "packInt4x16"; break; + case EOpUnpackInt4x16: out.debug << "unpackInt4x16"; break; + case EOpPackUint4x16: out.debug << "packUint4x16"; break; + case EOpUnpackUint4x16: out.debug << "unpackUint4x16"; break; + case EOpPackFloat2x16: out.debug << "packFloat2x16"; break; + case EOpUnpackFloat2x16: out.debug << "unpackFloat2x16"; break; + + case EOpLength: out.debug << "length"; break; + case EOpNormalize: out.debug << "normalize"; break; + case EOpDPdx: out.debug << "dPdx"; break; + case EOpDPdy: out.debug << "dPdy"; break; + case EOpFwidth: out.debug << "fwidth"; break; + case EOpDPdxFine: out.debug << "dPdxFine"; break; + case EOpDPdyFine: out.debug << "dPdyFine"; break; + case EOpFwidthFine: out.debug << "fwidthFine"; break; + case EOpDPdxCoarse: out.debug << "dPdxCoarse"; break; + case EOpDPdyCoarse: out.debug << "dPdyCoarse"; break; + case EOpFwidthCoarse: out.debug << "fwidthCoarse"; break; + + case EOpInterpolateAtCentroid: out.debug << "interpolateAtCentroid"; break; + + case EOpDeterminant: out.debug << "determinant"; break; + case EOpMatrixInverse: out.debug << "inverse"; break; + case EOpTranspose: out.debug << "transpose"; break; + + case EOpAny: out.debug << "any"; break; + case EOpAll: out.debug << "all"; break; + + case EOpArrayLength: out.debug << "array length"; break; + + case EOpEmitStreamVertex: out.debug << "EmitStreamVertex"; break; + case EOpEndStreamPrimitive: out.debug << "EndStreamPrimitive"; break; + + case EOpAtomicCounterIncrement: out.debug << "AtomicCounterIncrement";break; + case EOpAtomicCounterDecrement: out.debug << "AtomicCounterDecrement";break; + case EOpAtomicCounter: out.debug << "AtomicCounter"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + + case EOpBitFieldReverse: out.debug << "bitFieldReverse"; break; + case EOpBitCount: out.debug << "bitCount"; break; + case EOpFindLSB: out.debug << "findLSB"; break; + case EOpFindMSB: out.debug << "findMSB"; break; + + case EOpCountLeadingZeros: out.debug << "countLeadingZeros"; break; + case EOpCountTrailingZeros: out.debug << "countTrailingZeros"; break; + + case EOpNoise: out.debug << "noise"; break; + + case EOpBallot: out.debug << "ballot"; break; + case EOpReadFirstInvocation: out.debug << "readFirstInvocation"; break; + + case EOpAnyInvocation: out.debug << "anyInvocation"; break; + case EOpAllInvocations: out.debug << "allInvocations"; break; + case EOpAllInvocationsEqual: out.debug << "allInvocationsEqual"; break; + + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupRotate: out.debug << "subgroupRotate"; break; + case EOpSubgroupClusteredRotate: out.debug << "subgroupClusteredRotate"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + case EOpSubgroupQuadAll: out.debug << "subgroupQuadAll"; break; + case EOpSubgroupQuadAny: out.debug << "subgroupQuadAny"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpClip: out.debug << "clip"; break; + case EOpIsFinite: out.debug << "isfinite"; break; + case EOpLog10: out.debug << "log10"; break; + case EOpRcp: out.debug << "rcp"; break; + case EOpSaturate: out.debug << "saturate"; break; + + case EOpSparseTexelsResident: out.debug << "sparseTexelsResident"; break; + + case EOpMinInvocations: out.debug << "minInvocations"; break; + case EOpMaxInvocations: out.debug << "maxInvocations"; break; + case EOpAddInvocations: out.debug << "addInvocations"; break; + case EOpMinInvocationsNonUniform: out.debug << "minInvocationsNonUniform"; break; + case EOpMaxInvocationsNonUniform: out.debug << "maxInvocationsNonUniform"; break; + case EOpAddInvocationsNonUniform: out.debug << "addInvocationsNonUniform"; break; + + case EOpMinInvocationsInclusiveScan: out.debug << "minInvocationsInclusiveScan"; break; + case EOpMaxInvocationsInclusiveScan: out.debug << "maxInvocationsInclusiveScan"; break; + case EOpAddInvocationsInclusiveScan: out.debug << "addInvocationsInclusiveScan"; break; + case EOpMinInvocationsInclusiveScanNonUniform: out.debug << "minInvocationsInclusiveScanNonUniform"; break; + case EOpMaxInvocationsInclusiveScanNonUniform: out.debug << "maxInvocationsInclusiveScanNonUniform"; break; + case EOpAddInvocationsInclusiveScanNonUniform: out.debug << "addInvocationsInclusiveScanNonUniform"; break; + + case EOpMinInvocationsExclusiveScan: out.debug << "minInvocationsExclusiveScan"; break; + case EOpMaxInvocationsExclusiveScan: out.debug << "maxInvocationsExclusiveScan"; break; + case EOpAddInvocationsExclusiveScan: out.debug << "addInvocationsExclusiveScan"; break; + case EOpMinInvocationsExclusiveScanNonUniform: out.debug << "minInvocationsExclusiveScanNonUniform"; break; + case EOpMaxInvocationsExclusiveScanNonUniform: out.debug << "maxInvocationsExclusiveScanNonUniform"; break; + case EOpAddInvocationsExclusiveScanNonUniform: out.debug << "addInvocationsExclusiveScanNonUniform"; break; + + case EOpMbcnt: out.debug << "mbcnt"; break; + + case EOpFragmentMaskFetch: out.debug << "fragmentMaskFetchAMD"; break; + case EOpFragmentFetch: out.debug << "fragmentFetchAMD"; break; + + case EOpCubeFaceIndex: out.debug << "cubeFaceIndex"; break; + case EOpCubeFaceCoord: out.debug << "cubeFaceCoord"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpColorAttachmentReadEXT: out.debug << "colorAttachmentReadEXT"; break; + + case EOpConstructReference: out.debug << "Construct reference type"; break; + + case EOpDeclare: out.debug << "Declare"; break; + + case EOpSpirvInst: out.debug << "spirv_instruction"; break; + + case EOpCreateTensorLayoutNV: out.debug << "createTensorLayoutNV"; break; + case EOpTensorLayoutSetBlockSizeNV: out.debug << "setTensorLayoutBlockSizeNV"; break; + case EOpTensorLayoutSetDimensionNV: out.debug << "setTensorLayoutDimensionNV"; break; + case EOpTensorLayoutSetStrideNV: out.debug << "setTensorLayoutStrideNV"; break; + case EOpTensorLayoutSliceNV: out.debug << "sliceTensorLayoutNV"; break; + case EOpTensorLayoutSetClampValueNV: out.debug << "setTensorLayoutClampValueNV"; break; + case EOpCreateTensorViewNV: out.debug << "createTensorViewNV"; break; + case EOpTensorViewSetDimensionNV: out.debug << "setTensorViewDimensionsNV"; break; + case EOpTensorViewSetStrideNV: out.debug << "setTensorViewStrideNV"; break; + case EOpTensorViewSetClipNV: out.debug << "setTensorViewClipNV"; break; + + default: out.debug.message(EPrefixError, "Bad unary op"); + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + TInfoSink& out = infoSink; + + if (node->getOp() == EOpNull) { + out.debug.message(EPrefixError, "node is still EOpNull!"); + return true; + } + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpSequence: out.debug << "Sequence\n"; return true; + case EOpScope: out.debug << "Scope\n"; return true; + case EOpLinkerObjects: out.debug << "Linker Objects\n"; return true; + case EOpComma: out.debug << "Comma"; break; + case EOpFunction: out.debug << "Function Definition: " << node->getName(); break; + case EOpFunctionCall: out.debug << "Function Call: " << node->getName(); break; + case EOpParameters: out.debug << "Function Parameters: "; break; + + case EOpConstructFloat: out.debug << "Construct float"; break; + case EOpConstructDouble:out.debug << "Construct double"; break; + + case EOpConstructVec2: out.debug << "Construct vec2"; break; + case EOpConstructVec3: out.debug << "Construct vec3"; break; + case EOpConstructVec4: out.debug << "Construct vec4"; break; + case EOpConstructDVec2: out.debug << "Construct dvec2"; break; + case EOpConstructDVec3: out.debug << "Construct dvec3"; break; + case EOpConstructDVec4: out.debug << "Construct dvec4"; break; + case EOpConstructBool: out.debug << "Construct bool"; break; + case EOpConstructBVec2: out.debug << "Construct bvec2"; break; + case EOpConstructBVec3: out.debug << "Construct bvec3"; break; + case EOpConstructBVec4: out.debug << "Construct bvec4"; break; + case EOpConstructInt8: out.debug << "Construct int8_t"; break; + case EOpConstructI8Vec2: out.debug << "Construct i8vec2"; break; + case EOpConstructI8Vec3: out.debug << "Construct i8vec3"; break; + case EOpConstructI8Vec4: out.debug << "Construct i8vec4"; break; + case EOpConstructInt: out.debug << "Construct int"; break; + case EOpConstructIVec2: out.debug << "Construct ivec2"; break; + case EOpConstructIVec3: out.debug << "Construct ivec3"; break; + case EOpConstructIVec4: out.debug << "Construct ivec4"; break; + case EOpConstructUint8: out.debug << "Construct uint8_t"; break; + case EOpConstructU8Vec2: out.debug << "Construct u8vec2"; break; + case EOpConstructU8Vec3: out.debug << "Construct u8vec3"; break; + case EOpConstructU8Vec4: out.debug << "Construct u8vec4"; break; + case EOpConstructUint: out.debug << "Construct uint"; break; + case EOpConstructUVec2: out.debug << "Construct uvec2"; break; + case EOpConstructUVec3: out.debug << "Construct uvec3"; break; + case EOpConstructUVec4: out.debug << "Construct uvec4"; break; + case EOpConstructInt64: out.debug << "Construct int64"; break; + case EOpConstructI64Vec2: out.debug << "Construct i64vec2"; break; + case EOpConstructI64Vec3: out.debug << "Construct i64vec3"; break; + case EOpConstructI64Vec4: out.debug << "Construct i64vec4"; break; + case EOpConstructUint64: out.debug << "Construct uint64"; break; + case EOpConstructU64Vec2: out.debug << "Construct u64vec2"; break; + case EOpConstructU64Vec3: out.debug << "Construct u64vec3"; break; + case EOpConstructU64Vec4: out.debug << "Construct u64vec4"; break; + case EOpConstructInt16: out.debug << "Construct int16_t"; break; + case EOpConstructI16Vec2: out.debug << "Construct i16vec2"; break; + case EOpConstructI16Vec3: out.debug << "Construct i16vec3"; break; + case EOpConstructI16Vec4: out.debug << "Construct i16vec4"; break; + case EOpConstructUint16: out.debug << "Construct uint16_t"; break; + case EOpConstructU16Vec2: out.debug << "Construct u16vec2"; break; + case EOpConstructU16Vec3: out.debug << "Construct u16vec3"; break; + case EOpConstructU16Vec4: out.debug << "Construct u16vec4"; break; + case EOpConstructMat2x2: out.debug << "Construct mat2"; break; + case EOpConstructMat2x3: out.debug << "Construct mat2x3"; break; + case EOpConstructMat2x4: out.debug << "Construct mat2x4"; break; + case EOpConstructMat3x2: out.debug << "Construct mat3x2"; break; + case EOpConstructMat3x3: out.debug << "Construct mat3"; break; + case EOpConstructMat3x4: out.debug << "Construct mat3x4"; break; + case EOpConstructMat4x2: out.debug << "Construct mat4x2"; break; + case EOpConstructMat4x3: out.debug << "Construct mat4x3"; break; + case EOpConstructMat4x4: out.debug << "Construct mat4"; break; + case EOpConstructDMat2x2: out.debug << "Construct dmat2"; break; + case EOpConstructDMat2x3: out.debug << "Construct dmat2x3"; break; + case EOpConstructDMat2x4: out.debug << "Construct dmat2x4"; break; + case EOpConstructDMat3x2: out.debug << "Construct dmat3x2"; break; + case EOpConstructDMat3x3: out.debug << "Construct dmat3"; break; + case EOpConstructDMat3x4: out.debug << "Construct dmat3x4"; break; + case EOpConstructDMat4x2: out.debug << "Construct dmat4x2"; break; + case EOpConstructDMat4x3: out.debug << "Construct dmat4x3"; break; + case EOpConstructDMat4x4: out.debug << "Construct dmat4"; break; + case EOpConstructIMat2x2: out.debug << "Construct imat2"; break; + case EOpConstructIMat2x3: out.debug << "Construct imat2x3"; break; + case EOpConstructIMat2x4: out.debug << "Construct imat2x4"; break; + case EOpConstructIMat3x2: out.debug << "Construct imat3x2"; break; + case EOpConstructIMat3x3: out.debug << "Construct imat3"; break; + case EOpConstructIMat3x4: out.debug << "Construct imat3x4"; break; + case EOpConstructIMat4x2: out.debug << "Construct imat4x2"; break; + case EOpConstructIMat4x3: out.debug << "Construct imat4x3"; break; + case EOpConstructIMat4x4: out.debug << "Construct imat4"; break; + case EOpConstructUMat2x2: out.debug << "Construct umat2"; break; + case EOpConstructUMat2x3: out.debug << "Construct umat2x3"; break; + case EOpConstructUMat2x4: out.debug << "Construct umat2x4"; break; + case EOpConstructUMat3x2: out.debug << "Construct umat3x2"; break; + case EOpConstructUMat3x3: out.debug << "Construct umat3"; break; + case EOpConstructUMat3x4: out.debug << "Construct umat3x4"; break; + case EOpConstructUMat4x2: out.debug << "Construct umat4x2"; break; + case EOpConstructUMat4x3: out.debug << "Construct umat4x3"; break; + case EOpConstructUMat4x4: out.debug << "Construct umat4"; break; + case EOpConstructBMat2x2: out.debug << "Construct bmat2"; break; + case EOpConstructBMat2x3: out.debug << "Construct bmat2x3"; break; + case EOpConstructBMat2x4: out.debug << "Construct bmat2x4"; break; + case EOpConstructBMat3x2: out.debug << "Construct bmat3x2"; break; + case EOpConstructBMat3x3: out.debug << "Construct bmat3"; break; + case EOpConstructBMat3x4: out.debug << "Construct bmat3x4"; break; + case EOpConstructBMat4x2: out.debug << "Construct bmat4x2"; break; + case EOpConstructBMat4x3: out.debug << "Construct bmat4x3"; break; + case EOpConstructBMat4x4: out.debug << "Construct bmat4"; break; + case EOpConstructBFloat16: out.debug << "Construct bfloat16_t"; break; + case EOpConstructBF16Vec2: out.debug << "Construct bf16vec2"; break; + case EOpConstructBF16Vec3: out.debug << "Construct bf16vec3"; break; + case EOpConstructBF16Vec4: out.debug << "Construct bf16vec4"; break; + case EOpConstructFloat16: out.debug << "Construct float16_t"; break; + case EOpConstructF16Vec2: out.debug << "Construct f16vec2"; break; + case EOpConstructF16Vec3: out.debug << "Construct f16vec3"; break; + case EOpConstructF16Vec4: out.debug << "Construct f16vec4"; break; + case EOpConstructF16Mat2x2: out.debug << "Construct f16mat2"; break; + case EOpConstructF16Mat2x3: out.debug << "Construct f16mat2x3"; break; + case EOpConstructF16Mat2x4: out.debug << "Construct f16mat2x4"; break; + case EOpConstructF16Mat3x2: out.debug << "Construct f16mat3x2"; break; + case EOpConstructF16Mat3x3: out.debug << "Construct f16mat3"; break; + case EOpConstructF16Mat3x4: out.debug << "Construct f16mat3x4"; break; + case EOpConstructF16Mat4x2: out.debug << "Construct f16mat4x2"; break; + case EOpConstructF16Mat4x3: out.debug << "Construct f16mat4x3"; break; + case EOpConstructF16Mat4x4: out.debug << "Construct f16mat4"; break; + case EOpConstructStruct: out.debug << "Construct structure"; break; + case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break; + case EOpConstructReference: out.debug << "Construct reference"; break; + case EOpConstructCooperativeMatrixNV: out.debug << "Construct cooperative matrix NV"; break; + case EOpConstructCooperativeMatrixKHR: out.debug << "Construct cooperative matrix KHR"; break; + case EOpConstructCooperativeVectorNV: out.debug << "Construct cooperative vector NV"; break; + case EOpConstructAccStruct: out.debug << "Construct acceleration structure"; break; + + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpMod: out.debug << "mod"; break; + case EOpModf: out.debug << "modf"; break; + case EOpPow: out.debug << "pow"; break; + + case EOpAtan: out.debug << "arc tangent"; break; + + case EOpMin: out.debug << "min"; break; + case EOpMax: out.debug << "max"; break; + case EOpClamp: out.debug << "clamp"; break; + case EOpMix: out.debug << "mix"; break; + case EOpStep: out.debug << "step"; break; + case EOpSmoothStep: out.debug << "smoothstep"; break; + + case EOpDistance: out.debug << "distance"; break; + case EOpDot: out.debug << "dot-product"; break; + case EOpDotPackedEXT: out.debug << "dot-product-packed";break; + case EOpDotAccSatEXT: out.debug << "dot-product-accumulate-saturate";break; + case EOpDotPackedAccSatEXT: out.debug << "dot-product-packed-accumulate-saturate";break; + case EOpCross: out.debug << "cross-product"; break; + case EOpFaceForward: out.debug << "face-forward"; break; + case EOpReflect: out.debug << "reflect"; break; + case EOpRefract: out.debug << "refract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpOuterProduct: out.debug << "outer product"; break; + + case EOpEmitVertex: out.debug << "EmitVertex"; break; + case EOpEndPrimitive: out.debug << "EndPrimitive"; break; + + case EOpBarrier: out.debug << "Barrier"; break; + case EOpMemoryBarrier: out.debug << "MemoryBarrier"; break; + case EOpMemoryBarrierAtomicCounter: out.debug << "MemoryBarrierAtomicCounter"; break; + case EOpMemoryBarrierBuffer: out.debug << "MemoryBarrierBuffer"; break; + case EOpMemoryBarrierImage: out.debug << "MemoryBarrierImage"; break; + case EOpMemoryBarrierShared: out.debug << "MemoryBarrierShared"; break; + case EOpGroupMemoryBarrier: out.debug << "GroupMemoryBarrier"; break; + + case EOpReadInvocation: out.debug << "readInvocation"; break; + + case EOpSwizzleInvocations: out.debug << "swizzleInvocations"; break; + case EOpSwizzleInvocationsMasked: out.debug << "swizzleInvocationsMasked"; break; + case EOpWriteInvocation: out.debug << "writeInvocation"; break; + + case EOpMin3: out.debug << "min3"; break; + case EOpMax3: out.debug << "max3"; break; + case EOpMid3: out.debug << "mid3"; break; + case EOpTime: out.debug << "time"; break; + + case EOpAtomicAdd: out.debug << "AtomicAdd"; break; + case EOpAtomicSubtract: out.debug << "AtomicSubtract"; break; + case EOpAtomicMin: out.debug << "AtomicMin"; break; + case EOpAtomicMax: out.debug << "AtomicMax"; break; + case EOpAtomicAnd: out.debug << "AtomicAnd"; break; + case EOpAtomicOr: out.debug << "AtomicOr"; break; + case EOpAtomicXor: out.debug << "AtomicXor"; break; + case EOpAtomicExchange: out.debug << "AtomicExchange"; break; + case EOpAtomicCompSwap: out.debug << "AtomicCompSwap"; break; + case EOpAtomicLoad: out.debug << "AtomicLoad"; break; + case EOpAtomicStore: out.debug << "AtomicStore"; break; + + case EOpAtomicCounterAdd: out.debug << "AtomicCounterAdd"; break; + case EOpAtomicCounterSubtract: out.debug << "AtomicCounterSubtract"; break; + case EOpAtomicCounterMin: out.debug << "AtomicCounterMin"; break; + case EOpAtomicCounterMax: out.debug << "AtomicCounterMax"; break; + case EOpAtomicCounterAnd: out.debug << "AtomicCounterAnd"; break; + case EOpAtomicCounterOr: out.debug << "AtomicCounterOr"; break; + case EOpAtomicCounterXor: out.debug << "AtomicCounterXor"; break; + case EOpAtomicCounterExchange: out.debug << "AtomicCounterExchange"; break; + case EOpAtomicCounterCompSwap: out.debug << "AtomicCounterCompSwap"; break; + + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + case EOpImageStore: out.debug << "imageStore"; break; + case EOpImageAtomicAdd: out.debug << "imageAtomicAdd"; break; + case EOpImageAtomicMin: out.debug << "imageAtomicMin"; break; + case EOpImageAtomicMax: out.debug << "imageAtomicMax"; break; + case EOpImageAtomicAnd: out.debug << "imageAtomicAnd"; break; + case EOpImageAtomicOr: out.debug << "imageAtomicOr"; break; + case EOpImageAtomicXor: out.debug << "imageAtomicXor"; break; + case EOpImageAtomicExchange: out.debug << "imageAtomicExchange"; break; + case EOpImageAtomicCompSwap: out.debug << "imageAtomicCompSwap"; break; + case EOpImageAtomicLoad: out.debug << "imageAtomicLoad"; break; + case EOpImageAtomicStore: out.debug << "imageAtomicStore"; break; + case EOpImageLoadLod: out.debug << "imageLoadLod"; break; + case EOpImageStoreLod: out.debug << "imageStoreLod"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpTexture: out.debug << "texture"; break; + case EOpTextureProj: out.debug << "textureProj"; break; + case EOpTextureLod: out.debug << "textureLod"; break; + case EOpTextureOffset: out.debug << "textureOffset"; break; + case EOpTextureFetch: out.debug << "textureFetch"; break; + case EOpTextureFetchOffset: out.debug << "textureFetchOffset"; break; + case EOpTextureProjOffset: out.debug << "textureProjOffset"; break; + case EOpTextureLodOffset: out.debug << "textureLodOffset"; break; + case EOpTextureProjLod: out.debug << "textureProjLod"; break; + case EOpTextureProjLodOffset: out.debug << "textureProjLodOffset"; break; + case EOpTextureGrad: out.debug << "textureGrad"; break; + case EOpTextureGradOffset: out.debug << "textureGradOffset"; break; + case EOpTextureProjGrad: out.debug << "textureProjGrad"; break; + case EOpTextureProjGradOffset: out.debug << "textureProjGradOffset"; break; + case EOpTextureGather: out.debug << "textureGather"; break; + case EOpTextureGatherOffset: out.debug << "textureGatherOffset"; break; + case EOpTextureGatherOffsets: out.debug << "textureGatherOffsets"; break; + case EOpTextureClamp: out.debug << "textureClamp"; break; + case EOpTextureOffsetClamp: out.debug << "textureOffsetClamp"; break; + case EOpTextureGradClamp: out.debug << "textureGradClamp"; break; + case EOpTextureGradOffsetClamp: out.debug << "textureGradOffsetClamp"; break; + case EOpTextureGatherLod: out.debug << "textureGatherLod"; break; + case EOpTextureGatherLodOffset: out.debug << "textureGatherLodOffset"; break; + case EOpTextureGatherLodOffsets: out.debug << "textureGatherLodOffsets"; break; + + case EOpSparseTexture: out.debug << "sparseTexture"; break; + case EOpSparseTextureOffset: out.debug << "sparseTextureOffset"; break; + case EOpSparseTextureLod: out.debug << "sparseTextureLod"; break; + case EOpSparseTextureLodOffset: out.debug << "sparseTextureLodOffset"; break; + case EOpSparseTextureFetch: out.debug << "sparseTexelFetch"; break; + case EOpSparseTextureFetchOffset: out.debug << "sparseTexelFetchOffset"; break; + case EOpSparseTextureGrad: out.debug << "sparseTextureGrad"; break; + case EOpSparseTextureGradOffset: out.debug << "sparseTextureGradOffset"; break; + case EOpSparseTextureGather: out.debug << "sparseTextureGather"; break; + case EOpSparseTextureGatherOffset: out.debug << "sparseTextureGatherOffset"; break; + case EOpSparseTextureGatherOffsets: out.debug << "sparseTextureGatherOffsets"; break; + case EOpSparseImageLoad: out.debug << "sparseImageLoad"; break; + case EOpSparseTextureClamp: out.debug << "sparseTextureClamp"; break; + case EOpSparseTextureOffsetClamp: out.debug << "sparseTextureOffsetClamp"; break; + case EOpSparseTextureGradClamp: out.debug << "sparseTextureGradClamp"; break; + case EOpSparseTextureGradOffsetClamp: out.debug << "sparseTextureGradOffsetClam"; break; + case EOpSparseTextureGatherLod: out.debug << "sparseTextureGatherLod"; break; + case EOpSparseTextureGatherLodOffset: out.debug << "sparseTextureGatherLodOffset"; break; + case EOpSparseTextureGatherLodOffsets: out.debug << "sparseTextureGatherLodOffsets"; break; + case EOpSparseImageLoadLod: out.debug << "sparseImageLoadLod"; break; + case EOpImageSampleFootprintNV: out.debug << "imageSampleFootprintNV"; break; + case EOpImageSampleFootprintClampNV: out.debug << "imageSampleFootprintClampNV"; break; + case EOpImageSampleFootprintLodNV: out.debug << "imageSampleFootprintLodNV"; break; + case EOpImageSampleFootprintGradNV: out.debug << "imageSampleFootprintGradNV"; break; + case EOpImageSampleFootprintGradClampNV: out.debug << "mageSampleFootprintGradClampNV"; break; + case EOpAddCarry: out.debug << "addCarry"; break; + case EOpSubBorrow: out.debug << "subBorrow"; break; + case EOpUMulExtended: out.debug << "uMulExtended"; break; + case EOpIMulExtended: out.debug << "iMulExtended"; break; + case EOpBitfieldExtract: out.debug << "bitfieldExtract"; break; + case EOpBitfieldInsert: out.debug << "bitfieldInsert"; break; + + case EOpFma: out.debug << "fma"; break; + case EOpFrexp: out.debug << "frexp"; break; + case EOpLdexp: out.debug << "ldexp"; break; + + case EOpInterpolateAtSample: out.debug << "interpolateAtSample"; break; + case EOpInterpolateAtOffset: out.debug << "interpolateAtOffset"; break; + case EOpInterpolateAtVertex: out.debug << "interpolateAtVertex"; break; + + case EOpSinCos: out.debug << "sincos"; break; + case EOpGenMul: out.debug << "mul"; break; + + case EOpAllMemoryBarrierWithGroupSync: out.debug << "AllMemoryBarrierWithGroupSync"; break; + case EOpDeviceMemoryBarrier: out.debug << "DeviceMemoryBarrier"; break; + case EOpDeviceMemoryBarrierWithGroupSync: out.debug << "DeviceMemoryBarrierWithGroupSync"; break; + case EOpWorkgroupMemoryBarrier: out.debug << "WorkgroupMemoryBarrier"; break; + case EOpWorkgroupMemoryBarrierWithGroupSync: out.debug << "WorkgroupMemoryBarrierWithGroupSync"; break; + + case EOpSubgroupBarrier: out.debug << "subgroupBarrier"; break; + case EOpSubgroupMemoryBarrier: out.debug << "subgroupMemoryBarrier"; break; + case EOpSubgroupMemoryBarrierBuffer: out.debug << "subgroupMemoryBarrierBuffer"; break; + case EOpSubgroupMemoryBarrierImage: out.debug << "subgroupMemoryBarrierImage"; break; + case EOpSubgroupMemoryBarrierShared: out.debug << "subgroupMemoryBarrierShared"; break; + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupRotate: out.debug << "subgroupRotate"; break; + case EOpSubgroupClusteredRotate: out.debug << "subgroupClusteredRotate"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + case EOpSubgroupQuadAll: out.debug << "subgroupQuadAll"; break; + case EOpSubgroupQuadAny: out.debug << "subgroupQuadAny"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpColorAttachmentReadEXT: out.debug << "colorAttachmentReadEXT"; break; + + case EOpTraceNV: out.debug << "traceNV"; break; + case EOpTraceRayMotionNV: out.debug << "traceRayMotionNV"; break; + case EOpTraceKHR: out.debug << "traceRayKHR"; break; + case EOpReportIntersection: out.debug << "reportIntersectionNV"; break; + case EOpIgnoreIntersectionNV: out.debug << "ignoreIntersectionNV"; break; + case EOpIgnoreIntersectionKHR: out.debug << "ignoreIntersectionKHR"; break; + case EOpTerminateRayNV: out.debug << "terminateRayNV"; break; + case EOpTerminateRayKHR: out.debug << "terminateRayKHR"; break; + case EOpExecuteCallableNV: out.debug << "executeCallableNV"; break; + case EOpExecuteCallableKHR: out.debug << "executeCallableKHR"; break; + case EOpWritePackedPrimitiveIndices4x8NV: out.debug << "writePackedPrimitiveIndices4x8NV"; break; + case EOpEmitMeshTasksEXT: out.debug << "EmitMeshTasksEXT"; break; + case EOpSetMeshOutputsEXT: out.debug << "SetMeshOutputsEXT"; break; + + case EOpRayQueryInitialize: out.debug << "rayQueryInitializeEXT"; break; + case EOpRayQueryTerminate: out.debug << "rayQueryTerminateEXT"; break; + case EOpRayQueryGenerateIntersection: out.debug << "rayQueryGenerateIntersectionEXT"; break; + case EOpRayQueryConfirmIntersection: out.debug << "rayQueryConfirmIntersectionEXT"; break; + case EOpRayQueryProceed: out.debug << "rayQueryProceedEXT"; break; + case EOpRayQueryGetIntersectionType: out.debug << "rayQueryGetIntersectionTypeEXT"; break; + case EOpRayQueryGetRayTMin: out.debug << "rayQueryGetRayTMinEXT"; break; + case EOpRayQueryGetRayFlags: out.debug << "rayQueryGetRayFlagsEXT"; break; + case EOpRayQueryGetIntersectionT: out.debug << "rayQueryGetIntersectionTEXT"; break; + case EOpRayQueryGetIntersectionInstanceCustomIndex: out.debug << "rayQueryGetIntersectionInstanceCustomIndexEXT"; break; + case EOpRayQueryGetIntersectionInstanceId: out.debug << "rayQueryGetIntersectionInstanceIdEXT"; break; + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: out.debug << "rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT"; break; + case EOpRayQueryGetIntersectionGeometryIndex: out.debug << "rayQueryGetIntersectionGeometryIndexEXT"; break; + case EOpRayQueryGetIntersectionPrimitiveIndex: out.debug << "rayQueryGetIntersectionPrimitiveIndexEXT"; break; + case EOpRayQueryGetIntersectionBarycentrics: out.debug << "rayQueryGetIntersectionBarycentricsEXT"; break; + case EOpRayQueryGetIntersectionFrontFace: out.debug << "rayQueryGetIntersectionFrontFaceEXT"; break; + case EOpRayQueryGetIntersectionCandidateAABBOpaque: out.debug << "rayQueryGetIntersectionCandidateAABBOpaqueEXT"; break; + case EOpRayQueryGetIntersectionObjectRayDirection: out.debug << "rayQueryGetIntersectionObjectRayDirectionEXT"; break; + case EOpRayQueryGetIntersectionObjectRayOrigin: out.debug << "rayQueryGetIntersectionObjectRayOriginEXT"; break; + case EOpRayQueryGetWorldRayDirection: out.debug << "rayQueryGetWorldRayDirectionEXT"; break; + case EOpRayQueryGetWorldRayOrigin: out.debug << "rayQueryGetWorldRayOriginEXT"; break; + case EOpRayQueryGetIntersectionObjectToWorld: out.debug << "rayQueryGetIntersectionObjectToWorldEXT"; break; + case EOpRayQueryGetIntersectionWorldToObject: out.debug << "rayQueryGetIntersectionWorldToObjectEXT"; break; + case EOpRayQueryGetIntersectionTriangleVertexPositionsEXT: out.debug << "rayQueryGetIntersectionTriangleVertexPositionsEXT"; break; + case EOpRayQueryGetIntersectionClusterIdNV: out.debug << "rayQueryGetIntersectionClusterIdNV"; break; + case EOpRayQueryGetIntersectionSpherePositionNV: out.debug << "rayQueryGetIntersectionSpherePositionNV"; break; + case EOpRayQueryGetIntersectionSphereRadiusNV: out.debug << "rayQueryGetIntersectionSphereRadiusNV"; break; + case EOpRayQueryGetIntersectionLSSHitValueNV: out.debug << "rayQueryGetIntersectionLSSHitValueNV"; break; + case EOpRayQueryGetIntersectionLSSPositionsNV: out.debug << "rayQueryGetIntersectionLSSPositionsNV"; break; + case EOpRayQueryGetIntersectionLSSRadiiNV: out.debug << "rayQueryGetIntersectionLSSRadiiNV"; break; + case EOpRayQueryIsSphereHitNV: out.debug << "rayQueryIsSphereHitNV"; break; + case EOpRayQueryIsLSSHitNV: out.debug << "rayQueryIsLSSHitNV"; break; + + case EOpCooperativeMatrixLoad: out.debug << "Load cooperative matrix KHR"; break; + case EOpCooperativeMatrixStore: out.debug << "Store cooperative matrix KHR"; break; + case EOpCooperativeMatrixMulAdd: out.debug << "MulAdd cooperative matrices KHR"; break; + case EOpCooperativeMatrixLoadNV: out.debug << "Load cooperative matrix NV"; break; + case EOpCooperativeMatrixStoreNV: out.debug << "Store cooperative matrix NV"; break; + case EOpCooperativeMatrixLoadTensorNV: out.debug << "Load cooperative matrix tensor NV"; break; + case EOpCooperativeMatrixStoreTensorNV: out.debug << "Store cooperative matrix tensor NV"; break; + case EOpCooperativeMatrixMulAddNV: out.debug << "MulAdd cooperative matrices NV"; break; + case EOpCooperativeMatrixReduceNV: out.debug << "Reduce cooperative matrices"; break; + case EOpCooperativeMatrixPerElementOpNV: out.debug << "cooperative matrix per element op"; break; + case EOpCooperativeMatrixTransposeNV: out.debug << "Transpose cooperative matrix"; break; + + case EOpCooperativeVectorMatMulNV: out.debug << "Cooperative vector matrix multiply NV"; break; + case EOpCooperativeVectorMatMulAddNV: out.debug << "Cooperative vector matrix multiply add NV"; break; + case EOpCooperativeVectorLoadNV: out.debug << "Load cooperative vector NV"; break; + case EOpCooperativeVectorStoreNV: out.debug << "Store cooperative vector NV"; break; + case EOpCooperativeVectorOuterProductAccumulateNV: out.debug << "Cooperative vector outer product accumulate NV"; break; + case EOpCooperativeVectorReduceSumAccumulateNV: out.debug << "Cooperative vector reduce sum accumulate NV"; break; + + case EOpIsHelperInvocation: out.debug << "IsHelperInvocation"; break; + case EOpDebugPrintf: out.debug << "Debug printf"; break; + + case EOpHitObjectTraceRayNV: out.debug << "HitObjectTraceRayNV"; break; + case EOpHitObjectTraceRayMotionNV: out.debug << "HitObjectTraceRayMotionNV"; break; + case EOpHitObjectRecordHitNV: out.debug << "HitObjectRecordHitNV"; break; + case EOpHitObjectRecordHitMotionNV: out.debug << "HitObjectRecordHitMotionNV"; break; + case EOpHitObjectRecordHitWithIndexNV: out.debug << "HitObjectRecordHitWithIndexNV"; break; + case EOpHitObjectRecordHitWithIndexMotionNV: out.debug << "HitObjectRecordHitWithIndexMotionNV"; break; + case EOpHitObjectRecordMissNV: out.debug << "HitObjectRecordMissNV"; break; + case EOpHitObjectRecordMissMotionNV: out.debug << "HitObjectRecordMissMotionNV"; break; + case EOpHitObjectRecordEmptyNV: out.debug << "HitObjectRecordEmptyNV"; break; + case EOpHitObjectExecuteShaderNV: out.debug << "HitObjectExecuteShaderNV"; break; + case EOpHitObjectIsEmptyNV: out.debug << "HitObjectIsEmptyNV"; break; + case EOpHitObjectIsMissNV: out.debug << "HitObjectIsMissNV"; break; + case EOpHitObjectIsHitNV: out.debug << "HitObjectIsHitNV"; break; + case EOpHitObjectGetRayTMinNV: out.debug << "HitObjectGetRayTMinNV"; break; + case EOpHitObjectGetRayTMaxNV: out.debug << "HitObjectGetRayTMaxNV"; break; + case EOpHitObjectGetObjectRayOriginNV: out.debug << "HitObjectGetObjectRayOriginNV"; break; + case EOpHitObjectGetObjectRayDirectionNV: out.debug << "HitObjectGetObjectRayDirectionNV"; break; + case EOpHitObjectGetWorldRayOriginNV: out.debug << "HitObjectGetWorldRayOriginNV"; break; + case EOpHitObjectGetWorldRayDirectionNV: out.debug << "HitObjectGetWorldRayDirectionNV"; break; + case EOpHitObjectGetObjectToWorldNV: out.debug << "HitObjectGetObjectToWorldNV"; break; + case EOpHitObjectGetWorldToObjectNV: out.debug << "HitObjectGetWorldToObjectNV"; break; + case EOpHitObjectGetInstanceCustomIndexNV: out.debug<< "HitObjectGetInstanceCustomIndexNV"; break; + case EOpHitObjectGetInstanceIdNV: out.debug << "HitObjectGetInstaneIdNV"; break; + case EOpHitObjectGetGeometryIndexNV: out.debug << "HitObjectGetGeometryIndexNV"; break; + case EOpHitObjectGetPrimitiveIndexNV: out.debug << "HitObjectGetPrimitiveIndexNV"; break; + case EOpHitObjectGetHitKindNV: out.debug << "HitObjectGetHitKindNV"; break; + case EOpHitObjectGetAttributesNV: out.debug << "HitObjectGetAttributesNV"; break; + case EOpHitObjectGetCurrentTimeNV: out.debug << "HitObjectGetCurrentTimeNV"; break; + case EOpHitObjectGetShaderBindingTableRecordIndexNV: out.debug << "HitObjectGetShaderBindingTableRecordIndexNV"; break; + case EOpHitObjectGetShaderRecordBufferHandleNV: out.debug << "HitObjectReadShaderRecordBufferHandleNV"; break; + case EOpHitObjectGetClusterIdNV: out.debug << "HitObjectGetClusterIdNV"; break; + case EOpReorderThreadNV: out.debug << "ReorderThreadNV"; break; + case EOpFetchMicroTriangleVertexPositionNV: out.debug << "MicroTriangleVertexPositionNV"; break; + case EOpFetchMicroTriangleVertexBarycentricNV: out.debug << "MicroTriangleVertexBarycentricNV"; break; + case EOpHitObjectGetSpherePositionNV: out.debug << "HitObjectGetSpherePositionNV"; break; + case EOpHitObjectGetSphereRadiusNV: out.debug << "HitObjectGetSphereRadiusNV"; break; + case EOpHitObjectGetLSSPositionsNV: out.debug << "HitObjectGetLSSPositionsNV"; break; + case EOpHitObjectGetLSSRadiiNV: out.debug << "HitObjectGetLSSRadiiNV"; break; + case EOpHitObjectIsSphereHitNV: out.debug << "HitObjectIsSphereHitNV"; break; + case EOpHitObjectIsLSSHitNV: out.debug << "HitObjectIsLSSHitNV"; break; + + case EOpSpirvInst: out.debug << "spirv_instruction"; break; + case EOpStencilAttachmentReadEXT: out.debug << "stencilAttachmentReadEXT"; break; + case EOpDepthAttachmentReadEXT: out.debug << "depthAttachmentReadEXT"; break; + + case EOpCreateTensorLayoutNV: out.debug << "createTensorLayout"; break; + case EOpTensorLayoutSetBlockSizeNV: out.debug << "setBlockSize"; break; + case EOpTensorLayoutSetDimensionNV: out.debug << "setDimension"; break; + case EOpTensorLayoutSetStrideNV: out.debug << "setStride"; break; + case EOpTensorLayoutSliceNV: out.debug << "slice"; break; + case EOpTensorLayoutSetClampValueNV: out.debug << "setClampValue"; break; + case EOpCreateTensorViewNV: out.debug << "createTensorView"; break; + case EOpTensorViewSetDimensionNV: out.debug << "setTensorViewDimensions"; break; + case EOpTensorViewSetStrideNV: out.debug << "setTensorViewStride"; break; + case EOpTensorViewSetClipNV: out.debug << "clipTensorView"; break; + + default: out.debug.message(EPrefixError, "Bad aggregation op"); + } + + if (node->getOp() != EOpSequence && node->getOp() != EOpScope && node->getOp() != EOpParameters) + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Test condition and select"; + out.debug << " (" << node->getCompleteString() << ")"; + + if (node->getShortCircuit() == false) + out.debug << ": no shortcircuit"; + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + ++depth; + + OutputTreeText(out, node, depth); + out.debug << "Condition\n"; + node->getCondition()->traverse(this); + + OutputTreeText(out, node, depth); + if (node->getTrueBlock()) { + out.debug << "true case\n"; + node->getTrueBlock()->traverse(this); + } else + out.debug << "true case is null\n"; + + if (node->getFalseBlock()) { + OutputTreeText(out, node, depth); + out.debug << "false case\n"; + node->getFalseBlock()->traverse(this); + } + + --depth; + + return false; +} + +// Print infinities and NaNs, and numbers in a portable way. +// Goals: +// - portable (across IEEE 754 platforms) +// - shows all possible IEEE values +// - shows simple numbers in a simple way, e.g., no leading/trailing 0s +// - shows all digits, no premature rounding +static void OutputDouble(TInfoSink& out, double value, TOutputTraverser::EExtraOutput extra) +{ + if (std::isinf(value)) { + if (value < 0) + out.debug << "-1.#INF"; + else + out.debug << "+1.#INF"; + } else if (std::isnan(value)) + out.debug << "1.#IND"; + else { + const int maxSize = 340; + char buf[maxSize]; + const char* format = "%f"; + if (fabs(value) > 0.0 && (fabs(value) < 1e-5 || fabs(value) > 1e12)) + format = "%-.13e"; + int len = snprintf(buf, maxSize, format, value); + assert(len < maxSize); + + // remove a leading zero in the 100s slot in exponent; it is not portable + // pattern: XX...XXXe+0XX or XX...XXXe-0XX + if (len > 5) { + if (buf[len-5] == 'e' && (buf[len-4] == '+' || buf[len-4] == '-') && buf[len-3] == '0') { + buf[len-3] = buf[len-2]; + buf[len-2] = buf[len-1]; + buf[len-1] = '\0'; + } + } + + out.debug << buf; + + switch (extra) { + case TOutputTraverser::BinaryDoubleOutput: + { + uint64_t b; + static_assert(sizeof(b) == sizeof(value), "sizeof(uint64_t) != sizeof(double)"); + memcpy(&b, &value, sizeof(b)); + + out.debug << " : "; + for (size_t i = 0; i < 8 * sizeof(value); ++i, ++b) { + out.debug << ((b & 0x8000000000000000) != 0 ? "1" : "0"); + b <<= 1; + } + break; + } + default: + break; + } + } +} + +static void OutputConstantUnion(TInfoSink& out, const TIntermTyped* node, const TConstUnionArray& constUnion, + TOutputTraverser::EExtraOutput extra, int depth) +{ + int size = node->getType().computeNumComponents(); + + for (int i = 0; i < size; i++) { + OutputTreeText(out, node, depth); + switch (constUnion[i].getType()) { + case EbtBool: + if (constUnion[i].getBConst()) + out.debug << "true"; + else + out.debug << "false"; + + out.debug << " (" << "const bool" << ")"; + + out.debug << "\n"; + break; + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtBFloat16: + OutputDouble(out, constUnion[i].getDConst(), extra); + out.debug << "\n"; + break; + case EbtInt8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI8Const(), "const int8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU8Const(), "const uint8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI16Const(), "const int16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU16Const(), "const uint16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getIConst(), "const int"); + + out.debug << buf << "\n"; + } + break; + case EbtUint: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getUConst(), "const uint"); + + out.debug << buf << "\n"; + } + break; + case EbtInt64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%lld (%s)", constUnion[i].getI64Const(), "const int64_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%llu (%s)", constUnion[i].getU64Const(), "const uint64_t"); + + out.debug << buf << "\n"; + } + break; + case EbtString: + out.debug << "\"" << constUnion[i].getSConst()->c_str() << "\"\n"; + break; + default: + out.info.message(EPrefixInternalError, "Unknown constant", node->getLoc()); + break; + } + } +} + +void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + OutputTreeText(infoSink, node, depth); + infoSink.debug << "Constant:\n"; + + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); +} + +void TOutputTraverser::visitSymbol(TIntermSymbol* node) +{ + OutputTreeText(infoSink, node, depth); + + infoSink.debug << "'" << node->getName() << "' (" << node->getCompleteString() << ")\n"; + + if (! node->getConstArray().empty()) + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); + else if (node->getConstSubtree()) { + incrementDepth(node); + node->getConstSubtree()->traverse(this); + decrementDepth(); + } +} + +bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Loop with condition "; + if (! node->testFirst()) + out.debug << "not "; + out.debug << "tested first"; + + if (node->getUnroll()) + out.debug << ": Unroll"; + if (node->getDontUnroll()) + out.debug << ": DontUnroll"; + if (node->getLoopDependency()) { + out.debug << ": Dependency "; + out.debug << node->getLoopDependency(); + } + out.debug << "\n"; + + ++depth; + + OutputTreeText(infoSink, node, depth); + if (node->getTest()) { + out.debug << "Loop Condition\n"; + node->getTest()->traverse(this); + } else + out.debug << "No loop condition\n"; + + OutputTreeText(infoSink, node, depth); + if (node->getBody()) { + out.debug << "Loop Body\n"; + node->getBody()->traverse(this); + } else + out.debug << "No loop body\n"; + + if (node->getTerminal()) { + OutputTreeText(infoSink, node, depth); + out.debug << "Loop Terminal Expression\n"; + node->getTerminal()->traverse(this); + } + + --depth; + + return false; +} + +bool TOutputTraverser::visitBranch(TVisit /* visit*/, TIntermBranch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getFlowOp()) { + case EOpKill: out.debug << "Branch: Kill"; break; + case EOpTerminateInvocation: out.debug << "Branch: TerminateInvocation"; break; + case EOpIgnoreIntersectionKHR: out.debug << "Branch: IgnoreIntersectionKHR"; break; + case EOpTerminateRayKHR: out.debug << "Branch: TerminateRayKHR"; break; + case EOpBreak: out.debug << "Branch: Break"; break; + case EOpContinue: out.debug << "Branch: Continue"; break; + case EOpReturn: out.debug << "Branch: Return"; break; + case EOpCase: out.debug << "case: "; break; + case EOpDemote: out.debug << "Demote"; break; + case EOpDefault: out.debug << "default: "; break; + default: out.debug << "Branch: Unknown Branch"; break; + } + + if (node->getExpression()) { + out.debug << " with expression\n"; + ++depth; + node->getExpression()->traverse(this); + --depth; + } else + out.debug << "\n"; + + return false; +} + +bool TOutputTraverser::visitSwitch(TVisit /* visit */, TIntermSwitch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + out.debug << "switch"; + + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + OutputTreeText(out, node, depth); + out.debug << "condition\n"; + ++depth; + node->getCondition()->traverse(this); + + --depth; + OutputTreeText(out, node, depth); + out.debug << "body\n"; + ++depth; + node->getBody()->traverse(this); + + --depth; + + return false; +} + +// +// This function is the one to call externally to start the traversal. +// Individual functions can be initialized to 0 to skip processing of that +// type of node. It's children will still be processed. +// +void TIntermediate::output(TInfoSink& infoSink, bool tree) +{ + infoSink.debug << "Shader version: " << version << "\n"; + if (requestedExtensions.size() > 0) { + for (auto extIt = requestedExtensions.begin(); extIt != requestedExtensions.end(); ++extIt) + infoSink.debug << "Requested " << *extIt << "\n"; + } + + if (xfbMode) + infoSink.debug << "in xfb mode\n"; + + if (getSubgroupUniformControlFlow()) + infoSink.debug << "subgroup_uniform_control_flow\n"; + + if (getMaximallyReconverges()) + infoSink.debug << "maximally_reconverges\n"; + + switch (language) { + case EShLangVertex: + break; + + case EShLangTessControl: + infoSink.debug << "vertices = " << vertices << "\n"; + + if (inputPrimitive != ElgNone) + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + if (vertexSpacing != EvsNone) + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + if (vertexOrder != EvoNone) + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + break; + + case EShLangTessEvaluation: + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + if (pointMode) + infoSink.debug << "using point mode\n"; + break; + + case EShLangGeometry: + infoSink.debug << "invocations = " << invocations << "\n"; + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + break; + + case EShLangFragment: + if (pixelCenterInteger) + infoSink.debug << "gl_FragCoord pixel center is integer\n"; + if (originUpperLeft) + infoSink.debug << "gl_FragCoord origin is upper left\n"; + if (earlyFragmentTests) + infoSink.debug << "using early_fragment_tests\n"; + if (postDepthCoverage) + infoSink.debug << "using post_depth_coverage\n"; + if (nonCoherentColorAttachmentReadEXT) + infoSink.debug << "using non_coherent_color_attachment_readEXT\n"; + if (nonCoherentDepthAttachmentReadEXT) + infoSink.debug << "using non_coherent_depth_attachment_readEXT\n"; + if (nonCoherentStencilAttachmentReadEXT) + infoSink.debug << "using non_coherent_stencil_attachment_readEXT\n"; + if (depthLayout != EldNone) + infoSink.debug << "using " << TQualifier::getLayoutDepthString(depthLayout) << "\n"; + if (blendEquations != 0) { + infoSink.debug << "using"; + // blendEquations is a mask, decode it + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (blendEquations & (1 << be)) + infoSink.debug << " " << TQualifier::getBlendEquationString(be); + } + infoSink.debug << "\n"; + } + if (interlockOrdering != EioNone) + infoSink.debug << "interlock ordering = " << TQualifier::getInterlockOrderingString(interlockOrdering) << "\n"; + break; + + case EShLangMesh: + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "max_primitives = " << primitives << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + [[fallthrough]]; + case EShLangTask: + // Fall through + case EShLangCompute: + infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n"; + { + if (localSizeSpecId[0] != TQualifier::layoutNotSet || + localSizeSpecId[1] != TQualifier::layoutNotSet || + localSizeSpecId[2] != TQualifier::layoutNotSet) { + infoSink.debug << "local_size ids = (" << + localSizeSpecId[0] << ", " << + localSizeSpecId[1] << ", " << + localSizeSpecId[2] << ")\n"; + } + } + break; + + default: + break; + } + + if (treeRoot == nullptr || ! tree) + return; + + TOutputTraverser it(infoSink); + if (getBinaryDoubleOutput()) + it.setDoubleOutput(TOutputTraverser::BinaryDoubleOutput); + treeRoot->traverse(&it); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.cpp new file mode 100644 index 00000000..26c3eaa6 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.cpp @@ -0,0 +1,1851 @@ +// +// Copyright (C) 2016-2017 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/InfoSink.h" +#include "../Include/Types.h" + +#include "gl_types.h" +#include "iomapper.h" +#include "LiveTraverser.h" +#include "SymbolTable.h" + +// +// Map IO bindings. +// +// High-level algorithm for one stage: +// +// 1. Traverse all code (live+dead) to find the explicitly provided bindings. +// +// 2. Traverse (just) the live code to determine which non-provided bindings +// require auto-numbering. We do not auto-number dead ones. +// +// 3. Traverse all the code to apply the bindings: +// a. explicitly given bindings are offset according to their type +// b. implicit live bindings are auto-numbered into the holes, using +// any open binding slot. +// c. implicit dead bindings are left un-bound. +// + +namespace glslang { + +struct TVarEntryInfo { + long long id; + TIntermSymbol* symbol; + bool live; + TLayoutPacking upgradedToPushConstantPacking; // ElpNone means it hasn't been upgraded + int newBinding; + int newSet; + int newLocation; + int newComponent; + int newIndex; + EShLanguage stage; + + void clearNewAssignments() { + upgradedToPushConstantPacking = ElpNone; + newBinding = -1; + newSet = -1; + newLocation = -1; + newComponent = -1; + newIndex = -1; + } + + struct TOrderById { + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; } + }; + + struct TOrderByPriority { + // ordering: + // 1) has both binding and set + // 2) has binding but no set + // 3) has no binding but set + // 4) has no binding and no set + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { + const TQualifier& lq = l.symbol->getQualifier(); + const TQualifier& rq = r.symbol->getQualifier(); + + // simple rules: + // has binding gives 2 points + // has set gives 1 point + // who has the most points is more important. + int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); + int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); + + if (lPoints == rPoints) + return l.id < r.id; + return lPoints > rPoints; + } + }; + + struct TOrderByPriorityAndLive { + // ordering: + // 1) do live variables first + // 2) has both binding and set + // 3) has binding but no set + // 4) has no binding but set + // 5) has no binding and no set + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { + + const TQualifier& lq = l.symbol->getQualifier(); + const TQualifier& rq = r.symbol->getQualifier(); + + // simple rules: + // has binding gives 2 points + // has set gives 1 point + // who has the most points is more important. + int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); + int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); + + if (l.live != r.live) + return l.live > r.live; + + if (lPoints != rPoints) + return lPoints > rPoints; + + return l.id < r.id; + } + }; +}; + +// override function "operator=", if a vector being sort, +// when use vc++, the sort function will call : +// pair& operator=(const pair<_Other1, _Other2>& _Right) +// { +// first = _Right.first; +// second = _Right.second; +// return (*this); +// } +// that will make a const type handing on left. +// override this function can avoid a compiler error. +// In the future, if the vc++ compiler can handle such a situation, +// this part of the code will be removed. +struct TVarLivePair : std::pair { + TVarLivePair(const std::pair& _Right) : pair(_Right.first, _Right.second) {} + TVarLivePair& operator=(const TVarLivePair& _Right) { + const_cast(first) = _Right.first; + second = _Right.second; + return (*this); + } + TVarLivePair(const TVarLivePair& src) : pair(src) { } +}; +typedef std::vector TVarLiveVector; + + +class TVarGatherTraverser : public TLiveTraverser { +public: + TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) + : TLiveTraverser(i, traverseDeadCode, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) + { + TVarLiveMap* target = nullptr; + if (base->getQualifier().storage == EvqVaryingIn) + target = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + target = &outputList; + else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant() && !base->getQualifier().isShaderRecord()) + target = &uniformList; + // If a global is being visited, then we should also traverse it incase it's evaluation + // ends up visiting inputs we want to tag as live + else if (base->getQualifier().storage == EvqGlobal) + addGlobalReference(base->getAccessName()); + + if (target) { + TVarEntryInfo ent = {base->getId(), base, ! traverseAll, {}, {}, {}, {}, {}, {}, {}}; + ent.stage = intermediate.getStage(); + TVarLiveMap::iterator at = target->find( + ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); + if (at != target->end() && at->second.id == ent.id) + at->second.live = at->second.live || ! traverseAll; // update live state + else + (*target)[ent.symbol->getAccessName()] = ent; + } + } + +private: + TVarLiveMap& inputList; + TVarLiveMap& outputList; + TVarLiveMap& uniformList; +}; + +class TVarSetTraverser : public TLiveTraverser +{ +public: + TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList) + : TLiveTraverser(i, true, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) { + const TVarLiveMap* source; + if (base->getQualifier().storage == EvqVaryingIn) + source = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + source = &outputList; + else if (base->getQualifier().isUniformOrBuffer()) + source = &uniformList; + else + return; + + TVarEntryInfo ent = { base->getId(), {}, {}, {}, {}, {}, {}, {}, {}, {} }; + // Fix a defect, when block has no instance name, we need to find its block name + TVarLiveMap::const_iterator at = source->find(base->getAccessName()); + if (at == source->end()) + return; + + if (at->second.id != ent.id) + return; + + if (at->second.newBinding != -1) + base->getWritableType().getQualifier().layoutBinding = at->second.newBinding; + if (at->second.newSet != -1) + base->getWritableType().getQualifier().layoutSet = at->second.newSet; + if (at->second.newLocation != -1) + base->getWritableType().getQualifier().layoutLocation = at->second.newLocation; + if (at->second.newComponent != -1) + base->getWritableType().getQualifier().layoutComponent = at->second.newComponent; + if (at->second.newIndex != -1) + base->getWritableType().getQualifier().layoutIndex = at->second.newIndex; + if (at->second.upgradedToPushConstantPacking != ElpNone) { + base->getWritableType().getQualifier().layoutPushConstant = true; + base->getWritableType().getQualifier().setBlockStorage(EbsPushConstant); + base->getWritableType().getQualifier().layoutPacking = at->second.upgradedToPushConstantPacking; + } + } + + private: + const TVarLiveMap& inputList; + const TVarLiveMap& outputList; + const TVarLiveMap& uniformList; +}; + +struct TNotifyUniformAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyBinding(stage, entKey.second); + } + +private: + TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete; +}; + +struct TNotifyInOutAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyInOut(entKey.second.stage, entKey.second); + } + +private: + TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete; +}; + +struct TResolverUniformAdaptor { + TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent = entKey.second; + ent.clearNewAssignments(); + const bool isValid = resolver.validateBinding(stage, ent); + if (isValid) { + resolver.resolveSet(ent.stage, ent); + resolver.resolveBinding(ent.stage, ent); + resolver.resolveUniformLocation(ent.stage, ent); + + if (ent.newBinding != -1) { + if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) { + TString err = "mapped binding out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + + if (ent.symbol->getQualifier().hasBinding()) { + for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) { + if (idx == ent.stage || uniformVarMap[idx] == nullptr) + continue; + auto entKey2 = uniformVarMap[idx]->find(entKey.first); + if (entKey2 != uniformVarMap[idx]->end()) { + entKey2->second.newBinding = ent.newBinding; + } + } + } + } + if (ent.newSet != -1) { + if (ent.newSet >= int(TQualifier::layoutSetEnd)) { + TString err = "mapped set out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + if (ent.symbol->getQualifier().hasSet()) { + for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) { + if ((idx == stage) || (uniformVarMap[idx] == nullptr)) + continue; + auto entKey2 = uniformVarMap[idx]->find(entKey.first); + if (entKey2 != uniformVarMap[idx]->end()) { + entKey2->second.newSet = ent.newSet; + } + } + } + } + } else { + TString errorMsg = "Invalid binding: " + entKey.first; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + TVarLiveMap* uniformVarMap[EShLangCount]; +private: + TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete; +}; + +struct TResolverInOutAdaptor { + TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + } + + inline void operator()(std::pair& entKey) + { + TVarEntryInfo& ent = entKey.second; + ent.clearNewAssignments(); + const bool isValid = resolver.validateInOut(ent.stage, ent); + if (isValid) { + resolver.resolveInOutLocation(stage, ent); + resolver.resolveInOutComponent(stage, ent); + resolver.resolveInOutIndex(stage, ent); + } else { + TString errorMsg; + if (ent.symbol->getType().getQualifier().semanticName != nullptr) { + errorMsg = "Invalid shader In/Out variable semantic: "; + errorMsg += ent.symbol->getType().getQualifier().semanticName; + } else { + errorMsg = "Invalid shader In/Out variable: "; + errorMsg += ent.symbol->getName(); + } + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + +private: + TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete; +}; + +// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings +// xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform" + +struct TSymbolValidater +{ + TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], + TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version) + : resolver(r) + , infoSink(i) + , hadError(hadError) + , profile(profile) + , version(version) + { + memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); + + std::map anonymousMemberMap; + std::vector usedUniformLocation; + std::vector usedUniformName; + usedUniformLocation.clear(); + usedUniformName.clear(); + for (int i = 0; i < EShLangCount; i++) { + if (uniformVarMap[i]) { + for (auto uniformVar : *uniformVarMap[i]) + { + TIntermSymbol* pSymbol = uniformVar.second.symbol; + TQualifier qualifier = uniformVar.second.symbol->getQualifier(); + TString symbolName = pSymbol->getAccessName(); + + // All the uniform needs multi-stage location check (block/default) + int uniformLocation = qualifier.layoutLocation; + + if (uniformLocation != TQualifier::layoutLocationEnd) { + // Total size of current uniform, could be block, struct or other types. + int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType()); + + TRange locationRange(uniformLocation, uniformLocation + size - 1); + + // Combine location and component ranges + int overlapLocation = -1; + bool diffLocation = false; + + // Check for collisions, except for vertex inputs on desktop targeting OpenGL + overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation); + + // Overlap locations of uniforms, regardless of components (multi stages) + if (overlapLocation == -1) { + usedUniformLocation.push_back(locationRange); + usedUniformName.push_back(symbolName); + } + else if (overlapLocation >= 0) { + if (diffLocation == true) { + TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str(); + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + else { + TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str(); + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + } + + if ((uniformVar.second.symbol->getBasicType() == EbtBlock) && + IsAnonymous(uniformVar.second.symbol->getName())) + { + auto blockType = uniformVar.second.symbol->getType().getStruct(); + for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) { + auto memberName = (*blockType)[memberIdx].type->getFieldName(); + if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end()) + { + if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName()) + { + TString err = "Invalid block member name: " + memberName; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + else + { + anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName(); + } + } + } + if (hadError) + break; + } + } + } + } + + // In case we need to new an intermediate, which costs too much + int checkLocationOverlap(const TRange& locationRange, std::vector& usedUniformLocation, const TString symbolName, std::vector& usedUniformName, bool& diffLocation) + { + for (size_t r = 0; r < usedUniformLocation.size(); ++r) { + if (usedUniformName[r] == symbolName) { + diffLocation = true; + return (usedUniformLocation[r].start == locationRange.start && + usedUniformLocation[r].last == locationRange.last) + ? -2 : std::max(locationRange.start, usedUniformLocation[r].start); + } + if (locationRange.overlap(usedUniformLocation[r])) { + // there is a collision; pick one + return std::max(locationRange.start, usedUniformLocation[r].start); + } + } + + return -1; // no collision + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent1 = entKey.second; + TIntermSymbol* base = ent1.symbol; + const TType& type = ent1.symbol->getType(); + const TString& name = entKey.first; + TString mangleName1, mangleName2; + EShLanguage stage = ent1.stage; + EShLanguage preStage, currentStage, nextStage; + + preStage = EShLangCount; + for (int i = stage - 1; i >= 0; i--) { + if (inVarMaps[i] != nullptr) { + preStage = static_cast(i); + break; + } + } + currentStage = stage; + nextStage = EShLangCount; + for (int i = stage + 1; i < EShLangCount; i++) { + if (inVarMaps[i] != nullptr) { + nextStage = static_cast(i); + break; + } + } + + if (type.getQualifier().isArrayedIo(stage)) { + TType subType(type, 0); + subType.appendMangledName(mangleName1); + } else { + type.appendMangledName(mangleName1); + } + + + // basic checking that symbols match + // more extensive checking in the link stage + if (base->getQualifier().storage == EvqVaryingIn) { + // validate stage in; + if (preStage == EShLangCount) + return; + if (TSymbolTable::isBuiltInSymbol(base->getId())) + return; + if (outVarMaps[preStage] != nullptr) { + auto ent2 = outVarMaps[preStage]->find(name); + uint32_t location = base->getType().getQualifier().layoutLocation; + if (ent2 == outVarMaps[preStage]->end() && + location != glslang::TQualifier::layoutLocationEnd) { + for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) { + if (var->second.symbol->getType().getQualifier().layoutLocation == location) { + ent2 = var; + break; + } + } + } + if (ent2 != outVarMaps[preStage]->end()) { + auto& type1 = base->getType(); + auto& type2 = ent2->second.symbol->getType(); + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false); + if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) { + TType subType(ent2->second.symbol->getType(), 0); + subType.appendMangledName(mangleName2); + } else { + ent2->second.symbol->getType().appendMangledName(mangleName2); + } + + if (mangleName1 == mangleName2) { + // For ES 3.0 only, other versions have no such restrictions + // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and + // storage qualifiers of variables with the same name declared in all linked shaders must + // match, otherwise the link command will fail. + if (profile == EEsProfile && version == 300) { + // Don't need to check smooth qualifier, as it uses the default interpolation mode + if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) { + if (type1.getQualifier().flat != type2.getQualifier().flat || + type1.getQualifier().nopersp != type2.getQualifier().nopersp) { + TString err = "Interpolation qualifier mismatch : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + } + return; + } + else { + // Deal with input/output pairs where one is a block member but the other is loose, + // e.g. with ARB_separate_shader_objects + if (type1.getBasicType() == EbtBlock && + type1.isStruct() && !type2.isStruct()) { + // Iterate through block members tracking layout + glslang::TString name; + type1.getStruct()->begin()->type->appendMangledName(name); + if (name == mangleName2 + && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return; + } + if (type2.getBasicType() == EbtBlock && + type2.isStruct() && !type1.isStruct()) { + // Iterate through block members tracking layout + glslang::TString name; + type2.getStruct()->begin()->type->appendMangledName(name); + if (name == mangleName1 + && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return; + } + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + else if (!base->getType().isBuiltIn()) { + // According to spec: A link error is generated if any statically referenced input variable + // or block does not have a matching output + if (profile == EEsProfile && ent1.live) { + hadError = true; + TString errorStr = name + ": not been declare as a output variable in pre shader stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + } + return; + } + } else if (base->getQualifier().storage == EvqVaryingOut) { + // validate stage out; + if (nextStage == EShLangCount) + return; + if (TSymbolTable::isBuiltInSymbol(base->getId())) + return; + if (inVarMaps[nextStage] != nullptr) { + auto ent2 = inVarMaps[nextStage]->find(name); + if (ent2 != inVarMaps[nextStage]->end()) { + if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) { + TType subType(ent2->second.symbol->getType(), 0); + subType.appendMangledName(mangleName2); + } else { + ent2->second.symbol->getType().appendMangledName(mangleName2); + } + if (mangleName1 == mangleName2) + return; + else { + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + return; + } + } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) { + // validate uniform type; + for (int i = 0; i < EShLangCount; i++) { + if (i != currentStage && outVarMaps[i] != nullptr) { + auto ent2 = uniformVarMap[i]->find(name); + if (ent2 != uniformVarMap[i]->end()) { + ent2->second.symbol->getType().appendMangledName(mangleName2); + if (mangleName1 != mangleName2) { + ent2->second.symbol->getType().sameElementType(type); + TString err = "Invalid Uniform variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + mangleName2.clear(); + + // validate instance name of blocks + if (hadError == false && + base->getType().getBasicType() == EbtBlock && + IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) { + TString err = "Matched uniform block names must also either all be lacking " + "an instance name or all having an instance name: " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + + // validate uniform block member qualifier and member names + auto& type1 = base->getType(); + auto& type2 = ent2->second.symbol->getType(); + if (hadError == false && base->getType().getBasicType() == EbtBlock) { + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true); + } + else { + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false); + } + } + else if (base->getBasicType() == EbtBlock) + { + if (IsAnonymous(base->getName())) + { + // The name of anonymous block member can't same with default uniform variable. + auto blockType1 = base->getType().getStruct(); + for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) { + auto memberName = (*blockType1)[memberIdx].type->getFieldName(); + if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end()) + { + TString err = "Invalid Uniform variable name : " + memberName; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + } + } + } + } + } + } + + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount]; + + // Use for mark current shader stage for resolver + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& hadError; + EProfile profile; + int version; + +private: + TSymbolValidater& operator=(TSymbolValidater&) = delete; + + bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock) + { + bool hasError = false; + const TQualifier& qualifier1 = type1->getQualifier(); + const TQualifier& qualifier2 = type2->getQualifier(); + + if (((isBlock == false) && + (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) || + (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) { + if (qualifier1.precision != qualifier2.precision) { + hasError = true; + std::string errorStr = name + ": have precision conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.hasFormat() && qualifier2.hasFormat()) { + if (qualifier1.layoutFormat != qualifier2.layoutFormat) { + hasError = true; + std::string errorStr = name + ": have layout format conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + + } + } + + if (isBlock == true) { + if (qualifier1.layoutPacking != qualifier2.layoutPacking) { + hasError = true; + std::string errorStr = name + ": have layoutPacking conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) { + hasError = true; + std::string errorStr = name + ": have layoutMatrix conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutOffset != qualifier2.layoutOffset) { + hasError = true; + std::string errorStr = name + ": have layoutOffset conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutAlign != qualifier2.layoutAlign) { + hasError = true; + std::string errorStr = name + ": have layoutAlign conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + } + + return hasError; + } + + bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock) + { + bool hasError = false; + if (!(type1->isStruct() && type2->isStruct())) { + hasError = hasError || qualifierCheck(type1, type2, name, isBlock); + } + else { + if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock) + isBlock = true; + const TTypeList* typeList1 = type1->getStruct(); + const TTypeList* typeList2 = type2->getStruct(); + + std::string newName = name; + size_t memberCount = typeList1->size(); + size_t index2 = 0; + for (size_t index = 0; index < memberCount; index++, index2++) { + // Skip inactive member + if (typeList1->at(index).type->getBasicType() == EbtVoid) + continue; + while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) { + ++index2; + } + + // TypeList1 has more members in list + if (index2 == typeList2->size()) { + std::string errorStr = name + ": struct mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + break; + } + + if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) { + std::string errorStr = name + ": member name mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + } + else { + newName = typeList1->at(index).type->getFieldName().c_str(); + } + hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock); + } + + while (index2 < typeList2->size()) + { + // TypeList2 has more members + if (typeList2->at(index2).type->getBasicType() != EbtVoid) { + std::string errorStr = name + ": struct mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + break; + } + ++index2; + } + } + return hasError; + } +}; + +struct TSlotCollector { + TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { } + + inline void operator()(std::pair& entKey) { + resolver.reserverStorageSlot(entKey.second, infoSink); + resolver.reserverResourceSlot(entKey.second, infoSink); + } + TIoMapResolver& resolver; + TInfoSink& infoSink; + +private: + TSlotCollector& operator=(TSlotCollector&) = delete; +}; + +TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate) + : referenceIntermediate(intermediate) + , nextUniformLocation(intermediate.getUniformLocationBase()) + , nextInputLocation(0) + , nextOutputLocation(0) +{ + memset(stageMask, false, sizeof(bool) * (EShLangCount + 1)); + memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount)); + stageIntermediates[intermediate.getStage()] = &intermediate; +} + +int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const { + return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set)) + : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set)); +} + +const std::vector& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const { + return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding() + : referenceIntermediate.getResourceSetBinding(); +} + +bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); } + +bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); } + +TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) { + return std::lower_bound(slots[set].begin(), slots[set].end(), slot); +} + +bool TDefaultIoResolverBase::checkEmpty(int set, int slot) { + TSlotSet::iterator at = findSlot(set, slot); + return ! (at != slots[set].end() && *at == slot); +} + +int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) { + TSlotSet::iterator at = findSlot(set, slot); + // tolerate aliasing, by not double-recording aliases + // (policy about appropriateness of the alias is higher up) + for (int i = 0; i < size; i++) { + if (at == slots[set].end() || *at != slot + i) + at = slots[set].insert(at, slot + i); + ++at; + } + return slot; +} + +int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { + TSlotSet::iterator at = findSlot(set, base); + if (at == slots[set].end()) + return reserveSlot(set, base, size); + // look for a big enough gap + for (; at != slots[set].end(); ++at) { + if (*at - base >= size) + break; + base = *at + 1; + } + return reserveSlot(set, base, size); +} + +int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + if (type.getQualifier().hasSet()) { + return ent.newSet = type.getQualifier().layoutSet; + } + // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) + if (getResourceSetBinding(stage).size() == 1) { + return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str()); + } + return ent.newSet = 0; +} + +int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const char* name = ent.symbol->getAccessName().c_str(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int location = referenceIntermediate.getUniformLocationOverride(name); + if (location != -1) { + return ent.newLocation = location; + } + location = nextUniformLocation; + nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type); + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + + // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) { + return ent.newLocation = -1; + } + + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + // point to the right input or output location counter + int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation; + // Placeholder. This does not do proper cross-stage lining up, nor + // work with mixed location/no-location declarations. + int location = nextLocation; + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + typeLocationSize = computeTypeLocationSize(type, stage); + nextLocation += typeLocationSize; + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) { + return ent.newComponent = -1; +} + +int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; } + +uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) { + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + if (type.getQualifier().isArrayedIo(stage)) { + TType elementType(type, 0); + typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage); + } else { + typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage); + } + return typeLocationSize; +} + +//TDefaultGlslIoResolver +TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; +} + +TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate) + : TDefaultIoResolverBase(intermediate) + , preStage(EShLangCount) + , currentStage(EShLangCount) +{ } + +int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } + // kick out if not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation()) { + return ent.newLocation = type.getQualifier().layoutLocation; + } + // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate + if (type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int typeLocationSize = computeTypeLocationSize(type, stage); + int location = type.getQualifier().layoutLocation; + bool hasLocation = false; + EShLanguage keyStage(EShLangCount); + TStorageQualifier storage; + storage = EvqInOut; + if (type.getQualifier().isPipeInput()) { + // If this symbol is a input, search pre stage's out + keyStage = preStage; + } + if (type.getQualifier().isPipeOutput()) { + // If this symbol is a output, search next stage's in + keyStage = currentStage; + } + // The in/out in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the in/out has location + int resourceKey = buildStorageKey(keyStage, storage); + if (! storageSlotMap[resourceKey].empty()) { + TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name); + if (iter != storageSlotMap[resourceKey].end()) { + // If interface resource be found, set it has location and this symbol's new location + // equal the symbol's explicit location declaration in pre or next stage. + // + // vs: out vec4 a; + // fs: layout(..., location = 3,...) in vec4 a; + hasLocation = true; + location = iter->second; + // if we want deal like that: + // vs: layout(location=4) out vec4 a; + // out vec4 b; + // + // fs: in vec4 a; + // layout(location = 4) in vec4 b; + // we need retraverse the map. + } + if (! hasLocation) { + // If interface resource note found, It's mean the location in two stage are both implicit declarat. + // So we should find a new slot for this interface. + // + // vs: out vec4 a; + // fs: in vec4 a; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first interface declarated in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + //Update location + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) { + return ent.newLocation = type.getQualifier().layoutLocation; + } else { + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || type.isSpirvType() || + (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + } + int location = referenceIntermediate.getUniformLocationOverride(name.c_str()); + if (location != -1) { + return ent.newLocation = location; + } + + int size = TIntermediate::computeTypeUniformLocationSize(type); + + // The uniform in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the uniform has location + bool hasLocation = false; + int resourceKey = buildStorageKey(EShLangCount, EvqUniform); + TVarSlotMap& slotMap = storageSlotMap[resourceKey]; + // Check dose shader program has uniform resource + if (! slotMap.empty()) { + // If uniform resource not empty, try find a same name uniform + TVarSlotMap::iterator iter = slotMap.find(name); + if (iter != slotMap.end()) { + // If uniform resource be found, set it has location and this symbol's new location + // equal the uniform's explicit location declaration in other stage. + // + // vs: uniform vec4 a; + // fs: layout(..., location = 3,...) uniform vec4 a; + hasLocation = true; + location = iter->second; + } + if (! hasLocation) { + // No explicit location declaration in other stage. + // So we should find a new slot for this uniform. + // + // vs: uniform vec4 a; + // fs: uniform vec4 a; + location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage)); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first uniform declaration in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, size); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + // On OpenGL arrays of opaque types take a separate binding for each element + int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + // don't need to handle uniform symbol, it will be handled in resolveUniformLocation + if (resource == EResUbo && type.getBasicType() != EbtBlock) { + return ent.newBinding = -1; + } + // There is no 'set' qualifier in OpenGL shading language, each resource has its own + // binding name space, so remap the 'set' to resource type which make each resource + // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS + int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet; + int resourceKey = set; + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings); + return ent.newBinding = newBinding; + + } else { + // The resource in current stage is not declared with binding, but it is possible declared + // with explicit binding in other stages, find the resourceSlotMap firstly to check whether + // the resource has binding, don't need to allocate if it already has a binding + bool hasBinding = false; + ent.newBinding = -1; // leave as -1 if it isn't set below + + if (! resourceSlotMap[resourceKey].empty()) { + TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name); + if (iter != resourceSlotMap[resourceKey].end()) { + hasBinding = true; + ent.newBinding = iter->second; + } + } + if (!hasBinding && (ent.live && doAutoBindingMapping())) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings); + resourceSlotMap[resourceKey][name] = binding; + ent.newBinding = binding; + } + return ent.newBinding; + } + } + return ent.newBinding = -1; +} + +void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + TStorageQualifier storage = type.getQualifier().storage; + EShLanguage stage(EShLangCount); + switch (storage) { + case EvqUniform: + if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) { + // + // Reserve the slots for the uniforms who has explicit location + int storageKey = buildStorageKey(EShLangCount, EvqUniform); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + case EvqVaryingIn: + case EvqVaryingOut: + // + // Reserve the slots for the inout who has explicit location + if (type.getQualifier().hasLocation()) { + stage = storage == EvqVaryingIn ? preStage : stage; + stage = storage == EvqVaryingOut ? currentStage : stage; + int storageKey = buildStorageKey(stage, EvqInOut); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + default: + break; + } +} + +void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + TResourceType resource = getResourceType(type); + int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent); + int resourceKey = set; + + if (type.getQualifier().hasBinding()) { + TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set); + + if (iter == varSlotMap.end()) { + // Reserve the slots for the ubo, ssbo and opaques who has explicit binding + int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + varSlotMap[name] = binding; + reserveSlot(resourceKey, binding, numBindings); + } else { + // Allocate binding by name for OpenGL driver, so the resource in different + // stages should be declared with the same binding + if (iter->second != binding) { + TString errorMsg = "Invalid binding: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } +} + +//TDefaultGlslIoResolver end + +/* + * Basic implementation of glslang::TIoMapResolver that replaces the + * previous offset behavior. + * It does the same, uses the offsets for the corresponding uniform + * types. Also respects the EOptionAutoMapBindings flag and binds + * them if needed. + */ +/* + * Default resolver + */ +struct TDefaultIoResolver : public TDefaultIoResolverBase { + TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + // On OpenGL arrays of opaque types take a seperate binding for each element + int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot( + set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings); + } + } + return ent.newBinding = -1; + } +}; + +#ifdef ENABLE_HLSL +/******************************************************************************** +The following IO resolver maps types in HLSL register space, as follows: + +t - for shader resource views (SRV) + TEXTURE1D + TEXTURE1DARRAY + TEXTURE2D + TEXTURE2DARRAY + TEXTURE3D + TEXTURECUBE + TEXTURECUBEARRAY + TEXTURE2DMS + TEXTURE2DMSARRAY + STRUCTUREDBUFFER + BYTEADDRESSBUFFER + BUFFER + TBUFFER + +s - for samplers + SAMPLER + SAMPLER1D + SAMPLER2D + SAMPLER3D + SAMPLERCUBE + SAMPLERSTATE + SAMPLERCOMPARISONSTATE + +u - for unordered access views (UAV) + RWBYTEADDRESSBUFFER + RWSTRUCTUREDBUFFER + APPENDSTRUCTUREDBUFFER + CONSUMESTRUCTUREDBUFFER + RWBUFFER + RWTEXTURE1D + RWTEXTURE1DARRAY + RWTEXTURE2D + RWTEXTURE2DARRAY + RWTEXTURE3D + +b - for constant buffer views (CBV) + CBUFFER + CONSTANTBUFFER + ********************************************************************************/ +struct TDefaultHlslIoResolver : public TDefaultIoResolverBase { + TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isUavType(type)) { + return EResUav; + } + if (isSrvType(type)) { + return EResTexture; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set)); + } + } + return ent.newBinding = -1; + } +}; +#endif + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) + return true; + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) + return false; + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) + return false; + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultIoResolver defaultResolver(intermediate); +#ifdef ENABLE_HLSL + TDefaultHlslIoResolver defaultHlslResolver(intermediate); + if (resolver == nullptr) { + // TODO: use a passed in IO mapper for this + if (intermediate.usingHlslIoMapping()) + resolver = &defaultHlslResolver; + else + resolver = &defaultResolver; + } +#else + resolver = &defaultResolver; +#endif + resolver->addStage(stage, intermediate); + + TVarLiveMap inVarMap, outVarMap, uniformVarMap; + TVarLiveVector inVector, outVector, uniformVector; + TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap); + TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.destinations.empty()) { + TIntermNode* destination = iter_binding_live.destinations.back(); + iter_binding_live.destinations.pop_back(); + destination->traverse(&iter_binding_live); + } + + // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. + for (auto& var : inVarMap) { inVector.push_back(var); } + std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + for (auto& var : outVarMap) { outVector.push_back(var); } + std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + for (auto& var : uniformVarMap) { uniformVector.push_back(var); } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + bool hadError = false; + TVarLiveMap* dummyUniformVarMap[EShLangCount] = {}; + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError); + resolver->beginNotifications(stage); + std::for_each(inVector.begin(), inVector.end(), inOutNotify); + std::for_each(outVector.begin(), outVector.end(), inOutNotify); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify); + resolver->endNotifications(stage); + resolver->beginResolve(stage); + for (auto& var : inVector) { inOutResolve(var); } + std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) { + auto at = inVarMap.find(p.second.symbol->getAccessName()); + if (at != inVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + for (auto& var : outVector) { inOutResolve(var); } + std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) { + auto at = outVarMap.find(p.second.symbol->getAccessName()); + if (at != outVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); + std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) { + auto at = uniformVarMap.find(p.second.symbol->getAccessName()); + if (at != uniformVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + resolver->endResolve(stage); + if (!hadError) { + TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_iomap); + } + return !hadError; +} + +TGlslIoMapper::TGlslIoMapper() { + memset(inVarMaps, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(outVarMaps, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(uniformVarMap, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(intermediates, 0, sizeof(TIntermediate*) * EShLangCount); + profile = ENoProfile; + version = 0; + autoPushConstantMaxSize = 128; + autoPushConstantBlockPacking = ElpStd430; +} + +TGlslIoMapper::~TGlslIoMapper() { + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + delete inVarMaps[stage]; + inVarMaps[stage] = nullptr; + } + if (outVarMaps[stage] != nullptr) { + delete outVarMaps[stage]; + outVarMaps[stage] = nullptr; + } + if (uniformVarMap[stage] != nullptr) { + delete uniformVarMap[stage]; + uniformVarMap[stage] = nullptr; + } + if (intermediates[stage] != nullptr) + intermediates[stage] = nullptr; + } +} + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + bool somethingToDo = !intermediate.getResourceSetBinding().empty() || + intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + + // Profile and version are use for symbol validate. + profile = intermediate.getProfile(); + version = intermediate.getVersion(); + + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) { + return true; + } + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) { + return false; + } + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) { + return false; + } + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultGlslIoResolver defaultResolver(intermediate); +#ifdef ENABLE_HLSL + TDefaultHlslIoResolver defaultHlslResolver(intermediate); + if (resolver == nullptr) { + // TODO: use a passed in IO mapper for this + if (intermediate.usingHlslIoMapping()) + resolver = &defaultHlslResolver; + else + resolver = &defaultResolver; + } +#else + if (resolver == nullptr) { + resolver = &defaultResolver; + } +#endif + resolver->addStage(stage, intermediate); + inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap(); + TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.destinations.empty()) { + TIntermNode* destination = iter_binding_live.destinations.back(); + iter_binding_live.destinations.pop_back(); + destination->traverse(&iter_binding_live); + } + + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + // Resolve current stage input symbol location with previous stage output here, + // uniform symbol, ubo, ssbo and opaque symbols are per-program resource, + // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap() + resolver->beginNotifications(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify); + resolver->endNotifications(stage); + TSlotCollector slotCollector(*resolver, infoSink); + resolver->beginCollect(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector); + resolver->endCollect(stage); + intermediates[stage] = &intermediate; + return !hadError; +} + +bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { + resolver->endResolve(EShLangCount); + if (!hadError) { + //Resolve uniform location, ubo/ssbo/opaque bindings across stages + TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError); + TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, + outVarMaps, uniformVarMap, hadError, profile, version); + + TVarLiveVector inVectors[EShLangCount]; + TVarLiveVector outVectors[EShLangCount]; + TVarLiveVector uniformVector; + + resolver->beginResolve(EShLangCount); + for (int stage = EShLangVertex; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + inOutResolve.setStage(EShLanguage(stage)); + + // copy vars into a sorted list + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), + [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); }); + std::sort(inVectors[stage].begin(), inVectors[stage].end(), + [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), + [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); }); + std::sort(outVectors[stage].begin(), outVectors[stage].end(), + [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + + for (auto& var : inVectors[stage]) { symbolValidater(var); } + for (auto& var : inVectors[stage]) { inOutResolve(var); } + for (auto& var : outVectors[stage]) { symbolValidater(var); } + for (auto& var : outVectors[stage]) { inOutResolve(var); } + + // copy results back into maps + std::for_each(inVectors[stage].begin(), inVectors[stage].end(), + [this, stage](TVarLivePair p) { + auto at = inVarMaps[stage]->find(p.first); + if (at != inVarMaps[stage]->end()) + at->second = p.second; + }); + + std::for_each(outVectors[stage].begin(), outVectors[stage].end(), + [this, stage](TVarLivePair p) { + auto at = outVarMaps[stage]->find(p.first); + if (at != outVarMaps[stage]->end()) + at->second = p.second; + }); + + } + if (uniformVarMap[stage] != nullptr) { + uniformResolve.setStage(EShLanguage(stage)); + for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); } + } + } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second); + }); + for (auto& var : uniformVector) { symbolValidater(var); } + for (auto& var : uniformVector) { uniformResolve(var); } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + resolver->endResolve(EShLangCount); + if (autoPushConstantBlockName.length()) { + bool upgraded = false; + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (intermediates[stage] != nullptr) { + TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap; + auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName); + if (at == pUniformVarMap[stage]->end()) + continue; + TQualifier& qualifier = at->second.symbol->getQualifier(); + if (!qualifier.isUniform()) + continue; + TType& t = at->second.symbol->getWritableType(); + int size, stride; + TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking, + qualifier.layoutMatrix == ElmRowMajor); + if (size <= int(autoPushConstantMaxSize)) { + qualifier.setBlockStorage(EbsPushConstant); + qualifier.layoutPacking = autoPushConstantBlockPacking; + // Push constants don't have set/binding etc. decorations, remove those. + qualifier.layoutSet = TQualifier::layoutSetEnd; + at->second.clearNewAssignments(); + + upgraded = true; + } + } + } + // If it's been upgraded to push_constant, then set the flag so when its traversed + // in the next for loop, all references to this symbol will get their flag changed. + // so it doesn't get a set/binding assigned to it. + if (upgraded) { + std::for_each(uniformVector.begin(), uniformVector.end(), + [this](TVarLivePair& p) { + if (p.first == autoPushConstantBlockName) { + p.second.upgradedToPushConstantPacking = autoPushConstantBlockPacking; + p.second.newSet = TQualifier::layoutSetEnd; + } + }); + } + } + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (intermediates[stage] != nullptr) { + // traverse each stage, set new location to each input/output and unifom symbol, set new binding to + // ubo, ssbo and opaque symbols. Assign push_constant upgrades as well. + TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap; + std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) { + auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName()); + if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){ + if (p.second.upgradedToPushConstantPacking != ElpNone) { + at->second.upgradedToPushConstantPacking = p.second.upgradedToPushConstantPacking; + } else { + int resolvedBinding = at->second.newBinding; + at->second = p.second; + if (resolvedBinding > 0) + at->second.newBinding = resolvedBinding; + } + } + }); + TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage], + *uniformResolve.uniformVarMap[stage]); + intermediates[stage]->getTreeRoot()->traverse(&iter_iomap); + } + } + return !hadError; + } else { + return false; + } +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.h new file mode 100644 index 00000000..ef513d9a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/iomapper.h @@ -0,0 +1,223 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _IOMAPPER_INCLUDED +#define _IOMAPPER_INCLUDED + +#include +#include +#include +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +class TInfoSink; + +namespace glslang { + +class TIntermediate; +struct TVarEntryInfo; +// Base class for shared TIoMapResolver services, used by several derivations. +struct TDefaultIoResolverBase : public glslang::TIoMapResolver { +public: + TDefaultIoResolverBase(const TIntermediate& intermediate); + typedef std::vector TSlotSet; + typedef std::unordered_map TSlotSetMap; + + // grow the reflection stage by stage + void notifyBinding(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void notifyInOut(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void beginNotifications(EShLanguage) override {} + void endNotifications(EShLanguage) override {} + void beginResolve(EShLanguage) override {} + void endResolve(EShLanguage) override {} + void beginCollect(EShLanguage) override {} + void endCollect(EShLanguage) override {} + void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + int getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const; + const std::vector& getResourceSetBinding(EShLanguage stage) const; + virtual TResourceType getResourceType(const glslang::TType& type) = 0; + bool doAutoBindingMapping() const; + bool doAutoLocationMapping() const; + TSlotSet::iterator findSlot(int set, int slot); + bool checkEmpty(int set, int slot); + bool validateInOut(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + int reserveSlot(int set, int slot, int size = 1); + int getFreeSlot(int set, int base, int size = 1); + int resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void addStage(EShLanguage stage, TIntermediate& stageIntermediate) override { + if (stage < EShLangCount) { + stageMask[stage] = true; + stageIntermediates[stage] = &stageIntermediate; + } + } + uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage); + + TSlotSetMap slots; + bool hasError = false; + +protected: + TDefaultIoResolverBase(TDefaultIoResolverBase&); + TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&); + const TIntermediate& referenceIntermediate; + int nextUniformLocation; + int nextInputLocation; + int nextOutputLocation; + bool stageMask[EShLangCount + 1]; + const TIntermediate* stageIntermediates[EShLangCount]; + + // Return descriptor set specific base if there is one, and the generic base otherwise. + int selectBaseBinding(int base, int descriptorSetBase) const { + return descriptorSetBase != -1 ? descriptorSetBase : base; + } + + static int getLayoutSet(const glslang::TType& type) { + if (type.getQualifier().hasSet()) + return type.getQualifier().layoutSet; + else + return 0; + } + + static bool isSamplerType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler(); + } + + static bool isTextureType(const glslang::TType& type) { + return (type.getBasicType() == glslang::EbtSampler && + (type.getSampler().isTexture() || type.getSampler().isSubpass())); + } + + static bool isUboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqUniform; + } + + static bool isImageType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage(); + } + + static bool isSsboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a SRV (shader resource view) type: + static bool isSrvType(const glslang::TType& type) { + return isTextureType(type) || type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a UAV (unordered access view) type: + static bool isUavType(const glslang::TType& type) { + if (type.getQualifier().isReadOnly()) + return false; + return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || + (type.getQualifier().storage == EvqBuffer); + } +}; + +// Default I/O resolver for OpenGL +struct TDefaultGlslIoResolver : public TDefaultIoResolverBase { +public: + typedef std::map TVarSlotMap; // + typedef std::map TSlotMap; // + TDefaultGlslIoResolver(const TIntermediate& intermediate); + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + TResourceType getResourceType(const glslang::TType& type) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void beginResolve(EShLanguage /*stage*/) override; + void endResolve(EShLanguage stage) override; + void beginCollect(EShLanguage) override; + void endCollect(EShLanguage) override; + void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + // in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol. + // We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage. + // if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key. + // Note: both stage and type must less then 0xffff. + int buildStorageKey(EShLanguage stage, TStorageQualifier type) { + assert(static_cast(stage) <= 0x0000ffff && static_cast(type) <= 0x0000ffff); + return (stage << 16) | type; + } + +protected: + // Use for mark pre stage, to get more interface symbol information. + EShLanguage preStage; + // Use for mark current shader stage for resolver + EShLanguage currentStage; + // Slot map for storage resource(location of uniform and interface symbol) It's a program share slot + TSlotMap resourceSlotMap; + // Slot map for other resource(image, ubo, ssbo), It's a program share slot. + TSlotMap storageSlotMap; +}; + +typedef std::map TVarLiveMap; + +// I/O mapper for GLSL +class TGlslIoMapper : public TIoMapper { +public: + TGlslIoMapper(); + virtual ~TGlslIoMapper(); + // If set, the uniform block with the given name will be changed to be backed by + // push_constant if it's size is <= maxSize + bool setAutoPushConstantBlock(const char* name, unsigned int maxSize, TLayoutPacking packing) override { + autoPushConstantBlockName = name; + autoPushConstantMaxSize = maxSize; + autoPushConstantBlockPacking = packing; + return true; + } + // grow the reflection stage by stage + bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*) override; + bool doMap(TIoMapResolver*, TInfoSink&) override; + TIntermediate* intermediates[EShLangCount]; + bool hadError = false; + EProfile profile; + int version; + +private: + TString autoPushConstantBlockName; + unsigned int autoPushConstantMaxSize; + TLayoutPacking autoPushConstantBlockPacking; + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], + *uniformVarMap[EShLangCount]; +}; + +} // end namespace glslang + +#endif // _IOMAPPER_INCLUDED diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/limits.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/limits.cpp new file mode 100644 index 00000000..4404beca --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/limits.cpp @@ -0,0 +1,198 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do sub tree walks for +// 1) inductive loop bodies to see if the inductive variable is modified +// 2) array-index expressions to see if they are "constant-index-expression" +// +// These are per Appendix A of ES 2.0: +// +// "Within the body of the loop, the loop index is not statically assigned to nor is it used as the +// argument to a function out or inout parameter." +// +// "The following are constant-index-expressions: +// - Constant expressions +// - Loop indices as defined in section 4 +// - Expressions composed of both of the above" +// +// N.B.: assuming the last rule excludes function calls +// + +#include "ParseHelper.h" + +namespace glslang { + +// +// The inductive loop-body traverser. +// +// Just look at things that might modify the loop index. +// + +class TInductiveTraverser : public TIntermTraverser { +public: + TInductiveTraverser(long long id, TSymbolTable& st) + : loopId(id), symbolTable(st), bad(false) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + long long loopId; // unique ID of the symbol that's the loop inductive variable + TSymbolTable& symbolTable; + bool bad; + TSourceLoc badLoc; + +protected: + TInductiveTraverser(TInductiveTraverser&); + TInductiveTraverser& operator=(TInductiveTraverser&); +}; + +// check binary operations for those modifying the loop index +bool TInductiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + if (node->modifiesState() && node->getLeft()->getAsSymbolNode() && + node->getLeft()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check unary operations for those modifying the loop index +bool TInductiveTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + if (node->modifiesState() && node->getOperand()->getAsSymbolNode() && + node->getOperand()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check function calls for arguments modifying the loop index +bool TInductiveTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + // see if an out or inout argument is the loop index + const TIntermSequence& args = node->getSequence(); + for (int i = 0; i < (int)args.size(); ++i) { + if (args[i]->getAsSymbolNode() && args[i]->getAsSymbolNode()->getId() == loopId) { + TSymbol* function = symbolTable.find(node->getName()); + const TType* type = (*function->getAsFunction())[i].type; + if (type->getQualifier().storage == EvqOut || + type->getQualifier().storage == EvqInOut) { + bad = true; + badLoc = node->getLoc(); + } + } + } + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, long long loopId, TSymbolTable& symbolTable) +{ + TInductiveTraverser it(loopId, symbolTable); + + if (body == nullptr) + return; + + body->traverse(&it); + + if (it.bad) + error(it.badLoc, "inductive loop index modified", "limitations", ""); +} + +// +// The "constant-index-expression" tranverser. +// +// Just look at things that can form an index. +// + +class TIndexTraverser : public TIntermTraverser { +public: + TIndexTraverser(const TIdSetType& ids) : inductiveLoopIds(ids), bad(false) { } + virtual void visitSymbol(TIntermSymbol* symbol); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + const TIdSetType& inductiveLoopIds; + bool bad; + TSourceLoc badLoc; + +protected: + TIndexTraverser(TIndexTraverser&); + TIndexTraverser& operator=(TIndexTraverser&); +}; + +// make sure symbols are inductive-loop indexes +void TIndexTraverser::visitSymbol(TIntermSymbol* symbol) +{ + if (inductiveLoopIds.find(symbol->getId()) == inductiveLoopIds.end()) { + bad = true; + badLoc = symbol->getLoc(); + } +} + +// check for function calls, assuming they are bad; spec. doesn't really say +bool TIndexTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::constantIndexExpressionCheck(TIntermNode* index) +{ + TIndexTraverser it(inductiveLoopIds); + + index->traverse(&it); + + if (it.bad) + error(it.badLoc, "Non-constant-index-expression", "limitations", ""); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/linkValidate.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/linkValidate.cpp new file mode 100644 index 00000000..f53479e0 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/linkValidate.cpp @@ -0,0 +1,2734 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do link-time merging and validation of intermediate representations. +// +// Basic model is that during compilation, each compilation unit (shader) is +// compiled into one TIntermediate instance. Then, at link time, multiple +// units for the same stage can be merged together, which can generate errors. +// Then, after all merging, a single instance of TIntermediate represents +// the whole stage. A final error check can be done on the resulting stage, +// even if no merging was done (i.e., the stage was only one compilation unit). +// + +#include "glslang/Public/ShaderLang.h" +#include "localintermediate.h" +#include "../Include/InfoSink.h" +#include "SymbolTable.h" +#include "LiveTraverser.h" + +namespace glslang { + +// +// Link-time error emitter. +// +void TIntermediate::error(TInfoSink& infoSink, const TSourceLoc* loc, EShMessages messages, const char* message, + EShLanguage unitStage) +{ + infoSink.info.prefix(EPrefixError); + if (loc) + infoSink.info.location(*loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + if (unitStage == EShLangCount) + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; + else if (language == EShLangCount) + infoSink.info << "Linking " << StageName(unitStage) << " stage: " << message << "\n"; + else + infoSink.info << "Linking " << StageName(language) << " and " << StageName(unitStage) << " stages: " << message << "\n"; + + ++numErrors; +} + +// Link-time warning. +void TIntermediate::warn(TInfoSink& infoSink, const TSourceLoc* loc, EShMessages messages, const char* message, + EShLanguage unitStage) +{ + infoSink.info.prefix(EPrefixWarning); + if (loc) + infoSink.info.location(*loc, messages & EShMsgAbsolutePath, messages & EShMsgDisplayErrorColumn); + if (unitStage == EShLangCount) + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; + else if (language == EShLangCount) + infoSink.info << "Linking " << StageName(unitStage) << " stage: " << message << "\n"; + else + infoSink.info << "Linking " << StageName(language) << " and " << StageName(unitStage) << " stages: " << message << "\n"; +} + +// TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block +// name must have the exact same set of members qualified with offset and their integral-constant +// expression values must be the same, or a link-time error results." + +// +// Merge the information from 'unit' into 'this' +// +void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) +{ + mergeCallGraphs(infoSink, unit); + mergeModes(infoSink, unit); + mergeTrees(infoSink, unit); +} + +// +// check that link objects between stages +// +void TIntermediate::mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit) { + if (unit.treeRoot == nullptr || treeRoot == nullptr) + return; + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // filter unitLinkerObjects to only contain uniforms + auto end = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqUniform && + node->getAsSymbolNode()->getQualifier().storage != EvqBuffer; }); + unitLinkerObjects.resize(end - unitLinkerObjects.begin()); + + // merge uniforms and do error checking + bool mergeExistingOnly = false; + mergeGlobalUniformBlocks(infoSink, unit, mergeExistingOnly); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); +} + +static inline bool isSameInterface(TIntermSymbol* symbol, TIntermSymbol* unitSymbol) { + EShLanguage stage = symbol->getStage(); + EShLanguage unitStage = unitSymbol->getStage(); + return // 1) same stage and same shader interface + (stage == unitStage && symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) || + // 2) accross stages and both are uniform or buffer + (symbol->getQualifier().storage == EvqUniform && unitSymbol->getQualifier().storage == EvqUniform) || + (symbol->getQualifier().storage == EvqBuffer && unitSymbol->getQualifier().storage == EvqBuffer) || + // 3) in/out matched across stage boundary + (stage < unitStage && symbol->getQualifier().storage == EvqVaryingOut && unitSymbol->getQualifier().storage == EvqVaryingIn) || + (unitStage < stage && symbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().storage == EvqVaryingOut); +} + +static bool isSameSymbol(TIntermSymbol* symbol1, TIntermSymbol* symbol2) { + // If they are both blocks in the same shader interface, + // match by the block-name, not the identifier name. + if (symbol1->getType().getBasicType() == EbtBlock && symbol2->getType().getBasicType() == EbtBlock) { + if (isSameInterface(symbol1, symbol2)) { + return symbol1->getType().getTypeName() == symbol2->getType().getTypeName(); + } + } else if (symbol1->getName() == symbol2->getName()) + return true; + return false; +} + +// +// merge implicit array sizes for uniform/buffer objects +// +void TIntermediate::mergeImplicitArraySizes(TInfoSink&, TIntermediate& unit) { + if (unit.treeRoot == nullptr || treeRoot == nullptr) + return; + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // filter unitLinkerObjects to only contain uniforms + auto end = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqUniform && + node->getAsSymbolNode()->getQualifier().storage != EvqBuffer; }); + unitLinkerObjects.resize(end - unitLinkerObjects.begin()); + + std::size_t initialNumLinkerObjects = linkerObjects.size(); + for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + if (isSameSymbol(symbol, unitSymbol)) { + // Update implicit array sizes + mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); + } + } + } +} + +// +// do error checking on the shader boundary in / out vars +// +void TIntermediate::checkStageIO(TInfoSink& infoSink, TIntermediate& unit, EShMessages messages) { + if (unit.treeRoot == nullptr || treeRoot == nullptr) + return; + + // Get copies of the linker-object lists + TIntermSequence linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // filter linkerObjects to only contain out variables + auto end = std::remove_if(linkerObjects.begin(), linkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingOut; }); + linkerObjects.resize(end - linkerObjects.begin()); + + // filter unitLinkerObjects to only contain in variables + auto unitEnd = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingIn; }); + unitLinkerObjects.resize(unitEnd - unitLinkerObjects.begin()); + + // do matching and error checking + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); + + if ((messages & EShMsgValidateCrossStageIO) == 0) + return; + + // The OpenGL Shading Language, Version 4.60.8 (https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) + // 4.3.4 Input Variables + // Only the input variables that are statically read need to be written by the previous stage; it is + // allowed to have superfluous declarations of input variables. This is shown in the following table. + // +------------------------------------------------------------------------------------------------+ + // | Treatment of Mismatched Input | Consuming Shader (input variables) | + // | Variables |---------------------------------------------------------| + // | | No | Declared but no | Declared and Static Use | + // | | Declaration | Static Use | | + // |--------------------------------------+-------------+-----------------+-------------------------| + // | Generating Shader | No Declaration | Allowed | Allowed | Link-Time Error | + // | (output variables) |-----------------+-------------+-----------------+-------------------------| + // | | Declared but no | Allowed | Allowed | Allowed (values are | + // | | Static Use | | | undefined) | + // | |-----------------+-------------+-----------------+-------------------------| + // | | Declared and | Allowed | Allowed | Allowed (values are | + // | | Static Use | | | potentially undefined) | + // +------------------------------------------------------------------------------------------------+ + // Consumption errors are based on static use only. Compilation may generate a warning, but not an + // error, for any dynamic use the compiler can deduce that might cause consumption of undefined values. + + // TODO: implement support for geometry passthrough + if (getGeoPassthroughEXT()) { + unit.warn(infoSink, "GL_NV_geometry_shader_passthrough is enabled, skipping cross-stage IO validation", + getStage()); + return; + } + + class TIOTraverser : public TLiveTraverser { + public: + TIOTraverser(TIntermediate& i, bool all, TIntermSequence& sequence, TStorageQualifier storage) + : TLiveTraverser(i, all, true, false, false), sequence(sequence), storage(storage) + { + } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + if (symbol->getQualifier().storage == storage) + sequence.push_back(symbol); + } + + private: + TIntermSequence& sequence; + TStorageQualifier storage; + }; + + // live symbols only + TIntermSequence unitLiveInputs; + + TIOTraverser unitTraverser(unit, false, unitLiveInputs, EvqVaryingIn); + unitTraverser.pushFunction(unit.getEntryPointMangledName().c_str()); + while (! unitTraverser.destinations.empty()) { + TIntermNode* destination = unitTraverser.destinations.back(); + unitTraverser.destinations.pop_back(); + destination->traverse(&unitTraverser); + } + + // all symbols + TIntermSequence allOutputs; + + TIOTraverser traverser(*this, true, allOutputs, EvqVaryingOut); + getTreeRoot()->traverse(&traverser); + + std::unordered_set outputLocations; + for (auto& output : allOutputs) { + if (output->getAsSymbolNode()->getBasicType() == EbtBlock) { + int lastLocation = -1; + if (output->getAsSymbolNode()->getQualifier().hasLocation()) + lastLocation = output->getAsSymbolNode()->getQualifier().layoutLocation; + const TTypeList* members = output->getAsSymbolNode()->getType().getStruct(); + for (auto& member : *members) { + int location = lastLocation; + if (member.type->getQualifier().hasLocation()) + location = member.type->getQualifier().layoutLocation; + if (location != -1) { + int locationSize = TIntermediate::computeTypeLocationSize(*member.type, getStage()); + for (int i = 0; i < locationSize; ++i) + outputLocations.insert(location + i); + lastLocation = location + locationSize; + } + } + } else { + int locationSize = TIntermediate::computeTypeLocationSize(output->getAsSymbolNode()->getType(), getStage()); + for (int i = 0; i < locationSize; ++i) + outputLocations.insert(output->getAsSymbolNode()->getQualifier().layoutLocation + i); + } + } + + // remove unitStage inputs with matching outputs in the current stage + auto liveEnd = std::remove_if( + unitLiveInputs.begin(), unitLiveInputs.end(), [this, &allOutputs, &outputLocations](TIntermNode* input) { + // ignore built-ins + if (input->getAsSymbolNode()->getAccessName().compare(0, 3, "gl_") == 0) + return true; + // try to match by location + if (input->getAsSymbolNode()->getQualifier().hasLocation() && + outputLocations.find(input->getAsSymbolNode()->getQualifier().layoutLocation) != outputLocations.end()) + return true; + if (input->getAsSymbolNode()->getBasicType() == EbtBlock) { + int lastLocation = -1; + if (input->getAsSymbolNode()->getQualifier().hasLocation()) + lastLocation = input->getAsSymbolNode()->getQualifier().layoutLocation; + const TTypeList* members = input->getAsSymbolNode()->getType().getStruct(); + for (auto& member : *members) { + int location = lastLocation; + if (member.type->getQualifier().hasLocation()) + location = member.type->getQualifier().layoutLocation; + if (location != -1) { + int locationSize = TIntermediate::computeTypeLocationSize(*member.type, getStage()); + for (int i = 0; i < locationSize; ++i) + if (outputLocations.find(location + i) != outputLocations.end()) + return true; + lastLocation = location + locationSize; + } + } + } + // otherwise, try to match by name + return std::any_of(allOutputs.begin(), allOutputs.end(), [input](TIntermNode* output) { + return output->getAsSymbolNode()->getAccessName() == input->getAsSymbolNode()->getAccessName(); + }); + }); + unitLiveInputs.resize(liveEnd - unitLiveInputs.begin()); + + // check remaining loose unitStage inputs for a matching output block member + liveEnd = std::remove_if(unitLiveInputs.begin(), unitLiveInputs.end(), [&allOutputs](TIntermNode* input) { + return std::any_of(allOutputs.begin(), allOutputs.end(), [input](TIntermNode* output) { + if (output->getAsSymbolNode()->getBasicType() != EbtBlock) + return false; + const TTypeList* members = output->getAsSymbolNode()->getType().getStruct(); + return std::any_of(members->begin(), members->end(), [input](TTypeLoc type) { + return type.type->getFieldName() == input->getAsSymbolNode()->getName(); + }); + }); + }); + unitLiveInputs.resize(liveEnd - unitLiveInputs.begin()); + + // finally, check remaining unitStage block inputs for a matching loose output + liveEnd = std::remove_if( + unitLiveInputs.begin(), unitLiveInputs.end(), [&allOutputs](TIntermNode* input) { + if (input->getAsSymbolNode()->getBasicType() != EbtBlock) + return false; + // liveness isn't tracked per member so finding any one live member is the best we can do + const TTypeList* members = input->getAsSymbolNode()->getType().getStruct(); + return std::any_of(members->begin(), members->end(), [allOutputs](TTypeLoc type) { + return std::any_of(allOutputs.begin(), allOutputs.end(), [&type](TIntermNode* output) { + return type.type->getFieldName() == output->getAsSymbolNode()->getName(); + }); + }); + }); + unitLiveInputs.resize(liveEnd - unitLiveInputs.begin()); + + // any remaining unitStage inputs have no matching output + std::for_each(unitLiveInputs.begin(), unitLiveInputs.end(), [&](TIntermNode* input) { + unit.error(infoSink, &input->getLoc(), messages, + "Preceding stage has no matching declaration for statically used input:", getStage()); + infoSink.info << " " + << input->getAsSymbolNode()->getType().getCompleteString( + true, true, false, true, input->getAsSymbolNode()->getAccessName()) + << "\n"; + }); + + // TODO: warn about statically read inputs with outputs declared but not written to +} + +void TIntermediate::optimizeStageIO(TInfoSink&, TIntermediate& unit) +{ + // don't do any input/output demotion on compute, raytracing, or task/mesh stages + // TODO: support task/mesh + if (getStage() > EShLangFragment || unit.getStage() > EShLangFragment) { + return; + } + + class TIOTraverser : public TLiveTraverser { + public: + TIOTraverser(TIntermediate& i, bool all, TIntermSequence& sequence, TStorageQualifier storage) + : TLiveTraverser(i, all, true, false, false), sequence(sequence), storage(storage) + { + } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + if (symbol->getQualifier().storage == storage) { + sequence.push_back(symbol); + } + } + + private: + TIntermSequence& sequence; + TStorageQualifier storage; + }; + + // live symbols only + TIntermSequence unitLiveInputs; + + TIOTraverser unitTraverser(unit, false, unitLiveInputs, EvqVaryingIn); + unitTraverser.pushFunction(unit.getEntryPointMangledName().c_str()); + while (! unitTraverser.destinations.empty()) { + TIntermNode* destination = unitTraverser.destinations.back(); + unitTraverser.destinations.pop_back(); + destination->traverse(&unitTraverser); + } + + TIntermSequence allOutputs; + TIntermSequence unitAllInputs; + + TIOTraverser allTraverser(*this, true, allOutputs, EvqVaryingOut); + getTreeRoot()->traverse(&allTraverser); + + TIOTraverser unitAllTraverser(unit, true, unitAllInputs, EvqVaryingIn); + unit.getTreeRoot()->traverse(&unitAllTraverser); + + // find outputs not consumed by the next stage + std::for_each(allOutputs.begin(), allOutputs.end(), [&unitLiveInputs, &unitAllInputs](TIntermNode* output) { + // don't do anything to builtins + if (output->getAsSymbolNode()->getAccessName().compare(0, 3, "gl_") == 0) + return; + + // don't demote block outputs (for now) + if (output->getAsSymbolNode()->getBasicType() == EbtBlock) + return; + + // check if the (loose) output has a matching loose input + auto isMatchingInput = [output](TIntermNode* input) { + return output->getAsSymbolNode()->getAccessName() == input->getAsSymbolNode()->getAccessName(); + }; + + // check if the (loose) output has a matching block member input + auto isMatchingInputBlockMember = [output](TIntermNode* input) { + // ignore loose inputs + if (input->getAsSymbolNode()->getBasicType() != EbtBlock) + return false; + + // don't demote loose outputs with matching input block members + auto isMatchingBlockMember = [output](TTypeLoc type) { + return type.type->getFieldName() == output->getAsSymbolNode()->getName(); + }; + const TTypeList* members = input->getAsSymbolNode()->getType().getStruct(); + return std::any_of(members->begin(), members->end(), isMatchingBlockMember); + }; + + // determine if the input/output pair should be demoted + // do the faster (and more likely) loose-loose check first + if (std::none_of(unitLiveInputs.begin(), unitLiveInputs.end(), isMatchingInput) && + std::none_of(unitAllInputs.begin(), unitAllInputs.end(), isMatchingInputBlockMember)) { + // demote any input matching the output + auto demoteMatchingInputs = [output](TIntermNode* input) { + if (output->getAsSymbolNode()->getAccessName() == input->getAsSymbolNode()->getAccessName()) { + // demote input to a plain variable + TIntermSymbol* symbol = input->getAsSymbolNode(); + symbol->getQualifier().storage = EvqGlobal; + symbol->getQualifier().clearInterstage(); + symbol->getQualifier().clearLayout(); + } + }; + + // demote all matching outputs to a plain variable + TIntermSymbol* symbol = output->getAsSymbolNode(); + symbol->getQualifier().storage = EvqGlobal; + symbol->getQualifier().clearInterstage(); + symbol->getQualifier().clearLayout(); + std::for_each(unitAllInputs.begin(), unitAllInputs.end(), demoteMatchingInputs); + } + }); +} + +void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.getNumEntryPoints() > 0) { + if (getNumEntryPoints() > 0) + error(infoSink, "can't handle multiple entry points per stage"); + else { + entryPointName = unit.getEntryPointName(); + entryPointMangledName = unit.getEntryPointMangledName(); + } + } + numEntryPoints += unit.getNumEntryPoints(); + + callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end()); +} + +#define MERGE_MAX(member) member = std::max(member, unit.member) +#define MERGE_TRUE(member) if (unit.member) member = unit.member; + +void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit) +{ + if (language != unit.language) + error(infoSink, "stages must match when linking into a single stage"); + + if (getSource() == EShSourceNone) + setSource(unit.getSource()); + if (getSource() != unit.getSource()) + error(infoSink, "can't link compilation units from different source languages"); + + if (treeRoot == nullptr) { + profile = unit.profile; + version = unit.version; + requestedExtensions = unit.requestedExtensions; + } else { + if ((isEsProfile()) != (unit.isEsProfile())) + error(infoSink, "Cannot cross link ES and desktop profiles"); + else if (unit.profile == ECompatibilityProfile) + profile = ECompatibilityProfile; + version = std::max(version, unit.version); + requestedExtensions.insert(unit.requestedExtensions.begin(), unit.requestedExtensions.end()); + } + + MERGE_MAX(spvVersion.spv); + MERGE_MAX(spvVersion.vulkanGlsl); + MERGE_MAX(spvVersion.vulkan); + MERGE_MAX(spvVersion.openGl); + MERGE_TRUE(spvVersion.vulkanRelaxed); + + numErrors += unit.getNumErrors(); + // Only one push_constant is allowed, mergeLinkerObjects() will ensure the push_constant + // is the same for all units. + if (numPushConstants > 1 || unit.numPushConstants > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + numPushConstants = std::min(numPushConstants + unit.numPushConstants, 1); + + if (unit.invocations != TQualifier::layoutNotSet) { + if (invocations == TQualifier::layoutNotSet) + invocations = unit.invocations; + else if (invocations != unit.invocations) + error(infoSink, "number of invocations must match between compilation units"); + } + + if (vertices == TQualifier::layoutNotSet) + vertices = unit.vertices; + else if (unit.vertices != TQualifier::layoutNotSet && vertices != unit.vertices) { + if (language == EShLangGeometry || language == EShLangMesh) + error(infoSink, "Contradictory layout max_vertices values"); + else if (language == EShLangTessControl) + error(infoSink, "Contradictory layout vertices values"); + else + assert(0); + } + if (primitives == TQualifier::layoutNotSet) + primitives = unit.primitives; + else if (primitives != unit.primitives) { + if (language == EShLangMesh) + error(infoSink, "Contradictory layout max_primitives values"); + else + assert(0); + } + + if (inputPrimitive == ElgNone) + inputPrimitive = unit.inputPrimitive; + else if (unit.inputPrimitive != ElgNone && inputPrimitive != unit.inputPrimitive) + error(infoSink, "Contradictory input layout primitives"); + + if (outputPrimitive == ElgNone) + outputPrimitive = unit.outputPrimitive; + else if (unit.outputPrimitive != ElgNone && outputPrimitive != unit.outputPrimitive) + error(infoSink, "Contradictory output layout primitives"); + + if (originUpperLeft != unit.originUpperLeft || pixelCenterInteger != unit.pixelCenterInteger) + error(infoSink, "gl_FragCoord redeclarations must match across shaders"); + + if (vertexSpacing == EvsNone) + vertexSpacing = unit.vertexSpacing; + else if (vertexSpacing != unit.vertexSpacing) + error(infoSink, "Contradictory input vertex spacing"); + + if (vertexOrder == EvoNone) + vertexOrder = unit.vertexOrder; + else if (vertexOrder != unit.vertexOrder) + error(infoSink, "Contradictory triangle ordering"); + + MERGE_TRUE(pointMode); + + for (int i = 0; i < 3; ++i) { + if (unit.localSizeNotDefault[i]) { + if (!localSizeNotDefault[i]) { + localSize[i] = unit.localSize[i]; + localSizeNotDefault[i] = true; + } + else if (localSize[i] != unit.localSize[i]) + error(infoSink, "Contradictory local size"); + } + + if (localSizeSpecId[i] == TQualifier::layoutNotSet) + localSizeSpecId[i] = unit.localSizeSpecId[i]; + else if (localSizeSpecId[i] != unit.localSizeSpecId[i]) + error(infoSink, "Contradictory local size specialization ids"); + } + + MERGE_TRUE(earlyFragmentTests); + MERGE_TRUE(postDepthCoverage); + MERGE_TRUE(nonCoherentColorAttachmentReadEXT); + MERGE_TRUE(nonCoherentDepthAttachmentReadEXT); + MERGE_TRUE(nonCoherentStencilAttachmentReadEXT); + + if (depthLayout == EldNone) + depthLayout = unit.depthLayout; + else if (depthLayout != unit.depthLayout) + error(infoSink, "Contradictory depth layouts"); + + MERGE_TRUE(depthReplacing); + MERGE_TRUE(hlslFunctionality1); + + blendEquations |= unit.blendEquations; + + MERGE_TRUE(xfbMode); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = unit.xfbBuffers[b].stride; + else if (xfbBuffers[b].stride != unit.xfbBuffers[b].stride) + error(infoSink, "Contradictory xfb_stride"); + xfbBuffers[b].implicitStride = std::max(xfbBuffers[b].implicitStride, unit.xfbBuffers[b].implicitStride); + if (unit.xfbBuffers[b].contains64BitType) + xfbBuffers[b].contains64BitType = true; + if (unit.xfbBuffers[b].contains32BitType) + xfbBuffers[b].contains32BitType = true; + if (unit.xfbBuffers[b].contains16BitType) + xfbBuffers[b].contains16BitType = true; + // TODO: 4.4 link: enhanced layouts: compare ranges + } + + MERGE_TRUE(multiStream); + MERGE_TRUE(layoutOverrideCoverage); + MERGE_TRUE(geoPassthroughEXT); + + for (unsigned int i = 0; i < unit.shiftBinding.size(); ++i) { + if (unit.shiftBinding[i] > 0) + setShiftBinding((TResourceType)i, unit.shiftBinding[i]); + } + + for (unsigned int i = 0; i < unit.shiftBindingForSet.size(); ++i) { + for (auto it = unit.shiftBindingForSet[i].begin(); it != unit.shiftBindingForSet[i].end(); ++it) + setShiftBindingForSet((TResourceType)i, it->second, it->first); + } + + resourceSetBinding.insert(resourceSetBinding.end(), unit.resourceSetBinding.begin(), unit.resourceSetBinding.end()); + + MERGE_TRUE(autoMapBindings); + MERGE_TRUE(autoMapLocations); + MERGE_TRUE(invertY); + MERGE_TRUE(dxPositionW); + MERGE_TRUE(debugInfo); + MERGE_TRUE(flattenUniformArrays); + MERGE_TRUE(useUnknownFormat); + MERGE_TRUE(hlslOffsets); + MERGE_TRUE(useStorageBuffer); + MERGE_TRUE(invariantAll); + MERGE_TRUE(hlslIoMapping); + + // TODO: sourceFile + // TODO: sourceText + // TODO: processes + + MERGE_TRUE(needToLegalize); + MERGE_TRUE(binaryDoubleOutput); + MERGE_TRUE(usePhysicalStorageBuffer); +} + +// +// Merge the 'unit' AST into 'this' AST. +// That includes rationalizing the unique IDs, which were set up independently, +// and might have overlaps that are not the same symbol, or might have different +// IDs for what should be the same shared symbol. +// +void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.treeRoot == nullptr) + return; + + if (treeRoot == nullptr) { + treeRoot = unit.treeRoot; + return; + } + + // Getting this far means we have two existing trees to merge... + numShaderRecordBlocks += unit.numShaderRecordBlocks; + numTaskNVBlocks += unit.numTaskNVBlocks; + + // Get the top-level globals of each unit + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence(); + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + const TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // Map by global name to unique ID to rationalize the same object having + // differing IDs in different trees. + TIdMaps idMaps; + long long idShift; + seedIdMap(idMaps, idShift); + remapIds(idMaps, idShift + 1, unit); + + mergeBodies(infoSink, globals, unitGlobals); + bool mergeExistingOnly = false; + mergeGlobalUniformBlocks(infoSink, unit, mergeExistingOnly); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); + ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end()); +} + +static const TString& getNameForIdMap(TIntermSymbol* symbol) +{ + TShaderInterface si = symbol->getType().getShaderInterface(); + if (si == EsiNone) + return symbol->getName(); + else + return symbol->getType().getTypeName(); +} + + + +// Traverser that seeds an ID map with all built-ins, and tracks the +// maximum ID used, currently using (maximum ID + 1) as new symbol id shift seed. +// Level id will keep same after shifting. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TBuiltInIdTraverser : public TIntermTraverser { +public: + TBuiltInIdTraverser(TIdMaps& idMaps) : idMaps(idMaps), idShift(0) { } + // If it's a built in, add it to the map. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + idShift = (symbol->getId() & ~TSymbolTable::uniqueIdMask) | + std::max(idShift & TSymbolTable::uniqueIdMask, + symbol->getId() & TSymbolTable::uniqueIdMask); + } + long long getIdShift() const { return idShift; } +protected: + TBuiltInIdTraverser(TBuiltInIdTraverser&); + TBuiltInIdTraverser& operator=(TBuiltInIdTraverser&); + TIdMaps& idMaps; + long long idShift; +}; + +// Traverser that seeds an ID map with non-builtins. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TUserIdTraverser : public TIntermTraverser { +public: + TUserIdTraverser(TIdMaps& idMaps) : idMaps(idMaps) { } + // If its a non-built-in global, add it to the map. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn == EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + } + +protected: + TUserIdTraverser(TUserIdTraverser&); + TUserIdTraverser& operator=(TUserIdTraverser&); + TIdMaps& idMaps; // over biggest id +}; + +// Initialize the the ID map with what we know of 'this' AST. +void TIntermediate::seedIdMap(TIdMaps& idMaps, long long& idShift) +{ + // all built-ins everywhere need to align on IDs and contribute to the max ID + TBuiltInIdTraverser builtInIdTraverser(idMaps); + treeRoot->traverse(&builtInIdTraverser); + idShift = builtInIdTraverser.getIdShift() & TSymbolTable::uniqueIdMask; + + // user variables in the linker object list need to align on ids + TUserIdTraverser userIdTraverser(idMaps); + findLinkerObjects()->traverse(&userIdTraverser); +} + +// Traverser to map an AST ID to what was known from the seeding AST. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TRemapIdTraverser : public TIntermTraverser { +public: + TRemapIdTraverser(const TIdMaps& idMaps, long long idShift) : idMaps(idMaps), idShift(idShift) { } + // Do the mapping: + // - if the same symbol, adopt the 'this' ID + // - otherwise, ensure a unique ID by shifting to a new space + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + bool remapped = false; + if (qualifier.isLinkable() || qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + auto it = idMaps[si].find(getNameForIdMap(symbol)); + if (it != idMaps[si].end()) { + uint64_t id = (symbol->getId() & ~TSymbolTable::uniqueIdMask) | + (it->second & TSymbolTable::uniqueIdMask); + symbol->changeId(id); + remapped = true; + } + } + if (!remapped) + symbol->changeId(symbol->getId() + idShift); + } +protected: + TRemapIdTraverser(TRemapIdTraverser&); + TRemapIdTraverser& operator=(TRemapIdTraverser&); + const TIdMaps& idMaps; + long long idShift; +}; + +void TIntermediate::remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate& unit) +{ + // Remap all IDs to either share or be unique, as dictated by the idMap and idShift. + TRemapIdTraverser idTraverser(idMaps, idShift); + unit.getTreeRoot()->traverse(&idTraverser); +} + +// +// Merge the function bodies and global-level initializers from unitGlobals into globals. +// Will error check duplication of function bodies for the same signature. +// +void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals) +{ + // TODO: link-time performance: Processing in alphabetical order will be faster + + // Error check the global objects, not including the linker objects + for (unsigned int child = 0; child < globals.size() - 1; ++child) { + for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) { + TIntermAggregate* body = globals[child]->getAsAggregate(); + TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate(); + if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) { + error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:"); + infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n"; + } + } + } + + // Merge the global objects, just in front of the linker objects + globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1); +} + +// +// Global Unfiform block stores any default uniforms (i.e. uniforms without a block) +// If two linked stages declare the same member, they are meant to be the same uniform +// and need to be in the same block +// merge the members of different stages to allow them to be linked properly +// as a single block +// +void TIntermediate::mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly) +{ + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // build lists of default blocks from the intermediates + TIntermSequence defaultBlocks; + TIntermSequence unitDefaultBlocks; + + auto filter = [](TIntermSequence& list, TIntermNode* node) { + if (node->getAsSymbolNode()->getQualifier().defaultBlock) { + list.push_back(node); + } + }; + + std::for_each(linkerObjects.begin(), linkerObjects.end(), + [&defaultBlocks, &filter](TIntermNode* node) { + filter(defaultBlocks, node); + }); + std::for_each(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [&unitDefaultBlocks, &filter](TIntermNode* node) { + filter(unitDefaultBlocks, node); + }); + + auto itUnitBlock = unitDefaultBlocks.begin(); + for (; itUnitBlock != unitDefaultBlocks.end(); itUnitBlock++) { + + bool add = !mergeExistingOnly; + auto itBlock = defaultBlocks.begin(); + + for (; itBlock != defaultBlocks.end(); itBlock++) { + TIntermSymbol* block = (*itBlock)->getAsSymbolNode(); + TIntermSymbol* unitBlock = (*itUnitBlock)->getAsSymbolNode(); + + assert(block && unitBlock); + + // if the two default blocks match, then merge their definitions + if (block->getType().getTypeName() == unitBlock->getType().getTypeName() && + block->getQualifier().storage == unitBlock->getQualifier().storage) { + add = false; + mergeBlockDefinitions(infoSink, block, unitBlock, &unit); + } + } + if (add) { + // push back on original list; won't change the size of the list we're iterating over + linkerObjects.push_back(*itUnitBlock); + } + } +} + +void TIntermediate::mergeBlockDefinitions(TInfoSink& infoSink, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unit) { + + if (block->getType().getTypeName() != unitBlock->getType().getTypeName() || + block->getType().getBasicType() != unitBlock->getType().getBasicType() || + block->getQualifier().storage != unitBlock->getQualifier().storage || + block->getQualifier().layoutSet != unitBlock->getQualifier().layoutSet) { + // different block names likely means different blocks + return; + } + + // merge the struct + // order of declarations doesn't matter and they matched based on member name + TTypeList* memberList = block->getType().getWritableStruct(); + TTypeList* unitMemberList = unitBlock->getType().getWritableStruct(); + + // keep track of which members have changed position + // so we don't have to search the array again + std::map memberIndexUpdates; + + size_t memberListStartSize = memberList->size(); + for (unsigned int i = 0; i < unitMemberList->size(); ++i) { + bool merge = true; + for (unsigned int j = 0; j < memberListStartSize; ++j) { + if ((*memberList)[j].type->getFieldName() == (*unitMemberList)[i].type->getFieldName()) { + merge = false; + const TType* memberType = (*memberList)[j].type; + const TType* unitMemberType = (*unitMemberList)[i].type; + + // compare types + // don't need as many checks as when merging symbols, since + // initializers and most qualifiers are stripped when the member is moved into the block + if ((*memberType) != (*unitMemberType)) { + error(infoSink, "Types must match:", unitBlock->getStage()); + infoSink.info << " " << memberType->getFieldName() << ": "; + infoSink.info << "\"" << memberType->getCompleteString() << "\" in stage " << StageName(block->getStage()) << " versus "; + infoSink.info << "\"" << unitMemberType->getCompleteString() << "\" in stage " << StageName(unitBlock->getStage()) << "\n"; + } + + memberIndexUpdates[i] = j; + } + } + if (merge) { + memberList->push_back((*unitMemberList)[i]); + memberIndexUpdates[i] = (unsigned int)memberList->size() - 1; + } + } + + // update symbol node in unit tree, + // and other nodes that may reference it + class TMergeBlockTraverser : public TIntermTraverser { + public: + TMergeBlockTraverser(const TIntermSymbol* newSym) + : newSymbol(newSym), newType(nullptr), unit(nullptr), memberIndexUpdates(nullptr) + { + } + TMergeBlockTraverser(const TIntermSymbol* newSym, const glslang::TType* unitType, glslang::TIntermediate* unit, + const std::map* memberIdxUpdates) + : TIntermTraverser(false, true), newSymbol(newSym), newType(unitType), unit(unit), memberIndexUpdates(memberIdxUpdates) + { + } + virtual ~TMergeBlockTraverser() {} + + const TIntermSymbol* newSymbol; + const glslang::TType* newType; // shallow copy of the new type + glslang::TIntermediate* unit; // intermediate that is being updated + const std::map* memberIndexUpdates; + + virtual void visitSymbol(TIntermSymbol* symbol) + { + if (newSymbol->getAccessName() == symbol->getAccessName() && + newSymbol->getQualifier().getBlockStorage() == symbol->getQualifier().getBlockStorage()) { + // Each symbol node may have a local copy of the block structure. + // Update those structures to match the new one post-merge + *(symbol->getWritableType().getWritableStruct()) = *(newSymbol->getType().getStruct()); + } + } + + virtual bool visitBinary(TVisit, glslang::TIntermBinary* node) + { + if (!unit || !newType || !memberIndexUpdates || memberIndexUpdates->empty()) + return true; + + if (node->getOp() == EOpIndexDirectStruct && node->getLeft()->getType() == *newType) { + // this is a dereference to a member of the block since the + // member list changed, need to update this to point to the + // right index + assert(node->getRight()->getAsConstantUnion()); + + glslang::TIntermConstantUnion* constNode = node->getRight()->getAsConstantUnion(); + unsigned int memberIdx = constNode->getConstArray()[0].getUConst(); + unsigned int newIdx = memberIndexUpdates->at(memberIdx); + TIntermTyped* newConstNode = unit->addConstantUnion(newIdx, node->getRight()->getLoc()); + + node->setRight(newConstNode); + delete constNode; + + return true; + } + return true; + } + }; + + // 'this' may have symbols that are using the old block structure, so traverse the tree to update those + // in 'visitSymbol' + TMergeBlockTraverser finalLinkTraverser(block); + getTreeRoot()->traverse(&finalLinkTraverser); + + // The 'unit' intermediate needs the block structures update, but also structure entry indices + // may have changed from the old block to the new one that it was merged into, so update those + // in 'visitBinary' + TType newType; + newType.shallowCopy(block->getType()); + TMergeBlockTraverser unitFinalLinkTraverser(block, &newType, unit, &memberIndexUpdates); + unit->getTreeRoot()->traverse(&unitFinalLinkTraverser); + + // update the member list + (*unitMemberList) = (*memberList); +} + +// +// Merge the linker objects from unitLinkerObjects into linkerObjects. +// Duplication is expected and filtered out, but contradictions are an error. +// +void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage unitStage) +{ + // Error check and merge the linker objects (duplicates should not be created) + std::size_t initialNumLinkerObjects = linkerObjects.size(); + for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { + bool merge = true; + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + if (isSameSymbol(symbol, unitSymbol)) { + // filter out copy + merge = false; + + // but if one has an initializer and the other does not, update + // the initializer + if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty()) + symbol->setConstArray(unitSymbol->getConstArray()); + + // Similarly for binding + if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) + symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; + + // Similarly for location + if (!symbol->getQualifier().hasLocation() && unitSymbol->getQualifier().hasLocation()) { + symbol->getQualifier().layoutLocation = unitSymbol->getQualifier().layoutLocation; + } + + // Update implicit array sizes + if (symbol->getWritableType().isImplicitlySizedArray() && unitSymbol->getType().isImplicitlySizedArray()) { + if (unitSymbol->getType().getImplicitArraySize() > symbol->getType().getImplicitArraySize()){ + symbol->getWritableType().updateImplicitArraySize(unitSymbol->getType().getImplicitArraySize()); + } + } + else if (symbol->getWritableType().isImplicitlySizedArray() && unitSymbol->getType().isSizedArray()) { + if (symbol->getWritableType().getImplicitArraySize() > unitSymbol->getType().getOuterArraySize()) + error(infoSink, "Implicit size of unsized array doesn't match same symbol among multiple shaders.", unitStage); + } + else if (unitSymbol->getType().isImplicitlySizedArray() && symbol->getWritableType().isSizedArray()) { + if (unitSymbol->getType().getImplicitArraySize() > symbol->getWritableType().getOuterArraySize()) + error(infoSink, "Implicit size of unsized array doesn't match same symbol among multiple shaders.", unitStage); + } + + if (symbol->getType().isStruct() && unitSymbol->getType().isStruct() && + symbol->getType().getStruct()->size() == unitSymbol->getType().getStruct()->size()) { + for (int i = 0; i < (int)symbol->getType().getStruct()->size(); ++i) { + auto& type = (*symbol->getWritableType().getStruct())[i]; + auto& unitType = (*unitSymbol->getWritableType().getStruct())[i]; + + if (type.type->isImplicitlySizedArray() && unitType.type->isImplicitlySizedArray()) { + if (unitType.type->getImplicitArraySize() > type.type->getImplicitArraySize()) + type.type->updateImplicitArraySize(unitType.type->getImplicitArraySize()); + } + else if (type.type->isImplicitlySizedArray() && unitType.type->isSizedArray()) { + if (type.type->getImplicitArraySize() > unitType.type->getOuterArraySize()) + error(infoSink, "Implicit size of unsized array doesn't match same symbol among multiple shaders.", unitStage); + } + else if (type.type->isSizedArray() && unitType.type->isImplicitlySizedArray()) { + if (type.type->getOuterArraySize() < unitType.type->getImplicitArraySize()) + error(infoSink, "Implicit size of unsized array doesn't match same symbol among multiple shaders.", unitStage); + } + } + } + + // Update implicit array sizes + mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); + + // Check for consistent types/qualification/initializers etc. + mergeErrorCheck(infoSink, *symbol, *unitSymbol); + } + // If different symbols, verify they arn't push_constant since there can only be one per stage + else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant() && getStage() == unitStage) + error(infoSink, "Only one push_constant block is allowed per stage"); + } + + // Check conflicts between preset primitives and sizes of I/O variables among multiple geometry shaders + if (language == EShLangGeometry && unitStage == EShLangGeometry) + { + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + if (unitSymbol->isArray() && unitSymbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().builtIn == EbvNone) + if ((unitSymbol->getArraySizes()->isImplicitlySized() && + unitSymbol->getArraySizes()->getImplicitSize() != TQualifier::mapGeometryToSize(getInputPrimitive())) || + (! unitSymbol->getArraySizes()->isImplicitlySized() && + unitSymbol->getArraySizes()->getDimSize(0) != TQualifier::mapGeometryToSize(getInputPrimitive()))) + error(infoSink, "Not all array sizes match across all geometry shaders in the program"); + } + + if (merge) { + linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); + + // for anonymous blocks, check that their members don't conflict with other names + if (unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getBasicType() == EbtBlock && + IsAnonymous(unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getName())) { + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + auto checkName = [this, unitSymbol, &infoSink](const TString& name) { + for (unsigned int i = 0; i < unitSymbol->getType().getStruct()->size(); ++i) { + if (name == (*unitSymbol->getType().getStruct())[i].type->getFieldName() + && !((*unitSymbol->getType().getStruct())[i].type->getQualifier().hasLocation() + || unitSymbol->getType().getQualifier().hasLocation()) + ) { + error(infoSink, "Anonymous member name used for global variable or other anonymous member: "); + infoSink.info << (*unitSymbol->getType().getStruct())[i].type->getCompleteString() << "\n"; + } + } + }; + + if (isSameInterface(symbol, unitSymbol)) { + checkName(symbol->getName()); + + // check members of other anonymous blocks + if (symbol->getBasicType() == EbtBlock && IsAnonymous(symbol->getName())) { + for (unsigned int i = 0; i < symbol->getType().getStruct()->size(); ++i) { + checkName((*symbol->getType().getStruct())[i].type->getFieldName()); + } + } + } + } + } + } + } +} + +// TODO 4.5 link functionality: cull distance array size checking + +// Recursively merge the implicit array sizes through the objects' respective type trees. +void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) +{ + if (type.isUnsizedArray()) { + if (unitType.isUnsizedArray()) { + type.updateImplicitArraySize(unitType.getImplicitArraySize()); + if (unitType.isArrayVariablyIndexed()) + type.setArrayVariablyIndexed(); + } else if (unitType.isSizedArray()) + type.changeOuterArraySize(unitType.getOuterArraySize()); + } + + // Type mismatches are caught and reported after this, just be careful for now. + if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) + return; + + for (int i = 0; i < (int)type.getStruct()->size(); ++i) + mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); +} + +// +// Compare two global objects from two compilation units and see if they match +// well enough. Rules can be different for intra- vs. cross-stage matching. +// +// This function only does one of intra- or cross-stage matching per call. +// +void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol) +{ + EShLanguage stage = symbol.getStage(); + EShLanguage unitStage = unitSymbol.getStage(); + bool crossStage = stage != unitStage; + bool writeTypeComparison = false; + bool errorReported = false; + bool printQualifiers = false; + bool printPrecision = false; + bool printType = false; + + // Types have to match + { + // but, we make an exception if one is an implicit array and the other is sized + // or if the array sizes differ because of the extra array dimension on some in/out boundaries + bool arraysMatch = false; + if (isIoResizeArray(symbol.getType(), stage) || isIoResizeArray(unitSymbol.getType(), unitStage)) { + // if the arrays have an extra dimension because of the stage. + // compare dimensions while ignoring the outer dimension + unsigned int firstDim = isIoResizeArray(symbol.getType(), stage) ? 1 : 0; + unsigned int numDim = symbol.getArraySizes() + ? symbol.getArraySizes()->getNumDims() : 0; + unsigned int unitFirstDim = isIoResizeArray(unitSymbol.getType(), unitStage) ? 1 : 0; + unsigned int unitNumDim = unitSymbol.getArraySizes() + ? unitSymbol.getArraySizes()->getNumDims() : 0; + arraysMatch = (numDim - firstDim) == (unitNumDim - unitFirstDim); + // check that array sizes match as well + for (unsigned int i = 0; i < (numDim - firstDim) && arraysMatch; i++) { + if (symbol.getArraySizes()->getDimSize(firstDim + i) != + unitSymbol.getArraySizes()->getDimSize(unitFirstDim + i)) { + arraysMatch = false; + break; + } + } + } + else { + arraysMatch = symbol.getType().sameArrayness(unitSymbol.getType()) || + (symbol.getType().isArray() && unitSymbol.getType().isArray() && + (symbol.getType().isImplicitlySizedArray() || unitSymbol.getType().isImplicitlySizedArray() || + symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray())); + } + + int lpidx = -1; + int rpidx = -1; + if (!symbol.getType().sameElementType(unitSymbol.getType(), &lpidx, &rpidx)) { + if (lpidx >= 0 && rpidx >= 0) { + error(infoSink, "Member names and types must match:", unitStage); + infoSink.info << " Block: " << symbol.getType().getTypeName() << "\n"; + infoSink.info << " " << StageName(stage) << " stage: \"" + << (*symbol.getType().getStruct())[lpidx].type->getCompleteString(true, false, false, true, + (*symbol.getType().getStruct())[lpidx].type->getFieldName()) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: \"" + << (*unitSymbol.getType().getStruct())[rpidx].type->getCompleteString(true, false, false, true, + (*unitSymbol.getType().getStruct())[rpidx].type->getFieldName()) << "\"\n"; + errorReported = true; + } else if (lpidx >= 0 && rpidx == -1) { + TString errmsg = StageName(stage); + errmsg.append(" block member has no corresponding member in ").append(StageName(unitStage)).append(" block:"); + error(infoSink, errmsg.c_str(), unitStage); + infoSink.info << " " << StageName(stage) << " stage: Block: " << symbol.getType().getTypeName() << ", Member: " + << (*symbol.getType().getStruct())[lpidx].type->getFieldName() << "\n"; + infoSink.info << " " << StageName(unitStage) << " stage: Block: " << unitSymbol.getType().getTypeName() << ", Member: n/a \n"; + errorReported = true; + } else if (lpidx == -1 && rpidx >= 0) { + TString errmsg = StageName(unitStage); + errmsg.append(" block member has no corresponding member in ").append(StageName(stage)).append(" block:"); + error(infoSink, errmsg.c_str(), unitStage); + infoSink.info << " " << StageName(unitStage) << " stage: Block: " << unitSymbol.getType().getTypeName() << ", Member: " + << (*unitSymbol.getType().getStruct())[rpidx].type->getFieldName() << "\n"; + infoSink.info << " " << StageName(stage) << " stage: Block: " << symbol.getType().getTypeName() << ", Member: n/a \n"; + errorReported = true; + } else { + error(infoSink, "Types must match:", unitStage); + writeTypeComparison = true; + printType = true; + } + } else if (!arraysMatch) { + error(infoSink, "Array sizes must be compatible:", unitStage); + writeTypeComparison = true; + printType = true; + } else if (!symbol.getType().sameTypeParameters(unitSymbol.getType())) { + error(infoSink, "Type parameters must match:", unitStage); + writeTypeComparison = true; + printType = true; + } + } + + // Interface block member-wise layout qualifiers have to match + if (symbol.getType().getBasicType() == EbtBlock && unitSymbol.getType().getBasicType() == EbtBlock && + symbol.getType().getStruct() && unitSymbol.getType().getStruct() && + symbol.getType().sameStructType(unitSymbol.getType())) { + unsigned int li = 0; + unsigned int ri = 0; + while (li < symbol.getType().getStruct()->size() && ri < unitSymbol.getType().getStruct()->size()) { + if ((*symbol.getType().getStruct())[li].type->hiddenMember()) { + ++li; + continue; + } + if ((*unitSymbol.getType().getStruct())[ri].type->hiddenMember()) { + ++ri; + continue; + } + const TQualifier& qualifier = (*symbol.getType().getStruct())[li].type->getQualifier(); + const TQualifier & unitQualifier = (*unitSymbol.getType().getStruct())[ri].type->getQualifier(); + bool layoutQualifierError = false; + if (qualifier.layoutMatrix != unitQualifier.layoutMatrix) { + error(infoSink, "Interface block member layout matrix qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (qualifier.layoutOffset != unitQualifier.layoutOffset) { + error(infoSink, "Interface block member layout offset qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (qualifier.layoutAlign != unitQualifier.layoutAlign) { + error(infoSink, "Interface block member layout align qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (qualifier.layoutLocation != unitQualifier.layoutLocation) { + error(infoSink, "Interface block member layout location qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (qualifier.layoutComponent != unitQualifier.layoutComponent) { + error(infoSink, "Interface block member layout component qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (layoutQualifierError) { + infoSink.info << " " << StageName(stage) << " stage: Block: " << symbol.getType().getTypeName() << ", Member: " + << (*symbol.getType().getStruct())[li].type->getFieldName() << " \"" + << (*symbol.getType().getStruct())[li].type->getCompleteString(true, true, false, false) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: Block: " << unitSymbol.getType().getTypeName() << ", Member: " + << (*unitSymbol.getType().getStruct())[ri].type->getFieldName() << " \"" + << (*unitSymbol.getType().getStruct())[ri].type->getCompleteString(true, true, false, false) << "\"\n"; + errorReported = true; + } + ++li; + ++ri; + } + } + + bool isInOut = crossStage && + ((symbol.getQualifier().storage == EvqVaryingIn && unitSymbol.getQualifier().storage == EvqVaryingOut) || + (symbol.getQualifier().storage == EvqVaryingOut && unitSymbol.getQualifier().storage == EvqVaryingIn)); + + // Qualifiers have to (almost) match + // Storage... + if (!isInOut && symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { + error(infoSink, "Storage qualifiers must match:", unitStage); + writeTypeComparison = true; + printQualifiers = true; + } + + // Uniform and buffer blocks must either both have an instance name, or + // must both be anonymous. The names don't need to match though. + if (symbol.getQualifier().isUniformOrBuffer() && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()))) { + error(infoSink, "Matched Uniform or Storage blocks must all be anonymous," + " or all be named:", unitStage); + writeTypeComparison = true; + } + + if (symbol.getQualifier().storage == unitSymbol.getQualifier().storage && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()) || + (!IsAnonymous(symbol.getName()) && symbol.getName() != unitSymbol.getName()))) { + warn(infoSink, "Matched shader interfaces are using different instance names.", unitStage); + writeTypeComparison = true; + } + + // Precision... + if (!isInOut && symbol.getQualifier().precision != unitSymbol.getQualifier().precision) { + error(infoSink, "Precision qualifiers must match:", unitStage); + writeTypeComparison = true; + printPrecision = true; + } + + // Invariance... + if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) { + error(infoSink, "Presence of invariant qualifier must match:", unitStage); + writeTypeComparison = true; + printQualifiers = true; + } + + // Precise... + if (! crossStage && symbol.getQualifier().isNoContraction() != unitSymbol.getQualifier().isNoContraction()) { + error(infoSink, "Presence of precise qualifier must match:", unitStage); + writeTypeComparison = true; + printPrecision = true; + } + + // Auxiliary and interpolation... + // "interpolation qualification (e.g., flat) and auxiliary qualification (e.g. centroid) may differ. + // These mismatches are allowed between any pair of stages ... + // those provided in the fragment shader supersede those provided in previous stages." + if (!crossStage && + (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || + symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || + symbol.getQualifier().flat != unitSymbol.getQualifier().flat || + symbol.getQualifier().isSample()!= unitSymbol.getQualifier().isSample() || + symbol.getQualifier().isPatch() != unitSymbol.getQualifier().isPatch() || + symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective())) { + error(infoSink, "Interpolation and auxiliary storage qualifiers must match:", unitStage); + writeTypeComparison = true; + printQualifiers = true; + } + + // Memory... + bool memoryQualifierError = false; + if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent) { + error(infoSink, "Memory coherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().devicecoherent != unitSymbol.getQualifier().devicecoherent) { + error(infoSink, "Memory devicecoherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().queuefamilycoherent != unitSymbol.getQualifier().queuefamilycoherent) { + error(infoSink, "Memory queuefamilycoherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().workgroupcoherent != unitSymbol.getQualifier().workgroupcoherent) { + error(infoSink, "Memory workgroupcoherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().subgroupcoherent != unitSymbol.getQualifier().subgroupcoherent) { + error(infoSink, "Memory subgroupcoherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().shadercallcoherent != unitSymbol.getQualifier().shadercallcoherent) { + error(infoSink, "Memory shadercallcoherent qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().nonprivate != unitSymbol.getQualifier().nonprivate) { + error(infoSink, "Memory nonprivate qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil) { + error(infoSink, "Memory volatil qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().nontemporal != unitSymbol.getQualifier().nontemporal) { + error(infoSink, "Memory nontemporal qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict) { + error(infoSink, "Memory restrict qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly) { + error(infoSink, "Memory readonly qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) { + error(infoSink, "Memory writeonly qualifier must match:", unitStage); + memoryQualifierError = true; + } + if (memoryQualifierError) { + writeTypeComparison = true; + printQualifiers = true; + } + + // Layouts... + // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec + // requires separate user-supplied offset from actual computed offset, but + // current implementation only has one offset. + bool layoutQualifierError = false; + if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix) { + error(infoSink, "Layout matrix qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking) { + error(infoSink, "Layout packing qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().hasLocation() && unitSymbol.getQualifier().hasLocation() && symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation) { + error(infoSink, "Layout location qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent) { + error(infoSink, "Layout component qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex) { + error(infoSink, "Layout index qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().hasBinding() && unitSymbol.getQualifier().hasBinding() && symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding) { + error(infoSink, "Layout binding qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset)) { + error(infoSink, "Layout offset qualifier must match:", unitStage); + layoutQualifierError = true; + } + if (layoutQualifierError) { + writeTypeComparison = true; + printQualifiers = true; + } + + // Initializers have to match, if both are present, and if we don't already know the types don't match + if (! writeTypeComparison && ! errorReported) { + if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) { + if (symbol.getConstArray() != unitSymbol.getConstArray()) { + error(infoSink, "Initializers must match:", unitStage); + infoSink.info << " " << symbol.getName() << "\n"; + } + } + } + + if (writeTypeComparison) { + if (symbol.getType().getBasicType() == EbtBlock && unitSymbol.getType().getBasicType() == EbtBlock && + symbol.getType().getStruct() && unitSymbol.getType().getStruct()) { + if (printType) { + infoSink.info << " " << StageName(stage) << " stage: \"" << symbol.getType().getCompleteString(true, printQualifiers, printPrecision, + printType, symbol.getName(), symbol.getType().getTypeName()) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: \"" << unitSymbol.getType().getCompleteString(true, printQualifiers, printPrecision, + printType, unitSymbol.getName(), unitSymbol.getType().getTypeName()) << "\"\n"; + } else { + infoSink.info << " " << StageName(stage) << " stage: Block: " << symbol.getType().getTypeName() << " Instance: " << symbol.getName() + << ": \"" << symbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: Block: " << unitSymbol.getType().getTypeName() << " Instance: " << unitSymbol.getName() + << ": \"" << unitSymbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType) << "\"\n"; + } + } else { + if (printType) { + infoSink.info << " " << StageName(stage) << " stage: \"" + << symbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType, symbol.getName()) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: \"" + << unitSymbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType, unitSymbol.getName()) << "\"\n"; + } else { + infoSink.info << " " << StageName(stage) << " stage: " << symbol.getName() << " \"" + << symbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType) << "\"\n"; + infoSink.info << " " << StageName(unitStage) << " stage: " << unitSymbol.getName() << " \"" + << unitSymbol.getType().getCompleteString(true, printQualifiers, printPrecision, printType) << "\"\n"; + } + } + } +} + +void TIntermediate::sharedBlockCheck(TInfoSink& infoSink) +{ + bool has_shared_block = false; + bool has_shared_non_block = false; + TIntermSequence& linkObjects = findLinkerObjects()->getSequence(); + for (size_t i = 0; i < linkObjects.size(); ++i) { + const TType& type = linkObjects[i]->getAsTyped()->getType(); + const TQualifier& qualifier = type.getQualifier(); + if (qualifier.storage == glslang::EvqShared) { + if (type.getBasicType() == glslang::EbtBlock) + has_shared_block = true; + else + has_shared_non_block = true; + } + } + if (has_shared_block && has_shared_non_block) + error(infoSink, "cannot mix use of shared variables inside and outside blocks"); +} + +// +// Do final link-time error checking of a complete (merged) intermediate representation. +// (Much error checking was done during merging). +// +// Also, lock in defaults of things not set. +// Defer adopting implicit array sizes to later, after all stages are merged. +// +void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) +{ + if (getTreeRoot() == nullptr) + return; + + if (numEntryPoints < 1) { + if (getSource() == EShSourceGlsl) + error(infoSink, "Missing entry point: Each stage requires one entry point"); + else + warn(infoSink, "Entry point not found"); + } + + // recursion and missing body checking + checkCallGraphCycles(infoSink); + checkCallGraphBodies(infoSink, keepUncalled); + + // overlap/alias/missing I/O, etc. + inOutLocationCheck(infoSink); + + if (getNumPushConstants() > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + + // invocations + if (invocations == TQualifier::layoutNotSet) + invocations = 1; + + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_CullDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + + if (userOutputUsed() && (inIoAccessed("gl_FragColor") || inIoAccessed("gl_FragData"))) + error(infoSink, "Cannot use gl_FragColor or gl_FragData when using user-defined outputs"); + if (inIoAccessed("gl_FragColor") && inIoAccessed("gl_FragData")) + error(infoSink, "Cannot use both gl_FragColor and gl_FragData"); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].contains64BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 8); + else if (xfbBuffers[b].contains32BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 4); + else if (xfbBuffers[b].contains16BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 2); + + // "It is a compile-time or link-time error to have + // any xfb_offset that overflows xfb_stride, whether stated on declarations before or after the xfb_stride, or + // in different compilation units. While xfb_stride can be declared multiple times for the same buffer, it is a + // compile-time or link-time error to have different values specified for the stride for the same buffer." + if (xfbBuffers[b].stride != TQualifier::layoutXfbStrideEnd && xfbBuffers[b].implicitStride > xfbBuffers[b].stride) { + error(infoSink, "xfb_stride is too small to hold all buffer entries:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << ", minimum stride needed: " << xfbBuffers[b].implicitStride << "\n"; + } + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = xfbBuffers[b].implicitStride; + + // "If the buffer is capturing any + // outputs with double-precision or 64-bit integer components, the stride must be a multiple of 8, otherwise it must be a + // multiple of 4, or a compile-time or link-time error results." + if (xfbBuffers[b].contains64BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 8)) { + error(infoSink, "xfb_stride must be multiple of 8 for buffer holding a double or 64-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } else if (xfbBuffers[b].contains32BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) { + error(infoSink, "xfb_stride must be multiple of 4:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + // "If the buffer is capturing any + // outputs with half-precision or 16-bit integer components, the stride must be a multiple of 2" + else if (xfbBuffers[b].contains16BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 2)) { + error(infoSink, "xfb_stride must be multiple of 2 for buffer holding a half float or 16-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (xfbBuffers[b].stride > (unsigned int)(4 * resources->maxTransformFeedbackInterleavedComponents)) { + error(infoSink, "xfb_stride is too large:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources->maxTransformFeedbackInterleavedComponents << "\n"; + } + } + + switch (language) { + case EShLangVertex: + break; + case EShLangTessControl: + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify an output layout(vertices=...)"); + break; + case EShLangTessEvaluation: + if (getSource() == EShSourceGlsl) { + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (vertexSpacing == EvsNone) + vertexSpacing = EvsEqual; + if (vertexOrder == EvoNone) + vertexOrder = EvoCcw; + } + break; + case EShLangGeometry: + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + break; + case EShLangFragment: + // for GL_ARB_post_depth_coverage, EarlyFragmentTest is set automatically in + // ParseHelper.cpp. So if we reach here, this must be GL_EXT_post_depth_coverage + // requiring explicit early_fragment_tests + if (getPostDepthCoverage() && !getEarlyFragmentTests()) + error(infoSink, "post_depth_coverage requires early_fragment_tests"); + break; + case EShLangCompute: + sharedBlockCheck(infoSink); + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (numShaderRecordBlocks > 1) + error(infoSink, "Only one shaderRecordNV buffer block is allowed per stage"); + break; + case EShLangMesh: + // NV_mesh_shader doesn't allow use of both single-view and per-view builtins. + if (inIoAccessed("gl_Position") && inIoAccessed("gl_PositionPerViewNV")) + error(infoSink, "Can only use one of gl_Position or gl_PositionPerViewNV"); + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipDistancePerViewNV")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipDistancePerViewNV"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_CullDistancePerViewNV")) + error(infoSink, "Can only use one of gl_CullDistance or gl_CullDistancePerViewNV"); + if (inIoAccessed("gl_Layer") && inIoAccessed("gl_LayerPerViewNV")) + error(infoSink, "Can only use one of gl_Layer or gl_LayerPerViewNV"); + if (inIoAccessed("gl_ViewportMask") && inIoAccessed("gl_ViewportMaskPerViewNV")) + error(infoSink, "Can only use one of gl_ViewportMask or gl_ViewportMaskPerViewNV"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + if (primitives == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_primitives = value)"); + [[fallthrough]]; + case EShLangTask: + if (numTaskNVBlocks > 1) + error(infoSink, "Only one taskNV interface block is allowed per shader"); + if (numTaskEXTPayloads > 1) + error(infoSink, "Only single variable of type taskPayloadSharedEXT is allowed per shader"); + sharedBlockCheck(infoSink); + break; + default: + error(infoSink, "Unknown Stage."); + break; + } +} + +// +// See if the call graph contains any static recursion, which is disallowed +// by the specification. +// +void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->currentPath = false; + call->errorGiven = false; + } + + // + // Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration. + // + + TCall* newRoot; + do { + // See if we have unvisited parts of the graph. + newRoot = nullptr; + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (! call->visited) { + newRoot = &(*call); + break; + } + } + + // If not, we are done. + if (! newRoot) + break; + + // Otherwise, we found a new subgraph, process it: + // See what all can be reached by this new root, and if any of + // that is recursive. This is done by depth-first traversals, seeing + // if a new call is found that was already in the currentPath (a back edge), + // thereby detecting recursion. + std::list stack; + newRoot->currentPath = true; // currentPath will be true iff it is on the stack + stack.push_back(newRoot); + while (! stack.empty()) { + // get a caller + TCall* call = stack.back(); + + // Add to the stack just one callee. + // This algorithm always terminates, because only !visited and !currentPath causes a push + // and all pushes change currentPath to true, and all pops change visited to true. + TGraph::iterator child = callGraph.begin(); + for (; child != callGraph.end(); ++child) { + + // If we already visited this node, its whole subgraph has already been processed, so skip it. + if (child->visited) + continue; + + if (call->callee == child->caller) { + if (child->currentPath) { + // Then, we found a back edge + if (! child->errorGiven) { + error(infoSink, "Recursion detected:"); + infoSink.info << " " << call->callee << " calling " << child->callee << "\n"; + child->errorGiven = true; + recursive = true; + } + } else { + child->currentPath = true; + stack.push_back(&(*child)); + break; + } + } + } + if (child == callGraph.end()) { + // no more callees, we bottomed out, never look at this node again + stack.back()->currentPath = false; + stack.back()->visited = true; + stack.pop_back(); + } + } // end while, meaning nothing left to process in this subtree + + } while (newRoot); // redundant loop check; should always exit via the 'break' above +} + +// +// See which functions are reachable from the entry point and which have bodies. +// Reachable ones with missing bodies are errors. +// Unreachable bodies are dead code. +// +void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->calleeBodyPosition = -1; + } + + // The top level of the AST includes function definitions (bodies). + // Compare these to function calls in the call graph. + // We'll end up knowing which have bodies, and if so, + // how to map the call-graph node to the location in the AST. + TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence(); + std::vector reachable(functionSequence.size(), true); // so that non-functions are reachable + for (int f = 0; f < (int)functionSequence.size(); ++f) { + glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction)) { + if (node->getName().compare(getEntryPointMangledName().c_str()) != 0) + reachable[f] = false; // so that function bodies are unreachable, until proven otherwise + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->callee == node->getName()) + call->calleeBodyPosition = f; + } + } + } + + // Start call-graph traversal by visiting the entry point nodes. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller.compare(getEntryPointMangledName().c_str()) == 0) + call->visited = true; + } + + // Propagate 'visited' through the call-graph to every part of the graph it + // can reach (seeded with the entry-point setting above). + bool changed; + do { + changed = false; + for (auto call1 = callGraph.begin(); call1 != callGraph.end(); ++call1) { + if (call1->visited) { + for (TGraph::iterator call2 = callGraph.begin(); call2 != callGraph.end(); ++call2) { + if (! call2->visited) { + if (call1->callee == call2->caller) { + changed = true; + call2->visited = true; + } + } + } + } + } + } while (changed); + + // Any call-graph node set to visited but without a callee body is an error. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->visited) { + if (call->calleeBodyPosition == -1) { + error(infoSink, "No function definition (body) found: "); + infoSink.info << " " << call->callee << "\n"; + } else + reachable[call->calleeBodyPosition] = true; + } + } + + // Bodies in the AST not reached by the call graph are dead; + // clear them out, since they can't be reached and also can't + // be translated further due to possibility of being ill defined. + if (! keepUncalled) { + for (int f = 0; f < (int)functionSequence.size(); ++f) { + if (! reachable[f]) + { + resetTopLevelUncalledStatus(functionSequence[f]->getAsAggregate()->getName()); + functionSequence[f] = nullptr; + } + } + functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end()); + } +} + +// +// Satisfy rules for location qualifiers on inputs and outputs +// +void TIntermediate::inOutLocationCheck(TInfoSink& infoSink) +{ + // ES 3.0 requires all outputs to have location qualifiers if there is more than one output + bool fragOutWithNoLocation = false; + int numFragOut = 0; + + // TODO: linker functionality: location collision checking + + TIntermSequence& linkObjects = findLinkerObjects()->getSequence(); + for (size_t i = 0; i < linkObjects.size(); ++i) { + const TType& type = linkObjects[i]->getAsTyped()->getType(); + const TQualifier& qualifier = type.getQualifier(); + if (language == EShLangFragment) { + if (qualifier.storage == EvqVaryingOut && qualifier.builtIn == EbvNone) { + ++numFragOut; + if (!qualifier.hasAnyLocation()) + fragOutWithNoLocation = true; + } + } + } + + if (isEsProfile()) { + if (numFragOut > 1 && fragOutWithNoLocation) + error(infoSink, "when more than one fragment shader output, all must have location qualifiers"); + } +} + +TIntermAggregate* TIntermediate::findLinkerObjects() const +{ + // Get the top-level globals + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + + // Get the last member of the sequences, expected to be the linker-object lists + assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects); + + return globals.back()->getAsAggregate(); +} + +// See if a variable was both a user-declared output and used. +// Note: the spec discusses writing to one, but this looks at read or write, which +// is more useful, and perhaps the spec should be changed to reflect that. +bool TIntermediate::userOutputUsed() const +{ + const TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + + bool found = false; + for (size_t i = 0; i < linkerObjects.size(); ++i) { + const TIntermSymbol& symbolNode = *linkerObjects[i]->getAsSymbolNode(); + if (symbolNode.getQualifier().storage == EvqVaryingOut && + symbolNode.getName().compare(0, 3, "gl_") != 0 && + inIoAccessed(symbolNode.getName())) { + found = true; + break; + } + } + + return found; +} + +// Accumulate locations used for inputs, outputs, and uniforms, payload, callable data, and tileImageEXT +// and check for collisions as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +// typeCollision is set to true if there is no direct collision, but the types in the same location +// are different. +// +int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision) +{ + typeCollision = false; + + int set; + if (qualifier.isPipeInput()) + set = 0; + else if (qualifier.isPipeOutput()) + set = 1; + else if (qualifier.storage == EvqUniform) + set = 2; + else if (qualifier.storage == EvqBuffer) + set = 3; + else if (qualifier.storage == EvqTileImageEXT) + set = 4; + else if (qualifier.isAnyPayload()) + set = 0; + else if (qualifier.isAnyCallable()) + set = 1; + else if (qualifier.isHitObjectAttrNV()) + set = 2; + else + return -1; + + int size; + if (qualifier.isAnyPayload() || qualifier.isAnyCallable()) { + size = 1; + } else if (qualifier.isUniformOrBuffer() || qualifier.isTaskMemory()) { + if (type.isSizedArray()) + size = type.getCumulativeArraySize(); + else + size = 1; + } else { + // Strip off the outer array dimension for those having an extra one. + if (type.isArray() && qualifier.isArrayedIo(language)) { + TType elementType(type, 0); + size = computeTypeLocationSize(elementType, language); + } else + size = computeTypeLocationSize(type, language); + } + + // Locations, and components within locations. + // + // Almost always, dealing with components means a single location is involved. + // The exception is a dvec3. From the spec: + // + // "A dvec3 will consume all four components of the first location and components 0 and 1 of + // the second location. This leaves components 2 and 3 available for other component-qualified + // declarations." + // + // That means, without ever mentioning a component, a component range + // for a different location gets specified, if it's not a vertex shader input. (!) + // (A vertex shader input will show using only one location, even for a dvec3/4.) + // + // So, for the case of dvec3, we need two independent ioRanges. + // + // For raytracing IO (payloads and callabledata) each declaration occupies a single + // slot irrespective of type. + int collision = -1; // no collision + if (qualifier.isAnyPayload() || qualifier.isAnyCallable() || qualifier.isHitObjectAttrNV()) { + TRange range(qualifier.layoutLocation, qualifier.layoutLocation); + collision = checkLocationRT(set, qualifier.layoutLocation); + if (collision < 0) + usedIoRT[set].push_back(range); + return collision; + } + if (size == 2 && type.getBasicType() == EbtDouble && type.getVectorSize() == 3 && + (qualifier.isPipeInput() || qualifier.isPipeOutput())) { + // Dealing with dvec3 in/out split across two locations. + // Need two io-ranges. + // The case where the dvec3 doesn't start at component 0 was previously caught as overflow. + + // First range: + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation); + TRange componentRange(0, 3); + TIoRange range(locationRange, componentRange, type.getBasicType(), 0, qualifier.centroid, qualifier.smooth, qualifier.flat, qualifier.sample, qualifier.patch); + + // check for collisions + collision = checkLocationRange(set, range, type, typeCollision); + if (collision < 0) { + usedIo[set].push_back(range); + + // Second range: + TRange locationRange2(qualifier.layoutLocation + 1, qualifier.layoutLocation + 1); + TRange componentRange2(0, 1); + TIoRange range2(locationRange2, componentRange2, type.getBasicType(), 0, qualifier.centroid, qualifier.smooth, qualifier.flat, qualifier.sample, qualifier.patch); + + // check for collisions + collision = checkLocationRange(set, range2, type, typeCollision); + if (collision < 0) + usedIo[set].push_back(range2); + } + return collision; + } + + // Not a dvec3 in/out split across two locations, generic path. + // Need a single IO-range block. + + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation + size - 1); + TRange componentRange(0, 3); + if (qualifier.hasComponent() || type.getVectorSize() > 0) { + int consumedComponents = type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1); + if (qualifier.hasComponent()) + componentRange.start = qualifier.layoutComponent; + componentRange.last = componentRange.start + consumedComponents - 1; + } + + // combine location and component ranges + TBasicType basicTy = type.getBasicType(); + if (basicTy == EbtSampler && type.getSampler().isAttachmentEXT()) + basicTy = type.getSampler().type; + TIoRange range(locationRange, componentRange, basicTy, qualifier.hasIndex() ? qualifier.getIndex() : 0, qualifier.centroid, qualifier.smooth, qualifier.flat, qualifier.sample, qualifier.patch); + + // check for collisions, except for vertex inputs on desktop targeting OpenGL + if (! (!isEsProfile() && language == EShLangVertex && qualifier.isPipeInput()) || spvVersion.vulkan > 0) + collision = checkLocationRange(set, range, type, typeCollision); + + if (collision < 0) + usedIo[set].push_back(range); + + return collision; +} + +// Check that two types can be stored in different components in the same location. +// They must be the same type, except signed/unsigned integers are considered compatible. +static bool checkCompatibleTypes(TBasicType t1, TBasicType t2) { + if (t1 != t2) { + if ((t1 == EbtInt8 && t2 == EbtUint8) || + (t2 == EbtInt8 && t1 == EbtUint8) || + (t1 == EbtInt16 && t2 == EbtUint16) || + (t2 == EbtInt16 && t1 == EbtUint16)|| + (t1 == EbtInt && t2 == EbtUint) || + (t2 == EbtInt && t1 == EbtUint)|| + (t1 == EbtInt64 && t2 == EbtUint64) || + (t2 == EbtInt64 && t1 == EbtUint64)) { + return true; + } + } + return t1 == t2; +} + +// Compare a new (the passed in) 'range' against the existing set, and see +// if there are any collisions. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision) +{ + for (size_t r = 0; r < usedIo[set].size(); ++r) { + if (range.overlap(usedIo[set][r])) { + // there is a collision; pick one + return std::max(range.location.start, usedIo[set][r].location.start); + } else if (range.location.overlap(usedIo[set][r].location) && + (!checkCompatibleTypes(type.getBasicType(), usedIo[set][r].basicType) || + type.getQualifier().centroid != usedIo[set][r].centroid || + type.getQualifier().smooth != usedIo[set][r].smooth || + type.getQualifier().flat != usedIo[set][r].flat || + type.getQualifier().sample != usedIo[set][r].sample || + type.getQualifier().patch != usedIo[set][r].patch)) { + // aliased-type mismatch + typeCollision = true; + return std::max(range.location.start, usedIo[set][r].location.start); + } + } + + // check typeCollision between tileImageEXT and out + if (set == 4 || set == 1) { + // if the set is "tileImageEXT", check against "out" and vice versa + int againstSet = (set == 4) ? 1 : 4; + for (size_t r = 0; r < usedIo[againstSet].size(); ++r) { + if (range.location.overlap(usedIo[againstSet][r].location) && type.getBasicType() != usedIo[againstSet][r].basicType) { + // aliased-type mismatch + typeCollision = true; + return std::max(range.location.start, usedIo[againstSet][r].location.start); + } + } + } + + return -1; // no collision +} + +int TIntermediate::checkLocationRT(int set, int location) { + TRange range(location, location); + for (size_t r = 0; r < usedIoRT[set].size(); ++r) { + if (range.overlap(usedIoRT[set][r])) { + return range.start; + } + } + return -1; // no collision +} + +// Accumulate bindings and offsets, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) +{ + TRange bindingRange(binding, binding); + TRange offsetRange(offset, offset + numOffsets - 1); + TOffsetRange range(bindingRange, offsetRange); + + // check for collisions, except for vertex inputs on desktop + for (size_t r = 0; r < usedAtomics.size(); ++r) { + if (range.overlap(usedAtomics[r])) { + // there is a collision; pick one + return std::max(offset, usedAtomics[r].offset.start); + } + } + + usedAtomics.push_back(range); + + return -1; // no collision +} + +// Accumulate used constant_id values. +// +// Return false is one was already used. +bool TIntermediate::addUsedConstantId(int id) +{ + if (usedConstantId.find(id) != usedConstantId.end()) + return false; + + usedConstantId.insert(id); + + return true; +} + +// Recursively figure out how many locations are used up by an input or output type. +// Return the size of type, as measured by "locations". +int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage) +{ + // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n + // consecutive locations..." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + // TODO: are there valid cases of having an unsized array with a location? If so, running this code too early. + TType elementType(type, 0); + if (type.isSizedArray() && !type.getQualifier().isPerView()) + return type.getOuterArraySize() * computeTypeLocationSize(elementType, stage); + else { + // unset perViewNV attributes for arrayed per-view outputs: "perviewNV vec4 v[MAX_VIEWS][3];" + elementType.getQualifier().perViewNV = false; + return computeTypeLocationSize(elementType, stage); + } + } + + // "The locations consumed by block and structure members are determined by applying the rules above + // recursively..." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeLocationSize(memberType, stage); + } + return size; + } + + // ES: "If a shader input is any scalar or vector type, it will consume a single location." + + // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex + // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while + // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will + // consume only a single location, in all stages." + if (type.isScalar()) + return 1; + if (type.isVector()) { + if (stage == EShLangVertex && type.getQualifier().isPipeInput()) + return 1; + if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2) + return 2; + else + return 1; + } + + // "If the declared input is an n x m single- or double-precision matrix, ... + // The number of locations assigned for each matrix will be the same as + // for an n-element array of m-component vectors..." + if (type.isMatrix()) { + TType columnType(type, 0); + return type.getMatrixCols() * computeTypeLocationSize(columnType, stage); + } + + assert(0); + return 1; +} + +// Same as computeTypeLocationSize but for uniforms +int TIntermediate::computeTypeUniformLocationSize(const TType& type) +{ + // "Individual elements of a uniform array are assigned + // consecutive locations with the first element taking location + // location." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType elementType(type, 0); + if (type.isSizedArray()) { + return type.getOuterArraySize() * computeTypeUniformLocationSize(elementType); + } else { + // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early. + return computeTypeUniformLocationSize(elementType); + } + } + + // "Each subsequent inner-most member or element gets incremental + // locations for the entire structure or array." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeUniformLocationSize(memberType); + } + return size; + } + + return 1; +} + +// Accumulate xfb buffer ranges and check for collisions as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addXfbBufferOffset(const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + assert(qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()); + TXfbBuffer& buffer = xfbBuffers[qualifier.layoutXfbBuffer]; + + // compute the range + unsigned int size = computeTypeXfbSize(type, buffer.contains64BitType, buffer.contains32BitType, buffer.contains16BitType); + buffer.implicitStride = std::max(buffer.implicitStride, qualifier.layoutXfbOffset + size); + TRange range(qualifier.layoutXfbOffset, qualifier.layoutXfbOffset + size - 1); + + // check for collisions + for (size_t r = 0; r < buffer.ranges.size(); ++r) { + if (range.overlap(buffer.ranges[r])) { + // there is a collision; pick an example to return + return std::max(range.start, buffer.ranges[r].start); + } + } + + buffer.ranges.push_back(range); + + return -1; // no collision +} + +// Recursively figure out how many bytes of xfb buffer are used by the given type. +// Return the size of type, in bytes. +// Sets contains64BitType to true if the type contains a 64-bit data type. +// Sets contains32BitType to true if the type contains a 32-bit data type. +// Sets contains16BitType to true if the type contains a 16-bit data type. +// N.B. Caller must set contains64BitType, contains32BitType, and contains16BitType to false before calling. +unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const +{ + // "...if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8. + // ...within the qualified entity, subsequent components are each + // assigned, in order, to the next available offset aligned to a multiple of + // that component's size. Aggregate types are flattened down to the component + // level to get this sequence of components." + + if (type.isSizedArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + // Unsized array use to xfb should be a compile error. + TType elementType(type, 0); + return type.getOuterArraySize() * computeTypeXfbSize(elementType, contains64BitType, contains16BitType, contains16BitType); + } + + if (type.isStruct()) { + unsigned int size = 0; + bool structContains64BitType = false; + bool structContains32BitType = false; + bool structContains16BitType = false; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + // "... if applied to + // an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8." + bool memberContains64BitType = false; + bool memberContains32BitType = false; + bool memberContains16BitType = false; + int memberSize = computeTypeXfbSize(memberType, memberContains64BitType, memberContains32BitType, memberContains16BitType); + if (memberContains64BitType) { + structContains64BitType = true; + RoundToPow2(size, 8); + } else if (memberContains32BitType) { + structContains32BitType = true; + RoundToPow2(size, 4); + } else if (memberContains16BitType) { + structContains16BitType = true; + RoundToPow2(size, 2); + } + size += memberSize; + } + + if (structContains64BitType) { + contains64BitType = true; + RoundToPow2(size, 8); + } else if (structContains32BitType) { + contains32BitType = true; + RoundToPow2(size, 4); + } else if (structContains16BitType) { + contains16BitType = true; + RoundToPow2(size, 2); + } + return size; + } + + int numComponents {0}; + if (type.isScalar()) + numComponents = 1; + else if (type.isVector()) + numComponents = type.getVectorSize(); + else if (type.isMatrix()) + numComponents = type.getMatrixCols() * type.getMatrixRows(); + else { + assert(0); + numComponents = 1; + } + + if (type.getBasicType() == EbtDouble || type.getBasicType() == EbtInt64 || type.getBasicType() == EbtUint64) { + contains64BitType = true; + return 8 * numComponents; + } else if (type.getBasicType() == EbtFloat16 || type.getBasicType() == EbtInt16 || type.getBasicType() == EbtUint16) { + contains16BitType = true; + return 2 * numComponents; + } else if (type.getBasicType() == EbtInt8 || type.getBasicType() == EbtUint8) + return numComponents; + else { + contains32BitType = true; + return 4 * numComponents; + } +} + +const int baseAlignmentVec4Std140 = 16; + +// Return the size and alignment of a component of the given type. +// The size is returned in the 'size' parameter +// Return value is the alignment.. +int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size) +{ + switch (type.getBasicType()) { + case EbtInt64: + case EbtUint64: + case EbtDouble: size = 8; return 8; + case EbtFloat16: size = 2; return 2; + case EbtBFloat16: size = 2; return 2; + case EbtInt8: + case EbtUint8: size = 1; return 1; + case EbtInt16: + case EbtUint16: size = 2; return 2; + case EbtReference: size = 8; return 8; + case EbtSampler: + { + if (type.isBindlessImage() || type.isBindlessTexture()) { + size = 8; return 8; + } + else { + size = 4; return 4; + } + } + default: size = 4; return 4; + } +} + +// Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout +// Operates recursively. +// +// If std140 is true, it does the rounding up to vec4 size required by std140, +// otherwise it does not, yielding std430 rules. +// +// The size is returned in the 'size' parameter +// +// The stride is only non-0 for arrays or matrices, and is the stride of the +// top-level object nested within the type. E.g., for an array of matrices, +// it is the distances needed between matrices, despite the rules saying the +// stride comes from the flattening down to vectors. +// +// Return value is the alignment of the type. +int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + int alignment; + + bool std140 = layoutPacking == glslang::ElpStd140; + // When using the std140 storage layout, structures will be laid out in buffer + // storage with its members stored in monotonically increasing order based on their + // location in the declaration. A structure and each structure member have a base + // offset and a base alignment, from which an aligned offset is computed by rounding + // the base offset up to a multiple of the base alignment. The base offset of the first + // member of a structure is taken from the aligned offset of the structure itself. The + // base offset of all other structure members is derived by taking the offset of the + // last basic machine unit consumed by the previous member and adding one. Each + // structure member is stored in memory at its aligned offset. The members of a top- + // level uniform block are laid out in buffer storage by treating the uniform block as + // a structure with a base offset of zero. + // + // 1. If the member is a scalar consuming N basic machine units, the base alignment is N. + // + // 2. If the member is a two- or four-component vector with components consuming N basic + // machine units, the base alignment is 2N or 4N, respectively. + // + // 3. If the member is a three-component vector with components consuming N + // basic machine units, the base alignment is 4N. + // + // 4. If the member is an array of scalars or vectors, the base alignment and array + // stride are set to match the base alignment of a single array element, according + // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The + // array may have padding at the end; the base offset of the member following + // the array is rounded up to the next multiple of the base alignment. + // + // 5. If the member is a column-major matrix with C columns and R rows, the + // matrix is stored identically to an array of C column vectors with R + // components each, according to rule (4). + // + // 6. If the member is an array of S column-major matrices with C columns and + // R rows, the matrix is stored identically to a row of S X C column vectors + // with R components each, according to rule (4). + // + // 7. If the member is a row-major matrix with C columns and R rows, the matrix + // is stored identically to an array of R row vectors with C components each, + // according to rule (4). + // + // 8. If the member is an array of S row-major matrices with C columns and R + // rows, the matrix is stored identically to a row of S X R row vectors with C + // components each, according to rule (4). + // + // 9. If the member is a structure, the base alignment of the structure is N , where + // N is the largest base alignment value of any of its members, and rounded + // up to the base alignment of a vec4. The individual members of this substructure + // are then assigned offsets by applying this set of rules recursively, + // where the base offset of the first member of the sub-structure is equal to the + // aligned offset of the structure. The structure may have padding at the end; + // the base offset of the member following the sub-structure is rounded up to + // the next multiple of the base alignment of the structure. + // + // 10. If the member is an array of S structures, the S elements of the array are laid + // out in order, according to rule (9). + // + // Assuming, for rule 10: The stride is the same as the size of an element. + + stride = 0; + int dummyStride; + + // rules 4, 6, 8, and 10 + if (type.isArray()) { + // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType derefType(type, 0); + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected) + // uses the assumption for rule 10 in the comment above + // use one element to represent the last member of SSBO which is unsized array + int arraySize = (type.isUnsizedArray() && (type.getOuterArraySize() == 0)) ? 1 : type.getOuterArraySize(); + size = stride * arraySize; + return alignment; + } + + // rule 9 + if (type.getBasicType() == EbtStruct || type.getBasicType() == EbtBlock) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, layoutPacking, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + // The structure may have padding at the end; the base offset of + // the member following the sub-structure is rounded up to the next + // multiple of the base alignment of the structure. + RoundToPow2(size, maxAlignment); + + return maxAlignment; + } + + // rule 1 + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + // rules 2 and 3 + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + switch (type.getVectorSize()) { + case 1: // HLSL has this, GLSL does not + return scalarAlign; + case 2: + size *= 2; + return 2 * scalarAlign; + default: + size *= type.getVectorSize(); + return 4 * scalarAlign; + } + } + + // rules 5 and 7 + if (type.isMatrix()) { + // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows + TType derefType(type, 0, rowMajor); + + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = baseAlignmentVec4Std140; + return baseAlignmentVec4Std140; +} + +// To aid the basic HLSL rule about crossing vec4 boundaries. +bool TIntermediate::improperStraddle(const TType& type, int size, int offset, bool vectorLike) +{ + if (! vectorLike || type.isArray()) + return false; + + return size <= 16 ? offset / 16 != (offset + size - 1) / 16 + : offset % 16 != 0; +} + +int TIntermediate::getScalarAlignment(const TType& type, int& size, int& stride, bool rowMajor) +{ + int alignment; + + stride = 0; + int dummyStride; + + if (type.isArray()) { + TType derefType(type, 0); + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; + RoundToPow2(stride, alignment); + + size = stride * (type.getOuterArraySize() - 1) + size; + return alignment; + } + + if (type.getBasicType() == EbtStruct) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getScalarAlignment(*memberList[m].type, memberSize, dummyStride, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + return maxAlignment; + } + + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + + size *= type.getVectorSize(); + return scalarAlign; + } + + if (type.isMatrix()) { + TType derefType(type, 0, rowMajor); + + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = 1; + return 1; +} + +int TIntermediate::getMemberAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + if (layoutPacking == glslang::ElpScalar) { + return getScalarAlignment(type, size, stride, rowMajor); + } else { + return getBaseAlignment(type, size, stride, layoutPacking, rowMajor); + } +} + +// shared calculation by getOffset and getOffsets +void TIntermediate::updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize) +{ + int dummyStride; + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberType.getQualifier().layoutMatrix; + int memberAlignment = getMemberAlignment(memberType, memberSize, dummyStride, + parentType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : parentType.getQualifier().layoutMatrix == ElmRowMajor); + RoundToPow2(offset, memberAlignment); +} + +// Lookup or calculate the offset of a block member, using the recursively +// defined block offset rules. +int TIntermediate::getOffset(const TType& type, int index) +{ + const TTypeList& memberList = *type.getStruct(); + + // Don't calculate offset if one is present, it could be user supplied + // and different than what would be calculated. That is, this is faster, + // but not just an optimization. + if (memberList[index].type->getQualifier().hasOffset()) + return memberList[index].type->getQualifier().layoutOffset; + + int memberSize = 0; + int offset = 0; + for (int m = 0; m <= index; ++m) { + updateOffset(type, *memberList[m].type, offset, memberSize); + + if (m < index) + offset += memberSize; + } + + return offset; +} + +// Calculate the block data size. +// Block arrayness is not taken into account, each element is backed by a separate buffer. +int TIntermediate::getBlockSize(const TType& blockType) +{ + const TTypeList& memberList = *blockType.getStruct(); + int lastIndex = (int)memberList.size() - 1; + int lastOffset = getOffset(blockType, lastIndex); + + int lastMemberSize; + int dummyStride; + getMemberAlignment(*memberList[lastIndex].type, lastMemberSize, dummyStride, + blockType.getQualifier().layoutPacking, + blockType.getQualifier().layoutMatrix == ElmRowMajor); + + return lastOffset + lastMemberSize; +} + +int TIntermediate::computeBufferReferenceTypeSize(const TType& type) +{ + assert(type.isReference()); + int size = getBlockSize(*type.getReferentType()); + + int align = type.getBufferReferenceAlignment(); + + if (align) { + size = (size + align - 1) & ~(align-1); + } + + return size; +} + +bool TIntermediate::isIoResizeArray(const TType& type, EShLanguage language) { + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangTessControl && (type.getQualifier().storage == EvqVaryingIn || type.getQualifier().storage == EvqVaryingOut) && + ! type.getQualifier().patch) || + (language == EShLangTessEvaluation && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && + (type.getQualifier().pervertexNV || type.getQualifier().pervertexEXT)) || + (language == EShLangMesh && type.getQualifier().storage == EvqVaryingOut && + !type.getQualifier().perTaskNV)); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/localintermediate.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/localintermediate.h new file mode 100644 index 00000000..1f4b7681 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/localintermediate.h @@ -0,0 +1,1303 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _LOCAL_INTERMEDIATE_INCLUDED_ +#define _LOCAL_INTERMEDIATE_INCLUDED_ + +#include "../Include/intermediate.h" +#include "../Public/ShaderLang.h" +#include "Versions.h" + +#include +#include +#include +#include +#include +#include + +class TInfoSink; + +namespace glslang { + +struct TMatrixSelector { + int coord1; // stay agnostic about column/row; this is parse order + int coord2; +}; + +typedef int TVectorSelector; + +const int MaxSwizzleSelectors = 4; + +template +class TSwizzleSelectors { +public: + TSwizzleSelectors() : size_(0) { } + + void push_back(selectorType comp) + { + if (size_ < MaxSwizzleSelectors) + components[size_++] = comp; + } + void resize(int s) + { + assert(s <= size_); + size_ = s; + } + int size() const { return size_; } + selectorType operator[](int i) const + { + assert(i < MaxSwizzleSelectors); + return components[i]; + } + +private: + int size_; + selectorType components[MaxSwizzleSelectors]; +}; + +// +// Some helper structures for TIntermediate. Their contents are encapsulated +// by TIntermediate. +// + +// Used for call-graph algorithms for detecting recursion, missing bodies, and dead bodies. +// A "call" is a pair: . +// There can be duplicates. General assumption is the list is small. +struct TCall { + TCall(const TString& pCaller, const TString& pCallee) + : caller(pCaller), callee(pCallee), visited(false), currentPath(false), errorGiven(false) { } + TString caller; + TString callee; + bool visited; + bool currentPath; + bool errorGiven; + int calleeBodyPosition; +}; + +// A generic 1-D range. +struct TRange { + TRange(int start, int last) : start(start), last(last) { } + bool overlap(const TRange& rhs) const + { + return last >= rhs.start && start <= rhs.last; + } + int start; + int last; +}; + +// An IO range is a 3-D rectangle; the set of (location, component, index) triples all lying +// within the same location range, component range, and index value. Locations don't alias unless +// all other dimensions of their range overlap. +struct TIoRange { + TIoRange(TRange location, TRange component, TBasicType basicType, int index, bool centroid, bool smooth, bool flat, bool sample, bool patch) + : location(location), component(component), basicType(basicType), index(index), centroid(centroid), smooth(smooth), flat(flat), sample(sample), patch(patch) + { + } + bool overlap(const TIoRange& rhs) const + { + return location.overlap(rhs.location) && component.overlap(rhs.component) && index == rhs.index; + } + TRange location; + TRange component; + TBasicType basicType; + int index; + bool centroid; + bool smooth; + bool flat; + bool sample; + bool patch; +}; + +// An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying +// within the same binding and offset range. +struct TOffsetRange { + TOffsetRange(TRange binding, TRange offset) + : binding(binding), offset(offset) { } + bool overlap(const TOffsetRange& rhs) const + { + return binding.overlap(rhs.binding) && offset.overlap(rhs.offset); + } + TRange binding; + TRange offset; +}; + +// Things that need to be tracked per xfb buffer. +struct TXfbBuffer { + TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false), + contains32BitType(false), contains16BitType(false) { } + std::vector ranges; // byte offsets that have already been assigned + unsigned int stride; + unsigned int implicitStride; + bool contains64BitType; + bool contains32BitType; + bool contains16BitType; +}; + +// Track a set of strings describing how the module was processed. +// This includes command line options, transforms, etc., ideally inclusive enough +// to reproduce the steps used to transform the input source to the output. +// E.g., see SPIR-V OpModuleProcessed. +// Each "process" or "transform" uses is expressed in the form: +// process arg0 arg1 arg2 ... +// process arg0 arg1 arg2 ... +// where everything is textual, and there can be zero or more arguments +class TProcesses { +public: + TProcesses() {} + ~TProcesses() {} + + void addProcess(const char* process) + { + processes.push_back(process); + } + void addProcess(const std::string& process) + { + processes.push_back(process); + } + void addArgument(int arg) + { + processes.back().append(" "); + std::string argString = std::to_string(arg); + processes.back().append(argString); + } + void addArgument(const char* arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addArgument(const std::string& arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addIfNonZero(const char* process, int value) + { + if (value != 0) { + addProcess(process); + addArgument(value); + } + } + + const std::vector& getProcesses() const { return processes; } + +private: + std::vector processes; +}; + +class TSymbolTable; +class TSymbol; +class TVariable; + +// +// Texture and Sampler transformation mode. +// +enum ComputeDerivativeMode { + LayoutDerivativeNone, // default layout as SPV_NV_compute_shader_derivatives not enabled + LayoutDerivativeGroupQuads, // derivative_group_quadsNV + LayoutDerivativeGroupLinear, // derivative_group_linearNV +}; + +// +// Status type on AST level. Some uncalled status or functions would be reset in call graph. +// Currently we will keep status set by explicitly declared layout or variable decl. +// +enum AstRefType { + AstRefTypeVar, // Status set by variable decl + AstRefTypeFunc, // Status set by function decl + AstRefTypeLayout, // Status set by layout decl +}; + +class TIdMaps { +public: + TMap& operator[](long long i) { return maps[i]; } + const TMap& operator[](long long i) const { return maps[i]; } +private: + TMap maps[EsiCount]; +}; + +class TNumericFeatures { +public: + TNumericFeatures() : features(0) { } + TNumericFeatures(const TNumericFeatures&) = delete; + TNumericFeatures& operator=(const TNumericFeatures&) = delete; + typedef enum : unsigned int { + shader_explicit_arithmetic_types = 1 << 0, + shader_explicit_arithmetic_types_int8 = 1 << 1, + shader_explicit_arithmetic_types_int16 = 1 << 2, + shader_explicit_arithmetic_types_int32 = 1 << 3, + shader_explicit_arithmetic_types_int64 = 1 << 4, + shader_explicit_arithmetic_types_float16 = 1 << 5, + shader_explicit_arithmetic_types_float32 = 1 << 6, + shader_explicit_arithmetic_types_float64 = 1 << 7, + shader_implicit_conversions = 1 << 8, + gpu_shader_fp64 = 1 << 9, + gpu_shader_int16 = 1 << 10, + gpu_shader_half_float = 1 << 11, + } feature; + void insert(feature f) { features |= f; } + void erase(feature f) { features &= ~f; } + bool contains(feature f) const { return (features & f) != 0; } +private: + unsigned int features; +}; + +// MustBeAssigned wraps a T, asserting that it has been assigned with +// operator =() before attempting to read with operator T() or operator ->(). +// Used to catch cases where fields are read before they have been assigned. +template +class MustBeAssigned +{ +public: + MustBeAssigned() = default; + MustBeAssigned(const T& v) : value(v) {} + operator const T&() const { assert(isSet); return value; } + const T* operator ->() const { assert(isSet); return &value; } + MustBeAssigned& operator = (const T& v) { value = v; isSet = true; return *this; } +private: + T value; + bool isSet = false; +}; + +// +// Set of helper functions to help parse and build the tree. +// +class TIntermediate { +public: + explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : + language(l), + profile(p), version(v), + treeRoot(nullptr), + resources(TBuiltInResource{}), + numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false), + invertY(false), + dxPositionW(false), + enhancedMsgs(false), + debugInfo(false), + useStorageBuffer(false), + invariantAll(false), + nanMinMaxClamp(false), + depthReplacing(false), + stencilReplacing(false), + uniqueId(0), + globalUniformBlockName(""), + atomicCounterBlockName(""), + globalUniformBlockSet(TQualifier::layoutSetEnd), + globalUniformBlockBinding(TQualifier::layoutBindingEnd), + atomicCounterBlockSet(TQualifier::layoutSetEnd), + implicitThisName("@this"), implicitCounterName("@count"), + source(EShSourceNone), + useVulkanMemoryModel(false), + invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet), + inputPrimitive(ElgNone), outputPrimitive(ElgNone), + pixelCenterInteger(false), originUpperLeft(false),texCoordBuiltinRedeclared(false), + vertexSpacing(EvsNone), vertexOrder(EvoNone), interlockOrdering(EioNone), pointMode(false), earlyFragmentTests(false), + postDepthCoverage(false), earlyAndLateFragmentTestsAMD(false), + nonCoherentColorAttachmentReadEXT(false), + nonCoherentDepthAttachmentReadEXT(false), + nonCoherentStencilAttachmentReadEXT(false), + depthLayout(EldNone), + stencilLayout(ElsNone), + hlslFunctionality1(false), + blendEquations(0), xfbMode(false), multiStream(false), + layoutOverrideCoverage(false), + geoPassthroughEXT(false), + numShaderRecordBlocks(0), + computeDerivativeMode(LayoutDerivativeNone), + primitives(TQualifier::layoutNotSet), + numTaskNVBlocks(0), + layoutPrimitiveCulling(false), + numTaskEXTPayloads(0), + autoMapBindings(false), + autoMapLocations(false), + flattenUniformArrays(false), + useUnknownFormat(false), + hlslOffsets(false), + hlslIoMapping(false), + useVariablePointers(false), + textureSamplerTransformMode(EShTexSampTransKeep), + needToLegalize(false), + binaryDoubleOutput(false), + subgroupUniformControlFlow(false), + maximallyReconverges(false), + usePhysicalStorageBuffer(false), + spirvRequirement(nullptr), + spirvExecutionMode(nullptr), + uniformLocationBase(0), + quadDerivMode(false), reqFullQuadsMode(false) + { + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; + xfbBuffers.resize(TQualifier::layoutXfbBufferEnd); + shiftBinding.fill(0); + } + + void setVersion(int v) + { + version = v; + } + void setProfile(EProfile p) + { + profile = p; + } + + int getVersion() const { return version; } + EProfile getProfile() const { return profile; } + void setSpv(const SpvVersion& s) + { + spvVersion = s; + + // client processes + if (spvVersion.vulkan > 0) + processes.addProcess("client vulkan100"); + if (spvVersion.openGl > 0) + processes.addProcess("client opengl100"); + + // target SPV + switch (spvVersion.spv) { + case 0: + break; + case EShTargetSpv_1_0: + break; + case EShTargetSpv_1_1: + processes.addProcess("target-env spirv1.1"); + break; + case EShTargetSpv_1_2: + processes.addProcess("target-env spirv1.2"); + break; + case EShTargetSpv_1_3: + processes.addProcess("target-env spirv1.3"); + break; + case EShTargetSpv_1_4: + processes.addProcess("target-env spirv1.4"); + break; + case EShTargetSpv_1_5: + processes.addProcess("target-env spirv1.5"); + break; + case EShTargetSpv_1_6: + processes.addProcess("target-env spirv1.6"); + break; + default: + processes.addProcess("target-env spirvUnknown"); + break; + } + + // target-environment processes + switch (spvVersion.vulkan) { + case 0: + break; + case EShTargetVulkan_1_0: + processes.addProcess("target-env vulkan1.0"); + break; + case EShTargetVulkan_1_1: + processes.addProcess("target-env vulkan1.1"); + break; + case EShTargetVulkan_1_2: + processes.addProcess("target-env vulkan1.2"); + break; + case EShTargetVulkan_1_3: + processes.addProcess("target-env vulkan1.3"); + break; + case EShTargetVulkan_1_4: + processes.addProcess("target-env vulkan1.4"); + break; + default: + processes.addProcess("target-env vulkanUnknown"); + break; + } + if (spvVersion.openGl > 0) + processes.addProcess("target-env opengl"); + } + const SpvVersion& getSpv() const { return spvVersion; } + EShLanguage getStage() const { return language; } + void addRequestedExtension(const char* extension) { requestedExtensions.insert(extension); } + const std::set& getRequestedExtensions() const { return requestedExtensions; } + bool isRayTracingStage() const { + return language >= EShLangRayGen && language <= EShLangCallableNV; + } + + void setTreeRoot(TIntermNode* r) { treeRoot = r; } + TIntermNode* getTreeRoot() const { return treeRoot; } + void incrementEntryPointCount() { ++numEntryPoints; } + int getNumEntryPoints() const { return numEntryPoints; } + int getNumErrors() const { return numErrors; } + void addPushConstantCount() { ++numPushConstants; } + void setLimits(const TBuiltInResource& r) { resources = r; } + const TBuiltInResource& getLimits() const { return resources; } + + bool postProcess(TIntermNode*, EShLanguage); + void removeTree(); + + void setEntryPointName(const char* ep) + { + entryPointName = ep; + processes.addProcess("entry-point"); + processes.addArgument(entryPointName); + } + void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; } + const std::string& getEntryPointName() const { return entryPointName; } + const std::string& getEntryPointMangledName() const { return entryPointMangledName; } + + void setDebugInfo(bool debuginfo) + { + debugInfo = debuginfo; + } + bool getDebugInfo() const { return debugInfo; } + + void setInvertY(bool invert) + { + invertY = invert; + if (invertY) + processes.addProcess("invert-y"); + } + bool getInvertY() const { return invertY; } + + void setDxPositionW(bool dxPosW) + { + dxPositionW = dxPosW; + if (dxPositionW) + processes.addProcess("dx-position-w"); + } + bool getDxPositionW() const { return dxPositionW; } + + void setEnhancedMsgs() + { + enhancedMsgs = true; + } + bool getEnhancedMsgs() const { return enhancedMsgs && getSource() == EShSourceGlsl; } + +#ifdef ENABLE_HLSL + void setSource(EShSource s) { source = s; } + EShSource getSource() const { return source; } +#else + void setSource(EShSource s) { assert(s == EShSourceGlsl); (void)s; } + EShSource getSource() const { return EShSourceGlsl; } +#endif + + bool isRecursive() const { return recursive; } + + TIntermSymbol* addSymbol(const TVariable&); + TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TType&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TIntermSymbol&); + TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); + std::tuple addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1); + TIntermTyped* addUniShapeConversion(TOperator, const TType&, TIntermTyped*); + TIntermTyped* addConversion(TBasicType convertTo, TIntermTyped* node) const; + void addBiShapeConversion(TOperator, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode); + TIntermTyped* addShapeConversion(const TType&, TIntermTyped*); + TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc&); + TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, const TSourceLoc&); + TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType); + bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const; + bool isIntegralPromotion(TBasicType from, TBasicType to) const; + bool isFPPromotion(TBasicType from, TBasicType to) const; + bool isIntegralConversion(TBasicType from, TBasicType to) const; + bool isFPConversion(TBasicType from, TBasicType to) const; + bool isFPIntegralConversion(TBasicType from, TBasicType to) const; + TOperator mapTypeToConstructorOp(const TType&) const; + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right); + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); + TIntermAggregate* mergeAggregate(TIntermNode* left, TIntermNode* right); + TIntermAggregate* mergeAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); + TIntermAggregate* makeAggregate(TIntermNode* node); + TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&); + TIntermAggregate* makeAggregate(const TSourceLoc&); + TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, const TSourceLoc&); + bool areAllChildConst(TIntermAggregate* aggrNode); + TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); + TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); + TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&); + TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const; + TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; + bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); + TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); + TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, + const TSourceLoc&, TIntermLoop*&); + TIntermBranch* addBranch(TOperator, const TSourceLoc&); + TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); + template TIntermTyped* addSwizzle(TSwizzleSelectors&, const TSourceLoc&); + + // Low level functions to add nodes (no conversions or other higher level transformations) + // If a type is provided, the node's type will be set to it. + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&) const; + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&, + const TType&) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&, const TType&) const; + + // Constant folding (in Constant.cpp) + TIntermTyped* fold(TIntermAggregate* aggrNode); + TIntermTyped* foldConstructor(TIntermAggregate* aggrNode); + TIntermTyped* foldDereference(TIntermTyped* node, int index, const TSourceLoc&); + TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors& fields, const TSourceLoc&); + + // Tree ops + static const TIntermTyped* traverseLValueBase(const TIntermTyped*, bool swizzleOkay, bool bufferReferenceOk = false, + std::function proc = {}); + + // Linkage related + void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&); + TIntermAggregate* findLinkerObjects() const; + + void setGlobalUniformBlockName(const char* name) { globalUniformBlockName = std::string(name); } + const char* getGlobalUniformBlockName() const { return globalUniformBlockName.c_str(); } + void setGlobalUniformSet(unsigned int set) { globalUniformBlockSet = set; } + unsigned int getGlobalUniformSet() const { return globalUniformBlockSet; } + void setGlobalUniformBinding(unsigned int binding) { globalUniformBlockBinding = binding; } + unsigned int getGlobalUniformBinding() const { return globalUniformBlockBinding; } + + void setAtomicCounterBlockName(const char* name) { atomicCounterBlockName = std::string(name); } + const char* getAtomicCounterBlockName() const { return atomicCounterBlockName.c_str(); } + void setAtomicCounterBlockSet(unsigned int set) { atomicCounterBlockSet = set; } + unsigned int getAtomicCounterBlockSet() const { return atomicCounterBlockSet; } + + + void setUseStorageBuffer() { useStorageBuffer = true; } + bool usingStorageBuffer() const { return useStorageBuffer; } + void setInvariantAll() { invariantAll = true; } + bool isInvariantAll() const { return invariantAll; } + void setDepthReplacing() { depthReplacing = true; } + bool isDepthReplacing() const { return depthReplacing; } + void setStencilReplacing() { stencilReplacing = true; } + bool isStencilReplacing() const { return stencilReplacing; } + bool setLocalSize(int dim, int size) + { + if (localSizeNotDefault[dim]) + return size == localSize[dim]; + localSizeNotDefault[dim] = true; + localSize[dim] = size; + return true; + } + unsigned int getLocalSize(int dim) const { return localSize[dim]; } + bool isLocalSizeSet() const + { + // Return true if any component has been set (i.e. any component is not default). + return localSizeNotDefault[0] || localSizeNotDefault[1] || localSizeNotDefault[2]; + } + bool setLocalSizeSpecId(int dim, int id) + { + if (localSizeSpecId[dim] != TQualifier::layoutNotSet) + return id == localSizeSpecId[dim]; + localSizeSpecId[dim] = id; + return true; + } + int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; } + bool isLocalSizeSpecialized() const + { + // Return true if any component has been specialized. + return localSizeSpecId[0] != TQualifier::layoutNotSet || + localSizeSpecId[1] != TQualifier::layoutNotSet || + localSizeSpecId[2] != TQualifier::layoutNotSet; + } + void output(TInfoSink&, bool tree); + + bool isEsProfile() const { return profile == EEsProfile; } + + void setShiftBinding(TResourceType res, unsigned int shift) + { + shiftBinding[res] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) + processes.addIfNonZero(name, shift); + } + + unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; } + + void setShiftBindingForSet(TResourceType res, unsigned int shift, unsigned int set) + { + if (shift == 0) // ignore if there's no shift: it's a no-op. + return; + + shiftBindingForSet[res][set] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) { + processes.addProcess(name); + processes.addArgument(shift); + processes.addArgument(set); + } + } + + int getShiftBindingForSet(TResourceType res, unsigned int set) const + { + const auto shift = shiftBindingForSet[res].find(set); + return shift == shiftBindingForSet[res].end() ? -1 : shift->second; + } + bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); } + + void setResourceSetBinding(const std::vector& shift) + { + resourceSetBinding = shift; + if (shift.size() > 0) { + processes.addProcess("resource-set-binding"); + for (int s = 0; s < (int)shift.size(); ++s) + processes.addArgument(shift[s]); + } + } + const std::vector& getResourceSetBinding() const { return resourceSetBinding; } + void setAutoMapBindings(bool map) + { + autoMapBindings = map; + if (autoMapBindings) + processes.addProcess("auto-map-bindings"); + } + bool getAutoMapBindings() const { return autoMapBindings; } + void setAutoMapLocations(bool map) + { + autoMapLocations = map; + if (autoMapLocations) + processes.addProcess("auto-map-locations"); + } + bool getAutoMapLocations() const { return autoMapLocations; } + +#ifdef ENABLE_HLSL + void setFlattenUniformArrays(bool flatten) + { + flattenUniformArrays = flatten; + if (flattenUniformArrays) + processes.addProcess("flatten-uniform-arrays"); + } + bool getFlattenUniformArrays() const { return flattenUniformArrays; } +#endif + void setNoStorageFormat(bool b) + { + useUnknownFormat = b; + if (useUnknownFormat) + processes.addProcess("no-storage-format"); + } + bool getNoStorageFormat() const { return useUnknownFormat; } + void setUseVulkanMemoryModel() + { + useVulkanMemoryModel = true; + processes.addProcess("use-vulkan-memory-model"); + } + bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; } + void setUsePhysicalStorageBuffer() + { + usePhysicalStorageBuffer = true; + } + bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; } + void setReplicatedComposites() + { + useReplicatedComposites = true; + } + bool usingReplicatedComposites() const { return useReplicatedComposites; } + void setUseVariablePointers() + { + useVariablePointers = true; + processes.addProcess("use-variable-pointers"); + } + // Set the global flag for bindless texture + void setBindlessTextureMode(const TString& currentCaller, AstRefType type) + { + // When type is not func, currentCaller should be "" (empty string) + bindlessTextureModeCaller[currentCaller] = type; + } + + // Get the global flag for bindless texture + bool getBindlessTextureMode() const + { + return (bindlessTextureModeCaller.size() > 0); + } + + // Set the global flag for bindless image + void setBindlessImageMode(const TString& currentCaller, AstRefType type) + { + // When type is not func, currentCaller should be "" (empty string) + bindlessImageModeCaller[currentCaller] = type; + } + + // Get the global flag for bindless image + bool getBindlessImageMode() const + { + return (bindlessImageModeCaller.size() > 0); + } + + // Get the global flag for bindless texture + bool resetTopLevelUncalledStatus(const TString& deadCaller) + { + // For reflection collection purpose, currently uniform layout setting and some + // flags introduced by variables (IO, global, etc,.) won't be reset here. + // Remove each global status (AST top level) introduced by uncalled functions. + // If a status is set by several functions, keep those which in call graph. + bool result = false; + + // For two types of bindless mode flag, we would only reset which is set by an uncalled function. + // If one status flag's key in caller vec is empty, it should be come from a non-function setting. + if (!bindlessTextureModeCaller.empty()) { + auto caller = bindlessTextureModeCaller.find(deadCaller); + if (caller != bindlessTextureModeCaller.end() && bindlessTextureModeCaller[deadCaller] == AstRefTypeFunc) { + bindlessTextureModeCaller.erase(caller); + result = true; + } + } + if (!bindlessImageModeCaller.empty()) { + auto caller = bindlessImageModeCaller.find(deadCaller); + if (caller != bindlessImageModeCaller.end() && bindlessImageModeCaller[deadCaller] == AstRefTypeFunc) { + bindlessImageModeCaller.erase(caller); + result = true; + } + } + return result; + } + + bool getBindlessMode() const + { + return getBindlessTextureMode() || getBindlessImageMode(); + } + + bool usingVariablePointers() const { return useVariablePointers; } + +#ifdef ENABLE_HLSL + template T addCounterBufferName(const T& name) const { return name + implicitCounterName; } + bool hasCounterBufferName(const TString& name) const { + size_t len = strlen(implicitCounterName); + return name.size() > len && + name.compare(name.size() - len, len, implicitCounterName) == 0; + } +#endif + + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; } + int getNumPushConstants() const { return numPushConstants; } + void addShaderRecordCount() { ++numShaderRecordBlocks; } + void addTaskNVCount() { ++numTaskNVBlocks; } + void addTaskPayloadEXTCount() { ++numTaskEXTPayloads; } + + bool setInvocations(int i) + { + if (invocations != TQualifier::layoutNotSet) + return invocations == i; + invocations = i; + return true; + } + int getInvocations() const { return invocations; } + bool setVertices(int m) + { + if (vertices != TQualifier::layoutNotSet) + return vertices == m; + vertices = m; + return true; + } + int getVertices() const { return vertices; } + bool setInputPrimitive(TLayoutGeometry p) + { + if (inputPrimitive != ElgNone) + return inputPrimitive == p; + inputPrimitive = p; + return true; + } + TLayoutGeometry getInputPrimitive() const { return inputPrimitive; } + bool setVertexSpacing(TVertexSpacing s) + { + if (vertexSpacing != EvsNone) + return vertexSpacing == s; + vertexSpacing = s; + return true; + } + TVertexSpacing getVertexSpacing() const { return vertexSpacing; } + bool setVertexOrder(TVertexOrder o) + { + if (vertexOrder != EvoNone) + return vertexOrder == o; + vertexOrder = o; + return true; + } + TVertexOrder getVertexOrder() const { return vertexOrder; } + void setPointMode() { pointMode = true; } + bool getPointMode() const { return pointMode; } + + bool setInterlockOrdering(TInterlockOrdering o) + { + if (interlockOrdering != EioNone) + return interlockOrdering == o; + interlockOrdering = o; + return true; + } + TInterlockOrdering getInterlockOrdering() const { return interlockOrdering; } + + void setXfbMode() { xfbMode = true; } + bool getXfbMode() const { return xfbMode; } + void setQuadDerivMode(bool mode = true) { quadDerivMode = mode; } + bool getQuadDerivMode() const { return quadDerivMode; } + void setReqFullQuadsMode(bool mode = true) { reqFullQuadsMode = mode; } + bool getReqFullQuadsMode() const { return reqFullQuadsMode; } + void setMultiStream() { multiStream = true; } + bool isMultiStream() const { return multiStream; } + bool setOutputPrimitive(TLayoutGeometry p) + { + if (outputPrimitive != ElgNone) + return outputPrimitive == p; + outputPrimitive = p; + return true; + } + TLayoutGeometry getOutputPrimitive() const { return outputPrimitive; } + void setNonCoherentColorAttachmentReadEXT() { nonCoherentColorAttachmentReadEXT = true; } + bool getNonCoherentColorAttachmentReadEXT() const { return nonCoherentColorAttachmentReadEXT; } + void setNonCoherentDepthAttachmentReadEXT() { nonCoherentDepthAttachmentReadEXT = true; } + bool getNonCoherentDepthAttachmentReadEXT() const { return nonCoherentDepthAttachmentReadEXT; } + void setNonCoherentStencilAttachmentReadEXT() { nonCoherentStencilAttachmentReadEXT = true; } + bool getNonCoherentStencilAttachmentReadEXT() const { return nonCoherentStencilAttachmentReadEXT; } + void setPostDepthCoverage() { postDepthCoverage = true; } + bool getPostDepthCoverage() const { return postDepthCoverage; } + void setEarlyFragmentTests() { earlyFragmentTests = true; } + void setEarlyAndLateFragmentTestsAMD() { earlyAndLateFragmentTestsAMD = true; } + bool getEarlyFragmentTests() const { return earlyFragmentTests; } + bool getEarlyAndLateFragmentTestsAMD() const { return earlyAndLateFragmentTestsAMD; } + bool setDepth(TLayoutDepth d) + { + if (depthLayout != EldNone) + return depthLayout == d; + depthLayout = d; + return true; + } + bool setStencil(TLayoutStencil s) + { + if (stencilLayout != ElsNone) + return stencilLayout == s; + stencilLayout = s; + return true; + } + TLayoutDepth getDepth() const { return depthLayout; } + TLayoutStencil getStencil() const { return stencilLayout; } + void setOriginUpperLeft() { originUpperLeft = true; } + bool getOriginUpperLeft() const { return originUpperLeft; } + void setPixelCenterInteger() { pixelCenterInteger = true; } + bool getPixelCenterInteger() const { return pixelCenterInteger; } + void setTexCoordRedeclared() { texCoordBuiltinRedeclared = true; } + bool getTexCoordRedeclared() const { return texCoordBuiltinRedeclared; } + void addBlendEquation(TBlendEquationShift b) { blendEquations |= (1 << b); } + unsigned int getBlendEquations() const { return blendEquations; } + bool setXfbBufferStride(int buffer, unsigned stride) + { + if (xfbBuffers[buffer].stride != TQualifier::layoutXfbStrideEnd) + return xfbBuffers[buffer].stride == stride; + xfbBuffers[buffer].stride = stride; + return true; + } + unsigned getXfbStride(int buffer) const { return xfbBuffers[buffer].stride; } + int addXfbBufferOffset(const TType&); + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const; + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType) const; + void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; } + bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; } + void setGeoPassthroughEXT() { geoPassthroughEXT = true; } + bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } + void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; } + bool hasLayoutDerivativeModeNone() const { return computeDerivativeMode != LayoutDerivativeNone; } + ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; } + void setLayoutPrimitiveCulling() { layoutPrimitiveCulling = true; } + bool getLayoutPrimitiveCulling() const { return layoutPrimitiveCulling; } + bool setPrimitives(int m) + { + if (primitives != TQualifier::layoutNotSet) + return primitives == m; + primitives = m; + return true; + } + int getPrimitives() const { return primitives; } + const char* addSemanticName(const TString& name) + { + return semanticNameSet.insert(name).first->c_str(); + } + void addUniformLocationOverride(const char* nameStr, int location) + { + std::string name = nameStr; + uniformLocationOverrides[name] = location; + } + + int getUniformLocationOverride(const char* nameStr) const + { + std::string name = nameStr; + auto pos = uniformLocationOverrides.find(name); + if (pos == uniformLocationOverrides.end()) + return -1; + else + return pos->second; + } + + void setUniformLocationBase(int base) { uniformLocationBase = base; } + int getUniformLocationBase() const { return uniformLocationBase; } + + void setNeedsLegalization() { needToLegalize = true; } + bool needsLegalization() const { return needToLegalize; } + + void setBinaryDoubleOutput() { binaryDoubleOutput = true; } + bool getBinaryDoubleOutput() { return binaryDoubleOutput; } + + void setSubgroupUniformControlFlow() { subgroupUniformControlFlow = true; } + bool getSubgroupUniformControlFlow() const { return subgroupUniformControlFlow; } + + void setMaximallyReconverges() { maximallyReconverges = true; } + bool getMaximallyReconverges() const { return maximallyReconverges; } + + // GL_EXT_spirv_intrinsics + void insertSpirvRequirement(const TSpirvRequirement* spirvReq); + bool hasSpirvRequirement() const { return spirvRequirement != nullptr; } + const TSpirvRequirement& getSpirvRequirement() const { return *spirvRequirement; } + void insertSpirvExecutionMode(int executionMode, const TIntermAggregate* args = nullptr); + void insertSpirvExecutionModeId(int executionMode, const TIntermAggregate* args); + bool hasSpirvExecutionMode() const { return spirvExecutionMode != nullptr; } + const TSpirvExecutionMode& getSpirvExecutionMode() const { return *spirvExecutionMode; } + + void addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) + { + std::string name(nameStr); + blockBackingOverrides[name] = backing; + } + TBlockStorageClass getBlockStorageOverride(const char* nameStr) const + { + std::string name = nameStr; + auto pos = blockBackingOverrides.find(name); + if (pos == blockBackingOverrides.end()) + return EbsNone; + else + return pos->second; + } +#ifdef ENABLE_HLSL + void setHlslFunctionality1() { hlslFunctionality1 = true; } + bool getHlslFunctionality1() const { return hlslFunctionality1; } + void setHlslOffsets() + { + hlslOffsets = true; + if (hlslOffsets) + processes.addProcess("hlsl-offsets"); + } + bool usingHlslOffsets() const { return hlslOffsets; } + void setHlslIoMapping(bool b) + { + hlslIoMapping = b; + if (hlslIoMapping) + processes.addProcess("hlsl-iomap"); + } + bool usingHlslIoMapping() { return hlslIoMapping; } +#else + bool getHlslFunctionality1() const { return false; } + bool usingHlslOffsets() const { return false; } + bool usingHlslIoMapping() { return false; } +#endif + + bool usingScalarBlockLayout() const { + return IsRequestedExtension(E_GL_EXT_scalar_block_layout); + } + + bool usingTextureOffsetNonConst() const { + return IsRequestedExtension(E_GL_EXT_texture_offset_non_const); + } + + bool IsRequestedExtension(const char* extension) const + { + return (requestedExtensions.find(extension) != requestedExtensions.end()); + } + + void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee); + void merge(TInfoSink&, TIntermediate&); + void finalCheck(TInfoSink&, bool keepUncalled); + + void mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly); + void mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit); + void mergeImplicitArraySizes(TInfoSink& infoSink, TIntermediate& unit); + void checkStageIO(TInfoSink&, TIntermediate&, EShMessages); + void optimizeStageIO(TInfoSink&, TIntermediate&); + + bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const; + TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const; + + void addIoAccessed(const TString& name) { ioAccessed.insert(name); } + bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); } + + int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision); + int checkLocationRange(int set, const TIoRange& range, const TType&, bool& typeCollision); + int checkLocationRT(int set, int location); + int addUsedOffsets(int binding, int offset, int numOffsets); + bool addUsedConstantId(int id); + GLSLANG_EXPORT_FOR_TESTS + static int computeTypeLocationSize(const TType&, EShLanguage); + static int computeTypeUniformLocationSize(const TType&); + + static int getBaseAlignmentScalar(const TType&, int& size); + static int getBaseAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static int getScalarAlignment(const TType&, int& size, int& stride, bool rowMajor); + static int getMemberAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static bool improperStraddle(const TType& type, int size, int offset, bool vectorLike); + static void updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize); + static int getOffset(const TType& type, int index); + static int getBlockSize(const TType& blockType); + static int computeBufferReferenceTypeSize(const TType&); + static bool isIoResizeArray(const TType& type, EShLanguage language); + + bool promote(TIntermOperator*); + void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; } + bool getNanMinMaxClamp() const { return nanMinMaxClamp; } + + void setSourceFile(const char* file) { if (file != nullptr) sourceFile = file; } + const std::string& getSourceFile() const { return sourceFile; } + void addSourceText(const char* text, size_t len) { sourceText.append(text, len); } + const std::string& getSourceText() const { return sourceText; } + const std::map& getIncludeText() const { return includeText; } + void addIncludeText(const char* name, const char* text, size_t len) { includeText[name].assign(text,len); } + void addProcesses(const std::vector& p) + { + for (int i = 0; i < (int)p.size(); ++i) + processes.addProcess(p[i]); + } + void addProcess(const std::string& process) { processes.addProcess(process); } + void addProcessArgument(const std::string& arg) { processes.addArgument(arg); } + const std::vector& getProcesses() const { return processes.getProcesses(); } + unsigned long long getUniqueId() const { return uniqueId; } + void setUniqueId(unsigned long long id) { uniqueId = id; } + + // Certain explicit conversions are allowed conditionally + bool getArithemeticInt8Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); + } + bool getArithemeticInt16Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); + } + + bool getArithemeticFloat16Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); + } + void updateNumericFeature(TNumericFeatures::feature f, bool on) + { on ? numericFeatures.insert(f) : numericFeatures.erase(f); } + +protected: + TIntermSymbol* addSymbol(long long Id, const TString&, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); + void error(TInfoSink& infoSink, const TSourceLoc* loc, EShMessages messages, const char*, EShLanguage unitStage = EShLangCount); + void error(TInfoSink& infoSink, const char* message, EShLanguage unitStage = EShLangCount) { + error(infoSink, nullptr, EShMsgDefault, message, unitStage); + } + void warn(TInfoSink& infoSink, const TSourceLoc* loc, EShMessages, const char*, EShLanguage unitStage = EShLangCount); + void warn(TInfoSink& infoSink, const char* message, EShLanguage unitStage = EShLangCount) { + warn(infoSink, nullptr, EShMsgDefault, message, unitStage); + } + void mergeCallGraphs(TInfoSink&, TIntermediate&); + void mergeModes(TInfoSink&, TIntermediate&); + void mergeTrees(TInfoSink&, TIntermediate&); + void seedIdMap(TIdMaps& idMaps, long long& IdShift); + void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&); + void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); + void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage); + void mergeBlockDefinitions(TInfoSink&, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unitRoot); + void mergeImplicitArraySizes(TType&, const TType&); + void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&); + void checkCallGraphCycles(TInfoSink&); + void checkCallGraphBodies(TInfoSink&, bool keepUncalled); + void inOutLocationCheck(TInfoSink&); + void sharedBlockCheck(TInfoSink&); + bool userOutputUsed() const; + bool isSpecializationOperation(const TIntermOperator&) const; + bool isNonuniformPropagating(TOperator) const; + bool promoteUnary(TIntermUnary&); + bool promoteBinary(TIntermBinary&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&); + bool promoteAggregate(TIntermAggregate&); + void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&); + void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&); + bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&); + void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root); + bool isConversionAllowed(TOperator op, TIntermTyped* node) const; + std::tuple getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const; + + static const char* getResourceName(TResourceType); + + const EShLanguage language; // stage, known at construction time + std::string entryPointName; + std::string entryPointMangledName; + typedef std::list TGraph; + TGraph callGraph; + + EProfile profile; // source profile + int version; // source version + SpvVersion spvVersion; + TIntermNode* treeRoot; + std::set requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them + MustBeAssigned resources; + int numEntryPoints; + int numErrors; + int numPushConstants; + bool recursive; + bool invertY; + bool dxPositionW; + bool enhancedMsgs; + bool debugInfo; + bool useStorageBuffer; + bool invariantAll; + bool nanMinMaxClamp; // true if desiring min/max/clamp to favor non-NaN over NaN + bool depthReplacing; + bool stencilReplacing; + int localSize[3]; + bool localSizeNotDefault[3]; + int localSizeSpecId[3]; + unsigned long long uniqueId; + + std::string globalUniformBlockName; + std::string atomicCounterBlockName; + unsigned int globalUniformBlockSet; + unsigned int globalUniformBlockBinding; + unsigned int atomicCounterBlockSet; + +public: + const char* const implicitThisName; + const char* const implicitCounterName; +protected: + EShSource source; // source language, known a bit later + bool useVulkanMemoryModel; + int invocations; + int vertices; + TLayoutGeometry inputPrimitive; + TLayoutGeometry outputPrimitive; + bool pixelCenterInteger; + bool originUpperLeft; + bool texCoordBuiltinRedeclared; + TVertexSpacing vertexSpacing; + TVertexOrder vertexOrder; + TInterlockOrdering interlockOrdering; + bool pointMode; + bool earlyFragmentTests; + bool postDepthCoverage; + bool earlyAndLateFragmentTestsAMD; + bool nonCoherentColorAttachmentReadEXT; + bool nonCoherentDepthAttachmentReadEXT; + bool nonCoherentStencilAttachmentReadEXT; + TLayoutDepth depthLayout; + TLayoutStencil stencilLayout; + bool hlslFunctionality1; + int blendEquations; // an 'or'ing of masks of shifts of TBlendEquationShift + bool xfbMode; + std::vector xfbBuffers; // all the data we need to track per xfb buffer + bool multiStream; + bool layoutOverrideCoverage; + bool geoPassthroughEXT; + int numShaderRecordBlocks; + ComputeDerivativeMode computeDerivativeMode; + int primitives; + int numTaskNVBlocks; + bool layoutPrimitiveCulling; + int numTaskEXTPayloads; + + // Base shift values + std::array shiftBinding; + + // Per-descriptor-set shift values + std::array, EResCount> shiftBindingForSet; + + std::vector resourceSetBinding; + bool autoMapBindings; + bool autoMapLocations; + bool flattenUniformArrays; + bool useUnknownFormat; + bool hlslOffsets; + bool hlslIoMapping; + bool useVariablePointers; + + std::set semanticNameSet; + + EShTextureSamplerTransformMode textureSamplerTransformMode; + + bool needToLegalize; + bool binaryDoubleOutput; + bool subgroupUniformControlFlow; + bool maximallyReconverges; + bool usePhysicalStorageBuffer; + bool useReplicatedComposites { false }; + + TSpirvRequirement* spirvRequirement; + TSpirvExecutionMode* spirvExecutionMode; + std::map bindlessTextureModeCaller; + std::map bindlessImageModeCaller; + std::unordered_map uniformLocationOverrides; + int uniformLocationBase; + bool quadDerivMode; + bool reqFullQuadsMode; + TNumericFeatures numericFeatures; + std::unordered_map blockBackingOverrides; + + std::unordered_set usedConstantId; // specialization constant ids used + std::vector usedAtomics; // sets of bindings used by atomic counters + std::vector usedIo[5]; // sets of used locations, one for each of in, out, uniform, and buffers + std::vector usedIoRT[4]; // sets of used location, one for rayPayload/rayPayloadIN, + // one for callableData/callableDataIn, one for hitObjectAttributeNV and + // one for shaderrecordhitobjectNV + // set of names of statically read/written I/O that might need extra checking + std::set ioAccessed; + + // source code of shader, useful as part of debug information + std::string sourceFile; + std::string sourceText; + + // Included text. First string is a name, second is the included text + std::map includeText; + + // for OpModuleProcessed, or equivalent + TProcesses processes; + +private: + void operator=(TIntermediate&); // prevent assignments +}; + +} // end namespace glslang + +#endif // _LOCAL_INTERMEDIATE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseConst.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseConst.cpp new file mode 100644 index 00000000..83509723 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseConst.cpp @@ -0,0 +1,213 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Traverse a tree of constants to create a single folded constant. +// It should only be used when the whole tree is known to be constant. +// + +#include "ParseHelper.h" + +namespace glslang { + +class TConstTraverser : public TIntermTraverser { +public: + TConstTraverser(const TConstUnionArray& cUnion, bool singleConstParam, TOperator constructType, const TType& t) + : unionArray(cUnion), type(t), + constructorType(constructType), singleConstantParam(singleConstParam), error(false), isMatrix(false), + matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull; } + + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + int index; + TConstUnionArray unionArray; + TOperator tOp; + const TType& type; + TOperator constructorType; + bool singleConstantParam; + bool error; + int size; // size of the constructor ( 4 for vec4) + bool isMatrix; + int matrixCols; + int matrixRows; + +protected: + TConstTraverser(TConstTraverser&); + TConstTraverser& operator=(TConstTraverser&); +}; + +bool TConstTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (! node->isConstructor() && node->getOp() != EOpComma) { + error = true; + + return false; + } + + bool flag = node->getSequence().size() == 1 && node->getSequence()[0]->getAsTyped()->getAsConstantUnion(); + if (flag) { + singleConstantParam = true; + constructorType = node->getOp(); + size = node->getType().computeNumComponents(); + + if (node->getType().isMatrix()) { + isMatrix = true; + matrixCols = node->getType().getMatrixCols(); + matrixRows = node->getType().getMatrixRows(); + } + } + + for (TIntermSequence::iterator p = node->getSequence().begin(); + p != node->getSequence().end(); p++) { + + if (node->getOp() == EOpComma) + index = 0; + + (*p)->traverse(this); + } + if (flag) + { + singleConstantParam = false; + constructorType = EOpNull; + size = 0; + isMatrix = false; + matrixCols = 0; + matrixRows = 0; + } + + return false; +} + +void TConstTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + TConstUnionArray leftUnionArray(unionArray); + int instanceSize = type.computeNumComponents(); + + if (index >= instanceSize) + return; + + if (! singleConstantParam) { + int rightUnionSize = node->getType().computeNumComponents(); + + const TConstUnionArray& rightUnionArray = node->getConstArray(); + for (int i = 0; i < rightUnionSize; i++) { + if (index >= instanceSize) + return; + leftUnionArray[index] = rightUnionArray[i]; + + index++; + } + } else { + int endIndex = index + size; + const TConstUnionArray& rightUnionArray = node->getConstArray(); + if (! isMatrix) { + int count = 0; + int nodeComps = node->getType().computeNumComponents(); + for (int i = index; i < endIndex; i++) { + if (i >= instanceSize) + return; + + leftUnionArray[i] = rightUnionArray[count]; + + (index)++; + + if (nodeComps > 1) + count++; + } + } else { + // constructing a matrix, but from what? + if (node->isMatrix()) { + // Matrix from a matrix; this has the outer matrix, node is the argument matrix. + // Traverse the outer, potentially bigger matrix, fill in missing pieces with the + // identity matrix. + for (int c = 0; c < matrixCols; ++c) { + for (int r = 0; r < matrixRows; ++r) { + int targetOffset = index + c * matrixRows + r; + if (r < node->getType().getMatrixRows() && c < node->getType().getMatrixCols()) { + int srcOffset = c * node->getType().getMatrixRows() + r; + leftUnionArray[targetOffset] = rightUnionArray[srcOffset]; + } else if (r == c) + leftUnionArray[targetOffset].setDConst(1.0); + else + leftUnionArray[targetOffset].setDConst(0.0); + } + } + } else { + // matrix from vector or scalar + int nodeComps = node->getType().computeNumComponents(); + if (nodeComps == 1) { + for (int c = 0; c < matrixCols; ++c) { + for (int r = 0; r < matrixRows; ++r) { + if (r == c) + leftUnionArray[index] = rightUnionArray[0]; + else + leftUnionArray[index].setDConst(0.0); + index++; + } + } + } else { + int count = 0; + for (int i = index; i < endIndex; i++) { + if (i >= instanceSize) + return; + + // construct the matrix in column-major order, from + // the components provided, in order + leftUnionArray[i] = rightUnionArray[count]; + + index++; + count++; + } + } + } + } + } +} + +bool TIntermediate::parseConstTree(TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam) +{ + if (root == nullptr) + return false; + + TConstTraverser it(unionArray, singleConstantParam, constructorType, t); + + root->traverse(&it); + if (it.error) + return true; + else + return false; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseVersions.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseVersions.h new file mode 100644 index 00000000..1e06c23a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/parseVersions.h @@ -0,0 +1,189 @@ +// +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// This is implemented in Versions.cpp + +#ifndef _PARSE_VERSIONS_INCLUDED_ +#define _PARSE_VERSIONS_INCLUDED_ + +#include "../Public/ShaderLang.h" +#include "../Include/InfoSink.h" +#include "Scan.h" + +#include + +namespace glslang { + +// +// Base class for parse helpers. +// This just has version-related information and checking. +// This class should be sufficient for preprocessing. +// +class TParseVersions { +public: + TParseVersions(TIntermediate& interm, int version, EProfile profile, + const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, + bool forwardCompatible, EShMessages messages) + : + forwardCompatible(forwardCompatible), + profile(profile), + infoSink(infoSink), version(version), + language(language), + spvVersion(spvVersion), + intermediate(interm), messages(messages), numErrors(0), currentScanner(nullptr) { } + virtual ~TParseVersions() { } + void requireStage(const TSourceLoc&, EShLanguageMask, const char* featureDesc); + void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc); + + bool forwardCompatible; // true if errors are to be given for use of deprecated features + EProfile profile; // the declared profile in the shader (core by default) + bool isEsProfile() const { return profile == EEsProfile; } + void requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc); + virtual void initializeExtensionBehavior(); + virtual void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); + virtual void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); + virtual void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + template + constexpr void ppRequireExtensions(const TSourceLoc& loc, Container extensions, const char* featureDesc) { + ppRequireExtensions(loc, static_cast(extensions.size()), extensions.data(), featureDesc); + } + + virtual TExtensionBehavior getExtensionBehavior(const char*); + virtual bool extensionTurnedOn(const char* const extension); + virtual bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); + virtual void updateExtensionBehavior(int line, const char* const extension, const char* behavior); + virtual void updateExtensionBehavior(const char* const extension, TExtensionBehavior); + virtual bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void checkExtensionStage(const TSourceLoc&, const char* const extension); + virtual void extensionRequires(const TSourceLoc&, const char* const extension, const char* behavior); + virtual void fullIntegerCheck(const TSourceLoc&, const char* op); + + virtual void unimplemented(const TSourceLoc&, const char* featureDesc); + virtual void doubleCheck(const TSourceLoc&, const char* op); + virtual void float16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void float16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void bfloat16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool float16Arithmetic(); + virtual void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int16Arithmetic(); + virtual void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int8ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int8Arithmetic(); + virtual void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void float16OpaqueCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void int64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt8Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void fcoopmatCheckNV(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void intcoopmatCheckNV(const TSourceLoc&, const char *op, bool builtIn = false); + virtual void coopmatCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void tensorLayoutViewCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void coopvecCheck(const TSourceLoc&, const char* op, bool builtIn = false); + bool relaxedErrors() const { return (messages & EShMsgRelaxedErrors) != 0; } + bool suppressWarnings() const { return (messages & EShMsgSuppressWarnings) != 0; } + bool isForwardCompatible() const { return forwardCompatible; } + + virtual void spvRemoved(const TSourceLoc&, const char* op); + virtual void vulkanRemoved(const TSourceLoc&, const char* op); + virtual void requireVulkan(const TSourceLoc&, const char* op); + virtual void requireSpv(const TSourceLoc&, const char* op); + virtual void requireSpv(const TSourceLoc&, const char *op, unsigned int version); + + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + + void addError() { ++numErrors; } + int getNumErrors() const { return numErrors; } + + void setScanner(TInputScanner* scanner) { currentScanner = scanner; } + TInputScanner* getScanner() const { return currentScanner; } + const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } + void setCurrentLine(int line) { currentScanner->setLine(line); } + void setCurrentColumn(int col) { currentScanner->setColumn(col); } + void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } + void setCurrentString(int string) { currentScanner->setString(string); } + + void getPreamble(std::string&); +#ifdef ENABLE_HLSL + bool isReadingHLSL() const { return (messages & EShMsgReadHlsl) == EShMsgReadHlsl; } + bool hlslEnable16BitTypes() const { return (messages & EShMsgHlslEnable16BitTypes) != 0; } + bool hlslDX9Compatible() const { return (messages & EShMsgHlslDX9Compatible) != 0; } +#else + bool isReadingHLSL() const { return false; } +#endif + + TInfoSink& infoSink; + + // compilation mode + int version; // version, updated by #version in the shader + EShLanguage language; // really the stage + SpvVersion spvVersion; + TIntermediate& intermediate; // helper for making and hooking up pieces of the parse tree + +protected: + TMap extensionBehavior; // for each extension string, what its current behavior is + TMap extensionMinSpv; // for each extension string, store minimum spirv required + TVector spvUnsupportedExt; // for extensions reserved for spv usage. + EShMessages messages; // errors/warnings/rule-sets + int numErrors; // number of compile-time errors encountered + TInputScanner* currentScanner; + +private: + explicit TParseVersions(const TParseVersions&); + TParseVersions& operator=(const TParseVersions&); +}; + +} // end namespace glslang + +#endif // _PARSE_VERSIONS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/pch.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/pch.h new file mode 100644 index 00000000..6ea3761e --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/pch.h @@ -0,0 +1,49 @@ +#ifndef _PCH_H +#define _PCH_H +// +// Copyright (C) 2018 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "Scan.h" +#include "ScanContext.h" + +#endif /* _PCH_H */ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp new file mode 100644 index 00000000..5b44b130 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -0,0 +1,1349 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Handle #define +int TPpContext::CPPdefine(TPpToken* ppToken) +{ + MacroSymbol mac; + + // get the macro name + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#define", ""); + return token; + } + if (ppToken->loc.string >= 0) { + // We are in user code; check for reserved name use: + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#define"); + } + + // save the macro name + const int defAtom = atomStrings.getAddAtom(ppToken->name); + TSourceLoc defineLoc = ppToken->loc; // because ppToken might go to the next line before we report errors + + // gather parameters to the macro, between (...) + token = scanToken(ppToken); + if (token == '(' && !ppToken->space) { + mac.functionLike = 1; + do { + token = scanToken(ppToken); + if (mac.args.size() == 0 && token == ')') + break; + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "bad argument", "#define", ""); + + return token; + } + const int argAtom = atomStrings.getAddAtom(ppToken->name); + + // check for duplication of parameter name + bool duplicate = false; + for (size_t a = 0; a < mac.args.size(); ++a) { + if (mac.args[a] == argAtom) { + parseContext.ppError(ppToken->loc, "duplicate macro parameter", "#define", ""); + duplicate = true; + break; + } + } + if (! duplicate) + mac.args.push_back(argAtom); + token = scanToken(ppToken); + } while (token == ','); + if (token != ')') { + parseContext.ppError(ppToken->loc, "missing parenthesis", "#define", ""); + + return token; + } + + token = scanToken(ppToken); + } else if (token != '\n' && token != EndOfInput && !ppToken->space) { + parseContext.ppWarn(ppToken->loc, "missing space after macro name", "#define", ""); + + return token; + } + + // record the definition of the macro + while (token != '\n' && token != EndOfInput) { + mac.body.putToken(token, ppToken); + token = scanToken(ppToken); + if (token != '\n' && ppToken->space) + mac.body.putToken(' ', ppToken); + } + + // check for duplicate definition + MacroSymbol* existing = lookupMacroDef(defAtom); + if (existing != nullptr) { + if (! existing->undef) { + // Already defined -- need to make sure they are identical: + // "Two replacement lists are identical if and only if the + // preprocessing tokens in both have the same number, + // ordering, spelling, and white-space separation, where all + // white-space separations are considered identical." + if (existing->functionLike != mac.functionLike) { + parseContext.ppError(defineLoc, "Macro redefined; function-like versus object-like:", "#define", + atomStrings.getString(defAtom)); + } else if (existing->args.size() != mac.args.size()) { + parseContext.ppError(defineLoc, "Macro redefined; different number of arguments:", "#define", + atomStrings.getString(defAtom)); + } else { + if (existing->args != mac.args) { + parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", + atomStrings.getString(defAtom)); + } + // set up to compare the two + existing->body.reset(); + mac.body.reset(); + int newToken; + bool firstToken = true; + do { + int oldToken; + TPpToken oldPpToken; + TPpToken newPpToken; + oldToken = existing->body.getToken(parseContext, &oldPpToken); + newToken = mac.body.getToken(parseContext, &newPpToken); + // for the first token, preceding spaces don't matter + if (firstToken) { + newPpToken.space = oldPpToken.space; + firstToken = false; + } + if (oldToken != newToken || oldPpToken != newPpToken) { + parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", + atomStrings.getString(defAtom)); + break; + } + } while (newToken != EndOfInput); + } + } + *existing = mac; + } else + addMacroDef(defAtom, mac); + + return '\n'; +} + +// Handle #undef +int TPpContext::CPPundef(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#undef", ""); + + return token; + } + + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#undef"); + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + if (macro != nullptr) + macro->undef = 1; + token = scanToken(ppToken); + if (token != '\n') + parseContext.ppError(ppToken->loc, "can only be followed by a single macro name", "#undef", ""); + + return token; +} + +// Handle #else +/* Skip forward to appropriate spot. This is used both +** to skip to a #endif after seeing an #else, AND to skip to a #else, +** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false. +*/ +int TPpContext::CPPelse(int matchelse, TPpToken* ppToken) +{ + inElseSkip = true; + int depth = 0; + int token = scanToken(ppToken); + + while (token != EndOfInput) { + if (token != '#') { + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + if (token == EndOfInput) + return token; + + token = scanToken(ppToken); + continue; + } + + if ((token = scanToken(ppToken)) != PpAtomIdentifier) + continue; + + int nextAtom = atomStrings.getAtom(ppToken->name); + if (nextAtom == PpAtomIf || nextAtom == PpAtomIfdef || nextAtom == PpAtomIfndef) { + depth++; + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if/#ifdef/#ifndef", ""); + return EndOfInput; + } else { + ifdepth++; + elsetracker++; + } + } else if (nextAtom == PpAtomEndif) { + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + elseSeen[elsetracker] = false; + --elsetracker; + if (depth == 0) { + // found the #endif we are looking for + if (ifdepth > 0) + --ifdepth; + break; + } + --depth; + --ifdepth; + } else if (matchelse && depth == 0) { + if (nextAtom == PpAtomElse) { + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + // found the #else we are looking for + break; + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + /* we decrement ifdepth here, because CPPif will increment + * it and we really want to leave it alone */ + if (ifdepth > 0) { + --ifdepth; + elseSeen[elsetracker] = false; + --elsetracker; + } + inElseSkip = false; + return CPPif(ppToken); + } + } else if (nextAtom == PpAtomElse) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + else + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + } + } + + inElseSkip = false; + return token; +} + +// Call when there should be no more tokens left on a line. +int TPpContext::extraTokenCheck(int contextAtom, TPpToken* ppToken, int token) +{ + if (token != '\n' && token != EndOfInput) { + static const char* message = "unexpected tokens following directive"; + + const char* label; + if (contextAtom == PpAtomElse) + label = "#else"; + else if (contextAtom == PpAtomElif) + label = "#elif"; + else if (contextAtom == PpAtomEndif) + label = "#endif"; + else if (contextAtom == PpAtomIf) + label = "#if"; + else if (contextAtom == PpAtomLine) + label = "#line"; + else + label = ""; + + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, label, ""); + else + parseContext.ppError(ppToken->loc, message, label, ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + + return token; +} + +enum eval_prec { + MIN_PRECEDENCE, + COND, LOGOR, LOGAND, OR, XOR, AND, EQUAL, RELATION, SHIFT, ADD, MUL, UNARY, + MAX_PRECEDENCE +}; + +namespace { + + int op_logor(int a, int b) { return a || b; } + int op_logand(int a, int b) { return a && b; } + int op_or(int a, int b) { return a | b; } + int op_xor(int a, int b) { return a ^ b; } + int op_and(int a, int b) { return a & b; } + int op_eq(int a, int b) { return a == b; } + int op_ne(int a, int b) { return a != b; } + int op_ge(int a, int b) { return a >= b; } + int op_le(int a, int b) { return a <= b; } + int op_gt(int a, int b) { return a > b; } + int op_lt(int a, int b) { return a < b; } + int op_shl(int a, int b) { return a << b; } + int op_shr(int a, int b) { return a >> b; } + int op_add(int a, int b) { return a + b; } + int op_sub(int a, int b) { return a - b; } + int op_mul(int a, int b) { return a * b; } + int op_div(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a / b; } + int op_mod(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a % b; } + int op_pos(int a) { return a; } + int op_neg(int a) { return a == INT_MIN ? INT_MIN : -a; } + int op_cmpl(int a) { return ~a; } + int op_not(int a) { return !a; } + +struct TBinop { + int token, precedence, (*op)(int, int); +} binop[] = { + { PpAtomOr, LOGOR, op_logor }, + { PpAtomAnd, LOGAND, op_logand }, + { '|', OR, op_or }, + { '^', XOR, op_xor }, + { '&', AND, op_and }, + { PpAtomEQ, EQUAL, op_eq }, + { PpAtomNE, EQUAL, op_ne }, + { '>', RELATION, op_gt }, + { PpAtomGE, RELATION, op_ge }, + { '<', RELATION, op_lt }, + { PpAtomLE, RELATION, op_le }, + { PpAtomLeft, SHIFT, op_shl }, + { PpAtomRight, SHIFT, op_shr }, + { '+', ADD, op_add }, + { '-', ADD, op_sub }, + { '*', MUL, op_mul }, + { '/', MUL, op_div }, + { '%', MUL, op_mod }, +}; + +struct TUnop { + int token, (*op)(int); +} unop[] = { + { '+', op_pos }, + { '-', op_neg }, + { '~', op_cmpl }, + { '!', op_not }, +}; + +} // anonymous namespace + +#define NUM_ELEMENTS(A) (sizeof(A) / sizeof(A[0])) + +int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + TSourceLoc loc = ppToken->loc; // because we sometimes read the newline before reporting the error + if (token == PpAtomIdentifier) { + if (strcmp("defined", ppToken->name) == 0) { + if (! parseContext.isReadingHLSL() && isMacroInput()) { + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, "nonportable when expanded from macros for preprocessor expression", + "defined", ""); + else + parseContext.ppError(ppToken->loc, "cannot use in preprocessor expression when expanded from macros", + "defined", ""); + } + bool needclose = 0; + token = scanToken(ppToken); + if (token == '(') { + needclose = true; + token = scanToken(ppToken); + } + if (token != PpAtomIdentifier) { + parseContext.ppError(loc, "incorrect directive, expected identifier", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + res = macro != nullptr ? !macro->undef : 0; + token = scanToken(ppToken); + if (needclose) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + token = tokenPaste(token, *ppToken); + token = evalToToken(token, shortCircuit, res, err, ppToken); + return eval(token, precedence, shortCircuit, res, err, ppToken); + } + } else if (token == PpAtomConstInt) { + res = ppToken->ival; + token = scanToken(ppToken); + } else if (token == '(') { + token = scanToken(ppToken); + token = eval(token, MIN_PRECEDENCE, shortCircuit, res, err, ppToken); + if (! err) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + int op = NUM_ELEMENTS(unop) - 1; + for (; op >= 0; op--) { + if (unop[op].token == token) + break; + } + if (op >= 0) { + token = scanToken(ppToken); + token = eval(token, UNARY, shortCircuit, res, err, ppToken); + res = unop[op].op(res); + } else { + parseContext.ppError(loc, "bad expression", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + } + + token = evalToToken(token, shortCircuit, res, err, ppToken); + + // Perform evaluation of binary operation, if there is one, otherwise we are done. + while (! err) { + if (token == ')' || token == '\n') + break; + int op; + for (op = NUM_ELEMENTS(binop) - 1; op >= 0; op--) { + if (binop[op].token == token) + break; + } + if (op < 0 || binop[op].precedence <= precedence) + break; + int leftSide = res; + + // Setup short-circuiting, needed for ES, unless already in a short circuit. + // (Once in a short-circuit, can't turn off again, until that whole subexpression is done. + if (! shortCircuit) { + if ((token == PpAtomOr && leftSide == 1) || + (token == PpAtomAnd && leftSide == 0)) + shortCircuit = true; + } + + token = scanToken(ppToken); + token = eval(token, binop[op].precedence, shortCircuit, res, err, ppToken); + + if (binop[op].op == op_div || binop[op].op == op_mod) { + if (res == 0) { + parseContext.ppError(loc, "division by 0", "preprocessor evaluation", ""); + res = 1; + } + } + res = binop[op].op(leftSide, res); + } + + return token; +} + +// Expand macros, skipping empty expansions, to get to the first real token in those expansions. +int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + while (token == PpAtomIdentifier && strcmp("defined", ppToken->name) != 0) { + switch (MacroExpand(ppToken, true, false)) { + case MacroExpandNotStarted: + case MacroExpandError: + parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", ""); + err = true; + res = 0; + break; + case MacroExpandStarted: + break; + case MacroExpandUndef: + if (! shortCircuit && parseContext.isEsProfile()) { + const char* message = "undefined macro in expression not allowed in es profile"; + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + else + parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + } + break; + } + token = scanToken(ppToken); + if (err) + break; + } + + return token; +} + +// Handle #if +int TPpContext::CPPif(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + int res = 0; + bool err = false; + token = eval(token, MIN_PRECEDENCE, false, res, err, ppToken); + token = extraTokenCheck(PpAtomIf, ppToken, token); + if (!res && !err) + token = CPPelse(1, ppToken); + + return token; +} + +// Handle #ifdef +int TPpContext::CPPifdef(int defined, TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth > maxIfNesting || elsetracker > maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#ifdef", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + + if (token != PpAtomIdentifier) { + if (defined) + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifdef", ""); + else + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifndef", ""); + } else { + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + token = scanToken(ppToken); + if (token != '\n') { + parseContext.ppError(ppToken->loc, "unexpected tokens following #ifdef directive - expected a newline", "#ifdef", ""); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + if (((macro != nullptr && !macro->undef) ? 1 : 0) != defined) + token = CPPelse(1, ppToken); + } + + return token; +} + +// Handle #include ... +// TODO: Handle macro expansions for the header name +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + bool startWithLocalSearch = true; // to additionally include the extra "" paths + int token; + + // Find the first non-whitespace char after #include + int ch = getChar(); + while (ch == ' ' || ch == '\t') { + ch = getChar(); + } + if (ch == '<') { + // style + startWithLocalSearch = false; + token = scanHeaderName(ppToken, '>'); + } else if (ch == '"') { + // "header-name" style + token = scanHeaderName(ppToken, '"'); + } else { + // unexpected, get the full token to generate the error + ungetChar(); + token = scanToken(ppToken); + } + + if (token != PpAtomConstString) { + parseContext.ppError(directiveLoc, "must be followed by a header name", "#include", ""); + return token; + } + + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; + + // See if the directive was well formed + token = scanToken(ppToken); + if (token != '\n') { + if (token == EndOfInput) + parseContext.ppError(ppToken->loc, "expected newline after header name:", "#include", "%s", filename.c_str()); + else + parseContext.ppError(ppToken->loc, "extra content after header name:", "#include", "%s", filename.c_str()); + return token; + } + + // Process well-formed directive + + // Find the inclusion, first look in "Local" ("") paths, if requested, + // otherwise, only search the "System" (<>) paths. + TShader::Includer::IncludeResult* res = nullptr; + if (startWithLocalSearch) + res = includer.includeLocal(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + if (res == nullptr || res->headerName.empty()) { + includer.releaseInclude(res); + res = includer.includeSystem(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + } + + // Process the results + if (res != nullptr && !res->headerName.empty()) { + if (res->headerData != nullptr && res->headerLength > 0) { + // path for processing one or more tokens from an included header, hand off 'res' + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->headerName << "\"\n"; + epilogue << (res->headerData[res->headerLength - 1] == '\n'? "" : "\n") << + "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this)); + parseContext.intermediate.addIncludeText(res->headerName.c_str(), res->headerData, res->headerLength); + // There's no "current" location anymore. + parseContext.setCurrentColumn(0); + } else { + // things are okay, but there is nothing to process + includer.releaseInclude(res); + } + } else { + // error path, clean up + std::string message = + res != nullptr ? std::string(res->headerData, res->headerLength) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", "for header name: %s", filename.c_str()); + includer.releaseInclude(res); + } + + return token; +} + +// Handle #line +int TPpContext::CPPline(TPpToken* ppToken) +{ + // "#line must have, after macro substitution, one of the following forms: + // "#line line + // "#line line source-string-number" + + int token = scanToken(ppToken); + const TSourceLoc directiveLoc = ppToken->loc; + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must by followed by an integral literal", "#line", ""); + return token; + } + + int lineRes = 0; // Line number after macro expansion. + int lineToken = 0; + bool hasFile = false; + int fileRes = 0; // Source file number after macro expansion. + const char* sourceName = nullptr; // Optional source file name. + bool lineErr = false; + bool fileErr = false; + disableEscapeSequences = true; + token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken); + disableEscapeSequences = false; + if (! lineErr) { + lineToken = lineRes; + if (token == '\n') + ++lineRes; + + if (parseContext.lineDirectiveShouldSetNextLine()) + --lineRes; + parseContext.setCurrentLine(lineRes); + + if (token != '\n') { + if (token == PpAtomConstString) { + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + // We need to save a copy of the string instead of pointing + // to the name field of the token since the name field + // will likely be overwritten by the next token scan. + sourceName = atomStrings.getString(atomStrings.getAddAtom(ppToken->name)); + parseContext.setCurrentSourceName(sourceName); + hasFile = true; + token = scanToken(ppToken); + } else { + token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken); + if (! fileErr) { + parseContext.setCurrentString(fileRes); + hasFile = true; + } + } + } + } + if (!fileErr && !lineErr) { + parseContext.notifyLineDirective(directiveLoc.line, lineToken, hasFile, fileRes, sourceName); + } + token = extraTokenCheck(PpAtomLine, ppToken, token); + + return token; +} + +// Handle #error +int TPpContext::CPPerror(TPpToken* ppToken) +{ + disableEscapeSequences = true; + int token = scanToken(ppToken); + disableEscapeSequences = false; + std::string message; + TSourceLoc loc = ppToken->loc; + + while (token != '\n' && token != EndOfInput) { + if (token == PpAtomConstInt16 || token == PpAtomConstUint16 || + token == PpAtomConstInt || token == PpAtomConstUint || + token == PpAtomConstInt64 || token == PpAtomConstUint64 || + token == PpAtomConstFloat16 || + token == PpAtomConstFloat || token == PpAtomConstDouble) { + message.append(ppToken->name); + } else if (token == PpAtomIdentifier || token == PpAtomConstString) { + message.append(ppToken->name); + } else { + message.append(atomStrings.getString(token)); + } + message.append(" "); + token = scanToken(ppToken); + } + parseContext.notifyErrorDirective(loc.line, message.c_str()); + // store this msg into the shader's information log..set the Compile Error flag!!!! + parseContext.ppError(loc, message.c_str(), "#error", ""); + + return '\n'; +} + +// Handle #pragma +int TPpContext::CPPpragma(TPpToken* ppToken) +{ + char SrcStrName[2]; + TVector tokens; + + TSourceLoc loc = ppToken->loc; // because we go to the next line before processing + int token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) { + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + tokens.push_back(ppToken->name); + break; + default: + SrcStrName[0] = (char)token; + SrcStrName[1] = '\0'; + tokens.push_back(SrcStrName); + } + token = scanToken(ppToken); + } + + if (token == EndOfInput) + parseContext.ppError(loc, "directive must end with a newline", "#pragma", ""); + else + parseContext.handlePragma(loc, tokens); + + return token; +} + +// #version: This is just for error checking: the version and profile are decided before preprocessing starts +int TPpContext::CPPversion(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (errorOnVersion || versionSeen) { + if (parseContext.isReadingHLSL()) + parseContext.ppError(ppToken->loc, "invalid preprocessor command", "#version", ""); + else + parseContext.ppError(ppToken->loc, "must occur first in shader", "#version", ""); + } + versionSeen = true; + + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + return token; + } + + if (token != PpAtomConstInt) + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + ppToken->ival = atoi(ppToken->name); + int versionNumber = ppToken->ival; + int line = ppToken->loc.line; + token = scanToken(ppToken); + + if (token == '\n') { + parseContext.notifyVersion(line, versionNumber, nullptr); + return token; + } else { + int profileAtom = atomStrings.getAtom(ppToken->name); + if (profileAtom != PpAtomCore && + profileAtom != PpAtomCompatibility && + profileAtom != PpAtomEs) + parseContext.ppError(ppToken->loc, "bad profile name; use es, core, or compatibility", "#version", ""); + parseContext.notifyVersion(line, versionNumber, ppToken->name); + token = scanToken(ppToken); + + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "bad tokens following profile -- expected newline", "#version", ""); + } + + return token; +} + +// Handle #extension +int TPpContext::CPPextension(TPpToken* ppToken) +{ + int line = ppToken->loc.line; + int token = scanToken(ppToken); + char extensionName[MaxTokenLength + 1]; + + if (token=='\n') { + parseContext.ppError(ppToken->loc, "extension name not specified", "#extension", ""); + return token; + } + + if (token != PpAtomIdentifier) + parseContext.ppError(ppToken->loc, "extension name expected", "#extension", ""); + + snprintf(extensionName, sizeof(extensionName), "%s", ppToken->name); + + token = scanToken(ppToken); + if (token != ':') { + parseContext.ppError(ppToken->loc, "':' missing after extension name", "#extension", ""); + return token; + } + + token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "behavior for extension not specified", "#extension", ""); + return token; + } + + parseContext.updateExtensionBehavior(line, extensionName, ppToken->name); + parseContext.notifyExtensionDirective(line, extensionName, ppToken->name); + + token = scanToken(ppToken); + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "extra tokens -- expected newline", "#extension",""); + + return token; +} + +int TPpContext::readCPPline(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (token == PpAtomIdentifier) { + switch (atomStrings.getAtom(ppToken->name)) { + case PpAtomDefine: + token = CPPdefine(ppToken); + break; + case PpAtomElse: + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + elseSeen[elsetracker] = true; + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#else", ""); + token = extraTokenCheck(PpAtomElse, ppToken, scanToken(ppToken)); + token = CPPelse(0, ppToken); + break; + case PpAtomElif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#elif", ""); + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + // this token is really a dont care, but we still need to eat the tokens + token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + token = CPPelse(0, ppToken); + break; + case PpAtomEndif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", ""); + else { + elseSeen[elsetracker] = false; + --elsetracker; + --ifdepth; + } + token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken)); + break; + case PpAtomIf: + token = CPPif(ppToken); + break; + case PpAtomIfdef: + token = CPPifdef(1, ppToken); + break; + case PpAtomIfndef: + token = CPPifdef(0, ppToken); + break; + case PpAtomLine: + token = CPPline(ppToken); + break; + case PpAtomInclude: + if(!parseContext.isReadingHLSL()) { + const std::array exts = { E_GL_GOOGLE_include_directive, E_GL_ARB_shading_language_include }; + parseContext.ppRequireExtensions(ppToken->loc, exts, "#include"); + } + token = CPPinclude(ppToken); + break; + case PpAtomPragma: + token = CPPpragma(ppToken); + break; + case PpAtomUndef: + token = CPPundef(ppToken); + break; + case PpAtomError: + token = CPPerror(ppToken); + break; + case PpAtomVersion: + token = CPPversion(ppToken); + break; + case PpAtomExtension: + token = CPPextension(ppToken); + break; + default: + parseContext.ppError(ppToken->loc, "invalid directive:", "#", ppToken->name); + break; + } + } else if (token != '\n' && token != EndOfInput) + parseContext.ppError(ppToken->loc, "invalid directive", "#", ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + return token; +} + +// Context-dependent parsing of a #include . +// Assumes no macro expansions etc. are being done; the name is just on the current input. +// Always creates a name and returns PpAtomicConstString, unless we run out of input. +int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit) +{ + bool tooLong = false; + + if (inputStack.empty()) + return EndOfInput; + + int len = 0; + ppToken->name[0] = '\0'; + do { + int ch = inputStack.back()->getch(); + + // done yet? + if (ch == delimit) { + ppToken->name[len] = '\0'; + if (tooLong) + parseContext.ppError(ppToken->loc, "header name too long", "", ""); + return PpAtomConstString; + } else if (ch == EndOfInput) + return EndOfInput; + + // found a character to expand the name with + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else + tooLong = true; + } while (true); +} + +// Macro-expand a macro argument 'arg' to create 'expandedArg'. +// Does not replace 'arg'. +// Returns nullptr if no expanded argument is created. +TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay) +{ + // expand the argument + TokenStream* expandedArg = new TokenStream; + pushInput(new tMarkerInput(this)); + pushTokenStreamInput(arg); + int token; + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) { + token = tokenPaste(token, *ppToken); + if (token == PpAtomIdentifier) { + switch (MacroExpand(ppToken, false, newLineOkay)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + // toss the rest of the pushed-input argument by scanning until tMarkerInput + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) + ; + break; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + if (token == tMarkerInput::marker || token == EndOfInput) + break; + expandedArg->putToken(token, ppToken); + } + + if (token != tMarkerInput::marker) { + // Error, or MacroExpand ate the marker, so had bad input, recover + delete expandedArg; + expandedArg = nullptr; + } + + return expandedArg; +} + +// +// Return the next token for a macro expansion, handling macro arguments, +// whose semantics are dependent on being adjacent to ##. +// +int TPpContext::tMacroInput::scan(TPpToken* ppToken) +{ + int token; + do { + token = mac->body.getToken(pp->parseContext, ppToken); + } while (token == ' '); // handle white space in macro + + // Hash operators basically turn off a round of macro substitution + // (the round done on the argument before the round done on the RHS of the + // macro definition): + // + // "A parameter in the replacement list, unless preceded by a # or ## + // preprocessing token or followed by a ## preprocessing token (see below), + // is replaced by the corresponding argument after all macros contained + // therein have been expanded." + // + // "If, in the replacement list, a parameter is immediately preceded or + // followed by a ## preprocessing token, the parameter is replaced by the + // corresponding argument's preprocessing token sequence." + + bool pasting = false; + if (postpaste) { + // don't expand next token + pasting = true; + postpaste = false; + } + + if (prepaste) { + // already know we should be on a ##, verify + assert(token == PpAtomPaste); + prepaste = false; + postpaste = true; + } + + // see if are preceding a ## + if (mac->body.peekUntokenizedPasting()) { + prepaste = true; + pasting = true; + } + + + // TODO: preprocessor: properly handle whitespace (or lack of it) between tokens when expanding + if (token == PpAtomIdentifier) { + int i; + for (i = (int)mac->args.size() - 1; i >= 0; i--) + if (strcmp(pp->atomStrings.getString(mac->args[i]), ppToken->name) == 0) + break; + if (i >= 0) { + TokenStream* arg = expandedArgs[i]; + bool expanded = !!arg && !pasting; + // HLSL does expand macros before concatenation + if (arg == nullptr || (pasting && !pp->parseContext.isReadingHLSL()) ) { + arg = args[i]; + } + pp->pushTokenStreamInput(*arg, prepaste, expanded); + + return pp->scanToken(ppToken); + } + } + + if (token == EndOfInput) + mac->busy = 0; + + return token; +} + +// return a textual zero, for scanning a macro that was never defined +int TPpContext::tZeroInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + ppToken->name[0] = '0'; + ppToken->name[1] = 0; + ppToken->ival = 0; + ppToken->space = false; + done = true; + + return PpAtomConstInt; +} + +// +// Check a token to see if it is a macro that should be expanded: +// - If it is, and defined, push a tInput that will produce the appropriate +// expansion and return MacroExpandStarted. +// - If it is, but undefined, and expandUndef is requested, push a tInput +// that will expand to 0 and return MacroExpandUndef. +// - Otherwise, there is no expansion, and there are two cases: +// * It might be okay there is no expansion, and no specific error was +// detected. Returns MacroExpandNotStarted. +// * The expansion was started, but could not be completed, due to an error +// that cannot be recovered from. Returns MacroExpandError. +// +MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay) +{ + ppToken->space = false; + int macroAtom = atomStrings.getAtom(ppToken->name); + if (ppToken->fullyExpanded) + return MacroExpandNotStarted; + + switch (macroAtom) { + case PpAtomLineMacro: + // Arguments which are macro have been replaced in the first stage. + if (ppToken->ival == 0) + ppToken->ival = parseContext.getCurrentLoc().line; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + case PpAtomFileMacro: { + if (parseContext.getCurrentLoc().name) + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + ppToken->ival = parseContext.getCurrentLoc().string; + snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str()); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + } + + case PpAtomVersionMacro: + ppToken->ival = parseContext.version; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + default: + break; + } + + MacroSymbol* macro = macroAtom == 0 ? nullptr : lookupMacroDef(macroAtom); + + // no recursive expansions + if (macro != nullptr && macro->busy) { + ppToken->fullyExpanded = true; + return MacroExpandNotStarted; + } + + // not expanding undefined macros + if ((macro == nullptr || macro->undef) && ! expandUndef) + return MacroExpandNotStarted; + + // 0 is the value of an undefined macro + if ((macro == nullptr || macro->undef) && expandUndef) { + pushInput(new tZeroInput(this)); + return MacroExpandUndef; + } + + tMacroInput *in = new tMacroInput(this); + + TSourceLoc loc = ppToken->loc; // in case we go to the next line before discovering the error + in->mac = macro; + if (macro->functionLike) { + // We don't know yet if this will be a successful call of a + // function-like macro; need to look for a '(', but without trashing + // the passed in ppToken, until we know we are no longer speculative. + TPpToken parenToken; + int token = scanToken(&parenToken); + if (newLineOkay) { + while (token == '\n') + token = scanToken(&parenToken); + } + if (token != '(') { + // Function-like macro called with object-like syntax: okay, don't expand. + // (We ate exactly one token that might not be white space; put it back. + UngetToken(token, &parenToken); + delete in; + return MacroExpandNotStarted; + } + in->args.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->args[i] = new TokenStream; + in->expandedArgs.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = nullptr; + size_t arg = 0; + bool tokenRecorded = false; + do { + TVector nestStack; + while (true) { + token = scanToken(ppToken); + if (token == EndOfInput || token == tMarkerInput::marker) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (token == '\n') { + if (! newLineOkay) { + parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + continue; + } + if (token == '#') { + parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (in->mac->args.size() == 0 && token != ')') + break; + if (nestStack.size() == 0 && (token == ',' || token == ')')) + break; + if (token == '(') + nestStack.push_back(')'); + else if (token == '{' && parseContext.isReadingHLSL()) + nestStack.push_back('}'); + else if (nestStack.size() > 0 && token == nestStack.back()) + nestStack.pop_back(); + + //Macro replacement list is expanded in the last stage. + if (atomStrings.getAtom(ppToken->name) == PpAtomLineMacro) + ppToken->ival = parseContext.getCurrentLoc().line; + + in->args[arg]->putToken(token, ppToken); + tokenRecorded = true; + } + // end of single argument scan + + if (token == ')') { + // closing paren of call + if (in->mac->args.size() == 1 && !tokenRecorded) + break; + arg++; + break; + } + arg++; + } while (arg < in->mac->args.size()); + // end of all arguments scan + + if (arg < in->mac->args.size()) + parseContext.ppError(loc, "Too few args in Macro", "macro expansion", atomStrings.getString(macroAtom)); + else if (token != ')') { + // Error recover code; find end of call, if possible + int depth = 0; + while (token != EndOfInput && (depth > 0 || token != ')')) { + if (token == ')' || token == '}') + depth--; + token = scanToken(ppToken); + if (token == '(' || token == '{') + depth++; + } + + if (token == EndOfInput) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + parseContext.ppError(loc, "Too many args in macro", "macro expansion", atomStrings.getString(macroAtom)); + } + + // We need both expanded and non-expanded forms of the argument, for whether or + // not token pasting will be applied later when the argument is consumed next to ##. + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay); + } + + pushInput(in); + macro->busy = 1; + macro->body.reset(); + + return MacroExpandStarted; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp new file mode 100644 index 00000000..06c2333e --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp @@ -0,0 +1,181 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace { + +using namespace glslang; + +const struct { + int val; + const char* str; +} tokens[] = { + + { PPAtomAddAssign, "+=" }, + { PPAtomSubAssign, "-=" }, + { PPAtomMulAssign, "*=" }, + { PPAtomDivAssign, "/=" }, + { PPAtomModAssign, "%=" }, + + { PpAtomRight, ">>" }, + { PpAtomLeft, "<<" }, + { PpAtomAnd, "&&" }, + { PpAtomOr, "||" }, + { PpAtomXor, "^^" }, + + { PpAtomRightAssign, ">>=" }, + { PpAtomLeftAssign, "<<=" }, + { PpAtomAndAssign, "&=" }, + { PpAtomOrAssign, "|=" }, + { PpAtomXorAssign, "^=" }, + + { PpAtomEQ, "==" }, + { PpAtomNE, "!=" }, + { PpAtomGE, ">=" }, + { PpAtomLE, "<=" }, + + { PpAtomDecrement, "--" }, + { PpAtomIncrement, "++" }, + + { PpAtomColonColon, "::" }, + + { PpAtomDefine, "define" }, + { PpAtomUndef, "undef" }, + { PpAtomIf, "if" }, + { PpAtomElif, "elif" }, + { PpAtomElse, "else" }, + { PpAtomEndif, "endif" }, + { PpAtomIfdef, "ifdef" }, + { PpAtomIfndef, "ifndef" }, + { PpAtomLine, "line" }, + { PpAtomPragma, "pragma" }, + { PpAtomError, "error" }, + + { PpAtomVersion, "version" }, + { PpAtomCore, "core" }, + { PpAtomCompatibility, "compatibility" }, + { PpAtomEs, "es" }, + { PpAtomExtension, "extension" }, + + { PpAtomLineMacro, "__LINE__" }, + { PpAtomFileMacro, "__FILE__" }, + { PpAtomVersionMacro, "__VERSION__" }, + + { PpAtomInclude, "include" }, +}; + +} // end anonymous namespace + +namespace glslang { + +// +// Initialize the atom table. +// +TStringAtomMap::TStringAtomMap() +{ + badToken.assign(""); + + // Add single character tokens to the atom table: + const char* s = "~!%^&*()-+=|,.<>/?;:[]{}#\\"; + char t[2]; + + t[1] = '\0'; + while (*s) { + t[0] = *s; + addAtomFixed(t, s[0]); + s++; + } + + // Add multiple character scanner tokens : + for (size_t ii = 0; ii < sizeof(tokens)/sizeof(tokens[0]); ii++) + addAtomFixed(tokens[ii].str, tokens[ii].val); + + nextAtom = PpAtomLast; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp new file mode 100644 index 00000000..f27204bc --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -0,0 +1,121 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#include +#include + +#include "PpContext.h" + +namespace glslang { + +TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, TShader::Includer& inclr) : + preamble(nullptr), strings(nullptr), previous_token('\n'), parseContext(pc), includer(inclr), inComment(false), + rootFileName(rootFileName), + currentSourceFile(rootFileName), + disableEscapeSequences(false), + inElseSkip(false) +{ + ifdepth = 0; + for (elsetracker = 0; elsetracker < maxIfNesting; elsetracker++) + elseSeen[elsetracker] = false; + elsetracker = 0; + + strtodStream.imbue(std::locale::classic()); +} + +TPpContext::~TPpContext() +{ + delete [] preamble; + + // free up the inputStack + while (! inputStack.empty()) + popInput(); +} + +void TPpContext::setInput(TInputScanner& input, bool versionWillBeError) +{ + assert(inputStack.size() == 0); + + pushInput(new tStringInput(this, input)); + + errorOnVersion = versionWillBeError; + versionSeen = false; +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.h new file mode 100644 index 00000000..3446f1e1 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpContext.h @@ -0,0 +1,742 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PPCONTEXT_H +#define PPCONTEXT_H + +#include +#include +#include + +#include "../ParseHelper.h" +#include "PpTokens.h" + +namespace glslang { + +class TPpToken { +public: + TPpToken() { clear(); } + void clear() + { + space = false; + i64val = 0; + loc.init(); + name[0] = 0; + fullyExpanded = false; + } + + // Used for comparing macro definitions, so checks what is relevant for that. + bool operator==(const TPpToken& right) const + { + return space == right.space && + ival == right.ival && dval == right.dval && i64val == right.i64val && + strncmp(name, right.name, MaxTokenLength) == 0; + } + bool operator!=(const TPpToken& right) const { return ! operator==(right); } + + TSourceLoc loc; + // True if a space (for white space or a removed comment) should also be + // recognized, in front of the token returned: + bool space; + + bool fullyExpanded; + // Numeric value of the token: + union { + int ival; + double dval; + long long i64val; + }; + // Text string of the token: + char name[MaxTokenLength + 1]; +}; + +class TStringAtomMap { +// +// Implementation is in PpAtom.cpp +// +// Maintain a bi-directional mapping between relevant preprocessor strings and +// "atoms" which a unique integers (small, contiguous, not hash-like) per string. +// +public: + TStringAtomMap(); + + // Map string -> atom. + // Return 0 if no existing string. + int getAtom(const char* s) const + { + auto it = atomMap.find(s); + return it == atomMap.end() ? 0 : it->second; + } + + // Map a new or existing string -> atom, inventing a new atom if necessary. + int getAddAtom(const char* s) + { + int atom = getAtom(s); + if (atom == 0) { + atom = nextAtom++; + addAtomFixed(s, atom); + } + return atom; + } + + // Map atom -> string. + const char* getString(int atom) const { return stringMap[atom]->c_str(); } + +protected: + TStringAtomMap(TStringAtomMap&); + TStringAtomMap& operator=(TStringAtomMap&); + + TUnorderedMap atomMap; + TVector stringMap; // these point into the TString in atomMap + int nextAtom; + + // Bad source characters can lead to bad atoms, so gracefully handle those by + // pre-filling the table with them (to avoid if tests later). + TString badToken; + + // Add bi-directional mappings: + // - string -> atom + // - atom -> string + void addAtomFixed(const char* s, int atom) + { + auto it = atomMap.insert(std::pair(s, atom)).first; + if (stringMap.size() < (size_t)atom + 1) + stringMap.resize(atom + 100, &badToken); + stringMap[atom] = &it->first; + } +}; + +class TInputScanner; + +enum MacroExpandResult { + MacroExpandNotStarted, // macro not expanded, which might not be an error + MacroExpandError, // a clear error occurred while expanding, no expansion + MacroExpandStarted, // macro expansion process has started + MacroExpandUndef // macro is undefined and will be expanded +}; + +// This class is the result of turning a huge pile of C code communicating through globals +// into a class. This was done to allowing instancing to attain thread safety. +// Don't expect too much in terms of OO design. +class TPpContext { +public: + TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&); + virtual ~TPpContext(); + + void setPreamble(const char* preamble, size_t length); + + int tokenize(TPpToken& ppToken); + int tokenPaste(int token, TPpToken&); + + class tInput { + public: + tInput(TPpContext* p) : done(false), pp(p) { } + virtual ~tInput() { } + + virtual int scan(TPpToken*) = 0; + virtual int getch() = 0; + virtual void ungetch() = 0; + virtual bool peekPasting() { return false; } // true when about to see ## + virtual bool peekContinuedPasting(int) { return false; } // true when non-spaced tokens can paste + virtual bool endOfReplacementList() { return false; } // true when at the end of a macro replacement list (RHS of #define) + virtual bool isMacroInput() { return false; } + virtual bool isStringInput() { return false; } + + // Will be called when we start reading tokens from this instance + virtual void notifyActivated() {} + // Will be called when we do not read tokens from this instance anymore + virtual void notifyDeleted() {} + protected: + bool done; + TPpContext* pp; + }; + + void setInput(TInputScanner& input, bool versionWillBeError); + + void pushInput(tInput* in) + { + inputStack.push_back(in); + in->notifyActivated(); + } + void popInput() + { + inputStack.back()->notifyDeleted(); + delete inputStack.back(); + inputStack.pop_back(); + } + + // + // From PpTokens.cpp + // + + // Capture the needed parts of a token stream for macro recording/playback. + class TokenStream { + public: + // Manage a stream of these 'Token', which capture the relevant parts + // of a TPpToken, plus its atom. + class Token { + public: + Token(int atom, const TPpToken& ppToken) : + atom(atom), + space(ppToken.space), + i64val(ppToken.i64val), + name(ppToken.name) { } + int get(TPpToken& ppToken) + { + ppToken.clear(); + ppToken.space = space; + ppToken.i64val = i64val; + snprintf(ppToken.name, sizeof(ppToken.name), "%s", name.c_str()); + return atom; + } + bool isAtom(int a) const { return atom == a; } + int getAtom() const { return atom; } + bool nonSpaced() const { return !space; } + protected: + Token() {} + int atom; + bool space; // did a space precede the token? + long long i64val; + TString name; + }; + + TokenStream() : currentPos(0) { } + + void putToken(int token, TPpToken* ppToken); + bool peekToken(int atom) { return !atEnd() && stream[currentPos].isAtom(atom); } + bool peekContinuedPasting(int atom) + { + // This is basically necessary because, for example, the PP + // tokenizer only accepts valid numeric-literals plus suffixes, so + // separates numeric-literals plus bad suffix into two tokens, which + // should get both pasted together as one token when token pasting. + // + // The following code is a bit more generalized than the above example. + if (!atEnd() && atom == PpAtomIdentifier && stream[currentPos].nonSpaced()) { + switch(stream[currentPos].getAtom()) { + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + case PpAtomConstString: + case PpAtomIdentifier: + return true; + default: + break; + } + } + + return false; + } + int getToken(TParseContextBase&, TPpToken*); + bool atEnd() { return currentPos >= stream.size(); } + bool peekTokenizedPasting(bool lastTokenPastes); + bool peekUntokenizedPasting(); + void reset() { currentPos = 0; } + + protected: + TVector stream; + size_t currentPos; + }; + + // + // From Pp.cpp + // + + struct MacroSymbol { + MacroSymbol() : functionLike(0), busy(0), undef(0) { } + TVector args; + TokenStream body; + unsigned functionLike : 1; // 0 means object-like, 1 means function-like + unsigned busy : 1; + unsigned undef : 1; + }; + + typedef TMap TSymbolMap; + TSymbolMap macroDefs; // map atoms to macro definitions + MacroSymbol* lookupMacroDef(int atom) + { + auto existingMacroIt = macroDefs.find(atom); + return (existingMacroIt == macroDefs.end()) ? nullptr : &(existingMacroIt->second); + } + void addMacroDef(int atom, MacroSymbol& macroDef) { macroDefs[atom] = macroDef; } + +protected: + TPpContext(TPpContext&); + TPpContext& operator=(TPpContext&); + + TStringAtomMap atomStrings; + char* preamble; // string to parse, all before line 1 of string 0, it is 0 if no preamble + int preambleLength; + char** strings; // official strings of shader, starting a string 0 line 1 + size_t* lengths; + int numStrings; // how many official strings there are + int currentString; // which string we're currently parsing (-1 for preamble) + + // Scanner data: + int previous_token; + TParseContextBase& parseContext; + std::vector lastLineTokens; + std::vector lastLineTokenLocs; + // Get the next token from *stack* of input sources, popping input sources + // that are out of tokens, down until an input source is found that has a token. + // Return EndOfInput when there are no more tokens to be found by doing this. + int scanToken(TPpToken* ppToken) + { + int token = EndOfInput; + + while (! inputStack.empty()) { + token = inputStack.back()->scan(ppToken); + if (token != EndOfInput || inputStack.empty()) + break; + popInput(); + } + if (!inputStack.empty() && inputStack.back()->isStringInput() && !inElseSkip) { + if (token == '\n') { + bool seenNumSign = false; + for (int i = 0; i < (int)lastLineTokens.size() - 1;) { + int curPos = i; + int curToken = lastLineTokens[i++]; + if (curToken == '#' && lastLineTokens[i] == '#') { + curToken = PpAtomPaste; + i++; + } + if (curToken == '#') { + if (seenNumSign) { + parseContext.ppError(lastLineTokenLocs[curPos], "(#) can be preceded in its line only by spaces or horizontal tabs", "#", ""); + } else { + seenNumSign = true; + } + } + } + lastLineTokens.clear(); + lastLineTokenLocs.clear(); + } else { + lastLineTokens.push_back(token); + lastLineTokenLocs.push_back(ppToken->loc); + } + } + return token; + } + int getChar() { return inputStack.back()->getch(); } + void ungetChar() { inputStack.back()->ungetch(); } + bool peekPasting() { return !inputStack.empty() && inputStack.back()->peekPasting(); } + bool peekContinuedPasting(int a) + { + return !inputStack.empty() && inputStack.back()->peekContinuedPasting(a); + } + bool endOfReplacementList() { return inputStack.empty() || inputStack.back()->endOfReplacementList(); } + bool isMacroInput() { return inputStack.size() > 0 && inputStack.back()->isMacroInput(); } + + static const int maxIfNesting = 65; + + int ifdepth; // current #if-#else-#endif nesting in the cpp.c file (pre-processor) + bool elseSeen[maxIfNesting]; // Keep a track of whether an else has been seen at a particular depth + int elsetracker; // #if-#else and #endif constructs...Counter. + + class tMacroInput : public tInput { + public: + tMacroInput(TPpContext* pp) : tInput(pp), prepaste(false), postpaste(false) { } + virtual ~tMacroInput() + { + for (size_t i = 0; i < args.size(); ++i) + delete args[i]; + for (size_t i = 0; i < expandedArgs.size(); ++i) + delete expandedArgs[i]; + } + + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + bool peekPasting() override { return prepaste; } + bool peekContinuedPasting(int a) override { return mac->body.peekContinuedPasting(a); } + bool endOfReplacementList() override { return mac->body.atEnd(); } + bool isMacroInput() override { return true; } + + MacroSymbol *mac; + TVector args; + TVector expandedArgs; + + protected: + bool prepaste; // true if we are just before ## + bool postpaste; // true if we are right after ## + }; + + class tMarkerInput : public tInput { + public: + tMarkerInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override + { + if (done) + return EndOfInput; + done = true; + + return marker; + } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + static const int marker = -3; + }; + + class tZeroInput : public tInput { + public: + tZeroInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + }; + + std::vector inputStack; + bool errorOnVersion; + bool versionSeen; + + // + // from Pp.cpp + // + + // Used to obtain #include content. + TShader::Includer& includer; + + int CPPdefine(TPpToken * ppToken); + int CPPundef(TPpToken * ppToken); + int CPPelse(int matchelse, TPpToken * ppToken); + int extraTokenCheck(int atom, TPpToken* ppToken, int token); + int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int CPPif (TPpToken * ppToken); + int CPPifdef(int defined, TPpToken * ppToken); + int CPPinclude(TPpToken * ppToken); + int CPPline(TPpToken * ppToken); + int CPPerror(TPpToken * ppToken); + int CPPpragma(TPpToken * ppToken); + int CPPversion(TPpToken * ppToken); + int CPPextension(TPpToken * ppToken); + int readCPPline(TPpToken * ppToken); + int scanHeaderName(TPpToken* ppToken, char delimit); + TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay); + MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay); + + // + // From PpTokens.cpp + // + void pushTokenStreamInput(TokenStream&, bool pasting = false, bool expanded = false); + void UngetToken(int token, TPpToken*); + + class tTokenInput : public tInput { + public: + tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting, bool expanded) : + tInput(pp), + tokens(t), + lastTokenPastes(prepasting), + preExpanded(expanded) { } + virtual int scan(TPpToken *ppToken) override { + int token = tokens->getToken(pp->parseContext, ppToken); + ppToken->fullyExpanded = preExpanded; + if (tokens->atEnd() && token == PpAtomIdentifier) { + int macroAtom = pp->atomStrings.getAtom(ppToken->name); + MacroSymbol* macro = macroAtom == 0 ? nullptr : pp->lookupMacroDef(macroAtom); + if (macro && macro->functionLike) + ppToken->fullyExpanded = false; + } + return token; + } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); } + bool peekContinuedPasting(int a) override { return tokens->peekContinuedPasting(a); } + protected: + TokenStream* tokens; + bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token + bool preExpanded; + }; + + class tUngotTokenInput : public tInput { + public: + tUngotTokenInput(TPpContext* pp, int t, TPpToken* p) : tInput(pp), token(t), lval(*p) { } + virtual int scan(TPpToken *) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + protected: + int token; + TPpToken lval; + }; + + // + // From PpScanner.cpp + // + class tStringInput : public tInput { + public: + tStringInput(TPpContext* pp, TInputScanner& i) : tInput(pp), input(&i) { } + virtual int scan(TPpToken*) override; + bool isStringInput() override { return true; } + // Scanner used to get source stream characters. + // - Escaped newlines are handled here, invisibly to the caller. + // - All forms of newline are handled, and turned into just a '\n'. + int getch() override + { + int ch = input->get(); + + if (ch == '\\') { + // Move past escaped newlines, as many as sequentially exist + do { + if (input->peek() == '\r' || input->peek() == '\n') { + bool allowed = pp->parseContext.lineContinuationCheck(input->getSourceLoc(), pp->inComment); + if (! allowed && pp->inComment) + return '\\'; + + // escape one newline now + ch = input->get(); + int nextch = input->get(); + if (ch == '\r' && nextch == '\n') + ch = input->get(); + else + ch = nextch; + } else + return '\\'; + } while (ch == '\\'); + } + + // handle any non-escaped newline + if (ch == '\r' || ch == '\n') { + if (ch == '\r' && input->peek() == '\n') + input->get(); + return '\n'; + } + + return ch; + } + + // Scanner used to backup the source stream characters. Newlines are + // handled here, invisibly to the caller, meaning have to undo exactly + // what getch() above does (e.g., don't leave things in the middle of a + // sequence of escaped newlines). + void ungetch() override + { + input->unget(); + + do { + int ch = input->peek(); + if (ch == '\r' || ch == '\n') { + if (ch == '\n') { + // correct for two-character newline + input->unget(); + if (input->peek() != '\r') + input->get(); + } + // now in front of a complete newline, move past an escape character + input->unget(); + if (input->peek() == '\\') + input->unget(); + else { + input->get(); + break; + } + } else + break; + } while (true); + } + + protected: + TInputScanner* input; + }; + + // Holds a reference to included file data, as well as a + // prologue and an epilogue string. This can be scanned using the tInput + // interface and acts as a single source string. + class TokenizableIncludeFile : public tInput { + public: + // Copies prologue and epilogue. The includedFile must remain valid + // until this TokenizableIncludeFile is no longer used. + TokenizableIncludeFile(const TSourceLoc& startLoc, + const std::string& prologue, + TShader::Includer::IncludeResult* includedFile, + const std::string& epilogue, + TPpContext* pp) + : tInput(pp), + prologue_(prologue), + epilogue_(epilogue), + includedFile_(includedFile), + scanner(3, strings, lengths, nullptr, 0, 0, true), + prevScanner(nullptr), + stringInput(pp, scanner) + { + strings[0] = prologue_.data(); + strings[1] = includedFile_->headerData; + strings[2] = epilogue_.data(); + + lengths[0] = prologue_.size(); + lengths[1] = includedFile_->headerLength; + lengths[2] = epilogue_.size(); + + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + + scanner.setFile(startLoc.getFilenameStr(), 0); + scanner.setFile(startLoc.getFilenameStr(), 1); + scanner.setFile(startLoc.getFilenameStr(), 2); + } + + // tInput methods: + int scan(TPpToken* t) override { return stringInput.scan(t); } + int getch() override { return stringInput.getch(); } + void ungetch() override { stringInput.ungetch(); } + + void notifyActivated() override + { + prevScanner = pp->parseContext.getScanner(); + pp->parseContext.setScanner(&scanner); + pp->push_include(includedFile_); + } + + void notifyDeleted() override + { + pp->parseContext.setScanner(prevScanner); + pp->pop_include(); + } + + private: + TokenizableIncludeFile& operator=(const TokenizableIncludeFile&); + + // Stores the prologue for this string. + const std::string prologue_; + + // Stores the epilogue for this string. + const std::string epilogue_; + + // Points to the IncludeResult that this TokenizableIncludeFile represents. + TShader::Includer::IncludeResult* includedFile_; + + // Will point to prologue_, includedFile_->headerData and epilogue_ + // This is passed to scanner constructor. + // These do not own the storage and it must remain valid until this + // object has been destroyed. + const char* strings[3]; + // Length of str_, passed to scanner constructor. + size_t lengths[3]; + // Scans over str_. + TInputScanner scanner; + // The previous effective scanner before the scanner in this instance + // has been activated. + TInputScanner* prevScanner; + // Delegate object implementing the tInput interface. + tStringInput stringInput; + }; + + int ScanFromString(char* s); + void missingEndifCheck(); + int lFloatConst(int len, int ch, TPpToken* ppToken); + int characterLiteral(TPpToken* ppToken); + + void push_include(TShader::Includer::IncludeResult* result) + { + currentSourceFile = result->headerName; + includeStack.push(result); + } + + void pop_include() + { + TShader::Includer::IncludeResult* include = includeStack.top(); + includeStack.pop(); + includer.releaseInclude(include); + if (includeStack.empty()) { + currentSourceFile = rootFileName; + } else { + currentSourceFile = includeStack.top()->headerName; + } + } + + bool inComment; + std::string rootFileName; + std::stack includeStack; + std::string currentSourceFile; + + std::istringstream strtodStream; + bool disableEscapeSequences; + // True if we're skipping a section enclosed by #if/#ifdef/#elif/#else which was evaluated to + // be inactive, e.g. #if 0 + bool inElseSkip; +}; + +} // end namespace glslang + +#endif // PPCONTEXT_H diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp new file mode 100644 index 00000000..49dafa59 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -0,0 +1,1410 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (c) 2023, Mobica Limited +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" +#include "../Scan.h" + +namespace glslang { + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Floating point constants: ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +// +// Scan a single- or double-precision floating point constant. +// Assumes that the scanner has seen at least one digit, +// followed by either a decimal '.' or the letter 'e', or a +// precision ending (e.g., F or LF). +// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// +// See peekContinuedPasting(). +int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) +{ + const auto saveName = [&](int ch) { + if (len <= MaxTokenLength) + ppToken->name[len++] = static_cast(ch); + }; + + // find the range of non-zero digits before the decimal point + int startNonZero = 0; + while (startNonZero < len && ppToken->name[startNonZero] == '0') + ++startNonZero; + int endNonZero = len; + while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0') + --endNonZero; + int numWholeNumberDigits = endNonZero - startNonZero; + + // accumulate the range's value + bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false + unsigned long long wholeNumber = 0; + if (fastPath) { + for (int i = startNonZero; i < endNonZero; ++i) + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + int decimalShift = len - endNonZero; + + // Decimal point: + bool hasDecimalOrExponent = false; + if (ch == '.') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + int firstDecimal = len; + +#ifdef ENABLE_HLSL + // 1.#INF or -1.#INF + if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) { + if ((len < 2) || + (len == 2 && ppToken->name[0] != '1') || + (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) || + (len > 3)) + parseContext.ppError(ppToken->loc, "unexpected use of", "#", ""); + else { + // we have 1.# or -1.# or +1.#, check for 'INF' + if ((ch = getChar()) != 'I' || + (ch = getChar()) != 'N' || + (ch = getChar()) != 'F') + parseContext.ppError(ppToken->loc, "expected 'INF'", "#", ""); + else { + // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up: + saveName('I'); + saveName('N'); + saveName('F'); + ppToken->name[len] = '\0'; + if (ppToken->name[0] == '-') + ppToken->i64val = 0xfff0000000000000; // -Infinity + else + ppToken->i64val = 0x7ff0000000000000; // +Infinity + return PpAtomConstFloat; + } + } + } +#endif + + // Consume leading-zero digits after the decimal point + while (ch == '0') { + saveName(ch); + ch = getChar(); + } + int startNonZeroDecimal = len; + int endNonZeroDecimal = len; + + // Consume remaining digits, up to the exponent + while (ch >= '0' && ch <= '9') { + saveName(ch); + if (ch != '0') + endNonZeroDecimal = len; + ch = getChar(); + } + + // Compute accumulation up to the last non-zero digit + if (endNonZeroDecimal > startNonZeroDecimal) { + numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "." + if (numWholeNumberDigits > 15) + fastPath = false; + if (fastPath) { + for (int i = endNonZero; i < endNonZeroDecimal; ++i) { + if (ppToken->name[i] != '.') + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + } + decimalShift = firstDecimal - endNonZeroDecimal; + } + } + + // Exponent: + bool negativeExponent = false; + double exponentValue = 0.0; + int exponent = 0; + { + if (ch == 'e' || ch == 'E') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + if (ch == '+' || ch == '-') { + negativeExponent = ch == '-'; + saveName(ch); + ch = getChar(); + } + if (ch >= '0' && ch <= '9') { + while (ch >= '0' && ch <= '9') { + if (exponent < 500) { + exponent = exponent * 10 + (ch - '0'); + } + saveName(ch); + ch = getChar(); + } + } else { + parseContext.ppError(ppToken->loc, "bad character in float exponent", "", ""); + } + } + + // Compensate for location of decimal + if (negativeExponent) + exponent -= decimalShift; + else { + exponent += decimalShift; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + } + if (exponent > 22) + fastPath = false; + + if (fastPath) { + // Compute the floating-point value of the exponent + exponentValue = 1.0; + if (exponent > 0) { + double expFactor = 10; + while (exponent > 0) { + if (exponent & 0x1) + exponentValue *= expFactor; + expFactor *= expFactor; + exponent >>= 1; + } + } + } + } + + // Suffix: + bool isDouble = false; + bool isFloat16 = false; + if (ch == 'l' || ch == 'L') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.doubleCheck(ppToken->loc, "double floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isDouble = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isDouble = true; + } + } else if (ch == 'h' || ch == 'H') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.float16Check(ppToken->loc, "half floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isFloat16 = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isFloat16 = true; + } + } else + if (ch == 'f' || ch == 'F') { + if (ifdepth == 0) + parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix"); + if (ifdepth == 0 && !parseContext.relaxedErrors()) + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + saveName(ch); + } else + ungetChar(); + + // Patch up the name and length for overflow + + if (len > MaxTokenLength) { + len = MaxTokenLength; + parseContext.ppError(ppToken->loc, "float literal too long", "", ""); + } + ppToken->name[len] = '\0'; + + // Compute the numerical value + if (fastPath) { + // compute the floating-point value of the exponent + if (exponentValue == 0.0) + ppToken->dval = (double)wholeNumber; + else if (negativeExponent) + ppToken->dval = (double)wholeNumber / exponentValue; + else + ppToken->dval = (double)wholeNumber * exponentValue; + } else { + // slow path + ppToken->dval = 0.0; + + // remove suffix + TString numstr(ppToken->name); + if (numstr.back() == 'f' || numstr.back() == 'F') + numstr.pop_back(); + if (numstr.back() == 'h' || numstr.back() == 'H') + numstr.pop_back(); + if (numstr.back() == 'l' || numstr.back() == 'L') + numstr.pop_back(); + + // use platform library + strtodStream.clear(); + strtodStream.str(numstr.c_str()); + strtodStream >> ppToken->dval; + if (strtodStream.fail()) { + // Assume failure combined with a large exponent was overflow, in + // an attempt to set INF. + if (!negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->i64val = 0x7ff0000000000000; // +Infinity + // Assume failure combined with a small exponent was overflow. + if (negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->dval = 0.0; + // Unknown reason for failure. Theory is that either + // - the 0.0 is still there, or + // - something reasonable was written that is better than 0.0 + } + } + + // Return the right token type + if (isDouble) + return PpAtomConstDouble; + else if (isFloat16) + return PpAtomConstFloat16; + else + return PpAtomConstFloat; +} + +// Recognize a character literal. +// +// The first ' has already been accepted, read the rest, through the closing '. +// +// Always returns PpAtomConstInt. +// +int TPpContext::characterLiteral(TPpToken* ppToken) +{ + ppToken->name[0] = 0; + ppToken->ival = 0; + + if (parseContext.intermediate.getSource() != EShSourceHlsl) { + // illegal, except in macro definition, for which case we report the character + return '\''; + } + + int ch = getChar(); + switch (ch) { + case '\'': + // As empty sequence: '' + parseContext.ppError(ppToken->loc, "unexpected", "\'", ""); + return PpAtomConstInt; + case '\\': + // As escape sequence: '\XXX' + switch (ch = getChar()) { + case 'a': + ppToken->ival = 7; + break; + case 'b': + ppToken->ival = 8; + break; + case 't': + ppToken->ival = 9; + break; + case 'n': + ppToken->ival = 10; + break; + case 'v': + ppToken->ival = 11; + break; + case 'f': + ppToken->ival = 12; + break; + case 'r': + ppToken->ival = 13; + break; + case 'x': + case '0': + parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", ""); + break; + default: + // This catches '\'', '\"', '\?', etc. + // Also, things like '\C' mean the same thing as 'C' + // (after the above cases are filtered out). + ppToken->ival = ch; + break; + } + break; + default: + ppToken->ival = ch; + break; + } + ppToken->name[0] = (char)ppToken->ival; + ppToken->name[1] = '\0'; + ch = getChar(); + if (ch != '\'') { + parseContext.ppError(ppToken->loc, "expected", "\'", ""); + // Look ahead for a closing ' + do { + ch = getChar(); + } while (ch != '\'' && ch != EndOfInput && ch != '\n'); + } + + return PpAtomConstInt; +} + +// +// Scanner used to tokenize source stream. +// +// N.B. Invalid numeric suffixes are not consumed.// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// See peekContinuedPasting(). +// +int TPpContext::tStringInput::scan(TPpToken* ppToken) +{ + int AlreadyComplained = 0; + int len = 0; + int ch = 0; + int ii = 0; + unsigned long long ival = 0; + const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' || + ch == 'f' || ch == 'F' || + ch == 'h' || ch == 'H'; }; + + static const char* const Int64_Extensions[] = { + E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64 }; + static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]); + + static const char* const Int16_Extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16 }; + static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]); + + ppToken->clear(); + ch = getch(); + for (;;) { + while (ch == ' ' || ch == '\t') { + ppToken->space = true; + ch = getch(); + } + + ppToken->loc = pp->parseContext.getCurrentLoc(); + len = 0; + switch (ch) { + default: + // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token) + if (ch > PpAtomMaxSingle) + ch = PpAtomBadToken; + return ch; + + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + do { + if (len < MaxTokenLength) { + ppToken->name[len++] = (char)ch; + ch = getch(); + } else { + if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "name too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } + } while ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '_'); + + // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc. + if (len == 0) + continue; + + ppToken->name[len] = '\0'; + ungetch(); + return PpAtomIdentifier; + case '0': + ppToken->name[len++] = (char)ch; + ch = getch(); + if (ch == 'x' || ch == 'X') { + // must be hexadecimal + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + ppToken->name[len++] = (char)ch; + ch = getch(); + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + + ival = 0; + do { + if (len < MaxTokenLength && ival <= 0x7fffffffffffffffull) { + ppToken->name[len++] = (char)ch; + if (ch >= '0' && ch <= '9') { + ii = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + ii = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + ii = ch - 'a' + 10; + } else + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + ival = (ival << 4) | ii; + } else { + if (! AlreadyComplained) { + if(len < MaxTokenLength) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + else + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", ""); + AlreadyComplained = 1; + } + ival = 0xffffffffffffffffull; + } + ch = getch(); + } while ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')); + } else { + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + } + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + if (ival > 0xffffffffu && !AlreadyComplained) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } else if ((ch == 'b' || ch == 'B') && pp->parseContext.intermediate.getSource() == EShSourceHlsl) { + // must be binary + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + ppToken->name[len++] = (char)ch; + ch = getch(); + + // Check value + if ((ch == '0' || ch == '1')) + { + ival = 0; + do { + if (len < MaxTokenLength && ival <= 0x7fffffffffffffffull) { + ppToken->name[len++] = (char)ch; + if (ch == '0' || ch == '1') { + ii = ch - '0'; + } else { + pp->parseContext.ppError(ppToken->loc, "bad digit in binary literal", "", ""); + } + ival = (ival << 1) | ii; + } + else + { + if (! AlreadyComplained) { + if(len < MaxTokenLength) + pp->parseContext.ppError(ppToken->loc, "binary literal too big", "", ""); + else + pp->parseContext.ppError(ppToken->loc, "binary literal too long", "", ""); + AlreadyComplained = 1; + } + ival = 0xffffffffffffffffull; + } + ch = getch(); + } while (ch == '0' || ch == '1'); + } + else + { + pp->parseContext.ppError(ppToken->loc, "bad digit in binary literal", "", ""); + } + + // check type + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; + } else { + ungetch(); + } + ppToken->name[len] = '\0'; + + // Assign value + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit binary literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit binary literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit binary literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit binary literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } else { + // could be octal integer or floating point, speculative pursue octal until it must be floating point + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + bool octalOverflow = false; + bool nonOctal = false; + ival = 0; + + // see how much octal-like stuff we can read + while (ch >= '0' && ch <= '7') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + if (ival <= 0x1fffffffffffffffull) { + ii = ch - '0'; + ival = (ival << 3) | ii; + } else + octalOverflow = true; + ch = getch(); + } + + // could be part of a float... + if (ch == '8' || ch == '9') { + nonOctal = true; + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + } + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + + // wasn't a float, so must be octal... + if (nonOctal) + pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", ""); + + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (!isInt64 && ival > 0xffffffffu) + octalOverflow = true; + + if (octalOverflow) + pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", ""); + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // can't be hexadecimal or octal, is either decimal or floating point + + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + else { + // Finish handling signed and unsigned integers + int numericLen = len; + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; + } else + ungetch(); + + ppToken->name[len] = '\0'; + ival = 0; + const unsigned oneTenthMaxInt = 0xFFFFFFFFu / 10; + const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt; + const unsigned long long oneTenthMaxInt64 = 0xFFFFFFFFFFFFFFFFull / 10; + const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64; + const unsigned short oneTenthMaxInt16 = 0xFFFFu / 10; + const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16; + for (int i = 0; i < numericLen; i++) { + ch = ppToken->name[i] - '0'; + bool overflow = false; + if (isInt64) + overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64)); + else if (isInt16) + overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16)); + else + overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt)); + if (overflow) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", ""); + ival = 0xFFFFFFFFFFFFFFFFull; + break; + } else + ival = ival * 10 + ch; + } + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit literal"); + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '-': + ch = getch(); + if (ch == '-') { + return PpAtomDecrement; + } else if (ch == '=') { + return PPAtomSubAssign; + } else { + ungetch(); + return '-'; + } + case '+': + ch = getch(); + if (ch == '+') { + return PpAtomIncrement; + } else if (ch == '=') { + return PPAtomAddAssign; + } else { + ungetch(); + return '+'; + } + case '*': + ch = getch(); + if (ch == '=') { + return PPAtomMulAssign; + } else { + ungetch(); + return '*'; + } + case '%': + ch = getch(); + if (ch == '=') { + return PPAtomModAssign; + } else { + ungetch(); + return '%'; + } + case '^': + ch = getch(); + if (ch == '^') { + return PpAtomXor; + } else { + if (ch == '=') + return PpAtomXorAssign; + else{ + ungetch(); + return '^'; + } + } + + case '=': + ch = getch(); + if (ch == '=') { + return PpAtomEQ; + } else { + ungetch(); + return '='; + } + case '!': + ch = getch(); + if (ch == '=') { + return PpAtomNE; + } else { + ungetch(); + return '!'; + } + case '|': + ch = getch(); + if (ch == '|') { + return PpAtomOr; + } else if (ch == '=') { + return PpAtomOrAssign; + } else { + ungetch(); + return '|'; + } + case '&': + ch = getch(); + if (ch == '&') { + return PpAtomAnd; + } else if (ch == '=') { + return PpAtomAndAssign; + } else { + ungetch(); + return '&'; + } + case '<': + ch = getch(); + if (ch == '<') { + ch = getch(); + if (ch == '=') + return PpAtomLeftAssign; + else { + ungetch(); + return PpAtomLeft; + } + } else if (ch == '=') { + return PpAtomLE; + } else { + ungetch(); + return '<'; + } + case '>': + ch = getch(); + if (ch == '>') { + ch = getch(); + if (ch == '=') + return PpAtomRightAssign; + else { + ungetch(); + return PpAtomRight; + } + } else if (ch == '=') { + return PpAtomGE; + } else { + ungetch(); + return '>'; + } + case '.': + ch = getch(); + if (ch >= '0' && ch <= '9') { + ungetch(); + return pp->lFloatConst(0, '.', ppToken); + } else { + ungetch(); + return '.'; + } + case '/': + ch = getch(); + if (ch == '/') { + pp->inComment = true; + do { + ch = getch(); + } while (ch != '\n' && ch != EndOfInput); + ppToken->space = true; + pp->inComment = false; + + return ch; + } else if (ch == '*') { + ch = getch(); + do { + while (ch != '*') { + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + ch = getch(); + } + ch = getch(); + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + } while (ch != '/'); + ppToken->space = true; + // loop again to get the next token... + break; + } else if (ch == '=') { + return PPAtomDivAssign; + } else { + ungetch(); + return '/'; + } + break; + case '\'': + return pp->characterLiteral(ppToken); + case '"': + // #include uses scanHeaderName() to ignore these escape sequences. + ch = getch(); + while (ch != '"' && ch != '\n' && ch != EndOfInput) { + if (len < MaxTokenLength) { + if (ch == '\\' && !pp->disableEscapeSequences) { + int nextCh = getch(); + switch (nextCh) { + case '\'': ch = 0x27; break; + case '"': ch = 0x22; break; + case '?': ch = 0x3f; break; + case '\\': ch = 0x5c; break; + case 'a': ch = 0x07; break; + case 'b': ch = 0x08; break; + case 'f': ch = 0x0c; break; + case 'n': ch = 0x0a; break; + case 'r': ch = 0x0d; break; + case 't': ch = 0x09; break; + case 'v': ch = 0x0b; break; + case 'x': + // Hex value, arbitrary number of characters. Terminated by the first + // non-hex digit + { + int numDigits = 0; + ch = 0; + while (true) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '9') + nextCh -= '0'; + else if (nextCh >= 'A' && nextCh <= 'F') + nextCh -= 'A' - 10; + else if (nextCh >= 'a' && nextCh <= 'f') + nextCh -= 'a' - 10; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 0x10 + nextCh; + } + if (numDigits == 0) { + pp->parseContext.ppError(ppToken->loc, "Expected hex value in escape sequence", "string", ""); + } + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Octal value, up to three octal digits + { + int numDigits = 1; + ch = nextCh - '0'; + while (numDigits < 3) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '7') + nextCh -= '0'; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 8 + nextCh; + } + break; + } + default: + pp->parseContext.ppError(ppToken->loc, "Invalid escape sequence", "string", ""); + break; + } + } + ppToken->name[len] = (char)ch; + len++; + ch = getch(); + } else + break; + }; + ppToken->name[len] = '\0'; + if (ch != '"') { + ungetch(); + pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", ""); + } + return PpAtomConstString; + case ':': + ch = getch(); + if (ch == ':') + return PpAtomColonColon; + ungetch(); + return ':'; + } + + ch = getch(); + } +} + +// +// The main functional entry point into the preprocessor, which will +// scan the source strings to figure out and return the next processing token. +// +// Return the token, or EndOfInput when no more tokens. +// +int TPpContext::tokenize(TPpToken& ppToken) +{ + for(;;) { + int token = scanToken(&ppToken); + + // Handle token-pasting logic + token = tokenPaste(token, ppToken); + + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + if (token == '#') { + if (previous_token == '\n') { + token = readCPPline(&ppToken); + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + continue; + } else { + parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", ""); + return EndOfInput; + } + } + previous_token = token; + + if (token == '\n') + continue; + + // expand macros + if (token == PpAtomIdentifier) { + switch (MacroExpand(&ppToken, false, true)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + return EndOfInput; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstFloat: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstDouble: + case PpAtomConstFloat16: + if (ppToken.name[0] == '\0') + continue; + break; + case PpAtomConstString: + // HLSL allows string literals. + // GLSL allows string literals with GL_EXT_debug_printf. + if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) { + const char* const string_literal_EXTs[] = { E_GL_EXT_debug_printf, E_GL_EXT_spirv_intrinsics }; + parseContext.requireExtensions(ppToken.loc, 2, string_literal_EXTs, "string literal"); + if (!parseContext.extensionTurnedOn(E_GL_EXT_debug_printf) && + !parseContext.extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) + continue; + } + break; + case '\'': + parseContext.ppError(ppToken.loc, "character literals not supported", "\'", ""); + continue; + default: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token)); + break; + } + + return token; + } +} + +// +// Do all token-pasting related combining of two pasted tokens when getting a +// stream of tokens from a replacement list. Degenerates to no processing if a +// replacement list is not the source of the token stream. +// +int TPpContext::tokenPaste(int token, TPpToken& ppToken) +{ + // starting with ## is illegal, skip to next token + if (token == PpAtomPaste) { + parseContext.ppError(ppToken.loc, "unexpected location", "##", ""); + return scanToken(&ppToken); + } + + int resultToken = token; // "foo" pasted with "35" is an identifier, not a number + + // ## can be chained, process all in the chain at once + while (peekPasting()) { + TPpToken pastedPpToken; + + // next token has to be ## + token = scanToken(&pastedPpToken); + assert(token == PpAtomPaste); + + // This covers end of macro expansion + if (endOfReplacementList()) { + parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", ""); + break; + } + + // Get the token(s) after the ##. + // Because of "space" semantics, and prior tokenization, what + // appeared a single token, e.g. "3A", might have been tokenized + // into two tokens "3" and "A", but the "A" will have 'space' set to + // false. Accumulate all of these to recreate the original lexical + // appearing token. + do { + token = scanToken(&pastedPpToken); + + // This covers end of argument expansion + if (token == tMarkerInput::marker) { + parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", ""); + return resultToken; + } + + // get the token text + switch (resultToken) { + case PpAtomIdentifier: + // already have the correct text in token.names + break; + case '=': + case '!': + case '-': + case '~': + case '+': + case '*': + case '/': + case '%': + case '<': + case '>': + case '|': + case '^': + case '&': + case PpAtomRight: + case PpAtomLeft: + case PpAtomAnd: + case PpAtomOr: + case PpAtomXor: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(resultToken)); + snprintf(pastedPpToken.name, sizeof(pastedPpToken.name), "%s", atomStrings.getString(token)); + break; + default: + parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", ""); + return resultToken; + } + + // combine the tokens + if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) { + parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", ""); + return resultToken; + } + snprintf(&ppToken.name[0] + strlen(ppToken.name), sizeof(ppToken.name) - strlen(ppToken.name), + "%s", pastedPpToken.name); + + // correct the kind of token we are making, if needed (identifiers stay identifiers) + if (resultToken != PpAtomIdentifier) { + int newToken = atomStrings.getAtom(ppToken.name); + if (newToken > 0) + resultToken = newToken; + else + parseContext.ppError(ppToken.loc, "combined token is invalid", "##", ""); + } + } while (peekContinuedPasting(resultToken)); + } + + return resultToken; +} + +// Checks if we've seen balanced #if...#endif +void TPpContext::missingEndifCheck() +{ + if (ifdepth > 0) + parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", ""); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp new file mode 100644 index 00000000..e6ee64cf --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp @@ -0,0 +1,216 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +// +// For recording and playing back the stream of tokens in a macro definition. +// + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Add a token (including backing string) to the end of a macro +// token stream, for later playback. +void TPpContext::TokenStream::putToken(int atom, TPpToken* ppToken) +{ + TokenStream::Token streamToken(atom, *ppToken); + stream.push_back(streamToken); +} + +// Read the next token from a macro token stream. +int TPpContext::TokenStream::getToken(TParseContextBase& parseContext, TPpToken *ppToken) +{ + if (atEnd()) + return EndOfInput; + + int atom = stream[currentPos++].get(*ppToken); + ppToken->loc = parseContext.getCurrentLoc(); + + // Check for ##, unless the current # is the last character + if (atom == '#') { + if (peekToken('#')) { + parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)"); + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, nullptr, "token pasting (##)"); + currentPos++; + atom = PpAtomPaste; + } + } + + return atom; +} + +// We are pasting if +// 1. we are preceding a pasting operator within this stream +// or +// 2. the entire macro is preceding a pasting operator (lastTokenPastes) +// and we are also on the last token +bool TPpContext::TokenStream::peekTokenizedPasting(bool lastTokenPastes) +{ + // 1. preceding ##? + + size_t savePos = currentPos; + // skip white space + while (peekToken(' ')) + ++currentPos; + if (peekToken(PpAtomPaste)) { + currentPos = savePos; + return true; + } + + // 2. last token and we've been told after this there will be a ## + + if (! lastTokenPastes) + return false; + // Getting here means the last token will be pasted, after this + + // Are we at the last non-whitespace token? + savePos = currentPos; + bool moreTokens = false; + do { + if (atEnd()) + break; + if (!peekToken(' ')) { + moreTokens = true; + break; + } + ++currentPos; + } while (true); + currentPos = savePos; + + return !moreTokens; +} + +// See if the next non-white-space tokens are two consecutive # +bool TPpContext::TokenStream::peekUntokenizedPasting() +{ + // don't return early, have to restore this + size_t savePos = currentPos; + + // skip white-space + while (peekToken(' ')) + ++currentPos; + + // check for ## + bool pasting = false; + if (peekToken('#')) { + ++currentPos; + if (peekToken('#')) + pasting = true; + } + + currentPos = savePos; + + return pasting; +} + +void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting, bool expanded) +{ + pushInput(new tTokenInput(this, &ts, prepasting, expanded)); + ts.reset(); +} + +int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + int ret = token; + *ppToken = lval; + done = true; + + return ret; +} + +void TPpContext::UngetToken(int token, TPpToken* ppToken) +{ + pushInput(new tUngotTokenInput(this, token, ppToken)); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h new file mode 100644 index 00000000..7b0f8155 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h @@ -0,0 +1,179 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PARSER_H +#define PARSER_H + +namespace glslang { + +// Multi-character tokens +enum EFixedAtoms { + // single character tokens get their own char value as their token; start here for multi-character tokens + PpAtomMaxSingle = 127, + + // replace bad character tokens with this, to avoid accidental aliasing with the below + PpAtomBadToken, + + // Operators + + PPAtomAddAssign, + PPAtomSubAssign, + PPAtomMulAssign, + PPAtomDivAssign, + PPAtomModAssign, + + PpAtomRight, + PpAtomLeft, + + PpAtomRightAssign, + PpAtomLeftAssign, + PpAtomAndAssign, + PpAtomOrAssign, + PpAtomXorAssign, + + PpAtomAnd, + PpAtomOr, + PpAtomXor, + + PpAtomEQ, + PpAtomNE, + PpAtomGE, + PpAtomLE, + + PpAtomDecrement, + PpAtomIncrement, + + PpAtomColonColon, + + PpAtomPaste, + + // Constants + + PpAtomConstInt, + PpAtomConstUint, + PpAtomConstInt64, + PpAtomConstUint64, + PpAtomConstInt16, + PpAtomConstUint16, + PpAtomConstFloat, + PpAtomConstDouble, + PpAtomConstFloat16, + PpAtomConstString, + + // Identifiers + PpAtomIdentifier, + + // preprocessor "keywords" + + PpAtomDefine, + PpAtomUndef, + + PpAtomIf, + PpAtomIfdef, + PpAtomIfndef, + PpAtomElse, + PpAtomElif, + PpAtomEndif, + + PpAtomLine, + PpAtomPragma, + PpAtomError, + + // #version ... + PpAtomVersion, + PpAtomCore, + PpAtomCompatibility, + PpAtomEs, + + // #extension + PpAtomExtension, + + // __LINE__, __FILE__, __VERSION__ + + PpAtomLineMacro, + PpAtomFileMacro, + PpAtomVersionMacro, + + // #include + PpAtomInclude, + + PpAtomLast, +}; + +} // end namespace glslang + +#endif /* not PARSER_H */ diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.cpp new file mode 100644 index 00000000..600541f6 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.cpp @@ -0,0 +1,869 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate the 'noContraction' qualifier. +// + +#include "propagateNoContraction.h" + +#include +#include +#include +#include +#include + +#include "localintermediate.h" +namespace { + +// Use a string to hold the access chain information, as in most cases the +// access chain is short and may contain only one element, which is the symbol +// ID. +// Example: struct {float a; float b;} s; +// Object s.a will be represented with: /0 +// Object s.b will be represented with: /1 +// Object s will be represented with: +// For members of vector, matrix and arrays, they will be represented with the +// same symbol ID of their container symbol objects. This is because their +// preciseness is always the same as their container symbol objects. +typedef std::string ObjectAccessChain; + +// The delimiter used in the ObjectAccessChain string to separate symbol ID and +// different level of struct indices. +const char ObjectAccesschainDelimiter = '/'; + +// Mapping from Symbol IDs of symbol nodes, to their defining operation +// nodes. +typedef std::unordered_multimap NodeMapping; +// Mapping from object nodes to their access chain info string. +typedef std::unordered_map AccessChainMapping; + +// Set of object IDs. +typedef std::unordered_set ObjectAccesschainSet; +// Set of return branch nodes. +typedef std::unordered_set ReturnBranchNodeSet; + +// A helper function to tell whether a node is 'noContraction'. Returns true if +// the node has 'noContraction' qualifier, otherwise false. +bool isPreciseObjectNode(glslang::TIntermTyped* node) +{ + return node->getType().getQualifier().isNoContraction(); +} + +// Returns true if the opcode is a dereferencing one. +bool isDereferenceOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpIndexIndirect: + case glslang::EOpVectorSwizzle: + case glslang::EOpMatrixSwizzle: + return true; + default: + return false; + } +} + +// Returns true if the opcode leads to an assignment operation. +bool isAssignOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper function to get the unsigned int from a given constant union node. +// Note the node should only hold a uint scalar. +unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node) +{ + assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar()); + unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst(); + return struct_dereference_index; +} + +// A helper function to generate symbol_label. +ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node) +{ + ObjectAccessChain symbol_id = + std::to_string(node->getId()) + "(" + node->getName().c_str() + ")"; + return symbol_id; +} + +// Returns true if the operation is an arithmetic operation and valid for +// the 'NoContraction' decoration. +bool isArithmeticOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + + case glslang::EOpNegative: + + case glslang::EOpAdd: + case glslang::EOpSub: + case glslang::EOpMul: + case glslang::EOpDiv: + case glslang::EOpMod: + + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesMatrix: + case glslang::EOpMatrixTimesVector: + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesMatrix: + + case glslang::EOpDot: + case glslang::EOpDotPackedEXT: + case glslang::EOpDotAccSatEXT: + case glslang::EOpDotPackedAccSatEXT: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper class to help manage the populating_initial_no_contraction_ flag. +template class StateSettingGuard { +public: + StateSettingGuard(T* state_ptr, T new_state_value) + : state_ptr_(state_ptr), previous_state_(*state_ptr) + { + *state_ptr = new_state_value; + } + StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {} + void setState(T new_state_value) { *state_ptr_ = new_state_value; } + ~StateSettingGuard() { *state_ptr_ = previous_state_; } + +private: + T* state_ptr_; + T previous_state_; +}; + +// A helper function to get the front element from a given ObjectAccessChain +ObjectAccessChain getFrontElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter); +} + +// A helper function to get the access chain starting from the second element. +ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1); +} + +// A helper function to get the access chain after removing a given prefix. +ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain, + const ObjectAccessChain& prefix) +{ + size_t pos = chain.find(prefix); + if (pos != 0) + return chain; + return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter)); +} + +// +// A traverser which traverses the whole AST and populates: +// 1) A mapping from symbol nodes' IDs to their defining operation nodes. +// 2) A set of access chains of the initial precise object nodes. +// +class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser { +public: + TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping, + AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + ReturnBranchNodeSet* precise_return_nodes); + + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override; + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override; + void visitSymbol(glslang::TIntermSymbol*) override; + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override; + bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override; + +protected: + TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&); + + // The mapping from symbol node IDs to their defining nodes. This should be + // populated along traversing the AST. + NodeMapping& symbol_definition_mapping_; + // The set of symbol node IDs for precise symbol nodes, the ones marked as + // 'noContraction'. + ObjectAccesschainSet& precise_objects_; + // The set of precise return nodes. + ReturnBranchNodeSet& precise_return_nodes_; + // A temporary cache of the symbol node whose defining node is to be found + // currently along traversing the AST. + ObjectAccessChain current_object_; + // A map from object node to its access chain. This traverser stores + // the built access chains into this map for each object node it has + // visited. + AccessChainMapping& accesschain_mapping_; + // The pointer to the Function Definition node, so we can get the + // preciseness of the return expression from it when we traverse the + // return branch node. + glslang::TIntermAggregate* current_function_definition_node_; +}; + +TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser( + NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + std::unordered_set* precise_return_nodes) + : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping), + precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes), + current_object_(), accesschain_mapping_(*accesschain_mapping), + current_function_definition_node_(nullptr) {} + +// Visits a symbol node, set the current_object_ to the +// current node symbol ID, and record a mapping from this node to the current +// current_object_, which is the just obtained symbol +// ID. +void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + current_object_ = generateSymbolLabel(node); + accesschain_mapping_[node] = current_object_; +} + +// Visits an aggregate node, traverses all of its children. +bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit, + glslang::TIntermAggregate* node) +{ + // This aggregate node might be a function definition node, in which case we need to + // cache this node, so we can get the preciseness information of the return value + // of this function later. + StateSettingGuard current_function_definition_node_setting_guard( + ¤t_function_definition_node_); + if (node->getOp() == glslang::EOpFunction) { + // This is function definition node, we need to cache this node so that we can + // get the preciseness of the return value later. + current_function_definition_node_setting_guard.setState(node); + } + // Traverse the items in the sequence. + glslang::TIntermSequence& seq = node->getSequence(); + for (int i = 0; i < (int)seq.size(); ++i) { + current_object_.clear(); + seq[i]->traverse(this); + } + return false; +} + +bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit, + glslang::TIntermBranch* node) +{ + if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() && + current_function_definition_node_ && + current_function_definition_node_->getType().getQualifier().noContraction) { + // This node is a return node with an expression, and its function has a + // precise return value. We need to find the involved objects in its + // expression and add them to the set of initial precise objects. + precise_return_nodes_.insert(node); + node->getExpression()->traverse(this); + } + return false; +} + +// Visits a unary node. This might be an implicit assignment like i++, i--. etc. +bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */, + glslang::TIntermUnary* node) +{ + current_object_.clear(); + node->getOperand()->traverse(this); + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain of the operand node. + assert(!current_object_.empty()); + + // If the operand node object is 'precise', we collect its access chain + // for the initial set of 'precise' objects. + if (isPreciseObjectNode(node->getOperand())) { + // The operand node is an 'precise' object node, add its + // access chain to the set of 'precise' objects. This is to collect + // the initial set of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object's access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Add a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + } + // A unary node is not a dereference node, so we clear the access chain which + // is under construction. + current_object_.clear(); + return false; +} + +// Visits a binary node and updates the mapping from symbol IDs to the definition +// nodes. Also collects the access chains for the initial precise objects. +bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */, + glslang::TIntermBinary* node) +{ + // Traverses the left node to build the access chain info for the object. + current_object_.clear(); + node->getLeft()->traverse(this); + + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain for the left node. + assert(!current_object_.empty()); + + // If the left node object is 'precise', it is an initial precise object + // specified in the shader source. Adds it to the initial work list to + // process later. + if (isPreciseObjectNode(node->getLeft())) { + // The left node is an 'precise' object node, add its access chain to + // the set of 'precise' objects. This is to collect the initial set + // of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object access chain, which should be the + // first element recorded in the access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Adds a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + + // Traverses the right node, there may be other 'assignment' + // operations in the right. + current_object_.clear(); + node->getRight()->traverse(this); + + } else if (isDereferenceOperation(node->getOp())) { + // The left node (parent node) is a struct type object. We need to + // record the access chain information of the current node into its + // object id. + if (node->getOp() == glslang::EOpIndexDirectStruct) { + unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight()); + current_object_.push_back(ObjectAccesschainDelimiter); + current_object_.append(std::to_string(struct_dereference_index)); + } + accesschain_mapping_[node] = current_object_; + + // For a dereference node, there is no need to traverse the right child + // node as the right node should always be an integer type object. + + } else { + // For other binary nodes, still traverse the right node. + current_object_.clear(); + node->getRight()->traverse(this); + } + return false; +} + +// Traverses the AST and returns a tuple of four members: +// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols. +// 2) a mapping from object nodes in the AST to the access chains of these objects. +// 3) a set of access chains of precise objects. +// 4) a set of return nodes with precise expressions. +std::tuple +getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate) +{ + auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(), + ReturnBranchNodeSet()); + + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) + return result_tuple; + + NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple); + AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple); + ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple); + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple); + + // Traverses the AST and populate the results. + TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping, + &precise_objects, &precise_return_nodes); + root->traverse(&collector); + + return result_tuple; +} + +// +// A traverser that determine whether the left node (or operand node for unary +// node) of an assignment node is 'precise', containing 'precise' or not, +// according to the access chain a given precise object which share the same +// symbol as the left node. +// +// Post-orderly traverses the left node subtree of an binary assignment node and: +// +// 1) Propagates the 'precise' from the left object nodes to this object node. +// +// 2) Builds object access chain along the traversal, and also compares with +// the access chain of the given 'precise' object along with the traversal to +// tell if the node to be defined is 'precise' or not. +// +class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser { + + enum DecisionStatus { + // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects. + Mixed = 0, + // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'. + Precise = 1, + // The object node to be assigned to is not a 'precise' object. + NotPreicse = 2, + }; + +public: + TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping), + precise_object_(nullptr) {} + + // Checks the preciseness of a given assignment node with a precise object + // represented as access chain. The precise object shares the same symbol + // with the assignee of the given assignment node. Return a tuple of two: + // + // 1) The preciseness of the assignee node of this assignment node. True + // if the assignee contains 'precise' objects or is 'precise', false if + // the assignee is not 'precise' according to the access chain of the given + // precise object. + // + // 2) The incremental access chain from the assignee node to its nested + // 'precise' object, according to the access chain of the given precise + // object. This incremental access chain can be empty, which means the + // assignee is 'precise'. Otherwise it shows the path to the nested + // precise object. + std::tuple + getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node, + const ObjectAccessChain& precise_object) + { + assert(isAssignOperation(node->getOp())); + precise_object_ = &precise_object; + ObjectAccessChain assignee_object; + if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) { + // This is a binary assignment node, we need to check the + // preciseness of the left node. + assert(accesschain_mapping_.count(BN->getLeft())); + // The left node (assignee node) is an object node, traverse the + // node to let the 'precise' of nesting objects being transfered to + // nested objects. + BN->getLeft()->traverse(this); + // After traversing the left node, if the left node is 'precise', + // we can conclude this assignment should propagate 'precise'. + if (isPreciseObjectNode(BN->getLeft())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the left node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(BN->getLeft()); + + } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) { + // This is a unary assignment node, we need to check the + // preciseness of the operand node. For unary assignment node, the + // operand node should always be an object node. + assert(accesschain_mapping_.count(UN->getOperand())); + // Traverse the operand node to let the 'precise' being propagated + // from lower nodes to upper nodes. + UN->getOperand()->traverse(this); + // After traversing the operand node, if the operand node is + // 'precise', this assignment should propagate 'precise'. + if (isPreciseObjectNode(UN->getOperand())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the operand node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(UN->getOperand()); + } else { + // Not a binary or unary node, should not happen. + assert(false); + } + + // Compare the access chain string of the assignee node with the given + // precise object to determine if this assignment should propagate + // 'precise'. + if (assignee_object.find(precise_object) == 0) { + // The access chain string of the given precise object is a prefix + // of assignee's access chain string. The assignee should be + // 'precise'. + return make_tuple(true, ObjectAccessChain()); + } else if (precise_object.find(assignee_object) == 0) { + // The assignee's access chain string is a prefix of the given + // precise object, the assignee object contains 'precise' object, + // and we need to pass the remained access chain to the object nodes + // in the right. + return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object)); + } else { + // The access chain strings do not match, the assignee object can + // not be labeled as 'precise' according to the given precise + // object. + return make_tuple(false, ObjectAccessChain()); + } + } + +protected: + TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&); + + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override; + void visitSymbol(glslang::TIntermSymbol* node) override; + + // A map from object nodes to their access chain string (used as object ID). + const AccessChainMapping& accesschain_mapping_; + // A given precise object, represented in it access chain string. This + // precise object is used to be compared with the assignee node to tell if + // the assignee node is 'precise', contains 'precise' object or not + // 'precise'. + const ObjectAccessChain* precise_object_; +}; + +// Visits a binary node. If the node is an object node, it must be a dereference +// node. In such cases, if the left node is 'precise', this node should also be +// 'precise'. +bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit, + glslang::TIntermBinary* node) +{ + // Traverses the left so that we transfer the 'precise' from nesting object + // to its nested object. + node->getLeft()->traverse(this); + // If this binary node is an object node, we should have it in the + // accesschain_mapping_. + if (accesschain_mapping_.count(node)) { + // A binary object node must be a dereference node. + assert(isDereferenceOperation(node->getOp())); + // If the left node is 'precise', this node should also be precise, + // otherwise, compare with the given precise_object_. If the + // access chain of this node matches with the given precise_object_, + // this node should be marked as 'precise'. + if (isPreciseObjectNode(node->getLeft())) { + node->getWritableType().getQualifier().noContraction = true; + } else if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } + } + return false; +} + +// Visits a symbol node, if the symbol node ID (its access chain string) matches +// with the given precise object, this node should be 'precise'. +void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + // A symbol node should always be an object node, and should have been added + // to the map from object nodes to their access chain strings. + assert(accesschain_mapping_.count(node)); + if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } +} + +// +// A traverser that only traverses the right side of binary assignment nodes +// and the operand node of unary assignment nodes. +// +// 1) Marks arithmetic operations as 'NoContraction'. +// +// 2) Find the object which should be marked as 'precise' in the right and +// update the 'precise' object work list. +// +class TNoContractionPropagator : public glslang::TIntermTraverser { +public: + TNoContractionPropagator(ObjectAccesschainSet* precise_objects, + const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), + precise_objects_(*precise_objects), added_precise_object_ids_(), + remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {} + + // Propagates 'precise' in the right nodes of a given assignment node with + // access chain record from the assignee node to a 'precise' object it + // contains. + void + propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node, + const ObjectAccessChain& assignee_remained_accesschain) + { + remained_accesschain_ = assignee_remained_accesschain; + if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) { + assert(isAssignOperation(BN->getOp())); + BN->getRight()->traverse(this); + if (isArithmeticOperation(BN->getOp())) { + BN->getWritableType().getQualifier().noContraction = true; + } + } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) { + assert(isAssignOperation(UN->getOp())); + UN->getOperand()->traverse(this); + if (isArithmeticOperation(UN->getOp())) { + UN->getWritableType().getQualifier().noContraction = true; + } + } + } + + // Propagates 'precise' in a given precise return node. + void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node) + { + remained_accesschain_ = ""; + assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression()); + return_node->getExpression()->traverse(this); + } + +protected: + TNoContractionPropagator& operator=(const TNoContractionPropagator&); + + // Visits an aggregate node. The node can be a initializer list, in which + // case we need to find the 'precise' or 'precise' containing object node + // with the access chain record. In other cases, just need to traverse all + // the children nodes. + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override + { + if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) { + // This is a struct initializer node, and the remained + // access chain is not empty, we need to refer to the + // assignee_remained_access_chain_ to find the nested + // 'precise' object. And we don't need to visit other nodes in this + // aggregate node. + + // Gets the struct dereference index that leads to 'precise' object. + ObjectAccessChain precise_accesschain_index_str = + getFrontElement(remained_accesschain_); + unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10); + // Gets the node pointed by the access chain index extracted before. + glslang::TIntermTyped* potential_precise_node = + node->getSequence()[precise_accesschain_index]->getAsTyped(); + assert(potential_precise_node); + // Pop the front access chain index from the path, and visit the nested node. + { + ObjectAccessChain next_level_accesschain = + subAccessChainFromSecondElement(remained_accesschain_); + StateSettingGuard setup_remained_accesschain_for_next_level( + &remained_accesschain_, next_level_accesschain); + potential_precise_node->traverse(this); + } + return false; + } + return true; + } + + // Visits a binary node. A binary node can be an object node, e.g. a dereference node. + // As only the top object nodes in the right side of an assignment needs to be visited + // and added to 'precise' work list, this traverser won't visit the children nodes of + // an object node. If the binary node does not represent an object node, it should + // go on to traverse its children nodes and if it is an arithmetic operation node, this + // operation should be marked as 'noContraction'. + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override + { + if (isDereferenceOperation(node->getOp())) { + // This binary node is an object node. Need to update the precise + // object set with the access chain of this node + remained + // access chain . + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Cache the access chain as added precise object, so we won't add the + // same object to the work list again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + // Only the upper-most object nodes should be visited, so do not + // visit children of this object node. + return false; + } + // If this is an arithmetic operation, marks this node as 'noContraction'. + if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) { + node->getWritableType().getQualifier().noContraction = true; + } + // As this node is not an object node, need to traverse the children nodes. + return true; + } + + // Visits a unary node. A unary node can not be an object node. If the operation + // is an arithmetic operation, need to mark this node as 'noContraction'. + bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override + { + // If this is an arithmetic operation, marks this with 'noContraction' + if (isArithmeticOperation(node->getOp())) { + node->getWritableType().getQualifier().noContraction = true; + } + return true; + } + + // Visits a symbol node. A symbol node is always an object node. So we + // should always be able to find its in our collected mapping from object + // nodes to access chains. As an object node, a symbol node can be either + // 'precise' or containing 'precise' objects according to unused + // access chain information we have when we visit this node. + void visitSymbol(glslang::TIntermSymbol* node) override + { + // Symbol nodes are object nodes and should always have an + // access chain collected before matches with it. + assert(accesschain_mapping_.count(node)); + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + // If the unused access chain is empty, this symbol node should be + // marked as 'precise'. Otherwise, the unused access chain should be + // appended to the symbol ID to build a new access chain which points to + // the nested 'precise' object in this symbol object. + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Add the new 'precise' access chain to the work list and make sure we + // don't visit it again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + } + + // A set of precise objects, represented as access chains. + ObjectAccesschainSet& precise_objects_; + // Visited symbol nodes, should not revisit these nodes. + ObjectAccesschainSet added_precise_object_ids_; + // The left node of an assignment operation might be an parent of 'precise' objects. + // This means the left node might not be an 'precise' object node, but it may contains + // 'precise' qualifier which should be propagated to the corresponding child node in + // the right. So we need the path from the left node to its nested 'precise' node to + // tell us how to find the corresponding 'precise' node in the right. + ObjectAccessChain remained_accesschain_; + // A map from node pointers to their access chains. + const AccessChainMapping& accesschain_mapping_; +}; +} + +namespace glslang { + +void PropagateNoContraction(const glslang::TIntermediate& intermediate) +{ + // First, traverses the AST, records symbols with their defining operations + // and collects the initial set of precise symbols (symbol nodes that marked + // as 'noContraction') and precise return nodes. + auto mappings_and_precise_objects = + getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate); + + // The mapping of symbol node IDs to their defining nodes. This enables us + // to get the defining node directly from a given symbol ID without + // traversing the tree again. + NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects); + + // The mapping of object nodes to their access chains recorded. + AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects); + + // The initial set of 'precise' objects which are represented as the + // access chain toward them. + ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects); + + // The set of 'precise' return nodes. + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects); + + // Second, uses the initial set of precise objects as a work list, pops an + // access chain, extract the symbol ID from it. Then: + // 1) Check the assignee object, see if it is 'precise' object node or + // contains 'precise' object. Obtain the incremental access chain from the + // assignee node to its nested 'precise' node (if any). + // 2) If the assignee object node is 'precise' or it contains 'precise' + // objects, traverses the right side of the assignment operation + // expression to mark arithmetic operations as 'noContration' and update + // 'precise' access chain work list with new found object nodes. + // Repeat above steps until the work list is empty. + TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping); + TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping); + + // We have two initial precise work lists to handle: + // 1) precise return nodes + // 2) precise object access chains + // We should process the precise return nodes first and the involved + // objects in the return expression should be added to the precise object + // access chain set. + while (!precise_return_nodes.empty()) { + glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin(); + propagator.propagateNoContractionInReturnNode(precise_return_node); + precise_return_nodes.erase(precise_return_node); + } + + while (!precise_object_accesschains.empty()) { + // Get the access chain of a precise object from the work list. + ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin(); + // Get the symbol id from the access chain. + ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain); + // Get all the defining nodes of that symbol ID. + std::pair range = + symbol_definition_mapping.equal_range(symbol_id); + // Visits all the assignment nodes of that symbol ID and + // 1) Check if the assignee node is 'precise' or contains 'precise' + // objects. + // 2) Propagate the 'precise' to the top layer object nodes + // in the right side of the assignment operation, update the 'precise' + // work list with new access chains representing the new 'precise' + // objects, and mark arithmetic operations as 'noContraction'. + for (NodeMapping::iterator defining_node_iter = range.first; + defining_node_iter != range.second; defining_node_iter++) { + TIntermOperator* defining_node = defining_node_iter->second; + // Check the assignee node. + auto checker_result = checker.getPrecisenessAndRemainedAccessChain( + defining_node, precise_object_accesschain); + bool& contain_precise = std::get<0>(checker_result); + ObjectAccessChain& remained_accesschain = std::get<1>(checker_result); + // If the assignee node is 'precise' or contains 'precise', propagate the + // 'precise' to the right. Otherwise just skip this assignment node. + if (contain_precise) { + propagator.propagateNoContractionInOneExpression(defining_node, + remained_accesschain); + } + } + // Remove the last processed 'precise' object from the work list. + precise_object_accesschains.erase(precise_object_accesschain); + } +} +} diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.h new file mode 100644 index 00000000..d849be83 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/propagateNoContraction.h @@ -0,0 +1,56 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate 'noContraction' qualifier. +// + +#pragma once + +#include "../Include/intermediate.h" + +namespace glslang { + +// Propagates the 'precise' qualifier for objects (objects marked with +// 'noContraction' qualifier) from the shader source specified 'precise' +// variables to all the involved objects, and add 'noContraction' qualifier for +// the involved arithmetic operations. +// Note that the same qualifier: 'noContraction' is used in both object nodes +// and arithmetic operation nodes, but has different meaning. For object nodes, +// 'noContraction' means the object is 'precise'; and for arithmetic operation +// nodes, it means the operation should not be contracted. +void PropagateNoContraction(const glslang::TIntermediate& intermediate); + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.cpp b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.cpp new file mode 100644 index 00000000..7e316092 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.cpp @@ -0,0 +1,1289 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "reflection.h" +#include "LiveTraverser.h" +#include "localintermediate.h" + +#include "gl_types.h" + +// +// Grow the reflection database through a friend traverser class of TReflection and a +// collection of functions to do a liveness traversal that note what uniforms are used +// in semantically non-dead code. +// +// Can be used multiple times, once per stage, to grow a program reflection. +// +// High-level algorithm for one stage: +// +// 1. Put the entry point on the list of live functions. +// +// 2. Traverse any live function, while skipping if-tests with a compile-time constant +// condition of false, and while adding any encountered function calls to the live +// function list. +// +// Repeat until the live function list is empty. +// +// 3. Add any encountered uniform variables and blocks to the reflection database. +// +// Can be attempted with a failed link, but will return false if recursion had been detected, or +// there wasn't exactly one entry point. +// + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing binary nodes to see if they are dereferences of an aggregates to track +// - processing symbol nodes to see if they are non-aggregate objects to track +// +// This ignores semantically dead code by using TLiveTraverser. +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// + +class TReflectionTraverser : public TIntermTraverser { +public: + TReflectionTraverser(const TIntermediate& i, TReflection& r) : + TIntermTraverser(), intermediate(i), reflection(r), updateStageMasks(true) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual void visitSymbol(TIntermSymbol* base); + + // Add a simple reference to a uniform variable to the uniform database, no dereference involved. + // However, no dereference doesn't mean simple... it could be a complex aggregate. + void addUniform(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + int blockIndex = -1; + int offset = -1; + TList derefs; + TString baseName = base.getName(); + + if (base.getType().getBasicType() == EbtBlock) { + offset = 0; + bool anonymous = IsAnonymous(baseName); + const TString& blockName = base.getType().getTypeName(); + + if (!anonymous) + baseName = blockName; + else + baseName = ""; + + blockIndex = addBlockName(blockName, base.getType(), intermediate.getBlockSize(base.getType())); + } + + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + blowUpActiveAggregate(base.getType(), baseName, derefs, derefs.end(), offset, blockIndex, 0, -1, 0, + base.getQualifier().storage, updateStageMasks); + } + } + + void addPipeIOVariable(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + const TString &name = base.getName(); + const TType &type = base.getType(); + const bool input = base.getQualifier().isPipeInput(); + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + + TReflection::TNameToIndex &ioMapper = + input ? reflection.pipeInNameToIndex : reflection.pipeOutNameToIndex; + + if (reflection.options & EShReflectionUnwrapIOBlocks) { + bool anonymous = IsAnonymous(name); + + TString baseName; + if (type.getBasicType() == EbtBlock) { + baseName = anonymous ? TString() : type.getTypeName(); + } else { + baseName = anonymous ? TString() : name; + } + + // by convention if this is an arrayed block we ignore the array in the reflection + if (type.isArray() && type.getBasicType() == EbtBlock) { + blowUpIOAggregate(input, baseName, TType(type, 0)); + } else { + blowUpIOAggregate(input, baseName, type); + } + } else { + TReflection::TNameToIndex::const_iterator it = ioMapper.find(name.c_str()); + if (it == ioMapper.end()) { + // seperate pipe i/o params from uniforms and blocks + // in is only for input in first stage as out is only for last stage. check traverse in call stack. + ioMapper[name.c_str()] = static_cast(ioItems.size()); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + } + + // Lookup or calculate the offset of all block members at once, using the recursively + // defined block offset rules. + void getOffsets(const TType& type, TVector& offsets) + { + const TTypeList& memberList = *type.getStruct(); + int memberSize = 0; + int offset = 0; + + for (size_t m = 0; m < offsets.size(); ++m) { + // if the user supplied an offset, snap to it now + if (memberList[m].type->getQualifier().hasOffset()) + offset = memberList[m].type->getQualifier().layoutOffset; + + // calculate the offset of the next member and align the current offset to this member + intermediate.updateOffset(type, *memberList[m].type, offset, memberSize); + + // save the offset of this member + offsets[m] = offset; + + // update for the next member + offset += memberSize; + } + } + + // Calculate the stride of an array type + int getArrayStride(const TType& baseType, const TType& type) + { + int dummySize; + int stride; + + // consider blocks to have 0 stride, so that all offsets are relative to the start of their block + if (type.getBasicType() == EbtBlock) + return 0; + + TLayoutMatrix subMatrixLayout = type.getQualifier().layoutMatrix; + intermediate.getMemberAlignment(type, dummySize, stride, + baseType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : baseType.getQualifier().layoutMatrix == ElmRowMajor); + + return stride; + } + + // count the total number of leaf members from iterating out of a block type + int countAggregateMembers(const TType& parentType) + { + if (! parentType.isStruct()) + return 1; + + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + bool blockParent = (parentType.getBasicType() == EbtBlock && parentType.getQualifier().storage == EvqBuffer); + + const TTypeList &memberList = *parentType.getStruct(); + + int ret = 0; + + for (size_t i = 0; i < memberList.size(); i++) + { + const TType &memberType = *memberList[i].type; + int numMembers = countAggregateMembers(memberType); + // for sized arrays of structs, apply logic to expand out the same as we would below in + // blowUpActiveAggregate + if (memberType.isArray() && ! memberType.getArraySizes()->hasUnsized() && memberType.isStruct()) { + if (! strictArraySuffix || ! blockParent) + numMembers *= memberType.getArraySizes()->getCumulativeSize(); + } + ret += numMembers; + } + + return ret; + } + + // Traverse the provided deref chain, including the base, and + // - build a full reflection-granularity name, array size, etc. entry out of it, if it goes down to that granularity + // - recursively expand any variable array index in the middle of that traversal + // - recursively expand what's left at the end if the deref chain did not reach down to reflection granularity + // + // arraySize tracks, just for the final dereference in the chain, if there was a specific known size. + // A value of 0 for arraySize will mean to use the full array's size. + void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList& derefs, + TList::const_iterator deref, int offset, int blockIndex, int arraySize, + int topLevelArraySize, int topLevelArrayStride, TStorageQualifier baseStorage, bool active) + { + // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query. + // Broadly: + // * arrays-of-structs always have a [x] suffix. + // * with array-of-struct variables in the root of a buffer block, only ever return [0]. + // * otherwise, array suffixes are added whenever we iterate, even if that means expanding out an array. + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + // is this variable inside a buffer block. This flag is set back to false after we iterate inside the first array element. + bool blockParent = (baseType.getBasicType() == EbtBlock && baseType.getQualifier().storage == EvqBuffer); + + // process the part of the dereference chain that was explicit in the shader + TString name = baseName; + const TType* terminalType = &baseType; + for (; deref != derefs.end(); ++deref) { + TIntermBinary* visitNode = *deref; + terminalType = &visitNode->getType(); + int index; + switch (visitNode->getOp()) { + case EOpIndexIndirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + // Visit all the indices of this array, and for each one add on the remaining dereferencing + for (int i = 0; i < std::max(visitNode->getLeft()->getType().getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + if (terminalType->getBasicType() == EbtBlock) {} + else if (strictArraySuffix && blockParent) + newBaseName.append(TString("[0]")); + else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) + newBaseName.append(TString("[") + String(i) + "]"); + TList::const_iterator nextDeref = deref; + ++nextDeref; + blowUpActiveAggregate(*terminalType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize, + topLevelArraySize, topLevelArrayStride, baseStorage, active); + + if (offset >= 0) + offset += stride; + } + + // it was all completed in the recursive calls above + return; + } + case EOpIndexDirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (terminalType->getBasicType() == EbtBlock) {} + else if (strictArraySuffix && blockParent) + name.append(TString("[0]")); + else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) { + name.append(TString("[") + String(index) + "]"); + + if (offset >= 0) + offset += stride * index; + } + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + // expand top-level arrays in blocks with [0] suffix + if (topLevelArrayStride != 0 && visitNode->getLeft()->getType().isArray()) { + blockParent = false; + } + break; + } + case EOpIndexDirectStruct: + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (offset >= 0) + offset += intermediate.getOffset(visitNode->getLeft()->getType(), index); + if (name.size() > 0) + name.append("."); + name.append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName()); + + // expand non top-level arrays with [x] suffix + if (visitNode->getLeft()->getType().getBasicType() != EbtBlock && terminalType->isArray()) + { + blockParent = false; + } + break; + default: + break; + } + } + + // if the terminalType is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(*terminalType)) { + // the base offset of this node, that children are relative to + int baseOffset = offset; + + if (terminalType->isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + + int stride = 0; + if (offset >= 0) + stride = getArrayStride(baseType, *terminalType); + + int arrayIterateSize = std::max(terminalType->getOuterArraySize(), 1); + + // for top-level arrays in blocks, only expand [0] to avoid explosion of items + if ((strictArraySuffix && blockParent) || + ((topLevelArraySize == arrayIterateSize) && (topLevelArrayStride == 0))) { + arrayIterateSize = 1; + } + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + for (int i = 0; i < arrayIterateSize; ++i) { + TString newBaseName = name; + if (terminalType->getBasicType() != EbtBlock) + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(*terminalType, 0); + if (offset >= 0) + offset = baseOffset + stride * i; + + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + topLevelArraySize, topLevelArrayStride, baseStorage, active); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *terminalType->getStruct(); + + TVector memberOffsets; + + if (baseOffset >= 0) { + memberOffsets.resize(typeList.size()); + getOffsets(*terminalType, memberOffsets); + } + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(*terminalType, i); + if (offset >= 0) + offset = baseOffset + memberOffsets[i]; + + int arrayStride = topLevelArrayStride; + if (terminalType->getBasicType() == EbtBlock && terminalType->getQualifier().storage == EvqBuffer && + derefType.isArray()) { + arrayStride = getArrayStride(baseType, derefType); + } + + if (topLevelArraySize == -1 && arrayStride == 0 && blockParent) + topLevelArraySize = 1; + + if (strictArraySuffix && blockParent) { + // if this member is an array, store the top-level array stride but start the explosion from + // the inner struct type. + if (derefType.isArray() && derefType.isStruct()) { + newBaseName.append("[0]"); + auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0); + blowUpActiveAggregate(TType(derefType, 0), newBaseName, derefs, derefs.end(), memberOffsets[i], + blockIndex, 0, dimSize, arrayStride, terminalType->getQualifier().storage, false); + } + else if (derefType.isArray()) { + auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0); + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex, + 0, dimSize, 0, terminalType->getQualifier().storage, false); + } + else { + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex, + 0, 1, 0, terminalType->getQualifier().storage, false); + } + } else { + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + topLevelArraySize, arrayStride, baseStorage, active); + } + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && terminalType->isArray()) { + name.append(TString("[0]")); + } + + // Finally, add a full string to the reflection database, and update the array size if necessary. + // If the dereferenced entity to record is an array, compute the size and update the maximum size. + + // there might not be a final array dereference, it could have been copied as an array object + if (arraySize == 0) + arraySize = mapToGlArraySize(*terminalType); + + TReflection::TMapIndexToReflection& variables = reflection.GetVariableMapForStorage(baseStorage); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (it == reflection.nameToIndex.end()) { + int uniformIndex = (int)variables.size(); + reflection.nameToIndex[name.c_str()] = uniformIndex; + variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType), + arraySize, blockIndex)); + if (terminalType->isArray()) { + variables.back().arrayStride = getArrayStride(baseType, *terminalType); + if (topLevelArrayStride == 0) + topLevelArrayStride = variables.back().arrayStride; + } + + if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->isAtomic()) + reflection.atomicCounterUniformIndices.push_back(uniformIndex); + + variables.back().topLevelArraySize = topLevelArraySize; + variables.back().topLevelArrayStride = topLevelArrayStride; + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } else { + if (arraySize > 1) { + int& reflectedArraySize = variables[it->second].size; + reflectedArraySize = std::max(arraySize, reflectedArraySize); + } + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + + // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow. + void blowUpIOAggregate(bool input, const TString &baseName, const TType &type) + { + TString name = baseName; + + // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(type)) { + if (type.isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(type, 0); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *type.getStruct(); + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(type, i); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) { + name.append(TString("[0]")); + } + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + std::string namespacedName = input ? "in " : "out "; + namespacedName += name.c_str(); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[namespacedName] = (int)ioItems.size(); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + + // Add a uniform dereference where blocks/struct/arrays are involved in the access. + // Handles the situation where the left node is at the correct or too coarse a + // granularity for reflection. (That is, further dereferences up the tree will be + // skipped.) Earlier dereferences, down the tree, will be handled + // at the same time, and logged to prevent reprocessing as the tree is traversed. + // + // Note: Other things like the following must be caught elsewhere: + // - a simple non-array, non-struct variable (no dereference even conceivable) + // - an aggregrate consumed en masse, without a dereference + // + // So, this code is for cases like + // - a struct/block dereferencing a member (whether the member is array or not) + // - an array of struct + // - structs/arrays containing the above + // + void addDereferencedUniform(TIntermBinary* topNode) + { + // See if too fine-grained to process (wait to get further down the tree) + const TType& leftType = topNode->getLeft()->getType(); + if ((leftType.isVector() || leftType.isMatrix()) && ! leftType.isArray()) + return; + + // We have an array or structure or block dereference, see if it's a uniform + // based dereference (if not, skip it). + TIntermSymbol* base = findBase(topNode); + if (! base || ! base->getQualifier().isUniformOrBuffer()) + return; + + // See if we've already processed this (e.g., in the middle of something + // we did earlier), and if so skip it + if (processedDerefs.find(topNode) != processedDerefs.end()) + return; + + // Process this uniform dereference + + int offset = -1; + int blockIndex = -1; + bool anonymous = false; + + // See if we need to record the block itself + bool block = base->getBasicType() == EbtBlock; + if (block) { + offset = 0; + anonymous = IsAnonymous(base->getName()); + + const TString& blockName = base->getType().getTypeName(); + TString baseName; + + if (! anonymous) + baseName = blockName; + + blockIndex = addBlockName(blockName, base->getType(), intermediate.getBlockSize(base->getType())); + + if (reflection.options & EShReflectionAllBlockVariables) { + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + TList derefs; + + // otherwise - if we're not using strict array suffix rules, or this isn't a block so we are + // expanding root arrays anyway, just start the iteration from the base block type. + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, -1, 0, + base->getQualifier().storage, false); + } + } + + // Process the dereference chain, backward, accumulating the pieces for later forward traversal. + // If the topNode is a reflection-granularity-array dereference, don't include that last dereference. + TList derefs; + for (TIntermBinary* visitNode = topNode; visitNode; visitNode = visitNode->getLeft()->getAsBinaryNode()) { + if (isReflectionGranularity(visitNode->getLeft()->getType())) + continue; + + derefs.push_front(visitNode); + processedDerefs.insert(visitNode); + } + processedDerefs.insert(base); + + // See if we have a specific array size to stick to while enumerating the explosion of the aggregate + int arraySize = 0; + if (isReflectionGranularity(topNode->getLeft()->getType()) && topNode->getLeft()->isArray()) { + if (topNode->getOp() == EOpIndexDirect) + arraySize = topNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst() + 1; + } + + // Put the dereference chain together, forward + TString baseName; + if (! anonymous) { + if (block) + baseName = base->getType().getTypeName(); + else + baseName = base->getName(); + } + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, -1, 0, + base->getQualifier().storage, true); + } + + int addBlockName(const TString& name, const TType& type, int size) + { + int blockIndex = 0; + if (type.isArray()) { + TType derefType(type, 0); + for (int e = 0; e < type.getOuterArraySize(); ++e) { + int memberBlockIndex = addBlockName(name + "[" + String(e) + "]", derefType, size); + if (e == 0) + blockIndex = memberBlockIndex; + } + } else { + TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) { + blockIndex = (int)blocks.size(); + reflection.nameToIndex[name.c_str()] = blockIndex; + blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, blockIndex)); + + blocks.back().numMembers = countAggregateMembers(type); + + if (updateStageMasks) { + EShLanguageMask& stages = blocks.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + else { + blockIndex = it->second; + if (updateStageMasks) { + EShLanguageMask& stages = blocks[blockIndex].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + + return blockIndex; + } + + // Are we at a level in a dereference chain at which individual active uniform queries are made? + bool isReflectionGranularity(const TType& type) + { + return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct && !type.isArrayOfArrays(); + } + + // For a binary operation indexing into an aggregate, chase down the base of the aggregate. + // Return nullptr if the topology does not fit this situation. + TIntermSymbol* findBase(const TIntermBinary* node) + { + TIntermSymbol *base = node->getLeft()->getAsSymbolNode(); + if (base) + return base; + TIntermBinary* left = node->getLeft()->getAsBinaryNode(); + if (! left) + return nullptr; + + return findBase(left); + } + + // + // Translate a glslang sampler type into the GL API #define number. + // + int mapSamplerToGlType(TSampler sampler) + { + if (! sampler.image) { + // a sampler... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + if (sampler.shadow) + return sampler.arrayed ? GL_SAMPLER_1D_ARRAY_SHADOW : GL_SAMPLER_1D_SHADOW; + else + return sampler.arrayed ? GL_SAMPLER_1D_ARRAY : GL_SAMPLER_1D; + case Esd2D: + if (sampler.ms) { + return sampler.arrayed ? GL_SAMPLER_2D_MULTISAMPLE_ARRAY : GL_SAMPLER_2D_MULTISAMPLE; + } else { + if (sampler.shadow) + return sampler.arrayed ? GL_SAMPLER_2D_ARRAY_SHADOW : GL_SAMPLER_2D_SHADOW; + else + return sampler.arrayed ? GL_SAMPLER_2D_ARRAY : GL_SAMPLER_2D; + } + case Esd3D: + return GL_SAMPLER_3D; + case EsdCube: + if (sampler.shadow) + return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW : GL_SAMPLER_CUBE_SHADOW; + else + return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY : GL_SAMPLER_CUBE; + case EsdRect: + return sampler.shadow ? GL_SAMPLER_2D_RECT_SHADOW : GL_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_SAMPLER_BUFFER; + default: + return 0; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + if (sampler.shadow) + return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_1D_SHADOW_AMD; + else + return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_AMD : GL_FLOAT16_SAMPLER_1D_AMD; + case Esd2D: + if (sampler.ms) { + return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD; + } else { + if (sampler.shadow) + return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_SHADOW_AMD; + else + return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_AMD; + } + case Esd3D: + return GL_FLOAT16_SAMPLER_3D_AMD; + case EsdCube: + if (sampler.shadow) + return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD; + else + return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_SAMPLER_CUBE_AMD; + case EsdRect: + return sampler.shadow ? GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_SAMPLER_BUFFER_AMD; + default: + return 0; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_SAMPLER_1D_ARRAY : GL_INT_SAMPLER_1D; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_INT_SAMPLER_2D_MULTISAMPLE; + else + return sampler.arrayed ? GL_INT_SAMPLER_2D_ARRAY : GL_INT_SAMPLER_2D; + case Esd3D: + return GL_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_SAMPLER_CUBE_MAP_ARRAY : GL_INT_SAMPLER_CUBE; + case EsdRect: + return GL_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_INT_SAMPLER_BUFFER; + default: + return 0; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_1D_ARRAY : GL_UNSIGNED_INT_SAMPLER_1D; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE; + else + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_ARRAY : GL_UNSIGNED_INT_SAMPLER_2D; + case Esd3D: + return GL_UNSIGNED_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_SAMPLER_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_SAMPLER_BUFFER; + default: + return 0; + } + default: + return 0; + } + } else { + // an image... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_IMAGE_1D_ARRAY : GL_IMAGE_1D; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_IMAGE_2D_MULTISAMPLE_ARRAY : GL_IMAGE_2D_MULTISAMPLE; + else + return sampler.arrayed ? GL_IMAGE_2D_ARRAY : GL_IMAGE_2D; + case Esd3D: + return GL_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_IMAGE_CUBE_MAP_ARRAY : GL_IMAGE_CUBE; + case EsdRect: + return GL_IMAGE_2D_RECT; + case EsdBuffer: + return GL_IMAGE_BUFFER; + default: + return 0; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_FLOAT16_IMAGE_1D_ARRAY_AMD : GL_FLOAT16_IMAGE_1D_AMD; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD; + else + return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_AMD; + case Esd3D: + return GL_FLOAT16_IMAGE_3D_AMD; + case EsdCube: + return sampler.arrayed ? GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_IMAGE_CUBE_AMD; + case EsdRect: + return GL_FLOAT16_IMAGE_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_IMAGE_BUFFER_AMD; + default: + return 0; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_IMAGE_1D_ARRAY : GL_INT_IMAGE_1D; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY : GL_INT_IMAGE_2D_MULTISAMPLE; + else + return sampler.arrayed ? GL_INT_IMAGE_2D_ARRAY : GL_INT_IMAGE_2D; + case Esd3D: + return GL_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_IMAGE_CUBE_MAP_ARRAY : GL_INT_IMAGE_CUBE; + case EsdRect: + return GL_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_INT_IMAGE_BUFFER; + default: + return 0; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_1D_ARRAY : GL_UNSIGNED_INT_IMAGE_1D; + case Esd2D: + if (sampler.ms) + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE; + else + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_ARRAY : GL_UNSIGNED_INT_IMAGE_2D; + case Esd3D: + return GL_UNSIGNED_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_IMAGE_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_IMAGE_BUFFER; + default: + return 0; + } + default: + return 0; + } + } + } + + // + // Translate a glslang type into the GL API #define number. + // Ignores arrayness. + // + int mapToGlType(const TType& type) + { + switch (type.getBasicType()) { + case EbtSampler: + return mapSamplerToGlType(type.getSampler()); + case EbtStruct: + case EbtBlock: + case EbtVoid: + return 0; + default: + break; + } + + if (type.isVector()) { + int offset = type.getVectorSize() - 2; + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT_VEC2 + offset; + case EbtDouble: return GL_DOUBLE_VEC2 + offset; + case EbtFloat16: return GL_FLOAT16_VEC2_NV + offset; + case EbtInt: return GL_INT_VEC2 + offset; + case EbtUint: return GL_UNSIGNED_INT_VEC2 + offset; + case EbtInt64: return GL_INT64_VEC2_ARB + offset; + case EbtUint64: return GL_UNSIGNED_INT64_VEC2_ARB + offset; + case EbtBool: return GL_BOOL_VEC2 + offset; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER + offset; + default: return 0; + } + } + if (type.isMatrix()) { + switch (type.getBasicType()) { + case EbtFloat: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT2; + case 3: return GL_FLOAT_MAT2x3; + case 4: return GL_FLOAT_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT3x2; + case 3: return GL_FLOAT_MAT3; + case 4: return GL_FLOAT_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT4x2; + case 3: return GL_FLOAT_MAT4x3; + case 4: return GL_FLOAT_MAT4; + default: return 0; + } + default: return 0; + } + case EbtDouble: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT2; + case 3: return GL_DOUBLE_MAT2x3; + case 4: return GL_DOUBLE_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT3x2; + case 3: return GL_DOUBLE_MAT3; + case 4: return GL_DOUBLE_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT4x2; + case 3: return GL_DOUBLE_MAT4x3; + case 4: return GL_DOUBLE_MAT4; + default: return 0; + } + default: return 0; + } + case EbtFloat16: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT2_AMD; + case 3: return GL_FLOAT16_MAT2x3_AMD; + case 4: return GL_FLOAT16_MAT2x4_AMD; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT3x2_AMD; + case 3: return GL_FLOAT16_MAT3_AMD; + case 4: return GL_FLOAT16_MAT3x4_AMD; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT4x2_AMD; + case 3: return GL_FLOAT16_MAT4x3_AMD; + case 4: return GL_FLOAT16_MAT4_AMD; + default: return 0; + } + default: return 0; + } + default: + return 0; + } + } + if (type.getVectorSize() == 1) { + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT; + case EbtDouble: return GL_DOUBLE; + case EbtFloat16: return GL_FLOAT16_NV; + case EbtInt: return GL_INT; + case EbtUint: return GL_UNSIGNED_INT; + case EbtInt64: return GL_INT64_ARB; + case EbtUint64: return GL_UNSIGNED_INT64_ARB; + case EbtBool: return GL_BOOL; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER; + default: return 0; + } + } + + return 0; + } + + int mapToGlArraySize(const TType& type) + { + return type.isArray() ? type.getOuterArraySize() : 1; + } + + const TIntermediate& intermediate; + TReflection& reflection; + std::set processedDerefs; + bool updateStageMasks; + +protected: + TReflectionTraverser(TReflectionTraverser&); + TReflectionTraverser& operator=(TReflectionTraverser&); +}; + +// +// Implement the traversal functions of interest. +// + +// To catch dereferenced aggregates that must be reflected. +// This catches them at the highest level possible in the tree. +bool TReflectionTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + switch (node->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + addDereferencedUniform(node); + break; + default: + break; + } + + // still need to visit everything below, which could contain sub-expressions + // containing different uniforms + return true; +} + +// To reflect non-dereferenced objects. +void TReflectionTraverser::visitSymbol(TIntermSymbol* base) +{ + if (base->getQualifier().storage == EvqUniform) { + if (base->getBasicType() == EbtBlock) { + if (reflection.options & EShReflectionSharedStd140UBO) { + addUniform(*base); + } + } else { + addUniform(*base); + } + } + + // #TODO add std140/layout active rules for ssbo, same with ubo. + // Storage buffer blocks will be collected and expanding in this part. + if((reflection.options & EShReflectionSharedStd140SSBO) && + (base->getQualifier().storage == EvqBuffer && base->getBasicType() == EbtBlock && + (base->getQualifier().layoutPacking == ElpStd140 || base->getQualifier().layoutPacking == ElpShared))) + addUniform(*base); + + if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) || + (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput())) + addPipeIOVariable(*base); +} + +// +// Implement TObjectReflection methods. +// + +TObjectReflection::TObjectReflection(const std::string &pName, const TType &pType, int pOffset, int pGLDefineType, + int pSize, int pIndex) + : name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1), + numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone()) +{ +} + +int TObjectReflection::getBinding() const +{ + if (type == nullptr || !type->getQualifier().hasBinding()) + return -1; + return type->getQualifier().layoutBinding; +} + +void TObjectReflection::dump() const +{ + printf("%s: offset %d, type %x, size %d, index %d, binding %d, stages %d", name.c_str(), offset, glDefineType, size, + index, getBinding(), stages); + + if (counterIndex != -1) + printf(", counter %d", counterIndex); + + if (numMembers != -1) + printf(", numMembers %d", numMembers); + + if (arrayStride != 0) + printf(", arrayStride %d", arrayStride); + + if (topLevelArrayStride != 0) + printf(", topLevelArrayStride %d", topLevelArrayStride); + + printf("\n"); +} + +// +// Implement TReflection methods. +// + +// Track any required attribute reflection, such as compute shader numthreads. +// +void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediate& intermediate) +{ + if (stage == EShLangCompute) { + // Remember thread dimensions + for (int dim=0; dim<3; ++dim) + localSize[dim] = intermediate.getLocalSize(dim); + } +} + +// build counter block index associations for buffers +void TReflection::buildCounterIndices(const TIntermediate& intermediate) +{ +#ifdef ENABLE_HLSL + // search for ones that have counters + for (int i = 0; i < int(indexToUniformBlock.size()); ++i) { + const TString counterName(intermediate.addCounterBufferName(indexToUniformBlock[i].name).c_str()); + const int index = getIndex(counterName); + + if (index >= 0) + indexToUniformBlock[i].counterIndex = index; + } +#else + (void)intermediate; +#endif +} + +// build Shader Stages mask for all uniforms +void TReflection::buildUniformStageMask(const TIntermediate& intermediate) +{ + if (options & EShReflectionAllBlockVariables) + return; + + for (int i = 0; i < int(indexToUniform.size()); ++i) { + indexToUniform[i].stages = static_cast(indexToUniform[i].stages | 1 << intermediate.getStage()); + } + + for (int i = 0; i < int(indexToBufferVariable.size()); ++i) { + indexToBufferVariable[i].stages = + static_cast(indexToBufferVariable[i].stages | 1 << intermediate.getStage()); + } +} + +// Merge live symbols from 'intermediate' into the existing reflection database. +// +// Returns false if the input is too malformed to do this. +bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate) +{ + if (intermediate.getTreeRoot() == nullptr || + intermediate.getNumEntryPoints() != 1 || + intermediate.isRecursive()) + return false; + + buildAttributeReflection(stage, intermediate); + + TReflectionTraverser it(intermediate, *this); + + for (auto& sequnence : intermediate.getTreeRoot()->getAsAggregate()->getSequence()) { + if (sequnence->getAsAggregate() != nullptr) { + if (sequnence->getAsAggregate()->getOp() == glslang::EOpLinkerObjects) { + it.updateStageMasks = false; + TIntermAggregate* linkerObjects = sequnence->getAsAggregate(); + for (auto& sequnence : linkerObjects->getSequence()) { + auto pNode = sequnence->getAsSymbolNode(); + if (pNode != nullptr) { + if ((pNode->getQualifier().storage == EvqUniform && + (options & EShReflectionSharedStd140UBO)) || + (pNode->getQualifier().storage == EvqBuffer && + (options & EShReflectionSharedStd140SSBO))) { + // collect std140 and shared uniform block form AST + if ((pNode->getBasicType() == EbtBlock) && + ((pNode->getQualifier().layoutPacking == ElpStd140) || + (pNode->getQualifier().layoutPacking == ElpShared))) { + pNode->traverse(&it); + } + } + else if ((options & EShReflectionAllIOVariables) && + (pNode->getQualifier().isPipeInput() || pNode->getQualifier().isPipeOutput())) + { + pNode->traverse(&it); + } + } + } + } else { + // This traverser will travers all function in AST. + // If we want reflect uncalled function, we need set linke message EShMsgKeepUncalled. + // When EShMsgKeepUncalled been set to true, all function will be keep in AST, even it is a uncalled function. + // This will keep some uniform variables in reflection, if those uniform variables is used in these uncalled function. + // + // If we just want reflect only live node, we can use a default link message or set EShMsgKeepUncalled false. + // When linke message not been set EShMsgKeepUncalled, linker won't keep uncalled function in AST. + // So, travers all function node can equivalent to travers live function. + it.updateStageMasks = true; + sequnence->getAsAggregate()->traverse(&it); + } + } + } + it.updateStageMasks = true; + + buildCounterIndices(intermediate); + buildUniformStageMask(intermediate); + + return true; +} + +void TReflection::dump() +{ + printf("Uniform reflection:\n"); + for (size_t i = 0; i < indexToUniform.size(); ++i) + indexToUniform[i].dump(); + printf("\n"); + + printf("Uniform block reflection:\n"); + for (size_t i = 0; i < indexToUniformBlock.size(); ++i) + indexToUniformBlock[i].dump(); + printf("\n"); + + printf("Buffer variable reflection:\n"); + for (size_t i = 0; i < indexToBufferVariable.size(); ++i) + indexToBufferVariable[i].dump(); + printf("\n"); + + printf("Buffer block reflection:\n"); + for (size_t i = 0; i < indexToBufferBlock.size(); ++i) + indexToBufferBlock[i].dump(); + printf("\n"); + + printf("Pipeline input reflection:\n"); + for (size_t i = 0; i < indexToPipeInput.size(); ++i) + indexToPipeInput[i].dump(); + printf("\n"); + + printf("Pipeline output reflection:\n"); + for (size_t i = 0; i < indexToPipeOutput.size(); ++i) + indexToPipeOutput[i].dump(); + printf("\n"); + + if (getLocalSize(0) > 1) { + static const char* axis[] = { "X", "Y", "Z" }; + + for (int dim=0; dim<3; ++dim) + if (getLocalSize(dim) > 1) + printf("Local size %s: %u\n", axis[dim], getLocalSize(dim)); + + printf("\n"); + } + + // printf("Live names\n"); + // for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it) + // printf("%s: %d\n", it->first.c_str(), it->second); + // printf("\n"); +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.h new file mode 100644 index 00000000..8315b112 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/reflection.h @@ -0,0 +1,220 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _REFLECTION_INCLUDED +#define _REFLECTION_INCLUDED + +#include "../Public/ShaderLang.h" +#include "../Include/BaseTypes.h" +#include "../Include/visibility.h" +#include +#include + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +namespace glslang { + +class TIntermediate; +class TIntermAggregate; +class TReflectionTraverser; + +// The full reflection database +class TReflection { +public: + TReflection(EShReflectionOptions opts, EShLanguage first, EShLanguage last) + : options(opts), firstStage(first), lastStage(last), badReflection(TObjectReflection::badReflection()) + { + for (int dim=0; dim<3; ++dim) + localSize[dim] = 0; + } + + virtual ~TReflection() {} + + // grow the reflection stage by stage + GLSLANG_EXPORT_FOR_TESTS + bool addStage(EShLanguage, const TIntermediate&); + + // for mapping a uniform index to a uniform object's description + int getNumUniforms() { return (int)indexToUniform.size(); } + const TObjectReflection& getUniform(int i) const + { + if (i >= 0 && i < (int)indexToUniform.size()) + return indexToUniform[i]; + else + return badReflection; + } + + // for mapping a block index to the block's description + int getNumUniformBlocks() const { return (int)indexToUniformBlock.size(); } + const TObjectReflection& getUniformBlock(int i) const + { + if (i >= 0 && i < (int)indexToUniformBlock.size()) + return indexToUniformBlock[i]; + else + return badReflection; + } + + // for mapping an pipeline input index to the input's description + int getNumPipeInputs() { return (int)indexToPipeInput.size(); } + const TObjectReflection& getPipeInput(int i) const + { + if (i >= 0 && i < (int)indexToPipeInput.size()) + return indexToPipeInput[i]; + else + return badReflection; + } + + // for mapping an pipeline output index to the output's description + int getNumPipeOutputs() { return (int)indexToPipeOutput.size(); } + const TObjectReflection& getPipeOutput(int i) const + { + if (i >= 0 && i < (int)indexToPipeOutput.size()) + return indexToPipeOutput[i]; + else + return badReflection; + } + + // for mapping from an atomic counter to the uniform index + int getNumAtomicCounters() const { return (int)atomicCounterUniformIndices.size(); } + const TObjectReflection& getAtomicCounter(int i) const + { + if (i >= 0 && i < (int)atomicCounterUniformIndices.size()) + return getUniform(atomicCounterUniformIndices[i]); + else + return badReflection; + } + + // for mapping a buffer variable index to a buffer variable object's description + int getNumBufferVariables() { return (int)indexToBufferVariable.size(); } + const TObjectReflection& getBufferVariable(int i) const + { + if (i >= 0 && i < (int)indexToBufferVariable.size()) + return indexToBufferVariable[i]; + else + return badReflection; + } + + // for mapping a storage block index to the storage block's description + int getNumStorageBuffers() const { return (int)indexToBufferBlock.size(); } + const TObjectReflection& getStorageBufferBlock(int i) const + { + if (i >= 0 && i < (int)indexToBufferBlock.size()) + return indexToBufferBlock[i]; + else + return badReflection; + } + + // for mapping any name to its index (block names, uniform names and input/output names) + int getIndex(const char* name) const + { + TNameToIndex::const_iterator it = nameToIndex.find(name); + if (it == nameToIndex.end()) + return -1; + else + return it->second; + } + + // see getIndex(const char*) + int getIndex(const TString& name) const { return getIndex(name.c_str()); } + + + // for mapping any name to its index (only pipe input/output names) + int getPipeIOIndex(const char* name, const bool inOrOut) const + { + TNameToIndex::const_iterator it = inOrOut ? pipeInNameToIndex.find(name) : pipeOutNameToIndex.find(name); + if (it == (inOrOut ? pipeInNameToIndex.end() : pipeOutNameToIndex.end())) + return -1; + else + return it->second; + } + + // see gePipeIOIndex(const char*, const bool) + int getPipeIOIndex(const TString& name, const bool inOrOut) const { return getPipeIOIndex(name.c_str(), inOrOut); } + + // Thread local size + unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; } + + void dump(); + +protected: + friend class glslang::TReflectionTraverser; + + void buildCounterIndices(const TIntermediate&); + void buildUniformStageMask(const TIntermediate& intermediate); + void buildAttributeReflection(EShLanguage, const TIntermediate&); + + // Need a TString hash: typedef std::unordered_map TNameToIndex; + typedef std::map TNameToIndex; + typedef std::vector TMapIndexToReflection; + typedef std::vector TIndices; + + TMapIndexToReflection& GetBlockMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferBlock; + return indexToUniformBlock; + } + TMapIndexToReflection& GetVariableMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferVariable; + return indexToUniform; + } + + EShReflectionOptions options; + + EShLanguage firstStage; + EShLanguage lastStage; + + TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this + TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed + TNameToIndex pipeInNameToIndex; // maps pipe in names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TNameToIndex pipeOutNameToIndex; // maps pipe out names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TMapIndexToReflection indexToUniform; + TMapIndexToReflection indexToUniformBlock; + TMapIndexToReflection indexToBufferVariable; + TMapIndexToReflection indexToBufferBlock; + TMapIndexToReflection indexToPipeInput; + TMapIndexToReflection indexToPipeOutput; + TIndices atomicCounterUniformIndices; + + unsigned int localSize[3]; +}; + +} // end namespace glslang + +#endif // _REFLECTION_INCLUDED diff --git a/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/span.h b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/span.h new file mode 100644 index 00000000..bd705fea --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/MachineIndependent/span.h @@ -0,0 +1,92 @@ +#pragma once + +// +// Copyright (C) 2023 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Partial implementation of std::span for C++11 +// Replace with std::span if repo standard is bumped to C++20 +// +// This code was copied from https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/layers/containers/custom_containers.h +template +class span { + public: + using pointer = T *; + using const_pointer = T const *; + using iterator = pointer; + using const_iterator = const_pointer; + + span() = default; + span(pointer start, size_t n) : data_(start), count_(n) {} + template + span(Iterator start, Iterator end) : data_(&(*start)), count_(end - start) {} + template + span(Container &c) : data_(c.data()), count_(c.size()) {} + + iterator begin() { return data_; } + const_iterator begin() const { return data_; } + + iterator end() { return data_ + count_; } + const_iterator end() const { return data_ + count_; } + + T &operator[](int i) { return data_[i]; } + const T &operator[](int i) const { return data_[i]; } + + T &front() { return *data_; } + const T &front() const { return *data_; } + + T &back() { return *(data_ + (count_ - 1)); } + const T &back() const { return *(data_ + (count_ - 1)); } + + size_t size() const { return count_; } + bool empty() const { return count_ == 0; } + + pointer data() { return data_; } + const_pointer data() const { return data_; } + + private: + pointer data_ = {}; + size_t count_ = 0; +}; + +// +// Allow type inference that using the constructor doesn't allow in C++11 +template +span make_span(T *begin, size_t count) { + return span(begin, count); +} +template +span make_span(T *begin, T *end) { + return make_span(begin, end); +} diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/CMakeLists.txt b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/CMakeLists.txt new file mode 100644 index 00000000..53d9ad38 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(OSDEPENDENT_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/ossource.cpp + PARENT_SCOPE) + +set(OSDEPENDENT_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/../osinclude.h + PARENT_SCOPE) + +add_library(OSDependent STATIC ${CMAKE_CURRENT_SOURCE_DIR}/../../stub.cpp) +set_property(TARGET OSDependent PROPERTY FOLDER glslang) +set_property(TARGET OSDependent PROPERTY POSITION_INDEPENDENT_CODE ON) + +if(GLSLANG_ENABLE_INSTALL AND NOT BUILD_SHARED_LIBS) + install(TARGETS OSDependent EXPORT glslang-targets) +endif() diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/ossource.cpp b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/ossource.cpp new file mode 100644 index 00000000..fbb51f7b --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Unix/ossource.cpp @@ -0,0 +1,62 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This file contains the Linux-specific functions +// +#include "../osinclude.h" + +#include + +#if !defined(__Fuchsia__) +#include +#endif + +namespace glslang { + +// #define DUMP_COUNTERS + +void OS_DumpMemoryCounters() +{ +#ifdef DUMP_COUNTERS + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage) == 0) + printf("Working set size: %ld\n", usage.ru_maxrss * 1024); +#else + printf("Recompile with DUMP_COUNTERS defined to see counters.\n"); +#endif +} + +} // end namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/CMakeLists.txt b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/CMakeLists.txt new file mode 100644 index 00000000..ef50d410 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (C) 2020-2025 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(ENABLE_GLSLANG_JS) + add_executable(glslang.js "glslang.js.cpp") + glslang_set_link_args(glslang.js) + target_link_libraries(glslang.js glslang) + + # Link library names that start with "-" are treated as link flags. + # "-Os" should be OK in MSVC; don't use /Os because CMake won't + # treat it as a link flag. + target_link_libraries(glslang.js "-Os") + + if(EMSCRIPTEN) + set_target_properties(glslang.js PROPERTIES + OUTPUT_NAME "glslang" + SUFFIX ".js") + em_link_pre_js(glslang.js "${CMAKE_CURRENT_SOURCE_DIR}/glslang.pre.js") + + target_link_libraries(glslang.js "--llvm-lto 1") + target_link_libraries(glslang.js "--closure 1") + target_link_libraries(glslang.js "-s MODULARIZE=1") + target_link_libraries(glslang.js "-s ALLOW_MEMORY_GROWTH=1") + target_link_libraries(glslang.js "-s FILESYSTEM=0") + + # We use ccall in glslang.pre.js, so make sure it's exposed + target_link_libraries(glslang.js "-s EXPORTED_RUNTIME_METHODS=ccall") + + if(ENABLE_EMSCRIPTEN_SINGLE_FILE) + target_link_libraries(glslang.js "-s SINGLE_FILE=1") + endif() + + if(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE) + target_link_libraries(glslang.js "-s ENVIRONMENT=node -s BINARYEN_ASYNC_COMPILATION=0") + else() + target_link_libraries(glslang.js "-s ENVIRONMENT=web,worker") + endif() + + if(NOT ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE) + add_custom_command(TARGET glslang.js POST_BUILD + COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_CURRENT_SOURCE_DIR}/glslang.after.js >> ${CMAKE_CURRENT_BINARY_DIR}/glslang.js + ) + endif() + endif() +endif() \ No newline at end of file diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.after.js b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.after.js new file mode 100644 index 00000000..c2cfc35a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.after.js @@ -0,0 +1,26 @@ +export default (() => { + const initialize = () => { + return new Promise(resolve => { + Module({ + locateFile() { + const i = import.meta.url.lastIndexOf('/') + return import.meta.url.substring(0, i) + '/glslang.wasm'; + }, + onRuntimeInitialized() { + resolve({ + compileGLSLZeroCopy: this.compileGLSLZeroCopy, + compileGLSL: this.compileGLSL, + }); + }, + }); + }); + }; + + let instance; + return () => { + if (!instance) { + instance = initialize(); + } + return instance; + }; +})(); diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.js.cpp b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.js.cpp new file mode 100644 index 00000000..c820da6a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.js.cpp @@ -0,0 +1,296 @@ +// +// Copyright (C) 2019 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include "../../../SPIRV/GlslangToSpv.h" +#include "../../../glslang/Public/ShaderLang.h" + +#ifndef __EMSCRIPTEN__ +#define EMSCRIPTEN_KEEPALIVE +#endif + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxMeshOutputVerticesEXT = */ 256, + /* .maxMeshOutputPrimitivesEXT = */ 512, + /* .maxMeshWorkGroupSizeX_EXT = */ 32, + /* .maxMeshWorkGroupSizeY_EXT = */ 1, + /* .maxMeshWorkGroupSizeZ_EXT = */ 1, + /* .maxTaskWorkGroupSizeX_EXT = */ 32, + /* .maxTaskWorkGroupSizeY_EXT = */ 1, + /* .maxTaskWorkGroupSizeZ_EXT = */ 1, + /* .maxMeshViewCountEXT = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +static bool initialized = false; + +extern "C" { + +/* + * Takes in a GLSL shader as a string and converts it to SPIR-V in binary form. + * + * |glsl| Null-terminated string containing the shader to be converted. + * |stage_int| Magic number indicating the type of shader being processed. +* Legal values are as follows: + * Vertex = 0 + * Fragment = 4 + * Compute = 5 + * |gen_debug| Flag to indicate if debug information should be generated. + * |spirv| Output parameter for a pointer to the resulting SPIR-V data. + * |spirv_len| Output parameter for the length of the output binary buffer. + * + * Returns a void* pointer which, if not null, must be destroyed by + * destroy_output_buffer.o. (This is not the same pointer returned in |spirv|.) + * If null, the compilation failed. + */ +EMSCRIPTEN_KEEPALIVE +void* convert_glsl_to_spirv(const char* glsl, + int stage_int, + bool gen_debug, + glslang::EShTargetLanguageVersion spirv_version, + uint32_t** spirv, + size_t* spirv_len) +{ + if (glsl == nullptr) { + fprintf(stderr, "Input pointer null\n"); + return nullptr; + } + if (spirv == nullptr || spirv_len == nullptr) { + fprintf(stderr, "Output pointer null\n"); + return nullptr; + } + *spirv = nullptr; + *spirv_len = 0; + + if (stage_int != 0 && stage_int != 4 && stage_int != 5) { + fprintf(stderr, "Invalid shader stage\n"); + return nullptr; + } + EShLanguage stage = static_cast(stage_int); + switch (spirv_version) { + case glslang::EShTargetSpv_1_0: + case glslang::EShTargetSpv_1_1: + case glslang::EShTargetSpv_1_2: + case glslang::EShTargetSpv_1_3: + case glslang::EShTargetSpv_1_4: + case glslang::EShTargetSpv_1_5: + break; + default: + fprintf(stderr, "Invalid SPIR-V version number\n"); + return nullptr; + } + + if (!initialized) { + glslang::InitializeProcess(); + initialized = true; + } + + glslang::TShader shader(stage); + shader.setStrings(&glsl, 1); + shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100); + shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); + shader.setEnvTarget(glslang::EShTargetSpv, spirv_version); + if (!shader.parse(&DefaultTBuiltInResource, 100, true, EShMsgDefault)) { + fprintf(stderr, "Parse failed\n"); + fprintf(stderr, "%s\n", shader.getInfoLog()); + return nullptr; + } + + glslang::TProgram program; + program.addShader(&shader); + if (!program.link(EShMsgDefault)) { + fprintf(stderr, "Link failed\n"); + fprintf(stderr, "%s\n", program.getInfoLog()); + return nullptr; + } + + glslang::SpvOptions spvOptions; + spvOptions.generateDebugInfo = gen_debug; + spvOptions.optimizeSize = false; + spvOptions.disassemble = false; + spvOptions.validate = false; + + std::vector* output = new std::vector; + glslang::GlslangToSpv(*program.getIntermediate(stage), *output, nullptr, &spvOptions); + + *spirv_len = output->size(); + *spirv = output->data(); + return output; +} + +/* + * Destroys a buffer created by convert_glsl_to_spirv + */ +EMSCRIPTEN_KEEPALIVE +void destroy_output_buffer(void* p) +{ + delete static_cast*>(p); +} + +} // extern "C" + +/* + * For non-Emscripten builds we supply a generic main, so that the glslang.js + * build target can generate an executable with a trivial use case instead of + * generating a WASM binary. This is done so that there is a target that can be + * built and output analyzed using desktop tools, since WASM binaries are + * specific to the Emscripten toolchain. + */ +#ifndef __EMSCRIPTEN__ +int main() { + const char* input = R"(#version 310 es + +void main() { })"; + + uint32_t* output; + size_t output_len; + + void* id = convert_glsl_to_spirv(input, 4, false, glslang::EShTargetSpv_1_0, &output, &output_len); + assert(output != nullptr); + assert(output_len != 0); + destroy_output_buffer(id); + return 0; +} +#endif // ifndef __EMSCRIPTEN__ diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.pre.js b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.pre.js new file mode 100644 index 00000000..390390e9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Web/glslang.pre.js @@ -0,0 +1,56 @@ +Module['compileGLSLZeroCopy'] = function(glsl, shader_stage, gen_debug, spirv_version) { + gen_debug = !!gen_debug; + + var shader_stage_int; // EShLanguage + switch (shader_stage) { + case 'vertex': shader_stage_int = 0; break; + case 'fragment': shader_stage_int = 4; break; + case 'compute': shader_stage_int = 5; break; + default: + throw new Error("shader_stage must be 'vertex', 'fragment', or 'compute'."); + } + + spirv_version = spirv_version || '1.0'; + var spirv_version_int; // EShTargetLanguageVersion + switch (spirv_version) { + case '1.0': spirv_version_int = (1 << 16) | (0 << 8); break; + case '1.1': spirv_version_int = (1 << 16) | (1 << 8); break; + case '1.2': spirv_version_int = (1 << 16) | (2 << 8); break; + case '1.3': spirv_version_int = (1 << 16) | (3 << 8); break; + case '1.4': spirv_version_int = (1 << 16) | (4 << 8); break; + case '1.5': spirv_version_int = (1 << 16) | (5 << 8); break; + default: + throw new Error("spirv_version must be '1.0' ~ '1.5'."); + } + + var p_output = Module['_malloc'](4); + var p_output_len = Module['_malloc'](4); + var id = Module['ccall']('convert_glsl_to_spirv', + 'number', + ['string', 'number', 'boolean', 'number', 'number', 'number'], + [glsl, shader_stage_int, gen_debug, spirv_version_int, p_output, p_output_len]); + var output = getValue(p_output, 'i32'); + var output_len = getValue(p_output_len, 'i32'); + Module['_free'](p_output); + Module['_free'](p_output_len); + + if (id === 0) { + throw new Error('GLSL compilation failed'); + } + + var ret = {}; + var outputIndexU32 = output / 4; + ret['data'] = Module['HEAPU32'].subarray(outputIndexU32, outputIndexU32 + output_len); + ret['free'] = function() { + Module['_destroy_output_buffer'](id); + }; + + return ret; +}; + +Module['compileGLSL'] = function(glsl, shader_stage, gen_debug, spirv_version) { + var compiled = Module['compileGLSLZeroCopy'](glsl, shader_stage, gen_debug, spirv_version); + var ret = compiled['data'].slice() + compiled['free'](); + return ret; +}; diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/CMakeLists.txt b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/CMakeLists.txt new file mode 100644 index 00000000..040f4348 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(OSDEPENDENT_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/ossource.cpp + PARENT_SCOPE) + +set(OSDEPENDENT_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/../osinclude.h + PARENT_SCOPE) + +add_library(OSDependent STATIC) + +target_sources(OSDependent PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../stub.cpp) + +set_property(TARGET OSDependent PROPERTY FOLDER glslang) +set_property(TARGET OSDependent PROPERTY POSITION_INDEPENDENT_CODE ON) + +# MinGW GCC complains about function pointer casts to void*. +# Turn that off with -fpermissive. +if(MINGW AND ${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + set_source_files_properties(${OSDEPENDENT_SOURCES} PROPERTIES COMPILE_FLAGS -fpermissive) +endif() + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif() + +if(GLSLANG_ENABLE_INSTALL) + install(TARGETS OSDependent EXPORT glslang-targets) +endif() diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/ossource.cpp b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/ossource.cpp new file mode 100644 index 00000000..d7f89f71 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/Windows/ossource.cpp @@ -0,0 +1,67 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../osinclude.h" + +#define STRICT +#define VC_EXTRALEAN 1 +#include +#include +#include +#include + +// +// This file contains the Window-OS-specific functions +// + +#if !(defined(_WIN32) || defined(_WIN64)) +#error Trying to build a windows specific file in a non windows build. +#endif + +namespace glslang { + +//#define DUMP_COUNTERS + +void OS_DumpMemoryCounters() +{ +#ifdef DUMP_COUNTERS + PROCESS_MEMORY_COUNTERS counters; + GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)); + printf("Working set size: %d\n", counters.WorkingSetSize); +#else + printf("Recompile with DUMP_COUNTERS defined to see counters.\n"); +#endif +} + +} // namespace glslang diff --git a/engine/engine/3rdlibs/glslang/glslang/OSDependent/osinclude.h b/engine/engine/3rdlibs/glslang/glslang/OSDependent/osinclude.h new file mode 100644 index 00000000..da1c4f69 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/OSDependent/osinclude.h @@ -0,0 +1,45 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __OSINCLUDE_H +#define __OSINCLUDE_H + +#include "../Include/visibility.h" +namespace glslang { + +GLSLANG_EXPORT void OS_DumpMemoryCounters(); + +} // end namespace glslang + +#endif // __OSINCLUDE_H diff --git a/engine/engine/3rdlibs/glslang/glslang/Public/ResourceLimits.h b/engine/engine/3rdlibs/glslang/glslang/Public/ResourceLimits.h new file mode 100644 index 00000000..8245e12d --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Public/ResourceLimits.h @@ -0,0 +1,58 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ +#define _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ + +#include + +#include "../Include/ResourceLimits.h" +#include "../Include/visibility.h" + +// Return pointer to user-writable Resource to pass through API in +// future-proof way. +GLSLANG_EXPORT extern TBuiltInResource* GetResources(); + +// These are the default resources for TBuiltInResources, used for both +// - parsing this string for the case where the user didn't supply one, +// - dumping out a template for user construction of a config file. +GLSLANG_EXPORT extern const TBuiltInResource* GetDefaultResources(); + +// Returns the DefaultTBuiltInResource as a human-readable string. +GLSLANG_EXPORT std::string GetDefaultTBuiltInResourceString(); + +// Decodes the resource limits from |config| to |resources|. +GLSLANG_EXPORT void DecodeResourceLimits(TBuiltInResource* resources, char* config); + +#endif // _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Public/ShaderLang.h b/engine/engine/3rdlibs/glslang/glslang/Public/ShaderLang.h new file mode 100644 index 00000000..e681a55a --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Public/ShaderLang.h @@ -0,0 +1,1009 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _COMPILER_INTERFACE_INCLUDED_ +#define _COMPILER_INTERFACE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../Include/visibility.h" +#include "../MachineIndependent/Versions.h" + +#include +#include + +#ifdef _WIN32 + #define C_DECL __cdecl +#else + #define C_DECL +#endif + +// +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// + +#ifdef __cplusplus + extern "C" { +#endif + +// +// Call before doing any other compiler/linker operations. +// +// (Call once per process, not once per thread.) +// +GLSLANG_EXPORT int ShInitialize(); + +// +// Call this at process shutdown to clean up memory. +// +GLSLANG_EXPORT int ShFinalize(); + +// +// Types of languages the compiler can consume. +// +typedef enum { + EShLangVertex, + EShLangTessControl, + EShLangTessEvaluation, + EShLangGeometry, + EShLangFragment, + EShLangCompute, + EShLangRayGen, + EShLangRayGenNV = EShLangRayGen, + EShLangIntersect, + EShLangIntersectNV = EShLangIntersect, + EShLangAnyHit, + EShLangAnyHitNV = EShLangAnyHit, + EShLangClosestHit, + EShLangClosestHitNV = EShLangClosestHit, + EShLangMiss, + EShLangMissNV = EShLangMiss, + EShLangCallable, + EShLangCallableNV = EShLangCallable, + EShLangTask, + EShLangTaskNV = EShLangTask, + EShLangMesh, + EShLangMeshNV = EShLangMesh, + LAST_ELEMENT_MARKER(EShLangCount), +} EShLanguage; // would be better as stage, but this is ancient now + +typedef enum : unsigned { + EShLangVertexMask = (1 << EShLangVertex), + EShLangTessControlMask = (1 << EShLangTessControl), + EShLangTessEvaluationMask = (1 << EShLangTessEvaluation), + EShLangGeometryMask = (1 << EShLangGeometry), + EShLangFragmentMask = (1 << EShLangFragment), + EShLangComputeMask = (1 << EShLangCompute), + EShLangRayGenMask = (1 << EShLangRayGen), + EShLangRayGenNVMask = EShLangRayGenMask, + EShLangIntersectMask = (1 << EShLangIntersect), + EShLangIntersectNVMask = EShLangIntersectMask, + EShLangAnyHitMask = (1 << EShLangAnyHit), + EShLangAnyHitNVMask = EShLangAnyHitMask, + EShLangClosestHitMask = (1 << EShLangClosestHit), + EShLangClosestHitNVMask = EShLangClosestHitMask, + EShLangMissMask = (1 << EShLangMiss), + EShLangMissNVMask = EShLangMissMask, + EShLangCallableMask = (1 << EShLangCallable), + EShLangCallableNVMask = EShLangCallableMask, + EShLangTaskMask = (1 << EShLangTask), + EShLangTaskNVMask = EShLangTaskMask, + EShLangMeshMask = (1 << EShLangMesh), + EShLangMeshNVMask = EShLangMeshMask, + LAST_ELEMENT_MARKER(EShLanguageMaskCount), +} EShLanguageMask; + +namespace glslang { + +class TType; + +typedef enum { + EShSourceNone, + EShSourceGlsl, // GLSL, includes ESSL (OpenGL ES GLSL) + EShSourceHlsl, // HLSL + LAST_ELEMENT_MARKER(EShSourceCount), +} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead + +typedef enum { + EShClientNone, // use when there is no client, e.g. for validation + EShClientVulkan, // as GLSL dialect, specifies KHR_vulkan_glsl extension + EShClientOpenGL, // as GLSL dialect, specifies ARB_gl_spirv extension + LAST_ELEMENT_MARKER(EShClientCount), +} EShClient; + +typedef enum { + EShTargetNone, + EShTargetSpv, // SPIR-V (preferred spelling) + EshTargetSpv = EShTargetSpv, // legacy spelling + LAST_ELEMENT_MARKER(EShTargetCount), +} EShTargetLanguage; + +typedef enum { + EShTargetVulkan_1_0 = (1 << 22), // Vulkan 1.0 + EShTargetVulkan_1_1 = (1 << 22) | (1 << 12), // Vulkan 1.1 + EShTargetVulkan_1_2 = (1 << 22) | (2 << 12), // Vulkan 1.2 + EShTargetVulkan_1_3 = (1 << 22) | (3 << 12), // Vulkan 1.3 + EShTargetVulkan_1_4 = (1 << 22) | (4 << 12), // Vulkan 1.4 + EShTargetOpenGL_450 = 450, // OpenGL + LAST_ELEMENT_MARKER(EShTargetClientVersionCount = 6), +} EShTargetClientVersion; + +typedef EShTargetClientVersion EshTargetClientVersion; + +typedef enum { + EShTargetSpv_1_0 = (1 << 16), // SPIR-V 1.0 + EShTargetSpv_1_1 = (1 << 16) | (1 << 8), // SPIR-V 1.1 + EShTargetSpv_1_2 = (1 << 16) | (2 << 8), // SPIR-V 1.2 + EShTargetSpv_1_3 = (1 << 16) | (3 << 8), // SPIR-V 1.3 + EShTargetSpv_1_4 = (1 << 16) | (4 << 8), // SPIR-V 1.4 + EShTargetSpv_1_5 = (1 << 16) | (5 << 8), // SPIR-V 1.5 + EShTargetSpv_1_6 = (1 << 16) | (6 << 8), // SPIR-V 1.6 + LAST_ELEMENT_MARKER(EShTargetLanguageVersionCount = 7), +} EShTargetLanguageVersion; + +// +// Following are a series of helper enums for managing layouts and qualifiers, +// used for TPublicType, TType, others. +// + +enum TLayoutPacking { + ElpNone, + ElpShared, // default, but different than saying nothing + ElpStd140, + ElpStd430, + ElpPacked, + ElpScalar, + ElpCount // If expanding, see bitfield width below +}; + +struct TInputLanguage { + EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone + EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone + EShClient dialect; + int dialectVersion; // version of client's language definition, not the client (when not EShClientNone) + bool vulkanRulesRelaxed; +}; + +struct TClient { + EShClient client; + EShTargetClientVersion version; // version of client itself (not the client's input dialect) +}; + +struct TTarget { + EShTargetLanguage language; + EShTargetLanguageVersion version; // version to target, if SPIR-V, defined by "word 1" of the SPIR-V header + bool hlslFunctionality1; // can target hlsl_functionality1 extension(s) +}; + +// All source/client/target versions and settings. +// Can override previous methods of setting, when items are set here. +// Expected to grow, as more are added, rather than growing parameter lists. +struct TEnvironment { + TInputLanguage input; // definition of the input language + TClient client; // what client is the overall compilation being done for? + TTarget target; // what to generate +}; + +GLSLANG_EXPORT const char* StageName(EShLanguage); + +} // end namespace glslang + +// +// Types of output the linker will create. +// +typedef enum { + EShExVertexFragment, + EShExFragment +} EShExecutable; + +// +// Optimization level for the compiler. +// +typedef enum { + EShOptNoGeneration, + EShOptNone, + EShOptSimple, // Optimizations that can be done quickly + EShOptFull, // Optimizations that will take more time + LAST_ELEMENT_MARKER(EshOptLevelCount), +} EShOptimizationLevel; + +// +// Texture and Sampler transformation mode. +// +typedef enum { + EShTexSampTransKeep, // keep textures and samplers as is (default) + EShTexSampTransUpgradeTextureRemoveSampler, // change texture w/o embeded sampler into sampled texture and throw away all samplers + LAST_ELEMENT_MARKER(EShTexSampTransCount), +} EShTextureSamplerTransformMode; + +// +// Message choices for what errors and warnings are given. +// +enum EShMessages : unsigned { + EShMsgDefault = 0, // default is to give all required errors and extra warnings + EShMsgRelaxedErrors = (1 << 0), // be liberal in accepting input + EShMsgSuppressWarnings = (1 << 1), // suppress all warnings, except those required by the specification + EShMsgAST = (1 << 2), // print the AST intermediate representation + EShMsgSpvRules = (1 << 3), // issue messages for SPIR-V generation + EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V + EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor + EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics + EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit + EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions + EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules + EShMsgDebugInfo = (1 << 10), // save debug information + EShMsgHlslEnable16BitTypes = (1 << 11), // enable use of 16-bit types in SPIR-V for HLSL + EShMsgHlslLegalization = (1 << 12), // enable HLSL Legalization messages + EShMsgHlslDX9Compatible = (1 << 13), // enable HLSL DX9 compatible mode (for samplers and semantics) + EShMsgBuiltinSymbolTable = (1 << 14), // print the builtin symbol table + EShMsgEnhanced = (1 << 15), // enhanced message readability + EShMsgAbsolutePath = (1 << 16), // Output Absolute path for messages + EShMsgDisplayErrorColumn = (1 << 17), // Display error message column aswell as line + EShMsgLinkTimeOptimization = (1 << 18), // perform cross-stage optimizations during linking + EShMsgValidateCrossStageIO = (1 << 19), // validate shader inputs have matching outputs in previous stage + LAST_ELEMENT_MARKER(EShMsgCount), +}; + +// +// Options for building reflection +// +typedef enum { + EShReflectionDefault = 0, // default is original behaviour before options were added + EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes + EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection + EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader + EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately + EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive + EShReflectionUnwrapIOBlocks = (1 << 5), // unwrap input/output blocks the same as with uniform blocks + EShReflectionAllIOVariables = (1 << 6), // reflect all input/output variables, even if they are inactive + EShReflectionSharedStd140SSBO = (1 << 7), // Apply std140/shared rules for ubo to ssbo + EShReflectionSharedStd140UBO = (1 << 8), // Apply std140/shared rules for ubo to ssbo + LAST_ELEMENT_MARKER(EShReflectionCount), +} EShReflectionOptions; + +// +// Build a table for bindings. This can be used for locating +// attributes, uniforms, globals, etc., as needed. +// +typedef struct { + const char* name; + int binding; +} ShBinding; + +typedef struct { + int numBindings; + ShBinding* bindings; // array of bindings +} ShBindingTable; + +// +// ShHandle held by but opaque to the driver. It is allocated, +// managed, and de-allocated by the compiler/linker. Its contents +// are defined by and used by the compiler and linker. For example, +// symbol table information and object code passed from the compiler +// to the linker can be stored where ShHandle points. +// +// If handle creation fails, 0 will be returned. +// +typedef void* ShHandle; + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// +GLSLANG_EXPORT ShHandle ShConstructCompiler(const EShLanguage, int /*debugOptions unused*/); // one per shader +GLSLANG_EXPORT ShHandle ShConstructLinker(const EShExecutable, int /*debugOptions unused*/); // one per shader pair +GLSLANG_EXPORT ShHandle ShConstructUniformMap(); // one per uniform namespace (currently entire program object) +GLSLANG_EXPORT void ShDestruct(ShHandle); + +// +// The return value of ShCompile is boolean, non-zero indicating +// success. +// +// The info-log should be written by ShCompile into +// ShHandle, so it can answer future queries. +// +GLSLANG_EXPORT int ShCompile(const ShHandle, const char* const shaderStrings[], const int numStrings, + const int* lengths, const EShOptimizationLevel, const TBuiltInResource* resources, + int, // debugOptions unused + int defaultVersion = 110, // use 100 for ES environment, overridden by #version in shader + bool forwardCompatible = false, // give errors for use of deprecated features + EShMessages messages = EShMsgDefault, // warnings and errors + const char* fileName = nullptr +); + +GLSLANG_EXPORT int ShLinkExt( + const ShHandle, // linker object + const ShHandle h[], // compiler objects to link together + const int numHandles); + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +GLSLANG_EXPORT void ShSetEncryptionMethod(ShHandle); + +// +// All the following return 0 if the information is not +// available in the object passed down, or the object is bad. +// +GLSLANG_EXPORT const char* ShGetInfoLog(const ShHandle); +GLSLANG_EXPORT const void* ShGetExecutable(const ShHandle); +GLSLANG_EXPORT int ShSetVirtualAttributeBindings(const ShHandle, const ShBindingTable*); // to detect user aliasing +GLSLANG_EXPORT int ShSetFixedAttributeBindings(const ShHandle, const ShBindingTable*); // to force any physical mappings +// +// Tell the linker to never assign a vertex attribute to this list of physical attributes +// +GLSLANG_EXPORT int ShExcludeAttributes(const ShHandle, int *attributes, int count); + +// +// Returns the location ID of the named uniform. +// Returns -1 if error. +// +GLSLANG_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* name); + +#ifdef __cplusplus + } // end extern "C" +#endif + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface, which deprecates the above +// opaque handle-based interface. +// +// The below is further designed to handle multiple compilation units per stage, where +// the intermediate results, including the parse tree, are preserved until link time, +// rather than the above interface which is designed to have each compilation unit +// lowered at compile time. In the above model, linking occurs on the lowered results, +// whereas in this model intra-stage linking can occur at the parse tree +// (treeRoot in TIntermediate) level, and then a full stage can be lowered. +// + +#include +#include +#include + +class TCompiler; +class TInfoSink; + +namespace glslang { + +struct Version { + int major; + int minor; + int patch; + const char* flavor; +}; + +GLSLANG_EXPORT Version GetVersion(); +GLSLANG_EXPORT const char* GetEsslVersionString(); +GLSLANG_EXPORT const char* GetGlslVersionString(); +GLSLANG_EXPORT int GetKhronosToolId(); + +class TIntermediate; +class TProgram; +class TPoolAllocator; +class TIoMapResolver; + +// Call this exactly once per process before using anything else +GLSLANG_EXPORT bool InitializeProcess(); + +// Call once per process to tear down everything +GLSLANG_EXPORT void FinalizeProcess(); + +// Resource type for IO resolver +enum TResourceType { + EResSampler, + EResTexture, + EResImage, + EResUbo, + EResSsbo, + EResUav, + EResCount +}; + +enum TBlockStorageClass +{ + EbsUniform = 0, + EbsStorageBuffer, + EbsPushConstant, + EbsNone, // not a uniform or buffer variable + EbsCount, +}; + +// Make one TShader per shader that you will link into a program. Then +// - provide the shader through setStrings() or setStringsWithLengths() +// - optionally call setEnv*(), see below for more detail +// - optionally use setPreamble() to set a special shader string that will be +// processed before all others but won't affect the validity of #version +// - optionally call addProcesses() for each setting/transform, +// see comment for class TProcesses +// - call parse(): source language and target environment must be selected +// either by correct setting of EShMessages sent to parse(), or by +// explicitly calling setEnv*() +// - query the info logs +// +// N.B.: Does not yet support having the same TShader instance being linked into +// multiple programs. +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TShader { +public: + GLSLANG_EXPORT explicit TShader(EShLanguage); + GLSLANG_EXPORT virtual ~TShader(); + GLSLANG_EXPORT void setStrings(const char* const* s, int n); + GLSLANG_EXPORT void setStringsWithLengths( + const char* const* s, const int* l, int n); + GLSLANG_EXPORT void setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n); + void setPreamble(const char* s) { preamble = s; } + GLSLANG_EXPORT void setEntryPoint(const char* entryPoint); + GLSLANG_EXPORT void setSourceEntryPoint(const char* sourceEntryPointName); + GLSLANG_EXPORT void addProcesses(const std::vector&); + GLSLANG_EXPORT void setUniqueId(unsigned long long id); + GLSLANG_EXPORT void setOverrideVersion(int version); + GLSLANG_EXPORT void setDebugInfo(bool debugInfo); + + // IO resolver binding data: see comments in ShaderLang.cpp + GLSLANG_EXPORT void setShiftBinding(TResourceType res, unsigned int base); + GLSLANG_EXPORT void setShiftSamplerBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftTextureBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftImageBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftUboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftUavBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftCbufferBinding(unsigned int base); // synonym for setShiftUboBinding + GLSLANG_EXPORT void setShiftSsboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set); + GLSLANG_EXPORT void setResourceSetBinding(const std::vector& base); + GLSLANG_EXPORT void setAutoMapBindings(bool map); + GLSLANG_EXPORT void setAutoMapLocations(bool map); + GLSLANG_EXPORT void addUniformLocationOverride(const char* name, int loc); + GLSLANG_EXPORT void setUniformLocationBase(int base); + GLSLANG_EXPORT void setInvertY(bool invert); + GLSLANG_EXPORT void setDxPositionW(bool dxPosW); + GLSLANG_EXPORT void setEnhancedMsgs(); +#ifdef ENABLE_HLSL + GLSLANG_EXPORT void setHlslIoMapping(bool hlslIoMap); + GLSLANG_EXPORT void setFlattenUniformArrays(bool flatten); +#endif + GLSLANG_EXPORT void setNoStorageFormat(bool useUnknownFormat); + GLSLANG_EXPORT void setNanMinMaxClamp(bool nanMinMaxClamp); + GLSLANG_EXPORT void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); + GLSLANG_EXPORT void addBlockStorageOverride(const char* nameStr, glslang::TBlockStorageClass backing); + + GLSLANG_EXPORT void setGlobalUniformBlockName(const char* name); + GLSLANG_EXPORT void setAtomicCounterBlockName(const char* name); + GLSLANG_EXPORT void setGlobalUniformSet(unsigned int set); + GLSLANG_EXPORT void setGlobalUniformBinding(unsigned int binding); + GLSLANG_EXPORT void setAtomicCounterBlockSet(unsigned int set); + GLSLANG_EXPORT void setAtomicCounterBlockBinding(unsigned int binding); + + GLSLANG_EXPORT void addSourceText(const char* text, size_t len); + GLSLANG_EXPORT void setSourceFile(const char* file); + + // For setting up the environment (cleared to nothingness in the constructor). + // These must be called so that parsing is done for the right source language and + // target environment, either indirectly through TranslateEnvironment() based on + // EShMessages et. al., or directly by the user. + // + // setEnvInput: The input source language and stage. If generating code for a + // specific client, the input client semantics to use and the + // version of that client's input semantics to use, otherwise + // use EShClientNone and version of 0, e.g. for validation mode. + // Note 'version' does not describe the target environment, + // just the version of the source dialect to compile under. + // For example, to choose the Vulkan dialect of GLSL defined by + // version 100 of the KHR_vulkan_glsl extension: lang = EShSourceGlsl, + // dialect = EShClientVulkan, and version = 100. + // + // See the definitions of TEnvironment, EShSource, EShLanguage, + // and EShClient for choices and more detail. + // + // setEnvClient: The client that will be hosting the execution, and its version. + // Note 'version' is not the version of the languages involved, but + // the version of the client environment. + // Use EShClientNone and version of 0 if there is no client, e.g. + // for validation mode. + // + // See EShTargetClientVersion for choices. + // + // setEnvTarget: The language to translate to when generating code, and that + // language's version. + // Use EShTargetNone and version of 0 if there is no client, e.g. + // for validation mode. + // + void setEnvInput(EShSource lang, EShLanguage envStage, EShClient client, int version) + { + environment.input.languageFamily = lang; + environment.input.stage = envStage; + environment.input.dialect = client; + environment.input.dialectVersion = version; + } + void setEnvClient(EShClient client, EShTargetClientVersion version) + { + environment.client.client = client; + environment.client.version = version; + } + void setEnvTarget(EShTargetLanguage lang, EShTargetLanguageVersion version) + { + environment.target.language = lang; + environment.target.version = version; + } + + void getStrings(const char* const* &s, int& n) { s = strings; n = numStrings; } + +#ifdef ENABLE_HLSL + void setEnvTargetHlslFunctionality1() { environment.target.hlslFunctionality1 = true; } + bool getEnvTargetHlslFunctionality1() const { return environment.target.hlslFunctionality1; } +#else + bool getEnvTargetHlslFunctionality1() const { return false; } +#endif + + void setEnvInputVulkanRulesRelaxed() { environment.input.vulkanRulesRelaxed = true; } + bool getEnvInputVulkanRulesRelaxed() const { return environment.input.vulkanRulesRelaxed; } + + void setCompileOnly() { compileOnly = true; } + bool getCompileOnly() const { return compileOnly; } + + // Interface to #include handlers. + // + // To support #include, a client of Glslang does the following: + // 1. Call setStringsWithNames to set the source strings and associated + // names. For example, the names could be the names of the files + // containing the shader sources. + // 2. Call parse with an Includer. + // + // When the Glslang parser encounters an #include directive, it calls + // the Includer's include method with the requested include name + // together with the current string name. The returned IncludeResult + // contains the fully resolved name of the included source, together + // with the source text that should replace the #include directive + // in the source stream. After parsing that source, Glslang will + // release the IncludeResult object. + class Includer { + public: + // An IncludeResult contains the resolved name and content of a source + // inclusion. + struct IncludeResult { + IncludeResult(const std::string& headerName, const char* const headerData, const size_t headerLength, void* userData) : + headerName(headerName), headerData(headerData), headerLength(headerLength), userData(userData) { } + // For a successful inclusion, the fully resolved name of the requested + // include. For example, in a file system-based includer, full resolution + // should convert a relative path name into an absolute path name. + // For a failed inclusion, this is an empty string. + const std::string headerName; + // The content and byte length of the requested inclusion. The + // Includer producing this IncludeResult retains ownership of the + // storage. + // For a failed inclusion, the header + // field points to a string containing error details. + const char* const headerData; + const size_t headerLength; + // Include resolver's context. + void* userData; + protected: + IncludeResult& operator=(const IncludeResult&); + IncludeResult(); + }; + + // For both include methods below: + // + // Resolves an inclusion request by name, current source name, + // and include depth. + // On success, returns an IncludeResult containing the resolved name + // and content of the include. + // On failure, returns a nullptr, or an IncludeResult + // with an empty string for the headerName and error details in the + // header field. + // The Includer retains ownership of the contents + // of the returned IncludeResult value, and those contents must + // remain valid until the releaseInclude method is called on that + // IncludeResult object. + // + // Note "local" vs. "system" is not an "either/or": "local" is an + // extra thing to do over "system". Both might get called, as per + // the C++ specification. + + // For the "system" or <>-style includes; search the "system" paths. + virtual IncludeResult* includeSystem(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // For the "local"-only aspect of a "" include. Should not search in the + // "system" paths, because on returning a failure, the parser will + // call includeSystem() to look in the "system" locations. + virtual IncludeResult* includeLocal(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // Signals that the parser will no longer use the contents of the + // specified IncludeResult. + virtual void releaseInclude(IncludeResult*) = 0; + virtual ~Includer() {} + }; + + // Fail all Includer searches + class ForbidIncluder : public Includer { + public: + virtual void releaseInclude(IncludeResult*) override { } + }; + + GLSLANG_EXPORT bool parse( + const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, bool forwardCompatible, + EShMessages, Includer&); + + bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages) + { + TShader::ForbidIncluder includer; + return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer); + } + + // Equivalent to parse() without a default profile and without forcing defaults. + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages); + } + + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages, + Includer& includer) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages, includer); + } + + // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string + // is not an officially supported or fully working path. + GLSLANG_EXPORT bool preprocess( + const TBuiltInResource* builtInResources, int defaultVersion, + EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, std::string* outputString, + Includer& includer); + + GLSLANG_EXPORT const char* getInfoLog(); + GLSLANG_EXPORT const char* getInfoDebugLog(); + EShLanguage getStage() const { return stage; } + TIntermediate* getIntermediate() const { return intermediate; } + +protected: + TPoolAllocator* pool; + EShLanguage stage; + TCompiler* compiler; + TIntermediate* intermediate; + TInfoSink* infoSink; + // strings and lengths follow the standard for glShaderSource: + // strings is an array of numStrings pointers to string data. + // lengths can be null, but if not it is an array of numStrings + // integers containing the length of the associated strings. + // if lengths is null or lengths[n] < 0 the associated strings[n] is + // assumed to be null-terminated. + // stringNames is the optional names for all the strings. If stringNames + // is null, then none of the strings has name. If a certain element in + // stringNames is null, then the corresponding string does not have name. + const char* const* strings; // explicit code to compile, see previous comment + const int* lengths; + const char* const* stringNames; + int numStrings; // size of the above arrays + const char* preamble; // string of implicit code to compile before the explicitly provided code + + // a function in the source string can be renamed FROM this TO the name given in setEntryPoint. + std::string sourceEntryPointName; + + // overrides #version in shader source or default version if #version isn't present + int overrideVersion; + + TEnvironment environment; + + // Indicates this shader is meant to be used without linking + bool compileOnly = false; + + friend class TProgram; + +private: + TShader& operator=(TShader&); +}; + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +// Data needed for just a single object at the granularity exchanged by the reflection API +class TObjectReflection { +public: + GLSLANG_EXPORT TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex); + + const TType* getType() const { return type; } + GLSLANG_EXPORT int getBinding() const; + GLSLANG_EXPORT void dump() const; + static TObjectReflection badReflection() { return TObjectReflection(); } + + GLSLANG_EXPORT unsigned int layoutLocation() const; + + std::string name; + int offset; + int glDefineType; + int size; // data size in bytes for a block, array size for a (non-block) object that's an array + int index; + int counterIndex; + int numMembers; + int arrayStride; // stride of an array variable + int topLevelArraySize; // size of the top-level variable in a storage buffer member + int topLevelArrayStride; // stride of the top-level variable in a storage buffer member + EShLanguageMask stages; + +protected: + TObjectReflection() + : offset(-1), glDefineType(-1), size(-1), index(-1), counterIndex(-1), numMembers(-1), arrayStride(0), + topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr) + { + } + + const TType* type; +}; + +class TReflection; +class TIoMapper; +struct TVarEntryInfo; + +// Allows to customize the binding layout after linking. +// All used uniform variables will invoke at least validateBinding. +// If validateBinding returned true then the other resolveBinding, +// resolveSet, and resolveLocation are invoked to resolve the binding +// and descriptor set index respectively. +// +// Invocations happen in a particular order: +// 1) all shader inputs +// 2) all shader outputs +// 3) all uniforms with binding and set already defined +// 4) all uniforms with binding but no set defined +// 5) all uniforms with set but no binding defined +// 6) all uniforms with no binding and no set defined +// +// mapIO will use this resolver in two phases. The first +// phase is a notification phase, calling the corresponging +// notifiy callbacks, this phase ends with a call to endNotifications. +// Phase two starts directly after the call to endNotifications +// and calls all other callbacks to validate and to get the +// bindings, sets, locations, component and color indices. +// +// NOTE: that still limit checks are applied to bindings and sets +// and may result in an error. +class TIoMapResolver +{ +public: + virtual ~TIoMapResolver() {} + + // Should return true if the resulting/current binding would be okay. + // Basic idea is to do aliasing binding checks with this. + virtual bool validateBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current binding should be overridden. + // Return -1 if the current binding (including no binding) should be kept. + virtual int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current set should be overridden. + // Return -1 if the current set (including no set) should be kept. + virtual int resolveSet(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveUniformLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return true if the resulting/current setup would be okay. + // Basic idea is to do aliasing checks and reject invalid semantic names. + virtual bool validateInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current component index should be overridden. + // Return -1 if the current component index (including no index) should be kept. + virtual int resolveInOutComponent(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current color index should be overridden. + // Return -1 if the current color index (including no index) should be kept. + virtual int resolveInOutIndex(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a uniform variable + virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a in or out variable + virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Called by mapIO when it starts its notify pass for the given stage + virtual void beginNotifications(EShLanguage stage) = 0; + // Called by mapIO when it has finished the notify pass + virtual void endNotifications(EShLanguage stage) = 0; + // Called by mipIO when it starts its resolve pass for the given stage + virtual void beginResolve(EShLanguage stage) = 0; + // Called by mapIO when it has finished the resolve pass + virtual void endResolve(EShLanguage stage) = 0; + // Called by mapIO when it starts its symbol collect for teh given stage + virtual void beginCollect(EShLanguage stage) = 0; + // Called by mapIO when it has finished the symbol collect + virtual void endCollect(EShLanguage stage) = 0; + // Called by TSlotCollector to resolve storage locations or bindings + virtual void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by TSlotCollector to resolve resource locations or bindings + virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline + virtual void addStage(EShLanguage stage, TIntermediate& stageIntermediate) = 0; +}; + +// I/O mapper +class TIoMapper { +public: + TIoMapper() {} + virtual ~TIoMapper() {} + // grow the reflection stage by stage + bool virtual addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*); + bool virtual doMap(TIoMapResolver*, TInfoSink&) { return true; } + bool virtual setAutoPushConstantBlock(const char*, unsigned int, TLayoutPacking) { return false; } +}; + +// Get the default GLSL IO mapper +GLSLANG_EXPORT TIoMapper* GetGlslIoMapper(); + +// Make one TProgram per set of shaders that will get linked together. Add all +// the shaders that are to be linked together. After calling shader.parse() +// for all shaders, call link(). +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TProgram { +public: + GLSLANG_EXPORT TProgram(); + GLSLANG_EXPORT virtual ~TProgram(); + void addShader(TShader* shader) { stages[shader->stage].push_back(shader); } + std::list& getShaders(EShLanguage stage) { return stages[stage]; } + // Link Validation interface + GLSLANG_EXPORT bool link(EShMessages); + GLSLANG_EXPORT const char* getInfoLog(); + GLSLANG_EXPORT const char* getInfoDebugLog(); + + TIntermediate* getIntermediate(EShLanguage stage) const { return intermediate[stage]; } + + // Reflection Interface + + // call first, to do liveness analysis, index mapping, etc.; returns false on failure + GLSLANG_EXPORT bool buildReflection(int opts = EShReflectionDefault); + GLSLANG_EXPORT unsigned getLocalSize(int dim) const; // return dim'th local size + GLSLANG_EXPORT int getReflectionIndex(const char *name) const; + GLSLANG_EXPORT int getReflectionPipeIOIndex(const char* name, const bool inOrOut) const; + GLSLANG_EXPORT int getNumUniformVariables() const; + GLSLANG_EXPORT const TObjectReflection& getUniform(int index) const; + GLSLANG_EXPORT int getNumUniformBlocks() const; + GLSLANG_EXPORT const TObjectReflection& getUniformBlock(int index) const; + GLSLANG_EXPORT int getNumPipeInputs() const; + GLSLANG_EXPORT const TObjectReflection& getPipeInput(int index) const; + GLSLANG_EXPORT int getNumPipeOutputs() const; + GLSLANG_EXPORT const TObjectReflection& getPipeOutput(int index) const; + GLSLANG_EXPORT int getNumBufferVariables() const; + GLSLANG_EXPORT const TObjectReflection& getBufferVariable(int index) const; + GLSLANG_EXPORT int getNumBufferBlocks() const; + GLSLANG_EXPORT const TObjectReflection& getBufferBlock(int index) const; + GLSLANG_EXPORT int getNumAtomicCounters() const; + GLSLANG_EXPORT const TObjectReflection& getAtomicCounter(int index) const; + + // Legacy Reflection Interface - expressed in terms of above interface + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS) + int getNumLiveUniformVariables() const { return getNumUniformVariables(); } + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS) + int getNumLiveUniformBlocks() const { return getNumUniformBlocks(); } + + // can be used for glGetProgramiv(GL_ACTIVE_ATTRIBUTES) + int getNumLiveAttributes() const { return getNumPipeInputs(); } + + // can be used for glGetUniformIndices() + int getUniformIndex(const char *name) const { return getReflectionIndex(name); } + + int getPipeIOIndex(const char *name, const bool inOrOut) const + { return getReflectionPipeIOIndex(name, inOrOut); } + + // can be used for "name" part of glGetActiveUniform() + const char *getUniformName(int index) const { return getUniform(index).name.c_str(); } + + // returns the binding number + int getUniformBinding(int index) const { return getUniform(index).getBinding(); } + + // returns Shaders Stages where a Uniform is present + EShLanguageMask getUniformStages(int index) const { return getUniform(index).stages; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX) + int getUniformBlockIndex(int index) const { return getUniform(index).index; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE) + int getUniformType(int index) const { return getUniform(index).glDefineType; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET) + int getUniformBufferOffset(int index) const { return getUniform(index).offset; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE) + int getUniformArraySize(int index) const { return getUniform(index).size; } + + // returns a TType* + const TType *getUniformTType(int index) const { return getUniform(index).getType(); } + + // can be used for glGetActiveUniformBlockName() + const char *getUniformBlockName(int index) const { return getUniformBlock(index).name.c_str(); } + + // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE) + int getUniformBlockSize(int index) const { return getUniformBlock(index).size; } + + // returns the block binding number + int getUniformBlockBinding(int index) const { return getUniformBlock(index).getBinding(); } + + // returns block index of associated counter. + int getUniformBlockCounterIndex(int index) const { return getUniformBlock(index).counterIndex; } + + // returns a TType* + const TType *getUniformBlockTType(int index) const { return getUniformBlock(index).getType(); } + + // can be used for glGetActiveAttrib() + const char *getAttributeName(int index) const { return getPipeInput(index).name.c_str(); } + + // can be used for glGetActiveAttrib() + int getAttributeType(int index) const { return getPipeInput(index).glDefineType; } + + // returns a TType* + const TType *getAttributeTType(int index) const { return getPipeInput(index).getType(); } + + GLSLANG_EXPORT void dumpReflection(); + + // Get the IO resolver to use for mapIO + GLSLANG_EXPORT TIoMapResolver* getGlslIoResolver(EShLanguage stage); + + // I/O mapping: apply base offsets and map live unbound variables + // If resolver is not provided it uses the previous approach + // and respects auto assignment and offsets. + GLSLANG_EXPORT bool mapIO(TIoMapResolver* pResolver = nullptr, TIoMapper* pIoMapper = nullptr); + +protected: + GLSLANG_EXPORT bool linkStage(EShLanguage, EShMessages); + GLSLANG_EXPORT bool crossStageCheck(EShMessages); + + TPoolAllocator* pool; + std::list stages[EShLangCount]; + TIntermediate* intermediate[EShLangCount]; + bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage + TInfoSink* infoSink; + TReflection* reflection; + bool linked; + +private: + TProgram(TProgram&); + TProgram& operator=(TProgram&); +}; + +} // end namespace glslang + +#endif // _COMPILER_INTERFACE_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/Public/resource_limits_c.h b/engine/engine/3rdlibs/glslang/glslang/Public/resource_limits_c.h new file mode 100644 index 00000000..3cf7442f --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/Public/resource_limits_c.h @@ -0,0 +1,58 @@ +/** +BSD 2-Clause License + +Copyright (c) 2020, Travis Fort +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ +#define _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ + +#include "../Include/glslang_c_interface.h" +#include "../Include/visibility.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns a struct that can be use to create custom resource values. +GLSLANG_EXPORT glslang_resource_t* glslang_resource(void); + +// These are the default resources for TBuiltInResources, used for both +// - parsing this string for the case where the user didn't supply one, +// - dumping out a template for user construction of a config file. +GLSLANG_EXPORT const glslang_resource_t* glslang_default_resource(void); + +// Returns the DefaultTBuiltInResource as a human-readable string. +// NOTE: User is responsible for freeing this string. +GLSLANG_EXPORT const char* glslang_default_resource_string(); + +// Decodes the resource limits from |config| to |resources|. +GLSLANG_EXPORT void glslang_decode_resource_limits(glslang_resource_t* resources, char* config); + +#ifdef __cplusplus +} +#endif + +#endif // _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ diff --git a/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/ResourceLimits.cpp b/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/ResourceLimits.cpp new file mode 100644 index 00000000..0e9d1b54 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/ResourceLimits.cpp @@ -0,0 +1,542 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "glslang/Public/ResourceLimits.h" + +TBuiltInResource Resources; + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxMeshOutputVerticesEXT = */ 256, + /* .maxMeshOutputPrimitivesEXT = */ 256, + /* .maxMeshWorkGroupSizeX_EXT = */ 128, + /* .maxMeshWorkGroupSizeY_EXT = */ 128, + /* .maxMeshWorkGroupSizeZ_EXT = */ 128, + /* .maxTaskWorkGroupSizeX_EXT = */ 128, + /* .maxTaskWorkGroupSizeY_EXT = */ 128, + /* .maxTaskWorkGroupSizeZ_EXT = */ 128, + /* .maxMeshViewCountEXT = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +std::string GetDefaultTBuiltInResourceString() +{ + std::ostringstream ostream; + + ostream << "MaxLights " << DefaultTBuiltInResource.maxLights << "\n" + << "MaxClipPlanes " << DefaultTBuiltInResource.maxClipPlanes << "\n" + << "MaxTextureUnits " << DefaultTBuiltInResource.maxTextureUnits << "\n" + << "MaxTextureCoords " << DefaultTBuiltInResource.maxTextureCoords << "\n" + << "MaxVertexAttribs " << DefaultTBuiltInResource.maxVertexAttribs << "\n" + << "MaxVertexUniformComponents " << DefaultTBuiltInResource.maxVertexUniformComponents << "\n" + << "MaxVaryingFloats " << DefaultTBuiltInResource.maxVaryingFloats << "\n" + << "MaxVertexTextureImageUnits " << DefaultTBuiltInResource.maxVertexTextureImageUnits << "\n" + << "MaxCombinedTextureImageUnits " << DefaultTBuiltInResource.maxCombinedTextureImageUnits << "\n" + << "MaxTextureImageUnits " << DefaultTBuiltInResource.maxTextureImageUnits << "\n" + << "MaxFragmentUniformComponents " << DefaultTBuiltInResource.maxFragmentUniformComponents << "\n" + << "MaxDrawBuffers " << DefaultTBuiltInResource.maxDrawBuffers << "\n" + << "MaxVertexUniformVectors " << DefaultTBuiltInResource.maxVertexUniformVectors << "\n" + << "MaxVaryingVectors " << DefaultTBuiltInResource.maxVaryingVectors << "\n" + << "MaxFragmentUniformVectors " << DefaultTBuiltInResource.maxFragmentUniformVectors << "\n" + << "MaxVertexOutputVectors " << DefaultTBuiltInResource.maxVertexOutputVectors << "\n" + << "MaxFragmentInputVectors " << DefaultTBuiltInResource.maxFragmentInputVectors << "\n" + << "MinProgramTexelOffset " << DefaultTBuiltInResource.minProgramTexelOffset << "\n" + << "MaxProgramTexelOffset " << DefaultTBuiltInResource.maxProgramTexelOffset << "\n" + << "MaxClipDistances " << DefaultTBuiltInResource.maxClipDistances << "\n" + << "MaxComputeWorkGroupCountX " << DefaultTBuiltInResource.maxComputeWorkGroupCountX << "\n" + << "MaxComputeWorkGroupCountY " << DefaultTBuiltInResource.maxComputeWorkGroupCountY << "\n" + << "MaxComputeWorkGroupCountZ " << DefaultTBuiltInResource.maxComputeWorkGroupCountZ << "\n" + << "MaxComputeWorkGroupSizeX " << DefaultTBuiltInResource.maxComputeWorkGroupSizeX << "\n" + << "MaxComputeWorkGroupSizeY " << DefaultTBuiltInResource.maxComputeWorkGroupSizeY << "\n" + << "MaxComputeWorkGroupSizeZ " << DefaultTBuiltInResource.maxComputeWorkGroupSizeZ << "\n" + << "MaxComputeUniformComponents " << DefaultTBuiltInResource.maxComputeUniformComponents << "\n" + << "MaxComputeTextureImageUnits " << DefaultTBuiltInResource.maxComputeTextureImageUnits << "\n" + << "MaxComputeImageUniforms " << DefaultTBuiltInResource.maxComputeImageUniforms << "\n" + << "MaxComputeAtomicCounters " << DefaultTBuiltInResource.maxComputeAtomicCounters << "\n" + << "MaxComputeAtomicCounterBuffers " << DefaultTBuiltInResource.maxComputeAtomicCounterBuffers << "\n" + << "MaxVaryingComponents " << DefaultTBuiltInResource.maxVaryingComponents << "\n" + << "MaxVertexOutputComponents " << DefaultTBuiltInResource.maxVertexOutputComponents << "\n" + << "MaxGeometryInputComponents " << DefaultTBuiltInResource.maxGeometryInputComponents << "\n" + << "MaxGeometryOutputComponents " << DefaultTBuiltInResource.maxGeometryOutputComponents << "\n" + << "MaxFragmentInputComponents " << DefaultTBuiltInResource.maxFragmentInputComponents << "\n" + << "MaxImageUnits " << DefaultTBuiltInResource.maxImageUnits << "\n" + << "MaxCombinedImageUnitsAndFragmentOutputs " << DefaultTBuiltInResource.maxCombinedImageUnitsAndFragmentOutputs << "\n" + << "MaxCombinedShaderOutputResources " << DefaultTBuiltInResource.maxCombinedShaderOutputResources << "\n" + << "MaxImageSamples " << DefaultTBuiltInResource.maxImageSamples << "\n" + << "MaxVertexImageUniforms " << DefaultTBuiltInResource.maxVertexImageUniforms << "\n" + << "MaxTessControlImageUniforms " << DefaultTBuiltInResource.maxTessControlImageUniforms << "\n" + << "MaxTessEvaluationImageUniforms " << DefaultTBuiltInResource.maxTessEvaluationImageUniforms << "\n" + << "MaxGeometryImageUniforms " << DefaultTBuiltInResource.maxGeometryImageUniforms << "\n" + << "MaxFragmentImageUniforms " << DefaultTBuiltInResource.maxFragmentImageUniforms << "\n" + << "MaxCombinedImageUniforms " << DefaultTBuiltInResource.maxCombinedImageUniforms << "\n" + << "MaxGeometryTextureImageUnits " << DefaultTBuiltInResource.maxGeometryTextureImageUnits << "\n" + << "MaxGeometryOutputVertices " << DefaultTBuiltInResource.maxGeometryOutputVertices << "\n" + << "MaxGeometryTotalOutputComponents " << DefaultTBuiltInResource.maxGeometryTotalOutputComponents << "\n" + << "MaxGeometryUniformComponents " << DefaultTBuiltInResource.maxGeometryUniformComponents << "\n" + << "MaxGeometryVaryingComponents " << DefaultTBuiltInResource.maxGeometryVaryingComponents << "\n" + << "MaxTessControlInputComponents " << DefaultTBuiltInResource.maxTessControlInputComponents << "\n" + << "MaxTessControlOutputComponents " << DefaultTBuiltInResource.maxTessControlOutputComponents << "\n" + << "MaxTessControlTextureImageUnits " << DefaultTBuiltInResource.maxTessControlTextureImageUnits << "\n" + << "MaxTessControlUniformComponents " << DefaultTBuiltInResource.maxTessControlUniformComponents << "\n" + << "MaxTessControlTotalOutputComponents " << DefaultTBuiltInResource.maxTessControlTotalOutputComponents << "\n" + << "MaxTessEvaluationInputComponents " << DefaultTBuiltInResource.maxTessEvaluationInputComponents << "\n" + << "MaxTessEvaluationOutputComponents " << DefaultTBuiltInResource.maxTessEvaluationOutputComponents << "\n" + << "MaxTessEvaluationTextureImageUnits " << DefaultTBuiltInResource.maxTessEvaluationTextureImageUnits << "\n" + << "MaxTessEvaluationUniformComponents " << DefaultTBuiltInResource.maxTessEvaluationUniformComponents << "\n" + << "MaxTessPatchComponents " << DefaultTBuiltInResource.maxTessPatchComponents << "\n" + << "MaxPatchVertices " << DefaultTBuiltInResource.maxPatchVertices << "\n" + << "MaxTessGenLevel " << DefaultTBuiltInResource.maxTessGenLevel << "\n" + << "MaxViewports " << DefaultTBuiltInResource.maxViewports << "\n" + << "MaxVertexAtomicCounters " << DefaultTBuiltInResource.maxVertexAtomicCounters << "\n" + << "MaxTessControlAtomicCounters " << DefaultTBuiltInResource.maxTessControlAtomicCounters << "\n" + << "MaxTessEvaluationAtomicCounters " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounters << "\n" + << "MaxGeometryAtomicCounters " << DefaultTBuiltInResource.maxGeometryAtomicCounters << "\n" + << "MaxFragmentAtomicCounters " << DefaultTBuiltInResource.maxFragmentAtomicCounters << "\n" + << "MaxCombinedAtomicCounters " << DefaultTBuiltInResource.maxCombinedAtomicCounters << "\n" + << "MaxAtomicCounterBindings " << DefaultTBuiltInResource.maxAtomicCounterBindings << "\n" + << "MaxVertexAtomicCounterBuffers " << DefaultTBuiltInResource.maxVertexAtomicCounterBuffers << "\n" + << "MaxTessControlAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessControlAtomicCounterBuffers << "\n" + << "MaxTessEvaluationAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounterBuffers << "\n" + << "MaxGeometryAtomicCounterBuffers " << DefaultTBuiltInResource.maxGeometryAtomicCounterBuffers << "\n" + << "MaxFragmentAtomicCounterBuffers " << DefaultTBuiltInResource.maxFragmentAtomicCounterBuffers << "\n" + << "MaxCombinedAtomicCounterBuffers " << DefaultTBuiltInResource.maxCombinedAtomicCounterBuffers << "\n" + << "MaxAtomicCounterBufferSize " << DefaultTBuiltInResource.maxAtomicCounterBufferSize << "\n" + << "MaxTransformFeedbackBuffers " << DefaultTBuiltInResource.maxTransformFeedbackBuffers << "\n" + << "MaxTransformFeedbackInterleavedComponents " << DefaultTBuiltInResource.maxTransformFeedbackInterleavedComponents << "\n" + << "MaxCullDistances " << DefaultTBuiltInResource.maxCullDistances << "\n" + << "MaxCombinedClipAndCullDistances " << DefaultTBuiltInResource.maxCombinedClipAndCullDistances << "\n" + << "MaxSamples " << DefaultTBuiltInResource.maxSamples << "\n" + << "MaxMeshOutputVerticesNV " << DefaultTBuiltInResource.maxMeshOutputVerticesNV << "\n" + << "MaxMeshOutputPrimitivesNV " << DefaultTBuiltInResource.maxMeshOutputPrimitivesNV << "\n" + << "MaxMeshWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeX_NV << "\n" + << "MaxMeshWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeY_NV << "\n" + << "MaxMeshWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeZ_NV << "\n" + << "MaxTaskWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeX_NV << "\n" + << "MaxTaskWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeY_NV << "\n" + << "MaxTaskWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeZ_NV << "\n" + << "MaxMeshViewCountNV " << DefaultTBuiltInResource.maxMeshViewCountNV << "\n" + << "MaxMeshOutputVerticesEXT " << DefaultTBuiltInResource.maxMeshOutputVerticesEXT << "\n" + << "MaxMeshOutputPrimitivesEXT " << DefaultTBuiltInResource.maxMeshOutputPrimitivesEXT << "\n" + << "MaxMeshWorkGroupSizeX_EXT " << DefaultTBuiltInResource.maxMeshWorkGroupSizeX_EXT << "\n" + << "MaxMeshWorkGroupSizeY_EXT " << DefaultTBuiltInResource.maxMeshWorkGroupSizeY_EXT << "\n" + << "MaxMeshWorkGroupSizeZ_EXT " << DefaultTBuiltInResource.maxMeshWorkGroupSizeZ_EXT << "\n" + << "MaxTaskWorkGroupSizeX_EXT " << DefaultTBuiltInResource.maxTaskWorkGroupSizeX_EXT << "\n" + << "MaxTaskWorkGroupSizeY_EXT " << DefaultTBuiltInResource.maxTaskWorkGroupSizeY_EXT << "\n" + << "MaxTaskWorkGroupSizeZ_EXT " << DefaultTBuiltInResource.maxTaskWorkGroupSizeZ_EXT << "\n" + << "MaxMeshViewCountEXT " << DefaultTBuiltInResource.maxMeshViewCountEXT << "\n" + << "MaxDualSourceDrawBuffersEXT " << DefaultTBuiltInResource.maxDualSourceDrawBuffersEXT << "\n" + << "nonInductiveForLoops " << DefaultTBuiltInResource.limits.nonInductiveForLoops << "\n" + << "whileLoops " << DefaultTBuiltInResource.limits.whileLoops << "\n" + << "doWhileLoops " << DefaultTBuiltInResource.limits.doWhileLoops << "\n" + << "generalUniformIndexing " << DefaultTBuiltInResource.limits.generalUniformIndexing << "\n" + << "generalAttributeMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalAttributeMatrixVectorIndexing << "\n" + << "generalVaryingIndexing " << DefaultTBuiltInResource.limits.generalVaryingIndexing << "\n" + << "generalSamplerIndexing " << DefaultTBuiltInResource.limits.generalSamplerIndexing << "\n" + << "generalVariableIndexing " << DefaultTBuiltInResource.limits.generalVariableIndexing << "\n" + << "generalConstantMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalConstantMatrixVectorIndexing << "\n" + ; + + return ostream.str(); +} + +void DecodeResourceLimits(TBuiltInResource* resources, char* config) +{ + static const char* delims = " \t\n\r"; + + size_t pos = 0; + std::string configStr(config); + + while ((pos = configStr.find_first_not_of(delims, pos)) != std::string::npos) { + const size_t token_s = pos; + const size_t token_e = configStr.find_first_of(delims, token_s); + const size_t value_s = configStr.find_first_not_of(delims, token_e); + const size_t value_e = configStr.find_first_of(delims, value_s); + pos = value_e; + + // Faster to use compare(), but prefering readability. + const std::string tokenStr = configStr.substr(token_s, token_e-token_s); + const std::string valueStr = configStr.substr(value_s, value_e-value_s); + + if (value_s == std::string::npos || ! (valueStr[0] == '-' || isdigit(valueStr[0]))) { + printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", + valueStr.c_str()); + return; + } + + const int value = std::atoi(valueStr.c_str()); + + if (tokenStr == "MaxLights") + resources->maxLights = value; + else if (tokenStr == "MaxClipPlanes") + resources->maxClipPlanes = value; + else if (tokenStr == "MaxTextureUnits") + resources->maxTextureUnits = value; + else if (tokenStr == "MaxTextureCoords") + resources->maxTextureCoords = value; + else if (tokenStr == "MaxVertexAttribs") + resources->maxVertexAttribs = value; + else if (tokenStr == "MaxVertexUniformComponents") + resources->maxVertexUniformComponents = value; + else if (tokenStr == "MaxVaryingFloats") + resources->maxVaryingFloats = value; + else if (tokenStr == "MaxVertexTextureImageUnits") + resources->maxVertexTextureImageUnits = value; + else if (tokenStr == "MaxCombinedTextureImageUnits") + resources->maxCombinedTextureImageUnits = value; + else if (tokenStr == "MaxTextureImageUnits") + resources->maxTextureImageUnits = value; + else if (tokenStr == "MaxFragmentUniformComponents") + resources->maxFragmentUniformComponents = value; + else if (tokenStr == "MaxDrawBuffers") + resources->maxDrawBuffers = value; + else if (tokenStr == "MaxVertexUniformVectors") + resources->maxVertexUniformVectors = value; + else if (tokenStr == "MaxVaryingVectors") + resources->maxVaryingVectors = value; + else if (tokenStr == "MaxFragmentUniformVectors") + resources->maxFragmentUniformVectors = value; + else if (tokenStr == "MaxVertexOutputVectors") + resources->maxVertexOutputVectors = value; + else if (tokenStr == "MaxFragmentInputVectors") + resources->maxFragmentInputVectors = value; + else if (tokenStr == "MinProgramTexelOffset") + resources->minProgramTexelOffset = value; + else if (tokenStr == "MaxProgramTexelOffset") + resources->maxProgramTexelOffset = value; + else if (tokenStr == "MaxClipDistances") + resources->maxClipDistances = value; + else if (tokenStr == "MaxComputeWorkGroupCountX") + resources->maxComputeWorkGroupCountX = value; + else if (tokenStr == "MaxComputeWorkGroupCountY") + resources->maxComputeWorkGroupCountY = value; + else if (tokenStr == "MaxComputeWorkGroupCountZ") + resources->maxComputeWorkGroupCountZ = value; + else if (tokenStr == "MaxComputeWorkGroupSizeX") + resources->maxComputeWorkGroupSizeX = value; + else if (tokenStr == "MaxComputeWorkGroupSizeY") + resources->maxComputeWorkGroupSizeY = value; + else if (tokenStr == "MaxComputeWorkGroupSizeZ") + resources->maxComputeWorkGroupSizeZ = value; + else if (tokenStr == "MaxComputeUniformComponents") + resources->maxComputeUniformComponents = value; + else if (tokenStr == "MaxComputeTextureImageUnits") + resources->maxComputeTextureImageUnits = value; + else if (tokenStr == "MaxComputeImageUniforms") + resources->maxComputeImageUniforms = value; + else if (tokenStr == "MaxComputeAtomicCounters") + resources->maxComputeAtomicCounters = value; + else if (tokenStr == "MaxComputeAtomicCounterBuffers") + resources->maxComputeAtomicCounterBuffers = value; + else if (tokenStr == "MaxVaryingComponents") + resources->maxVaryingComponents = value; + else if (tokenStr == "MaxVertexOutputComponents") + resources->maxVertexOutputComponents = value; + else if (tokenStr == "MaxGeometryInputComponents") + resources->maxGeometryInputComponents = value; + else if (tokenStr == "MaxGeometryOutputComponents") + resources->maxGeometryOutputComponents = value; + else if (tokenStr == "MaxFragmentInputComponents") + resources->maxFragmentInputComponents = value; + else if (tokenStr == "MaxImageUnits") + resources->maxImageUnits = value; + else if (tokenStr == "MaxCombinedImageUnitsAndFragmentOutputs") + resources->maxCombinedImageUnitsAndFragmentOutputs = value; + else if (tokenStr == "MaxCombinedShaderOutputResources") + resources->maxCombinedShaderOutputResources = value; + else if (tokenStr == "MaxImageSamples") + resources->maxImageSamples = value; + else if (tokenStr == "MaxVertexImageUniforms") + resources->maxVertexImageUniforms = value; + else if (tokenStr == "MaxTessControlImageUniforms") + resources->maxTessControlImageUniforms = value; + else if (tokenStr == "MaxTessEvaluationImageUniforms") + resources->maxTessEvaluationImageUniforms = value; + else if (tokenStr == "MaxGeometryImageUniforms") + resources->maxGeometryImageUniforms = value; + else if (tokenStr == "MaxFragmentImageUniforms") + resources->maxFragmentImageUniforms = value; + else if (tokenStr == "MaxCombinedImageUniforms") + resources->maxCombinedImageUniforms = value; + else if (tokenStr == "MaxGeometryTextureImageUnits") + resources->maxGeometryTextureImageUnits = value; + else if (tokenStr == "MaxGeometryOutputVertices") + resources->maxGeometryOutputVertices = value; + else if (tokenStr == "MaxGeometryTotalOutputComponents") + resources->maxGeometryTotalOutputComponents = value; + else if (tokenStr == "MaxGeometryUniformComponents") + resources->maxGeometryUniformComponents = value; + else if (tokenStr == "MaxGeometryVaryingComponents") + resources->maxGeometryVaryingComponents = value; + else if (tokenStr == "MaxTessControlInputComponents") + resources->maxTessControlInputComponents = value; + else if (tokenStr == "MaxTessControlOutputComponents") + resources->maxTessControlOutputComponents = value; + else if (tokenStr == "MaxTessControlTextureImageUnits") + resources->maxTessControlTextureImageUnits = value; + else if (tokenStr == "MaxTessControlUniformComponents") + resources->maxTessControlUniformComponents = value; + else if (tokenStr == "MaxTessControlTotalOutputComponents") + resources->maxTessControlTotalOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationInputComponents") + resources->maxTessEvaluationInputComponents = value; + else if (tokenStr == "MaxTessEvaluationOutputComponents") + resources->maxTessEvaluationOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationTextureImageUnits") + resources->maxTessEvaluationTextureImageUnits = value; + else if (tokenStr == "MaxTessEvaluationUniformComponents") + resources->maxTessEvaluationUniformComponents = value; + else if (tokenStr == "MaxTessPatchComponents") + resources->maxTessPatchComponents = value; + else if (tokenStr == "MaxPatchVertices") + resources->maxPatchVertices = value; + else if (tokenStr == "MaxTessGenLevel") + resources->maxTessGenLevel = value; + else if (tokenStr == "MaxViewports") + resources->maxViewports = value; + else if (tokenStr == "MaxVertexAtomicCounters") + resources->maxVertexAtomicCounters = value; + else if (tokenStr == "MaxTessControlAtomicCounters") + resources->maxTessControlAtomicCounters = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounters") + resources->maxTessEvaluationAtomicCounters = value; + else if (tokenStr == "MaxGeometryAtomicCounters") + resources->maxGeometryAtomicCounters = value; + else if (tokenStr == "MaxFragmentAtomicCounters") + resources->maxFragmentAtomicCounters = value; + else if (tokenStr == "MaxCombinedAtomicCounters") + resources->maxCombinedAtomicCounters = value; + else if (tokenStr == "MaxAtomicCounterBindings") + resources->maxAtomicCounterBindings = value; + else if (tokenStr == "MaxVertexAtomicCounterBuffers") + resources->maxVertexAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessControlAtomicCounterBuffers") + resources->maxTessControlAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounterBuffers") + resources->maxTessEvaluationAtomicCounterBuffers = value; + else if (tokenStr == "MaxGeometryAtomicCounterBuffers") + resources->maxGeometryAtomicCounterBuffers = value; + else if (tokenStr == "MaxFragmentAtomicCounterBuffers") + resources->maxFragmentAtomicCounterBuffers = value; + else if (tokenStr == "MaxCombinedAtomicCounterBuffers") + resources->maxCombinedAtomicCounterBuffers = value; + else if (tokenStr == "MaxAtomicCounterBufferSize") + resources->maxAtomicCounterBufferSize = value; + else if (tokenStr == "MaxTransformFeedbackBuffers") + resources->maxTransformFeedbackBuffers = value; + else if (tokenStr == "MaxTransformFeedbackInterleavedComponents") + resources->maxTransformFeedbackInterleavedComponents = value; + else if (tokenStr == "MaxCullDistances") + resources->maxCullDistances = value; + else if (tokenStr == "MaxCombinedClipAndCullDistances") + resources->maxCombinedClipAndCullDistances = value; + else if (tokenStr == "MaxSamples") + resources->maxSamples = value; + else if (tokenStr == "MaxMeshOutputVerticesNV") + resources->maxMeshOutputVerticesNV = value; + else if (tokenStr == "MaxMeshOutputPrimitivesNV") + resources->maxMeshOutputPrimitivesNV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeX_NV") + resources->maxMeshWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeY_NV") + resources->maxMeshWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeZ_NV") + resources->maxMeshWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeX_NV") + resources->maxTaskWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeY_NV") + resources->maxTaskWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeZ_NV") + resources->maxTaskWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxMeshViewCountNV") + resources->maxMeshViewCountNV = value; + else if (tokenStr == "MaxMeshOutputVerticesEXT") + resources->maxMeshOutputVerticesEXT = value; + else if (tokenStr == "MaxMeshOutputPrimitivesEXT") + resources->maxMeshOutputPrimitivesEXT = value; + else if (tokenStr == "MaxMeshWorkGroupSizeX_EXT") + resources->maxMeshWorkGroupSizeX_EXT = value; + else if (tokenStr == "MaxMeshWorkGroupSizeY_EXT") + resources->maxMeshWorkGroupSizeY_EXT = value; + else if (tokenStr == "MaxMeshWorkGroupSizeZ_EXT") + resources->maxMeshWorkGroupSizeZ_EXT = value; + else if (tokenStr == "MaxTaskWorkGroupSizeX_EXT") + resources->maxTaskWorkGroupSizeX_EXT = value; + else if (tokenStr == "MaxTaskWorkGroupSizeY_EXT") + resources->maxTaskWorkGroupSizeY_EXT = value; + else if (tokenStr == "MaxTaskWorkGroupSizeZ_EXT") + resources->maxTaskWorkGroupSizeZ_EXT = value; + else if (tokenStr == "MaxMeshViewCountEXT") + resources->maxMeshViewCountEXT = value; + else if (tokenStr == "MaxDualSourceDrawBuffersEXT") + resources->maxDualSourceDrawBuffersEXT = value; + else if (tokenStr == "nonInductiveForLoops") + resources->limits.nonInductiveForLoops = (value != 0); + else if (tokenStr == "whileLoops") + resources->limits.whileLoops = (value != 0); + else if (tokenStr == "doWhileLoops") + resources->limits.doWhileLoops = (value != 0); + else if (tokenStr == "generalUniformIndexing") + resources->limits.generalUniformIndexing = (value != 0); + else if (tokenStr == "generalAttributeMatrixVectorIndexing") + resources->limits.generalAttributeMatrixVectorIndexing = (value != 0); + else if (tokenStr == "generalVaryingIndexing") + resources->limits.generalVaryingIndexing = (value != 0); + else if (tokenStr == "generalSamplerIndexing") + resources->limits.generalSamplerIndexing = (value != 0); + else if (tokenStr == "generalVariableIndexing") + resources->limits.generalVariableIndexing = (value != 0); + else if (tokenStr == "generalConstantMatrixVectorIndexing") + resources->limits.generalConstantMatrixVectorIndexing = (value != 0); + else + printf("Warning: unrecognized limit (%s) in configuration file.\n", tokenStr.c_str()); + + } +} + +TBuiltInResource* GetResources() +{ + return &Resources; +} + +const TBuiltInResource* GetDefaultResources() +{ + return &DefaultTBuiltInResource; +} diff --git a/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/resource_limits_c.cpp b/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/resource_limits_c.cpp new file mode 100644 index 00000000..8909d9ef --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/ResourceLimits/resource_limits_c.cpp @@ -0,0 +1,56 @@ +/** +BSD 2-Clause License + +Copyright (c) 2020, Travis Fort +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "glslang/Public/resource_limits_c.h" +#include "glslang/Public/ResourceLimits.h" +#include +#include +#include + +glslang_resource_t* glslang_resource(void) +{ + return reinterpret_cast(GetResources()); +} + +const glslang_resource_t* glslang_default_resource(void) +{ + return reinterpret_cast(GetDefaultResources()); +} + +const char* glslang_default_resource_string() +{ + std::string cpp_str = GetDefaultTBuiltInResourceString(); + char* c_str = (char*)malloc(cpp_str.length() + 1); + strncpy(c_str, cpp_str.c_str(), cpp_str.length() + 1); + return c_str; +} + +void glslang_decode_resource_limits(glslang_resource_t* resources, char* config) +{ + DecodeResourceLimits(reinterpret_cast(resources), config); +} diff --git a/engine/engine/3rdlibs/glslang/glslang/stub.cpp b/engine/engine/3rdlibs/glslang/glslang/stub.cpp new file mode 100644 index 00000000..9c286fb9 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/stub.cpp @@ -0,0 +1,51 @@ +// +// Copyright (C) 2024 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifdef GLSLANG_IS_SHARED_LIBRARY +#ifdef _WIN32 +#ifdef GLSLANG_EXPORTING +#define STUB_EXPORT __declspec(dllexport) +#else +#define STUB_EXPORT __declspec(dllimport) +#endif +#endif +#endif // GLSLANG_IS_SHARED_LIBRARY + +#ifndef STUB_EXPORT +#define STUB_EXPORT +#endif + +// Force stub library to export function to preserve backcompat. +// https://github.com/KhronosGroup/glslang/issues/3882 +STUB_EXPORT int stub_library_function() { return 0; } diff --git a/engine/engine/3rdlibs/glslang/glslang/updateGrammar b/engine/engine/3rdlibs/glslang/glslang/updateGrammar new file mode 100644 index 00000000..a15dc24b --- /dev/null +++ b/engine/engine/3rdlibs/glslang/glslang/updateGrammar @@ -0,0 +1,36 @@ +#!/bin/sh + +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +bison --defines=MachineIndependent/glslang_tab.cpp.h -t MachineIndependent/glslang.y -o MachineIndependent/glslang_tab.cpp diff --git a/engine/engine/3rdlibs/glslang/parse_version.cmake b/engine/engine/3rdlibs/glslang/parse_version.cmake new file mode 100644 index 00000000..b9ba4137 --- /dev/null +++ b/engine/engine/3rdlibs/glslang/parse_version.cmake @@ -0,0 +1,41 @@ +# Copyright (C) 2020 Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# parse_version() reads and parses the version string from FILE, assigning the +# version string to ${PROJECT}_VERSION and the parsed version to +# ${PROJECT}_VERSION_MAJOR, ${PROJECT}_VERSION_MINOR, ${PROJECT}_VERSION_PATCH, +# and the optional ${PROJECT}_VERSION_FLAVOR. +# +# The version string take one of the forms: +# .. +# ..- +function(parse_version FILE PROJECT) + configure_file(${FILE} "${CMAKE_CURRENT_BINARY_DIR}/CHANGES.md") # Required to re-run cmake on version change + file(READ ${FILE} CHANGES) + if(${CHANGES} MATCHES "#+ *([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[a-zA-Z0-9]+)?") + set(FLAVOR "") + if(NOT "${CMAKE_MATCH_4}" STREQUAL "") + string(SUBSTRING ${CMAKE_MATCH_4} 1 -1 FLAVOR) + endif() + set("${PROJECT}_VERSION_MAJOR" ${CMAKE_MATCH_1} PARENT_SCOPE) + set("${PROJECT}_VERSION_MINOR" ${CMAKE_MATCH_2} PARENT_SCOPE) + set("${PROJECT}_VERSION_PATCH" ${CMAKE_MATCH_3} PARENT_SCOPE) + set("${PROJECT}_VERSION_FLAVOR" ${FLAVOR} PARENT_SCOPE) + set("${PROJECT}_VERSION" + "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}${CMAKE_MATCH_4}" + PARENT_SCOPE) + else() + message(FATAL_ERROR "Unable to parse version from '${FILE}'") + endif() +endfunction() diff --git a/engine/engine/3rdlibs/imgui/CMakeLists.txt b/engine/engine/3rdlibs/imgui/CMakeLists.txt index 989f54e7..dca4d80a 100644 --- a/engine/engine/3rdlibs/imgui/CMakeLists.txt +++ b/engine/engine/3rdlibs/imgui/CMakeLists.txt @@ -7,5 +7,5 @@ target_compile_features(imgui PRIVATE cxx_std_17) target_link_libraries(imgui PRIVATE SDL3::SDL3 volk::volk) target_compile_definitions(imgui PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK) -compile_shader(backends/vulkan/glsl_shader.vert glsl_shader.vert.u32) -compile_shader(backends/vulkan/glsl_shader.frag glsl_shader.frag.u32) \ No newline at end of file +# compile_shader(backends/vulkan/glsl_shader.vert glsl_shader.vert.u32) +# compile_shader(backends/vulkan/glsl_shader.frag glsl_shader.frag.u32) \ No newline at end of file diff --git a/engine/engine/3rdlibs/spirv-tools/CHANGES b/engine/engine/3rdlibs/spirv-tools/CHANGES new file mode 100644 index 00000000..569fd34a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/CHANGES @@ -0,0 +1,1769 @@ +Revision history for SPIRV-Tools + +v2025.2 2025-04-22 + - General + - Add SPV_KHR_bfloat16 support (#6057) + - Optimizer + - Fold bitwise operator and arithmetic with 0 (#6013) + - Support scalar replacement of large structs (#6019) + - Support optimization of OpCopyLogical (#6016) + - add pass to split combined image samplers (#6035) + - value numbering: preserve loads of image, sampler, sampled image (#6059) + - Delete decoration for OpPhi when unrolling (#6064) + - Add QuadControlKHR to trim pass and allow lists (#6068) + - In copy propagate arrays, debug instructions are not stores. (#6078) + - Minimal opt support for SPV_KHR_untyped_pointers (#6087) + - Validator + - Add validation for invalid layout decoration usage (#6012, #6020) + - Add Vulkan Aligned PowerOfTwo check (#6027) + - Validate PhysicalStorageBuffer Stage Interface (#6000) + - Update location/component conflict validation (#5993) + - add resolve-binding-conflicts pass (#6044) + - Check that layouts match runtime array requirement (#6048) + - Validation for relaxed control barrier with storage class semantics (#5984) + - Validate version requirement for Vulkan Memory Model (#6042) + - Add support for pointer types in vector when using extension SPV_INTEL_masked_gather_scatter (#6041) + - Restrict VUID 09557 to Vulkan environments (#6080) + - Add Vulkan 1.3 and 1.4 capability checks (#6063) + - Assembler + - Add OpUnknown pseudo-instruction (#6024) + - Diff + - Try to pair functions by their complete type. (#6021) + +v2025.1 2025-02-28 + - General + - diff: Fix crash in OpString matching (#5988) + - Add SPV_AMDX_shader_enqueue version 2 support (#5838) + - add support for SPV_INTEL_subgroup_matrix_multiply_accumulate (#5928) + - update cmake_minimum_required to 3.22.1 (#5925) + - Add OpImageSampleFootprintNV to IsAllowedSampledImageOperand (#5914) + - assembler: ensure progress when seeking the version string (#5910) + - Optimizer + - opt: keep all OpSource instructions (#5901) + - [opt] Fix bug opt::InstructionBuilder::AddVariable (#6007) + - [OPT] Add SPV_KHR_ray_tracing to allow list (#5941) + - opt: keep all OpSource instructions (#5901) + - Validator + - spirv-val: Add AllowVulkan32BitBitwise option (#6001) + - Fix untyped pointer comparison validation (#6004) + - spirv-val: Update VUIDs for 308 header (#5990) + - spirv-val: fix env parsing for vk1.1spv1.4 (#5985) + - Add validation for SPV_NV_linear_swept_spheres. (#5975) + - Add validation SPV_NV_cluster_acceleration_structure. (#5974) + - Improve the instruction diagnostic for some access chain errors (#5978) + - Update physical storage buffer validation to match SPIR-V 1.6.5 (#5977) + - Validate SPV_NV_cooperative_vector (#5972) + - Fix layout checks with untyped pointers (#5970) + - spirv-val: Update mislabeled VUIDs (#5964) + - More explicit layout validation (#5958) + - spirv-val: Add VK_KHR_maintenance8 support (#5951) + - Add EXT_mesh_shader validation support (#5640) + - spirv-val: Remove OpenCL ivec3 req (#5940) + - spirv-val: Validate zero product workgroup size (#5407) + - Relax DebugLine validation (#5916) + - Linker + - linker: remove LinkOnceODR decorations when linking executables (#5979) + - fix: handle LinkOnceODR correctly (#5938) + +v2024.4 2024-12-04 + - General + - Add FPEncoding operand type. (#5726) + - Support SPV_KHR_untyped_pointers (#5736) + - add support for SPV_INTEL_global_variable_host_access (#5786) + - Add support for SPV_KHR_compute_shader_derivative (#5817) + - Accept hex representation as binary input (#5870) + - Vulkan 1.4 support (#5899) + - Optimizer + - Add knowledge of cooperative matrices (#5720) + - Add struct-packing pass and unit test. (#5778) + - Validator + - Validate presence of Stride operand to OpCooperativeMatrix{Load,Store}KHR (#5777) + - Update sampled image validation (#5789) + - Disallow stores according to VUID 06924 (#5368) + - Add validation for SPV_NV_tensor_addressing and SPV_NV_cooperative_matrix2 (#5865) + - Linker + - allow linking functions with different pointer arguments (#5534) + +v2024.3 2024-06-20 + - General + - Optimizer + - Do not fold mul and adds to generate fmas (#5682) + - add OpExtInst forward ref fixup pass (#5708) + - Validator + - Separate Location check for tess patch (#5654) + - Validate MemoryAccessMask of OpCooperativeMatrixStoreKHR (#5668) + - OpSampledImage extra validation (#5695) + - add support for OpExtInstWithForwardRefs (#5698)A + - Disassembler + - add decorations to comments (#5675) + - Add --nested-indent and --reorder-blocks (#5671) + +v2024.2 2024-04-22 + - General + - Add SPIRV_TOOLS_EXPORT to public C++ API (#5591) + - Use bazel 7 and bzlmod (#5601) + - Optimizer + - opt: add GroupNonUniformPartitionedNV capability to trim pass (#5648) + - Fix rebuilding types with circular references. (#5637) + - Add AliasedPointer decoration (#5635) + - add support for vulkan-shader-profiler external passes (#5512) + - Validator + - A fix to support of SPV_QCOM_image_processing2 (#5646) + - spirv-val: Add Vulkan check for Rect Dim in OpTypeImage (#5644) + - Validate duplicate decorations and execution modes (#5641) + - Validator: Support SPV_NV_raw_access_chains (#5568) + +v2024.1 2024-03-06 + - General + - Add tooling support for SPV_KHR_maximal_reconvergence (#5542) + - Add support for SPV_KHR_float_controls2 (#5543) + - SPV_KHR_quad_control (#5547) + - Fold 64-bit int operations (#5561) + - update image enum tests to remove Kernel capability (#5562) + - Support operand kind for SPV_INTEL_maximum_registers (#5580) + - SPV_NV_shader_atomic_fp16_vector (#5581) + - Support for SPV_QCOM_image_processing2 (#5582) + - Fix access chain struct checks (#5592) + - Optimizer + - opt: add Int16 and Float16 to capability trim pass (#5519) + - Add preserver-interface option to spirv-opt (#5524) + - spirv-opt: Fix OpCompositeExtract relaxation with struct operands (#5536) + - opt: Add VulkanMemoryModelDeviceScope to trim (#5544) + - opt: Add TrimCapabilities pass to spirv-opt tool (#5545) + - Add modify-maximal-reconvergence to spirv-opt help (#5546) + - opt: add SPV_EXT_mesh_shader to opt allowlist (#5551) + - opt: Add OpEntryPoint to DescriptorScalarReplacement pass (#5553) + - opt: prevent meld to merge block with MaximalReconvergence (#5557) + - [OPT] Use new instruction folder for for all opcodes in spec consti folding (#5569) + - [OPT] Identify arrays with unknown length in copy prop arrays (#5570) + - [OPT] Add removed unused interface var pass to legalization passes (#5579) + - Validator + - spirv-val: Re-enable OpControlBarrier VU (#5527) + - spirv-val: Add Mesh Primitive Built-In validaiton (#5529) + - spirv-val: Validate PhysicalStorageBuffer Stage Interface (#5539) + - spirv-val: Multiple interface var with same SC (#5528) + - spirv-val: Revert Validate PhysicalStorageBuffer Stage Interface (#5575) + - spirv-val: Make Constant evaluation consistent (#5587) + +v2023.6 2023-12-18 + - General + - update_build_version.py produce deterministic header. (#5426) + - Support missing git in update_build_version.py (#5473) + - Optimizer + - Add ComputeDerivativeGroup*NV capabilities to trim capabilities pass. (#5430) + - Do not crash when tryingto fold unsupported spec constant (#5496) + - instrument: Fix handling of gl_InvocationID (#5493) + - Fix nullptr argument in MarkInsertChain (#5465) + - opt: support 64-bit OpAccessChain index in FixStorageClass (#5446) + - opt: add StorageImageReadWithoutFormat to cap trim (#5475) + - opt: add PhysicalStorageBufferAddresses to trim (#5476) + - Fix array size calculation (#5463 + - Validator + - spirv-val: Loosen restriction on base type of DebugTypePointer and DebugTypeQualifier (#5479) + - spirv-val: Add WorkgroupMemoryExplicitLayoutKHR check for Block (#5461) + +v2023.5 2023-10-15 + - General + - Support 2 Intel extensions (#5357) + - SPV_QCOM_image_processing support (#5223) + - Optimizer + - opt: fix StorageInputOutput16 trimming. (#5359) + - opt: add StoragePushConstant16 to trim pass (#5366) + - opt: enable StorageUniform16 (#5371) + - opt: add bitmask support for capability trimming (#5372) + - opt: Add SwitchDescriptorSetPass (#5375) + - opt: add FragmentShader*InterlockEXT to capability trim pass (#5390) + - opt: add Int64 capability to trim pass (#5398) + - opt: add Float64 capability to trim pass (#5428) + - opt: add raytracing/rayquery to trim pass (#5397) + - opt: add ImageMSArray capability to trim pass. (#5395) + - Add SPV_KHR_physical_storage_buffer to allowlists (#5402) + - Add SPV_EXT_fragment_shader_interlock to allow lists (#5393) + - Make sure that fragment shader interlock instructions are not removed by DCE (#5400) + - instrument: Use Import linkage for instrumentation functions (#5355) + - Add a new legalization pass to dedupe invocation interlock instructions (#5409) + - instrument: Ensure linking works even of nothing is changed (#5419) + - Validator + - Move token version/cap/ext checks from parsing to validation (#5370) + - val: re-add ImageMSArray validation (#5394) + - Linker + - linker: Add --use-highest-version option + +v2023.4 2023-07-17 + - General + - Set cmake_policy CMP0128 (#5341) + - Add python3 requirement for the script (#5326) + - Add support for LiteralFloat type (#5323) + - SPV_KHR_cooperative_matrix (#5286) + - Allow OpTypeBool in UniformConstant (#5237) + - Allow physical storage buffer pointer in IO (#5251) + - Remove const zero image operands (#5232) + - Optimizer + - Enable vector constant folding (#4913) (#5272) + - Fold negation of integer vectors (#5269) + - Add folding rule for OpTranspose (#5241) + - Add SPV_NV_bindless_texture to spirv optimizations (#5231) + - Fix incorrect half float conversion (#5349) + - Add SPV_EXT_shader_atomic_float_add to allow lists (#5348) + - Instrument + - instrument: Cast gl_VertexIndex and InstanceIndex to uint (#5319) + - instrument: Fix buffer address length calculations (#5257) + - instrument: Reduce number of inst_bindless_stream_write_6 calls (#5327) + - Validator + - Validate GroupNonUniform instructions (#5296) + - spirv-val: Label SPV_KHR_cooperative_matrix VUID (#5301) + - Validate layouts for PhysicalStorageBuffer pointers (#5291) + - spirv-val: Remove VUID from 1.3.251 spec (#5244) + - Diff + - spirv-diff: Update test expectations (#5264) + - spirv-diff: Leave undefined ids unpaired. (#5262) + - spirv-diff: Properly match SPV_KHR_ray_query types. (#5259) + - diff: Don't give up entry point matching too early. (#5224) + +v2023.3 2023-05-15 + - General + - Update spirv_headers to include SPV_KHR_ray_tracing_position_fetch (#5205) + - spirv-tools: Add support for QNX (#5211) + - build: set std=c++17 for BUILD.gn (#5162) + - Optimizer + - Run ADCE when the printf extension is used. (#5215) + - Don't convert struct members to half (#5201) + - Apply scalar replacement on vars with Pointer decorations (#5208) + - opt: Fix null deref in OpMatrixTimesVector and OpVectorTimesMatrix (#5199) + - instrument: Add set and binding to bindless error records (#5204) + - instrument: Change descriptor state storage format (#5178) + - Fix LICMPass (#5087) + - Add Vulkan memory model to allow lists (#5173) + - Do not remove control barrier after spv1.3 (#5174) + - Validator + - spirv-val: Label Interface Location/Component VUIDs (#5221) + - Add support for SPV_EXT_shader_tile_image (#5188) + - Fix vector OpConstantComposite type validation (#5191) + - spirv-val: Label new Vulkan VUID 07951 (#5154) + - Fuzz + - Do not define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE if it is already defined. (#5200) + +v2023.2 2023-03-10 + - General + - build: move from c++11 to c++17 (#4983) + - tools: refactorize tools flags parsing. (#5111) + - Add C interface for Optimizer (#5030) + - libspirv.cpp: adds c++ api for spvBinaryParse (#5109) + - build: change the way we set cxx version for bazel. (#5114) + - Optimizer + - Fix null pointer in FoldInsertWithConstants. (#5093) + - Fix removal of dependent non-semantic instructions (#5122) + - Remove duplicate lists of constant and type opcodes (#5106) + - opt: fix spirv ABI on Linux again. (#5113) + - Validator + - Validate decoration of structs with RuntimeArray (#5094) + - Validate operand type before operating on it (#5092) + - spirv-val: Conditional Branch without an exit is invalid in loop header (#5069) + - spirv-val: Initial SPV_EXT_mesh_shader builtins (#5080) + +v2023.1 2023-01-17 + - General + - Renamed "master" to "main" (issue#5051) + - Validate version 5 of clspv reflection (#5050) + - Remove testing support for VS2015 (#5027) + - Fix undef behaviour in hex float parsing (#5025) + - Require C++11 *or later* (#5020) + - Instrument + - Instrument: Fix bindless checking for BufferDeviceAddress (#5049) + - Optimizer + - Optimize allocation of spvtools::opt::Instruction::operands_ (#5024) + - spirv-opt: Fix OpCompositeInsert with Null Constant (#5008) + - spirv-opt: Handle null CompositeInsert (#4998) + - Add option to ADCE to remove output variables from interface. (#4994) + - Add support for tesc, tese and geom to EliminateDead*Components (#4990) + - Add pass to eliminate dead output components (#4982) + - spirv-opt: Add const folding for CompositeInsert (#4943) + - Add passes to eliminate dead output stores (#4970) + - Prevent eliminating case constructs in block merging (#4976) + - Validator + - Fix layout validation (#5015) + - Fix use of invalid analysis (#5013) + - Fix infinite loop in validator (#5006) + - Add validation support for SPV_NV_shader_invocation_reorder. (#4979) + - Only validate full layout in Vulkan environments (#4972) + - spirv-val: Label new Vulkan OpPtrAccessChain VUs (#4975) + - spirv-val: Add OpPtrAccessChain Base checks (#4965) + + +v2022.4 2022-10-12 + - General + - Support Narrow Types in BitCast Folding Rule (#4941) + - spirv-diff: Allow no SpecId (#4904) + - build: cmake: Add support for GNU/Hurd (#4895) + - Implement tool changes for SPV_EXT_mesh_shader. (#4915) + - Validator + - spirv-val: Add SPV_ARM_core_builtins validation (#4958) + - spirv-val: Add an option to use friendly names or not (#4951) + - spirv-val: Consistently quote ids in messages (#4950) + - spirv-val: Add initial SPV_EXT_mesh_shader validation (#4924) + - spirv-val: Make it legal to use arrays of ray queries (#4938) + - spirv-val: Better message for using OpTypeBool in input/output (#4901) + - spirv-val: Add SPV_KHR_ray_tracing storage class (#4868) + - Optimizer + - spirv-opt: Fix stacked CompositeExtract constant folds (#4932) + - Improve time to build dominators (#4916) + - Fix ADCE to mark scope and inlined_at of line instructions as live. (#4910) + - Improve algorithm to reorder blocks in a function (#4911) + - Add structs to eliminate dead input components (#4894) + - spirv-opt: fix copy-propagate-arrays index opti on structs. (#4891) + - Fix ADCE to not eliminate top level DebugInfo instructions (#4889) + - Fix array copy propagation (#4890) + +v2022.3 2022-08-08 + - General + - Add SPV_KHR_fragment_shader_barycentric support (#4805) + - Add support for SPV_KHR_subgroup_rotate (#4786) + - use exec_tools instead of tools for better RBE compatibility (#4837) + - Write binary files to stdout in binary on windows. (#4834) + - Allow spirv-opt print-all to show pretty IDs (#4888) + - Validator + - spirv-val: Add PerVertexKHR (#4807) + - spirv-opt : Add FixFuncCallArgumentsPass (#4775) + - spirv-val: Add CullMaskKHR support (#4792) + - Require ColMajor or RowMajor for matrices (#4878) + - spirv-val: Add SPV_KHR_ray_query (#4848) + - spirv-val: Add SPV_KHR_ray_tracing instructions (#4871) + - Implement SPV_NV_bindless_texture related changes (#4847) + - spirv-val: Add OpConvertUToAccelerationStructureKHR (#4838) + - spirv-val: Add support for SPV_AMD_shader_early_and_late_fragment_tests (#4812) + - Optimizer + - Fold multiply and subtraction into FMA with negation (#4808) + - Add more folding for composite instructions (#4802) + - spirv-opt: add pass for interface variable scalar replacement (#4779) + - Don't try to unroll loop with step count 0. (#4769) + - spirv-opt: SPV_NV_bindless_texture related changes (#4870) + - Linker + - linker: Recalculate interface variables (#4784) + +v2022.2 2022-04-07 + - General + - Add OpModuleProcessed to debug opcode (#4694) + - Optimizer + - Complete handling of RayQueryKHR type (#4690) + - Have scalar replacement use undef instead of null (#4691) + - Optimize Instruction::Instruction (#4705) + - Handle propagation of arrays with decorations (#4717) + - spirv-opt: Add OpExecutionModeId support (#4719) + - Optimize Type::HashValue (#4707) + - Optimize DefUseManager allocations (#4709) + - Add pass to remove DontInline function control (#4747) + - Better handling of 0xFFFFFFFF when folding vector shuffle (#4743) + - Reset the id bound on the module in compact ids (#4744) + - spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720) + - Support SPV_KHR_uniform_group_instructions (#4734) + - Handle shaders without execution model in spread-volatile-semantics (#4766) + - Validator + - Fix handling of Nontemporal image operand (#4692) + - [spirv-val] Allow 0 Component Count for DebugTypeArray for Shader (#4706) + - spirv-val: Validate DebugTypeMatrix (#4732) + - spirv-val: Label Vulkan VUID 04734 (#4739) + - spirv-val: Label VUID 06491 (#4745) + - spirv-val: Disallow array of push constants (#4742) + - spirv-val: Label Vulkan RuntimeArray VUID (#4749) + - spirv-val: Add Vulkan Image VUID 06214 (#4750) + - spirv-val: Add Vulkan Dref not allowed 3D dim VUID (#4751) + - spirv-val: Label and add test for PSB Aligned (#4756) + - spirv-val: Add Vulkan 32-bit bit op Base (#4758) + - spirv-val: Add more Vulkan VUID labels (#4764) + - Diff + - Introduce spirv-diff (#4611) + - Stabilize the output of spirv-diff (#4698) + - spirv-diff: Handle OpSpecConstant array sizes (#4700) + - spirv-diff: Match OpSpecConstantComposite correctly (#4704) + - spirv-diff: Use GetSingleWord*Operand (#4768) + - spirv-diff: Basic support for OpTypeForwardPointer (#4761) + - spirv-diff: Fix OpTypeFunction matching w.r.t operand count (#4771) + +v2022.1 2022-01-26 + - General + - Add SPIR-V 1.6 support to wasm build (#4674) + - Improvements to disassembly within PassManager (#4677) + - Basic support for SPIR-V 1.6 (#4663) + - reflect debug (#4662) + - Fix endianness of string literals (#4622) + - Optimizer + - spirv-opt: add pass to Spread Volatile semantics (#4667) + - Fix constant propagation and folding of FClamp instructions (#4651) + - Manually fold floating point division by zero (#4637) + - Allow ADCE to remove dead inputs (#4629) + - Linker + - Linker improvements (#4679) + * test/linker: Code factorisation and small tweaks + * linker: Do not fail when going over limits + - Validator + - val: interface struct with builtins must be Block (#4665) + - Fuzzer + - Avoid id bound errors during opt fuzzing (#4658) + - Avoid uninitialised read when parsing hex float (#4646) + +v2021.4 2021-11-11 + - General + - Add a WebAssembly build (#3752) + - Make cxx exceptions controllable (#4591) + - Validator + - Improve decoration validation (#4490) + - Optimizer + - Add spirv-opt pass to replace descriptor accesses based on variable indices (#4574) + - Do not fold snegate feeding sdiv (#4600) + - Handle overflowing id in merge return (#4606) + - Fuzzer + - Add libFuzzer target for spirv-fuzz (#4434) + - Linter + +v2021.3 2021-08-24 + - General + - Initial support for SPV_KHR_integer_dot_product (#4327) + - Add non-semantic vulkan extended instruction set (#4362) + - Add common enum for debug info instructions from either opencl or vulkan (#4377) + - Validator + - Add validation for SPV_EXT_shader_atomic_float16_add (#4325) + - Disallow loading a runtime-sized array (#4473) + - spirv-val: Validate vulkan debug info similarly to opencl debug info (#4466) + - Optimizer + - spirv-opt: support SPV_EXT_shader_image_int64 (#4379) + - spirv-opt: Add dataflow analysis framework (#4402) + - Add control dependence analysis to opt (#4380) + - Add spirv-opt convert-to-sampled-image pass (#4340) + - spirv-opt: Add handling of vulkan debug info to DebugInfoManager (#4423) + - Fuzz + - spirv-fuzz: support AtomicLoad (#4330) + - spirv-fuzz: Support AtomicStore (#4440) + - spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376) + - spirv-fuzz: Add minimal SPIR-V example to test shaders (#4415) + - spirv-fuzz: support building using gn (#4365) + - Linter + - Add new target for spirv-lint (#4446) + - spirv-lint: add basic CLI argument handling (#4478) + - Add divergence analysis to linter (#4465) + +v2021.2 2021-06-18 + - General + - Support SPV_KHR_subgroup_uniform_control_flow (#4318) + - Support Intel extensions for fixed point and hls-float (#4321) + - Fix crash when optimizing shaders with DebugPrintf (#4280) + + - Validator + - Support Vulkan Storage Class for Execution Model (#4212) + + - Optimizer + - Handle SPV_KHR_vulkan_memory_model in dead-code elimination (#4320) + - Support folding OpBitcast with numeric constants (#4247) + + - Fuzz + - Add tests for MaybeGet* functions in fuzzerutil (#4284) + - Fix OutlineFunction in presence of unreachable blocks (#4308) + - Fix def-use update in PermutePhiOperands (#4309) + - Swap positions of two functions in a module (#4236) + +v2021.1 2021-04-19 + - General + - Support SPV_KHR_linkonce_odr, SPV_KHR_expect_assume (#4161) + - Fixes for the vscode language server extension (#4150) + - Validator + - Add validation for SPV_EXT_shader_atomic_float_min_max (#4105) + - Add Vulkan Execution Scope checks (#4183) + - Vulkan 64-bit OpAtomicStore check (#4163) + - Optimizer + - Add interpolate legalization pass (#4220) + - Fuzz + - Various performance optimizations + - Do not add too many dead blocks (#4217) + - Add WGSL compatibility flag to context (#4193) + - Add persistent state to the fuzzer (#4137) + +v2020.7 2021-02-16 + - General + - Support pending Intel extensions (#4116) + - Remove WebGPU support (#4108) + - Validator + - Vulkan image gather constant component (#4133) + - Add Vulkan PSB64 convert VUID (#4122) + - Validate SPV_KHR_workgroup_memory_explicit_layout (#4128) + - Validate VK_KHR_zero_initialize_workgroup_memory (#4124) + - Add Vulkan image gather offset VUID (#4118) + - Label Vulkan atomic semantics VUIDs (#4120) + - Label VUID 04662 (#4123) + - Label VUID 04683 (#4121) + - Add Vulkan EXT builtins (#4115) + - Validate Sampled=1 for Vulkan ImageQuerySizeLod, ImageQueryLevels, ImageQueryLod (#4103) + - Add Vulkan Memory Scope VUs (#4106) + - Add Vulkan Addressing Model check (#4107) + - Vulkan atomic storage class (#4079) + - Label standalone Vulkan VUID (#4091) + - Add Vulkan decroation VUID (#4090) + - Add Vulkan FP Mode VUID (#4088) + - Fix Vulkan image sampled check (#4085) + - Add Vulkan ForwardPointer VUID (#4089) + - Add Vulkan ImageTexelPointer format check (#4087) + - Add Vulkan Group Operation VUID (#4086) + - Add first StandAlone VUID 04633 (#4077) + - Add Subgroup VUIDs (#4074) + - validate return type of OpImageRead (#4072) + - tighter validation of multisampled images (#4059) + - validate OpTypeImage Sampled values for environemnts (#4064) + - validate StorageImageMultisampled capability (#4062) + - Add last TessLevelOuter and TessLevelInner VUID (#4055) + - Add last ClipDistance and CullDistance VUID (#4054) + - Add last ViewportIndex and Layer VUID (#4053) + - Add last Position VUID (#4052) + - Allow forward pointer to be used in types generally (#4044) + - Optimizer + - Mark module as modified if convert-to-half removes decorations (#4127) + - Fix binding number calculation in desc sroa (#4095) + - Run DCE when SPV_KHR_shader_clock is used (#4049) + - Debug Info + - Set correct scope and line info for DebugValue (#4125) + - Avoid integrity check failures caused by propagating line instructions (#4096) + - Linker + - Linker usability improvements (#4084) + - Instrumentation + - Generate differentiated error codes for buffer oob checking (#4097) + - Fuzz + - Fix OpPhi handling in DuplicateRegionWithSelection (#4065) + +v2020.6 2020-12-07 + - General + CMake: Add SPIRV_TOOLS_BUILD_STATIC flag (#3910) + - Disassembler + Add some context comments to disassembly. (#3847) + - Optimizer + - Take new (raytracing) termination instructions into account. (#4050) + - Do run DCE if SPV_KHR_ray_query is used. (#4047) + - Handle 8-bit index in elim dead member (#4043) + - Add texel buffer out-of-bounds checking instrumentation (#4038) + - Update MeshShadingNV dependencies (and land Ray tracing updates) (#4028) + - Fix buffer oob instrumentation for matrix refs (#4025) + - Fix SSA re-writing in the presence of variable pointers. (#4010) + - Add support to prevent functions from being inlined if they have + DontInline flag (#3858) + - Add SPV_EXT_shader_image_int64 (#3852) + - Support SPV_KHR_fragment_shading_rate (#3943) + - Fix use-after-move in val/validate.cpp (#3848) + - Debug Info + - properly preserve DebugValue indexes operand (#4022) + - Add DebugValue for invisible store in single_store_elim (#4002) + - Propagate OpLine to all applied instructions in spirv-opt (#3951) + - Add DebugValue for DebugDecl invisible to value assignment (#3973) + - Add DebugValue for function param regardless of scope (#3923) + - Debug info preservation in convert-local-access-chains pass (#3835) + - Debug info preservation in redundancy-elimination pass (#3839) + - Debug info preservation in if-conversion pass (#3861) + - Validator + - Add validation support for the ray tracing built-in variables (#4041) + - Use less stack space when validating Vulkan builtins (#4019) + - Fix SPV_KHR_fragment_shading_rate VUID label (#4014) + - Label Layer and ViewportIndex VUIDs (#4013) + - Allow the ViewportIndex and Layer built-ins on SPIR-V 1.5 (#3986) + - Fix validation of OpPhi instructions (#3919) + - Fuzz + - Fix facts arising from CompositeConstruct (#4034) + - Do not flatten conditionals that create synonyms (#4030) + - Add support for reining in rogue fuzzer passes (#3987) + - Fix assertion failure in FuzzerPassAddCompositeExtract (#3995) + - Fix invalid equation facts (#4009) + - Fix bugs in TransformationFlattenConditionalBranch (#4006) + - Fix bug related to transformation applicability (#3990) + - Add expand vector reduction transformation (#3869) + - Add FuzzerPassAddCompositeExtract (#3904) + - Fix mismatch with shrinker step limit (#3985) + - Fix off-by-one error in replayer (#3982) + - Get order right for OpSelect arguments (#3974) + - Do not add synonym-creating loops in dead blocks (#3975) + - Skip OpTypeSampledImage when propagating up (#3976) + - Pass OpUndef in function call if needed (#3978) + - Fix off-by-one in TransformationCompositeConstruct (#3979) + - Tolerate absent ids in data synonym fact management (#3966) + - Fix to id availability (#3971) + - Fix operand types (#3962) + - Don't flatten conditional if condition is irrelevant (#3944) + - Do not produce OpPhis of type OpTypeSampledImage (#3964) + - Restrict fuzzer pass to reachable blocks (#3970) + - Handle more types when extending OpPhi instructions (#3969) + - Skip early terminator wrappers when merging returns (#3968) + - Avoid irrelevant constants in synonym-creating loops (#3967) + - Skip dead blocks in FuzzerPassAddOpPhiSynonyms (#3965) + - Avoid the type manager when looking for struct types (#3963) + - Fix to TransformationDuplicateRegionWithSelection (#3941) + - Skip OpFunction when replacing irrelevant ids (#3932) + - Use component-wise selectors when flattening conditional branches (#3921) + - Avoid void struct member when outlining functions (#3936) + - Do not allow Block-decorated structs when adding parameters (#3931) + - Fix to operand id type (#3937) + - Handle dead blocks in TransformationEquationInstruction (#3933) + - Do not allow sampled image load when flattening conditionals (#3930) + - Take care of OpPhi instructions when inlining (#3939) + - Fix to TransformationInlineFunction (#3913) + - Wrap early terminators before merging returns (#3925) + - Lower probability of adding bit instruction synonyms (#3917) + - Fix handling of OpPhi in FlattenConditionalBranch (#3916) + - Avoid creating blocks without parents (#3908) + - Do not allow creation of constants of block-decorated structs (#3903) + - Fixes related to irrelevant ids (#3901) + - Fix to transformation that adds a synonym via a loop (#3898) + - Fix to duplicate region with selection (#3896) + - Do not expose synonym facts for non-existent ids (#3891) + - Do not add synonyms involving irrelevant ids (#3890) + - Do not replace irrelevant ids that are not in blocks (#3892) + - Wrap OpKill and similar in function calls (#3884) + - Integrate spirv-reduce with shrinker (#3849) + - Report fresh ids in transformations (#3856) + - Support OpNot bit instruction case (#3841) + - Return IR and transformation context after replay (#3846) + +v2020.5 2020-09-22 + - General + - Enable building with BUILD_SHARED_LIBS=1 (#3490) + - Avoid using /MP4 for clang on windows. (#3662) + - Fix compiler error on macOS with XCode12. (#3836) + - Optimizer + - Preserve OpenCL.DebugInfo.100 through private-to-local pass (#3571) + - Preserve debug info in scalar replacement pass (#3461) + - Debug info preservation in loop-unroll pass (#3548) + - Preserve debug info in dead-insert-elim pass (#3652) + - Improve non-semantic instruction handling in the optimizer (#3693) + - Let ADCE pass check DebugScope (#3703) + - Add undef for inlined void function (#3720) + - Fix SSA-rewrite to remove DebugDeclare for variables without loads (#3719) + - Handle DebugScope in compact-ids pass (#3724) + - Add buffer oob check to bindless instrumentation (#3800) + - Validator + - Update OpenCL capabilities validation (#3149) + - Validator support for non-semantic clspv reflection (#3618) + - OpenCL.DebugInfo.100 DebugTypeArray with variable size (#3549) + - Only validation locations for appropriate execution models (#3656) + - Validate more OpenCL.DebugInfo.100 instructions (#3684) + - Allow DebugTypeTemplate for Type operand (#3702) + - spirv-val: Add Vulkan VUID labels to BuiltIn (#3756) + - Allow SPV_KHR_8bit_storage extension. (#3780) + - Validate SPIRV Version number when parsing binary header (#3834) + - Reduce + - Support reducing a specific function (#3774) + - Fuzz + - adds TransformationReplaceCopyObjectWithStoreLoad (#3567) + - adds TransformationReplaceCopyMemoryWithLoadStore (#3575) + - adds TransformationReplaceLoadStoreWithCopyMemory (#3586) + - Implement the OpOuterProduct linear algebra case (#3617) + - Pass to replace int operands with ints of opposite signedness (#3612) + - TransformationMoveInstructionDown (#3477) + - Add TransformationMakeVectorOperationDynamic (#3597) + - TransformationReplaceAddSubMulWithCarryingExtended (#3598) + - FuzzerPassPropagateInstructionsUp (#3478) + - add FuzzerPassAddCompositeInserts (#3606) + - Add inline function transformation (#3517) + - Transformation to replace the use of an irrelevant id (#3697) + - Add SPIRV_FUZZ_PROTOC_COMMAND (#3789) + - Add TransformationDuplicateRegionWithSelection (#3773) + - Transformation to flatten conditional branch (#3667) + - Handle OpPhis in TransformationInlineFunction (#3833) + - Create synonym of int constant using a loop (#3790) + - Support dead blocks in TransformationAddSynonym (#3832) + - Linker + +v2020.4 2020-07-22 + - General + - Changed variable names to be more descriptive (#3433) + - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512) + - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463) + - Support SPV_KHR_terminate_invocation (#3568) + - Optimizer + - Preserving debug information in optimizations + (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542) + - Eliminate branches with condition of OpConstantNull (#3438) + - Use structured order to unroll loops. (#3443) + - Updated desc_sroa to support flattening structures (#3448) + - Support OpCompositeExtract pattern in desc_sroa (#3456) + - Fix ADCE pass bug for mulitple entries (#3470) + - Sink pointer instructions in merge return (#3569) + - Validator + - Validate location assignments (#3308) + - Fix reachability in the validator (#3541) + - Reduce + - Fuzz + - Add support for OpSpecConstant* (#3373) + - Add replace linear algebra instruction transformation (#3402) + - Implement vector shuffle fuzzer pass (#3412) + - Swap operands in OpBranchConditional (#3423) + - Permute OpPhi instruction operands (#3421) + - Add FuzzerPassAddCopyMemoryInstructions (#3391) + - TransformationInvertComparisonOperator (#3475) + - Add variables with workgroup storage class (#3485) + - Add image sample unused components transformation (#3439) + - TransformationReplaceParameterWithGlobal (#3434) + - Support adding dead break from back-edge block (#3519) + - Fuzzer pass to interchange zero-like constants (#3524) + - Linker + +v2020.3 2020-05-27 + - General + - Prevent Effcee from installing things when building spirv-tools with testing enabled (#3256) + - Update acorn version (#3294) + - If SPIRV-Headers is in our tree, include it as subproject (#3299) + - allow cross compiling for Windows Store, UWP, etc. (#3330) + - Optimizer + - Remove deprecated interfaces from instrument passes (#3361) + - Preserve debug info in inline pass (#3349) + - Handle more cases in dead member elim (#3289) + - Preserve debug info in eliminate-dead-functions (#3251) + - Fix Struct CFG analysis for single block loop (#3293) + - Add tests for recently added command line option (#3297) + - Consider sampled images as read-only storage (#3295) + - Allow various validation options to be passed to spirv-opt (#3314) + - Add debug information analysis (#3305) + - Preserve debug info for wrap-opkill (#3331) + - refactor inlining pass (#3328) + - Add unrolling to performance passes (#3082) + - Validator + - Add validation support for ImageGatherBiasLodAMD (#3363) + - Validate ShaderCallKHR memory scope (#3332) + - Validate Buffer and BufferBlock apply only to struct types (#3259) + - Reduce + - increase default step limit (#3327) + - Remove unused uniforms and similar (#3321) + - Fuzz + - Add support for StorageBuffer (#3348) + - Add validator options (#3254) + - Limit adding of new variables to 'basic' types (#3257) + - Transformation to add OpConstantNull (#3273) + - Handling of more fuzzing opportunities (#3277, #3280, #3281, #3290, #3292) + - Respect rules for OpSampledImage (#3287) + - Do not outline regions that produce pointer outputs (#3291) + - Linker + +v2020.2 2020-03-26 + - General: + - Support extended instructions in the vscode language server + - Make spvOpcodeString part of the public API (#3174) + - Added guide to writing a spirv-fuzz fuzzer pass (#3190) + - Add support for KHR_ray_{query,tracing} extensions (#3235) + - Optimizer + - Debug Printf support (#3215) + - Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183) + - Fix identification of Vulkan images and buffers (#3253) + - Validator + - Add support for SPV_AMD_shader_image_load_store_lod (#3186) + - Add validation rules for OpenCL.DebugInfo.100 extension (#3133) + - Adding WebGPU specific Workgroup scope rule (#3204) + - Disallow phis of images, samplers and sampled images (#3246) + - Reduce + - Fuzz + - Fuzzer passes to add local and global variables (#3175) + - Add fuzzer passes to add loads/stores (#3176) + - Fuzzer pass to add function calls (#3178) + - Fuzzer pass that adds access chains (#3182) + - Fuzzer pass to add equation instructions (#3202) + - Add swap commutable operands transformation (#3205) + - Add fuzzer pass to permute function parameters (#3212) + - Allow OpPhi operand to be replaced with a composite synonym (#3221) + - Linker + +v2020.1 2020-02-03 + - General: + - Add support for SPV_KHR_non_semantic_info (#3110) + - Support OpenCL.DebugInfo.100 extended instruction set (#3080) + - Added support for Vulkan 1.2 + - Add API function to better handle getting the necessary environment (#3142) + - Clarify mapping of target env to SPIR-V version (#3150) + - Implement constant folding for many transcendentals (#3166) + - Optimizer + - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119) + - Better handling of OpLine on merge blocks (#3130) + - Use placeholder switch instead of placeholder loop in MergeReturn pass. (#3151) + - Handle TimeAMD in AmdExtensionToKhrPass. (#3168) + - Validator + - Fix structured exit validation (#3141) + - Reduce + - Fuzz + - Fuzzer pass to merge blocks (#3097) + - Transformation to add a new function to a module (#3114) + - Add fuzzer pass to perform module donation (#3117) + - Fuzzer passes to create and branch to new dead blocks (#3135) + - Fuzzer pass to add composite types (#3171) + - Linker: + - Remove names and decorations of imported symbols (#3081) + +v2019.5 2019-12-11 + - General: + - Export SPIRV-Tools targets on installation + - SPIRV-Tools support for SPIR-V 1.5 (#2865) + - Add WebGPU SPIR-V Assembler in JavaScript. (#2876) + - Add Bazel build configuration. (#2891) + - Add support for building with emscripten (#2948) + - Update SPIR-V binary header test for SPIR-V 1.5 (#2967) + - Add fuzzer for spirv-as call path (#2976) + - Improved CMake install step. (#2963) + - Add fuzzer for spirv-dis call path (#2977) + - Ensure timestamp does not vary with timezone. (#2982) + - Add a vscode extension for SPIR-V disassembly files (#2987) + - Add iOS as a supported platform (#3001) + - utils/vscode: Add SPIR-V language server support + - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054) + - Permit the debug instructions in WebGPU SPIR-V (#3063) + - Add support for Fuchsia. (#3062) + - Optimizer + - Add descriptor array scalar replacement (#2742) + - Add pass to wrap OpKill in a function call (#2790) + - Fold FMix during constant folding. (#2818) + - Add pass to replace AMD shader ballot extension (#2811) + - Add pass to make Float32 operation relax precision (#2808) + - Add pass to make relax precision operation Float16 (#2808) + - Add pass to replace uses of 3 AMD extensions (#2814) + - Fold Min, Max, and Clamp instructions. (#2836) + - Better handling of OpKill in continues (#2842,#2922,#2933) + - Enable OpTypeCooperativeMatrix specialization (#2927) + - Support constant-folding UConvert and SConvert (#2960) + - Update Offset to ConstOffset bitmask if operand is constant. (#3024) + - Improve RegisterSizePasses (#3059) + - Folding: perform add and sub on mismatched integer types (#3084) + - Graphics robust access: use signed clamp (#3073) + Fixes: + - Instrument: Fix version 2 output record write for tess eval shaders. (#2782) + - Instrument: Add support for Buffer Device Address extension (#2792) + - Fix check for changed binary in API call. (#2798) + - For WebGPU<->Vulkan optimization, set correct execution environment (#2834) + - Handle OpConstantNull in copy-prop-arrays. (#2870) + - Use OpReturn* in wrap-opkill (#2886) + - Validator + - Add generic builtin validation of target (#2843) + - Extra resource interface validation (#2864) + - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862) + - Relaxed bitcast with pointers (#2878) + - Validate physical storage buffer restrictions (#2930) + - Add SPV_KHR_shader_clock validation (#2879, #3013) + - Validate that selections are structured (#2962) + - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980) + - Check that derivatives operate on 32-bit values (#2983) + - Validate array stride does not cause overlap (#3028) + - Validate nested constructs (#3068) + Fixes: + - Fix validation of constant matrices (#2794) + - Update "remquor" validation + - Only allow previously declared forward refs in structs (#2920) + - Reduce + - Remove relaxed precision decorations (#2797) + - Reduce/fuzz: improve command line args (#2932) + - Improve remove unref instr pass (#2945) + Fixes: + - Fuzz + - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838) + - Add fuzzer pass to copy objects (#2853) + - Add fuzzer pass to replace ids with synonyms (#2857) + - Allow validation during spirv-fuzz replay (#2873) + - Employ the "swarm testing" idea in spirv-fuzz (#2890) + - reduce/fuzz: improve command line args (#2932) + - option to convert shader into a form that renders red (#2934) + - Add fuzzer pass to change selection controls (#2944) + - add transformation and pass to construct composites (#2941) + - Add fuzzer pass to change loop controls (#2949) + - Add fuzzer pass to change function controls (#2951) + - Add fuzzer pass to add NoContraction decorations (#2950) + - Add missing functionality for matrix composites (#2974) + - Fuzzer pass to adjust memory access operands (#2968) + - Transformation to extract from a composite object (#2991) + - Vector shuffle transformation (#3015) + - Improve debugging facilities (#3074) + - Function outlining fuzzer pass (#3078) + + +v2019.4 2019-08-08 + - General: + - Memory model support for SPIR-V 1.4 + - Add new spirv-fuzz tool + - Add option for base branch in check_code_format.sh + - Removed MarkV and Stats code. (#2576) + - Instrument: Add version 2 of record formats (#2630) + - Linker: Better type comparison for OpTypeArray and OpTypeForwardPointer (#2580) + - Optimizer + - Bindless Validation: Instrument descriptor-based loads and stores (#2583) + - Better folding for OpSpecConstantOp (#2585, #2614) + - Add in individual flags for Vulkan <-> WebGPU passes (#2615) + - Handle nested breaks from switches. (#2624) + - Optimizer: Handle array type with OpSpecConstantOp length (#2652) + - Perform merge return with single return in loop. (#2714) + - Add --preserve-bindings and --preserve-spec-constants (#2693) + - Remove Common Uniform Elimination Pass (#2731) + - Allow ray tracing shaders in inst bindle check pass. (#2733) + - Add pass to inject code for robust-buffer-access semantics (#2771) + - Treat access chain indexes as signed in SROA (#2776) + - Handle RelaxedPrecision in SROA (#2788) + - Add descriptor array scalar replacement (#2742) + Fixes: + - Handle decorations better in some optimizations (#2716) + - Change the order branches are simplified in dead branch elim (#2728) + - Fix bug in merge return (#2734) + - SSA rewriter: Don't use trivial phis (#2757) + - Record correct dominators in merge return (#2760) + - Process OpDecorateId in ADCE (#2761) + - Fix check for unreachable blocks in merge-return (#2762) + - Handle out-of-bounds scalar replacements. (#2767) + - Don't move debug or decorations when folding (#2772) + - Protect against out-of-bounds references when folding OpCompositeExtract (#2774) + - Validator + - Validate loop merge (#2579) + - Validate construct exits (#2459) + - Validate OpenCL memory and addressing model environment rules (#2589) + - Validate OpenCL environment rules for OpTypeImage (#2606) + - Allow breaks to switch merge from nested construct (#2604) + - Validate OpenCL environment rules for OpImageWrite (#2619) + - Allow arrays of out per-primitive builtins for mesh shaders (#2617) + - Validate OpenCL rules for ImageRead and OpImageSampleExplicitLod (#2643) + - Add validation for SPV_EXT_fragment_shader_interlock (#2650) + - Add builtin validation for SPV_NV_shader_sm_builtins (#2656) + - Add validation for Subgroup builtins (#2637) + - Validate variable initializer type (#2668) + - Disallow stores to UBOs (#2651)A + - Validate Volatile memory semantics bit (#2672) + - Basic validation for Component decorations (#2679) + - Validate that in OpenGL env block variables have Binding (#2685) + - Validate usage of 8- and 16-bit types with only storage capabilities (#2704) + - Add validation for SPV_EXT_demote_to_helper_invocation (#2707) + - Extra small storage validation (#2732) + - For Vulkan, disallow structures containing opaque types (#2546) + - Validate storage class OpenCL environment rules for atomics (#2750) + - Update OpControlBarriers rules for WebGPU (#2769) + - Update OpMemoryBarriers rules for WebGPU (#2775) + - Update WebGPU validation rules of OpAtomic*s (#2777) + Fixes: + - Disallow merge targeting block with OpLoopMerge (#2610) + - Update vloadn and vstoren validation to match the OpenCL Extended + Instruction Set Specification (#2599) + - Update memory scope rules for WebGPU (#2725) + - Allow LOD ops in compute shaders with derivative group execution modes (#2752) + - Reduce + Fixes: + +v2019.3 2019-05-14 + - General: + - Require Python 3 since Python 2 will out of service soon. + - Add a continuous test that does memory checks using the address sanitizer. + - Fix the build files so the SPIRV_USE_SANITIZER=address build works. + - Packaging top of tree build artifacts again. + - Added support for SPIR-V 1.4. (#2550) + - Optimizer + - Remove duplicates from list of interface IDs in OpEntryPoint instruction (#2449) + - Bindless Validation: Descriptor Initialization Check (#2419) + - Add option to validate after each pass (#2462) + - Add legalization pass to fix mismatched pointer (#2430, #2535) + - Add error messages when the input contains unknown instructions. (#2487) + - Add pass to convert from WebGPU Spir-V to Vulkan Spir-V and back. (#2495) + Fixes: + - #2412: Dead memeber elimination should not change input and output variables. + - #2405: Fix OpDot folding of half float vectors. + - #2391: Dead branch elim should not fold away back edges. + - #2441: Removing decorations when doing constant propagation. + - #2455: Maintain inst to block mapping in merge return. + - #2453: Fix merge return in the face of breaks. + - #2456: Handle dead infinite loops in DCE. + - #2458: Handle variable pointer in some optimizations. + - #2452: Fix dead branch elimination to handle unreachable blocks better. + - #2528: Fix undefined bit shift in sroa. + - #2539: Change implementation of post order CFG traversal. + - Validator + - Add validation of storage classes for WebGPU (#2446) + - Add validation for ExecutionMode in WebGPU (#2443) + - Implement WebGPU specific CFG validation (#2386) + - Allow NonWritable to target struct members. (#2420) + - Allow storage type mismatch for parameter in relaxed addressing mode. + - Allow non memory objects as parameter in relaxed addressing mode. + - Disallow nested Blocks and buffer blocks (#2410). + - Add validation for SPV_NV_cooperative_matrix (#2404) + - Add --strip-atomic-counter-memory (#2413) + - Check OpSampledImage is only passed into valid instructions (#2467) + - Handle function decls in Structured CFG analysis (#2474) + - Validate that OpUnreacahble is not statically reachable (#2473) + - Add pass to generate needed initializers for WebGPU (#2481) + - Allow images without format for OpenCL. (#2470) + - Remove unreachable block validation (#2525) + - Reduce runtime of array layout checks (#2534) + - Add validation specific to OpExecutionModeId (#2536) + - Validate sign of int types. (#2549) + - VK_KHR_uniform_buffer_standard_layout validation (#2562) + Fixes: + - #2439: Add missing DepthGreater case to Fragment only check. + - #2168: Disallow BufferBlock on StorageBuffer variables for Vulkan. + - #2408: Restrict and Aliased decorations cannot be applied to the same id. + - #2447: Improve function call parameter check. + - Reduce + - Add Pass to remove unreferenced blocks. (#2398) + - Allows passing options to the validator. (#2401) + - Improve reducer algorithm and other changes (#2472) + - Add Pass to remove selections (#2485) + - Add passes to simplify branches (#2507) + Fixes: + - #2478: fix loop to selection pass for loops with combined header/continue block + +v2019.2 2019-02-20 + - General: + - Support SPV_EXT_physical_storage_buffer + - A number of memory leak have been fixed. + - Removed use of deprecated Google test macro: + - Changed the BUILD.gn to only build tests in Chromium. + - Optimizer + - Upgrade memory model improvments for modf and frexp. + - Add a new pass to move loads closer to their uses: code sinking. + - Invalidating the type manager now invalidates the constnat manager. + - Expand instrumentation pass for bindless bounds checking to runtime-sized descriptor arrays. + - Add a new pass that removes members from structs that are not used: dead member elimination. + Fixes: + - #2292: Remove undefined behaviour when folding bit shifts. + - #2294: Fixes for instrumentation code. + - #2293: Fix overflow when folding -INT_MIN. + - #2374: Don't merge unreachable blocks when merging blocks. + - Validator + - Support SPV_KHR_no_integer_wrap and related decorations. + - Validate Vulkan rules for OpTypeRuntimeArray. + - Validate NonWritable decoration. + - Many WebGPU specific validation rules were added. + - Validate variable pointer related function call rules. + - Better error messages. + Fixes: + - #2307: Check forwards references in OpTypeArray. + - #2315, #2303: Fixed the layout check for relaxed layout. + - #1628: Emit an error when an OpSwitch target is not an OpLabel. + - Reduce + - Added more documentation for spirv-reduce. + - Add ability to remove OpPhi instructions. + - Add ability to merge two basic blocks. + - Add ability to remove unused functions and unused basic blocks. + Fixes: + +v2019.1 2019-01-07 + - General: + - Created a new tool called spirv-reduce. + - Add cmake option to turn off SPIRV_TIMER_ENABLED (#2103) + - New optimization pass to update the memory model from GLSL450 to VulkanKHR. + - Recognize OpTypeAccelerationStructureNV as a type instruction and ray tracing storage classes. + - Fix GCC8 build. + - Add --target-env flag to spirv-opt. + - Add --webgpu-mode flag to run optimizations for webgpu. + - The output disassembled line number stead of byte offset in validation errors. (#2091) + - Optimizer + - Added the instrumentation passes for bindless validation. + - Added passes to help preserve OpLine information (#2027) + - Add basic support for EXT_fragment_invocation_density (#2100) + - Fix invalid OpPhi generated by merge-return. (#2172) + - Constant and type manager have been turned into analysies. (#2251) + Fixes: + - #2018: Don't inline functions with a return in a structured CFG contstruct. + - #2047: Fix bug in folding when volatile stores are present. + - #2053: Fix check for when folding floating pointer values is allowed. + - #2130: Don't inline recursive functions. + - #2202: Handle multiple edges between two basic blocks in SSA-rewriter. + - #2205: Don't unswitch a latch condition during loop unswitch. + - #2245: Don't fold branch in loop unswitch. Run dead branch elimination to fold them. + - #2204: Fix eliminate common uniform to place OpPhi instructions correctly. + - #2247: Fix type mismatches caused by scalar replacement. + - #2248: Fix missing OpPhi after merge return. + - #2211: After merge return, fix invalid continue target. + - #2210: Fix loop invariant code motion to not place code between merge instruction and branch. + - #2258: Handle CompositeInsert with no indices in VDCE. + - #2261: Have replace load size handle extact with no index. + - Validator + - Changed the naming convention of outputing ids with names in diagnostic messages. + - Added validation rules for UniformConstant variables in Vulkan. + - #1949: Validate uniform variable type in Vulkan + - Ensure for OpVariable that result type and storage class operand agree (#2052) + - Validator: Support VK_EXT_scalar_block_layout + - Added Vulkan memory model semantics validation + - Added validation checkes spefic to WebGPU environment. + - Add support for VK_EXT_Transform_feedback capabilities (#2088) + - Add validation for OpArrayLength. (#2117) + - Ensure that function parameter's type is not void (#2118) + - Validate pointer variables (#2111) + - Add check for QueueFamilyKHMR memory scope (#2144) + - Validate PushConstants annotation and type (#2140) + - Allow Float16/Int8 for Vulkan 1.0 (#2153) + - Check binding annotations in resource variables (#2151, #2167) + - Validate OpForwardPointer (#2156) + - Validate operation for OpSpecConstantOp (#2260) + Fixes: + - #2049: Allow InstanceId for NV ray tracing + - Reduce + - Initial commit wit a few passes to reduce test cases. + - Validation is run after each reduction step. + Fixes: + + +v2018.6 2018-11-07 + - General: + - Added support for the Nvidia Turing and ray tracing extensions. + - Make C++11 the CXX standard in CMakeLists.txt. + - Enabled a parallel build for MSVC. + - Enable pre-compiled headers for MSVC. + - Added a code of conduct. + - EFFCEE and RE2 are now required when build the tests. + - Optimizer + - Unrolling loops marked for unrolling in the legalization passes. + - Improved the compile time of loop unrolling. + - Changee merge-return to create a placeholder loop around the function. + - Small improvement to merge-blocks to allow it to merge more often. + - Enforce an upper bound for the ids, and add option to set it. + - #1966: Report error if there are unreachable block before running merge return + Fixes: + - #1917: Allow 0 (meaning unlimited) as a parameter to --scalar-replacement + - #1915: Improve handling of group decorations. + - #1942: Fix incorrect uses of the constant manager. Avoids type mismatches in generated code. + - #1997: Fix dead branch elimination when there is a loop in folded selection. + - #1991: Fixes legality check in if-conversion. + - #1987: Add nullptr check to array copy propagation. + - #1984: Better handling of OpUnreachable in ADCE. + - #1983: Run merge return on reachable functions only. + - #1956: Handled atomic operations in ADCE. + - #1963: Fold integer divisions by 0 to 0. + - #2019: Handle MemberDecorateStringGOOGLE in ADCE and strip reflect. + - Validator + - Added validation for OpGroupNonUniformBallotBitCount. + - Added validation for the Vulkan memory model. + - Added support for VK_KHR_shader_atddomic_int64. + - Added validation for execution modes. + - Added validation for runtime array layouts. + - Added validation for 8-bit storage. + - Added validation of OpPhi instructions with pointer result type. + - Added checks for the Vulkan memory model. + - Validate MakeTexelAvailableKHR and MakeTexelVisibleKHR + - Allow atomic function pointer for OpenCL. + - FPRounding mode checks were implemented. + - Added validation for the id bound with an option to set the max id bound. + Fixes: + - #1882: Improve the validation of decorations to reduce memory usage. + - #1891: Fix an potential infinite loop in dead-branch-elimination. + - #1405: Validate the storage class of boolean objects. + - #1880: Identify arrays of type void as invalid. + - #487: Validate OpImageTexelPointer. + - #1922: Validate OpPhi instructions are at the start of a block correctly. + - #1923: Validate function scope variable are at the start of the entry block. + +v2018.5 2018-09-07 + - General: + - Support SPV_KHR_vulkan_memory_model + - Update Dim capabilities, to match SPIR-V 1.3 Rev 4 + - Automated build bots no run tests for the VS2013 case + - Support Chromium GN build + - Use Kokoro bots: + - Disable Travis-CI bots + - Disable AppVeyor VisualStudio Release builds. Keep VS 2017 Debug build + - Don't check export symbols on OSX (Darwin): some installations don't have 'objdump' + - Reorganize source files and namespaces + - Fixes for ClangTidy, and whitespace (passes 'git cl presumit --all -uf') + - Fix unused param compile warnings/errors when Effcee not present + - Avoid including time headers when timer functionality is disabled + - Avoid too-stringent warnings flags for Clang on Windows + - Internal refactoring + - Add hooks for automated fuzzing + - Add testing of command line executables + - #1688: Use binary mode on stdin; fixes "spirv-dis . versioning, with "-dev" indicating + work in progress. The intent is to more easly report + and summarize functionality when SPIRV-Tools is incorporated + in downstream projects. + + - Summary of functionality (See the README.md for more): + - Supports SPIR-V 1.1 Rev 1 + - Supports SPIR-V 1.0 Rev 5 + - Supports GLSL std450 extended instructions 1.0 Rev 3 + - Supports OpenCL extended instructions 1.0 Rev 2 + - Assembler, disassembler are complete + - Supports floating point widths of 16, 32, 64 bits + - Supports integer widths up to 64 bits + - Validator is incomplete + - Checks capability requirements in most cases + - Checks module layout constraints + - Checks ID use-definition ordering constraints, + ignoring control flow + - Checks some control flow graph rules + - Optimizer is introduced, with few available transforms. + - Supported on Linux, OSX, Android, Windows + + - Fixes bugs: + - #143: OpenCL pow and pown arguments diff --git a/engine/engine/3rdlibs/spirv-tools/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/CMakeLists.txt new file mode 100644 index 00000000..d8f828f4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/CMakeLists.txt @@ -0,0 +1,397 @@ +# Copyright (c) 2015-2023 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.22.1) + +project(spirv-tools) + +# Avoid a bug in CMake 3.22.1. By default it will set -std=c++11 for +# targets in test/*, when those tests need -std=c++17. +# https://github.com/KhronosGroup/SPIRV-Tools/issues/5340 +# The bug is fixed in CMake 3.22.2 +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.22.1") + if (${CMAKE_VERSION} VERSION_LESS "3.22.2") + cmake_policy(SET CMP0128 NEW) + endif() +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +enable_testing() +set(SPIRV_TOOLS "SPIRV-Tools") + +include(GNUInstallDirs) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Require at least C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() +if(${CMAKE_CXX_STANDARD} LESS 17) + message(FATAL_ERROR "SPIRV-Tools requires C++17 or later, but is configured for C++${CMAKE_CXX_STANDARD})") +endif() +set(CMAKE_CXX_EXTENSIONS OFF) + + +option(ENABLE_RTTI "Enables RTTI" OFF) +option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Windows") + add_definitions(-DSPIRV_WINDOWS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN") + add_definitions(-DSPIRV_WINDOWS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") + set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) +endif() + +if (${SPIRV_TIMER_ENABLED}) + add_definitions(-DSPIRV_TIMER_ENABLED) +endif() + +if ("${CMAKE_BUILD_TYPE}" STREQUAL "") + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" ${SKIP_SPIRV_TOOLS_INSTALL}) +if(NOT ${SKIP_SPIRV_TOOLS_INSTALL}) + set(ENABLE_SPIRV_TOOLS_INSTALL ON) +endif() + +option(SPIRV_BUILD_COMPRESSION "Build SPIR-V compressing codec" OFF) +if(SPIRV_BUILD_COMPRESSION) + message(FATAL_ERROR "SPIR-V compression codec has been removed from SPIR-V tools. " + "Please remove SPIRV_BUILD_COMPRESSION from your build options.") +endif(SPIRV_BUILD_COMPRESSION) + +option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) + +set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used") + +option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF) + +option(SPIRV_WERROR "Enable error on warning" ON) +if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))) + set(COMPILER_IS_LIKE_GNU TRUE) +endif() +if(${COMPILER_IS_LIKE_GNU}) + set(SPIRV_WARNINGS -Wall -Wextra -Wnon-virtual-dtor -Wno-missing-field-initializers) + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wno-self-assign) + endif() + + option(SPIRV_WARN_EVERYTHING "Enable -Weverything" ${SPIRV_WARN_EVERYTHING}) + if(${SPIRV_WARN_EVERYTHING}) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} + -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wpedantic -pedantic-errors) + else() + message(STATUS "Unknown compiler ${CMAKE_CXX_COMPILER_ID}, " + "so SPIRV_WARN_EVERYTHING has no effect") + endif() + endif() + + if(${SPIRV_WERROR}) + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) + endif() +elseif(MSVC) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819) + + if(${SPIRV_WERROR}) + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) + endif() +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) + +option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON) +if(${SPIRV_COLOR_TERMINAL}) + add_definitions(-DSPIRV_COLOR_TERMINAL) +endif() + +option(SPIRV_LOG_DEBUG "Enable excessive debug output" OFF) +if(${SPIRV_LOG_DEBUG}) + add_definitions(-DSPIRV_LOG_DEBUG) +endif() + +if (DEFINED SPIRV_TOOLS_EXTRA_DEFINITIONS) + add_definitions(${SPIRV_TOOLS_EXTRA_DEFINITIONS}) +endif() + +# Library build setting definitions: +# +# * SPIRV_TOOLS_BUILD_STATIC - ON or OFF - Defaults to ON. +# If enabled the following targets will be created: +# ${SPIRV_TOOLS}-static - STATIC library. +# Has full public symbol visibility. +# ${SPIRV_TOOLS}-shared - SHARED library. +# Has default-hidden symbol visibility. +# ${SPIRV_TOOLS} - will alias to one of above, based on BUILD_SHARED_LIBS. +# If disabled the following targets will be created: +# ${SPIRV_TOOLS} - either STATIC or SHARED based on SPIRV_TOOLS_LIBRARY_TYPE. +# Has full public symbol visibility. +# ${SPIRV_TOOLS}-shared - SHARED library. +# Has default-hidden symbol visibility. +# +# * SPIRV_TOOLS_LIBRARY_TYPE - SHARED or STATIC. +# Specifies the library type used for building SPIRV-Tools libraries. +# Defaults to SHARED when BUILD_SHARED_LIBS=1, otherwise STATIC. +# +# * SPIRV_TOOLS_FULL_VISIBILITY - "${SPIRV_TOOLS}-static" or "${SPIRV_TOOLS}" +# Evaluates to the SPIRV_TOOLS target library name that has no hidden symbols. +# This is used by internal targets for accessing symbols that are non-public. +# Note this target provides no API stability guarantees. +# +# Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909. +option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON) +option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON) +if(SPIRV_TOOLS_BUILD_STATIC) + set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static) + set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC") +else(SPIRV_TOOLS_BUILD_STATIC) + set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}) + if (NOT DEFINED SPIRV_TOOLS_LIBRARY_TYPE) + if(BUILD_SHARED_LIBS) + set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED") + else() + set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC") + endif() + endif() +endif(SPIRV_TOOLS_BUILD_STATIC) + +function(spvtools_default_compile_options TARGET) + target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS}) + + if (${COMPILER_IS_LIKE_GNU}) + target_compile_options(${TARGET} PRIVATE + -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion + -Wno-sign-conversion -fno-exceptions) + + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() + # For good call stacks in profiles, keep the frame pointers. + if(NOT "${SPIRV_PERF}" STREQUAL "") + target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer) + endif() + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(SPIRV_USE_SANITIZER "" CACHE STRING + "Use the clang sanitizer [address|memory|thread|...]") + if(NOT "${SPIRV_USE_SANITIZER}" STREQUAL "") + target_compile_options(${TARGET} PRIVATE + -fsanitize=${SPIRV_USE_SANITIZER}) + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS -fsanitize=${SPIRV_USE_SANITIZER}) + endif() + target_compile_options(${TARGET} PRIVATE + -ftemplate-depth=1024) + else() + target_compile_options(${TARGET} PRIVATE + -Wno-missing-field-initializers) + endif() + endif() + + if (MSVC) + # Specify /EHs for exception handling. This makes using SPIRV-Tools as + # dependencies in other projects easier. + if(ENABLE_EXCEPTIONS_ON_MSVC) + target_compile_options(${TARGET} PRIVATE /EHs) + endif() + endif() + + # For MinGW cross compile, statically link to the C++ runtime. + # But it still depends on MSVCRT.dll. + if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") + if (NOT MSVC) + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS -static -static-libgcc -static-libstdc++) + endif() + endif() +endfunction() + +if(NOT COMMAND find_host_package) + macro(find_host_package) + find_package(${ARGN}) + endmacro() +endif() +if(NOT COMMAND find_host_program) + macro(find_host_program) + find_program(${ARGN}) + endmacro() +endif() + +# Tests require Python3 +find_host_package(Python3 REQUIRED) + +# Check for symbol exports on Linux. +# At the moment, this check will fail on the OSX build machines for the Android NDK. +# It appears they don't have objdump. +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + macro(spvtools_check_symbol_exports TARGET) + if (NOT "${SPIRV_SKIP_TESTS}") + add_test(NAME spirv-tools-symbol-exports-${TARGET} + COMMAND Python3::Interpreter + ${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$") + endif() + endmacro() +else() + macro(spvtools_check_symbol_exports TARGET) + if (NOT "${SPIRV_SKIP_TESTS}") + message("Skipping symbol exports test for ${TARGET}") + endif() + endmacro() +endif() + +if(ENABLE_SPIRV_TOOLS_INSTALL) + if(WIN32 AND NOT MINGW) + macro(spvtools_config_package_dir TARGET PATH) + set(${PATH} ${TARGET}/cmake) + endmacro() + else() + macro(spvtools_config_package_dir TARGET PATH) + set(${PATH} ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET}) + endmacro() + endif() + + macro(spvtools_generate_config_file TARGET) + file(WRITE ${CMAKE_BINARY_DIR}/${TARGET}Config.cmake + "include(CMakeFindDependencyMacro)\n" + "find_dependency(${SPIRV_TOOLS})\n" + "include(\${CMAKE_CURRENT_LIST_DIR}/${TARGET}Targets.cmake)\n" + "set(${TARGET}_LIBRARIES ${TARGET})\n" + "get_target_property(${TARGET}_INCLUDE_DIRS ${TARGET} INTERFACE_INCLUDE_DIRECTORIES)\n") + endmacro() +endif() + +# Currently iOS and Android are very similar. +# They both have their own packaging (APP/APK). +# Which makes regular executables/testing problematic. +# +# Currently the only deliverables for these platforms are +# libraries (either STATIC or SHARED). +# +# Furthermore testing is equally problematic. +if (IOS OR ANDROID) + set(SPIRV_SKIP_EXECUTABLES ON) +endif() + +option(SPIRV_SKIP_EXECUTABLES "Skip building the executable and tests along with the library") +if (SPIRV_SKIP_EXECUTABLES) + set(SPIRV_SKIP_TESTS ON) +endif() +option(SPIRV_SKIP_TESTS "Skip building tests along with the library") + +# Defaults to ON. The checks can be time consuming. +# Turn off if they take too long. +option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON) +if (${SPIRV_CHECK_CONTEXT}) + add_compile_options($<$:-DSPIRV_CHECK_CONTEXT>) +endif() + +# Precompiled header macro. Parameters are source file list and filename for pch cpp file. +macro(spvtools_pch SRCS PCHPREFIX) + if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio" AND NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(PCH_NAME "$(IntDir)\\${PCHPREFIX}.pch") + # make source files use/depend on PCH_NAME + set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS "/Yu${PCHPREFIX}.h /FI${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_DEPENDS "${PCH_NAME}") + # make PCHPREFIX.cpp file compile and generate PCH_NAME + set_source_files_properties("${PCHPREFIX}.cpp" PROPERTIES COMPILE_FLAGS "/Yc${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_OUTPUTS "${PCH_NAME}") + list(APPEND ${SRCS} "${PCHPREFIX}.cpp") + endif() +endmacro(spvtools_pch) + +add_subdirectory(external) + +# Warning about extra semi-colons. +# +# This is not supported on all compilers/versions. so enabling only +# for clang, since that works for all versions that our bots run. +# +# This is intentionally done after adding the external subdirectory, +# so we don't enforce this flag on our dependencies, some of which do +# not pass it. +# +# If the minimum version of CMake supported is updated to 3.0 or +# later, then check_cxx_compiler_flag could be used instead. +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + add_compile_options("-Wextra-semi") +endif() + +add_subdirectory(source) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/optimizer.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/linker.hpp + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/spirv-tools/) +endif(ENABLE_SPIRV_TOOLS_INSTALL) + +if (NOT "${SPIRV_SKIP_TESTS}") + add_test(NAME spirv-tools-copyrights + COMMAND Python3::Interpreter utils/check_copyright.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link") +set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") + +# Build pkg-config file +# Use a first-class target so it's regenerated when relevant files are updated. +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc + COMMAND ${CMAKE_COMMAND} + -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES + -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in + -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc + -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} + -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} + -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} + -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake + DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake") +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc + COMMAND ${CMAKE_COMMAND} + -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES + -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in + -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc + -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} + -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} + -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} + -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake + DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake") +add_custom_target(spirv-tools-pkg-config + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc) + +# Install pkg-config file +if (ENABLE_SPIRV_TOOLS_INSTALL) + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc + ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() diff --git a/engine/engine/3rdlibs/spirv-tools/README.md b/engine/engine/3rdlibs/spirv-tools/README.md new file mode 100644 index 00000000..7bf8a51f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/README.md @@ -0,0 +1,806 @@ +# SPIR-V Tools +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/KhronosGroup/SPIRV-Tools/badge)](https://securityscorecards.dev/viewer/?uri=github.com/KhronosGroup/SPIRV-Tools) + +NEWS 2023-01-11: Development occurs on the `main` branch. + +## Overview + +The SPIR-V Tools project provides an API and commands for processing SPIR-V +modules. + +The project includes an assembler, binary module parser, disassembler, +validator, and optimizer for SPIR-V. Except for the optimizer, all are based +on a common static library. The library contains all of the implementation +details, and is used in the standalone tools whilst also enabling integration +into other code bases directly. The optimizer implementation resides in its +own library, which depends on the core library. + +The interfaces have stabilized: +We don't anticipate making a breaking change for existing features. + +SPIR-V is defined by the Khronos Group Inc. +See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification, +headers, and XML registry. + +## Downloads + +The official releases for SPIRV-Tools can be found on LunarG's +[SDK download page](https://vulkan.lunarg.com/sdk/home). + +For convenience, here are also links to the latest builds (HEAD). +Those are untested automated builds. Those are not official releases, nor +are guaranteed to work. Official releases builds are in the Vulkan SDK. + +Linux[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) +MacOS[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) +Windows[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_release.html) + +[More downloads](docs/downloads.md) + +## Versioning SPIRV-Tools + +See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version. + +SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with +an optional `-dev` suffix to indicate work in progress. For example, the +following versions are ordered from oldest to newest: + +* `v2016.0` +* `v2016.1-dev` +* `v2016.1` +* `v2016.2-dev` +* `v2016.2` + +Use the `--version` option on each command line tool to see the software +version. An API call reports the software version as a C-style string. + +## Releases + +The official releases for SPIRV-Tools can be found on LunarG's +[SDK download page](https://vulkan.lunarg.com/sdk/home). + +You can find either the prebuilt, and QA tested binaries, or download the +SDK Config, which lists the commits to use to build the release from scratch. + +GitHub releases are deprecated, and we will not publish new releases until +further notice. + +## Supported features + +### Assembler, binary parser, and disassembler + +* Support for SPIR-V 1.0, through 1.5 + * Based on SPIR-V syntax described by JSON grammar files in the + [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository. + * Usually, support for a new version of SPIR-V is ready within days after + publication. +* Support for extended instruction sets: + * GLSL std450 version 1.0 Rev 3 + * OpenCL version 1.0 Rev 2 +* Assembler only does basic syntax checking. No cross validation of + IDs or types is performed, except to check literal arguments to + `OpConstant`, `OpSpecConstant`, and `OpSwitch`. +* Where tools expect binary input, a hex stream may be provided instead. See + `spirv-dis --help`. + +See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax. + +### Validator + +The validator checks validation rules described by the SPIR-V specification. + +Khronos recommends that tools that create or transform SPIR-V modules use the +validator to ensure their outputs are valid, and that tools that consume SPIR-V +modules optionally use the validator to protect themselves from bad inputs. +This is especially encouraged for debug and development scenarios. + +The validator has one-sided error: it will only return an error when it has +implemented a rule check and the module violates that rule. + +The validator is incomplete. +See the [CHANGES](CHANGES) file for reports on completed work, and +the [Validator +sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/1) for planned +and in-progress work. + +*Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec. +The validator will fail on a module that exceeds those minimum upper bound limits. +The validator has been parameterized to allow larger values, for use when targeting +a more-than-minimally-capable SPIR-V consumer. + +See [`tools/val/val.cpp`](tools/val/val.cpp) or run `spirv-val --help` for the command-line help. + +### Optimizer + +The optimizer is a collection of code transforms, or "passes". +Transforms are written for a diverse set of reasons: + +* To restructure, simplify, or normalize the code for further processing. +* To eliminate undesirable code. +* To improve code quality in some metric such as size or performance. + **Note**: These transforms are not guaranteed to actually improve any + given metric. Users should always measure results for their own situation. + +As of this writing, there are 67 transforms including examples such as: +* Simplification + * Strip debug info + * Strip reflection info +* Specialization Constants + * Set spec constant default value + * Freeze spec constant to default value + * Fold `OpSpecConstantOp` and `OpSpecConstantComposite` + * Unify constants + * Eliminate dead constant +* Code Reduction + * Inline all function calls exhaustively + * Convert local access chains to inserts/extracts + * Eliminate local load/store in single block + * Eliminate local load/store with single store + * Eliminate local load/store with multiple stores + * Eliminate local extract from insert + * Eliminate dead instructions (aggressive) + * Eliminate dead branches + * Merge single successor / single predecessor block pairs + * Eliminate common uniform loads + * Remove duplicates: Capabilities, extended instruction imports, types, and + decorations. +* Normalization + * Compact IDs + * CFG cleanup + * Flatten decorations + * Merge returns + * Convert AMD-specific instructions to KHR instructions +* Code improvement + * Conditional constant propagation + * If-conversion + * Loop fission + * Loop fusion + * Loop-invariant code motion + * Loop unroll +* Other + * Graphics robust access + * Upgrade memory model to VulkanKHR + +Additionally, certain sets of transformations have been packaged into +higher-level recipes. These include: + +* Optimization for size (`spirv-opt -Os`) +* Optimization for performance (`spirv-opt -O`) + +For the latest list with detailed documentation, please refer to +[`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp). + +For suggestions on using the code reduction options, please refer to this [white paper](https://www.lunarg.com/shader-compiler-technologies/white-paper-spirv-opt/). + + +### Linker + +*Note:* The linker is still under development. + +Current features: +* Combine multiple SPIR-V binary modules together. +* Combine into a library (exports are retained) or an executable (no symbols + are exported). + +See the [CHANGES](CHANGES) file for reports on completed work, and the [General +sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for +planned and in-progress work. + + +### Reducer + +*Note:* The reducer is still under development. + +The reducer simplifies and shrinks a SPIR-V module with respect to a +user-supplied *interestingness function*. For example, given a large +SPIR-V module that cause some SPIR-V compiler to fail with a given +fatal error message, the reducer could be used to look for a smaller +version of the module that causes the compiler to fail with the same +fatal error message. + +To suggest an additional capability for the reducer, [file an +issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with +"Reducer:" as the start of its title. + + +### Fuzzer + +*Note:* The fuzzer is still under development. + +The fuzzer applies semantics-preserving transformations to a SPIR-V binary +module, to produce an equivalent module. The original and transformed modules +should produce essentially identical results when executed on identical inputs: +their results should differ only due to floating-point round-off, if at all. +Significant differences in results can pinpoint bugs in tools that process +SPIR-V binaries, such as miscompilations. This *metamorphic testing* approach +is similar to the method used by the [GraphicsFuzz +project](https://github.com/google/graphicsfuzz) for fuzzing of GLSL shaders. + +To suggest an additional capability for the fuzzer, [file an +issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with +"Fuzzer:" as the start of its title. + + +### Diff + +*Note:* The diff tool is still under development. + +The diff tool takes two SPIR-V files, either in binary or text format and +produces a diff-style comparison between the two. The instructions between the +src and dst modules are matched as best as the tool can, and output is produced +(in src id-space) that shows which instructions are removed in src, added in dst +or modified between them. The order of instructions are not retained. + +Matching instructions between two SPIR-V modules is not trivial, and thus a +number of heuristics are applied in this tool. In particular, without debug +information, match functions is nontrivial as they can be reordered. As such, +this tool is primarily useful to produce the diff of two SPIR-V modules derived +from the same source, for example before and after a modification to the shader, +before and after a transformation, or SPIR-V produced from different tools. + + +### Extras + +* [Utility filters](#utility-filters) +* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`. + Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax + highlighting in Vim. This build target is not built by default. + +## Contributing + +The SPIR-V Tools project is maintained by members of the The Khronos Group Inc., +and is hosted at https://github.com/KhronosGroup/SPIRV-Tools. + +Consider joining the `public_spirv_tools_dev@khronos.org` mailing list, via +[https://www.khronos.org/spir/spirv-tools-mailing-list/](https://www.khronos.org/spir/spirv-tools-mailing-list/). +The mailing list is used to discuss development plans for the SPIRV-Tools as an open source project. +Once discussion is resolved, +specific work is tracked via issues and sometimes in one of the +[projects][spirv-tools-projects]. + +(To provide feedback on the SPIR-V _specification_, file an issue on the +[SPIRV-Headers][spirv-headers] GitHub repository.) + +See [`docs/projects.md`](docs/projects.md) to see how we use the +[GitHub Project +feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/) +to organize planned and in-progress work. + +Contributions via merge request are welcome. Changes should: +* Be provided under the [Apache 2.0](#license). +* You'll be prompted with a one-time "click-through" + [Khronos Open Source Contributor License Agreement][spirv-tools-cla] + (CLA) dialog as part of submitting your pull request or + other contribution to GitHub. +* Include tests to cover updated functionality. +* C++ code should follow the [Google C++ Style Guide][cpp-style-guide]. +* Code should be formatted with `clang-format`. + [kokoro/check-format/build.sh](kokoro/check-format/build.sh) + shows how to download it. Note that we currently use + `clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by + the included [.clang-format](.clang-format) file. + +We intend to maintain a linear history on the GitHub `main` branch. + +### Getting the source + +Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: + + git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + cd spirv-tools + + # Check out sources for dependencies, at versions known to work together, + # as listed in the DEPS file. + python3 utils/git-sync-deps + +For some kinds of development, you may need the latest sources from the third-party projects: + + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + git clone https://github.com/abseil/abseil-cpp.git spirv-tools/external/abseil_cpp + +#### Dependency on Effcee + +Some tests depend on the [Effcee][effcee] library for stateful matching. +Effcee itself depends on [RE2][re2], and RE2 depends on [Abseil][abseil-cpp]. + +* If SPIRV-Tools is configured as part of a larger project that already uses + Effcee, then that project should include Effcee before SPIRV-Tools. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee`, + RE2 sources to appear in `external/re2`, and Abseil sources to appear in + `external/abseil_cpp`. + +### Source code organization + +* `example`: demo code of using SPIRV-Tools APIs +* `external/googletest`: Intended location for the + [googletest][googletest] sources, not provided +* `external/effcee`: Location of [Effcee][effcee] sources, if the `effcee` library + is not already configured by an enclosing project. +* `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already + configured by an enclosing project. + (The Effcee project already requires RE2.) +* `external/abseil_cpp`: Location of [Abseil][abseil-cpp] sources, if Abseil is + not already configured by an enclosing project. + (The RE2 project already requires Abseil.) +* `include/`: API clients should add this directory to the include search path +* `external/spirv-headers`: Intended location for + [SPIR-V headers][spirv-headers], not provided +* `include/spirv-tools/libspirv.h`: C API public interface +* `source/`: API implementation +* `test/`: Tests, using the [googletest][googletest] framework +* `tools/`: Command line executables + +### Tests + +The project contains a number of tests, used to drive development +and ensure correctness. The tests are written using the +[googletest][googletest] framework. The `googletest` +source is not provided with this project. There are two ways to enable +tests: +* If SPIR-V Tools is configured as part of an enclosing project, then the + enclosing project should configure `googletest` before configuring SPIR-V Tools. +* If SPIR-V Tools is configured as a standalone project, then download the + `googletest` source into the `/external/googletest` directory before + configuring and building the project. + +## Build + +*Note*: Prebuilt binaries are available from the [downloads](docs/downloads.md) page. + +First [get the sources](#getting-the-source). +Then build using CMake, Bazel, Android ndk-build, or the Emscripten SDK. + +### Build using CMake +You can build the project using [CMake][cmake]: + +```sh +cd +mkdir build && cd build +cmake [-G ] +``` + +Once the build files have been generated, build using the appropriate build +command (e.g. `ninja`, `make`, `msbuild`, etc.; this depends on the platform +generator used above), or use your IDE, or use CMake to run the appropriate build +command for you: + +```sh +cmake --build . [--config Debug] # runs `make` or `ninja` or `msbuild` etc. +``` + +#### Note about the fuzzer + +The SPIR-V fuzzer, `spirv-fuzz`, can only be built via CMake, and is disabled by +default. To build it, clone protobuf and use the `SPIRV_BUILD_FUZZER` CMake +option, like so: + +```sh +# In (the SPIRV-Tools repo root): +git clone --depth=1 --branch v3.13.0.1 https://github.com/protocolbuffers/protobuf external/protobuf + +# In your build directory: +cmake [-G ] -DSPIRV_BUILD_FUZZER=ON +cmake --build . --config Debug +``` + +You can also add `-DSPIRV_ENABLE_LONG_FUZZER_TESTS=ON` to build additional +fuzzer tests. + + +### Build using Bazel +You can also use [Bazel](https://bazel.build/) to build the project. + +```sh +bazel build :all +``` + +### Build a node.js package using Emscripten + +The SPIRV-Tools core library can be built to a WebAssembly [node.js](https://nodejs.org) +module. The resulting `SpirvTools` WebAssembly module only exports methods to +assemble and disassemble SPIR-V modules. + +First, make sure you have the [Emscripten SDK](https://emscripten.org). +Then: + +```sh +cd +./source/wasm/build.sh +``` + +The resulting node package, with JavaScript and TypeScript bindings, is +written to `/out/web`. + +Note: This builds the package locally. It does *not* publish it to [npm](https://npmjs.org). + +To test the result: + +```sh +node ./test/wasm/test.js +``` + +### Tools you'll need + +For building and testing SPIRV-Tools, the following tools should be +installed regardless of your OS: + +- [CMake](http://www.cmake.org/): if using CMake for generating compilation +targets, you need to install CMake Version 2.8.12 or later. +- [Python 3](http://www.python.org/): for utility scripts and running the test +suite. +- [Bazel](https://bazel.build/) (optional): if building the source with Bazel, +you need to install Bazel Version 7.0.2 on your machine. Other versions may +also work, but are not verified. +- [Emscripten SDK](https://emscripten.org) (optional): if building the + WebAssembly module. + +SPIRV-Tools is regularly tested with the following compilers: + +On Linux +- GCC version 9.4 +- Clang version 10.0 + +On MacOS +- AppleClang 15.0 + +On Windows +- Visual Studio 2019 +- Visual Studio 2022 + +Note: Other compilers or later versions may work, but they are not tested. + +### CMake options + +The following CMake options are supported: + +* `SPIRV_BUILD_FUZZER={ON|OFF}`, default `OFF` - Build the spirv-fuzz tool. +* `SPIRV_COLOR_TERMINAL={ON|OFF}`, default `ON` - Enables color console output. +* `SPIRV_SKIP_TESTS={ON|OFF}`, default `OFF`- Build only the library and + the command line tools. This will prevent the tests from being built. +* `SPIRV_SKIP_EXECUTABLES={ON|OFF}`, default `OFF`- Build only the library, not + the command line tools and tests. +* `SPIRV_USE_SANITIZER=`, default is no sanitizing - On UNIX + platforms with an appropriate version of `clang` this option enables the use + of the sanitizers documented [here][clang-sanitizers]. + This should only be used with a debug build. +* `SPIRV_WARN_EVERYTHING={ON|OFF}`, default `OFF` - On UNIX platforms enable + more strict warnings. The code might not compile with this option enabled. + For Clang, enables `-Weverything`. For GCC, enables `-Wpedantic`. + See [`CMakeLists.txt`](CMakeLists.txt) for details. +* `SPIRV_WERROR={ON|OFF}`, default `ON` - Forces a compilation error on any + warnings encountered by enabling the compiler-specific compiler front-end + option. No compiler front-end options are enabled when this option is OFF. + +Additionally, you can pass additional C preprocessor definitions to SPIRV-Tools +via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to +`/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and +iterator debugging. + +### Android ndk-build + +SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and +`libSPIRV-Tools-opt.a` for Android. Using the Android NDK r25c or later: + +``` +cd + +export ANDROID_NDK=/path/to/your/ndk # NDK r25c or later + +mkdir build && cd build +mkdir libs +mkdir app + +$ANDROID_NDK/ndk-build -C ../android_test \ + NDK_PROJECT_PATH=. \ + NDK_LIBS_OUT=`pwd`/libs \ + NDK_APP_OUT=`pwd`/app +``` + +### Updating DEPS + +Occasionally the entries in [DEPS](DEPS) will need to be updated. This is done on +demand when there is a request to do this, often due to downstream breakages. +To update `DEPS`, run `utils/roll_deps.sh` and confirm that tests pass. +The script requires Chromium's +[`depot_tools`](https://chromium.googlesource.com/chromium/tools/depot_tools). + +## Library + +### Usage + +The internals of the library use C++17 features, and are exposed via both a C +and C++ API. + +In order to use the library from an application, the include path should point +to `/include`, which will enable the application to include the +header `/include/spirv-tools/libspirv.h{|pp}` then linking against +the static library in `/source/libSPIRV-Tools.a` or +`/source/SPIRV-Tools.lib`. +For optimization, the header file is +`/include/spirv-tools/optimizer.hpp`, and the static library is +`/source/libSPIRV-Tools-opt.a` or +`/source/SPIRV-Tools-opt.lib`. + +* `SPIRV-Tools` CMake target: Creates the static library: + * `/source/libSPIRV-Tools.a` on Linux and OS X. + * `/source/libSPIRV-Tools.lib` on Windows. +* `SPIRV-Tools-opt` CMake target: Creates the static library: + * `/source/libSPIRV-Tools-opt.a` on Linux and OS X. + * `/source/libSPIRV-Tools-opt.lib` on Windows. + +#### Entry points + +The interfaces are still under development, and are expected to change. + +There are five main entry points into the library in the C interface: + +* `spvTextToBinary`: An assembler, translating text to a binary SPIR-V module. +* `spvBinaryToText`: A disassembler, translating a binary SPIR-V module to + text. +* `spvBinaryParse`: The entry point to a binary parser API. It issues callbacks + for the header and each parsed instruction. The disassembler is implemented + as a client of `spvBinaryParse`. +* `spvValidate` implements the validator functionality. *Incomplete* +* `spvValidateBinary` implements the validator functionality. *Incomplete* + +The C++ interface is comprised of three classes, `SpirvTools`, `Optimizer` and +`Linker`, all in the `spvtools` namespace. +* `SpirvTools` provides `Assemble`, `Disassemble`, and `Validate` methods. +* `Optimizer` provides methods for registering and running optimization passes. +* `Linker` provides methods for combining together multiple binaries. + +## Command line tools + +Command line tools, which wrap the above library functions, are provided to +assemble or disassemble shader files. It's a convention to name SPIR-V +assembly and binary files with suffix `.spvasm` and `.spv`, respectively. + +### Assembler tool + +The assembler reads the assembly language text, and emits the binary form. + +The standalone assembler is the executable called `spirv-as`, and is located in +`/tools/spirv-as`. The functionality of the assembler is implemented +by the `spvTextToBinary` library function. + +* `spirv-as` - the standalone assembler + * `/tools/as` + +Use option `-h` to print help. + +### Disassembler tool + +The disassembler reads the binary form, and emits assembly language text. + +The standalone disassembler is the executable called `spirv-dis`, and is located in +`/tools/spirv-dis`. The functionality of the disassembler is implemented +by the `spvBinaryToText` library function. + +* `spirv-dis` - the standalone disassembler + * `/tools/dis` + +Use option `-h` to print help. + +The output includes syntax colouring when printing to the standard output stream, +on Linux, Windows, and OS X. + +### Linker tool + +The linker combines multiple SPIR-V binary modules together, resulting in a single +binary module as output. + +This is a work in progress. +The linker does not support OpenCL program linking options related to math +flags. (See section 5.6.5.2 in OpenCL 1.2) + +* `spirv-link` - the standalone linker + * `/tools/link` + +### Optimizer tool + +The optimizer processes a SPIR-V binary module, applying transformations +in the specified order. + +This is a work in progress, with initially only few available transformations. + +* `spirv-opt` - the standalone optimizer + * `/tools/opt` + +### Validator tool + +*Warning:* This functionality is under development, and is incomplete. + +The standalone validator is the executable called `spirv-val`, and is located in +`/tools/spirv-val`. The functionality of the validator is implemented +by the `spvValidate` library function. + +The validator operates on the binary form. + +* `spirv-val` - the standalone validator + * `/tools/val` + +### Reducer tool + +The reducer shrinks a SPIR-V binary module, guided by a user-supplied +*interestingness test*. + +This is a work in progress, with initially only shrinks a module in a few ways. + +* `spirv-reduce` - the standalone reducer + * `/tools/reduce` + +Run `spirv-reduce --help` to see how to specify interestingness. + +### Fuzzer tool + +The fuzzer transforms a SPIR-V binary module into a semantically-equivalent +SPIR-V binary module by applying transformations in a randomized fashion. + +This is a work in progress, with initially only a few semantics-preserving +transformations. + +* `spirv-fuzz` - the standalone fuzzer + * `/tools/fuzz` + +Run `spirv-fuzz --help` for a detailed list of options. + +### Control flow dumper tool + +The control flow dumper prints the control flow graph for a SPIR-V module as a +[GraphViz](http://www.graphviz.org/) graph. + +This is experimental. + +* `spirv-cfg` - the control flow graph dumper + * `/tools/cfg` + +### Diff tool + +*Warning:* This functionality is under development, and is incomplete. + +The diff tool produces a diff-style comparison between two SPIR-V modules. + +* `spirv-diff` - the standalone diff tool + * ``/tools/diff` + +### Utility filters + +* `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the + `less` program, on compatible systems. For example, set the `LESSOPEN` + environment variable as follows, assuming both `spirv-lesspipe.sh` and + `spirv-dis` are on your executable search path: + ``` + export LESSOPEN='| spirv-lesspipe.sh "%s"' + ``` + Then you page through a disassembled module as follows: + ``` + less foo.spv + ``` + * The `spirv-lesspipe.sh` script will pass through any extra arguments to + `spirv-dis`. So, for example, you can turn off colours and friendly ID + naming as follows: + ``` + export LESSOPEN='| spirv-lesspipe.sh "%s" --no-color --raw-id' + ``` + +* [vim-spirv](https://github.com/kbenzie/vim-spirv) - A vim plugin which + supports automatic disassembly of `.spv` files using the `:edit` command and + assembly using the `:write` command. The plugin also provides additional + features which include; syntax highlighting; highlighting of all ID's matching + the ID under the cursor; and highlighting errors where the `Instruction` + operand of `OpExtInst` is used without an appropriate `OpExtInstImport`. + +* `50spirv-tools.el` - Automatically disassembles '.spv' binary files when + loaded into the emacs text editor, and re-assembles them when saved, + provided any modifications to the file are valid. This functionality + must be explicitly requested by defining the symbol + SPIRV_TOOLS_INSTALL_EMACS_HELPERS as follows: + ``` + cmake -DSPIRV_TOOLS_INSTALL_EMACS_HELPERS=true ... + ``` + + In addition, this helper is only installed if the directory /etc/emacs/site-start.d + exists, which is typically true if emacs is installed on the system. + + Note that symbol IDs are not currently preserved through a load/edit/save operation. + This may change if the ability is added to spirv-as. + + +### Tests + +Tests are only built when googletest is found. + +#### Running test with CMake + +Use `ctest -j ` to run all the tests. To run tests using all threads: +```shell +ctest -j$(nproc) +``` + +To run a single test target, use `ctest [-j ] -R `. For example, +you can run all `opt` tests with: +```shell +ctest -R 'spirv-tools-test_opt' +``` + +#### Running test with Bazel + +Use `bazel test :all` to run all tests. This will run tests in parallel by default. + +To run a single test target, specify `:my_test_target` instead of `:all`. Test target +names get printed when you run `bazel test :all`. For example, you can run +`opt_def_use_test` with: + +on linux: +```shell +bazel test --cxxopt=-std=c++17 :opt_def_use_test +``` + +on windows: +```shell +bazel test --cxxopt=/std:c++17 :opt_def_use_test +``` + +## Future Work + + +_See the [projects pages](https://github.com/KhronosGroup/SPIRV-Tools/projects) +for more information._ + +### Assembler and disassembler + +* The disassembler could emit helpful annotations in comments. For example: + * Use variable name information from debug instructions to annotate + key operations on variables. + * Show control flow information by annotating `OpLabel` instructions with + that basic block's predecessors. +* Error messages could be improved. + +### Validator + +This is a work in progress. + +### Linker + +* The linker could accept math transformations such as allowing MADs, or other + math flags passed at linking-time in OpenCL. +* Linkage attributes can not be applied through a group. +* Check decorations of linked functions attributes. +* Remove dead instructions, such as OpName targeting imported symbols. + +## Licence + +Full license terms are in [LICENSE](LICENSE) +``` +Copyright (c) 2015-2016 The Khronos Group Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +[spirv-tools-cla]: https://cla-assistant.io/KhronosGroup/SPIRV-Tools +[spirv-tools-projects]: https://github.com/KhronosGroup/SPIRV-Tools/projects +[spirv-tools-mailing-list]: https://www.khronos.org/spir/spirv-tools-mailing-list +[spirv-registry]: https://www.khronos.org/registry/spir-v/ +[spirv-headers]: https://github.com/KhronosGroup/SPIRV-Headers +[googletest]: https://github.com/google/googletest +[googletest-pull-612]: https://github.com/google/googletest/pull/612 +[googletest-issue-610]: https://github.com/google/googletest/issues/610 +[effcee]: https://github.com/google/effcee +[re2]: https://github.com/google/re2 +[abseil-cpp]: https://github.com/abseil/abseil-cpp +[CMake]: https://cmake.org/ +[cpp-style-guide]: https://google.github.io/styleguide/cppguide.html +[clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation diff --git a/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools-shared.pc.in b/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools-shared.pc.in new file mode 100644 index 00000000..0dcaa276 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools-shared.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: SPIRV-Tools +Description: Tools for SPIR-V +Version: @CURRENT_VERSION@ +URL: https://github.com/KhronosGroup/SPIRV-Tools + +Libs: -L${libdir} @SPIRV_SHARED_LIBRARIES@ +Cflags: -I${includedir} diff --git a/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools.pc.in b/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools.pc.in new file mode 100644 index 00000000..2984dc57 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/cmake/SPIRV-Tools.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: SPIRV-Tools +Description: Tools for SPIR-V +Version: @CURRENT_VERSION@ +URL: https://github.com/KhronosGroup/SPIRV-Tools + +Libs: -L${libdir} @SPIRV_LIBRARIES@ +Cflags: -I${includedir} diff --git a/engine/engine/3rdlibs/spirv-tools/cmake/write_pkg_config.cmake b/engine/engine/3rdlibs/spirv-tools/cmake/write_pkg_config.cmake new file mode 100644 index 00000000..d367ce3e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/cmake/write_pkg_config.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Pierre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# First, retrieve the current version from CHANGES +file(STRINGS ${CHANGES_FILE} CHANGES_CONTENT) +string( +REGEX + MATCH "v[0-9]+(.[0-9]+)?(-dev)? [0-9]+-[0-9]+-[0-9]+" + FIRST_VERSION_LINE + ${CHANGES_CONTENT}) +string( +REGEX + REPLACE "^v([^ ]+) .+$" "\\1" + CURRENT_VERSION + "${FIRST_VERSION_LINE}") +# If this is a development version, replace "-dev" by ".0" as pkg-config nor +# CMake support "-dev" in the version. +# If it's not a "-dev" version then ensure it ends with ".1" +string(REGEX REPLACE "-dev.1" ".0" CURRENT_VERSION "${CURRENT_VERSION}.1") +configure_file(${TEMPLATE_FILE} ${OUT_FILE} @ONLY) diff --git a/engine/engine/3rdlibs/spirv-tools/external/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/external/CMakeLists.txt new file mode 100644 index 00000000..ff9998fa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/CMakeLists.txt @@ -0,0 +1,182 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Utility functions for pushing & popping variables. +function(push_variable var val) + set("${var}_SAVE_STACK" "${${var}}" "${${var}_SAVE_STACK}" PARENT_SCOPE) + set(${var} ${val} PARENT_SCOPE) +endfunction() +function(pop_variable var) + set(save_stack "${${var}_SAVE_STACK}") + list(GET save_stack 0 val) + list(REMOVE_AT save_stack 0) + set("${var}_SAVE_STACK" "${save_stack}" PARENT_SCOPE) + set(${var} ${val} PARENT_SCOPE) +endfunction() + +set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers) + +if (IS_DIRECTORY ${SPIRV_HEADER_DIR}) + # TODO(dneto): We should not be modifying the parent scope. + set(SPIRV_HEADER_INCLUDE_DIR ${SPIRV_HEADER_DIR}/include PARENT_SCOPE) + + # Add SPIRV-Headers as a sub-project if it isn't already defined. + # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find + # headers to include. + if (NOT DEFINED SPIRV-Headers_SOURCE_DIR) + add_subdirectory(${SPIRV_HEADER_DIR}) + endif() +else() + message(FATAL_ERROR + "SPIRV-Headers was not found - please checkout a copy at external/spirv-headers.") +endif() + +if (NOT ${SPIRV_SKIP_TESTS}) + # Find gmock if we can. If it's not already configured, then try finding + # it in external/googletest. + if (TARGET gmock) + message(STATUS "Google Mock already configured") + else() + if (NOT GMOCK_DIR) + set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest) + endif() + if(EXISTS ${GMOCK_DIR}) + if(MSVC) + # Our tests use ::testing::Combine. Work around a compiler + # detection problem in googletest, where that template is + # accidentally disabled for VS 2017. + # See https://github.com/google/googletest/issues/1352 + add_definitions(-DGTEST_HAS_COMBINE=1) + endif() + if(WIN32) + option(gtest_force_shared_crt + "Use shared (DLL) run-time lib even when Google Test is built as static lib." + ON) + endif() + # gtest requires special defines for building as a shared + # library, simply always build as static. + push_variable(BUILD_SHARED_LIBS 0) + add_subdirectory(${GMOCK_DIR} ${CMAKE_CURRENT_BINARY_DIR}/googletest EXCLUDE_FROM_ALL) + pop_variable(BUILD_SHARED_LIBS) + endif() + endif() + if (TARGET gmock) + set(GTEST_TARGETS + gtest + gtest_main + gmock + gmock_main + ) + foreach(target ${GTEST_TARGETS}) + set_property(TARGET ${target} PROPERTY FOLDER GoogleTest) + endforeach() + endif() + + # Find Effcee and RE2, for testing. + + # RE2 depends on Abseil. We set absl_SOURCE_DIR if it is not already set, so + # that effcee can find abseil. + if(NOT TARGET absl::base) + if (NOT absl_SOURCE_DIR) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp) + set(absl_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/abseil_cpp" CACHE STRING "Abseil source dir" ) + endif() + endif() + endif() + + # First find RE2, since Effcee depends on it. + # If already configured, then use that. Otherwise, prefer to find it under 're2' + # in this directory. + if (NOT TARGET re2) + + + # If we are configuring RE2, then turn off its testing. It takes a long time and + # does not add much value for us. If an enclosing project configured RE2, then it + # has already chosen whether to enable RE2 testing. + set(RE2_BUILD_TESTING OFF CACHE STRING "Run RE2 Tests") + if (NOT RE2_SOURCE_DIR) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/re2) + set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" ) + endif() + endif() + endif() + + if (NOT TARGET effcee) + # Expect to find effcee in this directory. + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/effcee) + # If we're configuring RE2 (via Effcee), then turn off RE2 testing. + if (NOT TARGET re2) + set(RE2_BUILD_TESTING OFF) + endif() + if (MSVC) + # SPIRV-Tools uses the shared CRT with MSVC. Tell Effcee to do the same. + set(EFFCEE_ENABLE_SHARED_CRT ON) + endif() + set(EFFCEE_BUILD_SAMPLES OFF CACHE BOOL "Do not build Effcee examples") + if (NOT TARGET effcee) + set(EFFCEE_BUILD_TESTING OFF CACHE BOOL "Do not build Effcee test suite") + endif() + push_variable(BUILD_SHARED_LIBS 0) # effcee does not export any symbols for building as a DLL. Always build as static. + add_subdirectory(effcee EXCLUDE_FROM_ALL) + pop_variable(BUILD_SHARED_LIBS) + set_property(TARGET effcee PROPERTY FOLDER Effcee) + # Turn off warnings for effcee and re2 + set_property(TARGET effcee APPEND PROPERTY COMPILE_OPTIONS -w) + set_property(TARGET re2 APPEND PROPERTY COMPILE_OPTIONS -w) + endif() + endif() +endif() + +if(SPIRV_BUILD_FUZZER) + + function(backup_compile_options) + get_property( + SPIRV_TOOLS_BACKUP_EXTERNAL_COMPILE_OPTIONS + DIRECTORY + PROPERTY COMPILE_OPTIONS + ) + endfunction() + + function(restore_compile_options) + set_property( + DIRECTORY + PROPERTY COMPILE_OPTIONS + ${SPIRV_TOOLS_BACKUP_EXTERNAL_COMPILE_OPTIONS} + ) + endfunction() + + if(NOT TARGET protobuf::libprotobuf OR NOT TARGET protobuf::protoc) + + set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf) + if (NOT IS_DIRECTORY ${SPIRV_TOOLS_PROTOBUF_DIR}) + message( + FATAL_ERROR + "protobuf not found - please checkout a copy under external/.") + endif() + set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests") + set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime") + + backup_compile_options() + + if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang) + add_compile_options(-Wno-inconsistent-missing-override) + endif() + + add_subdirectory(${SPIRV_TOOLS_PROTOBUF_DIR} EXCLUDE_FROM_ALL) + + restore_compile_options() + + endif() +endif() diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/CMakeLists.txt new file mode 100644 index 00000000..957b9224 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/CMakeLists.txt @@ -0,0 +1,67 @@ +# Copyright (c) 2015-2024 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and/or associated documentation files (the +# "Materials"), to deal in the Materials without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Materials, and to +# permit persons to whom the Materials are furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +# https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +cmake_minimum_required(VERSION 3.14) +project(SPIRV-Headers LANGUAGES C CXX VERSION 1.5.5) + +if (CMAKE_VERSION VERSION_LESS "3.21") + # https://cmake.org/cmake/help/latest/variable/PROJECT_IS_TOP_LEVEL.html + string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} PROJECT_IS_TOP_LEVEL) +endif() + +add_library(SPIRV-Headers INTERFACE) +add_library(SPIRV-Headers::SPIRV-Headers ALIAS SPIRV-Headers) +target_include_directories(SPIRV-Headers INTERFACE $) + +option(SPIRV_HEADERS_ENABLE_TESTS "Test SPIRV-Headers" ${PROJECT_IS_TOP_LEVEL}) +option(SPIRV_HEADERS_ENABLE_INSTALL "Install SPIRV-Headers" ${PROJECT_IS_TOP_LEVEL}) + +if(SPIRV_HEADERS_ENABLE_TESTS) + add_subdirectory(tests) +endif() + +if(SPIRV_HEADERS_ENABLE_INSTALL) + include(GNUInstallDirs) + include(CMakePackageConfigHelpers) + + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + set(cmake_install_dir "${CMAKE_INSTALL_DATADIR}/cmake/SPIRV-Headers") + set(version_config "${CMAKE_CURRENT_BINARY_DIR}/generated/SPIRV-HeadersConfigVersion.cmake") + + write_basic_package_version_file("${version_config}" COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT) + install(FILES "${version_config}" DESTINATION "${cmake_install_dir}") + + install(TARGETS SPIRV-Headers EXPORT "SPIRV-HeadersConfig" INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(EXPORT "SPIRV-HeadersConfig" NAMESPACE "SPIRV-Headers::" DESTINATION "${cmake_install_dir}") + + if (IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR}) + set(SPIRV_HEADERS_PKGCONFIG_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}) + else() + set(SPIRV_HEADERS_PKGCONFIG_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}) + endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Headers.pc.in ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Headers.pc @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Headers.pc" DESTINATION ${CMAKE_INSTALL_DATADIR}/pkgconfig) +endif() diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/LICENSE b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/LICENSE new file mode 100644 index 00000000..a02ace17 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/LICENSE @@ -0,0 +1,103 @@ +Files: All files except for those called out below. +Copyright (c) 2015-2024 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +--- + +Files: include/spirv/spir-v.xml +Copyright (c) 2015-2024 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +--- + +Files: tools/buildHeaders/jsoncpp/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== + +--- diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/README.md b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/README.md new file mode 100644 index 00000000..ed388285 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/README.md @@ -0,0 +1,226 @@ +# SPIR-V Headers + +This repository contains machine-readable files for the +[SPIR-V Registry](https://www.khronos.org/registry/spir-v/). +This includes: + +* Header files for various languages. +* JSON files describing the grammar for the SPIR-V core instruction set + and the extended instruction sets. +* The XML registry file. +* A tool to build the headers from the JSON grammar. + +Headers are provided in the [include](include) directory, with up-to-date +headers in the `unified1` subdirectory. Older headers are provided according to +their version. + +In contrast, the XML registry file has a linear history, so it is +not tied to SPIR-V specification versions. + +## How is this repository updated? + +When a new version or revision of the SPIR-V specification is published, +the SPIR-V Working Group will push new commits onto master, updating +the files under [include](include). + +[The SPIR-V XML registry file](include/spirv/spir-v.xml) +is updated by Khronos whenever a new enum range is allocated. + +Pull requests can be made to +- request allocation of new enum ranges in the XML registry file +- register a new magic number for a SPIR-V generator +- reserve specific tokens in the JSON grammar + +### Registering a SPIR-V Generator Magic Number + +Tools that generate SPIR-V should use a magic number in the SPIR-V to help identify the +generator. + +Care should be taken to follow existing precedent in populating the details of reserved tokens. +This includes: +- keeping generator numbers in numeric order +- filling out all the existing fields + +### Reserving tokens in the JSON grammar + +Care should be taken to follow existing precedent in populating the details of reserved tokens. +This includes: +- pointing to what extension has more information, when possible +- keeping enumerants in numeric order +- when there are aliases, listing the preferred spelling first +- adding the statement `"version" : "None"` + +## How to install the headers + +``` +mkdir build +cd build +cmake .. +cmake --build . --target install +``` + +Then, for example, you will have `/usr/local/include/spirv/unified1/spirv.h` + +If you want to install them somewhere else, then use +`-DCMAKE_INSTALL_PREFIX=/other/path` on the first `cmake` command. + +## Using the headers without installing + +### Using CMake +A CMake-based project can use the headers without installing, as follows: + +1. Add an `add_subdirectory` directive to include this source tree. +2. Use `${SPIRV-Headers_SOURCE_DIR}/include}` in a `target_include_directories` + directive. +3. In your C or C++ source code use `#include` directives that explicitly mention + the `spirv` path component. +``` +#include "spirv/unified1/GLSL.std.450.h" +#include "spirv/unified1/OpenCL.std.h" +#include "spirv/unified1/spirv.hpp" +``` + +See also the [example](example/) subdirectory. But since that example is +*inside* this repostory, it doesn't use and `add_subdirectory` directive. + +### Using Bazel +A Bazel-based project can use the headers without installing, as follows: + +1. Add SPIRV-Headers as a submodule of your project, and add a +`local_repository` to your `WORKSPACE` file. For example, if you place +SPIRV-Headers under `external/spirv-headers`, then add the following to your +`WORKSPACE` file: + +``` +local_repository( + name = "spirv_headers", + path = "external/spirv-headers", +) +``` + +2. Add one of the following to the `deps` attribute of your build target based +on your needs: +``` +@spirv_headers//:spirv_c_headers +@spirv_headers//:spirv_cpp_headers +@spirv_headers//:spirv_cpp11_headers +``` + +For example: + +``` +cc_library( + name = "project", + srcs = [ + # Path to project sources + ], + hdrs = [ + # Path to project headers + ], + deps = [ + "@spirv_tools//:spirv_c_headers", + # Other dependencies, + ], +) +``` + +3. In your C or C++ source code use `#include` directives that explicitly mention + the `spirv` path component. +``` +#include "spirv/unified1/GLSL.std.450.h" +#include "spirv/unified1/OpenCL.std.h" +#include "spirv/unified1/spirv.hpp" +``` + +## Generating headers from the JSON grammar for the SPIR-V core instruction set + +This will generally be done by Khronos, for a change to the JSON grammar. +However, the project for the tool to do this is included in this repository, +and can be used to test a PR, or even to include the results in the PR. +This is not required though. + +The header-generation project is under the `tools/buildHeaders` directory. +Use CMake to build and install the project, in a `build` subdirectory (under `tools/buildHeaders`). +There is then a bash script at `bin/makeHeaders` that shows how to use the built +header-generator binary to generate the headers from the JSON grammar. +(Execute `bin/makeHeaders` from the `tools/buildHeaders` directory.) +Here's a complete example: + +``` +cd tools/buildHeaders +mkdir build +cd build +cmake .. +cmake --build . --target install +cd .. +./bin/makeHeaders +``` + +Notes: +- this generator is used in a broader context within Khronos to generate the specification, + and that influences the languages used, for legacy reasons +- the C++ structures built may similarly include more than strictly necessary, for the same reason + +## Generating C headers for extended instruction sets + +The [GLSL.std.450.h](include/spirv/unified1/GLSL.std.450.h) +and [OpenCL.std.h](include/spirv/unified1/OpenCL.std.h) extended instruction set headers +are maintained manually. + +The C/C++ header for each of the other extended instruction sets +is generated from the corresponding JSON grammar file. For example, the +[OpenCLDebugInfo100.h](include/spirv/unified1/OpenCLDebugInfo100.h) header +is generated from the +[extinst.opencl.debuginfo.100.grammar.json](include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json) +grammar file. + +To generate these C/C++ headers, first make sure `python3` is in your PATH, then +invoke the build script as follows: +``` +cd tools/buildHeaders +python3 bin/makeExtinstHeaders.py +``` + +## FAQ + +* *How are different versions published?* + + The multiple versions of the headers have been simplified into a + single `unified1` view. The JSON grammar has a "version" field saying + what version things first showed up in. + +* *How do you handle the evolution of extended instruction sets?* + + Extended instruction sets evolve asynchronously from the core spec. + Right now there is only a single version of both the GLSL and OpenCL + headers. So we don't yet have a problematic example to resolve. + +## License + +``` +Copyright (c) 2015-2024 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +``` diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/cmake/SPIRV-Headers.pc.in b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/cmake/SPIRV-Headers.pc.in new file mode 100644 index 00000000..843b0a7e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/cmake/SPIRV-Headers.pc.in @@ -0,0 +1,8 @@ +includedir=@SPIRV_HEADERS_PKGCONFIG_INCLUDE_DIR@ + +Name: SPIRV-Headers +Description: Header files from the SPIR-V registry +Version: @CMAKE_PROJECT_VERSION@ +Requires: +Libs: +Cflags: -I${includedir} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h new file mode 100644 index 00000000..54cc00e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h new file mode 100644 index 00000000..19a66884 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json new file mode 100644 index 00000000..3d9f39e7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json new file mode 100644 index 00000000..4fe45060 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json new file mode 100644 index 00000000..f3cfc4c2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json @@ -0,0 +1,5775 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 0, + "revision" : 12, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs new file mode 100644 index 00000000..de325cc4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs @@ -0,0 +1,993 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010000; + public const uint Revision = 12; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h new file mode 100644 index 00000000..bd5a9b95 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h @@ -0,0 +1,993 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010000; +static const unsigned int SpvRevision = 12; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp new file mode 100644 index 00000000..e98a89ce --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp @@ -0,0 +1,1002 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010000; +static const unsigned int Revision = 12; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 new file mode 100644 index 00000000..8896e81b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 @@ -0,0 +1,1002 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010000; +static const unsigned int Revision = 12; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json new file mode 100644 index 00000000..9b0a8f3d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json @@ -0,0 +1,1020 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 65536, + "Revision": 12, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua new file mode 100644 index 00000000..2bd33ba0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua @@ -0,0 +1,949 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010000, + Revision = 12, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py new file mode 100644 index 00000000..8a200e76 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py @@ -0,0 +1,949 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010000, + 'Revision' : 12, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h new file mode 100644 index 00000000..54cc00e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h new file mode 100644 index 00000000..19a66884 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json new file mode 100644 index 00000000..3d9f39e7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json new file mode 100644 index 00000000..4fe45060 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json new file mode 100644 index 00000000..c142e60a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json @@ -0,0 +1,5938 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 1, + "revision" : 8, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpSizeOf", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpTypePipeStorage", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpConstantPipeStorage", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpTypeNamedBarrier", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpNamedBarrierInitialize", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpMemoryNamedBarrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpModuleProcessed", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs new file mode 100644 index 00000000..99194e51 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs @@ -0,0 +1,1015 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010100; + public const uint Revision = 8; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h new file mode 100644 index 00000000..971c3be6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h @@ -0,0 +1,1015 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010100; +static const unsigned int SpvRevision = 8; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp new file mode 100644 index 00000000..c26ac1f2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp @@ -0,0 +1,1024 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010100; +static const unsigned int Revision = 8; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 new file mode 100644 index 00000000..992d43b5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 @@ -0,0 +1,1024 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010100; +static const unsigned int Revision = 8; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json new file mode 100644 index 00000000..4c18e019 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json @@ -0,0 +1,1040 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 65792, + "Revision": 8, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua new file mode 100644 index 00000000..ad34e0ab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua @@ -0,0 +1,971 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010100, + Revision = 8, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py new file mode 100644 index 00000000..519a5971 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py @@ -0,0 +1,971 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010100, + 'Revision' : 8, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h new file mode 100644 index 00000000..54cc00e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h new file mode 100644 index 00000000..19a66884 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json new file mode 100644 index 00000000..3d9f39e7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json new file mode 100644 index 00000000..4fe45060 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json new file mode 100644 index 00000000..393ee3c8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json @@ -0,0 +1,5986 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 2, + "revision" : 2, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpSizeOf", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpTypePipeStorage", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpConstantPipeStorage", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpTypeNamedBarrier", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpNamedBarrierInitialize", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpMemoryNamedBarrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpModuleProcessed", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ] + }, + { + "opname" : "OpExecutionModeId", + "opcode" : 331, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroupId", + "value" : 37, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "LocalSizeId", + "value" : 38, + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHintId", + "value" : 39, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Local Size Hint'" } + ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "AlignmentId", + "value" : 46, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffsetId", + "value" : 47, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs new file mode 100644 index 00000000..493303d6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs @@ -0,0 +1,1021 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010200; + public const uint Revision = 2; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h new file mode 100644 index 00000000..7c6d884d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h @@ -0,0 +1,1021 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010200; +static const unsigned int SpvRevision = 2; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp new file mode 100644 index 00000000..57bd97a0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp @@ -0,0 +1,1030 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010200; +static const unsigned int Revision = 2; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 new file mode 100644 index 00000000..7a875fdb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 @@ -0,0 +1,1030 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010200; +static const unsigned int Revision = 2; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json new file mode 100644 index 00000000..9c0ff0a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json @@ -0,0 +1,1046 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 66048, + "Revision": 2, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "SubgroupsPerWorkgroupId": 37, + "LocalSizeId": 38, + "LocalSizeHintId": 39, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "AlignmentId": 46, + "MaxByteOffsetId": 47, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpExecutionModeId": 331, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua new file mode 100644 index 00000000..0de507d5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua @@ -0,0 +1,977 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010200, + Revision = 2, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py new file mode 100644 index 00000000..cefee4d6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py @@ -0,0 +1,977 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010200, + 'Revision' : 2, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'SubgroupsPerWorkgroupId' : 37, + 'LocalSizeId' : 38, + 'LocalSizeHintId' : 39, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'AlignmentId' : 46, + 'MaxByteOffsetId' : 47, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpExecutionModeId' : 331, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml new file mode 100644 index 00000000..1eb9707a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h new file mode 100644 index 00000000..e626a7a9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_gcn_shader_H_ +#define SPIRV_UNIFIED1_AMD_gcn_shader_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_gcn_shaderRevision = 2, + AMD_gcn_shaderRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_gcn_shaderInstructions { + AMD_gcn_shaderCubeFaceIndexAMD = 1, + AMD_gcn_shaderCubeFaceCoordAMD = 2, + AMD_gcn_shaderTimeAMD = 3, + AMD_gcn_shaderInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_gcn_shader_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h new file mode 100644 index 00000000..563c0b62 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_ballot_H_ +#define SPIRV_UNIFIED1_AMD_shader_ballot_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_ballotRevision = 5, + AMD_shader_ballotRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_ballotInstructions { + AMD_shader_ballotSwizzleInvocationsAMD = 1, + AMD_shader_ballotSwizzleInvocationsMaskedAMD = 2, + AMD_shader_ballotWriteInvocationAMD = 3, + AMD_shader_ballotMbcntAMD = 4, + AMD_shader_ballotInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_ballot_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h new file mode 100644 index 00000000..e663330c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ +#define SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_explicit_vertex_parameterRevision = 4, + AMD_shader_explicit_vertex_parameterRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_explicit_vertex_parameterInstructions { + AMD_shader_explicit_vertex_parameterInterpolateAtVertexAMD = 1, + AMD_shader_explicit_vertex_parameterInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h new file mode 100644 index 00000000..dd51c5fc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ +#define SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_trinary_minmaxRevision = 4, + AMD_shader_trinary_minmaxRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_trinary_minmaxInstructions { + AMD_shader_trinary_minmaxFMin3AMD = 1, + AMD_shader_trinary_minmaxUMin3AMD = 2, + AMD_shader_trinary_minmaxSMin3AMD = 3, + AMD_shader_trinary_minmaxFMax3AMD = 4, + AMD_shader_trinary_minmaxUMax3AMD = 5, + AMD_shader_trinary_minmaxSMax3AMD = 6, + AMD_shader_trinary_minmaxFMid3AMD = 7, + AMD_shader_trinary_minmaxUMid3AMD = 8, + AMD_shader_trinary_minmaxSMid3AMD = 9, + AMD_shader_trinary_minmaxInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h new file mode 100644 index 00000000..a3c0af45 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h @@ -0,0 +1,144 @@ +// Copyright (c) 2017-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_DebugInfo_H_ +#define SPIRV_UNIFIED1_DebugInfo_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + DebugInfoVersion = 100, + DebugInfoVersion_BitWidthPadding = 0x7fffffff +}; +enum { + DebugInfoRevision = 1, + DebugInfoRevision_BitWidthPadding = 0x7fffffff +}; + +enum DebugInfoInstructions { + DebugInfoDebugInfoNone = 0, + DebugInfoDebugCompilationUnit = 1, + DebugInfoDebugTypeBasic = 2, + DebugInfoDebugTypePointer = 3, + DebugInfoDebugTypeQualifier = 4, + DebugInfoDebugTypeArray = 5, + DebugInfoDebugTypeVector = 6, + DebugInfoDebugTypedef = 7, + DebugInfoDebugTypeFunction = 8, + DebugInfoDebugTypeEnum = 9, + DebugInfoDebugTypeComposite = 10, + DebugInfoDebugTypeMember = 11, + DebugInfoDebugTypeInheritance = 12, + DebugInfoDebugTypePtrToMember = 13, + DebugInfoDebugTypeTemplate = 14, + DebugInfoDebugTypeTemplateParameter = 15, + DebugInfoDebugTypeTemplateTemplateParameter = 16, + DebugInfoDebugTypeTemplateParameterPack = 17, + DebugInfoDebugGlobalVariable = 18, + DebugInfoDebugFunctionDeclaration = 19, + DebugInfoDebugFunction = 20, + DebugInfoDebugLexicalBlock = 21, + DebugInfoDebugLexicalBlockDiscriminator = 22, + DebugInfoDebugScope = 23, + DebugInfoDebugNoScope = 24, + DebugInfoDebugInlinedAt = 25, + DebugInfoDebugLocalVariable = 26, + DebugInfoDebugInlinedVariable = 27, + DebugInfoDebugDeclare = 28, + DebugInfoDebugValue = 29, + DebugInfoDebugOperation = 30, + DebugInfoDebugExpression = 31, + DebugInfoDebugMacroDef = 32, + DebugInfoDebugMacroUndef = 33, + DebugInfoInstructionsMax = 0x7fffffff +}; + + +enum DebugInfoDebugInfoFlags { + DebugInfoNone = 0x0000, + DebugInfoFlagIsProtected = 0x01, + DebugInfoFlagIsPrivate = 0x02, + DebugInfoFlagIsPublic = 0x03, + DebugInfoFlagIsLocal = 0x04, + DebugInfoFlagIsDefinition = 0x08, + DebugInfoFlagFwdDecl = 0x10, + DebugInfoFlagArtificial = 0x20, + DebugInfoFlagExplicit = 0x40, + DebugInfoFlagPrototyped = 0x80, + DebugInfoFlagObjectPointer = 0x100, + DebugInfoFlagStaticMember = 0x200, + DebugInfoFlagIndirectVariable = 0x400, + DebugInfoFlagLValueReference = 0x800, + DebugInfoFlagRValueReference = 0x1000, + DebugInfoFlagIsOptimized = 0x2000, + DebugInfoDebugInfoFlagsMax = 0x7fffffff +}; + +enum DebugInfoDebugBaseTypeAttributeEncoding { + DebugInfoUnspecified = 0, + DebugInfoAddress = 1, + DebugInfoBoolean = 2, + DebugInfoFloat = 4, + DebugInfoSigned = 5, + DebugInfoSignedChar = 6, + DebugInfoUnsigned = 7, + DebugInfoUnsignedChar = 8, + DebugInfoDebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum DebugInfoDebugCompositeType { + DebugInfoClass = 0, + DebugInfoStructure = 1, + DebugInfoUnion = 2, + DebugInfoDebugCompositeTypeMax = 0x7fffffff +}; + +enum DebugInfoDebugTypeQualifier { + DebugInfoConstType = 0, + DebugInfoVolatileType = 1, + DebugInfoRestrictType = 2, + DebugInfoDebugTypeQualifierMax = 0x7fffffff +}; + +enum DebugInfoDebugOperation { + DebugInfoDeref = 0, + DebugInfoPlus = 1, + DebugInfoMinus = 2, + DebugInfoPlusUconst = 3, + DebugInfoBitPiece = 4, + DebugInfoSwap = 5, + DebugInfoXderef = 6, + DebugInfoStackValue = 7, + DebugInfoConstu = 8, + DebugInfoDebugOperationMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_DebugInfo_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h new file mode 100644 index 00000000..0594f907 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2024 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h new file mode 100644 index 00000000..ee7c158a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h @@ -0,0 +1,98 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ +#define SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticClspvReflectionRevision = 7, + NonSemanticClspvReflectionRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticClspvReflectionInstructions { + NonSemanticClspvReflectionKernel = 1, + NonSemanticClspvReflectionArgumentInfo = 2, + NonSemanticClspvReflectionArgumentStorageBuffer = 3, + NonSemanticClspvReflectionArgumentUniform = 4, + NonSemanticClspvReflectionArgumentPodStorageBuffer = 5, + NonSemanticClspvReflectionArgumentPodUniform = 6, + NonSemanticClspvReflectionArgumentPodPushConstant = 7, + NonSemanticClspvReflectionArgumentSampledImage = 8, + NonSemanticClspvReflectionArgumentStorageImage = 9, + NonSemanticClspvReflectionArgumentSampler = 10, + NonSemanticClspvReflectionArgumentWorkgroup = 11, + NonSemanticClspvReflectionSpecConstantWorkgroupSize = 12, + NonSemanticClspvReflectionSpecConstantGlobalOffset = 13, + NonSemanticClspvReflectionSpecConstantWorkDim = 14, + NonSemanticClspvReflectionPushConstantGlobalOffset = 15, + NonSemanticClspvReflectionPushConstantEnqueuedLocalSize = 16, + NonSemanticClspvReflectionPushConstantGlobalSize = 17, + NonSemanticClspvReflectionPushConstantRegionOffset = 18, + NonSemanticClspvReflectionPushConstantNumWorkgroups = 19, + NonSemanticClspvReflectionPushConstantRegionGroupOffset = 20, + NonSemanticClspvReflectionConstantDataStorageBuffer = 21, + NonSemanticClspvReflectionConstantDataUniform = 22, + NonSemanticClspvReflectionLiteralSampler = 23, + NonSemanticClspvReflectionPropertyRequiredWorkgroupSize = 24, + NonSemanticClspvReflectionSpecConstantSubgroupMaxSize = 25, + NonSemanticClspvReflectionArgumentPointerPushConstant = 26, + NonSemanticClspvReflectionArgumentPointerUniform = 27, + NonSemanticClspvReflectionProgramScopeVariablesStorageBuffer = 28, + NonSemanticClspvReflectionProgramScopeVariablePointerRelocation = 29, + NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant = 30, + NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant = 31, + NonSemanticClspvReflectionImageArgumentInfoChannelOrderUniform = 32, + NonSemanticClspvReflectionImageArgumentInfoChannelDataTypeUniform = 33, + NonSemanticClspvReflectionArgumentStorageTexelBuffer = 34, + NonSemanticClspvReflectionArgumentUniformTexelBuffer = 35, + NonSemanticClspvReflectionConstantDataPointerPushConstant = 36, + NonSemanticClspvReflectionProgramScopeVariablePointerPushConstant = 37, + NonSemanticClspvReflectionPrintfInfo = 38, + NonSemanticClspvReflectionPrintfBufferStorageBuffer = 39, + NonSemanticClspvReflectionPrintfBufferPointerPushConstant = 40, + NonSemanticClspvReflectionNormalizedSamplerMaskPushConstant = 41, + NonSemanticClspvReflectionWorkgroupVariableSize = 42, + NonSemanticClspvReflectionInstructionsMax = 0x7fffffff +}; + + +enum NonSemanticClspvReflectionKernelPropertyFlags { + NonSemanticClspvReflectionNone = 0x0, + NonSemanticClspvReflectionMayUsePrintf = 0x1, + NonSemanticClspvReflectionKernelPropertyFlagsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugBreak.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugBreak.h new file mode 100644 index 00000000..8604fe78 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugBreak.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugBreak_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugBreak_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugBreakRevision = 1, + NonSemanticDebugBreakRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugBreakInstructions { + NonSemanticDebugBreakDebugBreak = 1, + NonSemanticDebugBreakInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugBreak_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h new file mode 100644 index 00000000..bc24683e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugPrintfRevision = 1, + NonSemanticDebugPrintfRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugPrintfInstructions { + NonSemanticDebugPrintfDebugPrintf = 1, + NonSemanticDebugPrintfInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h new file mode 100644 index 00000000..b276b560 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticShaderDebugInfo100.h @@ -0,0 +1,171 @@ +// Copyright (c) 2018-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ +#define SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticShaderDebugInfo100Version = 100, + NonSemanticShaderDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + NonSemanticShaderDebugInfo100Revision = 6, + NonSemanticShaderDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100Instructions { + NonSemanticShaderDebugInfo100DebugInfoNone = 0, + NonSemanticShaderDebugInfo100DebugCompilationUnit = 1, + NonSemanticShaderDebugInfo100DebugTypeBasic = 2, + NonSemanticShaderDebugInfo100DebugTypePointer = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifier = 4, + NonSemanticShaderDebugInfo100DebugTypeArray = 5, + NonSemanticShaderDebugInfo100DebugTypeVector = 6, + NonSemanticShaderDebugInfo100DebugTypedef = 7, + NonSemanticShaderDebugInfo100DebugTypeFunction = 8, + NonSemanticShaderDebugInfo100DebugTypeEnum = 9, + NonSemanticShaderDebugInfo100DebugTypeComposite = 10, + NonSemanticShaderDebugInfo100DebugTypeMember = 11, + NonSemanticShaderDebugInfo100DebugTypeInheritance = 12, + NonSemanticShaderDebugInfo100DebugTypePtrToMember = 13, + NonSemanticShaderDebugInfo100DebugTypeTemplate = 14, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameter = 15, + NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter = 16, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack = 17, + NonSemanticShaderDebugInfo100DebugGlobalVariable = 18, + NonSemanticShaderDebugInfo100DebugFunctionDeclaration = 19, + NonSemanticShaderDebugInfo100DebugFunction = 20, + NonSemanticShaderDebugInfo100DebugLexicalBlock = 21, + NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator = 22, + NonSemanticShaderDebugInfo100DebugScope = 23, + NonSemanticShaderDebugInfo100DebugNoScope = 24, + NonSemanticShaderDebugInfo100DebugInlinedAt = 25, + NonSemanticShaderDebugInfo100DebugLocalVariable = 26, + NonSemanticShaderDebugInfo100DebugInlinedVariable = 27, + NonSemanticShaderDebugInfo100DebugDeclare = 28, + NonSemanticShaderDebugInfo100DebugValue = 29, + NonSemanticShaderDebugInfo100DebugOperation = 30, + NonSemanticShaderDebugInfo100DebugExpression = 31, + NonSemanticShaderDebugInfo100DebugMacroDef = 32, + NonSemanticShaderDebugInfo100DebugMacroUndef = 33, + NonSemanticShaderDebugInfo100DebugImportedEntity = 34, + NonSemanticShaderDebugInfo100DebugSource = 35, + NonSemanticShaderDebugInfo100DebugFunctionDefinition = 101, + NonSemanticShaderDebugInfo100DebugSourceContinued = 102, + NonSemanticShaderDebugInfo100DebugLine = 103, + NonSemanticShaderDebugInfo100DebugNoLine = 104, + NonSemanticShaderDebugInfo100DebugBuildIdentifier = 105, + NonSemanticShaderDebugInfo100DebugStoragePath = 106, + NonSemanticShaderDebugInfo100DebugEntryPoint = 107, + NonSemanticShaderDebugInfo100DebugTypeMatrix = 108, + NonSemanticShaderDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum NonSemanticShaderDebugInfo100DebugInfoFlags { + NonSemanticShaderDebugInfo100None = 0x0000, + NonSemanticShaderDebugInfo100FlagIsProtected = 0x01, + NonSemanticShaderDebugInfo100FlagIsPrivate = 0x02, + NonSemanticShaderDebugInfo100FlagIsPublic = 0x03, + NonSemanticShaderDebugInfo100FlagIsLocal = 0x04, + NonSemanticShaderDebugInfo100FlagIsDefinition = 0x08, + NonSemanticShaderDebugInfo100FlagFwdDecl = 0x10, + NonSemanticShaderDebugInfo100FlagArtificial = 0x20, + NonSemanticShaderDebugInfo100FlagExplicit = 0x40, + NonSemanticShaderDebugInfo100FlagPrototyped = 0x80, + NonSemanticShaderDebugInfo100FlagObjectPointer = 0x100, + NonSemanticShaderDebugInfo100FlagStaticMember = 0x200, + NonSemanticShaderDebugInfo100FlagIndirectVariable = 0x400, + NonSemanticShaderDebugInfo100FlagLValueReference = 0x800, + NonSemanticShaderDebugInfo100FlagRValueReference = 0x1000, + NonSemanticShaderDebugInfo100FlagIsOptimized = 0x2000, + NonSemanticShaderDebugInfo100FlagIsEnumClass = 0x4000, + NonSemanticShaderDebugInfo100FlagTypePassByValue = 0x8000, + NonSemanticShaderDebugInfo100FlagTypePassByReference = 0x10000, + NonSemanticShaderDebugInfo100FlagUnknownPhysicalLayout = 0x20000, + NonSemanticShaderDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100BuildIdentifierFlags { + NonSemanticShaderDebugInfo100IdentifierPossibleDuplicates = 0x01, + NonSemanticShaderDebugInfo100BuildIdentifierFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncoding { + NonSemanticShaderDebugInfo100Unspecified = 0, + NonSemanticShaderDebugInfo100Address = 1, + NonSemanticShaderDebugInfo100Boolean = 2, + NonSemanticShaderDebugInfo100Float = 3, + NonSemanticShaderDebugInfo100Signed = 4, + NonSemanticShaderDebugInfo100SignedChar = 5, + NonSemanticShaderDebugInfo100Unsigned = 6, + NonSemanticShaderDebugInfo100UnsignedChar = 7, + NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugCompositeType { + NonSemanticShaderDebugInfo100Class = 0, + NonSemanticShaderDebugInfo100Structure = 1, + NonSemanticShaderDebugInfo100Union = 2, + NonSemanticShaderDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugTypeQualifier { + NonSemanticShaderDebugInfo100ConstType = 0, + NonSemanticShaderDebugInfo100VolatileType = 1, + NonSemanticShaderDebugInfo100RestrictType = 2, + NonSemanticShaderDebugInfo100AtomicType = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugOperation { + NonSemanticShaderDebugInfo100Deref = 0, + NonSemanticShaderDebugInfo100Plus = 1, + NonSemanticShaderDebugInfo100Minus = 2, + NonSemanticShaderDebugInfo100PlusUconst = 3, + NonSemanticShaderDebugInfo100BitPiece = 4, + NonSemanticShaderDebugInfo100Swap = 5, + NonSemanticShaderDebugInfo100Xderef = 6, + NonSemanticShaderDebugInfo100StackValue = 7, + NonSemanticShaderDebugInfo100Constu = 8, + NonSemanticShaderDebugInfo100Fragment = 9, + NonSemanticShaderDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugImportedEntity { + NonSemanticShaderDebugInfo100ImportedModule = 0, + NonSemanticShaderDebugInfo100ImportedDeclaration = 1, + NonSemanticShaderDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticVkspReflection.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticVkspReflection.h new file mode 100644 index 00000000..af4a556e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticVkspReflection.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticVkspReflection_H_ +#define SPIRV_UNIFIED1_NonSemanticVkspReflection_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticVkspReflectionRevision = 4, + NonSemanticVkspReflectionRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticVkspReflectionInstructions { + NonSemanticVkspReflectionConfiguration = 1, + NonSemanticVkspReflectionStartCounter = 2, + NonSemanticVkspReflectionStopCounter = 3, + NonSemanticVkspReflectionPushConstants = 4, + NonSemanticVkspReflectionSpecializationMapEntry = 5, + NonSemanticVkspReflectionDescriptorSetBuffer = 6, + NonSemanticVkspReflectionDescriptorSetImage = 7, + NonSemanticVkspReflectionDescriptorSetSampler = 8, + NonSemanticVkspReflectionInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticVkspReflection_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h new file mode 100644 index 00000000..ed74f203 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h @@ -0,0 +1,401 @@ +/* +** Copyright (c) 2015-2024 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef OPENCLstd_H +#define OPENCLstd_H + +#ifdef __cplusplus +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB + +#else + +enum OpenCLstd_Entrypoints { + + // Section 2.1: Math extended instructions + OpenCLstd_Acos = 0, + OpenCLstd_Acosh = 1, + OpenCLstd_Acospi = 2, + OpenCLstd_Asin = 3, + OpenCLstd_Asinh = 4, + OpenCLstd_Asinpi = 5, + OpenCLstd_Atan = 6, + OpenCLstd_Atan2 = 7, + OpenCLstd_Atanh = 8, + OpenCLstd_Atanpi = 9, + OpenCLstd_Atan2pi = 10, + OpenCLstd_Cbrt = 11, + OpenCLstd_Ceil = 12, + OpenCLstd_Copysign = 13, + OpenCLstd_Cos = 14, + OpenCLstd_Cosh = 15, + OpenCLstd_Cospi = 16, + OpenCLstd_Erfc = 17, + OpenCLstd_Erf = 18, + OpenCLstd_Exp = 19, + OpenCLstd_Exp2 = 20, + OpenCLstd_Exp10 = 21, + OpenCLstd_Expm1 = 22, + OpenCLstd_Fabs = 23, + OpenCLstd_Fdim = 24, + OpenCLstd_Floor = 25, + OpenCLstd_Fma = 26, + OpenCLstd_Fmax = 27, + OpenCLstd_Fmin = 28, + OpenCLstd_Fmod = 29, + OpenCLstd_Fract = 30, + OpenCLstd_Frexp = 31, + OpenCLstd_Hypot = 32, + OpenCLstd_Ilogb = 33, + OpenCLstd_Ldexp = 34, + OpenCLstd_Lgamma = 35, + OpenCLstd_Lgamma_r = 36, + OpenCLstd_Log = 37, + OpenCLstd_Log2 = 38, + OpenCLstd_Log10 = 39, + OpenCLstd_Log1p = 40, + OpenCLstd_Logb = 41, + OpenCLstd_Mad = 42, + OpenCLstd_Maxmag = 43, + OpenCLstd_Minmag = 44, + OpenCLstd_Modf = 45, + OpenCLstd_Nan = 46, + OpenCLstd_Nextafter = 47, + OpenCLstd_Pow = 48, + OpenCLstd_Pown = 49, + OpenCLstd_Powr = 50, + OpenCLstd_Remainder = 51, + OpenCLstd_Remquo = 52, + OpenCLstd_Rint = 53, + OpenCLstd_Rootn = 54, + OpenCLstd_Round = 55, + OpenCLstd_Rsqrt = 56, + OpenCLstd_Sin = 57, + OpenCLstd_Sincos = 58, + OpenCLstd_Sinh = 59, + OpenCLstd_Sinpi = 60, + OpenCLstd_Sqrt = 61, + OpenCLstd_Tan = 62, + OpenCLstd_Tanh = 63, + OpenCLstd_Tanpi = 64, + OpenCLstd_Tgamma = 65, + OpenCLstd_Trunc = 66, + OpenCLstd_Half_cos = 67, + OpenCLstd_Half_divide = 68, + OpenCLstd_Half_exp = 69, + OpenCLstd_Half_exp2 = 70, + OpenCLstd_Half_exp10 = 71, + OpenCLstd_Half_log = 72, + OpenCLstd_Half_log2 = 73, + OpenCLstd_Half_log10 = 74, + OpenCLstd_Half_powr = 75, + OpenCLstd_Half_recip = 76, + OpenCLstd_Half_rsqrt = 77, + OpenCLstd_Half_sin = 78, + OpenCLstd_Half_sqrt = 79, + OpenCLstd_Half_tan = 80, + OpenCLstd_Native_cos = 81, + OpenCLstd_Native_divide = 82, + OpenCLstd_Native_exp = 83, + OpenCLstd_Native_exp2 = 84, + OpenCLstd_Native_exp10 = 85, + OpenCLstd_Native_log = 86, + OpenCLstd_Native_log2 = 87, + OpenCLstd_Native_log10 = 88, + OpenCLstd_Native_powr = 89, + OpenCLstd_Native_recip = 90, + OpenCLstd_Native_rsqrt = 91, + OpenCLstd_Native_sin = 92, + OpenCLstd_Native_sqrt = 93, + OpenCLstd_Native_tan = 94, + + // Section 2.2: Integer instructions + OpenCLstd_SAbs = 141, + OpenCLstd_SAbs_diff = 142, + OpenCLstd_SAdd_sat = 143, + OpenCLstd_UAdd_sat = 144, + OpenCLstd_SHadd = 145, + OpenCLstd_UHadd = 146, + OpenCLstd_SRhadd = 147, + OpenCLstd_URhadd = 148, + OpenCLstd_SClamp = 149, + OpenCLstd_UClamp = 150, + OpenCLstd_Clz = 151, + OpenCLstd_Ctz = 152, + OpenCLstd_SMad_hi = 153, + OpenCLstd_UMad_sat = 154, + OpenCLstd_SMad_sat = 155, + OpenCLstd_SMax = 156, + OpenCLstd_UMax = 157, + OpenCLstd_SMin = 158, + OpenCLstd_UMin = 159, + OpenCLstd_SMul_hi = 160, + OpenCLstd_Rotate = 161, + OpenCLstd_SSub_sat = 162, + OpenCLstd_USub_sat = 163, + OpenCLstd_U_Upsample = 164, + OpenCLstd_S_Upsample = 165, + OpenCLstd_Popcount = 166, + OpenCLstd_SMad24 = 167, + OpenCLstd_UMad24 = 168, + OpenCLstd_SMul24 = 169, + OpenCLstd_UMul24 = 170, + OpenCLstd_UAbs = 201, + OpenCLstd_UAbs_diff = 202, + OpenCLstd_UMul_hi = 203, + OpenCLstd_UMad_hi = 204, + + // Section 2.3: Common instructions + OpenCLstd_FClamp = 95, + OpenCLstd_Degrees = 96, + OpenCLstd_FMax_common = 97, + OpenCLstd_FMin_common = 98, + OpenCLstd_Mix = 99, + OpenCLstd_Radians = 100, + OpenCLstd_Step = 101, + OpenCLstd_Smoothstep = 102, + OpenCLstd_Sign = 103, + + // Section 2.4: Geometric instructions + OpenCLstd_Cross = 104, + OpenCLstd_Distance = 105, + OpenCLstd_Length = 106, + OpenCLstd_Normalize = 107, + OpenCLstd_Fast_distance = 108, + OpenCLstd_Fast_length = 109, + OpenCLstd_Fast_normalize = 110, + + // Section 2.5: Relational instructions + OpenCLstd_Bitselect = 186, + OpenCLstd_Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + OpenCLstd_Vloadn = 171, + OpenCLstd_Vstoren = 172, + OpenCLstd_Vload_half = 173, + OpenCLstd_Vload_halfn = 174, + OpenCLstd_Vstore_half = 175, + OpenCLstd_Vstore_half_r = 176, + OpenCLstd_Vstore_halfn = 177, + OpenCLstd_Vstore_halfn_r = 178, + OpenCLstd_Vloada_halfn = 179, + OpenCLstd_Vstorea_halfn = 180, + OpenCLstd_Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + OpenCLstd_Shuffle = 182, + OpenCLstd_Shuffle2 = 183, + + // Section 2.8: Misc instructions + OpenCLstd_Printf = 184, + OpenCLstd_Prefetch = 185, +}; + +#endif + +#endif // #ifndef OPENCLstd_H diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h new file mode 100644 index 00000000..ffbd16f1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h @@ -0,0 +1,158 @@ +// Copyright (c) 2018-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ +#define SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + OpenCLDebugInfo100Version = 200, + OpenCLDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + OpenCLDebugInfo100Revision = 2, + OpenCLDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum OpenCLDebugInfo100Instructions { + OpenCLDebugInfo100DebugInfoNone = 0, + OpenCLDebugInfo100DebugCompilationUnit = 1, + OpenCLDebugInfo100DebugTypeBasic = 2, + OpenCLDebugInfo100DebugTypePointer = 3, + OpenCLDebugInfo100DebugTypeQualifier = 4, + OpenCLDebugInfo100DebugTypeArray = 5, + OpenCLDebugInfo100DebugTypeVector = 6, + OpenCLDebugInfo100DebugTypedef = 7, + OpenCLDebugInfo100DebugTypeFunction = 8, + OpenCLDebugInfo100DebugTypeEnum = 9, + OpenCLDebugInfo100DebugTypeComposite = 10, + OpenCLDebugInfo100DebugTypeMember = 11, + OpenCLDebugInfo100DebugTypeInheritance = 12, + OpenCLDebugInfo100DebugTypePtrToMember = 13, + OpenCLDebugInfo100DebugTypeTemplate = 14, + OpenCLDebugInfo100DebugTypeTemplateParameter = 15, + OpenCLDebugInfo100DebugTypeTemplateTemplateParameter = 16, + OpenCLDebugInfo100DebugTypeTemplateParameterPack = 17, + OpenCLDebugInfo100DebugGlobalVariable = 18, + OpenCLDebugInfo100DebugFunctionDeclaration = 19, + OpenCLDebugInfo100DebugFunction = 20, + OpenCLDebugInfo100DebugLexicalBlock = 21, + OpenCLDebugInfo100DebugLexicalBlockDiscriminator = 22, + OpenCLDebugInfo100DebugScope = 23, + OpenCLDebugInfo100DebugNoScope = 24, + OpenCLDebugInfo100DebugInlinedAt = 25, + OpenCLDebugInfo100DebugLocalVariable = 26, + OpenCLDebugInfo100DebugInlinedVariable = 27, + OpenCLDebugInfo100DebugDeclare = 28, + OpenCLDebugInfo100DebugValue = 29, + OpenCLDebugInfo100DebugOperation = 30, + OpenCLDebugInfo100DebugExpression = 31, + OpenCLDebugInfo100DebugMacroDef = 32, + OpenCLDebugInfo100DebugMacroUndef = 33, + OpenCLDebugInfo100DebugImportedEntity = 34, + OpenCLDebugInfo100DebugSource = 35, + OpenCLDebugInfo100DebugModuleINTEL = 36, + OpenCLDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum OpenCLDebugInfo100DebugInfoFlags { + OpenCLDebugInfo100None = 0x0000, + OpenCLDebugInfo100FlagIsProtected = 0x01, + OpenCLDebugInfo100FlagIsPrivate = 0x02, + OpenCLDebugInfo100FlagIsPublic = 0x03, + OpenCLDebugInfo100FlagIsLocal = 0x04, + OpenCLDebugInfo100FlagIsDefinition = 0x08, + OpenCLDebugInfo100FlagFwdDecl = 0x10, + OpenCLDebugInfo100FlagArtificial = 0x20, + OpenCLDebugInfo100FlagExplicit = 0x40, + OpenCLDebugInfo100FlagPrototyped = 0x80, + OpenCLDebugInfo100FlagObjectPointer = 0x100, + OpenCLDebugInfo100FlagStaticMember = 0x200, + OpenCLDebugInfo100FlagIndirectVariable = 0x400, + OpenCLDebugInfo100FlagLValueReference = 0x800, + OpenCLDebugInfo100FlagRValueReference = 0x1000, + OpenCLDebugInfo100FlagIsOptimized = 0x2000, + OpenCLDebugInfo100FlagIsEnumClass = 0x4000, + OpenCLDebugInfo100FlagTypePassByValue = 0x8000, + OpenCLDebugInfo100FlagTypePassByReference = 0x10000, + OpenCLDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugBaseTypeAttributeEncoding { + OpenCLDebugInfo100Unspecified = 0, + OpenCLDebugInfo100Address = 1, + OpenCLDebugInfo100Boolean = 2, + OpenCLDebugInfo100Float = 3, + OpenCLDebugInfo100Signed = 4, + OpenCLDebugInfo100SignedChar = 5, + OpenCLDebugInfo100Unsigned = 6, + OpenCLDebugInfo100UnsignedChar = 7, + OpenCLDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugCompositeType { + OpenCLDebugInfo100Class = 0, + OpenCLDebugInfo100Structure = 1, + OpenCLDebugInfo100Union = 2, + OpenCLDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugTypeQualifier { + OpenCLDebugInfo100ConstType = 0, + OpenCLDebugInfo100VolatileType = 1, + OpenCLDebugInfo100RestrictType = 2, + OpenCLDebugInfo100AtomicType = 3, + OpenCLDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugOperation { + OpenCLDebugInfo100Deref = 0, + OpenCLDebugInfo100Plus = 1, + OpenCLDebugInfo100Minus = 2, + OpenCLDebugInfo100PlusUconst = 3, + OpenCLDebugInfo100BitPiece = 4, + OpenCLDebugInfo100Swap = 5, + OpenCLDebugInfo100Xderef = 6, + OpenCLDebugInfo100StackValue = 7, + OpenCLDebugInfo100Constu = 8, + OpenCLDebugInfo100Fragment = 9, + OpenCLDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugImportedEntity { + OpenCLDebugInfo100ImportedModule = 0, + OpenCLDebugInfo100ImportedDeclaration = 1, + OpenCLDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json new file mode 100644 index 00000000..918a9e54 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json @@ -0,0 +1,572 @@ +{ + "copyright" : [ + "Copyright (c) 2017-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 1, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "LiteralInteger", "name" : "'DWARF Version'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "StorageClass", "name" : "'Storage Class'" }, + { "kind" : "DebugInfoFlags", "name" : "'Literal Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Paramter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugCompositeType", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Child'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "LiteralInteger", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "DebugOperation", "name" : "'OpCode'" }, + { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "4" + }, + { + "enumerant" : "Signed", + "value" : "5" + }, + { + "enumerant" : "SignedChar", + "value" : "6" + }, + { + "enumerant" : "Unsigned", + "value" : "7" + }, + { + "enumerant" : "UnsignedChar", + "value" : "8" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json new file mode 100644 index 00000000..ac8fc6dd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json new file mode 100644 index 00000000..bd8dfd1a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json @@ -0,0 +1,434 @@ +{ + "revision" : 7, + "instructions" : [ + { + "opname" : "Kernel", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Name" }, + { "kind" : "IdRef", "name" : "NumArguments", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Flags", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Attributes", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentInfo", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "Name" }, + { "kind" : "IdRef", "name" : "Type Name", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Address Qualifier", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Access Qualifier", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Type Qualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentStorageBuffer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentUniform", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodStorageBuffer", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodUniform", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodPushConstant", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentSampledImage", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentStorageImage", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentSampler", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentWorkgroup", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "SpecId" }, + { "kind" : "IdRef", "name" : "ElemSize" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "SpecConstantWorkgroupSize", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + }, + { + "opname" : "SpecConstantGlobalOffset", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + }, + { + "opname" : "SpecConstantWorkDim", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "Dim" } + ] + }, + { + "opname" : "PushConstantGlobalOffset", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantEnqueuedLocalSize", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantGlobalSize", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantRegionOffset", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantNumWorkgroups", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantRegionGroupOffset", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ConstantDataStorageBuffer", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "ConstantDataUniform", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "LiteralSampler", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Mask" } + ] + }, + { + "opname" : "PropertyRequiredWorkgroupSize", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + }, + { + "opname" : "SpecConstantSubgroupMaxSize", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ArgumentPointerPushConstant", + "opcode" : 26, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPointerUniform", + "opcode" : 27, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ProgramScopeVariablesStorageBuffer", + "opcode" : 28, + "operands": [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "ProgramScopeVariablePointerRelocation", + "opcode" : 29, + "operands": [ + { "kind" : "IdRef", "name" : "ObjectOffset" }, + { "kind" : "IdRef", "name" : "PointerOffset" }, + { "kind" : "IdRef", "name" : "PointerSize" } + ] + }, + { + "opname" : "ImageArgumentInfoChannelOrderPushConstant", + "opcode" : 30, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ImageArgumentInfoChannelDataTypePushConstant", + "opcode" : 31, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ImageArgumentInfoChannelOrderUniform", + "opcode" : 32, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ImageArgumentInfoChannelDataTypeUniform", + "opcode" : 33, + "operands": [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ArgumentStorageTexelBuffer", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentUniformTexelBuffer", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ConstantDataPointerPushConstant", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset"}, + { "kind" : "IdRef", "name" : "Size"}, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "ProgramScopeVariablePointerPushConstant", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset"}, + { "kind" : "IdRef", "name" : "Size"}, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "PrintfInfo", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "PrintfID" }, + { "kind" : "IdRef", "name" : "FormatString" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "ArgumentSizes"} + ] + }, + { + "opname" : "PrintfBufferStorageBuffer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "BufferSize"} + ] + }, + { + "opname" : "PrintfBufferPointerPushConstant", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size"}, + { "kind" : "IdRef", "name" : "BufferSize"} + ] + }, + { + "opname" : "NormalizedSamplerMaskPushConstant", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "WorkgroupVariableSize", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "Variable" }, + { "kind" : "IdRef", "name" : "Size" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "KernelPropertyFlags", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0" + }, + { + "enumerant" : "MayUsePrintf", + "value" : "0x1" + } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugbreak.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugbreak.grammar.json new file mode 100644 index 00000000..ae288833 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugbreak.grammar.json @@ -0,0 +1,9 @@ +{ + "revision" : 1, + "instructions" : [ + { + "opname" : "DebugBreak", + "opcode" : 1 + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json new file mode 100644 index 00000000..71fa7112 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json @@ -0,0 +1,13 @@ +{ + "revision" : 1, + "instructions" : [ + { + "opname" : "DebugPrintf", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Format'" }, + { "kind" : "IdRef", "quantifier" : "*" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json new file mode 100644 index 00000000..1eb2859a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json @@ -0,0 +1,713 @@ +{ + "copyright" : [ + "Copyright (c) 2018-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 6, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Version'" }, + { "kind" : "IdRef", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Encoding'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Storage Class'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'OpCode'" }, + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "IdRef", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDefinition", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Definition'" } + ] + }, + { + "opname" : "DebugSourceContinued", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'Text'" } + ] + }, + { + "opname" : "DebugLine", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Line Start'" }, + { "kind" : "IdRef", "name" : "'Line End'" }, + { "kind" : "IdRef", "name" : "'Column Start'" }, + { "kind" : "IdRef", "name" : "'Column End'" } + ] + }, + { + "opname" : "DebugNoLine", + "opcode" : 104 + }, + { + "opname" : "DebugBuildIdentifier", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'Identifier'" }, + { "kind" : "IdRef", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugStoragePath", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'Path'" } + ] + }, + { + "opname" : "DebugEntryPoint", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "IdRef", "name" : "'Compilation Unit'" }, + { "kind" : "IdRef", "name" : "'Compiler Signature'" }, + { "kind" : "IdRef", "name" : "'Command-line Arguments'" } + ] + }, + { + "opname" : "DebugTypeMatrix", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'Vector Type'" }, + { "kind" : "IdRef", "name" : "'Vector Count'" }, + { "kind" : "IdRef", "name" : "'Column Major'" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + }, + { + "enumerant" : "FlagIsEnumClass", + "value" : "0x4000" + }, + { + "enumerant" : "FlagTypePassByValue", + "value" : "0x8000" + }, + { + "enumerant" : "FlagTypePassByReference", + "value" : "0x10000" + }, + { + "enumerant" : "FlagUnknownPhysicalLayout", + "value" : "0x20000" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "BuildIdentifierFlags", + "enumerants" : [ + { + "enumerant" : "IdentifierPossibleDuplicates", + "value" : "0x01" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "3" + }, + { + "enumerant" : "Signed", + "value" : "4" + }, + { + "enumerant" : "SignedChar", + "value" : "5" + }, + { + "enumerant" : "Unsigned", + "value" : "6" + }, + { + "enumerant" : "UnsignedChar", + "value" : "7" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + }, + { + "enumerant" : "AtomicType", + "value" : "3" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.vkspreflection.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.vkspreflection.grammar.json new file mode 100644 index 00000000..0bc12d60 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.vkspreflection.grammar.json @@ -0,0 +1,138 @@ +{ + "revision" : 4, + "instructions" : [ + { + "opname" : "Configuration", + "opcode" : 1, + "operands" : [ + {"kind" : "IdRef", "name" : "enabledExtensionNames" }, + {"kind" : "IdRef", "name" : "specializationInfoDataSize" }, + {"kind" : "IdRef", "name" : "specializationInfoData" }, + {"kind" : "IdRef", "name" : "shaderName" }, + {"kind" : "IdRef", "name" : "EntryPoint" }, + {"kind" : "IdRef", "name" : "groupCountX" }, + {"kind" : "IdRef", "name" : "groupCountY" }, + {"kind" : "IdRef", "name" : "groupCountZ" }, + {"kind" : "IdRef", "name" : "dispatchId" } + ] + }, + { + "opname" : "StartCounter", + "opcode" : 2, + "operands" : [ + {"kind" : "IdRef", "name" : "name" } + ] + }, + { + "opname" : "StopCounter", + "opcode" : 3, + "operands" : [ + {"kind" : "IdRef", "name" : "counter" } + ] + }, + { + "opname" : "PushConstants", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "offset" }, + { "kind" : "IdRef", "name" : "size" }, + { "kind" : "IdRef", "name" : "pValues" }, + { "kind" : "IdRef", "name" : "stageFlags" } + ] + }, + { + "opname" : "SpecializationMapEntry", + "opcode" : 5, + "operands" : [ + {"kind" : "IdRef", "name" : "constantID" }, + {"kind" : "IdRef", "name" : "offset" }, + {"kind" : "IdRef", "name" : "size" } + ] + }, + { + "opname" : "DescriptorSetBuffer", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "ds" }, + { "kind" : "IdRef", "name" : "binding" }, + { "kind" : "IdRef", "name" : "type" }, + { "kind" : "IdRef", "name" : "flags" }, + { "kind" : "IdRef", "name" : "queueFamilyIndexCount" }, + { "kind" : "IdRef", "name" : "sharingMode" }, + { "kind" : "IdRef", "name" : "size" }, + { "kind" : "IdRef", "name" : "usage" }, + { "kind" : "IdRef", "name" : "range" }, + { "kind" : "IdRef", "name" : "offset" }, + { "kind" : "IdRef", "name" : "memorySize" }, + { "kind" : "IdRef", "name" : "memoryType" }, + { "kind" : "IdRef", "name" : "bindOffset" }, + { "kind" : "IdRef", "name" : "viewFlags" }, + { "kind" : "IdRef", "name" : "viewFormat" } + ] + }, + { + "opname" : "DescriptorSetImage", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "ds" }, + { "kind" : "IdRef", "name" : "binding" }, + { "kind" : "IdRef", "name" : "type" }, + { "kind" : "IdRef", "name" : "imageLayout"}, + { "kind" : "IdRef", "name" : "imageFlags"}, + { "kind" : "IdRef", "name" : "imageType"}, + { "kind" : "IdRef", "name" : "imageformat"}, + { "kind" : "IdRef", "name" : "width"}, + { "kind" : "IdRef", "name" : "height"}, + { "kind" : "IdRef", "name" : "depth"}, + { "kind" : "IdRef", "name" : "mipLevels"}, + { "kind" : "IdRef", "name" : "arrayLayers"}, + { "kind" : "IdRef", "name" : "samples"}, + { "kind" : "IdRef", "name" : "tiling"}, + { "kind" : "IdRef", "name" : "usage"}, + { "kind" : "IdRef", "name" : "sharingMode"}, + { "kind" : "IdRef", "name" : "queueFamilyIndexCount"}, + { "kind" : "IdRef", "name" : "initialLayout"}, + { "kind" : "IdRef", "name" : "aspectMask"}, + { "kind" : "IdRef", "name" : "baseMipLevel"}, + { "kind" : "IdRef", "name" : "levelCount"}, + { "kind" : "IdRef", "name" : "baseArrayLayer"}, + { "kind" : "IdRef", "name" : "layerCount"}, + { "kind" : "IdRef", "name" : "viewFlags"}, + { "kind" : "IdRef", "name" : "viewType"}, + { "kind" : "IdRef", "name" : "viewFormat"}, + { "kind" : "IdRef", "name" : "component_a"}, + { "kind" : "IdRef", "name" : "component_b"}, + { "kind" : "IdRef", "name" : "component_g"}, + { "kind" : "IdRef", "name" : "component_r"}, + { "kind" : "IdRef", "name" : "memorySize" }, + { "kind" : "IdRef", "name" : "memoryType" }, + { "kind" : "IdRef", "name" : "bindOffset"} + ] + }, + { + "opname" : "DescriptorSetSampler", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "ds" }, + { "kind" : "IdRef", "name" : "binding" }, + { "kind" : "IdRef", "name" : "type" }, + { "kind" : "IdRef", "name" : "flags"}, + { "kind" : "IdRef", "name" : "magFilter"}, + { "kind" : "IdRef", "name" : "minFilter"}, + { "kind" : "IdRef", "name" : "mipmapMode"}, + { "kind" : "IdRef", "name" : "addressModeU"}, + { "kind" : "IdRef", "name" : "addressModeV"}, + { "kind" : "IdRef", "name" : "addressModeW"}, + { "kind" : "IdRef", "name" : "mipLodBias"}, + { "kind" : "IdRef", "name" : "anisotropyEnable"}, + { "kind" : "IdRef", "name" : "maxAnisotropy"}, + { "kind" : "IdRef", "name" : "compareEnable"}, + { "kind" : "IdRef", "name" : "compareOp"}, + { "kind" : "IdRef", "name" : "minLod"}, + { "kind" : "IdRef", "name" : "maxLod"}, + { "kind" : "IdRef", "name" : "borderColor"}, + { "kind" : "IdRef", "name" : "unnormalizedCoordinates"} + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json new file mode 100644 index 00000000..53b001c5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json @@ -0,0 +1,651 @@ +{ + "copyright" : [ + "Copyright (c) 2018-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 200, + "revision" : 2, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "LiteralInteger", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "SourceLanguage", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "StorageClass", "name" : "'Storage Class'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugCompositeType", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Child'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "DebugOperation", "name" : "'OpCode'" }, + { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugImportedEntity", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugModuleINTEL", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'ConfigurationMacros'" }, + { "kind" : "IdRef", "name" : "'IncludePath'" }, + { "kind" : "IdRef", "name" : "'APINotesFile'" }, + { "kind" : "LiteralInteger", "name" : "'IsDeclaration'" } + ], + "capability" : "DebugInfoModuleINTEL" + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + }, + { + "enumerant" : "FlagIsEnumClass", + "value" : "0x4000" + }, + { + "enumerant" : "FlagTypePassByValue", + "value" : "0x8000" + }, + { + "enumerant" : "FlagTypePassByReference", + "value" : "0x10000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "3" + }, + { + "enumerant" : "Signed", + "value" : "4" + }, + { + "enumerant" : "SignedChar", + "value" : "5" + }, + { + "enumerant" : "Unsigned", + "value" : "6" + }, + { + "enumerant" : "UnsignedChar", + "value" : "7" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + }, + { + "enumerant" : "AtomicType", + "value" : "3" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json new file mode 100644 index 00000000..21b7876b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json new file mode 100644 index 00000000..e18251bb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json @@ -0,0 +1,26 @@ +{ + "revision" : 2, + "instructions" : [ + { + "opname" : "CubeFaceIndexAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'P'" } + ], + "extensions" : [ "SPV_AMD_gcn_shader" ] + }, + { + "opname" : "CubeFaceCoordAMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'P'" } + ], + "extensions" : [ "SPV_AMD_gcn_shader" ] + }, + { + "opname" : "TimeAMD", + "opcode" : 3, + "extensions" : [ "SPV_AMD_gcn_shader" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json new file mode 100644 index 00000000..62a470ee --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json @@ -0,0 +1,41 @@ +{ + "revision" : 5, + "instructions" : [ + { + "opname" : "SwizzleInvocationsAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "SwizzleInvocationsMaskedAMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'mask'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "WriteInvocationAMD", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'inputValue'" }, + { "kind" : "IdRef", "name" : "'writeValue'" }, + { "kind" : "IdRef", "name" : "'invocationIndex'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "MbcntAMD", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'mask'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json new file mode 100644 index 00000000..e156b1b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json @@ -0,0 +1,14 @@ +{ + "revision" : 4, + "instructions" : [ + { + "opname" : "InterpolateAtVertexAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'vertexIdx'" } + ], + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json new file mode 100644 index 00000000..c681976f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json @@ -0,0 +1,95 @@ +{ + "revision" : 4, + "instructions" : [ + { + "opname" : "FMin3AMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMin3AMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMin3AMD", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "FMax3AMD", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMax3AMD", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMax3AMD", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "FMid3AMD", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMid3AMD", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMid3AMD", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.bf b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.bf new file mode 100644 index 00000000..2fca6be6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.bf @@ -0,0 +1,2463 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + using System; + + public static class Specification + { + public const uint32 MagicNumber = 0x07230203; + public const uint32 Version = 0x00010600; + public const uint32 Revision = 1; + public const uint32 OpCodeMask = 0xffff; + public const uint32 WordCountShift = 16; + + [AllowDuplicates, CRepr] public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + CPP_for_OpenCL = 6, + SYCL = 7, + HERO_C = 8, + NZSL = 9, + WGSL = 10, + Slang = 11, + Zig = 12, + Rust = 13, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + TaskEXT = 5364, + MeshEXT = 5365, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + NonCoherentColorAttachmentReadEXT = 4169, + NonCoherentDepthAttachmentReadEXT = 4170, + NonCoherentStencilAttachmentReadEXT = 4171, + SubgroupUniformControlFlowKHR = 4421, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + NonCoherentTileAttachmentReadQCOM = 4489, + TileShadingRateQCOM = 4490, + EarlyAndLateFragmentTestsAMD = 5017, + StencilRefReplacingEXT = 5027, + CoalescingAMDX = 5069, + IsApiEntryAMDX = 5070, + MaxNodeRecursionAMDX = 5071, + StaticNumWorkgroupsAMDX = 5072, + ShaderIndexAMDX = 5073, + MaxNumWorkgroupsAMDX = 5077, + StencilRefUnchangedFrontAMD = 5079, + StencilRefGreaterFrontAMD = 5080, + StencilRefLessFrontAMD = 5081, + StencilRefUnchangedBackAMD = 5082, + StencilRefGreaterBackAMD = 5083, + StencilRefLessBackAMD = 5084, + QuadDerivativesKHR = 5088, + RequireFullQuadsKHR = 5089, + SharesInputWithAMDX = 5102, + OutputLinesEXT = 5269, + OutputLinesNV = 5269, + OutputPrimitivesEXT = 5270, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsKHR = 5289, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearKHR = 5290, + DerivativeGroupLinearNV = 5290, + OutputTrianglesEXT = 5298, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, + MaximallyReconvergesKHR = 6023, + FPFastMathDefault = 6028, + StreamingInterfaceINTEL = 6154, + RegisterMapInterfaceINTEL = 6160, + NamedBarrierCountINTEL = 6417, + MaximumRegistersINTEL = 6461, + MaximumRegistersIdINTEL = 6462, + NamedMaximumRegistersINTEL = 6463, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + TileImageEXT = 4172, + TileAttachmentQCOM = 4491, + NodePayloadAMDX = 5068, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + HitObjectAttributeNV = 5385, + TaskPayloadWorkgroupEXT = 5402, + CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + TileImageDataEXT = 4173, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + UnormInt10X6EXT = 17, + UnsignedIntRaw10EXT = 19, + UnsignedIntRaw12EXT = 20, + UnormInt2_101010EXT = 21, + UnsignedInt10X6EXT = 22, + UnsignedInt12X4EXT = 23, + UnsignedInt14X2EXT = 24, + UnormInt12X4EXT = 25, + UnormInt14X2EXT = 26, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Nontemporal = 14, + Offsets = 16, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + Nontemporal = 0x00004000, + Offsets = 0x00010000, + } + + [AllowDuplicates, CRepr] public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + AllowContract = 16, + AllowContractFastINTEL = 16, + AllowReassoc = 17, + AllowReassocINTEL = 17, + AllowTransform = 18, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + AllowContract = 0x00010000, + AllowContractFastINTEL = 0x00010000, + AllowReassoc = 0x00020000, + AllowReassocINTEL = 0x00020000, + AllowTransform = 0x00040000, + } + + [AllowDuplicates, CRepr] public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum LinkageType + { + Export = 0, + Import = 1, + LinkOnceODR = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + RuntimeAlignedINTEL = 5940, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + WeightTextureQCOM = 4487, + BlockMatchTextureQCOM = 4488, + BlockMatchSamplerQCOM = 4499, + ExplicitInterpAMD = 4999, + NodeSharesPayloadLimitsWithAMDX = 5019, + NodeMaxPayloadsAMDX = 5020, + TrackFinishWritingAMDX = 5078, + PayloadNodeNameAMDX = 5091, + PayloadNodeBaseIndexAMDX = 5098, + PayloadNodeSparseArrayAMDX = 5099, + PayloadNodeArraySizeAMDX = 5100, + PayloadDispatchIndirectAMDX = 5105, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveEXT = 5271, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexKHR = 5285, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + HitObjectShaderRecordBufferNV = 5386, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, + SIMTCallINTEL = 5599, + ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + StridesizeINTEL = 5883, + WordsizeINTEL = 5884, + TrueDualPortINTEL = 5885, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + MathOpDSPModeINTEL = 5909, + AliasScopeINTEL = 5914, + NoAliasINTEL = 5915, + InitiationIntervalINTEL = 5917, + MaxConcurrencyINTEL = 5918, + PipelineEnableINTEL = 5919, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, + MediaBlockIOINTEL = 6140, + StallFreeINTEL = 6151, + FPMaxErrorDecorationINTEL = 6170, + LatencyControlLabelINTEL = 6172, + LatencyControlConstraintINTEL = 6173, + ConduitKernelArgumentINTEL = 6175, + RegisterMapKernelArgumentINTEL = 6176, + MMHostInterfaceAddressWidthINTEL = 6177, + MMHostInterfaceDataWidthINTEL = 6178, + MMHostInterfaceLatencyINTEL = 6179, + MMHostInterfaceReadWriteModeINTEL = 6180, + MMHostInterfaceMaxBurstINTEL = 6181, + MMHostInterfaceWaitRequestINTEL = 6182, + StableKernelArgumentINTEL = 6183, + HostAccessINTEL = 6188, + InitModeINTEL = 6190, + ImplementInRegisterMapINTEL = 6191, + CacheControlLoadINTEL = 6442, + CacheControlStoreINTEL = 6443, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + CoreIDARM = 4160, + CoreCountARM = 4161, + CoreMaxIDARM = 4162, + WarpIDARM = 4163, + WarpMaxIDARM = 4164, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + TileOffsetQCOM = 4492, + TileDimensionQCOM = 4493, + TileApronSizeQCOM = 4494, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + RemainingRecursionLevelsAMDX = 5021, + ShaderIndexAMDX = 5073, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordKHR = 5286, + BaryCoordNV = 5286, + BaryCoordNoPerspKHR = 5287, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + PrimitivePointIndicesEXT = 5294, + PrimitiveLineIndicesEXT = 5295, + PrimitiveTriangleIndicesEXT = 5296, + CullPrimitiveEXT = 5299, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + CurrentRayTimeNV = 5334, + HitTriangleVertexPositionsKHR = 5335, + HitMicroTriangleVertexPositionsNV = 5337, + HitMicroTriangleVertexBarycentricsNV = 5344, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + HitIsSphereNV = 5359, + HitIsLSSNV = 5360, + HitSpherePositionNV = 5361, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + HitLSSPositionsNV = 5396, + HitKindFrontFacingMicroTriangleNV = 5405, + HitKindBackFacingMicroTriangleNV = 5406, + HitSphereRadiusNV = 5420, + HitLSSRadiiNV = 5421, + ClusterIDNV = 5436, + CullMaskKHR = 6021, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + [AllowDuplicates, CRepr] public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + NoFusionINTEL = 23, + LoopCountINTEL = 24, + MaxReinvocationDelayINTEL = 25, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + NoFusionINTEL = 0x00800000, + LoopCountINTEL = 0x01000000, + MaxReinvocationDelayINTEL = 0x02000000, + } + + [AllowDuplicates, CRepr] public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + OptNoneEXT = 16, + OptNoneINTEL = 16, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + OptNoneEXT = 0x00010000, + OptNoneINTEL = 0x00010000, + } + + [AllowDuplicates, CRepr] public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, + } + + [AllowDuplicates, CRepr] public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + AliasScopeINTELMask = 16, + NoAliasINTELMask = 17, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + AliasScopeINTELMask = 0x00010000, + NoAliasINTELMask = 0x00020000, + } + + [AllowDuplicates, CRepr] public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + [AllowDuplicates, CRepr] public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + UniformDecoration = 71, + CoreBuiltinsARM = 4165, + TileImageColorReadAccessEXT = 4166, + TileImageDepthReadAccessEXT = 4167, + TileImageStencilReadAccessEXT = 4168, + CooperativeMatrixLayoutsARM = 4201, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + UntypedPointersKHR = 4473, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + TextureSampleWeightedQCOM = 4484, + TextureBoxFilterQCOM = 4485, + TextureBlockMatchQCOM = 4486, + TileShadingQCOM = 4495, + TextureBlockMatch2QCOM = 4498, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + ShaderEnqueueAMDX = 5067, + QuadControlKHR = 5087, + BFloat16TypeKHR = 5116, + BFloat16DotProductKHR = 5117, + BFloat16CooperativeMatrixKHR = 5118, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + MeshShadingEXT = 5283, + FragmentBarycentricKHR = 5284, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsKHR = 5288, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingPositionFetchKHR = 5336, + RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearKHR = 5350, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocation = 5379, + DemoteToHelperInvocationEXT = 5379, + DisplacementMicromapNV = 5380, + RayTracingOpacityMicromapEXT = 5381, + ShaderInvocationReorderNV = 5383, + BindlessTextureNV = 5390, + RayQueryPositionFetchKHR = 5391, + CooperativeVectorNV = 5394, + AtomicFloat16VectorNV = 5404, + RayTracingDisplacementMicromapNV = 5409, + RawAccessChainsNV = 5414, + RayTracingSpheresGeometryNV = 5418, + RayTracingLinearSweptSpheresGeometryNV = 5419, + CooperativeMatrixReductionsNV = 5430, + CooperativeMatrixConversionsNV = 5431, + CooperativeMatrixPerElementOperationsNV = 5432, + CooperativeMatrixTensorAddressingNV = 5433, + CooperativeMatrixBlockLoadsNV = 5434, + CooperativeVectorTrainingNV = 5435, + RayTracingClusterAccelerationStructureNV = 5437, + TensorAddressingNV = 5439, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, + FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, + ArbitraryPrecisionFloatingPointINTEL = 5845, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGADSPControlINTEL = 5908, + MemoryAccessAliasingINTEL = 5910, + FPGAInvocationPipeliningAttributesINTEL = 5916, + FPGABufferLocationINTEL = 5920, + ArbitraryPrecisionFixedPointINTEL = 5922, + USMStorageClassesINTEL = 5935, + RuntimeAlignedAttributeINTEL = 5939, + IOPipesINTEL = 5943, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + DotProductInputAll = 6016, + DotProductInputAllKHR = 6016, + DotProductInput4x8Bit = 6017, + DotProductInput4x8BitKHR = 6017, + DotProductInput4x8BitPacked = 6018, + DotProductInput4x8BitPackedKHR = 6018, + DotProduct = 6019, + DotProductKHR = 6019, + RayCullMaskKHR = 6020, + CooperativeMatrixKHR = 6022, + ReplicatedCompositesEXT = 6024, + BitInstructions = 6025, + GroupNonUniformRotateKHR = 6026, + FloatControls2 = 6029, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + LongCompositesINTEL = 6089, + OptNoneEXT = 6094, + OptNoneINTEL = 6094, + AtomicFloat16AddEXT = 6095, + DebugInfoModuleINTEL = 6114, + BFloat16ConversionINTEL = 6115, + SplitBarrierINTEL = 6141, + ArithmeticFenceEXT = 6144, + FPGAClusterAttributesV2INTEL = 6150, + FPGAKernelAttributesv2INTEL = 6161, + TaskSequenceINTEL = 6162, + FPMaxErrorINTEL = 6169, + FPGALatencyControlINTEL = 6171, + FPGAArgumentInterfacesINTEL = 6174, + GlobalVariableHostAccessINTEL = 6187, + GlobalVariableFPGADecorationsINTEL = 6189, + SubgroupBufferPrefetchINTEL = 6220, + Subgroup2DBlockIOINTEL = 6228, + Subgroup2DBlockTransformINTEL = 6229, + Subgroup2DBlockTransposeINTEL = 6230, + SubgroupMatrixMultiplyAccumulateINTEL = 6236, + TernaryBitwiseFunctionINTEL = 6241, + GroupUniformArithmeticKHR = 6400, + TensorFloat32RoundingINTEL = 6425, + MaskedGatherScatterINTEL = 6427, + CacheControlsINTEL = 6441, + RegisterLimitsINTEL = 6460, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum RayFlagsShift + { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipBuiltinPrimitivesNV = 8, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + ForceOpacityMicromap2StateEXT = 10, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum RayFlagsMask + { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipBuiltinPrimitivesNV = 0x00000100, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + ForceOpacityMicromap2StateEXT = 0x00000400, + } + + [AllowDuplicates, CRepr] public enum RayQueryIntersection + { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum RayQueryCommittedIntersectionType + { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum RayQueryCandidateIntersectionType + { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FragmentShadingRateShift + { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FragmentShadingRateMask + { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, + } + + [AllowDuplicates, CRepr] public enum FPDenormMode + { + Preserve = 0, + FlushToZero = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum FPOperationMode + { + IEEE = 0, + ALT = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum QuantizationModes + { + TRN = 0, + TRN_ZERO = 1, + RND = 2, + RND_ZERO = 3, + RND_INF = 4, + RND_MIN_INF = 5, + RND_CONV = 6, + RND_CONV_ODD = 7, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum OverflowModes + { + WRAP = 0, + SAT = 1, + SAT_ZERO = 2, + SAT_SYM = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum PackedVectorFormat + { + PackedVectorFormat4x8Bit = 0, + PackedVectorFormat4x8BitKHR = 0, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixOperandsShift + { + MatrixASignedComponentsKHR = 0, + MatrixBSignedComponentsKHR = 1, + MatrixCSignedComponentsKHR = 2, + MatrixResultSignedComponentsKHR = 3, + SaturatingAccumulationKHR = 4, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixOperandsMask + { + MaskNone = 0, + MatrixASignedComponentsKHR = 0x00000001, + MatrixBSignedComponentsKHR = 0x00000002, + MatrixCSignedComponentsKHR = 0x00000004, + MatrixResultSignedComponentsKHR = 0x00000008, + SaturatingAccumulationKHR = 0x00000010, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixLayout + { + RowMajorKHR = 0, + ColumnMajorKHR = 1, + RowBlockedInterleavedARM = 4202, + ColumnBlockedInterleavedARM = 4203, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixUse + { + MatrixAKHR = 0, + MatrixBKHR = 1, + MatrixAccumulatorKHR = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixReduceShift + { + Row = 0, + Column = 1, + CooperativeMatrixReduce2x2 = 2, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeMatrixReduceMask + { + MaskNone = 0, + Row = 0x00000001, + Column = 0x00000002, + CooperativeMatrixReduce2x2 = 0x00000004, + } + + [AllowDuplicates, CRepr] public enum TensorClampMode + { + Undefined = 0, + Constant = 1, + ClampToEdge = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum TensorAddressingOperandsShift + { + TensorView = 0, + DecodeFunc = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum TensorAddressingOperandsMask + { + MaskNone = 0, + TensorView = 0x00000001, + DecodeFunc = 0x00000002, + } + + [AllowDuplicates, CRepr] public enum InitializationModeQualifier + { + InitOnDeviceReprogramINTEL = 0, + InitOnDeviceResetINTEL = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum HostAccessQualifier + { + NoneINTEL = 0, + ReadINTEL = 1, + WriteINTEL = 2, + ReadWriteINTEL = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum LoadCacheControl + { + UncachedINTEL = 0, + CachedINTEL = 1, + StreamingINTEL = 2, + InvalidateAfterReadINTEL = 3, + ConstCachedINTEL = 4, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum StoreCacheControl + { + UncachedINTEL = 0, + WriteThroughINTEL = 1, + WriteBackINTEL = 2, + StreamingINTEL = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum NamedMaximumNumberOfRegisters + { + AutoINTEL = 0, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum MatrixMultiplyAccumulateOperandsShift + { + MatrixASignedComponentsINTEL = 0, + MatrixBSignedComponentsINTEL = 1, + MatrixCBFloat16INTEL = 2, + MatrixResultBFloat16INTEL = 3, + MatrixAPackedInt8INTEL = 4, + MatrixBPackedInt8INTEL = 5, + MatrixAPackedInt4INTEL = 6, + MatrixBPackedInt4INTEL = 7, + MatrixATF32INTEL = 8, + MatrixBTF32INTEL = 9, + MatrixAPackedFloat16INTEL = 10, + MatrixBPackedFloat16INTEL = 11, + MatrixAPackedBFloat16INTEL = 12, + MatrixBPackedBFloat16INTEL = 13, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum MatrixMultiplyAccumulateOperandsMask + { + MaskNone = 0, + MatrixASignedComponentsINTEL = 0x00000001, + MatrixBSignedComponentsINTEL = 0x00000002, + MatrixCBFloat16INTEL = 0x00000004, + MatrixResultBFloat16INTEL = 0x00000008, + MatrixAPackedInt8INTEL = 0x00000010, + MatrixBPackedInt8INTEL = 0x00000020, + MatrixAPackedInt4INTEL = 0x00000040, + MatrixBPackedInt4INTEL = 0x00000080, + MatrixATF32INTEL = 0x00000100, + MatrixBTF32INTEL = 0x00000200, + MatrixAPackedFloat16INTEL = 0x00000400, + MatrixBPackedFloat16INTEL = 0x00000800, + MatrixAPackedBFloat16INTEL = 0x00001000, + MatrixBPackedBFloat16INTEL = 0x00002000, + } + + [AllowDuplicates, CRepr] public enum RawAccessChainOperandsShift + { + RobustnessPerComponentNV = 0, + RobustnessPerElementNV = 1, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum RawAccessChainOperandsMask + { + MaskNone = 0, + RobustnessPerComponentNV = 0x00000001, + RobustnessPerElementNV = 0x00000002, + } + + [AllowDuplicates, CRepr] public enum FPEncoding + { + BFloat16KHR = 0, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum CooperativeVectorMatrixLayout + { + RowMajorNV = 0, + ColumnMajorNV = 1, + InferencingOptimalNV = 2, + TrainingOptimalNV = 3, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum ComponentType + { + Float16NV = 0, + Float32NV = 1, + Float64NV = 2, + SignedInt8NV = 3, + SignedInt16NV = 4, + SignedInt32NV = 5, + SignedInt64NV = 6, + UnsignedInt8NV = 7, + UnsignedInt16NV = 8, + UnsignedInt32NV = 9, + UnsignedInt64NV = 10, + SignedInt8PackedNV = 1000491000, + UnsignedInt8PackedNV = 1000491001, + FloatE4M3NV = 1000491002, + FloatE5M2NV = 1000491003, + Max = 0x7fffffff, + } + + [AllowDuplicates, CRepr] public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + Max = 0x7fffffff, + } + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json new file mode 100644 index 00000000..1350da46 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json @@ -0,0 +1,17983 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 6, + "revision" : 4, + "instruction_printing_class" : [ + { + "tag" : "@exclude" + }, + { + "tag" : "Miscellaneous", + "heading" : "Miscellaneous Instructions" + }, + { + "tag" : "Debug", + "heading" : "Debug Instructions" + }, + { + "tag" : "Annotation", + "heading" : "Annotation Instructions" + }, + { + "tag" : "Extension", + "heading" : "Extension Instructions" + }, + { + "tag" : "Mode-Setting", + "heading" : "Mode-Setting Instructions" + }, + { + "tag" : "Type-Declaration", + "heading" : "Type-Declaration Instructions" + }, + { + "tag" : "Constant-Creation", + "heading" : "Constant-Creation Instructions" + }, + { + "tag" : "Memory", + "heading" : "Memory Instructions" + }, + { + "tag" : "Function", + "heading" : "Function Instructions" + }, + { + "tag" : "Image", + "heading" : "Image Instructions" + }, + { + "tag" : "Conversion", + "heading" : "Conversion Instructions" + }, + { + "tag" : "Composite", + "heading" : "Composite Instructions" + }, + { + "tag" : "Arithmetic", + "heading" : "Arithmetic Instructions" + }, + { + "tag" : "Bit", + "heading" : "Bit Instructions" + }, + { + "tag" : "Relational_and_Logical", + "heading" : "Relational and Logical Instructions" + }, + { + "tag" : "Derivative", + "heading" : "Derivative Instructions" + }, + { + "tag" : "Control-Flow", + "heading" : "Control-Flow Instructions" + }, + { + "tag" : "Atomic", + "heading" : "Atomic Instructions" + }, + { + "tag" : "Primitive", + "heading" : "Primitive Instructions" + }, + { + "tag" : "Barrier", + "heading" : "Barrier Instructions" + }, + { + "tag" : "Group", + "heading" : "Group and Subgroup Instructions" + }, + { + "tag" : "Device-Side_Enqueue", + "heading" : "Device-Side Enqueue Instructions" + }, + { + "tag" : "Pipe", + "heading" : "Pipe Instructions" + }, + { + "tag" : "Non-Uniform", + "heading" : "Non-Uniform Instructions" + }, + { + "tag" : "Reserved", + "heading" : "Reserved Instructions" + } + ], + "instructions" : [ + { + "opname" : "OpNop", + "class" : "Miscellaneous", + "opcode" : 0, + "version" : "1.0" + }, + { + "opname" : "OpUndef", + "class" : "Miscellaneous", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version" : "1.0" + }, + { + "opname" : "OpSourceContinued", + "class" : "Debug", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSource", + "class" : "Debug", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSourceExtension", + "class" : "Debug", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ], + "version": "1.0" + }, + { + "opname" : "OpName", + "class" : "Debug", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ], + "version": "1.0" + }, + { + "opname" : "OpMemberName", + "class" : "Debug", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ], + "version": "1.0" + }, + { + "opname" : "OpString", + "class" : "Debug", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLine", + "class" : "Debug", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ], + "version": "1.0" + }, + { + "opname" : "OpExtension", + "class" : "Extension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ], + "version": "1.0" + }, + { + "opname" : "OpExtInstImport", + "class" : "Extension", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ], + "version": "1.0" + }, + { + "opname" : "OpExtInst", + "class" : "Extension", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ], + "version": "1.0" + }, + { + "opname" : "OpMemoryModel", + "class" : "Mode-Setting", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ], + "version": "1.0" + }, + { + "opname" : "OpEntryPoint", + "class" : "Mode-Setting", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ], + "version": "1.0" + }, + { + "opname" : "OpExecutionMode", + "class" : "Mode-Setting", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ], + "version": "1.0" + }, + { + "opname" : "OpCapability", + "class" : "Mode-Setting", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeVoid", + "class" : "Type-Declaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeBool", + "class" : "Type-Declaration", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeInt", + "class" : "Type-Declaration", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeFloat", + "class" : "Type-Declaration", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "FPEncoding", "quantifier" : "?", "name" : "'Floating Point Encoding'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeVector", + "class" : "Type-Declaration", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeMatrix", + "class" : "Type-Declaration", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpTypeImage", + "class" : "Type-Declaration", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeSampler", + "class" : "Type-Declaration", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeSampledImage", + "class" : "Type-Declaration", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeArray", + "class" : "Type-Declaration", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeRuntimeArray", + "class" : "Type-Declaration", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpTypeStruct", + "class" : "Type-Declaration", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeOpaque", + "class" : "Type-Declaration", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpTypePointer", + "class" : "Type-Declaration", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeFunction", + "class" : "Type-Declaration", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ], + "version": "1.0" + }, + { + "opname" : "OpTypeEvent", + "class" : "Type-Declaration", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpTypeDeviceEvent", + "class" : "Type-Declaration", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpTypeReserveId", + "class" : "Type-Declaration", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpTypeQueue", + "class" : "Type-Declaration", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpTypePipe", + "class" : "Type-Declaration", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpTypeForwardPointer", + "class" : "Type-Declaration", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ], + "version": "1.0" + }, + { + "opname" : "OpConstantTrue", + "class" : "Constant-Creation", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpConstantFalse", + "class" : "Constant-Creation", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpConstant", + "class" : "Constant-Creation", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConstantComposite", + "class" : "Constant-Creation", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConstantSampler", + "class" : "Constant-Creation", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ], + "version": "1.0" + }, + { + "opname" : "OpConstantNull", + "class" : "Constant-Creation", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpSpecConstantTrue", + "class" : "Constant-Creation", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpSpecConstantFalse", + "class" : "Constant-Creation", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpSpecConstant", + "class" : "Constant-Creation", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSpecConstantComposite", + "class" : "Constant-Creation", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSpecConstantOp", + "class" : "Constant-Creation", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFunction", + "class" : "Function", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFunctionParameter", + "class" : "Function", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpFunctionEnd", + "class" : "Function", + "opcode" : 56, + "version" : "1.0" + }, + { + "opname" : "OpFunctionCall", + "class" : "Function", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ], + "version": "1.0" + }, + { + "opname" : "OpVariable", + "class" : "Memory", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageTexelPointer", + "class" : "Memory", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLoad", + "class" : "Memory", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpStore", + "class" : "Memory", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpCopyMemory", + "class" : "Memory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpCopyMemorySized", + "class" : "Memory", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ + "Addresses", + "UntypedPointersKHR" + ], + "version": "1.0" + }, + { + "opname" : "OpAccessChain", + "class" : "Memory", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "version": "1.0" + }, + { + "opname" : "OpInBoundsAccessChain", + "class" : "Memory", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "version": "1.0" + }, + { + "opname" : "OpPtrAccessChain", + "class" : "Memory", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer", + "PhysicalStorageBufferAddresses" + ], + "version": "1.0" + }, + { + "opname" : "OpArrayLength", + "class" : "Memory", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpGenericPtrMemSemantics", + "class" : "Memory", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "class" : "Memory", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ], + "version": "1.0" + }, + { + "opname" : "OpDecorate", + "class" : "Annotation", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "version": "1.0" + }, + { + "opname" : "OpMemberDecorate", + "class" : "Annotation", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "version": "1.0" + }, + { + "opname" : "OpDecorationGroup", + "class" : "Annotation", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpGroupDecorate", + "class" : "Annotation", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ], + "version": "1.0" + }, + { + "opname" : "OpGroupMemberDecorate", + "class" : "Annotation", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ], + "version": "1.0" + }, + { + "opname" : "OpVectorExtractDynamic", + "class" : "Composite", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "version": "1.0" + }, + { + "opname" : "OpVectorInsertDynamic", + "class" : "Composite", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "version": "1.0" + }, + { + "opname" : "OpVectorShuffle", + "class" : "Composite", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ], + "version": "1.0" + }, + { + "opname" : "OpCompositeConstruct", + "class" : "Composite", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "version": "1.0" + }, + { + "opname" : "OpCompositeExtract", + "class" : "Composite", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ], + "version": "1.0" + }, + { + "opname" : "OpCompositeInsert", + "class" : "Composite", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ], + "version": "1.0" + }, + { + "opname" : "OpCopyObject", + "class" : "Composite", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpTranspose", + "class" : "Composite", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpSampledImage", + "class" : "Image", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleImplicitLod", + "class" : "Image", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleExplicitLod", + "class" : "Image", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "class" : "Image", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "class" : "Image", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "class" : "Image", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "class" : "Image", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "class" : "Image", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "class" : "Image", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageFetch", + "class" : "Image", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageGather", + "class" : "Image", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageDrefGather", + "class" : "Image", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpImageRead", + "class" : "Image", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageWrite", + "class" : "Image", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "version": "1.0" + }, + { + "opname" : "OpImage", + "class" : "Image", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ], + "version": "1.0" + }, + { + "opname" : "OpImageQueryFormat", + "class" : "Image", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpImageQueryOrder", + "class" : "Image", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpImageQuerySizeLod", + "class" : "Image", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ], + "version": "1.0" + }, + { + "opname" : "OpImageQuerySize", + "class" : "Image", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ], + "version": "1.0" + }, + { + "opname" : "OpImageQueryLod", + "class" : "Image", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ], + "version": "1.0" + }, + { + "opname" : "OpImageQueryLevels", + "class" : "Image", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ], + "version": "1.0" + }, + { + "opname" : "OpImageQuerySamples", + "class" : "Image", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ], + "version": "1.0" + }, + { + "opname" : "OpConvertFToU", + "class" : "Conversion", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConvertFToS", + "class" : "Conversion", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConvertSToF", + "class" : "Conversion", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConvertUToF", + "class" : "Conversion", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUConvert", + "class" : "Conversion", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSConvert", + "class" : "Conversion", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFConvert", + "class" : "Conversion", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpQuantizeToF16", + "class" : "Conversion", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpConvertPtrToU", + "class" : "Conversion", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ], + "version": "1.0" + }, + { + "opname" : "OpSatConvertSToU", + "class" : "Conversion", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpSatConvertUToS", + "class" : "Conversion", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpConvertUToPtr", + "class" : "Conversion", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ], + "version": "1.0" + }, + { + "opname" : "OpPtrCastToGeneric", + "class" : "Conversion", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpGenericCastToPtr", + "class" : "Conversion", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "class" : "Conversion", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpBitcast", + "class" : "Conversion", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSNegate", + "class" : "Arithmetic", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFNegate", + "class" : "Arithmetic", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIAdd", + "class" : "Arithmetic", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFAdd", + "class" : "Arithmetic", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpISub", + "class" : "Arithmetic", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFSub", + "class" : "Arithmetic", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIMul", + "class" : "Arithmetic", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFMul", + "class" : "Arithmetic", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUDiv", + "class" : "Arithmetic", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSDiv", + "class" : "Arithmetic", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFDiv", + "class" : "Arithmetic", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUMod", + "class" : "Arithmetic", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSRem", + "class" : "Arithmetic", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSMod", + "class" : "Arithmetic", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFRem", + "class" : "Arithmetic", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFMod", + "class" : "Arithmetic", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpVectorTimesScalar", + "class" : "Arithmetic", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "version": "1.0" + }, + { + "opname" : "OpMatrixTimesScalar", + "class" : "Arithmetic", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpVectorTimesMatrix", + "class" : "Arithmetic", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpMatrixTimesVector", + "class" : "Arithmetic", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpMatrixTimesMatrix", + "class" : "Arithmetic", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpOuterProduct", + "class" : "Arithmetic", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "opname" : "OpDot", + "class" : "Arithmetic", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIAddCarry", + "class" : "Arithmetic", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpISubBorrow", + "class" : "Arithmetic", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUMulExtended", + "class" : "Arithmetic", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSMulExtended", + "class" : "Arithmetic", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAny", + "class" : "Relational_and_Logical", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAll", + "class" : "Relational_and_Logical", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIsNan", + "class" : "Relational_and_Logical", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIsInf", + "class" : "Relational_and_Logical", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIsFinite", + "class" : "Relational_and_Logical", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpIsNormal", + "class" : "Relational_and_Logical", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpSignBitSet", + "class" : "Relational_and_Logical", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpLessOrGreater", + "class" : "Relational_and_Logical", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ], + "version" : "1.0", + "lastVersion" : "1.5" + }, + { + "opname" : "OpOrdered", + "class" : "Relational_and_Logical", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpUnordered", + "class" : "Relational_and_Logical", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpLogicalEqual", + "class" : "Relational_and_Logical", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLogicalNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLogicalOr", + "class" : "Relational_and_Logical", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLogicalAnd", + "class" : "Relational_and_Logical", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version" : "1.0" + }, + { + "opname" : "OpLogicalNot", + "class" : "Relational_and_Logical", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSelect", + "class" : "Relational_and_Logical", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpIEqual", + "class" : "Relational_and_Logical", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpINotEqual", + "class" : "Relational_and_Logical", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpULessThan", + "class" : "Relational_and_Logical", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSLessThan", + "class" : "Relational_and_Logical", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpULessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdEqual", + "class" : "Relational_and_Logical", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordEqual", + "class" : "Relational_and_Logical", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdLessThan", + "class" : "Relational_and_Logical", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordLessThan", + "class" : "Relational_and_Logical", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpShiftRightLogical", + "class" : "Bit", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ], + "version": "1.0" + }, + { + "opname" : "OpShiftRightArithmetic", + "class" : "Bit", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ], + "version": "1.0" + }, + { + "opname" : "OpShiftLeftLogical", + "class" : "Bit", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ], + "version": "1.0" + }, + { + "opname" : "OpBitwiseOr", + "class" : "Bit", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpBitwiseXor", + "class" : "Bit", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpBitwiseAnd", + "class" : "Bit", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version": "1.0" + }, + { + "opname" : "OpNot", + "class" : "Bit", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version": "1.0" + }, + { + "opname" : "OpBitFieldInsert", + "class" : "Bit", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader", "BitInstructions" ], + "version": "1.0" + }, + { + "opname" : "OpBitFieldSExtract", + "class" : "Bit", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader", "BitInstructions" ], + "version": "1.0" + }, + { + "opname" : "OpBitFieldUExtract", + "class" : "Bit", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader", "BitInstructions" ], + "version": "1.0" + }, + { + "opname" : "OpBitReverse", + "class" : "Bit", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader", "BitInstructions" ], + "version": "1.0" + }, + { + "opname" : "OpBitCount", + "class" : "Bit", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "version": "1.0" + }, + { + "opname" : "OpDPdx", + "class" : "Derivative", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpDPdy", + "class" : "Derivative", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpFwidth", + "class" : "Derivative", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpDPdxFine", + "class" : "Derivative", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpDPdyFine", + "class" : "Derivative", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpFwidthFine", + "class" : "Derivative", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpDPdxCoarse", + "class" : "Derivative", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpDPdyCoarse", + "class" : "Derivative", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpFwidthCoarse", + "class" : "Derivative", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ], + "version": "1.0" + }, + { + "opname" : "OpEmitVertex", + "class" : "Primitive", + "opcode" : 218, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "opname" : "OpEndPrimitive", + "class" : "Primitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "opname" : "OpEmitStreamVertex", + "class" : "Primitive", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ], + "version": "1.0" + }, + { + "opname" : "OpEndStreamPrimitive", + "class" : "Primitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ], + "version": "1.0" + }, + { + "opname" : "OpControlBarrier", + "class" : "Barrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "version": "1.0" + }, + { + "opname" : "OpMemoryBarrier", + "class" : "Barrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicLoad", + "class" : "Atomic", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicStore", + "class" : "Atomic", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicExchange", + "class" : "Atomic", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicCompareExchange", + "class" : "Atomic", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "class" : "Atomic", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ], + "version" : "1.0", + "lastVersion" : "1.3" + }, + { + "opname" : "OpAtomicIIncrement", + "class" : "Atomic", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicIDecrement", + "class" : "Atomic", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicIAdd", + "class" : "Atomic", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicISub", + "class" : "Atomic", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicSMin", + "class" : "Atomic", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicUMin", + "class" : "Atomic", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicSMax", + "class" : "Atomic", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicUMax", + "class" : "Atomic", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicAnd", + "class" : "Atomic", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicOr", + "class" : "Atomic", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpAtomicXor", + "class" : "Atomic", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpPhi", + "class" : "Control-Flow", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ], + "version": "1.0" + }, + { + "opname" : "OpLoopMerge", + "class" : "Control-Flow", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ], + "version": "1.0" + }, + { + "opname" : "OpSelectionMerge", + "class" : "Control-Flow", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ], + "version": "1.0" + }, + { + "opname" : "OpLabel", + "class" : "Control-Flow", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ], + "version": "1.0" + }, + { + "opname" : "OpBranch", + "class" : "Control-Flow", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ], + "version": "1.0" + }, + { + "opname" : "OpBranchConditional", + "class" : "Control-Flow", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ], + "version": "1.0" + }, + { + "opname" : "OpSwitch", + "class" : "Control-Flow", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ], + "version": "1.0" + }, + { + "opname" : "OpKill", + "class" : "Control-Flow", + "opcode" : 252, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "opname" : "OpReturn", + "class" : "Control-Flow", + "opcode" : 253, + "version" : "1.0" + }, + { + "opname" : "OpReturnValue", + "class" : "Control-Flow", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ], + "version": "1.0" + }, + { + "opname" : "OpUnreachable", + "class" : "Control-Flow", + "opcode" : 255, + "version" : "1.0" + }, + { + "opname" : "OpLifetimeStart", + "class" : "Control-Flow", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpLifetimeStop", + "class" : "Control-Flow", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpGroupAsyncCopy", + "class" : "Group", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpGroupWaitEvents", + "class" : "Group", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpGroupAll", + "class" : "Group", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupAny", + "class" : "Group", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupBroadcast", + "class" : "Group", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupIAdd", + "class" : "Group", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupFAdd", + "class" : "Group", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupFMin", + "class" : "Group", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupUMin", + "class" : "Group", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupSMin", + "class" : "Group", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupFMax", + "class" : "Group", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupUMax", + "class" : "Group", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpGroupSMax", + "class" : "Group", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "version": "1.0" + }, + { + "opname" : "OpReadPipe", + "class" : "Pipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpWritePipe", + "class" : "Pipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpReservedReadPipe", + "class" : "Pipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpReservedWritePipe", + "class" : "Pipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpReserveReadPipePackets", + "class" : "Pipe", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpReserveWritePipePackets", + "class" : "Pipe", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpCommitReadPipe", + "class" : "Pipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpCommitWritePipe", + "class" : "Pipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpIsValidReserveId", + "class" : "Pipe", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGetNumPipePackets", + "class" : "Pipe", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGetMaxPipePackets", + "class" : "Pipe", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "class" : "Pipe", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "class" : "Pipe", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGroupCommitReadPipe", + "class" : "Pipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpGroupCommitWritePipe", + "class" : "Pipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ], + "version": "1.0" + }, + { + "opname" : "OpEnqueueMarker", + "class" : "Device-Side_Enqueue", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpEnqueueKernel", + "class" : "Device-Side_Enqueue", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "class" : "Device-Side_Enqueue", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "class" : "Device-Side_Enqueue", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "class" : "Device-Side_Enqueue", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "class" : "Device-Side_Enqueue", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpRetainEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpReleaseEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpCreateUserEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpIsValidEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpSetUserEventStatus", + "class" : "Device-Side_Enqueue", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "class" : "Device-Side_Enqueue", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpGetDefaultQueue", + "class" : "Device-Side_Enqueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpBuildNDRange", + "class" : "Device-Side_Enqueue", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "class" : "Image", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "class" : "Image", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "class" : "Image", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "class" : "Image", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "class" : "Image", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "class" : "Image", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "class" : "Image", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "class" : "Image", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseFetch", + "class" : "Image", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseGather", + "class" : "Image", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseDrefGather", + "class" : "Image", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseTexelsResident", + "class" : "Image", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpNoLine", + "class" : "Debug", + "opcode" : 317, + "version" : "1.0" + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "class" : "Atomic", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpAtomicFlagClear", + "class" : "Atomic", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "opname" : "OpImageSparseRead", + "class" : "Image", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version": "1.0" + }, + { + "opname" : "OpSizeOf", + "class" : "Miscellaneous", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ], + "version" : "1.1" + }, + { + "opname" : "OpTypePipeStorage", + "class" : "Type-Declaration", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpConstantPipeStorage", + "class" : "Pipe", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "class" : "Pipe", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "class" : "Device-Side_Enqueue", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ], + "version" : "1.1" + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "class" : "Device-Side_Enqueue", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ], + "version" : "1.1" + }, + { + "opname" : "OpTypeNamedBarrier", + "class" : "Type-Declaration", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpNamedBarrierInitialize", + "class" : "Barrier", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpMemoryNamedBarrier", + "class" : "Barrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpModuleProcessed", + "class" : "Debug", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ], + "version" : "1.1" + }, + { + "opname" : "OpExecutionModeId", + "class" : "Mode-Setting", + "opcode" : 331, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ], + "version" : "1.2" + }, + { + "opname" : "OpDecorateId", + "class" : "Annotation", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.2" + }, + { + "opname" : "OpGroupNonUniformElect", + "class" : "Non-Uniform", + "opcode" : 333, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" } + ], + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAll", + "class" : "Non-Uniform", + "opcode" : 334, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAny", + "class" : "Non-Uniform", + "opcode" : 335, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAllEqual", + "class" : "Non-Uniform", + "opcode" : 336, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBroadcast", + "class" : "Non-Uniform", + "opcode" : 337, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Id'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBroadcastFirst", + "class" : "Non-Uniform", + "opcode" : 338, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallot", + "class" : "Non-Uniform", + "opcode" : 339, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformInverseBallot", + "class" : "Non-Uniform", + "opcode" : 340, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotBitExtract", + "class" : "Non-Uniform", + "opcode" : 341, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotBitCount", + "class" : "Non-Uniform", + "opcode" : 342, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotFindLSB", + "class" : "Non-Uniform", + "opcode" : 343, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotFindMSB", + "class" : "Non-Uniform", + "opcode" : 344, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffle", + "class" : "Non-Uniform", + "opcode" : 345, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Id'" } + ], + "capabilities" : [ "GroupNonUniformShuffle" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleXor", + "class" : "Non-Uniform", + "opcode" : 346, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Mask'" } + ], + "capabilities" : [ "GroupNonUniformShuffle" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleUp", + "class" : "Non-Uniform", + "opcode" : 347, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "GroupNonUniformShuffleRelative" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleDown", + "class" : "Non-Uniform", + "opcode" : 348, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "GroupNonUniformShuffleRelative" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformIAdd", + "class" : "Non-Uniform", + "opcode" : 349, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFAdd", + "class" : "Non-Uniform", + "opcode" : 350, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformIMul", + "class" : "Non-Uniform", + "opcode" : 351, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMul", + "class" : "Non-Uniform", + "opcode" : 352, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformSMin", + "class" : "Non-Uniform", + "opcode" : 353, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformUMin", + "class" : "Non-Uniform", + "opcode" : 354, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMin", + "class" : "Non-Uniform", + "opcode" : 355, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformSMax", + "class" : "Non-Uniform", + "opcode" : 356, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformUMax", + "class" : "Non-Uniform", + "opcode" : 357, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMax", + "class" : "Non-Uniform", + "opcode" : 358, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseAnd", + "class" : "Non-Uniform", + "opcode" : 359, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseOr", + "class" : "Non-Uniform", + "opcode" : 360, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseXor", + "class" : "Non-Uniform", + "opcode" : 361, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalAnd", + "class" : "Non-Uniform", + "opcode" : 362, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalOr", + "class" : "Non-Uniform", + "opcode" : 363, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalXor", + "class" : "Non-Uniform", + "opcode" : 364, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformQuadBroadcast", + "class" : "Non-Uniform", + "opcode" : 365, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "GroupNonUniformQuad" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformQuadSwap", + "class" : "Non-Uniform", + "opcode" : 366, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "GroupNonUniformQuad" ], + "version" : "1.3" + }, + { + "opname" : "OpCopyLogical", + "class" : "Composite", + "opcode" : 400, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrEqual", + "class" : "Memory", + "opcode" : 401, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrNotEqual", + "class" : "Memory", + "opcode" : 402, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrDiff", + "class" : "Memory", + "opcode" : 403, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "Addresses", "VariablePointers", "VariablePointersStorageBuffer" ], + "version" : "1.4" + }, + { + "opname" : "OpColorAttachmentReadEXT", + "class" : "Image", + "opcode" : 4160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Attachment'" }, + { "kind" : "IdRef", "name" : "'Sample'", "quantifier" : "?" } + ], + "capabilities": [ "TileImageColorReadAccessEXT" ], + "version" : "None" + }, + { + "opname" : "OpDepthAttachmentReadEXT", + "class" : "Image", + "opcode" : 4161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sample'", "quantifier" : "?" } + ], + "capabilities" : [ "TileImageDepthReadAccessEXT" ], + "version" : "None" + }, + { + "opname" : "OpStencilAttachmentReadEXT", + "class" : "Image", + "opcode" : 4162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sample'", "quantifier" : "?" } + ], + "capabilities" : [ "TileImageStencilReadAccessEXT" ], + "version" : "None" + }, + { + "opname" : "OpTerminateInvocation", + "class" : "Control-Flow", + "opcode" : 4416, + "extensions" : [ + "SPV_KHR_terminate_invocation" + ], + "capabilities" : [ "Shader" ], + "version" : "1.6" + }, + { + "opname" : "OpTypeUntypedPointerKHR", + "class" : "Type-Declaration", + "opcode" : 4417, + "capabilities" : [ + "UntypedPointersKHR" + ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" } + ] + }, + { + "opname" : "OpUntypedVariableKHR", + "class" : "Memory", + "opcode" : 4418, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Data Type'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpUntypedAccessChainKHR", + "class" : "Memory", + "opcode" : 4419, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpUntypedInBoundsAccessChainKHR", + "class" : "Memory", + "opcode" : 4420, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "class" : "Group", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "class" : "Group", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpUntypedPtrAccessChainKHR", + "class" : "Memory", + "opcode" : 4423, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpUntypedInBoundsPtrAccessChainKHR", + "class" : "Memory", + "opcode" : 4424, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpUntypedArrayLengthKHR", + "class" : "Memory", + "opcode" : 4425, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ] + }, + { + "opname" : "OpUntypedPrefetchKHR", + "class" : "Memory", + "opcode" : 4426, + "capabilities" : [ "UntypedPointersKHR" ], + "provisional" : true, + "version" : "None", + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "IdRef", "name" : "'Num Bytes'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'RW'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Locality'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Cache Type'" } + ] + }, + { + "opname" : "OpSubgroupAllKHR", + "class" : "Group", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAnyKHR", + "class" : "Group", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "class" : "Group", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupNonUniformRotateKHR", + "class" : "Group", + "opcode" : 4431, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Delta'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformRotateKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "class" : "Group", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpExtInstWithForwardRefsKHR", + "class" : "Extension", + "opcode" : 4433, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ], + "extensions" : [ "SPV_KHR_relaxed_extended_instruction" ], + "version": "None" + }, + { + "opname" : "OpTraceRayKHR", + "class" : "Reserved", + "opcode" : 4445, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpExecuteCallableKHR", + "class" : "Reserved", + "opcode" : 4446, + "operands" : [ + + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Callable Data'" } + ], + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToAccelerationStructureKHR", + "class" : "Reserved", + "opcode" : 4447, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Accel'" } + ], + "capabilities" : [ "RayTracingKHR", "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing", "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpIgnoreIntersectionKHR", + "class" : "Reserved", + "opcode" : 4448, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTerminateRayKHR", + "class" : "Reserved", + "opcode" : 4449, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpSDot", + "class" : "Arithmetic", + "aliases" : ["OpSDotKHR"], + "opcode" : 4450, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpUDot", + "class" : "Arithmetic", + "aliases" : ["OpUDotKHR"], + "opcode" : 4451, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpSUDot", + "class" : "Arithmetic", + "aliases" : ["OpSUDotKHR"], + "opcode" : 4452, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpSDotAccSat", + "class" : "Arithmetic", + "aliases" : ["OpSDotAccSatKHR"], + "opcode" : 4453, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpUDotAccSat", + "class" : "Arithmetic", + "aliases" : ["OpUDotAccSatKHR"], + "opcode" : 4454, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpSUDotAccSat", + "class" : "Arithmetic", + "aliases" : ["OpSUDotAccSatKHR"], + "opcode" : 4455, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "IdRef", "name" : "'Accumulator'" }, + { "kind" : "PackedVectorFormat", "name" : "'Packed Vector Format'", "quantifier" : "?" } + ], + "capabilities" : [ "DotProduct" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "opname" : "OpTypeCooperativeMatrixKHR", + "class" : "Type-Declaration", + "opcode" : 4456, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Rows'" }, + { "kind" : "IdRef", "name" : "'Columns'" }, + { "kind" : "IdRef", "name" : "'Use'" } + ], + "capabilities" : [ "CooperativeMatrixKHR" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLoadKHR", + "class" : "Memory", + "opcode" : 4457, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'MemoryLayout'" }, + { "kind" : "IdRef", "name" : "'Stride'", "quantifier": "?" }, + { "kind" : "MemoryAccess", "name" : "'Memory Operand'", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixKHR" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixStoreKHR", + "class" : "Memory", + "opcode" : 4458, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'MemoryLayout'" }, + { "kind" : "IdRef", "name" : "'Stride'", "quantifier": "?" }, + { "kind" : "MemoryAccess", "name" : "'Memory Operand'", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixKHR" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixMulAddKHR", + "class" : "Arithmetic", + "opcode" : 4459, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "IdRef", "name" : "'C'" }, + { "kind" : "CooperativeMatrixOperands", "name" : "'Cooperative Matrix Operands'", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixKHR" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLengthKHR", + "class" : "Miscellaneous", + "opcode" : 4460, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Type'" } + ], + "capabilities" : [ "CooperativeMatrixKHR" ], + "version" : "None" + }, + { + "opname" : "OpConstantCompositeReplicateEXT", + "class" : "Constant-Creation", + "opcode" : 4461, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "ReplicatedCompositesEXT" ], + "version" : "None" + }, + { + "opname" : "OpSpecConstantCompositeReplicateEXT", + "class" : "Constant-Creation", + "opcode" : 4462, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "ReplicatedCompositesEXT" ], + "version" : "None" + }, + { + "opname" : "OpCompositeConstructReplicateEXT", + "class" : "Composite", + "opcode" : 4463, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "ReplicatedCompositesEXT" ], + "version" : "None" + }, + { + "opname" : "OpTypeRayQueryKHR", + "class" : "Type-Declaration", + "opcode" : 4472, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryInitializeKHR", + "class" : "Reserved", + "opcode" : 4473, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Accel'" + }, + { + "kind" : "IdRef", + "name" : "'RayFlags'" + }, + { + "kind" : "IdRef", + "name" : "'CullMask'" + }, + { + "kind" : "IdRef", + "name" : "'RayOrigin'" + }, + { + "kind" : "IdRef", + "name" : "'RayTMin'" + }, + { + "kind" : "IdRef", + "name" : "'RayDirection'" + }, + { + "kind" : "IdRef", + "name" : "'RayTMax'" + } + + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryTerminateKHR", + "class" : "Reserved", + "opcode" : 4474, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGenerateIntersectionKHR", + "class" : "Reserved", + "opcode" : 4475, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'HitT'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryConfirmIntersectionKHR", + "class" : "Reserved", + "opcode" : 4476, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryProceedKHR", + "class" : "Reserved", + "opcode" : 4477, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionTypeKHR", + "class" : "Reserved", + "opcode" : 4479, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpImageSampleWeightedQCOM", + "class" : "Image", + "opcode" : 4480, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Texture'" }, + { "kind" : "IdRef", "name" : "'Coordinates'" }, + { "kind" : "IdRef", "name" : "'Weights'" } + ], + "capabilities" : [ "TextureSampleWeightedQCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBoxFilterQCOM", + "class" : "Image", + "opcode" : 4481, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Texture'" }, + { "kind" : "IdRef", "name" : "'Coordinates'" }, + { "kind" : "IdRef", "name" : "'Box Size'" } + ], + "capabilities" : [ "TextureBoxFilterQCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchSSDQCOM", + "class" : "Image", + "opcode" : 4482, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatchQCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchSADQCOM", + "class" : "Image", + "opcode" : 4483, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatchQCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchWindowSSDQCOM", + "class" : "Image", + "opcode" : 4500, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatch2QCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchWindowSADQCOM", + "class" : "Image", + "opcode" : 4501, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatch2QCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchGatherSSDQCOM", + "class" : "Image", + "opcode" : 4502, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatch2QCOM" ], + "version" : "None" + }, + { + "opname" : "OpImageBlockMatchGatherSADQCOM", + "class" : "Image", + "opcode" : 4503, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Target Coordinates'" }, + { "kind" : "IdRef", "name" : "'Reference Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Reference Coordinates'" }, + { "kind" : "IdRef", "name" : "'Block Size'" } + ], + "capabilities" : [ "TextureBlockMatch2QCOM" ], + "version" : "None" + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "class" : "Group", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "class" : "Group", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "class" : "Group", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "class" : "Group", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "class" : "Group", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "class" : "Reserved", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "opname" : "OpFragmentFetchAMD", + "class" : "Reserved", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "opname" : "OpReadClockKHR", + "class" : "Reserved", + "opcode" : 5056, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Scope'" } + ], + "capabilities" : [ "ShaderClockKHR" ], + "version" : "None" + }, + { + "opname" : "OpAllocateNodePayloadsAMDX", + "class" : "Reserved", + "opcode" : 5074, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Visibility'" }, + { "kind" : "IdRef", "name": "'Payload Count'" }, + { "kind" : "IdRef", "name": "'Node Index'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "opname" : "OpEnqueueNodePayloadsAMDX", + "class" : "Reserved", + "opcode" : 5075, + "operands" : [ + { "kind" : "IdRef", "name": "'Payload Array'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "opname" : "OpTypeNodePayloadArrayAMDX", + "class" : "Reserved", + "opcode" : 5076, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name": "'Payload Type'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "opname" : "OpFinishWritingNodePayloadAMDX", + "class" : "Reserved", + "opcode" : 5078, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name": "'Payload'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "opname" : "OpNodePayloadArrayLengthAMDX", + "class" : "Reserved", + "opcode" : 5090, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name": "'Payload Array'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "opname" : "OpIsNodePayloadValidAMDX", + "class" : "Reserved", + "opcode" : 5101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name": "'Payload Type'" }, + { "kind" : "IdRef", "name": "'Node Index'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version": "None" + }, + { + "opname" : "OpConstantStringAMDX", + "class" : "Reserved", + "opcode" : 5103, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name": "'Literal String'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version": "None" + }, + { + "opname" : "OpSpecConstantStringAMDX", + "class" : "Reserved", + "opcode" : 5104, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name": "'Literal String'" } + ], + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version": "None" + }, + { + "opname" : "OpGroupNonUniformQuadAllKHR", + "class" : "Non-Uniform", + "opcode" : 5110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "QuadControlKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupNonUniformQuadAnyKHR", + "class" : "Non-Uniform", + "opcode" : 5111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "QuadControlKHR" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordHitMotionNV", + "class" : "Reserved", + "opcode" : 5249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'" }, + { "kind" : "IdRef", "name" : "'InstanceId'" }, + { "kind" : "IdRef", "name" : "'PrimitiveId'" }, + { "kind" : "IdRef", "name" : "'GeometryIndex'" }, + { "kind" : "IdRef", "name" : "'Hit Kind'" }, + { "kind" : "IdRef", "name" : "'SBT Record Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Record Stride'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" }, + { "kind" : "IdRef", "name" : "'Current Time'" }, + { "kind" : "IdRef", "name" : "'HitObject Attributes'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV", "RayTracingMotionBlurNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordHitWithIndexMotionNV", + "class" : "Reserved", + "opcode" : 5250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'" }, + { "kind" : "IdRef", "name" : "'InstanceId'" }, + { "kind" : "IdRef", "name" : "'PrimitiveId'" }, + { "kind" : "IdRef", "name" : "'GeometryIndex'" }, + { "kind" : "IdRef", "name" : "'Hit Kind'" }, + { "kind" : "IdRef", "name" : "'SBT Record Index'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" }, + { "kind" : "IdRef", "name" : "'Current Time'" }, + { "kind" : "IdRef", "name" : "'HitObject Attributes'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV", "RayTracingMotionBlurNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordMissMotionNV", + "class" : "Reserved", + "opcode" : 5251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" }, + { "kind" : "IdRef", "name" : "'Current Time'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV", "RayTracingMotionBlurNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetWorldToObjectNV", + "class" : "Reserved", + "opcode" : 5252, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetObjectToWorldNV", + "class" : "Reserved", + "opcode" : 5253, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetObjectRayDirectionNV", + "class" : "Reserved", + "opcode" : 5254, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetObjectRayOriginNV", + "class" : "Reserved", + "opcode" : 5255, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectTraceRayMotionNV", + "class" : "Reserved", + "opcode" : 5256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'"}, + { "kind" : "IdRef", "name" : "'RayFlags'"}, + { "kind" : "IdRef", "name" : "'Cullmask'"}, + { "kind" : "IdRef", "name" : "'SBT Record Offset'"}, + { "kind" : "IdRef", "name" : "'SBT Record Stride'"}, + { "kind" : "IdRef", "name" : "'Miss Index'"}, + { "kind" : "IdRef", "name" : "'Origin'"}, + { "kind" : "IdRef", "name" : "'TMin'"}, + { "kind" : "IdRef", "name" : "'Direction'"}, + { "kind" : "IdRef", "name" : "'TMax'"}, + { "kind" : "IdRef", "name" : "'Time'"}, + { "kind" : "IdRef", "name" : "'Payload'"} + ], + "capabilities" : [ "ShaderInvocationReorderNV", "RayTracingMotionBlurNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetShaderRecordBufferHandleNV", + "class" : "Reserved", + "opcode" : 5257, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetShaderBindingTableRecordIndexNV", + "class" : "Reserved", + "opcode" : 5258, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordEmptyNV", + "class" : "Reserved", + "opcode" : 5259, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectTraceRayNV", + "class" : "Reserved", + "opcode" : 5260, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'"}, + { "kind" : "IdRef", "name" : "'RayFlags'"}, + { "kind" : "IdRef", "name" : "'Cullmask'"}, + { "kind" : "IdRef", "name" : "'SBT Record Offset'"}, + { "kind" : "IdRef", "name" : "'SBT Record Stride'"}, + { "kind" : "IdRef", "name" : "'Miss Index'"}, + { "kind" : "IdRef", "name" : "'Origin'"}, + { "kind" : "IdRef", "name" : "'TMin'"}, + { "kind" : "IdRef", "name" : "'Direction'"}, + { "kind" : "IdRef", "name" : "'TMax'"}, + { "kind" : "IdRef", "name" : "'Payload'"} + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordHitNV", + "class" : "Reserved", + "opcode" : 5261, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'" }, + { "kind" : "IdRef", "name" : "'InstanceId'" }, + { "kind" : "IdRef", "name" : "'PrimitiveId'" }, + { "kind" : "IdRef", "name" : "'GeometryIndex'" }, + { "kind" : "IdRef", "name" : "'Hit Kind'" }, + { "kind" : "IdRef", "name" : "'SBT Record Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Record Stride'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" }, + { "kind" : "IdRef", "name" : "'HitObject Attributes'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordHitWithIndexNV", + "class" : "Reserved", + "opcode" : 5262, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Acceleration Structure'" }, + { "kind" : "IdRef", "name" : "'InstanceId'" }, + { "kind" : "IdRef", "name" : "'PrimitiveId'" }, + { "kind" : "IdRef", "name" : "'GeometryIndex'" }, + { "kind" : "IdRef", "name" : "'Hit Kind'" }, + { "kind" : "IdRef", "name" : "'SBT Record Index'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" }, + { "kind" : "IdRef", "name" : "'HitObject Attributes'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectRecordMissNV", + "class" : "Reserved", + "opcode" : 5263, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Origin'" }, + { "kind" : "IdRef", "name" : "'TMin'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'TMax'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectExecuteShaderNV", + "class" : "Reserved", + "opcode" : 5264, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetCurrentTimeNV", + "class" : "Reserved", + "opcode" : 5265, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetAttributesNV", + "class" : "Reserved", + "opcode" : 5266, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "name" : "'Hit Object Attribute'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetHitKindNV", + "class" : "Reserved", + "opcode" : 5267, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetPrimitiveIndexNV", + "class" : "Reserved", + "opcode" : 5268, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetGeometryIndexNV", + "class" : "Reserved", + "opcode" : 5269, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetInstanceIdNV", + "class" : "Reserved", + "opcode" : 5270, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetInstanceCustomIndexNV", + "class" : "Reserved", + "opcode" : 5271, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetWorldRayDirectionNV", + "class" : "Reserved", + "opcode" : 5272, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetWorldRayOriginNV", + "class" : "Reserved", + "opcode" : 5273, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetRayTMaxNV", + "class" : "Reserved", + "opcode" : 5274, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetRayTMinNV", + "class" : "Reserved", + "opcode" : 5275, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectIsEmptyNV", + "class" : "Reserved", + "opcode" : 5276, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectIsHitNV", + "class" : "Reserved", + "opcode" : 5277, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectIsMissNV", + "class" : "Reserved", + "opcode" : 5278, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpReorderThreadWithHitObjectNV", + "class" : "Reserved", + "opcode" : 5279, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hit Object'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Hint'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Bits'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpReorderThreadWithHintNV", + "class" : "Reserved", + "opcode" : 5280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Hint'" }, + { "kind" : "IdRef", "name" : "'Bits'" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpTypeHitObjectNV", + "class" : "Type-Declaration", + "opcode" : 5281, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "opname" : "OpImageSampleFootprintNV", + "class" : "Image", + "opcode" : 5283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Granularity'" }, + { "kind" : "IdRef", "name" : "'Coarse'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "ImageFootprintNV" ], + "extensions" : [ "SPV_NV_shader_image_footprint" ], + "version" : "None" + }, + { + "opname" : "OpTypeCooperativeVectorNV", + "class" : "Type-Declaration", + "opcode" : 5288, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "IdRef", "name" : "'Component Count'" } + ], + "capabilities" : [ "CooperativeVectorNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorMatrixMulNV", + "class" : "Reserved", + "opcode" : 5289, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "IdRef", "name" : "'InputInterpretation'" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'MatrixOffset'" }, + { "kind" : "IdRef", "name" : "'MatrixInterpretation'" }, + { "kind" : "IdRef", "name" : "'M'" }, + { "kind" : "IdRef", "name" : "'K'" }, + { "kind" : "IdRef", "name" : "'MemoryLayout'" }, + { "kind" : "IdRef", "name" : "'Transpose'" }, + { "kind" : "IdRef", "name" : "'MatrixStride'", "quantifier": "?" }, + { "kind" : "CooperativeMatrixOperands", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeVectorNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorOuterProductAccumulateNV", + "class" : "Reserved", + "opcode" : 5290, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "IdRef", "name" : "'MemoryLayout'" }, + { "kind" : "IdRef", "name" : "'MatrixInterpretation'" }, + { "kind" : "IdRef", "name" : "'MatrixStride'", "quantifier": "?" } + ], + "capabilities" : [ "CooperativeVectorTrainingNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorReduceSumAccumulateNV", + "class" : "Reserved", + "opcode" : 5291, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'V'" } + ], + "capabilities" : [ "CooperativeVectorTrainingNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorMatrixMulAddNV", + "class" : "Reserved", + "opcode" : 5292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "IdRef", "name" : "'InputInterpretation'" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'MatrixOffset'" }, + { "kind" : "IdRef", "name" : "'MatrixInterpretation'" }, + { "kind" : "IdRef", "name" : "'Bias'" }, + { "kind" : "IdRef", "name" : "'BiasOffset'" }, + { "kind" : "IdRef", "name" : "'BiasInterpretation'" }, + { "kind" : "IdRef", "name" : "'M'" }, + { "kind" : "IdRef", "name" : "'K'" }, + { "kind" : "IdRef", "name" : "'MemoryLayout'" }, + { "kind" : "IdRef", "name" : "'Transpose'" }, + { "kind" : "IdRef", "name" : "'MatrixStride'", "quantifier": "?" }, + { "kind" : "CooperativeMatrixOperands", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeVectorNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixConvertNV", + "class" : "Conversion", + "opcode" : 5293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "CooperativeMatrixConversionsNV" ], + "version" : "None" + }, + { + "opname" : "OpEmitMeshTasksEXT", + "class" : "Reserved", + "opcode" : 5294, + "operands" : [ + { "kind" : "IdRef", "name" : "'Group Count X'" }, + { "kind" : "IdRef", "name" : "'Group Count Y'" }, + { "kind" : "IdRef", "name" : "'Group Count Z'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Payload'" } + ], + "capabilities" : [ "MeshShadingEXT" ], + "version" : "None" + }, + { + "opname" : "OpSetMeshOutputsEXT", + "class" : "Reserved", + "opcode" : 5295, + "operands" : [ + { "kind" : "IdRef", "name" : "'Vertex Count'" }, + { "kind" : "IdRef", "name" : "'Primitive Count'" } + ], + "capabilities" : [ "MeshShadingEXT" ], + "version" : "None" + }, + { + "opname" : "OpGroupNonUniformPartitionNV", + "class" : "Non-Uniform", + "opcode" : 5296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "opname" : "OpWritePackedPrimitiveIndices4x8NV", + "class" : "Reserved", + "opcode" : 5299, + "operands" : [ + { "kind" : "IdRef", "name" : "'Index Offset'" }, + { "kind" : "IdRef", "name" : "'Packed Indices'" } + ], + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "opname" : "OpFetchMicroTriangleVertexPositionNV", + "class" : "Reserved", + "opcode" : 5300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Instance Id'" }, + { "kind" : "IdRef", "name" : "'Geometry Index'" }, + { "kind" : "IdRef", "name" : "'Primitive Index'" }, + { "kind" : "IdRef", "name" : "'Barycentric'" } + ], + "capabilities" : [ "DisplacementMicromapNV" ], + "version" : "None" + }, + { + "opname" : "OpFetchMicroTriangleVertexBarycentricNV", + "class" : "Reserved", + "opcode" : 5301, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Instance Id'" }, + { "kind" : "IdRef", "name" : "'Geometry Index'" }, + { "kind" : "IdRef", "name" : "'Primitive Index'" }, + { "kind" : "IdRef", "name" : "'Barycentric'" } + ], + "capabilities" : [ "DisplacementMicromapNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorLoadNV", + "class" : "Memory", + "opcode" : 5302, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeVectorNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeVectorStoreNV", + "class" : "Memory", + "opcode" : 5303, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeVectorNV" ], + "version" : "None" + }, + { + "opname" : "OpReportIntersectionKHR", + "class" : "Reserved", + "aliases" : ["OpReportIntersectionNV"], + "opcode" : 5334, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Hit'" }, + { "kind" : "IdRef", "name" : "'HitKind'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpIgnoreIntersectionNV", + "class" : "Reserved", + "opcode" : 5335, + "capabilities" : [ "RayTracingNV" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTerminateRayNV", + "class" : "Reserved", + "opcode" : 5336, + "capabilities" : [ "RayTracingNV" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTraceNV", + "class" : "Reserved", + "opcode" : 5337, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingNV" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTraceMotionNV", + "class" : "Reserved", + "opcode" : 5338, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "opname" : "OpTraceRayMotionNV", + "class" : "Reserved", + "opcode" : 5339, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'Time'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionTriangleVertexPositionsKHR", + "class" : "Reserved", + "opcode" : 5340, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryPositionFetchKHR" ], + "version" : "None" + }, + { + "opname" : "OpTypeAccelerationStructureKHR", + "class" : "Type-Declaration", + "aliases" : ["OpTypeAccelerationStructureNV"], + "opcode" : 5341, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR", "RayQueryKHR", "DisplacementMicromapNV" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", "SPV_NV_displacement_micromap" ], + "version" : "None" + }, + { + "opname" : "OpExecuteCallableNV", + "class" : "Reserved", + "opcode" : 5344, + "operands" : [ + + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Callable DataId'" } + ], + "capabilities" : [ "RayTracingNV" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetClusterIdNV", + "class" : "Reserved", + "opcode" : 5345, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingClusterAccelerationStructureNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetClusterIdNV", + "class" : "Reserved", + "opcode" : 5346, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingClusterAccelerationStructureNV" ], + "version" : "None" + }, + { + "opname" : "OpTypeCooperativeMatrixNV", + "class" : "Type-Declaration", + "opcode" : 5358, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Rows'" }, + { "kind" : "IdRef", "name" : "'Columns'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLoadNV", + "class" : "Reserved", + "opcode" : 5359, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Column Major'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixStoreNV", + "class" : "Reserved", + "opcode" : 5360, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Column Major'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixMulAddNV", + "class" : "Reserved", + "opcode" : 5361, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "IdRef", "name" : "'C'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLengthNV", + "class" : "Reserved", + "opcode" : 5362, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Type'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpBeginInvocationInterlockEXT", + "class" : "Reserved", + "opcode" : 5364, + "capabilities" : [ "FragmentShaderSampleInterlockEXT", "FragmentShaderPixelInterlockEXT", "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "opname" : "OpEndInvocationInterlockEXT", + "class" : "Reserved", + "opcode" : 5365, + "capabilities" : [ "FragmentShaderSampleInterlockEXT", "FragmentShaderPixelInterlockEXT", "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixReduceNV", + "class" : "Arithmetic", + "opcode" : 5366, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "CooperativeMatrixReduce", "name" : "'Reduce'" }, + { "kind" : "IdRef", "name" : "'CombineFunc'" } + ], + "capabilities" : [ "CooperativeMatrixReductionsNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLoadTensorNV", + "class" : "Memory", + "opcode" : 5367, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "MemoryAccess", "name" : "'Memory Operand'"}, + { "kind" : "TensorAddressingOperands", "name" : "'Tensor Addressing Operands'"} + ], + "capabilities" : [ "CooperativeMatrixTensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixStoreTensorNV", + "class" : "Memory", + "opcode" : 5368, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "MemoryAccess", "name" : "'Memory Operand'"}, + { "kind" : "TensorAddressingOperands", "name" : "'Tensor Addressing Operands'"} + ], + "capabilities" : [ "CooperativeMatrixTensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixPerElementOpNV", + "class" : "Function", + "opcode" : 5369, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Func'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operands'" } + ], + "capabilities" : [ "CooperativeMatrixPerElementOperationsNV" ], + "version" : "None" + }, + { + "opname" : "OpTypeTensorLayoutNV", + "class" : "Type-Declaration", + "opcode" : 5370, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Dim'" }, + { "kind" : "IdRef", "name" : "'ClampMode'" } + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTypeTensorViewNV", + "class" : "Type-Declaration", + "opcode" : 5371, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Dim'" }, + { "kind" : "IdRef", "name" : "'HasDimensions'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'p'" } + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpCreateTensorLayoutNV", + "class" : "Reserved", + "opcode" : 5372, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorLayoutSetDimensionNV", + "class" : "Reserved", + "opcode" : 5373, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Dim'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorLayoutSetStrideNV", + "class" : "Reserved", + "opcode" : 5374, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Stride'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorLayoutSliceNV", + "class" : "Reserved", + "opcode" : 5375, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operands'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorLayoutSetClampValueNV", + "class" : "Reserved", + "opcode" : 5376, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "IdRef", "name" : "'Value'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpCreateTensorViewNV", + "class" : "Reserved", + "opcode" : 5377, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorViewSetDimensionNV", + "class" : "Reserved", + "opcode" : 5378, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorView'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Dim'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorViewSetStrideNV", + "class" : "Reserved", + "opcode" : 5379, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorView'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Stride'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpDemoteToHelperInvocation", + "class" : "Control-Flow", + "aliases" : ["OpDemoteToHelperInvocationEXT"], + "opcode" : 5380, + "capabilities" : [ "DemoteToHelperInvocation" ], + "version" : "1.6" + }, + { + "opname" : "OpIsHelperInvocationEXT", + "class" : "Reserved", + "opcode" : 5381, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DemoteToHelperInvocation" ], + "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], + "version" : "None" + }, + { + "opname" : "OpTensorViewSetClipNV", + "class" : "Reserved", + "opcode" : 5382, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorView'" }, + { "kind" : "IdRef", "name" : "'ClipRowOffset'" }, + { "kind" : "IdRef", "name" : "'ClipRowSpan'" }, + { "kind" : "IdRef", "name" : "'ClipColOffset'" }, + { "kind" : "IdRef", "name" : "'ClipColSpan'" } + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpTensorLayoutSetBlockSizeNV", + "class" : "Reserved", + "opcode" : 5384, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'TensorLayout'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'BlockSize'" } + + ], + "capabilities" : [ "TensorAddressingNV" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixTransposeNV", + "class" : "Conversion", + "opcode" : 5390, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "CooperativeMatrixConversionsNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToImageNV", + "class" : "Reserved", + "opcode" : 5391, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSamplerNV", + "class" : "Reserved", + "opcode" : 5392, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertImageToUNV", + "class" : "Reserved", + "opcode" : 5393, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSamplerToUNV", + "class" : "Reserved", + "opcode" : 5394, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertUToSampledImageNV", + "class" : "Reserved", + "opcode" : 5395, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpConvertSampledImageToUNV", + "class" : "Reserved", + "opcode" : 5396, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpSamplerImageAddressingModeNV", + "class" : "Reserved", + "opcode" : 5397, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Bit Width'" } + ], + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "opname" : "OpRawAccessChainNV", + "class" : "Memory", + "opcode" : 5398, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Byte stride'" }, + { "kind" : "IdRef", "name" : "'Element index'" }, + { "kind" : "IdRef", "name" : "'Byte offset'" }, + { "kind" : "RawAccessChainOperands", "quantifier" : "?" } + ], + "capabilities" : [ + "RawAccessChainsNV" + ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionSpherePositionNV", + "class" : "Reserved", + "opcode" : 5427, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionSphereRadiusNV", + "class" : "Reserved", + "opcode" : 5428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionLSSPositionsNV", + "class" : "Reserved", + "opcode" : 5429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionLSSRadiiNV", + "class" : "Reserved", + "opcode" : 5430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV"], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionLSSHitValueNV", + "class" : "Reserved", + "opcode" : 5431, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetSpherePositionNV", + "class" : "Reserved", + "opcode" : 5432, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetSphereRadiusNV", + "class" : "Reserved", + "opcode" : 5433, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetLSSPositionsNV", + "class" : "Reserved", + "opcode" : 5434, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectGetLSSRadiiNV", + "class" : "Reserved", + "opcode" : 5435, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectIsSphereHitNV", + "class" : "Reserved", + "opcode" : 5436, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpHitObjectIsLSSHitNV", + "class" : "Reserved", + "opcode" : 5437, + "operands" : [ + { "kind" : "IdResultType"}, + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Hit Object'" } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryIsSphereHitNV", + "class" : "Reserved", + "opcode" : 5438, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryIsLSSHitNV", + "class" : "Reserved", + "opcode" : 5439, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "class" : "Group", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "class" : "Group", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "class" : "Group", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "class" : "Group", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "class" : "Group", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "class" : "Group", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "class" : "Group", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "class" : "Group", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageMediaBlockReadINTEL", + "class" : "Group", + "opcode" : 5580, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Width'" }, + { "kind" : "IdRef", "name" : "'Height'" } + ], + "capabilities" : [ "SubgroupImageMediaBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageMediaBlockWriteINTEL", + "class" : "Group", + "opcode" : 5581, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Width'" }, + { "kind" : "IdRef", "name" : "'Height'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageMediaBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpUCountLeadingZerosINTEL", + "class" : "Reserved", + "opcode" : 5585, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUCountTrailingZerosINTEL", + "class" : "Reserved", + "opcode" : 5586, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpAbsISubINTEL", + "class" : "Reserved", + "opcode" : 5587, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpAbsUSubINTEL", + "class" : "Reserved", + "opcode" : 5588, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAddSatINTEL", + "class" : "Reserved", + "opcode" : 5589, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAddSatINTEL", + "class" : "Reserved", + "opcode" : 5590, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAverageINTEL", + "class" : "Reserved", + "opcode" : 5591, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAverageINTEL", + "class" : "Reserved", + "opcode" : 5592, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAverageRoundedINTEL", + "class" : "Reserved", + "opcode" : 5593, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAverageRoundedINTEL", + "class" : "Reserved", + "opcode" : 5594, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpISubSatINTEL", + "class" : "Reserved", + "opcode" : 5595, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUSubSatINTEL", + "class" : "Reserved", + "opcode" : 5596, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIMul32x16INTEL", + "class" : "Reserved", + "opcode" : 5597, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUMul32x16INTEL", + "class" : "Reserved", + "opcode" : 5598, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpConstantFunctionPointerINTEL", + "class" : "@exclude", + "opcode" : 5600, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" } + ], + "capabilities" : [ "FunctionPointersINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "opname" : "OpFunctionPointerCallINTEL", + "class" : "@exclude", + "opcode" : 5601, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1'" } + ], + "capabilities" : [ "FunctionPointersINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "opname" : "OpAsmTargetINTEL", + "class" : "@exclude", + "opcode" : 5609, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Asm target'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAsmINTEL", + "class" : "@exclude", + "opcode" : 5610, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Asm type'" }, + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Asm instructions'" }, + { "kind" : "LiteralString", "name" : "'Constraints'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAsmCallINTEL", + "class" : "@exclude", + "opcode" : 5611, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Asm'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFMinEXT", + "class" : "Atomic", + "opcode" : 5614, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat16MinMaxEXT", "AtomicFloat32MinMaxEXT", "AtomicFloat64MinMaxEXT", "AtomicFloat16VectorNV" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFMaxEXT", + "class" : "Atomic", + "opcode" : 5615, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat16MinMaxEXT", "AtomicFloat32MinMaxEXT", "AtomicFloat64MinMaxEXT", "AtomicFloat16VectorNV" ], + "version" : "None" + }, + { + "opname" : "OpAssumeTrueKHR", + "class" : "Miscellaneous", + "opcode" : 5630, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" } + ], + "capabilities" : [ "ExpectAssumeKHR" ], + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, + { + "opname" : "OpExpectKHR", + "class" : "Miscellaneous", + "opcode" : 5631, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ExpectedValue'" } + ], + "capabilities" : [ "ExpectAssumeKHR" ], + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, + { + "opname" : "OpDecorateString", + "class" : "Annotation", + "aliases" : ["OpDecorateStringGOOGLE"], + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpMemberDecorateString", + "class" : "Annotation", + "aliases" : ["OpMemberDecorateStringGOOGLE"], + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpVmeImageINTEL", + "class" : "@exclude", + "opcode" : 5699, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeVmeImageINTEL", + "class" : "@exclude", + "opcode" : 5700, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImePayloadINTEL", + "class" : "@exclude", + "opcode" : 5701, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcRefPayloadINTEL", + "class" : "@exclude", + "opcode" : 5702, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcSicPayloadINTEL", + "class" : "@exclude", + "opcode" : 5703, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5704, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcMceResultINTEL", + "class" : "@exclude", + "opcode" : 5705, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultINTEL", + "class" : "@exclude", + "opcode" : 5706, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5707, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5708, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5709, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5710, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcRefResultINTEL", + "class" : "@exclude", + "opcode" : 5711, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcSicResultINTEL", + "class" : "@exclude", + "opcode" : 5712, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5713, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5714, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reference Base Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5715, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5716, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Shape Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5717, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5718, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Direction Cost'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5719, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL", + "class" : "@exclude", + "opcode" : 5720, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5721, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5722, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5723, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5724, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Cost Center Delta'" }, + { "kind" : "IdRef", "name" : "'Packed Cost Table'" }, + { "kind" : "IdRef", "name" : "'Cost Precision'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5725, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5726, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5727, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetAcOnlyHaarINTEL", + "class" : "@exclude", + "opcode" : 5728, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL", + "class" : "@exclude", + "opcode" : 5729, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Source Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL", + "class" : "@exclude", + "opcode" : 5730, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL", + "class" : "@exclude", + "opcode" : 5731, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Forward Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Backward Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToImePayloadINTEL", + "class" : "@exclude", + "opcode" : 5732, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToImeResultINTEL", + "class" : "@exclude", + "opcode" : 5733, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToRefPayloadINTEL", + "class" : "@exclude", + "opcode" : 5734, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToRefResultINTEL", + "class" : "@exclude", + "opcode" : 5735, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToSicPayloadINTEL", + "class" : "@exclude", + "opcode" : 5736, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToSicResultINTEL", + "class" : "@exclude", + "opcode" : 5737, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5738, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5739, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetBestInterDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5740, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMajorShapeINTEL", + "class" : "@exclude", + "opcode" : 5741, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMinorShapeINTEL", + "class" : "@exclude", + "opcode" : 5742, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterDirectionsINTEL", + "class" : "@exclude", + "opcode" : 5743, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMotionVectorCountINTEL", + "class" : "@exclude", + "opcode" : 5744, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5745, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL", + "class" : "@exclude", + "opcode" : 5746, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Parameter Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5747, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Partition Mask'" }, + { "kind" : "IdRef", "name" : "'SAD Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5748, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5749, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Offset'" }, + { "kind" : "IdRef", "name" : "'id> Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeRefWindowSizeINTEL", + "class" : "@exclude", + "opcode" : 5750, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Dual Ref'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeAdjustRefOffsetINTEL", + "class" : "@exclude", + "opcode" : 5751, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Ref Window Size'" }, + { "kind" : "IdRef", "name" : "'Image Size'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5752, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL", + "class" : "@exclude", + "opcode" : 5753, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Max Motion Vector Count'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL", + "class" : "@exclude", + "opcode" : 5754, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL", + "class" : "@exclude", + "opcode" : 5755, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Threshold'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetWeightedSadINTEL", + "class" : "@exclude", + "opcode" : 5756, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Sad Weights'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5757, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5758, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5759, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5760, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5761, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5762, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL", + "class" : "@exclude", + "opcode" : 5763, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL", + "class" : "@exclude", + "opcode" : 5764, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5765, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5766, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5767, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5768, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5769, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5770, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5771, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5772, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5773, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5774, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5775, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetBorderReachedINTEL", + "class" : "@exclude", + "opcode" : 5776, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Select'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL", + "class" : "@exclude", + "opcode" : 5777, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL", + "class" : "@exclude", + "opcode" : 5778, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL", + "class" : "@exclude", + "opcode" : 5779, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL", + "class" : "@exclude", + "opcode" : 5780, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcFmeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5781, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Major Shapes'" }, + { "kind" : "IdRef", "name" : "'Minor Shapes'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'Pixel Resolution'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcBmeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5782, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Major Shapes'" }, + { "kind" : "IdRef", "name" : "'Minor Shapes'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'Pixel Resolution'" }, + { "kind" : "IdRef", "name" : "'Bidirectional Weight'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5783, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL", + "class" : "@exclude", + "opcode" : 5784, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefSetBilinearFilterEnableINTEL", + "class" : "@exclude", + "opcode" : 5785, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5786, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5787, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL", + "class" : "@exclude", + "opcode" : 5788, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL", + "class" : "@exclude", + "opcode" : 5789, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5790, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicInitializeINTEL", + "class" : "@exclude", + "opcode" : 5791, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureSkcINTEL", + "class" : "@exclude", + "opcode" : 5792, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Skip Block Partition Type'" }, + { "kind" : "IdRef", "name" : "'Skip Motion Vector Mask'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Bidirectional Weight'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureIpeLumaINTEL", + "class" : "@exclude", + "opcode" : 5793, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Intra Partition Mask'" }, + { "kind" : "IdRef", "name" : "'Intra Neighbour Availabilty'" }, + { "kind" : "IdRef", "name" : "'Left Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Luma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Right Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL", + "class" : "@exclude", + "opcode" : 5794, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Intra Partition Mask'" }, + { "kind" : "IdRef", "name" : "'Intra Neighbour Availabilty'" }, + { "kind" : "IdRef", "name" : "'Left Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Luma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Right Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Left Edge Chroma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Chroma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Chroma Pixels'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetMotionVectorMaskINTEL", + "class" : "@exclude", + "opcode" : 5795, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Skip Block Partition Type'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5796, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5797, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Shape Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5798, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Mode Penalty'" }, + { "kind" : "IdRef", "name" : "'Luma Packed Neighbor Modes'" }, + { "kind" : "IdRef", "name" : "'Luma Packed Non Dc Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5799, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Chroma Mode Base Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetBilinearFilterEnableINTEL", + "class" : "@exclude", + "opcode" : 5800, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL", + "class" : "@exclude", + "opcode" : 5801, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Sad Coefficients'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL", + "class" : "@exclude", + "opcode" : 5802, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Block Based Skip Type'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateIpeINTEL", + "class" : "@exclude", + "opcode" : 5803, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5804, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5805, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL", + "class" : "@exclude", + "opcode" : 5806, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL", + "class" : "@exclude", + "opcode" : 5807, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5808, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetIpeLumaShapeINTEL", + "class" : "@exclude", + "opcode" : 5809, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL", + "class" : "@exclude", + "opcode" : 5810, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL", + "class" : "@exclude", + "opcode" : 5811, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL", + "class" : "@exclude", + "opcode" : 5812, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetIpeChromaModeINTEL", + "class" : "@exclude", + "opcode" : 5813, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL", + "class" : "@exclude", + "opcode" : 5814, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL", + "class" : "@exclude", + "opcode" : 5815, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetInterRawSadsINTEL", + "class" : "@exclude", + "opcode" : 5816, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpVariableLengthArrayINTEL", + "class" : "@exclude", + "opcode" : 5818, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Lenght'" } + ], + "capabilities" : [ "VariableLengthArrayINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSaveMemoryINTEL", + "class" : "@exclude", + "opcode" : 5819, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "VariableLengthArrayINTEL" ], + "version" : "None" + }, + { + "opname" : "OpRestoreMemoryINTEL", + "class" : "@exclude", + "opcode" : 5820, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "VariableLengthArrayINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinCosPiINTEL", + "class" : "@exclude", + "opcode" : 5840, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'MResult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCastINTEL", + "class" : "@exclude", + "opcode" : 5841, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCastFromIntINTEL", + "class" : "@exclude", + "opcode" : 5842, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'FromSign'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCastToIntINTEL", + "class" : "@exclude", + "opcode" : 5843, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'ToSign'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatAddINTEL", + "class" : "@exclude", + "opcode" : 5846, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'MResult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSubINTEL", + "class" : "@exclude", + "opcode" : 5847, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatMulINTEL", + "class" : "@exclude", + "opcode" : 5848, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatDivINTEL", + "class" : "@exclude", + "opcode" : 5849, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatGTINTEL", + "class" : "@exclude", + "opcode" : 5850, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatGEINTEL", + "class" : "@exclude", + "opcode" : 5851, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLTINTEL", + "class" : "@exclude", + "opcode" : 5852, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLEINTEL", + "class" : "@exclude", + "opcode" : 5853, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatEQINTEL", + "class" : "@exclude", + "opcode" : 5854, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatRecipINTEL", + "class" : "@exclude", + "opcode" : 5855, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatRSqrtINTEL", + "class" : "@exclude", + "opcode" : 5856, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCbrtINTEL", + "class" : "@exclude", + "opcode" : 5857, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatHypotINTEL", + "class" : "@exclude", + "opcode" : 5858, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSqrtINTEL", + "class" : "@exclude", + "opcode" : 5859, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLogINTEL", + "class" : "@exclude", + "opcode" : 5860, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog2INTEL", + "class" : "@exclude", + "opcode" : 5861, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog10INTEL", + "class" : "@exclude", + "opcode" : 5862, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatLog1pINTEL", + "class" : "@exclude", + "opcode" : 5863, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExpINTEL", + "class" : "@exclude", + "opcode" : 5864, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExp2INTEL", + "class" : "@exclude", + "opcode" : 5865, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExp10INTEL", + "class" : "@exclude", + "opcode" : 5866, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatExpm1INTEL", + "class" : "@exclude", + "opcode" : 5867, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinINTEL", + "class" : "@exclude", + "opcode" : 5868, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCosINTEL", + "class" : "@exclude", + "opcode" : 5869, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinCosINTEL", + "class" : "@exclude", + "opcode" : 5870, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatSinPiINTEL", + "class" : "@exclude", + "opcode" : 5871, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatCosPiINTEL", + "class" : "@exclude", + "opcode" : 5872, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatASinINTEL", + "class" : "@exclude", + "opcode" : 5873, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatASinPiINTEL", + "class" : "@exclude", + "opcode" : 5874, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatACosINTEL", + "class" : "@exclude", + "opcode" : 5875, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'M1'" }, + { "kind" : "LiteralInteger", "name" : "'Mout'" }, + { "kind" : "LiteralInteger", "name" : "'EnableSubnormals'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingMode'" }, + { "kind" : "LiteralInteger", "name" : "'RoundingAccuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatACosPiINTEL", + "class" : "@exclude", + "opcode" : 5876, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATanINTEL", + "class" : "@exclude", + "opcode" : 5877, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATanPiINTEL", + "class" : "@exclude", + "opcode" : 5878, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatATan2INTEL", + "class" : "@exclude", + "opcode" : 5879, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowINTEL", + "class" : "@exclude", + "opcode" : 5880, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowRINTEL", + "class" : "@exclude", + "opcode" : 5881, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'Mb'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArbitraryFloatPowNINTEL", + "class" : "@exclude", + "opcode" : 5882, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "LiteralInteger", "name" : "'Ma'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "LiteralInteger", "name" : "'SignOfB'" }, + { "kind" : "LiteralInteger", "name" : "'Mresult'" }, + { "kind" : "LiteralInteger", "name" : "'Subnormal'" }, + { "kind" : "LiteralInteger", "name" : "'Rounding'" }, + { "kind" : "LiteralInteger", "name" : "'Accuracy'" } + ], + "capabilities" : [ "ArbitraryPrecisionFloatingPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpLoopControlINTEL", + "class" : "Reserved", + "opcode" : 5887, + "operands" : [ + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Loop Control Parameters'" } + ], + "capabilities" : [ "UnstructuredLoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "version" : "None" + }, + { + "opname" : "OpAliasDomainDeclINTEL", + "class" : "@exclude", + "opcode" : 5911, + "operands" : [ + { "kind" : "IdResult"}, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Name'" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + }, + { + "opname" : "OpAliasScopeDeclINTEL", + "class" : "@exclude", + "opcode" : 5912, + "operands" : [ + { "kind" : "IdResult"}, + { "kind" : "IdRef", "name" : "'Alias Domain'"}, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Name'" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + }, + { + "opname" : "OpAliasScopeListDeclINTEL", + "class" : "@exclude", + "opcode" : 5913, + "operands" : [ + { "kind" : "IdResult"}, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'AliasScope1, AliasScope2, ...'" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + }, + { + "opname" : "OpFixedSqrtINTEL", + "class" : "@exclude", + "opcode" : 5923, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedRecipINTEL", + "class" : "@exclude", + "opcode" : 5924, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedRsqrtINTEL", + "class" : "@exclude", + "opcode" : 5925, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinINTEL", + "class" : "@exclude", + "opcode" : 5926, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedCosINTEL", + "class" : "@exclude", + "opcode" : 5927, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinCosINTEL", + "class" : "@exclude", + "opcode" : 5928, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinPiINTEL", + "class" : "@exclude", + "opcode" : 5929, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedCosPiINTEL", + "class" : "@exclude", + "opcode" : 5930, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedSinCosPiINTEL", + "class" : "@exclude", + "opcode" : 5931, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedLogINTEL", + "class" : "@exclude", + "opcode" : 5932, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpFixedExpINTEL", + "class" : "@exclude", + "opcode" : 5933, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" }, + { "kind" : "LiteralInteger", "name" : "'S'" }, + { "kind" : "LiteralInteger", "name" : "'I'" }, + { "kind" : "LiteralInteger", "name" : "'rI'" }, + { "kind" : "LiteralInteger", "name" : "'Q'" }, + { "kind" : "LiteralInteger", "name" : "'O'" } + ], + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL" ], + "version" : "None" + }, + { + "opname" : "OpPtrCastToCrossWorkgroupINTEL", + "class" : "@exclude", + "opcode" : 5934, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpCrossWorkgroupCastToPtrINTEL", + "class" : "@exclude", + "opcode" : 5938, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpReadPipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5946, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpWritePipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5947, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpFPGARegINTEL", + "class" : "Reserved", + "opcode" : 5949, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Input'" } + ], + "capabilities" : [ "FPGARegINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_reg" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetRayTMinKHR", + "class" : "Reserved", + "opcode" : 6016, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetRayFlagsKHR", + "class" : "Reserved", + "opcode" : 6017, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionTKHR", + "class" : "Reserved", + "opcode" : 6018, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + "class" : "Reserved", + "opcode" : 6019, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceIdKHR", + "class" : "Reserved", + "opcode" : 6020, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + "class" : "Reserved", + "opcode" : 6021, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionGeometryIndexKHR", + "class" : "Reserved", + "opcode" : 6022, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionPrimitiveIndexKHR", + "class" : "Reserved", + "opcode" : 6023, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionBarycentricsKHR", + "class" : "Reserved", + "opcode" : 6024, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionFrontFaceKHR", + "class" : "Reserved", + "opcode" : 6025, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + "class" : "Reserved", + "opcode" : 6026, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectRayDirectionKHR", + "class" : "Reserved", + "opcode" : 6027, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectRayOriginKHR", + "class" : "Reserved", + "opcode" : 6028, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetWorldRayDirectionKHR", + "class" : "Reserved", + "opcode" : 6029, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetWorldRayOriginKHR", + "class" : "Reserved", + "opcode" : 6030, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectToWorldKHR", + "class" : "Reserved", + "opcode" : 6031, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionWorldToObjectKHR", + "class" : "Reserved", + "opcode" : 6032, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFAddEXT", + "class" : "Atomic", + "opcode" : 6035, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat16AddEXT", "AtomicFloat32AddEXT", "AtomicFloat64AddEXT", "AtomicFloat16VectorNV" ], + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + }, + { + "opname" : "OpTypeBufferSurfaceINTEL", + "class" : "Type-Declaration", + "opcode" : 6086, + "operands" : [ + { "kind" : "IdResult" }, + { + "kind" : "AccessQualifier", + "name" : "'AccessQualifier'" + } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeStructContinuedINTEL", + "class" : "Type-Declaration", + "opcode" : 6090, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ], + "capabilities" : [ "LongCompositesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpConstantCompositeContinuedINTEL", + "class" : "Constant-Creation", + "opcode" : 6091, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "capabilities" : [ "LongCompositesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSpecConstantCompositeContinuedINTEL", + "class" : "Constant-Creation", + "opcode" : 6092, + "operands" : [ + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "capabilities" : [ "LongCompositesINTEL" ], + "version" : "None" + }, + { + "opname" : "OpCompositeConstructContinuedINTEL", + "class" : "Composite", + "opcode" : 6096, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ], + "capabilities" : [ "LongCompositesINTEL" ], + "version": "None" + }, + { + "opname" : "OpConvertFToBF16INTEL", + "class" : "Conversion", + "opcode" : 6116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ], + "capabilities" : [ "BFloat16ConversionINTEL" ], + "version" : "None" + }, + { + "opname" : "OpConvertBF16ToFINTEL", + "class" : "Conversion", + "opcode" : 6117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'BFloat16 Value'" } + ], + "capabilities" : [ "BFloat16ConversionINTEL" ], + "version" : "None" + }, + { + "opname" : "OpControlBarrierArriveINTEL", + "class" : "Barrier", + "opcode" : 6142, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "SplitBarrierINTEL" ], + "version" : "None" + }, + { + "opname" : "OpControlBarrierWaitINTEL", + "class" : "Barrier", + "opcode" : 6143, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "SplitBarrierINTEL" ], + "version" : "None" + }, + { + "opname" : "OpArithmeticFenceEXT", + "class" : "Miscellaneous", + "opcode" : 6145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Target'" } + ], + "capabilities" : [ "ArithmeticFenceEXT" ], + "version" : "None" + }, + { + "opname" : "OpTaskSequenceCreateINTEL", + "class" : "Reserved", + "opcode" : 6163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "LiteralInteger", "name" : "'Pipelined'" }, + { "kind" : "LiteralInteger", "name" : "'UseStallEnableClusters'" }, + { "kind" : "LiteralInteger", "name" : "'GetCapacity'" }, + { "kind" : "LiteralInteger", "name" : "'AsyncCapacity'" } + ], + "capabilities" : [ "TaskSequenceINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTaskSequenceAsyncINTEL", + "class" : "Reserved", + "opcode" : 6164, + "operands" : [ + { "kind" : "IdRef", "name" : "'Sequence'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Arguments'" } + ], + "capabilities" : [ "TaskSequenceINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTaskSequenceGetINTEL", + "class" : "Reserved", + "opcode" : 6165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sequence'" } + ], + "capabilities" : [ "TaskSequenceINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTaskSequenceReleaseINTEL", + "class" : "Reserved", + "opcode" : 6166, + "operands" : [ + { "kind" : "IdRef", "name" : "'Sequence'" } + ], + "capabilities" : [ "TaskSequenceINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeTaskSequenceINTEL", + "class" : "Type-Declaration", + "opcode" : 6199, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "TaskSequenceINTEL" ], + "version": "None" + }, + { + "opname" : "OpSubgroupBlockPrefetchINTEL", + "class" : "Group", + "opcode" : 6221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'NumBytes'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "SubgroupBufferPrefetchINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroup2DBlockLoadINTEL", + "class" : "Group", + "opcode" : 6231, + "operands" : [ + { "kind" : "IdRef", "name" : "'Element Size'" }, + { "kind" : "IdRef", "name" : "'Block Width'" }, + { "kind" : "IdRef", "name" : "'Block Height'" }, + { "kind" : "IdRef", "name" : "'Block Count'" }, + { "kind" : "IdRef", "name" : "'Src Base Pointer'" }, + { "kind" : "IdRef", "name" : "'Memory Width'" }, + { "kind" : "IdRef", "name" : "'Memory Height'" }, + { "kind" : "IdRef", "name" : "'Memory Pitch'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Dst Pointer'" } + ], + "capabilities" : [ "Subgroup2DBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroup2DBlockLoadTransformINTEL", + "class" : "Group", + "opcode" : 6232, + "operands" : [ + { "kind" : "IdRef", "name" : "'Element Size'" }, + { "kind" : "IdRef", "name" : "'Block Width'" }, + { "kind" : "IdRef", "name" : "'Block Height'" }, + { "kind" : "IdRef", "name" : "'Block Count'" }, + { "kind" : "IdRef", "name" : "'Src Base Pointer'" }, + { "kind" : "IdRef", "name" : "'Memory Width'" }, + { "kind" : "IdRef", "name" : "'Memory Height'" }, + { "kind" : "IdRef", "name" : "'Memory Pitch'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Dst Pointer'" } + ], + "capabilities" : [ "Subgroup2DBlockTransformINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroup2DBlockLoadTransposeINTEL", + "class" : "Group", + "opcode" : 6233, + "operands" : [ + { "kind" : "IdRef", "name" : "'Element Size'" }, + { "kind" : "IdRef", "name" : "'Block Width'" }, + { "kind" : "IdRef", "name" : "'Block Height'" }, + { "kind" : "IdRef", "name" : "'Block Count'" }, + { "kind" : "IdRef", "name" : "'Src Base Pointer'" }, + { "kind" : "IdRef", "name" : "'Memory Width'" }, + { "kind" : "IdRef", "name" : "'Memory Height'" }, + { "kind" : "IdRef", "name" : "'Memory Pitch'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Dst Pointer'" } + ], + "capabilities" : [ "Subgroup2DBlockTransposeINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroup2DBlockPrefetchINTEL", + "class" : "Group", + "opcode" : 6234, + "operands" : [ + { "kind" : "IdRef", "name" : "'Element Size'" }, + { "kind" : "IdRef", "name" : "'Block Width'" }, + { "kind" : "IdRef", "name" : "'Block Height'" }, + { "kind" : "IdRef", "name" : "'Block Count'" }, + { "kind" : "IdRef", "name" : "'Src Base Pointer'" }, + { "kind" : "IdRef", "name" : "'Memory Width'" }, + { "kind" : "IdRef", "name" : "'Memory Height'" }, + { "kind" : "IdRef", "name" : "'Memory Pitch'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "Subgroup2DBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroup2DBlockStoreINTEL", + "class" : "Group", + "opcode" : 6235, + "operands" : [ + { "kind" : "IdRef", "name" : "'Element Size'" }, + { "kind" : "IdRef", "name" : "'Block Width'" }, + { "kind" : "IdRef", "name" : "'Block Height'" }, + { "kind" : "IdRef", "name" : "'Block Count'" }, + { "kind" : "IdRef", "name" : "'Src Pointer'" }, + { "kind" : "IdRef", "name" : "'Dst Base Pointer'" }, + { "kind" : "IdRef", "name" : "'Memory Width'" }, + { "kind" : "IdRef", "name" : "'Memory Height'" }, + { "kind" : "IdRef", "name" : "'Memory Pitch'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "Subgroup2DBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupMatrixMultiplyAccumulateINTEL", + "class" : "Group", + "opcode" : 6237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'K Dim'" }, + { "kind" : "IdRef", "name" : "'Matrix A'" }, + { "kind" : "IdRef", "name" : "'Matrix B'" }, + { "kind" : "IdRef", "name" : "'Matrix C'" }, + { "kind" : "MatrixMultiplyAccumulateOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SubgroupMatrixMultiplyAccumulateINTEL" ], + "version" : "None" + }, + { + "opname" : "OpBitwiseFunctionINTEL", + "class" : "Bit", + "opcode" : 6242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "IdRef", "name" : "'C'" }, + { "kind" : "IdRef", "name" : "'LUTIndex'" } + ], + "capabilities" : [ "TernaryBitwiseFunctionINTEL" ], + "version" : "None" + }, + { + "opname" : "OpGroupIMulKHR", + "class" : "Group", + "opcode" : 6401, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupFMulKHR", + "class" : "Group", + "opcode" : 6402, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupBitwiseAndKHR", + "class" : "Group", + "opcode" : 6403, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupBitwiseOrKHR", + "class" : "Group", + "opcode" : 6404, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupBitwiseXorKHR", + "class" : "Group", + "opcode" : 6405, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupLogicalAndKHR", + "class" : "Group", + "opcode" : 6406, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupLogicalOrKHR", + "class" : "Group", + "opcode" : 6407, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpGroupLogicalXorKHR", + "class" : "Group", + "opcode" : 6408, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "GroupUniformArithmeticKHR" ], + "version" : "None" + }, + { + "opname" : "OpRoundFToTF32INTEL", + "class" : "Conversion", + "opcode" : 6426, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ], + "capabilities" : [ "TensorFloat32RoundingINTEL" ], + "version" : "None" + }, + { + "opname" : "OpMaskedGatherINTEL", + "class" : "Memory", + "opcode" : 6428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'PtrVector'" }, + { "kind" : "LiteralInteger", "name" : "'Alignment'" }, + { "kind" : "IdRef", "name" : "'Mask'" }, + { "kind" : "IdRef", "name" : "'FillEmpty'" } + ], + "capabilities" : [ "MaskedGatherScatterINTEL" ], + "version" : "None" + }, + { + "opname" : "OpMaskedScatterINTEL", + "class" : "Memory", + "opcode" : 6429, + "operands" : [ + { "kind" : "IdRef", "name" : "'InputVector'" }, + { "kind" : "IdRef", "name" : "'PtrVector'" }, + { "kind" : "LiteralInteger", "name" : "'Alignment'" }, + { "kind" : "IdRef", "name" : "'Mask'" } + ], + "capabilities" : [ "MaskedGatherScatterINTEL" ], + "version" : "None" + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + }, + { + "enumerant" : "MakeTexelAvailable", + "aliases" : [ "MakeTexelAvailableKHR" ], + "value" : "0x0100", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeTexelVisible", + "aliases" : [ "MakeTexelVisibleKHR" ], + "value" : "0x0200", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivateTexel", + "aliases" : [ "NonPrivateTexelKHR" ], + "value" : "0x0400", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "VolatileTexel", + "aliases" : [ "VolatileTexelKHR" ], + "value" : "0x0800", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "SignExtend", + "value" : "0x1000", + "version" : "1.4" + }, + { + "enumerant" : "ZeroExtend", + "value" : "0x2000", + "version" : "1.4" + }, + { + "enumerant" : "Nontemporal", + "value" : "0x4000", + "version" : "1.6" + }, + { + "enumerant" : "Offsets", + "value" : "0x10000", + "parameters" : [ + { "kind" : "IdRef" } + ], + "version": "1.0" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "version" : "1.0" + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "version" : "1.0" + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "version" : "1.0" + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "version" : "1.0" + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "version" : "1.0" + }, + { + "enumerant" : "AllowContract", + "aliases" : ["AllowContractFastINTEL"], + "value" : "0x10000", + "capabilities" : [ "FloatControls2", "FPFastMathModeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "AllowReassoc", + "aliases" : ["AllowReassocINTEL"], + "value" : "0x20000", + "capabilities" : [ "FloatControls2", "FPFastMathModeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "AllowTransform", + "value" : "0x40000", + "capabilities" : [ "FloatControls2" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001", + "version" : "1.0" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002", + "version" : "1.0" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001", + "version" : "1.0" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002", + "version" : "1.0" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004", + "version" : "1.1" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.1" + }, + { + "enumerant" : "MinIterations", + "value" : "0x0010", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "MaxIterations", + "value" : "0x0020", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "IterationMultiple", + "value" : "0x0040", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "PeelCount", + "value" : "0x0080", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "PartialCount", + "value" : "0x0100", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "InitiationIntervalINTEL", + "value" : "0x10000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaxConcurrencyINTEL", + "value" : "0x20000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "DependencyArrayINTEL", + "value" : "0x40000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "PipelineEnableINTEL", + "value" : "0x80000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "LoopCoalesceINTEL", + "value" : "0x100000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaxInterleavingINTEL", + "value" : "0x200000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "SpeculatedIterationsINTEL", + "value" : "0x400000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "NoFusionINTEL", + "value" : "0x800000", + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "LoopCountINTEL", + "value" : "0x1000000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaxReinvocationDelayINTEL", + "value" : "0x2000000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "Inline", + "value" : "0x0001", + "version" : "1.0" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002", + "version" : "1.0" + }, + { + "enumerant" : "Pure", + "value" : "0x0004", + "version" : "1.0" + }, + { + "enumerant" : "Const", + "value" : "0x0008", + "version" : "1.0" + }, + { + "enumerant" : "OptNoneEXT", + "aliases" : ["OptNoneINTEL"], + "value" : "0x10000", + "capabilities" : [ "OptNoneEXT" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "aliases" : ["None"], + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002", + "version" : "1.0" + }, + { + "enumerant" : "Release", + "value" : "0x0004", + "version" : "1.0" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008", + "version" : "1.0" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010", + "version" : "1.0" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080", + "version" : "1.0" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100", + "version" : "1.0" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200", + "version" : "1.0" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ], + "version": "1.0" + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800", + "version" : "1.0" + }, + { + "enumerant" : "OutputMemory", + "aliases" : ["OutputMemoryKHR"], + "value" : "0x1000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeAvailable", + "aliases" : ["MakeAvailableKHR"], + "value" : "0x2000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeVisible", + "aliases" : ["MakeVisibleKHR"], + "value" : "0x4000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "Volatile", + "value" : "0x8000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001", + "version" : "1.0" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.0" + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004", + "version" : "1.0" + }, + { + "enumerant" : "MakePointerAvailable", + "aliases" : ["MakePointerAvailableKHR"], + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakePointerVisible", + "aliases" : ["MakePointerVisibleKHR"], + "value" : "0x0010", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivatePointer", + "aliases" : ["NonPrivatePointerKHR"], + "value" : "0x0020", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "AliasScopeINTELMask", + "value" : "0x10000", + "parameters" : [ + { "kind" : "IdRef" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + }, + { + "enumerant" : "NoAliasINTELMask", + "parameters" : [ + { "kind" : "IdRef" } + ], + "value" : "0x20000", + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "1.0" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ], + "version": "1.0" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "RayFlags", + "enumerants" : [ + { + "enumerant" : "NoneKHR", + "value" : "0x0000", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "OpaqueKHR", + "value" : "0x0001", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "NoOpaqueKHR", + "value" : "0x0002", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "TerminateOnFirstHitKHR", + "value" : "0x0004", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipClosestHitShaderKHR", + "value" : "0x0008", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullBackFacingTrianglesKHR", + "value" : "0x0010", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullFrontFacingTrianglesKHR", + "value" : "0x0020", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullOpaqueKHR", + "value" : "0x0040", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullNoOpaqueKHR", + "value" : "0x0080", + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipTrianglesKHR", + "aliases" : ["SkipBuiltinPrimitivesNV"], + "value" : "0x0100", + "capabilities" : [ "RayTraversalPrimitiveCullingKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipAABBsKHR", + "value" : "0x0200", + "capabilities" : [ "RayTraversalPrimitiveCullingKHR" ], + "version" : "None" + }, + { + "enumerant" : "ForceOpacityMicromap2StateEXT", + "value" : "0x0400", + "capabilities" : [ "RayTracingOpacityMicromapEXT" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FragmentShadingRate", + "enumerants" : [ + { + "enumerant" : "Vertical2Pixels", + "value" : "0x0001", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Vertical4Pixels", + "value" : "0x0002", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Horizontal2Pixels", + "value" : "0x0004", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Horizontal4Pixels", + "value" : "0x0008", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "RawAccessChainOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "RobustnessPerComponentNV", + "value" : "0x0001", + "capabilities" : [ "RawAccessChainsNV" ], + "version" : "None" + }, + { + "enumerant" : "RobustnessPerElementNV", + "value" : "0x0002", + "capabilities" : [ "RawAccessChainsNV" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "ESSL", + "value" : 1, + "version" : "1.0" + }, + { + "enumerant" : "GLSL", + "value" : 2, + "version" : "1.0" + }, + { + "enumerant" : "OpenCL_C", + "value" : 3, + "version" : "1.0" + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4, + "version" : "1.0" + }, + { + "enumerant" : "HLSL", + "value" : 5, + "version" : "1.0" + }, + { + "enumerant" : "CPP_for_OpenCL", + "value" : 6, + "version" : "1.0" + }, + { + "enumerant" : "SYCL", + "value" : 7, + "version" : "1.0" + }, + { + "enumerant" : "HERO_C", + "value" : 8, + "version" : "1.0" + }, + { + "enumerant" : "NZSL", + "value" : 9, + "version" : "1.0" + }, + { + "enumerant" : "WGSL", + "value" : 10, + "version" : "1.0" + }, + { + "enumerant" : "Slang", + "value" : 11, + "version" : "1.0" + }, + { + "enumerant" : "Zig", + "value" : 12, + "version" : "1.0" + }, + { + "enumerant" : "Rust", + "value" : 13, + "version" : "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "TaskNV", + "value" : 5267, + "capabilities" : [ "MeshShadingNV" ], + "version" : "None" + }, + { + "enumerant" : "MeshNV", + "value" : 5268, + "capabilities" : [ "MeshShadingNV" ], + "version" : "None" + }, + { + "enumerant" : "RayGenerationKHR", + "aliases" : ["RayGenerationNV"], + "value" : 5313, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "IntersectionKHR", + "aliases" : ["IntersectionNV"], + "value" : 5314, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "AnyHitKHR", + "aliases" : ["AnyHitNV"], + "value" : 5315, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "ClosestHitKHR", + "aliases" : ["ClosestHitNV"], + "value" : 5316, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "MissKHR", + "aliases" : ["MissNV"], + "value" : 5317, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "CallableKHR", + "aliases" : ["CallableNV"], + "value" : 5318, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "TaskEXT", + "value" : 5364, + "capabilities" : [ "MeshShadingEXT" ], + "version" : "None" + }, + { + "enumerant" : "MeshEXT", + "value" : 5365, + "capabilities" : [ "MeshShadingEXT" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ], + "version": "1.0" + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ], + "version": "1.0" + }, + { + "enumerant" : "PhysicalStorageBuffer64", + "aliases" : ["PhysicalStorageBuffer64EXT"], + "value" : 5348, + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Vulkan", + "aliases" : ["VulkanKHR"], + "value" : 3, + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ], + "version": "1.0" + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ], + "version": "1.0" + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ], + "version": "1.0" + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ], + "version": "1.0" + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation", "MeshShadingNV", "MeshShadingEXT" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ], + "version": "1.0" + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry", "MeshShadingNV", "MeshShadingEXT" ], + "version": "1.0" + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ], + "version": "1.0" + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupsPerWorkgroupId", + "value" : 37, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Subgroups Per Workgroup'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "LocalSizeId", + "value" : 38, + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "LocalSizeHintId", + "value" : 39, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size hint'" }, + { "kind" : "IdRef", "name" : "'y size hint'" }, + { "kind" : "IdRef", "name" : "'z size hint'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "NonCoherentColorAttachmentReadEXT", + "value" : 4169, + "capabilities" : [ "TileImageColorReadAccessEXT" ], + "version" : "None" + }, + { + "enumerant" : "NonCoherentDepthAttachmentReadEXT", + "value" : 4170, + "capabilities" : [ "TileImageDepthReadAccessEXT" ], + "version" : "None" + }, + { + "enumerant" : "NonCoherentStencilAttachmentReadEXT", + "value" : 4171, + "capabilities" : [ "TileImageStencilReadAccessEXT" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupUniformControlFlowKHR", + "value" : 4421, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_subgroup_uniform_control_flow" ], + "version" : "None" + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ], + "extensions" : [ "SPV_KHR_post_depth_coverage" ], + "version" : "None" + }, + { + "enumerant" : "DenormPreserve", + "value" : 4459, + "capabilities" : [ "DenormPreserve" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "DenormFlushToZero", + "value" : 4460, + "capabilities" : [ "DenormFlushToZero" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "SignedZeroInfNanPreserve", + "value" : 4461, + "capabilities" : [ "SignedZeroInfNanPreserve" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTE", + "value" : 4462, + "capabilities" : [ "RoundingModeRTE" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTZ", + "value" : 4463, + "capabilities" : [ "RoundingModeRTZ" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "NonCoherentTileAttachmentReadQCOM", + "value" : 4489, + "capabilities" : [ "TileShadingQCOM" ], + "version" : "None" + }, + { + "enumerant" : "TileShadingRateQCOM", + "value" : 4490, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x rate'" }, + { "kind" : "LiteralInteger", "name" : "'y rate'" }, + { "kind" : "LiteralInteger", "name" : "'z rate'" } + ], + "capabilities" : [ "TileShadingQCOM" ], + "version": "None" + }, + { + "enumerant": "EarlyAndLateFragmentTestsAMD", + "value": 5017, + "capabilities": [ "Shader" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests" ], + "version": "None" + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "CoalescingAMDX", + "value" : 5069, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "IsApiEntryAMDX", + "value" : 5070, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Is Entry'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "MaxNodeRecursionAMDX", + "value" : 5071, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Number of recursions'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "StaticNumWorkgroupsAMDX", + "value" : 5072, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "ShaderIndexAMDX", + "value" : 5073, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Shader Index'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "MaxNumWorkgroupsAMDX", + "value" : 5077, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant": "StencilRefUnchangedFrontAMD", + "value": 5079, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "StencilRefGreaterFrontAMD", + "value": 5080, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "StencilRefLessFrontAMD", + "value": 5081, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "StencilRefUnchangedBackAMD", + "value": 5082, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "StencilRefGreaterBackAMD", + "value": 5083, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "StencilRefLessBackAMD", + "value": 5084, + "capabilities": [ "StencilExportEXT" ], + "extensions": [ "SPV_AMD_shader_early_and_late_fragment_tests", "SPV_EXT_shader_stencil_export" ], + "version": "None" + }, + { + "enumerant": "QuadDerivativesKHR", + "value": 5088, + "capabilities": [ "QuadControlKHR" ], + "version": "None" + }, + { + "enumerant" : "RequireFullQuadsKHR", + "value" : 5089, + "capabilities" : [ "QuadControlKHR" ], + "version" : "None" + }, + { + "enumerant" : "SharesInputWithAMDX", + "value" : 5102, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Node Name'" }, + { "kind" : "IdRef", "name" : "'Shader Index'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "OutputLinesEXT", + "aliases" : ["OutputLinesNV"], + "value" : 5269, + "capabilities" : [ "MeshShadingNV", "MeshShadingEXT" ], + "extensions" : [ "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "OutputPrimitivesEXT", + "aliases" : ["OutputPrimitivesNV"], + "value" : 5270, + "capabilities" : [ "MeshShadingNV", "MeshShadingEXT" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Primitive count'" } + ], + "extensions" : [ "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "DerivativeGroupQuadsKHR", + "aliases" : ["DerivativeGroupQuadsNV"], + "value" : 5289, + "capabilities" : [ "ComputeDerivativeGroupQuadsNV", "ComputeDerivativeGroupQuadsKHR" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives", "SPV_KHR_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "DerivativeGroupLinearKHR", + "aliases" : ["DerivativeGroupLinearNV"], + "value" : 5290, + "capabilities" : [ "ComputeDerivativeGroupLinearNV", "ComputeDerivativeGroupLinearKHR" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives", "SPV_KHR_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "OutputTrianglesEXT", + "aliases" : ["OutputTrianglesNV"], + "value" : 5298, + "capabilities" : [ "MeshShadingNV", "MeshShadingEXT" ], + "extensions" : [ "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PixelInterlockOrderedEXT", + "value" : 5366, + "capabilities" : [ "FragmentShaderPixelInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "PixelInterlockUnorderedEXT", + "value" : 5367, + "capabilities" : [ "FragmentShaderPixelInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "SampleInterlockOrderedEXT", + "value" : 5368, + "capabilities" : [ "FragmentShaderSampleInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "SampleInterlockUnorderedEXT", + "value" : 5369, + "capabilities" : [ "FragmentShaderSampleInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShadingRateInterlockOrderedEXT", + "value" : 5370, + "capabilities" : [ "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShadingRateInterlockUnorderedEXT", + "value" : 5371, + "capabilities" : [ "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "SharedLocalMemorySizeINTEL", + "value" : 5618, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RoundingModeRTPINTEL", + "value" : 5620, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RoundingModeRTNINTEL", + "value" : 5621, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeALTINTEL", + "value" : 5622, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeIEEEINTEL", + "value" : 5623, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "capabilities" : [ "RoundToInfinityINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaxWorkgroupSizeINTEL", + "value" : 5893, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'max_x_size'" }, + { "kind" : "LiteralInteger", "name" : "'max_y_size'" }, + { "kind" : "LiteralInteger", "name" : "'max_z_size'" } + ], + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxWorkDimINTEL", + "value" : 5894, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'max_dimensions'" } + ], + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NoGlobalOffsetINTEL", + "value" : 5895, + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NumSIMDWorkitemsINTEL", + "value" : 5896, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'vector_width'" } + ], + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "SchedulerTargetFmaxMhzINTEL", + "value" : 5903, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'target_fmax'" } + ], + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaximallyReconvergesKHR", + "value" : 6023, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_maximal_reconvergence" ], + "version" : "None" + }, + { + "enumerant" : "FPFastMathDefault", + "value" : 6028, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Target Type'" }, + { "kind" : "IdRef", "name" : "'Fast-Math Mode'" } + ], + "capabilities" : [ "FloatControls2" ], + "version" : "None" + }, + { + "enumerant" : "StreamingInterfaceINTEL", + "value" : 6154, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'StallFreeReturn'" } + ], + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RegisterMapInterfaceINTEL", + "value" : 6160, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'WaitForDoneWrite'" } + ], + "capabilities" : [ "FPGAKernelAttributesv2INTEL" ], + "version" : "None" + }, + { + "enumerant" : "NamedBarrierCountINTEL", + "value" : 6417, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Barrier Count'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaximumRegistersINTEL", + "value" : 6461, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of Registers'" } + ], + "capabilities" : [ "RegisterLimitsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaximumRegistersIdINTEL", + "value" : 6462, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Number of Registers'" } + ], + "capabilities" : [ "RegisterLimitsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "NamedMaximumRegistersINTEL", + "value" : 6463, + "parameters" : [ + { "kind" : "NamedMaximumNumberOfRegisters", "name" : "'Named Maximum Number of Registers'" } + ], + "capabilities" : [ "RegisterLimitsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "Input", + "value" : 1, + "version" : "1.0" + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Workgroup", + "value" : 4, + "version" : "1.0" + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5, + "version" : "1.0" + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader", "VectorComputeINTEL" ], + "version": "1.0" + }, + { + "enumerant" : "Function", + "value" : 7, + "version" : "1.0" + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ], + "version": "1.0" + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ], + "version": "1.0" + }, + { + "enumerant" : "Image", + "value" : 11, + "version" : "1.0" + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ], + "version" : "1.3" + }, + { + "enumerant" : "TileImageEXT", + "value" : 4172, + "capabilities" : [ "TileImageColorReadAccessEXT" ], + "version" : "None" + }, + { + "enumerant" : "TileAttachmentQCOM", + "value" : 4491, + "capabilities" : [ "TileShadingQCOM" ], + "version" : "None" + }, + { + "enumerant" : "NodePayloadAMDX", + "value" : 5068, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "CallableDataKHR", + "aliases" : ["CallableDataNV"], + "value" : 5328, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingCallableDataKHR", + "aliases" : ["IncomingCallableDataNV"], + "value" : 5329, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayPayloadKHR", + "aliases" : ["RayPayloadNV"], + "value" : 5338, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "HitAttributeKHR", + "aliases" : ["HitAttributeNV"], + "value" : 5339, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayPayloadKHR", + "aliases" : ["IncomingRayPayloadNV"], + "value" : 5342, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "ShaderRecordBufferKHR", + "aliases" : ["ShaderRecordBufferNV"], + "value" : 5343, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "version" : "None" + }, + { + "enumerant" : "PhysicalStorageBuffer", + "aliases" : ["PhysicalStorageBufferEXT"], + "value" : 5349, + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + }, + { + "enumerant" : "HitObjectAttributeNV", + "value" : 5385, + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "enumerant" : "TaskPayloadWorkgroupEXT", + "value" : 5402, + "extensions" : [ "SPV_EXT_mesh_shader" ], + "capabilities" : [ "MeshShadingEXT" ], + "version" : "1.4" + }, + { + "enumerant" : "CodeSectionINTEL", + "value" : 5605, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "capabilities" : [ "FunctionPointersINTEL" ], + "version" : "None" + }, + { + "enumerant" : "DeviceOnlyINTEL", + "value" : 5936, + "extensions" : [ + "SPV_INTEL_usm_storage_classes" + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "HostOnlyINTEL", + "value" : 5937, + "extensions" : [ + "SPV_INTEL_usm_storage_classes" + ], + "capabilities" : [ "USMStorageClassesINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ], + "version": "1.0" + }, + { + "enumerant" : "2D", + "value" : 1, + "version" : "1.0" + }, + { + "enumerant" : "3D", + "value" : 2, + "version" : "1.0" + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ], + "version": "1.0" + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ], + "version": "1.0" + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ], + "version": "1.0" + }, + { + "enumerant" : "TileImageDataEXT", + "value" : 4173, + "capabilities" : [ "TileImageColorReadAccessEXT" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "version": "1.0" + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "version": "1.0" + }, + { + "enumerant" : "Clamp", + "value" : 2, + "version": "1.0" + }, + { + "enumerant" : "Repeat", + "value" : 3, + "version": "1.0" + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "version": "1.0" + }, + { + "enumerant" : "Linear", + "value" : 1, + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ], + "version": "1.0" + }, + { + "enumerant" : "R64ui", + "value" : 40, + "capabilities" : [ "Int64ImageEXT" ], + "version": "1.0" + }, + { + "enumerant" : "R64i", + "value" : 41, + "capabilities" : [ "Int64ImageEXT" ], + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "version": "1.0" + }, + { + "enumerant" : "A", + "value" : 1, + "version": "1.0" + }, + { + "enumerant" : "RG", + "value" : 2, + "version": "1.0" + }, + { + "enumerant" : "RA", + "value" : 3, + "version": "1.0" + }, + { + "enumerant" : "RGB", + "value" : 4, + "version": "1.0" + }, + { + "enumerant" : "RGBA", + "value" : 5, + "version": "1.0" + }, + { + "enumerant" : "BGRA", + "value" : 6, + "version": "1.0" + }, + { + "enumerant" : "ARGB", + "value" : 7, + "version": "1.0" + }, + { + "enumerant" : "Intensity", + "value" : 8, + "version": "1.0" + }, + { + "enumerant" : "Luminance", + "value" : 9, + "version": "1.0" + }, + { + "enumerant" : "Rx", + "value" : 10, + "version": "1.0" + }, + { + "enumerant" : "RGx", + "value" : 11, + "version": "1.0" + }, + { + "enumerant" : "RGBx", + "value" : 12, + "version": "1.0" + }, + { + "enumerant" : "Depth", + "value" : 13, + "version": "1.0" + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "version": "1.0" + }, + { + "enumerant" : "sRGB", + "value" : 15, + "version": "1.0" + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "version": "1.0" + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "version": "1.0" + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "version": "1.0" + }, + { + "enumerant" : "ABGR", + "value" : 19, + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "version": "1.0" + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "version": "1.0" + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "version": "1.0" + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "version": "1.0" + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "version": "1.0" + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "version": "1.0" + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "version": "1.0" + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "version": "1.0" + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "version": "1.0" + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "version": "1.0" + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "version": "1.0" + }, + { + "enumerant" : "Float", + "value" : 14, + "version": "1.0" + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "version": "1.0" + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "version": "1.0" + }, + { + "enumerant" : "UnormInt10X6EXT", + "value" : 17, + "version": "1.0" + }, + { + "enumerant" : "UnsignedIntRaw10EXT", + "value" : 19, + "version": "1.0" + }, + { + "enumerant" : "UnsignedIntRaw12EXT", + "value" : 20, + "version": "1.0" + }, + { + "enumerant" : "UnormInt2_101010EXT", + "value" : 21, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt10X6EXT", + "value" : 22, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt12X4EXT", + "value" : 23, + "version": "1.0" + }, + { + "enumerant" : "UnsignedInt14X2EXT", + "value" : 24, + "version": "1.0" + }, + { + "enumerant" : "UnormInt12X4EXT", + "value" : 25, + "version": "1.0" + }, + { + "enumerant" : "UnormInt14X2EXT", + "value" : 26, + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "RTZ", + "value" : 1, + "version" : "1.0" + }, + { + "enumerant" : "RTP", + "value" : 2, + "version" : "1.0" + }, + { + "enumerant" : "RTN", + "value" : 3, + "version" : "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPDenormMode", + "enumerants" : [ + { + "enumerant" : "Preserve", + "value" : 0, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FlushToZero", + "value" : 1, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "QuantizationModes", + "enumerants" : [ + { + "enumerant" : "TRN", + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "TRN_ZERO", + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND", + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_ZERO", + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_INF", + "value" : 4, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_MIN_INF", + "value" : 5, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_CONV", + "value" : 6, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "RND_CONV_ODD", + "value" : 7, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPOperationMode", + "enumerants" : [ + { + "enumerant" : "IEEE", + "value" : 0, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ALT", + "value" : 1, + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "OverflowModes", + "enumerants" : [ + { + "enumerant" : "WRAP", + "value" : 0, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT", + "value" : 1, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT_ZERO", + "value" : 2, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + }, + { + "enumerant" : "SAT_SYM", + "value" : 3, + "capabilities" : [ "ArbitraryPrecisionFixedPointINTEL"], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ], + "version": "1.0" + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ], + "version": "1.0" + }, + { + "enumerant" : "LinkOnceODR", + "value" : 2, + "capabilities" : [ "Linkage" ], + "extensions" : [ "SPV_KHR_linkonce_odr" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ], + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "HostAccessQualifier", + "enumerants" : [ + { + "enumerant" : "NoneINTEL", + "value" : 0, + "capabilities" : [ "GlobalVariableHostAccessINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ReadINTEL", + "value" : 1, + "capabilities" : [ "GlobalVariableHostAccessINTEL" ], + "version" : "None" + }, + { + "enumerant" : "WriteINTEL", + "value" : 2, + "capabilities" : [ "GlobalVariableHostAccessINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ReadWriteINTEL", + "value" : 3, + "capabilities" : [ "GlobalVariableHostAccessINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "RuntimeAlignedINTEL", + "value" : 5940, + "capabilities" : [ "RuntimeAlignedAttributeINTEL" ], + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ], + "version": "1.0", + "lastVersion" : "1.3" + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ], + "version": "1.0" + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ], + "version": "1.0" + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ], + "version": "1.0" + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ], + "version": "1.0" + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Restrict", + "value" : 19, + "version" : "1.0" + }, + { + "enumerant" : "Aliased", + "value" : 20, + "version" : "1.0" + }, + { + "enumerant" : "Volatile", + "value" : 21, + "version" : "1.0" + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Coherent", + "value" : 23, + "version": "1.0" + }, + { + "enumerant" : "NonWritable", + "value" : 24, + "version": "1.0" + }, + { + "enumerant" : "NonReadable", + "value" : 25, + "version": "1.0" + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader", "UniformDecoration" ], + "version": "1.0" + }, + { + "enumerant" : "UniformId", + "value" : 27, + "capabilities" : [ "Shader", "UniformDecoration" ], + "parameters" : [ + { "kind" : "IdScope", "name" : "'Execution'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ], + "version": "1.0" + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ], + "version": "1.0" + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ], + "version": "1.0" + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ], + "version": "1.0" + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ], + "version": "1.0" + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ], + "version": "1.0" + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel", "FloatControls2" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ], + "version": "1.0" + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ], + "version": "1.0" + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ], + "version": "1.0" + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ], + "version": "1.0" + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "AlignmentId", + "value" : 46, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Alignment'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "MaxByteOffsetId", + "value" : 47, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Max Byte Offset'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "NoSignedWrap", + "value" : 4469, + "extensions" : [ "SPV_KHR_no_integer_wrap_decoration" ], + "version" : "1.4" + }, + { + "enumerant" : "NoUnsignedWrap", + "value" : 4470, + "extensions" : [ "SPV_KHR_no_integer_wrap_decoration" ], + "version" : "1.4" + }, + { + "enumerant" : "WeightTextureQCOM", + "value" : 4487, + "extensions" : [ "SPV_QCOM_image_processing" ], + "version" : "None" + }, + { + "enumerant" : "BlockMatchTextureQCOM", + "value" : 4488, + "extensions" : [ "SPV_QCOM_image_processing" ], + "version" : "None" + }, + { + "enumerant" : "BlockMatchSamplerQCOM", + "value" : 4499, + "extensions" : [ "SPV_QCOM_image_processing2" ], + "version" : "None" + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "NodeSharesPayloadLimitsWithAMDX", + "value" : 5019, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Payload Type'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "NodeMaxPayloadsAMDX", + "value" : 5020, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Max number of payloads'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "TrackFinishWritingAMDX", + "value" : 5078, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "PayloadNodeNameAMDX", + "value" : 5091, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Node Name'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "PayloadNodeBaseIndexAMDX", + "value" : 5098, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Base Index'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "PayloadNodeSparseArrayAMDX", + "value" : 5099, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "PayloadNodeArraySizeAMDX", + "value" : 5100, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Array Size'" } + ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "PayloadDispatchIndirectAMDX", + "value" : 5105, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ], + "version" : "None" + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ], + "version" : "None" + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None", + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "PerPrimitiveEXT", + "aliases" : ["PerPrimitiveNV"], + "value" : 5271, + "capabilities" : [ "MeshShadingNV", "MeshShadingEXT" ], + "extensions" : [ "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerViewNV", + "value" : 5272, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerTaskNV", + "value" : 5273, + "capabilities" : [ "MeshShadingNV", "MeshShadingEXT" ], + "extensions" : [ "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerVertexKHR", + "aliases" : ["PerVertexNV"], + "value" : 5285, + "capabilities" : [ "FragmentBarycentricKHR" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric", "SPV_KHR_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "NonUniform", + "aliases" : ["NonUniformEXT"], + "value" : 5300, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RestrictPointer", + "aliases" : ["RestrictPointerEXT"], + "value" : 5355, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "AliasedPointer", + "aliases" : ["AliasedPointerEXT"], + "value" : 5356, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "HitObjectShaderRecordBufferNV", + "value" : 5386, + "capabilities" : [ "ShaderInvocationReorderNV" ], + "version" : "None" + }, + { + "enumerant" : "BindlessSamplerNV", + "value" : 5398, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BindlessImageNV", + "value" : 5399, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundSamplerNV", + "value" : 5400, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "BoundImageNV", + "value" : 5401, + "capabilities" : [ "BindlessTextureNV" ], + "version" : "None" + }, + { + "enumerant" : "SIMTCallINTEL", + "value" : 5599, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'N'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ReferencedIndirectlyINTEL", + "value" : 5602, + "capabilities" : [ "IndirectReferencesINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "ClobberINTEL", + "value" : 5607, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Register'" } + ], + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "enumerant" : "SideEffectsINTEL", + "value" : 5608, + "capabilities" : [ "AsmINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeVariableINTEL", + "value" : 5624, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FuncParamIOKindINTEL", + "value" : 5625, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Kind'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeFunctionINTEL", + "value" : 5626, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StackCallINTEL", + "value" : 5627, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "GlobalVariableOffsetINTEL", + "value" : 5628, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ], + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "CounterBuffer", + "aliases" : ["HlslCounterBufferGOOGLE"], + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "enumerant" : "UserSemantic", + "aliases" : ["HlslSemanticGOOGLE"], + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "enumerant" : "UserTypeGOOGLE", + "value" : 5636, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'User Type'" } + ], + "extensions" : [ "SPV_GOOGLE_user_type" ], + "version" : "None" + }, + { + "enumerant" : "FunctionRoundingModeINTEL", + "value" : 5822, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPRoundingMode", "name" : "'FP Rounding Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FunctionDenormModeINTEL", + "value" : 5823, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPDenormMode", "name" : "'FP Denorm Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RegisterINTEL", + "value" : 5825, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MemoryINTEL", + "value" : 5826, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Memory Type'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NumbanksINTEL", + "value" : 5827, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Banks'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "BankwidthINTEL", + "value" : 5828, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Bank Width'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxPrivateCopiesINTEL", + "value" : 5829, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Maximum Copies'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "SinglepumpINTEL", + "value" : 5830, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "DoublepumpINTEL", + "value" : 5831, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxReplicatesINTEL", + "value" : 5832, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Maximum Replicates'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "SimpleDualPortINTEL", + "value" : 5833, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MergeINTEL", + "value" : 5834, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Merge Key'" }, + { "kind" : "LiteralString", "name" : "'Merge Type'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "BankBitsINTEL", + "value" : 5835, + "parameters" : [ + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Bank Bits'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "ForcePow2DepthINTEL", + "value" : 5836, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Force Key'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "StridesizeINTEL", + "value" : 5883, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stride Size'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "WordsizeINTEL", + "value" : 5884, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Word Size'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "TrueDualPortINTEL", + "value" : 5885, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "BurstCoalesceINTEL", + "value" : 5899, + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "CacheSizeINTEL", + "value" : 5900, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Cache Size in bytes'" } + ], + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "DontStaticallyCoalesceINTEL", + "value" : 5901, + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "PrefetchINTEL", + "value" : 5902, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Prefetcher Size in bytes'" } + ], + "capabilities" : [ "FPGAMemoryAccessesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StallEnableINTEL", + "value" : 5905, + "capabilities" : [ "FPGAClusterAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FuseLoopsInFunctionINTEL", + "value" : 5907, + "capabilities" : [ "LoopFuseINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MathOpDSPModeINTEL", + "value" : 5909, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Mode'" }, + { "kind" : "LiteralInteger", "name" : "'Propagate'" } + ], + "capabilities" : [ "FPGADSPControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "AliasScopeINTEL", + "value" : 5914, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Aliasing Scopes List'" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "version" : "None" + }, + { + "enumerant" : "NoAliasINTEL", + "value" : 5915, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Aliasing Scopes List'" } + ], + "capabilities" : [ "MemoryAccessAliasingINTEL" ], + "version" : "None" + }, + { + "enumerant" : "InitiationIntervalINTEL", + "value" : 5917, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Cycles'" } + ], + "capabilities" : [ "FPGAInvocationPipeliningAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MaxConcurrencyINTEL", + "value" : 5918, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Invocations'" } + ], + "capabilities" : [ "FPGAInvocationPipeliningAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "PipelineEnableINTEL", + "value" : 5919, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Enable'" } + ], + "capabilities" : [ "FPGAInvocationPipeliningAttributesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "BufferLocationINTEL", + "value" : 5921, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Buffer Location ID'" } + ], + "capabilities" : [ "FPGABufferLocationINTEL" ], + "version" : "None" + }, + { + "enumerant" : "IOPipeStorageINTEL", + "value" : 5944, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'IO Pipe ID'" } + ], + "capabilities" : [ "IOPipesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "FunctionFloatingPointModeINTEL", + "value" : 6080, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" }, + { "kind" : "FPOperationMode", "name" : "'FP Operation Mode'" } + ], + "capabilities" : [ "FunctionFloatControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "SingleElementVectorINTEL", + "value" : 6085, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeCallableFunctionINTEL", + "value" : 6087, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MediaBlockIOINTEL", + "value" : 6140, + "capabilities" : [ "VectorComputeINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StallFreeINTEL", + "value" : 6151, + "capabilities" : [ "FPGAClusterAttributesV2INTEL" ], + "version" : "None" + }, + { + "enumerant" : "FPMaxErrorDecorationINTEL", + "value" : 6170, + "parameters" : [ + { "kind" : "LiteralFloat", "name" : "'Max Error'" } + ], + "capabilities" : [ "FPMaxErrorINTEL" ], + "version" : "None" + }, + { + "enumerant" : "LatencyControlLabelINTEL", + "value" : 6172, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Latency Label'" } + ], + "capabilities" : [ "FPGALatencyControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "LatencyControlConstraintINTEL", + "value" : 6173, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Relative To'" }, + { "kind" : "LiteralInteger", "name" : "'Control Type'" }, + { "kind" : "LiteralInteger", "name" : "'Relative Cycle'" } + ], + "capabilities" : [ "FPGALatencyControlINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ConduitKernelArgumentINTEL", + "value" : 6175, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "RegisterMapKernelArgumentINTEL", + "value" : 6176, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceAddressWidthINTEL", + "value" : 6177, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'AddressWidth'" } + ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceDataWidthINTEL", + "value" : 6178, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'DataWidth'" } + ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceLatencyINTEL", + "value" : 6179, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Latency'" } + ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceReadWriteModeINTEL", + "value" : 6180, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "AccessQualifier", "name" : "'ReadWriteMode'" } + ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceMaxBurstINTEL", + "value" : 6181, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'MaxBurstCount'" } + ], + "version" : "None" + }, + { + "enumerant" : "MMHostInterfaceWaitRequestINTEL", + "value" : 6182, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Waitrequest'" } + ], + "version" : "None" + }, + { + "enumerant" : "StableKernelArgumentINTEL", + "value" : 6183, + "capabilities" : [ "FPGAArgumentInterfacesINTEL" ], + "version" : "None" + }, + { + "enumerant" : "HostAccessINTEL", + "value" : 6188, + "parameters": [ + { "kind" : "HostAccessQualifier", "name" : "'Access'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ], + "capabilities" : [ "GlobalVariableHostAccessINTEL" ], + "version" : "None" + }, + { + "enumerant" : "InitModeINTEL", + "value" : 6190, + "parameters": [ + { "kind" : "InitializationModeQualifier", "name" : "'Trigger'" } + ], + "capabilities" : [ "GlobalVariableFPGADecorationsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ImplementInRegisterMapINTEL", + "value" : 6191, + "parameters": [ + { "kind" : "LiteralInteger", "name" : "Value" } + ], + "capabilities" : [ "GlobalVariableFPGADecorationsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "CacheControlLoadINTEL", + "value" : 6442, + "capabilities" : [ "CacheControlsINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Cache Level'" }, + { "kind" : "LoadCacheControl", "name" : "'Cache Control'" } + ], + "version" : "None" + }, + { + "enumerant" : "CacheControlStoreINTEL", + "value" : 6443, + "capabilities" : [ "CacheControlsINTEL" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Cache Level'" }, + { "kind" : "StoreCacheControl", "name" : "'Cache Control'" } + ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ], + "version": "1.0" + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ], + "version": "1.0" + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation", "RayTracingNV", "RayTracingKHR", "MeshShadingNV", "MeshShadingEXT" ], + "version": "1.0" + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry", "ShaderLayer", "ShaderViewportIndexLayerEXT", "MeshShadingNV", "MeshShadingEXT" ], + "version": "1.0" + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport", "ShaderViewportIndex", "ShaderViewportIndexLayerEXT", "MeshShadingNV", "MeshShadingEXT" ], + "version": "1.0" + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ], + "version": "1.0" + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ], + "version": "1.0" + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24, + "version" : "1.0" + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25, + "version" : "1.0" + }, + { + "enumerant" : "WorkgroupId", + "value" : 26, + "version" : "1.0" + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27, + "version" : "1.0" + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28, + "version" : "1.0" + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29, + "version" : "1.0" + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "GroupNonUniform", "SubgroupBallotKHR" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel", "GroupNonUniform" ], + "version": "1.0" + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel", "GroupNonUniform" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "GroupNonUniform", "SubgroupBallotKHR" ], + "version": "1.0" + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "CoreIDARM", + "value" : 4160, + "capabilities" : [ "CoreBuiltinsARM" ], + "version": "1.0" + }, + { + "enumerant" : "CoreCountARM", + "value" : 4161, + "capabilities" : [ "CoreBuiltinsARM" ], + "version": "1.0" + }, + { + "enumerant" : "CoreMaxIDARM", + "value" : 4162, + "capabilities" : [ "CoreBuiltinsARM" ], + "version": "1.0" + }, + { + "enumerant" : "WarpIDARM", + "value" : 4163, + "capabilities" : [ "CoreBuiltinsARM" ], + "version": "1.0" + }, + { + "enumerant" : "WarpMaxIDARM", + "value" : 4164, + "capabilities" : [ "CoreBuiltinsARM" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupEqMask", + "aliases" : ["SubgroupEqMaskKHR"], + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGeMask", + "aliases" : ["SubgroupGeMaskKHR"], + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGtMask", + "aliases" : ["SubgroupGtMaskKHR"], + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLeMask", + "aliases" : ["SubgroupLeMaskKHR"], + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLtMask", + "aliases" : ["SubgroupLtMaskKHR"], + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters", "MeshShadingNV", "MeshShadingEXT" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters", "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader" ], + "version" : "1.3" + }, + { + "enumerant" : "PrimitiveShadingRateKHR", + "value" : 4432, + "capabilities" : [ "FragmentShadingRateKHR" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ], + "extensions" : [ "SPV_KHR_device_group" ], + "version" : "1.3" + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_KHR_multiview" ], + "version" : "1.3" + }, + { + "enumerant" : "ShadingRateKHR", + "value" : 4444, + "capabilities" : [ "FragmentShadingRateKHR" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "TileOffsetQCOM", + "value" : 4492, + "capabilities" : [ "TileShadingQCOM" ], + "version" : "None" + }, + { + "enumerant" : "TileDimensionQCOM", + "value" : 4493, + "capabilities" : [ "TileShadingQCOM" ], + "version" : "None" + }, + { + "enumerant" : "TileApronSizeQCOM", + "value" : 4494, + "capabilities" : [ "TileShadingQCOM" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "RemainingRecursionLevelsAMDX", + "value" : 5021, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "ShaderIndexAMDX", + "value" : 5073, + "capabilities" : [ "ShaderEnqueueAMDX" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NV_viewport_array2", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "FullyCoveredEXT", + "value" : 5264, + "capabilities" : [ "FragmentFullyCoveredEXT" ], + "extensions" : [ "SPV_EXT_fragment_fully_covered" ], + "version" : "None" + }, + { + "enumerant" : "TaskCountNV", + "value" : 5274, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveCountNV", + "value" : 5275, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveIndicesNV", + "value" : 5276, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ClipDistancePerViewNV", + "value" : 5277, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "CullDistancePerViewNV", + "value" : 5278, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "LayerPerViewNV", + "value" : 5279, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "MeshViewCountNV", + "value" : 5280, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "MeshViewIndicesNV", + "value" : 5281, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordKHR", + "aliases" : ["BaryCoordNV"], + "value" : 5286, + "capabilities" : [ "FragmentBarycentricKHR" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric", "SPV_KHR_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspKHR", + "aliases" : ["BaryCoordNoPerspNV"], + "value" : 5287, + "capabilities" : [ "FragmentBarycentricKHR" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric", "SPV_KHR_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "FragSizeEXT", + "aliases" : ["FragmentSizeNV"], + "value" : 5292 , + "capabilities" : [ "FragmentDensityEXT" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "FragInvocationCountEXT", + "aliases" : ["InvocationsPerPixelNV"], + "value" : 5293, + "capabilities" : [ "FragmentDensityEXT" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "PrimitivePointIndicesEXT", + "value" : 5294, + "capabilities" : [ "MeshShadingEXT" ], + "extensions" : [ "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveLineIndicesEXT", + "value" : 5295, + "capabilities" : [ "MeshShadingEXT" ], + "extensions" : [ "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveTriangleIndicesEXT", + "value" : 5296, + "capabilities" : [ "MeshShadingEXT" ], + "extensions" : [ "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "CullPrimitiveEXT", + "value" : 5299, + "capabilities" : [ "MeshShadingEXT" ], + "extensions" : [ "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "LaunchIdKHR", + "aliases" : ["LaunchIdNV"], + "value" : 5319, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "LaunchSizeKHR", + "aliases" : ["LaunchSizeNV"], + "value" : 5320, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayOriginKHR", + "aliases" : ["WorldRayOriginNV"], + "value" : 5321, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayDirectionKHR", + "aliases" : ["WorldRayDirectionNV"], + "value" : 5322, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayOriginKHR", + "aliases" : ["ObjectRayOriginNV"], + "value" : 5323, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayDirectionKHR", + "aliases" : ["ObjectRayDirectionNV"], + "value" : 5324, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTminKHR", + "aliases" : ["RayTminNV"], + "value" : 5325, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTmaxKHR", + "aliases" : ["RayTmaxNV"], + "value" : 5326, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "InstanceCustomIndexKHR", + "aliases" : ["InstanceCustomIndexNV"], + "value" : 5327, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectToWorldKHR", + "aliases" : ["ObjectToWorldNV"], + "value" : 5330, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldToObjectKHR", + "aliases" : ["WorldToObjectNV"], + "value" : 5331, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitTNV", + "value" : 5332, + "capabilities" : [ "RayTracingNV" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitKindKHR", + "aliases" : ["HitKindNV"], + "value" : 5333, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "CurrentRayTimeNV", + "value" : 5334, + "capabilities" : [ "RayTracingMotionBlurNV" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "enumerant" : "HitTriangleVertexPositionsKHR", + "value" : 5335, + "capabilities" : [ "RayTracingPositionFetchKHR" ], + "version" : "None" + }, + { + "enumerant" : "HitMicroTriangleVertexPositionsNV", + "value" : 5337, + "capabilities" : [ "RayTracingDisplacementMicromapNV" ], + "version" : "None" + }, + { + "enumerant" : "HitMicroTriangleVertexBarycentricsNV", + "value" : 5344, + "capabilities" : [ "RayTracingDisplacementMicromapNV" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayFlagsKHR", + "aliases" : ["IncomingRayFlagsNV"], + "value" : 5351, + "capabilities" : [ "RayTracingNV" , "RayTracingKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayGeometryIndexKHR", + "value" : 5352, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitIsSphereNV", + "value" : 5359, + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "HitIsLSSNV", + "value" : 5360, + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "HitSpherePositionNV", + "value" : 5361, + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "WarpsPerSMNV", + "value" : 5374, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "SMCountNV", + "value" : 5375, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "WarpIDNV", + "value" : 5376, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "SMIDNV", + "value" : 5377, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "HitLSSPositionsNV", + "value" : 5396, + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "HitKindFrontFacingMicroTriangleNV", + "value" : 5405, + "capabilities" : [ "RayTracingDisplacementMicromapNV" ], + "version" : "None" + }, + { + "enumerant" : "HitKindBackFacingMicroTriangleNV", + "value" : 5406, + "capabilities" : [ "RayTracingDisplacementMicromapNV" ], + "version" : "None" + }, + { + "enumerant" : "HitSphereRadiusNV", + "value" : 5420, + "capabilities" : [ "RayTracingSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "HitLSSRadiiNV", + "value" : 5421, + "capabilities" : [ "RayTracingLinearSweptSpheresGeometryNV" ], + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "ClusterIDNV", + "value" : 5436, + "capabilities" : [ "RayTracingClusterAccelerationStructureNV" ], + "extensions" : [ "SPV_NV_cluster_acceleration_structure" ], + "version" : "None" + }, + { + "enumerant" : "CullMaskKHR", + "value" : 6021, + "capabilities" : [ "RayCullMaskKHR" ], + "extensions" : [ "SPV_KHR_ray_cull_mask" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "Device", + "value" : 1, + "version" : "1.0" + }, + { + "enumerant" : "Workgroup", + "value" : 2, + "version" : "1.0" + }, + { + "enumerant" : "Subgroup", + "value" : 3, + "version" : "1.0" + }, + { + "enumerant" : "Invocation", + "value" : 4, + "version" : "1.0" + }, + { + "enumerant" : "QueueFamily", + "aliases" : ["QueueFamilyKHR"], + "value" : 5, + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "ShaderCallKHR", + "value" : 6, + "capabilities" : [ "RayTracingKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ], + "version": "1.0" + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ], + "version": "1.0" + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ], + "version": "1.0" + }, + { + "enumerant" : "ClusteredReduce", + "value" : 3, + "capabilities" : [ "GroupNonUniformClustered" ], + "version" : "1.3" + }, + { + "enumerant" : "PartitionedReduceNV", + "value" : 6, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "PartitionedInclusiveScanNV", + "value" : 7, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "PartitionedExclusiveScanNV", + "value" : 8, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ], + "version": "1.0" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0, + "version" : "1.0" + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ], + "version": "1.0" + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Addresses", + "value" : 4, + "version" : "1.0" + }, + { + "enumerant" : "Linkage", + "value" : 5, + "version" : "1.0" + }, + { + "enumerant" : "Kernel", + "value" : 6, + "version" : "1.0" + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Float16", + "value" : 9, + "version" : "1.0" + }, + { + "enumerant" : "Float64", + "value" : 10, + "version" : "1.0" + }, + { + "enumerant" : "Int64", + "value" : 11, + "version" : "1.0" + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ], + "version": "1.0" + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ], + "version": "1.0" + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ], + "version": "1.0" + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "Groups", + "value" : 18, + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version": "1.0" + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ], + "version": "1.0" + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Int16", + "value" : 22, + "version" : "1.0" + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ], + "version": "1.0" + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ], + "version": "1.0" + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ], + "version": "1.0" + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ], + "version": "1.0" + }, + { + "enumerant" : "Int8", + "value" : 39, + "version" : "1.0" + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "Sampled1D", + "value" : 43, + "version" : "1.0" + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ], + "version": "1.0" + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "SampledBuffer", + "value" : 46, + "version" : "1.0" + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ], + "version": "1.0" + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ], + "version": "1.0" + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ], + "version": "1.0" + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ], + "version" : "1.1" + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ], + "version" : "1.1" + }, + { + "enumerant" : "GroupNonUniform", + "value" : 61, + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformVote", + "value" : 62, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformArithmetic", + "value" : 63, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformBallot", + "value" : 64, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformShuffle", + "value" : 65, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformShuffleRelative", + "value" : 66, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformClustered", + "value" : 67, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformQuad", + "value" : 68, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "ShaderLayer", + "value" : 69, + "version" : "1.5" + }, + { + "enumerant" : "ShaderViewportIndex", + "value" : 70, + "version" : "1.5" + }, + { + "enumerant" : "UniformDecoration", + "value" : 71, + "version" : "1.6" + }, + { + "enumerant" : "CoreBuiltinsARM", + "value" : 4165, + "extensions" : [ "SPV_ARM_core_builtins" ], + "version": "None" + }, + { + "enumerant" : "TileImageColorReadAccessEXT", + "value" : 4166, + "extensions" : [ "SPV_EXT_shader_tile_image" ], + "version" : "None" + }, + { + "enumerant" : "TileImageDepthReadAccessEXT", + "value" : 4167, + "extensions" : [ "SPV_EXT_shader_tile_image" ], + "version" : "None" + }, + { + "enumerant" : "TileImageStencilReadAccessEXT", + "value" : 4168, + "extensions" : [ "SPV_EXT_shader_tile_image" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixLayoutsARM", + "value" : 4201, + "extensions" : [ "SPV_ARM_cooperative_matrix_layouts" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShadingRateKHR", + "value" : 4422, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "WorkgroupMemoryExplicitLayoutKHR", + "value" : 4428, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, + { + "enumerant" : "WorkgroupMemoryExplicitLayout8BitAccessKHR", + "value" : 4429, + "capabilities" : [ "WorkgroupMemoryExplicitLayoutKHR" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, + { + "enumerant" : "WorkgroupMemoryExplicitLayout16BitAccessKHR", + "value" : 4430, + "capabilities" : [ "WorkgroupMemoryExplicitLayoutKHR" ], + "extensions" : [ "SPV_KHR_workgroup_memory_explicit_layout" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ], + "version" : "None" + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "aliases" : ["StorageUniformBufferBlock16"], + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "aliases" : ["StorageUniform16"], + "value" : 4434, + "capabilities" : [ "StorageBuffer16BitAccess" ], + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ], + "version" : "1.3" + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ], + "version" : "1.3" + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ], + "version" : "1.3" + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ], + "version" : "1.3" + }, + { + "enumerant" : "AtomicStorageOps", + "value" : 4445, + "capabilities" : [ "AtomicStorage" ], + "extensions" : [ "SPV_KHR_shader_atomic_counter_ops" ], + "version" : "None" + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ], + "version" : "None" + }, + { + "enumerant" : "StorageBuffer8BitAccess", + "value" : 4448, + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformAndStorageBuffer8BitAccess", + "value" : 4449, + "capabilities" : [ "StorageBuffer8BitAccess" ], + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "StoragePushConstant8", + "value" : 4450, + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "DenormPreserve", + "value" : 4464, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "DenormFlushToZero", + "value" : 4465, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "SignedZeroInfNanPreserve", + "value" : 4466, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTE", + "value" : 4467, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTZ", + "value" : 4468, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RayQueryProvisionalKHR", + "value" : 4471, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryKHR", + "value" : 4472, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "enumerant" : "UntypedPointersKHR", + "value" : 4473, + "extensions" : [ "SPV_KHR_untyped_pointers" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "RayTraversalPrimitiveCullingKHR", + "value" : 4478, + "capabilities" : [ "RayQueryKHR","RayTracingKHR" ], + "extensions" : [ "SPV_KHR_ray_query","SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingKHR", + "value" : 4479, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "TextureSampleWeightedQCOM", + "value" : 4484, + "extensions" : [ "SPV_QCOM_image_processing" ], + "version" : "None" + }, + { + "enumerant" : "TextureBoxFilterQCOM", + "value" : 4485, + "extensions" : [ "SPV_QCOM_image_processing" ], + "version" : "None" + }, + { + "enumerant" : "TextureBlockMatchQCOM", + "value" : 4486, + "extensions" : [ "SPV_QCOM_image_processing" ], + "version" : "None" + }, + { + "enumerant" : "TileShadingQCOM", + "value" : 4495, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_QCOM_tile_shading" ], + "version" : "None" + }, + { + "enumerant" : "TextureBlockMatch2QCOM", + "value" : 4498, + "extensions" : [ "SPV_QCOM_image_processing2" ], + "version" : "None" + }, + { + "enumerant" : "Float16ImageAMD", + "value" : 5008, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_gpu_shader_half_float_fetch" ], + "version" : "None" + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ], + "version" : "None" + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ], + "version" : "None" + }, + { + "enumerant" : "Int64ImageEXT", + "value" : 5016, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_image_int64" ], + "version" : "None" + }, + { + "enumerant" : "ShaderClockKHR", + "value" : 5055, + "extensions" : [ "SPV_KHR_shader_clock" ], + "version" : "None" + }, + { + "enumerant" : "ShaderEnqueueAMDX", + "value" : 5067, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMDX_shader_enqueue" ], + "provisional" : true, + "version" : "None" + }, + { + "enumerant" : "QuadControlKHR", + "value" : 5087, + "extensions" : [ "SPV_KHR_quad_control" ], + "version" : "None" + }, + { + "enumerant" : "BFloat16TypeKHR", + "value" : 5116, + "extensions" : [ "SPV_KHR_bfloat16" ], + "version" : "None" + }, + { + "enumerant" : "BFloat16DotProductKHR", + "value" : 5117, + "capabilities" : [ "BFloat16TypeKHR" ], + "extensions" : [ "SPV_KHR_bfloat16" ], + "version" : "None" + }, + { + "enumerant" : "BFloat16CooperativeMatrixKHR", + "value" : 5118, + "capabilities" : [ "BFloat16TypeKHR", "CooperativeMatrixKHR" ], + "extensions" : [ "SPV_KHR_bfloat16" ], + "version" : "None" + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ], + "version" : "None" + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ], + "version" : "None" + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "aliases" : ["ShaderViewportIndexLayerNV"], + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer", "SPV_NV_viewport_array2" ], + "version" : "None" + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerEXT" ], + "extensions" : [ "SPV_NV_viewport_array2" ], + "version" : "None" + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FragmentFullyCoveredEXT", + "value" : 5265, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_fully_covered" ], + "version" : "None" + }, + { + "enumerant" : "MeshShadingNV", + "value" : 5266, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ImageFootprintNV", + "value" : 5282, + "extensions" : [ "SPV_NV_shader_image_footprint" ], + "version" : "None" + }, + { + "enumerant" : "MeshShadingEXT", + "value" : 5283, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "FragmentBarycentricKHR", + "aliases" : ["FragmentBarycentricNV"], + "value" : 5284, + "extensions" : [ "SPV_NV_fragment_shader_barycentric", "SPV_KHR_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "ComputeDerivativeGroupQuadsKHR", + "aliases" : ["ComputeDerivativeGroupQuadsNV"], + "value" : 5288, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives", "SPV_KHR_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "FragmentDensityEXT", + "aliases" : ["ShadingRateNV"], + "value" : 5291, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "GroupNonUniformPartitionedNV", + "value" : 5297, + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "ShaderNonUniform", + "aliases" : ["ShaderNonUniformEXT"], + "value" : 5301, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RuntimeDescriptorArray", + "aliases" : ["RuntimeDescriptorArrayEXT"], + "value" : 5302, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayDynamicIndexing", + "aliases" : ["InputAttachmentArrayDynamicIndexingEXT"], + "value" : 5303, + "capabilities" : [ "InputAttachment" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayDynamicIndexing", + "aliases" : ["UniformTexelBufferArrayDynamicIndexingEXT"], + "value" : 5304, + "capabilities" : [ "SampledBuffer" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayDynamicIndexing", + "aliases" : ["StorageTexelBufferArrayDynamicIndexingEXT"], + "value" : 5305, + "capabilities" : [ "ImageBuffer" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformBufferArrayNonUniformIndexing", + "aliases" : ["UniformBufferArrayNonUniformIndexingEXT"], + "value" : 5306, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "SampledImageArrayNonUniformIndexing", + "aliases" : ["SampledImageArrayNonUniformIndexingEXT"], + "value" : 5307, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageBufferArrayNonUniformIndexing", + "aliases" : ["StorageBufferArrayNonUniformIndexingEXT"], + "value" : 5308, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageImageArrayNonUniformIndexing", + "aliases" : ["StorageImageArrayNonUniformIndexingEXT"], + "value" : 5309, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayNonUniformIndexing", + "aliases" : ["InputAttachmentArrayNonUniformIndexingEXT"], + "value" : 5310, + "capabilities" : [ "InputAttachment", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayNonUniformIndexing", + "aliases" : ["UniformTexelBufferArrayNonUniformIndexingEXT"], + "value" : 5311, + "capabilities" : [ "SampledBuffer", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayNonUniformIndexing", + "aliases" : ["StorageTexelBufferArrayNonUniformIndexingEXT"], + "value" : 5312, + "capabilities" : [ "ImageBuffer", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RayTracingPositionFetchKHR", + "value" : 5336, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_tracing_position_fetch" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingNV", + "value" : 5340, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingMotionBlurNV", + "value" : 5341, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_ray_tracing_motion_blur" ], + "version" : "None" + }, + { + "enumerant" : "VulkanMemoryModel", + "aliases" : ["VulkanMemoryModelKHR"], + "value" : 5345, + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "VulkanMemoryModelDeviceScope", + "aliases" : ["VulkanMemoryModelDeviceScopeKHR"], + "value" : 5346, + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "PhysicalStorageBufferAddresses", + "aliases" : ["PhysicalStorageBufferAddressesEXT"], + "value" : 5347, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "ComputeDerivativeGroupLinearKHR", + "aliases" : ["ComputeDerivativeGroupLinearNV"], + "value" : 5350, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives", "SPV_KHR_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingProvisionalKHR", + "value" : 5353, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixNV", + "value" : 5357, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderSampleInterlockEXT", + "value" : 5363, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderShadingRateInterlockEXT", + "value" : 5372, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShaderSMBuiltinsNV", + "value" : 5373, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderPixelInterlockEXT", + "value" : 5378, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "DemoteToHelperInvocation", + "aliases" : ["DemoteToHelperInvocationEXT"], + "value" : 5379, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], + "version" : "1.6" + }, + { + "enumerant" : "DisplacementMicromapNV", + "value" : 5380, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_displacement_micromap" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingOpacityMicromapEXT", + "value" : 5381, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_opacity_micromap" ], + "version" : "None" + }, + { + "enumerant" : "ShaderInvocationReorderNV", + "value" : 5383, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_NV_shader_invocation_reorder" ], + "version" : "None" + }, + { + "enumerant" : "BindlessTextureNV", + "value" : 5390, + "extensions" : [ "SPV_NV_bindless_texture" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryPositionFetchKHR", + "value" : 5391, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_tracing_position_fetch" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeVectorNV", + "value" : 5394, + "extensions" : [ "SPV_NV_cooperative_vector" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat16VectorNV", + "value" : 5404, + "extensions" : [ "SPV_NV_shader_atomic_fp16_vector" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingDisplacementMicromapNV", + "value" : 5409, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_NV_displacement_micromap" ], + "version" : "None" + }, + { + "enumerant" : "RawAccessChainsNV", + "value" : 5414, + "extensions" : [ "SPV_NV_raw_access_chains" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingSpheresGeometryNV", + "value" : 5418, + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingLinearSweptSpheresGeometryNV", + "value" : 5419, + "extensions" : [ "SPV_NV_linear_swept_spheres" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixReductionsNV", + "value" : 5430, + "extensions" : [ "SPV_NV_cooperative_matrix2" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixConversionsNV", + "value" : 5431, + "extensions" : [ "SPV_NV_cooperative_matrix2" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixPerElementOperationsNV", + "value" : 5432, + "extensions" : [ "SPV_NV_cooperative_matrix2" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixTensorAddressingNV", + "value" : 5433, + "extensions" : [ "SPV_NV_cooperative_matrix2" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixBlockLoadsNV", + "value" : 5434, + "extensions" : [ "SPV_NV_cooperative_matrix2" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeVectorTrainingNV", + "value" : 5435, + "extensions" : [ "SPV_NV_cooperative_vector" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingClusterAccelerationStructureNV", + "value" : 5437, + "capabilities" : [ "RayTracingKHR" ], + "extensions" : [ "SPV_NV_cluster_acceleration_structure" ], + "version" : "None" + }, + { + "enumerant" : "TensorAddressingNV", + "value" : 5439, + "extensions" : [ "SPV_NV_tensor_addressing" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupImageMediaBlockIOINTEL", + "value" : 5579, + "extensions" : [ "SPV_INTEL_media_block_io" ], + "version" : "None" + }, + { + "enumerant" : "RoundToInfinityINTEL", + "value" : 5582, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, + { + "enumerant" : "FloatingPointModeINTEL", + "value" : 5583, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, + { + "enumerant" : "IntegerFunctions2INTEL", + "value" : 5584, + "extensions" : [ "SPV_INTEL_shader_integer_functions2" ], + "version" : "None" + }, + { + "enumerant" : "FunctionPointersINTEL", + "value" : 5603, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "IndirectReferencesINTEL", + "value" : 5604, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "AsmINTEL", + "value" : 5606, + "extensions" : [ "SPV_INTEL_inline_assembly" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat32MinMaxEXT", + "value" : 5612, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat64MinMaxEXT", + "value" : 5613, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat16MinMaxEXT", + "value" : 5616, + "extensions" : [ "SPV_EXT_shader_atomic_float_min_max" ], + "version" : "None" + }, + { + "enumerant" : "VectorComputeINTEL", + "value" : 5617, + "capabilities" : [ "VectorAnyINTEL" ], + "extensions" : [ "SPV_INTEL_vector_compute" ], + "version" : "None" + }, + { + "enumerant" : "VectorAnyINTEL", + "value" : 5619, + "extensions" : [ "SPV_INTEL_vector_compute" ], + "version" : "None" + }, + { + "enumerant" : "ExpectAssumeKHR", + "value" : 5629, + "extensions" : [ "SPV_KHR_expect_assume" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationINTEL", + "value" : 5696, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationIntraINTEL", + "value" : 5697, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationChromaINTEL", + "value" : 5698, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "VariableLengthArrayINTEL", + "value" : 5817, + "extensions" : [ "SPV_INTEL_variable_length_array" ], + "version" : "None" + }, + { + "enumerant" : "FunctionFloatControlINTEL", + "value" : 5821, + "extensions" : [ "SPV_INTEL_float_controls2" ], + "version" : "None" + }, + { + "enumerant" : "FPGAMemoryAttributesINTEL", + "value" : 5824, + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPFastMathModeINTEL", + "value" : 5837, + "capabilities" : [ "Kernel" ], + "extensions" : [ "SPV_INTEL_fp_fast_math_mode" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionIntegersINTEL", + "value" : 5844, + "extensions" : [ "SPV_INTEL_arbitrary_precision_integers" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionFloatingPointINTEL", + "value" : 5845, + "extensions" : [ "SPV_INTEL_arbitrary_precision_floating_point" ], + "version" : "None" + }, + { + "enumerant" : "UnstructuredLoopControlsINTEL", + "value" : 5886, + "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "FPGALoopControlsINTEL", + "value" : 5888, + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "KernelAttributesINTEL", + "value" : 5892, + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPGAKernelAttributesINTEL", + "value" : 5897, + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPGAMemoryAccessesINTEL", + "value" : 5898, + "extensions" : [ "SPV_INTEL_fpga_memory_accesses" ], + "version" : "None" + }, + { + "enumerant" : "FPGAClusterAttributesINTEL", + "value" : 5904, + "extensions" : [ "SPV_INTEL_fpga_cluster_attributes" ], + "version" : "None" + }, + { + "enumerant" : "LoopFuseINTEL", + "value" : 5906, + "extensions" : [ "SPV_INTEL_loop_fuse" ], + "version" : "None" + }, + { + "enumerant" : "FPGADSPControlINTEL", + "value" : 5908, + "extensions" : [ "SPV_INTEL_fpga_dsp_control" ], + "version" : "None" + }, + { + "enumerant" : "MemoryAccessAliasingINTEL", + "value" : 5910, + "extensions" : [ "SPV_INTEL_memory_access_aliasing" ], + "version" : "None" + }, + { + "enumerant" : "FPGAInvocationPipeliningAttributesINTEL", + "value" : 5916, + "extensions" : [ "SPV_INTEL_fpga_invocation_pipelining_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPGABufferLocationINTEL", + "value" : 5920, + "extensions" : [ "SPV_INTEL_fpga_buffer_location" ], + "version" : "None" + }, + { + "enumerant" : "ArbitraryPrecisionFixedPointINTEL", + "value" : 5922, + "extensions" : [ "SPV_INTEL_arbitrary_precision_fixed_point" ], + "version" : "None" + }, + { + "enumerant" : "USMStorageClassesINTEL", + "value" : 5935, + "extensions" : [ "SPV_INTEL_usm_storage_classes" ], + "version" : "None" + }, + { + "enumerant" : "RuntimeAlignedAttributeINTEL", + "value" : 5939, + "extensions" : [ "SPV_INTEL_runtime_aligned" ], + "version" : "None" + }, + { + "enumerant" : "IOPipesINTEL", + "value" : 5943, + "extensions" : [ "SPV_INTEL_io_pipes" ], + "version" : "None" + }, + { + "enumerant" : "BlockingPipesINTEL", + "value" : 5945, + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "enumerant" : "FPGARegINTEL", + "value" : 5948, + "extensions" : [ "SPV_INTEL_fpga_reg" ], + "version" : "None" + }, + { + "enumerant" : "DotProductInputAll", + "aliases" : ["DotProductInputAllKHR"], + "value" : 6016, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "enumerant" : "DotProductInput4x8Bit", + "aliases" : ["DotProductInput4x8BitKHR"], + "value" : 6017, + "capabilities" : [ "Int8" ], + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "enumerant" : "DotProductInput4x8BitPacked", + "aliases" : ["DotProductInput4x8BitPackedKHR"], + "value" : 6018, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "enumerant" : "DotProduct", + "aliases" : ["DotProductKHR"], + "value" : 6019, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + }, + { + "enumerant" : "RayCullMaskKHR", + "value" : 6020, + "extensions" : [ "SPV_KHR_ray_cull_mask" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixKHR", + "value" : 6022, + "extensions" : [ "SPV_KHR_cooperative_matrix" ], + "version" : "None" + }, + { + "enumerant" : "ReplicatedCompositesEXT", + "value" : 6024, + "extensions" : [ "SPV_EXT_replicated_composites" ], + "version" : "None" + }, + { + "enumerant" : "BitInstructions", + "value" : 6025, + "extensions" : [ "SPV_KHR_bit_instructions" ], + "version" : "None" + }, + { + "enumerant" : "GroupNonUniformRotateKHR", + "value" : 6026, + "capabilities" : [ "GroupNonUniform" ], + "extensions" : [ "SPV_KHR_subgroup_rotate" ], + "version" : "None" + }, + { + "enumerant" : "FloatControls2", + "value" : 6029, + "extensions" : [ "SPV_KHR_float_controls2" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat32AddEXT", + "value" : 6033, + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat64AddEXT", + "value" : 6034, + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + }, + { + "enumerant" : "LongCompositesINTEL", + "value" : 6089, + "extensions" : [ "SPV_INTEL_long_composites" ], + "version" : "None" + }, + { + "enumerant" : "OptNoneEXT", + "aliases" : ["OptNoneINTEL"], + "value" : 6094, + "extensions" : [ "SPV_EXT_optnone", "SPV_INTEL_optnone" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat16AddEXT", + "value" : 6095, + "extensions" : [ "SPV_EXT_shader_atomic_float16_add" ], + "version" : "None" + }, + { + "enumerant" : "DebugInfoModuleINTEL", + "value" : 6114, + "extensions" : [ "SPV_INTEL_debug_module" ], + "version" : "None" + }, + { + "enumerant" : "BFloat16ConversionINTEL", + "value" : 6115, + "extensions" : [ "SPV_INTEL_bfloat16_conversion" ], + "version" : "None" + }, + { + "enumerant" : "SplitBarrierINTEL", + "value" : 6141, + "extensions" : [ "SPV_INTEL_split_barrier" ], + "version" : "None" + }, + { + "enumerant" : "ArithmeticFenceEXT", + "value" : 6144, + "extensions" : [ "SPV_EXT_arithmetic_fence" ], + "version" : "None" + }, + { + "enumerant" : "FPGAClusterAttributesV2INTEL", + "value" : 6150, + "capabilities" : [ "FPGAClusterAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_cluster_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPGAKernelAttributesv2INTEL", + "value" : 6161, + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "TaskSequenceINTEL", + "value" : 6162, + "extensions" : [ "SPV_INTEL_task_sequence" ], + "version" : "None" + }, + { + "enumerant" : "FPMaxErrorINTEL", + "value" : 6169, + "extensions" : [ "SPV_INTEL_fp_max_error" ], + "version" : "None" + }, + { + "enumerant" : "FPGALatencyControlINTEL", + "value" : 6171, + "extensions" : [ "SPV_INTEL_fpga_latency_control" ], + "version" : "None" + }, + { + "enumerant" : "FPGAArgumentInterfacesINTEL", + "value" : 6174, + "extensions" : [ "SPV_INTEL_fpga_argument_interfaces" ], + "version" : "None" + }, + { + "enumerant" : "GlobalVariableHostAccessINTEL", + "value" : 6187, + "extensions": [ "SPV_INTEL_global_variable_host_access" ], + "version" : "None" + }, + { + "enumerant" : "GlobalVariableFPGADecorationsINTEL", + "value" : 6189, + "extensions": [ "SPV_INTEL_global_variable_fpga_decorations" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupBufferPrefetchINTEL", + "value" : 6220, + "extensions": [ "SPV_INTEL_subgroup_buffer_prefetch" ], + "version" : "None" + }, + { + "enumerant" : "Subgroup2DBlockIOINTEL", + "value" : 6228, + "extensions": [ "SPV_INTEL_2d_block_io" ], + "version" : "None" + }, + { + "enumerant" : "Subgroup2DBlockTransformINTEL", + "value" : 6229, + "capabilities" : [ "Subgroup2DBlockIOINTEL" ], + "extensions": [ "SPV_INTEL_2d_block_io" ], + "version" : "None" + }, + { + "enumerant" : "Subgroup2DBlockTransposeINTEL", + "value" : 6230, + "capabilities" : [ "Subgroup2DBlockIOINTEL" ], + "extensions": [ "SPV_INTEL_2d_block_io" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupMatrixMultiplyAccumulateINTEL", + "value" : 6236, + "extensions": [ "SPV_INTEL_subgroup_matrix_multiply_accumulate" ], + "version" : "None" + }, + { + "enumerant" : "TernaryBitwiseFunctionINTEL", + "value" : 6241, + "extensions" : [ "SPV_INTEL_ternary_bitwise_function"], + "version" : "None" + }, + { + "enumerant" : "GroupUniformArithmeticKHR", + "value" : 6400, + "extensions" : [ "SPV_KHR_uniform_group_instructions"], + "version" : "None" + }, + { + "enumerant" : "TensorFloat32RoundingINTEL", + "value" : 6425, + "extensions" : [ "SPV_INTEL_tensor_float32_conversion" ], + "version" : "None" + }, + { + "enumerant" : "MaskedGatherScatterINTEL", + "value" : 6427, + "extensions" : [ "SPV_INTEL_masked_gather_scatter"], + "version" : "None" + }, + { + "enumerant" : "CacheControlsINTEL", + "value" : 6441, + "extensions" : [ "SPV_INTEL_cache_controls" ], + "version" : "None" + }, + { + "enumerant" : "RegisterLimitsINTEL", + "value" : 6460, + "extensions" : [ "SPV_INTEL_maximum_registers" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryIntersection", + "enumerants" : [ + { + "enumerant" : "RayQueryCandidateIntersectionKHR", + "value" : 0, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionKHR", + "value" : 1, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryCommittedIntersectionType", + "enumerants" : [ + { + "enumerant" : "RayQueryCommittedIntersectionNoneKHR", + "value" : 0, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionTriangleKHR", + "value" : 1, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionGeneratedKHR", + "value" : 2, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryCandidateIntersectionType", + "enumerants" : [ + { + "enumerant" : "RayQueryCandidateIntersectionTriangleKHR", + "value" : 0, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCandidateIntersectionAABBKHR", + "value" : 1, + "capabilities" : [ "RayQueryKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "PackedVectorFormat", + "enumerants" : [ + { + "enumerant" : "PackedVectorFormat4x8Bit", + "aliases" : ["PackedVectorFormat4x8BitKHR"], + "value" : 0, + "extensions" : [ "SPV_KHR_integer_dot_product" ], + "version" : "1.6" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "CooperativeMatrixOperands", + "enumerants" : [ + { + "enumerant" : "NoneKHR", + "value" : "0x0000", + "version" : "None" + }, + { + "enumerant" : "MatrixASignedComponentsKHR", + "value" : "0x0001", + "version" : "None" + }, + { + "enumerant" : "MatrixBSignedComponentsKHR", + "value" : "0x0002", + "version" : "None" + }, + { + "enumerant" : "MatrixCSignedComponentsKHR", + "value" : "0x0004", + "version" : "None" + }, + { + "enumerant" : "MatrixResultSignedComponentsKHR", + "value" : "0x0008", + "version" : "None" + }, + { + "enumerant" : "SaturatingAccumulationKHR", + "value" : "0x0010", + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "CooperativeMatrixLayout", + "enumerants" : [ + { + "enumerant" : "RowMajorKHR", + "value" : 0, + "version" : "None" + }, + { + "enumerant" : "ColumnMajorKHR", + "value" : 1, + "version" : "None" + }, + { + "enumerant" : "RowBlockedInterleavedARM", + "value" : 4202, + "version" : "None" + }, + { + "enumerant" : "ColumnBlockedInterleavedARM", + "value" : 4203, + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "CooperativeMatrixUse", + "enumerants" : [ + { + "enumerant" : "MatrixAKHR", + "value" : 0, + "version" : "None" + }, + { + "enumerant" : "MatrixBKHR", + "value" : 1, + "version" : "None" + }, + { + "enumerant" : "MatrixAccumulatorKHR", + "value" : 2, + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "CooperativeMatrixReduce", + "enumerants" : [ + { + "enumerant" : "Row", + "value" : "0x0001", + "version" : "None" + }, + { + "enumerant" : "Column", + "value" : "0x0002", + "version" : "None" + }, + { + "enumerant" : "2x2", + "value" : "0x0004", + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "TensorClampMode", + "enumerants" : [ + { + "enumerant" : "Undefined", + "value" : 0, + "version": "None" + }, + { + "enumerant" : "Constant", + "value" : 1, + "version": "None" + }, + { + "enumerant" : "ClampToEdge", + "value" : 2, + "version": "None" + }, + { + "enumerant" : "Repeat", + "value" : 3, + "version": "None" + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "version": "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "TensorAddressingOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000", + "version" : "None" + }, + { + "enumerant" : "TensorView", + "value" : "0x0001", + "parameters" : [ + { "kind" : "IdRef" } + ], + "capabilities" : [ "CooperativeMatrixTensorAddressingNV" ], + "version" : "None" + }, + { + "enumerant" : "DecodeFunc", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ], + "capabilities" : [ "CooperativeMatrixBlockLoadsNV" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "InitializationModeQualifier", + "enumerants" : [ + { + "enumerant" : "InitOnDeviceReprogramINTEL", + "value" : 0, + "capabilities" : [ "GlobalVariableFPGADecorationsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "InitOnDeviceResetINTEL", + "value" : 1, + "capabilities" : [ "GlobalVariableFPGADecorationsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LoadCacheControl", + "enumerants" : [ + { + "enumerant" : "UncachedINTEL", + "value" : 0, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "CachedINTEL", + "value" : 1, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StreamingINTEL", + "value" : 2, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "InvalidateAfterReadINTEL", + "value" : 3, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "ConstCachedINTEL", + "value" : 4, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StoreCacheControl", + "enumerants" : [ + { + "enumerant" : "UncachedINTEL", + "value" : 0, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "WriteThroughINTEL", + "value" : 1, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "WriteBackINTEL", + "value" : 2, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + }, + { + "enumerant" : "StreamingINTEL", + "value" : 3, + "capabilities" : [ "CacheControlsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "NamedMaximumNumberOfRegisters", + "enumerants" : [ + { + "enumerant" : "AutoINTEL", + "value" : 0, + "capabilities" : [ "RegisterLimitsINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MatrixMultiplyAccumulateOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0", + "version" : "None" + }, + { + "enumerant" : "MatrixASignedComponentsINTEL", + "value" : "0x1", + "version" : "None" + }, + { + "enumerant" : "MatrixBSignedComponentsINTEL", + "value" : "0x2", + "version" : "None" + }, + { + "enumerant" : "MatrixCBFloat16INTEL", + "value" : "0x4", + "version" : "None" + }, + { + "enumerant" : "MatrixResultBFloat16INTEL", + "value" : "0x8", + "version" : "None" + }, + { + "enumerant" : "MatrixAPackedInt8INTEL", + "value" : "0x10", + "version" : "None" + }, + { + "enumerant" : "MatrixBPackedInt8INTEL", + "value" : "0x20", + "version" : "None" + }, + { + "enumerant" : "MatrixAPackedInt4INTEL", + "value" : "0x40", + "version" : "None" + }, + { + "enumerant" : "MatrixBPackedInt4INTEL", + "value" : "0x80", + "version" : "None" + }, + { + "enumerant" : "MatrixATF32INTEL", + "value" : "0x100", + "version" : "None" + }, + { + "enumerant" : "MatrixBTF32INTEL", + "value" : "0x200", + "version" : "None" + }, + { + "enumerant" : "MatrixAPackedFloat16INTEL", + "value" : "0x400", + "version" : "None" + }, + { + "enumerant" : "MatrixBPackedFloat16INTEL", + "value" : "0x800", + "version" : "None" + }, + { + "enumerant" : "MatrixAPackedBFloat16INTEL", + "value" : "0x1000", + "version" : "None" + }, + { + "enumerant" : "MatrixBPackedBFloat16INTEL", + "value" : "0x2000", + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPEncoding", + "enumerants" : [ + { + "enumerant" : "BFloat16KHR", + "value" : 0, + "capabilities" : [ "BFloat16TypeKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "CooperativeVectorMatrixLayout", + "enumerants" : [ + { + "enumerant" : "RowMajorNV", + "value" : 0, + "version" : "None" + }, + { + "enumerant" : "ColumnMajorNV", + "value" : 1, + "version" : "None" + }, + { + "enumerant" : "InferencingOptimalNV", + "value" : 2, + "version" : "None" + }, + { + "enumerant" : "TrainingOptimalNV", + "value" : 3, + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ComponentType", + "enumerants" : [ + { + "enumerant" : "Float16NV", + "value" : 0, + "version" : "None" + }, + { + "enumerant" : "Float32NV", + "value" : 1, + "version" : "None" + }, + { + "enumerant" : "Float64NV", + "value" : 2, + "version" : "None" + }, + { + "enumerant" : "SignedInt8NV", + "value" : 3, + "version" : "None" + }, + { + "enumerant" : "SignedInt16NV", + "value" : 4, + "version" : "None" + }, + { + "enumerant" : "SignedInt32NV", + "value" : 5, + "version" : "None" + }, + { + "enumerant" : "SignedInt64NV", + "value" : 6, + "version" : "None" + }, + { + "enumerant" : "UnsignedInt8NV", + "value" : 7, + "version" : "None" + }, + { + "enumerant" : "UnsignedInt16NV", + "value" : 8, + "version" : "None" + }, + { + "enumerant" : "UnsignedInt32NV", + "value" : 9, + "version" : "None" + }, + { + "enumerant" : "UnsignedInt64NV", + "value" : 10, + "version" : "None" + }, + { + "enumerant" : "SignedInt8PackedNV", + "value" : 1000491000, + "version" : "None" + }, + { + "enumerant" : "UnsignedInt8PackedNV", + "value" : 1000491001, + "version" : "None" + }, + { + "enumerant" : "FloatE4M3NV", + "value" : 1000491002, + "version" : "None" + }, + { + "enumerant" : "FloatE5M2NV", + "value" : 1000491003, + "version" : "None" + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralFloat", + "doc" : "A float consuming one word" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs new file mode 100644 index 00000000..ee0d1941 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs @@ -0,0 +1,2462 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010600; + public const uint Revision = 1; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + CPP_for_OpenCL = 6, + SYCL = 7, + HERO_C = 8, + NZSL = 9, + WGSL = 10, + Slang = 11, + Zig = 12, + Rust = 13, + Max = 0x7fffffff, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + TaskEXT = 5364, + MeshEXT = 5365, + Max = 0x7fffffff, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + NonCoherentColorAttachmentReadEXT = 4169, + NonCoherentDepthAttachmentReadEXT = 4170, + NonCoherentStencilAttachmentReadEXT = 4171, + SubgroupUniformControlFlowKHR = 4421, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + NonCoherentTileAttachmentReadQCOM = 4489, + TileShadingRateQCOM = 4490, + EarlyAndLateFragmentTestsAMD = 5017, + StencilRefReplacingEXT = 5027, + CoalescingAMDX = 5069, + IsApiEntryAMDX = 5070, + MaxNodeRecursionAMDX = 5071, + StaticNumWorkgroupsAMDX = 5072, + ShaderIndexAMDX = 5073, + MaxNumWorkgroupsAMDX = 5077, + StencilRefUnchangedFrontAMD = 5079, + StencilRefGreaterFrontAMD = 5080, + StencilRefLessFrontAMD = 5081, + StencilRefUnchangedBackAMD = 5082, + StencilRefGreaterBackAMD = 5083, + StencilRefLessBackAMD = 5084, + QuadDerivativesKHR = 5088, + RequireFullQuadsKHR = 5089, + SharesInputWithAMDX = 5102, + OutputLinesEXT = 5269, + OutputLinesNV = 5269, + OutputPrimitivesEXT = 5270, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsKHR = 5289, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearKHR = 5290, + DerivativeGroupLinearNV = 5290, + OutputTrianglesEXT = 5298, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, + MaximallyReconvergesKHR = 6023, + FPFastMathDefault = 6028, + StreamingInterfaceINTEL = 6154, + RegisterMapInterfaceINTEL = 6160, + NamedBarrierCountINTEL = 6417, + MaximumRegistersINTEL = 6461, + MaximumRegistersIdINTEL = 6462, + NamedMaximumRegistersINTEL = 6463, + Max = 0x7fffffff, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + TileImageEXT = 4172, + TileAttachmentQCOM = 4491, + NodePayloadAMDX = 5068, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + HitObjectAttributeNV = 5385, + TaskPayloadWorkgroupEXT = 5402, + CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, + Max = 0x7fffffff, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + TileImageDataEXT = 4173, + Max = 0x7fffffff, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + UnormInt10X6EXT = 17, + UnsignedIntRaw10EXT = 19, + UnsignedIntRaw12EXT = 20, + UnormInt2_101010EXT = 21, + UnsignedInt10X6EXT = 22, + UnsignedInt12X4EXT = 23, + UnsignedInt14X2EXT = 24, + UnormInt12X4EXT = 25, + UnormInt14X2EXT = 26, + Max = 0x7fffffff, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Nontemporal = 14, + Offsets = 16, + Max = 0x7fffffff, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + Nontemporal = 0x00004000, + Offsets = 0x00010000, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + AllowContract = 16, + AllowContractFastINTEL = 16, + AllowReassoc = 17, + AllowReassocINTEL = 17, + AllowTransform = 18, + Max = 0x7fffffff, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + AllowContract = 0x00010000, + AllowContractFastINTEL = 0x00010000, + AllowReassoc = 0x00020000, + AllowReassocINTEL = 0x00020000, + AllowTransform = 0x00040000, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + LinkOnceODR = 2, + Max = 0x7fffffff, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + RuntimeAlignedINTEL = 5940, + Max = 0x7fffffff, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + WeightTextureQCOM = 4487, + BlockMatchTextureQCOM = 4488, + BlockMatchSamplerQCOM = 4499, + ExplicitInterpAMD = 4999, + NodeSharesPayloadLimitsWithAMDX = 5019, + NodeMaxPayloadsAMDX = 5020, + TrackFinishWritingAMDX = 5078, + PayloadNodeNameAMDX = 5091, + PayloadNodeBaseIndexAMDX = 5098, + PayloadNodeSparseArrayAMDX = 5099, + PayloadNodeArraySizeAMDX = 5100, + PayloadDispatchIndirectAMDX = 5105, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveEXT = 5271, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexKHR = 5285, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + HitObjectShaderRecordBufferNV = 5386, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, + SIMTCallINTEL = 5599, + ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + StridesizeINTEL = 5883, + WordsizeINTEL = 5884, + TrueDualPortINTEL = 5885, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + MathOpDSPModeINTEL = 5909, + AliasScopeINTEL = 5914, + NoAliasINTEL = 5915, + InitiationIntervalINTEL = 5917, + MaxConcurrencyINTEL = 5918, + PipelineEnableINTEL = 5919, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, + MediaBlockIOINTEL = 6140, + StallFreeINTEL = 6151, + FPMaxErrorDecorationINTEL = 6170, + LatencyControlLabelINTEL = 6172, + LatencyControlConstraintINTEL = 6173, + ConduitKernelArgumentINTEL = 6175, + RegisterMapKernelArgumentINTEL = 6176, + MMHostInterfaceAddressWidthINTEL = 6177, + MMHostInterfaceDataWidthINTEL = 6178, + MMHostInterfaceLatencyINTEL = 6179, + MMHostInterfaceReadWriteModeINTEL = 6180, + MMHostInterfaceMaxBurstINTEL = 6181, + MMHostInterfaceWaitRequestINTEL = 6182, + StableKernelArgumentINTEL = 6183, + HostAccessINTEL = 6188, + InitModeINTEL = 6190, + ImplementInRegisterMapINTEL = 6191, + CacheControlLoadINTEL = 6442, + CacheControlStoreINTEL = 6443, + Max = 0x7fffffff, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + CoreIDARM = 4160, + CoreCountARM = 4161, + CoreMaxIDARM = 4162, + WarpIDARM = 4163, + WarpMaxIDARM = 4164, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + TileOffsetQCOM = 4492, + TileDimensionQCOM = 4493, + TileApronSizeQCOM = 4494, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + RemainingRecursionLevelsAMDX = 5021, + ShaderIndexAMDX = 5073, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordKHR = 5286, + BaryCoordNV = 5286, + BaryCoordNoPerspKHR = 5287, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + PrimitivePointIndicesEXT = 5294, + PrimitiveLineIndicesEXT = 5295, + PrimitiveTriangleIndicesEXT = 5296, + CullPrimitiveEXT = 5299, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + CurrentRayTimeNV = 5334, + HitTriangleVertexPositionsKHR = 5335, + HitMicroTriangleVertexPositionsNV = 5337, + HitMicroTriangleVertexBarycentricsNV = 5344, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + HitIsSphereNV = 5359, + HitIsLSSNV = 5360, + HitSpherePositionNV = 5361, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + HitLSSPositionsNV = 5396, + HitKindFrontFacingMicroTriangleNV = 5405, + HitKindBackFacingMicroTriangleNV = 5406, + HitSphereRadiusNV = 5420, + HitLSSRadiiNV = 5421, + ClusterIDNV = 5436, + CullMaskKHR = 6021, + Max = 0x7fffffff, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + NoFusionINTEL = 23, + LoopCountINTEL = 24, + MaxReinvocationDelayINTEL = 25, + Max = 0x7fffffff, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + NoFusionINTEL = 0x00800000, + LoopCountINTEL = 0x01000000, + MaxReinvocationDelayINTEL = 0x02000000, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + OptNoneEXT = 16, + OptNoneINTEL = 16, + Max = 0x7fffffff, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + OptNoneEXT = 0x00010000, + OptNoneINTEL = 0x00010000, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + AliasScopeINTELMask = 16, + NoAliasINTELMask = 17, + Max = 0x7fffffff, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + AliasScopeINTELMask = 0x00010000, + NoAliasINTELMask = 0x00020000, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + Max = 0x7fffffff, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + UniformDecoration = 71, + CoreBuiltinsARM = 4165, + TileImageColorReadAccessEXT = 4166, + TileImageDepthReadAccessEXT = 4167, + TileImageStencilReadAccessEXT = 4168, + CooperativeMatrixLayoutsARM = 4201, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + UntypedPointersKHR = 4473, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + TextureSampleWeightedQCOM = 4484, + TextureBoxFilterQCOM = 4485, + TextureBlockMatchQCOM = 4486, + TileShadingQCOM = 4495, + TextureBlockMatch2QCOM = 4498, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + ShaderEnqueueAMDX = 5067, + QuadControlKHR = 5087, + BFloat16TypeKHR = 5116, + BFloat16DotProductKHR = 5117, + BFloat16CooperativeMatrixKHR = 5118, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + MeshShadingEXT = 5283, + FragmentBarycentricKHR = 5284, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsKHR = 5288, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingPositionFetchKHR = 5336, + RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearKHR = 5350, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocation = 5379, + DemoteToHelperInvocationEXT = 5379, + DisplacementMicromapNV = 5380, + RayTracingOpacityMicromapEXT = 5381, + ShaderInvocationReorderNV = 5383, + BindlessTextureNV = 5390, + RayQueryPositionFetchKHR = 5391, + CooperativeVectorNV = 5394, + AtomicFloat16VectorNV = 5404, + RayTracingDisplacementMicromapNV = 5409, + RawAccessChainsNV = 5414, + RayTracingSpheresGeometryNV = 5418, + RayTracingLinearSweptSpheresGeometryNV = 5419, + CooperativeMatrixReductionsNV = 5430, + CooperativeMatrixConversionsNV = 5431, + CooperativeMatrixPerElementOperationsNV = 5432, + CooperativeMatrixTensorAddressingNV = 5433, + CooperativeMatrixBlockLoadsNV = 5434, + CooperativeVectorTrainingNV = 5435, + RayTracingClusterAccelerationStructureNV = 5437, + TensorAddressingNV = 5439, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, + FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, + ArbitraryPrecisionFloatingPointINTEL = 5845, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGADSPControlINTEL = 5908, + MemoryAccessAliasingINTEL = 5910, + FPGAInvocationPipeliningAttributesINTEL = 5916, + FPGABufferLocationINTEL = 5920, + ArbitraryPrecisionFixedPointINTEL = 5922, + USMStorageClassesINTEL = 5935, + RuntimeAlignedAttributeINTEL = 5939, + IOPipesINTEL = 5943, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + DotProductInputAll = 6016, + DotProductInputAllKHR = 6016, + DotProductInput4x8Bit = 6017, + DotProductInput4x8BitKHR = 6017, + DotProductInput4x8BitPacked = 6018, + DotProductInput4x8BitPackedKHR = 6018, + DotProduct = 6019, + DotProductKHR = 6019, + RayCullMaskKHR = 6020, + CooperativeMatrixKHR = 6022, + ReplicatedCompositesEXT = 6024, + BitInstructions = 6025, + GroupNonUniformRotateKHR = 6026, + FloatControls2 = 6029, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + LongCompositesINTEL = 6089, + OptNoneEXT = 6094, + OptNoneINTEL = 6094, + AtomicFloat16AddEXT = 6095, + DebugInfoModuleINTEL = 6114, + BFloat16ConversionINTEL = 6115, + SplitBarrierINTEL = 6141, + ArithmeticFenceEXT = 6144, + FPGAClusterAttributesV2INTEL = 6150, + FPGAKernelAttributesv2INTEL = 6161, + TaskSequenceINTEL = 6162, + FPMaxErrorINTEL = 6169, + FPGALatencyControlINTEL = 6171, + FPGAArgumentInterfacesINTEL = 6174, + GlobalVariableHostAccessINTEL = 6187, + GlobalVariableFPGADecorationsINTEL = 6189, + SubgroupBufferPrefetchINTEL = 6220, + Subgroup2DBlockIOINTEL = 6228, + Subgroup2DBlockTransformINTEL = 6229, + Subgroup2DBlockTransposeINTEL = 6230, + SubgroupMatrixMultiplyAccumulateINTEL = 6236, + TernaryBitwiseFunctionINTEL = 6241, + GroupUniformArithmeticKHR = 6400, + TensorFloat32RoundingINTEL = 6425, + MaskedGatherScatterINTEL = 6427, + CacheControlsINTEL = 6441, + RegisterLimitsINTEL = 6460, + Max = 0x7fffffff, + } + + public enum RayFlagsShift + { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipBuiltinPrimitivesNV = 8, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + ForceOpacityMicromap2StateEXT = 10, + Max = 0x7fffffff, + } + + public enum RayFlagsMask + { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipBuiltinPrimitivesNV = 0x00000100, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + ForceOpacityMicromap2StateEXT = 0x00000400, + } + + public enum RayQueryIntersection + { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, + } + + public enum RayQueryCommittedIntersectionType + { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, + } + + public enum RayQueryCandidateIntersectionType + { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, + } + + public enum FragmentShadingRateShift + { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, + } + + public enum FragmentShadingRateMask + { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, + } + + public enum FPDenormMode + { + Preserve = 0, + FlushToZero = 1, + Max = 0x7fffffff, + } + + public enum FPOperationMode + { + IEEE = 0, + ALT = 1, + Max = 0x7fffffff, + } + + public enum QuantizationModes + { + TRN = 0, + TRN_ZERO = 1, + RND = 2, + RND_ZERO = 3, + RND_INF = 4, + RND_MIN_INF = 5, + RND_CONV = 6, + RND_CONV_ODD = 7, + Max = 0x7fffffff, + } + + public enum OverflowModes + { + WRAP = 0, + SAT = 1, + SAT_ZERO = 2, + SAT_SYM = 3, + Max = 0x7fffffff, + } + + public enum PackedVectorFormat + { + PackedVectorFormat4x8Bit = 0, + PackedVectorFormat4x8BitKHR = 0, + Max = 0x7fffffff, + } + + public enum CooperativeMatrixOperandsShift + { + MatrixASignedComponentsKHR = 0, + MatrixBSignedComponentsKHR = 1, + MatrixCSignedComponentsKHR = 2, + MatrixResultSignedComponentsKHR = 3, + SaturatingAccumulationKHR = 4, + Max = 0x7fffffff, + } + + public enum CooperativeMatrixOperandsMask + { + MaskNone = 0, + MatrixASignedComponentsKHR = 0x00000001, + MatrixBSignedComponentsKHR = 0x00000002, + MatrixCSignedComponentsKHR = 0x00000004, + MatrixResultSignedComponentsKHR = 0x00000008, + SaturatingAccumulationKHR = 0x00000010, + } + + public enum CooperativeMatrixLayout + { + RowMajorKHR = 0, + ColumnMajorKHR = 1, + RowBlockedInterleavedARM = 4202, + ColumnBlockedInterleavedARM = 4203, + Max = 0x7fffffff, + } + + public enum CooperativeMatrixUse + { + MatrixAKHR = 0, + MatrixBKHR = 1, + MatrixAccumulatorKHR = 2, + Max = 0x7fffffff, + } + + public enum CooperativeMatrixReduceShift + { + Row = 0, + Column = 1, + CooperativeMatrixReduce2x2 = 2, + Max = 0x7fffffff, + } + + public enum CooperativeMatrixReduceMask + { + MaskNone = 0, + Row = 0x00000001, + Column = 0x00000002, + CooperativeMatrixReduce2x2 = 0x00000004, + } + + public enum TensorClampMode + { + Undefined = 0, + Constant = 1, + ClampToEdge = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + public enum TensorAddressingOperandsShift + { + TensorView = 0, + DecodeFunc = 1, + Max = 0x7fffffff, + } + + public enum TensorAddressingOperandsMask + { + MaskNone = 0, + TensorView = 0x00000001, + DecodeFunc = 0x00000002, + } + + public enum InitializationModeQualifier + { + InitOnDeviceReprogramINTEL = 0, + InitOnDeviceResetINTEL = 1, + Max = 0x7fffffff, + } + + public enum HostAccessQualifier + { + NoneINTEL = 0, + ReadINTEL = 1, + WriteINTEL = 2, + ReadWriteINTEL = 3, + Max = 0x7fffffff, + } + + public enum LoadCacheControl + { + UncachedINTEL = 0, + CachedINTEL = 1, + StreamingINTEL = 2, + InvalidateAfterReadINTEL = 3, + ConstCachedINTEL = 4, + Max = 0x7fffffff, + } + + public enum StoreCacheControl + { + UncachedINTEL = 0, + WriteThroughINTEL = 1, + WriteBackINTEL = 2, + StreamingINTEL = 3, + Max = 0x7fffffff, + } + + public enum NamedMaximumNumberOfRegisters + { + AutoINTEL = 0, + Max = 0x7fffffff, + } + + public enum MatrixMultiplyAccumulateOperandsShift + { + MatrixASignedComponentsINTEL = 0, + MatrixBSignedComponentsINTEL = 1, + MatrixCBFloat16INTEL = 2, + MatrixResultBFloat16INTEL = 3, + MatrixAPackedInt8INTEL = 4, + MatrixBPackedInt8INTEL = 5, + MatrixAPackedInt4INTEL = 6, + MatrixBPackedInt4INTEL = 7, + MatrixATF32INTEL = 8, + MatrixBTF32INTEL = 9, + MatrixAPackedFloat16INTEL = 10, + MatrixBPackedFloat16INTEL = 11, + MatrixAPackedBFloat16INTEL = 12, + MatrixBPackedBFloat16INTEL = 13, + Max = 0x7fffffff, + } + + public enum MatrixMultiplyAccumulateOperandsMask + { + MaskNone = 0, + MatrixASignedComponentsINTEL = 0x00000001, + MatrixBSignedComponentsINTEL = 0x00000002, + MatrixCBFloat16INTEL = 0x00000004, + MatrixResultBFloat16INTEL = 0x00000008, + MatrixAPackedInt8INTEL = 0x00000010, + MatrixBPackedInt8INTEL = 0x00000020, + MatrixAPackedInt4INTEL = 0x00000040, + MatrixBPackedInt4INTEL = 0x00000080, + MatrixATF32INTEL = 0x00000100, + MatrixBTF32INTEL = 0x00000200, + MatrixAPackedFloat16INTEL = 0x00000400, + MatrixBPackedFloat16INTEL = 0x00000800, + MatrixAPackedBFloat16INTEL = 0x00001000, + MatrixBPackedBFloat16INTEL = 0x00002000, + } + + public enum RawAccessChainOperandsShift + { + RobustnessPerComponentNV = 0, + RobustnessPerElementNV = 1, + Max = 0x7fffffff, + } + + public enum RawAccessChainOperandsMask + { + MaskNone = 0, + RobustnessPerComponentNV = 0x00000001, + RobustnessPerElementNV = 0x00000002, + } + + public enum FPEncoding + { + BFloat16KHR = 0, + Max = 0x7fffffff, + } + + public enum CooperativeVectorMatrixLayout + { + RowMajorNV = 0, + ColumnMajorNV = 1, + InferencingOptimalNV = 2, + TrainingOptimalNV = 3, + Max = 0x7fffffff, + } + + public enum ComponentType + { + Float16NV = 0, + Float32NV = 1, + Float64NV = 2, + SignedInt8NV = 3, + SignedInt16NV = 4, + SignedInt32NV = 5, + SignedInt64NV = 6, + UnsignedInt8NV = 7, + UnsignedInt16NV = 8, + UnsignedInt32NV = 9, + UnsignedInt64NV = 10, + SignedInt8PackedNV = 1000491000, + UnsignedInt8PackedNV = 1000491001, + FloatE4M3NV = 1000491002, + FloatE5M2NV = 1000491003, + Max = 0x7fffffff, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + Max = 0x7fffffff, + } + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h new file mode 100644 index 00000000..d8df1b33 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h @@ -0,0 +1,5154 @@ +/* +** Copyright (c) 2014-2024 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D, Beef +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** - Beef will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010600; +static const unsigned int SpvRevision = 1; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageCPP_for_OpenCL = 6, + SpvSourceLanguageSYCL = 7, + SpvSourceLanguageHERO_C = 8, + SpvSourceLanguageNZSL = 9, + SpvSourceLanguageWGSL = 10, + SpvSourceLanguageSlang = 11, + SpvSourceLanguageZig = 12, + SpvSourceLanguageRust = 13, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelTaskEXT = 5364, + SpvExecutionModelMeshEXT = 5365, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + SpvExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + SpvExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + SpvExecutionModeSubgroupUniformControlFlowKHR = 4421, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeNonCoherentTileAttachmentReadQCOM = 4489, + SpvExecutionModeTileShadingRateQCOM = 4490, + SpvExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeCoalescingAMDX = 5069, + SpvExecutionModeIsApiEntryAMDX = 5070, + SpvExecutionModeMaxNodeRecursionAMDX = 5071, + SpvExecutionModeStaticNumWorkgroupsAMDX = 5072, + SpvExecutionModeShaderIndexAMDX = 5073, + SpvExecutionModeMaxNumWorkgroupsAMDX = 5077, + SpvExecutionModeStencilRefUnchangedFrontAMD = 5079, + SpvExecutionModeStencilRefGreaterFrontAMD = 5080, + SpvExecutionModeStencilRefLessFrontAMD = 5081, + SpvExecutionModeStencilRefUnchangedBackAMD = 5082, + SpvExecutionModeStencilRefGreaterBackAMD = 5083, + SpvExecutionModeStencilRefLessBackAMD = 5084, + SpvExecutionModeQuadDerivativesKHR = 5088, + SpvExecutionModeRequireFullQuadsKHR = 5089, + SpvExecutionModeSharesInputWithAMDX = 5102, + SpvExecutionModeOutputLinesEXT = 5269, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesEXT = 5270, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsKHR = 5289, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearKHR = 5290, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesEXT = 5298, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeSharedLocalMemorySizeINTEL = 5618, + SpvExecutionModeRoundingModeRTPINTEL = 5620, + SpvExecutionModeRoundingModeRTNINTEL = 5621, + SpvExecutionModeFloatingPointModeALTINTEL = 5622, + SpvExecutionModeFloatingPointModeIEEEINTEL = 5623, + SpvExecutionModeMaxWorkgroupSizeINTEL = 5893, + SpvExecutionModeMaxWorkDimINTEL = 5894, + SpvExecutionModeNoGlobalOffsetINTEL = 5895, + SpvExecutionModeNumSIMDWorkitemsINTEL = 5896, + SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + SpvExecutionModeMaximallyReconvergesKHR = 6023, + SpvExecutionModeFPFastMathDefault = 6028, + SpvExecutionModeStreamingInterfaceINTEL = 6154, + SpvExecutionModeRegisterMapInterfaceINTEL = 6160, + SpvExecutionModeNamedBarrierCountINTEL = 6417, + SpvExecutionModeMaximumRegistersINTEL = 6461, + SpvExecutionModeMaximumRegistersIdINTEL = 6462, + SpvExecutionModeNamedMaximumRegistersINTEL = 6463, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassTileImageEXT = 4172, + SpvStorageClassTileAttachmentQCOM = 4491, + SpvStorageClassNodePayloadAMDX = 5068, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassHitObjectAttributeNV = 5385, + SpvStorageClassTaskPayloadWorkgroupEXT = 5402, + SpvStorageClassCodeSectionINTEL = 5605, + SpvStorageClassDeviceOnlyINTEL = 5936, + SpvStorageClassHostOnlyINTEL = 5937, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimTileImageDataEXT = 4173, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatR64ui = 40, + SpvImageFormatR64i = 41, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeUnormInt10X6EXT = 17, + SpvImageChannelDataTypeUnsignedIntRaw10EXT = 19, + SpvImageChannelDataTypeUnsignedIntRaw12EXT = 20, + SpvImageChannelDataTypeUnormInt2_101010EXT = 21, + SpvImageChannelDataTypeUnsignedInt10X6EXT = 22, + SpvImageChannelDataTypeUnsignedInt12X4EXT = 23, + SpvImageChannelDataTypeUnsignedInt14X2EXT = 24, + SpvImageChannelDataTypeUnormInt12X4EXT = 25, + SpvImageChannelDataTypeUnormInt14X2EXT = 26, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsNontemporalShift = 14, + SpvImageOperandsOffsetsShift = 16, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, + SpvImageOperandsNontemporalMask = 0x00004000, + SpvImageOperandsOffsetsMask = 0x00010000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeAllowContractShift = 16, + SpvFPFastMathModeAllowContractFastINTELShift = 16, + SpvFPFastMathModeAllowReassocShift = 17, + SpvFPFastMathModeAllowReassocINTELShift = 17, + SpvFPFastMathModeAllowTransformShift = 18, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, + SpvFPFastMathModeAllowContractMask = 0x00010000, + SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000, + SpvFPFastMathModeAllowReassocMask = 0x00020000, + SpvFPFastMathModeAllowReassocINTELMask = 0x00020000, + SpvFPFastMathModeAllowTransformMask = 0x00040000, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeLinkOnceODR = 2, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeRuntimeAlignedINTEL = 5940, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationWeightTextureQCOM = 4487, + SpvDecorationBlockMatchTextureQCOM = 4488, + SpvDecorationBlockMatchSamplerQCOM = 4499, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationNodeSharesPayloadLimitsWithAMDX = 5019, + SpvDecorationNodeMaxPayloadsAMDX = 5020, + SpvDecorationTrackFinishWritingAMDX = 5078, + SpvDecorationPayloadNodeNameAMDX = 5091, + SpvDecorationPayloadNodeBaseIndexAMDX = 5098, + SpvDecorationPayloadNodeSparseArrayAMDX = 5099, + SpvDecorationPayloadNodeArraySizeAMDX = 5100, + SpvDecorationPayloadDispatchIndirectAMDX = 5105, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveEXT = 5271, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexKHR = 5285, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationHitObjectShaderRecordBufferNV = 5386, + SpvDecorationBindlessSamplerNV = 5398, + SpvDecorationBindlessImageNV = 5399, + SpvDecorationBoundSamplerNV = 5400, + SpvDecorationBoundImageNV = 5401, + SpvDecorationSIMTCallINTEL = 5599, + SpvDecorationReferencedIndirectlyINTEL = 5602, + SpvDecorationClobberINTEL = 5607, + SpvDecorationSideEffectsINTEL = 5608, + SpvDecorationVectorComputeVariableINTEL = 5624, + SpvDecorationFuncParamIOKindINTEL = 5625, + SpvDecorationVectorComputeFunctionINTEL = 5626, + SpvDecorationStackCallINTEL = 5627, + SpvDecorationGlobalVariableOffsetINTEL = 5628, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationFunctionRoundingModeINTEL = 5822, + SpvDecorationFunctionDenormModeINTEL = 5823, + SpvDecorationRegisterINTEL = 5825, + SpvDecorationMemoryINTEL = 5826, + SpvDecorationNumbanksINTEL = 5827, + SpvDecorationBankwidthINTEL = 5828, + SpvDecorationMaxPrivateCopiesINTEL = 5829, + SpvDecorationSinglepumpINTEL = 5830, + SpvDecorationDoublepumpINTEL = 5831, + SpvDecorationMaxReplicatesINTEL = 5832, + SpvDecorationSimpleDualPortINTEL = 5833, + SpvDecorationMergeINTEL = 5834, + SpvDecorationBankBitsINTEL = 5835, + SpvDecorationForcePow2DepthINTEL = 5836, + SpvDecorationStridesizeINTEL = 5883, + SpvDecorationWordsizeINTEL = 5884, + SpvDecorationTrueDualPortINTEL = 5885, + SpvDecorationBurstCoalesceINTEL = 5899, + SpvDecorationCacheSizeINTEL = 5900, + SpvDecorationDontStaticallyCoalesceINTEL = 5901, + SpvDecorationPrefetchINTEL = 5902, + SpvDecorationStallEnableINTEL = 5905, + SpvDecorationFuseLoopsInFunctionINTEL = 5907, + SpvDecorationMathOpDSPModeINTEL = 5909, + SpvDecorationAliasScopeINTEL = 5914, + SpvDecorationNoAliasINTEL = 5915, + SpvDecorationInitiationIntervalINTEL = 5917, + SpvDecorationMaxConcurrencyINTEL = 5918, + SpvDecorationPipelineEnableINTEL = 5919, + SpvDecorationBufferLocationINTEL = 5921, + SpvDecorationIOPipeStorageINTEL = 5944, + SpvDecorationFunctionFloatingPointModeINTEL = 6080, + SpvDecorationSingleElementVectorINTEL = 6085, + SpvDecorationVectorComputeCallableFunctionINTEL = 6087, + SpvDecorationMediaBlockIOINTEL = 6140, + SpvDecorationStallFreeINTEL = 6151, + SpvDecorationFPMaxErrorDecorationINTEL = 6170, + SpvDecorationLatencyControlLabelINTEL = 6172, + SpvDecorationLatencyControlConstraintINTEL = 6173, + SpvDecorationConduitKernelArgumentINTEL = 6175, + SpvDecorationRegisterMapKernelArgumentINTEL = 6176, + SpvDecorationMMHostInterfaceAddressWidthINTEL = 6177, + SpvDecorationMMHostInterfaceDataWidthINTEL = 6178, + SpvDecorationMMHostInterfaceLatencyINTEL = 6179, + SpvDecorationMMHostInterfaceReadWriteModeINTEL = 6180, + SpvDecorationMMHostInterfaceMaxBurstINTEL = 6181, + SpvDecorationMMHostInterfaceWaitRequestINTEL = 6182, + SpvDecorationStableKernelArgumentINTEL = 6183, + SpvDecorationHostAccessINTEL = 6188, + SpvDecorationInitModeINTEL = 6190, + SpvDecorationImplementInRegisterMapINTEL = 6191, + SpvDecorationCacheControlLoadINTEL = 6442, + SpvDecorationCacheControlStoreINTEL = 6443, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInCoreIDARM = 4160, + SpvBuiltInCoreCountARM = 4161, + SpvBuiltInCoreMaxIDARM = 4162, + SpvBuiltInWarpIDARM = 4163, + SpvBuiltInWarpMaxIDARM = 4164, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInPrimitiveShadingRateKHR = 4432, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInShadingRateKHR = 4444, + SpvBuiltInTileOffsetQCOM = 4492, + SpvBuiltInTileDimensionQCOM = 4493, + SpvBuiltInTileApronSizeQCOM = 4494, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInRemainingRecursionLevelsAMDX = 5021, + SpvBuiltInShaderIndexAMDX = 5073, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordKHR = 5286, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspKHR = 5287, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInPrimitivePointIndicesEXT = 5294, + SpvBuiltInPrimitiveLineIndicesEXT = 5295, + SpvBuiltInPrimitiveTriangleIndicesEXT = 5296, + SpvBuiltInCullPrimitiveEXT = 5299, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInCurrentRayTimeNV = 5334, + SpvBuiltInHitTriangleVertexPositionsKHR = 5335, + SpvBuiltInHitMicroTriangleVertexPositionsNV = 5337, + SpvBuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInHitIsSphereNV = 5359, + SpvBuiltInHitIsLSSNV = 5360, + SpvBuiltInHitSpherePositionNV = 5361, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInHitLSSPositionsNV = 5396, + SpvBuiltInHitKindFrontFacingMicroTriangleNV = 5405, + SpvBuiltInHitKindBackFacingMicroTriangleNV = 5406, + SpvBuiltInHitSphereRadiusNV = 5420, + SpvBuiltInHitLSSRadiiNV = 5421, + SpvBuiltInClusterIDNV = 5436, + SpvBuiltInCullMaskKHR = 6021, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlInitiationIntervalINTELShift = 16, + SpvLoopControlMaxConcurrencyINTELShift = 17, + SpvLoopControlDependencyArrayINTELShift = 18, + SpvLoopControlPipelineEnableINTELShift = 19, + SpvLoopControlLoopCoalesceINTELShift = 20, + SpvLoopControlMaxInterleavingINTELShift = 21, + SpvLoopControlSpeculatedIterationsINTELShift = 22, + SpvLoopControlNoFusionINTELShift = 23, + SpvLoopControlLoopCountINTELShift = 24, + SpvLoopControlMaxReinvocationDelayINTELShift = 25, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, + SpvLoopControlInitiationIntervalINTELMask = 0x00010000, + SpvLoopControlMaxConcurrencyINTELMask = 0x00020000, + SpvLoopControlDependencyArrayINTELMask = 0x00040000, + SpvLoopControlPipelineEnableINTELMask = 0x00080000, + SpvLoopControlLoopCoalesceINTELMask = 0x00100000, + SpvLoopControlMaxInterleavingINTELMask = 0x00200000, + SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000, + SpvLoopControlNoFusionINTELMask = 0x00800000, + SpvLoopControlLoopCountINTELMask = 0x01000000, + SpvLoopControlMaxReinvocationDelayINTELMask = 0x02000000, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlOptNoneEXTShift = 16, + SpvFunctionControlOptNoneINTELShift = 16, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, + SpvFunctionControlOptNoneEXTMask = 0x00010000, + SpvFunctionControlOptNoneINTELMask = 0x00010000, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessAliasScopeINTELMaskShift = 16, + SpvMemoryAccessNoAliasINTELMaskShift = 17, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, + SpvMemoryAccessAliasScopeINTELMaskMask = 0x00010000, + SpvMemoryAccessNoAliasINTELMaskMask = 0x00020000, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilityUniformDecoration = 71, + SpvCapabilityCoreBuiltinsARM = 4165, + SpvCapabilityTileImageColorReadAccessEXT = 4166, + SpvCapabilityTileImageDepthReadAccessEXT = 4167, + SpvCapabilityTileImageStencilReadAccessEXT = 4168, + SpvCapabilityCooperativeMatrixLayoutsARM = 4201, + SpvCapabilityFragmentShadingRateKHR = 4422, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayQueryKHR = 4472, + SpvCapabilityUntypedPointersKHR = 4473, + SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478, + SpvCapabilityRayTracingKHR = 4479, + SpvCapabilityTextureSampleWeightedQCOM = 4484, + SpvCapabilityTextureBoxFilterQCOM = 4485, + SpvCapabilityTextureBlockMatchQCOM = 4486, + SpvCapabilityTileShadingQCOM = 4495, + SpvCapabilityTextureBlockMatch2QCOM = 4498, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityInt64ImageEXT = 5016, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilityShaderEnqueueAMDX = 5067, + SpvCapabilityQuadControlKHR = 5087, + SpvCapabilityBFloat16TypeKHR = 5116, + SpvCapabilityBFloat16DotProductKHR = 5117, + SpvCapabilityBFloat16CooperativeMatrixKHR = 5118, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityMeshShadingEXT = 5283, + SpvCapabilityFragmentBarycentricKHR = 5284, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsKHR = 5288, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingPositionFetchKHR = 5336, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityRayTracingMotionBlurNV = 5341, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearKHR = 5350, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocation = 5379, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilityDisplacementMicromapNV = 5380, + SpvCapabilityRayTracingOpacityMicromapEXT = 5381, + SpvCapabilityShaderInvocationReorderNV = 5383, + SpvCapabilityBindlessTextureNV = 5390, + SpvCapabilityRayQueryPositionFetchKHR = 5391, + SpvCapabilityCooperativeVectorNV = 5394, + SpvCapabilityAtomicFloat16VectorNV = 5404, + SpvCapabilityRayTracingDisplacementMicromapNV = 5409, + SpvCapabilityRawAccessChainsNV = 5414, + SpvCapabilityRayTracingSpheresGeometryNV = 5418, + SpvCapabilityRayTracingLinearSweptSpheresGeometryNV = 5419, + SpvCapabilityCooperativeMatrixReductionsNV = 5430, + SpvCapabilityCooperativeMatrixConversionsNV = 5431, + SpvCapabilityCooperativeMatrixPerElementOperationsNV = 5432, + SpvCapabilityCooperativeMatrixTensorAddressingNV = 5433, + SpvCapabilityCooperativeMatrixBlockLoadsNV = 5434, + SpvCapabilityCooperativeVectorTrainingNV = 5435, + SpvCapabilityRayTracingClusterAccelerationStructureNV = 5437, + SpvCapabilityTensorAddressingNV = 5439, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityRoundToInfinityINTEL = 5582, + SpvCapabilityFloatingPointModeINTEL = 5583, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilityFunctionPointersINTEL = 5603, + SpvCapabilityIndirectReferencesINTEL = 5604, + SpvCapabilityAsmINTEL = 5606, + SpvCapabilityAtomicFloat32MinMaxEXT = 5612, + SpvCapabilityAtomicFloat64MinMaxEXT = 5613, + SpvCapabilityAtomicFloat16MinMaxEXT = 5616, + SpvCapabilityVectorComputeINTEL = 5617, + SpvCapabilityVectorAnyINTEL = 5619, + SpvCapabilityExpectAssumeKHR = 5629, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityVariableLengthArrayINTEL = 5817, + SpvCapabilityFunctionFloatControlINTEL = 5821, + SpvCapabilityFPGAMemoryAttributesINTEL = 5824, + SpvCapabilityFPFastMathModeINTEL = 5837, + SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844, + SpvCapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + SpvCapabilityUnstructuredLoopControlsINTEL = 5886, + SpvCapabilityFPGALoopControlsINTEL = 5888, + SpvCapabilityKernelAttributesINTEL = 5892, + SpvCapabilityFPGAKernelAttributesINTEL = 5897, + SpvCapabilityFPGAMemoryAccessesINTEL = 5898, + SpvCapabilityFPGAClusterAttributesINTEL = 5904, + SpvCapabilityLoopFuseINTEL = 5906, + SpvCapabilityFPGADSPControlINTEL = 5908, + SpvCapabilityMemoryAccessAliasingINTEL = 5910, + SpvCapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + SpvCapabilityFPGABufferLocationINTEL = 5920, + SpvCapabilityArbitraryPrecisionFixedPointINTEL = 5922, + SpvCapabilityUSMStorageClassesINTEL = 5935, + SpvCapabilityRuntimeAlignedAttributeINTEL = 5939, + SpvCapabilityIOPipesINTEL = 5943, + SpvCapabilityBlockingPipesINTEL = 5945, + SpvCapabilityFPGARegINTEL = 5948, + SpvCapabilityDotProductInputAll = 6016, + SpvCapabilityDotProductInputAllKHR = 6016, + SpvCapabilityDotProductInput4x8Bit = 6017, + SpvCapabilityDotProductInput4x8BitKHR = 6017, + SpvCapabilityDotProductInput4x8BitPacked = 6018, + SpvCapabilityDotProductInput4x8BitPackedKHR = 6018, + SpvCapabilityDotProduct = 6019, + SpvCapabilityDotProductKHR = 6019, + SpvCapabilityRayCullMaskKHR = 6020, + SpvCapabilityCooperativeMatrixKHR = 6022, + SpvCapabilityReplicatedCompositesEXT = 6024, + SpvCapabilityBitInstructions = 6025, + SpvCapabilityGroupNonUniformRotateKHR = 6026, + SpvCapabilityFloatControls2 = 6029, + SpvCapabilityAtomicFloat32AddEXT = 6033, + SpvCapabilityAtomicFloat64AddEXT = 6034, + SpvCapabilityLongCompositesINTEL = 6089, + SpvCapabilityOptNoneEXT = 6094, + SpvCapabilityOptNoneINTEL = 6094, + SpvCapabilityAtomicFloat16AddEXT = 6095, + SpvCapabilityDebugInfoModuleINTEL = 6114, + SpvCapabilityBFloat16ConversionINTEL = 6115, + SpvCapabilitySplitBarrierINTEL = 6141, + SpvCapabilityArithmeticFenceEXT = 6144, + SpvCapabilityFPGAClusterAttributesV2INTEL = 6150, + SpvCapabilityFPGAKernelAttributesv2INTEL = 6161, + SpvCapabilityTaskSequenceINTEL = 6162, + SpvCapabilityFPMaxErrorINTEL = 6169, + SpvCapabilityFPGALatencyControlINTEL = 6171, + SpvCapabilityFPGAArgumentInterfacesINTEL = 6174, + SpvCapabilityGlobalVariableHostAccessINTEL = 6187, + SpvCapabilityGlobalVariableFPGADecorationsINTEL = 6189, + SpvCapabilitySubgroupBufferPrefetchINTEL = 6220, + SpvCapabilitySubgroup2DBlockIOINTEL = 6228, + SpvCapabilitySubgroup2DBlockTransformINTEL = 6229, + SpvCapabilitySubgroup2DBlockTransposeINTEL = 6230, + SpvCapabilitySubgroupMatrixMultiplyAccumulateINTEL = 6236, + SpvCapabilityTernaryBitwiseFunctionINTEL = 6241, + SpvCapabilityGroupUniformArithmeticKHR = 6400, + SpvCapabilityTensorFloat32RoundingINTEL = 6425, + SpvCapabilityMaskedGatherScatterINTEL = 6427, + SpvCapabilityCacheControlsINTEL = 6441, + SpvCapabilityRegisterLimitsINTEL = 6460, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipBuiltinPrimitivesNVShift = 8, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsForceOpacityMicromap2StateEXTShift = 10, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipBuiltinPrimitivesNVMask = 0x00000100, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, + SpvRayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvFragmentShadingRateShift_ { + SpvFragmentShadingRateVertical2PixelsShift = 0, + SpvFragmentShadingRateVertical4PixelsShift = 1, + SpvFragmentShadingRateHorizontal2PixelsShift = 2, + SpvFragmentShadingRateHorizontal4PixelsShift = 3, + SpvFragmentShadingRateMax = 0x7fffffff, +} SpvFragmentShadingRateShift; + +typedef enum SpvFragmentShadingRateMask_ { + SpvFragmentShadingRateMaskNone = 0, + SpvFragmentShadingRateVertical2PixelsMask = 0x00000001, + SpvFragmentShadingRateVertical4PixelsMask = 0x00000002, + SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004, + SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008, +} SpvFragmentShadingRateMask; + +typedef enum SpvFPDenormMode_ { + SpvFPDenormModePreserve = 0, + SpvFPDenormModeFlushToZero = 1, + SpvFPDenormModeMax = 0x7fffffff, +} SpvFPDenormMode; + +typedef enum SpvFPOperationMode_ { + SpvFPOperationModeIEEE = 0, + SpvFPOperationModeALT = 1, + SpvFPOperationModeMax = 0x7fffffff, +} SpvFPOperationMode; + +typedef enum SpvQuantizationModes_ { + SpvQuantizationModesTRN = 0, + SpvQuantizationModesTRN_ZERO = 1, + SpvQuantizationModesRND = 2, + SpvQuantizationModesRND_ZERO = 3, + SpvQuantizationModesRND_INF = 4, + SpvQuantizationModesRND_MIN_INF = 5, + SpvQuantizationModesRND_CONV = 6, + SpvQuantizationModesRND_CONV_ODD = 7, + SpvQuantizationModesMax = 0x7fffffff, +} SpvQuantizationModes; + +typedef enum SpvOverflowModes_ { + SpvOverflowModesWRAP = 0, + SpvOverflowModesSAT = 1, + SpvOverflowModesSAT_ZERO = 2, + SpvOverflowModesSAT_SYM = 3, + SpvOverflowModesMax = 0x7fffffff, +} SpvOverflowModes; + +typedef enum SpvPackedVectorFormat_ { + SpvPackedVectorFormatPackedVectorFormat4x8Bit = 0, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + SpvPackedVectorFormatMax = 0x7fffffff, +} SpvPackedVectorFormat; + +typedef enum SpvCooperativeMatrixOperandsShift_ { + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + SpvCooperativeMatrixOperandsMax = 0x7fffffff, +} SpvCooperativeMatrixOperandsShift; + +typedef enum SpvCooperativeMatrixOperandsMask_ { + SpvCooperativeMatrixOperandsMaskNone = 0, + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +} SpvCooperativeMatrixOperandsMask; + +typedef enum SpvCooperativeMatrixLayout_ { + SpvCooperativeMatrixLayoutRowMajorKHR = 0, + SpvCooperativeMatrixLayoutColumnMajorKHR = 1, + SpvCooperativeMatrixLayoutRowBlockedInterleavedARM = 4202, + SpvCooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203, + SpvCooperativeMatrixLayoutMax = 0x7fffffff, +} SpvCooperativeMatrixLayout; + +typedef enum SpvCooperativeMatrixUse_ { + SpvCooperativeMatrixUseMatrixAKHR = 0, + SpvCooperativeMatrixUseMatrixBKHR = 1, + SpvCooperativeMatrixUseMatrixAccumulatorKHR = 2, + SpvCooperativeMatrixUseMax = 0x7fffffff, +} SpvCooperativeMatrixUse; + +typedef enum SpvCooperativeMatrixReduceShift_ { + SpvCooperativeMatrixReduceRowShift = 0, + SpvCooperativeMatrixReduceColumnShift = 1, + SpvCooperativeMatrixReduce2x2Shift = 2, + SpvCooperativeMatrixReduceMax = 0x7fffffff, +} SpvCooperativeMatrixReduceShift; + +typedef enum SpvCooperativeMatrixReduceMask_ { + SpvCooperativeMatrixReduceMaskNone = 0, + SpvCooperativeMatrixReduceRowMask = 0x00000001, + SpvCooperativeMatrixReduceColumnMask = 0x00000002, + SpvCooperativeMatrixReduce2x2Mask = 0x00000004, +} SpvCooperativeMatrixReduceMask; + +typedef enum SpvTensorClampMode_ { + SpvTensorClampModeUndefined = 0, + SpvTensorClampModeConstant = 1, + SpvTensorClampModeClampToEdge = 2, + SpvTensorClampModeRepeat = 3, + SpvTensorClampModeRepeatMirrored = 4, + SpvTensorClampModeMax = 0x7fffffff, +} SpvTensorClampMode; + +typedef enum SpvTensorAddressingOperandsShift_ { + SpvTensorAddressingOperandsTensorViewShift = 0, + SpvTensorAddressingOperandsDecodeFuncShift = 1, + SpvTensorAddressingOperandsMax = 0x7fffffff, +} SpvTensorAddressingOperandsShift; + +typedef enum SpvTensorAddressingOperandsMask_ { + SpvTensorAddressingOperandsMaskNone = 0, + SpvTensorAddressingOperandsTensorViewMask = 0x00000001, + SpvTensorAddressingOperandsDecodeFuncMask = 0x00000002, +} SpvTensorAddressingOperandsMask; + +typedef enum SpvInitializationModeQualifier_ { + SpvInitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + SpvInitializationModeQualifierInitOnDeviceResetINTEL = 1, + SpvInitializationModeQualifierMax = 0x7fffffff, +} SpvInitializationModeQualifier; + +typedef enum SpvHostAccessQualifier_ { + SpvHostAccessQualifierNoneINTEL = 0, + SpvHostAccessQualifierReadINTEL = 1, + SpvHostAccessQualifierWriteINTEL = 2, + SpvHostAccessQualifierReadWriteINTEL = 3, + SpvHostAccessQualifierMax = 0x7fffffff, +} SpvHostAccessQualifier; + +typedef enum SpvLoadCacheControl_ { + SpvLoadCacheControlUncachedINTEL = 0, + SpvLoadCacheControlCachedINTEL = 1, + SpvLoadCacheControlStreamingINTEL = 2, + SpvLoadCacheControlInvalidateAfterReadINTEL = 3, + SpvLoadCacheControlConstCachedINTEL = 4, + SpvLoadCacheControlMax = 0x7fffffff, +} SpvLoadCacheControl; + +typedef enum SpvStoreCacheControl_ { + SpvStoreCacheControlUncachedINTEL = 0, + SpvStoreCacheControlWriteThroughINTEL = 1, + SpvStoreCacheControlWriteBackINTEL = 2, + SpvStoreCacheControlStreamingINTEL = 3, + SpvStoreCacheControlMax = 0x7fffffff, +} SpvStoreCacheControl; + +typedef enum SpvNamedMaximumNumberOfRegisters_ { + SpvNamedMaximumNumberOfRegistersAutoINTEL = 0, + SpvNamedMaximumNumberOfRegistersMax = 0x7fffffff, +} SpvNamedMaximumNumberOfRegisters; + +typedef enum SpvMatrixMultiplyAccumulateOperandsShift_ { + SpvMatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELShift = 0, + SpvMatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELShift = 1, + SpvMatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELShift = 2, + SpvMatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELShift = 3, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELShift = 4, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELShift = 5, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELShift = 6, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELShift = 7, + SpvMatrixMultiplyAccumulateOperandsMatrixATF32INTELShift = 8, + SpvMatrixMultiplyAccumulateOperandsMatrixBTF32INTELShift = 9, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELShift = 10, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELShift = 11, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELShift = 12, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELShift = 13, + SpvMatrixMultiplyAccumulateOperandsMax = 0x7fffffff, +} SpvMatrixMultiplyAccumulateOperandsShift; + +typedef enum SpvMatrixMultiplyAccumulateOperandsMask_ { + SpvMatrixMultiplyAccumulateOperandsMaskNone = 0, + SpvMatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELMask = 0x00000001, + SpvMatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELMask = 0x00000002, + SpvMatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELMask = 0x00000004, + SpvMatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELMask = 0x00000008, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELMask = 0x00000010, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELMask = 0x00000020, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELMask = 0x00000040, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELMask = 0x00000080, + SpvMatrixMultiplyAccumulateOperandsMatrixATF32INTELMask = 0x00000100, + SpvMatrixMultiplyAccumulateOperandsMatrixBTF32INTELMask = 0x00000200, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELMask = 0x00000400, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELMask = 0x00000800, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELMask = 0x00001000, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELMask = 0x00002000, +} SpvMatrixMultiplyAccumulateOperandsMask; + +typedef enum SpvRawAccessChainOperandsShift_ { + SpvRawAccessChainOperandsRobustnessPerComponentNVShift = 0, + SpvRawAccessChainOperandsRobustnessPerElementNVShift = 1, + SpvRawAccessChainOperandsMax = 0x7fffffff, +} SpvRawAccessChainOperandsShift; + +typedef enum SpvRawAccessChainOperandsMask_ { + SpvRawAccessChainOperandsMaskNone = 0, + SpvRawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + SpvRawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +} SpvRawAccessChainOperandsMask; + +typedef enum SpvFPEncoding_ { + SpvFPEncodingBFloat16KHR = 0, + SpvFPEncodingMax = 0x7fffffff, +} SpvFPEncoding; + +typedef enum SpvCooperativeVectorMatrixLayout_ { + SpvCooperativeVectorMatrixLayoutRowMajorNV = 0, + SpvCooperativeVectorMatrixLayoutColumnMajorNV = 1, + SpvCooperativeVectorMatrixLayoutInferencingOptimalNV = 2, + SpvCooperativeVectorMatrixLayoutTrainingOptimalNV = 3, + SpvCooperativeVectorMatrixLayoutMax = 0x7fffffff, +} SpvCooperativeVectorMatrixLayout; + +typedef enum SpvComponentType_ { + SpvComponentTypeFloat16NV = 0, + SpvComponentTypeFloat32NV = 1, + SpvComponentTypeFloat64NV = 2, + SpvComponentTypeSignedInt8NV = 3, + SpvComponentTypeSignedInt16NV = 4, + SpvComponentTypeSignedInt32NV = 5, + SpvComponentTypeSignedInt64NV = 6, + SpvComponentTypeUnsignedInt8NV = 7, + SpvComponentTypeUnsignedInt16NV = 8, + SpvComponentTypeUnsignedInt32NV = 9, + SpvComponentTypeUnsignedInt64NV = 10, + SpvComponentTypeSignedInt8PackedNV = 1000491000, + SpvComponentTypeUnsignedInt8PackedNV = 1000491001, + SpvComponentTypeFloatE4M3NV = 1000491002, + SpvComponentTypeFloatE5M2NV = 1000491003, + SpvComponentTypeMax = 0x7fffffff, +} SpvComponentType; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpColorAttachmentReadEXT = 4160, + SpvOpDepthAttachmentReadEXT = 4161, + SpvOpStencilAttachmentReadEXT = 4162, + SpvOpTerminateInvocation = 4416, + SpvOpTypeUntypedPointerKHR = 4417, + SpvOpUntypedVariableKHR = 4418, + SpvOpUntypedAccessChainKHR = 4419, + SpvOpUntypedInBoundsAccessChainKHR = 4420, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpUntypedPtrAccessChainKHR = 4423, + SpvOpUntypedInBoundsPtrAccessChainKHR = 4424, + SpvOpUntypedArrayLengthKHR = 4425, + SpvOpUntypedPrefetchKHR = 4426, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpGroupNonUniformRotateKHR = 4431, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpExtInstWithForwardRefsKHR = 4433, + SpvOpTraceRayKHR = 4445, + SpvOpExecuteCallableKHR = 4446, + SpvOpConvertUToAccelerationStructureKHR = 4447, + SpvOpIgnoreIntersectionKHR = 4448, + SpvOpTerminateRayKHR = 4449, + SpvOpSDot = 4450, + SpvOpSDotKHR = 4450, + SpvOpUDot = 4451, + SpvOpUDotKHR = 4451, + SpvOpSUDot = 4452, + SpvOpSUDotKHR = 4452, + SpvOpSDotAccSat = 4453, + SpvOpSDotAccSatKHR = 4453, + SpvOpUDotAccSat = 4454, + SpvOpUDotAccSatKHR = 4454, + SpvOpSUDotAccSat = 4455, + SpvOpSUDotAccSatKHR = 4455, + SpvOpTypeCooperativeMatrixKHR = 4456, + SpvOpCooperativeMatrixLoadKHR = 4457, + SpvOpCooperativeMatrixStoreKHR = 4458, + SpvOpCooperativeMatrixMulAddKHR = 4459, + SpvOpCooperativeMatrixLengthKHR = 4460, + SpvOpConstantCompositeReplicateEXT = 4461, + SpvOpSpecConstantCompositeReplicateEXT = 4462, + SpvOpCompositeConstructReplicateEXT = 4463, + SpvOpTypeRayQueryKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpImageSampleWeightedQCOM = 4480, + SpvOpImageBoxFilterQCOM = 4481, + SpvOpImageBlockMatchSSDQCOM = 4482, + SpvOpImageBlockMatchSADQCOM = 4483, + SpvOpImageBlockMatchWindowSSDQCOM = 4500, + SpvOpImageBlockMatchWindowSADQCOM = 4501, + SpvOpImageBlockMatchGatherSSDQCOM = 4502, + SpvOpImageBlockMatchGatherSADQCOM = 4503, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpAllocateNodePayloadsAMDX = 5074, + SpvOpEnqueueNodePayloadsAMDX = 5075, + SpvOpTypeNodePayloadArrayAMDX = 5076, + SpvOpFinishWritingNodePayloadAMDX = 5078, + SpvOpNodePayloadArrayLengthAMDX = 5090, + SpvOpIsNodePayloadValidAMDX = 5101, + SpvOpConstantStringAMDX = 5103, + SpvOpSpecConstantStringAMDX = 5104, + SpvOpGroupNonUniformQuadAllKHR = 5110, + SpvOpGroupNonUniformQuadAnyKHR = 5111, + SpvOpHitObjectRecordHitMotionNV = 5249, + SpvOpHitObjectRecordHitWithIndexMotionNV = 5250, + SpvOpHitObjectRecordMissMotionNV = 5251, + SpvOpHitObjectGetWorldToObjectNV = 5252, + SpvOpHitObjectGetObjectToWorldNV = 5253, + SpvOpHitObjectGetObjectRayDirectionNV = 5254, + SpvOpHitObjectGetObjectRayOriginNV = 5255, + SpvOpHitObjectTraceRayMotionNV = 5256, + SpvOpHitObjectGetShaderRecordBufferHandleNV = 5257, + SpvOpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + SpvOpHitObjectRecordEmptyNV = 5259, + SpvOpHitObjectTraceRayNV = 5260, + SpvOpHitObjectRecordHitNV = 5261, + SpvOpHitObjectRecordHitWithIndexNV = 5262, + SpvOpHitObjectRecordMissNV = 5263, + SpvOpHitObjectExecuteShaderNV = 5264, + SpvOpHitObjectGetCurrentTimeNV = 5265, + SpvOpHitObjectGetAttributesNV = 5266, + SpvOpHitObjectGetHitKindNV = 5267, + SpvOpHitObjectGetPrimitiveIndexNV = 5268, + SpvOpHitObjectGetGeometryIndexNV = 5269, + SpvOpHitObjectGetInstanceIdNV = 5270, + SpvOpHitObjectGetInstanceCustomIndexNV = 5271, + SpvOpHitObjectGetWorldRayDirectionNV = 5272, + SpvOpHitObjectGetWorldRayOriginNV = 5273, + SpvOpHitObjectGetRayTMaxNV = 5274, + SpvOpHitObjectGetRayTMinNV = 5275, + SpvOpHitObjectIsEmptyNV = 5276, + SpvOpHitObjectIsHitNV = 5277, + SpvOpHitObjectIsMissNV = 5278, + SpvOpReorderThreadWithHitObjectNV = 5279, + SpvOpReorderThreadWithHintNV = 5280, + SpvOpTypeHitObjectNV = 5281, + SpvOpImageSampleFootprintNV = 5283, + SpvOpTypeCooperativeVectorNV = 5288, + SpvOpCooperativeVectorMatrixMulNV = 5289, + SpvOpCooperativeVectorOuterProductAccumulateNV = 5290, + SpvOpCooperativeVectorReduceSumAccumulateNV = 5291, + SpvOpCooperativeVectorMatrixMulAddNV = 5292, + SpvOpCooperativeMatrixConvertNV = 5293, + SpvOpEmitMeshTasksEXT = 5294, + SpvOpSetMeshOutputsEXT = 5295, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpFetchMicroTriangleVertexPositionNV = 5300, + SpvOpFetchMicroTriangleVertexBarycentricNV = 5301, + SpvOpCooperativeVectorLoadNV = 5302, + SpvOpCooperativeVectorStoreNV = 5303, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTraceMotionNV = 5338, + SpvOpTraceRayMotionNV = 5339, + SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableNV = 5344, + SpvOpRayQueryGetClusterIdNV = 5345, + SpvOpHitObjectGetClusterIdNV = 5346, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpCooperativeMatrixReduceNV = 5366, + SpvOpCooperativeMatrixLoadTensorNV = 5367, + SpvOpCooperativeMatrixStoreTensorNV = 5368, + SpvOpCooperativeMatrixPerElementOpNV = 5369, + SpvOpTypeTensorLayoutNV = 5370, + SpvOpTypeTensorViewNV = 5371, + SpvOpCreateTensorLayoutNV = 5372, + SpvOpTensorLayoutSetDimensionNV = 5373, + SpvOpTensorLayoutSetStrideNV = 5374, + SpvOpTensorLayoutSliceNV = 5375, + SpvOpTensorLayoutSetClampValueNV = 5376, + SpvOpCreateTensorViewNV = 5377, + SpvOpTensorViewSetDimensionNV = 5378, + SpvOpTensorViewSetStrideNV = 5379, + SpvOpDemoteToHelperInvocation = 5380, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpTensorViewSetClipNV = 5382, + SpvOpTensorLayoutSetBlockSizeNV = 5384, + SpvOpCooperativeMatrixTransposeNV = 5390, + SpvOpConvertUToImageNV = 5391, + SpvOpConvertUToSamplerNV = 5392, + SpvOpConvertImageToUNV = 5393, + SpvOpConvertSamplerToUNV = 5394, + SpvOpConvertUToSampledImageNV = 5395, + SpvOpConvertSampledImageToUNV = 5396, + SpvOpSamplerImageAddressingModeNV = 5397, + SpvOpRawAccessChainNV = 5398, + SpvOpRayQueryGetIntersectionSpherePositionNV = 5427, + SpvOpRayQueryGetIntersectionSphereRadiusNV = 5428, + SpvOpRayQueryGetIntersectionLSSPositionsNV = 5429, + SpvOpRayQueryGetIntersectionLSSRadiiNV = 5430, + SpvOpRayQueryGetIntersectionLSSHitValueNV = 5431, + SpvOpHitObjectGetSpherePositionNV = 5432, + SpvOpHitObjectGetSphereRadiusNV = 5433, + SpvOpHitObjectGetLSSPositionsNV = 5434, + SpvOpHitObjectGetLSSRadiiNV = 5435, + SpvOpHitObjectIsSphereHitNV = 5436, + SpvOpHitObjectIsLSSHitNV = 5437, + SpvOpRayQueryIsSphereHitNV = 5438, + SpvOpRayQueryIsLSSHitNV = 5439, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpConstantFunctionPointerINTEL = 5600, + SpvOpFunctionPointerCallINTEL = 5601, + SpvOpAsmTargetINTEL = 5609, + SpvOpAsmINTEL = 5610, + SpvOpAsmCallINTEL = 5611, + SpvOpAtomicFMinEXT = 5614, + SpvOpAtomicFMaxEXT = 5615, + SpvOpAssumeTrueKHR = 5630, + SpvOpExpectKHR = 5631, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpVariableLengthArrayINTEL = 5818, + SpvOpSaveMemoryINTEL = 5819, + SpvOpRestoreMemoryINTEL = 5820, + SpvOpArbitraryFloatSinCosPiINTEL = 5840, + SpvOpArbitraryFloatCastINTEL = 5841, + SpvOpArbitraryFloatCastFromIntINTEL = 5842, + SpvOpArbitraryFloatCastToIntINTEL = 5843, + SpvOpArbitraryFloatAddINTEL = 5846, + SpvOpArbitraryFloatSubINTEL = 5847, + SpvOpArbitraryFloatMulINTEL = 5848, + SpvOpArbitraryFloatDivINTEL = 5849, + SpvOpArbitraryFloatGTINTEL = 5850, + SpvOpArbitraryFloatGEINTEL = 5851, + SpvOpArbitraryFloatLTINTEL = 5852, + SpvOpArbitraryFloatLEINTEL = 5853, + SpvOpArbitraryFloatEQINTEL = 5854, + SpvOpArbitraryFloatRecipINTEL = 5855, + SpvOpArbitraryFloatRSqrtINTEL = 5856, + SpvOpArbitraryFloatCbrtINTEL = 5857, + SpvOpArbitraryFloatHypotINTEL = 5858, + SpvOpArbitraryFloatSqrtINTEL = 5859, + SpvOpArbitraryFloatLogINTEL = 5860, + SpvOpArbitraryFloatLog2INTEL = 5861, + SpvOpArbitraryFloatLog10INTEL = 5862, + SpvOpArbitraryFloatLog1pINTEL = 5863, + SpvOpArbitraryFloatExpINTEL = 5864, + SpvOpArbitraryFloatExp2INTEL = 5865, + SpvOpArbitraryFloatExp10INTEL = 5866, + SpvOpArbitraryFloatExpm1INTEL = 5867, + SpvOpArbitraryFloatSinINTEL = 5868, + SpvOpArbitraryFloatCosINTEL = 5869, + SpvOpArbitraryFloatSinCosINTEL = 5870, + SpvOpArbitraryFloatSinPiINTEL = 5871, + SpvOpArbitraryFloatCosPiINTEL = 5872, + SpvOpArbitraryFloatASinINTEL = 5873, + SpvOpArbitraryFloatASinPiINTEL = 5874, + SpvOpArbitraryFloatACosINTEL = 5875, + SpvOpArbitraryFloatACosPiINTEL = 5876, + SpvOpArbitraryFloatATanINTEL = 5877, + SpvOpArbitraryFloatATanPiINTEL = 5878, + SpvOpArbitraryFloatATan2INTEL = 5879, + SpvOpArbitraryFloatPowINTEL = 5880, + SpvOpArbitraryFloatPowRINTEL = 5881, + SpvOpArbitraryFloatPowNINTEL = 5882, + SpvOpLoopControlINTEL = 5887, + SpvOpAliasDomainDeclINTEL = 5911, + SpvOpAliasScopeDeclINTEL = 5912, + SpvOpAliasScopeListDeclINTEL = 5913, + SpvOpFixedSqrtINTEL = 5923, + SpvOpFixedRecipINTEL = 5924, + SpvOpFixedRsqrtINTEL = 5925, + SpvOpFixedSinINTEL = 5926, + SpvOpFixedCosINTEL = 5927, + SpvOpFixedSinCosINTEL = 5928, + SpvOpFixedSinPiINTEL = 5929, + SpvOpFixedCosPiINTEL = 5930, + SpvOpFixedSinCosPiINTEL = 5931, + SpvOpFixedLogINTEL = 5932, + SpvOpFixedExpINTEL = 5933, + SpvOpPtrCastToCrossWorkgroupINTEL = 5934, + SpvOpCrossWorkgroupCastToPtrINTEL = 5938, + SpvOpReadPipeBlockingINTEL = 5946, + SpvOpWritePipeBlockingINTEL = 5947, + SpvOpFPGARegINTEL = 5949, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpAtomicFAddEXT = 6035, + SpvOpTypeBufferSurfaceINTEL = 6086, + SpvOpTypeStructContinuedINTEL = 6090, + SpvOpConstantCompositeContinuedINTEL = 6091, + SpvOpSpecConstantCompositeContinuedINTEL = 6092, + SpvOpCompositeConstructContinuedINTEL = 6096, + SpvOpConvertFToBF16INTEL = 6116, + SpvOpConvertBF16ToFINTEL = 6117, + SpvOpControlBarrierArriveINTEL = 6142, + SpvOpControlBarrierWaitINTEL = 6143, + SpvOpArithmeticFenceEXT = 6145, + SpvOpTaskSequenceCreateINTEL = 6163, + SpvOpTaskSequenceAsyncINTEL = 6164, + SpvOpTaskSequenceGetINTEL = 6165, + SpvOpTaskSequenceReleaseINTEL = 6166, + SpvOpTypeTaskSequenceINTEL = 6199, + SpvOpSubgroupBlockPrefetchINTEL = 6221, + SpvOpSubgroup2DBlockLoadINTEL = 6231, + SpvOpSubgroup2DBlockLoadTransformINTEL = 6232, + SpvOpSubgroup2DBlockLoadTransposeINTEL = 6233, + SpvOpSubgroup2DBlockPrefetchINTEL = 6234, + SpvOpSubgroup2DBlockStoreINTEL = 6235, + SpvOpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + SpvOpBitwiseFunctionINTEL = 6242, + SpvOpGroupIMulKHR = 6401, + SpvOpGroupFMulKHR = 6402, + SpvOpGroupBitwiseAndKHR = 6403, + SpvOpGroupBitwiseOrKHR = 6404, + SpvOpGroupBitwiseXorKHR = 6405, + SpvOpGroupLogicalAndKHR = 6406, + SpvOpGroupLogicalOrKHR = 6407, + SpvOpGroupLogicalXorKHR = 6408, + SpvOpRoundFToTF32INTEL = 6426, + SpvOpMaskedGatherINTEL = 6428, + SpvOpMaskedScatterINTEL = 6429, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpSDot: *hasResult = true; *hasResultType = true; break; + case SpvOpUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case SpvOpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case SpvOpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeVectorNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case SpvOpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSHitValueNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExpectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedLogINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedExpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceCreateINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceAsyncINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpTaskSequenceGetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceReleaseINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeTaskSequenceINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadTransformINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadTransposeINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockStoreINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupMatrixMultiplyAccumulateINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRoundFToTF32INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + } +} +inline const char* SpvSourceLanguageToString(SpvSourceLanguage value) { + switch (value) { + case SpvSourceLanguageUnknown: return "Unknown"; + case SpvSourceLanguageESSL: return "ESSL"; + case SpvSourceLanguageGLSL: return "GLSL"; + case SpvSourceLanguageOpenCL_C: return "OpenCL_C"; + case SpvSourceLanguageOpenCL_CPP: return "OpenCL_CPP"; + case SpvSourceLanguageHLSL: return "HLSL"; + case SpvSourceLanguageCPP_for_OpenCL: return "CPP_for_OpenCL"; + case SpvSourceLanguageSYCL: return "SYCL"; + case SpvSourceLanguageHERO_C: return "HERO_C"; + case SpvSourceLanguageNZSL: return "NZSL"; + case SpvSourceLanguageWGSL: return "WGSL"; + case SpvSourceLanguageSlang: return "Slang"; + case SpvSourceLanguageZig: return "Zig"; + case SpvSourceLanguageRust: return "Rust"; + default: return "Unknown"; + } +} + +inline const char* SpvExecutionModelToString(SpvExecutionModel value) { + switch (value) { + case SpvExecutionModelVertex: return "Vertex"; + case SpvExecutionModelTessellationControl: return "TessellationControl"; + case SpvExecutionModelTessellationEvaluation: return "TessellationEvaluation"; + case SpvExecutionModelGeometry: return "Geometry"; + case SpvExecutionModelFragment: return "Fragment"; + case SpvExecutionModelGLCompute: return "GLCompute"; + case SpvExecutionModelKernel: return "Kernel"; + case SpvExecutionModelTaskNV: return "TaskNV"; + case SpvExecutionModelMeshNV: return "MeshNV"; + case SpvExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case SpvExecutionModelIntersectionKHR: return "IntersectionKHR"; + case SpvExecutionModelAnyHitKHR: return "AnyHitKHR"; + case SpvExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case SpvExecutionModelMissKHR: return "MissKHR"; + case SpvExecutionModelCallableKHR: return "CallableKHR"; + case SpvExecutionModelTaskEXT: return "TaskEXT"; + case SpvExecutionModelMeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* SpvAddressingModelToString(SpvAddressingModel value) { + switch (value) { + case SpvAddressingModelLogical: return "Logical"; + case SpvAddressingModelPhysical32: return "Physical32"; + case SpvAddressingModelPhysical64: return "Physical64"; + case SpvAddressingModelPhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* SpvMemoryModelToString(SpvMemoryModel value) { + switch (value) { + case SpvMemoryModelSimple: return "Simple"; + case SpvMemoryModelGLSL450: return "GLSL450"; + case SpvMemoryModelOpenCL: return "OpenCL"; + case SpvMemoryModelVulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* SpvExecutionModeToString(SpvExecutionMode value) { + switch (value) { + case SpvExecutionModeInvocations: return "Invocations"; + case SpvExecutionModeSpacingEqual: return "SpacingEqual"; + case SpvExecutionModeSpacingFractionalEven: return "SpacingFractionalEven"; + case SpvExecutionModeSpacingFractionalOdd: return "SpacingFractionalOdd"; + case SpvExecutionModeVertexOrderCw: return "VertexOrderCw"; + case SpvExecutionModeVertexOrderCcw: return "VertexOrderCcw"; + case SpvExecutionModePixelCenterInteger: return "PixelCenterInteger"; + case SpvExecutionModeOriginUpperLeft: return "OriginUpperLeft"; + case SpvExecutionModeOriginLowerLeft: return "OriginLowerLeft"; + case SpvExecutionModeEarlyFragmentTests: return "EarlyFragmentTests"; + case SpvExecutionModePointMode: return "PointMode"; + case SpvExecutionModeXfb: return "Xfb"; + case SpvExecutionModeDepthReplacing: return "DepthReplacing"; + case SpvExecutionModeDepthGreater: return "DepthGreater"; + case SpvExecutionModeDepthLess: return "DepthLess"; + case SpvExecutionModeDepthUnchanged: return "DepthUnchanged"; + case SpvExecutionModeLocalSize: return "LocalSize"; + case SpvExecutionModeLocalSizeHint: return "LocalSizeHint"; + case SpvExecutionModeInputPoints: return "InputPoints"; + case SpvExecutionModeInputLines: return "InputLines"; + case SpvExecutionModeInputLinesAdjacency: return "InputLinesAdjacency"; + case SpvExecutionModeTriangles: return "Triangles"; + case SpvExecutionModeInputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case SpvExecutionModeQuads: return "Quads"; + case SpvExecutionModeIsolines: return "Isolines"; + case SpvExecutionModeOutputVertices: return "OutputVertices"; + case SpvExecutionModeOutputPoints: return "OutputPoints"; + case SpvExecutionModeOutputLineStrip: return "OutputLineStrip"; + case SpvExecutionModeOutputTriangleStrip: return "OutputTriangleStrip"; + case SpvExecutionModeVecTypeHint: return "VecTypeHint"; + case SpvExecutionModeContractionOff: return "ContractionOff"; + case SpvExecutionModeInitializer: return "Initializer"; + case SpvExecutionModeFinalizer: return "Finalizer"; + case SpvExecutionModeSubgroupSize: return "SubgroupSize"; + case SpvExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case SpvExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case SpvExecutionModeLocalSizeId: return "LocalSizeId"; + case SpvExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + case SpvExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case SpvExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case SpvExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case SpvExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case SpvExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case SpvExecutionModeDenormPreserve: return "DenormPreserve"; + case SpvExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case SpvExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case SpvExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case SpvExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case SpvExecutionModeNonCoherentTileAttachmentReadQCOM: return "NonCoherentTileAttachmentReadQCOM"; + case SpvExecutionModeTileShadingRateQCOM: return "TileShadingRateQCOM"; + case SpvExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case SpvExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case SpvExecutionModeCoalescingAMDX: return "CoalescingAMDX"; + case SpvExecutionModeIsApiEntryAMDX: return "IsApiEntryAMDX"; + case SpvExecutionModeMaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case SpvExecutionModeStaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case SpvExecutionModeShaderIndexAMDX: return "ShaderIndexAMDX"; + case SpvExecutionModeMaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case SpvExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case SpvExecutionModeStencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case SpvExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case SpvExecutionModeStencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case SpvExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case SpvExecutionModeStencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case SpvExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + case SpvExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case SpvExecutionModeSharesInputWithAMDX: return "SharesInputWithAMDX"; + case SpvExecutionModeOutputLinesEXT: return "OutputLinesEXT"; + case SpvExecutionModeOutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case SpvExecutionModeDerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case SpvExecutionModeDerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case SpvExecutionModeOutputTrianglesEXT: return "OutputTrianglesEXT"; + case SpvExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case SpvExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case SpvExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case SpvExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case SpvExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case SpvExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case SpvExecutionModeSharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case SpvExecutionModeRoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case SpvExecutionModeRoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case SpvExecutionModeFloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case SpvExecutionModeFloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case SpvExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case SpvExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case SpvExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case SpvExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case SpvExecutionModeSchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case SpvExecutionModeMaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case SpvExecutionModeFPFastMathDefault: return "FPFastMathDefault"; + case SpvExecutionModeStreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case SpvExecutionModeRegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case SpvExecutionModeNamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case SpvExecutionModeMaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case SpvExecutionModeMaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case SpvExecutionModeNamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvStorageClassToString(SpvStorageClass value) { + switch (value) { + case SpvStorageClassUniformConstant: return "UniformConstant"; + case SpvStorageClassInput: return "Input"; + case SpvStorageClassUniform: return "Uniform"; + case SpvStorageClassOutput: return "Output"; + case SpvStorageClassWorkgroup: return "Workgroup"; + case SpvStorageClassCrossWorkgroup: return "CrossWorkgroup"; + case SpvStorageClassPrivate: return "Private"; + case SpvStorageClassFunction: return "Function"; + case SpvStorageClassGeneric: return "Generic"; + case SpvStorageClassPushConstant: return "PushConstant"; + case SpvStorageClassAtomicCounter: return "AtomicCounter"; + case SpvStorageClassImage: return "Image"; + case SpvStorageClassStorageBuffer: return "StorageBuffer"; + case SpvStorageClassTileImageEXT: return "TileImageEXT"; + case SpvStorageClassTileAttachmentQCOM: return "TileAttachmentQCOM"; + case SpvStorageClassNodePayloadAMDX: return "NodePayloadAMDX"; + case SpvStorageClassCallableDataKHR: return "CallableDataKHR"; + case SpvStorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case SpvStorageClassRayPayloadKHR: return "RayPayloadKHR"; + case SpvStorageClassHitAttributeKHR: return "HitAttributeKHR"; + case SpvStorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case SpvStorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case SpvStorageClassPhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case SpvStorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case SpvStorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case SpvStorageClassCodeSectionINTEL: return "CodeSectionINTEL"; + case SpvStorageClassDeviceOnlyINTEL: return "DeviceOnlyINTEL"; + case SpvStorageClassHostOnlyINTEL: return "HostOnlyINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvDimToString(SpvDim value) { + switch (value) { + case SpvDim1D: return "1D"; + case SpvDim2D: return "2D"; + case SpvDim3D: return "3D"; + case SpvDimCube: return "Cube"; + case SpvDimRect: return "Rect"; + case SpvDimBuffer: return "Buffer"; + case SpvDimSubpassData: return "SubpassData"; + case SpvDimTileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SpvSamplerAddressingModeToString(SpvSamplerAddressingMode value) { + switch (value) { + case SpvSamplerAddressingModeNone: return "None"; + case SpvSamplerAddressingModeClampToEdge: return "ClampToEdge"; + case SpvSamplerAddressingModeClamp: return "Clamp"; + case SpvSamplerAddressingModeRepeat: return "Repeat"; + case SpvSamplerAddressingModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SpvSamplerFilterModeToString(SpvSamplerFilterMode value) { + switch (value) { + case SpvSamplerFilterModeNearest: return "Nearest"; + case SpvSamplerFilterModeLinear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* SpvImageFormatToString(SpvImageFormat value) { + switch (value) { + case SpvImageFormatUnknown: return "Unknown"; + case SpvImageFormatRgba32f: return "Rgba32f"; + case SpvImageFormatRgba16f: return "Rgba16f"; + case SpvImageFormatR32f: return "R32f"; + case SpvImageFormatRgba8: return "Rgba8"; + case SpvImageFormatRgba8Snorm: return "Rgba8Snorm"; + case SpvImageFormatRg32f: return "Rg32f"; + case SpvImageFormatRg16f: return "Rg16f"; + case SpvImageFormatR11fG11fB10f: return "R11fG11fB10f"; + case SpvImageFormatR16f: return "R16f"; + case SpvImageFormatRgba16: return "Rgba16"; + case SpvImageFormatRgb10A2: return "Rgb10A2"; + case SpvImageFormatRg16: return "Rg16"; + case SpvImageFormatRg8: return "Rg8"; + case SpvImageFormatR16: return "R16"; + case SpvImageFormatR8: return "R8"; + case SpvImageFormatRgba16Snorm: return "Rgba16Snorm"; + case SpvImageFormatRg16Snorm: return "Rg16Snorm"; + case SpvImageFormatRg8Snorm: return "Rg8Snorm"; + case SpvImageFormatR16Snorm: return "R16Snorm"; + case SpvImageFormatR8Snorm: return "R8Snorm"; + case SpvImageFormatRgba32i: return "Rgba32i"; + case SpvImageFormatRgba16i: return "Rgba16i"; + case SpvImageFormatRgba8i: return "Rgba8i"; + case SpvImageFormatR32i: return "R32i"; + case SpvImageFormatRg32i: return "Rg32i"; + case SpvImageFormatRg16i: return "Rg16i"; + case SpvImageFormatRg8i: return "Rg8i"; + case SpvImageFormatR16i: return "R16i"; + case SpvImageFormatR8i: return "R8i"; + case SpvImageFormatRgba32ui: return "Rgba32ui"; + case SpvImageFormatRgba16ui: return "Rgba16ui"; + case SpvImageFormatRgba8ui: return "Rgba8ui"; + case SpvImageFormatR32ui: return "R32ui"; + case SpvImageFormatRgb10a2ui: return "Rgb10a2ui"; + case SpvImageFormatRg32ui: return "Rg32ui"; + case SpvImageFormatRg16ui: return "Rg16ui"; + case SpvImageFormatRg8ui: return "Rg8ui"; + case SpvImageFormatR16ui: return "R16ui"; + case SpvImageFormatR8ui: return "R8ui"; + case SpvImageFormatR64ui: return "R64ui"; + case SpvImageFormatR64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* SpvImageChannelOrderToString(SpvImageChannelOrder value) { + switch (value) { + case SpvImageChannelOrderR: return "R"; + case SpvImageChannelOrderA: return "A"; + case SpvImageChannelOrderRG: return "RG"; + case SpvImageChannelOrderRA: return "RA"; + case SpvImageChannelOrderRGB: return "RGB"; + case SpvImageChannelOrderRGBA: return "RGBA"; + case SpvImageChannelOrderBGRA: return "BGRA"; + case SpvImageChannelOrderARGB: return "ARGB"; + case SpvImageChannelOrderIntensity: return "Intensity"; + case SpvImageChannelOrderLuminance: return "Luminance"; + case SpvImageChannelOrderRx: return "Rx"; + case SpvImageChannelOrderRGx: return "RGx"; + case SpvImageChannelOrderRGBx: return "RGBx"; + case SpvImageChannelOrderDepth: return "Depth"; + case SpvImageChannelOrderDepthStencil: return "DepthStencil"; + case SpvImageChannelOrdersRGB: return "sRGB"; + case SpvImageChannelOrdersRGBx: return "sRGBx"; + case SpvImageChannelOrdersRGBA: return "sRGBA"; + case SpvImageChannelOrdersBGRA: return "sBGRA"; + case SpvImageChannelOrderABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* SpvImageChannelDataTypeToString(SpvImageChannelDataType value) { + switch (value) { + case SpvImageChannelDataTypeSnormInt8: return "SnormInt8"; + case SpvImageChannelDataTypeSnormInt16: return "SnormInt16"; + case SpvImageChannelDataTypeUnormInt8: return "UnormInt8"; + case SpvImageChannelDataTypeUnormInt16: return "UnormInt16"; + case SpvImageChannelDataTypeUnormShort565: return "UnormShort565"; + case SpvImageChannelDataTypeUnormShort555: return "UnormShort555"; + case SpvImageChannelDataTypeUnormInt101010: return "UnormInt101010"; + case SpvImageChannelDataTypeSignedInt8: return "SignedInt8"; + case SpvImageChannelDataTypeSignedInt16: return "SignedInt16"; + case SpvImageChannelDataTypeSignedInt32: return "SignedInt32"; + case SpvImageChannelDataTypeUnsignedInt8: return "UnsignedInt8"; + case SpvImageChannelDataTypeUnsignedInt16: return "UnsignedInt16"; + case SpvImageChannelDataTypeUnsignedInt32: return "UnsignedInt32"; + case SpvImageChannelDataTypeHalfFloat: return "HalfFloat"; + case SpvImageChannelDataTypeFloat: return "Float"; + case SpvImageChannelDataTypeUnormInt24: return "UnormInt24"; + case SpvImageChannelDataTypeUnormInt101010_2: return "UnormInt101010_2"; + case SpvImageChannelDataTypeUnormInt10X6EXT: return "UnormInt10X6EXT"; + case SpvImageChannelDataTypeUnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case SpvImageChannelDataTypeUnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case SpvImageChannelDataTypeUnormInt2_101010EXT: return "UnormInt2_101010EXT"; + case SpvImageChannelDataTypeUnsignedInt10X6EXT: return "UnsignedInt10X6EXT"; + case SpvImageChannelDataTypeUnsignedInt12X4EXT: return "UnsignedInt12X4EXT"; + case SpvImageChannelDataTypeUnsignedInt14X2EXT: return "UnsignedInt14X2EXT"; + case SpvImageChannelDataTypeUnormInt12X4EXT: return "UnormInt12X4EXT"; + case SpvImageChannelDataTypeUnormInt14X2EXT: return "UnormInt14X2EXT"; + default: return "Unknown"; + } +} + +inline const char* SpvFPRoundingModeToString(SpvFPRoundingMode value) { + switch (value) { + case SpvFPRoundingModeRTE: return "RTE"; + case SpvFPRoundingModeRTZ: return "RTZ"; + case SpvFPRoundingModeRTP: return "RTP"; + case SpvFPRoundingModeRTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* SpvLinkageTypeToString(SpvLinkageType value) { + switch (value) { + case SpvLinkageTypeExport: return "Export"; + case SpvLinkageTypeImport: return "Import"; + case SpvLinkageTypeLinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* SpvAccessQualifierToString(SpvAccessQualifier value) { + switch (value) { + case SpvAccessQualifierReadOnly: return "ReadOnly"; + case SpvAccessQualifierWriteOnly: return "WriteOnly"; + case SpvAccessQualifierReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* SpvFunctionParameterAttributeToString(SpvFunctionParameterAttribute value) { + switch (value) { + case SpvFunctionParameterAttributeZext: return "Zext"; + case SpvFunctionParameterAttributeSext: return "Sext"; + case SpvFunctionParameterAttributeByVal: return "ByVal"; + case SpvFunctionParameterAttributeSret: return "Sret"; + case SpvFunctionParameterAttributeNoAlias: return "NoAlias"; + case SpvFunctionParameterAttributeNoCapture: return "NoCapture"; + case SpvFunctionParameterAttributeNoWrite: return "NoWrite"; + case SpvFunctionParameterAttributeNoReadWrite: return "NoReadWrite"; + case SpvFunctionParameterAttributeRuntimeAlignedINTEL: return "RuntimeAlignedINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvDecorationToString(SpvDecoration value) { + switch (value) { + case SpvDecorationRelaxedPrecision: return "RelaxedPrecision"; + case SpvDecorationSpecId: return "SpecId"; + case SpvDecorationBlock: return "Block"; + case SpvDecorationBufferBlock: return "BufferBlock"; + case SpvDecorationRowMajor: return "RowMajor"; + case SpvDecorationColMajor: return "ColMajor"; + case SpvDecorationArrayStride: return "ArrayStride"; + case SpvDecorationMatrixStride: return "MatrixStride"; + case SpvDecorationGLSLShared: return "GLSLShared"; + case SpvDecorationGLSLPacked: return "GLSLPacked"; + case SpvDecorationCPacked: return "CPacked"; + case SpvDecorationBuiltIn: return "BuiltIn"; + case SpvDecorationNoPerspective: return "NoPerspective"; + case SpvDecorationFlat: return "Flat"; + case SpvDecorationPatch: return "Patch"; + case SpvDecorationCentroid: return "Centroid"; + case SpvDecorationSample: return "Sample"; + case SpvDecorationInvariant: return "Invariant"; + case SpvDecorationRestrict: return "Restrict"; + case SpvDecorationAliased: return "Aliased"; + case SpvDecorationVolatile: return "Volatile"; + case SpvDecorationConstant: return "Constant"; + case SpvDecorationCoherent: return "Coherent"; + case SpvDecorationNonWritable: return "NonWritable"; + case SpvDecorationNonReadable: return "NonReadable"; + case SpvDecorationUniform: return "Uniform"; + case SpvDecorationUniformId: return "UniformId"; + case SpvDecorationSaturatedConversion: return "SaturatedConversion"; + case SpvDecorationStream: return "Stream"; + case SpvDecorationLocation: return "Location"; + case SpvDecorationComponent: return "Component"; + case SpvDecorationIndex: return "Index"; + case SpvDecorationBinding: return "Binding"; + case SpvDecorationDescriptorSet: return "DescriptorSet"; + case SpvDecorationOffset: return "Offset"; + case SpvDecorationXfbBuffer: return "XfbBuffer"; + case SpvDecorationXfbStride: return "XfbStride"; + case SpvDecorationFuncParamAttr: return "FuncParamAttr"; + case SpvDecorationFPRoundingMode: return "FPRoundingMode"; + case SpvDecorationFPFastMathMode: return "FPFastMathMode"; + case SpvDecorationLinkageAttributes: return "LinkageAttributes"; + case SpvDecorationNoContraction: return "NoContraction"; + case SpvDecorationInputAttachmentIndex: return "InputAttachmentIndex"; + case SpvDecorationAlignment: return "Alignment"; + case SpvDecorationMaxByteOffset: return "MaxByteOffset"; + case SpvDecorationAlignmentId: return "AlignmentId"; + case SpvDecorationMaxByteOffsetId: return "MaxByteOffsetId"; + case SpvDecorationNoSignedWrap: return "NoSignedWrap"; + case SpvDecorationNoUnsignedWrap: return "NoUnsignedWrap"; + case SpvDecorationWeightTextureQCOM: return "WeightTextureQCOM"; + case SpvDecorationBlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case SpvDecorationBlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case SpvDecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case SpvDecorationNodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case SpvDecorationNodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case SpvDecorationTrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case SpvDecorationPayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case SpvDecorationPayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case SpvDecorationPayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case SpvDecorationPayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case SpvDecorationPayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case SpvDecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case SpvDecorationPassthroughNV: return "PassthroughNV"; + case SpvDecorationViewportRelativeNV: return "ViewportRelativeNV"; + case SpvDecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case SpvDecorationPerPrimitiveEXT: return "PerPrimitiveEXT"; + case SpvDecorationPerViewNV: return "PerViewNV"; + case SpvDecorationPerTaskNV: return "PerTaskNV"; + case SpvDecorationPerVertexKHR: return "PerVertexKHR"; + case SpvDecorationNonUniform: return "NonUniform"; + case SpvDecorationRestrictPointer: return "RestrictPointer"; + case SpvDecorationAliasedPointer: return "AliasedPointer"; + case SpvDecorationHitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case SpvDecorationBindlessSamplerNV: return "BindlessSamplerNV"; + case SpvDecorationBindlessImageNV: return "BindlessImageNV"; + case SpvDecorationBoundSamplerNV: return "BoundSamplerNV"; + case SpvDecorationBoundImageNV: return "BoundImageNV"; + case SpvDecorationSIMTCallINTEL: return "SIMTCallINTEL"; + case SpvDecorationReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case SpvDecorationClobberINTEL: return "ClobberINTEL"; + case SpvDecorationSideEffectsINTEL: return "SideEffectsINTEL"; + case SpvDecorationVectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case SpvDecorationFuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case SpvDecorationVectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case SpvDecorationStackCallINTEL: return "StackCallINTEL"; + case SpvDecorationGlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case SpvDecorationCounterBuffer: return "CounterBuffer"; + case SpvDecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case SpvDecorationUserTypeGOOGLE: return "UserTypeGOOGLE"; + case SpvDecorationFunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case SpvDecorationFunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case SpvDecorationRegisterINTEL: return "RegisterINTEL"; + case SpvDecorationMemoryINTEL: return "MemoryINTEL"; + case SpvDecorationNumbanksINTEL: return "NumbanksINTEL"; + case SpvDecorationBankwidthINTEL: return "BankwidthINTEL"; + case SpvDecorationMaxPrivateCopiesINTEL: return "MaxPrivateCopiesINTEL"; + case SpvDecorationSinglepumpINTEL: return "SinglepumpINTEL"; + case SpvDecorationDoublepumpINTEL: return "DoublepumpINTEL"; + case SpvDecorationMaxReplicatesINTEL: return "MaxReplicatesINTEL"; + case SpvDecorationSimpleDualPortINTEL: return "SimpleDualPortINTEL"; + case SpvDecorationMergeINTEL: return "MergeINTEL"; + case SpvDecorationBankBitsINTEL: return "BankBitsINTEL"; + case SpvDecorationForcePow2DepthINTEL: return "ForcePow2DepthINTEL"; + case SpvDecorationStridesizeINTEL: return "StridesizeINTEL"; + case SpvDecorationWordsizeINTEL: return "WordsizeINTEL"; + case SpvDecorationTrueDualPortINTEL: return "TrueDualPortINTEL"; + case SpvDecorationBurstCoalesceINTEL: return "BurstCoalesceINTEL"; + case SpvDecorationCacheSizeINTEL: return "CacheSizeINTEL"; + case SpvDecorationDontStaticallyCoalesceINTEL: return "DontStaticallyCoalesceINTEL"; + case SpvDecorationPrefetchINTEL: return "PrefetchINTEL"; + case SpvDecorationStallEnableINTEL: return "StallEnableINTEL"; + case SpvDecorationFuseLoopsInFunctionINTEL: return "FuseLoopsInFunctionINTEL"; + case SpvDecorationMathOpDSPModeINTEL: return "MathOpDSPModeINTEL"; + case SpvDecorationAliasScopeINTEL: return "AliasScopeINTEL"; + case SpvDecorationNoAliasINTEL: return "NoAliasINTEL"; + case SpvDecorationInitiationIntervalINTEL: return "InitiationIntervalINTEL"; + case SpvDecorationMaxConcurrencyINTEL: return "MaxConcurrencyINTEL"; + case SpvDecorationPipelineEnableINTEL: return "PipelineEnableINTEL"; + case SpvDecorationBufferLocationINTEL: return "BufferLocationINTEL"; + case SpvDecorationIOPipeStorageINTEL: return "IOPipeStorageINTEL"; + case SpvDecorationFunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case SpvDecorationSingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case SpvDecorationVectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case SpvDecorationMediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case SpvDecorationStallFreeINTEL: return "StallFreeINTEL"; + case SpvDecorationFPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case SpvDecorationLatencyControlLabelINTEL: return "LatencyControlLabelINTEL"; + case SpvDecorationLatencyControlConstraintINTEL: return "LatencyControlConstraintINTEL"; + case SpvDecorationConduitKernelArgumentINTEL: return "ConduitKernelArgumentINTEL"; + case SpvDecorationRegisterMapKernelArgumentINTEL: return "RegisterMapKernelArgumentINTEL"; + case SpvDecorationMMHostInterfaceAddressWidthINTEL: return "MMHostInterfaceAddressWidthINTEL"; + case SpvDecorationMMHostInterfaceDataWidthINTEL: return "MMHostInterfaceDataWidthINTEL"; + case SpvDecorationMMHostInterfaceLatencyINTEL: return "MMHostInterfaceLatencyINTEL"; + case SpvDecorationMMHostInterfaceReadWriteModeINTEL: return "MMHostInterfaceReadWriteModeINTEL"; + case SpvDecorationMMHostInterfaceMaxBurstINTEL: return "MMHostInterfaceMaxBurstINTEL"; + case SpvDecorationMMHostInterfaceWaitRequestINTEL: return "MMHostInterfaceWaitRequestINTEL"; + case SpvDecorationStableKernelArgumentINTEL: return "StableKernelArgumentINTEL"; + case SpvDecorationHostAccessINTEL: return "HostAccessINTEL"; + case SpvDecorationInitModeINTEL: return "InitModeINTEL"; + case SpvDecorationImplementInRegisterMapINTEL: return "ImplementInRegisterMapINTEL"; + case SpvDecorationCacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case SpvDecorationCacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvBuiltInToString(SpvBuiltIn value) { + switch (value) { + case SpvBuiltInPosition: return "Position"; + case SpvBuiltInPointSize: return "PointSize"; + case SpvBuiltInClipDistance: return "ClipDistance"; + case SpvBuiltInCullDistance: return "CullDistance"; + case SpvBuiltInVertexId: return "VertexId"; + case SpvBuiltInInstanceId: return "InstanceId"; + case SpvBuiltInPrimitiveId: return "PrimitiveId"; + case SpvBuiltInInvocationId: return "InvocationId"; + case SpvBuiltInLayer: return "Layer"; + case SpvBuiltInViewportIndex: return "ViewportIndex"; + case SpvBuiltInTessLevelOuter: return "TessLevelOuter"; + case SpvBuiltInTessLevelInner: return "TessLevelInner"; + case SpvBuiltInTessCoord: return "TessCoord"; + case SpvBuiltInPatchVertices: return "PatchVertices"; + case SpvBuiltInFragCoord: return "FragCoord"; + case SpvBuiltInPointCoord: return "PointCoord"; + case SpvBuiltInFrontFacing: return "FrontFacing"; + case SpvBuiltInSampleId: return "SampleId"; + case SpvBuiltInSamplePosition: return "SamplePosition"; + case SpvBuiltInSampleMask: return "SampleMask"; + case SpvBuiltInFragDepth: return "FragDepth"; + case SpvBuiltInHelperInvocation: return "HelperInvocation"; + case SpvBuiltInNumWorkgroups: return "NumWorkgroups"; + case SpvBuiltInWorkgroupSize: return "WorkgroupSize"; + case SpvBuiltInWorkgroupId: return "WorkgroupId"; + case SpvBuiltInLocalInvocationId: return "LocalInvocationId"; + case SpvBuiltInGlobalInvocationId: return "GlobalInvocationId"; + case SpvBuiltInLocalInvocationIndex: return "LocalInvocationIndex"; + case SpvBuiltInWorkDim: return "WorkDim"; + case SpvBuiltInGlobalSize: return "GlobalSize"; + case SpvBuiltInEnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case SpvBuiltInGlobalOffset: return "GlobalOffset"; + case SpvBuiltInGlobalLinearId: return "GlobalLinearId"; + case SpvBuiltInSubgroupSize: return "SubgroupSize"; + case SpvBuiltInSubgroupMaxSize: return "SubgroupMaxSize"; + case SpvBuiltInNumSubgroups: return "NumSubgroups"; + case SpvBuiltInNumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case SpvBuiltInSubgroupId: return "SubgroupId"; + case SpvBuiltInSubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case SpvBuiltInVertexIndex: return "VertexIndex"; + case SpvBuiltInInstanceIndex: return "InstanceIndex"; + case SpvBuiltInCoreIDARM: return "CoreIDARM"; + case SpvBuiltInCoreCountARM: return "CoreCountARM"; + case SpvBuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case SpvBuiltInWarpIDARM: return "WarpIDARM"; + case SpvBuiltInWarpMaxIDARM: return "WarpMaxIDARM"; + case SpvBuiltInSubgroupEqMask: return "SubgroupEqMask"; + case SpvBuiltInSubgroupGeMask: return "SubgroupGeMask"; + case SpvBuiltInSubgroupGtMask: return "SubgroupGtMask"; + case SpvBuiltInSubgroupLeMask: return "SubgroupLeMask"; + case SpvBuiltInSubgroupLtMask: return "SubgroupLtMask"; + case SpvBuiltInBaseVertex: return "BaseVertex"; + case SpvBuiltInBaseInstance: return "BaseInstance"; + case SpvBuiltInDrawIndex: return "DrawIndex"; + case SpvBuiltInPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case SpvBuiltInDeviceIndex: return "DeviceIndex"; + case SpvBuiltInViewIndex: return "ViewIndex"; + case SpvBuiltInShadingRateKHR: return "ShadingRateKHR"; + case SpvBuiltInTileOffsetQCOM: return "TileOffsetQCOM"; + case SpvBuiltInTileDimensionQCOM: return "TileDimensionQCOM"; + case SpvBuiltInTileApronSizeQCOM: return "TileApronSizeQCOM"; + case SpvBuiltInBaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case SpvBuiltInBaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case SpvBuiltInBaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case SpvBuiltInBaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case SpvBuiltInBaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case SpvBuiltInBaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case SpvBuiltInBaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case SpvBuiltInFragStencilRefEXT: return "FragStencilRefEXT"; + case SpvBuiltInRemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case SpvBuiltInShaderIndexAMDX: return "ShaderIndexAMDX"; + case SpvBuiltInViewportMaskNV: return "ViewportMaskNV"; + case SpvBuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case SpvBuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case SpvBuiltInPositionPerViewNV: return "PositionPerViewNV"; + case SpvBuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case SpvBuiltInFullyCoveredEXT: return "FullyCoveredEXT"; + case SpvBuiltInTaskCountNV: return "TaskCountNV"; + case SpvBuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case SpvBuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case SpvBuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case SpvBuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case SpvBuiltInLayerPerViewNV: return "LayerPerViewNV"; + case SpvBuiltInMeshViewCountNV: return "MeshViewCountNV"; + case SpvBuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case SpvBuiltInBaryCoordKHR: return "BaryCoordKHR"; + case SpvBuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case SpvBuiltInFragSizeEXT: return "FragSizeEXT"; + case SpvBuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + case SpvBuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case SpvBuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case SpvBuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case SpvBuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case SpvBuiltInLaunchIdKHR: return "LaunchIdKHR"; + case SpvBuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case SpvBuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case SpvBuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case SpvBuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case SpvBuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case SpvBuiltInRayTminKHR: return "RayTminKHR"; + case SpvBuiltInRayTmaxKHR: return "RayTmaxKHR"; + case SpvBuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case SpvBuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case SpvBuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case SpvBuiltInHitTNV: return "HitTNV"; + case SpvBuiltInHitKindKHR: return "HitKindKHR"; + case SpvBuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case SpvBuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case SpvBuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case SpvBuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case SpvBuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case SpvBuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case SpvBuiltInHitIsSphereNV: return "HitIsSphereNV"; + case SpvBuiltInHitIsLSSNV: return "HitIsLSSNV"; + case SpvBuiltInHitSpherePositionNV: return "HitSpherePositionNV"; + case SpvBuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case SpvBuiltInSMCountNV: return "SMCountNV"; + case SpvBuiltInWarpIDNV: return "WarpIDNV"; + case SpvBuiltInSMIDNV: return "SMIDNV"; + case SpvBuiltInHitLSSPositionsNV: return "HitLSSPositionsNV"; + case SpvBuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case SpvBuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case SpvBuiltInHitSphereRadiusNV: return "HitSphereRadiusNV"; + case SpvBuiltInHitLSSRadiiNV: return "HitLSSRadiiNV"; + case SpvBuiltInClusterIDNV: return "ClusterIDNV"; + case SpvBuiltInCullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvScopeToString(SpvScope value) { + switch (value) { + case SpvScopeCrossDevice: return "CrossDevice"; + case SpvScopeDevice: return "Device"; + case SpvScopeWorkgroup: return "Workgroup"; + case SpvScopeSubgroup: return "Subgroup"; + case SpvScopeInvocation: return "Invocation"; + case SpvScopeQueueFamily: return "QueueFamily"; + case SpvScopeShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvGroupOperationToString(SpvGroupOperation value) { + switch (value) { + case SpvGroupOperationReduce: return "Reduce"; + case SpvGroupOperationInclusiveScan: return "InclusiveScan"; + case SpvGroupOperationExclusiveScan: return "ExclusiveScan"; + case SpvGroupOperationClusteredReduce: return "ClusteredReduce"; + case SpvGroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case SpvGroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case SpvGroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + default: return "Unknown"; + } +} + +inline const char* SpvKernelEnqueueFlagsToString(SpvKernelEnqueueFlags value) { + switch (value) { + case SpvKernelEnqueueFlagsNoWait: return "NoWait"; + case SpvKernelEnqueueFlagsWaitKernel: return "WaitKernel"; + case SpvKernelEnqueueFlagsWaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* SpvCapabilityToString(SpvCapability value) { + switch (value) { + case SpvCapabilityMatrix: return "Matrix"; + case SpvCapabilityShader: return "Shader"; + case SpvCapabilityGeometry: return "Geometry"; + case SpvCapabilityTessellation: return "Tessellation"; + case SpvCapabilityAddresses: return "Addresses"; + case SpvCapabilityLinkage: return "Linkage"; + case SpvCapabilityKernel: return "Kernel"; + case SpvCapabilityVector16: return "Vector16"; + case SpvCapabilityFloat16Buffer: return "Float16Buffer"; + case SpvCapabilityFloat16: return "Float16"; + case SpvCapabilityFloat64: return "Float64"; + case SpvCapabilityInt64: return "Int64"; + case SpvCapabilityInt64Atomics: return "Int64Atomics"; + case SpvCapabilityImageBasic: return "ImageBasic"; + case SpvCapabilityImageReadWrite: return "ImageReadWrite"; + case SpvCapabilityImageMipmap: return "ImageMipmap"; + case SpvCapabilityPipes: return "Pipes"; + case SpvCapabilityGroups: return "Groups"; + case SpvCapabilityDeviceEnqueue: return "DeviceEnqueue"; + case SpvCapabilityLiteralSampler: return "LiteralSampler"; + case SpvCapabilityAtomicStorage: return "AtomicStorage"; + case SpvCapabilityInt16: return "Int16"; + case SpvCapabilityTessellationPointSize: return "TessellationPointSize"; + case SpvCapabilityGeometryPointSize: return "GeometryPointSize"; + case SpvCapabilityImageGatherExtended: return "ImageGatherExtended"; + case SpvCapabilityStorageImageMultisample: return "StorageImageMultisample"; + case SpvCapabilityUniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case SpvCapabilitySampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case SpvCapabilityStorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case SpvCapabilityStorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case SpvCapabilityClipDistance: return "ClipDistance"; + case SpvCapabilityCullDistance: return "CullDistance"; + case SpvCapabilityImageCubeArray: return "ImageCubeArray"; + case SpvCapabilitySampleRateShading: return "SampleRateShading"; + case SpvCapabilityImageRect: return "ImageRect"; + case SpvCapabilitySampledRect: return "SampledRect"; + case SpvCapabilityGenericPointer: return "GenericPointer"; + case SpvCapabilityInt8: return "Int8"; + case SpvCapabilityInputAttachment: return "InputAttachment"; + case SpvCapabilitySparseResidency: return "SparseResidency"; + case SpvCapabilityMinLod: return "MinLod"; + case SpvCapabilitySampled1D: return "Sampled1D"; + case SpvCapabilityImage1D: return "Image1D"; + case SpvCapabilitySampledCubeArray: return "SampledCubeArray"; + case SpvCapabilitySampledBuffer: return "SampledBuffer"; + case SpvCapabilityImageBuffer: return "ImageBuffer"; + case SpvCapabilityImageMSArray: return "ImageMSArray"; + case SpvCapabilityStorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case SpvCapabilityImageQuery: return "ImageQuery"; + case SpvCapabilityDerivativeControl: return "DerivativeControl"; + case SpvCapabilityInterpolationFunction: return "InterpolationFunction"; + case SpvCapabilityTransformFeedback: return "TransformFeedback"; + case SpvCapabilityGeometryStreams: return "GeometryStreams"; + case SpvCapabilityStorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case SpvCapabilityStorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case SpvCapabilityMultiViewport: return "MultiViewport"; + case SpvCapabilitySubgroupDispatch: return "SubgroupDispatch"; + case SpvCapabilityNamedBarrier: return "NamedBarrier"; + case SpvCapabilityPipeStorage: return "PipeStorage"; + case SpvCapabilityGroupNonUniform: return "GroupNonUniform"; + case SpvCapabilityGroupNonUniformVote: return "GroupNonUniformVote"; + case SpvCapabilityGroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case SpvCapabilityGroupNonUniformBallot: return "GroupNonUniformBallot"; + case SpvCapabilityGroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case SpvCapabilityGroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case SpvCapabilityGroupNonUniformClustered: return "GroupNonUniformClustered"; + case SpvCapabilityGroupNonUniformQuad: return "GroupNonUniformQuad"; + case SpvCapabilityShaderLayer: return "ShaderLayer"; + case SpvCapabilityShaderViewportIndex: return "ShaderViewportIndex"; + case SpvCapabilityUniformDecoration: return "UniformDecoration"; + case SpvCapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + case SpvCapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case SpvCapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case SpvCapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case SpvCapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case SpvCapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case SpvCapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case SpvCapabilityDrawParameters: return "DrawParameters"; + case SpvCapabilityWorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case SpvCapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case SpvCapabilityStorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case SpvCapabilityStorageUniform16: return "StorageUniform16"; + case SpvCapabilityStoragePushConstant16: return "StoragePushConstant16"; + case SpvCapabilityStorageInputOutput16: return "StorageInputOutput16"; + case SpvCapabilityDeviceGroup: return "DeviceGroup"; + case SpvCapabilityMultiView: return "MultiView"; + case SpvCapabilityVariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case SpvCapabilityVariablePointers: return "VariablePointers"; + case SpvCapabilityAtomicStorageOps: return "AtomicStorageOps"; + case SpvCapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case SpvCapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case SpvCapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case SpvCapabilityStoragePushConstant8: return "StoragePushConstant8"; + case SpvCapabilityDenormPreserve: return "DenormPreserve"; + case SpvCapabilityDenormFlushToZero: return "DenormFlushToZero"; + case SpvCapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case SpvCapabilityRoundingModeRTE: return "RoundingModeRTE"; + case SpvCapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + case SpvCapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case SpvCapabilityRayQueryKHR: return "RayQueryKHR"; + case SpvCapabilityUntypedPointersKHR: return "UntypedPointersKHR"; + case SpvCapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case SpvCapabilityRayTracingKHR: return "RayTracingKHR"; + case SpvCapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case SpvCapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case SpvCapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case SpvCapabilityTileShadingQCOM: return "TileShadingQCOM"; + case SpvCapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case SpvCapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case SpvCapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case SpvCapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case SpvCapabilityStencilExportEXT: return "StencilExportEXT"; + case SpvCapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case SpvCapabilityInt64ImageEXT: return "Int64ImageEXT"; + case SpvCapabilityShaderClockKHR: return "ShaderClockKHR"; + case SpvCapabilityShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case SpvCapabilityQuadControlKHR: return "QuadControlKHR"; + case SpvCapabilityBFloat16TypeKHR: return "BFloat16TypeKHR"; + case SpvCapabilityBFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case SpvCapabilityBFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case SpvCapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case SpvCapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case SpvCapabilityShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case SpvCapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case SpvCapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case SpvCapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case SpvCapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case SpvCapabilityMeshShadingNV: return "MeshShadingNV"; + case SpvCapabilityImageFootprintNV: return "ImageFootprintNV"; + case SpvCapabilityMeshShadingEXT: return "MeshShadingEXT"; + case SpvCapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case SpvCapabilityComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case SpvCapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + case SpvCapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case SpvCapabilityShaderNonUniform: return "ShaderNonUniform"; + case SpvCapabilityRuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case SpvCapabilityInputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case SpvCapabilityUniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case SpvCapabilityStorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case SpvCapabilityUniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case SpvCapabilitySampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case SpvCapabilityStorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case SpvCapabilityStorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case SpvCapabilityInputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case SpvCapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case SpvCapabilityRayTracingNV: return "RayTracingNV"; + case SpvCapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case SpvCapabilityVulkanMemoryModel: return "VulkanMemoryModel"; + case SpvCapabilityVulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case SpvCapabilityPhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case SpvCapabilityComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case SpvCapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case SpvCapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case SpvCapabilityFragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case SpvCapabilityFragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case SpvCapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case SpvCapabilityFragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case SpvCapabilityDemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case SpvCapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case SpvCapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case SpvCapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case SpvCapabilityBindlessTextureNV: return "BindlessTextureNV"; + case SpvCapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case SpvCapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case SpvCapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case SpvCapabilityRayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case SpvCapabilityRawAccessChainsNV: return "RawAccessChainsNV"; + case SpvCapabilityRayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case SpvCapabilityRayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + case SpvCapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case SpvCapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case SpvCapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case SpvCapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case SpvCapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case SpvCapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case SpvCapabilityRayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + case SpvCapabilityTensorAddressingNV: return "TensorAddressingNV"; + case SpvCapabilitySubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case SpvCapabilitySubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case SpvCapabilitySubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case SpvCapabilitySubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case SpvCapabilityRoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case SpvCapabilityFloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case SpvCapabilityIntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case SpvCapabilityFunctionPointersINTEL: return "FunctionPointersINTEL"; + case SpvCapabilityIndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case SpvCapabilityAsmINTEL: return "AsmINTEL"; + case SpvCapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case SpvCapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case SpvCapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case SpvCapabilityVectorComputeINTEL: return "VectorComputeINTEL"; + case SpvCapabilityVectorAnyINTEL: return "VectorAnyINTEL"; + case SpvCapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + case SpvCapabilitySubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case SpvCapabilityVariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case SpvCapabilityFunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case SpvCapabilityFPGAMemoryAttributesINTEL: return "FPGAMemoryAttributesINTEL"; + case SpvCapabilityFPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case SpvCapabilityArbitraryPrecisionIntegersINTEL: return "ArbitraryPrecisionIntegersINTEL"; + case SpvCapabilityArbitraryPrecisionFloatingPointINTEL: return "ArbitraryPrecisionFloatingPointINTEL"; + case SpvCapabilityUnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case SpvCapabilityFPGALoopControlsINTEL: return "FPGALoopControlsINTEL"; + case SpvCapabilityKernelAttributesINTEL: return "KernelAttributesINTEL"; + case SpvCapabilityFPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case SpvCapabilityFPGAMemoryAccessesINTEL: return "FPGAMemoryAccessesINTEL"; + case SpvCapabilityFPGAClusterAttributesINTEL: return "FPGAClusterAttributesINTEL"; + case SpvCapabilityLoopFuseINTEL: return "LoopFuseINTEL"; + case SpvCapabilityFPGADSPControlINTEL: return "FPGADSPControlINTEL"; + case SpvCapabilityMemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case SpvCapabilityFPGAInvocationPipeliningAttributesINTEL: return "FPGAInvocationPipeliningAttributesINTEL"; + case SpvCapabilityFPGABufferLocationINTEL: return "FPGABufferLocationINTEL"; + case SpvCapabilityArbitraryPrecisionFixedPointINTEL: return "ArbitraryPrecisionFixedPointINTEL"; + case SpvCapabilityUSMStorageClassesINTEL: return "USMStorageClassesINTEL"; + case SpvCapabilityRuntimeAlignedAttributeINTEL: return "RuntimeAlignedAttributeINTEL"; + case SpvCapabilityIOPipesINTEL: return "IOPipesINTEL"; + case SpvCapabilityBlockingPipesINTEL: return "BlockingPipesINTEL"; + case SpvCapabilityFPGARegINTEL: return "FPGARegINTEL"; + case SpvCapabilityDotProductInputAll: return "DotProductInputAll"; + case SpvCapabilityDotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case SpvCapabilityDotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case SpvCapabilityDotProduct: return "DotProduct"; + case SpvCapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case SpvCapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case SpvCapabilityReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case SpvCapabilityBitInstructions: return "BitInstructions"; + case SpvCapabilityGroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case SpvCapabilityFloatControls2: return "FloatControls2"; + case SpvCapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case SpvCapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case SpvCapabilityLongCompositesINTEL: return "LongCompositesINTEL"; + case SpvCapabilityOptNoneEXT: return "OptNoneEXT"; + case SpvCapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case SpvCapabilityDebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case SpvCapabilityBFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case SpvCapabilitySplitBarrierINTEL: return "SplitBarrierINTEL"; + case SpvCapabilityArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case SpvCapabilityFPGAClusterAttributesV2INTEL: return "FPGAClusterAttributesV2INTEL"; + case SpvCapabilityFPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case SpvCapabilityTaskSequenceINTEL: return "TaskSequenceINTEL"; + case SpvCapabilityFPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case SpvCapabilityFPGALatencyControlINTEL: return "FPGALatencyControlINTEL"; + case SpvCapabilityFPGAArgumentInterfacesINTEL: return "FPGAArgumentInterfacesINTEL"; + case SpvCapabilityGlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case SpvCapabilityGlobalVariableFPGADecorationsINTEL: return "GlobalVariableFPGADecorationsINTEL"; + case SpvCapabilitySubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case SpvCapabilitySubgroup2DBlockIOINTEL: return "Subgroup2DBlockIOINTEL"; + case SpvCapabilitySubgroup2DBlockTransformINTEL: return "Subgroup2DBlockTransformINTEL"; + case SpvCapabilitySubgroup2DBlockTransposeINTEL: return "Subgroup2DBlockTransposeINTEL"; + case SpvCapabilitySubgroupMatrixMultiplyAccumulateINTEL: return "SubgroupMatrixMultiplyAccumulateINTEL"; + case SpvCapabilityTernaryBitwiseFunctionINTEL: return "TernaryBitwiseFunctionINTEL"; + case SpvCapabilityGroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case SpvCapabilityTensorFloat32RoundingINTEL: return "TensorFloat32RoundingINTEL"; + case SpvCapabilityMaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case SpvCapabilityCacheControlsINTEL: return "CacheControlsINTEL"; + case SpvCapabilityRegisterLimitsINTEL: return "RegisterLimitsINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryIntersectionToString(SpvRayQueryIntersection value) { + switch (value) { + case SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryCommittedIntersectionTypeToString(SpvRayQueryCommittedIntersectionType value) { + switch (value) { + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryCandidateIntersectionTypeToString(SpvRayQueryCandidateIntersectionType value) { + switch (value) { + case SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvFPDenormModeToString(SpvFPDenormMode value) { + switch (value) { + case SpvFPDenormModePreserve: return "Preserve"; + case SpvFPDenormModeFlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* SpvFPOperationModeToString(SpvFPOperationMode value) { + switch (value) { + case SpvFPOperationModeIEEE: return "IEEE"; + case SpvFPOperationModeALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* SpvQuantizationModesToString(SpvQuantizationModes value) { + switch (value) { + case SpvQuantizationModesTRN: return "TRN"; + case SpvQuantizationModesTRN_ZERO: return "TRN_ZERO"; + case SpvQuantizationModesRND: return "RND"; + case SpvQuantizationModesRND_ZERO: return "RND_ZERO"; + case SpvQuantizationModesRND_INF: return "RND_INF"; + case SpvQuantizationModesRND_MIN_INF: return "RND_MIN_INF"; + case SpvQuantizationModesRND_CONV: return "RND_CONV"; + case SpvQuantizationModesRND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* SpvOverflowModesToString(SpvOverflowModes value) { + switch (value) { + case SpvOverflowModesWRAP: return "WRAP"; + case SpvOverflowModesSAT: return "SAT"; + case SpvOverflowModesSAT_ZERO: return "SAT_ZERO"; + case SpvOverflowModesSAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* SpvPackedVectorFormatToString(SpvPackedVectorFormat value) { + switch (value) { + case SpvPackedVectorFormatPackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeMatrixLayoutToString(SpvCooperativeMatrixLayout value) { + switch (value) { + case SpvCooperativeMatrixLayoutRowMajorKHR: return "RowMajorKHR"; + case SpvCooperativeMatrixLayoutColumnMajorKHR: return "ColumnMajorKHR"; + case SpvCooperativeMatrixLayoutRowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case SpvCooperativeMatrixLayoutColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeMatrixUseToString(SpvCooperativeMatrixUse value) { + switch (value) { + case SpvCooperativeMatrixUseMatrixAKHR: return "MatrixAKHR"; + case SpvCooperativeMatrixUseMatrixBKHR: return "MatrixBKHR"; + case SpvCooperativeMatrixUseMatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvTensorClampModeToString(SpvTensorClampMode value) { + switch (value) { + case SpvTensorClampModeUndefined: return "Undefined"; + case SpvTensorClampModeConstant: return "Constant"; + case SpvTensorClampModeClampToEdge: return "ClampToEdge"; + case SpvTensorClampModeRepeat: return "Repeat"; + case SpvTensorClampModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SpvInitializationModeQualifierToString(SpvInitializationModeQualifier value) { + switch (value) { + case SpvInitializationModeQualifierInitOnDeviceReprogramINTEL: return "InitOnDeviceReprogramINTEL"; + case SpvInitializationModeQualifierInitOnDeviceResetINTEL: return "InitOnDeviceResetINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvHostAccessQualifierToString(SpvHostAccessQualifier value) { + switch (value) { + case SpvHostAccessQualifierNoneINTEL: return "NoneINTEL"; + case SpvHostAccessQualifierReadINTEL: return "ReadINTEL"; + case SpvHostAccessQualifierWriteINTEL: return "WriteINTEL"; + case SpvHostAccessQualifierReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvLoadCacheControlToString(SpvLoadCacheControl value) { + switch (value) { + case SpvLoadCacheControlUncachedINTEL: return "UncachedINTEL"; + case SpvLoadCacheControlCachedINTEL: return "CachedINTEL"; + case SpvLoadCacheControlStreamingINTEL: return "StreamingINTEL"; + case SpvLoadCacheControlInvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case SpvLoadCacheControlConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvStoreCacheControlToString(SpvStoreCacheControl value) { + switch (value) { + case SpvStoreCacheControlUncachedINTEL: return "UncachedINTEL"; + case SpvStoreCacheControlWriteThroughINTEL: return "WriteThroughINTEL"; + case SpvStoreCacheControlWriteBackINTEL: return "WriteBackINTEL"; + case SpvStoreCacheControlStreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvNamedMaximumNumberOfRegistersToString(SpvNamedMaximumNumberOfRegisters value) { + switch (value) { + case SpvNamedMaximumNumberOfRegistersAutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvFPEncodingToString(SpvFPEncoding value) { + switch (value) { + case SpvFPEncodingBFloat16KHR: return "BFloat16KHR"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeVectorMatrixLayoutToString(SpvCooperativeVectorMatrixLayout value) { + switch (value) { + case SpvCooperativeVectorMatrixLayoutRowMajorNV: return "RowMajorNV"; + case SpvCooperativeVectorMatrixLayoutColumnMajorNV: return "ColumnMajorNV"; + case SpvCooperativeVectorMatrixLayoutInferencingOptimalNV: return "InferencingOptimalNV"; + case SpvCooperativeVectorMatrixLayoutTrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* SpvComponentTypeToString(SpvComponentType value) { + switch (value) { + case SpvComponentTypeFloat16NV: return "Float16NV"; + case SpvComponentTypeFloat32NV: return "Float32NV"; + case SpvComponentTypeFloat64NV: return "Float64NV"; + case SpvComponentTypeSignedInt8NV: return "SignedInt8NV"; + case SpvComponentTypeSignedInt16NV: return "SignedInt16NV"; + case SpvComponentTypeSignedInt32NV: return "SignedInt32NV"; + case SpvComponentTypeSignedInt64NV: return "SignedInt64NV"; + case SpvComponentTypeUnsignedInt8NV: return "UnsignedInt8NV"; + case SpvComponentTypeUnsignedInt16NV: return "UnsignedInt16NV"; + case SpvComponentTypeUnsignedInt32NV: return "UnsignedInt32NV"; + case SpvComponentTypeUnsignedInt64NV: return "UnsignedInt64NV"; + case SpvComponentTypeSignedInt8PackedNV: return "SignedInt8PackedNV"; + case SpvComponentTypeUnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case SpvComponentTypeFloatE4M3NV: return "FloatE4M3NV"; + case SpvComponentTypeFloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* SpvOpToString(SpvOp value) { + switch (value) { + case SpvOpNop: return "OpNop"; + case SpvOpUndef: return "OpUndef"; + case SpvOpSourceContinued: return "OpSourceContinued"; + case SpvOpSource: return "OpSource"; + case SpvOpSourceExtension: return "OpSourceExtension"; + case SpvOpName: return "OpName"; + case SpvOpMemberName: return "OpMemberName"; + case SpvOpString: return "OpString"; + case SpvOpLine: return "OpLine"; + case SpvOpExtension: return "OpExtension"; + case SpvOpExtInstImport: return "OpExtInstImport"; + case SpvOpExtInst: return "OpExtInst"; + case SpvOpMemoryModel: return "OpMemoryModel"; + case SpvOpEntryPoint: return "OpEntryPoint"; + case SpvOpExecutionMode: return "OpExecutionMode"; + case SpvOpCapability: return "OpCapability"; + case SpvOpTypeVoid: return "OpTypeVoid"; + case SpvOpTypeBool: return "OpTypeBool"; + case SpvOpTypeInt: return "OpTypeInt"; + case SpvOpTypeFloat: return "OpTypeFloat"; + case SpvOpTypeVector: return "OpTypeVector"; + case SpvOpTypeMatrix: return "OpTypeMatrix"; + case SpvOpTypeImage: return "OpTypeImage"; + case SpvOpTypeSampler: return "OpTypeSampler"; + case SpvOpTypeSampledImage: return "OpTypeSampledImage"; + case SpvOpTypeArray: return "OpTypeArray"; + case SpvOpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case SpvOpTypeStruct: return "OpTypeStruct"; + case SpvOpTypeOpaque: return "OpTypeOpaque"; + case SpvOpTypePointer: return "OpTypePointer"; + case SpvOpTypeFunction: return "OpTypeFunction"; + case SpvOpTypeEvent: return "OpTypeEvent"; + case SpvOpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case SpvOpTypeReserveId: return "OpTypeReserveId"; + case SpvOpTypeQueue: return "OpTypeQueue"; + case SpvOpTypePipe: return "OpTypePipe"; + case SpvOpTypeForwardPointer: return "OpTypeForwardPointer"; + case SpvOpConstantTrue: return "OpConstantTrue"; + case SpvOpConstantFalse: return "OpConstantFalse"; + case SpvOpConstant: return "OpConstant"; + case SpvOpConstantComposite: return "OpConstantComposite"; + case SpvOpConstantSampler: return "OpConstantSampler"; + case SpvOpConstantNull: return "OpConstantNull"; + case SpvOpSpecConstantTrue: return "OpSpecConstantTrue"; + case SpvOpSpecConstantFalse: return "OpSpecConstantFalse"; + case SpvOpSpecConstant: return "OpSpecConstant"; + case SpvOpSpecConstantComposite: return "OpSpecConstantComposite"; + case SpvOpSpecConstantOp: return "OpSpecConstantOp"; + case SpvOpFunction: return "OpFunction"; + case SpvOpFunctionParameter: return "OpFunctionParameter"; + case SpvOpFunctionEnd: return "OpFunctionEnd"; + case SpvOpFunctionCall: return "OpFunctionCall"; + case SpvOpVariable: return "OpVariable"; + case SpvOpImageTexelPointer: return "OpImageTexelPointer"; + case SpvOpLoad: return "OpLoad"; + case SpvOpStore: return "OpStore"; + case SpvOpCopyMemory: return "OpCopyMemory"; + case SpvOpCopyMemorySized: return "OpCopyMemorySized"; + case SpvOpAccessChain: return "OpAccessChain"; + case SpvOpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case SpvOpPtrAccessChain: return "OpPtrAccessChain"; + case SpvOpArrayLength: return "OpArrayLength"; + case SpvOpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case SpvOpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case SpvOpDecorate: return "OpDecorate"; + case SpvOpMemberDecorate: return "OpMemberDecorate"; + case SpvOpDecorationGroup: return "OpDecorationGroup"; + case SpvOpGroupDecorate: return "OpGroupDecorate"; + case SpvOpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case SpvOpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case SpvOpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case SpvOpVectorShuffle: return "OpVectorShuffle"; + case SpvOpCompositeConstruct: return "OpCompositeConstruct"; + case SpvOpCompositeExtract: return "OpCompositeExtract"; + case SpvOpCompositeInsert: return "OpCompositeInsert"; + case SpvOpCopyObject: return "OpCopyObject"; + case SpvOpTranspose: return "OpTranspose"; + case SpvOpSampledImage: return "OpSampledImage"; + case SpvOpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case SpvOpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case SpvOpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case SpvOpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case SpvOpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case SpvOpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case SpvOpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case SpvOpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case SpvOpImageFetch: return "OpImageFetch"; + case SpvOpImageGather: return "OpImageGather"; + case SpvOpImageDrefGather: return "OpImageDrefGather"; + case SpvOpImageRead: return "OpImageRead"; + case SpvOpImageWrite: return "OpImageWrite"; + case SpvOpImage: return "OpImage"; + case SpvOpImageQueryFormat: return "OpImageQueryFormat"; + case SpvOpImageQueryOrder: return "OpImageQueryOrder"; + case SpvOpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case SpvOpImageQuerySize: return "OpImageQuerySize"; + case SpvOpImageQueryLod: return "OpImageQueryLod"; + case SpvOpImageQueryLevels: return "OpImageQueryLevels"; + case SpvOpImageQuerySamples: return "OpImageQuerySamples"; + case SpvOpConvertFToU: return "OpConvertFToU"; + case SpvOpConvertFToS: return "OpConvertFToS"; + case SpvOpConvertSToF: return "OpConvertSToF"; + case SpvOpConvertUToF: return "OpConvertUToF"; + case SpvOpUConvert: return "OpUConvert"; + case SpvOpSConvert: return "OpSConvert"; + case SpvOpFConvert: return "OpFConvert"; + case SpvOpQuantizeToF16: return "OpQuantizeToF16"; + case SpvOpConvertPtrToU: return "OpConvertPtrToU"; + case SpvOpSatConvertSToU: return "OpSatConvertSToU"; + case SpvOpSatConvertUToS: return "OpSatConvertUToS"; + case SpvOpConvertUToPtr: return "OpConvertUToPtr"; + case SpvOpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case SpvOpGenericCastToPtr: return "OpGenericCastToPtr"; + case SpvOpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case SpvOpBitcast: return "OpBitcast"; + case SpvOpSNegate: return "OpSNegate"; + case SpvOpFNegate: return "OpFNegate"; + case SpvOpIAdd: return "OpIAdd"; + case SpvOpFAdd: return "OpFAdd"; + case SpvOpISub: return "OpISub"; + case SpvOpFSub: return "OpFSub"; + case SpvOpIMul: return "OpIMul"; + case SpvOpFMul: return "OpFMul"; + case SpvOpUDiv: return "OpUDiv"; + case SpvOpSDiv: return "OpSDiv"; + case SpvOpFDiv: return "OpFDiv"; + case SpvOpUMod: return "OpUMod"; + case SpvOpSRem: return "OpSRem"; + case SpvOpSMod: return "OpSMod"; + case SpvOpFRem: return "OpFRem"; + case SpvOpFMod: return "OpFMod"; + case SpvOpVectorTimesScalar: return "OpVectorTimesScalar"; + case SpvOpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case SpvOpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case SpvOpMatrixTimesVector: return "OpMatrixTimesVector"; + case SpvOpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case SpvOpOuterProduct: return "OpOuterProduct"; + case SpvOpDot: return "OpDot"; + case SpvOpIAddCarry: return "OpIAddCarry"; + case SpvOpISubBorrow: return "OpISubBorrow"; + case SpvOpUMulExtended: return "OpUMulExtended"; + case SpvOpSMulExtended: return "OpSMulExtended"; + case SpvOpAny: return "OpAny"; + case SpvOpAll: return "OpAll"; + case SpvOpIsNan: return "OpIsNan"; + case SpvOpIsInf: return "OpIsInf"; + case SpvOpIsFinite: return "OpIsFinite"; + case SpvOpIsNormal: return "OpIsNormal"; + case SpvOpSignBitSet: return "OpSignBitSet"; + case SpvOpLessOrGreater: return "OpLessOrGreater"; + case SpvOpOrdered: return "OpOrdered"; + case SpvOpUnordered: return "OpUnordered"; + case SpvOpLogicalEqual: return "OpLogicalEqual"; + case SpvOpLogicalNotEqual: return "OpLogicalNotEqual"; + case SpvOpLogicalOr: return "OpLogicalOr"; + case SpvOpLogicalAnd: return "OpLogicalAnd"; + case SpvOpLogicalNot: return "OpLogicalNot"; + case SpvOpSelect: return "OpSelect"; + case SpvOpIEqual: return "OpIEqual"; + case SpvOpINotEqual: return "OpINotEqual"; + case SpvOpUGreaterThan: return "OpUGreaterThan"; + case SpvOpSGreaterThan: return "OpSGreaterThan"; + case SpvOpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case SpvOpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case SpvOpULessThan: return "OpULessThan"; + case SpvOpSLessThan: return "OpSLessThan"; + case SpvOpULessThanEqual: return "OpULessThanEqual"; + case SpvOpSLessThanEqual: return "OpSLessThanEqual"; + case SpvOpFOrdEqual: return "OpFOrdEqual"; + case SpvOpFUnordEqual: return "OpFUnordEqual"; + case SpvOpFOrdNotEqual: return "OpFOrdNotEqual"; + case SpvOpFUnordNotEqual: return "OpFUnordNotEqual"; + case SpvOpFOrdLessThan: return "OpFOrdLessThan"; + case SpvOpFUnordLessThan: return "OpFUnordLessThan"; + case SpvOpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case SpvOpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case SpvOpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case SpvOpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case SpvOpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case SpvOpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case SpvOpShiftRightLogical: return "OpShiftRightLogical"; + case SpvOpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case SpvOpShiftLeftLogical: return "OpShiftLeftLogical"; + case SpvOpBitwiseOr: return "OpBitwiseOr"; + case SpvOpBitwiseXor: return "OpBitwiseXor"; + case SpvOpBitwiseAnd: return "OpBitwiseAnd"; + case SpvOpNot: return "OpNot"; + case SpvOpBitFieldInsert: return "OpBitFieldInsert"; + case SpvOpBitFieldSExtract: return "OpBitFieldSExtract"; + case SpvOpBitFieldUExtract: return "OpBitFieldUExtract"; + case SpvOpBitReverse: return "OpBitReverse"; + case SpvOpBitCount: return "OpBitCount"; + case SpvOpDPdx: return "OpDPdx"; + case SpvOpDPdy: return "OpDPdy"; + case SpvOpFwidth: return "OpFwidth"; + case SpvOpDPdxFine: return "OpDPdxFine"; + case SpvOpDPdyFine: return "OpDPdyFine"; + case SpvOpFwidthFine: return "OpFwidthFine"; + case SpvOpDPdxCoarse: return "OpDPdxCoarse"; + case SpvOpDPdyCoarse: return "OpDPdyCoarse"; + case SpvOpFwidthCoarse: return "OpFwidthCoarse"; + case SpvOpEmitVertex: return "OpEmitVertex"; + case SpvOpEndPrimitive: return "OpEndPrimitive"; + case SpvOpEmitStreamVertex: return "OpEmitStreamVertex"; + case SpvOpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case SpvOpControlBarrier: return "OpControlBarrier"; + case SpvOpMemoryBarrier: return "OpMemoryBarrier"; + case SpvOpAtomicLoad: return "OpAtomicLoad"; + case SpvOpAtomicStore: return "OpAtomicStore"; + case SpvOpAtomicExchange: return "OpAtomicExchange"; + case SpvOpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case SpvOpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case SpvOpAtomicIIncrement: return "OpAtomicIIncrement"; + case SpvOpAtomicIDecrement: return "OpAtomicIDecrement"; + case SpvOpAtomicIAdd: return "OpAtomicIAdd"; + case SpvOpAtomicISub: return "OpAtomicISub"; + case SpvOpAtomicSMin: return "OpAtomicSMin"; + case SpvOpAtomicUMin: return "OpAtomicUMin"; + case SpvOpAtomicSMax: return "OpAtomicSMax"; + case SpvOpAtomicUMax: return "OpAtomicUMax"; + case SpvOpAtomicAnd: return "OpAtomicAnd"; + case SpvOpAtomicOr: return "OpAtomicOr"; + case SpvOpAtomicXor: return "OpAtomicXor"; + case SpvOpPhi: return "OpPhi"; + case SpvOpLoopMerge: return "OpLoopMerge"; + case SpvOpSelectionMerge: return "OpSelectionMerge"; + case SpvOpLabel: return "OpLabel"; + case SpvOpBranch: return "OpBranch"; + case SpvOpBranchConditional: return "OpBranchConditional"; + case SpvOpSwitch: return "OpSwitch"; + case SpvOpKill: return "OpKill"; + case SpvOpReturn: return "OpReturn"; + case SpvOpReturnValue: return "OpReturnValue"; + case SpvOpUnreachable: return "OpUnreachable"; + case SpvOpLifetimeStart: return "OpLifetimeStart"; + case SpvOpLifetimeStop: return "OpLifetimeStop"; + case SpvOpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case SpvOpGroupWaitEvents: return "OpGroupWaitEvents"; + case SpvOpGroupAll: return "OpGroupAll"; + case SpvOpGroupAny: return "OpGroupAny"; + case SpvOpGroupBroadcast: return "OpGroupBroadcast"; + case SpvOpGroupIAdd: return "OpGroupIAdd"; + case SpvOpGroupFAdd: return "OpGroupFAdd"; + case SpvOpGroupFMin: return "OpGroupFMin"; + case SpvOpGroupUMin: return "OpGroupUMin"; + case SpvOpGroupSMin: return "OpGroupSMin"; + case SpvOpGroupFMax: return "OpGroupFMax"; + case SpvOpGroupUMax: return "OpGroupUMax"; + case SpvOpGroupSMax: return "OpGroupSMax"; + case SpvOpReadPipe: return "OpReadPipe"; + case SpvOpWritePipe: return "OpWritePipe"; + case SpvOpReservedReadPipe: return "OpReservedReadPipe"; + case SpvOpReservedWritePipe: return "OpReservedWritePipe"; + case SpvOpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case SpvOpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case SpvOpCommitReadPipe: return "OpCommitReadPipe"; + case SpvOpCommitWritePipe: return "OpCommitWritePipe"; + case SpvOpIsValidReserveId: return "OpIsValidReserveId"; + case SpvOpGetNumPipePackets: return "OpGetNumPipePackets"; + case SpvOpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case SpvOpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case SpvOpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case SpvOpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case SpvOpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case SpvOpEnqueueMarker: return "OpEnqueueMarker"; + case SpvOpEnqueueKernel: return "OpEnqueueKernel"; + case SpvOpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case SpvOpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case SpvOpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case SpvOpRetainEvent: return "OpRetainEvent"; + case SpvOpReleaseEvent: return "OpReleaseEvent"; + case SpvOpCreateUserEvent: return "OpCreateUserEvent"; + case SpvOpIsValidEvent: return "OpIsValidEvent"; + case SpvOpSetUserEventStatus: return "OpSetUserEventStatus"; + case SpvOpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case SpvOpGetDefaultQueue: return "OpGetDefaultQueue"; + case SpvOpBuildNDRange: return "OpBuildNDRange"; + case SpvOpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case SpvOpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case SpvOpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case SpvOpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case SpvOpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case SpvOpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case SpvOpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case SpvOpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case SpvOpImageSparseFetch: return "OpImageSparseFetch"; + case SpvOpImageSparseGather: return "OpImageSparseGather"; + case SpvOpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case SpvOpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case SpvOpNoLine: return "OpNoLine"; + case SpvOpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case SpvOpAtomicFlagClear: return "OpAtomicFlagClear"; + case SpvOpImageSparseRead: return "OpImageSparseRead"; + case SpvOpSizeOf: return "OpSizeOf"; + case SpvOpTypePipeStorage: return "OpTypePipeStorage"; + case SpvOpConstantPipeStorage: return "OpConstantPipeStorage"; + case SpvOpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case SpvOpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case SpvOpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case SpvOpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case SpvOpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case SpvOpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case SpvOpModuleProcessed: return "OpModuleProcessed"; + case SpvOpExecutionModeId: return "OpExecutionModeId"; + case SpvOpDecorateId: return "OpDecorateId"; + case SpvOpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case SpvOpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case SpvOpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case SpvOpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case SpvOpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case SpvOpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case SpvOpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case SpvOpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case SpvOpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case SpvOpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case SpvOpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case SpvOpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case SpvOpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case SpvOpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case SpvOpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case SpvOpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case SpvOpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case SpvOpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case SpvOpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case SpvOpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case SpvOpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case SpvOpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case SpvOpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case SpvOpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case SpvOpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case SpvOpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case SpvOpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case SpvOpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case SpvOpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case SpvOpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case SpvOpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case SpvOpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case SpvOpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case SpvOpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case SpvOpCopyLogical: return "OpCopyLogical"; + case SpvOpPtrEqual: return "OpPtrEqual"; + case SpvOpPtrNotEqual: return "OpPtrNotEqual"; + case SpvOpPtrDiff: return "OpPtrDiff"; + case SpvOpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case SpvOpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case SpvOpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case SpvOpTerminateInvocation: return "OpTerminateInvocation"; + case SpvOpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case SpvOpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case SpvOpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case SpvOpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case SpvOpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case SpvOpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case SpvOpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case SpvOpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case SpvOpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case SpvOpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case SpvOpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case SpvOpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case SpvOpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case SpvOpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case SpvOpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case SpvOpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case SpvOpTraceRayKHR: return "OpTraceRayKHR"; + case SpvOpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case SpvOpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case SpvOpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case SpvOpTerminateRayKHR: return "OpTerminateRayKHR"; + case SpvOpSDot: return "OpSDot"; + case SpvOpUDot: return "OpUDot"; + case SpvOpSUDot: return "OpSUDot"; + case SpvOpSDotAccSat: return "OpSDotAccSat"; + case SpvOpUDotAccSat: return "OpUDotAccSat"; + case SpvOpSUDotAccSat: return "OpSUDotAccSat"; + case SpvOpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case SpvOpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case SpvOpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case SpvOpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case SpvOpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case SpvOpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case SpvOpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case SpvOpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case SpvOpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case SpvOpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case SpvOpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case SpvOpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case SpvOpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case SpvOpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case SpvOpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case SpvOpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case SpvOpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case SpvOpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case SpvOpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case SpvOpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case SpvOpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case SpvOpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case SpvOpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case SpvOpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case SpvOpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case SpvOpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case SpvOpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case SpvOpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case SpvOpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case SpvOpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case SpvOpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case SpvOpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case SpvOpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case SpvOpReadClockKHR: return "OpReadClockKHR"; + case SpvOpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case SpvOpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case SpvOpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case SpvOpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case SpvOpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case SpvOpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case SpvOpConstantStringAMDX: return "OpConstantStringAMDX"; + case SpvOpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case SpvOpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case SpvOpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case SpvOpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case SpvOpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case SpvOpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case SpvOpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case SpvOpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case SpvOpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case SpvOpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case SpvOpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case SpvOpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case SpvOpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case SpvOpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case SpvOpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case SpvOpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case SpvOpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case SpvOpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case SpvOpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case SpvOpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case SpvOpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case SpvOpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case SpvOpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case SpvOpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case SpvOpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case SpvOpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case SpvOpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case SpvOpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case SpvOpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case SpvOpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case SpvOpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case SpvOpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case SpvOpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case SpvOpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case SpvOpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case SpvOpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case SpvOpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case SpvOpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case SpvOpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case SpvOpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case SpvOpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case SpvOpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case SpvOpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case SpvOpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case SpvOpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case SpvOpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case SpvOpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case SpvOpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case SpvOpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case SpvOpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case SpvOpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case SpvOpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case SpvOpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case SpvOpTerminateRayNV: return "OpTerminateRayNV"; + case SpvOpTraceNV: return "OpTraceNV"; + case SpvOpTraceMotionNV: return "OpTraceMotionNV"; + case SpvOpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case SpvOpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case SpvOpExecuteCallableNV: return "OpExecuteCallableNV"; + case SpvOpRayQueryGetClusterIdNV: return "OpRayQueryGetClusterIdNV"; + case SpvOpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case SpvOpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case SpvOpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case SpvOpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case SpvOpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case SpvOpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case SpvOpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case SpvOpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case SpvOpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case SpvOpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case SpvOpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case SpvOpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case SpvOpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case SpvOpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case SpvOpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case SpvOpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case SpvOpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case SpvOpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case SpvOpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case SpvOpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case SpvOpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case SpvOpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case SpvOpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case SpvOpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case SpvOpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case SpvOpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case SpvOpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case SpvOpConvertUToImageNV: return "OpConvertUToImageNV"; + case SpvOpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case SpvOpConvertImageToUNV: return "OpConvertImageToUNV"; + case SpvOpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case SpvOpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case SpvOpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case SpvOpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case SpvOpRawAccessChainNV: return "OpRawAccessChainNV"; + case SpvOpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case SpvOpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case SpvOpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case SpvOpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case SpvOpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case SpvOpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case SpvOpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case SpvOpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case SpvOpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case SpvOpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case SpvOpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + case SpvOpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case SpvOpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + case SpvOpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case SpvOpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case SpvOpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case SpvOpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case SpvOpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case SpvOpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case SpvOpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case SpvOpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case SpvOpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case SpvOpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case SpvOpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case SpvOpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case SpvOpAbsISubINTEL: return "OpAbsISubINTEL"; + case SpvOpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case SpvOpIAddSatINTEL: return "OpIAddSatINTEL"; + case SpvOpUAddSatINTEL: return "OpUAddSatINTEL"; + case SpvOpIAverageINTEL: return "OpIAverageINTEL"; + case SpvOpUAverageINTEL: return "OpUAverageINTEL"; + case SpvOpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case SpvOpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case SpvOpISubSatINTEL: return "OpISubSatINTEL"; + case SpvOpUSubSatINTEL: return "OpUSubSatINTEL"; + case SpvOpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case SpvOpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case SpvOpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case SpvOpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case SpvOpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case SpvOpAsmINTEL: return "OpAsmINTEL"; + case SpvOpAsmCallINTEL: return "OpAsmCallINTEL"; + case SpvOpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case SpvOpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case SpvOpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case SpvOpExpectKHR: return "OpExpectKHR"; + case SpvOpDecorateString: return "OpDecorateString"; + case SpvOpMemberDecorateString: return "OpMemberDecorateString"; + case SpvOpVmeImageINTEL: return "OpVmeImageINTEL"; + case SpvOpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case SpvOpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case SpvOpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case SpvOpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case SpvOpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case SpvOpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case SpvOpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case SpvOpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case SpvOpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case SpvOpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case SpvOpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case SpvOpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case SpvOpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case SpvOpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case SpvOpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case SpvOpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case SpvOpArbitraryFloatSinCosPiINTEL: return "OpArbitraryFloatSinCosPiINTEL"; + case SpvOpArbitraryFloatCastINTEL: return "OpArbitraryFloatCastINTEL"; + case SpvOpArbitraryFloatCastFromIntINTEL: return "OpArbitraryFloatCastFromIntINTEL"; + case SpvOpArbitraryFloatCastToIntINTEL: return "OpArbitraryFloatCastToIntINTEL"; + case SpvOpArbitraryFloatAddINTEL: return "OpArbitraryFloatAddINTEL"; + case SpvOpArbitraryFloatSubINTEL: return "OpArbitraryFloatSubINTEL"; + case SpvOpArbitraryFloatMulINTEL: return "OpArbitraryFloatMulINTEL"; + case SpvOpArbitraryFloatDivINTEL: return "OpArbitraryFloatDivINTEL"; + case SpvOpArbitraryFloatGTINTEL: return "OpArbitraryFloatGTINTEL"; + case SpvOpArbitraryFloatGEINTEL: return "OpArbitraryFloatGEINTEL"; + case SpvOpArbitraryFloatLTINTEL: return "OpArbitraryFloatLTINTEL"; + case SpvOpArbitraryFloatLEINTEL: return "OpArbitraryFloatLEINTEL"; + case SpvOpArbitraryFloatEQINTEL: return "OpArbitraryFloatEQINTEL"; + case SpvOpArbitraryFloatRecipINTEL: return "OpArbitraryFloatRecipINTEL"; + case SpvOpArbitraryFloatRSqrtINTEL: return "OpArbitraryFloatRSqrtINTEL"; + case SpvOpArbitraryFloatCbrtINTEL: return "OpArbitraryFloatCbrtINTEL"; + case SpvOpArbitraryFloatHypotINTEL: return "OpArbitraryFloatHypotINTEL"; + case SpvOpArbitraryFloatSqrtINTEL: return "OpArbitraryFloatSqrtINTEL"; + case SpvOpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case SpvOpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case SpvOpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case SpvOpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case SpvOpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case SpvOpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case SpvOpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case SpvOpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case SpvOpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case SpvOpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case SpvOpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case SpvOpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case SpvOpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case SpvOpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case SpvOpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case SpvOpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case SpvOpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case SpvOpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case SpvOpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case SpvOpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case SpvOpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case SpvOpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case SpvOpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case SpvOpLoopControlINTEL: return "OpLoopControlINTEL"; + case SpvOpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case SpvOpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case SpvOpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case SpvOpFixedSqrtINTEL: return "OpFixedSqrtINTEL"; + case SpvOpFixedRecipINTEL: return "OpFixedRecipINTEL"; + case SpvOpFixedRsqrtINTEL: return "OpFixedRsqrtINTEL"; + case SpvOpFixedSinINTEL: return "OpFixedSinINTEL"; + case SpvOpFixedCosINTEL: return "OpFixedCosINTEL"; + case SpvOpFixedSinCosINTEL: return "OpFixedSinCosINTEL"; + case SpvOpFixedSinPiINTEL: return "OpFixedSinPiINTEL"; + case SpvOpFixedCosPiINTEL: return "OpFixedCosPiINTEL"; + case SpvOpFixedSinCosPiINTEL: return "OpFixedSinCosPiINTEL"; + case SpvOpFixedLogINTEL: return "OpFixedLogINTEL"; + case SpvOpFixedExpINTEL: return "OpFixedExpINTEL"; + case SpvOpPtrCastToCrossWorkgroupINTEL: return "OpPtrCastToCrossWorkgroupINTEL"; + case SpvOpCrossWorkgroupCastToPtrINTEL: return "OpCrossWorkgroupCastToPtrINTEL"; + case SpvOpReadPipeBlockingINTEL: return "OpReadPipeBlockingINTEL"; + case SpvOpWritePipeBlockingINTEL: return "OpWritePipeBlockingINTEL"; + case SpvOpFPGARegINTEL: return "OpFPGARegINTEL"; + case SpvOpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case SpvOpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case SpvOpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case SpvOpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case SpvOpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case SpvOpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case SpvOpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case SpvOpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case SpvOpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case SpvOpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case SpvOpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case SpvOpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case SpvOpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case SpvOpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case SpvOpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case SpvOpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case SpvOpTaskSequenceCreateINTEL: return "OpTaskSequenceCreateINTEL"; + case SpvOpTaskSequenceAsyncINTEL: return "OpTaskSequenceAsyncINTEL"; + case SpvOpTaskSequenceGetINTEL: return "OpTaskSequenceGetINTEL"; + case SpvOpTaskSequenceReleaseINTEL: return "OpTaskSequenceReleaseINTEL"; + case SpvOpTypeTaskSequenceINTEL: return "OpTypeTaskSequenceINTEL"; + case SpvOpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case SpvOpSubgroup2DBlockLoadINTEL: return "OpSubgroup2DBlockLoadINTEL"; + case SpvOpSubgroup2DBlockLoadTransformINTEL: return "OpSubgroup2DBlockLoadTransformINTEL"; + case SpvOpSubgroup2DBlockLoadTransposeINTEL: return "OpSubgroup2DBlockLoadTransposeINTEL"; + case SpvOpSubgroup2DBlockPrefetchINTEL: return "OpSubgroup2DBlockPrefetchINTEL"; + case SpvOpSubgroup2DBlockStoreINTEL: return "OpSubgroup2DBlockStoreINTEL"; + case SpvOpSubgroupMatrixMultiplyAccumulateINTEL: return "OpSubgroupMatrixMultiplyAccumulateINTEL"; + case SpvOpBitwiseFunctionINTEL: return "OpBitwiseFunctionINTEL"; + case SpvOpGroupIMulKHR: return "OpGroupIMulKHR"; + case SpvOpGroupFMulKHR: return "OpGroupFMulKHR"; + case SpvOpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case SpvOpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case SpvOpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case SpvOpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case SpvOpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case SpvOpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case SpvOpRoundFToTF32INTEL: return "OpRoundFToTF32INTEL"; + case SpvOpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case SpvOpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp new file mode 100644 index 00000000..bb690713 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp @@ -0,0 +1,5215 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010600; +static const unsigned int Revision = 1; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageCPP_for_OpenCL = 6, + SourceLanguageSYCL = 7, + SourceLanguageHERO_C = 8, + SourceLanguageNZSL = 9, + SourceLanguageWGSL = 10, + SourceLanguageSlang = 11, + SourceLanguageZig = 12, + SourceLanguageRust = 13, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelTaskEXT = 5364, + ExecutionModelMeshEXT = 5365, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + ExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + ExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + ExecutionModeSubgroupUniformControlFlowKHR = 4421, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeNonCoherentTileAttachmentReadQCOM = 4489, + ExecutionModeTileShadingRateQCOM = 4490, + ExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeCoalescingAMDX = 5069, + ExecutionModeIsApiEntryAMDX = 5070, + ExecutionModeMaxNodeRecursionAMDX = 5071, + ExecutionModeStaticNumWorkgroupsAMDX = 5072, + ExecutionModeShaderIndexAMDX = 5073, + ExecutionModeMaxNumWorkgroupsAMDX = 5077, + ExecutionModeStencilRefUnchangedFrontAMD = 5079, + ExecutionModeStencilRefGreaterFrontAMD = 5080, + ExecutionModeStencilRefLessFrontAMD = 5081, + ExecutionModeStencilRefUnchangedBackAMD = 5082, + ExecutionModeStencilRefGreaterBackAMD = 5083, + ExecutionModeStencilRefLessBackAMD = 5084, + ExecutionModeQuadDerivativesKHR = 5088, + ExecutionModeRequireFullQuadsKHR = 5089, + ExecutionModeSharesInputWithAMDX = 5102, + ExecutionModeOutputLinesEXT = 5269, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesEXT = 5270, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsKHR = 5289, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearKHR = 5290, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesEXT = 5298, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeSharedLocalMemorySizeINTEL = 5618, + ExecutionModeRoundingModeRTPINTEL = 5620, + ExecutionModeRoundingModeRTNINTEL = 5621, + ExecutionModeFloatingPointModeALTINTEL = 5622, + ExecutionModeFloatingPointModeIEEEINTEL = 5623, + ExecutionModeMaxWorkgroupSizeINTEL = 5893, + ExecutionModeMaxWorkDimINTEL = 5894, + ExecutionModeNoGlobalOffsetINTEL = 5895, + ExecutionModeNumSIMDWorkitemsINTEL = 5896, + ExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + ExecutionModeMaximallyReconvergesKHR = 6023, + ExecutionModeFPFastMathDefault = 6028, + ExecutionModeStreamingInterfaceINTEL = 6154, + ExecutionModeRegisterMapInterfaceINTEL = 6160, + ExecutionModeNamedBarrierCountINTEL = 6417, + ExecutionModeMaximumRegistersINTEL = 6461, + ExecutionModeMaximumRegistersIdINTEL = 6462, + ExecutionModeNamedMaximumRegistersINTEL = 6463, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassTileImageEXT = 4172, + StorageClassTileAttachmentQCOM = 4491, + StorageClassNodePayloadAMDX = 5068, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassHitObjectAttributeNV = 5385, + StorageClassTaskPayloadWorkgroupEXT = 5402, + StorageClassCodeSectionINTEL = 5605, + StorageClassDeviceOnlyINTEL = 5936, + StorageClassHostOnlyINTEL = 5937, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimTileImageDataEXT = 4173, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatR64ui = 40, + ImageFormatR64i = 41, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeUnormInt10X6EXT = 17, + ImageChannelDataTypeUnsignedIntRaw10EXT = 19, + ImageChannelDataTypeUnsignedIntRaw12EXT = 20, + ImageChannelDataTypeUnormInt2_101010EXT = 21, + ImageChannelDataTypeUnsignedInt10X6EXT = 22, + ImageChannelDataTypeUnsignedInt12X4EXT = 23, + ImageChannelDataTypeUnsignedInt14X2EXT = 24, + ImageChannelDataTypeUnormInt12X4EXT = 25, + ImageChannelDataTypeUnormInt14X2EXT = 26, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsNontemporalShift = 14, + ImageOperandsOffsetsShift = 16, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, + ImageOperandsNontemporalMask = 0x00004000, + ImageOperandsOffsetsMask = 0x00010000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeAllowContractShift = 16, + FPFastMathModeAllowContractFastINTELShift = 16, + FPFastMathModeAllowReassocShift = 17, + FPFastMathModeAllowReassocINTELShift = 17, + FPFastMathModeAllowTransformShift = 18, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, + FPFastMathModeAllowContractMask = 0x00010000, + FPFastMathModeAllowContractFastINTELMask = 0x00010000, + FPFastMathModeAllowReassocMask = 0x00020000, + FPFastMathModeAllowReassocINTELMask = 0x00020000, + FPFastMathModeAllowTransformMask = 0x00040000, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeLinkOnceODR = 2, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeRuntimeAlignedINTEL = 5940, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationWeightTextureQCOM = 4487, + DecorationBlockMatchTextureQCOM = 4488, + DecorationBlockMatchSamplerQCOM = 4499, + DecorationExplicitInterpAMD = 4999, + DecorationNodeSharesPayloadLimitsWithAMDX = 5019, + DecorationNodeMaxPayloadsAMDX = 5020, + DecorationTrackFinishWritingAMDX = 5078, + DecorationPayloadNodeNameAMDX = 5091, + DecorationPayloadNodeBaseIndexAMDX = 5098, + DecorationPayloadNodeSparseArrayAMDX = 5099, + DecorationPayloadNodeArraySizeAMDX = 5100, + DecorationPayloadDispatchIndirectAMDX = 5105, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveEXT = 5271, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexKHR = 5285, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationHitObjectShaderRecordBufferNV = 5386, + DecorationBindlessSamplerNV = 5398, + DecorationBindlessImageNV = 5399, + DecorationBoundSamplerNV = 5400, + DecorationBoundImageNV = 5401, + DecorationSIMTCallINTEL = 5599, + DecorationReferencedIndirectlyINTEL = 5602, + DecorationClobberINTEL = 5607, + DecorationSideEffectsINTEL = 5608, + DecorationVectorComputeVariableINTEL = 5624, + DecorationFuncParamIOKindINTEL = 5625, + DecorationVectorComputeFunctionINTEL = 5626, + DecorationStackCallINTEL = 5627, + DecorationGlobalVariableOffsetINTEL = 5628, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationFunctionRoundingModeINTEL = 5822, + DecorationFunctionDenormModeINTEL = 5823, + DecorationRegisterINTEL = 5825, + DecorationMemoryINTEL = 5826, + DecorationNumbanksINTEL = 5827, + DecorationBankwidthINTEL = 5828, + DecorationMaxPrivateCopiesINTEL = 5829, + DecorationSinglepumpINTEL = 5830, + DecorationDoublepumpINTEL = 5831, + DecorationMaxReplicatesINTEL = 5832, + DecorationSimpleDualPortINTEL = 5833, + DecorationMergeINTEL = 5834, + DecorationBankBitsINTEL = 5835, + DecorationForcePow2DepthINTEL = 5836, + DecorationStridesizeINTEL = 5883, + DecorationWordsizeINTEL = 5884, + DecorationTrueDualPortINTEL = 5885, + DecorationBurstCoalesceINTEL = 5899, + DecorationCacheSizeINTEL = 5900, + DecorationDontStaticallyCoalesceINTEL = 5901, + DecorationPrefetchINTEL = 5902, + DecorationStallEnableINTEL = 5905, + DecorationFuseLoopsInFunctionINTEL = 5907, + DecorationMathOpDSPModeINTEL = 5909, + DecorationAliasScopeINTEL = 5914, + DecorationNoAliasINTEL = 5915, + DecorationInitiationIntervalINTEL = 5917, + DecorationMaxConcurrencyINTEL = 5918, + DecorationPipelineEnableINTEL = 5919, + DecorationBufferLocationINTEL = 5921, + DecorationIOPipeStorageINTEL = 5944, + DecorationFunctionFloatingPointModeINTEL = 6080, + DecorationSingleElementVectorINTEL = 6085, + DecorationVectorComputeCallableFunctionINTEL = 6087, + DecorationMediaBlockIOINTEL = 6140, + DecorationStallFreeINTEL = 6151, + DecorationFPMaxErrorDecorationINTEL = 6170, + DecorationLatencyControlLabelINTEL = 6172, + DecorationLatencyControlConstraintINTEL = 6173, + DecorationConduitKernelArgumentINTEL = 6175, + DecorationRegisterMapKernelArgumentINTEL = 6176, + DecorationMMHostInterfaceAddressWidthINTEL = 6177, + DecorationMMHostInterfaceDataWidthINTEL = 6178, + DecorationMMHostInterfaceLatencyINTEL = 6179, + DecorationMMHostInterfaceReadWriteModeINTEL = 6180, + DecorationMMHostInterfaceMaxBurstINTEL = 6181, + DecorationMMHostInterfaceWaitRequestINTEL = 6182, + DecorationStableKernelArgumentINTEL = 6183, + DecorationHostAccessINTEL = 6188, + DecorationInitModeINTEL = 6190, + DecorationImplementInRegisterMapINTEL = 6191, + DecorationCacheControlLoadINTEL = 6442, + DecorationCacheControlStoreINTEL = 6443, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInCoreIDARM = 4160, + BuiltInCoreCountARM = 4161, + BuiltInCoreMaxIDARM = 4162, + BuiltInWarpIDARM = 4163, + BuiltInWarpMaxIDARM = 4164, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInPrimitiveShadingRateKHR = 4432, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInShadingRateKHR = 4444, + BuiltInTileOffsetQCOM = 4492, + BuiltInTileDimensionQCOM = 4493, + BuiltInTileApronSizeQCOM = 4494, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInRemainingRecursionLevelsAMDX = 5021, + BuiltInShaderIndexAMDX = 5073, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordKHR = 5286, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspKHR = 5287, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInPrimitivePointIndicesEXT = 5294, + BuiltInPrimitiveLineIndicesEXT = 5295, + BuiltInPrimitiveTriangleIndicesEXT = 5296, + BuiltInCullPrimitiveEXT = 5299, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInCurrentRayTimeNV = 5334, + BuiltInHitTriangleVertexPositionsKHR = 5335, + BuiltInHitMicroTriangleVertexPositionsNV = 5337, + BuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInHitIsSphereNV = 5359, + BuiltInHitIsLSSNV = 5360, + BuiltInHitSpherePositionNV = 5361, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInHitLSSPositionsNV = 5396, + BuiltInHitKindFrontFacingMicroTriangleNV = 5405, + BuiltInHitKindBackFacingMicroTriangleNV = 5406, + BuiltInHitSphereRadiusNV = 5420, + BuiltInHitLSSRadiiNV = 5421, + BuiltInClusterIDNV = 5436, + BuiltInCullMaskKHR = 6021, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlInitiationIntervalINTELShift = 16, + LoopControlMaxConcurrencyINTELShift = 17, + LoopControlDependencyArrayINTELShift = 18, + LoopControlPipelineEnableINTELShift = 19, + LoopControlLoopCoalesceINTELShift = 20, + LoopControlMaxInterleavingINTELShift = 21, + LoopControlSpeculatedIterationsINTELShift = 22, + LoopControlNoFusionINTELShift = 23, + LoopControlLoopCountINTELShift = 24, + LoopControlMaxReinvocationDelayINTELShift = 25, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, + LoopControlInitiationIntervalINTELMask = 0x00010000, + LoopControlMaxConcurrencyINTELMask = 0x00020000, + LoopControlDependencyArrayINTELMask = 0x00040000, + LoopControlPipelineEnableINTELMask = 0x00080000, + LoopControlLoopCoalesceINTELMask = 0x00100000, + LoopControlMaxInterleavingINTELMask = 0x00200000, + LoopControlSpeculatedIterationsINTELMask = 0x00400000, + LoopControlNoFusionINTELMask = 0x00800000, + LoopControlLoopCountINTELMask = 0x01000000, + LoopControlMaxReinvocationDelayINTELMask = 0x02000000, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlOptNoneEXTShift = 16, + FunctionControlOptNoneINTELShift = 16, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, + FunctionControlOptNoneEXTMask = 0x00010000, + FunctionControlOptNoneINTELMask = 0x00010000, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessAliasScopeINTELMaskShift = 16, + MemoryAccessNoAliasINTELMaskShift = 17, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, + MemoryAccessAliasScopeINTELMaskMask = 0x00010000, + MemoryAccessNoAliasINTELMaskMask = 0x00020000, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilityUniformDecoration = 71, + CapabilityCoreBuiltinsARM = 4165, + CapabilityTileImageColorReadAccessEXT = 4166, + CapabilityTileImageDepthReadAccessEXT = 4167, + CapabilityTileImageStencilReadAccessEXT = 4168, + CapabilityCooperativeMatrixLayoutsARM = 4201, + CapabilityFragmentShadingRateKHR = 4422, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayQueryKHR = 4472, + CapabilityUntypedPointersKHR = 4473, + CapabilityRayTraversalPrimitiveCullingKHR = 4478, + CapabilityRayTracingKHR = 4479, + CapabilityTextureSampleWeightedQCOM = 4484, + CapabilityTextureBoxFilterQCOM = 4485, + CapabilityTextureBlockMatchQCOM = 4486, + CapabilityTileShadingQCOM = 4495, + CapabilityTextureBlockMatch2QCOM = 4498, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityInt64ImageEXT = 5016, + CapabilityShaderClockKHR = 5055, + CapabilityShaderEnqueueAMDX = 5067, + CapabilityQuadControlKHR = 5087, + CapabilityBFloat16TypeKHR = 5116, + CapabilityBFloat16DotProductKHR = 5117, + CapabilityBFloat16CooperativeMatrixKHR = 5118, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityMeshShadingEXT = 5283, + CapabilityFragmentBarycentricKHR = 5284, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsKHR = 5288, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingPositionFetchKHR = 5336, + CapabilityRayTracingNV = 5340, + CapabilityRayTracingMotionBlurNV = 5341, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearKHR = 5350, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocation = 5379, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilityDisplacementMicromapNV = 5380, + CapabilityRayTracingOpacityMicromapEXT = 5381, + CapabilityShaderInvocationReorderNV = 5383, + CapabilityBindlessTextureNV = 5390, + CapabilityRayQueryPositionFetchKHR = 5391, + CapabilityCooperativeVectorNV = 5394, + CapabilityAtomicFloat16VectorNV = 5404, + CapabilityRayTracingDisplacementMicromapNV = 5409, + CapabilityRawAccessChainsNV = 5414, + CapabilityRayTracingSpheresGeometryNV = 5418, + CapabilityRayTracingLinearSweptSpheresGeometryNV = 5419, + CapabilityCooperativeMatrixReductionsNV = 5430, + CapabilityCooperativeMatrixConversionsNV = 5431, + CapabilityCooperativeMatrixPerElementOperationsNV = 5432, + CapabilityCooperativeMatrixTensorAddressingNV = 5433, + CapabilityCooperativeMatrixBlockLoadsNV = 5434, + CapabilityCooperativeVectorTrainingNV = 5435, + CapabilityRayTracingClusterAccelerationStructureNV = 5437, + CapabilityTensorAddressingNV = 5439, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityRoundToInfinityINTEL = 5582, + CapabilityFloatingPointModeINTEL = 5583, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilityFunctionPointersINTEL = 5603, + CapabilityIndirectReferencesINTEL = 5604, + CapabilityAsmINTEL = 5606, + CapabilityAtomicFloat32MinMaxEXT = 5612, + CapabilityAtomicFloat64MinMaxEXT = 5613, + CapabilityAtomicFloat16MinMaxEXT = 5616, + CapabilityVectorComputeINTEL = 5617, + CapabilityVectorAnyINTEL = 5619, + CapabilityExpectAssumeKHR = 5629, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityVariableLengthArrayINTEL = 5817, + CapabilityFunctionFloatControlINTEL = 5821, + CapabilityFPGAMemoryAttributesINTEL = 5824, + CapabilityFPFastMathModeINTEL = 5837, + CapabilityArbitraryPrecisionIntegersINTEL = 5844, + CapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + CapabilityUnstructuredLoopControlsINTEL = 5886, + CapabilityFPGALoopControlsINTEL = 5888, + CapabilityKernelAttributesINTEL = 5892, + CapabilityFPGAKernelAttributesINTEL = 5897, + CapabilityFPGAMemoryAccessesINTEL = 5898, + CapabilityFPGAClusterAttributesINTEL = 5904, + CapabilityLoopFuseINTEL = 5906, + CapabilityFPGADSPControlINTEL = 5908, + CapabilityMemoryAccessAliasingINTEL = 5910, + CapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + CapabilityFPGABufferLocationINTEL = 5920, + CapabilityArbitraryPrecisionFixedPointINTEL = 5922, + CapabilityUSMStorageClassesINTEL = 5935, + CapabilityRuntimeAlignedAttributeINTEL = 5939, + CapabilityIOPipesINTEL = 5943, + CapabilityBlockingPipesINTEL = 5945, + CapabilityFPGARegINTEL = 5948, + CapabilityDotProductInputAll = 6016, + CapabilityDotProductInputAllKHR = 6016, + CapabilityDotProductInput4x8Bit = 6017, + CapabilityDotProductInput4x8BitKHR = 6017, + CapabilityDotProductInput4x8BitPacked = 6018, + CapabilityDotProductInput4x8BitPackedKHR = 6018, + CapabilityDotProduct = 6019, + CapabilityDotProductKHR = 6019, + CapabilityRayCullMaskKHR = 6020, + CapabilityCooperativeMatrixKHR = 6022, + CapabilityReplicatedCompositesEXT = 6024, + CapabilityBitInstructions = 6025, + CapabilityGroupNonUniformRotateKHR = 6026, + CapabilityFloatControls2 = 6029, + CapabilityAtomicFloat32AddEXT = 6033, + CapabilityAtomicFloat64AddEXT = 6034, + CapabilityLongCompositesINTEL = 6089, + CapabilityOptNoneEXT = 6094, + CapabilityOptNoneINTEL = 6094, + CapabilityAtomicFloat16AddEXT = 6095, + CapabilityDebugInfoModuleINTEL = 6114, + CapabilityBFloat16ConversionINTEL = 6115, + CapabilitySplitBarrierINTEL = 6141, + CapabilityArithmeticFenceEXT = 6144, + CapabilityFPGAClusterAttributesV2INTEL = 6150, + CapabilityFPGAKernelAttributesv2INTEL = 6161, + CapabilityTaskSequenceINTEL = 6162, + CapabilityFPMaxErrorINTEL = 6169, + CapabilityFPGALatencyControlINTEL = 6171, + CapabilityFPGAArgumentInterfacesINTEL = 6174, + CapabilityGlobalVariableHostAccessINTEL = 6187, + CapabilityGlobalVariableFPGADecorationsINTEL = 6189, + CapabilitySubgroupBufferPrefetchINTEL = 6220, + CapabilitySubgroup2DBlockIOINTEL = 6228, + CapabilitySubgroup2DBlockTransformINTEL = 6229, + CapabilitySubgroup2DBlockTransposeINTEL = 6230, + CapabilitySubgroupMatrixMultiplyAccumulateINTEL = 6236, + CapabilityTernaryBitwiseFunctionINTEL = 6241, + CapabilityGroupUniformArithmeticKHR = 6400, + CapabilityTensorFloat32RoundingINTEL = 6425, + CapabilityMaskedGatherScatterINTEL = 6427, + CapabilityCacheControlsINTEL = 6441, + CapabilityRegisterLimitsINTEL = 6460, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipBuiltinPrimitivesNVShift = 8, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsForceOpacityMicromap2StateEXTShift = 10, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipBuiltinPrimitivesNVMask = 0x00000100, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, + RayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum FragmentShadingRateShift { + FragmentShadingRateVertical2PixelsShift = 0, + FragmentShadingRateVertical4PixelsShift = 1, + FragmentShadingRateHorizontal2PixelsShift = 2, + FragmentShadingRateHorizontal4PixelsShift = 3, + FragmentShadingRateMax = 0x7fffffff, +}; + +enum FragmentShadingRateMask { + FragmentShadingRateMaskNone = 0, + FragmentShadingRateVertical2PixelsMask = 0x00000001, + FragmentShadingRateVertical4PixelsMask = 0x00000002, + FragmentShadingRateHorizontal2PixelsMask = 0x00000004, + FragmentShadingRateHorizontal4PixelsMask = 0x00000008, +}; + +enum FPDenormMode { + FPDenormModePreserve = 0, + FPDenormModeFlushToZero = 1, + FPDenormModeMax = 0x7fffffff, +}; + +enum FPOperationMode { + FPOperationModeIEEE = 0, + FPOperationModeALT = 1, + FPOperationModeMax = 0x7fffffff, +}; + +enum QuantizationModes { + QuantizationModesTRN = 0, + QuantizationModesTRN_ZERO = 1, + QuantizationModesRND = 2, + QuantizationModesRND_ZERO = 3, + QuantizationModesRND_INF = 4, + QuantizationModesRND_MIN_INF = 5, + QuantizationModesRND_CONV = 6, + QuantizationModesRND_CONV_ODD = 7, + QuantizationModesMax = 0x7fffffff, +}; + +enum OverflowModes { + OverflowModesWRAP = 0, + OverflowModesSAT = 1, + OverflowModesSAT_ZERO = 2, + OverflowModesSAT_SYM = 3, + OverflowModesMax = 0x7fffffff, +}; + +enum PackedVectorFormat { + PackedVectorFormatPackedVectorFormat4x8Bit = 0, + PackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + PackedVectorFormatMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsShift { + CooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + CooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + CooperativeMatrixOperandsMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsMask { + CooperativeMatrixOperandsMaskNone = 0, + CooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + CooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +}; + +enum CooperativeMatrixLayout { + CooperativeMatrixLayoutRowMajorKHR = 0, + CooperativeMatrixLayoutColumnMajorKHR = 1, + CooperativeMatrixLayoutRowBlockedInterleavedARM = 4202, + CooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203, + CooperativeMatrixLayoutMax = 0x7fffffff, +}; + +enum CooperativeMatrixUse { + CooperativeMatrixUseMatrixAKHR = 0, + CooperativeMatrixUseMatrixBKHR = 1, + CooperativeMatrixUseMatrixAccumulatorKHR = 2, + CooperativeMatrixUseMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceShift { + CooperativeMatrixReduceRowShift = 0, + CooperativeMatrixReduceColumnShift = 1, + CooperativeMatrixReduce2x2Shift = 2, + CooperativeMatrixReduceMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceMask { + CooperativeMatrixReduceMaskNone = 0, + CooperativeMatrixReduceRowMask = 0x00000001, + CooperativeMatrixReduceColumnMask = 0x00000002, + CooperativeMatrixReduce2x2Mask = 0x00000004, +}; + +enum TensorClampMode { + TensorClampModeUndefined = 0, + TensorClampModeConstant = 1, + TensorClampModeClampToEdge = 2, + TensorClampModeRepeat = 3, + TensorClampModeRepeatMirrored = 4, + TensorClampModeMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsShift { + TensorAddressingOperandsTensorViewShift = 0, + TensorAddressingOperandsDecodeFuncShift = 1, + TensorAddressingOperandsMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsMask { + TensorAddressingOperandsMaskNone = 0, + TensorAddressingOperandsTensorViewMask = 0x00000001, + TensorAddressingOperandsDecodeFuncMask = 0x00000002, +}; + +enum InitializationModeQualifier { + InitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + InitializationModeQualifierInitOnDeviceResetINTEL = 1, + InitializationModeQualifierMax = 0x7fffffff, +}; + +enum HostAccessQualifier { + HostAccessQualifierNoneINTEL = 0, + HostAccessQualifierReadINTEL = 1, + HostAccessQualifierWriteINTEL = 2, + HostAccessQualifierReadWriteINTEL = 3, + HostAccessQualifierMax = 0x7fffffff, +}; + +enum LoadCacheControl { + LoadCacheControlUncachedINTEL = 0, + LoadCacheControlCachedINTEL = 1, + LoadCacheControlStreamingINTEL = 2, + LoadCacheControlInvalidateAfterReadINTEL = 3, + LoadCacheControlConstCachedINTEL = 4, + LoadCacheControlMax = 0x7fffffff, +}; + +enum StoreCacheControl { + StoreCacheControlUncachedINTEL = 0, + StoreCacheControlWriteThroughINTEL = 1, + StoreCacheControlWriteBackINTEL = 2, + StoreCacheControlStreamingINTEL = 3, + StoreCacheControlMax = 0x7fffffff, +}; + +enum NamedMaximumNumberOfRegisters { + NamedMaximumNumberOfRegistersAutoINTEL = 0, + NamedMaximumNumberOfRegistersMax = 0x7fffffff, +}; + +enum MatrixMultiplyAccumulateOperandsShift { + MatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELShift = 0, + MatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELShift = 1, + MatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELShift = 2, + MatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELShift = 3, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELShift = 4, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELShift = 5, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELShift = 6, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELShift = 7, + MatrixMultiplyAccumulateOperandsMatrixATF32INTELShift = 8, + MatrixMultiplyAccumulateOperandsMatrixBTF32INTELShift = 9, + MatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELShift = 10, + MatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELShift = 11, + MatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELShift = 12, + MatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELShift = 13, + MatrixMultiplyAccumulateOperandsMax = 0x7fffffff, +}; + +enum MatrixMultiplyAccumulateOperandsMask { + MatrixMultiplyAccumulateOperandsMaskNone = 0, + MatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELMask = 0x00000001, + MatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELMask = 0x00000002, + MatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELMask = 0x00000004, + MatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELMask = 0x00000008, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELMask = 0x00000010, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELMask = 0x00000020, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELMask = 0x00000040, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELMask = 0x00000080, + MatrixMultiplyAccumulateOperandsMatrixATF32INTELMask = 0x00000100, + MatrixMultiplyAccumulateOperandsMatrixBTF32INTELMask = 0x00000200, + MatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELMask = 0x00000400, + MatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELMask = 0x00000800, + MatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELMask = 0x00001000, + MatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELMask = 0x00002000, +}; + +enum RawAccessChainOperandsShift { + RawAccessChainOperandsRobustnessPerComponentNVShift = 0, + RawAccessChainOperandsRobustnessPerElementNVShift = 1, + RawAccessChainOperandsMax = 0x7fffffff, +}; + +enum RawAccessChainOperandsMask { + RawAccessChainOperandsMaskNone = 0, + RawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + RawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +}; + +enum FPEncoding { + FPEncodingBFloat16KHR = 0, + FPEncodingMax = 0x7fffffff, +}; + +enum CooperativeVectorMatrixLayout { + CooperativeVectorMatrixLayoutRowMajorNV = 0, + CooperativeVectorMatrixLayoutColumnMajorNV = 1, + CooperativeVectorMatrixLayoutInferencingOptimalNV = 2, + CooperativeVectorMatrixLayoutTrainingOptimalNV = 3, + CooperativeVectorMatrixLayoutMax = 0x7fffffff, +}; + +enum ComponentType { + ComponentTypeFloat16NV = 0, + ComponentTypeFloat32NV = 1, + ComponentTypeFloat64NV = 2, + ComponentTypeSignedInt8NV = 3, + ComponentTypeSignedInt16NV = 4, + ComponentTypeSignedInt32NV = 5, + ComponentTypeSignedInt64NV = 6, + ComponentTypeUnsignedInt8NV = 7, + ComponentTypeUnsignedInt16NV = 8, + ComponentTypeUnsignedInt32NV = 9, + ComponentTypeUnsignedInt64NV = 10, + ComponentTypeSignedInt8PackedNV = 1000491000, + ComponentTypeUnsignedInt8PackedNV = 1000491001, + ComponentTypeFloatE4M3NV = 1000491002, + ComponentTypeFloatE5M2NV = 1000491003, + ComponentTypeMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case OpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case OpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case OpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case OpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case OpSDot: *hasResult = true; *hasResultType = true; break; + case OpUDot: *hasResult = true; *hasResultType = true; break; + case OpSUDot: *hasResult = true; *hasResultType = true; break; + case OpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case OpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case OpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case OpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case OpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case OpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case OpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case OpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case OpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case OpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeVectorNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case OpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case OpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case OpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case OpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case OpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case OpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case OpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case OpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSHitValueNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmTargetINTEL: *hasResult = true; *hasResultType = false; break; + case OpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case OpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case OpExpectKHR: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case OpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case OpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case OpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case OpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedLogINTEL: *hasResult = true; *hasResultType = true; break; + case OpFixedExpINTEL: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case OpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case OpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case OpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case OpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceCreateINTEL: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceAsyncINTEL: *hasResult = false; *hasResultType = false; break; + case OpTaskSequenceGetINTEL: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceReleaseINTEL: *hasResult = false; *hasResultType = false; break; + case OpTypeTaskSequenceINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadTransformINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadTransposeINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockStoreINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupMatrixMultiplyAccumulateINTEL: *hasResult = true; *hasResultType = true; break; + case OpBitwiseFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case OpRoundFToTF32INTEL: *hasResult = true; *hasResultType = true; break; + case OpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case OpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + } +} +inline const char* SourceLanguageToString(SourceLanguage value) { + switch (value) { + case SourceLanguageUnknown: return "Unknown"; + case SourceLanguageESSL: return "ESSL"; + case SourceLanguageGLSL: return "GLSL"; + case SourceLanguageOpenCL_C: return "OpenCL_C"; + case SourceLanguageOpenCL_CPP: return "OpenCL_CPP"; + case SourceLanguageHLSL: return "HLSL"; + case SourceLanguageCPP_for_OpenCL: return "CPP_for_OpenCL"; + case SourceLanguageSYCL: return "SYCL"; + case SourceLanguageHERO_C: return "HERO_C"; + case SourceLanguageNZSL: return "NZSL"; + case SourceLanguageWGSL: return "WGSL"; + case SourceLanguageSlang: return "Slang"; + case SourceLanguageZig: return "Zig"; + case SourceLanguageRust: return "Rust"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModelToString(ExecutionModel value) { + switch (value) { + case ExecutionModelVertex: return "Vertex"; + case ExecutionModelTessellationControl: return "TessellationControl"; + case ExecutionModelTessellationEvaluation: return "TessellationEvaluation"; + case ExecutionModelGeometry: return "Geometry"; + case ExecutionModelFragment: return "Fragment"; + case ExecutionModelGLCompute: return "GLCompute"; + case ExecutionModelKernel: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + case ExecutionModelTaskEXT: return "TaskEXT"; + case ExecutionModelMeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* AddressingModelToString(AddressingModel value) { + switch (value) { + case AddressingModelLogical: return "Logical"; + case AddressingModelPhysical32: return "Physical32"; + case AddressingModelPhysical64: return "Physical64"; + case AddressingModelPhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* MemoryModelToString(MemoryModel value) { + switch (value) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModeToString(ExecutionMode value) { + switch (value) { + case ExecutionModeInvocations: return "Invocations"; + case ExecutionModeSpacingEqual: return "SpacingEqual"; + case ExecutionModeSpacingFractionalEven: return "SpacingFractionalEven"; + case ExecutionModeSpacingFractionalOdd: return "SpacingFractionalOdd"; + case ExecutionModeVertexOrderCw: return "VertexOrderCw"; + case ExecutionModeVertexOrderCcw: return "VertexOrderCcw"; + case ExecutionModePixelCenterInteger: return "PixelCenterInteger"; + case ExecutionModeOriginUpperLeft: return "OriginUpperLeft"; + case ExecutionModeOriginLowerLeft: return "OriginLowerLeft"; + case ExecutionModeEarlyFragmentTests: return "EarlyFragmentTests"; + case ExecutionModePointMode: return "PointMode"; + case ExecutionModeXfb: return "Xfb"; + case ExecutionModeDepthReplacing: return "DepthReplacing"; + case ExecutionModeDepthGreater: return "DepthGreater"; + case ExecutionModeDepthLess: return "DepthLess"; + case ExecutionModeDepthUnchanged: return "DepthUnchanged"; + case ExecutionModeLocalSize: return "LocalSize"; + case ExecutionModeLocalSizeHint: return "LocalSizeHint"; + case ExecutionModeInputPoints: return "InputPoints"; + case ExecutionModeInputLines: return "InputLines"; + case ExecutionModeInputLinesAdjacency: return "InputLinesAdjacency"; + case ExecutionModeTriangles: return "Triangles"; + case ExecutionModeInputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case ExecutionModeQuads: return "Quads"; + case ExecutionModeIsolines: return "Isolines"; + case ExecutionModeOutputVertices: return "OutputVertices"; + case ExecutionModeOutputPoints: return "OutputPoints"; + case ExecutionModeOutputLineStrip: return "OutputLineStrip"; + case ExecutionModeOutputTriangleStrip: return "OutputTriangleStrip"; + case ExecutionModeVecTypeHint: return "VecTypeHint"; + case ExecutionModeContractionOff: return "ContractionOff"; + case ExecutionModeInitializer: return "Initializer"; + case ExecutionModeFinalizer: return "Finalizer"; + case ExecutionModeSubgroupSize: return "SubgroupSize"; + case ExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionModeLocalSizeId: return "LocalSizeId"; + case ExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + case ExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case ExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case ExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case ExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case ExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case ExecutionModeDenormPreserve: return "DenormPreserve"; + case ExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case ExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case ExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionModeNonCoherentTileAttachmentReadQCOM: return "NonCoherentTileAttachmentReadQCOM"; + case ExecutionModeTileShadingRateQCOM: return "TileShadingRateQCOM"; + case ExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case ExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case ExecutionModeCoalescingAMDX: return "CoalescingAMDX"; + case ExecutionModeIsApiEntryAMDX: return "IsApiEntryAMDX"; + case ExecutionModeMaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case ExecutionModeStaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case ExecutionModeShaderIndexAMDX: return "ShaderIndexAMDX"; + case ExecutionModeMaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case ExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case ExecutionModeStencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case ExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case ExecutionModeStencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case ExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case ExecutionModeStencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case ExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + case ExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case ExecutionModeSharesInputWithAMDX: return "SharesInputWithAMDX"; + case ExecutionModeOutputLinesEXT: return "OutputLinesEXT"; + case ExecutionModeOutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case ExecutionModeDerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case ExecutionModeDerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case ExecutionModeOutputTrianglesEXT: return "OutputTrianglesEXT"; + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case ExecutionModeSharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case ExecutionModeRoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case ExecutionModeRoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case ExecutionModeFloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case ExecutionModeFloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case ExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case ExecutionModeSchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case ExecutionModeMaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case ExecutionModeFPFastMathDefault: return "FPFastMathDefault"; + case ExecutionModeStreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case ExecutionModeRegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case ExecutionModeNamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case ExecutionModeMaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case ExecutionModeMaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case ExecutionModeNamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* StorageClassToString(StorageClass value) { + switch (value) { + case StorageClassUniformConstant: return "UniformConstant"; + case StorageClassInput: return "Input"; + case StorageClassUniform: return "Uniform"; + case StorageClassOutput: return "Output"; + case StorageClassWorkgroup: return "Workgroup"; + case StorageClassCrossWorkgroup: return "CrossWorkgroup"; + case StorageClassPrivate: return "Private"; + case StorageClassFunction: return "Function"; + case StorageClassGeneric: return "Generic"; + case StorageClassPushConstant: return "PushConstant"; + case StorageClassAtomicCounter: return "AtomicCounter"; + case StorageClassImage: return "Image"; + case StorageClassStorageBuffer: return "StorageBuffer"; + case StorageClassTileImageEXT: return "TileImageEXT"; + case StorageClassTileAttachmentQCOM: return "TileAttachmentQCOM"; + case StorageClassNodePayloadAMDX: return "NodePayloadAMDX"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassPhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case StorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case StorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case StorageClassCodeSectionINTEL: return "CodeSectionINTEL"; + case StorageClassDeviceOnlyINTEL: return "DeviceOnlyINTEL"; + case StorageClassHostOnlyINTEL: return "HostOnlyINTEL"; + default: return "Unknown"; + } +} + +inline const char* DimToString(Dim value) { + switch (value) { + case Dim1D: return "1D"; + case Dim2D: return "2D"; + case Dim3D: return "3D"; + case DimCube: return "Cube"; + case DimRect: return "Rect"; + case DimBuffer: return "Buffer"; + case DimSubpassData: return "SubpassData"; + case DimTileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SamplerAddressingModeToString(SamplerAddressingMode value) { + switch (value) { + case SamplerAddressingModeNone: return "None"; + case SamplerAddressingModeClampToEdge: return "ClampToEdge"; + case SamplerAddressingModeClamp: return "Clamp"; + case SamplerAddressingModeRepeat: return "Repeat"; + case SamplerAddressingModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SamplerFilterModeToString(SamplerFilterMode value) { + switch (value) { + case SamplerFilterModeNearest: return "Nearest"; + case SamplerFilterModeLinear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* ImageFormatToString(ImageFormat value) { + switch (value) { + case ImageFormatUnknown: return "Unknown"; + case ImageFormatRgba32f: return "Rgba32f"; + case ImageFormatRgba16f: return "Rgba16f"; + case ImageFormatR32f: return "R32f"; + case ImageFormatRgba8: return "Rgba8"; + case ImageFormatRgba8Snorm: return "Rgba8Snorm"; + case ImageFormatRg32f: return "Rg32f"; + case ImageFormatRg16f: return "Rg16f"; + case ImageFormatR11fG11fB10f: return "R11fG11fB10f"; + case ImageFormatR16f: return "R16f"; + case ImageFormatRgba16: return "Rgba16"; + case ImageFormatRgb10A2: return "Rgb10A2"; + case ImageFormatRg16: return "Rg16"; + case ImageFormatRg8: return "Rg8"; + case ImageFormatR16: return "R16"; + case ImageFormatR8: return "R8"; + case ImageFormatRgba16Snorm: return "Rgba16Snorm"; + case ImageFormatRg16Snorm: return "Rg16Snorm"; + case ImageFormatRg8Snorm: return "Rg8Snorm"; + case ImageFormatR16Snorm: return "R16Snorm"; + case ImageFormatR8Snorm: return "R8Snorm"; + case ImageFormatRgba32i: return "Rgba32i"; + case ImageFormatRgba16i: return "Rgba16i"; + case ImageFormatRgba8i: return "Rgba8i"; + case ImageFormatR32i: return "R32i"; + case ImageFormatRg32i: return "Rg32i"; + case ImageFormatRg16i: return "Rg16i"; + case ImageFormatRg8i: return "Rg8i"; + case ImageFormatR16i: return "R16i"; + case ImageFormatR8i: return "R8i"; + case ImageFormatRgba32ui: return "Rgba32ui"; + case ImageFormatRgba16ui: return "Rgba16ui"; + case ImageFormatRgba8ui: return "Rgba8ui"; + case ImageFormatR32ui: return "R32ui"; + case ImageFormatRgb10a2ui: return "Rgb10a2ui"; + case ImageFormatRg32ui: return "Rg32ui"; + case ImageFormatRg16ui: return "Rg16ui"; + case ImageFormatRg8ui: return "Rg8ui"; + case ImageFormatR16ui: return "R16ui"; + case ImageFormatR8ui: return "R8ui"; + case ImageFormatR64ui: return "R64ui"; + case ImageFormatR64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelOrderToString(ImageChannelOrder value) { + switch (value) { + case ImageChannelOrderR: return "R"; + case ImageChannelOrderA: return "A"; + case ImageChannelOrderRG: return "RG"; + case ImageChannelOrderRA: return "RA"; + case ImageChannelOrderRGB: return "RGB"; + case ImageChannelOrderRGBA: return "RGBA"; + case ImageChannelOrderBGRA: return "BGRA"; + case ImageChannelOrderARGB: return "ARGB"; + case ImageChannelOrderIntensity: return "Intensity"; + case ImageChannelOrderLuminance: return "Luminance"; + case ImageChannelOrderRx: return "Rx"; + case ImageChannelOrderRGx: return "RGx"; + case ImageChannelOrderRGBx: return "RGBx"; + case ImageChannelOrderDepth: return "Depth"; + case ImageChannelOrderDepthStencil: return "DepthStencil"; + case ImageChannelOrdersRGB: return "sRGB"; + case ImageChannelOrdersRGBx: return "sRGBx"; + case ImageChannelOrdersRGBA: return "sRGBA"; + case ImageChannelOrdersBGRA: return "sBGRA"; + case ImageChannelOrderABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelDataTypeToString(ImageChannelDataType value) { + switch (value) { + case ImageChannelDataTypeSnormInt8: return "SnormInt8"; + case ImageChannelDataTypeSnormInt16: return "SnormInt16"; + case ImageChannelDataTypeUnormInt8: return "UnormInt8"; + case ImageChannelDataTypeUnormInt16: return "UnormInt16"; + case ImageChannelDataTypeUnormShort565: return "UnormShort565"; + case ImageChannelDataTypeUnormShort555: return "UnormShort555"; + case ImageChannelDataTypeUnormInt101010: return "UnormInt101010"; + case ImageChannelDataTypeSignedInt8: return "SignedInt8"; + case ImageChannelDataTypeSignedInt16: return "SignedInt16"; + case ImageChannelDataTypeSignedInt32: return "SignedInt32"; + case ImageChannelDataTypeUnsignedInt8: return "UnsignedInt8"; + case ImageChannelDataTypeUnsignedInt16: return "UnsignedInt16"; + case ImageChannelDataTypeUnsignedInt32: return "UnsignedInt32"; + case ImageChannelDataTypeHalfFloat: return "HalfFloat"; + case ImageChannelDataTypeFloat: return "Float"; + case ImageChannelDataTypeUnormInt24: return "UnormInt24"; + case ImageChannelDataTypeUnormInt101010_2: return "UnormInt101010_2"; + case ImageChannelDataTypeUnormInt10X6EXT: return "UnormInt10X6EXT"; + case ImageChannelDataTypeUnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case ImageChannelDataTypeUnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case ImageChannelDataTypeUnormInt2_101010EXT: return "UnormInt2_101010EXT"; + case ImageChannelDataTypeUnsignedInt10X6EXT: return "UnsignedInt10X6EXT"; + case ImageChannelDataTypeUnsignedInt12X4EXT: return "UnsignedInt12X4EXT"; + case ImageChannelDataTypeUnsignedInt14X2EXT: return "UnsignedInt14X2EXT"; + case ImageChannelDataTypeUnormInt12X4EXT: return "UnormInt12X4EXT"; + case ImageChannelDataTypeUnormInt14X2EXT: return "UnormInt14X2EXT"; + default: return "Unknown"; + } +} + +inline const char* FPRoundingModeToString(FPRoundingMode value) { + switch (value) { + case FPRoundingModeRTE: return "RTE"; + case FPRoundingModeRTZ: return "RTZ"; + case FPRoundingModeRTP: return "RTP"; + case FPRoundingModeRTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* LinkageTypeToString(LinkageType value) { + switch (value) { + case LinkageTypeExport: return "Export"; + case LinkageTypeImport: return "Import"; + case LinkageTypeLinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* AccessQualifierToString(AccessQualifier value) { + switch (value) { + case AccessQualifierReadOnly: return "ReadOnly"; + case AccessQualifierWriteOnly: return "WriteOnly"; + case AccessQualifierReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* FunctionParameterAttributeToString(FunctionParameterAttribute value) { + switch (value) { + case FunctionParameterAttributeZext: return "Zext"; + case FunctionParameterAttributeSext: return "Sext"; + case FunctionParameterAttributeByVal: return "ByVal"; + case FunctionParameterAttributeSret: return "Sret"; + case FunctionParameterAttributeNoAlias: return "NoAlias"; + case FunctionParameterAttributeNoCapture: return "NoCapture"; + case FunctionParameterAttributeNoWrite: return "NoWrite"; + case FunctionParameterAttributeNoReadWrite: return "NoReadWrite"; + case FunctionParameterAttributeRuntimeAlignedINTEL: return "RuntimeAlignedINTEL"; + default: return "Unknown"; + } +} + +inline const char* DecorationToString(Decoration value) { + switch (value) { + case DecorationRelaxedPrecision: return "RelaxedPrecision"; + case DecorationSpecId: return "SpecId"; + case DecorationBlock: return "Block"; + case DecorationBufferBlock: return "BufferBlock"; + case DecorationRowMajor: return "RowMajor"; + case DecorationColMajor: return "ColMajor"; + case DecorationArrayStride: return "ArrayStride"; + case DecorationMatrixStride: return "MatrixStride"; + case DecorationGLSLShared: return "GLSLShared"; + case DecorationGLSLPacked: return "GLSLPacked"; + case DecorationCPacked: return "CPacked"; + case DecorationBuiltIn: return "BuiltIn"; + case DecorationNoPerspective: return "NoPerspective"; + case DecorationFlat: return "Flat"; + case DecorationPatch: return "Patch"; + case DecorationCentroid: return "Centroid"; + case DecorationSample: return "Sample"; + case DecorationInvariant: return "Invariant"; + case DecorationRestrict: return "Restrict"; + case DecorationAliased: return "Aliased"; + case DecorationVolatile: return "Volatile"; + case DecorationConstant: return "Constant"; + case DecorationCoherent: return "Coherent"; + case DecorationNonWritable: return "NonWritable"; + case DecorationNonReadable: return "NonReadable"; + case DecorationUniform: return "Uniform"; + case DecorationUniformId: return "UniformId"; + case DecorationSaturatedConversion: return "SaturatedConversion"; + case DecorationStream: return "Stream"; + case DecorationLocation: return "Location"; + case DecorationComponent: return "Component"; + case DecorationIndex: return "Index"; + case DecorationBinding: return "Binding"; + case DecorationDescriptorSet: return "DescriptorSet"; + case DecorationOffset: return "Offset"; + case DecorationXfbBuffer: return "XfbBuffer"; + case DecorationXfbStride: return "XfbStride"; + case DecorationFuncParamAttr: return "FuncParamAttr"; + case DecorationFPRoundingMode: return "FPRoundingMode"; + case DecorationFPFastMathMode: return "FPFastMathMode"; + case DecorationLinkageAttributes: return "LinkageAttributes"; + case DecorationNoContraction: return "NoContraction"; + case DecorationInputAttachmentIndex: return "InputAttachmentIndex"; + case DecorationAlignment: return "Alignment"; + case DecorationMaxByteOffset: return "MaxByteOffset"; + case DecorationAlignmentId: return "AlignmentId"; + case DecorationMaxByteOffsetId: return "MaxByteOffsetId"; + case DecorationNoSignedWrap: return "NoSignedWrap"; + case DecorationNoUnsignedWrap: return "NoUnsignedWrap"; + case DecorationWeightTextureQCOM: return "WeightTextureQCOM"; + case DecorationBlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case DecorationBlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationNodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case DecorationNodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case DecorationTrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case DecorationPayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case DecorationPayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case DecorationPayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case DecorationPayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case DecorationPayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveEXT: return "PerPrimitiveEXT"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + case DecorationPerVertexKHR: return "PerVertexKHR"; + case DecorationNonUniform: return "NonUniform"; + case DecorationRestrictPointer: return "RestrictPointer"; + case DecorationAliasedPointer: return "AliasedPointer"; + case DecorationHitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case DecorationBindlessSamplerNV: return "BindlessSamplerNV"; + case DecorationBindlessImageNV: return "BindlessImageNV"; + case DecorationBoundSamplerNV: return "BoundSamplerNV"; + case DecorationBoundImageNV: return "BoundImageNV"; + case DecorationSIMTCallINTEL: return "SIMTCallINTEL"; + case DecorationReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case DecorationClobberINTEL: return "ClobberINTEL"; + case DecorationSideEffectsINTEL: return "SideEffectsINTEL"; + case DecorationVectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case DecorationFuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case DecorationVectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case DecorationStackCallINTEL: return "StackCallINTEL"; + case DecorationGlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case DecorationCounterBuffer: return "CounterBuffer"; + case DecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case DecorationUserTypeGOOGLE: return "UserTypeGOOGLE"; + case DecorationFunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case DecorationFunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case DecorationRegisterINTEL: return "RegisterINTEL"; + case DecorationMemoryINTEL: return "MemoryINTEL"; + case DecorationNumbanksINTEL: return "NumbanksINTEL"; + case DecorationBankwidthINTEL: return "BankwidthINTEL"; + case DecorationMaxPrivateCopiesINTEL: return "MaxPrivateCopiesINTEL"; + case DecorationSinglepumpINTEL: return "SinglepumpINTEL"; + case DecorationDoublepumpINTEL: return "DoublepumpINTEL"; + case DecorationMaxReplicatesINTEL: return "MaxReplicatesINTEL"; + case DecorationSimpleDualPortINTEL: return "SimpleDualPortINTEL"; + case DecorationMergeINTEL: return "MergeINTEL"; + case DecorationBankBitsINTEL: return "BankBitsINTEL"; + case DecorationForcePow2DepthINTEL: return "ForcePow2DepthINTEL"; + case DecorationStridesizeINTEL: return "StridesizeINTEL"; + case DecorationWordsizeINTEL: return "WordsizeINTEL"; + case DecorationTrueDualPortINTEL: return "TrueDualPortINTEL"; + case DecorationBurstCoalesceINTEL: return "BurstCoalesceINTEL"; + case DecorationCacheSizeINTEL: return "CacheSizeINTEL"; + case DecorationDontStaticallyCoalesceINTEL: return "DontStaticallyCoalesceINTEL"; + case DecorationPrefetchINTEL: return "PrefetchINTEL"; + case DecorationStallEnableINTEL: return "StallEnableINTEL"; + case DecorationFuseLoopsInFunctionINTEL: return "FuseLoopsInFunctionINTEL"; + case DecorationMathOpDSPModeINTEL: return "MathOpDSPModeINTEL"; + case DecorationAliasScopeINTEL: return "AliasScopeINTEL"; + case DecorationNoAliasINTEL: return "NoAliasINTEL"; + case DecorationInitiationIntervalINTEL: return "InitiationIntervalINTEL"; + case DecorationMaxConcurrencyINTEL: return "MaxConcurrencyINTEL"; + case DecorationPipelineEnableINTEL: return "PipelineEnableINTEL"; + case DecorationBufferLocationINTEL: return "BufferLocationINTEL"; + case DecorationIOPipeStorageINTEL: return "IOPipeStorageINTEL"; + case DecorationFunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case DecorationSingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case DecorationVectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case DecorationMediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case DecorationStallFreeINTEL: return "StallFreeINTEL"; + case DecorationFPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case DecorationLatencyControlLabelINTEL: return "LatencyControlLabelINTEL"; + case DecorationLatencyControlConstraintINTEL: return "LatencyControlConstraintINTEL"; + case DecorationConduitKernelArgumentINTEL: return "ConduitKernelArgumentINTEL"; + case DecorationRegisterMapKernelArgumentINTEL: return "RegisterMapKernelArgumentINTEL"; + case DecorationMMHostInterfaceAddressWidthINTEL: return "MMHostInterfaceAddressWidthINTEL"; + case DecorationMMHostInterfaceDataWidthINTEL: return "MMHostInterfaceDataWidthINTEL"; + case DecorationMMHostInterfaceLatencyINTEL: return "MMHostInterfaceLatencyINTEL"; + case DecorationMMHostInterfaceReadWriteModeINTEL: return "MMHostInterfaceReadWriteModeINTEL"; + case DecorationMMHostInterfaceMaxBurstINTEL: return "MMHostInterfaceMaxBurstINTEL"; + case DecorationMMHostInterfaceWaitRequestINTEL: return "MMHostInterfaceWaitRequestINTEL"; + case DecorationStableKernelArgumentINTEL: return "StableKernelArgumentINTEL"; + case DecorationHostAccessINTEL: return "HostAccessINTEL"; + case DecorationInitModeINTEL: return "InitModeINTEL"; + case DecorationImplementInRegisterMapINTEL: return "ImplementInRegisterMapINTEL"; + case DecorationCacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case DecorationCacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* BuiltInToString(BuiltIn value) { + switch (value) { + case BuiltInPosition: return "Position"; + case BuiltInPointSize: return "PointSize"; + case BuiltInClipDistance: return "ClipDistance"; + case BuiltInCullDistance: return "CullDistance"; + case BuiltInVertexId: return "VertexId"; + case BuiltInInstanceId: return "InstanceId"; + case BuiltInPrimitiveId: return "PrimitiveId"; + case BuiltInInvocationId: return "InvocationId"; + case BuiltInLayer: return "Layer"; + case BuiltInViewportIndex: return "ViewportIndex"; + case BuiltInTessLevelOuter: return "TessLevelOuter"; + case BuiltInTessLevelInner: return "TessLevelInner"; + case BuiltInTessCoord: return "TessCoord"; + case BuiltInPatchVertices: return "PatchVertices"; + case BuiltInFragCoord: return "FragCoord"; + case BuiltInPointCoord: return "PointCoord"; + case BuiltInFrontFacing: return "FrontFacing"; + case BuiltInSampleId: return "SampleId"; + case BuiltInSamplePosition: return "SamplePosition"; + case BuiltInSampleMask: return "SampleMask"; + case BuiltInFragDepth: return "FragDepth"; + case BuiltInHelperInvocation: return "HelperInvocation"; + case BuiltInNumWorkgroups: return "NumWorkgroups"; + case BuiltInWorkgroupSize: return "WorkgroupSize"; + case BuiltInWorkgroupId: return "WorkgroupId"; + case BuiltInLocalInvocationId: return "LocalInvocationId"; + case BuiltInGlobalInvocationId: return "GlobalInvocationId"; + case BuiltInLocalInvocationIndex: return "LocalInvocationIndex"; + case BuiltInWorkDim: return "WorkDim"; + case BuiltInGlobalSize: return "GlobalSize"; + case BuiltInEnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case BuiltInGlobalOffset: return "GlobalOffset"; + case BuiltInGlobalLinearId: return "GlobalLinearId"; + case BuiltInSubgroupSize: return "SubgroupSize"; + case BuiltInSubgroupMaxSize: return "SubgroupMaxSize"; + case BuiltInNumSubgroups: return "NumSubgroups"; + case BuiltInNumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case BuiltInSubgroupId: return "SubgroupId"; + case BuiltInSubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case BuiltInVertexIndex: return "VertexIndex"; + case BuiltInInstanceIndex: return "InstanceIndex"; + case BuiltInCoreIDARM: return "CoreIDARM"; + case BuiltInCoreCountARM: return "CoreCountARM"; + case BuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case BuiltInWarpIDARM: return "WarpIDARM"; + case BuiltInWarpMaxIDARM: return "WarpMaxIDARM"; + case BuiltInSubgroupEqMask: return "SubgroupEqMask"; + case BuiltInSubgroupGeMask: return "SubgroupGeMask"; + case BuiltInSubgroupGtMask: return "SubgroupGtMask"; + case BuiltInSubgroupLeMask: return "SubgroupLeMask"; + case BuiltInSubgroupLtMask: return "SubgroupLtMask"; + case BuiltInBaseVertex: return "BaseVertex"; + case BuiltInBaseInstance: return "BaseInstance"; + case BuiltInDrawIndex: return "DrawIndex"; + case BuiltInPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case BuiltInDeviceIndex: return "DeviceIndex"; + case BuiltInViewIndex: return "ViewIndex"; + case BuiltInShadingRateKHR: return "ShadingRateKHR"; + case BuiltInTileOffsetQCOM: return "TileOffsetQCOM"; + case BuiltInTileDimensionQCOM: return "TileDimensionQCOM"; + case BuiltInTileApronSizeQCOM: return "TileApronSizeQCOM"; + case BuiltInBaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case BuiltInBaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case BuiltInBaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case BuiltInBaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case BuiltInBaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case BuiltInBaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case BuiltInBaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case BuiltInFragStencilRefEXT: return "FragStencilRefEXT"; + case BuiltInRemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case BuiltInShaderIndexAMDX: return "ShaderIndexAMDX"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInFullyCoveredEXT: return "FullyCoveredEXT"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInBaryCoordKHR: return "BaryCoordKHR"; + case BuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + case BuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case BuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case BuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case BuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTNV: return "HitTNV"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case BuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case BuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case BuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInHitIsSphereNV: return "HitIsSphereNV"; + case BuiltInHitIsLSSNV: return "HitIsLSSNV"; + case BuiltInHitSpherePositionNV: return "HitSpherePositionNV"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + case BuiltInHitLSSPositionsNV: return "HitLSSPositionsNV"; + case BuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case BuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case BuiltInHitSphereRadiusNV: return "HitSphereRadiusNV"; + case BuiltInHitLSSRadiiNV: return "HitLSSRadiiNV"; + case BuiltInClusterIDNV: return "ClusterIDNV"; + case BuiltInCullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* ScopeToString(Scope value) { + switch (value) { + case ScopeCrossDevice: return "CrossDevice"; + case ScopeDevice: return "Device"; + case ScopeWorkgroup: return "Workgroup"; + case ScopeSubgroup: return "Subgroup"; + case ScopeInvocation: return "Invocation"; + case ScopeQueueFamily: return "QueueFamily"; + case ScopeShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* GroupOperationToString(GroupOperation value) { + switch (value) { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + default: return "Unknown"; + } +} + +inline const char* KernelEnqueueFlagsToString(KernelEnqueueFlags value) { + switch (value) { + case KernelEnqueueFlagsNoWait: return "NoWait"; + case KernelEnqueueFlagsWaitKernel: return "WaitKernel"; + case KernelEnqueueFlagsWaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* CapabilityToString(Capability value) { + switch (value) { + case CapabilityMatrix: return "Matrix"; + case CapabilityShader: return "Shader"; + case CapabilityGeometry: return "Geometry"; + case CapabilityTessellation: return "Tessellation"; + case CapabilityAddresses: return "Addresses"; + case CapabilityLinkage: return "Linkage"; + case CapabilityKernel: return "Kernel"; + case CapabilityVector16: return "Vector16"; + case CapabilityFloat16Buffer: return "Float16Buffer"; + case CapabilityFloat16: return "Float16"; + case CapabilityFloat64: return "Float64"; + case CapabilityInt64: return "Int64"; + case CapabilityInt64Atomics: return "Int64Atomics"; + case CapabilityImageBasic: return "ImageBasic"; + case CapabilityImageReadWrite: return "ImageReadWrite"; + case CapabilityImageMipmap: return "ImageMipmap"; + case CapabilityPipes: return "Pipes"; + case CapabilityGroups: return "Groups"; + case CapabilityDeviceEnqueue: return "DeviceEnqueue"; + case CapabilityLiteralSampler: return "LiteralSampler"; + case CapabilityAtomicStorage: return "AtomicStorage"; + case CapabilityInt16: return "Int16"; + case CapabilityTessellationPointSize: return "TessellationPointSize"; + case CapabilityGeometryPointSize: return "GeometryPointSize"; + case CapabilityImageGatherExtended: return "ImageGatherExtended"; + case CapabilityStorageImageMultisample: return "StorageImageMultisample"; + case CapabilityUniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case CapabilitySampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case CapabilityStorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case CapabilityStorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case CapabilityClipDistance: return "ClipDistance"; + case CapabilityCullDistance: return "CullDistance"; + case CapabilityImageCubeArray: return "ImageCubeArray"; + case CapabilitySampleRateShading: return "SampleRateShading"; + case CapabilityImageRect: return "ImageRect"; + case CapabilitySampledRect: return "SampledRect"; + case CapabilityGenericPointer: return "GenericPointer"; + case CapabilityInt8: return "Int8"; + case CapabilityInputAttachment: return "InputAttachment"; + case CapabilitySparseResidency: return "SparseResidency"; + case CapabilityMinLod: return "MinLod"; + case CapabilitySampled1D: return "Sampled1D"; + case CapabilityImage1D: return "Image1D"; + case CapabilitySampledCubeArray: return "SampledCubeArray"; + case CapabilitySampledBuffer: return "SampledBuffer"; + case CapabilityImageBuffer: return "ImageBuffer"; + case CapabilityImageMSArray: return "ImageMSArray"; + case CapabilityStorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case CapabilityImageQuery: return "ImageQuery"; + case CapabilityDerivativeControl: return "DerivativeControl"; + case CapabilityInterpolationFunction: return "InterpolationFunction"; + case CapabilityTransformFeedback: return "TransformFeedback"; + case CapabilityGeometryStreams: return "GeometryStreams"; + case CapabilityStorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case CapabilityStorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case CapabilityMultiViewport: return "MultiViewport"; + case CapabilitySubgroupDispatch: return "SubgroupDispatch"; + case CapabilityNamedBarrier: return "NamedBarrier"; + case CapabilityPipeStorage: return "PipeStorage"; + case CapabilityGroupNonUniform: return "GroupNonUniform"; + case CapabilityGroupNonUniformVote: return "GroupNonUniformVote"; + case CapabilityGroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case CapabilityGroupNonUniformBallot: return "GroupNonUniformBallot"; + case CapabilityGroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case CapabilityGroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case CapabilityGroupNonUniformClustered: return "GroupNonUniformClustered"; + case CapabilityGroupNonUniformQuad: return "GroupNonUniformQuad"; + case CapabilityShaderLayer: return "ShaderLayer"; + case CapabilityShaderViewportIndex: return "ShaderViewportIndex"; + case CapabilityUniformDecoration: return "UniformDecoration"; + case CapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + case CapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case CapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case CapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case CapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case CapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilityWorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case CapabilityStorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + case CapabilityVariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case CapabilityVariablePointers: return "VariablePointers"; + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + case CapabilityDenormPreserve: return "DenormPreserve"; + case CapabilityDenormFlushToZero: return "DenormFlushToZero"; + case CapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case CapabilityRoundingModeRTE: return "RoundingModeRTE"; + case CapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + case CapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case CapabilityRayQueryKHR: return "RayQueryKHR"; + case CapabilityUntypedPointersKHR: return "UntypedPointersKHR"; + case CapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case CapabilityRayTracingKHR: return "RayTracingKHR"; + case CapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case CapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case CapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case CapabilityTileShadingQCOM: return "TileShadingQCOM"; + case CapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityStencilExportEXT: return "StencilExportEXT"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case CapabilityInt64ImageEXT: return "Int64ImageEXT"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + case CapabilityShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case CapabilityQuadControlKHR: return "QuadControlKHR"; + case CapabilityBFloat16TypeKHR: return "BFloat16TypeKHR"; + case CapabilityBFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case CapabilityBFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilityMeshShadingEXT: return "MeshShadingEXT"; + case CapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case CapabilityComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case CapabilityShaderNonUniform: return "ShaderNonUniform"; + case CapabilityRuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case CapabilityInputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case CapabilityUniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case CapabilityStorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case CapabilityUniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case CapabilitySampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case CapabilityStorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case CapabilityStorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case CapabilityInputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case CapabilityUniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case CapabilityStorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case CapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case CapabilityVulkanMemoryModel: return "VulkanMemoryModel"; + case CapabilityVulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case CapabilityPhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case CapabilityComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityFragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case CapabilityFragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case CapabilityDemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case CapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case CapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case CapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case CapabilityBindlessTextureNV: return "BindlessTextureNV"; + case CapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case CapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case CapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case CapabilityRayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case CapabilityRawAccessChainsNV: return "RawAccessChainsNV"; + case CapabilityRayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case CapabilityRayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + case CapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case CapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case CapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case CapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case CapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case CapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case CapabilityRayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + case CapabilityTensorAddressingNV: return "TensorAddressingNV"; + case CapabilitySubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case CapabilitySubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case CapabilitySubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case CapabilitySubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case CapabilityRoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case CapabilityFloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case CapabilityIntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case CapabilityFunctionPointersINTEL: return "FunctionPointersINTEL"; + case CapabilityIndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case CapabilityAsmINTEL: return "AsmINTEL"; + case CapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case CapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case CapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case CapabilityVectorComputeINTEL: return "VectorComputeINTEL"; + case CapabilityVectorAnyINTEL: return "VectorAnyINTEL"; + case CapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + case CapabilitySubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case CapabilitySubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case CapabilitySubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case CapabilityVariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case CapabilityFunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case CapabilityFPGAMemoryAttributesINTEL: return "FPGAMemoryAttributesINTEL"; + case CapabilityFPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case CapabilityArbitraryPrecisionIntegersINTEL: return "ArbitraryPrecisionIntegersINTEL"; + case CapabilityArbitraryPrecisionFloatingPointINTEL: return "ArbitraryPrecisionFloatingPointINTEL"; + case CapabilityUnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case CapabilityFPGALoopControlsINTEL: return "FPGALoopControlsINTEL"; + case CapabilityKernelAttributesINTEL: return "KernelAttributesINTEL"; + case CapabilityFPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case CapabilityFPGAMemoryAccessesINTEL: return "FPGAMemoryAccessesINTEL"; + case CapabilityFPGAClusterAttributesINTEL: return "FPGAClusterAttributesINTEL"; + case CapabilityLoopFuseINTEL: return "LoopFuseINTEL"; + case CapabilityFPGADSPControlINTEL: return "FPGADSPControlINTEL"; + case CapabilityMemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case CapabilityFPGAInvocationPipeliningAttributesINTEL: return "FPGAInvocationPipeliningAttributesINTEL"; + case CapabilityFPGABufferLocationINTEL: return "FPGABufferLocationINTEL"; + case CapabilityArbitraryPrecisionFixedPointINTEL: return "ArbitraryPrecisionFixedPointINTEL"; + case CapabilityUSMStorageClassesINTEL: return "USMStorageClassesINTEL"; + case CapabilityRuntimeAlignedAttributeINTEL: return "RuntimeAlignedAttributeINTEL"; + case CapabilityIOPipesINTEL: return "IOPipesINTEL"; + case CapabilityBlockingPipesINTEL: return "BlockingPipesINTEL"; + case CapabilityFPGARegINTEL: return "FPGARegINTEL"; + case CapabilityDotProductInputAll: return "DotProductInputAll"; + case CapabilityDotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case CapabilityDotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case CapabilityDotProduct: return "DotProduct"; + case CapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case CapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case CapabilityReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case CapabilityBitInstructions: return "BitInstructions"; + case CapabilityGroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case CapabilityFloatControls2: return "FloatControls2"; + case CapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case CapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case CapabilityLongCompositesINTEL: return "LongCompositesINTEL"; + case CapabilityOptNoneEXT: return "OptNoneEXT"; + case CapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case CapabilityDebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case CapabilityBFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case CapabilitySplitBarrierINTEL: return "SplitBarrierINTEL"; + case CapabilityArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case CapabilityFPGAClusterAttributesV2INTEL: return "FPGAClusterAttributesV2INTEL"; + case CapabilityFPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case CapabilityTaskSequenceINTEL: return "TaskSequenceINTEL"; + case CapabilityFPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case CapabilityFPGALatencyControlINTEL: return "FPGALatencyControlINTEL"; + case CapabilityFPGAArgumentInterfacesINTEL: return "FPGAArgumentInterfacesINTEL"; + case CapabilityGlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case CapabilityGlobalVariableFPGADecorationsINTEL: return "GlobalVariableFPGADecorationsINTEL"; + case CapabilitySubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case CapabilitySubgroup2DBlockIOINTEL: return "Subgroup2DBlockIOINTEL"; + case CapabilitySubgroup2DBlockTransformINTEL: return "Subgroup2DBlockTransformINTEL"; + case CapabilitySubgroup2DBlockTransposeINTEL: return "Subgroup2DBlockTransposeINTEL"; + case CapabilitySubgroupMatrixMultiplyAccumulateINTEL: return "SubgroupMatrixMultiplyAccumulateINTEL"; + case CapabilityTernaryBitwiseFunctionINTEL: return "TernaryBitwiseFunctionINTEL"; + case CapabilityGroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case CapabilityTensorFloat32RoundingINTEL: return "TensorFloat32RoundingINTEL"; + case CapabilityMaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case CapabilityCacheControlsINTEL: return "CacheControlsINTEL"; + case CapabilityRegisterLimitsINTEL: return "RegisterLimitsINTEL"; + default: return "Unknown"; + } +} + +inline const char* RayQueryIntersectionToString(RayQueryIntersection value) { + switch (value) { + case RayQueryIntersectionRayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case RayQueryIntersectionRayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCommittedIntersectionTypeToString(RayQueryCommittedIntersectionType value) { + switch (value) { + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCandidateIntersectionTypeToString(RayQueryCandidateIntersectionType value) { + switch (value) { + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* FPDenormModeToString(FPDenormMode value) { + switch (value) { + case FPDenormModePreserve: return "Preserve"; + case FPDenormModeFlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* FPOperationModeToString(FPOperationMode value) { + switch (value) { + case FPOperationModeIEEE: return "IEEE"; + case FPOperationModeALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* QuantizationModesToString(QuantizationModes value) { + switch (value) { + case QuantizationModesTRN: return "TRN"; + case QuantizationModesTRN_ZERO: return "TRN_ZERO"; + case QuantizationModesRND: return "RND"; + case QuantizationModesRND_ZERO: return "RND_ZERO"; + case QuantizationModesRND_INF: return "RND_INF"; + case QuantizationModesRND_MIN_INF: return "RND_MIN_INF"; + case QuantizationModesRND_CONV: return "RND_CONV"; + case QuantizationModesRND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* OverflowModesToString(OverflowModes value) { + switch (value) { + case OverflowModesWRAP: return "WRAP"; + case OverflowModesSAT: return "SAT"; + case OverflowModesSAT_ZERO: return "SAT_ZERO"; + case OverflowModesSAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* PackedVectorFormatToString(PackedVectorFormat value) { + switch (value) { + case PackedVectorFormatPackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixLayoutToString(CooperativeMatrixLayout value) { + switch (value) { + case CooperativeMatrixLayoutRowMajorKHR: return "RowMajorKHR"; + case CooperativeMatrixLayoutColumnMajorKHR: return "ColumnMajorKHR"; + case CooperativeMatrixLayoutRowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case CooperativeMatrixLayoutColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixUseToString(CooperativeMatrixUse value) { + switch (value) { + case CooperativeMatrixUseMatrixAKHR: return "MatrixAKHR"; + case CooperativeMatrixUseMatrixBKHR: return "MatrixBKHR"; + case CooperativeMatrixUseMatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* TensorClampModeToString(TensorClampMode value) { + switch (value) { + case TensorClampModeUndefined: return "Undefined"; + case TensorClampModeConstant: return "Constant"; + case TensorClampModeClampToEdge: return "ClampToEdge"; + case TensorClampModeRepeat: return "Repeat"; + case TensorClampModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* InitializationModeQualifierToString(InitializationModeQualifier value) { + switch (value) { + case InitializationModeQualifierInitOnDeviceReprogramINTEL: return "InitOnDeviceReprogramINTEL"; + case InitializationModeQualifierInitOnDeviceResetINTEL: return "InitOnDeviceResetINTEL"; + default: return "Unknown"; + } +} + +inline const char* HostAccessQualifierToString(HostAccessQualifier value) { + switch (value) { + case HostAccessQualifierNoneINTEL: return "NoneINTEL"; + case HostAccessQualifierReadINTEL: return "ReadINTEL"; + case HostAccessQualifierWriteINTEL: return "WriteINTEL"; + case HostAccessQualifierReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* LoadCacheControlToString(LoadCacheControl value) { + switch (value) { + case LoadCacheControlUncachedINTEL: return "UncachedINTEL"; + case LoadCacheControlCachedINTEL: return "CachedINTEL"; + case LoadCacheControlStreamingINTEL: return "StreamingINTEL"; + case LoadCacheControlInvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case LoadCacheControlConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* StoreCacheControlToString(StoreCacheControl value) { + switch (value) { + case StoreCacheControlUncachedINTEL: return "UncachedINTEL"; + case StoreCacheControlWriteThroughINTEL: return "WriteThroughINTEL"; + case StoreCacheControlWriteBackINTEL: return "WriteBackINTEL"; + case StoreCacheControlStreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* NamedMaximumNumberOfRegistersToString(NamedMaximumNumberOfRegisters value) { + switch (value) { + case NamedMaximumNumberOfRegistersAutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* FPEncodingToString(FPEncoding value) { + switch (value) { + case FPEncodingBFloat16KHR: return "BFloat16KHR"; + default: return "Unknown"; + } +} + +inline const char* CooperativeVectorMatrixLayoutToString(CooperativeVectorMatrixLayout value) { + switch (value) { + case CooperativeVectorMatrixLayoutRowMajorNV: return "RowMajorNV"; + case CooperativeVectorMatrixLayoutColumnMajorNV: return "ColumnMajorNV"; + case CooperativeVectorMatrixLayoutInferencingOptimalNV: return "InferencingOptimalNV"; + case CooperativeVectorMatrixLayoutTrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* ComponentTypeToString(ComponentType value) { + switch (value) { + case ComponentTypeFloat16NV: return "Float16NV"; + case ComponentTypeFloat32NV: return "Float32NV"; + case ComponentTypeFloat64NV: return "Float64NV"; + case ComponentTypeSignedInt8NV: return "SignedInt8NV"; + case ComponentTypeSignedInt16NV: return "SignedInt16NV"; + case ComponentTypeSignedInt32NV: return "SignedInt32NV"; + case ComponentTypeSignedInt64NV: return "SignedInt64NV"; + case ComponentTypeUnsignedInt8NV: return "UnsignedInt8NV"; + case ComponentTypeUnsignedInt16NV: return "UnsignedInt16NV"; + case ComponentTypeUnsignedInt32NV: return "UnsignedInt32NV"; + case ComponentTypeUnsignedInt64NV: return "UnsignedInt64NV"; + case ComponentTypeSignedInt8PackedNV: return "SignedInt8PackedNV"; + case ComponentTypeUnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case ComponentTypeFloatE4M3NV: return "FloatE4M3NV"; + case ComponentTypeFloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* OpToString(Op value) { + switch (value) { + case OpNop: return "OpNop"; + case OpUndef: return "OpUndef"; + case OpSourceContinued: return "OpSourceContinued"; + case OpSource: return "OpSource"; + case OpSourceExtension: return "OpSourceExtension"; + case OpName: return "OpName"; + case OpMemberName: return "OpMemberName"; + case OpString: return "OpString"; + case OpLine: return "OpLine"; + case OpExtension: return "OpExtension"; + case OpExtInstImport: return "OpExtInstImport"; + case OpExtInst: return "OpExtInst"; + case OpMemoryModel: return "OpMemoryModel"; + case OpEntryPoint: return "OpEntryPoint"; + case OpExecutionMode: return "OpExecutionMode"; + case OpCapability: return "OpCapability"; + case OpTypeVoid: return "OpTypeVoid"; + case OpTypeBool: return "OpTypeBool"; + case OpTypeInt: return "OpTypeInt"; + case OpTypeFloat: return "OpTypeFloat"; + case OpTypeVector: return "OpTypeVector"; + case OpTypeMatrix: return "OpTypeMatrix"; + case OpTypeImage: return "OpTypeImage"; + case OpTypeSampler: return "OpTypeSampler"; + case OpTypeSampledImage: return "OpTypeSampledImage"; + case OpTypeArray: return "OpTypeArray"; + case OpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case OpTypeStruct: return "OpTypeStruct"; + case OpTypeOpaque: return "OpTypeOpaque"; + case OpTypePointer: return "OpTypePointer"; + case OpTypeFunction: return "OpTypeFunction"; + case OpTypeEvent: return "OpTypeEvent"; + case OpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case OpTypeReserveId: return "OpTypeReserveId"; + case OpTypeQueue: return "OpTypeQueue"; + case OpTypePipe: return "OpTypePipe"; + case OpTypeForwardPointer: return "OpTypeForwardPointer"; + case OpConstantTrue: return "OpConstantTrue"; + case OpConstantFalse: return "OpConstantFalse"; + case OpConstant: return "OpConstant"; + case OpConstantComposite: return "OpConstantComposite"; + case OpConstantSampler: return "OpConstantSampler"; + case OpConstantNull: return "OpConstantNull"; + case OpSpecConstantTrue: return "OpSpecConstantTrue"; + case OpSpecConstantFalse: return "OpSpecConstantFalse"; + case OpSpecConstant: return "OpSpecConstant"; + case OpSpecConstantComposite: return "OpSpecConstantComposite"; + case OpSpecConstantOp: return "OpSpecConstantOp"; + case OpFunction: return "OpFunction"; + case OpFunctionParameter: return "OpFunctionParameter"; + case OpFunctionEnd: return "OpFunctionEnd"; + case OpFunctionCall: return "OpFunctionCall"; + case OpVariable: return "OpVariable"; + case OpImageTexelPointer: return "OpImageTexelPointer"; + case OpLoad: return "OpLoad"; + case OpStore: return "OpStore"; + case OpCopyMemory: return "OpCopyMemory"; + case OpCopyMemorySized: return "OpCopyMemorySized"; + case OpAccessChain: return "OpAccessChain"; + case OpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case OpPtrAccessChain: return "OpPtrAccessChain"; + case OpArrayLength: return "OpArrayLength"; + case OpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case OpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case OpDecorate: return "OpDecorate"; + case OpMemberDecorate: return "OpMemberDecorate"; + case OpDecorationGroup: return "OpDecorationGroup"; + case OpGroupDecorate: return "OpGroupDecorate"; + case OpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case OpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case OpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case OpVectorShuffle: return "OpVectorShuffle"; + case OpCompositeConstruct: return "OpCompositeConstruct"; + case OpCompositeExtract: return "OpCompositeExtract"; + case OpCompositeInsert: return "OpCompositeInsert"; + case OpCopyObject: return "OpCopyObject"; + case OpTranspose: return "OpTranspose"; + case OpSampledImage: return "OpSampledImage"; + case OpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case OpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case OpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case OpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case OpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case OpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case OpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case OpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case OpImageFetch: return "OpImageFetch"; + case OpImageGather: return "OpImageGather"; + case OpImageDrefGather: return "OpImageDrefGather"; + case OpImageRead: return "OpImageRead"; + case OpImageWrite: return "OpImageWrite"; + case OpImage: return "OpImage"; + case OpImageQueryFormat: return "OpImageQueryFormat"; + case OpImageQueryOrder: return "OpImageQueryOrder"; + case OpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case OpImageQuerySize: return "OpImageQuerySize"; + case OpImageQueryLod: return "OpImageQueryLod"; + case OpImageQueryLevels: return "OpImageQueryLevels"; + case OpImageQuerySamples: return "OpImageQuerySamples"; + case OpConvertFToU: return "OpConvertFToU"; + case OpConvertFToS: return "OpConvertFToS"; + case OpConvertSToF: return "OpConvertSToF"; + case OpConvertUToF: return "OpConvertUToF"; + case OpUConvert: return "OpUConvert"; + case OpSConvert: return "OpSConvert"; + case OpFConvert: return "OpFConvert"; + case OpQuantizeToF16: return "OpQuantizeToF16"; + case OpConvertPtrToU: return "OpConvertPtrToU"; + case OpSatConvertSToU: return "OpSatConvertSToU"; + case OpSatConvertUToS: return "OpSatConvertUToS"; + case OpConvertUToPtr: return "OpConvertUToPtr"; + case OpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case OpGenericCastToPtr: return "OpGenericCastToPtr"; + case OpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case OpBitcast: return "OpBitcast"; + case OpSNegate: return "OpSNegate"; + case OpFNegate: return "OpFNegate"; + case OpIAdd: return "OpIAdd"; + case OpFAdd: return "OpFAdd"; + case OpISub: return "OpISub"; + case OpFSub: return "OpFSub"; + case OpIMul: return "OpIMul"; + case OpFMul: return "OpFMul"; + case OpUDiv: return "OpUDiv"; + case OpSDiv: return "OpSDiv"; + case OpFDiv: return "OpFDiv"; + case OpUMod: return "OpUMod"; + case OpSRem: return "OpSRem"; + case OpSMod: return "OpSMod"; + case OpFRem: return "OpFRem"; + case OpFMod: return "OpFMod"; + case OpVectorTimesScalar: return "OpVectorTimesScalar"; + case OpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case OpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case OpMatrixTimesVector: return "OpMatrixTimesVector"; + case OpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case OpOuterProduct: return "OpOuterProduct"; + case OpDot: return "OpDot"; + case OpIAddCarry: return "OpIAddCarry"; + case OpISubBorrow: return "OpISubBorrow"; + case OpUMulExtended: return "OpUMulExtended"; + case OpSMulExtended: return "OpSMulExtended"; + case OpAny: return "OpAny"; + case OpAll: return "OpAll"; + case OpIsNan: return "OpIsNan"; + case OpIsInf: return "OpIsInf"; + case OpIsFinite: return "OpIsFinite"; + case OpIsNormal: return "OpIsNormal"; + case OpSignBitSet: return "OpSignBitSet"; + case OpLessOrGreater: return "OpLessOrGreater"; + case OpOrdered: return "OpOrdered"; + case OpUnordered: return "OpUnordered"; + case OpLogicalEqual: return "OpLogicalEqual"; + case OpLogicalNotEqual: return "OpLogicalNotEqual"; + case OpLogicalOr: return "OpLogicalOr"; + case OpLogicalAnd: return "OpLogicalAnd"; + case OpLogicalNot: return "OpLogicalNot"; + case OpSelect: return "OpSelect"; + case OpIEqual: return "OpIEqual"; + case OpINotEqual: return "OpINotEqual"; + case OpUGreaterThan: return "OpUGreaterThan"; + case OpSGreaterThan: return "OpSGreaterThan"; + case OpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case OpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case OpULessThan: return "OpULessThan"; + case OpSLessThan: return "OpSLessThan"; + case OpULessThanEqual: return "OpULessThanEqual"; + case OpSLessThanEqual: return "OpSLessThanEqual"; + case OpFOrdEqual: return "OpFOrdEqual"; + case OpFUnordEqual: return "OpFUnordEqual"; + case OpFOrdNotEqual: return "OpFOrdNotEqual"; + case OpFUnordNotEqual: return "OpFUnordNotEqual"; + case OpFOrdLessThan: return "OpFOrdLessThan"; + case OpFUnordLessThan: return "OpFUnordLessThan"; + case OpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case OpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case OpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case OpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case OpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case OpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case OpShiftRightLogical: return "OpShiftRightLogical"; + case OpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case OpShiftLeftLogical: return "OpShiftLeftLogical"; + case OpBitwiseOr: return "OpBitwiseOr"; + case OpBitwiseXor: return "OpBitwiseXor"; + case OpBitwiseAnd: return "OpBitwiseAnd"; + case OpNot: return "OpNot"; + case OpBitFieldInsert: return "OpBitFieldInsert"; + case OpBitFieldSExtract: return "OpBitFieldSExtract"; + case OpBitFieldUExtract: return "OpBitFieldUExtract"; + case OpBitReverse: return "OpBitReverse"; + case OpBitCount: return "OpBitCount"; + case OpDPdx: return "OpDPdx"; + case OpDPdy: return "OpDPdy"; + case OpFwidth: return "OpFwidth"; + case OpDPdxFine: return "OpDPdxFine"; + case OpDPdyFine: return "OpDPdyFine"; + case OpFwidthFine: return "OpFwidthFine"; + case OpDPdxCoarse: return "OpDPdxCoarse"; + case OpDPdyCoarse: return "OpDPdyCoarse"; + case OpFwidthCoarse: return "OpFwidthCoarse"; + case OpEmitVertex: return "OpEmitVertex"; + case OpEndPrimitive: return "OpEndPrimitive"; + case OpEmitStreamVertex: return "OpEmitStreamVertex"; + case OpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case OpControlBarrier: return "OpControlBarrier"; + case OpMemoryBarrier: return "OpMemoryBarrier"; + case OpAtomicLoad: return "OpAtomicLoad"; + case OpAtomicStore: return "OpAtomicStore"; + case OpAtomicExchange: return "OpAtomicExchange"; + case OpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case OpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case OpAtomicIIncrement: return "OpAtomicIIncrement"; + case OpAtomicIDecrement: return "OpAtomicIDecrement"; + case OpAtomicIAdd: return "OpAtomicIAdd"; + case OpAtomicISub: return "OpAtomicISub"; + case OpAtomicSMin: return "OpAtomicSMin"; + case OpAtomicUMin: return "OpAtomicUMin"; + case OpAtomicSMax: return "OpAtomicSMax"; + case OpAtomicUMax: return "OpAtomicUMax"; + case OpAtomicAnd: return "OpAtomicAnd"; + case OpAtomicOr: return "OpAtomicOr"; + case OpAtomicXor: return "OpAtomicXor"; + case OpPhi: return "OpPhi"; + case OpLoopMerge: return "OpLoopMerge"; + case OpSelectionMerge: return "OpSelectionMerge"; + case OpLabel: return "OpLabel"; + case OpBranch: return "OpBranch"; + case OpBranchConditional: return "OpBranchConditional"; + case OpSwitch: return "OpSwitch"; + case OpKill: return "OpKill"; + case OpReturn: return "OpReturn"; + case OpReturnValue: return "OpReturnValue"; + case OpUnreachable: return "OpUnreachable"; + case OpLifetimeStart: return "OpLifetimeStart"; + case OpLifetimeStop: return "OpLifetimeStop"; + case OpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case OpGroupWaitEvents: return "OpGroupWaitEvents"; + case OpGroupAll: return "OpGroupAll"; + case OpGroupAny: return "OpGroupAny"; + case OpGroupBroadcast: return "OpGroupBroadcast"; + case OpGroupIAdd: return "OpGroupIAdd"; + case OpGroupFAdd: return "OpGroupFAdd"; + case OpGroupFMin: return "OpGroupFMin"; + case OpGroupUMin: return "OpGroupUMin"; + case OpGroupSMin: return "OpGroupSMin"; + case OpGroupFMax: return "OpGroupFMax"; + case OpGroupUMax: return "OpGroupUMax"; + case OpGroupSMax: return "OpGroupSMax"; + case OpReadPipe: return "OpReadPipe"; + case OpWritePipe: return "OpWritePipe"; + case OpReservedReadPipe: return "OpReservedReadPipe"; + case OpReservedWritePipe: return "OpReservedWritePipe"; + case OpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case OpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case OpCommitReadPipe: return "OpCommitReadPipe"; + case OpCommitWritePipe: return "OpCommitWritePipe"; + case OpIsValidReserveId: return "OpIsValidReserveId"; + case OpGetNumPipePackets: return "OpGetNumPipePackets"; + case OpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case OpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case OpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case OpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case OpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case OpEnqueueMarker: return "OpEnqueueMarker"; + case OpEnqueueKernel: return "OpEnqueueKernel"; + case OpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case OpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case OpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case OpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case OpRetainEvent: return "OpRetainEvent"; + case OpReleaseEvent: return "OpReleaseEvent"; + case OpCreateUserEvent: return "OpCreateUserEvent"; + case OpIsValidEvent: return "OpIsValidEvent"; + case OpSetUserEventStatus: return "OpSetUserEventStatus"; + case OpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case OpGetDefaultQueue: return "OpGetDefaultQueue"; + case OpBuildNDRange: return "OpBuildNDRange"; + case OpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case OpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case OpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case OpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case OpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case OpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case OpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case OpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case OpImageSparseFetch: return "OpImageSparseFetch"; + case OpImageSparseGather: return "OpImageSparseGather"; + case OpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case OpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case OpNoLine: return "OpNoLine"; + case OpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case OpAtomicFlagClear: return "OpAtomicFlagClear"; + case OpImageSparseRead: return "OpImageSparseRead"; + case OpSizeOf: return "OpSizeOf"; + case OpTypePipeStorage: return "OpTypePipeStorage"; + case OpConstantPipeStorage: return "OpConstantPipeStorage"; + case OpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case OpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case OpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case OpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case OpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case OpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case OpModuleProcessed: return "OpModuleProcessed"; + case OpExecutionModeId: return "OpExecutionModeId"; + case OpDecorateId: return "OpDecorateId"; + case OpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case OpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case OpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case OpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case OpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case OpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case OpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case OpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case OpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case OpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case OpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case OpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case OpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case OpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case OpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case OpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case OpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case OpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case OpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case OpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case OpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case OpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case OpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case OpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case OpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case OpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case OpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case OpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case OpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case OpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case OpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case OpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case OpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case OpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case OpCopyLogical: return "OpCopyLogical"; + case OpPtrEqual: return "OpPtrEqual"; + case OpPtrNotEqual: return "OpPtrNotEqual"; + case OpPtrDiff: return "OpPtrDiff"; + case OpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case OpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case OpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case OpTerminateInvocation: return "OpTerminateInvocation"; + case OpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case OpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case OpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case OpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case OpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case OpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case OpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case OpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case OpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case OpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case OpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case OpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case OpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case OpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case OpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case OpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpSDot: return "OpSDot"; + case OpUDot: return "OpUDot"; + case OpSUDot: return "OpSUDot"; + case OpSDotAccSat: return "OpSDotAccSat"; + case OpUDotAccSat: return "OpUDotAccSat"; + case OpSUDotAccSat: return "OpSUDotAccSat"; + case OpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case OpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case OpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case OpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case OpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case OpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case OpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case OpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case OpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case OpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case OpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case OpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case OpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case OpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case OpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case OpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case OpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case OpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case OpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case OpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case OpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case OpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case OpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case OpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case OpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case OpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case OpReadClockKHR: return "OpReadClockKHR"; + case OpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case OpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case OpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case OpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case OpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case OpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case OpConstantStringAMDX: return "OpConstantStringAMDX"; + case OpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case OpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case OpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case OpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case OpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case OpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case OpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case OpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case OpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case OpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case OpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case OpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case OpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case OpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case OpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case OpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case OpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case OpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case OpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case OpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case OpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case OpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case OpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case OpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case OpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case OpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case OpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case OpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case OpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case OpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case OpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case OpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case OpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case OpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case OpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case OpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case OpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case OpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case OpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case OpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case OpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case OpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case OpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case OpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case OpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case OpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case OpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case OpTerminateRayNV: return "OpTerminateRayNV"; + case OpTraceNV: return "OpTraceNV"; + case OpTraceMotionNV: return "OpTraceMotionNV"; + case OpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableNV: return "OpExecuteCallableNV"; + case OpRayQueryGetClusterIdNV: return "OpRayQueryGetClusterIdNV"; + case OpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case OpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case OpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case OpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case OpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case OpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case OpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case OpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case OpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case OpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case OpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case OpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case OpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case OpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case OpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case OpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case OpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case OpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case OpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case OpConvertUToImageNV: return "OpConvertUToImageNV"; + case OpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case OpConvertImageToUNV: return "OpConvertImageToUNV"; + case OpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case OpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case OpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case OpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case OpRawAccessChainNV: return "OpRawAccessChainNV"; + case OpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case OpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case OpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case OpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case OpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case OpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case OpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case OpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case OpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case OpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case OpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + case OpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case OpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + case OpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case OpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case OpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case OpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case OpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case OpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case OpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case OpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case OpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case OpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case OpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case OpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case OpAbsISubINTEL: return "OpAbsISubINTEL"; + case OpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case OpIAddSatINTEL: return "OpIAddSatINTEL"; + case OpUAddSatINTEL: return "OpUAddSatINTEL"; + case OpIAverageINTEL: return "OpIAverageINTEL"; + case OpUAverageINTEL: return "OpUAverageINTEL"; + case OpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case OpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case OpISubSatINTEL: return "OpISubSatINTEL"; + case OpUSubSatINTEL: return "OpUSubSatINTEL"; + case OpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case OpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case OpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case OpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case OpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case OpAsmINTEL: return "OpAsmINTEL"; + case OpAsmCallINTEL: return "OpAsmCallINTEL"; + case OpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case OpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case OpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case OpExpectKHR: return "OpExpectKHR"; + case OpDecorateString: return "OpDecorateString"; + case OpMemberDecorateString: return "OpMemberDecorateString"; + case OpVmeImageINTEL: return "OpVmeImageINTEL"; + case OpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case OpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case OpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case OpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case OpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case OpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case OpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case OpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case OpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case OpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case OpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case OpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case OpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case OpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case OpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case OpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case OpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case OpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case OpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case OpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case OpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case OpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case OpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case OpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case OpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case OpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case OpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case OpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case OpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case OpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case OpArbitraryFloatSinCosPiINTEL: return "OpArbitraryFloatSinCosPiINTEL"; + case OpArbitraryFloatCastINTEL: return "OpArbitraryFloatCastINTEL"; + case OpArbitraryFloatCastFromIntINTEL: return "OpArbitraryFloatCastFromIntINTEL"; + case OpArbitraryFloatCastToIntINTEL: return "OpArbitraryFloatCastToIntINTEL"; + case OpArbitraryFloatAddINTEL: return "OpArbitraryFloatAddINTEL"; + case OpArbitraryFloatSubINTEL: return "OpArbitraryFloatSubINTEL"; + case OpArbitraryFloatMulINTEL: return "OpArbitraryFloatMulINTEL"; + case OpArbitraryFloatDivINTEL: return "OpArbitraryFloatDivINTEL"; + case OpArbitraryFloatGTINTEL: return "OpArbitraryFloatGTINTEL"; + case OpArbitraryFloatGEINTEL: return "OpArbitraryFloatGEINTEL"; + case OpArbitraryFloatLTINTEL: return "OpArbitraryFloatLTINTEL"; + case OpArbitraryFloatLEINTEL: return "OpArbitraryFloatLEINTEL"; + case OpArbitraryFloatEQINTEL: return "OpArbitraryFloatEQINTEL"; + case OpArbitraryFloatRecipINTEL: return "OpArbitraryFloatRecipINTEL"; + case OpArbitraryFloatRSqrtINTEL: return "OpArbitraryFloatRSqrtINTEL"; + case OpArbitraryFloatCbrtINTEL: return "OpArbitraryFloatCbrtINTEL"; + case OpArbitraryFloatHypotINTEL: return "OpArbitraryFloatHypotINTEL"; + case OpArbitraryFloatSqrtINTEL: return "OpArbitraryFloatSqrtINTEL"; + case OpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case OpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case OpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case OpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case OpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case OpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case OpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case OpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case OpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case OpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case OpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case OpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case OpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case OpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case OpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case OpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case OpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case OpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case OpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case OpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case OpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case OpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case OpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case OpLoopControlINTEL: return "OpLoopControlINTEL"; + case OpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case OpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case OpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case OpFixedSqrtINTEL: return "OpFixedSqrtINTEL"; + case OpFixedRecipINTEL: return "OpFixedRecipINTEL"; + case OpFixedRsqrtINTEL: return "OpFixedRsqrtINTEL"; + case OpFixedSinINTEL: return "OpFixedSinINTEL"; + case OpFixedCosINTEL: return "OpFixedCosINTEL"; + case OpFixedSinCosINTEL: return "OpFixedSinCosINTEL"; + case OpFixedSinPiINTEL: return "OpFixedSinPiINTEL"; + case OpFixedCosPiINTEL: return "OpFixedCosPiINTEL"; + case OpFixedSinCosPiINTEL: return "OpFixedSinCosPiINTEL"; + case OpFixedLogINTEL: return "OpFixedLogINTEL"; + case OpFixedExpINTEL: return "OpFixedExpINTEL"; + case OpPtrCastToCrossWorkgroupINTEL: return "OpPtrCastToCrossWorkgroupINTEL"; + case OpCrossWorkgroupCastToPtrINTEL: return "OpCrossWorkgroupCastToPtrINTEL"; + case OpReadPipeBlockingINTEL: return "OpReadPipeBlockingINTEL"; + case OpWritePipeBlockingINTEL: return "OpWritePipeBlockingINTEL"; + case OpFPGARegINTEL: return "OpFPGARegINTEL"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case OpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case OpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case OpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case OpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case OpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case OpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case OpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case OpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case OpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case OpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case OpTaskSequenceCreateINTEL: return "OpTaskSequenceCreateINTEL"; + case OpTaskSequenceAsyncINTEL: return "OpTaskSequenceAsyncINTEL"; + case OpTaskSequenceGetINTEL: return "OpTaskSequenceGetINTEL"; + case OpTaskSequenceReleaseINTEL: return "OpTaskSequenceReleaseINTEL"; + case OpTypeTaskSequenceINTEL: return "OpTypeTaskSequenceINTEL"; + case OpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case OpSubgroup2DBlockLoadINTEL: return "OpSubgroup2DBlockLoadINTEL"; + case OpSubgroup2DBlockLoadTransformINTEL: return "OpSubgroup2DBlockLoadTransformINTEL"; + case OpSubgroup2DBlockLoadTransposeINTEL: return "OpSubgroup2DBlockLoadTransposeINTEL"; + case OpSubgroup2DBlockPrefetchINTEL: return "OpSubgroup2DBlockPrefetchINTEL"; + case OpSubgroup2DBlockStoreINTEL: return "OpSubgroup2DBlockStoreINTEL"; + case OpSubgroupMatrixMultiplyAccumulateINTEL: return "OpSubgroupMatrixMultiplyAccumulateINTEL"; + case OpBitwiseFunctionINTEL: return "OpBitwiseFunctionINTEL"; + case OpGroupIMulKHR: return "OpGroupIMulKHR"; + case OpGroupFMulKHR: return "OpGroupFMulKHR"; + case OpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case OpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case OpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case OpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case OpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case OpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case OpRoundFToTF32INTEL: return "OpRoundFToTF32INTEL"; + case OpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case OpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload bitwise operators for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline ImageOperandsMask operator&(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) & unsigned(b)); } +inline ImageOperandsMask operator^(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) ^ unsigned(b)); } +inline ImageOperandsMask operator~(ImageOperandsMask a) { return ImageOperandsMask(~unsigned(a)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator&(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) & unsigned(b)); } +inline FPFastMathModeMask operator^(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) ^ unsigned(b)); } +inline FPFastMathModeMask operator~(FPFastMathModeMask a) { return FPFastMathModeMask(~unsigned(a)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator&(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) & unsigned(b)); } +inline SelectionControlMask operator^(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) ^ unsigned(b)); } +inline SelectionControlMask operator~(SelectionControlMask a) { return SelectionControlMask(~unsigned(a)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator&(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) & unsigned(b)); } +inline LoopControlMask operator^(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) ^ unsigned(b)); } +inline LoopControlMask operator~(LoopControlMask a) { return LoopControlMask(~unsigned(a)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator&(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) & unsigned(b)); } +inline FunctionControlMask operator^(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) ^ unsigned(b)); } +inline FunctionControlMask operator~(FunctionControlMask a) { return FunctionControlMask(~unsigned(a)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator&(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) & unsigned(b)); } +inline MemorySemanticsMask operator^(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) ^ unsigned(b)); } +inline MemorySemanticsMask operator~(MemorySemanticsMask a) { return MemorySemanticsMask(~unsigned(a)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator&(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) & unsigned(b)); } +inline MemoryAccessMask operator^(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) ^ unsigned(b)); } +inline MemoryAccessMask operator~(MemoryAccessMask a) { return MemoryAccessMask(~unsigned(a)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator&(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) & unsigned(b)); } +inline KernelProfilingInfoMask operator^(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) ^ unsigned(b)); } +inline KernelProfilingInfoMask operator~(KernelProfilingInfoMask a) { return KernelProfilingInfoMask(~unsigned(a)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator&(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) & unsigned(b)); } +inline RayFlagsMask operator^(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) ^ unsigned(b)); } +inline RayFlagsMask operator~(RayFlagsMask a) { return RayFlagsMask(~unsigned(a)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator&(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) & unsigned(b)); } +inline FragmentShadingRateMask operator^(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) ^ unsigned(b)); } +inline FragmentShadingRateMask operator~(FragmentShadingRateMask a) { return FragmentShadingRateMask(~unsigned(a)); } +inline CooperativeMatrixOperandsMask operator|(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixOperandsMask operator&(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixOperandsMask operator^(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixOperandsMask operator~(CooperativeMatrixOperandsMask a) { return CooperativeMatrixOperandsMask(~unsigned(a)); } +inline CooperativeMatrixReduceMask operator|(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixReduceMask operator&(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixReduceMask operator^(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixReduceMask operator~(CooperativeMatrixReduceMask a) { return CooperativeMatrixReduceMask(~unsigned(a)); } +inline TensorAddressingOperandsMask operator|(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) | unsigned(b)); } +inline TensorAddressingOperandsMask operator&(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) & unsigned(b)); } +inline TensorAddressingOperandsMask operator^(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) ^ unsigned(b)); } +inline TensorAddressingOperandsMask operator~(TensorAddressingOperandsMask a) { return TensorAddressingOperandsMask(~unsigned(a)); } +inline MatrixMultiplyAccumulateOperandsMask operator|(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) | unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator&(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) & unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator^(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) ^ unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator~(MatrixMultiplyAccumulateOperandsMask a) { return MatrixMultiplyAccumulateOperandsMask(~unsigned(a)); } +inline RawAccessChainOperandsMask operator|(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) | unsigned(b)); } +inline RawAccessChainOperandsMask operator&(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) & unsigned(b)); } +inline RawAccessChainOperandsMask operator^(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) ^ unsigned(b)); } +inline RawAccessChainOperandsMask operator~(RawAccessChainOperandsMask a) { return RawAccessChainOperandsMask(~unsigned(a)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 new file mode 100644 index 00000000..bc4485d4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 @@ -0,0 +1,5215 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010600; +static const unsigned int Revision = 1; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + CPP_for_OpenCL = 6, + SYCL = 7, + HERO_C = 8, + NZSL = 9, + WGSL = 10, + Slang = 11, + Zig = 12, + Rust = 13, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + TaskEXT = 5364, + MeshEXT = 5365, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + NonCoherentColorAttachmentReadEXT = 4169, + NonCoherentDepthAttachmentReadEXT = 4170, + NonCoherentStencilAttachmentReadEXT = 4171, + SubgroupUniformControlFlowKHR = 4421, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + NonCoherentTileAttachmentReadQCOM = 4489, + TileShadingRateQCOM = 4490, + EarlyAndLateFragmentTestsAMD = 5017, + StencilRefReplacingEXT = 5027, + CoalescingAMDX = 5069, + IsApiEntryAMDX = 5070, + MaxNodeRecursionAMDX = 5071, + StaticNumWorkgroupsAMDX = 5072, + ShaderIndexAMDX = 5073, + MaxNumWorkgroupsAMDX = 5077, + StencilRefUnchangedFrontAMD = 5079, + StencilRefGreaterFrontAMD = 5080, + StencilRefLessFrontAMD = 5081, + StencilRefUnchangedBackAMD = 5082, + StencilRefGreaterBackAMD = 5083, + StencilRefLessBackAMD = 5084, + QuadDerivativesKHR = 5088, + RequireFullQuadsKHR = 5089, + SharesInputWithAMDX = 5102, + OutputLinesEXT = 5269, + OutputLinesNV = 5269, + OutputPrimitivesEXT = 5270, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsKHR = 5289, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearKHR = 5290, + DerivativeGroupLinearNV = 5290, + OutputTrianglesEXT = 5298, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, + MaximallyReconvergesKHR = 6023, + FPFastMathDefault = 6028, + StreamingInterfaceINTEL = 6154, + RegisterMapInterfaceINTEL = 6160, + NamedBarrierCountINTEL = 6417, + MaximumRegistersINTEL = 6461, + MaximumRegistersIdINTEL = 6462, + NamedMaximumRegistersINTEL = 6463, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + TileImageEXT = 4172, + TileAttachmentQCOM = 4491, + NodePayloadAMDX = 5068, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + HitObjectAttributeNV = 5385, + TaskPayloadWorkgroupEXT = 5402, + CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + TileImageDataEXT = 4173, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + UnormInt10X6EXT = 17, + UnsignedIntRaw10EXT = 19, + UnsignedIntRaw12EXT = 20, + UnormInt2_101010EXT = 21, + UnsignedInt10X6EXT = 22, + UnsignedInt12X4EXT = 23, + UnsignedInt14X2EXT = 24, + UnormInt12X4EXT = 25, + UnormInt14X2EXT = 26, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Nontemporal = 14, + Offsets = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + Nontemporal = 0x00004000, + Offsets = 0x00010000, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + AllowContract = 16, + AllowContractFastINTEL = 16, + AllowReassoc = 17, + AllowReassocINTEL = 17, + AllowTransform = 18, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + AllowContract = 0x00010000, + AllowContractFastINTEL = 0x00010000, + AllowReassoc = 0x00020000, + AllowReassocINTEL = 0x00020000, + AllowTransform = 0x00040000, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + LinkOnceODR = 2, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + RuntimeAlignedINTEL = 5940, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + WeightTextureQCOM = 4487, + BlockMatchTextureQCOM = 4488, + BlockMatchSamplerQCOM = 4499, + ExplicitInterpAMD = 4999, + NodeSharesPayloadLimitsWithAMDX = 5019, + NodeMaxPayloadsAMDX = 5020, + TrackFinishWritingAMDX = 5078, + PayloadNodeNameAMDX = 5091, + PayloadNodeBaseIndexAMDX = 5098, + PayloadNodeSparseArrayAMDX = 5099, + PayloadNodeArraySizeAMDX = 5100, + PayloadDispatchIndirectAMDX = 5105, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveEXT = 5271, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexKHR = 5285, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + HitObjectShaderRecordBufferNV = 5386, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, + SIMTCallINTEL = 5599, + ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + StridesizeINTEL = 5883, + WordsizeINTEL = 5884, + TrueDualPortINTEL = 5885, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + MathOpDSPModeINTEL = 5909, + AliasScopeINTEL = 5914, + NoAliasINTEL = 5915, + InitiationIntervalINTEL = 5917, + MaxConcurrencyINTEL = 5918, + PipelineEnableINTEL = 5919, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, + MediaBlockIOINTEL = 6140, + StallFreeINTEL = 6151, + FPMaxErrorDecorationINTEL = 6170, + LatencyControlLabelINTEL = 6172, + LatencyControlConstraintINTEL = 6173, + ConduitKernelArgumentINTEL = 6175, + RegisterMapKernelArgumentINTEL = 6176, + MMHostInterfaceAddressWidthINTEL = 6177, + MMHostInterfaceDataWidthINTEL = 6178, + MMHostInterfaceLatencyINTEL = 6179, + MMHostInterfaceReadWriteModeINTEL = 6180, + MMHostInterfaceMaxBurstINTEL = 6181, + MMHostInterfaceWaitRequestINTEL = 6182, + StableKernelArgumentINTEL = 6183, + HostAccessINTEL = 6188, + InitModeINTEL = 6190, + ImplementInRegisterMapINTEL = 6191, + CacheControlLoadINTEL = 6442, + CacheControlStoreINTEL = 6443, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + CoreIDARM = 4160, + CoreCountARM = 4161, + CoreMaxIDARM = 4162, + WarpIDARM = 4163, + WarpMaxIDARM = 4164, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + TileOffsetQCOM = 4492, + TileDimensionQCOM = 4493, + TileApronSizeQCOM = 4494, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + RemainingRecursionLevelsAMDX = 5021, + ShaderIndexAMDX = 5073, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordKHR = 5286, + BaryCoordNV = 5286, + BaryCoordNoPerspKHR = 5287, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + PrimitivePointIndicesEXT = 5294, + PrimitiveLineIndicesEXT = 5295, + PrimitiveTriangleIndicesEXT = 5296, + CullPrimitiveEXT = 5299, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + CurrentRayTimeNV = 5334, + HitTriangleVertexPositionsKHR = 5335, + HitMicroTriangleVertexPositionsNV = 5337, + HitMicroTriangleVertexBarycentricsNV = 5344, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + HitIsSphereNV = 5359, + HitIsLSSNV = 5360, + HitSpherePositionNV = 5361, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + HitLSSPositionsNV = 5396, + HitKindFrontFacingMicroTriangleNV = 5405, + HitKindBackFacingMicroTriangleNV = 5406, + HitSphereRadiusNV = 5420, + HitLSSRadiiNV = 5421, + ClusterIDNV = 5436, + CullMaskKHR = 6021, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + NoFusionINTEL = 23, + LoopCountINTEL = 24, + MaxReinvocationDelayINTEL = 25, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + NoFusionINTEL = 0x00800000, + LoopCountINTEL = 0x01000000, + MaxReinvocationDelayINTEL = 0x02000000, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + OptNoneEXT = 16, + OptNoneINTEL = 16, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + OptNoneEXT = 0x00010000, + OptNoneINTEL = 0x00010000, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + AliasScopeINTELMask = 16, + NoAliasINTELMask = 17, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + AliasScopeINTELMask = 0x00010000, + NoAliasINTELMask = 0x00020000, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + UniformDecoration = 71, + CoreBuiltinsARM = 4165, + TileImageColorReadAccessEXT = 4166, + TileImageDepthReadAccessEXT = 4167, + TileImageStencilReadAccessEXT = 4168, + CooperativeMatrixLayoutsARM = 4201, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + UntypedPointersKHR = 4473, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + TextureSampleWeightedQCOM = 4484, + TextureBoxFilterQCOM = 4485, + TextureBlockMatchQCOM = 4486, + TileShadingQCOM = 4495, + TextureBlockMatch2QCOM = 4498, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + ShaderEnqueueAMDX = 5067, + QuadControlKHR = 5087, + BFloat16TypeKHR = 5116, + BFloat16DotProductKHR = 5117, + BFloat16CooperativeMatrixKHR = 5118, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + MeshShadingEXT = 5283, + FragmentBarycentricKHR = 5284, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsKHR = 5288, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingPositionFetchKHR = 5336, + RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearKHR = 5350, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocation = 5379, + DemoteToHelperInvocationEXT = 5379, + DisplacementMicromapNV = 5380, + RayTracingOpacityMicromapEXT = 5381, + ShaderInvocationReorderNV = 5383, + BindlessTextureNV = 5390, + RayQueryPositionFetchKHR = 5391, + CooperativeVectorNV = 5394, + AtomicFloat16VectorNV = 5404, + RayTracingDisplacementMicromapNV = 5409, + RawAccessChainsNV = 5414, + RayTracingSpheresGeometryNV = 5418, + RayTracingLinearSweptSpheresGeometryNV = 5419, + CooperativeMatrixReductionsNV = 5430, + CooperativeMatrixConversionsNV = 5431, + CooperativeMatrixPerElementOperationsNV = 5432, + CooperativeMatrixTensorAddressingNV = 5433, + CooperativeMatrixBlockLoadsNV = 5434, + CooperativeVectorTrainingNV = 5435, + RayTracingClusterAccelerationStructureNV = 5437, + TensorAddressingNV = 5439, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, + FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, + ArbitraryPrecisionFloatingPointINTEL = 5845, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGADSPControlINTEL = 5908, + MemoryAccessAliasingINTEL = 5910, + FPGAInvocationPipeliningAttributesINTEL = 5916, + FPGABufferLocationINTEL = 5920, + ArbitraryPrecisionFixedPointINTEL = 5922, + USMStorageClassesINTEL = 5935, + RuntimeAlignedAttributeINTEL = 5939, + IOPipesINTEL = 5943, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + DotProductInputAll = 6016, + DotProductInputAllKHR = 6016, + DotProductInput4x8Bit = 6017, + DotProductInput4x8BitKHR = 6017, + DotProductInput4x8BitPacked = 6018, + DotProductInput4x8BitPackedKHR = 6018, + DotProduct = 6019, + DotProductKHR = 6019, + RayCullMaskKHR = 6020, + CooperativeMatrixKHR = 6022, + ReplicatedCompositesEXT = 6024, + BitInstructions = 6025, + GroupNonUniformRotateKHR = 6026, + FloatControls2 = 6029, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + LongCompositesINTEL = 6089, + OptNoneEXT = 6094, + OptNoneINTEL = 6094, + AtomicFloat16AddEXT = 6095, + DebugInfoModuleINTEL = 6114, + BFloat16ConversionINTEL = 6115, + SplitBarrierINTEL = 6141, + ArithmeticFenceEXT = 6144, + FPGAClusterAttributesV2INTEL = 6150, + FPGAKernelAttributesv2INTEL = 6161, + TaskSequenceINTEL = 6162, + FPMaxErrorINTEL = 6169, + FPGALatencyControlINTEL = 6171, + FPGAArgumentInterfacesINTEL = 6174, + GlobalVariableHostAccessINTEL = 6187, + GlobalVariableFPGADecorationsINTEL = 6189, + SubgroupBufferPrefetchINTEL = 6220, + Subgroup2DBlockIOINTEL = 6228, + Subgroup2DBlockTransformINTEL = 6229, + Subgroup2DBlockTransposeINTEL = 6230, + SubgroupMatrixMultiplyAccumulateINTEL = 6236, + TernaryBitwiseFunctionINTEL = 6241, + GroupUniformArithmeticKHR = 6400, + TensorFloat32RoundingINTEL = 6425, + MaskedGatherScatterINTEL = 6427, + CacheControlsINTEL = 6441, + RegisterLimitsINTEL = 6460, + Max = 0x7fffffff, +}; + +enum class RayFlagsShift : unsigned { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipBuiltinPrimitivesNV = 8, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + ForceOpacityMicromap2StateEXT = 10, + Max = 0x7fffffff, +}; + +enum class RayFlagsMask : unsigned { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipBuiltinPrimitivesNV = 0x00000100, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + ForceOpacityMicromap2StateEXT = 0x00000400, +}; + +enum class RayQueryIntersection : unsigned { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, +}; + +enum class RayQueryCommittedIntersectionType : unsigned { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, +}; + +enum class RayQueryCandidateIntersectionType : unsigned { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, +}; + +enum class FragmentShadingRateShift : unsigned { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, +}; + +enum class FragmentShadingRateMask : unsigned { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, +}; + +enum class FPDenormMode : unsigned { + Preserve = 0, + FlushToZero = 1, + Max = 0x7fffffff, +}; + +enum class FPOperationMode : unsigned { + IEEE = 0, + ALT = 1, + Max = 0x7fffffff, +}; + +enum class QuantizationModes : unsigned { + TRN = 0, + TRN_ZERO = 1, + RND = 2, + RND_ZERO = 3, + RND_INF = 4, + RND_MIN_INF = 5, + RND_CONV = 6, + RND_CONV_ODD = 7, + Max = 0x7fffffff, +}; + +enum class OverflowModes : unsigned { + WRAP = 0, + SAT = 1, + SAT_ZERO = 2, + SAT_SYM = 3, + Max = 0x7fffffff, +}; + +enum class PackedVectorFormat : unsigned { + PackedVectorFormat4x8Bit = 0, + PackedVectorFormat4x8BitKHR = 0, + Max = 0x7fffffff, +}; + +enum class CooperativeMatrixOperandsShift : unsigned { + MatrixASignedComponentsKHR = 0, + MatrixBSignedComponentsKHR = 1, + MatrixCSignedComponentsKHR = 2, + MatrixResultSignedComponentsKHR = 3, + SaturatingAccumulationKHR = 4, + Max = 0x7fffffff, +}; + +enum class CooperativeMatrixOperandsMask : unsigned { + MaskNone = 0, + MatrixASignedComponentsKHR = 0x00000001, + MatrixBSignedComponentsKHR = 0x00000002, + MatrixCSignedComponentsKHR = 0x00000004, + MatrixResultSignedComponentsKHR = 0x00000008, + SaturatingAccumulationKHR = 0x00000010, +}; + +enum class CooperativeMatrixLayout : unsigned { + RowMajorKHR = 0, + ColumnMajorKHR = 1, + RowBlockedInterleavedARM = 4202, + ColumnBlockedInterleavedARM = 4203, + Max = 0x7fffffff, +}; + +enum class CooperativeMatrixUse : unsigned { + MatrixAKHR = 0, + MatrixBKHR = 1, + MatrixAccumulatorKHR = 2, + Max = 0x7fffffff, +}; + +enum class CooperativeMatrixReduceShift : unsigned { + Row = 0, + Column = 1, + CooperativeMatrixReduce2x2 = 2, + Max = 0x7fffffff, +}; + +enum class CooperativeMatrixReduceMask : unsigned { + MaskNone = 0, + Row = 0x00000001, + Column = 0x00000002, + CooperativeMatrixReduce2x2 = 0x00000004, +}; + +enum class TensorClampMode : unsigned { + Undefined = 0, + Constant = 1, + ClampToEdge = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class TensorAddressingOperandsShift : unsigned { + TensorView = 0, + DecodeFunc = 1, + Max = 0x7fffffff, +}; + +enum class TensorAddressingOperandsMask : unsigned { + MaskNone = 0, + TensorView = 0x00000001, + DecodeFunc = 0x00000002, +}; + +enum class InitializationModeQualifier : unsigned { + InitOnDeviceReprogramINTEL = 0, + InitOnDeviceResetINTEL = 1, + Max = 0x7fffffff, +}; + +enum class HostAccessQualifier : unsigned { + NoneINTEL = 0, + ReadINTEL = 1, + WriteINTEL = 2, + ReadWriteINTEL = 3, + Max = 0x7fffffff, +}; + +enum class LoadCacheControl : unsigned { + UncachedINTEL = 0, + CachedINTEL = 1, + StreamingINTEL = 2, + InvalidateAfterReadINTEL = 3, + ConstCachedINTEL = 4, + Max = 0x7fffffff, +}; + +enum class StoreCacheControl : unsigned { + UncachedINTEL = 0, + WriteThroughINTEL = 1, + WriteBackINTEL = 2, + StreamingINTEL = 3, + Max = 0x7fffffff, +}; + +enum class NamedMaximumNumberOfRegisters : unsigned { + AutoINTEL = 0, + Max = 0x7fffffff, +}; + +enum class MatrixMultiplyAccumulateOperandsShift : unsigned { + MatrixASignedComponentsINTEL = 0, + MatrixBSignedComponentsINTEL = 1, + MatrixCBFloat16INTEL = 2, + MatrixResultBFloat16INTEL = 3, + MatrixAPackedInt8INTEL = 4, + MatrixBPackedInt8INTEL = 5, + MatrixAPackedInt4INTEL = 6, + MatrixBPackedInt4INTEL = 7, + MatrixATF32INTEL = 8, + MatrixBTF32INTEL = 9, + MatrixAPackedFloat16INTEL = 10, + MatrixBPackedFloat16INTEL = 11, + MatrixAPackedBFloat16INTEL = 12, + MatrixBPackedBFloat16INTEL = 13, + Max = 0x7fffffff, +}; + +enum class MatrixMultiplyAccumulateOperandsMask : unsigned { + MaskNone = 0, + MatrixASignedComponentsINTEL = 0x00000001, + MatrixBSignedComponentsINTEL = 0x00000002, + MatrixCBFloat16INTEL = 0x00000004, + MatrixResultBFloat16INTEL = 0x00000008, + MatrixAPackedInt8INTEL = 0x00000010, + MatrixBPackedInt8INTEL = 0x00000020, + MatrixAPackedInt4INTEL = 0x00000040, + MatrixBPackedInt4INTEL = 0x00000080, + MatrixATF32INTEL = 0x00000100, + MatrixBTF32INTEL = 0x00000200, + MatrixAPackedFloat16INTEL = 0x00000400, + MatrixBPackedFloat16INTEL = 0x00000800, + MatrixAPackedBFloat16INTEL = 0x00001000, + MatrixBPackedBFloat16INTEL = 0x00002000, +}; + +enum class RawAccessChainOperandsShift : unsigned { + RobustnessPerComponentNV = 0, + RobustnessPerElementNV = 1, + Max = 0x7fffffff, +}; + +enum class RawAccessChainOperandsMask : unsigned { + MaskNone = 0, + RobustnessPerComponentNV = 0x00000001, + RobustnessPerElementNV = 0x00000002, +}; + +enum class FPEncoding : unsigned { + BFloat16KHR = 0, + Max = 0x7fffffff, +}; + +enum class CooperativeVectorMatrixLayout : unsigned { + RowMajorNV = 0, + ColumnMajorNV = 1, + InferencingOptimalNV = 2, + TrainingOptimalNV = 3, + Max = 0x7fffffff, +}; + +enum class ComponentType : unsigned { + Float16NV = 0, + Float32NV = 1, + Float64NV = 2, + SignedInt8NV = 3, + SignedInt16NV = 4, + SignedInt32NV = 5, + SignedInt64NV = 6, + UnsignedInt8NV = 7, + UnsignedInt16NV = 8, + UnsignedInt32NV = 9, + UnsignedInt64NV = 10, + SignedInt8PackedNV = 1000491000, + UnsignedInt8PackedNV = 1000491001, + FloatE4M3NV = 1000491002, + FloatE5M2NV = 1000491003, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + Max = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case Op::OpNop: *hasResult = false; *hasResultType = false; break; + case Op::OpUndef: *hasResult = true; *hasResultType = true; break; + case Op::OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case Op::OpSource: *hasResult = false; *hasResultType = false; break; + case Op::OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case Op::OpName: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberName: *hasResult = false; *hasResultType = false; break; + case Op::OpString: *hasResult = true; *hasResultType = false; break; + case Op::OpLine: *hasResult = false; *hasResultType = false; break; + case Op::OpExtension: *hasResult = false; *hasResultType = false; break; + case Op::OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case Op::OpExtInst: *hasResult = true; *hasResultType = true; break; + case Op::OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case Op::OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case Op::OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case Op::OpCapability: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeBool: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeInt: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeVector: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeImage: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeArray: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case Op::OpTypePointer: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case Op::OpTypePipe: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case Op::OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case Op::OpConstant: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantNull: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case Op::OpFunction: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case Op::OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case Op::OpVariable: *hasResult = true; *hasResultType = true; break; + case Op::OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case Op::OpLoad: *hasResult = true; *hasResultType = true; break; + case Op::OpStore: *hasResult = false; *hasResultType = false; break; + case Op::OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case Op::OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case Op::OpAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpArrayLength: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case Op::OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case Op::OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case Op::OpCopyObject: *hasResult = true; *hasResultType = true; break; + case Op::OpTranspose: *hasResult = true; *hasResultType = true; break; + case Op::OpSampledImage: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageFetch: *hasResult = true; *hasResultType = true; break; + case Op::OpImageGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageRead: *hasResult = true; *hasResultType = true; break; + case Op::OpImageWrite: *hasResult = false; *hasResultType = false; break; + case Op::OpImage: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case Op::OpUConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpSConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpFConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case Op::OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case Op::OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case Op::OpBitcast: *hasResult = true; *hasResultType = true; break; + case Op::OpSNegate: *hasResult = true; *hasResultType = true; break; + case Op::OpFNegate: *hasResult = true; *hasResultType = true; break; + case Op::OpIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpISub: *hasResult = true; *hasResultType = true; break; + case Op::OpFSub: *hasResult = true; *hasResultType = true; break; + case Op::OpIMul: *hasResult = true; *hasResultType = true; break; + case Op::OpFMul: *hasResult = true; *hasResultType = true; break; + case Op::OpUDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpSDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpFDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpUMod: *hasResult = true; *hasResultType = true; break; + case Op::OpSRem: *hasResult = true; *hasResultType = true; break; + case Op::OpSMod: *hasResult = true; *hasResultType = true; break; + case Op::OpFRem: *hasResult = true; *hasResultType = true; break; + case Op::OpFMod: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case Op::OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case Op::OpDot: *hasResult = true; *hasResultType = true; break; + case Op::OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case Op::OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case Op::OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case Op::OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case Op::OpAny: *hasResult = true; *hasResultType = true; break; + case Op::OpAll: *hasResult = true; *hasResultType = true; break; + case Op::OpIsNan: *hasResult = true; *hasResultType = true; break; + case Op::OpIsInf: *hasResult = true; *hasResultType = true; break; + case Op::OpIsFinite: *hasResult = true; *hasResultType = true; break; + case Op::OpIsNormal: *hasResult = true; *hasResultType = true; break; + case Op::OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case Op::OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case Op::OpOrdered: *hasResult = true; *hasResultType = true; break; + case Op::OpUnordered: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case Op::OpSelect: *hasResult = true; *hasResultType = true; break; + case Op::OpIEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpINotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpULessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpSLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpNot: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpBitReverse: *hasResult = true; *hasResultType = true; break; + case Op::OpBitCount: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdx: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdy: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidth: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case Op::OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case Op::OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case Op::OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case Op::OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case Op::OpPhi: *hasResult = true; *hasResultType = true; break; + case Op::OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case Op::OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case Op::OpLabel: *hasResult = true; *hasResultType = false; break; + case Op::OpBranch: *hasResult = false; *hasResultType = false; break; + case Op::OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case Op::OpSwitch: *hasResult = false; *hasResultType = false; break; + case Op::OpKill: *hasResult = false; *hasResultType = false; break; + case Op::OpReturn: *hasResult = false; *hasResultType = false; break; + case Op::OpReturnValue: *hasResult = false; *hasResultType = false; break; + case Op::OpUnreachable: *hasResult = false; *hasResultType = false; break; + case Op::OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case Op::OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupAll: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupAny: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpReadPipe: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case Op::OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case Op::OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case Op::OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case Op::OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case Op::OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case Op::OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case Op::OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case Op::OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case Op::OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case Op::OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case Op::OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case Op::OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case Op::OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case Op::OpNoLine: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case Op::OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case Op::OpSizeOf: *hasResult = true; *hasResultType = true; break; + case Op::OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case Op::OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case Op::OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case Op::OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case Op::OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case Op::OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case Op::OpDecorateId: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case Op::OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case Op::OpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case Op::OpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpSDot: *hasResult = true; *hasResultType = true; break; + case Op::OpUDot: *hasResult = true; *hasResultType = true; break; + case Op::OpSUDot: *hasResult = true; *hasResultType = true; break; + case Op::OpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case Op::OpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case Op::OpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case Op::OpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case Op::OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case Op::OpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case Op::OpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case Op::OpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case Op::OpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case Op::OpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case Op::OpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case Op::OpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case Op::OpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case Op::OpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case Op::OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeCooperativeVectorNV: *hasResult = true; *hasResultType = false; break; + case Op::OpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case Op::OpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case Op::OpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case Op::OpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case Op::OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case Op::OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case Op::OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case Op::OpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case Op::OpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case Op::OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case Op::OpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case Op::OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case Op::OpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionLSSHitValueNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectGetLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case Op::OpHitObjectIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAsmTargetINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpExpectKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpDecorateString: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case Op::OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedSinINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedCosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedLogINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFixedExpINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpTaskSequenceCreateINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpTaskSequenceAsyncINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpTaskSequenceGetINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpTaskSequenceReleaseINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeTaskSequenceINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroup2DBlockLoadINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroup2DBlockLoadTransformINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroup2DBlockLoadTransposeINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroup2DBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroup2DBlockStoreINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupMatrixMultiplyAccumulateINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRoundFToTF32INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + } +} +inline const char* SourceLanguageToString(SourceLanguage value) { + switch (value) { + case SourceLanguage::Unknown: return "Unknown"; + case SourceLanguage::ESSL: return "ESSL"; + case SourceLanguage::GLSL: return "GLSL"; + case SourceLanguage::OpenCL_C: return "OpenCL_C"; + case SourceLanguage::OpenCL_CPP: return "OpenCL_CPP"; + case SourceLanguage::HLSL: return "HLSL"; + case SourceLanguage::CPP_for_OpenCL: return "CPP_for_OpenCL"; + case SourceLanguage::SYCL: return "SYCL"; + case SourceLanguage::HERO_C: return "HERO_C"; + case SourceLanguage::NZSL: return "NZSL"; + case SourceLanguage::WGSL: return "WGSL"; + case SourceLanguage::Slang: return "Slang"; + case SourceLanguage::Zig: return "Zig"; + case SourceLanguage::Rust: return "Rust"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModelToString(ExecutionModel value) { + switch (value) { + case ExecutionModel::Vertex: return "Vertex"; + case ExecutionModel::TessellationControl: return "TessellationControl"; + case ExecutionModel::TessellationEvaluation: return "TessellationEvaluation"; + case ExecutionModel::Geometry: return "Geometry"; + case ExecutionModel::Fragment: return "Fragment"; + case ExecutionModel::GLCompute: return "GLCompute"; + case ExecutionModel::Kernel: return "Kernel"; + case ExecutionModel::TaskNV: return "TaskNV"; + case ExecutionModel::MeshNV: return "MeshNV"; + case ExecutionModel::RayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModel::IntersectionKHR: return "IntersectionKHR"; + case ExecutionModel::AnyHitKHR: return "AnyHitKHR"; + case ExecutionModel::ClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModel::MissKHR: return "MissKHR"; + case ExecutionModel::CallableKHR: return "CallableKHR"; + case ExecutionModel::TaskEXT: return "TaskEXT"; + case ExecutionModel::MeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* AddressingModelToString(AddressingModel value) { + switch (value) { + case AddressingModel::Logical: return "Logical"; + case AddressingModel::Physical32: return "Physical32"; + case AddressingModel::Physical64: return "Physical64"; + case AddressingModel::PhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* MemoryModelToString(MemoryModel value) { + switch (value) { + case MemoryModel::Simple: return "Simple"; + case MemoryModel::GLSL450: return "GLSL450"; + case MemoryModel::OpenCL: return "OpenCL"; + case MemoryModel::Vulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModeToString(ExecutionMode value) { + switch (value) { + case ExecutionMode::Invocations: return "Invocations"; + case ExecutionMode::SpacingEqual: return "SpacingEqual"; + case ExecutionMode::SpacingFractionalEven: return "SpacingFractionalEven"; + case ExecutionMode::SpacingFractionalOdd: return "SpacingFractionalOdd"; + case ExecutionMode::VertexOrderCw: return "VertexOrderCw"; + case ExecutionMode::VertexOrderCcw: return "VertexOrderCcw"; + case ExecutionMode::PixelCenterInteger: return "PixelCenterInteger"; + case ExecutionMode::OriginUpperLeft: return "OriginUpperLeft"; + case ExecutionMode::OriginLowerLeft: return "OriginLowerLeft"; + case ExecutionMode::EarlyFragmentTests: return "EarlyFragmentTests"; + case ExecutionMode::PointMode: return "PointMode"; + case ExecutionMode::Xfb: return "Xfb"; + case ExecutionMode::DepthReplacing: return "DepthReplacing"; + case ExecutionMode::DepthGreater: return "DepthGreater"; + case ExecutionMode::DepthLess: return "DepthLess"; + case ExecutionMode::DepthUnchanged: return "DepthUnchanged"; + case ExecutionMode::LocalSize: return "LocalSize"; + case ExecutionMode::LocalSizeHint: return "LocalSizeHint"; + case ExecutionMode::InputPoints: return "InputPoints"; + case ExecutionMode::InputLines: return "InputLines"; + case ExecutionMode::InputLinesAdjacency: return "InputLinesAdjacency"; + case ExecutionMode::Triangles: return "Triangles"; + case ExecutionMode::InputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case ExecutionMode::Quads: return "Quads"; + case ExecutionMode::Isolines: return "Isolines"; + case ExecutionMode::OutputVertices: return "OutputVertices"; + case ExecutionMode::OutputPoints: return "OutputPoints"; + case ExecutionMode::OutputLineStrip: return "OutputLineStrip"; + case ExecutionMode::OutputTriangleStrip: return "OutputTriangleStrip"; + case ExecutionMode::VecTypeHint: return "VecTypeHint"; + case ExecutionMode::ContractionOff: return "ContractionOff"; + case ExecutionMode::Initializer: return "Initializer"; + case ExecutionMode::Finalizer: return "Finalizer"; + case ExecutionMode::SubgroupSize: return "SubgroupSize"; + case ExecutionMode::SubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionMode::SubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionMode::LocalSizeId: return "LocalSizeId"; + case ExecutionMode::LocalSizeHintId: return "LocalSizeHintId"; + case ExecutionMode::NonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case ExecutionMode::NonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case ExecutionMode::NonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case ExecutionMode::SubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case ExecutionMode::PostDepthCoverage: return "PostDepthCoverage"; + case ExecutionMode::DenormPreserve: return "DenormPreserve"; + case ExecutionMode::DenormFlushToZero: return "DenormFlushToZero"; + case ExecutionMode::SignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionMode::RoundingModeRTE: return "RoundingModeRTE"; + case ExecutionMode::RoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionMode::NonCoherentTileAttachmentReadQCOM: return "NonCoherentTileAttachmentReadQCOM"; + case ExecutionMode::TileShadingRateQCOM: return "TileShadingRateQCOM"; + case ExecutionMode::EarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case ExecutionMode::StencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case ExecutionMode::CoalescingAMDX: return "CoalescingAMDX"; + case ExecutionMode::IsApiEntryAMDX: return "IsApiEntryAMDX"; + case ExecutionMode::MaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case ExecutionMode::StaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case ExecutionMode::ShaderIndexAMDX: return "ShaderIndexAMDX"; + case ExecutionMode::MaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case ExecutionMode::StencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case ExecutionMode::StencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case ExecutionMode::StencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case ExecutionMode::StencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case ExecutionMode::StencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case ExecutionMode::StencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case ExecutionMode::QuadDerivativesKHR: return "QuadDerivativesKHR"; + case ExecutionMode::RequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case ExecutionMode::SharesInputWithAMDX: return "SharesInputWithAMDX"; + case ExecutionMode::OutputLinesEXT: return "OutputLinesEXT"; + case ExecutionMode::OutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case ExecutionMode::DerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case ExecutionMode::DerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case ExecutionMode::OutputTrianglesEXT: return "OutputTrianglesEXT"; + case ExecutionMode::PixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionMode::PixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionMode::SampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionMode::SampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionMode::ShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionMode::ShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case ExecutionMode::SharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case ExecutionMode::RoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case ExecutionMode::RoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case ExecutionMode::FloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case ExecutionMode::FloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case ExecutionMode::MaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionMode::MaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionMode::NoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionMode::NumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case ExecutionMode::SchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case ExecutionMode::MaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case ExecutionMode::FPFastMathDefault: return "FPFastMathDefault"; + case ExecutionMode::StreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case ExecutionMode::RegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case ExecutionMode::NamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case ExecutionMode::MaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case ExecutionMode::MaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case ExecutionMode::NamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* StorageClassToString(StorageClass value) { + switch (value) { + case StorageClass::UniformConstant: return "UniformConstant"; + case StorageClass::Input: return "Input"; + case StorageClass::Uniform: return "Uniform"; + case StorageClass::Output: return "Output"; + case StorageClass::Workgroup: return "Workgroup"; + case StorageClass::CrossWorkgroup: return "CrossWorkgroup"; + case StorageClass::Private: return "Private"; + case StorageClass::Function: return "Function"; + case StorageClass::Generic: return "Generic"; + case StorageClass::PushConstant: return "PushConstant"; + case StorageClass::AtomicCounter: return "AtomicCounter"; + case StorageClass::Image: return "Image"; + case StorageClass::StorageBuffer: return "StorageBuffer"; + case StorageClass::TileImageEXT: return "TileImageEXT"; + case StorageClass::TileAttachmentQCOM: return "TileAttachmentQCOM"; + case StorageClass::NodePayloadAMDX: return "NodePayloadAMDX"; + case StorageClass::CallableDataKHR: return "CallableDataKHR"; + case StorageClass::IncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case StorageClass::RayPayloadKHR: return "RayPayloadKHR"; + case StorageClass::HitAttributeKHR: return "HitAttributeKHR"; + case StorageClass::IncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClass::ShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClass::PhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case StorageClass::HitObjectAttributeNV: return "HitObjectAttributeNV"; + case StorageClass::TaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case StorageClass::CodeSectionINTEL: return "CodeSectionINTEL"; + case StorageClass::DeviceOnlyINTEL: return "DeviceOnlyINTEL"; + case StorageClass::HostOnlyINTEL: return "HostOnlyINTEL"; + default: return "Unknown"; + } +} + +inline const char* DimToString(Dim value) { + switch (value) { + case Dim::Dim1D: return "1D"; + case Dim::Dim2D: return "2D"; + case Dim::Dim3D: return "3D"; + case Dim::Cube: return "Cube"; + case Dim::Rect: return "Rect"; + case Dim::Buffer: return "Buffer"; + case Dim::SubpassData: return "SubpassData"; + case Dim::TileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SamplerAddressingModeToString(SamplerAddressingMode value) { + switch (value) { + case SamplerAddressingMode::None: return "None"; + case SamplerAddressingMode::ClampToEdge: return "ClampToEdge"; + case SamplerAddressingMode::Clamp: return "Clamp"; + case SamplerAddressingMode::Repeat: return "Repeat"; + case SamplerAddressingMode::RepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SamplerFilterModeToString(SamplerFilterMode value) { + switch (value) { + case SamplerFilterMode::Nearest: return "Nearest"; + case SamplerFilterMode::Linear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* ImageFormatToString(ImageFormat value) { + switch (value) { + case ImageFormat::Unknown: return "Unknown"; + case ImageFormat::Rgba32f: return "Rgba32f"; + case ImageFormat::Rgba16f: return "Rgba16f"; + case ImageFormat::R32f: return "R32f"; + case ImageFormat::Rgba8: return "Rgba8"; + case ImageFormat::Rgba8Snorm: return "Rgba8Snorm"; + case ImageFormat::Rg32f: return "Rg32f"; + case ImageFormat::Rg16f: return "Rg16f"; + case ImageFormat::R11fG11fB10f: return "R11fG11fB10f"; + case ImageFormat::R16f: return "R16f"; + case ImageFormat::Rgba16: return "Rgba16"; + case ImageFormat::Rgb10A2: return "Rgb10A2"; + case ImageFormat::Rg16: return "Rg16"; + case ImageFormat::Rg8: return "Rg8"; + case ImageFormat::R16: return "R16"; + case ImageFormat::R8: return "R8"; + case ImageFormat::Rgba16Snorm: return "Rgba16Snorm"; + case ImageFormat::Rg16Snorm: return "Rg16Snorm"; + case ImageFormat::Rg8Snorm: return "Rg8Snorm"; + case ImageFormat::R16Snorm: return "R16Snorm"; + case ImageFormat::R8Snorm: return "R8Snorm"; + case ImageFormat::Rgba32i: return "Rgba32i"; + case ImageFormat::Rgba16i: return "Rgba16i"; + case ImageFormat::Rgba8i: return "Rgba8i"; + case ImageFormat::R32i: return "R32i"; + case ImageFormat::Rg32i: return "Rg32i"; + case ImageFormat::Rg16i: return "Rg16i"; + case ImageFormat::Rg8i: return "Rg8i"; + case ImageFormat::R16i: return "R16i"; + case ImageFormat::R8i: return "R8i"; + case ImageFormat::Rgba32ui: return "Rgba32ui"; + case ImageFormat::Rgba16ui: return "Rgba16ui"; + case ImageFormat::Rgba8ui: return "Rgba8ui"; + case ImageFormat::R32ui: return "R32ui"; + case ImageFormat::Rgb10a2ui: return "Rgb10a2ui"; + case ImageFormat::Rg32ui: return "Rg32ui"; + case ImageFormat::Rg16ui: return "Rg16ui"; + case ImageFormat::Rg8ui: return "Rg8ui"; + case ImageFormat::R16ui: return "R16ui"; + case ImageFormat::R8ui: return "R8ui"; + case ImageFormat::R64ui: return "R64ui"; + case ImageFormat::R64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelOrderToString(ImageChannelOrder value) { + switch (value) { + case ImageChannelOrder::R: return "R"; + case ImageChannelOrder::A: return "A"; + case ImageChannelOrder::RG: return "RG"; + case ImageChannelOrder::RA: return "RA"; + case ImageChannelOrder::RGB: return "RGB"; + case ImageChannelOrder::RGBA: return "RGBA"; + case ImageChannelOrder::BGRA: return "BGRA"; + case ImageChannelOrder::ARGB: return "ARGB"; + case ImageChannelOrder::Intensity: return "Intensity"; + case ImageChannelOrder::Luminance: return "Luminance"; + case ImageChannelOrder::Rx: return "Rx"; + case ImageChannelOrder::RGx: return "RGx"; + case ImageChannelOrder::RGBx: return "RGBx"; + case ImageChannelOrder::Depth: return "Depth"; + case ImageChannelOrder::DepthStencil: return "DepthStencil"; + case ImageChannelOrder::sRGB: return "sRGB"; + case ImageChannelOrder::sRGBx: return "sRGBx"; + case ImageChannelOrder::sRGBA: return "sRGBA"; + case ImageChannelOrder::sBGRA: return "sBGRA"; + case ImageChannelOrder::ABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelDataTypeToString(ImageChannelDataType value) { + switch (value) { + case ImageChannelDataType::SnormInt8: return "SnormInt8"; + case ImageChannelDataType::SnormInt16: return "SnormInt16"; + case ImageChannelDataType::UnormInt8: return "UnormInt8"; + case ImageChannelDataType::UnormInt16: return "UnormInt16"; + case ImageChannelDataType::UnormShort565: return "UnormShort565"; + case ImageChannelDataType::UnormShort555: return "UnormShort555"; + case ImageChannelDataType::UnormInt101010: return "UnormInt101010"; + case ImageChannelDataType::SignedInt8: return "SignedInt8"; + case ImageChannelDataType::SignedInt16: return "SignedInt16"; + case ImageChannelDataType::SignedInt32: return "SignedInt32"; + case ImageChannelDataType::UnsignedInt8: return "UnsignedInt8"; + case ImageChannelDataType::UnsignedInt16: return "UnsignedInt16"; + case ImageChannelDataType::UnsignedInt32: return "UnsignedInt32"; + case ImageChannelDataType::HalfFloat: return "HalfFloat"; + case ImageChannelDataType::Float: return "Float"; + case ImageChannelDataType::UnormInt24: return "UnormInt24"; + case ImageChannelDataType::UnormInt101010_2: return "UnormInt101010_2"; + case ImageChannelDataType::UnormInt10X6EXT: return "UnormInt10X6EXT"; + case ImageChannelDataType::UnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case ImageChannelDataType::UnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case ImageChannelDataType::UnormInt2_101010EXT: return "UnormInt2_101010EXT"; + case ImageChannelDataType::UnsignedInt10X6EXT: return "UnsignedInt10X6EXT"; + case ImageChannelDataType::UnsignedInt12X4EXT: return "UnsignedInt12X4EXT"; + case ImageChannelDataType::UnsignedInt14X2EXT: return "UnsignedInt14X2EXT"; + case ImageChannelDataType::UnormInt12X4EXT: return "UnormInt12X4EXT"; + case ImageChannelDataType::UnormInt14X2EXT: return "UnormInt14X2EXT"; + default: return "Unknown"; + } +} + +inline const char* FPRoundingModeToString(FPRoundingMode value) { + switch (value) { + case FPRoundingMode::RTE: return "RTE"; + case FPRoundingMode::RTZ: return "RTZ"; + case FPRoundingMode::RTP: return "RTP"; + case FPRoundingMode::RTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* LinkageTypeToString(LinkageType value) { + switch (value) { + case LinkageType::Export: return "Export"; + case LinkageType::Import: return "Import"; + case LinkageType::LinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* AccessQualifierToString(AccessQualifier value) { + switch (value) { + case AccessQualifier::ReadOnly: return "ReadOnly"; + case AccessQualifier::WriteOnly: return "WriteOnly"; + case AccessQualifier::ReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* FunctionParameterAttributeToString(FunctionParameterAttribute value) { + switch (value) { + case FunctionParameterAttribute::Zext: return "Zext"; + case FunctionParameterAttribute::Sext: return "Sext"; + case FunctionParameterAttribute::ByVal: return "ByVal"; + case FunctionParameterAttribute::Sret: return "Sret"; + case FunctionParameterAttribute::NoAlias: return "NoAlias"; + case FunctionParameterAttribute::NoCapture: return "NoCapture"; + case FunctionParameterAttribute::NoWrite: return "NoWrite"; + case FunctionParameterAttribute::NoReadWrite: return "NoReadWrite"; + case FunctionParameterAttribute::RuntimeAlignedINTEL: return "RuntimeAlignedINTEL"; + default: return "Unknown"; + } +} + +inline const char* DecorationToString(Decoration value) { + switch (value) { + case Decoration::RelaxedPrecision: return "RelaxedPrecision"; + case Decoration::SpecId: return "SpecId"; + case Decoration::Block: return "Block"; + case Decoration::BufferBlock: return "BufferBlock"; + case Decoration::RowMajor: return "RowMajor"; + case Decoration::ColMajor: return "ColMajor"; + case Decoration::ArrayStride: return "ArrayStride"; + case Decoration::MatrixStride: return "MatrixStride"; + case Decoration::GLSLShared: return "GLSLShared"; + case Decoration::GLSLPacked: return "GLSLPacked"; + case Decoration::CPacked: return "CPacked"; + case Decoration::BuiltIn: return "BuiltIn"; + case Decoration::NoPerspective: return "NoPerspective"; + case Decoration::Flat: return "Flat"; + case Decoration::Patch: return "Patch"; + case Decoration::Centroid: return "Centroid"; + case Decoration::Sample: return "Sample"; + case Decoration::Invariant: return "Invariant"; + case Decoration::Restrict: return "Restrict"; + case Decoration::Aliased: return "Aliased"; + case Decoration::Volatile: return "Volatile"; + case Decoration::Constant: return "Constant"; + case Decoration::Coherent: return "Coherent"; + case Decoration::NonWritable: return "NonWritable"; + case Decoration::NonReadable: return "NonReadable"; + case Decoration::Uniform: return "Uniform"; + case Decoration::UniformId: return "UniformId"; + case Decoration::SaturatedConversion: return "SaturatedConversion"; + case Decoration::Stream: return "Stream"; + case Decoration::Location: return "Location"; + case Decoration::Component: return "Component"; + case Decoration::Index: return "Index"; + case Decoration::Binding: return "Binding"; + case Decoration::DescriptorSet: return "DescriptorSet"; + case Decoration::Offset: return "Offset"; + case Decoration::XfbBuffer: return "XfbBuffer"; + case Decoration::XfbStride: return "XfbStride"; + case Decoration::FuncParamAttr: return "FuncParamAttr"; + case Decoration::FPRoundingMode: return "FPRoundingMode"; + case Decoration::FPFastMathMode: return "FPFastMathMode"; + case Decoration::LinkageAttributes: return "LinkageAttributes"; + case Decoration::NoContraction: return "NoContraction"; + case Decoration::InputAttachmentIndex: return "InputAttachmentIndex"; + case Decoration::Alignment: return "Alignment"; + case Decoration::MaxByteOffset: return "MaxByteOffset"; + case Decoration::AlignmentId: return "AlignmentId"; + case Decoration::MaxByteOffsetId: return "MaxByteOffsetId"; + case Decoration::NoSignedWrap: return "NoSignedWrap"; + case Decoration::NoUnsignedWrap: return "NoUnsignedWrap"; + case Decoration::WeightTextureQCOM: return "WeightTextureQCOM"; + case Decoration::BlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case Decoration::BlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case Decoration::ExplicitInterpAMD: return "ExplicitInterpAMD"; + case Decoration::NodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case Decoration::NodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case Decoration::TrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case Decoration::PayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case Decoration::PayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case Decoration::PayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case Decoration::PayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case Decoration::PayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case Decoration::OverrideCoverageNV: return "OverrideCoverageNV"; + case Decoration::PassthroughNV: return "PassthroughNV"; + case Decoration::ViewportRelativeNV: return "ViewportRelativeNV"; + case Decoration::SecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case Decoration::PerPrimitiveEXT: return "PerPrimitiveEXT"; + case Decoration::PerViewNV: return "PerViewNV"; + case Decoration::PerTaskNV: return "PerTaskNV"; + case Decoration::PerVertexKHR: return "PerVertexKHR"; + case Decoration::NonUniform: return "NonUniform"; + case Decoration::RestrictPointer: return "RestrictPointer"; + case Decoration::AliasedPointer: return "AliasedPointer"; + case Decoration::HitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case Decoration::BindlessSamplerNV: return "BindlessSamplerNV"; + case Decoration::BindlessImageNV: return "BindlessImageNV"; + case Decoration::BoundSamplerNV: return "BoundSamplerNV"; + case Decoration::BoundImageNV: return "BoundImageNV"; + case Decoration::SIMTCallINTEL: return "SIMTCallINTEL"; + case Decoration::ReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case Decoration::ClobberINTEL: return "ClobberINTEL"; + case Decoration::SideEffectsINTEL: return "SideEffectsINTEL"; + case Decoration::VectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case Decoration::FuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case Decoration::VectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case Decoration::StackCallINTEL: return "StackCallINTEL"; + case Decoration::GlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case Decoration::CounterBuffer: return "CounterBuffer"; + case Decoration::HlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case Decoration::UserTypeGOOGLE: return "UserTypeGOOGLE"; + case Decoration::FunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case Decoration::FunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case Decoration::RegisterINTEL: return "RegisterINTEL"; + case Decoration::MemoryINTEL: return "MemoryINTEL"; + case Decoration::NumbanksINTEL: return "NumbanksINTEL"; + case Decoration::BankwidthINTEL: return "BankwidthINTEL"; + case Decoration::MaxPrivateCopiesINTEL: return "MaxPrivateCopiesINTEL"; + case Decoration::SinglepumpINTEL: return "SinglepumpINTEL"; + case Decoration::DoublepumpINTEL: return "DoublepumpINTEL"; + case Decoration::MaxReplicatesINTEL: return "MaxReplicatesINTEL"; + case Decoration::SimpleDualPortINTEL: return "SimpleDualPortINTEL"; + case Decoration::MergeINTEL: return "MergeINTEL"; + case Decoration::BankBitsINTEL: return "BankBitsINTEL"; + case Decoration::ForcePow2DepthINTEL: return "ForcePow2DepthINTEL"; + case Decoration::StridesizeINTEL: return "StridesizeINTEL"; + case Decoration::WordsizeINTEL: return "WordsizeINTEL"; + case Decoration::TrueDualPortINTEL: return "TrueDualPortINTEL"; + case Decoration::BurstCoalesceINTEL: return "BurstCoalesceINTEL"; + case Decoration::CacheSizeINTEL: return "CacheSizeINTEL"; + case Decoration::DontStaticallyCoalesceINTEL: return "DontStaticallyCoalesceINTEL"; + case Decoration::PrefetchINTEL: return "PrefetchINTEL"; + case Decoration::StallEnableINTEL: return "StallEnableINTEL"; + case Decoration::FuseLoopsInFunctionINTEL: return "FuseLoopsInFunctionINTEL"; + case Decoration::MathOpDSPModeINTEL: return "MathOpDSPModeINTEL"; + case Decoration::AliasScopeINTEL: return "AliasScopeINTEL"; + case Decoration::NoAliasINTEL: return "NoAliasINTEL"; + case Decoration::InitiationIntervalINTEL: return "InitiationIntervalINTEL"; + case Decoration::MaxConcurrencyINTEL: return "MaxConcurrencyINTEL"; + case Decoration::PipelineEnableINTEL: return "PipelineEnableINTEL"; + case Decoration::BufferLocationINTEL: return "BufferLocationINTEL"; + case Decoration::IOPipeStorageINTEL: return "IOPipeStorageINTEL"; + case Decoration::FunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case Decoration::SingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case Decoration::VectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case Decoration::MediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case Decoration::StallFreeINTEL: return "StallFreeINTEL"; + case Decoration::FPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case Decoration::LatencyControlLabelINTEL: return "LatencyControlLabelINTEL"; + case Decoration::LatencyControlConstraintINTEL: return "LatencyControlConstraintINTEL"; + case Decoration::ConduitKernelArgumentINTEL: return "ConduitKernelArgumentINTEL"; + case Decoration::RegisterMapKernelArgumentINTEL: return "RegisterMapKernelArgumentINTEL"; + case Decoration::MMHostInterfaceAddressWidthINTEL: return "MMHostInterfaceAddressWidthINTEL"; + case Decoration::MMHostInterfaceDataWidthINTEL: return "MMHostInterfaceDataWidthINTEL"; + case Decoration::MMHostInterfaceLatencyINTEL: return "MMHostInterfaceLatencyINTEL"; + case Decoration::MMHostInterfaceReadWriteModeINTEL: return "MMHostInterfaceReadWriteModeINTEL"; + case Decoration::MMHostInterfaceMaxBurstINTEL: return "MMHostInterfaceMaxBurstINTEL"; + case Decoration::MMHostInterfaceWaitRequestINTEL: return "MMHostInterfaceWaitRequestINTEL"; + case Decoration::StableKernelArgumentINTEL: return "StableKernelArgumentINTEL"; + case Decoration::HostAccessINTEL: return "HostAccessINTEL"; + case Decoration::InitModeINTEL: return "InitModeINTEL"; + case Decoration::ImplementInRegisterMapINTEL: return "ImplementInRegisterMapINTEL"; + case Decoration::CacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case Decoration::CacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* BuiltInToString(BuiltIn value) { + switch (value) { + case BuiltIn::Position: return "Position"; + case BuiltIn::PointSize: return "PointSize"; + case BuiltIn::ClipDistance: return "ClipDistance"; + case BuiltIn::CullDistance: return "CullDistance"; + case BuiltIn::VertexId: return "VertexId"; + case BuiltIn::InstanceId: return "InstanceId"; + case BuiltIn::PrimitiveId: return "PrimitiveId"; + case BuiltIn::InvocationId: return "InvocationId"; + case BuiltIn::Layer: return "Layer"; + case BuiltIn::ViewportIndex: return "ViewportIndex"; + case BuiltIn::TessLevelOuter: return "TessLevelOuter"; + case BuiltIn::TessLevelInner: return "TessLevelInner"; + case BuiltIn::TessCoord: return "TessCoord"; + case BuiltIn::PatchVertices: return "PatchVertices"; + case BuiltIn::FragCoord: return "FragCoord"; + case BuiltIn::PointCoord: return "PointCoord"; + case BuiltIn::FrontFacing: return "FrontFacing"; + case BuiltIn::SampleId: return "SampleId"; + case BuiltIn::SamplePosition: return "SamplePosition"; + case BuiltIn::SampleMask: return "SampleMask"; + case BuiltIn::FragDepth: return "FragDepth"; + case BuiltIn::HelperInvocation: return "HelperInvocation"; + case BuiltIn::NumWorkgroups: return "NumWorkgroups"; + case BuiltIn::WorkgroupSize: return "WorkgroupSize"; + case BuiltIn::WorkgroupId: return "WorkgroupId"; + case BuiltIn::LocalInvocationId: return "LocalInvocationId"; + case BuiltIn::GlobalInvocationId: return "GlobalInvocationId"; + case BuiltIn::LocalInvocationIndex: return "LocalInvocationIndex"; + case BuiltIn::WorkDim: return "WorkDim"; + case BuiltIn::GlobalSize: return "GlobalSize"; + case BuiltIn::EnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case BuiltIn::GlobalOffset: return "GlobalOffset"; + case BuiltIn::GlobalLinearId: return "GlobalLinearId"; + case BuiltIn::SubgroupSize: return "SubgroupSize"; + case BuiltIn::SubgroupMaxSize: return "SubgroupMaxSize"; + case BuiltIn::NumSubgroups: return "NumSubgroups"; + case BuiltIn::NumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case BuiltIn::SubgroupId: return "SubgroupId"; + case BuiltIn::SubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case BuiltIn::VertexIndex: return "VertexIndex"; + case BuiltIn::InstanceIndex: return "InstanceIndex"; + case BuiltIn::CoreIDARM: return "CoreIDARM"; + case BuiltIn::CoreCountARM: return "CoreCountARM"; + case BuiltIn::CoreMaxIDARM: return "CoreMaxIDARM"; + case BuiltIn::WarpIDARM: return "WarpIDARM"; + case BuiltIn::WarpMaxIDARM: return "WarpMaxIDARM"; + case BuiltIn::SubgroupEqMask: return "SubgroupEqMask"; + case BuiltIn::SubgroupGeMask: return "SubgroupGeMask"; + case BuiltIn::SubgroupGtMask: return "SubgroupGtMask"; + case BuiltIn::SubgroupLeMask: return "SubgroupLeMask"; + case BuiltIn::SubgroupLtMask: return "SubgroupLtMask"; + case BuiltIn::BaseVertex: return "BaseVertex"; + case BuiltIn::BaseInstance: return "BaseInstance"; + case BuiltIn::DrawIndex: return "DrawIndex"; + case BuiltIn::PrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case BuiltIn::DeviceIndex: return "DeviceIndex"; + case BuiltIn::ViewIndex: return "ViewIndex"; + case BuiltIn::ShadingRateKHR: return "ShadingRateKHR"; + case BuiltIn::TileOffsetQCOM: return "TileOffsetQCOM"; + case BuiltIn::TileDimensionQCOM: return "TileDimensionQCOM"; + case BuiltIn::TileApronSizeQCOM: return "TileApronSizeQCOM"; + case BuiltIn::BaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case BuiltIn::BaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case BuiltIn::BaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case BuiltIn::BaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case BuiltIn::BaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case BuiltIn::BaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case BuiltIn::BaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case BuiltIn::FragStencilRefEXT: return "FragStencilRefEXT"; + case BuiltIn::RemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case BuiltIn::ShaderIndexAMDX: return "ShaderIndexAMDX"; + case BuiltIn::ViewportMaskNV: return "ViewportMaskNV"; + case BuiltIn::SecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltIn::SecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltIn::PositionPerViewNV: return "PositionPerViewNV"; + case BuiltIn::ViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltIn::FullyCoveredEXT: return "FullyCoveredEXT"; + case BuiltIn::TaskCountNV: return "TaskCountNV"; + case BuiltIn::PrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltIn::PrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltIn::ClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltIn::CullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltIn::LayerPerViewNV: return "LayerPerViewNV"; + case BuiltIn::MeshViewCountNV: return "MeshViewCountNV"; + case BuiltIn::MeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltIn::BaryCoordKHR: return "BaryCoordKHR"; + case BuiltIn::BaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case BuiltIn::FragSizeEXT: return "FragSizeEXT"; + case BuiltIn::FragInvocationCountEXT: return "FragInvocationCountEXT"; + case BuiltIn::PrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case BuiltIn::PrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case BuiltIn::PrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case BuiltIn::CullPrimitiveEXT: return "CullPrimitiveEXT"; + case BuiltIn::LaunchIdKHR: return "LaunchIdKHR"; + case BuiltIn::LaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltIn::WorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltIn::WorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltIn::ObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltIn::ObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltIn::RayTminKHR: return "RayTminKHR"; + case BuiltIn::RayTmaxKHR: return "RayTmaxKHR"; + case BuiltIn::InstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltIn::ObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltIn::WorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltIn::HitTNV: return "HitTNV"; + case BuiltIn::HitKindKHR: return "HitKindKHR"; + case BuiltIn::CurrentRayTimeNV: return "CurrentRayTimeNV"; + case BuiltIn::HitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case BuiltIn::HitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case BuiltIn::HitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case BuiltIn::IncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltIn::RayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltIn::HitIsSphereNV: return "HitIsSphereNV"; + case BuiltIn::HitIsLSSNV: return "HitIsLSSNV"; + case BuiltIn::HitSpherePositionNV: return "HitSpherePositionNV"; + case BuiltIn::WarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltIn::SMCountNV: return "SMCountNV"; + case BuiltIn::WarpIDNV: return "WarpIDNV"; + case BuiltIn::SMIDNV: return "SMIDNV"; + case BuiltIn::HitLSSPositionsNV: return "HitLSSPositionsNV"; + case BuiltIn::HitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case BuiltIn::HitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case BuiltIn::HitSphereRadiusNV: return "HitSphereRadiusNV"; + case BuiltIn::HitLSSRadiiNV: return "HitLSSRadiiNV"; + case BuiltIn::ClusterIDNV: return "ClusterIDNV"; + case BuiltIn::CullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* ScopeToString(Scope value) { + switch (value) { + case Scope::CrossDevice: return "CrossDevice"; + case Scope::Device: return "Device"; + case Scope::Workgroup: return "Workgroup"; + case Scope::Subgroup: return "Subgroup"; + case Scope::Invocation: return "Invocation"; + case Scope::QueueFamily: return "QueueFamily"; + case Scope::ShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* GroupOperationToString(GroupOperation value) { + switch (value) { + case GroupOperation::Reduce: return "Reduce"; + case GroupOperation::InclusiveScan: return "InclusiveScan"; + case GroupOperation::ExclusiveScan: return "ExclusiveScan"; + case GroupOperation::ClusteredReduce: return "ClusteredReduce"; + case GroupOperation::PartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperation::PartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperation::PartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + default: return "Unknown"; + } +} + +inline const char* KernelEnqueueFlagsToString(KernelEnqueueFlags value) { + switch (value) { + case KernelEnqueueFlags::NoWait: return "NoWait"; + case KernelEnqueueFlags::WaitKernel: return "WaitKernel"; + case KernelEnqueueFlags::WaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* CapabilityToString(Capability value) { + switch (value) { + case Capability::Matrix: return "Matrix"; + case Capability::Shader: return "Shader"; + case Capability::Geometry: return "Geometry"; + case Capability::Tessellation: return "Tessellation"; + case Capability::Addresses: return "Addresses"; + case Capability::Linkage: return "Linkage"; + case Capability::Kernel: return "Kernel"; + case Capability::Vector16: return "Vector16"; + case Capability::Float16Buffer: return "Float16Buffer"; + case Capability::Float16: return "Float16"; + case Capability::Float64: return "Float64"; + case Capability::Int64: return "Int64"; + case Capability::Int64Atomics: return "Int64Atomics"; + case Capability::ImageBasic: return "ImageBasic"; + case Capability::ImageReadWrite: return "ImageReadWrite"; + case Capability::ImageMipmap: return "ImageMipmap"; + case Capability::Pipes: return "Pipes"; + case Capability::Groups: return "Groups"; + case Capability::DeviceEnqueue: return "DeviceEnqueue"; + case Capability::LiteralSampler: return "LiteralSampler"; + case Capability::AtomicStorage: return "AtomicStorage"; + case Capability::Int16: return "Int16"; + case Capability::TessellationPointSize: return "TessellationPointSize"; + case Capability::GeometryPointSize: return "GeometryPointSize"; + case Capability::ImageGatherExtended: return "ImageGatherExtended"; + case Capability::StorageImageMultisample: return "StorageImageMultisample"; + case Capability::UniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case Capability::SampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case Capability::StorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case Capability::StorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case Capability::ClipDistance: return "ClipDistance"; + case Capability::CullDistance: return "CullDistance"; + case Capability::ImageCubeArray: return "ImageCubeArray"; + case Capability::SampleRateShading: return "SampleRateShading"; + case Capability::ImageRect: return "ImageRect"; + case Capability::SampledRect: return "SampledRect"; + case Capability::GenericPointer: return "GenericPointer"; + case Capability::Int8: return "Int8"; + case Capability::InputAttachment: return "InputAttachment"; + case Capability::SparseResidency: return "SparseResidency"; + case Capability::MinLod: return "MinLod"; + case Capability::Sampled1D: return "Sampled1D"; + case Capability::Image1D: return "Image1D"; + case Capability::SampledCubeArray: return "SampledCubeArray"; + case Capability::SampledBuffer: return "SampledBuffer"; + case Capability::ImageBuffer: return "ImageBuffer"; + case Capability::ImageMSArray: return "ImageMSArray"; + case Capability::StorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case Capability::ImageQuery: return "ImageQuery"; + case Capability::DerivativeControl: return "DerivativeControl"; + case Capability::InterpolationFunction: return "InterpolationFunction"; + case Capability::TransformFeedback: return "TransformFeedback"; + case Capability::GeometryStreams: return "GeometryStreams"; + case Capability::StorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case Capability::StorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case Capability::MultiViewport: return "MultiViewport"; + case Capability::SubgroupDispatch: return "SubgroupDispatch"; + case Capability::NamedBarrier: return "NamedBarrier"; + case Capability::PipeStorage: return "PipeStorage"; + case Capability::GroupNonUniform: return "GroupNonUniform"; + case Capability::GroupNonUniformVote: return "GroupNonUniformVote"; + case Capability::GroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case Capability::GroupNonUniformBallot: return "GroupNonUniformBallot"; + case Capability::GroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case Capability::GroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case Capability::GroupNonUniformClustered: return "GroupNonUniformClustered"; + case Capability::GroupNonUniformQuad: return "GroupNonUniformQuad"; + case Capability::ShaderLayer: return "ShaderLayer"; + case Capability::ShaderViewportIndex: return "ShaderViewportIndex"; + case Capability::UniformDecoration: return "UniformDecoration"; + case Capability::CoreBuiltinsARM: return "CoreBuiltinsARM"; + case Capability::TileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case Capability::TileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case Capability::TileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case Capability::CooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case Capability::FragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case Capability::SubgroupBallotKHR: return "SubgroupBallotKHR"; + case Capability::DrawParameters: return "DrawParameters"; + case Capability::WorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case Capability::SubgroupVoteKHR: return "SubgroupVoteKHR"; + case Capability::StorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case Capability::StorageUniform16: return "StorageUniform16"; + case Capability::StoragePushConstant16: return "StoragePushConstant16"; + case Capability::StorageInputOutput16: return "StorageInputOutput16"; + case Capability::DeviceGroup: return "DeviceGroup"; + case Capability::MultiView: return "MultiView"; + case Capability::VariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case Capability::VariablePointers: return "VariablePointers"; + case Capability::AtomicStorageOps: return "AtomicStorageOps"; + case Capability::SampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case Capability::StorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case Capability::UniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case Capability::StoragePushConstant8: return "StoragePushConstant8"; + case Capability::DenormPreserve: return "DenormPreserve"; + case Capability::DenormFlushToZero: return "DenormFlushToZero"; + case Capability::SignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case Capability::RoundingModeRTE: return "RoundingModeRTE"; + case Capability::RoundingModeRTZ: return "RoundingModeRTZ"; + case Capability::RayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case Capability::RayQueryKHR: return "RayQueryKHR"; + case Capability::UntypedPointersKHR: return "UntypedPointersKHR"; + case Capability::RayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case Capability::RayTracingKHR: return "RayTracingKHR"; + case Capability::TextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case Capability::TextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case Capability::TextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case Capability::TileShadingQCOM: return "TileShadingQCOM"; + case Capability::TextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case Capability::Float16ImageAMD: return "Float16ImageAMD"; + case Capability::ImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case Capability::FragmentMaskAMD: return "FragmentMaskAMD"; + case Capability::StencilExportEXT: return "StencilExportEXT"; + case Capability::ImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case Capability::Int64ImageEXT: return "Int64ImageEXT"; + case Capability::ShaderClockKHR: return "ShaderClockKHR"; + case Capability::ShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case Capability::QuadControlKHR: return "QuadControlKHR"; + case Capability::BFloat16TypeKHR: return "BFloat16TypeKHR"; + case Capability::BFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case Capability::BFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case Capability::SampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case Capability::GeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case Capability::ShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case Capability::ShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case Capability::ShaderStereoViewNV: return "ShaderStereoViewNV"; + case Capability::PerViewAttributesNV: return "PerViewAttributesNV"; + case Capability::FragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case Capability::MeshShadingNV: return "MeshShadingNV"; + case Capability::ImageFootprintNV: return "ImageFootprintNV"; + case Capability::MeshShadingEXT: return "MeshShadingEXT"; + case Capability::FragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case Capability::ComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case Capability::FragmentDensityEXT: return "FragmentDensityEXT"; + case Capability::GroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case Capability::ShaderNonUniform: return "ShaderNonUniform"; + case Capability::RuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case Capability::InputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case Capability::UniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case Capability::StorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case Capability::UniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case Capability::SampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case Capability::StorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case Capability::StorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case Capability::InputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case Capability::UniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case Capability::StorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case Capability::RayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case Capability::RayTracingNV: return "RayTracingNV"; + case Capability::RayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case Capability::VulkanMemoryModel: return "VulkanMemoryModel"; + case Capability::VulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case Capability::PhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case Capability::ComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case Capability::RayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case Capability::CooperativeMatrixNV: return "CooperativeMatrixNV"; + case Capability::FragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case Capability::FragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case Capability::ShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case Capability::FragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case Capability::DemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case Capability::DisplacementMicromapNV: return "DisplacementMicromapNV"; + case Capability::RayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case Capability::ShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case Capability::BindlessTextureNV: return "BindlessTextureNV"; + case Capability::RayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case Capability::CooperativeVectorNV: return "CooperativeVectorNV"; + case Capability::AtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case Capability::RayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case Capability::RawAccessChainsNV: return "RawAccessChainsNV"; + case Capability::RayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case Capability::RayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + case Capability::CooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case Capability::CooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case Capability::CooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case Capability::CooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case Capability::CooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case Capability::CooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case Capability::RayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + case Capability::TensorAddressingNV: return "TensorAddressingNV"; + case Capability::SubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case Capability::SubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case Capability::SubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case Capability::SubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case Capability::RoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case Capability::FloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case Capability::IntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case Capability::FunctionPointersINTEL: return "FunctionPointersINTEL"; + case Capability::IndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case Capability::AsmINTEL: return "AsmINTEL"; + case Capability::AtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case Capability::AtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case Capability::AtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case Capability::VectorComputeINTEL: return "VectorComputeINTEL"; + case Capability::VectorAnyINTEL: return "VectorAnyINTEL"; + case Capability::ExpectAssumeKHR: return "ExpectAssumeKHR"; + case Capability::SubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case Capability::SubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case Capability::SubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case Capability::VariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case Capability::FunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case Capability::FPGAMemoryAttributesINTEL: return "FPGAMemoryAttributesINTEL"; + case Capability::FPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case Capability::ArbitraryPrecisionIntegersINTEL: return "ArbitraryPrecisionIntegersINTEL"; + case Capability::ArbitraryPrecisionFloatingPointINTEL: return "ArbitraryPrecisionFloatingPointINTEL"; + case Capability::UnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case Capability::FPGALoopControlsINTEL: return "FPGALoopControlsINTEL"; + case Capability::KernelAttributesINTEL: return "KernelAttributesINTEL"; + case Capability::FPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case Capability::FPGAMemoryAccessesINTEL: return "FPGAMemoryAccessesINTEL"; + case Capability::FPGAClusterAttributesINTEL: return "FPGAClusterAttributesINTEL"; + case Capability::LoopFuseINTEL: return "LoopFuseINTEL"; + case Capability::FPGADSPControlINTEL: return "FPGADSPControlINTEL"; + case Capability::MemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case Capability::FPGAInvocationPipeliningAttributesINTEL: return "FPGAInvocationPipeliningAttributesINTEL"; + case Capability::FPGABufferLocationINTEL: return "FPGABufferLocationINTEL"; + case Capability::ArbitraryPrecisionFixedPointINTEL: return "ArbitraryPrecisionFixedPointINTEL"; + case Capability::USMStorageClassesINTEL: return "USMStorageClassesINTEL"; + case Capability::RuntimeAlignedAttributeINTEL: return "RuntimeAlignedAttributeINTEL"; + case Capability::IOPipesINTEL: return "IOPipesINTEL"; + case Capability::BlockingPipesINTEL: return "BlockingPipesINTEL"; + case Capability::FPGARegINTEL: return "FPGARegINTEL"; + case Capability::DotProductInputAll: return "DotProductInputAll"; + case Capability::DotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case Capability::DotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case Capability::DotProduct: return "DotProduct"; + case Capability::RayCullMaskKHR: return "RayCullMaskKHR"; + case Capability::CooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case Capability::ReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case Capability::BitInstructions: return "BitInstructions"; + case Capability::GroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case Capability::FloatControls2: return "FloatControls2"; + case Capability::AtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case Capability::AtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case Capability::LongCompositesINTEL: return "LongCompositesINTEL"; + case Capability::OptNoneEXT: return "OptNoneEXT"; + case Capability::AtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case Capability::DebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case Capability::BFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case Capability::SplitBarrierINTEL: return "SplitBarrierINTEL"; + case Capability::ArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case Capability::FPGAClusterAttributesV2INTEL: return "FPGAClusterAttributesV2INTEL"; + case Capability::FPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case Capability::TaskSequenceINTEL: return "TaskSequenceINTEL"; + case Capability::FPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case Capability::FPGALatencyControlINTEL: return "FPGALatencyControlINTEL"; + case Capability::FPGAArgumentInterfacesINTEL: return "FPGAArgumentInterfacesINTEL"; + case Capability::GlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case Capability::GlobalVariableFPGADecorationsINTEL: return "GlobalVariableFPGADecorationsINTEL"; + case Capability::SubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case Capability::Subgroup2DBlockIOINTEL: return "Subgroup2DBlockIOINTEL"; + case Capability::Subgroup2DBlockTransformINTEL: return "Subgroup2DBlockTransformINTEL"; + case Capability::Subgroup2DBlockTransposeINTEL: return "Subgroup2DBlockTransposeINTEL"; + case Capability::SubgroupMatrixMultiplyAccumulateINTEL: return "SubgroupMatrixMultiplyAccumulateINTEL"; + case Capability::TernaryBitwiseFunctionINTEL: return "TernaryBitwiseFunctionINTEL"; + case Capability::GroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case Capability::TensorFloat32RoundingINTEL: return "TensorFloat32RoundingINTEL"; + case Capability::MaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case Capability::CacheControlsINTEL: return "CacheControlsINTEL"; + case Capability::RegisterLimitsINTEL: return "RegisterLimitsINTEL"; + default: return "Unknown"; + } +} + +inline const char* RayQueryIntersectionToString(RayQueryIntersection value) { + switch (value) { + case RayQueryIntersection::RayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case RayQueryIntersection::RayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCommittedIntersectionTypeToString(RayQueryCommittedIntersectionType value) { + switch (value) { + case RayQueryCommittedIntersectionType::RayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case RayQueryCommittedIntersectionType::RayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case RayQueryCommittedIntersectionType::RayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCandidateIntersectionTypeToString(RayQueryCandidateIntersectionType value) { + switch (value) { + case RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case RayQueryCandidateIntersectionType::RayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* FPDenormModeToString(FPDenormMode value) { + switch (value) { + case FPDenormMode::Preserve: return "Preserve"; + case FPDenormMode::FlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* FPOperationModeToString(FPOperationMode value) { + switch (value) { + case FPOperationMode::IEEE: return "IEEE"; + case FPOperationMode::ALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* QuantizationModesToString(QuantizationModes value) { + switch (value) { + case QuantizationModes::TRN: return "TRN"; + case QuantizationModes::TRN_ZERO: return "TRN_ZERO"; + case QuantizationModes::RND: return "RND"; + case QuantizationModes::RND_ZERO: return "RND_ZERO"; + case QuantizationModes::RND_INF: return "RND_INF"; + case QuantizationModes::RND_MIN_INF: return "RND_MIN_INF"; + case QuantizationModes::RND_CONV: return "RND_CONV"; + case QuantizationModes::RND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* OverflowModesToString(OverflowModes value) { + switch (value) { + case OverflowModes::WRAP: return "WRAP"; + case OverflowModes::SAT: return "SAT"; + case OverflowModes::SAT_ZERO: return "SAT_ZERO"; + case OverflowModes::SAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* PackedVectorFormatToString(PackedVectorFormat value) { + switch (value) { + case PackedVectorFormat::PackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixLayoutToString(CooperativeMatrixLayout value) { + switch (value) { + case CooperativeMatrixLayout::RowMajorKHR: return "RowMajorKHR"; + case CooperativeMatrixLayout::ColumnMajorKHR: return "ColumnMajorKHR"; + case CooperativeMatrixLayout::RowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case CooperativeMatrixLayout::ColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixUseToString(CooperativeMatrixUse value) { + switch (value) { + case CooperativeMatrixUse::MatrixAKHR: return "MatrixAKHR"; + case CooperativeMatrixUse::MatrixBKHR: return "MatrixBKHR"; + case CooperativeMatrixUse::MatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* TensorClampModeToString(TensorClampMode value) { + switch (value) { + case TensorClampMode::Undefined: return "Undefined"; + case TensorClampMode::Constant: return "Constant"; + case TensorClampMode::ClampToEdge: return "ClampToEdge"; + case TensorClampMode::Repeat: return "Repeat"; + case TensorClampMode::RepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* InitializationModeQualifierToString(InitializationModeQualifier value) { + switch (value) { + case InitializationModeQualifier::InitOnDeviceReprogramINTEL: return "InitOnDeviceReprogramINTEL"; + case InitializationModeQualifier::InitOnDeviceResetINTEL: return "InitOnDeviceResetINTEL"; + default: return "Unknown"; + } +} + +inline const char* HostAccessQualifierToString(HostAccessQualifier value) { + switch (value) { + case HostAccessQualifier::NoneINTEL: return "NoneINTEL"; + case HostAccessQualifier::ReadINTEL: return "ReadINTEL"; + case HostAccessQualifier::WriteINTEL: return "WriteINTEL"; + case HostAccessQualifier::ReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* LoadCacheControlToString(LoadCacheControl value) { + switch (value) { + case LoadCacheControl::UncachedINTEL: return "UncachedINTEL"; + case LoadCacheControl::CachedINTEL: return "CachedINTEL"; + case LoadCacheControl::StreamingINTEL: return "StreamingINTEL"; + case LoadCacheControl::InvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case LoadCacheControl::ConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* StoreCacheControlToString(StoreCacheControl value) { + switch (value) { + case StoreCacheControl::UncachedINTEL: return "UncachedINTEL"; + case StoreCacheControl::WriteThroughINTEL: return "WriteThroughINTEL"; + case StoreCacheControl::WriteBackINTEL: return "WriteBackINTEL"; + case StoreCacheControl::StreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* NamedMaximumNumberOfRegistersToString(NamedMaximumNumberOfRegisters value) { + switch (value) { + case NamedMaximumNumberOfRegisters::AutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* FPEncodingToString(FPEncoding value) { + switch (value) { + case FPEncoding::BFloat16KHR: return "BFloat16KHR"; + default: return "Unknown"; + } +} + +inline const char* CooperativeVectorMatrixLayoutToString(CooperativeVectorMatrixLayout value) { + switch (value) { + case CooperativeVectorMatrixLayout::RowMajorNV: return "RowMajorNV"; + case CooperativeVectorMatrixLayout::ColumnMajorNV: return "ColumnMajorNV"; + case CooperativeVectorMatrixLayout::InferencingOptimalNV: return "InferencingOptimalNV"; + case CooperativeVectorMatrixLayout::TrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* ComponentTypeToString(ComponentType value) { + switch (value) { + case ComponentType::Float16NV: return "Float16NV"; + case ComponentType::Float32NV: return "Float32NV"; + case ComponentType::Float64NV: return "Float64NV"; + case ComponentType::SignedInt8NV: return "SignedInt8NV"; + case ComponentType::SignedInt16NV: return "SignedInt16NV"; + case ComponentType::SignedInt32NV: return "SignedInt32NV"; + case ComponentType::SignedInt64NV: return "SignedInt64NV"; + case ComponentType::UnsignedInt8NV: return "UnsignedInt8NV"; + case ComponentType::UnsignedInt16NV: return "UnsignedInt16NV"; + case ComponentType::UnsignedInt32NV: return "UnsignedInt32NV"; + case ComponentType::UnsignedInt64NV: return "UnsignedInt64NV"; + case ComponentType::SignedInt8PackedNV: return "SignedInt8PackedNV"; + case ComponentType::UnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case ComponentType::FloatE4M3NV: return "FloatE4M3NV"; + case ComponentType::FloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* OpToString(Op value) { + switch (value) { + case Op::OpNop: return "OpNop"; + case Op::OpUndef: return "OpUndef"; + case Op::OpSourceContinued: return "OpSourceContinued"; + case Op::OpSource: return "OpSource"; + case Op::OpSourceExtension: return "OpSourceExtension"; + case Op::OpName: return "OpName"; + case Op::OpMemberName: return "OpMemberName"; + case Op::OpString: return "OpString"; + case Op::OpLine: return "OpLine"; + case Op::OpExtension: return "OpExtension"; + case Op::OpExtInstImport: return "OpExtInstImport"; + case Op::OpExtInst: return "OpExtInst"; + case Op::OpMemoryModel: return "OpMemoryModel"; + case Op::OpEntryPoint: return "OpEntryPoint"; + case Op::OpExecutionMode: return "OpExecutionMode"; + case Op::OpCapability: return "OpCapability"; + case Op::OpTypeVoid: return "OpTypeVoid"; + case Op::OpTypeBool: return "OpTypeBool"; + case Op::OpTypeInt: return "OpTypeInt"; + case Op::OpTypeFloat: return "OpTypeFloat"; + case Op::OpTypeVector: return "OpTypeVector"; + case Op::OpTypeMatrix: return "OpTypeMatrix"; + case Op::OpTypeImage: return "OpTypeImage"; + case Op::OpTypeSampler: return "OpTypeSampler"; + case Op::OpTypeSampledImage: return "OpTypeSampledImage"; + case Op::OpTypeArray: return "OpTypeArray"; + case Op::OpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case Op::OpTypeStruct: return "OpTypeStruct"; + case Op::OpTypeOpaque: return "OpTypeOpaque"; + case Op::OpTypePointer: return "OpTypePointer"; + case Op::OpTypeFunction: return "OpTypeFunction"; + case Op::OpTypeEvent: return "OpTypeEvent"; + case Op::OpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case Op::OpTypeReserveId: return "OpTypeReserveId"; + case Op::OpTypeQueue: return "OpTypeQueue"; + case Op::OpTypePipe: return "OpTypePipe"; + case Op::OpTypeForwardPointer: return "OpTypeForwardPointer"; + case Op::OpConstantTrue: return "OpConstantTrue"; + case Op::OpConstantFalse: return "OpConstantFalse"; + case Op::OpConstant: return "OpConstant"; + case Op::OpConstantComposite: return "OpConstantComposite"; + case Op::OpConstantSampler: return "OpConstantSampler"; + case Op::OpConstantNull: return "OpConstantNull"; + case Op::OpSpecConstantTrue: return "OpSpecConstantTrue"; + case Op::OpSpecConstantFalse: return "OpSpecConstantFalse"; + case Op::OpSpecConstant: return "OpSpecConstant"; + case Op::OpSpecConstantComposite: return "OpSpecConstantComposite"; + case Op::OpSpecConstantOp: return "OpSpecConstantOp"; + case Op::OpFunction: return "OpFunction"; + case Op::OpFunctionParameter: return "OpFunctionParameter"; + case Op::OpFunctionEnd: return "OpFunctionEnd"; + case Op::OpFunctionCall: return "OpFunctionCall"; + case Op::OpVariable: return "OpVariable"; + case Op::OpImageTexelPointer: return "OpImageTexelPointer"; + case Op::OpLoad: return "OpLoad"; + case Op::OpStore: return "OpStore"; + case Op::OpCopyMemory: return "OpCopyMemory"; + case Op::OpCopyMemorySized: return "OpCopyMemorySized"; + case Op::OpAccessChain: return "OpAccessChain"; + case Op::OpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case Op::OpPtrAccessChain: return "OpPtrAccessChain"; + case Op::OpArrayLength: return "OpArrayLength"; + case Op::OpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case Op::OpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case Op::OpDecorate: return "OpDecorate"; + case Op::OpMemberDecorate: return "OpMemberDecorate"; + case Op::OpDecorationGroup: return "OpDecorationGroup"; + case Op::OpGroupDecorate: return "OpGroupDecorate"; + case Op::OpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case Op::OpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case Op::OpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case Op::OpVectorShuffle: return "OpVectorShuffle"; + case Op::OpCompositeConstruct: return "OpCompositeConstruct"; + case Op::OpCompositeExtract: return "OpCompositeExtract"; + case Op::OpCompositeInsert: return "OpCompositeInsert"; + case Op::OpCopyObject: return "OpCopyObject"; + case Op::OpTranspose: return "OpTranspose"; + case Op::OpSampledImage: return "OpSampledImage"; + case Op::OpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case Op::OpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case Op::OpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case Op::OpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case Op::OpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case Op::OpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case Op::OpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case Op::OpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case Op::OpImageFetch: return "OpImageFetch"; + case Op::OpImageGather: return "OpImageGather"; + case Op::OpImageDrefGather: return "OpImageDrefGather"; + case Op::OpImageRead: return "OpImageRead"; + case Op::OpImageWrite: return "OpImageWrite"; + case Op::OpImage: return "OpImage"; + case Op::OpImageQueryFormat: return "OpImageQueryFormat"; + case Op::OpImageQueryOrder: return "OpImageQueryOrder"; + case Op::OpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case Op::OpImageQuerySize: return "OpImageQuerySize"; + case Op::OpImageQueryLod: return "OpImageQueryLod"; + case Op::OpImageQueryLevels: return "OpImageQueryLevels"; + case Op::OpImageQuerySamples: return "OpImageQuerySamples"; + case Op::OpConvertFToU: return "OpConvertFToU"; + case Op::OpConvertFToS: return "OpConvertFToS"; + case Op::OpConvertSToF: return "OpConvertSToF"; + case Op::OpConvertUToF: return "OpConvertUToF"; + case Op::OpUConvert: return "OpUConvert"; + case Op::OpSConvert: return "OpSConvert"; + case Op::OpFConvert: return "OpFConvert"; + case Op::OpQuantizeToF16: return "OpQuantizeToF16"; + case Op::OpConvertPtrToU: return "OpConvertPtrToU"; + case Op::OpSatConvertSToU: return "OpSatConvertSToU"; + case Op::OpSatConvertUToS: return "OpSatConvertUToS"; + case Op::OpConvertUToPtr: return "OpConvertUToPtr"; + case Op::OpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case Op::OpGenericCastToPtr: return "OpGenericCastToPtr"; + case Op::OpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case Op::OpBitcast: return "OpBitcast"; + case Op::OpSNegate: return "OpSNegate"; + case Op::OpFNegate: return "OpFNegate"; + case Op::OpIAdd: return "OpIAdd"; + case Op::OpFAdd: return "OpFAdd"; + case Op::OpISub: return "OpISub"; + case Op::OpFSub: return "OpFSub"; + case Op::OpIMul: return "OpIMul"; + case Op::OpFMul: return "OpFMul"; + case Op::OpUDiv: return "OpUDiv"; + case Op::OpSDiv: return "OpSDiv"; + case Op::OpFDiv: return "OpFDiv"; + case Op::OpUMod: return "OpUMod"; + case Op::OpSRem: return "OpSRem"; + case Op::OpSMod: return "OpSMod"; + case Op::OpFRem: return "OpFRem"; + case Op::OpFMod: return "OpFMod"; + case Op::OpVectorTimesScalar: return "OpVectorTimesScalar"; + case Op::OpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case Op::OpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case Op::OpMatrixTimesVector: return "OpMatrixTimesVector"; + case Op::OpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case Op::OpOuterProduct: return "OpOuterProduct"; + case Op::OpDot: return "OpDot"; + case Op::OpIAddCarry: return "OpIAddCarry"; + case Op::OpISubBorrow: return "OpISubBorrow"; + case Op::OpUMulExtended: return "OpUMulExtended"; + case Op::OpSMulExtended: return "OpSMulExtended"; + case Op::OpAny: return "OpAny"; + case Op::OpAll: return "OpAll"; + case Op::OpIsNan: return "OpIsNan"; + case Op::OpIsInf: return "OpIsInf"; + case Op::OpIsFinite: return "OpIsFinite"; + case Op::OpIsNormal: return "OpIsNormal"; + case Op::OpSignBitSet: return "OpSignBitSet"; + case Op::OpLessOrGreater: return "OpLessOrGreater"; + case Op::OpOrdered: return "OpOrdered"; + case Op::OpUnordered: return "OpUnordered"; + case Op::OpLogicalEqual: return "OpLogicalEqual"; + case Op::OpLogicalNotEqual: return "OpLogicalNotEqual"; + case Op::OpLogicalOr: return "OpLogicalOr"; + case Op::OpLogicalAnd: return "OpLogicalAnd"; + case Op::OpLogicalNot: return "OpLogicalNot"; + case Op::OpSelect: return "OpSelect"; + case Op::OpIEqual: return "OpIEqual"; + case Op::OpINotEqual: return "OpINotEqual"; + case Op::OpUGreaterThan: return "OpUGreaterThan"; + case Op::OpSGreaterThan: return "OpSGreaterThan"; + case Op::OpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case Op::OpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case Op::OpULessThan: return "OpULessThan"; + case Op::OpSLessThan: return "OpSLessThan"; + case Op::OpULessThanEqual: return "OpULessThanEqual"; + case Op::OpSLessThanEqual: return "OpSLessThanEqual"; + case Op::OpFOrdEqual: return "OpFOrdEqual"; + case Op::OpFUnordEqual: return "OpFUnordEqual"; + case Op::OpFOrdNotEqual: return "OpFOrdNotEqual"; + case Op::OpFUnordNotEqual: return "OpFUnordNotEqual"; + case Op::OpFOrdLessThan: return "OpFOrdLessThan"; + case Op::OpFUnordLessThan: return "OpFUnordLessThan"; + case Op::OpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case Op::OpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case Op::OpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case Op::OpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case Op::OpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case Op::OpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case Op::OpShiftRightLogical: return "OpShiftRightLogical"; + case Op::OpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case Op::OpShiftLeftLogical: return "OpShiftLeftLogical"; + case Op::OpBitwiseOr: return "OpBitwiseOr"; + case Op::OpBitwiseXor: return "OpBitwiseXor"; + case Op::OpBitwiseAnd: return "OpBitwiseAnd"; + case Op::OpNot: return "OpNot"; + case Op::OpBitFieldInsert: return "OpBitFieldInsert"; + case Op::OpBitFieldSExtract: return "OpBitFieldSExtract"; + case Op::OpBitFieldUExtract: return "OpBitFieldUExtract"; + case Op::OpBitReverse: return "OpBitReverse"; + case Op::OpBitCount: return "OpBitCount"; + case Op::OpDPdx: return "OpDPdx"; + case Op::OpDPdy: return "OpDPdy"; + case Op::OpFwidth: return "OpFwidth"; + case Op::OpDPdxFine: return "OpDPdxFine"; + case Op::OpDPdyFine: return "OpDPdyFine"; + case Op::OpFwidthFine: return "OpFwidthFine"; + case Op::OpDPdxCoarse: return "OpDPdxCoarse"; + case Op::OpDPdyCoarse: return "OpDPdyCoarse"; + case Op::OpFwidthCoarse: return "OpFwidthCoarse"; + case Op::OpEmitVertex: return "OpEmitVertex"; + case Op::OpEndPrimitive: return "OpEndPrimitive"; + case Op::OpEmitStreamVertex: return "OpEmitStreamVertex"; + case Op::OpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case Op::OpControlBarrier: return "OpControlBarrier"; + case Op::OpMemoryBarrier: return "OpMemoryBarrier"; + case Op::OpAtomicLoad: return "OpAtomicLoad"; + case Op::OpAtomicStore: return "OpAtomicStore"; + case Op::OpAtomicExchange: return "OpAtomicExchange"; + case Op::OpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case Op::OpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case Op::OpAtomicIIncrement: return "OpAtomicIIncrement"; + case Op::OpAtomicIDecrement: return "OpAtomicIDecrement"; + case Op::OpAtomicIAdd: return "OpAtomicIAdd"; + case Op::OpAtomicISub: return "OpAtomicISub"; + case Op::OpAtomicSMin: return "OpAtomicSMin"; + case Op::OpAtomicUMin: return "OpAtomicUMin"; + case Op::OpAtomicSMax: return "OpAtomicSMax"; + case Op::OpAtomicUMax: return "OpAtomicUMax"; + case Op::OpAtomicAnd: return "OpAtomicAnd"; + case Op::OpAtomicOr: return "OpAtomicOr"; + case Op::OpAtomicXor: return "OpAtomicXor"; + case Op::OpPhi: return "OpPhi"; + case Op::OpLoopMerge: return "OpLoopMerge"; + case Op::OpSelectionMerge: return "OpSelectionMerge"; + case Op::OpLabel: return "OpLabel"; + case Op::OpBranch: return "OpBranch"; + case Op::OpBranchConditional: return "OpBranchConditional"; + case Op::OpSwitch: return "OpSwitch"; + case Op::OpKill: return "OpKill"; + case Op::OpReturn: return "OpReturn"; + case Op::OpReturnValue: return "OpReturnValue"; + case Op::OpUnreachable: return "OpUnreachable"; + case Op::OpLifetimeStart: return "OpLifetimeStart"; + case Op::OpLifetimeStop: return "OpLifetimeStop"; + case Op::OpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case Op::OpGroupWaitEvents: return "OpGroupWaitEvents"; + case Op::OpGroupAll: return "OpGroupAll"; + case Op::OpGroupAny: return "OpGroupAny"; + case Op::OpGroupBroadcast: return "OpGroupBroadcast"; + case Op::OpGroupIAdd: return "OpGroupIAdd"; + case Op::OpGroupFAdd: return "OpGroupFAdd"; + case Op::OpGroupFMin: return "OpGroupFMin"; + case Op::OpGroupUMin: return "OpGroupUMin"; + case Op::OpGroupSMin: return "OpGroupSMin"; + case Op::OpGroupFMax: return "OpGroupFMax"; + case Op::OpGroupUMax: return "OpGroupUMax"; + case Op::OpGroupSMax: return "OpGroupSMax"; + case Op::OpReadPipe: return "OpReadPipe"; + case Op::OpWritePipe: return "OpWritePipe"; + case Op::OpReservedReadPipe: return "OpReservedReadPipe"; + case Op::OpReservedWritePipe: return "OpReservedWritePipe"; + case Op::OpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case Op::OpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case Op::OpCommitReadPipe: return "OpCommitReadPipe"; + case Op::OpCommitWritePipe: return "OpCommitWritePipe"; + case Op::OpIsValidReserveId: return "OpIsValidReserveId"; + case Op::OpGetNumPipePackets: return "OpGetNumPipePackets"; + case Op::OpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case Op::OpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case Op::OpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case Op::OpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case Op::OpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case Op::OpEnqueueMarker: return "OpEnqueueMarker"; + case Op::OpEnqueueKernel: return "OpEnqueueKernel"; + case Op::OpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case Op::OpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case Op::OpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case Op::OpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case Op::OpRetainEvent: return "OpRetainEvent"; + case Op::OpReleaseEvent: return "OpReleaseEvent"; + case Op::OpCreateUserEvent: return "OpCreateUserEvent"; + case Op::OpIsValidEvent: return "OpIsValidEvent"; + case Op::OpSetUserEventStatus: return "OpSetUserEventStatus"; + case Op::OpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case Op::OpGetDefaultQueue: return "OpGetDefaultQueue"; + case Op::OpBuildNDRange: return "OpBuildNDRange"; + case Op::OpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case Op::OpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case Op::OpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case Op::OpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case Op::OpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case Op::OpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case Op::OpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case Op::OpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case Op::OpImageSparseFetch: return "OpImageSparseFetch"; + case Op::OpImageSparseGather: return "OpImageSparseGather"; + case Op::OpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case Op::OpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case Op::OpNoLine: return "OpNoLine"; + case Op::OpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case Op::OpAtomicFlagClear: return "OpAtomicFlagClear"; + case Op::OpImageSparseRead: return "OpImageSparseRead"; + case Op::OpSizeOf: return "OpSizeOf"; + case Op::OpTypePipeStorage: return "OpTypePipeStorage"; + case Op::OpConstantPipeStorage: return "OpConstantPipeStorage"; + case Op::OpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case Op::OpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case Op::OpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case Op::OpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case Op::OpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case Op::OpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case Op::OpModuleProcessed: return "OpModuleProcessed"; + case Op::OpExecutionModeId: return "OpExecutionModeId"; + case Op::OpDecorateId: return "OpDecorateId"; + case Op::OpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case Op::OpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case Op::OpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case Op::OpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case Op::OpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case Op::OpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case Op::OpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case Op::OpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case Op::OpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case Op::OpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case Op::OpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case Op::OpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case Op::OpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case Op::OpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case Op::OpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case Op::OpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case Op::OpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case Op::OpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case Op::OpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case Op::OpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case Op::OpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case Op::OpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case Op::OpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case Op::OpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case Op::OpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case Op::OpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case Op::OpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case Op::OpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case Op::OpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case Op::OpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case Op::OpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case Op::OpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case Op::OpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case Op::OpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case Op::OpCopyLogical: return "OpCopyLogical"; + case Op::OpPtrEqual: return "OpPtrEqual"; + case Op::OpPtrNotEqual: return "OpPtrNotEqual"; + case Op::OpPtrDiff: return "OpPtrDiff"; + case Op::OpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case Op::OpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case Op::OpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case Op::OpTerminateInvocation: return "OpTerminateInvocation"; + case Op::OpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case Op::OpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case Op::OpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case Op::OpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case Op::OpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case Op::OpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case Op::OpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case Op::OpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case Op::OpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case Op::OpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case Op::OpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case Op::OpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case Op::OpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case Op::OpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case Op::OpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case Op::OpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case Op::OpTraceRayKHR: return "OpTraceRayKHR"; + case Op::OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case Op::OpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case Op::OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case Op::OpTerminateRayKHR: return "OpTerminateRayKHR"; + case Op::OpSDot: return "OpSDot"; + case Op::OpUDot: return "OpUDot"; + case Op::OpSUDot: return "OpSUDot"; + case Op::OpSDotAccSat: return "OpSDotAccSat"; + case Op::OpUDotAccSat: return "OpUDotAccSat"; + case Op::OpSUDotAccSat: return "OpSUDotAccSat"; + case Op::OpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case Op::OpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case Op::OpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case Op::OpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case Op::OpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case Op::OpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case Op::OpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case Op::OpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case Op::OpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case Op::OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case Op::OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case Op::OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case Op::OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case Op::OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case Op::OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case Op::OpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case Op::OpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case Op::OpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case Op::OpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case Op::OpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case Op::OpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case Op::OpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case Op::OpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case Op::OpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case Op::OpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case Op::OpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case Op::OpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case Op::OpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case Op::OpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case Op::OpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case Op::OpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case Op::OpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case Op::OpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case Op::OpReadClockKHR: return "OpReadClockKHR"; + case Op::OpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case Op::OpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case Op::OpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case Op::OpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case Op::OpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case Op::OpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case Op::OpConstantStringAMDX: return "OpConstantStringAMDX"; + case Op::OpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case Op::OpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case Op::OpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case Op::OpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case Op::OpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case Op::OpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case Op::OpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case Op::OpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case Op::OpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case Op::OpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case Op::OpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case Op::OpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case Op::OpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case Op::OpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case Op::OpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case Op::OpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case Op::OpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case Op::OpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case Op::OpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case Op::OpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case Op::OpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case Op::OpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case Op::OpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case Op::OpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case Op::OpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case Op::OpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case Op::OpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case Op::OpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case Op::OpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case Op::OpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case Op::OpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case Op::OpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case Op::OpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case Op::OpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case Op::OpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case Op::OpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case Op::OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case Op::OpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case Op::OpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case Op::OpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case Op::OpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case Op::OpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case Op::OpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case Op::OpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case Op::OpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case Op::OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case Op::OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case Op::OpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case Op::OpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case Op::OpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case Op::OpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case Op::OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case Op::OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case Op::OpTerminateRayNV: return "OpTerminateRayNV"; + case Op::OpTraceNV: return "OpTraceNV"; + case Op::OpTraceMotionNV: return "OpTraceMotionNV"; + case Op::OpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case Op::OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case Op::OpExecuteCallableNV: return "OpExecuteCallableNV"; + case Op::OpRayQueryGetClusterIdNV: return "OpRayQueryGetClusterIdNV"; + case Op::OpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case Op::OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case Op::OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case Op::OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case Op::OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case Op::OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case Op::OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case Op::OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case Op::OpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case Op::OpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case Op::OpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case Op::OpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case Op::OpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case Op::OpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case Op::OpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case Op::OpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case Op::OpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case Op::OpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case Op::OpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case Op::OpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case Op::OpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case Op::OpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case Op::OpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case Op::OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case Op::OpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case Op::OpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case Op::OpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case Op::OpConvertUToImageNV: return "OpConvertUToImageNV"; + case Op::OpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case Op::OpConvertImageToUNV: return "OpConvertImageToUNV"; + case Op::OpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case Op::OpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case Op::OpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case Op::OpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case Op::OpRawAccessChainNV: return "OpRawAccessChainNV"; + case Op::OpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case Op::OpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case Op::OpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case Op::OpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case Op::OpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case Op::OpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case Op::OpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case Op::OpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case Op::OpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case Op::OpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case Op::OpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + case Op::OpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case Op::OpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + case Op::OpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case Op::OpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case Op::OpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case Op::OpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case Op::OpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case Op::OpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case Op::OpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case Op::OpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case Op::OpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case Op::OpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case Op::OpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case Op::OpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case Op::OpAbsISubINTEL: return "OpAbsISubINTEL"; + case Op::OpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case Op::OpIAddSatINTEL: return "OpIAddSatINTEL"; + case Op::OpUAddSatINTEL: return "OpUAddSatINTEL"; + case Op::OpIAverageINTEL: return "OpIAverageINTEL"; + case Op::OpUAverageINTEL: return "OpUAverageINTEL"; + case Op::OpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case Op::OpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case Op::OpISubSatINTEL: return "OpISubSatINTEL"; + case Op::OpUSubSatINTEL: return "OpUSubSatINTEL"; + case Op::OpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case Op::OpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case Op::OpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case Op::OpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case Op::OpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case Op::OpAsmINTEL: return "OpAsmINTEL"; + case Op::OpAsmCallINTEL: return "OpAsmCallINTEL"; + case Op::OpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case Op::OpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case Op::OpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case Op::OpExpectKHR: return "OpExpectKHR"; + case Op::OpDecorateString: return "OpDecorateString"; + case Op::OpMemberDecorateString: return "OpMemberDecorateString"; + case Op::OpVmeImageINTEL: return "OpVmeImageINTEL"; + case Op::OpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case Op::OpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case Op::OpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case Op::OpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case Op::OpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case Op::OpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case Op::OpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case Op::OpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case Op::OpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case Op::OpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case Op::OpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case Op::OpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case Op::OpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case Op::OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case Op::OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case Op::OpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case Op::OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case Op::OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case Op::OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case Op::OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case Op::OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case Op::OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case Op::OpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case Op::OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case Op::OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case Op::OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case Op::OpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case Op::OpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case Op::OpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case Op::OpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case Op::OpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case Op::OpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case Op::OpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case Op::OpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case Op::OpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case Op::OpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case Op::OpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case Op::OpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case Op::OpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case Op::OpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case Op::OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case Op::OpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case Op::OpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case Op::OpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case Op::OpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case Op::OpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case Op::OpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case Op::OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case Op::OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case Op::OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case Op::OpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case Op::OpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case Op::OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case Op::OpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case Op::OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case Op::OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case Op::OpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case Op::OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case Op::OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case Op::OpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case Op::OpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case Op::OpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case Op::OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case Op::OpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case Op::OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case Op::OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case Op::OpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case Op::OpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case Op::OpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case Op::OpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case Op::OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case Op::OpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case Op::OpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case Op::OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case Op::OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case Op::OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case Op::OpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case Op::OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case Op::OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case Op::OpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case Op::OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case Op::OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case Op::OpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case Op::OpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case Op::OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case Op::OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case Op::OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case Op::OpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case Op::OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case Op::OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case Op::OpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case Op::OpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case Op::OpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case Op::OpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case Op::OpArbitraryFloatSinCosPiINTEL: return "OpArbitraryFloatSinCosPiINTEL"; + case Op::OpArbitraryFloatCastINTEL: return "OpArbitraryFloatCastINTEL"; + case Op::OpArbitraryFloatCastFromIntINTEL: return "OpArbitraryFloatCastFromIntINTEL"; + case Op::OpArbitraryFloatCastToIntINTEL: return "OpArbitraryFloatCastToIntINTEL"; + case Op::OpArbitraryFloatAddINTEL: return "OpArbitraryFloatAddINTEL"; + case Op::OpArbitraryFloatSubINTEL: return "OpArbitraryFloatSubINTEL"; + case Op::OpArbitraryFloatMulINTEL: return "OpArbitraryFloatMulINTEL"; + case Op::OpArbitraryFloatDivINTEL: return "OpArbitraryFloatDivINTEL"; + case Op::OpArbitraryFloatGTINTEL: return "OpArbitraryFloatGTINTEL"; + case Op::OpArbitraryFloatGEINTEL: return "OpArbitraryFloatGEINTEL"; + case Op::OpArbitraryFloatLTINTEL: return "OpArbitraryFloatLTINTEL"; + case Op::OpArbitraryFloatLEINTEL: return "OpArbitraryFloatLEINTEL"; + case Op::OpArbitraryFloatEQINTEL: return "OpArbitraryFloatEQINTEL"; + case Op::OpArbitraryFloatRecipINTEL: return "OpArbitraryFloatRecipINTEL"; + case Op::OpArbitraryFloatRSqrtINTEL: return "OpArbitraryFloatRSqrtINTEL"; + case Op::OpArbitraryFloatCbrtINTEL: return "OpArbitraryFloatCbrtINTEL"; + case Op::OpArbitraryFloatHypotINTEL: return "OpArbitraryFloatHypotINTEL"; + case Op::OpArbitraryFloatSqrtINTEL: return "OpArbitraryFloatSqrtINTEL"; + case Op::OpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case Op::OpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case Op::OpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case Op::OpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case Op::OpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case Op::OpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case Op::OpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case Op::OpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case Op::OpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case Op::OpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case Op::OpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case Op::OpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case Op::OpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case Op::OpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case Op::OpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case Op::OpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case Op::OpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case Op::OpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case Op::OpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case Op::OpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case Op::OpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case Op::OpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case Op::OpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case Op::OpLoopControlINTEL: return "OpLoopControlINTEL"; + case Op::OpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case Op::OpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case Op::OpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case Op::OpFixedSqrtINTEL: return "OpFixedSqrtINTEL"; + case Op::OpFixedRecipINTEL: return "OpFixedRecipINTEL"; + case Op::OpFixedRsqrtINTEL: return "OpFixedRsqrtINTEL"; + case Op::OpFixedSinINTEL: return "OpFixedSinINTEL"; + case Op::OpFixedCosINTEL: return "OpFixedCosINTEL"; + case Op::OpFixedSinCosINTEL: return "OpFixedSinCosINTEL"; + case Op::OpFixedSinPiINTEL: return "OpFixedSinPiINTEL"; + case Op::OpFixedCosPiINTEL: return "OpFixedCosPiINTEL"; + case Op::OpFixedSinCosPiINTEL: return "OpFixedSinCosPiINTEL"; + case Op::OpFixedLogINTEL: return "OpFixedLogINTEL"; + case Op::OpFixedExpINTEL: return "OpFixedExpINTEL"; + case Op::OpPtrCastToCrossWorkgroupINTEL: return "OpPtrCastToCrossWorkgroupINTEL"; + case Op::OpCrossWorkgroupCastToPtrINTEL: return "OpCrossWorkgroupCastToPtrINTEL"; + case Op::OpReadPipeBlockingINTEL: return "OpReadPipeBlockingINTEL"; + case Op::OpWritePipeBlockingINTEL: return "OpWritePipeBlockingINTEL"; + case Op::OpFPGARegINTEL: return "OpFPGARegINTEL"; + case Op::OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case Op::OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case Op::OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case Op::OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case Op::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case Op::OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case Op::OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case Op::OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case Op::OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case Op::OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case Op::OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case Op::OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case Op::OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case Op::OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case Op::OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case Op::OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case Op::OpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case Op::OpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case Op::OpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case Op::OpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case Op::OpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case Op::OpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case Op::OpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case Op::OpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case Op::OpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case Op::OpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case Op::OpTaskSequenceCreateINTEL: return "OpTaskSequenceCreateINTEL"; + case Op::OpTaskSequenceAsyncINTEL: return "OpTaskSequenceAsyncINTEL"; + case Op::OpTaskSequenceGetINTEL: return "OpTaskSequenceGetINTEL"; + case Op::OpTaskSequenceReleaseINTEL: return "OpTaskSequenceReleaseINTEL"; + case Op::OpTypeTaskSequenceINTEL: return "OpTypeTaskSequenceINTEL"; + case Op::OpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case Op::OpSubgroup2DBlockLoadINTEL: return "OpSubgroup2DBlockLoadINTEL"; + case Op::OpSubgroup2DBlockLoadTransformINTEL: return "OpSubgroup2DBlockLoadTransformINTEL"; + case Op::OpSubgroup2DBlockLoadTransposeINTEL: return "OpSubgroup2DBlockLoadTransposeINTEL"; + case Op::OpSubgroup2DBlockPrefetchINTEL: return "OpSubgroup2DBlockPrefetchINTEL"; + case Op::OpSubgroup2DBlockStoreINTEL: return "OpSubgroup2DBlockStoreINTEL"; + case Op::OpSubgroupMatrixMultiplyAccumulateINTEL: return "OpSubgroupMatrixMultiplyAccumulateINTEL"; + case Op::OpBitwiseFunctionINTEL: return "OpBitwiseFunctionINTEL"; + case Op::OpGroupIMulKHR: return "OpGroupIMulKHR"; + case Op::OpGroupFMulKHR: return "OpGroupFMulKHR"; + case Op::OpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case Op::OpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case Op::OpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case Op::OpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case Op::OpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case Op::OpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case Op::OpRoundFToTF32INTEL: return "OpRoundFToTF32INTEL"; + case Op::OpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case Op::OpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload bitwise operators for mask bit combining + +constexpr ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +constexpr ImageOperandsMask operator&(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) & unsigned(b)); } +constexpr ImageOperandsMask operator^(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) ^ unsigned(b)); } +constexpr ImageOperandsMask operator~(ImageOperandsMask a) { return ImageOperandsMask(~unsigned(a)); } +constexpr FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +constexpr FPFastMathModeMask operator&(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) & unsigned(b)); } +constexpr FPFastMathModeMask operator^(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) ^ unsigned(b)); } +constexpr FPFastMathModeMask operator~(FPFastMathModeMask a) { return FPFastMathModeMask(~unsigned(a)); } +constexpr SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +constexpr SelectionControlMask operator&(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) & unsigned(b)); } +constexpr SelectionControlMask operator^(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) ^ unsigned(b)); } +constexpr SelectionControlMask operator~(SelectionControlMask a) { return SelectionControlMask(~unsigned(a)); } +constexpr LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +constexpr LoopControlMask operator&(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) & unsigned(b)); } +constexpr LoopControlMask operator^(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) ^ unsigned(b)); } +constexpr LoopControlMask operator~(LoopControlMask a) { return LoopControlMask(~unsigned(a)); } +constexpr FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +constexpr FunctionControlMask operator&(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) & unsigned(b)); } +constexpr FunctionControlMask operator^(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) ^ unsigned(b)); } +constexpr FunctionControlMask operator~(FunctionControlMask a) { return FunctionControlMask(~unsigned(a)); } +constexpr MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +constexpr MemorySemanticsMask operator&(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) & unsigned(b)); } +constexpr MemorySemanticsMask operator^(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) ^ unsigned(b)); } +constexpr MemorySemanticsMask operator~(MemorySemanticsMask a) { return MemorySemanticsMask(~unsigned(a)); } +constexpr MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +constexpr MemoryAccessMask operator&(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) & unsigned(b)); } +constexpr MemoryAccessMask operator^(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) ^ unsigned(b)); } +constexpr MemoryAccessMask operator~(MemoryAccessMask a) { return MemoryAccessMask(~unsigned(a)); } +constexpr KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +constexpr KernelProfilingInfoMask operator&(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) & unsigned(b)); } +constexpr KernelProfilingInfoMask operator^(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) ^ unsigned(b)); } +constexpr KernelProfilingInfoMask operator~(KernelProfilingInfoMask a) { return KernelProfilingInfoMask(~unsigned(a)); } +constexpr RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +constexpr RayFlagsMask operator&(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) & unsigned(b)); } +constexpr RayFlagsMask operator^(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) ^ unsigned(b)); } +constexpr RayFlagsMask operator~(RayFlagsMask a) { return RayFlagsMask(~unsigned(a)); } +constexpr FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } +constexpr FragmentShadingRateMask operator&(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) & unsigned(b)); } +constexpr FragmentShadingRateMask operator^(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) ^ unsigned(b)); } +constexpr FragmentShadingRateMask operator~(FragmentShadingRateMask a) { return FragmentShadingRateMask(~unsigned(a)); } +constexpr CooperativeMatrixOperandsMask operator|(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) | unsigned(b)); } +constexpr CooperativeMatrixOperandsMask operator&(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) & unsigned(b)); } +constexpr CooperativeMatrixOperandsMask operator^(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) ^ unsigned(b)); } +constexpr CooperativeMatrixOperandsMask operator~(CooperativeMatrixOperandsMask a) { return CooperativeMatrixOperandsMask(~unsigned(a)); } +constexpr CooperativeMatrixReduceMask operator|(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) | unsigned(b)); } +constexpr CooperativeMatrixReduceMask operator&(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) & unsigned(b)); } +constexpr CooperativeMatrixReduceMask operator^(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) ^ unsigned(b)); } +constexpr CooperativeMatrixReduceMask operator~(CooperativeMatrixReduceMask a) { return CooperativeMatrixReduceMask(~unsigned(a)); } +constexpr TensorAddressingOperandsMask operator|(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) | unsigned(b)); } +constexpr TensorAddressingOperandsMask operator&(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) & unsigned(b)); } +constexpr TensorAddressingOperandsMask operator^(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) ^ unsigned(b)); } +constexpr TensorAddressingOperandsMask operator~(TensorAddressingOperandsMask a) { return TensorAddressingOperandsMask(~unsigned(a)); } +constexpr MatrixMultiplyAccumulateOperandsMask operator|(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) | unsigned(b)); } +constexpr MatrixMultiplyAccumulateOperandsMask operator&(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) & unsigned(b)); } +constexpr MatrixMultiplyAccumulateOperandsMask operator^(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) ^ unsigned(b)); } +constexpr MatrixMultiplyAccumulateOperandsMask operator~(MatrixMultiplyAccumulateOperandsMask a) { return MatrixMultiplyAccumulateOperandsMask(~unsigned(a)); } +constexpr RawAccessChainOperandsMask operator|(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) | unsigned(b)); } +constexpr RawAccessChainOperandsMask operator&(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) & unsigned(b)); } +constexpr RawAccessChainOperandsMask operator^(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) ^ unsigned(b)); } +constexpr RawAccessChainOperandsMask operator~(RawAccessChainOperandsMask a) { return RawAccessChainOperandsMask(~unsigned(a)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json new file mode 100644 index 00000000..7c4f9db0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json @@ -0,0 +1,2383 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2024 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python, C#, D, Beef", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,", + " e.g.: Spv.Specification.SourceLanguage.GLSL", + "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL", + "- Beef will use enum classes in the Specification class located in the \"Spv\" namespace,", + " e.g.: Spv.Specification.SourceLanguage.GLSL", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 67072, + "Revision": 1, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5, + "CPP_for_OpenCL": 6, + "SYCL": 7, + "HERO_C": 8, + "NZSL": 9, + "WGSL": 10, + "Slang": 11, + "Zig": 12, + "Rust": 13 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6, + "TaskNV": 5267, + "MeshNV": 5268, + "RayGenerationKHR": 5313, + "RayGenerationNV": 5313, + "IntersectionKHR": 5314, + "IntersectionNV": 5314, + "AnyHitKHR": 5315, + "AnyHitNV": 5315, + "ClosestHitKHR": 5316, + "ClosestHitNV": 5316, + "MissKHR": 5317, + "MissNV": 5317, + "CallableKHR": 5318, + "CallableNV": 5318, + "TaskEXT": 5364, + "MeshEXT": 5365 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2, + "PhysicalStorageBuffer64": 5348, + "PhysicalStorageBuffer64EXT": 5348 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2, + "Vulkan": 3, + "VulkanKHR": 3 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "SubgroupsPerWorkgroupId": 37, + "LocalSizeId": 38, + "LocalSizeHintId": 39, + "NonCoherentColorAttachmentReadEXT": 4169, + "NonCoherentDepthAttachmentReadEXT": 4170, + "NonCoherentStencilAttachmentReadEXT": 4171, + "SubgroupUniformControlFlowKHR": 4421, + "PostDepthCoverage": 4446, + "DenormPreserve": 4459, + "DenormFlushToZero": 4460, + "SignedZeroInfNanPreserve": 4461, + "RoundingModeRTE": 4462, + "RoundingModeRTZ": 4463, + "NonCoherentTileAttachmentReadQCOM": 4489, + "TileShadingRateQCOM": 4490, + "EarlyAndLateFragmentTestsAMD": 5017, + "StencilRefReplacingEXT": 5027, + "CoalescingAMDX": 5069, + "IsApiEntryAMDX": 5070, + "MaxNodeRecursionAMDX": 5071, + "StaticNumWorkgroupsAMDX": 5072, + "ShaderIndexAMDX": 5073, + "MaxNumWorkgroupsAMDX": 5077, + "StencilRefUnchangedFrontAMD": 5079, + "StencilRefGreaterFrontAMD": 5080, + "StencilRefLessFrontAMD": 5081, + "StencilRefUnchangedBackAMD": 5082, + "StencilRefGreaterBackAMD": 5083, + "StencilRefLessBackAMD": 5084, + "QuadDerivativesKHR": 5088, + "RequireFullQuadsKHR": 5089, + "SharesInputWithAMDX": 5102, + "OutputLinesEXT": 5269, + "OutputLinesNV": 5269, + "OutputPrimitivesEXT": 5270, + "OutputPrimitivesNV": 5270, + "DerivativeGroupQuadsKHR": 5289, + "DerivativeGroupQuadsNV": 5289, + "DerivativeGroupLinearKHR": 5290, + "DerivativeGroupLinearNV": 5290, + "OutputTrianglesEXT": 5298, + "OutputTrianglesNV": 5298, + "PixelInterlockOrderedEXT": 5366, + "PixelInterlockUnorderedEXT": 5367, + "SampleInterlockOrderedEXT": 5368, + "SampleInterlockUnorderedEXT": 5369, + "ShadingRateInterlockOrderedEXT": 5370, + "ShadingRateInterlockUnorderedEXT": 5371, + "SharedLocalMemorySizeINTEL": 5618, + "RoundingModeRTPINTEL": 5620, + "RoundingModeRTNINTEL": 5621, + "FloatingPointModeALTINTEL": 5622, + "FloatingPointModeIEEEINTEL": 5623, + "MaxWorkgroupSizeINTEL": 5893, + "MaxWorkDimINTEL": 5894, + "NoGlobalOffsetINTEL": 5895, + "NumSIMDWorkitemsINTEL": 5896, + "SchedulerTargetFmaxMhzINTEL": 5903, + "MaximallyReconvergesKHR": 6023, + "FPFastMathDefault": 6028, + "StreamingInterfaceINTEL": 6154, + "RegisterMapInterfaceINTEL": 6160, + "NamedBarrierCountINTEL": 6417, + "MaximumRegistersINTEL": 6461, + "MaximumRegistersIdINTEL": 6462, + "NamedMaximumRegistersINTEL": 6463 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12, + "TileImageEXT": 4172, + "TileAttachmentQCOM": 4491, + "NodePayloadAMDX": 5068, + "CallableDataKHR": 5328, + "CallableDataNV": 5328, + "IncomingCallableDataKHR": 5329, + "IncomingCallableDataNV": 5329, + "RayPayloadKHR": 5338, + "RayPayloadNV": 5338, + "HitAttributeKHR": 5339, + "HitAttributeNV": 5339, + "IncomingRayPayloadKHR": 5342, + "IncomingRayPayloadNV": 5342, + "ShaderRecordBufferKHR": 5343, + "ShaderRecordBufferNV": 5343, + "PhysicalStorageBuffer": 5349, + "PhysicalStorageBufferEXT": 5349, + "HitObjectAttributeNV": 5385, + "TaskPayloadWorkgroupEXT": 5402, + "CodeSectionINTEL": 5605, + "DeviceOnlyINTEL": 5936, + "HostOnlyINTEL": 5937 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6, + "TileImageDataEXT": 4173 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39, + "R64ui": 40, + "R64i": 41 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16, + "UnormInt10X6EXT": 17, + "UnsignedIntRaw10EXT": 19, + "UnsignedIntRaw12EXT": 20, + "UnormInt2_101010EXT": 21, + "UnsignedInt10X6EXT": 22, + "UnsignedInt12X4EXT": 23, + "UnsignedInt14X2EXT": 24, + "UnormInt12X4EXT": 25, + "UnormInt14X2EXT": 26 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7, + "MakeTexelAvailable": 8, + "MakeTexelAvailableKHR": 8, + "MakeTexelVisible": 9, + "MakeTexelVisibleKHR": 9, + "NonPrivateTexel": 10, + "NonPrivateTexelKHR": 10, + "VolatileTexel": 11, + "VolatileTexelKHR": 11, + "SignExtend": 12, + "ZeroExtend": 13, + "Nontemporal": 14, + "Offsets": 16 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4, + "AllowContract": 16, + "AllowContractFastINTEL": 16, + "AllowReassoc": 17, + "AllowReassocINTEL": 17, + "AllowTransform": 18 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1, + "LinkOnceODR": 2 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7, + "RuntimeAlignedINTEL": 5940 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "UniformId": 27, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "AlignmentId": 46, + "MaxByteOffsetId": 47, + "NoSignedWrap": 4469, + "NoUnsignedWrap": 4470, + "WeightTextureQCOM": 4487, + "BlockMatchTextureQCOM": 4488, + "BlockMatchSamplerQCOM": 4499, + "ExplicitInterpAMD": 4999, + "NodeSharesPayloadLimitsWithAMDX": 5019, + "NodeMaxPayloadsAMDX": 5020, + "TrackFinishWritingAMDX": 5078, + "PayloadNodeNameAMDX": 5091, + "PayloadNodeBaseIndexAMDX": 5098, + "PayloadNodeSparseArrayAMDX": 5099, + "PayloadNodeArraySizeAMDX": 5100, + "PayloadDispatchIndirectAMDX": 5105, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "PerPrimitiveEXT": 5271, + "PerPrimitiveNV": 5271, + "PerViewNV": 5272, + "PerTaskNV": 5273, + "PerVertexKHR": 5285, + "PerVertexNV": 5285, + "NonUniform": 5300, + "NonUniformEXT": 5300, + "RestrictPointer": 5355, + "RestrictPointerEXT": 5355, + "AliasedPointer": 5356, + "AliasedPointerEXT": 5356, + "HitObjectShaderRecordBufferNV": 5386, + "BindlessSamplerNV": 5398, + "BindlessImageNV": 5399, + "BoundSamplerNV": 5400, + "BoundImageNV": 5401, + "SIMTCallINTEL": 5599, + "ReferencedIndirectlyINTEL": 5602, + "ClobberINTEL": 5607, + "SideEffectsINTEL": 5608, + "VectorComputeVariableINTEL": 5624, + "FuncParamIOKindINTEL": 5625, + "VectorComputeFunctionINTEL": 5626, + "StackCallINTEL": 5627, + "GlobalVariableOffsetINTEL": 5628, + "CounterBuffer": 5634, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635, + "UserSemantic": 5635, + "UserTypeGOOGLE": 5636, + "FunctionRoundingModeINTEL": 5822, + "FunctionDenormModeINTEL": 5823, + "RegisterINTEL": 5825, + "MemoryINTEL": 5826, + "NumbanksINTEL": 5827, + "BankwidthINTEL": 5828, + "MaxPrivateCopiesINTEL": 5829, + "SinglepumpINTEL": 5830, + "DoublepumpINTEL": 5831, + "MaxReplicatesINTEL": 5832, + "SimpleDualPortINTEL": 5833, + "MergeINTEL": 5834, + "BankBitsINTEL": 5835, + "ForcePow2DepthINTEL": 5836, + "StridesizeINTEL": 5883, + "WordsizeINTEL": 5884, + "TrueDualPortINTEL": 5885, + "BurstCoalesceINTEL": 5899, + "CacheSizeINTEL": 5900, + "DontStaticallyCoalesceINTEL": 5901, + "PrefetchINTEL": 5902, + "StallEnableINTEL": 5905, + "FuseLoopsInFunctionINTEL": 5907, + "MathOpDSPModeINTEL": 5909, + "AliasScopeINTEL": 5914, + "NoAliasINTEL": 5915, + "InitiationIntervalINTEL": 5917, + "MaxConcurrencyINTEL": 5918, + "PipelineEnableINTEL": 5919, + "BufferLocationINTEL": 5921, + "IOPipeStorageINTEL": 5944, + "FunctionFloatingPointModeINTEL": 6080, + "SingleElementVectorINTEL": 6085, + "VectorComputeCallableFunctionINTEL": 6087, + "MediaBlockIOINTEL": 6140, + "StallFreeINTEL": 6151, + "FPMaxErrorDecorationINTEL": 6170, + "LatencyControlLabelINTEL": 6172, + "LatencyControlConstraintINTEL": 6173, + "ConduitKernelArgumentINTEL": 6175, + "RegisterMapKernelArgumentINTEL": 6176, + "MMHostInterfaceAddressWidthINTEL": 6177, + "MMHostInterfaceDataWidthINTEL": 6178, + "MMHostInterfaceLatencyINTEL": 6179, + "MMHostInterfaceReadWriteModeINTEL": 6180, + "MMHostInterfaceMaxBurstINTEL": 6181, + "MMHostInterfaceWaitRequestINTEL": 6182, + "StableKernelArgumentINTEL": 6183, + "HostAccessINTEL": 6188, + "InitModeINTEL": 6190, + "ImplementInRegisterMapINTEL": 6191, + "CacheControlLoadINTEL": 6442, + "CacheControlStoreINTEL": 6443 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "CoreIDARM": 4160, + "CoreCountARM": 4161, + "CoreMaxIDARM": 4162, + "WarpIDARM": 4163, + "WarpMaxIDARM": 4164, + "SubgroupEqMask": 4416, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMask": 4417, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMask": 4418, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMask": 4419, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMask": 4420, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "PrimitiveShadingRateKHR": 4432, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "ShadingRateKHR": 4444, + "TileOffsetQCOM": 4492, + "TileDimensionQCOM": 4493, + "TileApronSizeQCOM": 4494, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "RemainingRecursionLevelsAMDX": 5021, + "ShaderIndexAMDX": 5073, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262, + "FullyCoveredEXT": 5264, + "TaskCountNV": 5274, + "PrimitiveCountNV": 5275, + "PrimitiveIndicesNV": 5276, + "ClipDistancePerViewNV": 5277, + "CullDistancePerViewNV": 5278, + "LayerPerViewNV": 5279, + "MeshViewCountNV": 5280, + "MeshViewIndicesNV": 5281, + "BaryCoordKHR": 5286, + "BaryCoordNV": 5286, + "BaryCoordNoPerspKHR": 5287, + "BaryCoordNoPerspNV": 5287, + "FragSizeEXT": 5292, + "FragmentSizeNV": 5292, + "FragInvocationCountEXT": 5293, + "InvocationsPerPixelNV": 5293, + "PrimitivePointIndicesEXT": 5294, + "PrimitiveLineIndicesEXT": 5295, + "PrimitiveTriangleIndicesEXT": 5296, + "CullPrimitiveEXT": 5299, + "LaunchIdKHR": 5319, + "LaunchIdNV": 5319, + "LaunchSizeKHR": 5320, + "LaunchSizeNV": 5320, + "WorldRayOriginKHR": 5321, + "WorldRayOriginNV": 5321, + "WorldRayDirectionKHR": 5322, + "WorldRayDirectionNV": 5322, + "ObjectRayOriginKHR": 5323, + "ObjectRayOriginNV": 5323, + "ObjectRayDirectionKHR": 5324, + "ObjectRayDirectionNV": 5324, + "RayTminKHR": 5325, + "RayTminNV": 5325, + "RayTmaxKHR": 5326, + "RayTmaxNV": 5326, + "InstanceCustomIndexKHR": 5327, + "InstanceCustomIndexNV": 5327, + "ObjectToWorldKHR": 5330, + "ObjectToWorldNV": 5330, + "WorldToObjectKHR": 5331, + "WorldToObjectNV": 5331, + "HitTNV": 5332, + "HitKindKHR": 5333, + "HitKindNV": 5333, + "CurrentRayTimeNV": 5334, + "HitTriangleVertexPositionsKHR": 5335, + "HitMicroTriangleVertexPositionsNV": 5337, + "HitMicroTriangleVertexBarycentricsNV": 5344, + "IncomingRayFlagsKHR": 5351, + "IncomingRayFlagsNV": 5351, + "RayGeometryIndexKHR": 5352, + "HitIsSphereNV": 5359, + "HitIsLSSNV": 5360, + "HitSpherePositionNV": 5361, + "WarpsPerSMNV": 5374, + "SMCountNV": 5375, + "WarpIDNV": 5376, + "SMIDNV": 5377, + "HitLSSPositionsNV": 5396, + "HitKindFrontFacingMicroTriangleNV": 5405, + "HitKindBackFacingMicroTriangleNV": 5406, + "HitSphereRadiusNV": 5420, + "HitLSSRadiiNV": 5421, + "ClusterIDNV": 5436, + "CullMaskKHR": 6021 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3, + "MinIterations": 4, + "MaxIterations": 5, + "IterationMultiple": 6, + "PeelCount": 7, + "PartialCount": 8, + "InitiationIntervalINTEL": 16, + "MaxConcurrencyINTEL": 17, + "DependencyArrayINTEL": 18, + "PipelineEnableINTEL": 19, + "LoopCoalesceINTEL": 20, + "MaxInterleavingINTEL": 21, + "SpeculatedIterationsINTEL": 22, + "NoFusionINTEL": 23, + "LoopCountINTEL": 24, + "MaxReinvocationDelayINTEL": 25 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3, + "OptNoneEXT": 16, + "OptNoneINTEL": 16 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11, + "OutputMemory": 12, + "OutputMemoryKHR": 12, + "MakeAvailable": 13, + "MakeAvailableKHR": 13, + "MakeVisible": 14, + "MakeVisibleKHR": 14, + "Volatile": 15 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2, + "MakePointerAvailable": 3, + "MakePointerAvailableKHR": 3, + "MakePointerVisible": 4, + "MakePointerVisibleKHR": 4, + "NonPrivatePointer": 5, + "NonPrivatePointerKHR": 5, + "AliasScopeINTELMask": 16, + "NoAliasINTELMask": 17 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4, + "QueueFamily": 5, + "QueueFamilyKHR": 5, + "ShaderCallKHR": 6 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2, + "ClusteredReduce": 3, + "PartitionedReduceNV": 6, + "PartitionedInclusiveScanNV": 7, + "PartitionedExclusiveScanNV": 8 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "GroupNonUniform": 61, + "GroupNonUniformVote": 62, + "GroupNonUniformArithmetic": 63, + "GroupNonUniformBallot": 64, + "GroupNonUniformShuffle": 65, + "GroupNonUniformShuffleRelative": 66, + "GroupNonUniformClustered": 67, + "GroupNonUniformQuad": 68, + "ShaderLayer": 69, + "ShaderViewportIndex": 70, + "UniformDecoration": 71, + "CoreBuiltinsARM": 4165, + "TileImageColorReadAccessEXT": 4166, + "TileImageDepthReadAccessEXT": 4167, + "TileImageStencilReadAccessEXT": 4168, + "CooperativeMatrixLayoutsARM": 4201, + "FragmentShadingRateKHR": 4422, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "WorkgroupMemoryExplicitLayoutKHR": 4428, + "WorkgroupMemoryExplicitLayout8BitAccessKHR": 4429, + "WorkgroupMemoryExplicitLayout16BitAccessKHR": 4430, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "StorageBuffer8BitAccess": 4448, + "UniformAndStorageBuffer8BitAccess": 4449, + "StoragePushConstant8": 4450, + "DenormPreserve": 4464, + "DenormFlushToZero": 4465, + "SignedZeroInfNanPreserve": 4466, + "RoundingModeRTE": 4467, + "RoundingModeRTZ": 4468, + "RayQueryProvisionalKHR": 4471, + "RayQueryKHR": 4472, + "UntypedPointersKHR": 4473, + "RayTraversalPrimitiveCullingKHR": 4478, + "RayTracingKHR": 4479, + "TextureSampleWeightedQCOM": 4484, + "TextureBoxFilterQCOM": 4485, + "TextureBlockMatchQCOM": 4486, + "TileShadingQCOM": 4495, + "TextureBlockMatch2QCOM": 4498, + "Float16ImageAMD": 5008, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "Int64ImageEXT": 5016, + "ShaderClockKHR": 5055, + "ShaderEnqueueAMDX": 5067, + "QuadControlKHR": 5087, + "BFloat16TypeKHR": 5116, + "BFloat16DotProductKHR": 5117, + "BFloat16CooperativeMatrixKHR": 5118, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "FragmentFullyCoveredEXT": 5265, + "MeshShadingNV": 5266, + "ImageFootprintNV": 5282, + "MeshShadingEXT": 5283, + "FragmentBarycentricKHR": 5284, + "FragmentBarycentricNV": 5284, + "ComputeDerivativeGroupQuadsKHR": 5288, + "ComputeDerivativeGroupQuadsNV": 5288, + "FragmentDensityEXT": 5291, + "ShadingRateNV": 5291, + "GroupNonUniformPartitionedNV": 5297, + "ShaderNonUniform": 5301, + "ShaderNonUniformEXT": 5301, + "RuntimeDescriptorArray": 5302, + "RuntimeDescriptorArrayEXT": 5302, + "InputAttachmentArrayDynamicIndexing": 5303, + "InputAttachmentArrayDynamicIndexingEXT": 5303, + "UniformTexelBufferArrayDynamicIndexing": 5304, + "UniformTexelBufferArrayDynamicIndexingEXT": 5304, + "StorageTexelBufferArrayDynamicIndexing": 5305, + "StorageTexelBufferArrayDynamicIndexingEXT": 5305, + "UniformBufferArrayNonUniformIndexing": 5306, + "UniformBufferArrayNonUniformIndexingEXT": 5306, + "SampledImageArrayNonUniformIndexing": 5307, + "SampledImageArrayNonUniformIndexingEXT": 5307, + "StorageBufferArrayNonUniformIndexing": 5308, + "StorageBufferArrayNonUniformIndexingEXT": 5308, + "StorageImageArrayNonUniformIndexing": 5309, + "StorageImageArrayNonUniformIndexingEXT": 5309, + "InputAttachmentArrayNonUniformIndexing": 5310, + "InputAttachmentArrayNonUniformIndexingEXT": 5310, + "UniformTexelBufferArrayNonUniformIndexing": 5311, + "UniformTexelBufferArrayNonUniformIndexingEXT": 5311, + "StorageTexelBufferArrayNonUniformIndexing": 5312, + "StorageTexelBufferArrayNonUniformIndexingEXT": 5312, + "RayTracingPositionFetchKHR": 5336, + "RayTracingNV": 5340, + "RayTracingMotionBlurNV": 5341, + "VulkanMemoryModel": 5345, + "VulkanMemoryModelKHR": 5345, + "VulkanMemoryModelDeviceScope": 5346, + "VulkanMemoryModelDeviceScopeKHR": 5346, + "PhysicalStorageBufferAddresses": 5347, + "PhysicalStorageBufferAddressesEXT": 5347, + "ComputeDerivativeGroupLinearKHR": 5350, + "ComputeDerivativeGroupLinearNV": 5350, + "RayTracingProvisionalKHR": 5353, + "CooperativeMatrixNV": 5357, + "FragmentShaderSampleInterlockEXT": 5363, + "FragmentShaderShadingRateInterlockEXT": 5372, + "ShaderSMBuiltinsNV": 5373, + "FragmentShaderPixelInterlockEXT": 5378, + "DemoteToHelperInvocation": 5379, + "DemoteToHelperInvocationEXT": 5379, + "DisplacementMicromapNV": 5380, + "RayTracingOpacityMicromapEXT": 5381, + "ShaderInvocationReorderNV": 5383, + "BindlessTextureNV": 5390, + "RayQueryPositionFetchKHR": 5391, + "CooperativeVectorNV": 5394, + "AtomicFloat16VectorNV": 5404, + "RayTracingDisplacementMicromapNV": 5409, + "RawAccessChainsNV": 5414, + "RayTracingSpheresGeometryNV": 5418, + "RayTracingLinearSweptSpheresGeometryNV": 5419, + "CooperativeMatrixReductionsNV": 5430, + "CooperativeMatrixConversionsNV": 5431, + "CooperativeMatrixPerElementOperationsNV": 5432, + "CooperativeMatrixTensorAddressingNV": 5433, + "CooperativeMatrixBlockLoadsNV": 5434, + "CooperativeVectorTrainingNV": 5435, + "RayTracingClusterAccelerationStructureNV": 5437, + "TensorAddressingNV": 5439, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570, + "SubgroupImageMediaBlockIOINTEL": 5579, + "RoundToInfinityINTEL": 5582, + "FloatingPointModeINTEL": 5583, + "IntegerFunctions2INTEL": 5584, + "FunctionPointersINTEL": 5603, + "IndirectReferencesINTEL": 5604, + "AsmINTEL": 5606, + "AtomicFloat32MinMaxEXT": 5612, + "AtomicFloat64MinMaxEXT": 5613, + "AtomicFloat16MinMaxEXT": 5616, + "VectorComputeINTEL": 5617, + "VectorAnyINTEL": 5619, + "ExpectAssumeKHR": 5629, + "SubgroupAvcMotionEstimationINTEL": 5696, + "SubgroupAvcMotionEstimationIntraINTEL": 5697, + "SubgroupAvcMotionEstimationChromaINTEL": 5698, + "VariableLengthArrayINTEL": 5817, + "FunctionFloatControlINTEL": 5821, + "FPGAMemoryAttributesINTEL": 5824, + "FPFastMathModeINTEL": 5837, + "ArbitraryPrecisionIntegersINTEL": 5844, + "ArbitraryPrecisionFloatingPointINTEL": 5845, + "UnstructuredLoopControlsINTEL": 5886, + "FPGALoopControlsINTEL": 5888, + "KernelAttributesINTEL": 5892, + "FPGAKernelAttributesINTEL": 5897, + "FPGAMemoryAccessesINTEL": 5898, + "FPGAClusterAttributesINTEL": 5904, + "LoopFuseINTEL": 5906, + "FPGADSPControlINTEL": 5908, + "MemoryAccessAliasingINTEL": 5910, + "FPGAInvocationPipeliningAttributesINTEL": 5916, + "FPGABufferLocationINTEL": 5920, + "ArbitraryPrecisionFixedPointINTEL": 5922, + "USMStorageClassesINTEL": 5935, + "RuntimeAlignedAttributeINTEL": 5939, + "IOPipesINTEL": 5943, + "BlockingPipesINTEL": 5945, + "FPGARegINTEL": 5948, + "DotProductInputAll": 6016, + "DotProductInputAllKHR": 6016, + "DotProductInput4x8Bit": 6017, + "DotProductInput4x8BitKHR": 6017, + "DotProductInput4x8BitPacked": 6018, + "DotProductInput4x8BitPackedKHR": 6018, + "DotProduct": 6019, + "DotProductKHR": 6019, + "RayCullMaskKHR": 6020, + "CooperativeMatrixKHR": 6022, + "ReplicatedCompositesEXT": 6024, + "BitInstructions": 6025, + "GroupNonUniformRotateKHR": 6026, + "FloatControls2": 6029, + "AtomicFloat32AddEXT": 6033, + "AtomicFloat64AddEXT": 6034, + "LongCompositesINTEL": 6089, + "OptNoneEXT": 6094, + "OptNoneINTEL": 6094, + "AtomicFloat16AddEXT": 6095, + "DebugInfoModuleINTEL": 6114, + "BFloat16ConversionINTEL": 6115, + "SplitBarrierINTEL": 6141, + "ArithmeticFenceEXT": 6144, + "FPGAClusterAttributesV2INTEL": 6150, + "FPGAKernelAttributesv2INTEL": 6161, + "TaskSequenceINTEL": 6162, + "FPMaxErrorINTEL": 6169, + "FPGALatencyControlINTEL": 6171, + "FPGAArgumentInterfacesINTEL": 6174, + "GlobalVariableHostAccessINTEL": 6187, + "GlobalVariableFPGADecorationsINTEL": 6189, + "SubgroupBufferPrefetchINTEL": 6220, + "Subgroup2DBlockIOINTEL": 6228, + "Subgroup2DBlockTransformINTEL": 6229, + "Subgroup2DBlockTransposeINTEL": 6230, + "SubgroupMatrixMultiplyAccumulateINTEL": 6236, + "TernaryBitwiseFunctionINTEL": 6241, + "GroupUniformArithmeticKHR": 6400, + "TensorFloat32RoundingINTEL": 6425, + "MaskedGatherScatterINTEL": 6427, + "CacheControlsINTEL": 6441, + "RegisterLimitsINTEL": 6460 + } + }, + { + "Name": "RayFlags", + "Type": "Bit", + "Values": + { + "OpaqueKHR": 0, + "NoOpaqueKHR": 1, + "TerminateOnFirstHitKHR": 2, + "SkipClosestHitShaderKHR": 3, + "CullBackFacingTrianglesKHR": 4, + "CullFrontFacingTrianglesKHR": 5, + "CullOpaqueKHR": 6, + "CullNoOpaqueKHR": 7, + "SkipBuiltinPrimitivesNV": 8, + "SkipTrianglesKHR": 8, + "SkipAABBsKHR": 9, + "ForceOpacityMicromap2StateEXT": 10 + } + }, + { + "Name": "RayQueryIntersection", + "Type": "Value", + "Values": + { + "RayQueryCandidateIntersectionKHR": 0, + "RayQueryCommittedIntersectionKHR": 1 + } + }, + { + "Name": "RayQueryCommittedIntersectionType", + "Type": "Value", + "Values": + { + "RayQueryCommittedIntersectionNoneKHR": 0, + "RayQueryCommittedIntersectionTriangleKHR": 1, + "RayQueryCommittedIntersectionGeneratedKHR": 2 + } + }, + { + "Name": "RayQueryCandidateIntersectionType", + "Type": "Value", + "Values": + { + "RayQueryCandidateIntersectionTriangleKHR": 0, + "RayQueryCandidateIntersectionAABBKHR": 1 + } + }, + { + "Name": "FragmentShadingRate", + "Type": "Bit", + "Values": + { + "Vertical2Pixels": 0, + "Vertical4Pixels": 1, + "Horizontal2Pixels": 2, + "Horizontal4Pixels": 3 + } + }, + { + "Name": "FPDenormMode", + "Type": "Value", + "Values": + { + "Preserve": 0, + "FlushToZero": 1 + } + }, + { + "Name": "FPOperationMode", + "Type": "Value", + "Values": + { + "IEEE": 0, + "ALT": 1 + } + }, + { + "Name": "QuantizationModes", + "Type": "Value", + "Values": + { + "TRN": 0, + "TRN_ZERO": 1, + "RND": 2, + "RND_ZERO": 3, + "RND_INF": 4, + "RND_MIN_INF": 5, + "RND_CONV": 6, + "RND_CONV_ODD": 7 + } + }, + { + "Name": "OverflowModes", + "Type": "Value", + "Values": + { + "WRAP": 0, + "SAT": 1, + "SAT_ZERO": 2, + "SAT_SYM": 3 + } + }, + { + "Name": "PackedVectorFormat", + "Type": "Value", + "Values": + { + "PackedVectorFormat4x8Bit": 0, + "PackedVectorFormat4x8BitKHR": 0 + } + }, + { + "Name": "CooperativeMatrixOperands", + "Type": "Bit", + "Values": + { + "MatrixASignedComponentsKHR": 0, + "MatrixBSignedComponentsKHR": 1, + "MatrixCSignedComponentsKHR": 2, + "MatrixResultSignedComponentsKHR": 3, + "SaturatingAccumulationKHR": 4 + } + }, + { + "Name": "CooperativeMatrixLayout", + "Type": "Value", + "Values": + { + "RowMajorKHR": 0, + "ColumnMajorKHR": 1, + "RowBlockedInterleavedARM": 4202, + "ColumnBlockedInterleavedARM": 4203 + } + }, + { + "Name": "CooperativeMatrixUse", + "Type": "Value", + "Values": + { + "MatrixAKHR": 0, + "MatrixBKHR": 1, + "MatrixAccumulatorKHR": 2 + } + }, + { + "Name": "CooperativeMatrixReduce", + "Type": "Bit", + "Values": + { + "Row": 0, + "Column": 1, + "CooperativeMatrixReduce2x2": 2 + } + }, + { + "Name": "TensorClampMode", + "Type": "Value", + "Values": + { + "Undefined": 0, + "Constant": 1, + "ClampToEdge": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "TensorAddressingOperands", + "Type": "Bit", + "Values": + { + "TensorView": 0, + "DecodeFunc": 1 + } + }, + { + "Name": "InitializationModeQualifier", + "Type": "Value", + "Values": + { + "InitOnDeviceReprogramINTEL": 0, + "InitOnDeviceResetINTEL": 1 + } + }, + { + "Name": "HostAccessQualifier", + "Type": "Value", + "Values": + { + "NoneINTEL": 0, + "ReadINTEL": 1, + "WriteINTEL": 2, + "ReadWriteINTEL": 3 + } + }, + { + "Name": "LoadCacheControl", + "Type": "Value", + "Values": + { + "UncachedINTEL": 0, + "CachedINTEL": 1, + "StreamingINTEL": 2, + "InvalidateAfterReadINTEL": 3, + "ConstCachedINTEL": 4 + } + }, + { + "Name": "StoreCacheControl", + "Type": "Value", + "Values": + { + "UncachedINTEL": 0, + "WriteThroughINTEL": 1, + "WriteBackINTEL": 2, + "StreamingINTEL": 3 + } + }, + { + "Name": "NamedMaximumNumberOfRegisters", + "Type": "Value", + "Values": + { + "AutoINTEL": 0 + } + }, + { + "Name": "MatrixMultiplyAccumulateOperands", + "Type": "Bit", + "Values": + { + "MatrixASignedComponentsINTEL": 0, + "MatrixBSignedComponentsINTEL": 1, + "MatrixCBFloat16INTEL": 2, + "MatrixResultBFloat16INTEL": 3, + "MatrixAPackedInt8INTEL": 4, + "MatrixBPackedInt8INTEL": 5, + "MatrixAPackedInt4INTEL": 6, + "MatrixBPackedInt4INTEL": 7, + "MatrixATF32INTEL": 8, + "MatrixBTF32INTEL": 9, + "MatrixAPackedFloat16INTEL": 10, + "MatrixBPackedFloat16INTEL": 11, + "MatrixAPackedBFloat16INTEL": 12, + "MatrixBPackedBFloat16INTEL": 13 + } + }, + { + "Name": "RawAccessChainOperands", + "Type": "Bit", + "Values": + { + "RobustnessPerComponentNV": 0, + "RobustnessPerElementNV": 1 + } + }, + { + "Name": "FPEncoding", + "Type": "Value", + "Values": + { + "BFloat16KHR": 0 + } + }, + { + "Name": "CooperativeVectorMatrixLayout", + "Type": "Value", + "Values": + { + "RowMajorNV": 0, + "ColumnMajorNV": 1, + "InferencingOptimalNV": 2, + "TrainingOptimalNV": 3 + } + }, + { + "Name": "ComponentType", + "Type": "Value", + "Values": + { + "Float16NV": 0, + "Float32NV": 1, + "Float64NV": 2, + "SignedInt8NV": 3, + "SignedInt16NV": 4, + "SignedInt32NV": 5, + "SignedInt64NV": 6, + "UnsignedInt8NV": 7, + "UnsignedInt16NV": 8, + "UnsignedInt32NV": 9, + "UnsignedInt64NV": 10, + "SignedInt8PackedNV": 1000491000, + "UnsignedInt8PackedNV": 1000491001, + "FloatE4M3NV": 1000491002, + "FloatE5M2NV": 1000491003 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpExecutionModeId": 331, + "OpDecorateId": 332, + "OpGroupNonUniformElect": 333, + "OpGroupNonUniformAll": 334, + "OpGroupNonUniformAny": 335, + "OpGroupNonUniformAllEqual": 336, + "OpGroupNonUniformBroadcast": 337, + "OpGroupNonUniformBroadcastFirst": 338, + "OpGroupNonUniformBallot": 339, + "OpGroupNonUniformInverseBallot": 340, + "OpGroupNonUniformBallotBitExtract": 341, + "OpGroupNonUniformBallotBitCount": 342, + "OpGroupNonUniformBallotFindLSB": 343, + "OpGroupNonUniformBallotFindMSB": 344, + "OpGroupNonUniformShuffle": 345, + "OpGroupNonUniformShuffleXor": 346, + "OpGroupNonUniformShuffleUp": 347, + "OpGroupNonUniformShuffleDown": 348, + "OpGroupNonUniformIAdd": 349, + "OpGroupNonUniformFAdd": 350, + "OpGroupNonUniformIMul": 351, + "OpGroupNonUniformFMul": 352, + "OpGroupNonUniformSMin": 353, + "OpGroupNonUniformUMin": 354, + "OpGroupNonUniformFMin": 355, + "OpGroupNonUniformSMax": 356, + "OpGroupNonUniformUMax": 357, + "OpGroupNonUniformFMax": 358, + "OpGroupNonUniformBitwiseAnd": 359, + "OpGroupNonUniformBitwiseOr": 360, + "OpGroupNonUniformBitwiseXor": 361, + "OpGroupNonUniformLogicalAnd": 362, + "OpGroupNonUniformLogicalOr": 363, + "OpGroupNonUniformLogicalXor": 364, + "OpGroupNonUniformQuadBroadcast": 365, + "OpGroupNonUniformQuadSwap": 366, + "OpCopyLogical": 400, + "OpPtrEqual": 401, + "OpPtrNotEqual": 402, + "OpPtrDiff": 403, + "OpColorAttachmentReadEXT": 4160, + "OpDepthAttachmentReadEXT": 4161, + "OpStencilAttachmentReadEXT": 4162, + "OpTerminateInvocation": 4416, + "OpTypeUntypedPointerKHR": 4417, + "OpUntypedVariableKHR": 4418, + "OpUntypedAccessChainKHR": 4419, + "OpUntypedInBoundsAccessChainKHR": 4420, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpUntypedPtrAccessChainKHR": 4423, + "OpUntypedInBoundsPtrAccessChainKHR": 4424, + "OpUntypedArrayLengthKHR": 4425, + "OpUntypedPrefetchKHR": 4426, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpGroupNonUniformRotateKHR": 4431, + "OpSubgroupReadInvocationKHR": 4432, + "OpExtInstWithForwardRefsKHR": 4433, + "OpTraceRayKHR": 4445, + "OpExecuteCallableKHR": 4446, + "OpConvertUToAccelerationStructureKHR": 4447, + "OpIgnoreIntersectionKHR": 4448, + "OpTerminateRayKHR": 4449, + "OpSDot": 4450, + "OpSDotKHR": 4450, + "OpUDot": 4451, + "OpUDotKHR": 4451, + "OpSUDot": 4452, + "OpSUDotKHR": 4452, + "OpSDotAccSat": 4453, + "OpSDotAccSatKHR": 4453, + "OpUDotAccSat": 4454, + "OpUDotAccSatKHR": 4454, + "OpSUDotAccSat": 4455, + "OpSUDotAccSatKHR": 4455, + "OpTypeCooperativeMatrixKHR": 4456, + "OpCooperativeMatrixLoadKHR": 4457, + "OpCooperativeMatrixStoreKHR": 4458, + "OpCooperativeMatrixMulAddKHR": 4459, + "OpCooperativeMatrixLengthKHR": 4460, + "OpConstantCompositeReplicateEXT": 4461, + "OpSpecConstantCompositeReplicateEXT": 4462, + "OpCompositeConstructReplicateEXT": 4463, + "OpTypeRayQueryKHR": 4472, + "OpRayQueryInitializeKHR": 4473, + "OpRayQueryTerminateKHR": 4474, + "OpRayQueryGenerateIntersectionKHR": 4475, + "OpRayQueryConfirmIntersectionKHR": 4476, + "OpRayQueryProceedKHR": 4477, + "OpRayQueryGetIntersectionTypeKHR": 4479, + "OpImageSampleWeightedQCOM": 4480, + "OpImageBoxFilterQCOM": 4481, + "OpImageBlockMatchSSDQCOM": 4482, + "OpImageBlockMatchSADQCOM": 4483, + "OpImageBlockMatchWindowSSDQCOM": 4500, + "OpImageBlockMatchWindowSADQCOM": 4501, + "OpImageBlockMatchGatherSSDQCOM": 4502, + "OpImageBlockMatchGatherSADQCOM": 4503, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpReadClockKHR": 5056, + "OpAllocateNodePayloadsAMDX": 5074, + "OpEnqueueNodePayloadsAMDX": 5075, + "OpTypeNodePayloadArrayAMDX": 5076, + "OpFinishWritingNodePayloadAMDX": 5078, + "OpNodePayloadArrayLengthAMDX": 5090, + "OpIsNodePayloadValidAMDX": 5101, + "OpConstantStringAMDX": 5103, + "OpSpecConstantStringAMDX": 5104, + "OpGroupNonUniformQuadAllKHR": 5110, + "OpGroupNonUniformQuadAnyKHR": 5111, + "OpHitObjectRecordHitMotionNV": 5249, + "OpHitObjectRecordHitWithIndexMotionNV": 5250, + "OpHitObjectRecordMissMotionNV": 5251, + "OpHitObjectGetWorldToObjectNV": 5252, + "OpHitObjectGetObjectToWorldNV": 5253, + "OpHitObjectGetObjectRayDirectionNV": 5254, + "OpHitObjectGetObjectRayOriginNV": 5255, + "OpHitObjectTraceRayMotionNV": 5256, + "OpHitObjectGetShaderRecordBufferHandleNV": 5257, + "OpHitObjectGetShaderBindingTableRecordIndexNV": 5258, + "OpHitObjectRecordEmptyNV": 5259, + "OpHitObjectTraceRayNV": 5260, + "OpHitObjectRecordHitNV": 5261, + "OpHitObjectRecordHitWithIndexNV": 5262, + "OpHitObjectRecordMissNV": 5263, + "OpHitObjectExecuteShaderNV": 5264, + "OpHitObjectGetCurrentTimeNV": 5265, + "OpHitObjectGetAttributesNV": 5266, + "OpHitObjectGetHitKindNV": 5267, + "OpHitObjectGetPrimitiveIndexNV": 5268, + "OpHitObjectGetGeometryIndexNV": 5269, + "OpHitObjectGetInstanceIdNV": 5270, + "OpHitObjectGetInstanceCustomIndexNV": 5271, + "OpHitObjectGetWorldRayDirectionNV": 5272, + "OpHitObjectGetWorldRayOriginNV": 5273, + "OpHitObjectGetRayTMaxNV": 5274, + "OpHitObjectGetRayTMinNV": 5275, + "OpHitObjectIsEmptyNV": 5276, + "OpHitObjectIsHitNV": 5277, + "OpHitObjectIsMissNV": 5278, + "OpReorderThreadWithHitObjectNV": 5279, + "OpReorderThreadWithHintNV": 5280, + "OpTypeHitObjectNV": 5281, + "OpImageSampleFootprintNV": 5283, + "OpTypeCooperativeVectorNV": 5288, + "OpCooperativeVectorMatrixMulNV": 5289, + "OpCooperativeVectorOuterProductAccumulateNV": 5290, + "OpCooperativeVectorReduceSumAccumulateNV": 5291, + "OpCooperativeVectorMatrixMulAddNV": 5292, + "OpCooperativeMatrixConvertNV": 5293, + "OpEmitMeshTasksEXT": 5294, + "OpSetMeshOutputsEXT": 5295, + "OpGroupNonUniformPartitionNV": 5296, + "OpWritePackedPrimitiveIndices4x8NV": 5299, + "OpFetchMicroTriangleVertexPositionNV": 5300, + "OpFetchMicroTriangleVertexBarycentricNV": 5301, + "OpCooperativeVectorLoadNV": 5302, + "OpCooperativeVectorStoreNV": 5303, + "OpReportIntersectionKHR": 5334, + "OpReportIntersectionNV": 5334, + "OpIgnoreIntersectionNV": 5335, + "OpTerminateRayNV": 5336, + "OpTraceNV": 5337, + "OpTraceMotionNV": 5338, + "OpTraceRayMotionNV": 5339, + "OpRayQueryGetIntersectionTriangleVertexPositionsKHR": 5340, + "OpTypeAccelerationStructureKHR": 5341, + "OpTypeAccelerationStructureNV": 5341, + "OpExecuteCallableNV": 5344, + "OpRayQueryGetClusterIdNV": 5345, + "OpHitObjectGetClusterIdNV": 5346, + "OpTypeCooperativeMatrixNV": 5358, + "OpCooperativeMatrixLoadNV": 5359, + "OpCooperativeMatrixStoreNV": 5360, + "OpCooperativeMatrixMulAddNV": 5361, + "OpCooperativeMatrixLengthNV": 5362, + "OpBeginInvocationInterlockEXT": 5364, + "OpEndInvocationInterlockEXT": 5365, + "OpCooperativeMatrixReduceNV": 5366, + "OpCooperativeMatrixLoadTensorNV": 5367, + "OpCooperativeMatrixStoreTensorNV": 5368, + "OpCooperativeMatrixPerElementOpNV": 5369, + "OpTypeTensorLayoutNV": 5370, + "OpTypeTensorViewNV": 5371, + "OpCreateTensorLayoutNV": 5372, + "OpTensorLayoutSetDimensionNV": 5373, + "OpTensorLayoutSetStrideNV": 5374, + "OpTensorLayoutSliceNV": 5375, + "OpTensorLayoutSetClampValueNV": 5376, + "OpCreateTensorViewNV": 5377, + "OpTensorViewSetDimensionNV": 5378, + "OpTensorViewSetStrideNV": 5379, + "OpDemoteToHelperInvocation": 5380, + "OpDemoteToHelperInvocationEXT": 5380, + "OpIsHelperInvocationEXT": 5381, + "OpTensorViewSetClipNV": 5382, + "OpTensorLayoutSetBlockSizeNV": 5384, + "OpCooperativeMatrixTransposeNV": 5390, + "OpConvertUToImageNV": 5391, + "OpConvertUToSamplerNV": 5392, + "OpConvertImageToUNV": 5393, + "OpConvertSamplerToUNV": 5394, + "OpConvertUToSampledImageNV": 5395, + "OpConvertSampledImageToUNV": 5396, + "OpSamplerImageAddressingModeNV": 5397, + "OpRawAccessChainNV": 5398, + "OpRayQueryGetIntersectionSpherePositionNV": 5427, + "OpRayQueryGetIntersectionSphereRadiusNV": 5428, + "OpRayQueryGetIntersectionLSSPositionsNV": 5429, + "OpRayQueryGetIntersectionLSSRadiiNV": 5430, + "OpRayQueryGetIntersectionLSSHitValueNV": 5431, + "OpHitObjectGetSpherePositionNV": 5432, + "OpHitObjectGetSphereRadiusNV": 5433, + "OpHitObjectGetLSSPositionsNV": 5434, + "OpHitObjectGetLSSRadiiNV": 5435, + "OpHitObjectIsSphereHitNV": 5436, + "OpHitObjectIsLSSHitNV": 5437, + "OpRayQueryIsSphereHitNV": 5438, + "OpRayQueryIsLSSHitNV": 5439, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpSubgroupImageMediaBlockReadINTEL": 5580, + "OpSubgroupImageMediaBlockWriteINTEL": 5581, + "OpUCountLeadingZerosINTEL": 5585, + "OpUCountTrailingZerosINTEL": 5586, + "OpAbsISubINTEL": 5587, + "OpAbsUSubINTEL": 5588, + "OpIAddSatINTEL": 5589, + "OpUAddSatINTEL": 5590, + "OpIAverageINTEL": 5591, + "OpUAverageINTEL": 5592, + "OpIAverageRoundedINTEL": 5593, + "OpUAverageRoundedINTEL": 5594, + "OpISubSatINTEL": 5595, + "OpUSubSatINTEL": 5596, + "OpIMul32x16INTEL": 5597, + "OpUMul32x16INTEL": 5598, + "OpConstantFunctionPointerINTEL": 5600, + "OpFunctionPointerCallINTEL": 5601, + "OpAsmTargetINTEL": 5609, + "OpAsmINTEL": 5610, + "OpAsmCallINTEL": 5611, + "OpAtomicFMinEXT": 5614, + "OpAtomicFMaxEXT": 5615, + "OpAssumeTrueKHR": 5630, + "OpExpectKHR": 5631, + "OpDecorateString": 5632, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateString": 5633, + "OpMemberDecorateStringGOOGLE": 5633, + "OpVmeImageINTEL": 5699, + "OpTypeVmeImageINTEL": 5700, + "OpTypeAvcImePayloadINTEL": 5701, + "OpTypeAvcRefPayloadINTEL": 5702, + "OpTypeAvcSicPayloadINTEL": 5703, + "OpTypeAvcMcePayloadINTEL": 5704, + "OpTypeAvcMceResultINTEL": 5705, + "OpTypeAvcImeResultINTEL": 5706, + "OpTypeAvcImeResultSingleReferenceStreamoutINTEL": 5707, + "OpTypeAvcImeResultDualReferenceStreamoutINTEL": 5708, + "OpTypeAvcImeSingleReferenceStreaminINTEL": 5709, + "OpTypeAvcImeDualReferenceStreaminINTEL": 5710, + "OpTypeAvcRefResultINTEL": 5711, + "OpTypeAvcSicResultINTEL": 5712, + "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL": 5713, + "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL": 5714, + "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL": 5715, + "OpSubgroupAvcMceSetInterShapePenaltyINTEL": 5716, + "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL": 5717, + "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL": 5718, + "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL": 5719, + "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL": 5720, + "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL": 5721, + "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL": 5722, + "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL": 5723, + "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL": 5724, + "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL": 5725, + "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL": 5726, + "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL": 5727, + "OpSubgroupAvcMceSetAcOnlyHaarINTEL": 5728, + "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL": 5729, + "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL": 5730, + "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL": 5731, + "OpSubgroupAvcMceConvertToImePayloadINTEL": 5732, + "OpSubgroupAvcMceConvertToImeResultINTEL": 5733, + "OpSubgroupAvcMceConvertToRefPayloadINTEL": 5734, + "OpSubgroupAvcMceConvertToRefResultINTEL": 5735, + "OpSubgroupAvcMceConvertToSicPayloadINTEL": 5736, + "OpSubgroupAvcMceConvertToSicResultINTEL": 5737, + "OpSubgroupAvcMceGetMotionVectorsINTEL": 5738, + "OpSubgroupAvcMceGetInterDistortionsINTEL": 5739, + "OpSubgroupAvcMceGetBestInterDistortionsINTEL": 5740, + "OpSubgroupAvcMceGetInterMajorShapeINTEL": 5741, + "OpSubgroupAvcMceGetInterMinorShapeINTEL": 5742, + "OpSubgroupAvcMceGetInterDirectionsINTEL": 5743, + "OpSubgroupAvcMceGetInterMotionVectorCountINTEL": 5744, + "OpSubgroupAvcMceGetInterReferenceIdsINTEL": 5745, + "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL": 5746, + "OpSubgroupAvcImeInitializeINTEL": 5747, + "OpSubgroupAvcImeSetSingleReferenceINTEL": 5748, + "OpSubgroupAvcImeSetDualReferenceINTEL": 5749, + "OpSubgroupAvcImeRefWindowSizeINTEL": 5750, + "OpSubgroupAvcImeAdjustRefOffsetINTEL": 5751, + "OpSubgroupAvcImeConvertToMcePayloadINTEL": 5752, + "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL": 5753, + "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL": 5754, + "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL": 5755, + "OpSubgroupAvcImeSetWeightedSadINTEL": 5756, + "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL": 5757, + "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL": 5758, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL": 5759, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL": 5760, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL": 5761, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL": 5762, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL": 5763, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL": 5764, + "OpSubgroupAvcImeConvertToMceResultINTEL": 5765, + "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL": 5766, + "OpSubgroupAvcImeGetDualReferenceStreaminINTEL": 5767, + "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL": 5768, + "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL": 5769, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL": 5770, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL": 5771, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL": 5772, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL": 5773, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL": 5774, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL": 5775, + "OpSubgroupAvcImeGetBorderReachedINTEL": 5776, + "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL": 5777, + "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL": 5778, + "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL": 5779, + "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL": 5780, + "OpSubgroupAvcFmeInitializeINTEL": 5781, + "OpSubgroupAvcBmeInitializeINTEL": 5782, + "OpSubgroupAvcRefConvertToMcePayloadINTEL": 5783, + "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL": 5784, + "OpSubgroupAvcRefSetBilinearFilterEnableINTEL": 5785, + "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL": 5786, + "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL": 5787, + "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL": 5788, + "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL": 5789, + "OpSubgroupAvcRefConvertToMceResultINTEL": 5790, + "OpSubgroupAvcSicInitializeINTEL": 5791, + "OpSubgroupAvcSicConfigureSkcINTEL": 5792, + "OpSubgroupAvcSicConfigureIpeLumaINTEL": 5793, + "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL": 5794, + "OpSubgroupAvcSicGetMotionVectorMaskINTEL": 5795, + "OpSubgroupAvcSicConvertToMcePayloadINTEL": 5796, + "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL": 5797, + "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL": 5798, + "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL": 5799, + "OpSubgroupAvcSicSetBilinearFilterEnableINTEL": 5800, + "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL": 5801, + "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL": 5802, + "OpSubgroupAvcSicEvaluateIpeINTEL": 5803, + "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL": 5804, + "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL": 5805, + "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL": 5806, + "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL": 5807, + "OpSubgroupAvcSicConvertToMceResultINTEL": 5808, + "OpSubgroupAvcSicGetIpeLumaShapeINTEL": 5809, + "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL": 5810, + "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL": 5811, + "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL": 5812, + "OpSubgroupAvcSicGetIpeChromaModeINTEL": 5813, + "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": 5814, + "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": 5815, + "OpSubgroupAvcSicGetInterRawSadsINTEL": 5816, + "OpVariableLengthArrayINTEL": 5818, + "OpSaveMemoryINTEL": 5819, + "OpRestoreMemoryINTEL": 5820, + "OpArbitraryFloatSinCosPiINTEL": 5840, + "OpArbitraryFloatCastINTEL": 5841, + "OpArbitraryFloatCastFromIntINTEL": 5842, + "OpArbitraryFloatCastToIntINTEL": 5843, + "OpArbitraryFloatAddINTEL": 5846, + "OpArbitraryFloatSubINTEL": 5847, + "OpArbitraryFloatMulINTEL": 5848, + "OpArbitraryFloatDivINTEL": 5849, + "OpArbitraryFloatGTINTEL": 5850, + "OpArbitraryFloatGEINTEL": 5851, + "OpArbitraryFloatLTINTEL": 5852, + "OpArbitraryFloatLEINTEL": 5853, + "OpArbitraryFloatEQINTEL": 5854, + "OpArbitraryFloatRecipINTEL": 5855, + "OpArbitraryFloatRSqrtINTEL": 5856, + "OpArbitraryFloatCbrtINTEL": 5857, + "OpArbitraryFloatHypotINTEL": 5858, + "OpArbitraryFloatSqrtINTEL": 5859, + "OpArbitraryFloatLogINTEL": 5860, + "OpArbitraryFloatLog2INTEL": 5861, + "OpArbitraryFloatLog10INTEL": 5862, + "OpArbitraryFloatLog1pINTEL": 5863, + "OpArbitraryFloatExpINTEL": 5864, + "OpArbitraryFloatExp2INTEL": 5865, + "OpArbitraryFloatExp10INTEL": 5866, + "OpArbitraryFloatExpm1INTEL": 5867, + "OpArbitraryFloatSinINTEL": 5868, + "OpArbitraryFloatCosINTEL": 5869, + "OpArbitraryFloatSinCosINTEL": 5870, + "OpArbitraryFloatSinPiINTEL": 5871, + "OpArbitraryFloatCosPiINTEL": 5872, + "OpArbitraryFloatASinINTEL": 5873, + "OpArbitraryFloatASinPiINTEL": 5874, + "OpArbitraryFloatACosINTEL": 5875, + "OpArbitraryFloatACosPiINTEL": 5876, + "OpArbitraryFloatATanINTEL": 5877, + "OpArbitraryFloatATanPiINTEL": 5878, + "OpArbitraryFloatATan2INTEL": 5879, + "OpArbitraryFloatPowINTEL": 5880, + "OpArbitraryFloatPowRINTEL": 5881, + "OpArbitraryFloatPowNINTEL": 5882, + "OpLoopControlINTEL": 5887, + "OpAliasDomainDeclINTEL": 5911, + "OpAliasScopeDeclINTEL": 5912, + "OpAliasScopeListDeclINTEL": 5913, + "OpFixedSqrtINTEL": 5923, + "OpFixedRecipINTEL": 5924, + "OpFixedRsqrtINTEL": 5925, + "OpFixedSinINTEL": 5926, + "OpFixedCosINTEL": 5927, + "OpFixedSinCosINTEL": 5928, + "OpFixedSinPiINTEL": 5929, + "OpFixedCosPiINTEL": 5930, + "OpFixedSinCosPiINTEL": 5931, + "OpFixedLogINTEL": 5932, + "OpFixedExpINTEL": 5933, + "OpPtrCastToCrossWorkgroupINTEL": 5934, + "OpCrossWorkgroupCastToPtrINTEL": 5938, + "OpReadPipeBlockingINTEL": 5946, + "OpWritePipeBlockingINTEL": 5947, + "OpFPGARegINTEL": 5949, + "OpRayQueryGetRayTMinKHR": 6016, + "OpRayQueryGetRayFlagsKHR": 6017, + "OpRayQueryGetIntersectionTKHR": 6018, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": 6019, + "OpRayQueryGetIntersectionInstanceIdKHR": 6020, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": 6021, + "OpRayQueryGetIntersectionGeometryIndexKHR": 6022, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": 6023, + "OpRayQueryGetIntersectionBarycentricsKHR": 6024, + "OpRayQueryGetIntersectionFrontFaceKHR": 6025, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": 6026, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": 6027, + "OpRayQueryGetIntersectionObjectRayOriginKHR": 6028, + "OpRayQueryGetWorldRayDirectionKHR": 6029, + "OpRayQueryGetWorldRayOriginKHR": 6030, + "OpRayQueryGetIntersectionObjectToWorldKHR": 6031, + "OpRayQueryGetIntersectionWorldToObjectKHR": 6032, + "OpAtomicFAddEXT": 6035, + "OpTypeBufferSurfaceINTEL": 6086, + "OpTypeStructContinuedINTEL": 6090, + "OpConstantCompositeContinuedINTEL": 6091, + "OpSpecConstantCompositeContinuedINTEL": 6092, + "OpCompositeConstructContinuedINTEL": 6096, + "OpConvertFToBF16INTEL": 6116, + "OpConvertBF16ToFINTEL": 6117, + "OpControlBarrierArriveINTEL": 6142, + "OpControlBarrierWaitINTEL": 6143, + "OpArithmeticFenceEXT": 6145, + "OpTaskSequenceCreateINTEL": 6163, + "OpTaskSequenceAsyncINTEL": 6164, + "OpTaskSequenceGetINTEL": 6165, + "OpTaskSequenceReleaseINTEL": 6166, + "OpTypeTaskSequenceINTEL": 6199, + "OpSubgroupBlockPrefetchINTEL": 6221, + "OpSubgroup2DBlockLoadINTEL": 6231, + "OpSubgroup2DBlockLoadTransformINTEL": 6232, + "OpSubgroup2DBlockLoadTransposeINTEL": 6233, + "OpSubgroup2DBlockPrefetchINTEL": 6234, + "OpSubgroup2DBlockStoreINTEL": 6235, + "OpSubgroupMatrixMultiplyAccumulateINTEL": 6237, + "OpBitwiseFunctionINTEL": 6242, + "OpGroupIMulKHR": 6401, + "OpGroupFMulKHR": 6402, + "OpGroupBitwiseAndKHR": 6403, + "OpGroupBitwiseOrKHR": 6404, + "OpGroupBitwiseXorKHR": 6405, + "OpGroupLogicalAndKHR": 6406, + "OpGroupLogicalOrKHR": 6407, + "OpGroupLogicalXorKHR": 6408, + "OpRoundFToTF32INTEL": 6426, + "OpMaskedGatherINTEL": 6428, + "OpMaskedScatterINTEL": 6429 + } + } + ] + } +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua new file mode 100644 index 00000000..6cae8ce4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua @@ -0,0 +1,2386 @@ +-- Copyright (c) 2014-2024 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python, C#, D, Beef +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- - C# will use enum classes in the Specification class located in the "Spv" namespace, +-- e.g.: Spv.Specification.SourceLanguage.GLSL +-- - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +-- - Beef will use enum classes in the Specification class located in the "Spv" namespace, +-- e.g.: Spv.Specification.SourceLanguage.GLSL +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010600, + Revision = 1, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + CPP_for_OpenCL = 6, + SYCL = 7, + HERO_C = 8, + NZSL = 9, + WGSL = 10, + Slang = 11, + Zig = 12, + Rust = 13, + Max = 0x7fffffff, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + TaskEXT = 5364, + MeshEXT = 5365, + Max = 0x7fffffff, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + NonCoherentColorAttachmentReadEXT = 4169, + NonCoherentDepthAttachmentReadEXT = 4170, + NonCoherentStencilAttachmentReadEXT = 4171, + SubgroupUniformControlFlowKHR = 4421, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + NonCoherentTileAttachmentReadQCOM = 4489, + TileShadingRateQCOM = 4490, + EarlyAndLateFragmentTestsAMD = 5017, + StencilRefReplacingEXT = 5027, + CoalescingAMDX = 5069, + IsApiEntryAMDX = 5070, + MaxNodeRecursionAMDX = 5071, + StaticNumWorkgroupsAMDX = 5072, + ShaderIndexAMDX = 5073, + MaxNumWorkgroupsAMDX = 5077, + StencilRefUnchangedFrontAMD = 5079, + StencilRefGreaterFrontAMD = 5080, + StencilRefLessFrontAMD = 5081, + StencilRefUnchangedBackAMD = 5082, + StencilRefGreaterBackAMD = 5083, + StencilRefLessBackAMD = 5084, + QuadDerivativesKHR = 5088, + RequireFullQuadsKHR = 5089, + SharesInputWithAMDX = 5102, + OutputLinesEXT = 5269, + OutputLinesNV = 5269, + OutputPrimitivesEXT = 5270, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsKHR = 5289, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearKHR = 5290, + DerivativeGroupLinearNV = 5290, + OutputTrianglesEXT = 5298, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, + MaximallyReconvergesKHR = 6023, + FPFastMathDefault = 6028, + StreamingInterfaceINTEL = 6154, + RegisterMapInterfaceINTEL = 6160, + NamedBarrierCountINTEL = 6417, + MaximumRegistersINTEL = 6461, + MaximumRegistersIdINTEL = 6462, + NamedMaximumRegistersINTEL = 6463, + Max = 0x7fffffff, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + TileImageEXT = 4172, + TileAttachmentQCOM = 4491, + NodePayloadAMDX = 5068, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + HitObjectAttributeNV = 5385, + TaskPayloadWorkgroupEXT = 5402, + CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, + Max = 0x7fffffff, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + TileImageDataEXT = 4173, + Max = 0x7fffffff, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + UnormInt10X6EXT = 17, + UnsignedIntRaw10EXT = 19, + UnsignedIntRaw12EXT = 20, + UnormInt2_101010EXT = 21, + UnsignedInt10X6EXT = 22, + UnsignedInt12X4EXT = 23, + UnsignedInt14X2EXT = 24, + UnormInt12X4EXT = 25, + UnormInt14X2EXT = 26, + Max = 0x7fffffff, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Nontemporal = 14, + Offsets = 16, + Max = 0x7fffffff, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + Nontemporal = 0x00004000, + Offsets = 0x00010000, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + AllowContract = 16, + AllowContractFastINTEL = 16, + AllowReassoc = 17, + AllowReassocINTEL = 17, + AllowTransform = 18, + Max = 0x7fffffff, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + AllowContract = 0x00010000, + AllowContractFastINTEL = 0x00010000, + AllowReassoc = 0x00020000, + AllowReassocINTEL = 0x00020000, + AllowTransform = 0x00040000, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, + }, + + LinkageType = { + Export = 0, + Import = 1, + LinkOnceODR = 2, + Max = 0x7fffffff, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + RuntimeAlignedINTEL = 5940, + Max = 0x7fffffff, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + WeightTextureQCOM = 4487, + BlockMatchTextureQCOM = 4488, + BlockMatchSamplerQCOM = 4499, + ExplicitInterpAMD = 4999, + NodeSharesPayloadLimitsWithAMDX = 5019, + NodeMaxPayloadsAMDX = 5020, + TrackFinishWritingAMDX = 5078, + PayloadNodeNameAMDX = 5091, + PayloadNodeBaseIndexAMDX = 5098, + PayloadNodeSparseArrayAMDX = 5099, + PayloadNodeArraySizeAMDX = 5100, + PayloadDispatchIndirectAMDX = 5105, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveEXT = 5271, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexKHR = 5285, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + HitObjectShaderRecordBufferNV = 5386, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, + SIMTCallINTEL = 5599, + ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + StridesizeINTEL = 5883, + WordsizeINTEL = 5884, + TrueDualPortINTEL = 5885, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + MathOpDSPModeINTEL = 5909, + AliasScopeINTEL = 5914, + NoAliasINTEL = 5915, + InitiationIntervalINTEL = 5917, + MaxConcurrencyINTEL = 5918, + PipelineEnableINTEL = 5919, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, + MediaBlockIOINTEL = 6140, + StallFreeINTEL = 6151, + FPMaxErrorDecorationINTEL = 6170, + LatencyControlLabelINTEL = 6172, + LatencyControlConstraintINTEL = 6173, + ConduitKernelArgumentINTEL = 6175, + RegisterMapKernelArgumentINTEL = 6176, + MMHostInterfaceAddressWidthINTEL = 6177, + MMHostInterfaceDataWidthINTEL = 6178, + MMHostInterfaceLatencyINTEL = 6179, + MMHostInterfaceReadWriteModeINTEL = 6180, + MMHostInterfaceMaxBurstINTEL = 6181, + MMHostInterfaceWaitRequestINTEL = 6182, + StableKernelArgumentINTEL = 6183, + HostAccessINTEL = 6188, + InitModeINTEL = 6190, + ImplementInRegisterMapINTEL = 6191, + CacheControlLoadINTEL = 6442, + CacheControlStoreINTEL = 6443, + Max = 0x7fffffff, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + CoreIDARM = 4160, + CoreCountARM = 4161, + CoreMaxIDARM = 4162, + WarpIDARM = 4163, + WarpMaxIDARM = 4164, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + TileOffsetQCOM = 4492, + TileDimensionQCOM = 4493, + TileApronSizeQCOM = 4494, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + RemainingRecursionLevelsAMDX = 5021, + ShaderIndexAMDX = 5073, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordKHR = 5286, + BaryCoordNV = 5286, + BaryCoordNoPerspKHR = 5287, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + PrimitivePointIndicesEXT = 5294, + PrimitiveLineIndicesEXT = 5295, + PrimitiveTriangleIndicesEXT = 5296, + CullPrimitiveEXT = 5299, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + CurrentRayTimeNV = 5334, + HitTriangleVertexPositionsKHR = 5335, + HitMicroTriangleVertexPositionsNV = 5337, + HitMicroTriangleVertexBarycentricsNV = 5344, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + HitIsSphereNV = 5359, + HitIsLSSNV = 5360, + HitSpherePositionNV = 5361, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + HitLSSPositionsNV = 5396, + HitKindFrontFacingMicroTriangleNV = 5405, + HitKindBackFacingMicroTriangleNV = 5406, + HitSphereRadiusNV = 5420, + HitLSSRadiiNV = 5421, + ClusterIDNV = 5436, + CullMaskKHR = 6021, + Max = 0x7fffffff, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + NoFusionINTEL = 23, + LoopCountINTEL = 24, + MaxReinvocationDelayINTEL = 25, + Max = 0x7fffffff, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + NoFusionINTEL = 0x00800000, + LoopCountINTEL = 0x01000000, + MaxReinvocationDelayINTEL = 0x02000000, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + OptNoneEXT = 16, + OptNoneINTEL = 16, + Max = 0x7fffffff, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + OptNoneEXT = 0x00010000, + OptNoneINTEL = 0x00010000, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + AliasScopeINTELMask = 16, + NoAliasINTELMask = 17, + Max = 0x7fffffff, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + AliasScopeINTELMask = 0x00010000, + NoAliasINTELMask = 0x00020000, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + Max = 0x7fffffff, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + UniformDecoration = 71, + CoreBuiltinsARM = 4165, + TileImageColorReadAccessEXT = 4166, + TileImageDepthReadAccessEXT = 4167, + TileImageStencilReadAccessEXT = 4168, + CooperativeMatrixLayoutsARM = 4201, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + UntypedPointersKHR = 4473, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + TextureSampleWeightedQCOM = 4484, + TextureBoxFilterQCOM = 4485, + TextureBlockMatchQCOM = 4486, + TileShadingQCOM = 4495, + TextureBlockMatch2QCOM = 4498, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + ShaderEnqueueAMDX = 5067, + QuadControlKHR = 5087, + BFloat16TypeKHR = 5116, + BFloat16DotProductKHR = 5117, + BFloat16CooperativeMatrixKHR = 5118, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + MeshShadingEXT = 5283, + FragmentBarycentricKHR = 5284, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsKHR = 5288, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingPositionFetchKHR = 5336, + RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearKHR = 5350, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocation = 5379, + DemoteToHelperInvocationEXT = 5379, + DisplacementMicromapNV = 5380, + RayTracingOpacityMicromapEXT = 5381, + ShaderInvocationReorderNV = 5383, + BindlessTextureNV = 5390, + RayQueryPositionFetchKHR = 5391, + CooperativeVectorNV = 5394, + AtomicFloat16VectorNV = 5404, + RayTracingDisplacementMicromapNV = 5409, + RawAccessChainsNV = 5414, + RayTracingSpheresGeometryNV = 5418, + RayTracingLinearSweptSpheresGeometryNV = 5419, + CooperativeMatrixReductionsNV = 5430, + CooperativeMatrixConversionsNV = 5431, + CooperativeMatrixPerElementOperationsNV = 5432, + CooperativeMatrixTensorAddressingNV = 5433, + CooperativeMatrixBlockLoadsNV = 5434, + CooperativeVectorTrainingNV = 5435, + RayTracingClusterAccelerationStructureNV = 5437, + TensorAddressingNV = 5439, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, + FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, + ArbitraryPrecisionFloatingPointINTEL = 5845, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGADSPControlINTEL = 5908, + MemoryAccessAliasingINTEL = 5910, + FPGAInvocationPipeliningAttributesINTEL = 5916, + FPGABufferLocationINTEL = 5920, + ArbitraryPrecisionFixedPointINTEL = 5922, + USMStorageClassesINTEL = 5935, + RuntimeAlignedAttributeINTEL = 5939, + IOPipesINTEL = 5943, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + DotProductInputAll = 6016, + DotProductInputAllKHR = 6016, + DotProductInput4x8Bit = 6017, + DotProductInput4x8BitKHR = 6017, + DotProductInput4x8BitPacked = 6018, + DotProductInput4x8BitPackedKHR = 6018, + DotProduct = 6019, + DotProductKHR = 6019, + RayCullMaskKHR = 6020, + CooperativeMatrixKHR = 6022, + ReplicatedCompositesEXT = 6024, + BitInstructions = 6025, + GroupNonUniformRotateKHR = 6026, + FloatControls2 = 6029, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + LongCompositesINTEL = 6089, + OptNoneEXT = 6094, + OptNoneINTEL = 6094, + AtomicFloat16AddEXT = 6095, + DebugInfoModuleINTEL = 6114, + BFloat16ConversionINTEL = 6115, + SplitBarrierINTEL = 6141, + ArithmeticFenceEXT = 6144, + FPGAClusterAttributesV2INTEL = 6150, + FPGAKernelAttributesv2INTEL = 6161, + TaskSequenceINTEL = 6162, + FPMaxErrorINTEL = 6169, + FPGALatencyControlINTEL = 6171, + FPGAArgumentInterfacesINTEL = 6174, + GlobalVariableHostAccessINTEL = 6187, + GlobalVariableFPGADecorationsINTEL = 6189, + SubgroupBufferPrefetchINTEL = 6220, + Subgroup2DBlockIOINTEL = 6228, + Subgroup2DBlockTransformINTEL = 6229, + Subgroup2DBlockTransposeINTEL = 6230, + SubgroupMatrixMultiplyAccumulateINTEL = 6236, + TernaryBitwiseFunctionINTEL = 6241, + GroupUniformArithmeticKHR = 6400, + TensorFloat32RoundingINTEL = 6425, + MaskedGatherScatterINTEL = 6427, + CacheControlsINTEL = 6441, + RegisterLimitsINTEL = 6460, + Max = 0x7fffffff, + }, + + RayFlagsShift = { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipBuiltinPrimitivesNV = 8, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + ForceOpacityMicromap2StateEXT = 10, + Max = 0x7fffffff, + }, + + RayFlagsMask = { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipBuiltinPrimitivesNV = 0x00000100, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + ForceOpacityMicromap2StateEXT = 0x00000400, + }, + + RayQueryIntersection = { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, + }, + + RayQueryCommittedIntersectionType = { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, + }, + + RayQueryCandidateIntersectionType = { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, + }, + + FragmentShadingRateShift = { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, + }, + + FragmentShadingRateMask = { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, + }, + + FPDenormMode = { + Preserve = 0, + FlushToZero = 1, + Max = 0x7fffffff, + }, + + FPOperationMode = { + IEEE = 0, + ALT = 1, + Max = 0x7fffffff, + }, + + QuantizationModes = { + TRN = 0, + TRN_ZERO = 1, + RND = 2, + RND_ZERO = 3, + RND_INF = 4, + RND_MIN_INF = 5, + RND_CONV = 6, + RND_CONV_ODD = 7, + Max = 0x7fffffff, + }, + + OverflowModes = { + WRAP = 0, + SAT = 1, + SAT_ZERO = 2, + SAT_SYM = 3, + Max = 0x7fffffff, + }, + + PackedVectorFormat = { + PackedVectorFormat4x8Bit = 0, + PackedVectorFormat4x8BitKHR = 0, + Max = 0x7fffffff, + }, + + CooperativeMatrixOperandsShift = { + MatrixASignedComponentsKHR = 0, + MatrixBSignedComponentsKHR = 1, + MatrixCSignedComponentsKHR = 2, + MatrixResultSignedComponentsKHR = 3, + SaturatingAccumulationKHR = 4, + Max = 0x7fffffff, + }, + + CooperativeMatrixOperandsMask = { + MaskNone = 0, + MatrixASignedComponentsKHR = 0x00000001, + MatrixBSignedComponentsKHR = 0x00000002, + MatrixCSignedComponentsKHR = 0x00000004, + MatrixResultSignedComponentsKHR = 0x00000008, + SaturatingAccumulationKHR = 0x00000010, + }, + + CooperativeMatrixLayout = { + RowMajorKHR = 0, + ColumnMajorKHR = 1, + RowBlockedInterleavedARM = 4202, + ColumnBlockedInterleavedARM = 4203, + Max = 0x7fffffff, + }, + + CooperativeMatrixUse = { + MatrixAKHR = 0, + MatrixBKHR = 1, + MatrixAccumulatorKHR = 2, + Max = 0x7fffffff, + }, + + CooperativeMatrixReduceShift = { + Row = 0, + Column = 1, + CooperativeMatrixReduce2x2 = 2, + Max = 0x7fffffff, + }, + + CooperativeMatrixReduceMask = { + MaskNone = 0, + Row = 0x00000001, + Column = 0x00000002, + CooperativeMatrixReduce2x2 = 0x00000004, + }, + + TensorClampMode = { + Undefined = 0, + Constant = 1, + ClampToEdge = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + }, + + TensorAddressingOperandsShift = { + TensorView = 0, + DecodeFunc = 1, + Max = 0x7fffffff, + }, + + TensorAddressingOperandsMask = { + MaskNone = 0, + TensorView = 0x00000001, + DecodeFunc = 0x00000002, + }, + + InitializationModeQualifier = { + InitOnDeviceReprogramINTEL = 0, + InitOnDeviceResetINTEL = 1, + Max = 0x7fffffff, + }, + + HostAccessQualifier = { + NoneINTEL = 0, + ReadINTEL = 1, + WriteINTEL = 2, + ReadWriteINTEL = 3, + Max = 0x7fffffff, + }, + + LoadCacheControl = { + UncachedINTEL = 0, + CachedINTEL = 1, + StreamingINTEL = 2, + InvalidateAfterReadINTEL = 3, + ConstCachedINTEL = 4, + Max = 0x7fffffff, + }, + + StoreCacheControl = { + UncachedINTEL = 0, + WriteThroughINTEL = 1, + WriteBackINTEL = 2, + StreamingINTEL = 3, + Max = 0x7fffffff, + }, + + NamedMaximumNumberOfRegisters = { + AutoINTEL = 0, + Max = 0x7fffffff, + }, + + MatrixMultiplyAccumulateOperandsShift = { + MatrixASignedComponentsINTEL = 0, + MatrixBSignedComponentsINTEL = 1, + MatrixCBFloat16INTEL = 2, + MatrixResultBFloat16INTEL = 3, + MatrixAPackedInt8INTEL = 4, + MatrixBPackedInt8INTEL = 5, + MatrixAPackedInt4INTEL = 6, + MatrixBPackedInt4INTEL = 7, + MatrixATF32INTEL = 8, + MatrixBTF32INTEL = 9, + MatrixAPackedFloat16INTEL = 10, + MatrixBPackedFloat16INTEL = 11, + MatrixAPackedBFloat16INTEL = 12, + MatrixBPackedBFloat16INTEL = 13, + Max = 0x7fffffff, + }, + + MatrixMultiplyAccumulateOperandsMask = { + MaskNone = 0, + MatrixASignedComponentsINTEL = 0x00000001, + MatrixBSignedComponentsINTEL = 0x00000002, + MatrixCBFloat16INTEL = 0x00000004, + MatrixResultBFloat16INTEL = 0x00000008, + MatrixAPackedInt8INTEL = 0x00000010, + MatrixBPackedInt8INTEL = 0x00000020, + MatrixAPackedInt4INTEL = 0x00000040, + MatrixBPackedInt4INTEL = 0x00000080, + MatrixATF32INTEL = 0x00000100, + MatrixBTF32INTEL = 0x00000200, + MatrixAPackedFloat16INTEL = 0x00000400, + MatrixBPackedFloat16INTEL = 0x00000800, + MatrixAPackedBFloat16INTEL = 0x00001000, + MatrixBPackedBFloat16INTEL = 0x00002000, + }, + + RawAccessChainOperandsShift = { + RobustnessPerComponentNV = 0, + RobustnessPerElementNV = 1, + Max = 0x7fffffff, + }, + + RawAccessChainOperandsMask = { + MaskNone = 0, + RobustnessPerComponentNV = 0x00000001, + RobustnessPerElementNV = 0x00000002, + }, + + FPEncoding = { + BFloat16KHR = 0, + Max = 0x7fffffff, + }, + + CooperativeVectorMatrixLayout = { + RowMajorNV = 0, + ColumnMajorNV = 1, + InferencingOptimalNV = 2, + TrainingOptimalNV = 3, + Max = 0x7fffffff, + }, + + ComponentType = { + Float16NV = 0, + Float32NV = 1, + Float64NV = 2, + SignedInt8NV = 3, + SignedInt16NV = 4, + SignedInt32NV = 5, + SignedInt64NV = 6, + UnsignedInt8NV = 7, + UnsignedInt16NV = 8, + UnsignedInt32NV = 9, + UnsignedInt64NV = 10, + SignedInt8PackedNV = 1000491000, + UnsignedInt8PackedNV = 1000491001, + FloatE4M3NV = 1000491002, + FloatE5M2NV = 1000491003, + Max = 0x7fffffff, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + Max = 0x7fffffff, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py new file mode 100644 index 00000000..7f6e8e8e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py @@ -0,0 +1,2329 @@ +# Copyright (c) 2014-2024 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python, C#, D, Beef +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# - C# will use enum classes in the Specification class located in the "Spv" namespace, +# e.g.: Spv.Specification.SourceLanguage.GLSL +# - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +# - Beef will use enum classes in the Specification class located in the "Spv" namespace, +# e.g.: Spv.Specification.SourceLanguage.GLSL +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010600, + 'Revision' : 1, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + 'CPP_for_OpenCL' : 6, + 'SYCL' : 7, + 'HERO_C' : 8, + 'NZSL' : 9, + 'WGSL' : 10, + 'Slang' : 11, + 'Zig' : 12, + 'Rust' : 13, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + 'TaskNV' : 5267, + 'MeshNV' : 5268, + 'RayGenerationKHR' : 5313, + 'RayGenerationNV' : 5313, + 'IntersectionKHR' : 5314, + 'IntersectionNV' : 5314, + 'AnyHitKHR' : 5315, + 'AnyHitNV' : 5315, + 'ClosestHitKHR' : 5316, + 'ClosestHitNV' : 5316, + 'MissKHR' : 5317, + 'MissNV' : 5317, + 'CallableKHR' : 5318, + 'CallableNV' : 5318, + 'TaskEXT' : 5364, + 'MeshEXT' : 5365, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + 'PhysicalStorageBuffer64' : 5348, + 'PhysicalStorageBuffer64EXT' : 5348, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + 'Vulkan' : 3, + 'VulkanKHR' : 3, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'SubgroupsPerWorkgroupId' : 37, + 'LocalSizeId' : 38, + 'LocalSizeHintId' : 39, + 'NonCoherentColorAttachmentReadEXT' : 4169, + 'NonCoherentDepthAttachmentReadEXT' : 4170, + 'NonCoherentStencilAttachmentReadEXT' : 4171, + 'SubgroupUniformControlFlowKHR' : 4421, + 'PostDepthCoverage' : 4446, + 'DenormPreserve' : 4459, + 'DenormFlushToZero' : 4460, + 'SignedZeroInfNanPreserve' : 4461, + 'RoundingModeRTE' : 4462, + 'RoundingModeRTZ' : 4463, + 'NonCoherentTileAttachmentReadQCOM' : 4489, + 'TileShadingRateQCOM' : 4490, + 'EarlyAndLateFragmentTestsAMD' : 5017, + 'StencilRefReplacingEXT' : 5027, + 'CoalescingAMDX' : 5069, + 'IsApiEntryAMDX' : 5070, + 'MaxNodeRecursionAMDX' : 5071, + 'StaticNumWorkgroupsAMDX' : 5072, + 'ShaderIndexAMDX' : 5073, + 'MaxNumWorkgroupsAMDX' : 5077, + 'StencilRefUnchangedFrontAMD' : 5079, + 'StencilRefGreaterFrontAMD' : 5080, + 'StencilRefLessFrontAMD' : 5081, + 'StencilRefUnchangedBackAMD' : 5082, + 'StencilRefGreaterBackAMD' : 5083, + 'StencilRefLessBackAMD' : 5084, + 'QuadDerivativesKHR' : 5088, + 'RequireFullQuadsKHR' : 5089, + 'SharesInputWithAMDX' : 5102, + 'OutputLinesEXT' : 5269, + 'OutputLinesNV' : 5269, + 'OutputPrimitivesEXT' : 5270, + 'OutputPrimitivesNV' : 5270, + 'DerivativeGroupQuadsKHR' : 5289, + 'DerivativeGroupQuadsNV' : 5289, + 'DerivativeGroupLinearKHR' : 5290, + 'DerivativeGroupLinearNV' : 5290, + 'OutputTrianglesEXT' : 5298, + 'OutputTrianglesNV' : 5298, + 'PixelInterlockOrderedEXT' : 5366, + 'PixelInterlockUnorderedEXT' : 5367, + 'SampleInterlockOrderedEXT' : 5368, + 'SampleInterlockUnorderedEXT' : 5369, + 'ShadingRateInterlockOrderedEXT' : 5370, + 'ShadingRateInterlockUnorderedEXT' : 5371, + 'SharedLocalMemorySizeINTEL' : 5618, + 'RoundingModeRTPINTEL' : 5620, + 'RoundingModeRTNINTEL' : 5621, + 'FloatingPointModeALTINTEL' : 5622, + 'FloatingPointModeIEEEINTEL' : 5623, + 'MaxWorkgroupSizeINTEL' : 5893, + 'MaxWorkDimINTEL' : 5894, + 'NoGlobalOffsetINTEL' : 5895, + 'NumSIMDWorkitemsINTEL' : 5896, + 'SchedulerTargetFmaxMhzINTEL' : 5903, + 'MaximallyReconvergesKHR' : 6023, + 'FPFastMathDefault' : 6028, + 'StreamingInterfaceINTEL' : 6154, + 'RegisterMapInterfaceINTEL' : 6160, + 'NamedBarrierCountINTEL' : 6417, + 'MaximumRegistersINTEL' : 6461, + 'MaximumRegistersIdINTEL' : 6462, + 'NamedMaximumRegistersINTEL' : 6463, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + 'TileImageEXT' : 4172, + 'TileAttachmentQCOM' : 4491, + 'NodePayloadAMDX' : 5068, + 'CallableDataKHR' : 5328, + 'CallableDataNV' : 5328, + 'IncomingCallableDataKHR' : 5329, + 'IncomingCallableDataNV' : 5329, + 'RayPayloadKHR' : 5338, + 'RayPayloadNV' : 5338, + 'HitAttributeKHR' : 5339, + 'HitAttributeNV' : 5339, + 'IncomingRayPayloadKHR' : 5342, + 'IncomingRayPayloadNV' : 5342, + 'ShaderRecordBufferKHR' : 5343, + 'ShaderRecordBufferNV' : 5343, + 'PhysicalStorageBuffer' : 5349, + 'PhysicalStorageBufferEXT' : 5349, + 'HitObjectAttributeNV' : 5385, + 'TaskPayloadWorkgroupEXT' : 5402, + 'CodeSectionINTEL' : 5605, + 'DeviceOnlyINTEL' : 5936, + 'HostOnlyINTEL' : 5937, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + 'TileImageDataEXT' : 4173, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + 'R64ui' : 40, + 'R64i' : 41, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + 'UnormInt10X6EXT' : 17, + 'UnsignedIntRaw10EXT' : 19, + 'UnsignedIntRaw12EXT' : 20, + 'UnormInt2_101010EXT' : 21, + 'UnsignedInt10X6EXT' : 22, + 'UnsignedInt12X4EXT' : 23, + 'UnsignedInt14X2EXT' : 24, + 'UnormInt12X4EXT' : 25, + 'UnormInt14X2EXT' : 26, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + 'MakeTexelAvailable' : 8, + 'MakeTexelAvailableKHR' : 8, + 'MakeTexelVisible' : 9, + 'MakeTexelVisibleKHR' : 9, + 'NonPrivateTexel' : 10, + 'NonPrivateTexelKHR' : 10, + 'VolatileTexel' : 11, + 'VolatileTexelKHR' : 11, + 'SignExtend' : 12, + 'ZeroExtend' : 13, + 'Nontemporal' : 14, + 'Offsets' : 16, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + 'MakeTexelAvailable' : 0x00000100, + 'MakeTexelAvailableKHR' : 0x00000100, + 'MakeTexelVisible' : 0x00000200, + 'MakeTexelVisibleKHR' : 0x00000200, + 'NonPrivateTexel' : 0x00000400, + 'NonPrivateTexelKHR' : 0x00000400, + 'VolatileTexel' : 0x00000800, + 'VolatileTexelKHR' : 0x00000800, + 'SignExtend' : 0x00001000, + 'ZeroExtend' : 0x00002000, + 'Nontemporal' : 0x00004000, + 'Offsets' : 0x00010000, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + 'AllowContract' : 16, + 'AllowContractFastINTEL' : 16, + 'AllowReassoc' : 17, + 'AllowReassocINTEL' : 17, + 'AllowTransform' : 18, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + 'AllowContract' : 0x00010000, + 'AllowContractFastINTEL' : 0x00010000, + 'AllowReassoc' : 0x00020000, + 'AllowReassocINTEL' : 0x00020000, + 'AllowTransform' : 0x00040000, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + 'LinkOnceODR' : 2, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + 'RuntimeAlignedINTEL' : 5940, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'UniformId' : 27, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'AlignmentId' : 46, + 'MaxByteOffsetId' : 47, + 'NoSignedWrap' : 4469, + 'NoUnsignedWrap' : 4470, + 'WeightTextureQCOM' : 4487, + 'BlockMatchTextureQCOM' : 4488, + 'BlockMatchSamplerQCOM' : 4499, + 'ExplicitInterpAMD' : 4999, + 'NodeSharesPayloadLimitsWithAMDX' : 5019, + 'NodeMaxPayloadsAMDX' : 5020, + 'TrackFinishWritingAMDX' : 5078, + 'PayloadNodeNameAMDX' : 5091, + 'PayloadNodeBaseIndexAMDX' : 5098, + 'PayloadNodeSparseArrayAMDX' : 5099, + 'PayloadNodeArraySizeAMDX' : 5100, + 'PayloadDispatchIndirectAMDX' : 5105, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'PerPrimitiveEXT' : 5271, + 'PerPrimitiveNV' : 5271, + 'PerViewNV' : 5272, + 'PerTaskNV' : 5273, + 'PerVertexKHR' : 5285, + 'PerVertexNV' : 5285, + 'NonUniform' : 5300, + 'NonUniformEXT' : 5300, + 'RestrictPointer' : 5355, + 'RestrictPointerEXT' : 5355, + 'AliasedPointer' : 5356, + 'AliasedPointerEXT' : 5356, + 'HitObjectShaderRecordBufferNV' : 5386, + 'BindlessSamplerNV' : 5398, + 'BindlessImageNV' : 5399, + 'BoundSamplerNV' : 5400, + 'BoundImageNV' : 5401, + 'SIMTCallINTEL' : 5599, + 'ReferencedIndirectlyINTEL' : 5602, + 'ClobberINTEL' : 5607, + 'SideEffectsINTEL' : 5608, + 'VectorComputeVariableINTEL' : 5624, + 'FuncParamIOKindINTEL' : 5625, + 'VectorComputeFunctionINTEL' : 5626, + 'StackCallINTEL' : 5627, + 'GlobalVariableOffsetINTEL' : 5628, + 'CounterBuffer' : 5634, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + 'UserSemantic' : 5635, + 'UserTypeGOOGLE' : 5636, + 'FunctionRoundingModeINTEL' : 5822, + 'FunctionDenormModeINTEL' : 5823, + 'RegisterINTEL' : 5825, + 'MemoryINTEL' : 5826, + 'NumbanksINTEL' : 5827, + 'BankwidthINTEL' : 5828, + 'MaxPrivateCopiesINTEL' : 5829, + 'SinglepumpINTEL' : 5830, + 'DoublepumpINTEL' : 5831, + 'MaxReplicatesINTEL' : 5832, + 'SimpleDualPortINTEL' : 5833, + 'MergeINTEL' : 5834, + 'BankBitsINTEL' : 5835, + 'ForcePow2DepthINTEL' : 5836, + 'StridesizeINTEL' : 5883, + 'WordsizeINTEL' : 5884, + 'TrueDualPortINTEL' : 5885, + 'BurstCoalesceINTEL' : 5899, + 'CacheSizeINTEL' : 5900, + 'DontStaticallyCoalesceINTEL' : 5901, + 'PrefetchINTEL' : 5902, + 'StallEnableINTEL' : 5905, + 'FuseLoopsInFunctionINTEL' : 5907, + 'MathOpDSPModeINTEL' : 5909, + 'AliasScopeINTEL' : 5914, + 'NoAliasINTEL' : 5915, + 'InitiationIntervalINTEL' : 5917, + 'MaxConcurrencyINTEL' : 5918, + 'PipelineEnableINTEL' : 5919, + 'BufferLocationINTEL' : 5921, + 'IOPipeStorageINTEL' : 5944, + 'FunctionFloatingPointModeINTEL' : 6080, + 'SingleElementVectorINTEL' : 6085, + 'VectorComputeCallableFunctionINTEL' : 6087, + 'MediaBlockIOINTEL' : 6140, + 'StallFreeINTEL' : 6151, + 'FPMaxErrorDecorationINTEL' : 6170, + 'LatencyControlLabelINTEL' : 6172, + 'LatencyControlConstraintINTEL' : 6173, + 'ConduitKernelArgumentINTEL' : 6175, + 'RegisterMapKernelArgumentINTEL' : 6176, + 'MMHostInterfaceAddressWidthINTEL' : 6177, + 'MMHostInterfaceDataWidthINTEL' : 6178, + 'MMHostInterfaceLatencyINTEL' : 6179, + 'MMHostInterfaceReadWriteModeINTEL' : 6180, + 'MMHostInterfaceMaxBurstINTEL' : 6181, + 'MMHostInterfaceWaitRequestINTEL' : 6182, + 'StableKernelArgumentINTEL' : 6183, + 'HostAccessINTEL' : 6188, + 'InitModeINTEL' : 6190, + 'ImplementInRegisterMapINTEL' : 6191, + 'CacheControlLoadINTEL' : 6442, + 'CacheControlStoreINTEL' : 6443, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'CoreIDARM' : 4160, + 'CoreCountARM' : 4161, + 'CoreMaxIDARM' : 4162, + 'WarpIDARM' : 4163, + 'WarpMaxIDARM' : 4164, + 'SubgroupEqMask' : 4416, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMask' : 4417, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMask' : 4418, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMask' : 4419, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMask' : 4420, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'PrimitiveShadingRateKHR' : 4432, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'ShadingRateKHR' : 4444, + 'TileOffsetQCOM' : 4492, + 'TileDimensionQCOM' : 4493, + 'TileApronSizeQCOM' : 4494, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'RemainingRecursionLevelsAMDX' : 5021, + 'ShaderIndexAMDX' : 5073, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + 'FullyCoveredEXT' : 5264, + 'TaskCountNV' : 5274, + 'PrimitiveCountNV' : 5275, + 'PrimitiveIndicesNV' : 5276, + 'ClipDistancePerViewNV' : 5277, + 'CullDistancePerViewNV' : 5278, + 'LayerPerViewNV' : 5279, + 'MeshViewCountNV' : 5280, + 'MeshViewIndicesNV' : 5281, + 'BaryCoordKHR' : 5286, + 'BaryCoordNV' : 5286, + 'BaryCoordNoPerspKHR' : 5287, + 'BaryCoordNoPerspNV' : 5287, + 'FragSizeEXT' : 5292, + 'FragmentSizeNV' : 5292, + 'FragInvocationCountEXT' : 5293, + 'InvocationsPerPixelNV' : 5293, + 'PrimitivePointIndicesEXT' : 5294, + 'PrimitiveLineIndicesEXT' : 5295, + 'PrimitiveTriangleIndicesEXT' : 5296, + 'CullPrimitiveEXT' : 5299, + 'LaunchIdKHR' : 5319, + 'LaunchIdNV' : 5319, + 'LaunchSizeKHR' : 5320, + 'LaunchSizeNV' : 5320, + 'WorldRayOriginKHR' : 5321, + 'WorldRayOriginNV' : 5321, + 'WorldRayDirectionKHR' : 5322, + 'WorldRayDirectionNV' : 5322, + 'ObjectRayOriginKHR' : 5323, + 'ObjectRayOriginNV' : 5323, + 'ObjectRayDirectionKHR' : 5324, + 'ObjectRayDirectionNV' : 5324, + 'RayTminKHR' : 5325, + 'RayTminNV' : 5325, + 'RayTmaxKHR' : 5326, + 'RayTmaxNV' : 5326, + 'InstanceCustomIndexKHR' : 5327, + 'InstanceCustomIndexNV' : 5327, + 'ObjectToWorldKHR' : 5330, + 'ObjectToWorldNV' : 5330, + 'WorldToObjectKHR' : 5331, + 'WorldToObjectNV' : 5331, + 'HitTNV' : 5332, + 'HitKindKHR' : 5333, + 'HitKindNV' : 5333, + 'CurrentRayTimeNV' : 5334, + 'HitTriangleVertexPositionsKHR' : 5335, + 'HitMicroTriangleVertexPositionsNV' : 5337, + 'HitMicroTriangleVertexBarycentricsNV' : 5344, + 'IncomingRayFlagsKHR' : 5351, + 'IncomingRayFlagsNV' : 5351, + 'RayGeometryIndexKHR' : 5352, + 'HitIsSphereNV' : 5359, + 'HitIsLSSNV' : 5360, + 'HitSpherePositionNV' : 5361, + 'WarpsPerSMNV' : 5374, + 'SMCountNV' : 5375, + 'WarpIDNV' : 5376, + 'SMIDNV' : 5377, + 'HitLSSPositionsNV' : 5396, + 'HitKindFrontFacingMicroTriangleNV' : 5405, + 'HitKindBackFacingMicroTriangleNV' : 5406, + 'HitSphereRadiusNV' : 5420, + 'HitLSSRadiiNV' : 5421, + 'ClusterIDNV' : 5436, + 'CullMaskKHR' : 6021, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + 'MinIterations' : 4, + 'MaxIterations' : 5, + 'IterationMultiple' : 6, + 'PeelCount' : 7, + 'PartialCount' : 8, + 'InitiationIntervalINTEL' : 16, + 'MaxConcurrencyINTEL' : 17, + 'DependencyArrayINTEL' : 18, + 'PipelineEnableINTEL' : 19, + 'LoopCoalesceINTEL' : 20, + 'MaxInterleavingINTEL' : 21, + 'SpeculatedIterationsINTEL' : 22, + 'NoFusionINTEL' : 23, + 'LoopCountINTEL' : 24, + 'MaxReinvocationDelayINTEL' : 25, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + 'MinIterations' : 0x00000010, + 'MaxIterations' : 0x00000020, + 'IterationMultiple' : 0x00000040, + 'PeelCount' : 0x00000080, + 'PartialCount' : 0x00000100, + 'InitiationIntervalINTEL' : 0x00010000, + 'MaxConcurrencyINTEL' : 0x00020000, + 'DependencyArrayINTEL' : 0x00040000, + 'PipelineEnableINTEL' : 0x00080000, + 'LoopCoalesceINTEL' : 0x00100000, + 'MaxInterleavingINTEL' : 0x00200000, + 'SpeculatedIterationsINTEL' : 0x00400000, + 'NoFusionINTEL' : 0x00800000, + 'LoopCountINTEL' : 0x01000000, + 'MaxReinvocationDelayINTEL' : 0x02000000, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + 'OptNoneEXT' : 16, + 'OptNoneINTEL' : 16, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + 'OptNoneEXT' : 0x00010000, + 'OptNoneINTEL' : 0x00010000, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + 'OutputMemory' : 12, + 'OutputMemoryKHR' : 12, + 'MakeAvailable' : 13, + 'MakeAvailableKHR' : 13, + 'MakeVisible' : 14, + 'MakeVisibleKHR' : 14, + 'Volatile' : 15, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + 'OutputMemory' : 0x00001000, + 'OutputMemoryKHR' : 0x00001000, + 'MakeAvailable' : 0x00002000, + 'MakeAvailableKHR' : 0x00002000, + 'MakeVisible' : 0x00004000, + 'MakeVisibleKHR' : 0x00004000, + 'Volatile' : 0x00008000, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + 'MakePointerAvailable' : 3, + 'MakePointerAvailableKHR' : 3, + 'MakePointerVisible' : 4, + 'MakePointerVisibleKHR' : 4, + 'NonPrivatePointer' : 5, + 'NonPrivatePointerKHR' : 5, + 'AliasScopeINTELMask' : 16, + 'NoAliasINTELMask' : 17, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + 'MakePointerAvailable' : 0x00000008, + 'MakePointerAvailableKHR' : 0x00000008, + 'MakePointerVisible' : 0x00000010, + 'MakePointerVisibleKHR' : 0x00000010, + 'NonPrivatePointer' : 0x00000020, + 'NonPrivatePointerKHR' : 0x00000020, + 'AliasScopeINTELMask' : 0x00010000, + 'NoAliasINTELMask' : 0x00020000, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + 'QueueFamily' : 5, + 'QueueFamilyKHR' : 5, + 'ShaderCallKHR' : 6, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + 'ClusteredReduce' : 3, + 'PartitionedReduceNV' : 6, + 'PartitionedInclusiveScanNV' : 7, + 'PartitionedExclusiveScanNV' : 8, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'GroupNonUniform' : 61, + 'GroupNonUniformVote' : 62, + 'GroupNonUniformArithmetic' : 63, + 'GroupNonUniformBallot' : 64, + 'GroupNonUniformShuffle' : 65, + 'GroupNonUniformShuffleRelative' : 66, + 'GroupNonUniformClustered' : 67, + 'GroupNonUniformQuad' : 68, + 'ShaderLayer' : 69, + 'ShaderViewportIndex' : 70, + 'UniformDecoration' : 71, + 'CoreBuiltinsARM' : 4165, + 'TileImageColorReadAccessEXT' : 4166, + 'TileImageDepthReadAccessEXT' : 4167, + 'TileImageStencilReadAccessEXT' : 4168, + 'CooperativeMatrixLayoutsARM' : 4201, + 'FragmentShadingRateKHR' : 4422, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'WorkgroupMemoryExplicitLayoutKHR' : 4428, + 'WorkgroupMemoryExplicitLayout8BitAccessKHR' : 4429, + 'WorkgroupMemoryExplicitLayout16BitAccessKHR' : 4430, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'StorageBuffer8BitAccess' : 4448, + 'UniformAndStorageBuffer8BitAccess' : 4449, + 'StoragePushConstant8' : 4450, + 'DenormPreserve' : 4464, + 'DenormFlushToZero' : 4465, + 'SignedZeroInfNanPreserve' : 4466, + 'RoundingModeRTE' : 4467, + 'RoundingModeRTZ' : 4468, + 'RayQueryProvisionalKHR' : 4471, + 'RayQueryKHR' : 4472, + 'UntypedPointersKHR' : 4473, + 'RayTraversalPrimitiveCullingKHR' : 4478, + 'RayTracingKHR' : 4479, + 'TextureSampleWeightedQCOM' : 4484, + 'TextureBoxFilterQCOM' : 4485, + 'TextureBlockMatchQCOM' : 4486, + 'TileShadingQCOM' : 4495, + 'TextureBlockMatch2QCOM' : 4498, + 'Float16ImageAMD' : 5008, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'Int64ImageEXT' : 5016, + 'ShaderClockKHR' : 5055, + 'ShaderEnqueueAMDX' : 5067, + 'QuadControlKHR' : 5087, + 'BFloat16TypeKHR' : 5116, + 'BFloat16DotProductKHR' : 5117, + 'BFloat16CooperativeMatrixKHR' : 5118, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'FragmentFullyCoveredEXT' : 5265, + 'MeshShadingNV' : 5266, + 'ImageFootprintNV' : 5282, + 'MeshShadingEXT' : 5283, + 'FragmentBarycentricKHR' : 5284, + 'FragmentBarycentricNV' : 5284, + 'ComputeDerivativeGroupQuadsKHR' : 5288, + 'ComputeDerivativeGroupQuadsNV' : 5288, + 'FragmentDensityEXT' : 5291, + 'ShadingRateNV' : 5291, + 'GroupNonUniformPartitionedNV' : 5297, + 'ShaderNonUniform' : 5301, + 'ShaderNonUniformEXT' : 5301, + 'RuntimeDescriptorArray' : 5302, + 'RuntimeDescriptorArrayEXT' : 5302, + 'InputAttachmentArrayDynamicIndexing' : 5303, + 'InputAttachmentArrayDynamicIndexingEXT' : 5303, + 'UniformTexelBufferArrayDynamicIndexing' : 5304, + 'UniformTexelBufferArrayDynamicIndexingEXT' : 5304, + 'StorageTexelBufferArrayDynamicIndexing' : 5305, + 'StorageTexelBufferArrayDynamicIndexingEXT' : 5305, + 'UniformBufferArrayNonUniformIndexing' : 5306, + 'UniformBufferArrayNonUniformIndexingEXT' : 5306, + 'SampledImageArrayNonUniformIndexing' : 5307, + 'SampledImageArrayNonUniformIndexingEXT' : 5307, + 'StorageBufferArrayNonUniformIndexing' : 5308, + 'StorageBufferArrayNonUniformIndexingEXT' : 5308, + 'StorageImageArrayNonUniformIndexing' : 5309, + 'StorageImageArrayNonUniformIndexingEXT' : 5309, + 'InputAttachmentArrayNonUniformIndexing' : 5310, + 'InputAttachmentArrayNonUniformIndexingEXT' : 5310, + 'UniformTexelBufferArrayNonUniformIndexing' : 5311, + 'UniformTexelBufferArrayNonUniformIndexingEXT' : 5311, + 'StorageTexelBufferArrayNonUniformIndexing' : 5312, + 'StorageTexelBufferArrayNonUniformIndexingEXT' : 5312, + 'RayTracingPositionFetchKHR' : 5336, + 'RayTracingNV' : 5340, + 'RayTracingMotionBlurNV' : 5341, + 'VulkanMemoryModel' : 5345, + 'VulkanMemoryModelKHR' : 5345, + 'VulkanMemoryModelDeviceScope' : 5346, + 'VulkanMemoryModelDeviceScopeKHR' : 5346, + 'PhysicalStorageBufferAddresses' : 5347, + 'PhysicalStorageBufferAddressesEXT' : 5347, + 'ComputeDerivativeGroupLinearKHR' : 5350, + 'ComputeDerivativeGroupLinearNV' : 5350, + 'RayTracingProvisionalKHR' : 5353, + 'CooperativeMatrixNV' : 5357, + 'FragmentShaderSampleInterlockEXT' : 5363, + 'FragmentShaderShadingRateInterlockEXT' : 5372, + 'ShaderSMBuiltinsNV' : 5373, + 'FragmentShaderPixelInterlockEXT' : 5378, + 'DemoteToHelperInvocation' : 5379, + 'DemoteToHelperInvocationEXT' : 5379, + 'DisplacementMicromapNV' : 5380, + 'RayTracingOpacityMicromapEXT' : 5381, + 'ShaderInvocationReorderNV' : 5383, + 'BindlessTextureNV' : 5390, + 'RayQueryPositionFetchKHR' : 5391, + 'CooperativeVectorNV' : 5394, + 'AtomicFloat16VectorNV' : 5404, + 'RayTracingDisplacementMicromapNV' : 5409, + 'RawAccessChainsNV' : 5414, + 'RayTracingSpheresGeometryNV' : 5418, + 'RayTracingLinearSweptSpheresGeometryNV' : 5419, + 'CooperativeMatrixReductionsNV' : 5430, + 'CooperativeMatrixConversionsNV' : 5431, + 'CooperativeMatrixPerElementOperationsNV' : 5432, + 'CooperativeMatrixTensorAddressingNV' : 5433, + 'CooperativeMatrixBlockLoadsNV' : 5434, + 'CooperativeVectorTrainingNV' : 5435, + 'RayTracingClusterAccelerationStructureNV' : 5437, + 'TensorAddressingNV' : 5439, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + 'SubgroupImageMediaBlockIOINTEL' : 5579, + 'RoundToInfinityINTEL' : 5582, + 'FloatingPointModeINTEL' : 5583, + 'IntegerFunctions2INTEL' : 5584, + 'FunctionPointersINTEL' : 5603, + 'IndirectReferencesINTEL' : 5604, + 'AsmINTEL' : 5606, + 'AtomicFloat32MinMaxEXT' : 5612, + 'AtomicFloat64MinMaxEXT' : 5613, + 'AtomicFloat16MinMaxEXT' : 5616, + 'VectorComputeINTEL' : 5617, + 'VectorAnyINTEL' : 5619, + 'ExpectAssumeKHR' : 5629, + 'SubgroupAvcMotionEstimationINTEL' : 5696, + 'SubgroupAvcMotionEstimationIntraINTEL' : 5697, + 'SubgroupAvcMotionEstimationChromaINTEL' : 5698, + 'VariableLengthArrayINTEL' : 5817, + 'FunctionFloatControlINTEL' : 5821, + 'FPGAMemoryAttributesINTEL' : 5824, + 'FPFastMathModeINTEL' : 5837, + 'ArbitraryPrecisionIntegersINTEL' : 5844, + 'ArbitraryPrecisionFloatingPointINTEL' : 5845, + 'UnstructuredLoopControlsINTEL' : 5886, + 'FPGALoopControlsINTEL' : 5888, + 'KernelAttributesINTEL' : 5892, + 'FPGAKernelAttributesINTEL' : 5897, + 'FPGAMemoryAccessesINTEL' : 5898, + 'FPGAClusterAttributesINTEL' : 5904, + 'LoopFuseINTEL' : 5906, + 'FPGADSPControlINTEL' : 5908, + 'MemoryAccessAliasingINTEL' : 5910, + 'FPGAInvocationPipeliningAttributesINTEL' : 5916, + 'FPGABufferLocationINTEL' : 5920, + 'ArbitraryPrecisionFixedPointINTEL' : 5922, + 'USMStorageClassesINTEL' : 5935, + 'RuntimeAlignedAttributeINTEL' : 5939, + 'IOPipesINTEL' : 5943, + 'BlockingPipesINTEL' : 5945, + 'FPGARegINTEL' : 5948, + 'DotProductInputAll' : 6016, + 'DotProductInputAllKHR' : 6016, + 'DotProductInput4x8Bit' : 6017, + 'DotProductInput4x8BitKHR' : 6017, + 'DotProductInput4x8BitPacked' : 6018, + 'DotProductInput4x8BitPackedKHR' : 6018, + 'DotProduct' : 6019, + 'DotProductKHR' : 6019, + 'RayCullMaskKHR' : 6020, + 'CooperativeMatrixKHR' : 6022, + 'ReplicatedCompositesEXT' : 6024, + 'BitInstructions' : 6025, + 'GroupNonUniformRotateKHR' : 6026, + 'FloatControls2' : 6029, + 'AtomicFloat32AddEXT' : 6033, + 'AtomicFloat64AddEXT' : 6034, + 'LongCompositesINTEL' : 6089, + 'OptNoneEXT' : 6094, + 'OptNoneINTEL' : 6094, + 'AtomicFloat16AddEXT' : 6095, + 'DebugInfoModuleINTEL' : 6114, + 'BFloat16ConversionINTEL' : 6115, + 'SplitBarrierINTEL' : 6141, + 'ArithmeticFenceEXT' : 6144, + 'FPGAClusterAttributesV2INTEL' : 6150, + 'FPGAKernelAttributesv2INTEL' : 6161, + 'TaskSequenceINTEL' : 6162, + 'FPMaxErrorINTEL' : 6169, + 'FPGALatencyControlINTEL' : 6171, + 'FPGAArgumentInterfacesINTEL' : 6174, + 'GlobalVariableHostAccessINTEL' : 6187, + 'GlobalVariableFPGADecorationsINTEL' : 6189, + 'SubgroupBufferPrefetchINTEL' : 6220, + 'Subgroup2DBlockIOINTEL' : 6228, + 'Subgroup2DBlockTransformINTEL' : 6229, + 'Subgroup2DBlockTransposeINTEL' : 6230, + 'SubgroupMatrixMultiplyAccumulateINTEL' : 6236, + 'TernaryBitwiseFunctionINTEL' : 6241, + 'GroupUniformArithmeticKHR' : 6400, + 'TensorFloat32RoundingINTEL' : 6425, + 'MaskedGatherScatterINTEL' : 6427, + 'CacheControlsINTEL' : 6441, + 'RegisterLimitsINTEL' : 6460, + }, + + 'RayFlagsShift' : { + 'OpaqueKHR' : 0, + 'NoOpaqueKHR' : 1, + 'TerminateOnFirstHitKHR' : 2, + 'SkipClosestHitShaderKHR' : 3, + 'CullBackFacingTrianglesKHR' : 4, + 'CullFrontFacingTrianglesKHR' : 5, + 'CullOpaqueKHR' : 6, + 'CullNoOpaqueKHR' : 7, + 'SkipBuiltinPrimitivesNV' : 8, + 'SkipTrianglesKHR' : 8, + 'SkipAABBsKHR' : 9, + 'ForceOpacityMicromap2StateEXT' : 10, + }, + + 'RayFlagsMask' : { + 'MaskNone' : 0, + 'OpaqueKHR' : 0x00000001, + 'NoOpaqueKHR' : 0x00000002, + 'TerminateOnFirstHitKHR' : 0x00000004, + 'SkipClosestHitShaderKHR' : 0x00000008, + 'CullBackFacingTrianglesKHR' : 0x00000010, + 'CullFrontFacingTrianglesKHR' : 0x00000020, + 'CullOpaqueKHR' : 0x00000040, + 'CullNoOpaqueKHR' : 0x00000080, + 'SkipBuiltinPrimitivesNV' : 0x00000100, + 'SkipTrianglesKHR' : 0x00000100, + 'SkipAABBsKHR' : 0x00000200, + 'ForceOpacityMicromap2StateEXT' : 0x00000400, + }, + + 'RayQueryIntersection' : { + 'RayQueryCandidateIntersectionKHR' : 0, + 'RayQueryCommittedIntersectionKHR' : 1, + }, + + 'RayQueryCommittedIntersectionType' : { + 'RayQueryCommittedIntersectionNoneKHR' : 0, + 'RayQueryCommittedIntersectionTriangleKHR' : 1, + 'RayQueryCommittedIntersectionGeneratedKHR' : 2, + }, + + 'RayQueryCandidateIntersectionType' : { + 'RayQueryCandidateIntersectionTriangleKHR' : 0, + 'RayQueryCandidateIntersectionAABBKHR' : 1, + }, + + 'FragmentShadingRateShift' : { + 'Vertical2Pixels' : 0, + 'Vertical4Pixels' : 1, + 'Horizontal2Pixels' : 2, + 'Horizontal4Pixels' : 3, + }, + + 'FragmentShadingRateMask' : { + 'MaskNone' : 0, + 'Vertical2Pixels' : 0x00000001, + 'Vertical4Pixels' : 0x00000002, + 'Horizontal2Pixels' : 0x00000004, + 'Horizontal4Pixels' : 0x00000008, + }, + + 'FPDenormMode' : { + 'Preserve' : 0, + 'FlushToZero' : 1, + }, + + 'FPOperationMode' : { + 'IEEE' : 0, + 'ALT' : 1, + }, + + 'QuantizationModes' : { + 'TRN' : 0, + 'TRN_ZERO' : 1, + 'RND' : 2, + 'RND_ZERO' : 3, + 'RND_INF' : 4, + 'RND_MIN_INF' : 5, + 'RND_CONV' : 6, + 'RND_CONV_ODD' : 7, + }, + + 'OverflowModes' : { + 'WRAP' : 0, + 'SAT' : 1, + 'SAT_ZERO' : 2, + 'SAT_SYM' : 3, + }, + + 'PackedVectorFormat' : { + 'PackedVectorFormat4x8Bit' : 0, + 'PackedVectorFormat4x8BitKHR' : 0, + }, + + 'CooperativeMatrixOperandsShift' : { + 'MatrixASignedComponentsKHR' : 0, + 'MatrixBSignedComponentsKHR' : 1, + 'MatrixCSignedComponentsKHR' : 2, + 'MatrixResultSignedComponentsKHR' : 3, + 'SaturatingAccumulationKHR' : 4, + }, + + 'CooperativeMatrixOperandsMask' : { + 'MaskNone' : 0, + 'MatrixASignedComponentsKHR' : 0x00000001, + 'MatrixBSignedComponentsKHR' : 0x00000002, + 'MatrixCSignedComponentsKHR' : 0x00000004, + 'MatrixResultSignedComponentsKHR' : 0x00000008, + 'SaturatingAccumulationKHR' : 0x00000010, + }, + + 'CooperativeMatrixLayout' : { + 'RowMajorKHR' : 0, + 'ColumnMajorKHR' : 1, + 'RowBlockedInterleavedARM' : 4202, + 'ColumnBlockedInterleavedARM' : 4203, + }, + + 'CooperativeMatrixUse' : { + 'MatrixAKHR' : 0, + 'MatrixBKHR' : 1, + 'MatrixAccumulatorKHR' : 2, + }, + + 'CooperativeMatrixReduceShift' : { + 'Row' : 0, + 'Column' : 1, + 'CooperativeMatrixReduce2x2' : 2, + }, + + 'CooperativeMatrixReduceMask' : { + 'MaskNone' : 0, + 'Row' : 0x00000001, + 'Column' : 0x00000002, + 'CooperativeMatrixReduce2x2' : 0x00000004, + }, + + 'TensorClampMode' : { + 'Undefined' : 0, + 'Constant' : 1, + 'ClampToEdge' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'TensorAddressingOperandsShift' : { + 'TensorView' : 0, + 'DecodeFunc' : 1, + }, + + 'TensorAddressingOperandsMask' : { + 'MaskNone' : 0, + 'TensorView' : 0x00000001, + 'DecodeFunc' : 0x00000002, + }, + + 'InitializationModeQualifier' : { + 'InitOnDeviceReprogramINTEL' : 0, + 'InitOnDeviceResetINTEL' : 1, + }, + + 'HostAccessQualifier' : { + 'NoneINTEL' : 0, + 'ReadINTEL' : 1, + 'WriteINTEL' : 2, + 'ReadWriteINTEL' : 3, + }, + + 'LoadCacheControl' : { + 'UncachedINTEL' : 0, + 'CachedINTEL' : 1, + 'StreamingINTEL' : 2, + 'InvalidateAfterReadINTEL' : 3, + 'ConstCachedINTEL' : 4, + }, + + 'StoreCacheControl' : { + 'UncachedINTEL' : 0, + 'WriteThroughINTEL' : 1, + 'WriteBackINTEL' : 2, + 'StreamingINTEL' : 3, + }, + + 'NamedMaximumNumberOfRegisters' : { + 'AutoINTEL' : 0, + }, + + 'MatrixMultiplyAccumulateOperandsShift' : { + 'MatrixASignedComponentsINTEL' : 0, + 'MatrixBSignedComponentsINTEL' : 1, + 'MatrixCBFloat16INTEL' : 2, + 'MatrixResultBFloat16INTEL' : 3, + 'MatrixAPackedInt8INTEL' : 4, + 'MatrixBPackedInt8INTEL' : 5, + 'MatrixAPackedInt4INTEL' : 6, + 'MatrixBPackedInt4INTEL' : 7, + 'MatrixATF32INTEL' : 8, + 'MatrixBTF32INTEL' : 9, + 'MatrixAPackedFloat16INTEL' : 10, + 'MatrixBPackedFloat16INTEL' : 11, + 'MatrixAPackedBFloat16INTEL' : 12, + 'MatrixBPackedBFloat16INTEL' : 13, + }, + + 'MatrixMultiplyAccumulateOperandsMask' : { + 'MaskNone' : 0, + 'MatrixASignedComponentsINTEL' : 0x00000001, + 'MatrixBSignedComponentsINTEL' : 0x00000002, + 'MatrixCBFloat16INTEL' : 0x00000004, + 'MatrixResultBFloat16INTEL' : 0x00000008, + 'MatrixAPackedInt8INTEL' : 0x00000010, + 'MatrixBPackedInt8INTEL' : 0x00000020, + 'MatrixAPackedInt4INTEL' : 0x00000040, + 'MatrixBPackedInt4INTEL' : 0x00000080, + 'MatrixATF32INTEL' : 0x00000100, + 'MatrixBTF32INTEL' : 0x00000200, + 'MatrixAPackedFloat16INTEL' : 0x00000400, + 'MatrixBPackedFloat16INTEL' : 0x00000800, + 'MatrixAPackedBFloat16INTEL' : 0x00001000, + 'MatrixBPackedBFloat16INTEL' : 0x00002000, + }, + + 'RawAccessChainOperandsShift' : { + 'RobustnessPerComponentNV' : 0, + 'RobustnessPerElementNV' : 1, + }, + + 'RawAccessChainOperandsMask' : { + 'MaskNone' : 0, + 'RobustnessPerComponentNV' : 0x00000001, + 'RobustnessPerElementNV' : 0x00000002, + }, + + 'FPEncoding' : { + 'BFloat16KHR' : 0, + }, + + 'CooperativeVectorMatrixLayout' : { + 'RowMajorNV' : 0, + 'ColumnMajorNV' : 1, + 'InferencingOptimalNV' : 2, + 'TrainingOptimalNV' : 3, + }, + + 'ComponentType' : { + 'Float16NV' : 0, + 'Float32NV' : 1, + 'Float64NV' : 2, + 'SignedInt8NV' : 3, + 'SignedInt16NV' : 4, + 'SignedInt32NV' : 5, + 'SignedInt64NV' : 6, + 'UnsignedInt8NV' : 7, + 'UnsignedInt16NV' : 8, + 'UnsignedInt32NV' : 9, + 'UnsignedInt64NV' : 10, + 'SignedInt8PackedNV' : 1000491000, + 'UnsignedInt8PackedNV' : 1000491001, + 'FloatE4M3NV' : 1000491002, + 'FloatE5M2NV' : 1000491003, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpExecutionModeId' : 331, + 'OpDecorateId' : 332, + 'OpGroupNonUniformElect' : 333, + 'OpGroupNonUniformAll' : 334, + 'OpGroupNonUniformAny' : 335, + 'OpGroupNonUniformAllEqual' : 336, + 'OpGroupNonUniformBroadcast' : 337, + 'OpGroupNonUniformBroadcastFirst' : 338, + 'OpGroupNonUniformBallot' : 339, + 'OpGroupNonUniformInverseBallot' : 340, + 'OpGroupNonUniformBallotBitExtract' : 341, + 'OpGroupNonUniformBallotBitCount' : 342, + 'OpGroupNonUniformBallotFindLSB' : 343, + 'OpGroupNonUniformBallotFindMSB' : 344, + 'OpGroupNonUniformShuffle' : 345, + 'OpGroupNonUniformShuffleXor' : 346, + 'OpGroupNonUniformShuffleUp' : 347, + 'OpGroupNonUniformShuffleDown' : 348, + 'OpGroupNonUniformIAdd' : 349, + 'OpGroupNonUniformFAdd' : 350, + 'OpGroupNonUniformIMul' : 351, + 'OpGroupNonUniformFMul' : 352, + 'OpGroupNonUniformSMin' : 353, + 'OpGroupNonUniformUMin' : 354, + 'OpGroupNonUniformFMin' : 355, + 'OpGroupNonUniformSMax' : 356, + 'OpGroupNonUniformUMax' : 357, + 'OpGroupNonUniformFMax' : 358, + 'OpGroupNonUniformBitwiseAnd' : 359, + 'OpGroupNonUniformBitwiseOr' : 360, + 'OpGroupNonUniformBitwiseXor' : 361, + 'OpGroupNonUniformLogicalAnd' : 362, + 'OpGroupNonUniformLogicalOr' : 363, + 'OpGroupNonUniformLogicalXor' : 364, + 'OpGroupNonUniformQuadBroadcast' : 365, + 'OpGroupNonUniformQuadSwap' : 366, + 'OpCopyLogical' : 400, + 'OpPtrEqual' : 401, + 'OpPtrNotEqual' : 402, + 'OpPtrDiff' : 403, + 'OpColorAttachmentReadEXT' : 4160, + 'OpDepthAttachmentReadEXT' : 4161, + 'OpStencilAttachmentReadEXT' : 4162, + 'OpTerminateInvocation' : 4416, + 'OpTypeUntypedPointerKHR' : 4417, + 'OpUntypedVariableKHR' : 4418, + 'OpUntypedAccessChainKHR' : 4419, + 'OpUntypedInBoundsAccessChainKHR' : 4420, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpUntypedPtrAccessChainKHR' : 4423, + 'OpUntypedInBoundsPtrAccessChainKHR' : 4424, + 'OpUntypedArrayLengthKHR' : 4425, + 'OpUntypedPrefetchKHR' : 4426, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpGroupNonUniformRotateKHR' : 4431, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpExtInstWithForwardRefsKHR' : 4433, + 'OpTraceRayKHR' : 4445, + 'OpExecuteCallableKHR' : 4446, + 'OpConvertUToAccelerationStructureKHR' : 4447, + 'OpIgnoreIntersectionKHR' : 4448, + 'OpTerminateRayKHR' : 4449, + 'OpSDot' : 4450, + 'OpSDotKHR' : 4450, + 'OpUDot' : 4451, + 'OpUDotKHR' : 4451, + 'OpSUDot' : 4452, + 'OpSUDotKHR' : 4452, + 'OpSDotAccSat' : 4453, + 'OpSDotAccSatKHR' : 4453, + 'OpUDotAccSat' : 4454, + 'OpUDotAccSatKHR' : 4454, + 'OpSUDotAccSat' : 4455, + 'OpSUDotAccSatKHR' : 4455, + 'OpTypeCooperativeMatrixKHR' : 4456, + 'OpCooperativeMatrixLoadKHR' : 4457, + 'OpCooperativeMatrixStoreKHR' : 4458, + 'OpCooperativeMatrixMulAddKHR' : 4459, + 'OpCooperativeMatrixLengthKHR' : 4460, + 'OpConstantCompositeReplicateEXT' : 4461, + 'OpSpecConstantCompositeReplicateEXT' : 4462, + 'OpCompositeConstructReplicateEXT' : 4463, + 'OpTypeRayQueryKHR' : 4472, + 'OpRayQueryInitializeKHR' : 4473, + 'OpRayQueryTerminateKHR' : 4474, + 'OpRayQueryGenerateIntersectionKHR' : 4475, + 'OpRayQueryConfirmIntersectionKHR' : 4476, + 'OpRayQueryProceedKHR' : 4477, + 'OpRayQueryGetIntersectionTypeKHR' : 4479, + 'OpImageSampleWeightedQCOM' : 4480, + 'OpImageBoxFilterQCOM' : 4481, + 'OpImageBlockMatchSSDQCOM' : 4482, + 'OpImageBlockMatchSADQCOM' : 4483, + 'OpImageBlockMatchWindowSSDQCOM' : 4500, + 'OpImageBlockMatchWindowSADQCOM' : 4501, + 'OpImageBlockMatchGatherSSDQCOM' : 4502, + 'OpImageBlockMatchGatherSADQCOM' : 4503, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpReadClockKHR' : 5056, + 'OpAllocateNodePayloadsAMDX' : 5074, + 'OpEnqueueNodePayloadsAMDX' : 5075, + 'OpTypeNodePayloadArrayAMDX' : 5076, + 'OpFinishWritingNodePayloadAMDX' : 5078, + 'OpNodePayloadArrayLengthAMDX' : 5090, + 'OpIsNodePayloadValidAMDX' : 5101, + 'OpConstantStringAMDX' : 5103, + 'OpSpecConstantStringAMDX' : 5104, + 'OpGroupNonUniformQuadAllKHR' : 5110, + 'OpGroupNonUniformQuadAnyKHR' : 5111, + 'OpHitObjectRecordHitMotionNV' : 5249, + 'OpHitObjectRecordHitWithIndexMotionNV' : 5250, + 'OpHitObjectRecordMissMotionNV' : 5251, + 'OpHitObjectGetWorldToObjectNV' : 5252, + 'OpHitObjectGetObjectToWorldNV' : 5253, + 'OpHitObjectGetObjectRayDirectionNV' : 5254, + 'OpHitObjectGetObjectRayOriginNV' : 5255, + 'OpHitObjectTraceRayMotionNV' : 5256, + 'OpHitObjectGetShaderRecordBufferHandleNV' : 5257, + 'OpHitObjectGetShaderBindingTableRecordIndexNV' : 5258, + 'OpHitObjectRecordEmptyNV' : 5259, + 'OpHitObjectTraceRayNV' : 5260, + 'OpHitObjectRecordHitNV' : 5261, + 'OpHitObjectRecordHitWithIndexNV' : 5262, + 'OpHitObjectRecordMissNV' : 5263, + 'OpHitObjectExecuteShaderNV' : 5264, + 'OpHitObjectGetCurrentTimeNV' : 5265, + 'OpHitObjectGetAttributesNV' : 5266, + 'OpHitObjectGetHitKindNV' : 5267, + 'OpHitObjectGetPrimitiveIndexNV' : 5268, + 'OpHitObjectGetGeometryIndexNV' : 5269, + 'OpHitObjectGetInstanceIdNV' : 5270, + 'OpHitObjectGetInstanceCustomIndexNV' : 5271, + 'OpHitObjectGetWorldRayDirectionNV' : 5272, + 'OpHitObjectGetWorldRayOriginNV' : 5273, + 'OpHitObjectGetRayTMaxNV' : 5274, + 'OpHitObjectGetRayTMinNV' : 5275, + 'OpHitObjectIsEmptyNV' : 5276, + 'OpHitObjectIsHitNV' : 5277, + 'OpHitObjectIsMissNV' : 5278, + 'OpReorderThreadWithHitObjectNV' : 5279, + 'OpReorderThreadWithHintNV' : 5280, + 'OpTypeHitObjectNV' : 5281, + 'OpImageSampleFootprintNV' : 5283, + 'OpTypeCooperativeVectorNV' : 5288, + 'OpCooperativeVectorMatrixMulNV' : 5289, + 'OpCooperativeVectorOuterProductAccumulateNV' : 5290, + 'OpCooperativeVectorReduceSumAccumulateNV' : 5291, + 'OpCooperativeVectorMatrixMulAddNV' : 5292, + 'OpCooperativeMatrixConvertNV' : 5293, + 'OpEmitMeshTasksEXT' : 5294, + 'OpSetMeshOutputsEXT' : 5295, + 'OpGroupNonUniformPartitionNV' : 5296, + 'OpWritePackedPrimitiveIndices4x8NV' : 5299, + 'OpFetchMicroTriangleVertexPositionNV' : 5300, + 'OpFetchMicroTriangleVertexBarycentricNV' : 5301, + 'OpCooperativeVectorLoadNV' : 5302, + 'OpCooperativeVectorStoreNV' : 5303, + 'OpReportIntersectionKHR' : 5334, + 'OpReportIntersectionNV' : 5334, + 'OpIgnoreIntersectionNV' : 5335, + 'OpTerminateRayNV' : 5336, + 'OpTraceNV' : 5337, + 'OpTraceMotionNV' : 5338, + 'OpTraceRayMotionNV' : 5339, + 'OpRayQueryGetIntersectionTriangleVertexPositionsKHR' : 5340, + 'OpTypeAccelerationStructureKHR' : 5341, + 'OpTypeAccelerationStructureNV' : 5341, + 'OpExecuteCallableNV' : 5344, + 'OpRayQueryGetClusterIdNV' : 5345, + 'OpHitObjectGetClusterIdNV' : 5346, + 'OpTypeCooperativeMatrixNV' : 5358, + 'OpCooperativeMatrixLoadNV' : 5359, + 'OpCooperativeMatrixStoreNV' : 5360, + 'OpCooperativeMatrixMulAddNV' : 5361, + 'OpCooperativeMatrixLengthNV' : 5362, + 'OpBeginInvocationInterlockEXT' : 5364, + 'OpEndInvocationInterlockEXT' : 5365, + 'OpCooperativeMatrixReduceNV' : 5366, + 'OpCooperativeMatrixLoadTensorNV' : 5367, + 'OpCooperativeMatrixStoreTensorNV' : 5368, + 'OpCooperativeMatrixPerElementOpNV' : 5369, + 'OpTypeTensorLayoutNV' : 5370, + 'OpTypeTensorViewNV' : 5371, + 'OpCreateTensorLayoutNV' : 5372, + 'OpTensorLayoutSetDimensionNV' : 5373, + 'OpTensorLayoutSetStrideNV' : 5374, + 'OpTensorLayoutSliceNV' : 5375, + 'OpTensorLayoutSetClampValueNV' : 5376, + 'OpCreateTensorViewNV' : 5377, + 'OpTensorViewSetDimensionNV' : 5378, + 'OpTensorViewSetStrideNV' : 5379, + 'OpDemoteToHelperInvocation' : 5380, + 'OpDemoteToHelperInvocationEXT' : 5380, + 'OpIsHelperInvocationEXT' : 5381, + 'OpTensorViewSetClipNV' : 5382, + 'OpTensorLayoutSetBlockSizeNV' : 5384, + 'OpCooperativeMatrixTransposeNV' : 5390, + 'OpConvertUToImageNV' : 5391, + 'OpConvertUToSamplerNV' : 5392, + 'OpConvertImageToUNV' : 5393, + 'OpConvertSamplerToUNV' : 5394, + 'OpConvertUToSampledImageNV' : 5395, + 'OpConvertSampledImageToUNV' : 5396, + 'OpSamplerImageAddressingModeNV' : 5397, + 'OpRawAccessChainNV' : 5398, + 'OpRayQueryGetIntersectionSpherePositionNV' : 5427, + 'OpRayQueryGetIntersectionSphereRadiusNV' : 5428, + 'OpRayQueryGetIntersectionLSSPositionsNV' : 5429, + 'OpRayQueryGetIntersectionLSSRadiiNV' : 5430, + 'OpRayQueryGetIntersectionLSSHitValueNV' : 5431, + 'OpHitObjectGetSpherePositionNV' : 5432, + 'OpHitObjectGetSphereRadiusNV' : 5433, + 'OpHitObjectGetLSSPositionsNV' : 5434, + 'OpHitObjectGetLSSRadiiNV' : 5435, + 'OpHitObjectIsSphereHitNV' : 5436, + 'OpHitObjectIsLSSHitNV' : 5437, + 'OpRayQueryIsSphereHitNV' : 5438, + 'OpRayQueryIsLSSHitNV' : 5439, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpSubgroupImageMediaBlockReadINTEL' : 5580, + 'OpSubgroupImageMediaBlockWriteINTEL' : 5581, + 'OpUCountLeadingZerosINTEL' : 5585, + 'OpUCountTrailingZerosINTEL' : 5586, + 'OpAbsISubINTEL' : 5587, + 'OpAbsUSubINTEL' : 5588, + 'OpIAddSatINTEL' : 5589, + 'OpUAddSatINTEL' : 5590, + 'OpIAverageINTEL' : 5591, + 'OpUAverageINTEL' : 5592, + 'OpIAverageRoundedINTEL' : 5593, + 'OpUAverageRoundedINTEL' : 5594, + 'OpISubSatINTEL' : 5595, + 'OpUSubSatINTEL' : 5596, + 'OpIMul32x16INTEL' : 5597, + 'OpUMul32x16INTEL' : 5598, + 'OpConstantFunctionPointerINTEL' : 5600, + 'OpFunctionPointerCallINTEL' : 5601, + 'OpAsmTargetINTEL' : 5609, + 'OpAsmINTEL' : 5610, + 'OpAsmCallINTEL' : 5611, + 'OpAtomicFMinEXT' : 5614, + 'OpAtomicFMaxEXT' : 5615, + 'OpAssumeTrueKHR' : 5630, + 'OpExpectKHR' : 5631, + 'OpDecorateString' : 5632, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateString' : 5633, + 'OpMemberDecorateStringGOOGLE' : 5633, + 'OpVmeImageINTEL' : 5699, + 'OpTypeVmeImageINTEL' : 5700, + 'OpTypeAvcImePayloadINTEL' : 5701, + 'OpTypeAvcRefPayloadINTEL' : 5702, + 'OpTypeAvcSicPayloadINTEL' : 5703, + 'OpTypeAvcMcePayloadINTEL' : 5704, + 'OpTypeAvcMceResultINTEL' : 5705, + 'OpTypeAvcImeResultINTEL' : 5706, + 'OpTypeAvcImeResultSingleReferenceStreamoutINTEL' : 5707, + 'OpTypeAvcImeResultDualReferenceStreamoutINTEL' : 5708, + 'OpTypeAvcImeSingleReferenceStreaminINTEL' : 5709, + 'OpTypeAvcImeDualReferenceStreaminINTEL' : 5710, + 'OpTypeAvcRefResultINTEL' : 5711, + 'OpTypeAvcSicResultINTEL' : 5712, + 'OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL' : 5713, + 'OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL' : 5714, + 'OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL' : 5715, + 'OpSubgroupAvcMceSetInterShapePenaltyINTEL' : 5716, + 'OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL' : 5717, + 'OpSubgroupAvcMceSetInterDirectionPenaltyINTEL' : 5718, + 'OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL' : 5719, + 'OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL' : 5720, + 'OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL' : 5721, + 'OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL' : 5722, + 'OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL' : 5723, + 'OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL' : 5724, + 'OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL' : 5725, + 'OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL' : 5726, + 'OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL' : 5727, + 'OpSubgroupAvcMceSetAcOnlyHaarINTEL' : 5728, + 'OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL' : 5729, + 'OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL' : 5730, + 'OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL' : 5731, + 'OpSubgroupAvcMceConvertToImePayloadINTEL' : 5732, + 'OpSubgroupAvcMceConvertToImeResultINTEL' : 5733, + 'OpSubgroupAvcMceConvertToRefPayloadINTEL' : 5734, + 'OpSubgroupAvcMceConvertToRefResultINTEL' : 5735, + 'OpSubgroupAvcMceConvertToSicPayloadINTEL' : 5736, + 'OpSubgroupAvcMceConvertToSicResultINTEL' : 5737, + 'OpSubgroupAvcMceGetMotionVectorsINTEL' : 5738, + 'OpSubgroupAvcMceGetInterDistortionsINTEL' : 5739, + 'OpSubgroupAvcMceGetBestInterDistortionsINTEL' : 5740, + 'OpSubgroupAvcMceGetInterMajorShapeINTEL' : 5741, + 'OpSubgroupAvcMceGetInterMinorShapeINTEL' : 5742, + 'OpSubgroupAvcMceGetInterDirectionsINTEL' : 5743, + 'OpSubgroupAvcMceGetInterMotionVectorCountINTEL' : 5744, + 'OpSubgroupAvcMceGetInterReferenceIdsINTEL' : 5745, + 'OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL' : 5746, + 'OpSubgroupAvcImeInitializeINTEL' : 5747, + 'OpSubgroupAvcImeSetSingleReferenceINTEL' : 5748, + 'OpSubgroupAvcImeSetDualReferenceINTEL' : 5749, + 'OpSubgroupAvcImeRefWindowSizeINTEL' : 5750, + 'OpSubgroupAvcImeAdjustRefOffsetINTEL' : 5751, + 'OpSubgroupAvcImeConvertToMcePayloadINTEL' : 5752, + 'OpSubgroupAvcImeSetMaxMotionVectorCountINTEL' : 5753, + 'OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL' : 5754, + 'OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL' : 5755, + 'OpSubgroupAvcImeSetWeightedSadINTEL' : 5756, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL' : 5757, + 'OpSubgroupAvcImeEvaluateWithDualReferenceINTEL' : 5758, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL' : 5759, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL' : 5760, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL' : 5761, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL' : 5762, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL' : 5763, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL' : 5764, + 'OpSubgroupAvcImeConvertToMceResultINTEL' : 5765, + 'OpSubgroupAvcImeGetSingleReferenceStreaminINTEL' : 5766, + 'OpSubgroupAvcImeGetDualReferenceStreaminINTEL' : 5767, + 'OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL' : 5768, + 'OpSubgroupAvcImeStripDualReferenceStreamoutINTEL' : 5769, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL' : 5770, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL' : 5771, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL' : 5772, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL' : 5773, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL' : 5774, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL' : 5775, + 'OpSubgroupAvcImeGetBorderReachedINTEL' : 5776, + 'OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL' : 5777, + 'OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL' : 5778, + 'OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL' : 5779, + 'OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL' : 5780, + 'OpSubgroupAvcFmeInitializeINTEL' : 5781, + 'OpSubgroupAvcBmeInitializeINTEL' : 5782, + 'OpSubgroupAvcRefConvertToMcePayloadINTEL' : 5783, + 'OpSubgroupAvcRefSetBidirectionalMixDisableINTEL' : 5784, + 'OpSubgroupAvcRefSetBilinearFilterEnableINTEL' : 5785, + 'OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL' : 5786, + 'OpSubgroupAvcRefEvaluateWithDualReferenceINTEL' : 5787, + 'OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL' : 5788, + 'OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL' : 5789, + 'OpSubgroupAvcRefConvertToMceResultINTEL' : 5790, + 'OpSubgroupAvcSicInitializeINTEL' : 5791, + 'OpSubgroupAvcSicConfigureSkcINTEL' : 5792, + 'OpSubgroupAvcSicConfigureIpeLumaINTEL' : 5793, + 'OpSubgroupAvcSicConfigureIpeLumaChromaINTEL' : 5794, + 'OpSubgroupAvcSicGetMotionVectorMaskINTEL' : 5795, + 'OpSubgroupAvcSicConvertToMcePayloadINTEL' : 5796, + 'OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL' : 5797, + 'OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL' : 5798, + 'OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL' : 5799, + 'OpSubgroupAvcSicSetBilinearFilterEnableINTEL' : 5800, + 'OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL' : 5801, + 'OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL' : 5802, + 'OpSubgroupAvcSicEvaluateIpeINTEL' : 5803, + 'OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL' : 5804, + 'OpSubgroupAvcSicEvaluateWithDualReferenceINTEL' : 5805, + 'OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL' : 5806, + 'OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL' : 5807, + 'OpSubgroupAvcSicConvertToMceResultINTEL' : 5808, + 'OpSubgroupAvcSicGetIpeLumaShapeINTEL' : 5809, + 'OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL' : 5810, + 'OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL' : 5811, + 'OpSubgroupAvcSicGetPackedIpeLumaModesINTEL' : 5812, + 'OpSubgroupAvcSicGetIpeChromaModeINTEL' : 5813, + 'OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL' : 5814, + 'OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL' : 5815, + 'OpSubgroupAvcSicGetInterRawSadsINTEL' : 5816, + 'OpVariableLengthArrayINTEL' : 5818, + 'OpSaveMemoryINTEL' : 5819, + 'OpRestoreMemoryINTEL' : 5820, + 'OpArbitraryFloatSinCosPiINTEL' : 5840, + 'OpArbitraryFloatCastINTEL' : 5841, + 'OpArbitraryFloatCastFromIntINTEL' : 5842, + 'OpArbitraryFloatCastToIntINTEL' : 5843, + 'OpArbitraryFloatAddINTEL' : 5846, + 'OpArbitraryFloatSubINTEL' : 5847, + 'OpArbitraryFloatMulINTEL' : 5848, + 'OpArbitraryFloatDivINTEL' : 5849, + 'OpArbitraryFloatGTINTEL' : 5850, + 'OpArbitraryFloatGEINTEL' : 5851, + 'OpArbitraryFloatLTINTEL' : 5852, + 'OpArbitraryFloatLEINTEL' : 5853, + 'OpArbitraryFloatEQINTEL' : 5854, + 'OpArbitraryFloatRecipINTEL' : 5855, + 'OpArbitraryFloatRSqrtINTEL' : 5856, + 'OpArbitraryFloatCbrtINTEL' : 5857, + 'OpArbitraryFloatHypotINTEL' : 5858, + 'OpArbitraryFloatSqrtINTEL' : 5859, + 'OpArbitraryFloatLogINTEL' : 5860, + 'OpArbitraryFloatLog2INTEL' : 5861, + 'OpArbitraryFloatLog10INTEL' : 5862, + 'OpArbitraryFloatLog1pINTEL' : 5863, + 'OpArbitraryFloatExpINTEL' : 5864, + 'OpArbitraryFloatExp2INTEL' : 5865, + 'OpArbitraryFloatExp10INTEL' : 5866, + 'OpArbitraryFloatExpm1INTEL' : 5867, + 'OpArbitraryFloatSinINTEL' : 5868, + 'OpArbitraryFloatCosINTEL' : 5869, + 'OpArbitraryFloatSinCosINTEL' : 5870, + 'OpArbitraryFloatSinPiINTEL' : 5871, + 'OpArbitraryFloatCosPiINTEL' : 5872, + 'OpArbitraryFloatASinINTEL' : 5873, + 'OpArbitraryFloatASinPiINTEL' : 5874, + 'OpArbitraryFloatACosINTEL' : 5875, + 'OpArbitraryFloatACosPiINTEL' : 5876, + 'OpArbitraryFloatATanINTEL' : 5877, + 'OpArbitraryFloatATanPiINTEL' : 5878, + 'OpArbitraryFloatATan2INTEL' : 5879, + 'OpArbitraryFloatPowINTEL' : 5880, + 'OpArbitraryFloatPowRINTEL' : 5881, + 'OpArbitraryFloatPowNINTEL' : 5882, + 'OpLoopControlINTEL' : 5887, + 'OpAliasDomainDeclINTEL' : 5911, + 'OpAliasScopeDeclINTEL' : 5912, + 'OpAliasScopeListDeclINTEL' : 5913, + 'OpFixedSqrtINTEL' : 5923, + 'OpFixedRecipINTEL' : 5924, + 'OpFixedRsqrtINTEL' : 5925, + 'OpFixedSinINTEL' : 5926, + 'OpFixedCosINTEL' : 5927, + 'OpFixedSinCosINTEL' : 5928, + 'OpFixedSinPiINTEL' : 5929, + 'OpFixedCosPiINTEL' : 5930, + 'OpFixedSinCosPiINTEL' : 5931, + 'OpFixedLogINTEL' : 5932, + 'OpFixedExpINTEL' : 5933, + 'OpPtrCastToCrossWorkgroupINTEL' : 5934, + 'OpCrossWorkgroupCastToPtrINTEL' : 5938, + 'OpReadPipeBlockingINTEL' : 5946, + 'OpWritePipeBlockingINTEL' : 5947, + 'OpFPGARegINTEL' : 5949, + 'OpRayQueryGetRayTMinKHR' : 6016, + 'OpRayQueryGetRayFlagsKHR' : 6017, + 'OpRayQueryGetIntersectionTKHR' : 6018, + 'OpRayQueryGetIntersectionInstanceCustomIndexKHR' : 6019, + 'OpRayQueryGetIntersectionInstanceIdKHR' : 6020, + 'OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR' : 6021, + 'OpRayQueryGetIntersectionGeometryIndexKHR' : 6022, + 'OpRayQueryGetIntersectionPrimitiveIndexKHR' : 6023, + 'OpRayQueryGetIntersectionBarycentricsKHR' : 6024, + 'OpRayQueryGetIntersectionFrontFaceKHR' : 6025, + 'OpRayQueryGetIntersectionCandidateAABBOpaqueKHR' : 6026, + 'OpRayQueryGetIntersectionObjectRayDirectionKHR' : 6027, + 'OpRayQueryGetIntersectionObjectRayOriginKHR' : 6028, + 'OpRayQueryGetWorldRayDirectionKHR' : 6029, + 'OpRayQueryGetWorldRayOriginKHR' : 6030, + 'OpRayQueryGetIntersectionObjectToWorldKHR' : 6031, + 'OpRayQueryGetIntersectionWorldToObjectKHR' : 6032, + 'OpAtomicFAddEXT' : 6035, + 'OpTypeBufferSurfaceINTEL' : 6086, + 'OpTypeStructContinuedINTEL' : 6090, + 'OpConstantCompositeContinuedINTEL' : 6091, + 'OpSpecConstantCompositeContinuedINTEL' : 6092, + 'OpCompositeConstructContinuedINTEL' : 6096, + 'OpConvertFToBF16INTEL' : 6116, + 'OpConvertBF16ToFINTEL' : 6117, + 'OpControlBarrierArriveINTEL' : 6142, + 'OpControlBarrierWaitINTEL' : 6143, + 'OpArithmeticFenceEXT' : 6145, + 'OpTaskSequenceCreateINTEL' : 6163, + 'OpTaskSequenceAsyncINTEL' : 6164, + 'OpTaskSequenceGetINTEL' : 6165, + 'OpTaskSequenceReleaseINTEL' : 6166, + 'OpTypeTaskSequenceINTEL' : 6199, + 'OpSubgroupBlockPrefetchINTEL' : 6221, + 'OpSubgroup2DBlockLoadINTEL' : 6231, + 'OpSubgroup2DBlockLoadTransformINTEL' : 6232, + 'OpSubgroup2DBlockLoadTransposeINTEL' : 6233, + 'OpSubgroup2DBlockPrefetchINTEL' : 6234, + 'OpSubgroup2DBlockStoreINTEL' : 6235, + 'OpSubgroupMatrixMultiplyAccumulateINTEL' : 6237, + 'OpBitwiseFunctionINTEL' : 6242, + 'OpGroupIMulKHR' : 6401, + 'OpGroupFMulKHR' : 6402, + 'OpGroupBitwiseAndKHR' : 6403, + 'OpGroupBitwiseOrKHR' : 6404, + 'OpGroupBitwiseXorKHR' : 6405, + 'OpGroupLogicalAndKHR' : 6406, + 'OpGroupLogicalOrKHR' : 6407, + 'OpGroupLogicalXorKHR' : 6408, + 'OpRoundFToTF32INTEL' : 6426, + 'OpMaskedGatherINTEL' : 6428, + 'OpMaskedScatterINTEL' : 6429, + }, + +} + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d new file mode 100644 index 00000000..63f66907 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d @@ -0,0 +1,2464 @@ +/+ + + Copyright (c) 2014-2024 The Khronos Group Inc. + + + + Permission is hereby granted, free of charge, to any person obtaining a copy + + of this software and/or associated documentation files (the "Materials"), + + to deal in the Materials without restriction, including without limitation + + the rights to use, copy, modify, merge, publish, distribute, sublicense, + + and/or sell copies of the Materials, and to permit persons to whom the + + Materials are furnished to do so, subject to the following conditions: + + + + The above copyright notice and this permission notice shall be included in + + all copies or substantial portions of the Materials. + + + + MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS + + STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND + + HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ + + + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + + FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS + + IN THE MATERIALS. + +/ + +/+ + + This header is automatically generated by the same tool that creates + + the Binary Section of the SPIR-V specification. + +/ + +/+ + + Enumeration tokens for SPIR-V, in various styles: + + C, C++, C++11, JSON, Lua, Python, C#, D, Beef + + + + - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL + + - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL + + - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL + + - Lua will use tables, e.g.: spv.SourceLanguage.GLSL + + - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] + + - C# will use enum classes in the Specification class located in the "Spv" namespace, + + e.g.: Spv.Specification.SourceLanguage.GLSL + + - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL + + - Beef will use enum classes in the Specification class located in the "Spv" namespace, + + e.g.: Spv.Specification.SourceLanguage.GLSL + + + + Some tokens act like mask values, which can be OR'd together, + + while others are mutually exclusive. The mask-like ones have + + "Mask" in their name, and a parallel enum that has the shift + + amount (1 << x) for each corresponding enumerant. + +/ + +module spv; + +enum uint MagicNumber = 0x07230203; +enum uint Version = 0x00010600; +enum uint Revision = 1; +enum uint OpCodeMask = 0xffff; +enum uint WordCountShift = 16; + +enum SourceLanguage : uint +{ + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + CPP_for_OpenCL = 6, + SYCL = 7, + HERO_C = 8, + NZSL = 9, + WGSL = 10, + Slang = 11, + Zig = 12, + Rust = 13, + Max = 0x7fffffff, +} + +enum ExecutionModel : uint +{ + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + TaskEXT = 5364, + MeshEXT = 5365, + Max = 0x7fffffff, +} + +enum AddressingModel : uint +{ + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, +} + +enum MemoryModel : uint +{ + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, +} + +enum ExecutionMode : uint +{ + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + NonCoherentColorAttachmentReadEXT = 4169, + NonCoherentDepthAttachmentReadEXT = 4170, + NonCoherentStencilAttachmentReadEXT = 4171, + SubgroupUniformControlFlowKHR = 4421, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + NonCoherentTileAttachmentReadQCOM = 4489, + TileShadingRateQCOM = 4490, + EarlyAndLateFragmentTestsAMD = 5017, + StencilRefReplacingEXT = 5027, + CoalescingAMDX = 5069, + IsApiEntryAMDX = 5070, + MaxNodeRecursionAMDX = 5071, + StaticNumWorkgroupsAMDX = 5072, + ShaderIndexAMDX = 5073, + MaxNumWorkgroupsAMDX = 5077, + StencilRefUnchangedFrontAMD = 5079, + StencilRefGreaterFrontAMD = 5080, + StencilRefLessFrontAMD = 5081, + StencilRefUnchangedBackAMD = 5082, + StencilRefGreaterBackAMD = 5083, + StencilRefLessBackAMD = 5084, + QuadDerivativesKHR = 5088, + RequireFullQuadsKHR = 5089, + SharesInputWithAMDX = 5102, + OutputLinesEXT = 5269, + OutputLinesNV = 5269, + OutputPrimitivesEXT = 5270, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsKHR = 5289, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearKHR = 5290, + DerivativeGroupLinearNV = 5290, + OutputTrianglesEXT = 5298, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, + MaximallyReconvergesKHR = 6023, + FPFastMathDefault = 6028, + StreamingInterfaceINTEL = 6154, + RegisterMapInterfaceINTEL = 6160, + NamedBarrierCountINTEL = 6417, + MaximumRegistersINTEL = 6461, + MaximumRegistersIdINTEL = 6462, + NamedMaximumRegistersINTEL = 6463, + Max = 0x7fffffff, +} + +enum StorageClass : uint +{ + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + TileImageEXT = 4172, + TileAttachmentQCOM = 4491, + NodePayloadAMDX = 5068, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + HitObjectAttributeNV = 5385, + TaskPayloadWorkgroupEXT = 5402, + CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, + Max = 0x7fffffff, +} + +enum Dim : uint +{ + _1D = 0, + _2D = 1, + _3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + TileImageDataEXT = 4173, + Max = 0x7fffffff, +} + +enum SamplerAddressingMode : uint +{ + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +} + +enum SamplerFilterMode : uint +{ + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +} + +enum ImageFormat : uint +{ + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, +} + +enum ImageChannelOrder : uint +{ + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +} + +enum ImageChannelDataType : uint +{ + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + UnormInt10X6EXT = 17, + UnsignedIntRaw10EXT = 19, + UnsignedIntRaw12EXT = 20, + UnormInt2_101010EXT = 21, + UnsignedInt10X6EXT = 22, + UnsignedInt12X4EXT = 23, + UnsignedInt14X2EXT = 24, + UnormInt12X4EXT = 25, + UnormInt14X2EXT = 26, + Max = 0x7fffffff, +} + +enum ImageOperandsShift : uint +{ + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Nontemporal = 14, + Offsets = 16, + Max = 0x7fffffff, +} + +enum ImageOperandsMask : uint +{ + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + Nontemporal = 0x00004000, + Offsets = 0x00010000, +} + +enum FPFastMathModeShift : uint +{ + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + AllowContract = 16, + AllowContractFastINTEL = 16, + AllowReassoc = 17, + AllowReassocINTEL = 17, + AllowTransform = 18, + Max = 0x7fffffff, +} + +enum FPFastMathModeMask : uint +{ + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + AllowContract = 0x00010000, + AllowContractFastINTEL = 0x00010000, + AllowReassoc = 0x00020000, + AllowReassocINTEL = 0x00020000, + AllowTransform = 0x00040000, +} + +enum FPRoundingMode : uint +{ + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +} + +enum LinkageType : uint +{ + Export = 0, + Import = 1, + LinkOnceODR = 2, + Max = 0x7fffffff, +} + +enum AccessQualifier : uint +{ + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +} + +enum FunctionParameterAttribute : uint +{ + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + RuntimeAlignedINTEL = 5940, + Max = 0x7fffffff, +} + +enum Decoration : uint +{ + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + WeightTextureQCOM = 4487, + BlockMatchTextureQCOM = 4488, + BlockMatchSamplerQCOM = 4499, + ExplicitInterpAMD = 4999, + NodeSharesPayloadLimitsWithAMDX = 5019, + NodeMaxPayloadsAMDX = 5020, + TrackFinishWritingAMDX = 5078, + PayloadNodeNameAMDX = 5091, + PayloadNodeBaseIndexAMDX = 5098, + PayloadNodeSparseArrayAMDX = 5099, + PayloadNodeArraySizeAMDX = 5100, + PayloadDispatchIndirectAMDX = 5105, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveEXT = 5271, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexKHR = 5285, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + HitObjectShaderRecordBufferNV = 5386, + BindlessSamplerNV = 5398, + BindlessImageNV = 5399, + BoundSamplerNV = 5400, + BoundImageNV = 5401, + SIMTCallINTEL = 5599, + ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + StridesizeINTEL = 5883, + WordsizeINTEL = 5884, + TrueDualPortINTEL = 5885, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + MathOpDSPModeINTEL = 5909, + AliasScopeINTEL = 5914, + NoAliasINTEL = 5915, + InitiationIntervalINTEL = 5917, + MaxConcurrencyINTEL = 5918, + PipelineEnableINTEL = 5919, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, + MediaBlockIOINTEL = 6140, + StallFreeINTEL = 6151, + FPMaxErrorDecorationINTEL = 6170, + LatencyControlLabelINTEL = 6172, + LatencyControlConstraintINTEL = 6173, + ConduitKernelArgumentINTEL = 6175, + RegisterMapKernelArgumentINTEL = 6176, + MMHostInterfaceAddressWidthINTEL = 6177, + MMHostInterfaceDataWidthINTEL = 6178, + MMHostInterfaceLatencyINTEL = 6179, + MMHostInterfaceReadWriteModeINTEL = 6180, + MMHostInterfaceMaxBurstINTEL = 6181, + MMHostInterfaceWaitRequestINTEL = 6182, + StableKernelArgumentINTEL = 6183, + HostAccessINTEL = 6188, + InitModeINTEL = 6190, + ImplementInRegisterMapINTEL = 6191, + CacheControlLoadINTEL = 6442, + CacheControlStoreINTEL = 6443, + Max = 0x7fffffff, +} + +enum BuiltIn : uint +{ + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + CoreIDARM = 4160, + CoreCountARM = 4161, + CoreMaxIDARM = 4162, + WarpIDARM = 4163, + WarpMaxIDARM = 4164, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + TileOffsetQCOM = 4492, + TileDimensionQCOM = 4493, + TileApronSizeQCOM = 4494, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + RemainingRecursionLevelsAMDX = 5021, + ShaderIndexAMDX = 5073, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordKHR = 5286, + BaryCoordNV = 5286, + BaryCoordNoPerspKHR = 5287, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + PrimitivePointIndicesEXT = 5294, + PrimitiveLineIndicesEXT = 5295, + PrimitiveTriangleIndicesEXT = 5296, + CullPrimitiveEXT = 5299, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + CurrentRayTimeNV = 5334, + HitTriangleVertexPositionsKHR = 5335, + HitMicroTriangleVertexPositionsNV = 5337, + HitMicroTriangleVertexBarycentricsNV = 5344, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + HitIsSphereNV = 5359, + HitIsLSSNV = 5360, + HitSpherePositionNV = 5361, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + HitLSSPositionsNV = 5396, + HitKindFrontFacingMicroTriangleNV = 5405, + HitKindBackFacingMicroTriangleNV = 5406, + HitSphereRadiusNV = 5420, + HitLSSRadiiNV = 5421, + ClusterIDNV = 5436, + CullMaskKHR = 6021, + Max = 0x7fffffff, +} + +enum SelectionControlShift : uint +{ + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +} + +enum SelectionControlMask : uint +{ + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +} + +enum LoopControlShift : uint +{ + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + NoFusionINTEL = 23, + LoopCountINTEL = 24, + MaxReinvocationDelayINTEL = 25, + Max = 0x7fffffff, +} + +enum LoopControlMask : uint +{ + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + NoFusionINTEL = 0x00800000, + LoopCountINTEL = 0x01000000, + MaxReinvocationDelayINTEL = 0x02000000, +} + +enum FunctionControlShift : uint +{ + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + OptNoneEXT = 16, + OptNoneINTEL = 16, + Max = 0x7fffffff, +} + +enum FunctionControlMask : uint +{ + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + OptNoneEXT = 0x00010000, + OptNoneINTEL = 0x00010000, +} + +enum MemorySemanticsShift : uint +{ + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, +} + +enum MemorySemanticsMask : uint +{ + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, +} + +enum MemoryAccessShift : uint +{ + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + AliasScopeINTELMask = 16, + NoAliasINTELMask = 17, + Max = 0x7fffffff, +} + +enum MemoryAccessMask : uint +{ + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + AliasScopeINTELMask = 0x00010000, + NoAliasINTELMask = 0x00020000, +} + +enum Scope : uint +{ + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, +} + +enum GroupOperation : uint +{ + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, +} + +enum KernelEnqueueFlags : uint +{ + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +} + +enum KernelProfilingInfoShift : uint +{ + CmdExecTime = 0, + Max = 0x7fffffff, +} + +enum KernelProfilingInfoMask : uint +{ + MaskNone = 0, + CmdExecTime = 0x00000001, +} + +enum Capability : uint +{ + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + UniformDecoration = 71, + CoreBuiltinsARM = 4165, + TileImageColorReadAccessEXT = 4166, + TileImageDepthReadAccessEXT = 4167, + TileImageStencilReadAccessEXT = 4168, + CooperativeMatrixLayoutsARM = 4201, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + UntypedPointersKHR = 4473, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + TextureSampleWeightedQCOM = 4484, + TextureBoxFilterQCOM = 4485, + TextureBlockMatchQCOM = 4486, + TileShadingQCOM = 4495, + TextureBlockMatch2QCOM = 4498, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + ShaderEnqueueAMDX = 5067, + QuadControlKHR = 5087, + BFloat16TypeKHR = 5116, + BFloat16DotProductKHR = 5117, + BFloat16CooperativeMatrixKHR = 5118, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + MeshShadingEXT = 5283, + FragmentBarycentricKHR = 5284, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsKHR = 5288, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingPositionFetchKHR = 5336, + RayTracingNV = 5340, + RayTracingMotionBlurNV = 5341, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearKHR = 5350, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocation = 5379, + DemoteToHelperInvocationEXT = 5379, + DisplacementMicromapNV = 5380, + RayTracingOpacityMicromapEXT = 5381, + ShaderInvocationReorderNV = 5383, + BindlessTextureNV = 5390, + RayQueryPositionFetchKHR = 5391, + CooperativeVectorNV = 5394, + AtomicFloat16VectorNV = 5404, + RayTracingDisplacementMicromapNV = 5409, + RawAccessChainsNV = 5414, + RayTracingSpheresGeometryNV = 5418, + RayTracingLinearSweptSpheresGeometryNV = 5419, + CooperativeMatrixReductionsNV = 5430, + CooperativeMatrixConversionsNV = 5431, + CooperativeMatrixPerElementOperationsNV = 5432, + CooperativeMatrixTensorAddressingNV = 5433, + CooperativeMatrixBlockLoadsNV = 5434, + CooperativeVectorTrainingNV = 5435, + RayTracingClusterAccelerationStructureNV = 5437, + TensorAddressingNV = 5439, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, + FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, + ArbitraryPrecisionFloatingPointINTEL = 5845, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGADSPControlINTEL = 5908, + MemoryAccessAliasingINTEL = 5910, + FPGAInvocationPipeliningAttributesINTEL = 5916, + FPGABufferLocationINTEL = 5920, + ArbitraryPrecisionFixedPointINTEL = 5922, + USMStorageClassesINTEL = 5935, + RuntimeAlignedAttributeINTEL = 5939, + IOPipesINTEL = 5943, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + DotProductInputAll = 6016, + DotProductInputAllKHR = 6016, + DotProductInput4x8Bit = 6017, + DotProductInput4x8BitKHR = 6017, + DotProductInput4x8BitPacked = 6018, + DotProductInput4x8BitPackedKHR = 6018, + DotProduct = 6019, + DotProductKHR = 6019, + RayCullMaskKHR = 6020, + CooperativeMatrixKHR = 6022, + ReplicatedCompositesEXT = 6024, + BitInstructions = 6025, + GroupNonUniformRotateKHR = 6026, + FloatControls2 = 6029, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + LongCompositesINTEL = 6089, + OptNoneEXT = 6094, + OptNoneINTEL = 6094, + AtomicFloat16AddEXT = 6095, + DebugInfoModuleINTEL = 6114, + BFloat16ConversionINTEL = 6115, + SplitBarrierINTEL = 6141, + ArithmeticFenceEXT = 6144, + FPGAClusterAttributesV2INTEL = 6150, + FPGAKernelAttributesv2INTEL = 6161, + TaskSequenceINTEL = 6162, + FPMaxErrorINTEL = 6169, + FPGALatencyControlINTEL = 6171, + FPGAArgumentInterfacesINTEL = 6174, + GlobalVariableHostAccessINTEL = 6187, + GlobalVariableFPGADecorationsINTEL = 6189, + SubgroupBufferPrefetchINTEL = 6220, + Subgroup2DBlockIOINTEL = 6228, + Subgroup2DBlockTransformINTEL = 6229, + Subgroup2DBlockTransposeINTEL = 6230, + SubgroupMatrixMultiplyAccumulateINTEL = 6236, + TernaryBitwiseFunctionINTEL = 6241, + GroupUniformArithmeticKHR = 6400, + TensorFloat32RoundingINTEL = 6425, + MaskedGatherScatterINTEL = 6427, + CacheControlsINTEL = 6441, + RegisterLimitsINTEL = 6460, + Max = 0x7fffffff, +} + +enum RayFlagsShift : uint +{ + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipBuiltinPrimitivesNV = 8, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + ForceOpacityMicromap2StateEXT = 10, + Max = 0x7fffffff, +} + +enum RayFlagsMask : uint +{ + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipBuiltinPrimitivesNV = 0x00000100, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + ForceOpacityMicromap2StateEXT = 0x00000400, +} + +enum RayQueryIntersection : uint +{ + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, +} + +enum RayQueryCommittedIntersectionType : uint +{ + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, +} + +enum RayQueryCandidateIntersectionType : uint +{ + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, +} + +enum FragmentShadingRateShift : uint +{ + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, +} + +enum FragmentShadingRateMask : uint +{ + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, +} + +enum FPDenormMode : uint +{ + Preserve = 0, + FlushToZero = 1, + Max = 0x7fffffff, +} + +enum FPOperationMode : uint +{ + IEEE = 0, + ALT = 1, + Max = 0x7fffffff, +} + +enum QuantizationModes : uint +{ + TRN = 0, + TRN_ZERO = 1, + RND = 2, + RND_ZERO = 3, + RND_INF = 4, + RND_MIN_INF = 5, + RND_CONV = 6, + RND_CONV_ODD = 7, + Max = 0x7fffffff, +} + +enum OverflowModes : uint +{ + WRAP = 0, + SAT = 1, + SAT_ZERO = 2, + SAT_SYM = 3, + Max = 0x7fffffff, +} + +enum PackedVectorFormat : uint +{ + PackedVectorFormat4x8Bit = 0, + PackedVectorFormat4x8BitKHR = 0, + Max = 0x7fffffff, +} + +enum CooperativeMatrixOperandsShift : uint +{ + MatrixASignedComponentsKHR = 0, + MatrixBSignedComponentsKHR = 1, + MatrixCSignedComponentsKHR = 2, + MatrixResultSignedComponentsKHR = 3, + SaturatingAccumulationKHR = 4, + Max = 0x7fffffff, +} + +enum CooperativeMatrixOperandsMask : uint +{ + MaskNone = 0, + MatrixASignedComponentsKHR = 0x00000001, + MatrixBSignedComponentsKHR = 0x00000002, + MatrixCSignedComponentsKHR = 0x00000004, + MatrixResultSignedComponentsKHR = 0x00000008, + SaturatingAccumulationKHR = 0x00000010, +} + +enum CooperativeMatrixLayout : uint +{ + RowMajorKHR = 0, + ColumnMajorKHR = 1, + RowBlockedInterleavedARM = 4202, + ColumnBlockedInterleavedARM = 4203, + Max = 0x7fffffff, +} + +enum CooperativeMatrixUse : uint +{ + MatrixAKHR = 0, + MatrixBKHR = 1, + MatrixAccumulatorKHR = 2, + Max = 0x7fffffff, +} + +enum CooperativeMatrixReduceShift : uint +{ + Row = 0, + Column = 1, + _2x2 = 2, + Max = 0x7fffffff, +} + +enum CooperativeMatrixReduceMask : uint +{ + MaskNone = 0, + Row = 0x00000001, + Column = 0x00000002, + _2x2 = 0x00000004, +} + +enum TensorClampMode : uint +{ + Undefined = 0, + Constant = 1, + ClampToEdge = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +} + +enum TensorAddressingOperandsShift : uint +{ + TensorView = 0, + DecodeFunc = 1, + Max = 0x7fffffff, +} + +enum TensorAddressingOperandsMask : uint +{ + MaskNone = 0, + TensorView = 0x00000001, + DecodeFunc = 0x00000002, +} + +enum InitializationModeQualifier : uint +{ + InitOnDeviceReprogramINTEL = 0, + InitOnDeviceResetINTEL = 1, + Max = 0x7fffffff, +} + +enum HostAccessQualifier : uint +{ + NoneINTEL = 0, + ReadINTEL = 1, + WriteINTEL = 2, + ReadWriteINTEL = 3, + Max = 0x7fffffff, +} + +enum LoadCacheControl : uint +{ + UncachedINTEL = 0, + CachedINTEL = 1, + StreamingINTEL = 2, + InvalidateAfterReadINTEL = 3, + ConstCachedINTEL = 4, + Max = 0x7fffffff, +} + +enum StoreCacheControl : uint +{ + UncachedINTEL = 0, + WriteThroughINTEL = 1, + WriteBackINTEL = 2, + StreamingINTEL = 3, + Max = 0x7fffffff, +} + +enum NamedMaximumNumberOfRegisters : uint +{ + AutoINTEL = 0, + Max = 0x7fffffff, +} + +enum MatrixMultiplyAccumulateOperandsShift : uint +{ + MatrixASignedComponentsINTEL = 0, + MatrixBSignedComponentsINTEL = 1, + MatrixCBFloat16INTEL = 2, + MatrixResultBFloat16INTEL = 3, + MatrixAPackedInt8INTEL = 4, + MatrixBPackedInt8INTEL = 5, + MatrixAPackedInt4INTEL = 6, + MatrixBPackedInt4INTEL = 7, + MatrixATF32INTEL = 8, + MatrixBTF32INTEL = 9, + MatrixAPackedFloat16INTEL = 10, + MatrixBPackedFloat16INTEL = 11, + MatrixAPackedBFloat16INTEL = 12, + MatrixBPackedBFloat16INTEL = 13, + Max = 0x7fffffff, +} + +enum MatrixMultiplyAccumulateOperandsMask : uint +{ + MaskNone = 0, + MatrixASignedComponentsINTEL = 0x00000001, + MatrixBSignedComponentsINTEL = 0x00000002, + MatrixCBFloat16INTEL = 0x00000004, + MatrixResultBFloat16INTEL = 0x00000008, + MatrixAPackedInt8INTEL = 0x00000010, + MatrixBPackedInt8INTEL = 0x00000020, + MatrixAPackedInt4INTEL = 0x00000040, + MatrixBPackedInt4INTEL = 0x00000080, + MatrixATF32INTEL = 0x00000100, + MatrixBTF32INTEL = 0x00000200, + MatrixAPackedFloat16INTEL = 0x00000400, + MatrixBPackedFloat16INTEL = 0x00000800, + MatrixAPackedBFloat16INTEL = 0x00001000, + MatrixBPackedBFloat16INTEL = 0x00002000, +} + +enum RawAccessChainOperandsShift : uint +{ + RobustnessPerComponentNV = 0, + RobustnessPerElementNV = 1, + Max = 0x7fffffff, +} + +enum RawAccessChainOperandsMask : uint +{ + MaskNone = 0, + RobustnessPerComponentNV = 0x00000001, + RobustnessPerElementNV = 0x00000002, +} + +enum FPEncoding : uint +{ + BFloat16KHR = 0, + Max = 0x7fffffff, +} + +enum CooperativeVectorMatrixLayout : uint +{ + RowMajorNV = 0, + ColumnMajorNV = 1, + InferencingOptimalNV = 2, + TrainingOptimalNV = 3, + Max = 0x7fffffff, +} + +enum ComponentType : uint +{ + Float16NV = 0, + Float32NV = 1, + Float64NV = 2, + SignedInt8NV = 3, + SignedInt16NV = 4, + SignedInt32NV = 5, + SignedInt64NV = 6, + UnsignedInt8NV = 7, + UnsignedInt16NV = 8, + UnsignedInt32NV = 9, + UnsignedInt64NV = 10, + SignedInt8PackedNV = 1000491000, + UnsignedInt8PackedNV = 1000491001, + FloatE4M3NV = 1000491002, + FloatE5M2NV = 1000491003, + Max = 0x7fffffff, +} + +enum Op : uint +{ + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtINTEL = 5923, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtINTEL = 5925, + OpFixedSinINTEL = 5926, + OpFixedCosINTEL = 5927, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogINTEL = 5932, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + Max = 0x7fffffff, +} + + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt new file mode 100644 index 00000000..3f86c3ed --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.14) + +set(CMAKE_INSTALL_PREFIX "install" CACHE STRING "prefix" FORCE) + +project(buildSpvHeaders) + +set(SOURCES + main.cpp + jsonToSpirv.cpp + header.cpp + jsoncpp/dist/jsoncpp.cpp) + +set(HEADERS + jsonToSpirv.h + header.h + jsoncpp/dist/json/json.h) + +if(CMAKE_COMPILER_IS_GNUCXX) + add_definitions(-std=c++11) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_definitions(-std=c++11) +endif() + +add_executable(buildSpvHeaders ${SOURCES} ${HEADERS}) + +install(TARGETS buildSpvHeaders RUNTIME DESTINATION bin) diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py new file mode 100644 index 00000000..ccd843b9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2024 Google LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and/or associated documentation files (the +# "Materials"), to deal in the Materials without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Materials, and to +# permit persons to whom the Materials are furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +# https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +"""Generates a C language headers from a SPIR-V JSON grammar file""" + +import errno +import json +import os.path +import re + +DEFAULT_COPYRIGHT="""Copyright (c) 2020-2024 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +""".split('\n') + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they + don't yet exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + +class ExtInstGrammar: + """The grammar for an extended instruction set""" + + def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None): + self.name = name + self.copyright = copyright + self.instructions = instructions + self.operand_kinds = operand_kinds + self.version = version + self.revision = revision + + +class LangGenerator: + """A language-specific generator""" + + def __init__(self): + self.upper_case_initial = re.compile('^[A-Z]') + pass + + def comment_prefix(self): + return "" + + def namespace_prefix(self): + return "" + + def uses_guards(self): + return False + + def cpp_guard_preamble(self): + return "" + + def cpp_guard_postamble(self): + return "" + + def enum_value(self, prefix, name, value): + if self.upper_case_initial.match(name): + use_name = name + else: + use_name = '_' + name + + return " {}{} = {},".format(prefix, use_name, value) + + def generate(self, grammar): + """Returns a string that is the language-specific header for the given grammar""" + + parts = [] + if grammar.copyright: + parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright]) + parts.append('') + + guard = 'SPIRV_UNIFIED1_{}_H_'.format(grammar.name) + if self.uses_guards: + parts.append('#ifndef {}'.format(guard)) + parts.append('#define {}'.format(guard)) + parts.append('') + + parts.append(self.cpp_guard_preamble()) + + if grammar.version: + parts.append(self.const_definition(grammar.name, 'Version', grammar.version)) + + if grammar.revision is not None: + parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision)) + + parts.append('') + + if grammar.instructions: + parts.append(self.enum_prefix(grammar.name, 'Instructions')) + for inst in grammar.instructions: + parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode'])) + parts.append(self.enum_end(grammar.name, 'Instructions')) + parts.append('') + + if grammar.operand_kinds: + for kind in grammar.operand_kinds: + parts.append(self.enum_prefix(grammar.name, kind['kind'])) + for e in kind['enumerants']: + parts.append(self.enum_value(grammar.name, e['enumerant'], e['value'])) + parts.append(self.enum_end(grammar.name, kind['kind'])) + parts.append('') + + parts.append(self.cpp_guard_postamble()) + + if self.uses_guards: + parts.append('#endif // {}'.format(guard)) + + # Ensre the file ends in an end of line + parts.append('') + + return '\n'.join(parts) + + +class CLikeGenerator(LangGenerator): + def uses_guards(self): + return True + + def comment_prefix(self): + return "// " + + def const_definition(self, prefix, var, value): + # Use an anonymous enum. Don't use a static const int variable because + # that can bloat binary size. + return 'enum {0}{1}{2}{3} = {4},{1}{2}{3}_BitWidthPadding = 0x7fffffff{5};'.format( + '{', '\n ', prefix, var, value, '\n}') + + def enum_prefix(self, prefix, name): + return 'enum {}{} {}'.format(prefix, name, '{') + + def enum_end(self, prefix, enum): + return ' {}{}Max = 0x7fffffff\n{};\n'.format(prefix, enum, '}') + + def cpp_guard_preamble(self): + return '#ifdef __cplusplus\nextern "C" {\n#endif\n' + + def cpp_guard_postamble(self): + return '#ifdef __cplusplus\n}\n#endif\n' + + +class CGenerator(CLikeGenerator): + pass + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') + + parser.add_argument('--extinst-name', + type=str, required=True, + help='The name to use in tokens') + parser.add_argument('--extinst-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for extended instruction set') + parser.add_argument('--extinst-output-base', metavar='', + type=str, required=True, + help='Basename of the language-specific output file.') + args = parser.parse_args() + + with open(args.extinst_grammar) as json_file: + grammar_json = json.loads(json_file.read()) + if 'copyright' in grammar_json: + copyright = grammar_json['copyright'] + else: + copyright = DEFAULT_COPYRIGHT + if 'version' in grammar_json: + version = grammar_json['version'] + else: + version = 0 + if 'operand_kinds' in grammar_json: + operand_kinds = grammar_json['operand_kinds'] + else: + operand_kinds = [] + + grammar = ExtInstGrammar(name = args.extinst_name, + copyright = copyright, + instructions = grammar_json['instructions'], + operand_kinds = operand_kinds, + version = version, + revision = grammar_json['revision']) + make_path_to_file(args.extinst_output_base) + with open(args.extinst_output_base + '.h', 'w') as f: + f.write(CGenerator().generate(grammar)) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py new file mode 100644 index 00000000..96ce008f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +"""Generate C headers for certain extended instruction sets""" + +import subprocess +import os + +# Assume we are running from the tools/buildHeaders directory +os.chdir('../../include/spirv/unified1') + +def mk_extinst(name, grammar_file): + """Generate one C header from a grammar""" + script = '../../../tools/buildHeaders/bin/generate_language_headers.py' + subprocess.check_call(['python3', + script, + '--extinst-name=' + name, + '--extinst-grammar=' + grammar_file, + '--extinst-output-base=' + name]) + subprocess.check_call(['dos2unix', name + '.h']) + + +mk_extinst('DebugInfo', 'extinst.debuginfo.grammar.json') +mk_extinst('OpenCLDebugInfo100', 'extinst.opencl.debuginfo.100.grammar.json') +mk_extinst('AMD_gcn_shader', 'extinst.spv-amd-gcn-shader.grammar.json') +mk_extinst('AMD_shader_ballot', 'extinst.spv-amd-shader-ballot.grammar.json') +mk_extinst('AMD_shader_explicit_vertex_parameter', 'extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json') +mk_extinst('AMD_shader_trinary_minmax', 'extinst.spv-amd-shader-trinary-minmax.grammar.json') +mk_extinst('NonSemanticDebugPrintf', 'extinst.nonsemantic.debugprintf.grammar.json') +mk_extinst('NonSemanticClspvReflection', 'extinst.nonsemantic.clspvreflection.grammar.json') +mk_extinst('NonSemanticDebugBreak', 'extinst.nonsemantic.debugbreak.grammar.json') +mk_extinst('NonSemanticVkspReflection', 'extinst.nonsemantic.vkspreflection.grammar.json') diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders new file mode 100644 index 00000000..853c4672 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -ex + +python3 bin/makeExtinstHeaders.py + +cd ../../include/spirv/unified1 +../../../tools/buildHeaders/build/install/bin/buildSpvHeaders -H spirv.core.grammar.json +dos2unix spirv.* spv.* diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp new file mode 100644 index 00000000..a5570971 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp @@ -0,0 +1,944 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// +// Print headers for SPIR-V in several languages. +// +// To change the header information, change the C++-built database in doc.*. +// +// Then, use "spriv -h " - e.g, spriv.{h,hpp,lua,py,etc}: +// replace the auto-generated header, or "spirv -H" to generate all +// supported language headers to predefined names in the current directory. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jsoncpp/dist/json/json.h" + +#include "header.h" +#include "jsonToSpirv.h" + +// snprintf and _snprintf are not quite the same, but close enough +// for our use. +#ifdef _MSC_VER +#pragma warning(disable:4996) +#define snprintf _snprintf +#endif + +// This file converts SPIR-V definitions to an internal JSON +// representation, and then generates language specific +// data from that single internal form. + +// Initially, the internal form is created from C++ data, +// though this can be changed to a JSON master in time. + +namespace { + class TPrinter { + protected: + TPrinter(); + + static const int DocMagicNumber = 0x07230203; + static const int DocVersion = 0x00010600; + static const int DocRevision = 1; + #define DocRevisionString "1" + static const std::string DocCopyright; + static const std::string DocComment1; + static const std::string DocComment2; + + enum enumStyle_t { + enumNoMask, + enumCount, + enumShift, + enumMask, + enumHex, + }; + + static std::string styleStr(enumStyle_t s) { + return s == enumShift ? "Shift" : + s == enumMask ? "Mask" : ""; + } + + friend std::ostream& operator<<(std::ostream&, const TPrinter&); + + virtual void printAll(std::ostream&) const; + virtual void printComments(std::ostream&) const; + virtual void printPrologue(std::ostream&) const { } + virtual void printDefs(std::ostream&) const; + virtual void printEpilogue(std::ostream&) const { } + virtual void printMeta(std::ostream&) const; + virtual void printTypes(std::ostream&) const { } + virtual void printUtility(std::ostream&) const { }; + + virtual std::string escapeComment(const std::string& s) const; + + // Default printComments() uses these comment strings + virtual std::string commentBeg() const { return ""; } + virtual std::string commentEnd(bool isLast) const { return ""; } + virtual std::string commentBOL() const { return ""; } + virtual std::string commentEOL(bool isLast) const { return ""; } + + typedef std::pair valpair_t; + + // for printing enum values + virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; } + virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const { + return ""; + } + virtual std::string enumFmt(const std::string&, const valpair_t&, + enumStyle_t, bool isLast = false) const { + return ""; + } + virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const { + return enumFmt(s, v, style, true); + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast = false) const { + return ""; + } + + std::vector getSortedVals(const Json::Value&) const; + + virtual std::string indent(int count = 1) const { + return std::string(count * 4, ' '); // default indent level = 4 + } + + static std::string fmtNum(const char* fmt, unsigned val) { + char buff[16]; // ample for 8 hex digits + 0x + snprintf(buff, sizeof(buff), fmt, val); + buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination + return buff; + } + + static std::string fmtStyleVal(unsigned v, enumStyle_t style); + + // If the enum value name would start with a sigit, prepend the enum name. + // E.g, "3D" -> "Dim3D". + static std::string prependIfDigit(const std::string& ename, const std::string& vname) { + return (std::isdigit(vname[0]) ? ename : std::string("")) + vname; + } + + void addComment(Json::Value& node, const std::string& str); + + Json::Value spvRoot; // JSON SPIR-V data + }; + + // Format value as mask or value + std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style) + { + switch (style) { + case enumMask: + return fmtNum("0x%08x", 1< + TPrinter::getSortedVals(const Json::Value& p) const + { + std::vector values; + + for (auto e = p.begin(); e != p.end(); ++e) + values.push_back(valpair_t(e->asUInt(), e.name())); + + // Use a stable sort because we might have aliases, e.g. + // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. + std::stable_sort(values.begin(), values.end()); + + return values; + } + + // Escape comment characters if needed + std::string TPrinter::escapeComment(const std::string& s) const { return s; } + + // Format comments in language specific way + void TPrinter::printComments(std::ostream& out) const + { + const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); + int commentNum = 0; + + for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { + out << commentBeg(); + + for (int line = 0; line < int(comment.size()); ++line) + out << commentBOL() << escapeComment(comment[line].asString()) << + commentEOL((line+1) == comment.size()) << std::endl; + + out << commentEnd(++commentNum == commentCount) << std::endl; + } + } + + // Format header metadata + void TPrinter::printMeta(std::ostream& out) const + { + const Json::Value& meta = spvRoot["spv"]["meta"]; + + const auto print = [&](const char* name, const char* fmt, bool isLast) { + out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); + }; + + print("MagicNumber", "0x%08lx", false); + print("Version", "0x%08lx", false); + print("Revision", "%d", false); + print("OpCodeMask", "0x%04x", false); + print("WordCountShift", "%d", true); + } + + // Format value definitions in language specific way + void TPrinter::printDefs(std::ostream& out) const + { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const bool isMask = (*opClass)["Type"].asString() == "Bit"; + const auto opName = (*opClass)["Name"].asString(); + const auto opPrefix = opName == "Op" ? "" : opName; + + for (enumStyle_t style = (isMask ? enumShift : enumCount); + style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { + + out << enumBeg(opName, style); + + if (style == enumMask) + out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); + + const auto sorted = getSortedVals((*opClass)["Values"]); + + std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); + + bool printMax = (style != enumMask && maxEnum.size() > 0); + + for (const auto& v : sorted) + out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); + + if (printMax) + out << maxEnum; + + auto nextOpClass = opClass; + out << enumEnd(opName, style, ++nextOpClass == enums.end()); + } + } + } + + void TPrinter::printAll(std::ostream& out) const + { + printComments(out); + printPrologue(out); + printTypes(out); + printMeta(out); + printDefs(out); + printUtility(out); + printEpilogue(out); + } + + // Stream entire header to output + std::ostream& operator<<(std::ostream& out, const TPrinter &p) + { + p.printAll(out); + return out; + } + + // JSON printer. Rather than use the default printer, we supply our own so + // we can control the printing order within various containers. + class TPrinterJSON final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } + void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } + + std::string escapeComment(const std::string& s) const override { + std::string newStr; + for (auto c : s) { + if (c == '"') { + newStr += '\\'; + newStr += c; + } else { + newStr += c; + } + } + return newStr; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); + } + + void printMeta(std::ostream& out) const override + { + out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; + printComments(out); + TPrinter::printMeta(out); + out << indent(2) + "},\n"; + } + + std::string commentBeg() const override { return indent(4) + "[\n"; } + std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } + std::string commentBOL() const override { return indent(5) + '"'; } + std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } + + void printComments(std::ostream& out) const override + { + out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; + TPrinter::printComments(out); + out << indent(3) + "],\n"; + } + + void printDefs(std::ostream& out) const override + { + out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; + TPrinter::printDefs(out); + out << indent(2) + "]\n"; + } + + void printAll(std::ostream& out) const override + { + printPrologue(out); + printMeta(out); + printDefs(out); + printEpilogue(out); + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + if (style == enumMask) + return ""; + return indent(3) + "{\n" + + indent(4) + "\"Name\": \"" + s + "\",\n" + + indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + + indent(4) + "\"Values\":\n" + + indent(4) + "{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + if (style == enumMask) + return ""; + return indent(4) + "}\n" + + indent(3) + "}" + (isLast ? "" : ",") + "\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + if (style == enumMask || style == enumNoMask) + return ""; + return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + + (isLast ? "\n" : ",\n"); + } + std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const override { + return ""; + } + }; + + // base for C and C++ + class TPrinterCBase : public TPrinter { + protected: + virtual void printPrologue(std::ostream& out) const override { + out << "#ifndef spirv_" << headerGuardSuffix() << std::endl + << "#define spirv_" << headerGuardSuffix() << std::endl + << std::endl; + } + + void printMeta(std::ostream& out) const override { + out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; + out << "#define SPV_REVISION " << DocRevision << "\n"; + out << "\n"; + + return TPrinter::printMeta(out); + } + + virtual void printEpilogue(std::ostream& out) const override { + out << "#endif" << std::endl; + } + + virtual void printTypes(std::ostream& out) const override { + out << "typedef unsigned int " << pre() << "Id;\n\n"; + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return std::string("static const unsigned int ") + pre() + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + + virtual std::string pre() const { return ""; } // C name prefix + virtual std::string headerGuardSuffix() const = 0; + + virtual std::string fmtEnumUse(const std::string &opPrefix, const std::string &opEnum, const std::string &name) const { return pre() + opPrefix + name; } + + void printUtility(std::ostream& out) const override + { + out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; + out << "#ifndef __cplusplus" << std::endl; + out << "#include " << std::endl; + out << "#endif" << std::endl; + + printHasResultType(out); + printStringFunctions(out); + + out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; + } + + void printHasResultType(std::ostream& out) const { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + std::set seenValues; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const auto opName = (*opClass)["Name"].asString(); + if (opName != "Op") { + continue; + } + + + out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; + out << " *hasResult = *hasResultType = false;" << std::endl; + out << " switch (opcode) {" << std::endl; + out << " default: /* unknown opcode */ break;" << std::endl; + + for (auto& inst : spv::InstructionDesc) { + + // Filter out duplicate enum values, which would break the switch statement. + // These are probably just extension enums promoted to core. + if (seenValues.find(inst.value) != seenValues.end()) { + continue; + } + seenValues.insert(inst.value); + + std::string name = inst.name; + out << " case " << fmtEnumUse("", "Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; + } + + out << " }" << std::endl; + out << "}" << std::endl; + } + } + + void printStringFunctions(std::ostream& out) const { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + for (auto it = enums.begin(); it != enums.end(); ++it) { + const auto type = (*it)["Type"].asString(); + // Skip bitmasks + if (type == "Bit") { + continue; + } + const auto name = (*it)["Name"].asString(); + const auto sorted = getSortedVals((*it)["Values"]); + + std::set seenValues; + std::string fullName = pre() + name; + + out << "inline const char* " << fullName << "ToString(" << fullName << " value) {" << std::endl; + out << " switch (value) {" << std::endl; + for (const auto& v : sorted) { + // Filter out duplicate enum values, which would break the switch statement. + // These are probably just extension enums promoted to core. + if (seenValues.count(v.first)) { + continue; + } + seenValues.insert(v.first); + + out << " " << "case "; + if (name == "Op") { + out << fmtEnumUse("", name, v.second); + } + else + out << fmtEnumUse(name, name, v.second); + out << ": return " << "\"" << v.second << "\";" << std::endl; + } + out << " default: return \"Unknown\";" << std::endl; + out << " }" << std::endl; + out << "}" << std::endl << std::endl; + } + } + }; + + // C printer + class TPrinterC final : public TPrinterCBase { + private: + std::string commentBeg() const override { return "/*\n"; } + std::string commentEnd(bool isLast) const override { return "*/\n"; } + std::string commentBOL() const override { return "** "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return "} " + pre() + s + styleStr(style) + ";\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string pre() const override { return "Spv"; } // C name prefix + std::string headerGuardSuffix() const override { return "H"; } + }; + + // C++ printer + class TPrinterCPP : public TPrinterCBase { + protected: + void printMaskOperators(std::ostream& out, const std::string& specifiers) const { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + out << "// Overload bitwise operators for mask bit combining\n\n"; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const bool isMask = (*opClass)["Type"].asString() == "Bit"; + const auto opName = (*opClass)["Name"].asString(); + + if (isMask) { + const auto typeName = opName + styleStr(enumMask); + + // Overload operator| + out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << + typeName << "(unsigned(a) | unsigned(b)); }\n"; + // Overload operator& + out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << + typeName << "(unsigned(a) & unsigned(b)); }\n"; + // Overload operator^ + out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << + typeName << "(unsigned(a) ^ unsigned(b)); }\n"; + // Overload operator~ + out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " << + typeName << "(~unsigned(a)); }\n"; + } + } + } + private: + void printPrologue(std::ostream& out) const override { + TPrinterCBase::printPrologue(out); + out << "namespace spv {\n\n"; + } + + void printEpilogue(std::ostream& out) const override { + printMaskOperators(out, "inline"); + out << "\n} // end namespace spv\n\n"; + out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; + } + + std::string commentBOL() const override { return "// "; } + + + virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("enum ") + s + styleStr(style) + " {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return "};\n\n"; + } + + virtual std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + // The C++ and C++11 headers define types with the same name. So they + // should use the same header guard. + std::string headerGuardSuffix() const override { return "HPP"; } + + std::string operators; + }; + + // C++11 printer (uses enum classes) + class TPrinterCPP11 final : public TPrinterCPP { + private: + void printEpilogue(std::ostream& out) const override { + printMaskOperators(out, "constexpr"); + out << "\n} // end namespace spv\n\n"; + out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; + } + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + // Add type prefix for scoped enum + std::string fmtEnumUse(const std::string& opPrefix, const std::string& opEnum, const std::string& name) const override { return opEnum + "::" + prependIfDigit(opEnum, name); } + + std::string headerGuardSuffix() const override { return "HPP"; } + }; + + // LUA printer + class TPrinterLua final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } + + void printEpilogue(std::ostream& out) const override { out << "}\n"; } + + std::string commentBOL() const override { return "-- "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent() + s + styleStr(style) + " = {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent() + "},\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); + } + }; + + // Python printer + class TPrinterPython final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } + + void printEpilogue(std::ostream& out) const override { out << "}\n"; } + + std::string commentBOL() const override { return "# "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent() + "},\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; + } + std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const override { + return ""; + } + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); + } + }; + + // C# printer + class TPrinterCSharp final : public TPrinter { + private: + std::string commentBOL() const override { return "// "; } + + void printPrologue(std::ostream& out) const override { + out << "namespace Spv\n{\n\n"; + out << indent() << "public static class Specification\n"; + out << indent() << "{\n"; + } + + void printEpilogue(std::ostream& out) const override { + out << indent() << "}\n"; + out << "}\n"; + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return indent(2) + std::string("public const uint ") + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + }; + + // D printer + class TPrinterD final : public TPrinter { + private: + std::string commentBeg() const override { return "/+\n"; } + std::string commentBOL() const override { return " + "; } + std::string commentEnd(bool isLast) const override { return " +/\n"; } + + void printPrologue(std::ostream& out) const override { + out << "module spv;\n\n"; + } + + void printEpilogue(std::ostream& out) const override { + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return "enum " + s + styleStr(style) + " : uint\n{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return std::string("}\n\n"); + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return std::string("enum uint ") + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + }; + + // Beef printer + class TPrinterBeef final : public TPrinter { + private: + std::string commentBOL() const override { return "// "; } + + void printPrologue(std::ostream& out) const override { + out << "namespace Spv\n{\n"; + out << indent() << "using System;\n\n"; + out << indent() << "public static class Specification\n"; + out << indent() << "{\n"; + } + + void printEpilogue(std::ostream& out) const override { + out << indent() << "}\n"; + out << "}\n"; + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return indent(2) + std::string("public const uint32 ") + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + }; + +} // namespace + +namespace spv { + void PrintAllHeaders() + { + // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here + std::vector> langInfo; + + langInfo.push_back(std::make_pair(ELangC, "spirv.h")); + langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); + langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); + langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); + langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); + langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); + langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); + langInfo.push_back(std::make_pair(ELangD, "spv.d")); + langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); + + for (const auto& lang : langInfo) { + std::ofstream out(lang.second, std::ios::out); + + if ((out.rdstate() & std::ifstream::failbit)) { + std::cerr << "Unable to open file: " << lang.second << std::endl; + } else { + PrintHeader(lang.first, out); + } + } + } + + // Print header for given language to given output stream + void PrintHeader(TLanguage lang, std::ostream& out) + { + typedef std::unique_ptr TPrinterPtr; + TPrinterPtr p; + + switch (lang) { + case ELangC: p = TPrinterPtr(new TPrinterC); break; + case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; + case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; + case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; + case ELangLua: p = TPrinterPtr(new TPrinterLua); break; + case ELangPython: p = TPrinterPtr(new TPrinterPython); break; + case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; + case ELangD: p = TPrinterPtr(new TPrinterD); break; + case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; + case ELangAll: PrintAllHeaders(); break; + default: + std::cerr << "Unknown language." << std::endl; + return; + } + + // Print the data in the requested format + if (p) + out << *p << std::endl; + + // object is auto-deleted + } + +} // namespace spv diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h new file mode 100644 index 00000000..6854f80b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h @@ -0,0 +1,55 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// +// Print headers for SPIR-V in several languages. +// + +#pragma once +#ifndef header +#define header + +#include + +namespace spv { + // Languages supported + enum TLanguage { + ELangC, // C + ELangCPP, // C++03 + ELangCPP11, // C++11 + ELangJSON, // JSON + ELangLua, // Lua + ELangPython, // Python + ELangCSharp, // CSharp + ELangD, // D + ELangBeef, // Beef + + ELangAll, // print headers in all languages to files + }; + + // Generate header for requested language + void PrintHeader(TLanguage, std::ostream&); +} // namespace spv + +#endif // header diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp new file mode 100644 index 00000000..0249cb3b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp @@ -0,0 +1,866 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#include +#include +#include +#include +#include +#include +#include + +#include "jsoncpp/dist/json/json.h" + +#include "jsonToSpirv.h" + +namespace { +// Returns true if the given string is a valid SPIR-V version. +bool validSpirvVersionString(const std::string s) { + return + s == "1.0" || + s == "1.1" || + s == "1.2" || + s == "1.3" || + s == "1.4" || + s == "1.5" || + s == "1.6"; +} + +// Returns true if the given string is a valid version +// specifier in the grammar file. +bool validSpirvVersionStringSpecifier(const std::string s) { + return s == "None" || validSpirvVersionString(s); +} +} // anonymous namespace + +namespace spv { + +bool IsLegacyDoublyEnabledInstruction(const std::string& instruction) { + static std::unordered_set allowed = { + "OpSubgroupBallotKHR", + "OpSubgroupFirstInvocationKHR", + "OpSubgroupAllKHR", + "OpSubgroupAnyKHR", + "OpSubgroupAllEqualKHR", + "OpSubgroupReadInvocationKHR", + "OpTraceRayKHR", + "OpExecuteCallableKHR", + "OpConvertUToAccelerationStructureKHR", + "OpIgnoreIntersectionKHR", + "OpTerminateRayKHR", + "OpTypeRayQueryKHR", + "OpRayQueryInitializeKHR", + "OpRayQueryTerminateKHR", + "OpRayQueryGenerateIntersectionKHR", + "OpRayQueryConfirmIntersectionKHR", + "OpRayQueryProceedKHR", + "OpRayQueryGetIntersectionTypeKHR", + "OpGroupIAddNonUniformAMD", + "OpGroupFAddNonUniformAMD", + "OpGroupFMinNonUniformAMD", + "OpGroupUMinNonUniformAMD", + "OpGroupSMinNonUniformAMD", + "OpGroupFMaxNonUniformAMD", + "OpGroupUMaxNonUniformAMD", + "OpGroupSMaxNonUniformAMD", + "OpFragmentMaskFetchAMD", + "OpFragmentFetchAMD", + "OpImageSampleFootprintNV", + "OpGroupNonUniformPartitionNV", + "OpWritePackedPrimitiveIndices4x8NV", + "OpReportIntersectionNV", + "OpReportIntersectionKHR", + "OpIgnoreIntersectionNV", + "OpTerminateRayNV", + "OpTraceNV", + "OpTraceMotionNV", + "OpTraceRayMotionNV", + "OpTypeAccelerationStructureNV", + "OpTypeAccelerationStructureKHR", + "OpExecuteCallableNV", + "OpTypeCooperativeMatrixNV", + "OpCooperativeMatrixLoadNV", + "OpCooperativeMatrixStoreNV", + "OpCooperativeMatrixMulAddNV", + "OpCooperativeMatrixLengthNV", + "OpBeginInvocationInterlockEXT", + "OpEndInvocationInterlockEXT", + "OpIsHelperInvocationEXT", + "OpConstantFunctionPointerINTEL", + "OpFunctionPointerCallINTEL", + "OpAssumeTrueKHR", + "OpExpectKHR", + "OpLoopControlINTEL", + "OpAliasDomainDeclINTEL", + "OpAliasScopeDeclINTEL", + "OpAliasScopeListDeclINTEL", + "OpReadPipeBlockingINTEL", + "OpWritePipeBlockingINTEL", + "OpFPGARegINTEL", + "OpRayQueryGetRayTMinKHR", + "OpRayQueryGetRayFlagsKHR", + "OpRayQueryGetIntersectionTKHR", + "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + "OpRayQueryGetIntersectionInstanceIdKHR", + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + "OpRayQueryGetIntersectionGeometryIndexKHR", + "OpRayQueryGetIntersectionPrimitiveIndexKHR", + "OpRayQueryGetIntersectionBarycentricsKHR", + "OpRayQueryGetIntersectionFrontFaceKHR", + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + "OpRayQueryGetIntersectionObjectRayDirectionKHR", + "OpRayQueryGetIntersectionObjectRayOriginKHR", + "OpRayQueryGetWorldRayDirectionKHR", + "OpRayQueryGetWorldRayOriginKHR", + "OpRayQueryGetIntersectionObjectToWorldKHR", + "OpRayQueryGetIntersectionWorldToObjectKHR", + "OpAtomicFAddEXT", + }; + return allowed.count(instruction) != 0; +} + +bool EnumValue::IsValid(OperandClass oc, const std::string& context) const +{ + bool result = true; + if (firstVersion.empty()) { + std::cerr << "Error: " << context << " " << name << " \"version\" must be set, probably to \"None\"" << std::endl; + result = false; + } else if (!validSpirvVersionStringSpecifier(firstVersion)) { + std::cerr << "Error: " << context << " " << name << " \"version\" is invalid: " << firstVersion << std::endl; + result = false; + } + if (!lastVersion.empty() && !validSpirvVersionString(lastVersion)) { + std::cerr << "Error: " << context << " " << name << " \"lastVersion\" is invalid: " << lastVersion << std::endl; + result = false; + } + + // When a feature is introduced by an extension, the firstVersion is set to + // "None". There are three cases: + // - A new capability should be guarded/enabled by the extension + // - A new instruction should be: + // - Guarded/enabled by a new capability. + // - Not enabled by *both* a capability and an extension. + // There are many existing instructions that are already like this, + // and we grandparent them as allowed. + // - Other enums fall into two cases: + // 1. The enum is part of a new operand kind introduced by the extension. + // In this case we rely on transitivity: The use of the operand occurs + // in a new instruction that itself is guarded; or as the operand of + // another operand that itself is (recursively) guarded. + // 2. The enum is a new case in an existing operand kind. This case + // should be guarded by a capability. However, we do not check this + // here. Checking it requires more context than we have here. + if (oc == OperandOpcode) { + const bool instruction_unusable = + (firstVersion == "None") && extensions.empty() && capabilities.empty(); + if (instruction_unusable) { + std::cerr << "Error: " << context << " " << name << " is not usable: " + << "its version is set to \"None\", and it is not enabled by a " + << "capability or extension. Guard it with a capability." + << std::endl; + result = false; + } + // Complain if an instruction is not in any core version and also enabled by + // both an extension and a capability. + // It's important to check the "not in any core version" case, because, + // for example, OpTerminateInvocation is in SPIR-V 1.6 *and* enabled by an + // extension, and guarded by the Shader capability. + const bool instruction_doubly_enabled = (firstVersion == "None") && + !extensions.empty() && + !capabilities.empty(); + if (instruction_doubly_enabled && !IsLegacyDoublyEnabledInstruction(name)) { + std::cerr << "Error: " << context << " " << name << " is doubly-enabled: " + << "it is enabled by both a capability and an extension. " + << "Guard it with a capability only." << std::endl; + result = false; + } + } + if (oc == OperandCapability) { + // If capability X lists capabilities Y and Z, then Y and Z are *enabled* + // when X is enabled. They are not *guards* on X's use. + // Only versions and extensions can guard a capability. + const bool capability_unusable = + (firstVersion == "None") && extensions.empty(); + if (capability_unusable) { + std::cerr << "Error: " << context << " " << name << " is not usable: " + << "its version is set to \"None\", and it is not enabled by " + << "an extension. Guard it with an extension." << std::endl; + result = false; + } + } + + return result; +} + +// The set of objects that hold all the instruction/operand +// parameterization information. +InstructionValues InstructionDesc; + +// The ordered list (in printing order) of printing classes +// (specification subsections). +PrintingClasses InstructionPrintingClasses; + +// Note: There is no entry for OperandOpcode. Use InstructionDesc instead. +EnumDefinition OperandClassParams[OperandOpcode]; +EnumValues SourceLanguageParams; +EnumValues ExecutionModelParams; +EnumValues AddressingParams; +EnumValues MemoryParams; +EnumValues ExecutionModeParams; +EnumValues StorageParams; +EnumValues SamplerAddressingModeParams; +EnumValues SamplerFilterModeParams; +EnumValues ImageFormatParams; +EnumValues ImageChannelOrderParams; +EnumValues ImageChannelDataTypeParams; +EnumValues ImageOperandsParams; +EnumValues FPFastMathParams; +EnumValues FPRoundingModeParams; +EnumValues FPDenormModeParams; +EnumValues FPOperationModeParams; +EnumValues QuantizationModesParams; +EnumValues OverflowModesParams; +EnumValues LinkageTypeParams; +EnumValues DecorationParams; +EnumValues BuiltInParams; +EnumValues DimensionalityParams; +EnumValues FuncParamAttrParams; +EnumValues AccessQualifierParams; +EnumValues GroupOperationParams; +EnumValues LoopControlParams; +EnumValues SelectionControlParams; +EnumValues FunctionControlParams; +EnumValues MemorySemanticsParams; +EnumValues MemoryAccessParams; +EnumValues ScopeParams; +EnumValues KernelEnqueueFlagsParams; +EnumValues KernelProfilingInfoParams; +EnumValues CapabilityParams; +EnumValues RayFlagsParams; +EnumValues RayQueryIntersectionParams; +EnumValues RayQueryCommittedIntersectionTypeParams; +EnumValues RayQueryCandidateIntersectionTypeParams; +EnumValues FragmentShadingRateParams; +EnumValues PackedVectorFormatParams; +EnumValues CooperativeMatrixOperandsParams; +EnumValues CooperativeMatrixLayoutParams; +EnumValues CooperativeMatrixUseParams; +EnumValues CooperativeMatrixReduceParams; +EnumValues TensorClampModeParams; +EnumValues TensorAddressingOperandsParams; +EnumValues InitializationModeQualifierParams; +EnumValues HostAccessQualifierParams; +EnumValues LoadCacheControlParams; +EnumValues StoreCacheControlParams; +EnumValues NamedMaximumNumberOfRegistersParams; +EnumValues MatrixMultiplyAccumulateOperandsParams; +EnumValues RawAccessChainOperandsParams; +EnumValues FPEncodingParams; +EnumValues CooperativeVectorMatrixLayoutParams; +EnumValues ComponentTypeParams; + +std::pair ReadFile(const std::string& path) +{ + std::ifstream fstream(path, std::ios::in); + if (fstream) { + std::string contents; + fstream.seekg(0, std::ios::end); + contents.reserve((unsigned int)fstream.tellg()); + fstream.seekg(0, std::ios::beg); + contents.assign((std::istreambuf_iterator(fstream)), + std::istreambuf_iterator()); + return std::make_pair(true, contents); + } + return std::make_pair(false, ""); +} + +struct ClassOptionality { + OperandClass type; + bool optional; +}; + +// Converts the |operandKind| and |quantifier| pair used to describe operands +// in the JSON grammar to OperandClass and optionality used in this repo. +ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier) +{ + assert(quantifier.empty() || quantifier == "?" || quantifier == "*"); + + if (operandKind == "IdRef") { + if (quantifier.empty()) + return {OperandId, false}; + else if (quantifier == "?") + return {OperandId, true}; + else + return {OperandVariableIds, false}; + } else if (operandKind == "LiteralInteger") { + if (quantifier.empty()) + return {OperandLiteralNumber, false}; + if (quantifier == "?") + return {OperandOptionalLiteral, true}; + else + return {OperandVariableLiterals, false}; + } else if (operandKind == "LiteralString") { + if (quantifier.empty()) + return {OperandLiteralString, false}; + else if (quantifier == "?") + return {OperandLiteralString, true}; + else { + return {OperandOptionalLiteralStrings, false}; + } + } else if (operandKind == "PairLiteralIntegerIdRef") { + // Used by OpSwitch in the grammar + return {OperandVariableLiteralId, false}; + } else if (operandKind == "PairIdRefLiteralInteger") { + // Used by OpGroupMemberDecorate in the grammar + return {OperandVariableIdLiteral, false}; + } else if (operandKind == "PairIdRefIdRef") { + // Used by OpPhi in the grammar + return {OperandVariableIds, false}; + } else { + OperandClass type = OperandNone; + if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") { + type = OperandMemorySemantics; + } else if (operandKind == "IdScope" || operandKind == "Scope") { + type = OperandScope; + } else if (operandKind == "LiteralExtInstInteger") { + type = OperandLiteralNumber; + } else if (operandKind == "LiteralSpecConstantOpInteger") { + type = OperandLiteralNumber; + } else if (operandKind == "LiteralContextDependentNumber") { + type = OperandAnySizeLiteralNumber; + } else if (operandKind == "LiteralFloat") { + type = OperandLiteralNumber; + } else if (operandKind == "SourceLanguage") { + type = OperandSource; + } else if (operandKind == "ExecutionModel") { + type = OperandExecutionModel; + } else if (operandKind == "AddressingModel") { + type = OperandAddressing; + } else if (operandKind == "MemoryModel") { + type = OperandMemory; + } else if (operandKind == "ExecutionMode") { + type = OperandExecutionMode; + } else if (operandKind == "StorageClass") { + type = OperandStorage; + } else if (operandKind == "Dim") { + type = OperandDimensionality; + } else if (operandKind == "SamplerAddressingMode") { + type = OperandSamplerAddressingMode; + } else if (operandKind == "SamplerFilterMode") { + type = OperandSamplerFilterMode; + } else if (operandKind == "ImageFormat") { + type = OperandSamplerImageFormat; + } else if (operandKind == "ImageChannelOrder") { + type = OperandImageChannelOrder; + } else if (operandKind == "ImageChannelDataType") { + type = OperandImageChannelDataType; + } else if (operandKind == "FPRoundingMode") { + type = OperandFPRoundingMode; + } else if (operandKind == "FPDenormMode") { + type = OperandFPDenormMode; + } else if (operandKind == "FPOperationMode") { + type = OperandFPOperationMode; + } else if (operandKind == "QuantizationModes") { + type = OperandQuantizationModes; + } else if (operandKind == "OverflowModes") { + type = OperandOverflowModes; + } else if (operandKind == "LinkageType") { + type = OperandLinkageType; + } else if (operandKind == "AccessQualifier") { + type = OperandAccessQualifier; + } else if (operandKind == "FunctionParameterAttribute") { + type = OperandFuncParamAttr; + } else if (operandKind == "Decoration") { + type = OperandDecoration; + } else if (operandKind == "BuiltIn") { + type = OperandBuiltIn; + } else if (operandKind == "GroupOperation") { + type = OperandGroupOperation; + } else if (operandKind == "KernelEnqueueFlags") { + type = OperandKernelEnqueueFlags; + } else if (operandKind == "KernelProfilingInfo") { + type = OperandKernelProfilingInfo; + } else if (operandKind == "Capability") { + type = OperandCapability; + } else if (operandKind == "ImageOperands") { + type = OperandImageOperands; + } else if (operandKind == "FPFastMathMode") { + type = OperandFPFastMath; + } else if (operandKind == "SelectionControl") { + type = OperandSelect; + } else if (operandKind == "LoopControl") { + type = OperandLoop; + } else if (operandKind == "FunctionControl") { + type = OperandFunction; + } else if (operandKind == "MemoryAccess") { + type = OperandMemoryOperands; + } else if (operandKind == "RayFlags") { + type = OperandRayFlags; + } else if (operandKind == "RayQueryIntersection") { + type = OperandRayQueryIntersection; + } else if (operandKind == "RayQueryCommittedIntersectionType") { + type = OperandRayQueryCommittedIntersectionType; + } else if (operandKind == "RayQueryCandidateIntersectionType") { + type = OperandRayQueryCandidateIntersectionType; + } else if (operandKind == "FragmentShadingRate") { + type = OperandFragmentShadingRate; + } else if (operandKind == "PackedVectorFormat") { + type = OperandPackedVectorFormat; + } else if (operandKind == "CooperativeMatrixOperands") { + type = OperandCooperativeMatrixOperands; + } else if (operandKind == "TensorAddressingOperands") { + type = OperandTensorAddressingOperands; + } else if (operandKind == "CooperativeMatrixLayout") { + type = OperandCooperativeMatrixLayout; + } else if (operandKind == "CooperativeMatrixUse") { + type = OperandCooperativeMatrixUse; + } else if (operandKind == "CooperativeMatrixReduce") { + type = OperandCooperativeMatrixReduce; + } else if (operandKind == "TensorClampMode") { + type = OperandTensorClampMode; + } else if (operandKind == "InitializationModeQualifier") { + type = OperandInitializationModeQualifier; + } else if (operandKind == "HostAccessQualifier") { + type = OperandHostAccessQualifier; + } else if (operandKind == "LoadCacheControl") { + type = OperandLoadCacheControl; + } else if (operandKind == "StoreCacheControl") { + type = OperandStoreCacheControl; + } else if (operandKind == "NamedMaximumNumberOfRegisters") { + type = OperandNamedMaximumNumberOfRegisters; + } else if (operandKind == "MatrixMultiplyAccumulateOperands") { + type = OperandMatrixMultiplyAccumulateOperands; + } else if (operandKind == "RawAccessChainOperands") { + type = OperandRawAccessChainOperands; + } else if (operandKind == "FPEncoding") { + type = OperandFPEncoding; + } else if (operandKind == "CooperativeVectorMatrixLayout") { + type = OperandCooperativeVectorMatrixLayout; + } else if (operandKind == "ComponentType") { + type = OperandComponentType; + } + + if (type == OperandNone) { + std::cerr << "Unhandled operand kind found: " << operandKind << std::endl; + exit(1); + } + return {type, !quantifier.empty()}; + } +} + +bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult) +{ + if (str == "IdResultType") + return *isType = true; + if (str == "IdResult") + return *isResult = true; + return false; +} + +// Given a number string, returns the position of the only bits set in the number. +// So it requires the number is a power of two. +unsigned int NumberStringToBit(const std::string& str) +{ + char* parseEnd; + unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16); + assert(!(value & (value - 1)) && "input number is not a power of 2"); + unsigned int bit = 0; + for (; value; value >>= 1) ++bit; + return bit; +} + +void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders) +{ + // only do this once. + static bool initialized = false; + if (initialized) + return; + initialized = true; + + size_t errorCount = 0; + + // Read the JSON grammar file. + bool fileReadOk = false; + std::string content; + std::tie(fileReadOk, content) = ReadFile(jsonPath); + if (!fileReadOk) { + std::cerr << "Failed to read JSON grammar file: " + << jsonPath << std::endl; + exit(1); + } + + // Decode the JSON grammar file. + Json::Reader reader; + Json::Value root; + if (!reader.parse(content, root)) { + std::cerr << "Failed to parse JSON grammar:\n" + << reader.getFormattedErrorMessages(); + exit(1); + } + + // Layouts for all instructions. + + // A lambda for returning capabilities from a JSON object as strings. + const auto getCaps = [](const Json::Value& object) { + EnumCaps result; + const auto& caps = object["capabilities"]; + if (!caps.empty()) { + assert(caps.isArray()); + for (const auto& cap : caps) { + result.emplace_back(cap.asString()); + } + } + return result; + }; + + // A lambda for returning extensions from a JSON object as strings. + const auto getExts = [](const Json::Value& object) { + Extensions result; + const auto& exts = object["extensions"]; + if (!exts.empty()) { + assert(exts.isArray()); + for (const auto& ext : exts) { + result.emplace_back(ext.asString()); + } + } + return result; + }; + + const auto getAliases = [](const Json::Value& object) { + Aliases result; + const auto& aliases = object["aliases"]; + if (!aliases.empty()) { + assert(aliases.isArray()); + for (const auto& alias : aliases) { + result.emplace_back(alias.asString()); + } + } + return result; + }; + + // set up the printing classes + std::unordered_set tags; // short-lived local for error checking below + const Json::Value printingClasses = root["instruction_printing_class"]; + for (const auto& printingClass : printingClasses) { + if (printingClass["tag"].asString().size() > 0) + tags.insert(printingClass["tag"].asString()); // just for error checking + else { + std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl; + std::exit(1); + } + if (buildingHeaders || printingClass["tag"].asString() != "@exclude") { + InstructionPrintingClasses.push_back({printingClass["tag"].asString(), + printingClass["heading"].asString()}); + } + } + + // process the instructions + const Json::Value insts = root["instructions"]; + unsigned maxOpcode = 0; + std::string maxName = ""; + bool maxCore = false; + bool firstOpcode = true; + for (const auto& inst : insts) { + const auto printingClass = inst["class"].asString(); + if (printingClass.size() == 0) { + std::cerr << "Error: " << inst["opname"].asString() + << " requires a non-empty printing \"class\" tag" << std::endl; + std::exit(1); + } + if (!buildingHeaders && printingClass == "@exclude") + continue; + if (tags.find(printingClass) == tags.end()) { + std::cerr << "Error: " << inst["opname"].asString() + << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\"" + << std::endl; + std::exit(1); + } + const auto opcode = inst["opcode"].asUInt(); + const std::string name = inst["opname"].asString(); + std::string version = inst["version"].asString(); + if (firstOpcode) { + maxOpcode = opcode; + maxName = name; + maxCore = version != "None"; + firstOpcode = false; + } else { + if (maxOpcode > opcode) { + std::cerr << "Error: " << name + << " is out of order. It follows the instruction with opcode " << maxOpcode + << std::endl; + std::exit(1); + } else if (maxOpcode == opcode) { + std::cerr << "Error: " << name << " is an alias of " << maxName + << ". Use \"aliases\" instead." << std::endl; + std::exit(1); + } else { + maxOpcode = opcode; + maxName = name; + maxCore = version != "None"; + } + } + Aliases aliases = getAliases(inst); + EnumCaps caps = getCaps(inst); + std::string lastVersion = inst["lastVersion"].asString(); + Extensions exts = getExts(inst); + OperandParameters operands; + bool defResultId = false; + bool defTypeId = false; + for (const auto& operand : inst["operands"]) { + const std::string kind = operand["kind"].asString(); + const std::string quantifier = operand.get("quantifier", "").asString(); + const std::string doc = operand.get("name", "").asString(); + if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) { + const auto p = ToOperandClassAndOptionality(kind, quantifier); + operands.push(p.type, doc, p.optional); + } + } + InstructionDesc.emplace_back( + std::move(EnumValue(opcode, name, std::move(aliases), + std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), + std::move(operands))), + printingClass, defTypeId, defResultId); + if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) { + errorCount++; + } + } + + // Specific additional context-dependent operands + + // Populate dest with EnumValue objects constructed from source. + const auto populateEnumValues = [&getCaps,&getAliases,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) { + // A lambda for determining the numeric value to be used for a given + // enumerant in JSON form, and whether that value is a 0 in a bitfield. + auto getValue = [&bitEnum](const Json::Value& enumerant) { + std::pair result{0u,false}; + if (!bitEnum) { + result.first = enumerant["value"].asUInt(); + } else { + const unsigned int bit = NumberStringToBit(enumerant["value"].asString()); + if (bit == 0) + result.second = true; + else + result.first = bit - 1; // This is the *shift* amount. + } + return result; + }; + + unsigned maxValue = 0; + std::string maxName = ""; + bool maxCore = false; + bool firstValue = true; + for (const auto& enumerant : source["enumerants"]) { + unsigned value; + bool skip_zero_in_bitfield; + std::tie(value, skip_zero_in_bitfield) = getValue(enumerant); + std::string name = enumerant["enumerant"].asString(); + std::string version = enumerant["version"].asString(); + if (skip_zero_in_bitfield) + continue; + if (firstValue) { + maxValue = value; + maxName = name; + maxCore = version != "None"; + firstValue = false; + } else { + if (maxValue > value) { + std::cerr << "Error: " << source["kind"] << " enumerant " << name + << " is out of order. It has value " << value + << " but follows the enumerant with value " << maxValue << std::endl; + std::exit(1); + } else if (maxValue == value ) { + std::cerr << "Error: " << source["kind"] << " enumerant " << name + << " is an alias of " << maxName << ". Use \"aliases\" instead." << std::endl; + std::exit(1); + } else { + maxValue = value; + maxName = name; + maxCore = version != "None"; + } + } + Aliases aliases = getAliases(enumerant); + EnumCaps caps(getCaps(enumerant)); + std::string lastVersion = enumerant["lastVersion"].asString(); + Extensions exts(getExts(enumerant)); + OperandParameters params; + const Json::Value& paramsJson = enumerant["parameters"]; + if (!paramsJson.empty()) { // This enumerant has parameters. + assert(paramsJson.isArray()); + for (const auto& param : paramsJson) { + const std::string kind = param["kind"].asString(); + const std::string doc = param.get("name", "").asString(); + const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required! + params.push(p.type, doc); + } + } + dest->emplace_back( + value, enumerant["enumerant"].asString(), std::move(aliases), + std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params)); + } + }; + + const auto establishOperandClass = [&populateEnumValues,&errorCount]( + const std::string& enumName, spv::OperandClass operandClass, + spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) { + assert(category == "BitEnum" || category == "ValueEnum"); + bool bitEnum = (category == "BitEnum"); + if (!operandEnum["version"].empty()) { + std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl; + errorCount++; + } + populateEnumValues(enumValues, operandEnum, bitEnum); + const std::string errContext = "enum " + enumName; + for (const auto& e: *enumValues) { + if (!e.IsValid(operandClass, errContext)) { + errorCount++; + } + } + OperandClassParams[operandClass].set(enumName, enumValues, bitEnum); + }; + + const Json::Value operandEnums = root["operand_kinds"]; + for (const auto& operandEnum : operandEnums) { + const std::string enumName = operandEnum["kind"].asString(); + const std::string category = operandEnum["category"].asString(); + if (enumName == "SourceLanguage") { + establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category); + } else if (enumName == "Decoration") { + establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category); + } else if (enumName == "ExecutionMode") { + establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category); + } else if (enumName == "Capability") { + establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category); + } else if (enumName == "AddressingModel") { + establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category); + } else if (enumName == "MemoryModel") { + establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category); + } else if (enumName == "MemorySemantics") { + establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category); + } else if (enumName == "ExecutionModel") { + establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category); + } else if (enumName == "StorageClass") { + establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category); + } else if (enumName == "SamplerAddressingMode") { + establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category); + } else if (enumName == "SamplerFilterMode") { + establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category); + } else if (enumName == "ImageFormat") { + establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category); + } else if (enumName == "ImageChannelOrder") { + establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category); + } else if (enumName == "ImageChannelDataType") { + establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category); + } else if (enumName == "ImageOperands") { + establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category); + } else if (enumName == "FPFastMathMode") { + establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category); + } else if (enumName == "FPRoundingMode") { + establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category); + } else if (enumName == "FPDenormMode") { + establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category); + } else if (enumName == "FPOperationMode") { + establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category); + } else if (enumName == "QuantizationModes") { + establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category); + } else if (enumName == "OverflowModes") { + establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category); + } else if (enumName == "LinkageType") { + establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category); + } else if (enumName == "FunctionParameterAttribute") { + establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category); + } else if (enumName == "AccessQualifier") { + establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category); + } else if (enumName == "BuiltIn") { + establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category); + } else if (enumName == "SelectionControl") { + establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category); + } else if (enumName == "LoopControl") { + establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category); + } else if (enumName == "FunctionControl") { + establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category); + } else if (enumName == "Dim") { + establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category); + } else if (enumName == "MemoryAccess") { + establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category); + } else if (enumName == "Scope") { + establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category); + } else if (enumName == "GroupOperation") { + establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category); + } else if (enumName == "KernelEnqueueFlags") { + establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category); + } else if (enumName == "KernelProfilingInfo") { + establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category); + } else if (enumName == "RayFlags") { + establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category); + } else if (enumName == "RayQueryIntersection") { + establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category); + } else if (enumName == "RayQueryCommittedIntersectionType") { + establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category); + } else if (enumName == "RayQueryCandidateIntersectionType") { + establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category); + } else if (enumName == "FragmentShadingRate") { + establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category); + } else if (enumName == "PackedVectorFormat") { + establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category); + } else if (enumName == "CooperativeMatrixOperands") { + establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category); + } else if (enumName == "TensorAddressingOperands") { + establishOperandClass(enumName, OperandTensorAddressingOperands, &TensorAddressingOperandsParams, operandEnum, category); + } else if (enumName == "CooperativeMatrixLayout") { + establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category); + } else if (enumName == "CooperativeMatrixUse") { + establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category); + } else if (enumName == "CooperativeMatrixReduce") { + establishOperandClass(enumName, OperandCooperativeMatrixReduce, &CooperativeMatrixReduceParams, operandEnum, category); + } else if (enumName == "TensorClampMode") { + establishOperandClass(enumName, OperandTensorClampMode, &TensorClampModeParams, operandEnum, category); + } else if (enumName == "InitializationModeQualifier") { + establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category); + } else if (enumName == "HostAccessQualifier") { + establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category); + } else if (enumName == "LoadCacheControl") { + establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category); + } else if (enumName == "StoreCacheControl") { + establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category); + } else if (enumName == "NamedMaximumNumberOfRegisters") { + establishOperandClass(enumName, OperandNamedMaximumNumberOfRegisters, &NamedMaximumNumberOfRegistersParams, operandEnum, category); + } else if (enumName == "MatrixMultiplyAccumulateOperands") { + establishOperandClass(enumName, OperandMatrixMultiplyAccumulateOperands, &MatrixMultiplyAccumulateOperandsParams, operandEnum, category); + } else if (enumName == "RawAccessChainOperands") { + establishOperandClass(enumName, OperandRawAccessChainOperands, &RawAccessChainOperandsParams, operandEnum, category); + } else if (enumName == "FPEncoding") { + establishOperandClass(enumName, OperandFPEncoding, &FPEncodingParams, operandEnum, category); + } else if (enumName == "CooperativeVectorMatrixLayout") { + establishOperandClass(enumName, OperandCooperativeVectorMatrixLayout, &CooperativeVectorMatrixLayoutParams, operandEnum, category); + } else if (enumName == "ComponentType") { + establishOperandClass(enumName, OperandComponentType, &ComponentTypeParams, operandEnum, category); + } + } + + if (errorCount > 0) { + std::exit(1); + } +} + +}; // end namespace spv diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h new file mode 100644 index 00000000..a91d0909 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h @@ -0,0 +1,322 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#pragma once +#ifndef JSON_TO_SPIRV +#define JSON_TO_SPIRV + +#include +#include +#include +#include + +namespace spv { + + // Reads the file in the given |path|. Returns true and the contents of the +// file on success; otherwise, returns false and an empty string. +std::pair ReadFile(const std::string& path); + +// Fill in all the parameters +void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders); + +// For parameterizing operands. +// The ordering here affects the printing order in the SPIR-V specification. +// Please add new operand classes at the end. +enum OperandClass { + OperandNone, + OperandId, + OperandVariableIds, + OperandOptionalLiteral, + OperandOptionalLiteralString, + OperandOptionalLiteralStrings, + OperandVariableLiterals, + OperandVariableIdLiteral, + OperandVariableLiteralId, + OperandAnySizeLiteralNumber, + OperandLiteralNumber, + OperandLiteralString, + OperandSource, + OperandExecutionModel, + OperandAddressing, + OperandMemory, + OperandExecutionMode, + OperandStorage, + OperandDimensionality, + OperandSamplerAddressingMode, + OperandSamplerFilterMode, + OperandSamplerImageFormat, + OperandImageChannelOrder, + OperandImageChannelDataType, + OperandImageOperands, + OperandFPFastMath, + OperandFPRoundingMode, + OperandLinkageType, + OperandAccessQualifier, + OperandFuncParamAttr, + OperandDecoration, + OperandBuiltIn, + OperandSelect, + OperandLoop, + OperandFunction, + OperandMemorySemantics, + OperandMemoryOperands, + OperandScope, + OperandGroupOperation, + OperandKernelEnqueueFlags, + OperandKernelProfilingInfo, + OperandCapability, + OperandRayFlags, + OperandRayQueryIntersection, + OperandRayQueryCommittedIntersectionType, + OperandRayQueryCandidateIntersectionType, + OperandFragmentShadingRate, + OperandFPDenormMode, + OperandFPOperationMode, + OperandQuantizationModes, + OperandOverflowModes, + OperandPackedVectorFormat, + OperandCooperativeMatrixOperands, + OperandCooperativeMatrixLayout, + OperandCooperativeMatrixUse, + OperandCooperativeMatrixReduce, + OperandTensorClampMode, + OperandTensorAddressingOperands, + OperandInitializationModeQualifier, + OperandHostAccessQualifier, + OperandLoadCacheControl, + OperandStoreCacheControl, + OperandNamedMaximumNumberOfRegisters, + OperandMatrixMultiplyAccumulateOperands, + OperandRawAccessChainOperands, + OperandFPEncoding, + OperandCooperativeVectorMatrixLayout, + OperandComponentType, + + OperandOpcode, + + OperandCount +}; + +// For direct representation of the JSON grammar "instruction_printing_class". +struct PrintingClass { + std::string tag; + std::string heading; +}; +using PrintingClasses = std::vector; + +// Any specific enum can have a set of capabilities that allow it: +typedef std::vector EnumCaps; + +// A set of extensions. +typedef std::vector Extensions; + +// A set of aliases. +typedef std::vector Aliases; + +// Parameterize a set of operands with their OperandClass(es) and descriptions. +class OperandParameters { +public: + OperandParameters() { } + void push(OperandClass oc, const std::string& d, bool opt = false) + { + opClass.push_back(oc); + desc.push_back(d); + optional.push_back(opt); + } + void setOptional(); + OperandClass getClass(int op) const { return opClass[op]; } + const char* getDesc(int op) const { return desc[op].c_str(); } + bool isOptional(int op) const { return optional[op]; } + int getNum() const { return (int)opClass.size(); } + +protected: + std::vector opClass; + std::vector desc; + std::vector optional; +}; + +// An ordered sequence of EValue. We'll preserve the order found in the +// JSON file. You can look up a value by enum or by name. If there are +// duplicate values, then take the first. We assume names are unique. +// The EValue must have an unsigned |value| field and a string |name| field. +template +class EnumValuesContainer { +public: + using ContainerType = std::vector; + using iterator = typename ContainerType::iterator; + using const_iterator = typename ContainerType::const_iterator; + + EnumValuesContainer() {} + + // Constructs an EValue in place as a new element at the end of the + // sequence. + template + void emplace_back(Args&&... args) { + values.emplace_back(std::forward(args)...); + } + + // Returns the first EValue in the sequence with the given value. + // More than one EValue might have the same value. + EValue& operator[](unsigned value) { + auto where = std::find_if(begin(), end(), [&value](const EValue& e) { + return value == e.value; + }); + assert((where != end()) && "Could not find enum in the enum list"); + return *where; + } + // gets *all* entries for the value, including the first one + void gatherAliases(unsigned value, std::vector& aliases) { + std::for_each(begin(), end(), [&](EValue& e) { + if (value == e.value) + aliases.push_back(&e);}); + } + // Returns the EValue with the given name. We assume uniqueness + // by name. + EValue& at(std::string name) { + auto where = std::find_if(begin(), end(), [&name](const EValue& e) { + return name == e.name; + }); + assert((where != end()) && "Could not find name in the enum list"); + return *where; + } + + iterator begin() { return values.begin(); } + iterator end() { return values.end(); } + EValue& back() { return values.back(); } + +private: + ContainerType values; +}; + +// A single enumerant value. Corresponds to a row in an enumeration table +// in the spec. +class EnumValue { +public: + EnumValue() : value(0), desc(nullptr) {} + EnumValue(unsigned int the_value, const std::string& the_name, Aliases&& the_aliases, EnumCaps&& the_caps, + const std::string& the_firstVersion, const std::string& the_lastVersion, + Extensions&& the_extensions, OperandParameters&& the_operands) : + value(the_value), name(the_name), aliases(std::move(the_aliases)), capabilities(std::move(the_caps)), + firstVersion(std::move(the_firstVersion)), lastVersion(std::move(the_lastVersion)), + extensions(std::move(the_extensions)), operands(std::move(the_operands)), desc(nullptr) { } + + bool hasAliases() const { return !aliases.empty(); } + + // For ValueEnum, the value from the JSON file. + // For BitEnum, the index of the bit position represented by this mask. + // (That is, what you shift 1 by to get the mask.) + unsigned value; + std::string name; + Aliases aliases; + EnumCaps capabilities; + std::string firstVersion; + std::string lastVersion; + // A feature only be enabled by certain extensions. + // An empty list means the feature does not require an extension. + // Normally, only Capability enums are enabled by extension. In turn, + // other enums and instructions are enabled by those capabilities. + Extensions extensions; + OperandParameters operands; + const char* desc; + + // Returns true if this enum is valid, in isolation. + // Otherwise emits a diagnostic to std::cerr and returns false. + bool IsValid(OperandClass oc, const std::string& context) const; +}; + +using EnumValues = EnumValuesContainer; + +// Parameterize a set of enumerants that form an enum +class EnumDefinition { +public: + EnumDefinition() : + desc(0), bitmask(false), enumValues(nullptr) { } + void set(const std::string& enumName, EnumValues* enumValuesArg, bool mask = false) + { + codeName = enumName; + bitmask = mask; + enumValues = enumValuesArg; + } + // Returns the first EnumValue in the sequence with the given value. + // More than one EnumValue might have the same value. Only valid + // if enumValues has been populated. + EnumValue& operator[](unsigned value) { + assert(enumValues != nullptr); + return (*enumValues)[value]; + } + // Returns the name of the first EnumValue with the given value. + // Assumes enumValues has been populated. + const char* getName(unsigned value) { + return (*this)[value].name.c_str(); + } + + using iterator = EnumValues::iterator; + iterator begin() { return enumValues->begin(); } + iterator end() { return enumValues->end(); } + + std::string codeName; // name to use when declaring headers for code + const char* desc; + bool bitmask; // true if these enumerants combine into a bitmask + EnumValues* enumValues; // parameters for each individual enumerant +}; + +// Parameterize an instruction's logical format, including its known set of operands, +// per OperandParameters above. +class InstructionValue : public EnumValue { +public: + InstructionValue(EnumValue&& e, const std::string& printClass, bool has_type, bool has_result) + : EnumValue(std::move(e)), + printingClass(printClass), + opDesc("TBD."), + typePresent(has_type), + resultPresent(has_result) { } + InstructionValue(const InstructionValue& v) + { + *this = v; + } + + bool hasResult() const { return resultPresent != 0; } + bool hasType() const { return typePresent != 0; } + + std::string printingClass; + const char* opDesc; + +protected: + int typePresent : 1; + int resultPresent : 1; +}; + +using InstructionValues = EnumValuesContainer; + +// Parameterization info for all instructions. +extern InstructionValues InstructionDesc; +extern PrintingClasses InstructionPrintingClasses; + +// These hold definitions of the enumerants used for operands. +// This is indexed by OperandClass, but not including OperandOpcode. +extern EnumDefinition OperandClassParams[]; + +}; // end namespace spv + +#endif // JSON_TO_SPIRV diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json-forwards.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json-forwards.h new file mode 100644 index 00000000..901bc063 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json-forwards.h @@ -0,0 +1,255 @@ +/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json-forwards.h" +/// This header provides forward declaration for all JsonCpp types. + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED +# define JSON_FORWARD_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json.h b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json.h new file mode 100644 index 00000000..7145b1a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/json/json.h @@ -0,0 +1,2017 @@ +/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGATED_H_INCLUDED +# define JSON_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +// DO NOT EDIT. This file is generated by CMake from "version" +// and "version.h.in" files. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "1.6.2" +# define JSONCPP_VERSION_MAJOR 1 +# define JSONCPP_VERSION_MINOR 6 +# define JSONCPP_VERSION_PATCH 2 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception; +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError; +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError; + +/// used internally +void throwRuntimeError(std::string const& msg); +/// used internally +void throwLogicError(std::string const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). + static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* beginValue, const char* endValue); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value& operator=(Value other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + std::string asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** str, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const char* key, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30 + Value const* find(char const* key, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* key, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const std::string& key); + /// Same as removeMember(const char* key, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(std::string const& key, Value* removed); + /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(const char* key, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const std::string& key) const; + /// Same as isMember(std::string const& key)const + bool isMember(const char* key, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(size_t start); + void setOffsetLimit(size_t limit); + size_t getOffsetStart() const; + size_t getOffsetLimit() const; + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + size_t start_; + size_t limit_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + std::string name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const std::string& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const std::string& message, const Value& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) = 0; + + class Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + std::string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + virtual ~CharReaderBuilder(); + + virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + std::istream&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + virtual ~StreamWriterBuilder(); + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; + bool dropNullPlaceholders_; + bool omitEndingLineFeed_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp new file mode 100644 index 00000000..49569f91 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/jsoncpp/dist/jsoncpp.cpp @@ -0,0 +1,5124 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json/json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string codePointToUTF8(unsigned int cp) { + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = char(value % 10) + '0'; + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +static int const stackLimit_g = 1000; +static int stackDepth_g = 0; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L +typedef std::unique_ptr CharReaderPtr; +#else +typedef std::auto_ptr CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + document_ = document; + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_g = 0; // Yes, this is bad coding, but options are limited. + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // This is a non-reentrant way to support a stackLimit. Terrible! + // But this deprecated class has a security problem: Bad input can + // cause a seg-fault. This seems like a fair, binary-compatible way + // to prevent the problem. + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_g; + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_g; + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { + std::string normalized; + normalized.reserve(end - begin); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} + +bool Reader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +Reader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const std::string& message) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + OurFeatures(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures::OurFeatures() + : allowComments_(true), strictRoot_(false) + , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) + , allowSingleQuotes_(false) + , failIfExtra_(false) +{ +} + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + std::string getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + bool pushError(const Value& value, const std::string& message); + bool pushError(const Value& value, const std::string& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_ = 0; + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_; + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_; + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const std::string& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void OurReader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : 0; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : 0; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : 0; + } +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + std::string msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + std::string decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +OurReader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +std::string OurReader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const std::string& message) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { + size_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool OurReader::good() const { + return !errors_.size(); +} + + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, std::istream& sin, + Value* root, std::string* errs) +{ + std::ostringstream ssin; + ssin << sin.rdbuf(); + std::string doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +std::istream& operator>>(std::istream& sin, Value& root) { + CharReaderBuilder b; + std::string errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + errs.c_str()); + + throwRuntimeError("reader error"); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() + : current_(), isNull_(true) { +} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { + return current_->second; +} + +void ValueIteratorBase::increment() { + ++current_; +} + +void ValueIteratorBase::decrement() { + --current_; +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +std::string ValueIteratorBase::name() const { + char const* key; + char const* end; + key = memberName(&end); + if (!key) return std::string(); + return std::string(key, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* name = (*current_).first.data(); + return name ? name : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* name = (*current_).first.data(); + if (!name) { + *end = NULL; + return NULL; + } + *end = name + (*current_).first.length(); + return name; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t +#include // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +const unsigned char& kNullRef = kNull[0]; +const Value& Value::null = reinterpret_cast(kNullRef); +const Value& Value::nullRef = null; + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + size_t length) { + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (size_t)Value::maxInt) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + sizeof(unsigned) + 1U; + char* newString = static_cast(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = strlen(prefixed); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +static inline void releaseStringValue(char* value) { free(value); } + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +Exception::Exception(std::string const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(std::string const& msg) + : Exception(msg) +{} +LogicError::LogicError(std::string const& msg) + : Exception(msg) +{} +void throwRuntimeError(std::string const& msg) +{ + throw RuntimeError(msg); +} +void throwLogicError(std::string const& msg) +{ + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) {} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_); + comment_ = 0; + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} + +Value::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate) + : cstr_(str) +{ + // allocate != duplicate + storage_.policy_ = allocate; + storage_.length_ = length; +} + +Value::CZString::CZString(const CZString& other) + : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_) +{ + storage_.policy_ = (other.cstr_ + ? (other.storage_.policy_ == noDuplication + ? noDuplication : duplicate) + : other.storage_.policy_); + storage_.length_ = other.storage_.length_; +} + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) + releaseStringValue(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) { + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +Value::Value(const std::string& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(Value const& other) + : type_(other.type_), allocated_(false) + , + comments_(0), start_(other.start_), limit_(other.limit_) +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.string_ = other.value_.string_; + allocated_ = false; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); + } + } +} + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releaseStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min(this_len, other_len); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +bool Value::getString(char const** str, char const** end) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *end = *str + length; + return true; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return std::string(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + return value_.real_ ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + assert(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullRef); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullRef; + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullRef; + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType type, bool allocated) { + type_ = type; + allocated_ = allocated; + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* end) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(end-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullRef); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullRef ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* key, char const* end) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast(end-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullRef; + return *found; +} +Value const& Value::operator[](std::string const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullRef; + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const std::string& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullRef; + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(char const* key, char const* end, Value const& defaultValue) const +{ + Value const* found = find(key, end); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(std::string const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +bool Value::removeMember(const char* key, const char* end, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast(end-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(std::string const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullRef; + + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const std::string& key) +{ + return removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString key(i); + (*value_.map_)[key] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* key, char const* end) const +{ + Value const* value = find(key, end); + return NULL != value; +} +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(std::string const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(std::string((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == realValue || isIntegral(); } + +bool Value::isNumeric() const { return isIntegral() || isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const std::string& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(size_t start) { start_ = start; } + +void Value::setOffsetLimit(size_t limit) { limit_ = limit; } + +size_t Value::getOffsetStart() const { return start_; } + +size_t Value::getOffsetLimit() const { return limit_; } + +std::string Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const std::string& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::string& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullRef) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#include +#define isfinite finite +#else +#include +#define isfinite std::isfinite +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#elif defined(__ANDROID__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#define snprintf std::snprintf +#endif + +#if defined(__BORLANDC__) +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L +typedef std::unique_ptr StreamWriterPtr; +#else +typedef std::auto_ptr StreamWriterPtr; +#endif + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +std::string valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(LargestUInt(value), current); + if (isNegative) + *--current = '-'; + assert(current >= buffer); + return current; +} + +std::string valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +std::string valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +std::string valueToString(double value) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[32]; + int len = -1; + +// Print into the buffer. We need not request the alternative representation +// that always has a decimal point because JSON doesn't distingish the +// concepts of reals and integers. +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with + // visual studio 2005 to + // avoid warning. +#if defined(WINCE) + len = _snprintf(buffer, sizeof(buffer), "%.17g", value); +#else + len = sprintf_s(buffer, sizeof(buffer), "%.17g", value); +#endif +#else + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), "%.17g", value); + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } +#endif + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} + +std::string valueToString(bool value) { return value ? "true" : "false"; } + +std::string valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static std::string valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +std::string FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::string& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), name.length()); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +std::string StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(std::ostream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol); + virtual int write(Value const& root, std::ostream* sout); +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(std::string const& value); + void writeIndent(); + void writeWithIndent(std::string const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + CommentStyle::Enum cs_; + std::string colonSymbol_; + std::string nullSymbol_; + std::string endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + std::string const& indentation, + CommentStyle::Enum cs, + std::string const& colonSymbol, + std::string const& nullSymbol, + std::string const& endingLineFeedSymbol) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL is possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + std::string const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), name.length())); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ", "; + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(std::string const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const std::string& comment = root.getComment(commentBefore); + std::string::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + std::string indentation = settings_["indentation"].asString(); + std::string cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + std::string colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + std::string nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + std::string endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol); +} +static void getValidWriterKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + std::string const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](std::string key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + //! [StreamWriterBuilderDefaults] +} + +std::string writeString(StreamWriter::Factory const& builder, Value const& root) { + std::ostringstream sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +std::ostream& operator<<(std::ostream& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp new file mode 100644 index 00000000..6e6a03b3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2014-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +//#include +#include +#include + +#include "jsonToSpirv.h" +#include "header.h" + +// Command-line options +enum TOptions { + EOptionNone = 0x000, + EOptionPrintHeader = 0x008, +}; + +std::string jsonPath; +int Options; +spv::TLanguage Language; + +void Usage() +{ + printf("Usage: spirv option [file]\n" + "\n" + " -h print header for given language to stdout, from one of:\n" + " C - C99 header\n" + " C++ - C++03 or greater header (also accepts C++03)\n" + " C++11 - C++11 or greater header\n" + " JSON - JSON format data\n" + " Lua - Lua module\n" + " Python - Python module (also accepts Py)\n" + " C# - C# module (also accepts CSharp)\n" + " D - D module\n" + " Beef - Beef module\n" + " -H print header in all supported languages to files in current directory\n" + ); +} + +std::string tolower_s(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; +} + +bool ProcessArguments(int argc, char* argv[]) +{ + argc--; + argv++; + for (; argc >= 1; argc--, argv++) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'H': + Options |= EOptionPrintHeader; + Language = spv::ELangAll; + break; + case 'h': { + if (argc < 2) + return false; + + Options |= EOptionPrintHeader; + const std::string language(tolower_s(argv[1])); + + if (language == "c") { + Language = spv::ELangC; + } else if (language == "c++" || language == "c++03") { + Language = spv::ELangCPP; + } else if (language == "c++11") { + Language = spv::ELangCPP11; + } else if (language == "json") { + Language = spv::ELangJSON; + } else if (language == "lua") { + Language = spv::ELangLua; + } else if (language == "python" || language == "py") { + Language = spv::ELangPython; + } else if (language == "c#" || language == "csharp") { + Language = spv::ELangCSharp; + } else if (language == "d") { + Language = spv::ELangD; + } else if (language == "beef") { + Language = spv::ELangBeef; + } else + return false; + + return true; + } + default: + return false; + } + } else { + jsonPath = std::string(argv[0]); + } + } + + return true; +} + +int main(int argc, char* argv[]) +{ + if (argc < 2 || ! ProcessArguments(argc, argv)) { + Usage(); + return 1; + } + + spv::jsonToSpirv(jsonPath, (Options & EOptionPrintHeader) != 0); + if (Options & EOptionPrintHeader) + spv::PrintHeader(Language, std::cout); + + return 0; +} diff --git a/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.h b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.h new file mode 100644 index 00000000..bcc8332d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.h @@ -0,0 +1,1045 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ +#define INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +#include +#include + +#if defined(SPIRV_TOOLS_SHAREDLIB) +#if defined(_WIN32) +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __declspec(dllexport) +#else +#define SPIRV_TOOLS_EXPORT __declspec(dllimport) +#endif +#define SPIRV_TOOLS_LOCAL +#else +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __attribute__((visibility("default"))) +#define SPIRV_TOOLS_LOCAL __attribute__((visibility("hidden"))) +#else +#define SPIRV_TOOLS_EXPORT +#define SPIRV_TOOLS_LOCAL +#endif +#endif +#else +#define SPIRV_TOOLS_EXPORT +#define SPIRV_TOOLS_LOCAL +#endif + +// Helpers + +#define SPV_BIT(shift) (1 << (shift)) + +#define SPV_FORCE_16_BIT_ENUM(name) SPV_FORCE_16BIT_##name = 0x7fff +#define SPV_FORCE_32_BIT_ENUM(name) SPV_FORCE_32BIT_##name = 0x7fffffff + +// Enumerations + +typedef enum spv_result_t { + SPV_SUCCESS = 0, + SPV_UNSUPPORTED = 1, + SPV_END_OF_STREAM = 2, + SPV_WARNING = 3, + SPV_FAILED_MATCH = 4, + SPV_REQUESTED_TERMINATION = 5, // Success, but signals early termination. + SPV_ERROR_INTERNAL = -1, + SPV_ERROR_OUT_OF_MEMORY = -2, + SPV_ERROR_INVALID_POINTER = -3, + SPV_ERROR_INVALID_BINARY = -4, + SPV_ERROR_INVALID_TEXT = -5, + SPV_ERROR_INVALID_TABLE = -6, + SPV_ERROR_INVALID_VALUE = -7, + SPV_ERROR_INVALID_DIAGNOSTIC = -8, + SPV_ERROR_INVALID_LOOKUP = -9, + SPV_ERROR_INVALID_ID = -10, + SPV_ERROR_INVALID_CFG = -11, + SPV_ERROR_INVALID_LAYOUT = -12, + SPV_ERROR_INVALID_CAPABILITY = -13, + SPV_ERROR_INVALID_DATA = -14, // Indicates data rules validation failure. + SPV_ERROR_MISSING_EXTENSION = -15, + SPV_ERROR_WRONG_VERSION = -16, // Indicates wrong SPIR-V version + SPV_FORCE_32_BIT_ENUM(spv_result_t) +} spv_result_t; + +// Severity levels of messages communicated to the consumer. +typedef enum spv_message_level_t { + SPV_MSG_FATAL, // Unrecoverable error due to environment. + // Will exit the program immediately. E.g., + // out of memory. + SPV_MSG_INTERNAL_ERROR, // Unrecoverable error due to SPIRV-Tools + // internals. + // Will exit the program immediately. E.g., + // unimplemented feature. + SPV_MSG_ERROR, // Normal error due to user input. + SPV_MSG_WARNING, // Warning information. + SPV_MSG_INFO, // General information. + SPV_MSG_DEBUG, // Debug information. +} spv_message_level_t; + +typedef enum spv_endianness_t { + SPV_ENDIANNESS_LITTLE, + SPV_ENDIANNESS_BIG, + SPV_FORCE_32_BIT_ENUM(spv_endianness_t) +} spv_endianness_t; + +// The kinds of operands that an instruction may have. +// +// Some operand types are "concrete". The binary parser uses a concrete +// operand type to describe an operand of a parsed instruction. +// +// The assembler uses all operand types. In addition to determining what +// kind of value an operand may be, non-concrete operand types capture the +// fact that an operand might be optional (may be absent, or present exactly +// once), or might occur zero or more times. +// +// Sometimes we also need to be able to express the fact that an operand +// is a member of an optional tuple of values. In that case the first member +// would be optional, and the subsequent members would be required. +// +// NOTE: Although we don't promise binary compatibility, as a courtesy, please +// add new enum values at the end. +typedef enum spv_operand_type_t { + // A sentinel value. + SPV_OPERAND_TYPE_NONE = 0, + + // Set 1: Operands that are IDs. + SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_TYPE_ID, + SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, // SPIR-V Sec 3.25 + SPV_OPERAND_TYPE_SCOPE_ID, // SPIR-V Sec 3.27 + + // Set 2: Operands that are literal numbers. + SPV_OPERAND_TYPE_LITERAL_INTEGER, // Always unsigned 32-bits. + // The Instruction argument to OpExtInst. It's an unsigned 32-bit literal + // number indicating which instruction to use from an extended instruction + // set. + SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + // The Opcode argument to OpSpecConstantOp. It determines the operation + // to be performed on constant operands to compute a specialization constant + // result. + SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, + // A literal number whose format and size are determined by a previous operand + // in the same instruction. It's a signed integer, an unsigned integer, or a + // floating point number. It also has a specified bit width. The width + // may be larger than 32, which would require such a typed literal value to + // occupy multiple SPIR-V words. + SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + SPV_OPERAND_TYPE_LITERAL_FLOAT, // Always 32-bit float. + + // Set 3: The literal string operand type. + SPV_OPERAND_TYPE_LITERAL_STRING, + + // Set 4: Operands that are a single word enumerated value. + SPV_OPERAND_TYPE_SOURCE_LANGUAGE, // SPIR-V Sec 3.2 + SPV_OPERAND_TYPE_EXECUTION_MODEL, // SPIR-V Sec 3.3 + SPV_OPERAND_TYPE_ADDRESSING_MODEL, // SPIR-V Sec 3.4 + SPV_OPERAND_TYPE_MEMORY_MODEL, // SPIR-V Sec 3.5 + SPV_OPERAND_TYPE_EXECUTION_MODE, // SPIR-V Sec 3.6 + SPV_OPERAND_TYPE_STORAGE_CLASS, // SPIR-V Sec 3.7 + SPV_OPERAND_TYPE_DIMENSIONALITY, // SPIR-V Sec 3.8 + SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE, // SPIR-V Sec 3.9 + SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, // SPIR-V Sec 3.10 + SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT, // SPIR-V Sec 3.11 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER, // SPIR-V Sec 3.12 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE, // SPIR-V Sec 3.13 + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, // SPIR-V Sec 3.16 + SPV_OPERAND_TYPE_LINKAGE_TYPE, // SPIR-V Sec 3.17 + SPV_OPERAND_TYPE_ACCESS_QUALIFIER, // SPIR-V Sec 3.18 + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, // SPIR-V Sec 3.19 + SPV_OPERAND_TYPE_DECORATION, // SPIR-V Sec 3.20 + SPV_OPERAND_TYPE_BUILT_IN, // SPIR-V Sec 3.21 + SPV_OPERAND_TYPE_GROUP_OPERATION, // SPIR-V Sec 3.28 + SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 + SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 + SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 + SPV_OPERAND_TYPE_FPENCODING, // SPIR-V Sec 3.51 + + // NOTE: New concrete enum values should be added at the end. + + // Set 5: Operands that are a single word bitmask. + // Sometimes a set bit indicates the instruction requires still more operands. + SPV_OPERAND_TYPE_IMAGE, // SPIR-V Sec 3.14 + SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, // SPIR-V Sec 3.15 + SPV_OPERAND_TYPE_SELECTION_CONTROL, // SPIR-V Sec 3.22 + SPV_OPERAND_TYPE_LOOP_CONTROL, // SPIR-V Sec 3.23 + SPV_OPERAND_TYPE_FUNCTION_CONTROL, // SPIR-V Sec 3.24 + SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 + SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR + +// NOTE: New concrete enum values should be added at the end. + +// The "optional" and "variable" operand types are only used internally by +// the assembler and the binary parser. +// There are two categories: +// Optional : expands to 0 or 1 operand, like ? in regular expressions. +// Variable : expands to 0, 1 or many operands or pairs of operands. +// This is similar to * in regular expressions. + +// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED. +// The concept of "optional" and "variable" operand types are only intended +// for use as an implementation detail of parsing SPIR-V, either in text or +// binary form. Instead of using enum ranges, use characteristic function +// spvOperandIsConcrete. +// The use of enum value ranges in a public API makes it difficult to insert +// new values into a range without also breaking binary compatibility. +// +// Macros for defining bounds on optional and variable operand types. +// Any variable operand type is also optional. +// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_* +#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM +#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM +#define LAST_VARIABLE(ENUM) \ + ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \ + SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM + + // An optional operand represents zero or one logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID), + // An optional image operand type. + SPV_OPERAND_TYPE_OPTIONAL_IMAGE, + // An optional memory access type. + SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + // An optional literal integer. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + // An optional literal number, which may be either integer or floating point. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, + // Like SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, but optional, and integral. + SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER, + // An optional literal string. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, + // An optional access qualifier + SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER, + // An optional context-independent value, or CIV. CIVs are tokens that we can + // assemble regardless of where they occur -- literals, IDs, immediate + // integers, etc. + SPV_OPERAND_TYPE_OPTIONAL_CIV, + // An optional floating point encoding enum + SPV_OPERAND_TYPE_OPTIONAL_FPENCODING, + + // A variable operand represents zero or more logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID), + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, + // A sequence of zero or more pairs of (typed literal integer, Id). + // Expands to zero or more: + // (SPV_OPERAND_TYPE_TYPED_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID) + // where the literal number must always be an integer of some sort. + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID, + // A sequence of zero or more pairs of (Id, Literal integer) + LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), + + // The following are concrete enum types from the DebugInfo extended + // instruction set. + SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, // DebugInfo Sec 3.2. A mask. + SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // DebugInfo Sec 3.3 + SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, // DebugInfo Sec 3.4 + SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, // DebugInfo Sec 3.5 + SPV_OPERAND_TYPE_DEBUG_OPERATION, // DebugInfo Sec 3.6 + + // The following are concrete enum types from the OpenCL.DebugInfo.100 + // extended instruction set. + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS, // Sec 3.2. A Mask + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // Sec 3.3 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE, // Sec 3.4 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER, // Sec 3.5 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, // Sec 3.6 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY, // Sec 3.7 + + // The following are concrete enum types from SPV_INTEL_float_controls2 + // https://github.com/intel/llvm/blob/39fa9b0cbfbae88327118990a05c5b387b56d2ef/sycl/doc/extensions/SPIRV/SPV_INTEL_float_controls2.asciidoc + SPV_OPERAND_TYPE_FPDENORM_MODE, // Sec 3.17 FP Denorm Mode + SPV_OPERAND_TYPE_FPOPERATION_MODE, // Sec 3.18 FP Operation Mode + // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177 + SPV_OPERAND_TYPE_QUANTIZATION_MODES, + // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177 + SPV_OPERAND_TYPE_OVERFLOW_MODES, + + // Concrete operand types for the provisional Vulkan ray tracing feature. + SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF + SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection + SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCommitted + SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCandidate + + // Concrete operand types for integer dot product. + // Packed vector format + SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT, // SPIR-V Sec 3.x + // An optional packed vector format + SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT, + + // Concrete operand types for cooperative matrix. + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS, + // An optional cooperative matrix operands + SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS, + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT, + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE, + + // Enum type from SPV_INTEL_global_variable_fpga_decorations + SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER, + // Enum type from SPV_INTEL_global_variable_host_access + SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER, + // Enum type from SPV_INTEL_cache_controls + SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL, + // Enum type from SPV_INTEL_cache_controls + SPV_OPERAND_TYPE_STORE_CACHE_CONTROL, + // Enum type from SPV_INTEL_maximum_registers + SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS, + // Enum type from SPV_NV_raw_access_chains + SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS, + // Optional enum type from SPV_NV_raw_access_chains + SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS, + // Enum type from SPV_NV_tensor_addressing + SPV_OPERAND_TYPE_TENSOR_CLAMP_MODE, + // Enum type from SPV_NV_cooperative_matrix2 + SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE, + // Enum type from SPV_NV_cooperative_matrix2 + SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS, + // Optional types from SPV_INTEL_subgroup_matrix_multiply_accumulate + SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS, + SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS, + + SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT, + SPV_OPERAND_TYPE_COMPONENT_TYPE, + + // This is a sentinel value, and does not represent an operand type. + // It should come last. + SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, + + SPV_FORCE_32_BIT_ENUM(spv_operand_type_t) +} spv_operand_type_t; + +// Returns true if the given type is concrete. +bool spvOperandIsConcrete(spv_operand_type_t type); + +// Returns true if the given type is concrete and also a mask. +bool spvOperandIsConcreteMask(spv_operand_type_t type); + +typedef enum spv_ext_inst_type_t { + SPV_EXT_INST_TYPE_NONE = 0, + SPV_EXT_INST_TYPE_GLSL_STD_450, + SPV_EXT_INST_TYPE_OPENCL_STD, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + SPV_EXT_INST_TYPE_DEBUGINFO, + SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, + + // Multiple distinct extended instruction set types could return this + // value, if they are prefixed with NonSemantic. and are otherwise + // unrecognised + SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN, + + SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t) +} spv_ext_inst_type_t; + +// This determines at a high level the kind of a binary-encoded literal +// number, but not the bit width. +// In principle, these could probably be folded into new entries in +// spv_operand_type_t. But then we'd have some special case differences +// between the assembler and disassembler. +typedef enum spv_number_kind_t { + SPV_NUMBER_NONE = 0, // The default for value initialization. + SPV_NUMBER_UNSIGNED_INT, + SPV_NUMBER_SIGNED_INT, + SPV_NUMBER_FLOATING, +} spv_number_kind_t; + +typedef enum spv_text_to_binary_options_t { + SPV_TEXT_TO_BINARY_OPTION_NONE = SPV_BIT(0), + // Numeric IDs in the binary will have the same values as in the source. + // Non-numeric IDs are allocated by filling in the gaps, starting with 1 + // and going up. + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS = SPV_BIT(1), + SPV_FORCE_32_BIT_ENUM(spv_text_to_binary_options_t) +} spv_text_to_binary_options_t; + +typedef enum spv_binary_to_text_options_t { + SPV_BINARY_TO_TEXT_OPTION_NONE = SPV_BIT(0), + SPV_BINARY_TO_TEXT_OPTION_PRINT = SPV_BIT(1), + SPV_BINARY_TO_TEXT_OPTION_COLOR = SPV_BIT(2), + SPV_BINARY_TO_TEXT_OPTION_INDENT = SPV_BIT(3), + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET = SPV_BIT(4), + // Do not output the module header as leading comments in the assembly. + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER = SPV_BIT(5), + // Use friendly names where possible. The heuristic may expand over + // time, but will use common names for scalar types, and debug names from + // OpName instructions. + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES = SPV_BIT(6), + // Add some comments to the generated assembly + SPV_BINARY_TO_TEXT_OPTION_COMMENT = SPV_BIT(7), + // Use nested indentation for more readable SPIR-V + SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT = SPV_BIT(8), + // Reorder blocks to match the structured control flow of SPIR-V to increase + // readability. + SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS = SPV_BIT(9), + SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t) +} spv_binary_to_text_options_t; + +// Constants + +// The default id bound is to the minimum value for the id limit +// in the spir-v specification under the section "Universal Limits". +const uint32_t kDefaultMaxIdBound = 0x3FFFFF; + +// Structures + +// Information about an operand parsed from a binary SPIR-V module. +// Note that the values are not included. You still need access to the binary +// to extract the values. +typedef struct spv_parsed_operand_t { + // Location of the operand, in words from the start of the instruction. + uint16_t offset; + // Number of words occupied by this operand. + uint16_t num_words; + // The "concrete" operand type. See the definition of spv_operand_type_t + // for details. + spv_operand_type_t type; + // If type is a literal number type, then number_kind says whether it's + // a signed integer, an unsigned integer, or a floating point number. + spv_number_kind_t number_kind; + // The number of bits for a literal number type. + uint32_t number_bit_width; +} spv_parsed_operand_t; + +// An instruction parsed from a binary SPIR-V module. +typedef struct spv_parsed_instruction_t { + // An array of words for this instruction, in native endianness. + const uint32_t* words; + // The number of words in this instruction. + uint16_t num_words; + uint16_t opcode; + // The extended instruction type, if opcode is OpExtInst. Otherwise + // this is the "none" value. + spv_ext_inst_type_t ext_inst_type; + // The type id, or 0 if this instruction doesn't have one. + uint32_t type_id; + // The result id, or 0 if this instruction doesn't have one. + uint32_t result_id; + // The array of parsed operands. + const spv_parsed_operand_t* operands; + uint16_t num_operands; +} spv_parsed_instruction_t; + +typedef struct spv_parsed_header_t { + // The magic number of the SPIR-V module. + uint32_t magic; + // Version number. + uint32_t version; + // Generator's magic number. + uint32_t generator; + // IDs bound for this module (0 < id < bound). + uint32_t bound; + // reserved. + uint32_t reserved; +} spv_parsed_header_t; + +typedef struct spv_const_binary_t { + const uint32_t* code; + const size_t wordCount; +} spv_const_binary_t; + +typedef struct spv_binary_t { + uint32_t* code; + size_t wordCount; +} spv_binary_t; + +typedef struct spv_text_t { + const char* str; + size_t length; +} spv_text_t; + +typedef struct spv_position_t { + size_t line; + size_t column; + size_t index; +} spv_position_t; + +typedef struct spv_diagnostic_t { + spv_position_t position; + char* error; + bool isTextSource; +} spv_diagnostic_t; + +// Opaque struct containing the context used to operate on a SPIR-V module. +// Its object is used by various translation API functions. +typedef struct spv_context_t spv_context_t; + +typedef struct spv_validator_options_t spv_validator_options_t; + +typedef struct spv_optimizer_options_t spv_optimizer_options_t; + +typedef struct spv_reducer_options_t spv_reducer_options_t; + +typedef struct spv_fuzzer_options_t spv_fuzzer_options_t; + +typedef struct spv_optimizer_t spv_optimizer_t; + +// Type Definitions + +typedef spv_const_binary_t* spv_const_binary; +typedef spv_binary_t* spv_binary; +typedef spv_text_t* spv_text; +typedef spv_position_t* spv_position; +typedef spv_diagnostic_t* spv_diagnostic; +typedef const spv_context_t* spv_const_context; +typedef spv_context_t* spv_context; +typedef spv_validator_options_t* spv_validator_options; +typedef const spv_validator_options_t* spv_const_validator_options; +typedef spv_optimizer_options_t* spv_optimizer_options; +typedef const spv_optimizer_options_t* spv_const_optimizer_options; +typedef spv_reducer_options_t* spv_reducer_options; +typedef const spv_reducer_options_t* spv_const_reducer_options; +typedef spv_fuzzer_options_t* spv_fuzzer_options; +typedef const spv_fuzzer_options_t* spv_const_fuzzer_options; + +// Platform API + +// Returns the SPIRV-Tools software version as a null-terminated string. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionString(void); +// Returns a null-terminated string containing the name of the project, +// the software version string, and commit details. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void); + +// Certain target environments impose additional restrictions on SPIR-V, so it's +// often necessary to specify which one applies. SPV_ENV_UNIVERSAL_* implies an +// environment-agnostic SPIR-V. +// +// When an API method needs to derive a SPIR-V version from a target environment +// (from the spv_context object), the method will choose the highest version of +// SPIR-V supported by the target environment. Examples: +// SPV_ENV_VULKAN_1_0 -> SPIR-V 1.0 +// SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3 +// SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4 +// SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5 +// SPV_ENV_VULKAN_1_3 -> SPIR-V 1.6 +// SPV_ENV_VULKAN_1_4 -> SPIR-V 1.6 +// Consult the description of API entry points for specific rules. +typedef enum { + SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_0, // Vulkan 1.0 latest revision. + SPV_ENV_UNIVERSAL_1_1, // SPIR-V 1.1 latest revision, no other restrictions. + SPV_ENV_OPENCL_2_1, // OpenCL Full Profile 2.1 latest revision. + SPV_ENV_OPENCL_2_2, // OpenCL Full Profile 2.2 latest revision. + SPV_ENV_OPENGL_4_0, // OpenGL 4.0 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_1, // OpenGL 4.1 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_2, // OpenGL 4.2 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_3, // OpenGL 4.3 plus GL_ARB_gl_spirv, latest revisions. + // There is no variant for OpenGL 4.4. + SPV_ENV_OPENGL_4_5, // OpenGL 4.5 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_UNIVERSAL_1_2, // SPIR-V 1.2, latest revision, no other restrictions. + SPV_ENV_OPENCL_1_2, // OpenCL Full Profile 1.2 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_1_2, // OpenCL Embedded Profile 1.2 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_2_0, // OpenCL Full Profile 2.0 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_0, // OpenCL Embedded Profile 2.0 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_1, // OpenCL Embedded Profile 2.1 latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_2, // OpenCL Embedded Profile 2.2 latest revision. + SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision. + SPV_ENV_WEBGPU_0, // DEPRECATED, may be removed in the future. + SPV_ENV_UNIVERSAL_1_4, // SPIR-V 1.4 latest revision, no other restrictions. + + // Vulkan 1.1 with VK_KHR_spirv_1_4, i.e. SPIR-V 1.4 binary. + SPV_ENV_VULKAN_1_1_SPIRV_1_4, + + SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. + + SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_3, // Vulkan 1.3 latest revision. + SPV_ENV_VULKAN_1_4, // Vulkan 1.4 latest revision. + + SPV_ENV_MAX // Keep this as the last enum value. +} spv_target_env; + +// SPIR-V Validator can be parameterized with the following Universal Limits. +typedef enum { + spv_validator_limit_max_struct_members, + spv_validator_limit_max_struct_depth, + spv_validator_limit_max_local_variables, + spv_validator_limit_max_global_variables, + spv_validator_limit_max_switch_branches, + spv_validator_limit_max_function_args, + spv_validator_limit_max_control_flow_nesting_depth, + spv_validator_limit_max_access_chain_indexes, + spv_validator_limit_max_id_bound, +} spv_validator_limit; + +// Returns a string describing the given SPIR-V target environment. +SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env); + +// Parses s into *env and returns true if successful. If unparsable, returns +// false and sets *env to SPV_ENV_UNIVERSAL_1_0. +SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env); + +// Determines the target env value with the least features but which enables +// the given Vulkan and SPIR-V versions. If such a target is supported, returns +// true and writes the value to |env|, otherwise returns false. +// +// The Vulkan version is given as an unsigned 32-bit number as specified in +// Vulkan section "29.2.1 Version Numbers": the major version number appears +// in bits 22 to 21, and the minor version is in bits 12 to 21. The SPIR-V +// version is given in the SPIR-V version header word: major version in bits +// 16 to 23, and minor version in bits 8 to 15. +SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, + uint32_t spirv_ver, + spv_target_env* env); + +// Creates a context object for most of the SPIRV-Tools API. +// Returns null if env is invalid. +// +// See specific API calls for how the target environment is interpreted +// (particularly assembly and validation). +SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); + +// Destroys the given context object. +SPIRV_TOOLS_EXPORT void spvContextDestroy(spv_context context); + +// Creates a Validator options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// spvValidatorOptionsDestroy. +SPIRV_TOOLS_EXPORT spv_validator_options spvValidatorOptionsCreate(void); + +// Destroys the given Validator options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsDestroy( + spv_validator_options options); + +// Records the maximum Universal Limit that is considered valid in the given +// Validator options object. argument must be a valid options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniversalLimit( + spv_validator_options options, spv_validator_limit limit_type, + uint32_t limit); + +// Record whether or not the validator should relax the rules on types for +// stores to structs. When relaxed, it will allow a type mismatch as long as +// the types are structs with the same layout. Two structs have the same layout +// if +// +// 1) the members of the structs are either the same type or are structs with +// same layout, and +// +// 2) the decorations that affect the memory layout are identical for both +// types. Other decorations are not relevant. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxStoreStruct( + spv_validator_options options, bool val); + +// Records whether or not the validator should relax the rules on pointer usage +// in logical addressing mode. +// +// When relaxed, it will allow the following usage cases of pointers: +// 1) OpVariable allocating an object whose type is a pointer type +// 2) OpReturnValue returning a pointer value +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( + spv_validator_options options, bool val); + +// Records whether or not the validator should relax the rules because it is +// expected that the optimizations will make the code legal. +// +// When relaxed, it will allow the following: +// 1) It will allow relaxed logical pointers. Setting this option will also +// set that option. +// 2) Pointers that are pass as parameters to function calls do not have to +// match the storage class of the formal parameter. +// 3) Pointers that are actual parameters on function calls do not have to point +// to the same type pointed as the formal parameter. The types just need to +// logically match. +// 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant +// for a first argument. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBeforeHlslLegalization( + spv_validator_options options, bool val); + +// Records whether the validator should use "relaxed" block layout rules. +// Relaxed layout rules are described by Vulkan extension +// VK_KHR_relaxed_block_layout, and they affect uniform blocks, storage blocks, +// and push constants. +// +// This is enabled by default when targeting Vulkan 1.1 or later. +// Relaxed layout is more permissive than the default rules in Vulkan 1.0. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout( + spv_validator_options options, bool val); + +// Records whether the validator should use standard block layout rules for +// uniform blocks. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( + spv_validator_options options, bool val); + +// Records whether the validator should use "scalar" block layout rules. +// Scalar layout rules are more permissive than relaxed block layout. +// +// See Vulkan extension VK_EXT_scalar_block_layout. The scalar alignment is +// defined as follows: +// - scalar alignment of a scalar is the scalar size +// - scalar alignment of a vector is the scalar alignment of its component +// - scalar alignment of a matrix is the scalar alignment of its component +// - scalar alignment of an array is the scalar alignment of its element +// - scalar alignment of a struct is the max scalar alignment among its +// members +// +// For a struct in Uniform, StorageClass, or PushConstant: +// - a member Offset must be a multiple of the member's scalar alignment +// - ArrayStride or MatrixStride must be a multiple of the array or matrix +// scalar alignment +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout( + spv_validator_options options, bool val); + +// Records whether the validator should use "scalar" block layout +// rules (as defined above) for Workgroup blocks. See Vulkan +// extension VK_KHR_workgroup_memory_explicit_layout. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout( + spv_validator_options options, bool val); + +// Records whether or not the validator should skip validating standard +// uniform/storage block layout. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( + spv_validator_options options, bool val); + +// Records whether or not the validator should allow the LocalSizeId +// decoration where the environment otherwise would not allow it. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId( + spv_validator_options options, bool val); + +// Allow Offset (in addition to ConstOffset) for texture operations. +// Was added for VK_KHR_maintenance8 +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowOffsetTextureOperand( + spv_validator_options options, bool val); + +// Allow base operands of some bit operations to be non-32-bit wide. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowVulkan32BitBitwise( + spv_validator_options options, bool val); + +// Whether friendly names should be used in validation error messages. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames( + spv_validator_options options, bool val); + +// Creates an optimizer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvOptimizerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate(void); + +// Destroys the given optimizer options object. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options); + +// Records whether or not the optimizer should run the validator before +// optimizing. If |val| is true, the validator will be run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val); + +// Records the validator options that should be passed to the validator if it is +// run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val); + +// Records the maximum possible value for the id bound. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val); + +// Records whether all bindings within the module should be preserved. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveBindings( + spv_optimizer_options options, bool val); + +// Records whether all specialization constants within the module +// should be preserved. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveSpecConstants( + spv_optimizer_options options, bool val); + +// Creates a reducer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvReducerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(void); + +// Destroys the given reducer options object. +SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options); + +// Sets the maximum number of reduction steps that should run before the reducer +// gives up. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( + spv_reducer_options options, uint32_t step_limit); + +// Sets the fail-on-validation-error option; if true, the reducer will return +// kStateInvalid if a reduction step yields a state that fails SPIR-V +// validation. Otherwise, an invalid state is treated as uninteresting and the +// reduction backtracks and continues. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error); + +// Sets the function that the reducer should target. If set to zero the reducer +// will target all functions as well as parts of the module that lie outside +// functions. Otherwise the reducer will restrict reduction to the function +// with result id |target_function|, which is required to exist. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction( + spv_reducer_options options, uint32_t target_function); + +// Creates a fuzzer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvFuzzerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(void); + +// Destroys the given fuzzer options object. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options); + +// Enables running the validator after every transformation is applied during +// a replay. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation( + spv_fuzzer_options options); + +// Sets the seed with which the random number generator used by the fuzzer +// should be initialized. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed); + +// Sets the range of transformations that should be applied during replay: 0 +// means all transformations, +N means the first N transformations, -N means all +// except the final N transformations. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range); + +// Sets the maximum number of steps that the shrinker should take before giving +// up. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( + spv_fuzzer_options options, uint32_t shrinker_step_limit); + +// Enables running the validator after every pass is applied during a fuzzing +// run. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options); + +// Enables all fuzzer passes during a fuzzing run (instead of a random subset +// of passes). +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses( + spv_fuzzer_options options); + +// Encodes the given SPIR-V assembly text to its binary representation. The +// length parameter specifies the number of bytes for text. Encoded binary will +// be stored into *binary. Any error will be written into *diagnostic if +// diagnostic is non-null, otherwise the context's message consumer will be +// used. The generated binary is independent of the context and may outlive it. +// The SPIR-V binary version is set to the highest version of SPIR-V supported +// by the context's target environment. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinary(const spv_const_context context, + const char* text, + const size_t length, + spv_binary* binary, + spv_diagnostic* diagnostic); + +// Encodes the given SPIR-V assembly text to its binary representation. Same as +// spvTextToBinary but with options. The options parameter is a bit field of +// spv_text_to_binary_options_t. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinaryWithOptions( + const spv_const_context context, const char* text, const size_t length, + const uint32_t options, spv_binary* binary, spv_diagnostic* diagnostic); + +// Frees an allocated text stream. This is a no-op if the text parameter +// is a null pointer. +SPIRV_TOOLS_EXPORT void spvTextDestroy(spv_text text); + +// Decodes the given SPIR-V binary representation to its assembly text. The +// word_count parameter specifies the number of words for binary. The options +// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will +// be stored into *text. Any error will be written into *diagnostic if +// diagnostic is non-null, otherwise the context's message consumer will be +// used. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryToText(const spv_const_context context, + const uint32_t* binary, + const size_t word_count, + const uint32_t options, + spv_text* text, + spv_diagnostic* diagnostic); + +// Frees a binary stream from memory. This is a no-op if binary is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvBinaryDestroy(spv_binary binary); + +// Validates a SPIR-V binary for correctness. Any errors will be written into +// *diagnostic if diagnostic is non-null, otherwise the context's message +// consumer will be used. +// +// Validate for SPIR-V spec rules for the SPIR-V version named in the +// binary's header (at word offset 1). Additionally, if the context target +// environment is a client API (such as Vulkan 1.1), then validate for that +// client API version, to the extent that it is verifiable from data in the +// binary itself. +SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context, + const spv_const_binary binary, + spv_diagnostic* diagnostic); + +// Validates a SPIR-V binary for correctness. Uses the provided Validator +// options. Any errors will be written into *diagnostic if diagnostic is +// non-null, otherwise the context's message consumer will be used. +// +// Validate for SPIR-V spec rules for the SPIR-V version named in the +// binary's header (at word offset 1). Additionally, if the context target +// environment is a client API (such as Vulkan 1.1), then validate for that +// client API version, to the extent that it is verifiable from data in the +// binary itself, or in the validator options. +SPIRV_TOOLS_EXPORT spv_result_t spvValidateWithOptions( + const spv_const_context context, const spv_const_validator_options options, + const spv_const_binary binary, spv_diagnostic* diagnostic); + +// Validates a raw SPIR-V binary for correctness. Any errors will be written +// into *diagnostic if diagnostic is non-null, otherwise the context's message +// consumer will be used. +SPIRV_TOOLS_EXPORT spv_result_t +spvValidateBinary(const spv_const_context context, const uint32_t* words, + const size_t num_words, spv_diagnostic* diagnostic); + +// Creates a diagnostic object. The position parameter specifies the location in +// the text/binary stream. The message parameter, copied into the diagnostic +// object, contains the error message to display. +SPIRV_TOOLS_EXPORT spv_diagnostic +spvDiagnosticCreate(const spv_position position, const char* message); + +// Destroys a diagnostic object. This is a no-op if diagnostic is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvDiagnosticDestroy(spv_diagnostic diagnostic); + +// Prints the diagnostic to stderr. +SPIRV_TOOLS_EXPORT spv_result_t +spvDiagnosticPrint(const spv_diagnostic diagnostic); + +// Gets the name of an instruction, without the "Op" prefix. +SPIRV_TOOLS_EXPORT const char* spvOpcodeString(const uint32_t opcode); + +// The binary parser interface. + +// A pointer to a function that accepts a parsed SPIR-V header. +// The integer arguments are the 32-bit words from the header, as specified +// in SPIR-V 1.0 Section 2.3 Table 1. +// The function should return SPV_SUCCESS if parsing should continue. +typedef spv_result_t (*spv_parsed_header_fn_t)( + void* user_data, spv_endianness_t endian, uint32_t magic, uint32_t version, + uint32_t generator, uint32_t id_bound, uint32_t reserved); + +// A pointer to a function that accepts a parsed SPIR-V instruction. +// The parsed_instruction value is transient: it may be overwritten +// or released immediately after the function has returned. That also +// applies to the words array member of the parsed instruction. The +// function should return SPV_SUCCESS if and only if parsing should +// continue. +typedef spv_result_t (*spv_parsed_instruction_fn_t)( + void* user_data, const spv_parsed_instruction_t* parsed_instruction); + +// Parses a SPIR-V binary, specified as counted sequence of 32-bit words. +// Parsing feedback is provided via two callbacks provided as function +// pointers. Each callback function pointer can be a null pointer, in +// which case it is never called. Otherwise, in a valid parse the +// parsed-header callback is called once, and then the parsed-instruction +// callback once for each instruction in the stream. The user_data parameter +// is supplied as context to the callbacks. Returns SPV_SUCCESS on successful +// parse where the callbacks always return SPV_SUCCESS. For an invalid parse, +// returns a status code other than SPV_SUCCESS, and if diagnostic is non-null +// also emits a diagnostic. If diagnostic is null the context's message consumer +// will be used to emit any errors. If a callback returns anything other than +// SPV_SUCCESS, then that status code is returned, no further callbacks are +// issued, and no additional diagnostics are emitted. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryParse( + const spv_const_context context, void* user_data, const uint32_t* words, + const size_t num_words, spv_parsed_header_fn_t parse_header, + spv_parsed_instruction_fn_t parse_instruction, spv_diagnostic* diagnostic); + +// The optimizer interface. + +// A pointer to a function that accepts a log message from an optimizer. +typedef void (*spv_message_consumer)( + spv_message_level_t, const char*, const spv_position_t*, const char*); + +// Creates and returns an optimizer object. This object must be passed to +// optimizer APIs below and is valid until passed to spvOptimizerDestroy. +SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env); + +// Destroys the given optimizer object. +SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer); + +// Sets an spv_message_consumer on an optimizer object. +SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer( + spv_optimizer_t* optimizer, spv_message_consumer consumer); + +// Registers passes that attempt to legalize the generated code. +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses( + spv_optimizer_t* optimizer); + +// Registers passes that attempt to improve performance of generated code. +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses( + spv_optimizer_t* optimizer); + +// Registers passes that attempt to improve the size of generated code. +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses( + spv_optimizer_t* optimizer); + +// Registers a pass specified by a flag in an optimizer object. +SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag( + spv_optimizer_t* optimizer, const char* flag); + +// Registers passes specified by length number of flags in an optimizer object. +// Passes may remove interface variables that are unused. +SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); + +// Registers passes specified by length number of flags in an optimizer object. +// Passes will not remove interface variables. +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count); + +// Optimizes the SPIR-V code of size |word_count| pointed to by |binary| and +// returns an optimized spv_binary in |optimized_binary|. +// +// Returns SPV_SUCCESS on successful optimization, whether or not the module is +// modified. Returns an SPV_ERROR_* if the module fails to validate or if +// errors occur when processing using any of the registered passes. In that +// case, no further passes are executed and the |optimized_binary| contents may +// be invalid. +// +// By default, the binary is validated before any transforms are performed, +// and optionally after each transform. Validation uses SPIR-V spec rules +// for the SPIR-V version named in the binary's header (at word offset 1). +// Additionally, if the target environment is a client API (such as +// Vulkan 1.1), then validate for that client API version, to the extent +// that it is verifiable from data in the binary itself, or from the +// validator options set on the optimizer options. +SPIRV_TOOLS_EXPORT spv_result_t spvOptimizerRun( + spv_optimizer_t* optimizer, const uint32_t* binary, const size_t word_count, + spv_binary* optimized_binary, const spv_optimizer_options options); + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.hpp b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.hpp new file mode 100644 index 00000000..2cbb7492 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/libspirv.hpp @@ -0,0 +1,409 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ +#define INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ + +#include +#include +#include +#include + +#include "libspirv.h" + +namespace spvtools { + +// Message consumer. The C strings for source and message are only alive for the +// specific invocation. +using MessageConsumer = std::function; + +using HeaderParser = std::function; +using InstructionParser = + std::function; + +// C++ RAII wrapper around the C context object spv_context. +class SPIRV_TOOLS_EXPORT Context { + public: + // Constructs a context targeting the given environment |env|. + // + // See specific API calls for how the target environment is interpreted + // (particularly assembly and validation). + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit Context(spv_target_env env); + + // Enables move constructor/assignment operations. + Context(Context&& other); + Context& operator=(Context&& other); + + // Disables copy constructor/assignment operations. + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + // Destructs this instance. + ~Context(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns the underlying spv_context. + spv_context& CContext(); + const spv_context& CContext() const; + + private: + spv_context context_; +}; + +// A RAII wrapper around a validator options object. +class SPIRV_TOOLS_EXPORT ValidatorOptions { + public: + ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} + ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } + // Allow implicit conversion to the underlying object. + operator spv_validator_options() const { return options_; } + + // Sets a limit. + void SetUniversalLimit(spv_validator_limit limit_type, uint32_t limit) { + spvValidatorOptionsSetUniversalLimit(options_, limit_type, limit); + } + + void SetRelaxStructStore(bool val) { + spvValidatorOptionsSetRelaxStoreStruct(options_, val); + } + + // Enables VK_KHR_relaxed_block_layout when validating standard + // uniform/storage buffer/push-constant layout. If true, disables + // scalar block layout rules. + void SetRelaxBlockLayout(bool val) { + spvValidatorOptionsSetRelaxBlockLayout(options_, val); + } + + // Enables VK_KHR_uniform_buffer_standard_layout when validating standard + // uniform layout. If true, disables scalar block layout rules. + void SetUniformBufferStandardLayout(bool val) { + spvValidatorOptionsSetUniformBufferStandardLayout(options_, val); + } + + // Enables VK_EXT_scalar_block_layout when validating standard + // uniform/storage buffer/push-constant layout. If true, disables + // relaxed block layout rules. + void SetScalarBlockLayout(bool val) { + spvValidatorOptionsSetScalarBlockLayout(options_, val); + } + + // Enables scalar layout when validating Workgroup blocks. See + // VK_KHR_workgroup_memory_explicit_layout. + void SetWorkgroupScalarBlockLayout(bool val) { + spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val); + } + + // Skips validating standard uniform/storage buffer/push-constant layout. + void SetSkipBlockLayout(bool val) { + spvValidatorOptionsSetSkipBlockLayout(options_, val); + } + + // Enables LocalSizeId decorations where the environment would not otherwise + // allow them. + void SetAllowLocalSizeId(bool val) { + spvValidatorOptionsSetAllowLocalSizeId(options_, val); + } + + // Allow Offset (in addition to ConstOffset) for texture + // operations. Was added for VK_KHR_maintenance8 + void SetAllowOffsetTextureOperand(bool val) { + spvValidatorOptionsSetAllowOffsetTextureOperand(options_, val); + } + + // Allow base operands of some bit operations to be non-32-bit wide. + void SetAllowVulkan32BitBitwise(bool val) { + spvValidatorOptionsSetAllowVulkan32BitBitwise(options_, val); + } + + // Records whether or not the validator should relax the rules on pointer + // usage in logical addressing mode. + // + // When relaxed, it will allow the following usage cases of pointers: + // 1) OpVariable allocating an object whose type is a pointer type + // 2) OpReturnValue returning a pointer value + void SetRelaxLogicalPointer(bool val) { + spvValidatorOptionsSetRelaxLogicalPointer(options_, val); + } + + // Records whether or not the validator should relax the rules because it is + // expected that the optimizations will make the code legal. + // + // When relaxed, it will allow the following: + // 1) It will allow relaxed logical pointers. Setting this option will also + // set that option. + // 2) Pointers that are pass as parameters to function calls do not have to + // match the storage class of the formal parameter. + // 3) Pointers that are actual parameters on function calls do not have to + // point to the same type pointed as the formal parameter. The types just + // need to logically match. + // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant + // for a first argument. + void SetBeforeHlslLegalization(bool val) { + spvValidatorOptionsSetBeforeHlslLegalization(options_, val); + } + + // Whether friendly names should be used in validation error messages. + void SetFriendlyNames(bool val) { + spvValidatorOptionsSetFriendlyNames(options_, val); + } + + private: + spv_validator_options options_; +}; + +// A C++ wrapper around an optimization options object. +class SPIRV_TOOLS_EXPORT OptimizerOptions { + public: + OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} + ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_optimizer_options() const { return options_; } + + // Records whether or not the optimizer should run the validator before + // optimizing. If |run| is true, the validator will be run. + void set_run_validator(bool run) { + spvOptimizerOptionsSetRunValidator(options_, run); + } + + // Records the validator options that should be passed to the validator if it + // is run. + void set_validator_options(const ValidatorOptions& val_options) { + spvOptimizerOptionsSetValidatorOptions(options_, val_options); + } + + // Records the maximum possible value for the id bound. + void set_max_id_bound(uint32_t new_bound) { + spvOptimizerOptionsSetMaxIdBound(options_, new_bound); + } + + // Records whether all bindings within the module should be preserved. + void set_preserve_bindings(bool preserve_bindings) { + spvOptimizerOptionsSetPreserveBindings(options_, preserve_bindings); + } + + // Records whether all specialization constants within the module + // should be preserved. + void set_preserve_spec_constants(bool preserve_spec_constants) { + spvOptimizerOptionsSetPreserveSpecConstants(options_, + preserve_spec_constants); + } + + private: + spv_optimizer_options options_; +}; + +// A C++ wrapper around a reducer options object. +class SPIRV_TOOLS_EXPORT ReducerOptions { + public: + ReducerOptions() : options_(spvReducerOptionsCreate()) {} + ~ReducerOptions() { spvReducerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } + + // See spvReducerOptionsSetStepLimit. + void set_step_limit(uint32_t step_limit) { + spvReducerOptionsSetStepLimit(options_, step_limit); + } + + // See spvReducerOptionsSetFailOnValidationError. + void set_fail_on_validation_error(bool fail_on_validation_error) { + spvReducerOptionsSetFailOnValidationError(options_, + fail_on_validation_error); + } + + // See spvReducerOptionsSetTargetFunction. + void set_target_function(uint32_t target_function) { + spvReducerOptionsSetTargetFunction(options_, target_function); + } + + private: + spv_reducer_options options_; +}; + +// A C++ wrapper around a fuzzer options object. +class SPIRV_TOOLS_EXPORT FuzzerOptions { + public: + FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} + ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } + + // See spvFuzzerOptionsEnableReplayValidation. + void enable_replay_validation() { + spvFuzzerOptionsEnableReplayValidation(options_); + } + + // See spvFuzzerOptionsSetRandomSeed. + void set_random_seed(uint32_t seed) { + spvFuzzerOptionsSetRandomSeed(options_, seed); + } + + // See spvFuzzerOptionsSetReplayRange. + void set_replay_range(int32_t replay_range) { + spvFuzzerOptionsSetReplayRange(options_, replay_range); + } + + // See spvFuzzerOptionsSetShrinkerStepLimit. + void set_shrinker_step_limit(uint32_t shrinker_step_limit) { + spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); + } + + // See spvFuzzerOptionsEnableFuzzerPassValidation. + void enable_fuzzer_pass_validation() { + spvFuzzerOptionsEnableFuzzerPassValidation(options_); + } + + // See spvFuzzerOptionsEnableAllPasses. + void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); } + + private: + spv_fuzzer_options options_; +}; + +// C++ interface for SPIRV-Tools functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for assembling, disassembling, and validating. +// +// Instances of this class provide basic thread-safety guarantee. +class SPIRV_TOOLS_EXPORT SpirvTools { + public: + enum { + // Default assembling option used by assemble(): + kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, + + // Default disassembling option used by Disassemble(): + // * Avoid prefix comments from decoding the SPIR-V module header, and + // * Use friendly names for variables. + kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES + }; + + // Constructs an instance targeting the given environment |env|. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit SpirvTools(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + SpirvTools(const SpirvTools&) = delete; + SpirvTools(SpirvTools&&) = delete; + SpirvTools& operator=(const SpirvTools&) = delete; + SpirvTools& operator=(SpirvTools&&) = delete; + + // Destructs this instance. + ~SpirvTools(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Assembles the given assembly |text| and writes the result to |binary|. + // Returns true on successful assembling. |binary| will be kept untouched if + // assembling is unsuccessful. + // The SPIR-V binary version is set to the highest version of SPIR-V supported + // by the target environment with which this SpirvTools object was created. + bool Assemble(const std::string& text, std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + // |text_size| specifies the number of bytes in |text|. A terminating null + // character is not required to present in |text| as long as |text| is valid. + // The SPIR-V binary version is set to the highest version of SPIR-V supported + // by the target environment with which this SpirvTools object was created. + bool Assemble(const char* text, size_t text_size, + std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + + // Disassembles the given SPIR-V |binary| with the given |options| and writes + // the assembly to |text|. Returns true on successful disassembling. |text| + // will be kept untouched if diassembling is unsuccessful. + bool Disassemble(const std::vector& binary, std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + // |binary_size| specifies the number of words in |binary|. + bool Disassemble(const uint32_t* binary, size_t binary_size, + std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + + // Parses a SPIR-V binary, specified as counted sequence of 32-bit words. + // Parsing feedback is provided via two callbacks provided as std::function. + // In a valid parse the parsed-header callback is called once, and + // then the parsed-instruction callback is called once for each instruction + // in the stream. + // Returns true on successful parsing. + // If diagnostic is non-null, a diagnostic is emitted on failed parsing. + // If diagnostic is null the context's message consumer + // will be used to emit any errors. If a callback returns anything other than + // SPV_SUCCESS, then that status code is returned, no further callbacks are + // issued, and no additional diagnostics are emitted. + // This is a wrapper around the C API spvBinaryParse. + bool Parse(const std::vector& binary, + const HeaderParser& header_parser, + const InstructionParser& instruction_parser, + spv_diagnostic* diagnostic = nullptr); + + // Validates the given SPIR-V |binary|. Returns true if no issues are found. + // Otherwise, returns false and communicates issues via the message consumer + // registered. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself. + bool Validate(const std::vector& binary) const; + // Like the previous overload, but provides the binary as a pointer and size: + // |binary_size| specifies the number of words in |binary|. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself. + bool Validate(const uint32_t* binary, size_t binary_size) const; + // Like the previous overload, but takes an options object. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself, or in the validator options. + bool Validate(const uint32_t* binary, size_t binary_size, + spv_validator_options options) const; + + // Was this object successfully constructed. + bool IsValid() const; + + private: + struct SPIRV_TOOLS_LOCAL + Impl; // Opaque struct for holding the data fields used by this class. + std::unique_ptr impl_; // Unique pointer to implementation data. +}; + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ diff --git a/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linker.hpp b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linker.hpp new file mode 100644 index 00000000..9037b948 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linker.hpp @@ -0,0 +1,104 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LINKER_HPP_ +#define INCLUDE_SPIRV_TOOLS_LINKER_HPP_ + +#include +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +class SPIRV_TOOLS_EXPORT LinkerOptions { + public: + // Returns whether a library or an executable should be produced by the + // linking phase. + // + // All exported symbols are kept when creating a library, whereas they will + // be removed when creating an executable. + // The returned value will be true if creating a library, and false if + // creating an executable. + bool GetCreateLibrary() const { return create_library_; } + + // Sets whether a library or an executable should be produced. + void SetCreateLibrary(bool create_library) { + create_library_ = create_library; + } + + // Returns whether to verify the uniqueness of the unique ids in the merged + // context. + bool GetVerifyIds() const { return verify_ids_; } + + // Sets whether to verify the uniqueness of the unique ids in the merged + // context. + void SetVerifyIds(bool verify_ids) { verify_ids_ = verify_ids; } + + // Returns whether to allow for imported symbols to have no corresponding + // exported symbols + bool GetAllowPartialLinkage() const { return allow_partial_linkage_; } + + // Sets whether to allow for imported symbols to have no corresponding + // exported symbols + void SetAllowPartialLinkage(bool allow_partial_linkage) { + allow_partial_linkage_ = allow_partial_linkage; + } + + bool GetUseHighestVersion() const { return use_highest_version_; } + void SetUseHighestVersion(bool use_highest_vers) { + use_highest_version_ = use_highest_vers; + } + + bool GetAllowPtrTypeMismatch() const { return allow_ptr_type_mismatch_; } + void SetAllowPtrTypeMismatch(bool allow_ptr_type_mismatch) { + allow_ptr_type_mismatch_ = allow_ptr_type_mismatch; + } + + private: + bool create_library_{false}; + bool verify_ids_{false}; + bool allow_partial_linkage_{false}; + bool use_highest_version_{false}; + bool allow_ptr_type_mismatch_{false}; +}; + +// Links one or more SPIR-V modules into a new SPIR-V module. That is, combine +// several SPIR-V modules into one, resolving link dependencies between them. +// +// At least one binary has to be provided in |binaries|. Those binaries do not +// have to be valid, but they should be at least parseable. +// The functions can fail due to the following: +// * The given context was not initialised using `spvContextCreate()`; +// * No input modules were given; +// * One or more of those modules were not parseable; +// * The input modules used different addressing or memory models; +// * The ID or global variable number limit were exceeded; +// * Some entry points were defined multiple times; +// * Some imported symbols did not have an exported counterpart; +// * Possibly other reasons. +SPIRV_TOOLS_EXPORT spv_result_t +Link(const Context& context, const std::vector>& binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); +SPIRV_TOOLS_EXPORT spv_result_t +Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LINKER_HPP_ diff --git a/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linter.hpp b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linter.hpp new file mode 100644 index 00000000..ccbcf0c1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/linter.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LINTER_HPP_ +#define INCLUDE_SPIRV_TOOLS_LINTER_HPP_ + +#include "libspirv.hpp" + +namespace spvtools { + +// C++ interface for SPIR-V linting functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides a method for linting. +// +// Instances of this class provides basic thread-safety guarantee. +class SPIRV_TOOLS_EXPORT Linter { + public: + explicit Linter(spv_target_env env); + + ~Linter(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns a reference to the registered message consumer. + const MessageConsumer& Consumer() const; + + bool Run(const uint32_t* binary, size_t binary_size); + + private: + struct SPIRV_TOOLS_LOCAL Impl; + std::unique_ptr impl_; +}; +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LINTER_HPP_ diff --git a/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/optimizer.hpp b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/optimizer.hpp new file mode 100644 index 00000000..5fb9a339 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/include/spirv-tools/optimizer.hpp @@ -0,0 +1,1027 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ +#define INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +namespace opt { +class Pass; +struct DescriptorSetAndBinding; +} // namespace opt + +// C++ interface for SPIR-V optimization functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for registering optimization passes and optimizing. +// +// Instances of this class provides basic thread-safety guarantee. +class SPIRV_TOOLS_EXPORT Optimizer { + public: + // The token for an optimization pass. It is returned via one of the + // Create*Pass() standalone functions at the end of this header file and + // consumed by the RegisterPass() method. Tokens are one-time objects that + // only support move; copying is not allowed. + struct PassToken { + struct SPIRV_TOOLS_LOCAL Impl; // Opaque struct for holding internal data. + + PassToken(std::unique_ptr); + + // Tokens for built-in passes should be created using Create*Pass functions + // below; for out-of-tree passes, use this constructor instead. + // Note that this API isn't guaranteed to be stable and may change without + // preserving source or binary compatibility in the future. + PassToken(std::unique_ptr&& pass); + + // Tokens can only be moved. Copying is disabled. + PassToken(const PassToken&) = delete; + PassToken(PassToken&&); + PassToken& operator=(const PassToken&) = delete; + PassToken& operator=(PassToken&&); + + ~PassToken(); + + std::unique_ptr impl_; // Unique pointer to internal data. + }; + + // Constructs an instance with the given target |env|, which is used to decode + // the binaries to be optimized later. + // + // The instance will have an empty message consumer, which ignores all + // messages from the library. Use SetMessageConsumer() to supply a consumer + // if messages are of concern. + explicit Optimizer(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + Optimizer(const Optimizer&) = delete; + Optimizer(Optimizer&&) = delete; + Optimizer& operator=(const Optimizer&) = delete; + Optimizer& operator=(Optimizer&&) = delete; + + // Destructs this instance. + ~Optimizer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns a reference to the registered message consumer. + const MessageConsumer& consumer() const; + + // Registers the given |pass| to this optimizer. Passes will be run in the + // exact order of registration. The token passed in will be consumed by this + // method. + Optimizer& RegisterPass(PassToken&& pass); + + // Registers passes that attempt to improve performance of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + Optimizer& RegisterPerformancePasses(); + Optimizer& RegisterPerformancePasses(bool preserve_interface); + + // Registers passes that attempt to improve the size of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + Optimizer& RegisterSizePasses(); + Optimizer& RegisterSizePasses(bool preserve_interface); + + // Registers passes that attempt to legalize the generated code. + // + // Note: this recipe is specially designed for legalizing SPIR-V. It should be + // used by compilers after translating HLSL source code literally. It should + // *not* be used by general workloads for performance or size improvement. + // + // This sequence of passes is subject to constant review and will change + // from time to time. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + Optimizer& RegisterLegalizationPasses(); + Optimizer& RegisterLegalizationPasses(bool preserve_interface); + + // Register passes specified in the list of |flags|. Each flag must be a + // string of a form accepted by Optimizer::FlagHasValidForm(). + // + // If the list of flags contains an invalid entry, it returns false and an + // error message is emitted to the MessageConsumer object (use + // Optimizer::SetMessageConsumer to define a message consumer, if needed). + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + // + // If all the passes are registered successfully, it returns true. + bool RegisterPassesFromFlags(const std::vector& flags); + bool RegisterPassesFromFlags(const std::vector& flags, + bool preserve_interface); + + // Registers the optimization pass associated with |flag|. This only accepts + // |flag| values of the form "--pass_name[=pass_args]". If no such pass + // exists, it returns false. Otherwise, the pass is registered and it returns + // true. + // + // The following flags have special meaning: + // + // -O: Registers all performance optimization passes + // (Optimizer::RegisterPerformancePasses) + // + // -Os: Registers all size optimization passes + // (Optimizer::RegisterSizePasses). + // + // --legalize-hlsl: Registers all passes that legalize SPIR-V generated by an + // HLSL front-end. + // + // If |preserve_interface| is true, all non-io variables in the entry point + // interface are considered live and are not eliminated. + bool RegisterPassFromFlag(const std::string& flag); + bool RegisterPassFromFlag(const std::string& flag, bool preserve_interface); + + // Validates that |flag| has a valid format. Strings accepted: + // + // --pass_name[=pass_args] + // -O + // -Os + // + // If |flag| takes one of the forms above, it returns true. Otherwise, it + // returns false. + bool FlagHasValidForm(const std::string& flag) const; + + // Allows changing, after creation time, the target environment to be + // optimized for and validated. Should be called before calling Run(). + void SetTargetEnv(const spv_target_env env); + + // Optimizes the given SPIR-V module |original_binary| and writes the + // optimized binary into |optimized_binary|. The optimized binary uses + // the same SPIR-V version as the original binary. + // + // Returns true on successful optimization, whether or not the module is + // modified. Returns false if |original_binary| fails to validate or if errors + // occur when processing |original_binary| using any of the registered passes. + // In that case, no further passes are executed and the contents in + // |optimized_binary| may be invalid. + // + // By default, the binary is validated before any transforms are performed, + // and optionally after each transform. Validation uses SPIR-V spec rules + // for the SPIR-V version named in the binary's header (at word offset 1). + // Additionally, if the target environment is a client API (such as + // Vulkan 1.1), then validate for that client API version, to the extent + // that it is verifiable from data in the binary itself. + // + // It's allowed to alias |original_binary| to the start of |optimized_binary|. + bool Run(const uint32_t* original_binary, size_t original_binary_size, + std::vector* optimized_binary) const; + + // DEPRECATED: Same as above, except passes |options| to the validator when + // trying to validate the binary. If |skip_validation| is true, then the + // caller is guaranteeing that |original_binary| is valid, and the validator + // will not be run. The |max_id_bound| is the limit on the max id in the + // module. + bool Run(const uint32_t* original_binary, const size_t original_binary_size, + std::vector* optimized_binary, + const ValidatorOptions& options, bool skip_validation) const; + + // Same as above, except it takes an options object. See the documentation + // for |OptimizerOptions| to see which options can be set. + // + // By default, the binary is validated before any transforms are performed, + // and optionally after each transform. Validation uses SPIR-V spec rules + // for the SPIR-V version named in the binary's header (at word offset 1). + // Additionally, if the target environment is a client API (such as + // Vulkan 1.1), then validate for that client API version, to the extent + // that it is verifiable from data in the binary itself, or from the + // validator options set on the optimizer options. + bool Run(const uint32_t* original_binary, const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const; + + // Returns a vector of strings with all the pass names added to this + // optimizer's pass manager. These strings are valid until the associated + // pass manager is destroyed. + std::vector GetPassNames() const; + + // Sets the option to print the disassembly before each pass and after the + // last pass. If |out| is null, then no output is generated. Otherwise, + // output is sent to the |out| output stream. + Optimizer& SetPrintAll(std::ostream* out); + + // Sets the option to print the resource utilization of each pass. If |out| + // is null, then no output is generated. Otherwise, output is sent to the + // |out| output stream. + Optimizer& SetTimeReport(std::ostream* out); + + // Sets the option to validate the module after each pass. + Optimizer& SetValidateAfterAll(bool validate); + + private: + struct SPIRV_TOOLS_LOCAL Impl; // Opaque struct for holding internal data. + std::unique_ptr impl_; // Unique pointer to internal data. +}; + +// Creates a null pass. +// A null pass does nothing to the SPIR-V module to be optimized. +Optimizer::PassToken CreateNullPass(); + +// Creates a strip-debug-info pass. +// A strip-debug-info pass removes all debug instructions (as documented in +// Section 3.42.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +Optimizer::PassToken CreateStripDebugInfoPass(); + +// [Deprecated] This will create a strip-nonsemantic-info pass. See below. +Optimizer::PassToken CreateStripReflectInfoPass(); + +// Creates a strip-nonsemantic-info pass. +// A strip-nonsemantic-info pass removes all reflections and explicitly +// non-semantic instructions. +Optimizer::PassToken CreateStripNonSemanticInfoPass(); + +// Creates an eliminate-dead-functions pass. +// An eliminate-dead-functions pass will remove all functions that are not in +// the call trees rooted at entry points and exported functions. These +// functions are not needed because they will never be called. +Optimizer::PassToken CreateEliminateDeadFunctionsPass(); + +// Creates an eliminate-dead-members pass. +// An eliminate-dead-members pass will remove all unused members of structures. +// This will not affect the data layout of the remaining members. +Optimizer::PassToken CreateEliminateDeadMembersPass(); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of string. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map& id_value_map); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of bit pattern. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map>& id_value_map); + +// Creates a flatten-decoration pass. +// A flatten-decoration pass replaces grouped decorations with equivalent +// ungrouped decorations. That is, it replaces each OpDecorationGroup +// instruction and associated OpGroupDecorate and OpGroupMemberDecorate +// instructions with equivalent OpDecorate and OpMemberDecorate instructions. +// The pass does not attempt to preserve debug information for instructions +// it removes. +Optimizer::PassToken CreateFlattenDecorationPass(); + +// Creates a freeze-spec-constant-value pass. +// A freeze-spec-constant pass specializes the value of spec constants to +// their default values. This pass only processes the spec constants that have +// SpecId decorations (defined by OpSpecConstant, OpSpecConstantTrue, or +// OpSpecConstantFalse instructions) and replaces them with their normal +// counterparts (OpConstant, OpConstantTrue, or OpConstantFalse). The +// corresponding SpecId annotation instructions will also be removed. This +// pass does not fold the newly added normal constants and does not process +// other spec constants defined by OpSpecConstantComposite or +// OpSpecConstantOp. +Optimizer::PassToken CreateFreezeSpecConstantValuePass(); + +// Creates a fold-spec-constant-op-and-composite pass. +// A fold-spec-constant-op-and-composite pass folds spec constants defined by +// OpSpecConstantOp or OpSpecConstantComposite instruction, to normal Constants +// defined by OpConstantTrue, OpConstantFalse, OpConstant, OpConstantNull, or +// OpConstantComposite instructions. Note that spec constants defined with +// OpSpecConstant, OpSpecConstantTrue, or OpSpecConstantFalse instructions are +// not handled, as these instructions indicate their value are not determined +// and can be changed in future. A spec constant is foldable if all of its +// value(s) can be determined from the module. E.g., an integer spec constant +// defined with OpSpecConstantOp instruction can be folded if its value won't +// change later. This pass will replace the original OpSpecConstantOp +// instruction with an OpConstant instruction. When folding composite spec +// constants, new instructions may be inserted to define the components of the +// composite constant first, then the original spec constants will be replaced +// by OpConstantComposite instructions. +// +// There are some operations not supported yet: +// OpSConvert, OpFConvert, OpQuantizeToF16 and +// all the operations under Kernel capability. +// TODO(qining): Add support for the operations listed above. +Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass(); + +// Creates a unify-constant pass. +// A unify-constant pass de-duplicates the constants. Constants with the exact +// same value and identical form will be unified and only one constant will +// be kept for each unique pair of type and value. +// There are several cases not handled by this pass: +// 1) Constants defined by OpConstantNull instructions (null constants) and +// constants defined by OpConstantFalse, OpConstant or OpConstantComposite +// with value 0 (zero-valued normal constants) are not considered equivalent. +// So null constants won't be used to replace zero-valued normal constants, +// vice versa. +// 2) Whenever there are decorations to the constant's result id id, the +// constant won't be handled, which means, it won't be used to replace any +// other constants, neither can other constants replace it. +// 3) NaN in float point format with different bit patterns are not unified. +Optimizer::PassToken CreateUnifyConstantPass(); + +// Creates a eliminate-dead-constant pass. +// A eliminate-dead-constant pass removes dead constants, including normal +// constants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// OpConstantFalse and spec constants defined by OpSpecConstant, +// OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or +// OpSpecConstantOp. +Optimizer::PassToken CreateEliminateDeadConstantPass(); + +// Creates a strength-reduction pass. +// A strength-reduction pass will look for opportunities to replace an +// instruction with an equivalent and less expensive one. For example, +// multiplying by a power of 2 can be replaced by a bit shift. +Optimizer::PassToken CreateStrengthReductionPass(); + +// Creates a block merge pass. +// This pass searches for blocks with a single Branch to a block with no +// other predecessors and merges the blocks into a single block. Continue +// blocks and Merge blocks are not candidates for the second block. +// +// The pass is most useful after Dead Branch Elimination, which can leave +// such sequences of blocks. Merging them makes subsequent passes more +// effective, such as single block local store-load elimination. +// +// While this pass reduces the number of occurrences of this sequence, at +// this time it does not guarantee all such sequences are eliminated. +// +// Presence of phi instructions can inhibit this optimization. Handling +// these is left for future improvements. +Optimizer::PassToken CreateBlockMergePass(); + +// Creates an exhaustive inline pass. +// An exhaustive inline pass attempts to exhaustively inline all function +// calls in all functions in an entry point call tree. The intent is to enable, +// albeit through brute force, analysis and optimization across function +// calls by subsequent optimization passes. As the inlining is exhaustive, +// there is no attempt to optimize for size or runtime performance. Functions +// that are not in the call tree of an entry point are not changed. +Optimizer::PassToken CreateInlineExhaustivePass(); + +// Creates an opaque inline pass. +// An opaque inline pass inlines all function calls in all functions in all +// entry point call trees where the called function contains an opaque type +// in either its parameter types or return type. An opaque type is currently +// defined as Image, Sampler or SampledImage. The intent is to enable, albeit +// through brute force, analysis and optimization across these function calls +// by subsequent passes in order to remove the storing of opaque types which is +// not legal in Vulkan. Functions that are not in the call tree of an entry +// point are not changed. +Optimizer::PassToken CreateInlineOpaquePass(); + +// Creates a single-block local variable load/store elimination pass. +// For every entry point function, do single block memory optimization of +// function variables referenced only with non-access-chain loads and stores. +// For each targeted variable load, if previous store to that variable in the +// block, replace the load's result id with the value id of the store. +// If previous load within the block, replace the current load's result id +// with the previous load's result id. In either case, delete the current +// load. Finally, check if any remaining stores are useless, and delete store +// and variable if possible. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only modules with relaxed logical addressing (see opt/instruction.h) are +// currently processed. +// +// This pass is most effective if preceded by Inlining and +// LocalAccessChainConvert. This pass will reduce the work needed to be done +// by LocalSingleStoreElim and LocalMultiStoreElim. +// +// Only functions in the call tree of an entry point are processed. +Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); + +// Create dead branch elimination pass. +// For each entry point function, this pass will look for SelectionMerge +// BranchConditionals with constant condition and convert to a Branch to +// the indicated label. It will delete resulting dead blocks. +// +// For all phi functions in merge block, replace all uses with the id +// corresponding to the living predecessor. +// +// Note that some branches and blocks may be left to avoid creating invalid +// control flow. Improving this is left to future work. +// +// This pass is most effective when preceded by passes which eliminate +// local loads and stores, effectively propagating constant values where +// possible. +Optimizer::PassToken CreateDeadBranchElimPass(); + +// Creates an SSA local variable load/store elimination pass. +// For every entry point function, eliminate all loads and stores of function +// scope variables only referenced with non-access-chain loads and stores. +// Eliminate the variables as well. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. Currently modules with any extensions enabled are +// not processed. This is left for future work. +// +// This pass is most effective if preceded by Inlining and +// LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim +// will reduce the work that this pass has to do. +Optimizer::PassToken CreateLocalMultiStoreElimPass(); + +// Creates a local access chain conversion pass. +// A local access chain conversion pass identifies all function scope +// variables which are accessed only with loads, stores and access chains +// with constant indices. It then converts all loads and stores of such +// variables into equivalent sequences of loads, stores, extracts and inserts. +// +// This pass only processes entry point functions. It currently only converts +// non-nested, non-ptr access chains. It does not process modules with +// non-32-bit integer types present. Optional memory access options on loads +// and stores are ignored as we are only processing function scope variables. +// +// This pass unifies access to these variables to a single mode and simplifies +// subsequent analysis and elimination of these variables along with their +// loads and stores allowing values to propagate to their points of use where +// possible. +Optimizer::PassToken CreateLocalAccessChainConvertPass(); + +// Creates a local single store elimination pass. +// For each entry point function, this pass eliminates loads and stores for +// function scope variable that are stored to only once, where possible. Only +// whole variable loads and stores are eliminated; access-chain references are +// not optimized. Replace all loads of such variables with the value that is +// stored and eliminate any resulting dead code. +// +// Currently, the presence of access chains and function calls can inhibit this +// pass, however the Inlining and LocalAccessChainConvert passes can make it +// more effective. In additional, many non-load/store memory operations are +// not supported and will prohibit optimization of a function. Support of +// these operations are future work. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. +// +// This pass will reduce the work needed to be done by LocalSingleBlockElim +// and LocalMultiStoreElim and can improve the effectiveness of other passes +// such as DeadBranchElimination which depend on values for their analysis. +Optimizer::PassToken CreateLocalSingleStoreElimPass(); + +// Creates an insert/extract elimination pass. +// This pass processes each entry point function in the module, searching for +// extracts on a sequence of inserts. It further searches the sequence for an +// insert with indices identical to the extract. If such an insert can be +// found before hitting a conflicting insert, the extract's result id is +// replaced with the id of the values from the insert. +// +// Besides removing extracts this pass enables subsequent dead code elimination +// passes to delete the inserts. This pass performs best after access chains are +// converted to inserts and extracts and local loads and stores are eliminated. +Optimizer::PassToken CreateInsertExtractElimPass(); + +// Creates a dead insert elimination pass. +// This pass processes each entry point function in the module, searching for +// unreferenced inserts into composite types. These are most often unused +// stores to vector components. They are unused because they are never +// referenced, or because there is another insert to the same component between +// the insert and the reference. After removing the inserts, dead code +// elimination is attempted on the inserted values. +// +// This pass performs best after access chains are converted to inserts and +// extracts and local loads and stores are eliminated. While executing this +// pass can be advantageous on its own, it is also advantageous to execute +// this pass after CreateInsertExtractPass() as it will remove any unused +// inserts created by that pass. +Optimizer::PassToken CreateDeadInsertElimPass(); + +// Create aggressive dead code elimination pass +// This pass eliminates unused code from the module. In addition, +// it detects and eliminates code which may have spurious uses but which do +// not contribute to the output of the function. The most common cause of +// such code sequences is summations in loops whose result is no longer used +// due to dead code elimination. This optimization has additional compile +// time cost over standard dead code elimination. +// +// This pass only processes entry point functions. It also only processes +// shaders with relaxed logical addressing (see opt/instruction.h). It +// currently will not process functions with function calls. Unreachable +// functions are deleted. +// +// This pass will be made more effective by first running passes that remove +// dead control flow and inlines function calls. +// +// This pass can be especially useful after running Local Access Chain +// Conversion, which tends to cause cycles of dead code to be left after +// Store/Load elimination passes are completed. These cycles cannot be +// eliminated with standard dead code elimination. +// +// If |preserve_interface| is true, all non-io variables in the entry point +// interface are considered live and are not eliminated. This mode is needed +// by GPU-Assisted validation instrumentation, where a change in the interface +// is not allowed. +// +// If |remove_outputs| is true, allow outputs to be removed from the interface. +// This is only safe if the caller knows that there is no corresponding input +// variable in the following shader. It is false by default. +Optimizer::PassToken CreateAggressiveDCEPass(); +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface); +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface, + bool remove_outputs); + +// Creates a remove-unused-interface-variables pass. +// Removes variables referenced on the |OpEntryPoint| instruction that are not +// referenced in the entry point function or any function in its call tree. Note +// that this could cause the shader interface to no longer match other shader +// stages. +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass(); + +// Creates an empty pass. +// This is deprecated and will be removed. +// TODO(jaebaek): remove this pass after handling glslang's broken unit tests. +// https://github.com/KhronosGroup/glslang/pull/2440 +Optimizer::PassToken CreatePropagateLineInfoPass(); + +// Creates an empty pass. +// This is deprecated and will be removed. +// TODO(jaebaek): remove this pass after handling glslang's broken unit tests. +// https://github.com/KhronosGroup/glslang/pull/2440 +Optimizer::PassToken CreateRedundantLineInfoElimPass(); + +// Creates a compact ids pass. +// The pass remaps result ids to a compact and gapless range starting from %1. +Optimizer::PassToken CreateCompactIdsPass(); + +// Creates a remove duplicate pass. +// This pass removes various duplicates: +// * duplicate capabilities; +// * duplicate extended instruction imports; +// * duplicate types; +// * duplicate decorations. +Optimizer::PassToken CreateRemoveDuplicatesPass(); + +// Creates a CFG cleanup pass. +// This pass removes cruft from the control flow graph of functions that are +// reachable from entry points and exported functions. It currently includes the +// following functionality: +// +// - Removal of unreachable basic blocks. +Optimizer::PassToken CreateCFGCleanupPass(); + +// Create dead variable elimination pass. +// This pass will delete module scope variables, along with their decorations, +// that are not referenced. +Optimizer::PassToken CreateDeadVariableEliminationPass(); + +// create merge return pass. +// changes functions that have multiple return statements so they have a single +// return statement. +// +// for structured control flow it is assumed that the only unreachable blocks in +// the function are trivial merge and continue blocks. +// +// a trivial merge block contains the label and an opunreachable instructions, +// nothing else. a trivial continue block contain a label and an opbranch to +// the header, nothing else. +// +// these conditions are guaranteed to be met after running dead-branch +// elimination. +Optimizer::PassToken CreateMergeReturnPass(); + +// Create value numbering pass. +// This pass will look for instructions in the same basic block that compute the +// same value, and remove the redundant ones. +Optimizer::PassToken CreateLocalRedundancyEliminationPass(); + +// Create LICM pass. +// This pass will look for invariant instructions inside loops and hoist them to +// the loops preheader. +Optimizer::PassToken CreateLoopInvariantCodeMotionPass(); + +// Creates a loop fission pass. +// This pass will split all top level loops whose register pressure exceedes the +// given |threshold|. +Optimizer::PassToken CreateLoopFissionPass(size_t threshold); + +// Creates a loop fusion pass. +// This pass will look for adjacent loops that are compatible and legal to be +// fused. The fuse all such loops as long as the register usage for the fused +// loop stays under the threshold defined by |max_registers_per_loop|. +Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop); + +// Creates a loop peeling pass. +// This pass will look for conditions inside a loop that are true or false only +// for the N first or last iteration. For loop with such condition, those N +// iterations of the loop will be executed outside of the main loop. +// To limit code size explosion, the loop peeling can only happen if the code +// size growth for each loop is under |code_growth_threshold|. +Optimizer::PassToken CreateLoopPeelingPass(); + +// Creates a loop unswitch pass. +// This pass will look for loop independent branch conditions and move the +// condition out of the loop and version the loop based on the taken branch. +// Works best after LICM and local multi store elimination pass. +Optimizer::PassToken CreateLoopUnswitchPass(); + +// Create global value numbering pass. +// This pass will look for instructions where the same value is computed on all +// paths leading to the instruction. Those instructions are deleted. +Optimizer::PassToken CreateRedundancyEliminationPass(); + +// Create scalar replacement pass. +// This pass replaces composite function scope variables with variables for each +// element if those elements are accessed individually. The parameter is a +// limit on the number of members in the composite variable that the pass will +// consider replacing. +Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 0); + +// Create a private to local pass. +// This pass looks for variables declared in the private storage class that are +// used in only one function. Those variables are moved to the function storage +// class in the function that they are used. +Optimizer::PassToken CreatePrivateToLocalPass(); + +// Creates a conditional constant propagation (CCP) pass. +// This pass implements the SSA-CCP algorithm in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. +// +// Constant values in expressions and conditional jumps are folded and +// simplified. This may reduce code size by removing never executed jump targets +// and computations with constant operands. +Optimizer::PassToken CreateCCPPass(); + +// Creates a workaround driver bugs pass. This pass attempts to work around +// a known driver bug (issue #1209) by identifying the bad code sequences and +// rewriting them. +// +// Current workaround: Avoid OpUnreachable instructions in loops. +Optimizer::PassToken CreateWorkaround1209Pass(); + +// Creates a pass that converts if-then-else like assignments into OpSelect. +Optimizer::PassToken CreateIfConversionPass(); + +// Creates a pass that will replace instructions that are not valid for the +// current shader stage by constants. Has no effect on non-shader modules. +Optimizer::PassToken CreateReplaceInvalidOpcodePass(); + +// Creates a pass that simplifies instructions using the instruction folder. +Optimizer::PassToken CreateSimplificationPass(); + +// Create loop unroller pass. +// Creates a pass to unroll loops which have the "Unroll" loop control +// mask set. The loops must meet a specific criteria in order to be unrolled +// safely this criteria is checked before doing the unroll by the +// LoopUtils::CanPerformUnroll method. Any loop that does not meet the criteria +// won't be unrolled. See CanPerformUnroll LoopUtils.h for more information. +Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor = 0); + +// Create the SSA rewrite pass. +// This pass converts load/store operations on function local variables into +// operations on SSA IDs. This allows SSA optimizers to act on these variables. +// Only variables that are local to the function and of supported types are +// processed (see IsSSATargetVar for details). +Optimizer::PassToken CreateSSARewritePass(); + +// Create pass to convert relaxed precision instructions to half precision. +// This pass converts as many relaxed float32 arithmetic operations to half as +// possible. It converts any float32 operands to half if needed. It converts +// any resulting half precision values back to float32 as needed. No variables +// are changed. No image operations are changed. +// +// Best if run after function scope store/load and composite operation +// eliminations are run. Also best if followed by instruction simplification, +// redundancy elimination and DCE. +Optimizer::PassToken CreateConvertRelaxedToHalfPass(); + +// Create relax float ops pass. +// This pass decorates all float32 result instructions with RelaxedPrecision +// if not already so decorated. +Optimizer::PassToken CreateRelaxFloatOpsPass(); + +// Create copy propagate arrays pass. +// This pass looks to copy propagate memory references for arrays. It looks +// for specific code patterns to recognize array copies. +Optimizer::PassToken CreateCopyPropagateArraysPass(); + +// Create a vector dce pass. +// This pass looks for components of vectors that are unused, and removes them +// from the vector. Note this would still leave around lots of dead code that +// a pass of ADCE will be able to remove. +Optimizer::PassToken CreateVectorDCEPass(); + +// Create a pass to reduce the size of loads. +// This pass looks for loads of structures where only a few of its members are +// used. It replaces the loads feeding an OpExtract with an OpAccessChain and +// a load of the specific elements. The parameter is a threshold to determine +// whether we have to replace the load or not. If the ratio of the used +// components of the load is less than the threshold, we replace the load. +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold = 0.9); + +// Create a pass to combine chained access chains. +// This pass looks for access chains fed by other access chains and combines +// them into a single instruction where possible. +Optimizer::PassToken CreateCombineAccessChainsPass(); + +// Create a pass to upgrade to the VulkanKHR memory model. +// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR. +// Additionally, it modifies memory, image, atomic and barrier operations to +// conform to that model's requirements. +Optimizer::PassToken CreateUpgradeMemoryModelPass(); + +// Create a pass to do code sinking. Code sinking is a transformation +// where an instruction is moved into a more deeply nested construct. +Optimizer::PassToken CreateCodeSinkingPass(); + +// Create a pass to fix incorrect storage classes. In order to make code +// generation simpler, DXC may generate code where the storage classes do not +// match up correctly. This pass will fix the errors that it can. +Optimizer::PassToken CreateFixStorageClassPass(); + +// Creates a graphics robust access pass. +// +// This pass injects code to clamp indexed accesses to buffers and internal +// arrays, providing guarantees satisfying Vulkan's robustBufferAccess rules. +// +// TODO(dneto): Clamps coordinates and sample index for pointer calculations +// into storage images (OpImageTexelPointer). For an cube array image, it +// assumes the maximum layer count times 6 is at most 0xffffffff. +// +// NOTE: This pass will fail with a message if: +// - The module is not a Shader module. +// - The module declares VariablePointers, VariablePointersStorageBuffer, or +// RuntimeDescriptorArrayEXT capabilities. +// - The module uses an addressing model other than Logical +// - Access chain indices are wider than 64 bits. +// - Access chain index for a struct is not an OpConstant integer or is out +// of range. (The module is already invalid if that is the case.) +// - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits +// wide. +// +// NOTE: Access chain indices are always treated as signed integers. So +// if an array has a fixed size of more than 2^31 elements, then elements +// from 2^31 and above are never accessible with a 32-bit index, +// signed or unsigned. For this case, this pass will clamp the index +// between 0 and at 2^31-1, inclusive. +// Similarly, if an array has more then 2^15 element and is accessed with +// a 16-bit index, then elements from 2^15 and above are not accessible. +// In this case, the pass will clamp the index between 0 and 2^15-1 +// inclusive. +Optimizer::PassToken CreateGraphicsRobustAccessPass(); + +// Create a pass to spread Volatile semantics to variables with SMIDNV, +// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, +// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn +// decorations or OpLoad for them when the shader model is the ray generation, +// closest hit, miss, intersection, or callable. This pass can be used for +// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and +// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V +// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for +// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads +// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration +// in the fragement shader. +Optimizer::PassToken CreateSpreadVolatileSemanticsPass(); + +// Create a pass to replace a descriptor access using variable index. +// This pass replaces every access using a variable index to array variable +// |desc| that has a DescriptorSet and Binding decorations with a constant +// element of the array. In order to replace the access using a variable index +// with the constant element, it uses a switch statement. +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass(); + +// Create descriptor scalar replacement pass. +// This pass replaces every array variable |desc| that has a DescriptorSet and +// Binding decorations with a new variable for each element of the +// array/composite. Suppose |desc| was bound at binding |b|. Then the variable +// corresponding to |desc[i]| will have binding |b+i|. The descriptor set will +// be the same. It is assumed that no other variable already has a binding that +// will used by one of the new variables. If not, the pass will generate +// invalid Spir-V. All accesses to |desc| must be OpAccessChain instructions +// with a literal index for the first index. This variant flattens both +// composites and arrays. +Optimizer::PassToken CreateDescriptorScalarReplacementPass(); +// This variant flattens only composites. +Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass(); +// This variant flattens only arrays. +Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass(); + +// Create a pass to replace each OpKill instruction with a function call to a +// function that has a single OpKill. Also replace each OpTerminateInvocation +// instruction with a function call to a function that has a single +// OpTerminateInvocation. This allows more code to be inlined. +Optimizer::PassToken CreateWrapOpKillPass(); + +// Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and +// capabilities. +Optimizer::PassToken CreateAmdExtToKhrPass(); + +// Replaces the internal version of GLSLstd450 InterpolateAt* extended +// instructions with the externally valid version. The internal version allows +// an OpLoad of the interpolant for the first argument. This pass removes the +// OpLoad and replaces it with its pointer. glslang and possibly other +// frontends will create the internal version for HLSL. This pass will be part +// of HLSL legalization and should be called after interpolants have been +// propagated into their final positions. +Optimizer::PassToken CreateInterpolateFixupPass(); + +// Replace OpExtInst instructions with OpExtInstWithForwardRefsKHR when +// the instruction contains a forward reference to another debug instuction. +// Replace OpExtInstWithForwardRefsKHR with OpExtInst when there are no forward +// reference to another debug instruction. +Optimizer::PassToken CreateOpExtInstWithForwardReferenceFixupPass(); + +// Removes unused components from composite input variables. Current +// implementation just removes trailing unused components from input arrays +// and structs. The pass performs best after maximizing dead code removal. +// A subsequent dead code elimination pass would be beneficial in removing +// newly unused component types. +// +// WARNING: This pass can only be safely applied standalone to vertex shaders +// as it can otherwise cause interface incompatibilities with the preceding +// shader in the pipeline. If applied to non-vertex shaders, the user should +// follow by applying EliminateDeadOutputStores and +// EliminateDeadOutputComponents to the preceding shader. +Optimizer::PassToken CreateEliminateDeadInputComponentsPass(); + +// Removes unused components from composite output variables. Current +// implementation just removes trailing unused components from output arrays +// and structs. The pass performs best after eliminating dead output stores. +// A subsequent dead code elimination pass would be beneficial in removing +// newly unused component types. Currently only supports vertex and fragment +// shaders. +// +// WARNING: This pass cannot be safely applied standalone as it can cause +// interface incompatibility with the following shader in the pipeline. The +// user should first apply EliminateDeadInputComponents to the following +// shader, then apply EliminateDeadOutputStores to this shader. +Optimizer::PassToken CreateEliminateDeadOutputComponentsPass(); + +// Removes unused components from composite input variables. This safe +// version will not cause interface incompatibilities since it only changes +// vertex shaders. The current implementation just removes trailing unused +// components from input structs and input arrays. The pass performs best +// after maximizing dead code removal. A subsequent dead code elimination +// pass would be beneficial in removing newly unused component types. +Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass(); + +// Analyzes shader and populates |live_locs| and |live_builtins|. Best results +// will be obtained if shader has all dead code eliminated first. |live_locs| +// and |live_builtins| are subsequently used when calling +// CreateEliminateDeadOutputStoresPass on the preceding shader. Currently only +// supports tesc, tese, geom, and frag shaders. +Optimizer::PassToken CreateAnalyzeLiveInputPass( + std::unordered_set* live_locs, + std::unordered_set* live_builtins); + +// Removes stores to output locations not listed in |live_locs| or +// |live_builtins|. Best results are obtained if constant propagation is +// performed first. A subsequent call to ADCE will eliminate any dead code +// created by the removal of the stores. A subsequent call to +// CreateEliminateDeadOutputComponentsPass will eliminate any dead output +// components created by the elimination of the stores. Currently only supports +// vert, tesc, tese, and geom shaders. +Optimizer::PassToken CreateEliminateDeadOutputStoresPass( + std::unordered_set* live_locs, + std::unordered_set* live_builtins); + +// Creates a convert-to-sampled-image pass to convert images and/or +// samplers with given pairs of descriptor set and binding to sampled image. +// If a pair of an image and a sampler have the same pair of descriptor set and +// binding that is one of the given pairs, they will be converted to a sampled +// image. In addition, if only an image has the descriptor set and binding that +// is one of the given pairs, it will be converted to a sampled image as well. +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs); + +// Create an interface-variable-scalar-replacement pass that replaces array or +// matrix interface variables with a series of scalar or vector interface +// variables. For example, it replaces `float3 foo[2]` with `float3 foo0, foo1`. +Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass(); + +// Creates a remove-dont-inline pass to remove the |DontInline| function control +// from every function in the module. This is useful if you want the inliner to +// inline these functions some reason. +Optimizer::PassToken CreateRemoveDontInlinePass(); +// Create a fix-func-call-param pass to fix non memory argument for the function +// call, as spirv-validation requires function parameters to be an memory +// object, currently the pass would remove accesschain pointer argument passed +// to the function +Optimizer::PassToken CreateFixFuncCallArgumentsPass(); + +// Creates a trim-capabilities pass. +// This pass removes unused capabilities for a given module, and if possible, +// associated extensions. +// See `trim_capabilities.h` for the list of supported capabilities. +// +// If the module contains unsupported capabilities, this pass will ignore them. +// This should be fine in most cases, but could yield to incorrect results if +// the unknown capability interacts with one of the trimmed capabilities. +Optimizer::PassToken CreateTrimCapabilitiesPass(); + +// Creates a struct-packing pass. +// This pass re-assigns all offset layout decorators to tightly pack +// the struct with OpName matching `structToPack` according to the given packing +// rule. Accepted packing rules are: std140, std140EnhancedLayout, std430, +// std430EnhancedLayout, hlslCbuffer, hlslCbufferPackOffset, scalar, +// scalarEnhancedLayout. +Optimizer::PassToken CreateStructPackingPass(const char* structToPack, + const char* packingRule); + +// Creates a switch-descriptorset pass. +// This pass changes any DescriptorSet decorations with the value |ds_from| to +// use the new value |ds_to|. +Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t ds_from, + uint32_t ds_to); + +// Creates an invocation interlock placement pass. +// This pass ensures that an entry point will have at most one +// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that +// order. +Optimizer::PassToken CreateInvocationInterlockPlacementPass(); + +// Creates a pass to add/remove maximal reconvergence execution mode. +// This pass either adds or removes maximal reconvergence from all entry points. +Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add); + +// Creates a pass to split combined image+sampler variables and function +// parameters into separate image and sampler parts. Binding numbers and +// other decorations are copied. +Optimizer::PassToken CreateSplitCombinedImageSamplerPass(); + +// Creates a pass to remap bindings to avoid conflicts, assuming the module +// is valid for Vulkan. A conflict exits when an entry point uses two distinct +// variables with the same descriptor set and binding. Vulkan allows one kind +// of conflict: when one varible is an image (or array of images), and the +// other is a sampler (or an array of samplers). + +// Conflicts are eliminated by incrementing the binding number of the sampler +// part, and then propagating that increment through variables with +// higher-numbered bindings until no conflict remains. This handles the case +// when multiple shaders may share the same resource variables; this can +// introduce holes in binding slots. +// +// Here's an example where shaders Alpha, Beta, Gamma, Delta collectively use +// resource variables %100, %101, %102, %103, %104 all with the same +// DescriptorSet and with Bindings as in the following table: +// +// Before: +// +// Binding: 0 1 2 3 +// Alpha: %100,%101 +// Beta: %100 %102 +// Gamma: %102 %103 +// Delta: %103 %104 +// +// The Alpha shader has a conflict where variables %100, %101 have the same +// descriptor set and binding. If %100 is a sampler resource variable, then +// the conflict is resolved by incrementing the binding number on %100 from 0 +// to 1. But this causes a new confict for shader Beta because it now uses +// both %100 and %102 with binding number 1. That conflict is resolved by +// incrementing the binding number on its variable that originally appeared +// second (i.e. %102), so %102 gets binding 2. This now produces a conflict +// for Gamma between %102 and %103 using binding number 2. Since %103 originally +// appeared second (in the view from Gamma), the algorithm bumps %103 to binding +// number %103. Now Delta has a conflict between %103 and %104, resulting in +// %104 getting the next binding number, 4. The picture afterward is: +// +// After: +// +// Binding: 0 1 2 3 4 +// Alpha: %101 %100 +// Beta: %100 %102 +// Gamma: %102 %103 +// Delta: %103 %104 +// +// +// This pass assumes binding numbers are not applid via decoration groups +// (OpDecorationGroup). +Optimizer::PassToken CreateResolveBindingConflictsPass(); +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/CMakeLists.txt new file mode 100644 index 00000000..3af15a4e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/CMakeLists.txt @@ -0,0 +1,443 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py") +set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/vim/generate_syntax.py") +set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") +set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") + +# Pull in grammar files that have migrated to SPIRV-Headers +set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") +set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") +set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json") + +# macro() definitions are used in the following because we need to append .inc +# file paths into some global lists (*_CPP_DEPENDS). And those global lists are +# later used by set_source_files_properties() calls. +# function() definitions are not suitable because they create new scopes. +macro(spvtools_core_tables CONFIG_VERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GRAMMAR_INSTS_INC_FILE "${spirv-tools_BINARY_DIR}/core.insts-${CONFIG_VERSION}.inc") + set(GRAMMAR_KINDS_INC_FILE "${spirv-tools_BINARY_DIR}/operand.kinds-${CONFIG_VERSION}.inc") + add_custom_command(OUTPUT ${GRAMMAR_INSTS_INC_FILE} ${GRAMMAR_KINDS_INC_FILE} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + --core-insts-output=${GRAMMAR_INSTS_INC_FILE} + --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.") + list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE}) + list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE}) +endmacro(spvtools_core_tables) + +macro(spvtools_enum_string_mapping CONFIG_VERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GRAMMAR_EXTENSION_ENUM_INC_FILE "${spirv-tools_BINARY_DIR}/extension_enum.inc") + set(GRAMMAR_ENUM_STRING_MAPPING_INC_FILE "${spirv-tools_BINARY_DIR}/enum_string_mapping.inc") + add_custom_command(OUTPUT ${GRAMMAR_EXTENSION_ENUM_INC_FILE} + ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE} + --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.") + list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE}) + list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}) +endmacro(spvtools_enum_string_mapping) + +macro(spvtools_vimsyntax CONFIG_VERSION CLVERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json") + set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") + set(VIMSYNTAX_FILE "${spirv-tools_BINARY_DIR}/spvasm.vim") + add_custom_command(OUTPUT ${VIMSYNTAX_FILE} + COMMAND Python3::Interpreter ${VIMSYNTAX_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} + --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} + >${VIMSYNTAX_FILE} + DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} + ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE} + COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.") +endmacro(spvtools_vimsyntax) + +macro(spvtools_glsl_tables CONFIG_VERSION) + set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json") + set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/glsl.std.450.insts.inc") + add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} + --glsl-insts-output=${GRAMMAR_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${GLSL_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for GLSL extended instructions and operands v${CONFIG_VERSION}.") + list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) +endmacro(spvtools_glsl_tables) + +macro(spvtools_opencl_tables CONFIG_VERSION) + set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") + set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/opencl.std.insts.inc") + add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} + --opencl-insts-output=${GRAMMAR_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for OpenCL extended instructions and operands v${CONFIG_VERSION}.") + list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) +endmacro(spvtools_opencl_tables) + +macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) + set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") + set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") + if(NOT EXISTS ${GRAMMAR_FILE}) + set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json") + endif() + add_custom_command(OUTPUT ${INSTS_FILE} + COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-vendor-grammar=${GRAMMAR_FILE} + --vendor-insts-output=${INSTS_FILE} + --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE} + COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.") + add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE}) + set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build") + list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME}) +endmacro(spvtools_vendor_tables) + +macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) + set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h) + add_custom_command(OUTPUT ${OUT_H} + COMMAND Python3::Interpreter ${LANG_HEADER_PROCESSING_SCRIPT} + --extinst-grammar=${GRAMMAR_FILE} + --extinst-output-path=${OUT_H} + DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE} + COMMENT "Generate language specific header for ${NAME}.") + add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H}) + set_property(TARGET spirv-tools-header-${NAME} PROPERTY FOLDER "SPIRV-Tools build") + list(APPEND EXTINST_CPP_DEPENDS spirv-tools-header-${NAME}) +endmacro(spvtools_extinst_lang_headers) + +spvtools_core_tables("unified1") +spvtools_enum_string_mapping("unified1") +spvtools_opencl_tables("unified1") +spvtools_glsl_tables("unified1") +spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "") +spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "") +spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") +spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") +spvtools_vendor_tables("debuginfo" "debuginfo" "") +spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") +spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") +spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") +spvtools_vendor_tables("nonsemantic.vkspreflection" "vkspreflection" "") +spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) + +spvtools_vimsyntax("unified1" "1.0") +add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) +set_property(TARGET spirv-tools-vimsyntax PROPERTY FOLDER "SPIRV-Tools utilities") + +# Extract the list of known generators from the SPIR-V XML registry file. +set(GENERATOR_INC_FILE ${spirv-tools_BINARY_DIR}/generators.inc) +set(SPIRV_XML_REGISTRY_FILE ${SPIRV_HEADER_INCLUDE_DIR}/spirv/spir-v.xml) +add_custom_command(OUTPUT ${GENERATOR_INC_FILE} + COMMAND Python3::Interpreter ${XML_REGISTRY_PROCESSING_SCRIPT} + --xml=${SPIRV_XML_REGISTRY_FILE} + --generator-output=${GENERATOR_INC_FILE} + DEPENDS ${XML_REGISTRY_PROCESSING_SCRIPT} ${SPIRV_XML_REGISTRY_FILE} + COMMENT "Generate tables based on the SPIR-V XML registry.") +list(APPEND OPCODE_CPP_DEPENDS ${GENERATOR_INC_FILE}) + +# The following .cpp files include the above generated .inc files. +# Add those .inc files as their dependencies. +# +# We need to wrap the .inc files with a custom target to avoid problems when +# multiple targets depend on the same custom command. +add_custom_target(core_tables + DEPENDS ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS}) +add_custom_target(enum_string_mapping + DEPENDS ${EXTENSION_H_DEPENDS} ${ENUM_STRING_MAPPING_CPP_DEPENDS}) +add_custom_target(extinst_tables + DEPENDS ${EXTINST_CPP_DEPENDS}) + +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h + PROPERTIES HEADER_FILE_ONLY TRUE) + +set(SPIRV_TOOLS_BUILD_VERSION_INC + ${spirv-tools_BINARY_DIR}/build-version.inc) +set(SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR + ${spirv-tools_SOURCE_DIR}/utils/update_build_version.py) +set(SPIRV_TOOLS_CHANGES_FILE + ${spirv-tools_SOURCE_DIR}/CHANGES) +add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC} + COMMAND Python3::Interpreter + ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} + ${SPIRV_TOOLS_CHANGES_FILE} ${SPIRV_TOOLS_BUILD_VERSION_INC} + DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} + ${SPIRV_TOOLS_CHANGES_FILE} + COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).") +# Convenience target for standalone generation of the build-version.inc file. +# This is not required for any dependence chain. +add_custom_target(spirv-tools-build-version + DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC}) +set_property(TARGET spirv-tools-build-version PROPERTY FOLDER "SPIRV-Tools build") + +list(APPEND PCH_DEPENDS ${ENUM_STRING_MAPPING_CPP_DEPENDS} ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS} ${EXTENSION_H_DEPENDS} ${EXTINST_CPP_DEPENDS} ${SPIRV_TOOLS_BUILD_VERSION_INC}) +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/pch_source.cpp + PROPERTIES OBJECT_DEPENDS "${PCH_DEPENDS}") + +add_subdirectory(opt) +add_subdirectory(reduce) +add_subdirectory(fuzz) +add_subdirectory(link) +add_subdirectory(lint) +add_subdirectory(diff) + +set(SPIRV_SOURCES + ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h + + ${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hash_combine.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/small_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.h + ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h + ${CMAKE_CURRENT_SOURCE_DIR}/binary.h + ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h + ${CMAKE_CURRENT_SOURCE_DIR}/common_debug_info.h + ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h + ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h + ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h + ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h + ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_glsl_std_450_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_opencl_std_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_spirv_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/macro.h + ${CMAKE_CURRENT_SOURCE_DIR}/name_mapper.h + ${CMAKE_CURRENT_SOURCE_DIR}/opcode.h + ${CMAKE_CURRENT_SOURCE_DIR}/operand.h + ${CMAKE_CURRENT_SOURCE_DIR}/parsed_operand.h + ${CMAKE_CURRENT_SOURCE_DIR}/print.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_constant.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/table.h + ${CMAKE_CURRENT_SOURCE_DIR}/text.h + ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/to_string.h + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate.h + + ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libspirv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/name_mapper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/opcode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/operand.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/parsed_operand.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/text.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/to_string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_adjacency.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_annotation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_arithmetics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_atomics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_barriers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_bitwise.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_builtins.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_capability.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_cfg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_composites.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_constants.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_conversion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_id.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_image.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_interfaces.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_instruction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_literals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logicals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory_semantics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mesh_shading.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_misc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing_reorder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_tensor_layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_invalid_type.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h + ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/construct.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/instruction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validation_state.cpp) + +if (${SPIRV_TIMER_ENABLED}) + set(SPIRV_SOURCES + ${SPIRV_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.cpp) +endif() + +# The software_version.cpp file includes build-version.inc. +# Rebuild the software_version.cpp object file if it is older than +# build-version.inc or whenever build-version.inc itself is out of +# date. In the latter case, rebuild build-version.inc first. +# CMake is not smart enough to detect this dependency automatically. +# Without this, the dependency detection system for #included files +# does not kick in on a clean build for the following reason: The +# build will fail early because it doesn't know how to build the +# missing source file build-version.inc. That occurs before the +# preprocessor is run on software_version.cpp to detect the +# #include dependency. +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp + PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}") + +spvtools_pch(SPIRV_SOURCES pch_source) + +# spirv_tools_default_target_options() sets the target options that are common +# for all ${SPIRV_TOOLS} targets. +function(spirv_tools_default_target_options target) + spvtools_default_compile_options(${target}) + target_include_directories(${target} + PUBLIC + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE ${SPIRV_HEADER_INCLUDE_DIR} + ) + set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries") + spvtools_check_symbol_exports(${target}) + add_dependencies(${target} spirv-tools-build-version core_tables enum_string_mapping extinst_tables) +endfunction() + +# Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and +# unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility. +add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES}) +spirv_tools_default_target_options(${SPIRV_TOOLS}-shared) +set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${SPIRV_TOOLS}-shared + PRIVATE SPIRV_TOOLS_IMPLEMENTATION + PUBLIC SPIRV_TOOLS_SHAREDLIB +) + +if(SPIRV_TOOLS_BUILD_STATIC) + add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES}) + spirv_tools_default_target_options(${SPIRV_TOOLS}-static) + # The static target does not have the '-static' suffix. + set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}") + + # Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static" + # or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS. + if(BUILD_SHARED_LIBS) + add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared) + else() + add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static) + endif() + + set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared) +else() + add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES}) + spirv_tools_default_target_options(${SPIRV_TOOLS}) + set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared) +endif() + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + find_library(LIBRT rt) + if(LIBRT) + foreach(target ${SPIRV_TOOLS_TARGETS}) + target_link_libraries(${target} rt) + endforeach() + endif() +endif() + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets) + export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake) + + spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR) + install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR}) + + # Special config file for root library compared to other libs. + file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake + "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n" + "if(TARGET ${SPIRV_TOOLS})\n" + " set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" + " get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n" + "endif()\n") + install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() diff --git a/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.cpp b/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.cpp new file mode 100644 index 00000000..0092d01a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.cpp @@ -0,0 +1,269 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/assembly_grammar.h" + +#include +#include +#include + +#include "source/ext_inst.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_target_env.h" +#include "source/table.h" + +namespace spvtools { +namespace { + +/// @brief Parses a mask expression string for the given operand type. +/// +/// A mask expression is a sequence of one or more terms separated by '|', +/// where each term a named enum value for the given type. No whitespace +/// is permitted. +/// +/// On success, the value is written to pValue. +/// +/// @param[in] operandTable operand lookup table +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[out] pValue where the resulting value is written +/// +/// @return result code +spv_result_t spvTextParseMaskOperand(spv_target_env env, + const spv_operand_table operandTable, + const spv_operand_type_t type, + const char* textValue, uint32_t* pValue) { + if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; + size_t text_length = strlen(textValue); + if (text_length == 0) return SPV_ERROR_INVALID_TEXT; + const char* text_end = textValue + text_length; + + // We only support mask expressions in ASCII, so the separator value is a + // char. + const char separator = '|'; + + // Accumulate the result by interpreting one word at a time, scanning + // from left to right. + uint32_t value = 0; + const char* begin = textValue; // The left end of the current word. + const char* end = nullptr; // One character past the end of the current word. + do { + end = std::find(begin, text_end, separator); + + spv_operand_desc entry = nullptr; + if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin, + end - begin, &entry)) { + return error; + } + value |= entry->value; + + // Advance to the next word by skipping over the separator. + begin = end + 1; + } while (end != text_end); + + *pValue = value; + return SPV_SUCCESS; +} + +// Associates an opcode with its name. +struct SpecConstantOpcodeEntry { + spv::Op opcode; + const char* name; +}; + +// All the opcodes allowed as the operation for OpSpecConstantOp. +// The name does not have the usual "Op" prefix. For example opcode +// spv::Op::IAdd is associated with the name "IAdd". +// +// clang-format off +#define CASE(NAME) { spv::Op::Op##NAME, #NAME } +const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = { + // Conversion + CASE(SConvert), + CASE(FConvert), + CASE(ConvertFToS), + CASE(ConvertSToF), + CASE(ConvertFToU), + CASE(ConvertUToF), + CASE(UConvert), + CASE(ConvertPtrToU), + CASE(ConvertUToPtr), + CASE(GenericCastToPtr), + CASE(PtrCastToGeneric), + CASE(Bitcast), + CASE(QuantizeToF16), + // Arithmetic + CASE(SNegate), + CASE(Not), + CASE(IAdd), + CASE(ISub), + CASE(IMul), + CASE(UDiv), + CASE(SDiv), + CASE(UMod), + CASE(SRem), + CASE(SMod), + CASE(ShiftRightLogical), + CASE(ShiftRightArithmetic), + CASE(ShiftLeftLogical), + CASE(BitwiseOr), + CASE(BitwiseAnd), + CASE(BitwiseXor), + CASE(FNegate), + CASE(FAdd), + CASE(FSub), + CASE(FMul), + CASE(FDiv), + CASE(FRem), + CASE(FMod), + // Composite + CASE(VectorShuffle), + CASE(CompositeExtract), + CASE(CompositeInsert), + // Logical + CASE(LogicalOr), + CASE(LogicalAnd), + CASE(LogicalNot), + CASE(LogicalEqual), + CASE(LogicalNotEqual), + CASE(Select), + // Comparison + CASE(IEqual), + CASE(INotEqual), + CASE(ULessThan), + CASE(SLessThan), + CASE(UGreaterThan), + CASE(SGreaterThan), + CASE(ULessThanEqual), + CASE(SLessThanEqual), + CASE(UGreaterThanEqual), + CASE(SGreaterThanEqual), + // Memory + CASE(AccessChain), + CASE(InBoundsAccessChain), + CASE(PtrAccessChain), + CASE(InBoundsPtrAccessChain), + CASE(CooperativeMatrixLengthNV), + CASE(CooperativeMatrixLengthKHR) +}; + +// The 60 is determined by counting the opcodes listed in the spec. +static_assert(61 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), + "OpSpecConstantOp opcode table is incomplete"); +#undef CASE +// clang-format on + +const size_t kNumOpSpecConstantOpcodes = + sizeof(kOpSpecConstantOpcodes) / sizeof(kOpSpecConstantOpcodes[0]); + +} // namespace + +bool AssemblyGrammar::isValid() const { + return operandTable_ && opcodeTable_ && extInstTable_; +} + +CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv( + const spv::Capability* cap_array, uint32_t count) const { + CapabilitySet cap_set; + const auto version = spvVersionForTargetEnv(target_env_); + for (uint32_t i = 0; i < count; ++i) { + spv_operand_desc entry = {}; + if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast(cap_array[i]), + &entry)) { + // This token is visible in this environment if it's in an appropriate + // core version, or it is enabled by a capability or an extension. + if ((version >= entry->minVersion && version <= entry->lastVersion) || + entry->numExtensions > 0u || entry->numCapabilities > 0u) { + cap_set.insert(cap_array[i]); + } + } + } + return cap_set; +} + +spv_result_t AssemblyGrammar::lookupOpcode(const char* name, + spv_opcode_desc* desc) const { + return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc); +} + +spv_result_t AssemblyGrammar::lookupOpcode(spv::Op opcode, + spv_opcode_desc* desc) const { + return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc); +} + +spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, + const char* name, size_t name_len, + spv_operand_desc* desc) const { + return spvOperandTableNameLookup(target_env_, operandTable_, type, name, + name_len, desc); +} + +spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, + uint32_t operand, + spv_operand_desc* desc) const { + return spvOperandTableValueLookup(target_env_, operandTable_, type, operand, + desc); +} + +spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name, + spv::Op* opcode) const { + const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; + const auto* found = + std::find_if(kOpSpecConstantOpcodes, last, + [name](const SpecConstantOpcodeEntry& entry) { + return 0 == strcmp(name, entry.name); + }); + if (found == last) return SPV_ERROR_INVALID_LOOKUP; + *opcode = found->opcode; + return SPV_SUCCESS; +} + +spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(spv::Op opcode) const { + const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; + const auto* found = + std::find_if(kOpSpecConstantOpcodes, last, + [opcode](const SpecConstantOpcodeEntry& entry) { + return opcode == entry.opcode; + }); + if (found == last) return SPV_ERROR_INVALID_LOOKUP; + return SPV_SUCCESS; +} + +spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type, + const char* textValue, + uint32_t* pValue) const { + return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue, + pValue); +} +spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, + const char* textValue, + spv_ext_inst_desc* extInst) const { + return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst); +} + +spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, + uint32_t firstWord, + spv_ext_inst_desc* extInst) const { + return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst); +} + +void AssemblyGrammar::pushOperandTypesForMask( + const spv_operand_type_t type, const uint32_t mask, + spv_operand_pattern_t* pattern) const { + spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern); +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.h b/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.h new file mode 100644 index 00000000..36fdd08a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/assembly_grammar.h @@ -0,0 +1,139 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_ASSEMBLY_GRAMMAR_H_ +#define SOURCE_ASSEMBLY_GRAMMAR_H_ + +#include "source/enum_set.h" +#include "source/latest_version_spirv_header.h" +#include "source/operand.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Encapsulates the grammar to use for SPIR-V assembly. +// Contains methods to query for valid instructions and operands. +class AssemblyGrammar { + public: + explicit AssemblyGrammar(const spv_const_context context) + : target_env_(context->target_env), + operandTable_(context->operand_table), + opcodeTable_(context->opcode_table), + extInstTable_(context->ext_inst_table) {} + + // Returns true if the internal tables have been initialized with valid data. + bool isValid() const; + + // Returns the SPIR-V target environment. + spv_target_env target_env() const { return target_env_; } + + // Removes capabilities not available in the current target environment and + // returns the rest. + CapabilitySet filterCapsAgainstTargetEnv(const spv::Capability* cap_array, + uint32_t count) const; + + // Fills in the desc parameter with the information about the opcode + // of the given name. Returns SPV_SUCCESS if the opcode was found, and + // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. + spv_result_t lookupOpcode(const char* name, spv_opcode_desc* desc) const; + + // Fills in the desc parameter with the information about the opcode + // of the valid. Returns SPV_SUCCESS if the opcode was found, and + // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. + spv_result_t lookupOpcode(spv::Op opcode, spv_opcode_desc* desc) const; + + // Fills in the desc parameter with the information about the given + // operand. Returns SPV_SUCCESS if the operand was found, and + // SPV_ERROR_INVALID_LOOKUP otherwise. + spv_result_t lookupOperand(spv_operand_type_t type, const char* name, + size_t name_len, spv_operand_desc* desc) const; + + // Fills in the desc parameter with the information about the given + // operand. Returns SPV_SUCCESS if the operand was found, and + // SPV_ERROR_INVALID_LOOKUP otherwise. + spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand, + spv_operand_desc* desc) const; + + // Finds operand entry in the grammar table and returns its name. + // Returns "Unknown" if not found. + const char* lookupOperandName(spv_operand_type_t type, + uint32_t operand) const { + spv_operand_desc desc = nullptr; + if (lookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) { + return "Unknown"; + } + return desc->name; + } + + // Finds the opcode for the given OpSpecConstantOp opcode name. The name + // should not have the "Op" prefix. For example, "IAdd" corresponds to + // the integer add opcode for OpSpecConstantOp. On success, returns + // SPV_SUCCESS and sends the discovered operation code through the opcode + // parameter. On failure, returns SPV_ERROR_INVALID_LOOKUP. + spv_result_t lookupSpecConstantOpcode(const char* name, + spv::Op* opcode) const; + + // Returns SPV_SUCCESS if the given opcode is valid as the opcode operand + // to OpSpecConstantOp. + spv_result_t lookupSpecConstantOpcode(spv::Op opcode) const; + + // Parses a mask expression string for the given operand type. + // + // A mask expression is a sequence of one or more terms separated by '|', + // where each term is a named enum value for a given type. No whitespace + // is permitted. + // + // On success, the value is written to pValue, and SPV_SUCCESS is returned. + // The operand type is defined by the type parameter, and the text to be + // parsed is defined by the textValue parameter. + spv_result_t parseMaskOperand(const spv_operand_type_t type, + const char* textValue, uint32_t* pValue) const; + + // Writes the extended operand with the given type and text to the *extInst + // parameter. + // Returns SPV_SUCCESS if the value could be found. + spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char* textValue, + spv_ext_inst_desc* extInst) const; + + // Writes the extended operand with the given type and first encoded word + // to the *extInst parameter. + // Returns SPV_SUCCESS if the value could be found. + spv_result_t lookupExtInst(spv_ext_inst_type_t type, uint32_t firstWord, + spv_ext_inst_desc* extInst) const; + + // Inserts the operands expected after the given typed mask onto the end + // of the given pattern. + // + // Each set bit in the mask represents zero or more operand types that + // should be appended onto the pattern. Operands for a less significant + // bit must always match before operands for a more significant bit, so + // the operands for a less significant bit must appear closer to the end + // of the pattern stack. + // + // If a set bit is unknown, then we assume it has no operands. + void pushOperandTypesForMask(const spv_operand_type_t type, + const uint32_t mask, + spv_operand_pattern_t* pattern) const; + + private: + const spv_target_env target_env_; + const spv_operand_table operandTable_; + const spv_opcode_table opcodeTable_; + const spv_ext_inst_table extInstTable_; +}; + +} // namespace spvtools + +#endif // SOURCE_ASSEMBLY_GRAMMAR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/binary.cpp b/engine/engine/3rdlibs/spirv-tools/source/binary.cpp new file mode 100644 index 00000000..3807441d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/binary.cpp @@ -0,0 +1,884 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/binary.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/ext_inst.h" +#include "source/latest_version_spirv_header.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/util/string_utils.h" + +spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, + const spv_endianness_t endian, + spv_header_t* pHeader) { + if (!binary->code) return SPV_ERROR_INVALID_BINARY; + if (binary->wordCount < SPV_INDEX_INSTRUCTION) + return SPV_ERROR_INVALID_BINARY; + if (!pHeader) return SPV_ERROR_INVALID_POINTER; + + // TODO: Validation checking? + pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian); + pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian); + // Per 2.3.1 version's high and low bytes are 0 + if ((pHeader->version & 0x000000ff) || pHeader->version & 0xff000000) + return SPV_ERROR_INVALID_BINARY; + // Minimum version was 1.0 and max version is defined by SPV_VERSION. + if (pHeader->version < SPV_SPIRV_VERSION_WORD(1, 0) || + pHeader->version > SPV_VERSION) + return SPV_ERROR_INVALID_BINARY; + + pHeader->generator = + spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian); + pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian); + pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian); + pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION]; + + return SPV_SUCCESS; +} + +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + + return spvtools::utils::MakeString(inst.words + operand.offset, + operand.num_words); +} + +namespace { + +// A SPIR-V binary parser. A parser instance communicates detailed parse +// results via callbacks. +class Parser { + public: + // The user_data value is provided to the callbacks as context. + Parser(const spv_const_context context, void* user_data, + spv_parsed_header_fn_t parsed_header_fn, + spv_parsed_instruction_fn_t parsed_instruction_fn) + : grammar_(context), + consumer_(context->consumer), + user_data_(user_data), + parsed_header_fn_(parsed_header_fn), + parsed_instruction_fn_(parsed_instruction_fn) {} + + // Parses the specified binary SPIR-V module, issuing callbacks on a parsed + // header and for each parsed instruction. Returns SPV_SUCCESS on success. + // Otherwise returns an error code and issues a diagnostic. + spv_result_t parse(const uint32_t* words, size_t num_words, + spv_diagnostic* diagnostic); + + private: + // All remaining methods work on the current module parse state. + + // Like the parse method, but works on the current module parse state. + spv_result_t parseModule(); + + // Parses an instruction at the current position of the binary. Assumes + // the header has been parsed, the endian has been set, and the word index is + // still in range. Advances the parsing position past the instruction, and + // updates other parsing state for the current module. + // On success, returns SPV_SUCCESS and issues the parsed-instruction callback. + // On failure, returns an error code and issues a diagnostic. + spv_result_t parseInstruction(); + + // Parses an instruction operand with the given type, for an instruction + // starting at inst_offset words into the SPIR-V binary. + // If the SPIR-V binary is the same endianness as the host, then the + // endian_converted_inst_words parameter is ignored. Otherwise, this method + // appends the words for this operand, converted to host native endianness, + // to the end of endian_converted_inst_words. This method also updates the + // expected_operands parameter, and the scalar members of the inst parameter. + // On success, returns SPV_SUCCESS, advances past the operand, and pushes a + // new entry on to the operands vector. Otherwise returns an error code and + // issues a diagnostic. + spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst, + const spv_operand_type_t type, + std::vector* endian_converted_inst_words, + std::vector* operands, + spv_operand_pattern_t* expected_operands); + + // Records the numeric type for an operand according to the type information + // associated with the given non-zero type Id. This can fail if the type Id + // is not a type Id, or if the type Id does not reference a scalar numeric + // type. On success, return SPV_SUCCESS and populates the num_words, + // number_kind, and number_bit_width fields of parsed_operand. + spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand, + uint32_t type_id); + + // Records the number type for an instruction at the given offset, if that + // instruction generates a type. For types that aren't scalar numbers, + // record something with number kind SPV_NUMBER_NONE. + void recordNumberType(size_t inst_offset, + const spv_parsed_instruction_t* inst); + + // Returns a diagnostic stream object initialized with current position in + // the input stream, and for the given error code. Any data written to the + // returned object will be propagated to the current parse's diagnostic + // object. + spvtools::DiagnosticStream diagnostic(spv_result_t error) { + return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_, + "", error); + } + + // Returns a diagnostic stream object with the default parse error code. + spvtools::DiagnosticStream diagnostic() { + // The default failure for parsing is invalid binary. + return diagnostic(SPV_ERROR_INVALID_BINARY); + } + + // Issues a diagnostic describing an exhaustion of input condition when + // trying to decode an instruction operand, and returns + // SPV_ERROR_INVALID_BINARY. + spv_result_t exhaustedInputDiagnostic(size_t inst_offset, spv::Op opcode, + spv_operand_type_t type) { + return diagnostic() << "End of input reached while decoding Op" + << spvOpcodeString(opcode) << " starting at word " + << inst_offset + << ((_.word_index < _.num_words) ? ": truncated " + : ": missing ") + << spvOperandTypeStr(type) << " operand at word offset " + << _.word_index - inst_offset << "."; + } + + // Returns the endian-corrected word at the current position. + uint32_t peek() const { return peekAt(_.word_index); } + + // Returns the endian-corrected word at the given position. + uint32_t peekAt(size_t index) const { + assert(index < _.num_words); + return spvFixWord(_.words[index], _.endian); + } + + // Data members + + const spvtools::AssemblyGrammar grammar_; // SPIR-V syntax utility. + const spvtools::MessageConsumer& consumer_; // Message consumer callback. + void* const user_data_; // Context for the callbacks + const spv_parsed_header_fn_t parsed_header_fn_; // Parsed header callback + const spv_parsed_instruction_fn_t + parsed_instruction_fn_; // Parsed instruction callback + + // Describes the format of a typed literal number. + struct NumberType { + spv_number_kind_t type; + uint32_t bit_width; + }; + + // The state used to parse a single SPIR-V binary module. + struct State { + State(const uint32_t* words_arg, size_t num_words_arg, + spv_diagnostic* diagnostic_arg) + : words(words_arg), + num_words(num_words_arg), + diagnostic(diagnostic_arg), + word_index(0), + instruction_count(0), + endian(), + requires_endian_conversion(false) { + // Temporary storage for parser state within a single instruction. + // Most instructions require fewer than 25 words or operands. + operands.reserve(25); + endian_converted_words.reserve(25); + expected_operands.reserve(25); + } + State() : State(0, 0, nullptr) {} + const uint32_t* words; // Words in the binary SPIR-V module. + size_t num_words; // Number of words in the module. + spv_diagnostic* diagnostic; // Where diagnostics go. + size_t word_index; // The current position in words. + size_t instruction_count; // The count of processed instructions + spv_endianness_t endian; // The endianness of the binary. + // Is the SPIR-V binary in a different endianness from the host native + // endianness? + bool requires_endian_conversion; + + // Maps a result ID to its type ID. By convention: + // - a result ID that is a type definition maps to itself. + // - a result ID without a type maps to 0. (E.g. for OpLabel) + std::unordered_map id_to_type_id; + // Maps a type ID to its number type description. + std::unordered_map type_id_to_number_type_info; + // Maps an ExtInstImport id to the extended instruction type. + std::unordered_map + import_id_to_ext_inst_type; + + // Used by parseOperand + std::vector operands; + std::vector endian_converted_words; + spv_operand_pattern_t expected_operands; + } _; +}; + +spv_result_t Parser::parse(const uint32_t* words, size_t num_words, + spv_diagnostic* diagnostic_arg) { + _ = State(words, num_words, diagnostic_arg); + + const spv_result_t result = parseModule(); + + // Clear the module state. The tables might be big. + _ = State(); + + return result; +} + +spv_result_t Parser::parseModule() { + if (!_.words) return diagnostic() << "Missing module."; + + if (_.num_words < SPV_INDEX_INSTRUCTION) + return diagnostic() << "Module has incomplete header: only " << _.num_words + << " words instead of " << SPV_INDEX_INSTRUCTION; + + // Check the magic number and detect the module's endianness. + spv_const_binary_t binary{_.words, _.num_words}; + if (spvBinaryEndianness(&binary, &_.endian)) { + return diagnostic() << "Invalid SPIR-V magic number '" << std::hex + << _.words[0] << "'."; + } + _.requires_endian_conversion = !spvIsHostEndian(_.endian); + + // Process the header. + spv_header_t header; + if (spvBinaryHeaderGet(&binary, _.endian, &header)) { + // It turns out there is no way to trigger this error since the only + // failure cases are already handled above, with better messages. + return diagnostic(SPV_ERROR_INTERNAL) + << "Internal error: unhandled header parse failure"; + } + if (parsed_header_fn_) { + if (auto error = parsed_header_fn_(user_data_, _.endian, header.magic, + header.version, header.generator, + header.bound, header.schema)) { + return error; + } + } + + // Process the instructions. + _.word_index = SPV_INDEX_INSTRUCTION; + while (_.word_index < _.num_words) + if (auto error = parseInstruction()) return error; + + // Running off the end should already have been reported earlier. + assert(_.word_index == _.num_words); + + return SPV_SUCCESS; +} + +spv_result_t Parser::parseInstruction() { + _.instruction_count++; + + // The zero values for all members except for opcode are the + // correct initial values. + spv_parsed_instruction_t inst = {}; + + const uint32_t first_word = peek(); + + // If the module's endianness is different from the host native endianness, + // then converted_words contains the endian-translated words in the + // instruction. + _.endian_converted_words.clear(); + _.endian_converted_words.push_back(first_word); + + // After a successful parse of the instruction, the inst.operands member + // will point to this vector's storage. + _.operands.clear(); + + assert(_.word_index < _.num_words); + // Decompose and check the first word. + uint16_t inst_word_count = 0; + spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode); + if (inst_word_count < 1) { + return diagnostic() << "Invalid instruction word count: " + << inst_word_count; + } + spv_opcode_desc opcode_desc; + if (grammar_.lookupOpcode(static_cast(inst.opcode), &opcode_desc)) + return diagnostic() << "Invalid opcode: " << inst.opcode; + + // Advance past the opcode word. But remember the of the start + // of the instruction. + const size_t inst_offset = _.word_index; + _.word_index++; + + // Maintains the ordered list of expected operand types. + // For many instructions we only need the {numTypes, operandTypes} + // entries in opcode_desc. However, sometimes we need to modify + // the list as we parse the operands. This occurs when an operand + // has its own logical operands (such as the LocalSize operand for + // ExecutionMode), or for extended instructions that may have their + // own operands depending on the selected extended instruction. + _.expected_operands.clear(); + for (auto i = 0; i < opcode_desc->numTypes; i++) + _.expected_operands.push_back( + opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]); + + while (_.word_index < inst_offset + inst_word_count) { + const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset); + if (_.expected_operands.empty()) { + return diagnostic() << "Invalid instruction Op" << opcode_desc->name + << " starting at word " << inst_offset + << ": expected no more operands after " + << inst_word_index + << " words, but stated word count is " + << inst_word_count << "."; + } + + spv_operand_type_t type = + spvTakeFirstMatchableOperand(&_.expected_operands); + + if (auto error = + parseOperand(inst_offset, &inst, type, &_.endian_converted_words, + &_.operands, &_.expected_operands)) { + return error; + } + } + + if (!_.expected_operands.empty() && + !spvOperandIsOptional(_.expected_operands.back())) { + return diagnostic() << "End of input reached while decoding Op" + << opcode_desc->name << " starting at word " + << inst_offset << ": expected more operands after " + << inst_word_count << " words."; + } + + if ((inst_offset + inst_word_count) != _.word_index) { + return diagnostic() << "Invalid word count: Op" << opcode_desc->name + << " starting at word " << inst_offset + << " says it has " << inst_word_count + << " words, but found " << _.word_index - inst_offset + << " words instead."; + } + + // Check the computed length of the endian-converted words vector against + // the declared number of words in the instruction. If endian conversion + // is required, then they should match. If no endian conversion was + // performed, then the vector only contains the initial opcode/word-count + // word. + assert(!_.requires_endian_conversion || + (inst_word_count == _.endian_converted_words.size())); + assert(_.requires_endian_conversion || + (_.endian_converted_words.size() == 1)); + + recordNumberType(inst_offset, &inst); + + if (_.requires_endian_conversion) { + // We must wait until here to set this pointer, because the vector might + // have been be resized while we accumulated its elements. + inst.words = _.endian_converted_words.data(); + } else { + // If no conversion is required, then just point to the underlying binary. + // This saves time and space. + inst.words = _.words + inst_offset; + } + inst.num_words = inst_word_count; + + // We must wait until here to set this pointer, because the vector might + // have been be resized while we accumulated its elements. + inst.operands = _.operands.data(); + inst.num_operands = uint16_t(_.operands.size()); + + // Issue the callback. The callee should know that all the storage in inst + // is transient, and will disappear immediately afterward. + if (parsed_instruction_fn_) { + if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error; + } + + return SPV_SUCCESS; +} + +spv_result_t Parser::parseOperand(size_t inst_offset, + spv_parsed_instruction_t* inst, + const spv_operand_type_t type, + std::vector* words, + std::vector* operands, + spv_operand_pattern_t* expected_operands) { + const spv::Op opcode = static_cast(inst->opcode); + // We'll fill in this result as we go along. + spv_parsed_operand_t parsed_operand; + parsed_operand.offset = uint16_t(_.word_index - inst_offset); + // Most operands occupy one word. This might be be adjusted later. + parsed_operand.num_words = 1; + // The type argument is the one used by the grammar to parse the instruction. + // But it can exposes internal parser details such as whether an operand is + // optional or actually represents a variable-length sequence of operands. + // The resulting type should be adjusted to avoid those internal details. + // In most cases, the resulting operand type is the same as the grammar type. + parsed_operand.type = type; + + // Assume non-numeric values. This will be updated for literal numbers. + parsed_operand.number_kind = SPV_NUMBER_NONE; + parsed_operand.number_bit_width = 0; + + if (_.word_index >= _.num_words) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + + const uint32_t word = peek(); + + // Do the words in this operand have to be converted to native endianness? + // True for all but literal strings. + bool convert_operand_endianness = true; + + switch (type) { + case SPV_OPERAND_TYPE_TYPE_ID: + if (!word) + return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0"; + inst->type_id = word; + break; + + case SPV_OPERAND_TYPE_RESULT_ID: + if (!word) + return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Result Id is 0"; + inst->result_id = word; + // Save the result ID to type ID mapping. + // In the grammar, type ID always appears before result ID. + if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end()) + return diagnostic(SPV_ERROR_INVALID_ID) + << "Id " << inst->result_id << " is defined more than once"; + // Record it. + // A regular value maps to its type. Some instructions (e.g. OpLabel) + // have no type Id, and will map to 0. The result Id for a + // type-generating instruction (e.g. OpTypeInt) maps to itself. + _.id_to_type_id[inst->result_id] = + spvOpcodeGeneratesType(opcode) ? inst->result_id : inst->type_id; + break; + + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: + if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0"; + parsed_operand.type = SPV_OPERAND_TYPE_ID; + + if (spvIsExtendedInstruction(opcode) && parsed_operand.offset == 3) { + // The current word is the extended instruction set Id. + // Set the extended instruction set type for the current instruction. + auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word); + if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) { + return diagnostic(SPV_ERROR_INVALID_ID) + << "OpExtInst set Id " << word + << " does not reference an OpExtInstImport result Id"; + } + inst->ext_inst_type = ext_inst_type_iter->second; + } + break; + + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + // Check for trivially invalid values. The operand descriptions already + // have the word "ID" in them. + if (!word) return diagnostic() << spvOperandTypeStr(type) << " is 0"; + break; + + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + assert(spvIsExtendedInstruction(opcode)); + assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE); + spv_ext_inst_desc ext_inst; + if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + // if we know about this ext inst, push the expected operands + spvPushOperandTypes(ext_inst->operandTypes, expected_operands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) { + return diagnostic() + << "Invalid extended instruction number: " << word; + } else { + // for non-semantic instruction sets, we know the form of all such + // extended instructions contains a series of IDs as parameters + expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } + } break; + + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + assert(spv::Op::OpSpecConstantOp == opcode); + if (word > static_cast(spv::Op::Max) || + grammar_.lookupSpecConstantOpcode(spv::Op(word))) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(type) << ": " << word; + } + spv_opcode_desc opcode_entry = nullptr; + if (grammar_.lookupOpcode(spv::Op(word), &opcode_entry)) { + return diagnostic(SPV_ERROR_INTERNAL) + << "OpSpecConstant opcode table out of sync"; + } + // OpSpecConstant opcodes must have a type and result. We've already + // processed them, so skip them when preparing to parse the other + // operants for the opcode. + assert(opcode_entry->hasType); + assert(opcode_entry->hasResult); + assert(opcode_entry->numTypes >= 2); + spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands); + } break; + + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + // These are regular single-word literal integer operands. + // Post-parsing validation should check the range of the parsed value. + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER; + // It turns out they are always unsigned integers! + parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT; + parsed_operand.number_bit_width = 32; + break; + + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + // These are regular single-word literal float operands. + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_FLOAT; + parsed_operand.number_kind = SPV_NUMBER_FLOATING; + parsed_operand.number_bit_width = 32; + break; + + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER; + if (opcode == spv::Op::OpSwitch) { + // The literal operands have the same type as the value + // referenced by the selector Id. + const uint32_t selector_id = peekAt(inst_offset + 1); + const auto type_id_iter = _.id_to_type_id.find(selector_id); + if (type_id_iter == _.id_to_type_id.end() || + type_id_iter->second == 0) { + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " has no type"; + } + uint32_t type_id = type_id_iter->second; + + if (selector_id == type_id) { + // Recall that by convention, a result ID that is a type definition + // maps to itself. + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " is a type, not a value"; + } + if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id)) + return error; + if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT && + parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) { + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " is not a scalar integer"; + } + } else { + assert(opcode == spv::Op::OpConstant || + opcode == spv::Op::OpSpecConstant); + // The literal number type is determined by the type Id for the + // constant. + assert(inst->type_id); + if (auto error = + setNumericTypeInfoForType(&parsed_operand, inst->type_id)) + return error; + } + break; + + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { + const size_t max_words = _.num_words - _.word_index; + std::string string = + spvtools::utils::MakeString(_.words + _.word_index, max_words, false); + + if (string.length() == max_words * 4) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + + // Make sure we can record the word count without overflow. + // + // This error can't currently be triggered because of validity + // checks elsewhere. + const size_t string_num_words = string.length() / 4 + 1; + if (string_num_words > std::numeric_limits::max()) { + return diagnostic() << "Literal string is longer than " + << std::numeric_limits::max() + << " words: " << string_num_words << " words long"; + } + parsed_operand.num_words = uint16_t(string_num_words); + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING; + + if (spv::Op::OpExtInstImport == opcode) { + // Record the extended instruction type for the ID for this import. + // There is only one string literal argument to OpExtInstImport, + // so it's sufficient to guard this just on the opcode. + const spv_ext_inst_type_t ext_inst_type = + spvExtInstImportTypeGet(string.c_str()); + if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { + return diagnostic() + << "Invalid extended instruction import '" << string << "'"; + } + // We must have parsed a valid result ID. It's a condition + // of the grammar, and we only accept non-zero result Ids. + assert(inst->result_id); + _.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type; + } + } break; + + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_FPENCODING: + case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING: + case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL: + case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL: + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: { + // A single word that is a plain enum value. + + // Map an optional operand type to its corresponding concrete type. + if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER) + parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER; + if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT) + parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT; + if (type == SPV_OPERAND_TYPE_OPTIONAL_FPENCODING) + parsed_operand.type = SPV_OPERAND_TYPE_FPENCODING; + + spv_operand_desc entry; + if (grammar_.lookupOperand(type, word, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word; + } + // Prepare to accept operands to this operand, if needed. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } break; + + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: { + spv_operand_desc entry; + if (grammar_.lookupOperand(type, word, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word + << ", if you are creating a new source language please use " + "value 0 " + "(Unknown) and when ready, add your source language to " + "SPIRV-Headers"; + } + // Prepare to accept operands to this operand, if needed. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } break; + + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: + case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: { + // This operand is a mask. + + // Map an optional operand type to its corresponding concrete type. + if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE) + parsed_operand.type = SPV_OPERAND_TYPE_IMAGE; + if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS) + parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS) + parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS) + parsed_operand.type = SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS; + if (type == SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS) + parsed_operand.type = + SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS; + + // Check validity of set mask bits. Also prepare for operands for those + // masks if they have any. To get operand order correct, scan from + // MSB to LSB since we can only prepend operands to a pattern. + // The only case in the grammar where you have more than one mask bit + // having an operand is for image operands. See SPIR-V 3.14 Image + // Operands. + uint32_t remaining_word = word; + for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) { + if (remaining_word & mask) { + spv_operand_desc entry; + if (grammar_.lookupOperand(type, mask, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word << " has invalid mask component " + << mask; + } + remaining_word ^= mask; + spvPushOperandTypes(entry->operandTypes, expected_operands); + } + } + if (word == 0) { + // An all-zeroes mask *might* also be valid. + spv_operand_desc entry; + if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) { + // Prepare for its operands, if any. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } + } + } break; + default: + return diagnostic() << "Internal error: Unhandled operand type: " << type; + } + + assert(spvOperandIsConcrete(parsed_operand.type)); + + operands->push_back(parsed_operand); + + const size_t index_after_operand = _.word_index + parsed_operand.num_words; + + // Avoid buffer overrun for the cases where the operand has more than one + // word, and where it isn't a string. (Those other cases have already been + // handled earlier.) For example, this error can occur for a multi-word + // argument to OpConstant, or a multi-word case literal operand for OpSwitch. + if (_.num_words < index_after_operand) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + + if (_.requires_endian_conversion) { + // Copy instruction words. Translate to native endianness as needed. + if (convert_operand_endianness) { + const spv_endianness_t endianness = _.endian; + std::transform(_.words + _.word_index, _.words + index_after_operand, + std::back_inserter(*words), + [endianness](const uint32_t raw_word) { + return spvFixWord(raw_word, endianness); + }); + } else { + words->insert(words->end(), _.words + _.word_index, + _.words + index_after_operand); + } + } + + // Advance past the operand. + _.word_index = index_after_operand; + + return SPV_SUCCESS; +} + +spv_result_t Parser::setNumericTypeInfoForType( + spv_parsed_operand_t* parsed_operand, uint32_t type_id) { + assert(type_id != 0); + auto type_info_iter = _.type_id_to_number_type_info.find(type_id); + if (type_info_iter == _.type_id_to_number_type_info.end()) { + return diagnostic() << "Type Id " << type_id << " is not a type"; + } + const NumberType& info = type_info_iter->second; + if (info.type == SPV_NUMBER_NONE) { + // This is a valid type, but for something other than a scalar number. + return diagnostic() << "Type Id " << type_id + << " is not a scalar numeric type"; + } + + parsed_operand->number_kind = info.type; + parsed_operand->number_bit_width = info.bit_width; + // Round up the word count. + parsed_operand->num_words = static_cast((info.bit_width + 31) / 32); + return SPV_SUCCESS; +} + +void Parser::recordNumberType(size_t inst_offset, + const spv_parsed_instruction_t* inst) { + const spv::Op opcode = static_cast(inst->opcode); + if (spvOpcodeGeneratesType(opcode)) { + NumberType info = {SPV_NUMBER_NONE, 0}; + if (spv::Op::OpTypeInt == opcode) { + const bool is_signed = peekAt(inst_offset + 3) != 0; + info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT; + info.bit_width = peekAt(inst_offset + 2); + } else if (spv::Op::OpTypeFloat == opcode) { + info.type = SPV_NUMBER_FLOATING; + info.bit_width = peekAt(inst_offset + 2); + } + // The *result* Id of a type generating instruction is the type Id. + _.type_id_to_number_type_info[inst->result_id] = info; + } +} + +} // anonymous namespace + +spv_result_t spvBinaryParse(const spv_const_context context, void* user_data, + const uint32_t* code, const size_t num_words, + spv_parsed_header_fn_t parsed_header, + spv_parsed_instruction_fn_t parsed_instruction, + spv_diagnostic* diagnostic) { + spv_context_t hijack_context = *context; + if (diagnostic) { + *diagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic); + } + Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction); + return parser.parse(code, num_words, diagnostic); +} + +// TODO(dneto): This probably belongs in text.cpp since that's the only place +// that a spv_binary_t value is created. +void spvBinaryDestroy(spv_binary binary) { + if (binary) { + if (binary->code) delete[] binary->code; + delete binary; + } +} + +size_t spv_strnlen_s(const char* str, size_t strsz) { + if (!str) return 0; + for (size_t i = 0; i < strsz; i++) { + if (!str[i]) return i; + } + return strsz; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/binary.h b/engine/engine/3rdlibs/spirv-tools/source/binary.h new file mode 100644 index 00000000..eb3beaca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/binary.h @@ -0,0 +1,43 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_BINARY_H_ +#define SOURCE_BINARY_H_ + +#include + +#include "source/spirv_definition.h" +#include "spirv-tools/libspirv.h" + +// Functions + +// Grabs the header from the SPIR-V module given in the binary parameter. The +// endian parameter specifies the endianness of the binary module. On success, +// returns SPV_SUCCESS and writes the parsed header into *header. +spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, + const spv_endianness_t endian, + spv_header_t* header); + +// Returns the number of non-null characters in str before the first null +// character, or strsz if there is no null character. Examines at most the +// first strsz characters in str. Returns 0 if str is nullptr. This is a +// replacement for C11's strnlen_s which might not exist in all environments. +size_t spv_strnlen_s(const char* str, size_t strsz); + +// Decode the string literal operand with index operand_index from instruction +// inst. +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + +#endif // SOURCE_BINARY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/cfa.h b/engine/engine/3rdlibs/spirv-tools/source/cfa.h new file mode 100644 index 00000000..dc8b9ba0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/cfa.h @@ -0,0 +1,398 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_CFA_H_ +#define SOURCE_CFA_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spvtools { + +// Control Flow Analysis of control flow graphs of basic block nodes |BB|. +template +class CFA { + using bb_ptr = BB*; + using cbb_ptr = const BB*; + using bb_iter = typename std::vector::const_iterator; + using get_blocks_func = std::function*(const BB*)>; + + struct block_info { + cbb_ptr block; ///< pointer to the block + bb_iter iter; ///< Iterator to the current child node being processed + }; + + /// Returns true if a block with @p id is found in the @p work_list vector + /// + /// @param[in] work_list Set of blocks visited in the depth first + /// traversal + /// of the CFG + /// @param[in] id The ID of the block being checked + /// + /// @return true if the edge work_list.back().block->id() => id is a back-edge + static bool FindInWorkList(const std::vector& work_list, + uint32_t id); + + public: + /// @brief Depth first traversal starting from the \p entry BasicBlock + /// + /// This function performs a depth first traversal from the \p entry + /// BasicBlock and calls the pre/postorder functions when it needs to process + /// the node in pre order, post order. + /// + /// @param[in] entry The root BasicBlock of a CFG + /// @param[in] successor_func A function which will return a pointer to the + /// successor nodes + /// @param[in] preorder A function that will be called for every block in a + /// CFG following preorder traversal semantics + /// @param[in] postorder A function that will be called for every block in a + /// CFG following postorder traversal semantics + /// @param[in] terminal A function that will be called to determine if the + /// search should stop at the given node. + /// NOTE: The @p successor_func and predecessor_func each return a pointer to + /// a collection such that iterators to that collection remain valid for the + /// lifetime of the algorithm. + static void DepthFirstTraversal(const BB* entry, + get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function terminal); + + /// @brief Depth first traversal starting from the \p entry BasicBlock + /// + /// This function performs a depth first traversal from the \p entry + /// BasicBlock and calls the pre/postorder functions when it needs to process + /// the node in pre order, post order. It also calls the backedge function + /// when a back edge is encountered. The backedge function can be empty. The + /// runtime of the algorithm is improved if backedge is empty. + /// + /// @param[in] entry The root BasicBlock of a CFG + /// @param[in] successor_func A function which will return a pointer to the + /// successor nodes + /// @param[in] preorder A function that will be called for every block in a + /// CFG following preorder traversal semantics + /// @param[in] postorder A function that will be called for every block in a + /// CFG following postorder traversal semantics + /// @param[in] backedge A function that will be called when a backedge is + /// encountered during a traversal. + /// @param[in] terminal A function that will be called to determine if the + /// search should stop at the given node. + /// NOTE: The @p successor_func and predecessor_func each return a pointer to + /// a collection such that iterators to that collection remain valid for the + /// lifetime of the algorithm. + static void DepthFirstTraversal( + const BB* entry, get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function backedge, + std::function terminal); + + /// @brief Calculates dominator edges for a set of blocks + /// + /// Computes dominators using the algorithm of Cooper, Harvey, and Kennedy + /// "A Simple, Fast Dominance Algorithm", 2001. + /// + /// The algorithm assumes there is a unique root node (a node without + /// predecessors), and it is therefore at the end of the postorder vector. + /// + /// This function calculates the dominator edges for a set of blocks in the + /// CFG. + /// Uses the dominator algorithm by Cooper et al. + /// + /// @param[in] postorder A vector of blocks in post order traversal + /// order + /// in a CFG + /// @param[in] predecessor_func Function used to get the predecessor nodes of + /// a + /// block + /// + /// @return the dominator tree of the graph, as a vector of pairs of nodes. + /// The first node in the pair is a node in the graph. The second node in the + /// pair is its immediate dominator in the sense of Cooper et.al., where a + /// block + /// without predecessors (such as the root node) is its own immediate + /// dominator. + static std::vector> CalculateDominators( + const std::vector& postorder, get_blocks_func predecessor_func); + + // Computes a minimal set of root nodes required to traverse, in the forward + // direction, the CFG represented by the given vector of blocks, and successor + // and predecessor functions. When considering adding two nodes, each having + // predecessors, favour using the one that appears earlier on the input blocks + // list. + static std::vector TraversalRoots(const std::vector& blocks, + get_blocks_func succ_func, + get_blocks_func pred_func); + + static void ComputeAugmentedCFG( + std::vector& ordered_blocks, BB* pseudo_entry_block, + BB* pseudo_exit_block, + std::unordered_map>* augmented_successors_map, + std::unordered_map>* + augmented_predecessors_map, + get_blocks_func succ_func, get_blocks_func pred_func); +}; + +template +bool CFA::FindInWorkList(const std::vector& work_list, + uint32_t id) { + for (const auto& b : work_list) { + if (b.block->id() == id) return true; + } + return false; +} + +template +void CFA::DepthFirstTraversal(const BB* entry, + get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function terminal) { + DepthFirstTraversal(entry, successor_func, preorder, postorder, + /* backedge = */ {}, terminal); +} + +template +void CFA::DepthFirstTraversal( + const BB* entry, get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function backedge, + std::function terminal) { + assert(successor_func && "The successor function cannot be empty."); + assert(preorder && "The preorder function cannot be empty."); + assert(postorder && "The postorder function cannot be empty."); + assert(terminal && "The terminal function cannot be empty."); + + std::unordered_set processed; + + /// NOTE: work_list is the sequence of nodes from the root node to the node + /// being processed in the traversal + std::vector work_list; + work_list.reserve(10); + + work_list.push_back({entry, std::begin(*successor_func(entry))}); + preorder(entry); + processed.insert(entry->id()); + + while (!work_list.empty()) { + block_info& top = work_list.back(); + if (terminal(top.block) || top.iter == end(*successor_func(top.block))) { + postorder(top.block); + work_list.pop_back(); + } else { + BB* child = *top.iter; + top.iter++; + if (backedge && FindInWorkList(work_list, child->id())) { + backedge(top.block, child); + } + if (processed.count(child->id()) == 0) { + preorder(child); + work_list.emplace_back( + block_info{child, std::begin(*successor_func(child))}); + processed.insert(child->id()); + } + } + } +} + +template +std::vector> CFA::CalculateDominators( + const std::vector& postorder, get_blocks_func predecessor_func) { + struct block_detail { + size_t dominator; ///< The index of blocks's dominator in post order array + size_t postorder_index; ///< The index of the block in the post order array + }; + const size_t undefined_dom = postorder.size(); + + std::unordered_map idoms; + for (size_t i = 0; i < postorder.size(); i++) { + idoms[postorder[i]] = {undefined_dom, i}; + } + idoms[postorder.back()].dominator = idoms[postorder.back()].postorder_index; + + bool changed = true; + while (changed) { + changed = false; + for (auto b = postorder.rbegin() + 1; b != postorder.rend(); ++b) { + const std::vector& predecessors = *predecessor_func(*b); + // Find the first processed/reachable predecessor that is reachable + // in the forward traversal. + auto res = std::find_if(std::begin(predecessors), std::end(predecessors), + [&idoms, undefined_dom](BB* pred) { + return idoms.count(pred) && + idoms[pred].dominator != undefined_dom; + }); + if (res == end(predecessors)) continue; + const BB* idom = *res; + size_t idom_idx = idoms[idom].postorder_index; + + // all other predecessors + for (const auto* p : predecessors) { + if (idom == p) continue; + // Only consider nodes reachable in the forward traversal. + // Otherwise the intersection doesn't make sense and will never + // terminate. + if (!idoms.count(p)) continue; + if (idoms[p].dominator != undefined_dom) { + size_t finger1 = idoms[p].postorder_index; + size_t finger2 = idom_idx; + while (finger1 != finger2) { + while (finger1 < finger2) { + finger1 = idoms[postorder[finger1]].dominator; + } + while (finger2 < finger1) { + finger2 = idoms[postorder[finger2]].dominator; + } + } + idom_idx = finger1; + } + } + if (idoms[*b].dominator != idom_idx) { + idoms[*b].dominator = idom_idx; + changed = true; + } + } + } + + std::vector> out; + for (auto idom : idoms) { + // At this point if there is no dominator for the node, just make it + // reflexive. + auto dominator = std::get<1>(idom).dominator; + if (dominator == undefined_dom) { + dominator = std::get<1>(idom).postorder_index; + } + // NOTE: performing a const cast for convenient usage with + // UpdateImmediateDominators + out.push_back({const_cast(std::get<0>(idom)), + const_cast(postorder[dominator])}); + } + + // Sort by postorder index to generate a deterministic ordering of edges. + std::sort( + out.begin(), out.end(), + [&idoms](const std::pair& lhs, + const std::pair& rhs) { + assert(lhs.first); + assert(lhs.second); + assert(rhs.first); + assert(rhs.second); + auto lhs_indices = std::make_pair(idoms[lhs.first].postorder_index, + idoms[lhs.second].postorder_index); + auto rhs_indices = std::make_pair(idoms[rhs.first].postorder_index, + idoms[rhs.second].postorder_index); + return lhs_indices < rhs_indices; + }); + return out; +} + +template +std::vector CFA::TraversalRoots(const std::vector& blocks, + get_blocks_func succ_func, + get_blocks_func pred_func) { + // The set of nodes which have been visited from any of the roots so far. + std::unordered_set visited; + + auto mark_visited = [&visited](const BB* b) { visited.insert(b); }; + auto ignore_block = [](const BB*) {}; + auto no_terminal_blocks = [](const BB*) { return false; }; + + auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block, + &no_terminal_blocks](const BB* entry) { + DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block, + no_terminal_blocks); + }; + + std::vector result; + + // First collect nodes without predecessors. + for (auto block : blocks) { + if (pred_func(block)->empty()) { + assert(visited.count(block) == 0 && "Malformed graph!"); + result.push_back(block); + traverse_from_root(block); + } + } + + // Now collect other stranded nodes. These must be in unreachable cycles. + for (auto block : blocks) { + if (visited.count(block) == 0) { + result.push_back(block); + traverse_from_root(block); + } + } + + return result; +} + +template +void CFA::ComputeAugmentedCFG( + std::vector& ordered_blocks, BB* pseudo_entry_block, + BB* pseudo_exit_block, + std::unordered_map>* augmented_successors_map, + std::unordered_map>* augmented_predecessors_map, + get_blocks_func succ_func, get_blocks_func pred_func) { + // Compute the successors of the pseudo-entry block, and + // the predecessors of the pseudo exit block. + auto sources = TraversalRoots(ordered_blocks, succ_func, pred_func); + + // For the predecessor traversals, reverse the order of blocks. This + // will affect the post-dominance calculation as follows: + // - Suppose you have blocks A and B, with A appearing before B in + // the list of blocks. + // - Also, A branches only to B, and B branches only to A. + // - We want to compute A as dominating B, and B as post-dominating B. + // By using reversed blocks for predecessor traversal roots discovery, + // we'll add an edge from B to the pseudo-exit node, rather than from A. + // All this is needed to correctly process the dominance/post-dominance + // constraint when A is a loop header that points to itself as its + // own continue target, and B is the latch block for the loop. + std::vector reversed_blocks(ordered_blocks.rbegin(), + ordered_blocks.rend()); + auto sinks = TraversalRoots(reversed_blocks, pred_func, succ_func); + + // Wire up the pseudo entry block. + (*augmented_successors_map)[pseudo_entry_block] = sources; + for (auto block : sources) { + auto& augmented_preds = (*augmented_predecessors_map)[block]; + const auto preds = pred_func(block); + augmented_preds.reserve(1 + preds->size()); + augmented_preds.push_back(pseudo_entry_block); + augmented_preds.insert(augmented_preds.end(), preds->begin(), preds->end()); + } + + // Wire up the pseudo exit block. + (*augmented_predecessors_map)[pseudo_exit_block] = sinks; + for (auto block : sinks) { + auto& augmented_succ = (*augmented_successors_map)[block]; + const auto succ = succ_func(block); + augmented_succ.reserve(1 + succ->size()); + augmented_succ.push_back(pseudo_exit_block); + augmented_succ.insert(augmented_succ.end(), succ->begin(), succ->end()); + } +} + +} // namespace spvtools + +#endif // SOURCE_CFA_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/common_debug_info.h b/engine/engine/3rdlibs/spirv-tools/source/common_debug_info.h new file mode 100644 index 00000000..ffa5d340 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/common_debug_info.h @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_COMMON_DEBUG_INFO_HEADER_H_ +#define SOURCE_COMMON_DEBUG_INFO_HEADER_H_ + +// This enum defines the known common set of instructions that are the same +// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100. +// Note that NonSemantic.Shader.* instructions can still have slightly +// different encoding, as it does not use literals anywhere and only constants. +enum CommonDebugInfoInstructions { + CommonDebugInfoDebugInfoNone = 0, + CommonDebugInfoDebugCompilationUnit = 1, + CommonDebugInfoDebugTypeBasic = 2, + CommonDebugInfoDebugTypePointer = 3, + CommonDebugInfoDebugTypeQualifier = 4, + CommonDebugInfoDebugTypeArray = 5, + CommonDebugInfoDebugTypeVector = 6, + CommonDebugInfoDebugTypedef = 7, + CommonDebugInfoDebugTypeFunction = 8, + CommonDebugInfoDebugTypeEnum = 9, + CommonDebugInfoDebugTypeComposite = 10, + CommonDebugInfoDebugTypeMember = 11, + CommonDebugInfoDebugTypeInheritance = 12, + CommonDebugInfoDebugTypePtrToMember = 13, + CommonDebugInfoDebugTypeTemplate = 14, + CommonDebugInfoDebugTypeTemplateParameter = 15, + CommonDebugInfoDebugTypeTemplateTemplateParameter = 16, + CommonDebugInfoDebugTypeTemplateParameterPack = 17, + CommonDebugInfoDebugGlobalVariable = 18, + CommonDebugInfoDebugFunctionDeclaration = 19, + CommonDebugInfoDebugFunction = 20, + CommonDebugInfoDebugLexicalBlock = 21, + CommonDebugInfoDebugLexicalBlockDiscriminator = 22, + CommonDebugInfoDebugScope = 23, + CommonDebugInfoDebugNoScope = 24, + CommonDebugInfoDebugInlinedAt = 25, + CommonDebugInfoDebugLocalVariable = 26, + CommonDebugInfoDebugInlinedVariable = 27, + CommonDebugInfoDebugDeclare = 28, + CommonDebugInfoDebugValue = 29, + CommonDebugInfoDebugOperation = 30, + CommonDebugInfoDebugExpression = 31, + CommonDebugInfoDebugMacroDef = 32, + CommonDebugInfoDebugMacroUndef = 33, + CommonDebugInfoDebugImportedEntity = 34, + CommonDebugInfoDebugSource = 35, + CommonDebugInfoInstructionsMax = 0x7ffffff +}; + +#endif // SOURCE_COMMON_DEBUG_INFO_HEADER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/diagnostic.cpp b/engine/engine/3rdlibs/spirv-tools/source/diagnostic.cpp new file mode 100644 index 00000000..f3aa2594 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diagnostic.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/diagnostic.h" + +#include +#include +#include +#include +#include + +#include "source/table.h" + +// Diagnostic API + +spv_diagnostic spvDiagnosticCreate(const spv_position position, + const char* message) { + spv_diagnostic diagnostic = new spv_diagnostic_t; + if (!diagnostic) return nullptr; + size_t length = strlen(message) + 1; + diagnostic->error = new char[length]; + if (!diagnostic->error) { + delete diagnostic; + return nullptr; + } + diagnostic->position = *position; + diagnostic->isTextSource = false; + memset(diagnostic->error, 0, length); + strcpy(diagnostic->error, message); + return diagnostic; +} + +void spvDiagnosticDestroy(spv_diagnostic diagnostic) { + if (!diagnostic) return; + delete[] diagnostic->error; + delete diagnostic; +} + +spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic) { + if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; + + if (diagnostic->isTextSource) { + // NOTE: This is a text position + // NOTE: add 1 to the line as editors start at line 1, we are counting new + // line characters to start at line 0 + std::cerr << "error: " << diagnostic->position.line + 1 << ": " + << diagnostic->position.column + 1 << ": " << diagnostic->error + << "\n"; + return SPV_SUCCESS; + } + + // NOTE: Assume this is a binary position + std::cerr << "error: "; + if (diagnostic->position.index > 0) + std::cerr << diagnostic->position.index << ": "; + std::cerr << diagnostic->error << "\n"; + return SPV_SUCCESS; +} + +namespace spvtools { + +DiagnosticStream::DiagnosticStream(DiagnosticStream&& other) + : stream_(), + position_(other.position_), + consumer_(other.consumer_), + disassembled_instruction_(std::move(other.disassembled_instruction_)), + error_(other.error_) { + // Prevent the other object from emitting output during destruction. + other.error_ = SPV_FAILED_MATCH; + // Some platforms are missing support for std::ostringstream functionality, + // including: move constructor, swap method. Either would have been a + // better choice than copying the string. + stream_ << other.stream_.str(); +} + +DiagnosticStream::~DiagnosticStream() { + if (error_ != SPV_FAILED_MATCH && consumer_ != nullptr) { + auto level = SPV_MSG_ERROR; + switch (error_) { + case SPV_SUCCESS: + case SPV_REQUESTED_TERMINATION: // Essentially success. + level = SPV_MSG_INFO; + break; + case SPV_WARNING: + level = SPV_MSG_WARNING; + break; + case SPV_UNSUPPORTED: + case SPV_ERROR_INTERNAL: + case SPV_ERROR_INVALID_TABLE: + level = SPV_MSG_INTERNAL_ERROR; + break; + case SPV_ERROR_OUT_OF_MEMORY: + level = SPV_MSG_FATAL; + break; + default: + break; + } + if (disassembled_instruction_.size() > 0) + stream_ << std::endl << " " << disassembled_instruction_ << std::endl; + + consumer_(level, "input", position_, stream_.str().c_str()); + } +} + +void UseDiagnosticAsMessageConsumer(spv_context context, + spv_diagnostic* diagnostic) { + assert(diagnostic && *diagnostic == nullptr); + + auto create_diagnostic = [diagnostic](spv_message_level_t, const char*, + const spv_position_t& position, + const char* message) { + auto p = position; + spvDiagnosticDestroy(*diagnostic); // Avoid memory leak. + *diagnostic = spvDiagnosticCreate(&p, message); + }; + SetContextMessageConsumer(context, std::move(create_diagnostic)); +} + +std::string spvResultToString(spv_result_t res) { + std::string out; + switch (res) { + case SPV_SUCCESS: + out = "SPV_SUCCESS"; + break; + case SPV_UNSUPPORTED: + out = "SPV_UNSUPPORTED"; + break; + case SPV_END_OF_STREAM: + out = "SPV_END_OF_STREAM"; + break; + case SPV_WARNING: + out = "SPV_WARNING"; + break; + case SPV_FAILED_MATCH: + out = "SPV_FAILED_MATCH"; + break; + case SPV_REQUESTED_TERMINATION: + out = "SPV_REQUESTED_TERMINATION"; + break; + case SPV_ERROR_INTERNAL: + out = "SPV_ERROR_INTERNAL"; + break; + case SPV_ERROR_OUT_OF_MEMORY: + out = "SPV_ERROR_OUT_OF_MEMORY"; + break; + case SPV_ERROR_INVALID_POINTER: + out = "SPV_ERROR_INVALID_POINTER"; + break; + case SPV_ERROR_INVALID_BINARY: + out = "SPV_ERROR_INVALID_BINARY"; + break; + case SPV_ERROR_INVALID_TEXT: + out = "SPV_ERROR_INVALID_TEXT"; + break; + case SPV_ERROR_INVALID_TABLE: + out = "SPV_ERROR_INVALID_TABLE"; + break; + case SPV_ERROR_INVALID_VALUE: + out = "SPV_ERROR_INVALID_VALUE"; + break; + case SPV_ERROR_INVALID_DIAGNOSTIC: + out = "SPV_ERROR_INVALID_DIAGNOSTIC"; + break; + case SPV_ERROR_INVALID_LOOKUP: + out = "SPV_ERROR_INVALID_LOOKUP"; + break; + case SPV_ERROR_INVALID_ID: + out = "SPV_ERROR_INVALID_ID"; + break; + case SPV_ERROR_INVALID_CFG: + out = "SPV_ERROR_INVALID_CFG"; + break; + case SPV_ERROR_INVALID_LAYOUT: + out = "SPV_ERROR_INVALID_LAYOUT"; + break; + default: + out = "Unknown Error"; + } + return out; +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/diagnostic.h b/engine/engine/3rdlibs/spirv-tools/source/diagnostic.h new file mode 100644 index 00000000..22df9614 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diagnostic.h @@ -0,0 +1,79 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DIAGNOSTIC_H_ +#define SOURCE_DIAGNOSTIC_H_ + +#include +#include + +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +// A DiagnosticStream remembers the current position of the input and an error +// code, and captures diagnostic messages via the left-shift operator. +// If the error code is not SPV_FAILED_MATCH, then captured messages are +// emitted during the destructor. +class DiagnosticStream { + public: + DiagnosticStream(spv_position_t position, const MessageConsumer& consumer, + const std::string& disassembled_instruction, + spv_result_t error) + : position_(position), + consumer_(consumer), + disassembled_instruction_(disassembled_instruction), + error_(error) {} + + // Creates a DiagnosticStream from an expiring DiagnosticStream. + // The new object takes the contents of the other, and prevents the + // other from emitting anything during destruction. + DiagnosticStream(DiagnosticStream&& other); + + // Destroys a DiagnosticStream. + // If its status code is something other than SPV_FAILED_MATCH + // then emit the accumulated message to the consumer. + ~DiagnosticStream(); + + // Adds the given value to the diagnostic message to be written. + template + DiagnosticStream& operator<<(const T& val) { + stream_ << val; + return *this; + } + + // Conversion operator to spv_result, returning the error code. + operator spv_result_t() { return error_; } + + private: + std::ostringstream stream_; + spv_position_t position_; + MessageConsumer consumer_; // Message consumer callback. + std::string disassembled_instruction_; + spv_result_t error_; +}; + +// Changes the MessageConsumer in |context| to one that updates |diagnostic| +// with the last message received. +// +// This function expects that |diagnostic| is not nullptr and its content is a +// nullptr. +void UseDiagnosticAsMessageConsumer(spv_context context, + spv_diagnostic* diagnostic); + +std::string spvResultToString(spv_result_t res); + +} // namespace spvtools + +#endif // SOURCE_DIAGNOSTIC_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/diff/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/diff/CMakeLists.txt new file mode 100644 index 00000000..52f18f2d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diff/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (c) 2022 Google LLC. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_DIFF_SOURCES + diff.h + lcs.h + + diff.cpp +) + +add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-diff) +target_include_directories(SPIRV-Tools-diff + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-diff + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-diff + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-diff PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-diff) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets) + export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR) + install(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-diff) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-diffConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/engine/engine/3rdlibs/spirv-tools/source/diff/diff.cpp b/engine/engine/3rdlibs/spirv-tools/source/diff/diff.cpp new file mode 100644 index 00000000..2e455e5a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diff/diff.cpp @@ -0,0 +1,3021 @@ +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/diff/diff.h" + +#include "source/diff/lcs.h" +#include "source/disassemble.h" +#include "source/ext_inst.h" +#include "source/latest_version_spirv_header.h" +#include "source/print.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace diff { + +namespace { + +// A map from an id to the instruction that defines it. +using IdToInstructionMap = std::vector; +// A map from an id to the instructions that decorate it, or name it, etc. +using IdToInfoMap = std::vector>; +// A map from an instruction to another, used for instructions without id. +using InstructionToInstructionMap = + std::unordered_map; +// A flat list of instructions in a function for easier iteration. +using InstructionList = std::vector; +// A map from a function to its list of instructions. +using FunctionInstMap = std::map; +// A list of ids with some similar property, for example functions with the same +// name. +using IdGroup = std::vector; +// A map of names to ids with the same name. This is an ordered map so +// different implementations produce identical results. +using IdGroupMapByName = std::map; +using IdGroupMapByTypeId = std::map; +using IdGroupMapByOp = std::map; +using IdGroupMapByStorageClass = std::map; + +// A set of potential id mappings that haven't been resolved yet. Any id in src +// may map in any id in dst. Note that ids are added in the same order as they +// appear in src and dst to facilitate matching dependent instructions. For +// example, this guarantees that when matching OpTypeVector, the basic type of +// the vector is already (potentially) matched. +struct PotentialIdMap { + std::vector src_ids; + std::vector dst_ids; +}; + +void CompactIds(std::vector& ids) { + size_t write_index = 0; + for (size_t i = 0; i < ids.size(); ++i) { + if (ids[i] != 0) { + ids[write_index++] = ids[i]; + } + } + ids.resize(write_index); +} + +// A mapping from ids in one module to ids in the other. +// +// Differ contains two of these, for src->dst and dst->src. +class IdMap { + public: + IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); } + + void MapIds(uint32_t from, uint32_t to) { + assert(from != 0); + assert(to != 0); + assert(from < id_map_.size()); + assert(id_map_[from] == 0); + + id_map_[from] = to; + } + + uint32_t MappedId(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() ? id_map_[from] : 0; + } + const opt::Instruction* MappedInst(const opt::Instruction* from_inst) const { + assert(from_inst != nullptr); + assert(!from_inst->HasResultId()); + + auto mapped = inst_map_.find(from_inst); + if (mapped == inst_map_.end()) { + return nullptr; + } + return mapped->second; + } + + bool IsMapped(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() && id_map_[from] != 0; + } + + bool IsMapped(const opt::Instruction* from_inst) const { + assert(from_inst != nullptr); + assert(!from_inst->HasResultId()); + + return inst_map_.find(from_inst) != inst_map_.end(); + } + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* from_inst, + const opt::Instruction* to_inst) { + assert(from_inst != nullptr); + assert(to_inst != nullptr); + assert(inst_map_.find(from_inst) == inst_map_.end()); + + inst_map_[from_inst] = to_inst; + } + + uint32_t IdBound() const { return static_cast(id_map_.size()); } + + // Generate a fresh id in this mapping's domain. + uint32_t MakeFreshId() { + id_map_.push_back(0); + return static_cast(id_map_.size()) - 1; + } + + private: + // Given an id, returns the corresponding id in the other module, or 0 if not + // matched yet. + std::vector id_map_; + + // Same for instructions that don't have an id. + InstructionToInstructionMap inst_map_; +}; + +// Two way mapping of ids. +class SrcDstIdMap { + public: + SrcDstIdMap(size_t src_id_bound, size_t dst_id_bound) + : src_to_dst_(src_id_bound), dst_to_src_(dst_id_bound) {} + + void MapIds(uint32_t src, uint32_t dst) { + src_to_dst_.MapIds(src, dst); + dst_to_src_.MapIds(dst, src); + } + + uint32_t MappedDstId(uint32_t src) { + uint32_t dst = src_to_dst_.MappedId(src); + assert(dst == 0 || dst_to_src_.MappedId(dst) == src); + return dst; + } + uint32_t MappedSrcId(uint32_t dst) { + uint32_t src = dst_to_src_.MappedId(dst); + assert(src == 0 || src_to_dst_.MappedId(src) == dst); + return src; + } + + bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); } + bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); } + bool IsDstMapped(const opt::Instruction* dst_inst) { + return dst_to_src_.IsMapped(dst_inst); + } + + // Map any ids in src and dst that have not been mapped to new ids in dst and + // src respectively. Use src_insn_defined and dst_insn_defined to ignore ids + // that are simply never defined. (Since we assume the inputs are valid + // SPIR-V, this implies they are also never used.) + void MapUnmatchedIds(std::function src_insn_defined, + std::function dst_insn_defined); + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->HasResultId() == dst_inst->HasResultId()); + if (src_inst->HasResultId()) { + MapIds(src_inst->result_id(), dst_inst->result_id()); + } else { + src_to_dst_.MapInsts(src_inst, dst_inst); + dst_to_src_.MapInsts(dst_inst, src_inst); + } + } + + const IdMap& SrcToDstMap() const { return src_to_dst_; } + const IdMap& DstToSrcMap() const { return dst_to_src_; } + + private: + IdMap src_to_dst_; + IdMap dst_to_src_; +}; + +// Mappings from ids to instructions and metadata, for a single module's ids. +struct IdInstructions { + IdInstructions(const opt::Module* module) + : inst_map_(module->IdBound(), nullptr), + name_map_(module->IdBound()), + decoration_map_(module->IdBound()), + forward_pointer_map_(module->IdBound()) { + // Map ids from all sections to instructions that define them. + MapIdsToInstruction(module->ext_inst_imports()); + MapIdsToInstruction(module->debugs1()); + MapIdsToInstruction(module->debugs2()); + MapIdsToInstruction(module->debugs3()); + MapIdsToInstruction(module->ext_inst_debuginfo()); + MapIdsToInstruction(module->types_values()); + for (const opt::Function& function : *module) { + function.ForEachInst( + [this](const opt::Instruction* inst) { + if (inst->HasResultId()) { + MapIdToInstruction(inst->result_id(), inst); + } + }, + true, true); + } + + // Gather decorations applied to ids that could be useful in matching them + // between src and dst modules. + MapIdsToInfos(module->debugs2()); + MapIdsToInfos(module->annotations()); + MapIdsToInfos(module->types_values()); + } + + void MapIdToInstruction(uint32_t id, const opt::Instruction* inst); + + // Return true if id is mapped to any instruction, false otherwise. + bool IsDefined(uint32_t id) { + return id < inst_map_.size() && inst_map_[id] != nullptr; + } + + void MapIdsToInstruction( + opt::IteratorRange section); + void MapIdsToInfos( + opt::IteratorRange section); + + IdToInstructionMap inst_map_; + IdToInfoMap name_map_; + IdToInfoMap decoration_map_; + IdToInstructionMap forward_pointer_map_; +}; + +class Differ { + public: + Differ(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) + : src_context_(src), + dst_context_(dst), + src_(src->module()), + dst_(dst->module()), + options_(options), + out_(out), + src_id_to_(src_), + dst_id_to_(dst_), + id_map_(src_->IdBound(), dst_->IdBound()) { + // Cache function bodies in canonicalization order. + GetFunctionBodies(src_context_, &src_funcs_, &src_func_insts_); + GetFunctionBodies(dst_context_, &dst_funcs_, &dst_func_insts_); + } + + // Match ids or instructions of different sections. + void MatchCapabilities(); + void MatchExtensions(); + void MatchExtInstImportIds(); + void MatchMemoryModel(); + void MatchEntryPointIds(); + void MatchExecutionModes(); + void MatchTypeForwardPointers(); + void MatchTypeIds(); + void MatchConstants(); + void MatchVariableIds(); + void MatchFunctions(); + + // Debug info and annotations are matched only after ids are matched. + void MatchDebugs1(); + void MatchDebugs2(); + void MatchDebugs3(); + void MatchExtInstDebugInfo(); + void MatchAnnotations(); + + // Output the diff. + spv_result_t Output(); + + void DumpIdMap() { + if (!options_.dump_id_map) { + return; + } + + out_ << " Src -> Dst\n"; + for (uint32_t src_id = 1; src_id < src_->IdBound(); ++src_id) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (src_id_to_.inst_map_[src_id] != nullptr && dst_id != 0) + out_ << std::setw(4) << src_id << " -> " << std::setw(4) << dst_id + << " [" << spvOpcodeString(src_id_to_.inst_map_[src_id]->opcode()) + << "]\n"; + } + } + + private: + // Helper functions that match ids between src and dst + void PoolPotentialIds( + opt::IteratorRange section, + std::vector& ids, bool is_src, + std::function filter, + std::function get_id); + void MatchIds( + PotentialIdMap& potential, + std::function + match); + // Helper functions that match id-less instructions between src and dst. + void MatchPreambleInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts); + InstructionList SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange insts); + int ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module); + // Helper functions that match debug and annotation instructions of already + // matched ids. + void MatchDebugAndAnnotationInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts); + + // Get various properties from an id. These Helper functions are passed to + // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument). + uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id); + uint32_t GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to, + uint32_t id); + spv::StorageClass GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id); + spv::Op GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id); + + // Given a list of ids, groups them based on some value. The `get_group` + // function extracts a piece of information corresponding to each id, and the + // ids are bucketed based on that (and output in `groups`). This is useful to + // attempt to match ids between src and dst only when said property is + // identical. + template + void GroupIds(const IdGroup& ids, bool is_src, std::map* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)); + + // Calls GroupIds to bucket ids in src and dst based on a property returned by + // `get_group`. This function then calls `match_group` for each bucket (i.e. + // "group") with identical values for said property. + // + // For example, say src and dst ids have the following properties + // correspondingly: + // + // - src ids' properties: {id0: A, id1: A, id2: B, id3: C, id4: B} + // - dst ids' properties: {id0': B, id1': C, id2': B, id3': D, id4': B} + // + // Then `match_group` is called 2 times: + // + // - Once with: ([id2, id4], [id0', id2', id4']) corresponding to B + // - Once with: ([id3], [id2']) corresponding to C + // + // Ids corresponding to A and D cannot match based on this property. + template + void GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group); + + // Bucket `src_ids` and `dst_ids` by the key ids returned by `get_group`, and + // then call `match_group` on pairs of buckets whose key ids are matched with + // each other. + // + // For example, suppose we want to pair up groups of instructions with the + // same type. Naturally, the source instructions refer to their types by their + // ids in the source, and the destination instructions use destination type + // ids, so simply comparing source and destination type ids as integers, as + // `GroupIdsAndMatch` would do, is meaningless. But if a prior call to + // `MatchTypeIds` has established type matches between the two modules, then + // we can consult those to pair source and destination buckets whose types are + // equivalent. + // + // Suppose our input groups are as follows: + // + // - src_ids: { 1 -> 100, 2 -> 300, 3 -> 100, 4 -> 200 } + // - dst_ids: { 5 -> 10, 6 -> 20, 7 -> 10, 8 -> 300 } + // + // Here, `X -> Y` means that the instruction with SPIR-V id `X` is a member of + // the group, and `Y` is the id of its type. If we use + // `Differ::GroupIdsHelperGetTypeId` for `get_group`, then + // `get_group(X) == Y`. + // + // These instructions are bucketed by type as follows: + // + // - source: [1, 3] -> 100 + // [4] -> 200 + // [2] -> 300 + // + // - destination: [5, 7] -> 10 + // [6] -> 20 + // [8] -> 300 + // + // Now suppose that we have previously matched up src type 100 with dst type + // 10, and src type 200 with dst type 20, but no other types are matched. + // + // Then `match_group` is called twice: + // - Once with ([1,3], [5, 7]), corresponding to 100/10 + // - Once with ([4],[6]), corresponding to 200/20 + // + // The source type 300 isn't matched with anything, so the fact that there's a + // destination type 300 is irrelevant, and thus 2 and 8 are never passed to + // `match_group`. + // + // This function isn't specific to types; it simply buckets by the ids + // returned from `get_group`, and consults existing matches to pair up the + // resulting buckets. + void GroupIdsAndMatchByMappedId( + const IdGroup& src_ids, const IdGroup& dst_ids, + uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group); + + // Helper functions that determine if two instructions match + bool DoIdsMatch(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count); + bool DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id); + bool DoDebugAndAnnotationInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility); + bool MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + bool MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id); + bool MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + + // Helper functions for matching OpTypeForwardPointer + void MatchTypeForwardPointersByName(const IdGroup& src, const IdGroup& dst); + void MatchTypeForwardPointersByTypeOp(const IdGroup& src, const IdGroup& dst); + + // Helper functions for function matching. + using FunctionMap = std::map; + + InstructionList GetFunctionBody(opt::IRContext* context, + opt::Function& function); + InstructionList GetFunctionHeader(const opt::Function& function); + void GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts); + void GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts); + void BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts); + + // Calculates the diff of two function bodies. Note that the matched + // instructions themselves may not be identical; output of exact matches + // should produce the exact instruction while inexact matches should produce a + // diff as well. + // + // Returns the similarity of the two bodies = 2*N_match / (N_src + N_dst) + void MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func); + float MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result); + void MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility); + void MatchVariablesUsedByMatchedInstructions(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + + // Helper functions to retrieve information pertaining to an id + const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id); + uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id); + spv::ExecutionModel GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id); + bool HasName(const IdInstructions& id_to, uint32_t id); + // Get the OpName associated with an id + std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name); + // Get the OpName associated with an id, with argument types stripped for + // functions. Some tools don't encode function argument types in the OpName + // string, and this improves diff between SPIR-V from those tools and others. + std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id); + uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + spv::StorageClass* storage_class); + bool GetDecorationValue(const IdInstructions& id_to, uint32_t id, + spv::Decoration decoration, + uint32_t* decoration_value); + const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to, + uint32_t id); + bool IsIntType(const IdInstructions& id_to, uint32_t type_id); + bool IsFloatType(const IdInstructions& id_to, uint32_t type_id); + bool IsConstantUint(const IdInstructions& id_to, uint32_t id); + bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id); + bool IsOp(const IdInstructions& id_to, uint32_t id, spv::Op opcode); + bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id); + bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id); + spv::StorageClass GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id); + spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to, + uint32_t set_id); + spv_number_kind_t GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width); + spv_number_kind_t GetTypeNumberKind(const IdInstructions& id_to, uint32_t id, + uint32_t* number_bit_width); + + // Helper functions to output a diff line + const opt::Instruction* MappedDstInst(const opt::Instruction* src_inst); + const opt::Instruction* MappedSrcInst(const opt::Instruction* dst_inst); + const opt::Instruction* MappedInstImpl(const opt::Instruction* inst, + const IdMap& to_other, + const IdInstructions& other_id_to); + void OutputLine(std::function are_lines_identical, + std::function output_src_line, + std::function output_dst_line); + template + void OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function + write_inst); + void ToParsedInstruction(const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector& parsed_operands, + std::vector& inst_binary); + opt::Instruction ToMappedSrcIds(const opt::Instruction& dst_inst); + + void OutputRed() { + if (options_.color_output) out_ << spvtools::clr::red{true}; + } + void OutputGreen() { + if (options_.color_output) out_ << spvtools::clr::green{true}; + } + void OutputResetColor() { + if (options_.color_output) out_ << spvtools::clr::reset{true}; + } + + opt::IRContext* src_context_; + opt::IRContext* dst_context_; + const opt::Module* src_; + const opt::Module* dst_; + Options options_; + std::ostream& out_; + + // Helpers to look up instructions based on id. + IdInstructions src_id_to_; + IdInstructions dst_id_to_; + + // The ids that have been matched between src and dst so far. + SrcDstIdMap id_map_; + + // List of instructions in function bodies after canonicalization. Cached + // here to avoid duplicate work. More importantly, some maps use + // opt::Instruction pointers so they need to be unique. + FunctionInstMap src_func_insts_; + FunctionInstMap dst_func_insts_; + FunctionMap src_funcs_; + FunctionMap dst_funcs_; +}; + +void SrcDstIdMap::MapUnmatchedIds( + std::function src_insn_defined, + std::function dst_insn_defined) { + const uint32_t src_id_bound = static_cast(src_to_dst_.IdBound()); + const uint32_t dst_id_bound = static_cast(dst_to_src_.IdBound()); + + for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) { + if (!src_to_dst_.IsMapped(src_id) && src_insn_defined(src_id)) { + uint32_t fresh_dst_id = dst_to_src_.MakeFreshId(); + MapIds(src_id, fresh_dst_id); + } + } + + for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) { + if (!dst_to_src_.IsMapped(dst_id) && dst_insn_defined(dst_id)) { + uint32_t fresh_src_id = src_to_dst_.MakeFreshId(); + MapIds(fresh_src_id, dst_id); + } + } +} + +void IdInstructions::MapIdToInstruction(uint32_t id, + const opt::Instruction* inst) { + assert(id != 0); + assert(id < inst_map_.size()); + assert(inst_map_[id] == nullptr); + + inst_map_[id] = inst; +} + +void IdInstructions::MapIdsToInstruction( + opt::IteratorRange section) { + for (const opt::Instruction& inst : section) { + uint32_t result_id = inst.result_id(); + if (result_id == 0) { + continue; + } + + MapIdToInstruction(result_id, &inst); + } +} + +void IdInstructions::MapIdsToInfos( + opt::IteratorRange section) { + for (const opt::Instruction& inst : section) { + IdToInfoMap* info_map = nullptr; + uint32_t id_operand = 0; + + switch (inst.opcode()) { + case spv::Op::OpName: + info_map = &name_map_; + break; + case spv::Op::OpMemberName: + info_map = &name_map_; + break; + case spv::Op::OpDecorate: + info_map = &decoration_map_; + break; + case spv::Op::OpMemberDecorate: + info_map = &decoration_map_; + break; + case spv::Op::OpTypeForwardPointer: { + uint32_t id = inst.GetSingleWordOperand(0); + assert(id != 0); + + assert(id < forward_pointer_map_.size()); + forward_pointer_map_[id] = &inst; + continue; + } + default: + // Currently unsupported instruction, don't attempt to use it for + // matching. + break; + } + + if (info_map == nullptr) { + continue; + } + + uint32_t id = inst.GetOperand(id_operand).AsId(); + assert(id != 0); + + assert(id < info_map->size()); + assert(std::find((*info_map)[id].begin(), (*info_map)[id].end(), &inst) == + (*info_map)[id].end()); + + (*info_map)[id].push_back(&inst); + } +} + +void Differ::PoolPotentialIds( + opt::IteratorRange section, + std::vector& ids, bool is_src, + std::function filter, + std::function get_id) { + for (const opt::Instruction& inst : section) { + if (!filter(inst)) { + continue; + } + + uint32_t result_id = get_id(inst); + assert(result_id != 0); + + assert(std::find(ids.begin(), ids.end(), result_id) == ids.end()); + + // Don't include ids that are already matched, for example through + // OpTypeForwardPointer. + const bool is_matched = is_src ? id_map_.IsSrcMapped(result_id) + : id_map_.IsDstMapped(result_id); + if (is_matched) { + continue; + } + + ids.push_back(result_id); + } +} + +void Differ::MatchIds( + PotentialIdMap& potential, + std::function + match) { + for (size_t src_index = 0; src_index < potential.src_ids.size(); + ++src_index) { + for (size_t dst_index = 0; dst_index < potential.dst_ids.size(); + ++dst_index) { + const uint32_t src_id = potential.src_ids[src_index]; + const uint32_t dst_id = potential.dst_ids[dst_index]; + + if (dst_id == 0) { + // Already matched. + continue; + } + + const opt::Instruction* src_inst = src_id_to_.inst_map_[src_id]; + const opt::Instruction* dst_inst = dst_id_to_.inst_map_[dst_id]; + + if (match(src_inst, dst_inst)) { + id_map_.MapIds(src_id, dst_id); + + // Remove the ids from the potential list. + potential.src_ids[src_index] = 0; + potential.dst_ids[dst_index] = 0; + + // Find a match for the next src id. + break; + } + } + } + + // Remove matched ids to make the next iteration faster. + CompactIds(potential.src_ids); + CompactIds(potential.dst_ids); +} + +void Differ::MatchPreambleInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts) { + // First, pool all instructions from each section and sort them. + InstructionList sorted_src_insts = SortPreambleInstructions(src_, src_insts); + InstructionList sorted_dst_insts = SortPreambleInstructions(dst_, dst_insts); + + // Then walk and match them. + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < sorted_src_insts.size() && + dst_cur < sorted_dst_insts.size()) { + const opt::Instruction* src_inst = sorted_src_insts[src_cur]; + const opt::Instruction* dst_inst = sorted_dst_insts[dst_cur]; + + int compare = ComparePreambleInstructions(src_inst, dst_inst, src_, dst_); + if (compare == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + if (compare <= 0) { + ++src_cur; + } + if (compare >= 0) { + ++dst_cur; + } + } +} + +InstructionList Differ::SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange insts) { + InstructionList sorted; + for (const opt::Instruction& inst : insts) { + sorted.push_back(&inst); + } + std::sort( + sorted.begin(), sorted.end(), + [this, module](const opt::Instruction* a, const opt::Instruction* b) { + return ComparePreambleInstructions(a, b, module, module) < 0; + }); + return sorted; +} + +int Differ::ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module) { + assert(a->opcode() == b->opcode()); + assert(!a->HasResultId()); + assert(!a->HasResultType()); + + const uint32_t a_operand_count = a->NumOperands(); + const uint32_t b_operand_count = b->NumOperands(); + + if (a_operand_count < b_operand_count) { + return -1; + } + if (a_operand_count > b_operand_count) { + return 1; + } + + // Instead of comparing OpExecutionMode entry point ids as ids, compare them + // through their corresponding execution model. This simplifies traversing + // the sorted list of instructions between src and dst modules. + if (a->opcode() == spv::Op::OpExecutionMode) { + const spv::ExecutionModel src_model = + GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0)); + const spv::ExecutionModel dst_model = + GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0)); + + if (src_model < dst_model) { + return -1; + } + if (src_model > dst_model) { + return 1; + } + } + + // Match every operand of the instruction. + for (uint32_t operand_index = 0; operand_index < a_operand_count; + ++operand_index) { + const opt::Operand& a_operand = a->GetOperand(operand_index); + const opt::Operand& b_operand = b->GetOperand(operand_index); + + if (a_operand.type < b_operand.type) { + return -1; + } + if (a_operand.type > b_operand.type) { + return 1; + } + + switch (a_operand.type) { + case SPV_OPERAND_TYPE_ID: + // Don't compare ids, there can't be multiple instances of the + // OpExecutionMode with different ids of the same execution model. + break; + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + assert(false && "Unreachable"); + break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + int str_compare = + strcmp(a_operand.AsString().c_str(), b_operand.AsString().c_str()); + if (str_compare != 0) { + return str_compare; + } + break; + } + default: + // Expect literal values to match. + assert(a_operand.words.size() == 1); + assert(b_operand.words.size() == 1); + + if (a_operand.words[0] < b_operand.words[0]) { + return -1; + } + if (a_operand.words[0] > b_operand.words[0]) { + return 1; + } + break; + } + } + + return 0; +} + +void Differ::MatchDebugAndAnnotationInstructions( + opt::IteratorRange src_insts, + opt::IteratorRange dst_insts) { + for (const opt::Instruction& src_inst : src_insts) { + for (const opt::Instruction& dst_inst : dst_insts) { + if (MappedSrcInst(&dst_inst) != nullptr) { + continue; + } + + // Map instructions as soon as they match. Debug and annotation + // instructions are matched such that there can't be multiple matches. + if (DoDebugAndAnnotationInstructionsMatch(&src_inst, &dst_inst)) { + id_map_.MapInsts(&src_inst, &dst_inst); + break; + } + } + } +} + +uint32_t Differ::GroupIdsHelperGetTypeId(const IdInstructions& id_to, + uint32_t id) { + return GetInst(id_to, id)->type_id(); +} + +// Return an `OpFunction` instruction's full `OpTypeFunction` type, +// which includes parameter types. +// +// `GroupIdsHelperGetTypeId` applied to an `OpFunction` only gets the +// function's return type, so this is a slightly more precise way to +// match up functions by signature. +uint32_t Differ::GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to, + uint32_t id) { + return GetInst(id_to, id)->GetSingleWordOperand(3); +} + +spv::StorageClass Differ::GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == spv::Op::OpTypePointer); + return spv::StorageClass(inst->GetSingleWordInOperand(0)); +} + +spv::Op Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == spv::Op::OpTypePointer); + + const uint32_t type_id = inst->GetSingleWordInOperand(1); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + assert(type_inst); + + return type_inst->opcode(); +} + +// Group unmatched ids in `ids` according to some characteristic, +// determined by `get_group`. +// +// Using `get_group` to compute some sort of key for each id, set +// `groups` to map each key to all the ids that have that key. +// +// For example, to group ids by name, pass `Differ::GetName` as +// `get_group`. This will fill `groups` with a map from each name to +// all the ids with that name. +// +// Under the assumption that we're trying to establish new pairings, +// ids that are already paired are omitted from `groups`. +// +// The `is_src` parameter indicates whether `ids` are drawn from the +// source module or the destination module. +// +// The template parameter `T` is the key type, like `std::string` or +// `uint32_t`. +template +void Differ::GroupIds(const IdGroup& ids, bool is_src, + std::map* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)) { + assert(groups->empty()); + + const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_; + + for (const uint32_t id : ids) { + // Don't include ids that are already matched, for example through + // OpEntryPoint. + const bool is_matched = + is_src ? id_map_.IsSrcMapped(id) : id_map_.IsDstMapped(id); + if (is_matched) { + continue; + } + + T group = (this->*get_group)(id_to, id); + (*groups)[group].push_back(id); + } +} + +// Group `src_ids` and `dst_ids` according to `get_group`, and then use +// `match_group` to pair up ids in corresponding groups. +// +// Don't try to pair ids in groups whose key is `invalid_group_key`. +template +void Differ::GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group) { + // Group the ids based on a key (get_group) + std::map src_groups; + std::map dst_groups; + + GroupIds(src_ids, true, &src_groups, get_group); + GroupIds(dst_ids, false, &dst_groups, get_group); + + // Iterate over the groups, and match those with identical keys + for (const auto& iter : src_groups) { + const T& key = iter.first; + const IdGroup& src_group = iter.second; + + if (key == invalid_group_key) { + continue; + } + + const IdGroup& dst_group = dst_groups[key]; + + // Let the caller match the groups as appropriate. + match_group(src_group, dst_group); + } +} + +void Differ::GroupIdsAndMatchByMappedId( + const IdGroup& src_ids, const IdGroup& dst_ids, + uint32_t (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function + match_group) { + // Group the ids based on a key (get_group) + std::map src_groups; + std::map dst_groups; + + GroupIds(src_ids, true, &src_groups, get_group); + GroupIds(dst_ids, false, &dst_groups, get_group); + + // Iterate over pairs of groups whose keys map to each other. + for (const auto& iter : src_groups) { + const uint32_t& src_key = iter.first; + const IdGroup& src_group = iter.second; + + if (src_key == 0) { + continue; + } + + if (id_map_.IsSrcMapped(src_key)) { + const uint32_t& dst_key = id_map_.MappedDstId(src_key); + const IdGroup& dst_group = dst_groups[dst_key]; + + // Let the caller match the groups as appropriate. + match_group(src_group, dst_group); + } + } +} + +bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + return id_map_.MappedDstId(src_id) == dst_id; +} + +bool Differ::DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + assert(src_operand.type == dst_operand.type); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match ids only if they are already matched in the id map. + return DoIdsMatch(src_operand.AsId(), dst_operand.AsId()); + case SPV_OPERAND_TYPE_LITERAL_STRING: + return src_operand.AsString() == dst_operand.AsString(); + default: + // Otherwise expect them to match exactly. + assert(src_operand.type != SPV_OPERAND_TYPE_LITERAL_STRING); + if (src_operand.words.size() != dst_operand.words.size()) { + return false; + } + for (size_t i = 0; i < src_operand.words.size(); ++i) { + if (src_operand.words[i] != dst_operand.words[i]) { + return false; + } + } + return true; + } +} + +bool Differ::DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count) { + // Caller should have returned early for instructions with different opcode. + assert(src_inst->opcode() == dst_inst->opcode()); + + bool match = true; + for (uint32_t i = 0; i < in_operand_count; ++i) { + const uint32_t in_operand_index = in_operand_index_start + i; + + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatch(src_operand, dst_operand); + } + + return match; +} + +bool Differ::DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Check whether the two instructions are identical, that is the instructions + // themselves are matched, every id is matched, and every other value is + // identical. + if (MappedDstInst(src_inst) != dst_inst) { + return false; + } + + assert(src_inst->opcode() == dst_inst->opcode()); + if (src_inst->NumOperands() != dst_inst->NumOperands()) { + return false; + } + + for (uint32_t operand_index = 0; operand_index < src_inst->NumOperands(); + ++operand_index) { + const opt::Operand& src_operand = src_inst->GetOperand(operand_index); + const opt::Operand& dst_operand = dst_inst->GetOperand(operand_index); + + if (!DoesOperandMatch(src_operand, dst_operand)) { + return false; + } + } + + return true; +} + +bool Differ::DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + const uint32_t mapped_dst_id = id_map_.MappedDstId(src_id); + + // Consider unmatched ids as a match. In function bodies, no result id is + // matched yet and thus they are excluded from instruction matching when used + // as parameters in subsequent instructions. + if (mapped_dst_id == 0 || mapped_dst_id == dst_id) { + return true; + } + + // Int and Uint constants are interchangeable, match them in that case. + if (AreIdenticalUintConstants(src_id, dst_id)) { + return true; + } + + return false; +} + +bool Differ::DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + if (src_operand.type != dst_operand.type) { + return false; + } + + assert(src_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + assert(dst_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match id operands only if they are already matched in the id map. + return DoIdsMatchFuzzy(src_operand.AsId(), dst_operand.AsId()); + default: + // Otherwise allow everything to match. + return true; + } +} + +bool Differ::DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Similar to DoOperandsMatch, but only checks that ids that have already been + // matched are identical. Ids that are unknown are allowed to match, as well + // as any non-id operand. + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + // For external instructions, make sure the set and opcode of the external + // instruction matches too. + if (src_inst->opcode() == spv::Op::OpExtInst) { + if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) { + return false; + } + } + + assert(src_inst->HasResultType() == dst_inst->HasResultType()); + if (src_inst->HasResultType() && + !DoIdsMatchFuzzy(src_inst->type_id(), dst_inst->type_id())) { + return false; + } + + // TODO: allow some instructions to match with different instruction lengths, + // for example OpImage* with additional operands. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + + bool match = true; + for (uint32_t in_operand_index = 0; + in_operand_index < src_inst->NumInOperandWords(); ++in_operand_index) { + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatchFuzzy(src_operand, dst_operand); + } + + return match; +} + +bool Differ::AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id) { + return IsConstantUint(src_id_to_, src_id) && + IsConstantUint(dst_id_to_, dst_id) && + GetConstantUint(src_id_to_, src_id) == + GetConstantUint(dst_id_to_, dst_id); +} + +bool Differ::DoDebugAndAnnotationInstructionsMatch( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst) { + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + + switch (src_inst->opcode()) { + case spv::Op::OpString: + case spv::Op::OpSourceExtension: + case spv::Op::OpModuleProcessed: + return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0)); + case spv::Op::OpSource: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case spv::Op::OpSourceContinued: + return true; + case spv::Op::OpName: + return DoOperandsMatch(src_inst, dst_inst, 0, 1); + case spv::Op::OpMemberName: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case spv::Op::OpDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case spv::Op::OpMemberDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 3); + case spv::Op::OpExtInst: + case spv::Op::OpDecorationGroup: + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: + return false; + default: + return false; + } +} + +bool Differ::AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility) { + // Variables must match by their built-in decorations. + uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, spv::Decoration::BuiltIn, &src_built_in_decoration); + const bool dst_is_built_in = GetDecorationValue( + dst_id_to_, dst_id, spv::Decoration::BuiltIn, &dst_built_in_decoration); + + if (src_is_built_in != dst_is_built_in) { + return false; + } + if (src_is_built_in && src_built_in_decoration != dst_built_in_decoration) { + return false; + } + + // Check their types and storage classes. + spv::StorageClass src_storage_class, dst_storage_class; + const uint32_t src_type_id = + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + const uint32_t dst_type_id = + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (!DoIdsMatch(src_type_id, dst_type_id)) { + return false; + } + switch (flexibility) { + case 0: + if (src_storage_class != dst_storage_class) { + return false; + } + break; + case 1: + if (src_storage_class != dst_storage_class) { + // Allow one of the two to be Private while the other is Input or + // Output, this allows matching in/out variables that have been turned + // global as part of linking two stages (as done in ANGLE). + const bool src_is_io = src_storage_class == spv::StorageClass::Input || + src_storage_class == spv::StorageClass::Output; + const bool dst_is_io = dst_storage_class == spv::StorageClass::Input || + dst_storage_class == spv::StorageClass::Output; + const bool src_is_private = + src_storage_class == spv::StorageClass::Private; + const bool dst_is_private = + dst_storage_class == spv::StorageClass::Private; + + if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) { + return false; + } + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + // TODO: Is there any other way to check compatiblity of the variables? It's + // easy to tell when the variables definitely don't match, but there's little + // information that can be used for a definite match. + return true; +} + +bool Differ::MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_type_id = src_inst->result_id(); + const uint32_t dst_type_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_type_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_type_id, &dst_has_name); + + // If debug info is present, always match the structs by name. + if (src_has_name && dst_has_name) { + if (src_name != dst_name) { + return false; + } + + // For gl_PerVertex, find the type pointer of this type (array) and make + // sure the storage classes of src and dst match; geometry and tessellation + // shaders have two instances of gl_PerVertex. + if (src_name == "gl_PerVertex") { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + return true; + } + + // If debug info is not present, match the structs by their type. + + // For gl_PerVertex, find the type pointer of this type (array) and match by + // storage class. The gl_PerVertex struct is itself found by the BuiltIn + // decorations applied to its members. + const bool src_is_per_vertex = IsPerVertexType(src_id_to_, src_type_id); + const bool dst_is_per_vertex = IsPerVertexType(dst_id_to_, dst_type_id); + if (src_is_per_vertex != dst_is_per_vertex) { + return false; + } + + if (src_is_per_vertex) { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + switch (flexibility) { + case 0: + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case 1: + // TODO: match by taking a diff of the fields, and see if there's a >75% + // match. Need to then make sure OpMemberName, OpMemberDecorate, + // OpAccessChain etc are aware of the struct field matching. + return false; + default: + assert(false && "Unreachable"); + return false; + } +} + +bool Differ::MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + // The constants' type must match. In flexibility == 1, match constants of + // int and uint, as they are generally interchangeable. + switch (flexibility) { + case 0: + if (!DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0))) { + return false; + } + break; + case 1: + if (!IsIntType(src_id_to_, src_inst->type_id()) || + !IsIntType(dst_id_to_, dst_inst->type_id())) { + return false; + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + const opt::Operand& src_value_operand = src_inst->GetOperand(2); + const opt::Operand& dst_value_operand = dst_inst->GetOperand(2); + + const uint64_t src_value = src_value_operand.AsLiteralUint64(); + const uint64_t dst_value = dst_value_operand.AsLiteralUint64(); + + // If values are identical, it's a match. + if (src_value == dst_value) { + return true; + } + + // Otherwise, only allow flexibility for float types. + if (IsFloatType(src_id_to_, src_inst->type_id()) && flexibility == 1) { + // Tolerance is: + // + // - For float: allow 4 bits of mantissa as error + // - For double: allow 6 bits of mantissa as error + // + // TODO: the above values are arbitrary and a placeholder; investigate the + // amount of error resulting from using `printf("%f", f)` and `printf("%lf", + // d)` and having glslang parse them. + const uint64_t tolerance = src_value_operand.words.size() == 1 ? 16 : 64; + return src_value - dst_value < tolerance || + dst_value - src_value < tolerance; + } + + return false; +} + +bool Differ::MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the spec consts by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // Otherwise, match them by SpecId. + uint32_t src_spec_id, dst_spec_id; + + if (GetDecorationValue(src_id_to_, src_id, spv::Decoration::SpecId, + &src_spec_id) && + GetDecorationValue(dst_id_to_, dst_id, spv::Decoration::SpecId, + &dst_spec_id)) { + return src_spec_id == dst_spec_id; + } + + // There is no SpecId decoration, while not practical, still valid. + // SpecConstantOp don't have SpecId and can be matched by operands + if (src_inst->opcode() == spv::Op::OpSpecConstantOp) { + if (src_inst->NumInOperandWords() == dst_inst->NumInOperandWords()) { + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + } + } + + return false; +} + +bool Differ::MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + const bool src_is_pervertex = IsPerVertexVariable(src_id_to_, src_id); + const bool dst_is_pervertex = IsPerVertexVariable(dst_id_to_, dst_id); + + // For gl_PerVertex, make sure the input and output instances are matched + // correctly. + if (src_is_pervertex != dst_is_pervertex) { + return false; + } + if (src_is_pervertex) { + return MatchPerVertexVariable(src_inst, dst_inst); + } + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the variables by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // If debug info is not present, see if the variables can be matched by their + // built-in decorations. + uint32_t src_built_in_decoration; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, spv::Decoration::BuiltIn, &src_built_in_decoration); + + if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) { + return true; + } + + spv::StorageClass src_storage_class, dst_storage_class; + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (src_storage_class != dst_storage_class) { + return false; + } + + // If variables are decorated with set/binding, match by the value of those + // decorations. + if (!options_.ignore_set_binding) { + uint32_t src_set = 0, dst_set = 0; + uint32_t src_binding = 0, dst_binding = 0; + + const bool src_has_set = GetDecorationValue( + src_id_to_, src_id, spv::Decoration::DescriptorSet, &src_set); + const bool dst_has_set = GetDecorationValue( + dst_id_to_, dst_id, spv::Decoration::DescriptorSet, &dst_set); + const bool src_has_binding = GetDecorationValue( + src_id_to_, src_id, spv::Decoration::Binding, &src_set); + const bool dst_has_binding = GetDecorationValue( + dst_id_to_, dst_id, spv::Decoration::Binding, &dst_set); + + if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) { + return src_set == dst_set && src_binding == dst_binding; + } + } + + // If variables are decorated with location, match by the value of that + // decoration. + if (!options_.ignore_location) { + uint32_t src_location, dst_location; + + const bool src_has_location = GetDecorationValue( + src_id_to_, src_id, spv::Decoration::Location, &src_location); + const bool dst_has_location = GetDecorationValue( + dst_id_to_, dst_id, spv::Decoration::Location, &dst_location); + + if (src_has_location && dst_has_location) { + return src_location == dst_location; + } + } + + // Currently, there's no other way to match variables. + return false; +} + +bool Differ::MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id) { + // For gl_PerVertex, find the type pointer of this type (array) and make sure + // the storage classes of src and dst match; geometry and tessellation shaders + // have two instances of gl_PerVertex. + spv::StorageClass src_storage_class = + GetPerVertexStorageClass(src_, src_type_id); + spv::StorageClass dst_storage_class = + GetPerVertexStorageClass(dst_, dst_type_id); + + assert(src_storage_class == spv::StorageClass::Input || + src_storage_class == spv::StorageClass::Output); + assert(dst_storage_class == spv::StorageClass::Input || + dst_storage_class == spv::StorageClass::Output); + + return src_storage_class == dst_storage_class; +} + +bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + spv::StorageClass src_storage_class = + spv::StorageClass(src_inst->GetSingleWordInOperand(0)); + spv::StorageClass dst_storage_class = + spv::StorageClass(dst_inst->GetSingleWordInOperand(0)); + + return src_storage_class == dst_storage_class; +} + +void Differ::MatchTypeForwardPointersByName(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by name. + + // Group them by debug info and loop over them. + GroupIdsAndMatch( + src, dst, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + // Match only if there's a unique forward declaration with this debug + // name. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); +} + +void Differ::MatchTypeForwardPointersByTypeOp(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by type op. Must be called after + // MatchTypeForwardPointersByName to match as many as possible by debug info. + + // Remove ids that are matched with debug info in + // MatchTypeForwardPointersByName. + IdGroup src_unmatched_ids; + IdGroup dst_unmatched_ids; + + std::copy_if(src.begin(), src.end(), std::back_inserter(src_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsSrcMapped(id); }); + std::copy_if(dst.begin(), dst.end(), std::back_inserter(dst_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsDstMapped(id); }); + + // Match only if there's a unique forward declaration with this + // storage class and type opcode. If both have debug info, they + // must not have been matchable. + if (src_unmatched_ids.size() == 1 && dst_unmatched_ids.size() == 1) { + uint32_t src_id = src_unmatched_ids[0]; + uint32_t dst_id = dst_unmatched_ids[0]; + if (!HasName(src_id_to_, src_id) || !HasName(dst_id_to_, dst_id)) { + id_map_.MapIds(src_id, dst_id); + } + } +} + +InstructionList Differ::GetFunctionBody(opt::IRContext* context, + opt::Function& function) { + // Canonicalize the blocks of the function to produce better diff, for example + // to not produce any diff if the src and dst have the same switch/case blocks + // but with the cases simply reordered. + std::list order; + context->cfg()->ComputeStructuredOrder(&function, &*function.begin(), &order); + + // Go over the instructions of the function and add the instructions to a flat + // list to simplify future iterations. + InstructionList body; + for (opt::BasicBlock* block : order) { + block->ForEachInst( + [&body](const opt::Instruction* inst) { body.push_back(inst); }, true); + } + body.push_back(function.EndInst()); + + return body; +} + +InstructionList Differ::GetFunctionHeader(const opt::Function& function) { + // Go over the instructions of the function and add the header instructions to + // a flat list to simplify diff generation. + InstructionList body; + function.WhileEachInst( + [&body](const opt::Instruction* inst) { + if (inst->opcode() == spv::Op::OpLabel) { + return false; + } + body.push_back(inst); + return true; + }, + true, true); + + return body; +} + +void Differ::GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts) { + for (opt::Function& function : *context->module()) { + uint32_t id = function.result_id(); + assert(functions->find(id) == functions->end()); + assert(function_insts->find(id) == function_insts->end()); + + (*functions)[id] = &function; + + InstructionList body = GetFunctionBody(context, function); + (*function_insts)[id] = std::move(body); + } +} + +void Differ::GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts) { + for (opt::Function& function : *module) { + InstructionList body = GetFunctionHeader(function); + (*function_insts)[function.result_id()] = std::move(body); + } +} + +void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts) { + struct MatchResult { + uint32_t src_id; + uint32_t dst_id; + DiffMatch src_match; + DiffMatch dst_match; + float match_rate; + bool operator<(const MatchResult& other) const { + return match_rate > other.match_rate; + } + }; + std::vector all_match_results; + + for (const uint32_t src_func_id : src_func_ids) { + if (id_map_.IsSrcMapped(src_func_id)) { + continue; + } + const std::string src_name = GetSanitizedName(src_id_to_, src_func_id); + + for (const uint32_t dst_func_id : dst_func_ids) { + if (id_map_.IsDstMapped(dst_func_id)) { + continue; + } + + // Don't match functions that are named, but the names are different. + const std::string dst_name = GetSanitizedName(dst_id_to_, dst_func_id); + if (src_name != "" && dst_name != "" && src_name != dst_name) { + continue; + } + + DiffMatch src_match_result, dst_match_result; + float match_rate = MatchFunctionBodies( + src_func_insts.at(src_func_id), dst_func_insts.at(dst_func_id), + &src_match_result, &dst_match_result); + + // Only consider the functions a match if there's at least 60% match. + // This is an arbitrary limit that should be tuned. + constexpr float pass_match_rate = 0.6f; + if (match_rate >= pass_match_rate) { + all_match_results.emplace_back( + MatchResult{src_func_id, dst_func_id, std::move(src_match_result), + std::move(dst_match_result), match_rate}); + } + } + } + + std::sort(all_match_results.begin(), all_match_results.end()); + + for (const MatchResult& match_result : all_match_results) { + if (id_map_.IsSrcMapped(match_result.src_id) || + id_map_.IsDstMapped(match_result.dst_id)) { + continue; + } + + id_map_.MapIds(match_result.src_id, match_result.dst_id); + + MatchFunctionParamIds(src_funcs_[match_result.src_id], + dst_funcs_[match_result.dst_id]); + MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id), + dst_func_insts.at(match_result.dst_id), + match_result.src_match, match_result.dst_match, 0); + } +} + +void Differ::MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func) { + IdGroup src_params; + IdGroup dst_params; + src_func->ForEachParam( + [&src_params](const opt::Instruction* param) { + src_params.push_back(param->result_id()); + }, + false); + dst_func->ForEachParam( + [&dst_params](const opt::Instruction* param) { + dst_params.push_back(param->result_id()); + }, + false); + + GroupIdsAndMatch( + src_params, dst_params, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + // There shouldn't be two parameters with the same name, so the ids + // should match. There is nothing restricting the SPIR-V however to have + // two parameters with the same name, so be resilient against that. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); + + // Then match the parameters by their type. If there are multiple of them, + // match them by their order. + GroupIdsAndMatchByMappedId( + src_params, dst_params, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + const size_t shared_param_count = + std::min(src_group_by_type_id.size(), dst_group_by_type_id.size()); + + for (size_t param_index = 0; param_index < shared_param_count; + ++param_index) { + id_map_.MapIds(src_group_by_type_id[param_index], + dst_group_by_type_id[param_index]); + } + }); +} + +float Differ::MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result) { + LongestCommonSubsequence> lcs(src_body, + dst_body); + + uint32_t best_match_length = lcs.Get( + [this](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + return DoInstructionsMatchFuzzy(src_inst, dst_inst); + }, + src_match_result, dst_match_result); + + // TODO: take the gaps in between matches and match those again with a relaxed + // instruction-and-type-only comparison. This can produce a better diff for + // example if an array index is changed, causing the OpAccessChain id to not + // match and subsequently every operation that's derived from that id. + // Usually this mismatch cascades until the next OpStore which doesn't produce + // an id. + + return static_cast(best_match_length) * 2.0f / + static_cast(src_body.size() + dst_body.size()); +} + +void Differ::MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility) { + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < src_body.size() && dst_cur < dst_body.size()) { + if (src_match_result[src_cur] && dst_match_result[dst_cur]) { + // Match instructions the src and dst instructions. + // + // TODO: count the matchings between variables discovered this way and + // choose the "best match" after all functions have been diffed and all + // instructions analyzed. + const opt::Instruction* src_inst = src_body[src_cur++]; + const opt::Instruction* dst_inst = dst_body[dst_cur++]; + + // Record the matching between the instructions. This is done only once + // (hence flexibility == 0). Calls with non-zero flexibility values will + // only deal with matching other ids based on the operands. + if (flexibility == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + + // Match any unmatched variables referenced by the instructions. + MatchVariablesUsedByMatchedInstructions(src_inst, dst_inst, flexibility); + continue; + } + if (!src_match_result[src_cur]) { + ++src_cur; + } + if (!dst_match_result[dst_cur]) { + ++dst_cur; + } + } +} + +void Differ::MatchVariablesUsedByMatchedInstructions( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst, + uint32_t flexibility) { + // For OpAccessChain, OpLoad and OpStore instructions that reference unmatched + // variables, match them as a best effort. + assert(src_inst->opcode() == dst_inst->opcode()); + switch (src_inst->opcode()) { + default: + // TODO: match functions based on OpFunctionCall? + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpLoad: + case spv::Op::OpStore: + const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0); + const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0); + if (IsVariable(src_id_to_, src_pointer_id) && + IsVariable(dst_id_to_, dst_pointer_id) && + !id_map_.IsSrcMapped(src_pointer_id) && + !id_map_.IsDstMapped(dst_pointer_id) && + AreVariablesMatchable(src_pointer_id, dst_pointer_id, flexibility)) { + id_map_.MapIds(src_pointer_id, dst_pointer_id); + } + break; + } +} + +const opt::Instruction* Differ::GetInst(const IdInstructions& id_to, + uint32_t id) { + assert(id != 0); + assert(id < id_to.inst_map_.size()); + + const opt::Instruction* inst = id_to.inst_map_[id]; + assert(inst != nullptr); + + return inst; +} + +uint32_t Differ::GetConstantUint(const IdInstructions& id_to, + uint32_t constant_id) { + const opt::Instruction* constant_inst = GetInst(id_to, constant_id); + assert(constant_inst->opcode() == spv::Op::OpConstant); + assert(GetInst(id_to, constant_inst->type_id())->opcode() == + spv::Op::OpTypeInt); + + return constant_inst->GetSingleWordInOperand(0); +} + +spv::ExecutionModel Differ::GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id) { + for (const opt::Instruction& inst : module->entry_points()) { + assert(inst.opcode() == spv::Op::OpEntryPoint); + if (inst.GetSingleWordOperand(1) == entry_point_id) { + return spv::ExecutionModel(inst.GetSingleWordOperand(0)); + } + } + + assert(false && "Unreachable"); + return spv::ExecutionModel(0xFFF); +} + +bool Differ::HasName(const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == spv::Op::OpName) { + return true; + } + } + + return false; +} + +std::string Differ::GetName(const IdInstructions& id_to, uint32_t id, + bool* has_name) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == spv::Op::OpName) { + *has_name = true; + return inst->GetOperand(1).AsString(); + } + } + + *has_name = false; + return ""; +} + +std::string Differ::GetSanitizedName(const IdInstructions& id_to, uint32_t id) { + bool has_name = false; + std::string name = GetName(id_to, id, &has_name); + + if (!has_name) { + return ""; + } + + // Remove args from the name, in case this is a function name + return name.substr(0, name.find('(')); +} + +uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + spv::StorageClass* storage_class) { + const opt::Instruction* var_inst = GetInst(id_to, var_id); + assert(var_inst->opcode() == spv::Op::OpVariable); + + *storage_class = spv::StorageClass(var_inst->GetSingleWordInOperand(0)); + + // Get the type pointer from the variable. + const uint32_t type_pointer_id = var_inst->type_id(); + const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id); + + // Get the type from the type pointer. + return type_pointer_inst->GetSingleWordInOperand(1); +} + +bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id, + spv::Decoration decoration, + uint32_t* decoration_value) { + assert(id != 0); + assert(id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[id]) { + if (inst->opcode() == spv::Op::OpDecorate && + inst->GetSingleWordOperand(0) == id && + spv::Decoration(inst->GetSingleWordOperand(1)) == decoration) { + *decoration_value = inst->GetSingleWordOperand(2); + return true; + } + } + + return false; +} + +const opt::Instruction* Differ::GetForwardPointerInst( + const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.forward_pointer_map_.size()); + return id_to.forward_pointer_map_[id]; +} + +bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, spv::Op::OpTypeInt); +} + +bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, spv::Op::OpTypeFloat); +} + +bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* constant_inst = GetInst(id_to, id); + if (constant_inst->opcode() != spv::Op::OpConstant) { + return false; + } + + const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id()); + return type_inst->opcode() == spv::Op::OpTypeInt; +} + +bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) { + return IsOp(id_to, pointer_id, spv::Op::OpVariable); +} + +bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, spv::Op op) { + return GetInst(id_to, id)->opcode() == op; +} + +bool Differ::IsPerVertexType(const IdInstructions& id_to, uint32_t type_id) { + assert(type_id != 0); + assert(type_id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) { + if (inst->opcode() == spv::Op::OpMemberDecorate && + inst->GetSingleWordOperand(0) == type_id && + spv::Decoration(inst->GetSingleWordOperand(2)) == + spv::Decoration::BuiltIn) { + spv::BuiltIn built_in = spv::BuiltIn(inst->GetSingleWordOperand(3)); + + // Only gl_PerVertex can have, and it can only have, the following + // built-in decorations. + return built_in == spv::BuiltIn::Position || + built_in == spv::BuiltIn::PointSize || + built_in == spv::BuiltIn::ClipDistance || + built_in == spv::BuiltIn::CullDistance; + } + } + + return false; +} + +bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) { + // Get the type from the type pointer. + spv::StorageClass storage_class; + uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + + // If array, get the element type. + if (type_inst->opcode() == spv::Op::OpTypeArray) { + type_id = type_inst->GetSingleWordInOperand(0); + } + + // Now check if the type is gl_PerVertex. + return IsPerVertexType(id_to, type_id); +} + +spv::StorageClass Differ::GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id) { + for (const opt::Instruction& inst : module->types_values()) { + switch (inst.opcode()) { + case spv::Op::OpTypeArray: + // The gl_PerVertex instance could be an array, look for a variable of + // the array type instead. + if (inst.GetSingleWordInOperand(0) == type_id) { + type_id = inst.result_id(); + } + break; + case spv::Op::OpTypePointer: + // Find the storage class of the pointer to this type. + if (inst.GetSingleWordInOperand(1) == type_id) { + return spv::StorageClass(inst.GetSingleWordInOperand(0)); + } + break; + default: + break; + } + } + + // gl_PerVertex is declared, but is unused. Return either of Input or Output + // classes just so it matches one in the other module. This should be highly + // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios. + return spv::StorageClass::Output; +} + +spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to, + uint32_t set_id) { + const opt::Instruction* set_inst = GetInst(id_to, set_id); + return spvExtInstImportTypeGet(set_inst->GetInOperand(0).AsString().c_str()); +} + +spv_number_kind_t Differ::GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width) { + const opt::Operand& operand = inst.GetOperand(operand_index); + *number_bit_width = 0; + + // A very limited version of Parser::parseOperand. + switch (operand.type) { + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + // Always unsigned integers. + *number_bit_width = 32; + return SPV_NUMBER_UNSIGNED_INT; + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + // Always float. + *number_bit_width = 32; + return SPV_NUMBER_FLOATING; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + switch (inst.opcode()) { + case spv::Op::OpSwitch: + case spv::Op::OpConstant: + case spv::Op::OpSpecConstant: + // Same kind of number as the selector (OpSwitch) or the type + // (Op*Constant). + return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0), + number_bit_width); + default: + assert(false && "Unreachable"); + break; + } + break; + default: + break; + } + + return SPV_NUMBER_NONE; +} + +spv_number_kind_t Differ::GetTypeNumberKind(const IdInstructions& id_to, + uint32_t id, + uint32_t* number_bit_width) { + const opt::Instruction* type_inst = GetInst(id_to, id); + if (!spvOpcodeIsScalarType(type_inst->opcode())) { + type_inst = GetInst(id_to, type_inst->type_id()); + } + + switch (type_inst->opcode()) { + case spv::Op::OpTypeInt: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT + : SPV_NUMBER_SIGNED_INT; + break; + case spv::Op::OpTypeFloat: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return SPV_NUMBER_FLOATING; + default: + assert(false && "Unreachable"); + return SPV_NUMBER_NONE; + } +} + +void Differ::MatchCapabilities() { + MatchPreambleInstructions(src_->capabilities(), dst_->capabilities()); +} + +void Differ::MatchExtensions() { + MatchPreambleInstructions(src_->extensions(), dst_->extensions()); +} + +void Differ::MatchExtInstImportIds() { + // Bunch all of this section's ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_all = [](const opt::Instruction&) { return true; }; + + PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids, true, + accept_all, get_result_id); + PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids, false, + accept_all, get_result_id); + + // Then match the ids. + MatchIds(potential_id_map, [](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Match OpExtInstImport by exact name, which is operand 1 + const opt::Operand& src_name = src_inst->GetOperand(1); + const opt::Operand& dst_name = dst_inst->GetOperand(1); + + return src_name.AsString() == dst_name.AsString(); + }); +} +void Differ::MatchMemoryModel() { + // Always match the memory model instructions, there is always a single one of + // it. + id_map_.MapInsts(src_->GetMemoryModel(), dst_->GetMemoryModel()); +} + +void Differ::MatchEntryPointIds() { + // Match OpEntryPoint ids (at index 1) by ExecutionModel (at index 0) and + // possibly name (at index 2). OpEntryPoint doesn't produce a result id, so + // this function doesn't use the helpers the other functions use. + + // Map from execution model to OpEntryPoint instructions of that model. + using ExecutionModelMap = + std::unordered_map>; + ExecutionModelMap src_entry_points_map; + ExecutionModelMap dst_entry_points_map; + std::set all_execution_models; + + for (const opt::Instruction& src_inst : src_->entry_points()) { + uint32_t execution_model = src_inst.GetSingleWordOperand(0); + src_entry_points_map[execution_model].push_back(&src_inst); + all_execution_models.insert(execution_model); + } + for (const opt::Instruction& dst_inst : dst_->entry_points()) { + uint32_t execution_model = dst_inst.GetSingleWordOperand(0); + dst_entry_points_map[execution_model].push_back(&dst_inst); + all_execution_models.insert(execution_model); + } + + // Go through each model and match the ids. + for (const uint32_t execution_model : all_execution_models) { + auto& src_insts = src_entry_points_map[execution_model]; + auto& dst_insts = dst_entry_points_map[execution_model]; + + // If there is only one entry point in src and dst with that model, match + // them unconditionally. + if (src_insts.size() == 1 && dst_insts.size() == 1) { + uint32_t src_id = src_insts[0]->GetSingleWordOperand(1); + uint32_t dst_id = dst_insts[0]->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_insts[0], dst_insts[0]); + continue; + } + + // Otherwise match them by name. + for (const opt::Instruction* src_inst : src_insts) { + for (const opt::Instruction* dst_inst : dst_insts) { + if (id_map_.IsDstMapped(dst_inst)) continue; + + const opt::Operand& src_name = src_inst->GetOperand(2); + const opt::Operand& dst_name = dst_inst->GetOperand(2); + + if (src_name.AsString() == dst_name.AsString()) { + uint32_t src_id = src_inst->GetSingleWordOperand(1); + uint32_t dst_id = dst_inst->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_inst, dst_inst); + break; + } + } + } + } +} + +void Differ::MatchExecutionModes() { + MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes()); +} + +void Differ::MatchTypeForwardPointers() { + // Bunch all of type forward pointers as potential matches. + PotentialIdMap potential_id_map; + auto get_pointer_type_id = [](const opt::Instruction& inst) { + return inst.GetSingleWordOperand(0); + }; + auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) { + return inst.opcode() == spv::Op::OpTypeForwardPointer; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_forward_pointer_ops, get_pointer_type_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_forward_pointer_ops, get_pointer_type_id); + + // Matching types with cyclical references (i.e. in the style of linked lists) + // can get very complex. Currently, the diff tool matches types bottom up, so + // on every instruction it expects to know if its operands are already matched + // or not. With cyclical references, it cannot know that. Type matching may + // need significant modifications to be able to support this use case. + // + // Currently, forwarded types are only matched by storage class and debug + // info, with minimal matching of the type being forwarded: + // + // - Group by class + // - Group by OpType being pointed to + // - Group by debug info + // - If same name and unique, match + // - If leftover is unique, match + + // Group forwarded pointers by storage class first and loop over them. + GroupIdsAndMatch( + potential_id_map.src_ids, potential_id_map.dst_ids, + spv::StorageClass::Max, &Differ::GroupIdsHelperGetTypePointerStorageClass, + [this](const IdGroup& src_group_by_storage_class, + const IdGroup& dst_group_by_storage_class) { + // Group them further by the type they are pointing to and loop over + // them. + GroupIdsAndMatch( + src_group_by_storage_class, dst_group_by_storage_class, + spv::Op::Max, &Differ::GroupIdsHelperGetTypePointerTypeOp, + [this](const IdGroup& src_group_by_type_op, + const IdGroup& dst_group_by_type_op) { + // Group them even further by debug info, if possible and match by + // debug name. + MatchTypeForwardPointersByName(src_group_by_type_op, + dst_group_by_type_op); + + // Match the leftovers only if they lack debug info and there is + // only one instance of them. + MatchTypeForwardPointersByTypeOp(src_group_by_type_op, + dst_group_by_type_op); + }); + }); + + // Match the instructions that forward declare the same type themselves + for (uint32_t src_id : potential_id_map.src_ids) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (dst_id == 0) continue; + + const opt::Instruction* src_forward_inst = + GetForwardPointerInst(src_id_to_, src_id); + const opt::Instruction* dst_forward_inst = + GetForwardPointerInst(dst_id_to_, dst_id); + + assert(src_forward_inst); + assert(dst_forward_inst); + + id_map_.MapInsts(src_forward_inst, dst_forward_inst); + } +} + +void Differ::MatchTypeIds() { + // Bunch all of type ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeGeneratesType(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two block types will be matched if they differ only in a few of + // the fields. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const spv::Op src_op = src_inst->opcode(); + const spv::Op dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case spv::Op::OpTypeVoid: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeAccelerationStructureNV: + case spv::Op::OpTypeRayQueryKHR: + // the above types have no operands and are unique, match them. + return true; + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypePointer: + // Match these instructions when all operands match. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case spv::Op::OpTypeFunction: + case spv::Op::OpTypeImage: + // Match function types only if they have the same number of operands, + // and they all match. + // Match image types similarly, expecting the optional final parameter + // to match (if provided in both) + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case spv::Op::OpTypeArray: + // Match arrays only if the element type and length match. The length + // is an id of a constant, so the actual constant it's defining is + // compared instead. + if (!DoOperandsMatch(src_inst, dst_inst, 0, 1)) { + return false; + } + + if (AreIdenticalUintConstants(src_inst->GetSingleWordInOperand(1), + dst_inst->GetSingleWordInOperand(1))) { + return true; + } + + // If size is not OpConstant, expect the ids to match exactly (for + // example if a spec contant is used). + return DoOperandsMatch(src_inst, dst_inst, 1, 1); + + case spv::Op::OpTypeStruct: + return MatchOpTypeStruct(src_inst, dst_inst, flexibility); + + default: + return false; + } + }); + } +} + +void Differ::MatchConstants() { + // Bunch all of constant ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeIsConstant(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Constants are matched exactly, except for float types + // that are first matched exactly, then leftovers are matched with a small + // error. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const spv::Op src_op = src_inst->opcode(); + const spv::Op dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + // true and false are unique, match them. + return true; + case spv::Op::OpConstant: + return MatchOpConstant(src_inst, dst_inst, flexibility); + case spv::Op::OpConstantComposite: + case spv::Op::OpSpecConstantComposite: + // Composite constants must match in type and value. + // + // TODO: match OpConstantNull with OpConstantComposite with all zeros + // at flexibility == 1 + // TODO: match constants from structs that have been flexibly-matched. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)) && + DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case spv::Op::OpConstantSampler: + // Match sampler constants exactly. + // TODO: Allow flexibility in parameters to better diff shaders where + // the sampler param has changed. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case spv::Op::OpConstantNull: + // Match null constants as long as the type matches. + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)); + + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstant: + case spv::Op::OpSpecConstantOp: + // Match spec constants by name if available, then by the SpecId + // decoration. + return MatchOpSpecConstant(src_inst, dst_inst); + + default: + return false; + } + }); + } +} + +void Differ::MatchVariableIds() { + // Bunch all of variable ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return inst.opcode() == spv::Op::OpVariable; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two otherwise identical variables will be matched if one of + // them has a Private storage class and the other doesn't. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, + [this, flexibility](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->opcode() == spv::Op::OpVariable); + assert(dst_inst->opcode() == spv::Op::OpVariable); + + return MatchOpVariable(src_inst, dst_inst, flexibility); + }); + } +} + +void Differ::MatchFunctions() { + IdGroup src_func_ids; + IdGroup dst_func_ids; + + for (const auto& func : src_funcs_) { + src_func_ids.push_back(func.first); + } + for (const auto& func : dst_funcs_) { + dst_func_ids.push_back(func.first); + } + + // Base the matching of functions on debug info when available. + GroupIdsAndMatch( + src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + // If there is a single function with this name in src and dst, it's a + // definite match. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + return; + } + + // If there are multiple functions with the same name, group them by + // type, and match only if the types match (and are unique). + GroupIdsAndMatchByMappedId(src_group, dst_group, + &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + if (src_group_by_type_id.size() == 1 && + dst_group_by_type_id.size() == 1) { + id_map_.MapIds(src_group_by_type_id[0], + dst_group_by_type_id[0]); + } + }); + }); + + // Any functions that are left are pooled together and matched as if unnamed, + // with the only exception that two functions with mismatching names are not + // matched. + // + // Before that however, the diff of the functions that are matched are taken + // and processed, so that more of the global variables can be matched before + // attempting to match the rest of the functions. They can contribute to the + // precision of the diff of those functions. + for (const uint32_t src_func_id : src_func_ids) { + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_id == 0) { + continue; + } + + // Since these functions are definite matches, match their parameters for a + // better diff. + MatchFunctionParamIds(src_funcs_[src_func_id], dst_funcs_[dst_func_id]); + + // Take the diff of the two functions. + DiffMatch src_match_result, dst_match_result; + MatchFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], &src_match_result, + &dst_match_result); + + // Match ids between the two function bodies; which can also result in + // global variables getting matched. + MatchIdsInFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], src_match_result, + dst_match_result, 0); + } + + // Best effort match functions with matching return and argument types. + GroupIdsAndMatchByMappedId( + src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetFunctionTypeId, + [this](const IdGroup& src_group_by_func_type_id, + const IdGroup& dst_group_by_func_type_id) { + BestEffortMatchFunctions(src_group_by_func_type_id, + dst_group_by_func_type_id, src_func_insts_, + dst_func_insts_); + }); + + // Best effort match functions with matching return types. + GroupIdsAndMatchByMappedId( + src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id, + src_func_insts_, dst_func_insts_); + }); + + // Any function that's left, best effort match them. + BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_, + dst_func_insts_); +} + +void Differ::MatchDebugs1() { + // This section in cludes: OpString, OpSourceExtension, OpSource, + // OpSourceContinued + MatchDebugAndAnnotationInstructions(src_->debugs1(), dst_->debugs1()); +} + +void Differ::MatchDebugs2() { + // This section includes: OpName, OpMemberName + MatchDebugAndAnnotationInstructions(src_->debugs2(), dst_->debugs2()); +} + +void Differ::MatchDebugs3() { + // This section includes: OpModuleProcessed + MatchDebugAndAnnotationInstructions(src_->debugs3(), dst_->debugs3()); +} + +void Differ::MatchExtInstDebugInfo() { + // This section includes OpExtInst for DebugInfo extension + MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(), + dst_->ext_inst_debuginfo()); +} + +void Differ::MatchAnnotations() { + // This section includes OpDecorate and family. + MatchDebugAndAnnotationInstructions(src_->annotations(), dst_->annotations()); +} + +const opt::Instruction* Differ::MappedDstInst( + const opt::Instruction* src_inst) { + return MappedInstImpl(src_inst, id_map_.SrcToDstMap(), dst_id_to_); +} + +const opt::Instruction* Differ::MappedSrcInst( + const opt::Instruction* dst_inst) { + return MappedInstImpl(dst_inst, id_map_.DstToSrcMap(), src_id_to_); +} + +const opt::Instruction* Differ::MappedInstImpl( + const opt::Instruction* inst, const IdMap& to_other, + const IdInstructions& other_id_to) { + if (inst->HasResultId()) { + if (to_other.IsMapped(inst->result_id())) { + const uint32_t other_result_id = to_other.MappedId(inst->result_id()); + + assert(other_result_id < other_id_to.inst_map_.size()); + return other_id_to.inst_map_[other_result_id]; + } + + return nullptr; + } + + return to_other.MappedInst(inst); +} + +void Differ::OutputLine(std::function are_lines_identical, + std::function output_src_line, + std::function output_dst_line) { + if (are_lines_identical()) { + out_ << " "; + output_src_line(); + } else { + OutputRed(); + out_ << "-"; + output_src_line(); + + OutputGreen(); + out_ << "+"; + output_dst_line(); + + OutputResetColor(); + } +} + +const opt::Instruction* IterInst(opt::Module::const_inst_iterator& iter) { + return &*iter; +} + +const opt::Instruction* IterInst(InstructionList::const_iterator& iter) { + return *iter; +} + +template +void Differ::OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function + write_inst) { + auto src_iter = src_insts.begin(); + auto dst_iter = dst_insts.begin(); + + // - While src_inst doesn't have a match, output it with - + // - While dst_inst doesn't have a match, output it with + + // - Now src_inst and dst_inst both have matches; might not match each other! + // * If section is unordered, just process src_inst and its match (dst_inst + // or not), + // dst_inst will eventually be processed when its match is seen. + // * If section is ordered, also just process src_inst and its match. Its + // match must + // necessarily be dst_inst. + while (src_iter != src_insts.end() || dst_iter != dst_insts.end()) { + OutputRed(); + while (src_iter != src_insts.end() && + MappedDstInst(IterInst(src_iter)) == nullptr) { + out_ << "-"; + write_inst(*IterInst(src_iter), src_id_to_, *IterInst(src_iter)); + ++src_iter; + } + OutputGreen(); + while (dst_iter != dst_insts.end() && + MappedSrcInst(IterInst(dst_iter)) == nullptr) { + out_ << "+"; + write_inst(ToMappedSrcIds(*IterInst(dst_iter)), dst_id_to_, + *IterInst(dst_iter)); + ++dst_iter; + } + OutputResetColor(); + + if (src_iter != src_insts.end() && dst_iter != dst_insts.end()) { + const opt::Instruction* src_inst = IterInst(src_iter); + const opt::Instruction* matched_dst_inst = MappedDstInst(src_inst); + + assert(matched_dst_inst != nullptr); + assert(MappedSrcInst(IterInst(dst_iter)) != nullptr); + + OutputLine( + [this, src_inst, matched_dst_inst]() { + return DoInstructionsMatch(src_inst, matched_dst_inst); + }, + [this, src_inst, &write_inst]() { + write_inst(*src_inst, src_id_to_, *src_inst); + }, + [this, matched_dst_inst, &write_inst]() { + write_inst(ToMappedSrcIds(*matched_dst_inst), dst_id_to_, + *matched_dst_inst); + }); + + ++src_iter; + ++dst_iter; + } + } +} + +void Differ::ToParsedInstruction( + const opt::Instruction& inst, const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector& parsed_operands, + std::vector& inst_binary) { + inst.ToBinaryWithoutAttachedDebugInsts(&inst_binary); + parsed_operands.resize(inst.NumOperands()); + + parsed_inst->words = inst_binary.data(); + parsed_inst->num_words = static_cast(inst_binary.size()); + parsed_inst->opcode = static_cast(inst.opcode()); + parsed_inst->ext_inst_type = + inst.opcode() == spv::Op::OpExtInst + ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0)) + : SPV_EXT_INST_TYPE_NONE; + parsed_inst->type_id = + inst.HasResultType() ? inst.GetSingleWordOperand(0) : 0; + parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0; + parsed_inst->operands = parsed_operands.data(); + parsed_inst->num_operands = static_cast(parsed_operands.size()); + + // Word 0 is always op and num_words, so operands start at offset 1. + uint32_t offset = 1; + for (uint16_t operand_index = 0; operand_index < parsed_inst->num_operands; + ++operand_index) { + const opt::Operand& operand = inst.GetOperand(operand_index); + spv_parsed_operand_t& parsed_operand = parsed_operands[operand_index]; + + parsed_operand.offset = static_cast(offset); + parsed_operand.num_words = static_cast(operand.words.size()); + parsed_operand.type = operand.type; + parsed_operand.number_kind = GetNumberKind( + id_to, original_inst, operand_index, &parsed_operand.number_bit_width); + + offset += parsed_operand.num_words; + } +} + +opt::Instruction Differ::ToMappedSrcIds(const opt::Instruction& dst_inst) { + // Create an identical instruction to dst_inst, except ids are changed to the + // mapped one. + opt::Instruction mapped_inst = dst_inst; + + for (uint32_t operand_index = 0; operand_index < mapped_inst.NumOperands(); + ++operand_index) { + opt::Operand& operand = mapped_inst.GetOperand(operand_index); + + if (spvIsIdType(operand.type)) { + assert(id_map_.IsDstMapped(operand.AsId())); + operand.words[0] = id_map_.MappedSrcId(operand.AsId()); + } + } + + return mapped_inst; +} + +spv_result_t Differ::Output() { + id_map_.MapUnmatchedIds( + [this](uint32_t src_id) { return src_id_to_.IsDefined(src_id); }, + [this](uint32_t dst_id) { return dst_id_to_.IsDefined(dst_id); }); + src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr); + dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr); + + const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; + spv_opcode_table opcode_table; + spv_operand_table operand_table; + spv_ext_inst_table ext_inst_table; + spv_result_t result; + + result = spvOpcodeTableGet(&opcode_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvOperandTableGet(&operand_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvExtInstTableGet(&ext_inst_table, target_env); + if (result != SPV_SUCCESS) return result; + + spv_context_t context{ + target_env, + opcode_table, + operand_table, + ext_inst_table, + }; + + const AssemblyGrammar grammar(&context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT; + if (options_.indent) { + disassembly_options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; + } + + NameMapper name_mapper = GetTrivialNameMapper(); + disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options, + name_mapper); + + if (!options_.no_header) { + // Output the header + // TODO: when using diff with text, the assembler overrides the version and + // generator, so these aren't reflected correctly in the output. Could + // potentially extract this info from the header comment. + OutputLine([]() { return true; }, [&dis]() { dis.EmitHeaderSpirv(); }, + []() { assert(false && "Unreachable"); }); + OutputLine([this]() { return src_->version() == dst_->version(); }, + [this, &dis]() { dis.EmitHeaderVersion(src_->version()); }, + [this, &dis]() { dis.EmitHeaderVersion(dst_->version()); }); + OutputLine([this]() { return src_->generator() == dst_->generator(); }, + [this, &dis]() { dis.EmitHeaderGenerator(src_->generator()); }, + [this, &dis]() { dis.EmitHeaderGenerator(dst_->generator()); }); + OutputLine( + [this]() { return src_->IdBound() == id_map_.SrcToDstMap().IdBound(); }, + [this, &dis]() { dis.EmitHeaderIdBound(src_->IdBound()); }, + [this, &dis]() { + dis.EmitHeaderIdBound(id_map_.SrcToDstMap().IdBound()); + }); + OutputLine([this]() { return src_->schema() == dst_->schema(); }, + [this, &dis]() { dis.EmitHeaderSchema(src_->schema()); }, + [this, &dis]() { dis.EmitHeaderSchema(dst_->schema()); }); + } + + // For each section, iterate both modules and output the disassembly. + auto write_inst = [this, &dis](const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst) { + spv_parsed_instruction_t parsed_inst; + std::vector parsed_operands; + std::vector inst_binary; + + ToParsedInstruction(inst, id_to, original_inst, &parsed_inst, + parsed_operands, inst_binary); + + dis.EmitInstruction(parsed_inst, 0); + }; + + OutputSection(src_->capabilities(), dst_->capabilities(), write_inst); + OutputSection(src_->extensions(), dst_->extensions(), write_inst); + OutputSection(src_->ext_inst_imports(), dst_->ext_inst_imports(), write_inst); + + // There is only one memory model. + OutputLine( + [this]() { + return DoInstructionsMatch(src_->GetMemoryModel(), + dst_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*src_->GetMemoryModel(), src_id_to_, + *src_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*dst_->GetMemoryModel(), dst_id_to_, + *dst_->GetMemoryModel()); + }); + + OutputSection(src_->entry_points(), dst_->entry_points(), write_inst); + OutputSection(src_->execution_modes(), dst_->execution_modes(), write_inst); + OutputSection(src_->debugs1(), dst_->debugs1(), write_inst); + OutputSection(src_->debugs2(), dst_->debugs2(), write_inst); + OutputSection(src_->debugs3(), dst_->debugs3(), write_inst); + OutputSection(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo(), + write_inst); + OutputSection(src_->annotations(), dst_->annotations(), write_inst); + OutputSection(src_->types_values(), dst_->types_values(), write_inst); + + // Get the body of all the functions. + FunctionInstMap src_func_header_insts; + FunctionInstMap dst_func_header_insts; + + GetFunctionHeaderInstructions(src_, &src_func_header_insts); + GetFunctionHeaderInstructions(dst_, &dst_func_header_insts); + + for (const auto& src_func : src_func_insts_) { + const uint32_t src_func_id = src_func.first; + const InstructionList& src_insts = src_func.second; + const InstructionList& src_header_insts = + src_func_header_insts[src_func_id]; + + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_insts_.find(dst_func_id) == dst_func_insts_.end()) { + OutputSection(src_header_insts, InstructionList(), write_inst); + OutputSection(src_insts, InstructionList(), write_inst); + continue; + } + + const InstructionList& dst_insts = dst_func_insts_[dst_func_id]; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + OutputSection(src_header_insts, dst_header_insts, write_inst); + OutputSection(src_insts, dst_insts, write_inst); + } + + for (const auto& dst_func : dst_func_insts_) { + const uint32_t dst_func_id = dst_func.first; + const InstructionList& dst_insts = dst_func.second; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + + const uint32_t src_func_id = id_map_.MappedSrcId(dst_func_id); + if (src_func_insts_.find(src_func_id) == src_func_insts_.end()) { + OutputSection(InstructionList(), dst_header_insts, write_inst); + OutputSection(InstructionList(), dst_insts, write_inst); + } + } + + out_ << std::flush; + + return SPV_SUCCESS; +} + +} // anonymous namespace + +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) { + // High level algorithm: + // + // - Some sections of SPIR-V don't deal with ids; instructions in those + // sections are matched identically. For example OpCapability instructions. + // - Some sections produce ids, and they can be trivially matched by their + // parameters. For example OpExtInstImport instructions. + // - Some sections annotate ids. These are matched at the end, after the ids + // themselves are matched. For example OpName or OpDecorate instructions. + // - Some sections produce ids that depend on other ids and they can be + // recursively matched. For example OpType* instructions. + // - Some sections produce ids that are not trivially matched. For these ids, + // the debug info is used when possible, or a best guess (such as through + // decorations) is used. For example OpVariable instructions. + // - Matching functions is done with multiple attempts: + // * Functions with identical debug names are matched if there are no + // overloads. + // * Otherwise, functions with identical debug names and types are matched. + // * The rest of the functions are best-effort matched, first in groups of + // identical type, then any with any. + // * The best-effort matching takes the diff of every pair of functions in + // a group and selects the top matches that also meet a similarity + // index. + // * Once a pair of functions are matched, the fuzzy diff of the + // instructions is used to match the instructions in the function body. + // The fuzzy diff makes sure that sufficiently similar instructions are + // matched and that yet-to-be-matched result ids don't result in a larger + // diff. + // + // Once the instructions are matched between the src and dst SPIR-V, the src + // is traversed and its disassembly is output. In the process, any unmatched + // instruction is prefixed with -, and any unmatched instruction in dst in the + // same section is output prefixed with +. To avoid confusion, the + // instructions in dst are output with matching ids in src so the output + // assembly is consistent. + + Differ differ(src, dst, out, options); + + // First, match instructions between the different non-annotation sections of + // the SPIR-V. + differ.MatchCapabilities(); + differ.MatchExtensions(); + differ.MatchExtInstImportIds(); + differ.MatchMemoryModel(); + differ.MatchEntryPointIds(); + differ.MatchExecutionModes(); + differ.MatchTypeForwardPointers(); + differ.MatchTypeIds(); + differ.MatchConstants(); + differ.MatchVariableIds(); + differ.MatchFunctions(); + + // Match instructions that annotate previously-matched ids. + differ.MatchDebugs1(); + differ.MatchDebugs2(); + differ.MatchDebugs3(); + differ.MatchExtInstDebugInfo(); + differ.MatchAnnotations(); + + // Show the disassembly with the diff. + // + // TODO: Based on an option, output either based on src or dst, i.e. the diff + // can show the ids and instruction/function order either from src or dst. + spv_result_t result = differ.Output(); + + differ.DumpIdMap(); + + return result; +} + +} // namespace diff +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/diff/diff.h b/engine/engine/3rdlibs/spirv-tools/source/diff/diff.h new file mode 100644 index 00000000..932de9ee --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diff/diff.h @@ -0,0 +1,48 @@ +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DIFF_DIFF_H_ +#define SOURCE_DIFF_DIFF_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace diff { + +struct Options { + bool ignore_set_binding = false; + bool ignore_location = false; + bool indent = false; + bool no_header = false; + bool color_output = false; + bool dump_id_map = false; +}; + +// Given two SPIR-V modules, this function outputs the textual diff of their +// assembly in `out`. The diff is *semantic*, so that the ordering of certain +// instructions wouldn't matter. +// +// The output is a disassembly of src, with diff(1)-style + and - lines that +// show how the src is changed into dst. To make this disassembly +// self-consistent, the ids that are output are all in the space of the src +// module; e.g. any + lines (showing instructions from the dst module) have +// their ids mapped to the matched instruction in the src module (or a new id +// allocated in the src module if unmatched). +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options); + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_DIFF_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/diff/lcs.h b/engine/engine/3rdlibs/spirv-tools/source/diff/lcs.h new file mode 100644 index 00000000..6c00e864 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/diff/lcs.h @@ -0,0 +1,224 @@ +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DIFF_LCS_H_ +#define SOURCE_DIFF_LCS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace spvtools { +namespace diff { + +// The result of a diff. +using DiffMatch = std::vector; + +// Helper class to find the longest common subsequence between two function +// bodies. +template +class LongestCommonSubsequence { + public: + LongestCommonSubsequence(const Sequence& src, const Sequence& dst) + : src_(src), + dst_(dst), + table_(src.size(), std::vector(dst.size())) {} + + // Given two sequences, it creates a matching between them. The elements are + // simply marked as matched in src and dst, with any unmatched element in src + // implying a removal and any unmatched element in dst implying an addition. + // + // Returns the length of the longest common subsequence. + template + uint32_t Get(std::function match, + DiffMatch* src_match_result, DiffMatch* dst_match_result); + + private: + struct DiffMatchIndex { + uint32_t src_offset; + uint32_t dst_offset; + }; + + template + void CalculateLCS(std::function match); + void RetrieveMatch(DiffMatch* src_match_result, DiffMatch* dst_match_result); + bool IsInBound(DiffMatchIndex index) { + return index.src_offset < src_.size() && index.dst_offset < dst_.size(); + } + bool IsCalculated(DiffMatchIndex index) { + assert(IsInBound(index)); + return table_[index.src_offset][index.dst_offset].valid; + } + bool IsCalculatedOrOutOfBound(DiffMatchIndex index) { + return !IsInBound(index) || IsCalculated(index); + } + uint32_t GetMemoizedLength(DiffMatchIndex index) { + if (!IsInBound(index)) { + return 0; + } + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].best_match_length; + } + bool IsMatched(DiffMatchIndex index) { + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].matched; + } + void MarkMatched(DiffMatchIndex index, uint32_t best_match_length, + bool matched) { + assert(IsInBound(index)); + DiffMatchEntry& entry = table_[index.src_offset][index.dst_offset]; + assert(!entry.valid); + + entry.best_match_length = best_match_length & 0x3FFFFFFF; + assert(entry.best_match_length == best_match_length); + entry.matched = matched; + entry.valid = true; + } + + const Sequence& src_; + const Sequence& dst_; + + struct DiffMatchEntry { + DiffMatchEntry() : best_match_length(0), matched(false), valid(false) {} + + uint32_t best_match_length : 30; + // Whether src[i] and dst[j] matched. This is an optimization to avoid + // calling the `match` function again when walking the LCS table. + uint32_t matched : 1; + // Use for the recursive algorithm to know if the contents of this entry are + // valid. + uint32_t valid : 1; + }; + + std::vector> table_; +}; + +template +template +uint32_t LongestCommonSubsequence::Get( + std::function match, + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + CalculateLCS(match); + RetrieveMatch(src_match_result, dst_match_result); + return GetMemoizedLength({0, 0}); +} + +template +template +void LongestCommonSubsequence::CalculateLCS( + std::function match) { + // The LCS algorithm is simple. Given sequences s and d, with a:b depicting a + // range in python syntax: + // + // lcs(s[i:], d[j:]) = + // lcs(s[i+1:], d[j+1:]) + 1 if s[i] == d[j] + // max(lcs(s[i+1:], d[j:]), lcs(s[i:], d[j+1:])) o.w. + // + // Once the LCS table is filled according to the above, it can be walked and + // the best match retrieved. + // + // This is a recursive function with memoization, which avoids filling table + // entries where unnecessary. This makes the best case O(N) instead of + // O(N^2). The implemention uses a std::stack to avoid stack overflow on long + // sequences. + + if (src_.empty() || dst_.empty()) { + return; + } + + std::stack to_calculate; + to_calculate.push({0, 0}); + + while (!to_calculate.empty()) { + DiffMatchIndex current = to_calculate.top(); + to_calculate.pop(); + assert(IsInBound(current)); + + // If already calculated through another path, ignore it. + if (IsCalculated(current)) { + continue; + } + + if (match(src_[current.src_offset], dst_[current.dst_offset])) { + // If the current elements match, advance both indices and calculate the + // LCS if not already. Visit `current` again afterwards, so its + // corresponding entry will be updated. + DiffMatchIndex next = {current.src_offset + 1, current.dst_offset + 1}; + if (IsCalculatedOrOutOfBound(next)) { + MarkMatched(current, GetMemoizedLength(next) + 1, true); + } else { + to_calculate.push(current); + to_calculate.push(next); + } + continue; + } + + // We've reached a pair of elements that don't match. Calculate the LCS for + // both cases of either being left unmatched and take the max. Visit + // `current` again afterwards, so its corresponding entry will be updated. + DiffMatchIndex next_src = {current.src_offset + 1, current.dst_offset}; + DiffMatchIndex next_dst = {current.src_offset, current.dst_offset + 1}; + + if (IsCalculatedOrOutOfBound(next_src) && + IsCalculatedOrOutOfBound(next_dst)) { + uint32_t best_match_length = + std::max(GetMemoizedLength(next_src), GetMemoizedLength(next_dst)); + MarkMatched(current, best_match_length, false); + continue; + } + + to_calculate.push(current); + if (!IsCalculatedOrOutOfBound(next_src)) { + to_calculate.push(next_src); + } + if (!IsCalculatedOrOutOfBound(next_dst)) { + to_calculate.push(next_dst); + } + } +} + +template +void LongestCommonSubsequence::RetrieveMatch( + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + src_match_result->clear(); + dst_match_result->clear(); + + src_match_result->resize(src_.size(), false); + dst_match_result->resize(dst_.size(), false); + + DiffMatchIndex current = {0, 0}; + while (IsInBound(current)) { + if (IsMatched(current)) { + (*src_match_result)[current.src_offset++] = true; + (*dst_match_result)[current.dst_offset++] = true; + continue; + } + + if (GetMemoizedLength({current.src_offset + 1, current.dst_offset}) >= + GetMemoizedLength({current.src_offset, current.dst_offset + 1})) { + ++current.src_offset; + } else { + ++current.dst_offset; + } + } +} + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_LCS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/disassemble.cpp b/engine/engine/3rdlibs/spirv-tools/source/disassemble.cpp new file mode 100644 index 00000000..93791a0c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/disassemble.cpp @@ -0,0 +1,1111 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains a disassembler: It converts a SPIR-V binary +// to text. + +#include "source/disassemble.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/ext_inst.h" +#include "source/opcode.h" +#include "source/parsed_operand.h" +#include "source/print.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace { + +// Indices to ControlFlowGraph's list of blocks from one block to its successors +struct BlockSuccessors { + // Merge block in OpLoopMerge and OpSelectionMerge + uint32_t merge_block_id = 0; + // The continue block in OpLoopMerge + uint32_t continue_block_id = 0; + // The true and false blocks in OpBranchConditional + uint32_t true_block_id = 0; + uint32_t false_block_id = 0; + // The body block of a loop, as specified by OpBranch after a merge + // instruction + uint32_t body_block_id = 0; + // The same-nesting-level block that follows this one, indicated by an + // OpBranch with no merge instruction. + uint32_t next_block_id = 0; + // The cases (including default) of an OpSwitch + std::vector case_block_ids; +}; + +class ParsedInstruction { + public: + ParsedInstruction(const spv_parsed_instruction_t* instruction) { + // Make a copy of the parsed instruction, including stable memory for its + // operands. + instruction_ = *instruction; + operands_ = + std::make_unique(instruction->num_operands); + memcpy(operands_.get(), instruction->operands, + instruction->num_operands * sizeof(*instruction->operands)); + instruction_.operands = operands_.get(); + } + + const spv_parsed_instruction_t* get() const { return &instruction_; } + + private: + spv_parsed_instruction_t instruction_; + std::unique_ptr operands_; +}; + +// One block in the CFG +struct SingleBlock { + // The byte offset in the SPIR-V where the block starts. Used for printing in + // a comment. + size_t byte_offset; + + // Block instructions + std::vector instructions; + + // Successors of this block + BlockSuccessors successors; + + // The nesting level for this block. + uint32_t nest_level = 0; + bool nest_level_assigned = false; + + // Whether the block was reachable + bool reachable = false; +}; + +// CFG for one function +struct ControlFlowGraph { + std::vector blocks; +}; + +// A Disassembler instance converts a SPIR-V binary to its assembly +// representation. +class Disassembler { + public: + Disassembler(const AssemblyGrammar& grammar, uint32_t options, + NameMapper name_mapper) + : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + nested_indent_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT, options)), + reorder_blocks_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS, options)), + text_(), + out_(print_ ? out_stream() : out_stream(text_)), + instruction_disassembler_(grammar, out_.get(), options, name_mapper), + header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), + byte_offset_(0) {} + + // Emits the assembly header for the module, and sets up internal state + // so subsequent callbacks can handle the cases where the entire module + // is either big-endian or little-endian. + spv_result_t HandleHeader(spv_endianness_t endian, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema); + // Emits the assembly text for the given instruction. + spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst); + + // If not printing, populates text_result with the accumulated text. + // Returns SPV_SUCCESS on success. + spv_result_t SaveTextResult(spv_text* text_result) const; + + private: + void EmitCFG(); + + const bool print_; // Should we also print to the standard output stream? + const bool nested_indent_; // Should the blocks be indented according to the + // control flow structure? + const bool + reorder_blocks_; // Should the blocks be reordered for readability? + spv_endianness_t endian_; // The detected endianness of the binary. + std::stringstream text_; // Captures the text, if not printing. + out_stream out_; // The Output stream. Either to text_ or standard output. + disassemble::InstructionDisassembler instruction_disassembler_; + const bool header_; // Should we output header as the leading comment? + size_t byte_offset_; // The number of bytes processed so far. + bool inserted_decoration_space_ = false; + bool inserted_debug_space_ = false; + bool inserted_type_space_ = false; + + // The CFG for the current function + ControlFlowGraph current_function_cfg_; +}; + +spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t schema) { + endian_ = endian; + + if (header_) { + instruction_disassembler_.EmitHeaderSpirv(); + instruction_disassembler_.EmitHeaderVersion(version); + instruction_disassembler_.EmitHeaderGenerator(generator); + instruction_disassembler_.EmitHeaderIdBound(id_bound); + instruction_disassembler_.EmitHeaderSchema(schema); + } + + byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t); + + return SPV_SUCCESS; +} + +spv_result_t Disassembler::HandleInstruction( + const spv_parsed_instruction_t& inst) { + instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_, + inserted_debug_space_, + inserted_type_space_); + + // When nesting needs to be calculated or when the blocks are reordered, we + // have to have the full picture of the CFG first. Defer processing of the + // instructions until the entire function is visited. This is not done + // without those options (even if simpler) to improve debuggability; for + // example to be able to see whatever is parsed so far even if there is a + // parse error. + if (nested_indent_ || reorder_blocks_) { + switch (static_cast(inst.opcode)) { + case spv::Op::OpLabel: { + // Add a new block to the CFG + SingleBlock new_block; + new_block.byte_offset = byte_offset_; + new_block.instructions.emplace_back(&inst); + current_function_cfg_.blocks.push_back(std::move(new_block)); + break; + } + case spv::Op::OpFunctionEnd: + // Process the CFG and output the instructions + EmitCFG(); + // Output OpFunctionEnd itself too + [[fallthrough]]; + default: + if (!current_function_cfg_.blocks.empty()) { + // If in a function, stash the instruction for later. + current_function_cfg_.blocks.back().instructions.emplace_back(&inst); + } else { + // Otherwise emit the instruction right away. + instruction_disassembler_.EmitInstruction(inst, byte_offset_); + } + break; + } + } else { + instruction_disassembler_.EmitInstruction(inst, byte_offset_); + } + + byte_offset_ += inst.num_words * sizeof(uint32_t); + + return SPV_SUCCESS; +} + +// Helper to get the operand of an instruction as an id. +uint32_t GetOperand(const spv_parsed_instruction_t* instruction, + uint32_t operand) { + return instruction->words[instruction->operands[operand].offset]; +} + +std::unordered_map BuildControlFlowGraph( + ControlFlowGraph& cfg) { + std::unordered_map id_to_index; + + for (size_t index = 0; index < cfg.blocks.size(); ++index) { + SingleBlock& block = cfg.blocks[index]; + + // For future use, build the ID->index map + assert(static_cast(block.instructions[0].get()->opcode) == + spv::Op::OpLabel); + const uint32_t id = block.instructions[0].get()->result_id; + + id_to_index[id] = static_cast(index); + + // Look for a merge instruction first. The function of OpBranch depends on + // that. + if (block.instructions.size() >= 3) { + const spv_parsed_instruction_t* maybe_merge = + block.instructions[block.instructions.size() - 2].get(); + + switch (static_cast(maybe_merge->opcode)) { + case spv::Op::OpLoopMerge: + block.successors.merge_block_id = GetOperand(maybe_merge, 0); + block.successors.continue_block_id = GetOperand(maybe_merge, 1); + break; + + case spv::Op::OpSelectionMerge: + block.successors.merge_block_id = GetOperand(maybe_merge, 0); + break; + + default: + break; + } + } + + // Then look at the last instruction; it must be a branch + assert(block.instructions.size() >= 2); + + const spv_parsed_instruction_t* branch = block.instructions.back().get(); + switch (static_cast(branch->opcode)) { + case spv::Op::OpBranch: + if (block.successors.merge_block_id != 0) { + block.successors.body_block_id = GetOperand(branch, 0); + } else { + block.successors.next_block_id = GetOperand(branch, 0); + } + break; + + case spv::Op::OpBranchConditional: + block.successors.true_block_id = GetOperand(branch, 1); + block.successors.false_block_id = GetOperand(branch, 2); + break; + + case spv::Op::OpSwitch: + for (uint32_t case_index = 1; case_index < branch->num_operands; + case_index += 2) { + block.successors.case_block_ids.push_back( + GetOperand(branch, case_index)); + } + break; + + default: + break; + } + } + + return id_to_index; +} + +// Helper to deal with nesting and non-existing ids / previously-assigned +// levels. It assigns a given nesting level `level` to the block identified by +// `id` (unless that block already has a nesting level assigned). +void Nest(ControlFlowGraph& cfg, + const std::unordered_map& id_to_index, + uint32_t id, uint32_t level) { + if (id == 0) { + return; + } + + const uint32_t block_index = id_to_index.at(id); + SingleBlock& block = cfg.blocks[block_index]; + + if (!block.nest_level_assigned) { + block.nest_level = level; + block.nest_level_assigned = true; + } +} + +// For a given block, assign nesting level to its successors. +void NestSuccessors(ControlFlowGraph& cfg, const SingleBlock& block, + const std::unordered_map& id_to_index) { + assert(block.nest_level_assigned); + + // Nest loops as such: + // + // %loop = OpLabel + // OpLoopMerge %merge %cont ... + // OpBranch %body + // %body = OpLabel + // Op... + // %cont = OpLabel + // Op... + // %merge = OpLabel + // Op... + // + // Nest conditional branches as such: + // + // %header = OpLabel + // OpSelectionMerge %merge ... + // OpBranchConditional ... %true %false + // %true = OpLabel + // Op... + // %false = OpLabel + // Op... + // %merge = OpLabel + // Op... + // + // Nest switch/case as such: + // + // %header = OpLabel + // OpSelectionMerge %merge ... + // OpSwitch ... %default ... %case0 ... %case1 ... + // %default = OpLabel + // Op... + // %case0 = OpLabel + // Op... + // %case1 = OpLabel + // Op... + // ... + // %merge = OpLabel + // Op... + // + // The following can be observed: + // + // - In all cases, the merge block has the same nesting as this block + // - The continue block of loops is nested 1 level deeper + // - The body/branches/cases are nested 2 levels deeper + // + // Back branches to the header block, branches to the merge block, etc + // are correctly handled by processing the header block first (that is + // _this_ block, already processed), then following the above rules + // (in the same order) for any block that is not already processed. + Nest(cfg, id_to_index, block.successors.merge_block_id, block.nest_level); + Nest(cfg, id_to_index, block.successors.continue_block_id, + block.nest_level + 1); + Nest(cfg, id_to_index, block.successors.true_block_id, block.nest_level + 2); + Nest(cfg, id_to_index, block.successors.false_block_id, block.nest_level + 2); + Nest(cfg, id_to_index, block.successors.body_block_id, block.nest_level + 2); + Nest(cfg, id_to_index, block.successors.next_block_id, block.nest_level); + for (uint32_t case_block_id : block.successors.case_block_ids) { + Nest(cfg, id_to_index, case_block_id, block.nest_level + 2); + } +} + +struct StackEntry { + // The index of the block (in ControlFlowGraph::blocks) to process. + uint32_t block_index; + // Whether this is the pre or post visit of the block. Because a post-visit + // traversal is needed, the same block is pushed back on the stack on + // pre-visit so it can be visited again on post-visit. + bool post_visit = false; +}; + +// Helper to deal with DFS traversal and non-existing ids +void VisitSuccesor(std::stack* dfs_stack, + const std::unordered_map& id_to_index, + uint32_t id) { + if (id != 0) { + dfs_stack->push({id_to_index.at(id), false}); + } +} + +// Given the control flow graph, calculates and returns the reverse post-order +// ordering of the blocks. The blocks are then disassembled in that order for +// readability. +std::vector OrderBlocks( + ControlFlowGraph& cfg, + const std::unordered_map& id_to_index) { + std::vector post_order; + + // Nest level of a function's first block is 0. + cfg.blocks[0].nest_level = 0; + cfg.blocks[0].nest_level_assigned = true; + + // Stack of block indices as they are visited. + std::stack dfs_stack; + dfs_stack.push({0, false}); + + std::set visited; + + while (!dfs_stack.empty()) { + const uint32_t block_index = dfs_stack.top().block_index; + const bool post_visit = dfs_stack.top().post_visit; + dfs_stack.pop(); + + // If this is the second time the block is visited, that's the post-order + // visit. + if (post_visit) { + post_order.push_back(block_index); + continue; + } + + // If already visited, another path got to it first (like a case + // fallthrough), avoid reprocessing it. + if (visited.count(block_index) > 0) { + continue; + } + visited.insert(block_index); + + // Push it back in the stack for post-order visit + dfs_stack.push({block_index, true}); + + SingleBlock& block = cfg.blocks[block_index]; + + // Assign nest levels of successors right away. The successors are either + // nested under this block, or are back or forward edges to blocks outside + // this nesting level (no farther than the merge block), whose nesting + // levels are already assigned before this block is visited. + NestSuccessors(cfg, block, id_to_index); + block.reachable = true; + + // The post-order visit yields the order in which the blocks are naturally + // ordered _backwards_. So blocks to be ordered last should be visited + // first. In other words, they should be pushed to the DFS stack last. + VisitSuccesor(&dfs_stack, id_to_index, block.successors.true_block_id); + VisitSuccesor(&dfs_stack, id_to_index, block.successors.false_block_id); + VisitSuccesor(&dfs_stack, id_to_index, block.successors.body_block_id); + VisitSuccesor(&dfs_stack, id_to_index, block.successors.next_block_id); + for (uint32_t case_block_id : block.successors.case_block_ids) { + VisitSuccesor(&dfs_stack, id_to_index, case_block_id); + } + VisitSuccesor(&dfs_stack, id_to_index, block.successors.continue_block_id); + VisitSuccesor(&dfs_stack, id_to_index, block.successors.merge_block_id); + } + + std::vector order(post_order.rbegin(), post_order.rend()); + + // Finally, dump all unreachable blocks at the end + for (size_t index = 0; index < cfg.blocks.size(); ++index) { + SingleBlock& block = cfg.blocks[index]; + + if (!block.reachable) { + order.push_back(static_cast(index)); + block.nest_level = 0; + block.nest_level_assigned = true; + } + } + + return order; +} + +void Disassembler::EmitCFG() { + // Build the CFG edges. At the same time, build an ID->block index map to + // simplify building the CFG edges. + const std::unordered_map id_to_index = + BuildControlFlowGraph(current_function_cfg_); + + // Walk the CFG in reverse post-order to find the best ordering of blocks for + // presentation + std::vector block_order = + OrderBlocks(current_function_cfg_, id_to_index); + assert(block_order.size() == current_function_cfg_.blocks.size()); + + // Walk the CFG either in block order or input order based on whether the + // reorder_blocks_ option is given. + for (uint32_t index = 0; index < current_function_cfg_.blocks.size(); + ++index) { + const uint32_t block_index = reorder_blocks_ ? block_order[index] : index; + const SingleBlock& block = current_function_cfg_.blocks[block_index]; + + // Emit instructions for this block + size_t byte_offset = block.byte_offset; + assert(block.nest_level_assigned); + + for (const ParsedInstruction& inst : block.instructions) { + instruction_disassembler_.EmitInstructionInBlock(*inst.get(), byte_offset, + block.nest_level); + byte_offset += inst.get()->num_words * sizeof(uint32_t); + } + } + + current_function_cfg_.blocks.clear(); +} + +spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { + if (!print_) { + size_t length = text_.str().size(); + char* str = new char[length + 1]; + if (!str) return SPV_ERROR_OUT_OF_MEMORY; + strncpy(str, text_.str().c_str(), length + 1); + spv_text text = new spv_text_t(); + if (!text) { + delete[] str; + return SPV_ERROR_OUT_OF_MEMORY; + } + text->str = str; + text->length = length; + *text_result = text; + } + return SPV_SUCCESS; +} + +spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleHeader(endian, version, generator, id_bound, + schema); +} + +spv_result_t DisassembleInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleInstruction(*parsed_instruction); +} + +// Simple wrapper class to provide extra data necessary for targeted +// instruction disassembly. +class WrappedDisassembler { + public: + WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) + : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} + + Disassembler* disassembler() { return disassembler_; } + const uint32_t* inst_binary() const { return inst_binary_; } + size_t word_count() const { return word_count_; } + + private: + Disassembler* disassembler_; + const uint32_t* inst_binary_; + const size_t word_count_; +}; + +spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto wrapped = static_cast(user_data); + return wrapped->disassembler()->HandleHeader(endian, version, generator, + id_bound, schema); +} + +spv_result_t DisassembleTargetInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto wrapped = static_cast(user_data); + // Check if this is the instruction we want to disassemble. + if (wrapped->word_count() == parsed_instruction->num_words && + std::equal(wrapped->inst_binary(), + wrapped->inst_binary() + wrapped->word_count(), + parsed_instruction->words)) { + // Found the target instruction. Disassemble it and signal that we should + // stop searching so we don't output the same instruction again. + if (auto error = + wrapped->disassembler()->HandleInstruction(*parsed_instruction)) + return error; + return SPV_REQUESTED_TERMINATION; + } + return SPV_SUCCESS; +} + +uint32_t GetLineLengthWithoutColor(const std::string line) { + // Currently, every added color is in the form \x1b...m, so instead of doing a + // lot of string comparisons with spvtools::clr::* strings, we just ignore + // those ranges. + uint32_t length = 0; + for (size_t i = 0; i < line.size(); ++i) { + if (line[i] == '\x1b') { + do { + ++i; + } while (i < line.size() && line[i] != 'm'); + continue; + } + + ++length; + } + + return length; +} + +constexpr int kStandardIndent = 15; +constexpr int kBlockNestIndent = 2; +constexpr int kBlockBodyIndentOffset = 2; +constexpr uint32_t kCommentColumn = 50; +} // namespace + +namespace disassemble { +InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar, + std::ostream& stream, + uint32_t options, + NameMapper name_mapper) + : grammar_(grammar), + stream_(stream), + print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), + indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) + ? kStandardIndent + : 0), + nested_indent_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT, options)), + comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + show_byte_offset_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + name_mapper_(std::move(name_mapper)), + last_instruction_comment_alignment_(0) {} + +void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; } + +void InstructionDisassembler::EmitHeaderVersion(uint32_t version) { + stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n"; +} + +void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) { + const char* generator_tool = + spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); + stream_ << "; Generator: " << generator_tool; + // For unknown tools, print the numeric tool value. + if (0 == strcmp("Unknown", generator_tool)) { + stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; + } + // Print the miscellaneous part of the generator word on the same + // line as the tool name. + stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n"; +} + +void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) { + stream_ << "; Bound: " << id_bound << "\n"; +} + +void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) { + stream_ << "; Schema: " << schema << "\n"; +} + +void InstructionDisassembler::EmitInstruction( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset) { + EmitInstructionImpl(inst, inst_byte_offset, 0, false); +} + +void InstructionDisassembler::EmitInstructionInBlock( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset, + uint32_t block_indent) { + EmitInstructionImpl(inst, inst_byte_offset, block_indent, true); +} + +void InstructionDisassembler::EmitInstructionImpl( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset, + uint32_t block_indent, bool is_in_block) { + auto opcode = static_cast(inst.opcode); + + // To better align the comments (if any), write the instruction to a line + // first so its length can be readily available. + std::ostringstream line; + + if (nested_indent_ && opcode == spv::Op::OpLabel) { + // Separate the blocks by an empty line to make them easier to separate + stream_ << std::endl; + } + + if (inst.result_id) { + SetBlue(); + const std::string id_name = name_mapper_(inst.result_id); + if (indent_) + line << std::setw(std::max(0, indent_ - 3 - int(id_name.size()))); + line << "%" << id_name; + ResetColor(); + line << " = "; + } else { + line << std::string(indent_, ' '); + } + + if (nested_indent_ && is_in_block) { + // Output OpLabel at the specified nest level, and instructions inside + // blocks nested a little more. + uint32_t indent = block_indent; + bool body_indent = opcode != spv::Op::OpLabel; + + line << std::string( + indent * kBlockNestIndent + (body_indent ? kBlockBodyIndentOffset : 0), + ' '); + } + + line << "Op" << spvOpcodeString(opcode); + + for (uint16_t i = 0; i < inst.num_operands; i++) { + const spv_operand_type_t type = inst.operands[i].type; + assert(type != SPV_OPERAND_TYPE_NONE); + if (type == SPV_OPERAND_TYPE_RESULT_ID) continue; + line << " "; + EmitOperand(line, inst, i); + } + + // For the sake of comment generation, store information from some + // instructions for the future. + if (comment_) { + GenerateCommentForDecoratedId(inst); + } + + std::ostringstream comments; + const char* comment_separator = ""; + + if (show_byte_offset_) { + SetGrey(comments); + auto saved_flags = comments.flags(); + auto saved_fill = comments.fill(); + comments << comment_separator << "0x" << std::setw(8) << std::hex + << std::setfill('0') << inst_byte_offset; + comments.flags(saved_flags); + comments.fill(saved_fill); + ResetColor(comments); + comment_separator = ", "; + } + + if (comment_ && opcode == spv::Op::OpName) { + const spv_parsed_operand_t& operand = inst.operands[0]; + const uint32_t word = inst.words[operand.offset]; + comments << comment_separator << "id %" << word; + comment_separator = ", "; + } + + if (comment_ && inst.result_id && id_comments_.count(inst.result_id) > 0) { + comments << comment_separator << id_comments_[inst.result_id].str(); + comment_separator = ", "; + } + + stream_ << line.str(); + + if (!comments.str().empty()) { + // Align the comments + const uint32_t line_length = GetLineLengthWithoutColor(line.str()); + uint32_t align = std::max( + {line_length + 2, last_instruction_comment_alignment_, kCommentColumn}); + // Round up the alignment to a multiple of 4 for more niceness. + align = (align + 3) & ~0x3u; + last_instruction_comment_alignment_ = align; + + stream_ << std::string(align - line_length, ' ') << "; " << comments.str(); + } else { + last_instruction_comment_alignment_ = 0; + } + + stream_ << "\n"; +} + +void InstructionDisassembler::GenerateCommentForDecoratedId( + const spv_parsed_instruction_t& inst) { + assert(comment_); + auto opcode = static_cast(inst.opcode); + + std::ostringstream partial; + uint32_t id = 0; + const char* separator = ""; + + switch (opcode) { + case spv::Op::OpDecorate: + // Take everything after `OpDecorate %id` and associate it with id. + id = inst.words[inst.operands[0].offset]; + for (uint16_t i = 1; i < inst.num_operands; i++) { + partial << separator; + separator = " "; + EmitOperand(partial, inst, i); + } + break; + default: + break; + } + + if (id == 0) { + return; + } + + // Add the new comment to the comments of this id + std::ostringstream& id_comment = id_comments_[id]; + if (!id_comment.str().empty()) { + id_comment << ", "; + } + id_comment << partial.str(); +} + +void InstructionDisassembler::EmitSectionComment( + const spv_parsed_instruction_t& inst, bool& inserted_decoration_space, + bool& inserted_debug_space, bool& inserted_type_space) { + auto opcode = static_cast(inst.opcode); + if (comment_ && opcode == spv::Op::OpFunction) { + stream_ << std::endl; + if (nested_indent_) { + // Double the empty lines between Function sections since nested_indent_ + // also separates blocks by a blank. + stream_ << std::endl; + } + stream_ << std::string(indent_, ' '); + stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; + } + if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) { + inserted_decoration_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Annotations" << std::endl; + } + if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) { + inserted_debug_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Debug Information" << std::endl; + } + if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) { + inserted_type_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Types, variables and constants" << std::endl; + } +} + +void InstructionDisassembler::EmitOperand(std::ostream& stream, + const spv_parsed_instruction_t& inst, + const uint16_t operand_index) const { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + const uint32_t word = inst.words[operand.offset]; + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + assert(false && " is not supposed to be handled here"); + SetBlue(stream); + stream << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + SetYellow(stream); + stream << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + spv_ext_inst_desc ext_inst; + SetRed(stream); + if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + stream << ext_inst->name; + } else { + if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { + assert(false && "should have caught this earlier"); + } else { + // for non-semantic instruction sets we can just print the number + stream << word; + } + } + } break; + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + spv_opcode_desc opcode_desc; + if (grammar_.lookupOpcode(spv::Op(word), &opcode_desc)) + assert(false && "should have caught this earlier"); + SetRed(stream); + stream << opcode_desc->name; + } break; + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: { + SetRed(stream); + EmitNumericLiteral(&stream, inst, operand); + ResetColor(stream); + } break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + stream << "\""; + SetGreen(stream); + + std::string str = spvDecodeLiteralStringOperand(inst, operand_index); + for (char const& c : str) { + if (c == '"' || c == '\\') stream << '\\'; + stream << c; + } + ResetColor(stream); + stream << '"'; + } break; + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_FPENCODING: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream << entry->name; + } break; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: + EmitMaskOperand(stream, operand.type, word); + break; + default: + if (spvOperandIsConcreteMask(operand.type)) { + EmitMaskOperand(stream, operand.type, word); + } else if (spvOperandIsConcrete(operand.type)) { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream << entry->name; + } else { + assert(false && "unhandled or invalid case"); + } + break; + } + ResetColor(stream); +} + +void InstructionDisassembler::EmitMaskOperand(std::ostream& stream, + const spv_operand_type_t type, + const uint32_t word) const { + // Scan the mask from least significant bit to most significant bit. For each + // set bit, emit the name of that bit. Separate multiple names with '|'. + uint32_t remaining_word = word; + uint32_t mask; + int num_emitted = 0; + for (mask = 1; remaining_word; mask <<= 1) { + if (remaining_word & mask) { + remaining_word ^= mask; + spv_operand_desc entry; + if (grammar_.lookupOperand(type, mask, &entry)) + assert(false && "should have caught this earlier"); + if (num_emitted) stream << "|"; + stream << entry->name; + num_emitted++; + } + } + if (!num_emitted) { + // An operand value of 0 was provided, so represent it by the name + // of the 0 value. In many cases, that's "None". + spv_operand_desc entry; + if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) + stream << entry->name; + } +} + +void InstructionDisassembler::ResetColor(std::ostream& stream) const { + if (color_) stream << spvtools::clr::reset{print_}; +} +void InstructionDisassembler::SetGrey(std::ostream& stream) const { + if (color_) stream << spvtools::clr::grey{print_}; +} +void InstructionDisassembler::SetBlue(std::ostream& stream) const { + if (color_) stream << spvtools::clr::blue{print_}; +} +void InstructionDisassembler::SetYellow(std::ostream& stream) const { + if (color_) stream << spvtools::clr::yellow{print_}; +} +void InstructionDisassembler::SetRed(std::ostream& stream) const { + if (color_) stream << spvtools::clr::red{print_}; +} +void InstructionDisassembler::SetGreen(std::ostream& stream) const { + if (color_) stream << spvtools::clr::green{print_}; +} + +void InstructionDisassembler::ResetColor() { ResetColor(stream_); } +void InstructionDisassembler::SetGrey() { SetGrey(stream_); } +void InstructionDisassembler::SetBlue() { SetBlue(stream_); } +void InstructionDisassembler::SetYellow() { SetYellow(stream_); } +void InstructionDisassembler::SetRed() { SetRed(stream_); } +void InstructionDisassembler::SetGreen() { SetGreen(stream_); } +} // namespace disassemble + +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* instCode, + const size_t instWordCount, + const uint32_t* code, + const size_t wordCount, + const uint32_t options) { + spv_context context = spvContextCreate(env); + const AssemblyGrammar grammar(context); + if (!grammar.isValid()) { + spvContextDestroy(context); + return ""; + } + + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + NameMapper name_mapper = GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = MakeUnique(context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } + + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); + spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr); + + spv_text text = nullptr; + std::string output; + if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { + output.assign(text->str, text->str + text->length); + // Drop trailing newline characters. + while (!output.empty() && output.back() == '\n') output.pop_back(); + } + spvTextDestroy(text); + spvContextDestroy(context); + + return output; +} +} // namespace spvtools + +spv_result_t spvBinaryToText(const spv_const_context context, + const uint32_t* code, const size_t wordCount, + const uint32_t options, spv_text* pText, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + const spvtools::AssemblyGrammar grammar(&hijack_context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = spvtools::MakeUnique( + &hijack_context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } + + // Now disassemble! + spvtools::Disassembler disassembler(grammar, options, name_mapper); + if (auto error = + spvBinaryParse(&hijack_context, &disassembler, code, wordCount, + spvtools::DisassembleHeader, + spvtools::DisassembleInstruction, pDiagnostic)) { + return error; + } + + return disassembler.SaveTextResult(pText); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/disassemble.h b/engine/engine/3rdlibs/spirv-tools/source/disassemble.h new file mode 100644 index 00000000..b6d13c64 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/disassemble.h @@ -0,0 +1,128 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DISASSEMBLE_H_ +#define SOURCE_DISASSEMBLE_H_ + +#include +#include +#include + +#include "source/name_mapper.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Decodes the given SPIR-V instruction binary representation to its assembly +// text. The context is inferred from the provided module binary. The options +// parameter is a bit field of spv_binary_to_text_options_t (note: the option +// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be +// stored into *text. Any error will be written into *diagnostic if diagnostic +// is non-null. +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* inst_binary, + const size_t inst_word_count, + const uint32_t* binary, + const size_t word_count, + const uint32_t options); + +class AssemblyGrammar; +namespace disassemble { + +// Shared code with other tools (than the disassembler) that might need to +// output disassembly. An InstructionDisassembler instance converts SPIR-V +// binary for an instruction to its assembly representation. +class InstructionDisassembler { + public: + InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream, + uint32_t options, NameMapper name_mapper); + + // Emits the assembly header for the module. + void EmitHeaderSpirv(); + void EmitHeaderVersion(uint32_t version); + void EmitHeaderGenerator(uint32_t generator); + void EmitHeaderIdBound(uint32_t id_bound); + void EmitHeaderSchema(uint32_t schema); + + // Emits the assembly text for the given instruction. + void EmitInstruction(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset); + // Same as EmitInstruction, but only for block instructions (including + // OpLabel) and useful for nested indentation. If nested indentation is not + // desired, EmitInstruction can still be used for block instructions. + void EmitInstructionInBlock(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset, uint32_t block_indent); + + // Emits a comment between different sections of the module. + void EmitSectionComment(const spv_parsed_instruction_t& inst, + bool& inserted_decoration_space, + bool& inserted_debug_space, + bool& inserted_type_space); + + // Resets the output color, if color is turned on. + void ResetColor(); + // Set the output color, if color is turned on. + void SetGrey(); + void SetBlue(); + void SetYellow(); + void SetRed(); + void SetGreen(); + + private: + void ResetColor(std::ostream& stream) const; + void SetGrey(std::ostream& stream) const; + void SetBlue(std::ostream& stream) const; + void SetYellow(std::ostream& stream) const; + void SetRed(std::ostream& stream) const; + void SetGreen(std::ostream& stream) const; + + void EmitInstructionImpl(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset, uint32_t block_indent, + bool is_in_block); + + // Emits an operand for the given instruction, where the instruction + // is at offset words from the start of the binary. + void EmitOperand(std::ostream& stream, const spv_parsed_instruction_t& inst, + uint16_t operand_index) const; + + // Emits a mask expression for the given mask word of the specified type. + void EmitMaskOperand(std::ostream& stream, spv_operand_type_t type, + uint32_t word) const; + + // Generate part of the instruction as a comment to be added to + // |id_comments_|. + void GenerateCommentForDecoratedId(const spv_parsed_instruction_t& inst); + + const spvtools::AssemblyGrammar& grammar_; + std::ostream& stream_; + const bool print_; // Should we also print to the standard output stream? + const bool color_; // Should we print in colour? + const int indent_; // How much to indent. 0 means don't indent + const bool nested_indent_; // Whether indentation should indicate nesting + const int comment_; // Should we comment the source + const bool show_byte_offset_; // Should we print byte offset, in hex? + spvtools::NameMapper name_mapper_; + + // Some comments are generated as instructions (such as OpDecorate) are + // visited so that when the instruction with that result id is visited, the + // comment can be output. + std::unordered_map id_comments_; + // Align the comments in consecutive lines for more readability. + uint32_t last_instruction_comment_alignment_; +}; + +} // namespace disassemble +} // namespace spvtools + +#endif // SOURCE_DISASSEMBLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/enum_set.h b/engine/engine/3rdlibs/spirv-tools/source/enum_set.h new file mode 100644 index 00000000..340f5b97 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/enum_set.h @@ -0,0 +1,472 @@ +// Copyright (c) 2023 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOURCE_ENUM_SET_H_ +#define SOURCE_ENUM_SET_H_ + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { + +// This container is optimized to store and retrieve unsigned enum values. +// The base model for this implementation is an open-addressing hashtable with +// linear probing. For small enums (max index < 64), all operations are O(1). +// +// - Enums are stored in buckets (64 contiguous values max per bucket) +// - Buckets ranges don't overlap, but don't have to be contiguous. +// - Enums are packed into 64-bits buckets, using 1 bit per enum value. +// +// Example: +// - MyEnum { A = 0, B = 1, C = 64, D = 65 } +// - 2 buckets are required: +// - bucket 0, storing values in the range [ 0; 64[ +// - bucket 1, storing values in the range [64; 128[ +// +// - Buckets are stored in a sorted vector (sorted by bucket range). +// - Retrieval is done by computing the theoretical bucket index using the enum +// value, and +// doing a linear scan from this position. +// - Insertion is done by retrieving the bucket and either: +// - inserting a new bucket in the sorted vector when no buckets has a +// compatible range. +// - setting the corresponding bit in the bucket. +// This means insertion in the middle/beginning can cause a memmove when no +// bucket is available. In our case, this happens at most 23 times for the +// largest enum we have (Opcodes). +template +class EnumSet { + private: + using BucketType = uint64_t; + using ElementType = std::underlying_type_t; + static_assert(std::is_enum_v, "EnumSets only works with enums."); + static_assert(std::is_signed_v == false, + "EnumSet doesn't supports signed enums."); + + // Each bucket can hold up to `kBucketSize` distinct, contiguous enum values. + // The first value a bucket can hold must be aligned on `kBucketSize`. + struct Bucket { + // bit mask to store `kBucketSize` enums. + BucketType data; + // 1st enum this bucket can represent. + T start; + + friend bool operator==(const Bucket& lhs, const Bucket& rhs) { + return lhs.start == rhs.start && lhs.data == rhs.data; + } + }; + + // How many distinct values can a bucket hold? 1 bit per value. + static constexpr size_t kBucketSize = sizeof(BucketType) * 8ULL; + + public: + class Iterator { + public: + typedef Iterator self_type; + typedef T value_type; + typedef T& reference; + typedef T* pointer; + typedef std::forward_iterator_tag iterator_category; + typedef size_t difference_type; + + Iterator(const Iterator& other) + : set_(other.set_), + bucketIndex_(other.bucketIndex_), + bucketOffset_(other.bucketOffset_) {} + + Iterator& operator++() { + do { + if (bucketIndex_ >= set_->buckets_.size()) { + bucketIndex_ = set_->buckets_.size(); + bucketOffset_ = 0; + break; + } + + if (bucketOffset_ + 1 == kBucketSize) { + bucketOffset_ = 0; + ++bucketIndex_; + } else { + ++bucketOffset_; + } + + } while (bucketIndex_ < set_->buckets_.size() && + !set_->HasEnumAt(bucketIndex_, bucketOffset_)); + return *this; + } + + Iterator operator++(int) { + Iterator old = *this; + operator++(); + return old; + } + + T operator*() const { + assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && + "operator*() called on an invalid iterator."); + return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); + } + + bool operator!=(const Iterator& other) const { + return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || + bucketIndex_ != other.bucketIndex_; + } + + bool operator==(const Iterator& other) const { + return !(operator!=(other)); + } + + Iterator& operator=(const Iterator& other) { + set_ = other.set_; + bucketIndex_ = other.bucketIndex_; + bucketOffset_ = other.bucketOffset_; + return *this; + } + + private: + Iterator(const EnumSet* set, size_t bucketIndex, ElementType bucketOffset) + : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {} + + private: + const EnumSet* set_ = nullptr; + // Index of the bucket in the vector. + size_t bucketIndex_ = 0; + // Offset in bits in the current bucket. + ElementType bucketOffset_ = 0; + + friend class EnumSet; + }; + + // Required to allow the use of std::inserter. + using value_type = T; + using const_iterator = Iterator; + using iterator = Iterator; + + public: + iterator cbegin() const noexcept { + auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); + if (buckets_.size() == 0) { + return it; + } + + // The iterator has the logic to find the next valid bit. If the value 0 + // is not stored, use it to find the next valid bit. + if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { + ++it; + } + + return it; + } + + iterator begin() const noexcept { return cbegin(); } + + iterator cend() const noexcept { + return iterator(this, buckets_.size(), /* bucketOffset= */ 0); + } + + iterator end() const noexcept { return cend(); } + + // Creates an empty set. + EnumSet() : buckets_(0), size_(0) {} + + // Creates a set and store `value` in it. + EnumSet(T value) : EnumSet() { insert(value); } + + // Creates a set and stores each `values` in it. + EnumSet(std::initializer_list values) : EnumSet() { + for (auto item : values) { + insert(item); + } + } + + // Creates a set, and insert `count` enum values pointed by `array` in it. + EnumSet(ElementType count, const T* array) : EnumSet() { + for (ElementType i = 0; i < count; i++) { + insert(array[i]); + } + } + + // Creates a set initialized with the content of the range [begin; end[. + template + EnumSet(InputIt begin, InputIt end) : EnumSet() { + for (; begin != end; ++begin) { + insert(*begin); + } + } + + // Copies the EnumSet `other` into a new EnumSet. + EnumSet(const EnumSet& other) + : buckets_(other.buckets_), size_(other.size_) {} + + // Moves the EnumSet `other` into a new EnumSet. + EnumSet(EnumSet&& other) + : buckets_(std::move(other.buckets_)), size_(other.size_) {} + + // Deep-copies the EnumSet `other` into this EnumSet. + EnumSet& operator=(const EnumSet& other) { + buckets_ = other.buckets_; + size_ = other.size_; + return *this; + } + + // Matches std::unordered_set::insert behavior. + std::pair insert(const T& value) { + const size_t index = FindBucketForValue(value); + const ElementType offset = ComputeBucketOffset(value); + + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + size_ += 1; + InsertBucketFor(index, value); + return std::make_pair(Iterator(this, index, offset), true); + } + + auto& bucket = buckets_[index]; + const auto mask = ComputeMaskForValue(value); + if (bucket.data & mask) { + return std::make_pair(Iterator(this, index, offset), false); + } + + size_ += 1; + bucket.data |= ComputeMaskForValue(value); + return std::make_pair(Iterator(this, index, offset), true); + } + + // Inserts `value` in the set if possible. + // Similar to `std::unordered_set::insert`, except the hint is ignored. + // Returns an iterator to the inserted element, or the element preventing + // insertion. + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + // Inserts `value` in the set if possible. + // Similar to `std::unordered_set::insert`, except the hint is ignored. + // Returns an iterator to the inserted element, or the element preventing + // insertion. + iterator insert(const_iterator, T&& value) { return insert(value).first; } + + // Inserts all the values in the range [`first`; `last[. + // Similar to `std::unordered_set::insert`. + template + void insert(InputIt first, InputIt last) { + for (auto it = first; it != last; ++it) { + insert(*it); + } + } + + // Removes the value `value` into the set. + // Similar to `std::unordered_set::erase`. + // Returns the number of erased elements. + size_t erase(const T& value) { + const size_t index = FindBucketForValue(value); + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + return 0; + } + + auto& bucket = buckets_[index]; + const auto mask = ComputeMaskForValue(value); + if (!(bucket.data & mask)) { + return 0; + } + + size_ -= 1; + bucket.data &= ~mask; + if (bucket.data == 0) { + buckets_.erase(buckets_.cbegin() + index); + } + return 1; + } + + // Returns true if `value` is present in the set. + bool contains(T value) const { + const size_t index = FindBucketForValue(value); + if (index >= buckets_.size() || + buckets_[index].start != ComputeBucketStart(value)) { + return false; + } + auto& bucket = buckets_[index]; + return bucket.data & ComputeMaskForValue(value); + } + + // Returns the 1 if `value` is present in the set, `0` otherwise. + inline size_t count(T value) const { return contains(value) ? 1 : 0; } + + // Returns true if the set is holds no values. + inline bool empty() const { return size_ == 0; } + + // Returns the number of enums stored in this set. + size_t size() const { return size_; } + + // Returns true if this set contains at least one value contained in `in_set`. + // Note: If `in_set` is empty, this function returns true. + bool HasAnyOf(const EnumSet& in_set) const { + if (in_set.empty()) { + return true; + } + + auto lhs = buckets_.cbegin(); + auto rhs = in_set.buckets_.cbegin(); + + while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { + if (lhs->start == rhs->start) { + if (lhs->data & rhs->data) { + // At least 1 bit is shared. Early return. + return true; + } + + lhs++; + rhs++; + continue; + } + + // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. + if (lhs->start < rhs->start) { + lhs++; + continue; + } + + // Otherwise, RHS needs to catch up on LHS. + rhs++; + } + + return false; + } + + private: + // Returns the index of the last bucket in which `value` could be stored. + static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { + return static_cast(value) / kBucketSize; + } + + // Returns the smallest enum value that could be contained in the same bucket + // as `value`. + static constexpr inline T ComputeBucketStart(T value) { + return static_cast(kBucketSize * + ComputeLargestPossibleBucketIndexFor(value)); + } + + // Returns the index of the bit that corresponds to `value` in the bucket. + static constexpr inline ElementType ComputeBucketOffset(T value) { + return static_cast(value) % kBucketSize; + } + + // Returns the bitmask used to represent the enum `value` in its bucket. + static constexpr inline BucketType ComputeMaskForValue(T value) { + return 1ULL << ComputeBucketOffset(value); + } + + // Returns the `enum` stored in `bucket` at `offset`. + // `offset` is the bit-offset in the bucket storage. + static constexpr inline T GetValueFromBucket(const Bucket& bucket, + BucketType offset) { + return static_cast(static_cast(bucket.start) + offset); + } + + // For a given enum `value`, finds the bucket index that could contain this + // value. If no such bucket is found, the index at which the new bucket should + // be inserted is returned. + size_t FindBucketForValue(T value) const { + // Set is empty, insert at 0. + if (buckets_.size() == 0) { + return 0; + } + + const T wanted_start = ComputeBucketStart(value); + assert(buckets_.size() > 0 && + "Size must not be 0 here. Has the code above changed?"); + size_t index = std::min(buckets_.size() - 1, + ComputeLargestPossibleBucketIndexFor(value)); + + // This loops behaves like std::upper_bound with a reverse iterator. + // Buckets are sorted. 3 main cases: + // - The bucket matches + // => returns the bucket index. + // - The found bucket is larger + // => scans left until it finds the correct bucket, or insertion point. + // - The found bucket is smaller + // => We are at the end, so we return past-end index for insertion. + for (; buckets_[index].start >= wanted_start; index--) { + if (index == 0) { + return 0; + } + } + + return index + 1; + } + + // Creates a new bucket to store `value` and inserts it at `index`. + // If the `index` is past the end, the bucket is inserted at the end of the + // vector. + void InsertBucketFor(size_t index, T value) { + const T bucket_start = ComputeBucketStart(value); + Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; + auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); +#if defined(NDEBUG) + (void)it; // Silencing unused variable warning. +#else + assert(std::next(it) == buckets_.end() || + std::next(it)->start > bucket_start); + assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); +#endif + } + + // Returns true if the bucket at `bucketIndex/ stores the enum at + // `bucketOffset`, false otherwise. + bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { + assert(bucketIndex < buckets_.size()); + assert(bucketOffset < kBucketSize); + return buckets_[bucketIndex].data & (1ULL << bucketOffset); + } + + // Returns true if `lhs` and `rhs` hold the exact same values. + friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { + if (lhs.size_ != rhs.size_) { + return false; + } + + if (lhs.buckets_.size() != rhs.buckets_.size()) { + return false; + } + return lhs.buckets_ == rhs.buckets_; + } + + // Returns true if `lhs` and `rhs` hold at least 1 different value. + friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { + return !(lhs == rhs); + } + + // Storage for the buckets. + std::vector buckets_; + // How many enums is this set storing. + size_t size_ = 0; +}; + +// A set of spv::Capability. +using CapabilitySet = EnumSet; + +} // namespace spvtools + +#endif // SOURCE_ENUM_SET_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.cpp b/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.cpp new file mode 100644 index 00000000..32361a08 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/enum_string_mapping.h" + +#include +#include +#include +#include +#include + +#include "source/extensions.h" + +namespace spvtools { + +#include "enum_string_mapping.inc" + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.h b/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.h new file mode 100644 index 00000000..b1365840 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/enum_string_mapping.h @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_ENUM_STRING_MAPPING_H_ +#define SOURCE_ENUM_STRING_MAPPING_H_ + +#include + +#include "source/extensions.h" +#include "source/latest_version_spirv_header.h" + +namespace spvtools { + +// Finds Extension enum corresponding to |str|. Returns false if not found. +bool GetExtensionFromString(const char* str, Extension* extension); + +// Returns text string corresponding to |extension|. +const char* ExtensionToString(Extension extension); + +// Returns text string corresponding to |capability|. +const char* CapabilityToString(spv::Capability capability); + +} // namespace spvtools + +#endif // SOURCE_ENUM_STRING_MAPPING_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/ext_inst.cpp b/engine/engine/3rdlibs/spirv-tools/source/ext_inst.cpp new file mode 100644 index 00000000..f2ff63fc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/ext_inst.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/ext_inst.h" + +#include + +// DebugInfo extended instruction set. +// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html +// TODO(dneto): DebugInfo.h should probably move to SPIRV-Headers. +#include "DebugInfo.h" + +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/macro.h" +#include "source/spirv_definition.h" + +#include "debuginfo.insts.inc" +#include "glsl.std.450.insts.inc" +#include "nonsemantic.clspvreflection.insts.inc" +#include "nonsemantic.shader.debuginfo.100.insts.inc" +#include "nonsemantic.vkspreflection.insts.inc" +#include "opencl.debuginfo.100.insts.inc" +#include "opencl.std.insts.inc" + +#include "spirv-tools/libspirv.h" +#include "spv-amd-gcn-shader.insts.inc" +#include "spv-amd-shader-ballot.insts.inc" +#include "spv-amd-shader-explicit-vertex-parameter.insts.inc" +#include "spv-amd-shader-trinary-minmax.insts.inc" + +static const spv_ext_inst_group_t kGroups_1_0[] = { + {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries}, + {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries), + spv_amd_shader_explicit_vertex_parameter_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries), + spv_amd_shader_trinary_minmax_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries}, + {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries), + debuginfo_entries}, + {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries), + nonsemantic_shader_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + ARRAY_SIZE(nonsemantic_clspvreflection_entries), + nonsemantic_clspvreflection_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, + ARRAY_SIZE(nonsemantic_vkspreflection_entries), + nonsemantic_vkspreflection_entries}, +}; + +static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), + kGroups_1_0}; + +spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, + spv_target_env env) { + if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER; + + switch (env) { + // The extended instruction sets are all version 1.0 so far. + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + *pExtInstTable = &kTable_1_0; + return SPV_SUCCESS; + default: + return SPV_ERROR_INVALID_TABLE; + } +} + +spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { + // The names are specified by the respective extension instruction + // specifications. + if (!strcmp("GLSL.std.450", name)) { + return SPV_EXT_INST_TYPE_GLSL_STD_450; + } + if (!strcmp("OpenCL.std", name)) { + return SPV_EXT_INST_TYPE_OPENCL_STD; + } + if (!strcmp("SPV_AMD_shader_explicit_vertex_parameter", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER; + } + if (!strcmp("SPV_AMD_shader_trinary_minmax", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX; + } + if (!strcmp("SPV_AMD_gcn_shader", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER; + } + if (!strcmp("SPV_AMD_shader_ballot", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT; + } + if (!strcmp("DebugInfo", name)) { + return SPV_EXT_INST_TYPE_DEBUGINFO; + } + if (!strcmp("OpenCL.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; + } + if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + } + if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; + } + if (!strncmp("NonSemantic.VkspReflection.", name, 27)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION; + } + // ensure to add any known non-semantic extended instruction sets + // above this point, and update spvExtInstIsNonSemantic() + if (!strncmp("NonSemantic.", name, 12)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN; + } + return SPV_EXT_INST_TYPE_NONE; +} + +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { + if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION) { + return true; + } + return false; +} + +bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { + if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_DEBUGINFO) { + return true; + } + return false; +} + +spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const char* name, + spv_ext_inst_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { + const auto& group = table->groups[groupIndex]; + if (type != group.type) continue; + for (uint32_t index = 0; index < group.count; index++) { + const auto& entry = group.entries[index]; + if (!strcmp(name, entry.name)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const uint32_t value, + spv_ext_inst_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { + const auto& group = table->groups[groupIndex]; + if (type != group.type) continue; + for (uint32_t index = 0; index < group.count; index++) { + const auto& entry = group.entries[index]; + if (value == entry.ext_inst) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/ext_inst.h b/engine/engine/3rdlibs/spirv-tools/source/ext_inst.h new file mode 100644 index 00000000..4027f4c3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/ext_inst.h @@ -0,0 +1,46 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_EXT_INST_H_ +#define SOURCE_EXT_INST_H_ + +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// Gets the type of the extended instruction set with the specified name. +spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name); + +// Returns true if the extended instruction set is non-semantic +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); + +// Returns true if the extended instruction set is debug info +bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); + +// Finds the named extended instruction of the given type in the given extended +// instruction table. On success, returns SPV_SUCCESS and writes a handle of +// the instruction entry into *entry. +spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const char* name, + spv_ext_inst_desc* entry); + +// Finds the extended instruction of the given type in the given extended +// instruction table by value. On success, returns SPV_SUCCESS and writes a +// handle of the instruction entry into *entry. +spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const uint32_t value, + spv_ext_inst_desc* pEntry); + +#endif // SOURCE_EXT_INST_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/extensions.cpp b/engine/engine/3rdlibs/spirv-tools/source/extensions.cpp new file mode 100644 index 00000000..ac987fcc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/extensions.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/extensions.h" + +#include +#include +#include + +#include "source/binary.h" +#include "source/enum_string_mapping.h" + +namespace spvtools { + +std::string GetExtensionString(const spv_parsed_instruction_t* inst) { + if (inst->opcode != static_cast(spv::Op::OpExtension)) { + return "ERROR_not_op_extension"; + } + + assert(inst->num_operands == 1); + + const auto& operand = inst->operands[0]; + assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); + assert(inst->num_words > operand.offset); + (void)operand; /* No unused variables in release builds. */ + + return spvDecodeLiteralStringOperand(*inst, 0); +} + +std::string ExtensionSetToString(const ExtensionSet& extensions) { + std::stringstream ss; + for (auto extension : extensions) { + ss << ExtensionToString(extension) << " "; + } + return ss.str(); +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/extensions.h b/engine/engine/3rdlibs/spirv-tools/source/extensions.h new file mode 100644 index 00000000..cda4924a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/extensions.h @@ -0,0 +1,41 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_EXTENSIONS_H_ +#define SOURCE_EXTENSIONS_H_ + +#include +#include + +#include "source/enum_set.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// The known SPIR-V extensions. +enum Extension : uint32_t { +#include "extension_enum.inc" +}; + +using ExtensionSet = EnumSet; + +// Returns literal string operand of OpExtension instruction. +std::string GetExtensionString(const spv_parsed_instruction_t* inst); + +// Returns text string listing |extensions| separated by whitespace. +std::string ExtensionSetToString(const ExtensionSet& extensions); + +} // namespace spvtools + +#endif // SOURCE_EXTENSIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/fuzz/CMakeLists.txt new file mode 100644 index 00000000..86ee657a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/CMakeLists.txt @@ -0,0 +1,484 @@ +# Copyright (c) 2019 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(SPIRV_BUILD_FUZZER) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobufs) + + set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto) + + set( + SPIRV_FUZZ_PROTOC_COMMAND + "protobuf::protoc" + CACHE + STRING + "The command to invoke the protobuf compiler (protoc). By default it is the protobufs::protoc CMake target. It should be overridden when cross-compiling, such as for Android.") + + add_custom_command( + OUTPUT protobufs/spvtoolsfuzz.pb.cc protobufs/spvtoolsfuzz.pb.h + COMMAND "${SPIRV_FUZZ_PROTOC_COMMAND}" + -I=${CMAKE_CURRENT_SOURCE_DIR}/protobufs + --cpp_out=protobufs + ${PROTOBUF_SOURCE} + DEPENDS ${PROTOBUF_SOURCE} + COMMENT "Generate protobuf sources from proto definition file." + ) + + set(SPIRV_TOOLS_FUZZ_SOURCES + added_function_reducer.h + available_instructions.h + call_graph.h + comparator_deep_blocks_first.h + counter_overflow_id_source.h + data_descriptor.h + equivalence_relation.h + fact_manager/constant_uniform_facts.h + fact_manager/data_synonym_and_id_equation_facts.h + fact_manager/dead_block_facts.h + fact_manager/fact_manager.h + fact_manager/irrelevant_value_facts.h + fact_manager/livesafe_function_facts.h + force_render_red.h + fuzzer.h + fuzzer_context.h + fuzzer_pass.h + fuzzer_pass_add_access_chains.h + fuzzer_pass_add_bit_instruction_synonyms.h + fuzzer_pass_add_composite_extract.h + fuzzer_pass_add_composite_inserts.h + fuzzer_pass_add_composite_types.h + fuzzer_pass_add_copy_memory.h + fuzzer_pass_add_dead_blocks.h + fuzzer_pass_add_dead_breaks.h + fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_equation_instructions.h + fuzzer_pass_add_function_calls.h + fuzzer_pass_add_global_variables.h + fuzzer_pass_add_image_sample_unused_components.h + fuzzer_pass_add_loads.h + fuzzer_pass_add_local_variables.h + fuzzer_pass_add_loop_preheaders.h + fuzzer_pass_add_loops_to_create_int_constant_synonyms.h + fuzzer_pass_add_no_contraction_decorations.h + fuzzer_pass_add_opphi_synonyms.h + fuzzer_pass_add_parameters.h + fuzzer_pass_add_relaxed_decorations.h + fuzzer_pass_add_stores.h + fuzzer_pass_add_synonyms.h + fuzzer_pass_add_vector_shuffle_instructions.h + fuzzer_pass_adjust_branch_weights.h + fuzzer_pass_adjust_function_controls.h + fuzzer_pass_adjust_loop_controls.h + fuzzer_pass_adjust_memory_operands_masks.h + fuzzer_pass_adjust_selection_controls.h + fuzzer_pass_apply_id_synonyms.h + fuzzer_pass_construct_composites.h + fuzzer_pass_copy_objects.h + fuzzer_pass_donate_modules.h + fuzzer_pass_duplicate_regions_with_selections.h + fuzzer_pass_expand_vector_reductions.h + fuzzer_pass_flatten_conditional_branches.h + fuzzer_pass_inline_functions.h + fuzzer_pass_invert_comparison_operators.h + fuzzer_pass_interchange_signedness_of_integer_operands.h + fuzzer_pass_interchange_zero_like_constants.h + fuzzer_pass_make_vector_operations_dynamic.h + fuzzer_pass_merge_blocks.h + fuzzer_pass_merge_function_returns.h + fuzzer_pass_mutate_pointers.h + fuzzer_pass_obfuscate_constants.h + fuzzer_pass_outline_functions.h + fuzzer_pass_permute_blocks.h + fuzzer_pass_permute_function_parameters.h + fuzzer_pass_permute_function_variables.h + fuzzer_pass_permute_instructions.h + fuzzer_pass_permute_phi_operands.h + fuzzer_pass_propagate_instructions_down.h + fuzzer_pass_propagate_instructions_up.h + fuzzer_pass_push_ids_through_variables.h + fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h + fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h + fuzzer_pass_replace_copy_memories_with_loads_stores.h + fuzzer_pass_replace_copy_objects_with_stores_loads.h + fuzzer_pass_replace_irrelevant_ids.h + fuzzer_pass_replace_linear_algebra_instructions.h + fuzzer_pass_replace_loads_stores_with_copy_memories.h + fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h + fuzzer_pass_replace_opselects_with_conditional_branches.h + fuzzer_pass_replace_parameter_with_global.h + fuzzer_pass_replace_params_with_struct.h + fuzzer_pass_split_blocks.h + fuzzer_pass_swap_commutable_operands.h + fuzzer_pass_swap_conditional_branch_operands.h + fuzzer_pass_swap_functions.h + fuzzer_pass_toggle_access_chain_instruction.h + fuzzer_pass_wrap_regions_in_selections.h + fuzzer_pass_wrap_vector_synonym.h + fuzzer_util.h + id_use_descriptor.h + instruction_descriptor.h + instruction_message.h + overflow_id_source.h + pass_management/repeated_pass_instances.h + pass_management/repeated_pass_manager.h + pass_management/repeated_pass_manager_looped_with_recommendations.h + pass_management/repeated_pass_manager_random_with_recommendations.h + pass_management/repeated_pass_manager_simple.h + pass_management/repeated_pass_recommender.h + pass_management/repeated_pass_recommender_standard.h + protobufs/spirvfuzz_protobufs.h + pseudo_random_generator.h + random_generator.h + replayer.h + shrinker.h + transformation.h + transformation_access_chain.h + transformation_add_bit_instruction_synonym.h + transformation_add_constant_boolean.h + transformation_add_constant_composite.h + transformation_add_constant_null.h + transformation_add_constant_scalar.h + transformation_add_copy_memory.h + transformation_add_dead_block.h + transformation_add_dead_break.h + transformation_add_dead_continue.h + transformation_add_early_terminator_wrapper.h + transformation_add_function.h + transformation_add_global_undef.h + transformation_add_global_variable.h + transformation_add_image_sample_unused_components.h + transformation_add_local_variable.h + transformation_add_loop_preheader.h + transformation_add_loop_to_create_int_constant_synonym.h + transformation_add_no_contraction_decoration.h + transformation_add_opphi_synonym.h + transformation_add_parameter.h + transformation_add_relaxed_decoration.h + transformation_add_spec_constant_op.h + transformation_add_synonym.h + transformation_add_type_array.h + transformation_add_type_boolean.h + transformation_add_type_float.h + transformation_add_type_function.h + transformation_add_type_int.h + transformation_add_type_matrix.h + transformation_add_type_pointer.h + transformation_add_type_struct.h + transformation_add_type_vector.h + transformation_adjust_branch_weights.h + transformation_composite_construct.h + transformation_composite_extract.h + transformation_composite_insert.h + transformation_compute_data_synonym_fact_closure.h + transformation_context.h + transformation_duplicate_region_with_selection.h + transformation_equation_instruction.h + transformation_expand_vector_reduction.h + transformation_flatten_conditional_branch.h + transformation_function_call.h + transformation_inline_function.h + transformation_invert_comparison_operator.h + transformation_load.h + transformation_make_vector_operation_dynamic.h + transformation_merge_blocks.h + transformation_merge_function_returns.h + transformation_move_block_down.h + transformation_move_instruction_down.h + transformation_mutate_pointer.h + transformation_outline_function.h + transformation_permute_function_parameters.h + transformation_permute_phi_operands.h + transformation_propagate_instruction_down.h + transformation_propagate_instruction_up.h + transformation_push_id_through_variable.h + transformation_record_synonymous_constants.h + transformation_replace_add_sub_mul_with_carrying_extended.h + transformation_replace_boolean_constant_with_constant_binary.h + transformation_replace_branch_from_dead_block_with_exit.h + transformation_replace_constant_with_uniform.h + transformation_replace_copy_memory_with_load_store.h + transformation_replace_copy_object_with_store_load.h + transformation_replace_id_with_synonym.h + transformation_replace_irrelevant_id.h + transformation_replace_linear_algebra_instruction.h + transformation_replace_load_store_with_copy_memory.h + transformation_replace_opphi_id_from_dead_predecessor.h + transformation_replace_opselect_with_conditional_branch.h + transformation_replace_parameter_with_global.h + transformation_replace_params_with_struct.h + transformation_set_function_control.h + transformation_set_loop_control.h + transformation_set_memory_operands_mask.h + transformation_set_selection_control.h + transformation_split_block.h + transformation_store.h + transformation_swap_commutable_operands.h + transformation_swap_conditional_branch_operands.h + transformation_swap_function_variables.h + transformation_swap_two_functions.h + transformation_toggle_access_chain_instruction.h + transformation_vector_shuffle.h + transformation_wrap_early_terminator_in_function.h + transformation_wrap_region_in_selection.h + transformation_wrap_vector_synonym.h + uniform_buffer_element_descriptor.h + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h + + added_function_reducer.cpp + available_instructions.cpp + call_graph.cpp + counter_overflow_id_source.cpp + data_descriptor.cpp + fact_manager/constant_uniform_facts.cpp + fact_manager/data_synonym_and_id_equation_facts.cpp + fact_manager/dead_block_facts.cpp + fact_manager/fact_manager.cpp + fact_manager/irrelevant_value_facts.cpp + fact_manager/livesafe_function_facts.cpp + force_render_red.cpp + fuzzer.cpp + fuzzer_context.cpp + fuzzer_pass.cpp + fuzzer_pass_add_access_chains.cpp + fuzzer_pass_add_bit_instruction_synonyms.cpp + fuzzer_pass_add_composite_extract.cpp + fuzzer_pass_add_composite_inserts.cpp + fuzzer_pass_add_composite_types.cpp + fuzzer_pass_add_copy_memory.cpp + fuzzer_pass_add_dead_blocks.cpp + fuzzer_pass_add_dead_breaks.cpp + fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_equation_instructions.cpp + fuzzer_pass_add_function_calls.cpp + fuzzer_pass_add_global_variables.cpp + fuzzer_pass_add_image_sample_unused_components.cpp + fuzzer_pass_add_loads.cpp + fuzzer_pass_add_local_variables.cpp + fuzzer_pass_add_loop_preheaders.cpp + fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp + fuzzer_pass_add_no_contraction_decorations.cpp + fuzzer_pass_add_opphi_synonyms.cpp + fuzzer_pass_add_parameters.cpp + fuzzer_pass_add_relaxed_decorations.cpp + fuzzer_pass_add_stores.cpp + fuzzer_pass_add_synonyms.cpp + fuzzer_pass_add_vector_shuffle_instructions.cpp + fuzzer_pass_adjust_branch_weights.cpp + fuzzer_pass_adjust_function_controls.cpp + fuzzer_pass_adjust_loop_controls.cpp + fuzzer_pass_adjust_memory_operands_masks.cpp + fuzzer_pass_adjust_selection_controls.cpp + fuzzer_pass_apply_id_synonyms.cpp + fuzzer_pass_construct_composites.cpp + fuzzer_pass_copy_objects.cpp + fuzzer_pass_donate_modules.cpp + fuzzer_pass_duplicate_regions_with_selections.cpp + fuzzer_pass_expand_vector_reductions.cpp + fuzzer_pass_flatten_conditional_branches.cpp + fuzzer_pass_inline_functions.cpp + fuzzer_pass_invert_comparison_operators.cpp + fuzzer_pass_interchange_signedness_of_integer_operands.cpp + fuzzer_pass_interchange_zero_like_constants.cpp + fuzzer_pass_make_vector_operations_dynamic.cpp + fuzzer_pass_merge_blocks.cpp + fuzzer_pass_merge_function_returns.cpp + fuzzer_pass_mutate_pointers.cpp + fuzzer_pass_obfuscate_constants.cpp + fuzzer_pass_outline_functions.cpp + fuzzer_pass_permute_blocks.cpp + fuzzer_pass_permute_function_parameters.cpp + fuzzer_pass_permute_function_variables.cpp + fuzzer_pass_permute_instructions.cpp + fuzzer_pass_permute_phi_operands.cpp + fuzzer_pass_propagate_instructions_down.cpp + fuzzer_pass_propagate_instructions_up.cpp + fuzzer_pass_push_ids_through_variables.cpp + fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp + fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp + fuzzer_pass_replace_copy_memories_with_loads_stores.cpp + fuzzer_pass_replace_copy_objects_with_stores_loads.cpp + fuzzer_pass_replace_irrelevant_ids.cpp + fuzzer_pass_replace_linear_algebra_instructions.cpp + fuzzer_pass_replace_loads_stores_with_copy_memories.cpp + fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp + fuzzer_pass_replace_opselects_with_conditional_branches.cpp + fuzzer_pass_replace_parameter_with_global.cpp + fuzzer_pass_replace_params_with_struct.cpp + fuzzer_pass_split_blocks.cpp + fuzzer_pass_swap_commutable_operands.cpp + fuzzer_pass_swap_conditional_branch_operands.cpp + fuzzer_pass_swap_functions.cpp + fuzzer_pass_toggle_access_chain_instruction.cpp + fuzzer_pass_wrap_regions_in_selections.cpp + fuzzer_pass_wrap_vector_synonym.cpp + fuzzer_util.cpp + id_use_descriptor.cpp + instruction_descriptor.cpp + instruction_message.cpp + overflow_id_source.cpp + pass_management/repeated_pass_manager.cpp + pass_management/repeated_pass_manager_looped_with_recommendations.cpp + pass_management/repeated_pass_manager_random_with_recommendations.cpp + pass_management/repeated_pass_manager_simple.cpp + pass_management/repeated_pass_recommender.cpp + pass_management/repeated_pass_recommender_standard.cpp + pseudo_random_generator.cpp + random_generator.cpp + replayer.cpp + shrinker.cpp + transformation.cpp + transformation_access_chain.cpp + transformation_add_bit_instruction_synonym.cpp + transformation_add_constant_boolean.cpp + transformation_add_constant_composite.cpp + transformation_add_constant_null.cpp + transformation_add_constant_scalar.cpp + transformation_add_copy_memory.cpp + transformation_add_dead_block.cpp + transformation_add_dead_break.cpp + transformation_add_dead_continue.cpp + transformation_add_early_terminator_wrapper.cpp + transformation_add_function.cpp + transformation_add_global_undef.cpp + transformation_add_global_variable.cpp + transformation_add_image_sample_unused_components.cpp + transformation_add_local_variable.cpp + transformation_add_loop_preheader.cpp + transformation_add_loop_to_create_int_constant_synonym.cpp + transformation_add_no_contraction_decoration.cpp + transformation_add_opphi_synonym.cpp + transformation_add_parameter.cpp + transformation_add_relaxed_decoration.cpp + transformation_add_spec_constant_op.cpp + transformation_add_synonym.cpp + transformation_add_type_array.cpp + transformation_add_type_boolean.cpp + transformation_add_type_float.cpp + transformation_add_type_function.cpp + transformation_add_type_int.cpp + transformation_add_type_matrix.cpp + transformation_add_type_pointer.cpp + transformation_add_type_struct.cpp + transformation_add_type_vector.cpp + transformation_adjust_branch_weights.cpp + transformation_composite_construct.cpp + transformation_composite_extract.cpp + transformation_composite_insert.cpp + transformation_compute_data_synonym_fact_closure.cpp + transformation_context.cpp + transformation_duplicate_region_with_selection.cpp + transformation_equation_instruction.cpp + transformation_expand_vector_reduction.cpp + transformation_flatten_conditional_branch.cpp + transformation_function_call.cpp + transformation_inline_function.cpp + transformation_invert_comparison_operator.cpp + transformation_load.cpp + transformation_make_vector_operation_dynamic.cpp + transformation_merge_blocks.cpp + transformation_merge_function_returns.cpp + transformation_move_block_down.cpp + transformation_move_instruction_down.cpp + transformation_mutate_pointer.cpp + transformation_outline_function.cpp + transformation_permute_function_parameters.cpp + transformation_permute_phi_operands.cpp + transformation_propagate_instruction_down.cpp + transformation_propagate_instruction_up.cpp + transformation_push_id_through_variable.cpp + transformation_record_synonymous_constants.cpp + transformation_replace_add_sub_mul_with_carrying_extended.cpp + transformation_replace_boolean_constant_with_constant_binary.cpp + transformation_replace_branch_from_dead_block_with_exit.cpp + transformation_replace_constant_with_uniform.cpp + transformation_replace_copy_memory_with_load_store.cpp + transformation_replace_copy_object_with_store_load.cpp + transformation_replace_id_with_synonym.cpp + transformation_replace_irrelevant_id.cpp + transformation_replace_linear_algebra_instruction.cpp + transformation_replace_load_store_with_copy_memory.cpp + transformation_replace_opphi_id_from_dead_predecessor.cpp + transformation_replace_opselect_with_conditional_branch.cpp + transformation_replace_parameter_with_global.cpp + transformation_replace_params_with_struct.cpp + transformation_set_function_control.cpp + transformation_set_loop_control.cpp + transformation_set_memory_operands_mask.cpp + transformation_set_selection_control.cpp + transformation_split_block.cpp + transformation_store.cpp + transformation_swap_commutable_operands.cpp + transformation_swap_conditional_branch_operands.cpp + transformation_swap_function_variables.cpp + transformation_swap_two_functions.cpp + transformation_toggle_access_chain_instruction.cpp + transformation_vector_shuffle.cpp + transformation_wrap_early_terminator_in_function.cpp + transformation_wrap_region_in_selection.cpp + transformation_wrap_vector_synonym.cpp + uniform_buffer_element_descriptor.cpp + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc + ) + + if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) + endif() + + spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz) + + add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES}) + + spvtools_default_compile_options(SPIRV-Tools-fuzz) + + # Compilation of the auto-generated protobuf source file will yield warnings, + # which we have no control over and thus wish to ignore. + if(${COMPILER_IS_LIKE_GNU}) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS -w) + endif() + if(MSVC) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS /w) + endif() + + target_include_directories(SPIRV-Tools-fuzz + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE ${CMAKE_BINARY_DIR}) + + # The fuzzer reuses a lot of functionality from the SPIRV-Tools library. + target_link_libraries(SPIRV-Tools-fuzz + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY} + PUBLIC SPIRV-Tools-opt + PUBLIC SPIRV-Tools-reduce + PUBLIC protobuf::libprotobuf) + + set_property(TARGET SPIRV-Tools-fuzz PROPERTY FOLDER "SPIRV-Tools libraries") + spvtools_check_symbol_exports(SPIRV-Tools-fuzz) + + if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets) + export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake) + + spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR) + install(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-fuzz) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-fuzzConfig.cmake DESTINATION ${PACKAGE_DIR}) + endif(ENABLE_SPIRV_TOOLS_INSTALL) + +endif(SPIRV_BUILD_FUZZER) diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.cpp new file mode 100644 index 00000000..95cf9d6d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.cpp @@ -0,0 +1,302 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/added_function_reducer.h" + +#include "source/fuzz/instruction_message.h" +#include "source/fuzz/replayer.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/reduce/reducer.h" + +namespace spvtools { +namespace fuzz { + +AddedFunctionReducer::AddedFunctionReducer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t index_of_add_function_transformation, + const Shrinker::InterestingnessFunction& shrinker_interestingness_function, + bool validate_during_replay, spv_validator_options validator_options, + uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + index_of_add_function_transformation_( + index_of_add_function_transformation), + shrinker_interestingness_function_(shrinker_interestingness_function), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options), + shrinker_step_limit_(shrinker_step_limit), + num_existing_shrink_attempts_(num_existing_shrink_attempts), + num_reducer_interestingness_function_invocations_(0) {} + +AddedFunctionReducer::~AddedFunctionReducer() = default; + +AddedFunctionReducer::AddedFunctionReducerResult AddedFunctionReducer::Run() { + // Replay all transformations before the AddFunction transformation, then + // add the raw function associated with the AddFunction transformation. + std::vector binary_to_reduce; + std::unordered_set irrelevant_pointee_global_variables; + ReplayPrefixAndAddFunction(&binary_to_reduce, + &irrelevant_pointee_global_variables); + + // Set up spirv-reduce to use our very specific interestingness function. + reduce::Reducer reducer(target_env_); + reducer.SetMessageConsumer(consumer_); + reducer.AddDefaultReductionPasses(); + reducer.SetInterestingnessFunction( + [this, &irrelevant_pointee_global_variables]( + const std::vector& binary_under_reduction, + uint32_t /*unused*/) { + return InterestingnessFunctionForReducingAddedFunction( + binary_under_reduction, irrelevant_pointee_global_variables); + }); + + // Instruct spirv-reduce to only target the function with the id associated + // with the AddFunction transformation that we care about. + spvtools::ReducerOptions reducer_options; + reducer_options.set_target_function(GetAddedFunctionId()); + // Bound the number of reduction steps that spirv-reduce can make according + // to the overall shrinker step limit and the number of shrink attempts that + // have already been tried. + assert(shrinker_step_limit_ > num_existing_shrink_attempts_ && + "The added function reducer should not have been invoked."); + reducer_options.set_step_limit(shrinker_step_limit_ - + num_existing_shrink_attempts_); + + // Run spirv-reduce. + std::vector reduced_binary; + auto reducer_result = + reducer.Run(std::move(binary_to_reduce), &reduced_binary, reducer_options, + validator_options_); + if (reducer_result != reduce::Reducer::kComplete && + reducer_result != reduce::Reducer::kReachedStepLimit) { + return {AddedFunctionReducerResultStatus::kReductionFailed, + std::vector(), protobufs::TransformationSequence(), 0}; + } + + // Provide the outer shrinker with an adapted sequence of transformations in + // which the AddFunction transformation of interest has been simplified to use + // the version of the added function that appears in |reduced_binary|. + std::vector binary_out; + protobufs::TransformationSequence transformation_sequence_out; + ReplayAdaptedTransformations(reduced_binary, &binary_out, + &transformation_sequence_out); + // We subtract 1 from |num_reducer_interestingness_function_invocations_| to + // account for the fact that spirv-reduce invokes its interestingness test + // once before reduction commences in order to check that the initial module + // is interesting. + assert(num_reducer_interestingness_function_invocations_ > 0 && + "At a minimum spirv-reduce should have invoked its interestingness " + "test once."); + return {AddedFunctionReducerResultStatus::kComplete, std::move(binary_out), + std::move(transformation_sequence_out), + num_reducer_interestingness_function_invocations_ - 1}; +} + +bool AddedFunctionReducer::InterestingnessFunctionForReducingAddedFunction( + const std::vector& binary_under_reduction, + const std::unordered_set& irrelevant_pointee_global_variables) { + uint32_t counter_for_shrinker_interestingness_function = + num_existing_shrink_attempts_ + + num_reducer_interestingness_function_invocations_; + num_reducer_interestingness_function_invocations_++; + + // The reduced version of the added function must be limited to accessing + // global variables appearing in |irrelevant_pointee_global_variables|. This + // is to guard against the possibility of spirv-reduce changing a reference + // to an irrelevant global to a reference to a regular global variable, which + // could cause the added function to change the semantics of the original + // module. + auto ir_context = + BuildModule(target_env_, consumer_, binary_under_reduction.data(), + binary_under_reduction.size()); + assert(ir_context != nullptr && "The binary should be parsable."); + for (auto& type_or_value : ir_context->module()->types_values()) { + if (type_or_value.opcode() != spv::Op::OpVariable) { + continue; + } + if (irrelevant_pointee_global_variables.count(type_or_value.result_id())) { + continue; + } + if (!ir_context->get_def_use_mgr()->WhileEachUse( + &type_or_value, + [this, &ir_context](opt::Instruction* user, + uint32_t /*unused*/) -> bool { + auto block = ir_context->get_instr_block(user); + if (block != nullptr && + block->GetParent()->result_id() == GetAddedFunctionId()) { + return false; + } + return true; + })) { + return false; + } + } + + // For the binary to be deemed interesting, it must be possible to + // successfully apply all the transformations, with the transformation at + // index |index_of_add_function_transformation_| simplified to use the version + // of the added function from |binary_under_reduction|. + // + // This might not be the case: spirv-reduce might have removed a chunk of the + // added function on which future transformations depend. + // + // This is an optimization: the assumption is that having already shrunk the + // transformation sequence down to minimal form, all transformations have a + // role to play, and it's almost certainly a waste of time to invoke the + // shrinker's interestingness function if we have eliminated transformations + // that the shrinker previously tried to -- but could not -- eliminate. + std::vector binary_out; + protobufs::TransformationSequence modified_transformations; + ReplayAdaptedTransformations(binary_under_reduction, &binary_out, + &modified_transformations); + if (transformation_sequence_in_.transformation_size() != + modified_transformations.transformation_size()) { + return false; + } + + // The resulting binary must be deemed interesting according to the shrinker's + // interestingness function. + return shrinker_interestingness_function_( + binary_out, counter_for_shrinker_interestingness_function); +} + +void AddedFunctionReducer::ReplayPrefixAndAddFunction( + std::vector* binary_out, + std::unordered_set* irrelevant_pointee_global_variables) const { + assert(transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .has_add_function() && + "A TransformationAddFunction is required at the given index."); + + auto replay_result = Replayer(target_env_, consumer_, binary_in_, + initial_facts_, transformation_sequence_in_, + index_of_add_function_transformation_, + validate_during_replay_, validator_options_) + .Run(); + assert(replay_result.status == Replayer::ReplayerResultStatus::kComplete && + "Replay should succeed"); + assert(static_cast( + replay_result.applied_transformations.transformation_size()) == + index_of_add_function_transformation_ && + "All requested transformations should have applied."); + + auto* ir_context = replay_result.transformed_module.get(); + + for (auto& type_or_value : ir_context->module()->types_values()) { + if (type_or_value.opcode() != spv::Op::OpVariable) { + continue; + } + if (replay_result.transformation_context->GetFactManager() + ->PointeeValueIsIrrelevant(type_or_value.result_id())) { + irrelevant_pointee_global_variables->insert(type_or_value.result_id()); + } + } + + // Add the function associated with the transformation at + // |index_of_add_function_transformation| to the module. By construction this + // should succeed. + const protobufs::TransformationAddFunction& + transformation_add_function_message = + transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function(); + bool success = TransformationAddFunction(transformation_add_function_message) + .TryToAddFunction(ir_context); + (void)success; // Keep release mode compilers happy. + assert(success && "Addition of the function should have succeeded."); + + // Get the binary representation of the module with this function added. + ir_context->module()->ToBinary(binary_out, false); +} + +void AddedFunctionReducer::ReplayAdaptedTransformations( + const std::vector& binary_under_reduction, + std::vector* binary_out, + protobufs::TransformationSequence* transformation_sequence_out) const { + assert(index_of_add_function_transformation_ < + static_cast( + transformation_sequence_in_.transformation_size()) && + "The relevant add function transformation must be present."); + std::unique_ptr ir_context_under_reduction = + BuildModule(target_env_, consumer_, binary_under_reduction.data(), + binary_under_reduction.size()); + assert(ir_context_under_reduction && "Error building module."); + + protobufs::TransformationSequence modified_transformations; + for (uint32_t i = 0; + i < + static_cast(transformation_sequence_in_.transformation_size()); + i++) { + if (i == index_of_add_function_transformation_) { + protobufs::TransformationAddFunction modified_add_function = + transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function(); + assert(GetAddedFunctionId() == + modified_add_function.instruction(0).result_id() && + "Unexpected result id for added function."); + modified_add_function.clear_instruction(); + for (auto& function : *ir_context_under_reduction->module()) { + if (function.result_id() != GetAddedFunctionId()) { + continue; + } + function.ForEachInst( + [&modified_add_function](const opt::Instruction* instruction) { + *modified_add_function.add_instruction() = + MakeInstructionMessage(instruction); + }); + } + assert(modified_add_function.instruction_size() > 0 && + "Some instructions for the added function should remain."); + *modified_transformations.add_transformation()->mutable_add_function() = + modified_add_function; + } else { + *modified_transformations.add_transformation() = + transformation_sequence_in_.transformation(i); + } + } + assert( + transformation_sequence_in_.transformation_size() == + modified_transformations.transformation_size() && + "The original and modified transformations should have the same size."); + auto replay_result = Replayer(target_env_, consumer_, binary_in_, + initial_facts_, modified_transformations, + modified_transformations.transformation_size(), + validate_during_replay_, validator_options_) + .Run(); + assert(replay_result.status == Replayer::ReplayerResultStatus::kComplete && + "Replay should succeed."); + replay_result.transformed_module->module()->ToBinary(binary_out, false); + *transformation_sequence_out = + std::move(replay_result.applied_transformations); +} + +uint32_t AddedFunctionReducer::GetAddedFunctionId() const { + return transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function() + .instruction(0) + .result_id(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.h new file mode 100644 index 00000000..3efd2684 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/added_function_reducer.h @@ -0,0 +1,193 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ +#define SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/shrinker.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// An auxiliary class used by Shrinker, this class takes care of using +// spirv-reduce to reduce the body of a function encoded in an AddFunction +// transformation, in case a smaller, simpler function can be added instead. +class AddedFunctionReducer { + public: + // Possible statuses that can result from running the shrinker. + enum class AddedFunctionReducerResultStatus { + kComplete, + kReductionFailed, + }; + + struct AddedFunctionReducerResult { + AddedFunctionReducerResultStatus status; + std::vector transformed_binary; + protobufs::TransformationSequence applied_transformations; + uint32_t num_reduction_attempts; + }; + + AddedFunctionReducer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t index_of_add_function_transformation, + const Shrinker::InterestingnessFunction& + shrinker_interestingness_function, + bool validate_during_replay, spv_validator_options validator_options, + uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts); + + // Disables copy/move constructor/assignment operations. + AddedFunctionReducer(const AddedFunctionReducer&) = delete; + AddedFunctionReducer(AddedFunctionReducer&&) = delete; + AddedFunctionReducer& operator=(const AddedFunctionReducer&) = delete; + AddedFunctionReducer& operator=(AddedFunctionReducer&&) = delete; + + ~AddedFunctionReducer(); + + // Invokes spirv-reduce on the function in the AddFunction transformation + // identified by |index_of_add_function_transformation|. Returns a sequence + // of transformations identical to |transformation_sequence_in|, except that + // the AddFunction transformation at |index_of_add_function_transformation| + // might have been simplified. The binary associated with applying the + // resulting sequence of transformations to |binary_in| is also returned, as + // well as the number of reduction steps that spirv-reduce made. + // + // On failure, an empty transformation sequence and binary are returned, + // with a placeholder value of 0 for the number of reduction attempts. + AddedFunctionReducerResult Run(); + + private: + // Yields, via |binary_out|, the binary obtained by applying transformations + // [0, |index_of_added_function_| - 1] from |transformations_in_| to + // |binary_in_|, and then adding the raw function encoded in + // |transformations_in_[index_of_added_function_]| (without adapting that + // function to make it livesafe). This function has |added_function_id_| as + // its result id. + // + // The ids associated with all global variables in |binary_out| that had the + // "irrelevant pointee value" fact are also returned via + // |irrelevant_pointee_global_variables|. + // + // The point of this function is that spirv-reduce can subsequently be applied + // to function |added_function_id_| in |binary_out|. By construction, + // |added_function_id_| should originally manipulate globals for which + // "irrelevant pointee value" facts hold. The set + // |irrelevant_pointee_global_variables| can be used to force spirv-reduce + // to preserve this, to avoid the reduced function ending up manipulating + // other global variables of the SPIR-V module, potentially changing their + // value and thus changing the semantics of the module. + void ReplayPrefixAndAddFunction( + std::vector* binary_out, + std::unordered_set* irrelevant_pointee_global_variables) const; + + // This is the interestingness function that will be used by spirv-reduce + // when shrinking the added function. + // + // For |binary_under_reduction| to be deemed interesting, the following + // conditions must hold: + // - The function with id |added_function_id_| in |binary_under_reduction| + // must only reference global variables in + // |irrelevant_pointee_global_variables|. This avoids the reduced function + // changing the semantics of the original SPIR-V module. + // - It must be possible to successfully replay the transformations in + // |transformation_sequence_in_|, adapted so that the function added by the + // transformation at |index_of_add_function_transformation_| is replaced by + // the function with id |added_function_id_| in |binary_under_reduction|, + // to |binary_in| (starting with initial facts |initial_facts_|). + // - All the transformations in this sequence must be successfully applied + // during replay. + // - The resulting binary must be interesting according to + // |shrinker_interestingness_function_|. + bool InterestingnessFunctionForReducingAddedFunction( + const std::vector& binary_under_reduction, + const std::unordered_set& irrelevant_pointee_global_variables); + + // Starting with |binary_in_| and |initial_facts_|, the transformations in + // |transformation_sequence_in_| are replayed. However, the transformation + // at index |index_of_add_function_transformation_| of + // |transformation_sequence_in_| -- which is guaranteed to be an AddFunction + // transformation -- is adapted so that the function to be added is replaced + // with the function in |binary_under_reduction| with id |added_function_id_|. + // + // The binary resulting from this replay is returned via |binary_out|, and the + // adapted transformation sequence via |transformation_sequence_out|. + void ReplayAdaptedTransformations( + const std::vector& binary_under_reduction, + std::vector* binary_out, + protobufs::TransformationSequence* transformation_sequence_out) const; + + // Returns the id of the function to be added by the AddFunction + // transformation at + // |transformation_sequence_in_[index_of_add_function_transformation_]|. + uint32_t GetAddedFunctionId() const; + + // Target environment. + const spv_target_env target_env_; + + // Message consumer. + MessageConsumer consumer_; + + // The initial binary to which transformations are applied -- i.e., the + // binary to which spirv-fuzz originally applied transformations. + const std::vector& binary_in_; + + // Initial facts about |binary_in_|. + const protobufs::FactSequence& initial_facts_; + + // A set of transformations that can be successfully applied to |binary_in_|. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // An index into |transformation_sequence_in_| referring to an AddFunction + // transformation. This is the transformation to be simplified using + // spirv-reduce. + const uint32_t index_of_add_function_transformation_; + + // The interestingness function that has been provided to guide the + // overall shrinking process. The AddFunction transformation being simplified + // by this class should still -- when applied in conjunction with the other + // transformations in |transformation_sequence_in_| -- lead to a binary that + // is deemed interesting by this function. + const Shrinker::InterestingnessFunction& shrinker_interestingness_function_; + + // Determines whether to check for validity during the replaying of + // transformations. + const bool validate_during_replay_; + + // Options to control validation. + spv_validator_options validator_options_; + + // The step limit associated with the overall shrinking process. + const uint32_t shrinker_step_limit_; + + // The number of shrink attempts that had been applied prior to invoking this + // AddedFunctionReducer instance. + const uint32_t num_existing_shrink_attempts_; + + // Tracks the number of attempts that spirv-reduce has invoked its + // interestingness function, which it does once at the start of reduction, + // and then once more each time it makes a reduction step. + uint32_t num_reducer_interestingness_function_invocations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.cpp new file mode 100644 index 00000000..0db8b207 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/available_instructions.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +AvailableInstructions::AvailableInstructions( + opt::IRContext* ir_context, + const std::function& predicate) + : ir_context_(ir_context) { + // Consider all global declarations + for (auto& global : ir_context->module()->types_values()) { + if (predicate(ir_context, &global)) { + available_globals_.push_back(&global); + } + } + + // Consider every function + for (auto& function : *ir_context->module()) { + // Identify those function parameters that satisfy the predicate. + std::vector available_params_for_function; + function.ForEachParam( + [&predicate, ir_context, + &available_params_for_function](opt::Instruction* param) { + if (predicate(ir_context, param)) { + available_params_for_function.push_back(param); + } + }); + + // Consider every reachable block in the function. + auto dominator_analysis = ir_context->GetDominatorAnalysis(&function); + for (auto& block : function) { + if (!ir_context->IsReachable(block)) { + // The block is not reachable. + continue; + } + if (&block == &*function.begin()) { + // The function entry block is special: only the relevant globals and + // function parameters are available at its entry point. + num_available_at_block_entry_.insert( + {&block, + static_cast(available_params_for_function.size() + + available_globals_.size())}); + } else { + // |block| is not the entry block and is reachable, so it must have an + // immediate dominator. The number of instructions available on entry to + // |block| is thus the number of instructions available on entry to the + // immediate dominator + the number of instructions generated_by_block + // by the immediate dominator. + auto immediate_dominator = + dominator_analysis->ImmediateDominator(&block); + assert(immediate_dominator != nullptr && + "The block is reachable so should have an immediate dominator."); + assert(generated_by_block_.count(immediate_dominator) != 0 && + "Immediate dominator should have already been processed."); + assert(num_available_at_block_entry_.count(immediate_dominator) != 0 && + "Immediate dominator should have already been processed."); + num_available_at_block_entry_.insert( + {&block, + static_cast( + generated_by_block_.at(immediate_dominator).size()) + + num_available_at_block_entry_.at(immediate_dominator)}); + } + // Now consider each instruction in the block. + std::vector generated_by_block; + for (auto& inst : block) { + assert(num_available_at_block_entry_.count(&block) != 0 && + "Block should have already been processed."); + // The number of available instructions before |inst| is the number + // available at the start of the block + the number of relevant + // instructions generated by the block so far. + num_available_before_instruction_.insert( + {&inst, num_available_at_block_entry_.at(&block) + + static_cast(generated_by_block.size())}); + if (predicate(ir_context, &inst)) { + // This instruction satisfies the predicate, so note that it is + // generated by |block|. + generated_by_block.push_back(&inst); + } + } + generated_by_block_.emplace(&block, std::move(generated_by_block)); + } + available_params_.emplace(&function, + std::move(available_params_for_function)); + } +} + +AvailableInstructions::AvailableBeforeInstruction +AvailableInstructions::GetAvailableBeforeInstruction( + opt::Instruction* inst) const { + assert(num_available_before_instruction_.count(inst) != 0 && + "Availability can only be queried for reachable instructions."); + return {*this, inst}; +} + +AvailableInstructions::AvailableBeforeInstruction::AvailableBeforeInstruction( + const AvailableInstructions& available_instructions, opt::Instruction* inst) + : available_instructions_(available_instructions), inst_(inst) {} + +uint32_t AvailableInstructions::AvailableBeforeInstruction::size() const { + return available_instructions_.num_available_before_instruction_.at(inst_); +} + +bool AvailableInstructions::AvailableBeforeInstruction::empty() const { + return size() == 0; +} + +opt::Instruction* AvailableInstructions::AvailableBeforeInstruction::operator[]( + uint32_t index) const { + assert(index < size() && "Index out of bounds."); + + // First, check the cache to see whether we can return the available + // instruction in constant time. + auto cached_result = index_cache.find(index); + if (cached_result != index_cache.end()) { + return cached_result->second; + } + + // Next check whether the index falls into the global region. + if (index < available_instructions_.available_globals_.size()) { + auto result = available_instructions_.available_globals_[index]; + index_cache.insert({index, result}); + return result; + } + + auto block = available_instructions_.ir_context_->get_instr_block(inst_); + auto function = block->GetParent(); + + // Next check whether the index falls into the available instructions that + // correspond to function parameters. + if (index < + available_instructions_.available_globals_.size() + + available_instructions_.available_params_.at(function).size()) { + auto result = available_instructions_.available_params_.at( + function)[index - available_instructions_.available_globals_.size()]; + index_cache.insert({index, result}); + return result; + } + + auto dominator_analysis = + available_instructions_.ir_context_->GetDominatorAnalysis(function); + + // Now the expensive part (which is why we have the cache): walk the dominator + // tree backwards starting from the block containing |inst_| until we get to + // the block in which the instruction corresponding to |index| exists. + for (auto* ancestor = block; true; + ancestor = dominator_analysis->ImmediateDominator(ancestor)) { + uint32_t num_available_at_ancestor_entry = + available_instructions_.num_available_at_block_entry_.at(ancestor); + if (index_cache.count(num_available_at_ancestor_entry) == 0) { + // This is the first time we have traversed this block, so we populate the + // cache with the index of each instruction, so that if a future index + // query relates to indices associated with this block we can return the + // result in constant time. + auto& generated_by_ancestor = + available_instructions_.generated_by_block_.at(ancestor); + for (uint32_t local_index = 0; local_index < generated_by_ancestor.size(); + local_index++) { + index_cache.insert({num_available_at_ancestor_entry + local_index, + generated_by_ancestor[local_index]}); + } + } + if (index >= num_available_at_ancestor_entry) { + // This block contains the instruction we want, so by now it will be in + // the cache. + return index_cache.at(index); + } + assert(ancestor != &*function->begin() && + "By construction we should find a block associated with the index."); + } + + assert(false && "Unreachable."); + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.h new file mode 100644 index 00000000..5c0b4175 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/available_instructions.h @@ -0,0 +1,111 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A class for allowing efficient querying of the instruction that satisfy a +// particular predicate that are available before a given instruction. +// Availability information is only computed for instructions in *reachable* +// basic blocks. +class AvailableInstructions { + public: + // The outer class captures availability information for a whole module, and + // each instance of this inner class captures availability for a particular + // instruction. + class AvailableBeforeInstruction { + public: + AvailableBeforeInstruction( + const AvailableInstructions& available_instructions, + opt::Instruction* inst); + + // Returns the number of instructions that are available before the + // instruction associated with this class. + uint32_t size() const; + + // Returns true if and only if |size()| is 0. + bool empty() const; + + // Requires |index| < |size()|. Returns the ith available instruction. + opt::Instruction* operator[](uint32_t index) const; + + private: + // A references to an instance of the outer class. + const AvailableInstructions& available_instructions_; + + // The instruction for which availability information is captured. + opt::Instruction* inst_; + + // A cache to improve the efficiency of the [] operator. The [] operator + // requires walking the instruction's dominator tree to find an instruction + // at a particular index, which is a linear time operation. By inserting all + // instructions that are traversed during this search into a cache, future + // lookups will take constant time unless they require traversing the + // dominator tree more deeply. + mutable std::unordered_map index_cache; + }; + + // Constructs availability instructions for |ir_context|, where instructions + // are only available if they satisfy |predicate|. + AvailableInstructions( + opt::IRContext* ir_context, + const std::function& predicate); + + // Yields instruction availability for |inst|. + AvailableBeforeInstruction GetAvailableBeforeInstruction( + opt::Instruction* inst) const; + + private: + // The module in which all instructions are contained. + opt::IRContext* ir_context_; + + // The global instructions that satisfy the predicate. + std::vector available_globals_; + + // Per function, the parameters that satisfy the predicate. + std::unordered_map> + available_params_; + + // The number of instructions that satisfy the predicate and that are + // available at the entry to a block. For the entry block of a function this + // is the number of available globals + the number of available function + // parameters. For any other block it is the number of available instructions + // for the blocks immediate dominator + the number of instructions generated + // by the immediate dominator. + std::unordered_map num_available_at_block_entry_; + + // For each block this records those instructions in the block that satisfy + // the predicate. + std::unordered_map> + generated_by_block_; + + // For each instruction this records how many instructions satisfying the + // predicate are available before the instruction. + std::unordered_map + num_available_before_instruction_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.cpp new file mode 100644 index 00000000..d61a5f84 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/call_graph.h" + +#include + +namespace spvtools { +namespace fuzz { + +CallGraph::CallGraph(opt::IRContext* context) { + // Initialize function in-degree, call graph edges and corresponding maximum + // loop nesting depth to 0, empty and 0 respectively. + for (auto& function : *context->module()) { + function_in_degree_[function.result_id()] = 0; + call_graph_edges_[function.result_id()] = std::set(); + function_max_loop_nesting_depth_[function.result_id()] = 0; + } + + // Record the maximum loop nesting depth for each edge, by keeping a map from + // pairs of function ids, where (A, B) represents a function call from A to B, + // to the corresponding maximum depth. + std::map, uint32_t> call_to_max_depth; + + // Compute |function_in_degree_|, |call_graph_edges_| and |call_to_max_depth|. + BuildGraphAndGetDepthOfFunctionCalls(context, &call_to_max_depth); + + // Compute |functions_in_topological_order_|. + ComputeTopologicalOrderOfFunctions(); + + // Compute |function_max_loop_nesting_depth_|. + ComputeInterproceduralFunctionCallDepths(call_to_max_depth); +} + +void CallGraph::BuildGraphAndGetDepthOfFunctionCalls( + opt::IRContext* context, + std::map, uint32_t>* call_to_max_depth) { + // Consider every function. + for (auto& function : *context->module()) { + // Avoid considering the same callee of this function multiple times by + // recording known callees. + std::set known_callees; + // Consider every function call instruction in every block. + for (auto& block : function) { + for (auto& instruction : block) { + if (instruction.opcode() != spv::Op::OpFunctionCall) { + continue; + } + // Get the id of the function being called. + uint32_t callee = instruction.GetSingleWordInOperand(0); + + // Get the loop nesting depth of this function call. + uint32_t loop_nesting_depth = + context->GetStructuredCFGAnalysis()->LoopNestingDepth(block.id()); + // If inside a loop header, consider the function call nested inside the + // loop headed by the block. + if (block.IsLoopHeader()) { + loop_nesting_depth++; + } + + // Update the map if we have not seen this pair (caller, callee) + // before or if this function call is from a greater depth. + if (!known_callees.count(callee) || + call_to_max_depth->at({function.result_id(), callee}) < + loop_nesting_depth) { + call_to_max_depth->insert( + {{function.result_id(), callee}, loop_nesting_depth}); + } + + if (known_callees.count(callee)) { + // We have already considered a call to this function - ignore it. + continue; + } + // Increase the callee's in-degree and add an edge to the call graph. + function_in_degree_[callee]++; + call_graph_edges_[function.result_id()].insert(callee); + // Mark the callee as 'known'. + known_callees.insert(callee); + } + } + } +} + +void CallGraph::ComputeTopologicalOrderOfFunctions() { + // This is an implementation of Kahn’s algorithm for topological sorting. + + // Initialise |functions_in_topological_order_|. + functions_in_topological_order_.clear(); + + // Get a copy of the initial in-degrees of all functions. The algorithm + // involves decrementing these values, hence why we work on a copy. + std::map function_in_degree = GetFunctionInDegree(); + + // Populate a queue with all those function ids with in-degree zero. + std::queue queue; + for (auto& entry : function_in_degree) { + if (entry.second == 0) { + queue.push(entry.first); + } + } + + // Pop ids from the queue, adding them to the sorted order and decreasing the + // in-degrees of their successors. A successor who's in-degree becomes zero + // gets added to the queue. + while (!queue.empty()) { + auto next = queue.front(); + queue.pop(); + functions_in_topological_order_.push_back(next); + for (auto successor : GetDirectCallees(next)) { + assert(function_in_degree.at(successor) > 0 && + "The in-degree cannot be zero if the function is a successor."); + function_in_degree[successor] = function_in_degree.at(successor) - 1; + if (function_in_degree.at(successor) == 0) { + queue.push(successor); + } + } + } + + assert(functions_in_topological_order_.size() == function_in_degree.size() && + "Every function should appear in the sort."); + + return; +} + +void CallGraph::ComputeInterproceduralFunctionCallDepths( + const std::map, uint32_t>& + call_to_max_depth) { + // Find the maximum loop nesting depth that each function can be + // called from, by considering them in topological order. + for (uint32_t function_id : functions_in_topological_order_) { + const auto& callees = call_graph_edges_[function_id]; + + // For each callee, update its maximum loop nesting depth, if a call from + // |function_id| increases it. + for (uint32_t callee : callees) { + uint32_t max_depth_from_this_function = + function_max_loop_nesting_depth_[function_id] + + call_to_max_depth.at({function_id, callee}); + if (function_max_loop_nesting_depth_[callee] < + max_depth_from_this_function) { + function_max_loop_nesting_depth_[callee] = max_depth_from_this_function; + } + } + } +} + +void CallGraph::PushDirectCallees(uint32_t function_id, + std::queue* queue) const { + for (auto callee : GetDirectCallees(function_id)) { + queue->push(callee); + } +} + +std::set CallGraph::GetIndirectCallees(uint32_t function_id) const { + std::set result; + std::queue queue; + PushDirectCallees(function_id, &queue); + + while (!queue.empty()) { + auto next = queue.front(); + queue.pop(); + if (result.count(next)) { + continue; + } + result.insert(next); + PushDirectCallees(next, &queue); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.h new file mode 100644 index 00000000..840b1f12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/call_graph.h @@ -0,0 +1,110 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_CALL_GRAPH_H_ +#define SOURCE_FUZZ_CALL_GRAPH_H_ + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Represents the acyclic call graph of a SPIR-V module. +// The module is assumed to be recursion-free, so there are no cycles in the +// graph. This class is immutable, so it will need to be recomputed if the +// module changes. +class CallGraph { + public: + // Creates a call graph corresponding to the given SPIR-V module. + explicit CallGraph(opt::IRContext* context); + + // Returns a mapping from each function to its number of distinct callers. + const std::map& GetFunctionInDegree() const { + return function_in_degree_; + } + + // Returns the ids of the functions that |function_id| directly invokes. + const std::set& GetDirectCallees(uint32_t function_id) const { + return call_graph_edges_.at(function_id); + } + + // Returns the ids of the functions that |function_id| directly or indirectly + // invokes. + std::set GetIndirectCallees(uint32_t function_id) const; + + // Returns the ids of all the functions in the graph in a topological order, + // in relation to the function calls, which are assumed to be recursion-free. + const std::vector& GetFunctionsInTopologicalOrder() const { + return functions_in_topological_order_; + } + + // Returns the maximum loop nesting depth from which |function_id| can be + // called. This is computed inter-procedurally (i.e. if main calls A from + // depth 2 and A calls B from depth 1, the result will be 3 for A). + // This is a static analysis, so it's not necessarily true that the depth + // returned can actually be reached at runtime. + uint32_t GetMaxCallNestingDepth(uint32_t function_id) const { + return function_max_loop_nesting_depth_.at(function_id); + } + + private: + // Computes |call_graph_edges_| and |function_in_degree_|. For each pair (A, + // B) of functions such that there is at least a function call from A to B, + // adds, to |call_to_max_depth|, a mapping from (A, B) to the maximum loop + // nesting depth (within A) of any such function call. + void BuildGraphAndGetDepthOfFunctionCalls( + opt::IRContext* context, + std::map, uint32_t>* call_to_max_depth); + + // Computes a topological order of the functions in the graph, writing the + // result to |functions_in_topological_order_|. Assumes that the function + // calls are recursion-free and that |function_in_degree_| has been computed. + void ComputeTopologicalOrderOfFunctions(); + + // Computes |function_max_loop_nesting_depth_| so that each function is mapped + // to the maximum loop nesting depth from which it can be called, as described + // by the comment to GetMaxCallNestingDepth. Assumes that |call_graph_edges_| + // and |functions_in_topological_order_| have been computed, and that + // |call_to_max_depth| contains a mapping for each edge in the graph. + void ComputeInterproceduralFunctionCallDepths( + const std::map, uint32_t>& + call_to_max_depth); + + // Pushes the direct callees of |function_id| on to |queue|. + void PushDirectCallees(uint32_t function_id, + std::queue* queue) const; + + // Maps each function id to the ids of its immediate callees. + std::map> call_graph_edges_; + + // For each function id, stores the number of distinct functions that call + // the function. + std::map function_in_degree_; + + // Stores the ids of the functions in a topological order, + // in relation to the function calls, which are assumed to be recursion-free. + std::vector functions_in_topological_order_; + + // For each function id, stores the maximum loop nesting depth that the + // function can be called from. + std::map function_max_loop_nesting_depth_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_CALL_GRAPH_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/comparator_deep_blocks_first.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/comparator_deep_blocks_first.h new file mode 100644 index 00000000..be63d1a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/comparator_deep_blocks_first.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ +#define SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Comparator for blocks, comparing them based on how deep they are nested +// inside selection or loop constructs. Deeper blocks are considered less than +// ones that are not as deep. The blocks are required to be in the same +// function. +class ComparatorDeepBlocksFirst { + public: + explicit ComparatorDeepBlocksFirst(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + + bool operator()(uint32_t bb1, uint32_t bb2) const { + return this->operator()(fuzzerutil::MaybeFindBlock(ir_context_, bb1), + fuzzerutil::MaybeFindBlock(ir_context_, bb2)); + } + + bool operator()(const opt::BasicBlock* bb1, opt::BasicBlock* bb2) const { + assert(bb1 && bb2 && "The blocks must exist."); + assert(bb1->GetParent() == bb2->GetParent() && + "The blocks must be in the same functions."); + return ir_context_->GetStructuredCFGAnalysis()->NestingDepth(bb1->id()) > + ir_context_->GetStructuredCFGAnalysis()->NestingDepth(bb2->id()); + } + + private: + opt::IRContext* ir_context_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.cpp new file mode 100644 index 00000000..0c217345 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/counter_overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +CounterOverflowIdSource::CounterOverflowIdSource(uint32_t first_available_id) + : next_available_id_(first_available_id), issued_ids_() {} + +bool CounterOverflowIdSource::HasOverflowIds() const { return true; } + +uint32_t CounterOverflowIdSource::GetNextOverflowId() { + issued_ids_.insert(next_available_id_); + return next_available_id_++; +} + +const std::unordered_set& +CounterOverflowIdSource::GetIssuedOverflowIds() const { + return issued_ids_; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.h new file mode 100644 index 00000000..852bbd07 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/counter_overflow_id_source.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_ +#define SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_ + +#include "source/fuzz/overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +// A source of overflow ids that uses a counter to provide successive ids from +// a given starting value. +class CounterOverflowIdSource : public OverflowIdSource { + public: + // |first_available_id| is the starting value for the counter. + explicit CounterOverflowIdSource(uint32_t first_available_id); + + // Always returns true. + bool HasOverflowIds() const override; + + // Returns the current counter value and increments the counter. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) We should + // account for the case where the maximum allowed id is reached. + uint32_t GetNextOverflowId() override; + + const std::unordered_set& GetIssuedOverflowIds() const override; + + private: + uint32_t next_available_id_; + + std::unordered_set issued_ids_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_COUNTER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.cpp new file mode 100644 index 00000000..c4673631 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/data_descriptor.h" + +#include + +namespace spvtools { +namespace fuzz { + +protobufs::DataDescriptor MakeDataDescriptor( + uint32_t object, const std::vector& indices) { + protobufs::DataDescriptor result; + result.set_object(object); + for (auto index : indices) { + result.add_index(index); + } + return result; +} + +size_t DataDescriptorHash::operator()( + const protobufs::DataDescriptor* data_descriptor) const { + std::u32string hash; + hash.push_back(data_descriptor->object()); + for (auto an_index : data_descriptor->index()) { + hash.push_back(an_index); + } + return std::hash()(hash); +} + +bool DataDescriptorEquals::operator()( + const protobufs::DataDescriptor* first, + const protobufs::DataDescriptor* second) const { + return first->object() == second->object() && + first->index().size() == second->index().size() && + std::equal(first->index().begin(), first->index().end(), + second->index().begin()); +} + +std::ostream& operator<<(std::ostream& out, + const protobufs::DataDescriptor& data_descriptor) { + out << data_descriptor.object(); + out << "["; + bool first = true; + for (auto index : data_descriptor.index()) { + if (first) { + first = false; + } else { + out << ", "; + } + out << index; + } + out << "]"; + return out; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.h new file mode 100644 index 00000000..f4b8e9c0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/data_descriptor.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_DATA_DESCRIPTOR_H_ +#define SOURCE_FUZZ_DATA_DESCRIPTOR_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" + +namespace spvtools { +namespace fuzz { + +// Factory method to create a data descriptor message from an object id and a +// list of indices. +protobufs::DataDescriptor MakeDataDescriptor( + uint32_t object, const std::vector& indices); + +// Hash function for data descriptors. +struct DataDescriptorHash { + size_t operator()(const protobufs::DataDescriptor* data_descriptor) const; +}; + +// Equality function for data descriptors. +struct DataDescriptorEquals { + bool operator()(const protobufs::DataDescriptor* first, + const protobufs::DataDescriptor* second) const; +}; + +std::ostream& operator<<(std::ostream& out, + const protobufs::DataDescriptor& data_descriptor); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_DATA_DESCRIPTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/equivalence_relation.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/equivalence_relation.h new file mode 100644 index 00000000..a01eac00 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/equivalence_relation.h @@ -0,0 +1,247 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ +#define SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +// A class for representing an equivalence relation on objects of type |T|, +// which should be a value type. The type |T| is required to have a copy +// constructor, and |PointerHashT| and |PointerEqualsT| must be functors +// providing hashing and equality testing functionality for pointers to objects +// of type |T|. +// +// A disjoint-set (a.k.a. union-find or merge-find) data structure is used to +// represent the equivalence relation. Path compression is used. Union by +// rank/size is not used. +// +// Each disjoint set is represented as a tree, rooted at the representative +// of the set. +// +// Getting the representative of a value simply requires chasing parent pointers +// from the value until you reach the root. +// +// Checking equivalence of two elements requires checking that the +// representatives are equal. +// +// Traversing the tree rooted at a value's representative visits the value's +// equivalence class. +// +// |PointerHashT| and |PointerEqualsT| are used to define *equality* between +// values, and otherwise are *not* used to define the equivalence relation +// (except that equal values are equivalent). The equivalence relation is +// constructed by repeatedly adding pairs of (typically non-equal) values that +// are deemed to be equivalent. +// +// For example in an equivalence relation on integers, 1 and 5 might be added +// as equivalent, so that IsEquivalent(1, 5) holds, because they represent +// IDs in a SPIR-V binary that are known to contain the same value at run time, +// but clearly 1 != 5. Since 1 and 1 are equal, IsEquivalent(1, 1) will also +// hold. +// +// Each unique (up to equality) value added to the relation is copied into +// |owned_values_|, so there is one canonical memory address per unique value. +// Uniqueness is ensured by storing (and checking) a set of pointers to these +// values in |value_set_|, which uses |PointerHashT| and |PointerEqualsT|. +// +// |parent_| and |children_| encode the equivalence relation, i.e., the trees. +template +class EquivalenceRelation { + public: + // Requires that |value1| and |value2| are already registered in the + // equivalence relation. Merges the equivalence classes associated with + // |value1| and |value2|. + void MakeEquivalent(const T& value1, const T& value2) { + assert(Exists(value1) && + "Precondition: value1 must already be registered."); + assert(Exists(value2) && + "Precondition: value2 must already be registered."); + + // Look up canonical pointers to each of the values in the value pool. + const T* value1_ptr = *value_set_.find(&value1); + const T* value2_ptr = *value_set_.find(&value2); + + // If the values turn out to be identical, they are already in the same + // equivalence class so there is nothing to do. + if (value1_ptr == value2_ptr) { + return; + } + + // Find the representative for each value's equivalence class, and if they + // are not already in the same class, make one the parent of the other. + const T* representative1 = Find(value1_ptr); + const T* representative2 = Find(value2_ptr); + assert(representative1 && "Representatives should never be null."); + assert(representative2 && "Representatives should never be null."); + if (representative1 != representative2) { + parent_[representative1] = representative2; + children_[representative2].push_back(representative1); + } + } + + // Requires that |value| is not known to the equivalence relation. Registers + // it in its own equivalence class and returns a pointer to the equivalence + // class representative. + const T* Register(const T& value) { + assert(!Exists(value)); + + // This relies on T having a copy constructor. + auto unique_pointer_to_value = MakeUnique(value); + auto pointer_to_value = unique_pointer_to_value.get(); + owned_values_.push_back(std::move(unique_pointer_to_value)); + value_set_.insert(pointer_to_value); + + // Initially say that the value is its own parent and that it has no + // children. + assert(pointer_to_value && "Representatives should never be null."); + parent_[pointer_to_value] = pointer_to_value; + children_[pointer_to_value] = std::vector(); + + return pointer_to_value; + } + + // Returns exactly one representative per equivalence class. + std::vector GetEquivalenceClassRepresentatives() const { + std::vector result; + for (auto& value : owned_values_) { + if (parent_[value.get()] == value.get()) { + result.push_back(value.get()); + } + } + return result; + } + + // Returns pointers to all values in the equivalence class of |value|, which + // must already be part of the equivalence relation. + std::vector GetEquivalenceClass(const T& value) const { + assert(Exists(value)); + + std::vector result; + + // Traverse the tree of values rooted at the representative of the + // equivalence class to which |value| belongs, and collect up all the values + // that are encountered. This constitutes the whole equivalence class. + std::vector stack; + stack.push_back(Find(*value_set_.find(&value))); + while (!stack.empty()) { + const T* item = stack.back(); + result.push_back(item); + stack.pop_back(); + for (auto child : children_[item]) { + stack.push_back(child); + } + } + return result; + } + + // Returns true if and only if |value1| and |value2| are in the same + // equivalence class. Both values must already be known to the equivalence + // relation. + bool IsEquivalent(const T& value1, const T& value2) const { + return Find(&value1) == Find(&value2); + } + + // Returns all values known to be part of the equivalence relation. + std::vector GetAllKnownValues() const { + std::vector result; + for (auto& value : owned_values_) { + result.push_back(value.get()); + } + return result; + } + + // Returns true if and only if |value| is known to be part of the equivalence + // relation. + bool Exists(const T& value) const { + return value_set_.find(&value) != value_set_.end(); + } + + // Returns the representative of the equivalence class of |value|, which must + // already be known to the equivalence relation. This is the 'Find' operation + // in a classic union-find data structure. + const T* Find(const T* value) const { + assert(Exists(*value)); + + // Get the canonical pointer to the value from the value pool. + const T* known_value = *value_set_.find(value); + assert(parent_[known_value] && "Every known value should have a parent."); + + // Compute the result by chasing parents until we find a value that is its + // own parent. + const T* result = known_value; + while (parent_[result] != result) { + result = parent_[result]; + } + assert(result && "Representatives should never be null."); + + // At this point, |result| is the representative of the equivalence class. + // Now perform the 'path compression' optimization by doing another pass up + // the parent chain, setting the parent of each node to be the + // representative, and rewriting children correspondingly. + const T* current = known_value; + while (parent_[current] != result) { + const T* next = parent_[current]; + parent_[current] = result; + children_[result].push_back(current); + auto child_iterator = + std::find(children_[next].begin(), children_[next].end(), current); + assert(child_iterator != children_[next].end() && + "'next' is the parent of 'current', so 'current' should be a " + "child of 'next'"); + children_[next].erase(child_iterator); + current = next; + } + return result; + } + + private: + // Maps every value to a parent. The representative of an equivalence class + // is its own parent. A value's representative can be found by walking its + // chain of ancestors. + // + // Mutable because the intuitively const method, 'Find', performs path + // compression. + mutable std::unordered_map parent_; + + // Stores the children of each value. This allows the equivalence class of + // a value to be calculated by traversing all descendents of the class's + // representative. + // + // Mutable because the intuitively const method, 'Find', performs path + // compression. + mutable std::unordered_map> children_; + + // The values known to the equivalence relation are allocated in + // |owned_values_|, and |value_pool_| provides (via |PointerHashT| and + // |PointerEqualsT|) a means for mapping a value of interest to a pointer + // into an equivalent value in |owned_values_|. + std::unordered_set value_set_; + std::vector> owned_values_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp new file mode 100644 index 00000000..461859bd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp @@ -0,0 +1,236 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/constant_uniform_facts.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +ConstantUniformFacts::ConstantUniformFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +uint32_t ConstantUniformFacts::GetConstantId( + const protobufs::FactConstantUniform& constant_uniform_fact, + uint32_t type_id) const { + auto type = ir_context_->get_type_mgr()->GetType(type_id); + assert(type != nullptr && "Unknown type id."); + const opt::analysis::Constant* known_constant; + if (type->AsInteger()) { + opt::analysis::IntConstant candidate_constant( + type->AsInteger(), GetConstantWords(constant_uniform_fact)); + known_constant = + ir_context_->get_constant_mgr()->FindConstant(&candidate_constant); + } else { + assert( + type->AsFloat() && + "Uniform constant facts are only supported for int and float types."); + opt::analysis::FloatConstant candidate_constant( + type->AsFloat(), GetConstantWords(constant_uniform_fact)); + known_constant = + ir_context_->get_constant_mgr()->FindConstant(&candidate_constant); + } + if (!known_constant) { + return 0; + } + return ir_context_->get_constant_mgr()->FindDeclaredConstant(known_constant, + type_id); +} + +std::vector ConstantUniformFacts::GetConstantWords( + const protobufs::FactConstantUniform& constant_uniform_fact) { + std::vector result; + for (auto constant_word : constant_uniform_fact.constant_word()) { + result.push_back(constant_word); + } + return result; +} + +bool ConstantUniformFacts::DataMatches( + const opt::Instruction& constant_instruction, + const protobufs::FactConstantUniform& constant_uniform_fact) { + assert(constant_instruction.opcode() == spv::Op::OpConstant); + std::vector data_in_constant; + for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) { + data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i)); + } + return data_in_constant == GetConstantWords(constant_uniform_fact); +} + +std::vector +ConstantUniformFacts::GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const { + std::vector result; + std::set already_seen; + for (auto& fact_and_type_id : facts_and_type_ids_) { + if (fact_and_type_id.second != type_id) { + continue; + } + if (auto constant_id = GetConstantId(fact_and_type_id.first, type_id)) { + if (already_seen.find(constant_id) == already_seen.end()) { + result.push_back(constant_id); + already_seen.insert(constant_id); + } + } + } + return result; +} + +std::vector +ConstantUniformFacts::GetUniformDescriptorsForConstant( + uint32_t constant_id) const { + std::vector result; + auto constant_inst = ir_context_->get_def_use_mgr()->GetDef(constant_id); + assert(constant_inst->opcode() == spv::Op::OpConstant && + "The given id must be that of a constant"); + auto type_id = constant_inst->type_id(); + for (auto& fact_and_type_id : facts_and_type_ids_) { + if (fact_and_type_id.second != type_id) { + continue; + } + if (DataMatches(*constant_inst, fact_and_type_id.first)) { + result.emplace_back( + fact_and_type_id.first.uniform_buffer_element_descriptor()); + } + } + return result; +} + +uint32_t ConstantUniformFacts::GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const { + // Consider each fact. + for (auto& fact_and_type : facts_and_type_ids_) { + // Check whether the uniform descriptor associated with the fact matches + // |uniform_descriptor|. + if (UniformBufferElementDescriptorEquals()( + &uniform_descriptor, + &fact_and_type.first.uniform_buffer_element_descriptor())) { + return GetConstantId(fact_and_type.first, fact_and_type.second); + } + } + // No fact associated with the given uniform descriptor was found. + return 0; +} + +std::vector +ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown() const { + std::vector result; + for (auto& fact_and_type : facts_and_type_ids_) { + if (std::find(result.begin(), result.end(), fact_and_type.second) == + result.end()) { + result.push_back(fact_and_type.second); + } + } + return result; +} + +bool ConstantUniformFacts::FloatingPointValueIsSuitable( + const protobufs::FactConstantUniform& fact, uint32_t width) { + const uint32_t kFloatWidth = 32; + const uint32_t kDoubleWidth = 64; + if (width != kFloatWidth && width != kDoubleWidth) { + // Only 32- and 64-bit floating-point types are handled. + return false; + } + std::vector words = GetConstantWords(fact); + if (width == 32) { + float value; + memcpy(&value, words.data(), sizeof(float)); + if (!std::isfinite(value)) { + return false; + } + } else { + double value; + memcpy(&value, words.data(), sizeof(double)); + if (!std::isfinite(value)) { + return false; + } + } + return true; +} + +bool ConstantUniformFacts::MaybeAddFact( + const protobufs::FactConstantUniform& fact) { + // Try to find a unique instruction that declares a variable such that the + // variable is decorated with the descriptor set and binding associated with + // the constant uniform fact. + opt::Instruction* uniform_variable = FindUniformVariable( + fact.uniform_buffer_element_descriptor(), ir_context_, true); + + if (!uniform_variable) { + return false; + } + + assert(spv::Op::OpVariable == uniform_variable->opcode()); + assert(spv::StorageClass::Uniform == + spv::StorageClass(uniform_variable->GetSingleWordInOperand(0))); + + auto should_be_uniform_pointer_type = + ir_context_->get_type_mgr()->GetType(uniform_variable->type_id()); + if (!should_be_uniform_pointer_type->AsPointer()) { + return false; + } + if (should_be_uniform_pointer_type->AsPointer()->storage_class() != + spv::StorageClass::Uniform) { + return false; + } + auto should_be_uniform_pointer_instruction = + ir_context_->get_def_use_mgr()->GetDef(uniform_variable->type_id()); + auto composite_type = + should_be_uniform_pointer_instruction->GetSingleWordInOperand(1); + + auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, composite_type, + fact.uniform_buffer_element_descriptor().index()); + if (!final_element_type_id) { + return false; + } + auto final_element_type = + ir_context_->get_type_mgr()->GetType(final_element_type_id); + assert(final_element_type && + "There should be a type corresponding to this id."); + + if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) { + return false; + } + auto width = final_element_type->AsFloat() + ? final_element_type->AsFloat()->width() + : final_element_type->AsInteger()->width(); + + if (final_element_type->AsFloat() && + !FloatingPointValueIsSuitable(fact, width)) { + return false; + } + + auto required_words = (width + 32 - 1) / 32; + if (static_cast(fact.constant_word().size()) != required_words) { + return false; + } + facts_and_type_ids_.emplace_back( + std::pair( + fact, final_element_type_id)); + return true; +} + +const std::vector>& +ConstantUniformFacts::GetConstantUniformFactsAndTypes() const { + return facts_and_type_ids_; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h new file mode 100644 index 00000000..41d253ea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h @@ -0,0 +1,90 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about uniform constants. +class ConstantUniformFacts { + public: + explicit ConstantUniformFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. + bool MaybeAddFact(const protobufs::FactConstantUniform& fact); + + // See method in FactManager which delegates to this method. + std::vector GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const; + + // See method in FactManager which delegates to this method. + std::vector + GetUniformDescriptorsForConstant(uint32_t constant_id) const; + + // See method in FactManager which delegates to this method. + uint32_t GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) + const; + + // See method in FactManager which delegates to this method. + std::vector GetTypesForWhichUniformValuesAreKnown() const; + + // See method in FactManager which delegates to this method. + const std::vector>& + GetConstantUniformFactsAndTypes() const; + + private: + // Returns true if and only if the words associated with + // |constant_instruction| exactly match the words for the constant associated + // with |constant_uniform_fact|. + static bool DataMatches( + const opt::Instruction& constant_instruction, + const protobufs::FactConstantUniform& constant_uniform_fact); + + // Yields the constant words associated with |constant_uniform_fact|. + static std::vector GetConstantWords( + const protobufs::FactConstantUniform& constant_uniform_fact); + + // Yields the id of a constant of type |type_id| whose data matches the + // constant data in |constant_uniform_fact|, or 0 if no such constant is + // declared. + uint32_t GetConstantId( + const protobufs::FactConstantUniform& constant_uniform_fact, + uint32_t type_id) const; + + // Checks that the width of a floating-point constant is supported, and that + // the constant is finite. + static bool FloatingPointValueIsSuitable( + const protobufs::FactConstantUniform& fact, uint32_t width); + + std::vector> + facts_and_type_ids_; + + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp new file mode 100644 index 00000000..b43b8edf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp @@ -0,0 +1,933 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +size_t DataSynonymAndIdEquationFacts::OperationHash::operator()( + const Operation& operation) const { + std::u32string hash; + hash.push_back(uint32_t(operation.opcode)); + for (auto operand : operation.operands) { + hash.push_back(static_cast(DataDescriptorHash()(operand))); + } + return std::hash()(hash); +} + +bool DataSynonymAndIdEquationFacts::OperationEquals::operator()( + const Operation& first, const Operation& second) const { + // Equal operations require... + // + // Equal opcodes. + if (first.opcode != second.opcode) { + return false; + } + // Matching operand counts. + if (first.operands.size() != second.operands.size()) { + return false; + } + // Equal operands. + for (uint32_t i = 0; i < first.operands.size(); i++) { + if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) { + return false; + } + } + return true; +} + +DataSynonymAndIdEquationFacts::DataSynonymAndIdEquationFacts( + opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool DataSynonymAndIdEquationFacts::MaybeAddFact( + const protobufs::FactDataSynonym& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts) { + if (irrelevant_value_facts.IdIsIrrelevant(fact.data1().object(), + dead_block_facts) || + irrelevant_value_facts.IdIsIrrelevant(fact.data2().object(), + dead_block_facts)) { + // Irrelevant ids cannot be synonymous with other ids. + return false; + } + + // Add the fact, including all facts relating sub-components of the data + // descriptors that are involved. + AddDataSynonymFactRecursive(fact.data1(), fact.data2()); + return true; +} + +bool DataSynonymAndIdEquationFacts::MaybeAddFact( + const protobufs::FactIdEquation& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts) { + if (irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id(), dead_block_facts)) { + // Irrelevant ids cannot participate in IdEquation facts. + return false; + } + + for (auto id : fact.rhs_id()) { + if (irrelevant_value_facts.IdIsIrrelevant(id, dead_block_facts)) { + // Irrelevant ids cannot participate in IdEquation facts. + return false; + } + } + + protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {}); + + // Register the LHS in the equivalence relation if needed. + RegisterDataDescriptor(lhs_dd); + + // Get equivalence class representatives for all ids used on the RHS of the + // equation. + std::vector rhs_dds; + for (auto rhs_id : fact.rhs_id()) { + // Register a data descriptor based on this id in the equivalence relation + // if needed, and then record the equivalence class representative. + rhs_dds.push_back(RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {}))); + } + + // Now add the fact. + AddEquationFactRecursive(lhs_dd, static_cast(fact.opcode()), + rhs_dds); + return true; +} + +DataSynonymAndIdEquationFacts::OperationSet +DataSynonymAndIdEquationFacts::GetEquations( + const protobufs::DataDescriptor* lhs) const { + auto existing = id_equations_.find(lhs); + if (existing == id_equations_.end()) { + return OperationSet(); + } + return existing->second; +} + +void DataSynonymAndIdEquationFacts::AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, spv::Op opcode, + const std::vector& rhs_dds) { + assert(synonymous_.Exists(lhs_dd) && + "The LHS must be known to the equivalence relation."); + for (auto rhs_dd : rhs_dds) { + // Keep release compilers happy. + (void)(rhs_dd); + assert(synonymous_.Exists(*rhs_dd) && + "The RHS operands must be known to the equivalence relation."); + } + + auto lhs_dd_representative = synonymous_.Find(&lhs_dd); + + if (id_equations_.count(lhs_dd_representative) == 0) { + // We have not seen an equation with this LHS before, so associate the LHS + // with an initially empty set. + id_equations_.insert({lhs_dd_representative, OperationSet()}); + } + + { + auto existing_equations = id_equations_.find(lhs_dd_representative); + assert(existing_equations != id_equations_.end() && + "A set of operations should be present, even if empty."); + + Operation new_operation = {opcode, rhs_dds}; + if (existing_equations->second.count(new_operation)) { + // This equation is known, so there is nothing further to be done. + return; + } + // Add the equation to the set of known equations. + existing_equations->second.insert(new_operation); + } + + // Now try to work out corollaries implied by the new equation and existing + // facts. + switch (opcode) { + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + ComputeConversionDataSynonymFacts(*rhs_dds[0]); + break; + case spv::Op::OpBitcast: { + assert(DataDescriptorsAreWellFormedAndComparable(lhs_dd, *rhs_dds[0]) && + "Operands of OpBitcast equation fact must have compatible types"); + if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) { + AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0]); + } + } break; + case spv::Op::OpIAdd: { + // Equation form: "a = b + c" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == spv::Op::OpISub) { + // Equation form: "a = (d - e) + c" + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) { + // Equation form: "a = (d - c) + c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + } + for (const auto& equation : GetEquations(rhs_dds[1])) { + if (equation.opcode == spv::Op::OpISub) { + // Equation form: "a = b + (d - e)" + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) { + // Equation form: "a = b + (d - b)" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + } + break; + } + case spv::Op::OpISub: { + // Equation form: "a = b - c" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == spv::Op::OpIAdd) { + // Equation form: "a = (d + e) - c" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) { + // Equation form: "a = (c + e) - c" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1]); + } + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) { + // Equation form: "a = (d + c) - c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + + if (equation.opcode == spv::Op::OpISub) { + // Equation form: "a = (d - e) - c" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) { + // Equation form: "a = (c - e) - c" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate, + {equation.operands[1]}); + } + } + } + + for (const auto& equation : GetEquations(rhs_dds[1])) { + if (equation.opcode == spv::Op::OpIAdd) { + // Equation form: "a = b - (d + e)" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) { + // Equation form: "a = b - (b + e)" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate, + {equation.operands[1]}); + } + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) { + // Equation form: "a = b - (d + b)" + // We can thus infer "a = -d" + AddEquationFactRecursive(lhs_dd, spv::Op::OpSNegate, + {equation.operands[0]}); + } + } + if (equation.opcode == spv::Op::OpISub) { + // Equation form: "a = b - (d - e)" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) { + // Equation form: "a = b - (b - e)" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1]); + } + } + } + break; + } + case spv::Op::OpLogicalNot: + case spv::Op::OpSNegate: { + // Equation form: "a = !b" or "a = -b" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == opcode) { + // Equation form: "a = !!b" or "a = -(-b)" + // We can thus infer "a = b" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + break; + } + default: + break; + } +} + +void DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + assert((!ObjectStillExists(dd1) || !ObjectStillExists(dd2) || + DataDescriptorsAreWellFormedAndComparable(dd1, dd2)) && + "Mismatched data descriptors."); + + // Record that the data descriptors provided in the fact are equivalent. + MakeEquivalent(dd1, dd2); + assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) && + "|dd1| and |dd2| must have a single representative"); + + // Compute various corollary facts. + + // |dd1| and |dd2| belong to the same equivalence class so it doesn't matter + // which one we use here. + ComputeConversionDataSynonymFacts(dd1); + + ComputeCompositeDataSynonymFacts(dd1, dd2); +} + +void DataSynonymAndIdEquationFacts::ComputeConversionDataSynonymFacts( + const protobufs::DataDescriptor& dd) { + assert(synonymous_.Exists(dd) && + "|dd| should've been registered in the equivalence relation"); + + if (!ObjectStillExists(dd)) { + // The object is gone from the module, so we cannot proceed. + return; + } + + const auto* type = + ir_context_->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd.object()), + dd.index())); + assert(type && "Data descriptor has invalid type"); + + if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) || + type->AsInteger()) { + // If there exist equation facts of the form |%a = opcode %representative| + // and |%b = opcode %representative| where |opcode| is either OpConvertSToF + // or OpConvertUToF, then |a| and |b| are synonymous. + std::vector convert_s_to_f_lhs; + std::vector convert_u_to_f_lhs; + + for (const auto& fact : id_equations_) { + auto equivalence_class = synonymous_.GetEquivalenceClass(*fact.first); + auto dd_it = + std::find_if(equivalence_class.begin(), equivalence_class.end(), + [this](const protobufs::DataDescriptor* a) { + return ObjectStillExists(*a); + }); + if (dd_it == equivalence_class.end()) { + // Skip |equivalence_class| if it has no valid ids. + continue; + } + + for (const auto& equation : fact.second) { + if (synonymous_.IsEquivalent(*equation.operands[0], dd)) { + if (equation.opcode == spv::Op::OpConvertSToF) { + convert_s_to_f_lhs.push_back(*dd_it); + } else if (equation.opcode == spv::Op::OpConvertUToF) { + convert_u_to_f_lhs.push_back(*dd_it); + } + } + } + } + + // We use pointers in the initializer list here since otherwise we would + // copy memory from these vectors. + for (const auto* synonyms : {&convert_s_to_f_lhs, &convert_u_to_f_lhs}) { + for (const auto* synonym_a : *synonyms) { + for (const auto* synonym_b : *synonyms) { + // DataDescriptorsAreWellFormedAndComparable will be called in the + // AddDataSynonymFactRecursive method. + if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b)) { + // |synonym_a| and |synonym_b| have compatible types - they are + // synonymous. + AddDataSynonymFactRecursive(*synonym_a, *synonym_b); + } + } + } + } + } +} + +void DataSynonymAndIdEquationFacts::ComputeCompositeDataSynonymFacts( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + // Check whether this is a synonym about composite objects. If it is, + // we can recursively add synonym facts about their associated sub-components. + + // Get the type of the object referred to by the first data descriptor in the + // synonym fact. + uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, + ir_context_->get_def_use_mgr()->GetDef(dd1.object())->type_id(), + dd1.index()); + auto type = ir_context_->get_type_mgr()->GetType(type_id); + auto type_instruction = ir_context_->get_def_use_mgr()->GetDef(type_id); + assert(type != nullptr && + "Invalid data synonym fact: one side has an unknown type."); + + // Check whether the type is composite, recording the number of elements + // associated with the composite if so. + uint32_t num_composite_elements; + if (type->AsArray()) { + num_composite_elements = + fuzzerutil::GetArraySize(*type_instruction, ir_context_); + } else if (type->AsMatrix()) { + num_composite_elements = type->AsMatrix()->element_count(); + } else if (type->AsStruct()) { + num_composite_elements = + fuzzerutil::GetNumberOfStructMembers(*type_instruction); + } else if (type->AsVector()) { + num_composite_elements = type->AsVector()->element_count(); + } else { + // The type is not a composite, so return. + return; + } + + // If the fact has the form: + // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n] + // then for each composite index i, we add a fact of the form: + // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i] + // + // However, to avoid adding a large number of synonym facts e.g. in the case + // of arrays, we bound the number of composite elements to which this is + // applied. Nevertheless, we always add a synonym fact for the final + // components, as this may be an interesting edge case. + + // The bound on the number of indices of the composite pair to note as being + // synonymous. + const uint32_t kCompositeElementBound = 10; + + for (uint32_t i = 0; i < num_composite_elements;) { + std::vector extended_indices1 = + fuzzerutil::RepeatedFieldToVector(dd1.index()); + extended_indices1.push_back(i); + std::vector extended_indices2 = + fuzzerutil::RepeatedFieldToVector(dd2.index()); + extended_indices2.push_back(i); + AddDataSynonymFactRecursive( + MakeDataDescriptor(dd1.object(), extended_indices1), + MakeDataDescriptor(dd2.object(), extended_indices2)); + + if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) { + // We have not reached the bound yet, or have already skipped ahead to the + // last element, so increment the loop counter as standard. + i++; + } else { + // We have reached the bound, so skip ahead to the last element. + assert(i == kCompositeElementBound - 1); + i = num_composite_elements - 1; + } + } +} + +void DataSynonymAndIdEquationFacts::ComputeClosureOfFacts( + uint32_t maximum_equivalence_class_size) { + // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct + // data descriptors that describe objects of the same composite type, and that + // the composite type is comprised of k components. + // + // For example, if m is a mat4x4 and v a vec4, we might consider: + // m[2]: describes the 2nd column of m, a vec4 + // v[]: describes all of v, a vec4 + // + // Suppose that we know, for every 0 <= i < k, that the fact: + // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i] + // holds - i.e. that the children of the two data descriptors are synonymous. + // + // Then we can conclude that: + // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n] + // holds. + // + // For instance, if we have the facts: + // m[2, 0] == v[0] + // m[2, 1] == v[1] + // m[2, 2] == v[2] + // m[2, 3] == v[3] + // then we can conclude that: + // m[2] == v. + // + // This method repeatedly searches the equivalence relation of data + // descriptors, deducing and adding such facts, until a pass over the + // relation leads to no further facts being deduced. + + // The method relies on working with pairs of data descriptors, and in + // particular being able to hash and compare such pairs. + + using DataDescriptorPair = + std::pair; + + struct DataDescriptorPairHash { + std::size_t operator()(const DataDescriptorPair& pair) const { + return DataDescriptorHash()(&pair.first) ^ + DataDescriptorHash()(&pair.second); + } + }; + + struct DataDescriptorPairEquals { + bool operator()(const DataDescriptorPair& first, + const DataDescriptorPair& second) const { + return (DataDescriptorEquals()(&first.first, &second.first) && + DataDescriptorEquals()(&first.second, &second.second)) || + (DataDescriptorEquals()(&first.first, &second.second) && + DataDescriptorEquals()(&first.second, &second.first)); + } + }; + + // This map records, for a given pair of composite data descriptors of the + // same type, all the indices at which the data descriptors are known to be + // synonymous. A pair is a key to this map only if we have observed that + // the pair are synonymous at *some* index, but not at *all* indices. + // Once we find that a pair of data descriptors are equivalent at all indices + // we record the fact that they are synonymous and remove them from the map. + // + // Using the m and v example from above, initially the pair (m[2], v) would + // not be a key to the map. If we find that m[2, 2] == v[2] holds, we would + // add an entry: + // (m[2], v) -> [false, false, true, false] + // to record that they are synonymous at index 2. If we then find that + // m[2, 0] == v[0] holds, we would update this entry to: + // (m[2], v) -> [true, false, true, false] + // If we then find that m[2, 3] == v[3] holds, we would update this entry to: + // (m[2], v) -> [true, false, true, true] + // Finally, if we then find that m[2, 1] == v[1] holds, which would make the + // boolean vector true at every index, we would add the fact: + // m[2] == v + // to the equivalence relation and remove (m[2], v) from the map. + std::unordered_map, + DataDescriptorPairHash, DataDescriptorPairEquals> + candidate_composite_synonyms; + + // We keep looking for new facts until we perform a complete pass over the + // equivalence relation without finding any new facts. + while (closure_computation_required_) { + // We have not found any new facts yet during this pass; we set this to + // 'true' if we do find a new fact. + closure_computation_required_ = false; + + // Consider each class in the equivalence relation. + for (auto representative : + synonymous_.GetEquivalenceClassRepresentatives()) { + auto equivalence_class = synonymous_.GetEquivalenceClass(*representative); + + if (equivalence_class.size() > maximum_equivalence_class_size) { + // This equivalence class is larger than the maximum size we are willing + // to consider, so we skip it. This potentially leads to missed fact + // deductions, but avoids excessive runtime for closure computation. + continue; + } + + // Consider every data descriptor in the equivalence class. + for (auto dd1_it = equivalence_class.begin(); + dd1_it != equivalence_class.end(); ++dd1_it) { + // If this data descriptor has no indices then it does not have the form + // obj_1[a_1, ..., a_m, i], so move on. + auto dd1 = *dd1_it; + if (dd1->index_size() == 0) { + continue; + } + + // Consider every other data descriptor later in the equivalence class + // (due to symmetry, there is no need to compare with previous data + // descriptors). + auto dd2_it = dd1_it; + for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) { + auto dd2 = *dd2_it; + // If this data descriptor has no indices then it does not have the + // form obj_2[b_1, ..., b_n, i], so move on. + if (dd2->index_size() == 0) { + continue; + } + + // At this point we know that: + // - |dd1| has the form obj_1[a_1, ..., a_m, i] + // - |dd2| has the form obj_2[b_1, ..., b_n, j] + assert(dd1->index_size() > 0 && dd2->index_size() > 0 && + "Control should not reach here if either data descriptor has " + "no indices."); + + // We are only interested if i == j. + if (dd1->index(dd1->index_size() - 1) != + dd2->index(dd2->index_size() - 1)) { + continue; + } + + const uint32_t common_final_index = dd1->index(dd1->index_size() - 1); + + // Make data descriptors |dd1_prefix| and |dd2_prefix| for + // obj_1[a_1, ..., a_m] + // and + // obj_2[b_1, ..., b_n] + // These are the two data descriptors we might be getting closer to + // deducing as being synonymous, due to knowing that they are + // synonymous when extended by a particular index. + protobufs::DataDescriptor dd1_prefix; + dd1_prefix.set_object(dd1->object()); + for (uint32_t i = 0; i < static_cast(dd1->index_size() - 1); + i++) { + dd1_prefix.add_index(dd1->index(i)); + } + protobufs::DataDescriptor dd2_prefix; + dd2_prefix.set_object(dd2->object()); + for (uint32_t i = 0; i < static_cast(dd2->index_size() - 1); + i++) { + dd2_prefix.add_index(dd2->index(i)); + } + assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) && + "By construction these prefixes should be different."); + + // If we already know that these prefixes are synonymous, move on. + if (synonymous_.Exists(dd1_prefix) && + synonymous_.Exists(dd2_prefix) && + synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) { + continue; + } + if (!ObjectStillExists(*dd1) || !ObjectStillExists(*dd2)) { + // The objects are not both available in the module, so we cannot + // investigate the types of the associated data descriptors; we need + // to move on. + continue; + } + // Get the type of obj_1 + auto dd1_root_type_id = + fuzzerutil::GetTypeId(ir_context_, dd1->object()); + // Use this type, together with a_1, ..., a_m, to get the type of + // obj_1[a_1, ..., a_m]. + auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, dd1_root_type_id, dd1_prefix.index()); + + // Similarly, get the type of obj_2 and use it to get the type of + // obj_2[b_1, ..., b_n]. + auto dd2_root_type_id = + fuzzerutil::GetTypeId(ir_context_, dd2->object()); + auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, dd2_root_type_id, dd2_prefix.index()); + + // If the types of dd1_prefix and dd2_prefix are not the same, they + // cannot be synonymous. + if (dd1_prefix_type != dd2_prefix_type) { + continue; + } + + // At this point, we know we have synonymous data descriptors of the + // form: + // obj_1[a_1, ..., a_m, i] + // obj_2[b_1, ..., b_n, i] + // with the same last_index i, such that: + // obj_1[a_1, ..., a_m] + // and + // obj_2[b_1, ..., b_n] + // have the same type. + + // Work out how many components there are in the (common) commposite + // type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n]. + // This depends on whether the composite type is array, matrix, struct + // or vector. + uint32_t num_components_in_composite; + auto composite_type = + ir_context_->get_type_mgr()->GetType(dd1_prefix_type); + auto composite_type_instruction = + ir_context_->get_def_use_mgr()->GetDef(dd1_prefix_type); + if (composite_type->AsArray()) { + num_components_in_composite = fuzzerutil::GetArraySize( + *composite_type_instruction, ir_context_); + if (num_components_in_composite == 0) { + // This indicates that the array has an unknown size, in which + // case we cannot be sure we have matched all of its elements with + // synonymous elements of another array. + continue; + } + } else if (composite_type->AsMatrix()) { + num_components_in_composite = + composite_type->AsMatrix()->element_count(); + } else if (composite_type->AsStruct()) { + num_components_in_composite = fuzzerutil::GetNumberOfStructMembers( + *composite_type_instruction); + } else { + assert(composite_type->AsVector()); + num_components_in_composite = + composite_type->AsVector()->element_count(); + } + + // We are one step closer to being able to say that |dd1_prefix| and + // |dd2_prefix| are synonymous. + DataDescriptorPair candidate_composite_synonym(dd1_prefix, + dd2_prefix); + + // We look up what we already know about this pair. + auto existing_entry = + candidate_composite_synonyms.find(candidate_composite_synonym); + + if (existing_entry == candidate_composite_synonyms.end()) { + // If this is the first time we have seen the pair, we make a vector + // of size |num_components_in_composite| that is 'true' at the + // common final index associated with |dd1| and |dd2|, and 'false' + // everywhere else, and register this vector as being associated + // with the pair. + std::vector entry; + for (uint32_t i = 0; i < num_components_in_composite; i++) { + entry.push_back(i == common_final_index); + } + candidate_composite_synonyms[candidate_composite_synonym] = entry; + existing_entry = + candidate_composite_synonyms.find(candidate_composite_synonym); + } else { + // We have seen this pair of data descriptors before, and we now + // know that they are synonymous at one further index, so we + // update the entry to record that. + existing_entry->second[common_final_index] = true; + } + assert(existing_entry != candidate_composite_synonyms.end()); + + // Check whether |dd1_prefix| and |dd2_prefix| are now known to match + // at every sub-component. + bool all_components_match = true; + for (uint32_t i = 0; i < num_components_in_composite; i++) { + if (!existing_entry->second[i]) { + all_components_match = false; + break; + } + } + if (all_components_match) { + // The two prefixes match on all sub-components, so we know that + // they are synonymous. We add this fact *non-recursively*, as we + // have deduced that |dd1_prefix| and |dd2_prefix| are synonymous + // by observing that all their sub-components are already + // synonymous. + assert(DataDescriptorsAreWellFormedAndComparable(dd1_prefix, + dd2_prefix)); + MakeEquivalent(dd1_prefix, dd2_prefix); + // Now that we know this pair of data descriptors are synonymous, + // there is no point recording how close they are to being + // synonymous. + candidate_composite_synonyms.erase(candidate_composite_synonym); + } + } + } + } + } +} + +void DataSynonymAndIdEquationFacts::MakeEquivalent( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + // Register the data descriptors if they are not already known to the + // equivalence relation. + RegisterDataDescriptor(dd1); + RegisterDataDescriptor(dd2); + + if (synonymous_.IsEquivalent(dd1, dd2)) { + // The data descriptors are already known to be equivalent, so there is + // nothing to do. + return; + } + + // We must make the data descriptors equivalent, and also make sure any + // equation facts known about their representatives are merged. + + // Record the original equivalence class representatives of the data + // descriptors. + auto dd1_original_representative = synonymous_.Find(&dd1); + auto dd2_original_representative = synonymous_.Find(&dd2); + + // Make the data descriptors equivalent. + synonymous_.MakeEquivalent(dd1, dd2); + // As we have updated the equivalence relation, we might be able to deduce + // more facts by performing a closure computation, so we record that such a + // computation is required. + closure_computation_required_ = true; + + // At this point, exactly one of |dd1_original_representative| and + // |dd2_original_representative| will be the representative of the combined + // equivalence class. We work out which one of them is still the class + // representative and which one is no longer the class representative. + + auto still_representative = synonymous_.Find(dd1_original_representative) == + dd1_original_representative + ? dd1_original_representative + : dd2_original_representative; + auto no_longer_representative = + still_representative == dd1_original_representative + ? dd2_original_representative + : dd1_original_representative; + + assert(no_longer_representative != still_representative && + "The current and former representatives cannot be the same."); + + // We now need to add all equations about |no_longer_representative| to the + // set of equations known about |still_representative|. + + // Get the equations associated with |no_longer_representative|. + auto no_longer_representative_id_equations = + id_equations_.find(no_longer_representative); + if (no_longer_representative_id_equations != id_equations_.end()) { + // There are some equations to transfer. There might not yet be any + // equations about |still_representative|; create an empty set of equations + // if this is the case. + if (!id_equations_.count(still_representative)) { + id_equations_.insert({still_representative, OperationSet()}); + } + auto still_representative_id_equations = + id_equations_.find(still_representative); + assert(still_representative_id_equations != id_equations_.end() && + "At this point there must be a set of equations."); + // Add all the equations known about |no_longer_representative| to the set + // of equations known about |still_representative|. + still_representative_id_equations->second.insert( + no_longer_representative_id_equations->second.begin(), + no_longer_representative_id_equations->second.end()); + } + // Delete the no longer-relevant equations about |no_longer_representative|. + id_equations_.erase(no_longer_representative); +} + +const protobufs::DataDescriptor* +DataSynonymAndIdEquationFacts::RegisterDataDescriptor( + const protobufs::DataDescriptor& dd) { + return synonymous_.Exists(dd) ? synonymous_.Find(&dd) + : synonymous_.Register(dd); +} + +bool DataSynonymAndIdEquationFacts::DataDescriptorsAreWellFormedAndComparable( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const { + if (!ObjectStillExists(dd1) || !ObjectStillExists(dd2)) { + // We trivially return true if one or other of the objects associated with + // the data descriptors is gone. + return true; + } + + auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd1.object()), + dd1.index()); + auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd2.object()), + dd2.index()); + // The end types of the data descriptors must exist. + if (end_type_id_1 == 0 || end_type_id_2 == 0) { + return false; + } + // Neither end type is allowed to be void. + if (ir_context_->get_def_use_mgr()->GetDef(end_type_id_1)->opcode() == + spv::Op::OpTypeVoid || + ir_context_->get_def_use_mgr()->GetDef(end_type_id_2)->opcode() == + spv::Op::OpTypeVoid) { + return false; + } + // If the end types are the same, the data descriptors are comparable. + if (end_type_id_1 == end_type_id_2) { + return true; + } + // Otherwise they are only comparable if they are integer scalars or integer + // vectors that differ only in signedness. + + // Get both types. + const auto* type_a = ir_context_->get_type_mgr()->GetType(end_type_id_1); + const auto* type_b = ir_context_->get_type_mgr()->GetType(end_type_id_2); + assert(type_a && type_b && "Data descriptors have invalid type(s)"); + + // If both types are numerical or vectors of numerical components, then they + // are compatible if they have the same number of components and the same bit + // count per component. + + if (type_a->AsVector() && type_b->AsVector()) { + const auto* vector_a = type_a->AsVector(); + const auto* vector_b = type_b->AsVector(); + + if (vector_a->element_count() != vector_b->element_count() || + vector_a->element_type()->AsBool() || + vector_b->element_type()->AsBool()) { + // The case where both vectors have boolean elements and the same number + // of components is handled by the direct equality check earlier. + // You can't have multiple identical boolean vector types. + return false; + } + + type_a = vector_a->element_type(); + type_b = vector_b->element_type(); + } + + auto get_bit_count_for_numeric_type = + [](const opt::analysis::Type& type) -> uint32_t { + if (const auto* integer = type.AsInteger()) { + return integer->width(); + } else if (const auto* floating = type.AsFloat()) { + return floating->width(); + } else { + assert(false && "|type| must be a numerical type"); + return 0; + } + }; + + // Checks that both |type_a| and |type_b| are either numerical or vectors of + // numerical components and have the same number of bits. + return (type_a->AsInteger() || type_a->AsFloat()) && + (type_b->AsInteger() || type_b->AsFloat()) && + (get_bit_count_for_numeric_type(*type_a) == + get_bit_count_for_numeric_type(*type_b)); +} + +std::vector +DataSynonymAndIdEquationFacts::GetSynonymsForId(uint32_t id) const { + return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {})); +} + +std::vector +DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const { + std::vector result; + if (synonymous_.Exists(data_descriptor)) { + for (auto dd : synonymous_.GetEquivalenceClass(data_descriptor)) { + // There may be data descriptors in the equivalence class whose base + // objects have been removed from the module. We do not expose these + // data descriptors to clients of the fact manager. + if (ObjectStillExists(*dd)) { + result.push_back(dd); + } + } + } + return result; +} + +std::vector +DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown() const { + std::vector result; + for (auto& data_descriptor : synonymous_.GetAllKnownValues()) { + // We skip any data descriptors whose base objects no longer exist in the + // module, and we restrict attention to data descriptors for plain ids, + // which have no indices. + if (ObjectStillExists(*data_descriptor) && + data_descriptor->index().empty()) { + result.push_back(data_descriptor->object()); + } + } + return result; +} + +std::vector +DataSynonymAndIdEquationFacts::GetAllKnownSynonyms() const { + std::vector result; + for (const auto* dd : synonymous_.GetAllKnownValues()) { + if (ObjectStillExists(*dd)) { + result.push_back(dd); + } + } + return result; +} + +bool DataSynonymAndIdEquationFacts::IsSynonymous( + const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const { + return synonymous_.Exists(data_descriptor1) && + synonymous_.Exists(data_descriptor2) && + synonymous_.IsEquivalent(data_descriptor1, data_descriptor2); +} + +bool DataSynonymAndIdEquationFacts::ObjectStillExists( + const protobufs::DataDescriptor& dd) const { + return ir_context_->get_def_use_mgr()->GetDef(dd.object()) != nullptr; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h new file mode 100644 index 00000000..36d92ca7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h @@ -0,0 +1,185 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ + +#include +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/equivalence_relation.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// Forward reference to the DeadBlockFacts class. +class DeadBlockFacts; +// Forward reference to the IrrelevantValueFacts class. +class IrrelevantValueFacts; + +// The purpose of this class is to group the fields and data used to represent +// facts about data synonyms and id equations. +class DataSynonymAndIdEquationFacts { + public: + explicit DataSynonymAndIdEquationFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // neither |fact.data1()| nor |fact.data2()| contain an + // irrelevant id. Otherwise, returns false. |dead_block_facts| and + // |irrelevant_value_facts| are passed for consistency checks. + bool MaybeAddFact(const protobufs::FactDataSynonym& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts); + + // See method in FactManager which delegates to this method. Returns true if + // neither |fact.lhs_id()| nor any of |fact.rhs_id()| is irrelevant. Returns + // false otherwise. |dead_block_facts| and |irrelevant_value_facts| are passed + // for consistency checks. + bool MaybeAddFact(const protobufs::FactIdEquation& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts); + + // See method in FactManager which delegates to this method. + std::vector GetSynonymsForId( + uint32_t id) const; + + // See method in FactManager which delegates to this method. + std::vector GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const; + + // See method in FactManager which delegates to this method. + std::vector GetIdsForWhichSynonymsAreKnown() const; + + // See method in FactManager which delegates to this method. + std::vector GetAllKnownSynonyms() const; + + // See method in FactManager which delegates to this method. + bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const; + + // See method in FactManager which delegates to this method. + void ComputeClosureOfFacts(uint32_t maximum_equivalence_class_size); + + private: + // This helper struct represents the right hand side of an equation as an + // operator applied to a number of data descriptor operands. + struct Operation { + spv::Op opcode; + std::vector operands; + }; + + // Hashing for operations, to allow deterministic unordered sets. + struct OperationHash { + size_t operator()(const Operation& operation) const; + }; + + // Equality for operations, to allow deterministic unordered sets. + struct OperationEquals { + bool operator()(const Operation& first, const Operation& second) const; + }; + + using OperationSet = + std::unordered_set; + + // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses + // into sub-components of the data descriptors, if they are composites, to + // record that their components are pairwise-synonymous. + void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Computes various corollary facts from the data descriptor |dd| if members + // of its equivalence class participate in equation facts with OpConvert* + // opcodes. The descriptor should be registered in the equivalence relation. + void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd); + + // Recurses into sub-components of the data descriptors, if they are + // composites, to record that their components are pairwise-synonymous. + void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets + // of equations that are known about them. + void MakeEquivalent(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Registers a data descriptor in the equivalence relation if it hasn't been + // registered yet, and returns its representative. + const protobufs::DataDescriptor* RegisterDataDescriptor( + const protobufs::DataDescriptor& dd); + + // Trivially returns true if either |dd1| or |dd2|'s objects are not present + // in the module. + // + // Otherwise, returns true if and only if |dd1| and |dd2| are valid data + // descriptors whose associated data have compatible types. Two types are + // compatible if: + // - they are the same + // - they both are numerical or vectors of numerical components with the same + // number of components and the same bit count per component + bool DataDescriptorsAreWellFormedAndComparable( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const; + + OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const; + + // Requires that |lhs_dd| and every element of |rhs_dds| is present in the + // |synonymous_| equivalence relation, but is not necessarily its own + // representative. Records the fact that the equation + // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any + // corollaries, in the form of data synonym or equation facts, that follow + // from this and other known facts. + void AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, spv::Op opcode, + const std::vector& rhs_dds); + + // Returns true if and only if |dd.object()| still exists in the module. + bool ObjectStillExists(const protobufs::DataDescriptor& dd) const; + + // The data descriptors that are known to be synonymous with one another are + // captured by this equivalence relation. + EquivalenceRelation + synonymous_; + + // When a new synonym fact is added, it may be possible to deduce further + // synonym facts by computing a closure of all known facts. However, this is + // an expensive operation, so it should be performed sparingly and only there + // is some chance of new facts being deduced. This boolean tracks whether a + // closure computation is required - i.e., whether a new fact has been added + // since the last time such a computation was performed. + bool closure_computation_required_ = false; + + // Represents a set of equations on data descriptors as a map indexed by + // left-hand-side, mapping a left-hand-side to a set of operations, each of + // which (together with the left-hand-side) defines an equation. + // + // All data descriptors occurring in equations are required to be present in + // the |synonymous_| equivalence relation, and to be their own representatives + // in that relation. + std::unordered_map + id_equations_; + + // Pointer to the SPIR-V module we store facts about. + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp new file mode 100644 index 00000000..f3e0ef8e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/dead_block_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +DeadBlockFacts::DeadBlockFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool DeadBlockFacts::MaybeAddFact(const protobufs::FactBlockIsDead& fact) { + if (!fuzzerutil::MaybeFindBlock(ir_context_, fact.block_id())) { + return false; + } + + dead_block_ids_.insert(fact.block_id()); + return true; +} + +bool DeadBlockFacts::BlockIsDead(uint32_t block_id) const { + return dead_block_ids_.count(block_id) != 0; +} + +const std::unordered_set& DeadBlockFacts::GetDeadBlocks() const { + return dead_block_ids_; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h new file mode 100644 index 00000000..8ac5c3ca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about data blocks. +class DeadBlockFacts { + public: + explicit DeadBlockFacts(opt::IRContext* ir_context); + + // Marks |fact.block_id()| as being dead. Returns true if |fact.block_id()| + // represents a result id of some OpLabel instruction in |ir_context_|. + // Returns false otherwise. + bool MaybeAddFact(const protobufs::FactBlockIsDead& fact); + + // See method in FactManager which delegates to this method. + bool BlockIsDead(uint32_t block_id) const; + + // Returns a set of all the block ids that have been declared dead. + const std::unordered_set& GetDeadBlocks() const; + + private: + std::unordered_set dead_block_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp new file mode 100644 index 00000000..c99f690d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp @@ -0,0 +1,279 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fact_manager.h" + +#include +#include + +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace { + +std::string ToString(const protobufs::FactConstantUniform& fact) { + std::stringstream stream; + stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set() + << ", " << fact.uniform_buffer_element_descriptor().binding() << ")["; + + bool first = true; + for (auto index : fact.uniform_buffer_element_descriptor().index()) { + if (first) { + first = false; + } else { + stream << ", "; + } + stream << index; + } + + stream << "] == ["; + + first = true; + for (auto constant_word : fact.constant_word()) { + if (first) { + first = false; + } else { + stream << ", "; + } + stream << constant_word; + } + + stream << "]"; + return stream.str(); +} + +std::string ToString(const protobufs::FactDataSynonym& fact) { + std::stringstream stream; + stream << fact.data1() << " = " << fact.data2(); + return stream.str(); +} + +std::string ToString(const protobufs::FactIdEquation& fact) { + std::stringstream stream; + stream << fact.lhs_id(); + stream << " " << fact.opcode(); + for (auto rhs_id : fact.rhs_id()) { + stream << " " << rhs_id; + } + return stream.str(); +} + +std::string ToString(const protobufs::Fact& fact) { + switch (fact.fact_case()) { + case protobufs::Fact::kConstantUniformFact: + return ToString(fact.constant_uniform_fact()); + case protobufs::Fact::kDataSynonymFact: + return ToString(fact.data_synonym_fact()); + case protobufs::Fact::kIdEquationFact: + return ToString(fact.id_equation_fact()); + default: + assert(false && "Stringification not supported for this fact."); + return ""; + } +} + +} // namespace + +FactManager::FactManager(opt::IRContext* ir_context) + : constant_uniform_facts_(ir_context), + data_synonym_and_id_equation_facts_(ir_context), + dead_block_facts_(ir_context), + livesafe_function_facts_(ir_context), + irrelevant_value_facts_(ir_context) {} + +void FactManager::AddInitialFacts(const MessageConsumer& message_consumer, + const protobufs::FactSequence& facts) { + for (auto& fact : facts.fact()) { + if (!MaybeAddFact(fact)) { + auto message = "Invalid fact " + ToString(fact) + " ignored."; + message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str()); + } + } +} + +bool FactManager::MaybeAddFact(const fuzz::protobufs::Fact& fact) { + switch (fact.fact_case()) { + case protobufs::Fact::kBlockIsDeadFact: + return dead_block_facts_.MaybeAddFact(fact.block_is_dead_fact()); + case protobufs::Fact::kConstantUniformFact: + return constant_uniform_facts_.MaybeAddFact(fact.constant_uniform_fact()); + case protobufs::Fact::kDataSynonymFact: + return data_synonym_and_id_equation_facts_.MaybeAddFact( + fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_); + case protobufs::Fact::kFunctionIsLivesafeFact: + return livesafe_function_facts_.MaybeAddFact( + fact.function_is_livesafe_fact()); + case protobufs::Fact::kIdEquationFact: + return data_synonym_and_id_equation_facts_.MaybeAddFact( + fact.id_equation_fact(), dead_block_facts_, irrelevant_value_facts_); + case protobufs::Fact::kIdIsIrrelevant: + return irrelevant_value_facts_.MaybeAddFact( + fact.id_is_irrelevant(), data_synonym_and_id_equation_facts_); + case protobufs::Fact::kPointeeValueIsIrrelevantFact: + return irrelevant_value_facts_.MaybeAddFact( + fact.pointee_value_is_irrelevant_fact(), + data_synonym_and_id_equation_facts_); + case protobufs::Fact::FACT_NOT_SET: + assert(false && "The fact must be set"); + return false; + } + + assert(false && "Unreachable"); + return false; +} + +void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1, + const protobufs::DataDescriptor& data2) { + protobufs::FactDataSynonym fact; + *fact.mutable_data1() = data1; + *fact.mutable_data2() = data2; + auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( + fact, dead_block_facts_, irrelevant_value_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "Unable to create DataSynonym fact"); +} + +std::vector FactManager::GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const { + return constant_uniform_facts_.GetConstantsAvailableFromUniformsForType( + type_id); +} + +std::vector +FactManager::GetUniformDescriptorsForConstant(uint32_t constant_id) const { + return constant_uniform_facts_.GetUniformDescriptorsForConstant(constant_id); +} + +uint32_t FactManager::GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const { + return constant_uniform_facts_.GetConstantFromUniformDescriptor( + uniform_descriptor); +} + +std::vector FactManager::GetTypesForWhichUniformValuesAreKnown() + const { + return constant_uniform_facts_.GetTypesForWhichUniformValuesAreKnown(); +} + +const std::vector>& +FactManager::GetConstantUniformFactsAndTypes() const { + return constant_uniform_facts_.GetConstantUniformFactsAndTypes(); +} + +std::vector FactManager::GetIdsForWhichSynonymsAreKnown() const { + return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown(); +} + +std::vector FactManager::GetAllSynonyms() + const { + return data_synonym_and_id_equation_facts_.GetAllKnownSynonyms(); +} + +std::vector +FactManager::GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const { + return data_synonym_and_id_equation_facts_.GetSynonymsForDataDescriptor( + data_descriptor); +} + +std::vector FactManager::GetSynonymsForId( + uint32_t id) const { + return data_synonym_and_id_equation_facts_.GetSynonymsForId(id); +} + +bool FactManager::IsSynonymous( + const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const { + return data_synonym_and_id_equation_facts_.IsSynonymous(data_descriptor1, + data_descriptor2); +} + +bool FactManager::BlockIsDead(uint32_t block_id) const { + return dead_block_facts_.BlockIsDead(block_id); +} + +void FactManager::AddFactBlockIsDead(uint32_t block_id) { + protobufs::FactBlockIsDead fact; + fact.set_block_id(block_id); + auto success = dead_block_facts_.MaybeAddFact(fact); + (void)success; // Keep compilers happy in release mode. + assert(success && "|block_id| is invalid"); +} + +bool FactManager::FunctionIsLivesafe(uint32_t function_id) const { + return livesafe_function_facts_.FunctionIsLivesafe(function_id); +} + +void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { + protobufs::FactFunctionIsLivesafe fact; + fact.set_function_id(function_id); + auto success = livesafe_function_facts_.MaybeAddFact(fact); + (void)success; // Keep compilers happy in release mode. + assert(success && "|function_id| is invalid"); +} + +bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const { + return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id); +} + +bool FactManager::IdIsIrrelevant(uint32_t result_id) const { + return irrelevant_value_facts_.IdIsIrrelevant(result_id, dead_block_facts_); +} + +std::unordered_set FactManager::GetIrrelevantIds() const { + return irrelevant_value_facts_.GetIrrelevantIds(dead_block_facts_); +} + +void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { + protobufs::FactPointeeValueIsIrrelevant fact; + fact.set_pointer_id(pointer_id); + auto success = irrelevant_value_facts_.MaybeAddFact( + fact, data_synonym_and_id_equation_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "|pointer_id| is invalid"); +} + +void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) { + protobufs::FactIdIsIrrelevant fact; + fact.set_result_id(result_id); + auto success = irrelevant_value_facts_.MaybeAddFact( + fact, data_synonym_and_id_equation_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "|result_id| is invalid"); +} + +void FactManager::AddFactIdEquation(uint32_t lhs_id, spv::Op opcode, + const std::vector& rhs_id) { + protobufs::FactIdEquation fact; + fact.set_lhs_id(lhs_id); + fact.set_opcode(uint32_t(opcode)); + for (auto an_rhs_id : rhs_id) { + fact.add_rhs_id(an_rhs_id); + } + auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( + fact, dead_block_facts_, irrelevant_value_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "Can't create IdIsIrrelevant fact"); +} + +void FactManager::ComputeClosureOfFacts( + uint32_t maximum_equivalence_class_size) { + data_synonym_and_id_equation_facts_.ComputeClosureOfFacts( + maximum_equivalence_class_size); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.h new file mode 100644 index 00000000..4453e445 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/fact_manager.h @@ -0,0 +1,225 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ +#define SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ + +#include +#include +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fact_manager/constant_uniform_facts.h" +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" +#include "source/fuzz/fact_manager/dead_block_facts.h" +#include "source/fuzz/fact_manager/irrelevant_value_facts.h" +#include "source/fuzz/fact_manager/livesafe_function_facts.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/constants.h" + +namespace spvtools { +namespace fuzz { + +// Keeps track of facts about the module being transformed on which the fuzzing +// process can depend. Some initial facts can be provided, for example about +// guarantees on the values of inputs to SPIR-V entry points. Transformations +// may then rely on these facts, can add further facts that they establish. +// Facts are intended to be simple properties that either cannot be deduced from +// the module (such as properties that are guaranteed to hold for entry point +// inputs), or that are established by transformations, likely to be useful for +// future transformations, and not completely trivial to deduce straight from +// the module. +class FactManager { + public: + explicit FactManager(opt::IRContext* ir_context); + + // Adds all the facts from |facts|, checking them for validity with respect to + // |ir_context_|. Warnings about invalid facts are communicated via + // |message_consumer|; such facts are otherwise ignored. + void AddInitialFacts(const MessageConsumer& message_consumer, + const protobufs::FactSequence& facts); + + // Checks the fact for validity with respect to |ir_context_|. Returns false, + // with no side effects, if the fact is invalid. Otherwise adds |fact| to the + // fact manager. + bool MaybeAddFact(const protobufs::Fact& fact); + + // Record the fact that |data1| and |data2| are synonymous. Neither |data1| + // nor |data2| may contain an irrelevant id. + void AddFactDataSynonym(const protobufs::DataDescriptor& data1, + const protobufs::DataDescriptor& data2); + + // Records the fact that |block_id| is dead. |block_id| must be a result id + // of some OpLabel instruction in the |ir_context_|. + void AddFactBlockIsDead(uint32_t block_id); + + // Records the fact that |function_id| is livesafe. |function_id| must be a + // result id of some non-entry-point function in the module. + void AddFactFunctionIsLivesafe(uint32_t function_id); + + // Records the fact that the value of the pointee associated with |pointer_id| + // is irrelevant: it does not affect the observable behaviour of the module. + // |pointer_id| must exist in the module and actually be a pointer. + void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id); + + // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect + // the semantics of the module). + // |result_id| must exist in the module and it may not be a pointer. + void AddFactIdIsIrrelevant(uint32_t result_id); + + // Records the fact that |lhs_id| is defined by the equation: + // + // |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]| + // + // Neither |lhs_id| nor any of |rhs_id| may be irrelevant. + void AddFactIdEquation(uint32_t lhs_id, spv::Op opcode, + const std::vector& rhs_id); + + // Inspects all known facts and adds corollary facts; e.g. if we know that + // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record + // that a == b holds. + // + // This method is expensive, and should only be called (by applying a + // transformation) at the start of a fuzzer pass that depends on data + // synonym facts, rather than calling it every time a new data synonym fact + // is added. + // + // The parameter |maximum_equivalence_class_size| specifies the size beyond + // which equivalence classes should not be mined for new facts, to avoid + // excessively-long closure computations. + void ComputeClosureOfFacts(uint32_t maximum_equivalence_class_size); + + // The fact manager is responsible for managing a few distinct categories of + // facts. In principle there could be different fact managers for each kind + // of fact, but in practice providing one 'go to' place for facts is + // convenient. To keep some separation, the public methods of the fact + // manager should be grouped according to the kind of fact to which they + // relate. + + //============================== + // Querying facts about uniform constants + + // Provides the distinct type ids for which at least one "constant == + // uniform element" fact is known. + std::vector GetTypesForWhichUniformValuesAreKnown() const; + + // Provides distinct constant ids with type |type_id| for which at least one + // "constant == uniform element" fact is known. If multiple identically- + // valued constants are relevant, only one will appear in the sequence. + std::vector GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const; + + // Provides details of all uniform elements that are known to be equal to the + // constant associated with |constant_id| in |ir_context_|. + std::vector + GetUniformDescriptorsForConstant(uint32_t constant_id) const; + + // Returns the id of a constant whose value is known to match that of + // |uniform_descriptor|, and whose type matches the type of the uniform + // element. If multiple such constant is exist, the one that is returned + // is arbitrary. Returns 0 if no such constant id exists. + uint32_t GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) + const; + + // Returns all "constant == uniform element" facts known to the fact + // manager, pairing each fact with id of the type that is associated with + // both the constant and the uniform element. + const std::vector>& + GetConstantUniformFactsAndTypes() const; + + // End of uniform constant facts + //============================== + + //============================== + // Querying facts about id synonyms + + // Returns every id for which a fact of the form "this id is synonymous with + // this piece of data" is known. + std::vector GetIdsForWhichSynonymsAreKnown() const; + + // Returns a vector of all data descriptors that participate in DataSynonym + // facts. All descriptors are guaranteed to exist in the |ir_context_|. + std::vector GetAllSynonyms() const; + + // Returns the equivalence class of all known synonyms of |id|, or an empty + // set if no synonyms are known. + std::vector GetSynonymsForId( + uint32_t id) const; + + // Returns the equivalence class of all known synonyms of |data_descriptor|, + // or empty if no synonyms are known. + std::vector GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const; + + // Returns true if and only if |data_descriptor1| and |data_descriptor2| are + // known to be synonymous. + bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const; + + // End of id synonym facts + //============================== + + //============================== + // Querying facts about dead blocks + + // Returns true if and only if |block_id| is the id of a block known to be + // dynamically unreachable. + bool BlockIsDead(uint32_t block_id) const; + + // End of dead block facts + //============================== + + //============================== + // Querying facts about livesafe function + + // Returns true if and only if |function_id| is the id of a function known + // to be livesafe. + bool FunctionIsLivesafe(uint32_t function_id) const; + + // End of dead livesafe function facts + //============================== + + //============================== + // Querying facts about irrelevant values + + // Returns true if and only if the value of the pointee associated with + // |pointer_id| is irrelevant. + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; + + // Returns true if there exists a fact that the |result_id| is irrelevant or + // if |result_id| is declared in a block that has been declared dead. + bool IdIsIrrelevant(uint32_t result_id) const; + + // Returns a set of all the ids which have been declared irrelevant, or which + // have been declared inside a dead block. + std::unordered_set GetIrrelevantIds() const; + + // End of irrelevant value facts + //============================== + + private: + // Keep these in alphabetical order. + fact_manager::ConstantUniformFacts constant_uniform_facts_; + fact_manager::DataSynonymAndIdEquationFacts + data_synonym_and_id_equation_facts_; + fact_manager::DeadBlockFacts dead_block_facts_; + fact_manager::LivesafeFunctionFacts livesafe_function_facts_; + fact_manager::IrrelevantValueFacts irrelevant_value_facts_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp new file mode 100644 index 00000000..07836add --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/irrelevant_value_facts.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" +#include "source/fuzz/fact_manager/dead_block_facts.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +IrrelevantValueFacts::IrrelevantValueFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool IrrelevantValueFacts::MaybeAddFact( + const protobufs::FactPointeeValueIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) { + const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.pointer_id()); + if (!inst || !inst->type_id()) { + // The id must exist in the module and have type id. + return false; + } + + if (!ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + // The id must be a pointer. + return false; + } + + if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.pointer_id()) + .empty()) { + // Irrelevant id cannot participate in DataSynonym facts. + return false; + } + + pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id()); + return true; +} + +bool IrrelevantValueFacts::MaybeAddFact( + const protobufs::FactIdIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) { + const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.result_id()); + if (!inst || !inst->type_id()) { + // The id must exist in the module and have type id. + return false; + } + + if (ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + // The id may not be a pointer. + return false; + } + + if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.result_id()) + .empty()) { + // Irrelevant id cannot participate in DataSynonym facts. + return false; + } + + irrelevant_ids_.insert(fact.result_id()); + return true; +} + +bool IrrelevantValueFacts::PointeeValueIsIrrelevant(uint32_t pointer_id) const { + return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0; +} + +bool IrrelevantValueFacts::IdIsIrrelevant( + uint32_t result_id, const DeadBlockFacts& dead_block_facts) const { + // The id is irrelevant if it has been declared irrelevant. + if (irrelevant_ids_.count(result_id)) { + return true; + } + + // The id must have a non-pointer type to be irrelevant. + auto def = ir_context_->get_def_use_mgr()->GetDef(result_id); + if (!def) { + return false; + } + auto type = ir_context_->get_type_mgr()->GetType(def->type_id()); + if (!type || type->AsPointer()) { + return false; + } + + // The id is irrelevant if it is in a dead block. + return ir_context_->get_instr_block(result_id) && + dead_block_facts.BlockIsDead( + ir_context_->get_instr_block(result_id)->id()); +} + +std::unordered_set IrrelevantValueFacts::GetIrrelevantIds( + const DeadBlockFacts& dead_block_facts) const { + // Get all the ids that have been declared irrelevant. + auto irrelevant_ids = irrelevant_ids_; + + // Get all the non-pointer ids declared in dead blocks that have a type. + for (uint32_t block_id : dead_block_facts.GetDeadBlocks()) { + auto block = fuzzerutil::MaybeFindBlock(ir_context_, block_id); + // It is possible and allowed for the block not to exist, e.g. it could have + // been merged with another block. + if (!block) { + continue; + } + block->ForEachInst([this, &irrelevant_ids](opt::Instruction* inst) { + // The instruction must have a result id and a type, and it must not be a + // pointer. + if (inst->HasResultId() && inst->type_id() && + !ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + irrelevant_ids.emplace(inst->result_id()); + } + }); + } + + return irrelevant_ids; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h new file mode 100644 index 00000000..9faddc04 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// Forward reference to the DataSynonymAndIdEquationFacts class. +class DataSynonymAndIdEquationFacts; +// Forward reference to the DeadBlockFacts class. +class DeadBlockFacts; + +// The purpose of this class is to group the fields and data used to represent +// facts about various irrelevant values in the module. +class IrrelevantValueFacts { + public: + explicit IrrelevantValueFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.pointer_id()| is a result id of pointer type in the |ir_context_| and + // |fact.pointer_id()| does not participate in DataSynonym facts. Returns + // false otherwise. |data_synonym_and_id_equation_facts| and |context| are + // passed for consistency checks. + bool MaybeAddFact( + const protobufs::FactPointeeValueIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.result_id()| is a result id of non-pointer type in the |ir_context_| + // and |fact.result_id()| does not participate in DataSynonym facts. Returns + // false otherwise. |data_synonym_and_id_equation_facts| and |context| are + // passed for consistency checks. + bool MaybeAddFact( + const protobufs::FactIdIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts); + + // See method in FactManager which delegates to this method. + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; + + // See method in FactManager which delegates to this method. + // |dead_block_facts| and |context| are passed to check whether |result_id| is + // declared inside a dead block, in which case it is irrelevant. + bool IdIsIrrelevant(uint32_t result_id, + const DeadBlockFacts& dead_block_facts) const; + + // See method in FactManager which delegates to this method. + // |dead_block_facts| and |context| are passed to also add all the ids + // declared in dead blocks to the set of irrelevant ids. + std::unordered_set GetIrrelevantIds( + const DeadBlockFacts& dead_block_facts) const; + + private: + std::unordered_set pointers_to_irrelevant_pointees_ids_; + std::unordered_set irrelevant_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp new file mode 100644 index 00000000..553ac579 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/livesafe_function_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +LivesafeFunctionFacts::LivesafeFunctionFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool LivesafeFunctionFacts::MaybeAddFact( + const protobufs::FactFunctionIsLivesafe& fact) { + if (!fuzzerutil::FindFunction(ir_context_, fact.function_id())) { + return false; + } + + if (fuzzerutil::FunctionIsEntryPoint(ir_context_, fact.function_id())) { + return false; + } + + livesafe_function_ids_.insert(fact.function_id()); + return true; +} + +bool LivesafeFunctionFacts::FunctionIsLivesafe(uint32_t function_id) const { + return livesafe_function_ids_.count(function_id) != 0; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h new file mode 100644 index 00000000..2156d643 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about livesafe functions. +class LivesafeFunctionFacts { + public: + explicit LivesafeFunctionFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.function_id()| is a result id of some non-entry-point function in + // |ir_context_|. Returns false otherwise. + bool MaybeAddFact(const protobufs::FactFunctionIsLivesafe& fact); + + // See method in FactManager which delegates to this method. + bool FunctionIsLivesafe(uint32_t function_id) const; + + private: + std::unordered_set livesafe_function_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.cpp new file mode 100644 index 00000000..191fd716 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.cpp @@ -0,0 +1,375 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/force_render_red.h" + +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// Helper method to find the fragment shader entry point, complaining if there +// is no shader or if there is no fragment entry point. +opt::Function* FindFragmentShaderEntryPoint(opt::IRContext* ir_context, + MessageConsumer message_consumer) { + // Check that this is a fragment shader + bool found_capability_shader = false; + for (auto& capability : ir_context->capabilities()) { + assert(capability.opcode() == spv::Op::OpCapability); + if (spv::Capability(capability.GetSingleWordInOperand(0)) == + spv::Capability::Shader) { + found_capability_shader = true; + break; + } + } + if (!found_capability_shader) { + message_consumer( + SPV_MSG_ERROR, nullptr, {}, + "Forcing of red rendering requires the Shader capability."); + return nullptr; + } + + opt::Instruction* fragment_entry_point = nullptr; + for (auto& entry_point : ir_context->module()->entry_points()) { + if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) == + spv::ExecutionModel::Fragment) { + fragment_entry_point = &entry_point; + break; + } + } + if (fragment_entry_point == nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Forcing of red rendering requires an entry point with " + "the Fragment execution model."); + return nullptr; + } + + for (auto& function : *ir_context->module()) { + if (function.result_id() == + fragment_entry_point->GetSingleWordInOperand(1)) { + return &function; + } + } + assert( + false && + "A valid module must have a function associate with each entry point."); + return nullptr; +} + +// Helper method to check that there is a single vec4 output variable and get a +// pointer to it. +opt::Instruction* FindVec4OutputVariable(opt::IRContext* ir_context, + MessageConsumer message_consumer) { + opt::Instruction* output_variable = nullptr; + for (auto& inst : ir_context->types_values()) { + if (inst.opcode() == spv::Op::OpVariable && + spv::StorageClass(inst.GetSingleWordInOperand(0)) == + spv::StorageClass::Output) { + if (output_variable != nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Only one output variable can be handled at present; " + "found multiple."); + return nullptr; + } + output_variable = &inst; + // Do not break, as we want to check for multiple output variables. + } + } + if (output_variable == nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "No output variable to which to write red was found."); + return nullptr; + } + + auto output_variable_base_type = ir_context->get_type_mgr() + ->GetType(output_variable->type_id()) + ->AsPointer() + ->pointee_type() + ->AsVector(); + if (!output_variable_base_type || + output_variable_base_type->element_count() != 4 || + !output_variable_base_type->element_type()->AsFloat()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "The output variable must have type vec4."); + return nullptr; + } + + return output_variable; +} + +// Helper to get the ids of float constants 0.0 and 1.0, creating them if +// necessary. +std::pair FindOrCreateFloatZeroAndOne( + opt::IRContext* ir_context, opt::analysis::Float* float_type) { + float one = 1.0; + uint32_t one_as_uint; + memcpy(&one_as_uint, &one, sizeof(float)); + std::vector zero_bytes = {0}; + std::vector one_bytes = {one_as_uint}; + auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(float_type, zero_bytes)); + auto constant_one = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(float_type, one_bytes)); + auto constant_zero_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_zero) + ->result_id(); + auto constant_one_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_one) + ->result_id(); + return std::pair(constant_zero_id, constant_one_id); +} + +std::unique_ptr +MakeConstantUniformReplacement(opt::IRContext* ir_context, + const FactManager& fact_manager, + uint32_t constant_id, + uint32_t greater_than_instruction, + uint32_t in_operand_index) { + return MakeUnique( + MakeIdUseDescriptor( + constant_id, + MakeInstructionDescriptor(greater_than_instruction, + spv::Op::OpFOrdGreaterThan, 0), + in_operand_index), + fact_manager.GetUniformDescriptorsForConstant(constant_id)[0], + ir_context->TakeNextId(), ir_context->TakeNextId()); +} + +} // namespace + +bool ForceRenderRed( + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const MessageConsumer& message_consumer, + std::vector* binary_out) { + spvtools::SpirvTools tools(target_env); + if (!tools.IsValid()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return false; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return false; + } + + // Build the module from the input binary. + std::unique_ptr ir_context = BuildModule( + target_env, message_consumer, binary_in.data(), binary_in.size()); + assert(ir_context); + + // Set up a fact manager with any given initial facts. + TransformationContext transformation_context( + MakeUnique(ir_context.get()), validator_options); + for (auto& fact : initial_facts.fact()) { + transformation_context.GetFactManager()->MaybeAddFact(fact); + } + + auto entry_point_function = + FindFragmentShaderEntryPoint(ir_context.get(), message_consumer); + auto output_variable = + FindVec4OutputVariable(ir_context.get(), message_consumer); + if (entry_point_function == nullptr || output_variable == nullptr) { + return false; + } + + opt::analysis::Float temp_float_type(32); + opt::analysis::Float* float_type = ir_context->get_type_mgr() + ->GetRegisteredType(&temp_float_type) + ->AsFloat(); + std::pair zero_one_float_ids = + FindOrCreateFloatZeroAndOne(ir_context.get(), float_type); + + // Make the new exit block + auto new_exit_block_id = ir_context->TakeNextId(); + { + auto label = MakeUnique( + ir_context.get(), spv::Op::OpLabel, 0, new_exit_block_id, + opt::Instruction::OperandList()); + auto new_exit_block = MakeUnique(std::move(label)); + new_exit_block->AddInstruction( + MakeUnique(ir_context.get(), spv::Op::OpReturn, 0, 0, + opt::Instruction::OperandList())); + entry_point_function->AddBasicBlock(std::move(new_exit_block)); + } + + // Make the new entry block + { + auto label = MakeUnique( + ir_context.get(), spv::Op::OpLabel, 0, ir_context->TakeNextId(), + opt::Instruction::OperandList()); + auto new_entry_block = MakeUnique(std::move(label)); + + // Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing + // the colour red. + opt::Operand zero_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.first}}; + opt::Operand one_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.second}}; + opt::Instruction::OperandList op_composite_construct_operands = { + one_float, zero_float, zero_float, one_float}; + auto temp_vec4 = opt::analysis::Vector(float_type, 4); + auto vec4_id = ir_context->get_type_mgr()->GetId(&temp_vec4); + auto red = MakeUnique( + ir_context.get(), spv::Op::OpCompositeConstruct, vec4_id, + ir_context->TakeNextId(), op_composite_construct_operands); + auto red_id = red->result_id(); + new_entry_block->AddInstruction(std::move(red)); + + // Make an instruction to store red into the output color. + opt::Operand variable_to_store_into = {SPV_OPERAND_TYPE_ID, + {output_variable->result_id()}}; + opt::Operand value_to_be_stored = {SPV_OPERAND_TYPE_ID, {red_id}}; + opt::Instruction::OperandList op_store_operands = {variable_to_store_into, + value_to_be_stored}; + new_entry_block->AddInstruction(MakeUnique( + ir_context.get(), spv::Op::OpStore, 0, 0, op_store_operands)); + + // We are going to attempt to construct 'false' as an expression of the form + // 'literal1 > literal2'. If we succeed, we will later replace each literal + // with a uniform of the same value - we can only do that replacement once + // we have added the entry block to the module. + std::unique_ptr + first_greater_then_operand_replacement = nullptr; + std::unique_ptr + second_greater_then_operand_replacement = nullptr; + uint32_t id_guaranteed_to_be_false = 0; + + opt::analysis::Bool temp_bool_type; + opt::analysis::Bool* registered_bool_type = + ir_context->get_type_mgr() + ->GetRegisteredType(&temp_bool_type) + ->AsBool(); + + auto float_type_id = ir_context->get_type_mgr()->GetId(float_type); + auto types_for_which_uniforms_are_known = + transformation_context.GetFactManager() + ->GetTypesForWhichUniformValuesAreKnown(); + + // Check whether we have any float uniforms. + if (std::find(types_for_which_uniforms_are_known.begin(), + types_for_which_uniforms_are_known.end(), + float_type_id) != types_for_which_uniforms_are_known.end()) { + // We have at least one float uniform; let's see whether we have at least + // two. + auto available_constants = + transformation_context.GetFactManager() + ->GetConstantsAvailableFromUniformsForType(float_type_id); + if (available_constants.size() > 1) { + // Grab the float constants associated with the first two known float + // uniforms. + auto first_constant = + ir_context->get_constant_mgr() + ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef( + available_constants[0])) + ->AsFloatConstant(); + auto second_constant = + ir_context->get_constant_mgr() + ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef( + available_constants[1])) + ->AsFloatConstant(); + + // Now work out which of the two constants is larger than the other. + uint32_t larger_constant_index = 0; + uint32_t smaller_constant_index = 0; + if (first_constant->GetFloat() > second_constant->GetFloat()) { + larger_constant_index = 0; + smaller_constant_index = 1; + } else if (first_constant->GetFloat() < second_constant->GetFloat()) { + larger_constant_index = 1; + smaller_constant_index = 0; + } + + // Only proceed with these constants if they have turned out to be + // distinct. + if (larger_constant_index != smaller_constant_index) { + // We are in a position to create 'false' as 'literal1 > literal2', so + // reserve an id for this computation; this id will end up being + // guaranteed to be 'false'. + id_guaranteed_to_be_false = ir_context->TakeNextId(); + + auto smaller_constant = available_constants[smaller_constant_index]; + auto larger_constant = available_constants[larger_constant_index]; + + opt::Instruction::OperandList greater_than_operands = { + {SPV_OPERAND_TYPE_ID, {smaller_constant}}, + {SPV_OPERAND_TYPE_ID, {larger_constant}}}; + new_entry_block->AddInstruction(MakeUnique( + ir_context.get(), spv::Op::OpFOrdGreaterThan, + ir_context->get_type_mgr()->GetId(registered_bool_type), + id_guaranteed_to_be_false, greater_than_operands)); + + first_greater_then_operand_replacement = + MakeConstantUniformReplacement( + ir_context.get(), *transformation_context.GetFactManager(), + smaller_constant, id_guaranteed_to_be_false, 0); + second_greater_then_operand_replacement = + MakeConstantUniformReplacement( + ir_context.get(), *transformation_context.GetFactManager(), + larger_constant, id_guaranteed_to_be_false, 1); + } + } + } + + if (id_guaranteed_to_be_false == 0) { + auto constant_false = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(registered_bool_type, false)); + id_guaranteed_to_be_false = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_false) + ->result_id(); + } + + opt::Operand false_condition = {SPV_OPERAND_TYPE_ID, + {id_guaranteed_to_be_false}}; + opt::Operand then_block = {SPV_OPERAND_TYPE_ID, + {entry_point_function->entry()->id()}}; + opt::Operand else_block = {SPV_OPERAND_TYPE_ID, {new_exit_block_id}}; + opt::Instruction::OperandList op_branch_conditional_operands = { + false_condition, then_block, else_block}; + new_entry_block->AddInstruction(MakeUnique( + ir_context.get(), spv::Op::OpBranchConditional, 0, 0, + op_branch_conditional_operands)); + + entry_point_function->InsertBasicBlockBefore( + std::move(new_entry_block), entry_point_function->entry().get()); + + for (auto& replacement : {first_greater_then_operand_replacement.get(), + second_greater_then_operand_replacement.get()}) { + if (replacement) { + assert(replacement->IsApplicable(ir_context.get(), + transformation_context)); + replacement->Apply(ir_context.get(), &transformation_context); + } + } + } + + // Write out the module as a binary. + ir_context->module()->ToBinary(binary_out, false); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.h new file mode 100644 index 00000000..5b8eab1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/force_render_red.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FORCE_RENDER_RED_H_ +#define SOURCE_FORCE_RENDER_RED_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Requires |binary_in| to be a valid SPIR-V module with Shader capability, +// containing an entry point with the Fragment execution model, and a single +// output variable of type vec4. +// +// Turns the body of this entry point into effectively: +// +// output_variable = vec4(1.0, 0.0, 0.0, 1.0); +// if (false) { +// original_body +// } +// +// If suitable facts about values of uniforms are available, the 'false' will +// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for +// which it is known that 'u < v' holds. +bool ForceRenderRed( + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const MessageConsumer& message_consumer, std::vector* binary_out); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FORCE_RENDER_RED_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.cpp new file mode 100644 index 00000000..9838e64f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer.h" + +#include +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_add_access_chains.h" +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" +#include "source/fuzz/fuzzer_pass_add_composite_types.h" +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" +#include "source/fuzz/fuzzer_pass_add_function_calls.h" +#include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" +#include "source/fuzz/fuzzer_pass_add_loads.h" +#include "source/fuzz/fuzzer_pass_add_local_variables.h" +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_parameters.h" +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" +#include "source/fuzz/fuzzer_pass_add_stores.h" +#include "source/fuzz/fuzzer_pass_add_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" +#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" +#include "source/fuzz/fuzzer_pass_adjust_function_controls.h" +#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h" +#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h" +#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h" +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" +#include "source/fuzz/fuzzer_pass_construct_composites.h" +#include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_inline_functions.h" +#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h" +#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h" +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" +#include "source/fuzz/fuzzer_pass_merge_blocks.h" +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include "source/fuzz/fuzzer_pass_outline_functions.h" +#include "source/fuzz/fuzzer_pass_permute_blocks.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_pass_permute_function_variables.h" +#include "source/fuzz/fuzzer_pass_permute_instructions.h" +#include "source/fuzz/fuzzer_pass_permute_phi_operands.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" +#include "source/fuzz/fuzzer_pass_split_blocks.h" +#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" +#include "source/fuzz/fuzzer_pass_swap_functions.h" +#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/build_module.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +Fuzzer::Fuzzer(std::unique_ptr ir_context, + std::unique_ptr transformation_context, + std::unique_ptr fuzzer_context, + MessageConsumer consumer, + const std::vector& donor_suppliers, + bool enable_all_passes, + RepeatedPassStrategy repeated_pass_strategy, + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options, + bool ignore_inapplicable_transformations /* = true */) + : consumer_(std::move(consumer)), + enable_all_passes_(enable_all_passes), + validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), + validator_options_(validator_options), + num_repeated_passes_applied_(0), + is_valid_(true), + ir_context_(std::move(ir_context)), + transformation_context_(std::move(transformation_context)), + fuzzer_context_(std::move(fuzzer_context)), + transformation_sequence_out_(), + pass_instances_(), + repeated_pass_recommender_(nullptr), + repeated_pass_manager_(nullptr), + final_passes_(), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) { + assert(ir_context_ && "IRContext is not initialized"); + assert(fuzzer_context_ && "FuzzerContext is not initialized"); + assert(transformation_context_ && "TransformationContext is not initialized"); + assert(fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_, + consumer_) && + "IRContext is invalid"); + + // The following passes are likely to be very useful: many other passes + // introduce synonyms, irrelevant ids and constants that these passes can work + // with. We thus enable them with high probability. + MaybeAddRepeatedPass(90, &pass_instances_); + MaybeAddRepeatedPass(90, &pass_instances_); + MaybeAddRepeatedPass(90, &pass_instances_); + + do { + // Each call to MaybeAddRepeatedPass randomly decides whether the given pass + // should be enabled, and adds an instance of the pass to |pass_instances| + // if it is enabled. + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_, + donor_suppliers); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass( + &pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + MaybeAddRepeatedPass(&pass_instances_); + // There is a theoretical possibility that no pass instances were created + // until now; loop again if so. + } while (pass_instances_.GetPasses().empty()); + + repeated_pass_recommender_ = MakeUnique( + &pass_instances_, fuzzer_context_.get()); + repeated_pass_manager_ = RepeatedPassManager::Create( + repeated_pass_strategy, fuzzer_context_.get(), &pass_instances_, + repeated_pass_recommender_.get()); + + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + if (!fuzzer_context_->IsWgslCompatible()) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4214): + // this is disabled temporarily due to some issues in the Tint compiler. + // Enable it back when the issues are resolved. + MaybeAddFinalPass( + &final_passes_); + } + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); + MaybeAddFinalPass(&final_passes_); +} + +Fuzzer::~Fuzzer() = default; + +template +void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, + RepeatedPassInstances* pass_instances, + Args&&... extra_args) { + if (enable_all_passes_ || + fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) { + pass_instances->SetPass(MakeUnique( + ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); + } +} + +template +void Fuzzer::MaybeAddFinalPass(std::vector>* passes, + Args&&... extra_args) { + if (enable_all_passes_ || fuzzer_context_->ChooseEven()) { + passes->push_back(MakeUnique( + ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), + &transformation_sequence_out_, ignore_inapplicable_transformations_, + std::forward(extra_args)...)); + } +} + +bool Fuzzer::ApplyPassAndCheckValidity(FuzzerPass* pass) const { + pass->Apply(); + return !validate_after_each_fuzzer_pass_ || + fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_, + consumer_); +} + +opt::IRContext* Fuzzer::GetIRContext() { return ir_context_.get(); } + +const protobufs::TransformationSequence& Fuzzer::GetTransformationSequence() + const { + return transformation_sequence_out_; +} + +Fuzzer::Result Fuzzer::Run(uint32_t num_of_transformations_to_apply) { + assert(is_valid_ && "The module was invalidated during the previous fuzzing"); + + const auto initial_num_of_transformations = + static_cast(transformation_sequence_out_.transformation_size()); + + auto status = Status::kComplete; + do { + if (!ApplyPassAndCheckValidity( + repeated_pass_manager_->ChoosePass(transformation_sequence_out_))) { + status = Status::kFuzzerPassLedToInvalidModule; + break; + } + + // Check that the module is small enough. + if (ir_context_->module()->id_bound() >= + fuzzer_context_->GetIdBoundLimit()) { + status = Status::kModuleTooBig; + break; + } + + auto transformations_applied_so_far = static_cast( + transformation_sequence_out_.transformation_size()); + assert(transformations_applied_so_far >= initial_num_of_transformations && + "Number of transformations cannot decrease"); + + // Check if we've already applied the maximum number of transformations. + if (transformations_applied_so_far >= + fuzzer_context_->GetTransformationLimit()) { + status = Status::kTransformationLimitReached; + break; + } + + // Check that we've not got stuck (this can happen if the only available + // fuzzer passes are not able to apply any transformations, or can only + // apply very few transformations). + if (num_repeated_passes_applied_ >= + fuzzer_context_->GetTransformationLimit()) { + status = Status::kFuzzerStuck; + break; + } + + // Check whether we've exceeded the number of transformations we can apply + // in a single call to this method. + if (num_of_transformations_to_apply != 0 && + transformations_applied_so_far - initial_num_of_transformations >= + num_of_transformations_to_apply) { + status = Status::kComplete; + break; + } + + } while (ShouldContinueRepeatedPasses(num_of_transformations_to_apply == 0)); + + if (status != Status::kFuzzerPassLedToInvalidModule) { + // We apply this transformations despite the fact that we might exceed + // |num_of_transformations_to_apply|. This is not a problem for us since + // these fuzzer passes are relatively simple yet might trigger some bugs. + for (auto& pass : final_passes_) { + if (!ApplyPassAndCheckValidity(pass.get())) { + status = Status::kFuzzerPassLedToInvalidModule; + break; + } + } + } + + is_valid_ = status != Status::kFuzzerPassLedToInvalidModule; + return {status, static_cast( + transformation_sequence_out_.transformation_size()) != + initial_num_of_transformations}; +} + +bool Fuzzer::ShouldContinueRepeatedPasses( + bool continue_fuzzing_probabilistically) { + if (continue_fuzzing_probabilistically) { + // If we have applied T transformations so far, and the limit on the number + // of transformations to apply is L (where T < L), the chance that we will + // continue fuzzing is: + // + // 1 - T/(2*L) + // + // That is, the chance of continuing decreases as more transformations are + // applied. Using 2*L instead of L increases the number of transformations + // that are applied on average. + auto transformations_applied_so_far = static_cast( + transformation_sequence_out_.transformation_size()); + auto chance_of_continuing = static_cast( + 100.0 * + (1.0 - (static_cast(transformations_applied_so_far) / + (2.0 * static_cast( + fuzzer_context_->GetTransformationLimit()))))); + if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) { + // We have probabilistically decided to stop. + return false; + } + } + // Continue fuzzing! + num_repeated_passes_applied_++; + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.h new file mode 100644 index 00000000..4c38977f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer.h @@ -0,0 +1,202 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_H_ +#define SOURCE_FUZZ_FUZZER_H_ + +#include +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/random_generator.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by +// running a number of randomized fuzzer passes. +class Fuzzer { + public: + // Possible statuses that can result from running the fuzzer. + enum class Status { + kComplete, + kModuleTooBig, + kTransformationLimitReached, + kFuzzerStuck, + kFuzzerPassLedToInvalidModule, + }; + + struct Result { + // Status of the fuzzing session. + Status status; + + // Equals to true if new transformations were applied during the previous + // fuzzing session. + bool is_changed; + }; + + Fuzzer(std::unique_ptr ir_context, + std::unique_ptr transformation_context, + std::unique_ptr fuzzer_context, + MessageConsumer consumer, + const std::vector& donor_suppliers, + bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options, + bool ignore_inapplicable_transformations = true); + + // Disables copy/move constructor/assignment operations. + Fuzzer(const Fuzzer&) = delete; + Fuzzer(Fuzzer&&) = delete; + Fuzzer& operator=(const Fuzzer&) = delete; + Fuzzer& operator=(Fuzzer&&) = delete; + + ~Fuzzer(); + + // Transforms |ir_context_| by running a number of randomized fuzzer passes. + // Initial facts about the input binary and the context in which it will be + // executed are provided with |transformation_context_|. + // |num_of_transformations| is equal to the maximum number of transformations + // applied in a single call to this method. This parameter is ignored if its + // value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer + // pass, fuzzing will stop after the fuzzer pass that exceeds + // |num_of_transformations| has completed, so that the total number of + // transformations may be somewhat larger than this number. + Result Run(uint32_t num_of_transformations_to_apply); + + // Returns the current IR context. It may be invalid if the Run method + // returned Status::kFuzzerPassLedToInvalidModule previously. + opt::IRContext* GetIRContext(); + + // Returns the sequence of applied transformations. + const protobufs::TransformationSequence& GetTransformationSequence() const; + + private: + // A convenience method to add a repeated fuzzer pass to |pass_instances| with + // probability |percentage_chance_of_adding_pass|%, or with probability 100% + // if |enable_all_passes_| is true. + // + // All fuzzer passes take members |ir_context_|, |transformation_context_|, + // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra + // arguments can be provided via |extra_args|. + template + void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, + RepeatedPassInstances* pass_instances, + Args&&... extra_args); + + // The same as the above, with |percentage_chance_of_adding_pass| == 50%. + template + void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances, + Args&&... extra_args) { + MaybeAddRepeatedPass(50, pass_instances, + std::forward(extra_args)...); + } + + // A convenience method to add a final fuzzer pass to |passes| with + // probability 50%, or with probability 100% if |enable_all_passes_| is true. + // + // All fuzzer passes take members |ir_context_|, |transformation_context_|, + // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra + // arguments can be provided via |extra_args|. + template + void MaybeAddFinalPass(std::vector>* passes, + Args&&... extra_args); + + // Decides whether to apply more repeated passes. The probability decreases as + // the number of transformations that have been applied increases. + // The described probability is only applied if + // |continue_fuzzing_probabilistically| is true. + bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically); + + // Applies |pass|, which must be a pass constructed with |ir_context|. + // If |validate_after_each_fuzzer_pass_| is not set, true is always returned. + // Otherwise, true is returned if and only if |ir_context| passes validation, + // every block has its enclosing function as its parent, and every + // instruction has a distinct unique id. + bool ApplyPassAndCheckValidity(FuzzerPass* pass) const; + + // Message consumer that will be invoked once for each message communicated + // from the library. + const MessageConsumer consumer_; + + // Determines whether all passes should be enabled, vs. having passes be + // probabilistically enabled. + const bool enable_all_passes_; + + // Determines whether the validator should be invoked after every fuzzer pass. + const bool validate_after_each_fuzzer_pass_; + + // Options to control validation. + const spv_validator_options validator_options_; + + // The number of repeated fuzzer passes that have been applied is kept track + // of, in order to enforce a hard limit on the number of times such passes + // can be applied. + uint32_t num_repeated_passes_applied_; + + // We use this to determine whether we can continue fuzzing incrementally + // since the previous call to the Run method could've returned + // kFuzzerPassLedToInvalidModule. + bool is_valid_; + + // Intermediate representation for the module being fuzzed, which gets + // mutated as fuzzing proceeds. + std::unique_ptr ir_context_; + + // Contextual information that is required in order to apply + // transformations. + std::unique_ptr transformation_context_; + + // Provides probabilities that control the fuzzing process. + std::unique_ptr fuzzer_context_; + + // The sequence of transformations that have been applied during fuzzing. It + // is initially empty and grows as fuzzer passes are applied. + protobufs::TransformationSequence transformation_sequence_out_; + + // This object contains instances of all fuzzer passes that will participate + // in the fuzzing. + RepeatedPassInstances pass_instances_; + + // This object defines the recommendation logic for fuzzer passes. + std::unique_ptr repeated_pass_recommender_; + + // This object manager a list of fuzzer pass and their available + // recommendations. + std::unique_ptr repeated_pass_manager_; + + // Some passes that it does not make sense to apply repeatedly, as they do not + // unlock other passes. + std::vector> final_passes_; + + // When set, this flag causes inapplicable transformations that should be + // applicable by construction to be ignored. This is useful when the fuzzer + // is being deployed at scale to test a SPIR-V processing tool, and where it + // is desirable to ignore bugs in the fuzzer itself. + const bool ignore_inapplicable_transformations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.cpp new file mode 100644 index 00000000..7e34cc33 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -0,0 +1,438 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_context.h" + +#include + +namespace spvtools { +namespace fuzz { + +namespace { + +// An offset between the module's id bound and the minimum fresh id. +// +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider +// the case where the maximum id bound is reached. +const uint32_t kIdBoundGap = 100; + +// Limits to help control the overall fuzzing process and rein in individual +// fuzzer passes. +const uint32_t kIdBoundLimit = 50000; +const uint32_t kTransformationLimit = 2000; + +// Default pairs of probabilities for applying various +// transformations. All values are percentages. Keep them in alphabetical order. +const std::pair + kChanceOfAcceptingRepeatedPassRecommendation = {50, 80}; +const std::pair kChanceOfAddingAccessChain = {5, 50}; +const std::pair kChanceOfAddingAnotherPassToPassLoop = {50, + 90}; +const std::pair kChanceOfAddingAnotherStructField = {20, + 90}; +const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair KChanceOfAddingAtomicLoad = {30, 90}; +const std::pair KChanceOfAddingAtomicStore = {20, 90}; +const std::pair kChanceOfAddingBitInstructionSynonym = {5, + 20}; +const std::pair + kChanceOfAddingBothBranchesWhenReplacingOpSelect = {40, 60}; +const std::pair kChanceOfAddingCompositeExtract = {20, 50}; +const std::pair kChanceOfAddingCompositeInsert = {20, 50}; +const std::pair kChanceOfAddingCopyMemory = {20, 50}; +const std::pair kChanceOfAddingDeadBlock = {20, 90}; +const std::pair kChanceOfAddingDeadBreak = {5, 80}; +const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingEquationInstruction = {5, + 90}; +const std::pair kChanceOfAddingGlobalVariable = {20, 90}; +const std::pair kChanceOfAddingImageSampleUnusedComponents = + {20, 90}; +const std::pair kChanceOfAddingLoad = {5, 50}; +const std::pair kChanceOfAddingLocalVariable = {20, 90}; +const std::pair kChanceOfAddingLoopPreheader = {20, 90}; +const std::pair kChanceOfAddingMatrixType = {20, 70}; +const std::pair kChanceOfAddingNoContractionDecoration = { + 5, 70}; +const std::pair kChanceOfAddingOpPhiSynonym = {5, 70}; +const std::pair kChanceOfAddingParameters = {5, 70}; +const std::pair kChanceOfAddingRelaxedDecoration = {20, 90}; +const std::pair kChanceOfAddingStore = {5, 50}; +const std::pair kChanceOfAddingSynonyms = {20, 50}; +const std::pair + kChanceOfAddingTrueBranchWhenReplacingOpSelect = {40, 60}; +const std::pair kChanceOfAddingVectorType = {20, 70}; +const std::pair kChanceOfAddingVectorShuffle = {20, 70}; +const std::pair kChanceOfAdjustingBranchWeights = {20, 90}; +const std::pair kChanceOfAdjustingFunctionControl = {20, + 70}; +const std::pair kChanceOfAdjustingLoopControl = {20, 90}; +const std::pair kChanceOfAdjustingMemoryOperandsMask = {20, + 90}; +const std::pair kChanceOfAdjustingSelectionControl = {20, + 90}; +const std::pair kChanceOfCallingFunction = {1, 10}; +const std::pair kChanceOfChoosingStructTypeVsArrayType = { + 20, 80}; +const std::pair kChanceOfChoosingWorkgroupStorageClass = { + 50, 50}; +const std::pair kChanceOfConstructingComposite = {20, 50}; +const std::pair kChanceOfCopyingObject = {20, 50}; +const std::pair kChanceOfCreatingIntSynonymsUsingLoops = { + 5, 10}; +const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; +const std::pair kChanceOfDuplicatingRegionWithSelection = { + 20, 50}; +const std::pair kChanceOfExpandingVectorReduction = {20, + 90}; +const std::pair kChanceOfFlatteningConditionalBranch = {45, + 95}; +const std::pair kChanceOfGoingDeeperToExtractComposite = { + 30, 70}; +const std::pair kChanceOfGoingDeeperToInsertInComposite = { + 30, 70}; +const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = + {50, 95}; +const std::pair + kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym = {50, 80}; +const std::pair kChanceOfInliningFunction = {10, 90}; +const std::pair kChanceOfInterchangingZeroLikeConstants = { + 10, 90}; +const std::pair + kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90}; +const std::pair kChanceOfInvertingComparisonOperators = { + 20, 50}; +const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; +const std::pair kChanceOfMakingVectorOperationDynamic = { + 20, 90}; +const std::pair kChanceOfMergingBlocks = {20, 95}; +const std::pair kChanceOfMergingFunctionReturns = {20, 90}; +const std::pair kChanceOfMovingBlockDown = {20, 50}; +const std::pair kChanceOfMutatingPointer = {20, 90}; +const std::pair kChanceOfObfuscatingConstant = {10, 90}; +const std::pair kChanceOfOutliningFunction = {10, 90}; +const std::pair kChanceOfPermutingFunctionVariables = {30, + 90}; +const std::pair kChanceOfPermutingInstructions = {20, 70}; +const std::pair kChanceOfPermutingParameters = {30, 90}; +const std::pair kChanceOfPermutingPhiOperands = {30, 90}; +const std::pair kChanceOfPropagatingInstructionsDown = {20, + 70}; +const std::pair kChanceOfPropagatingInstructionsUp = {20, + 70}; +const std::pair kChanceOfPushingIdThroughVariable = {5, 50}; +const std::pair + kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 70}; +const std::pair + kChanceOfReplacingBranchFromDeadBlockWithExit = {10, 65}; +const std::pair kChanceOfReplacingCopyMemoryWithLoadStore = + {20, 90}; +const std::pair kChanceOfReplacingCopyObjectWithStoreLoad = + {20, 90}; +const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; +const std::pair kChanceOfReplacingIrrelevantId = {35, 95}; +const std::pair + kChanceOfReplacingLinearAlgebraInstructions = {10, 90}; +const std::pair kChanceOfReplacingLoadStoreWithCopyMemory = + {20, 90}; +const std::pair + kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90}; +const std::pair + kChanceOfReplacingOpSelectWithConditionalBranch = {20, 90}; +const std::pair kChanceOfReplacingParametersWithGlobals = { + 30, 70}; +const std::pair kChanceOfReplacingParametersWithStruct = { + 20, 40}; +const std::pair kChanceOfSplittingBlock = {40, 95}; +const std::pair + kChanceOfSwappingAnotherPairOfFunctionVariables = {30, 90}; +const std::pair kChanceOfSwappingConditionalBranchOperands = + {10, 70}; +const std::pair kChanceOfSwappingFunctions = {10, 90}; +const std::pair kChanceOfTogglingAccessChainInstruction = { + 20, 90}; +const std::pair kChanceOfWrappingRegionInSelection = {70, + 90}; +const std::pair kChanceOfWrappingVectorSynonym = {10, 90}; + +// Default limits for various quantities that are chosen during fuzzing. +// Keep them in alphabetical order. +const uint32_t kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure = 1000; +const uint32_t kDefaultMaxLoopControlPartialCount = 100; +const uint32_t kDefaultMaxLoopControlPeelCount = 100; +const uint32_t kDefaultMaxLoopLimit = 20; +const uint32_t kDefaultMaxNewArraySizeLimit = 100; +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424): +// think whether there is a better limit on the maximum number of parameters. +const uint32_t kDefaultMaxNumberOfFunctionParameters = 128; +const uint32_t kDefaultMaxNumberOfNewParameters = 15; +const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5; + +// Default functions for controlling how deep to go during recursive +// generation/transformation. Keep them in alphabetical order. + +const std::function + kDefaultGoDeeperInConstantObfuscation = + [](uint32_t current_depth, RandomGenerator* random_generator) -> bool { + double chance = 1.0 / std::pow(3.0, static_cast(current_depth + 1)); + return random_generator->RandomDouble() < chance; +}; + +} // namespace + +FuzzerContext::FuzzerContext(std::unique_ptr random_generator, + uint32_t min_fresh_id, bool is_wgsl_compatible) + : random_generator_(std::move(random_generator)), + next_fresh_id_(min_fresh_id), + is_wgsl_compatible_(is_wgsl_compatible), + max_equivalence_class_size_for_data_synonym_fact_closure_( + kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure), + max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount), + max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount), + max_loop_limit_(kDefaultMaxLoopLimit), + max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit), + max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters), + max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters), + max_number_of_parameters_replaced_with_struct_( + kGetDefaultMaxNumberOfParametersReplacedWithStruct), + go_deeper_in_constant_obfuscation_( + kDefaultGoDeeperInConstantObfuscation) { + chance_of_accepting_repeated_pass_recommendation_ = + ChooseBetweenMinAndMax(kChanceOfAcceptingRepeatedPassRecommendation); + chance_of_adding_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfAddingAccessChain); + chance_of_adding_another_pass_to_pass_loop_ = + ChooseBetweenMinAndMax(kChanceOfAddingAnotherPassToPassLoop); + chance_of_adding_another_struct_field_ = + ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); + chance_of_adding_array_or_struct_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_atomic_load_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicLoad); + chance_of_adding_atomic_store_ = + ChooseBetweenMinAndMax(KChanceOfAddingAtomicStore); + chance_of_adding_bit_instruction_synonym_ = + ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); + chance_of_adding_both_branches_when_replacing_opselect_ = + ChooseBetweenMinAndMax(kChanceOfAddingBothBranchesWhenReplacingOpSelect); + chance_of_adding_composite_extract_ = + ChooseBetweenMinAndMax(kChanceOfAddingCompositeExtract); + chance_of_adding_composite_insert_ = + ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert); + chance_of_adding_copy_memory_ = + ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory); + chance_of_adding_dead_block_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock); + chance_of_adding_dead_break_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); + chance_of_adding_dead_continue_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_equation_instruction_ = + ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction); + chance_of_adding_global_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); + chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad); + chance_of_adding_loop_preheader_ = + ChooseBetweenMinAndMax(kChanceOfAddingLoopPreheader); + chance_of_adding_image_sample_unused_components_ = + ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents); + chance_of_adding_local_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable); + chance_of_adding_matrix_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); + chance_of_adding_no_contraction_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); + chance_of_adding_opphi_synonym_ = + ChooseBetweenMinAndMax(kChanceOfAddingOpPhiSynonym); + chance_of_adding_parameters = + ChooseBetweenMinAndMax(kChanceOfAddingParameters); + chance_of_adding_relaxed_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration); + chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore); + chance_of_adding_true_branch_when_replacing_opselect_ = + ChooseBetweenMinAndMax(kChanceOfAddingTrueBranchWhenReplacingOpSelect); + chance_of_adding_vector_shuffle_ = + ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle); + chance_of_adding_vector_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingVectorType); + chance_of_adjusting_branch_weights_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights); + chance_of_adjusting_function_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl); + chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms); + chance_of_adjusting_loop_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl); + chance_of_adjusting_memory_operands_mask_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask); + chance_of_adjusting_selection_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl); + chance_of_calling_function_ = + ChooseBetweenMinAndMax(kChanceOfCallingFunction); + chance_of_choosing_struct_type_vs_array_type_ = + ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType); + chance_of_choosing_workgroup_storage_class_ = + ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass); + chance_of_constructing_composite_ = + ChooseBetweenMinAndMax(kChanceOfConstructingComposite); + chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); + chance_of_creating_int_synonyms_using_loops_ = + ChooseBetweenMinAndMax(kChanceOfCreatingIntSynonymsUsingLoops); + chance_of_donating_additional_module_ = + ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); + chance_of_duplicating_region_with_selection_ = + ChooseBetweenMinAndMax(kChanceOfDuplicatingRegionWithSelection); + chance_of_expanding_vector_reduction_ = + ChooseBetweenMinAndMax(kChanceOfExpandingVectorReduction); + chance_of_flattening_conditional_branch_ = + ChooseBetweenMinAndMax(kChanceOfFlatteningConditionalBranch); + chance_of_going_deeper_to_extract_composite_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperToExtractComposite); + chance_of_going_deeper_to_insert_in_composite_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite); + chance_of_going_deeper_when_making_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain); + chance_of_having_two_blocks_in_loop_to_create_int_synonym_ = + ChooseBetweenMinAndMax(kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym); + chance_of_inlining_function_ = + ChooseBetweenMinAndMax(kChanceOfInliningFunction); + chance_of_interchanging_signedness_of_integer_operands_ = + ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands); + chance_of_interchanging_zero_like_constants_ = + ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants); + chance_of_inverting_comparison_operators_ = + ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators); + chance_of_making_donor_livesafe_ = + ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe); + chance_of_making_vector_operation_dynamic_ = + ChooseBetweenMinAndMax(kChanceOfMakingVectorOperationDynamic); + chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); + chance_of_merging_function_returns_ = + ChooseBetweenMinAndMax(kChanceOfMergingFunctionReturns); + chance_of_moving_block_down_ = + ChooseBetweenMinAndMax(kChanceOfMovingBlockDown); + chance_of_mutating_pointer_ = + ChooseBetweenMinAndMax(kChanceOfMutatingPointer); + chance_of_obfuscating_constant_ = + ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); + chance_of_outlining_function_ = + ChooseBetweenMinAndMax(kChanceOfOutliningFunction); + chance_of_permuting_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfPermutingFunctionVariables); + chance_of_permuting_instructions_ = + ChooseBetweenMinAndMax(kChanceOfPermutingInstructions); + chance_of_permuting_parameters_ = + ChooseBetweenMinAndMax(kChanceOfPermutingParameters); + chance_of_permuting_phi_operands_ = + ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands); + chance_of_propagating_instructions_down_ = + ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsDown); + chance_of_propagating_instructions_up_ = + ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsUp); + chance_of_pushing_id_through_variable_ = + ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable); + chance_of_replacing_add_sub_mul_with_carrying_extended_ = + ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended); + chance_of_replacing_branch_from_dead_block_with_exit_ = + ChooseBetweenMinAndMax(kChanceOfReplacingBranchFromDeadBlockWithExit); + chance_of_replacing_copy_memory_with_load_store_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore); + chance_of_replacing_copyobject_with_store_load_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad); + chance_of_replacing_id_with_synonym_ = + ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); + chance_of_replacing_irrelevant_id_ = + ChooseBetweenMinAndMax(kChanceOfReplacingIrrelevantId); + chance_of_replacing_linear_algebra_instructions_ = + ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions); + chance_of_replacing_load_store_with_copy_memory_ = + ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory); + chance_of_replacing_opphi_id_from_dead_predecessor_ = + ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor); + chance_of_replacing_opselect_with_conditional_branch_ = + ChooseBetweenMinAndMax(kChanceOfReplacingOpSelectWithConditionalBranch); + chance_of_replacing_parameters_with_globals_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals); + chance_of_replacing_parameters_with_struct_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct); + chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + chance_of_swapping_another_pair_of_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfSwappingAnotherPairOfFunctionVariables); + chance_of_swapping_conditional_branch_operands_ = + ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands); + chance_of_swapping_functions_ = + ChooseBetweenMinAndMax(kChanceOfSwappingFunctions); + chance_of_toggling_access_chain_instruction_ = + ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); + chance_of_wrapping_region_in_selection_ = + ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection); + chance_of_wrapping_vector_synonym_ = + ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym); +} + +FuzzerContext::~FuzzerContext() = default; + +uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; } + +std::vector FuzzerContext::GetFreshIds(const uint32_t count) { + std::vector fresh_ids(count); + + for (uint32_t& fresh_id : fresh_ids) { + fresh_id = next_fresh_id_++; + } + + return fresh_ids; +} + +bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); } + +bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) { + assert(percentage_chance <= 100); + return random_generator_->RandomPercentage() < percentage_chance; +} + +uint32_t FuzzerContext::ChooseBetweenMinAndMax( + const std::pair& min_max) { + assert(min_max.first <= min_max.second); + return min_max.first + + random_generator_->RandomUint32(min_max.second - min_max.first + 1); +} + +protobufs::TransformationAddSynonym::SynonymType +FuzzerContext::GetRandomSynonymType() { + // value_count method is guaranteed to return a value greater than 0. + auto result_index = ChooseBetweenMinAndMax( + {0, static_cast( + protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value_count() - + 1)}); + auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value(result_index) + ->number(); + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) && + "|result| is not a value of SynonymType"); + return static_cast(result); +} + +uint32_t FuzzerContext::GetIdBoundLimit() const { return kIdBoundLimit; } + +uint32_t FuzzerContext::GetTransformationLimit() const { + return kTransformationLimit; +} + +uint32_t FuzzerContext::GetMinFreshId(opt::IRContext* ir_context) { + return ir_context->module()->id_bound() + kIdBoundGap; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.h new file mode 100644 index 00000000..77a5d400 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_context.h @@ -0,0 +1,610 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_ +#define SOURCE_FUZZ_FUZZER_CONTEXT_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/random_generator.h" +#include "source/opt/function.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all parameters that control the fuzzing process, such as the +// source of randomness and the probabilities with which transformations are +// applied. +class FuzzerContext { + public: + // Constructs a fuzzer context with a given random generator and the minimum + // value that can be used for fresh ids. + FuzzerContext(std::unique_ptr random_generator, + uint32_t min_fresh_id, bool is_wgsl_compatible); + + ~FuzzerContext(); + + // Returns a random boolean. + bool ChooseEven(); + + // Returns true if and only if a randomly-chosen integer in the range [0, 100] + // is less than |percentage_chance|. + bool ChoosePercentage(uint32_t percentage_chance); + + // Returns a random index into |sequence|, which is expected to have a 'size' + // method, and which must be non-empty. Typically 'HasSizeMethod' will be an + // std::vector. + template + uint32_t RandomIndex(const HasSizeMethod& sequence) const { + assert(sequence.size() > 0); + return random_generator_->RandomUint32( + static_cast(sequence.size())); + } + + // Selects a random index into |sequence|, removes the element at that index + // and returns it. + template + T RemoveAtRandomIndex(std::vector* sequence) const { + uint32_t index = RandomIndex(*sequence); + T result = sequence->at(index); + sequence->erase(sequence->begin() + index); + return result; + } + + // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively. + // |lo| and |hi| must be valid indices to the |sequence|. + template + void Shuffle(std::vector* sequence, size_t lo, size_t hi) const { + auto& array = *sequence; + + if (array.empty()) { + return; + } + + assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid"); + + // i > lo to account for potential infinite loop when lo == 0 + for (size_t i = hi; i > lo; --i) { + auto index = + random_generator_->RandomUint32(static_cast(i - lo + 1)); + + if (lo + index != i) { + // Introduce std::swap to the scope but don't use it + // directly since there might be a better overload + using std::swap; + swap(array[lo + index], array[i]); + } + } + } + + // Randomly shuffles a |sequence|. + template + void Shuffle(std::vector* sequence) const { + if (!sequence->empty()) { + Shuffle(sequence, 0, sequence->size() - 1); + } + } + + // Yields an id that is guaranteed not to be used in the module being fuzzed, + // or to have been issued before. + uint32_t GetFreshId(); + + // Returns a vector of |count| fresh ids. + std::vector GetFreshIds(uint32_t count); + + // A suggested limit on the id bound for the module being fuzzed. This is + // useful for deciding when to stop the overall fuzzing process. Furthermore, + // fuzzer passes that run the risk of spiralling out of control can + // periodically check this limit and terminate early if it has been reached. + uint32_t GetIdBoundLimit() const; + + // A suggested limit on the number of transformations that should be applied. + // Also useful to control the overall fuzzing process and rein in individual + // fuzzer passes. + uint32_t GetTransformationLimit() const; + + // Returns the minimum fresh id that can be used given the |ir_context|. + static uint32_t GetMinFreshId(opt::IRContext* ir_context); + + // Returns true if all transformations should be compatible with WGSL. + bool IsWgslCompatible() const { + return is_wgsl_compatible_; + } + + // Probabilities associated with applying various transformations. + // Keep them in alphabetical order. + uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const { + return chance_of_accepting_repeated_pass_recommendation_; + } + uint32_t GetChanceOfAddingAccessChain() const { + return chance_of_adding_access_chain_; + } + uint32_t GetChanceOfAddingAnotherPassToPassLoop() const { + return chance_of_adding_another_pass_to_pass_loop_; + } + uint32_t GetChanceOfAddingAnotherStructField() const { + return chance_of_adding_another_struct_field_; + } + uint32_t GetChanceOfAddingArrayOrStructType() const { + return chance_of_adding_array_or_struct_type_; + } + uint32_t GetChanceOfAddingAtomicLoad() const { + return chance_of_adding_atomic_load_; + } + uint32_t GetChanceOfAddingAtomicStore() const { + return chance_of_adding_atomic_store_; + } + uint32_t GetChanceOfAddingBitInstructionSynonym() const { + return chance_of_adding_bit_instruction_synonym_; + } + uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() const { + return chance_of_adding_both_branches_when_replacing_opselect_; + } + uint32_t GetChanceOfAddingCompositeExtract() const { + return chance_of_adding_composite_extract_; + } + uint32_t GetChanceOfAddingCompositeInsert() const { + return chance_of_adding_composite_insert_; + } + uint32_t GetChanceOfAddingCopyMemory() const { + return chance_of_adding_copy_memory_; + } + uint32_t GetChanceOfAddingDeadBlock() const { + return chance_of_adding_dead_block_; + } + uint32_t GetChanceOfAddingDeadBreak() const { + return chance_of_adding_dead_break_; + } + uint32_t GetChanceOfAddingDeadContinue() const { + return chance_of_adding_dead_continue_; + } + uint32_t GetChanceOfAddingEquationInstruction() const { + return chance_of_adding_equation_instruction_; + } + uint32_t GetChanceOfAddingGlobalVariable() const { + return chance_of_adding_global_variable_; + } + uint32_t GetChanceOfAddingImageSampleUnusedComponents() const { + return chance_of_adding_image_sample_unused_components_; + } + uint32_t GetChanceOfAddingLoad() const { return chance_of_adding_load_; } + uint32_t GetChanceOfAddingLocalVariable() const { + return chance_of_adding_local_variable_; + } + uint32_t GetChanceOfAddingLoopPreheader() const { + return chance_of_adding_loop_preheader_; + } + uint32_t GetChanceOfAddingMatrixType() const { + return chance_of_adding_matrix_type_; + } + uint32_t GetChanceOfAddingNoContractionDecoration() const { + return chance_of_adding_no_contraction_decoration_; + } + uint32_t GetChanceOfAddingOpPhiSynonym() const { + return chance_of_adding_opphi_synonym_; + } + uint32_t GetChanceOfAddingParameters() const { + return chance_of_adding_parameters; + } + uint32_t GetChanceOfAddingRelaxedDecoration() const { + return chance_of_adding_relaxed_decoration_; + } + uint32_t GetChanceOfAddingStore() const { return chance_of_adding_store_; } + uint32_t GetChanceOfAddingSynonyms() const { + return chance_of_adding_synonyms_; + } + uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() const { + return chance_of_adding_true_branch_when_replacing_opselect_; + } + uint32_t GetChanceOfAddingVectorShuffle() const { + return chance_of_adding_vector_shuffle_; + } + uint32_t GetChanceOfAddingVectorType() const { + return chance_of_adding_vector_type_; + } + uint32_t GetChanceOfAdjustingBranchWeights() const { + return chance_of_adjusting_branch_weights_; + } + uint32_t GetChanceOfAdjustingFunctionControl() const { + return chance_of_adjusting_function_control_; + } + uint32_t GetChanceOfAdjustingLoopControl() const { + return chance_of_adjusting_loop_control_; + } + uint32_t GetChanceOfAdjustingMemoryOperandsMask() const { + return chance_of_adjusting_memory_operands_mask_; + } + uint32_t GetChanceOfAdjustingSelectionControl() const { + return chance_of_adjusting_selection_control_; + } + uint32_t GetChanceOfCallingFunction() const { + return chance_of_calling_function_; + } + uint32_t GetChanceOfChoosingStructTypeVsArrayType() const { + return chance_of_choosing_struct_type_vs_array_type_; + } + uint32_t GetChanceOfChoosingWorkgroupStorageClass() const { + return chance_of_choosing_workgroup_storage_class_; + } + uint32_t GetChanceOfConstructingComposite() const { + return chance_of_constructing_composite_; + } + uint32_t GetChanceOfCopyingObject() const { + return chance_of_copying_object_; + } + uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() const { + return chance_of_creating_int_synonyms_using_loops_; + } + uint32_t GetChanceOfDonatingAdditionalModule() const { + return chance_of_donating_additional_module_; + } + uint32_t GetChanceOfDuplicatingRegionWithSelection() const { + return chance_of_duplicating_region_with_selection_; + } + uint32_t GetChanceOfExpandingVectorReduction() const { + return chance_of_expanding_vector_reduction_; + } + uint32_t GetChanceOfFlatteningConditionalBranch() const { + return chance_of_flattening_conditional_branch_; + } + uint32_t GetChanceOfGoingDeeperToExtractComposite() const { + return chance_of_going_deeper_to_extract_composite_; + } + uint32_t GetChanceOfGoingDeeperToInsertInComposite() const { + return chance_of_going_deeper_to_insert_in_composite_; + } + uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() const { + return chance_of_going_deeper_when_making_access_chain_; + } + uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() const { + return chance_of_having_two_blocks_in_loop_to_create_int_synonym_; + } + uint32_t GetChanceOfInliningFunction() const { + return chance_of_inlining_function_; + } + uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() const { + return chance_of_interchanging_signedness_of_integer_operands_; + } + uint32_t GetChanceOfInterchangingZeroLikeConstants() const { + return chance_of_interchanging_zero_like_constants_; + } + uint32_t GetChanceOfInvertingComparisonOperators() const { + return chance_of_inverting_comparison_operators_; + } + uint32_t ChanceOfMakingDonorLivesafe() const { + return chance_of_making_donor_livesafe_; + } + uint32_t GetChanceOfMakingVectorOperationDynamic() const { + return chance_of_making_vector_operation_dynamic_; + } + uint32_t GetChanceOfMergingBlocks() const { + return chance_of_merging_blocks_; + } + uint32_t GetChanceOfMergingFunctionReturns() const { + return chance_of_merging_function_returns_; + } + uint32_t GetChanceOfMovingBlockDown() const { + return chance_of_moving_block_down_; + } + uint32_t GetChanceOfMutatingPointer() const { + return chance_of_mutating_pointer_; + } + uint32_t GetChanceOfObfuscatingConstant() const { + return chance_of_obfuscating_constant_; + } + uint32_t GetChanceOfOutliningFunction() const { + return chance_of_outlining_function_; + } + uint32_t GetChanceOfPermutingFunctionVariables() const { + return chance_of_permuting_function_variables_; + } + uint32_t GetChanceOfPermutingInstructions() const { + return chance_of_permuting_instructions_; + } + uint32_t GetChanceOfPermutingParameters() const { + return chance_of_permuting_parameters_; + } + uint32_t GetChanceOfPermutingPhiOperands() const { + return chance_of_permuting_phi_operands_; + } + uint32_t GetChanceOfPropagatingInstructionsDown() const { + return chance_of_propagating_instructions_down_; + } + uint32_t GetChanceOfPropagatingInstructionsUp() const { + return chance_of_propagating_instructions_up_; + } + uint32_t GetChanceOfPushingIdThroughVariable() const { + return chance_of_pushing_id_through_variable_; + } + uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() const { + return chance_of_replacing_add_sub_mul_with_carrying_extended_; + } + uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() const { + return chance_of_replacing_branch_from_dead_block_with_exit_; + } + uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() const { + return chance_of_replacing_copy_memory_with_load_store_; + } + uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() const { + return chance_of_replacing_copyobject_with_store_load_; + } + uint32_t GetChanceOfReplacingIdWithSynonym() const { + return chance_of_replacing_id_with_synonym_; + } + uint32_t GetChanceOfReplacingIrrelevantId() const { + return chance_of_replacing_irrelevant_id_; + } + uint32_t GetChanceOfReplacingLinearAlgebraInstructions() const { + return chance_of_replacing_linear_algebra_instructions_; + } + uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() const { + return chance_of_replacing_load_store_with_copy_memory_; + } + uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() const { + return chance_of_replacing_opphi_id_from_dead_predecessor_; + } + uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() const { + return chance_of_replacing_opselect_with_conditional_branch_; + } + uint32_t GetChanceOfReplacingParametersWithGlobals() const { + return chance_of_replacing_parameters_with_globals_; + } + uint32_t GetChanceOfReplacingParametersWithStruct() const { + return chance_of_replacing_parameters_with_struct_; + } + uint32_t GetChanceOfSplittingBlock() const { + return chance_of_splitting_block_; + } + uint32_t GetChanceOfSwappingAnotherPairOfFunctionVariables() const { + return chance_of_swapping_another_pair_of_function_variables_; + } + uint32_t GetChanceOfSwappingConditionalBranchOperands() const { + return chance_of_swapping_conditional_branch_operands_; + } + + uint32_t GetChanceOfSwappingFunctions() const { + return chance_of_swapping_functions_; + } + + uint32_t GetChanceOfTogglingAccessChainInstruction() const { + return chance_of_toggling_access_chain_instruction_; + } + uint32_t GetChanceOfWrappingRegionInSelection() const { + return chance_of_wrapping_region_in_selection_; + } + + uint32_t GetChanceOfWrappingVectorSynonym() const { + return chance_of_wrapping_vector_synonym_; + } + + // Other functions to control transformations. Keep them in alphabetical + // order. + uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const { + return max_equivalence_class_size_for_data_synonym_fact_closure_; + } + uint32_t GetMaximumNumberOfFunctionParameters() const { + return max_number_of_function_parameters_; + } + uint32_t GetMaximumNumberOfParametersReplacedWithStruct() const { + return max_number_of_parameters_replaced_with_struct_; + } + std::pair GetRandomBranchWeights() { + std::pair branch_weights = {0, 0}; + + while (branch_weights.first == 0 && branch_weights.second == 0) { + // Using INT32_MAX to do not overflow UINT32_MAX when the branch weights + // are added together. + branch_weights.first = random_generator_->RandomUint32(INT32_MAX); + branch_weights.second = random_generator_->RandomUint32(INT32_MAX); + } + + return branch_weights; + } + std::vector GetRandomComponentsForVectorShuffle( + uint32_t max_component_index) { + // Component count must be in range [2, 4]. + std::vector components(random_generator_->RandomUint32(2) + 2); + + for (uint32_t& component : components) { + component = random_generator_->RandomUint32(max_component_index); + } + + return components; + } + uint32_t GetRandomCompositeExtractIndex(uint32_t number_of_members) { + assert(number_of_members > 0 && "Composite object must have some members"); + return ChooseBetweenMinAndMax({0, number_of_members - 1}); + } + uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) { + return random_generator_->RandomUint32(composite_size_bound); + } + uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) { + return random_generator_->RandomUint32(number_of_components); + } + uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) { + return random_generator_->RandomUint32(vector_width); + } + int64_t GetRandomValueForStepConstantInLoop() { + return random_generator_->RandomUint64(UINT64_MAX); + } + uint32_t GetRandomLoopControlPartialCount() { + return random_generator_->RandomUint32(max_loop_control_partial_count_); + } + uint32_t GetRandomLoopControlPeelCount() { + return random_generator_->RandomUint32(max_loop_control_peel_count_); + } + uint32_t GetRandomLoopLimit() { + return random_generator_->RandomUint32(max_loop_limit_); + } + uint32_t GetRandomNumberOfLoopIterations(uint32_t max_num_iterations) { + return ChooseBetweenMinAndMax({1, max_num_iterations}); + } + uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) { + assert(num_of_params < GetMaximumNumberOfFunctionParameters()); + return ChooseBetweenMinAndMax( + {1, std::min(max_number_of_new_parameters_, + GetMaximumNumberOfFunctionParameters() - num_of_params)}); + } + uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) { + assert(num_params != 0 && "A function must have parameters to replace"); + return ChooseBetweenMinAndMax( + {1, std::min(num_params, + GetMaximumNumberOfParametersReplacedWithStruct())}); + } + uint32_t GetRandomSizeForNewArray() { + // Ensure that the array size is non-zero. + return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; + } + protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType(); + uint32_t GetRandomUnusedComponentCountForImageSample( + uint32_t max_unused_component_count) { + // Ensure that the number of unused components is non-zero. + return random_generator_->RandomUint32(max_unused_component_count) + 1; + } + uint32_t GetWidthOfWrappingVector() { + return 2 + random_generator_->RandomUint32(3); + } + bool GoDeeperInConstantObfuscation(uint32_t depth) { + return go_deeper_in_constant_obfuscation_(depth, random_generator_.get()); + } + + private: + // The source of randomness. + std::unique_ptr random_generator_; + // The next fresh id to be issued. + uint32_t next_fresh_id_; + + // True if all transformations should be compatible with WGSL spec. + bool is_wgsl_compatible_; + + // Probabilities associated with applying various transformations. + // Keep them in alphabetical order. + uint32_t chance_of_accepting_repeated_pass_recommendation_; + uint32_t chance_of_adding_access_chain_; + uint32_t chance_of_adding_another_pass_to_pass_loop_; + uint32_t chance_of_adding_another_struct_field_; + uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_atomic_load_; + uint32_t chance_of_adding_atomic_store_; + uint32_t chance_of_adding_bit_instruction_synonym_; + uint32_t chance_of_adding_both_branches_when_replacing_opselect_; + uint32_t chance_of_adding_composite_extract_; + uint32_t chance_of_adding_composite_insert_; + uint32_t chance_of_adding_copy_memory_; + uint32_t chance_of_adding_dead_block_; + uint32_t chance_of_adding_dead_break_; + uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_equation_instruction_; + uint32_t chance_of_adding_global_variable_; + uint32_t chance_of_adding_image_sample_unused_components_; + uint32_t chance_of_adding_load_; + uint32_t chance_of_adding_local_variable_; + uint32_t chance_of_adding_loop_preheader_; + uint32_t chance_of_adding_matrix_type_; + uint32_t chance_of_adding_no_contraction_decoration_; + uint32_t chance_of_adding_opphi_synonym_; + uint32_t chance_of_adding_parameters; + uint32_t chance_of_adding_relaxed_decoration_; + uint32_t chance_of_adding_store_; + uint32_t chance_of_adding_synonyms_; + uint32_t chance_of_adding_true_branch_when_replacing_opselect_; + uint32_t chance_of_adding_vector_shuffle_; + uint32_t chance_of_adding_vector_type_; + uint32_t chance_of_adjusting_branch_weights_; + uint32_t chance_of_adjusting_function_control_; + uint32_t chance_of_adjusting_loop_control_; + uint32_t chance_of_adjusting_memory_operands_mask_; + uint32_t chance_of_adjusting_selection_control_; + uint32_t chance_of_calling_function_; + uint32_t chance_of_choosing_struct_type_vs_array_type_; + uint32_t chance_of_choosing_workgroup_storage_class_; + uint32_t chance_of_constructing_composite_; + uint32_t chance_of_copying_object_; + uint32_t chance_of_creating_int_synonyms_using_loops_; + uint32_t chance_of_donating_additional_module_; + uint32_t chance_of_duplicating_region_with_selection_; + uint32_t chance_of_expanding_vector_reduction_; + uint32_t chance_of_flattening_conditional_branch_; + uint32_t chance_of_going_deeper_to_extract_composite_; + uint32_t chance_of_going_deeper_to_insert_in_composite_; + uint32_t chance_of_going_deeper_when_making_access_chain_; + uint32_t chance_of_having_two_blocks_in_loop_to_create_int_synonym_; + uint32_t chance_of_inlining_function_; + uint32_t chance_of_interchanging_signedness_of_integer_operands_; + uint32_t chance_of_interchanging_zero_like_constants_; + uint32_t chance_of_inverting_comparison_operators_; + uint32_t chance_of_making_donor_livesafe_; + uint32_t chance_of_making_vector_operation_dynamic_; + uint32_t chance_of_merging_blocks_; + uint32_t chance_of_merging_function_returns_; + uint32_t chance_of_moving_block_down_; + uint32_t chance_of_mutating_pointer_; + uint32_t chance_of_obfuscating_constant_; + uint32_t chance_of_outlining_function_; + uint32_t chance_of_permuting_function_variables_; + uint32_t chance_of_permuting_instructions_; + uint32_t chance_of_permuting_parameters_; + uint32_t chance_of_permuting_phi_operands_; + uint32_t chance_of_propagating_instructions_down_; + uint32_t chance_of_propagating_instructions_up_; + uint32_t chance_of_pushing_id_through_variable_; + uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_; + uint32_t chance_of_replacing_branch_from_dead_block_with_exit_; + uint32_t chance_of_replacing_copy_memory_with_load_store_; + uint32_t chance_of_replacing_copyobject_with_store_load_; + uint32_t chance_of_replacing_id_with_synonym_; + uint32_t chance_of_replacing_irrelevant_id_; + uint32_t chance_of_replacing_linear_algebra_instructions_; + uint32_t chance_of_replacing_load_store_with_copy_memory_; + uint32_t chance_of_replacing_opphi_id_from_dead_predecessor_; + uint32_t chance_of_replacing_opselect_with_conditional_branch_; + uint32_t chance_of_replacing_parameters_with_globals_; + uint32_t chance_of_replacing_parameters_with_struct_; + uint32_t chance_of_splitting_block_; + uint32_t chance_of_swapping_another_pair_of_function_variables_; + uint32_t chance_of_swapping_conditional_branch_operands_; + uint32_t chance_of_swapping_functions_; + uint32_t chance_of_toggling_access_chain_instruction_; + uint32_t chance_of_wrapping_region_in_selection_; + uint32_t chance_of_wrapping_vector_synonym_; + + // Limits associated with various quantities for which random values are + // chosen during fuzzing. + // Keep them in alphabetical order. + uint32_t max_equivalence_class_size_for_data_synonym_fact_closure_; + uint32_t max_loop_control_partial_count_; + uint32_t max_loop_control_peel_count_; + uint32_t max_loop_limit_; + uint32_t max_new_array_size_limit_; + uint32_t max_number_of_function_parameters_; + uint32_t max_number_of_new_parameters_; + uint32_t max_number_of_parameters_replaced_with_struct_; + + // Functions to determine with what probability to go deeper when generating + // or mutating constructs recursively. + const std::function& + go_deeper_in_constant_obfuscation_; + + // Requires |min_max.first| <= |min_max.second|, and returns a value in the + // range [ |min_max.first|, |min_max.second| ] + uint32_t ChooseBetweenMinAndMax(const std::pair& min_max); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_CONTEXT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.cpp new file mode 100644 index 00000000..02d8aa1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -0,0 +1,801 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_loop_preheader.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPass::FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : ir_context_(ir_context), + transformation_context_(transformation_context), + fuzzer_context_(fuzzer_context), + transformations_(transformations), + ignore_inapplicable_transformations_( + ignore_inapplicable_transformations) {} + +FuzzerPass::~FuzzerPass() = default; + +std::vector FuzzerPass::FindAvailableInstructions( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, + std::function + instruction_is_relevant) const { + // TODO(afd) The following is (relatively) simple, but may end up being + // prohibitively inefficient, as it walks the whole dominator tree for + // every instruction that is considered. + + std::vector result; + // Consider all global declarations + for (auto& global : GetIRContext()->module()->types_values()) { + if (instruction_is_relevant(GetIRContext(), &global)) { + result.push_back(&global); + } + } + + // Consider all function parameters + function->ForEachParam( + [this, &instruction_is_relevant, &result](opt::Instruction* param) { + if (instruction_is_relevant(GetIRContext(), param)) { + result.push_back(param); + } + }); + + // Consider all previous instructions in this block + for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it; + ++prev_inst_it) { + if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) { + result.push_back(&*prev_inst_it); + } + } + + // Walk the dominator tree to consider all instructions from dominating + // blocks + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); + for (auto next_dominator = dominator_analysis->ImmediateDominator(block); + next_dominator != nullptr; + next_dominator = + dominator_analysis->ImmediateDominator(next_dominator)) { + for (auto& dominating_inst : *next_dominator) { + if (instruction_is_relevant(GetIRContext(), &dominating_inst)) { + result.push_back(&dominating_inst); + } + } + } + return result; +} + +void FuzzerPass::ForEachInstructionWithInstructionDescriptor( + opt::Function* function, + std::function< + void(opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action) { + // Consider only reachable blocks. We do this in a separate loop to avoid + // recomputing the dominator analysis every time |action| changes the + // module. + std::vector reachable_blocks; + + for (auto& block : *function) { + if (GetIRContext()->IsReachable(block)) { + reachable_blocks.push_back(&block); + } + } + + for (auto* block : reachable_blocks) { + // We now consider every instruction in the block, randomly deciding + // whether to apply a transformation before it. + + // In order for transformations to insert new instructions, they need to + // be able to identify the instruction to insert before. We describe an + // instruction via its opcode, 'opc', a base instruction 'base' that has a + // result id, and the number of instructions with opcode 'opc' that we + // should skip when searching from 'base' for the desired instruction. + // (An instruction that has a result id is represented by its own opcode, + // itself as 'base', and a skip-count of 0.) + std::vector> + base_opcode_skip_triples; + + // The initial base instruction is the block label. + uint32_t base = block->id(); + + // Counts the number of times we have seen each opcode since we reset the + // base instruction. + std::map skip_count; + + // Consider every instruction in the block. The label is excluded: it is + // only necessary to consider it as a base in case the first instruction + // in the block does not have a result id. + for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) { + if (inst_it->HasResultId()) { + // In the case that the instruction has a result id, we use the + // instruction as its own base, and clear the skip counts we have + // collected. + base = inst_it->result_id(); + skip_count.clear(); + } + const spv::Op opcode = inst_it->opcode(); + + // Invoke the provided function, which might apply a transformation. + action(block, inst_it, + MakeInstructionDescriptor( + base, opcode, + skip_count.count(opcode) ? skip_count.at(opcode) : 0)); + + if (!inst_it->HasResultId()) { + skip_count[opcode] = + skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1; + } + } + } +} + +void FuzzerPass::ForEachInstructionWithInstructionDescriptor( + std::function< + void(opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action) { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + ForEachInstructionWithInstructionDescriptor( + &function, + [&action, &function]( + opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + action(&function, block, inst_it, instruction_descriptor); + }); + } +} + +void FuzzerPass::ApplyTransformation(const Transformation& transformation) { + if (ignore_inapplicable_transformations_) { + // If an applicable-by-construction transformation turns out to be + // inapplicable, this is a bug in the fuzzer. However, when deploying the + // fuzzer at scale for finding bugs in SPIR-V processing tools it is + // desirable to silently ignore such bugs. This code path caters for that + // scenario. + if (!transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + return; + } + } else { + // This code path caters for debugging bugs in the fuzzer, where an + // applicable-by-construction transformation turns out to be inapplicable. + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + } + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); +} + +bool FuzzerPass::MaybeApplyTransformation( + const Transformation& transformation) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); + return true; + } + return false; +} + +uint32_t FuzzerPass::FindOrCreateBoolType() { + if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeBoolean(result)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { + opt::analysis::Integer int_type(width, is_signed); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeInt(result, width, is_signed)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { + opt::analysis::Float float_type(width); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeFloat(result, width)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFunctionType( + uint32_t return_type_id, const std::vector& argument_id) { + // FindFunctionType has a single argument for OpTypeFunction operands + // so we will have to copy them all in this vector + std::vector type_ids(argument_id.size() + 1); + type_ids[0] = return_type_id; + std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1); + + // Check if type exists + auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids); + if (existing_id) { + return existing_id; + } + + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeFunction(result, return_type_id, argument_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count) { + assert(component_count >= 2 && component_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Type* component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "Precondition: the component type must exist."); + opt::analysis::Vector vector_type(component_type, component_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeVector(result, component_type_id, component_count)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, + uint32_t row_count) { + assert(column_count >= 2 && column_count <= 4 && + "Precondition: column count must be in range [2, 4]."); + assert(row_count >= 2 && row_count <= 4 && + "Precondition: row count must be in range [2, 4]."); + uint32_t column_type_id = + FindOrCreateVectorType(FindOrCreateFloatType(32), row_count); + opt::analysis::Type* column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeMatrix(result, column_type_id, column_count)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateStructType( + const std::vector& component_type_ids) { + if (auto existing_id = + fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) { + return existing_id; + } + auto new_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids)); + return new_id; +} + +uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id, + spv::StorageClass storage_class) { + // We do not use the type manager here, due to problems related to isomorphic + // but distinct structs not being regarded as different. + auto existing_id = fuzzerutil::MaybeGetPointerType( + GetIRContext(), base_type_id, storage_class); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypePointer(result, storage_class, base_type_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreatePointerToIntegerType( + uint32_t width, bool is_signed, spv::StorageClass storage_class) { + return FindOrCreatePointerType(FindOrCreateIntegerType(width, is_signed), + storage_class); +} + +uint32_t FuzzerPass::FindOrCreateIntegerConstant( + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { + auto int_type_id = FindOrCreateIntegerType(width, is_signed); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, int_type_id, + is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantScalar(result, int_type_id, + words, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFloatConstant( + const std::vector& words, uint32_t width, bool is_irrelevant) { + auto float_type_id = FindOrCreateFloatType(width); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, float_type_id, + is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantScalar(result, float_type_id, + words, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) { + auto bool_type_id = FindOrCreateBoolType(); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u}, + bool_type_id, is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddConstantBoolean(result, value, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, + uint32_t type_id, + bool is_irrelevant) { + assert(type_id && "Constant's type id can't be 0."); + + const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id); + assert(type && "Type does not exist."); + + if (type->AsBool()) { + assert(words.size() == 1); + return FindOrCreateBoolConstant(words[0], is_irrelevant); + } else if (const auto* integer = type->AsInteger()) { + return FindOrCreateIntegerConstant(words, integer->width(), + integer->IsSigned(), is_irrelevant); + } else if (const auto* floating = type->AsFloat()) { + return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant); + } + + // This assertion will fail in debug build but not in release build + // so we return 0 to make compiler happy. + assert(false && "Constant type is not supported"); + return 0; +} + +uint32_t FuzzerPass::FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant) { + if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant( + GetIRContext(), *GetTransformationContext(), component_ids, type_id, + is_irrelevant)) { + return existing_constant; + } + uint32_t result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantComposite( + result, type_id, component_ids, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { + for (auto& inst : GetIRContext()->types_values()) { + if (inst.opcode() == spv::Op::OpUndef && inst.type_id() == type_id) { + return inst.result_id(); + } + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef(result, type_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) { + // Find existing declaration + opt::analysis::NullConstant null_constant( + GetIRContext()->get_type_mgr()->GetType(type_id)); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&null_constant); + + // Return if found + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + + // Create new if not found + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull(result, type_id)); + return result; +} + +std::pair, std::map>> +FuzzerPass::GetAvailableBasicTypesAndPointers( + spv::StorageClass storage_class) const { + // Records all of the basic types available in the module. + std::set basic_types; + + // For each basic type, records all the associated pointer types that target + // the basic type and that have |storage_class| as their storage class. + std::map> basic_type_to_pointers; + + for (auto& inst : GetIRContext()->types_values()) { + // For each basic type that we come across, record type, and the fact that + // we cannot yet have seen any pointers that use the basic type as its + // pointee type. + // + // For pointer types with basic pointee types, associate the pointer type + // with the basic type. + switch (inst.opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + // These are all basic types. + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + break; + case spv::Op::OpTypeArray: + // An array type is basic if its base type is basic. + if (basic_types.count(inst.GetSingleWordInOperand(0))) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } + break; + case spv::Op::OpTypeStruct: { + // A struct type is basic if it does not have the Block/BufferBlock + // decoration, and if all of its members are basic. + if (!fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { + bool all_members_are_basic_types = true; + for (uint32_t i = 0; i < inst.NumInOperands(); i++) { + if (!basic_types.count(inst.GetSingleWordInOperand(i))) { + all_members_are_basic_types = false; + break; + } + } + if (all_members_are_basic_types) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } + } + break; + } + case spv::Op::OpTypePointer: { + // We are interested in the pointer if its pointee type is basic and it + // has the right storage class. + auto pointee_type = inst.GetSingleWordInOperand(1); + if (spv::StorageClass(inst.GetSingleWordInOperand(0)) == + storage_class && + basic_types.count(pointee_type)) { + // The pointer has the desired storage class, and its pointee type is + // a basic type, so we are interested in it. Associate it with its + // basic type. + basic_type_to_pointers.at(pointee_type).push_back(inst.result_id()); + } + break; + } + default: + break; + } + } + return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers}; +} + +uint32_t FuzzerPass::FindOrCreateZeroConstant( + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { + auto type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); + assert(type_instruction && "The type instruction must exist."); + switch (type_instruction->opcode()) { + case spv::Op::OpTypeBool: + return FindOrCreateBoolConstant(false, is_irrelevant); + case spv::Op::OpTypeFloat: { + auto width = type_instruction->GetSingleWordInOperand(0); + auto num_words = (width + 32 - 1) / 32; + return FindOrCreateFloatConstant(std::vector(num_words, 0), + width, is_irrelevant); + } + case spv::Op::OpTypeInt: { + auto width = type_instruction->GetSingleWordInOperand(0); + auto num_words = (width + 32 - 1) / 32; + return FindOrCreateIntegerConstant( + std::vector(num_words, 0), width, + type_instruction->GetSingleWordInOperand(1), is_irrelevant); + } + case spv::Op::OpTypeArray: { + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = + fuzzerutil::GetArraySize(*type_instruction, GetIRContext()); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); + } + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: { + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = type_instruction->GetSingleWordInOperand(1); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); + } + case spv::Op::OpTypeStruct: { + assert(!fuzzerutil::HasBlockOrBufferBlockDecoration( + GetIRContext(), scalar_or_composite_type_id) && + "We do not construct constants of struct types decorated with " + "Block or BufferBlock."); + std::vector field_zero_ids; + for (uint32_t index = 0; index < type_instruction->NumInOperands(); + index++) { + field_zero_ids.push_back(FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(index), is_irrelevant)); + } + return FindOrCreateCompositeConstant( + field_zero_ids, scalar_or_composite_type_id, is_irrelevant); + } + default: + assert(false && "Unknown type."); + return 0; + } +} + +void FuzzerPass::MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace) { + // Only consider this use if it is in a block + if (!GetIRContext()->get_instr_block(use_inst)) { + return; + } + + // Get the index of the operand restricted to input operands. + uint32_t in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + auto id_use_descriptor = + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index); + uses_to_replace->emplace_back( + std::make_pair(id_use_descriptor, replacement_id)); +} + +opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader( + uint32_t header_id) { + auto header_block = fuzzerutil::MaybeFindBlock(GetIRContext(), header_id); + + assert(header_block && header_block->IsLoopHeader() && + "|header_id| should be the label id of a loop header"); + + auto predecessors = GetIRContext()->cfg()->preds(header_id); + + assert(predecessors.size() >= 2 && + "The block |header_id| should be reachable."); + + auto function = header_block->GetParent(); + + if (predecessors.size() == 2) { + // The header has a single out-of-loop predecessor, which could be a + // preheader. + + opt::BasicBlock* maybe_preheader; + + if (GetIRContext()->GetDominatorAnalysis(function)->Dominates( + header_id, predecessors[0])) { + // The first predecessor is the back-edge block, because the header + // dominates it, so the second one is out of the loop. + maybe_preheader = &*function->FindBlock(predecessors[1]); + } else { + // The first predecessor is out of the loop. + maybe_preheader = &*function->FindBlock(predecessors[0]); + } + + // |maybe_preheader| is a preheader if it branches unconditionally to + // the header. We also require it not to be a loop header. + if (maybe_preheader->terminator()->opcode() == spv::Op::OpBranch && + !maybe_preheader->IsLoopHeader()) { + return maybe_preheader; + } + } + + // We need to add a preheader. + + // Get a fresh id for the preheader. + uint32_t preheader_id = GetFuzzerContext()->GetFreshId(); + + // Get a fresh id for each OpPhi instruction, if there is more than one + // out-of-loop predecessor. + std::vector phi_ids; + if (predecessors.size() > 2) { + header_block->ForEachPhiInst( + [this, &phi_ids](opt::Instruction* /* unused */) { + phi_ids.push_back(GetFuzzerContext()->GetFreshId()); + }); + } + + // Add the preheader. + ApplyTransformation( + TransformationAddLoopPreheader(header_id, preheader_id, phi_ids)); + + // Make the newly-created preheader the new entry block. + return &*function->FindBlock(preheader_id); +} + +opt::BasicBlock* FuzzerPass::SplitBlockAfterOpPhiOrOpVariable( + uint32_t block_id) { + auto block = fuzzerutil::MaybeFindBlock(GetIRContext(), block_id); + assert(block && "|block_id| must be a block label"); + assert(!block->IsLoopHeader() && "|block_id| cannot be a loop header"); + + // Find the first non-OpPhi and non-OpVariable instruction. + auto non_phi_or_var_inst = &*block->begin(); + while (non_phi_or_var_inst->opcode() == spv::Op::OpPhi || + non_phi_or_var_inst->opcode() == spv::Op::OpVariable) { + non_phi_or_var_inst = non_phi_or_var_inst->NextNode(); + } + + // Split the block. + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst), + new_block_id)); + + // We need to return the newly-created block. + return &*block->GetParent()->FindBlock(new_block_id); +} + +uint32_t FuzzerPass::FindOrCreateLocalVariable( + uint32_t pointer_type_id, uint32_t function_id, + bool pointee_value_is_irrelevant) { + auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id); + // No unused variables in release mode. + (void)pointer_type; + assert(pointer_type && pointer_type->AsPointer() && + pointer_type->AsPointer()->storage_class() == + spv::StorageClass::Function && + "The pointer_type_id must refer to a defined pointer type with " + "storage class Function"); + auto function = fuzzerutil::FindFunction(GetIRContext(), function_id); + assert(function && "The function must be defined."); + + // First we try to find a suitable existing variable. + // All of the local variable declarations are located in the first block. + for (auto& instruction : *function->begin()) { + if (instruction.opcode() != spv::Op::OpVariable) { + continue; + } + // The existing OpVariable must have type |pointer_type_id|. + if (instruction.type_id() != pointer_type_id) { + continue; + } + // Check if the found variable is marked with PointeeValueIsIrrelevant + // according to |pointee_value_is_irrelevant|. + if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant( + instruction.result_id()) != pointee_value_is_irrelevant) { + continue; + } + return instruction.result_id(); + } + + // No such variable was found. Apply a transformation to get one. + uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_type_id); + uint32_t result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddLocalVariable( + result_id, pointer_type_id, function_id, + FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant), + pointee_value_is_irrelevant)); + return result_id; +} + +uint32_t FuzzerPass::FindOrCreateGlobalVariable( + uint32_t pointer_type_id, bool pointee_value_is_irrelevant) { + auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id); + // No unused variables in release mode. + (void)pointer_type; + assert( + pointer_type && pointer_type->AsPointer() && + (pointer_type->AsPointer()->storage_class() == + spv::StorageClass::Private || + pointer_type->AsPointer()->storage_class() == + spv::StorageClass::Workgroup) && + "The pointer_type_id must refer to a defined pointer type with storage " + "class Private or Workgroup"); + + // First we try to find a suitable existing variable. + for (auto& instruction : GetIRContext()->module()->types_values()) { + if (instruction.opcode() != spv::Op::OpVariable) { + continue; + } + // The existing OpVariable must have type |pointer_type_id|. + if (instruction.type_id() != pointer_type_id) { + continue; + } + // Check if the found variable is marked with PointeeValueIsIrrelevant + // according to |pointee_value_is_irrelevant|. + if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant( + instruction.result_id()) != pointee_value_is_irrelevant) { + continue; + } + return instruction.result_id(); + } + + // No such variable was found. Apply a transformation to get one. + uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_type_id); + auto storage_class = fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), pointer_type_id); + uint32_t result_id = GetFuzzerContext()->GetFreshId(); + + // A variable with storage class Workgroup shouldn't have an initializer. + if (storage_class == spv::StorageClass::Workgroup) { + ApplyTransformation(TransformationAddGlobalVariable( + result_id, pointer_type_id, spv::StorageClass::Workgroup, 0, + pointee_value_is_irrelevant)); + } else { + ApplyTransformation(TransformationAddGlobalVariable( + result_id, pointer_type_id, spv::StorageClass::Private, + FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant), + pointee_value_is_irrelevant)); + } + return result_id; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.h new file mode 100644 index 00000000..5c76be1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass.h @@ -0,0 +1,334 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_H_ + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Interface for applying a pass of transformations to a module. +class FuzzerPass { + public: + FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + virtual ~FuzzerPass(); + + // Applies the pass to the module |ir_context_|, assuming and updating + // information from |transformation_context_|, and using |fuzzer_context_| to + // guide the process. Appends to |transformations_| all transformations that + // were applied during the pass. + virtual void Apply() = 0; + + protected: + opt::IRContext* GetIRContext() const { return ir_context_; } + + TransformationContext* GetTransformationContext() const { + return transformation_context_; + } + + FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; } + + protobufs::TransformationSequence* GetTransformations() const { + return transformations_; + } + + // Returns all instructions that are *available* at |inst_it|, which is + // required to be inside block |block| of function |function| - that is, all + // instructions at global scope and all instructions that strictly dominate + // |inst_it|. + // + // Filters said instructions to return only those that satisfy the + // |instruction_is_relevant| predicate. This, for instance, could ignore all + // instructions that have a particular decoration. + std::vector FindAvailableInstructions( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, + std::function + instruction_is_relevant) const; + + // A helper method that iterates through each instruction in each reachable + // block of |function|, at all times tracking an instruction descriptor that + // allows the latest instruction to be located even if it has no result id. + // + // The code to manipulate the instruction descriptor is a bit fiddly. The + // point of this method is to avoiding having to duplicate it in multiple + // transformation passes. + // + // The function |action| is invoked for each instruction |inst_it| in block + // |block| of function |function| that is encountered. The + // |instruction_descriptor| parameter to the function object allows |inst_it| + // to be identified. + // + // In most intended use cases, the job of |action| is to randomly decide + // whether to try to apply some transformation, and then - if selected - to + // attempt to apply it. + void ForEachInstructionWithInstructionDescriptor( + opt::Function* function, + std::function< + void(opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action); + + // Applies the above overload of ForEachInstructionWithInstructionDescriptor + // to every function in the module, so that |action| is applied to an + // |instruction_descriptor| for every instruction, |inst_it|, of every |block| + // in every |function|. + void ForEachInstructionWithInstructionDescriptor( + std::function< + void(opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action); + + // A generic helper for applying a transformation that should be applicable + // by construction, and adding it to the sequence of applied transformations. + void ApplyTransformation(const Transformation& transformation); + + // A generic helper for applying a transformation only if it is applicable. + // If it is applicable, the transformation is applied and then added to the + // sequence of applied transformations and the function returns true. + // Otherwise, the function returns false. + bool MaybeApplyTransformation(const Transformation& transformation); + + // Returns the id of an OpTypeBool instruction. If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateBoolType(); + + // Returns the id of an OpTypeInt instruction, with width and signedness + // specified by |width| and |is_signed|, respectively. If such an instruction + // does not exist, a transformation is applied to add it. + uint32_t FindOrCreateIntegerType(uint32_t width, bool is_signed); + + // Returns the id of an OpTypeFloat instruction, with width specified by + // |width|. If such an instruction does not exist, a transformation is + // applied to add it. + uint32_t FindOrCreateFloatType(uint32_t width); + + // Returns the id of an OpTypeFunction % %<...argument_id> + // instruction. If such an instruction doesn't exist, a transformation + // is applied to create a new one. + uint32_t FindOrCreateFunctionType(uint32_t return_type_id, + const std::vector& argument_id); + + // Returns the id of an OpTypeVector instruction, with |component_type_id| + // (which must already exist) as its base type, and |component_count| + // elements (which must be in the range [2, 4]). If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count); + + // Returns the id of an OpTypeMatrix instruction, with |column_count| columns + // and |row_count| rows (each of which must be in the range [2, 4]). If the + // float and vector types required to build this matrix type or the matrix + // type itself do not exist, transformations are applied to add them. + uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + + // Returns the id of an OpTypeStruct instruction with |component_type_ids| as + // type ids for struct's components. If no such a struct type exists, + // transformations are applied to add it. |component_type_ids| may not contain + // a result id of an OpTypeFunction. + uint32_t FindOrCreateStructType( + const std::vector& component_type_ids); + + // Returns the id of a pointer type with base type |base_type_id| (which must + // already exist) and storage class |storage_class|. A transformation is + // applied to add the pointer if it does not already exist. + uint32_t FindOrCreatePointerType(uint32_t base_type_id, + spv::StorageClass storage_class); + + // Returns the id of an OpTypePointer instruction, with a integer base + // type of width and signedness specified by |width| and |is_signed|, + // respectively. If the pointer type or required integer base type do not + // exist, transformations are applied to add them. + uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed, + spv::StorageClass storage_class); + + // Returns the id of an OpConstant instruction, with a integer type of + // width and signedness specified by |width| and |is_signed|, respectively, + // with |words| as its value. If either the required integer type or the + // constant do not exist, transformations are applied to add them. + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateIntegerConstant(const std::vector& words, + uint32_t width, bool is_signed, + bool is_irrelevant); + + // Returns the id of an OpConstant instruction, with a floating-point + // type of width specified by |width|, with |words| as its value. If either + // the required floating-point type or the constant do not exist, + // transformations are applied to add them. The returned id either + // participates in IdIsIrrelevant fact or not, depending on the + // |is_irrelevant| parameter. + uint32_t FindOrCreateFloatConstant(const std::vector& words, + uint32_t width, bool is_irrelevant); + + // Returns the id of an OpConstantTrue or OpConstantFalse instruction, + // according to |value|. If either the required instruction or the bool + // type do not exist, transformations are applied to add them. + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant); + + // Returns the id of an OpConstant instruction of type with |type_id| + // that consists of |words|. If that instruction doesn't exist, + // transformations are applied to add it. |type_id| must be a valid + // result id of either scalar or boolean OpType* instruction that exists + // in the module. The returned id either participates in IdIsIrrelevant fact + // or not, depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateConstant(const std::vector& words, + uint32_t type_id, bool is_irrelevant); + + // Returns the id of an OpConstantComposite instruction of type with |type_id| + // that consists of |component_ids|. If that instruction doesn't exist, + // transformations are applied to add it. |type_id| must be a valid + // result id of an OpType* instruction that represents a composite type + // (i.e. a vector, matrix, struct or array). + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant); + + // Returns the result id of an instruction of the form: + // %id = OpUndef %|type_id| + // If no such instruction exists, a transformation is applied to add it. + uint32_t FindOrCreateGlobalUndef(uint32_t type_id); + + // Returns the id of an OpNullConstant instruction of type |type_id|. If + // that instruction doesn't exist, it is added through a transformation. + // |type_id| must be a valid result id of an OpType* instruction that exists + // in the module. + uint32_t FindOrCreateNullConstant(uint32_t type_id); + + // Define a *basic type* to be an integer, boolean or floating-point type, + // or a matrix, vector, struct or fixed-size array built from basic types. In + // particular, a basic type cannot contain an opaque type (such as an image), + // or a runtime-sized array. + // + // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that: + // - basic_type_ids captures every basic type declared in the module. + // - basic_type_ids_to_pointers maps every such basic type to the sequence + // of all pointer types that have storage class |storage_class| and the + // given basic type as their pointee type. The sequence may be empty for + // some basic types if no pointers to those types are defined for the given + // storage class, and the sequence will have multiple elements if there are + // repeated pointer declarations for the same basic type and storage class. + std::pair, std::map>> + GetAvailableBasicTypesAndPointers(spv::StorageClass storage_class) const; + + // Given a type id, |scalar_or_composite_type_id|, which must correspond to + // some scalar or composite type, returns the result id of an instruction + // defining a constant of the given type that is zero or false at everywhere. + // If such an instruction does not yet exist, transformations are applied to + // add it. The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + // + // Examples: + // --------------+------------------------------- + // TYPE | RESULT is id corresponding to + // --------------+------------------------------- + // bool | false + // --------------+------------------------------- + // bvec4 | (false, false, false, false) + // --------------+------------------------------- + // float | 0.0 + // --------------+------------------------------- + // vec2 | (0.0, 0.0) + // --------------+------------------------------- + // int[3] | [0, 0, 0] + // --------------+------------------------------- + // struct S { | + // int i; | S(0, false, (0u, 0u)) + // bool b; | + // uint2 u; | + // } | + // --------------+------------------------------- + uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id, + bool is_irrelevant); + + // Adds a pair (id_use_descriptor, |replacement_id|) to the vector + // |uses_to_replace|, where id_use_descriptor is the id use descriptor + // representing the usage of an id in the |use_inst| instruction, at operand + // index |use_index|, only if the instruction is in a basic block. + // If the instruction is not in a basic block, it does nothing. + void MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace); + + // Returns the preheader of the loop with header |header_id|, which satisfies + // all of the following conditions: + // - It is the only out-of-loop predecessor of the header + // - It unconditionally branches to the header + // - It is not a loop header itself + // If such preheader does not exist, a new one is added and returned. + // Requires |header_id| to be the label id of a loop header block that is + // reachable in the CFG (and thus has at least 2 predecessors). + opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id); + + // Returns the second block in the pair obtained by splitting |block_id| just + // after the last OpPhi or OpVariable instruction in it. Assumes that the + // block is not a loop header. + opt::BasicBlock* SplitBlockAfterOpPhiOrOpVariable(uint32_t block_id); + + // Returns the id of an available local variable (storage class Function) with + // the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. If there is no such variable, it creates one + // in the |function| adding a zero initializer constant that is irrelevant. + // The new variable has the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. The function returns the id of the created + // variable. + uint32_t FindOrCreateLocalVariable(uint32_t pointer_type_id, + uint32_t function_id, + bool pointee_value_is_irrelevant); + + // Returns the id of an available global variable (storage class Private or + // Workgroup) with the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. If there is no such variable, it creates + // one, adding a zero initializer constant that is irrelevant. The new + // variable has the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. The function returns the id of the created + // variable. + uint32_t FindOrCreateGlobalVariable(uint32_t pointer_type_id, + bool pointee_value_is_irrelevant); + + private: + opt::IRContext* ir_context_; + TransformationContext* transformation_context_; + FuzzerContext* fuzzer_context_; + protobufs::TransformationSequence* transformations_; + // If set, then transformations that should be applicable by construction are + // still tested for applicability, and ignored if they turn out to be + // inapplicable. Otherwise, applicability by construction is asserted. + const bool ignore_inapplicable_transformations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp new file mode 100644 index 00000000..85ca57dc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_access_chains.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddAccessChains::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + inst_it->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert an access chain + // instruction before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpAccessChain, inst_it)) { + return; + } + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAccessChain())) { + return; + } + + // Get all of the pointers that are currently in scope, excluding + // explicitly null and undefined pointers. + std::vector relevant_pointer_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + // A pointer needs both a result and type id. + return false; + } + switch (instruction->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + // Do not allow making an access chain from a null or + // undefined pointer. (We can eliminate these cases + // before actually checking that the instruction is a + // pointer.) + return false; + default: + break; + } + // If the instruction has pointer type, we can legitimately + // make an access chain from it. + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == spv::Op::OpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of making an access chain from. + if (relevant_pointer_instructions.empty()) { + return; + } + + auto chosen_pointer = + relevant_pointer_instructions[GetFuzzerContext()->RandomIndex( + relevant_pointer_instructions)]; + std::vector index_ids; + + // Each index accessing a non-struct composite will be clamped, thus + // needing a pair of fresh ids + std::vector> fresh_ids_for_clamping; + + auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef( + chosen_pointer->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + while (true) { + auto subobject_type = + GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id); + if (!spvOpcodeIsComposite(subobject_type->opcode())) { + break; + } + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperWhenMakingAccessChain())) { + break; + } + uint32_t bound; + switch (subobject_type->opcode()) { + case spv::Op::OpTypeArray: + bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext()); + break; + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + bound = subobject_type->GetSingleWordInOperand(1); + break; + case spv::Op::OpTypeStruct: + bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type); + break; + default: + assert(false && "Not a composite type opcode."); + // Set the bound to a value in order to keep release compilers + // happy. + bound = 0; + break; + } + if (bound == 0) { + // It is possible for a composite type to legitimately have zero + // sub-components, at least in the case of a struct, which + // can have no fields. + break; + } + + uint32_t index_value = + GetFuzzerContext()->GetRandomIndexForAccessChain(bound); + + switch (subobject_type->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: { + // The index will be clamped + + bool is_signed = GetFuzzerContext()->ChooseEven(); + + // Make the constant ready for clamping. We need: + // - an OpTypeBool to be present in the module + // - an OpConstant with the same type as the index and value + // the maximum value for an index + // - a new pair of fresh ids for the clamping instructions + FindOrCreateBoolType(); + FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false); + std::pair fresh_pair_of_ids = { + GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId()}; + fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids); + + index_ids.push_back(FindOrCreateIntegerConstant( + {index_value}, 32, is_signed, false)); + subobject_type_id = subobject_type->GetSingleWordInOperand(0); + + } break; + case spv::Op::OpTypeStruct: + index_ids.push_back(FindOrCreateIntegerConstant( + {index_value}, 32, GetFuzzerContext()->ChooseEven(), false)); + subobject_type_id = + subobject_type->GetSingleWordInOperand(index_value); + break; + default: + assert(false && "Not a composite type opcode."); + } + } + // The transformation we are about to create will only apply if a + // pointer suitable for the access chain's result type exists, so we + // create one if it does not. + FindOrCreatePointerType(subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))); + // Apply the transformation to add an access chain. + ApplyTransformation(TransformationAccessChain( + GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(), + index_ids, instruction_descriptor, fresh_ids_for_clamping)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h new file mode 100644 index 00000000..5e209cd2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds access chains based on pointers available in +// the module. Other passes can use these access chains, e.g. by loading from +// them. +class FuzzerPassAddAccessChains : public FuzzerPass { + public: + FuzzerPassAddAccessChains(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp new file mode 100644 index 00000000..1b0d7b10 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddBitInstructionSynonyms::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // This fuzzer pass can add a *lot* of ids. We bail out early if we hit + // the recommended id limit. + if (GetIRContext()->module()->id_bound() >= + GetFuzzerContext()->GetIdBoundLimit()) { + return; + } + + // Randomly decides whether the transformation will be applied. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingBitInstructionSynonym())) { + continue; + } + + // Make sure fuzzer never applies a transformation to a bitwise + // instruction with differently signed operands, only integer operands + // are supported and bitwise operations are supported only. + if (!TransformationAddBitInstructionSynonym::IsInstructionSupported( + GetIRContext(), &instruction)) { + continue; + } + + // Make sure all bit indexes are defined as 32-bit unsigned integers. + uint32_t width = GetIRContext() + ->get_type_mgr() + ->GetType(instruction.type_id()) + ->AsInteger() + ->width(); + for (uint32_t i = 0; i < width; i++) { + FindOrCreateIntegerConstant({i}, 32, false, false); + } + + // Applies the add bit instruction synonym transformation. + ApplyTransformation(TransformationAddBitInstructionSynonym( + instruction.result_id(), + GetFuzzerContext()->GetFreshIds( + TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount( + GetIRContext(), &instruction)))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h new file mode 100644 index 00000000..38d81aa7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass adds synonyms for bit instructions. It iterates over the +// module instructions, checks if they are bit instructions and randomly applies +// the transformation. +class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { + public: + FuzzerPassAddBitInstructionSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp new file mode 100644 index 00000000..c33ae44c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" + +#include "source/fuzz/available_instructions.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddCompositeExtract::Apply() { + std::vector composite_synonyms; + for (const auto* dd : + GetTransformationContext()->GetFactManager()->GetAllSynonyms()) { + // |dd| must describe a component of a composite. + if (!dd->index().empty()) { + composite_synonyms.push_back(dd); + } + } + + AvailableInstructions available_composites( + GetIRContext(), [](opt::IRContext* ir_context, opt::Instruction* inst) { + return inst->type_id() && inst->result_id() && + fuzzerutil::IsCompositeType( + ir_context->get_type_mgr()->GetType(inst->type_id())); + }); + + ForEachInstructionWithInstructionDescriptor( + [this, &available_composites, &composite_synonyms]( + opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeExtract, inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCompositeExtract())) { + return; + } + + std::vector available_synonyms; + for (const auto* dd : composite_synonyms) { + if (fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), &*inst_it, dd->object())) { + available_synonyms.push_back(dd); + } + } + + auto candidate_composites = + available_composites.GetAvailableBeforeInstruction(&*inst_it); + + if (available_synonyms.empty() && candidate_composites.empty()) { + return; + } + + uint32_t composite_id = 0; + std::vector indices; + + if (available_synonyms.empty() || (!candidate_composites.empty() && + GetFuzzerContext()->ChooseEven())) { + const auto* inst = + candidate_composites[GetFuzzerContext()->RandomIndex( + candidate_composites)]; + composite_id = inst->result_id(); + + auto type_id = inst->type_id(); + do { + uint32_t number_of_members = 0; + + const auto* type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(type_id); + assert(type_inst && "Composite instruction has invalid type id"); + + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + number_of_members = + fuzzerutil::GetArraySize(*type_inst, GetIRContext()); + break; + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + number_of_members = type_inst->GetSingleWordInOperand(1); + break; + case spv::Op::OpTypeStruct: + number_of_members = type_inst->NumInOperands(); + break; + default: + assert(false && "|type_inst| is not a composite"); + return; + } + + if (number_of_members == 0) { + return; + } + + indices.push_back( + GetFuzzerContext()->GetRandomCompositeExtractIndex( + number_of_members)); + + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: + type_id = type_inst->GetSingleWordInOperand(indices.back()); + break; + default: + assert(false && "|type_inst| is not a composite"); + return; + } + } while (fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType(type_id)) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperToExtractComposite())); + } else { + const auto* dd = available_synonyms[GetFuzzerContext()->RandomIndex( + available_synonyms)]; + + composite_id = dd->object(); + indices.assign(dd->index().begin(), dd->index().end()); + } + + assert(composite_id != 0 && !indices.empty() && + "Composite object should have been chosen correctly"); + + ApplyTransformation(TransformationCompositeExtract( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + composite_id, indices)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h new file mode 100644 index 00000000..e7ed18af --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCompositeExtract before some instruction +// in the module. +class FuzzerPassAddCompositeExtract : public FuzzerPass { + public: + FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp new file mode 100644 index 00000000..048cdfde --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/transformation_composite_insert.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddCompositeInserts::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + instruction_iterator->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try adding an OpCompositeInsert + // instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) { + return; + } + + // It must be possible to insert an OpCompositeInsert instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeInsert, instruction_iterator)) { + return; + } + + // Look for available values that have composite type. + std::vector available_composites = + FindAvailableInstructions( + function, block, instruction_iterator, + [instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + // |instruction| must be a supported instruction of composite + // type. + if (!TransformationCompositeInsert:: + IsCompositeInstructionSupported(ir_context, + instruction)) { + return false; + } + + auto instruction_type = ir_context->get_type_mgr()->GetType( + instruction->type_id()); + + // No components of the composite can have type + // OpTypeRuntimeArray. + if (ContainsRuntimeArray(*instruction_type)) { + return false; + } + + // No components of the composite can be pointers. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3658): + // Structs can have components of pointer type. + // FindOrCreateZeroConstant cannot be called on a + // pointer. We ignore pointers for now. Consider adding + // support for pointer types. + if (ContainsPointer(*instruction_type)) { + return false; + } + + return true; + }); + + // If there are no available values, then return. + if (available_composites.empty()) { + return; + } + + // Choose randomly one available composite value. + auto available_composite = + available_composites[GetFuzzerContext()->RandomIndex( + available_composites)]; + + // Take a random component of the chosen composite value. If the chosen + // component is itself a composite, then randomly decide whether to take + // its component and repeat. + uint32_t current_node_type_id = available_composite->type_id(); + std::vector path_to_replaced; + while (true) { + auto current_node_type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id); + uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex( + *current_node_type_inst, GetIRContext()); + + // If the composite is empty, then end the iteration. + if (num_of_components == 0) { + break; + } + uint32_t one_selected_index = + GetFuzzerContext()->GetRandomIndexForCompositeInsert( + num_of_components); + + // Construct a final index by appending the current index. + path_to_replaced.push_back(one_selected_index); + current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + GetIRContext(), current_node_type_id, one_selected_index); + + // If the component is not a composite then end the iteration. + if (!fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType( + current_node_type_id))) { + break; + } + + // If the component is a composite, but we decide not to go deeper, + // then end the iteration. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperToInsertInComposite())) { + break; + } + } + + // Look for available objects that have the type id + // |current_node_type_id| and can be inserted. + std::vector available_objects = + FindAvailableInstructions( + function, block, instruction_iterator, + [instruction_descriptor, current_node_type_id]( + opt::IRContext* /*unused*/, + opt::Instruction* instruction) -> bool { + if (instruction->result_id() == 0 || + instruction->type_id() == 0) { + return false; + } + if (instruction->type_id() != current_node_type_id) { + return false; + } + return true; + }); + + // If there are no objects of the specific type available, check if + // FindOrCreateZeroConstant can be called and create a zero constant of + // this type. + uint32_t available_object_id; + if (available_objects.empty()) { + if (!fuzzerutil::CanCreateConstant(GetIRContext(), + current_node_type_id)) { + return; + } + available_object_id = + FindOrCreateZeroConstant(current_node_type_id, false); + } else { + available_object_id = + available_objects[GetFuzzerContext()->RandomIndex( + available_objects)] + ->result_id(); + } + auto new_result_id = GetFuzzerContext()->GetFreshId(); + + // Insert an OpCompositeInsert instruction which copies + // |available_composite| and in the copy inserts the object + // of type |available_object_id| at index |index_to_replace|. + ApplyTransformation(TransformationCompositeInsert( + instruction_descriptor, new_result_id, + available_composite->result_id(), available_object_id, + path_to_replaced)); + }); +} + +bool FuzzerPassAddCompositeInserts::ContainsPointer( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kPointer: + return true; + case opt::analysis::Type::kArray: + return ContainsPointer(*type.AsArray()->element_type()); + case opt::analysis::Type::kMatrix: + return ContainsPointer(*type.AsMatrix()->element_type()); + case opt::analysis::Type::kVector: + return ContainsPointer(*type.AsVector()->element_type()); + case opt::analysis::Type::kStruct: + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsPointer(*element_type); + }); + default: + return false; + } +} + +bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kRuntimeArray: + return true; + case opt::analysis::Type::kStruct: + // If any component of a struct is of type OpTypeRuntimeArray, return + // true. + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsRuntimeArray(*element_type); + }); + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h new file mode 100644 index 00000000..d9f42d58 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds new OpCompositeInsert instructions to +// available values that have the composite type. +class FuzzerPassAddCompositeInserts : public FuzzerPass { + public: + FuzzerPassAddCompositeInserts( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + // Checks if any component of a composite is a pointer. + static bool ContainsPointer(const opt::analysis::Type& type); + + // Checks if any component of a composite has type OpTypeRuntimeArray. + static bool ContainsRuntimeArray(const opt::analysis::Type& type); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp new file mode 100644 index 00000000..bb909913 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_types.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddCompositeTypes::Apply() { + MaybeAddMissingVectorTypes(); + MaybeAddMissingMatrixTypes(); + + // Randomly interleave between adding struct and array composite types + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) { + AddNewStructType(); + } else { + AddNewArrayType(); + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() { + // Functions to lazily supply scalar base types on demand if we decide to + // create vectors with the relevant base types. + std::function bool_type_supplier = [this]() -> uint32_t { + return FindOrCreateBoolType(); + }; + std::function float_type_supplier = [this]() -> uint32_t { + return FindOrCreateFloatType(32); + }; + std::function int_type_supplier = [this]() -> uint32_t { + return FindOrCreateIntegerType(32, true); + }; + std::function uint_type_supplier = [this]() -> uint32_t { + return FindOrCreateIntegerType(32, false); + }; + + // Consider each of the base types with which we can make vectors. + for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier, + int_type_supplier, uint_type_supplier}) { + // Consider each valid vector size. + for (uint32_t size = 2; size <= 4; size++) { + // Randomly decide whether to create (if it does not already exist) a + // vector with this size and base type. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingVectorType())) { + FindOrCreateVectorType(base_type_supplier(), size); + } + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() { + // Consider every valid matrix dimension. + for (uint32_t columns = 2; columns <= 4; columns++) { + for (uint32_t rows = 2; rows <= 4; rows++) { + // Randomly decide whether to create (if it does not already exist) a + // matrix with these dimensions. As matrices can only have floating-point + // base type, we do not need to consider multiple base types as in the + // case for vectors. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingMatrixType())) { + FindOrCreateMatrixType(columns, rows); + } + } + } +} + +void FuzzerPassAddCompositeTypes::AddNewArrayType() { + ApplyTransformation(TransformationAddTypeArray( + GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(), + FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false))); +} + +void FuzzerPassAddCompositeTypes::AddNewStructType() { + std::vector field_type_ids; + do { + field_type_ids.push_back(ChooseScalarOrCompositeType()); + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAnotherStructField())); + ApplyTransformation(TransformationAddTypeStruct( + GetFuzzerContext()->GetFreshId(), field_type_ids)); +} + +uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() { + // Gather up all the possibly-relevant types. + std::vector candidates; + for (auto& inst : GetIRContext()->types_values()) { + switch (inst.opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + candidates.push_back(inst.result_id()); + break; + case spv::Op::OpTypeStruct: { + if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(), + inst.result_id()) && + !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { + candidates.push_back(inst.result_id()); + } + } break; + default: + break; + } + } + assert(!candidates.empty() && + "This function should only be called if there is at least one scalar " + "or composite type available."); + // Return one of these types at random. + return candidates[GetFuzzerContext()->RandomIndex(candidates)]; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h new file mode 100644 index 00000000..f16c79e3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds missing vector and matrix types, and new +// array and struct types, to the module. +class FuzzerPassAddCompositeTypes : public FuzzerPass { + public: + FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Creates an array of a random size with a random existing base type and adds + // it to the module. + void AddNewArrayType(); + + // Creates a struct with fields of random existing types and adds it to the + // module. + void AddNewStructType(); + + // For each vector type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingVectorTypes(); + + // For each matrix type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingMatrixTypes(); + + // Returns the id of a scalar or composite type declared in the module, + // chosen randomly. + uint32_t ChooseScalarOrCompositeType(); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp new file mode 100644 index 00000000..d54d4ad8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_copy_memory.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddCopyMemory::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + // Check that we can insert an OpCopyMemory before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyMemory, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCopyMemory())) { + return; + } + + // Get all instructions available before |inst_it| according to the + // domination rules. + auto instructions = FindAvailableInstructions( + function, block, inst_it, + TransformationAddCopyMemory::IsInstructionSupported); + + if (instructions.empty()) { + return; + } + + const auto* inst = + instructions[GetFuzzerContext()->RandomIndex(instructions)]; + + // Decide whether to create global or local variable. + auto storage_class = GetFuzzerContext()->ChooseEven() + ? spv::StorageClass::Private + : spv::StorageClass::Function; + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), inst->type_id()); + + // Create a pointer type with |storage_class| if needed. + FindOrCreatePointerType(pointee_type_id, storage_class); + + ApplyTransformation(TransformationAddCopyMemory( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + inst->result_id(), storage_class, + FindOrCreateZeroConstant(pointee_type_id, false))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h new file mode 100644 index 00000000..e258752c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCopyMemory before some instruction in the +// module. +class FuzzerPassAddCopyMemory : public FuzzerPass { + public: + FuzzerPassAddCopyMemory(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp new file mode 100644 index 00000000..82d53ebb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_block.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +const size_t kMaxTransformationsInOnePass = 100U; + +} // namespace + +FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddDeadBlocks::Apply() { + // We iterate over all blocks in the module collecting up those at which we + // might add a branch to a new dead block. We then loop over all such + // candidates and actually apply transformations. This separation is to + // avoid modifying the module as we traverse it. + std::vector candidate_transformations; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadBlock())) { + continue; + } + + // Make sure the module contains a boolean constant equal to + // |condition_value|. + bool condition_value = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(condition_value, false); + + // We speculatively create a transformation, and then apply it (below) if + // it turns out to be applicable. This avoids duplicating the logic for + // applicability checking. + // + // It means that fresh ids for transformations that turn out not to be + // applicable end up being unused. + candidate_transformations.emplace_back(TransformationAddDeadBlock( + GetFuzzerContext()->GetFreshId(), block.id(), condition_value)); + } + } + // Applying transformations can be expensive as each transformation requires + // dominator information and also invalidates dominator information. We thus + // limit the number of transformations that one application of this fuzzer + // pass can apply. We choose to do this after identifying all the + // transformations that we *might* want to apply, rather than breaking the + // above loops once the limit is reached, to avoid biasing towards + // transformations that target early parts of the module. + GetFuzzerContext()->Shuffle(&candidate_transformations); + for (size_t i = 0; i < std::min(kMaxTransformationsInOnePass, + candidate_transformations.size()); + i++) { + MaybeApplyTransformation(candidate_transformations[i]); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h new file mode 100644 index 00000000..4567e87d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass to add dynamically unreachable blocks to the module. Future +// passes can then manipulate such blocks. +class FuzzerPassAddDeadBlocks : public FuzzerPass { + public: + FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp new file mode 100644 index 00000000..c3664c87 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_break.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddDeadBreaks::Apply() { + // We first collect up lots of possibly-applicable transformations. + std::vector candidate_transformations; + // We consider each function separately. + for (auto& function : *GetIRContext()->module()) { + // For a given function, we find all the merge blocks in that function. + std::vector merge_blocks; + for (auto& block : function) { + auto maybe_merge_id = block.MergeBlockIdIfAny(); + if (maybe_merge_id) { + auto merge_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id); + + assert(merge_block && "Merge block can't be null"); + + merge_blocks.push_back(merge_block); + } + } + // We rather aggressively consider the possibility of adding a break from + // every block in the function to every merge block. Many of these will be + // inapplicable as they would be illegal. That's OK - we later discard the + // ones that turn out to be no good. + for (auto& block : function) { + for (auto* merge_block : merge_blocks) { + // Populate this vector with ids that are available at the branch point + // of this basic block. We will use these ids to update OpPhi + // instructions later. + std::vector phi_ids; + + // Determine how we need to adjust OpPhi instructions' operands + // for this transformation to be valid. + // + // If |block| has a branch to |merge_block|, the latter must have all of + // its OpPhi instructions set up correctly - we don't need to adjust + // anything. + if (!block.IsSuccessor(merge_block)) { + merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. Use a constant + // if possible, and an undef otherwise. + if (fuzzerutil::CanCreateConstant(GetIRContext(), phi->type_id())) { + // We mark the constant as irrelevant so that we can replace it + // with a more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); + } else { + phi_ids.push_back(FindOrCreateGlobalUndef(phi->type_id())); + } + }); + } + + // Make sure the module has a required boolean constant to be used in + // OpBranchConditional instruction. + auto break_condition = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(break_condition, false); + + auto candidate_transformation = TransformationAddDeadBreak( + block.id(), merge_block->id(), break_condition, std::move(phi_ids)); + if (candidate_transformation.IsApplicable( + GetIRContext(), *GetTransformationContext())) { + // Only consider a transformation as a candidate if it is applicable. + candidate_transformations.push_back( + std::move(candidate_transformation)); + } + } + } + } + + // Go through the candidate transformations that were accumulated, + // probabilistically deciding whether to consider each one further and + // applying the still-applicable ones that are considered further. + // + // We iterate through the candidate transformations in a random order by + // repeatedly removing a random candidate transformation from the sequence + // until no candidate transformations remain. This is done because + // transformations can potentially disable one another, so that iterating + // through them in order would lead to a higher probability of + // transformations appearing early in the sequence being applied compared + // with later transformations. + while (!candidate_transformations.empty()) { + // Choose a random index into the sequence of remaining candidate + // transformations. + auto index = GetFuzzerContext()->RandomIndex(candidate_transformations); + // Remove the transformation at the chosen index from the sequence. + auto transformation = std::move(candidate_transformations[index]); + candidate_transformations.erase(candidate_transformations.begin() + index); + // Probabilistically decide whether to try to apply it vs. ignore it, in the + // case that it is applicable. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadBreak())) { + MaybeApplyTransformation(transformation); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h new file mode 100644 index 00000000..361c3464 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding dead break edges to the module. +class FuzzerPassAddDeadBreaks : public FuzzerPass { + public: + FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp new file mode 100644 index 00000000..38814811 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_continue.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddDeadContinues::Apply() { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Get the label id of the continue target of the innermost loop. + auto continue_block_id = + block.IsLoopHeader() + ? block.ContinueBlockId() + : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock( + block.id()); + + // This transformation is not applicable if current block is not inside a + // loop. + if (continue_block_id == 0) { + continue; + } + + auto* continue_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id); + assert(continue_block && "Continue block is null"); + + // Analyze return type of each OpPhi instruction in the continue target + // and provide an id for the transformation if needed. + std::vector phi_ids; + // Check whether current block has an edge to the continue target. + // If this is the case, we don't need to do anything. + if (!block.IsSuccessor(continue_block)) { + continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. Use a constant + // if possible, and an undef otherwise. + if (fuzzerutil::CanCreateConstant(GetIRContext(), phi->type_id())) { + // We mark the constant as irrelevant so that we can replace it with + // a more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); + } else { + phi_ids.push_back(FindOrCreateGlobalUndef(phi->type_id())); + } + }); + } + + // Make sure the module contains a boolean constant equal to + // |condition_value|. + bool condition_value = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(condition_value, false); + + // Make a transformation to add a dead continue from this node; if the + // node turns out to be inappropriate (e.g. by not being in a loop) the + // precondition for the transformation will fail and it will be ignored. + auto candidate_transformation = TransformationAddDeadContinue( + block.id(), condition_value, std::move(phi_ids)); + // Probabilistically decide whether to apply the transformation in the + // case that it is applicable. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadContinue())) { + MaybeApplyTransformation(candidate_transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h new file mode 100644 index 00000000..4f1bd601 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding dead continue edges to the module. +class FuzzerPassAddDeadContinues : public FuzzerPass { + public: + FuzzerPassAddDeadContinues(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp new file mode 100644 index 00000000..e720c4ec --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -0,0 +1,416 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_equation_instruction.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) { + switch (bit_width) { + case 32: + return true; + case 64: + return ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Float64) && + ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Int64); + case 16: + return ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Float16) && + ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Int16); + default: + return false; + } +} + +} // namespace + +FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddEquationInstructions::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) { + return; + } + + // Check that it is OK to add an equation instruction before the given + // instruction in principle - e.g. check that this does not lead to + // inserting before an OpVariable or OpPhi instruction. We use OpIAdd + // as an example opcode for this check, to be representative of *some* + // opcode that defines an equation, even though we may choose a + // different opcode below. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd, + inst_it)) { + return; + } + + // Get all available instructions with result ids and types that are not + // OpUndef. + std::vector available_instructions = + FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* /*unused*/, + opt::Instruction* instruction) -> bool { + return instruction->result_id() && instruction->type_id() && + instruction->opcode() != spv::Op::OpUndef && + !GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()); + }); + + // Try the opcodes for which we know how to make ids at random until + // something works. + std::vector candidate_opcodes = { + spv::Op::OpIAdd, spv::Op::OpISub, spv::Op::OpLogicalNot, + spv::Op::OpSNegate, spv::Op::OpConvertUToF, spv::Op::OpConvertSToF, + spv::Op::OpBitcast}; + do { + auto opcode = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes); + switch (opcode) { + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: { + std::vector candidate_instructions; + for (const auto* inst : + GetIntegerInstructions(available_instructions)) { + const auto* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "|inst| has invalid type"); + + if (const auto* vector_type = type->AsVector()) { + type = vector_type->element_type(); + } + + if (IsBitWidthSupported(GetIRContext(), + type->AsInteger()->width())) { + candidate_instructions.push_back(inst); + } + } + + if (candidate_instructions.empty()) { + break; + } + + const auto* operand = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + + const auto* type = + GetIRContext()->get_type_mgr()->GetType(operand->type_id()); + assert(type && "Operand has invalid type"); + + // Make sure a result type exists in the module. + if (const auto* vector = type->AsVector()) { + // We store element count in a separate variable since the + // call FindOrCreate* functions below might invalidate + // |vector| pointer. + const auto element_count = vector->element_count(); + + FindOrCreateVectorType( + FindOrCreateFloatType( + vector->element_type()->AsInteger()->width()), + element_count); + } else { + FindOrCreateFloatType(type->AsInteger()->width()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand->result_id()}, instruction_descriptor)); + return; + } + case spv::Op::OpBitcast: { + const auto candidate_instructions = + GetNumericalInstructions(available_instructions); + + if (!candidate_instructions.empty()) { + const auto* operand_inst = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + const auto* operand_type = + GetIRContext()->get_type_mgr()->GetType( + operand_inst->type_id()); + assert(operand_type && "Operand instruction has invalid type"); + + // Make sure a result type exists in the module. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters + // is that they must have the same number of bits. Consider + // improving the code below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + // We store element count in a separate variable since the + // call FindOrCreate* functions below might invalidate + // |vector| pointer. + const auto element_count = vector->element_count(); + + uint32_t element_type_id; + if (const auto* int_type = + vector->element_type()->AsInteger()) { + element_type_id = FindOrCreateFloatType(int_type->width()); + } else { + assert(vector->element_type()->AsFloat() && + "Vector must have numerical elements"); + element_type_id = FindOrCreateIntegerType( + vector->element_type()->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + FindOrCreateVectorType(element_type_id, element_count); + } else if (const auto* int_type = operand_type->AsInteger()) { + FindOrCreateFloatType(int_type->width()); + } else { + assert(operand_type->AsFloat() && + "Operand is not a scalar of numerical type"); + FindOrCreateIntegerType(operand_type->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand_inst->result_id()}, instruction_descriptor)); + return; + } + } break; + case spv::Op::OpIAdd: + case spv::Op::OpISub: { + // Instructions of integer (scalar or vector) result type are + // suitable for these opcodes. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + // There is at least one such instruction, so pick one at random + // for the LHS of an equation. + auto lhs = integer_instructions.at( + GetFuzzerContext()->RandomIndex(integer_instructions)); + + // For the RHS, we can use any instruction with an integer + // scalar/vector result type of the same number of components + // and the same bit-width for the underlying integer type. + + // Work out the element count and bit-width. + auto lhs_type = + GetIRContext()->get_type_mgr()->GetType(lhs->type_id()); + uint32_t lhs_element_count; + uint32_t lhs_bit_width; + if (lhs_type->AsVector()) { + lhs_element_count = lhs_type->AsVector()->element_count(); + lhs_bit_width = lhs_type->AsVector() + ->element_type() + ->AsInteger() + ->width(); + } else { + lhs_element_count = 1; + lhs_bit_width = lhs_type->AsInteger()->width(); + } + + // Get all the instructions that match on element count and + // bit-width. + auto candidate_rhs_instructions = RestrictToElementBitWidth( + RestrictToVectorWidth(integer_instructions, + lhs_element_count), + lhs_bit_width); + + // Choose a RHS instruction at random; there is guaranteed to + // be at least one choice as the LHS will be available. + auto rhs = candidate_rhs_instructions.at( + GetFuzzerContext()->RandomIndex( + candidate_rhs_instructions)); + + // Add the equation instruction. + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {lhs->result_id(), rhs->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case spv::Op::OpLogicalNot: { + // Choose any available instruction of boolean scalar/vector + // result type and equate its negation with a fresh id. + auto boolean_instructions = + GetBooleanInstructions(available_instructions); + if (!boolean_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {boolean_instructions + .at(GetFuzzerContext()->RandomIndex( + boolean_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case spv::Op::OpSNegate: { + // Similar to OpLogicalNot, but for signed integer negation. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {integer_instructions + .at(GetFuzzerContext()->RandomIndex( + integer_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + default: + assert(false && "Unexpected opcode."); + break; + } + } while (!candidate_opcodes.empty()); + // Reaching here means that we did not manage to apply any + // transformation at this point of the module. + }); +} + +std::vector +FuzzerPassAddEquationInstructions::GetIntegerInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsInteger() || + (type->AsVector() && type->AsVector()->element_type()->AsInteger())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetFloatInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsFloat() || + (type->AsVector() && type->AsVector()->element_type()->AsFloat())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetBooleanInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsBool() || + (type->AsVector() && type->AsVector()->element_type()->AsBool())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is + // otherwise derived from its vector type. + uint32_t other_vector_width = + type->AsVector() ? type->AsVector()->element_count() : 1; + // Keep |inst| if the vector widths match. + if (vector_width == other_vector_width) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const { + std::vector result; + for (auto& inst : instructions) { + const opt::analysis::Type* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsVector()) { + type = type->AsVector()->element_type(); + } + assert((type->AsInteger() || type->AsFloat()) && + "Precondition: all input instructions must " + "have integer or float scalar or vector type."); + if ((type->AsInteger() && type->AsInteger()->width() == bit_width) || + (type->AsFloat() && type->AsFloat()->width() == bit_width)) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetNumericalInstructions( + const std::vector& instructions) const { + std::vector result; + + for (auto* inst : instructions) { + const auto* type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction has invalid type"); + + if (const auto* vector_type = type->AsVector()) { + type = vector_type->element_type(); + } + + if (!type->AsInteger() && !type->AsFloat()) { + // Only numerical scalars or vectors of numerical components are + // supported. + continue; + } + + if (!IsBitWidthSupported(GetIRContext(), type->AsInteger() + ? type->AsInteger()->width() + : type->AsFloat()->width())) { + continue; + } + + result.push_back(inst); + } + + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h new file mode 100644 index 00000000..dc9a27b3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that sprinkles instructions through the module that define +// equations using various arithmetic and logical operators. +class FuzzerPassAddEquationInstructions : public FuzzerPass { + public: + FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Yields those instructions in |instructions| that have integer scalar or + // vector result type. + std::vector GetIntegerInstructions( + const std::vector& instructions) const; + + // Returns only instructions, that have either a scalar floating-point or a + // vector type. + std::vector GetFloatInstructions( + const std::vector& instructions) const; + + // Yields those instructions in |instructions| that have boolean scalar or + // vector result type. + std::vector GetBooleanInstructions( + const std::vector& instructions) const; + + // Yields those instructions in |instructions| that have a scalar numerical or + // a vector of numerical components type. Only 16, 32 and 64-bit numericals + // are supported if both OpTypeInt and OpTypeFloat instructions can be created + // with the specified width (e.g. for 16-bit types both Float16 and Int16 + // capabilities must be present). + std::vector GetNumericalInstructions( + const std::vector& instructions) const; + + // Requires that |instructions| are scalars or vectors of some type. Returns + // only those instructions whose width is |width|. If |width| is 1 this means + // the scalars. + std::vector RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const; + + // Requires that |instructions| are integer or float scalars or vectors. + // Returns only those instructions for which the bit-width of the underlying + // integer or floating-point type is |bit_width|. + std::vector RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp new file mode 100644 index 00000000..70b86573 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -0,0 +1,202 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_function_calls.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_function_call.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddFunctionCalls::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + // Check whether it is legitimate to insert a function call before the + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpFunctionCall, inst_it)) { + return; + } + + // Randomly decide whether to try inserting a function call here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCallingFunction())) { + return; + } + + // Compute the module's call graph - we don't cache it since it may + // change each time we apply a transformation. If this proves to be + // a bottleneck the call graph data structure could be made updatable. + CallGraph call_graph(GetIRContext()); + + // Gather all the non-entry point functions different from this + // function. It is important to ignore entry points as a function + // cannot be an entry point and the target of an OpFunctionCall + // instruction. We ignore this function to avoid direct recursion. + std::vector candidate_functions; + for (auto& other_function : *GetIRContext()->module()) { + if (&other_function != function && + !fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + other_function.result_id())) { + candidate_functions.push_back(&other_function); + } + } + + // Choose a function to call, at random, by considering candidate + // functions until a suitable one is found. + opt::Function* chosen_function = nullptr; + while (!candidate_functions.empty()) { + opt::Function* candidate_function = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions); + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id()) && + !GetTransformationContext()->GetFactManager()->FunctionIsLivesafe( + candidate_function->result_id())) { + // Unless in a dead block, only livesafe functions can be invoked + continue; + } + if (call_graph.GetIndirectCallees(candidate_function->result_id()) + .count(function->result_id())) { + // Calling this function could lead to indirect recursion + continue; + } + chosen_function = candidate_function; + break; + } + + if (!chosen_function) { + // No suitable function was found to call. (This can happen, for + // instance, if the current function is the only function in the + // module.) + return; + } + + ApplyTransformation(TransformationFunctionCall( + GetFuzzerContext()->GetFreshId(), chosen_function->result_id(), + ChooseFunctionCallArguments(*chosen_function, function, block, + inst_it), + instruction_descriptor)); + }); +} + +std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it) { + auto available_pointers = FindAvailableInstructions( + caller_function, caller_block, caller_inst_it, + [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) { + if (inst->opcode() != spv::Op::OpVariable || + inst->opcode() != spv::Op::OpFunctionParameter) { + // Function parameters and variables are the only + // kinds of pointer that can be used as actual + // parameters. + return false; + } + + return GetTransformationContext()->GetFactManager()->BlockIsDead( + caller_block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant(inst->result_id()); + }); + + std::unordered_map> type_id_to_result_id; + for (const auto* inst : available_pointers) { + type_id_to_result_id[inst->type_id()].push_back(inst->result_id()); + } + + std::vector result; + for (const auto* param : + fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) { + const auto* param_type = + GetIRContext()->get_type_mgr()->GetType(param->type_id()); + assert(param_type && "Parameter has invalid type"); + + if (!param_type->AsPointer()) { + if (fuzzerutil::CanCreateConstant(GetIRContext(), param->type_id())) { + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + result.push_back(FindOrCreateZeroConstant(param->type_id(), true)); + } else { + result.push_back(FindOrCreateGlobalUndef(param->type_id())); + } + continue; + } + + if (type_id_to_result_id.count(param->type_id())) { + // Use an existing pointer if there are any. + const auto& candidates = type_id_to_result_id[param->type_id()]; + result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]); + continue; + } + + // Make a new variable, at function or global scope depending on the storage + // class of the pointer. + + // Get a fresh id for the new variable. + uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId(); + + // The id of this variable is what we pass as the parameter to + // the call. + result.push_back(fresh_variable_id); + type_id_to_result_id[param->type_id()].push_back(fresh_variable_id); + + // Now bring the variable into existence. + auto storage_class = param_type->AsPointer()->storage_class(); + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), param->type_id()); + if (storage_class == spv::StorageClass::Function) { + // Add a new zero-initialized local variable to the current + // function, noting that its pointee value is irrelevant. + ApplyTransformation(TransformationAddLocalVariable( + fresh_variable_id, param->type_id(), caller_function->result_id(), + FindOrCreateZeroConstant(pointee_type_id, false), true)); + } else { + assert((storage_class == spv::StorageClass::Private || + storage_class == spv::StorageClass::Workgroup) && + "Only Function, Private and Workgroup storage classes are " + "supported at present."); + // Add a new global variable to the module, zero-initializing it if + // it has Private storage class, and noting that its pointee value is + // irrelevant. + ApplyTransformation(TransformationAddGlobalVariable( + fresh_variable_id, param->type_id(), storage_class, + storage_class == spv::StorageClass::Private + ? FindOrCreateZeroConstant(pointee_type_id, false) + : 0, + true)); + } + } + + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h new file mode 100644 index 00000000..80b03d7b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds calls at random to (a) livesafe functions, from +// anywhere, and (b) any functions, from dead blocks. +class FuzzerPassAddFunctionCalls : public FuzzerPass { + public: + FuzzerPassAddFunctionCalls(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Randomly chooses suitable arguments to invoke |callee| right before + // instruction |caller_inst_it| of block |caller_block| in |caller_function|, + // based on both existing available instructions and the addition of new + // instructions to the module. + std::vector ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp new file mode 100644 index 00000000..41068565 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_global_variables.h" + +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddGlobalVariables::Apply() { + spv::StorageClass variable_storage_class = spv::StorageClass::Private; + for (auto& entry_point : GetIRContext()->module()->entry_points()) { + // If the execution model of some entry point is GLCompute, + // then the variable storage class may be Workgroup. + if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) == + spv::ExecutionModel::GLCompute) { + variable_storage_class = + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass()) + ? spv::StorageClass::Workgroup + : spv::StorageClass::Private; + break; + } + } + + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(variable_storage_class); + + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // There are no basic types, so there is nothing this fuzzer pass can do. + return; + } + + // These are the pointers to those basic types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; + + // Probabilistically keep adding global variables. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) { + // Choose a random basic type; the new variable's type will be a pointer to + // this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + uint32_t pointer_type_id; + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the basic type so that future variables can potentially + // use it. + pointer_type_id = GetFuzzerContext()->GetFreshId(); + available_pointers_to_basic_type.push_back(pointer_type_id); + ApplyTransformation(TransformationAddTypePointer( + pointer_type_id, variable_storage_class, basic_type)); + } else { + // There is - grab one. + pointer_type_id = + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; + } + + ApplyTransformation(TransformationAddGlobalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type_id, + variable_storage_class, + variable_storage_class == spv::StorageClass::Private + ? FindOrCreateZeroConstant(basic_type, false) + : 0, + true)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h new file mode 100644 index 00000000..1496646e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds global variables, with Private storage class, +// to the module. +class FuzzerPassAddGlobalVariables : public FuzzerPass { + public: + FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp new file mode 100644 index 00000000..19661d10 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -0,0 +1,198 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" +#include "source/fuzz/transformation_composite_construct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddImageSampleUnusedComponents:: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddImageSampleUnusedComponents::Apply() { + // SPIR-V module to help understand the transformation. + // + // OpCapability Shader + // %1 = OpExtInstImport "GLSL.std.450" + // OpMemoryModel Logical GLSL450 + // OpEntryPoint Fragment %15 "main" %12 %14 + // OpExecutionMode %15 OriginUpperLeft + // + // ; Decorations + // OpDecorate %12 Location 0 ; Input color variable location + // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable + // descriptor set OpDecorate %13 Binding 0 ; Image coordinate + // variable binding OpDecorate %14 Location 0 ; Fragment color + // variable location + // + // ; Types + // %2 = OpTypeVoid + // %3 = OpTypeFunction %2 + // %4 = OpTypeFloat 32 + // %5 = OpTypeVector %4 2 + // %6 = OpTypeVector %4 4 + // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + // %8 = OpTypeSampledImage %7 + // %9 = OpTypePointer Input %5 + // %10 = OpTypePointer UniformConstant %8 + // %11 = OpTypePointer Output %6 + // + // ; Variables + // %12 = OpVariable %9 Input ; Input image coordinate variable + // %13 = OpVariable %10 UniformConstant ; Image variable + // %14 = OpVariable %11 Output ; Fragment color variable + // + // ; main function + // %15 = OpFunction %2 None %3 + // %16 = OpLabel + // %17 = OpLoad %5 %12 + // %18 = OpLoad %8 %13 + // %19 = OpImageSampleImplicitLod %6 %18 %17 + // OpStore %14 %19 + // OpReturn + // OpFunctionEnd + + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17 + if (!spvOpcodeIsImageSample(instruction->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingImageSampleUnusedComponents())) { + return; + } + + // Gets image sample coordinate information. + // |coordinate_instruction| %17 = OpLoad %5 %12 + uint32_t coordinate_id = instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the coordinate is a 4-dimensional vector, then no unused components + // may be added. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return; + } + + // If the coordinate is a scalar, then at most 3 unused components may be + // added. If the coordinate is a vector, then the maximum number of unused + // components depends on the vector size. + // For the sample module, the coordinate type instruction is %5 = + // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2. + uint32_t max_unused_component_count = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? 3 + : 4 - coordinate_type->AsVector()->element_count(); + + // |unused_component_count| may be 1 or 2. + uint32_t unused_component_count = + GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample( + max_unused_component_count); + + // Gets a type for the zero-unused components. + uint32_t zero_constant_type_id; + switch (unused_component_count) { + case 1: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the vector components + // type. + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? coordinate_instruction->type_id() + : GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()); + break; + case 2: + case 3: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the coordinate + // components type. + // |zero_constant_type_id| %5 = OpTypeVector %4 2 + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + unused_component_count); + break; + default: + assert(false && "Should be unreachable."); + zero_constant_type_id = 0; + break; + } + + // Gets |coordinate_type| again because the module may have changed due to + // the use of FindOrCreateVectorType above. + coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the new vector type with unused components does not exist, then create + // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4 + uint32_t coordinate_with_unused_components_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + 1 + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + coordinate_type->AsVector()->element_count() + + unused_component_count); + + // Inserts an OpCompositeConstruct instruction which + // represents the coordinate with unused components. + // |coordinate_with_unused_components_id| + // %22 = OpCompositeConstruct %6 %17 %21 + uint32_t coordinate_with_unused_components_id = + GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + coordinate_with_unused_components_type_id, + {coordinate_instruction->result_id(), + // FindOrCreateZeroConstant + // %20 = OpConstant %4 0 + // %21 = OpConstantComposite %5 %20 %20 + FindOrCreateZeroConstant(zero_constant_type_id, true)}, + MakeInstructionDescriptor(GetIRContext(), instruction), + coordinate_with_unused_components_id)); + + // Tries to add unused components to the image sample coordinate. + // %19 = OpImageSampleImplicitLod %6 %18 %22 + ApplyTransformation(TransformationAddImageSampleUnusedComponents( + coordinate_with_unused_components_id, + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h new file mode 100644 index 00000000..1a278931 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for image sample instructions in the module and +// randomly applies the transformation to add unused components to the image +// sample coordinate. +class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { + public: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp new file mode 100644 index 00000000..36603284 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLoads::FuzzerPassAddLoads( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddLoads::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + inst_it->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLoad())) { + return; + } + + // Check whether it is legitimate to insert a load or atomic load before + // this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, + inst_it)) { + return; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpAtomicLoad, + inst_it)) { + return; + } + + std::vector relevant_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + switch (instruction->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + // Do not allow loading from a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == spv::Op::OpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of loading from. + if (relevant_instructions.empty()) { + return; + } + + auto chosen_instruction = + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)]; + + bool is_atomic_load = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = static_cast( + GetIRContext() + ->get_def_use_mgr() + ->GetDef(chosen_instruction->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::Workgroup: + case spv::StorageClass::CrossWorkgroup: + case spv::StorageClass::AtomicCounter: + case spv::StorageClass::Image: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicLoad())) { + is_atomic_load = true; + + memory_scope_id = FindOrCreateConstant( + {uint32_t(spv::Scope::Invocation)}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), chosen_instruction->result_id(), + is_atomic_load, memory_scope_id, memory_semantics_id, + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h new file mode 100644 index 00000000..14460e7f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, from pointers in the module. +class FuzzerPassAddLoads : public FuzzerPass { + public: + FuzzerPassAddLoads(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp new file mode 100644 index 00000000..f467f46b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_local_variables.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddLocalVariables::Apply() { + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(spv::StorageClass::Function); + + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; + if (basic_types.empty()) { + // The pass cannot do anything if there are no basic types. + return; + } + + // These are the pointers to those basic types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; + + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Probabilistically keep adding random variables to this function. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLocalVariable())) { + // Choose a random basic type; the new variable's type will be a pointer + // to this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + uint32_t pointer_type; + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the basic type so that future variables can potentially + // use it. + pointer_type = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + pointer_type, spv::StorageClass::Function, basic_type)); + available_pointers_to_basic_type.push_back(pointer_type); + } else { + // There is - grab one. + pointer_type = + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; + } + ApplyTransformation(TransformationAddLocalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), + FindOrCreateZeroConstant(basic_type, false), true)); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h new file mode 100644 index 00000000..c969ba06 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds local variables, with Function storage class, +// to the module. +class FuzzerPassAddLocalVariables : public FuzzerPass { + public: + FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp new file mode 100644 index 00000000..e8c9e964 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_loop_preheader.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddLoopPreheaders::Apply() { + for (auto& function : *GetIRContext()->module()) { + // Keep track of all the loop headers we want to add a preheader to. + std::vector loop_header_ids_to_consider; + for (auto& block : function) { + // We only care about loop headers. + if (!block.IsLoopHeader()) { + continue; + } + + // Randomly decide whether to consider this header. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLoopPreheader())) { + continue; + } + + // We exclude loop headers with just one predecessor (the back-edge block) + // because they are unreachable. + if (GetIRContext()->cfg()->preds(block.id()).size() < 2) { + continue; + } + + loop_header_ids_to_consider.push_back(block.id()); + } + + for (uint32_t header_id : loop_header_ids_to_consider) { + // If not already present, add a preheader which is not also a loop + // header. + GetOrCreateSimpleLoopPreheader(header_id); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h new file mode 100644 index 00000000..2a13dbac --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly adds simple loop preheaders to loops that do not +// have one. A simple loop preheader is a block that: +// - is the only out-of-loop predecessor of the header +// - branches unconditionally to the header +// - is not a loop header itself +class FuzzerPassAddLoopPreheaders : public FuzzerPass { + public: + FuzzerPassAddLoopPreheaders( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp new file mode 100644 index 00000000..1a65ef61 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -0,0 +1,256 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" + +namespace spvtools { +namespace fuzz { +namespace { +uint32_t kMaxNestingDepth = 4; +} // namespace + +FuzzerPassAddLoopsToCreateIntConstantSynonyms:: + FuzzerPassAddLoopsToCreateIntConstantSynonyms( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { + std::vector constants; + + // Choose the constants for which to create synonyms. + for (auto constant_def : GetIRContext()->GetConstants()) { + // Randomly decide whether to consider this constant. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCreatingIntSynonymsUsingLoops())) { + continue; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + constant_def->result_id()); + + // We do not consider irrelevant constants + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_def->result_id())) { + continue; + } + + // We only consider integer constants (scalar or vector). + if (!constant->AsIntConstant() && + !(constant->AsVectorConstant() && + constant->AsVectorConstant()->component_type()->AsInteger())) { + continue; + } + + constants.push_back(constant_def->result_id()); + } + + std::vector blocks; + + // Get a list of all the blocks before which we can add a loop creating a new + // synonym. We cannot apply the transformation while iterating over the + // module, because we are going to add new blocks. + for (auto& function : *GetIRContext()->module()) { + // Consider all non-dead blocks reachable from the first block of the + // function. + GetIRContext()->cfg()->ForEachBlockInPostOrder( + &*function.begin(), [this, &blocks](opt::BasicBlock* block) { + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + blocks.push_back(block->id()); + } + }); + } + + // Make sure that the module has an OpTypeBool instruction, and 32-bit signed + // integer constants 0 and 1, adding them if necessary. + FindOrCreateBoolType(); + FindOrCreateIntegerConstant({0}, 32, true, false); + FindOrCreateIntegerConstant({1}, 32, true, false); + + // Compute the call graph. We can use this for any further computation, since + // we are not adding or removing functions or function calls. + auto call_graph = CallGraph(GetIRContext()); + + // Consider each constant and each block. + for (uint32_t constant_id : constants) { + // Choose one of the blocks. + uint32_t block_id = blocks[GetFuzzerContext()->RandomIndex(blocks)]; + + // Adjust the block so that the transformation can be applied. + auto block = GetIRContext()->get_instr_block(block_id); + + // If the block is a loop header, add a simple preheader. We can do this + // because we have excluded all the non-reachable headers. + if (block->IsLoopHeader()) { + block = GetOrCreateSimpleLoopPreheader(block->id()); + block_id = block->id(); + } + + assert(!block->IsLoopHeader() && + "The block cannot be a loop header at this point."); + + // If the block is a merge block, a continue block or it does not have + // exactly 1 predecessor, split it after any OpPhi or OpVariable + // instructions. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id()) || + GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + block->id()) || + GetIRContext()->cfg()->preds(block->id()).size() != 1) { + block = SplitBlockAfterOpPhiOrOpVariable(block->id()); + block_id = block->id(); + } + + // Randomly decide the values for the number of iterations and the step + // value, and compute the initial value accordingly. + + // The maximum number of iterations depends on the maximum possible loop + // nesting depth of the block, computed interprocedurally, i.e. also + // considering the possibility that the enclosing function is called inside + // a loop. It is: + // - 1 if the nesting depth is >= kMaxNestingDepth + // - 2^(kMaxNestingDepth - nesting_depth) otherwise + uint32_t max_nesting_depth = + call_graph.GetMaxCallNestingDepth(block->GetParent()->result_id()) + + GetIRContext()->GetStructuredCFGAnalysis()->LoopNestingDepth( + block->id()); + uint32_t num_iterations = + max_nesting_depth >= kMaxNestingDepth + ? 1 + : GetFuzzerContext()->GetRandomNumberOfLoopIterations( + 1u << (kMaxNestingDepth - max_nesting_depth)); + + // Find or create the corresponding constant containing the number of + // iterations. + uint32_t num_iterations_id = + FindOrCreateIntegerConstant({num_iterations}, 32, true, false); + + // Find the other constants. + // We use 64-bit values and then use the bits that we need. We find the + // step value (S) randomly and then compute the initial value (I) using + // the equation I = C + S*N. + uint32_t initial_value_id = 0; + uint32_t step_value_id = 0; + + // Get the content of the existing constant. + const auto constant = + GetIRContext()->get_constant_mgr()->FindDeclaredConstant(constant_id); + const auto constant_type_id = + GetIRContext()->get_def_use_mgr()->GetDef(constant_id)->type_id(); + + if (constant->AsIntConstant()) { + // The constant is a scalar integer. + + std::tie(initial_value_id, step_value_id) = + FindSuitableStepAndInitialValueConstants( + constant->GetZeroExtendedValue(), + constant->type()->AsInteger()->width(), + constant->type()->AsInteger()->IsSigned(), num_iterations); + } else { + // The constant is a vector of integers. + assert(constant->AsVectorConstant() && + constant->AsVectorConstant()->component_type()->AsInteger() && + "If the program got here, the constant should be a vector of " + "integers."); + + // Find a constant for each component of the initial value and the step + // values. + std::vector initial_value_component_ids; + std::vector step_value_component_ids; + + // Get the value, width and signedness of the components. + std::vector component_values; + for (auto component : constant->AsVectorConstant()->GetComponents()) { + component_values.push_back(component->GetZeroExtendedValue()); + } + uint32_t bit_width = + constant->AsVectorConstant()->component_type()->AsInteger()->width(); + uint32_t is_signed = constant->AsVectorConstant() + ->component_type() + ->AsInteger() + ->IsSigned(); + + for (uint64_t component_val : component_values) { + uint32_t initial_val_id; + uint32_t step_val_id; + std::tie(initial_val_id, step_val_id) = + FindSuitableStepAndInitialValueConstants(component_val, bit_width, + is_signed, num_iterations); + initial_value_component_ids.push_back(initial_val_id); + step_value_component_ids.push_back(step_val_id); + } + + // Find or create the vector constants. + initial_value_id = FindOrCreateCompositeConstant( + initial_value_component_ids, constant_type_id, false); + step_value_id = FindOrCreateCompositeConstant(step_value_component_ids, + constant_type_id, false); + } + + assert(initial_value_id && step_value_id && + "|initial_value_id| and |step_value_id| should have been defined."); + + // Randomly decide whether to have two blocks (or just one) in the new + // loop. + uint32_t additional_block_id = + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym()) + ? GetFuzzerContext()->GetFreshId() + : 0; + + // Add the loop and create the synonym. + ApplyTransformation(TransformationAddLoopToCreateIntConstantSynonym( + constant_id, initial_value_id, step_value_id, num_iterations_id, + block_id, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + additional_block_id)); + } +} + +std::pair FuzzerPassAddLoopsToCreateIntConstantSynonyms:: + FindSuitableStepAndInitialValueConstants(uint64_t constant_val, + uint32_t bit_width, bool is_signed, + uint32_t num_iterations) { + // Choose the step value randomly and compute the initial value accordingly. + // The result of |initial_value| could overflow, but this is OK, since + // the transformation takes overflows into consideration (the equation still + // holds as long as the last |bit_width| bits of C and of (I-S*N) match). + uint64_t step_value = + GetFuzzerContext()->GetRandomValueForStepConstantInLoop(); + uint64_t initial_value = constant_val + step_value * num_iterations; + + uint32_t initial_val_id = FindOrCreateIntegerConstant( + fuzzerutil::IntToWords(initial_value, bit_width, is_signed), bit_width, + is_signed, false); + + uint32_t step_val_id = FindOrCreateIntegerConstant( + fuzzerutil::IntToWords(step_value, bit_width, is_signed), bit_width, + is_signed, false); + + return {initial_val_id, step_val_id}; +} + +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h new file mode 100644 index 00000000..14e17542 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that adds synonyms for integer, scalar or vector, constants, by +// adding loops that compute the same value by subtracting a value S from an +// initial value I, and for N times, so that C = I - S*N. +class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { + public: + FuzzerPassAddLoopsToCreateIntConstantSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Returns a pair (initial_val_id, step_val_id) such that both ids are + // integer scalar constants of the same type as the scalar integer constant + // identified by the given |constant_val|, |bit_width| and signedness, and + // such that, if I is the value of initial_val_id, S is the value of + // step_val_id and C is the value of the constant, the equation (C = I - S * + // num_iterations) holds, (only considering the last |bit_width| bits of each + // constant). + std::pair FindSuitableStepAndInitialValueConstants( + uint64_t constant_val, uint32_t bit_width, bool is_signed, + uint32_t num_iterations); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp new file mode 100644 index 00000000..8ae6e90a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddNoContractionDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Restrict attention to arithmetic instructions (as defined in the + // SPIR-V specification). + if (TransformationAddNoContractionDecoration::IsArithmetic( + inst.opcode())) { + // Randomly choose whether to apply the NoContraction decoration to + // this arithmetic instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingNoContractionDecoration())) { + TransformationAddNoContractionDecoration transformation( + inst.result_id()); + ApplyTransformation(transformation); + } + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h new file mode 100644 index 00000000..7aeb26de --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the NoContraction decoration to arithmetic instructions. +class FuzzerPassAddNoContractionDecorations : public FuzzerPass { + public: + FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp new file mode 100644 index 00000000..d0b1275b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -0,0 +1,311 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_opphi_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddOpPhiSynonyms::Apply() { + // Get a list of synonymous ids with the same type that can be used in the + // same OpPhi instruction. + auto equivalence_classes = GetIdEquivalenceClasses(); + + // Make a list of references, to avoid copying sets unnecessarily. + std::vector*> equivalence_class_pointers; + for (auto& set : equivalence_classes) { + equivalence_class_pointers.push_back(&set); + } + + // Keep a list of transformations to apply at the end. + std::vector transformations_to_apply; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Randomly decide whether to consider this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingOpPhiSynonym())) { + continue; + } + + // The block must not be dead. + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block.id())) { + continue; + } + + // The block must have at least one predecessor. + size_t num_preds = GetIRContext()->cfg()->preds(block.id()).size(); + if (num_preds == 0) { + continue; + } + + std::set* chosen_equivalence_class = nullptr; + + if (num_preds > 1) { + // If the block has more than one predecessor, prioritise sets with at + // least 2 ids available at some predecessor. + chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly( + equivalence_class_pointers, block.id(), 2); + } + + // If a set was not already chosen, choose one with at least one available + // id. + if (!chosen_equivalence_class) { + chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly( + equivalence_class_pointers, block.id(), 1); + } + + // If no suitable set was found, we cannot apply the transformation to + // this block. + if (!chosen_equivalence_class) { + continue; + } + + // Initialise the map from predecessor labels to ids. + std::map preds_to_ids; + + // Keep track of the ids used and of the id of a predecessor with at least + // two ids to choose from. This is to ensure that, if possible, at least + // two distinct ids will be used. + std::set ids_chosen; + uint32_t pred_with_alternatives = 0; + + // Choose an id for each predecessor. + for (uint32_t pred_id : GetIRContext()->cfg()->preds(block.id())) { + auto suitable_ids = GetSuitableIds(*chosen_equivalence_class, pred_id); + assert(!suitable_ids.empty() && + "We must be able to find at least one suitable id because the " + "equivalence class was chosen among suitable ones."); + + // If this predecessor has more than one id to choose from and it is the + // first one of this kind that we found, remember its id. + if (suitable_ids.size() > 1 && !pred_with_alternatives) { + pred_with_alternatives = pred_id; + } + + uint32_t chosen_id = + suitable_ids[GetFuzzerContext()->RandomIndex(suitable_ids)]; + + // Add this id to the set of ids chosen. + ids_chosen.emplace(chosen_id); + + // Add the pair (predecessor, chosen id) to the map. + preds_to_ids[pred_id] = chosen_id; + } + + // If: + // - the block has more than one predecessor + // - at least one predecessor has more than one alternative + // - the same id has been chosen by all the predecessors + // then choose another one for the predecessor with more than one + // alternative. + if (num_preds > 1 && pred_with_alternatives != 0 && + ids_chosen.size() == 1) { + auto suitable_ids = + GetSuitableIds(*chosen_equivalence_class, pred_with_alternatives); + uint32_t chosen_id = + GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids); + if (chosen_id == preds_to_ids[pred_with_alternatives]) { + chosen_id = GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids); + } + + preds_to_ids[pred_with_alternatives] = chosen_id; + } + + // Add the transformation to the list of transformations to apply. + transformations_to_apply.emplace_back(block.id(), preds_to_ids, + GetFuzzerContext()->GetFreshId()); + } + } + + // Apply the transformations. + for (const auto& transformation : transformations_to_apply) { + ApplyTransformation(transformation); + } +} + +std::vector> +FuzzerPassAddOpPhiSynonyms::GetIdEquivalenceClasses() { + std::vector> id_equivalence_classes; + + // Keep track of all the ids that have already be assigned to a class. + std::set already_in_a_class; + + for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) { + // Exclude ids that have already been assigned to a class. + if (already_in_a_class.count(pair.first)) { + continue; + } + + // Exclude irrelevant ids. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + pair.first)) { + continue; + } + + // Exclude ids having a type that is not allowed by the transformation. + if (!TransformationAddOpPhiSynonym::CheckTypeIsAllowed( + GetIRContext(), pair.second->type_id())) { + continue; + } + + // Exclude OpFunction and OpUndef instructions, because: + // - OpFunction does not yield a value; + // - OpUndef yields an undefined value at each use, so it should never be a + // synonym of another id. + if (pair.second->opcode() == spv::Op::OpFunction || + pair.second->opcode() == spv::Op::OpUndef) { + continue; + } + + // We need a new equivalence class for this id. + std::set new_equivalence_class; + + // Add this id to the class. + new_equivalence_class.emplace(pair.first); + already_in_a_class.emplace(pair.first); + + // Add all the synonyms with the same type to this class. + for (auto synonym : + GetTransformationContext()->GetFactManager()->GetSynonymsForId( + pair.first)) { + // The synonym must be a plain id - it cannot be an indexed access into a + // composite. + if (synonym->index_size() > 0) { + continue; + } + + // The synonym must not be irrelevant. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + synonym->object())) { + continue; + } + + auto synonym_def = + GetIRContext()->get_def_use_mgr()->GetDef(synonym->object()); + // The synonym must exist and have the same type as the id we are + // considering. + if (!synonym_def || synonym_def->type_id() != pair.second->type_id()) { + continue; + } + + // We can add this synonym to the new equivalence class. + new_equivalence_class.emplace(synonym->object()); + already_in_a_class.emplace(synonym->object()); + } + + // Add the new equivalence class to the list of equivalence classes. + id_equivalence_classes.emplace_back(std::move(new_equivalence_class)); + } + + return id_equivalence_classes; +} + +bool FuzzerPassAddOpPhiSynonyms::EquivalenceClassIsSuitableForBlock( + const std::set& equivalence_class, uint32_t block_id, + uint32_t distinct_ids_required) { + bool at_least_one_id_for_each_pred = true; + + // Keep a set of the suitable ids found. + std::set suitable_ids_found; + + // Loop through all the predecessors of the block. + for (auto pred_id : GetIRContext()->cfg()->preds(block_id)) { + // Find the last instruction in the predecessor block. + auto last_instruction = + GetIRContext()->get_instr_block(pred_id)->terminator(); + + // Initially assume that there is not a suitable id for this predecessor. + bool at_least_one_suitable_id_found = false; + for (uint32_t id : equivalence_class) { + if (fuzzerutil::IdIsAvailableBeforeInstruction(GetIRContext(), + last_instruction, id)) { + // We have found a suitable id. + at_least_one_suitable_id_found = true; + suitable_ids_found.emplace(id); + + // If we have already found enough distinct suitable ids, we don't need + // to check the remaining ones for this predecessor. + if (suitable_ids_found.size() >= distinct_ids_required) { + break; + } + } + } + // If no suitable id was found for this predecessor, this equivalence class + // is not suitable and we don't need to check the other predecessors. + if (!at_least_one_suitable_id_found) { + at_least_one_id_for_each_pred = false; + break; + } + } + + // The equivalence class is suitable if at least one suitable id was found for + // each predecessor and we have found at least |distinct_ids_required| + // distinct suitable ids in general. + return at_least_one_id_for_each_pred && + suitable_ids_found.size() >= distinct_ids_required; +} + +std::vector FuzzerPassAddOpPhiSynonyms::GetSuitableIds( + const std::set& ids, uint32_t pred_id) { + // Initialise an empty vector of suitable ids. + std::vector suitable_ids; + + // Get the predecessor block. + auto predecessor = fuzzerutil::MaybeFindBlock(GetIRContext(), pred_id); + + // Loop through the ids to find the suitable ones. + for (uint32_t id : ids) { + if (fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), predecessor->terminator(), id)) { + suitable_ids.push_back(id); + } + } + + return suitable_ids; +} + +std::set* +FuzzerPassAddOpPhiSynonyms::MaybeFindSuitableEquivalenceClassRandomly( + const std::vector*>& candidates, uint32_t block_id, + uint32_t distinct_ids_required) { + auto remaining_candidates = candidates; + while (!remaining_candidates.empty()) { + // Choose one set randomly and return it if it is suitable. + auto chosen = + GetFuzzerContext()->RemoveAtRandomIndex(&remaining_candidates); + if (EquivalenceClassIsSuitableForBlock(*chosen, block_id, + distinct_ids_required)) { + return chosen; + } + } + + // No suitable sets were found. + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h new file mode 100644 index 00000000..c45c9a8a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -0,0 +1,72 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to add OpPhi instructions which can take the values of ids that +// have been marked as synonymous. This instruction will itself be marked as +// synonymous with the others. +class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { + public: + FuzzerPassAddOpPhiSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + // Computes the equivalence classes for the non-pointer and non-irrelevant ids + // in the module, where two ids are considered equivalent iff they have been + // declared synonymous and they have the same type. + std::vector> GetIdEquivalenceClasses(); + + // Returns true iff |equivalence_class| contains at least + // |distinct_ids_required| ids so that all of these ids are available at the + // end of at least one predecessor of the block with label |block_id|. + // Assumes that the block has at least one predecessor. + bool EquivalenceClassIsSuitableForBlock( + const std::set& equivalence_class, uint32_t block_id, + uint32_t distinct_ids_required); + + // Returns a vector with the ids that are available to use at the end of the + // block with id |pred_id|, selected among the given |ids|. Assumes that + // |pred_id| is the label of a block and all ids in |ids| exist in the module. + std::vector GetSuitableIds(const std::set& ids, + uint32_t pred_id); + + private: + // Randomly chooses one of the equivalence classes in |candidates|, so that it + // satisfies all of the following conditions: + // - For each of the predecessors of the |block_id| block, there is at least + // one id in the chosen equivalence class that is available at the end of + // it. + // - There are at least |distinct_ids_required| ids available at the end of + // some predecessor. + // Returns nullptr if no equivalence class in |candidates| satisfies the + // requirements. + std::set* MaybeFindSuitableEquivalenceClassRandomly( + const std::vector*>& candidates, uint32_t block_id, + uint32_t distinct_ids_required); +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp new file mode 100644 index 00000000..e663d89b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_parameters.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_parameter.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddParameters::FuzzerPassAddParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddParameters::Apply() { + // Compute type candidates for the new parameter. + std::vector type_candidates; + for (const auto& type_inst : GetIRContext()->module()->GetTypes()) { + if (TransformationAddParameter::IsParameterTypeSupported( + GetIRContext(), type_inst->result_id())) { + type_candidates.push_back(type_inst->result_id()); + } + } + + if (type_candidates.empty()) { + // The module contains no suitable types to use in new parameters. + return; + } + + // Iterate over all functions in the module. + for (const auto& function : *GetIRContext()->module()) { + // Skip all entry-point functions - we don't want to change those. + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + function.result_id())) { + continue; + } + + if (GetNumberOfParameters(function) >= + GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingParameters())) { + continue; + } + + auto num_new_parameters = + GetFuzzerContext()->GetRandomNumberOfNewParameters( + GetNumberOfParameters(function)); + + for (uint32_t i = 0; i < num_new_parameters; ++i) { + auto current_type_id = + type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)]; + auto current_type = + GetIRContext()->get_type_mgr()->GetType(current_type_id); + std::map call_parameter_ids; + + // Consider the case when a pointer type was selected. + if (current_type->kind() == opt::analysis::Type::kPointer) { + auto storage_class = fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), current_type_id); + switch (storage_class) { + case spv::StorageClass::Function: { + // In every caller find or create a local variable that has the + // selected type. + for (auto* instr : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + auto block = GetIRContext()->get_instr_block(instr); + auto function_id = block->GetParent()->result_id(); + uint32_t variable_id = + FindOrCreateLocalVariable(current_type_id, function_id, true); + call_parameter_ids[instr->result_id()] = variable_id; + } + } break; + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: { + // If there exists at least one caller, find or create a global + // variable that has the selected type. + std::vector callers = + fuzzerutil::GetCallers(GetIRContext(), function.result_id()); + if (!callers.empty()) { + uint32_t variable_id = + FindOrCreateGlobalVariable(current_type_id, true); + for (auto* instr : callers) { + call_parameter_ids[instr->result_id()] = variable_id; + } + } + } break; + default: + break; + } + } else { + // If there exists at least one caller, find or create a zero constant + // that has the selected type. + std::vector callers = + fuzzerutil::GetCallers(GetIRContext(), function.result_id()); + if (!callers.empty()) { + uint32_t constant_id = + FindOrCreateZeroConstant(current_type_id, true); + for (auto* instr : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + call_parameter_ids[instr->result_id()] = constant_id; + } + } + } + + ApplyTransformation(TransformationAddParameter( + function.result_id(), GetFuzzerContext()->GetFreshId(), + current_type_id, std::move(call_parameter_ids), + GetFuzzerContext()->GetFreshId())); + } + } +} + +uint32_t FuzzerPassAddParameters::GetNumberOfParameters( + const opt::Function& function) const { + const auto* type = GetIRContext()->get_type_mgr()->GetType( + function.DefInst().GetSingleWordInOperand(1)); + assert(type && type->AsFunction()); + + return static_cast(type->AsFunction()->param_types().size()); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h new file mode 100644 index 00000000..c79f0e09 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides for each non-entry-point function in the module whether to +// add new parameters to it. If so, randomly determines the number of parameters +// to add, their type and creates constants used to initialize them. +class FuzzerPassAddParameters : public FuzzerPass { + public: + FuzzerPassAddParameters(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Returns number of parameters of |function|. + uint32_t GetNumberOfParameters(const opt::Function& function) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp new file mode 100644 index 00000000..b8c2b559 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddRelaxedDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Randomly choose whether to apply the RelaxedPrecision decoration + // to this instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) { + TransformationAddRelaxedDecoration transformation(inst.result_id()); + // Restrict attention to numeric instructions (returning 32-bit + // floats or ints according to SPIR-V documentation) in dead blocks. + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } + } + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h new file mode 100644 index 00000000..51eb5942 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the Relaxed decoration to numeric instructions. +class FuzzerPassAddRelaxedDecorations : public FuzzerPass { + public: + FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp new file mode 100644 index 00000000..0de02a5b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddStores::FuzzerPassAddStores( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddStores::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + inst_it->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + + // Check whether it is legitimate to insert a store before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore, + inst_it)) { + return; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpAtomicStore, inst_it)) { + return; + } + + // Look for pointers we might consider storing to. + std::vector relevant_pointers = + FindAvailableInstructions( + function, block, inst_it, + [this, block](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + auto type_inst = context->get_def_use_mgr()->GetDef( + instruction->type_id()); + if (type_inst->opcode() != spv::Op::OpTypePointer) { + // Not a pointer. + return false; + } + if (instruction->IsReadOnlyPointer()) { + // Read only: cannot store to it. + return false; + } + switch (instruction->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + // Do not allow storing to a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return GetTransformationContext() + ->GetFactManager() + ->BlockIsDead(block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant( + instruction->result_id()); + }); + + // At this point, |relevant_pointers| contains all the pointers we might + // think of storing to. + if (relevant_pointers.empty()) { + return; + } + + auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex( + relevant_pointers)]; + + std::vector relevant_values = + FindAvailableInstructions( + function, block, inst_it, + [pointer](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + return instruction->type_id() == + context->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(1); + }); + + if (relevant_values.empty()) { + return; + } + + bool is_atomic_store = false; + uint32_t memory_scope_id = 0; + uint32_t memory_semantics_id = 0; + + auto storage_class = + static_cast(GetIRContext() + ->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(0)); + + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::Workgroup: + case spv::StorageClass::CrossWorkgroup: + case spv::StorageClass::AtomicCounter: + case spv::StorageClass::Image: + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAtomicStore())) { + is_atomic_store = true; + + memory_scope_id = FindOrCreateConstant( + {uint32_t(spv::Scope::Invocation)}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + + memory_semantics_id = FindOrCreateConstant( + {static_cast( + fuzzerutil::GetMemorySemanticsForStorageClass( + storage_class))}, + FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()), + false); + } + break; + + default: + break; + } + + // Create and apply the transformation. + ApplyTransformation(TransformationStore( + pointer->result_id(), is_atomic_store, memory_scope_id, + memory_semantics_id, + relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] + ->result_id(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h new file mode 100644 index 00000000..b18dde1f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, through pointers in the module, +// either (a) from dead blocks, or (b) through pointers whose pointee values +// are known not to affect the module's overall behaviour. +class FuzzerPassAddStores : public FuzzerPass { + public: + FuzzerPassAddStores(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp new file mode 100644 index 00000000..5782732c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_synonyms.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddSynonyms::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + // Don't create synonyms in dead blocks. + return; + } + + // Skip |inst_it| if we can't insert anything above it. OpIAdd is just + // a representative of some instruction that might be produced by the + // transformation. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingSynonyms())) { + return; + } + + auto synonym_type = GetFuzzerContext()->GetRandomSynonymType(); + + // Select all instructions that can be used to create a synonym to. + auto available_instructions = FindAvailableInstructions( + function, block, inst_it, + [synonym_type, this](opt::IRContext* ir_context, + opt::Instruction* inst) { + // Check that we can create a synonym to |inst| as described by + // the |synonym_type| and insert it before |inst_it|. + return TransformationAddSynonym::IsInstructionValid( + ir_context, *GetTransformationContext(), inst, synonym_type); + }); + + if (available_instructions.empty()) { + return; + } + + const auto* existing_synonym = + available_instructions[GetFuzzerContext()->RandomIndex( + available_instructions)]; + + // Make sure the module contains all instructions required to apply the + // transformation. + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: + // Create a zero constant to be used as an operand of the synonymous + // instruction. + FindOrCreateZeroConstant(existing_synonym->type_id(), false); + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + const auto* existing_synonym_type = + GetIRContext()->get_type_mgr()->GetType( + existing_synonym->type_id()); + assert(existing_synonym_type && "Instruction has invalid type"); + + if (const auto* vector = existing_synonym_type->AsVector()) { + auto element_type_id = + GetIRContext()->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = vector->element_type()->AsFloat() + ? fuzzerutil::FloatToWord(1) + : 1u; + FindOrCreateCompositeConstant( + std::vector( + vector->element_count(), + FindOrCreateConstant({one_word}, element_type_id, false)), + existing_synonym->type_id(), false); + } else { + FindOrCreateConstant( + {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) + : 1u}, + existing_synonym->type_id(), false); + } + } break; + default: + // This assertion will fail if some SynonymType is missing from the + // switch statement. + assert( + !TransformationAddSynonym::IsAdditionalConstantRequired( + synonym_type) && + "|synonym_type| requires an additional constant to be present " + "in the module"); + break; + } + + ApplyTransformation(TransformationAddSynonym( + existing_synonym->result_id(), synonym_type, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h new file mode 100644 index 00000000..b0c174bc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Sprinkles instructions through the module that produce ids, synonymous to +// some other instructions. +class FuzzerPassAddSynonyms : public FuzzerPass { + public: + FuzzerPassAddSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp new file mode 100644 index 00000000..4cddf55e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_vector_shuffle.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAddVectorShuffleInstructions::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + instruction_iterator->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try adding an OpVectorShuffle instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) { + return; + } + + // It must be valid to insert an OpVectorShuffle instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpVectorShuffle, instruction_iterator)) { + return; + } + + // Looks for vectors that we might consider to use as OpVectorShuffle + // operands. + std::vector vector_instructions = + FindAvailableInstructions( + function, block, instruction_iterator, + [this, instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + + if (!ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsVector()) { + return false; + } + + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + *instruction)) { + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + return false; + } + + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, + FindInstruction(instruction_descriptor, ir_context), + instruction->result_id()); + }); + + // If there are no vector instructions, then return. + if (vector_instructions.empty()) { + return; + } + + auto vector_1_instruction = + vector_instructions[GetFuzzerContext()->RandomIndex( + vector_instructions)]; + auto vector_1_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector(); + + auto vector_2_instruction = + GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions); + auto vector_2_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector(); + + // |vector_1| and |vector_2| must have the same element type as each + // other. The loop is guaranteed to terminate because each iteration + // removes on possible choice for |vector_2|, and there is at least one + // choice that will cause the loop to exit - namely |vector_1|. + while (vector_1_type->element_type() != vector_2_type->element_type()) { + vector_2_instruction = + GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions); + vector_2_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector(); + } + + // Gets components and creates the appropriate result vector type. + std::vector components = + GetFuzzerContext()->GetRandomComponentsForVectorShuffle( + vector_1_type->element_count() + + vector_2_type->element_count()); + FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId( + vector_1_type->element_type()), + static_cast(components.size())); + + // Applies the vector shuffle transformation. + ApplyTransformation(TransformationVectorShuffle( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + vector_1_instruction->result_id(), + vector_2_instruction->result_id(), components)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h new file mode 100644 index 00000000..64d6608a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Adds OpVectorShuffle instructions to the module. +class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { + public: + FuzzerPassAddVectorShuffleInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp new file mode 100644 index 00000000..6bf46e1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_adjust_branch_weights.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAdjustBranchWeights::Apply() { + // For all OpBranchConditional instructions, + // randomly applies the transformation. + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + if (instruction->opcode() == spv::Op::OpBranchConditional && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingBranchWeights())) { + ApplyTransformation(TransformationAdjustBranchWeights( + MakeInstructionDescriptor(GetIRContext(), instruction), + GetFuzzerContext()->GetRandomBranchWeights())); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h new file mode 100644 index 00000000..ae1ea345 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for branch conditional instructions +// and randomly chooses which of these instructions will have their weights +// adjusted. +class FuzzerPassAdjustBranchWeights : public FuzzerPass { + public: + FuzzerPassAdjustBranchWeights( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp new file mode 100644 index 00000000..363edc70 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_function_controls.h" + +#include "source/fuzz/transformation_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAdjustFunctionControls::Apply() { + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Randomly decide whether to adjust this function's controls. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) { + // Grab the function control mask for the function in its present form. + uint32_t existing_function_control_mask = + function.DefInst().GetSingleWordInOperand(0); + + // For the new mask, we first randomly select one of three basic masks: + // None, Inline or DontInline. These are always valid (and are mutually + // exclusive). + std::vector basic_function_control_masks = { + spv::FunctionControlMask::MaskNone, spv::FunctionControlMask::Inline, + spv::FunctionControlMask::DontInline}; + uint32_t new_function_control_mask = + uint32_t(basic_function_control_masks[GetFuzzerContext()->RandomIndex( + basic_function_control_masks)]); + + // We now consider the Pure and Const mask bits. If these are already + // set on the function then it's OK to keep them, but also interesting + // to consider dropping them, so we decide randomly in each case. + for (auto mask_bit : + {spv::FunctionControlMask::Pure, spv::FunctionControlMask::Const}) { + if ((existing_function_control_mask & uint32_t(mask_bit)) && + GetFuzzerContext()->ChooseEven()) { + new_function_control_mask |= uint32_t(mask_bit); + } + } + + // Create and add a transformation. + TransformationSetFunctionControl transformation( + function.DefInst().result_id(), new_function_control_mask); + ApplyTransformation(transformation); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h new file mode 100644 index 00000000..7a8c4927 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the function controls on OpFunction instructions. +class FuzzerPassAdjustFunctionControls : public FuzzerPass { + public: + FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp new file mode 100644 index 00000000..53dbe540 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h" + +#include "source/fuzz/transformation_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAdjustLoopControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a loop merge. + if (merge_inst->opcode() != spv::Op::OpLoopMerge) { + continue; + } + + // Decide randomly whether to adjust this loop merge. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) { + continue; + } + + uint32_t existing_mask = merge_inst->GetSingleWordOperand( + TransformationSetLoopControl::kLoopControlMaskInOperandIndex); + + // First, set the new mask to one of None, Unroll or DontUnroll. + std::vector basic_masks = { + uint32_t(spv::LoopControlMask::MaskNone), + uint32_t(spv::LoopControlMask::Unroll), + uint32_t(spv::LoopControlMask::DontUnroll)}; + uint32_t new_mask = + basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)]; + + // For the loop controls that depend on guarantees about what the loop + // does, check which of these were present in the existing mask and + // randomly decide whether to keep them. They are just hints, so + // removing them should not change the semantics of the module. + for (auto mask_bit : {spv::LoopControlMask::DependencyInfinite, + spv::LoopControlMask::DependencyLength, + spv::LoopControlMask::MinIterations, + spv::LoopControlMask::MaxIterations, + spv::LoopControlMask::IterationMultiple}) { + if ((existing_mask & uint32_t(mask_bit)) && + GetFuzzerContext()->ChooseEven()) { + // The mask bits we are considering are not available in all SPIR-V + // versions. However, we only include a mask bit if it was present + // in the original loop control mask, and we work under the + // assumption that we are transforming a valid module, thus we don't + // need to actually check whether the SPIR-V version being used + // supports these loop control mask bits. + new_mask |= uint32_t(mask_bit); + } + } + + // We use 0 for peel count and partial count in the case that we choose + // not to set these controls. + uint32_t peel_count = 0; + uint32_t partial_count = 0; + + // PeelCount and PartialCount are not compatible with DontUnroll, so + // we check whether DontUnroll is set. + if (!(new_mask & uint32_t(spv::LoopControlMask::DontUnroll))) { + // If PeelCount is supported by this SPIR-V version, randomly choose + // whether to set it. If it was set in the original mask and is not + // selected for setting here, that amounts to dropping it. + if (TransformationSetLoopControl::PeelCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= uint32_t(spv::LoopControlMask::PeelCount); + // The peel count is chosen randomly - if PeelCount was already set + // this will overwrite whatever peel count was previously used. + peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount(); + } + // Similar, but for PartialCount. + if (TransformationSetLoopControl::PartialCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= uint32_t(spv::LoopControlMask::PartialCount); + partial_count = + GetFuzzerContext()->GetRandomLoopControlPartialCount(); + } + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetLoopControl transformation(block.id(), new_mask, + peel_count, partial_count); + ApplyTransformation(transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h new file mode 100644 index 00000000..25e83047 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the loop controls on OpLoopMerge instructions. +class FuzzerPassAdjustLoopControls : public FuzzerPass { + public: + FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp new file mode 100644 index 00000000..efae7d66 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h" + +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_set_memory_operands_mask.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAdjustMemoryOperandsMasks::Apply() { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Consider every instruction in this block, using an explicit iterator so + // that when we find an instruction of interest we can search backwards to + // create an id descriptor for it. + for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) { + if (!TransformationSetMemoryOperandsMask::IsMemoryAccess(*inst_it)) { + // We are only interested in memory access instructions. + continue; + } + + std::vector indices_of_available_masks_to_adjust; + // All memory instructions have at least one memory operands mask. + indices_of_available_masks_to_adjust.push_back(0); + // From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a + // second mask. + switch (inst_it->opcode()) { + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + if (TransformationSetMemoryOperandsMask:: + MultipleMemoryOperandMasksAreSupported(GetIRContext())) { + indices_of_available_masks_to_adjust.push_back(1); + } + break; + default: + break; + } + + // Consider the available masks + for (auto mask_index : indices_of_available_masks_to_adjust) { + // Randomly decide whether to adjust this mask. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAdjustingMemoryOperandsMask())) { + continue; + } + // Get the existing mask, using None if there was no mask present at + // all. + auto existing_mask_in_operand_index = + TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( + *inst_it, mask_index); + auto existing_mask = + existing_mask_in_operand_index < inst_it->NumInOperands() + ? inst_it->GetSingleWordInOperand( + existing_mask_in_operand_index) + : static_cast(spv::MemoryAccessMask::MaskNone); + + // There are two things we can do to a mask: + // - add Volatile if not already present + // - toggle Nontemporal + // The following ensures that we do at least one of these + bool add_volatile = + !(existing_mask & uint32_t(spv::MemoryAccessMask::Volatile)) && + GetFuzzerContext()->ChooseEven(); + bool toggle_nontemporal = + !add_volatile || GetFuzzerContext()->ChooseEven(); + + // These bitwise operations use '|' to add Volatile if desired, and + // '^' to toggle Nontemporal if desired. + uint32_t new_mask = + (existing_mask | + (add_volatile ? uint32_t(spv::MemoryAccessMask::Volatile) + : uint32_t(spv::MemoryAccessMask::MaskNone))) ^ + (toggle_nontemporal ? uint32_t(spv::MemoryAccessMask::Nontemporal) + : uint32_t(spv::MemoryAccessMask::MaskNone)); + + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(block, inst_it), new_mask, mask_index); + ApplyTransformation(transformation); + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h new file mode 100644 index 00000000..3197f91b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to adjust the memory operand masks in memory access +// instructions. +class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { + public: + FuzzerPassAdjustMemoryOperandsMasks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp new file mode 100644 index 00000000..fe0cf7af --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h" + +#include "source/fuzz/transformation_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassAdjustSelectionControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a selection merge. + if (merge_inst->opcode() != spv::Op::OpSelectionMerge) { + continue; + } + + // Choose randomly whether to change the selection control for this + // instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { + continue; + } + + // The choices to change the selection control to are the set of valid + // controls, minus the current control. + std::vector choices; + for (auto control : {spv::SelectionControlMask::MaskNone, + spv::SelectionControlMask::Flatten, + spv::SelectionControlMask::DontFlatten}) { + if (control == + spv::SelectionControlMask(merge_inst->GetSingleWordOperand(1))) { + continue; + } + choices.push_back(uint32_t(control)); + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetSelectionControl transformation( + block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]); + ApplyTransformation(transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h new file mode 100644 index 00000000..ac55de79 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the selection controls on OpSelectionMerge instructions. +class FuzzerPassAdjustSelectionControls : public FuzzerPass { + public: + FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp new file mode 100644 index 00000000..0367a26b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -0,0 +1,206 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassApplyIdSynonyms::Apply() { + // Compute a closure of data synonym facts, to enrich the pool of synonyms + // that are available. + ApplyTransformation(TransformationComputeDataSynonymFactClosure( + GetFuzzerContext() + ->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure())); + + for (auto id_with_known_synonyms : GetTransformationContext() + ->GetFactManager() + ->GetIdsForWhichSynonymsAreKnown()) { + // Gather up all uses of |id_with_known_synonym| as a regular id, and + // subsequently iterate over these uses. We use this separation because, + // when considering a given use, we might apply a transformation that will + // invalidate the def-use manager. + std::vector> uses; + GetIRContext()->get_def_use_mgr()->ForEachUse( + id_with_known_synonyms, + [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void { + // We only gather up regular id uses; e.g. we do not include a use of + // the id as the scope for an atomic operation. + if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) { + uses.emplace_back( + std::pair(use_inst, use_index)); + } + }); + + for (const auto& use : uses) { + auto use_inst = use.first; + auto use_index = use.second; + auto block_containing_use = GetIRContext()->get_instr_block(use_inst); + // The use might not be in a block; e.g. it could be a decoration. + if (!block_containing_use) { + continue; + } + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) { + continue; + } + // |use_index| is the absolute index of the operand. We require + // the index of the operand restricted to input operands only. + uint32_t use_in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(), + *GetTransformationContext(), use_inst, + use_in_operand_index)) { + continue; + } + + std::vector synonyms_to_try; + for (const auto* data_descriptor : + GetTransformationContext()->GetFactManager()->GetSynonymsForId( + id_with_known_synonyms)) { + protobufs::DataDescriptor descriptor_for_this_id = + MakeDataDescriptor(id_with_known_synonyms, {}); + if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) { + // Exclude the fact that the id is synonymous with itself. + continue; + } + + if (DataDescriptorsHaveCompatibleTypes( + use_inst->opcode(), use_in_operand_index, + descriptor_for_this_id, *data_descriptor)) { + synonyms_to_try.push_back(data_descriptor); + } + } + while (!synonyms_to_try.empty()) { + auto synonym_to_try = + GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try); + + // If the synonym's |index_size| is zero, the synonym represents an id. + // Otherwise it represents some element of a composite structure, in + // which case we need to be able to add an extract instruction to get + // that element out. + if (synonym_to_try->index_size() > 0 && + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeExtract, use_inst) && + use_inst->opcode() != spv::Op::OpPhi) { + // We cannot insert an extract before this instruction, so this + // synonym is no good. + continue; + } + + if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst, + use_in_operand_index, + synonym_to_try->object())) { + continue; + } + + // We either replace the use with an id known to be synonymous (when + // the synonym's |index_size| is 0), or an id that will hold the result + // of extracting a synonym from a composite (when the synonym's + // |index_size| is > 0). + uint32_t id_with_which_to_replace_use; + if (synonym_to_try->index_size() == 0) { + id_with_which_to_replace_use = synonym_to_try->object(); + } else { + id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId(); + opt::Instruction* instruction_to_insert_before = nullptr; + + if (use_inst->opcode() != spv::Op::OpPhi) { + instruction_to_insert_before = use_inst; + } else { + auto parent_block_id = + use_inst->GetSingleWordInOperand(use_in_operand_index + 1); + auto parent_block_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id); + auto parent_block = + GetIRContext()->get_instr_block(parent_block_instruction); + + instruction_to_insert_before = parent_block->GetMergeInst() + ? parent_block->GetMergeInst() + : parent_block->terminator(); + } + + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + GetIRContext() + ->get_instr_block(instruction_to_insert_before) + ->id())) { + // We cannot create a synonym via a composite extraction in a dead + // block, as the resulting id is irrelevant. + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + synonym_to_try->object()) && + "Irrelevant ids can't participate in DataSynonym facts"); + ApplyTransformation(TransformationCompositeExtract( + MakeInstructionDescriptor(GetIRContext(), + instruction_to_insert_before), + id_with_which_to_replace_use, synonym_to_try->object(), + fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()))); + assert(GetTransformationContext()->GetFactManager()->IsSynonymous( + MakeDataDescriptor(id_with_which_to_replace_use, {}), + *synonym_to_try) && + "The extracted id must be synonymous with the component from " + "which it was extracted."); + } + + ApplyTransformation(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, + use_in_operand_index), + id_with_which_to_replace_use)); + break; + } + } + } +} + +bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes( + spv::Op opcode, uint32_t use_in_operand_index, + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + auto base_object_type_id_1 = + fuzzerutil::GetTypeId(GetIRContext(), dd1.object()); + auto base_object_type_id_2 = + fuzzerutil::GetTypeId(GetIRContext(), dd2.object()); + assert(base_object_type_id_1 && base_object_type_id_2 && + "Data descriptors are invalid"); + + auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), base_object_type_id_1, dd1.index()); + auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), base_object_type_id_2, dd2.index()); + assert(type_id_1 && type_id_2 && "Data descriptors have invalid types"); + + return fuzzerutil::TypesAreCompatible( + GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h new file mode 100644 index 00000000..d1a0e1aa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A pass that replaces ids with other ids, or accesses into structures, that +// are known to hold the same values. +class FuzzerPassApplyIdSynonyms : public FuzzerPass { + public: + FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Returns true if uses of |dd1| can be replaced with |dd2| and vice-versa + // with respect to the type. Concretely, returns true if |dd1| and |dd2| have + // the same type or both |dd1| and |dd2| are either a numerical or a vector + // type of integral components with possibly different signedness. + bool DataDescriptorsHaveCompatibleTypes(spv::Op opcode, + uint32_t use_in_operand_index, + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp new file mode 100644 index 00000000..0ad630c1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -0,0 +1,351 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_construct_composites.h" + +#include + +#include "source/fuzz/available_instructions.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassConstructComposites::FuzzerPassConstructComposites( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassConstructComposites::Apply() { + // Gather up the ids of all composite types, but skip block-/buffer + // block-decorated struct types. + std::vector composite_type_ids; + for (auto& inst : GetIRContext()->types_values()) { + if (fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType(inst.result_id())) && + !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { + composite_type_ids.push_back(inst.result_id()); + } + } + + if (composite_type_ids.empty()) { + // There are no composite types, so this fuzzer pass cannot do anything. + return; + } + + AvailableInstructions available_composite_constituents( + GetIRContext(), + [this](opt::IRContext* ir_context, opt::Instruction* inst) -> bool { + if (!inst->result_id() || !inst->type_id()) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be able + // to produce a synonym out of the id. + return GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + inst->result_id()) || + fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), *inst); + }); + + ForEachInstructionWithInstructionDescriptor( + [this, &available_composite_constituents, &composite_type_ids]( + opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + // Randomly decide whether to try inserting a composite construction + // here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfConstructingComposite())) { + return; + } + + // Check whether it is legitimate to insert a composite construction + // before the instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeConstruct, inst_it)) { + return; + } + + // For each instruction that is available at this program point (i.e. an + // instruction that is global or whose definition strictly dominates the + // program point) and suitable for making a synonym of, associate it + // with the id of its result type. + TypeIdToInstructions type_id_to_available_instructions; + auto available_instructions = + available_composite_constituents.GetAvailableBeforeInstruction( + &*inst_it); + for (uint32_t available_instruction_index = 0; + available_instruction_index < available_instructions.size(); + available_instruction_index++) { + opt::Instruction* inst = + available_instructions[available_instruction_index]; + type_id_to_available_instructions[inst->type_id()].push_back( + inst->result_id()); + } + + // At this point, |composite_type_ids| captures all the composite types + // we could try to create, while |type_id_to_available_instructions| + // captures all the available result ids we might use, organized by + // type. + + // Now we choose a composite type to construct, building it from + // available constituent components and using zero constants if suitable + // components are not available. + + std::vector constructor_arguments; + uint32_t chosen_composite_type = + composite_type_ids[GetFuzzerContext()->RandomIndex( + composite_type_ids)]; + + // Construct a composite of this type, using an appropriate helper + // method depending on the kind of composite type. + auto composite_type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(chosen_composite_type); + switch (composite_type_inst->opcode()) { + case spv::Op::OpTypeArray: + constructor_arguments = FindComponentsToConstructArray( + *composite_type_inst, type_id_to_available_instructions); + break; + case spv::Op::OpTypeMatrix: + constructor_arguments = FindComponentsToConstructMatrix( + *composite_type_inst, type_id_to_available_instructions); + break; + case spv::Op::OpTypeStruct: + constructor_arguments = FindComponentsToConstructStruct( + *composite_type_inst, type_id_to_available_instructions); + break; + case spv::Op::OpTypeVector: + constructor_arguments = FindComponentsToConstructVector( + *composite_type_inst, type_id_to_available_instructions); + break; + default: + assert(false && + "The space of possible composite types should be covered " + "by the above cases."); + break; + } + assert(!constructor_arguments.empty()); + + // Make and apply a transformation. + ApplyTransformation(TransformationCompositeConstruct( + chosen_composite_type, constructor_arguments, + instruction_descriptor, GetFuzzerContext()->GetFreshId())); + }); +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(array_type_instruction.opcode() == spv::Op::OpTypeArray && + "Precondition: instruction must be an array type."); + + // Get the element type for the array. + auto element_type_id = array_type_instruction.GetSingleWordInOperand(0); + + // Get all instructions at our disposal that compute something of this element + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + + uint32_t array_length = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(array_type_instruction.GetSingleWordInOperand(1)) + ->GetSingleWordInOperand(0); + + std::vector result; + for (uint32_t index = 0; index < array_length; index++) { + if (available_instructions == type_id_to_available_instructions.cend()) { + // No suitable instructions are available, so use a zero constant + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); + } + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(matrix_type_instruction.opcode() == spv::Op::OpTypeMatrix && + "Precondition: instruction must be a matrix type."); + + // Get the element type for the matrix. + auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0); + + // Get all instructions at our disposal that compute something of this element + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + + std::vector result; + for (uint32_t index = 0; + index < matrix_type_instruction.GetSingleWordInOperand(1); index++) { + if (available_instructions == type_id_to_available_instructions.cend()) { + // No suitable components are available, so use a zero constant. + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); + } + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(struct_type_instruction.opcode() == spv::Op::OpTypeStruct && + "Precondition: instruction must be a struct type."); + std::vector result; + // Consider the type of each field of the struct. + for (uint32_t in_operand_index = 0; + in_operand_index < struct_type_instruction.NumInOperands(); + in_operand_index++) { + auto element_type_id = + struct_type_instruction.GetSingleWordInOperand(in_operand_index); + // Find the instructions at our disposal that compute something of the field + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + if (available_instructions == type_id_to_available_instructions.cend()) { + // No suitable component is available for this element type, so use a zero + // constant. + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); + } + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(vector_type_instruction.opcode() == spv::Op::OpTypeVector && + "Precondition: instruction must be a vector type."); + + // Get details of the type underlying the vector, and the width of the vector, + // for convenience. + auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0); + auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id); + auto element_count = vector_type_instruction.GetSingleWordInOperand(1); + + // Collect a mapping, from type id to width, for scalar/vector types that are + // smaller in width than |vector_type|, but that have the same underlying + // type. For example, if |vector_type| is vec4, the mapping will be: + // { float -> 1, vec2 -> 2, vec3 -> 3 } + // The mapping will have missing entries if some of these types do not exist. + + std::map smaller_vector_type_id_to_width; + // Add the underlying type. This id must exist, in order for |vector_type| to + // exist. + smaller_vector_type_id_to_width[element_type_id] = 1; + + // Now add every vector type with width at least 2, and less than the width of + // |vector_type|. + for (uint32_t width = 2; width < element_count; width++) { + opt::analysis::Vector smaller_vector_type(element_type, width); + auto smaller_vector_type_id = + GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type); + // We might find that there is no declared type of this smaller width. + // For example, a module can declare vec4 without having declared vec2 or + // vec3. + if (smaller_vector_type_id) { + smaller_vector_type_id_to_width[smaller_vector_type_id] = width; + } + } + + // Now we know the types that are available to us, we set about populating a + // vector of the right length. We do this by deciding, with no order in mind, + // which instructions we will use to populate the vector, and subsequently + // randomly choosing an order. This is to avoid biasing construction of + // vectors with smaller vectors to the left and scalars to the right. That is + // a concern because, e.g. in the case of populating a vec4, if we populate + // the constructor instructions left-to-right, we can always choose a vec3 to + // construct the first three elements, but can only choose a vec3 to construct + // the last three elements if we chose a float to construct the first element + // (otherwise there will not be space left for a vec3). + + uint32_t vector_slots_used = 0; + + // The instructions result ids we will use to construct the vector, in no + // particular order at this stage. + std::vector result; + + while (vector_slots_used < element_count) { + std::vector instructions_to_choose_from; + for (auto& entry : smaller_vector_type_id_to_width) { + if (entry.second > + std::min(element_count - 1, element_count - vector_slots_used)) { + continue; + } + auto available_instructions = + type_id_to_available_instructions.find(entry.first); + if (available_instructions == type_id_to_available_instructions.cend()) { + continue; + } + instructions_to_choose_from.insert(instructions_to_choose_from.end(), + available_instructions->second.begin(), + available_instructions->second.end()); + } + // If there are no instructions to choose from then use a zero constant, + // otherwise select one of the instructions at random. + uint32_t id_of_instruction_to_use = + instructions_to_choose_from.empty() + ? FindOrCreateZeroConstant(element_type_id, true) + : instructions_to_choose_from[GetFuzzerContext()->RandomIndex( + instructions_to_choose_from)]; + opt::Instruction* instruction_to_use = + GetIRContext()->get_def_use_mgr()->GetDef(id_of_instruction_to_use); + result.push_back(instruction_to_use->result_id()); + auto chosen_type = + GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id()); + if (chosen_type->AsVector()) { + assert(chosen_type->AsVector()->element_type() == element_type); + assert(chosen_type->AsVector()->element_count() < element_count); + assert(chosen_type->AsVector()->element_count() <= + element_count - vector_slots_used); + vector_slots_used += chosen_type->AsVector()->element_count(); + } else { + assert(chosen_type == element_type); + vector_slots_used += 1; + } + } + assert(vector_slots_used == element_count); + + GetFuzzerContext()->Shuffle(&result); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h new file mode 100644 index 00000000..81413980 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h @@ -0,0 +1,70 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ + +#include +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for constructing composite objects from smaller objects. +class FuzzerPassConstructComposites : public FuzzerPass { + public: + FuzzerPassConstructComposites( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Used to map a type id to the ids of relevant instructions of the type. + using TypeIdToInstructions = + std::unordered_map>; + + // Requires that |array_type_instruction| has opcode OpTypeArray. + // Attempts to find suitable instruction result ids from the values of + // |type_id_to_available_instructions| that would allow a composite of type + // |array_type_instruction| to be constructed. Returns said ids if they can + // be found and an empty vector otherwise. + std::vector FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for matrices. + std::vector FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for structs. + std::vector FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for vectors. + std::vector FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp new file mode 100644 index 00000000..725c33e5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_copy_objects.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_add_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassCopyObjects::FuzzerPassCopyObjects( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassCopyObjects::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + inst_it->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + // Don't create synonyms in dead blocks. + return; + } + + // Check whether it is legitimate to insert a copy before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyObject, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting an object copy here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCopyingObject())) { + return; + } + + const auto relevant_instructions = FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* ir_context, opt::Instruction* inst) { + return TransformationAddSynonym::IsInstructionValid( + ir_context, *GetTransformationContext(), inst, + protobufs::TransformationAddSynonym::COPY_OBJECT); + }); + + // At this point, |relevant_instructions| contains all the instructions + // we might think of copying. + if (relevant_instructions.empty()) { + return; + } + + // Choose a copyable instruction at random, and create and apply an + // object copying transformation based on it. + ApplyTransformation(TransformationAddSynonym( + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)] + ->result_id(), + protobufs::TransformationAddSynonym::COPY_OBJECT, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h new file mode 100644 index 00000000..2fb5a53f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding adding copies of objects to the module. +class FuzzerPassCopyObjects : public FuzzerPass { + public: + FuzzerPassCopyObjects(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp new file mode 100644 index 00000000..1696c74e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -0,0 +1,1220 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_donate_modules.h" + +#include +#include +#include + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/instruction_message.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_spec_constant_op.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassDonateModules::FuzzerPassDonateModules( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations, + std::vector donor_suppliers) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations), + donor_suppliers_(std::move(donor_suppliers)) {} + +void FuzzerPassDonateModules::Apply() { + // If there are no donor suppliers, this fuzzer pass is a no-op. + if (donor_suppliers_.empty()) { + return; + } + + // Donate at least one module, and probabilistically decide when to stop + // donating modules. + do { + // Choose a donor supplier at random, and get the module that it provides. + std::unique_ptr donor_ir_context = donor_suppliers_.at( + GetFuzzerContext()->RandomIndex(donor_suppliers_))(); + assert(donor_ir_context != nullptr && "Supplying of donor failed"); + assert( + fuzzerutil::IsValid(donor_ir_context.get(), + GetTransformationContext()->GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer) && + "The donor module must be valid"); + // Donate the supplied module. + // + // Randomly decide whether to make the module livesafe (see + // FactFunctionIsLivesafe); doing so allows it to be used for live code + // injection but restricts its behaviour to allow this, and means that its + // functions cannot be transformed as if they were arbitrary dead code. + bool make_livesafe = GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->ChanceOfMakingDonorLivesafe()); + DonateSingleModule(donor_ir_context.get(), make_livesafe); + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfDonatingAdditionalModule())); +} + +void FuzzerPassDonateModules::DonateSingleModule( + opt::IRContext* donor_ir_context, bool make_livesafe) { + // Check that the donated module has capabilities, supported by the recipient + // module. + for (const auto& capability_inst : donor_ir_context->capabilities()) { + auto capability = + static_cast(capability_inst.GetSingleWordInOperand(0)); + if (!GetIRContext()->get_feature_mgr()->HasCapability(capability)) { + return; + } + } + + // The ids used by the donor module may very well clash with ids defined in + // the recipient module. Furthermore, some instructions defined in the donor + // module will be equivalent to instructions defined in the recipient module, + // and it is not always legal to re-declare equivalent instructions. For + // example, OpTypeVoid cannot be declared twice. + // + // To handle this, we maintain a mapping from an id used in the donor module + // to the corresponding id that will be used by the donated code when it + // appears in the recipient module. + // + // This mapping is populated in two ways: + // (1) by mapping a donor instruction's result id to the id of some equivalent + // existing instruction in the recipient (e.g. this has to be done for + // OpTypeVoid) + // (2) by mapping a donor instruction's result id to a freshly chosen id that + // is guaranteed to be different from any id already used by the recipient + // (or from any id already chosen to handle a previous donor id) + std::map original_id_to_donated_id; + + HandleExternalInstructionImports(donor_ir_context, + &original_id_to_donated_id); + HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id); + HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some + // kinds of decoration. +} + +spv::StorageClass FuzzerPassDonateModules::AdaptStorageClass( + spv::StorageClass donor_storage_class) { + switch (donor_storage_class) { + case spv::StorageClass::Function: + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: + // We leave these alone + return donor_storage_class; + case spv::StorageClass::Input: + case spv::StorageClass::Output: + case spv::StorageClass::Uniform: + case spv::StorageClass::UniformConstant: + case spv::StorageClass::PushConstant: + case spv::StorageClass::Image: + case spv::StorageClass::StorageBuffer: + // We change these to Private + return spv::StorageClass::Private; + default: + // Handle other cases on demand. + assert(false && "Currently unsupported storage class."); + return spv::StorageClass::Max; + } +} + +void FuzzerPassDonateModules::HandleExternalInstructionImports( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id) { + // Consider every external instruction set import in the donor module. + for (auto& donor_import : donor_ir_context->module()->ext_inst_imports()) { + const auto& donor_import_name_words = donor_import.GetInOperand(0).words; + // Look for an identical import in the recipient module. + for (auto& existing_import : GetIRContext()->module()->ext_inst_imports()) { + const auto& existing_import_name_words = + existing_import.GetInOperand(0).words; + if (donor_import_name_words == existing_import_name_words) { + // A matching import has found. Map the result id for the donor import + // to the id of the existing import, so that when donor instructions + // rely on the import they will be rewritten to use the existing import. + original_id_to_donated_id->insert( + {donor_import.result_id(), existing_import.result_id()}); + break; + } + } + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3116): At present + // we do not handle donation of instruction imports, i.e. we do not allow + // the donor to import instruction sets that the recipient did not already + // import. It might be a good idea to allow this, but it requires some + // thought. + assert(original_id_to_donated_id->count(donor_import.result_id()) && + "Donation of imports is not yet supported."); + } +} + +void FuzzerPassDonateModules::HandleTypesAndValues( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id) { + // Consider every type/global/constant/undef in the module. + for (auto& type_or_value : donor_ir_context->module()->types_values()) { + HandleTypeOrValue(type_or_value, original_id_to_donated_id); + } +} + +void FuzzerPassDonateModules::HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id) { + // The type/value instruction generates a result id, and we need to associate + // the donor's result id with a new result id. That new result id will either + // be the id of some existing instruction, or a fresh id. This variable + // captures it. + uint32_t new_result_id; + + // Decide how to handle each kind of instruction on a case-by-case basis. + // + // Because the donor module is required to be valid, when we encounter a + // type comprised of component types (e.g. an aggregate or pointer), we know + // that its component types will have been considered previously, and that + // |original_id_to_donated_id| will already contain an entry for them. + switch (type_or_value.opcode()) { + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeSampler: + // We do not donate types and variables that relate to images and + // samplers, so we skip these types and subsequently skip anything that + // depends on them. + return; + case spv::Op::OpTypeVoid: { + // Void has to exist already in order for us to have an entry point. + // Get the existing id of void. + opt::analysis::Void void_type; + new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type); + assert(new_result_id && + "The module being transformed will always have 'void' type " + "declared."); + } break; + case spv::Op::OpTypeBool: { + // Bool cannot be declared multiple times, so use its existing id if + // present, or add a declaration of Bool with a fresh id if not. + opt::analysis::Bool bool_type; + auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); + if (bool_type_id) { + new_result_id = bool_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeBoolean(new_result_id)); + } + } break; + case spv::Op::OpTypeInt: { + // Int cannot be declared multiple times with the same width and + // signedness, so check whether an existing identical Int type is + // present and use its id if so. Otherwise add a declaration of the + // Int type used by the donor, with a fresh id. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + const bool is_signed = + static_cast(type_or_value.GetSingleWordInOperand(1)); + opt::analysis::Integer int_type(width, is_signed); + auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (int_type_id) { + new_result_id = int_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeInt(new_result_id, width, is_signed)); + } + } break; + case spv::Op::OpTypeFloat: { + // Similar to spv::Op::OpTypeInt. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + opt::analysis::Float float_type(width); + auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (float_type_id) { + new_result_id = float_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeFloat(new_result_id, width)); + } + } break; + case spv::Op::OpTypeVector: { + // It is not legal to have two Vector type declarations with identical + // element types and element counts, so check whether an existing + // identical Vector type is present and use its id if so. Otherwise add + // a declaration of the Vector type used by the donor, with a fresh id. + + // When considering the vector's component type id, we look up the id + // use in the donor to find the id to which this has been remapped. + uint32_t component_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "The base type should be registered."); + auto component_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Vector vector_type(component_type, component_count); + auto vector_type_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (vector_type_id) { + new_result_id = vector_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeVector( + new_result_id, component_type_id, component_count)); + } + } break; + case spv::Op::OpTypeMatrix: { + // Similar to spv::Op::OpTypeVector. + uint32_t column_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + assert(column_type && column_type->AsVector() && + "The column type should be a registered vector type."); + auto column_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto matrix_type_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (matrix_type_id) { + new_result_id = matrix_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeMatrix( + new_result_id, column_type_id, column_count)); + } + + } break; + case spv::Op::OpTypeArray: { + // It is OK to have multiple structurally identical array types, so + // we go ahead and add a remapped version of the type declared by the + // donor. + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this array type, so we + // cannot donate the array type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)))); + } break; + case spv::Op::OpTypeRuntimeArray: { + // A runtime array is allowed as the final member of an SSBO. During + // donation we turn runtime arrays into fixed-size arrays. For dead + // code donations this is OK because the array is never indexed into at + // runtime, so it does not matter what its size is. For live-safe code, + // all accesses are made in-bounds, so this is also OK. + // + // The special OpArrayLength instruction, which works on runtime arrays, + // is rewritten to yield the fixed length that is used for the array. + + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this runtime array type, so + // we cannot donate it as a fixed-size array. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, + false))); + } break; + case spv::Op::OpTypeStruct: { + // Similar to spv::Op::OpTypeArray. + std::vector member_type_ids; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + auto component_type_id = type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate every member type for this struct type, so we + // cannot donate the struct type. + return; + } + member_type_ids.push_back( + original_id_to_donated_id->at(component_type_id)); + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeStruct(new_result_id, member_type_ids)); + } break; + case spv::Op::OpTypePointer: { + // Similar to spv::Op::OpTypeArray. + uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1); + if (!original_id_to_donated_id->count(pointee_type_id)) { + // We did not donate the pointee type for this pointer type, so we + // cannot donate the pointer type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + new_result_id, + AdaptStorageClass(static_cast( + type_or_value.GetSingleWordInOperand(0))), + original_id_to_donated_id->at(pointee_type_id))); + } break; + case spv::Op::OpTypeFunction: { + // It is not OK to have multiple function types that use identical ids + // for their return and parameter types. We thus go through all + // existing function types to look for a match. We do not use the + // type manager here because we want to regard two function types that + // are structurally identical but that differ with respect to the + // actual ids used for pointer types as different. + // + // Example: + // + // %1 = OpTypeVoid + // %2 = OpTypeInt 32 0 + // %3 = OpTypePointer Function %2 + // %4 = OpTypePointer Function %2 + // %5 = OpTypeFunction %1 %3 + // %6 = OpTypeFunction %1 %4 + // + // We regard %5 and %6 as distinct function types here, even though + // they both have the form "uint32* -> void" + + std::vector return_and_parameter_types; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + uint32_t return_or_parameter_type = + type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(return_or_parameter_type)) { + // We did not donate every return/parameter type for this function + // type, so we cannot donate the function type. + return; + } + return_and_parameter_types.push_back( + original_id_to_donated_id->at(return_or_parameter_type)); + } + uint32_t existing_function_id = fuzzerutil::FindFunctionType( + GetIRContext(), return_and_parameter_types); + if (existing_function_id) { + new_result_id = existing_function_id; + } else { + // No match was found, so add a remapped version of the function type + // to the module, with a fresh id. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector argument_type_ids; + for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) { + argument_type_ids.push_back(original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(i))); + } + ApplyTransformation(TransformationAddTypeFunction( + new_result_id, + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)), + argument_type_ids)); + } + } break; + case spv::Op::OpSpecConstantOp: { + new_result_id = GetFuzzerContext()->GetFreshId(); + auto type_id = original_id_to_donated_id->at(type_or_value.type_id()); + auto opcode = + static_cast(type_or_value.GetSingleWordInOperand(0)); + + // Make sure we take into account |original_id_to_donated_id| when + // computing operands for OpSpecConstantOp. + opt::Instruction::OperandList operands; + for (uint32_t i = 1; i < type_or_value.NumInOperands(); ++i) { + const auto& operand = type_or_value.GetInOperand(i); + auto data = + operand.type == SPV_OPERAND_TYPE_ID + ? opt::Operand::OperandData{original_id_to_donated_id->at( + operand.words[0])} + : operand.words; + + operands.push_back({operand.type, std::move(data)}); + } + + ApplyTransformation(TransformationAddSpecConstantOp( + new_result_id, type_id, opcode, std::move(operands))); + } break; + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: { + // It is OK to have duplicate definitions of True and False, so add + // these to the module, using a remapped Bool type. + new_result_id = GetFuzzerContext()->GetFreshId(); + auto value = type_or_value.opcode() == spv::Op::OpConstantTrue || + type_or_value.opcode() == spv::Op::OpSpecConstantTrue; + ApplyTransformation( + TransformationAddConstantBoolean(new_result_id, value, false)); + } break; + case spv::Op::OpSpecConstant: + case spv::Op::OpConstant: { + // It is OK to have duplicate constant definitions, so add this to the + // module using a remapped result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector data_words; + type_or_value.ForEachInOperand([&data_words](const uint32_t* in_operand) { + data_words.push_back(*in_operand); + }); + ApplyTransformation(TransformationAddConstantScalar( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + data_words, false)); + } break; + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpConstantComposite: { + assert(original_id_to_donated_id->count(type_or_value.type_id()) && + "Composite types for which it is possible to create a constant " + "should have been donated."); + + // It is OK to have duplicate constant composite definitions, so add + // this to the module using remapped versions of all constituent ids and + // the result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector constituent_ids; + type_or_value.ForEachInId([&constituent_ids, &original_id_to_donated_id]( + const uint32_t* constituent_id) { + assert(original_id_to_donated_id->count(*constituent_id) && + "The constants used to construct this composite should " + "have been donated."); + constituent_ids.push_back( + original_id_to_donated_id->at(*constituent_id)); + }); + ApplyTransformation(TransformationAddConstantComposite( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + constituent_ids, false)); + } break; + case spv::Op::OpConstantNull: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this null constant, so + // we cannot donate the null constant. + return; + } + + // It is fine to have multiple OpConstantNull instructions of the same + // type, so we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + case spv::Op::OpVariable: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the pointer type associated with this variable, + // so we cannot donate the variable. + return; + } + + // This is a global variable that could have one of various storage + // classes. However, we change all global variable pointer storage + // classes (such as Uniform, Input and Output) to private when donating + // pointer types, with the exception of the Workgroup storage class. + // + // Thus this variable's pointer type is guaranteed to have storage class + // Private or Workgroup. + // + // We add a global variable with either Private or Workgroup storage + // class, using remapped versions of the result type and initializer ids + // for the global variable in the donor. + // + // We regard the added variable as having an irrelevant value. This + // means that future passes can add stores to the variable in any + // way they wish, and pass them as pointer parameters to functions + // without worrying about whether their data might get modified. + new_result_id = GetFuzzerContext()->GetFreshId(); + uint32_t remapped_pointer_type = + original_id_to_donated_id->at(type_or_value.type_id()); + uint32_t initializer_id; + spv::StorageClass storage_class = + static_cast(type_or_value.GetSingleWordInOperand( + 0)) == spv::StorageClass::Workgroup + ? spv::StorageClass::Workgroup + : spv::StorageClass::Private; + if (type_or_value.NumInOperands() == 1) { + // The variable did not have an initializer. Initialize it to zero + // if it has Private storage class (to limit problems associated with + // uninitialized data), and leave it uninitialized if it has Workgroup + // storage class (as Workgroup variables cannot have initializers). + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we + // could initialize Workgroup variables at the start of an entry + // point, and should do so if their uninitialized nature proves + // problematic. + initializer_id = storage_class == spv::StorageClass::Workgroup + ? 0 + : FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), remapped_pointer_type), + false); + } else { + // The variable already had an initializer; use its remapped id. + initializer_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)); + } + ApplyTransformation( + TransformationAddGlobalVariable(new_result_id, remapped_pointer_type, + storage_class, initializer_id, true)); + } break; + case spv::Op::OpUndef: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this undef, so we cannot + // donate the undef. + return; + } + + // It is fine to have multiple Undef instructions of the same type, so + // we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + default: { + assert(0 && "Unknown type/value."); + new_result_id = 0; + } break; + } + + // Update the id mapping to associate the instruction's result id with its + // corresponding id in the recipient. + original_id_to_donated_id->insert({type_or_value.result_id(), new_result_id}); +} + +void FuzzerPassDonateModules::HandleFunctions( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + bool make_livesafe) { + // Get the ids of functions in the donor module, topologically sorted + // according to the donor's call graph. + auto topological_order = + CallGraph(donor_ir_context).GetFunctionsInTopologicalOrder(); + + // Donate the functions in reverse topological order. This ensures that a + // function gets donated before any function that depends on it. This allows + // donation of the functions to be separated into a number of transformations, + // each adding one function, such that every prefix of transformations leaves + // the module valid. + for (auto function_id = topological_order.rbegin(); + function_id != topological_order.rend(); ++function_id) { + // Find the function to be donated. + opt::Function* function_to_donate = nullptr; + for (auto& function : *donor_ir_context->module()) { + if (function.result_id() == *function_id) { + function_to_donate = &function; + break; + } + } + assert(function_to_donate && "Function to be donated was not found."); + + if (!original_id_to_donated_id->count( + function_to_donate->DefInst().GetSingleWordInOperand(1))) { + // We were not able to donate this function's type, so we cannot donate + // the function. + continue; + } + + // We will collect up protobuf messages representing the donor function's + // instructions here, and use them to create an AddFunction transformation. + std::vector donated_instructions; + + // This set tracks the ids of those instructions for which donation was + // completely skipped: neither the instruction nor a substitute for it was + // donated. + std::set skipped_instructions; + + // Consider every instruction of the donor function. + function_to_donate->ForEachInst( + [this, &donated_instructions, donor_ir_context, + &original_id_to_donated_id, + &skipped_instructions](const opt::Instruction* instruction) { + if (instruction->opcode() == spv::Op::OpArrayLength) { + // We treat OpArrayLength specially. + HandleOpArrayLength(*instruction, original_id_to_donated_id, + &donated_instructions); + } else if (!CanDonateInstruction(donor_ir_context, *instruction, + *original_id_to_donated_id, + skipped_instructions)) { + // This is an instruction that we cannot directly donate. + HandleDifficultInstruction(*instruction, original_id_to_donated_id, + &donated_instructions, + &skipped_instructions); + } else { + PrepareInstructionForDonation(*instruction, donor_ir_context, + original_id_to_donated_id, + &donated_instructions); + } + }); + + // If |make_livesafe| is true, try to add the function in a livesafe manner. + // Otherwise (if |make_lifesafe| is false or an attempt to make the function + // livesafe has failed), add the function in a non-livesafe manner. + if (!make_livesafe || + !MaybeAddLivesafeFunction(*function_to_donate, donor_ir_context, + *original_id_to_donated_id, + donated_instructions)) { + ApplyTransformation(TransformationAddFunction(donated_instructions)); + } + } +} + +bool FuzzerPassDonateModules::CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const { + if (instruction.type_id() && + !original_id_to_donated_id.count(instruction.type_id())) { + // We could not donate the result type of this instruction, so we cannot + // donate the instruction. + return false; + } + + // Now consider instructions we specifically want to skip because we do not + // yet support them. + switch (instruction.opcode()) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + // We conservatively ignore all atomic instructions at present. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider + // being less conservative here. + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageWrite: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseRead: + case spv::Op::OpImageSampleFootprintNV: + case spv::Op::OpImage: + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQueryLod: + case spv::Op::OpImageQueryOrder: + case spv::Op::OpImageQuerySamples: + case spv::Op::OpImageQuerySize: + case spv::Op::OpImageQuerySizeLod: + case spv::Op::OpSampledImage: + // We ignore all instructions related to accessing images, since we do not + // donate images. + return false; + case spv::Op::OpLoad: + switch (donor_ir_context->get_def_use_mgr() + ->GetDef(instruction.type_id()) + ->opcode()) { + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeSampler: + // Again, we ignore instructions that relate to accessing images. + return false; + default: + break; + } + default: + break; + } + + // Examine each id input operand to the instruction. If it turns out that we + // have skipped any of these operands then we cannot donate the instruction. + bool result = true; + instruction.WhileEachInId( + [donor_ir_context, &original_id_to_donated_id, &result, + &skipped_instructions](const uint32_t* in_id) -> bool { + if (!original_id_to_donated_id.count(*in_id)) { + // We do not have a mapped result id for this id operand. That either + // means that it is a forward reference (which is OK), that we skipped + // the instruction that generated it (which is not OK), or that it is + // the id of a function or global value that we did not donate (which + // is not OK). We check for the latter two cases. + if (skipped_instructions.count(*in_id) || + // A function or global value does not have an associated basic + // block. + !donor_ir_context->get_instr_block(*in_id)) { + result = false; + return false; + } + } + return true; + }); + return result; +} + +bool FuzzerPassDonateModules::IsBasicType( + const opt::Instruction& instruction) const { + switch (instruction.opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeStruct: + case spv::Op::OpTypeVector: + return true; + default: + return false; + } +} + +void FuzzerPassDonateModules::HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const { + assert(instruction.opcode() == spv::Op::OpArrayLength && + "Precondition: instruction must be OpArrayLength."); + uint32_t donated_variable_id = + original_id_to_donated_id->at(instruction.GetSingleWordInOperand(0)); + auto donated_variable_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id); + auto pointer_to_struct_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + donated_variable_instruction->type_id()); + assert(pointer_to_struct_instruction->opcode() == spv::Op::OpTypePointer && + "Type of variable must be pointer."); + auto donated_struct_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + pointer_to_struct_instruction->GetSingleWordInOperand(1)); + assert(donated_struct_type_instruction->opcode() == spv::Op::OpTypeStruct && + "Pointee type of pointer used by OpArrayLength must be struct."); + assert(donated_struct_type_instruction->NumInOperands() == + instruction.GetSingleWordInOperand(1) + 1 && + "OpArrayLength must refer to the final member of the given " + "struct."); + uint32_t fixed_size_array_type_id = + donated_struct_type_instruction->GetSingleWordInOperand( + donated_struct_type_instruction->NumInOperands() - 1); + auto fixed_size_array_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(fixed_size_array_type_id); + assert(fixed_size_array_type_instruction->opcode() == spv::Op::OpTypeArray && + "The donated array type must be fixed-size."); + auto array_size_id = + fixed_size_array_type_instruction->GetSingleWordInOperand(1); + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + donated_instructions->push_back(MakeInstructionMessage( + spv::Op::OpCopyObject, + original_id_to_donated_id->at(instruction.type_id()), + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {array_size_id}}}))); +} + +void FuzzerPassDonateModules::HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions) { + if (!instruction.result_id()) { + // It does not generate a result id, so it can be ignored. + return; + } + if (!original_id_to_donated_id->count(instruction.type_id())) { + // We cannot handle this instruction's result type, so we need to skip it + // all together. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We now attempt to replace the instruction with an OpCopyObject. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3278): We could do + // something more refined here - we could check which operands to the + // instruction could not be donated and replace those operands with + // references to other ids (such as constants), so that we still get an + // instruction with the opcode and easy-to-handle operands of the donor + // instruction. + auto remapped_type_id = original_id_to_donated_id->at(instruction.type_id()); + if (!IsBasicType( + *GetIRContext()->get_def_use_mgr()->GetDef(remapped_type_id))) { + // The instruction has a non-basic result type, so we cannot replace it with + // an object copy of a constant. We thus skip it completely. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3279): We could + // instead look for an available id of the right type and generate an + // OpCopyObject of that id. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We are going to add an OpCopyObject instruction. Add a mapping for the + // result id of the original instruction if does not already exist (it may + // exist in the case that it has been forward-referenced). + if (!original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // We find or add a zero constant to the receiving module for the type in + // question, and add an OpCopyObject instruction that copies this zero. + // + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true); + donated_instructions->push_back(MakeInstructionMessage( + spv::Op::OpCopyObject, remapped_type_id, + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {zero_constant}}}))); +} + +void FuzzerPassDonateModules::PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) { + // Get the instruction's input operands into donation-ready form, + // remapping any id uses in the process. + opt::Instruction::OperandList input_operands; + + // Consider each input operand in turn. + for (uint32_t in_operand_index = 0; + in_operand_index < instruction.NumInOperands(); in_operand_index++) { + std::vector operand_data; + const opt::Operand& in_operand = instruction.GetInOperand(in_operand_index); + // Check whether this operand is an id. + if (spvIsIdType(in_operand.type)) { + // This is an id operand - it consists of a single word of data, + // which needs to be remapped so that it is replaced with the + // donated form of the id. + auto operand_id = in_operand.words[0]; + if (!original_id_to_donated_id->count(operand_id)) { + // This is a forward reference. We will choose a corresponding + // donor id for the referenced id and update the mapping to + // reflect it. + + // Keep release compilers happy because |donor_ir_context| is only used + // in this assertion. + (void)(donor_ir_context); + assert((donor_ir_context->get_def_use_mgr() + ->GetDef(operand_id) + ->opcode() == spv::Op::OpLabel || + instruction.opcode() == spv::Op::OpPhi) && + "Unsupported forward reference."); + original_id_to_donated_id->insert( + {operand_id, GetFuzzerContext()->GetFreshId()}); + } + operand_data.push_back(original_id_to_donated_id->at(operand_id)); + } else { + // For non-id operands, we just add each of the data words. + for (auto word : in_operand.words) { + operand_data.push_back(word); + } + } + input_operands.push_back({in_operand.type, operand_data}); + } + + if (instruction.opcode() == spv::Op::OpVariable && + instruction.NumInOperands() == 1) { + // This is an uninitialized local variable. Initialize it to zero. + input_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), + original_id_to_donated_id->at(instruction.type_id())), + false)}}); + } + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // Remap the result type and result id (if present) of the + // instruction, and turn it into a protobuf message. + donated_instructions->push_back(MakeInstructionMessage( + instruction.opcode(), + instruction.type_id() + ? original_id_to_donated_id->at(instruction.type_id()) + : 0, + instruction.result_id() + ? original_id_to_donated_id->at(instruction.result_id()) + : 0, + input_operands)); +} + +bool FuzzerPassDonateModules::CreateLoopLimiterInfo( + opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header, + const std::map& original_id_to_donated_id, + protobufs::LoopLimiterInfo* out) { + assert(loop_header.IsLoopHeader() && "|loop_header| is not a loop header"); + + // Grab the loop header's id, mapped to its donated value. + out->set_loop_header_id(original_id_to_donated_id.at(loop_header.id())); + + // Get fresh ids that will be used to load the loop limiter, increment + // it, compare it with the loop limit, and an id for a new block that + // will contain the loop's original terminator. + out->set_load_id(GetFuzzerContext()->GetFreshId()); + out->set_increment_id(GetFuzzerContext()->GetFreshId()); + out->set_compare_id(GetFuzzerContext()->GetFreshId()); + out->set_logical_op_id(GetFuzzerContext()->GetFreshId()); + + // We are creating a branch from the back-edge block to the merge block. Thus, + // if merge block has any OpPhi instructions, we might need to adjust + // them. + + // Note that the loop might have an unreachable back-edge block. This means + // that the loop can't iterate, so we don't need to adjust anything. + const auto back_edge_block_id = TransformationAddFunction::GetBackEdgeBlockId( + donor_ir_context, loop_header.id()); + if (!back_edge_block_id) { + return true; + } + + auto* back_edge_block = donor_ir_context->cfg()->block(back_edge_block_id); + assert(back_edge_block && "|back_edge_block_id| is invalid"); + + const auto* merge_block = + donor_ir_context->cfg()->block(loop_header.MergeBlockId()); + assert(merge_block && "Loop header has invalid merge block id"); + + // We don't need to adjust anything if there is already a branch from + // the back-edge block to the merge block. + if (back_edge_block->IsSuccessor(merge_block)) { + return true; + } + + // Adjust OpPhi instructions in the |merge_block|. + for (const auto& inst : *merge_block) { + if (inst.opcode() != spv::Op::OpPhi) { + break; + } + + // There is no simple way to ensure that a chosen operand for the OpPhi + // instruction will never cause any problems (e.g. if we choose an + // integer id, it might have a zero value when we branch from the back + // edge block. This might cause a division by 0 later in the function.). + // Thus, we ignore possible problems and proceed as follows: + // - if any of the existing OpPhi operands dominates the back-edge + // block - use it + // - if OpPhi has a basic type (see IsBasicType method) - create + // a zero constant + // - otherwise, we can't add a livesafe function. + uint32_t suitable_operand_id = 0; + for (uint32_t i = 0; i < inst.NumInOperands(); i += 2) { + auto dependency_inst_id = inst.GetSingleWordInOperand(i); + + if (fuzzerutil::IdIsAvailableBeforeInstruction( + donor_ir_context, back_edge_block->terminator(), + dependency_inst_id)) { + suitable_operand_id = original_id_to_donated_id.at(dependency_inst_id); + break; + } + } + + if (suitable_operand_id == 0 && + IsBasicType( + *donor_ir_context->get_def_use_mgr()->GetDef(inst.type_id()))) { + // We mark this constant as irrelevant so that we can replace it + // with more interesting value later. + suitable_operand_id = FindOrCreateZeroConstant( + original_id_to_donated_id.at(inst.type_id()), true); + } + + if (suitable_operand_id == 0) { + return false; + } + + out->add_phi_id(suitable_operand_id); + } + + return true; +} + +bool FuzzerPassDonateModules::MaybeAddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions) { + // Various types and constants must be in place for a function to be made + // live-safe. Add them if not already present. + FindOrCreateBoolType(); // Needed for comparisons + FindOrCreatePointerToIntegerType( + 32, false, + spv::StorageClass::Function); // Needed for adding loop limiters + FindOrCreateIntegerConstant({0}, 32, false, + false); // Needed for initializing loop limiters + FindOrCreateIntegerConstant({1}, 32, false, + false); // Needed for incrementing loop limiters + + // Get a fresh id for the variable that will be used as a loop limiter. + const uint32_t loop_limiter_variable_id = GetFuzzerContext()->GetFreshId(); + // Choose a random loop limit, and add the required constant to the + // module if not already there. + const uint32_t loop_limit = FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false); + + // Consider every loop header in the function to donate, and create a + // structure capturing the ids to be used for manipulating the loop + // limiter each time the loop is iterated. + std::vector loop_limiters; + for (auto& block : function_to_donate) { + if (block.IsLoopHeader()) { + protobufs::LoopLimiterInfo loop_limiter; + + if (!CreateLoopLimiterInfo(donor_ir_context, block, + original_id_to_donated_id, &loop_limiter)) { + return false; + } + + loop_limiters.emplace_back(std::move(loop_limiter)); + } + } + + // Consider every access chain in the function to donate, and create a + // structure containing the ids necessary to clamp the access chain + // indices to be in-bounds. + std::vector access_chain_clamping_info; + for (auto& block : function_to_donate) { + for (auto& inst : block) { + switch (inst.opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + protobufs::AccessChainClampingInfo clamping_info; + clamping_info.set_access_chain_id( + original_id_to_donated_id.at(inst.result_id())); + + auto base_object = donor_ir_context->get_def_use_mgr()->GetDef( + inst.GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef( + base_object->type_id()); + assert(pointer_type && + pointer_type->opcode() == spv::Op::OpTypePointer && + "The base object must have pointer type."); + + auto should_be_composite_type = + donor_ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Walk the access chain, creating fresh ids to facilitate + // clamping each index. For simplicity we do this for every + // index, even though constant indices will not end up being + // clamped. + for (uint32_t index = 1; index < inst.NumInOperands(); index++) { + auto compare_and_select_ids = + clamping_info.add_compare_and_select_ids(); + compare_and_select_ids->set_first(GetFuzzerContext()->GetFreshId()); + compare_and_select_ids->set_second( + GetFuzzerContext()->GetFreshId()); + + // Get the bound for the component being indexed into. + uint32_t bound; + if (should_be_composite_type->opcode() == + spv::Op::OpTypeRuntimeArray) { + // The donor is indexing into a runtime array. We do not + // donate runtime arrays. Instead, we donate a corresponding + // fixed-size array for every runtime array. We should thus + // find that donor composite type's result id maps to a fixed- + // size array. + auto fixed_size_array_type = + GetIRContext()->get_def_use_mgr()->GetDef( + original_id_to_donated_id.at( + should_be_composite_type->result_id())); + assert(fixed_size_array_type->opcode() == spv::Op::OpTypeArray && + "A runtime array type in the donor should have been " + "replaced by a fixed-sized array in the recipient."); + // The size of this fixed-size array is a suitable bound. + bound = fuzzerutil::GetBoundForCompositeIndex( + *fixed_size_array_type, GetIRContext()); + } else { + bound = fuzzerutil::GetBoundForCompositeIndex( + *should_be_composite_type, donor_ir_context); + } + const uint32_t index_id = inst.GetSingleWordInOperand(index); + auto index_inst = + donor_ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef( + index_inst->type_id()); + assert(index_type_inst->opcode() == spv::Op::OpTypeInt); + opt::analysis::Integer* index_int_type = + donor_ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + if (index_inst->opcode() != spv::Op::OpConstant) { + // We will have to clamp this index, so we need a constant + // whose value is one less than the bound, to compare + // against and to use as the clamped value. + FindOrCreateIntegerConstant({bound - 1}, 32, + index_int_type->IsSigned(), false); + } + should_be_composite_type = + TransformationAddFunction::FollowCompositeIndex( + donor_ir_context, *should_be_composite_type, index_id); + } + access_chain_clamping_info.push_back(clamping_info); + break; + } + default: + break; + } + } + } + + // If |function_to_donate| has non-void return type and contains an + // OpKill/OpUnreachable instruction, then a value is needed in order to turn + // these into instructions of the form OpReturnValue %value_id. + uint32_t kill_unreachable_return_value_id = 0; + auto function_return_type_inst = + donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id()); + if (function_return_type_inst->opcode() != spv::Op::OpTypeVoid && + fuzzerutil::FunctionContainsOpKillOrUnreachable(function_to_donate)) { + kill_unreachable_return_value_id = FindOrCreateZeroConstant( + original_id_to_donated_id.at(function_return_type_inst->result_id()), + false); + } + + // Try to add the function in a livesafe manner. This may fail due to edge + // cases, e.g. where adding loop limiters changes dominance such that the + // module becomes invalid. It would be ideal to handle all such edge cases, + // but as they are rare it is more pragmatic to bail out of making the + // function livesafe if the transformation's precondition fails to hold. + return MaybeApplyTransformation(TransformationAddFunction( + donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters, + kill_unreachable_return_value_id, access_chain_clamping_info)); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h new file mode 100644 index 00000000..004f1587 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h @@ -0,0 +1,164 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly adds code from other SPIR-V modules to the module +// being transformed. +class FuzzerPassDonateModules : public FuzzerPass { + public: + FuzzerPassDonateModules( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations, + std::vector donor_suppliers); + + void Apply() override; + + // Donates the global declarations and functions of |donor_ir_context| into + // the fuzzer pass's IR context. |make_livesafe| dictates whether the + // functions of the donated module will be made livesafe (see + // FactFunctionIsLivesafe). + void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe); + + private: + // Adapts a storage class coming from a donor module so that it will work + // in a recipient module, e.g. by changing Uniform to Private. + static spv::StorageClass AdaptStorageClass( + spv::StorageClass donor_storage_class); + + // Identifies all external instruction set imports in |donor_ir_context| and + // populates |original_id_to_donated_id| with a mapping from the donor's id + // for such an import to a corresponding import in the recipient. Aborts if + // no such corresponding import is available. + void HandleExternalInstructionImports( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id); + + // Considers all types, globals, constants and undefs in |donor_ir_context|. + // For each instruction, uses |original_to_donated_id| to map its result id to + // either (1) the id of an existing identical instruction in the recipient, or + // (2) to a fresh id, in which case the instruction is also added to the + // recipient (with any operand ids that it uses being remapped via + // |original_id_to_donated_id|). + void HandleTypesAndValues( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id); + + // Helper method for HandleTypesAndValues, to handle a single type/value. + void HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id); + + // Assumes that |donor_ir_context| does not exhibit recursion. Considers the + // functions in |donor_ir_context|'s call graph in a reverse-topologically- + // sorted order (leaves-to-root), adding each function to the recipient + // module, rewritten to use fresh ids and using |original_id_to_donated_id| to + // remap ids. The |make_livesafe| argument captures whether the functions in + // the module are required to be made livesafe before being added to the + // recipient. + void HandleFunctions(opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + bool make_livesafe); + + // During donation we will have to ignore some instructions, e.g. because they + // use opcodes that we cannot support or because they reference the ids of + // instructions that have not been donated. This function encapsulates the + // logic for deciding which whether instruction |instruction| from + // |donor_ir_context| can be donated. + bool CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const; + + // We treat the OpArrayLength instruction specially. In the donor shader this + // instruction yields the length of a runtime array that is the final member + // of a struct. During donation, we will have converted the runtime array + // type, and the associated struct field, into a fixed-size array. + // + // Instead of donating this instruction, we turn it into an OpCopyObject + // instruction that copies the size of the fixed-size array. + void HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const; + + // The instruction |instruction| is required to be an instruction that cannot + // be easily donated, either because it uses an unsupported opcode, has an + // unsupported result type, or uses id operands that could not be donated. + // + // If |instruction| generates a result id, the function attempts to add a + // substitute for |instruction| to |donated_instructions| that has the correct + // result type. If this cannot be done, the instruction's result id is added + // to |skipped_instructions|. The mapping from donor ids to recipient ids is + // managed by |original_id_to_donated_id|. + void HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions); + + // Adds an instruction based in |instruction| to |donated_instructions| in a + // form ready for donation. The original instruction comes from + // |donor_ir_context|, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to corresponding ids in the recipient module. + void PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions); + + // Tries to create a protobufs::LoopLimiterInfo given a loop header basic + // block. Returns true if successful and outputs loop limiter into the |out| + // variable. Otherwise, returns false. |out| contains an undefined value when + // this function returns false. + bool CreateLoopLimiterInfo( + opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header, + const std::map& original_id_to_donated_id, + protobufs::LoopLimiterInfo* out); + + // Requires that |donated_instructions| represents a prepared version of the + // instructions of |function_to_donate| (which comes from |donor_ir_context|) + // ready for donation, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to their corresponding ids in the recipient module. + // + // Attempts to add a livesafe version of the function, based on + // |donated_instructions|, to the recipient module. Returns true if the + // donation was successful, false otherwise. + bool MaybeAddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions); + + // Returns true if and only if |instruction| is a scalar, vector, matrix, + // array or struct; i.e. it is not an opaque type. + bool IsBasicType(const opt::Instruction& instruction) const; + + // Functions that supply SPIR-V modules + std::vector donor_suppliers_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp new file mode 100644 index 00000000..3a9a7e66 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_duplicate_region_with_selection.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassDuplicateRegionsWithSelections:: + FuzzerPassDuplicateRegionsWithSelections( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassDuplicateRegionsWithSelections::Apply() { + // Iterate over all of the functions in the module. + for (auto& function : *GetIRContext()->module()) { + // Randomly decide whether to apply the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfDuplicatingRegionWithSelection())) { + continue; + } + std::vector candidate_entry_blocks; + for (auto& block : function) { + // We don't consider the first block to be the entry block, since it + // could contain OpVariable instructions that would require additional + // operations to be reassigned. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3778): + // Consider extending this fuzzer pass to allow the first block to be + // used in duplication. + if (&block == &*function.begin()) { + continue; + } + candidate_entry_blocks.push_back(&block); + } + if (candidate_entry_blocks.empty()) { + continue; + } + // Randomly choose the entry block. + auto entry_block = candidate_entry_blocks[GetFuzzerContext()->RandomIndex( + candidate_entry_blocks)]; + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function); + auto postdominator_analysis = + GetIRContext()->GetPostDominatorAnalysis(&function); + std::vector candidate_exit_blocks; + for (auto postdominates_entry_block = entry_block; + postdominates_entry_block != nullptr; + postdominates_entry_block = postdominator_analysis->ImmediateDominator( + postdominates_entry_block)) { + // The candidate exit block must be dominated by the entry block and the + // entry block must be post-dominated by the candidate exit block. Ignore + // the block if it heads a selection construct or a loop construct. + if (dominator_analysis->Dominates(entry_block, + postdominates_entry_block) && + !postdominates_entry_block->GetMergeInst()) { + candidate_exit_blocks.push_back(postdominates_entry_block); + } + } + if (candidate_exit_blocks.empty()) { + continue; + } + // Randomly choose the exit block. + auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex( + candidate_exit_blocks)]; + + auto region_blocks = + TransformationDuplicateRegionWithSelection::GetRegionBlocks( + GetIRContext(), entry_block, exit_block); + + // Construct |original_label_to_duplicate_label| by iterating over all + // blocks in the region. Construct |original_id_to_duplicate_id| and + // |original_id_to_phi_id| by iterating over all instructions in each block. + std::map original_label_to_duplicate_label; + std::map original_id_to_duplicate_id; + std::map original_id_to_phi_id; + for (auto& block : region_blocks) { + original_label_to_duplicate_label[block->id()] = + GetFuzzerContext()->GetFreshId(); + for (auto& instr : *block) { + if (instr.result_id()) { + original_id_to_duplicate_id[instr.result_id()] = + GetFuzzerContext()->GetFreshId(); + auto final_instruction = &*exit_block->tail(); + // &*exit_block->tail() is the final instruction of the region. + // The instruction is available at the end of the region if and only + // if it is available before this final instruction or it is the final + // instruction. + if ((&instr == final_instruction || + fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), final_instruction, instr.result_id()))) { + original_id_to_phi_id[instr.result_id()] = + GetFuzzerContext()->GetFreshId(); + } + } + } + } + // Randomly decide between value "true" or "false" for a bool constant. + // Make sure the transformation has access to a bool constant to be used + // while creating conditional construct. + auto condition_id = + FindOrCreateBoolConstant(GetFuzzerContext()->ChooseEven(), true); + + TransformationDuplicateRegionWithSelection transformation = + TransformationDuplicateRegionWithSelection( + GetFuzzerContext()->GetFreshId(), condition_id, + GetFuzzerContext()->GetFreshId(), entry_block->id(), + exit_block->id(), original_label_to_duplicate_label, + original_id_to_duplicate_id, original_id_to_phi_id); + MaybeApplyTransformation(transformation); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h new file mode 100644 index 00000000..74343a66 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that iterates over the whole module. For each function it +// finds a single-entry, single-exit region with its constructs and their merge +// blocks either completely within or completely outside the region. It +// duplicates this region using the corresponding transformation. +class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { + public: + FuzzerPassDuplicateRegionsWithSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp new file mode 100644 index 00000000..fecd82e4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_expand_vector_reduction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassExpandVectorReductions::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decides whether the transformation will be applied. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfExpandingVectorReduction())) { + continue; + } + + // |instruction| must be OpAny or OpAll. + if (instruction.opcode() != spv::Op::OpAny && + instruction.opcode() != spv::Op::OpAll) { + continue; + } + + // It must be able to make a synonym of |instruction|. + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), instruction)) { + continue; + } + + // Applies the expand vector reduction transformation. + ApplyTransformation(TransformationExpandVectorReduction( + instruction.result_id(), + GetFuzzerContext()->GetFreshIds( + TransformationExpandVectorReduction::GetRequiredFreshIdCount( + GetIRContext(), &instruction)))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h new file mode 100644 index 00000000..c0673e12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass adds synonyms for the OpAny and OpAll instructions. It +// iterates over the module, checks if there are any OpAny or OpAll applicable +// instructions and randomly applies the expand vector reduction transformation. +class FuzzerPassExpandVectorReductions : public FuzzerPass { + public: + FuzzerPassExpandVectorReductions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp new file mode 100644 index 00000000..86ffff45 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -0,0 +1,249 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" + +#include "source/fuzz/comparator_deep_blocks_first.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_flatten_conditional_branch.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly selects conditional branches to flatten and +// flattens them, if possible. +FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassFlattenConditionalBranches::Apply() { + for (auto& function : *GetIRContext()->module()) { + // Get all the selection headers that we want to flatten. We need to collect + // all of them first, because, since we are changing the structure of the + // module, it's not safe to modify them while iterating. + std::vector selection_headers; + + for (auto& block : function) { + // Randomly decide whether to consider this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfFlatteningConditionalBranch())) { + continue; + } + + // Only consider this block if it is the header of a conditional, with a + // non-irrelevant condition. + if (block.GetMergeInst() && + block.GetMergeInst()->opcode() == spv::Op::OpSelectionMerge && + block.terminator()->opcode() == spv::Op::OpBranchConditional && + !GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + block.terminator()->GetSingleWordInOperand(0))) { + selection_headers.emplace_back(&block); + } + } + + // Sort the headers so that those that are more deeply nested are considered + // first, possibly enabling outer conditionals to be flattened. + std::sort(selection_headers.begin(), selection_headers.end(), + ComparatorDeepBlocksFirst(GetIRContext())); + + // Apply the transformation to the headers which can be flattened. + for (auto header : selection_headers) { + // Make a set to keep track of the instructions that need fresh ids. + std::set instructions_that_need_ids; + + // Do not consider this header if the conditional cannot be flattened. + if (!TransformationFlattenConditionalBranch:: + GetProblematicInstructionsIfConditionalCanBeFlattened( + GetIRContext(), header, *GetTransformationContext(), + &instructions_that_need_ids)) { + continue; + } + + uint32_t convergence_block_id = + TransformationFlattenConditionalBranch::FindConvergenceBlock( + GetIRContext(), *header); + + // If the SPIR-V version is restricted so that OpSelect can only work on + // scalar, pointer and vector types then we cannot apply this + // transformation to a header whose convergence block features OpPhi + // instructions on different types, as we cannot convert such instructions + // to OpSelect instructions. + if (TransformationFlattenConditionalBranch:: + OpSelectArgumentsAreRestricted(GetIRContext())) { + if (!GetIRContext() + ->cfg() + ->block(convergence_block_id) + ->WhileEachPhiInst( + [this](opt::Instruction* phi_instruction) -> bool { + switch (GetIRContext() + ->get_def_use_mgr() + ->GetDef(phi_instruction->type_id()) + ->opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypePointer: + case spv::Op::OpTypeVector: + return true; + default: + return false; + } + })) { + // An OpPhi is performed on a type not supported by OpSelect; we + // cannot flatten this selection. + continue; + } + } + + // If the construct's convergence block features OpPhi instructions with + // vector result types then we may be *forced*, by the SPIR-V version, to + // turn these into component-wise OpSelect instructions, or we might wish + // to do so anyway. The following booleans capture whether we will opt + // to use a component-wise select even if we don't have to. + bool use_component_wise_2d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + bool use_component_wise_3d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + bool use_component_wise_4d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + + // If we do need to perform any component-wise selections, we will need a + // fresh id for a boolean vector representing the selection's condition + // repeated N times, where N is the vector dimension. + uint32_t fresh_id_for_bvec2_selector = 0; + uint32_t fresh_id_for_bvec3_selector = 0; + uint32_t fresh_id_for_bvec4_selector = 0; + + GetIRContext() + ->cfg() + ->block(convergence_block_id) + ->ForEachPhiInst([this, &fresh_id_for_bvec2_selector, + &fresh_id_for_bvec3_selector, + &fresh_id_for_bvec4_selector, + use_component_wise_2d_select_even_if_optional, + use_component_wise_3d_select_even_if_optional, + use_component_wise_4d_select_even_if_optional]( + opt::Instruction* phi_instruction) { + opt::Instruction* type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + phi_instruction->type_id()); + switch (type_instruction->opcode()) { + case spv::Op::OpTypeVector: { + uint32_t dimension = + type_instruction->GetSingleWordInOperand(1); + switch (dimension) { + case 2: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_2d_select_even_if_optional, + &fresh_id_for_bvec2_selector); + break; + case 3: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_3d_select_even_if_optional, + &fresh_id_for_bvec3_selector); + break; + case 4: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_4d_select_even_if_optional, + &fresh_id_for_bvec4_selector); + break; + default: + assert(false && "Invalid vector dimension."); + } + break; + } + default: + break; + } + }); + + // Some instructions will require to be enclosed inside conditionals + // because they have side effects (for example, loads and stores). Some of + // this have no result id, so we require instruction descriptors to + // identify them. Each of them is associated with the necessary ids for it + // via a SideEffectWrapperInfo message. + std::vector wrappers_info; + + for (auto instruction : instructions_that_need_ids) { + protobufs::SideEffectWrapperInfo wrapper_info; + *wrapper_info.mutable_instruction() = + MakeInstructionDescriptor(GetIRContext(), instruction); + wrapper_info.set_merge_block_id(GetFuzzerContext()->GetFreshId()); + wrapper_info.set_execute_block_id(GetFuzzerContext()->GetFreshId()); + + // If the instruction has a non-void result id, we need to define more + // fresh ids and provide an id of the suitable type whose value can be + // copied in order to create a placeholder id. + if (TransformationFlattenConditionalBranch::InstructionNeedsPlaceholder( + GetIRContext(), *instruction)) { + wrapper_info.set_actual_result_id(GetFuzzerContext()->GetFreshId()); + wrapper_info.set_alternative_block_id( + GetFuzzerContext()->GetFreshId()); + wrapper_info.set_placeholder_result_id( + GetFuzzerContext()->GetFreshId()); + + // The id will be a zero constant if the type allows it, and an + // OpUndef otherwise. We want to avoid using OpUndef, if possible, to + // avoid undefined behaviour in the module as much as possible. + if (fuzzerutil::CanCreateConstant(GetIRContext(), + instruction->type_id())) { + wrapper_info.set_value_to_copy_id( + FindOrCreateZeroConstant(instruction->type_id(), true)); + } else { + wrapper_info.set_value_to_copy_id( + FindOrCreateGlobalUndef(instruction->type_id())); + } + } + + wrappers_info.push_back(std::move(wrapper_info)); + } + + // Apply the transformation, evenly choosing whether to lay out the true + // branch or the false branch first. + ApplyTransformation(TransformationFlattenConditionalBranch( + header->id(), GetFuzzerContext()->ChooseEven(), + fresh_id_for_bvec2_selector, fresh_id_for_bvec3_selector, + fresh_id_for_bvec4_selector, wrappers_info)); + } + } +} + +void FuzzerPassFlattenConditionalBranches::PrepareForOpPhiOnVectors( + uint32_t vector_dimension, bool use_vector_select_if_optional, + uint32_t* fresh_id_for_bvec_selector) { + if (*fresh_id_for_bvec_selector != 0) { + // We already have a fresh id for a component-wise OpSelect of this + // dimension + return; + } + if (TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted( + GetIRContext()) || + use_vector_select_if_optional) { + // We either have to, or have chosen to, perform a component-wise select, so + // we ensure that the right boolean vector type is available, and grab a + // fresh id. + FindOrCreateVectorType(FindOrCreateBoolType(), vector_dimension); + *fresh_id_for_bvec_selector = GetFuzzerContext()->GetFreshId(); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h new file mode 100644 index 00000000..abfbc3c1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H +#define SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +class FuzzerPassFlattenConditionalBranches : public FuzzerPass { + public: + FuzzerPassFlattenConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // If the SPIR-V version requires vector OpSelects to be component-wise, or + // if |use_vector_select_if_optional| holds, |fresh_id_for_bvec_selector| is + // populated with a fresh id if it is currently zero, and a + // |vector_dimension|-dimensional boolean vector type is added to the module + // if not already present. + void PrepareForOpPhiOnVectors(uint32_t vector_dimension, + bool use_vector_select_if_optional, + uint32_t* fresh_id_for_bvec_selector); +}; +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp new file mode 100644 index 00000000..6839bbe2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_inline_functions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_inline_function.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassInlineFunctions::Apply() { + // |function_call_instructions| are the instructions that will be inlined. + // First, they will be collected and then do the inlining in another loop. + // This avoids changing the module while it is being inspected. + std::vector function_call_instructions; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfInliningFunction())) { + continue; + } + + // |instruction| must be suitable for inlining. + if (!TransformationInlineFunction::IsSuitableForInlining( + GetIRContext(), &instruction)) { + continue; + } + + function_call_instructions.push_back(&instruction); + } + } + } + + // Once the function calls have been collected, it's time to actually create + // and apply the inlining transformations. + for (auto& function_call_instruction : function_call_instructions) { + // If |function_call_instruction| is not the penultimate instruction in its + // block or its block termination instruction is not OpBranch, then try to + // split |function_call_block| such that the conditions are met. + auto* function_call_block = + GetIRContext()->get_instr_block(function_call_instruction); + if ((function_call_instruction != &*--function_call_block->tail() || + function_call_block->terminator()->opcode() != spv::Op::OpBranch) && + !MaybeApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), + function_call_instruction->NextNode()), + GetFuzzerContext()->GetFreshId()))) { + continue; + } + + auto* called_function = fuzzerutil::FindFunction( + GetIRContext(), function_call_instruction->GetSingleWordInOperand(0)); + + // Mapping the called function instructions. + std::map result_id_map; + for (auto& called_function_block : *called_function) { + // The called function entry block label will not be inlined. + if (&called_function_block != &*called_function->entry()) { + result_id_map[called_function_block.GetLabelInst()->result_id()] = + GetFuzzerContext()->GetFreshId(); + } + + for (auto& instruction_to_inline : called_function_block) { + // The instructions are mapped to fresh ids. + if (instruction_to_inline.HasResultId()) { + result_id_map[instruction_to_inline.result_id()] = + GetFuzzerContext()->GetFreshId(); + } + } + } + + // Applies the inline function transformation. + ApplyTransformation(TransformationInlineFunction( + function_call_instruction->result_id(), result_id_map)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h new file mode 100644 index 00000000..c4e0b831 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Looks for OpFunctionCall instructions and randomly decides which ones to +// inline. If the instructions of the called function are going to be inlined, +// then a mapping, between their result ids and suitable ids, is done. +class FuzzerPassInlineFunctions : public FuzzerPass { + public: + FuzzerPassInlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp new file mode 100644 index 00000000..d8780ff7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fuzzer_pass_interchange_signedness_of_integer_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInterchangeSignednessOfIntegerOperands:: + FuzzerPassInterchangeSignednessOfIntegerOperands( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { + assert(!GetFuzzerContext()->IsWgslCompatible() && + "Cannot interchange signedness in WGSL"); + + // Make vector keeping track of all the uses we want to replace. + // This is a vector of pairs, where the first element is an id use descriptor + // identifying the use of a constant id and the second is the id that should + // be used to replace it. + std::vector> uses_to_replace; + + for (auto constant : GetIRContext()->GetConstants()) { + uint32_t constant_id = constant->result_id(); + + // We want to record the synonymity of an integer constant with another + // constant with opposite signedness, and this can only be done if they are + // not irrelevant. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_id)) { + continue; + } + + uint32_t toggled_id = + FindOrCreateToggledIntegerConstant(constant->result_id()); + if (!toggled_id) { + // Not an integer constant + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + toggled_id) && + "FindOrCreateToggledConstant can't produce an irrelevant id"); + + // Record synonymous constants + ApplyTransformation( + TransformationRecordSynonymousConstants(constant_id, toggled_id)); + + // Find all the uses of the constant and, for each, probabilistically + // decide whether to replace it. + GetIRContext()->get_def_use_mgr()->ForEachUse( + constant_id, + [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst, + uint32_t use_index) -> void { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfInterchangingSignednessOfIntegerOperands())) { + MaybeAddUseToReplace(use_inst, use_index, toggled_id, + &uses_to_replace); + } + }); + } + + // Replace the ids if it is allowed. + for (auto use_to_replace : uses_to_replace) { + MaybeApplyTransformation(TransformationReplaceIdWithSynonym( + use_to_replace.first, use_to_replace.second)); + } +} + +uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands:: + FindOrCreateToggledIntegerConstant(uint32_t id) { + // |id| must not be a specialization constant because we do not know the value + // of specialization constants. + if (opt::IsSpecConstantInst( + GetIRContext()->get_def_use_mgr()->GetDef(id)->opcode())) { + return 0; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id); + + // This pass only toggles integer constants. + if (!constant->AsIntConstant() && + (!constant->AsVectorConstant() || + !constant->AsVectorConstant()->component_type()->AsInteger())) { + return 0; + } + + if (auto integer = constant->AsIntConstant()) { + auto type = integer->type()->AsInteger(); + + // Find or create and return the toggled constant. + return FindOrCreateIntegerConstant(std::vector(integer->words()), + type->width(), !type->IsSigned(), false); + } + + // The constant is an integer vector. + + // Find the component type. + auto component_type = + constant->AsVectorConstant()->component_type()->AsInteger(); + + // Find or create the toggled component type. + uint32_t toggled_component_type = FindOrCreateIntegerType( + component_type->width(), !component_type->IsSigned()); + + // Get the information about the toggled components. We need to extract this + // information now because the analyses might be invalidated, which would make + // the constant and component_type variables invalid. + std::vector> component_words; + + for (auto component : constant->AsVectorConstant()->GetComponents()) { + component_words.push_back(component->AsIntConstant()->words()); + } + uint32_t width = component_type->width(); + bool is_signed = !component_type->IsSigned(); + + std::vector toggled_components; + + // Find or create the toggled components. + for (auto words : component_words) { + toggled_components.push_back( + FindOrCreateIntegerConstant(words, width, is_signed, false)); + } + + // Find or create the required toggled vector type. + uint32_t toggled_type = FindOrCreateVectorType( + toggled_component_type, (uint32_t)toggled_components.size()); + + // Find or create and return the toggled vector constant. + return FindOrCreateCompositeConstant(toggled_components, toggled_type, false); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h new file mode 100644 index 00000000..6a10e897 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that: +// - Finds all the integer constant (scalar and vector) definitions in the +// module and adds the definitions of the integer with the same data words but +// opposite signedness. If the synonym is already in the module, it does not +// add a new one. +// - For each use of an integer constant where its signedness does not matter, +// decides whether to change it to the id of the toggled constant. +class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { + public: + FuzzerPassInterchangeSignednessOfIntegerOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Given the id of an integer constant (scalar or vector), it finds or creates + // the corresponding toggled constant (the integer with the same data words + // but opposite signedness). Returns the id of the toggled instruction if the + // constant is an integer scalar or vector, 0 otherwise. + uint32_t FindOrCreateToggledIntegerConstant(uint32_t id); +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp new file mode 100644 index 00000000..2c16cd5b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { +FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( + opt::Instruction* declaration) { + // |declaration| must not be a specialization constant because we do not know + // the value of specialization constants. + if (opt::IsSpecConstantInst(declaration->opcode())) { + return 0; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + declaration->result_id()); + + // This pass only toggles zero-like constants + if (!constant->IsZero()) { + return 0; + } + + if (constant->AsScalarConstant()) { + return FindOrCreateNullConstant(declaration->type_id()); + } else if (constant->AsNullConstant()) { + // Add declaration of equivalent scalar constant + auto kind = constant->type()->kind(); + if (kind == opt::analysis::Type::kBool || + kind == opt::analysis::Type::kInteger || + kind == opt::analysis::Type::kFloat) { + return FindOrCreateZeroConstant(declaration->type_id(), false); + } + } + + return 0; +} + +void FuzzerPassInterchangeZeroLikeConstants::Apply() { + // Make vector keeping track of all the uses we want to replace. + // This is a vector of pairs, where the first element is an id use descriptor + // identifying the use of a constant id and the second is the id that should + // be used to replace it. + std::vector> uses_to_replace; + + for (auto constant : GetIRContext()->GetConstants()) { + uint32_t constant_id = constant->result_id(); + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_id)) { + continue; + } + + uint32_t toggled_id = FindOrCreateToggledConstant(constant); + if (!toggled_id) { + // Not a zero-like constant + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + toggled_id) && + "FindOrCreateToggledConstant can't produce an irrelevant id"); + + // Record synonymous constants + ApplyTransformation( + TransformationRecordSynonymousConstants(constant_id, toggled_id)); + + // Find all the uses of the constant and, for each, probabilistically + // decide whether to replace it. + GetIRContext()->get_def_use_mgr()->ForEachUse( + constant_id, + [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst, + uint32_t use_index) -> void { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfInterchangingZeroLikeConstants())) { + MaybeAddUseToReplace(use_inst, use_index, toggled_id, + &uses_to_replace); + } + }); + } + + // Replace the ids if it is allowed. + for (auto use_to_replace : uses_to_replace) { + MaybeApplyTransformation(TransformationReplaceIdWithSynonym( + use_to_replace.first, use_to_replace.second)); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h new file mode 100644 index 00000000..bb4a4dc7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that: +// - Finds all the zero-like constant definitions in the module and adds the +// definitions of the corresponding synonym, recording the fact that they +// are synonymous. If the synonym is already in the module, it does not +// add a new one. +// - For each use of a zero-like constant, decides whether to change it to the +// id of the toggled constant. +class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { + public: + FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Given the declaration of a zero-like constant, it finds or creates the + // corresponding toggled constant (a scalar constant of value 0 becomes a + // null constant of the same type and vice versa). + // Returns the id of the toggled instruction if the constant is zero-like, + // 0 otherwise. + uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration); +}; + +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp new file mode 100644 index 00000000..130f7502 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassInvertComparisonOperators::Apply() { + GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { + if (!TransformationInvertComparisonOperator::IsInversionSupported( + inst->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) { + return; + } + + ApplyTransformation(TransformationInvertComparisonOperator( + inst->result_id(), GetFuzzerContext()->GetFreshId())); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h new file mode 100644 index 00000000..d0d09edb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over different comparison operators in the module (>=, <, > etc.) +// and randomly decides whether to invert each one or not. +class FuzzerPassInvertComparisonOperators : public FuzzerPass { + public: + FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ \ No newline at end of file diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp new file mode 100644 index 00000000..ec5fc4b7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassMakeVectorOperationsDynamic::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decide whether to try applying the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfMakingVectorOperationDynamic())) { + continue; + } + + // |instruction| must be a vector operation. + if (!TransformationMakeVectorOperationDynamic::IsVectorOperation( + GetIRContext(), &instruction)) { + continue; + } + + // Make sure |instruction| has only one indexing operand. + assert( + instruction.NumInOperands() == + (instruction.opcode() == spv::Op::OpCompositeExtract ? 2 : 3) && + "FuzzerPassMakeVectorOperationsDynamic: the composite " + "instruction must have " + "only one indexing operand."); + + // Applies the make vector operation dynamic transformation. + ApplyTransformation(TransformationMakeVectorOperationDynamic( + instruction.result_id(), + FindOrCreateIntegerConstant( + {instruction.GetSingleWordInOperand( + instruction.opcode() == spv::Op::OpCompositeExtract ? 1 + : 2)}, + 32, GetFuzzerContext()->ChooseEven(), false))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h new file mode 100644 index 00000000..b6a46c78 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Looks for OpCompositeExtract/Insert instructions on vectors, and replaces +// them with OpVectorExtract/InsertDynamic. +class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { + public: + FuzzerPassMakeVectorOperationsDynamic( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp new file mode 100644 index 00000000..70200629 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_merge_blocks.h" + +#include + +#include "source/fuzz/transformation_merge_blocks.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassMergeBlocks::Apply() { + // First we populate a sequence of transformations that we might consider + // applying. + std::vector potential_transformations; + // We do this by considering every block of every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We probabilistically decide to ignore some blocks. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMergingBlocks())) { + continue; + } + // For other blocks, we add a transformation to merge the block into its + // predecessor if that transformation would be applicable. + TransformationMergeBlocks transformation(block.id()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + potential_transformations.push_back(transformation); + } + } + } + + while (!potential_transformations.empty()) { + auto transformation = + GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations); + MaybeApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h new file mode 100644 index 00000000..46e257f1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for merging blocks in the module. +class FuzzerPassMergeBlocks : public FuzzerPass { + public: + FuzzerPassMergeBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp new file mode 100644 index 00000000..48c18618 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -0,0 +1,338 @@ +// Copyright (c) 2020 Stefano Milizia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" +#include "source/fuzz/transformation_merge_function_returns.h" +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassMergeFunctionReturns::Apply() { + // The pass might add new functions to the module (due to wrapping early + // terminator instructions in function calls), so we record the functions that + // are currently present and then iterate over them. + std::vector functions; + for (auto& function : *GetIRContext()->module()) { + functions.emplace_back(&function); + } + + for (auto* function : functions) { + // Randomly decide whether to consider this function. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMergingFunctionReturns())) { + continue; + } + + // We skip wrappers for early terminators, since this fuzzer pass introduces + // such wrappers to eliminate early terminators. + if (IsEarlyTerminatorWrapper(*function)) { + continue; + } + + // Only consider functions that have early returns. + if (!function->HasEarlyReturn()) { + continue; + } + + // Wrap early terminators in function calls. + ForEachInstructionWithInstructionDescriptor( + function, + [this, function]( + opt::BasicBlock* /*unused*/, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + const spv::Op opcode = inst_it->opcode(); + switch (opcode) { + case spv::Op::OpKill: + case spv::Op::OpUnreachable: + case spv::Op::OpTerminateInvocation: { + // This is an early termination instruction - we need to wrap it + // so that it becomes a return. + if (TransformationWrapEarlyTerminatorInFunction:: + MaybeGetWrapperFunction(GetIRContext(), opcode) == + nullptr) { + // We don't have a suitable wrapper function, so create one. + ApplyTransformation(TransformationAddEarlyTerminatorWrapper( + GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), opcode)); + } + // If the function has non-void return type then we need a + // suitable value to use in an OpReturnValue instruction. + opt::Instruction* function_return_type = + GetIRContext()->get_def_use_mgr()->GetDef( + function->type_id()); + uint32_t returned_value_id; + if (function_return_type->opcode() == spv::Op::OpTypeVoid) { + // No value is needed. + returned_value_id = 0; + } else if (fuzzerutil::CanCreateConstant( + GetIRContext(), + function_return_type->result_id())) { + // We favour returning an irrelevant zero. + returned_value_id = FindOrCreateZeroConstant( + function_return_type->result_id(), true); + } else { + // It's not possible to use an irrelevant zero, so we use an + // OpUndef instead. + returned_value_id = + FindOrCreateGlobalUndef(function_return_type->result_id()); + } + // Wrap the early termination instruction in a function call. + ApplyTransformation(TransformationWrapEarlyTerminatorInFunction( + GetFuzzerContext()->GetFreshId(), instruction_descriptor, + returned_value_id)); + break; + } + default: + break; + } + }); + + // Get the return blocks. + auto return_blocks = fuzzerutil::GetReachableReturnBlocks( + GetIRContext(), function->result_id()); + + // Only go ahead if there is more than one reachable return block. + if (return_blocks.size() <= 1) { + continue; + } + + // Make sure that OpConstantTrue and OpConstantFalse are in the module. + FindOrCreateBoolConstant(true, false); + FindOrCreateBoolConstant(false, false); + + // Collect the ids available after the entry block of the function. + auto ids_available_after_entry_block = + GetTypesToIdsAvailableAfterEntryBlock(function); + + // If the entry block does not branch unconditionally to another block, + // split it. + if (function->entry()->terminator()->opcode() != spv::Op::OpBranch) { + SplitBlockAfterOpPhiOrOpVariable(function->entry()->id()); + } + + // Collect the merge blocks of the function whose corresponding loops + // contain return blocks. + auto merge_blocks = GetMergeBlocksOfLoopsContainingBlocks(return_blocks); + + // Split the merge blocks, if they contain instructions different from + // OpLabel, OpPhi and OpBranch. Collect the new ids of merge blocks. + std::vector actual_merge_blocks; + for (uint32_t merge_block : merge_blocks) { + opt::BasicBlock* block = GetIRContext()->get_instr_block(merge_block); + + // We don't need to split blocks that are already suitable (they only + // contain OpLabel, OpPhi or OpBranch instructions). + if (GetIRContext() + ->get_instr_block(merge_block) + ->WhileEachInst([](opt::Instruction* inst) { + return inst->opcode() == spv::Op::OpLabel || + inst->opcode() == spv::Op::OpPhi || + inst->opcode() == spv::Op::OpBranch; + })) { + actual_merge_blocks.emplace_back(merge_block); + continue; + } + + // If the merge block is also a loop header, we need to add a preheader, + // which will be the new merge block. + if (block->IsLoopHeader()) { + actual_merge_blocks.emplace_back( + GetOrCreateSimpleLoopPreheader(merge_block)->id()); + continue; + } + + // If the merge block is not a loop header, we must split it after the + // last OpPhi instruction. The merge block will be the first of the pair + // of blocks obtained after splitting, and it keeps the original id. + SplitBlockAfterOpPhiOrOpVariable(merge_block); + actual_merge_blocks.emplace_back(merge_block); + } + + // Get the ids needed by the transformation. + const uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); + const uint32_t unreachable_continue_id = GetFuzzerContext()->GetFreshId(); + const uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); + + bool function_is_void = + GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid(); + + // We only need a return value if the function is not void. + uint32_t return_val_id = + function_is_void ? 0 : GetFuzzerContext()->GetFreshId(); + + // We only need a placeholder for the return value if the function is not + // void and there is at least one relevant merge block. + uint32_t returnable_val_id = 0; + if (!function_is_void && !actual_merge_blocks.empty()) { + // If there is an id of the suitable type, choose one at random. + if (ids_available_after_entry_block.count(function->type_id())) { + const auto& candidates = + ids_available_after_entry_block[function->type_id()]; + returnable_val_id = + candidates[GetFuzzerContext()->RandomIndex(candidates)]; + } else { + // If there is no id, add a global OpUndef. + uint32_t suitable_id = FindOrCreateGlobalUndef(function->type_id()); + // Add the new id to the map of available ids. + ids_available_after_entry_block.emplace( + function->type_id(), std::vector({suitable_id})); + returnable_val_id = suitable_id; + } + } + + // Collect all the ids needed for merge blocks. + auto merge_blocks_info = GetInfoNeededForMergeBlocks( + actual_merge_blocks, &ids_available_after_entry_block); + + // Apply the transformation if it is applicable (it could be inapplicable if + // adding new predecessors to merge blocks breaks dominance rules). + MaybeApplyTransformation(TransformationMergeFunctionReturns( + function->result_id(), outer_header_id, unreachable_continue_id, + outer_return_id, return_val_id, returnable_val_id, merge_blocks_info)); + } +} + +std::map> +FuzzerPassMergeFunctionReturns::GetTypesToIdsAvailableAfterEntryBlock( + opt::Function* function) const { + std::map> result; + // Consider all global declarations + for (auto& global : GetIRContext()->module()->types_values()) { + if (global.HasResultId() && global.type_id()) { + if (!result.count(global.type_id())) { + result.emplace(global.type_id(), std::vector()); + } + result[global.type_id()].emplace_back(global.result_id()); + } + } + + // Consider all function parameters + function->ForEachParam([&result](opt::Instruction* param) { + if (param->HasResultId() && param->type_id()) { + if (!result.count(param->type_id())) { + result.emplace(param->type_id(), std::vector()); + } + + result[param->type_id()].emplace_back(param->result_id()); + } + }); + + // Consider all the instructions in the entry block. + for (auto& inst : *function->entry()) { + if (inst.HasResultId() && inst.type_id()) { + if (!result.count(inst.type_id())) { + result.emplace(inst.type_id(), std::vector()); + } + result[inst.type_id()].emplace_back(inst.result_id()); + } + } + + return result; +} + +std::set +FuzzerPassMergeFunctionReturns::GetMergeBlocksOfLoopsContainingBlocks( + const std::set& blocks) const { + std::set result; + for (uint32_t block : blocks) { + uint32_t merge_block = + GetIRContext()->GetStructuredCFGAnalysis()->LoopMergeBlock(block); + + while (merge_block != 0 && !result.count(merge_block)) { + // Add a new entry. + result.emplace(merge_block); + + // Walk up the loop tree. + block = merge_block; + merge_block = GetIRContext()->GetStructuredCFGAnalysis()->LoopMergeBlock( + merge_block); + } + } + + return result; +} + +std::vector +FuzzerPassMergeFunctionReturns::GetInfoNeededForMergeBlocks( + const std::vector& merge_blocks, + std::map>* + ids_available_after_entry_block) { + std::vector result; + for (uint32_t merge_block : merge_blocks) { + protobufs::ReturnMergingInfo info; + info.set_merge_block_id(merge_block); + info.set_is_returning_id(this->GetFuzzerContext()->GetFreshId()); + info.set_maybe_return_val_id(this->GetFuzzerContext()->GetFreshId()); + + // Add all the ids needed for the OpPhi instructions. + this->GetIRContext() + ->get_instr_block(merge_block) + ->ForEachPhiInst([this, &info, &ids_available_after_entry_block]( + opt::Instruction* phi_inst) { + protobufs::UInt32Pair entry; + entry.set_first(phi_inst->result_id()); + + // If there is an id of the suitable type, choose one at random. + if (ids_available_after_entry_block->count(phi_inst->type_id())) { + auto& candidates = + ids_available_after_entry_block->at(phi_inst->type_id()); + entry.set_second( + candidates[this->GetFuzzerContext()->RandomIndex(candidates)]); + } else { + // If there is no id, add a global OpUndef. + uint32_t suitable_id = + this->FindOrCreateGlobalUndef(phi_inst->type_id()); + // Add the new id to the map of available ids. + ids_available_after_entry_block->emplace( + phi_inst->type_id(), std::vector({suitable_id})); + entry.set_second(suitable_id); + } + + // Add the entry to the list. + *info.add_opphi_to_suitable_id() = entry; + }); + + result.emplace_back(info); + } + + return result; +} + +bool FuzzerPassMergeFunctionReturns::IsEarlyTerminatorWrapper( + const opt::Function& function) const { + for (spv::Op opcode : {spv::Op::OpKill, spv::Op::OpUnreachable, + spv::Op::OpTerminateInvocation}) { + if (TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction( + GetIRContext(), opcode) == &function) { + return true; + } + } + return false; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h new file mode 100644 index 00000000..a799b8be --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Stefano Milizia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for changing functions in the module so that they don't have an +// early return. When handling a function the pass first eliminates early +// terminator instructions, such as OpKill, by wrapping them in functions and +// replacing them with a function call followed by a return. The return +// instructions that arise are then modified so that the function does not have +// early returns. +class FuzzerPassMergeFunctionReturns : public FuzzerPass { + public: + FuzzerPassMergeFunctionReturns( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Returns a map from type ids to a list of ids with that type and which are + // available at the end of the entry block of |function|. + std::map> + GetTypesToIdsAvailableAfterEntryBlock(opt::Function* function) const; + + // Returns the set of all the loop merge blocks whose corresponding loops + // contain at least one of the blocks in |blocks|. + std::set GetMergeBlocksOfLoopsContainingBlocks( + const std::set& blocks) const; + + // Returns a list of ReturnMergingInfo messages, containing the information + // needed by the transformation for each of the relevant merge blocks. + // If a new id is created (because |ids_available_after_entry_block| does not + // have an entry for the corresponding type), a new entry is added to + // |ids_available_after_entry_block|, mapping its type to a singleton set + // containing it. + std::vector GetInfoNeededForMergeBlocks( + const std::vector& merge_blocks, + std::map>* + ids_available_after_entry_block); + + // Returns true if and only if |function| is a wrapper for an early terminator + // instruction such as OpKill. + bool IsEarlyTerminatorWrapper(const opt::Function& function) const; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp new file mode 100644 index 00000000..a7e9fdc3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_mutate_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMutatePointers::FuzzerPassMutatePointers( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassMutatePointers::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMutatingPointer())) { + return; + } + + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, + inst_it)) { + return; + } + + auto available_pointers = FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* ir_context, opt::Instruction* inst) { + return TransformationMutatePointer::IsValidPointerInstruction( + ir_context, *inst); + }); + + if (available_pointers.empty()) { + return; + } + + const auto* pointer_inst = + available_pointers[GetFuzzerContext()->RandomIndex( + available_pointers)]; + + // Make sure there is an irrelevant constant in the module. + FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_inst->type_id()), + true); + + ApplyTransformation(TransformationMutatePointer( + pointer_inst->result_id(), GetFuzzerContext()->GetFreshId(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h new file mode 100644 index 00000000..45c6a7cc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly mutates the value of each pointer instruction in the module. +class FuzzerPassMutatePointers : public FuzzerPass { + public: + FuzzerPassMutatePointers(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp new file mode 100644 index 00000000..48ac589e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -0,0 +1,524 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" + +#include +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger) { + auto bool_constant_opcode = GetIRContext() + ->get_def_use_mgr() + ->GetDef(bool_constant_use.id_of_interest()) + ->opcode(); + assert((bool_constant_opcode == spv::Op::OpConstantFalse || + bool_constant_opcode == spv::Op::OpConstantTrue) && + "Precondition: this must be a usage of a boolean constant."); + + // Pick an opcode at random. First randomly decide whether to generate + // a 'greater than' or 'less than' kind of opcode, and then select a + // random opcode from the resulting subset. + spv::Op comparison_opcode; + if (GetFuzzerContext()->ChooseEven()) { + comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex( + greater_than_opcodes)]; + } else { + comparison_opcode = + less_than_opcodes[GetFuzzerContext()->RandomIndex(less_than_opcodes)]; + } + + // We now need to decide how to order constant_id_1 and constant_id_2 such + // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the + // boolean constant. + const bool is_greater_than_opcode = + std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(), + comparison_opcode) != greater_than_opcodes.end(); + uint32_t lhs_id; + uint32_t rhs_id; + if ((bool_constant_opcode == spv::Op::OpConstantTrue && + first_constant_is_larger == is_greater_than_opcode) || + (bool_constant_opcode == spv::Op::OpConstantFalse && + first_constant_is_larger != is_greater_than_opcode)) { + lhs_id = constant_id_1; + rhs_id = constant_id_2; + } else { + lhs_id = constant_id_2; + rhs_id = constant_id_1; + } + + // We can now make a transformation that will replace |bool_constant_use| + // with an expression of the form (written using infix notation): + // |lhs_id| |comparison_opcode| |rhs_id| + auto transformation = TransformationReplaceBooleanConstantWithConstantBinary( + bool_constant_use, lhs_id, rhs_id, comparison_opcode, + GetFuzzerContext()->GetFreshId()); + // The transformation should be applicable by construction. + assert( + transformation.IsApplicable(GetIRContext(), *GetTransformationContext())); + + // Applying this transformation yields a pointer to the new instruction that + // computes the result of the binary expression. + auto binary_operator_instruction = transformation.ApplyWithResult( + GetIRContext(), GetTransformationContext()); + + // Add this transformation to the sequence of transformations that have been + // applied. + *GetTransformations()->add_transformation() = transformation.ToMessage(); + + // Having made a binary expression, there may now be opportunities to further + // obfuscate the constants used as the LHS and RHS of the expression (e.g. by + // replacing them with loads from known uniforms). + // + // We thus consider operands 0 and 1 (LHS and RHS in turn). + for (uint32_t index : {0u, 1u}) { + // We randomly decide, based on the current depth of obfuscation, whether + // to further obfuscate this operand. + if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) { + auto in_operand_use = MakeIdUseDescriptor( + binary_operator_instruction->GetSingleWordInOperand(index), + MakeInstructionDescriptor(binary_operator_instruction->result_id(), + binary_operator_instruction->opcode(), 0), + index); + ObfuscateConstant(depth + 1, in_operand_use); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2) { + auto float_constant_1 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_1) + ->AsFloatConstant(); + auto float_constant_2 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_2) + ->AsFloatConstant(); + assert(float_constant_1->words() != float_constant_2->words() && + "The constants should not be identical."); + assert(std::isfinite(float_constant_1->GetValueAsDouble()) && + "The constants must be finite numbers."); + assert(std::isfinite(float_constant_2->GetValueAsDouble()) && + "The constants must be finite numbers."); + bool first_constant_is_larger; + assert(float_constant_1->type()->AsFloat()->width() == + float_constant_2->type()->AsFloat()->width() && + "First and second floating-point constants must have the same width."); + if (float_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + float_constant_1->GetFloat() > float_constant_2->GetFloat(); + } else { + assert(float_constant_1->type()->AsFloat()->width() == 64 && + "Supported floating-point widths are 32 and 64."); + first_constant_is_larger = + float_constant_1->GetDouble() > float_constant_2->GetDouble(); + } + std::vector greater_than_opcodes{ + spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual, + spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual}; + std::vector less_than_opcodes{ + spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual, + spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + float_constant_id_1, float_constant_id_2, first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) { + auto signed_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_1) + ->AsIntConstant(); + auto signed_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_2) + ->AsIntConstant(); + assert(signed_int_constant_1->words() != signed_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(signed_int_constant_1->type()->AsInteger()->width() == + signed_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(signed_int_constant_1->type()->AsInteger()->IsSigned()); + assert(signed_int_constant_2->type()->AsInteger()->IsSigned()); + if (signed_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32(); + } else { + assert(signed_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64(); + } + std::vector greater_than_opcodes{spv::Op::OpSGreaterThan, + spv::Op::OpSGreaterThanEqual}; + std::vector less_than_opcodes{spv::Op::OpSLessThan, + spv::Op::OpSLessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + signed_int_constant_id_1, signed_int_constant_id_2, + first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, + uint32_t unsigned_int_constant_id_2) { + auto unsigned_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_1) + ->AsIntConstant(); + auto unsigned_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_2) + ->AsIntConstant(); + assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(unsigned_int_constant_1->type()->AsInteger()->width() == + unsigned_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned()); + assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned()); + if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32(); + } else { + assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64(); + } + std::vector greater_than_opcodes{spv::Op::OpUGreaterThan, + spv::Op::OpUGreaterThanEqual}; + std::vector less_than_opcodes{spv::Op::OpULessThan, + spv::Op::OpULessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + unsigned_int_constant_id_1, unsigned_int_constant_id_2, + first_constant_is_larger); +} + +std::vector> +FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType( + uint32_t type_id) { + assert(type_id && "Type id can't be 0"); + std::vector> result; + + for (const auto& facts_and_types : GetTransformationContext() + ->GetFactManager() + ->GetConstantUniformFactsAndTypes()) { + if (facts_and_types.second != type_id) { + continue; + } + + std::vector words(facts_and_types.first.constant_word().begin(), + facts_and_types.first.constant_word().end()); + if (std::find(result.begin(), result.end(), words) == result.end()) { + result.push_back(std::move(words)); + } + } + + return result; +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + // We want to replace the boolean constant use with a binary expression over + // scalar constants, but only if we can then potentially replace the constants + // with uniforms of the same value. + + auto available_types_with_uniforms = + GetTransformationContext() + ->GetFactManager() + ->GetTypesForWhichUniformValuesAreKnown(); + if (available_types_with_uniforms.empty()) { + // Do not try to obfuscate if we do not have access to any uniform + // elements with known values. + return; + } + auto chosen_type_id = + available_types_with_uniforms[GetFuzzerContext()->RandomIndex( + available_types_with_uniforms)]; + auto available_constant_words = + GetConstantWordsFromUniformsForType(chosen_type_id); + if (available_constant_words.size() == 1) { + // TODO(afd): for now we only obfuscate a boolean if there are at least + // two constants available from uniforms, so that we can do a + // comparison between them. It would be good to be able to do the + // obfuscation even if there is only one such constant, if there is + // also another regular constant available. + return; + } + + assert(!available_constant_words.empty() && + "There exists a fact but no constants - impossible"); + + // We know we have at least two known-to-be-constant uniforms of the chosen + // type. Pick one of them at random. + auto constant_index_1 = + GetFuzzerContext()->RandomIndex(available_constant_words); + uint32_t constant_index_2; + + // Now choose another one distinct from the first one. + do { + constant_index_2 = + GetFuzzerContext()->RandomIndex(available_constant_words); + } while (constant_index_1 == constant_index_2); + + auto constant_id_1 = FindOrCreateConstant( + available_constant_words[constant_index_1], chosen_type_id, false); + auto constant_id_2 = FindOrCreateConstant( + available_constant_words[constant_index_2], chosen_type_id, false); + + assert(constant_id_1 != 0 && constant_id_2 != 0 && + "We should not find an available constant with an id of 0."); + + // Now perform the obfuscation, according to whether the type of the constants + // is float, signed int, or unsigned int. + auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id); + if (chosen_type->AsFloat()) { + ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use, + constant_id_1, constant_id_2); + } else { + assert(chosen_type->AsInteger() && + "We should only have uniform facts about ints and floats."); + if (chosen_type->AsInteger()->IsSigned()) { + ObfuscateBoolConstantViaSignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } else { + ObfuscateBoolConstantViaUnsignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( + uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider + // additional ways to obfuscate scalar constants. + + // Check whether we know that any uniforms are guaranteed to be equal to the + // scalar constant associated with |constant_use|. + auto uniform_descriptors = + GetTransformationContext() + ->GetFactManager() + ->GetUniformDescriptorsForConstant(constant_use.id_of_interest()); + if (uniform_descriptors.empty()) { + // No relevant uniforms, so do not obfuscate. + return; + } + + // Choose a random available uniform known to be equal to the constant. + const auto& uniform_descriptor = + uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)]; + + // Make sure the module has OpConstant instructions for each index used to + // access a uniform. + for (auto index : uniform_descriptor.index()) { + FindOrCreateIntegerConstant({index}, 32, true, false); + } + + // Make sure the module has OpTypePointer that points to the element type of + // the uniform. + const auto* uniform_variable_instr = + FindUniformVariable(uniform_descriptor, GetIRContext(), true); + assert(uniform_variable_instr && + "Uniform variable does not exist or not unique."); + + const auto* uniform_variable_type_intr = + GetIRContext()->get_def_use_mgr()->GetDef( + uniform_variable_instr->type_id()); + assert(uniform_variable_type_intr && "Uniform variable has invalid type"); + + auto element_type_id = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1), + uniform_descriptor.index()); + assert(element_type_id && "Type of uniform variable is invalid"); + + FindOrCreatePointerType(element_type_id, spv::StorageClass::Uniform); + + // Create, apply and record a transformation to replace the constant use with + // the result of a load from the chosen uniform. + ApplyTransformation(TransformationReplaceConstantWithUniform( + constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId())); +} + +void FuzzerPassObfuscateConstants::ObfuscateConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + switch (GetIRContext() + ->get_def_use_mgr() + ->GetDef(constant_use.id_of_interest()) + ->opcode()) { + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + ObfuscateBoolConstant(depth, constant_use); + break; + case spv::Op::OpConstant: + ObfuscateScalarConstant(depth, constant_use); + break; + default: + assert(false && "The opcode should be one of the above."); + break; + } +} + +void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses) { + if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) { + // The operand is not an id, so it cannot be a constant id. + return; + } + auto operand_id = inst.GetSingleWordInOperand(in_operand_index); + auto operand_definition = + GetIRContext()->get_def_use_mgr()->GetDef(operand_id); + switch (operand_definition->opcode()) { + case spv::Op::OpConstantFalse: + case spv::Op::OpConstantTrue: + case spv::Op::OpConstant: { + // The operand is a constant id, so make an id use descriptor and record + // it. + protobufs::IdUseDescriptor id_use_descriptor; + id_use_descriptor.set_id_of_interest(operand_id); + id_use_descriptor.mutable_enclosing_instruction() + ->set_target_instruction_opcode(uint32_t(inst.opcode())); + id_use_descriptor.mutable_enclosing_instruction() + ->set_base_instruction_result_id(base_instruction_result_id); + id_use_descriptor.mutable_enclosing_instruction() + ->set_num_opcodes_to_ignore( + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 0 + : skipped_opcode_count.at(inst.opcode())); + id_use_descriptor.set_in_operand_index(in_operand_index); + constant_uses->push_back(id_use_descriptor); + } break; + default: + break; + } +} + +void FuzzerPassObfuscateConstants::Apply() { + // First, gather up all the constant uses available in the module, by going + // through each block in each function. + std::vector constant_uses; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // For each constant use we encounter we are going to make an id use + // descriptor. An id use is described with respect to a base instruction; + // if there are instructions at the start of the block without result ids, + // the base instruction will have to be the block's label. + uint32_t base_instruction_result_id = block.id(); + + // An id use descriptor also records how many instructions of a particular + // opcode need to be skipped in order to find the instruction of interest + // from the base instruction. We maintain a mapping that records a skip + // count for each relevant opcode. + std::map skipped_opcode_count; + + // Go through each instruction in the block. + for (auto& inst : block) { + if (inst.HasResultId()) { + // The instruction has a result id, so can be used as the base + // instruction from now on, until another instruction with a result id + // is encountered. + base_instruction_result_id = inst.result_id(); + // Opcode skip counts were with respect to the previous base + // instruction and are now irrelevant. + skipped_opcode_count.clear(); + } + + // The instruction must not be an OpVariable, the only id that an + // OpVariable uses is an initializer id, which has to remain + // constant. + if (inst.opcode() != spv::Op::OpVariable) { + // Consider each operand of the instruction, and add a constant id + // use for the operand if relevant. + for (uint32_t in_operand_index = 0; + in_operand_index < inst.NumInOperands(); in_operand_index++) { + MaybeAddConstantIdUse(inst, in_operand_index, + base_instruction_result_id, + skipped_opcode_count, &constant_uses); + } + } + + if (!inst.HasResultId()) { + // The instruction has no result id, so in order to identify future id + // uses for instructions with this opcode from the existing base + // instruction, we need to increase the skip count for this opcode. + skipped_opcode_count[inst.opcode()] = + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 1 + : skipped_opcode_count[inst.opcode()] + 1; + } + } + } + } + + // Go through the constant uses in a random order by repeatedly pulling out a + // constant use at a random index. + while (!constant_uses.empty()) { + auto index = GetFuzzerContext()->RandomIndex(constant_uses); + auto constant_use = std::move(constant_uses[index]); + constant_uses.erase(constant_uses.begin() + index); + // Decide probabilistically whether to skip or obfuscate this constant use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfObfuscatingConstant())) { + continue; + } + ObfuscateConstant(0, constant_use); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h new file mode 100644 index 00000000..bfef597c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -0,0 +1,111 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for turning uses of constants into more complex forms. +// Examples include replacing 'true' with '42 < 52', and replacing '42' with +// 'a.b.c' if 'a.b.c' is known to hold the value '42'. +class FuzzerPassObfuscateConstants : public FuzzerPass { + public: + FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Applies 0 or more transformations to potentially obfuscate the constant + // use represented by |constant_use|. The |depth| parameter controls how + // deeply obfuscation can recurse. + void ObfuscateConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // boolean constant, into a binary expression on scalar constants, which may + // themselves be recursively obfuscated. + void ObfuscateBoolConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // scalar constant, into the value loaded from a uniform known to have the + // same value as the constant (if one exists). + void ObfuscateScalarConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // Applies a transformation to replace the boolean constant usage represented + // by |bool_constant_use| with a binary expression involving + // |float_constant_id_1| and |float_constant_id_2|, which must not be equal + // to one another. Possibly further obfuscates the uses of these float + // constants. The |depth| parameter controls how deeply obfuscation can + // recurse. + void ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2); + + // Similar to the above, but for signed int constants. + void ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2); + + // Similar to the above, but for unsigned int constants. + void ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, uint32_t unsigned_int_constant_id_2); + + // A helper method to capture the common parts of the above methods. + // The method is used to obfuscate the boolean constant usage represented by + // |bool_constant_use| by replacing it with '|constant_id_1| OP + // |constant_id_2|', where 'OP' is chosen from either |greater_than_opcodes| + // or |less_than_opcodes|. + // + // The two constant ids must not represent the same value, and thus + // |greater_than_opcodes| may include 'greater than or equal' opcodes + // (similar for |less_than_opcodes|). + void ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger); + + // A helper method to determine whether input operand |in_operand_index| of + // |inst| is the id of a constant, and add an id use descriptor to + // |candidate_constant_uses| if so. The other parameters are used for id use + // descriptor construction. + void MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses); + + // Returns a vector of unique words that denote constants. Every such constant + // is used in |FactConstantUniform| and has type with id equal to |type_id|. + std::vector> GetConstantWordsFromUniformsForType( + uint32_t type_id); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp new file mode 100644 index 00000000..e64373a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_outline_functions.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_outline_function.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassOutlineFunctions::Apply() { + std::vector original_functions; + for (auto& function : *GetIRContext()->module()) { + original_functions.push_back(&function); + } + for (auto& function : original_functions) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfOutliningFunction())) { + continue; + } + std::vector blocks; + for (auto& block : *function) { + blocks.push_back(&block); + } + auto entry_block = MaybeGetEntryBlockSuitableForOutlining( + blocks[GetFuzzerContext()->RandomIndex(blocks)]); + + if (!entry_block) { + // The chosen block is not suitable to be the entry block of a region that + // will be outlined. + continue; + } + + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); + auto postdominator_analysis = + GetIRContext()->GetPostDominatorAnalysis(function); + std::vector candidate_exit_blocks; + for (auto postdominates_entry_block = entry_block; + postdominates_entry_block != nullptr; + postdominates_entry_block = postdominator_analysis->ImmediateDominator( + postdominates_entry_block)) { + // Consider the block if it is dominated by the entry block, ignore it if + // it is a continue target. + if (dominator_analysis->Dominates(entry_block, + postdominates_entry_block) && + !GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + postdominates_entry_block->id())) { + candidate_exit_blocks.push_back(postdominates_entry_block); + } + } + if (candidate_exit_blocks.empty()) { + continue; + } + auto exit_block = MaybeGetExitBlockSuitableForOutlining( + candidate_exit_blocks[GetFuzzerContext()->RandomIndex( + candidate_exit_blocks)]); + + if (!exit_block) { + // The block chosen is not suitable + continue; + } + + auto region_blocks = TransformationOutlineFunction::GetRegionBlocks( + GetIRContext(), entry_block, exit_block); + std::map input_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionInputIds( + GetIRContext(), region_blocks, exit_block)) { + input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + std::map output_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionOutputIds( + GetIRContext(), region_blocks, exit_block)) { + output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + TransformationOutlineFunction transformation( + entry_block->id(), exit_block->id(), + /*new_function_struct_return_type_id*/ + GetFuzzerContext()->GetFreshId(), + /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_region_entry_block*/ + GetFuzzerContext()->GetFreshId(), + /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(), + /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(), + /*input_id_to_fresh_id*/ input_id_to_fresh_id, + /*output_id_to_fresh_id*/ output_id_to_fresh_id); + MaybeApplyTransformation(transformation); + } +} + +opt::BasicBlock* +FuzzerPassOutlineFunctions::MaybeGetEntryBlockSuitableForOutlining( + opt::BasicBlock* entry_block) { + // If the entry block is a loop header, we need to get or create its + // preheader and make it the entry block, if possible. + if (entry_block->IsLoopHeader()) { + auto predecessors = + GetIRContext()->cfg()->preds(entry_block->GetLabel()->result_id()); + + if (predecessors.size() < 2) { + // The header only has one predecessor (the back-edge block) and thus + // it is unreachable. The block cannot be adjusted to be suitable for + // outlining. + return nullptr; + } + + // Get or create a suitable preheader and make it become the entry block. + entry_block = + GetOrCreateSimpleLoopPreheader(entry_block->GetLabel()->result_id()); + } + + assert(!entry_block->IsLoopHeader() && + "The entry block cannot be a loop header at this point."); + + // If the entry block starts with OpPhi or OpVariable, try to split it. + if (entry_block->begin()->opcode() == spv::Op::OpPhi || + entry_block->begin()->opcode() == spv::Op::OpVariable) { + // Find the first non-OpPhi and non-OpVariable instruction. + auto non_phi_or_var_inst = &*entry_block->begin(); + while (non_phi_or_var_inst->opcode() == spv::Op::OpPhi || + non_phi_or_var_inst->opcode() == spv::Op::OpVariable) { + non_phi_or_var_inst = non_phi_or_var_inst->NextNode(); + } + + // Split the block. + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst), + new_block_id)); + + // The new entry block is the newly-created block. + entry_block = &*entry_block->GetParent()->FindBlock(new_block_id); + } + + return entry_block; +} + +opt::BasicBlock* +FuzzerPassOutlineFunctions::MaybeGetExitBlockSuitableForOutlining( + opt::BasicBlock* exit_block) { + // The exit block must not be a continue target. + assert(!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + exit_block->id()) && + "A candidate exit block cannot be a continue target."); + + // If the exit block is a merge block, try to split it and return the second + // block in the pair as the exit block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + exit_block->id())) { + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + + // Find the first non-OpPhi instruction, after which to split. + auto split_before = &*exit_block->begin(); + while (split_before->opcode() == spv::Op::OpPhi) { + split_before = split_before->NextNode(); + } + + if (!MaybeApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), split_before), + new_block_id))) { + return nullptr; + } + + return &*exit_block->GetParent()->FindBlock(new_block_id); + } + + return exit_block; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h new file mode 100644 index 00000000..d80dc4a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h @@ -0,0 +1,64 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for outlining single-entry single-exit regions of a control +// flow graph into their own functions. +class FuzzerPassOutlineFunctions : public FuzzerPass { + public: + FuzzerPassOutlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + // Returns a block suitable to be an entry block for a region that can be + // outlined, i.e. a block that is not a loop header and that does not start + // with OpPhi or OpVariable. In particular, it returns: + // - |entry_block| if it is suitable + // - otherwise, a block found by: + // - looking for or creating a new preheader, if |entry_block| is a loop + // header + // - splitting the candidate entry block, if it starts with OpPhi or + // OpVariable. + // Returns nullptr if a suitable block cannot be found following the + // instructions above. + opt::BasicBlock* MaybeGetEntryBlockSuitableForOutlining( + opt::BasicBlock* entry_block); + + // Returns: + // - |exit_block| if it is not a merge block + // - the second block obtained by splitting |exit_block|, if |exit_block| is a + // merge block. + // Assumes that |exit_block| is not a continue target. + // The block returned by this function should be suitable to be the exit block + // of a region that can be outlined. + // Returns nullptr if |exit_block| is a merge block and it cannot be split. + opt::BasicBlock* MaybeGetExitBlockSuitableForOutlining( + opt::BasicBlock* exit_block); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp new file mode 100644 index 00000000..e55fae32 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_blocks.h" + +#include "source/fuzz/transformation_move_block_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPermuteBlocks::Apply() { + // For now we do something very simple: we randomly decide whether to move a + // block, and for each block that we do move, we push it down as far as we + // legally can. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2635): it would be + // nice to randomly sample from the set of legal block permutations and then + // encode the chosen permutation via a series of move-block-down + // transformations. This should be possible but will require some thought. + + for (auto& function : *GetIRContext()->module()) { + std::vector block_ids; + // Collect all block ids for the function before messing with block + // ordering. + for (auto& block : function) { + block_ids.push_back(block.id()); + } + // Now consider each block id. We consider block ids in reverse, because + // e.g. in code generated from the following: + // + // if (...) { + // A + // B + // } else { + // C + // } + // + // block A cannot be moved down, but B has freedom to move and that movement + // would provide more freedom for A to move. + for (auto id = block_ids.rbegin(); id != block_ids.rend(); ++id) { + // Randomly decide whether to ignore the block id. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMovingBlockDown())) { + continue; + } + // Keep pushing the block down, until pushing down fails. + // The loop is guaranteed to terminate because a block cannot be pushed + // down indefinitely. + while (true) { + TransformationMoveBlockDown transformation(*id); + if (!MaybeApplyTransformation(transformation)) { + break; + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h new file mode 100644 index 00000000..39326c2b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for shuffling the blocks of the module in a validity-preserving +// manner. +class FuzzerPassPermuteBlocks : public FuzzerPass { + public: + FuzzerPassPermuteBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp new file mode 100644 index 00000000..a8035b90 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_permute_function_parameters.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPermuteFunctionParameters::Apply() { + for (const auto& function : *GetIRContext()->module()) { + uint32_t function_id = function.result_id(); + + // Skip the function if it is an entry point + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingParameters())) { + continue; + } + + // Compute permutation for parameters + auto* function_type = + fuzzerutil::GetFunctionType(GetIRContext(), &function); + assert(function_type && "Function type is null"); + + // Don't take return type into account + uint32_t arg_size = function_type->NumInOperands() - 1; + + // Create a vector, fill it with [0, n-1] values and shuffle it + std::vector permutation(arg_size); + std::iota(permutation.begin(), permutation.end(), 0); + GetFuzzerContext()->Shuffle(&permutation); + + // Apply our transformation + ApplyTransformation(TransformationPermuteFunctionParameters( + function_id, GetFuzzerContext()->GetFreshId(), permutation)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h new file mode 100644 index 00000000..c5b6ad43 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -0,0 +1,44 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that, given a non-entry-point function taking n parameters +// and a permutation of the set [0, n - 1]: +// 1. Introduces a new function type that is the same as the original +// function's type but with the order of arguments permuted +// (only add this if it doesn't already exist) +// 2. Changes the type of the function to this type +// 3. Adjusts all calls to the function so that their arguments are permuted +class FuzzerPassPermuteFunctionParameters : public FuzzerPass { + public: + FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp new file mode 100644 index 00000000..2313f420 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_function_variables.h" + +#include +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_function_variables.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) { +} // Here we call parent constructor. + +void FuzzerPassPermuteFunctionVariables::Apply() { + // Permuting OpVariable instructions in each function. + for (auto& function : *GetIRContext()->module()) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingFunctionVariables())) { + continue; + } + + auto first_block = function.entry().get(); + + std::vector variables; + for (auto& instruction : *first_block) { + if (instruction.opcode() == spv::Op::OpVariable) { + variables.push_back(&instruction); + } + } + if (variables.size() <= 1) { + continue; + } + do { + uint32_t instruction_1_index = GetFuzzerContext()->RandomIndex(variables); + uint32_t instruction_2_index = GetFuzzerContext()->RandomIndex(variables); + + if (instruction_1_index != instruction_2_index) { + ApplyTransformation(TransformationSwapFunctionVariables( + variables[instruction_1_index]->result_id(), + variables[instruction_2_index]->result_id())); + } + + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfSwappingAnotherPairOfFunctionVariables()) && + variables.size() > 2); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h new file mode 100644 index 00000000..2ebc15f2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_function_variables.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass permutes variables in functions in the module. +class FuzzerPassPermuteFunctionVariables : public FuzzerPass { + public: + FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp new file mode 100644 index 00000000..3ef76be4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_instructions.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_move_instruction_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPermuteInstructions::Apply() { + // We are iterating over all instructions in all basic blocks. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We need to collect all instructions in the block into a separate vector + // since application of the transformation below might invalidate + // iterators. + std::vector instructions; + for (auto& instruction : block) { + instructions.push_back(&instruction); + } + + // We consider all instructions in reverse to increase the possible number + // of applied transformations. + for (auto it = instructions.rbegin(); it != instructions.rend(); ++it) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingInstructions())) { + continue; + } + + while (MaybeApplyTransformation(TransformationMoveInstructionDown( + MakeInstructionDescriptor(GetIRContext(), *it)))) { + // Apply the transformation as many times as possible. + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h new file mode 100644 index 00000000..b7ccbccb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Permutes instructions in every block of all while preserving the module's +// semantics. +class FuzzerPassPermuteInstructions : public FuzzerPass { + public: + FuzzerPassPermuteInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp new file mode 100644 index 00000000..7fbdd3b2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_phi_operands.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_permute_phi_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPermutePhiOperands::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& /*unused*/) { + const auto& inst = *inst_it; + + if (inst.opcode() != spv::Op::OpPhi) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) { + return; + } + + // Create a vector of indices for each pair of operands in the |inst|. + // OpPhi always has an even number of operands. + std::vector permutation(inst.NumInOperands() / 2); + std::iota(permutation.begin(), permutation.end(), 0); + GetFuzzerContext()->Shuffle(&permutation); + + ApplyTransformation(TransformationPermutePhiOperands( + inst.result_id(), std::move(permutation))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h new file mode 100644 index 00000000..30a9d4f5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all instructions in the module and randomly decides for each +// OpPhi instruction whether to permute its operands. +class FuzzerPassPermutePhiOperands : public FuzzerPass { + public: + FuzzerPassPermutePhiOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp new file mode 100644 index 00000000..4c46dcd3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/transformation_propagate_instruction_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPropagateInstructionsDown::Apply() { + for (const auto& function : *GetIRContext()->module()) { + std::vector reachable_blocks; + for (const auto& block : function) { + if (GetIRContext()->IsReachable(block)) { + reachable_blocks.push_back(&block); + } + } + + for (const auto* block : reachable_blocks) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPropagatingInstructionsDown())) { + continue; + } + + if (TransformationPropagateInstructionDown::IsApplicableToBlock( + GetIRContext(), block->id())) { + // Record fresh ids for every successor of the |block| that we can + // propagate an instruction into. + std::map fresh_ids; + for (auto id : + TransformationPropagateInstructionDown::GetAcceptableSuccessors( + GetIRContext(), block->id())) { + fresh_ids[id] = GetFuzzerContext()->GetFreshId(); + } + + ApplyTransformation(TransformationPropagateInstructionDown( + block->id(), GetFuzzerContext()->GetFreshId(), fresh_ids)); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h new file mode 100644 index 00000000..18f01654 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly propagates instructions from some block into the block's successors. +class FuzzerPassPropagateInstructionsDown : public FuzzerPass { + public: + FuzzerPassPropagateInstructionsDown( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp new file mode 100644 index 00000000..5e45da83 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_propagate_instruction_up.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPropagateInstructionsUp::Apply() { + for (const auto& function : *GetIRContext()->module()) { + for (const auto& block : function) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPropagatingInstructionsUp())) { + continue; + } + + if (TransformationPropagateInstructionUp::IsApplicableToBlock( + GetIRContext(), block.id())) { + std::map fresh_ids; + for (auto id : GetIRContext()->cfg()->preds(block.id())) { + auto& fresh_id = fresh_ids[id]; + + if (!fresh_id) { + // Create a fresh id if it hasn't been created yet. |fresh_id| will + // be default-initialized to 0 in this case. + fresh_id = GetFuzzerContext()->GetFreshId(); + } + } + ApplyTransformation( + TransformationPropagateInstructionUp(block.id(), fresh_ids)); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h new file mode 100644 index 00000000..0cb8396b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Decides whether to propagate instructions from some block into its +// predecessors. +class FuzzerPassPropagateInstructionsUp : public FuzzerPass { + public: + FuzzerPassPropagateInstructionsUp( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp new file mode 100644 index 00000000..c0397e18 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_push_id_through_variable.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassPushIdsThroughVariables::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert( + instruction_iterator->opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode()) && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try pushing an id through a variable. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPushingIdThroughVariable())) { + return; + } + + // The block containing the instruction we are going to insert before + // must be reachable. + if (!GetIRContext()->IsReachable(*block)) { + return; + } + + // It must be valid to insert OpStore and OpLoad instructions + // before the instruction to insert before. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpStore, instruction_iterator) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpLoad, instruction_iterator)) { + return; + } + + // Randomly decides whether a global or local variable will be added. + auto variable_storage_class = GetFuzzerContext()->ChooseEven() + ? spv::StorageClass::Private + : spv::StorageClass::Function; + + // Gets the available basic and pointer types. + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(variable_storage_class); + auto& basic_types = basic_type_ids_and_pointers.first; + + // There must be at least some basic types. + if (basic_types.empty()) { + return; + } + + uint32_t basic_type_id = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + + // Looks for ids that we might wish to consider pushing through a + // variable. + std::vector value_instructions = + FindAvailableInstructions( + function, block, instruction_iterator, + [this, basic_type_id, instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + + if (instruction->type_id() != basic_type_id) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + *instruction)) { + return false; + } + + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, + FindInstruction(instruction_descriptor, ir_context), + instruction->result_id()); + }); + + if (value_instructions.empty()) { + return; + } + + // If the pointer type does not exist, then create it. + FindOrCreatePointerType(basic_type_id, variable_storage_class); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // type support here is limited by the FindOrCreateZeroConstant + // function. + const auto* type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id); + assert(type_inst); + switch (type_inst->opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeStruct: + break; + default: + return; + } + + // Create a constant to initialize the variable from. This might update + // module's id bound so it must be done before any fresh ids are + // computed. + auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false); + + // Applies the push id through variable transformation. + ApplyTransformation(TransformationPushIdThroughVariable( + value_instructions[GetFuzzerContext()->RandomIndex( + value_instructions)] + ->result_id(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + uint32_t(variable_storage_class), initializer_id, + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h new file mode 100644 index 00000000..71b69492 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Adds instructions to the module that store existing ids into fresh variables +// and immediately load from said variables into new ids, thus creating synonyms +// between the existing and fresh ids. +class FuzzerPassPushIdsThroughVariables : public FuzzerPass { + public: + FuzzerPassPushIdsThroughVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp new file mode 100644 index 00000000..467e6134 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kArithmeticInstructionIndexLeftInOperand = 0; +} // namespace + +FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: + FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { + std::vector instructions_for_transformation; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decide whether to apply the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingAddSubMulWithCarryingExtended())) { + continue; + } + + // Check if the transformation can be applied to this instruction. + if (!TransformationReplaceAddSubMulWithCarryingExtended:: + IsInstructionSuitable(GetIRContext(), instruction)) { + continue; + } + instructions_for_transformation.push_back(instruction); + } + } + } + for (auto& instruction : instructions_for_transformation) { + // Get the operand type id. We know that both operands have the same + // type. + uint32_t operand_type_id = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + // Ensure the required struct type exists. The struct type is based on + // the operand type. + FindOrCreateStructType({operand_type_id, operand_type_id}); + + ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended( + GetFuzzerContext()->GetFreshId(), instruction.result_id())); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h new file mode 100644 index 00000000..268655f9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of +// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended, +// OpSMulExtended) computes the result into a struct. The second one extracts +// the appropriate component from the struct to yield the original result. +class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { + public: + FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp new file mode 100644 index 00000000..52c03818 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" + +#include +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: + FuzzerPassReplaceBranchesFromDeadBlocksWithExits( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { + // OpKill can only be used as a terminator in a function that is guaranteed + // to be executed with the Fragment execution model. We conservatively only + // allow OpKill if every entry point in the module has the Fragment execution + // model. + auto fragment_execution_model_guaranteed = std::all_of( + GetIRContext()->module()->entry_points().begin(), + GetIRContext()->module()->entry_points().end(), + [](const opt::Instruction& entry_point) -> bool { + return spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) == + spv::ExecutionModel::Fragment; + }); + + // Transformations of this type can disable one another. To avoid ordering + // bias, we therefore build a set of candidate transformations to apply, and + // subsequently apply them in a random order, skipping any that cease to be + // applicable. + std::vector + candidate_transformations; + + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Probabilistically decide whether to skip this block. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingBranchFromDeadBlockWithExit())) { + continue; + } + // Check whether the block is suitable for having its terminator replaced. + if (!TransformationReplaceBranchFromDeadBlockWithExit::BlockIsSuitable( + GetIRContext(), *GetTransformationContext(), block)) { + continue; + } + // We can always use OpUnreachable to replace a block's terminator. + // Whether we can use OpKill depends on the execution model, and which of + // OpReturn and OpReturnValue we can use depends on the return type of the + // enclosing function. + std::vector opcodes = {spv::Op::OpUnreachable}; + if (fragment_execution_model_guaranteed) { + opcodes.emplace_back(spv::Op::OpKill); + } + auto function_return_type = + GetIRContext()->get_type_mgr()->GetType(function.type_id()); + if (function_return_type->AsVoid()) { + opcodes.emplace_back(spv::Op::OpReturn); + } else if (fuzzerutil::CanCreateConstant(GetIRContext(), + function.type_id())) { + // For simplicity we only allow OpReturnValue if the function return + // type is a type for which we can create a constant. This allows us a + // zero of the given type as a default return value. + opcodes.emplace_back(spv::Op::OpReturnValue); + } + // Choose one of the available terminator opcodes at random and create a + // candidate transformation. + auto opcode = opcodes[GetFuzzerContext()->RandomIndex(opcodes)]; + candidate_transformations.emplace_back( + TransformationReplaceBranchFromDeadBlockWithExit( + block.id(), opcode, + opcode == spv::Op::OpReturnValue + ? FindOrCreateZeroConstant(function.type_id(), true) + : 0)); + } + } + + // Process the candidate transformations in a random order. + while (!candidate_transformations.empty()) { + // Transformations of this type can disable one another. For example, + // suppose we have dead blocks A, B, C, D arranged as follows: + // + // A | + // / \ | + // B C | + // \ / | + // D | + // + // Here we can replace the terminator of either B or C with an early exit, + // because D has two predecessors. But if we replace the terminator of B, + // say, we get: + // + // A | + // / \ | + // B C | + // / | + // D | + // + // and now it is no longer OK to replace the terminator of C as D only has + // one predecessor and we do not want to make D unreachable in the control + // flow graph. + MaybeApplyTransformation( + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_transformations)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h new file mode 100644 index 00000000..cdbb66f2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that, under the right conditions, replaces branch instructions +// from dead blocks with non-branching "exit" terminators, such as OpKill and +// OpReturn. +class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { + public: + FuzzerPassReplaceBranchesFromDeadBlocksWithExits( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp new file mode 100644 index 00000000..aabc6bc3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyMemoriesWithLoadsStores:: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace the OpCopyMemory. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyMemoryWithLoadStore())) { + return; + } + + // The instruction must be OpCopyMemory. + if (instruction->opcode() != spv::Op::OpCopyMemory) { + return; + } + + // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore. + ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore( + GetFuzzerContext()->GetFreshId(), + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h new file mode 100644 index 00000000..7d954abe --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyMemory with loading the source variable to +// an intermediate value and storing this value into the target variable of +// the original OpCopyMemory instruction. +class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { + public: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp new file mode 100644 index 00000000..c1892be1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyObjectsWithStoresLoads:: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace OpCopyObject. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyObjectWithStoreLoad())) { + return; + } + // The instruction must be OpCopyObject. + if (instruction->opcode() != spv::Op::OpCopyObject) { + return; + } + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (GetIRContext() + ->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == spv::Op::OpTypePointer) { + return; + } + // It must be valid to insert OpStore and OpLoad instructions + // before the instruction OpCopyObject. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore, + instruction) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, + instruction)) { + return; + } + + // Randomly decides whether a global or local variable will be added. + auto variable_storage_class = GetFuzzerContext()->ChooseEven() + ? spv::StorageClass::Private + : spv::StorageClass::Function; + + // Find or create a constant to initialize the variable from. The type of + // |instruction| must be such that the function FindOrCreateConstant can be + // called. + if (!fuzzerutil::CanCreateConstant(GetIRContext(), + instruction->type_id())) { + return; + } + auto variable_initializer_id = + FindOrCreateZeroConstant(instruction->type_id(), false); + + // Make sure that pointer type is defined. + FindOrCreatePointerType(instruction->type_id(), variable_storage_class); + // Apply the transformation replacing OpCopyObject with Store and Load. + ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad( + instruction->result_id(), GetFuzzerContext()->GetFreshId(), + uint32_t(variable_storage_class), variable_initializer_id)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h new file mode 100644 index 00000000..2ffc00b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyObject with storing into a new variable +// and immediately loading this variable to |result_id| of the +// original OpCopyObject instruction. +class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { + public: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp new file mode 100644 index 00000000..4c0bd859 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_replace_irrelevant_id.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that, for every use of an id that has been recorded as +// irrelevant, randomly decides whether to replace it with another id of the +// same type. +FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceIrrelevantIds::Apply() { + // Keep track of the irrelevant ids. This includes all the ids that are + // irrelevant according to the fact manager and that are still present in the + // module (some of them may have been removed by previously-run + // transformations). + std::vector irrelevant_ids; + + // Keep a map from the type ids of irrelevant ids to all the ids with that + // type. + std::unordered_map> types_to_ids; + + // Find all the irrelevant ids that still exist in the module and all the + // types for which irrelevant ids exist. + for (auto id : + GetTransformationContext()->GetFactManager()->GetIrrelevantIds()) { + // Check that the id still exists in the module. + auto declaration = GetIRContext()->get_def_use_mgr()->GetDef(id); + if (!declaration) { + continue; + } + + irrelevant_ids.push_back(id); + + // If the type of this id has not been seen before, add a mapping from this + // type id to an empty list in |types_to_ids|. The list will be filled later + // on. + if (types_to_ids.count(declaration->type_id()) == 0) { + types_to_ids.insert({declaration->type_id(), {}}); + } + } + + // If no irrelevant ids were found, return. + if (irrelevant_ids.empty()) { + return; + } + + // For every type for which we have at least one irrelevant id, record all ids + // in the module which have that type. Skip ids of OpFunction instructions as + // we cannot use these as replacements. + for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) { + uint32_t type_id = pair.second->type_id(); + if (pair.second->opcode() != spv::Op::OpFunction && type_id && + types_to_ids.count(type_id)) { + types_to_ids[type_id].push_back(pair.first); + } + } + + // Keep a list of all the transformations to perform. We avoid applying the + // transformations while traversing the uses since applying the transformation + // invalidates all analyses, and we want to avoid invalidating and recomputing + // them every time. + std::vector transformations_to_apply; + + // Loop through all the uses of irrelevant ids, check that the id can be + // replaced and randomly decide whether to apply the transformation. + for (auto irrelevant_id : irrelevant_ids) { + uint32_t type_id = + GetIRContext()->get_def_use_mgr()->GetDef(irrelevant_id)->type_id(); + + GetIRContext()->get_def_use_mgr()->ForEachUse( + irrelevant_id, [this, &irrelevant_id, &type_id, &types_to_ids, + &transformations_to_apply](opt::Instruction* use_inst, + uint32_t use_index) { + // Randomly decide whether to consider this use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingIrrelevantId())) { + return; + } + + // The id must be used as an input operand. + if (use_index < use_inst->NumOperands() - use_inst->NumInOperands()) { + // The id is used as an output operand, so we cannot replace this + // usage. + return; + } + + // Get the input operand index for this use, from the absolute operand + // index. + uint32_t in_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + + // Only go ahead if this id use can be replaced in principle. + if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(), + *GetTransformationContext(), + use_inst, in_index)) { + return; + } + + // Find out which ids could be used to replace this use. + std::vector available_replacement_ids; + + for (auto replacement_id : types_to_ids[type_id]) { + // It would be pointless to replace an id with itself. + if (replacement_id == irrelevant_id) { + continue; + } + + // We cannot replace a variable initializer with a non-constant. + if (TransformationReplaceIrrelevantId:: + AttemptsToReplaceVariableInitializerWithNonConstant( + *use_inst, *GetIRContext()->get_def_use_mgr()->GetDef( + replacement_id))) { + continue; + } + + // Only consider this replacement if the use point is within a basic + // block and the id is available at the use point. + // + // There might be opportunities for replacing a non-block use of an + // irrelevant id - such as the initializer of a global variable - + // with another id, but it would require some care (e.g. to ensure + // that the replacement id is defined earlier) and does not seem + // worth doing. + if (GetIRContext()->get_instr_block(use_inst) && + fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst, + in_index, replacement_id)) { + available_replacement_ids.push_back(replacement_id); + } + } + + // Only go ahead if there is at least one id with which this use can + // be replaced. + if (available_replacement_ids.empty()) { + return; + } + + // Choose the replacement id randomly. + uint32_t replacement_id = + available_replacement_ids[GetFuzzerContext()->RandomIndex( + available_replacement_ids)]; + + // Add this replacement to the list of transformations to apply. + transformations_to_apply.emplace_back( + TransformationReplaceIrrelevantId( + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, + in_index), + replacement_id)); + }); + } + + // Apply all the transformations. + for (const auto& transformation : transformations_to_apply) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h new file mode 100644 index 00000000..80f8eb86 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that, for every use of an irrelevant id, checks if it is +// possible to replace it with other ids of the same type and randomly decides +// whether to do it. +class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { + public: + FuzzerPassReplaceIrrelevantIds( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp new file mode 100644 index 00000000..445dbfe1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceLinearAlgebraInstructions:: + FuzzerPassReplaceLinearAlgebraInstructions( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { + // For each instruction, checks whether it is a linear algebra instruction. In + // this case, the transformation is randomly applied. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + if (!spvOpcodeIsLinearAlgebra(instruction.opcode())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingLinearAlgebraInstructions())) { + continue; + } + + ApplyTransformation(TransformationReplaceLinearAlgebraInstruction( + GetFuzzerContext()->GetFreshIds( + TransformationReplaceLinearAlgebraInstruction:: + GetRequiredFreshIdCount(GetIRContext(), &instruction)), + MakeInstructionDescriptor(GetIRContext(), &instruction))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h new file mode 100644 index 00000000..5734bf1f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass replaces linear algebra instructions with its mathematical +// definition. +class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { + public: + FuzzerPassReplaceLinearAlgebraInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp new file mode 100644 index 00000000..8d292ac3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceLoadsStoresWithCopyMemories:: + FuzzerPassReplaceLoadsStoresWithCopyMemories( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { + // We look for matching pairs of instructions OpLoad and + // OpStore within the same block. Potential instructions OpLoad to be matched + // are stored in a hash map. If we encounter instructions that write to memory + // or instructions of memory barriers that could operate on variables within + // unsafe storage classes we need to erase the hash map to avoid unsafe + // operations. + + // A vector of matching OpLoad and OpStore instructions. + std::vector> + op_load_store_pairs; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // A hash map storing potential OpLoad instructions. + std::unordered_map current_op_loads; + for (auto& instruction : block) { + // Add a potential OpLoad instruction. + if (instruction.opcode() == spv::Op::OpLoad) { + current_op_loads[instruction.result_id()] = &instruction; + } else if (instruction.opcode() == spv::Op::OpStore) { + if (current_op_loads.find(instruction.GetSingleWordOperand(1)) != + current_op_loads.end()) { + // We have found the matching OpLoad instruction to the current + // OpStore instruction. + op_load_store_pairs.push_back(std::make_pair( + current_op_loads[instruction.GetSingleWordOperand(1)], + &instruction)); + } + } + if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode( + instruction.opcode())) { + current_op_loads.clear(); + } else if (TransformationReplaceLoadStoreWithCopyMemory:: + IsMemoryBarrierOpCode(instruction.opcode())) { + for (auto it = current_op_loads.begin(); + it != current_op_loads.end();) { + // Get the storage class. + opt::Instruction* source_id = + GetIRContext()->get_def_use_mgr()->GetDef( + it->second->GetSingleWordOperand(2)); + spv::StorageClass storage_class = + fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), source_id->type_id()); + if (!TransformationReplaceLoadStoreWithCopyMemory:: + IsStorageClassSafeAcrossMemoryBarriers(storage_class)) { + it = current_op_loads.erase(it); + } else { + it++; + } + } + } + } + } + } + for (auto instr_pair : op_load_store_pairs) { + // Randomly decide to apply the transformation for the + // potential pairs. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingLoadStoreWithCopyMemory())) { + ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory( + MakeInstructionDescriptor(GetIRContext(), instr_pair.first), + MakeInstructionDescriptor(GetIRContext(), instr_pair.second))); + } + } +} // namespace fuzz +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h new file mode 100644 index 00000000..f6209fc0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and +// OpStore that have the same intermediate value and in each pair replaces the +// OpStore with an equivalent OpCopyMemory. +class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { + public: + FuzzerPassReplaceLoadsStoresWithCopyMemories( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp new file mode 100644 index 00000000..26475ab2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" + +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: + FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { + // Keep a vector of the transformations to apply. + std::vector transformations; + + // Loop through the reachable blocks in the module. + for (auto& function : *GetIRContext()->module()) { + GetIRContext()->cfg()->ForEachBlockInPostOrder( + &*function.begin(), + [this, &function, &transformations](opt::BasicBlock* block) { + // Only consider dead blocks. + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + return; + } + + // Find all the uses of the label id of the block inside OpPhi + // instructions. + GetIRContext()->get_def_use_mgr()->ForEachUse( + block->id(), [this, &function, block, &transformations]( + opt::Instruction* instruction, uint32_t) { + // Only consider OpPhi instructions. + if (instruction->opcode() != spv::Op::OpPhi) { + return; + } + + // Randomly decide whether to consider this use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingOpPhiIdFromDeadPredecessor())) { + return; + } + + // Get the current id corresponding to the predecessor. + uint32_t current_id = 0; + for (uint32_t i = 1; i < instruction->NumInOperands(); i += 2) { + if (instruction->GetSingleWordInOperand(i) == block->id()) { + // The corresponding id is at the index of the block - 1. + current_id = instruction->GetSingleWordInOperand(i - 1); + break; + } + } + assert( + current_id != 0 && + "The predecessor - and corresponding id - should always be " + "found."); + + uint32_t type_id = instruction->type_id(); + + // Find all the suitable instructions to replace the id. + const auto& candidates = FindAvailableInstructions( + &function, block, block->end(), + [type_id, current_id](opt::IRContext* /* unused */, + opt::Instruction* candidate) -> bool { + // Only consider instructions with a result id different + // from the currently-used one, and with the right type. + return candidate->HasResultId() && + candidate->type_id() == type_id && + candidate->result_id() != current_id; + }); + + // If there is no possible replacement, we cannot apply any + // transformation. + if (candidates.empty()) { + return; + } + + // Choose one of the candidates. + uint32_t replacement_id = + candidates[GetFuzzerContext()->RandomIndex(candidates)] + ->result_id(); + + // Add a new transformation to the list of transformations to + // apply. + transformations.emplace_back( + TransformationReplaceOpPhiIdFromDeadPredecessor( + instruction->result_id(), block->id(), replacement_id)); + }); + }); + } + + // Apply all the transformations. + for (const auto& transformation : transformations) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h new file mode 100644 index 00000000..b01e242f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces id operands in OpPhi instructions with other available ids of the +// right type, where the corresponding predecessor is dead. +class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { + public: + FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp new file mode 100644 index 00000000..4691e0a8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceOpSelectsWithConditionalBranches:: + FuzzerPassReplaceOpSelectsWithConditionalBranches( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { + // Keep track of the instructions that we want to replace. We need to collect + // them in a vector, since it's not safe to modify the module while iterating + // over it. + std::vector replaceable_opselect_instruction_ids; + + // Loop over all the instructions in the module. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We cannot split loop headers, so we don't need to consider instructions + // in loop headers that are also merge blocks (since they would need to be + // split). + if (block.IsLoopHeader() && + GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + block.id())) { + continue; + } + + for (auto& instruction : block) { + // We only care about OpSelect instructions. + if (instruction.opcode() != spv::Op::OpSelect) { + continue; + } + + // Randomly choose whether to consider this instruction for replacement. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingOpselectWithConditionalBranch())) { + continue; + } + + // If the selector does not have scalar boolean type (i.e., it is a + // boolean vector) then ignore this OpSelect. + if (GetIRContext() + ->get_def_use_mgr() + ->GetDef(fuzzerutil::GetTypeId( + GetIRContext(), instruction.GetSingleWordInOperand(0))) + ->opcode() != spv::Op::OpTypeBool) { + continue; + } + + // If the block is a loop header and we need to split it, the + // transformation cannot be applied because loop headers cannot be + // split. We can break out of this loop because the transformation can + // only be applied to at most the first instruction in a loop header. + if (block.IsLoopHeader() && InstructionNeedsSplitBefore(&instruction)) { + break; + } + + // If the instruction separates an OpSampledImage from its use, the + // block cannot be split around it and the instruction cannot be + // replaced. + if (fuzzerutil:: + SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + &block, &instruction)) { + continue; + } + + // We can apply the transformation to this instruction. + replaceable_opselect_instruction_ids.push_back(instruction.result_id()); + } + } + } + + // Apply the transformations, splitting the blocks containing the + // instructions, if necessary. + for (uint32_t instruction_id : replaceable_opselect_instruction_ids) { + auto instruction = + GetIRContext()->get_def_use_mgr()->GetDef(instruction_id); + + // If the instruction requires the block containing it to be split before + // it, split the block. + if (InstructionNeedsSplitBefore(instruction)) { + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), instruction), + GetFuzzerContext()->GetFreshId())); + } + + // Decide whether to have two branches or just one. + bool two_branches = GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingBothBranchesWhenReplacingOpSelect()); + + // If there will be only one branch, decide whether it will be the true + // branch or the false branch. + bool true_branch_id_zero = + !two_branches && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingTrueBranchWhenReplacingOpSelect()); + bool false_branch_id_zero = !two_branches && !true_branch_id_zero; + + uint32_t true_branch_id = + true_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId(); + uint32_t false_branch_id = + false_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId(); + + ApplyTransformation(TransformationReplaceOpSelectWithConditionalBranch( + instruction_id, true_branch_id, false_branch_id)); + } +} + +bool FuzzerPassReplaceOpSelectsWithConditionalBranches:: + InstructionNeedsSplitBefore(opt::Instruction* instruction) { + assert(instruction && instruction->opcode() == spv::Op::OpSelect && + "The instruction must be OpSelect."); + + auto block = GetIRContext()->get_instr_block(instruction); + assert(block && "The instruction must be contained in a block."); + + // We need to split the block if the instruction is not the first in its + // block. + if (instruction->unique_id() != block->begin()->unique_id()) { + return true; + } + + // We need to split the block if it is a merge block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return true; + } + + // We need to split the block if it has more than one predecessor. + if (GetIRContext()->cfg()->preds(block->id()).size() != 1) { + return true; + } + + // We need to split the block if its predecessor is a header or it does not + // branch unconditionally to the block. + auto predecessor = GetIRContext()->get_instr_block( + GetIRContext()->cfg()->preds(block->id())[0]); + return predecessor->MergeBlockIdIfAny() || + predecessor->terminator()->opcode() != spv::Op::OpBranch; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h new file mode 100644 index 00000000..174962ee --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to replace OpSelect instructions (where the condition is a +// scalar boolean) with conditional branches and OpPhi instructions. +class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { + public: + FuzzerPassReplaceOpSelectsWithConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Returns true if any of the following holds: + // - the instruction is not the first in its block + // - the block containing it is a merge block + // - the block does not have a unique predecessor + // - the predecessor of the block is the header of a construct + // - the predecessor does not branch unconditionally to the block + // If this function returns true, the block must be split before the + // instruction for TransformationReplaceOpSelectWithConditionalBranch to be + // applicable. + // Assumes that the instruction is OpSelect. + bool InstructionNeedsSplitBefore(opt::Instruction* instruction); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp new file mode 100644 index 00000000..d7eddca1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceParameterWithGlobal::Apply() { + for (const auto& function : *GetIRContext()->module()) { + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) { + continue; + } + + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + // Make sure at least one parameter can be replaced. Also checks that the + // function has at least one parameter. + if (std::none_of(params.begin(), params.end(), + [this](const opt::Instruction* param) { + return TransformationReplaceParameterWithGlobal:: + IsParameterTypeSupported(GetIRContext(), + param->type_id()); + })) { + continue; + } + + // Select id of a parameter to replace. + const opt::Instruction* replaced_param; + uint32_t param_type_id; + do { + replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(¶ms); + param_type_id = replaced_param->type_id(); + assert(param_type_id && "Parameter has invalid type"); + } while ( + !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( + GetIRContext(), param_type_id)); + + assert(replaced_param && "Unable to find a parameter to replace"); + + // Make sure type id for the global variable exists in the module. + FindOrCreatePointerType(replaced_param->type_id(), + spv::StorageClass::Private); + + // Make sure initializer for the global variable exists in the module. + FindOrCreateZeroConstant(replaced_param->type_id(), false); + + ApplyTransformation(TransformationReplaceParameterWithGlobal( + GetFuzzerContext()->GetFreshId(), replaced_param->result_id(), + GetFuzzerContext()->GetFreshId())); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h new file mode 100644 index 00000000..4eb50866 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all non-entry-point functions in the module and randomly +// replaces a parameter with a global variable. +class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { + public: + FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp new file mode 100644 index 00000000..f029316e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_params_with_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassReplaceParamsWithStruct::Apply() { + for (const auto& function : *GetIRContext()->module()) { + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + if (params.empty() || fuzzerutil::FunctionIsEntryPoint( + GetIRContext(), function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) { + continue; + } + + std::vector parameter_index(params.size()); + std::iota(parameter_index.begin(), parameter_index.end(), 0); + + // Remove the indices of unsupported parameters. + auto new_end = + std::remove_if(parameter_index.begin(), parameter_index.end(), + [this, ¶ms](uint32_t index) { + return !TransformationReplaceParamsWithStruct:: + IsParameterTypeSupported(GetIRContext(), + params[index]->type_id()); + }); + + // std::remove_if changes the vector so that removed elements are placed at + // the end (i.e. [new_end, parameter_index.end()) is a range of removed + // elements). However, the size of the vector is not changed so we need to + // change that explicitly by popping those elements from the vector. + parameter_index.erase(new_end, parameter_index.end()); + + if (parameter_index.empty()) { + continue; + } + + // Select |num_replaced_params| parameters at random. We shuffle the vector + // of indices for randomization and shrink it to select first + // |num_replaced_params| parameters. + auto num_replaced_params = std::min( + parameter_index.size(), + GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct( + static_cast(params.size()))); + + GetFuzzerContext()->Shuffle(¶meter_index); + parameter_index.resize(num_replaced_params); + + // Make sure OpTypeStruct exists in the module. + std::vector component_type_ids; + for (auto index : parameter_index) { + component_type_ids.push_back(params[index]->type_id()); + } + + FindOrCreateStructType(component_type_ids); + + // Map parameters' indices to parameters' ids. + std::vector parameter_id; + for (auto index : parameter_index) { + parameter_id.push_back(params[index]->result_id()); + } + + std::map caller_id_to_fresh_id; + for (const auto* inst : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + caller_id_to_fresh_id[inst->result_id()] = + GetFuzzerContext()->GetFreshId(); + } + + ApplyTransformation(TransformationReplaceParamsWithStruct( + parameter_id, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h new file mode 100644 index 00000000..3af7367a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all functions in the module and randomly decides for each one +// whether to replace a subset of its parameters with a struct value. +class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { + public: + FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp new file mode 100644 index 00000000..5b4afcc5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_split_blocks.h" + +#include + +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassSplitBlocks::Apply() { + // Gather up pointers to all the blocks in the module. We are then able to + // iterate over these pointers and split the blocks to which they point; + // we cannot safely split blocks while we iterate through the module. + std::vector blocks; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + blocks.push_back(&block); + } + } + + // Now go through all the block pointers that were gathered. + for (auto& block : blocks) { + // Probabilistically decide whether to try to split this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfSplittingBlock())) { + // We are not going to try to split this block. + continue; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2964): consider + // taking a simpler approach to identifying the instruction before which + // to split a block. + + // We are going to try to split this block. We now need to choose where + // to split it. We describe the instruction before which we would like to + // split a block via an InstructionDescriptor, details of which are + // commented in the protobufs definition file. + std::vector instruction_descriptors; + + // The initial base instruction is the block label. + uint32_t base = block->id(); + + // Counts the number of times we have seen each opcode since we reset the + // base instruction. + std::map skip_count; + + // Consider every instruction in the block. The label is excluded: it is + // only necessary to consider it as a base in case the first instruction + // in the block does not have a result id. + for (auto& inst : *block) { + if (inst.HasResultId()) { + // In the case that the instruction has a result id, we use the + // instruction as its own base, and clear the skip counts we have + // collected. + base = inst.result_id(); + skip_count.clear(); + } + const spv::Op opcode = inst.opcode(); + instruction_descriptors.emplace_back(MakeInstructionDescriptor( + base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0)); + if (!inst.HasResultId()) { + skip_count[opcode] = + skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1; + } + } + // Having identified all the places we might be able to split the block, + // we choose one of them. + auto transformation = TransformationSplitBlock( + instruction_descriptors[GetFuzzerContext()->RandomIndex( + instruction_descriptors)], + GetFuzzerContext()->GetFreshId()); + // If the position we have chosen turns out to be a valid place to split + // the block, we apply the split. Otherwise the block just doesn't get + // split. + MaybeApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h new file mode 100644 index 00000000..b1b94240 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for splitting blocks in the module, to create more blocks; this +// can be very useful for giving other passes a chance to apply. +class FuzzerPassSplitBlocks : public FuzzerPass { + public: + FuzzerPassSplitBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp new file mode 100644 index 00000000..dce65f08 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassSwapCommutableOperands::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // commutative. In this case, the transformation is probabilistically applied. + context->module()->ForEachInst( + [this, context](opt::Instruction* instruction) { + if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) && + GetFuzzerContext()->ChooseEven()) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h new file mode 100644 index 00000000..13d8fb6e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all commutative instructions in the module, +// probabilistically choosing which of these instructions will have its input +// operands swapped. +class FuzzerPassSwapCommutableOperands : public FuzzerPass { + public: + FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp new file mode 100644 index 00000000..72c8358a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapBranchConditionalOperands:: + FuzzerPassSwapBranchConditionalOperands( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassSwapBranchConditionalOperands::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + const auto& inst = *inst_it; + + if (inst.opcode() != spv::Op::OpBranchConditional) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfSwappingConditionalBranchOperands())) { + return; + } + + ApplyTransformation(TransformationSwapConditionalBranchOperands( + instruction_descriptor, GetFuzzerContext()->GetFreshId())); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h new file mode 100644 index 00000000..7f71f9b1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides for each OpBranchConditional instruction in the module +// whether to swap its operands or not. +class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { + public: + FuzzerPassSwapBranchConditionalOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp new file mode 100644 index 00000000..8eeec850 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_functions.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/transformation_swap_two_functions.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapFunctions::FuzzerPassSwapFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassSwapFunctions::Apply() { + // Collect all function ids in a module. + std::vector function_ids; + for (auto& function : *GetIRContext()->module()) { + function_ids.emplace_back(function.result_id()); + } + + // Iterate through every combination of id i & j where i!=j. + for (size_t i = 0; i < function_ids.size(); ++i) { + for (size_t j = i + 1; j < function_ids.size(); ++j) { + // Perform function swap randomly. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfSwappingFunctions())) { + continue; + } + TransformationSwapTwoFunctions transformation(function_ids[i], + function_ids[j]); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h new file mode 100644 index 00000000..7af527f0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_swap_functions.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly swap functions within a module. +class FuzzerPassSwapFunctions : public FuzzerPass { + public: + FuzzerPassSwapFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp new file mode 100644 index 00000000..2a1ad6e5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassToggleAccessChainInstruction::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is + // probabilistically applied. + context->module()->ForEachInst([this, + context](opt::Instruction* instruction) { + spv::Op opcode = instruction->opcode(); + if ((opcode == spv::Op::OpAccessChain || + opcode == spv::Op::OpInBoundsAccessChain) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h new file mode 100644 index 00000000..f0b6166b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all access chain instructions in the module, +// probabilistically choosing which of these instructions will be toggled. +class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { + public: + FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp new file mode 100644 index 00000000..3a3b12c1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_split_block.h" +#include "source/fuzz/transformation_wrap_region_in_selection.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassWrapRegionsInSelections::Apply() { + for (auto& function : *GetIRContext()->module()) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingRegionInSelection())) { + continue; + } + + // It is easier to select an element at random from a vector than from an + // instruction list. + std::vector header_block_candidates; + for (auto& block : function) { + header_block_candidates.push_back(&block); + } + + if (header_block_candidates.empty()) { + continue; + } + + // Try to get a header block candidate that will increase the chances of the + // transformation being applicable. + auto* header_block_candidate = MaybeGetHeaderBlockCandidate( + header_block_candidates[GetFuzzerContext()->RandomIndex( + header_block_candidates)]); + if (!header_block_candidate) { + continue; + } + + std::vector merge_block_candidates; + for (auto& block : function) { + if (GetIRContext()->GetDominatorAnalysis(&function)->StrictlyDominates( + header_block_candidate, &block) && + GetIRContext() + ->GetPostDominatorAnalysis(&function) + ->StrictlyDominates(&block, header_block_candidate)) { + merge_block_candidates.push_back(&block); + } + } + + if (merge_block_candidates.empty()) { + continue; + } + + // Try to get a merge block candidate that will increase the chances of the + // transformation being applicable. + auto* merge_block_candidate = MaybeGetMergeBlockCandidate( + merge_block_candidates[GetFuzzerContext()->RandomIndex( + merge_block_candidates)]); + if (!merge_block_candidate) { + continue; + } + + if (!TransformationWrapRegionInSelection::IsApplicableToBlockRange( + GetIRContext(), header_block_candidate->id(), + merge_block_candidate->id())) { + continue; + } + + // This boolean constant will be used as a condition for the + // OpBranchConditional instruction. We mark it as irrelevant to be able to + // replace it with a more interesting value later. + auto branch_condition = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(branch_condition, true); + + ApplyTransformation(TransformationWrapRegionInSelection( + header_block_candidate->id(), merge_block_candidate->id(), + branch_condition)); + } +} + +opt::BasicBlock* +FuzzerPassWrapRegionsInSelections::MaybeGetHeaderBlockCandidate( + opt::BasicBlock* header_block_candidate) { + // Try to create a preheader if |header_block_candidate| is a loop header. + if (header_block_candidate->IsLoopHeader()) { + // GetOrCreateSimpleLoopPreheader only supports reachable blocks. + return GetIRContext()->cfg()->preds(header_block_candidate->id()).size() == + 1 + ? nullptr + : GetOrCreateSimpleLoopPreheader(header_block_candidate->id()); + } + + // Try to split |header_block_candidate| if it's already a header block. + if (header_block_candidate->GetMergeInst()) { + SplitBlockAfterOpPhiOrOpVariable(header_block_candidate->id()); + } + + return header_block_candidate; +} + +opt::BasicBlock* FuzzerPassWrapRegionsInSelections::MaybeGetMergeBlockCandidate( + opt::BasicBlock* merge_block_candidate) { + // If |merge_block_candidate| is a merge block of some construct, try to split + // it and return a newly created block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + merge_block_candidate->id())) { + // We can't split a merge block if it's also a loop header. + return merge_block_candidate->IsLoopHeader() + ? nullptr + : SplitBlockAfterOpPhiOrOpVariable(merge_block_candidate->id()); + } + + return merge_block_candidate; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h new file mode 100644 index 00000000..fc3d7df9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wraps a region of blocks in every function into a selection +// construct. +class FuzzerPassWrapRegionsInSelections : public FuzzerPass { + public: + FuzzerPassWrapRegionsInSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; + + private: + // Tries to adjust |header_block_candidate| such that + // TransformationWrapRegionInSelection has higher chances of being + // applied. In particular, tries to split |header_block_candidate| if it's + // already a header block of some other construct. + opt::BasicBlock* MaybeGetHeaderBlockCandidate( + opt::BasicBlock* header_block_candidate); + + // Tries to adjust |merge_block_candidate| such that + // TransformationWrapRegionInSelection has higher chances of being + // applied. In particular, tries to split |merge_block_candidate| if it's + // already a merge block of some other construct. + opt::BasicBlock* MaybeGetMergeBlockCandidate( + opt::BasicBlock* merge_block_candidate); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp new file mode 100644 index 00000000..55fbe2c2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations, ignore_inapplicable_transformations) {} + +void FuzzerPassWrapVectorSynonym::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + + // Randomly decide whether to wrap it to a vector operation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) { + return; + } + + // The transformation is not applicable if the instruction has missing + // result id, type id, or is not supported type. + if (!TransformationWrapVectorSynonym::IsInstructionSupported( + GetIRContext(), *instruction_iterator)) { + return; + } + + // It must be valid to insert an OpCompositeConstruct instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeConstruct, instruction_iterator)) { + return; + } + + // Get the scalar operands from the original instruction. + opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(0)); + opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef( + instruction_iterator->GetSingleWordInOperand(1)); + + // We need to be able to make a synonym of the scalar operation's result + // id, as well as the operand ids (for example, they cannot be + // irrelevant). + if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), + *GetTransformationContext(), + *instruction_iterator)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand1)) { + return; + } + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), *operand2)) { + return; + } + + // Get a random vector size from 2 to 4. + uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); + + // Randomly choose a position that target ids should be placed at. + // The position is in range [0, n - 1], where n is the size of the + // vector. + uint32_t position = + GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); + + // Stores the ids of scalar constants. + std::vector vec1_components; + std::vector vec2_components; + + // Populate components based on vector type and size. + for (uint32_t i = 0; i < vector_size; ++i) { + if (i == position) { + vec1_components.emplace_back(operand1->result_id()); + vec2_components.emplace_back(operand2->result_id()); + } else { + vec1_components.emplace_back( + FindOrCreateZeroConstant(operand1->type_id(), true)); + vec2_components.emplace_back( + FindOrCreateZeroConstant(operand2->type_id(), true)); + } + } + + // Add two OpCompositeConstruct to the module with result id returned. + // The added vectors may have different types, for instance if the + // scalar instruction operates on integers with differing sign. + + // Add the first OpCompositeConstruct that wraps the id of the first + // operand. + uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand1->type_id(), vector_size), + vec1_components, instruction_descriptor, result_id1)); + + // Add the second OpCompositeConstruct that wraps the id of the second + // operand. + uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + FindOrCreateVectorType(operand2->type_id(), vector_size), + vec2_components, instruction_descriptor, result_id2)); + + // The result of the vector instruction that + // TransformationWrapVectorSynonym will create should be a vector of the + // right size, with the scalar instruction's result type as its element + // type. This can be distinct from the types of the operands, if the + // scalar instruction adds two signed integers and stores the result in + // an unsigned id, for example. A transformation is applied to add the + // right type to the module. + FindOrCreateVectorType(instruction_iterator->type_id(), vector_size); + + // Apply transformation to do vector operation and add synonym between + // the result vector id and the id of the original instruction. + ApplyTransformation(TransformationWrapVectorSynonym( + instruction_iterator->result_id(), result_id1, result_id2, + GetFuzzerContext()->GetFreshId(), position)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h new file mode 100644 index 00000000..51458383 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_pass_wrap_vector_synonym.h @@ -0,0 +1,38 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wrap a scalar operation into a vector operation. +class FuzzerPassWrapVectorSynonym : public FuzzerPass { + public: + FuzzerPassWrapVectorSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + bool ignore_inapplicable_transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.cpp new file mode 100644 index 00000000..e85ff2fb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -0,0 +1,2119 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_util.h" + +#include +#include + +#include "source/opt/build_module.h" + +namespace spvtools { +namespace fuzz { + +namespace fuzzerutil { +namespace { + +// A utility class that uses RAII to change and restore the terminator +// instruction of the |block|. +class ChangeTerminatorRAII { + public: + explicit ChangeTerminatorRAII(opt::BasicBlock* block, + opt::Instruction new_terminator) + : block_(block), old_terminator_(std::move(*block->terminator())) { + *block_->terminator() = std::move(new_terminator); + } + + ~ChangeTerminatorRAII() { + *block_->terminator() = std::move(old_terminator_); + } + + private: + opt::BasicBlock* block_; + opt::Instruction old_terminator_; +}; + +uint32_t MaybeGetOpConstant(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, + uint32_t type_id, bool is_irrelevant) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == spv::Op::OpConstant && inst.type_id() == type_id && + inst.GetInOperand(0).words == words && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } + + return 0; +} + +} // namespace + +const spvtools::MessageConsumer kSilentMessageConsumer = + [](spv_message_level_t, const char*, const spv_position_t&, + const char*) -> void {}; + +bool BuildIRContext(spv_target_env target_env, + const spvtools::MessageConsumer& message_consumer, + const std::vector& binary_in, + spv_validator_options validator_options, + std::unique_ptr* ir_context) { + SpirvTools tools(target_env); + tools.SetMessageConsumer(message_consumer); + if (!tools.IsValid()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return false; + } + + // Initial binary should be valid. + if (!tools.Validate(binary_in.data(), binary_in.size(), validator_options)) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return false; + } + + // Build the module from the input binary. + auto result = BuildModule(target_env, message_consumer, binary_in.data(), + binary_in.size()); + assert(result && "IRContext must be valid"); + *ir_context = std::move(result); + return true; +} + +bool IsFreshId(opt::IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->GetDef(id); +} + +void UpdateModuleIdBound(opt::IRContext* context, uint32_t id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the + // case where the maximum id bound is reached. + context->module()->SetIdBound( + std::max(context->module()->id_bound(), id + 1)); +} + +opt::BasicBlock* MaybeFindBlock(opt::IRContext* context, + uint32_t maybe_block_id) { + auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id); + if (inst == nullptr) { + // No instruction defining this id was found. + return nullptr; + } + if (inst->opcode() != spv::Op::OpLabel) { + // The instruction defining the id is not a label, so it cannot be a block + // id. + return nullptr; + } + return context->cfg()->block(maybe_block_id); +} + +bool PhiIdsOkForNewEdge( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + const google::protobuf::RepeatedField& phi_ids) { + if (bb_from->IsSuccessor(bb_to)) { + // There is already an edge from |from_block| to |to_block|, so there is + // no need to extend OpPhi instructions. Do not allow phi ids to be + // present. This might turn out to be too strict; perhaps it would be OK + // just to ignore the ids in this case. + return phi_ids.empty(); + } + // The edge would add a previously non-existent edge from |from_block| to + // |to_block|, so we go through the given phi ids and check that they exactly + // match the OpPhi instructions in |to_block|. + uint32_t phi_index = 0; + // An explicit loop, rather than applying a lambda to each OpPhi in |bb_to|, + // makes sense here because we need to increment |phi_index| for each OpPhi + // instruction. + for (auto& inst : *bb_to) { + if (inst.opcode() != spv::Op::OpPhi) { + // The OpPhi instructions all occur at the start of the block; if we find + // a non-OpPhi then we have seen them all. + break; + } + if (phi_index == static_cast(phi_ids.size())) { + // Not enough phi ids have been provided to account for the OpPhi + // instructions. + return false; + } + // Look for an instruction defining the next phi id. + opt::Instruction* phi_extension = + context->get_def_use_mgr()->GetDef(phi_ids[phi_index]); + if (!phi_extension) { + // The id given to extend this OpPhi does not exist. + return false; + } + if (phi_extension->type_id() != inst.type_id()) { + // The instruction given to extend this OpPhi either does not have a type + // or its type does not match that of the OpPhi. + return false; + } + + if (context->get_instr_block(phi_extension)) { + // The instruction defining the phi id has an associated block (i.e., it + // is not a global value). Check whether its definition dominates the + // exit of |from_block|. + auto dominator_analysis = + context->GetDominatorAnalysis(bb_from->GetParent()); + if (!dominator_analysis->Dominates(phi_extension, + bb_from->terminator())) { + // The given id is no good as its definition does not dominate the exit + // of |from_block| + return false; + } + } + phi_index++; + } + // We allow some of the ids provided for extending OpPhi instructions to be + // unused. Their presence does no harm, and requiring a perfect match may + // make transformations less likely to cleanly apply. + return true; +} + +opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context, + uint32_t bb_from_id, + uint32_t bb_to_id, + uint32_t bool_id) { + const auto* bb_from = MaybeFindBlock(ir_context, bb_from_id); + assert(bb_from && "|bb_from_id| is invalid"); + assert(MaybeFindBlock(ir_context, bb_to_id) && "|bb_to_id| is invalid"); + assert(bb_from->terminator()->opcode() == spv::Op::OpBranch && + "Precondition on terminator of bb_from is not satisfied"); + + // Get the id of the boolean constant to be used as the condition. + auto condition_inst = ir_context->get_def_use_mgr()->GetDef(bool_id); + assert(condition_inst && + (condition_inst->opcode() == spv::Op::OpConstantTrue || + condition_inst->opcode() == spv::Op::OpConstantFalse) && + "|bool_id| is invalid"); + + auto condition_value = condition_inst->opcode() == spv::Op::OpConstantTrue; + auto successor_id = bb_from->terminator()->GetSingleWordInOperand(0); + + // Add the dead branch, by turning OpBranch into OpBranchConditional, and + // ordering the targets depending on whether the given boolean corresponds to + // true or false. + return opt::Instruction( + ir_context, spv::Op::OpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {bool_id}}, + {SPV_OPERAND_TYPE_ID, {condition_value ? successor_id : bb_to_id}}, + {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to_id : successor_id}}}); +} + +void AddUnreachableEdgeAndUpdateOpPhis( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + uint32_t bool_id, + const google::protobuf::RepeatedField& phi_ids) { + assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) && + "Precondition on phi_ids is not satisfied"); + + const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to); + *bb_from->terminator() = CreateUnreachableEdgeInstruction( + context, bb_from->id(), bb_to->id(), bool_id); + + // Update OpPhi instructions in the target block if this branch adds a + // previously non-existent edge from source to target. + if (!from_to_edge_already_exists) { + uint32_t phi_index = 0; + for (auto& inst : *bb_to) { + if (inst.opcode() != spv::Op::OpPhi) { + break; + } + assert(phi_index < static_cast(phi_ids.size()) && + "There should be at least one phi id per OpPhi instruction."); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}}); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}}); + phi_index++; + } + } +} + +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id) { + auto block = context->cfg()->block(block_id); + auto loop_header = context->cfg()->block(loop_header_id); + + // |block| and |loop_header| must be defined, |loop_header| must be in fact + // loop header and |block| must branch to it. + if (!(block && loop_header && loop_header->IsLoopHeader() && + block->IsSuccessor(loop_header))) { + return false; + } + + // |block| must be reachable and be dominated by |loop_header|. + opt::DominatorAnalysis* dominator_analysis = + context->GetDominatorAnalysis(loop_header->GetParent()); + return context->IsReachable(*block) && + dominator_analysis->Dominates(loop_header, block); +} + +bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, + uint32_t maybe_loop_header_id) { + // We deem a block to be part of a loop's continue construct if the loop's + // continue target dominates the block. + auto containing_construct_block = context->cfg()->block(maybe_loop_header_id); + if (containing_construct_block->IsLoopHeader()) { + auto continue_target = containing_construct_block->ContinueBlockId(); + if (context->GetDominatorAnalysis(containing_construct_block->GetParent()) + ->Dominates(continue_target, block_id)) { + return true; + } + } + return false; +} + +opt::BasicBlock::iterator GetIteratorForInstruction( + opt::BasicBlock* block, const opt::Instruction* inst) { + for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) { + if (inst == &*inst_it) { + return inst_it; + } + } + return block->end(); +} + +bool CanInsertOpcodeBeforeInstruction( + spv::Op opcode, const opt::BasicBlock::iterator& instruction_in_block) { + if (instruction_in_block->PreviousNode() && + (instruction_in_block->PreviousNode()->opcode() == spv::Op::OpLoopMerge || + instruction_in_block->PreviousNode()->opcode() == + spv::Op::OpSelectionMerge)) { + // We cannot insert directly after a merge instruction. + return false; + } + if (opcode != spv::Op::OpVariable && + instruction_in_block->opcode() == spv::Op::OpVariable) { + // We cannot insert a non-OpVariable instruction directly before a + // variable; variables in a function must be contiguous in the entry block. + return false; + } + // We cannot insert a non-OpPhi instruction directly before an OpPhi, because + // OpPhi instructions need to be contiguous at the start of a block. + return opcode == spv::Op::OpPhi || + instruction_in_block->opcode() != spv::Op::OpPhi; +} + +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::Instruction& inst) { + if (inst.opcode() == spv::Op::OpSampledImage) { + // The SPIR-V data rules say that only very specific instructions may + // may consume the result id of an OpSampledImage, and this excludes the + // instructions that are used for making synonyms. + return false; + } + if (!inst.HasResultId()) { + // We can only make a synonym of an instruction that generates an id. + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id())) { + // An irrelevant id can't be a synonym of anything. + return false; + } + if (!inst.type_id()) { + // We can only make a synonym of an instruction that has a type. + return false; + } + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst.type_id()); + if (type_inst->opcode() == spv::Op::OpTypeVoid) { + // We only make synonyms of instructions that define objects, and an object + // cannot have void type. + return false; + } + if (type_inst->opcode() == spv::Op::OpTypePointer) { + switch (inst.opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + // We disallow making synonyms of null or undefined pointers. This is + // to provide the property that if the original shader exhibited no bad + // pointer accesses, the transformed shader will not either. + return false; + default: + break; + } + } + + // We do not make synonyms of objects that have decorations: if the synonym is + // not decorated analogously, using the original object vs. its synonymous + // form may not be equivalent. + return ir_context->get_decoration_mgr() + ->GetDecorationsFor(inst.result_id(), true) + .empty(); +} + +bool IsCompositeType(const opt::analysis::Type* type) { + return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() || + type->AsVector()); +} + +std::vector RepeatedFieldToVector( + const google::protobuf::RepeatedField& repeated_field) { + std::vector result; + for (auto i : repeated_field) { + result.push_back(i); + } + return result; +} + +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index) { + auto should_be_composite_type = + context->get_def_use_mgr()->GetDef(base_object_type_id); + assert(should_be_composite_type && "The type should exist."); + switch (should_be_composite_type->opcode()) { + case spv::Op::OpTypeArray: { + auto array_length = GetArraySize(*should_be_composite_type, context); + if (array_length == 0 || index >= array_length) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: { + auto count = should_be_composite_type->GetSingleWordInOperand(1); + if (index >= count) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case spv::Op::OpTypeStruct: { + if (index >= GetNumberOfStructMembers(*should_be_composite_type)) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(index); + } + default: + return 0; + } +} + +uint32_t WalkCompositeTypeIndices( + opt::IRContext* context, uint32_t base_object_type_id, + const google::protobuf::RepeatedField& indices) { + uint32_t sub_object_type_id = base_object_type_id; + for (auto index : indices) { + sub_object_type_id = + WalkOneCompositeTypeIndex(context, sub_object_type_id, index); + if (!sub_object_type_id) { + return 0; + } + } + return sub_object_type_id; +} + +uint32_t GetNumberOfStructMembers( + const opt::Instruction& struct_type_instruction) { + assert(struct_type_instruction.opcode() == spv::Op::OpTypeStruct && + "An OpTypeStruct instruction is required here."); + return struct_type_instruction.NumInOperands(); +} + +uint32_t GetArraySize(const opt::Instruction& array_type_instruction, + opt::IRContext* context) { + auto array_length_constant = + context->get_constant_mgr() + ->GetConstantFromInst(context->get_def_use_mgr()->GetDef( + array_type_instruction.GetSingleWordInOperand(1))) + ->AsIntConstant(); + if (array_length_constant->words().size() != 1) { + return 0; + } + return array_length_constant->GetU32(); +} + +uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context) { + switch (composite_type_inst.opcode()) { + case spv::Op::OpTypeArray: + return fuzzerutil::GetArraySize(composite_type_inst, ir_context); + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + return composite_type_inst.GetSingleWordInOperand(1); + case spv::Op::OpTypeStruct: { + return fuzzerutil::GetNumberOfStructMembers(composite_type_inst); + } + case spv::Op::OpTypeRuntimeArray: + assert(false && + "GetBoundForCompositeIndex should not be invoked with an " + "OpTypeRuntimeArray, which does not have a static bound."); + return 0; + default: + assert(false && "Unknown composite type."); + return 0; + } +} + +spv::MemorySemanticsMask GetMemorySemanticsForStorageClass( + spv::StorageClass storage_class) { + switch (storage_class) { + case spv::StorageClass::Workgroup: + return spv::MemorySemanticsMask::WorkgroupMemory; + + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + return spv::MemorySemanticsMask::UniformMemory; + + case spv::StorageClass::CrossWorkgroup: + return spv::MemorySemanticsMask::CrossWorkgroupMemory; + + case spv::StorageClass::AtomicCounter: + return spv::MemorySemanticsMask::AtomicCounterMemory; + + case spv::StorageClass::Image: + return spv::MemorySemanticsMask::ImageMemory; + + default: + return spv::MemorySemanticsMask::MaskNone; + } +} + +bool IsValid(const opt::IRContext* context, + spv_validator_options validator_options, + MessageConsumer consumer) { + std::vector binary; + context->module()->ToBinary(&binary, false); + SpirvTools tools(context->grammar().target_env()); + tools.SetMessageConsumer(std::move(consumer)); + return tools.Validate(binary.data(), binary.size(), validator_options); +} + +bool IsValidAndWellFormed(const opt::IRContext* ir_context, + spv_validator_options validator_options, + MessageConsumer consumer) { + if (!IsValid(ir_context, validator_options, consumer)) { + // Expression to dump |ir_context| to /data/temp/shader.spv: + // DumpShader(ir_context, "/data/temp/shader.spv") + consumer(SPV_MSG_INFO, nullptr, {}, + "Module is invalid (set a breakpoint to inspect)."); + return false; + } + // Check that all blocks in the module have appropriate parent functions. + for (auto& function : *ir_context->module()) { + for (auto& block : function) { + if (block.GetParent() == nullptr) { + std::stringstream ss; + ss << "Block " << block.id() << " has no parent; its parent should be " + << function.result_id() << " (set a breakpoint to inspect)."; + consumer(SPV_MSG_INFO, nullptr, {}, ss.str().c_str()); + return false; + } + if (block.GetParent() != &function) { + std::stringstream ss; + ss << "Block " << block.id() << " should have parent " + << function.result_id() << " but instead has parent " + << block.GetParent() << " (set a breakpoint to inspect)."; + consumer(SPV_MSG_INFO, nullptr, {}, ss.str().c_str()); + return false; + } + } + } + + // Check that all instructions have distinct unique ids. We map each unique + // id to the first instruction it is observed to be associated with so that + // if we encounter a duplicate we have access to the previous instruction - + // this is a useful aid to debugging. + std::unordered_map unique_ids; + bool found_duplicate = false; + ir_context->module()->ForEachInst([&consumer, &found_duplicate, ir_context, + &unique_ids](opt::Instruction* inst) { + (void)ir_context; // Only used in an assertion; keep release-mode compilers + // happy. + assert(inst->context() == ir_context && + "Instruction has wrong IR context."); + if (unique_ids.count(inst->unique_id()) != 0) { + consumer(SPV_MSG_INFO, nullptr, {}, + "Two instructions have the same unique id (set a breakpoint to " + "inspect)."); + found_duplicate = true; + } + unique_ids.insert({inst->unique_id(), inst}); + }); + return !found_duplicate; +} + +std::unique_ptr CloneIRContext(opt::IRContext* context) { + std::vector binary; + context->module()->ToBinary(&binary, false); + return BuildModule(context->grammar().target_env(), nullptr, binary.data(), + binary.size()); +} + +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) { + auto type = ir_context->get_type_mgr()->GetType(id); + return type && !type->AsFunction(); +} + +bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) { + bool result = false; + ir_context->get_def_use_mgr()->WhileEachUse( + block_id, + [&result](const opt::Instruction* use_instruction, + uint32_t /*unused*/) -> bool { + switch (use_instruction->opcode()) { + case spv::Op::OpLoopMerge: + case spv::Op::OpSelectionMerge: + result = true; + return false; + default: + return true; + } + }); + return result; +} + +uint32_t GetLoopFromMergeBlock(opt::IRContext* ir_context, + uint32_t merge_block_id) { + uint32_t result = 0; + ir_context->get_def_use_mgr()->WhileEachUse( + merge_block_id, + [ir_context, &result](opt::Instruction* use_instruction, + uint32_t use_index) -> bool { + switch (use_instruction->opcode()) { + case spv::Op::OpLoopMerge: + // The merge block operand is the first operand in OpLoopMerge. + if (use_index == 0) { + result = ir_context->get_instr_block(use_instruction)->id(); + return false; + } + return true; + default: + return true; + } + }); + return result; +} + +uint32_t FindFunctionType(opt::IRContext* ir_context, + const std::vector& type_ids) { + // Look through the existing types for a match. + for (auto& type_or_value : ir_context->types_values()) { + if (type_or_value.opcode() != spv::Op::OpTypeFunction) { + // We are only interested in function types. + continue; + } + if (type_or_value.NumInOperands() != type_ids.size()) { + // Not a match: different numbers of arguments. + continue; + } + // Check whether the return type and argument types match. + bool input_operands_match = true; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) { + input_operands_match = false; + break; + } + } + if (input_operands_match) { + // Everything matches. + return type_or_value.result_id(); + } + } + // No match was found. + return 0; +} + +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function) { + uint32_t type_id = function->DefInst().GetSingleWordInOperand(1); + return context->get_def_use_mgr()->GetDef(type_id); +} + +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { + for (auto& function : *ir_context->module()) { + if (function.result_id() == function_id) { + return &function; + } + } + return nullptr; +} + +bool FunctionContainsOpKillOrUnreachable(const opt::Function& function) { + for (auto& block : function) { + if (block.terminator()->opcode() == spv::Op::OpKill || + block.terminator()->opcode() == spv::Op::OpUnreachable) { + return true; + } + } + return false; +} + +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) { + for (auto& entry_point : context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(1) == function_id) { + return true; + } + } + return false; +} + +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id) { + assert(context->get_instr_block(use_instruction) && + "|use_instruction| must be in a basic block"); + + auto defining_instruction = context->get_def_use_mgr()->GetDef(id); + auto enclosing_function = + context->get_instr_block(use_instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the use. + if (defining_instruction->opcode() == spv::Op::OpFunctionParameter) { + return InstructionIsFunctionParameter(defining_instruction, + enclosing_function); + } + if (!context->get_instr_block(id)) { + // The id must be at global scope. + return true; + } + if (defining_instruction == use_instruction) { + // It is not OK for a definition to use itself. + return false; + } + if (!context->IsReachable(*context->get_instr_block(use_instruction)) || + !context->IsReachable(*context->get_instr_block(id))) { + // Skip unreachable blocks. + return false; + } + auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); + if (use_instruction->opcode() == spv::Op::OpPhi) { + // In the case where the use is an operand to OpPhi, it is actually the + // *parent* block associated with the operand that must be dominated by + // the synonym. + auto parent_block = + use_instruction->GetSingleWordInOperand(use_input_operand_index + 1); + return dominator_analysis->Dominates( + context->get_instr_block(defining_instruction)->id(), parent_block); + } + return dominator_analysis->Dominates(defining_instruction, use_instruction); +} + +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, + uint32_t id) { + assert(context->get_instr_block(instruction) && + "|instruction| must be in a basic block"); + + auto id_definition = context->get_def_use_mgr()->GetDef(id); + auto function_enclosing_instruction = + context->get_instr_block(instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the instruction. + if (id_definition->opcode() == spv::Op::OpFunctionParameter) { + return InstructionIsFunctionParameter(id_definition, + function_enclosing_instruction); + } + if (!context->get_instr_block(id)) { + // The id is at global scope. + return true; + } + if (id_definition == instruction) { + // The instruction is not available right before its own definition. + return false; + } + const auto* dominator_analysis = + context->GetDominatorAnalysis(function_enclosing_instruction); + if (context->IsReachable(*context->get_instr_block(instruction)) && + context->IsReachable(*context->get_instr_block(id)) && + dominator_analysis->Dominates(id_definition, instruction)) { + // The id's definition dominates the instruction, and both the definition + // and the instruction are in reachable blocks, thus the id is available at + // the instruction. + return true; + } + if (id_definition->opcode() == spv::Op::OpVariable && + function_enclosing_instruction == + context->get_instr_block(id)->GetParent()) { + assert(!context->IsReachable(*context->get_instr_block(instruction)) && + "If the instruction were in a reachable block we should already " + "have returned true."); + // The id is a variable and it is in the same function as |instruction|. + // This is OK despite |instruction| being unreachable. + return true; + } + return false; +} + +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function) { + if (instruction->opcode() != spv::Op::OpFunctionParameter) { + return false; + } + bool found_parameter = false; + function->ForEachParam( + [instruction, &found_parameter](opt::Instruction* param) { + if (param == instruction) { + found_parameter = true; + } + }); + return found_parameter; +} + +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) { + const auto* inst = context->get_def_use_mgr()->GetDef(result_id); + assert(inst && "|result_id| is invalid"); + return inst->type_id(); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && + pointer_type_inst->opcode() == spv::Op::OpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return pointer_type_inst->GetSingleWordInOperand(1); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetPointeeTypeIdFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + +spv::StorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && + pointer_type_inst->opcode() == spv::Op::OpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return static_cast( + pointer_type_inst->GetSingleWordInOperand(0)); +} + +spv::StorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetStorageClassFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + spv::StorageClass storage_class) { + for (auto& inst : context->types_values()) { + switch (inst.opcode()) { + case spv::Op::OpTypePointer: + if (spv::StorageClass(inst.GetSingleWordInOperand(0)) == + storage_class && + inst.GetSingleWordInOperand(1) == pointee_type_id) { + return inst.result_id(); + } + break; + default: + break; + } + } + return 0; +} + +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index) { + // Subtract the number of non-input operands from the index + return absolute_index - inst.NumOperands() + inst.NumInOperands(); +} + +bool IsNullConstantSupported(opt::IRContext* ir_context, + const opt::Instruction& type_inst) { + switch (type_inst.opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeDeviceEvent: + case spv::Op::OpTypeEvent: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeQueue: + case spv::Op::OpTypeReserveId: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeStruct: + return true; + case spv::Op::OpTypePointer: + // Null pointers are allowed if the VariablePointers capability is + // enabled, or if the VariablePointersStorageBuffer capability is enabled + // and the pointer type has StorageBuffer as its storage class. + if (ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointers)) { + return true; + } + if (ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointersStorageBuffer)) { + return spv::StorageClass(type_inst.GetSingleWordInOperand(0)) == + spv::StorageClass::StorageBuffer; + } + return false; + default: + return false; + } +} + +bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( + const opt::IRContext* ir_context) { + // TODO(afd): We capture the environments for which this requirement holds. + // The check should be refined on demand for other target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + return false; + default: + return true; + } +} + +void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) { + if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) { + // Conservatively add this global to the interface of every entry point in + // the module. This means that the global is available for other + // transformations to use. + // + // A downside of this is that the global will be in the interface even if it + // ends up never being used. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit + // this if a more thorough approach to entry point interfaces is taken. + for (auto& entry_point : context->module()->entry_points()) { + entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}}); + } + } +} + +opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, + spv::StorageClass storage_class, + uint32_t initializer_id) { + // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + + assert((storage_class == spv::StorageClass::Private || + storage_class == spv::StorageClass::Workgroup) && + "Variable's storage class must be either Private or Workgroup"); + + auto* type_inst = context->get_def_use_mgr()->GetDef(type_id); + (void)type_inst; // Variable becomes unused in release mode. + assert(type_inst && type_inst->opcode() == spv::Op::OpTypePointer && + GetStorageClassFromPointerType(type_inst) == storage_class && + "Variable's type is invalid"); + + if (storage_class == spv::StorageClass::Workgroup) { + assert(initializer_id == 0); + } + + if (initializer_id != 0) { + const auto* constant_inst = + context->get_def_use_mgr()->GetDef(initializer_id); + (void)constant_inst; // Variable becomes unused in release mode. + assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) && + GetPointeeTypeIdFromPointerType(type_inst) == + constant_inst->type_id() && + "Initializer is invalid"); + } + + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast(storage_class)}}}; + + if (initializer_id) { + operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}}); + } + + auto new_instruction = MakeUnique( + context, spv::Op::OpVariable, type_id, result_id, std::move(operands)); + auto result = new_instruction.get(); + context->module()->AddGlobalValue(std::move(new_instruction)); + + AddVariableIdToEntryPointInterfaces(context, result_id); + UpdateModuleIdBound(context, result_id); + + return result; +} + +opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id) { + // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + + auto* type_inst = context->get_def_use_mgr()->GetDef(type_id); + (void)type_inst; // Variable becomes unused in release mode. + assert(type_inst && type_inst->opcode() == spv::Op::OpTypePointer && + GetStorageClassFromPointerType(type_inst) == + spv::StorageClass::Function && + "Variable's type is invalid"); + + const auto* constant_inst = + context->get_def_use_mgr()->GetDef(initializer_id); + (void)constant_inst; // Variable becomes unused in release mode. + assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) && + GetPointeeTypeIdFromPointerType(type_inst) == + constant_inst->type_id() && + "Initializer is invalid"); + + auto* function = FindFunction(context, function_id); + assert(function && "Function id is invalid"); + + auto new_instruction = MakeUnique( + context, spv::Op::OpVariable, type_id, result_id, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}, + {SPV_OPERAND_TYPE_ID, {initializer_id}}}); + auto result = new_instruction.get(); + function->begin()->begin()->InsertBefore(std::move(new_instruction)); + + UpdateModuleIdBound(context, result_id); + + return result; +} + +bool HasDuplicates(const std::vector& arr) { + return std::unordered_set(arr.begin(), arr.end()).size() != + arr.size(); +} + +bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, + uint32_t hi) { + if (arr.empty()) { + return lo > hi; + } + + if (HasDuplicates(arr)) { + return false; + } + + auto min_max = std::minmax_element(arr.begin(), arr.end()); + return arr.size() == hi - lo + 1 && *min_max.first == lo && + *min_max.second == hi; +} + +std::vector GetParameters(opt::IRContext* ir_context, + uint32_t function_id) { + auto* function = FindFunction(ir_context, function_id); + assert(function && "|function_id| is invalid"); + + std::vector result; + function->ForEachParam( + [&result](opt::Instruction* inst) { result.push_back(inst); }); + + return result; +} + +void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id) { + auto* function = GetFunctionFromParameterId(ir_context, parameter_id); + assert(function && "|parameter_id| is invalid"); + assert(!FunctionIsEntryPoint(ir_context, function->result_id()) && + "Can't remove parameter from an entry point function"); + + function->RemoveParameter(parameter_id); + + // We've just removed parameters from the function and cleared their memory. + // Make sure analyses have no dangling pointers. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id) { + assert(FindFunction(ir_context, function_id) && + "|function_id| is not a result id of a function"); + + std::vector result; + ir_context->get_def_use_mgr()->ForEachUser( + function_id, [&result, function_id](opt::Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall && + inst->GetSingleWordInOperand(0) == function_id) { + result.push_back(inst); + } + }); + + return result; +} + +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id) { + auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id); + assert(param_inst && "Parameter id is invalid"); + + for (auto& function : *ir_context->module()) { + if (InstructionIsFunctionParameter(param_inst, &function)) { + return &function; + } + } + + return nullptr; +} + +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids) { + // Check some initial constraints. + assert(ir_context->get_type_mgr()->GetType(return_type_id) && + "Return type is invalid"); + for (auto id : parameter_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(id); + (void)type; // Make compilers happy in release mode. + // Parameters can't be OpTypeVoid. + assert(type && !type->AsVoid() && "Parameter has invalid type"); + } + + auto* function = FindFunction(ir_context, function_id); + assert(function && "|function_id| is invalid"); + + auto* old_function_type = GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + std::vector operand_ids = {return_type_id}; + operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(), + parameter_type_ids.end()); + + // A trivial case - we change nothing. + if (FindFunctionType(ir_context, operand_ids) == + old_function_type->result_id()) { + return old_function_type->result_id(); + } + + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 && + FindFunctionType(ir_context, operand_ids) == 0) { + // We can change |old_function_type| only if it's used once in the module + // and we are certain we won't create a duplicate as a result of the change. + + // Update |old_function_type| in-place. + opt::Instruction::OperandList operands; + for (auto id : operand_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + old_function_type->SetInOperands(std::move(operands)); + + // |operands| may depend on result ids defined below the |old_function_type| + // in the module. + old_function_type->RemoveFromList(); + ir_context->AddType(std::unique_ptr(old_function_type)); + return old_function_type->result_id(); + } else { + // We can't modify the |old_function_type| so we have to either use an + // existing one or create a new one. + auto type_id = FindOrCreateFunctionType( + ir_context, new_function_type_result_id, operand_ids); + assert(type_id != old_function_type->result_id() && + "We should've handled this case above"); + + function->DefInst().SetInOperand(1, {type_id}); + + // DefUseManager hasn't been updated yet, so if the following condition is + // true, then |old_function_type| will have no users when this function + // returns. We might as well remove it. + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) { + ir_context->KillInst(old_function_type); + } + + return type_id; + } +} + +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids) { + assert(result_id != 0 && "Result id can't be 0"); + assert(!type_ids.empty() && + "OpTypeFunction always has at least one operand - function's return " + "type"); + assert(IsNonFunctionTypeId(ir_context, type_ids[0]) && + "Return type must not be a function"); + + for (size_t i = 1; i < type_ids.size(); ++i) { + const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]); + (void)param_type; // Make compiler happy in release mode. + assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() && + "Function parameter can't have a function or void type"); + } + + opt::Instruction::OperandList operands; + operands.reserve(type_ids.size()); + for (auto id : type_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, spv::Op::OpTypeFunction, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids) { + if (auto existing_id = FindFunctionType(ir_context, type_ids)) { + return existing_id; + } + AddFunctionType(ir_context, result_id, type_ids); + return result_id; +} + +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed) { + opt::analysis::Integer type(width, is_signed); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) { + opt::analysis::Float type(width); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetBoolType(opt::IRContext* ir_context) { + opt::analysis::Bool type; + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, + uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Vector type(component_type, element_count); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids) { + for (auto& type_or_value : ir_context->types_values()) { + if (type_or_value.opcode() != spv::Op::OpTypeStruct || + type_or_value.NumInOperands() != + static_cast(component_type_ids.size())) { + continue; + } + bool all_components_match = true; + for (uint32_t i = 0; i < component_type_ids.size(); i++) { + if (type_or_value.GetSingleWordInOperand(i) != component_type_ids[i]) { + all_components_match = false; + break; + } + } + if (all_components_match) { + return type_or_value.result_id(); + } + } + return 0; +} + +uint32_t MaybeGetVoidType(opt::IRContext* ir_context) { + opt::analysis::Void type; + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { + const auto* type_inst = + ir_context->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); + assert(type_inst && "|scalar_or_composite_type_id| is invalid"); + + switch (type_inst->opcode()) { + case spv::Op::OpTypeBool: + return MaybeGetBoolConstant(ir_context, transformation_context, false, + is_irrelevant); + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: { + const auto width = type_inst->GetSingleWordInOperand(0); + std::vector words = {0}; + if (width > 32) { + words.push_back(0); + } + + return MaybeGetScalarConstant(ir_context, transformation_context, words, + scalar_or_composite_type_id, is_irrelevant); + } + case spv::Op::OpTypeStruct: { + std::vector component_ids; + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + const auto component_type_id = type_inst->GetSingleWordInOperand(i); + + auto component_id = + MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + component_ids.push_back(component_id); + } + + return MaybeGetCompositeConstant( + ir_context, transformation_context, component_ids, + scalar_or_composite_type_id, is_irrelevant); + } + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: { + const auto component_type_id = type_inst->GetSingleWordInOperand(0); + + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + const auto component_count = type_inst->GetSingleWordInOperand(1); + return MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(component_count, component_id), + scalar_or_composite_type_id, is_irrelevant); + } + case spv::Op::OpTypeArray: { + const auto component_type_id = type_inst->GetSingleWordInOperand(0); + + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + return MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(GetArraySize(*type_inst, ir_context), + component_id), + scalar_or_composite_type_id, is_irrelevant); + } + default: + assert(false && "Type is not supported"); + return 0; + } +} + +bool CanCreateConstant(opt::IRContext* ir_context, uint32_t type_id) { + opt::Instruction* type_instr = ir_context->get_def_use_mgr()->GetDef(type_id); + assert(type_instr != nullptr && "The type must exist."); + assert(spvOpcodeGeneratesType(type_instr->opcode()) && + "A type-generating opcode was expected."); + switch (type_instr->opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + return true; + case spv::Op::OpTypeArray: + return CanCreateConstant(ir_context, + type_instr->GetSingleWordInOperand(0)); + case spv::Op::OpTypeStruct: + if (HasBlockOrBufferBlockDecoration(ir_context, type_id)) { + return false; + } + for (uint32_t index = 0; index < type_instr->NumInOperands(); index++) { + if (!CanCreateConstant(ir_context, + type_instr->GetSingleWordInOperand(index))) { + return false; + } + } + return true; + default: + return false; + } +} + +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant) { + const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id); + assert(type && "|scalar_type_id| is invalid"); + + if (const auto* int_type = type->AsInteger()) { + return MaybeGetIntegerConstant(ir_context, transformation_context, words, + int_type->width(), int_type->IsSigned(), + is_irrelevant); + } else if (const auto* float_type = type->AsFloat()) { + return MaybeGetFloatConstant(ir_context, transformation_context, words, + float_type->width(), is_irrelevant); + } else { + assert(type->AsBool() && words.size() == 1 && + "|scalar_type_id| doesn't represent a scalar type"); + return MaybeGetBoolConstant(ir_context, transformation_context, words[0], + is_irrelevant); + } +} + +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant) { + const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id); + (void)type; // Make compilers happy in release mode. + assert(IsCompositeType(type) && "|composite_type_id| is invalid"); + + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == spv::Op::OpConstantComposite && + inst.type_id() == composite_type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant && + inst.NumInOperands() == component_ids.size()) { + bool is_match = true; + + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + if (inst.GetSingleWordInOperand(i) != component_ids[i]) { + is_match = false; + break; + } + } + + if (is_match) { + return inst.result_id(); + } + } + } + + return 0; +} + +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { + if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); + } + + return 0; +} + +uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context, + uint32_t value, + uint32_t int_type_id) { + auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id); + + assert(int_type_inst && "The given type id must exist."); + + auto int_type = ir_context->get_type_mgr() + ->GetType(int_type_inst->result_id()) + ->AsInteger(); + + assert(int_type && int_type->width() == 32 && + "The given type id must correspond to an 32-bit integer type."); + + opt::analysis::IntConstant constant(int_type, {value}); + + // Check that the constant exists in the module. + if (!ir_context->get_constant_mgr()->FindConstant(&constant)) { + return 0; + } + + return ir_context->get_constant_mgr() + ->GetDefiningInstruction(&constant) + ->result_id(); +} + +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant) { + if (auto type_id = MaybeGetFloatType(ir_context, width)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); + } + + return 0; +} + +uint32_t MaybeGetBoolConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant) { + if (auto type_id = MaybeGetBoolType(ir_context)) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == + (value ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse) && + inst.type_id() == type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } + } + + return 0; +} + +std::vector IntToWords(uint64_t value, uint32_t width, + bool is_signed) { + assert(width <= 64 && "The bit width should not be more than 64 bits"); + + // Sign-extend or zero-extend the last |width| bits of |value|, depending on + // |is_signed|. + if (is_signed) { + // Sign-extend by shifting left and then shifting right, interpreting the + // integer as signed. + value = static_cast(value << (64 - width)) >> (64 - width); + } else { + // Zero-extend by shifting left and then shifting right, interpreting the + // integer as unsigned. + value = (value << (64 - width)) >> (64 - width); + } + + std::vector result; + result.push_back(static_cast(value)); + if (width > 32) { + result.push_back(static_cast(value >> 32)); + } + return result; +} + +bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id, + uint32_t type2_id) { + if (type1_id == type2_id) { + return true; + } + + auto type1 = ir_context->get_type_mgr()->GetType(type1_id); + auto type2 = ir_context->get_type_mgr()->GetType(type2_id); + + // Integer scalar types must have the same width + if (type1->AsInteger() && type2->AsInteger()) { + return type1->AsInteger()->width() == type2->AsInteger()->width(); + } + + // Integer vector types must have the same number of components and their + // component types must be integers with the same width. + if (type1->AsVector() && type2->AsVector()) { + auto component_type1 = type1->AsVector()->element_type()->AsInteger(); + auto component_type2 = type2->AsVector()->element_type()->AsInteger(); + + // Only check the component count and width if they are integer. + if (component_type1 && component_type2) { + return type1->AsVector()->element_count() == + type2->AsVector()->element_count() && + component_type1->width() == component_type2->width(); + } + } + + // In all other cases, the types cannot be considered equal. + return false; +} + +std::map RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data) { + std::map result; + + for (const auto& entry : data) { + result[entry.first()] = entry.second(); + } + + return result; +} + +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data) { + google::protobuf::RepeatedPtrField result; + + for (const auto& entry : data) { + protobufs::UInt32Pair pair; + pair.set_first(entry.first); + pair.set_second(entry.second); + *result.Add() = std::move(pair); + } + + return result; +} + +opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context, + uint32_t block_id, + spv::Op opcode) { + // CFG::block uses std::map::at which throws an exception when |block_id| is + // invalid. The error message is unhelpful, though. Thus, we test that + // |block_id| is valid here. + const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id); + (void)label_inst; // Make compilers happy in release mode. + assert(label_inst && label_inst->opcode() == spv::Op::OpLabel && + "|block_id| is invalid"); + + auto* block = ir_context->cfg()->block(block_id); + auto it = block->rbegin(); + assert(it != block->rend() && "Basic block can't be empty"); + + if (block->GetMergeInst()) { + ++it; + assert(it != block->rend() && + "|block| must have at least two instructions:" + "terminator and a merge instruction"); + } + + return CanInsertOpcodeBeforeInstruction(opcode, &*it) ? &*it : nullptr; +} + +bool IdUseCanBeReplaced(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* use_instruction, + uint32_t use_in_operand_index) { + if (spvOpcodeIsAccessChain(use_instruction->opcode()) && + use_in_operand_index > 0) { + // A replacement for an irrelevant index in OpAccessChain must be clamped + // first. + if (transformation_context.GetFactManager()->IdIsIrrelevant( + use_instruction->GetSingleWordInOperand(use_in_operand_index))) { + return false; + } + + // This is an access chain index. If the (sub-)object being accessed by the + // given index has struct type then we cannot replace the use, as it needs + // to be an OpConstant. + + // Get the top-level composite type that is being accessed. + auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(0)); + auto pointer_type = + ir_context->get_type_mgr()->GetType(object_being_accessed->type_id()); + assert(pointer_type->AsPointer()); + auto composite_type_being_accessed = + pointer_type->AsPointer()->pointee_type(); + + // Now walk the access chain, tracking the type of each sub-object of the + // composite that is traversed, until the index of interest is reached. + for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index; + index_in_operand++) { + // For vectors, matrices and arrays, getting the type of the sub-object is + // trivial. For the struct case, the sub-object type is field-sensitive, + // and depends on the constant index that is used. + if (composite_type_being_accessed->AsVector()) { + composite_type_being_accessed = + composite_type_being_accessed->AsVector()->element_type(); + } else if (composite_type_being_accessed->AsMatrix()) { + composite_type_being_accessed = + composite_type_being_accessed->AsMatrix()->element_type(); + } else if (composite_type_being_accessed->AsArray()) { + composite_type_being_accessed = + composite_type_being_accessed->AsArray()->element_type(); + } else if (composite_type_being_accessed->AsRuntimeArray()) { + composite_type_being_accessed = + composite_type_being_accessed->AsRuntimeArray()->element_type(); + } else { + assert(composite_type_being_accessed->AsStruct()); + auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(index_in_operand)); + assert(constant_index_instruction->opcode() == spv::Op::OpConstant); + uint32_t member_index = + constant_index_instruction->GetSingleWordInOperand(0); + composite_type_being_accessed = + composite_type_being_accessed->AsStruct() + ->element_types()[member_index]; + } + } + + // We have found the composite type being accessed by the index we are + // considering replacing. If it is a struct, then we cannot do the + // replacement as struct indices must be constants. + if (composite_type_being_accessed->AsStruct()) { + return false; + } + } + + if (use_instruction->opcode() == spv::Op::OpFunctionCall && + use_in_operand_index > 0) { + // This is a function call argument. It is not allowed to have pointer + // type. + + // Get the definition of the function being called. + auto function = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(0)); + // From the function definition, get the function type. + auto function_type = ir_context->get_def_use_mgr()->GetDef( + function->GetSingleWordInOperand(1)); + // OpTypeFunction's 0-th input operand is the function return type, and the + // function argument types follow. Because the arguments to OpFunctionCall + // start from input operand 1, we can use |use_in_operand_index| to get the + // type associated with this function argument. + auto parameter_type = ir_context->get_type_mgr()->GetType( + function_type->GetSingleWordInOperand(use_in_operand_index)); + if (parameter_type->AsPointer()) { + return false; + } + } + + if (use_instruction->opcode() == spv::Op::OpImageTexelPointer && + use_in_operand_index == 2) { + // The OpImageTexelPointer instruction has a Sample parameter that in some + // situations must be an id for the value 0. To guard against disrupting + // that requirement, we do not replace this argument to that instruction. + return false; + } + + if (ir_context->get_feature_mgr()->HasCapability(spv::Capability::Shader)) { + // With the Shader capability, memory scope and memory semantics operands + // are required to be constants, so they cannot be replaced arbitrarily. + switch (use_instruction->opcode()) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + if (use_in_operand_index == 1 || use_in_operand_index == 2) { + return false; + } + break; + case spv::Op::OpAtomicCompareExchange: + if (use_in_operand_index == 1 || use_in_operand_index == 2 || + use_in_operand_index == 3) { + return false; + } + break; + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: + case spv::Op::OpAtomicFAddEXT: + assert(false && "Not allowed with the Shader capability."); + default: + break; + } + } + + return true; +} + +bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context, + uint32_t struct_type_id) { + const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(struct_type_id); + assert(type_inst && type_inst->opcode() == spv::Op::OpTypeStruct && + "|struct_type_id| is not a result id of an OpTypeStruct"); + + uint32_t builtin_count = 0; + ir_context->get_def_use_mgr()->ForEachUser( + type_inst, + [struct_type_id, &builtin_count](const opt::Instruction* user) { + if (user->opcode() == spv::Op::OpMemberDecorate && + user->GetSingleWordInOperand(0) == struct_type_id && + static_cast(user->GetSingleWordInOperand(2)) == + spv::Decoration::BuiltIn) { + ++builtin_count; + } + }); + + assert((builtin_count == 0 || builtin_count == type_inst->NumInOperands()) && + "The module is invalid: either none or all of the members of " + "|struct_type_id| may be builtin"); + + return builtin_count != 0; +} + +bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id) { + for (auto decoration : + {spv::Decoration::Block, spv::Decoration::BufferBlock}) { + if (!ir_context->get_decoration_mgr()->WhileEachDecoration( + id, uint32_t(decoration), + [](const opt::Instruction & /*unused*/) -> bool { + return false; + })) { + return true; + } + } + return false; +} + +bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + opt::BasicBlock* block_to_split, opt::Instruction* split_before) { + std::set sampled_image_result_ids; + bool before_split = true; + + // Check all the instructions in the block to split. + for (auto& instruction : *block_to_split) { + if (&instruction == &*split_before) { + before_split = false; + } + if (before_split) { + // If the instruction comes before the split and its opcode is + // OpSampledImage, record its result id. + if (instruction.opcode() == spv::Op::OpSampledImage) { + sampled_image_result_ids.insert(instruction.result_id()); + } + } else { + // If the instruction comes after the split, check if ids + // corresponding to OpSampledImage instructions defined before the split + // are used, and return true if they are. + if (!instruction.WhileEachInId( + [&sampled_image_result_ids](uint32_t* id) -> bool { + return !sampled_image_result_ids.count(*id); + })) { + return true; + } + } + } + + // No usage that would be separated from the definition has been found. + return false; +} + +bool InstructionHasNoSideEffects(const opt::Instruction& instruction) { + switch (instruction.opcode()) { + case spv::Op::OpUndef: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpArrayLength: + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCopyObject: + case spv::Op::OpTranspose: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + case spv::Op::OpBitcast: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpCopyLogical: + case spv::Op::OpPhi: + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + return true; + default: + return false; + } +} + +std::set GetReachableReturnBlocks(opt::IRContext* ir_context, + uint32_t function_id) { + auto function = ir_context->GetFunction(function_id); + assert(function && "The function |function_id| must exist."); + + std::set result; + + ir_context->cfg()->ForEachBlockInPostOrder(function->entry().get(), + [&result](opt::BasicBlock* block) { + if (block->IsReturn()) { + result.emplace(block->id()); + } + }); + + return result; +} + +bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, + uint32_t block_id, + opt::Instruction new_terminator) { + auto* mutated_block = MaybeFindBlock(ir_context, block_id); + assert(mutated_block && "|block_id| is invalid"); + + ChangeTerminatorRAII change_terminator_raii(mutated_block, + std::move(new_terminator)); + opt::DominatorAnalysis dominator_analysis; + dominator_analysis.InitializeTree(*ir_context->cfg(), + mutated_block->GetParent()); + + // Check that each dominator appears before each dominated block. + std::unordered_map positions; + for (const auto& block : *mutated_block->GetParent()) { + positions[block.id()] = positions.size(); + } + + std::queue q({mutated_block->GetParent()->begin()->id()}); + std::unordered_set visited; + while (!q.empty()) { + auto block = q.front(); + q.pop(); + visited.insert(block); + + auto success = ir_context->cfg()->block(block)->WhileEachSuccessorLabel( + [&positions, &visited, &dominator_analysis, block, &q](uint32_t id) { + if (id == block) { + // Handle the case when loop header and continue target are the same + // block. + return true; + } + + if (dominator_analysis.Dominates(block, id) && + positions[block] > positions[id]) { + // |block| dominates |id| but appears after |id| - violates + // domination rules. + return false; + } + + if (!visited.count(id)) { + q.push(id); + } + + return true; + }); + + if (!success) { + return false; + } + } + + // For each instruction in the |block->GetParent()| function check whether + // all its dependencies satisfy domination rules (i.e. all id operands + // dominate that instruction). + for (const auto& block : *mutated_block->GetParent()) { + if (!ir_context->IsReachable(block)) { + // If some block is not reachable then we don't need to worry about the + // preservation of domination rules for its instructions. + continue; + } + + for (const auto& inst : block) { + for (uint32_t i = 0; i < inst.NumInOperands(); + i += inst.opcode() == spv::Op::OpPhi ? 2 : 1) { + const auto& operand = inst.GetInOperand(i); + if (!spvIsInIdType(operand.type)) { + continue; + } + + if (MaybeFindBlock(ir_context, operand.words[0])) { + // Ignore operands that refer to OpLabel instructions. + continue; + } + + const auto* dependency_block = + ir_context->get_instr_block(operand.words[0]); + if (!dependency_block) { + // A global instruction always dominates all instructions in any + // function. + continue; + } + + auto domination_target_id = inst.opcode() == spv::Op::OpPhi + ? inst.GetSingleWordInOperand(i + 1) + : block.id(); + + if (!dominator_analysis.Dominates(dependency_block->id(), + domination_target_id)) { + return false; + } + } + } + } + + return true; +} + +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id) { + return std::find_if(ir_context->module()->begin(), + ir_context->module()->end(), + [function_id](const opt::Function& f) { + return f.result_id() == function_id; + }); +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all +// opcodes that are agnostic to signedness of operands to function. +// This is not exhaustive yet. +bool IsAgnosticToSignednessOfOperand(spv::Op opcode, + uint32_t use_in_operand_index) { + switch (opcode) { + case spv::Op::OpSNegate: + case spv::Op::OpNot: + case spv::Op::OpIAdd: + case spv::Op::OpISub: + case spv::Op::OpIMul: + case spv::Op::OpSDiv: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + return true; + + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFAddEXT: // Capability AtomicFloat32AddEXT, + // AtomicFloat64AddEXT. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index == 1 || use_in_operand_index == 2; + + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1 && use_in_operand_index <= 3; + + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicFlagTestAndSet: // Capability Kernel. + case spv::Op::OpAtomicFlagClear: // Capability Kernel. + assert(use_in_operand_index != 0 && + "Signedness check should not occur on a pointer operand."); + return use_in_operand_index >= 1; + + case spv::Op::OpAccessChain: + // The signedness of indices does not matter. + return use_in_operand_index > 0; + + default: + // Conservatively assume that the id cannot be swapped in other + // instructions. + return false; + } +} + +bool TypesAreCompatible(opt::IRContext* ir_context, spv::Op opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2) { + assert(ir_context->get_type_mgr()->GetType(type_id_1) && + ir_context->get_type_mgr()->GetType(type_id_2) && + "Type ids are invalid"); + + return type_id_1 == type_id_2 || + (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && + fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); +} + +} // namespace fuzzerutil +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.h new file mode 100644 index 00000000..374e32e1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/fuzzer_util.h @@ -0,0 +1,626 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_UTIL_H_ +#define SOURCE_FUZZ_FUZZER_UTIL_H_ + +#include +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Provides types and global utility methods for use by the fuzzer +namespace fuzzerutil { + +// A silent message consumer. +extern const spvtools::MessageConsumer kSilentMessageConsumer; + +// Function type that produces a SPIR-V module. +using ModuleSupplier = std::function()>; + +// Builds a new opt::IRContext object. Returns true if successful and changes +// the |ir_context| parameter. Otherwise (if any errors occur), returns false +// and |ir_context| remains unchanged. +bool BuildIRContext(spv_target_env target_env, + const spvtools::MessageConsumer& message_consumer, + const std::vector& binary_in, + spv_validator_options validator_options, + std::unique_ptr* ir_context); + +// Returns true if and only if the module does not define the given id. +bool IsFreshId(opt::IRContext* context, uint32_t id); + +// Updates the module's id bound if needed so that it is large enough to +// account for the given id. +void UpdateModuleIdBound(opt::IRContext* context, uint32_t id); + +// Return the block with id |maybe_block_id| if it exists, and nullptr +// otherwise. +opt::BasicBlock* MaybeFindBlock(opt::IRContext* context, + uint32_t maybe_block_id); + +// When adding an edge from |bb_from| to |bb_to| (which are assumed to be blocks +// in the same function), it is important to supply |bb_to| with ids that can be +// used to augment OpPhi instructions in the case that there is not already such +// an edge. This function returns true if and only if the ids provided in +// |phi_ids| suffice for this purpose, +bool PhiIdsOkForNewEdge( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + const google::protobuf::RepeatedField& phi_ids); + +// Returns an OpBranchConditional instruction that will create an unreachable +// branch from |bb_from_id| to |bb_to_id|. |bool_id| must be a result id of +// either OpConstantTrue or OpConstantFalse. Based on the opcode of |bool_id|, +// operands of the returned instruction will be positioned in a way that the +// branch from |bb_from_id| to |bb_to_id| is always unreachable. +opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context, + uint32_t bb_from_id, + uint32_t bb_to_id, + uint32_t bool_id); + +// Requires that |bool_id| is a valid result id of either OpConstantTrue or +// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) +// holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch +// into "OpBranchConditional |condition_value| ...", such that control will +// branch to %some_block, with |bb_to| being the unreachable alternative. +// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is +// valid. |condition_value| above is equal to |true| if |bool_id| is a result id +// of an OpConstantTrue instruction. +void AddUnreachableEdgeAndUpdateOpPhis( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + uint32_t bool_id, + const google::protobuf::RepeatedField& phi_ids); + +// Returns true if and only if |loop_header_id| is a loop header and +// |block_id| is a reachable block branching to and dominated by +// |loop_header_id|. +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id); + +// Returns true if and only if |maybe_loop_header_id| is a loop header and +// |block_id| is in the continue construct of the associated loop. +bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, + uint32_t maybe_loop_header_id); + +// If |block| contains |inst|, an iterator for |inst| is returned. +// Otherwise |block|->end() is returned. +opt::BasicBlock::iterator GetIteratorForInstruction( + opt::BasicBlock* block, const opt::Instruction* inst); + +// Determines whether it is OK to insert an instruction with opcode |opcode| +// before |instruction_in_block|. +bool CanInsertOpcodeBeforeInstruction( + spv::Op opcode, const opt::BasicBlock::iterator& instruction_in_block); + +// Determines whether it is OK to make a synonym of |inst|. +// |transformation_context| is used to verify that the result id of |inst| +// does not participate in IdIsIrrelevant fact. +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::Instruction& inst); + +// Determines whether the given type is a composite; that is: an array, matrix, +// struct or vector. +bool IsCompositeType(const opt::analysis::Type* type); + +// Returns a vector containing the same elements as |repeated_field|. +std::vector RepeatedFieldToVector( + const google::protobuf::RepeatedField& repeated_field); + +// Given a type id, |base_object_type_id|, returns 0 if the type is not a +// composite type or if |index| is too large to be used as an index into the +// composite. Otherwise returns the type id of the type associated with the +// composite's index. +// +// Example: if |base_object_type_id| is 10, and we have: +// +// %10 = OpTypeStruct %3 %4 %5 +// +// then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index +// is 3 or larger. +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index); + +// Given a type id, |base_object_type_id|, checks that the given sequence of +// |indices| is suitable for indexing into this type. Returns the id of the +// type of the final sub-object reached via the indices if they are valid, and +// 0 otherwise. +uint32_t WalkCompositeTypeIndices( + opt::IRContext* context, uint32_t base_object_type_id, + const google::protobuf::RepeatedField& indices); + +// Returns the number of members associated with |struct_type_instruction|, +// which must be an OpStructType instruction. +uint32_t GetNumberOfStructMembers( + const opt::Instruction& struct_type_instruction); + +// Returns the constant size of the array associated with +// |array_type_instruction|, which must be an OpArrayType instruction. Returns +// 0 if there is not a static size. +uint32_t GetArraySize(const opt::Instruction& array_type_instruction, + opt::IRContext* context); + +// Returns the bound for indexing into a composite of type +// |composite_type_inst|, i.e. the number of fields of a struct, the size of an +// array, the number of components of a vector, or the number of columns of a +// matrix. |composite_type_inst| must be the type of a composite. +uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context); + +// Returns memory semantics mask for specific storage class. +spv::MemorySemanticsMask GetMemorySemanticsForStorageClass( + spv::StorageClass storage_class); + +// Returns true if and only if |context| is valid, according to the validator +// instantiated with |validator_options|. |consumer| is used for error +// reporting. +bool IsValid(const opt::IRContext* context, + spv_validator_options validator_options, MessageConsumer consumer); + +// Returns true if and only if IsValid(|context|, |validator_options|) holds, +// and furthermore every basic block in |context| has its enclosing function as +// its parent, and every instruction in |context| has a distinct unique id. +// |consumer| is used for error reporting. +bool IsValidAndWellFormed(const opt::IRContext* context, + spv_validator_options validator_options, + MessageConsumer consumer); + +// Returns a clone of |context|, by writing |context| to a binary and then +// parsing it again. +std::unique_ptr CloneIRContext(opt::IRContext* context); + +// Returns true if and only if |id| is the id of a type that is not a function +// type. +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id); + +// Returns true if and only if |block_id| is a merge block or continue target +bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id); + +// Returns the id of the header of the loop corresponding to the given loop +// merge block. Returns 0 if |merge_block_id| is not a loop merge block. +uint32_t GetLoopFromMergeBlock(opt::IRContext* ir_context, + uint32_t merge_block_id); + +// Returns the result id of an instruction of the form: +// %id = OpTypeFunction |type_ids| +// or 0 if no such instruction exists. +uint32_t FindFunctionType(opt::IRContext* ir_context, + const std::vector& type_ids); + +// Returns a type instruction (OpTypeFunction) for |function|. +// Returns |nullptr| if type is not found. +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function); + +// Returns the function with result id |function_id|, or |nullptr| if no such +// function exists. +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); + +// Returns true if |function| has a block that the termination instruction is +// OpKill or OpUnreachable. +bool FunctionContainsOpKillOrUnreachable(const opt::Function& function); + +// Returns |true| if one of entry points has function id |function_id|. +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id); + +// Checks whether |id| is available (according to dominance rules) at the use +// point defined by input operand |use_input_operand_index| of +// |use_instruction|. |use_instruction| must be a in some basic block. +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id); + +// Checks whether |id| is available (according to dominance rules) at the +// program point directly before |instruction|. |instruction| must be in some +// basic block. +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, uint32_t id); + +// Returns true if and only if |instruction| is an OpFunctionParameter +// associated with |function|. +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function); + +// Returns the type id of the instruction defined by |result_id|, or 0 if there +// is no such result id. +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the associated storage class. +spv::StorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// associated storage class. +spv::StorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + +// Returns the id of a pointer with pointee type |pointee_type_id| and storage +// class |storage_class|, if it exists, and 0 otherwise. +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + spv::StorageClass storage_class); + +// Given an instruction |inst| and an operand absolute index |absolute_index|, +// returns the index of the operand restricted to the input operands. +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index); + +// Returns true if and only if |type| is one of the types for which it is legal +// to have an OpConstantNull value. This may depend on the capabilities declared +// in |context|. +bool IsNullConstantSupported(opt::IRContext* context, + const opt::Instruction& type); + +// Returns true if and only if the SPIR-V version being used requires that +// global variables accessed in the static call graph of an entry point need +// to be listed in that entry point's interface. +bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( + const opt::IRContext* context); + +// Adds |id| into the interface of every entry point of the shader. +// Does nothing if SPIR-V doesn't require global variables, that are accessed +// from an entry point function, to be listed in that function's interface. +void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id); + +// Adds a global variable with storage class |storage_class| to the module, with +// type |type_id| and either no initializer or |initializer_id| as an +// initializer, depending on whether |initializer_id| is 0. The global variable +// has result id |result_id|. Updates module's id bound to accommodate for +// |result_id|. +// +// - |type_id| must be the id of a pointer type with the same storage class as +// |storage_class|. +// - |storage_class| must be Private or Workgroup. +// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise +// may either be 0 or the id of a constant whose type is the pointee type of +// |type_id|. +// +// Returns a pointer to the new global variable instruction. +opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, + spv::StorageClass storage_class, + uint32_t initializer_id); + +// Adds an instruction to the start of |function_id|, of the form: +// |result_id| = OpVariable |type_id| Function |initializer_id|. +// Updates module's id bound to accommodate for |result_id|. +// +// - |type_id| must be the id of a pointer type with Function storage class. +// - |initializer_id| must be the id of a constant with the same type as the +// pointer's pointee type. +// - |function_id| must be the id of a function. +// +// Returns a pointer to the new local variable instruction. +opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id); + +// Returns true if the vector |arr| has duplicates. +bool HasDuplicates(const std::vector& arr); + +// Checks that the given vector |arr| contains a permutation of a range +// [lo, hi]. That being said, all elements in the range are present without +// duplicates. If |arr| is empty, returns true iff |lo > hi|. +bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, + uint32_t hi); + +// Returns OpFunctionParameter instructions corresponding to the function +// with result id |function_id|. +std::vector GetParameters(opt::IRContext* ir_context, + uint32_t function_id); + +// Removes an OpFunctionParameter instruction with result id |parameter_id| +// from the its function. Parameter's function must not be an entry-point +// function. The function must have a parameter with result id |parameter_id|. +// +// Prefer using this function to opt::Function::RemoveParameter since +// this function also guarantees that |ir_context| has no invalid pointers +// to the removed parameter. +void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id); + +// Returns all OpFunctionCall instructions that call a function with result id +// |function_id|. +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id); + +// Returns a function that contains OpFunctionParameter instruction with result +// id |param_id|. Returns nullptr if the module has no such function. +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id); + +// Changes the type of function |function_id| so that its return type is +// |return_type_id| and its parameters' types are |parameter_type_ids|. If a +// suitable function type already exists in the module, it is used, otherwise +// |new_function_type_result_id| is used as the result id of a suitable new +// function type instruction. If the old type of the function doesn't have any +// more users, it is removed from the module. Returns the result id of the +// OpTypeFunction instruction that is used as a type of the function with +// |function_id|. +// +// CAUTION: When the old type of the function is removed from the module, its +// memory is deallocated. Be sure not to use any pointers to the old +// type when this function returns. +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids); + +// Creates new OpTypeFunction instruction in the module. |type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| may not equal to 0. Updates module's id bound to accommodate for +// |result_id|. +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids); + +// Returns a result id of an OpTypeFunction instruction in the module. Creates a +// new instruction if required and returns |result_id|. type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| must not be equal to 0. Updates module's id bound to accommodate +// for |result_id|. +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids); + +// Returns a result id of an OpTypeInt instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed); + +// Returns a result id of an OpTypeFloat instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width); + +// Returns a result id of an OpTypeBool instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetBoolType(opt::IRContext* ir_context); + +// Returns a result id of an OpTypeVector instruction if present. Returns 0 +// otherwise. |component_type_id| must be a valid result id of an OpTypeInt, +// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be +// in the range [2, 4]. +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction whose field types exactly +// match |component_type_ids| if such an instruction is present. Returns 0 +// otherwise. |component_type_ids| may not contain a result id of an +// OpTypeFunction. +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids); + +// Returns a result id of an OpTypeVoid instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetVoidType(opt::IRContext* ir_context); + +// Recursive definition is the following: +// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns +// a result id of the following constants (depending on the type): int -> 0, +// float -> 0.0, bool -> false. +// - otherwise, returns a result id of an OpConstantComposite instruction. +// Every component of the composite constant is looked up by calling this +// function with the type id of that component. +// Returns 0 if no such instruction is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant); + +// Returns true if it is possible to create an OpConstant or an +// OpConstantComposite instruction of type |type_id|. That is, returns true if +// the type associated with |type_id| and all its constituents are either scalar +// or composite. +bool CanCreateConstant(opt::IRContext* ir_context, uint32_t type_id); + +// Returns the result id of an OpConstant instruction. |scalar_type_id| must be +// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such +// instruction is present in the module. The returned id either participates in +// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant); + +// Returns the result id of an OpConstantComposite instruction. +// |composite_type_id| must be a result id of a composite type (i.e. vector, +// matrix, struct or array). Returns 0 if no such instruction is present in the +// module. The returned id either participates in IdIsIrrelevant fact or not, +// depending on the |is_irrelevant| parameter. +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant); + +// Returns the result id of an OpConstant instruction of integral type. +// Returns 0 if no such instruction or type is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant); + +// Returns the id of a 32-bit integer constant in the module with type +// |int_type_id| and value |value|, or 0 if no such constant exists in the +// module. |int_type_id| must exist in the module and it must correspond to a +// 32-bit integer type. +uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context, + uint32_t value, + uint32_t int_type_id); + +// Returns the result id of an OpConstant instruction of floating-point type. +// Returns 0 if no such instruction or type is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant); + +// Returns the id of a boolean constant with value |value| if it exists in the +// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant +// fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetBoolConstant( + opt::IRContext* context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant); + +// Returns a vector of words representing the integer |value|, only considering +// the last |width| bits. The last |width| bits are sign-extended if the value +// is signed, zero-extended if it is unsigned. +// |width| must be <= 64. +// If |width| <= 32, returns a vector containing one value. If |width| > 64, +// returns a vector containing two values, with the first one representing the +// lower-order word of the value and the second one representing the +// higher-order word. +std::vector IntToWords(uint64_t value, uint32_t width, + bool is_signed); + +// Returns a bit pattern that represents a floating-point |value|. +inline uint32_t FloatToWord(float value) { + uint32_t result; + memcpy(&result, &value, sizeof(uint32_t)); + return result; +} + +// Returns true if any of the following is true: +// - |type1_id| and |type2_id| are the same id +// - |type1_id| and |type2_id| refer to integer scalar or vector types, only +// differing by their signedness. +bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id, + uint32_t type2_id); + +// Converts repeated field of UInt32Pair to a map. If two or more equal values +// of |UInt32Pair::first()| are available in |data|, the last value of +// |UInt32Pair::second()| is used. +std::map RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data); + +// Converts a map into a repeated field of UInt32Pair. +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data); + +// Returns the last instruction in |block_id| before which an instruction with +// opcode |opcode| can be inserted, or nullptr if there is no such instruction. +opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context, + uint32_t block_id, + spv::Op opcode); + +// Checks whether various conditions hold related to the acceptability of +// replacing the id use at |use_in_operand_index| of |use_instruction| with a +// synonym or another id of appropriate type if the original id is irrelevant. +// In particular, this checks that: +// - If id use is an index of an irrelevant id (|use_in_operand_index > 0|) +// in OpAccessChain - it can't be replaced. +// - The id use is not an index into a struct field in an OpAccessChain - such +// indices must be constants, so it is dangerous to replace them. +// - The id use is not a pointer function call argument, on which there are +// restrictions that make replacement problematic. +// - The id use is not the Sample parameter of an OpImageTexelPointer +// instruction, as this must satisfy particular requirements. +bool IdUseCanBeReplaced(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* use_instruction, + uint32_t use_in_operand_index); + +// Requires that |struct_type_id| is the id of a struct type, and (as per the +// SPIR-V spec) that either all or none of the members of |struct_type_id| have +// the BuiltIn decoration. Returns true if and only if all members have the +// BuiltIn decoration. +bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context, + uint32_t struct_type_id); + +// Returns true if and only if |id| is decorated with either Block or +// BufferBlock. Even though these decorations are only allowed on struct types, +// for convenience |id| can be any result id so that it is possible to call this +// method on something that *might* be a struct type. +bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id); + +// Returns true iff splitting block |block_to_split| just before the instruction +// |split_before| would separate an OpSampledImage instruction from its usage. +bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + opt::BasicBlock* block_to_split, opt::Instruction* split_before); + +// Returns true if the instruction given has no side effects. +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3758): Add any +// missing instructions to the list. In particular, GLSL extended instructions +// (called using OpExtInst) have not been considered. +bool InstructionHasNoSideEffects(const opt::Instruction& instruction); + +// Returns a set of the ids of all the return blocks that are reachable from +// the entry block of |function_id|. +// Assumes that the function exists in the module. +std::set GetReachableReturnBlocks(opt::IRContext* ir_context, + uint32_t function_id); + +// Returns true if changing terminator instruction to |new_terminator| in the +// basic block with id |block_id| preserves domination rules and valid block +// order (i.e. dominator must always appear before dominated in the CFG). +// Returns false otherwise. +bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, + uint32_t block_id, + opt::Instruction new_terminator); + +// Return the iterator that points to the function with the corresponding +// function id. If the function is not found, return the pointer pointing to +// module()->end(). +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id); + +// Returns true if the instruction with opcode |opcode| does not change its +// behaviour depending on the signedness of the operand at +// |use_in_operand_index|. +// Assumes that the operand must be the id of an integer scalar or vector. +bool IsAgnosticToSignednessOfOperand(spv::Op opcode, + uint32_t use_in_operand_index); + +// Returns true if |type_id_1| and |type_id_2| represent compatible types +// given the context of the instruction with |opcode| (i.e. we can replace +// an operand of |opcode| of the first type with an id of the second type +// and vice-versa). +bool TypesAreCompatible(opt::IRContext* ir_context, spv::Op opcode, + uint32_t use_in_operand_index, uint32_t type_id_1, + uint32_t type_id_2); + +} // namespace fuzzerutil +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_UTIL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.cpp new file mode 100644 index 00000000..4e10146f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/id_use_descriptor.h" + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context) { + auto result = + FindInstruction(id_use_descriptor.enclosing_instruction(), context); + if (!result) { + return nullptr; + } + if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) { + return nullptr; + } + if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) != + id_use_descriptor.id_of_interest()) { + return nullptr; + } + return result; +} + +protobufs::IdUseDescriptor MakeIdUseDescriptor( + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index) { + protobufs::IdUseDescriptor result; + result.set_id_of_interest(id_of_interest); + *result.mutable_enclosing_instruction() = enclosing_instruction; + result.set_in_operand_index(in_operand_index); + return result; +} + +protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( + opt::IRContext* context, opt::Instruction* inst, + uint32_t in_operand_index) { + const auto& in_operand = inst->GetInOperand(in_operand_index); + assert(spvIsInIdType(in_operand.type)); + return MakeIdUseDescriptor(in_operand.words[0], + MakeInstructionDescriptor(context, inst), + in_operand_index); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.h new file mode 100644 index 00000000..d18bb668 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/id_use_descriptor.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ +#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Looks for an instruction in |context| that contains a use +// identified by |id_use_descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context); + +// Creates an IdUseDescriptor protobuf message from the given components. +// See the protobuf definition for details of what these components mean. +protobufs::IdUseDescriptor MakeIdUseDescriptor( + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index); + +// Given an id use, represented by the instruction |inst| that uses the id, and +// the input operand index |in_operand_index| associated with the usage, returns +// an IdUseDescriptor that represents the use. +protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( + opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.cpp new file mode 100644 index 00000000..120d8f87 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + spvtools::opt::IRContext* context) { + auto block = context->get_instr_block( + instruction_descriptor.base_instruction_result_id()); + if (block == nullptr) { + return nullptr; + } + bool found_base = + block->id() == instruction_descriptor.base_instruction_result_id(); + uint32_t num_ignored = 0; + for (auto& instruction : *block) { + if (instruction.HasResultId() && + instruction.result_id() == + instruction_descriptor.base_instruction_result_id()) { + assert(!found_base && + "It should not be possible to find the base instruction " + "multiple times."); + found_base = true; + assert(num_ignored == 0 && + "The skipped instruction count should only be incremented " + "after the instruction base has been found."); + } + if (found_base && + instruction.opcode() == + spv::Op(instruction_descriptor.target_instruction_opcode())) { + if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) { + return &instruction; + } + num_ignored++; + } + } + return nullptr; +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, spv::Op target_instruction_opcode, + uint32_t num_opcodes_to_ignore) { + protobufs::InstructionDescriptor result; + result.set_base_instruction_result_id(base_instruction_result_id); + result.set_target_instruction_opcode(uint32_t(target_instruction_opcode)); + result.set_num_opcodes_to_ignore(num_opcodes_to_ignore); + return result; +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + const opt::BasicBlock& block, + const opt::BasicBlock::const_iterator& inst_it) { + const spv::Op opcode = + inst_it->opcode(); // The opcode of the instruction being described. + uint32_t skip_count = 0; // The number of these opcodes we have skipped when + // searching backwards. + + // Consider instructions in the block in reverse order, starting from + // |inst_it|. + for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;; + --backwards_iterator) { + if (backwards_iterator->HasResultId()) { + // As soon as we find an instruction with a result id, we can return a + // descriptor for |inst_it|. + return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode, + skip_count); + } + if (backwards_iterator != inst_it && + backwards_iterator->opcode() == opcode) { + // We are skipping over an instruction with the same opcode as |inst_it|; + // we increase our skip count to reflect this. + skip_count++; + } + if (backwards_iterator == block.begin()) { + // We exit the loop when we reach the start of the block, but only after + // we have processed the first instruction in the block. + break; + } + } + // We did not find an instruction inside the block with a result id, so we use + // the block's label's id. + return MakeInstructionDescriptor(block.id(), opcode, skip_count); +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + opt::IRContext* context, opt::Instruction* inst) { + auto block = context->get_instr_block(inst); + uint32_t base_instruction_result_id = block->id(); + uint32_t num_opcodes_to_ignore = 0; + for (auto& inst_in_block : *block) { + if (inst_in_block.HasResultId()) { + base_instruction_result_id = inst_in_block.result_id(); + num_opcodes_to_ignore = 0; + } + if (&inst_in_block == inst) { + return MakeInstructionDescriptor(base_instruction_result_id, + inst->opcode(), num_opcodes_to_ignore); + } + if (inst_in_block.opcode() == inst->opcode()) { + num_opcodes_to_ignore++; + } + } + assert(false && "No matching instruction was found."); + return protobufs::InstructionDescriptor(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.h new file mode 100644 index 00000000..063cad47 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_descriptor.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ +#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Looks for an instruction in |context| corresponding to |descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + opt::IRContext* context); + +// Creates an InstructionDescriptor protobuf message from the given +// components. See the protobuf definition for details of what these +// components mean. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, spv::Op target_instruction_opcode, + uint32_t num_opcodes_to_ignore); + +// Returns an instruction descriptor that describing the instruction at +// |inst_it|, which must be inside |block|. The descriptor will be with +// respect to the first instruction at or before |inst_it| that has a result +// id. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + const opt::BasicBlock& block, + const opt::BasicBlock::const_iterator& inst_it); + +// Returns an InstructionDescriptor that describes the given instruction |inst|. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + opt::IRContext* context, opt::Instruction* inst); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.cpp new file mode 100644 index 00000000..7e8ac71b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_message.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +protobufs::Instruction MakeInstructionMessage( + spv::Op opcode, uint32_t result_type_id, uint32_t result_id, + const opt::Instruction::OperandList& input_operands) { + protobufs::Instruction result; + result.set_opcode(uint32_t(opcode)); + result.set_result_type_id(result_type_id); + result.set_result_id(result_id); + for (auto& operand : input_operands) { + auto operand_message = result.add_input_operand(); + operand_message->set_operand_type(static_cast(operand.type)); + for (auto operand_word : operand.words) { + operand_message->add_operand_data(operand_word); + } + } + return result; +} + +protobufs::Instruction MakeInstructionMessage( + const opt::Instruction* instruction) { + opt::Instruction::OperandList input_operands; + for (uint32_t input_operand_index = 0; + input_operand_index < instruction->NumInOperands(); + input_operand_index++) { + input_operands.push_back(instruction->GetInOperand(input_operand_index)); + } + return MakeInstructionMessage(instruction->opcode(), instruction->type_id(), + instruction->result_id(), input_operands); +} + +std::unique_ptr InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message) { + // First, update the module's id bound with respect to the new instruction, + // if it has a result id. + if (instruction_message.result_id()) { + fuzzerutil::UpdateModuleIdBound(ir_context, + instruction_message.result_id()); + } + // Now create a sequence of input operands from the input operand data in the + // protobuf message. + opt::Instruction::OperandList in_operands; + for (auto& operand_message : instruction_message.input_operand()) { + opt::Operand::OperandData operand_data; + for (auto& word : operand_message.operand_data()) { + operand_data.push_back(word); + } + in_operands.push_back( + {static_cast(operand_message.operand_type()), + operand_data}); + } + // Create and return the instruction. + return MakeUnique( + ir_context, static_cast(instruction_message.opcode()), + instruction_message.result_type_id(), instruction_message.result_id(), + in_operands); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.h new file mode 100644 index 00000000..e1312f45 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/instruction_message.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ +#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Creates an Instruction protobuf message from its component parts. +protobufs::Instruction MakeInstructionMessage( + spv::Op opcode, uint32_t result_type_id, uint32_t result_id, + const opt::Instruction::OperandList& input_operands); + +// Creates an Instruction protobuf message from a parsed instruction. +protobufs::Instruction MakeInstructionMessage( + const opt::Instruction* instruction); + +// Creates and returns an opt::Instruction from protobuf message +// |instruction_message|, relative to |ir_context|. In the process, the module +// id bound associated with |ir_context| is updated to be at least as large as +// the result id (if any) associated with the new instruction. +std::unique_ptr InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.cpp new file mode 100644 index 00000000..d9004554 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +OverflowIdSource::~OverflowIdSource() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.h new file mode 100644 index 00000000..cbf399a5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/overflow_id_source.h @@ -0,0 +1,111 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ +#define SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ + +#include +#include + +namespace spvtools { +namespace fuzz { + +// An implementation of this interface can be used to provide fresh ids on +// demand when applying a transformation. +// +// During fuzzing this should never be required: a fuzzer pass should determine +// all the fresh ids it requires to apply a transformation. +// +// However, during shrinking we can have the situation where, after removing +// an early transformation, a later transformation needs more ids. +// +// As an example, suppose a SPIR-V function originally has this form: +// +// main() { +// stmt1; +// stmt2; +// stmt3; +// stmt4; +// } +// +// Now suppose two *outlining* transformations are applied. The first +// transformation, T1, outlines "stmt1; stmt2;" into a function foo, giving us: +// +// foo() { +// stmt1; +// stmt2; +// } +// +// main() { +// foo(); +// stmt3; +// stmt4; +// } +// +// The second transformation, T2, outlines "foo(); stmt3;" from main into a +// function bar, giving us: +// +// foo() { +// stmt1; +// stmt2; +// } +// +// bar() { +// foo(); +// stmt3; +// } +// +// main() { +// bar(); +// stmt4; +// } +// +// Suppose that T2 used a set of fresh ids, FRESH, in order to perform its +// outlining. +// +// Now suppose that during shrinking we remove T1, but still want to apply T2. +// The fresh ids used by T2 - FRESH - are sufficient to outline "foo(); stmt3;". +// However, because we did not apply T1, "foo();" does not exist and instead the +// task of T2 is to outline "stmt1; stmt2; stmt3;". The set FRESH contains +// *some* of the fresh ids required to do this (those for "stmt3;"), but not all +// of them (those for "stmt1; stmt2;" are missing). +// +// A source of overflow ids can be used to allow the shrinker to proceed +// nevertheless. +// +// It is desirable to use overflow ids only when needed. In our worked example, +// T2 should still use the ids from FRESH when handling "stmt3;", because later +// transformations might refer to those ids and will become inapplicable if +// overflow ids are used instead. +class OverflowIdSource { + public: + virtual ~OverflowIdSource(); + + // Returns true if and only if this source is capable of providing overflow + // ids. + virtual bool HasOverflowIds() const = 0; + + // Precondition: HasOverflowIds() must hold. Returns the next available + // overflow id. + virtual uint32_t GetNextOverflowId() = 0; + + // Returns the set of overflow ids from this source that have been previously + // issued via calls to GetNextOverflowId(). + virtual const std::unordered_set& GetIssuedOverflowIds() const = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h new file mode 100644 index 00000000..da61fdab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h @@ -0,0 +1,189 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ +#define SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ + +#include "source/fuzz/fuzzer_pass_add_access_chains.h" +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" +#include "source/fuzz/fuzzer_pass_add_composite_types.h" +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" +#include "source/fuzz/fuzzer_pass_add_function_calls.h" +#include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" +#include "source/fuzz/fuzzer_pass_add_loads.h" +#include "source/fuzz/fuzzer_pass_add_local_variables.h" +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_parameters.h" +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" +#include "source/fuzz/fuzzer_pass_add_stores.h" +#include "source/fuzz/fuzzer_pass_add_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" +#include "source/fuzz/fuzzer_pass_construct_composites.h" +#include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_inline_functions.h" +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" +#include "source/fuzz/fuzzer_pass_merge_blocks.h" +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include "source/fuzz/fuzzer_pass_outline_functions.h" +#include "source/fuzz/fuzzer_pass_permute_blocks.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_pass_permute_instructions.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" +#include "source/fuzz/fuzzer_pass_split_blocks.h" +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h" + +namespace spvtools { +namespace fuzz { + +// This class has a distinct member for each repeated fuzzer pass (i.e., a +// fuzzer pass that it makes sense to run multiple times). If a member is null +// then we do not have an instance of that fuzzer pass, i.e. it is disabled. +// The class also provides access to the set of passes that are enabled. +class RepeatedPassInstances { +// This macro should be invoked below for every repeated fuzzer pass. If a +// repeated fuzzer pass is called FuzzerPassFoo then the macro invocation: +// +// REPEATED_PASS_INSTANCE(Foo); +// +// should be used. This adds a private member of type FuzzerPassFoo*, and +// provides the following public methods: +// +// // Requires that SetPass has not been called previously with FuzzerPassFoo. +// // Adds |pass| to the set of known pass instances. +// void SetPass(std::unique_ptr pass); +// +// // Returns a pointer to a pass instance of type FuzzerPassFoo that was +// // previously registered via SetPass(), or nullptr if no such instance was +// // registered +// FuzzerPassFoo* GetFoo(); +#define REPEATED_PASS_INSTANCE(NAME) \ + public: \ + FuzzerPass##NAME* Get##NAME() const { return NAME##_; } \ + void SetPass(std::unique_ptr pass) { \ + assert(NAME##_ == nullptr && "Attempt to set pass multiple times."); \ + NAME##_ = pass.get(); \ + passes_.push_back(std::move(pass)); \ + } \ + \ + private: \ + FuzzerPass##NAME* NAME##_ = nullptr + + REPEATED_PASS_INSTANCE(AddAccessChains); + REPEATED_PASS_INSTANCE(AddBitInstructionSynonyms); + REPEATED_PASS_INSTANCE(AddCompositeExtract); + REPEATED_PASS_INSTANCE(AddCompositeInserts); + REPEATED_PASS_INSTANCE(AddCompositeTypes); + REPEATED_PASS_INSTANCE(AddCopyMemory); + REPEATED_PASS_INSTANCE(AddDeadBlocks); + REPEATED_PASS_INSTANCE(AddDeadBreaks); + REPEATED_PASS_INSTANCE(AddDeadContinues); + REPEATED_PASS_INSTANCE(AddEquationInstructions); + REPEATED_PASS_INSTANCE(AddFunctionCalls); + REPEATED_PASS_INSTANCE(AddGlobalVariables); + REPEATED_PASS_INSTANCE(AddImageSampleUnusedComponents); + REPEATED_PASS_INSTANCE(AddLoads); + REPEATED_PASS_INSTANCE(AddLocalVariables); + REPEATED_PASS_INSTANCE(AddLoopPreheaders); + REPEATED_PASS_INSTANCE(AddLoopsToCreateIntConstantSynonyms); + REPEATED_PASS_INSTANCE(AddOpPhiSynonyms); + REPEATED_PASS_INSTANCE(AddParameters); + REPEATED_PASS_INSTANCE(AddRelaxedDecorations); + REPEATED_PASS_INSTANCE(AddStores); + REPEATED_PASS_INSTANCE(AddSynonyms); + REPEATED_PASS_INSTANCE(AddVectorShuffleInstructions); + REPEATED_PASS_INSTANCE(ApplyIdSynonyms); + REPEATED_PASS_INSTANCE(ConstructComposites); + REPEATED_PASS_INSTANCE(CopyObjects); + REPEATED_PASS_INSTANCE(DonateModules); + REPEATED_PASS_INSTANCE(DuplicateRegionsWithSelections); + REPEATED_PASS_INSTANCE(ExpandVectorReductions); + REPEATED_PASS_INSTANCE(FlattenConditionalBranches); + REPEATED_PASS_INSTANCE(InlineFunctions); + REPEATED_PASS_INSTANCE(InvertComparisonOperators); + REPEATED_PASS_INSTANCE(MakeVectorOperationsDynamic); + REPEATED_PASS_INSTANCE(MergeBlocks); + REPEATED_PASS_INSTANCE(MergeFunctionReturns); + REPEATED_PASS_INSTANCE(MutatePointers); + REPEATED_PASS_INSTANCE(ObfuscateConstants); + REPEATED_PASS_INSTANCE(OutlineFunctions); + REPEATED_PASS_INSTANCE(PermuteBlocks); + REPEATED_PASS_INSTANCE(PermuteFunctionParameters); + REPEATED_PASS_INSTANCE(PermuteInstructions); + REPEATED_PASS_INSTANCE(PropagateInstructionsDown); + REPEATED_PASS_INSTANCE(PropagateInstructionsUp); + REPEATED_PASS_INSTANCE(PushIdsThroughVariables); + REPEATED_PASS_INSTANCE(ReplaceAddsSubsMulsWithCarryingExtended); + REPEATED_PASS_INSTANCE(ReplaceBranchesFromDeadBlocksWithExits); + REPEATED_PASS_INSTANCE(ReplaceCopyMemoriesWithLoadsStores); + REPEATED_PASS_INSTANCE(ReplaceCopyObjectsWithStoresLoads); + REPEATED_PASS_INSTANCE(ReplaceLoadsStoresWithCopyMemories); + REPEATED_PASS_INSTANCE(ReplaceIrrelevantIds); + REPEATED_PASS_INSTANCE(ReplaceOpPhiIdsFromDeadPredecessors); + REPEATED_PASS_INSTANCE(ReplaceOpSelectsWithConditionalBranches); + REPEATED_PASS_INSTANCE(ReplaceParameterWithGlobal); + REPEATED_PASS_INSTANCE(ReplaceLinearAlgebraInstructions); + REPEATED_PASS_INSTANCE(ReplaceParamsWithStruct); + REPEATED_PASS_INSTANCE(SplitBlocks); + REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands); + REPEATED_PASS_INSTANCE(WrapRegionsInSelections); + REPEATED_PASS_INSTANCE(WrapVectorSynonym); +#undef REPEATED_PASS_INSTANCE + + public: + // Yields the sequence of fuzzer pass instances that have been registered. + const std::vector>& GetPasses() const { + return passes_; + } + + private: + // The distinct fuzzer pass instances that have been registered via SetPass(). + std::vector> passes_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp new file mode 100644 index 00000000..ec443733 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager.h" + +#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManager::RepeatedPassManager(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances) + : fuzzer_context_(fuzzer_context), pass_instances_(pass_instances) {} + +RepeatedPassManager::~RepeatedPassManager() = default; + +std::unique_ptr RepeatedPassManager::Create( + RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) { + switch (strategy) { + case RepeatedPassStrategy::kSimple: + return MakeUnique(fuzzer_context, + pass_instances); + case RepeatedPassStrategy::kLoopedWithRecommendations: + return MakeUnique( + fuzzer_context, pass_instances, pass_recommender); + case RepeatedPassStrategy::kRandomWithRecommendations: + return MakeUnique( + fuzzer_context, pass_instances, pass_recommender); + } + + assert(false && "Unreachable"); + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h new file mode 100644 index 00000000..1e6ae3e4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" + +namespace spvtools { +namespace fuzz { + +// Each field of this enum corresponds to an available repeated pass +// strategy, and is used to decide which kind of RepeatedPassManager object +// to create. +enum class RepeatedPassStrategy { + kSimple, + kRandomWithRecommendations, + kLoopedWithRecommendations +}; + +// An interface to encapsulate the manner in which the sequence of repeated +// passes that are applied during fuzzing is chosen. An implementation of this +// interface could, for example, keep track of the history of passes that have +// been run and bias the selection of future passes according to this history. +class RepeatedPassManager { + public: + RepeatedPassManager(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances); + + virtual ~RepeatedPassManager(); + + // Returns the fuzzer pass instance that should be run next. The + // transformations that have been applied so far are provided via + // |applied_transformations| and can be used to influence the decision. + virtual FuzzerPass* ChoosePass( + const protobufs::TransformationSequence& applied_transformations) = 0; + + // Creates a corresponding RepeatedPassManager based on the |strategy|. + static std::unique_ptr Create( + RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + + protected: + FuzzerContext* GetFuzzerContext() { return fuzzer_context_; } + + RepeatedPassInstances* GetPassInstances() { return pass_instances_; } + + private: + // Provided in order to allow the pass manager to make random decisions. + FuzzerContext* fuzzer_context_; + + // The repeated fuzzer passes that are enabled. + RepeatedPassInstances* pass_instances_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp new file mode 100644 index 00000000..e91ba353 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerLoopedWithRecommendations:: + RepeatedPassManagerLoopedWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) + : RepeatedPassManager(fuzzer_context, pass_instances), + num_transformations_applied_before_last_pass_choice_(0), + next_pass_index_(0) { + auto& passes = GetPassInstances()->GetPasses(); + do { + FuzzerPass* current_pass = + passes[GetFuzzerContext()->RandomIndex(passes)].get(); + pass_loop_.push_back(current_pass); + for (auto future_pass : + pass_recommender->GetFuturePassRecommendations(*current_pass)) { + recommended_pass_indices_.insert( + static_cast(pass_loop_.size())); + pass_loop_.push_back(future_pass); + } + } while (fuzzer_context->ChoosePercentage( + fuzzer_context->GetChanceOfAddingAnotherPassToPassLoop())); +} + +RepeatedPassManagerLoopedWithRecommendations:: + ~RepeatedPassManagerLoopedWithRecommendations() = default; + +FuzzerPass* RepeatedPassManagerLoopedWithRecommendations::ChoosePass( + const protobufs::TransformationSequence& applied_transformations) { + assert((next_pass_index_ > 0 || + recommended_pass_indices_.count(next_pass_index_) == 0) && + "The first pass in the loop should not be a recommendation."); + assert(static_cast(applied_transformations.transformation_size()) >= + num_transformations_applied_before_last_pass_choice_ && + "The number of applied transformations should not decrease."); + if (num_transformations_applied_before_last_pass_choice_ == + static_cast(applied_transformations.transformation_size())) { + // The last pass that was applied did not lead to any new transformations. + // We thus do not want to apply recommendations based on it, so we skip on + // to the next non-recommended pass. + while (recommended_pass_indices_.count(next_pass_index_)) { + next_pass_index_ = + (next_pass_index_ + 1) % static_cast(pass_loop_.size()); + } + } + auto result = pass_loop_[next_pass_index_]; + next_pass_index_ = + (next_pass_index_ + 1) % static_cast(pass_loop_.size()); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h new file mode 100644 index 00000000..42ce38e3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ + +#include + +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// On construction, this pass manager creates a sequence of fuzzer passes which +// is not changed thereafter. Passes from this sequence are served up in round +// robin fashion each time ChoosePass is invoked - i.e., the sequence is a "pass +// loop". +// +// The pass loop is constructed by repeatedly: +// - Randomly adding an enabled pass +// - Adding all recommended follow-on passes for this pass +// and probabilistically terminating this process. +class RepeatedPassManagerLoopedWithRecommendations + : public RepeatedPassManager { + public: + RepeatedPassManagerLoopedWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + + ~RepeatedPassManagerLoopedWithRecommendations() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; + + private: + // The loop of fuzzer passes to be applied, populated on construction. + std::vector pass_loop_; + + // A set of indices into |pass_loop_| recording which passes are in the loop + // because they are recommended based on previous passes in the loop. This + // allows these recommended passes to be skipped if the passes they are + // meant to amplify had no effect. + std::unordered_set recommended_pass_indices_; + + // Used to detect when chosen passes have had no effect, so that their + // associated recommendations are skipped. + uint32_t num_transformations_applied_before_last_pass_choice_; + + // An index into |pass_loop_| specifying which pass should be served up next + // time ChoosePass is invoked. + uint32_t next_pass_index_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp new file mode 100644 index 00000000..6d77f25a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerRandomWithRecommendations:: + RepeatedPassManagerRandomWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) + : RepeatedPassManager(fuzzer_context, pass_instances), + pass_recommender_(pass_recommender), + num_transformations_applied_before_last_pass_choice_(0), + last_pass_choice_(nullptr) {} + +RepeatedPassManagerRandomWithRecommendations:: + ~RepeatedPassManagerRandomWithRecommendations() = default; + +FuzzerPass* RepeatedPassManagerRandomWithRecommendations::ChoosePass( + const protobufs::TransformationSequence& applied_transformations) { + assert(static_cast(applied_transformations.transformation_size()) >= + num_transformations_applied_before_last_pass_choice_ && + "The number of applied transformations should not decrease."); + if (last_pass_choice_ != nullptr && + static_cast(applied_transformations.transformation_size()) > + num_transformations_applied_before_last_pass_choice_) { + // The last pass had some effect, so we make future recommendations based on + // it. + for (auto future_pass : + pass_recommender_->GetFuturePassRecommendations(*last_pass_choice_)) { + recommended_passes_.push_back(future_pass); + } + } + + FuzzerPass* result; + if (recommended_passes_.empty() || GetFuzzerContext()->ChooseEven()) { + auto& passes = GetPassInstances()->GetPasses(); + result = passes[GetFuzzerContext()->RandomIndex(passes)].get(); + } else { + result = recommended_passes_.front(); + recommended_passes_.pop_front(); + } + assert(result != nullptr && "A pass must have been chosen."); + last_pass_choice_ = result; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h new file mode 100644 index 00000000..5dbd4554 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ + +#include + +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// This repeated pass manager uses a pass recommender to recommend future passes +// each time a fuzzer pass is run. It keeps a queue of recommended passes. +// +// Each time a fuzzer pass is requested, the manager either selects an enabled +// fuzzer pass at random, or selects the pass at the front of the recommendation +// queue, removing it from the queue. The decision of which of these pass +// selection methods to use is made randomly each time ChoosePass is called. +// +// Either way, recommended follow-on passes for the chosen pass are added to +// the recommendation queue. +class RepeatedPassManagerRandomWithRecommendations + : public RepeatedPassManager { + public: + RepeatedPassManagerRandomWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + + ~RepeatedPassManagerRandomWithRecommendations() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; + + private: + // The queue of passes that have been recommended based on previously-chosen + // passes. + std::deque recommended_passes_; + + // Used to recommend future passes. + RepeatedPassRecommender* pass_recommender_; + + // Used to detect when chosen passes have had no effect, so that their + // associated recommendations are skipped. + uint32_t num_transformations_applied_before_last_pass_choice_; + + // The fuzzer pass returned last time ChoosePass() was called; nullptr if + // ChoosePass() has not yet been called. + FuzzerPass* last_pass_choice_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp new file mode 100644 index 00000000..c593ffe5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerSimple::RepeatedPassManagerSimple( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances) + : RepeatedPassManager(fuzzer_context, pass_instances) {} + +RepeatedPassManagerSimple::~RepeatedPassManagerSimple() = default; + +FuzzerPass* RepeatedPassManagerSimple::ChoosePass( + const protobufs::TransformationSequence& /*unused*/) { + auto& passes = GetPassInstances()->GetPasses(); + return passes[GetFuzzerContext()->RandomIndex(passes)].get(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h new file mode 100644 index 00000000..a4cc6520 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ + +#include "source/fuzz/pass_management/repeated_pass_manager.h" + +namespace spvtools { +namespace fuzz { + +// Selects the next pass to run uniformly at random from the enabled repeated +// passes. Recommendations are not used. +class RepeatedPassManagerSimple : public RepeatedPassManager { + public: + RepeatedPassManagerSimple(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances); + + ~RepeatedPassManagerSimple() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp new file mode 100644 index 00000000..c7789dc0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassRecommender::~RepeatedPassRecommender() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h new file mode 100644 index 00000000..a6b13385 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ +#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Interface for influencing interactions between repeated fuzzer passes, by +// allowing hints as to which passes are recommended to be run after one +// another. +class RepeatedPassRecommender { + public: + virtual ~RepeatedPassRecommender(); + + // Given a reference to a repeated pass, |pass|, returns a sequence of + // repeated pass instances that might be worth running soon after having + // run |pass|. + virtual std::vector GetFuturePassRecommendations( + const FuzzerPass& pass) = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp new file mode 100644 index 00000000..6c61c0d4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -0,0 +1,383 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" + +#include + +namespace spvtools { +namespace fuzz { + +RepeatedPassRecommenderStandard::RepeatedPassRecommenderStandard( + RepeatedPassInstances* pass_instances, FuzzerContext* fuzzer_context) + : pass_instances_(pass_instances), fuzzer_context_(fuzzer_context) {} + +RepeatedPassRecommenderStandard::~RepeatedPassRecommenderStandard() = default; + +std::vector +RepeatedPassRecommenderStandard::GetFuturePassRecommendations( + const FuzzerPass& pass) { + if (&pass == pass_instances_->GetAddAccessChains()) { + // - Adding access chains means there is more scope for loading and storing + // - It could be worth making more access chains from the recently-added + // access chains + return RandomOrderAndNonNull({pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores(), + pass_instances_->GetAddAccessChains()}); + } + if (&pass == pass_instances_->GetAddBitInstructionSynonyms()) { + // - Adding bit instruction synonyms creates opportunities to apply synonyms + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddCompositeExtract()) { + // - This transformation can introduce synonyms to the fact manager. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddCompositeInserts()) { + // - Having added inserts we will have more vectors, so there is scope for + // vector shuffling + // - Adding inserts creates synonyms, which we should try to use + // - Vector inserts can be made dynamic + return RandomOrderAndNonNull( + {pass_instances_->GetAddVectorShuffleInstructions(), + pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetMakeVectorOperationsDynamic()}); + } + if (&pass == pass_instances_->GetAddCompositeTypes()) { + // - More composite types gives more scope for constructing composites + return RandomOrderAndNonNull({pass_instances_->GetConstructComposites()}); + } + if (&pass == pass_instances_->GetAddCopyMemory()) { + // - Recently-added copy memories could be replace with load-store pairs + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()}); + } + if (&pass == pass_instances_->GetAddDeadBlocks()) { + // - Dead blocks are great for adding function calls + // - Dead blocks are also great for adding loads and stores + // - The guard associated with a dead block can be obfuscated + // - Branches from dead blocks may be replaced with exits + return RandomOrderAndNonNull( + {pass_instances_->GetAddFunctionCalls(), pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores(), + pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()}); + } + if (&pass == pass_instances_->GetAddDeadBreaks()) { + // - The guard of the dead break is a good candidate for obfuscation + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()}); + } + if (&pass == pass_instances_->GetAddDeadContinues()) { + // - The guard of the dead continue is a good candidate for obfuscation + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()}); + } + if (&pass == pass_instances_->GetAddEquationInstructions()) { + // - Equation instructions can create synonyms, which we can apply + // - Equation instructions collaborate with one another to make synonyms, so + // having added some it is worth adding more + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetAddEquationInstructions()}); + } + if (&pass == pass_instances_->GetAddFunctionCalls()) { + // - Called functions can be inlined + // - Irrelevant ids are created, so they can be replaced + return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddGlobalVariables()) { + // - New globals provide new possibilities for making access chains + // - We can load from and store to new globals + return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(), + pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores()}); + } + if (&pass == pass_instances_->GetAddImageSampleUnusedComponents()) { + // - This introduces an unused component whose id is irrelevant and can be + // replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddLoads()) { + // - Loads might end up with corresponding stores, so that pairs can be + // replaced with memory copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetAddLocalVariables()) { + // - New locals provide new possibilities for making access chains + // - We can load from and store to new locals + return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(), + pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores()}); + } + if (&pass == pass_instances_->GetAddLoopPreheaders()) { + // - The loop preheader provides more scope for duplicating regions and + // outlining functions. + return RandomOrderAndNonNull( + {pass_instances_->GetDuplicateRegionsWithSelections(), + pass_instances_->GetOutlineFunctions(), + pass_instances_->GetWrapRegionsInSelections()}); + } + if (&pass == pass_instances_->GetAddLoopsToCreateIntConstantSynonyms()) { + // - New synonyms can be applied + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddOpPhiSynonyms()) { + // - New synonyms can be applied + // - If OpPhi synonyms are introduced for blocks with dead predecessors, the + // values consumed from dead predecessors can be replaced + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()}); + } + if (&pass == pass_instances_->GetAddParameters()) { + // - We might be able to create interesting synonyms of new parameters. + // - This introduces irrelevant ids, which can be replaced + return RandomOrderAndNonNull({pass_instances_->GetAddSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddRelaxedDecorations()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetAddStores()) { + // - Stores might end up with corresponding loads, so that pairs can be + // replaced with memory copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetAddSynonyms()) { + // - New synonyms can be applied + // - Synonym instructions use constants, which can be obfuscated + // - Synonym instructions use irrelevant ids, which can be replaced + // - Synonym instructions introduce addition/subtraction, which can be + // replaced with carrying/extended versions + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddVectorShuffleInstructions()) { + // - Vector shuffles create synonyms that can be applied + // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806) Extract + // from composites. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetApplyIdSynonyms()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetConstructComposites()) { + // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806): Extract + // from composites. + return RandomOrderAndNonNull({}); + } + if (&pass == pass_instances_->GetCopyObjects()) { + // - Object copies create synonyms that can be applied + // - OpCopyObject can be replaced with a store/load pair + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceCopyObjectsWithStoresLoads()}); + } + if (&pass == pass_instances_->GetDonateModules()) { + // - New functions in the module can be called + // - Donated dead functions produce irrelevant ids, which can be replaced + // - Donated functions are good candidates for having their returns merged + // - Donated dead functions may allow branches to be replaced with exits + return RandomOrderAndNonNull( + {pass_instances_->GetAddFunctionCalls(), + pass_instances_->GetReplaceIrrelevantIds(), + pass_instances_->GetMergeFunctionReturns(), + pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()}); + } + if (&pass == pass_instances_->GetDuplicateRegionsWithSelections()) { + // - Parts of duplicated regions can be outlined + return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()}); + } + if (&pass == pass_instances_->GetExpandVectorReductions()) { + // - Adding OpAny and OpAll synonyms creates opportunities to apply synonyms + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetFlattenConditionalBranches()) { + // - Parts of flattened selections can be outlined + // - The flattening transformation introduces constants and irrelevant ids + // for enclosing hard-to-flatten operations; these can be obfuscated or + // replaced + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants(), + pass_instances_->GetOutlineFunctions(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetInlineFunctions()) { + // - Parts of inlined functions can be outlined again + return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()}); + } + if (&pass == pass_instances_->GetInvertComparisonOperators()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetMakeVectorOperationsDynamic()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetMergeBlocks()) { + // - Having merged some blocks it may be interesting to split them in a + // different way + return RandomOrderAndNonNull({pass_instances_->GetSplitBlocks()}); + } + if (&pass == pass_instances_->GetMergeFunctionReturns()) { + // - Functions without early returns are more likely to be able to be + // inlined. + return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions()}); + } + if (&pass == pass_instances_->GetMutatePointers()) { + // - This creates irrelevant ids, which can be replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetObfuscateConstants()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetOutlineFunctions()) { + // - This creates more functions, which can be called + // - Inlining the function for the region that was outlined might also be + // fruitful; it will be inlined in a different form + return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(), + pass_instances_->GetInlineFunctions()}); + } + if (&pass == pass_instances_->GetPermuteBlocks()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPermuteFunctionParameters()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPermuteInstructions()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPropagateInstructionsDown()) { + // - This fuzzer pass might create new synonyms that can later be applied. + // - This fuzzer pass might create irrelevant ids that can later be + // replaced. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetPropagateInstructionsUp()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPushIdsThroughVariables()) { + // - This pass creates synonyms, so it is worth applying them + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()) { + // - Changing a branch to OpReturnValue introduces an irrelevant id, which + // can be replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceCopyObjectsWithStoresLoads()) { + // - We may end up with load/store pairs that could be used to create memory + // copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetReplaceIrrelevantIds()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceLinearAlgebraInstructions()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceLoadsStoresWithCopyMemories()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceOpSelectsWithConditionalBranches()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceParameterWithGlobal()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceParamsWithStruct()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetSplitBlocks()) { + // - More blocks means more chances for adding dead breaks/continues, and + // for adding dead blocks + return RandomOrderAndNonNull({pass_instances_->GetAddDeadBreaks(), + pass_instances_->GetAddDeadContinues(), + pass_instances_->GetAddDeadBlocks()}); + } + if (&pass == pass_instances_->GetSwapBranchConditionalOperands()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetWrapRegionsInSelections()) { + // - This pass uses an irrelevant boolean constant - we can replace it with + // something more interesting. + // - We can obfuscate that very constant as well. + // - We can flatten created selection construct. + return RandomOrderAndNonNull( + {pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceIrrelevantIds(), + pass_instances_->GetFlattenConditionalBranches()}); + } + if (&pass == pass_instances_->GetWrapVectorSynonym()) { + // This transformation introduces synonym facts and irrelevant ids. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + + assert(false && "Unreachable: every fuzzer pass should be dealt with."); + return {}; +} + +std::vector RepeatedPassRecommenderStandard::RandomOrderAndNonNull( + const std::vector& passes) { + std::vector indices(passes.size()); + std::iota(indices.begin(), indices.end(), 0); + std::vector result; + while (!indices.empty()) { + FuzzerPass* maybe_pass = + passes[fuzzer_context_->RemoveAtRandomIndex(&indices)]; + if (maybe_pass != nullptr && + fuzzer_context_->ChoosePercentage( + fuzzer_context_ + ->GetChanceOfAcceptingRepeatedPassRecommendation())) { + result.push_back(maybe_pass); + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h new file mode 100644 index 00000000..293b8e0f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ +#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// A manually-crafter recommender of repeated passes, designed based on +// knowledge of how the various fuzzer passes work and speculation as to how +// they might interact in interesting ways. +class RepeatedPassRecommenderStandard : public RepeatedPassRecommender { + public: + RepeatedPassRecommenderStandard(RepeatedPassInstances* pass_instances, + FuzzerContext* fuzzer_context); + + ~RepeatedPassRecommenderStandard(); + + std::vector GetFuturePassRecommendations( + const FuzzerPass& pass) override; + + private: + std::vector RandomOrderAndNonNull( + const std::vector& passes); + + RepeatedPassInstances* pass_instances_; + + FuzzerContext* fuzzer_context_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h new file mode 100644 index 00000000..44aecfd6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ +#define SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ + +// This header file serves to act as a barrier between the protobuf header +// files and files that include them. It uses compiler pragmas to disable +// diagnostics, in order to ignore warnings generated during the processing +// of these header files without having to compromise on freedom from warnings +// in the rest of the project. + +#ifndef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE +#define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE 1 +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" // Must come first +#pragma clang diagnostic ignored "-Wreserved-identifier" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsuggest-destructor-override" +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi" +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + +// The following should be the only place in the project where protobuf files +// are directly included. This is so that they can be compiled in a manner +// where warnings are ignored. + +#include "google/protobuf/util/json_util.h" +#include "google/protobuf/util/message_differencer.h" +#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h" + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto new file mode 100644 index 00000000..e71b6a3f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -0,0 +1,2428 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is specifically named spvtools_fuzz.proto so that the string +// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc +// generates when targeting C++. This is to reduce the potential for name +// clashes with other globally-scoped symbols. + +syntax = "proto3"; + +package spvtools.fuzz.protobufs; + +message UInt32Pair { + + // A pair of uint32s; useful for defining mappings. + + uint32 first = 1; + + uint32 second = 2; + +} + +message InstructionDescriptor { + + // Describes an instruction in some block of a function with respect to a + // base instruction. + + // The id of an instruction after which the instruction being described is + // believed to be located. It might be the using instruction itself. + uint32 base_instruction_result_id = 1; + + // The opcode for the instruction being described. + uint32 target_instruction_opcode = 2; + + // The number of matching opcodes to skip over when searching from the base + // instruction to the instruction being described. + uint32 num_opcodes_to_ignore = 3; + +} + +message IdUseDescriptor { + + // Describes a use of an id as an input operand to an instruction in some + // block of a function. + + // Example: + // - id_of_interest = 42 + // - enclosing_instruction = ( + // base_instruction_result_id = 50, + // target_instruction_opcode = OpStore + // num_opcodes_to_ignore = 7 + // ) + // - in_operand_index = 1 + // represents a use of id 42 as input operand 1 to an OpStore instruction, + // such that the OpStore instruction can be found in the same basic block as + // the instruction with result id 50, and in particular is the 8th OpStore + // instruction found from instruction 50 onwards (i.e. 7 OpStore + // instructions are skipped). + + // An id that we would like to be able to find a use of. + uint32 id_of_interest = 1; + + // The input operand index at which the use is expected. + InstructionDescriptor enclosing_instruction = 2; + + uint32 in_operand_index = 3; + +} + +message DataDescriptor { + + // Represents a data element that can be accessed from an id, by walking the + // type hierarchy via a sequence of 0 or more indices. + // + // Very similar to a UniformBufferElementDescriptor, except that a + // DataDescriptor is rooted at the id of a scalar or composite. + + // The object being accessed - a scalar or composite + uint32 object = 1; + + // 0 or more indices, used to index into a composite object + repeated uint32 index = 2; + +} + +message UniformBufferElementDescriptor { + + // Represents a data element inside a uniform buffer. The element is + // specified via (a) the result id of a uniform variable in which the element + // is contained, and (b) a series of indices that need to be followed to get + // to the element (via fields and array/vector indices). + // + // Example: suppose there is a uniform variable with descriptor set 7 and + // binding 9, and that the uniform variable has the following type (using + // GLSL-like syntax): + // + // struct S { + // float f; + // vec3 g; + // int4 h[10]; + // }; + // + // Then: + // - (7, 9, [0]) describes the 'f' field. + // - (7, 9, [1,1]) describes the y component of the 'g' field. + // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field + + // The descriptor set and binding associated with a uniform variable. + uint32 descriptor_set = 1; + uint32 binding = 2; + + // An ordered sequence of indices through composite structures in the + // uniform buffer. + repeated uint32 index = 3; + +} + +message InstructionOperand { + + // Represents an operand to a SPIR-V instruction. + + // The type of the operand. + uint32 operand_type = 1; + + // The data associated with the operand. For most operands (e.g. ids, + // storage classes and literals) this will be a single word. + repeated uint32 operand_data = 2; + +} + +message Instruction { + + // Represents a SPIR-V instruction. + + // The instruction's opcode (e.g. OpLabel). + uint32 opcode = 1; + + // The id of the instruction's result type; 0 if there is no result type. + uint32 result_type_id = 2; + + // The id of the instruction's result; 0 if there is no result. + uint32 result_id = 3; + + // Zero or more input operands. + repeated InstructionOperand input_operand = 4; + +} + +message FactSequence { + repeated Fact fact = 1; +} + +message Fact { + oneof fact { + // Order the fact options by numeric id (rather than alphabetically). + FactConstantUniform constant_uniform_fact = 1; + FactDataSynonym data_synonym_fact = 2; + FactBlockIsDead block_is_dead_fact = 3; + FactFunctionIsLivesafe function_is_livesafe_fact = 4; + FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5; + FactIdEquation id_equation_fact = 6; + FactIdIsIrrelevant id_is_irrelevant = 7; + } +} + +// Keep fact message types in alphabetical order: + +message FactBlockIsDead { + + // Records the fact that a block is guaranteed to be dynamically unreachable. + // This is useful because it informs the fuzzer that rather arbitrary changes + // can be made to this block. + + uint32 block_id = 1; + +} + +message FactConstantUniform { + + // Records the fact that a uniform buffer element is guaranteed to be equal + // to a particular constant value. spirv-fuzz can use such guarantees to + // obfuscate code, e.g. to manufacture an expression that will (due to the + // guarantee) evaluate to a particular value at runtime but in a manner that + // cannot be predicted at compile-time. + + // An element of a uniform buffer + UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1; + + // The words of the associated constant + repeated uint32 constant_word = 2; + +} + +message FactDataSynonym { + + // Records the fact that the data held in two data descriptors are guaranteed + // to be equal. spirv-fuzz can use this to replace uses of one piece of data + // with a known-to-be-equal piece of data. + + // Data descriptors guaranteed to hold identical data. + DataDescriptor data1 = 1; + + DataDescriptor data2 = 2; + +} + +message FactFunctionIsLivesafe { + + // Records the fact that a function is guaranteed to be "livesafe", meaning + // that it will not make out-of-bounds accesses, does not contain reachable + // OpKill or OpUnreachable instructions, does not contain loops that will + // execute for large numbers of iterations, and only invokes other livesafe + // functions. + + uint32 function_id = 1; + +} + +message FactIdEquation { + + // Records the fact that the equation: + // + // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1] + // + // holds; e.g. that the equation: + // + // %12 = OpIAdd %13 %14 + // + // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is + // OpIAdd. + + // The left-hand-side of the equation. + uint32 lhs_id = 1; + + // A SPIR-V opcode, from a restricted set of instructions for which equation + // facts make sense. + uint32 opcode = 2; + + // The operands to the right-hand-side of the equation. + repeated uint32 rhs_id = 3; + +} + +message FactIdIsIrrelevant { + + // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't + // change the semantics of the module). This implies that a use of this id + // can later be replaced with some other id of the same type, or the + // definition of |result_id| can be changed so that it yields a different value. + + // An irrelevant id. + uint32 result_id = 1; + +} + +message FactPointeeValueIsIrrelevant { + + // Records the fact that value of the data pointed to by a pointer id does + // not influence the observable behaviour of the module. This means that + // arbitrary stores can be made through the pointer, and that nothing can be + // guaranteed about the values that are loaded via the pointer. + + // A result id of pointer type + uint32 pointer_id = 1; + +} + +message AccessChainClampingInfo { + + // When making a function livesafe it is necessary to clamp the indices that + // occur as operands to access chain instructions so that they are guaranteed + // to be in bounds. This message type allows an access chain instruction to + // have an associated sequence of ids that are reserved for comparing an + // access chain index with a bound (e.g. an array size), and selecting + // between the access chain index (if it is within bounds) and the bound (if + // it is not). + // + // This allows turning an instruction of the form: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + + // The result id of an OpAccessChain or OpInBoundsAccessChain instruction. + uint32 access_chain_id = 1; + + // A series of pairs of fresh ids, one per access chain index, for the results + // of a compare instruction and a select instruction, serving the roles of %t1 + // and %t2 in the above example. + repeated UInt32Pair compare_and_select_ids = 2; + +} + +message SideEffectWrapperInfo { + // When flattening a conditional branch, it is necessary to enclose + // instructions that have side effects inside conditionals, so that + // they are only executed if the condition holds. Otherwise, there + // might be unintended changes in memory, or crashes that would not + // originally happen. + // For example, the instruction %id = OpLoad %type %ptr, found in + // the true branch of the conditional, will be enclosed in a new + // conditional (assuming that the block containing it can be split + // around it) as follows: + // + // [previous instructions in the block] + // OpSelectionMerge %merge_block_id None + // OpBranchConditional %cond %execute_block_id %alternative_block_id + // %execute_block_id = OpLabel + // %actual_result_id = OpLoad %type %ptr + // OpBranch %merge_block_id + // %alternative_block_id = OpLabel + // %placeholder_result_id = OpCopyObject %type %value_to_copy_id + // OpBranch %merge_block_id + // %merge_block_id = OpLabel + // %id = OpPhi %type %actual_result_id %execute_block_id %placeholder_result_id %alternative_block_id + // [following instructions from the original block] + // + // If the instruction does not have a result id, this is simplified. + // For example, OpStore %ptr %value, found in the true branch of a + // conditional, is enclosed as follows: + // + // [previous instructions in the block] + // OpSelectionMerge %merge_block None + // OpBranchConditional %cond %execute_block_id %merge_block_id + // %execute_block_id = OpLabel + // OpStore %ptr %value + // OpBranch %merge_block_id + // %merge_block_id = OpLabel + // [following instructions from the original block] + // + // The same happens if the instruction is found in the false branch + // of the conditional being flattened, except that the label ids in + // the OpBranchConditional are swapped. + + + // An instruction descriptor for identifying the instruction to be + // enclosed inside a conditional. An instruction descriptor is + // necessary because the instruction might not have a result id. + InstructionDescriptor instruction = 1; + + // A fresh id for the new merge block. + uint32 merge_block_id = 2; + + // A fresh id for the new block where the actual instruction is + // executed. + uint32 execute_block_id = 3; + + // The following fields are only needed if the original instruction has a + // result id. They can be set to 0 if not needed. + + // A fresh id for the result id of the instruction (the original + // one is used by the OpPhi instruction). + uint32 actual_result_id = 4; + + // A fresh id for the new block where the placeholder instruction + // is placed. + uint32 alternative_block_id = 5; + + // A fresh id for the placeholder instruction. + uint32 placeholder_result_id = 6; + + // An id present in the module, available to use at this point in + // the program and with the same type as the original instruction, + // that can be used to create a placeholder OpCopyObject + // instruction. + uint32 value_to_copy_id = 7; +} + +message ReturnMergingInfo { + // TransformationMergeFunctionReturns needs to modify each merge block of + // loops containing return instructions, by: + // - adding instructions to decide whether the function is returning + // - adding instructions to pass on the return value of the function, + // if it is returning + // - changing the branch instruction (which must be an unconditional branch) + // to a conditional branch that, if the function is returning, branches to + // the merge block of the innermost loop that contains this merge block + // (which can be the new merge block introduced by the transformation). + // + // One such merge block of the form: + // %block = OpLabel + // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 + // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 + // OpBranch %next + // + // is transformed into: + // %block = OpLabel + // %is_returning_id = OpPhi %bool %false %pred1 %false %pred2 %true %ret_bb1 %is_bb2_returning %mer_bb2 + // %maybe_return_val_id = OpPhi %return_type %any_returnable_val %pred1 %any_returnable_val %pred2 + // %ret_val1 %ret_bb1 %ret_val2 %mer_bb2 + // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 + // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 + // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 + // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 + // OpBranchConditional %is_returning_id %innermost_loop_merge %next + // + // where %ret_bb1 is a block that originally contains a return instruction and %mer_bb2 is the merge block of an inner + // loop, from where the function might be returning. + // + // Note that the block is required to only have OpLabel, OpPhi or OpBranch instructions. + + // The id of the merge block that needs to be modified. + uint32 merge_block_id = 1; + + // A fresh id for a boolean OpPhi whose value will be true iff the function + // is returning. This will be used to decide whether to break out of the loop + // or to use the original branch of the function. This value will also be + // used by the merge block of the enclosing loop (if there is one) if the + // function is returning from this block. + uint32 is_returning_id = 2; + + // A fresh id that will get the value being returned, if the function is + // returning. If the function return type is void, this is ignored. + uint32 maybe_return_val_id = 3; + + // A mapping from each existing OpPhi id to a suitable id of the same type + // available to use before the instruction. + repeated UInt32Pair opphi_to_suitable_id = 4; +} + +message LoopLimiterInfo { + + // Structure capturing the information required to manipulate a loop limiter + // at a loop header. + + // The header for the loop. + uint32 loop_header_id = 1; + + // A fresh id into which the loop limiter's current value can be loaded. + uint32 load_id = 2; + + // A fresh id that can be used to increment the loaded value by 1. + uint32 increment_id = 3; + + // A fresh id that can be used to compare the loaded value with the loop + // limit. + uint32 compare_id = 4; + + // A fresh id that can be used to compute the conjunction or disjunction of + // an original loop exit condition with |compare_id|, if the loop's back edge + // block can conditionally exit the loop. + uint32 logical_op_id = 5; + + // A sequence of ids suitable for extending OpPhi instructions of the loop + // merge block if it did not previously have an incoming edge from the loop + // back edge block. + repeated uint32 phi_id = 6; + +} + +message TransformationSequence { + repeated Transformation transformation = 1; +} + +message Transformation { + oneof transformation { + // Order the transformation options by numeric id (rather than + // alphabetically). + TransformationMoveBlockDown move_block_down = 1; + TransformationSplitBlock split_block = 2; + TransformationAddConstantBoolean add_constant_boolean = 3; + TransformationAddConstantScalar add_constant_scalar = 4; + TransformationAddTypeBoolean add_type_boolean = 5; + TransformationAddTypeFloat add_type_float = 6; + TransformationAddTypeInt add_type_int = 7; + TransformationAddDeadBreak add_dead_break = 8; + TransformationReplaceBooleanConstantWithConstantBinary + replace_boolean_constant_with_constant_binary = 9; + TransformationAddTypePointer add_type_pointer = 10; + TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; + TransformationAddDeadContinue add_dead_continue = 12; + TransformationReplaceIdWithSynonym replace_id_with_synonym = 13; + TransformationSetSelectionControl set_selection_control = 14; + TransformationCompositeConstruct composite_construct = 15; + TransformationSetLoopControl set_loop_control = 16; + TransformationSetFunctionControl set_function_control = 17; + TransformationAddNoContractionDecoration add_no_contraction_decoration = 18; + TransformationSetMemoryOperandsMask set_memory_operands_mask = 19; + TransformationCompositeExtract composite_extract = 20; + TransformationVectorShuffle vector_shuffle = 21; + TransformationOutlineFunction outline_function = 22; + TransformationMergeBlocks merge_blocks = 23; + TransformationAddTypeVector add_type_vector = 24; + TransformationAddTypeArray add_type_array = 25; + TransformationAddTypeMatrix add_type_matrix = 26; + TransformationAddTypeStruct add_type_struct = 27; + TransformationAddTypeFunction add_type_function = 28; + TransformationAddConstantComposite add_constant_composite = 29; + TransformationAddGlobalVariable add_global_variable = 30; + TransformationAddGlobalUndef add_global_undef = 31; + TransformationAddFunction add_function = 32; + TransformationAddDeadBlock add_dead_block = 33; + TransformationAddLocalVariable add_local_variable = 34; + TransformationLoad load = 35; + TransformationStore store = 36; + TransformationFunctionCall function_call = 37; + TransformationAccessChain access_chain = 38; + TransformationEquationInstruction equation_instruction = 39; + TransformationSwapCommutableOperands swap_commutable_operands = 40; + TransformationPermuteFunctionParameters permute_function_parameters = 41; + TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42; + TransformationAddConstantNull add_constant_null = 43; + TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44; + TransformationAdjustBranchWeights adjust_branch_weights = 45; + TransformationPushIdThroughVariable push_id_through_variable = 46; + TransformationAddSpecConstantOp add_spec_constant_op = 47; + TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48; + TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49; + TransformationPermutePhiOperands permute_phi_operands = 50; + TransformationAddParameter add_parameter = 51; + TransformationAddCopyMemory add_copy_memory = 52; + TransformationInvertComparisonOperator invert_comparison_operator = 53; + TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54; + TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55; + TransformationRecordSynonymousConstants record_synonymous_constants = 56; + TransformationAddSynonym add_synonym = 57; + TransformationAddRelaxedDecoration add_relaxed_decoration = 58; + TransformationReplaceParamsWithStruct replace_params_with_struct = 59; + TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60; + TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61; + TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62; + TransformationAddLoopPreheader add_loop_preheader = 63; + TransformationMoveInstructionDown move_instruction_down = 64; + TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65; + TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66; + TransformationPropagateInstructionUp propagate_instruction_up = 67; + TransformationCompositeInsert composite_insert = 68; + TransformationInlineFunction inline_function = 69; + TransformationAddOpPhiSynonym add_opphi_synonym = 70; + TransformationMutatePointer mutate_pointer = 71; + TransformationReplaceIrrelevantId replace_irrelevant_id = 72; + TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73; + TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74; + TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75; + TransformationFlattenConditionalBranch flatten_conditional_branch = 76; + TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77; + TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78; + TransformationWrapRegionInSelection wrap_region_in_selection = 79; + TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80; + TransformationPropagateInstructionDown propagate_instruction_down = 81; + TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82; + TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83; + TransformationMergeFunctionReturns merge_function_returns = 84; + TransformationExpandVectorReduction expand_vector_reduction = 85; + TransformationSwapFunctionVariables swap_function_variables = 86; + TransformationSwapTwoFunctions swap_two_functions = 87; + TransformationWrapVectorSynonym wrap_vector_synonym = 88; + // Add additional option using the next available number. + } +} + +// Keep transformation message types in alphabetical order: + +message TransformationAccessChain { + + // Adds an access chain instruction based on a given pointer and indices. + + // When accessing a struct, the corresponding indices must be 32-bit integer constants. + // For any other composite, the indices can be any 32-bit integer, and the transformation + // adds two instructions for each such index to clamp it to the bound, as follows: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + + // Result id for the access chain + uint32 fresh_id = 1; + + // The pointer from which the access chain starts + uint32 pointer_id = 2; + + // Zero or more access chain indices + repeated uint32 index_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpAccessChain instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + + // Additional fresh ids, required to clamp index variables. A pair is needed + // for each access to a non-struct composite. + repeated UInt32Pair fresh_ids_for_clamping = 5; + +} + +message TransformationAddBitInstructionSynonym { + + // A transformation that adds synonyms for bit instructions by evaluating + // each bit with the corresponding operation. There is a SPIR-V code example in the + // header file of the transformation class that can help understand the transformation. + + // This transformation is only applicable if the described instruction has one of the following opcodes. + // Supported: + // OpBitwiseOr + // OpBitwiseXor + // OpBitwiseAnd + // OpNot + // To be supported in the future: + // OpShiftRightLogical + // OpShiftRightArithmetic + // OpShiftLeftLogical + // OpBitReverse + // OpBitCount + + // The bit instruction result id. + uint32 instruction_result_id = 1; + + // The fresh ids required to apply the transformation. + repeated uint32 fresh_ids = 2; + +} + +message TransformationAddConstantBoolean { + + // Supports adding the constants true and false to a module, which may be + // necessary in order to enable other transformations if they are not present. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. + + uint32 fresh_id = 1; + bool is_true = 2; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 3; + +} + +message TransformationAddConstantComposite { + + // Adds a constant of the given composite type to the module. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if + // |is_irrelevant| is true. + + // Fresh id for the composite + uint32 fresh_id = 1; + + // A composite type id + uint32 type_id = 2; + + // Constituent ids for the composite + repeated uint32 constituent_id = 3; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + +} + +message TransformationAddConstantNull { + + // Adds a null constant. + + // Id for the constant + uint32 fresh_id = 1; + + // Type of the constant + uint32 type_id = 2; + +} + +message TransformationAddConstantScalar { + + // Adds a constant of the given scalar type. + // Also, creates an IdIsIrrelevant fact about + // |fresh_id| if |is_irrelevant| is true. + + // Id for the constant + uint32 fresh_id = 1; + + // Id for the scalar type of the constant + uint32 type_id = 2; + + // Value of the constant + repeated uint32 word = 3; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + +} + +message TransformationAddCopyMemory { + + // Adds an OpCopyMemory instruction into the module. + // Creates either a global or a local variable (based on + // |storage_class| field) to copy the target into. + + // OpCopyMemory will be inserted before this instruction. + InstructionDescriptor instruction_descriptor = 1; + + // Fresh id to copy memory into. + uint32 fresh_id = 2; + + // Source to copy memory from. + uint32 source_id = 3; + + // Storage class for the target variable. Can be either Function or Private. + uint32 storage_class = 4; + + // Result id for the variable's initializer operand. Its type must be equal to + // variable's pointee type. + uint32 initializer_id = 5; + +} + +message TransformationAddDeadBlock { + + // Adds a new block to the module that is statically reachable from an + // existing block, but dynamically unreachable. + + // Fresh id for the dead block + uint32 fresh_id = 1; + + // Id of an existing block terminated with OpBranch, such that this OpBranch + // can be replaced with an OpBranchConditional to its exiting successor or + // the dead block + uint32 existing_block = 2; + + // Determines whether the condition associated with the OpBranchConditional + // is true or false + bool condition_value = 3; + +} + +message TransformationAddDeadBreak { + + // A transformation that turns a basic block that unconditionally branches to + // its successor into a block that potentially breaks out of a structured + // control flow construct, but in such a manner that the break cannot actually + // be taken. + + // The block to break from + uint32 from_block = 1; + + // The merge block to break to + uint32 to_block = 2; + + // Determines whether the break condition is true or false + bool break_condition_value = 3; + + // A sequence of ids suitable for extending OpPhi instructions as a result of + // the new break edge + repeated uint32 phi_id = 4; + +} + +message TransformationAddDeadContinue { + + // A transformation that turns a basic block appearing in a loop and that + // unconditionally branches to its successor into a block that potentially + // branches to the continue target of the loop, but in such a manner that the + // continue branch cannot actually be taken. + + // The block to continue from + uint32 from_block = 1; + + // Determines whether the continue condition is true or false + bool continue_condition_value = 2; + + // A sequence of ids suitable for extending OpPhi instructions as a result of + // the new break edge + repeated uint32 phi_id = 3; + +} + +message TransformationAddEarlyTerminatorWrapper { + + // Adds a function to the module containing a single block with a single non- + // label instruction that is either OpKill, OpUnreachable, or + // OpTerminateInvocation. The purpose of this is to allow such instructions + // to be subsequently replaced with wrapper functions, which can then enable + // transformations (such as inlining) that are hard in the direct presence + // of these instructions. + + // Fresh id for the function. + uint32 function_fresh_id = 1; + + // Fresh id for the single basic block in the function. + uint32 label_fresh_id = 2; + + // One of OpKill, OpUnreachable, OpTerminateInvocation. If additional early + // termination instructions are added to SPIR-V they should also be handled + // here. + uint32 opcode = 3; + +} + +message TransformationAddFunction { + + // Adds a SPIR-V function to the module. + + // The series of instructions that comprise the function. + repeated Instruction instruction = 1; + + // True if and only if the given function should be made livesafe (see + // FactFunctionIsLivesafe for definition). + bool is_livesafe = 2; + + // Fresh id for a new variable that will serve as a "loop limiter" for the + // function; only relevant if |is_livesafe| holds. + uint32 loop_limiter_variable_id = 3; + + // Id of an existing unsigned integer constant providing the maximum value + // that the loop limiter can reach before the loop is broken from; only + // relevant if |is_livesafe| holds. + uint32 loop_limit_constant_id = 4; + + // Fresh ids for each loop in the function that allow the loop limiter to be + // manipulated; only relevant if |is_livesafe| holds. + repeated LoopLimiterInfo loop_limiter_info = 5; + + // Id of an existing global value with the same return type as the function + // that can be used to replace OpKill and OpReachable instructions with + // ReturnValue instructions. Ignored if the function has void return type. + // Only relevant if |is_livesafe| holds. + uint32 kill_unreachable_return_value_id = 6; + + // A mapping (represented as a sequence) from every access chain result id in + // the function to the ids required to clamp its indices to ensure they are in + // bounds; only relevant if |is_livesafe| holds. + repeated AccessChainClampingInfo access_chain_clamping_info = 7; + +} + +message TransformationAddGlobalUndef { + + // Adds an undefined value of a given type to the module at global scope. + + // Fresh id for the undefined value + uint32 fresh_id = 1; + + // The type of the undefined value + uint32 type_id = 2; + +} + +message TransformationAddGlobalVariable { + + // Adds a global variable of the given type to the module, with Private or + // Workgroup storage class, and optionally (for the Private case) with an + // initializer. + + // Fresh id for the global variable + uint32 fresh_id = 1; + + // The type of the global variable + uint32 type_id = 2; + + uint32 storage_class = 3; + + // Initial value of the variable + uint32 initializer_id = 4; + + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. + bool value_is_irrelevant = 5; + +} + +message TransformationAddImageSampleUnusedComponents { + + // A transformation that adds unused components to an image sample coordinate. + + // An vector id with the original coordinate and the unused components. + uint32 coordinate_with_unused_components_id = 1; + + // A descriptor for an image sample instruction. + InstructionDescriptor instruction_descriptor = 2; + +} + +message TransformationAddLocalVariable { + + // Adds a local variable of the given type (which must be a pointer with + // Function storage class) to the given function, initialized to the given + // id. + + // Fresh id for the local variable + uint32 fresh_id = 1; + + // The type of the local variable + uint32 type_id = 2; + + // The id of the function to which the local variable should be added + uint32 function_id = 3; + + // Initial value of the variable + uint32 initializer_id = 4; + + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. + bool value_is_irrelevant = 5; + +} + +message TransformationAddLoopPreheader { + + // A transformation that adds a loop preheader block before the given loop header. + + // The id of the loop header block + uint32 loop_header_block = 1; + + // A fresh id for the preheader block + uint32 fresh_id = 2; + + // Fresh ids for splitting the OpPhi instructions in the header. + // A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header, + // if the header has more than one predecessor outside of the loop. + // This allows turning instructions of the form: + // + // %loop_header_block = OpLabel + // %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id + // + // into: + // %fresh_id = OpLabel + // %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id + // OpBranch %header_id + // %loop_header_block = OpLabel + // %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id + repeated uint32 phi_id = 3; + +} + +message TransformationAddLoopToCreateIntConstantSynonym { + // A transformation that uses a loop to create a synonym for an integer + // constant C (scalar or vector) using an initial value I, a step value S and + // a number of iterations N such that C = I - N * S. For each iteration, S is + // subtracted from the total. + // The loop can be made up of one or two blocks, and it is inserted before a + // block with a single predecessor. In the one-block case, it is of the form: + // + // %loop_id = OpLabel + // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id + // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id + // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id + // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 + // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id + // OpLoopMerge %block_after_loop_id %loop_id None + // OpBranchConditional %cond_id %loop_id %block_after_loop_id + // + // A new OpPhi instruction is then added to %block_after_loop_id, as follows: + // + // %block_after_loop_id = OpLabel + // %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id + // + // This can be translated, assuming that N > 0, to: + // int syn = I; + // for (int ctr = 0; ctr < N; ctr++) syn = syn - S; + // + // All existing OpPhi instructions in %block_after_loop_id are also updated + // to reflect the fact that its predecessor is now %loop_id. + + // The following are existing ids. + + // The id of the integer constant C that we want a synonym of. + uint32 constant_id = 1; + + // The id of the initial value integer constant I. + uint32 initial_val_id = 2; + + // The id of the step value integer constant S. + uint32 step_val_id = 3; + + // The id of the integer scalar constant, its value being the number of + // iterations N. + uint32 num_iterations_id = 4; + + // The label id of the block before which the loop must be inserted. + uint32 block_after_loop_id = 5; + + + // The following are fresh ids. + + // A fresh id for the synonym. + uint32 syn_id = 6; + + // A fresh id for the label of the loop, + uint32 loop_id = 7; + + // A fresh id for the counter. + uint32 ctr_id = 8; + + // A fresh id taking the value I - S * ctr at the ctr-th iteration. + uint32 temp_id = 9; + + // A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and + // thus I - S * N at the last iteration. + uint32 eventual_syn_id = 10; + + // A fresh id for the incremented counter. + uint32 incremented_ctr_id = 11; + + // A fresh id for the loop condition. + uint32 cond_id = 12; + + // The instructions in the loop can also be laid out in two basic blocks, as follows: + // + // %loop_id = OpLabel + // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id + // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id + // OpLoopMerge %block_after_loop_id %additional_block_id None + // OpBranch %additional_block_id + // + // %additional_block_id = OpLabel + // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id + // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 + // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id + // OpBranchConditional %cond_id %loop_id %block_after_loop_id + + // A fresh id for the additional block. If this is 0, it means that only one + // block is to be created. + uint32 additional_block_id = 13; +} + +message TransformationAddNoContractionDecoration { + + // Applies OpDecorate NoContraction to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + +message TransformationAddOpPhiSynonym { + + // Adds an OpPhi instruction at the start of a block with n predecessors (pred_1, pred_2, ..., pred_n) + // and n related ids (id_1, id_2, ..., id_n) which are pairwise synonymous. + // The instruction will be of the form: + // %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n + // and fresh_id will be recorded as being synonymous with all the other ids. + + // Label id of the block + uint32 block_id = 1; + + // Pairs (pred_i, id_i) + repeated UInt32Pair pred_to_id = 2; + + // Fresh id for the new instruction + uint32 fresh_id = 3; +} + +message TransformationAddParameter { + + // Adds a new parameter into the function. + + // Result id of the function to add parameters to. + uint32 function_id = 1; + + // Fresh id for a new parameter. + uint32 parameter_fresh_id = 2; + + // Type id for a new parameter. + uint32 parameter_type_id = 3; + + // A map that maps from the OpFunctionCall id to the id that will be passed as the new + // parameter at that call site. It must have the same type as that of the new parameter. + repeated UInt32Pair call_parameter_ids = 4; + + // A fresh id for a new function type. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 5; + +} + +message TransformationAddRelaxedDecoration { + + // Applies OpDecorate RelaxedPrecision to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + +message TransformationAddSpecConstantOp { + + // Adds OpSpecConstantOp into the module. + + // Result id for the new instruction. + uint32 fresh_id = 1; + + // Type id for the new instruction. + uint32 type_id = 2; + + // Opcode operand of the OpSpecConstantOp instruction. + uint32 opcode = 3; + + // Operands of the |opcode| instruction. + repeated InstructionOperand operand = 4; + +} + +message TransformationAddSynonym { + + // Adds a |synonymous_instruction| before |insert_before| instruction with + // and creates a fact that |result_id| and the result id of |synonymous_instruction| + // are synonymous. + + // Result id of the first synonym. + uint32 result_id = 1; + + // Type of the synonym to apply. Some types might produce instructions + // with commutative operands. Such types do not specify the order of the + // operands since we have a special transformation to swap commutable operands. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499): + // Consider adding more types here. + enum SynonymType { + // New synonym is derived by adding zero to the |result_id|. + ADD_ZERO = 0; + + // New synonym is derived by subtracting zero from the |result_id|. + SUB_ZERO = 1; + + // New synonym is derived by multiplying |result_id| by one. + MUL_ONE = 2; + + // New synonym is derived by applying OpCopyObject instruction to |result_id|. + COPY_OBJECT = 3; + + // New synonym is derived by applying OpLogicalOr to |result_id| with the second + // operand being 'false'. + LOGICAL_OR = 4; + + // New synonym is derived by applying OpLogicalAnd to |result_id| with the second + // operand being 'true'. + LOGICAL_AND = 5; + + // New synonym is derived by applying OpBitwiseOr to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_OR = 6; + + // New synonym is derived by applying OpBitwiseXor to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_XOR = 7; + } + + // Type of the synonym to create. See SynonymType for more details. + SynonymType synonym_type = 2; + + // Fresh result id for a created synonym. + uint32 synonym_fresh_id = 3; + + // An instruction to insert a new synonym before. + InstructionDescriptor insert_before = 4; + +} + +message TransformationAddTypeArray { + + // Adds an array type of the given element type and size to the module + + // Fresh id for the array type + uint32 fresh_id = 1; + + // The array's element type + uint32 element_type_id = 2; + + // The array's size + uint32 size_id = 3; + +} + +message TransformationAddTypeBoolean { + + // Adds OpTypeBool to the module + + // Id to be used for the type + uint32 fresh_id = 1; + +} + +message TransformationAddTypeFloat { + + // Adds OpTypeFloat to the module with the given width + + // Id to be used for the type + uint32 fresh_id = 1; + + // Floating-point width + uint32 width = 2; + +} + +message TransformationAddTypeFunction { + + // Adds a function type to the module + + // Fresh id for the function type + uint32 fresh_id = 1; + + // The function's return type + uint32 return_type_id = 2; + + // The function's argument types + repeated uint32 argument_type_id = 3; + +} + +message TransformationAddTypeInt { + + // Adds OpTypeInt to the module with the given width and signedness + + // Id to be used for the type + uint32 fresh_id = 1; + + // Integer width + uint32 width = 2; + + // True if and only if this is a signed type + bool is_signed = 3; + +} + +message TransformationAddTypeMatrix { + + // Adds a matrix type to the module + + // Fresh id for the matrix type + uint32 fresh_id = 1; + + // The matrix's column type, which must be a floating-point vector (as per + // the "data rules" in the SPIR-V specification). + uint32 column_type_id = 2; + + // The matrix's column count + uint32 column_count = 3; + +} + +message TransformationAddTypePointer { + + // Adds OpTypePointer to the module, with the given storage class and base + // type + + // Id to be used for the type + uint32 fresh_id = 1; + + // Pointer storage class + uint32 storage_class = 2; + + // Id of the base type for the pointer + uint32 base_type_id = 3; + +} + +message TransformationAddTypeStruct { + + // Adds a struct type to the module + + // Fresh id for the struct type + uint32 fresh_id = 1; + + // The struct's member types + repeated uint32 member_type_id = 3; + +} + +message TransformationAddTypeVector { + + // Adds a vector type to the module + + // Fresh id for the vector type + uint32 fresh_id = 1; + + // The vector's component type + uint32 component_type_id = 2; + + // The vector's component count + uint32 component_count = 3; + +} + +message TransformationAdjustBranchWeights { + + // A transformation that adjusts the branch weights + // of a branch conditional instruction. + + // A descriptor for a branch conditional instruction. + InstructionDescriptor instruction_descriptor = 1; + + // Branch weights of a branch conditional instruction. + UInt32Pair branch_weights = 2; + +} + +message TransformationCompositeConstruct { + + // A transformation that introduces an OpCompositeConstruct instruction to + // make a composite object. + + // Id of the type of the composite that is to be constructed + uint32 composite_type_id = 1; + + // Ids of the objects that will form the components of the composite + repeated uint32 component = 2; + + // A descriptor for an instruction in a block before which the new + // OpCompositeConstruct instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + + // A fresh id for the composite object + uint32 fresh_id = 4; + +} + +message TransformationCompositeExtract { + + // A transformation that adds an instruction to extract an element from a + // composite. + + // A descriptor for an instruction in a block before which the new + // OpCompositeExtract instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id for the extract operation. + uint32 fresh_id = 2; + + // Id of the composite from which data is to be extracted. + uint32 composite_id = 3; + + // Indices that indicate which part of the composite should be extracted. + repeated uint32 index = 4; + +} + +message TransformationCompositeInsert { + + // A transformation that adds an instruction OpCompositeInsert which creates + // a new composite from an existing composite, with an element inserted. + + // A descriptor for an instruction before which the new instruction + // OpCompositeInsert should be inserted. + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id of the inserted OpCompositeInsert instruction. + uint32 fresh_id = 2; + + // Id of the composite used as the basis for the insertion. + uint32 composite_id = 3; + + // Id of the object to be inserted. + uint32 object_id = 4; + + // Indices that indicate which part of the composite should be inserted into. + repeated uint32 index = 5; + +} + +message TransformationComputeDataSynonymFactClosure { + + // A transformation that impacts the fact manager only, forcing a computation + // of the closure of data synonym facts, so that e.g. if the components of + // vectors v and w are known to be pairwise synonymous, it is deduced that v + // and w are themselves synonymous. + + // When searching equivalence classes for implied facts, equivalence classes + // larger than this size will be skipped. + uint32 maximum_equivalence_class_size = 1; + +} + +message TransformationDuplicateRegionWithSelection { + + // A transformation that inserts a conditional statement with a boolean expression + // of arbitrary value and duplicates a given single-entry, single-exit region, so + // that it is present in each conditional branch and will be executed regardless + // of which branch will be taken. + + // Fresh id for a label of the new entry block. + uint32 new_entry_fresh_id = 1; + + // Id for a boolean expression. + uint32 condition_id = 2; + + // Fresh id for a label of the merge block of the conditional. + uint32 merge_label_fresh_id = 3; + + // Block id of the entry block of the original region. + uint32 entry_block_id = 4; + + // Block id of the exit block of the original region. + uint32 exit_block_id = 5; + + // Map that maps from a label in the original region to the corresponding label + // in the duplicated region. + repeated UInt32Pair original_label_to_duplicate_label = 6; + + // Map that maps from a result id in the original region to the corresponding + // result id in the duplicated region. + repeated UInt32Pair original_id_to_duplicate_id = 7; + + // Map that maps from a result id in the original region to the result id of the + // corresponding OpPhi instruction. + repeated UInt32Pair original_id_to_phi_id = 8; +} + +message TransformationEquationInstruction { + + // A transformation that adds an instruction to the module that defines an + // equation between its result id and input operand ids, such that the + // equation is guaranteed to hold at any program point where all ids involved + // are available (i.e. at any program point dominated by the instruction). + + // The result id of the new instruction + uint32 fresh_id = 1; + + // The instruction's opcode + uint32 opcode = 2; + + // The input operands to the instruction + repeated uint32 in_operand_id = 3; + + // A descriptor for an instruction in a block before which the new + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + +message TransformationExpandVectorReduction { + + // A transformation that adds synonyms for OpAny and OpAll instructions by + // evaluating each vector component with the corresponding logical operation. + // There is a SPIR-V code example in the header file of the transformation + // class that can help understand the transformation. + + // The OpAny or OpAll instruction result id. + uint32 instruction_result_id = 1; + + // The fresh ids required to apply the transformation. + repeated uint32 fresh_ids = 2; + +} + +message TransformationFlattenConditionalBranch { + + // A transformation that takes a selection construct with a header + // containing an OpBranchConditional instruction and flattens it. + // For example, something of the form: + // + // %1 = OpLabel + // [header instructions] + // OpSelectionMerge %4 None + // OpBranchConditional %cond %2 %3 + // %2 = OpLabel + // [true branch instructions] + // OpBranch %4 + // %3 = OpLabel + // [false branch instructions] + // OpBranch %4 + // %4 = OpLabel + // ... + // + // becomes: + // + // %1 = OpLabel + // [header instructions] + // OpBranch %2 + // %2 = OpLabel + // [true branch instructions] + // OpBranch %3 + // %3 = OpLabel + // [false branch instructions] + // OpBranch %4 + // %4 = OpLabel + // ... + // + // If all of the instructions in the true or false branches have + // no side effects, this is semantics-preserving. + // Side-effecting instructions will instead be enclosed by smaller + // conditionals. For more details, look at the definition for the + // SideEffectWrapperInfo message. + // + // Nested conditionals or loops are not supported. The false branch + // could also be executed before the true branch, depending on the + // |true_branch_first| field. + + // The label id of the header block + uint32 header_block_id = 1; + + // A boolean field deciding the order in which the original branches + // will be laid out: the true branch will be laid out first iff this + // field is true. + bool true_branch_first = 2; + + // If the convergence block contains an OpPhi with bvec2 result type, it may + // be necessary to introduce a bvec2 with the selection construct's condition + // in both components in order to turn the OpPhi into an OpSelect. This + // this field provides a fresh id for an OpCompositeConstruct instruction for + // this purpose. It should be set to 0 if no such instruction is required. + uint32 fresh_id_for_bvec2_selector = 3; + + // The same as |fresh_id_for_bvec2_selector| but for the bvec3 case. + uint32 fresh_id_for_bvec3_selector = 4; + + // The same as |fresh_id_for_bvec2_selector| but for the bvec4 case. + uint32 fresh_id_for_bvec4_selector = 5; + + // A list of instructions with side effects, which must be enclosed + // inside smaller conditionals before flattening the main one, and + // the corresponding fresh ids and module ids needed. + repeated SideEffectWrapperInfo side_effect_wrapper_info = 6; +} + +message TransformationFunctionCall { + + // A transformation that introduces an OpFunctionCall instruction. The call + // must not make the module's call graph cyclic. Beyond that, if the call + // is in a dead block it can be to any function with arbitrary suitably-typed + // arguments; otherwise it must be to a livesafe function, with injected + // variables as pointer arguments and arbitrary non-pointer arguments. + + // A fresh id for the result of the call + uint32 fresh_id = 1; + + // Id of the function to be called + uint32 callee_id = 2; + + // Ids for arguments to the function + repeated uint32 argument_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpFunctionCall instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + +message TransformationInlineFunction { + + // This transformation inlines a function by mapping the function instructions to fresh ids. + + // Result id of the function call instruction. + uint32 function_call_id = 1; + + // For each result id defined by the called function, + // this map provides an associated fresh id that can + // be used in the inlined version of the function call. + repeated UInt32Pair result_id_map = 2; + +} + +message TransformationInvertComparisonOperator { + + // For some instruction with result id |operator_id| that + // represents a binary comparison operator (e.g. <, >, <=), this transformation + // will replace that instruction's result id with |fresh_id|, + // invert the opcode (< will become >=) and insert OpLogicalNot + // instruction with result id |operator_id| below. + + // Result id of the instruction to invert. + uint32 operator_id = 1; + + // Fresh id that will be used by the operator after the inversion. + uint32 fresh_id = 2; + +} + +message TransformationLoad { + + // Transformation that adds an OpLoad or OpAtomicLoad instruction from a pointer into an id. + + // The result of the load instruction. + uint32 fresh_id = 1; + + // The pointer to be loaded from. + uint32 pointer_id = 2; + + // True if and only if the load should be atomic. + bool is_atomic = 3; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 4; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 5; + + // A descriptor for an instruction in a block before which the new OpLoad + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; + +} + +message TransformationMakeVectorOperationDynamic { + + // A transformation that replaces the OpCompositeExtract and OpCompositeInsert + // instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions. + + // The composite instruction result id. + uint32 instruction_result_id = 1; + + // The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object. + // However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction. + // This is the result id of the integer instruction. + uint32 constant_index_id = 2; + +} + +message TransformationMergeBlocks { + + // A transformation that merges a block with its predecessor. + + // The id of the block that is to be merged with its predecessor; the merged + // block will have the *predecessor's* id. + uint32 block_id = 1; + +} + +message TransformationMergeFunctionReturns { + + // A transformation that modifies a function so that it does not return early, + // so it only has one return statement (ignoring unreachable blocks). + // + // The function is enclosed inside an outer loop, that is only executed once, + // and whose merge block is the new return block of the function. + // + // Each return instruction is replaced by: + // OpBranch %innermost_loop_merge + // where %innermost_loop_merge is the innermost loop containing the return + // instruction. + // + // Each merge block whose associated loop contains return instructions is + // changed so that it branches to the merge block of the loop containing it, + // as explained in the comments to the ReturnMergingInfo message. + // + // The new return block (the merge block of the new outer loop) will be of + // the following form (if the return type is not void): + // %outer_return_id = OpLabel + // %return_val_id = OpPhi %return_type %val1 %block_1 %val2 %block_2 ... + // OpReturnValue %return_val_id + // where %block_k is either a return block that, in the original function, is + // outside of any loops, or the merge block of a loop that contains return + // instructions and is not, originally, nested inside another loop, and + // %block_k is the corresponding return value. + // If the function has void type, there will be no OpPhi instruction and the + // last instruction will be OpReturn. + + // The id of the function to which the transformation is being applied. + uint32 function_id = 1; + + // A fresh id for the header of the new outer loop. + uint32 outer_header_id = 2; + + // A fresh id for an unreachable continue construct for the new outer loop. + uint32 unreachable_continue_id = 7; + + // A fresh id for the new return block of the function, + // i.e. the merge block of the new outer loop. + uint32 outer_return_id = 3; + + // A fresh id for the value that will be returned. + // This is ignored if the function has void return type. + uint32 return_val_id = 4; + + // An existing id of the same type as the return value, which is + // available to use at the end of the entry block. + // This is ignored if the function has void return type or if no + // loops in the function contain a return instruction. + // If the function is not void, the transformation will add an + // OpPhi instruction to each merge block whose associated loop + // contains at least a return instruction. The value associated + // with existing predecessors from which the function cannot be + // returning will be this id, used as a placeholder. + uint32 any_returnable_val_id = 5; + + // The information needed to modify the merge blocks of + // loops containing return instructions. + repeated ReturnMergingInfo return_merging_info = 6; +} + +message TransformationMoveBlockDown { + + // A transformation that moves a basic block to be one position lower in + // program order. + + // The id of the block to move down. + uint32 block_id = 1; +} + +message TransformationMoveInstructionDown { + + // Swaps |instruction| with the next instruction in the block. + + // The instruction to move down. + InstructionDescriptor instruction = 1; + +} + +message TransformationMutatePointer { + + // Backs up value of the pointer, writes into the pointer and + // restores the original value. + + // Result id of the pointer instruction to mutate. + uint32 pointer_id = 1; + + // Fresh id for the OpLoad instruction. + uint32 fresh_id = 2; + + // Instruction to insert backup, mutation and restoration code before. + InstructionDescriptor insert_before = 3; + +} + +message TransformationOutlineFunction { + + // A transformation that outlines a single-entry single-exit region of a + // control flow graph into a separate function, and replaces the region with + // a call to that function. + + // Id of the entry block of the single-entry single-exit region to be outlined + uint32 entry_block = 1; + + // Id of the exit block of the single-entry single-exit region to be outlined + uint32 exit_block = 2; + + // Id of a struct that will store the return values of the new function + uint32 new_function_struct_return_type_id = 3; + + // A fresh id for the type of the outlined function + uint32 new_function_type_id = 4; + + // A fresh id for the outlined function itself + uint32 new_function_id = 5; + + // A fresh id to represent the block in the outlined function that represents + // the first block of the outlined region. + uint32 new_function_region_entry_block = 6; + + // A fresh id for the result of the OpFunctionCall instruction that will call + // the outlined function + uint32 new_caller_result_id = 7; + + // A fresh id to capture the return value of the outlined function - the + // argument to OpReturn + uint32 new_callee_result_id = 8; + + // Ids defined outside the region and used inside the region will become + // parameters to the outlined function. This is a mapping from used ids to + // fresh parameter ids. + repeated UInt32Pair input_id_to_fresh_id = 9; + + // Ids defined inside the region and used outside the region will become + // fresh ids defined by the outlined function, which get copied into the + // function's struct return value and then copied into their destination ids + // by the caller. This is a mapping from original ids to corresponding fresh + // ids. + repeated UInt32Pair output_id_to_fresh_id = 10; + +} + +message TransformationPermuteFunctionParameters { + + // A transformation that, given a non-entry-point function taking n + // parameters and a permutation of the set [0, n-1]: + // - Introduces a new function type that is the same as the original + // function's type but with the order of arguments permuted + // (only if it doesn't already exist) + // - Changes the type of the function to this type + // - Adjusts all calls to the function so that their arguments are permuted + + // Function, whose parameters will be permuted + uint32 function_id = 1; + + // Fresh id for a new type of the function. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 2; + + // An array of size |n|, where |n| is a number of arguments to a function + // with |function_id|. For each i: 0 <= permutation[i] < n. + // + // i-th element of this array contains a position for an i-th + // function's argument (i.e. i-th argument will be permutation[i]-th + // after running this transformation) + repeated uint32 permutation = 3; + +} + +message TransformationPermutePhiOperands { + + // Permutes operands of some OpPhi instruction. + + // Result id of the instruction to apply the transformation to. + uint32 result_id = 1; + + // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number + // of operands of the OpPhi instruction with |result_id|. + repeated uint32 permutation = 2; + +} + +message TransformationPropagateInstructionDown { + + // Propagates an instruction from |block_id| into its successors. + // Concretely, the transformation clones the propagated instruction + // into some of the successors of |block_id| and removes the original + // instruction. Additionally, an OpPhi instruction may be added to make sure + // that the transformation can be applied in various scenarios. + // + // Note that the instruction might not be propagated down into every successor + // of |block_id| since it might make the module invalid. + + // Id of the block to propagate an instruction from. The decision on what + // instruction to propagate is made based on whether the instruction interacts + // with memory, whether that instruction is used in its block etc (see the + // transformation class for more details). + uint32 block_id = 1; + + // A fresh id for an OpPhi instruction. This might not be used by the + // transformation since an OpPhi instruction is created only if needed + // (e.g. an instruction is propagated into divergent blocks). + uint32 phi_fresh_id = 2; + + // A map from the id of some successor of the |block_id| to the fresh id. + // The map contains a fresh id for at least every successor of the |block_id|. + // Every fresh id in the map corresponds to the result id of the clone, + // propagated into the corresponding successor block. This transformation + // might use overflow ids if they are available and this field doesn't account + // for every successor of |block_id|. + repeated UInt32Pair successor_id_to_fresh_id = 3; + +} + +message TransformationPropagateInstructionUp { + + // Propagates an instruction in the block into the block's predecessors. + // Concretely, this transformation clones some particular instruction from + // the |block_id| into every block's predecessor and replaces the original + // instruction with OpPhi. Take a look at the transformation class to learn + // more about how we choose what instruction to propagate. + + // Id of the block to propagate an instruction from. + uint32 block_id = 1; + + // A map from the id of some predecessor of the |block_id| to the fresh id. + // The map contains a fresh id for at least every predecessor of the |block_id|. + // The instruction is propagated by creating a number of clones - one clone for + // each predecessor. Fresh ids from this field are used as result ids of cloned + // instructions. + repeated UInt32Pair predecessor_id_to_fresh_id = 2; + +} + +message TransformationPushIdThroughVariable { + + // A transformation that makes |value_synonym_id| and |value_id| to be + // synonymous by storing |value_id| into |variable_id| and + // loading |variable_id| to |value_synonym_id|. + + // The value to be stored. + uint32 value_id = 1; + + // A fresh id for the result of the load instruction. + uint32 value_synonym_id = 2; + + // A fresh id for the variable to be stored to. + uint32 variable_id = 3; + + // Constant to initialize the variable from. + uint32 initializer_id = 4; + + // The variable storage class (global or local). + uint32 variable_storage_class = 5; + + // A descriptor for an instruction which the new OpStore + // and OpLoad instructions might be inserted before. + InstructionDescriptor instruction_descriptor = 6; + +} + +message TransformationRecordSynonymousConstants { + + // A transformation that, given the IDs to two synonymous constants, + // records the fact that they are synonymous. The module is not changed. + // Two constants are synonymous if: + // - they have the same type (ignoring the presence of integer sign) + // - they have the same opcode (one of OpConstant, OpConstantTrue, + // OpConstantFalse, OpConstantNull) + // - they have the same value + // If the types are the same, OpConstantNull is equivalent to + // OpConstantFalse or OpConstant with value zero. + + // The id of a constant + uint32 constant1_id = 1; + + // The id of the synonym + uint32 constant2_id = 2; + +} + +message TransformationReplaceAddSubMulWithCarryingExtended { + + // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul + // with OpUMulExtended or OpSMulExtended (depending on the signedness + // of the operands) and stores the result into a |struct_fresh_id|. + // In the original instruction the result type id and the type ids of + // the operands must be the same. Then the transformation extracts + // the first element of the result into the original |result_id|. + // This value is the same as the result of the original instruction. + + // The fresh id of the intermediate result. + uint32 struct_fresh_id = 1; + + // The result id of the original instruction. + uint32 result_id = 2; + +} + +message TransformationReplaceBranchFromDeadBlockWithExit { + + // Given a dead block that ends with OpBranch, replaces OpBranch with an + // "exit" instruction; one of OpReturn/OpReturnValue, OpKill (in a fragment + // shader) or OpUnreachable. + + // The dead block whose terminator is to be replaced. + uint32 block_id = 1; + + // The opcode of the new terminator. + uint32 opcode = 2; + + // Ignored unless opcode is OpReturnValue, in which case this field provides + // a suitable result id to be returned. + uint32 return_value_id = 3; + +} + +message TransformationReplaceParameterWithGlobal { + + // Removes parameter with result id |parameter_id| from its function + // and creates a global variable to pass its value to the function instead. + + // Fresh id for a new function type. This might not be used if a required + // function type already exists or if we can change the old function type. + uint32 function_type_fresh_id = 2; + + // Result id of the OpFunctionParameter instruction to remove. + uint32 parameter_id = 3; + + // Fresh id of a global variable used to pass parameter's value to the function. + uint32 global_variable_fresh_id = 4; + +} + +message TransformationReplaceBooleanConstantWithConstantBinary { + + // A transformation to capture replacing a use of a boolean constant with + // binary operation on two constant values + + // A descriptor for the boolean constant id we would like to replace + IdUseDescriptor id_use_descriptor = 1; + + // Id for the constant to be used on the LHS of the comparison + uint32 lhs_id = 2; + + // Id for the constant to be used on the RHS of the comparison + uint32 rhs_id = 3; + + // Opcode for binary operator + uint32 opcode = 4; + + // Id that will store the result of the binary operation instruction + uint32 fresh_id_for_binary_operation = 5; + +} + +message TransformationReplaceConstantWithUniform { + + // Replaces a use of a constant id with the result of a load from an + // element of uniform buffer known to hold the same value as the constant + + // A descriptor for the id we would like to replace + IdUseDescriptor id_use_descriptor = 1; + + // Uniform descriptor to identify which uniform value to choose + UniformBufferElementDescriptor uniform_descriptor = 2; + + // Id that will store the result of an access chain + uint32 fresh_id_for_access_chain = 3; + + // Id that will store the result of a load + uint32 fresh_id_for_load = 4; + +} + +message TransformationReplaceCopyMemoryWithLoadStore { + + // A transformation that replaces instructions OpCopyMemory with loading + // the source variable to an intermediate value and storing this value into the + // target variable of the original OpCopyMemory instruction. + + // The intermediate value. + uint32 fresh_id = 1; + + // The instruction descriptor to OpCopyMemory. It is necessary, because + // OpCopyMemory doesn't have a result id. + InstructionDescriptor copy_memory_instruction_descriptor = 2; +} + +message TransformationReplaceCopyObjectWithStoreLoad { + + // A transformation that replaces instruction OpCopyObject with + // storing into a new variable and immediately loading from this + // variable to |result_id| of the original OpCopyObject instruction. + + // The result id of initial OpCopyObject instruction + uint32 copy_object_result_id = 1; + + // A fresh id for the variable to be stored to. + uint32 fresh_variable_id = 2; + + // The variable storage class (Function or Private). + uint32 variable_storage_class = 3; + + // Constant to initialize the variable with. + uint32 variable_initializer_id = 4; +} + +message TransformationReplaceIdWithSynonym { + + // Replaces a use of an id with an id that is known to be synonymous, e.g. + // because it was obtained via applying OpCopyObject + + // The id use that is to be replaced + IdUseDescriptor id_use_descriptor = 1; + + // The synonymous id + uint32 synonymous_id = 2; + +} + +message TransformationReplaceIrrelevantId { + + // Replaces an irrelevant id with another id of the same type. + + // The id use that is to be replaced + IdUseDescriptor id_use_descriptor = 1; + + // The replacement id + uint32 replacement_id = 2; +} + +message TransformationReplaceLinearAlgebraInstruction { + + // Replaces a linear algebra instruction with its + // mathematical definition. + + // The fresh ids needed to apply the transformation. + repeated uint32 fresh_ids = 1; + + // A descriptor for a linear algebra instruction. + InstructionDescriptor instruction_descriptor = 2; + +} + +message TransformationReplaceLoadStoreWithCopyMemory { + // A transformation that takes a pair of instruction descriptors + // to OpLoad and OpStore that have the same intermediate value + // and replaces the OpStore with an equivalent OpCopyMemory. + + // The instruction descriptor to OpLoad + InstructionDescriptor load_instruction_descriptor = 1; + + // The instruction descriptor to OpStore + InstructionDescriptor store_instruction_descriptor = 2; +} + +message TransformationReplaceOpPhiIdFromDeadPredecessor { + + // Replaces one of the ids used by an OpPhi instruction, when + // the corresponding predecessor is dead, with any available id + // of the correct type. + + // The result id of the OpPhi instruction. + uint32 opphi_id = 1; + + // The label id of one of the predecessors of the block containing + // the OpPhi instruction, corresponding to the id that we want to + // replace. + uint32 pred_label_id = 2; + + // The id that, after the transformation, will be associated with + // the given predecessor. + uint32 replacement_id = 3; + +} + +message TransformationReplaceOpSelectWithConditionalBranch { + + // A transformation that takes an OpSelect instruction with a + // scalar boolean condition and replaces it with a conditional + // branch and an OpPhi instruction. + // The OpSelect instruction must be the first instruction in its + // block, which must have a unique predecessor. The block will + // become the merge block of a new construct, while its predecessor + // will become the header. + // Given the original OpSelect instruction: + // %id = OpSelect %type %cond %then %else + // The branching instruction of the header will be: + // OpBranchConditional %cond %true_block_id %false_block_id + // and the OpSelect instruction will be turned into: + // %id = OpPhi %type %then %true_block_id %else %false_block_id + // At most one of |true_block_id| and |false_block_id| can be zero. In + // that case, there will be no such block and all references to it + // will be replaced by %merge_block (where %merge_block is the + // block containing the OpSelect instruction). + + // The result id of the OpSelect instruction. + uint32 select_id = 1; + + // A fresh id for the new block that the predecessor of the block + // containing |select_id| will branch to if the condition holds. + uint32 true_block_id = 2; + + // A fresh id for the new block that the predecessor of the block + // containing |select_id| will branch to if the condition does not + // hold. + uint32 false_block_id = 3; +} + +message TransformationReplaceParamsWithStruct { + + // Replaces parameters of the function with a struct containing + // values of those parameters. + + // Result ids of parameters to replace. + repeated uint32 parameter_id = 1; + + // Fresh id for a new function type. This might be unused if the required type + // already exists in the module or if we can change the old type. + uint32 fresh_function_type_id = 2; + + // Fresh id for a new struct function parameter to be used as a replacement. + uint32 fresh_parameter_id = 3; + + // Fresh ids for struct objects containing values of replaced parameters. + // This field contains a fresh id for at least every result id of a relevant + // OpFunctionCall instruction. + repeated UInt32Pair caller_id_to_fresh_composite_id = 4; + +} + +message TransformationSetFunctionControl { + + // A transformation that sets the function control operand of an OpFunction + // instruction. + + // The result id of an OpFunction instruction + uint32 function_id = 1; + + // The value to which the 'function control' operand should be set. + uint32 function_control = 2; + +} + +message TransformationSetLoopControl { + + // A transformation that sets the loop control operand of an OpLoopMerge + // instruction. + + // The id of a basic block that should contain OpLoopMerge + uint32 block_id = 1; + + // The value to which the 'loop control' operand should be set. + // This must be a legal loop control mask. + uint32 loop_control = 2; + + // Provides a peel count value for the loop. Used if and only if the + // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can + // still be zero if this bit is set). + uint32 peel_count = 3; + + // Provides a partial count value for the loop. Used if and only if the + // PartialCount bit is set. Must be zero if the PartialCount bit is not set + // (can still be zero if this bit is set). + uint32 partial_count = 4; + +} + +message TransformationSetMemoryOperandsMask { + + // A transformation that sets the memory operands mask of a memory access + // instruction. + + // A descriptor for a memory access instruction, e.g. an OpLoad + InstructionDescriptor memory_access_instruction = 1; + + // A mask of memory operands to be applied to the instruction. It must be the + // same as the original mask, except that Volatile can be added, and + // Nontemporal can be added or removed. + uint32 memory_operands_mask = 2; + + // Some memory access instructions allow more than one mask to be specified; + // this field indicates which mask should be set + uint32 memory_operands_mask_index = 3; + +} + +message TransformationSetSelectionControl { + + // A transformation that sets the selection control operand of an + // OpSelectionMerge instruction. + + // The id of a basic block that should contain OpSelectionMerge + uint32 block_id = 1; + + // The value to which the 'selection control' operand should be set. + // Although technically 'selection control' is a literal mask that can be + // some combination of 'None', 'Flatten' and 'DontFlatten', the combination + // 'Flatten | DontFlatten' does not make sense and is not allowed here. + uint32 selection_control = 2; + +} + +message TransformationSplitBlock { + + // A transformation that splits a basic block into two basic blocks + + // A descriptor for an instruction such that the block containing the + // described instruction should be split right before the instruction. + InstructionDescriptor instruction_to_split_before = 1; + + // An id that must not yet be used by the module to which this transformation + // is applied. Rather than having the transformation choose a suitable id on + // application, we require the id to be given upfront in order to facilitate + // reducing fuzzed shaders by removing transformations. The reason is that + // future transformations may refer to the fresh id introduced by this + // transformation, and if we end up changing what that id is, due to removing + // earlier transformations, it may inhibit later transformations from + // applying. + uint32 fresh_id = 2; + +} + +message TransformationStore { + + // Transformation that adds an OpStore or OpAtomicStore instruction of an id to a pointer. + + // The pointer to be stored to. + uint32 pointer_id = 1; + + // True if and only if the load should be atomic. + bool is_atomic = 2; + + // The memory scope for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_scope_id = 3; + + // The memory semantics for the atomic load. Ignored unless |is_atomic| is true. + uint32 memory_semantics_id = 4; + + // The value to be stored. + uint32 value_id = 5; + + // A descriptor for an instruction in a block before which the new OpStore + // instruction should be inserted. + InstructionDescriptor instruction_to_insert_before = 6; + +} + +message TransformationSwapCommutableOperands { + + // A transformation that swaps the operands of a commutative instruction. + + // A descriptor for a commutative instruction + InstructionDescriptor instruction_descriptor = 1; + +} + +message TransformationSwapConditionalBranchOperands { + + // Swaps label ids in OpBranchConditional instruction. + // Additionally, inverts the guard and swaps branch weights + // if present. + + // Descriptor of the instruction to swap operands of. + InstructionDescriptor instruction_descriptor = 1; + + // Fresh result id for the OpLogicalNot instruction, used + // to invert the guard. + uint32 fresh_id = 2; + +} + +message TransformationSwapFunctionVariables { + // A transformation that swaps function variables + + // Result id of the first variable. + uint32 result_id1 = 1; + // Result id of the second variable. + uint32 result_id2 = 2; + +} + +message TransformationSwapTwoFunctions { + // A transformation that swaps the position of two functions within the same module. + + // the IDs for the two functions that are swapped. + uint32 function_id1 = 1; + uint32 function_id2 = 2; +} + +message TransformationToggleAccessChainInstruction { + + // A transformation that toggles an access chain instruction. + + // A descriptor for an access chain instruction + InstructionDescriptor instruction_descriptor = 1; + +} + +message TransformationVectorShuffle { + + // A transformation that adds a vector shuffle instruction. + + // A descriptor for an instruction in a block before which the new + // OpVectorShuffle instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id for the shuffle operation. + uint32 fresh_id = 2; + + // Id of the first vector operand. + uint32 vector1 = 3; + + // Id of the second vector operand. + uint32 vector2 = 4; + + // Indices that indicate which components of the input vectors should be used. + repeated uint32 component = 5; + +} + +message TransformationWrapEarlyTerminatorInFunction { + + // Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation + // - with a call to a wrapper function for the terminator. + + // A fresh id for a new OpFunctionCall instruction. + uint32 fresh_id = 1; + + // A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation + // instruction. + InstructionDescriptor early_terminator_instruction = 2; + + // An id with the same type as the enclosing function's return type that is + // available at the early terminator. This is used to change the terminator + // to OpReturnValue. Ignored if the enclosing function has void return type, + // in which case OpReturn can be used as the new terminator. + uint32 returned_value_id = 3; + +} + +message TransformationWrapRegionInSelection { + + // Transforms a single-entry-single-exit region R into + // if (|branch_condition|) { R } else { R } + // The entry block for R becomes a selection header and + // the exit block - a selection merge. + // + // Note that the region R is not duplicated. Thus, the effect of + // this transformation can be represented as follows: + // entry + // entry / \ + // | \ / + // R --> R + // | | + // exit exit + + // This behaviour is different from TransformationDuplicateRegionWithSelection + // that copies the blocks in R. + + // The entry block for the region R. + uint32 region_entry_block_id = 1; + + // The exit block for the region R. + uint32 region_exit_block_id = 2; + + // Boolean value for the condition expression. + bool branch_condition = 3; + +} + +message TransformationWrapVectorSynonym { + // A transformation that wraps an arithmetic operation into a vector operation + // and get the result of the original operation from the corresponding index. + // For instance, for this transformation, an scalar operation between two scalars: + // define op ∈ {+, -, *} + // c = a op b + // + // requires the availability of two vectors: + // + // va = vector(..., a, ...) + // vb = vector(..., b, ...) + // + // where a and b are in the same position i in each of their corresponding vector + // and a is synonymous with va[i] and b is synonymous with vb[i]. + // + // The transformation then add an instruction vc = va op vb where c is synonymous + // with vc[i]. + + // The result if of the original scalar operation instruction. + uint32 instruction_id = 1; + + // The result id for the first vector that contains the first value of the scalar operation. + uint32 vector_operand1 = 2; + + // The result id for the second vector that contains the second value of the scalar operation. + uint32 vector_operand2 = 3; + + // A fresh id for the resulted vector from the addition of the first and second vector. + uint32 fresh_id = 4; + + // The position in the vector where the value of original instruction is located. Must be in + // the corresponding vector range. + uint32 scalar_position = 5; + +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.cpp new file mode 100644 index 00000000..51f0538b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pseudo_random_generator.h" + +#include + +namespace spvtools { +namespace fuzz { + +PseudoRandomGenerator::PseudoRandomGenerator(uint32_t seed) : mt_(seed) {} + +PseudoRandomGenerator::~PseudoRandomGenerator() = default; + +uint32_t PseudoRandomGenerator::RandomUint32(uint32_t bound) { + assert(bound > 0 && "Bound must be positive"); + return std::uniform_int_distribution(0, bound - 1)(mt_); +} + +uint64_t PseudoRandomGenerator::RandomUint64(uint64_t bound) { + assert(bound > 0 && "Bound must be positive"); + return std::uniform_int_distribution(0, bound - 1)(mt_); +} + +bool PseudoRandomGenerator::RandomBool() { + return static_cast(std::uniform_int_distribution<>(0, 1)(mt_)); +} + +uint32_t PseudoRandomGenerator::RandomPercentage() { + // We use 101 because we want a result in the closed interval [0, 100], and + // RandomUint32 is not inclusive of its bound. + return RandomUint32(101); +} + +double PseudoRandomGenerator::RandomDouble() { + return std::uniform_real_distribution(0.0, 1.0)(mt_); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.h new file mode 100644 index 00000000..7c19833f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/pseudo_random_generator.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ + +#include + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +// Generates random data from a pseudo-random number generator. +class PseudoRandomGenerator : public RandomGenerator { + public: + explicit PseudoRandomGenerator(uint32_t seed); + + ~PseudoRandomGenerator() override; + + uint32_t RandomUint32(uint32_t bound) override; + + uint64_t RandomUint64(uint64_t bound) override; + + uint32_t RandomPercentage() override; + + bool RandomBool() override; + + double RandomDouble() override; + + private: + std::mt19937 mt_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.cpp new file mode 100644 index 00000000..9ec4845d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +RandomGenerator::RandomGenerator() = default; + +RandomGenerator::~RandomGenerator() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.h new file mode 100644 index 00000000..8c1baaab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/random_generator.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_RANDOM_GENERATOR_H_ + +#include + +namespace spvtools { +namespace fuzz { + +class RandomGenerator { + public: + RandomGenerator(); + + virtual ~RandomGenerator(); + + // Returns a value in the half-open interval [0, bound). + virtual uint32_t RandomUint32(uint32_t bound) = 0; + + // Returns a value in the half-open interval [0, bound). + virtual uint64_t RandomUint64(uint64_t bound) = 0; + + // Returns a value in the closed interval [0, 100]. + virtual uint32_t RandomPercentage() = 0; + + // Returns a boolean. + virtual bool RandomBool() = 0; + + // Returns a double in the closed interval [0, 1] + virtual double RandomDouble() = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_RANDOM_GENERATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.cpp new file mode 100644 index 00000000..5b3468ea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/replayer.h" + +#include +#include +#include + +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/build_module.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +Replayer::Replayer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t num_transformations_to_apply, bool validate_during_replay, + spv_validator_options validator_options) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + num_transformations_to_apply_(num_transformations_to_apply), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options) {} + +Replayer::~Replayer() = default; + +Replayer::ReplayerResult Replayer::Run() { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (num_transformations_to_apply_ > + static_cast( + transformation_sequence_in_.transformation_size())) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "The number of transformations to be replayed must not " + "exceed the size of the transformation sequence."); + return {Replayer::ReplayerResultStatus::kTooManyTransformationsRequested, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + spvtools::SpirvTools tools(target_env_); + if (!tools.IsValid()) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return {Replayer::ReplayerResultStatus::kFailedToCreateSpirvToolsInterface, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return {Replayer::ReplayerResultStatus::kInitialBinaryInvalid, nullptr, + nullptr, protobufs::TransformationSequence()}; + } + + // Build the module from the input binary. + std::unique_ptr ir_context = + BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size()); + assert(ir_context); + + // For replay validation, we track the last valid SPIR-V binary that was + // observed. Initially this is the input binary. + std::vector last_valid_binary; + if (validate_during_replay_) { + last_valid_binary = binary_in_; + } + + // We find the smallest id that is (a) not in use by the original module, and + // (b) not used by any transformation in the sequence to be replayed. This + // serves as a starting id from which to issue overflow ids if they are + // required during replay. + uint32_t first_overflow_id = ir_context->module()->id_bound(); + for (auto& transformation : transformation_sequence_in_.transformation()) { + auto fresh_ids = Transformation::FromMessage(transformation)->GetFreshIds(); + if (!fresh_ids.empty()) { + first_overflow_id = + std::max(first_overflow_id, + *std::max_element(fresh_ids.begin(), fresh_ids.end()) + 1); + } + } + + std::unique_ptr transformation_context = + MakeUnique( + MakeUnique(ir_context.get()), validator_options_, + MakeUnique(first_overflow_id)); + transformation_context->GetFactManager()->AddInitialFacts(consumer_, + initial_facts_); + + // We track the largest id bound observed, to ensure that it only increases + // as transformations are applied. + uint32_t max_observed_id_bound = ir_context->module()->id_bound(); + (void)(max_observed_id_bound); // Keep release-mode compilers happy. + + protobufs::TransformationSequence transformation_sequence_out; + + // Consider the transformation proto messages in turn. + uint32_t counter = 0; + for (auto& message : transformation_sequence_in_.transformation()) { + if (counter >= num_transformations_to_apply_) { + break; + } + counter++; + + auto transformation = Transformation::FromMessage(message); + + // Check whether the transformation can be applied. + if (transformation->IsApplicable(ir_context.get(), + *transformation_context)) { + // The transformation is applicable, so apply it, and copy it to the + // sequence of transformations that were applied. + transformation->Apply(ir_context.get(), transformation_context.get()); + *transformation_sequence_out.add_transformation() = message; + + assert(ir_context->module()->id_bound() >= max_observed_id_bound && + "The module's id bound should only increase due to applying " + "transformations."); + max_observed_id_bound = ir_context->module()->id_bound(); + + if (validate_during_replay_) { + std::vector binary_to_validate; + ir_context->module()->ToBinary(&binary_to_validate, false); + + // Check whether the latest transformation led to a valid binary. + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(), + validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Binary became invalid during replay (set a " + "breakpoint to inspect); stopping."); + return {Replayer::ReplayerResultStatus::kReplayValidationFailure, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + // The binary was valid, so it becomes the latest valid binary. + last_valid_binary = std::move(binary_to_validate); + } + } + } + + return {Replayer::ReplayerResultStatus::kComplete, std::move(ir_context), + std::move(transformation_context), + std::move(transformation_sequence_out)}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.h new file mode 100644 index 00000000..6730bd1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/replayer.h @@ -0,0 +1,105 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPLAYER_H_ +#define SOURCE_FUZZ_REPLAYER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by +// applying a series of pre-defined transformations. +class Replayer { + public: + // Possible statuses that can result from running the replayer. + enum class ReplayerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kInitialBinaryInvalid, + kReplayValidationFailure, + kTooManyTransformationsRequested, + }; + + struct ReplayerResult { + ReplayerResultStatus status; + std::unique_ptr transformed_module; + std::unique_ptr transformation_context; + protobufs::TransformationSequence applied_transformations; + }; + + Replayer(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t num_transformations_to_apply, bool validate_during_replay, + spv_validator_options validator_options); + + // Disables copy/move constructor/assignment operations. + Replayer(const Replayer&) = delete; + Replayer(Replayer&&) = delete; + Replayer& operator=(const Replayer&) = delete; + Replayer& operator=(Replayer&&) = delete; + + ~Replayer(); + + // Attempts to apply the first |num_transformations_to_apply_| transformations + // from |transformation_sequence_in_| to |binary_in_|. Initial facts about + // the input binary and the context in which it will execute are provided via + // |initial_facts_|. + // + // On success, returns a successful result status together with the + // transformations that were applied, the IR for the transformed module, and + // the transformation context that arises from applying these transformations. + // Otherwise, returns an appropriate result status, an empty transformation + // sequence, and null pointers for the IR context and transformation context. + ReplayerResult Run(); + + private: + // Target environment. + const spv_target_env target_env_; + + // Message consumer. + MessageConsumer consumer_; + + // The binary to which transformations are to be applied. + const std::vector& binary_in_; + + // Initial facts known to hold in advance of applying any transformations. + const protobufs::FactSequence& initial_facts_; + + // The transformations to be replayed. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // The number of transformations that should be replayed. + const uint32_t num_transformations_to_apply_; + + // Controls whether the validator should be run after every replay step. + const bool validate_during_replay_; + + // Options to control validation + spv_validator_options validator_options_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPLAYER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.cpp new file mode 100644 index 00000000..c1e2514c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/shrinker.h" + +#include + +#include "source/fuzz/added_function_reducer.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/replayer.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// A helper to get the size of a protobuf transformation sequence in a less +// verbose manner. +uint32_t NumRemainingTransformations( + const protobufs::TransformationSequence& transformation_sequence) { + return static_cast(transformation_sequence.transformation_size()); +} + +// A helper to return a transformation sequence identical to |transformations|, +// except that a chunk of size |chunk_size| starting from |chunk_index| x +// |chunk_size| is removed (or as many transformations as available if the whole +// chunk is not). +protobufs::TransformationSequence RemoveChunk( + const protobufs::TransformationSequence& transformations, + uint32_t chunk_index, uint32_t chunk_size) { + uint32_t lower = chunk_index * chunk_size; + uint32_t upper = std::min((chunk_index + 1) * chunk_size, + NumRemainingTransformations(transformations)); + assert(lower < upper); + assert(upper <= NumRemainingTransformations(transformations)); + protobufs::TransformationSequence result; + for (uint32_t j = 0; j < NumRemainingTransformations(transformations); j++) { + if (j >= lower && j < upper) { + continue; + } + protobufs::Transformation transformation = + transformations.transformation()[j]; + *result.mutable_transformation()->Add() = transformation; + } + return result; +} + +} // namespace + +Shrinker::Shrinker( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + const InterestingnessFunction& interestingness_function, + uint32_t step_limit, bool validate_during_replay, + spv_validator_options validator_options) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + interestingness_function_(interestingness_function), + step_limit_(step_limit), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options) {} + +Shrinker::~Shrinker() = default; + +Shrinker::ShrinkerResult Shrinker::Run() { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + SpirvTools tools(target_env_); + if (!tools.IsValid()) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return {Shrinker::ShrinkerResultStatus::kFailedToCreateSpirvToolsInterface, + std::vector(), protobufs::TransformationSequence()}; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return {Shrinker::ShrinkerResultStatus::kInitialBinaryInvalid, + std::vector(), protobufs::TransformationSequence()}; + } + + // Run a replay of the initial transformation sequence to check that it + // succeeds. + auto initial_replay_result = + Replayer(target_env_, consumer_, binary_in_, initial_facts_, + transformation_sequence_in_, + static_cast( + transformation_sequence_in_.transformation_size()), + validate_during_replay_, validator_options_) + .Run(); + if (initial_replay_result.status != + Replayer::ReplayerResultStatus::kComplete) { + return {ShrinkerResultStatus::kReplayFailed, std::vector(), + protobufs::TransformationSequence()}; + } + // Get the binary that results from running these transformations, and the + // subsequence of the initial transformations that actually apply (in + // principle this could be a strict subsequence). + std::vector current_best_binary; + initial_replay_result.transformed_module->module()->ToBinary( + ¤t_best_binary, false); + protobufs::TransformationSequence current_best_transformations = + std::move(initial_replay_result.applied_transformations); + + // Check that the binary produced by applying the initial transformations is + // indeed interesting. + if (!interestingness_function_(current_best_binary, 0)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is not interesting; stopping."); + return {ShrinkerResultStatus::kInitialBinaryNotInteresting, + std::vector(), protobufs::TransformationSequence()}; + } + + uint32_t attempt = 0; // Keeps track of the number of shrink attempts that + // have been tried, whether successful or not. + + uint32_t chunk_size = + std::max(1u, NumRemainingTransformations(current_best_transformations) / + 2); // The number of contiguous transformations that the + // shrinker will try to remove in one go; starts + // high and decreases during the shrinking process. + + // Keep shrinking until we: + // - reach the step limit, + // - run out of transformations to remove, or + // - cannot make the chunk size any smaller. + while (attempt < step_limit_ && + !current_best_transformations.transformation().empty() && + chunk_size > 0) { + bool progress_this_round = + false; // Used to decide whether to make the chunk size with which we + // remove transformations smaller. If we managed to remove at + // least one chunk of transformations at a particular chunk + // size, we set this flag so that we do not yet decrease the + // chunk size. + + assert(chunk_size <= + NumRemainingTransformations(current_best_transformations) && + "Chunk size should never exceed the number of transformations that " + "remain."); + + // The number of chunks is the ceiling of (#remaining_transformations / + // chunk_size). + const uint32_t num_chunks = + (NumRemainingTransformations(current_best_transformations) + + chunk_size - 1) / + chunk_size; + assert(num_chunks >= 1 && "There should be at least one chunk."); + assert(num_chunks * chunk_size >= + NumRemainingTransformations(current_best_transformations) && + "All transformations should be in some chunk."); + + // We go through the transformations in reverse, in chunks of size + // |chunk_size|, using |chunk_index| to track which chunk to try removing + // next. The loop exits early if we reach the shrinking step limit. + for (int chunk_index = num_chunks - 1; + attempt < step_limit_ && chunk_index >= 0; chunk_index--) { + // Remove a chunk of transformations according to the current index and + // chunk size. + auto transformations_with_chunk_removed = + RemoveChunk(current_best_transformations, + static_cast(chunk_index), chunk_size); + + // Replay the smaller sequence of transformations to get a next binary and + // transformation sequence. Note that the transformations arising from + // replay might be even smaller than the transformations with the chunk + // removed, because removing those transformations might make further + // transformations inapplicable. + auto replay_result = + Replayer( + target_env_, consumer_, binary_in_, initial_facts_, + transformations_with_chunk_removed, + static_cast( + transformations_with_chunk_removed.transformation_size()), + validate_during_replay_, validator_options_) + .Run(); + if (replay_result.status != Replayer::ReplayerResultStatus::kComplete) { + // Replay should not fail; if it does, we need to abort shrinking. + return {ShrinkerResultStatus::kReplayFailed, std::vector(), + protobufs::TransformationSequence()}; + } + + assert( + NumRemainingTransformations(replay_result.applied_transformations) >= + chunk_index * chunk_size && + "Removing this chunk of transformations should not have an effect " + "on earlier chunks."); + + std::vector transformed_binary; + replay_result.transformed_module->module()->ToBinary(&transformed_binary, + false); + if (interestingness_function_(transformed_binary, attempt)) { + // If the binary arising from the smaller transformation sequence is + // interesting, this becomes our current best binary and transformation + // sequence. + current_best_binary = std::move(transformed_binary); + current_best_transformations = + std::move(replay_result.applied_transformations); + progress_this_round = true; + } + // Either way, this was a shrink attempt, so increment our count of shrink + // attempts. + attempt++; + } + if (!progress_this_round) { + // If we didn't manage to remove any chunks at this chunk size, try a + // smaller chunk size. + chunk_size /= 2; + } + // Decrease the chunk size until it becomes no larger than the number of + // remaining transformations. + while (chunk_size > + NumRemainingTransformations(current_best_transformations)) { + chunk_size /= 2; + } + } + + // We now use spirv-reduce to minimise the functions associated with any + // AddFunction transformations that remain. + // + // Consider every remaining transformation. + for (uint32_t transformation_index = 0; + attempt < step_limit_ && + transformation_index < + static_cast( + current_best_transformations.transformation_size()); + transformation_index++) { + // Skip all transformations apart from TransformationAddFunction. + if (!current_best_transformations.transformation(transformation_index) + .has_add_function()) { + continue; + } + // Invoke spirv-reduce on the function encoded in this AddFunction + // transformation. The details of this are rather involved, and so are + // encapsulated in a separate class. + auto added_function_reducer_result = + AddedFunctionReducer(target_env_, consumer_, binary_in_, initial_facts_, + current_best_transformations, transformation_index, + interestingness_function_, validate_during_replay_, + validator_options_, step_limit_, attempt) + .Run(); + // Reducing the added function should succeed. If it doesn't, we report + // a shrinking error. + if (added_function_reducer_result.status != + AddedFunctionReducer::AddedFunctionReducerResultStatus::kComplete) { + return {ShrinkerResultStatus::kAddedFunctionReductionFailed, + std::vector(), protobufs::TransformationSequence()}; + } + assert(current_best_transformations.transformation_size() == + added_function_reducer_result.applied_transformations + .transformation_size() && + "The number of transformations should not have changed."); + current_best_binary = + std::move(added_function_reducer_result.transformed_binary); + current_best_transformations = + std::move(added_function_reducer_result.applied_transformations); + // The added function reducer reports how many reduction attempts + // spirv-reduce took when reducing the function. We regard each of these + // as a shrinker attempt. + attempt += added_function_reducer_result.num_reduction_attempts; + } + + // Indicate whether shrinking completed or was truncated due to reaching the + // step limit. + // + // Either way, the output from the shrinker is the best binary we saw, and the + // transformations that led to it. + assert(attempt <= step_limit_); + if (attempt == step_limit_) { + std::stringstream strstream; + strstream << "Shrinking did not complete; step limit " << step_limit_ + << " was reached."; + consumer_(SPV_MSG_WARNING, nullptr, {}, strstream.str().c_str()); + return {Shrinker::ShrinkerResultStatus::kStepLimitReached, + std::move(current_best_binary), + std::move(current_best_transformations)}; + } + return {Shrinker::ShrinkerResultStatus::kComplete, + std::move(current_best_binary), + std::move(current_best_transformations)}; +} + +uint32_t Shrinker::GetIdBound(const std::vector& binary) const { + // Build the module from the input binary. + std::unique_ptr ir_context = + BuildModule(target_env_, consumer_, binary.data(), binary.size()); + assert(ir_context && "Error building module."); + return ir_context->module()->id_bound(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.h new file mode 100644 index 00000000..b9015d66 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/shrinker.h @@ -0,0 +1,128 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_SHRINKER_H_ +#define SOURCE_FUZZ_SHRINKER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Shrinks a sequence of transformations that lead to an interesting SPIR-V +// binary to yield a smaller sequence of transformations that still produce an +// interesting binary. +class Shrinker { + public: + // Possible statuses that can result from running the shrinker. + enum class ShrinkerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kInitialBinaryInvalid, + kInitialBinaryNotInteresting, + kReplayFailed, + kStepLimitReached, + kAddedFunctionReductionFailed, + }; + + struct ShrinkerResult { + ShrinkerResultStatus status; + std::vector transformed_binary; + protobufs::TransformationSequence applied_transformations; + }; + + // The type for a function that will take a binary, |binary|, and return true + // if and only if the binary is deemed interesting. (The function also takes + // an integer argument, |counter|, that will be incremented each time the + // function is called; this is for debugging purposes). + // + // The notion of "interesting" depends on what properties of the binary or + // tools that process the binary we are trying to maintain during shrinking. + using InterestingnessFunction = std::function& binary, uint32_t counter)>; + + Shrinker(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + const InterestingnessFunction& interestingness_function, + uint32_t step_limit, bool validate_during_replay, + spv_validator_options validator_options); + + // Disables copy/move constructor/assignment operations. + Shrinker(const Shrinker&) = delete; + Shrinker(Shrinker&&) = delete; + Shrinker& operator=(const Shrinker&) = delete; + Shrinker& operator=(Shrinker&&) = delete; + + ~Shrinker(); + + // Requires that when |transformation_sequence_in_| is applied to |binary_in_| + // with initial facts |initial_facts_|, the resulting binary is interesting + // according to |interestingness_function_|. + // + // If shrinking succeeded -- possibly terminating early due to reaching the + // shrinker's step limit -- an associated result status is returned together + // with a subsequence of |transformation_sequence_in_| that, when applied + // to |binary_in_| with initial facts |initial_facts_|, produces a binary + // that is also interesting according to |interestingness_function_|; this + // binary is also returned. + // + // If shrinking failed for some reason, an appropriate result status is + // returned together with an empty binary and empty transformation sequence. + ShrinkerResult Run(); + + private: + // Returns the id bound for the given SPIR-V binary, which is assumed to be + // valid. + uint32_t GetIdBound(const std::vector& binary) const; + + // Target environment. + const spv_target_env target_env_; + + // Message consumer that will be invoked once for each message communicated + // from the library. + MessageConsumer consumer_; + + // The binary to which transformations are to be applied. + const std::vector& binary_in_; + + // Initial facts known to hold in advance of applying any transformations. + const protobufs::FactSequence& initial_facts_; + + // The series of transformations to be shrunk. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // Function that decides whether a given module is interesting. + const InterestingnessFunction& interestingness_function_; + + // Step limit to decide when to terminate shrinking early. + const uint32_t step_limit_; + + // Determines whether to check for validity during the replaying of + // transformations. + const bool validate_during_replay_; + + // Options to control validation. + spv_validator_options validator_options_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_SHRINKER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.cpp new file mode 100644 index 00000000..70a302b7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.cpp @@ -0,0 +1,411 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_copy_memory.h" +#include "source/fuzz/transformation_add_dead_block.h" +#include "source/fuzz/transformation_add_dead_break.h" +#include "source/fuzz/transformation_add_dead_continue.h" +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_loop_preheader.h" +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" +#include "source/fuzz/transformation_add_no_contraction_decoration.h" +#include "source/fuzz/transformation_add_opphi_synonym.h" +#include "source/fuzz/transformation_add_parameter.h" +#include "source/fuzz/transformation_add_relaxed_decoration.h" +#include "source/fuzz/transformation_add_spec_constant_op.h" +#include "source/fuzz/transformation_add_synonym.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" +#include "source/fuzz/transformation_adjust_branch_weights.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_composite_extract.h" +#include "source/fuzz/transformation_composite_insert.h" +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" +#include "source/fuzz/transformation_duplicate_region_with_selection.h" +#include "source/fuzz/transformation_equation_instruction.h" +#include "source/fuzz/transformation_expand_vector_reduction.h" +#include "source/fuzz/transformation_flatten_conditional_branch.h" +#include "source/fuzz/transformation_function_call.h" +#include "source/fuzz/transformation_inline_function.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" +#include "source/fuzz/transformation_load.h" +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" +#include "source/fuzz/transformation_merge_blocks.h" +#include "source/fuzz/transformation_merge_function_returns.h" +#include "source/fuzz/transformation_move_block_down.h" +#include "source/fuzz/transformation_move_instruction_down.h" +#include "source/fuzz/transformation_mutate_pointer.h" +#include "source/fuzz/transformation_outline_function.h" +#include "source/fuzz/transformation_permute_function_parameters.h" +#include "source/fuzz/transformation_permute_phi_operands.h" +#include "source/fuzz/transformation_propagate_instruction_down.h" +#include "source/fuzz/transformation_propagate_instruction_up.h" +#include "source/fuzz/transformation_push_id_through_variable.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" +#include "source/fuzz/transformation_replace_irrelevant_id.h" +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" +#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h" +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" +#include "source/fuzz/transformation_replace_params_with_struct.h" +#include "source/fuzz/transformation_set_function_control.h" +#include "source/fuzz/transformation_set_loop_control.h" +#include "source/fuzz/transformation_set_memory_operands_mask.h" +#include "source/fuzz/transformation_set_selection_control.h" +#include "source/fuzz/transformation_split_block.h" +#include "source/fuzz/transformation_store.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" +#include "source/fuzz/transformation_swap_function_variables.h" +#include "source/fuzz/transformation_swap_two_functions.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" +#include "source/fuzz/transformation_vector_shuffle.h" +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" +#include "source/fuzz/transformation_wrap_region_in_selection.h" +#include "source/fuzz/transformation_wrap_vector_synonym.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +Transformation::~Transformation() = default; + +std::unique_ptr Transformation::FromMessage( + const protobufs::Transformation& message) { + switch (message.transformation_case()) { + case protobufs::Transformation::TransformationCase::kAccessChain: + return MakeUnique(message.access_chain()); + case protobufs::Transformation::TransformationCase:: + kAddBitInstructionSynonym: + return MakeUnique( + message.add_bit_instruction_synonym()); + case protobufs::Transformation::TransformationCase::kAddConstantBoolean: + return MakeUnique( + message.add_constant_boolean()); + case protobufs::Transformation::TransformationCase::kAddConstantComposite: + return MakeUnique( + message.add_constant_composite()); + case protobufs::Transformation::TransformationCase::kAddConstantNull: + return MakeUnique( + message.add_constant_null()); + case protobufs::Transformation::TransformationCase::kAddConstantScalar: + return MakeUnique( + message.add_constant_scalar()); + case protobufs::Transformation::TransformationCase::kAddCopyMemory: + return MakeUnique(message.add_copy_memory()); + case protobufs::Transformation::TransformationCase::kAddDeadBlock: + return MakeUnique(message.add_dead_block()); + case protobufs::Transformation::TransformationCase::kAddDeadBreak: + return MakeUnique(message.add_dead_break()); + case protobufs::Transformation::TransformationCase::kAddDeadContinue: + return MakeUnique( + message.add_dead_continue()); + case protobufs::Transformation::TransformationCase:: + kAddEarlyTerminatorWrapper: + return MakeUnique( + message.add_early_terminator_wrapper()); + case protobufs::Transformation::TransformationCase::kAddFunction: + return MakeUnique(message.add_function()); + case protobufs::Transformation::TransformationCase::kAddGlobalUndef: + return MakeUnique( + message.add_global_undef()); + case protobufs::Transformation::TransformationCase::kAddGlobalVariable: + return MakeUnique( + message.add_global_variable()); + case protobufs::Transformation::TransformationCase:: + kAddImageSampleUnusedComponents: + return MakeUnique( + message.add_image_sample_unused_components()); + case protobufs::Transformation::TransformationCase::kAddLocalVariable: + return MakeUnique( + message.add_local_variable()); + case protobufs::Transformation::TransformationCase::kAddLoopPreheader: + return MakeUnique( + message.add_loop_preheader()); + case protobufs::Transformation::TransformationCase:: + kAddLoopToCreateIntConstantSynonym: + return MakeUnique( + message.add_loop_to_create_int_constant_synonym()); + case protobufs::Transformation::TransformationCase:: + kAddNoContractionDecoration: + return MakeUnique( + message.add_no_contraction_decoration()); + case protobufs::Transformation::TransformationCase::kAddOpphiSynonym: + return MakeUnique( + message.add_opphi_synonym()); + case protobufs::Transformation::TransformationCase::kAddParameter: + return MakeUnique(message.add_parameter()); + case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration: + return MakeUnique( + message.add_relaxed_decoration()); + case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: + return MakeUnique( + message.add_spec_constant_op()); + case protobufs::Transformation::TransformationCase::kAddSynonym: + return MakeUnique(message.add_synonym()); + case protobufs::Transformation::TransformationCase::kAddTypeArray: + return MakeUnique(message.add_type_array()); + case protobufs::Transformation::TransformationCase::kAddTypeBoolean: + return MakeUnique( + message.add_type_boolean()); + case protobufs::Transformation::TransformationCase::kAddTypeFloat: + return MakeUnique(message.add_type_float()); + case protobufs::Transformation::TransformationCase::kAddTypeFunction: + return MakeUnique( + message.add_type_function()); + case protobufs::Transformation::TransformationCase::kAddTypeInt: + return MakeUnique(message.add_type_int()); + case protobufs::Transformation::TransformationCase::kAddTypeMatrix: + return MakeUnique(message.add_type_matrix()); + case protobufs::Transformation::TransformationCase::kAddTypePointer: + return MakeUnique( + message.add_type_pointer()); + case protobufs::Transformation::TransformationCase::kAddTypeStruct: + return MakeUnique(message.add_type_struct()); + case protobufs::Transformation::TransformationCase::kAddTypeVector: + return MakeUnique(message.add_type_vector()); + case protobufs::Transformation::TransformationCase::kAdjustBranchWeights: + return MakeUnique( + message.adjust_branch_weights()); + case protobufs::Transformation::TransformationCase::kCompositeConstruct: + return MakeUnique( + message.composite_construct()); + case protobufs::Transformation::TransformationCase::kCompositeExtract: + return MakeUnique( + message.composite_extract()); + case protobufs::Transformation::TransformationCase::kCompositeInsert: + return MakeUnique( + message.composite_insert()); + case protobufs::Transformation::TransformationCase:: + kComputeDataSynonymFactClosure: + return MakeUnique( + message.compute_data_synonym_fact_closure()); + case protobufs::Transformation::TransformationCase:: + kDuplicateRegionWithSelection: + return MakeUnique( + message.duplicate_region_with_selection()); + case protobufs::Transformation::TransformationCase::kEquationInstruction: + return MakeUnique( + message.equation_instruction()); + case protobufs::Transformation::TransformationCase::kExpandVectorReduction: + return MakeUnique( + message.expand_vector_reduction()); + case protobufs::Transformation::TransformationCase:: + kFlattenConditionalBranch: + return MakeUnique( + message.flatten_conditional_branch()); + case protobufs::Transformation::TransformationCase::kFunctionCall: + return MakeUnique(message.function_call()); + case protobufs::Transformation::TransformationCase::kInlineFunction: + return MakeUnique( + message.inline_function()); + case protobufs::Transformation::TransformationCase:: + kInvertComparisonOperator: + return MakeUnique( + message.invert_comparison_operator()); + case protobufs::Transformation::TransformationCase::kLoad: + return MakeUnique(message.load()); + case protobufs::Transformation::TransformationCase:: + kMakeVectorOperationDynamic: + return MakeUnique( + message.make_vector_operation_dynamic()); + case protobufs::Transformation::TransformationCase::kMergeBlocks: + return MakeUnique(message.merge_blocks()); + case protobufs::Transformation::TransformationCase::kMergeFunctionReturns: + return MakeUnique( + message.merge_function_returns()); + case protobufs::Transformation::TransformationCase::kMoveBlockDown: + return MakeUnique(message.move_block_down()); + case protobufs::Transformation::TransformationCase::kMoveInstructionDown: + return MakeUnique( + message.move_instruction_down()); + case protobufs::Transformation::TransformationCase::kMutatePointer: + return MakeUnique(message.mutate_pointer()); + case protobufs::Transformation::TransformationCase::kOutlineFunction: + return MakeUnique( + message.outline_function()); + case protobufs::Transformation::TransformationCase:: + kPermuteFunctionParameters: + return MakeUnique( + message.permute_function_parameters()); + case protobufs::Transformation::TransformationCase::kPermutePhiOperands: + return MakeUnique( + message.permute_phi_operands()); + case protobufs::Transformation::TransformationCase:: + kPropagateInstructionDown: + return MakeUnique( + message.propagate_instruction_down()); + case protobufs::Transformation::TransformationCase::kPropagateInstructionUp: + return MakeUnique( + message.propagate_instruction_up()); + case protobufs::Transformation::TransformationCase::kPushIdThroughVariable: + return MakeUnique( + message.push_id_through_variable()); + case protobufs::Transformation::TransformationCase:: + kRecordSynonymousConstants: + return MakeUnique( + message.record_synonymous_constants()); + case protobufs::Transformation::TransformationCase:: + kReplaceAddSubMulWithCarryingExtended: + return MakeUnique( + message.replace_add_sub_mul_with_carrying_extended()); + case protobufs::Transformation::TransformationCase:: + kReplaceBooleanConstantWithConstantBinary: + return MakeUnique( + message.replace_boolean_constant_with_constant_binary()); + case protobufs::Transformation::TransformationCase:: + kReplaceBranchFromDeadBlockWithExit: + return MakeUnique( + message.replace_branch_from_dead_block_with_exit()); + case protobufs::Transformation::TransformationCase:: + kReplaceConstantWithUniform: + return MakeUnique( + message.replace_constant_with_uniform()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyMemoryWithLoadStore: + return MakeUnique( + message.replace_copy_memory_with_load_store()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyObjectWithStoreLoad: + return MakeUnique( + message.replace_copy_object_with_store_load()); + case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym: + return MakeUnique( + message.replace_id_with_synonym()); + case protobufs::Transformation::TransformationCase::kReplaceIrrelevantId: + return MakeUnique( + message.replace_irrelevant_id()); + case protobufs::Transformation::TransformationCase:: + kReplaceLinearAlgebraInstruction: + return MakeUnique( + message.replace_linear_algebra_instruction()); + case protobufs::Transformation::TransformationCase:: + kReplaceLoadStoreWithCopyMemory: + return MakeUnique( + message.replace_load_store_with_copy_memory()); + case protobufs::Transformation::TransformationCase:: + kReplaceOpselectWithConditionalBranch: + return MakeUnique( + message.replace_opselect_with_conditional_branch()); + case protobufs::Transformation::TransformationCase:: + kReplaceParameterWithGlobal: + return MakeUnique( + message.replace_parameter_with_global()); + case protobufs::Transformation::TransformationCase:: + kReplaceParamsWithStruct: + return MakeUnique( + message.replace_params_with_struct()); + case protobufs::Transformation::TransformationCase:: + kReplaceOpphiIdFromDeadPredecessor: + return MakeUnique( + message.replace_opphi_id_from_dead_predecessor()); + case protobufs::Transformation::TransformationCase::kSetFunctionControl: + return MakeUnique( + message.set_function_control()); + case protobufs::Transformation::TransformationCase::kSetLoopControl: + return MakeUnique( + message.set_loop_control()); + case protobufs::Transformation::TransformationCase::kSetMemoryOperandsMask: + return MakeUnique( + message.set_memory_operands_mask()); + case protobufs::Transformation::TransformationCase::kSetSelectionControl: + return MakeUnique( + message.set_selection_control()); + case protobufs::Transformation::TransformationCase::kSplitBlock: + return MakeUnique(message.split_block()); + case protobufs::Transformation::TransformationCase::kStore: + return MakeUnique(message.store()); + case protobufs::Transformation::TransformationCase::kSwapCommutableOperands: + return MakeUnique( + message.swap_commutable_operands()); + case protobufs::Transformation::TransformationCase:: + kSwapConditionalBranchOperands: + return MakeUnique( + message.swap_conditional_branch_operands()); + case protobufs::Transformation::TransformationCase::kSwapFunctionVariables: + return MakeUnique( + message.swap_function_variables()); + case protobufs::Transformation::TransformationCase::kSwapTwoFunctions: + return MakeUnique( + message.swap_two_functions()); + case protobufs::Transformation::TransformationCase:: + kToggleAccessChainInstruction: + return MakeUnique( + message.toggle_access_chain_instruction()); + case protobufs::Transformation::TransformationCase::kVectorShuffle: + return MakeUnique(message.vector_shuffle()); + case protobufs::Transformation::TransformationCase:: + kWrapEarlyTerminatorInFunction: + return MakeUnique( + message.wrap_early_terminator_in_function()); + case protobufs::Transformation::TransformationCase::kWrapRegionInSelection: + return MakeUnique( + message.wrap_region_in_selection()); + case protobufs::Transformation::TransformationCase::kWrapVectorSynonym: + return MakeUnique( + message.wrap_vector_synonym()); + case protobufs::Transformation::TRANSFORMATION_NOT_SET: + assert(false && "An unset transformation was encountered."); + return nullptr; + } + assert(false && "Should be unreachable as all cases must be handled above."); + return nullptr; +} + +bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* ir_context, + std::set* ids_used_by_this_transformation) { + if (!fuzzerutil::IsFreshId(ir_context, id)) { + return false; + } + if (ids_used_by_this_transformation->count(id) != 0) { + return false; + } + ids_used_by_this_transformation->insert(id); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.h new file mode 100644 index 00000000..8d618e8c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation.h @@ -0,0 +1,108 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Rules for transformations +// ------------------------- +// +// - Immutability: a transformation must be immutable. +// - Ability to copy and serialize: to ensure that a copy of a transformation, +// possibly saved out to disk and read back again, is indistinguishable +// from the original transformation, thus a transformation must depend +// only on well-defined pieces of state, such as instruction ids. It must +// not rely on state such as pointers to instructions and blocks. +// - Determinism: the effect of a transformation on a module be a deterministic +// function of the module and the transformation. Any randomization should +// be applied before creating the transformation, not during its +// application. +// - Well-defined and precondition: the 'IsApplicable' method should only +// return true if the transformation can be cleanly applied to the given +// module, to mutate it into a valid and semantically-equivalent module, as +// long as the module is initially valid. +// - Ability to test precondition on any valid module: 'IsApplicable' should be +// designed so that it is safe to ask whether a transformation is +// applicable to an arbitrary valid module. For example, if a +// transformation involves a block id, 'IsApplicable' should check whether +// the module indeed has a block with that id, and return false if not. It +// must not assume that there is such a block. +// - Documented precondition: while the implementation of 'IsApplicable' should +// should codify the precondition, the method should be commented in the +// header file for a transformation with a precise English description of +// the precondition. +// - Documented effect: while the implementation of 'Apply' should codify the +// effect of the transformation, the method should be commented in the +// header file for a transformation with a precise English description of +// the effect. + +class Transformation { + public: + virtual ~Transformation(); + + // Factory method to obtain a transformation object from the protobuf + // representation of a transformation given by |message|. + static std::unique_ptr FromMessage( + const protobufs::Transformation& message); + + // A precondition that determines whether the transformation can be cleanly + // applied in a semantics-preserving manner to the SPIR-V module given by + // |ir_context|, in the presence of facts and other contextual information + // captured by |transformation_context|. + // + // Preconditions for individual transformations must be documented in the + // associated header file using precise English. The transformation context + // provides access to facts about the module that are known to be true, on + // which the precondition may depend. + virtual bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const = 0; + + // Requires that IsApplicable(ir_context, *transformation_context) holds. + // Applies the transformation, mutating |ir_context| and possibly updating + // |transformation_context| with new facts established by the transformation. + virtual void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const = 0; + + // Returns the set of fresh ids that appear in the transformation's protobuf + // message. + virtual std::unordered_set GetFreshIds() const = 0; + + // Turns the transformation into a protobuf message for serialization. + virtual protobufs::Transformation ToMessage() const = 0; + + // Helper that returns true if and only if (a) |id| is a fresh id for the + // module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of + // ids already known to be in use by a transformation. This is useful when + // checking id freshness for a transformation that uses many ids, all of which + // must be distinct. + static bool CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* ir_context, + std::set* ids_used_by_this_transformation); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.cpp new file mode 100644 index 00000000..1e7f87bd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.cpp @@ -0,0 +1,438 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_access_chain.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAccessChain::TransformationAccessChain( + protobufs::TransformationAccessChain message) + : message_(std::move(message)) {} + +TransformationAccessChain::TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& fresh_ids_for_clamping) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + for (auto id : index_id) { + message_.add_index_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + for (auto clamping_ids_pair : fresh_ids_for_clamping) { + protobufs::UInt32Pair pair; + pair.set_first(clamping_ids_pair.first); + pair.set_second(clamping_ids_pair.second); + *message_.add_fresh_ids_for_clamping() = pair; + } +} + +bool TransformationAccessChain::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Keep track of the fresh ids used to make sure that they are distinct. + std::set fresh_ids_used; + + // The result id must be fresh. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.fresh_id(), ir_context, &fresh_ids_used)) { + return false; + } + // The pointer id must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + if (pointer_type->opcode() != spv::Op::OpTypePointer) { + return false; + } + + // The described instruction to insert before must exist and be a suitable + // point where an OpAccessChain instruction could be inserted. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpAccessChain, instruction_to_insert_before)) { + return false; + } + + // Do not allow making an access chain from a null or undefined pointer, as + // we do not want to allow accessing such pointers. This might be acceptable + // in dead blocks, but we conservatively avoid it. + switch (pointer->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + assert( + false && + "Access chains should not be created from null/undefined pointers"); + return false; + default: + break; + } + + // The pointer on which the access chain is to be based needs to be available + // (according to dominance rules) at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.pointer_id())) { + return false; + } + + // We now need to use the given indices to walk the type structure of the + // base type of the pointer, making sure that (a) the indices correspond to + // integers, and (b) these integer values are in-bounds. + + // Start from the base type of the pointer. + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + int id_pairs_used = 0; + + // Consider the given index ids in turn. + for (auto index_id : message_.index_id()) { + // The index value will correspond to the value of the index if the object + // is a struct, otherwise the value 0 will be used. + uint32_t index_value; + + // Check whether the object is a struct. + if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() == + spv::Op::OpTypeStruct) { + // It is a struct: we need to retrieve the integer value. + + bool successful; + std::tie(successful, index_value) = + GetStructIndexValue(ir_context, index_id, subobject_type_id); + + if (!successful) { + return false; + } + } else { + // It is not a struct: the index will need clamping. + + if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) { + // We don't have enough ids + return false; + } + + // Get two new ids to use and update the amount used. + protobufs::UInt32Pair fresh_ids = + message_.fresh_ids_for_clamping()[id_pairs_used++]; + + // Valid ids need to have been given + if (fresh_ids.first() == 0 || fresh_ids.second() == 0) { + return false; + } + + // Check that the ids are actually fresh and not already used by this + // transformation. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_ids.first(), ir_context, &fresh_ids_used) || + !CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_ids.second(), ir_context, &fresh_ids_used)) { + return false; + } + + if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) { + return false; + } + + // Perform the clamping using the fresh ids at our disposal. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(subobject_type_id), + ir_context); + + // The module must have an integer constant of value bound-1 of the same + // type as the index. + if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType( + ir_context, bound - 1, index_instruction->type_id())) { + return false; + } + + // The module must have the definition of bool type to make a comparison. + if (!fuzzerutil::MaybeGetBoolType(ir_context)) { + return false; + } + + // The index is not necessarily a constant, so we may not know its value. + // We can use index 0 because the components of a non-struct composite + // all have the same type, and index 0 is always in bounds. + index_value = 0; + } + + // Try to walk down the type using this index. This will yield 0 if the + // type is not a composite or the index is out of bounds, and the id of + // the next type otherwise. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, subobject_type_id, index_value); + if (!subobject_type_id) { + // Either the type was not a composite (so that too many indices were + // provided), or the index was out of bounds. + return false; + } + } + // At this point, |subobject_type_id| is the type of the value targeted by + // the new access chain. The result type of the access chain should be a + // pointer to this type, with the same storage class as for the original + // pointer. Such a pointer type needs to exist in the module. + // + // We do not use the type manager to look up this type, due to problems + // associated with pointers to isomorphic structs being regarded as the same. + return fuzzerutil::MaybeGetPointerType( + ir_context, subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))) != 0; +} + +void TransformationAccessChain::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // The operands to the access chain are the pointer followed by the indices. + // The result type of the access chain is determined by where the indices + // lead. We thus push the pointer to a sequence of operands, and then follow + // the indices, pushing each to the operand list and tracking the type + // obtained by following it. Ultimately this yields the type of the + // component reached by following all the indices, and the result type is + // a pointer to this component type. + opt::Instruction::OperandList operands; + + // Add the pointer id itself. + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}); + + // Start walking the indices, starting with the pointer's base type. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef( + ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + uint32_t id_pairs_used = 0; + + opt::Instruction* instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(instruction_to_insert_before); + + // Go through the index ids in turn. + for (auto index_id : message_.index_id()) { + uint32_t index_value; + + // Actual id to be used in the instruction: the original id + // or the clamped one. + uint32_t new_index_id; + + // Check whether the object is a struct. + if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() == + spv::Op::OpTypeStruct) { + // It is a struct: we need to retrieve the integer value. + + index_value = + GetStructIndexValue(ir_context, index_id, subobject_type_id).second; + + new_index_id = index_id; + + } else { + // It is not a struct: the index will need clamping. + + // Get two new ids to use and update the amount used. + protobufs::UInt32Pair fresh_ids = + message_.fresh_ids_for_clamping()[id_pairs_used++]; + + // Perform the clamping using the fresh ids at our disposal. + // The module will not be changed if |add_clamping_instructions| is not + // set. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(subobject_type_id), + ir_context); + + auto bound_minus_one_id = + fuzzerutil::MaybeGetIntegerConstantFromValueAndType( + ir_context, bound - 1, index_instruction->type_id()); + + assert(bound_minus_one_id && + "A constant of value bound - 1 and the same type as the index " + "must exist as a precondition."); + + uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context); + + assert(bool_type_id && + "An OpTypeBool instruction must exist as a precondition."); + + auto int_type_inst = + ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + + // Clamp the integer and add the corresponding instructions in the module + // if |add_clamping_instructions| is set. + + // Compare the index with the bound via an instruction of the form: + // %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one. + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first()); + auto comparison_instruction = MakeUnique( + ir_context, spv::Op::OpULessThanEqual, bool_type_id, + fresh_ids.first(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})); + auto comparison_instruction_ptr = comparison_instruction.get(); + instruction_to_insert_before->InsertBefore( + std::move(comparison_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + comparison_instruction_ptr); + ir_context->set_instr_block(comparison_instruction_ptr, enclosing_block); + + // Select the index if in-bounds, otherwise one less than the bound: + // %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id + // %bound_minus_one + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second()); + auto select_instruction = MakeUnique( + ir_context, spv::Op::OpSelect, int_type_inst->result_id(), + fresh_ids.second(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}}, + {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})); + auto select_instruction_ptr = select_instruction.get(); + instruction_to_insert_before->InsertBefore(std::move(select_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(select_instruction_ptr); + ir_context->set_instr_block(select_instruction_ptr, enclosing_block); + + new_index_id = fresh_ids.second(); + + index_value = 0; + } + + // Add the correct index id to the operands. + operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}}); + + // Walk to the next type in the composite object using this index. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, subobject_type_id, index_value); + } + // The access chain's result type is a pointer to the composite component + // that was reached after following all indices. The storage class is that + // of the original pointer. + uint32_t result_type = fuzzerutil::MaybeGetPointerType( + ir_context, subobject_type_id, + static_cast(pointer_type->GetSingleWordInOperand(0))); + + // Add the access chain instruction to the module, and update the module's + // id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto access_chain_instruction = + MakeUnique(ir_context, spv::Op::OpAccessChain, + result_type, message_.fresh_id(), operands); + auto access_chain_instruction_ptr = access_chain_instruction.get(); + instruction_to_insert_before->InsertBefore( + std::move(access_chain_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + access_chain_instruction_ptr); + ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block); + + // If the base pointer's pointee value was irrelevant, the same is true of + // the pointee value of the result of this access chain. + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAccessChain::ToMessage() const { + protobufs::Transformation result; + *result.mutable_access_chain() = message_; + return result; +} + +std::pair TransformationAccessChain::GetStructIndexValue( + opt::IRContext* ir_context, uint32_t index_id, + uint32_t object_type_id) const { + assert(ir_context->get_def_use_mgr()->GetDef(object_type_id)->opcode() == + spv::Op::OpTypeStruct && + "Precondition: the type must be a struct type."); + if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) { + return {false, 0}; + } + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context); + + // Ensure that the index given must represent a constant. + assert(spvOpcodeIsConstant(index_instruction->opcode()) && + "A non-constant index should already have been rejected."); + + // The index must be in bounds. + uint32_t value = index_instruction->GetSingleWordInOperand(0); + + if (value >= bound) { + return {false, 0}; + } + + return {true, value}; +} + +bool TransformationAccessChain::ValidIndexToComposite( + opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) { + auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id); + // The object being indexed must be a composite. + if (!spvOpcodeIsComposite(object_type_def->opcode())) { + return false; + } + + // Get the defining instruction of the index. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + if (!index_instruction) { + return false; + } + + // The index type must be 32-bit integer. + auto index_type = + ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + if (index_type->opcode() != spv::Op::OpTypeInt || + index_type->GetSingleWordInOperand(0) != 32) { + return false; + } + + // If the object being traversed is a struct, the id must correspond to an + // in-bound constant. + if (object_type_def->opcode() == spv::Op::OpTypeStruct) { + if (!spvOpcodeIsConstant(index_instruction->opcode())) { + return false; + } + } + return true; +} + +std::unordered_set TransformationAccessChain::GetFreshIds() const { + std::unordered_set result = {message_.fresh_id()}; + for (auto& fresh_ids_for_clamping : message_.fresh_ids_for_clamping()) { + result.insert(fresh_ids_for_clamping.first()); + result.insert(fresh_ids_for_clamping.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.h new file mode 100644 index 00000000..4e4fd2b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_access_chain.h @@ -0,0 +1,106 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAccessChain : public Transformation { + public: + explicit TransformationAccessChain( + protobufs::TransformationAccessChain message); + + TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& fresh_ids_for_clamping = + {}); + + // - |message_.fresh_id| must be fresh. + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is legitimate to insert an OpAccessChain instruction. + // - |message_.pointer_id| must be a result id with pointer type that is + // available (according to dominance rules) at the insertion point. + // - The pointer must not be OpConstantNull or OpUndef. + // - |message_.index_id| must be a sequence of ids of 32-bit integers + // such that it is possible to walk the pointee type of + // |message_.pointer_id| using these indices. + // - All indices used to access a struct must be OpConstant. + // - The indices used to index non-struct composites will be clamped to be + // in bound. Enough fresh ids must be given in + // |message_.fresh_id_for_clamping| to perform clamping (2 for + // each index accessing a non-struct). This requires the bool type and + // a constant of value (bound - 1) to be declared in the module. + // - If type t is the final type reached by walking these indices, the module + // must include an instruction "OpTypePointer SC %t" where SC is the storage + // class associated with |message_.pointer_id|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpAccessChain %ptr |message_.index_id| + // where %ptr is the result if of an instruction declaring a pointer to the + // type reached by walking the pointee type of |message_.pointer_id| using + // the indices in |message_.index_id|, and with the same storage class as + // |message_.pointer_id|. + // + // For each of the indices traversing non-struct composites, two clamping + // instructions are added using ids in |message_.fresh_id_for_clamping|. + // + // If the fact manager in |transformation_context| reports that + // |message_.pointer_id| has an irrelevant pointee value, then the fact that + // |message_.fresh_id| (the result of the access chain) also has an irrelevant + // pointee value is also recorded. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns {false, 0} in each of the following cases: + // - |index_id| does not correspond to a 32-bit integer constant + // - |object_type_id| must be a struct type + // - the constant at |index_id| is out of bounds. + // Otherwise, returns {true, value}, where value is the value of the constant + // at |index_id|. + std::pair GetStructIndexValue(opt::IRContext* ir_context, + uint32_t index_id, + uint32_t object_type_id) const; + + // Returns true if |index_id| corresponds, in the given context, to a 32-bit + // integer which can be used to index an object of the type specified by + // |object_type_id|. Returns false otherwise. + static bool ValidIndexToComposite(opt::IRContext* ir_context, + uint32_t index_id, uint32_t object_type_id); + + protobufs::TransformationAccessChain message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp new file mode 100644 index 00000000..4b26c2b5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( + protobufs::TransformationAddBitInstructionSynonym message) + : message_(std::move(message)) {} + +TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( + const uint32_t instruction_result_id, + const std::vector& fresh_ids) { + message_.set_instruction_result_id(instruction_result_id); + *message_.mutable_fresh_ids() = + google::protobuf::RepeatedField( + fresh_ids.begin(), fresh_ids.end()); +} + +bool TransformationAddBitInstructionSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // Checks on: only integer operands are supported, instructions are bitwise + // operations only. Signedness of the operands must be the same. + if (!IsInstructionSupported(ir_context, instruction)) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3791): + // This condition could be relaxed if the index exists as another integer + // type. + // All bit indexes must be defined as 32-bit unsigned integers. + uint32_t width = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->width(); + for (uint32_t i = 0; i < width; i++) { + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {i}, 32, false, false)) { + return false; + } + } + + // |message_.fresh_ids.size| must have the exact number of fresh ids required + // to apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + // All ids in |message_.fresh_ids| must be fresh. + for (uint32_t fresh_id : message_.fresh_ids()) { + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + } + + return true; +} + +void TransformationAddBitInstructionSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto bit_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // Use an appropriate helper function to add the new instruction and new + // synonym fact. The helper function should take care of invalidating + // analyses before adding facts. + switch (bit_instruction->opcode()) { + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context, + bit_instruction); + break; + default: + assert(false && "Should be unreachable."); + break; + } +} + +bool TransformationAddBitInstructionSynonym::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now we only support certain operations. When this issue is addressed + // the following conditional can use the function |spvOpcodeIsBit|. + // |instruction| must be defined and must be a supported bit instruction. + if (!instruction || (instruction->opcode() != spv::Op::OpBitwiseOr && + instruction->opcode() != spv::Op::OpBitwiseXor && + instruction->opcode() != spv::Op::OpBitwiseAnd && + instruction->opcode() != spv::Op::OpNot)) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): + // Right now, only integer operands are supported. + if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + return false; + } + + if (instruction->opcode() == spv::Op::OpNot) { + auto operand = instruction->GetInOperand(0).words[0]; + auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand); + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + auto operand_sign = operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return operand_sign == type_id_sign; + + } else { + // Other BitWise operations that takes two operands. + auto first_operand = instruction->GetInOperand(0).words[0]; + auto first_operand_inst = + ir_context->get_def_use_mgr()->GetDef(first_operand); + auto first_operand_type = + ir_context->get_type_mgr()->GetType(first_operand_inst->type_id()); + auto first_operand_sign = first_operand_type->AsInteger()->IsSigned(); + + auto second_operand = instruction->GetInOperand(1).words[0]; + auto second_operand_inst = + ir_context->get_def_use_mgr()->GetDef(second_operand); + auto second_operand_type = + ir_context->get_type_mgr()->GetType(second_operand_inst->type_id()); + auto second_operand_sign = second_operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return first_operand_sign == second_operand_sign && + first_operand_sign == type_id_sign; + } +} + +protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_bit_instruction_synonym() = message_; + return result; +} + +uint32_t TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* bit_instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now, only certain operations are supported. + switch (bit_instruction->opcode()) { + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + return (2 + bit_instruction->NumInOperands()) * + ir_context->get_type_mgr() + ->GetType(bit_instruction->type_id()) + ->AsInteger() + ->width() - + 1; + default: + assert(false && "Unsupported bit instruction."); + return 0; + } +} + +void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + opt::Instruction* bit_instruction) const { + // Fresh id iterator. + auto fresh_id = message_.fresh_ids().begin(); + + // |width| is the bit width of operands (8, 16, 32 or 64). + const uint32_t width = ir_context->get_type_mgr() + ->GetType(bit_instruction->type_id()) + ->AsInteger() + ->width(); + + // |count| is the number of bits to be extracted and inserted at a time. + const uint32_t count = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, false, false); + + // |extracted_bit_instructions| is the collection of OpBiwise* or OpNot + // instructions that evaluate the extracted bits. Those ids will be used to + // insert the result bits. + std::vector extracted_bit_instructions(width); + + for (uint32_t i = 0; i < width; i++) { + // |offset| is the current bit index. + uint32_t offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {i}, 32, false, false); + + // |bit_extract_ids| are the two extracted bits from the operands. + opt::Instruction::OperandList bit_extract_ids; + + // Extracts the i-th bit from operands. + for (auto operand = bit_instruction->begin() + 2; + operand != bit_instruction->end(); operand++) { + auto bit_extract = + opt::Instruction(ir_context, spv::Op::OpBitFieldUExtract, + bit_instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, operand->words}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_extract)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_extract.result_id()); + bit_extract_ids.push_back( + {SPV_OPERAND_TYPE_ID, {bit_extract.result_id()}}); + } + + // Applies |bit_instruction| to the extracted bits. + auto extracted_bit_instruction = opt::Instruction( + ir_context, bit_instruction->opcode(), bit_instruction->type_id(), + *fresh_id++, bit_extract_ids); + bit_instruction->InsertBefore( + MakeUnique(extracted_bit_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, + extracted_bit_instruction.result_id()); + extracted_bit_instructions[i] = extracted_bit_instruction.result_id(); + } + + // The first two ids in |extracted_bit_instructions| are used to insert the + // first two bits of the result. + uint32_t offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, false, false); + auto bit_insert = + opt::Instruction(ir_context, spv::Op::OpBitFieldInsert, + bit_instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}}, + {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_insert)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id()); + + // Inserts the remaining bits. + for (uint32_t i = 2; i < width; i++) { + offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {i}, 32, false, false); + bit_insert = opt::Instruction( + ir_context, spv::Op::OpBitFieldInsert, bit_instruction->type_id(), + *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}}, + {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_insert)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id()); + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We only add a synonym fact if the bit instruction is not irrelevant, and if + // the new result id we would make it synonymous with is not irrelevant. (It + // could be irrelevant if we are in a dead block.) + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + bit_instruction->result_id()) && + !transformation_context->GetFactManager()->IdIsIrrelevant( + bit_insert.result_id())) { + // Adds the fact that the last |bit_insert| instruction is synonymous of + // |bit_instruction|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(bit_insert.result_id(), {}), + MakeDataDescriptor(bit_instruction->result_id(), {})); + } +} + +std::unordered_set +TransformationAddBitInstructionSynonym::GetFreshIds() const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h new file mode 100644 index 00000000..40b947a2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h @@ -0,0 +1,151 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// clang-format off +// SPIR-V code to help understand the transformation. +// +// ---------------------------------------------------------------------------------------------------------------- +// | Reference shader | Variant shader | +// ---------------------------------------------------------------------------------------------------------------- +// | OpCapability Shader | OpCapability Shader | +// | OpCapability Int8 | OpCapability Int8 | +// | %1 = OpExtInstImport "GLSL.std.450" | %1 = OpExtInstImport "GLSL.std.450" | +// | OpMemoryModel Logical GLSL450 | OpMemoryModel Logical GLSL450 | +// | OpEntryPoint Vertex %7 "main" | OpEntryPoint Vertex %7 "main" | +// | | | +// | ; Types | ; Types | +// | %2 = OpTypeInt 8 0 | %2 = OpTypeInt 8 0 | +// | %3 = OpTypeVoid | %3 = OpTypeVoid | +// | %4 = OpTypeFunction %3 | %4 = OpTypeFunction %3 | +// | | | +// | ; Constants | ; Constants | +// | %5 = OpConstant %2 0 | %5 = OpConstant %2 0 | +// | %6 = OpConstant %2 1 | %6 = OpConstant %2 1 | +// | | %10 = OpConstant %2 2 | +// | ; main function | %11 = OpConstant %2 3 | +// | %7 = OpFunction %3 None %4 | %12 = OpConstant %2 4 | +// | %8 = OpLabel | %13 = OpConstant %2 5 | +// | %9 = OpBitwiseOr %2 %5 %6 ; bit instruction | %14 = OpConstant %2 6 | +// | OpReturn | %15 = OpConstant %2 7 | +// | OpFunctionEnd | | +// | | ; main function | +// | | %7 = OpFunction %3 None %4 | +// | | %8 = OpLabel | +// | | | +// | | %16 = OpBitFieldUExtract %2 %5 %5 %6 ; extracts bit 0 from %5 | +// | | %17 = OpBitFieldUExtract %2 %6 %5 %6 ; extracts bit 0 from %6 | +// | | %18 = OpBitwiseOr %2 %16 %17 | +// | | | +// | | %19 = OpBitFieldUExtract %2 %5 %6 %6 ; extracts bit 1 from %5 | +// | | %20 = OpBitFieldUExtract %2 %6 %6 %6 ; extracts bit 1 from %6 | +// | | %21 = OpBitwiseOr %2 %19 %20 | +// | | | +// | | %22 = OpBitFieldUExtract %2 %5 %10 %6 ; extracts bit 2 from %5 | +// | | %23 = OpBitFieldUExtract %2 %6 %10 %6 ; extracts bit 2 from %6 | +// | | %24 = OpBitwiseOr %2 %22 %23 | +// | | | +// | | %25 = OpBitFieldUExtract %2 %5 %11 %6 ; extracts bit 3 from %5 | +// | | %26 = OpBitFieldUExtract %2 %6 %11 %6 ; extracts bit 3 from %6 | +// | | %27 = OpBitwiseOr %2 %25 %26 | +// | | | +// | | %28 = OpBitFieldUExtract %2 %5 %12 %6 ; extracts bit 4 from %5 | +// | | %29 = OpBitFieldUExtract %2 %6 %12 %6 ; extracts bit 4 from %6 | +// | | %30 = OpBitwiseOr %2 %28 %29 | +// | | | +// | | %31 = OpBitFieldUExtract %2 %5 %13 %6 ; extracts bit 5 from %5 | +// | | %32 = OpBitFieldUExtract %2 %6 %13 %6 ; extracts bit 5 from %6 | +// | | %33 = OpBitwiseOr %2 %31 %32 | +// | | | +// | | %34 = OpBitFieldUExtract %2 %5 %14 %6 ; extracts bit 6 from %5 | +// | | %35 = OpBitFieldUExtract %2 %6 %14 %6 ; extracts bit 6 from %6 | +// | | %36 = OpBitwiseOr %2 %34 %35 | +// | | | +// | | %37 = OpBitFieldUExtract %2 %5 %15 %6 ; extracts bit 7 from %5 | +// | | %38 = OpBitFieldUExtract %2 %6 %15 %6 ; extracts bit 7 from %6 | +// | | %39 = OpBitwiseOr %2 %37 %38 | +// | | | +// | | %40 = OpBitFieldInsert %2 %18 %21 %6 %6 ; inserts bit 1 | +// | | %41 = OpBitFieldInsert %2 %40 %24 %10 %6 ; inserts bit 2 | +// | | %42 = OpBitFieldInsert %2 %41 %27 %11 %6 ; inserts bit 3 | +// | | %43 = OpBitFieldInsert %2 %42 %30 %12 %6 ; inserts bit 4 | +// | | %44 = OpBitFieldInsert %2 %43 %33 %13 %6 ; inserts bit 5 | +// | | %45 = OpBitFieldInsert %2 %44 %36 %14 %6 ; inserts bit 6 | +// | | %46 = OpBitFieldInsert %2 %45 %39 %15 %6 ; inserts bit 7 | +// | | %9 = OpBitwiseOr %2 %5 %6 ; bit instruction | +// | | OpReturn | +// | | OpFunctionEnd | +// ---------------------------------------------------------------------------------------------------------------- +// +// After the transformation, %9 and %46 will be synonymous. +// clang-format on +class TransformationAddBitInstructionSynonym : public Transformation { + public: + explicit TransformationAddBitInstructionSynonym( + protobufs::TransformationAddBitInstructionSynonym message); + + TransformationAddBitInstructionSynonym( + const uint32_t instruction_result_id, + const std::vector& fresh_ids); + + // - |message_.instruction_result_id| must be a bit instruction. + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a bit instruction synonym. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of fresh ids required to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* bit_instruction); + + // Returns true if: + // - A |bit_instruction| is one of OpBitwiseOr, OpBitwiseAnd, OpBitwiseXor or + // OpNot. + // - |bit_instruction|'s operands are scalars. + // - The operands have the same signedness. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationAddBitInstructionSynonym message_; + + // Adds OpBitwise* or OpNot synonym. + void AddOpBitwiseOrOpNotSynonym(opt::IRContext* ir_context, + TransformationContext* transformation_context, + opt::Instruction* bitwise_instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp new file mode 100644 index 00000000..89c2e579 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_boolean.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantBoolean::TransformationAddConstantBoolean( + protobufs::TransformationAddConstantBoolean message) + : message_(std::move(message)) {} + +TransformationAddConstantBoolean::TransformationAddConstantBoolean( + uint32_t fresh_id, bool is_true, bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_is_true(is_true); + message_.set_is_irrelevant(is_irrelevant); +} + +bool TransformationAddConstantBoolean::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + return fuzzerutil::MaybeGetBoolType(ir_context) != 0 && + fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); +} + +void TransformationAddConstantBoolean::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add the boolean constant to the module, ensuring the module's id bound is + // high enough. + auto new_instruction = MakeUnique( + ir_context, + message_.is_true() ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, + fuzzerutil::MaybeGetBoolType(ir_context), message_.fresh_id(), + opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_boolean() = message_; + return result; +} + +std::unordered_set TransformationAddConstantBoolean::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.h new file mode 100644 index 00000000..7f31471c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_boolean.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantBoolean : public Transformation { + public: + explicit TransformationAddConstantBoolean( + protobufs::TransformationAddConstantBoolean message); + + TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true, + bool is_irrelevant); + + // - |message_.fresh_id| must not be used by the module. + // - The module must already contain OpTypeBool. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds OpConstantTrue (OpConstantFalse) to the module with id + // |message_.fresh_id| if |message_.is_true| holds (does not hold). + // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| + // is true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantBoolean message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp new file mode 100644 index 00000000..b8ee8be7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_composite.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantComposite::TransformationAddConstantComposite( + spvtools::fuzz::protobufs::TransformationAddConstantComposite message) + : message_(std::move(message)) {} + +TransformationAddConstantComposite::TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector& constituent_ids, bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); + for (auto constituent_id : constituent_ids) { + message_.add_constituent_id(constituent_id); + } +} + +bool TransformationAddConstantComposite::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that the given id is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // Check that the composite type id is an instruction id. + auto composite_type_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!composite_type_instruction) { + return false; + } + // Gather up the operands for the composite constant, in the process checking + // whether the given type really defines a composite and - in the case of a + // struct - whether its decorations are OK. + std::vector constituent_type_ids; + switch (composite_type_instruction->opcode()) { + case spv::Op::OpTypeArray: + for (uint32_t index = 0; + index < + fuzzerutil::GetArraySize(*composite_type_instruction, ir_context); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + for (uint32_t index = 0; + index < composite_type_instruction->GetSingleWordInOperand(1); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case spv::Op::OpTypeStruct: + // We do not create constants of structs decorated with Block nor + // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it + // seems like a strange thing to do, so we disallow it to avoid triggering + // low priority edge case issues related to it. + if (fuzzerutil::HasBlockOrBufferBlockDecoration( + ir_context, composite_type_instruction->result_id())) { + return false; + } + composite_type_instruction->ForEachInOperand( + [&constituent_type_ids](const uint32_t* member_type_id) { + constituent_type_ids.push_back(*member_type_id); + }); + break; + default: + // Not a composite type. + return false; + } + + // Check that the number of provided operands matches the number of + // constituents required by the type. + if (constituent_type_ids.size() != + static_cast(message_.constituent_id().size())) { + return false; + } + + // Check that every provided operand refers to an instruction of the + // corresponding constituent type. + for (uint32_t index = 0; index < constituent_type_ids.size(); index++) { + auto constituent_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index)); + if (!constituent_instruction) { + return false; + } + if (constituent_instruction->type_id() != constituent_type_ids.at(index)) { + return false; + } + } + return true; +} + +void TransformationAddConstantComposite::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction::OperandList in_operands; + for (auto constituent_id : message_.constituent_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); + } + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpConstantComposite, message_.type_id(), + message_.fresh_id(), in_operands); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager of the new instruction. Invalidate the constant + // manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantComposite::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_constant_composite() = message_; + return result; +} + +std::unordered_set TransformationAddConstantComposite::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.h new file mode 100644 index 00000000..94e7a927 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_composite.h @@ -0,0 +1,66 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantComposite : public Transformation { + public: + explicit TransformationAddConstantComposite( + protobufs::TransformationAddConstantComposite message); + + TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector& constituent_ids, bool is_irrelevant); + + // - |message_.fresh_id| must be a fresh id + // - |message_.type_id| must be the id of a composite type + // - |message_.constituent_id| must refer to ids that match the constituent + // types of this composite type + // - If |message_.type_id| is a struct type, it must not have the Block or + // BufferBlock decoration + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds an OpConstantComposite instruction defining a constant of type + // |message_.type_id|, using |message_.constituent_id| as constituents, with + // result id |message_.fresh_id|. + // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is + // true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantComposite message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.cpp new file mode 100644 index 00000000..7b83baea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_null.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantNull::TransformationAddConstantNull( + spvtools::fuzz::protobufs::TransformationAddConstantNull message) + : message_(std::move(message)) {} + +TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddConstantNull::IsApplicable( + opt::IRContext* context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto type = context->get_def_use_mgr()->GetDef(message_.type_id()); + // The type must exist. + if (!type) { + return false; + } + // The type must be one of the types for which null constants are allowed, + // according to the SPIR-V spec. + return fuzzerutil::IsNullConstantSupported(context, *type); +} + +void TransformationAddConstantNull::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpConstantNull, message_.type_id(), + message_.fresh_id(), opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); +} + +protobufs::Transformation TransformationAddConstantNull::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_null() = message_; + return result; +} + +std::unordered_set TransformationAddConstantNull::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.h new file mode 100644 index 00000000..bb1d1b7c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_null.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantNull : public Transformation { + public: + explicit TransformationAddConstantNull( + protobufs::TransformationAddConstantNull message); + + TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a type for which it is acceptable + // to create a null constant + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpConstantNull instruction to the module, with |message_.type_id| + // as its type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantNull message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp new file mode 100644 index 00000000..45989d45 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_scalar.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantScalar::TransformationAddConstantScalar( + spvtools::fuzz::protobufs::TransformationAddConstantScalar message) + : message_(std::move(message)) {} + +TransformationAddConstantScalar::TransformationAddConstantScalar( + uint32_t fresh_id, uint32_t type_id, const std::vector& words, + bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); + for (auto word : words) { + message_.add_word(word); + } +} + +bool TransformationAddConstantScalar::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id needs to be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The type id for the scalar must exist and be a type. + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); + if (!type) { + return false; + } + uint32_t width; + if (type->AsFloat()) { + width = type->AsFloat()->width(); + } else if (type->AsInteger()) { + width = type->AsInteger()->width(); + } else { + return false; + } + // The number of words is the integer floor of the width. + auto words = (width + 32 - 1) / 32; + + // The number of words provided by the transformation needs to match the + // width of the type. + return static_cast(message_.word().size()) == words; +} + +void TransformationAddConstantScalar::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpConstant, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_LITERAL_INTEGER, + std::vector(message_.word().begin(), + message_.word().end())}})); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantScalar::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_scalar() = message_; + return result; +} + +std::unordered_set TransformationAddConstantScalar::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.h new file mode 100644 index 00000000..adb07355 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_constant_scalar.h @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantScalar : public Transformation { + public: + explicit TransformationAddConstantScalar( + protobufs::TransformationAddConstantScalar message); + + TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id, + const std::vector& words, + bool is_irrelevant); + + // - |message_.fresh_id| must not be used by the module + // - |message_.type_id| must be the id of a floating-point or integer type + // - The size of |message_.word| must be compatible with the width of this + // type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new OpConstant instruction with the given type and words. + // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantScalar message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp new file mode 100644 index 00000000..9d1f3256 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp @@ -0,0 +1,219 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_copy_memory.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddCopyMemory::TransformationAddCopyMemory( + protobufs::TransformationAddCopyMemory message) + : message_(std::move(message)) {} + +TransformationAddCopyMemory::TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, spv::StorageClass storage_class, + uint32_t initializer_id) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.set_fresh_id(fresh_id); + message_.set_source_id(source_id); + message_.set_storage_class(uint32_t(storage_class)); + message_.set_initializer_id(initializer_id); +} + +bool TransformationAddCopyMemory::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that target id is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Check that instruction descriptor is valid. This also checks that + // |message_.instruction_descriptor| is not a global instruction. + auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context); + if (!inst) { + return false; + } + + // Check that we can insert OpCopyMemory before |instruction_descriptor|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpCopyMemory, + iter)) { + return false; + } + + // Check that source instruction exists and is valid. + auto* source_inst = + ir_context->get_def_use_mgr()->GetDef(message_.source_id()); + if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) { + return false; + } + + // |storage_class| is either Function or Private. + if (spv::StorageClass(message_.storage_class()) != + spv::StorageClass::Function && + spv::StorageClass(message_.storage_class()) != + spv::StorageClass::Private) { + return false; + } + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source_inst->type_id()); + + // OpTypePointer with |message_.storage_class| exists. + if (!fuzzerutil::MaybeGetPointerType( + ir_context, pointee_type_id, + static_cast(message_.storage_class()))) { + return false; + } + + // Check that |initializer_id| exists and has valid type. + const auto* initializer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) { + return false; + } + + // Check that domination rules are satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst, + message_.source_id()); +} + +void TransformationAddCopyMemory::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Insert OpCopyMemory before |instruction_descriptor|. + auto* insert_before_inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + assert(insert_before_inst); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); + + // Add global or local variable to copy memory into. + auto storage_class = static_cast(message_.storage_class()); + auto type_id = fuzzerutil::MaybeGetPointerType( + ir_context, + fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())), + storage_class); + + if (storage_class == spv::StorageClass::Private) { + opt::Instruction* new_global = + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id, + storage_class, message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global); + } else { + assert(storage_class == spv::StorageClass::Function && + "Storage class can be either Private or Function"); + opt::Function* enclosing_function = enclosing_block->GetParent(); + opt::Instruction* new_local = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_id(), type_id, + enclosing_function->result_id(), message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local); + ir_context->set_instr_block(new_local, &*enclosing_function->entry()); + } + + auto insert_before_iter = fuzzerutil::GetIteratorForInstruction( + enclosing_block, insert_before_inst); + + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpCopyMemory, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}); + auto new_instruction_ptr = new_instruction.get(); + insert_before_iter.InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, enclosing_block); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Even though the copy memory instruction will - at least temporarily - lead + // to the destination and source pointers referring to identical values, this + // fact is not guaranteed to hold throughout execution of the SPIR-V code + // since the source pointer could be over-written. We thus assume nothing + // about the destination pointer, and record this fact so that the destination + // pointer can be used freely by other fuzzer passes. + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); +} + +protobufs::Transformation TransformationAddCopyMemory::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_copy_memory() = message_; + return result; +} + +bool TransformationAddCopyMemory::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->result_id() || !inst->type_id() || + inst->opcode() == spv::Op::OpConstantNull || + inst->opcode() == spv::Op::OpUndef) { + return false; + } + + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction must have a valid type"); + + if (!type->AsPointer()) { + return false; + } + + // We do not support copying memory from a pointer to a block-/buffer + // block-decorated struct. + auto pointee_type_inst = ir_context->get_def_use_mgr() + ->GetDef(inst->type_id()) + ->GetSingleWordInOperand(1); + if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, + pointee_type_inst)) { + return false; + } + + return CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type()); +} + +bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kBool: + case opt::analysis::Type::kInteger: + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kArray: + return true; + case opt::analysis::Type::kVector: + return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type()); + case opt::analysis::Type::kMatrix: + return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type()); + case opt::analysis::Type::kStruct: + return std::all_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element) { + return CanUsePointeeWithCopyMemory(*element); + }); + default: + return false; + } +} + +std::unordered_set TransformationAddCopyMemory::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.h new file mode 100644 index 00000000..053b6291 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_copy_memory.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddCopyMemory : public Transformation { + public: + explicit TransformationAddCopyMemory( + protobufs::TransformationAddCopyMemory message); + + TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, spv::StorageClass storage_class, + uint32_t initializer_id); + + // - |instruction_descriptor| must point to a valid instruction in the module. + // - it should be possible to insert OpCopyMemory before + // |instruction_descriptor| (i.e. the module remains valid after the + // insertion). + // - |source_id| must be a result id for some valid instruction in the module. + // - |fresh_id| must be a fresh id to copy memory into. + // - type of |source_id| must be OpTypePointer where pointee can be used with + // OpCopyMemory. + // - If the pointee type of |source_id| is a struct type, it must not have the + // Block or BufferBlock decoration. + // - |storage_class| must be either Private or Function. + // - type ids of instructions with result ids |source_id| and |initialize_id| + // must be the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A global or local variable with id |target_id| and |storage_class| class is + // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted + // before the |instruction_descriptor|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can copy memory from |instruction| using OpCopyMemory. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* inst); + + private: + // Returns whether the type, pointed to by some OpTypePointer, can be used + // with OpCopyMemory instruction. + static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type); + + protobufs::TransformationAddCopyMemory message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.cpp new file mode 100644 index 00000000..930adc10 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_block.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadBlock::TransformationAddDeadBlock( + protobufs::TransformationAddDeadBlock message) + : message_(std::move(message)) {} + +TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id, + uint32_t existing_block, + bool condition_value) { + message_.set_fresh_id(fresh_id); + message_.set_existing_block(existing_block); + message_.set_condition_value(condition_value); +} + +bool TransformationAddDeadBlock::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The new block's id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // First, we check that a constant with the same value as + // |message_.condition_value| is present. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.condition_value(), false)) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // The existing block must indeed exist. + auto existing_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block()); + if (!existing_block) { + return false; + } + + // It must not head a loop. + if (existing_block->IsLoopHeader()) { + return false; + } + + // It must end with OpBranch. + if (existing_block->terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + + // Its successor must not be a merge block nor continue target. + auto successor_block_id = + existing_block->terminator()->GetSingleWordInOperand(0); + if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) { + return false; + } + + // The successor must not be a loop header (i.e., |message_.existing_block| + // must not be a back-edge block. + if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) { + return false; + } + + // |existing_block| must be reachable. + if (!ir_context->IsReachable(*existing_block)) { + return false; + } + + assert(existing_block->id() != successor_block_id && + "|existing_block| must be different from |successor_block_id|"); + + // Even though we know |successor_block_id| is not a merge block, it might + // still have multiple predecessors because divergent control flow is allowed + // to converge early (before the merge block). In this case, when we create + // the selection construct, its header |existing_block| will not dominate the + // merge block |successor_block_id|, which is invalid. Thus, |existing_block| + // must dominate |successor_block_id|. + opt::DominatorAnalysis* dominator_analysis = + ir_context->GetDominatorAnalysis(existing_block->GetParent()); + if (!dominator_analysis->Dominates(existing_block->id(), + successor_block_id)) { + return false; + } + + return true; +} + +void TransformationAddDeadBlock::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Update the module id bound so that it is at least the id of the new block. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Get the existing block and its successor. + auto existing_block = ir_context->cfg()->block(message_.existing_block()); + auto successor_block_id = + existing_block->terminator()->GetSingleWordInOperand(0); + + // Get the id of the boolean value that will be used as the branch condition. + auto bool_id = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, message_.condition_value(), false); + + // Make a new block that unconditionally branches to the original successor + // block. + auto enclosing_function = existing_block->GetParent(); + std::unique_ptr new_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.fresh_id(), + opt::Instruction::OperandList())); + new_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {successor_block_id}}}))); + + // Turn the original block into a selection merge, with its original successor + // as the merge block. + existing_block->terminator()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpSelectionMerge, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {successor_block_id}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}}))); + + // Change the original block's terminator to be a conditional branch on the + // given boolean, with the original successor and the new successor as branch + // targets, and such that at runtime control will always transfer to the + // original successor. + existing_block->terminator()->SetOpcode(spv::Op::OpBranchConditional); + existing_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {bool_id}}, + {SPV_OPERAND_TYPE_ID, + {message_.condition_value() ? successor_block_id + : message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, + {message_.condition_value() ? message_.fresh_id() + : successor_block_id}}}); + + // Add the new block to the enclosing function. + enclosing_function->InsertBasicBlockAfter(std::move(new_block), + existing_block); + + // Fix up OpPhi instructions in the successor block, so that the values they + // yield when control has transferred from the new block are the same as if + // control had transferred from |message_.existing_block|. This is guaranteed + // to be valid since |message_.existing_block| dominates the new block by + // construction. Other transformations can change these phi operands to more + // interesting values. + ir_context->cfg() + ->block(successor_block_id) + ->ForEachPhiInst([this](opt::Instruction* phi_inst) { + // Copy the operand that provides the phi value for the first of any + // existing predecessors. + opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0); + // Use this as the value associated with the new predecessor. + phi_inst->AddOperand(std::move(copy_of_existing_operand)); + phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}); + }); + + // Do not rely on any existing analysis results since the control flow graph + // of the module has changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Record the fact that the new block is dead. + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); +} + +protobufs::Transformation TransformationAddDeadBlock::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_block() = message_; + return result; +} + +std::unordered_set TransformationAddDeadBlock::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.h new file mode 100644 index 00000000..d8b3c2ae --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_block.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadBlock : public Transformation { + public: + explicit TransformationAddDeadBlock( + protobufs::TransformationAddDeadBlock message); + + TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block, + bool condition_value); + + // - |message_.fresh_id| must be a fresh id + // - A constant with the same value as |message_.condition_value| must be + // available + // - |message_.existing_block| must be a block that is not a loop header, + // and that ends with OpBranch to a block that is not a merge block nor + // continue target - this is because the successor will become the merge + // block of a selection construct headed at |message_.existing_block| + // - |message_.existing_block| must not be a back-edge block, since in this + // case the newly-added block would lead to another back-edge to the + // associated loop header + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the OpBranch from |message_.existing_block| to its successor 's' + // to an OpBranchConditional to either 's' or a new block, + // |message_.fresh_id|, which itself unconditionally branches to 's'. The + // conditional branch uses |message.condition_value| as its condition, and is + // arranged so that control will pass to 's' at runtime. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddDeadBlock message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.cpp new file mode 100644 index 00000000..07ed4dc5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_break.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" +#include "source/opt/struct_cfg_analysis.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadBreak::TransformationAddDeadBreak( + protobufs::TransformationAddDeadBreak message) + : message_(std::move(message)) {} + +TransformationAddDeadBreak::TransformationAddDeadBreak( + uint32_t from_block, uint32_t to_block, bool break_condition_value, + std::vector phi_id) { + message_.set_from_block(from_block); + message_.set_to_block(to_block); + message_.set_break_condition_value(break_condition_value); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( + opt::IRContext* ir_context, opt::BasicBlock* bb_from) const { + // Look at the structured control flow associated with |from_block| and + // check whether it is contained in an appropriate construct with merge id + // |to_block| such that a break from |from_block| to |to_block| is legal. + + // There are three legal cases to consider: + // (1) |from_block| is a loop header and |to_block| is its merge + // (2) |from_block| is a non-header node of a construct, and |to_block| + // is the merge for that construct + // (3) |from_block| is a non-header node of a selection construct, and + // |to_block| is the merge for the innermost loop containing + // |from_block| + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2653) It may be + // possible to be more aggressive in breaking from switch constructs. + // + // The reason we need to distinguish between cases (1) and (2) is that the + // structured CFG analysis does not deem a header to be part of the construct + // that it heads. + + // Consider case (1) + if (bb_from->IsLoopHeader()) { + // Case (1) holds if |to_block| is the merge block for the loop; + // otherwise no case holds + return bb_from->MergeBlockId() == message_.to_block(); + } + + // Both cases (2) and (3) require that |from_block| is inside some + // structured control flow construct. + + auto containing_construct = + ir_context->GetStructuredCFGAnalysis()->ContainingConstruct( + message_.from_block()); + if (!containing_construct) { + // |from_block| is not in a construct from which we can break. + return false; + } + + // Consider case (2) + if (message_.to_block() == + ir_context->cfg()->block(containing_construct)->MergeBlockId()) { + // This looks like an instance of case (2). + // However, the structured CFG analysis regards the continue construct of a + // loop as part of the loop, but it is not legal to jump from a loop's + // continue construct to the loop's merge (except from the back-edge block), + // so we need to check for this case. + return !fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), containing_construct) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_construct); + } + + // Case (3) holds if and only if |to_block| is the merge block for this + // innermost loop that contains |from_block| + auto containing_loop_header = + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + message_.from_block()); + if (containing_loop_header && + message_.to_block() == + ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) { + return !fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), containing_loop_header) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_loop_header); + } + return false; +} + +bool TransformationAddDeadBreak::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // First, we check that a constant with the same value as + // |message_.break_condition_value| is present. + const auto bool_id = + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), false); + if (!bool_id) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // Check that |message_.from_block| and |message_.to_block| really are block + // ids + opt::BasicBlock* bb_from = + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); + if (bb_from == nullptr) { + return false; + } + opt::BasicBlock* bb_to = + fuzzerutil::MaybeFindBlock(ir_context, message_.to_block()); + if (bb_to == nullptr) { + return false; + } + + if (!ir_context->IsReachable(*bb_to)) { + // If the target of the break is unreachable, we conservatively do not + // allow adding a dead break, to avoid the compilations that arise due to + // the lack of sensible dominance information for unreachable blocks. + return false; + } + + // Check that |message_.from_block| ends with an unconditional branch. + if (bb_from->terminator()->opcode() != spv::Op::OpBranch) { + // The block associated with the id does not end with an unconditional + // branch. + return false; + } + + assert(bb_from != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_from->id() == message_.from_block() && + "The id of the block we found should match the source id for the break."); + assert(bb_to != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_to->id() == message_.to_block() && + "The id of the block we found should match the target id for the break."); + + // Check whether the data passed to extend OpPhi instructions is appropriate. + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, bb_to, + message_.phi_id())) { + return false; + } + + // Check that adding the break would respect the rules of structured + // control flow. + if (!AddingBreakRespectsStructuredControlFlow(ir_context, bb_from)) { + return false; + } + + // Adding the dead break is only valid if SPIR-V rules related to dominance + // hold. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, message_.from_block(), + fuzzerutil::CreateUnreachableEdgeInstruction( + ir_context, message_.from_block(), message_.to_block(), bool_id)); +} + +void TransformationAddDeadBreak::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + ir_context, ir_context->cfg()->block(message_.from_block()), + ir_context->cfg()->block(message_.to_block()), + fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, + message_.break_condition_value(), false), + message_.phi_id()); + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_break() = message_; + return result; +} + +std::unordered_set TransformationAddDeadBreak::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.h new file mode 100644 index 00000000..8c1ab4a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_break.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadBreak : public Transformation { + public: + explicit TransformationAddDeadBreak( + protobufs::TransformationAddDeadBreak message); + + TransformationAddDeadBreak(uint32_t from_block, uint32_t to_block, + bool break_condition_value, + std::vector phi_id); + + // - |message_.from_block| must be the id of a block a in the given module. + // - |message_.to_block| must be the id of a block b in the given module. + // - if |message_.break_condition_value| holds (does not hold) then + // OpConstantTrue (OpConstantFalse) must be present in the module + // - |message_.phi_ids| must be a list of ids that are all available at + // |message_.from_block| + // - a and b must be in the same function. + // - b must be a merge block. + // - a must end with an unconditional branch to some block c. + // - replacing this branch with a conditional branch to b or c, with + // the boolean constant associated with |message_.break_condition_value| as + // the condition, and the ids in |message_.phi_ids| used to extend + // any OpPhi instructions at b as a result of the edge from a, must + // maintain validity of the module. + // In particular, the new branch must not lead to violations of the rule + // that a use must be dominated by its definition. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the terminator of a with a conditional branch to b or c. + // The boolean constant associated with |message_.break_condition_value| is + // used as the condition, and the order of b and c is arranged such that + // control is guaranteed to jump to c. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns true if and only if adding an edge from |bb_from| to + // |message_.to_block| respects structured control flow. + bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context, + opt::BasicBlock* bb_from) const; + + protobufs::TransformationAddDeadBreak message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp new file mode 100644 index 00000000..c534801b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_continue.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadContinue::TransformationAddDeadContinue( + protobufs::TransformationAddDeadContinue message) + : message_(std::move(message)) {} + +TransformationAddDeadContinue::TransformationAddDeadContinue( + uint32_t from_block, bool continue_condition_value, + std::vector phi_id) { + message_.set_from_block(from_block); + message_.set_continue_condition_value(continue_condition_value); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddDeadContinue::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // First, we check that a constant with the same value as + // |message_.continue_condition_value| is present. + const auto bool_id = fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, message_.continue_condition_value(), + false); + if (!bool_id) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // Check that |message_.from_block| really is a block id. + opt::BasicBlock* bb_from = + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); + if (bb_from == nullptr) { + return false; + } + + // Check that |message_.from_block| ends with an unconditional branch. + if (bb_from->terminator()->opcode() != spv::Op::OpBranch) { + // The block associated with the id does not end with an unconditional + // branch. + return false; + } + + assert(bb_from != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_from->id() == message_.from_block() && + "The id of the block we found should match the source id for the break."); + + // Get the header for the innermost loop containing |message_.from_block|. + // Because the structured CFG analysis does not regard a loop header as part + // of the loop it heads, we check first whether bb_from is a loop header + // before using the structured CFG analysis. + auto loop_header = + bb_from->IsLoopHeader() + ? message_.from_block() + : ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + message_.from_block()); + if (!loop_header) { + return false; + } + + auto continue_block = + ir_context->cfg()->block(loop_header)->ContinueBlockId(); + + if (!ir_context->IsReachable(*ir_context->cfg()->block(continue_block))) { + // If the loop's continue block is unreachable, we conservatively do not + // allow adding a dead continue, to avoid the compilations that arise due to + // the lack of sensible dominance information for unreachable blocks. + return false; + } + + if (fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), loop_header)) { + // We cannot jump to the continue target from the continue construct. + return false; + } + + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) { + // A branch straight to the continue target that is also a merge block might + // break the property that a construct header must dominate its merge block + // (if the merge block is reachable). + return false; + } + + // Check whether the data passed to extend OpPhi instructions is appropriate. + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, + ir_context->cfg()->block(continue_block), + message_.phi_id())) { + return false; + } + + // Adding the dead break is only valid if SPIR-V rules related to dominance + // hold. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, message_.from_block(), + fuzzerutil::CreateUnreachableEdgeInstruction( + ir_context, message_.from_block(), continue_block, bool_id)); +} + +void TransformationAddDeadContinue::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto bb_from = ir_context->cfg()->block(message_.from_block()); + auto continue_block = + bb_from->IsLoopHeader() + ? bb_from->ContinueBlockId() + : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock( + message_.from_block()); + assert(continue_block && "message_.from_block must be in a loop."); + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + ir_context, bb_from, ir_context->cfg()->block(continue_block), + fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, + message_.continue_condition_value(), + false), + message_.phi_id()); + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_continue() = message_; + return result; +} + +std::unordered_set TransformationAddDeadContinue::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.h new file mode 100644 index 00000000..9463aebc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_dead_continue.h @@ -0,0 +1,77 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadContinue : public Transformation { + public: + explicit TransformationAddDeadContinue( + protobufs::TransformationAddDeadContinue message); + + TransformationAddDeadContinue(uint32_t from_block, + bool continue_condition_value, + std::vector phi_id); + + // - |message_.from_block| must be the id of a block a in the given module. + // - a must be contained in a loop with continue target b + // - The continue target b must be dominated by the head of the loop in which + // it is contained + // - b must not be the merge block of a selection construct + // - if |message_.continue_condition_value| holds (does not hold) then + // OpConstantTrue (OpConstantFalse) must be present in the module + // - |message_.phi_ids| must be a list of ids that are all available at + // |message_.from_block| + // - a must end with an unconditional branch to some block c. + // - replacing this branch with a conditional branch to b or c, with + // the boolean constant associated with |message_.continue_condition_value| + // as the condition, and the ids in |message_.phi_ids| used to extend any + // OpPhi instructions at b as a result of the edge from a, must maintain + // validity of the module. + // In particular, adding an edge from somewhere in the loop to the continue + // target must not prevent uses of ids in the continue target from being + // dominated by the definitions of those ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the terminator of a with a conditional branch to b or c. + // The boolean constant associated with |message_.continue_condition_value| is + // used as the condition, and the order of b and c is arranged such that + // control is guaranteed to jump to c. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddDeadContinue message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp new file mode 100644 index 00000000..28e0186d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddEarlyTerminatorWrapper:: + TransformationAddEarlyTerminatorWrapper( + protobufs::TransformationAddEarlyTerminatorWrapper message) + : message_(std::move(message)) {} + +TransformationAddEarlyTerminatorWrapper:: + TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, + uint32_t label_fresh_id, + spv::Op opcode) { + message_.set_function_fresh_id(function_fresh_id); + message_.set_label_fresh_id(label_fresh_id); + message_.set_opcode(uint32_t(opcode)); +} + +bool TransformationAddEarlyTerminatorWrapper::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + assert((spv::Op(message_.opcode()) == spv::Op::OpKill || + spv::Op(message_.opcode()) == spv::Op::OpUnreachable || + spv::Op(message_.opcode()) == spv::Op::OpTerminateInvocation) && + "Invalid opcode."); + + if (!fuzzerutil::IsFreshId(ir_context, message_.function_fresh_id())) { + return false; + } + if (!fuzzerutil::IsFreshId(ir_context, message_.label_fresh_id())) { + return false; + } + if (message_.function_fresh_id() == message_.label_fresh_id()) { + return false; + } + uint32_t void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + if (!void_type_id) { + return false; + } + return fuzzerutil::FindFunctionType(ir_context, {void_type_id}); +} + +void TransformationAddEarlyTerminatorWrapper::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.function_fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.label_fresh_id()); + + // Create a basic block of the form: + // %label_fresh_id = OpLabel + // OpKill|Unreachable|TerminateInvocation + auto basic_block = MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.label_fresh_id(), + opt::Instruction::OperandList())); + basic_block->AddInstruction(MakeUnique( + ir_context, static_cast(message_.opcode()), 0, 0, + opt::Instruction::OperandList())); + + // Create a zero-argument void function. + auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + auto function = MakeUnique(MakeUnique( + ir_context, spv::Op::OpFunction, void_type_id, + message_.function_fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {uint32_t(spv::FunctionControlMask::MaskNone)}}, + {SPV_OPERAND_TYPE_TYPE_ID, + {fuzzerutil::FindFunctionType(ir_context, {void_type_id})}}}))); + + // Add the basic block to the function as the sole block, and add the function + // to the module. + function->AddBasicBlock(std::move(basic_block)); + function->SetFunctionEnd( + MakeUnique(ir_context, spv::Op::OpFunctionEnd, 0, 0, + opt::Instruction::OperandList())); + ir_context->module()->AddFunction(std::move(function)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationAddEarlyTerminatorWrapper::GetFreshIds() const { + return std::unordered_set( + {message_.function_fresh_id(), message_.label_fresh_id()}); +} + +protobufs::Transformation TransformationAddEarlyTerminatorWrapper::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_early_terminator_wrapper() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h new file mode 100644 index 00000000..5d0201dd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ +#define SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddEarlyTerminatorWrapper : public Transformation { + public: + explicit TransformationAddEarlyTerminatorWrapper( + protobufs::TransformationAddEarlyTerminatorWrapper message); + + TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, + uint32_t label_fresh_id, + spv::Op opcode); + + // - |message_.function_fresh_id| and |message_.label_fresh_id| must be fresh + // and distinct. + // - OpTypeVoid must be declared in the module. + // - The module must contain a type for a zero-argument void function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a function to the module of the form: + // + // |message_.function_fresh_id| = OpFunction %void None %zero_args_return_void + // |message_.label_fresh_id| = OpLabel + // |message_.opcode| + // OpFunctionEnd + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddEarlyTerminatorWrapper message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.cpp new file mode 100644 index 00000000..1f61ede7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.cpp @@ -0,0 +1,966 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_message.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddFunction::TransformationAddFunction( + protobufs::TransformationAddFunction message) + : message_(std::move(message)) {} + +TransformationAddFunction::TransformationAddFunction( + const std::vector& instructions) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } + message_.set_is_livesafe(false); +} + +TransformationAddFunction::TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } + message_.set_is_livesafe(true); + message_.set_loop_limiter_variable_id(loop_limiter_variable_id); + message_.set_loop_limit_constant_id(loop_limit_constant_id); + for (auto& loop_limiter : loop_limiters) { + *message_.add_loop_limiter_info() = loop_limiter; + } + message_.set_kill_unreachable_return_value_id( + kill_unreachable_return_value_id); + for (auto& access_clamper : access_chain_clampers) { + *message_.add_access_chain_clamping_info() = access_clamper; + } +} + +bool TransformationAddFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // This transformation may use a lot of ids, all of which need to be fresh + // and distinct. This set tracks them. + std::set ids_used_by_this_transformation; + + // Ensure that all result ids in the new function are fresh and distinct. + for (auto& instruction : message_.instruction()) { + if (instruction.result_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + instruction.result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + } + } + + if (message_.is_livesafe()) { + // Ensure that all ids provided for making the function livesafe are fresh + // and distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.loop_limiter_variable_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + for (auto& loop_limiter_info : message_.loop_limiter_info()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.load_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.increment_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.compare_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.logical_op_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + } + for (auto& access_chain_clamping_info : + message_.access_chain_clamping_info()) { + for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.first(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + } + } + + // Because checking all the conditions for a function to be valid is a big + // job that the SPIR-V validator can already do, a "try it and see" approach + // is taken here. + + // We first clone the current module, so that we can try adding the new + // function without risking wrecking |ir_context|. + auto cloned_module = fuzzerutil::CloneIRContext(ir_context); + + // We try to add a function to the cloned module, which may fail if + // |message_.instruction| is not sufficiently well-formed. + if (!TryToAddFunction(cloned_module.get())) { + return false; + } + + // Check whether the cloned module is still valid after adding the function. + // If it is not, the transformation is not applicable. + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer)) { + return false; + } + + if (message_.is_livesafe()) { + if (!TryToMakeFunctionLivesafe(cloned_module.get(), + transformation_context)) { + return false; + } + // After making the function livesafe, we check validity of the module + // again. This is because the turning of OpKill, OpUnreachable and OpReturn + // instructions into branches changes control flow graph reachability, which + // has the potential to make the module invalid when it was otherwise valid. + // It is simpler to rely on the validator to guard against this than to + // consider all scenarios when making a function livesafe. + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer)) { + return false; + } + } + return true; +} + +void TransformationAddFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add the function to the module. As the transformation is applicable, this + // should succeed. + bool success = TryToAddFunction(ir_context); + assert(success && "The function should be successfully added."); + (void)(success); // Keep release builds happy (otherwise they may complain + // that |success| is not used). + + if (message_.is_livesafe()) { + // Make the function livesafe, which also should succeed. + success = TryToMakeFunctionLivesafe(ir_context, *transformation_context); + assert(success && "It should be possible to make the function livesafe."); + (void)(success); // Keep release builds happy. + } + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + assert(spv::Op(message_.instruction(0).opcode()) == spv::Op::OpFunction && + "The first instruction of an 'add function' transformation must be " + "OpFunction."); + + if (message_.is_livesafe()) { + // Inform the fact manager that the function is livesafe. + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( + message_.instruction(0).result_id()); + } else { + // Inform the fact manager that all blocks in the function are dead. + for (auto& inst : message_.instruction()) { + if (spv::Op(inst.opcode()) == spv::Op::OpLabel) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + inst.result_id()); + } + } + } + + // Record the fact that all pointer parameters and variables declared in the + // function should be regarded as having irrelevant values. This allows other + // passes to store arbitrarily to such variables, and to pass them freely as + // parameters to other functions knowing that it is OK if they get + // over-written. + for (auto& instruction : message_.instruction()) { + switch (spv::Op(instruction.opcode())) { + case spv::Op::OpFunctionParameter: + if (ir_context->get_def_use_mgr() + ->GetDef(instruction.result_type_id()) + ->opcode() == spv::Op::OpTypePointer) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); + } + break; + case spv::Op::OpVariable: + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); + break; + default: + break; + } + } +} + +protobufs::Transformation TransformationAddFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_function() = message_; + return result; +} + +bool TransformationAddFunction::TryToAddFunction( + opt::IRContext* ir_context) const { + // This function returns false if |message_.instruction| was not well-formed + // enough to actually create a function and add it to |ir_context|. + + // A function must have at least some instructions. + if (message_.instruction().empty()) { + return false; + } + + // A function must start with OpFunction. + auto function_begin = message_.instruction(0); + if (spv::Op(function_begin.opcode()) != spv::Op::OpFunction) { + return false; + } + + // Make a function, headed by the OpFunction instruction. + std::unique_ptr new_function = MakeUnique( + InstructionFromMessage(ir_context, function_begin)); + + // Keeps track of which instruction protobuf message we are currently + // considering. + uint32_t instruction_index = 1; + const auto num_instructions = + static_cast(message_.instruction().size()); + + // Iterate through all function parameter instructions, adding parameters to + // the new function. + while (instruction_index < num_instructions && + spv::Op(message_.instruction(instruction_index).opcode()) == + spv::Op::OpFunctionParameter) { + new_function->AddParameter(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + instruction_index++; + } + + // After the parameters, there needs to be a label. + if (instruction_index == num_instructions || + spv::Op(message_.instruction(instruction_index).opcode()) != + spv::Op::OpLabel) { + return false; + } + + // Iterate through the instructions block by block until the end of the + // function is reached. + while (instruction_index < num_instructions && + spv::Op(message_.instruction(instruction_index).opcode()) != + spv::Op::OpFunctionEnd) { + // Invariant: we should always be at a label instruction at this point. + assert(spv::Op(message_.instruction(instruction_index).opcode()) == + spv::Op::OpLabel); + + // Make a basic block using the label instruction. + std::unique_ptr block = + MakeUnique(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + + // Consider successive instructions until we hit another label or the end + // of the function, adding each such instruction to the block. + instruction_index++; + while (instruction_index < num_instructions && + spv::Op(message_.instruction(instruction_index).opcode()) != + spv::Op::OpFunctionEnd && + spv::Op(message_.instruction(instruction_index).opcode()) != + spv::Op::OpLabel) { + block->AddInstruction(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + instruction_index++; + } + // Add the block to the new function. + new_function->AddBasicBlock(std::move(block)); + } + // Having considered all the blocks, we should be at the last instruction and + // it needs to be OpFunctionEnd. + if (instruction_index != num_instructions - 1 || + spv::Op(message_.instruction(instruction_index).opcode()) != + spv::Op::OpFunctionEnd) { + return false; + } + // Set the function's final instruction, add the function to the module and + // report success. + new_function->SetFunctionEnd(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + ir_context->AddFunction(std::move(new_function)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + return true; +} + +bool TransformationAddFunction::TryToMakeFunctionLivesafe( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(message_.is_livesafe() && "Precondition: is_livesafe must hold."); + + // Get a pointer to the added function. + opt::Function* added_function = nullptr; + for (auto& function : *ir_context->module()) { + if (function.result_id() == message_.instruction(0).result_id()) { + added_function = &function; + break; + } + } + assert(added_function && "The added function should have been found."); + + if (!TryToAddLoopLimiters(ir_context, added_function)) { + // Adding loop limiters did not work; bail out. + return false; + } + + // Consider all the instructions in the function, and: + // - attempt to replace OpKill and OpUnreachable with return instructions + // - attempt to clamp access chains to be within bounds + // - check that OpFunctionCall instructions are only to livesafe functions + for (auto& block : *added_function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case spv::Op::OpKill: + case spv::Op::OpUnreachable: + if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function, + &inst)) { + return false; + } + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (!TryToClampAccessChainIndices(ir_context, &inst)) { + return false; + } + break; + case spv::Op::OpFunctionCall: + // A livesafe function my only call other livesafe functions. + if (!transformation_context.GetFactManager()->FunctionIsLivesafe( + inst.GetSingleWordInOperand(0))) { + return false; + } + default: + break; + } + } + } + return true; +} + +uint32_t TransformationAddFunction::GetBackEdgeBlockId( + opt::IRContext* ir_context, uint32_t loop_header_block_id) { + const auto* loop_header_block = + ir_context->cfg()->block(loop_header_block_id); + assert(loop_header_block && "|loop_header_block_id| is invalid"); + + for (auto pred : ir_context->cfg()->preds(loop_header_block_id)) { + if (ir_context->GetDominatorAnalysis(loop_header_block->GetParent()) + ->Dominates(loop_header_block_id, pred)) { + return pred; + } + } + + return 0; +} + +bool TransformationAddFunction::TryToAddLoopLimiters( + opt::IRContext* ir_context, opt::Function* added_function) const { + // Collect up all the loop headers so that we can subsequently add loop + // limiting logic. + std::vector loop_headers; + for (auto& block : *added_function) { + if (block.IsLoopHeader()) { + loop_headers.push_back(&block); + } + } + + if (loop_headers.empty()) { + // There are no loops, so no need to add any loop limiters. + return true; + } + + // Check that the module contains appropriate ingredients for declaring and + // manipulating a loop limiter. + + auto loop_limit_constant_id_instr = + ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id()); + if (!loop_limit_constant_id_instr || + loop_limit_constant_id_instr->opcode() != spv::Op::OpConstant) { + // The loop limit constant id instruction must exist and have an + // appropriate opcode. + return false; + } + + auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef( + loop_limit_constant_id_instr->type_id()); + if (loop_limit_type->opcode() != spv::Op::OpTypeInt || + loop_limit_type->GetSingleWordInOperand(0) != 32) { + // The type of the loop limit constant must be 32-bit integer. It + // doesn't actually matter whether the integer is signed or not. + return false; + } + + // Find the id of the "unsigned int" type. + opt::analysis::Integer unsigned_int_type(32, false); + uint32_t unsigned_int_type_id = + ir_context->get_type_mgr()->GetId(&unsigned_int_type); + if (!unsigned_int_type_id) { + // Unsigned int is not available; we need this type in order to add loop + // limiters. + return false; + } + auto registered_unsigned_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type); + + // Look for 0 of type unsigned int. + opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(), + {0}); + auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero); + if (!registered_zero) { + // We need 0 in order to be able to initialize loop limiters. + return false; + } + uint32_t zero_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(registered_zero) + ->result_id(); + + // Look for 1 of type unsigned int. + opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(), + {1}); + auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one); + if (!registered_one) { + // We need 1 in order to be able to increment loop limiters. + return false; + } + uint32_t one_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(registered_one) + ->result_id(); + + // Look for pointer-to-unsigned int type. + opt::analysis::Pointer pointer_to_unsigned_int_type( + registered_unsigned_int_type, spv::StorageClass::Function); + uint32_t pointer_to_unsigned_int_type_id = + ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type); + if (!pointer_to_unsigned_int_type_id) { + // We need pointer-to-unsigned int in order to declare the loop limiter + // variable. + return false; + } + + // Look for bool type. + opt::analysis::Bool bool_type; + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // We need bool in order to compare the loop limiter's value with the loop + // limit constant. + return false; + } + + // Declare the loop limiter variable at the start of the function's entry + // block, via an instruction of the form: + // %loop_limiter_var = spv::Op::OpVariable %ptr_to_uint Function %zero + added_function->begin()->begin()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpVariable, pointer_to_unsigned_int_type_id, + message_.loop_limiter_variable_id(), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}, + {SPV_OPERAND_TYPE_ID, {zero_id}}}))); + // Update the module's id bound since we have added the loop limiter + // variable id. + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.loop_limiter_variable_id()); + + // Consider each loop in turn. + for (auto loop_header : loop_headers) { + // Look for the loop's back-edge block. This is a predecessor of the loop + // header that is dominated by the loop header. + const auto back_edge_block_id = + GetBackEdgeBlockId(ir_context, loop_header->id()); + if (!back_edge_block_id) { + // The loop's back-edge block must be unreachable. This means that the + // loop cannot iterate, so there is no need to make it lifesafe; we can + // move on from this loop. + continue; + } + + // If the loop's merge block is unreachable, then there are no constraints + // on where the merge block appears in relation to the blocks of the loop. + // This means we need to be careful when adding a branch from the back-edge + // block to the merge block: the branch might make the loop merge reachable, + // and it might then be dominated by the loop header and possibly by other + // blocks in the loop. Since a block needs to appear before those blocks it + // strictly dominates, this could make the module invalid. To avoid this + // problem we bail out in the case where the loop header does not dominate + // the loop merge. + if (!ir_context->GetDominatorAnalysis(added_function) + ->Dominates(loop_header->id(), loop_header->MergeBlockId())) { + return false; + } + + // Go through the sequence of loop limiter infos and find the one + // corresponding to this loop. + bool found = false; + protobufs::LoopLimiterInfo loop_limiter_info; + for (auto& info : message_.loop_limiter_info()) { + if (info.loop_header_id() == loop_header->id()) { + loop_limiter_info = info; + found = true; + break; + } + } + if (!found) { + // We don't have loop limiter info for this loop header. + return false; + } + + // The back-edge block either has the form: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // OpBranch %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_merge %loop_header + // + // We turn these into the following: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // OpBranchConditional %t3 %loop_merge %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpULessThan %bool %t1 %loop_limit + // %t4 = OpLogicalAnd %bool %c %t3 + // OpBranchConditional %t4 %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // %t4 = OpLogicalOr %bool %c %t3 + // OpBranchConditional %t4 %loop_merge %loop_header + + auto back_edge_block = ir_context->cfg()->block(back_edge_block_id); + auto back_edge_block_terminator = back_edge_block->terminator(); + bool compare_using_greater_than_equal; + if (back_edge_block_terminator->opcode() == spv::Op::OpBranch) { + compare_using_greater_than_equal = true; + } else { + assert(back_edge_block_terminator->opcode() == + spv::Op::OpBranchConditional); + assert(((back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->MergeBlockId()) || + (back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId())) && + "A back edge edge block must branch to" + " either the loop header or merge"); + compare_using_greater_than_equal = + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId(); + } + + std::vector> new_instructions; + + // Add a load from the loop limiter variable, of the form: + // %t1 = OpLoad %uint32 %loop_limiter + new_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpLoad, unsigned_int_type_id, + loop_limiter_info.load_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}}))); + + // Increment the loaded value: + // %t2 = OpIAdd %uint32 %t1 %one + new_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpIAdd, unsigned_int_type_id, + loop_limiter_info.increment_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {one_id}}}))); + + // Store the incremented value back to the loop limiter variable: + // OpStore %loop_limiter %t2 + new_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}}))); + + // Compare the loaded value with the loop limit; either: + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // or + // %t3 = OpULessThan %bool %t1 %loop_limit + new_instructions.push_back(MakeUnique( + ir_context, + compare_using_greater_than_equal ? spv::Op::OpUGreaterThanEqual + : spv::Op::OpULessThan, + bool_type_id, loop_limiter_info.compare_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}}))); + + if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) { + new_instructions.push_back(MakeUnique( + ir_context, + compare_using_greater_than_equal ? spv::Op::OpLogicalOr + : spv::Op::OpLogicalAnd, + bool_type_id, loop_limiter_info.logical_op_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {back_edge_block_terminator->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}}))); + } + + // Add the new instructions at the end of the back edge block, before the + // terminator and any loop merge instruction (as the back edge block can + // be the loop header). + if (back_edge_block->GetLoopMergeInst()) { + back_edge_block->GetLoopMergeInst()->InsertBefore( + std::move(new_instructions)); + } else { + back_edge_block_terminator->InsertBefore(std::move(new_instructions)); + } + + if (back_edge_block_terminator->opcode() == spv::Op::OpBranchConditional) { + back_edge_block_terminator->SetInOperand( + 0, {loop_limiter_info.logical_op_id()}); + } else { + assert(back_edge_block_terminator->opcode() == spv::Op::OpBranch && + "Back-edge terminator must be OpBranch or OpBranchConditional"); + + // Check that, if the merge block starts with OpPhi instructions, suitable + // ids have been provided to give these instructions a value corresponding + // to the new incoming edge from the back edge block. + auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId()); + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block, + merge_block, + loop_limiter_info.phi_id())) { + return false; + } + + // Augment OpPhi instructions at the loop merge with the given ids. + uint32_t phi_index = 0; + for (auto& inst : *merge_block) { + if (inst.opcode() != spv::Op::OpPhi) { + break; + } + assert(phi_index < + static_cast(loop_limiter_info.phi_id().size()) && + "There should be at least one phi id per OpPhi instruction."); + inst.AddOperand( + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}}); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}}); + phi_index++; + } + + // Add the new edge, by changing OpBranch to OpBranchConditional. + back_edge_block_terminator->SetOpcode(spv::Op::OpBranchConditional); + back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}}, + {SPV_OPERAND_TYPE_ID, {loop_header->id()}}})); + } + + // Update the module's id bound with respect to the various ids that + // have been used for loop limiter manipulation. + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.increment_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.logical_op_id()); + } + return true; +} + +bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* ir_context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const { + assert((kill_or_unreachable_inst->opcode() == spv::Op::OpKill || + kill_or_unreachable_inst->opcode() == spv::Op::OpUnreachable) && + "Precondition: instruction must be OpKill or OpUnreachable."); + + // Get the function's return type. + auto function_return_type_inst = + ir_context->get_def_use_mgr()->GetDef(added_function->type_id()); + + if (function_return_type_inst->opcode() == spv::Op::OpTypeVoid) { + // The function has void return type, so change this instruction to + // OpReturn. + kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturn); + } else { + // The function has non-void return type, so change this instruction + // to OpReturnValue, using the value id provided with the + // transformation. + + // We first check that the id, %id, provided with the transformation + // specifically to turn OpKill and OpUnreachable instructions into + // OpReturnValue %id has the same type as the function's return type. + if (ir_context->get_def_use_mgr() + ->GetDef(message_.kill_unreachable_return_value_id()) + ->type_id() != function_return_type_inst->result_id()) { + return false; + } + kill_or_unreachable_inst->SetOpcode(spv::Op::OpReturnValue); + kill_or_unreachable_inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}}); + } + return true; +} + +bool TransformationAddFunction::TryToClampAccessChainIndices( + opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const { + assert((access_chain_inst->opcode() == spv::Op::OpAccessChain || + access_chain_inst->opcode() == spv::Op::OpInBoundsAccessChain) && + "Precondition: instruction must be OpAccessChain or " + "OpInBoundsAccessChain."); + + // Find the AccessChainClampingInfo associated with this access chain. + const protobufs::AccessChainClampingInfo* access_chain_clamping_info = + nullptr; + for (auto& clamping_info : message_.access_chain_clamping_info()) { + if (clamping_info.access_chain_id() == access_chain_inst->result_id()) { + access_chain_clamping_info = &clamping_info; + break; + } + } + if (!access_chain_clamping_info) { + // No access chain clamping information was found; the function cannot be + // made livesafe. + return false; + } + + // Check that there is a (compare_id, select_id) pair for every + // index associated with the instruction. + if (static_cast( + access_chain_clamping_info->compare_and_select_ids().size()) != + access_chain_inst->NumInOperands() - 1) { + return false; + } + + // Walk the access chain, clamping each index to be within bounds if it is + // not a constant. + auto base_object = ir_context->get_def_use_mgr()->GetDef( + access_chain_inst->GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = + ir_context->get_def_use_mgr()->GetDef(base_object->type_id()); + assert(pointer_type && pointer_type->opcode() == spv::Op::OpTypePointer && + "The base object must have pointer type."); + auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Consider each index input operand in turn (operand 0 is the base object). + for (uint32_t index = 1; index < access_chain_inst->NumInOperands(); + index++) { + // We are going to turn: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + // + // ... unless %index is already a constant. + + // Get the bound for the composite being indexed into; e.g. the number of + // columns of matrix or the size of an array. + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *should_be_composite_type, ir_context); + + // Get the instruction associated with the index and figure out its integer + // type. + const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index); + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = + ir_context->get_def_use_mgr()->GetDef(index_inst->type_id()); + assert(index_type_inst->opcode() == spv::Op::OpTypeInt); + assert(index_type_inst->GetSingleWordInOperand(0) == 32); + opt::analysis::Integer* index_int_type = + ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + + if (index_inst->opcode() != spv::Op::OpConstant || + index_inst->GetSingleWordInOperand(0) >= bound) { + // The index is either non-constant or an out-of-bounds constant, so we + // need to clamp it. + assert(should_be_composite_type->opcode() != spv::Op::OpTypeStruct && + "Access chain indices into structures are required to be " + "constants."); + opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1}); + if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) { + // We do not have an integer constant whose value is |bound| -1. + return false; + } + + opt::analysis::Bool bool_type; + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // Bool type is not declared; we cannot do a comparison. + return false; + } + + uint32_t bound_minus_one_id = + ir_context->get_constant_mgr() + ->GetDefiningInstruction(&bound_minus_one) + ->result_id(); + + uint32_t compare_id = + access_chain_clamping_info->compare_and_select_ids(index - 1).first(); + uint32_t select_id = + access_chain_clamping_info->compare_and_select_ids(index - 1) + .second(); + std::vector> new_instructions; + + // Compare the index with the bound via an instruction of the form: + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + new_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpULessThanEqual, bool_type_id, compare_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Select the index if in-bounds, otherwise one less than the bound: + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + new_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpSelect, index_type_inst->result_id(), + select_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {compare_id}}, + {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Add the new instructions before the access chain + access_chain_inst->InsertBefore(std::move(new_instructions)); + + // Replace %index with %t2. + access_chain_inst->SetInOperand(index, {select_id}); + fuzzerutil::UpdateModuleIdBound(ir_context, compare_id); + fuzzerutil::UpdateModuleIdBound(ir_context, select_id); + } + should_be_composite_type = + FollowCompositeIndex(ir_context, *should_be_composite_type, index_id); + } + return true; +} + +opt::Instruction* TransformationAddFunction::FollowCompositeIndex( + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, + uint32_t index_id) { + uint32_t sub_object_type_id; + switch (composite_type_inst.opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: { + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); + assert(index_inst->opcode() == spv::Op::OpConstant); + assert(ir_context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->opcode() == spv::Op::OpTypeInt); + assert(ir_context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->GetSingleWordInOperand(0) == 32); + uint32_t index_value = index_inst->GetSingleWordInOperand(0); + sub_object_type_id = + composite_type_inst.GetSingleWordInOperand(index_value); + break; + } + default: + assert(false && "Unknown composite type."); + sub_object_type_id = 0; + break; + } + assert(sub_object_type_id && "No sub-object found."); + return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id); +} + +std::unordered_set TransformationAddFunction::GetFreshIds() const { + std::unordered_set result; + for (auto& instruction : message_.instruction()) { + result.insert(instruction.result_id()); + } + if (message_.is_livesafe()) { + result.insert(message_.loop_limiter_variable_id()); + for (auto& loop_limiter_info : message_.loop_limiter_info()) { + result.insert(loop_limiter_info.load_id()); + result.insert(loop_limiter_info.increment_id()); + result.insert(loop_limiter_info.compare_id()); + result.insert(loop_limiter_info.logical_op_id()); + } + for (auto& access_chain_clamping_info : + message_.access_chain_clamping_info()) { + for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { + result.insert(pair.first()); + result.insert(pair.second()); + } + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.h new file mode 100644 index 00000000..c41eee33 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_function.h @@ -0,0 +1,128 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddFunction : public Transformation { + public: + explicit TransformationAddFunction( + protobufs::TransformationAddFunction message); + + // Creates a transformation to add a non live-safe function. + explicit TransformationAddFunction( + const std::vector& instructions); + + // Creates a transformation to add a live-safe function. + TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers); + + // - |message_.instruction| must correspond to a sufficiently well-formed + // sequence of instructions that a function can be created from them + // - If |message_.is_livesafe| holds then |message_| must contain suitable + // ingredients to make the function livesafe, and the function must only + // invoke other livesafe functions + // - Adding the created function to the module must lead to a valid module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds the function defined by |message_.instruction| to the module, making + // it livesafe if |message_.is_livesafe| holds. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Helper method that, given composite type |composite_type_inst|, returns the + // type of the sub-object at index |index_id|, which is required to be in- + // bounds. + static opt::Instruction* FollowCompositeIndex( + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, + uint32_t index_id); + + // Returns id of the back-edge block, given the corresponding + // |loop_header_block_id|. |loop_header_block_id| must be the id of a loop + // header block. Returns 0 if the loop has no back-edge block. + static uint32_t GetBackEdgeBlockId(opt::IRContext* ir_context, + uint32_t loop_header_block_id); + + // Attempts to create a function from the series of instructions in + // |message_.instruction| and add it to |ir_context|. + // + // Returns false if adding the function is not possible due to the messages + // not respecting the basic structure of a function, e.g. if there is no + // OpFunction instruction or no blocks; in this case |ir_context| is left in + // an indeterminate state. + // + // Otherwise returns true. Whether |ir_context| is valid after addition of + // the function depends on the contents of |message_.instruction|. + // + // Intended usage: + // - Perform a dry run of this method on a clone of a module, and use + // the validator to check whether the resulting module is valid. Working + // on a clone means it does not matter if the function fails to be cleanly + // added, or leads to an invalid module. + // - If the dry run succeeds, run the method on the real module of interest, + // to add the function. + bool TryToAddFunction(opt::IRContext* ir_context) const; + + private: + // Should only be called if |message_.is_livesafe| holds. Attempts to make + // the function livesafe (see FactFunctionIsLivesafe for a definition). + // Returns false if this is not possible, due to |message_| or |ir_context| + // not containing sufficient ingredients (such as types and fresh ids) to add + // the instrumentation necessary to make the function livesafe. + bool TryToMakeFunctionLivesafe( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting + // logic. + bool TryToAddLoopLimiters(opt::IRContext* ir_context, + opt::Function* added_function) const; + + // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and + // OpUnreachable instructions into return instructions. + bool TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* ir_context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const; + + // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain + // indices so that they are guaranteed to be in-bounds. + bool TryToClampAccessChainIndices(opt::IRContext* ir_context, + opt::Instruction* access_chain_inst) const; + + protobufs::TransformationAddFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.cpp new file mode 100644 index 00000000..5aca19d9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_undef.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalUndef::TransformationAddGlobalUndef( + spvtools::fuzz::protobufs::TransformationAddGlobalUndef message) + : message_(std::move(message)) {} + +TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddGlobalUndef::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto type = ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + // The type must exist, and must not be a function or pointer type. + return type != nullptr && opt::IsTypeInst(type->opcode()) && + type->opcode() != spv::Op::OpTypeFunction && + type->opcode() != spv::Op::OpTypePointer; +} + +void TransformationAddGlobalUndef::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpUndef, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); +} + +protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_undef() = message_; + return result; +} + +std::unordered_set TransformationAddGlobalUndef::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.h new file mode 100644 index 00000000..fff1ad9d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_undef.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalUndef : public Transformation { + public: + explicit TransformationAddGlobalUndef( + protobufs::TransformationAddGlobalUndef message); + + TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a non-function, non-pointer type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpUndef instruction to the module, with |message_.type_id| as its + // type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddGlobalUndef message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.cpp new file mode 100644 index 00000000..6e82d594 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + spvtools::fuzz::protobufs::TransformationAddGlobalVariable message) + : message_(std::move(message)) {} + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + uint32_t fresh_id, uint32_t type_id, spv::StorageClass storage_class, + uint32_t initializer_id, bool value_is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_storage_class(uint32_t(storage_class)); + message_.set_initializer_id(initializer_id); + message_.set_value_is_irrelevant(value_is_irrelevant); +} + +bool TransformationAddGlobalVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The storage class must be Private or Workgroup. + auto storage_class = static_cast(message_.storage_class()); + switch (storage_class) { + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: + break; + default: + assert(false && "Unsupported storage class."); + return false; + } + // The type id must correspond to a type. + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); + if (!type) { + return false; + } + // That type must be a pointer type ... + auto pointer_type = type->AsPointer(); + if (!pointer_type) { + return false; + } + // ... with the right storage class. + if (pointer_type->storage_class() != storage_class) { + return false; + } + if (message_.initializer_id()) { + // An initializer is not allowed if the storage class is Workgroup. + if (storage_class == spv::StorageClass::Workgroup) { + assert(false && + "By construction this transformation should not have an " + "initializer when Workgroup storage class is used."); + return false; + } + // The initializer id must be the id of a constant. Check this with the + // constant manager. + auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds( + {message_.initializer_id()}); + if (constant_id.empty()) { + return false; + } + assert(constant_id.size() == 1 && + "We asked for the constant associated with a single id; we should " + "get a single constant."); + // The type of the constant must match the pointee type of the pointer. + if (pointer_type->pointee_type() != constant_id[0]->type()) { + return false; + } + } + return true; +} + +void TransformationAddGlobalVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction* new_instruction = fuzzerutil::AddGlobalVariable( + ir_context, message_.fresh_id(), message_.type_id(), + static_cast(message_.storage_class()), + message_.initializer_id()); + + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + + if (message_.value_is_irrelevant()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_variable() = message_; + return result; +} + +std::unordered_set TransformationAddGlobalVariable::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.h new file mode 100644 index 00000000..01782198 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_global_variable.h @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalVariable : public Transformation { + public: + explicit TransformationAddGlobalVariable( + protobufs::TransformationAddGlobalVariable message); + + TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, + spv::StorageClass storage_class, + uint32_t initializer_id, + bool value_is_irrelevant); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a pointer type with the same storage + // class as |message_.storage_class| + // - |message_.storage_class| must be Private or Workgroup + // - |message_.initializer_id| must be 0 if |message_.storage_class| is + // Workgroup, and otherwise may either be 0 or the id of a constant whose + // type is the pointee type of |message_.type_id| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a global variable with storage class |message_.storage_class| to the + // module, with type |message_.type_id| and either no initializer or + // |message_.initializer_id| as an initializer, depending on whether + // |message_.initializer_id| is 0. The global variable has result id + // |message_.fresh_id|. + // + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddGlobalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp new file mode 100644 index 00000000..018fed4c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + protobufs::TransformationAddImageSampleUnusedComponents message) + : message_(std::move(message)) {} + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor) { + message_.set_coordinate_with_unused_components_id( + coordinate_with_unused_components_id); + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationAddImageSampleUnusedComponents::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + // The image sample instruction must be defined. + if (image_sample_instruction == nullptr) { + return false; + } + + // The instruction must be an image sample instruction. + if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) { + return false; + } + + uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + ir_context->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = + ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id()); + + // It must be possible to add unused components. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return false; + } + + auto coordinate_with_unused_components_instruction = + ir_context->get_def_use_mgr()->GetDef( + message_.coordinate_with_unused_components_id()); + + // The coordinate with unused components instruction must be defined. + if (coordinate_with_unused_components_instruction == nullptr) { + return false; + } + + // It must be an OpCompositeConstruct instruction such that it can be checked + // that the original components are present. + if (coordinate_with_unused_components_instruction->opcode() != + spv::Op::OpCompositeConstruct) { + return false; + } + + // The first constituent must be the original coordinate. + if (coordinate_with_unused_components_instruction->GetSingleWordInOperand( + 0) != coordinate_id) { + return false; + } + + auto coordinate_with_unused_components_type = + ir_context->get_type_mgr()->GetType( + coordinate_with_unused_components_instruction->type_id()); + + // |coordinate_with_unused_components_type| must be a vector. + if (!coordinate_with_unused_components_type->AsVector()) { + return false; + } + + return true; +} + +void TransformationAddImageSampleUnusedComponents::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Sets the coordinate operand. + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + image_sample_instruction->SetInOperand( + 1, {message_.coordinate_with_unused_components_id()}); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationAddImageSampleUnusedComponents::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_image_sample_unused_components() = message_; + return result; +} + +std::unordered_set +TransformationAddImageSampleUnusedComponents::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h new file mode 100644 index 00000000..7b13f9f6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddImageSampleUnusedComponents : public Transformation { + public: + explicit TransformationAddImageSampleUnusedComponents( + protobufs::TransformationAddImageSampleUnusedComponents message); + + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |coordinate_with_unused_components_id| must identify a vector such that + // the first components match the components of the image sample coordinate. + // - |message_.instruction_descriptor| must identify an image sample + // instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Add unused components to an image sample coordinate by replacing the + // coordinate with |coordinate_with_unused_components_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddImageSampleUnusedComponents message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.cpp new file mode 100644 index 00000000..9ee210cb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_local_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddLocalVariable::TransformationAddLocalVariable( + spvtools::fuzz::protobufs::TransformationAddLocalVariable message) + : message_(std::move(message)) {} + +TransformationAddLocalVariable::TransformationAddLocalVariable( + uint32_t fresh_id, uint32_t type_id, uint32_t function_id, + uint32_t initializer_id, bool value_is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_function_id(function_id); + message_.set_initializer_id(initializer_id); + message_.set_value_is_irrelevant(value_is_irrelevant); +} + +bool TransformationAddLocalVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The provided id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The pointer type id must indeed correspond to a pointer, and it must have + // function storage class. + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!type_instruction || + type_instruction->opcode() != spv::Op::OpTypePointer || + spv::StorageClass(type_instruction->GetSingleWordInOperand(0)) != + spv::StorageClass::Function) { + return false; + } + // The initializer must... + auto initializer_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + // ... exist, ... + if (!initializer_instruction) { + return false; + } + // ... be a constant, ... + if (!spvOpcodeIsConstant(initializer_instruction->opcode())) { + return false; + } + // ... and have the same type as the pointee type. + if (initializer_instruction->type_id() != + type_instruction->GetSingleWordInOperand(1)) { + return false; + } + // The function to which the local variable is to be added must exist. + return fuzzerutil::FindFunction(ir_context, message_.function_id()); +} + +void TransformationAddLocalVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction* new_instruction = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_id(), message_.type_id(), + message_.function_id(), message_.initializer_id()); + + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block( + new_instruction, + fuzzerutil::FindFunction(ir_context, message_.function_id()) + ->entry() + .get()); + + if (message_.value_is_irrelevant()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddLocalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_local_variable() = message_; + return result; +} + +std::unordered_set TransformationAddLocalVariable::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.h new file mode 100644 index 00000000..b008a1c0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_local_variable.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddLocalVariable : public Transformation { + public: + explicit TransformationAddLocalVariable( + protobufs::TransformationAddLocalVariable message); + + TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id, + uint32_t function_id, uint32_t initializer_id, + bool value_is_irrelevant); + + // - |message_.fresh_id| must not be used by the module + // - |message_.type_id| must be the id of a pointer type with Function + // storage class + // - |message_.initializer_id| must be the id of a constant with the same + // type as the pointer's pointee type + // - |message_.function_id| must be the id of a function + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction to the start of |message_.function_id|, of the form: + // |message_.fresh_id| = OpVariable |message_.type_id| Function + // |message_.initializer_id| + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLocalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp new file mode 100644 index 00000000..4b66b69f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp @@ -0,0 +1,233 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_add_loop_preheader.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { +TransformationAddLoopPreheader::TransformationAddLoopPreheader( + protobufs::TransformationAddLoopPreheader message) + : message_(std::move(message)) {} + +TransformationAddLoopPreheader::TransformationAddLoopPreheader( + uint32_t loop_header_block, uint32_t fresh_id, + std::vector phi_id) { + message_.set_loop_header_block(loop_header_block); + message_.set_fresh_id(fresh_id); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddLoopPreheader::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& /* unused */) const { + // |message_.loop_header_block()| must be the id of a loop header block. + opt::BasicBlock* loop_header_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block()); + if (!loop_header_block || !loop_header_block->IsLoopHeader()) { + return false; + } + + // The id for the preheader must actually be fresh. + std::set used_ids; + if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(), + ir_context, &used_ids)) { + return false; + } + + size_t num_predecessors = + ir_context->cfg()->preds(message_.loop_header_block()).size(); + + // The block must have at least 2 predecessors (the back-edge block and + // another predecessor outside of the loop) + if (num_predecessors < 2) { + return false; + } + + // If the block only has one predecessor outside of the loop (and thus 2 in + // total), then no additional fresh ids are necessary. + if (num_predecessors == 2) { + return true; + } + + // Count the number of OpPhi instructions. + int32_t num_phi_insts = 0; + loop_header_block->ForEachPhiInst( + [&num_phi_insts](opt::Instruction* /* unused */) { num_phi_insts++; }); + + // There must be enough fresh ids for the OpPhi instructions. + if (num_phi_insts > message_.phi_id_size()) { + return false; + } + + // Check that the needed ids are fresh and distinct. + for (int32_t i = 0; i < num_phi_insts; i++) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i), + ir_context, &used_ids)) { + return false; + } + } + + return true; +} + +void TransformationAddLoopPreheader::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Find the loop header. + opt::BasicBlock* loop_header = + fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block()); + + auto dominator_analysis = + ir_context->GetDominatorAnalysis(loop_header->GetParent()); + + uint32_t back_edge_block_id = 0; + + // Update the branching instructions of the out-of-loop predecessors of the + // header. Set |back_edge_block_id| to be the id of the back-edge block. + ir_context->get_def_use_mgr()->ForEachUse( + loop_header->id(), + [this, &ir_context, &dominator_analysis, &loop_header, + &back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) { + if (dominator_analysis->Dominates(loop_header->GetLabelInst(), + use_inst)) { + // If |use_inst| is a branch instruction dominated by the header, the + // block containing it is the back-edge block. + if (use_inst->IsBranch()) { + assert(back_edge_block_id == 0 && + "There should only be one back-edge block"); + back_edge_block_id = ir_context->get_instr_block(use_inst)->id(); + } + // References to the header inside the loop should not be updated + return; + } + + // If |use_inst| is not a branch or merge instruction, it should not be + // changed. + if (!use_inst->IsBranch() && + use_inst->opcode() != spv::Op::OpSelectionMerge && + use_inst->opcode() != spv::Op::OpLoopMerge) { + return; + } + + // Update the reference. + use_inst->SetOperand(use_index, {message_.fresh_id()}); + }); + + assert(back_edge_block_id && "The back-edge block should have been found"); + + // Make a new block for the preheader. + std::unique_ptr preheader = MakeUnique( + std::unique_ptr(new opt::Instruction( + ir_context, spv::Op::OpLabel, 0, message_.fresh_id(), {}))); + + uint32_t phi_ids_used = 0; + + // Update the OpPhi instructions and, if there is more than one out-of-loop + // predecessor, add necessary OpPhi instructions so the preheader. + loop_header->ForEachPhiInst([this, &ir_context, &preheader, + &back_edge_block_id, + &phi_ids_used](opt::Instruction* phi_inst) { + // The loop header must have at least 2 incoming edges (the back edge, and + // at least one from outside the loop). + assert(phi_inst->NumInOperands() >= 4); + + if (phi_inst->NumInOperands() == 4) { + // There is just one out-of-loop predecessor, so no additional + // instructions in the preheader are necessary. The reference to the + // original out-of-loop predecessor needs to be updated so that it refers + // to the preheader. + uint32_t index_of_out_of_loop_pred_id = + phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1; + phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()}); + } else { + // There is more than one out-of-loop predecessor, so an OpPhi instruction + // needs to be added to the preheader, and its value will depend on all + // the current out-of-loop predecessors of the header. + + // Get the operand list and the value corresponding to the back-edge + // block. + std::vector preheader_in_operands; + uint32_t back_edge_val = 0; + + for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) { + // Only add operands if they don't refer to the back-edge block. + if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) { + back_edge_val = phi_inst->GetInOperand(i).words[0]; + } else { + preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i))); + preheader_in_operands.push_back( + std::move(phi_inst->GetInOperand(i + 1))); + } + } + + // Add the new instruction to the preheader. + uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++); + + // Update id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id); + + preheader->AddInstruction(std::unique_ptr( + new opt::Instruction(ir_context, spv::Op::OpPhi, phi_inst->type_id(), + fresh_phi_id, preheader_in_operands))); + + // Update the OpPhi instruction in the header so that it refers to the + // back edge block and the preheader as the predecessors, and it uses the + // newly-defined OpPhi in the preheader for the corresponding value. + phi_inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {fresh_phi_id}}, + {SPV_OPERAND_TYPE_ID, {preheader->id()}}, + {SPV_OPERAND_TYPE_ID, {back_edge_val}}, + {SPV_OPERAND_TYPE_ID, {back_edge_block_id}}}); + } + }); + + // Update id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add an unconditional branch from the preheader to the header. + preheader->AddInstruction( + std::unique_ptr(new opt::Instruction( + ir_context, spv::Op::OpBranch, 0, 0, + std::initializer_list{opt::Operand( + spv_operand_type_t::SPV_OPERAND_TYPE_ID, {loop_header->id()})}))); + + // Insert the preheader in the module. + loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader), + loop_header); + + // Invalidate analyses because the structure of the program changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_loop_preheader() = message_; + return result; +} + +std::unordered_set TransformationAddLoopPreheader::GetFreshIds() + const { + std::unordered_set result = {message_.fresh_id()}; + for (auto id : message_.phi_id()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.h new file mode 100644 index 00000000..9d2c565f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_preheader.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddLoopPreheader : public Transformation { + public: + explicit TransformationAddLoopPreheader( + protobufs::TransformationAddLoopPreheader message); + + TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id, + std::vector phi_id); + + // - |message_.loop_header_block| must be the id of a loop header block in + // the given module. + // - |message_.fresh_id| must be an available id. + // - |message_.phi_ids| must be a list of available ids. + // It can be empty if the loop header only has one predecessor outside of + // the loop. Otherwise, it must contain at least as many ids as OpPhi + // instructions in the loop header block. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a preheader block as the unique out-of-loop predecessor of the given + // loop header block. All of the existing out-of-loop predecessors of the + // header are changed so that they branch to the preheader instead. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLoopPreheader message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp new file mode 100644 index 00000000..00030e79 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp @@ -0,0 +1,454 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace { +uint32_t kMaxNumOfIterations = 32; +} + +TransformationAddLoopToCreateIntConstantSynonym:: + TransformationAddLoopToCreateIntConstantSynonym( + protobufs::TransformationAddLoopToCreateIntConstantSynonym message) + : message_(std::move(message)) {} + +TransformationAddLoopToCreateIntConstantSynonym:: + TransformationAddLoopToCreateIntConstantSynonym( + uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id, + uint32_t num_iterations_id, uint32_t block_after_loop_id, + uint32_t syn_id, uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id, + uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id, + uint32_t additional_block_id) { + message_.set_constant_id(constant_id); + message_.set_initial_val_id(initial_val_id); + message_.set_step_val_id(step_val_id); + message_.set_num_iterations_id(num_iterations_id); + message_.set_block_after_loop_id(block_after_loop_id); + message_.set_syn_id(syn_id); + message_.set_loop_id(loop_id); + message_.set_ctr_id(ctr_id); + message_.set_temp_id(temp_id); + message_.set_eventual_syn_id(eventual_syn_id); + message_.set_incremented_ctr_id(incremented_ctr_id); + message_.set_cond_id(cond_id); + message_.set_additional_block_id(additional_block_id); +} + +bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |message_.constant_id|, |message_.initial_val_id| and + // |message_.step_val_id| are existing constants, and that their values are + // not irrelevant. + auto constant = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.constant_id()); + auto initial_val = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.initial_val_id()); + auto step_val = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.step_val_id()); + + if (!constant || !initial_val || !step_val) { + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.initial_val_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.step_val_id())) { + return false; + } + + // Check that the type of |constant| is integer scalar or vector with integer + // components. + if (!constant->AsIntConstant() && + (!constant->AsVectorConstant() || + !constant->type()->AsVector()->element_type()->AsInteger())) { + return false; + } + + // Check that the component bit width of |constant| is <= 64. + // Consider the width of the constant if it is an integer, of a single + // component if it is a vector. + uint32_t bit_width = + constant->AsIntConstant() + ? constant->type()->AsInteger()->width() + : constant->type()->AsVector()->element_type()->AsInteger()->width(); + if (bit_width > 64) { + return false; + } + + auto constant_def = + ir_context->get_def_use_mgr()->GetDef(message_.constant_id()); + auto initial_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id()); + auto step_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.step_val_id()); + + // Check that |constant|, |initial_val| and |step_val| have the same type, + // with possibly different signedness. + if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(), + initial_val_def->type_id()) || + !fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(), + step_val_def->type_id())) { + return false; + } + + // |message_.num_iterations_id| must be a non-irrelevant integer constant with + // bit width 32. + auto num_iterations = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.num_iterations_id()); + + if (!num_iterations || !num_iterations->AsIntConstant() || + num_iterations->type()->AsInteger()->width() != 32 || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.num_iterations_id())) { + return false; + } + + // Check that the number of iterations is > 0 and <= 32. + uint32_t num_iterations_value = + num_iterations->AsIntConstant()->GetU32BitValue(); + + if (num_iterations_value == 0 || num_iterations_value > kMaxNumOfIterations) { + return false; + } + + // Check that the module contains 32-bit signed integer scalar constants of + // value 0 and 1. + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {0}, 32, true, false)) { + return false; + } + + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {1}, 32, true, false)) { + return false; + } + + // Check that the module contains the Bool type. + if (!fuzzerutil::MaybeGetBoolType(ir_context)) { + return false; + } + + // Check that the equation C = I - S * N is satisfied. + + // Collect the components in vectors (if the constants are scalars, these + // vectors will contain the constants themselves). + std::vector c_components; + std::vector i_components; + std::vector s_components; + if (constant->AsIntConstant()) { + c_components.emplace_back(constant); + i_components.emplace_back(initial_val); + s_components.emplace_back(step_val); + } else { + // It is a vector: get all the components. + c_components = constant->AsVectorConstant()->GetComponents(); + i_components = initial_val->AsVectorConstant()->GetComponents(); + s_components = step_val->AsVectorConstant()->GetComponents(); + } + + // Check the value of the components satisfy the equation. + for (uint32_t i = 0; i < c_components.size(); i++) { + // Use 64-bits integers to be able to handle constants of any width <= 64. + uint64_t c_value = c_components[i]->AsIntConstant()->GetZeroExtendedValue(); + uint64_t i_value = i_components[i]->AsIntConstant()->GetZeroExtendedValue(); + uint64_t s_value = s_components[i]->AsIntConstant()->GetZeroExtendedValue(); + + uint64_t result = i_value - s_value * num_iterations_value; + + // Use bit shifts to ignore the first bits in excess (if there are any). By + // shifting left, we discard the first |64 - bit_width| bits. By shifting + // right, we move the bits back to their correct position. + result = (result << (64 - bit_width)) >> (64 - bit_width); + + if (c_value != result) { + return false; + } + } + + // Check that |message_.block_after_loop_id| is the label of a block. + auto block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_after_loop_id()); + + // Check that the block exists and has a single predecessor. + if (!block || ir_context->cfg()->preds(block->id()).size() != 1) { + return false; + } + + // Check that the block is not dead. If it is then the new loop would be + // dead and the data it computes would be irrelevant, so we would not be able + // to make a synonym. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + return false; + } + + // Check that the block is not a merge block. + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return false; + } + + // Check that the block is not a continue block. + if (ir_context->GetStructuredCFGAnalysis()->IsContinueBlock(block->id())) { + return false; + } + + // Check that the block is not a loop header. + if (block->IsLoopHeader()) { + return false; + } + + // Check all the fresh ids. + std::set fresh_ids_used; + for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(), + message_.temp_id(), message_.eventual_syn_id(), + message_.incremented_ctr_id(), message_.cond_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &fresh_ids_used)) { + return false; + } + } + + // Check the additional block id if it is non-zero. + return !message_.additional_block_id() || + CheckIdIsFreshAndNotUsedByThisTransformation( + message_.additional_block_id(), ir_context, &fresh_ids_used); +} + +void TransformationAddLoopToCreateIntConstantSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Find 32-bit signed integer constants 0 and 1. + uint32_t const_0_id = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {0}, 32, true, false); + auto const_0_def = ir_context->get_def_use_mgr()->GetDef(const_0_id); + uint32_t const_1_id = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, true, false); + + // Retrieve the instruction defining the initial value constant. + auto initial_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id()); + + // Retrieve the block before which we want to insert the loop. + auto block_after_loop = + ir_context->get_instr_block(message_.block_after_loop_id()); + + // Find the predecessor of the block. + uint32_t pred_id = + ir_context->cfg()->preds(message_.block_after_loop_id())[0]; + + // Get the id for the last block in the new loop. It will be + // |message_.additional_block_id| if this is non_zero, |message_.loop_id| + // otherwise. + uint32_t last_loop_block_id = message_.additional_block_id() + ? message_.additional_block_id() + : message_.loop_id(); + + // Create the loop header block. + std::unique_ptr loop_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.loop_id(), + opt::Instruction::OperandList{})); + + // Add OpPhi instructions to retrieve the current value of the counter and of + // the temporary variable that will be decreased at each operation. + loop_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpPhi, const_0_def->type_id(), message_.ctr_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {const_0_id}}, + {SPV_OPERAND_TYPE_ID, {pred_id}}, + {SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + loop_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpPhi, initial_val_def->type_id(), + message_.temp_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.initial_val_id()}}, + {SPV_OPERAND_TYPE_ID, {pred_id}}, + {SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + // Collect the other instructions in a list. These will be added to an + // additional block if |message_.additional_block_id| is defined, to the loop + // header otherwise. + std::vector> other_instructions; + + // Add an instruction to subtract the step value from the temporary value. + // The value of this id will converge to the constant in the last iteration. + other_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpISub, initial_val_def->type_id(), + message_.eventual_syn_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.temp_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.step_val_id()}}})); + + // Add an instruction to increment the counter. + other_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpIAdd, const_0_def->type_id(), + message_.incremented_ctr_id(), + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {message_.ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {const_1_id}}})); + + // Add an instruction to decide whether the condition holds. + other_instructions.push_back(MakeUnique( + ir_context, spv::Op::OpSLessThan, + fuzzerutil::MaybeGetBoolType(ir_context), message_.cond_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.num_iterations_id()}}})); + + // Define the OpLoopMerge instruction for the loop header. The merge block is + // the existing block, the continue block is the last block in the loop + // (either the loop itself or the additional block). + std::unique_ptr merge_inst = MakeUnique( + ir_context, spv::Op::OpLoopMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, + {uint32_t(spv::LoopControlMask::MaskNone)}}}); + + // Define a conditional branch instruction, branching to the loop header if + // the condition holds, and to the existing block otherwise. This instruction + // will be added to the last block in the loop. + std::unique_ptr conditional_branch = + MakeUnique( + ir_context, spv::Op::OpBranchConditional, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.cond_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.loop_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}}}); + + if (message_.additional_block_id()) { + // If an id for the additional block is specified, create an additional + // block, containing the instructions in the list and a branching + // instruction. + + std::unique_ptr additional_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.additional_block_id(), + opt::Instruction::OperandList{})); + + for (auto& instruction : other_instructions) { + additional_block->AddInstruction(std::move(instruction)); + } + + additional_block->AddInstruction(std::move(conditional_branch)); + + // Add the merge instruction to the header. + loop_block->AddInstruction(std::move(merge_inst)); + + // Add an unconditional branch from the header to the additional block. + loop_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.additional_block_id()}}})); + + // Insert the two loop blocks before the existing block. + block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block), + block_after_loop); + block_after_loop->GetParent()->InsertBasicBlockBefore( + std::move(additional_block), block_after_loop); + } else { + // If no id for an additional block is specified, the loop will only be made + // up of one block, so we need to add all the instructions to it. + + for (auto& instruction : other_instructions) { + loop_block->AddInstruction(std::move(instruction)); + } + + // Add the merge and conditional branch instructions. + loop_block->AddInstruction(std::move(merge_inst)); + loop_block->AddInstruction(std::move(conditional_branch)); + + // Insert the header before the existing block. + block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block), + block_after_loop); + } + + // Update the branching instructions leading to this block. + ir_context->get_def_use_mgr()->ForEachUse( + message_.block_after_loop_id(), + [this](opt::Instruction* instruction, uint32_t operand_index) { + assert(instruction->opcode() != spv::Op::OpLoopMerge && + instruction->opcode() != spv::Op::OpSelectionMerge && + "The block should not be referenced by OpLoopMerge or " + "OpSelectionMerge, by construction."); + // Replace all uses of the label inside branch instructions. + if (instruction->opcode() == spv::Op::OpBranch || + instruction->opcode() == spv::Op::OpBranchConditional || + instruction->opcode() == spv::Op::OpSwitch) { + instruction->SetOperand(operand_index, {message_.loop_id()}); + } + }); + + // Update all the OpPhi instructions in the block after the loop: its + // predecessor is now the last block in the loop. + block_after_loop->ForEachPhiInst( + [last_loop_block_id](opt::Instruction* phi_inst) { + // Since the block only had one predecessor, the id of the predecessor + // is input operand 1. + phi_inst->SetInOperand(1, {last_loop_block_id}); + }); + + // Add a new OpPhi instruction at the beginning of the block after the loop, + // defining the synonym of the constant. The type id will be the same as + // |message_.initial_value_id|, since this is the value that is decremented in + // the loop. + block_after_loop->begin()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpPhi, initial_val_def->type_id(), message_.syn_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + // Update the module id bound with all the fresh ids used. + for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(), + message_.temp_id(), message_.eventual_syn_id(), + message_.incremented_ctr_id(), message_.cond_id(), + message_.cond_id(), message_.additional_block_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Since we changed the structure of the module, we need to invalidate all the + // analyses. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Record that |message_.syn_id| is synonymous with |message_.constant_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.syn_id(), {}), + MakeDataDescriptor(message_.constant_id(), {})); +} + +protobufs::Transformation +TransformationAddLoopToCreateIntConstantSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_loop_to_create_int_constant_synonym() = message_; + return result; +} + +std::unordered_set +TransformationAddLoopToCreateIntConstantSynonym::GetFreshIds() const { + return {message_.syn_id(), message_.loop_id(), + message_.ctr_id(), message_.temp_id(), + message_.eventual_syn_id(), message_.incremented_ctr_id(), + message_.cond_id(), message_.additional_block_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h new file mode 100644 index 00000000..a6dfe63a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h @@ -0,0 +1,71 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationAddLoopToCreateIntConstantSynonym : public Transformation { + public: + explicit TransformationAddLoopToCreateIntConstantSynonym( + protobufs::TransformationAddLoopToCreateIntConstantSynonym message); + + TransformationAddLoopToCreateIntConstantSynonym( + uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id, + uint32_t num_iterations_id, uint32_t block_after_loop_id, uint32_t syn_id, + uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id, + uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id, + uint32_t additional_block_id); + + // - |message_.constant_id|, |message_.initial_value_id|, + // |message_.step_val_id| are integer constants (scalar or vectors) with the + // same type (with possibly different signedness, but same bit width, which + // must be <= 64). Let their value be C, I, S respectively. + // - |message_.num_iterations_id| is a 32-bit integer scalar constant, with + // value N > 0 and N <= 32. + // - The module contains 32-bit signed integer scalar constants of values 0 + // and 1. + // - The module contains the boolean type. + // - C = I - S * N + // - |message_.block_after_loop_id| is the label of a block which has a single + // predecessor and which is not a merge block, a continue block or a loop + // header. + // - |message_.block_after_loop_id| must not be a dead block. + // - |message_.additional_block_id| is either 0 or a valid fresh id, distinct + // from the other fresh ids. + // - All of the other parameters are valid fresh ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a loop to the module, defining a synonym of an integer (scalar or + // vector) constant. This id is marked as synonym with the original constant. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLoopToCreateIntConstantSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp new file mode 100644 index 00000000..87393e95 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration( + protobufs::TransformationAddNoContractionDecoration message) + : message_(std::move(message)) {} + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration(uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddNoContractionDecoration::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.result_id| must be the id of an instruction. + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + // |instr| must not be decorated with NoContraction. + if (ir_context->get_decoration_mgr()->HasDecoration( + message_.result_id(), spv::Decoration::NoContraction)) { + return false; + } + // The instruction must be arithmetic. + return IsArithmetic(instr->opcode()); +} + +void TransformationAddNoContractionDecoration::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add a NoContraction decoration targeting |message_.result_id|. + ir_context->get_decoration_mgr()->AddDecoration( + message_.result_id(), uint32_t(spv::Decoration::NoContraction)); +} + +protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_no_contraction_decoration() = message_; + return result; +} + +bool TransformationAddNoContractionDecoration::IsArithmetic(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + return true; + default: + return false; + } +} + +std::unordered_set +TransformationAddNoContractionDecoration::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h new file mode 100644 index 00000000..4235dc10 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddNoContractionDecoration : public Transformation { + public: + explicit TransformationAddNoContractionDecoration( + protobufs::TransformationAddNoContractionDecoration message); + + explicit TransformationAddNoContractionDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an arithmetic instruction, + // as defined by the SPIR-V specification. + // - It does not matter whether this instruction is already annotated with the + // NoContraction decoration. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| NoContraction' + // to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an arithmetic + // instruction, as defined by the SPIR-V specification. + static bool IsArithmetic(spv::Op opcode); + + private: + protobufs::TransformationAddNoContractionDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp new file mode 100644 index 00000000..31c56b66 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_opphi_synonym.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( + protobufs::TransformationAddOpPhiSynonym message) + : message_(std::move(message)) {} + +TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( + uint32_t block_id, const std::map& preds_to_ids, + uint32_t fresh_id) { + message_.set_block_id(block_id); + *message_.mutable_pred_to_id() = + fuzzerutil::MapToRepeatedUInt32Pair(preds_to_ids); + message_.set_fresh_id(fresh_id); +} + +bool TransformationAddOpPhiSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |message_.block_id| is a block label id, and that it is not + // dead. + auto block = fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + if (!block || + transformation_context.GetFactManager()->BlockIsDead(block->id())) { + return false; + } + + // Check that |message_.fresh_id| is actually fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Check that |message_.pred_to_id| contains a mapping for all of the block's + // predecessors. + std::vector predecessors = ir_context->cfg()->preds(block->id()); + + // There must be at least one predecessor. + if (predecessors.empty()) { + return false; + } + + std::map preds_to_ids = + fuzzerutil::RepeatedUInt32PairToMap(message_.pred_to_id()); + + // There must not be repeated key values in |message_.pred_to_id|. + if (preds_to_ids.size() != static_cast(message_.pred_to_id_size())) { + return false; + } + + // Check that each predecessor has a corresponding mapping and all of the + // corresponding ids exist. + for (uint32_t pred : predecessors) { + if (preds_to_ids.count(pred) == 0) { + return false; + } + + // Check that the id exists in the module. + if (!ir_context->get_def_use_mgr()->GetDef(preds_to_ids[pred])) { + return false; + } + } + + // Get the first id and its type (which should be the same as all the other + // ones) and check that the transformation supports this type. + uint32_t first_id = preds_to_ids[predecessors[0]]; + uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id(); + if (!CheckTypeIsAllowed(ir_context, type_id)) { + return false; + } + + // Check that the ids corresponding to predecessors are all synonymous, have + // the same type and are available to use at the end of the predecessor. + for (uint32_t pred : predecessors) { + auto id = preds_to_ids[pred]; + + // Check that the id has the same type as the other ones. + if (ir_context->get_def_use_mgr()->GetDef(id)->type_id() != type_id) { + return false; + } + + // Check that the id is synonymous with the others by checking that it is + // synonymous with the first one (or it is the same id). + if (id != first_id && + !transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(id, {}), MakeDataDescriptor(first_id, {}))) { + return false; + } + + // Check that the id is available at the end of the corresponding + // predecessor block. + + auto pred_block = ir_context->get_instr_block(pred); + + // We should always be able to find the predecessor block, since it is in + // the predecessors list of |block|. + assert(pred_block && "Could not find one of the predecessor blocks."); + + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, pred_block->terminator(), id)) { + return false; + } + } + + return true; +} + +void TransformationAddOpPhiSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Get the type id from one of the ids. + uint32_t first_id = message_.pred_to_id(0).second(); + uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id(); + + // Define the operand list. + opt::Instruction::OperandList operand_list; + + // For each predecessor, add the corresponding operands. + for (auto& pair : message_.pred_to_id()) { + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {pair.second()}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {pair.first()}}); + } + + // Add a new OpPhi instructions at the beginning of the block. + ir_context->get_instr_block(message_.block_id()) + ->begin() + .InsertBefore(MakeUnique(ir_context, spv::Op::OpPhi, + type_id, message_.fresh_id(), + std::move(operand_list))); + + // Update the module id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Invalidate all analyses, since we added an instruction to the module. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Record the fact that the new id is synonym with the other ones by declaring + // that it is a synonym of the first one. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {}), + MakeDataDescriptor(first_id, {})); +} + +protobufs::Transformation TransformationAddOpPhiSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_opphi_synonym() = message_; + return result; +} + +bool TransformationAddOpPhiSynonym::CheckTypeIsAllowed( + opt::IRContext* ir_context, uint32_t type_id) { + auto type = ir_context->get_type_mgr()->GetType(type_id); + if (!type) { + return false; + } + + // We allow the following types: Bool, Integer, Float, Vector, Matrix, Array, + // Struct. + if (type->AsBool() || type->AsInteger() || type->AsFloat() || + type->AsVector() || type->AsMatrix() || type->AsArray() || + type->AsStruct()) { + return true; + } + + // We allow pointer types if the VariablePointers capability is enabled and + // the pointer has the correct storage class (Workgroup or StorageBuffer). + if (type->AsPointer()) { + auto storage_class = type->AsPointer()->storage_class(); + return ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointers) && + (storage_class == spv::StorageClass::Workgroup || + storage_class == spv::StorageClass::StorageBuffer); + } + + // We do not allow other types. + return false; +} + +std::unordered_set TransformationAddOpPhiSynonym::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h new file mode 100644 index 00000000..39ebea89 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationAddOpPhiSynonym : public Transformation { + public: + explicit TransformationAddOpPhiSynonym( + protobufs::TransformationAddOpPhiSynonym message); + + TransformationAddOpPhiSynonym( + uint32_t block_id, const std::map& preds_to_ids, + uint32_t fresh_id); + + // - |message_.block_id| is the label of a block with at least one + // predecessor. + // - |message_.block_id| must not be a dead block. + // - |message_.pred_to_id| contains a mapping from each of the predecessors of + // the block to an id that is available at the end of the predecessor. + // - All the ids corresponding to a predecessor in |message_.pred_to_id|: + // - have been recorded as synonymous and all have the same type. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3726): if a + // predecessor is a dead block, any id of the right type could be used, + // even if it is not synonym with the others. + // - have one of the following types: Bool, Integer, Float, Vector, Matrix, + // Array, Struct. Pointer types are also allowed if the VariablePointers + // capability is enabled and the storage class is Workgroup or + // StorageBuffer. + // - |message_.fresh_id| is a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Given a block with n predecessors, with n >= 1, and n corresponding + // synonymous ids of the same type, each available to use at the end of the + // corresponding predecessor, adds an OpPhi instruction at the beginning of + // the block of the form: + // %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n + // This instruction is then marked as synonymous with the ids. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Returns true if |type_id| is the id of a type in the module, which is one + // of the following: Bool, Integer, Float, Vector, Matrix, Array, Struct. + // Pointer types are also allowed if the VariablePointers capability is + // enabled and the storage class is Workgroup or StorageBuffer. + static bool CheckTypeIsAllowed(opt::IRContext* ir_context, uint32_t type_id); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddOpPhiSynonym message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.cpp new file mode 100644 index 00000000..8bd2ed78 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_parameter.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddParameter::TransformationAddParameter( + protobufs::TransformationAddParameter message) + : message_(std::move(message)) {} + +TransformationAddParameter::TransformationAddParameter( + uint32_t function_id, uint32_t parameter_fresh_id, + uint32_t parameter_type_id, std::map call_parameter_ids, + uint32_t function_type_fresh_id) { + message_.set_function_id(function_id); + message_.set_parameter_fresh_id(parameter_fresh_id); + message_.set_parameter_type_id(parameter_type_id); + *message_.mutable_call_parameter_ids() = + fuzzerutil::MapToRepeatedUInt32Pair(call_parameter_ids); + message_.set_function_type_fresh_id(function_type_fresh_id); +} + +bool TransformationAddParameter::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that function exists. + const auto* function = + fuzzerutil::FindFunction(ir_context, message_.function_id()); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // The type must be supported. + if (ir_context->get_def_use_mgr()->GetDef(message_.parameter_type_id()) == + nullptr) { + return false; + } + if (!IsParameterTypeSupported(ir_context, message_.parameter_type_id())) { + return false; + } + + // Iterate over all callers. + std::map call_parameter_ids_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids()); + for (auto* instr : + fuzzerutil::GetCallers(ir_context, message_.function_id())) { + uint32_t caller_id = instr->result_id(); + + // If there is no entry for this caller, return false. + if (call_parameter_ids_map.find(caller_id) == + call_parameter_ids_map.end()) { + return false; + } + uint32_t value_id = call_parameter_ids_map[caller_id]; + + auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id); + if (!value_instr) { + return false; + } + // If the id of the value of the map is not available before the caller, + // return false. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, instr, + value_id)) { + return false; + } + + // The type of the value must be defined. + uint32_t value_type_id = fuzzerutil::GetTypeId(ir_context, value_id); + if (!value_type_id) { + return false; + } + + // Type of every value of the map must be the same for all callers. + if (message_.parameter_type_id() != value_type_id) { + return false; + } + } + return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + message_.parameter_fresh_id() != message_.function_type_fresh_id(); +} + +void TransformationAddParameter::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Find the function that will be transformed. + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); + assert(function && "Can't find the function"); + + std::map call_parameter_ids_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids()); + + uint32_t new_parameter_type_id = message_.parameter_type_id(); + auto new_parameter_type = + ir_context->get_type_mgr()->GetType(new_parameter_type_id); + assert(new_parameter_type && "New parameter has invalid type."); + + // Add new parameters to the function. + function->AddParameter(MakeUnique( + ir_context, spv::Op::OpFunctionParameter, new_parameter_type_id, + message_.parameter_fresh_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id()); + + // Fix all OpFunctionCall instructions. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + inst->AddOperand( + {SPV_OPERAND_TYPE_ID, {call_parameter_ids_map[inst->result_id()]}}); + } + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + const auto* old_function_type = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function must have a valid type"); + + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + + parameter_type_ids.push_back(new_parameter_type_id); + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + auto new_parameter_kind = new_parameter_type->kind(); + + // Make sure our changes are analyzed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // If the |new_parameter_type_id| is not a pointer type, mark id as + // irrelevant so that we can replace its use with some other id. If the + // |new_parameter_type_id| is a pointer type, we cannot mark it with + // IdIsIrrelevant, because this pointer might be replaced by a pointer from + // original shader. This would change the semantics of the module. In the case + // of a pointer type we mark it with PointeeValueIsIrrelevant. + if (new_parameter_kind != opt::analysis::Type::kPointer) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.parameter_fresh_id()); + } else { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.parameter_fresh_id()); + } +} + +protobufs::Transformation TransformationAddParameter::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_parameter() = message_; + return result; +} + +bool TransformationAddParameter::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + opt::Instruction* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + return true; + case spv::Op::OpTypeArray: + return IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(0)); + case spv::Op::OpTypeStruct: + if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) { + return false; + } + for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) { + if (!IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(i))) { + return false; + } + } + return true; + case spv::Op::OpTypePointer: { + spv::StorageClass storage_class = + static_cast(type_inst->GetSingleWordInOperand(0)); + switch (storage_class) { + case spv::StorageClass::Private: + case spv::StorageClass::Function: + case spv::StorageClass::Workgroup: { + return IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(1)); + } + default: + return false; + } + } + default: + return false; + } +} + +std::unordered_set TransformationAddParameter::GetFreshIds() const { + return {message_.parameter_fresh_id(), message_.function_type_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.h new file mode 100644 index 00000000..0bc096e7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_parameter.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddParameter : public Transformation { + public: + explicit TransformationAddParameter( + protobufs::TransformationAddParameter message); + + TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id, + uint32_t parameter_type_id, + std::map call_parameter_ids, + uint32_t function_type_fresh_id); + + // - |function_id| must be a valid result id of some non-entry-point function + // in the module. + // - |parameter_type_id| is a type id of the new parameter. The type must be + // supported by this transformation as specified by IsParameterTypeSupported + // function. + // - |call_parameter_id| must map from every id of an OpFunctionCall + // instruction of this function to the id that will be passed as the new + // parameter at that call site. There could be no callers, therefore this + // map can be empty. + // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are + // not equal. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new OpFunctionParameter instruction with result id + // |parameter_fresh_id| for the function with |function_id|. + // - Adjusts function's type to include a new parameter. + // - Adds an argument to every caller of the function to account for the added + // parameter. The argument is the value in |call_parameter_id| map. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the type of the parameter is supported by this + // transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t type_id); + + private: + protobufs::TransformationAddParameter message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp new file mode 100644 index 00000000..601546c9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + protobufs::TransformationAddRelaxedDecoration message) + : message_(std::move(message)) {} + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddRelaxedDecoration::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |message_.result_id| must be the id of an instruction. + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + // |instr| must not be decorated with RelaxedPrecision. + if (ir_context->get_decoration_mgr()->HasDecoration( + message_.result_id(), spv::Decoration::RelaxedPrecision)) { + return false; + } + opt::BasicBlock* cur_block = ir_context->get_instr_block(instr); + // The instruction must have a block. + if (cur_block == nullptr) { + return false; + } + // |cur_block| must be a dead block. + if (!(transformation_context.GetFactManager()->BlockIsDead( + cur_block->id()))) { + return false; + } + + // The instruction must be numeric. + return IsNumeric(instr->opcode()); +} + +void TransformationAddRelaxedDecoration::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add a RelaxedPrecision decoration targeting |message_.result_id|. + ir_context->get_decoration_mgr()->AddDecoration( + message_.result_id(), uint32_t(spv::Decoration::RelaxedPrecision)); +} + +protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_relaxed_decoration() = message_; + return result; +} + +bool TransformationAddRelaxedDecoration::IsNumeric(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpConvertPtrToU: + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpTranspose: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + return true; + default: + return false; + } +} + +std::unordered_set TransformationAddRelaxedDecoration::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h new file mode 100644 index 00000000..e13594e8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddRelaxedDecoration : public Transformation { + public: + explicit TransformationAddRelaxedDecoration( + protobufs::TransformationAddRelaxedDecoration message); + + explicit TransformationAddRelaxedDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an instruction, which is + // located in a dead block and Relaxed decoration can be applied. + // - It does not matter whether this instruction is already annotated with the + // Relaxed decoration. + bool IsApplicable( + + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| RelaxedPrecision' + // to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an instruction + // that operates on 32-bit integers and 32-bit floats + // as defined by the SPIR-V specification. + static bool IsNumeric(spv::Op opcode); + + private: + protobufs::TransformationAddRelaxedDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp new file mode 100644 index 00000000..685f0a4a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_spec_constant_op.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddSpecConstantOp::TransformationAddSpecConstantOp( + spvtools::fuzz::protobufs::TransformationAddSpecConstantOp message) + : message_(std::move(message)) {} + +TransformationAddSpecConstantOp::TransformationAddSpecConstantOp( + uint32_t fresh_id, uint32_t type_id, spv::Op opcode, + const opt::Instruction::OperandList& operands) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_opcode(uint32_t(opcode)); + for (const auto& operand : operands) { + auto* op = message_.add_operand(); + op->set_operand_type(operand.type); + for (auto word : operand.words) { + op->add_operand_data(word); + } + } +} + +bool TransformationAddSpecConstantOp::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto clone = fuzzerutil::CloneIRContext(ir_context); + ApplyImpl(clone.get()); + return fuzzerutil::IsValid(clone.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer); +} + +void TransformationAddSpecConstantOp::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ApplyImpl(ir_context); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +void TransformationAddSpecConstantOp::ApplyImpl( + opt::IRContext* ir_context) const { + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {message_.opcode()}}}; + + for (const auto& operand : message_.operand()) { + std::vector words(operand.operand_data().begin(), + operand.operand_data().end()); + operands.push_back({static_cast(operand.operand_type()), + std::move(words)}); + } + + ir_context->AddGlobalValue(MakeUnique( + ir_context, spv::Op::OpSpecConstantOp, message_.type_id(), + message_.fresh_id(), std::move(operands))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); +} + +protobufs::Transformation TransformationAddSpecConstantOp::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_spec_constant_op() = message_; + return result; +} + +std::unordered_set TransformationAddSpecConstantOp::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h new file mode 100644 index 00000000..665f66a0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddSpecConstantOp : public Transformation { + public: + explicit TransformationAddSpecConstantOp( + protobufs::TransformationAddSpecConstantOp message); + + TransformationAddSpecConstantOp( + uint32_t fresh_id, uint32_t type_id, spv::Op opcode, + const opt::Instruction::OperandList& operands); + + // - |fresh_id| is a fresh result id in the module. + // - |type_id| is a valid result id of some OpType* instruction in the + // module. It is also a valid type id with respect to |opcode|. + // - |opcode| is one of the opcodes supported by OpSpecConstantOp. + // - |operands| are valid with respect to |opcode| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // |%fresh_id = OpSpecConstantOp %type_id opcode operands...| is added to the + // module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + void ApplyImpl(opt::IRContext* ir_context) const; + + protobufs::TransformationAddSpecConstantOp message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.cpp new file mode 100644 index 00000000..00df9cf2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_synonym.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddSynonym::TransformationAddSynonym( + protobufs::TransformationAddSynonym message) + : message_(std::move(message)) {} + +TransformationAddSynonym::TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before) { + message_.set_result_id(result_id); + message_.set_synonym_type(synonym_type); + message_.set_synonym_fresh_id(synonym_fresh_id); + *message_.mutable_insert_before() = insert_before; +} + +bool TransformationAddSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid( + message_.synonym_type()) && + "Synonym type is invalid"); + + // |synonym_fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) { + return false; + } + + // Check that |message_.result_id| is valid. + auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!synonym) { + return false; + } + + // Check that we can apply |synonym_type| to |result_id|. + if (!IsInstructionValid(ir_context, transformation_context, synonym, + message_.synonym_type())) { + return false; + } + + // Check that |insert_before| is valid. + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + if (!insert_before_inst) { + return false; + } + + const auto* insert_before_inst_block = + ir_context->get_instr_block(insert_before_inst); + assert(insert_before_inst_block && + "|insert_before_inst| must be in some block"); + + if (transformation_context.GetFactManager()->BlockIsDead( + insert_before_inst_block->id())) { + // We don't create synonyms in dead blocks. + return false; + } + + // Check that we can insert |message._synonymous_instruction| before + // |message_.insert_before| instruction. We use OpIAdd to represent some + // instruction that can produce a synonym. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd, + insert_before_inst)) { + return false; + } + + // A constant instruction must be present in the module if required. + if (IsAdditionalConstantRequired(message_.synonym_type()) && + MaybeGetConstantId(ir_context, transformation_context) == 0) { + return false; + } + + // Domination rules must be satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, message_.result_id()); +} + +void TransformationAddSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add a synonymous instruction. + auto new_instruction = + MakeSynonymousInstruction(ir_context, *transformation_context); + auto new_instruction_ptr = new_instruction.get(); + auto insert_before = FindInstruction(message_.insert_before(), ir_context); + insert_before->InsertBefore(std::move(new_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id()); + + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + // Propagate PointeeValueIsIrrelevant fact. + const auto* new_synonym_type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id())); + assert(new_synonym_type && "New synonym should have a valid type"); + + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.result_id()) && + new_synonym_type->AsPointer()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.synonym_fresh_id()); + } + + // Mark two ids as synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.result_id(), {}), + MakeDataDescriptor(message_.synonym_fresh_id(), {})); +} + +protobufs::Transformation TransformationAddSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_synonym() = message_; + return result; +} + +bool TransformationAddSynonym::IsInstructionValid( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + // Instruction must have a result id, type id. We skip OpUndef and + // OpConstantNull. + if (!inst || !inst->result_id() || !inst->type_id() || + inst->opcode() == spv::Op::OpUndef || + inst->opcode() == spv::Op::OpConstantNull) { + return false; + } + + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *inst)) { + return false; + } + + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::MUL_ONE: { + // The instruction must be either scalar or vector of integers or floats. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + + if (const auto* vector = type->AsVector()) { + return vector->element_type()->AsInteger() || + vector->element_type()->AsFloat(); + } + + return type->AsInteger() || type->AsFloat(); + } + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: { + // The instruction must be either an integer or a vector of integers. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + + if (const auto* vector = type->AsVector()) { + return vector->element_type()->AsInteger(); + } + + return type->AsInteger(); + } + case protobufs::TransformationAddSynonym::COPY_OBJECT: + // All checks for OpCopyObject are handled by + // fuzzerutil::CanMakeSynonymOf. + return true; + case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::LOGICAL_OR: { + // The instruction must be either a scalar or a vector of booleans. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + return (type->AsVector() && type->AsVector()->element_type()->AsBool()) || + type->AsBool(); + } + default: + assert(false && "Synonym type is not supported"); + return false; + } +} + +std::unique_ptr +TransformationAddSynonym::MakeSynonymousInstruction( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + auto opcode = spv::Op::OpNop; + const auto* synonym_type = + ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + auto is_integral = (synonym_type->AsVector() && + synonym_type->AsVector()->element_type()->AsInteger()) || + synonym_type->AsInteger(); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::SUB_ZERO: + opcode = is_integral ? spv::Op::OpISub : spv::Op::OpFSub; + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + opcode = is_integral ? spv::Op::OpIMul : spv::Op::OpFMul; + break; + case protobufs::TransformationAddSynonym::ADD_ZERO: + opcode = is_integral ? spv::Op::OpIAdd : spv::Op::OpFAdd; + break; + case protobufs::TransformationAddSynonym::LOGICAL_OR: + opcode = spv::Op::OpLogicalOr; + break; + case protobufs::TransformationAddSynonym::LOGICAL_AND: + opcode = spv::Op::OpLogicalAnd; + break; + case protobufs::TransformationAddSynonym::BITWISE_OR: + opcode = spv::Op::OpBitwiseOr; + break; + case protobufs::TransformationAddSynonym::BITWISE_XOR: + opcode = spv::Op::OpBitwiseXor; + break; + + case protobufs::TransformationAddSynonym::COPY_OBJECT: + return MakeUnique( + ir_context, spv::Op::OpCopyObject, synonym_type_id, + message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}}); + + default: + assert(false && "Unhandled synonym type"); + return nullptr; + } + + return MakeUnique( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); +} + +uint32_t TransformationAddSynonym::MaybeGetConstantId( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(IsAdditionalConstantRequired(message_.synonym_type()) && + "Synonym type doesn't require an additional constant"); + + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: + return fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, synonym_type_id, false); + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + if (const auto* vector = synonym_type->AsVector()) { + auto element_type_id = + ir_context->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = + vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u; + if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, {one_word}, element_type_id, + false)) { + return fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(vector->element_count(), scalar_one_id), + synonym_type_id, false); + } + + return 0; + } else { + return fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u}, + synonym_type_id, false); + } + } + default: + // The assertion at the beginning of the function will fail in the debug + // mode. + return 0; + } +} + +bool TransformationAddSynonym::IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: + return true; + default: + return false; + } +} + +std::unordered_set TransformationAddSynonym::GetFreshIds() const { + return {message_.synonym_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.h new file mode 100644 index 00000000..33c43537 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_synonym.h @@ -0,0 +1,94 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddSynonym : public Transformation { + public: + explicit TransformationAddSynonym( + protobufs::TransformationAddSynonym message); + + TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before); + + // - |result_id| must be a valid result id of some instruction in the module. + // - |result_id| may not be an irrelevant id. + // - |synonym_type| is a type of the synonymous instruction that will be + // created. + // - |synonym_fresh_id| is a fresh id. + // - |insert_before| must be a valid instruction descriptor and we must be + // able to insert a new synonymous instruction before |insert_before|. + // - |result_id| must be available before |insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Creates a new synonymous instruction according to the |synonym_type| with + // result id |synonym_fresh_id|. + // Inserts that instruction before |insert_before| and creates a fact + // that the |synonym_fresh_id| and the |result_id| are synonymous. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can create a synonym of |inst| according to the + // |synonym_type|. + static bool IsInstructionValid( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + // Returns true if |synonym_type| requires an additional constant instruction + // to be present in the module. + static bool IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + private: + // Returns a new instruction which is synonymous to |message_.result_id|. + std::unique_ptr MakeSynonymousInstruction( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + // Returns a result id of a constant instruction that is required to be + // present in some synonym types (e.g. returns a result id of a zero constant + // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in + // the module. This method should only be called when + // IsAdditionalConstantRequired returns true. + uint32_t MaybeGetConstantId( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + protobufs::TransformationAddSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.cpp new file mode 100644 index 00000000..d00d0e43 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_array.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeArray::TransformationAddTypeArray( + protobufs::TransformationAddTypeArray message) + : message_(std::move(message)) {} + +TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id, + uint32_t element_type_id, + uint32_t size_id) { + message_.set_fresh_id(fresh_id); + message_.set_element_type_id(element_type_id); + message_.set_size_id(size_id); +} + +bool TransformationAddTypeArray::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto element_type = + ir_context->get_type_mgr()->GetType(message_.element_type_id()); + if (!element_type || element_type->AsFunction() || + fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, + message_.element_type_id())) { + // The element type id either does not refer to a type, refers to a function + // type, or refers to a block-decorated struct. These cases are all illegal. + return false; + } + auto constant = + ir_context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()}); + if (constant.empty()) { + // The size id does not refer to a constant. + return false; + } + assert(constant.size() == 1 && + "Only one constant id was provided, so only one constant should have " + "been returned"); + + auto int_constant = constant[0]->AsIntConstant(); + if (!int_constant) { + // The size constant is not an integer. + return false; + } + // We require that the size constant be a 32-bit value that is positive when + // interpreted as being signed. + return int_constant->words().size() == 1 && int_constant->GetS32() >= 1; +} + +void TransformationAddTypeArray::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}}); + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeArray, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeArray::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_array() = message_; + return result; +} + +std::unordered_set TransformationAddTypeArray::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.h new file mode 100644 index 00000000..3162497a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_array.h @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeArray : public Transformation { + public: + explicit TransformationAddTypeArray( + protobufs::TransformationAddTypeArray message); + + TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id, + uint32_t size_id); + + // - |message_.fresh_id| must be fresh + // - |message_.element_type_id| must be the id of a non-function type + // - |message_.member_type_id| must not be the result id of an OpTypeStruct + // instruction that has the Block or BufferBlock decoration + // - |message_.size_id| must be the id of a 32-bit integer constant that is + // positive when interpreted as signed + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeArray instruction to the module, with element type given by + // |message_.element_type_id| and size given by |message_.size_id|. The + // result id of the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeArray message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp new file mode 100644 index 00000000..47fc744f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_boolean.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeBoolean::TransformationAddTypeBoolean( + protobufs::TransformationAddTypeBoolean message) + : message_(std::move(message)) {} + +TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) { + message_.set_fresh_id(fresh_id); +} + +bool TransformationAddTypeBoolean::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Applicable if there is no bool type already declared in the module. + opt::analysis::Bool bool_type; + return ir_context->get_type_mgr()->GetId(&bool_type) == 0; +} + +void TransformationAddTypeBoolean::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList empty_operands; + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeBool, 0, message_.fresh_id(), empty_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_boolean() = message_; + return result; +} + +std::unordered_set TransformationAddTypeBoolean::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.h new file mode 100644 index 00000000..ee640153 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_boolean.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeBoolean : public Transformation { + public: + explicit TransformationAddTypeBoolean( + protobufs::TransformationAddTypeBoolean message); + + explicit TransformationAddTypeBoolean(uint32_t fresh_id); + + // - |message_.fresh_id| must not be used by the module. + // - The module must not yet declare OpTypeBoolean + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds OpTypeBoolean with |message_.fresh_id| as result id. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeBoolean message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.cpp new file mode 100644 index 00000000..1943ffa5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_float.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeFloat::TransformationAddTypeFloat(uint32_t fresh_id, + uint32_t width) { + message_.set_fresh_id(fresh_id); + message_.set_width(width); +} + +TransformationAddTypeFloat::TransformationAddTypeFloat( + protobufs::TransformationAddTypeFloat message) + : message_(std::move(message)) {} + +bool TransformationAddTypeFloat::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Checks float type width capabilities. + switch (message_.width()) { + case 16: + // The Float16 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Float16)) { + return false; + } + break; + case 32: + // No capabilities needed. + break; + case 64: + // The Float64 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Float64)) { + return false; + } + break; + default: + assert(false && "Unexpected float type width"); + return false; + } + + // Applicable if there is no float type with this width already declared in + // the module. + return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0; +} + +void TransformationAddTypeFloat::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeFloat, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition, and invalidate + // the type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeFloat::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_float() = message_; + return result; +} + +std::unordered_set TransformationAddTypeFloat::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.h new file mode 100644 index 00000000..e049d9a0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_float.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeFloat : public Transformation { + public: + explicit TransformationAddTypeFloat( + protobufs::TransformationAddTypeFloat message); + + TransformationAddTypeFloat(uint32_t fresh_id, uint32_t width); + + // - |message_.fresh_id| must not be used by the module + // - The module must not contain an OpTypeFloat instruction with width + // |message_.width| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeFloat instruction to the module with the given width + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeFloat message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.cpp new file mode 100644 index 00000000..e96067f2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_function.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeFunction::TransformationAddTypeFunction( + protobufs::TransformationAddTypeFunction message) + : message_(std::move(message)) {} + +TransformationAddTypeFunction::TransformationAddTypeFunction( + uint32_t fresh_id, uint32_t return_type_id, + const std::vector& argument_type_ids) { + message_.set_fresh_id(fresh_id); + message_.set_return_type_id(return_type_id); + for (auto id : argument_type_ids) { + message_.add_argument_type_id(id); + } +} + +bool TransformationAddTypeFunction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The return and argument types must be type ids but not not be function + // type ids. + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, message_.return_type_id())) { + return false; + } + for (auto argument_type_id : message_.argument_type_id()) { + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, argument_type_id)) { + return false; + } + } + // Check whether there is already an OpTypeFunction definition that uses + // exactly the same return and argument type ids. (Note that the type manager + // does not allow us to check this, as it does not distinguish between + // function types with different but isomorphic pointer argument types.) + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0; +} + +void TransformationAddTypeFunction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + + fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_function() = message_; + return result; +} + +std::unordered_set TransformationAddTypeFunction::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.h new file mode 100644 index 00000000..71044579 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_function.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeFunction : public Transformation { + public: + explicit TransformationAddTypeFunction( + protobufs::TransformationAddTypeFunction message); + + TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id, + const std::vector& argument_type_ids); + + // - |message_.fresh_id| must not be used by the module + // - |message_.return_type_id| and each element of |message_.argument_type_id| + // must be the ids of non-function types + // - The module must not contain an OpTypeFunction instruction defining a + // function type with the signature provided by the given return and + // argument types + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeFunction instruction to the module, with signature given by + // |message_.return_type_id| and |message_.argument_type_id|. The result id + // for the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.cpp new file mode 100644 index 00000000..35663f96 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_int.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeInt::TransformationAddTypeInt( + protobufs::TransformationAddTypeInt message) + : message_(std::move(message)) {} + +TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id, + uint32_t width, + bool is_signed) { + message_.set_fresh_id(fresh_id); + message_.set_width(width); + message_.set_is_signed(is_signed); +} + +bool TransformationAddTypeInt::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Checks integer type width capabilities. + switch (message_.width()) { + case 8: + // The Int8 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Int8)) { + return false; + } + break; + case 16: + // The Int16 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Int16)) { + return false; + } + break; + case 32: + // No capabilities needed. + break; + case 64: + // The Int64 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::Int64)) { + return false; + } + break; + default: + assert(false && "Unexpected integer type width"); + return false; + } + + // Applicable if there is no int type with this width and signedness already + // declared in the module. + return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(), + message_.is_signed()) == 0; +} + +void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeInt, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {message_.is_signed() ? 1u : 0u}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeInt::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_int() = message_; + return result; +} + +std::unordered_set TransformationAddTypeInt::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.h new file mode 100644 index 00000000..dc67b7dc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_int.h @@ -0,0 +1,56 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeInt : public Transformation { + public: + explicit TransformationAddTypeInt( + protobufs::TransformationAddTypeInt message); + + TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, bool is_signed); + + // - |message_.fresh_id| must not be used by the module + // - The module must not contain an OpTypeInt instruction with width + // |message_.width| and signedness |message.is_signed| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeInt instruction to the module with the given width and + // signedness. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeInt message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp new file mode 100644 index 00000000..e3f1786b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_matrix.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + protobufs::TransformationAddTypeMatrix message) + : message_(std::move(message)) {} + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) { + message_.set_fresh_id(fresh_id); + message_.set_column_type_id(column_type_id); + message_.set_column_count(column_count); +} + +bool TransformationAddTypeMatrix::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The column type must be a floating-point vector. + auto column_type = + ir_context->get_type_mgr()->GetType(message_.column_type_id()); + if (!column_type) { + return false; + } + return column_type->AsVector() && + column_type->AsVector()->element_type()->AsFloat(); +} + +void TransformationAddTypeMatrix::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}}); + in_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}}); + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeMatrix, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_matrix() = message_; + return result; +} + +std::unordered_set TransformationAddTypeMatrix::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.h new file mode 100644 index 00000000..b4788b1b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_matrix.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeMatrix : public Transformation { + public: + explicit TransformationAddTypeMatrix( + protobufs::TransformationAddTypeMatrix message); + + TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id, + uint32_t column_count); + + // - |message_.fresh_id| must be a fresh id + // - |message_.column_type_id| must be the id of a floating-point vector type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeMatrix instruction to the module, with column type + // |message_.column_type_id| and |message_.column_count| columns, with result + // id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeMatrix message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp new file mode 100644 index 00000000..c1126154 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_pointer.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypePointer::TransformationAddTypePointer( + protobufs::TransformationAddTypePointer message) + : message_(std::move(message)) {} + +TransformationAddTypePointer::TransformationAddTypePointer( + uint32_t fresh_id, spv::StorageClass storage_class, uint32_t base_type_id) { + message_.set_fresh_id(fresh_id); + message_.set_storage_class(uint32_t(storage_class)); + message_.set_base_type_id(base_type_id); +} + +bool TransformationAddTypePointer::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The base type must be known. + return ir_context->get_type_mgr()->GetType(message_.base_type_id()) != + nullptr; +} + +void TransformationAddTypePointer::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add the pointer type. + opt::Instruction::OperandList in_operands = { + {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}}, + {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}}; + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypePointer, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypePointer::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_pointer() = message_; + return result; +} + +std::unordered_set TransformationAddTypePointer::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.h new file mode 100644 index 00000000..e4ef9d88 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_pointer.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypePointer : public Transformation { + public: + explicit TransformationAddTypePointer( + protobufs::TransformationAddTypePointer message); + + TransformationAddTypePointer(uint32_t fresh_id, + spv::StorageClass storage_class, + uint32_t base_type_id); + + // - |message_.fresh_id| must not be used by the module + // - |message_.base_type_id| must be the result id of an OpType[...] + // instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypePointer instruction with the given storage class and base + // type to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypePointer message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.cpp new file mode 100644 index 00000000..95fbbbab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_struct.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeStruct::TransformationAddTypeStruct( + protobufs::TransformationAddTypeStruct message) + : message_(std::move(message)) {} + +TransformationAddTypeStruct::TransformationAddTypeStruct( + uint32_t fresh_id, const std::vector& member_type_ids) { + message_.set_fresh_id(fresh_id); + for (auto member_type_id : member_type_ids) { + message_.add_member_type_id(member_type_id); + } +} + +bool TransformationAddTypeStruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + for (auto member_type : message_.member_type_id()) { + auto type = ir_context->get_type_mgr()->GetType(member_type); + if (!type || type->AsFunction() || + fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, member_type)) { + // The member type id either does not refer to a type, refers to a + // function type, or refers to a block-decorated struct. These cases are + // all illegal. + return false; + } + + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + if (type->AsStruct() && + fuzzerutil::MembersHaveBuiltInDecoration(ir_context, member_type)) { + return false; + } + } + return true; +} + +void TransformationAddTypeStruct::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList operands; + operands.reserve(message_.member_type_id().size()); + + for (auto type_id : message_.member_type_id()) { + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + (void)type; // Make compiler happy in release mode. + assert(type && !type->AsFunction() && "Component's type id is invalid"); + + if (type->AsStruct()) { + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + assert(!fuzzerutil::MembersHaveBuiltInDecoration(ir_context, type_id) && + "A member struct has BuiltIn members"); + } + + operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); + } + + auto type_instruction = + MakeUnique(ir_context, spv::Op::OpTypeStruct, 0, + message_.fresh_id(), std::move(operands)); + auto type_instruction_ptr = type_instruction.get(); + ir_context->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeStruct::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_struct() = message_; + return result; +} + +std::unordered_set TransformationAddTypeStruct::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.h new file mode 100644 index 00000000..6a8dce7c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_struct.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeStruct : public Transformation { + public: + explicit TransformationAddTypeStruct( + protobufs::TransformationAddTypeStruct message); + + TransformationAddTypeStruct(uint32_t fresh_id, + const std::vector& component_type_ids); + + // - |message_.fresh_id| must be a fresh id + // - |message_.member_type_id| must be a sequence of non-function type ids + // - |message_.member_type_id| may not contain a result id of an OpTypeStruct + // instruction with BuiltIn members (i.e. members of the struct are + // decorated via OpMemberDecorate with BuiltIn decoration) + // - |message_.member_type_id| may not contain a result id of an OpTypeStruct + // instruction that has the Block or BufferBlock decoration + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeStruct instruction whose field types are given by + // |message_.member_type_id|, with result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.cpp new file mode 100644 index 00000000..a7b0fa7a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_vector.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeVector::TransformationAddTypeVector( + protobufs::TransformationAddTypeVector message) + : message_(std::move(message)) {} + +TransformationAddTypeVector::TransformationAddTypeVector( + uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) { + message_.set_fresh_id(fresh_id); + message_.set_component_type_id(component_type_id); + message_.set_component_count(component_count); +} + +bool TransformationAddTypeVector::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto component_type = + ir_context->get_type_mgr()->GetType(message_.component_type_id()); + if (!component_type) { + return false; + } + return component_type->AsBool() || component_type->AsFloat() || + component_type->AsInteger(); +} + +void TransformationAddTypeVector::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + const auto* component_type = + ir_context->get_type_mgr()->GetType(message_.component_type_id()); + (void)component_type; // Make compiler happy in release mode. + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(message_.component_count() >= 2 && message_.component_count() <= 4 && + "Precondition: component count must be in range [2, 4]."); + + auto type_instruction = MakeUnique( + ir_context, spv::Op::OpTypeVector, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); +} + +protobufs::Transformation TransformationAddTypeVector::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_vector() = message_; + return result; +} + +std::unordered_set TransformationAddTypeVector::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.h new file mode 100644 index 00000000..43460ce4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_add_type_vector.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeVector : public Transformation { + public: + explicit TransformationAddTypeVector( + protobufs::TransformationAddTypeVector message); + + TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id, + uint32_t component_count); + + // - |message_.fresh_id| must be a fresh id + // - |message_.component_type_id| must be the id of a scalar type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeVector instruction to the module, with component type + // |message_.component_type_id| and |message_.component_count| components, + // with result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeVector message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp new file mode 100644 index 00000000..26519389 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_adjust_branch_weights.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +const uint32_t kBranchWeightForTrueLabelIndex = 3; +const uint32_t kBranchWeightForFalseLabelIndex = 4; + +} // namespace + +TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( + protobufs::TransformationAdjustBranchWeights message) + : message_(std::move(message)) {} + +TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( + const protobufs::InstructionDescriptor& instruction_descriptor, + const std::pair& branch_weights) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.mutable_branch_weights()->set_first(branch_weights.first); + message_.mutable_branch_weights()->set_second(branch_weights.second); +} + +bool TransformationAdjustBranchWeights::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + spv::Op opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + + // Must be an OpBranchConditional instruction. + if (opcode != spv::Op::OpBranchConditional) { + return false; + } + + assert((message_.branch_weights().first() != 0 || + message_.branch_weights().second() != 0) && + "At least one weight must be non-zero."); + + assert(message_.branch_weights().first() <= + UINT32_MAX - message_.branch_weights().second() && + "The sum of the two weights must not be greater than UINT32_MAX."); + + return true; +} + +void TransformationAdjustBranchWeights::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction->HasBranchWeights()) { + instruction->SetOperand(kBranchWeightForTrueLabelIndex, + {message_.branch_weights().first()}); + instruction->SetOperand(kBranchWeightForFalseLabelIndex, + {message_.branch_weights().second()}); + } else { + instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + {message_.branch_weights().first()}}); + instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + {message_.branch_weights().second()}}); + } +} + +protobufs::Transformation TransformationAdjustBranchWeights::ToMessage() const { + protobufs::Transformation result; + *result.mutable_adjust_branch_weights() = message_; + return result; +} + +std::unordered_set TransformationAdjustBranchWeights::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h new file mode 100644 index 00000000..41eceeb5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAdjustBranchWeights : public Transformation { + public: + explicit TransformationAdjustBranchWeights( + protobufs::TransformationAdjustBranchWeights message); + + TransformationAdjustBranchWeights( + const protobufs::InstructionDescriptor& instruction_descriptor, + const std::pair& branch_weights); + + // - |message_.instruction_descriptor| must identify an existing + // branch conditional instruction + // - At least one of |branch_weights| must be non-zero and + // the two weights must not overflow a 32-bit unsigned integer when added + // together + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adjust the branch weights of a branch conditional instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAdjustBranchWeights message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.cpp new file mode 100644 index 00000000..075b33d4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_construct.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeConstruct::TransformationCompositeConstruct( + protobufs::TransformationCompositeConstruct message) + : message_(std::move(message)) {} + +TransformationCompositeConstruct::TransformationCompositeConstruct( + uint32_t composite_type_id, std::vector component, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id) { + message_.set_composite_type_id(composite_type_id); + for (auto a_component : component) { + message_.add_component(a_component); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); +} + +bool TransformationCompositeConstruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + // We require the id for the composite constructor to be unused. + return false; + } + + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + // The instruction before which the composite should be inserted was not + // found. + return false; + } + + auto composite_type = + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); + + if (!fuzzerutil::IsCompositeType(composite_type)) { + // The type must actually be a composite. + return false; + } + + // If the type is an array, matrix, struct or vector, the components need to + // be suitable for constructing something of that type. + if (composite_type->AsArray() && + !ComponentsForArrayConstructionAreOK(ir_context, + *composite_type->AsArray())) { + return false; + } + if (composite_type->AsMatrix() && + !ComponentsForMatrixConstructionAreOK(ir_context, + *composite_type->AsMatrix())) { + return false; + } + if (composite_type->AsStruct() && + !ComponentsForStructConstructionAreOK(ir_context, + *composite_type->AsStruct())) { + return false; + } + if (composite_type->AsVector() && + !ComponentsForVectorConstructionAreOK(ir_context, + *composite_type->AsVector())) { + return false; + } + + // Now check whether every component being used to initialize the composite is + // available at the desired program point. + for (auto component : message_.component()) { + auto* inst = ir_context->get_def_use_mgr()->GetDef(component); + if (!inst) { + return false; + } + + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + component)) { + return false; + } + } + + return true; +} + +void TransformationCompositeConstruct::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Use the base and offset information from the transformation to determine + // where in the module a new instruction should be inserted. + auto insert_before_inst = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto destination_block = ir_context->get_instr_block(insert_before_inst); + auto insert_before = fuzzerutil::GetIteratorForInstruction( + destination_block, insert_before_inst); + + // Prepare the input operands for an OpCompositeConstruct instruction. + opt::Instruction::OperandList in_operands; + for (auto& component_id : message_.component()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}}); + } + + // Insert an OpCompositeConstruct instruction. + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, message_.composite_type_id(), + message_.fresh_id(), in_operands); + auto new_instruction_ptr = new_instruction.get(); + insert_before.InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. + + AddDataSynonymFacts(ir_context, transformation_context); +} + +bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK( + opt::IRContext* ir_context, const opt::analysis::Array& array_type) const { + if (array_type.length_info().words[0] != + opt::analysis::Array::LengthInfo::kConstant) { + // We only handle constant-sized arrays. + return false; + } + if (array_type.length_info().words.size() != 2) { + // We only handle the case where the array size can be captured in a single + // word. + return false; + } + // Get the array size. + auto array_size = array_type.length_info().words[1]; + if (static_cast(message_.component().size()) != array_size) { + // The number of components must match the array size. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the array's element type. + for (auto component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != array_type.element_type()) { + // The component's type does not match the array's element type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const { + if (static_cast(message_.component().size()) != + matrix_type.element_count()) { + // The number of components must match the number of columns of the matrix. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the matrix's column type. + for (auto component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != matrix_type.element_type()) { + // The component's type does not match the matrix's column type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const { + if (static_cast(message_.component().size()) != + struct_type.element_types().size()) { + // The number of components must match the number of fields of the struct. + return false; + } + // Check that each component is the result id of an instruction those type + // matches the associated field type. + for (uint32_t field_index = 0; + field_index < struct_type.element_types().size(); field_index++) { + auto inst = ir_context->get_def_use_mgr()->GetDef( + message_.component()[field_index]); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != struct_type.element_types()[field_index]) { + // The component's type does not match the corresponding field type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Vector& vector_type) const { + uint32_t base_element_count = 0; + auto element_type = vector_type.element_type(); + for (auto& component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type == element_type) { + base_element_count++; + } else if (component_type->AsVector() && + component_type->AsVector()->element_type() == element_type) { + base_element_count += component_type->AsVector()->element_count(); + } else { + // The component was not appropriate; e.g. no type corresponding to the + // given id was found, or the type that was found was not compatible + // with the vector being constructed. + return false; + } + } + // The number of components provided (when vector components are flattened + // out) needs to match the length of the vector being constructed. + return base_element_count == vector_type.element_count(); +} + +protobufs::Transformation TransformationCompositeConstruct::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_construct() = message_; + return result; +} + +std::unordered_set TransformationCompositeConstruct::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeConstruct::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the result id of the composite we are constructing is irrelevant (e.g. + // because it is in a dead block) then we do not make any synonyms. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Inform the fact manager that we now have new synonyms: every component of + // the composite is synonymous with the id used to construct that component + // (so long as it is legitimate to create a synonym from that id), except in + // the case of a vector where a single vector id can span multiple components. + auto composite_type = + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); + uint32_t index = 0; + for (auto component : message_.component()) { + auto component_type = ir_context->get_type_mgr()->GetType( + ir_context->get_def_use_mgr()->GetDef(component)->type_id()); + // Whether the component is a vector being packed into a vector determines + // how we should keep track of the indices associated with components. + const bool packing_vector_into_vector = + composite_type->AsVector() && component_type->AsVector(); + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef(component))) { + // We can't make a synonym of this component, so we skip on to the next + // component. In the case where we're packing a vector into a vector we + // have to skip as many components of the resulting vectors as there are + // elements of the component vector. + index += packing_vector_into_vector + ? component_type->AsVector()->element_count() + : 1; + continue; + } + if (packing_vector_into_vector) { + // The case where the composite being constructed is a vector and the + // component provided for construction is also a vector is special. It + // requires adding a synonym fact relating each element of the sub-vector + // to the corresponding element of the composite being constructed. + assert(component_type->AsVector()->element_type() == + composite_type->AsVector()->element_type()); + assert(component_type->AsVector()->element_count() < + composite_type->AsVector()->element_count()); + for (uint32_t subvector_index = 0; + subvector_index < component_type->AsVector()->element_count(); + subvector_index++) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {subvector_index}), + MakeDataDescriptor(message_.fresh_id(), {index})); + index++; + } + } else { + // The other cases are simple: the component is made directly synonymous + // with the element of the composite being constructed. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {}), + MakeDataDescriptor(message_.fresh_id(), {index})); + index++; + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.h new file mode 100644 index 00000000..cc44a612 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_construct.h @@ -0,0 +1,104 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeConstruct : public Transformation { + public: + explicit TransformationCompositeConstruct( + protobufs::TransformationCompositeConstruct message); + + TransformationCompositeConstruct( + uint32_t composite_type_id, std::vector component, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id); + + // - |message_.fresh_id| must not be used by the module. + // - |message_.composite_type_id| must be the id of a composite type + // - The elements of |message_.component| must be result ids that are + // suitable for constructing an element of the given composite type, in + // order + // - The elements of |message_.component| must not be the target of any + // decorations. + // - |message_.base_instruction_id| must be the result id of an instruction + // 'base' in some block 'blk'. + // - 'blk' must contain an instruction 'inst' located |message_.offset| + // instructions after 'base' (if |message_.offset| = 0 then 'inst' = + // 'base'). + // - It must be legal to insert an OpCompositeConstruct instruction directly + // before 'inst'. + // - Each element of |message_.component| must be available directly before + // 'inst'. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts a new OpCompositeConstruct instruction, with id + // |message_.fresh_id|, directly before the instruction identified by + // |message_.base_instruction_id| and |message_.offset|. The instruction + // creates a composite of type |message_.composite_type_id| using the ids of + // |message_.component|. + // + // Synonym facts are added between the elements of the resulting composite + // and the components used to construct it, as long as the associated ids + // support synonym creation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper to decide whether the components of the transformation are suitable + // for constructing an array of the given type. + bool ComponentsForArrayConstructionAreOK( + opt::IRContext* ir_context, const opt::analysis::Array& array_type) const; + + // Similar, but for matrices. + bool ComponentsForMatrixConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const; + + // Similar, but for structs. + bool ComponentsForStructConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const; + + // Similar, but for vectors. + bool ComponentsForVectorConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Vector& vector_type) const; + + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeConstruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.cpp new file mode 100644 index 00000000..7118432f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_extract.h" + +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeExtract::TransformationCompositeExtract( + protobufs::TransformationCompositeExtract message) + : message_(std::move(message)) {} + +TransformationCompositeExtract::TransformationCompositeExtract( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, + const std::vector& index) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_composite_id(composite_id); + for (auto an_index : index) { + message_.add_index(an_index); + } +} + +bool TransformationCompositeExtract::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + if (!composite_instruction) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.composite_id())) { + return false; + } + + auto composite_type = + ir_context->get_type_mgr()->GetType(composite_instruction->type_id()); + if (!fuzzerutil::IsCompositeType(composite_type)) { + return false; + } + + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeExtract, instruction_to_insert_before)) { + return false; + } + + return fuzzerutil::WalkCompositeTypeIndices(ir_context, + composite_instruction->type_id(), + message_.index()) != 0; +} + +void TransformationCompositeExtract::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction::OperandList extract_operands; + extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}}); + for (auto an_index : message_.index()) { + extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}}); + } + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + auto extracted_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context, composite_instruction->type_id(), message_.index()); + + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, extracted_type, + message_.fresh_id(), extract_operands)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. + + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationCompositeExtract::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_extract() = message_; + return result; +} + +std::unordered_set TransformationCompositeExtract::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeExtract::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Don't add synonyms if the composite being extracted from is not suitable, + // or if the result id into which we are extracting is irrelevant. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || + transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Add the fact that the id storing the extracted element is synonymous with + // the index into the structure. + std::vector indices(message_.index().begin(), + message_.index().end()); + auto data_descriptor_for_extracted_element = + MakeDataDescriptor(message_.composite_id(), indices); + auto data_descriptor_for_result_id = + MakeDataDescriptor(message_.fresh_id(), {}); + transformation_context->GetFactManager()->AddFactDataSynonym( + data_descriptor_for_extracted_element, data_descriptor_for_result_id); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.h new file mode 100644 index 00000000..0682a61c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_extract.h @@ -0,0 +1,76 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeExtract : public Transformation { + public: + explicit TransformationCompositeExtract( + protobufs::TransformationCompositeExtract message); + + TransformationCompositeExtract( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, + const std::vector& index); + + // - |message_.fresh_id| must be available + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to place an OpCompositeExtract + // - |message_.composite_id| must be the id of an instruction that defines + // a composite object, and this id must be available at the instruction + // identified by |message_.instruction_to_insert_before| + // - |message_.index| must be a suitable set of indices for + // |message_.composite_id|, i.e. it must be possible to follow this chain + // of indices to reach a sub-object of |message_.composite_id| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpCompositeConstruct instruction before the instruction identified + // by |message_.instruction_to_insert_before|, that extracts from + // |message_.composite_id| via indices |message_.index| into + // |message_.fresh_id|. + // + // Adds a synonym fact associating |message_.fresh_id| with the relevant + // element of |message_.composite_id|, as long as these ids support synonym + // creation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeExtract message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.cpp new file mode 100644 index 00000000..2f69c959 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.cpp @@ -0,0 +1,246 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_composite_insert.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeInsert::TransformationCompositeInsert( + protobufs::TransformationCompositeInsert message) + : message_(std::move(message)) {} + +TransformationCompositeInsert::TransformationCompositeInsert( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, uint32_t object_id, + const std::vector& index) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_composite_id(composite_id); + message_.set_object_id(object_id); + for (auto an_index : index) { + message_.add_index(an_index); + } +} + +bool TransformationCompositeInsert::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // |message_.composite_id| must refer to an existing composite value. + auto composite = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + + if (!IsCompositeInstructionSupported(ir_context, composite)) { + return false; + } + + // The indices in |message_.index| must be suitable for indexing into + // |composite->type_id()|. + auto component_to_be_replaced_type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context, composite->type_id(), message_.index()); + if (component_to_be_replaced_type_id == 0) { + return false; + } + + // The instruction having the id of |message_.object_id| must be defined. + auto object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.object_id()); + if (object_instruction == nullptr || object_instruction->type_id() == 0) { + return false; + } + + // We ignore pointers for now. + auto object_instruction_type = + ir_context->get_type_mgr()->GetType(object_instruction->type_id()); + if (object_instruction_type->AsPointer() != nullptr) { + return false; + } + + // The type id of the object having |message_.object_id| and the type id of + // the component of the composite at index |message_.index| must be the same. + if (component_to_be_replaced_type_id != object_instruction->type_id()) { + return false; + } + + // |message_.instruction_to_insert_before| must be a defined instruction. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (instruction_to_insert_before == nullptr) { + return false; + } + + // |message_.composite_id| and |message_.object_id| must be available before + // the |message_.instruction_to_insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.composite_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.object_id())) { + return false; + } + + // It must be possible to insert an OpCompositeInsert before this + // instruction. + return fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeInsert, instruction_to_insert_before); +} + +void TransformationCompositeInsert::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // |message_.struct_fresh_id| must be fresh. + assert(fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && + "|message_.fresh_id| must be fresh"); + + std::vector index = + fuzzerutil::RepeatedFieldToVector(message_.index()); + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.object_id()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}}); + for (auto i : index) { + in_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}); + } + auto composite_type_id = + fuzzerutil::GetTypeId(ir_context, message_.composite_id()); + + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpCompositeInsert, composite_type_id, + message_.fresh_id(), std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + // Add data synonym facts that arise from the insertion. + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationCompositeInsert::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_insert() = message_; + return result; +} + +bool TransformationCompositeInsert::IsCompositeInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* instruction) { + if (instruction == nullptr) { + return false; + } + if (instruction->result_id() == 0 || instruction->type_id() == 0) { + return false; + } + auto composite_type = + ir_context->get_type_mgr()->GetType(instruction->type_id()); + if (!fuzzerutil::IsCompositeType(composite_type)) { + return false; + } + + // Empty composites are not supported. + auto instruction_type_inst = + ir_context->get_def_use_mgr()->GetDef(instruction->type_id()); + if (fuzzerutil::GetBoundForCompositeIndex(*instruction_type_inst, + ir_context) == 0) { + return false; + } + return true; +} + +std::unordered_set TransformationCompositeInsert::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeInsert::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the result id arising from the insertion is irrelevant then do not add + // any data synonym facts. (The result id can be irrelevant if the insertion + // occurs in a dead block.) + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // So long as the |message_.composite_id| is suitable for participating in + // synonyms, every every element of the insertion result except for at the + // index being inserted into is synonymous with the corresponding element of + // |message_.composite_id|. In that case, for every index that is a prefix of + // |index|, the components different from the one that contains the inserted + // object are synonymous with corresponding elements in the original + // composite. + uint32_t current_node_type_id = + fuzzerutil::GetTypeId(ir_context, message_.composite_id()); + std::vector current_index; + + std::vector index = + fuzzerutil::RepeatedFieldToVector(message_.index()); + + for (uint32_t current_level : index) { + auto current_node_type_inst = + ir_context->get_def_use_mgr()->GetDef(current_node_type_id); + uint32_t index_to_skip = current_level; + uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex( + *current_node_type_inst, ir_context); + + // Update the current_node_type_id. + current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, current_node_type_id, index_to_skip); + + for (uint32_t i = 0; i < num_of_components; i++) { + if (i == index_to_skip) { + continue; + } + current_index.push_back(i); + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef( + message_.composite_id()))) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), current_index), + MakeDataDescriptor(message_.composite_id(), current_index)); + } + current_index.pop_back(); + } + // Store the prefix of the |index|. + current_index.push_back(current_level); + } + // If the object being inserted supports synonym creation then it is + // synonymous with the result of the insert instruction at the given index. + if (fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.object_id(), {}), + MakeDataDescriptor(message_.fresh_id(), index)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.h new file mode 100644 index 00000000..413d41f1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_composite_insert.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeInsert : public Transformation { + public: + explicit TransformationCompositeInsert( + protobufs::TransformationCompositeInsert message); + + TransformationCompositeInsert( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, uint32_t object_id, + const std::vector& index); + + // - |message_.fresh_id| must be fresh. + // - |message_.composite_id| must refer to an existing composite value. + // - |message_.index| must refer to a correct index in the composite. + // - The type id of the object and the type id of the component of the + // composite at index |message_.index| must be the same. + // - |message_.instruction_to_insert_before| must refer to a defined + // instruction. + // - It must be possible to insert OpCompositeInsert before + // |instruction_to_insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction OpCompositeInsert before + // |instruction_to_insert_before|, which creates a new composite from + // |composite_id| by inserting |object_id| at the specified |index|. + // Synonyms are created between those components which are identical in the + // original and the modified composite and between the inserted object and its + // copy in the modified composite. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks if |instruction| is a instruction of a composite type supported by + // this transformation. + static bool IsCompositeInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeInsert message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp new file mode 100644 index 00000000..7ae9df82 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" + +namespace spvtools { +namespace fuzz { + +TransformationComputeDataSynonymFactClosure:: + TransformationComputeDataSynonymFactClosure( + protobufs::TransformationComputeDataSynonymFactClosure message) + : message_(std::move(message)) {} + +TransformationComputeDataSynonymFactClosure:: + TransformationComputeDataSynonymFactClosure( + uint32_t maximum_equivalence_class_size) { + message_.set_maximum_equivalence_class_size(maximum_equivalence_class_size); +} + +bool TransformationComputeDataSynonymFactClosure::IsApplicable( + opt::IRContext* /*unused*/, const TransformationContext& /*unused*/) const { + return true; +} + +void TransformationComputeDataSynonymFactClosure::Apply( + opt::IRContext* /*unused*/, + TransformationContext* transformation_context) const { + transformation_context->GetFactManager()->ComputeClosureOfFacts( + message_.maximum_equivalence_class_size()); +} + +protobufs::Transformation +TransformationComputeDataSynonymFactClosure::ToMessage() const { + protobufs::Transformation result; + *result.mutable_compute_data_synonym_fact_closure() = message_; + return result; +} + +std::unordered_set +TransformationComputeDataSynonymFactClosure::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h new file mode 100644 index 00000000..c61b26ec --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationComputeDataSynonymFactClosure : public Transformation { + public: + explicit TransformationComputeDataSynonymFactClosure( + protobufs::TransformationComputeDataSynonymFactClosure message); + + explicit TransformationComputeDataSynonymFactClosure( + uint32_t maximum_equivalence_class_size); + + // This transformation is trivially applicable. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Forces the fact manager to compute a closure of data synonym facts, so that + // facts implied by existing facts are deduced. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationComputeDataSynonymFactClosure message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.cpp new file mode 100644 index 00000000..d008ba91 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_context.h" + +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// An overflow id source that should never be used: its methods assert false. +// This is the right id source for use during fuzzing, when overflow ids should +// never be required. +class NullOverflowIdSource : public OverflowIdSource { + bool HasOverflowIds() const override { + assert(false && "Bad attempt to query whether overflow ids are available."); + return false; + } + + uint32_t GetNextOverflowId() override { + assert(false && "Bad attempt to request an overflow id."); + return 0; + } + + const std::unordered_set& GetIssuedOverflowIds() const override { + assert(false && "Operation not supported."); + return placeholder_; + } + + private: + std::unordered_set placeholder_; +}; + +} // namespace + +TransformationContext::TransformationContext( + std::unique_ptr fact_manager, + spv_validator_options validator_options) + : fact_manager_(std::move(fact_manager)), + validator_options_(validator_options), + overflow_id_source_(MakeUnique()) {} + +TransformationContext::TransformationContext( + std::unique_ptr fact_manager, + spv_validator_options validator_options, + std::unique_ptr overflow_id_source) + : fact_manager_(std::move(fact_manager)), + validator_options_(validator_options), + overflow_id_source_(std::move(overflow_id_source)) {} + +TransformationContext::~TransformationContext() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.h new file mode 100644 index 00000000..3edcb636 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_context.h @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ + +#include + +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/overflow_id_source.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all information that is required to inform how to apply a +// transformation to a module. +class TransformationContext { + public: + // Constructs a transformation context with a given fact manager and validator + // options. Overflow ids are not available from a transformation context + // constructed in this way. + TransformationContext(std::unique_ptr, + spv_validator_options validator_options); + + // Constructs a transformation context with a given fact manager, validator + // options and overflow id source. + TransformationContext(std::unique_ptr, + spv_validator_options validator_options, + std::unique_ptr overflow_id_source); + + ~TransformationContext(); + + FactManager* GetFactManager() { return fact_manager_.get(); } + + const FactManager* GetFactManager() const { return fact_manager_.get(); } + + OverflowIdSource* GetOverflowIdSource() { return overflow_id_source_.get(); } + + const OverflowIdSource* GetOverflowIdSource() const { + return overflow_id_source_.get(); + } + + spv_validator_options GetValidatorOptions() const { + return validator_options_; + } + + private: + // Manages facts that inform whether transformations can be applied, and that + // are produced by applying transformations. + std::unique_ptr fact_manager_; + + // Options to control validation when deciding whether transformations can be + // applied. + spv_validator_options validator_options_; + + std::unique_ptr overflow_id_source_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp new file mode 100644 index 00000000..9176bf75 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -0,0 +1,721 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_duplicate_region_with_selection.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationDuplicateRegionWithSelection:: + TransformationDuplicateRegionWithSelection( + protobufs::TransformationDuplicateRegionWithSelection message) + : message_(std::move(message)) {} + +TransformationDuplicateRegionWithSelection:: + TransformationDuplicateRegionWithSelection( + uint32_t new_entry_fresh_id, uint32_t condition_id, + uint32_t merge_label_fresh_id, uint32_t entry_block_id, + uint32_t exit_block_id, + const std::map& original_label_to_duplicate_label, + const std::map& original_id_to_duplicate_id, + const std::map& original_id_to_phi_id) { + message_.set_new_entry_fresh_id(new_entry_fresh_id); + message_.set_condition_id(condition_id); + message_.set_merge_label_fresh_id(merge_label_fresh_id); + message_.set_entry_block_id(entry_block_id); + message_.set_exit_block_id(exit_block_id); + *message_.mutable_original_label_to_duplicate_label() = + fuzzerutil::MapToRepeatedUInt32Pair(original_label_to_duplicate_label); + *message_.mutable_original_id_to_duplicate_id() = + fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_duplicate_id); + *message_.mutable_original_id_to_phi_id() = + fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_phi_id); +} + +bool TransformationDuplicateRegionWithSelection::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Instruction with the id |condition_id| must exist and must be of a bool + // type. + auto bool_instr = + ir_context->get_def_use_mgr()->GetDef(message_.condition_id()); + if (bool_instr == nullptr || !bool_instr->type_id()) { + return false; + } + if (!ir_context->get_type_mgr()->GetType(bool_instr->type_id())->AsBool()) { + return false; + } + + // The |new_entry_fresh_id| must be fresh and distinct. + std::set ids_used_by_this_transformation; + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_entry_fresh_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + // The |merge_label_fresh_id| must be fresh and distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.merge_label_fresh_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + // The entry and exit block ids must refer to blocks. + for (auto block_id : {message_.entry_block_id(), message_.exit_block_id()}) { + auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!block_label || block_label->opcode() != spv::Op::OpLabel) { + return false; + } + } + auto entry_block = ir_context->cfg()->block(message_.entry_block_id()); + auto exit_block = ir_context->cfg()->block(message_.exit_block_id()); + + // The |entry_block| and the |exit_block| must be in the same function. + if (entry_block->GetParent() != exit_block->GetParent()) { + return false; + } + + // The |entry_block| must dominate the |exit_block|. + auto dominator_analysis = + ir_context->GetDominatorAnalysis(entry_block->GetParent()); + if (!dominator_analysis->Dominates(entry_block, exit_block)) { + return false; + } + + // The |exit_block| must post-dominate the |entry_block|. + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(entry_block->GetParent()); + if (!postdominator_analysis->Dominates(exit_block, entry_block)) { + return false; + } + + auto enclosing_function = entry_block->GetParent(); + + // |entry_block| cannot be the first block of the |enclosing_function|. + if (&*enclosing_function->begin() == entry_block) { + return false; + } + + // To make the process of resolving OpPhi instructions easier, we require that + // the entry block has only one predecessor. + auto entry_block_preds = ir_context->cfg()->preds(entry_block->id()); + std::sort(entry_block_preds.begin(), entry_block_preds.end()); + entry_block_preds.erase( + std::unique(entry_block_preds.begin(), entry_block_preds.end()), + entry_block_preds.end()); + if (entry_block_preds.size() > 1) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785): + // The following code has been copied from TransformationOutlineFunction. + // Consider refactoring to avoid duplication. + auto region_set = GetRegionBlocks(ir_context, entry_block, exit_block); + + // Check whether |region_set| really is a single-entry single-exit region, and + // also check whether structured control flow constructs and their merge + // and continue constructs are either wholly in or wholly out of the region - + // e.g. avoid the situation where the region contains the head of a loop but + // not the loop's continue construct. + // + // This is achieved by going through every block in the |enclosing_function| + for (auto& block : *enclosing_function) { + if (&block == exit_block) { + // It is not OK for the exit block to head a loop construct or a + // conditional construct. + if (block.GetMergeInst()) { + return false; + } + continue; + } + if (region_set.count(&block) != 0) { + // The block is in the region and is not the region's exit block. Let's + // see whether all of the block's successors are in the region. If they + // are not, the region is not single-entry single-exit. + bool all_successors_in_region = true; + block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context, + ®ion_set](uint32_t successor) -> bool { + if (region_set.count(ir_context->cfg()->block(successor)) == 0) { + all_successors_in_region = false; + return false; + } + return true; + }); + if (!all_successors_in_region) { + return false; + } + } + + if (auto merge = block.GetMergeInst()) { + // The block is a loop or selection header. The header and its + // associated merge block must be both in the region or both be + // outside the region. + auto merge_block = + ir_context->cfg()->block(merge->GetSingleWordOperand(0)); + if (region_set.count(&block) != region_set.count(merge_block)) { + return false; + } + } + + if (auto loop_merge = block.GetLoopMergeInst()) { + // The continue target of a loop must be within the region if and only if + // the header of the loop is. + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + // The continue target is a single-entry, single-exit region. Therefore, + // if the continue target is the exit block, the region might not contain + // the loop header. However, we would like to exclude this situation, + // since it would be impossible for the modified exit block to branch to + // the new selection merge block. In this scenario the exit block is + // required to branch to the loop header. + if (region_set.count(&block) != region_set.count(continue_target)) { + return false; + } + } + } + + // Get the maps from the protobuf. + std::map original_label_to_duplicate_label = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_label_to_duplicate_label()); + + std::map original_id_to_duplicate_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_id_to_duplicate_id()); + + std::map original_id_to_phi_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); + + for (auto block : region_set) { + // The label of every block in the region must be present in the map + // |original_label_to_duplicate_label|, unless overflow ids are present. + if (original_label_to_duplicate_label.count(block->id()) == 0) { + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } else { + auto duplicate_label = original_label_to_duplicate_label.at(block->id()); + // Each id assigned to labels in the region must be distinct and fresh. + if (!duplicate_label || + !CheckIdIsFreshAndNotUsedByThisTransformation( + duplicate_label, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + for (auto& instr : *block) { + if (!instr.HasResultId()) { + continue; + } + // Every instruction with a result id in the region must be present in the + // map |original_id_to_duplicate_id|, unless overflow ids are present. + if (original_id_to_duplicate_id.count(instr.result_id()) == 0) { + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } else { + auto duplicate_id = original_id_to_duplicate_id.at(instr.result_id()); + // Id assigned to this result id in the region must be distinct and + // fresh. + if (!duplicate_id || + !CheckIdIsFreshAndNotUsedByThisTransformation( + duplicate_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + // If the instruction is available at the end of the region then we would + // like to be able to add an OpPhi instruction at the merge point of the + // duplicated region to capture the values computed by both duplicates of + // the instruction, so that this is also available after the region. We + // do this not just for instructions that are already used after the + // region, but for all instructions so that the phi is available to future + // transformations. + if (AvailableAfterRegion(instr, exit_block, ir_context)) { + if (!ValidOpPhiArgument(instr, ir_context)) { + // The instruction cannot be used as an OpPhi argument. This is a + // blocker if there are uses of the instruction after the region. + // Otherwise we can simply avoid generating an OpPhi for this + // instruction and its duplicate. + if (!ir_context->get_def_use_mgr()->WhileEachUser( + &instr, + [ir_context, + ®ion_set](opt::Instruction* use_instr) -> bool { + opt::BasicBlock* use_block = + ir_context->get_instr_block(use_instr); + return use_block == nullptr || + region_set.count(use_block) > 0; + })) { + return false; + } + } else { + // Every instruction with a result id available at the end of the + // region must be present in the map |original_id_to_phi_id|, unless + // overflow ids are present. + if (original_id_to_phi_id.count(instr.result_id()) == 0) { + if (!transformation_context.GetOverflowIdSource() + ->HasOverflowIds()) { + return false; + } + } else { + auto phi_id = original_id_to_phi_id.at(instr.result_id()); + // Id assigned to this result id in the region must be distinct and + // fresh. + if (!phi_id || + !CheckIdIsFreshAndNotUsedByThisTransformation( + phi_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + } + } + } + } + return true; +} + +void TransformationDuplicateRegionWithSelection::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_entry_fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.merge_label_fresh_id()); + + // Create the new entry block containing the main conditional instruction. Set + // its parent to the parent of the original entry block, since it is located + // in the same function. + std::unique_ptr new_entry_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.new_entry_fresh_id(), + opt::Instruction::OperandList())); + auto entry_block = ir_context->cfg()->block(message_.entry_block_id()); + auto enclosing_function = entry_block->GetParent(); + auto exit_block = ir_context->cfg()->block(message_.exit_block_id()); + + // Get the blocks contained in the region. + std::set region_blocks = + GetRegionBlocks(ir_context, entry_block, exit_block); + + // Construct the merge block. + std::unique_ptr merge_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.merge_label_fresh_id(), + opt::Instruction::OperandList())); + + // Get the maps from the protobuf. + std::map original_label_to_duplicate_label = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_label_to_duplicate_label()); + + std::map original_id_to_duplicate_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_id_to_duplicate_id()); + + std::map original_id_to_phi_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); + + // Use overflow ids to fill in any required ids that are missing from these + // maps. + for (auto block : region_blocks) { + if (original_label_to_duplicate_label.count(block->id()) == 0) { + original_label_to_duplicate_label.insert( + {block->id(), + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + for (auto& instr : *block) { + if (!instr.HasResultId()) { + continue; + } + if (original_id_to_duplicate_id.count(instr.result_id()) == 0) { + original_id_to_duplicate_id.insert( + {instr.result_id(), transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + if (AvailableAfterRegion(instr, exit_block, ir_context) && + ValidOpPhiArgument(instr, ir_context)) { + if (original_id_to_phi_id.count(instr.result_id()) == 0) { + original_id_to_phi_id.insert( + {instr.result_id(), transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + } + } + } + + // Before adding duplicate blocks, we need to update the OpPhi instructions in + // the successors of the |exit_block|. We know that the execution of the + // transformed region will end in |merge_block|. Hence, we need to change all + // occurrences of the label id of the |exit_block| to the label id of the + // |merge_block|. + exit_block->ForEachSuccessorLabel([this, ir_context](uint32_t label_id) { + auto block = ir_context->cfg()->block(label_id); + for (auto& instr : *block) { + if (instr.opcode() == spv::Op::OpPhi) { + instr.ForEachId([this](uint32_t* id) { + if (*id == message_.exit_block_id()) { + *id = message_.merge_label_fresh_id(); + } + }); + } + } + }); + + // Get vector of predecessors id of |entry_block|. Remove any duplicate + // values. + auto entry_block_preds = ir_context->cfg()->preds(entry_block->id()); + std::sort(entry_block_preds.begin(), entry_block_preds.end()); + entry_block_preds.erase( + unique(entry_block_preds.begin(), entry_block_preds.end()), + entry_block_preds.end()); + // We know that |entry_block| has only one predecessor, since the region is + // single-entry, single-exit and its constructs and their merge blocks must be + // either wholly within or wholly outside of the region. + assert(entry_block_preds.size() == 1 && + "The entry of the region to be duplicated can have only one " + "predecessor."); + uint32_t entry_block_pred_id = + ir_context->get_instr_block(entry_block_preds[0])->id(); + // Update all the OpPhi instructions in the |entry_block|. Change every + // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we + // will insert |new_entry| before |entry_block|. + for (auto& instr : *entry_block) { + if (instr.opcode() == spv::Op::OpPhi) { + instr.ForEachId([this, entry_block_pred_id](uint32_t* id) { + if (*id == entry_block_pred_id) { + *id = message_.new_entry_fresh_id(); + } + }); + } + } + + // Duplication of blocks will invalidate iterators. Store all the blocks from + // the enclosing function. + std::vector blocks; + for (auto& block : *enclosing_function) { + blocks.push_back(&block); + } + + opt::BasicBlock* previous_block = nullptr; + opt::BasicBlock* duplicated_exit_block = nullptr; + // Iterate over all blocks of the function to duplicate blocks of the original + // region and their instructions. + for (auto& block : blocks) { + // The block must be contained in the region. + if (region_blocks.count(block) == 0) { + continue; + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, original_label_to_duplicate_label.at(block->id())); + + std::unique_ptr duplicated_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, + original_label_to_duplicate_label.at(block->id()), + opt::Instruction::OperandList())); + + for (auto& instr : *block) { + // Case where an instruction is the terminator of the exit block is + // handled separately. + if (block == exit_block && instr.IsBlockTerminator()) { + switch (instr.opcode()) { + case spv::Op::OpBranch: + case spv::Op::OpBranchConditional: + case spv::Op::OpReturn: + case spv::Op::OpReturnValue: + case spv::Op::OpUnreachable: + case spv::Op::OpKill: + continue; + default: + assert(false && + "Unexpected terminator for |exit_block| of the region."); + } + } + // Duplicate the instruction. + auto cloned_instr = instr.Clone(ir_context); + duplicated_block->AddInstruction( + std::unique_ptr(cloned_instr)); + + if (instr.HasResultId()) { + fuzzerutil::UpdateModuleIdBound( + ir_context, original_id_to_duplicate_id.at(instr.result_id())); + } + + // If an id from the original region was used in this instruction, + // replace it with the value from |original_id_to_duplicate_id|. + // If a label from the original region was used in this instruction, + // replace it with the value from |original_label_to_duplicate_label|. + cloned_instr->ForEachId( + [original_id_to_duplicate_id, + original_label_to_duplicate_label](uint32_t* op) { + if (original_id_to_duplicate_id.count(*op) != 0) { + *op = original_id_to_duplicate_id.at(*op); + } else if (original_label_to_duplicate_label.count(*op) != 0) { + *op = original_label_to_duplicate_label.at(*op); + } + }); + } + + // If the block is the first duplicated block, insert it after the exit + // block of the original region. Otherwise, insert it after the preceding + // one. + auto duplicated_block_ptr = duplicated_block.get(); + if (previous_block) { + enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block), + previous_block); + } else { + enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block), + exit_block); + } + previous_block = duplicated_block_ptr; + if (block == exit_block) { + // After execution of the loop, this variable stores a pointer to the last + // duplicated block. + duplicated_exit_block = duplicated_block_ptr; + } + } + + for (auto& block : region_blocks) { + for (auto& instr : *block) { + if (instr.result_id() == 0) { + continue; + } + if (AvailableAfterRegion(instr, exit_block, ir_context) && + ValidOpPhiArgument(instr, ir_context)) { + // Add an OpPhi instruction for every result id that is available at + // the end of the region, as long as the result id is valid for use + // with OpPhi. + merge_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpPhi, instr.type_id(), + original_id_to_phi_id.at(instr.result_id()), + opt::Instruction::OperandList({ + {SPV_OPERAND_TYPE_ID, {instr.result_id()}}, + {SPV_OPERAND_TYPE_ID, {exit_block->id()}}, + {SPV_OPERAND_TYPE_ID, + {original_id_to_duplicate_id.at(instr.result_id())}}, + {SPV_OPERAND_TYPE_ID, {duplicated_exit_block->id()}}, + }))); + + fuzzerutil::UpdateModuleIdBound( + ir_context, original_id_to_phi_id.at(instr.result_id())); + + // If the instruction has been remapped by an OpPhi, look + // for all its uses outside of the region and outside of the + // merge block (to not overwrite just added instructions in + // the merge block) and replace the original instruction id + // with the id of the corresponding OpPhi instruction. + ir_context->get_def_use_mgr()->ForEachUse( + &instr, + [ir_context, &instr, region_blocks, original_id_to_phi_id, + &merge_block](opt::Instruction* user, uint32_t operand_index) { + auto user_block = ir_context->get_instr_block(user); + if ((region_blocks.find(user_block) != region_blocks.end()) || + user_block == merge_block.get()) { + return; + } + user->SetOperand(operand_index, + {original_id_to_phi_id.at(instr.result_id())}); + }); + } + } + } + + // Construct a conditional instruction in the |new_entry_block|. + // If the condition is true, the execution proceeds in the + // |entry_block| of the original region. If the condition is + // false, the execution proceeds in the first block of the + // duplicated region. + new_entry_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpSelectionMerge, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}}))); + + new_entry_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranchConditional, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}}, + {SPV_OPERAND_TYPE_ID, + {original_label_to_duplicate_label.at( + message_.entry_block_id())}}}))); + + // Move the terminator of |exit_block| to the end of + // |merge_block|. + auto exit_block_terminator = exit_block->terminator(); + auto cloned_instr = exit_block_terminator->Clone(ir_context); + merge_block->AddInstruction(std::unique_ptr(cloned_instr)); + ir_context->KillInst(exit_block_terminator); + + // Add OpBranch instruction to the merge block at the end of + // |exit_block| and at the end of |duplicated_exit_block|, so that + // the execution proceeds in the |merge_block|. + opt::Instruction merge_branch_instr = opt::Instruction( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}})); + exit_block->AddInstruction(MakeUnique(merge_branch_instr)); + duplicated_exit_block->AddInstruction( + std::unique_ptr(merge_branch_instr.Clone(ir_context))); + + // Execution needs to start in the |new_entry_block|. Change all + // the uses of |entry_block_label_instr| outside of the original + // region to |message_.new_entry_fresh_id|. + auto entry_block_label_instr = + ir_context->get_def_use_mgr()->GetDef(message_.entry_block_id()); + ir_context->get_def_use_mgr()->ForEachUse( + entry_block_label_instr, + [this, ir_context, region_blocks](opt::Instruction* user, + uint32_t operand_index) { + auto user_block = ir_context->get_instr_block(user); + if ((region_blocks.count(user_block) != 0)) { + return; + } + switch (user->opcode()) { + case spv::Op::OpSwitch: + case spv::Op::OpBranch: + case spv::Op::OpBranchConditional: + case spv::Op::OpLoopMerge: + case spv::Op::OpSelectionMerge: { + user->SetOperand(operand_index, {message_.new_entry_fresh_id()}); + } break; + case spv::Op::OpName: + break; + default: + assert(false && + "The label id cannot be used by instructions " + "other than " + "OpSwitch, OpBranch, OpBranchConditional, " + "OpLoopMerge, " + "OpSelectionMerge"); + } + }); + + opt::Instruction* merge_block_terminator = merge_block->terminator(); + switch (merge_block_terminator->opcode()) { + case spv::Op::OpReturnValue: + case spv::Op::OpBranchConditional: { + uint32_t operand = merge_block_terminator->GetSingleWordInOperand(0); + if (original_id_to_phi_id.count(operand)) { + merge_block_terminator->SetInOperand( + 0, {original_id_to_phi_id.at(operand)}); + } + break; + } + default: + break; + } + + // Insert the merge block after the |duplicated_exit_block| (the + // last duplicated block). + enclosing_function->InsertBasicBlockAfter(std::move(merge_block), + duplicated_exit_block); + + // Insert the |new_entry_block| before the entry block of the + // original region. + enclosing_function->InsertBasicBlockBefore(std::move(new_entry_block), + entry_block); + + // Since we have changed the module, most of the analysis are now + // invalid. We can invalidate analyses now after all of the blocks + // have been registered. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785): +// The following method has been copied from +// TransformationOutlineFunction. Consider refactoring to avoid +// duplication. +std::set +TransformationDuplicateRegionWithSelection::GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block) { + auto enclosing_function = entry_block->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + // A block belongs to a region between the entry block and the exit + // block if and only if it is dominated by the entry block and + // post-dominated by the exit block. + std::set result; + for (auto& block : *enclosing_function) { + if (dominator_analysis->Dominates(entry_block, &block) && + postdominator_analysis->Dominates(exit_block, &block)) { + result.insert(&block); + } + } + return result; +} + +protobufs::Transformation +TransformationDuplicateRegionWithSelection::ToMessage() const { + protobufs::Transformation result; + *result.mutable_duplicate_region_with_selection() = message_; + return result; +} + +std::unordered_set +TransformationDuplicateRegionWithSelection::GetFreshIds() const { + std::unordered_set result = {message_.new_entry_fresh_id(), + message_.merge_label_fresh_id()}; + for (auto& pair : message_.original_label_to_duplicate_label()) { + result.insert(pair.second()); + } + for (auto& pair : message_.original_id_to_duplicate_id()) { + result.insert(pair.second()); + } + for (auto& pair : message_.original_id_to_phi_id()) { + result.insert(pair.second()); + } + return result; +} + +bool TransformationDuplicateRegionWithSelection::AvailableAfterRegion( + const opt::Instruction& instr, opt::BasicBlock* exit_block, + opt::IRContext* ir_context) { + opt::Instruction* final_instruction_in_region = &*exit_block->tail(); + return &instr == final_instruction_in_region || + fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, final_instruction_in_region, instr.result_id()); +} + +bool TransformationDuplicateRegionWithSelection::ValidOpPhiArgument( + const opt::Instruction& instr, opt::IRContext* ir_context) { + opt::Instruction* instr_type = + ir_context->get_def_use_mgr()->GetDef(instr.type_id()); + + // It is invalid to apply OpPhi to void-typed values. + if (instr_type->opcode() == spv::Op::OpTypeVoid) { + return false; + } + + // Using pointers with OpPhi requires capability VariablePointers. + if (instr_type->opcode() == spv::Op::OpTypePointer && + !ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointers)) { + return false; + } + + // OpTypeSampledImage cannot be the result type of an OpPhi instruction. + if (instr_type->opcode() == spv::Op::OpTypeSampledImage) { + return false; + } + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h new file mode 100644 index 00000000..30e3c373 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationDuplicateRegionWithSelection : public Transformation { + public: + explicit TransformationDuplicateRegionWithSelection( + protobufs::TransformationDuplicateRegionWithSelection message); + + explicit TransformationDuplicateRegionWithSelection( + uint32_t new_entry_fresh_id, uint32_t condition_id, + uint32_t merge_label_fresh_id, uint32_t entry_block_id, + uint32_t exit_block_id, + const std::map& original_label_to_duplicate_label, + const std::map& original_id_to_duplicate_id, + const std::map& original_id_to_phi_id); + + // - |new_entry_fresh_id|, |merge_label_fresh_id| must be fresh and distinct. + // - |condition_id| must refer to a valid instruction of boolean type. + // - |entry_block_id| and |exit_block_id| must refer to valid blocks and they + // must form a single-entry, single-exit region. Its constructs and their + // merge blocks must be either wholly within or wholly outside of the + // region. + // - |original_label_to_duplicate_label| must contain at least a key for every + // block in the original region. + // - |original_id_to_duplicate_id| must contain at least a key for every + // result id in the original region. + // - |original_id_to_phi_id| must contain at least a key for every result id + // available at the end of the original region. + // - In each of these three maps, each value must be a distinct, fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A transformation that inserts a conditional statement with a boolean + // expression of arbitrary value and duplicates a given single-entry, + // single-exit region, so that it is present in each conditional branch and + // will be executed regardless of which branch will be taken. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Returns the set of blocks dominated by |entry_block| and post-dominated + // by |exit_block|. + static std::set GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block); + + // Returns true if and only if |instr| is available at the end of the region + // for which |exit_block| is the final block. + static bool AvailableAfterRegion(const opt::Instruction& instr, + opt::BasicBlock* exit_block, + opt::IRContext* ir_context); + + // Returns true if and only if |instr| is valid as an argument to an OpPhi + // instruction. + static bool ValidOpPhiArgument(const opt::Instruction& instr, + opt::IRContext* ir_context); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationDuplicateRegionWithSelection message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.cpp new file mode 100644 index 00000000..72487a8e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_equation_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationEquationInstruction::TransformationEquationInstruction( + protobufs::TransformationEquationInstruction message) + : message_(std::move(message)) {} + +TransformationEquationInstruction::TransformationEquationInstruction( + uint32_t fresh_id, spv::Op opcode, + const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_opcode(uint32_t(opcode)); + for (auto id : in_operand_id) { + message_.add_in_operand_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationEquationInstruction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The instruction to insert before must exist. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + return false; + } + // The input ids must all exist, not be OpUndef, not be irrelevant, and be + // available before this instruction. + for (auto id : message_.in_operand_id()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(id); + if (!inst) { + return false; + } + if (inst->opcode() == spv::Op::OpUndef) { + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + id)) { + return false; + } + } + + return MaybeGetResultTypeId(ir_context) != 0; +} + +void TransformationEquationInstruction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + opt::Instruction::OperandList in_operands; + std::vector rhs_id; + for (auto id : message_.in_operand_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + rhs_id.push_back(id); + } + + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique( + ir_context, static_cast(message_.opcode()), + MaybeGetResultTypeId(ir_context), message_.fresh_id(), + std::move(in_operands))); + + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); + + // Add an equation fact as long as the result id is not irrelevant (it could + // be if we are inserting into a dead block). + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + transformation_context->GetFactManager()->AddFactIdEquation( + message_.fresh_id(), static_cast(message_.opcode()), rhs_id); + } +} + +protobufs::Transformation TransformationEquationInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_equation_instruction() = message_; + return result; +} + +uint32_t TransformationEquationInstruction::MaybeGetResultTypeId( + opt::IRContext* ir_context) const { + auto opcode = static_cast(message_.opcode()); + switch (opcode) { + case spv::Op::OpConvertUToF: + case spv::Op::OpConvertSToF: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0))); + if (!type) { + return 0; + } + + if (const auto* vector = type->AsVector()) { + if (!vector->element_type()->AsInteger()) { + return 0; + } + + if (auto element_type_id = fuzzerutil::MaybeGetFloatType( + ir_context, vector->element_type()->AsInteger()->width())) { + return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id, + vector->element_count()); + } + + return 0; + } else { + if (!type->AsInteger()) { + return 0; + } + + return fuzzerutil::MaybeGetFloatType(ir_context, + type->AsInteger()->width()); + } + } + case spv::Op::OpBitcast: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst) { + return 0; + } + + const auto* operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!operand_type) { + return 0; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters is that + // they must have the same number of bits. Consider improving the code + // below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + uint32_t component_type_id; + if (const auto* int_type = vector->element_type()->AsInteger()) { + component_type_id = + fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = vector->element_type()->AsFloat()) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true); + if (component_type_id == 0 || + fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()) == 0) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), false); + } + } else { + assert(false && "Only vectors of numerical components are supported"); + return 0; + } + + if (component_type_id == 0) { + return 0; + } + + return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()); + } else if (const auto* int_type = operand_type->AsInteger()) { + return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = operand_type->AsFloat()) { + if (auto existing_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true)) { + return existing_id; + } + + return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(), + false); + } else { + assert(false && + "Operand is not a scalar or a vector of numerical type"); + return 0; + } + } + case spv::Op::OpIAdd: + case spv::Op::OpISub: { + if (message_.in_operand_id_size() != 2) { + return 0; + } + uint32_t first_operand_width = 0; + uint32_t first_operand_type_id = 0; + for (uint32_t index = 0; index < 2; index++) { + auto operand_inst = ir_context->get_def_use_mgr()->GetDef( + message_.in_operand_id(index)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + uint32_t operand_width = + operand_type->AsInteger() + ? 1 + : operand_type->AsVector()->element_count(); + if (index == 0) { + first_operand_width = operand_width; + first_operand_type_id = operand_inst->type_id(); + } else { + assert(first_operand_width != 0 && + "The first operand should have been processed."); + if (operand_width != first_operand_width) { + return 0; + } + } + } + assert(first_operand_type_id != 0 && + "A type must have been found for the first operand."); + return first_operand_type_id; + } + case spv::Op::OpLogicalNot: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsBool() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsBool()))) { + return 0; + } + return operand_inst->type_id(); + } + case spv::Op::OpSNegate: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + return operand_inst->type_id(); + } + default: + assert(false && "Inappropriate opcode for equation instruction."); + return 0; + } +} + +std::unordered_set TransformationEquationInstruction::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.h new file mode 100644 index 00000000..40118d91 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_equation_instruction.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationEquationInstruction : public Transformation { + public: + explicit TransformationEquationInstruction( + protobufs::TransformationEquationInstruction message); + + TransformationEquationInstruction( + uint32_t fresh_id, spv::Op opcode, + const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh. + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an equation instruction can legitimately be inserted. + // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and + // be available before |message_.instruction_to_insert_before|. + // - |message_.opcode| must be an opcode for which we know how to handle + // equations, the types of the ids in |message_.in_operand_id| must be + // suitable for use with this opcode, and the module must contain an + // appropriate result type id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction to the module, right before + // |message_.instruction_to_insert_before|, of the form: + // + // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids| + // + // where %type is a type id that already exists in the module and that is + // compatible with the opcode and input operands. + // + // The fact manager is also updated to inform it of this equation fact. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns type id for the equation instruction. Returns 0 if result type does + // not exist. + uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const; + + protobufs::TransformationEquationInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp new file mode 100644 index 00000000..4c13ec1f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_expand_vector_reduction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationExpandVectorReduction::TransformationExpandVectorReduction( + protobufs::TransformationExpandVectorReduction message) + : message_(std::move(message)) {} + +TransformationExpandVectorReduction::TransformationExpandVectorReduction( + const uint32_t instruction_result_id, + const std::vector& fresh_ids) { + message_.set_instruction_result_id(instruction_result_id); + *message_.mutable_fresh_ids() = + google::protobuf::RepeatedField( + fresh_ids.begin(), fresh_ids.end()); +} + +bool TransformationExpandVectorReduction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // |instruction| must be defined. + if (!instruction) { + return false; + } + + // |instruction| must be OpAny or OpAll. + if (instruction->opcode() != spv::Op::OpAny && + instruction->opcode() != spv::Op::OpAll) { + return false; + } + + // |message_.fresh_ids.size| must have the exact number of fresh ids required + // to apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + std::set ids_used_by_this_transformation; + for (uint32_t fresh_id : message_.fresh_ids()) { + // All ids in |message_.fresh_ids| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + + // All fresh ids need to be distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + return true; +} + +void TransformationExpandVectorReduction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + auto* vector = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector->type_id()) + ->AsVector() + ->element_count(); + + // Fresh id iterator. + auto fresh_id = message_.fresh_ids().begin(); + + // |vector_components| are the ids of the extracted components from |vector|. + std::vector vector_components; + + for (uint32_t i = 0; i < vector_component_count; i++) { + // Extracts the i-th |vector| component. + auto vector_component = + opt::Instruction(ir_context, spv::Op::OpCompositeExtract, + instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}); + instruction->InsertBefore(MakeUnique(vector_component)); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_component.result_id()); + vector_components.push_back(vector_component.result_id()); + } + + // The first two |vector| components are used in the first logical operation. + auto logical_instruction = opt::Instruction( + ir_context, + instruction->opcode() == spv::Op::OpAny ? spv::Op::OpLogicalOr + : spv::Op::OpLogicalAnd, + instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector_components[0]}}, + {SPV_OPERAND_TYPE_ID, {vector_components[1]}}}); + instruction->InsertBefore(MakeUnique(logical_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, logical_instruction.result_id()); + + // Evaluates the remaining components. + for (uint32_t i = 2; i < vector_components.size(); i++) { + logical_instruction = opt::Instruction( + ir_context, logical_instruction.opcode(), instruction->type_id(), + *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector_components[i]}}, + {SPV_OPERAND_TYPE_ID, {logical_instruction.result_id()}}}); + instruction->InsertBefore( + MakeUnique(logical_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, + logical_instruction.result_id()); + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // If it's possible to make a synonym of |instruction|, then add the fact that + // the last |logical_instruction| is a synonym of |instruction|. + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *instruction)) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(logical_instruction.result_id(), {}), + MakeDataDescriptor(instruction->result_id(), {})); + } +} + +protobufs::Transformation TransformationExpandVectorReduction::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_expand_vector_reduction() = message_; + return result; +} + +uint32_t TransformationExpandVectorReduction::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // For each vector component, 1 OpCompositeExtract and 1 OpLogical* (except + // for the first component) instructions will be inserted. + return 2 * ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count() - + 1; +} + +std::unordered_set TransformationExpandVectorReduction::GetFreshIds() + const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h new file mode 100644 index 00000000..6ee2cefa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h @@ -0,0 +1,105 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// clang-format off +// SPIR-V code to help understand the transformation. +// +// ------------------------------------------------------------------------------- +// | Reference shader | Variant shader | +// ------------------------------------------------------------------------------- +// | OpCapability Shader | OpCapability Shader | +// | %1 = OpExtInstImport "GLSL.std.450" | %1 = OpExtInstImport "GLSL.std.450" | +// | OpMemoryModel Logical GLSL450 | OpMemoryModel Logical GLSL450 | +// | OpEntryPoint Vertex %9 "main" | OpEntryPoint Vertex %9 "main" | +// | | | +// | ; Types | ; Types | +// | %2 = OpTypeBool | %2 = OpTypeBool | +// | %3 = OpTypeVector %2 2 | %3 = OpTypeVector %2 2 | +// | %4 = OpTypeVoid | %4 = OpTypeVoid | +// | %5 = OpTypeFunction %4 | %5 = OpTypeFunction %4 | +// | | | +// | ; Constants | ; Constants | +// | %6 = OpConstantTrue %2 | %6 = OpConstantTrue %2 | +// | %7 = OpConstantFalse %2 | %7 = OpConstantFalse %2 | +// | %8 = OpConstantComposite %3 %6 %7 | %8 = OpConstantComposite %3 %6 %7 | +// | | | +// | ; main function | ; main function | +// | %9 = OpFunction %4 None %5 | %9 = OpFunction %4 None %5 | +// | %10 = OpLabel | %10 = OpLabel | +// | %11 = OpAny %2 %8 | | +// | %12 = OpAll %2 %8 | ; Add OpAny synonym | +// | OpReturn | %13 = OpCompositeExtract %2 %8 0 | +// | OpFunctionEnd | %14 = OpCompositeExtract %2 %8 1 | +// | | %15 = OpLogicalOr %2 %13 %14 | +// | | %11 = OpAny %2 %8 | +// | | | +// | | ; Add OpAll synonym | +// | | %16 = OpCompositeExtract %2 %8 0 | +// | | %17 = OpCompositeExtract %2 %8 1 | +// | | %18 = OpLogicalAnd %2 %16 %17 | +// | | %12 = OpAll %2 %8 | +// | | OpReturn | +// | | OpFunctionEnd | +// ------------------------------------------------------------------------------- +// +// %11 and %15 are synonymous +// %12 and %18 are synonymous +// clang-format on +class TransformationExpandVectorReduction : public Transformation { + public: + explicit TransformationExpandVectorReduction( + protobufs::TransformationExpandVectorReduction message); + + TransformationExpandVectorReduction(const uint32_t instruction_result_id, + const std::vector& fresh_ids); + + // - |message_.instruction_result_id| must be OpAny or OpAll. + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds synonyms for OpAny and OpAll instructions by evaluating each vector + // component with the corresponding logical operation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of fresh ids required to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationExpandVectorReduction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp new file mode 100644 index 00000000..f8d1c338 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp @@ -0,0 +1,1039 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_flatten_conditional_branch.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( + protobufs::TransformationFlattenConditionalBranch message) + : message_(std::move(message)) {} + +TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( + uint32_t header_block_id, bool true_branch_first, + uint32_t fresh_id_for_bvec2_selector, uint32_t fresh_id_for_bvec3_selector, + uint32_t fresh_id_for_bvec4_selector, + const std::vector& + side_effect_wrappers_info) { + message_.set_header_block_id(header_block_id); + message_.set_true_branch_first(true_branch_first); + message_.set_fresh_id_for_bvec2_selector(fresh_id_for_bvec2_selector); + message_.set_fresh_id_for_bvec3_selector(fresh_id_for_bvec3_selector); + message_.set_fresh_id_for_bvec4_selector(fresh_id_for_bvec4_selector); + for (auto const& side_effect_wrapper_info : side_effect_wrappers_info) { + *message_.add_side_effect_wrapper_info() = side_effect_wrapper_info; + } +} + +bool TransformationFlattenConditionalBranch::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto header_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.header_block_id()); + + // The block must have been found and it must be a selection header. + if (!header_block || !header_block->GetMergeInst() || + header_block->GetMergeInst()->opcode() != spv::Op::OpSelectionMerge) { + return false; + } + + // The header block must end with an OpBranchConditional instruction. + if (header_block->terminator()->opcode() != spv::Op::OpBranchConditional) { + return false; + } + + // The branch condition cannot be irrelevant: we will make reference to it + // multiple times and we need to be guaranteed that these references will + // yield the same result; if they are replaced by other ids that will not + // work. + if (transformation_context.GetFactManager()->IdIsIrrelevant( + header_block->terminator()->GetSingleWordInOperand(0))) { + return false; + } + + std::set used_fresh_ids; + + // If ids have been provided to be used as vector guards for OpSelect + // instructions then they must be fresh. + for (uint32_t fresh_id_for_bvec_selector : + {message_.fresh_id_for_bvec2_selector(), + message_.fresh_id_for_bvec3_selector(), + message_.fresh_id_for_bvec4_selector()}) { + if (fresh_id_for_bvec_selector != 0) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_id_for_bvec_selector, ir_context, &used_fresh_ids)) { + return false; + } + } + } + + // Use a set to keep track of the instructions that require fresh ids. + std::set instructions_that_need_ids; + + // Check that, if there are enough ids, the conditional can be flattened and, + // if so, add all the problematic instructions that need to be enclosed inside + // conditionals to |instructions_that_need_ids|. + if (!GetProblematicInstructionsIfConditionalCanBeFlattened( + ir_context, header_block, transformation_context, + &instructions_that_need_ids)) { + return false; + } + + // Get the mapping from instructions to the fresh ids needed to enclose them + // inside conditionals. + auto insts_to_wrapper_info = GetInstructionsToWrapperInfo(ir_context); + + { + // Check the ids in the map. + for (const auto& inst_to_info : insts_to_wrapper_info) { + // Check the fresh ids needed for all of the instructions that need to be + // enclosed inside a conditional. + for (uint32_t id : {inst_to_info.second.merge_block_id(), + inst_to_info.second.execute_block_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation( + id, ir_context, &used_fresh_ids)) { + return false; + } + } + + // Check the other ids needed, if the instruction needs a placeholder. + if (InstructionNeedsPlaceholder(ir_context, *inst_to_info.first)) { + // Check the fresh ids. + for (uint32_t id : {inst_to_info.second.actual_result_id(), + inst_to_info.second.alternative_block_id(), + inst_to_info.second.placeholder_result_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation( + id, ir_context, &used_fresh_ids)) { + return false; + } + } + + // Check that the placeholder value id exists, has the right type and is + // available to use at this point. + auto value_def = ir_context->get_def_use_mgr()->GetDef( + inst_to_info.second.value_to_copy_id()); + if (!value_def || + value_def->type_id() != inst_to_info.first->type_id() || + !fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, inst_to_info.first, + inst_to_info.second.value_to_copy_id())) { + return false; + } + } + } + } + + // If some instructions that require ids are not in the map, the + // transformation needs overflow ids to be applicable. + for (auto instruction : instructions_that_need_ids) { + if (insts_to_wrapper_info.count(instruction) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + if (OpSelectArgumentsAreRestricted(ir_context)) { + // OpPhi instructions at the convergence block for the selection are handled + // by turning them into OpSelect instructions. As the SPIR-V version in use + // has restrictions on the arguments that OpSelect can take, we must check + // that any OpPhi instructions are compatible with these restrictions. + uint32_t convergence_block_id = + FindConvergenceBlock(ir_context, *header_block); + // Consider every OpPhi instruction at the convergence block. + if (!ir_context->cfg() + ->block(convergence_block_id) + ->WhileEachPhiInst([this, + ir_context](opt::Instruction* inst) -> bool { + // Decide whether the OpPhi can be handled based on its result + // type. + opt::Instruction* phi_result_type = + ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + switch (phi_result_type->opcode()) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypePointer: + // Fine: OpSelect can work directly on scalar and pointer + // types. + return true; + case spv::Op::OpTypeVector: { + // In its restricted form, OpSelect can only select between + // vectors if the condition of the select is a boolean + // boolean vector. We thus require the appropriate boolean + // vector type to be present. + uint32_t bool_type_id = + fuzzerutil::MaybeGetBoolType(ir_context); + if (!bool_type_id) { + return false; + } + + uint32_t dimension = + phi_result_type->GetSingleWordInOperand(1); + if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id, + dimension) == 0) { + // The required boolean vector type is not present. + return false; + } + // The transformation needs to be equipped with a fresh id + // in which to store the vectorized version of the selection + // construct's condition. + switch (dimension) { + case 2: + return message_.fresh_id_for_bvec2_selector() != 0; + case 3: + return message_.fresh_id_for_bvec3_selector() != 0; + default: + assert(dimension == 4 && "Invalid vector dimension."); + return message_.fresh_id_for_bvec4_selector() != 0; + } + } + default: + return false; + } + })) { + return false; + } + } + + // All checks were passed. + return true; +} + +void TransformationFlattenConditionalBranch::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // branch = 1 corresponds to the true branch, branch = 2 corresponds to the + // false branch. If the true branch is to be laid out first, we need to visit + // the false branch first, because each branch is moved to right after the + // header while it is visited. + std::vector branches = {2, 1}; + if (!message_.true_branch_first()) { + // Similarly, we need to visit the true branch first, if we want it to be + // laid out after the false branch. + branches = {1, 2}; + } + + auto header_block = ir_context->cfg()->block(message_.header_block_id()); + + // Get the ids of the starting blocks of the first and last branches to be + // laid out. The first branch is the true branch iff + // |message_.true_branch_first| is true. + auto branch_instruction = header_block->terminator(); + uint32_t first_block_first_branch_id = + branch_instruction->GetSingleWordInOperand(branches[1]); + uint32_t first_block_last_branch_id = + branch_instruction->GetSingleWordInOperand(branches[0]); + + uint32_t convergence_block_id = + FindConvergenceBlock(ir_context, *header_block); + + // If the OpBranchConditional instruction in the header branches to the same + // block for both values of the condition, this is the convergence block (the + // flow does not actually diverge) and the OpPhi instructions in it are still + // valid, so we do not need to make any changes. + if (first_block_first_branch_id != first_block_last_branch_id) { + RewriteOpPhiInstructionsAtConvergenceBlock( + *header_block, convergence_block_id, ir_context); + } + + // Get the mapping from instructions to fresh ids. + auto insts_to_info = GetInstructionsToWrapperInfo(ir_context); + + // Get a reference to the last block in the first branch that will be laid out + // (this depends on |message_.true_branch_first|). The last block is the block + // in the branch just before flow converges (it might not exist). + opt::BasicBlock* last_block_first_branch = nullptr; + + // Keep track of blocks and ids for which we should later add dead block and + // irrelevant id facts. We wait until we have finished applying the + // transformation before adding these facts, so that the fact manager has + // access to the fully up-to-date module. + std::vector dead_blocks; + std::vector irrelevant_ids; + + // Adjust the conditional branches by enclosing problematic instructions + // within conditionals and get references to the last block in each branch. + for (uint32_t branch : branches) { + auto current_block = header_block; + // Get the id of the first block in this branch. + uint32_t next_block_id = branch_instruction->GetSingleWordInOperand(branch); + + // Consider all blocks in the branch until the convergence block is reached. + while (next_block_id != convergence_block_id) { + // Move the next block to right after the current one. + current_block->GetParent()->MoveBasicBlockToAfter(next_block_id, + current_block); + + // Move forward in the branch. + current_block = ir_context->cfg()->block(next_block_id); + + // Find all the instructions in the current block which need to be + // enclosed inside conditionals. + std::vector problematic_instructions; + + current_block->ForEachInst( + [&problematic_instructions](opt::Instruction* instruction) { + if (instruction->opcode() != spv::Op::OpLabel && + instruction->opcode() != spv::Op::OpBranch && + !fuzzerutil::InstructionHasNoSideEffects(*instruction)) { + problematic_instructions.push_back(instruction); + } + }); + + uint32_t condition_id = + header_block->terminator()->GetSingleWordInOperand(0); + + // Enclose all of the problematic instructions in conditionals, with the + // same condition as the selection construct being flattened. + for (auto instruction : problematic_instructions) { + // Get the info needed by this instruction to wrap it inside a + // conditional. + protobufs::SideEffectWrapperInfo wrapper_info; + + if (insts_to_info.count(instruction) != 0) { + // Get the fresh ids from the map, if present. + wrapper_info = insts_to_info[instruction]; + } else { + // If we could not get it from the map, use overflow ids. We don't + // need to set |wrapper_info.instruction|, as it will not be used. + wrapper_info.set_merge_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_execute_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + + if (InstructionNeedsPlaceholder(ir_context, *instruction)) { + // Ge the fresh ids from the overflow ids. + wrapper_info.set_actual_result_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_alternative_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_placeholder_result_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + + // Try to find a zero constant. It does not matter whether it is + // relevant or irrelevant. + for (bool is_irrelevant : {true, false}) { + wrapper_info.set_value_to_copy_id( + fuzzerutil::MaybeGetZeroConstant( + ir_context, *transformation_context, + instruction->type_id(), is_irrelevant)); + if (wrapper_info.value_to_copy_id()) { + break; + } + } + } + } + + // Enclose the instruction in a conditional and get the merge block + // generated by this operation (this is where all the following + // instructions will be). + current_block = EncloseInstructionInConditional( + ir_context, *transformation_context, current_block, instruction, + wrapper_info, condition_id, branch == 1, &dead_blocks, + &irrelevant_ids); + } + + next_block_id = current_block->terminator()->GetSingleWordInOperand(0); + + // If the next block is the convergence block and this the branch that + // will be laid out right after the header, record this as the last block + // in the first branch. + if (next_block_id == convergence_block_id && branch == branches[1]) { + last_block_first_branch = current_block; + } + } + } + + // The current header should unconditionally branch to the starting block in + // the first branch to be laid out, if such a branch exists (i.e. the header + // does not branch directly to the convergence block), and to the starting + // block in the last branch to be laid out otherwise. + uint32_t after_header = first_block_first_branch_id != convergence_block_id + ? first_block_first_branch_id + : first_block_last_branch_id; + + // Kill the merge instruction and the branch instruction in the current + // header. + auto merge_inst = header_block->GetMergeInst(); + ir_context->KillInst(branch_instruction); + ir_context->KillInst(merge_inst); + + // Add a new, unconditional, branch instruction from the current header to + // |after_header|. + header_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {after_header}}})); + + // If the first branch to be laid out exists, change the branch instruction so + // that the last block in such branch unconditionally branches to the first + // block in the other branch (or the convergence block if there is no other + // branch) and change the OpPhi instructions in the last branch accordingly + // (the predecessor changed). + if (last_block_first_branch) { + last_block_first_branch->terminator()->SetInOperand( + 0, {first_block_last_branch_id}); + + // Change the OpPhi instructions of the last branch (if there is another + // branch) so that the predecessor is now the last block of the first + // branch. The block must have a single predecessor, so the operand + // specifying the predecessor is always in the same position. + if (first_block_last_branch_id != convergence_block_id) { + ir_context->get_instr_block(first_block_last_branch_id) + ->ForEachPhiInst( + [&last_block_first_branch](opt::Instruction* phi_inst) { + // The operand specifying the predecessor is the second input + // operand. + phi_inst->SetInOperand(1, {last_block_first_branch->id()}); + }); + } + } + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Now that we have finished adding blocks and ids to the module and + // invalidated existing analyses, update the fact manager regarding dead + // blocks and irrelevant ids. + for (auto dead_block : dead_blocks) { + transformation_context->GetFactManager()->AddFactBlockIsDead(dead_block); + } + for (auto irrelevant_id : irrelevant_ids) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + irrelevant_id); + } +} + +protobufs::Transformation TransformationFlattenConditionalBranch::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_flatten_conditional_branch() = message_; + return result; +} + +bool TransformationFlattenConditionalBranch:: + GetProblematicInstructionsIfConditionalCanBeFlattened( + opt::IRContext* ir_context, opt::BasicBlock* header, + const TransformationContext& transformation_context, + std::set* instructions_that_need_ids) { + uint32_t merge_block_id = header->MergeBlockIdIfAny(); + assert(merge_block_id && + header->GetMergeInst()->opcode() == spv::Op::OpSelectionMerge && + header->terminator()->opcode() == spv::Op::OpBranchConditional && + "|header| must be the header of a conditional."); + + // |header| must be reachable. + if (!ir_context->IsReachable(*header)) { + return false; + } + + auto enclosing_function = header->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + // Check that the header and the merge block describe a single-entry, + // single-exit region. + if (!dominator_analysis->Dominates(header->id(), merge_block_id) || + !postdominator_analysis->Dominates(merge_block_id, header->id())) { + return false; + } + + // Traverse the CFG starting from the header and check that, for all the + // blocks that can be reached by the header before the flow converges: + // - they don't contain merge, barrier or OpSampledImage instructions + // - they branch unconditionally to another block + // Add any side-effecting instruction, requiring fresh ids, to + // |instructions_that_need_ids| + std::queue to_check; + header->ForEachSuccessorLabel( + [&to_check](uint32_t label) { to_check.push(label); }); + + auto* structured_cfg = ir_context->GetStructuredCFGAnalysis(); + while (!to_check.empty()) { + uint32_t block_id = to_check.front(); + to_check.pop(); + + if (structured_cfg->ContainingConstruct(block_id) != header->id() && + block_id != merge_block_id) { + // This block can be reached from the |header| but doesn't belong to its + // selection construct. This might be a continue target of some loop - + // we can't flatten the |header|. + return false; + } + + // If the block post-dominates the header, this is where flow converges, and + // we don't need to check this branch any further, because the + // transformation will only change the part of the graph where flow is + // divergent. + if (postdominator_analysis->Dominates(block_id, header->id())) { + continue; + } + + if (!transformation_context.GetFactManager()->BlockIsDead(header->id()) && + transformation_context.GetFactManager()->BlockIsDead(block_id)) { + // The |header| is not dead but the |block_id| is. Since |block_id| + // doesn't postdominate the |header|, CFG hasn't converged yet. Thus, we + // don't flatten the construct to prevent |block_id| from becoming + // executable. + return false; + } + + auto block = ir_context->cfg()->block(block_id); + + // The block must not have a merge instruction, because inner constructs are + // not allowed. + if (block->GetMergeInst()) { + return false; + } + + // The terminator instruction for the block must be OpBranch. + if (block->terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + + // The base objects for all data descriptors involved in synonym facts. + std::unordered_set synonym_base_objects; + for (auto* synonym : + transformation_context.GetFactManager()->GetAllSynonyms()) { + synonym_base_objects.insert(synonym->object()); + } + + // Check all of the instructions in the block. + bool all_instructions_compatible = block->WhileEachInst( + [ir_context, instructions_that_need_ids, + &synonym_base_objects](opt::Instruction* instruction) { + // We can ignore OpLabel instructions. + if (instruction->opcode() == spv::Op::OpLabel) { + return true; + } + + // If the instruction is the base object of some synonym then we + // conservatively bail out: if a synonym ends up depending on an + // instruction that needs to be enclosed in a side-effect wrapper then + // it might no longer hold after we flatten the conditional. + if (instruction->result_id() && + synonym_base_objects.count(instruction->result_id())) { + return false; + } + + // If the instruction is a branch, it must be an unconditional branch. + if (instruction->IsBranch()) { + return instruction->opcode() == spv::Op::OpBranch; + } + + // We cannot go ahead if we encounter an instruction that cannot be + // handled. + if (!InstructionCanBeHandled(ir_context, *instruction)) { + return false; + } + + // If the instruction has side effects, add it to the + // |instructions_that_need_ids| set. + if (!fuzzerutil::InstructionHasNoSideEffects(*instruction)) { + instructions_that_need_ids->emplace(instruction); + } + + return true; + }); + + if (!all_instructions_compatible) { + return false; + } + + // Add the successor of this block to the list of blocks that need to be + // checked. + to_check.push(block->terminator()->GetSingleWordInOperand(0)); + } + + // All the blocks are compatible with the transformation and this is indeed a + // single-entry, single-exit region. + return true; +} + +bool TransformationFlattenConditionalBranch::InstructionNeedsPlaceholder( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + assert(!fuzzerutil::InstructionHasNoSideEffects(instruction) && + InstructionCanBeHandled(ir_context, instruction) && + "The instruction must have side effects and it must be possible to " + "enclose it inside a conditional."); + + if (instruction.HasResultId()) { + // We need a placeholder iff the type is not Void. + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + return type && !type->AsVoid(); + } + + return false; +} + +std::unordered_map +TransformationFlattenConditionalBranch::GetInstructionsToWrapperInfo( + opt::IRContext* ir_context) const { + std::unordered_map + instructions_to_ids; + for (const auto& wrapper_info : message_.side_effect_wrapper_info()) { + auto instruction = FindInstruction(wrapper_info.instruction(), ir_context); + if (instruction) { + instructions_to_ids.emplace(instruction, wrapper_info); + } + } + + return instructions_to_ids; +} + +opt::BasicBlock* +TransformationFlattenConditionalBranch::EncloseInstructionInConditional( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, opt::BasicBlock* block, + opt::Instruction* instruction, + const protobufs::SideEffectWrapperInfo& wrapper_info, uint32_t condition_id, + bool exec_if_cond_true, std::vector* dead_blocks, + std::vector* irrelevant_ids) { + // Get the next instruction (it will be useful for splitting). + auto next_instruction = instruction->NextNode(); + + // Update the module id bound. + for (uint32_t id : + {wrapper_info.merge_block_id(), wrapper_info.execute_block_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Create the block where the instruction is executed by splitting the + // original block. + auto execute_block = block->SplitBasicBlock( + ir_context, wrapper_info.execute_block_id(), + fuzzerutil::GetIteratorForInstruction(block, instruction)); + + // Create the merge block for the conditional that we are about to create by + // splitting execute_block (this will leave |instruction| as the only + // instruction in |execute_block|). + auto merge_block = execute_block->SplitBasicBlock( + ir_context, wrapper_info.merge_block_id(), + fuzzerutil::GetIteratorForInstruction(execute_block, next_instruction)); + + // Propagate the fact that the block is dead to the newly-created blocks. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + dead_blocks->emplace_back(execute_block->id()); + dead_blocks->emplace_back(merge_block->id()); + } + + // Initially, consider the merge block as the alternative block to branch to + // if the instruction should not be executed. + auto alternative_block = merge_block; + + // Add an unconditional branch from |execute_block| to |merge_block|. + execute_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {merge_block->id()}}})); + + // If the instruction requires a placeholder, it means that it has a result id + // and its result needs to be able to be used later on, and we need to: + // - add an additional block |ids.alternative_block_id| where a placeholder + // result id (using fresh id |ids.placeholder_result_id|) is obtained either + // by using OpCopyObject and copying |ids.value_to_copy_id| or, if such id + // was not given and a suitable constant was not found, by using OpUndef. + // - mark |ids.placeholder_result_id| as irrelevant + // - change the result id of the instruction to a fresh id + // (|ids.actual_result_id|). + // - add an OpPhi instruction, which will have the original result id of the + // instruction, in the merge block. + if (InstructionNeedsPlaceholder(ir_context, *instruction)) { + // Update the module id bound with the additional ids. + for (uint32_t id : + {wrapper_info.actual_result_id(), wrapper_info.alternative_block_id(), + wrapper_info.placeholder_result_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Create a new block using |fresh_ids.alternative_block_id| for its label. + auto alternative_block_temp = MakeUnique( + MakeUnique(ir_context, spv::Op::OpLabel, 0, + wrapper_info.alternative_block_id(), + opt::Instruction::OperandList{})); + + // Keep the original result id of the instruction in a variable. + uint32_t original_result_id = instruction->result_id(); + + // Set the result id of the instruction to be |ids.actual_result_id|. + instruction->SetResultId(wrapper_info.actual_result_id()); + + // Add a placeholder instruction, with the same type as the original + // instruction and id |ids.placeholder_result_id|, to the new block. + if (wrapper_info.value_to_copy_id()) { + // If there is an available id to copy from, the placeholder instruction + // will be %placeholder_result_id = OpCopyObject %type %value_to_copy_id + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, spv::Op::OpCopyObject, instruction->type_id(), + wrapper_info.placeholder_result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {wrapper_info.value_to_copy_id()}}})); + } else { + // If there is no such id, use an OpUndef instruction. + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, spv::Op::OpUndef, instruction->type_id(), + wrapper_info.placeholder_result_id(), + opt::Instruction::OperandList{})); + } + + // Mark |ids.placeholder_result_id| as irrelevant. + irrelevant_ids->emplace_back(wrapper_info.placeholder_result_id()); + + // Add an unconditional branch from the new block to the merge block. + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {merge_block->id()}}})); + + // Insert the block before the merge block. + alternative_block = block->GetParent()->InsertBasicBlockBefore( + std::move(alternative_block_temp), merge_block); + + // Using the original instruction result id, add an OpPhi instruction to the + // merge block, which will either take the value of the result of the + // instruction or the placeholder value defined in the alternative block. + merge_block->begin().InsertBefore(MakeUnique( + ir_context, spv::Op::OpPhi, instruction->type_id(), original_result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {execute_block->id()}}, + {SPV_OPERAND_TYPE_ID, {wrapper_info.placeholder_result_id()}}, + {SPV_OPERAND_TYPE_ID, {alternative_block->id()}}})); + + // Propagate the fact that the block is dead to the new block. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + dead_blocks->emplace_back(alternative_block->id()); + } + } + + // Depending on whether the instruction should be executed in the if branch or + // in the else branch, get the corresponding ids. + auto if_block_id = (exec_if_cond_true ? execute_block : alternative_block) + ->GetLabel() + ->result_id(); + auto else_block_id = (exec_if_cond_true ? alternative_block : execute_block) + ->GetLabel() + ->result_id(); + + // Add an OpSelectionMerge instruction to the block. + block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpSelectionMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {merge_block->id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}})); + + // Add an OpBranchConditional, to the block, using |condition_id| as the + // condition and branching to |if_block_id| if the condition is true and to + // |else_block_id| if the condition is false. + block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranchConditional, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {condition_id}}, + {SPV_OPERAND_TYPE_ID, {if_block_id}}, + {SPV_OPERAND_TYPE_ID, {else_block_id}}})); + + return merge_block; +} + +bool TransformationFlattenConditionalBranch::InstructionCanBeHandled( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + // We can handle all instructions with no side effects. + if (fuzzerutil::InstructionHasNoSideEffects(instruction)) { + return true; + } + + // We cannot handle barrier instructions, while we should be able to handle + // all other instructions by enclosing them inside a conditional. + if (instruction.opcode() == spv::Op::OpControlBarrier || + instruction.opcode() == spv::Op::OpMemoryBarrier || + instruction.opcode() == spv::Op::OpNamedBarrierInitialize || + instruction.opcode() == spv::Op::OpMemoryNamedBarrier || + instruction.opcode() == spv::Op::OpTypeNamedBarrier) { + return false; + } + + // We cannot handle OpSampledImage instructions, as they need to be in the + // same block as their use. + if (instruction.opcode() == spv::Op::OpSampledImage) { + return false; + } + + // We cannot handle a sampled image load, because we re-work loads using + // conditional branches and OpPhi instructions, and the result type of OpPhi + // cannot be OpTypeSampledImage. + if (instruction.opcode() == spv::Op::OpLoad && + ir_context->get_def_use_mgr()->GetDef(instruction.type_id())->opcode() == + spv::Op::OpTypeSampledImage) { + return false; + } + + // We cannot handle instructions with an id which return a void type, if the + // result id is used in the module (e.g. a function call to a function that + // returns nothing). + if (instruction.HasResultId()) { + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + assert(type && "The type should be found in the module"); + + if (type->AsVoid() && + !ir_context->get_def_use_mgr()->WhileEachUse( + instruction.result_id(), + [](opt::Instruction* use_inst, uint32_t use_index) { + // Return false if the id is used as an input operand. + return use_index < + use_inst->NumOperands() - use_inst->NumInOperands(); + })) { + return false; + } + } + + return true; +} + +std::unordered_set +TransformationFlattenConditionalBranch::GetFreshIds() const { + std::unordered_set result = { + message_.fresh_id_for_bvec2_selector(), + message_.fresh_id_for_bvec3_selector(), + message_.fresh_id_for_bvec4_selector()}; + for (auto& side_effect_wrapper_info : message_.side_effect_wrapper_info()) { + result.insert(side_effect_wrapper_info.merge_block_id()); + result.insert(side_effect_wrapper_info.execute_block_id()); + result.insert(side_effect_wrapper_info.actual_result_id()); + result.insert(side_effect_wrapper_info.alternative_block_id()); + result.insert(side_effect_wrapper_info.placeholder_result_id()); + } + return result; +} + +uint32_t TransformationFlattenConditionalBranch::FindConvergenceBlock( + opt::IRContext* ir_context, const opt::BasicBlock& header_block) { + uint32_t result = header_block.terminator()->GetSingleWordInOperand(1); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(header_block.GetParent()); + while (!postdominator_analysis->Dominates(result, header_block.id())) { + auto current_block = ir_context->get_instr_block(result); + // If the transformation is applicable, the terminator is OpBranch. + result = current_block->terminator()->GetSingleWordInOperand(0); + } + return result; +} + +bool TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted( + opt::IRContext* ir_context) { + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: { + return true; + } + default: + return false; + } +} + +void TransformationFlattenConditionalBranch::AddBooleanVectorConstructorToBlock( + uint32_t fresh_id, uint32_t dimension, + const opt::Operand& branch_condition_operand, opt::IRContext* ir_context, + opt::BasicBlock* block) { + opt::Instruction::OperandList in_operands; + for (uint32_t i = 0; i < dimension; i++) { + in_operands.emplace_back(branch_condition_operand); + } + block->begin()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + fuzzerutil::MaybeGetVectorType( + ir_context, fuzzerutil::MaybeGetBoolType(ir_context), dimension), + fresh_id, in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_id); +} + +void TransformationFlattenConditionalBranch:: + RewriteOpPhiInstructionsAtConvergenceBlock( + const opt::BasicBlock& header_block, uint32_t convergence_block_id, + opt::IRContext* ir_context) const { + const opt::Instruction& branch_instruction = *header_block.terminator(); + + const opt::Operand& branch_condition_operand = + branch_instruction.GetInOperand(0); + + // If we encounter OpPhi instructions on vector types then we may need to + // introduce vector versions of the selection construct's condition to use + // in corresponding OpSelect instructions. These booleans track whether we + // need to introduce such boolean vectors. + bool require_2d_boolean_vector = false; + bool require_3d_boolean_vector = false; + bool require_4d_boolean_vector = false; + + // Consider every OpPhi instruction at the convergence block. + opt::BasicBlock* convergence_block = + ir_context->get_instr_block(convergence_block_id); + convergence_block->ForEachPhiInst( + [this, &branch_condition_operand, branch_instruction, + convergence_block_id, &header_block, ir_context, + &require_2d_boolean_vector, &require_3d_boolean_vector, + &require_4d_boolean_vector](opt::Instruction* phi_inst) { + assert(phi_inst->NumInOperands() == 4 && + "We are going to replace an OpPhi with an OpSelect. This " + "only makes sense if the block has two distinct " + "predecessors."); + // We are going to replace the OpPhi with an OpSelect. By default, + // the condition for the OpSelect will be the branch condition's + // operand. However, if the OpPhi has vector result type we may need + // to use a boolean vector as the condition instead. + opt::Operand selector_operand = branch_condition_operand; + opt::Instruction* type_inst = + ir_context->get_def_use_mgr()->GetDef(phi_inst->type_id()); + if (type_inst->opcode() == spv::Op::OpTypeVector) { + uint32_t dimension = type_inst->GetSingleWordInOperand(1); + switch (dimension) { + case 2: + // The OpPhi's result type is a 2D vector. If a fresh id for a + // bvec2 selector was provided then we should use it as the + // OpSelect's condition, and note the fact that we will need to + // add an instruction to bring this bvec2 into existence. + if (message_.fresh_id_for_bvec2_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec2_selector()}}; + require_2d_boolean_vector = true; + } + break; + case 3: + // Similar to the 2D case. + if (message_.fresh_id_for_bvec3_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec3_selector()}}; + require_3d_boolean_vector = true; + } + break; + case 4: + // Similar to the 2D case. + if (message_.fresh_id_for_bvec4_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec4_selector()}}; + require_4d_boolean_vector = true; + } + break; + default: + assert(dimension == 4 && "Invalid vector dimension."); + break; + } + } + std::vector operands; + operands.emplace_back(selector_operand); + + uint32_t branch_instruction_true_block_id = + branch_instruction.GetSingleWordInOperand(1); + uint32_t branch_instruction_false_block_id = + branch_instruction.GetSingleWordInOperand(2); + + // The OpPhi takes values from two distinct predecessors. One + // predecessor is associated with the "true" path of the conditional + // we are flattening, the other with the "false" path, but these + // predecessors can appear in either order as operands to the OpPhi + // instruction. We determine in which order the OpPhi inputs should + // appear as OpSelect arguments by first checking whether the + // convergence block is a direct successor of the selection header, and + // otherwise checking dominance of the true and false immediate + // successors of the header block. + if (branch_instruction_true_block_id == convergence_block_id) { + // The branch instruction's true block is the convergence block. This + // means that the OpPhi's value associated with the branch + // instruction's block should the "true" result of the OpSelect. + assert(branch_instruction_false_block_id != convergence_block_id && + "Control should not reach here if both branches target the " + "convergence block."); + if (phi_inst->GetSingleWordInOperand(1) == + message_.header_block_id()) { + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } else { + assert(phi_inst->GetSingleWordInOperand(3) == + message_.header_block_id() && + "Since the convergence block has the header block as one of " + "two predecessors, if it is not handled by the first pair " + "of operands of this OpPhi instruction it should be handled " + "by the second pair."); + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } + } else if (branch_instruction_false_block_id == convergence_block_id) { + // The branch instruction's false block is the convergence block. This + // means that the OpPhi's value associated with the branch + // instruction's block should the "false" result of the OpSelect. + if (phi_inst->GetSingleWordInOperand(1) == + message_.header_block_id()) { + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } else { + assert(phi_inst->GetSingleWordInOperand(3) == + message_.header_block_id() && + "Since the convergence block has the header block as one of " + "two predecessors, if it is not handled by the first pair " + "of operands of this OpPhi instruction it should be handled " + "by the second pair."); + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } + } else if (ir_context->GetDominatorAnalysis(header_block.GetParent()) + ->Dominates(branch_instruction_true_block_id, + phi_inst->GetSingleWordInOperand(1))) { + // The "true" branch of the conditional is handled first in the + // OpPhi's operands; we thus provide operands to OpSelect in the same + // order that they appear in the OpPhi. + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } else { + // The "false" branch of the conditional is handled first in the + // OpPhi's operands; we thus provide operands to OpSelect in reverse + // of the order that they appear in the OpPhi. + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } + phi_inst->SetOpcode(spv::Op::OpSelect); + phi_inst->SetInOperands(std::move(operands)); + }); + + // Add boolean vector instructions to the start of the block as required. + if (require_2d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec2_selector(), + 2, branch_condition_operand, ir_context, + convergence_block); + } + if (require_3d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec3_selector(), + 3, branch_condition_operand, ir_context, + convergence_block); + } + if (require_4d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec4_selector(), + 4, branch_condition_operand, ir_context, + convergence_block); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h new file mode 100644 index 00000000..4efff670 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h @@ -0,0 +1,159 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ +#define SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationFlattenConditionalBranch : public Transformation { + public: + explicit TransformationFlattenConditionalBranch( + protobufs::TransformationFlattenConditionalBranch message); + + TransformationFlattenConditionalBranch( + uint32_t header_block_id, bool true_branch_first, + uint32_t fresh_id_for_bvec2_selector, + uint32_t fresh_id_for_bvec3_selector, + uint32_t fresh_id_for_bvec4_selector, + const std::vector& + side_effect_wrappers_info); + + // - |message_.header_block_id| must be the label id of a reachable selection + // header, which ends with an OpBranchConditional instruction. + // - The condition of the OpBranchConditional instruction must not be an + // irrelevant id. + // - The header block and the merge block must describe a single-entry, + // single-exit region. + // - The region must not contain barrier or OpSampledImage instructions. + // - The region must not contain selection or loop constructs. + // - The region must not define ids that are the base objects for existing + // synonym facts. + // - For each instruction that requires additional fresh ids, then: + // - if the instruction is mapped to the required ids for enclosing it by + // |message_.side_effect_wrapper_info|, these must be valid (the + // fresh ids must be non-zero, fresh and distinct); + // - if there is no such mapping, the transformation context must have + // overflow ids available. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Flattens the selection construct with header |message_.header_block_id|, + // changing any OpPhi in the block where the flow converges to OpSelect and + // enclosing any instruction with side effects in conditionals so that + // they are only executed when they should. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the conditional headed by |header| can be flattened, + // according to the conditions of the IsApplicable method, assuming that + // enough fresh ids would be provided. In this case, it fills the + // |instructions_that_need_ids| set with all the instructions that would + // require fresh ids. + // Returns false otherwise. + // Assumes that |header| is the header of a conditional, so its last two + // instructions are OpSelectionMerge and OpBranchConditional. + static bool GetProblematicInstructionsIfConditionalCanBeFlattened( + opt::IRContext* ir_context, opt::BasicBlock* header, + const TransformationContext& transformation_context, + std::set* instructions_that_need_ids); + + // Returns true iff the given instruction needs a placeholder to be enclosed + // inside a conditional. So, it returns: + // - true if the instruction has a non-void result id, + // - false if the instruction does not have a result id or has a void result + // id. + // Assumes that the instruction has side effects, requiring it to be enclosed + // inside a conditional, and that it can be enclosed inside a conditional + // keeping the module valid. Assumes that, if the instruction has a void + // result type, its result id is not used in the module. + static bool InstructionNeedsPlaceholder(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + // Returns true if and only if the SPIR-V version is such that the arguments + // to OpSelect are restricted to only scalars, pointers (if the appropriate + // capability is enabled) and component-wise vectors. + static bool OpSelectArgumentsAreRestricted(opt::IRContext* ir_context); + + // Find the first block where flow converges (it is not necessarily the merge + // block) by walking the true branch until reaching a block that post- + // dominates the header. + // This is necessary because a potential common set of blocks at the end of + // the construct should not be duplicated. + static uint32_t FindConvergenceBlock(opt::IRContext* ir_context, + const opt::BasicBlock& header_block); + + private: + // Returns an unordered_map mapping instructions to the info required to + // enclose them inside a conditional. It maps the instructions to the + // corresponding entry in |message_.side_effect_wrapper_info|. + std::unordered_map + GetInstructionsToWrapperInfo(opt::IRContext* ir_context) const; + + // Splits the given block, adding a new selection construct so that the given + // instruction is only executed if the boolean value of |condition_id| matches + // the value of |exec_if_cond_true|. + // Assumes that all parameters are consistent. + // 2 fresh ids are required if the instruction does not have a result id (the + // first two ids in |wrapper_info| must be valid fresh ids), 5 otherwise. + // Returns the merge block created. + // + // |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks + // and instructions for which dead block and irrelevant id facts should + // ultimately be created. + static opt::BasicBlock* EncloseInstructionInConditional( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::BasicBlock* block, opt::Instruction* instruction, + const protobufs::SideEffectWrapperInfo& wrapper_info, + uint32_t condition_id, bool exec_if_cond_true, + std::vector* dead_blocks, + std::vector* irrelevant_ids); + + // Turns every OpPhi instruction of |convergence_block| -- the convergence + // block for |header_block| (both in |ir_context|) into an OpSelect + // instruction. + void RewriteOpPhiInstructionsAtConvergenceBlock( + const opt::BasicBlock& header_block, uint32_t convergence_block_id, + opt::IRContext* ir_context) const; + + // Adds an OpCompositeExtract instruction to the start of |block| in + // |ir_context|, with result id given by |fresh_id|. The instruction will + // make a |dimension|-dimensional boolean vector with + // |branch_condition_operand| at every component. + static void AddBooleanVectorConstructorToBlock( + uint32_t fresh_id, uint32_t dimension, + const opt::Operand& branch_condition_operand, opt::IRContext* ir_context, + opt::BasicBlock* block); + + // Returns true if the given instruction either has no side effects or it can + // be handled by being enclosed in a conditional. + static bool InstructionCanBeHandled(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + protobufs::TransformationFlattenConditionalBranch message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.cpp new file mode 100644 index 00000000..e96a230c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_function_call.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationFunctionCall::TransformationFunctionCall( + protobufs::TransformationFunctionCall message) + : message_(std::move(message)) {} + +TransformationFunctionCall::TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_callee_id(callee_id); + for (auto argument : argument_id) { + message_.add_argument_id(argument); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationFunctionCall::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The result id must be fresh + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The function must exist + auto callee_inst = + ir_context->get_def_use_mgr()->GetDef(message_.callee_id()); + if (!callee_inst || callee_inst->opcode() != spv::Op::OpFunction) { + return false; + } + + // The function must not be an entry point + if (fuzzerutil::FunctionIsEntryPoint(ir_context, message_.callee_id())) { + return false; + } + + auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef( + callee_inst->GetSingleWordInOperand(1)); + assert(callee_type_inst->opcode() == spv::Op::OpTypeFunction && + "Bad function type."); + + // The number of expected function arguments must match the number of given + // arguments. The number of expected arguments is one less than the function + // type's number of input operands, as one operand is for the return type. + if (callee_type_inst->NumInOperands() - 1 != + static_cast(message_.argument_id().size())) { + return false; + } + + // The instruction descriptor must refer to a position where it is valid to + // insert the call + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpFunctionCall, + insert_before)) { + return false; + } + + auto block = ir_context->get_instr_block(insert_before); + auto enclosing_function = block->GetParent(); + + // If the block is not dead, the function must be livesafe + bool block_is_dead = + transformation_context.GetFactManager()->BlockIsDead(block->id()); + if (!block_is_dead && + !transformation_context.GetFactManager()->FunctionIsLivesafe( + message_.callee_id())) { + return false; + } + + // The ids must all match and have the right types and satisfy rules on + // pointers. If the block is not dead, pointers must be arbitrary. + for (uint32_t arg_index = 0; + arg_index < static_cast(message_.argument_id().size()); + arg_index++) { + opt::Instruction* arg_inst = + ir_context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index)); + if (!arg_inst) { + // The given argument does not correspond to an instruction. + return false; + } + if (!arg_inst->type_id()) { + // The given argument does not have a type; it is thus not suitable. + } + if (arg_inst->type_id() != + callee_type_inst->GetSingleWordInOperand(arg_index + 1)) { + // Argument type mismatch. + return false; + } + opt::Instruction* arg_type_inst = + ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id()); + if (arg_type_inst->opcode() == spv::Op::OpTypePointer) { + switch (arg_inst->opcode()) { + case spv::Op::OpFunctionParameter: + case spv::Op::OpVariable: + // These are OK + break; + default: + // Other pointer ids cannot be passed as parameters + return false; + } + if (!block_is_dead && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + arg_inst->result_id())) { + // This is not a dead block, so pointer parameters passed to the called + // function might really have their contents modified. We thus require + // such pointers to be to arbitrary-valued variables, which this is not. + return false; + } + } + + // The argument id needs to be available (according to dominance rules) at + // the point where the call will occur. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + arg_inst->result_id())) { + return false; + } + } + + // Introducing the call must not lead to recursion. + if (message_.callee_id() == enclosing_function->result_id()) { + // This would be direct recursion. + return false; + } + // Ensure the call would not lead to indirect recursion. + return !CallGraph(ir_context) + .GetIndirectCallees(message_.callee_id()) + .count(block->GetParent()->result_id()); +} + +void TransformationFunctionCall::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Update the module's bound to reflect the fresh id for the result of the + // function call. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Get the return type of the function being called. + uint32_t return_type = + ir_context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id(); + // Populate the operands to the call instruction, with the function id and the + // arguments. + opt::Instruction::OperandList operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}}); + for (auto arg : message_.argument_id()) { + operands.push_back({SPV_OPERAND_TYPE_ID, {arg}}); + } + // Insert the function call before the instruction specified in the message. + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFunctionCall, return_type, message_.fresh_id(), + operands)); + // Invalidate all analyses since we have changed the module. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationFunctionCall::ToMessage() const { + protobufs::Transformation result; + *result.mutable_function_call() = message_; + return result; +} + +std::unordered_set TransformationFunctionCall::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.h new file mode 100644 index 00000000..a2aaf365 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_function_call.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationFunctionCall : public Transformation { + public: + explicit TransformationFunctionCall( + protobufs::TransformationFunctionCall message); + + TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an OpFunctionCall can be legitimately inserted + // - |message_.function_id| must be the id of a function, and calling the + // function before the identified instruction must not introduce recursion + // - |message_.arg_id| must provide suitable arguments for the function call + // (they must have the right types and be available according to dominance + // rules) + // - If the insertion point is not in a dead block then |message_function_id| + // must refer to a livesafe function, and every pointer argument in + // |message_.arg_id| must refer to an arbitrary-valued variable + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...| + // before |instruction_to_insert_before|, where %type is the return type of + // |callee_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationFunctionCall message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.cpp new file mode 100644 index 00000000..69e88fd5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.cpp @@ -0,0 +1,366 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_inline_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationInlineFunction::TransformationInlineFunction( + protobufs::TransformationInlineFunction message) + : message_(std::move(message)) {} + +TransformationInlineFunction::TransformationInlineFunction( + uint32_t function_call_id, + const std::map& result_id_map) { + message_.set_function_call_id(function_call_id); + *message_.mutable_result_id_map() = + fuzzerutil::MapToRepeatedUInt32Pair(result_id_map); +} + +bool TransformationInlineFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The values in the |message_.result_id_map| must be all fresh and all + // distinct. + const auto result_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.result_id_map()); + std::set ids_used_by_this_transformation; + for (auto& pair : result_id_map) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + // |function_call_instruction| must be suitable for inlining. + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + if (!IsSuitableForInlining(ir_context, function_call_instruction)) { + return false; + } + + // |function_call_instruction| must be the penultimate instruction in its + // block and its block termination instruction must be an OpBranch. This + // avoids the case where the penultimate instruction is an OpLoopMerge, which + // would make the back-edge block not branch to the loop header. + auto* function_call_instruction_block = + ir_context->get_instr_block(function_call_instruction); + if (function_call_instruction != + &*--function_call_instruction_block->tail() || + function_call_instruction_block->terminator()->opcode() != + spv::Op::OpBranch) { + return false; + } + + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + for (auto& block : *called_function) { + // Since the entry block label will not be inlined, only the remaining + // labels must have a corresponding value in the map. + if (&block != &*called_function->entry() && + !result_id_map.count(block.id()) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + + // |result_id_map| must have an entry for every result id in the called + // function. + for (auto& instruction : block) { + // If |instruction| has result id, then it must have a mapped id in + // |result_id_map|. + if (instruction.HasResultId() && + !result_id_map.count(instruction.result_id()) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + } + + // |result_id_map| must not contain an entry for any parameter of the function + // that is being inlined. + bool found_entry_for_parameter = false; + called_function->ForEachParam( + [&result_id_map, &found_entry_for_parameter](opt::Instruction* param) { + if (result_id_map.count(param->result_id())) { + found_entry_for_parameter = true; + } + }); + return !found_entry_for_parameter; +} + +void TransformationInlineFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + auto* caller_function = + ir_context->get_instr_block(function_call_instruction)->GetParent(); + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + std::map result_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.result_id_map()); + + // If there are gaps in the result id map, fill them using overflow ids. + for (auto& block : *called_function) { + if (&block != &*called_function->entry() && + !result_id_map.count(block.id())) { + result_id_map.insert( + {block.id(), + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + for (auto& instruction : block) { + // If |instruction| has result id, then it must have a mapped id in + // |result_id_map|. + if (instruction.HasResultId() && + !result_id_map.count(instruction.result_id())) { + result_id_map.insert({instruction.result_id(), + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + } + } + + auto* successor_block = ir_context->cfg()->block( + ir_context->get_instr_block(function_call_instruction) + ->terminator() + ->GetSingleWordInOperand(0)); + + // Inline the |called_function| entry block. + for (auto& entry_block_instruction : *called_function->entry()) { + opt::Instruction* inlined_instruction; + + if (entry_block_instruction.opcode() == spv::Op::OpVariable) { + // All OpVariable instructions in a function must be in the first block + // in the function. + inlined_instruction = caller_function->begin()->begin()->InsertBefore( + std::unique_ptr( + entry_block_instruction.Clone(ir_context))); + } else { + inlined_instruction = function_call_instruction->InsertBefore( + std::unique_ptr( + entry_block_instruction.Clone(ir_context))); + } + + AdaptInlinedInstruction(result_id_map, ir_context, inlined_instruction); + } + + // If the function call's successor block contains OpPhi instructions that + // refer to the block containing the call then these will need to be rewritten + // to instead refer to the block associated with "returning" from the inlined + // function, as this block will be the predecessor of what used to be the + // function call's successor block. We look out for this block. + uint32_t new_return_block_id = 0; + + // Inline the |called_function| non-entry blocks. + for (auto& block : *called_function) { + if (&block == &*called_function->entry()) { + continue; + } + + // Check whether this is the function's return block. Take note if it is, + // so that OpPhi instructions in the successor of the original function call + // block can be re-written. + if (block.terminator()->IsReturn()) { + assert(new_return_block_id == 0 && + "There should be only one return block."); + new_return_block_id = result_id_map.at(block.id()); + } + + auto* cloned_block = block.Clone(ir_context); + cloned_block = caller_function->InsertBasicBlockBefore( + std::unique_ptr(cloned_block), successor_block); + cloned_block->GetLabel()->SetResultId(result_id_map.at(cloned_block->id())); + fuzzerutil::UpdateModuleIdBound(ir_context, cloned_block->id()); + + for (auto& inlined_instruction : *cloned_block) { + AdaptInlinedInstruction(result_id_map, ir_context, &inlined_instruction); + } + } + + opt::BasicBlock* block_containing_function_call = + ir_context->get_instr_block(function_call_instruction); + + assert(((new_return_block_id == 0) == + called_function->entry()->terminator()->IsReturn()) && + "We should have found a return block unless the function being " + "inlined returns in its first block."); + if (new_return_block_id != 0) { + // Rewrite any OpPhi instructions in the successor block so that they refer + // to the new return block instead of the block that originally contained + // the function call. + ir_context->get_def_use_mgr()->ForEachUse( + block_containing_function_call->id(), + [ir_context, new_return_block_id, successor_block]( + opt::Instruction* use_instruction, uint32_t operand_index) { + if (use_instruction->opcode() == spv::Op::OpPhi && + ir_context->get_instr_block(use_instruction) == successor_block) { + use_instruction->SetOperand(operand_index, {new_return_block_id}); + } + }); + } + + // Removes the function call instruction and its block termination instruction + // from |caller_function|. + ir_context->KillInst(block_containing_function_call->terminator()); + ir_context->KillInst(function_call_instruction); + + // Since the SPIR-V module has changed, no analyses must be validated. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationInlineFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_inline_function() = message_; + return result; +} + +bool TransformationInlineFunction::IsSuitableForInlining( + opt::IRContext* ir_context, opt::Instruction* function_call_instruction) { + // |function_call_instruction| must be defined and must be an OpFunctionCall + // instruction. + if (!function_call_instruction || + function_call_instruction->opcode() != spv::Op::OpFunctionCall) { + return false; + } + + // If |function_call_instruction| return type is void, then + // |function_call_instruction| must not have uses. + if (ir_context->get_type_mgr() + ->GetType(function_call_instruction->type_id()) + ->AsVoid() && + ir_context->get_def_use_mgr()->NumUses(function_call_instruction) != 0) { + return false; + } + + // |called_function| must not have an early return. + auto called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + if (called_function->HasEarlyReturn()) { + return false; + } + + // |called_function| must not use OpKill or OpUnreachable. + if (fuzzerutil::FunctionContainsOpKillOrUnreachable(*called_function)) { + return false; + } + + return true; +} + +void TransformationInlineFunction::AdaptInlinedInstruction( + const std::map& result_id_map, + opt::IRContext* ir_context, + opt::Instruction* instruction_to_be_inlined) const { + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + + const auto* function_call_block = + ir_context->get_instr_block(function_call_instruction); + assert(function_call_block && "OpFunctionCall must belong to some block"); + + // Replaces the operand ids with their mapped result ids. + instruction_to_be_inlined->ForEachInId( + [called_function, function_call_instruction, &result_id_map, + function_call_block](uint32_t* id) { + // We are not inlining the entry block of the |called_function|. + // + // We must check this condition first since we can't use the fresh id + // from |result_id_map| even if it has one. This is because that fresh + // id will never be added to the module since entry blocks are not + // inlined. + if (*id == called_function->entry()->id()) { + *id = function_call_block->id(); + return; + } + + // If |id| is mapped, then set it to its mapped value. + if (result_id_map.count(*id)) { + *id = result_id_map.at(*id); + return; + } + + uint32_t parameter_index = 0; + called_function->ForEachParam( + [id, function_call_instruction, + ¶meter_index](opt::Instruction* parameter_instruction) { + // If the id is a function parameter, then set it to the + // parameter value passed in the function call instruction. + if (*id == parameter_instruction->result_id()) { + // We do + 1 because the first in-operand for OpFunctionCall is + // the function id that is being called. + *id = function_call_instruction->GetSingleWordInOperand( + parameter_index + 1); + } + parameter_index++; + }); + }); + + // If |instruction_to_be_inlined| has result id, then set it to its mapped + // value. + if (instruction_to_be_inlined->HasResultId()) { + assert(result_id_map.count(instruction_to_be_inlined->result_id()) && + "Result id must be mapped to a fresh id."); + instruction_to_be_inlined->SetResultId( + result_id_map.at(instruction_to_be_inlined->result_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, + instruction_to_be_inlined->result_id()); + } + + // The return instruction will be changed into an OpBranch to the basic + // block that follows the block containing the function call. + if (spvOpcodeIsReturn(instruction_to_be_inlined->opcode())) { + uint32_t successor_block_id = + ir_context->get_instr_block(function_call_instruction) + ->terminator() + ->GetSingleWordInOperand(0); + switch (instruction_to_be_inlined->opcode()) { + case spv::Op::OpReturn: + instruction_to_be_inlined->AddOperand( + {SPV_OPERAND_TYPE_ID, {successor_block_id}}); + break; + case spv::Op::OpReturnValue: { + instruction_to_be_inlined->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCopyObject, + function_call_instruction->type_id(), + function_call_instruction->result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {instruction_to_be_inlined->GetSingleWordOperand(0)}}}))); + instruction_to_be_inlined->SetInOperand(0, {successor_block_id}); + break; + } + default: + break; + } + instruction_to_be_inlined->SetOpcode(spv::Op::OpBranch); + } +} + +std::unordered_set TransformationInlineFunction::GetFreshIds() const { + std::unordered_set result; + for (auto& pair : message_.result_id_map()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.h new file mode 100644 index 00000000..f4dc410a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_inline_function.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationInlineFunction : public Transformation { + public: + explicit TransformationInlineFunction( + protobufs::TransformationInlineFunction message); + + TransformationInlineFunction( + uint32_t function_call_id, + const std::map& result_id_map); + + // - |message_.result_id_map| must map the instructions of the called function + // to fresh ids, unless overflow ids are available. + // - |message_.function_call_id| must be an OpFunctionCall instruction. + // It must not have an early return and must not use OpUnreachable or + // OpKill. This is to guard against making the module invalid when the + // caller is inside a continue construct. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3735): + // Allow functions that use OpKill or OpUnreachable to be inlined if the + // function call is not part of a continue construct. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpFunctionCall instruction, identified by + // |message_.function_call_id|, with a copy of the function's body. + // |message_.result_id_map| is used to provide fresh ids for duplicate + // instructions. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |function_call_instruction| is defined, is an + // OpFunctionCall instruction, has no uses if its return type is void, has no + // early returns and has no uses of OpKill or OpUnreachable. + static bool IsSuitableForInlining( + opt::IRContext* ir_context, opt::Instruction* function_call_instruction); + + private: + protobufs::TransformationInlineFunction message_; + + // Inline |instruction_to_be_inlined| by setting its ids to the corresponding + // ids in |result_id_map|. + void AdaptInlinedInstruction( + const std::map& result_id_map, + opt::IRContext* ir_context, opt::Instruction* instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp new file mode 100644 index 00000000..49801e39 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_invert_comparison_operator.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message) + : message_(std::move(message)) {} + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + uint32_t operator_id, uint32_t fresh_id) { + message_.set_operator_id(operator_id); + message_.set_fresh_id(fresh_id); +} + +bool TransformationInvertComparisonOperator::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.operator_id| must be valid and inversion must be supported for + // it. + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + if (!inst || !IsInversionSupported(inst->opcode())) { + return false; + } + + // Check that we can insert negation instruction. + auto* block = ir_context->get_instr_block(inst); + assert(block && "Instruction must have a basic block"); + + auto iter = fuzzerutil::GetIteratorForInstruction(block, inst); + ++iter; + assert(iter != block->end() && "Instruction can't be the last in the block"); + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot, + iter) && + "Can't insert negation after comparison operator"); + + // |message_.fresh_id| must be fresh. + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); +} + +void TransformationInvertComparisonOperator::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + assert(inst && "Result id of an operator is invalid"); + + // Insert negation after |inst|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + ++iter; + + iter.InsertBefore(MakeUnique( + ir_context, spv::Op::OpLogicalNot, inst->type_id(), inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})); + + // Change the result id of the original operator to |fresh_id|. + inst->SetResultId(message_.fresh_id()); + + // Invert the operator. + inst->SetOpcode(InvertOpcode(inst->opcode())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +bool TransformationInvertComparisonOperator::IsInversionSupported( + spv::Op opcode) { + switch (opcode) { + case spv::Op::OpSGreaterThan: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpSLessThan: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + return true; + default: + return false; + } +} + +spv::Op TransformationInvertComparisonOperator::InvertOpcode(spv::Op opcode) { + assert(IsInversionSupported(opcode) && "Inversion must be supported"); + + switch (opcode) { + case spv::Op::OpSGreaterThan: + return spv::Op::OpSLessThanEqual; + case spv::Op::OpSGreaterThanEqual: + return spv::Op::OpSLessThan; + case spv::Op::OpSLessThan: + return spv::Op::OpSGreaterThanEqual; + case spv::Op::OpSLessThanEqual: + return spv::Op::OpSGreaterThan; + case spv::Op::OpUGreaterThan: + return spv::Op::OpULessThanEqual; + case spv::Op::OpUGreaterThanEqual: + return spv::Op::OpULessThan; + case spv::Op::OpULessThan: + return spv::Op::OpUGreaterThanEqual; + case spv::Op::OpULessThanEqual: + return spv::Op::OpUGreaterThan; + case spv::Op::OpIEqual: + return spv::Op::OpINotEqual; + case spv::Op::OpINotEqual: + return spv::Op::OpIEqual; + case spv::Op::OpFOrdEqual: + return spv::Op::OpFUnordNotEqual; + case spv::Op::OpFUnordEqual: + return spv::Op::OpFOrdNotEqual; + case spv::Op::OpFOrdNotEqual: + return spv::Op::OpFUnordEqual; + case spv::Op::OpFUnordNotEqual: + return spv::Op::OpFOrdEqual; + case spv::Op::OpFOrdLessThan: + return spv::Op::OpFUnordGreaterThanEqual; + case spv::Op::OpFUnordLessThan: + return spv::Op::OpFOrdGreaterThanEqual; + case spv::Op::OpFOrdLessThanEqual: + return spv::Op::OpFUnordGreaterThan; + case spv::Op::OpFUnordLessThanEqual: + return spv::Op::OpFOrdGreaterThan; + case spv::Op::OpFOrdGreaterThan: + return spv::Op::OpFUnordLessThanEqual; + case spv::Op::OpFUnordGreaterThan: + return spv::Op::OpFOrdLessThanEqual; + case spv::Op::OpFOrdGreaterThanEqual: + return spv::Op::OpFUnordLessThan; + case spv::Op::OpFUnordGreaterThanEqual: + return spv::Op::OpFOrdLessThan; + default: + // The program will fail in the debug mode because of the assertion + // at the beginning of the function. + return spv::Op::OpNop; + } +} + +protobufs::Transformation TransformationInvertComparisonOperator::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_invert_comparison_operator() = message_; + return result; +} + +std::unordered_set +TransformationInvertComparisonOperator::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h new file mode 100644 index 00000000..39c2fe0f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationInvertComparisonOperator : public Transformation { + public: + explicit TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message); + + TransformationInvertComparisonOperator(uint32_t operator_id, + uint32_t fresh_id); + + // - |operator_id| should be a result id of some instruction for which + // IsInversionSupported returns true. + // - |fresh_id| must be a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inverts the opcode of the instruction with result id |operator_id| (e.g >= + // becomes <) and inserts OpLogicalNot instruction after |operator_id|. Also, + // changes the result id of OpLogicalNot to |operator_id| and the result id of + // the inverted operator to |fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |opcode| is supported by this transformation. + static bool IsInversionSupported(spv::Op opcode); + + private: + // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.) + static spv::Op InvertOpcode(spv::Op opcode); + + protobufs::TransformationInvertComparisonOperator message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.cpp new file mode 100644 index 00000000..1cfde770 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationLoad::TransformationLoad(protobufs::TransformationLoad message) + : message_(std::move(message)) {} + +TransformationLoad::TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); + + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationLoad::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The pointer must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer type. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != spv::Op::OpTypePointer) { + return false; + } + // We do not want to allow loading from null or undefined pointers, as it is + // not clear how punishing the consequences of doing so are from a semantics + // point of view. + switch (pointer->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a load before it. + if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpLoad, insert_before)) { + return false; + } + + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpAtomicLoad, insert_before)) { + return false; + } + + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != spv::Op::OpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != spv::Op::OpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != spv::Op::OpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of spv::Scope::Invocation. + auto memory_scope_const_value = + spv::Scope(memory_scope_instruction->GetSingleWordInOperand(0)); + if (memory_scope_const_value != spv::Scope::Invocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + + // The pointer needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.pointer_id()); +} + +void TransformationLoad::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + if (message_.is_atomic()) { + // OpAtomicLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpAtomicLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } else { + // OpLoad instruction. + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } +} + +protobufs::Transformation TransformationLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_load() = message_; + return result; +} + +std::unordered_set TransformationLoad::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.h new file mode 100644 index 00000000..66229185 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_load.h @@ -0,0 +1,72 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ +#define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationLoad : public Transformation { + public: + explicit TransformationLoad(protobufs::TransformationLoad message); + + TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, bool is_atomic, + uint32_t memory_scope, uint32_t memory_semantics, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.pointer_id| must be the id of a pointer + // - |message_.is_atomic| must be true if want to work with OpAtomicLoad + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // spv::Scope::Invocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpLoad, and where + // |message_.pointer_id| is available (according to dominance rules) + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpLoad %type |message_.pointer_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|, where %type is the pointer's + // pointee type. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp new file mode 100644 index 00000000..ecc8dcac --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationMakeVectorOperationDynamic:: + TransformationMakeVectorOperationDynamic( + protobufs::TransformationMakeVectorOperationDynamic message) + : message_(std::move(message)) {} + +TransformationMakeVectorOperationDynamic:: + TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, + uint32_t constant_index_id) { + message_.set_instruction_result_id(instruction_result_id); + message_.set_constant_index_id(constant_index_id); +} + +bool TransformationMakeVectorOperationDynamic::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |instruction| must be a vector operation. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + if (!IsVectorOperation(ir_context, instruction)) { + return false; + } + + // |constant_index_instruction| must be defined as an integer instruction. + auto constant_index_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.constant_index_id()); + if (!constant_index_instruction || !constant_index_instruction->type_id() || + !ir_context->get_type_mgr() + ->GetType(constant_index_instruction->type_id()) + ->AsInteger()) { + return false; + } + + return true; +} + +void TransformationMakeVectorOperationDynamic::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // The OpVectorInsertDynamic instruction has the vector and component operands + // in reverse order in relation to the OpCompositeInsert corresponding + // operands. + if (instruction->opcode() == spv::Op::OpCompositeInsert) { + std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); + } + + // Sets the literal operand to the equivalent constant. + instruction->SetInOperand( + instruction->opcode() == spv::Op::OpCompositeExtract ? 1 : 2, + {message_.constant_index_id()}); + + // Sets the |instruction| opcode to the corresponding vector dynamic opcode. + instruction->SetOpcode(instruction->opcode() == spv::Op::OpCompositeExtract + ? spv::Op::OpVectorExtractDynamic + : spv::Op::OpVectorInsertDynamic); +} + +protobufs::Transformation TransformationMakeVectorOperationDynamic::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_make_vector_operation_dynamic() = message_; + return result; +} + +bool TransformationMakeVectorOperationDynamic::IsVectorOperation( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // |instruction| must be defined and must be an OpCompositeExtract/Insert + // instruction. + if (!instruction || (instruction->opcode() != spv::Op::OpCompositeExtract && + instruction->opcode() != spv::Op::OpCompositeInsert)) { + return false; + } + + // The composite must be a vector. + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction->GetSingleWordInOperand( + instruction->opcode() == spv::Op::OpCompositeExtract ? 0 : 1)); + if (!ir_context->get_type_mgr() + ->GetType(composite_instruction->type_id()) + ->AsVector()) { + return false; + } + + return true; +} + +std::unordered_set +TransformationMakeVectorOperationDynamic::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h new file mode 100644 index 00000000..e444f40c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMakeVectorOperationDynamic : public Transformation { + public: + explicit TransformationMakeVectorOperationDynamic( + protobufs::TransformationMakeVectorOperationDynamic message); + + TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, + uint32_t constant_index_id); + + // - |message_.instruction_result_id| must be the result id of an + // OpCompositeExtract/Insert instruction such that the composite operand is a + // vector. + // - |message_.constant_index_id| must be the result id of an integer + // instruction such that its value equals the indexing literal of the + // OpCompositeExtract/Insert instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpCompositeExtract and OpCompositeInsert instructions with the + // OpVectorExtractDynamic and OpVectorInsertDynamic instructions. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks |instruction| is defined, is an OpCompositeExtract/Insert + // instruction and the composite operand is a vector. + static bool IsVectorOperation(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationMakeVectorOperationDynamic message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.cpp new file mode 100644 index 00000000..dbf782e2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_blocks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/block_merge_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationMergeBlocks::TransformationMergeBlocks( + protobufs::TransformationMergeBlocks message) + : message_(std::move(message)) {} + +TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) { + message_.set_block_id(block_id); +} + +bool TransformationMergeBlocks::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + // The given block must exist. + if (!second_block) { + return false; + } + // The block must have just one predecessor. + auto predecessors = ir_context->cfg()->preds(second_block->id()); + if (predecessors.size() != 1) { + return false; + } + auto first_block = ir_context->cfg()->block(predecessors.at(0)); + + if (!ir_context->IsReachable(*first_block)) { + return false; + } + return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block); +} + +void TransformationMergeBlocks::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + auto first_block = ir_context->cfg()->block( + ir_context->cfg()->preds(second_block->id()).at(0)); + + auto function = first_block->GetParent(); + // We need an iterator pointing to the predecessor, hence the loop. + for (auto bi = function->begin(); bi != function->end(); ++bi) { + if (bi->id() == first_block->id()) { + assert(opt::blockmergeutil::CanMergeWithSuccessor(ir_context, &*bi) && + "Because 'Apply' should only be invoked if 'IsApplicable' holds, " + "it must be possible to merge |bi| with its successor."); + opt::blockmergeutil::MergeWithSuccessor(ir_context, function, bi); + // Invalidate all analyses, since we have changed the module + // significantly. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return; + } + } + assert(false && + "Control should not reach here - we should always find the desired " + "block"); +} + +protobufs::Transformation TransformationMergeBlocks::ToMessage() const { + protobufs::Transformation result; + *result.mutable_merge_blocks() = message_; + return result; +} + +std::unordered_set TransformationMergeBlocks::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.h new file mode 100644 index 00000000..f3462fae --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_blocks.h @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMergeBlocks : public Transformation { + public: + explicit TransformationMergeBlocks( + protobufs::TransformationMergeBlocks message); + + TransformationMergeBlocks(uint32_t block_id); + + // - |message_.block_id| must be the id of a block, b + // - b must be statically reachable in the control flow graph of its function + // - b must have a single predecessor, a + // - b must be the sole successor of a + // - Replacing a with the merge of a and b (and removing b) must lead to a + // valid module + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The contents of b are merged into a, and a's terminator is replaced with + // the terminator of b. Block b is removed from the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationMergeBlocks message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp new file mode 100644 index 00000000..b35e358d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp @@ -0,0 +1,849 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_function_returns.h" + +#include "source/fuzz/comparator_deep_blocks_first.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( + protobufs::TransformationMergeFunctionReturns message) + : message_(std::move(message)) {} + +TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, + uint32_t return_val_id, uint32_t any_returnable_val_id, + const std::vector& returns_merging_info) { + message_.set_function_id(function_id); + message_.set_outer_header_id(outer_header_id); + message_.set_unreachable_continue_id(unreachable_continue_id); + message_.set_outer_return_id(outer_return_id); + message_.set_return_val_id(return_val_id); + message_.set_any_returnable_val_id(any_returnable_val_id); + for (const auto& return_merging_info : returns_merging_info) { + *message_.add_return_merging_info() = return_merging_info; + } +} + +bool TransformationMergeFunctionReturns::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto function = ir_context->GetFunction(message_.function_id()); + // The function must exist. + if (!function) { + return false; + } + + // The entry block must end in an unconditional branch. + if (function->entry()->terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + + // The module must contain an OpConstantTrue instruction. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + true, false)) { + return false; + } + + // The module must contain an OpConstantFalse instruction. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + false, false)) { + return false; + } + + // Check that the fresh ids provided are fresh and distinct. + std::set used_fresh_ids; + for (uint32_t id : + {message_.outer_header_id(), message_.unreachable_continue_id(), + message_.outer_return_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &used_fresh_ids)) { + return false; + } + } + + // Check the additional fresh id required if the function is not void. + auto function_type = ir_context->get_type_mgr()->GetType(function->type_id()); + assert(function_type && "The function type should always exist."); + + if (!function_type->AsVoid() && + (!message_.return_val_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + message_.return_val_id(), ir_context, &used_fresh_ids))) { + return false; + } + + // Get a map from the types for which ids are available at the end of the + // entry block to one of the ids with that type. We compute this here to avoid + // potentially doing it multiple times later on. + auto types_to_available_ids = + GetTypesToIdAvailableAfterEntryBlock(ir_context); + + // Get the reachable return blocks. + auto return_blocks = + fuzzerutil::GetReachableReturnBlocks(ir_context, message_.function_id()); + + // Map each merge block of loops containing reachable return blocks to the + // corresponding returning predecessors (all the blocks that, at the end of + // the transformation, will branch to the merge block because the function is + // returning). + std::map> merge_blocks_to_returning_preds; + for (uint32_t block : return_blocks) { + uint32_t merge_block = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(block); + + while (merge_block != 0) { + // If we have seen this merge block before, update the corresponding set + // and break out of the loop. + if (merge_blocks_to_returning_preds.count(merge_block)) { + merge_blocks_to_returning_preds[merge_block].emplace(block); + break; + } + + // If we have not seen this merge block before, add a new entry and walk + // up the loop tree. + merge_blocks_to_returning_preds.emplace(merge_block, + std::set({block})); + + // Walk up the loop tree. + block = merge_block; + merge_block = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(merge_block); + } + } + + // Instructions in the relevant merge blocks must be restricted to OpLabel, + // OpPhi and OpBranch. + for (const auto& merge_block_entry : merge_blocks_to_returning_preds) { + uint32_t merge_block = merge_block_entry.first; + bool all_instructions_allowed = + ir_context->get_instr_block(merge_block) + ->WhileEachInst([](opt::Instruction* inst) { + return inst->opcode() == spv::Op::OpLabel || + inst->opcode() == spv::Op::OpPhi || + inst->opcode() == spv::Op::OpBranch; + }); + if (!all_instructions_allowed) { + return false; + } + } + + auto merge_blocks_to_info = GetMappingOfMergeBlocksToInfo(); + + // For each relevant merge block, check that the correct ids are available. + for (const auto& merge_block_entry : merge_blocks_to_returning_preds) { + if (!CheckThatTheCorrectIdsAreGivenForMergeBlock( + merge_block_entry.first, merge_blocks_to_info, + types_to_available_ids, function_type->AsVoid(), ir_context, + transformation_context, &used_fresh_ids)) { + return false; + } + } + + // If the function has a non-void return type, and there are merge loops which + // contain return instructions, we need to check that either: + // - |message_.any_returnable_val_id| exists. In this case, it must have the + // same type as the return type of the function and be available at the end + // of the entry block. + // - a suitable id, available at the end of the entry block can be found in + // the module. + if (!function_type->AsVoid() && !merge_blocks_to_returning_preds.empty()) { + auto returnable_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.any_returnable_val_id()); + if (!returnable_val_def) { + // Check if a suitable id can be found in the module. + if (types_to_available_ids.count(function->type_id()) == 0) { + return false; + } + } else if (returnable_val_def->type_id() != function->type_id()) { + return false; + } else if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, function->entry()->terminator(), + message_.any_returnable_val_id())) { + // The id must be available at the end of the entry block. + return false; + } + } + + // Check that adding new predecessors to the relevant merge blocks does not + // render any instructions invalid (each id definition must still dominate + // each of its uses). + if (!CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + ir_context, function, merge_blocks_to_returning_preds)) { + return false; + } + + return true; +} + +void TransformationMergeFunctionReturns::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto function = ir_context->GetFunction(message_.function_id()); + auto function_type = ir_context->get_type_mgr()->GetType(function->type_id()); + + // Get a map from the types for which ids are available at the end of the + // entry block to one of the ids with that type. We compute this here to avoid + // potentially doing it multiple times later on. + auto types_to_available_ids = + GetTypesToIdAvailableAfterEntryBlock(ir_context); + + uint32_t bool_type = fuzzerutil::MaybeGetBoolType(ir_context); + + uint32_t constant_true = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, true, false); + + uint32_t constant_false = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, false, false); + + // Get the reachable return blocks. + auto return_blocks = + fuzzerutil::GetReachableReturnBlocks(ir_context, message_.function_id()); + + // Keep a map from the relevant merge blocks to a mapping from each of the + // returning predecessors to the corresponding pair (return value, + // boolean specifying whether the function is returning). Returning + // predecessors are blocks in the loop (not further nested inside loops), + // which either return or are merge blocks of nested loops containing return + // instructions. + std::map>> + merge_blocks_to_returning_predecessors; + + // Initialise the map, mapping each relevant merge block to an empty map. + for (uint32_t ret_block_id : return_blocks) { + uint32_t merge_block_id = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(ret_block_id); + + while (merge_block_id != 0 && + !merge_blocks_to_returning_predecessors.count(merge_block_id)) { + merge_blocks_to_returning_predecessors.emplace( + merge_block_id, std::map>()); + merge_block_id = ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock( + merge_block_id); + } + } + + // Get a reference to an instruction with the same type id as the function's + // return type, if the type of the function is not void and ther are loops + // containing return instructions. + uint32_t returnable_val_id = 0; + if (!function_type->AsVoid() && + !merge_blocks_to_returning_predecessors.empty()) { + // If |message.any_returnable_val_id| can be found in the module, use it. + // Otherwise, use another suitable id found in the module. + auto returnable_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.any_returnable_val_id()); + returnable_val_id = returnable_val_def + ? returnable_val_def->result_id() + : types_to_available_ids[function->type_id()]; + } + + // Keep a map from all the new predecessors of the merge block of the new + // outer loop, to the related return value ids. + std::map outer_merge_predecessors; + + // Adjust the return blocks and add the related information to the map or + // |outer_merge_predecessors| set. + for (uint32_t ret_block_id : return_blocks) { + auto ret_block = ir_context->get_instr_block(ret_block_id); + + // Get the return value id (if the function is not void). + uint32_t ret_val_id = + function_type->AsVoid() + ? 0 + : ret_block->terminator()->GetSingleWordInOperand(0); + + uint32_t merge_block_id = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(ret_block_id); + + // Add a new entry to the map corresponding to the merge block of the + // innermost enclosing loop (or that of the new outer loop if there is no + // enclosing loop). + if (merge_block_id != 0) { + merge_blocks_to_returning_predecessors[merge_block_id].emplace( + ret_block_id, + std::pair(ret_val_id, constant_true)); + } else { + // If there is no enclosing loop, the block will branch to the merge block + // of the new outer loop. + merge_block_id = message_.outer_return_id(); + outer_merge_predecessors.emplace(ret_block_id, ret_val_id); + } + + // Replace the return instruction with an unconditional branch. + ret_block->terminator()->SetOpcode(spv::Op::OpBranch); + ret_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {merge_block_id}}}); + } + + // Get a list of all the relevant merge blocks. + std::vector merge_blocks; + merge_blocks.reserve(merge_blocks_to_returning_predecessors.size()); + for (const auto& entry : merge_blocks_to_returning_predecessors) { + merge_blocks.emplace_back(entry.first); + } + + // Sort the list so that deeper merge blocks come first. + // We need to consider deeper merge blocks first so that, when a merge block + // is considered, all the merge blocks enclosed by the corresponding loop have + // already been considered and, thus, the mapping from this merge block to the + // returning predecessors is complete. + std::sort(merge_blocks.begin(), merge_blocks.end(), + ComparatorDeepBlocksFirst(ir_context)); + + auto merge_blocks_to_info = GetMappingOfMergeBlocksToInfo(); + + // Adjust the merge blocks and add the related information to the map or + // |outer_merge_predecessors| set. + for (uint32_t merge_block_id : merge_blocks) { + // Get the info corresponding to |merge_block| from the map, if a + // corresponding entry exists. Otherwise use overflow ids and find suitable + // ids in the module. + protobufs::ReturnMergingInfo* info = + merge_blocks_to_info.count(merge_block_id) + ? &merge_blocks_to_info[merge_block_id] + : nullptr; + + uint32_t is_returning_id = + info ? info->is_returning_id() + : transformation_context->GetOverflowIdSource() + ->GetNextOverflowId(); + + uint32_t maybe_return_val_id = 0; + if (!function_type->AsVoid()) { + maybe_return_val_id = info ? info->maybe_return_val_id() + : transformation_context->GetOverflowIdSource() + ->GetNextOverflowId(); + } + + // Map from existing OpPhi to overflow ids. If there is no mapping, get an + // empty map. + auto phi_to_id = info ? fuzzerutil::RepeatedUInt32PairToMap( + *merge_blocks_to_info[merge_block_id] + .mutable_opphi_to_suitable_id()) + : std::map(); + + // Get a reference to the info related to the returning predecessors. + const auto& returning_preds = + merge_blocks_to_returning_predecessors[merge_block_id]; + + // Get a set of the original predecessors. + auto preds_list = ir_context->cfg()->preds(merge_block_id); + auto preds = std::set(preds_list.begin(), preds_list.end()); + + auto merge_block = ir_context->get_instr_block(merge_block_id); + + // Adjust the existing OpPhi instructions. + merge_block->ForEachPhiInst( + [&preds, &returning_preds, &phi_to_id, + &types_to_available_ids](opt::Instruction* inst) { + // We need a placeholder value id. If |phi_to_id| contains a mapping + // for this instruction, we use the given id, otherwise a suitable id + // for the instruction's type from |types_to_available_ids|. + uint32_t placeholder_val_id = + phi_to_id.count(inst->result_id()) + ? phi_to_id[inst->result_id()] + : types_to_available_ids[inst->type_id()]; + assert(placeholder_val_id && + "We should always be able to find a suitable if the " + "transformation is applicable."); + + // Add a pair of operands (placeholder id, new predecessor) for each + // new predecessor of the merge block. + for (const auto& entry : returning_preds) { + // A returning predecessor may already be a predecessor of the + // block. In that case, we should not add new operands. + // Each entry is in the form (predecessor, {return val, is + // returning}). + if (!preds.count(entry.first)) { + inst->AddOperand({SPV_OPERAND_TYPE_ID, {placeholder_val_id}}); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {entry.first}}); + } + } + }); + + // If the function is not void, add a new OpPhi instructions to collect the + // return value from the returning predecessors. + if (!function_type->AsVoid()) { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, predecessor) for each returning + // predecessor. + for (auto entry : returning_preds) { + // Each entry is in the form (predecessor, {return value, + // is returning}). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second.first}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Add two operands for each original predecessor from which the function + // does not return. + for (uint32_t original_pred : preds) { + // Only add operands if the function cannot be returning from this + // block. + if (returning_preds.count(original_pred)) { + continue; + } + + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {returnable_val_id}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {original_pred}}); + } + + // Insert the instruction. + merge_block->begin()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpPhi, function->type_id(), maybe_return_val_id, + std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, maybe_return_val_id); + } + + // Add an OpPhi instruction deciding whether the function is returning. + { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, is returning) for each returning + // predecessor. + for (auto entry : returning_preds) { + // Each entry is in the form (predecessor, {return value, + // is returning}). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second.second}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Add two operands for each original predecessor from which the function + // does not return. + for (uint32_t original_pred : preds) { + // Only add operands if the function cannot be returning from this + // block. + if (returning_preds.count(original_pred)) { + continue; + } + + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {constant_false}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {original_pred}}); + } + + // Insert the instruction. + merge_block->begin()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpPhi, bool_type, is_returning_id, + std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, is_returning_id); + } + + // Change the branching instruction of the block. + assert(merge_block->terminator()->opcode() == spv::Op::OpBranch && + "Each block should branch unconditionally to the next."); + + // Add a new entry to the map corresponding to the merge block of the + // innermost enclosing loop (or that of the new outer loop if there is no + // enclosing loop). + uint32_t enclosing_merge = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(merge_block_id); + if (enclosing_merge == 0) { + enclosing_merge = message_.outer_return_id(); + outer_merge_predecessors.emplace(merge_block_id, maybe_return_val_id); + } else { + merge_blocks_to_returning_predecessors[enclosing_merge].emplace( + merge_block_id, + std::pair(maybe_return_val_id, is_returning_id)); + } + + // Get the current successor. + uint32_t original_succ = + merge_block->terminator()->GetSingleWordInOperand(0); + // Leave the instruction as it is if the block already branches to the merge + // block of the enclosing loop. + if (original_succ == enclosing_merge) { + continue; + } + + // The block should branch to |enclosing_merge| if |is_returning_id| is + // true, to |original_succ| otherwise. + merge_block->terminator()->SetOpcode(spv::Op::OpBranchConditional); + merge_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {is_returning_id}}, + {SPV_OPERAND_TYPE_ID, {enclosing_merge}}, + {SPV_OPERAND_TYPE_ID, {original_succ}}}); + } + + assert(function->entry()->terminator()->opcode() == spv::Op::OpBranch && + "The entry block should branch unconditionally to another block."); + uint32_t block_after_entry = + function->entry()->terminator()->GetSingleWordInOperand(0); + + // Create the header for the new outer loop. + auto outer_loop_header = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.outer_header_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id()); + + // Add the instruction: + // OpLoopMerge %outer_return_id %unreachable_continue_id None + outer_loop_header->AddInstruction(MakeUnique( + ir_context, spv::Op::OpLoopMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, + {uint32_t(spv::LoopControlMask::MaskNone)}}})); + + // Add unconditional branch to %block_after_entry. + outer_loop_header->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {block_after_entry}}})); + + // Insert the header right after the entry block. + function->InsertBasicBlockAfter(std::move(outer_loop_header), + function->entry().get()); + + // Update the branching instruction of the entry block. + function->entry()->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}); + + // If the entry block is referenced in an OpPhi instruction, the header for + // the new loop should be referenced instead. + ir_context->get_def_use_mgr()->ForEachUse( + function->entry()->id(), + [this](opt::Instruction* use_instruction, uint32_t use_operand_index) { + if (use_instruction->opcode() == spv::Op::OpPhi) { + use_instruction->SetOperand(use_operand_index, + {message_.outer_header_id()}); + } + }); + + // Create the merge block for the loop (and return block for the function). + auto outer_return_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.outer_return_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_return_id()); + + // If the function is not void, insert an instruction to collect the return + // value from the predecessors and an OpReturnValue instruction. + if (!function_type->AsVoid()) { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, predecessor) for each predecessor. + for (auto entry : outer_merge_predecessors) { + // Each entry is in the form (predecessor, return value). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Insert the OpPhi instruction. + outer_return_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpPhi, function->type_id(), + message_.return_val_id(), std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.return_val_id()); + + // Insert the OpReturnValue instruction. + outer_return_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpReturnValue, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.return_val_id()}}})); + } else { + // Insert an OpReturn instruction (the function is void). + outer_return_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpReturn, 0, 0, opt::Instruction::OperandList{})); + } + + // Insert the new return block at the end of the function. + function->AddBasicBlock(std::move(outer_return_block)); + + // Create the unreachable continue block associated with the enclosing loop. + auto unreachable_continue_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, message_.unreachable_continue_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.unreachable_continue_id()); + + // Insert an branch back to the loop header, to create a back edge. + unreachable_continue_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + + // Insert the unreachable continue block at the end of the function. + function->AddBasicBlock(std::move(unreachable_continue_block)); + + // All analyses must be invalidated because the structure of the module was + // changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set TransformationMergeFunctionReturns::GetFreshIds() + const { + std::unordered_set result; + result.emplace(message_.outer_header_id()); + result.emplace(message_.unreachable_continue_id()); + result.emplace(message_.outer_return_id()); + // |message_.return_val_info| can be 0 if the function is void. + if (message_.return_val_id()) { + result.emplace(message_.return_val_id()); + } + + for (const auto& merging_info : message_.return_merging_info()) { + result.emplace(merging_info.is_returning_id()); + // |maybe_return_val_id| can be 0 if the function is void. + if (merging_info.maybe_return_val_id()) { + result.emplace(merging_info.maybe_return_val_id()); + } + } + + return result; +} + +protobufs::Transformation TransformationMergeFunctionReturns::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_merge_function_returns() = message_; + return result; +} + +std::map +TransformationMergeFunctionReturns::GetMappingOfMergeBlocksToInfo() const { + std::map result; + for (const auto& info : message_.return_merging_info()) { + result.emplace(info.merge_block_id(), info); + } + return result; +} + +std::map +TransformationMergeFunctionReturns::GetTypesToIdAvailableAfterEntryBlock( + opt::IRContext* ir_context) const { + std::map result; + // Consider all global declarations + for (auto& global : ir_context->module()->types_values()) { + if (global.HasResultId() && global.type_id()) { + result.emplace(global.type_id(), global.result_id()); + } + } + + auto function = ir_context->GetFunction(message_.function_id()); + assert(function && "The function must exist."); + + // Consider all function parameters + function->ForEachParam([&result](opt::Instruction* param) { + if (param->HasResultId() && param->type_id()) { + result.emplace(param->type_id(), param->result_id()); + } + }); + + // Consider all the instructions in the entry block. + for (auto& inst : *function->entry()) { + if (inst.HasResultId() && inst.type_id()) { + result.emplace(inst.type_id(), inst.result_id()); + } + } + + return result; +} + +bool TransformationMergeFunctionReturns:: + CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + opt::IRContext* ir_context, const opt::Function* function, + const std::map>& + merge_blocks_to_new_predecessors) { + for (const auto& merge_block_entry : merge_blocks_to_new_predecessors) { + uint32_t merge_block = merge_block_entry.first; + const auto& returning_preds = merge_block_entry.second; + + // Find a list of blocks in which there might be problematic definitions. + // These are all the blocks that dominate the merge block but do not + // dominate all of the new predecessors. + std::vector problematic_blocks; + + auto dominator_analysis = ir_context->GetDominatorAnalysis(function); + + // Start from the immediate dominator of the merge block. + auto current_block = dominator_analysis->ImmediateDominator(merge_block); + assert(current_block && + "Each merge block should have at least one dominator."); + + for (uint32_t pred : returning_preds) { + while (!dominator_analysis->Dominates(current_block->id(), pred)) { + // The current block does not dominate all of the new predecessor + // blocks, so it might be problematic. + problematic_blocks.emplace_back(current_block); + + // Walk up the dominator tree. + current_block = dominator_analysis->ImmediateDominator(current_block); + assert(current_block && + "We should be able to find a dominator for all the blocks, " + "since they must all be dominated at least by the header."); + } + } + + // Identify the loop header corresponding to the merge block. + uint32_t loop_header = + fuzzerutil::GetLoopFromMergeBlock(ir_context, merge_block); + + // For all the ids defined in blocks inside |problematic_blocks|, check that + // all their uses are either: + // - inside the loop (or in the loop header). If this is the case, the path + // from the definition to the use does not go through the merge block, so + // adding new predecessor to it is not a problem. + // - inside an OpPhi instruction in the merge block. If this is the case, + // the definition does not need to dominate the merge block. + for (auto block : problematic_blocks) { + assert((block->id() == loop_header || + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + block->id()) == loop_header) && + "The problematic blocks should all be inside the loop (also " + "considering the header)."); + bool dominance_rules_maintained = + block->WhileEachInst([ir_context, loop_header, + merge_block](opt::Instruction* instruction) { + // Instruction without a result id do not cause any problems. + if (!instruction->HasResultId()) { + return true; + } + + // Check that all the uses of the id are inside the loop. + return ir_context->get_def_use_mgr()->WhileEachUse( + instruction->result_id(), + [ir_context, loop_header, merge_block]( + opt::Instruction* inst_use, uint32_t /* unused */) { + uint32_t block_use = + ir_context->get_instr_block(inst_use)->id(); + + // The usage is OK if it is inside the loop (including the + // header). + if (block_use == loop_header || + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + block_use)) { + return true; + } + + // The usage is OK if it is inside an OpPhi instruction in the + // merge block. + return block_use == merge_block && + inst_use->opcode() == spv::Op::OpPhi; + }); + }); + + // If not all instructions in the block satisfy the requirement, the + // transformation is not applicable. + if (!dominance_rules_maintained) { + return false; + } + } + } + + return true; +} + +bool TransformationMergeFunctionReturns:: + CheckThatTheCorrectIdsAreGivenForMergeBlock( + uint32_t merge_block, + const std::map& + merge_blocks_to_info, + const std::map& types_to_available_id, + bool function_is_void, opt::IRContext* ir_context, + const TransformationContext& transformation_context, + std::set* used_fresh_ids) { + // A map from OpPhi ids to ids of the same type available at the beginning + // of the merge block. + std::map phi_to_id; + + if (merge_blocks_to_info.count(merge_block) > 0) { + // If the map contains an entry for the merge block, check that the fresh + // ids are fresh and distinct. + auto info = merge_blocks_to_info.at(merge_block); + if (!info.is_returning_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + info.is_returning_id(), ir_context, used_fresh_ids)) { + return false; + } + + if (!function_is_void && + (!info.maybe_return_val_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + info.maybe_return_val_id(), ir_context, used_fresh_ids))) { + return false; + } + + // Get the mapping from OpPhis to suitable ids. + phi_to_id = fuzzerutil::RepeatedUInt32PairToMap( + *info.mutable_opphi_to_suitable_id()); + } else { + // If the map does not contain an entry for the merge block, check that + // overflow ids are available. + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + // For each OpPhi instruction, check that a suitable placeholder id is + // available. + bool suitable_info_for_phi = + ir_context->get_instr_block(merge_block) + ->WhileEachPhiInst([ir_context, &phi_to_id, + &types_to_available_id](opt::Instruction* inst) { + if (phi_to_id.count(inst->result_id()) > 0) { + // If there exists a mapping for this instruction and the + // placeholder id exists in the module, check that it has the + // correct type and it is available before the instruction. + auto placeholder_def = ir_context->get_def_use_mgr()->GetDef( + phi_to_id[inst->result_id()]); + if (placeholder_def) { + if (inst->type_id() != placeholder_def->type_id()) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, inst, placeholder_def->result_id())) { + return false; + } + + return true; + } + } + + // If there is no mapping, check if there is a suitable id + // available at the end of the entry block. + return types_to_available_id.count(inst->type_id()) > 0; + }); + + if (!suitable_info_for_phi) { + return false; + } + + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.h new file mode 100644 index 00000000..b3208acf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_merge_function_returns.h @@ -0,0 +1,116 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ +#define SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationMergeFunctionReturns : public Transformation { + public: + explicit TransformationMergeFunctionReturns( + protobufs::TransformationMergeFunctionReturns message); + + TransformationMergeFunctionReturns( + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, + uint32_t return_val_id, uint32_t any_returnable_val_id, + const std::vector& returns_merging_info); + + // - |message_.function_id| is the id of a function. + // - The entry block of |message_.function_id| branches unconditionally to + // another block. + // - |message_.any_returnable_val_id| is an id whose type is the same as the + // return type of the function and which is available at the end of the + // entry block. If this id is not found in the module, the transformation + // will try to find a suitable one. + // If the function is void, or no loops in the function contain return + // statements, this id will be ignored. + // - Merge blocks of reachable loops that contain return statements only + // consist of OpLabel, OpPhi or OpBranch instructions. + // - The module contains OpConstantTrue and OpConstantFalse instructions. + // - For all merge blocks of reachable loops that contain return statements, + // either: + // - a mapping is provided in |message_.return_merging_info|, all of the + // corresponding fresh ids are valid and, for each OpPhi instruction in + // the block, there is a mapping to an available id of the same type in + // |opphi_to_suitable_id| or a suitable id, available at the end of the + // entry block, can be found in the module. + // - there is no mapping, but overflow ids are available and, for every + // OpPhi instruction in the merge blocks that need to be modified, a + // suitable id, available at the end of the entry block, can be found. + // - The addition of new predecessors to the relevant merge blocks does not + // cause any id use to be invalid (i.e. every id must dominate all its uses + // even after the transformation has added new branches). + // - All of the fresh ids that are provided and needed by the transformation + // are valid. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the function so that there is only one reachable return + // instruction. The function is enclosed by an outer loop, whose merge block + // is the new return block. All existing return statements are replaced by + // branch instructions to the merge block of the loop enclosing them, and + // OpPhi instructions are used to keep track of the return value and of + // whether the function is returning. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns a map from merge block ids to the corresponding info in + // |message_.return_merging_info|. + std::map + GetMappingOfMergeBlocksToInfo() const; + + // Returns a map from type ids to an id with that type and which is available + // at the end of the entry block of |message_.function_id|. + // Assumes that the function exists. + std::map GetTypesToIdAvailableAfterEntryBlock( + opt::IRContext* ir_context) const; + + // Returns true if adding new predecessors to the given loop merge blocks + // does not render any instructions invalid (each id definition must still + // dominate all of its uses). The loop merge blocks and corresponding new + // predecessors to consider are given in |merge_blocks_to_new_predecessors|. + // All of the new predecessors are assumed to be inside the loop associated + // with the corresponding loop merge block. + static bool CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + opt::IRContext* ir_context, const opt::Function* function, + const std::map>& + merge_blocks_to_new_predecessors); + + // Returns true if the required ids for |merge_block| are provided in the + // |merge_blocks_to_info| map, or if ids of the suitable type can be found. + static bool CheckThatTheCorrectIdsAreGivenForMergeBlock( + uint32_t merge_block, + const std::map& + merge_blocks_to_info, + const std::map& types_to_available_id, + bool function_is_void, opt::IRContext* ir_context, + const TransformationContext& transformation_context, + std::set* used_fresh_ids); + + protobufs::TransformationMergeFunctionReturns message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.cpp new file mode 100644 index 00000000..dc1b2430 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_block_down.h" + +#include "source/opt/basic_block.h" + +namespace spvtools { +namespace fuzz { + +TransformationMoveBlockDown::TransformationMoveBlockDown( + protobufs::TransformationMoveBlockDown message) + : message_(std::move(message)) {} + +TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) { + message_.set_block_id(id); +} + +bool TransformationMoveBlockDown::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Go through every block in every function, looking for a block whose id + // matches that of the block we want to consider moving down. + for (auto& function : *ir_context->module()) { + for (auto block_it = function.begin(); block_it != function.end(); + ++block_it) { + if (block_it->id() == message_.block_id()) { + // We have found a match. + if (block_it == function.begin()) { + // The block is the first one appearing in the function. We are not + // allowed to move this block down. + return false; + } + // Record the block we would like to consider moving down. + opt::BasicBlock* block_matching_id = &*block_it; + if (!ir_context->GetDominatorAnalysis(&function)->IsReachable( + block_matching_id)) { + // The block is not reachable. We are not allowed to move it down. + return false; + } + // Now see whether there is some block following that block in program + // order. + ++block_it; + if (block_it == function.end()) { + // There is no such block; i.e., the block we are considering moving + // is the last one in the function. The transformation thus does not + // apply. + return false; + } + opt::BasicBlock* next_block_in_program_order = &*block_it; + // We can move the block of interest down if and only if it does not + // dominate the block that comes next. + return !ir_context->GetDominatorAnalysis(&function)->Dominates( + block_matching_id, next_block_in_program_order); + } + } + } + + // We did not find a matching block, so the transformation is not applicable: + // there is no relevant block to move. + return false; +} + +void TransformationMoveBlockDown::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Go through every block in every function, looking for a block whose id + // matches that of the block we want to move down. + for (auto& function : *ir_context->module()) { + for (auto block_it = function.begin(); block_it != function.end(); + ++block_it) { + if (block_it->id() == message_.block_id()) { + ++block_it; + assert(block_it != function.end() && + "To be able to move a block down, it needs to have a " + "program-order successor."); + function.MoveBasicBlockToAfter(message_.block_id(), &*block_it); + // For performance, it is vital to keep the dominator analysis valid + // (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889 + // requires keeping the CFG analysis valid). + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisDefUse | + opt::IRContext::Analysis::kAnalysisCFG | + opt::IRContext::Analysis::kAnalysisDominatorAnalysis); + + return; + } + } + } + assert(false && "No block was found to move down."); +} + +protobufs::Transformation TransformationMoveBlockDown::ToMessage() const { + protobufs::Transformation result; + *result.mutable_move_block_down() = message_; + return result; +} + +std::unordered_set TransformationMoveBlockDown::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.h new file mode 100644 index 00000000..cbad9451 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_block_down.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMoveBlockDown : public Transformation { + public: + explicit TransformationMoveBlockDown( + protobufs::TransformationMoveBlockDown message); + + explicit TransformationMoveBlockDown(uint32_t id); + + // - |message_.block_id| must be the id of a block b in the given module. + // - b must not be the first nor last block appearing, in program order, + // in a function. + // - b must not dominate the block that follows it in program order. + // - b must be reachable. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The block with id |message_.block_id| is moved down; i.e. the program order + // between it and the block that follows it is swapped. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationMoveBlockDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp new file mode 100644 index 00000000..4d5d9f0c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp @@ -0,0 +1,734 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_instruction_down.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "spirv/unified1/GLSL.std.450.h" + +namespace spvtools { +namespace fuzz { +namespace { + +const char* const kExtensionSetName = "GLSL.std.450"; + +std::string GetExtensionSet(opt::IRContext* ir_context, + const opt::Instruction& op_ext_inst) { + assert(op_ext_inst.opcode() == spv::Op::OpExtInst && "Wrong opcode"); + + const auto* ext_inst_import = ir_context->get_def_use_mgr()->GetDef( + op_ext_inst.GetSingleWordInOperand(0)); + assert(ext_inst_import && "Extension set is not imported"); + + return ext_inst_import->GetInOperand(0).AsString(); +} + +} // namespace + +TransformationMoveInstructionDown::TransformationMoveInstructionDown( + protobufs::TransformationMoveInstructionDown message) + : message_(std::move(message)) {} + +TransformationMoveInstructionDown::TransformationMoveInstructionDown( + const protobufs::InstructionDescriptor& instruction) { + *message_.mutable_instruction() = instruction; +} + +bool TransformationMoveInstructionDown::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |instruction| must be valid. + auto* inst = FindInstruction(message_.instruction(), ir_context); + if (!inst) { + return false; + } + + // Instruction's opcode must be supported by this transformation. + if (!IsInstructionSupported(ir_context, *inst)) { + return false; + } + + auto* inst_block = ir_context->get_instr_block(inst); + assert(inst_block && + "Global instructions and function parameters are not supported"); + + auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst); + assert(inst_it != inst_block->end() && + "Can't get an iterator for the instruction"); + + // |instruction| can't be the last instruction in the block. + auto successor_it = ++inst_it; + if (successor_it == inst_block->end()) { + return false; + } + + // We don't risk swapping a memory instruction with an unsupported one. + if (!IsSimpleInstruction(ir_context, *inst) && + !IsInstructionSupported(ir_context, *successor_it)) { + return false; + } + + // It must be safe to swap the instructions without changing the semantics of + // the module. + if (IsInstructionSupported(ir_context, *successor_it) && + !CanSafelySwapInstructions(ir_context, *inst, *successor_it, + *transformation_context.GetFactManager())) { + return false; + } + + // Check that we can insert |instruction| after |inst_it|. + auto successors_successor_it = ++inst_it; + if (successors_successor_it == inst_block->end() || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(), + successors_successor_it)) { + return false; + } + + // Check that |instruction|'s successor doesn't depend on the |instruction|. + if (inst->result_id()) { + for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) { + const auto& operand = successor_it->GetInOperand(i); + if (spvIsInIdType(operand.type) && + operand.words[0] == inst->result_id()) { + return false; + } + } + } + + return true; +} + +void TransformationMoveInstructionDown::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = FindInstruction(message_.instruction(), ir_context); + assert(inst && + "The instruction should've been validated in the IsApplicable"); + + auto inst_it = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + + // Move the instruction down in the block. + inst->InsertAfter(&*++inst_it); + + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const { + protobufs::Transformation result; + *result.mutable_move_instruction_down() = message_; + return result; +} + +bool TransformationMoveInstructionDown::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& inst) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // Add support for more instructions here. + return IsSimpleInstruction(ir_context, inst) || + IsMemoryInstruction(ir_context, inst) || IsBarrierInstruction(inst); +} + +bool TransformationMoveInstructionDown::IsSimpleInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + case spv::Op::OpNop: + case spv::Op::OpUndef: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + // OpAccessChain and OpInBoundsAccessChain are considered simple + // instructions since they result in a pointer to the object in memory, + // not the object itself. + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCopyObject: + case spv::Op::OpTranspose: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + case spv::Op::OpBitcast: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpCopyLogical: + return true; + case spv::Op::OpExtInst: { + const auto* ext_inst_import = + ir_context->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0)); + + if (ext_inst_import->GetInOperand(0).AsString() != kExtensionSetName) { + return false; + } + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450Trunc: + case GLSLstd450FAbs: + case GLSLstd450SAbs: + case GLSLstd450FSign: + case GLSLstd450SSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Atan2: + case GLSLstd450Pow: + case GLSLstd450Exp: + case GLSLstd450Log: + case GLSLstd450Exp2: + case GLSLstd450Log2: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450Determinant: + case GLSLstd450MatrixInverse: + case GLSLstd450ModfStruct: + case GLSLstd450FMin: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450FMax: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450FClamp: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FMix: + case GLSLstd450IMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450FrexpStruct: + case GLSLstd450Ldexp: + case GLSLstd450PackSnorm4x8: + case GLSLstd450PackUnorm4x8: + case GLSLstd450PackSnorm2x16: + case GLSLstd450PackUnorm2x16: + case GLSLstd450PackHalf2x16: + case GLSLstd450PackDouble2x32: + case GLSLstd450UnpackSnorm2x16: + case GLSLstd450UnpackUnorm2x16: + case GLSLstd450UnpackHalf2x16: + case GLSLstd450UnpackSnorm4x8: + case GLSLstd450UnpackUnorm4x8: + case GLSLstd450UnpackDouble2x32: + case GLSLstd450Length: + case GLSLstd450Distance: + case GLSLstd450Cross: + case GLSLstd450Normalize: + case GLSLstd450FaceForward: + case GLSLstd450Reflect: + case GLSLstd450Refract: + case GLSLstd450FindILsb: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: + return true; + default: + return false; + } + } + default: + return false; + } +} + +bool TransformationMoveInstructionDown::IsMemoryReadInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + // Some simple instructions. + case spv::Op::OpLoad: + case spv::Op::OpCopyMemory: + // Image instructions. + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseRead: + // Atomic instructions. + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + return true; + // Extensions. + case spv::Op::OpExtInst: { + if (GetExtensionSet(ir_context, inst) != kExtensionSetName) { + return false; + } + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return true; + default: + return false; + } + } + default: + return false; + } +} + +uint32_t TransformationMoveInstructionDown::GetMemoryReadTarget( + opt::IRContext* ir_context, const opt::Instruction& inst) { + (void)ir_context; // |ir_context| is only used in assertions. + assert(IsMemoryReadInstruction(ir_context, inst) && + "|inst| is not a memory read instruction"); + + switch (inst.opcode()) { + // Simple instructions. + case spv::Op::OpLoad: + // Image instructions. + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseRead: + // Atomic instructions. + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + return inst.GetSingleWordInOperand(0); + case spv::Op::OpCopyMemory: + return inst.GetSingleWordInOperand(1); + case spv::Op::OpExtInst: { + assert(GetExtensionSet(ir_context, inst) == kExtensionSetName && + "Extension set is not supported"); + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return inst.GetSingleWordInOperand(2); + default: + // This assertion will fail if not all memory read extension + // instructions are handled in the switch. + assert(false && "Not all memory opcodes are handled"); + return 0; + } + } + default: + // This assertion will fail if not all memory read opcodes are handled in + // the switch. + assert(false && "Not all memory opcodes are handled"); + return 0; + } +} + +bool TransformationMoveInstructionDown::IsMemoryWriteInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + // Simple Instructions. + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + // Image instructions. + case spv::Op::OpImageWrite: + // Atomic instructions. + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: + return true; + // Extensions. + case spv::Op::OpExtInst: { + if (GetExtensionSet(ir_context, inst) != kExtensionSetName) { + return false; + } + + auto extension = static_cast(inst.GetSingleWordInOperand(1)); + return extension == GLSLstd450Modf || extension == GLSLstd450Frexp; + } + default: + return false; + } +} + +uint32_t TransformationMoveInstructionDown::GetMemoryWriteTarget( + opt::IRContext* ir_context, const opt::Instruction& inst) { + (void)ir_context; // |ir_context| is only used in assertions. + assert(IsMemoryWriteInstruction(ir_context, inst) && + "|inst| is not a memory write instruction"); + + switch (inst.opcode()) { + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + case spv::Op::OpImageWrite: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: + return inst.GetSingleWordInOperand(0); + case spv::Op::OpExtInst: { + assert(GetExtensionSet(ir_context, inst) == kExtensionSetName && + "Extension set is not supported"); + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450Modf: + case GLSLstd450Frexp: + return inst.GetSingleWordInOperand(3); + default: + // This assertion will fail if not all memory write extension + // instructions are handled in the switch. + assert(false && "Not all opcodes are handled"); + return 0; + } + } + default: + // This assertion will fail if not all memory write opcodes are handled in + // the switch. + assert(false && "Not all opcodes are handled"); + return 0; + } +} + +bool TransformationMoveInstructionDown::IsMemoryInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + return IsMemoryReadInstruction(ir_context, inst) || + IsMemoryWriteInstruction(ir_context, inst); +} + +bool TransformationMoveInstructionDown::IsBarrierInstruction( + const opt::Instruction& inst) { + switch (inst.opcode()) { + case spv::Op::OpMemoryBarrier: + case spv::Op::OpControlBarrier: + case spv::Op::OpMemoryNamedBarrier: + return true; + default: + return false; + } +} + +bool TransformationMoveInstructionDown::CanSafelySwapInstructions( + opt::IRContext* ir_context, const opt::Instruction& a, + const opt::Instruction& b, const FactManager& fact_manager) { + assert(IsInstructionSupported(ir_context, a) && + IsInstructionSupported(ir_context, b) && + "Both opcodes must be supported"); + + // One of opcodes is simple - we can swap them without any side-effects. + if (IsSimpleInstruction(ir_context, a) || + IsSimpleInstruction(ir_context, b)) { + return true; + } + + // Both parameters are either memory instruction or barriers. + + // One of the opcodes is a barrier - can't swap them. + if (IsBarrierInstruction(a) || IsBarrierInstruction(b)) { + return false; + } + + // Both parameters are memory instructions. + + // Both parameters only read from memory - it's OK to swap them. + if (!IsMemoryWriteInstruction(ir_context, a) && + !IsMemoryWriteInstruction(ir_context, b)) { + return true; + } + + // At least one of parameters is a memory read instruction. + + // In theory, we can swap two memory instructions, one of which reads + // from the memory, if the read target (the pointer the memory is read from) + // and the write target (the memory is written into): + // - point to different memory regions + // - point to the same region with irrelevant value + // - point to the same region and the region is not used anymore. + // + // However, we can't currently determine if two pointers point to two + // different memory regions. That being said, if two pointers are not + // synonymous, they still might point to the same memory region. For example: + // %1 = OpVariable ... + // %2 = OpAccessChain %1 0 + // %3 = OpAccessChain %1 0 + // In this pseudo-code, %2 and %3 are not synonymous but point to the same + // memory location. This implies that we can't determine if some memory + // location is not used in the block. + // + // With this in mind, consider two cases (we will build a table for each one): + // - one instruction only reads from memory, the other one only writes to it. + // S - both point to the same memory region. + // D - both point to different memory regions. + // 0, 1, 2 - neither, one of or both of the memory regions are irrelevant. + // |-| - can't swap; |+| - can swap. + // | 0 | 1 | 2 | + // S : - + + + // D : + + + + // - both instructions write to memory. Notation is the same. + // | 0 | 1 | 2 | + // S : * + + + // D : + + + + // * - we can swap two instructions that write into the same non-irrelevant + // memory region if the written value is the same. + // + // Note that we can't always distinguish between S and D. Also note that + // in case of S, if one of the instructions is marked with + // PointeeValueIsIrrelevant, then the pointee of the other one is irrelevant + // as well even if the instruction is not marked with that fact. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3723): + // This procedure can be improved when we can determine if two pointers point + // to different memory regions. + + // From now on we will denote an instruction that: + // - only reads from memory - R + // - only writes into memory - W + // - reads and writes - RW + // + // Both |a| and |b| can be either W or RW at this point. Additionally, at most + // one of them can be R. The procedure below checks all possible combinations + // of R, W and RW according to the tables above. We conservatively assume that + // both |a| and |b| point to the same memory region. + + auto memory_is_irrelevant = [ir_context, &fact_manager](uint32_t id) { + const auto* inst = ir_context->get_def_use_mgr()->GetDef(id); + if (!inst->type_id()) { + return false; + } + + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "|id| has invalid type"); + + if (!type->AsPointer()) { + return false; + } + + return fact_manager.PointeeValueIsIrrelevant(id); + }; + + if (IsMemoryWriteInstruction(ir_context, a) && + IsMemoryWriteInstruction(ir_context, b) && + (memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) || + memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b)))) { + // We ignore the case when the written value is the same. This is because + // the written value might not be equal to any of the instruction's + // operands. + return true; + } + + if (IsMemoryReadInstruction(ir_context, a) && + IsMemoryWriteInstruction(ir_context, b) && + !memory_is_irrelevant(GetMemoryReadTarget(ir_context, a)) && + !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b))) { + return false; + } + + if (IsMemoryWriteInstruction(ir_context, a) && + IsMemoryReadInstruction(ir_context, b) && + !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) && + !memory_is_irrelevant(GetMemoryReadTarget(ir_context, b))) { + return false; + } + + return IsMemoryReadInstruction(ir_context, a) || + IsMemoryReadInstruction(ir_context, b); +} + +std::unordered_set TransformationMoveInstructionDown::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.h new file mode 100644 index 00000000..2a5a8f1f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_move_instruction_down.h @@ -0,0 +1,107 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMoveInstructionDown : public Transformation { + public: + explicit TransformationMoveInstructionDown( + protobufs::TransformationMoveInstructionDown message); + + explicit TransformationMoveInstructionDown( + const protobufs::InstructionDescriptor& instruction); + + // - |instruction| should be a descriptor of a valid instruction in the module + // - |instruction|'s opcode should be supported by this transformation + // - neither |instruction| nor its successor may be the last instruction in + // the block + // - |instruction|'s successor may not be dependent on the |instruction| + // - it should be possible to insert |instruction|'s opcode after its + // successor + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps |instruction| with its successor. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns true if the |inst| is supported by this transformation. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| represents a "simple" instruction. That is, it + // neither reads from nor writes to the memory and is not a barrier. + static bool IsSimpleInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| reads from memory. + static bool IsMemoryReadInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns id being used by |inst| to read from. |inst| must be a memory read + // instruction (see IsMemoryReadInstruction). Returned id is not guaranteed to + // have pointer type. + static uint32_t GetMemoryReadTarget(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| that writes to the memory. + static bool IsMemoryWriteInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns id being used by |inst| to write into. |inst| must be a memory + // write instruction (see IsMemoryWriteInstruction). Returned id is not + // guaranteed to have pointer type. + static uint32_t GetMemoryWriteTarget(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| either reads from or writes to the memory + // (see IsMemoryReadInstruction and IsMemoryWriteInstruction accordingly). + static bool IsMemoryInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| is a barrier instruction. + static bool IsBarrierInstruction(const opt::Instruction& inst); + + // Returns true if it is possible to swap |a| and |b| without changing the + // module's semantics. |a| and |b| are required to be supported instructions + // (see IsInstructionSupported). In particular, if either |a| or |b| are + // memory or barrier instructions, some checks are used to only say that they + // can be swapped if the swap is definitely semantics-preserving. + static bool CanSafelySwapInstructions(opt::IRContext* ir_context, + const opt::Instruction& a, + const opt::Instruction& b, + const FactManager& fact_manager); + + protobufs::TransformationMoveInstructionDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp new file mode 100644 index 00000000..a1620cca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_mutate_pointer.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationMutatePointer::TransformationMutatePointer( + protobufs::TransformationMutatePointer message) + : message_(std::move(message)) {} + +TransformationMutatePointer::TransformationMutatePointer( + uint32_t pointer_id, uint32_t fresh_id, + const protobufs::InstructionDescriptor& insert_before) { + message_.set_pointer_id(pointer_id); + message_.set_fresh_id(fresh_id); + *message_.mutable_insert_before() = insert_before; +} + +bool TransformationMutatePointer::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |fresh_id| is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + + // Check that |insert_before| is a valid instruction descriptor. + if (!insert_before_inst) { + return false; + } + + // Check that it is possible to insert OpLoad and OpStore before + // |insert_before_inst|. We are only using OpLoad here since the result does + // not depend on the opcode. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, + insert_before_inst)) { + return false; + } + + const auto* pointer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + + // Check that |pointer_id| is a result id of a valid pointer instruction. + if (!pointer_inst || !IsValidPointerInstruction(ir_context, *pointer_inst)) { + return false; + } + + // Check that the module contains an irrelevant constant that will be used to + // mutate |pointer_inst|. The constant is irrelevant so that the latter + // transformation can change its value to something more interesting. + auto constant_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, + fuzzerutil::GetPointeeTypeIdFromPointerType(ir_context, + pointer_inst->type_id()), + true); + if (!constant_id) { + return false; + } + + assert(fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, constant_id) && + "Global constant instruction is not available before " + "|insert_before_inst|"); + + // Check that |pointer_inst| is available before |insert_before_inst|. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, pointer_inst->result_id()); +} + +void TransformationMutatePointer::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + assert(insert_before_inst && "|insert_before| descriptor is invalid"); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + + // Back up the original value. + auto backup_instruction = MakeUnique( + ir_context, spv::Op::OpLoad, pointee_type_id, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}); + auto backup_instruction_ptr = backup_instruction.get(); + insert_before_inst->InsertBefore(std::move(backup_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr); + ir_context->set_instr_block(backup_instruction_ptr, enclosing_block); + + // Insert a new value. + auto new_value_instruction = MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, + {fuzzerutil::MaybeGetZeroConstant( + ir_context, *transformation_context, pointee_type_id, true)}}}); + auto new_value_instruction_ptr = new_value_instruction.get(); + insert_before_inst->InsertBefore(std::move(new_value_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr); + ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block); + + // Restore the original value. + auto restore_instruction = MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}); + auto restore_instruction_ptr = restore_instruction.get(); + insert_before_inst->InsertBefore(std::move(restore_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr); + ir_context->set_instr_block(restore_instruction_ptr, enclosing_block); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); +} + +protobufs::Transformation TransformationMutatePointer::ToMessage() const { + protobufs::Transformation result; + *result.mutable_mutate_pointer() = message_; + return result; +} + +bool TransformationMutatePointer::IsValidPointerInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + // |inst| must have both result id and type id and it may not cause undefined + // behaviour. + if (!inst.result_id() || !inst.type_id() || + inst.opcode() == spv::Op::OpUndef || + inst.opcode() == spv::Op::OpConstantNull) { + return false; + } + + opt::Instruction* type_inst = + ir_context->get_def_use_mgr()->GetDef(inst.type_id()); + assert(type_inst != nullptr && "|inst| has invalid type id"); + + // |inst| must be a pointer. + if (type_inst->opcode() != spv::Op::OpTypePointer) { + return false; + } + + // |inst| must have a supported storage class. + switch ( + static_cast(type_inst->GetSingleWordInOperand(0))) { + case spv::StorageClass::Function: + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: + break; + default: + return false; + } + + // |inst|'s pointee must consist of scalars and/or composites. + return fuzzerutil::CanCreateConstant(ir_context, + type_inst->GetSingleWordInOperand(1)); +} + +std::unordered_set TransformationMutatePointer::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.h new file mode 100644 index 00000000..2c712909 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_mutate_pointer.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMutatePointer : public Transformation { + public: + explicit TransformationMutatePointer( + protobufs::TransformationMutatePointer message); + + explicit TransformationMutatePointer( + uint32_t pointer_id, uint32_t fresh_id, + const protobufs::InstructionDescriptor& insert_before); + + // - |fresh_id| must be fresh. + // - |insert_before| must be a valid instruction descriptor of some + // instruction in the module. + // - It should be possible to insert OpLoad and OpStore before + // |insert_before|. + // - |pointer_id| must be a result id of some instruction in the module. + // - Instruction with result id |pointer_id| must be valid (see + // IsValidPointerInstruction method). + // - There must exist an irrelevant constant in the module. Type of the + // constant must be equal to the type of the |pointer_id|'s pointee. + // - |pointer_id| must be available (according to the dominance rules) before + // |insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts the following instructions before |insert_before|: + // %fresh_id = OpLoad %pointee_type_id %pointer_id + // OpStore %pointer_id %constant_id + // OpStore %pointer_id %fresh_id + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |inst| valid pointer according to the following: + // - |inst| has result id and type id. + // - |inst| is neither OpUndef nor OpConstantNull. + // - |inst| has a pointer type. + // - |inst|'s storage class is either Private, Function or Workgroup. + // - |inst|'s pointee type and all its constituents are either scalar or + // composite. + static bool IsValidPointerInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + private: + protobufs::TransformationMutatePointer message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.cpp new file mode 100644 index 00000000..4ab68d07 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.cpp @@ -0,0 +1,1027 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_outline_function.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationOutlineFunction::TransformationOutlineFunction( + protobufs::TransformationOutlineFunction message) + : message_(std::move(message)) {} + +TransformationOutlineFunction::TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id, + uint32_t new_function_id, uint32_t new_function_region_entry_block, + uint32_t new_caller_result_id, uint32_t new_callee_result_id, + const std::map& input_id_to_fresh_id, + const std::map& output_id_to_fresh_id) { + message_.set_entry_block(entry_block); + message_.set_exit_block(exit_block); + message_.set_new_function_struct_return_type_id( + new_function_struct_return_type_id); + message_.set_new_function_type_id(new_function_type_id); + message_.set_new_function_id(new_function_id); + message_.set_new_function_region_entry_block(new_function_region_entry_block); + message_.set_new_caller_result_id(new_caller_result_id); + message_.set_new_callee_result_id(new_callee_result_id); + *message_.mutable_input_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(input_id_to_fresh_id); + *message_.mutable_output_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(output_id_to_fresh_id); +} + +bool TransformationOutlineFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + std::set ids_used_by_this_transformation; + + // The various new ids used by the transformation must be fresh and distinct. + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_struct_return_type_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_type_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_region_entry_block(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_caller_result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_callee_result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + for (auto& pair : message_.input_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + for (auto& pair : message_.output_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + // The entry and exit block ids must indeed refer to blocks. + for (auto block_id : {message_.entry_block(), message_.exit_block()}) { + auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!block_label || block_label->opcode() != spv::Op::OpLabel) { + return false; + } + } + + auto entry_block = ir_context->cfg()->block(message_.entry_block()); + auto exit_block = ir_context->cfg()->block(message_.exit_block()); + + // The entry block cannot start with OpVariable - this would mean that + // outlining would remove a variable from the function containing the region + // being outlined. + if (entry_block->begin()->opcode() == spv::Op::OpVariable) { + return false; + } + + // For simplicity, we do not allow the entry block to be a loop header. + if (entry_block->GetLoopMergeInst()) { + return false; + } + + // For simplicity, we do not allow the exit block to be a merge block or + // continue target. + if (fuzzerutil::IsMergeOrContinue(ir_context, exit_block->id())) { + return false; + } + + // The entry block cannot start with OpPhi. This is to keep the + // transformation logic simple. (Another transformation to split the OpPhis + // from a block could be applied to avoid this scenario.) + if (entry_block->begin()->opcode() == spv::Op::OpPhi) { + return false; + } + + // The block must be in the same function. + if (entry_block->GetParent() != exit_block->GetParent()) { + return false; + } + + // The entry block must dominate the exit block. + auto dominator_analysis = + ir_context->GetDominatorAnalysis(entry_block->GetParent()); + if (!dominator_analysis->Dominates(entry_block, exit_block)) { + return false; + } + + // The exit block must post-dominate the entry block. + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(entry_block->GetParent()); + if (!postdominator_analysis->Dominates(exit_block, entry_block)) { + return false; + } + + // Find all the blocks dominated by |message_.entry_block| and post-dominated + // by |message_.exit_block|. + auto region_set = GetRegionBlocks( + ir_context, + entry_block = ir_context->cfg()->block(message_.entry_block()), + exit_block = ir_context->cfg()->block(message_.exit_block())); + + // Check whether |region_set| really is a single-entry single-exit region, and + // also check whether structured control flow constructs and their merge + // and continue constructs are either wholly in or wholly out of the region - + // e.g. avoid the situation where the region contains the head of a loop but + // not the loop's continue construct. + // + // This is achieved by going through every block in the function that contains + // the region. + for (auto& block : *entry_block->GetParent()) { + if (region_set.count(&block) != 0) { + // The block is in the region. Check that it does not have any unreachable + // predecessors. If it does, then we do not regard the region as single- + // entry-single-exit and hence do not outline it. + for (auto pred : ir_context->cfg()->preds(block.id())) { + if (!ir_context->IsReachable(*ir_context->cfg()->block(pred))) { + // The predecessor is unreachable. + return false; + } + } + } + + if (&block == exit_block) { + // It is OK (and typically expected) for the exit block of the region to + // have successors outside the region. + // + // It is also OK for the exit block to head a selection construct: the + // block containing the call to the outlined function will end up heading + // this construct if outlining takes place. However, it is not OK for + // the exit block to head a loop construct. + if (block.GetLoopMergeInst()) { + return false; + } + continue; + } + + if (region_set.count(&block) != 0) { + // The block is in the region and is not the region's exit block. Let's + // see whether all of the block's successors are in the region. If they + // are not, the region is not single-entry single-exit. + bool all_successors_in_region = true; + block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context, + ®ion_set](uint32_t successor) -> bool { + if (region_set.count(ir_context->cfg()->block(successor)) == 0) { + all_successors_in_region = false; + return false; + } + return true; + }); + if (!all_successors_in_region) { + return false; + } + } + + if (auto merge = block.GetMergeInst()) { + // The block is a loop or selection header -- the header and its + // associated merge block had better both be in the region or both be + // outside the region. + auto merge_block = + ir_context->cfg()->block(merge->GetSingleWordOperand(0)); + if (region_set.count(&block) != region_set.count(merge_block)) { + return false; + } + } + + if (auto loop_merge = block.GetLoopMergeInst()) { + // Similar to the above, but for the continue target of a loop. + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + if (continue_target != exit_block && + region_set.count(&block) != region_set.count(continue_target)) { + return false; + } + } + } + + // For each region input id, i.e. every id defined outside the region but + // used inside the region, ... + auto input_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id()); + for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) { + // There needs to be a corresponding fresh id to be used as a function + // parameter, or overflow ids need to be available. + if (input_id_to_fresh_id_map.count(id) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + // Furthermore, if the input id has pointer type it must be an OpVariable + // or OpFunctionParameter. + auto input_id_inst = ir_context->get_def_use_mgr()->GetDef(id); + if (ir_context->get_def_use_mgr() + ->GetDef(input_id_inst->type_id()) + ->opcode() == spv::Op::OpTypePointer) { + switch (input_id_inst->opcode()) { + case spv::Op::OpFunctionParameter: + case spv::Op::OpVariable: + // These are OK. + break; + default: + // Anything else is not OK. + return false; + } + } + } + + // For each region output id -- i.e. every id defined inside the region but + // used outside the region, ... + auto output_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id()); + for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) { + if ( + // ... there needs to be a corresponding fresh id that can hold the + // value for this id computed in the outlined function (or overflow ids + // must be available), and ... + (output_id_to_fresh_id_map.count(id) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) + // ... the output id must not have pointer type (to avoid creating a + // struct with pointer members to pass data out of the outlined + // function) + || ir_context->get_def_use_mgr() + ->GetDef(fuzzerutil::GetTypeId(ir_context, id)) + ->opcode() == spv::Op::OpTypePointer) { + return false; + } + } + + return true; +} + +void TransformationOutlineFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // The entry block for the region before outlining. + auto original_region_entry_block = + ir_context->cfg()->block(message_.entry_block()); + + // The exit block for the region before outlining. + auto original_region_exit_block = + ir_context->cfg()->block(message_.exit_block()); + + // The single-entry single-exit region defined by |message_.entry_block| and + // |message_.exit_block|. + std::set region_blocks = GetRegionBlocks( + ir_context, original_region_entry_block, original_region_exit_block); + + // Input and output ids for the region being outlined. + std::vector region_input_ids = + GetRegionInputIds(ir_context, region_blocks, original_region_exit_block); + std::vector region_output_ids = + GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block); + + // Maps from input and output ids to fresh ids. + auto input_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id()); + auto output_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id()); + + // Use overflow ids to augment these maps at any locations where fresh ids are + // required but not provided. + for (uint32_t id : region_input_ids) { + if (input_id_to_fresh_id_map.count(id) == 0) { + input_id_to_fresh_id_map.insert( + {id, + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + } + for (uint32_t id : region_output_ids) { + if (output_id_to_fresh_id_map.count(id) == 0) { + output_id_to_fresh_id_map.insert( + {id, + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + } + + UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map, + output_id_to_fresh_id_map); + + // Construct a map that associates each output id with its type id. + std::map output_id_to_type_id; + for (uint32_t output_id : region_output_ids) { + output_id_to_type_id[output_id] = + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); + } + + // The region will be collapsed to a single block that calls a function + // containing the outlined region. This block needs to end with whatever + // the exit block of the region ended with before outlining. We thus clone + // the terminator of the region's exit block, and the merge instruction for + // the block if there is one, so that we can append them to the end of the + // collapsed block later. + std::unique_ptr cloned_exit_block_terminator = + std::unique_ptr( + original_region_exit_block->terminator()->Clone(ir_context)); + std::unique_ptr cloned_exit_block_merge = + original_region_exit_block->GetMergeInst() + ? std::unique_ptr( + original_region_exit_block->GetMergeInst()->Clone(ir_context)) + : nullptr; + + // Make a function prototype for the outlined function, which involves + // figuring out its required type. + std::unique_ptr outlined_function = PrepareFunctionPrototype( + region_input_ids, region_output_ids, input_id_to_fresh_id_map, ir_context, + transformation_context); + + // Adapt the region to be outlined so that its input ids are replaced with the + // ids of the outlined function's input parameters, and so that output ids + // are similarly remapped. + RemapInputAndOutputIdsInRegion( + ir_context, *original_region_exit_block, region_blocks, region_input_ids, + region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map); + + // Fill out the body of the outlined function according to the region that is + // being outlined. + PopulateOutlinedFunction( + *original_region_entry_block, *original_region_exit_block, region_blocks, + region_output_ids, output_id_to_type_id, output_id_to_fresh_id_map, + ir_context, outlined_function.get()); + + // Collapse the region that has been outlined into a function down to a single + // block that calls said function. + ShrinkOriginalRegion( + ir_context, region_blocks, region_input_ids, region_output_ids, + output_id_to_type_id, outlined_function->type_id(), + std::move(cloned_exit_block_merge), + std::move(cloned_exit_block_terminator), original_region_entry_block); + + // Add the outlined function to the module. + const auto* outlined_function_ptr = outlined_function.get(); + ir_context->module()->AddFunction(std::move(outlined_function)); + + // Major surgery has been conducted on the module, so invalidate all analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // If the original function was livesafe, the new function should also be + // livesafe. + if (transformation_context->GetFactManager()->FunctionIsLivesafe( + original_region_entry_block->GetParent()->result_id())) { + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( + message_.new_function_id()); + } + + // Record the fact that all blocks in the outlined region are dead if the + // first block is dead. + if (transformation_context->GetFactManager()->BlockIsDead( + original_region_entry_block->id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + outlined_function_ptr->entry()->id()); + } +} + +protobufs::Transformation TransformationOutlineFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_outline_function() = message_; + return result; +} + +std::vector TransformationOutlineFunction::GetRegionInputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block) { + std::vector result; + + auto enclosing_function = region_exit_block->GetParent(); + + // Consider each parameter of the function containing the region. + enclosing_function->ForEachParam( + [ir_context, ®ion_set, &result](opt::Instruction* function_parameter) { + // Consider every use of the parameter. + ir_context->get_def_use_mgr()->WhileEachUse( + function_parameter, + [ir_context, function_parameter, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) { + // Get the block, if any, in which the parameter is used. + auto use_block = ir_context->get_instr_block(use); + // If the use is in a block that lies within the region, the + // parameter is an input id for the region. + if (use_block && region_set.count(use_block) != 0) { + result.push_back(function_parameter->result_id()); + return false; + } + return true; + }); + }); + + // Consider all definitions in the function that might turn out to be input + // ids. + for (auto& block : *enclosing_function) { + std::vector candidate_input_ids_for_block; + if (region_set.count(&block) == 0) { + // All instructions in blocks outside the region are candidate's for + // generating input ids. + for (auto& inst : block) { + candidate_input_ids_for_block.push_back(&inst); + } + } else { + // Blocks in the region cannot generate input ids. + continue; + } + + // Consider each candidate input id to check whether it is used in the + // region. + for (auto& inst : candidate_input_ids_for_block) { + ir_context->get_def_use_mgr()->WhileEachUse( + inst, + [ir_context, &inst, region_exit_block, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + // Find the block in which this id use occurs, recording the id as + // an input id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = ir_context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) == 0) { + // The use is not in the region: this does not make it an input + // id. + return true; + } + + if (use_block == region_exit_block && use->IsBlockTerminator()) { + // We do not regard uses in the exit block terminator as input + // ids, as this terminator does not get outlined. + return true; + } + + result.push_back(inst->result_id()); + return false; + }); + } + } + return result; +} + +std::vector TransformationOutlineFunction::GetRegionOutputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block) { + std::vector result; + + // Consider each block in the function containing the region. + for (auto& block : *region_exit_block->GetParent()) { + if (region_set.count(&block) == 0) { + // Skip blocks that are not in the region. + continue; + } + // Consider each use of each instruction defined in the block. + for (auto& inst : block) { + ir_context->get_def_use_mgr()->WhileEachUse( + &inst, + [®ion_set, ir_context, &inst, region_exit_block, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + // Find the block in which this id use occurs, recording the id as + // an output id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = ir_context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) != 0) { + // The use is in the region. + if (use_block != region_exit_block || !use->IsBlockTerminator()) { + // Furthermore, the use is not in the terminator of the region's + // exit block. + return true; + } + } + + result.push_back(inst.result_id()); + return false; + }); + } + } + return result; +} + +std::set TransformationOutlineFunction::GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block) { + auto enclosing_function = entry_block->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + std::set result; + for (auto& block : *enclosing_function) { + if (dominator_analysis->Dominates(entry_block, &block) && + postdominator_analysis->Dominates(exit_block, &block)) { + result.insert(&block); + } + } + return result; +} + +std::unique_ptr +TransformationOutlineFunction::PrepareFunctionPrototype( + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + uint32_t return_type_id = 0; + uint32_t function_type_id = 0; + + // First, try to find an existing function type that is suitable. This is + // only possible if the region generates no output ids; if it generates output + // ids we are going to make a new struct for those, and since that struct does + // not exist there cannot already be a function type with this struct as its + // return type. + if (region_output_ids.empty()) { + std::vector return_and_parameter_types; + opt::analysis::Void void_type; + return_type_id = ir_context->get_type_mgr()->GetId(&void_type); + return_and_parameter_types.push_back(return_type_id); + for (auto id : region_input_ids) { + return_and_parameter_types.push_back( + ir_context->get_def_use_mgr()->GetDef(id)->type_id()); + } + function_type_id = + fuzzerutil::FindFunctionType(ir_context, return_and_parameter_types); + } + + // If no existing function type was found, we need to create one. + if (function_type_id == 0) { + assert( + ((return_type_id == 0) == !region_output_ids.empty()) && + "We should only have set the return type if there are no output ids."); + // If the region generates output ids, we need to make a struct with one + // field per output id. + if (!region_output_ids.empty()) { + opt::Instruction::OperandList struct_member_types; + for (uint32_t output_id : region_output_ids) { + auto output_id_type = + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); + if (ir_context->get_def_use_mgr()->GetDef(output_id_type)->opcode() == + spv::Op::OpTypeVoid) { + // We cannot add a void field to a struct. We instead use OpUndef to + // handle void output ids. + continue; + } + struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}}); + } + // Add a new struct type to the module. + ir_context->module()->AddType(MakeUnique( + ir_context, spv::Op::OpTypeStruct, 0, + message_.new_function_struct_return_type_id(), + std::move(struct_member_types))); + // The return type for the function is the newly-created struct. + return_type_id = message_.new_function_struct_return_type_id(); + } + assert( + return_type_id != 0 && + "We should either have a void return type, or have created a struct."); + + // The region's input ids dictate the parameter types to the function. + opt::Instruction::OperandList function_type_operands; + function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}}); + for (auto id : region_input_ids) { + function_type_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {ir_context->get_def_use_mgr()->GetDef(id)->type_id()}}); + } + // Add a new function type to the module, and record that this is the type + // id for the new function. + ir_context->module()->AddType(MakeUnique( + ir_context, spv::Op::OpTypeFunction, 0, message_.new_function_type_id(), + function_type_operands)); + function_type_id = message_.new_function_type_id(); + } + + // Create a new function with |message_.new_function_id| as the function id, + // and the return type and function type prepared above. + std::unique_ptr outlined_function = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpFunction, return_type_id, + message_.new_function_id(), + opt::Instruction::OperandList( + {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {uint32_t(spv::FunctionControlMask::MaskNone)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {function_type_id}}}))); + + // Add one parameter to the function for each input id, using the fresh ids + // provided in |input_id_to_fresh_id_map|, or overflow ids if needed. + for (auto id : region_input_ids) { + uint32_t fresh_id = input_id_to_fresh_id_map.at(id); + outlined_function->AddParameter(MakeUnique( + ir_context, spv::Op::OpFunctionParameter, + ir_context->get_def_use_mgr()->GetDef(id)->type_id(), fresh_id, + opt::Instruction::OperandList())); + + // Analyse the use of the new parameter instruction. + outlined_function->ForEachParam( + [fresh_id, ir_context](opt::Instruction* inst) { + if (inst->result_id() == fresh_id) { + ir_context->AnalyzeDefUse(inst); + } + }); + + // If the input id is an irrelevant-valued variable, the same should be true + // of the corresponding parameter. + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + id)) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(input_id_to_fresh_id_map.at(id)); + } + } + + return outlined_function; +} + +void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds( + opt::IRContext* ir_context, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const { + // Enlarge the module's id bound as needed to accommodate the various fresh + // ids associated with the transformation. + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.new_function_struct_return_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.new_function_region_entry_block()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_caller_result_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_callee_result_id()); + + for (auto& entry : input_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); + } + + for (auto& entry : output_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); + } +} + +void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( + opt::IRContext* ir_context, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const { + // Change all uses of input ids inside the region to the corresponding fresh + // ids that will ultimately be parameters of the outlined function. + // This is done by considering each region input id in turn. + for (uint32_t id : region_input_ids) { + // We then consider each use of the input id. + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, id, &input_id_to_fresh_id_map, region_blocks]( + opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the input id occurs. + opt::BasicBlock* use_block = ir_context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region. + if (region_blocks.count(use_block) != 0) { + // Rewrite this use of the input id. + use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)}); + } + }); + } + + // Change each definition of a region output id to define the corresponding + // fresh ids that will store intermediate value for the output ids. Also + // change all uses of the output id located in the outlined region. + // This is done by considering each region output id in turn. + for (uint32_t id : region_output_ids) { + // First consider each use of the output id and update the relevant uses. + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, &original_region_exit_block, id, + &output_id_to_fresh_id_map, + region_blocks](opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the output id occurs. + auto use_block = ir_context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region, with one exception: the terminator of the exit block of + // the region is going to remain in the original function, so if the + // use appears in such a terminator instruction we leave it alone. + if ( + // The block is in the region ... + region_blocks.count(use_block) != 0 && + // ... and the use is not in the terminator instruction of the + // region's exit block. + !(use_block == &original_region_exit_block && + use->IsBlockTerminator())) { + // Rewrite this use of the output id. + use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)}); + } + }); + + // Now change the instruction that defines the output id so that it instead + // defines the corresponding fresh id. We do this after changing all the + // uses so that the definition of the original id is still registered when + // we analyse its uses. + ir_context->get_def_use_mgr()->GetDef(id)->SetResultId( + output_id_to_fresh_id_map.at(id)); + } +} + +void TransformationOutlineFunction::PopulateOutlinedFunction( + const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + const std::map& output_id_to_fresh_id_map, + opt::IRContext* ir_context, opt::Function* outlined_function) const { + // When we create the exit block for the outlined region, we use this pointer + // to track of it so that we can manipulate it later. + opt::BasicBlock* outlined_region_exit_block = nullptr; + + // The region entry block in the new function is identical to the entry block + // of the region being outlined, except that it has + // |message_.new_function_region_entry_block| as its id. + std::unique_ptr outlined_region_entry_block = + MakeUnique(MakeUnique( + ir_context, spv::Op::OpLabel, 0, + message_.new_function_region_entry_block(), + opt::Instruction::OperandList())); + + if (&original_region_entry_block == &original_region_exit_block) { + outlined_region_exit_block = outlined_region_entry_block.get(); + } + + for (auto& inst : original_region_entry_block) { + outlined_region_entry_block->AddInstruction( + std::unique_ptr(inst.Clone(ir_context))); + } + outlined_function->AddBasicBlock(std::move(outlined_region_entry_block)); + + // We now go through the single-entry single-exit region defined by the entry + // and exit blocks, adding clones of all blocks to the new function. + + // Consider every block in the enclosing function. + auto enclosing_function = original_region_entry_block.GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + // Skip the region's entry block - we already dealt with it above. + if (region_blocks.count(&*block_it) == 0 || + &*block_it == &original_region_entry_block) { + ++block_it; + continue; + } + // Clone the block so that it can be added to the new function. + auto cloned_block = + std::unique_ptr(block_it->Clone(ir_context)); + + // If this is the region's exit block, then the cloned block is the outlined + // region's exit block. + if (&*block_it == &original_region_exit_block) { + assert(outlined_region_exit_block == nullptr && + "We should not yet have encountered the exit block."); + outlined_region_exit_block = cloned_block.get(); + } + + // Redirect any OpPhi operands whose predecessors are the original region + // entry block to become the new function entry block. + cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.entry_block()) { + phi_inst->SetInOperand(predecessor_index, + {message_.new_function_region_entry_block()}); + } + } + }); + + outlined_function->AddBasicBlock(std::move(cloned_block)); + block_it = block_it.Erase(); + } + assert(outlined_region_exit_block != nullptr && + "We should have encountered the region's exit block when iterating " + "through the function"); + + // We now need to adapt the exit block for the region - in the new function - + // so that it ends with a return. + + // We first eliminate the merge instruction (if any) and the terminator for + // the cloned exit block. + for (auto inst_it = outlined_region_exit_block->begin(); + inst_it != outlined_region_exit_block->end();) { + if (inst_it->opcode() == spv::Op::OpLoopMerge || + inst_it->opcode() == spv::Op::OpSelectionMerge) { + inst_it = inst_it.Erase(); + } else if (inst_it->IsBlockTerminator()) { + inst_it = inst_it.Erase(); + } else { + ++inst_it; + } + } + + // We now add either OpReturn or OpReturnValue as the cloned exit block's + // terminator. + if (region_output_ids.empty()) { + // The case where there are no region output ids is simple: we just add + // OpReturn. + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpReturn, 0, 0, opt::Instruction::OperandList())); + } else { + // In the case where there are output ids, we add an OpCompositeConstruct + // instruction to pack all the non-void output values into a struct, and + // then an OpReturnValue instruction to return this struct. + opt::Instruction::OperandList struct_member_operands; + for (uint32_t id : region_output_ids) { + if (ir_context->get_def_use_mgr() + ->GetDef(output_id_to_type_id.at(id)) + ->opcode() != spv::Op::OpTypeVoid) { + struct_member_operands.push_back( + {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}}); + } + } + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + message_.new_function_struct_return_type_id(), + message_.new_callee_result_id(), struct_member_operands)); + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpReturnValue, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}}))); + } + + outlined_function->SetFunctionEnd( + MakeUnique(ir_context, spv::Op::OpFunctionEnd, 0, 0, + opt::Instruction::OperandList())); +} + +void TransformationOutlineFunction::ShrinkOriginalRegion( + opt::IRContext* ir_context, const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr cloned_exit_block_merge, + std::unique_ptr cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const { + // Erase all blocks from the original function that are in the outlined + // region, except for the region's entry block. + // + // In the process, identify all references to the exit block of the region, + // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them + // to refer to the region entry block (the single block to which we are + // shrinking the region). + auto enclosing_function = original_region_entry_block->GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (&*block_it == original_region_entry_block) { + ++block_it; + } else if (region_blocks.count(&*block_it) == 0) { + // The block is not in the region. Check whether it has the last block + // of the region as an OpPhi predecessor, and if so change the + // predecessor to be the first block of the region (i.e. the block + // containing the call to what was outlined). + assert(block_it->MergeBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a merge block"); + assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a continue target"); + block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.exit_block()) { + phi_inst->SetInOperand(predecessor_index, {message_.entry_block()}); + } + } + }); + ++block_it; + } else { + // The block is in the region and is not the region's entry block: kill + // it. + block_it = block_it.Erase(); + } + } + + // Now erase all instructions from the region's entry block, as they have + // been outlined. + for (auto inst_it = original_region_entry_block->begin(); + inst_it != original_region_entry_block->end();) { + inst_it = inst_it.Erase(); + } + + // Now we add a call to the outlined function to the region's entry block. + opt::Instruction::OperandList function_call_operands; + function_call_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}}); + // The function parameters are the region input ids. + for (auto input_id : region_input_ids) { + function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}}); + } + + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpFunctionCall, return_type_id, + message_.new_caller_result_id(), function_call_operands)); + + // If there are output ids, the function call will return a struct. For each + // output id, we add an extract operation to pull the appropriate struct + // member out into an output id. The exception is for output ids with void + // type. There are no struct entries for these, so we use an OpUndef of void + // type instead. + uint32_t struct_member_index = 0; + for (uint32_t output_id : region_output_ids) { + uint32_t output_type_id = output_id_to_type_id.at(output_id); + if (ir_context->get_def_use_mgr()->GetDef(output_type_id)->opcode() == + spv::Op::OpTypeVoid) { + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpUndef, output_type_id, output_id, + opt::Instruction::OperandList())); + // struct_member_index is not incremented since there was no struct member + // associated with this void-typed output id. + } else { + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, output_type_id, output_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {struct_member_index}}}))); + struct_member_index++; + } + } + + // Finally, we terminate the block with the merge instruction (if any) that + // used to belong to the region's exit block, and the terminator that used + // to belong to the region's exit block. + if (cloned_exit_block_merge != nullptr) { + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_merge)); + } + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_terminator)); +} + +std::unordered_set TransformationOutlineFunction::GetFreshIds() + const { + std::unordered_set result = { + message_.new_function_struct_return_type_id(), + message_.new_function_type_id(), + message_.new_function_id(), + message_.new_function_region_entry_block(), + message_.new_caller_result_id(), + message_.new_callee_result_id()}; + for (auto& pair : message_.input_id_to_fresh_id()) { + result.insert(pair.second()); + } + for (auto& pair : message_.output_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.h new file mode 100644 index 00000000..94ce556d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_outline_function.h @@ -0,0 +1,229 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ + +#include +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationOutlineFunction : public Transformation { + public: + explicit TransformationOutlineFunction( + protobufs::TransformationOutlineFunction message); + + TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, + uint32_t new_function_type_id, uint32_t new_function_id, + uint32_t new_function_region_entry_block, uint32_t new_caller_result_id, + uint32_t new_callee_result_id, + const std::map& input_id_to_fresh_id, + const std::map& output_id_to_fresh_id); + + // - All the fresh ids occurring in the transformation must be distinct and + // fresh + // - |message_.entry_block| and |message_.exit_block| must form a single-entry + // single-exit control flow graph region + // - |message_.entry_block| must not start with OpVariable + // - |message_.entry_block| must not be a loop header + // - |message_.exit_block| must not be a merge block or the continue target + // of a loop + // - A structured control flow construct must lie either completely within the + // region or completely outside it + // - |message.entry_block| must not start with OpPhi; this is to keep the + // transformation simple - another transformation should be used to split + // a desired entry block that starts with OpPhi if needed + // - |message_.input_id_to_fresh_id| must contain an entry for every id + // defined outside the region but used in the region + // - |message_.output_id_to_fresh_id| must contain an entry for every id + // defined in the region but used outside the region + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - A new function with id |message_.new_function_id| is added to the module. + // - If the region generates output ids, the return type of this function is + // a new struct type with one field per output id, and with type id + // |message_.new_function_struct_return_type|, otherwise the function return + // types is void and |message_.new_function_struct_return_type| is not used. + // - If the region generates input ids, the new function has one parameter per + // input id. Fresh ids for these parameters are provided by + // |message_.input_id_to_fresh_id|. + // - Unless the type required for the new function is already known, + // |message_.new_function_type_id| is used as the type id for a new function + // type, and the new function uses this type. + // - The new function starts with a placeholder block with id + // |message_.new_function_first_block|, which jumps straight to a successor + // block, to avoid violating rules on what the first block in a function may + // look like. + // - The outlined region is replaced with a single block, with the same id + // as |message_.entry_block|, and which calls the new function, passing the + // region's input ids as parameters. The result is stored in + // |message_.new_caller_result_id|, which has type + // |message_.new_function_struct_return_type| (unless there are + // no output ids, in which case the return type is void). The components + // of this returned struct are then copied out into the region's output ids. + // The block ends with the merge instruction (if any) and terminator of + // |message_.exit_block|. + // - The body of the new function is identical to the outlined region, except + // that (a) the region's entry block has id + // |message_.new_function_region_entry_block|, (b) input id uses are + // replaced with parameter accesses, (c) and definitions of output ids are + // replaced with definitions of corresponding fresh ids provided by + // |message_.output_id_to_fresh_id|, and (d) the block of the function + // ends by returning a composite of type + // |message_.new_function_struct_return_type| comprised of all the fresh + // output ids (unless the return type is void, in which case no value is + // returned. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the set of blocks dominated by |entry_block| and post-dominated + // by |exit_block|. + static std::set GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block); + + // Yields ids that are used in |region_set| and that are either parameters + // to the function containing |region_set|, or are defined by blocks of this + // function that are outside |region_set|. + // + // Special cases: OpPhi instructions in |region_entry_block| and the + // terminator of |region_exit_block| do not get outlined, therefore + // - id uses in OpPhi instructions in |region_entry_block| are ignored + // - id uses in the terminator instruction of |region_exit_block| are ignored + static std::vector GetRegionInputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block); + + // Yields all ids that are defined in |region_set| and used outside + // |region_set|. + // + // Special cases: for similar reasons as for |GetRegionInputIds|, + // - ids defined in the region and used in the terminator of + // |region_exit_block| count as output ids + static std::vector GetRegionOutputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block); + + private: + // Ensures that the module's id bound is at least the maximum of any fresh id + // associated with the transformation. + void UpdateModuleIdBoundForFreshIds( + opt::IRContext* ir_context, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const; + + // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert, + // in the region to be outlined, all the input ids in |region_input_ids| and + // the output ids in |region_output_ids| to their fresh counterparts. + // Parameters |region_blocks| provides access to the blocks that must be + // modified, and |original_region_exit_block| allows for some special cases + // where ids should not be remapped. + void RemapInputAndOutputIdsInRegion( + opt::IRContext* ir_context, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const; + + // Produce a Function object that has the right function type and parameter + // declarations. The function argument types and parameter ids are dictated + // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return + // type is dictated by |region_output_ids|. + // + // A new struct type to represent the function return type, and a new function + // type for the function, will be added to the module (unless suitable types + // are already present). + // + // Facts about the function containing the outlined region that are relevant + // to the new function are propagated via the vact manager in + // |transformation_context|. + std::unique_ptr PrepareFunctionPrototype( + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + // Creates the body of the outlined function by cloning blocks from the + // original region, given by |region_blocks|, adapting the cloned version + // of |original_region_exit_block| so that it returns something appropriate, + // and patching up branches to |original_region_entry_block| to refer to its + // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are + // used to determine what the function should return. Parameter + // |output_id_to_type_id| provides the type of each output id. + // + // The |transformation_context| argument allow facts about blocks being + // outlined, e.g. whether they are dead blocks, to be asserted about blocks + // that get created during outlining. + void PopulateOutlinedFunction( + const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + const std::map& output_id_to_fresh_id_map, + opt::IRContext* ir_context, opt::Function* outlined_function) const; + + // Shrinks the outlined region, given by |region_blocks|, down to the single + // block |original_region_entry_block|. This block is itself shrunk to just + // contain: + // - any OpPhi instructions that were originally present + // - a call to the outlined function, with parameters provided by + // |region_input_ids| + // - instructions to route components of the call's return value into + // |region_output_ids| + // - The merge instruction (if any) and terminator of the original region's + // exit block, given by |cloned_exit_block_merge| and + // |cloned_exit_block_terminator| + // Parameters |output_id_to_type_id| and |return_type_id| provide the + // provide types for the region's output ids, and the return type of the + // outlined function: as the module is in an inconsistent state when this + // function is called, this information cannot be gotten from the def-use + // manager. + void ShrinkOriginalRegion( + opt::IRContext* ir_context, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr cloned_exit_block_merge, + std::unique_ptr cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const; + + protobufs::TransformationOutlineFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp new file mode 100644 index 00000000..b666cdfe --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_function_parameters.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + protobufs::TransformationPermuteFunctionParameters message) + : message_(std::move(message)) {} + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t function_type_fresh_id, + const std::vector& permutation) { + message_.set_function_id(function_id); + message_.set_function_type_fresh_id(function_type_fresh_id); + + for (auto index : permutation) { + message_.add_permutation(index); + } +} + +bool TransformationPermuteFunctionParameters::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that function exists + const auto* function = + fuzzerutil::FindFunction(ir_context, message_.function_id()); + if (!function || function->DefInst().opcode() != spv::Op::OpFunction || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // Check that permutation has valid indices + const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(function_type && "Function type is null"); + + std::vector permutation(message_.permutation().begin(), + message_.permutation().end()); + + // Don't take return type into account + auto arg_size = function_type->NumInOperands() - 1; + + // |permutation| vector should be equal to the number of arguments + if (static_cast(permutation.size()) != arg_size) { + return false; + } + + // Check that permutation doesn't have duplicated values. + assert(!fuzzerutil::HasDuplicates(permutation) && + "Permutation has duplicates"); + + // Check that elements in permutation are in range [0, arg_size - 1]. + // + // We must check whether the permutation is empty first because in that case + // |arg_size - 1| will produce |std::numeric_limits::max()| since + // it's an unsigned integer. + if (!permutation.empty() && + !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()); +} + +void TransformationPermuteFunctionParameters::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Find the function that will be transformed + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); + assert(function && "Can't find the function"); + + // Adjust OpFunctionParameter instructions + + // Collect ids and types from OpFunctionParameter instructions + std::vector param_id, param_type; + function->ForEachParam( + [¶m_id, ¶m_type](const opt::Instruction* param) { + param_id.push_back(param->result_id()); + param_type.push_back(param->type_id()); + }); + + // Permute parameters' ids and types + std::vector permuted_param_id, permuted_param_type; + for (auto index : message_.permutation()) { + permuted_param_id.push_back(param_id[index]); + permuted_param_type.push_back(param_type[index]); + } + + // Set OpFunctionParameter instructions to point to new parameters + size_t i = 0; + function->ForEachParam( + [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) { + param->SetResultType(permuted_param_type[i]); + param->SetResultId(permuted_param_id[i]); + ++i; + }); + + // Fix all OpFunctionCall instructions + for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) { + opt::Instruction::OperandList call_operands = { + call->GetInOperand(0) // Function id + }; + + for (auto index : message_.permutation()) { + // Take function id into account + call_operands.push_back(call->GetInOperand(index + 1)); + } + + call->SetInOperands(std::move(call_operands)); + } + + // Update function type. + { + // We use a separate scope here since |old_function_type_inst| might become + // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type_inst = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type_inst && "Function must have a valid type"); + + std::vector parameter_type_ids; + for (auto index : message_.permutation()) { + // +1 since the first operand to OpTypeFunction is a return type. + parameter_type_ids.push_back( + old_function_type_inst->GetSingleWordInOperand(index + 1)); + } + + // Change function's type. + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_permute_function_parameters() = message_; + return result; +} + +std::unordered_set +TransformationPermuteFunctionParameters::GetFreshIds() const { + return {message_.function_type_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.h new file mode 100644 index 00000000..abb5675c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_function_parameters.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPermuteFunctionParameters : public Transformation { + public: + explicit TransformationPermuteFunctionParameters( + protobufs::TransformationPermuteFunctionParameters message); + + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t function_type_fresh_id, + const std::vector& permutation); + + // - |function_id| is a valid non-entry-point OpFunction instruction + // - |function_type_fresh_id| is a fresh id. + // New type is valid if: + // - it has the same number of operands as the old one + // - function's result type is the same as the old one + // - function's arguments are permuted according to |permutation| vector + // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments + // to the function + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - OpFunction instruction with |result_id == function_id| is changed. + // Its arguments are permuted according to the |permutation| vector + // - Adjusts function's type to accommodate for permuted parameters. + // - Calls to the function are adjusted accordingly + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPermuteFunctionParameters message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp new file mode 100644 index 00000000..f2f40570 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_phi_operands.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationPermutePhiOperands::TransformationPermutePhiOperands( + protobufs::TransformationPermutePhiOperands message) + : message_(std::move(message)) {} + +TransformationPermutePhiOperands::TransformationPermutePhiOperands( + uint32_t result_id, const std::vector& permutation) { + message_.set_result_id(result_id); + + for (auto index : permutation) { + message_.add_permutation(index); + } +} + +bool TransformationPermutePhiOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that |message_.result_id| is valid. + const auto* inst = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!inst || inst->opcode() != spv::Op::OpPhi) { + return false; + } + + // Check that |message_.permutation| has expected size. + auto expected_permutation_size = inst->NumInOperands() / 2; + if (static_cast(message_.permutation().size()) != + expected_permutation_size) { + return false; + } + + // Check that |message_.permutation| has elements in range + // [0, expected_permutation_size - 1]. + std::vector permutation(message_.permutation().begin(), + message_.permutation().end()); + assert(!fuzzerutil::HasDuplicates(permutation) && + "Permutation has duplicates"); + + // We must check whether the permutation is empty first because in that case + // |expected_permutation_size - 1| will produce + // |std::numeric_limits::max()| since it's an unsigned integer. + return permutation.empty() || + fuzzerutil::IsPermutationOfRange(permutation, 0, + expected_permutation_size - 1); +} + +void TransformationPermutePhiOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + assert(inst); + + opt::Instruction::OperandList permuted_operands; + permuted_operands.reserve(inst->NumInOperands()); + + for (auto index : message_.permutation()) { + permuted_operands.push_back(std::move(inst->GetInOperand(2 * index))); + permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1))); + } + + inst->SetInOperands(std::move(permuted_operands)); + + // Update the def-use manager. + ir_context->UpdateDefUse(inst); +} + +protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const { + protobufs::Transformation result; + *result.mutable_permute_phi_operands() = message_; + return result; +} + +std::unordered_set TransformationPermutePhiOperands::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.h new file mode 100644 index 00000000..16427113 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_permute_phi_operands.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPermutePhiOperands : public Transformation { + public: + explicit TransformationPermutePhiOperands( + protobufs::TransformationPermutePhiOperands message); + + TransformationPermutePhiOperands(uint32_t result_id, + const std::vector& permutation); + + // - |result_id| must be a valid id of some OpPhi instruction in the module. + // - |permutation| must contain elements in the range [0, n/2 - 1] where |n| + // is a number of operands to the instruction with |result_id|. All elements + // must be unique (i.e. |permutation.size() == n / 2|). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Permutes operands of the OpPhi instruction with |result_id| according to + // the elements in |permutation|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPermutePhiOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp new file mode 100644 index 00000000..4b987842 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp @@ -0,0 +1,592 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_down.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( + protobufs::TransformationPropagateInstructionDown message) + : message_(std::move(message)) {} + +TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( + uint32_t block_id, uint32_t phi_fresh_id, + const std::map& successor_id_to_fresh_id) { + message_.set_block_id(block_id); + message_.set_phi_fresh_id(phi_fresh_id); + *message_.mutable_successor_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(successor_id_to_fresh_id); +} + +bool TransformationPropagateInstructionDown::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that we can apply this transformation to the |block_id|. + if (!IsApplicableToBlock(ir_context, message_.block_id())) { + return false; + } + + const auto successor_id_to_fresh_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id()); + + for (auto id : GetAcceptableSuccessors(ir_context, message_.block_id())) { + // Each successor must have a fresh id in the |successor_id_to_fresh_id| + // map, unless overflow ids are available. + if (!successor_id_to_fresh_id.count(id) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + std::vector maybe_fresh_ids = {message_.phi_fresh_id()}; + maybe_fresh_ids.reserve(successor_id_to_fresh_id.size()); + for (const auto& entry : successor_id_to_fresh_id) { + maybe_fresh_ids.push_back(entry.second); + } + + // All ids must be unique and fresh. + return !fuzzerutil::HasDuplicates(maybe_fresh_ids) && + std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationPropagateInstructionDown::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Get instruction to propagate down. There must be one. + auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, message_.block_id()); + assert(inst_to_propagate && "There must be an instruction to propagate"); + + auto successor_id_to_fresh_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id()); + std::vector created_inst_ids; + auto successor_ids = GetAcceptableSuccessors(ir_context, message_.block_id()); + + // Clone |inst_to_propagate| into every successor. + for (auto successor_id : successor_ids) { + std::unique_ptr clone( + inst_to_propagate->Clone(ir_context)); + + uint32_t new_result_id; + if (successor_id_to_fresh_id.count(successor_id)) { + new_result_id = successor_id_to_fresh_id.at(successor_id); + } else { + assert(transformation_context->GetOverflowIdSource()->HasOverflowIds() && + "Overflow ids must be available"); + new_result_id = + transformation_context->GetOverflowIdSource()->GetNextOverflowId(); + successor_id_to_fresh_id[successor_id] = new_result_id; + } + + clone->SetResultId(new_result_id); + fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id); + + auto* insert_before_inst = GetFirstInsertBeforeInstruction( + ir_context, successor_id, clone->opcode()); + assert(insert_before_inst && "Can't insert into one of the successors"); + + insert_before_inst->InsertBefore(std::move(clone)); + created_inst_ids.push_back(new_result_id); + } + + // Add an OpPhi instruction into the module if possible. + if (auto merge_block_id = GetOpPhiBlockId( + ir_context, message_.block_id(), *inst_to_propagate, successor_ids)) { + opt::Instruction::OperandList in_operands; + std::unordered_set visited_predecessors; + for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) { + if (visited_predecessors.count(predecessor_id)) { + // Merge block might have multiple identical predecessors. + continue; + } + + visited_predecessors.insert(predecessor_id); + + const auto* dominator_analysis = ir_context->GetDominatorAnalysis( + ir_context->cfg()->block(message_.block_id())->GetParent()); + + // Find the successor of |source_block| that dominates the predecessor of + // the merge block |predecessor_id|. + auto it = std::find_if( + successor_ids.begin(), successor_ids.end(), + [predecessor_id, dominator_analysis](uint32_t successor_id) { + return dominator_analysis->Dominates(successor_id, predecessor_id); + }); + + // OpPhi requires a single operand pair for every predecessor of the + // OpPhi's block. + assert(it != successor_ids.end() && "Unable to insert OpPhi"); + + in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {successor_id_to_fresh_id.at(*it)}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}}); + } + + ir_context->cfg() + ->block(merge_block_id) + ->begin() + ->InsertBefore(MakeUnique( + ir_context, spv::Op::OpPhi, inst_to_propagate->type_id(), + message_.phi_fresh_id(), std::move(in_operands))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.phi_fresh_id()); + created_inst_ids.push_back(message_.phi_fresh_id()); + } + + // Make sure analyses are updated when we adjust users of |inst_to_propagate|. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Copy decorations from the original instructions to its propagated copies. + for (auto id : created_inst_ids) { + ir_context->get_decoration_mgr()->CloneDecorations( + inst_to_propagate->result_id(), id); + } + + // Remove all decorations from the original instruction. + ir_context->get_decoration_mgr()->RemoveDecorationsFrom( + inst_to_propagate->result_id()); + + // Update every use of the |inst_to_propagate| with a result id of some of the + // newly created instructions. + ir_context->get_def_use_mgr()->ForEachUse( + inst_to_propagate, [ir_context, &created_inst_ids]( + opt::Instruction* user, uint32_t operand_index) { + assert(ir_context->get_instr_block(user) && + "All decorations should have already been adjusted"); + + auto in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*user, operand_index); + for (auto id : created_inst_ids) { + if (fuzzerutil::IdIsAvailableAtUse(ir_context, user, in_operand_index, + id)) { + user->SetInOperand(in_operand_index, {id}); + return; + } + } + + // Every user of |inst_to_propagate| must be updated since we will + // remove that instruction from the module. + assert(false && "Every user of |inst_to_propagate| must be updated"); + }); + + // Add synonyms about newly created instructions. + assert(inst_to_propagate->HasResultId() && + "Result id is required to add facts"); + if (transformation_context->GetFactManager()->IdIsIrrelevant( + inst_to_propagate->result_id())) { + for (auto id : created_inst_ids) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant(id); + } + } else { + std::vector non_irrelevant_ids; + for (auto id : created_inst_ids) { + // |id| can be irrelevant implicitly (e.g. if we propagate it into a dead + // block). + if (!transformation_context->GetFactManager()->IdIsIrrelevant(id)) { + non_irrelevant_ids.push_back(id); + } + } + + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + inst_to_propagate->result_id())) { + for (auto id : non_irrelevant_ids) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(id); + } + } + + for (auto id : non_irrelevant_ids) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(id, {}), + MakeDataDescriptor(non_irrelevant_ids[0], {})); + } + } + + // Remove the propagated instruction from the module. + ir_context->KillInst(inst_to_propagate); + + // We've adjusted all users - make sure these changes are analyzed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationPropagateInstructionDown::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_propagate_instruction_down() = message_; + return result; +} + +bool TransformationPropagateInstructionDown::IsOpcodeSupported(spv::Op opcode) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // We only support "simple" instructions that don't work with memory. + // We should extend this so that we support the ones that modify the memory + // too. + switch (opcode) { + case spv::Op::OpUndef: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpArrayLength: + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCopyObject: + case spv::Op::OpTranspose: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + case spv::Op::OpBitcast: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpCopyLogical: + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + return true; + default: + return false; + } +} + +opt::Instruction* +TransformationPropagateInstructionDown::GetInstructionToPropagate( + opt::IRContext* ir_context, uint32_t block_id) { + auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + for (auto it = block->rbegin(); it != block->rend(); ++it) { + if (!it->result_id() || !it->type_id() || + !IsOpcodeSupported(it->opcode())) { + continue; + } + + auto all_users_from_different_blocks = + ir_context->get_def_use_mgr()->WhileEachUser( + &*it, [ir_context, block](opt::Instruction* user) { + return ir_context->get_instr_block(user) != block; + }); + + if (!all_users_from_different_blocks) { + // We can't propagate an instruction if it's used in the same block. + continue; + } + + return &*it; + } + + return nullptr; +} + +bool TransformationPropagateInstructionDown::IsApplicableToBlock( + opt::IRContext* ir_context, uint32_t block_id) { + // Check that |block_id| is valid. + const auto* block = fuzzerutil::MaybeFindBlock(ir_context, block_id); + if (!block) { + return false; + } + + // |block| must be reachable. + if (!ir_context->IsReachable(*block)) { + return false; + } + + // The block must have an instruction to propagate. + const auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, block_id); + if (!inst_to_propagate) { + return false; + } + + // Check that |block| has successors. + auto successor_ids = GetAcceptableSuccessors(ir_context, block_id); + if (successor_ids.empty()) { + return false; + } + + // Check that |successor_block| doesn't have any OpPhi instructions that + // use |inst|. + for (auto successor_id : successor_ids) { + for (const auto& maybe_phi_inst : *ir_context->cfg()->block(successor_id)) { + if (maybe_phi_inst.opcode() != spv::Op::OpPhi) { + // OpPhis can be intermixed with OpLine and OpNoLine. + continue; + } + + for (uint32_t i = 0; i < maybe_phi_inst.NumInOperands(); i += 2) { + if (maybe_phi_inst.GetSingleWordInOperand(i) == + inst_to_propagate->result_id()) { + return false; + } + } + } + } + + // Get the result id of the block we will insert OpPhi instruction into. + // This is either 0 or a result id of some merge block in the function. + auto phi_block_id = + GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids); + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + + // Make sure we can adjust all users of the propagated instruction. + return ir_context->get_def_use_mgr()->WhileEachUse( + inst_to_propagate, + [ir_context, &successor_ids, dominator_analysis, phi_block_id]( + opt::Instruction* user, uint32_t index) { + const auto* user_block = ir_context->get_instr_block(user); + + if (!user_block) { + // |user| might be a global instruction (e.g. OpDecorate). + return true; + } + + // Check that at least one of the ids in |successor_ids| or a + // |phi_block_id| dominates |user|'s block (or its predecessor if the + // user is an OpPhi). We can't use fuzzerutil::IdIsAvailableAtUse since + // the id in question hasn't yet been created in the module. + auto block_id_to_dominate = user->opcode() == spv::Op::OpPhi + ? user->GetSingleWordOperand(index + 1) + : user_block->id(); + + if (phi_block_id != 0 && + dominator_analysis->Dominates(phi_block_id, block_id_to_dominate)) { + return true; + } + + return std::any_of( + successor_ids.begin(), successor_ids.end(), + [dominator_analysis, block_id_to_dominate](uint32_t id) { + return dominator_analysis->Dominates(id, block_id_to_dominate); + }); + }); +} + +opt::Instruction* +TransformationPropagateInstructionDown::GetFirstInsertBeforeInstruction( + opt::IRContext* ir_context, uint32_t block_id, spv::Op opcode) { + auto* block = ir_context->cfg()->block(block_id); + + auto it = block->begin(); + + while (it != block->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(opcode, it)) { + ++it; + } + + return it == block->end() ? nullptr : &*it; +} + +std::unordered_set +TransformationPropagateInstructionDown::GetAcceptableSuccessors( + opt::IRContext* ir_context, uint32_t block_id) { + const auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + const auto* inst = GetInstructionToPropagate(ir_context, block_id); + assert(inst && "The block must have an instruction to propagate"); + + std::unordered_set result; + block->ForEachSuccessorLabel([ir_context, &result, + inst](uint32_t successor_id) { + if (result.count(successor_id)) { + return; + } + + auto* successor_block = ir_context->cfg()->block(successor_id); + + // We can't propagate |inst| into |successor_block| if the latter is not + // dominated by the |inst|'s dependencies. + if (!inst->WhileEachInId([ir_context, successor_block](const uint32_t* id) { + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, &*successor_block->begin(), *id); + })) { + return; + } + + // We don't propagate any "special" instructions (e.g. OpSelectionMerge + // etc), thus, insertion point must always exist if the module is valid. + assert(GetFirstInsertBeforeInstruction(ir_context, successor_id, + inst->opcode()) && + "There must exist an insertion point."); + + result.insert(successor_id); + }); + + return result; +} + +uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId( + opt::IRContext* ir_context, uint32_t block_id, + const opt::Instruction& inst_to_propagate, + const std::unordered_set& successor_ids) { + const auto* block = ir_context->cfg()->block(block_id); + + // |block_id| must belong to some construct. + auto merge_block_id = + block->GetMergeInst() + ? block->GetMergeInst()->GetSingleWordInOperand(0) + : ir_context->GetStructuredCFGAnalysis()->MergeBlock(block_id); + if (!merge_block_id) { + return 0; + } + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + + // Check that |merge_block_id| is reachable in the CFG and |block_id| + // dominates |merge_block_id|. + if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) || + !dominator_analysis->Dominates(block_id, merge_block_id)) { + return 0; + } + + // We can't insert an OpPhi into |merge_block_id| if it's an acceptable + // successor of |block_id|. + if (successor_ids.count(merge_block_id)) { + return 0; + } + + // All predecessors of the merge block must be dominated by at least one + // successor of the |block_id|. + assert(!ir_context->cfg()->preds(merge_block_id).empty() && + "Merge block must be reachable"); + for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) { + if (std::none_of( + successor_ids.begin(), successor_ids.end(), + [dominator_analysis, predecessor_id](uint32_t successor_id) { + return dominator_analysis->Dominates(successor_id, + predecessor_id); + })) { + return 0; + } + } + + const auto* propagate_type = + ir_context->get_type_mgr()->GetType(inst_to_propagate.type_id()); + assert(propagate_type && "|inst_to_propagate| must have a valid type"); + + // VariablePointers capability implicitly declares + // VariablePointersStorageBuffer. We need those capabilities since otherwise + // OpPhi instructions cannot have operands of pointer types. + if (propagate_type->AsPointer() && + !ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointersStorageBuffer)) { + return 0; + } + + return merge_block_id; +} + +std::unordered_set +TransformationPropagateInstructionDown::GetFreshIds() const { + std::unordered_set result = {message_.phi_fresh_id()}; + for (const auto& pair : message_.successor_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h new file mode 100644 index 00000000..91339285 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPropagateInstructionDown : public Transformation { + public: + explicit TransformationPropagateInstructionDown( + protobufs::TransformationPropagateInstructionDown message); + + TransformationPropagateInstructionDown( + uint32_t block_id, uint32_t phi_fresh_id, + const std::map& successor_id_to_fresh_id); + + // - It should be possible to apply this transformation to |block_id| (see + // IsApplicableToBlock method). + // - Every acceptable successor of |block_id| (see GetAcceptableSuccessors + // method) must have an entry in the |successor_id_to_fresh_id| map unless + // overflow ids are available. + // - All values in |successor_id_to_fresh_id| and |phi_fresh_id| must be + // unique and fresh. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds a clone of the propagated instruction into every acceptable + // successor of |block_id|. + // - Removes the original instruction. + // - Creates an OpPhi instruction if possible, that tries to group created + // clones. + // - If the original instruction's id was irrelevant - marks created + // instructions as irrelevant. Otherwise, marks the created instructions as + // synonymous to each other if possible (i.e. skips instructions, copied + // into dead blocks). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if this transformation can be applied to the block with id + // |block_id|. Concretely, returns true iff: + // - |block_id| is a result id of some reachable basic block in the module. + // - the block has an instruction to propagate (see + // GetInstructionToPropagate method). + // - the block has at least one acceptable successor (see + // GetAcceptableSuccessors method). + // - none of the acceptable successors have OpPhi instructions that use the + // original instruction. + // - it is possible to replace every use of the original instruction with some + // of the propagated instructions (or an OpPhi if we can create it - see + // GetOpPhiBlockId method). + static bool IsApplicableToBlock(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns ids of successors of |block_id|, that can be used to propagate an + // instruction into. Concretely, a successor block is acceptable if all + // dependencies of the propagated instruction dominate it. Note that this + // implies that an acceptable successor must be reachable in the CFG. + // For example: + // %1 = OpLabel + // OpSelectionMerge %2 None + // OpBranchConditional %cond %2 %3 + // %3 = OpLabel + // %4 = OpUndef %int + // %5 = OpCopyObject %int %4 + // OpBranch %2 + // %2 = OpLabel + // ... + // In this example, %2 is not an acceptable successor of %3 since one of the + // dependencies (%4) of the propagated instruction (%5) does not dominate it. + static std::unordered_set GetAcceptableSuccessors( + opt::IRContext* ir_context, uint32_t block_id); + + std::unordered_set GetFreshIds() const override; + + private: + // Returns the last possible instruction in the |block_id| that satisfies the + // following properties: + // - has result id + // - has type id + // - has supported opcode (see IsOpcodeSupported method) + // - has no users in its basic block. + // Returns nullptr if no such an instruction exists. For example: + // %1 = OpLabel + // %2 = OpUndef %int + // %3 = OpUndef %int + // OpStore %var %3 + // OpBranch %some_block + // In this example: + // - We cannot propagate OpBranch nor OpStore since they both have unsupported + // opcodes and have neither result ids nor type ids. + // - We cannot propagate %3 either since it is used by OpStore. + // - We can propagate %2 since it satisfies all our conditions. + // The basic idea behind this method it to make sure that the returned + // instruction will not break domination rules in its original block when + // propagated. + static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns true if |opcode| is supported by this transformation. + static bool IsOpcodeSupported(spv::Op opcode); + + // Returns the first instruction in the |block| that allows us to insert + // |opcode| above itself. Returns nullptr is no such instruction exists. + static opt::Instruction* GetFirstInsertBeforeInstruction( + opt::IRContext* ir_context, uint32_t block_id, spv::Op opcode); + + // Returns a result id of a basic block, where an OpPhi instruction can be + // inserted. Returns nullptr if it's not possible to create an OpPhi. The + // created OpPhi instruction groups all the propagated clones of the original + // instruction. |block_id| is a result id of the block we propagate the + // instruction from. |successor_ids| contains result ids of the successors we + // propagate the instruction into. Concretely, returns a non-null value if: + // - |block_id| is in some construct. + // - The merge block of that construct is reachable. + // - |block_id| dominates that merge block. + // - That merge block may not be an acceptable successor of |block_id|. + // - There must be at least one |block_id|'s acceptable successor for every + // predecessor of the merge block, dominating that predecessor. + // - We can't create an OpPhi if the module has neither VariablePointers nor + // VariablePointersStorageBuffer capabilities. + // A simple example of when we can insert an OpPhi instruction is: + // - This snippet of code: + // %1 = OpLabel + // %2 = OpUndef %int + // OpSelectionMerge %5 None + // OpBranchConditional %cond %3 %4 + // %3 = OpLabel + // OpBranch %5 + // %4 = OpLabel + // OpBranch %5 + // %5 = OpLabel + // ... + // will be transformed into the following one (if %2 is propagated): + // %1 = OpLabel + // OpSelectionMerge %5 None + // OpBranchConditional %cond %3 %4 + // %3 = OpLabel + // %6 = OpUndef %int + // OpBranch %5 + // %4 = OpLabel + // %7 = OpUndef %int + // OpBranch %5 + // %5 = OpLabel + // %8 = OpPhi %int %6 %3 %7 %4 + // ... + // The fact that we introduce an OpPhi allows us to increase the applicability + // of the transformation. Concretely, we wouldn't be able to apply it in the + // example above if %2 were used in %5. Some more complicated examples can be + // found in unit tests. + static uint32_t GetOpPhiBlockId( + opt::IRContext* ir_context, uint32_t block_id, + const opt::Instruction& inst_to_propagate, + const std::unordered_set& successor_ids); + + protobufs::TransformationPropagateInstructionDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp new file mode 100644 index 00000000..f9674fa9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp @@ -0,0 +1,417 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_up.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { +namespace { + +uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst, + uint32_t label_id) { + assert(phi_inst.opcode() == spv::Op::OpPhi && "|phi_inst| is not an OpPhi"); + + for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) { + if (phi_inst.GetSingleWordInOperand(i) == label_id) { + return phi_inst.GetSingleWordInOperand(i - 1); + } + } + + return 0; +} + +bool ContainsPointers(const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kPointer: + return true; + case opt::analysis::Type::kStruct: + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsPointers(*element_type); + }); + default: + return false; + } +} + +bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) { + const auto* inst_block = ir_context->get_instr_block(inst); + assert(inst_block && + "This function shouldn't be applied to global instructions or function" + "parameters"); + + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + const auto& operand = inst->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID) { + // Consider only operands. + continue; + } + + auto* dependency = ir_context->get_def_use_mgr()->GetDef(operand.words[0]); + assert(dependency && "Operand has invalid id"); + + if (ir_context->get_instr_block(dependency) == inst_block && + dependency->opcode() != spv::Op::OpPhi) { + // |dependency| is "valid" if it's an OpPhi from the same basic block or + // an instruction from a different basic block. + return false; + } + } + + return true; +} + +} // namespace + +TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( + protobufs::TransformationPropagateInstructionUp message) + : message_(std::move(message)) {} + +TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( + uint32_t block_id, + const std::map& predecessor_id_to_fresh_id) { + message_.set_block_id(block_id); + *message_.mutable_predecessor_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(predecessor_id_to_fresh_id); +} + +bool TransformationPropagateInstructionUp::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that we can apply this transformation to the |block_id|. + if (!IsApplicableToBlock(ir_context, message_.block_id())) { + return false; + } + + const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap( + message_.predecessor_id_to_fresh_id()); + for (auto id : ir_context->cfg()->preds(message_.block_id())) { + // Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id| + // map. + if (!predecessor_id_to_fresh_id.count(id)) { + return false; + } + } + + std::vector maybe_fresh_ids; + maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size()); + for (const auto& entry : predecessor_id_to_fresh_id) { + maybe_fresh_ids.push_back(entry.second); + } + + // All ids must be unique and fresh. + return !fuzzerutil::HasDuplicates(maybe_fresh_ids) && + std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationPropagateInstructionUp::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = GetInstructionToPropagate(ir_context, message_.block_id()); + assert(inst && + "The block must have at least one supported instruction to propagate"); + assert(inst->result_id() && inst->type_id() && + "|inst| must have a result id and a type id"); + + opt::Instruction::OperandList op_phi_operands; + const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap( + message_.predecessor_id_to_fresh_id()); + std::unordered_set visited_predecessors; + for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) { + // A block can have multiple identical predecessors. + if (visited_predecessors.count(predecessor_id)) { + continue; + } + + visited_predecessors.insert(predecessor_id); + + auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id); + + // Compute InOperands for the OpPhi instruction to be inserted later. + op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {new_result_id}}); + op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}}); + + // Create a clone of the |inst| to be inserted into the |predecessor_id|. + std::unique_ptr clone(inst->Clone(ir_context)); + clone->SetResultId(new_result_id); + + fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id); + + // Adjust |clone|'s operands to account for possible dependencies on OpPhi + // instructions from the same basic block. + for (uint32_t i = 0; i < clone->NumInOperands(); ++i) { + auto& operand = clone->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID) { + // Consider only ids. + continue; + } + + const auto* dependency_inst = + ir_context->get_def_use_mgr()->GetDef(operand.words[0]); + assert(dependency_inst && "|clone| depends on an invalid id"); + + if (ir_context->get_instr_block(dependency_inst->result_id()) != + ir_context->cfg()->block(message_.block_id())) { + // We don't need to adjust anything if |dependency_inst| is from a + // different block, a global instruction or a function parameter. + continue; + } + + assert(dependency_inst->opcode() == spv::Op::OpPhi && + "Propagated instruction can depend only on OpPhis from the same " + "basic block or instructions from different basic blocks"); + + auto new_id = GetResultIdFromLabelId(*dependency_inst, predecessor_id); + assert(new_id && "OpPhi instruction is missing a predecessor"); + operand.words[0] = new_id; + } + + auto* insert_before_inst = fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, predecessor_id, clone->opcode()); + assert(insert_before_inst && "Can't insert |clone| into |predecessor_id"); + + insert_before_inst->InsertBefore(std::move(clone)); + } + + // Insert an OpPhi instruction into the basic block of |inst|. + ir_context->get_instr_block(inst)->begin()->InsertBefore( + MakeUnique(ir_context, spv::Op::OpPhi, inst->type_id(), + inst->result_id(), + std::move(op_phi_operands))); + + // Remove |inst| from the basic block. + ir_context->KillInst(inst); + + // We have changed the module so most analyzes are now invalid. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPropagateInstructionUp::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_propagate_instruction_up() = message_; + return result; +} + +bool TransformationPropagateInstructionUp::IsOpcodeSupported(spv::Op opcode) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // We only support "simple" instructions that don't work with memory. + // We should extend this so that we support the ones that modify the memory + // too. + switch (opcode) { + case spv::Op::OpUndef: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpArrayLength: + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCopyObject: + case spv::Op::OpTranspose: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: + case spv::Op::OpBitcast: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpCopyLogical: + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + return true; + default: + return false; + } +} + +opt::Instruction* +TransformationPropagateInstructionUp::GetInstructionToPropagate( + opt::IRContext* ir_context, uint32_t block_id) { + auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + for (auto& inst : *block) { + // We look for the first instruction in the block that satisfies the + // following rules: + // - it's not an OpPhi + // - it must be supported by this transformation + // - it may depend only on instructions from different basic blocks or on + // OpPhi instructions from the same basic block. + if (inst.opcode() == spv::Op::OpPhi || !IsOpcodeSupported(inst.opcode()) || + !inst.type_id() || !inst.result_id()) { + continue; + } + + const auto* inst_type = ir_context->get_type_mgr()->GetType(inst.type_id()); + assert(inst_type && "|inst| has invalid type"); + + if (inst_type->AsSampledImage()) { + // OpTypeSampledImage cannot be used as an argument to OpPhi instructions, + // thus we cannot support this type. + continue; + } + + if (!ir_context->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointersStorageBuffer) && + ContainsPointers(*inst_type)) { + // OpPhi supports pointer operands only with VariablePointers or + // VariablePointersStorageBuffer capabilities. + // + // Note that VariablePointers capability implicitly declares + // VariablePointersStorageBuffer capability. + continue; + } + + if (!HasValidDependencies(ir_context, &inst)) { + continue; + } + + return &inst; + } + + return nullptr; +} + +bool TransformationPropagateInstructionUp::IsApplicableToBlock( + opt::IRContext* ir_context, uint32_t block_id) { + // Check that |block_id| is valid. + const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!label_inst || label_inst->opcode() != spv::Op::OpLabel) { + return false; + } + + // Check that |block| has predecessors. + const auto& predecessors = ir_context->cfg()->preds(block_id); + if (predecessors.empty()) { + return false; + } + + // The block must contain an instruction to propagate. + const auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, block_id); + if (!inst_to_propagate) { + return false; + } + + // We should be able to insert |inst_to_propagate| into every predecessor of + // |block|. + return std::all_of(predecessors.begin(), predecessors.end(), + [ir_context, inst_to_propagate](uint32_t predecessor_id) { + return fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, predecessor_id, + inst_to_propagate->opcode()) != nullptr; + }); +} + +std::unordered_set TransformationPropagateInstructionUp::GetFreshIds() + const { + std::unordered_set result; + for (auto& pair : message_.predecessor_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h new file mode 100644 index 00000000..93aa365a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPropagateInstructionUp : public Transformation { + public: + explicit TransformationPropagateInstructionUp( + protobufs::TransformationPropagateInstructionUp message); + + TransformationPropagateInstructionUp( + uint32_t block_id, + const std::map& predecessor_id_to_fresh_id); + + // - |block_id| must be a valid result id of some OpLabel instruction. + // - |block_id| must have at least one predecessor + // - |block_id| must contain an instruction that can be propagated using this + // transformation + // - the instruction can be propagated if: + // - it's not an OpPhi + // - it is supported by this transformation + // - it depends only on instructions from different basic blocks or on + // OpPhi instructions from the same basic block + // - it should be possible to insert the propagated instruction at the end of + // each |block_id|'s predecessor + // - |predecessor_id_to_fresh_id| must have an entry for at least every + // predecessor of |block_id| + // - each value in the |predecessor_id_to_fresh_id| map must be a fresh id + // - all fresh ids in the |predecessor_id_to_fresh_id| must be unique + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts a copy of the propagated instruction into each |block_id|'s + // predecessor. Replaces the original instruction with an OpPhi referring + // inserted copies. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if this transformation can be applied to the block with id + // |block_id|. Concretely, returns true iff: + // - |block_id| is a valid id of some block in the module + // - |block_id| has predecessors + // - |block_id| contains an instruction that can be propagated + // - it is possible to insert the propagated instruction into every + // |block_id|'s predecessor + static bool IsApplicableToBlock(opt::IRContext* ir_context, + uint32_t block_id); + + private: + // Returns the instruction that will be propagated into the predecessors of + // the |block_id|. Returns nullptr if no such an instruction exists. + static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns true if |opcode| is supported by this transformation. + static bool IsOpcodeSupported(spv::Op opcode); + + protobufs::TransformationPropagateInstructionUp message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp new file mode 100644 index 00000000..2372d70c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_push_id_through_variable.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( + protobufs::TransformationPushIdThroughVariable message) + : message_(std::move(message)) {} + +TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( + uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id, + uint32_t variable_storage_class, uint32_t initializer_id, + const protobufs::InstructionDescriptor& instruction_descriptor) { + message_.set_value_id(value_id); + message_.set_value_synonym_id(value_synonym_id); + message_.set_variable_id(variable_id); + message_.set_variable_storage_class(variable_storage_class); + message_.set_initializer_id(initializer_id); + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationPushIdThroughVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.value_synonym_id| and |message_.variable_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) || + !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) { + return false; + } + + // The instruction to insert before must be defined. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + + // It must be valid to insert the OpStore and OpLoad instruction before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpStore, instruction_to_insert_before) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpLoad, instruction_to_insert_before)) { + return false; + } + + // The instruction to insert before must belong to a reachable block. + auto basic_block = ir_context->get_instr_block(instruction_to_insert_before); + if (!ir_context->IsReachable(*basic_block)) { + return false; + } + + // The value instruction must be defined and have a type. + auto value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + if (!value_instruction || !value_instruction->type_id()) { + return false; + } + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, value_instruction->type_id(), + static_cast(message_.variable_storage_class())); + if (!pointer_type_id) { + return false; + } + + // |message_.variable_storage_class| must be private or function. + assert((message_.variable_storage_class() == + (uint32_t)spv::StorageClass::Private || + message_.variable_storage_class() == + (uint32_t)spv::StorageClass::Function) && + "The variable storage class must be private or function."); + + // Check that initializer is valid. + const auto* constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) || + value_instruction->type_id() != constant_inst->type_id()) { + return false; + } + + // |message_.value_id| must be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.value_id()); +} + +void TransformationPushIdThroughVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + + opt::Instruction* insert_before = + FindInstruction(message_.instruction_descriptor(), ir_context); + opt::BasicBlock* enclosing_block = ir_context->get_instr_block(insert_before); + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, value_instruction->type_id(), + static_cast(message_.variable_storage_class())); + assert(pointer_type_id && "The required pointer type must be available."); + + // Adds whether a global or local variable. + if (spv::StorageClass(message_.variable_storage_class()) == + spv::StorageClass::Private) { + opt::Instruction* global_variable = fuzzerutil::AddGlobalVariable( + ir_context, message_.variable_id(), pointer_type_id, + spv::StorageClass::Private, message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(global_variable); + } else { + opt::Function* function = + ir_context + ->get_instr_block( + FindInstruction(message_.instruction_descriptor(), ir_context)) + ->GetParent(); + opt::Instruction* local_variable = fuzzerutil::AddLocalVariable( + ir_context, message_.variable_id(), pointer_type_id, + function->result_id(), message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(local_variable); + ir_context->set_instr_block(local_variable, &*function->entry()); + } + + // First, insert the OpLoad instruction before |instruction_descriptor| and + // then insert the OpStore instruction before the OpLoad instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id()); + opt::Instruction* load_instruction = + insert_before->InsertBefore(MakeUnique( + ir_context, spv::Op::OpLoad, value_instruction->type_id(), + message_.value_synonym_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))); + opt::Instruction* store_instruction = + load_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction); + ir_context->set_instr_block(store_instruction, enclosing_block); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction); + ir_context->set_instr_block(load_instruction, enclosing_block); + + // We should be able to create a synonym of |value_id| if it's not irrelevant. + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + *value_instruction) && + !transformation_context->GetFactManager()->IdIsIrrelevant( + message_.value_synonym_id())) { + // Adds the fact that |message_.value_synonym_id| + // and |message_.value_id| are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.value_synonym_id(), {}), + MakeDataDescriptor(message_.value_id(), {})); + } +} + +protobufs::Transformation TransformationPushIdThroughVariable::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_push_id_through_variable() = message_; + return result; +} + +std::unordered_set TransformationPushIdThroughVariable::GetFreshIds() + const { + return {message_.value_synonym_id(), message_.variable_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.h new file mode 100644 index 00000000..ec6943ca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_push_id_through_variable.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPushIdThroughVariable : public Transformation { + public: + explicit TransformationPushIdThroughVariable( + protobufs::TransformationPushIdThroughVariable message); + + TransformationPushIdThroughVariable( + uint32_t value_id, uint32_t value_synonym_fresh_id, + uint32_t variable_fresh_id, uint32_t variable_storage_class, + uint32_t initializer_id, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.value_id| must be an instruction result id that has the same + // type as the pointee type of |message_.pointer_id| + // - |message_.value_synonym_id| must be fresh + // - |message_.variable_id| must be fresh + // - |message_.variable_storage_class| must be either StorageClassPrivate or + // StorageClassFunction + // - |message_.initializer_id| must be a result id of some constant in the + // module. Its type must be equal to the pointee type of the variable that + // will be created. + // - |message_.instruction_descriptor| must identify an instruction + // which it is valid to insert the OpStore and OpLoad instructions before it + // and must be belongs to a reachable block. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Stores |value_id| to |variable_id|, loads |variable_id| to + // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id| + // are synonymous if |value_id| and |value_synonym_id| are not irrelevant. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPushIdThroughVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp new file mode 100644 index 00000000..3278d7d8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_record_synonymous_constants.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants( + protobufs::TransformationRecordSynonymousConstants message) + : message_(std::move(message)) {} + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id) { + message_.set_constant1_id(constant1_id); + message_.set_constant2_id(constant2_id); +} + +bool TransformationRecordSynonymousConstants::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The ids must be different + if (message_.constant1_id() == message_.constant2_id()) { + return false; + } + + if (transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant1_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant2_id())) { + return false; + } + + return AreEquivalentConstants(ir_context, message_.constant1_id(), + message_.constant2_id()); +} + +void TransformationRecordSynonymousConstants::Apply( + opt::IRContext* /*unused*/, + TransformationContext* transformation_context) const { + // Add the fact to the fact manager + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.constant1_id(), {}), + MakeDataDescriptor(message_.constant2_id(), {})); +} + +protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_record_synonymous_constants() = message_; + return result; +} + +bool TransformationRecordSynonymousConstants::AreEquivalentConstants( + opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) { + const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1); + const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2); + + // Check that the definitions exist + if (!def_1 || !def_2) { + // We don't use an assertion since otherwise the shrinker fails. + return false; + } + + auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1); + auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2); + + // The ids must refer to constants. + if (!constant1 || !constant2) { + return false; + } + + // The types must be compatible. + if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(), + def_2->type_id())) { + return false; + } + + // If either constant is null, the other is equivalent iff it is zero-like + if (constant1->AsNullConstant()) { + return constant2->IsZero(); + } + + if (constant2->AsNullConstant()) { + return constant1->IsZero(); + } + + // If the constants are scalar, they are equal iff their words are the same + if (auto scalar1 = constant1->AsScalarConstant()) { + // Either both or neither constant is scalar since we've already checked + // that their types are compatible. + assert(constant2->AsScalarConstant() && "Both constants must be scalar"); + return scalar1->words() == constant2->AsScalarConstant()->words(); + } + + // The only remaining possibility is that the constants are composite + assert(constant1->AsCompositeConstant() && constant2->AsCompositeConstant() && + "Equivalence of constants can only be checked with scalar, composite " + "or null constants."); + + // Since the types match, we already know that the number of components is + // the same. We check that the input operands of the definitions are all + // constants and that they are pairwise equivalent. + for (uint32_t i = 0; i < def_1->NumInOperands(); i++) { + if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i), + def_2->GetSingleWordInOperand(i))) { + return false; + } + } + + // If we get here, all the components are equivalent + return true; +} + +std::unordered_set +TransformationRecordSynonymousConstants::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h new file mode 100644 index 00000000..d99b0e2a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H +#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationRecordSynonymousConstants : public Transformation { + public: + explicit TransformationRecordSynonymousConstants( + protobufs::TransformationRecordSynonymousConstants message); + + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id); + + // - |message_.constant_id| and |message_.synonym_id| are distinct ids + // of constants + // - |message_.constant_id| and |message_.synonym_id| refer to constants + // that are equivalent. + // Constants are equivalent if at least one of the following holds: + // - they are equal (i.e. they have the same type ids and equal values) + // - both of them represent zero-like values of compatible types + // - they are composite constants with compatible types and their + // components are pairwise equivalent + // Two types are compatible if at least one of the following holds: + // - they have the same id + // - they are integer scalar types with the same width + // - they are integer vectors and their components have the same width + // (this is always the case if the components are equivalent) + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds the fact that |message_.constant_id| and |message_.synonym_id| + // are synonyms to the fact manager. The module is not changed. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationRecordSynonymousConstants message_; + + // Returns true if the two given constants are equivalent + // (the description of IsApplicable specifies the conditions they must satisfy + // to be considered equivalent) + static bool AreEquivalentConstants(opt::IRContext* ir_context, + uint32_t constant_id1, + uint32_t constant_id2); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp new file mode 100644 index 00000000..03e97375 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp @@ -0,0 +1,236 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kOpCompositeExtractIndexLowOrderBits = 0; +const uint32_t kArithmeticInstructionIndexLeftInOperand = 0; +const uint32_t kArithmeticInstructionIndexRightInOperand = 1; +} // namespace + +TransformationReplaceAddSubMulWithCarryingExtended:: + TransformationReplaceAddSubMulWithCarryingExtended( + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message) + : message_(std::move(message)) {} + +TransformationReplaceAddSubMulWithCarryingExtended:: + TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id, + uint32_t result_id) { + message_.set_struct_fresh_id(struct_fresh_id); + message_.set_result_id(result_id); +} + +bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.struct_fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) { + return false; + } + + // |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul + // instruction. The instruction must be defined. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (instruction == nullptr) { + return false; + } + if (!TransformationReplaceAddSubMulWithCarryingExtended:: + IsInstructionSuitable(ir_context, *instruction)) { + return false; + } + + // The struct type for holding the intermediate result must exist in the + // module. The struct type is based on the operand type. + uint32_t operand_type_id = ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t struct_type_id = fuzzerutil::MaybeGetStructType( + ir_context, {operand_type_id, operand_type_id}); + if (struct_type_id == 0) { + return false; + } + return true; +} + +void TransformationReplaceAddSubMulWithCarryingExtended::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // |message_.struct_fresh_id| must be fresh. + assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) && + "|message_.struct_fresh_id| must be fresh"); + + // Get the signedness of an operand if it is an int or the signedness of a + // component if it is a vector. + auto type_id = + ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id(); + auto type = ir_context->get_type_mgr()->GetType(type_id); + bool operand_is_signed; + if (type->kind() == opt::analysis::Type::kVector) { + auto operand_type = type->AsVector()->element_type(); + operand_is_signed = operand_type->AsInteger()->IsSigned(); + } else { + operand_is_signed = type->AsInteger()->IsSigned(); + } + + auto original_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id()); + + // Determine the opcode of the new instruction that computes the result into a + // struct. + spv::Op new_instruction_opcode; + + switch (original_instruction->opcode()) { + case spv::Op::OpIAdd: + new_instruction_opcode = spv::Op::OpIAddCarry; + break; + case spv::Op::OpISub: + new_instruction_opcode = spv::Op::OpISubBorrow; + break; + case spv::Op::OpIMul: + if (!operand_is_signed) { + new_instruction_opcode = spv::Op::OpUMulExtended; + } else { + new_instruction_opcode = spv::Op::OpSMulExtended; + } + break; + default: + assert(false && "The instruction has an unsupported opcode."); + return; + } + // Get the type of struct type id holding the intermediate result based on the + // operand type. + uint32_t operand_type_id = + ir_context->get_def_use_mgr() + ->GetDef(original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t struct_type_id = fuzzerutil::MaybeGetStructType( + ir_context, {operand_type_id, operand_type_id}); + // Avoid unused variables in release mode. + (void)struct_type_id; + assert(struct_type_id && "The struct type must exist in the module."); + + // Insert the new instruction that computes the result into a struct before + // the |original_instruction|. + original_instruction->InsertBefore(MakeUnique( + ir_context, new_instruction_opcode, struct_type_id, + message_.struct_fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)}}, + {SPV_OPERAND_TYPE_ID, + {original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexRightInOperand)}}}))); + + // Insert the OpCompositeExtract after the added instruction. This instruction + // takes the first component of the struct which represents low-order bits of + // the operation. This is the original result. + original_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, original_instruction->type_id(), + message_.result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {kOpCompositeExtractIndexLowOrderBits}}}))); + + // Remove the original instruction. + ir_context->KillInst(original_instruction); + + // We have modified the module so most analyzes are now invalid. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + auto instruction_opcode = instruction.opcode(); + + // Only instructions OpIAdd, OpISub, OpIMul are supported. + switch (instruction_opcode) { + case spv::Op::OpIAdd: + case spv::Op::OpISub: + case spv::Op::OpIMul: + break; + default: + return false; + } + uint32_t operand_1_type_id = + ir_context->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t operand_2_type_id = + ir_context->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexRightInOperand)) + ->type_id(); + + uint32_t result_type_id = instruction.type_id(); + + // Both type ids of the operands and the result type ids must be equal. + if (operand_1_type_id != operand_2_type_id) { + return false; + } + if (operand_2_type_id != result_type_id) { + return false; + } + + // In case of OpIAdd and OpISub, the type must be unsigned. + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + + switch (instruction_opcode) { + case spv::Op::OpIAdd: + case spv::Op::OpISub: { + // In case of OpIAdd and OpISub if the operand is a vector, the component + // type must be unsigned. Otherwise (if the operand is an int), the + // operand must be unsigned. + bool operand_is_signed = + type->AsVector() + ? type->AsVector()->element_type()->AsInteger()->IsSigned() + : type->AsInteger()->IsSigned(); + if (operand_is_signed) { + return false; + } + } break; + default: + break; + } + return true; +} + +protobufs::Transformation +TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_add_sub_mul_with_carrying_extended() = message_; + return result; +} + +std::unordered_set +TransformationReplaceAddSubMulWithCarryingExtended::GetFreshIds() const { + return {message_.struct_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h new file mode 100644 index 00000000..9deb2803 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceAddSubMulWithCarryingExtended + : public Transformation { + public: + explicit TransformationReplaceAddSubMulWithCarryingExtended( + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message); + + explicit TransformationReplaceAddSubMulWithCarryingExtended( + uint32_t struct_fresh_id, uint32_t result_id); + + // - |message_.struct_fresh_id| must be fresh. + // - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul + // instruction. In this instruction the result type id and the type ids of + // the operands must be the same. + // - The type of struct holding the intermediate result must exists in the + // module. + // - For OpIAdd, OpISub both operands must be unsigned. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A transformation that replaces instructions OpIAdd, OpISub, OpIMul with + // pairs of instructions. The first one (OpIAddCarry, OpISubBorrow, + // OpUMulExtended, OpSMulExtended) computes the result into a struct. The + // second one extracts the appropriate component from the struct to yield the + // original result. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the + // transformation. + bool static IsInstructionSuitable(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp new file mode 100644 index 00000000..efd15551 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -0,0 +1,330 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// Given floating-point values |lhs| and |rhs|, and a floating-point binary +// operator |binop|, returns true if it is certain that 'lhs binop rhs' +// evaluates to |required_value|. +template +bool float_binop_evaluates_to(T lhs, T rhs, spv::Op binop, + bool required_value) { + // Infinity and NaN values are conservatively treated as out of scope. + if (!std::isfinite(lhs) || !std::isfinite(rhs)) { + return false; + } + bool binop_result; + // The following captures the binary operators that spirv-fuzz can actually + // generate when turning a boolean constant into a binary expression. + switch (binop) { + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + binop_result = (lhs > rhs); + break; + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + binop_result = (lhs <= rhs); + break; + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +// Analogous to 'float_binop_evaluates_to', but for signed int values. +template +bool signed_int_binop_evaluates_to(T lhs, T rhs, spv::Op binop, + bool required_value) { + bool binop_result; + switch (binop) { + case spv::Op::OpSGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case spv::Op::OpSGreaterThan: + binop_result = (lhs > rhs); + break; + case spv::Op::OpSLessThanEqual: + binop_result = (lhs <= rhs); + break; + case spv::Op::OpSLessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +// Analogous to 'float_binop_evaluates_to', but for unsigned int values. +template +bool unsigned_int_binop_evaluates_to(T lhs, T rhs, spv::Op binop, + bool required_value) { + bool binop_result; + switch (binop) { + case spv::Op::OpUGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case spv::Op::OpUGreaterThan: + binop_result = (lhs > rhs); + break; + case spv::Op::OpULessThanEqual: + binop_result = (lhs <= rhs); + break; + case spv::Op::OpULessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +} // namespace + +TransformationReplaceBooleanConstantWithConstantBinary:: + TransformationReplaceBooleanConstantWithConstantBinary( + protobufs::TransformationReplaceBooleanConstantWithConstantBinary + message) + : message_(std::move(message)) {} + +TransformationReplaceBooleanConstantWithConstantBinary:: + TransformationReplaceBooleanConstantWithConstantBinary( + const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id, + uint32_t rhs_id, spv::Op comparison_opcode, + uint32_t fresh_id_for_binary_operation) { + *message_.mutable_id_use_descriptor() = id_use_descriptor; + message_.set_lhs_id(lhs_id); + message_.set_rhs_id(rhs_id); + message_.set_opcode(uint32_t(comparison_opcode)); + message_.set_fresh_id_for_binary_operation(fresh_id_for_binary_operation); +} + +bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id for the binary result must be fresh + if (!fuzzerutil::IsFreshId(ir_context, + message_.fresh_id_for_binary_operation())) { + return false; + } + + // The used id must be for a boolean constant + auto boolean_constant = ir_context->get_def_use_mgr()->GetDef( + message_.id_use_descriptor().id_of_interest()); + if (!boolean_constant) { + return false; + } + if (!(boolean_constant->opcode() == spv::Op::OpConstantFalse || + boolean_constant->opcode() == spv::Op::OpConstantTrue)) { + return false; + } + + // The left-hand-side id must correspond to a constant instruction. + auto lhs_constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.lhs_id()); + if (!lhs_constant_inst) { + return false; + } + if (lhs_constant_inst->opcode() != spv::Op::OpConstant) { + return false; + } + + // The right-hand-side id must correspond to a constant instruction. + auto rhs_constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.rhs_id()); + if (!rhs_constant_inst) { + return false; + } + if (rhs_constant_inst->opcode() != spv::Op::OpConstant) { + return false; + } + + // The left- and right-hand side instructions must have the same type. + if (lhs_constant_inst->type_id() != rhs_constant_inst->type_id()) { + return false; + } + + // The expression 'LHS opcode RHS' must evaluate to the boolean constant. + auto lhs_constant = + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id()); + auto rhs_constant = + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id()); + bool expected_result = + (boolean_constant->opcode() == spv::Op::OpConstantTrue); + + const auto binary_opcode = static_cast(message_.opcode()); + + // We consider the floating point, signed and unsigned integer cases + // separately. In each case the logic is very similar. + if (lhs_constant->AsFloatConstant()) { + assert(rhs_constant->AsFloatConstant() && + "Both constants should be of the same type."); + if (lhs_constant->type()->AsFloat()->width() == 32) { + if (!float_binop_evaluates_to(lhs_constant->GetFloat(), + rhs_constant->GetFloat(), binary_opcode, + expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsFloat()->width() == 64); + if (!float_binop_evaluates_to(lhs_constant->GetDouble(), + rhs_constant->GetDouble(), binary_opcode, + expected_result)) { + return false; + } + } + } else { + assert(lhs_constant->AsIntConstant() && "Constants should be in or float."); + assert(rhs_constant->AsIntConstant() && + "Both constants should be of the same type."); + if (lhs_constant->type()->AsInteger()->IsSigned()) { + if (lhs_constant->type()->AsInteger()->width() == 32) { + if (!signed_int_binop_evaluates_to(lhs_constant->GetS32(), + rhs_constant->GetS32(), + binary_opcode, expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsInteger()->width() == 64); + if (!signed_int_binop_evaluates_to(lhs_constant->GetS64(), + rhs_constant->GetS64(), + binary_opcode, expected_result)) { + return false; + } + } + } else { + if (lhs_constant->type()->AsInteger()->width() == 32) { + if (!unsigned_int_binop_evaluates_to(lhs_constant->GetU32(), + rhs_constant->GetU32(), + binary_opcode, expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsInteger()->width() == 64); + if (!unsigned_int_binop_evaluates_to(lhs_constant->GetU64(), + rhs_constant->GetU64(), + binary_opcode, expected_result)) { + return false; + } + } + } + } + + // The id use descriptor must identify some instruction + auto instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + // The instruction must not be an OpVariable, because (a) we cannot insert + // a binary operator before an OpVariable, but in any case (b) the + // constant we would be replacing is the initializer constant of the + // OpVariable, and this cannot be the result of a binary operation. + if (instruction->opcode() == spv::Op::OpVariable) { + return false; + } + + return true; +} + +void TransformationReplaceBooleanConstantWithConstantBinary::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyWithResult(ir_context, transformation_context); +} + +opt::Instruction* +TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::analysis::Bool bool_type; + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_ID, {message_.lhs_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}}; + auto binary_instruction = MakeUnique( + ir_context, static_cast(message_.opcode()), + ir_context->get_type_mgr()->GetId(&bool_type), + message_.fresh_id_for_binary_operation(), operands); + opt::Instruction* result = binary_instruction.get(); + auto instruction_containing_constant_use = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + auto instruction_before_which_to_insert = instruction_containing_constant_use; + + // If |instruction_before_which_to_insert| is an OpPhi instruction, + // then |binary_instruction| will be inserted into the parent block associated + // with the OpPhi variable operand. + if (instruction_containing_constant_use->opcode() == spv::Op::OpPhi) { + instruction_before_which_to_insert = + ir_context->cfg() + ->block(instruction_containing_constant_use->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index() + 1)) + ->terminator(); + } + + // We want to insert the new instruction before the instruction that contains + // the use of the boolean, but we need to go backwards one more instruction if + // the using instruction is preceded by a merge instruction. + { + opt::Instruction* previous_node = + instruction_before_which_to_insert->PreviousNode(); + if (previous_node && + (previous_node->opcode() == spv::Op::OpLoopMerge || + previous_node->opcode() == spv::Op::OpSelectionMerge)) { + instruction_before_which_to_insert = previous_node; + } + } + + instruction_before_which_to_insert->InsertBefore( + std::move(binary_instruction)); + instruction_containing_constant_use->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.fresh_id_for_binary_operation()}); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.fresh_id_for_binary_operation()); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + return result; +} + +protobufs::Transformation +TransformationReplaceBooleanConstantWithConstantBinary::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_boolean_constant_with_constant_binary() = message_; + return result; +} + +std::unordered_set +TransformationReplaceBooleanConstantWithConstantBinary::GetFreshIds() const { + return {message_.fresh_id_for_binary_operation()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h new file mode 100644 index 00000000..4d48bf2c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceBooleanConstantWithConstantBinary + : public Transformation { + public: + explicit TransformationReplaceBooleanConstantWithConstantBinary( + protobufs::TransformationReplaceBooleanConstantWithConstantBinary + message); + + TransformationReplaceBooleanConstantWithConstantBinary( + const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id, + uint32_t rhs_id, spv::Op comparison_opcode, + uint32_t fresh_id_for_binary_operation); + + // - |message_.fresh_id_for_binary_operation| must not already be used by the + // module. + // - |message_.id_use_descriptor| must identify a use of a boolean constant c. + // - |message_.lhs_id| and |message.rhs_id| must be the ids of constant + // instructions with the same type + // - |message_.opcode| must be suitable for applying to |message.lhs_id| and + // |message_.rhs_id|, and the result must evaluate to the boolean constant + // c. + // - The boolean constant usage must not be an argument to OpPhi, because in + // this case it is not legal to insert a binary operator instruction right + // before the OpPhi. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider + // replacing a boolean in an OpPhi by adding a binary operator instruction + // to the parent block for the OpPhi. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A new instruction is added before the boolean constant usage that computes + // the result of applying |message_.opcode| to |message_.lhs_id| and + // |message_.rhs_id| is added, with result id + // |message_.fresh_id_for_binary_operation|. The boolean constant usage is + // replaced with this result id. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // The same as Apply, except that the newly-added binary instruction is + // returned. + opt::Instruction* ApplyWithResult( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceBooleanConstantWithConstantBinary message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp new file mode 100644 index 00000000..4150bb13 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceBranchFromDeadBlockWithExit:: + TransformationReplaceBranchFromDeadBlockWithExit( + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message) + : message_(std::move(message)) {} + +TransformationReplaceBranchFromDeadBlockWithExit:: + TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, + spv::Op opcode, + uint32_t return_value_id) { + message_.set_block_id(block_id); + message_.set_opcode(uint32_t(opcode)); + message_.set_return_value_id(return_value_id); +} + +bool TransformationReplaceBranchFromDeadBlockWithExit::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The block whose terminator is to be changed must exist. + auto block = ir_context->get_instr_block(message_.block_id()); + if (!block) { + return false; + } + if (!BlockIsSuitable(ir_context, transformation_context, *block)) { + return false; + } + auto function_return_type_id = block->GetParent()->type_id(); + switch (spv::Op(message_.opcode())) { + case spv::Op::OpKill: + for (auto& entry_point : ir_context->module()->entry_points()) { + if (spv::ExecutionModel(entry_point.GetSingleWordInOperand(0)) != + spv::ExecutionModel::Fragment) { + // OpKill is only allowed in a fragment shader. This is a + // conservative check: if the module contains a non-fragment entry + // point then adding an OpKill might lead to OpKill being used in a + // non-fragment shader. + return false; + } + } + break; + case spv::Op::OpReturn: + if (ir_context->get_def_use_mgr() + ->GetDef(function_return_type_id) + ->opcode() != spv::Op::OpTypeVoid) { + // OpReturn is only allowed in a function with void return type. + return false; + } + break; + case spv::Op::OpReturnValue: { + // If the terminator is to be changed to OpReturnValue, with + // |message_.return_value_id| being the value that will be returned, then + // |message_.return_value_id| must have a compatible type and be available + // at the block terminator. + auto return_value = + ir_context->get_def_use_mgr()->GetDef(message_.return_value_id()); + if (!return_value || return_value->type_id() != function_return_type_id) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, block->terminator(), message_.return_value_id())) { + return false; + } + break; + } + default: + assert(spv::Op(message_.opcode()) == spv::Op::OpUnreachable && + "Invalid early exit opcode."); + break; + } + return true; +} + +void TransformationReplaceBranchFromDeadBlockWithExit::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // If the successor block has OpPhi instructions then arguments related to + // |message_.block_id| need to be removed from these instruction. + auto block = ir_context->get_instr_block(message_.block_id()); + assert(block->terminator()->opcode() == spv::Op::OpBranch && + "Precondition: the block must end with OpBranch."); + auto successor = ir_context->get_instr_block( + block->terminator()->GetSingleWordInOperand(0)); + successor->ForEachPhiInst([block](opt::Instruction* phi_inst) { + opt::Instruction::OperandList new_phi_in_operands; + for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) { + if (phi_inst->GetSingleWordInOperand(i + 1) == block->id()) { + continue; + } + new_phi_in_operands.emplace_back(phi_inst->GetInOperand(i)); + new_phi_in_operands.emplace_back(phi_inst->GetInOperand(i + 1)); + } + assert(new_phi_in_operands.size() == phi_inst->NumInOperands() - 2); + phi_inst->SetInOperands(std::move(new_phi_in_operands)); + }); + + // Rewrite the terminator of |message_.block_id|. + opt::Instruction::OperandList new_terminator_in_operands; + if (spv::Op(message_.opcode()) == spv::Op::OpReturnValue) { + new_terminator_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.return_value_id()}}); + } + auto terminator = block->terminator(); + terminator->SetOpcode(static_cast(message_.opcode())); + terminator->SetInOperands(std::move(new_terminator_in_operands)); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationReplaceBranchFromDeadBlockWithExit::GetFreshIds() const { + return std::unordered_set(); +} + +protobufs::Transformation +TransformationReplaceBranchFromDeadBlockWithExit::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_branch_from_dead_block_with_exit() = message_; + return result; +} + +bool TransformationReplaceBranchFromDeadBlockWithExit::BlockIsSuitable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::BasicBlock& block) { + // The block must be dead. + if (!transformation_context.GetFactManager()->BlockIsDead(block.id())) { + return false; + } + // The block's terminator must be OpBranch. + if (block.terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + if (ir_context->GetStructuredCFGAnalysis()->IsInContinueConstruct( + block.id())) { + // Early exits from continue constructs are not allowed as they would break + // the SPIR-V structured control flow rules. + return false; + } + // We only allow changing OpBranch to an early terminator if the target of the + // OpBranch has at least one other predecessor. + auto successor = ir_context->get_instr_block( + block.terminator()->GetSingleWordInOperand(0)); + if (ir_context->cfg()->preds(successor->id()).size() < 2) { + return false; + } + // Make sure that domination rules are satisfied when we remove the branch + // from the |block| to its |successor|. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, block.id(), {ir_context, spv::Op::OpUnreachable}); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h new file mode 100644 index 00000000..9a5d42e2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h @@ -0,0 +1,87 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceBranchFromDeadBlockWithExit : public Transformation { + public: + explicit TransformationReplaceBranchFromDeadBlockWithExit( + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message); + + TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, + spv::Op opcode, + uint32_t return_value_id); + + // - |message_.block_id| must be the id of a dead block that is not part of + // a continue construct + // - |message_.block_id| must end with OpBranch + // - The successor of |message_.block_id| must have at least one other + // predecessor + // - |message_.opcode()| must be one of OpKill, OpReturn, OpReturnValue and + // OpUnreachable + // - |message_.opcode()| can only be OpKill if the module's entry points all + // have Fragment execution mode + // - |message_.opcode()| can only be OpReturn if the return type of the + // function containing the block is void + // - If |message_.opcode()| is OpReturnValue then |message_.return_value_id| + // must be an id that is available at the block terminator and that matches + // the return type of the enclosing function + // - Domination rules should be preserved when we apply this transformation. + // In particular, if some block appears after the |block_id|'s successor in + // the CFG, then that block cannot dominate |block_id|'s successor when this + // transformation is applied. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the terminator of |message_.block_id| to have opcode + // |message_.opcode|, additionally with input operand + // |message_.return_value_id| in the case that |message_.opcode| is + // OpReturnValue. + // + // If |message_.block_id|'s successor starts with OpPhi instructions these are + // updated so that they no longer refer to |message_.block_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |block| meets the criteria for having its + // terminator replaced with an early exit (see IsApplicable for details of the + // criteria.) + static bool BlockIsSuitable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::BasicBlock& block); + + private: + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp new file mode 100644 index 00000000..7174e6a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -0,0 +1,305 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_constant_with_uniform.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceConstantWithUniform:: + TransformationReplaceConstantWithUniform( + protobufs::TransformationReplaceConstantWithUniform message) + : message_(std::move(message)) {} + +TransformationReplaceConstantWithUniform:: + TransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) { + *message_.mutable_id_use_descriptor() = std::move(id_use); + *message_.mutable_uniform_descriptor() = std::move(uniform_descriptor); + message_.set_fresh_id_for_access_chain(fresh_id_for_access_chain); + message_.set_fresh_id_for_load(fresh_id_for_load); +} + +std::unique_ptr +TransformationReplaceConstantWithUniform::MakeAccessChainInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { + // The input operands for the access chain. + opt::Instruction::OperandList operands_for_access_chain; + + opt::Instruction* uniform_variable = + FindUniformVariable(message_.uniform_descriptor(), ir_context, false); + + // The first input operand is the id of the uniform variable. + operands_for_access_chain.push_back( + {SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}}); + + // The other input operands are the ids of the constants used to index into + // the uniform. The uniform buffer descriptor specifies a series of literals; + // for each we find the id of the instruction that defines it, and add these + // instruction ids as operands. + opt::analysis::Integer int_type(32, true); + auto registered_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); + for (auto index : message_.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + auto constant_id = ir_context->get_constant_mgr()->FindDeclaredConstant( + &int_constant, int_type_id); + operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}}); + } + + // The type id for the access chain is a uniform pointer with base type + // matching the given constant id type. + auto type_and_pointer_type = + ir_context->get_type_mgr()->GetTypeAndPointerType( + constant_type_id, spv::StorageClass::Uniform); + assert(type_and_pointer_type.first != nullptr); + assert(type_and_pointer_type.second != nullptr); + auto pointer_to_uniform_constant_type_id = + ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get()); + + return MakeUnique( + ir_context, spv::Op::OpAccessChain, pointer_to_uniform_constant_type_id, + message_.fresh_id_for_access_chain(), operands_for_access_chain); +} + +std::unique_ptr +TransformationReplaceConstantWithUniform::MakeLoadInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { + opt::Instruction::OperandList operands_for_load = { + {SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}}; + return MakeUnique( + ir_context, spv::Op::OpLoad, constant_type_id, + message_.fresh_id_for_load(), operands_for_load); +} + +opt::Instruction* +TransformationReplaceConstantWithUniform::GetInsertBeforeInstruction( + opt::IRContext* ir_context) const { + auto* result = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!result) { + return nullptr; + } + + // The use might be in an OpPhi instruction. + if (result->opcode() == spv::Op::OpPhi) { + // OpPhi instructions must be the first instructions in a block. Thus, we + // can't insert above the OpPhi instruction. Given the predecessor block + // that corresponds to the id use, get the last instruction in that block + // above which we can insert OpAccessChain and OpLoad. + return fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, + result->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index() + 1), + spv::Op::OpLoad); + } + + // The only operand that we could've replaced in the OpBranchConditional is + // the condition id. But that operand has a boolean type and uniform variables + // can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be + // an OpBranchConditional. + assert(result->opcode() != spv::Op::OpBranchConditional && + "OpBranchConditional has no operands to replace"); + + assert( + fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, result) && + "We should be able to insert OpLoad and OpAccessChain at this point"); + return result; +} + +bool TransformationReplaceConstantWithUniform::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The following is really an invariant of the transformation rather than + // merely a requirement of the precondition. We check it here since we cannot + // check it in the message_ constructor. + assert(message_.fresh_id_for_access_chain() != message_.fresh_id_for_load() && + "Fresh ids for access chain and load result cannot be the same."); + + // The ids for the access chain and load instructions must both be fresh. + if (!fuzzerutil::IsFreshId(ir_context, + message_.fresh_id_for_access_chain())) { + return false; + } + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_load())) { + return false; + } + + // The id specified in the id use descriptor must be that of a declared scalar + // constant. + auto declared_constant = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.id_use_descriptor().id_of_interest()); + if (!declared_constant) { + return false; + } + if (!declared_constant->AsScalarConstant()) { + return false; + } + + // The fact manager needs to believe that the uniform data element described + // by the uniform buffer element descriptor will hold a scalar value. + auto constant_id_associated_with_uniform = + transformation_context.GetFactManager()->GetConstantFromUniformDescriptor( + message_.uniform_descriptor()); + if (!constant_id_associated_with_uniform) { + return false; + } + auto constant_associated_with_uniform = + ir_context->get_constant_mgr()->FindDeclaredConstant( + constant_id_associated_with_uniform); + assert(constant_associated_with_uniform && + "The constant should be present in the module."); + if (!constant_associated_with_uniform->AsScalarConstant()) { + return false; + } + + // The types and values of the scalar value held in the id specified by the id + // use descriptor and in the uniform data element specified by the uniform + // buffer element descriptor need to match on both type and value. + if (!declared_constant->type()->IsSame( + constant_associated_with_uniform->type())) { + return false; + } + if (declared_constant->AsScalarConstant()->words() != + constant_associated_with_uniform->AsScalarConstant()->words()) { + return false; + } + + // The id use descriptor must identify some instruction with respect to the + // module. + auto instruction_using_constant = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!instruction_using_constant) { + return false; + } + + // The use must not be a variable initializer; these are required to be + // constants, so it would be illegal to replace one with a uniform access. + if (instruction_using_constant->opcode() == spv::Op::OpVariable) { + return false; + } + + // The module needs to have a uniform pointer type suitable for indexing into + // the uniform variable, i.e. matching the type of the constant we wish to + // replace with a uniform. + opt::analysis::Pointer pointer_to_type_of_constant( + declared_constant->type(), spv::StorageClass::Uniform); + if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) { + return false; + } + + // In order to index into the uniform, the module has got to contain the int32 + // type, plus an OpConstant for each of the indices of interest. + opt::analysis::Integer int_type(32, true); + if (!ir_context->get_type_mgr()->GetId(&int_type)) { + return false; + } + auto registered_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); + for (auto index : message_.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + if (!ir_context->get_constant_mgr()->FindDeclaredConstant(&int_constant, + int_type_id)) { + return false; + } + } + + // Once all checks are completed, we should be able to safely insert + // OpAccessChain and OpLoad into the module. + assert(GetInsertBeforeInstruction(ir_context) && + "There must exist an instruction that we can use to insert " + "OpAccessChain and OpLoad above"); + + return true; +} + +void TransformationReplaceConstantWithUniform::Apply( + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + // Get the instruction that contains the id use we wish to replace. + auto* instruction_containing_constant_use = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + assert(instruction_containing_constant_use && + "Precondition requires that the id use can be found."); + assert(instruction_containing_constant_use->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index()) == + message_.id_use_descriptor().id_of_interest() && + "Does not appear to be a usage of the desired id."); + + // The id of the type for the constant whose use we wish to replace. + auto constant_type_id = + ir_context->get_def_use_mgr() + ->GetDef(message_.id_use_descriptor().id_of_interest()) + ->type_id(); + + // Get an instruction that will be used to insert OpAccessChain and OpLoad. + auto* insert_before_inst = GetInsertBeforeInstruction(ir_context); + assert(insert_before_inst && + "There must exist an insertion point for OpAccessChain and OpLoad"); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); + + // Add an access chain instruction to target the uniform element. + auto access_chain_instruction = + MakeAccessChainInstruction(ir_context, constant_type_id); + auto access_chain_instruction_ptr = access_chain_instruction.get(); + insert_before_inst->InsertBefore(std::move(access_chain_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + access_chain_instruction_ptr); + ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block); + + // Add a load from this access chain. + auto load_instruction = MakeLoadInstruction(ir_context, constant_type_id); + auto load_instruction_ptr = load_instruction.get(); + insert_before_inst->InsertBefore(std::move(load_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction_ptr); + ir_context->set_instr_block(load_instruction_ptr, enclosing_block); + + // Adjust the instruction containing the usage of the constant so that this + // usage refers instead to the result of the load. + instruction_containing_constant_use->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.fresh_id_for_load()}); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_containing_constant_use); + ir_context->get_def_use_mgr()->AnalyzeInstUse( + instruction_containing_constant_use); + + // Update the module id bound to reflect the new instructions. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load()); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.fresh_id_for_access_chain()); +} + +protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_constant_with_uniform() = message_; + return result; +} + +std::unordered_set +TransformationReplaceConstantWithUniform::GetFreshIds() const { + return {message_.fresh_id_for_access_chain(), message_.fresh_id_for_load()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h new file mode 100644 index 00000000..21210927 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h @@ -0,0 +1,99 @@ +#include + +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ + +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceConstantWithUniform : public Transformation { + public: + explicit TransformationReplaceConstantWithUniform( + protobufs::TransformationReplaceConstantWithUniform message); + + TransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load); + + // - |message_.fresh_id_for_access_chain| and |message_.fresh_id_for_load| + // must be distinct fresh ids. + // - |message_.uniform_descriptor| specifies a result id and a list of integer + // literal indices. + // As an example, suppose |message_.uniform_descriptor| is (18, [0, 1, 0]) + // It is required that: + // - the result id (18 in our example) is the id of some uniform variable + // - the module contains an integer constant instruction corresponding to + // each of the literal indices; in our example there must thus be + // OpConstant instructions %A and %B say for each of 0 and 1 + // - it is legitimate to index into the uniform variable using the + // sequence of indices; in our example this means indexing into %18 + // using the sequence %A %B %A + // - the module contains a uniform pointer type corresponding to the type + // of the uniform data element obtained by following these indices + // - |message_.id_use_descriptor| identifies the use of some id %C. It is + // required that: + // - this use does indeed exist in the module + // - %C is an OpConstant + // - According to the fact manager, the uniform data element specified by + // |message_.uniform_descriptor| holds a value with the same type and + // value as %C + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Introduces two new instructions: + // - An access chain targeting the uniform data element specified by + // |message_.uniform_descriptor|, with result id + // |message_.fresh_id_for_access_chain| + // - A load from this access chain, with id |message_.fresh_id_for_load| + // - Replaces the id use specified by |message_.id_use_descriptor| with + // |message_.fresh_id_for_load| + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper method to create an access chain for the uniform element associated + // with the transformation. + std::unique_ptr MakeAccessChainInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; + + // Helper to create a load instruction. + std::unique_ptr MakeLoadInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; + + // OpAccessChain and OpLoad will be inserted above the instruction returned + // by this function. Returns nullptr if no such instruction is present. + opt::Instruction* GetInsertBeforeInstruction( + opt::IRContext* ir_context) const; + + protobufs::TransformationReplaceConstantWithUniform message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp new file mode 100644 index 00000000..8f141451 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + protobufs::TransformationReplaceCopyMemoryWithLoadStore message) + : message_(std::move(message)) {} + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor) { + message_.set_fresh_id(fresh_id); + *message_.mutable_copy_memory_instruction_descriptor() = + copy_memory_instruction_descriptor; +} + +bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The instruction to be replaced must be defined and have opcode + // OpCopyMemory. + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + if (!copy_memory_instruction || + copy_memory_instruction->opcode() != spv::Op::OpCopyMemory) { + return false; + } + return true; +} + +void TransformationReplaceCopyMemoryWithLoadStore::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + // |copy_memory_instruction| must be defined. + assert(copy_memory_instruction && + copy_memory_instruction->opcode() == spv::Op::OpCopyMemory && + "The required OpCopyMemory instruction must be defined."); + + // Integrity check: Both operands must be pointers. + + // Get types of ids used as a source and target of |copy_memory_instruction|. + auto target = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(0)); + auto source = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(1)); + auto target_type_opcode = + ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode(); + auto source_type_opcode = + ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode(); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target; + (void)source; + (void)target_type_opcode; + (void)source_type_opcode; + + assert(target_type_opcode == spv::Op::OpTypePointer && + source_type_opcode == spv::Op::OpTypePointer && + "Operands must be of type OpTypePointer"); + + // Integrity check: |source| and |target| must point to the same type. + uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, target->type_id()); + uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source->type_id()); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target_pointee_type; + (void)source_pointee_type; + + assert(target_pointee_type == source_pointee_type && + "Operands must have the same type to which they point to."); + + // First, insert the OpStore instruction before the OpCopyMemory instruction + // and then insert the OpLoad instruction before the OpStore instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {target->result_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, spv::Op::OpLoad, target_pointee_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {source->result_id()}}}))); + + // Remove the OpCopyMemory instruction. + ir_context->KillInst(copy_memory_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_memory_with_load_store() = message_; + return result; +} + +std::unordered_set +TransformationReplaceCopyMemoryWithLoadStore::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h new file mode 100644 index 00000000..55d1e064 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceCopyMemoryWithLoadStore : public Transformation { + public: + explicit TransformationReplaceCopyMemoryWithLoadStore( + protobufs::TransformationReplaceCopyMemoryWithLoadStore message); + + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor); + + // - |message_.fresh_id| must be fresh. + // - |message_.copy_memory_instruction_descriptor| must refer to an + // OpCopyMemory instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyMemory with loading the source variable to an + // intermediate value and storing this value into the target variable of the + // original OpCopyMemory instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyMemoryWithLoadStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp new file mode 100644 index 00000000..f08a7345 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + protobufs::TransformationReplaceCopyObjectWithStoreLoad message) + : message_(std::move(message)) {} + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id) { + message_.set_copy_object_result_id(copy_object_result_id); + message_.set_fresh_variable_id(fresh_variable_id); + message_.set_variable_storage_class(variable_storage_class); + message_.set_variable_initializer_id(variable_initializer_id); +} + +bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_variable_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) { + return false; + } + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + + // This must be a defined OpCopyObject instruction. + if (!copy_object_instruction || + copy_object_instruction->opcode() != spv::Op::OpCopyObject) { + return false; + } + + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (ir_context->get_def_use_mgr() + ->GetDef(copy_object_instruction->type_id()) + ->opcode() == spv::Op::OpTypePointer) { + return false; + } + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + if (!pointer_type_id) { + return false; + } + + // Check that initializer is valid. + const auto* constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id()); + if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) || + copy_object_instruction->type_id() != constant_inst->type_id()) { + return false; + } + // |message_.variable_storage_class| must be Private or Function. + return spv::StorageClass(message_.variable_storage_class()) == + spv::StorageClass::Private || + spv::StorageClass(message_.variable_storage_class()) == + spv::StorageClass::Function; +} + +void TransformationReplaceCopyObjectWithStoreLoad::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + // |copy_object_instruction| must be defined. + assert(copy_object_instruction && + copy_object_instruction->opcode() == spv::Op::OpCopyObject && + "The required OpCopyObject instruction must be defined."); + + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(copy_object_instruction); + + // Get id used as a source by the OpCopyObject instruction. + uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0); + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + assert(pointer_type_id && "The required pointer type must be available."); + + // Adds a global or local variable (according to the storage class). + if (spv::StorageClass(message_.variable_storage_class()) == + spv::StorageClass::Private) { + opt::Instruction* new_global = fuzzerutil::AddGlobalVariable( + ir_context, message_.fresh_variable_id(), pointer_type_id, + spv::StorageClass::Private, message_.variable_initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global); + } else { + opt::Function* function = + ir_context->get_instr_block(copy_object_instruction)->GetParent(); + opt::Instruction* new_local = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_variable_id(), pointer_type_id, + function->result_id(), message_.variable_initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local); + ir_context->set_instr_block(new_local, &*function->begin()); + } + + // First, insert the OpLoad instruction before the OpCopyObject instruction + // and then insert the OpStore instruction before the OpLoad instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id()); + opt::Instruction* load_instruction = + copy_object_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpLoad, copy_object_instruction->type_id(), + message_.copy_object_result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}}))); + opt::Instruction* store_instruction = + load_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {src_operand}}}))); + + // Register the new instructions with the def-use manager, and record their + // enclosing block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction); + ir_context->set_instr_block(store_instruction, enclosing_block); + ir_context->set_instr_block(load_instruction, enclosing_block); + + // Remove the CopyObject instruction. + ir_context->KillInst(copy_object_instruction); + + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.copy_object_result_id()) && + !transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) { + // Adds the fact that |message_.copy_object_result_id| + // and src_operand (id used by OpCopyObject) are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.copy_object_result_id(), {}), + MakeDataDescriptor(src_operand, {})); + } +} + +protobufs::Transformation +TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_object_with_store_load() = message_; + return result; +} + +std::unordered_set +TransformationReplaceCopyObjectWithStoreLoad::GetFreshIds() const { + return {message_.fresh_variable_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h new file mode 100644 index 00000000..8c5ce9e2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceCopyObjectWithStoreLoad : public Transformation { + public: + explicit TransformationReplaceCopyObjectWithStoreLoad( + protobufs::TransformationReplaceCopyObjectWithStoreLoad message); + + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id); + + // - |message_.copy_object_result_id| must be a result id of an OpCopyObject + // instruction. + // - |message_.fresh_variable_id| must be a fresh id given to variable used by + // OpStore. + // - |message_.variable_storage_class| must be either StorageClassPrivate or + // StorageClassFunction. + // - |message_.initializer_id| must be a result id of some constant in the + // module. Its type must be equal to the pointee type of the variable that + // will be created. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyObject with storing into a new variable and + // immediately loading from this variable to |result_id| of the original + // OpCopyObject instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyObjectWithStoreLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp new file mode 100644 index 00000000..8d21d233 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( + protobufs::TransformationReplaceIdWithSynonym message) + : message_(std::move(message)) {} + +TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( + protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) { + *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor); + message_.set_synonymous_id(synonymous_id); +} + +bool TransformationReplaceIdWithSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto id_of_interest = message_.id_use_descriptor().id_of_interest(); + + // Does the fact manager know about the synonym? + auto data_descriptor_for_synonymous_id = + MakeDataDescriptor(message_.synonymous_id(), {}); + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(id_of_interest, {}), + data_descriptor_for_synonymous_id)) { + return false; + } + + // Does the id use descriptor in the transformation identify an instruction? + auto use_instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!use_instruction) { + return false; + } + + uint32_t type_id_of_interest = + ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id(); + uint32_t type_id_synonym = ir_context->get_def_use_mgr() + ->GetDef(message_.synonymous_id()) + ->type_id(); + + // If the id of interest and the synonym are scalar or vector integer + // constants with different signedness, their use can only be swapped if the + // instruction is agnostic to the signedness of the operand. + if (!fuzzerutil::TypesAreCompatible( + ir_context, use_instruction->opcode(), + message_.id_use_descriptor().in_operand_index(), type_id_of_interest, + type_id_synonym)) { + return false; + } + + // Is the use suitable for being replaced in principle? + if (!fuzzerutil::IdUseCanBeReplaced( + ir_context, transformation_context, use_instruction, + message_.id_use_descriptor().in_operand_index())) { + return false; + } + + // The transformation is applicable if the synonymous id is available at the + // use point. + return fuzzerutil::IdIsAvailableAtUse( + ir_context, use_instruction, + message_.id_use_descriptor().in_operand_index(), + message_.synonymous_id()); +} + +void TransformationReplaceIdWithSynonym::Apply( + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto instruction_to_change = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + instruction_to_change->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.synonymous_id()}); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_to_change); + ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change); + + // No analyses need to be invalidated, since the transformation is local to a + // block, and the def-use analysis has been updated. +} + +protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_id_with_synonym() = message_; + return result; +} + +std::unordered_set TransformationReplaceIdWithSynonym::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h new file mode 100644 index 00000000..66f8e439 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceIdWithSynonym : public Transformation { + public: + explicit TransformationReplaceIdWithSynonym( + protobufs::TransformationReplaceIdWithSynonym message); + + TransformationReplaceIdWithSynonym( + protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); + + // - The fact manager must know that the id identified by + // |message_.id_use_descriptor| is synonymous with |message_.synonymous_id|. + // - Replacing the id in |message_.id_use_descriptor| by + // |message_.synonymous_id| must respect SPIR-V's rules about uses being + // dominated by their definitions. + // - The id use must be replaceable in principle. See + // fuzzerutil::IdUseCanBeReplaced for details. + // - |fresh_id_for_temporary| must be 0. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the use identified by |message_.id_use_descriptor| with the + // synonymous id identified by |message_.synonymous_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceIdWithSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp new file mode 100644 index 00000000..7f640639 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_irrelevant_id.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( + protobufs::TransformationReplaceIrrelevantId message) + : message_(std::move(message)) {} + +TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( + const protobufs::IdUseDescriptor& id_use_descriptor, + uint32_t replacement_id) { + *message_.mutable_id_use_descriptor() = id_use_descriptor; + message_.set_replacement_id(replacement_id); +} + +bool TransformationReplaceIrrelevantId::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto id_of_interest = message_.id_use_descriptor().id_of_interest(); + + // The id must be irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + id_of_interest)) { + return false; + } + + // Find the instruction containing the id use, which must exist. + auto use_instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!use_instruction) { + return false; + } + + // Check that the replacement id exists and retrieve its definition. + auto replacement_id_def = + ir_context->get_def_use_mgr()->GetDef(message_.replacement_id()); + if (!replacement_id_def) { + return false; + } + + // The type of the id of interest and of the replacement id must be the same. + uint32_t type_id_of_interest = + ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id(); + uint32_t type_replacement_id = replacement_id_def->type_id(); + if (type_id_of_interest != type_replacement_id) { + return false; + } + + // The replacement id must not be the result of an OpFunction instruction. + if (replacement_id_def->opcode() == spv::Op::OpFunction) { + return false; + } + + // Consistency check: an irrelevant id cannot be a pointer. + assert( + !ir_context->get_type_mgr()->GetType(type_id_of_interest)->AsPointer() && + "An irrelevant id cannot be a pointer"); + + uint32_t use_in_operand_index = + message_.id_use_descriptor().in_operand_index(); + + // The id use must be replaceable with any other id of the same type. + if (!fuzzerutil::IdUseCanBeReplaced(ir_context, transformation_context, + use_instruction, use_in_operand_index)) { + return false; + } + + if (AttemptsToReplaceVariableInitializerWithNonConstant( + *use_instruction, *replacement_id_def)) { + return false; + } + + // The id must be available to use at the use point. + return fuzzerutil::IdIsAvailableAtUse( + ir_context, use_instruction, + message_.id_use_descriptor().in_operand_index(), + message_.replacement_id()); +} + +void TransformationReplaceIrrelevantId::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Find the instruction. + auto instruction_to_change = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + + // Replace the instruction. + instruction_to_change->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.replacement_id()}); + + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_to_change); + ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change); + + // No analyses need to be invalidated, since the transformation is local to a + // block, and the def-use analysis has been updated. +} + +protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_irrelevant_id() = message_; + return result; +} + +std::unordered_set TransformationReplaceIrrelevantId::GetFreshIds() + const { + return std::unordered_set(); +} + +bool TransformationReplaceIrrelevantId:: + AttemptsToReplaceVariableInitializerWithNonConstant( + const opt::Instruction& use_instruction, + const opt::Instruction& replacement_for_use) { + return use_instruction.opcode() == spv::Op::OpVariable && + !spvOpcodeIsConstant(replacement_for_use.opcode()); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h new file mode 100644 index 00000000..e6210b4d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceIrrelevantId : public Transformation { + public: + explicit TransformationReplaceIrrelevantId( + protobufs::TransformationReplaceIrrelevantId message); + + TransformationReplaceIrrelevantId( + const protobufs::IdUseDescriptor& id_use_descriptor, + uint32_t replacement_id); + + // - The id of interest in |message_.id_use_descriptor| is irrelevant + // according to the fact manager. + // - The types of the original id and of the replacement ids are the same. + // - The replacement must not be the result id of an OpFunction instruction. + // - |message_.replacement_id| is available to use at the enclosing + // instruction of |message_.id_use_descriptor|. + // - The original id is in principle replaceable with any other id of the same + // type. See fuzzerutil::IdUseCanBeReplaced for details. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the use of an irrelevant id identified by + // |message_.id_use_descriptor| with the id |message_.replacement_id|, which + // has the same type as the id of interest. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |use_instruction| is OpVariable and + // |replacement_for_use| is not a constant instruction - i.e., if it would be + // illegal to replace the variable's initializer with the given instruction. + static bool AttemptsToReplaceVariableInitializerWithNonConstant( + const opt::Instruction& use_instruction, + const opt::Instruction& replacement_for_use); + + private: + protobufs::TransformationReplaceIrrelevantId message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp new file mode 100644 index 00000000..fb7b2948 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -0,0 +1,1041 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceLinearAlgebraInstruction:: + TransformationReplaceLinearAlgebraInstruction( + protobufs::TransformationReplaceLinearAlgebraInstruction message) + : message_(std::move(message)) {} + +TransformationReplaceLinearAlgebraInstruction:: + TransformationReplaceLinearAlgebraInstruction( + const std::vector& fresh_ids, + const protobufs::InstructionDescriptor& instruction_descriptor) { + for (auto fresh_id : fresh_ids) { + message_.add_fresh_ids(fresh_id); + } + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationReplaceLinearAlgebraInstruction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + // It must be a linear algebra instruction. + if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) { + return false; + } + + // |message_.fresh_ids.size| must be the exact number of fresh ids needed to + // apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + // All ids in |message_.fresh_ids| must be fresh. + for (uint32_t fresh_id : message_.fresh_ids()) { + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + } + + return true; +} + +void TransformationReplaceLinearAlgebraInstruction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto linear_algebra_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + switch (linear_algebra_instruction->opcode()) { + case spv::Op::OpTranspose: + ReplaceOpTranspose(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpVectorTimesScalar: + ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpMatrixTimesScalar: + ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpVectorTimesMatrix: + ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpMatrixTimesVector: + ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpMatrixTimesMatrix: + ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpOuterProduct: + ReplaceOpOuterProduct(ir_context, linear_algebra_instruction); + break; + case spv::Op::OpDot: + ReplaceOpDot(ir_context, linear_algebra_instruction); + break; + default: + assert(false && "Should be unreachable."); + break; + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceLinearAlgebraInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_linear_algebra_instruction() = message_; + return result; +} + +uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354): + // Right now we only support certain operations. + switch (instruction->opcode()) { + case spv::Op::OpTranspose: { + // For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1 + // OpCompositeConstruct will be inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_row_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + return matrix_row_count * (2 * matrix_column_count + 1); + } + case spv::Op::OpVectorTimesScalar: + // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be + // inserted. + return 2 * + ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count(); + case spv::Op::OpMatrixTimesScalar: { + // For each matrix column, |1 + column.size| OpCompositeExtract, + // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be + // inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto matrix_type = + ir_context->get_type_mgr()->GetType(matrix_instruction->type_id()); + return 2 * matrix_type->AsMatrix()->element_count() * + (1 + matrix_type->AsMatrix() + ->element_type() + ->AsVector() + ->element_count()); + } + case spv::Op::OpVectorTimesMatrix: { + // For each vector component, 1 OpCompositeExtract instruction will be + // inserted. For each matrix column, |1 + vector_component_count| + // OpCompositeExtract, |vector_component_count| OpFMul and + // |vector_component_count - 1| OpFAdd instructions will be inserted. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t vector_component_count = + ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return vector_component_count * (3 * matrix_column_count + 1); + } + case spv::Op::OpMatrixTimesVector: { + // For each matrix column, |1 + matrix_row_count| OpCompositeExtract + // will be inserted. For each matrix row, |matrix_column_count| OpFMul and + // |matrix_column_count - 1| OpFAdd instructions will be inserted. For + // each vector component, 1 OpCompositeExtract instruction will be + // inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_row_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + return 3 * matrix_column_count * matrix_row_count + + 2 * matrix_column_count - matrix_row_count; + } + case spv::Op::OpMatrixTimesMatrix: { + // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct, + // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract, + // |matrix_1_row_count * matrix_1_column_count| OpFMul, + // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions + // will be inserted. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_1_row_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return matrix_2_column_count * + (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1)); + } + case spv::Op::OpOuterProduct: { + // For each |vector_2| component, |vector_1_component_count + 1| + // OpCompositeExtract, |vector_1_component_count| OpFMul and 1 + // OpCompositeConstruct instructions will be inserted. + auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t vector_1_component_count = + ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_count(); + uint32_t vector_2_component_count = + ir_context->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector() + ->element_count(); + return 2 * vector_2_component_count * (vector_1_component_count + 1); + } + case spv::Op::OpDot: + // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul + // will be inserted. The first two OpFMul instructions will result the + // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1 + // OpFAdd will be inserted. The last OpFAdd instruction is got by changing + // the OpDot instruction. + return 4 * ir_context->get_type_mgr() + ->GetType( + ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count() - + 2; + default: + assert(false && "Unsupported linear algebra instruction."); + return 0; + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpTranspose instruction information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + auto matrix_column_component_type = + matrix_column_type->AsVector()->element_type(); + uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count(); + auto resulting_matrix_column_type = + ir_context->get_type_mgr() + ->GetType(linear_algebra_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector column_component_ids(matrix_column_count); + for (uint32_t j = 0; j < matrix_column_count; j++) { + // Extracts the matrix column. + uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Extracts the matrix column component. + column_component_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_component_type), + column_component_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + ir_context->get_type_mgr()->GetId(resulting_matrix_column_type), + result_column_ids[i], opt::Instruction::OperandList(in_operands))); + } + + // The OpTranspose instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + for (uint32_t i = 1; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpVectorTimesScalar in operands. + auto vector = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto scalar = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector->type_id()) + ->AsVector() + ->element_count(); + std::vector float_multiplication_ids(vector_component_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < vector_component_count; i++) { + // Extracts |vector| component. + uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, scalar->type_id(), + vector_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies the |vector| component with the |scalar|. + uint32_t float_multiplication_id = message_.fresh_ids(fresh_id_index++); + float_multiplication_ids[i] = float_multiplication_id; + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, scalar->type_id(), float_multiplication_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_extract_id}}, + {SPV_OPERAND_TYPE_ID, {scalar->result_id()}}}))); + } + + // The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]}); + for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}}); + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpMatrixTimesScalar in operands. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + // Gets matrix information. + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count(); + + std::vector composite_construct_ids(matrix_column_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < matrix_column_count; i++) { + // Extracts |matrix| column. + uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector float_multiplication_ids(matrix_column_size); + + for (uint32_t j = 0; j < matrix_column_size; j++) { + // Extracts |column| component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + scalar_instruction->type_id(), column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies the |column| component with the |scalar|. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, scalar_instruction->type_id(), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {column_extract_id}}, + {SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}}))); + } + + // Constructs a new column multiplied by |scalar|. + opt::Instruction::OperandList composite_construct_in_operands; + for (uint32_t& float_multiplication_id : float_multiplication_ids) { + composite_construct_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {float_multiplication_id}}); + } + composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + ir_context->get_type_mgr()->GetId(matrix_column_type), + composite_construct_ids[i], composite_construct_in_operands)); + } + + // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]}); + for (uint32_t i = 2; i < composite_construct_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}}); + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + uint32_t fresh_id_index = 0; + std::vector vector_component_ids(vector_component_count); + for (uint32_t i = 0; i < vector_component_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + std::vector result_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + // Extracts matrix column. + uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector float_multiplication_ids(vector_component_count); + for (uint32_t j = 0; j < vector_component_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {column_extract_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count(); + + // Extracts matrix columns. + uint32_t fresh_id_index = 0; + std::vector matrix_column_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_column_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + std::vector vector_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + std::vector result_component_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector float_multiplication_ids(matrix_column_count); + for (uint32_t j = 0; j < matrix_column_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {column_extract_id}}, + {SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix 1 information. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_1_column_type = ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type(); + auto matrix_1_column_component_type = + matrix_1_column_type->AsVector()->element_type(); + uint32_t matrix_1_row_count = + matrix_1_column_type->AsVector()->element_count(); + + // Gets matrix 2 information. + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_2_column_type = ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(matrix_2_column_count); + for (uint32_t i = 0; i < matrix_2_column_count; i++) { + // Extracts matrix 2 column. + uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_2_column_type), + matrix_2_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector column_component_ids(matrix_1_row_count); + for (uint32_t j = 0; j < matrix_1_row_count; j++) { + std::vector float_multiplication_ids(matrix_1_column_count); + for (uint32_t k = 0; k < matrix_1_column_count; k++) { + // Extracts matrix 1 column. + uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + matrix_1_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Extracts matrix 1 column component. + uint32_t matrix_1_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_1_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Extracts matrix 2 column component. + uint32_t matrix_2_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_2_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Multiplies corresponding matrix 1 and matrix 2 column components. + float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_multiplication_ids[k], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}}, + {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}}))); + } + + column_component_ids[j] = float_add_ids.back(); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + result_column_ids[i], opt::Instruction::OperandList(in_operands))); + } + + // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]}); + for (uint32_t i = 2; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets vector 1 information. + auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t vector_1_component_count = + ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_count(); + auto vector_1_component_type = ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Gets vector 2 information. + auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t vector_2_component_count = + ir_context->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector() + ->element_count(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(vector_2_component_count); + for (uint32_t i = 0; i < vector_2_component_count; i++) { + // Extracts |vector_2| component. + uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + vector_2_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector column_component_ids(vector_1_component_count); + for (uint32_t j = 0; j < vector_1_component_count; j++) { + // Extracts |vector_1| component. + uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + vector_1_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies |vector_1| and |vector_2| components. + column_component_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + column_component_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2_component_id}}, + {SPV_OPERAND_TYPE_ID, {vector_1_component_id}}}))); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, + vector_1_instruction->type_id(), result_column_ids[i], in_operands)); + } + + // The OpOuterProduct instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(spv::Op::OpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]}); + for (uint32_t i = 2; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpDot in operands. + auto vector_1 = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto vector_2 = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + uint32_t vectors_component_count = ir_context->get_type_mgr() + ->GetType(vector_1->type_id()) + ->AsVector() + ->element_count(); + std::vector float_multiplication_ids(vectors_component_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < vectors_component_count; i++) { + // Extracts |vector_1| component. + uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + linear_algebra_instruction->type_id(), vector_1_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Extracts |vector_2| component. + uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, + linear_algebra_instruction->type_id(), vector_2_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies the pair of components. + float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFMul, linear_algebra_instruction->type_id(), + float_multiplication_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}}, + {SPV_OPERAND_TYPE_ID, {vector_2_extract_id}}}))); + } + + // If the vector has 2 components, then there will be 2 float multiplication + // instructions. + if (vectors_component_count == 2) { + linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd); + linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]}); + } else { + // The first OpFAdd instruction has as operands the first two OpFMul + // instructions. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + + // The remaining OpFAdd instructions has as operands an OpFMul and an OpFAdd + // instruction. + for (uint32_t i = 2; i < float_multiplication_ids.size() - 1; i++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFAdd, linear_algebra_instruction->type_id(), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[i - 2]}}}))); + } + + // The last OpFAdd instruction is got by changing some of the OpDot + // instruction attributes. + linear_algebra_instruction->SetOpcode(spv::Op::OpFAdd); + linear_algebra_instruction->SetInOperand( + 0, {float_multiplication_ids[float_multiplication_ids.size() - 1]}); + linear_algebra_instruction->SetInOperand( + 1, {float_add_ids[float_add_ids.size() - 1]}); + } +} + +std::unordered_set +TransformationReplaceLinearAlgebraInstruction::GetFreshIds() const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h new file mode 100644 index 00000000..0f0c18bf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -0,0 +1,93 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceLinearAlgebraInstruction : public Transformation { + public: + explicit TransformationReplaceLinearAlgebraInstruction( + protobufs::TransformationReplaceLinearAlgebraInstruction message); + + TransformationReplaceLinearAlgebraInstruction( + const std::vector& fresh_ids, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + // - |message_.instruction_descriptor| must be a linear algebra instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces a linear algebra instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of ids needed to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationReplaceLinearAlgebraInstruction message_; + + // Replaces an OpTranspose instruction. + void ReplaceOpTranspose(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpVectorTimesScalar instruction. + void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesScalar instruction. + void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpVectorTimesMatrix instruction. + void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesVector instruction. + void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesMatrix instruction. + void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpOuterProduct instruction. + void ReplaceOpOuterProduct(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpDot instruction. + void ReplaceOpDot(opt::IRContext* ir_context, + opt::Instruction* instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp new file mode 100644 index 00000000..36610f2f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_replace_load_store_with_copy_memory.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opcode.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kOpStoreOperandIndexTargetVariable = 0; +const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1; +const uint32_t kOpLoadOperandIndexSourceVariable = 2; +} // namespace + +TransformationReplaceLoadStoreWithCopyMemory:: + TransformationReplaceLoadStoreWithCopyMemory( + protobufs::TransformationReplaceLoadStoreWithCopyMemory message) + : message_(std::move(message)) {} + +TransformationReplaceLoadStoreWithCopyMemory:: + TransformationReplaceLoadStoreWithCopyMemory( + const protobufs::InstructionDescriptor& load_instruction_descriptor, + const protobufs::InstructionDescriptor& store_instruction_descriptor) { + *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor; + *message_.mutable_store_instruction_descriptor() = + store_instruction_descriptor; +} +bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // This transformation is only applicable to the pair of OpLoad and OpStore + // instructions. + + // The OpLoad instruction must be defined. + auto load_instruction = + FindInstruction(message_.load_instruction_descriptor(), ir_context); + if (!load_instruction || load_instruction->opcode() != spv::Op::OpLoad) { + return false; + } + + // The OpStore instruction must be defined. + auto store_instruction = + FindInstruction(message_.store_instruction_descriptor(), ir_context); + if (!store_instruction || store_instruction->opcode() != spv::Op::OpStore) { + return false; + } + + // Intermediate values of the OpLoad and the OpStore must match. + if (load_instruction->result_id() != + store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexIntermediateIdToWrite)) { + return false; + } + + // Get storage class of the variable pointed by the source operand in OpLoad. + opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef( + load_instruction->GetSingleWordOperand(2)); + spv::StorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType( + ir_context, source_id->type_id()); + + // Iterate over all instructions between |load_instruction| and + // |store_instruction|. + for (auto it = load_instruction; it != store_instruction; + it = it->NextNode()) { + //|load_instruction| and |store_instruction| are not in the same block. + if (it == nullptr) { + return false; + } + + // We need to make sure that the value pointed to by the source of the + // OpLoad hasn't changed by the time we see the matching OpStore + // instruction. + if (IsMemoryWritingOpCode(it->opcode())) { + return false; + } else if (IsMemoryBarrierOpCode(it->opcode()) && + !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) { + return false; + } + } + return true; +} + +void TransformationReplaceLoadStoreWithCopyMemory::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // OpLoad and OpStore instructions must be defined. + auto load_instruction = + FindInstruction(message_.load_instruction_descriptor(), ir_context); + assert(load_instruction && load_instruction->opcode() == spv::Op::OpLoad && + "The required OpLoad instruction must be defined."); + auto store_instruction = + FindInstruction(message_.store_instruction_descriptor(), ir_context); + assert(store_instruction && store_instruction->opcode() == spv::Op::OpStore && + "The required OpStore instruction must be defined."); + + // Intermediate values of the OpLoad and the OpStore must match. + assert(load_instruction->result_id() == + store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexIntermediateIdToWrite) && + "OpLoad and OpStore must refer to the same value."); + + // Get the ids of the source operand of the OpLoad and the target operand of + // the OpStore. + uint32_t source_variable_id = + load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable); + uint32_t target_variable_id = store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexTargetVariable); + + // Insert the OpCopyMemory instruction before the OpStore instruction. + store_instruction->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCopyMemory, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {target_variable_id}}, + {SPV_OPERAND_TYPE_ID, {source_variable_id}}}))); + + // Remove the OpStore instruction. + ir_context->KillInst(store_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode( + spv::Op op_code) { + if (spvOpcodeIsAtomicOp(op_code)) { + return op_code != spv::Op::OpAtomicLoad; + } + switch (op_code) { + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + return true; + default: + return false; + } +} + +bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode( + spv::Op op_code) { + switch (op_code) { + case spv::Op::OpMemoryBarrier: + case spv::Op::OpMemoryNamedBarrier: + return true; + default: + return false; + } +} + +bool TransformationReplaceLoadStoreWithCopyMemory:: + IsStorageClassSafeAcrossMemoryBarriers(spv::StorageClass storage_class) { + switch (storage_class) { + case spv::StorageClass::UniformConstant: + case spv::StorageClass::Input: + case spv::StorageClass::Uniform: + case spv::StorageClass::Private: + case spv::StorageClass::Function: + return true; + default: + return false; + } +} + +protobufs::Transformation +TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_load_store_with_copy_memory() = message_; + return result; +} + +std::unordered_set +TransformationReplaceLoadStoreWithCopyMemory::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h new file mode 100644 index 00000000..43c754e6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceLoadStoreWithCopyMemory : public Transformation { + public: + explicit TransformationReplaceLoadStoreWithCopyMemory( + protobufs::TransformationReplaceLoadStoreWithCopyMemory message); + + TransformationReplaceLoadStoreWithCopyMemory( + const protobufs::InstructionDescriptor& load_instruction_descriptor, + const protobufs::InstructionDescriptor& store_instruction_descriptor); + + // - |message_.load_instruction_descriptor| must identify an OpLoad + // instruction. + // - |message_.store_instruction_descriptor| must identify an OpStore + // instruction. + // - The OpStore must write the intermediate value loaded by the OpLoad. + // - The OpLoad and the OpStore must not have certain instruction in between + // (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(), + // IsStorageClassSafeAcrossMemoryBarriers()). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Takes a pair of instruction descriptors to OpLoad and OpStore that have the + // same intermediate value and replaces the OpStore with an equivalent + // OpCopyMemory. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Checks if the instruction that has an |op_code| might write to + // the source operand of the OpLoad instruction. + static bool IsMemoryWritingOpCode(spv::Op op_code); + + // Checks if the instruction that has an |op_code| is a memory barrier that + // could interfere with the source operand of the OpLoad instruction + static bool IsMemoryBarrierOpCode(spv::Op op_code); + + // Checks if the |storage_class| of the source operand of the OpLoad + // instruction implies that this variable cannot change (due to other threads) + // across memory barriers. + static bool IsStorageClassSafeAcrossMemoryBarriers( + spv::StorageClass storage_class); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceLoadStoreWithCopyMemory message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp new file mode 100644 index 00000000..eeda68df --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceOpPhiIdFromDeadPredecessor:: + TransformationReplaceOpPhiIdFromDeadPredecessor( + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message) + : message_(std::move(message)) {} + +TransformationReplaceOpPhiIdFromDeadPredecessor:: + TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, + uint32_t pred_label_id, + uint32_t replacement_id) { + message_.set_opphi_id(opphi_id); + message_.set_pred_label_id(pred_label_id); + message_.set_replacement_id(replacement_id); +} + +bool TransformationReplaceOpPhiIdFromDeadPredecessor::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |opphi_id| must be the id of an OpPhi instruction. + auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id()); + if (!opphi_def || opphi_def->opcode() != spv::Op::OpPhi) { + return false; + } + + // |pred_label_id| must be the label id of a dead block. + auto pred_block = ir_context->get_instr_block(message_.pred_label_id()); + if (!pred_block || pred_block->id() != message_.pred_label_id() || + !transformation_context.GetFactManager()->BlockIsDead(pred_block->id())) { + return false; + } + + // |pred_label_id| must be one of the predecessors of the block containing the + // OpPhi instruction. + bool found = false; + for (auto pred : + ir_context->cfg()->preds(ir_context->get_instr_block(opphi_def)->id())) { + if (pred == message_.pred_label_id()) { + found = true; + break; + } + } + + if (!found) { + return false; + } + + // |replacement_id| must have the same type id as the OpPhi instruction. + auto replacement_def = + ir_context->get_def_use_mgr()->GetDef(message_.replacement_id()); + + if (!replacement_def || replacement_def->type_id() != opphi_def->type_id()) { + return false; + } + + // The replacement id must be available at the end of the predecessor. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, pred_block->terminator(), replacement_def->result_id()); +} + +void TransformationReplaceOpPhiIdFromDeadPredecessor::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Get the OpPhi instruction. + auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id()); + + // Find the index corresponding to the operand being replaced and replace it, + // by looping through the odd-indexed input operands and finding + // |pred_label_id|. The index that we are interested in is the one before + // that. + for (uint32_t i = 1; i < opphi_def->NumInOperands(); i += 2) { + if (opphi_def->GetSingleWordInOperand(i) == message_.pred_label_id()) { + // The operand to be replaced is at index i-1. + opphi_def->SetInOperand(i - 1, {message_.replacement_id()}); + } + } + + // Invalidate the analyses because we have altered the usages of ids. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceOpPhiIdFromDeadPredecessor::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_opphi_id_from_dead_predecessor() = message_; + return result; +} + +std::unordered_set +TransformationReplaceOpPhiIdFromDeadPredecessor::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h new file mode 100644 index 00000000..87ce8ad1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceOpPhiIdFromDeadPredecessor : public Transformation { + public: + explicit TransformationReplaceOpPhiIdFromDeadPredecessor( + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message); + + TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, + uint32_t pred_label_id, + uint32_t replacement_id); + + // - |message_.opphi_id| is the id of an OpPhi instruction. + // - |message_.pred_label_id| is the label id of one of the predecessors of + // the block containing the OpPhi instruction. + // - The predecessor has been recorded as dead. + // - |message_.replacement_id| is the id of an instruction with the same type + // as the OpPhi instruction, available at the end of the predecessor. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the id corresponding to predecessor |message_.pred_label_id|, in + // the OpPhi instruction |message_.opphi_id|, with |message_.replacement_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp new file mode 100644 index 00000000..b2010a94 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +TransformationReplaceOpSelectWithConditionalBranch:: + TransformationReplaceOpSelectWithConditionalBranch( + protobufs::TransformationReplaceOpSelectWithConditionalBranch message) + : message_(std::move(message)) {} + +TransformationReplaceOpSelectWithConditionalBranch:: + TransformationReplaceOpSelectWithConditionalBranch( + uint32_t select_id, uint32_t true_block_id, uint32_t false_block_id) { + message_.set_select_id(select_id); + message_.set_true_block_id(true_block_id); + message_.set_false_block_id(false_block_id); +} + +bool TransformationReplaceOpSelectWithConditionalBranch::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& /* unused */) const { + assert((message_.true_block_id() || message_.false_block_id()) && + "At least one of the ids must be non-zero."); + + // Check that the non-zero ids are fresh. + std::set used_ids; + for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) { + if (id && !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &used_ids)) { + return false; + } + } + + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.select_id()); + + // The instruction must exist and it must be an OpSelect instruction. + if (!instruction || instruction->opcode() != spv::Op::OpSelect) { + return false; + } + + // Check that the condition is a scalar boolean. + auto condition = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + assert(condition && "The condition should always exist in a valid module."); + + auto condition_type = + ir_context->get_type_mgr()->GetType(condition->type_id()); + if (!condition_type->AsBool()) { + return false; + } + + auto block = ir_context->get_instr_block(instruction); + assert(block && "The block containing the instruction must be found"); + + // The instruction must be the first in its block. + if (instruction->unique_id() != block->begin()->unique_id()) { + return false; + } + + // The block must not be a merge block. + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return false; + } + + // The block must have exactly one predecessor. + auto predecessors = ir_context->cfg()->preds(block->id()); + if (predecessors.size() != 1) { + return false; + } + + uint32_t pred_id = predecessors[0]; + auto predecessor = ir_context->get_instr_block(pred_id); + + // The predecessor must not be the header of a construct and it must end with + // OpBranch. + if (predecessor->GetMergeInst() != nullptr || + predecessor->terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + + return true; +} + +void TransformationReplaceOpSelectWithConditionalBranch::Apply( + opt::IRContext* ir_context, TransformationContext* /* unused */) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.select_id()); + + auto block = ir_context->get_instr_block(instruction); + + auto predecessor = + ir_context->get_instr_block(ir_context->cfg()->preds(block->id())[0]); + + // Create a new block for each non-zero id in {|message_.true_branch_id|, + // |message_.false_branch_id|}. Make each newly-created block branch + // unconditionally to the instruction block. + for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) { + if (id) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + + // Create the new block. + auto new_block = MakeUnique( + MakeUnique(ir_context, spv::Op::OpLabel, 0, id, + opt::Instruction::OperandList{})); + + // Add an unconditional branch from the new block to the instruction + // block. + new_block->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}})); + + // Insert the new block right after the predecessor of the instruction + // block. + block->GetParent()->InsertBasicBlockBefore(std::move(new_block), block); + } + } + + // Delete the OpBranch instruction from the predecessor. + ir_context->KillInst(predecessor->terminator()); + + // Add an OpSelectionMerge instruction to the predecessor block, where the + // merge block is the instruction block. + predecessor->AddInstruction(MakeUnique( + ir_context, spv::Op::OpSelectionMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {block->id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}})); + + // |if_block| will be the true block, if it has been created, the instruction + // block otherwise. + uint32_t if_block = + message_.true_block_id() ? message_.true_block_id() : block->id(); + + // |else_block| will be the false block, if it has been created, the + // instruction block otherwise. + uint32_t else_block = + message_.false_block_id() ? message_.false_block_id() : block->id(); + + assert(if_block != else_block && + "|if_block| and |else_block| should always be different, if the " + "transformation is applicable."); + + // Add a conditional branching instruction to the predecessor, branching to + // |if_block| if the condition is true and to |if_false| otherwise. + predecessor->AddInstruction(MakeUnique( + ir_context, spv::Op::OpBranchConditional, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {instruction->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {if_block}}, + {SPV_OPERAND_TYPE_ID, {else_block}}})); + + // |if_pred| will be the true block, if it has been created, the existing + // predecessor otherwise. + uint32_t if_pred = + message_.true_block_id() ? message_.true_block_id() : predecessor->id(); + + // |else_pred| will be the false block, if it has been created, the existing + // predecessor otherwise. + uint32_t else_pred = + message_.false_block_id() ? message_.false_block_id() : predecessor->id(); + + // Replace the OpSelect instruction in the merge block with an OpPhi. + // This: OpSelect %type %cond %if %else + // will become: OpPhi %type %if %if_pred %else %else_pred + instruction->SetOpcode(spv::Op::OpPhi); + std::vector operands; + + operands.emplace_back(instruction->GetInOperand(1)); + operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {if_pred}}); + + operands.emplace_back(instruction->GetInOperand(2)); + operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {else_pred}}); + + instruction->SetInOperands(std::move(operands)); + + // Invalidate all analyses, since the structure of the module was changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceOpSelectWithConditionalBranch::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_opselect_with_conditional_branch() = message_; + return result; +} + +std::unordered_set +TransformationReplaceOpSelectWithConditionalBranch::GetFreshIds() const { + return {message_.true_block_id(), message_.false_block_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h new file mode 100644 index 00000000..ec926c65 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceOpSelectWithConditionalBranch + : public Transformation { + public: + explicit TransformationReplaceOpSelectWithConditionalBranch( + protobufs::TransformationReplaceOpSelectWithConditionalBranch message); + + TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id, + uint32_t true_block_id, + uint32_t false_block_id); + + // - |message_.select_id| is the result id of an OpSelect instruction. + // - The condition of the OpSelect must be a scalar boolean. + // - The OpSelect instruction is the first instruction in its block. + // - The block containing the instruction is not a merge block, and it has a + // single predecessor, which is not a header and whose last instruction is + // OpBranch. + // - Each of |message_.true_block_id| and |message_.false_block_id| is either + // 0 or a valid fresh id, and at most one of them is 0. They must be + // distinct. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpSelect instruction with id |message_.select_id| with a + // conditional branch and an OpPhi instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceOpSelectWithConditionalBranch message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp new file mode 100644 index 00000000..e80dfe90 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + protobufs::TransformationReplaceParameterWithGlobal message) + : message_(std::move(message)) {} + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + uint32_t function_type_fresh_id, uint32_t parameter_id, + uint32_t global_variable_fresh_id) { + message_.set_function_type_fresh_id(function_type_fresh_id); + message_.set_parameter_id(parameter_id); + message_.set_global_variable_fresh_id(global_variable_fresh_id); +} + +bool TransformationReplaceParameterWithGlobal::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |parameter_id| is valid. + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + if (!param_inst || param_inst->opcode() != spv::Op::OpFunctionParameter) { + return false; + } + + // Check that function exists and is not an entry point. + const auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // We already know that the function has at least one parameter - + // |parameter_id|. + + // Check that replaced parameter has valid type. + if (!IsParameterTypeSupported(ir_context, param_inst->type_id())) { + return false; + } + + // Check that initializer for the global variable exists in the module. + if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context, + param_inst->type_id(), false) == 0) { + return false; + } + + // Check that pointer type for the global variable exists in the module. + if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + spv::StorageClass::Private)) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, + message_.global_variable_fresh_id()) && + message_.function_type_fresh_id() != + message_.global_variable_fresh_id(); +} + +void TransformationReplaceParameterWithGlobal::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + assert(param_inst && "Parameter must exist"); + + // Create global variable to store parameter's value. + fuzzerutil::AddGlobalVariable( + ir_context, message_.global_variable_fresh_id(), + fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + spv::StorageClass::Private), + spv::StorageClass::Private, + fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context, + param_inst->type_id(), false)); + + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); + assert(function && "Function must exist"); + + // Insert an OpLoad instruction right after OpVariable instructions. + auto it = function->begin()->begin(); + while (it != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, it)) { + ++it; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad, it) && + "Can't insert OpLoad or OpCopyMemory into the first basic block of " + "the function"); + + it.InsertBefore(MakeUnique( + ir_context, spv::Op::OpLoad, param_inst->type_id(), + param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}})); + + // Calculate the index of the replaced parameter (we need to know this to + // remove operands from the OpFunctionCall). + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + auto parameter_index = static_cast(params.size()); + for (uint32_t i = 0, n = static_cast(params.size()); i < n; ++i) { + if (params[i]->result_id() == message_.parameter_id()) { + parameter_index = i; + break; + } + } + + assert(parameter_index != params.size() && + "Parameter must exist in the function"); + + // Update all relevant OpFunctionCall instructions. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + assert( + fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore, inst) && + "Can't insert OpStore right before the function call"); + + // Insert an OpStore before the OpFunctionCall. +1 since the first + // operand of OpFunctionCall is an id of the function. + inst->InsertBefore(MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}, + {SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand(parameter_index + 1)}}})); + + // +1 since the first operand of OpFunctionCall is an id of the + // function. + inst->RemoveInOperand(parameter_index + 1); + } + + // Remove the parameter from the function. + fuzzerutil::RemoveParameter(ir_context, message_.parameter_id()); + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // +1 and -1 since the first operand is the return type id. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (i - 1 != parameter_index) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + } + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Mark the pointee of the global variable storing the parameter's value as + // irrelevant if replaced parameter is irrelevant. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.parameter_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.global_variable_fresh_id()); + } +} + +protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_parameter_with_global() = message_; + return result; +} + +bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t param_type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + return fuzzerutil::CanCreateConstant(ir_context, param_type_id); +} + +std::unordered_set +TransformationReplaceParameterWithGlobal::GetFreshIds() const { + return {message_.function_type_fresh_id(), + message_.global_variable_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h new file mode 100644 index 00000000..38a9c17f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceParameterWithGlobal : public Transformation { + public: + explicit TransformationReplaceParameterWithGlobal( + protobufs::TransformationReplaceParameterWithGlobal message); + + TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id, + uint32_t parameter_id, + uint32_t global_variable_fresh_id); + + // - |function_type_fresh_id| is a fresh id. + // - |parameter_id| is the result id of the parameter to replace. + // - |global_variable_fresh_id| is a fresh id. + // - |function_type_fresh_id| is not equal to |global_variable_fresh_id|. + // - the function that contains |parameter_id| may not be an entry-point + // function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Removes parameter with result id |parameter_id| from its function + // - Adds a global variable to store the value for the parameter + // - Add an OpStore instruction before each function call to + // store parameter's value into the variable + // - Adds OpLoad at the beginning of the function to load the + // value from the variable into the old parameter's id + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the type of the parameter is supported by this + // transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t param_type_id); + + private: + protobufs::TransformationReplaceParameterWithGlobal message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp new file mode 100644 index 00000000..c8af14ae --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_params_with_struct.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + protobufs::TransformationReplaceParamsWithStruct message) + : message_(std::move(message)) {} + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, uint32_t fresh_function_type_id, + uint32_t fresh_parameter_id, + const std::map& caller_id_to_fresh_composite_id) { + message_.set_fresh_function_type_id(fresh_function_type_id); + message_.set_fresh_parameter_id(fresh_parameter_id); + + for (auto id : parameter_id) { + message_.add_parameter_id(id); + } + + *message_.mutable_caller_id_to_fresh_composite_id() = + fuzzerutil::MapToRepeatedUInt32Pair(caller_id_to_fresh_composite_id); +} + +bool TransformationReplaceParamsWithStruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + std::vector parameter_id(message_.parameter_id().begin(), + message_.parameter_id().end()); + + // Check that |parameter_id| is neither empty nor it has duplicates. + if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) { + return false; + } + + // All ids must correspond to valid parameters of the same function. + // The function can't be an entry-point function. + + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) { + return false; + } + + const auto* function = + fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // Compute all ids of the function's parameters. + std::unordered_set all_parameter_ids; + for (const auto* param : + fuzzerutil::GetParameters(ir_context, function->result_id())) { + all_parameter_ids.insert(param->result_id()); + } + + // Check that all elements in |parameter_id| are valid. + for (auto id : parameter_id) { + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(id)) { + return false; + } + + // Check that |id| is a result id of one of the |function|'s parameters. + if (!all_parameter_ids.count(id)) { + return false; + } + + // Check that the parameter with result id |id| has supported type. + if (!IsParameterTypeSupported(ir_context, + fuzzerutil::GetTypeId(ir_context, id))) { + return false; + } + } + + // We already know that the function has at least |parameter_id.size()| + // parameters. + + // Check that a relevant OpTypeStruct exists in the module. + if (!MaybeGetRequiredStructType(ir_context)) { + return false; + } + + const auto caller_id_to_fresh_composite_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.caller_id_to_fresh_composite_id()); + + // Check that |callee_id_to_fresh_composite_id| is valid. + for (const auto* inst : + fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Check that the callee is present in the map. It's ok if the map contains + // more ids that there are callees (those ids will not be used). + if (!caller_id_to_fresh_composite_id.count(inst->result_id())) { + return false; + } + } + + // Check that all fresh ids are unique and fresh. + std::vector fresh_ids = {message_.fresh_function_type_id(), + message_.fresh_parameter_id()}; + + for (const auto& entry : caller_id_to_fresh_composite_id) { + fresh_ids.push_back(entry.second); + } + + return !fuzzerutil::HasDuplicates(fresh_ids) && + std::all_of(fresh_ids.begin(), fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationReplaceParamsWithStruct::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id(0)); + assert(function && + "All parameters' ids should've been checked in the IsApplicable"); + + // Get a type id of the OpTypeStruct used as a type id of the new parameter. + auto struct_type_id = MaybeGetRequiredStructType(ir_context); + assert(struct_type_id && + "IsApplicable should've guaranteed that this value isn't equal to 0"); + + // Add new parameter to the function. + function->AddParameter(MakeUnique( + ir_context, spv::Op::OpFunctionParameter, struct_type_id, + message_.fresh_parameter_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id()); + + // Compute indices of replaced parameters. This will be used to adjust + // OpFunctionCall instructions and create OpCompositeConstruct instructions at + // every call site. + const auto indices_of_replaced_params = + ComputeIndicesOfReplacedParameters(ir_context); + + const auto caller_id_to_fresh_composite_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.caller_id_to_fresh_composite_id()); + + // Update all function calls. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Create a list of operands for the OpCompositeConstruct instruction. + opt::Instruction::OperandList composite_components; + for (auto index : indices_of_replaced_params) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + composite_components.emplace_back( + std::move(inst->GetInOperand(index + 1))); + } + + // Remove arguments from the function call. We do it in a separate loop + // and in decreasing order to make sure we have removed correct operands. + for (auto index : std::set>( + indices_of_replaced_params.begin(), + indices_of_replaced_params.end())) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + inst->RemoveInOperand(index + 1); + } + + // Insert OpCompositeConstruct before the function call. + auto fresh_composite_id = + caller_id_to_fresh_composite_id.at(inst->result_id()); + inst->InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeConstruct, struct_type_id, + fresh_composite_id, std::move(composite_components))); + + // Add a new operand to the OpFunctionCall instruction. + inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}}); + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id); + } + + // Insert OpCompositeExtract instructions into the entry point block of the + // function and remove replaced parameters. + for (int i = 0; i < message_.parameter_id_size(); ++i) { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i)); + assert(param_inst && "Parameter id is invalid"); + + // Skip all OpVariable instructions. + auto iter = function->begin()->begin(); + while (iter != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeExtract, iter)) { + ++iter; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpCompositeExtract, iter) && + "Can't extract parameter's value from the structure"); + + // Insert OpCompositeExtract instructions to unpack parameters' values from + // the struct type. + iter.InsertBefore(MakeUnique( + ir_context, spv::Op::OpCompositeExtract, param_inst->type_id(), + param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast(i)}}})); + + fuzzerutil::RemoveParameter(ir_context, param_inst->result_id()); + } + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // +1 since the first in operand to OpTypeFunction is the result type id + // of the function. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (std::find(indices_of_replaced_params.begin(), + indices_of_replaced_params.end(), + i - 1) == indices_of_replaced_params.end()) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + } + + parameter_type_ids.push_back(struct_type_id); + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.fresh_function_type_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_params_with_struct() = message_; + return result; +} + +bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t param_type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Consider adding support for more types of parameters. + return fuzzerutil::CanCreateConstant(ir_context, param_type_id); +} + +uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType( + opt::IRContext* ir_context) const { + std::vector component_type_ids; + for (auto id : message_.parameter_id()) { + component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id)); + } + + return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids); +} + +std::vector +TransformationReplaceParamsWithStruct::ComputeIndicesOfReplacedParameters( + opt::IRContext* ir_context) const { + assert(!message_.parameter_id().empty() && + "There must be at least one parameter to replace"); + + const auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id(0)); + assert(function && "|parameter_id|s are invalid"); + + std::vector result; + + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + for (auto id : message_.parameter_id()) { + auto it = std::find_if(params.begin(), params.end(), + [id](const opt::Instruction* param) { + return param->result_id() == id; + }); + assert(it != params.end() && "Parameter's id is invalid"); + result.push_back(static_cast(it - params.begin())); + } + + return result; +} + +std::unordered_set +TransformationReplaceParamsWithStruct::GetFreshIds() const { + std::unordered_set result = {message_.fresh_function_type_id(), + message_.fresh_parameter_id()}; + for (auto& pair : message_.caller_id_to_fresh_composite_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h new file mode 100644 index 00000000..705f3405 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceParamsWithStruct : public Transformation { + public: + explicit TransformationReplaceParamsWithStruct( + protobufs::TransformationReplaceParamsWithStruct message); + + TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, + uint32_t fresh_function_type_id, uint32_t fresh_parameter_id, + const std::map& caller_id_to_fresh_composite_id); + + // - Each element of |parameter_id| is a valid result id of some + // OpFunctionParameter instruction. All parameter ids must correspond to + // parameters of the same function. That function may not be an entry-point + // function. + // - Types of all parameters must be supported by this transformation (see + // IsParameterTypeSupported method). + // - |parameter_id| may not be empty or contain duplicates. + // - There must exist an OpTypeStruct instruction containing types of all + // replaced parameters. Type of the i'th component of the struct is equal + // to the type of the instruction with result id |parameter_id[i]|. + // - |caller_id_to_fresh_composite_id| should contain a key for at least every + // result id of an OpFunctionCall instruction that calls the function. + // - |fresh_function_type_id|, |fresh_parameter_id|, + // |caller_id_to_fresh_composite_id| are all fresh and unique ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new function parameter with result id |fresh_parameter_id|. + // Parameter's type is OpTypeStruct with each components type equal to the + // type of the replaced parameter. + // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted + // before each OpFunctionCall instruction. + // - OpCompositeExtract with result id equal to the result id of the replaced + // parameter is created in the function. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if parameter's type is supported by this transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t param_type_id); + + private: + // Returns a result id of the OpTypeStruct instruction required by this + // transformation (see docs on the IsApplicable method to learn more). + uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const; + + // Returns a vector of indices of parameters to replace. Concretely, i'th + // element is the index of the parameter with result id |parameter_id[i]| in + // its function. + std::vector ComputeIndicesOfReplacedParameters( + opt::IRContext* ir_context) const; + + protobufs::TransformationReplaceParamsWithStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.cpp new file mode 100644 index 00000000..ef481719 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetFunctionControl::TransformationSetFunctionControl( + protobufs::TransformationSetFunctionControl message) + : message_(std::move(message)) {} + +TransformationSetFunctionControl::TransformationSetFunctionControl( + uint32_t function_id, uint32_t function_control) { + message_.set_function_id(function_id); + message_.set_function_control(function_control); +} + +bool TransformationSetFunctionControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(ir_context); + if (!function_def_instruction) { + // The given function id does not correspond to any function. + return false; + } + uint32_t existing_function_control_mask = + function_def_instruction->GetSingleWordInOperand(0); + + // Check (via an assertion) that function control mask doesn't have any bad + // bits set. + uint32_t acceptable_function_control_bits = uint32_t( + spv::FunctionControlMask::Inline | spv::FunctionControlMask::DontInline | + spv::FunctionControlMask::Pure | spv::FunctionControlMask::Const); + // The following is to keep release-mode compilers happy as this variable is + // only used in an assertion. + (void)(acceptable_function_control_bits); + assert(!(message_.function_control() & ~acceptable_function_control_bits) && + "Nonsensical loop control bits were found."); + + // Check (via an assertion) that function control mask does not have both + // Inline and DontInline bits set. + assert(!((message_.function_control() & + (uint32_t)spv::FunctionControlMask::Inline) && + (message_.function_control() & + (uint32_t)spv::FunctionControlMask::DontInline)) && + "It is not OK to set both the 'Inline' and 'DontInline' bits of a " + "function control mask"); + + // Check that Const and Pure are only present if they were present on the + // original function + for (auto mask_bit : + {spv::FunctionControlMask::Pure, spv::FunctionControlMask::Const}) { + if ((message_.function_control() & uint32_t(mask_bit)) && + !(existing_function_control_mask & uint32_t(mask_bit))) { + return false; + } + } + + return true; +} + +void TransformationSetFunctionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(ir_context); + function_def_instruction->SetInOperand(0, {message_.function_control()}); +} + +protobufs::Transformation TransformationSetFunctionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_function_control() = message_; + return result; +} + +opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction( + opt::IRContext* ir_context) const { + // Look through all functions for a function whose defining instruction's + // result id matches |message_.function_id|, returning the defining + // instruction if found. + for (auto& function : *ir_context->module()) { + if (function.DefInst().result_id() == message_.function_id()) { + return &function.DefInst(); + } + } + // A nullptr result indicates that no match was found. + return nullptr; +} + +std::unordered_set TransformationSetFunctionControl::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.h new file mode 100644 index 00000000..2e16e1ca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_function_control.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetFunctionControl : public Transformation { + public: + explicit TransformationSetFunctionControl( + protobufs::TransformationSetFunctionControl message); + + TransformationSetFunctionControl(uint32_t function_id, + uint32_t function_control); + + // - |message_.function_id| must be the result id of an OpFunction + // instruction. + // - |message_.function_control| must be a function control mask that sets + // at most one of 'Inline' or 'DontInline', and that may not contain 'Pure' + // (respectively 'Const') unless the existing function control mask contains + // 'Pure' (respectively 'Const'). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The function control operand of instruction |message_.function_id| is + // over-written with |message_.function_control|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + opt::Instruction* FindFunctionDefInstruction( + opt::IRContext* ir_context) const; + + protobufs::TransformationSetFunctionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.cpp new file mode 100644 index 00000000..dc9ee7cf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.cpp @@ -0,0 +1,235 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetLoopControl::TransformationSetLoopControl( + protobufs::TransformationSetLoopControl message) + : message_(std::move(message)) {} + +TransformationSetLoopControl::TransformationSetLoopControl( + uint32_t block_id, uint32_t loop_control, uint32_t peel_count, + uint32_t partial_count) { + message_.set_block_id(block_id); + message_.set_loop_control(loop_control); + message_.set_peel_count(peel_count); + message_.set_partial_count(partial_count); +} + +bool TransformationSetLoopControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.block_id| must identify a block that ends with OpLoopMerge. + auto block = ir_context->get_instr_block(message_.block_id()); + if (!block) { + return false; + } + auto merge_inst = block->GetMergeInst(); + if (!merge_inst || merge_inst->opcode() != spv::Op::OpLoopMerge) { + return false; + } + + // We assert that the transformation does not try to set any meaningless bits + // of the loop control mask. + uint32_t all_loop_control_mask_bits_set = uint32_t( + spv::LoopControlMask::Unroll | spv::LoopControlMask::DontUnroll | + spv::LoopControlMask::DependencyInfinite | + spv::LoopControlMask::DependencyLength | + spv::LoopControlMask::MinIterations | + spv::LoopControlMask::MaxIterations | + spv::LoopControlMask::IterationMultiple | + spv::LoopControlMask::PeelCount | spv::LoopControlMask::PartialCount); + + // The variable is only used in an assertion; the following keeps release-mode + // compilers happy. + (void)(all_loop_control_mask_bits_set); + + // No additional bits should be set. + assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set)); + + // Grab the loop control mask currently associated with the OpLoopMerge + // instruction. + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // Check that there is no attempt to set one of the loop controls that + // requires guarantees to hold. + for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyInfinite, + spv::LoopControlMask::DependencyLength, + spv::LoopControlMask::MinIterations, + spv::LoopControlMask::MaxIterations, + spv::LoopControlMask::IterationMultiple}) { + // We have a problem if this loop control bit was not set in the original + // loop control mask but is set by the transformation. + if (LoopControlBitIsAddedByTransformation(mask, + existing_loop_control_mask)) { + return false; + } + } + + // Check that PeelCount and PartialCount are supported if used. + if ((message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) && + !PeelCountIsSupported(ir_context)) { + return false; + } + + if ((message_.loop_control() & + uint32_t(spv::LoopControlMask::PartialCount)) && + !PartialCountIsSupported(ir_context)) { + return false; + } + + if (message_.peel_count() > 0 && + !(message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount))) { + // Peel count provided, but peel count mask bit not set. + return false; + } + + if (message_.partial_count() > 0 && + !(message_.loop_control() & + uint32_t(spv::LoopControlMask::PartialCount))) { + // Partial count provided, but partial count mask bit not set. + return false; + } + + // We must not set both 'don't unroll' and one of 'peel count' or 'partial + // count'. + return !( + (message_.loop_control() & uint32_t(spv::LoopControlMask::DontUnroll)) && + (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount | + spv::LoopControlMask::PartialCount))); +} + +void TransformationSetLoopControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Grab the loop merge instruction and its associated loop control mask. + auto merge_inst = + ir_context->get_instr_block(message_.block_id())->GetMergeInst(); + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // We are going to replace the OpLoopMerge's operands with this list. + opt::Instruction::OperandList new_operands; + // We add the existing merge block and continue target ids. + new_operands.push_back(merge_inst->GetInOperand(0)); + new_operands.push_back(merge_inst->GetInOperand(1)); + // We use the loop control mask from the transformation. + new_operands.push_back( + {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}}); + + // It remains to determine what literals to provide, in association with + // the new loop control mask. + // + // For the loop controls that require guarantees to hold about the number + // of loop iterations, we need to keep, from the original OpLoopMerge, any + // literals associated with loop control bits that are still set. + + uint32_t literal_index = 0; // Indexes into the literals from the original + // instruction. + for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyLength, + spv::LoopControlMask::MinIterations, + spv::LoopControlMask::MaxIterations, + spv::LoopControlMask::IterationMultiple}) { + // Check whether the bit was set in the original loop control mask. + if (existing_loop_control_mask & uint32_t(mask)) { + // Check whether the bit is set in the new loop control mask. + if (message_.loop_control() & uint32_t(mask)) { + // Add the associated literal to our sequence of replacement operands. + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {merge_inst->GetSingleWordInOperand( + kLoopControlFirstLiteralInOperandIndex + literal_index)}}); + } + // Increment our index into the original loop control mask's literals, + // whether or not the bit was set in the new mask. + literal_index++; + } + } + + // If PeelCount is set in the new mask, |message_.peel_count| provides the + // associated peel count. + if (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}}); + } + + // Similar, but for PartialCount. + if (message_.loop_control() & uint32_t(spv::LoopControlMask::PartialCount)) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}}); + } + + // Replace the input operands of the OpLoopMerge with the new operands we have + // accumulated. + merge_inst->SetInOperands(std::move(new_operands)); +} + +protobufs::Transformation TransformationSetLoopControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_loop_control() = message_; + return result; +} + +bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation( + spv::LoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const { + return !(uint32_t(loop_control_single_bit_mask) & + existing_loop_control_mask) && + (uint32_t(loop_control_single_bit_mask) & message_.loop_control()); +} + +bool TransformationSetLoopControl::PartialCountIsSupported( + opt::IRContext* ir_context) { + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + return false; + default: + return true; + } +} + +bool TransformationSetLoopControl::PeelCountIsSupported( + opt::IRContext* ir_context) { + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + return false; + default: + return true; + } +} + +std::unordered_set TransformationSetLoopControl::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.h new file mode 100644 index 00000000..d57447be --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_loop_control.h @@ -0,0 +1,83 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetLoopControl : public Transformation { + public: + const static uint32_t kLoopControlMaskInOperandIndex = 2; + const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3; + + explicit TransformationSetLoopControl( + protobufs::TransformationSetLoopControl message); + + TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control, + uint32_t peel_count, uint32_t partial_count); + + // - |message_.block_id| must be a block containing an OpLoopMerge + // instruction. + // - |message_.loop_control| must be a legal loop control mask that + // only uses controls available in the SPIR-V version associated with + // |ir_context|, and must not add loop controls that are only valid in the + // presence of guarantees about what the loop does (e.g. MinIterations). + // - |message_.peel_count| (respectively |message_.partial_count|) must be + // zero PeelCount (respectively PartialCount) is set in + // |message_.loop_control|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - The loop control operand of the OpLoopMergeInstruction in + // |message_.block_id| is overwritten with |message_.loop_control|. + // - The literals associated with the loop control are updated to reflect any + // controls with associated literals that have been removed (e.g. + // MinIterations), and any that have been added (PeelCount and/or + // PartialCount). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Does the version of SPIR-V being used support the PartialCount loop + // control? + static bool PartialCountIsSupported(opt::IRContext* ir_context); + + // Does the version of SPIR-V being used support the PeelCount loop control? + static bool PeelCountIsSupported(opt::IRContext* ir_context); + + private: + // Returns true if and only if |loop_single_bit_mask| is *not* set in + // |existing_loop_control| but *is* set in |message_.loop_control|. + bool LoopControlBitIsAddedByTransformation( + spv::LoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const; + + protobufs::TransformationSetLoopControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp new file mode 100644 index 00000000..16d591a6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_memory_operands_mask.h" + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +const uint32_t kOpLoadMemoryOperandsMaskIndex = 1; +const uint32_t kOpStoreMemoryOperandsMaskIndex = 2; +const uint32_t kOpCopyMemoryFirstMemoryOperandsMaskIndex = 2; +const uint32_t kOpCopyMemorySizedFirstMemoryOperandsMaskIndex = 3; + +} // namespace + +TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( + protobufs::TransformationSetMemoryOperandsMask message) + : message_(std::move(message)) {} + +TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( + const protobufs::InstructionDescriptor& memory_access_instruction, + uint32_t memory_operands_mask, uint32_t memory_operands_mask_index) { + *message_.mutable_memory_access_instruction() = memory_access_instruction; + message_.set_memory_operands_mask(memory_operands_mask); + message_.set_memory_operands_mask_index(memory_operands_mask_index); +} + +bool TransformationSetMemoryOperandsMask::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (message_.memory_operands_mask_index() != 0) { + // The following conditions should never be violated, even if + // transformations end up being replayed in a different way to the manner in + // which they were applied during fuzzing, hence why these are assertions + // rather than applicability checks. + assert(message_.memory_operands_mask_index() == 1); + assert( + spv::Op( + message_.memory_access_instruction().target_instruction_opcode()) == + spv::Op::OpCopyMemory || + spv::Op( + message_.memory_access_instruction().target_instruction_opcode()) == + spv::Op::OpCopyMemorySized); + assert(MultipleMemoryOperandMasksAreSupported(ir_context) && + "Multiple memory operand masks are not supported for this SPIR-V " + "version."); + } + + auto instruction = + FindInstruction(message_.memory_access_instruction(), ir_context); + if (!instruction) { + return false; + } + if (!IsMemoryAccess(*instruction)) { + return false; + } + + auto original_mask_in_operand_index = GetInOperandIndexForMask( + *instruction, message_.memory_operands_mask_index()); + assert(original_mask_in_operand_index != 0 && + "The given mask index is not valid."); + uint32_t original_mask = + original_mask_in_operand_index < instruction->NumInOperands() + ? instruction->GetSingleWordInOperand(original_mask_in_operand_index) + : static_cast(spv::MemoryAccessMask::MaskNone); + uint32_t new_mask = message_.memory_operands_mask(); + + // Volatile must not be removed + if ((original_mask & uint32_t(spv::MemoryAccessMask::Volatile)) && + !(new_mask & uint32_t(spv::MemoryAccessMask::Volatile))) { + return false; + } + + // Nontemporal can be added or removed, and no other flag is allowed to + // change. We do this by checking that the masks are equal once we set + // their Volatile and Nontemporal flags to the same value (this works + // because valid manipulation of Volatile is checked above, and the manner + // in which Nontemporal is manipulated does not matter). + return (original_mask | uint32_t(spv::MemoryAccessMask::Volatile) | + uint32_t(spv::MemoryAccessMask::Nontemporal)) == + (new_mask | uint32_t(spv::MemoryAccessMask::Volatile) | + uint32_t(spv::MemoryAccessMask::Nontemporal)); +} + +void TransformationSetMemoryOperandsMask::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.memory_access_instruction(), ir_context); + auto original_mask_in_operand_index = GetInOperandIndexForMask( + *instruction, message_.memory_operands_mask_index()); + // Either add a new operand, if no mask operand was already present, or + // replace an existing mask operand. + if (original_mask_in_operand_index >= instruction->NumInOperands()) { + // Add first memory operand if it's missing. + if (message_.memory_operands_mask_index() == 1 && + GetInOperandIndexForMask(*instruction, 0) >= + instruction->NumInOperands()) { + instruction->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS, + {uint32_t(spv::MemoryAccessMask::MaskNone)}}); + } + + instruction->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}}); + + } else { + instruction->SetInOperand(original_mask_in_operand_index, + {message_.memory_operands_mask()}); + } +} + +protobufs::Transformation TransformationSetMemoryOperandsMask::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_set_memory_operands_mask() = message_; + return result; +} + +bool TransformationSetMemoryOperandsMask::IsMemoryAccess( + const opt::Instruction& instruction) { + switch (instruction.opcode()) { + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + return true; + default: + return false; + } +} + +uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( + const opt::Instruction& instruction, uint32_t mask_index) { + // Get the input operand index associated with the first memory operands mask + // for the instruction. + uint32_t first_mask_in_operand_index = 0; + switch (instruction.opcode()) { + case spv::Op::OpLoad: + first_mask_in_operand_index = kOpLoadMemoryOperandsMaskIndex; + break; + case spv::Op::OpStore: + first_mask_in_operand_index = kOpStoreMemoryOperandsMaskIndex; + break; + case spv::Op::OpCopyMemory: + first_mask_in_operand_index = kOpCopyMemoryFirstMemoryOperandsMaskIndex; + break; + case spv::Op::OpCopyMemorySized: + first_mask_in_operand_index = + kOpCopyMemorySizedFirstMemoryOperandsMaskIndex; + break; + default: + assert(false && "Unknown memory instruction."); + break; + } + // If we are looking for the input operand index of the first mask, return it. + // This will also return a correct value if the operand is missing. + if (mask_index == 0) { + return first_mask_in_operand_index; + } + assert(mask_index == 1 && "Memory operands mask index must be 0 or 1."); + + // Memory mask operands are optional. Thus, if the second operand exists, + // its index will be >= |first_mask_in_operand_index + 1|. We can reason as + // follows to separate the cases where the index of the second operand is + // equal to |first_mask_in_operand_index + 1|: + // - If the first memory operand doesn't exist, its value is equal to None. + // This means that it doesn't have additional operands following it and the + // condition in the if statement below will be satisfied. + // - If the first memory operand exists and has no additional memory operands + // following it, the condition in the if statement below will be satisfied + // and we will return the correct value from the function. + if (first_mask_in_operand_index + 1 >= instruction.NumInOperands()) { + return first_mask_in_operand_index + 1; + } + + // We are looking for the input operand index of the second mask. This is a + // little complicated because, depending on the contents of the first mask, + // there may be some input operands separating the two masks. + uint32_t first_mask = + instruction.GetSingleWordInOperand(first_mask_in_operand_index); + + // Consider each bit that might have an associated extra input operand, and + // count how many there are expected to be. + uint32_t first_mask_extra_operand_count = 0; + for (auto mask_bit : {spv::MemoryAccessMask::Aligned, + spv::MemoryAccessMask::MakePointerAvailable, + spv::MemoryAccessMask::MakePointerAvailableKHR, + spv::MemoryAccessMask::MakePointerVisible, + spv::MemoryAccessMask::MakePointerVisibleKHR}) { + if (first_mask & uint32_t(mask_bit)) { + first_mask_extra_operand_count++; + } + } + return first_mask_in_operand_index + first_mask_extra_operand_count + 1; +} + +bool TransformationSetMemoryOperandsMask:: + MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_context) { + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + return false; + default: + return true; + } +} + +std::unordered_set TransformationSetMemoryOperandsMask::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h new file mode 100644 index 00000000..c52fbdba --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetMemoryOperandsMask : public Transformation { + public: + explicit TransformationSetMemoryOperandsMask( + protobufs::TransformationSetMemoryOperandsMask message); + + TransformationSetMemoryOperandsMask( + const protobufs::InstructionDescriptor& memory_access_instruction, + uint32_t memory_operands_mask, uint32_t memory_operands_mask_index); + + // - |message_.memory_access_instruction| must describe a memory access + // instruction. + // - |message_.memory_operands_mask_index| must be suitable for this memory + // access instruction, e.g. it must be 0 in the case of OpLoad, and may be + // 1 in the case of OpCopyMemory if the SPIR-V version is 1.4 or higher. + // - |message_.memory_operands_mask| must be identical to the original memory + // operands mask, except that Volatile may be added, and Nontemporal may be + // toggled. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the operands mask identified by + // |message_.memory_operands_mask_index| in the instruction described by + // |message_.memory_access_instruction| with |message_.memory_operands_mask|, + // creating an input operand for the mask if no such operand was present. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Helper function that determines whether |instruction| is a memory + // instruction (e.g. OpLoad). + static bool IsMemoryAccess(const opt::Instruction& instruction); + + // Does the version of SPIR-V being used support multiple memory operand + // masks on relevant memory access instructions? + static bool MultipleMemoryOperandMasksAreSupported( + opt::IRContext* ir_context); + + // Helper function to get the input operand index associated with mask number + // |mask_index|. This is a bit tricky if there are multiple masks, because the + // index associated with the second mask depends on whether the first mask + // includes any flags such as Aligned that have corresponding operands. + static uint32_t GetInOperandIndexForMask(const opt::Instruction& instruction, + uint32_t mask_index); + + private: + protobufs::TransformationSetMemoryOperandsMask message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.cpp new file mode 100644 index 00000000..c6f68d9c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetSelectionControl::TransformationSetSelectionControl( + protobufs::TransformationSetSelectionControl message) + : message_(std::move(message)) {} + +TransformationSetSelectionControl::TransformationSetSelectionControl( + uint32_t block_id, uint32_t selection_control) { + message_.set_block_id(block_id); + message_.set_selection_control(selection_control); +} + +bool TransformationSetSelectionControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + assert((spv::SelectionControlMask(message_.selection_control()) == + spv::SelectionControlMask::MaskNone || + spv::SelectionControlMask(message_.selection_control()) == + spv::SelectionControlMask::Flatten || + spv::SelectionControlMask(message_.selection_control()) == + spv::SelectionControlMask::DontFlatten) && + "Selection control should never be set to something other than " + "'None', 'Flatten' or 'DontFlatten'"); + if (auto block = ir_context->get_instr_block(message_.block_id())) { + if (auto merge_inst = block->GetMergeInst()) { + return merge_inst->opcode() == spv::Op::OpSelectionMerge; + } + } + // Either the block did not exit, or did not end with OpSelectionMerge. + return false; +} + +void TransformationSetSelectionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ir_context->get_instr_block(message_.block_id()) + ->GetMergeInst() + ->SetInOperand(1, {message_.selection_control()}); +} + +protobufs::Transformation TransformationSetSelectionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_selection_control() = message_; + return result; +} + +std::unordered_set TransformationSetSelectionControl::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.h new file mode 100644 index 00000000..93b19049 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_set_selection_control.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetSelectionControl : public Transformation { + public: + explicit TransformationSetSelectionControl( + protobufs::TransformationSetSelectionControl message); + + TransformationSetSelectionControl(uint32_t block_id, + uint32_t selection_control); + + // - |message_.block_id| must be a block containing an OpSelectionMerge + // instruction. + // - |message_.selection_control| must be one of None, Flatten or + // DontFlatten. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - The selection control operand of the OpSelectionMergeInstruction in + // |message_.block_id| is overwritten with |message_.selection_control|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSetSelectionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.cpp new file mode 100644 index 00000000..959bedcd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_split_block.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationSplitBlock::TransformationSplitBlock( + protobufs::TransformationSplitBlock message) + : message_(std::move(message)) {} + +TransformationSplitBlock::TransformationSplitBlock( + const protobufs::InstructionDescriptor& instruction_to_split_before, + uint32_t fresh_id) { + *message_.mutable_instruction_to_split_before() = instruction_to_split_before; + message_.set_fresh_id(fresh_id); +} + +bool TransformationSplitBlock::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + // We require the id for the new block to be unused. + return false; + } + auto instruction_to_split_before = + FindInstruction(message_.instruction_to_split_before(), ir_context); + if (!instruction_to_split_before) { + // The instruction describing the block we should split does not exist. + return false; + } + auto block_to_split = + ir_context->get_instr_block(instruction_to_split_before); + assert(block_to_split && + "We should not have managed to find the " + "instruction if it was not contained in a block."); + + if (block_to_split->IsLoopHeader()) { + // We cannot split a loop header block: back-edges would become invalid. + return false; + } + + auto split_before = fuzzerutil::GetIteratorForInstruction( + block_to_split, instruction_to_split_before); + assert(split_before != block_to_split->end() && + "At this point we know the" + " block split point exists."); + + if (split_before->PreviousNode() && + split_before->PreviousNode()->opcode() == spv::Op::OpSelectionMerge) { + // We cannot split directly after a selection merge: this would separate + // the merge from its associated branch or switch operation. + return false; + } + if (split_before->opcode() == spv::Op::OpVariable) { + // We cannot split directly after a variable; variables in a function + // must be contiguous in the entry block. + return false; + } + // We cannot split before an OpPhi unless the OpPhi has exactly one + // associated incoming edge. + if (split_before->opcode() == spv::Op::OpPhi && + split_before->NumInOperands() != 2) { + return false; + } + + // Splitting the block must not separate the definition of an OpSampledImage + // from its use: the SPIR-V data rules require them to be in the same block. + return !fuzzerutil:: + SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + block_to_split, instruction_to_split_before); +} + +void TransformationSplitBlock::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction* instruction_to_split_before = + FindInstruction(message_.instruction_to_split_before(), ir_context); + opt::BasicBlock* block_to_split = + ir_context->get_instr_block(instruction_to_split_before); + auto split_before = fuzzerutil::GetIteratorForInstruction( + block_to_split, instruction_to_split_before); + assert(split_before != block_to_split->end() && + "If the transformation is applicable, we should have an " + "instruction to split on."); + + // We need to make sure the module's id bound is large enough to add the + // fresh id. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Split the block. + auto new_bb = block_to_split->SplitBasicBlock(ir_context, message_.fresh_id(), + split_before); + // The split does not automatically add a branch between the two parts of + // the original block, so we add one. + auto branch_instruction = MakeUnique( + ir_context, spv::Op::OpBranch, 0, 0, + std::initializer_list{opt::Operand( + spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}); + auto branch_instruction_ptr = branch_instruction.get(); + block_to_split->AddInstruction(std::move(branch_instruction)); + + // Inform the def-use manager about the branch instruction, and record its + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(branch_instruction_ptr); + ir_context->set_instr_block(branch_instruction_ptr, block_to_split); + + // If we split before OpPhi instructions, we need to update their + // predecessor operand so that the block they used to be inside is now the + // predecessor. + new_bb->ForEachPhiInst([block_to_split, + ir_context](opt::Instruction* phi_inst) { + assert( + phi_inst->NumInOperands() == 2 && + "Precondition: a block can only be split before an OpPhi if the block" + "has exactly one predecessor."); + phi_inst->SetInOperand(1, {block_to_split->id()}); + ir_context->UpdateDefUse(phi_inst); + }); + + // We have updated the def-use manager and the instruction to block mapping, + // but other analyses (especially control flow-related ones) need to be + // recomputed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisDefUse | + opt::IRContext::Analysis::kAnalysisInstrToBlockMapping); + + // If the block being split was dead, the new block arising from the split is + // also dead. + if (transformation_context->GetFactManager()->BlockIsDead( + block_to_split->id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationSplitBlock::ToMessage() const { + protobufs::Transformation result; + *result.mutable_split_block() = message_; + return result; +} + +std::unordered_set TransformationSplitBlock::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.h new file mode 100644 index 00000000..ace77b5b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_split_block.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSplitBlock : public Transformation { + public: + explicit TransformationSplitBlock( + protobufs::TransformationSplitBlock message); + + TransformationSplitBlock( + const protobufs::InstructionDescriptor& instruction_to_split_before, + uint32_t fresh_id); + + // - |message_.base_instruction_id| must be the result id of an instruction + // 'base' in some block 'blk'. + // - 'blk' must contain an instruction 'inst' located |message_.offset| + // instructions after 'base' (if |message_.offset| = 0 then 'inst' = + // 'base'). + // - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards + // appear in a new block that 'blk' directly jumps to must be valid. + // - |message_.fresh_id| must not be used by the module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - A new block with label |message_.fresh_id| is inserted right after 'blk' + // in program order. + // - All instructions of 'blk' from 'inst' onwards are moved into the new + // block. + // - 'blk' is made to jump unconditionally to the new block. + // - If 'blk' was dead, the new block is also dead. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSplitBlock message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.cpp new file mode 100644 index 00000000..6ba1c551 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationStore::TransformationStore(protobufs::TransformationStore message) + : message_(std::move(message)) {} + +TransformationStore::TransformationStore( + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_pointer_id(pointer_id); + message_.set_is_atomic(is_atomic); + message_.set_memory_scope_id(memory_scope); + message_.set_memory_semantics_id(memory_semantics); + message_.set_value_id(value_id); + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationStore::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The pointer must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + + // The pointer type must indeed be a pointer. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != spv::Op::OpTypePointer) { + return false; + } + + // The pointer must not be read only. + if (pointer->IsReadOnlyPointer()) { + return false; + } + + // We do not want to allow storing to null or undefined pointers. + switch (pointer->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a store before it. + if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpStore, insert_before)) { + return false; + } + if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpAtomicStore, insert_before)) { + return false; + } + + // The block we are inserting into needs to be dead, or else the pointee type + // of the pointer we are storing to needs to be irrelevant (otherwise the + // store could impact on the observable behaviour of the module). + if (!transformation_context.GetFactManager()->BlockIsDead( + ir_context->get_instr_block(insert_before)->id()) && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { + return false; + } + + // The value being stored needs to exist and have a type. + auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + if (!value || !value->type_id()) { + return false; + } + + // The type of the value must match the pointee type. + if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) { + return false; + } + + // The pointer needs to be available at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.pointer_id())) { + return false; + } + + if (message_.is_atomic()) { + // Check the exists of memory scope and memory semantics ids. + auto memory_scope_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id()); + auto memory_semantics_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id()); + + if (!memory_scope_instruction) { + return false; + } + if (!memory_semantics_instruction) { + return false; + } + // The memory scope and memory semantics instructions must have the + // 'OpConstant' opcode. + if (memory_scope_instruction->opcode() != spv::Op::OpConstant) { + return false; + } + if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) { + return false; + } + // The memory scope and memory semantics need to be available before + // |insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_scope_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before, message_.memory_semantics_id())) { + return false; + } + // The memory scope and memory semantics instructions must have an Integer + // operand type with signedness does not matters. + if (ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->opcode() != spv::Op::OpTypeInt) { + return false; + } + if (ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->opcode() != spv::Op::OpTypeInt) { + return false; + } + + // The size of the integer for memory scope and memory semantics + // instructions must be equal to 32 bits. + auto memory_scope_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_scope_instruction->type_id()) + ->GetSingleWordInOperand(0); + auto memory_semantics_int_width = + ir_context->get_def_use_mgr() + ->GetDef(memory_semantics_instruction->type_id()) + ->GetSingleWordInOperand(0); + + if (memory_scope_int_width != 32) { + return false; + } + if (memory_semantics_int_width != 32) { + return false; + } + + // The memory scope constant value must be that of spv::Scope::Invocation. + auto memory_scope_const_value = + memory_scope_instruction->GetSingleWordInOperand(0); + if (spv::Scope(memory_scope_const_value) != spv::Scope::Invocation) { + return false; + } + + // The memory semantics constant value must match the storage class of the + // pointer being loaded from. + auto memory_semantics_const_value = static_cast( + memory_semantics_instruction->GetSingleWordInOperand(0)); + if (memory_semantics_const_value != + fuzzerutil::GetMemorySemanticsForStorageClass( + static_cast( + pointer_type->GetSingleWordInOperand(0)))) { + return false; + } + } + + // The value needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.value_id()); +} + +void TransformationStore::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + if (message_.is_atomic()) { + // OpAtomicStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpAtomicStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, + {message_.memory_semantics_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + + } else { + // OpStore instruction. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); + } +} + +protobufs::Transformation TransformationStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_store() = message_; + return result; +} + +std::unordered_set TransformationStore::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.h new file mode 100644 index 00000000..5b55ce6c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_store.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_STORE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationStore : public Transformation { + public: + explicit TransformationStore(protobufs::TransformationStore message); + + TransformationStore( + uint32_t pointer_id, bool is_atomic, uint32_t memory_scope, + uint32_t memory_semantics, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.pointer_id| must be the id of a pointer + // - The pointer type must not have read-only storage class + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.is_atomic| must be true if want to work with OpAtomicStore. + // - If |is_atomic| is true then |message_memory_scope_id| must be the id of + // an OpConstant 32 bit integer instruction with the value + // spv::Scope::Invocation. + // - If |is_atomic| is true then |message_.memory_semantics_id| must be the id + // of an OpConstant 32 bit integer instruction with the values + // SpvMemorySemanticsWorkgroupMemoryMask or + // SpvMemorySemanticsUniformMemoryMask. + // - |message_.value_id| must be an instruction result id that has the same + // type as the pointee type of |message_.pointer_id| + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpStore, and where both + // |message_.pointer_id| and |message_.value_id| are available (according + // to dominance rules) + // - Either the insertion point must be in a dead block, or it must be known + // that the pointee value of |message_.pointer_id| is irrelevant + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // OpStore |pointer_id| |value_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_STORE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp new file mode 100644 index 00000000..15121443 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + protobufs::TransformationSwapCommutableOperands message) + : message_(std::move(message)) {} + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationSwapCommutableOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) return false; + + spv::Op opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + assert(spv::Op(instruction->opcode()) == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + return spvOpcodeIsCommutativeBinaryOperator(opcode); +} + +void TransformationSwapCommutableOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + // By design, the instructions defined to be commutative have exactly two + // input parameters. + std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); +} + +protobufs::Transformation TransformationSwapCommutableOperands::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_commutable_operands() = message_; + return result; +} + +std::unordered_set TransformationSwapCommutableOperands::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h new file mode 100644 index 00000000..5e211f19 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapCommutableOperands : public Transformation { + public: + explicit TransformationSwapCommutableOperands( + protobufs::TransformationSwapCommutableOperands message); + + TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // commutative instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps the commutable operands. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapCommutableOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp new file mode 100644 index 00000000..520a6a8e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapConditionalBranchOperands:: + TransformationSwapConditionalBranchOperands( + protobufs::TransformationSwapConditionalBranchOperands message) + : message_(std::move(message)) {} + +TransformationSwapConditionalBranchOperands:: + TransformationSwapConditionalBranchOperands( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.set_fresh_id(fresh_id); +} + +bool TransformationSwapConditionalBranchOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + const auto* inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst && + inst->opcode() == spv::Op::OpBranchConditional; +} + +void TransformationSwapConditionalBranchOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* branch_inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + assert(branch_inst); + + auto* block = ir_context->get_instr_block(branch_inst); + assert(block); + + // Compute the last instruction in the |block| that allows us to insert + // OpLogicalNot above it. + auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst); + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot, + iter)) { + // There might be a merge instruction before OpBranchConditional. + --iter; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot, + iter) && + "We should now be able to insert spv::Op::OpLogicalNot before |iter|"); + + // Get the instruction whose result is used as a condition for + // OpBranchConditional. + const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef( + branch_inst->GetSingleWordInOperand(0)); + assert(condition_inst); + + // We are swapping the labels in OpBranchConditional. This means that we must + // invert the guard as well. We are using OpLogicalNot for that purpose here. + auto new_instruction = MakeUnique( + ir_context, spv::Op::OpLogicalNot, condition_inst->type_id(), + message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}); + auto new_instruction_ptr = new_instruction.get(); + iter.InsertBefore(std::move(new_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Update OpBranchConditional condition operand. + branch_inst->GetInOperand(0).words[0] = message_.fresh_id(); + + // Swap label operands. + std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2)); + + // Additionally, swap branch weights if present. + if (branch_inst->NumInOperands() > 3) { + std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4)); + } + + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, block); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst); + ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst); + + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. +} + +protobufs::Transformation +TransformationSwapConditionalBranchOperands::ToMessage() const { + protobufs::Transformation result; + *result.mutable_swap_conditional_branch_operands() = message_; + return result; +} + +std::unordered_set +TransformationSwapConditionalBranchOperands::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h new file mode 100644 index 00000000..165ab806 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapConditionalBranchOperands : public Transformation { + public: + explicit TransformationSwapConditionalBranchOperands( + protobufs::TransformationSwapConditionalBranchOperands message); + + TransformationSwapConditionalBranchOperands( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id); + + // - |message_.instruction_descriptor| must be a valid descriptor of some + // OpBranchConditional instruction in the module. + // - |message_.fresh_id| must be a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before + // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|. + // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_* + // operands. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapConditionalBranchOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.cpp new file mode 100644 index 00000000..14ba7bb7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_function_variables.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message) + : message_(std::move(message)) {} + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + uint32_t result_id1, uint32_t result_id2) { + message_.set_result_id1(result_id1); + message_.set_result_id2(result_id2); +} + +bool TransformationSwapFunctionVariables::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + uint32_t result_id1 = message_.result_id1(); + uint32_t result_id2 = message_.result_id2(); + + assert((result_id1 != result_id2) && "Two results ids are equal"); + + // The result ids used in the message must refer to instructions. + auto instruction1 = ir_context->get_def_use_mgr()->GetDef(result_id1); + auto instruction2 = ir_context->get_def_use_mgr()->GetDef(result_id2); + if (instruction1 == nullptr || instruction2 == nullptr) { + return false; + } + // Both instructions must be variables. + if (instruction1->opcode() != spv::Op::OpVariable || + instruction2->opcode() != spv::Op::OpVariable) { + return false; + } + + // Both variable instructions must be in some basic block (as they are + // function-local variables), and they must be in the same block (as they need + // to be variables of the same function). + auto* block_1 = ir_context->get_instr_block(result_id1); + auto* block_2 = ir_context->get_instr_block(result_id2); + if (block_1 == nullptr || block_2 == nullptr) { + return false; + } + + return block_1 == block_2; +} + +void TransformationSwapFunctionVariables::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // The result ids used in the message must refer to instructions. + auto instruction1 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id1()); + auto instruction2 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id2()); + + std::unique_ptr temp_instruction = + MakeUnique(); + + temp_instruction->InsertBefore(instruction1); + instruction1->InsertAfter(instruction2); + instruction2->InsertAfter(temp_instruction.get()); + temp_instruction->RemoveFromList(); +} + +protobufs::Transformation TransformationSwapFunctionVariables::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_function_variables() = message_; + return result; +} + +std::unordered_set TransformationSwapFunctionVariables::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.h new file mode 100644 index 00000000..eb383f4f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_function_variables.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A transformation that swaps two variable declaration instructions that appear +// in the same function. +class TransformationSwapFunctionVariables : public Transformation { + public: + explicit TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message); + + TransformationSwapFunctionVariables(uint32_t result_id1, uint32_t result_id2); + + // - |message_.result_id1| and |message_.result_id2| must be the ids of + // distinct OpVariable instructions appearing in the same function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps two OpVariable instructions with result ids |message_.result_id1| + // and |message_.result_id2|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + std::unordered_set GetFreshIds() const override; + + private: + protobufs::TransformationSwapFunctionVariables message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.cpp new file mode 100644 index 00000000..85d9e79c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_two_functions.h" + +#include "source/opt/function.h" +#include "source/opt/module.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message) + : message_(std::move(message)) {} + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(uint32_t id1, + uint32_t id2) { + assert(id1 != id2 && "The two function ids cannot be the same."); + message_.set_function_id1(id1); + message_.set_function_id2(id2); +} + +bool TransformationSwapTwoFunctions::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto func1_ptr = ir_context->GetFunction(message_.function_id1()); + auto func2_ptr = ir_context->GetFunction(message_.function_id2()); + return func1_ptr != nullptr && func2_ptr != nullptr; +} + +void TransformationSwapTwoFunctions::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Module::iterator func1_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id1()); + opt::Module::iterator func2_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id2()); + + assert(func1_it != ir_context->module()->end() && + "Could not find function 1."); + assert(func2_it != ir_context->module()->end() && + "Could not find function 2."); + + // Two function pointers are all set, swap the two functions within the + // module. + std::iter_swap(func1_it.Get(), func2_it.Get()); +} + +protobufs::Transformation TransformationSwapTwoFunctions::ToMessage() const { + protobufs::Transformation result; + *result.mutable_swap_two_functions() = message_; + return result; +} + +std::unordered_set TransformationSwapTwoFunctions::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.h new file mode 100644 index 00000000..1b6d6e80 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_swap_two_functions.h @@ -0,0 +1,56 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapTwoFunctions : public Transformation { + public: + explicit TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message); + + TransformationSwapTwoFunctions(uint32_t function_id1, uint32_t function_id2); + + // |function_id1| and |function_id1| should all be existing ids. + // Swap function operation is only permitted if: + // - both ids must be ids of functions. + // - both ids can be found in the module. + // - function_id1 and function_id2 are not the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // OpFunction with |function_id1| and |function_id1| are swapped. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapTwoFunctions message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp new file mode 100644 index 00000000..ae6ab9ad --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + protobufs::TransformationToggleAccessChainInstruction message) + : message_(std::move(message)) {} + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationToggleAccessChainInstruction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + spv::Op opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + + if (opcode == spv::Op::OpAccessChain || + opcode == spv::Op::OpInBoundsAccessChain) { + return true; + } + + return false; +} + +void TransformationToggleAccessChainInstruction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + spv::Op opcode = instruction->opcode(); + + if (opcode == spv::Op::OpAccessChain) { + instruction->SetOpcode(spv::Op::OpInBoundsAccessChain); + } else { + assert(opcode == spv::Op::OpInBoundsAccessChain && + "The located instruction must be an OpInBoundsAccessChain " + "instruction."); + instruction->SetOpcode(spv::Op::OpAccessChain); + } +} + +protobufs::Transformation +TransformationToggleAccessChainInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_toggle_access_chain_instruction() = message_; + return result; +} + +std::unordered_set +TransformationToggleAccessChainInstruction::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h new file mode 100644 index 00000000..be2718b0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationToggleAccessChainInstruction : public Transformation { + public: + explicit TransformationToggleAccessChainInstruction( + protobufs::TransformationToggleAccessChainInstruction message); + + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // access chain instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Toggles the access chain instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationToggleAccessChainInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp new file mode 100644 index 00000000..8ba557c9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp @@ -0,0 +1,238 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_vector_shuffle.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationVectorShuffle::TransformationVectorShuffle( + protobufs::TransformationVectorShuffle message) + : message_(std::move(message)) {} + +TransformationVectorShuffle::TransformationVectorShuffle( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t vector1, uint32_t vector2, + const std::vector& component) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_vector1(vector1); + message_.set_vector2(vector2); + for (auto a_component : component) { + message_.add_component(a_component); + } +} + +bool TransformationVectorShuffle::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The fresh id must not already be in use. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The instruction before which the shuffle will be inserted must exist. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + // The first vector must be an instruction with a type id + auto vector1_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.vector1()); + if (!vector1_instruction || !vector1_instruction->type_id()) { + return false; + } + // The second vector must be an instruction with a type id + auto vector2_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.vector2()); + if (!vector2_instruction || !vector2_instruction->type_id()) { + return false; + } + auto vector1_type = + ir_context->get_type_mgr()->GetType(vector1_instruction->type_id()); + // The first vector instruction's type must actually be a vector type. + if (!vector1_type->AsVector()) { + return false; + } + auto vector2_type = + ir_context->get_type_mgr()->GetType(vector2_instruction->type_id()); + // The second vector instruction's type must actually be a vector type. + if (!vector2_type->AsVector()) { + return false; + } + // The element types of the vectors must be the same. + if (vector1_type->AsVector()->element_type() != + vector2_type->AsVector()->element_type()) { + return false; + } + uint32_t combined_size = vector1_type->AsVector()->element_count() + + vector2_type->AsVector()->element_count(); + for (auto a_compoment : message_.component()) { + // 0xFFFFFFFF is used to represent an undefined component. Unless + // undefined, a component must be less than the combined size of the + // vectors. + if (a_compoment != 0xFFFFFFFF && a_compoment >= combined_size) { + return false; + } + } + // The module must already declare an appropriate type in which to store the + // result of the shuffle. + if (!GetResultTypeId(ir_context, *vector1_type->AsVector()->element_type())) { + return false; + } + // Each of the vectors used in the shuffle must be available at the insertion + // point. + for (auto used_instruction : {vector1_instruction, vector2_instruction}) { + if (auto block = ir_context->get_instr_block(used_instruction)) { + if (!ir_context->GetDominatorAnalysis(block->GetParent()) + ->Dominates(used_instruction, instruction_to_insert_before)) { + return false; + } + } + } + + // It must be legitimate to insert an OpVectorShuffle before the identified + // instruction. + return fuzzerutil::CanInsertOpcodeBeforeInstruction( + spv::Op::OpVectorShuffle, instruction_to_insert_before); +} + +void TransformationVectorShuffle::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Make input operands for a shuffle instruction - these comprise the two + // vectors being shuffled, followed by the integer literal components. + opt::Instruction::OperandList shuffle_operands = { + {SPV_OPERAND_TYPE_ID, {message_.vector1()}}, + {SPV_OPERAND_TYPE_ID, {message_.vector2()}}}; + for (auto a_component : message_.component()) { + shuffle_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {a_component}}); + } + + uint32_t result_type_id = GetResultTypeId( + ir_context, + *GetVectorType(ir_context, message_.vector1())->element_type()); + + // Add a shuffle instruction right before the instruction identified by + // |message_.instruction_to_insert_before|. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique( + ir_context, spv::Op::OpVectorShuffle, result_type_id, + message_.fresh_id(), shuffle_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); + + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationVectorShuffle::ToMessage() const { + protobufs::Transformation result; + *result.mutable_vector_shuffle() = message_; + return result; +} + +uint32_t TransformationVectorShuffle::GetResultTypeId( + opt::IRContext* ir_context, const opt::analysis::Type& element_type) const { + opt::analysis::Vector result_type( + &element_type, static_cast(message_.component_size())); + return ir_context->get_type_mgr()->GetId(&result_type); +} + +opt::analysis::Vector* TransformationVectorShuffle::GetVectorType( + opt::IRContext* ir_context, uint32_t id_of_vector) { + return ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr()->GetDef(id_of_vector)->type_id()) + ->AsVector(); +} + +std::unordered_set TransformationVectorShuffle::GetFreshIds() const { + return {message_.fresh_id()}; +} + +void TransformationVectorShuffle::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the new instruction is irrelevant (because it is in a dead block), it + // cannot participate in any DataSynonym fact. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Add synonym facts relating the defined elements of the shuffle result to + // the vector components that they come from. + for (uint32_t component_index = 0; + component_index < static_cast(message_.component_size()); + component_index++) { + uint32_t component = message_.component(component_index); + if (component == 0xFFFFFFFF) { + // This component is undefined, we do not introduce a synonym. + continue; + } + // This describes the element of the result vector associated with + // |component_index|. + protobufs::DataDescriptor descriptor_for_result_component = + MakeDataDescriptor(message_.fresh_id(), {component_index}); + + protobufs::DataDescriptor descriptor_for_source_component; + + // Get a data descriptor for the component of the input vector to which + // |component| refers. + if (component < + GetVectorType(ir_context, message_.vector1())->element_count()) { + // Check that the first vector can participate in data synonym facts. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { + continue; + } + descriptor_for_source_component = + MakeDataDescriptor(message_.vector1(), {component}); + } else { + // Check that the second vector can participate in data synonym facts. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + *ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { + continue; + } + auto index_into_vector_2 = + component - + GetVectorType(ir_context, message_.vector1())->element_count(); + assert( + index_into_vector_2 < + GetVectorType(ir_context, message_.vector2())->element_count() && + "Vector shuffle index is out of bounds."); + descriptor_for_source_component = + MakeDataDescriptor(message_.vector2(), {index_into_vector_2}); + } + + // Add a fact relating this input vector component with the associated + // result component. + transformation_context->GetFactManager()->AddFactDataSynonym( + descriptor_for_result_component, descriptor_for_source_component); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.h new file mode 100644 index 00000000..7360906f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_vector_shuffle.h @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace fuzz { + +class TransformationVectorShuffle : public Transformation { + public: + explicit TransformationVectorShuffle( + protobufs::TransformationVectorShuffle message); + + TransformationVectorShuffle( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t vector1, uint32_t vector2, + const std::vector& component); + + // - |message_.fresh_id| must not be in use + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is legitimate to insert an OpVectorShuffle + // - |message_.vector1| and |message_.vector2| must be instructions of vector + // type, and the element types of these vectors must be the same + // - Each element of |message_.component| must either be 0xFFFFFFFF + // (representing an undefined component), or must be less than the combined + // sizes of the input vectors + // - The module must already contain a vector type with the same element type + // as |message_.vector1| and |message_.vector2|, and with the size of + // |message_component| as its element count + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts an OpVectorShuffle instruction before + // |message_.instruction_to_insert_before|, shuffles vectors + // |message_.vector1| and |message_.vector2| using the indices provided by + // |message_.component|, into |message_.fresh_id|. + // + // If |message_.fresh_id| is irrelevant (e.g. due to being in a dead block) + // of if one of |message_.vector1| or |message_.vector2| is irrelevant and the + // shuffle reads components from the irrelevant vector then no synonym facts + // are added. + // + // Otherwise, a fact is added recording that element of |message_.fresh_id| is + // synonymous with the element of |message_.vector1| or |message_.vector2| + // from which it came (with undefined components being ignored). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns a type id that already exists in |ir_context| suitable for + // representing the result of the shuffle, where |element_type| is known to + // be the common element type of the vectors to which the shuffle is being + // applied. Returns 0 if no such id exists. + uint32_t GetResultTypeId(opt::IRContext* ir_context, + const opt::analysis::Type& element_type) const; + + // Returns the type associated with |id_of_vector| in |ir_context|. + static opt::analysis::Vector* GetVectorType(opt::IRContext* ir_context, + uint32_t id_of_vector); + + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationVectorShuffle message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp new file mode 100644 index 00000000..f59d78ad --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapEarlyTerminatorInFunction:: + TransformationWrapEarlyTerminatorInFunction( + protobufs::TransformationWrapEarlyTerminatorInFunction message) + : message_(std::move(message)) {} + +TransformationWrapEarlyTerminatorInFunction:: + TransformationWrapEarlyTerminatorInFunction( + uint32_t fresh_id, + const protobufs::InstructionDescriptor& early_terminator_instruction, + uint32_t returned_value_id) { + message_.set_fresh_id(fresh_id); + *message_.mutable_early_terminator_instruction() = + early_terminator_instruction; + message_.set_returned_value_id(returned_value_id); +} + +bool TransformationWrapEarlyTerminatorInFunction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The given id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // |message_.early_terminator_instruction| must identify an instruction, and + // the instruction must indeed be an early terminator. + auto early_terminator = + FindInstruction(message_.early_terminator_instruction(), ir_context); + if (!early_terminator) { + return false; + } + switch (early_terminator->opcode()) { + case spv::Op::OpKill: + case spv::Op::OpUnreachable: + case spv::Op::OpTerminateInvocation: + break; + default: + return false; + } + // A wrapper function for the early terminator must exist. + auto wrapper_function = + MaybeGetWrapperFunction(ir_context, early_terminator->opcode()); + if (wrapper_function == nullptr) { + return false; + } + auto enclosing_function = + ir_context->get_instr_block(early_terminator)->GetParent(); + // The wrapper function cannot be the function containing the instruction we + // would like to wrap. + if (wrapper_function->result_id() == enclosing_function->result_id()) { + return false; + } + if (!ir_context->get_type_mgr() + ->GetType(enclosing_function->type_id()) + ->AsVoid()) { + // The enclosing function has non-void return type. We thus need to make + // sure that |message_.returned_value_instruction| provides a suitable + // result id to use in an OpReturnValue instruction. + auto returned_value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.returned_value_id()); + if (!returned_value_instruction || !returned_value_instruction->type_id() || + returned_value_instruction->type_id() != + enclosing_function->type_id()) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, early_terminator, message_.returned_value_id())) { + return false; + } + } + return true; +} + +void TransformationWrapEarlyTerminatorInFunction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto early_terminator = + FindInstruction(message_.early_terminator_instruction(), ir_context); + auto enclosing_block = ir_context->get_instr_block(early_terminator); + auto enclosing_function = enclosing_block->GetParent(); + + // We would like to add an OpFunctionCall before the block's terminator + // instruction, and then change the block's terminator to OpReturn or + // OpReturnValue. + + // We get an iterator to the instruction we would like to insert the function + // call before. It will be an iterator to the final instruction in the block + // unless the block is a merge block in which case it will be to the + // penultimate instruction (because we cannot insert an OpFunctionCall after + // a merge instruction). + auto iterator = enclosing_block->tail(); + if (enclosing_block->MergeBlockIdIfAny()) { + --iterator; + } + + auto wrapper_function = + MaybeGetWrapperFunction(ir_context, early_terminator->opcode()); + + iterator->InsertBefore(MakeUnique( + ir_context, spv::Op::OpFunctionCall, wrapper_function->type_id(), + message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {wrapper_function->result_id()}}}))); + + opt::Instruction::OperandList new_in_operands; + if (!ir_context->get_type_mgr() + ->GetType(enclosing_function->type_id()) + ->AsVoid()) { + new_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.returned_value_id()}}); + early_terminator->SetOpcode(spv::Op::OpReturnValue); + } else { + early_terminator->SetOpcode(spv::Op::OpReturn); + } + early_terminator->SetInOperands(std::move(new_in_operands)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationWrapEarlyTerminatorInFunction::GetFreshIds() const { + return std::unordered_set({message_.fresh_id()}); +} + +protobufs::Transformation +TransformationWrapEarlyTerminatorInFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_early_terminator_in_function() = message_; + return result; +} + +opt::Function* +TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction( + opt::IRContext* ir_context, spv::Op early_terminator_opcode) { + assert((early_terminator_opcode == spv::Op::OpKill || + early_terminator_opcode == spv::Op::OpUnreachable || + early_terminator_opcode == spv::Op::OpTerminateInvocation) && + "Invalid opcode."); + auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + if (!void_type_id) { + return nullptr; + } + auto void_function_type_id = + fuzzerutil::FindFunctionType(ir_context, {void_type_id}); + if (!void_function_type_id) { + return nullptr; + } + for (auto& function : *ir_context->module()) { + if (function.DefInst().GetSingleWordInOperand(1) != void_function_type_id) { + continue; + } + if (function.begin()->begin()->opcode() == early_terminator_opcode) { + return &function; + } + } + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h new file mode 100644 index 00000000..d824783b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapEarlyTerminatorInFunction : public Transformation { + public: + explicit TransformationWrapEarlyTerminatorInFunction( + protobufs::TransformationWrapEarlyTerminatorInFunction message); + + TransformationWrapEarlyTerminatorInFunction( + uint32_t fresh_id, + const protobufs::InstructionDescriptor& early_terminator_instruction, + uint32_t returned_value_id); + + // - |message_.fresh_id| must be fresh. + // - |message_.early_terminator_instruction| must identify an early terminator + // instruction, i.e. an instruction with opcode OpKill, OpUnreachable or + // OpTerminateInvocation. + // - A suitable wrapper function for the early terminator must exist, and it + // must be distinct from the function containing + // |message_.early_terminator_instruction|. + // - If the enclosing function has non-void return type then + // |message_.returned_value_instruction| must be the id of an instruction of + // the return type that is available at the point of the early terminator + // instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // An OpFunctionCall instruction to an appropriate wrapper function is + // inserted before |message_.early_terminator_instruction|, and + // |message_.early_terminator_instruction| is replaced with either OpReturn + // or OpReturnValue |message_.returned_value_instruction| depending on whether + // the enclosing function's return type is void. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + static opt::Function* MaybeGetWrapperFunction( + opt::IRContext* ir_context, spv::Op early_terminator_opcode); + + private: + protobufs::TransformationWrapEarlyTerminatorInFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp new file mode 100644 index 00000000..a63d1ac5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_region_in_selection.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( + protobufs::TransformationWrapRegionInSelection message) + : message_(std::move(message)) {} + +TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( + uint32_t region_entry_block_id, uint32_t region_exit_block_id, + bool branch_condition) { + message_.set_region_entry_block_id(region_entry_block_id); + message_.set_region_exit_block_id(region_exit_block_id); + message_.set_branch_condition(branch_condition); +} + +bool TransformationWrapRegionInSelection::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that it is possible to outline a region of blocks without breaking + // domination and structured control flow rules. + if (!IsApplicableToBlockRange(ir_context, message_.region_entry_block_id(), + message_.region_exit_block_id())) { + return false; + } + + // There must exist an irrelevant boolean constant to be used as a condition + // in the OpBranchConditional instruction. + return fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.branch_condition(), + true) != 0; +} + +void TransformationWrapRegionInSelection::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* new_header_block = + ir_context->cfg()->block(message_.region_entry_block_id()); + assert(new_header_block->terminator()->opcode() == spv::Op::OpBranch && + "This condition should have been checked in the IsApplicable"); + + const auto successor_id = + new_header_block->terminator()->GetSingleWordInOperand(0); + + // Change |entry_block|'s terminator to |OpBranchConditional|. + new_header_block->terminator()->SetOpcode(spv::Op::OpBranchConditional); + new_header_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, + message_.branch_condition(), true)}}, + {SPV_OPERAND_TYPE_ID, {successor_id}}, + {SPV_OPERAND_TYPE_ID, {successor_id}}}); + + // Insert OpSelectionMerge before the terminator. + new_header_block->terminator()->InsertBefore(MakeUnique( + ir_context, spv::Op::OpSelectionMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.region_exit_block_id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}})); + + // We've change the module so we must invalidate analyses. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationWrapRegionInSelection::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_wrap_region_in_selection() = message_; + return result; +} + +bool TransformationWrapRegionInSelection::IsApplicableToBlockRange( + opt::IRContext* ir_context, uint32_t header_block_candidate_id, + uint32_t merge_block_candidate_id) { + // Check that |header_block_candidate_id| and |merge_block_candidate_id| are + // valid. + const auto* header_block_candidate = + fuzzerutil::MaybeFindBlock(ir_context, header_block_candidate_id); + if (!header_block_candidate) { + return false; + } + + const auto* merge_block_candidate = + fuzzerutil::MaybeFindBlock(ir_context, merge_block_candidate_id); + if (!merge_block_candidate) { + return false; + } + + // |header_block_candidate| and |merge_block_candidate| must be from the same + // function. + if (header_block_candidate->GetParent() != + merge_block_candidate->GetParent()) { + return false; + } + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(header_block_candidate->GetParent()); + const auto* postdominator_analysis = + ir_context->GetPostDominatorAnalysis(header_block_candidate->GetParent()); + + if (!dominator_analysis->StrictlyDominates(header_block_candidate, + merge_block_candidate) || + !postdominator_analysis->StrictlyDominates(merge_block_candidate, + header_block_candidate)) { + return false; + } + + // |header_block_candidate| can't be a header since we are about to make it + // one. + if (header_block_candidate->GetMergeInst()) { + return false; + } + + // |header_block_candidate| must have an OpBranch terminator. + if (header_block_candidate->terminator()->opcode() != spv::Op::OpBranch) { + return false; + } + + // Every header block must have a unique merge block. Thus, + // |merge_block_candidate| can't be a merge block of some other header. + auto* structured_cfg = ir_context->GetStructuredCFGAnalysis(); + if (structured_cfg->IsMergeBlock(merge_block_candidate_id)) { + return false; + } + + // |header_block_candidate|'s containing construct must also contain + // |merge_block_candidate|. + // + // ContainingConstruct will return the id of a loop header for a block in the + // loop's continue construct. Thus, we must also check the case when one of + // the candidates is in continue construct and the other one is not. + if (structured_cfg->ContainingConstruct(header_block_candidate_id) != + structured_cfg->ContainingConstruct(merge_block_candidate_id) || + structured_cfg->IsInContinueConstruct(header_block_candidate_id) != + structured_cfg->IsInContinueConstruct(merge_block_candidate_id)) { + return false; + } + + return true; +} + +std::unordered_set TransformationWrapRegionInSelection::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h new file mode 100644 index 00000000..66d16dad --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h @@ -0,0 +1,88 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapRegionInSelection : public Transformation { + public: + explicit TransformationWrapRegionInSelection( + protobufs::TransformationWrapRegionInSelection message); + + TransformationWrapRegionInSelection(uint32_t region_entry_block_id, + uint32_t region_exit_block_id, + bool branch_condition); + + // - It should be possible to apply this transformation to a + // single-exit-single-entry region of blocks dominated by + // |region_entry_block_id| and postdominated by |region_exit_block_id| + // (see IsApplicableToBlockRange method for further details). + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3828): + // Consider applying this transformation to non-single-entry-single-exit + // regions of blocks. + // - There must exist an irrelevant boolean constant with value + // |branch_condition|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Transforms |region_entry_block_id| into a selection header with both + // branches pointing to the block's successor. + // - |branch_condition| is used as a condition in the header's + // OpBranchConditional instruction. + // - Transforms |region_exit_block_id| into a merge block of the selection's + // header. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if it's possible to apply this transformation to the + // single-exit-single-entry region of blocks starting with + // |header_block_candidate_id| and ending with |merge_block_candidate_id|. + // Concretely: + // - Both |header_block_candidate_id| and |merge_block_candidate_id| must be + // result ids of some blocks in the module. + // - Both blocks must belong to the same function. + // - |header_block_candidate_id| must strictly dominate + // |merge_block_candidate_id| and |merge_block_candidate_id| must strictly + // postdominate |header_block_candidate_id|. + // - |header_block_candidate_id| can't be a header block of any construct. + // - |header_block_candidate_id|'s terminator must be an OpBranch. + // - |merge_block_candidate_id| can't be a merge block of any other construct. + // - Both |header_block_candidate_id| and |merge_block_candidate_id| must be + // inside the same construct if any. + static bool IsApplicableToBlockRange(opt::IRContext* ir_context, + uint32_t header_block_candidate_id, + uint32_t merge_block_candidate_id); + + std::unordered_set GetFreshIds() const override; + + private: + protobufs::TransformationWrapRegionInSelection message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp new file mode 100644 index 00000000..3b1543dd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_vector_synonym.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message) + : message_(std::move(message)) {} + +TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( + uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2, + uint32_t fresh_id, uint32_t pos) { + message_.set_instruction_id(instruction_id); + message_.set_vector_operand1(vector_operand1); + message_.set_vector_operand2(vector_operand2); + message_.set_fresh_id(fresh_id); + message_.set_scalar_position(pos); +} + +bool TransformationWrapVectorSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + const opt::Instruction* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + + // |instruction_id| must refer to an existing instruction. + if (instruction == nullptr) { + return false; + } + + if (!IsInstructionSupported(ir_context, *instruction)) { + return false; + } + + // It must be possible to make a synonym of the result id of the scalar + // operation + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, + *instruction)) { + return false; + } + + // |vector_operand1| and |vector_operand2| must exist. + auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); + auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2()); + + if (vec1 == nullptr || vec2 == nullptr) { + return false; + } + + // The 2 vectors must have compatible vector types. + auto vec1_type_id = vec1->type_id(); + auto vec2_type_id = vec2->type_id(); + + for (auto operand_index : {0, 1}) { + if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(), + operand_index, vec1_type_id, + vec2_type_id)) { + return false; + } + } + + auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id); + if (vec1_type->opcode() != spv::Op::OpTypeVector) { + return false; + } + + // A suitable vector for the result type of the new vector instruction must + // exist in the module. This is a vector of the right length, whose element + // type matches the result type of the scalar instruction. + uint32_t vector_size = vec1_type->GetSingleWordInOperand(1); + if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(), + vector_size)) { + return false; + } + + // |scalar_position| needs to be a non-negative integer less than the vector + // length. + // OpTypeVector instruction has the component count at index 2. + if (message_.scalar_position() >= ir_context->get_def_use_mgr() + ->GetDef(vec1_type_id) + ->GetSingleWordInOperand(1)) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand1(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) { + return false; + } + + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(message_.vector_operand2(), + {message_.scalar_position()}), + MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) { + return false; + } + + return true; +} + +void TransformationWrapVectorSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Create an instruction descriptor for the original instruction. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); + auto destination_block = ir_context->get_instr_block(instruction); + + // Populate input operand list with two vectors for vector operation. + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}}); + + // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 + // %result_id2. + auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef( + fuzzerutil::GetTypeId(ir_context, message_.vector_operand1())); + uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1); + auto vec_type_id = fuzzerutil::MaybeGetVectorType( + ir_context, instruction->type_id(), vector_size); + auto new_instruction = MakeUnique( + ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), + std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + instruction->InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); + + // Add |fresh_id| to id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add synonyms between |fresh_id| and |instruction_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}), + MakeDataDescriptor(message_.instruction_id(), {})); +} + +protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_vector_synonym() = message_; + return result; +} + +std::unordered_set TransformationWrapVectorSynonym::GetFreshIds() + const { + return std::unordered_set{message_.fresh_id()}; +} + +bool TransformationWrapVectorSynonym::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + if (!instruction.result_id() || !instruction.type_id()) { + return false; + } + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction.type_id()); + + if ((type_instruction->opcode() != spv::Op::OpTypeInt && + type_instruction->opcode() != spv::Op::OpTypeFloat)) { + return false; + } + + switch (instruction.opcode()) { + case spv::Op::OpIAdd: + case spv::Op::OpISub: + case spv::Op::OpIMul: + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + return true; + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h new file mode 100644 index 00000000..94437fe5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/transformation_wrap_vector_synonym.h @@ -0,0 +1,80 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapVectorSynonym : public Transformation { + public: + explicit TransformationWrapVectorSynonym( + protobufs::TransformationWrapVectorSynonym message); + + TransformationWrapVectorSynonym(uint32_t instruction_id, + uint32_t vector_operand1, + uint32_t vector_operand2, uint32_t fresh_id, + uint32_t pos); + // - |instruction_id| must be the id of a supported arithmetic operation + // and must be relevant. + // - |vector_operand1| and |vector_operand2| represents the result ids of the + // two vector operands. + // - |fresh_id| is an unused id that will be used as a result id of the + // created instruction. + // - |vector_operand1| and |vector_operand2| must have compatible vector types + // that are supported by this transformation. + // - |pos| is an index of the operands of |instruction_id| in the + // |vector_operand1| and |vector_operand2|. It must be less than the size + // of those vector operands. + // - A vector type with the same width as the types of the vector operands, + // and element type matching the type of |instruction_id|, must exist in the + // module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new instruction before the |instruction_id| with |fresh_id| + // result id and |instruction_id|'s opcode. The added instruction has + // two operands: |vector_operand1| and |vector_operand2| and its type + // id is equal to the type ids of those operands. A new fact is added + // to the fact manager specifying that |fresh_id[pos]| is synonymous + // to |instruction_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + // Checks whether the instruction given is supported by the transformation. + // A valid instruction must: + // - has both result id and type id. + // - is a supported scalar operation instruction. + // - has a supported type that is either int or float. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationWrapVectorSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp b/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp new file mode 100644 index 00000000..55e84de8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +#include + +namespace spvtools { +namespace fuzz { + +protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( + uint32_t descriptor_set, uint32_t binding, + std::vector&& indices) { + protobufs::UniformBufferElementDescriptor result; + result.set_descriptor_set(descriptor_set); + result.set_binding(binding); + for (auto index : indices) { + result.add_index(index); + } + return result; +} + +bool UniformBufferElementDescriptorEquals::operator()( + const protobufs::UniformBufferElementDescriptor* first, + const protobufs::UniformBufferElementDescriptor* second) const { + return first->descriptor_set() == second->descriptor_set() && + first->binding() == second->binding() && + first->index().size() == second->index().size() && + std::equal(first->index().begin(), first->index().end(), + second->index().begin()); +} + +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique) { + opt::Instruction* result = nullptr; + + for (auto& inst : context->types_values()) { + // Consider all global variables with uniform storage class. + if (inst.opcode() != spv::Op::OpVariable) { + continue; + } + if (spv::StorageClass(inst.GetSingleWordInOperand(0)) != + spv::StorageClass::Uniform) { + continue; + } + + // Determine whether the variable is decorated with a descriptor set + // matching that in |uniform_buffer_element|. + bool descriptor_set_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), uint32_t(spv::Decoration::DescriptorSet), + [&descriptor_set_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kDescriptorSetOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand( + kDescriptorSetOperandIndex) == + uniform_buffer_element_descriptor.descriptor_set()) { + descriptor_set_matches = true; + } + }); + if (!descriptor_set_matches) { + // Descriptor set does not match. + continue; + } + + // Determine whether the variable is decorated with a binding matching that + // in |uniform_buffer_element|. + bool binding_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), uint32_t(spv::Decoration::Binding), + [&binding_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kBindingOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand(kBindingOperandIndex) == + uniform_buffer_element_descriptor.binding()) { + binding_matches = true; + } + }); + if (!binding_matches) { + // Binding does not match. + continue; + } + + // This instruction is a uniform variable with the right descriptor set and + // binding. + if (!check_unique) { + // If we aren't checking uniqueness, return it. + return &inst; + } + + if (result) { + // More than one uniform variable is decorated with the given descriptor + // set and binding. This means the fact is ambiguous. + return nullptr; + } + result = &inst; + } + + // We get here either if no match was found, or if |check_unique| holds and + // exactly one match was found. + assert(result == nullptr || check_unique); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h b/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h new file mode 100644 index 00000000..f5d7320a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h @@ -0,0 +1,52 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ +#define SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Factory method to create a uniform buffer element descriptor message from +// descriptor set and binding ids and a list of indices. +protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( + uint32_t descriptor_set, uint32_t binding, std::vector&& indices); + +// Equality function for uniform buffer element descriptors. +struct UniformBufferElementDescriptorEquals { + bool operator()( + const protobufs::UniformBufferElementDescriptor* first, + const protobufs::UniformBufferElementDescriptor* second) const; +}; + +// Returns a pointer to an OpVariable in |context| that is decorated with the +// descriptor set and binding associated with |uniform_buffer_element|. Returns +// nullptr if no such variable exists. If multiple such variables exist, a +// pointer to an arbitrary one of the associated instructions is returned if +// |check_unique| is false, and nullptr is returned if |check_unique| is true. +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/instruction.h b/engine/engine/3rdlibs/spirv-tools/source/instruction.h new file mode 100644 index 00000000..2acbb572 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/instruction.h @@ -0,0 +1,49 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_INSTRUCTION_H_ +#define SOURCE_INSTRUCTION_H_ + +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.h" + +// Describes an instruction. +struct spv_instruction_t { + // Normally, both opcode and extInstType contain valid data. + // However, when the assembler parses ! as the first word in + // an instruction and opcode and extInstType are invalid. + spv::Op opcode; + spv_ext_inst_type_t extInstType; + + // The Id of the result type, if this instruction has one. Zero otherwise. + uint32_t resultTypeId; + + // The instruction, as a sequence of 32-bit words. + // For a regular instruction the opcode and word count are combined + // in words[0], as described in the SPIR-V spec. + // Otherwise, the first token was !, and that number appears + // in words[0]. Subsequent elements are the result of parsing + // tokens in the alternate parsing mode as described in syntax.md. + std::vector words; +}; + +// Appends a word to an instruction, without checking for overflow. +inline void spvInstructionAddWord(spv_instruction_t* inst, uint32_t value) { + inst->words.push_back(value); +} + +#endif // SOURCE_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/latest_version_glsl_std_450_header.h b/engine/engine/3rdlibs/spirv-tools/source/latest_version_glsl_std_450_header.h new file mode 100644 index 00000000..bed1f250 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/latest_version_glsl_std_450_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ +#define SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ + +#include "spirv/unified1/GLSL.std.450.h" + +#endif // SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/latest_version_opencl_std_header.h b/engine/engine/3rdlibs/spirv-tools/source/latest_version_opencl_std_header.h new file mode 100644 index 00000000..90ff9c03 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/latest_version_opencl_std_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ +#define SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ + +#include "spirv/unified1/OpenCL.std.h" + +#endif // SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/latest_version_spirv_header.h b/engine/engine/3rdlibs/spirv-tools/source/latest_version_spirv_header.h new file mode 100644 index 00000000..f6ab5c84 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/latest_version_spirv_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ +#define SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ + +#include "spirv/unified1/spirv.hpp11" + +#endif // SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/libspirv.cpp b/engine/engine/3rdlibs/spirv-tools/source/libspirv.cpp new file mode 100644 index 00000000..83e8629b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/libspirv.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.hpp" + +#include +#include +#include +#include +#include + +#include "source/table.h" + +namespace spvtools { + +Context::Context(spv_target_env env) : context_(spvContextCreate(env)) {} + +Context::Context(Context&& other) : context_(other.context_) { + other.context_ = nullptr; +} + +Context& Context::operator=(Context&& other) { + spvContextDestroy(context_); + context_ = other.context_; + other.context_ = nullptr; + + return *this; +} + +Context::~Context() { spvContextDestroy(context_); } + +void Context::SetMessageConsumer(MessageConsumer consumer) { + SetContextMessageConsumer(context_, std::move(consumer)); +} + +spv_context& Context::CContext() { return context_; } + +const spv_context& Context::CContext() const { return context_; } + +// Structs for holding the data members for SpvTools. +struct SpirvTools::Impl { + explicit Impl(spv_target_env env) : context(spvContextCreate(env)) { + // The default consumer in spv_context_t is a null consumer, which provides + // equivalent functionality (from the user's perspective) as a real consumer + // does nothing. + } + ~Impl() { spvContextDestroy(context); } + + spv_context context; // C interface context object. +}; + +SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) { + assert(env != SPV_ENV_WEBGPU_0); +} + +SpirvTools::~SpirvTools() {} + +void SpirvTools::SetMessageConsumer(MessageConsumer consumer) { + SetContextMessageConsumer(impl_->context, std::move(consumer)); +} + +bool SpirvTools::Assemble(const std::string& text, + std::vector* binary, + uint32_t options) const { + return Assemble(text.data(), text.size(), binary, options); +} + +bool SpirvTools::Assemble(const char* text, const size_t text_size, + std::vector* binary, + uint32_t options) const { + spv_binary spvbinary = nullptr; + spv_result_t status = spvTextToBinaryWithOptions( + impl_->context, text, text_size, options, &spvbinary, nullptr); + if (status == SPV_SUCCESS) { + binary->assign(spvbinary->code, spvbinary->code + spvbinary->wordCount); + } + spvBinaryDestroy(spvbinary); + return status == SPV_SUCCESS; +} + +bool SpirvTools::Disassemble(const std::vector& binary, + std::string* text, uint32_t options) const { + return Disassemble(binary.data(), binary.size(), text, options); +} + +bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size, + std::string* text, uint32_t options) const { + spv_text spvtext = nullptr; + spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size, + options, &spvtext, nullptr); + if (status == SPV_SUCCESS && + (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) { + assert(spvtext); + text->assign(spvtext->str, spvtext->str + spvtext->length); + } + spvTextDestroy(spvtext); + return status == SPV_SUCCESS; +} + +struct CxxParserContext { + const HeaderParser& header_parser; + const InstructionParser& instruction_parser; +}; + +bool SpirvTools::Parse(const std::vector& binary, + const HeaderParser& header_parser, + const InstructionParser& instruction_parser, + spv_diagnostic* diagnostic) { + CxxParserContext parser_context = {header_parser, instruction_parser}; + + spv_parsed_header_fn_t header_fn_wrapper = + [](void* user_data, spv_endianness_t endianness, uint32_t magic, + uint32_t version, uint32_t generator, uint32_t id_bound, + uint32_t reserved) { + CxxParserContext* ctx = reinterpret_cast(user_data); + spv_parsed_header_t header = {magic, version, generator, id_bound, + reserved}; + + return ctx->header_parser(endianness, header); + }; + + spv_parsed_instruction_fn_t instruction_fn_wrapper = + [](void* user_data, const spv_parsed_instruction_t* instruction) { + CxxParserContext* ctx = reinterpret_cast(user_data); + return ctx->instruction_parser(*instruction); + }; + + spv_result_t status = spvBinaryParse( + impl_->context, &parser_context, binary.data(), binary.size(), + header_fn_wrapper, instruction_fn_wrapper, diagnostic); + return status == SPV_SUCCESS; +} + +bool SpirvTools::Validate(const std::vector& binary) const { + return Validate(binary.data(), binary.size()); +} + +bool SpirvTools::Validate(const uint32_t* binary, + const size_t binary_size) const { + return spvValidateBinary(impl_->context, binary, binary_size, nullptr) == + SPV_SUCCESS; +} + +bool SpirvTools::Validate(const uint32_t* binary, const size_t binary_size, + spv_validator_options options) const { + spv_const_binary_t the_binary{binary, binary_size}; + spv_diagnostic diagnostic = nullptr; + bool valid = spvValidateWithOptions(impl_->context, options, &the_binary, + &diagnostic) == SPV_SUCCESS; + if (!valid && impl_->context->consumer) { + impl_->context->consumer.operator()( + SPV_MSG_ERROR, nullptr, diagnostic->position, diagnostic->error); + } + spvDiagnosticDestroy(diagnostic); + return valid; +} + +bool SpirvTools::IsValid() const { return impl_->context != nullptr; } + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/link/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/link/CMakeLists.txt new file mode 100644 index 00000000..a35b9a58 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/link/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) 2017 Pierre Moreau + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_library(SPIRV-Tools-link ${SPIRV_TOOLS_LIBRARY_TYPE} + linker.cpp +) + +spvtools_default_compile_options(SPIRV-Tools-link) +target_include_directories(SPIRV-Tools-link + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the IR functionalities from the optimizer +target_link_libraries(SPIRV-Tools-link + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-link PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-link) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-link EXPORT SPIRV-Tools-linkTargets) + export(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-link PACKAGE_DIR) + install(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-link) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-linkConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/engine/engine/3rdlibs/spirv-tools/source/link/linker.cpp b/engine/engine/3rdlibs/spirv-tools/source/link/linker.cpp new file mode 100644 index 00000000..de305255 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/link/linker.cpp @@ -0,0 +1,946 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/linker.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/opt/build_module.h" +#include "source/opt/compact_ids_pass.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_loader.h" +#include "source/opt/pass_manager.h" +#include "source/opt/remove_duplicates_pass.h" +#include "source/opt/remove_unused_interface_variables_pass.h" +#include "source/opt/type_manager.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace { + +using opt::Instruction; +using opt::InstructionBuilder; +using opt::IRContext; +using opt::Module; +using opt::PassManager; +using opt::RemoveDuplicatesPass; +using opt::analysis::DecorationManager; +using opt::analysis::DefUseManager; +using opt::analysis::Function; +using opt::analysis::Type; +using opt::analysis::TypeManager; + +// Stores various information about an imported or exported symbol. +struct LinkageSymbolInfo { + spv::Id id; // ID of the symbol + spv::Id type_id; // ID of the type of the symbol + std::string name; // unique name defining the symbol and used for matching + // imports and exports together + std::vector parameter_ids; // ID of the parameters of the symbol, if + // it is a function +}; +struct LinkageEntry { + LinkageSymbolInfo imported_symbol; + LinkageSymbolInfo exported_symbol; + + LinkageEntry(const LinkageSymbolInfo& import_info, + const LinkageSymbolInfo& export_info) + : imported_symbol(import_info), exported_symbol(export_info) {} +}; +using LinkageTable = std::vector; + +// Shifts the IDs used in each binary of |modules| so that they occupy a +// disjoint range from the other binaries, and compute the new ID bound which +// is returned in |max_id_bound|. +// +// Both |modules| and |max_id_bound| should not be null, and |modules| should +// not be empty either. Furthermore |modules| should not contain any null +// pointers. +spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, + std::vector* modules, + uint32_t* max_id_bound); + +// Generates the header for the linked module and returns it in |header|. +// +// |header| should not be null, |modules| should not be empty and pointers +// should be non-null. |max_id_bound| should be strictly greater than 0. +spv_result_t GenerateHeader(const MessageConsumer& consumer, + const std::vector& modules, + uint32_t max_id_bound, opt::ModuleHeader* header, + const LinkerOptions& options); + +// Merge all the modules from |in_modules| into a single module owned by +// |linked_context|. +// +// |linked_context| should not be null. +spv_result_t MergeModules(const MessageConsumer& consumer, + const std::vector& in_modules, + const AssemblyGrammar& grammar, + IRContext* linked_context); + +// Compute all pairs of import and export and return it in |linkings_to_do|. +// +// |linkings_to_do should not be null. Built-in symbols will be ignored. +// +// TODO(pierremoreau): Linkage attributes applied by a group decoration are +// currently not handled. (You could have a group being +// applied to a single ID.) +// TODO(pierremoreau): What should be the proper behaviour with built-in +// symbols? +spv_result_t GetImportExportPairs(const MessageConsumer& consumer, + const opt::IRContext& linked_context, + const DefUseManager& def_use_manager, + const DecorationManager& decoration_manager, + bool allow_partial_linkage, + LinkageTable* linkings_to_do); + +// Checks that for each pair of import and export, the import and export have +// the same type as well as the same decorations. +// +// TODO(pierremoreau): Decorations on functions parameters are currently not +// checked. +spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, + const LinkageTable& linkings_to_do, + bool allow_ptr_type_mismatch, + opt::IRContext* context); + +// Remove linkage specific instructions, such as prototypes of imported +// functions, declarations of imported variables, import (and export if +// necessary) linkage attributes. +// +// |linked_context| and |decoration_manager| should not be null, and the +// 'RemoveDuplicatePass' should be run first. +// +// TODO(pierremoreau): Linkage attributes applied by a group decoration are +// currently not handled. (You could have a group being +// applied to a single ID.) +spv_result_t RemoveLinkageSpecificInstructions( + const MessageConsumer& consumer, const LinkerOptions& options, + const LinkageTable& linkings_to_do, DecorationManager* decoration_manager, + opt::IRContext* linked_context); + +// Verify that the unique ids of each instruction in |linked_context| (i.e. the +// merged module) are truly unique. Does not check the validity of other ids +spv_result_t VerifyIds(const MessageConsumer& consumer, + opt::IRContext* linked_context); + +// Verify that the universal limits are not crossed, and warn the user +// otherwise. +// +// TODO(pierremoreau): +// - Verify against the limits of the environment (e.g. Vulkan limits if +// consuming vulkan1.x) +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context); + +spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, + std::vector* modules, + uint32_t* max_id_bound) { + spv_position_t position = {}; + + if (modules == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of ShiftIdsInModules should not be null."; + if (modules->empty()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of ShiftIdsInModules should not be empty."; + if (max_id_bound == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|max_id_bound| of ShiftIdsInModules should not be null."; + + const size_t id_bound = + std::accumulate(modules->begin(), modules->end(), static_cast(1), + [](const size_t& accumulation, opt::Module* module) { + return accumulation + module->IdBound() - 1u; + }); + if (id_bound > std::numeric_limits::max()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "Too many IDs (" << id_bound + << "): combining all modules would overflow the 32-bit word of the " + "SPIR-V header."; + + *max_id_bound = static_cast(id_bound); + + uint32_t id_offset = modules->front()->IdBound() - 1u; + for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); + ++module_iter) { + Module* module = *module_iter; + module->ForEachInst([&id_offset](Instruction* insn) { + insn->ForEachId([&id_offset](uint32_t* id) { *id += id_offset; }); + }); + id_offset += module->IdBound() - 1u; + + // Invalidate the DefUseManager + module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); + } + + return SPV_SUCCESS; +} + +spv_result_t GenerateHeader(const MessageConsumer& consumer, + const std::vector& modules, + uint32_t max_id_bound, opt::ModuleHeader* header, + const LinkerOptions& options) { + spv_position_t position = {}; + + if (modules.empty()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of GenerateHeader should not be empty."; + if (max_id_bound == 0u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|max_id_bound| of GenerateHeader should not be null."; + + uint32_t linked_version = modules.front()->version(); + for (std::size_t i = 1; i < modules.size(); ++i) { + const uint32_t module_version = modules[i]->version(); + if (options.GetUseHighestVersion()) { + linked_version = std::max(linked_version, module_version); + } else if (module_version != linked_version) { + return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting SPIR-V versions: " + << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(linked_version) + << " (input modules 1 through " << i << ") vs " + << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(module_version) + << " (input module " << (i + 1) << ")."; + } + } + + header->magic_number = spv::MagicNumber; + header->version = linked_version; + header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); + header->bound = max_id_bound; + header->schema = 0u; + + return SPV_SUCCESS; +} + +spv_result_t MergeModules(const MessageConsumer& consumer, + const std::vector& input_modules, + const AssemblyGrammar& grammar, + IRContext* linked_context) { + spv_position_t position = {}; + + if (linked_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linked_module| of MergeModules should not be null."; + Module* linked_module = linked_context->module(); + + if (input_modules.empty()) return SPV_SUCCESS; + + for (const auto& module : input_modules) + for (const auto& inst : module->capabilities()) + linked_module->AddCapability( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->extensions()) + linked_module->AddExtension( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->ext_inst_imports()) + linked_module->AddExtInstImport( + std::unique_ptr(inst.Clone(linked_context))); + + const Instruction* linked_memory_model_inst = + input_modules.front()->GetMemoryModel(); + if (linked_memory_model_inst == nullptr) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module 1 is lacking an OpMemoryModel instruction."; + } + const uint32_t linked_addressing_model = + linked_memory_model_inst->GetSingleWordOperand(0u); + const uint32_t linked_memory_model = + linked_memory_model_inst->GetSingleWordOperand(1u); + + for (std::size_t i = 1; i < input_modules.size(); ++i) { + const Module* module = input_modules[i]; + const Instruction* memory_model_inst = module->GetMemoryModel(); + if (memory_model_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module " << (i + 1) + << " is lacking an OpMemoryModel instruction."; + + const uint32_t module_addressing_model = + memory_model_inst->GetSingleWordOperand(0u); + if (module_addressing_model != linked_addressing_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + linked_addressing_model, &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + module_addressing_model, &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting addressing models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; + } + + const uint32_t module_memory_model = + memory_model_inst->GetSingleWordOperand(1u); + if (module_memory_model != linked_memory_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model, + &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model, + &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting memory models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; + } + } + linked_module->SetMemoryModel(std::unique_ptr( + linked_memory_model_inst->Clone(linked_context))); + + std::vector> entry_points; + for (const auto& module : input_modules) + for (const auto& inst : module->entry_points()) { + const uint32_t model = inst.GetSingleWordInOperand(0); + const std::string name = inst.GetInOperand(2).AsString(); + const auto i = std::find_if( + entry_points.begin(), entry_points.end(), + [model, name](const std::pair& v) { + return v.first == model && v.second == name; + }); + if (i != entry_points.end()) { + spv_operand_desc desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "The entry point \"" << name << "\", with execution model " + << desc->name << ", was already defined."; + } + linked_module->AddEntryPoint( + std::unique_ptr(inst.Clone(linked_context))); + entry_points.emplace_back(model, name); + } + + for (const auto& module : input_modules) + for (const auto& inst : module->execution_modes()) + linked_module->AddExecutionMode( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs1()) + linked_module->AddDebug1Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs2()) + linked_module->AddDebug2Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs3()) + linked_module->AddDebug3Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->ext_inst_debuginfo()) + linked_module->AddExtInstDebugInfo( + std::unique_ptr(inst.Clone(linked_context))); + + // If the generated module uses SPIR-V 1.1 or higher, add an + // OpModuleProcessed instruction about the linking step. + if (linked_module->version() >= SPV_SPIRV_VERSION_WORD(1, 1)) { + const std::string processed_string("Linked by SPIR-V Tools Linker"); + std::vector processed_words = + spvtools::utils::MakeVector(processed_string); + linked_module->AddDebug3Inst(std::unique_ptr( + new Instruction(linked_context, spv::Op::OpModuleProcessed, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}}))); + } + + for (const auto& module : input_modules) + for (const auto& inst : module->annotations()) + linked_module->AddAnnotationInst( + std::unique_ptr(inst.Clone(linked_context))); + + // TODO(pierremoreau): Since the modules have not been validate, should we + // expect spv::StorageClass::Function variables outside + // functions? + for (const auto& module : input_modules) { + for (const auto& inst : module->types_values()) { + linked_module->AddType( + std::unique_ptr(inst.Clone(linked_context))); + } + } + + // Process functions and their basic blocks + for (const auto& module : input_modules) { + for (const auto& func : *module) { + std::unique_ptr cloned_func(func.Clone(linked_context)); + linked_module->AddFunction(std::move(cloned_func)); + } + } + + return SPV_SUCCESS; +} + +spv_result_t GetImportExportPairs(const MessageConsumer& consumer, + const opt::IRContext& linked_context, + const DefUseManager& def_use_manager, + const DecorationManager& decoration_manager, + bool allow_partial_linkage, + LinkageTable* linkings_to_do) { + spv_position_t position = {}; + + if (linkings_to_do == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linkings_to_do| of GetImportExportPairs should not be empty."; + + std::vector imports; + std::unordered_map> exports; + std::unordered_map linkonce; + + // Figure out the imports and exports + for (const auto& decoration : linked_context.annotations()) { + if (decoration.opcode() != spv::Op::OpDecorate || + spv::Decoration(decoration.GetSingleWordInOperand(1u)) != + spv::Decoration::LinkageAttributes) + continue; + + const spv::Id id = decoration.GetSingleWordInOperand(0u); + // Ignore if the targeted symbol is a built-in + bool is_built_in = false; + for (const auto& id_decoration : + decoration_manager.GetDecorationsFor(id, false)) { + if (spv::Decoration(id_decoration->GetSingleWordInOperand(1u)) == + spv::Decoration::BuiltIn) { + is_built_in = true; + break; + } + } + if (is_built_in) { + continue; + } + + const uint32_t type = decoration.GetSingleWordInOperand(3u); + + LinkageSymbolInfo symbol_info; + symbol_info.name = decoration.GetInOperand(2u).AsString(); + symbol_info.id = id; + symbol_info.type_id = 0u; + + // Retrieve the type of the current symbol. This information will be used + // when checking that the imported and exported symbols have the same + // types. + const Instruction* def_inst = def_use_manager.GetDef(id); + if (def_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "ID " << id << " is never defined:\n"; + + if (def_inst->opcode() == spv::Op::OpVariable) { + symbol_info.type_id = def_inst->type_id(); + } else if (def_inst->opcode() == spv::Op::OpFunction) { + symbol_info.type_id = def_inst->GetSingleWordInOperand(1u); + + // range-based for loop calls begin()/end(), but never cbegin()/cend(), + // which will not work here. + for (auto func_iter = linked_context.module()->cbegin(); + func_iter != linked_context.module()->cend(); ++func_iter) { + if (func_iter->result_id() != id) continue; + func_iter->ForEachParam([&symbol_info](const Instruction* inst) { + symbol_info.parameter_ids.push_back(inst->result_id()); + }); + } + } else { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Only global variables and functions can be decorated using" + << " LinkageAttributes; " << id << " is neither of them.\n"; + } + + if (spv::LinkageType(type) == spv::LinkageType::Import) { + imports.push_back(symbol_info); + } else if (spv::LinkageType(type) == spv::LinkageType::Export) { + exports[symbol_info.name].push_back(symbol_info); + } else if (spv::LinkageType(type) == spv::LinkageType::LinkOnceODR) { + if (linkonce.find(symbol_info.name) == linkonce.end()) + linkonce[symbol_info.name] = symbol_info; + } + } + + for (const auto& possible_export : linkonce) { + if (exports.find(possible_export.first) == exports.end()) + exports[possible_export.first].push_back(possible_export.second); + else + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Combination of Export and LinkOnceODR is not allowed, found " + "for \"" + << possible_export.second.name << "\"."; + } + + // Find the import/export pairs + for (const auto& import : imports) { + std::vector possible_exports; + const auto& exp = exports.find(import.name); + if (exp != exports.end()) possible_exports = exp->second; + if (possible_exports.empty() && !allow_partial_linkage) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Unresolved external reference to \"" << import.name << "\"."; + else if (possible_exports.size() > 1u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Too many external references, " << possible_exports.size() + << ", were found for \"" << import.name << "\"."; + + if (!possible_exports.empty()) + linkings_to_do->emplace_back(import, possible_exports.front()); + } + + return SPV_SUCCESS; +} + +spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, + const LinkageTable& linkings_to_do, + bool allow_ptr_type_mismatch, + opt::IRContext* context) { + spv_position_t position = {}; + + // Ensure the import and export types are the same. + const DecorationManager& decoration_manager = *context->get_decoration_mgr(); + const TypeManager& type_manager = *context->get_type_mgr(); + for (const auto& linking_entry : linkings_to_do) { + Type* imported_symbol_type = + type_manager.GetType(linking_entry.imported_symbol.type_id); + Type* exported_symbol_type = + type_manager.GetType(linking_entry.exported_symbol.type_id); + if (!(*imported_symbol_type == *exported_symbol_type)) { + Function* imported_symbol_type_func = imported_symbol_type->AsFunction(); + Function* exported_symbol_type_func = exported_symbol_type->AsFunction(); + + if (imported_symbol_type_func && exported_symbol_type_func) { + const auto& imported_params = imported_symbol_type_func->param_types(); + const auto& exported_params = exported_symbol_type_func->param_types(); + // allow_ptr_type_mismatch allows linking functions where the pointer + // type of arguments doesn't match. Everything else still needs to be + // equal. This is to workaround LLVM-17+ not having typed pointers and + // generated SPIR-Vs not knowing the actual pointer types in some cases. + if (allow_ptr_type_mismatch && + imported_params.size() == exported_params.size()) { + bool correct = true; + for (size_t i = 0; i < imported_params.size(); i++) { + const auto& imported_param = imported_params[i]; + const auto& exported_param = exported_params[i]; + + if (!imported_param->IsSame(exported_param) && + (imported_param->kind() != Type::kPointer || + exported_param->kind() != Type::kPointer)) { + correct = false; + break; + } + } + if (correct) continue; + } + } + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Type mismatch on symbol \"" + << linking_entry.imported_symbol.name + << "\" between imported variable/function %" + << linking_entry.imported_symbol.id + << " and exported variable/function %" + << linking_entry.exported_symbol.id << "."; + } + } + + // Ensure the import and export decorations are similar + for (const auto& linking_entry : linkings_to_do) { + if (!decoration_manager.HaveTheSameDecorations( + linking_entry.imported_symbol.id, linking_entry.exported_symbol.id)) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Decorations mismatch on symbol \"" + << linking_entry.imported_symbol.name + << "\" between imported variable/function %" + << linking_entry.imported_symbol.id + << " and exported variable/function %" + << linking_entry.exported_symbol.id << "."; + // TODO(pierremoreau): Decorations on function parameters should probably + // match, except for FuncParamAttr if I understand the + // spec correctly. + // TODO(pierremoreau): Decorations on the function return type should + // match, except for FuncParamAttr. + } + + return SPV_SUCCESS; +} + +spv_result_t RemoveLinkageSpecificInstructions( + const MessageConsumer& consumer, const LinkerOptions& options, + const LinkageTable& linkings_to_do, DecorationManager* decoration_manager, + opt::IRContext* linked_context) { + spv_position_t position = {}; + + if (decoration_manager == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|decoration_manager| of RemoveLinkageSpecificInstructions " + "should not be empty."; + if (linked_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linked_module| of RemoveLinkageSpecificInstructions should not " + "be empty."; + + // TODO(pierremoreau): Remove FuncParamAttr decorations of imported + // functions' return type. + + // Remove prototypes of imported functions + for (const auto& linking_entry : linkings_to_do) { + for (auto func_iter = linked_context->module()->begin(); + func_iter != linked_context->module()->end();) { + if (func_iter->result_id() == linking_entry.imported_symbol.id) + func_iter = func_iter.Erase(); + else + ++func_iter; + } + } + + // Remove declarations of imported variables + for (const auto& linking_entry : linkings_to_do) { + auto next = linked_context->types_values_begin(); + for (auto inst = next; inst != linked_context->types_values_end(); + inst = next) { + ++next; + if (inst->result_id() == linking_entry.imported_symbol.id) { + linked_context->KillInst(&*inst); + } + } + } + + // If partial linkage is allowed, we need an efficient way to check whether + // an imported ID had a corresponding export symbol. As uses of the imported + // symbol have already been replaced by the exported symbol, use the exported + // symbol ID. + // TODO(pierremoreau): This will not work if the decoration is applied + // through a group, but the linker does not support that + // either. + std::unordered_set imports; + if (options.GetAllowPartialLinkage()) { + imports.reserve(linkings_to_do.size()); + for (const auto& linking_entry : linkings_to_do) + imports.emplace(linking_entry.exported_symbol.id); + } + + // Remove import linkage attributes + auto next = linked_context->annotation_begin(); + for (auto inst = next; inst != linked_context->annotation_end(); + inst = next) { + ++next; + // If this is an import annotation: + // * if we do not allow partial linkage, remove all import annotations; + // * otherwise, remove the annotation only if there was a corresponding + // export. + if (inst->opcode() == spv::Op::OpDecorate && + spv::Decoration(inst->GetSingleWordOperand(1u)) == + spv::Decoration::LinkageAttributes && + spv::LinkageType(inst->GetSingleWordOperand(3u)) == + spv::LinkageType::Import && + (!options.GetAllowPartialLinkage() || + imports.find(inst->GetSingleWordOperand(0u)) != imports.end())) { + linked_context->KillInst(&*inst); + } + } + + // Remove export linkage attributes if making an executable + if (!options.GetCreateLibrary()) { + next = linked_context->annotation_begin(); + for (auto inst = next; inst != linked_context->annotation_end(); + inst = next) { + ++next; + if (inst->opcode() == spv::Op::OpDecorate && + spv::Decoration(inst->GetSingleWordOperand(1u)) == + spv::Decoration::LinkageAttributes && + (spv::LinkageType(inst->GetSingleWordOperand(3u)) == + spv::LinkageType::Export || + spv::LinkageType(inst->GetSingleWordOperand(3u)) == + spv::LinkageType::LinkOnceODR)) { + linked_context->KillInst(&*inst); + } + } + } + + // Remove Linkage capability if making an executable and partial linkage is + // not allowed + if (!options.GetCreateLibrary() && !options.GetAllowPartialLinkage()) { + for (auto& inst : linked_context->capabilities()) + if (spv::Capability(inst.GetSingleWordInOperand(0u)) == + spv::Capability::Linkage) { + linked_context->KillInst(&inst); + // The RemoveDuplicatesPass did remove duplicated capabilities, so we + // now there aren’t more spv::Capability::Linkage further down. + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t VerifyIds(const MessageConsumer& consumer, + opt::IRContext* linked_context) { + std::unordered_set ids; + bool ok = true; + linked_context->module()->ForEachInst( + [&ids, &ok](const opt::Instruction* inst) { + ok &= ids.insert(inst->unique_id()).second; + }); + + if (!ok) { + consumer(SPV_MSG_INTERNAL_ERROR, "", {}, "Non-unique id in merged module"); + return SPV_ERROR_INVALID_ID; + } + + return SPV_SUCCESS; +} + +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context) { + spv_position_t position = {}; + + const uint32_t max_id_bound = linked_context.module()->id_bound(); + if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND) + DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING) + << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1) + << ", was exceeded:" + << " " << max_id_bound << " is the current ID bound.\n" + << "The resulting module might not be supported by all " + "implementations."; + + size_t num_global_values = 0u; + for (const auto& inst : linked_context.module()->types_values()) { + num_global_values += inst.opcode() == spv::Op::OpVariable; + } + if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX) + DiagnosticStream(position, consumer, "", SPV_WARNING) + << "The minimum limit of global values, " + << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;" + << " " << num_global_values << " global values were found.\n" + << "The resulting module might not be supported by all " + "implementations."; + + return SPV_SUCCESS; +} + +spv_result_t FixFunctionCallTypes(opt::IRContext& context, + const LinkageTable& linkings) { + auto mod = context.module(); + const auto type_manager = context.get_type_mgr(); + const auto def_use_mgr = context.get_def_use_mgr(); + + for (auto& func : *mod) { + func.ForEachInst([&](Instruction* inst) { + if (inst->opcode() != spv::Op::OpFunctionCall) return; + opt::Operand& target = inst->GetInOperand(0); + + // only fix calls to imported functions + auto linking = std::find_if( + linkings.begin(), linkings.end(), [&](const auto& entry) { + return entry.exported_symbol.id == target.AsId(); + }); + if (linking == linkings.end()) return; + + auto builder = InstructionBuilder(&context, inst); + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + auto exported_func_param = + def_use_mgr->GetDef(linking->exported_symbol.parameter_ids[i - 1]); + const Type* target_type = + type_manager->GetType(exported_func_param->type_id()); + if (target_type->kind() != Type::kPointer) continue; + + opt::Operand& arg = inst->GetInOperand(i); + const Type* param_type = + type_manager->GetType(def_use_mgr->GetDef(arg.AsId())->type_id()); + + // No need to cast if it already matches + if (*param_type == *target_type) continue; + + auto new_id = context.TakeNextId(); + + // cast to the expected pointer type + builder.AddInstruction(MakeUnique( + &context, spv::Op::OpBitcast, exported_func_param->type_id(), + new_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {arg.AsId()}}}))); + + inst->SetInOperand(i, {new_id}); + } + }); + } + context.InvalidateAnalyses(opt::IRContext::kAnalysisDefUse | + opt::IRContext::kAnalysisInstrToBlockMapping); + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t Link(const Context& context, + const std::vector>& binaries, + std::vector* linked_binary, + const LinkerOptions& options) { + std::vector binary_ptrs; + binary_ptrs.reserve(binaries.size()); + std::vector binary_sizes; + binary_sizes.reserve(binaries.size()); + + for (const auto& binary : binaries) { + binary_ptrs.push_back(binary.data()); + binary_sizes.push_back(binary.size()); + } + + return Link(context, binary_ptrs.data(), binary_sizes.data(), binaries.size(), + linked_binary, options); +} + +spv_result_t Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector* linked_binary, + const LinkerOptions& options) { + spv_position_t position = {}; + const spv_context& c_context = context.CContext(); + const MessageConsumer& consumer = c_context->consumer; + + linked_binary->clear(); + if (num_binaries == 0u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "No modules were given."; + + std::vector> ir_contexts; + std::vector modules; + modules.reserve(num_binaries); + for (size_t i = 0u; i < num_binaries; ++i) { + const uint32_t schema = binaries[i][4u]; + if (schema != 0u) { + position.index = 4u; + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Schema is non-zero for module " << i + 1 << "."; + } + + std::unique_ptr ir_context = BuildModule( + c_context->target_env, consumer, binaries[i], binary_sizes[i]); + if (ir_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Failed to build module " << i + 1 << " out of " << num_binaries + << "."; + modules.push_back(ir_context->module()); + ir_contexts.push_back(std::move(ir_context)); + } + + // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint + // range from the other binaries, and compute the new ID bound. + uint32_t max_id_bound = 0u; + spv_result_t res = ShiftIdsInModules(consumer, &modules, &max_id_bound); + if (res != SPV_SUCCESS) return res; + + // Phase 2: Generate the header + opt::ModuleHeader header; + res = GenerateHeader(consumer, modules, max_id_bound, &header, options); + if (res != SPV_SUCCESS) return res; + IRContext linked_context(c_context->target_env, consumer); + linked_context.module()->SetHeader(header); + + // Phase 3: Merge all the binaries into a single one. + AssemblyGrammar grammar(c_context); + res = MergeModules(consumer, modules, grammar, &linked_context); + if (res != SPV_SUCCESS) return res; + + if (options.GetVerifyIds()) { + res = VerifyIds(consumer, &linked_context); + if (res != SPV_SUCCESS) return res; + } + + // Phase 4: Remove duplicates + PassManager manager; + manager.SetMessageConsumer(consumer); + manager.AddPass(); + opt::Pass::Status pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 5: Find the import/export pairs + LinkageTable linkings_to_do; + res = GetImportExportPairs(consumer, linked_context, + *linked_context.get_def_use_mgr(), + *linked_context.get_decoration_mgr(), + options.GetAllowPartialLinkage(), &linkings_to_do); + if (res != SPV_SUCCESS) return res; + + // Phase 6: Ensure the import and export have the same types and decorations. + res = CheckImportExportCompatibility(consumer, linkings_to_do, + options.GetAllowPtrTypeMismatch(), + &linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 7: Remove all names and decorations of import variables/functions + for (const auto& linking_entry : linkings_to_do) { + linked_context.KillNamesAndDecorates(linking_entry.imported_symbol.id); + for (const auto parameter_id : + linking_entry.imported_symbol.parameter_ids) { + linked_context.KillNamesAndDecorates(parameter_id); + } + } + + // Phase 8: Rematch import variables/functions to export variables/functions + for (const auto& linking_entry : linkings_to_do) { + linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id, + linking_entry.exported_symbol.id); + } + + // Phase 9: Remove linkage specific instructions, such as import/export + // attributes, linkage capability, etc. if applicable + res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do, + linked_context.get_decoration_mgr(), + &linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 10: Optionally fix function call types + if (options.GetAllowPtrTypeMismatch()) { + res = FixFunctionCallTypes(linked_context, linkings_to_do); + if (res != SPV_SUCCESS) return res; + } + + // Phase 11: Compact the IDs used in the module + manager.AddPass(); + pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 12: Recompute EntryPoint variables + manager.AddPass(); + pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 13: Warn if SPIR-V limits were exceeded + res = VerifyLimits(consumer, linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 14: Output the module + linked_context.module()->ToBinary(linked_binary, true); + + return SPV_SUCCESS; +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/lint/CMakeLists.txt new file mode 100644 index 00000000..4704beb1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (c) 2021 Google LLC. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_LINT_SOURCES + divergence_analysis.h + lints.h + + linter.cpp + divergence_analysis.cpp + lint_divergent_derivatives.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib. + add_definitions(/MP4) +endif() + +add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-lint) +target_include_directories(SPIRV-Tools-lint + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-lint + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-lint + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-lint PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-lint) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-lint EXPORT SPIRV-Tools-lintTargets) + export(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-lint PACKAGE_DIR) + install(EXPORT SPIRV-Tools-lintTargets FILE SPIRV-Tools-lintTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-lint) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-lintConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.cpp b/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.cpp new file mode 100644 index 00000000..fe32e1ac --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.cpp @@ -0,0 +1,246 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/lint/divergence_analysis.h" + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace lint { + +void DivergenceAnalysis::EnqueueSuccessors(opt::Instruction* inst) { + // Enqueue control dependents of block, if applicable. + // There are two ways for a dependence source to be updated: + // 1. control -> control: source block is marked divergent. + // 2. data -> control: branch condition is marked divergent. + uint32_t block_id; + if (inst->IsBlockTerminator()) { + block_id = context().get_instr_block(inst)->id(); + } else if (inst->opcode() == spv::Op::OpLabel) { + block_id = inst->result_id(); + opt::BasicBlock* bb = context().cfg()->block(block_id); + // Only enqueue phi instructions, as other uses don't affect divergence. + bb->ForEachPhiInst([this](opt::Instruction* phi) { Enqueue(phi); }); + } else { + opt::ForwardDataFlowAnalysis::EnqueueUsers(inst); + return; + } + if (!cd_.HasBlock(block_id)) { + return; + } + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceTargets(block_id)) { + opt::Instruction* target_inst = + context().cfg()->block(dep.target_bb_id())->GetLabelInst(); + Enqueue(target_inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::Visit( + opt::Instruction* inst) { + if (inst->opcode() == spv::Op::OpLabel) { + return VisitBlock(inst->result_id()); + } else { + return VisitInstruction(inst); + } +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitBlock(uint32_t id) { + if (!cd_.HasBlock(id)) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + for (const spvtools::opt::ControlDependence& dep : + cd_.GetDependenceSources(id)) { + if (divergence_[dep.source_bb_id()] > cur_level) { + cur_level = divergence_[dep.source_bb_id()]; + divergence_source_[id] = dep.source_bb_id(); + } else if (dep.source_bb_id() != 0) { + uint32_t condition_id = dep.GetConditionID(*context().cfg()); + DivergenceLevel dep_level = divergence_[condition_id]; + // Check if we are along the chain of unconditional branches starting from + // the branch target. + if (follow_unconditional_branches_[dep.branch_target_bb_id()] != + follow_unconditional_branches_[dep.target_bb_id()]) { + // We must have reconverged in order to reach this block. + // Promote partially uniform to divergent. + if (dep_level == DivergenceLevel::kPartiallyUniform) { + dep_level = DivergenceLevel::kDivergent; + } + } + if (dep_level > cur_level) { + cur_level = dep_level; + divergence_source_[id] = condition_id; + divergence_dependence_source_[id] = dep.source_bb_id(); + } + } + } + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +opt::DataFlowAnalysis::VisitResult DivergenceAnalysis::VisitInstruction( + opt::Instruction* inst) { + if (inst->IsBlockTerminator()) { + // This is called only when the condition has changed, so return changed. + return VisitResult::kResultChanged; + } + if (!inst->HasResultId()) { + return VisitResult::kResultFixed; + } + uint32_t id = inst->result_id(); + DivergenceLevel& cur_level = divergence_[id]; + if (cur_level == DivergenceLevel::kDivergent) { + return opt::DataFlowAnalysis::VisitResult::kResultFixed; + } + DivergenceLevel orig = cur_level; + cur_level = ComputeInstructionDivergence(inst); + return cur_level > orig ? VisitResult::kResultChanged + : VisitResult::kResultFixed; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeInstructionDivergence(opt::Instruction* inst) { + // TODO(kuhar): Check to see if inst is decorated with Uniform or UniformId + // and use that to short circuit other checks. Uniform is for subgroups which + // would satisfy derivative groups too. UniformId takes a scope, so if it is + // subgroup or greater it could satisfy derivative group and + // Device/QueueFamily could satisfy fully uniform. + uint32_t id = inst->result_id(); + // Handle divergence roots. + if (inst->opcode() == spv::Op::OpFunctionParameter) { + divergence_source_[id] = 0; + return divergence_[id] = DivergenceLevel::kDivergent; + } else if (inst->IsLoad()) { + spvtools::opt::Instruction* var = inst->GetBaseAddress(); + if (var->opcode() != spv::Op::OpVariable) { + // Assume divergent. + divergence_source_[id] = 0; + return DivergenceLevel::kDivergent; + } + DivergenceLevel ret = ComputeVariableDivergence(var); + if (ret > DivergenceLevel::kUniform) { + divergence_source_[inst->result_id()] = 0; + } + return divergence_[id] = ret; + } + // Get the maximum divergence of the operands. + DivergenceLevel ret = DivergenceLevel::kUniform; + inst->ForEachInId([this, inst, &ret](const uint32_t* op) { + if (!op) return; + if (divergence_[*op] > ret) { + divergence_source_[inst->result_id()] = *op; + ret = divergence_[*op]; + } + }); + divergence_[inst->result_id()] = ret; + return ret; +} + +DivergenceAnalysis::DivergenceLevel +DivergenceAnalysis::ComputeVariableDivergence(opt::Instruction* var) { + uint32_t type_id = var->type_id(); + spvtools::opt::analysis::Pointer* type = + context().get_type_mgr()->GetType(type_id)->AsPointer(); + assert(type != nullptr); + uint32_t def_id = var->result_id(); + DivergenceLevel ret; + switch (type->storage_class()) { + case spv::StorageClass::Function: + case spv::StorageClass::Generic: + case spv::StorageClass::AtomicCounter: + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::Output: + case spv::StorageClass::Workgroup: + case spv::StorageClass::Image: // Image atomics probably aren't uniform. + case spv::StorageClass::Private: + ret = DivergenceLevel::kDivergent; + break; + case spv::StorageClass::Input: + ret = DivergenceLevel::kDivergent; + // If this variable has a Flat decoration, it is partially uniform. + // TODO(kuhar): Track access chain indices and also consider Flat members + // of a structure. + context().get_decoration_mgr()->WhileEachDecoration( + def_id, static_cast(spv::Decoration::Flat), + [&ret](const opt::Instruction&) { + ret = DivergenceLevel::kPartiallyUniform; + return false; + }); + break; + case spv::StorageClass::UniformConstant: + // May be a storage image which is also written to; mark those as + // divergent. + if (!var->IsVulkanStorageImage() || var->IsReadOnlyPointer()) { + ret = DivergenceLevel::kUniform; + } else { + ret = DivergenceLevel::kDivergent; + } + break; + case spv::StorageClass::Uniform: + case spv::StorageClass::PushConstant: + case spv::StorageClass::CrossWorkgroup: // Not for shaders; default + // uniform. + default: + ret = DivergenceLevel::kUniform; + break; + } + return ret; +} + +void DivergenceAnalysis::Setup(opt::Function* function) { + // TODO(kuhar): Run functions called by |function| so we can detect + // reconvergence caused by multiple returns. + cd_.ComputeControlDependenceGraph( + *context().cfg(), *context().GetPostDominatorAnalysis(function)); + context().cfg()->ForEachBlockInPostOrder( + function->entry().get(), [this](const opt::BasicBlock* bb) { + uint32_t id = bb->id(); + if (bb->terminator() == nullptr || + bb->terminator()->opcode() != spv::Op::OpBranch) { + follow_unconditional_branches_[id] = id; + } else { + uint32_t target_id = bb->terminator()->GetSingleWordInOperand(0); + // Target is guaranteed to have been visited before us in postorder. + follow_unconditional_branches_[id] = + follow_unconditional_branches_[target_id]; + } + }); +} + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level) { + switch (level) { + case DivergenceAnalysis::DivergenceLevel::kUniform: + return os << "uniform"; + case DivergenceAnalysis::DivergenceLevel::kPartiallyUniform: + return os << "partially uniform"; + case DivergenceAnalysis::DivergenceLevel::kDivergent: + return os << "divergent"; + default: + return os << ""; + } +} + +} // namespace lint +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.h b/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.h new file mode 100644 index 00000000..4d595ec2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/divergence_analysis.h @@ -0,0 +1,163 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ +#define SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/control_dependence.h" +#include "source/opt/dataflow.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace lint { + +// Computes the static divergence level for blocks (control flow) and values. +// +// A value is uniform if all threads that execute it are guaranteed to have the +// same value. Similarly, a value is partially uniform if this is true only +// within each derivative group. If neither apply, it is divergent. +// +// Control flow through a block is uniform if for any possible execution and +// point in time, all threads are executing it, or no threads are executing it. +// In particular, it is never possible for some threads to be inside the block +// and some threads not executing. +// TODO(kuhar): Clarify the difference between uniform, divergent, and +// partially-uniform execution in this analysis. +// +// Caveat: +// As we use control dependence to determine how divergence is propagated, this +// analysis can be overly permissive when the merge block for a conditional +// branch or switch is later than (strictly postdominates) the expected merge +// block, which is the immediate postdominator. However, this is not expected to +// be a problem in practice, given that SPIR-V is generally output by compilers +// and other automated tools, which would assign the earliest possible merge +// block, rather than written by hand. +// TODO(kuhar): Handle late merges. +class DivergenceAnalysis : public opt::ForwardDataFlowAnalysis { + public: + // The tightest (most uniform) level of divergence that can be determined + // statically for a value or control flow for a block. + // + // The values are ordered such that A > B means that A is potentially more + // divergent than B. + // TODO(kuhar): Rename |PartiallyUniform' to something less confusing. For + // example, the enum could be based on scopes. + enum class DivergenceLevel { + // The value or control flow is uniform across the entire invocation group. + kUniform = 0, + // The value or control flow is uniform across the derivative group, but not + // the invocation group. + kPartiallyUniform = 1, + // The value or control flow is not statically uniform. + kDivergent = 2, + }; + + DivergenceAnalysis(opt::IRContext& context) + : ForwardDataFlowAnalysis(context, LabelPosition::kLabelsAtEnd) {} + + // Returns the divergence level for the given value (non-label instructions), + // or control flow for the given block. + DivergenceLevel GetDivergenceLevel(uint32_t id) { + auto it = divergence_.find(id); + if (it == divergence_.end()) { + return DivergenceLevel::kUniform; + } + return it->second; + } + + // Returns the divergence source for the given id. The following types of + // divergence flows from A to B are possible: + // + // data -> data: A is used as an operand in the definition of B. + // data -> control: B is control-dependent on a branch with condition A. + // control -> data: B is a OpPhi instruction in which A is a block operand. + // control -> control: B is control-dependent on A. + uint32_t GetDivergenceSource(uint32_t id) { + auto it = divergence_source_.find(id); + if (it == divergence_source_.end()) { + return 0; + } + return it->second; + } + + // Returns the dependence source for the control dependence for the given id. + // This only exists for data -> control edges. + // + // In other words, if block 2 is dependent on block 1 due to value 3 (e.g. + // block 1 terminates with OpBranchConditional %3 %2 %4): + // * GetDivergenceSource(2) = 3 + // * GetDivergenceDependenceSource(2) = 1 + // + // Returns 0 if not applicable. + uint32_t GetDivergenceDependenceSource(uint32_t id) { + auto it = divergence_dependence_source_.find(id); + if (it == divergence_dependence_source_.end()) { + return 0; + } + return it->second; + } + + void InitializeWorklist(opt::Function* function, + bool is_first_iteration) override { + // Since |EnqueueSuccessors| is complete, we only need one pass. + if (is_first_iteration) { + Setup(function); + opt::ForwardDataFlowAnalysis::InitializeWorklist(function, true); + } + } + + void EnqueueSuccessors(opt::Instruction* inst) override; + + VisitResult Visit(opt::Instruction* inst) override; + + private: + VisitResult VisitBlock(uint32_t id); + VisitResult VisitInstruction(opt::Instruction* inst); + + // Computes the divergence level for the result of the given instruction + // based on the current state of the analysis. This is always an + // underapproximation, which will be improved as the analysis proceeds. + DivergenceLevel ComputeInstructionDivergence(opt::Instruction* inst); + + // Computes the divergence level for a variable, which is used for loads. + DivergenceLevel ComputeVariableDivergence(opt::Instruction* var); + + // Initializes data structures for performing dataflow on the given function. + void Setup(opt::Function* function); + + std::unordered_map divergence_; + std::unordered_map divergence_source_; + std::unordered_map divergence_dependence_source_; + + // Stores the result of following unconditional branches starting from the + // given block. This is used to detect when reconvergence needs to be + // accounted for. + std::unordered_map follow_unconditional_branches_; + + opt::ControlDependenceAnalysis cd_; +}; + +std::ostream& operator<<(std::ostream& os, + DivergenceAnalysis::DivergenceLevel level); + +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_DIVERGENCE_ANALYSIS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/lint_divergent_derivatives.cpp b/engine/engine/3rdlibs/spirv-tools/source/lint/lint_divergent_derivatives.cpp new file mode 100644 index 00000000..82d5ac63 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/lint_divergent_derivatives.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/lint/divergence_analysis.h" +#include "source/lint/lints.h" +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/control_dependence.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace lint { +namespace lints { +namespace { +// Returns the %name[id], where `name` is the first name associated with the +// given id, or just %id if one is not found. +std::string GetFriendlyName(opt::IRContext* context, uint32_t id) { + auto names = context->GetNames(id); + std::stringstream ss; + ss << "%"; + if (names.empty()) { + ss << id; + } else { + opt::Instruction* inst_name = names.begin()->second; + if (inst_name->opcode() == spv::Op::OpName) { + ss << names.begin()->second->GetInOperand(0).AsString(); + ss << "[" << id << "]"; + } else { + ss << id; + } + } + return ss.str(); +} + +bool InstructionHasDerivative(const opt::Instruction& inst) { + static const spv::Op derivative_opcodes[] = { + // Implicit derivatives. + spv::Op::OpImageSampleImplicitLod, + spv::Op::OpImageSampleDrefImplicitLod, + spv::Op::OpImageSampleProjImplicitLod, + spv::Op::OpImageSampleProjDrefImplicitLod, + spv::Op::OpImageSparseSampleImplicitLod, + spv::Op::OpImageSparseSampleDrefImplicitLod, + spv::Op::OpImageSparseSampleProjImplicitLod, + spv::Op::OpImageSparseSampleProjDrefImplicitLod, + // Explicit derivatives. + spv::Op::OpDPdx, + spv::Op::OpDPdy, + spv::Op::OpFwidth, + spv::Op::OpDPdxFine, + spv::Op::OpDPdyFine, + spv::Op::OpFwidthFine, + spv::Op::OpDPdxCoarse, + spv::Op::OpDPdyCoarse, + spv::Op::OpFwidthCoarse, + }; + return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes), + inst.opcode()) != std::end(derivative_opcodes); +} + +spvtools::DiagnosticStream Warn(opt::IRContext* context, + opt::Instruction* inst) { + if (inst == nullptr) { + return DiagnosticStream({0, 0, 0}, context->consumer(), "", SPV_WARNING); + } else { + // TODO(kuhar): Use line numbers based on debug info. + return DiagnosticStream( + {0, 0, 0}, context->consumer(), + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES), + SPV_WARNING); + } +} + +void PrintDivergenceFlow(opt::IRContext* context, DivergenceAnalysis div, + uint32_t id) { + opt::analysis::DefUseManager* def_use = context->get_def_use_mgr(); + opt::CFG* cfg = context->cfg(); + while (id != 0) { + bool is_block = def_use->GetDef(id)->opcode() == spv::Op::OpLabel; + if (is_block) { + Warn(context, nullptr) + << "block " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + // Skip intermediate blocks. + while (source != 0 && + def_use->GetDef(source)->opcode() == spv::Op::OpLabel) { + id = source; + source = div.GetDivergenceSource(id); + } + if (source == 0) break; + spvtools::opt::Instruction* branch = + cfg->block(div.GetDivergenceDependenceSource(id))->terminator(); + Warn(context, branch) + << "because it depends on a conditional branch on divergent value " + << GetFriendlyName(context, source) << ""; + id = source; + } else { + Warn(context, nullptr) + << "value " << GetFriendlyName(context, id) << " is divergent"; + uint32_t source = div.GetDivergenceSource(id); + opt::Instruction* def = def_use->GetDef(id); + opt::Instruction* source_def = + source == 0 ? nullptr : def_use->GetDef(source); + // First print data -> data dependencies. + while (source != 0 && source_def->opcode() != spv::Op::OpLabel) { + Warn(context, def_use->GetDef(id)) + << "because " << GetFriendlyName(context, id) << " uses value " + << GetFriendlyName(context, source) + << "in its definition, which is divergent"; + id = source; + def = source_def; + source = div.GetDivergenceSource(id); + source_def = def_use->GetDef(source); + } + if (source == 0) { + Warn(context, def) << "because it has a divergent definition"; + break; + } + Warn(context, def) << "because it is conditionally set in block " + << GetFriendlyName(context, source); + id = source; + } + } +} +} // namespace + +bool CheckDivergentDerivatives(opt::IRContext* context) { + DivergenceAnalysis div(*context); + for (opt::Function& func : *context->module()) { + div.Run(&func); + for (const opt::BasicBlock& bb : func) { + for (const opt::Instruction& inst : bb) { + if (InstructionHasDerivative(inst) && + div.GetDivergenceLevel(bb.id()) > + DivergenceAnalysis::DivergenceLevel::kPartiallyUniform) { + Warn(context, nullptr) + << "derivative with divergent control flow" + << " located in block " << GetFriendlyName(context, bb.id()); + PrintDivergenceFlow(context, div, bb.id()); + } + } + } + } + return true; +} + +} // namespace lints +} // namespace lint +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/linter.cpp b/engine/engine/3rdlibs/spirv-tools/source/lint/linter.cpp new file mode 100644 index 00000000..74806767 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/linter.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/linter.hpp" + +#include "source/lint/lints.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +struct Linter::Impl { + explicit Impl(spv_target_env env) : target_env(env) { + message_consumer = [](spv_message_level_t /*level*/, const char* /*source*/, + const spv_position_t& /*position*/, + const char* /*message*/) {}; + } + + spv_target_env target_env; // Target environment. + MessageConsumer message_consumer; // Message consumer. +}; + +Linter::Linter(spv_target_env env) : impl_(new Impl(env)) {} + +Linter::~Linter() {} + +void Linter::SetMessageConsumer(MessageConsumer consumer) { + impl_->message_consumer = std::move(consumer); +} + +const MessageConsumer& Linter::Consumer() const { + return impl_->message_consumer; +} + +bool Linter::Run(const uint32_t* binary, size_t binary_size) { + std::unique_ptr context = + BuildModule(SPV_ENV_VULKAN_1_2, Consumer(), binary, binary_size); + if (context == nullptr) return false; + + bool result = true; + result &= lint::lints::CheckDivergentDerivatives(context.get()); + + return result; +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/lint/lints.h b/engine/engine/3rdlibs/spirv-tools/source/lint/lints.h new file mode 100644 index 00000000..a1995d2f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/lint/lints.h @@ -0,0 +1,34 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LINT_LINTS_H_ +#define SOURCE_LINT_LINTS_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace lint { + +// All of the functions in this namespace output to the error consumer in the +// |context| argument and return |true| if no errors are found. They do not +// modify the IR. +namespace lints { + +bool CheckDivergentDerivatives(opt::IRContext* context); + +} // namespace lints +} // namespace lint +} // namespace spvtools + +#endif // SOURCE_LINT_LINTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/macro.h b/engine/engine/3rdlibs/spirv-tools/source/macro.h new file mode 100644 index 00000000..7219ffed --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/macro.h @@ -0,0 +1,25 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_MACRO_H_ +#define SOURCE_MACRO_H_ + +// Evaluates to the number of elements of array A. +// +// If we could use constexpr, then we could make this a template function. +// If the source arrays were std::array, then we could have used +// std::array::size. +#define ARRAY_SIZE(A) (static_cast(sizeof(A) / sizeof(A[0]))) + +#endif // SOURCE_MACRO_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/name_mapper.cpp b/engine/engine/3rdlibs/spirv-tools/source/name_mapper.cpp new file mode 100644 index 00000000..ae3ef49a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/name_mapper.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/name_mapper.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "source/binary.h" +#include "source/latest_version_spirv_header.h" +#include "source/parsed_operand.h" +#include "source/to_string.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +NameMapper GetTrivialNameMapper() { + return [](uint32_t i) { return spvtools::to_string(i); }; +} + +FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context, + const uint32_t* code, + const size_t wordCount) + : grammar_(AssemblyGrammar(context)) { + spv_diagnostic diag = nullptr; + // We don't care if the parse fails. + spvBinaryParse(context, this, code, wordCount, nullptr, + ParseInstructionForwarder, &diag); + spvDiagnosticDestroy(diag); +} + +std::string FriendlyNameMapper::NameForId(uint32_t id) { + auto iter = name_for_id_.find(id); + if (iter == name_for_id_.end()) { + // It must have been an invalid module, so just return a trivial mapping. + // We don't care about uniqueness. + return to_string(id); + } else { + return iter->second; + } +} + +std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) { + if (suggested_name.empty()) return "_"; + // Otherwise, replace invalid characters by '_'. + std::string result; + std::string valid = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_0123456789"; + std::transform(suggested_name.begin(), suggested_name.end(), + std::back_inserter(result), [&valid](const char c) { + return (std::string::npos == valid.find(c)) ? '_' : c; + }); + return result; +} + +void FriendlyNameMapper::SaveName(uint32_t id, + const std::string& suggested_name) { + if (name_for_id_.find(id) != name_for_id_.end()) return; + + const std::string sanitized_suggested_name = Sanitize(suggested_name); + std::string name = sanitized_suggested_name; + auto inserted = used_names_.insert(name); + if (!inserted.second) { + const std::string base_name = sanitized_suggested_name + "_"; + for (uint32_t index = 0; !inserted.second; ++index) { + name = base_name + to_string(index); + inserted = used_names_.insert(name); + } + } + name_for_id_[id] = name; +} + +void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id, + uint32_t built_in) { +#define GLCASE(name) \ + case spv::BuiltIn::name: \ + SaveName(target_id, "gl_" #name); \ + return; +#define GLCASE2(name, suggested) \ + case spv::BuiltIn::name: \ + SaveName(target_id, "gl_" #suggested); \ + return; +#define CASE(name) \ + case spv::BuiltIn::name: \ + SaveName(target_id, #name); \ + return; + switch (spv::BuiltIn(built_in)) { + GLCASE(Position) + GLCASE(PointSize) + GLCASE(ClipDistance) + GLCASE(CullDistance) + GLCASE2(VertexId, VertexID) + GLCASE2(InstanceId, InstanceID) + GLCASE2(PrimitiveId, PrimitiveID) + GLCASE2(InvocationId, InvocationID) + GLCASE(Layer) + GLCASE(ViewportIndex) + GLCASE(TessLevelOuter) + GLCASE(TessLevelInner) + GLCASE(TessCoord) + GLCASE(PatchVertices) + GLCASE(FragCoord) + GLCASE(PointCoord) + GLCASE(FrontFacing) + GLCASE2(SampleId, SampleID) + GLCASE(SamplePosition) + GLCASE(SampleMask) + GLCASE(FragDepth) + GLCASE(HelperInvocation) + GLCASE2(NumWorkgroups, NumWorkGroups) + GLCASE2(WorkgroupSize, WorkGroupSize) + GLCASE2(WorkgroupId, WorkGroupID) + GLCASE2(LocalInvocationId, LocalInvocationID) + GLCASE2(GlobalInvocationId, GlobalInvocationID) + GLCASE(LocalInvocationIndex) + CASE(WorkDim) + CASE(GlobalSize) + CASE(EnqueuedWorkgroupSize) + CASE(GlobalOffset) + CASE(GlobalLinearId) + CASE(SubgroupSize) + CASE(SubgroupMaxSize) + CASE(NumSubgroups) + CASE(NumEnqueuedSubgroups) + CASE(SubgroupId) + CASE(SubgroupLocalInvocationId) + GLCASE(VertexIndex) + GLCASE(InstanceIndex) + GLCASE(BaseInstance) + CASE(SubgroupEqMaskKHR) + CASE(SubgroupGeMaskKHR) + CASE(SubgroupGtMaskKHR) + CASE(SubgroupLeMaskKHR) + CASE(SubgroupLtMaskKHR) + default: + break; + } +#undef GLCASE +#undef GLCASE2 +#undef CASE +} + +spv_result_t FriendlyNameMapper::ParseInstruction( + const spv_parsed_instruction_t& inst) { + const auto result_id = inst.result_id; + switch (spv::Op(inst.opcode)) { + case spv::Op::OpName: + SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1)); + break; + case spv::Op::OpDecorate: + // Decorations come after OpName. So OpName will take precedence over + // decorations. + // + // In theory, we should also handle OpGroupDecorate. But that's unlikely + // to occur. + if (spv::Decoration(inst.words[2]) == spv::Decoration::BuiltIn) { + assert(inst.num_words > 3); + SaveBuiltInName(inst.words[1], inst.words[3]); + } + break; + case spv::Op::OpTypeVoid: + SaveName(result_id, "void"); + break; + case spv::Op::OpTypeBool: + SaveName(result_id, "bool"); + break; + case spv::Op::OpTypeInt: { + std::string signedness; + std::string root; + const auto bit_width = inst.words[2]; + switch (bit_width) { + case 8: + root = "char"; + break; + case 16: + root = "short"; + break; + case 32: + root = "int"; + break; + case 64: + root = "long"; + break; + default: + root = to_string(bit_width); + signedness = "i"; + break; + } + if (0 == inst.words[3]) signedness = "u"; + SaveName(result_id, signedness + root); + } break; + case spv::Op::OpTypeFloat: { + const auto bit_width = inst.words[2]; + if (inst.num_words > 3) { + if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::BFloat16KHR) { + SaveName(result_id, "bfloat16"); + break; + } + } + switch (bit_width) { + case 16: + SaveName(result_id, "half"); + break; + case 32: + SaveName(result_id, "float"); + break; + case 64: + SaveName(result_id, "double"); + break; + default: + SaveName(result_id, std::string("fp") + to_string(bit_width)); + break; + } + } break; + case spv::Op::OpTypeVector: + SaveName(result_id, std::string("v") + to_string(inst.words[3]) + + NameForId(inst.words[2])); + break; + case spv::Op::OpTypeMatrix: + SaveName(result_id, std::string("mat") + to_string(inst.words[3]) + + NameForId(inst.words[2])); + break; + case spv::Op::OpTypeArray: + SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) + + "_" + NameForId(inst.words[3])); + break; + case spv::Op::OpTypeRuntimeArray: + SaveName(result_id, + std::string("_runtimearr_") + NameForId(inst.words[2])); + break; + case spv::Op::OpTypeNodePayloadArrayAMDX: + SaveName(result_id, + std::string("_payloadarr_") + NameForId(inst.words[2])); + break; + case spv::Op::OpTypePointer: + SaveName(result_id, std::string("_ptr_") + + NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, + inst.words[2]) + + "_" + NameForId(inst.words[3])); + break; + case spv::Op::OpTypeUntypedPointerKHR: + SaveName(result_id, std::string("_ptr_") + + NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, + inst.words[2])); + break; + case spv::Op::OpTypePipe: + SaveName(result_id, + std::string("Pipe") + + NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + inst.words[2])); + break; + case spv::Op::OpTypeEvent: + SaveName(result_id, "Event"); + break; + case spv::Op::OpTypeDeviceEvent: + SaveName(result_id, "DeviceEvent"); + break; + case spv::Op::OpTypeReserveId: + SaveName(result_id, "ReserveId"); + break; + case spv::Op::OpTypeQueue: + SaveName(result_id, "Queue"); + break; + case spv::Op::OpTypeOpaque: + SaveName(result_id, std::string("Opaque_") + + Sanitize(spvDecodeLiteralStringOperand(inst, 1))); + break; + case spv::Op::OpTypePipeStorage: + SaveName(result_id, "PipeStorage"); + break; + case spv::Op::OpTypeNamedBarrier: + SaveName(result_id, "NamedBarrier"); + break; + case spv::Op::OpTypeStruct: + // Structs are mapped rather simplisitically. Just indicate that they + // are a struct and then give the raw Id number. + SaveName(result_id, std::string("_struct_") + to_string(result_id)); + break; + case spv::Op::OpConstantTrue: + SaveName(result_id, "true"); + break; + case spv::Op::OpConstantFalse: + SaveName(result_id, "false"); + break; + case spv::Op::OpConstant: { + std::ostringstream value; + EmitNumericLiteral(&value, inst, inst.operands[2]); + auto value_str = value.str(); + // Use 'n' to signify negative. Other invalid characters will be mapped + // to underscore. + for (auto& c : value_str) + if (c == '-') c = 'n'; + SaveName(result_id, NameForId(inst.type_id) + "_" + value_str); + } break; + default: + // If this instruction otherwise defines an Id, then save a mapping for + // it. This is needed to ensure uniqueness in there is an OpName with + // string something like "1" that might collide with this result_id. + // We should only do this if a name hasn't already been registered by some + // previous forward reference. + if (result_id && name_for_id_.find(result_id) == name_for_id_.end()) + SaveName(result_id, to_string(result_id)); + break; + } + return SPV_SUCCESS; +} + +std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type, + uint32_t word) { + spv_operand_desc desc = nullptr; + if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) { + return desc->name; + } else { + // Invalid input. Just give something. + return std::string("StorageClass") + to_string(word); + } +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/name_mapper.h b/engine/engine/3rdlibs/spirv-tools/source/name_mapper.h new file mode 100644 index 00000000..6902141b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/name_mapper.h @@ -0,0 +1,122 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_NAME_MAPPER_H_ +#define SOURCE_NAME_MAPPER_H_ + +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// A NameMapper maps SPIR-V Id values to names. Each name is valid to use in +// SPIR-V assembly. The mapping is one-to-one, i.e. no two Ids map to the same +// name. +using NameMapper = std::function; + +// Returns a NameMapper which always maps an Id to its decimal representation. +NameMapper GetTrivialNameMapper(); + +// A FriendlyNameMapper parses a module upon construction. If the parse is +// successful, then the NameForId method maps an Id to a friendly name +// while also satisfying the constraints on a NameMapper. +// +// The mapping is friendly in the following sense: +// - If an Id has a debug name (via OpName), then that will be used when +// possible. +// - Well known scalar types map to friendly names. For example, +// OpTypeVoid should be %void. Scalar types map to their names in OpenCL +// when +// there is a correspondence, and otherwise as follows: +// - unsigned integer type of n bits map to "u" followed by n +// - signed integer type of n bits map to "i" followed by n +// - floating point type of n bits map to "fp" followed by n +// - Vector type names map to "v" followed by the number of components, +// followed by the friendly name for the base type. +// - Matrix type names map to "mat" followed by the number of columns, +// followed by the friendly name for the base vector type. +// - Pointer types map to "_ptr_", then the name of the storage class, then the +// name for the pointee type. +// - Exotic types like event, pipe, opaque, queue, reserve-id map to their own +// human readable names. +// - A struct type maps to "_struct_" followed by the raw Id number. That's +// pretty simplistic, but workable. +// - A built-in variable maps to its GLSL variable name. +// - Numeric literals in OpConstant map to a human-friendly name. +class FriendlyNameMapper { + public: + // Construct a friendly name mapper, and determine friendly names for each + // defined Id in the specified module. The module is specified by the code + // wordCount, and should be parseable in the specified context. + FriendlyNameMapper(const spv_const_context context, const uint32_t* code, + const size_t wordCount); + + // Returns a NameMapper which maps ids to the friendly names parsed from the + // module provided to the constructor. + NameMapper GetNameMapper() { + return [this](uint32_t id) { return this->NameForId(id); }; + } + + // Returns the friendly name for the given id. If the module parsed during + // construction is valid, then the mapping satisfies the rules for a + // NameMapper. + std::string NameForId(uint32_t id); + + private: + // Transforms the given string so that it is acceptable as an Id name in + // assembly language. Two distinct inputs can map to the same output. + std::string Sanitize(const std::string& suggested_name); + + // Records a name for the given id. If this id already has a name, then + // this is a no-op. If the id doesn't have a name, use the given + // suggested_name if it hasn't already been taken, and otherwise generate + // a new (unused) name based on the suggested name. + void SaveName(uint32_t id, const std::string& suggested_name); + + // Records a built-in variable name for target_id. If target_id already + // has a name then this is a no-op. + void SaveBuiltInName(uint32_t target_id, uint32_t built_in); + + // Collects information from the given parsed instruction to populate + // name_for_id_. Returns SPV_SUCCESS; + spv_result_t ParseInstruction(const spv_parsed_instruction_t& inst); + + // Forwards a parsed-instruction callback from the binary parser into the + // FriendlyNameMapper hidden inside the user_data parameter. + static spv_result_t ParseInstructionForwarder( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + return reinterpret_cast(user_data)->ParseInstruction( + *parsed_instruction); + } + + // Returns the friendly name for an enumerant. + std::string NameForEnumOperand(spv_operand_type_t type, uint32_t word); + + // Maps an id to its friendly name. This will have an entry for each Id + // defined in the module. + std::unordered_map name_for_id_; + // The set of names that have a mapping in name_for_id_; + std::unordered_set used_names_; + // The assembly grammar for the current context. + const AssemblyGrammar grammar_; +}; + +} // namespace spvtools + +#endif // SOURCE_NAME_MAPPER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opcode.cpp b/engine/engine/3rdlibs/spirv-tools/source/opcode.cpp new file mode 100644 index 00000000..985c91c3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opcode.cpp @@ -0,0 +1,849 @@ +// Copyright (c) 2015-2022 The Khronos Group Inc. +// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All +// rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" + +#include +#include + +#include +#include + +#include "source/instruction.h" +#include "source/macro.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.h" + +namespace { +struct OpcodeDescPtrLen { + const spv_opcode_desc_t* ptr; + uint32_t len; +}; + +#include "core.insts-unified1.inc" + +static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), + kOpcodeTableEntries}; + +// Represents a vendor tool entry in the SPIR-V XML Registry. +struct VendorTool { + uint32_t value; + const char* vendor; + const char* tool; // Might be empty string. + const char* vendor_tool; // Combination of vendor and tool. +}; + +const VendorTool vendor_tools[] = { +#include "generators.inc" +}; + +} // anonymous namespace + +// TODO(dneto): Move this to another file. It doesn't belong with opcode +// processing. +const char* spvGeneratorStr(uint32_t generator) { + auto where = std::find_if( + std::begin(vendor_tools), std::end(vendor_tools), + [generator](const VendorTool& vt) { return generator == vt.value; }); + if (where != std::end(vendor_tools)) return where->vendor_tool; + return "Unknown"; +} + +uint32_t spvOpcodeMake(uint16_t wordCount, spv::Op opcode) { + return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16); +} + +void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount, + uint16_t* pOpcode) { + if (pWordCount) { + *pWordCount = (uint16_t)((0xffff0000 & word) >> 16); + } + if (pOpcode) { + *pOpcode = 0x0000ffff & word; + } +} + +spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) { + if (!pInstTable) return SPV_ERROR_INVALID_POINTER; + + // Descriptions of each opcode. Each entry describes the format of the + // instruction that follows a particular opcode. + + *pInstTable = &kOpcodeTable; + return SPV_SUCCESS; +} + +spv_result_t spvOpcodeTableNameLookup(spv_target_env env, + const spv_opcode_table table, + const char* name, + spv_opcode_desc* pEntry) { + if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; + if (!table) return SPV_ERROR_INVALID_TABLE; + + // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be + // preferable but the table requires sorting on the Opcode name, but it's + // static const initialized and matches the order of the spec. + const size_t nameLength = strlen(name); + const auto version = spvVersionForTargetEnv(env); + for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { + const spv_opcode_desc_t& entry = table->entries[opcodeIndex]; + // We consider the current opcode as available as long as + // 1. The target environment satisfies the minimal requirement of the + // opcode; or + // 2. There is at least one extension enabling this opcode. + // + // Note that the second rule assumes the extension enabling this instruction + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if ((version >= entry.minVersion && version <= entry.lastVersion) || + entry.numExtensions > 0u || entry.numCapabilities > 0u) { + // Exact match case. + if (nameLength == strlen(entry.name) && + !strncmp(name, entry.name, nameLength)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + // Lack of binary search really hurts here. There isn't an easy filter to + // apply before checking aliases since we need to handle promotion from + // vendor to KHR/EXT and KHR/EXT to core. It would require a sure-fire way + // of dropping suffices. Fortunately, most lookup are based on token + // value. + // + // If this was a binary search we could iterate between the lower and + // upper bounds. + if (entry.numAliases > 0) { + for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases; + aliasIndex++) { + // Skip Op prefix. Should this be encoded in the table instead? + const auto alias = entry.aliases[aliasIndex] + 2; + const size_t aliasLength = strlen(alias); + if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvOpcodeTableValueLookup(spv_target_env env, + const spv_opcode_table table, + const spv::Op opcode, + spv_opcode_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + const auto beg = table->entries; + const auto end = table->entries + table->count; + + spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 0, + {}, false, false, 0, nullptr, ~0u, ~0u}; + + auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { + return lhs.opcode < rhs.opcode; + }; + + // We need to loop here because there can exist multiple symbols for the same + // opcode value, and they can be introduced in different target environments, + // which means they can have different minimal version requirements. + // Assumes the underlying table is already sorted ascendingly according to + // opcode value. + const auto version = spvVersionForTargetEnv(env); + for (auto it = std::lower_bound(beg, end, needle, comp); + it != end && it->opcode == opcode; ++it) { + // We considers the current opcode as available as long as + // 1. The target environment satisfies the minimal requirement of the + // opcode; or + // 2. There is at least one extension enabling this opcode. + // + // Note that the second rule assumes the extension enabling this instruction + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if ((version >= it->minVersion && version <= it->lastVersion) || + it->numExtensions > 0u || it->numCapabilities > 0u) { + *pEntry = it; + return SPV_SUCCESS; + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +void spvInstructionCopy(const uint32_t* words, const spv::Op opcode, + const uint16_t wordCount, const spv_endianness_t endian, + spv_instruction_t* pInst) { + pInst->opcode = opcode; + pInst->words.resize(wordCount); + for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) { + pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian); + if (!wordIndex) { + uint16_t thisWordCount; + uint16_t thisOpcode; + spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode); + assert(opcode == static_cast(thisOpcode) && + wordCount == thisWordCount && "Endianness failed!"); + } + } +} + +const char* spvOpcodeString(const uint32_t opcode) { + const auto beg = kOpcodeTableEntries; + const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); + spv_opcode_desc_t needle = {"", static_cast(opcode), + 0, nullptr, + 0, {}, + 0, {}, + false, false, + 0, nullptr, + ~0u, ~0u}; + auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { + return lhs.opcode < rhs.opcode; + }; + auto it = std::lower_bound(beg, end, needle, comp); + if (it != end && it->opcode == spv::Op(opcode)) { + return it->name; + } + + assert(0 && "Unreachable!"); + return "unknown"; +} + +const char* spvOpcodeString(const spv::Op opcode) { + return spvOpcodeString(static_cast(opcode)); +} + +int32_t spvOpcodeIsScalarType(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeBool: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsSpecConstant(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstant: + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpSpecConstantCompositeReplicateEXT: + case spv::Op::OpSpecConstantOp: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsConstant(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + case spv::Op::OpConstant: + case spv::Op::OpConstantComposite: + case spv::Op::OpConstantCompositeReplicateEXT: + case spv::Op::OpConstantSampler: + case spv::Op::OpConstantNull: + case spv::Op::OpConstantFunctionPointerINTEL: + case spv::Op::OpConstantStringAMDX: + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstant: + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpSpecConstantCompositeReplicateEXT: + case spv::Op::OpSpecConstantOp: + case spv::Op::OpSpecConstantStringAMDX: + return true; + default: + return false; + } +} + +bool spvOpcodeIsConstantOrUndef(const spv::Op opcode) { + return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode); +} + +bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstant: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsComposite(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeStruct: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + return true; + default: + return false; + } +} + +bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpFunctionParameter: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyObject: + case spv::Op::OpAllocateNodePayloadsAMDX: + case spv::Op::OpSelect: + case spv::Op::OpPhi: + case spv::Op::OpFunctionCall: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpLoad: + case spv::Op::OpConstantNull: + case spv::Op::OpRawAccessChainNV: + return true; + default: + return false; + } +} + +int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpFunctionParameter: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyObject: + case spv::Op::OpRawAccessChainNV: + case spv::Op::OpAllocateNodePayloadsAMDX: + return true; + default: + return false; + } +} + +int32_t spvOpcodeGeneratesType(spv::Op op) { + switch (op) { + case spv::Op::OpTypeVoid: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeStruct: + case spv::Op::OpTypeOpaque: + case spv::Op::OpTypePointer: + case spv::Op::OpTypeFunction: + case spv::Op::OpTypeEvent: + case spv::Op::OpTypeDeviceEvent: + case spv::Op::OpTypeReserveId: + case spv::Op::OpTypeQueue: + case spv::Op::OpTypePipe: + case spv::Op::OpTypePipeStorage: + case spv::Op::OpTypeNamedBarrier: + case spv::Op::OpTypeAccelerationStructureNV: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + // case spv::Op::OpTypeAccelerationStructureKHR: covered by + // spv::Op::OpTypeAccelerationStructureNV + case spv::Op::OpTypeRayQueryKHR: + case spv::Op::OpTypeHitObjectNV: + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypeNodePayloadArrayAMDX: + case spv::Op::OpTypeTensorLayoutNV: + case spv::Op::OpTypeTensorViewNV: + return true; + default: + // In particular, OpTypeForwardPointer does not generate a type, + // but declares a storage class for a pointer type generated + // by a different instruction. + break; + } + return 0; +} + +bool spvOpcodeIsDecoration(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpMemberDecorate: + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorateStringGOOGLE: + return true; + default: + break; + } + return false; +} + +bool spvOpcodeIsLoad(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpLoad: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSampleFootprintNV: + case spv::Op::OpImageFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseRead: + return true; + default: + return false; + } +} + +bool spvOpcodeIsBranch(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpBranch: + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: + return true; + default: + return false; + } +} + +bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + return true; + default: + return false; + } +} + +bool spvOpcodeIsAtomicOp(const spv::Op opcode) { + return (spvOpcodeIsAtomicWithLoad(opcode) || + opcode == spv::Op::OpAtomicStore || + opcode == spv::Op::OpAtomicFlagClear); +} + +bool spvOpcodeIsReturn(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpReturn: + case spv::Op::OpReturnValue: + return true; + default: + return false; + } +} + +bool spvOpcodeIsAbort(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpKill: + case spv::Op::OpUnreachable: + case spv::Op::OpTerminateInvocation: + case spv::Op::OpTerminateRayKHR: + case spv::Op::OpIgnoreIntersectionKHR: + case spv::Op::OpEmitMeshTasksEXT: + return true; + default: + return false; + } +} + +bool spvOpcodeIsReturnOrAbort(spv::Op opcode) { + return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode); +} + +bool spvOpcodeIsBlockTerminator(spv::Op opcode) { + return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode); +} + +bool spvOpcodeIsBaseOpaqueType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeOpaque: + case spv::Op::OpTypeEvent: + case spv::Op::OpTypeDeviceEvent: + case spv::Op::OpTypeReserveId: + case spv::Op::OpTypeQueue: + case spv::Op::OpTypePipe: + case spv::Op::OpTypeForwardPointer: + case spv::Op::OpTypePipeStorage: + case spv::Op::OpTypeNamedBarrier: + return true; + default: + return false; + } +} + +bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpGroupNonUniformElect: + case spv::Op::OpGroupNonUniformAll: + case spv::Op::OpGroupNonUniformAny: + case spv::Op::OpGroupNonUniformAllEqual: + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformBroadcastFirst: + case spv::Op::OpGroupNonUniformBallot: + case spv::Op::OpGroupNonUniformInverseBallot: + case spv::Op::OpGroupNonUniformBallotBitExtract: + case spv::Op::OpGroupNonUniformBallotBitCount: + case spv::Op::OpGroupNonUniformBallotFindLSB: + case spv::Op::OpGroupNonUniformBallotFindMSB: + case spv::Op::OpGroupNonUniformShuffle: + case spv::Op::OpGroupNonUniformShuffleXor: + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + case spv::Op::OpGroupNonUniformIAdd: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformIMul: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformSMin: + case spv::Op::OpGroupNonUniformUMin: + case spv::Op::OpGroupNonUniformFMin: + case spv::Op::OpGroupNonUniformSMax: + case spv::Op::OpGroupNonUniformUMax: + case spv::Op::OpGroupNonUniformFMax: + case spv::Op::OpGroupNonUniformBitwiseAnd: + case spv::Op::OpGroupNonUniformBitwiseOr: + case spv::Op::OpGroupNonUniformBitwiseXor: + case spv::Op::OpGroupNonUniformLogicalAnd: + case spv::Op::OpGroupNonUniformLogicalOr: + case spv::Op::OpGroupNonUniformLogicalXor: + case spv::Op::OpGroupNonUniformQuadBroadcast: + case spv::Op::OpGroupNonUniformQuadSwap: + case spv::Op::OpGroupNonUniformRotateKHR: + case spv::Op::OpGroupNonUniformQuadAllKHR: + case spv::Op::OpGroupNonUniformQuadAnyKHR: + return true; + default: + return false; + } +} + +bool spvOpcodeIsScalarizable(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpPhi: + case spv::Op::OpCopyObject: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + return true; + default: + return false; + } +} + +bool spvOpcodeIsDebug(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpName: + case spv::Op::OpMemberName: + case spv::Op::OpSource: + case spv::Op::OpSourceContinued: + case spv::Op::OpSourceExtension: + case spv::Op::OpString: + case spv::Op::OpLine: + case spv::Op::OpNoLine: + case spv::Op::OpModuleProcessed: + return true; + default: + return false; + } +} + +bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + return true; + default: + return false; + } +} + +bool spvOpcodeIsLinearAlgebra(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpTranspose: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + return true; + default: + return false; + } +} + +bool spvOpcodeIsImageSample(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSampleFootprintNV: + return true; + default: + return false; + } +} + +bool spvIsExtendedInstruction(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + return true; + default: + return false; + } +} + +std::vector spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpMemoryBarrier: + return {1}; + case spv::Op::OpAtomicStore: + case spv::Op::OpControlBarrier: + case spv::Op::OpAtomicFlagClear: + case spv::Op::OpMemoryNamedBarrier: + return {2}; + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + return {4}; + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + return {4, 5}; + default: + return {}; + } +} + +bool spvOpcodeIsAccessChain(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpRawAccessChainNV: + return true; + default: + return false; + } +} + +bool spvOpcodeIsBit(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + return true; + default: + return false; + } +} + +bool spvOpcodeGeneratesUntypedPointer(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpUntypedVariableKHR: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: + return true; + default: + return false; + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/opcode.h b/engine/engine/3rdlibs/spirv-tools/source/opcode.h new file mode 100644 index 00000000..08fc56d8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opcode.h @@ -0,0 +1,168 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPCODE_H_ +#define SOURCE_OPCODE_H_ + +#include "source/instruction.h" +#include "source/latest_version_spirv_header.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// Returns the name of a registered SPIR-V generator as a null-terminated +// string. If the generator is not known, then returns the string "Unknown". +// The generator parameter should be most significant 16-bits of the generator +// word in the SPIR-V module header. +// +// See the registry at https://www.khronos.org/registry/spir-v/api/spir-v.xml. +const char* spvGeneratorStr(uint32_t generator); + +// Combines word_count and opcode enumerant in single word. +uint32_t spvOpcodeMake(uint16_t word_count, spv::Op opcode); + +// Splits word into into two constituent parts: word_count and opcode. +void spvOpcodeSplit(const uint32_t word, uint16_t* word_count, + uint16_t* opcode); + +// Finds the named opcode in the given opcode table. On success, returns +// SPV_SUCCESS and writes a handle of the table entry into *entry. +spv_result_t spvOpcodeTableNameLookup(spv_target_env, + const spv_opcode_table table, + const char* name, spv_opcode_desc* entry); + +// Finds the opcode by enumerant in the given opcode table. On success, returns +// SPV_SUCCESS and writes a handle of the table entry into *entry. +spv_result_t spvOpcodeTableValueLookup(spv_target_env, + const spv_opcode_table table, + const spv::Op opcode, + spv_opcode_desc* entry); + +// Copies an instruction's word and fixes the endianness to host native. The +// source instruction's stream/opcode/endianness is in the words/opcode/endian +// parameter. The word_count parameter specifies the number of words to copy. +// Writes copied instruction into *inst. +void spvInstructionCopy(const uint32_t* words, const spv::Op opcode, + const uint16_t word_count, + const spv_endianness_t endian, spv_instruction_t* inst); + +// Determine if the given opcode is a scalar type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeIsScalarType(const spv::Op opcode); + +// Determines if the given opcode is a specialization constant. Returns zero if +// false, non-zero otherwise. +int32_t spvOpcodeIsSpecConstant(const spv::Op opcode); + +// Determines if the given opcode is a constant. Returns zero if false, non-zero +// otherwise. +int32_t spvOpcodeIsConstant(const spv::Op opcode); + +// Returns true if the given opcode is a constant or undef. +bool spvOpcodeIsConstantOrUndef(const spv::Op opcode); + +// Returns true if the given opcode is a scalar specialization constant. +bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode); + +// Determines if the given opcode is a composite type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeIsComposite(const spv::Op opcode); + +// Determines if the given opcode results in a pointer when using the logical +// addressing model. Returns zero if false, non-zero otherwise. +int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode); + +// Returns whether the given opcode could result in a pointer or a variable +// pointer when using the logical addressing model. +bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode); + +// Determines if the given opcode generates a type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeGeneratesType(spv::Op opcode); + +// Returns true if the opcode adds a decoration to an id. +bool spvOpcodeIsDecoration(const spv::Op opcode); + +// Returns true if the opcode is a load from memory into a result id. This +// function only considers core instructions. +bool spvOpcodeIsLoad(const spv::Op opcode); + +// Returns true if the opcode is an atomic operation that uses the original +// value. +bool spvOpcodeIsAtomicWithLoad(const spv::Op opcode); + +// Returns true if the opcode is an atomic operation. +bool spvOpcodeIsAtomicOp(const spv::Op opcode); + +// Returns true if the given opcode is a branch instruction. +bool spvOpcodeIsBranch(spv::Op opcode); + +// Returns true if the given opcode is a return instruction. +bool spvOpcodeIsReturn(spv::Op opcode); + +// Returns true if the given opcode aborts execution. To abort means that after +// executing that instruction, no other instructions will be executed regardless +// of the context in which the instruction appears. Note that `OpUnreachable` +// is considered an abort even if its behaviour is undefined. +bool spvOpcodeIsAbort(spv::Op opcode); + +// Returns true if the given opcode is a return instruction or it aborts +// execution. +bool spvOpcodeIsReturnOrAbort(spv::Op opcode); + +// Returns true if the given opcode is a basic block terminator. +bool spvOpcodeIsBlockTerminator(spv::Op opcode); + +// Returns true if the given opcode always defines an opaque type. +bool spvOpcodeIsBaseOpaqueType(spv::Op opcode); + +// Returns true if the given opcode is a non-uniform group operation. +bool spvOpcodeIsNonUniformGroupOperation(spv::Op opcode); + +// Returns true if the opcode with vector inputs could be divided into a series +// of independent scalar operations that would give the same result. +bool spvOpcodeIsScalarizable(spv::Op opcode); + +// Returns true if the given opcode is a debug instruction. +bool spvOpcodeIsDebug(spv::Op opcode); + +// Returns true for opcodes that are binary operators, +// where the order of the operands is irrelevant. +bool spvOpcodeIsCommutativeBinaryOperator(spv::Op opcode); + +// Returns true for opcodes that represent linear algebra instructions. +bool spvOpcodeIsLinearAlgebra(spv::Op opcode); + +// Returns true for opcodes that represent image sample instructions. +bool spvOpcodeIsImageSample(spv::Op opcode); + +// Returns true if the opcode is either OpExtInst or OpExtInstWithForwardRefsKHR +bool spvIsExtendedInstruction(spv::Op opcode); + +// Returns a vector containing the indices of the memory semantics +// operands for |opcode|. +std::vector spvOpcodeMemorySemanticsOperandIndices(spv::Op opcode); + +// Returns true for opcodes that represent access chain instructions. +bool spvOpcodeIsAccessChain(spv::Op opcode); + +// Returns true for opcodes that represent bit instructions. +bool spvOpcodeIsBit(spv::Op opcode); + +// Gets the name of an instruction, without the "Op" prefix. +const char* spvOpcodeString(const spv::Op opcode); + +// Returns true for opcodes that generate an untyped pointer result. +bool spvOpcodeGeneratesUntypedPointer(spv::Op opcode); + +#endif // SOURCE_OPCODE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/operand.cpp b/engine/engine/3rdlibs/spirv-tools/source/operand.cpp new file mode 100644 index 00000000..869a7ca1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/operand.cpp @@ -0,0 +1,678 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/operand.h" + +#include +#include + +#include + +#include "DebugInfo.h" +#include "OpenCLDebugInfo100.h" +#include "source/macro.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" + +// For now, assume unified1 contains up to SPIR-V 1.3 and no later +// SPIR-V version. +// TODO(dneto): Make one set of tables, but with version tags on a +// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195 + +#include "operand.kinds-unified1.inc" +#include "spirv-tools/libspirv.h" + +static const spv_operand_table_t kOperandTable = { + ARRAY_SIZE(pygen_variable_OperandInfoTable), + pygen_variable_OperandInfoTable}; + +spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, + spv_target_env) { + if (!pOperandTable) return SPV_ERROR_INVALID_POINTER; + + *pOperandTable = &kOperandTable; + return SPV_SUCCESS; +} + +spv_result_t spvOperandTableNameLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const char* name, + const size_t nameLength, + spv_operand_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; + + for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { + const auto& group = table->types[typeIndex]; + if (type != group.type) continue; + for (uint64_t index = 0; index < group.count; ++index) { + const auto& entry = group.entries[index]; + // We consider the current operand as available as long as + // it is in the grammar. It might not be *valid* to use, + // but that should be checked by the validator, not by parsing. + // + // Exact match case + if (nameLength == strlen(entry.name) && + !strncmp(entry.name, name, nameLength)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + + // Check the aliases. Ideally we would have a version of the table sorted + // by name and then we could iterate between the lower and upper bounds to + // restrict the amount comparisons. Fortunately, name-based lookups are + // mostly restricted to the assembler. + if (entry.numAliases > 0) { + for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases; + aliasIndex++) { + const auto alias = entry.aliases[aliasIndex]; + const size_t aliasLength = strlen(alias); + if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvOperandTableValueLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const uint32_t value, + spv_operand_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, + 0, nullptr, {}, ~0u, ~0u}; + + auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) { + return lhs.value < rhs.value; + }; + + for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { + const auto& group = table->types[typeIndex]; + if (type != group.type) continue; + + const auto beg = group.entries; + const auto end = group.entries + group.count; + + // Assumes the underlying table is already sorted ascendingly according to + // opcode value. + auto it = std::lower_bound(beg, end, needle, comp); + if (it != end && it->value == value) { + // The current operand is considered available as long as + // it is in the grammar. It might not be *valid* to use, + // but that should be checked by the validator, not by parsing. + *pEntry = it; + return SPV_SUCCESS; + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +const char* spvOperandTypeStr(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: + return "ID"; + case SPV_OPERAND_TYPE_TYPE_ID: + return "type ID"; + case SPV_OPERAND_TYPE_RESULT_ID: + return "result ID"; + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + return "literal number"; + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + return "possibly multi-word literal integer"; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + return "possibly multi-word literal number"; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: + return "extension instruction number"; + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: + return "OpSpecConstantOp opcode"; + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: + return "literal string"; + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + return "source language"; + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + return "execution model"; + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + return "addressing model"; + case SPV_OPERAND_TYPE_MEMORY_MODEL: + return "memory model"; + case SPV_OPERAND_TYPE_EXECUTION_MODE: + return "execution mode"; + case SPV_OPERAND_TYPE_STORAGE_CLASS: + return "storage class"; + case SPV_OPERAND_TYPE_DIMENSIONALITY: + return "dimensionality"; + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + return "sampler addressing mode"; + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + return "sampler filter mode"; + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + return "image format"; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + return "floating-point fast math mode"; + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + return "floating-point rounding mode"; + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + return "linkage type"; + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + return "access qualifier"; + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + return "function parameter attribute"; + case SPV_OPERAND_TYPE_DECORATION: + return "decoration"; + case SPV_OPERAND_TYPE_BUILT_IN: + return "built-in"; + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + return "selection control"; + case SPV_OPERAND_TYPE_LOOP_CONTROL: + return "loop control"; + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + return "function control"; + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + return "memory semantics ID"; + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + return "memory access"; + case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE: + return "shading rate"; + case SPV_OPERAND_TYPE_SCOPE_ID: + return "scope ID"; + case SPV_OPERAND_TYPE_GROUP_OPERATION: + return "group operation"; + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + return "kernel enqeue flags"; + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + return "kernel profiling info"; + case SPV_OPERAND_TYPE_CAPABILITY: + return "capability"; + case SPV_OPERAND_TYPE_RAY_FLAGS: + return "ray flags"; + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + return "ray query intersection"; + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + return "ray query committed intersection type"; + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + return "ray query candidate intersection type"; + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + return "packed vector format"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + return "cooperative matrix operands"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT: + return "cooperative matrix layout"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE: + return "cooperative matrix use"; + case SPV_OPERAND_TYPE_TENSOR_CLAMP_MODE: + return "tensor clamp mode"; + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: + return "cooperative matrix reduce"; + case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + return "tensor addressing operands"; + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + return "matrix multiply accumulate operands"; + case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER: + return "initialization mode qualifier"; + case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: + return "host access qualifier"; + case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL: + return "load cache control"; + case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL: + return "store cache control"; + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: + return "named maximum number of registers"; + case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: + return "raw access chain operands"; + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + return "image"; + case SPV_OPERAND_TYPE_OPTIONAL_CIV: + return "context-insensitive value"; + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + return "debug info flags"; + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "debug base type encoding"; + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + return "debug composite type"; + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + return "debug type qualifier"; + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + return "debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + return "OpenCL.DebugInfo.100 debug info flags"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "OpenCL.DebugInfo.100 debug base type encoding"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + return "OpenCL.DebugInfo.100 debug composite type"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + return "OpenCL.DebugInfo.100 debug type qualifier"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + return "OpenCL.DebugInfo.100 debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + return "OpenCL.DebugInfo.100 debug imported entity"; + case SPV_OPERAND_TYPE_FPENCODING: + case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING: + return "FP encoding"; + + // The next values are for values returned from an instruction, not actually + // an operand. So the specific strings don't matter. But let's add them + // for completeness and ease of testing. + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER: + return "image channel order"; + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE: + return "image channel data type"; + + case SPV_OPERAND_TYPE_FPDENORM_MODE: + return "FP denorm mode"; + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + return "FP operation mode"; + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + return "quantization mode"; + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + return "overflow mode"; + case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT: + return "cooperative vector matrix layout"; + case SPV_OPERAND_TYPE_COMPONENT_TYPE: + return "component type"; + + case SPV_OPERAND_TYPE_NONE: + return "NONE"; + default: + break; + } + return "unknown"; +} + +void spvPushOperandTypes(const spv_operand_type_t* types, + spv_operand_pattern_t* pattern) { + const spv_operand_type_t* endTypes; + for (endTypes = types; *endTypes != SPV_OPERAND_TYPE_NONE; ++endTypes) { + } + + while (endTypes-- != types) { + pattern->push_back(*endTypes); + } +} + +void spvPushOperandTypesForMask(spv_target_env env, + const spv_operand_table operandTable, + const spv_operand_type_t type, + const uint32_t mask, + spv_operand_pattern_t* pattern) { + // Scan from highest bits to lowest bits because we will append in LIFO + // fashion, and we need the operands for lower order bits to be consumed first + for (uint32_t candidate_bit = (1u << 31u); candidate_bit; + candidate_bit >>= 1) { + if (candidate_bit & mask) { + spv_operand_desc entry = nullptr; + if (SPV_SUCCESS == spvOperandTableValueLookup(env, operandTable, type, + candidate_bit, &entry)) { + spvPushOperandTypes(entry->operandTypes, pattern); + } + } + } +} + +bool spvOperandIsConcrete(spv_operand_type_t type) { + if (spvIsIdType(type) || spvOperandIsConcreteMask(type)) { + return true; + } + switch (type) { + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_LITERAL_FLOAT: + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER: + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_LAYOUT: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_USE: + case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER: + case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL: + case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL: + case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS: + case SPV_OPERAND_TYPE_FPENCODING: + case SPV_OPERAND_TYPE_TENSOR_CLAMP_MODE: + case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT: + case SPV_OPERAND_TYPE_COMPONENT_TYPE: + return true; + default: + break; + } + return false; +} + +bool spvOperandIsConcreteMask(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: + case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + return true; + default: + break; + } + return false; +} + +bool spvOperandIsOptional(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_OPTIONAL_ID: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_CIV: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING: + return true; + default: + break; + } + // Any variable operand is also optional. + return spvOperandIsVariable(type); +} + +bool spvOperandIsVariable(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_VARIABLE_ID: + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: + case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + return true; + default: + break; + } + return false; +} + +bool spvExpandOperandSequenceOnce(spv_operand_type_t type, + spv_operand_pattern_t* pattern) { + switch (type) { + case SPV_OPERAND_TYPE_VARIABLE_ID: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); + return true; + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER); + return true; + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: + // Represents Zero or more (Literal number, Id) pairs, + // where the literal number must be a scalar integer. + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_ID); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER); + return true; + case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + // Represents Zero or more (Id, Literal number) pairs. + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_LITERAL_INTEGER); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); + return true; + default: + break; + } + return false; +} + +spv_operand_type_t spvTakeFirstMatchableOperand( + spv_operand_pattern_t* pattern) { + assert(!pattern->empty()); + spv_operand_type_t result; + do { + result = pattern->back(); + pattern->pop_back(); + } while (spvExpandOperandSequenceOnce(result, pattern)); + return result; +} + +spv_operand_pattern_t spvAlternatePatternFollowingImmediate( + const spv_operand_pattern_t& pattern) { + auto it = + std::find(pattern.crbegin(), pattern.crend(), SPV_OPERAND_TYPE_RESULT_ID); + if (it != pattern.crend()) { + spv_operand_pattern_t alternatePattern(it - pattern.crbegin() + 2, + SPV_OPERAND_TYPE_OPTIONAL_CIV); + alternatePattern[1] = SPV_OPERAND_TYPE_RESULT_ID; + return alternatePattern; + } + + // No result-id found, so just expect CIVs. + return {SPV_OPERAND_TYPE_OPTIONAL_CIV}; +} + +bool spvIsIdType(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + return true; + default: + return false; + } +} + +bool spvIsInIdType(spv_operand_type_t type) { + if (!spvIsIdType(type)) { + // If it is not an ID it cannot be an input ID. + return false; + } + switch (type) { + // Deny non-input IDs. + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + return false; + default: + return true; + } +} + +std::function spvOperandCanBeForwardDeclaredFunction( + spv::Op opcode) { + std::function out; + if (spvOpcodeGeneratesType(opcode)) { + // All types can use forward pointers. + out = [](unsigned) { return true; }; + return out; + } + switch (opcode) { + case spv::Op::OpExecutionMode: + case spv::Op::OpExecutionModeId: + case spv::Op::OpEntryPoint: + case spv::Op::OpName: + case spv::Op::OpMemberName: + case spv::Op::OpSelectionMerge: + case spv::Op::OpDecorate: + case spv::Op::OpMemberDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorateStringGOOGLE: + case spv::Op::OpBranch: + case spv::Op::OpLoopMerge: + out = [](unsigned) { return true; }; + break; + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: + out = [](unsigned index) { return index != 0; }; + break; + + case spv::Op::OpFunctionCall: + // The Function parameter. + out = [](unsigned index) { return index == 2; }; + break; + + case spv::Op::OpPhi: + out = [](unsigned index) { return index > 1; }; + break; + + case spv::Op::OpEnqueueKernel: + // The Invoke parameter. + out = [](unsigned index) { return index == 8; }; + break; + + case spv::Op::OpGetKernelNDrangeSubGroupCount: + case spv::Op::OpGetKernelNDrangeMaxSubGroupSize: + // The Invoke parameter. + out = [](unsigned index) { return index == 3; }; + break; + + case spv::Op::OpGetKernelWorkGroupSize: + case spv::Op::OpGetKernelPreferredWorkGroupSizeMultiple: + // The Invoke parameter. + out = [](unsigned index) { return index == 2; }; + break; + case spv::Op::OpTypeForwardPointer: + out = [](unsigned index) { return index == 0; }; + break; + case spv::Op::OpTypeArray: + out = [](unsigned index) { return index == 1; }; + break; + case spv::Op::OpCooperativeMatrixPerElementOpNV: + out = [](unsigned index) { return index == 3; }; + break; + case spv::Op::OpCooperativeMatrixReduceNV: + out = [](unsigned index) { return index == 4; }; + break; + case spv::Op::OpCooperativeMatrixLoadTensorNV: + // approximate, due to variable operands + out = [](unsigned index) { return index > 6; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + return out; +} + +std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + spv::Op opcode, spv_ext_inst_type_t ext_type, uint32_t key) { + // The Vulkan debug info extended instruction set is non-semantic so allows no + // forward references except if used through OpExtInstWithForwardRefsKHR. + if (ext_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + return [opcode](unsigned) { + return opcode == spv::Op::OpExtInstWithForwardRefsKHR; + }; + } + + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward + // references for debug info instructions are still in discussion. We must + // update the following lines of code when we conclude the spec. + std::function out; + if (ext_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + switch (OpenCLDebugInfo100Instructions(key)) { + case OpenCLDebugInfo100DebugFunction: + out = [](unsigned index) { return index == 13; }; + break; + case OpenCLDebugInfo100DebugTypeComposite: + out = [](unsigned index) { return index >= 13; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + } else { + switch (DebugInfoInstructions(key)) { + case DebugInfoDebugFunction: + out = [](unsigned index) { return index == 13; }; + break; + case DebugInfoDebugTypeComposite: + out = [](unsigned index) { return index >= 12; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + } + return out; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/operand.h b/engine/engine/3rdlibs/spirv-tools/source/operand.h new file mode 100644 index 00000000..3d42a059 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/operand.h @@ -0,0 +1,145 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPERAND_H_ +#define SOURCE_OPERAND_H_ + +#include +#include + +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// A sequence of operand types. +// +// A SPIR-V parser uses an operand pattern to describe what is expected +// next on the input. +// +// As we parse an instruction in text or binary form from left to right, +// we pop and push at the end of the pattern vector. Symbols later in the +// pattern vector are matched against the input before symbols earlier in the +// pattern vector are matched. + +// Using a vector in this way reduces memory traffic, which is good for +// performance. +using spv_operand_pattern_t = std::vector; + +// Finds the named operand in the table. The type parameter specifies the +// operand's group. A handle of the operand table entry for this operand will +// be written into *entry. +spv_result_t spvOperandTableNameLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const char* name, + const size_t name_length, + spv_operand_desc* entry); + +// Finds the operand with value in the table. The type parameter specifies the +// operand's group. A handle of the operand table entry for this operand will +// be written into *entry. +spv_result_t spvOperandTableValueLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const uint32_t value, + spv_operand_desc* entry); + +// Gets the name string of the non-variable operand type. +const char* spvOperandTypeStr(spv_operand_type_t type); + +// Returns true if an operand of the given type is optional. +bool spvOperandIsOptional(spv_operand_type_t type); + +// Returns true if an operand type represents zero or more logical operands. +// +// Note that a single logical operand may still be a variable number of words. +// For example, a literal string may be many words, but is just one logical +// operand. +bool spvOperandIsVariable(spv_operand_type_t type); + +// Append a list of operand types to the end of the pattern vector. +// The types parameter specifies the source array of types, ending with +// SPV_OPERAND_TYPE_NONE. +void spvPushOperandTypes(const spv_operand_type_t* types, + spv_operand_pattern_t* pattern); + +// Appends the operands expected after the given typed mask onto the +// end of the given pattern. +// +// Each set bit in the mask represents zero or more operand types that should +// be appended onto the pattern. Operands for a less significant bit always +// appear after operands for a more significant bit. +// +// If a set bit is unknown, then we assume it has no operands. +void spvPushOperandTypesForMask(spv_target_env, + const spv_operand_table operand_table, + const spv_operand_type_t mask_type, + const uint32_t mask, + spv_operand_pattern_t* pattern); + +// Expands an operand type representing zero or more logical operands, +// exactly once. +// +// If the given type represents potentially several logical operands, +// then prepend the given pattern with the first expansion of the logical +// operands, followed by original type. Otherwise, don't modify the pattern. +// +// For example, the SPV_OPERAND_TYPE_VARIABLE_ID represents zero or more +// IDs. In that case we would prepend the pattern with SPV_OPERAND_TYPE_ID +// followed by SPV_OPERAND_TYPE_VARIABLE_ID again. +// +// This also applies to zero or more tuples of logical operands. In that case +// we prepend pattern with for the members of the tuple, followed by the +// original type argument. The pattern must encode the fact that if any part +// of the tuple is present, then all tuple members should be. So the first +// member of the tuple must be optional, and the remaining members +// non-optional. +// +// Returns true if we modified the pattern. +bool spvExpandOperandSequenceOnce(spv_operand_type_t type, + spv_operand_pattern_t* pattern); + +// Expands the first element in the pattern until it is a matchable operand +// type, then pops it off the front and returns it. The pattern must not be +// empty. +// +// A matchable operand type is anything other than a zero-or-more-items +// operand type. +spv_operand_type_t spvTakeFirstMatchableOperand(spv_operand_pattern_t* pattern); + +// Calculates the corresponding post-immediate alternate pattern, which allows +// a limited set of operand types. +spv_operand_pattern_t spvAlternatePatternFollowingImmediate( + const spv_operand_pattern_t& pattern); + +// Is the operand an ID? +bool spvIsIdType(spv_operand_type_t type); + +// Is the operand an input ID? +bool spvIsInIdType(spv_operand_type_t type); + +// Takes the opcode of an instruction and returns +// a function object that will return true if the index +// of the operand can be forward declared. This function will +// used in the SSA validation stage of the pipeline +std::function spvOperandCanBeForwardDeclaredFunction( + spv::Op opcode); + +// Takes the instruction key of a debug info extension instruction +// and returns a function object that will return true if the index +// of the operand can be forward declared. This function will +// used in the SSA validation stage of the pipeline +std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + spv::Op opcode, spv_ext_inst_type_t ext_type, uint32_t key); + +#endif // SOURCE_OPERAND_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/opt/CMakeLists.txt new file mode 100644 index 00000000..ee3aeb19 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/CMakeLists.txt @@ -0,0 +1,290 @@ +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_OPT_SOURCES + fix_func_call_arguments.h + aggressive_dead_code_elim_pass.h + amd_ext_to_khr.h + analyze_live_input_pass.h + basic_block.h + block_merge_pass.h + block_merge_util.h + build_module.h + ccp_pass.h + cfg_cleanup_pass.h + cfg.h + code_sink.h + combine_access_chains.h + compact_ids_pass.h + composite.h + const_folding_rules.h + constants.h + control_dependence.h + convert_to_sampled_image_pass.h + convert_to_half_pass.h + copy_prop_arrays.h + dataflow.h + dead_branch_elim_pass.h + dead_insert_elim_pass.h + dead_variable_elimination.h + decoration_manager.h + debug_info_manager.h + def_use_manager.h + desc_sroa.h + desc_sroa_util.h + dominator_analysis.h + dominator_tree.h + eliminate_dead_constant_pass.h + eliminate_dead_functions_pass.h + eliminate_dead_functions_util.h + eliminate_dead_io_components_pass.h + eliminate_dead_members_pass.h + eliminate_dead_output_stores_pass.h + empty_pass.h + feature_manager.h + fix_storage_class.h + flatten_decoration_pass.h + fold.h + folding_rules.h + fold_spec_constant_op_and_composite_pass.h + freeze_spec_constant_value_pass.h + function.h + graphics_robust_access_pass.h + if_conversion.h + inline_exhaustive_pass.h + inline_opaque_pass.h + inline_pass.h + instruction.h + instruction_list.h + interface_var_sroa.h + invocation_interlock_placement_pass.h + interp_fixup_pass.h + opextinst_forward_ref_fixup_pass.h + ir_builder.h + ir_context.h + ir_loader.h + licm_pass.h + liveness.h + local_access_chain_convert_pass.h + local_redundancy_elimination.h + local_single_block_elim_pass.h + local_single_store_elim_pass.h + log.h + loop_dependence.h + loop_descriptor.h + loop_fission.h + loop_fusion.h + loop_fusion_pass.h + loop_peeling.h + loop_unroller.h + loop_utils.h + loop_unswitch_pass.h + mem_pass.h + merge_return_pass.h + modify_maximal_reconvergence.h + module.h + null_pass.h + passes.h + pass.h + pass_manager.h + private_to_local_pass.h + propagator.h + reduce_load_size.h + redundancy_elimination.h + reflect.h + register_pressure.h + relax_float_ops_pass.h + remove_dontinline_pass.h + remove_duplicates_pass.h + remove_unused_interface_variables_pass.h + replace_desc_array_access_using_var_index.h + replace_invalid_opc.h + resolve_binding_conflicts_pass.h + scalar_analysis.h + scalar_analysis_nodes.h + scalar_replacement_pass.h + set_spec_constant_default_value_pass.h + simplification_pass.h + split_combined_image_sampler_pass.h + spread_volatile_semantics.h + ssa_rewrite_pass.h + strength_reduction_pass.h + strip_debug_info_pass.h + strip_nonsemantic_info_pass.h + struct_cfg_analysis.h + switch_descriptorset_pass.h + tree_iterator.h + trim_capabilities_pass.h + type_manager.h + types.h + unify_const_pass.h + upgrade_memory_model.h + value_number_table.h + vector_dce.h + workaround1209.h + wrap_opkill.h + + fix_func_call_arguments.cpp + aggressive_dead_code_elim_pass.cpp + amd_ext_to_khr.cpp + analyze_live_input_pass.cpp + basic_block.cpp + block_merge_pass.cpp + block_merge_util.cpp + build_module.cpp + ccp_pass.cpp + cfg_cleanup_pass.cpp + cfg.cpp + code_sink.cpp + combine_access_chains.cpp + compact_ids_pass.cpp + composite.cpp + const_folding_rules.cpp + constants.cpp + control_dependence.cpp + convert_to_sampled_image_pass.cpp + convert_to_half_pass.cpp + copy_prop_arrays.cpp + dataflow.cpp + dead_branch_elim_pass.cpp + dead_insert_elim_pass.cpp + dead_variable_elimination.cpp + decoration_manager.cpp + debug_info_manager.cpp + def_use_manager.cpp + desc_sroa.cpp + desc_sroa_util.cpp + dominator_analysis.cpp + dominator_tree.cpp + eliminate_dead_constant_pass.cpp + eliminate_dead_functions_pass.cpp + eliminate_dead_functions_util.cpp + eliminate_dead_io_components_pass.cpp + eliminate_dead_members_pass.cpp + eliminate_dead_output_stores_pass.cpp + feature_manager.cpp + fix_storage_class.cpp + flatten_decoration_pass.cpp + fold.cpp + folding_rules.cpp + fold_spec_constant_op_and_composite_pass.cpp + freeze_spec_constant_value_pass.cpp + function.cpp + graphics_robust_access_pass.cpp + if_conversion.cpp + inline_exhaustive_pass.cpp + inline_opaque_pass.cpp + inline_pass.cpp + instruction.cpp + instruction_list.cpp + interface_var_sroa.cpp + invocation_interlock_placement_pass.cpp + interp_fixup_pass.cpp + opextinst_forward_ref_fixup_pass.cpp + ir_context.cpp + ir_loader.cpp + licm_pass.cpp + liveness.cpp + local_access_chain_convert_pass.cpp + local_redundancy_elimination.cpp + local_single_block_elim_pass.cpp + local_single_store_elim_pass.cpp + loop_dependence.cpp + loop_dependence_helpers.cpp + loop_descriptor.cpp + loop_fission.cpp + loop_fusion.cpp + loop_fusion_pass.cpp + loop_peeling.cpp + loop_utils.cpp + loop_unroller.cpp + loop_unswitch_pass.cpp + mem_pass.cpp + merge_return_pass.cpp + modify_maximal_reconvergence.cpp + module.cpp + optimizer.cpp + pass.cpp + pass_manager.cpp + private_to_local_pass.cpp + propagator.cpp + reduce_load_size.cpp + redundancy_elimination.cpp + register_pressure.cpp + relax_float_ops_pass.cpp + remove_dontinline_pass.cpp + remove_duplicates_pass.cpp + remove_unused_interface_variables_pass.cpp + replace_desc_array_access_using_var_index.cpp + replace_invalid_opc.cpp + resolve_binding_conflicts_pass.cpp + scalar_analysis.cpp + scalar_analysis_simplification.cpp + scalar_replacement_pass.cpp + set_spec_constant_default_value_pass.cpp + simplification_pass.cpp + split_combined_image_sampler_pass.cpp + spread_volatile_semantics.cpp + ssa_rewrite_pass.cpp + strength_reduction_pass.cpp + strip_debug_info_pass.cpp + strip_nonsemantic_info_pass.cpp + struct_cfg_analysis.cpp + struct_packing_pass.cpp + switch_descriptorset_pass.cpp + trim_capabilities_pass.cpp + type_manager.cpp + types.cpp + unify_const_pass.cpp + upgrade_memory_model.cpp + value_number_table.cpp + vector_dce.cpp + workaround1209.cpp + wrap_opkill.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() + +spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt) + +add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_OPT_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-opt) +target_include_directories(SPIRV-Tools-opt + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-opt + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) + +set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-opt) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets) + export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR) + install(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-opt) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-optConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp new file mode 100644 index 00000000..e6b02a79 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -0,0 +1,1157 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2018-2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/aggressive_dead_code_elim_pass.h" + +#include +#include + +#include "source/cfa.h" +#include "source/opt/eliminate_dead_functions_util.h" +#include "source/opt/ir_builder.h" +#include "source/opt/reflect.h" +#include "source/spirv_constant.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +constexpr uint32_t kTypePointerStorageClassInIdx = 0; +constexpr uint32_t kEntryPointFunctionIdInIdx = 1; +constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0; +constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1; +constexpr uint32_t kCopyMemoryTargetAddrInIdx = 0; +constexpr uint32_t kCopyMemorySourceAddrInIdx = 1; +constexpr uint32_t kLoadSourceAddrInIdx = 0; +constexpr uint32_t kDebugDeclareOperandVariableIndex = 5; +constexpr uint32_t kGlobalVariableVariableIndex = 12; +constexpr uint32_t kExtInstSetInIdx = 0; +constexpr uint32_t kExtInstOpInIdx = 1; +constexpr uint32_t kInterpolantInIdx = 2; +constexpr uint32_t kCooperativeMatrixLoadSourceAddrInIdx = 0; + +// Sorting functor to present annotation instructions in an easy-to-process +// order. The functor orders by opcode first and falls back on unique id +// ordering if both instructions have the same opcode. +// +// Desired priority: +// spv::Op::OpGroupDecorate +// spv::Op::OpGroupMemberDecorate +// spv::Op::OpDecorate +// spv::Op::OpMemberDecorate +// spv::Op::OpDecorateId +// spv::Op::OpDecorateStringGOOGLE +// spv::Op::OpDecorationGroup +struct DecorationLess { + bool operator()(const Instruction* lhs, const Instruction* rhs) const { + assert(lhs && rhs); + spv::Op lhsOp = lhs->opcode(); + spv::Op rhsOp = rhs->opcode(); + if (lhsOp != rhsOp) { +#define PRIORITY_CASE(opcode) \ + if (lhsOp == opcode && rhsOp != opcode) return true; \ + if (rhsOp == opcode && lhsOp != opcode) return false; + // OpGroupDecorate and OpGroupMember decorate are highest priority to + // eliminate dead targets early and simplify subsequent checks. + PRIORITY_CASE(spv::Op::OpGroupDecorate) + PRIORITY_CASE(spv::Op::OpGroupMemberDecorate) + PRIORITY_CASE(spv::Op::OpDecorate) + PRIORITY_CASE(spv::Op::OpMemberDecorate) + PRIORITY_CASE(spv::Op::OpDecorateId) + PRIORITY_CASE(spv::Op::OpDecorateStringGOOGLE) + // OpDecorationGroup is lowest priority to ensure use/def chains remain + // usable for instructions that target this group. + PRIORITY_CASE(spv::Op::OpDecorationGroup) +#undef PRIORITY_CASE + } + + // Fall back to maintain total ordering (compare unique ids). + return *lhs < *rhs; + } +}; + +} // namespace + +bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, + spv::StorageClass storageClass) { + if (varId == 0) return false; + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + const spv::Op op = varInst->opcode(); + if (op != spv::Op::OpVariable) return false; + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (varTypeInst->opcode() != spv::Op::OpTypePointer) return false; + return spv::StorageClass(varTypeInst->GetSingleWordInOperand( + kTypePointerStorageClassInIdx)) == storageClass; +} + +bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) { + if (IsVarOfStorage(varId, spv::StorageClass::Function)) { + return true; + } + + if (!IsVarOfStorage(varId, spv::StorageClass::Private) && + !IsVarOfStorage(varId, spv::StorageClass::Workgroup)) { + return false; + } + + // For a variable in the Private or WorkGroup storage class, the variable will + // get a new instance for every call to an entry point. If the entry point + // does not have a call, then no other function can read or write to that + // instance of the variable. + return IsEntryPointWithNoCalls(func); +} + +void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { + get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) { + // If the user is not a part of |func|, skip it. + BasicBlock* blk = context()->get_instr_block(user); + if (blk && blk->GetParent() != func) return; + + switch (user->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpCopyObject: + this->AddStores(func, user->result_id()); + break; + case spv::Op::OpLoad: + break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) { + AddToWorklist(user); + } + break; + // If default, assume it stores e.g. frexp, modf, function call + case spv::Op::OpStore: { + const uint32_t kStoreTargetAddrInIdx = 0; + if (user->GetSingleWordInOperand(kStoreTargetAddrInIdx) == ptrId) + AddToWorklist(user); + break; + } + default: + AddToWorklist(user); + break; + } + }); +} + +bool AggressiveDCEPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const std::string extName = ei.GetInOperand(0).AsString(); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + (extension_name != "NonSemantic.Shader.DebugInfo.100") && + (extension_name != "NonSemantic.DebugPrintf")) { + return false; + } + } + return true; +} + +bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { + const uint32_t tId = inst->GetSingleWordInOperand(0); + Instruction* tInst = get_def_use_mgr()->GetDef(tId); + if (IsAnnotationInst(tInst->opcode())) { + // This must be a decoration group. We go through annotations in a specific + // order. So if this is not used by any group or group member decorates, it + // is dead. + assert(tInst->opcode() == spv::Op::OpDecorationGroup); + bool dead = true; + get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) { + if (user->opcode() == spv::Op::OpGroupDecorate || + user->opcode() == spv::Op::OpGroupMemberDecorate) + dead = false; + }); + return dead; + } + return !IsLive(tInst); +} + +void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { + // Only process locals + if (!IsLocalVar(varId, func)) return; + // Return if already processed + if (live_local_vars_.find(varId) != live_local_vars_.end()) return; + // Mark all stores to varId as live + AddStores(func, varId); + // Cache varId as processed + live_local_vars_.insert(varId); +} + +void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) { + std::unique_ptr newBranch( + new Instruction(context(), spv::Op::OpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}})); + context()->AnalyzeDefUse(&*newBranch); + context()->set_instr_block(&*newBranch, bp); + bp->AddInstruction(std::move(newBranch)); +} + +void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( + Instruction* mergeInst) { + assert(mergeInst->opcode() == spv::Op::OpSelectionMerge || + mergeInst->opcode() == spv::Op::OpLoopMerge); + + BasicBlock* header = context()->get_instr_block(mergeInst); + const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0); + get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) { + if (!user->IsBranch()) return; + BasicBlock* block = context()->get_instr_block(user); + if (BlockIsInConstruct(header, block)) { + // This is a break from the loop. + AddToWorklist(user); + // Add branch's merge if there is one. + Instruction* userMerge = GetMergeInstruction(user); + if (userMerge != nullptr) AddToWorklist(userMerge); + } + }); + + if (mergeInst->opcode() != spv::Op::OpLoopMerge) { + return; + } + + // For loops we need to find the continues as well. + const uint32_t contId = + mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); + get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) { + spv::Op op = user->opcode(); + if (op == spv::Op::OpBranchConditional || op == spv::Op::OpSwitch) { + // A conditional branch or switch can only be a continue if it does not + // have a merge instruction or its merge block is not the continue block. + Instruction* hdrMerge = GetMergeInstruction(user); + if (hdrMerge != nullptr && + hdrMerge->opcode() == spv::Op::OpSelectionMerge) { + uint32_t hdrMergeId = + hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + if (hdrMergeId == contId) return; + // Need to mark merge instruction too + AddToWorklist(hdrMerge); + } + } else if (op == spv::Op::OpBranch) { + // An unconditional branch can only be a continue if it is not + // branching to its own merge block. + BasicBlock* blk = context()->get_instr_block(user); + Instruction* hdrBranch = GetHeaderBranch(blk); + if (hdrBranch == nullptr) return; + Instruction* hdrMerge = GetMergeInstruction(hdrBranch); + if (hdrMerge->opcode() == spv::Op::OpLoopMerge) return; + uint32_t hdrMergeId = + hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + if (contId == hdrMergeId) return; + } else { + return; + } + AddToWorklist(user); + }); +} + +bool AggressiveDCEPass::AggressiveDCE(Function* func) { + if (func->IsDeclaration()) return false; + std::list structured_order; + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order); + live_local_vars_.clear(); + InitializeWorkList(func, structured_order); + ProcessWorkList(func); + return KillDeadInstructions(func, structured_order); +} + +bool AggressiveDCEPass::KillDeadInstructions( + const Function* func, std::list& structured_order) { + bool modified = false; + for (auto bi = structured_order.begin(); bi != structured_order.end();) { + uint32_t merge_block_id = 0; + (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) { + if (IsLive(inst)) return; + if (inst->opcode() == spv::Op::OpLabel) return; + // If dead instruction is selection merge, remember merge block + // for new branch at end of block + if (inst->opcode() == spv::Op::OpSelectionMerge || + inst->opcode() == spv::Op::OpLoopMerge) + merge_block_id = inst->GetSingleWordInOperand(0); + to_kill_.push_back(inst); + modified = true; + }); + // If a structured if or loop was deleted, add a branch to its merge + // block, and traverse to the merge block and continue processing there. + // We know the block still exists because the label is not deleted. + if (merge_block_id != 0) { + AddBranch(merge_block_id, *bi); + for (++bi; (*bi)->id() != merge_block_id; ++bi) { + } + + auto merge_terminator = (*bi)->terminator(); + if (merge_terminator->opcode() == spv::Op::OpUnreachable) { + // The merge was unreachable. This is undefined behaviour so just + // return (or return an undef). Then mark the new return as live. + auto func_ret_type_inst = get_def_use_mgr()->GetDef(func->type_id()); + if (func_ret_type_inst->opcode() == spv::Op::OpTypeVoid) { + merge_terminator->SetOpcode(spv::Op::OpReturn); + } else { + // Find an undef for the return value and make sure it gets kept by + // the pass. + auto undef_id = Type2Undef(func->type_id()); + auto undef = get_def_use_mgr()->GetDef(undef_id); + live_insts_.Set(undef->unique_id()); + merge_terminator->SetOpcode(spv::Op::OpReturnValue); + merge_terminator->SetInOperands({{SPV_OPERAND_TYPE_ID, {undef_id}}}); + get_def_use_mgr()->AnalyzeInstUse(merge_terminator); + } + live_insts_.Set(merge_terminator->unique_id()); + } + } else { + Instruction* inst = (*bi)->terminator(); + if (!IsLive(inst)) { + // If the terminator is not live, this block has no live instructions, + // and it will be unreachable. + AddUnreachable(*bi); + } + ++bi; + } + } + return modified; +} + +void AggressiveDCEPass::ProcessWorkList(Function* func) { + while (!worklist_.empty()) { + Instruction* live_inst = worklist_.front(); + worklist_.pop(); + AddOperandsToWorkList(live_inst); + MarkBlockAsLive(live_inst); + MarkLoadedVariablesAsLive(func, live_inst); + AddDecorationsToWorkList(live_inst); + AddDebugInstructionsToWorkList(live_inst); + } +} + +void AggressiveDCEPass::AddDebugScopeToWorkList(const Instruction* inst) { + auto scope = inst->GetDebugScope(); + auto lex_scope_id = scope.GetLexicalScope(); + if (lex_scope_id != kNoDebugScope) + AddToWorklist(get_def_use_mgr()->GetDef(lex_scope_id)); + auto inlined_at_id = scope.GetInlinedAt(); + if (inlined_at_id != kNoInlinedAt) + AddToWorklist(get_def_use_mgr()->GetDef(inlined_at_id)); +} + +void AggressiveDCEPass::AddDebugInstructionsToWorkList( + const Instruction* inst) { + for (auto& line_inst : inst->dbg_line_insts()) { + if (line_inst.IsDebugLineInst()) { + AddOperandsToWorkList(&line_inst); + } + AddDebugScopeToWorkList(&line_inst); + } + AddDebugScopeToWorkList(inst); +} + +void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) { + // Add OpDecorateId instructions that apply to this instruction to the work + // list. We use the decoration manager to look through the group + // decorations to get to the OpDecorate* instructions themselves. + auto decorations = + get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false); + for (Instruction* dec : decorations) { + // We only care about OpDecorateId instructions because the are the only + // decorations that will reference an id that will have to be kept live + // because of that use. + if (dec->opcode() != spv::Op::OpDecorateId) { + continue; + } + if (spv::Decoration(dec->GetSingleWordInOperand(1)) == + spv::Decoration::HlslCounterBufferGOOGLE) { + // These decorations should not force the use id to be live. It will be + // removed if either the target or the in operand are dead. + continue; + } + AddToWorklist(dec); + } +} + +void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func, + Instruction* inst) { + std::vector live_variables = GetLoadedVariables(inst); + for (uint32_t var_id : live_variables) { + ProcessLoad(func, var_id); + } +} + +std::vector AggressiveDCEPass::GetLoadedVariables(Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall) { + return GetLoadedVariablesFromFunctionCall(inst); + } + uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst); + if (var_id == 0) { + return {}; + } + return {var_id}; +} + +uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls( + Instruction* inst) { + std::vector live_variables; + if (inst->IsAtomicWithLoad()) { + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + } + + switch (inst->opcode()) { + case spv::Op::OpLoad: + case spv::Op::OpImageTexelPointer: + return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx)); + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + return GetVariableId( + inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx)); + case spv::Op::OpExtInst: { + if (inst->GetSingleWordInOperand(kExtInstSetInIdx) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450()) { + auto ext_inst = inst->GetSingleWordInOperand(kExtInstOpInIdx); + switch (ext_inst) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return inst->GetSingleWordInOperand(kInterpolantInIdx); + } + } + break; + } + case spv::Op::OpCooperativeMatrixLoadNV: + case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeMatrixLoadTensorNV: + return GetVariableId( + inst->GetSingleWordInOperand(kCooperativeMatrixLoadSourceAddrInIdx)); + default: + break; + } + + switch (inst->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: + return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + case CommonDebugInfoDebugValue: { + analysis::DebugInfoManager* debug_info_mgr = + context()->get_debug_info_mgr(); + return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst); + } + default: + break; + } + return 0; +} + +std::vector AggressiveDCEPass::GetLoadedVariablesFromFunctionCall( + const Instruction* inst) { + assert(inst->opcode() == spv::Op::OpFunctionCall); + std::vector live_variables; + // NOTE: we should only be checking function call parameters here, not the + // function itself, however, `IsPtr` will trivially return false for + // OpFunction + inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) { + if (!IsPtr(*operand_id)) return; + uint32_t var_id = GetVariableId(*operand_id); + live_variables.push_back(var_id); + }); + return live_variables; +} + +uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) { + assert(IsPtr(ptr_id) && + "Cannot get the variable when input is not a pointer."); + uint32_t varId = 0; + (void)GetPtr(ptr_id, &varId); + return varId; +} + +void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) { + BasicBlock* basic_block = context()->get_instr_block(inst); + if (basic_block == nullptr) { + return; + } + + // If we intend to keep this instruction, we need the block label and + // block terminator to have a valid block for the instruction. + AddToWorklist(basic_block->GetLabelInst()); + + // We need to mark the successors blocks that follow as live. If this is + // header of the merge construct, the construct may be folded, but we will + // definitely need the merge label. If it is not a construct, the terminator + // must be live, and the successor blocks will be marked as live when + // processing the terminator. + uint32_t merge_id = basic_block->MergeBlockIdIfAny(); + if (merge_id == 0) { + AddToWorklist(basic_block->terminator()); + } else { + AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id)); + } + + // Mark the structured control flow constructs that contains this block as + // live. If |inst| is an instruction in the loop header, then it is part of + // the loop, so the loop construct must be live. We exclude the label because + // it does not matter how many times it is executed. This could be extended + // to more instructions, but we will need it for now. + if (inst->opcode() != spv::Op::OpLabel) + MarkLoopConstructAsLiveIfLoopHeader(basic_block); + + Instruction* next_branch_inst = GetBranchForNextHeader(basic_block); + if (next_branch_inst != nullptr) { + AddToWorklist(next_branch_inst); + Instruction* mergeInst = GetMergeInstruction(next_branch_inst); + AddToWorklist(mergeInst); + } + + if (inst->opcode() == spv::Op::OpLoopMerge || + inst->opcode() == spv::Op::OpSelectionMerge) { + AddBreaksAndContinuesToWorklist(inst); + } +} +void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader( + BasicBlock* basic_block) { + // If this is the header for a loop, then loop structure needs to keep as well + // because the loop header is also part of the loop. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst != nullptr) { + AddToWorklist(basic_block->terminator()); + AddToWorklist(merge_inst); + } +} + +void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) { + inst->ForEachInId([this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + AddToWorklist(inInst); + }); + if (inst->type_id() != 0) { + AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id())); + } +} + +void AggressiveDCEPass::InitializeWorkList( + Function* func, std::list& structured_order) { + AddToWorklist(&func->DefInst()); + MarkFunctionParameterAsLive(func); + MarkFirstBlockAsLive(func); + + // Add instructions with external side effects to the worklist. Also add + // branches that are not attached to a structured construct. + // TODO(s-perron): The handling of branch seems to be adhoc. This needs to be + // cleaned up. + for (auto& bi : structured_order) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + spv::Op op = ii->opcode(); + if (ii->IsBranch()) { + continue; + } + switch (op) { + case spv::Op::OpStore: { + uint32_t var_id = 0; + (void)GetPtr(&*ii, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: { + uint32_t var_id = 0; + uint32_t target_addr_id = + ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx); + (void)GetPtr(target_addr_id, &var_id); + if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii); + } break; + case spv::Op::OpLoopMerge: + case spv::Op::OpSelectionMerge: + case spv::Op::OpUnreachable: + break; + default: { + // Function calls, atomics, function params, function returns, etc. + if (!ii->IsOpcodeSafeToDelete()) { + AddToWorklist(&*ii); + } + } break; + } + } + } +} + +void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { + // Keep all execution modes. + for (auto& exec : get_module()->execution_modes()) { + AddToWorklist(&exec); + } + // Keep all entry points. + for (auto& entry : get_module()->entry_points()) { + if (!preserve_interface_) { + live_insts_.Set(entry.unique_id()); + // The actual function is live always. + AddToWorklist( + get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(1u))); + for (uint32_t i = 3; i < entry.NumInOperands(); ++i) { + auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); + auto storage_class = var->GetSingleWordInOperand(0u); + // Vulkan support outputs without an associated input, but not inputs + // without an associated output. Don't remove outputs unless explicitly + // allowed. + if (!remove_outputs_ && + spv::StorageClass(storage_class) == spv::StorageClass::Output) { + AddToWorklist(var); + } + } + } else { + AddToWorklist(&entry); + } + } + for (auto& anno : get_module()->annotations()) { + if (anno.opcode() == spv::Op::OpDecorate) { + // Keep workgroup size. + if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::BuiltIn && + spv::BuiltIn(anno.GetSingleWordInOperand(2u)) == + spv::BuiltIn::WorkgroupSize) { + AddToWorklist(&anno); + } + + if (context()->preserve_bindings()) { + // Keep all bindings. + if ((spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::DescriptorSet) || + (spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::Binding)) { + AddToWorklist(&anno); + } + } + + if (context()->preserve_spec_constants()) { + // Keep all specialization constant instructions + if (spv::Decoration(anno.GetSingleWordInOperand(1u)) == + spv::Decoration::SpecId) { + AddToWorklist(&anno); + } + } + } + } + + // For each DebugInfo GlobalVariable keep all operands except the Variable. + // Later, if the variable is killed with KillInst(), we will set the operand + // to DebugInfoNone. Create and save DebugInfoNone now for this possible + // later use. This is slightly unoptimal, but it avoids generating it during + // instruction killing when the module is not consistent. + bool debug_global_seen = false; + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) + continue; + debug_global_seen = true; + dbg.ForEachInId([this](const uint32_t* iid) { + Instruction* in_inst = get_def_use_mgr()->GetDef(*iid); + if (in_inst->opcode() == spv::Op::OpVariable) return; + AddToWorklist(in_inst); + }); + } + if (debug_global_seen) { + auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone(); + AddToWorklist(dbg_none); + } + + // Add top level DebugInfo to worklist + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + auto op = dbg.GetShader100DebugOpcode(); + if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit || + op == NonSemanticShaderDebugInfo100DebugEntryPoint || + op == NonSemanticShaderDebugInfo100DebugSource || + op == NonSemanticShaderDebugInfo100DebugSourceContinued) { + AddToWorklist(&dbg); + } + } +} + +Pass::Status AggressiveDCEPass::ProcessImpl() { + // Current functionality assumes shader capability + // TODO(greg-lunarg): Handle additional capabilities + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return Status::SuccessWithoutChange; + + // Current functionality assumes relaxed logical addressing (see + // instruction.h) + // TODO(greg-lunarg): Handle non-logical addressing + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses)) + return Status::SuccessWithoutChange; + + // The variable pointer extension is no longer needed to use the capability, + // so we have to look for the capability. + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointersStorageBuffer)) + return Status::SuccessWithoutChange; + + // If any extensions in the module are not explicitly supported, + // return unmodified. + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + + // Eliminate Dead functions. + bool modified = EliminateDeadFunctions(); + + InitializeModuleScopeLiveInstructions(); + + // Run |AggressiveDCE| on the remaining functions. The order does not matter, + // since |AggressiveDCE| is intra-procedural. This can mean that function + // will become dead if all function call to them are removed. These dead + // function will still be in the module after this pass. We expect this to be + // rare. + for (Function& fp : *context()->module()) { + modified |= AggressiveDCE(&fp); + } + + // If the decoration manager is kept live then the context will try to keep it + // up to date. ADCE deals with group decorations by changing the operands in + // |OpGroupDecorate| instruction directly without informing the decoration + // manager. This can put it in an invalid state which will cause an error + // when the context tries to update it. To avoid this problem invalidate + // the decoration manager upfront. + // + // We kill it at now because it is used when processing the entry point + // functions. + context()->InvalidateAnalyses(IRContext::Analysis::kAnalysisDecorations); + + // Process module-level instructions. Now that all live instructions have + // been marked, it is safe to remove dead global values. + modified |= ProcessGlobalValues(); + + assert((to_kill_.empty() || modified) && + "A dead instruction was identified, but no change recorded."); + + // Kill all dead instructions. + for (auto inst : to_kill_) { + context()->KillInst(inst); + } + + // Cleanup all CFG including all unreachable blocks. + for (Function& fp : *context()->module()) { + modified |= CFGCleanup(&fp); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool AggressiveDCEPass::EliminateDeadFunctions() { + // Identify live functions first. Those that are not live + // are dead. + std::unordered_set live_function_set; + ProcessFunction mark_live = [&live_function_set](Function* fp) { + live_function_set.insert(fp); + return false; + }; + context()->ProcessReachableCallTree(mark_live); + + bool modified = false; + for (auto funcIter = get_module()->begin(); + funcIter != get_module()->end();) { + if (live_function_set.count(&*funcIter) == 0) { + modified = true; + funcIter = + eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter); + } else { + ++funcIter; + } + } + + return modified; +} + +bool AggressiveDCEPass::ProcessGlobalValues() { + // Remove debug and annotation statements referencing dead instructions. + // This must be done before killing the instructions, otherwise there are + // dead objects in the def/use database. + bool modified = false; + Instruction* instruction = &*get_module()->debug2_begin(); + while (instruction) { + if (instruction->opcode() != spv::Op::OpName) { + instruction = instruction->NextNode(); + continue; + } + + if (IsTargetDead(instruction)) { + instruction = context()->KillInst(instruction); + modified = true; + } else { + instruction = instruction->NextNode(); + } + } + + // This code removes all unnecessary decorations safely (see #1174). It also + // does so in a more efficient manner than deleting them only as the targets + // are deleted. + std::vector annotations; + for (auto& inst : get_module()->annotations()) annotations.push_back(&inst); + std::sort(annotations.begin(), annotations.end(), DecorationLess()); + for (auto annotation : annotations) { + switch (annotation->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpMemberDecorate: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorateStringGOOGLE: + if (IsTargetDead(annotation)) { + context()->KillInst(annotation); + modified = true; + } + break; + case spv::Op::OpDecorateId: + if (IsTargetDead(annotation)) { + context()->KillInst(annotation); + modified = true; + } else { + if (spv::Decoration(annotation->GetSingleWordInOperand(1)) == + spv::Decoration::HlslCounterBufferGOOGLE) { + // HlslCounterBuffer will reference an id other than the target. + // If that id is dead, then the decoration can be removed as well. + uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2); + Instruction* counter_buffer_inst = + get_def_use_mgr()->GetDef(counter_buffer_id); + if (!IsLive(counter_buffer_inst)) { + context()->KillInst(annotation); + modified = true; + } + } + } + break; + case spv::Op::OpGroupDecorate: { + // Go through the targets of this group decorate. Remove each dead + // target. If all targets are dead, remove this decoration. + bool dead = true; + bool removed_operand = false; + for (uint32_t i = 1; i < annotation->NumOperands();) { + Instruction* opInst = + get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); + if (!IsLive(opInst)) { + // Don't increment |i|. + annotation->RemoveOperand(i); + modified = true; + removed_operand = true; + } else { + i++; + dead = false; + } + } + if (dead) { + context()->KillInst(annotation); + modified = true; + } else if (removed_operand) { + context()->UpdateDefUse(annotation); + } + break; + } + case spv::Op::OpGroupMemberDecorate: { + // Go through the targets of this group member decorate. Remove each + // dead target (and member index). If all targets are dead, remove this + // decoration. + bool dead = true; + bool removed_operand = false; + for (uint32_t i = 1; i < annotation->NumOperands();) { + Instruction* opInst = + get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); + if (!IsLive(opInst)) { + // Don't increment |i|. + annotation->RemoveOperand(i + 1); + annotation->RemoveOperand(i); + modified = true; + removed_operand = true; + } else { + i += 2; + dead = false; + } + } + if (dead) { + context()->KillInst(annotation); + modified = true; + } else if (removed_operand) { + context()->UpdateDefUse(annotation); + } + break; + } + case spv::Op::OpDecorationGroup: + // By the time we hit decoration groups we've checked everything that + // can target them. So if they have no uses they must be dead. + if (get_def_use_mgr()->NumUsers(annotation) == 0) { + context()->KillInst(annotation); + modified = true; + } + break; + default: + assert(false); + break; + } + } + + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (IsLive(&dbg)) continue; + // Save GlobalVariable if its variable is live, otherwise null out variable + // index + if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { + auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); + Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); + if (IsLive(var_inst)) continue; + context()->ForgetUses(&dbg); + dbg.SetOperand( + kGlobalVariableVariableIndex, + {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()}); + context()->AnalyzeUses(&dbg); + continue; + } + to_kill_.push_back(&dbg); + modified = true; + } + + // Since ADCE is disabled for non-shaders, we don't check for export linkage + // attributes here. + for (auto& val : get_module()->types_values()) { + if (!IsLive(&val)) { + // Save forwarded pointer if pointer is live since closure does not mark + // this live as it does not have a result id. This is a little too + // conservative since it is not known if the structure type that needed + // it is still live. TODO(greg-lunarg): Only save if needed. + if (val.opcode() == spv::Op::OpTypeForwardPointer) { + uint32_t ptr_ty_id = val.GetSingleWordInOperand(0); + Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); + if (IsLive(ptr_ty_inst)) continue; + } + to_kill_.push_back(&val); + modified = true; + } + } + + if (!preserve_interface_) { + // Remove the dead interface variables from the entry point interface list. + for (auto& entry : get_module()->entry_points()) { + std::vector new_operands; + for (uint32_t i = 0; i < entry.NumInOperands(); ++i) { + if (i < 3) { + // Execution model, function id and name are always valid. + new_operands.push_back(entry.GetInOperand(i)); + } else { + auto* var = + get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); + if (IsLive(var)) { + new_operands.push_back(entry.GetInOperand(i)); + } + } + } + if (new_operands.size() != entry.NumInOperands()) { + entry.SetInOperands(std::move(new_operands)); + get_def_use_mgr()->UpdateDefUse(&entry); + } + } + } + + return modified; +} + +Pass::Status AggressiveDCEPass::Process() { + // Initialize extensions allowlist + InitExtensions(); + return ProcessImpl(); +} + +void AggressiveDCEPass::InitExtensions() { + extensions_allowlist_.clear(); + + // clang-format off + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + "SPV_KHR_shader_clock", + "SPV_KHR_vulkan_memory_model", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control", + }); + // clang-format on +} + +Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + BasicBlock* header_block = GetHeaderBlock(blk); + if (header_block == nullptr) { + return nullptr; + } + return header_block->terminator(); +} + +BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const { + if (blk == nullptr) { + return nullptr; + } + + BasicBlock* header_block = nullptr; + if (blk->IsLoopHeader()) { + header_block = blk; + } else { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + header_block = context()->get_instr_block(header); + } + return header_block; +} + +Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return nullptr; + } + return bb->GetMergeInst(); +} + +Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) { + if (blk == nullptr) { + return nullptr; + } + + if (blk->IsLoopHeader()) { + uint32_t header = + context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id()); + blk = context()->get_instr_block(header); + } + return GetHeaderBranch(blk); +} + +void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) { + func->ForEachParam( + [this](const Instruction* param) { + AddToWorklist(const_cast(param)); + }, + false); +} + +bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block, + BasicBlock* bb) { + if (bb == nullptr || header_block == nullptr) { + return false; + } + + uint32_t current_header = bb->id(); + while (current_header != 0) { + if (current_header == header_block->id()) return true; + current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct( + current_header); + } + return false; +} + +bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) { + auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id()); + if (cached_result != entry_point_with_no_calls_cache_.end()) { + return cached_result->second; + } + bool result = IsEntryPoint(func) && !HasCall(func); + entry_point_with_no_calls_cache_[func->result_id()] = result; + return result; +} + +bool AggressiveDCEPass::IsEntryPoint(Function* func) { + for (const Instruction& entry_point : get_module()->entry_points()) { + uint32_t entry_point_id = + entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + if (entry_point_id == func->result_id()) { + return true; + } + } + return false; +} + +bool AggressiveDCEPass::HasCall(Function* func) { + return !func->WhileEachInst([](Instruction* inst) { + return inst->opcode() != spv::Op::OpFunctionCall; + }); +} + +void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) { + BasicBlock* first_block = &*func->begin(); + MarkBlockAsLive(first_block->GetLabelInst()); +} + +void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) { + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + builder.AddUnreachable(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h new file mode 100644 index 00000000..fbe08ad0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h @@ -0,0 +1,265 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ +#define SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class AggressiveDCEPass : public MemPass { + using cbb_ptr = const BasicBlock*; + + public: + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + + AggressiveDCEPass(bool preserve_interface = false, + bool remove_outputs = false) + : preserve_interface_(preserve_interface), + remove_outputs_(remove_outputs) {} + + const char* name() const override { return "eliminate-dead-code-aggressive"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Preserve entry point interface if true. All variables in interface + // will be marked live and will not be eliminated. This mode is needed by + // GPU-Assisted Validation instrumentation where a change in the interface + // is not allowed. + bool preserve_interface_; + + // Output variables can be removed from the interface if this is true. + // This is safe if the caller knows that the corresponding input variable + // in the following shader has been removed. It is false by default. + bool remove_outputs_; + + // Return true if |varId| is a variable of |storageClass|. |varId| must either + // be 0 or the result of an instruction. + bool IsVarOfStorage(uint32_t varId, spv::StorageClass storageClass); + + // Return true if the instance of the variable |varId| can only be access in + // |func|. For example, a function scope variable, or a private variable + // where |func| is an entry point with no function calls. + bool IsLocalVar(uint32_t varId, Function* func); + + // Return true if |inst| is marked live. + bool IsLive(const Instruction* inst) const { + return live_insts_.Get(inst->unique_id()); + } + + // Adds entry points, execution modes and workgroup size decorations to the + // worklist for processing with the first function. + void InitializeModuleScopeLiveInstructions(); + + // Add |inst| to worklist_ and live_insts_. + void AddToWorklist(Instruction* inst) { + if (!live_insts_.Set(inst->unique_id())) { + worklist_.push(inst); + } + } + + // Add all store instruction which use |ptrId|, directly or indirectly, + // to the live instruction worklist. + void AddStores(Function* func, uint32_t ptrId); + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are supported by this pass. + bool AllExtensionsSupported() const; + + // Returns true if the target of |inst| is dead. An instruction is dead if + // its result id is used in decoration or debug instructions only. |inst| is + // assumed to be OpName, OpMemberName or an annotation instruction. + bool IsTargetDead(Instruction* inst); + + // If |varId| is local, mark all stores of varId as live. + void ProcessLoad(Function* func, uint32_t varId); + + // Add branch to |labelId| to end of block |bp|. + void AddBranch(uint32_t labelId, BasicBlock* bp); + + // Add all break and continue branches in the construct associated with + // |mergeInst| to worklist if not already live + void AddBreaksAndContinuesToWorklist(Instruction* mergeInst); + + // Eliminates dead debug2 and annotation instructions. Marks dead globals for + // removal (e.g. types, constants and variables). + bool ProcessGlobalValues(); + + // Erases functions that are unreachable from the entry points of the module. + bool EliminateDeadFunctions(); + + // For function |func|, mark all Stores to non-function-scope variables + // and block terminating instructions as live. Recursively mark the values + // they use. When complete, mark any non-live instructions to be deleted. + // Returns true if the function has been modified. + // + // Note: This function does not delete useless control structures. All + // existing control structures will remain. This can leave not-insignificant + // sequences of ultimately useless code. + // TODO(): Remove useless control constructs. + bool AggressiveDCE(Function* func); + + Pass::Status ProcessImpl(); + + // Adds instructions which must be kept because of they have side-effects + // that ADCE cannot model to the work list. + void InitializeWorkList(Function* func, + std::list& structured_order); + + // Process each instruction in the work list by marking any instruction that + // that it depends on as live, and adding it to the work list. The work list + // will be empty at the end. + void ProcessWorkList(Function* func); + + // Kills any instructions in |func| that have not been marked as live. + bool KillDeadInstructions(const Function* func, + std::list& structured_order); + + // Adds the instructions that define the operands of |inst| to the work list. + void AddOperandsToWorkList(const Instruction* inst); + + // Marks all of the labels and branch that inst requires as live. + void MarkBlockAsLive(Instruction* inst); + + // Marks any variables from which |inst| may require data as live. + void MarkLoadedVariablesAsLive(Function* func, Instruction* inst); + + // Returns the id of the variable that |ptr_id| point to. |ptr_id| must be a + // value whose type is a pointer. + uint32_t GetVariableId(uint32_t ptr_id); + + // Returns all of the ids for the variables from which |inst| will load data. + std::vector GetLoadedVariables(Instruction* inst); + + // Returns all of the ids for the variables from which |inst| will load data. + // The opcode of |inst| must be OpFunctionCall. + std::vector GetLoadedVariablesFromFunctionCall( + const Instruction* inst); + + // Returns the id of the variable from which |inst| will load data. |inst| + // must not be an OpFunctionCall. Returns 0 if no data is read or the + // variable cannot be determined. Note that in logical addressing mode the + // latter is not possible for function and private storage class because there + // cannot be variable pointers pointing to those storage classes. + uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst); + + // Adds all decorations of |inst| to the work list. + void AddDecorationsToWorkList(const Instruction* inst); + + // Adds DebugScope instruction associated with |inst| to the work list. + void AddDebugScopeToWorkList(const Instruction* inst); + + // Adds all debug instruction associated with |inst| to the work list. + void AddDebugInstructionsToWorkList(const Instruction* inst); + + // Marks all of the OpFunctionParameter instructions in |func| as live. + void MarkFunctionParameterAsLive(const Function* func); + + // Returns the terminator instruction in the header for the innermost + // construct that contains |blk|. Returns nullptr if no such header exists. + Instruction* GetHeaderBranch(BasicBlock* blk); + + // Returns the header for the innermost construct that contains |blk|. A loop + // header will be its own header. Returns nullptr if no such header exists. + BasicBlock* GetHeaderBlock(BasicBlock* blk) const; + + // Returns the same as |GetHeaderBlock| except if |blk| is a loop header it + // will return the header of the next enclosing construct. Returns nullptr if + // no such header exists. + Instruction* GetBranchForNextHeader(BasicBlock* blk); + + // Returns the merge instruction in the same basic block as |inst|. Returns + // nullptr if one does not exist. + Instruction* GetMergeInstruction(Instruction* inst); + + // Returns true if |bb| is in the construct with header |header_block|. + bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb); + + // Returns true if |func| is an entry point that does not have any function + // calls. + bool IsEntryPointWithNoCalls(Function* func); + + // Returns true if |func| is an entry point. + bool IsEntryPoint(Function* func); + + // Returns true if |func| contains a function call. + bool HasCall(Function* func); + + // Marks the first block, which is the entry block, in |func| as live. + void MarkFirstBlockAsLive(Function* func); + + // Adds an OpUnreachable instruction at the end of |block|. + void AddUnreachable(BasicBlock*& block); + + // Marks the OpLoopMerge and the terminator in |basic_block| as live if + // |basic_block| is a loop header. + void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block); + + // The cached results for |IsEntryPointWithNoCalls|. It maps the function's + // result id to the return value. + std::unordered_map entry_point_with_no_calls_cache_; + + // Live Instruction Worklist. An instruction is added to this list + // if it might have a side effect, either directly or indirectly. + // If we don't know, then add it to this list. Instructions are + // removed from this list as the algorithm traces side effects, + // building up the live instructions set |live_insts_|. + std::queue worklist_; + + // Live Instructions + utils::BitVector live_insts_; + + // Live Local Variables + std::unordered_set live_local_vars_; + + // List of instructions to delete. Deletion is delayed until debug and + // annotation instructions are processed. + std::vector to_kill_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.cpp new file mode 100644 index 00000000..a314567f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.cpp @@ -0,0 +1,980 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/amd_ext_to_khr.h" + +#include +#include + +#include "ir_builder.h" +#include "source/opt/ir_context.h" +#include "spv-amd-shader-ballot.insts.inc" +#include "type_manager.h" + +namespace spvtools { +namespace opt { +namespace { + +enum AmdShaderBallotExtOpcodes { + AmdShaderBallotSwizzleInvocationsAMD = 1, + AmdShaderBallotSwizzleInvocationsMaskedAMD = 2, + AmdShaderBallotWriteInvocationAMD = 3, + AmdShaderBallotMbcntAMD = 4 +}; + +enum AmdShaderTrinaryMinMaxExtOpCodes { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 +}; + +enum AmdGcnShader { CubeFaceCoordAMD = 2, CubeFaceIndexAMD = 1, TimeAMD = 3 }; + +analysis::Type* GetUIntType(IRContext* ctx) { + analysis::Integer int_type(32, false); + return ctx->get_type_mgr()->GetRegisteredType(&int_type); +} + +// Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where +// |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450 +// extended instruction set that corresponds to the trinary instruction being +// replaced. +template +bool ReplaceTrinaryMinMax(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t op1 = inst->GetSingleWordInOperand(2); + uint32_t op2 = inst->GetSingleWordInOperand(3); + uint32_t op3 = inst->GetSingleWordInOperand(4); + + Instruction* temp = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2}); + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); + new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(opcode)}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {temp->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {op3}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that replaces |mid(a,b,c)| by |clamp(a, min(b,c), +// max(b,c)|. The three parameters are the opcode that correspond to the min, +// max, and clamp operations for the type of the instruction being replaced. +template +bool ReplaceTrinaryMid(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t op1 = inst->GetSingleWordInOperand(2); + uint32_t op2 = inst->GetSingleWordInOperand(3); + uint32_t op3 = inst->GetSingleWordInOperand(4); + + Instruction* min = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, static_cast(min_opcode), + {op2, op3}); + Instruction* max = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, static_cast(max_opcode), + {op2, op3}); + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); + new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(clamp_opcode)}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {op1}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {min->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {max->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the opcode with |opcode| and add +// the capabilities required. The folding rule assumes it is folding an +// OpGroup*NonUniformAMD instruction from the SPV_AMD_shader_ballot extension. +template +bool ReplaceGroupNonuniformOperationOpCode( + IRContext* ctx, Instruction* inst, + const std::vector&) { + switch (new_opcode) { + case spv::Op::OpGroupNonUniformIAdd: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformUMin: + case spv::Op::OpGroupNonUniformSMin: + case spv::Op::OpGroupNonUniformFMin: + case spv::Op::OpGroupNonUniformUMax: + case spv::Op::OpGroupNonUniformSMax: + case spv::Op::OpGroupNonUniformFMax: + break; + default: + assert( + false && + "Should be replacing with a group non uniform arithmetic operation."); + } + + switch (inst->opcode()) { + case spv::Op::OpGroupIAddNonUniformAMD: + case spv::Op::OpGroupFAddNonUniformAMD: + case spv::Op::OpGroupUMinNonUniformAMD: + case spv::Op::OpGroupSMinNonUniformAMD: + case spv::Op::OpGroupFMinNonUniformAMD: + case spv::Op::OpGroupUMaxNonUniformAMD: + case spv::Op::OpGroupSMaxNonUniformAMD: + case spv::Op::OpGroupFMaxNonUniformAMD: + break; + default: + assert(false && + "Should be replacing a group non uniform arithmetic operation."); + } + + ctx->AddCapability(spv::Capability::GroupNonUniformArithmetic); + inst->SetOpcode(new_opcode); + return true; +} + +// Returns a folding rule that will replace the SwizzleInvocationsAMD extended +// instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %offset = OpConstantComposite %v3uint %x %y %z %w +// %result = OpExtInst %type %1 SwizzleInvocationsAMD %data %offset +// +// is replaced with +// +// potentially new constants and types +// +// clang-format off +// %uint_max = OpConstant %uint 0xFFFFFFFF +// %v4uint = OpTypeVector %uint 4 +// %ballot_value = OpConstantComposite %v4uint %uint_max %uint_max %uint_max %uint_max +// %null = OpConstantNull %type +// clang-format on +// +// and the following code in the function body +// +// clang-format off +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %quad_idx = OpBitwiseAnd %uint %id %uint_3 +// %quad_ldr = OpBitwiseXor %uint %id %quad_idx +// %my_offset = OpVectorExtractDynamic %uint %offset %quad_idx +// %target_inv = OpIAdd %uint %quad_ldr %my_offset +// %is_active = OpGroupNonUniformBallotBitExtract %bool %uint_3 %ballot_value %target_inv +// %shuffle = OpGroupNonUniformShuffle %type %uint_3 %data %target_inv +// %result = OpSelect %type %is_active %shuffle %null +// clang-format on +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + ctx->AddExtension("SPV_KHR_shader_ballot"); + ctx->AddCapability(spv::Capability::GroupNonUniformBallot); + ctx->AddCapability(spv::Capability::GroupNonUniformShuffle); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t data_id = inst->GetSingleWordInOperand(2); + uint32_t offset_id = inst->GetSingleWordInOperand(3); + + // Get the subgroup invocation id. + uint32_t var_id = ctx->GetBuiltinInputVarId( + uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + + Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + + uint32_t quad_mask = ir_builder.GetUintConstantId(3); + + // This gives the offset in the group of 4 of this invocation. + Instruction* quad_idx = ir_builder.AddBinaryOp( + uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask); + + // Get the invocation id of the first invocation in the group of 4. + Instruction* quad_ldr = + ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor, + id->result_id(), quad_idx->result_id()); + + // Get the offset of the target invocation from the offset vector. + Instruction* my_offset = + ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic, + offset_id, quad_idx->result_id()); + + // Determine the index of the invocation to read from. + Instruction* target_inv = + ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd, + quad_ldr->result_id(), my_offset->result_id()); + + // Do the group operations + uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + uint32_t subgroup_scope = + ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup)); + const auto* ballot_value_const = const_mgr->GetConstant( + type_mgr->GetUIntVectorType(4), + {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + Instruction* ballot_value = + const_mgr->GetDefiningInstruction(ballot_value_const); + Instruction* is_active = ir_builder.AddNaryOp( + type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract, + {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + Instruction* shuffle = + ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle, + {subgroup_scope, data_id, target_inv->result_id()}); + + // Create the null constant to use in the select. + const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), + std::vector()); + Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + + // Build the select. + inst->SetOpcode(spv::Op::OpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the SwizzleInvocationsMaskedAMD +// extended instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %mask = OpConstantComposite %v3uint %uint_x %uint_y %uint_z +// %result = OpExtInst %uint %1 SwizzleInvocationsMaskedAMD %data %mask +// +// is replaced with +// +// potentially new constants and types +// +// clang-format off +// %uint_mask_extend = OpConstant %uint 0xFFFFFFE0 +// %uint_max = OpConstant %uint 0xFFFFFFFF +// %v4uint = OpTypeVector %uint 4 +// %ballot_value = OpConstantComposite %v4uint %uint_max %uint_max %uint_max %uint_max +// clang-format on +// +// and the following code in the function body +// +// clang-format off +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %and_mask = OpBitwiseOr %uint %uint_x %uint_mask_extend +// %and = OpBitwiseAnd %uint %id %and_mask +// %or = OpBitwiseOr %uint %and %uint_y +// %target_inv = OpBitwiseXor %uint %or %uint_z +// %is_active = OpGroupNonUniformBallotBitExtract %bool %uint_3 %ballot_value %target_inv +// %shuffle = OpGroupNonUniformShuffle %type %uint_3 %data %target_inv +// %result = OpSelect %type %is_active %shuffle %uint_0 +// clang-format on +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceSwizzleInvocationsMasked( + IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + ctx->AddCapability(spv::Capability::GroupNonUniformBallot); + ctx->AddCapability(spv::Capability::GroupNonUniformShuffle); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + // Get the operands to inst, and the components of the mask + uint32_t data_id = inst->GetSingleWordInOperand(2); + + Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3)); + assert(mask_inst->opcode() == spv::Op::OpConstantComposite && + "The mask is suppose to be a vector constant."); + assert(mask_inst->NumInOperands() == 3 && + "The mask is suppose to have 3 components."); + + uint32_t uint_x = mask_inst->GetSingleWordInOperand(0); + uint32_t uint_y = mask_inst->GetSingleWordInOperand(1); + uint32_t uint_z = mask_inst->GetSingleWordInOperand(2); + + // Get the subgroup invocation id. + uint32_t var_id = ctx->GetBuiltinInputVarId( + uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); + ctx->AddExtension("SPV_KHR_shader_ballot"); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + + Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + + // Do the bitwise operations. + uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0); + Instruction* and_mask = ir_builder.AddBinaryOp( + uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended); + Instruction* and_result = + ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd, + id->result_id(), and_mask->result_id()); + Instruction* or_result = ir_builder.AddBinaryOp( + uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y); + Instruction* target_inv = ir_builder.AddBinaryOp( + uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z); + + // Do the group operations + uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + uint32_t subgroup_scope = + ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup)); + const auto* ballot_value_const = const_mgr->GetConstant( + type_mgr->GetUIntVectorType(4), + {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + Instruction* ballot_value = + const_mgr->GetDefiningInstruction(ballot_value_const); + Instruction* is_active = ir_builder.AddNaryOp( + type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract, + {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + Instruction* shuffle = + ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle, + {subgroup_scope, data_id, target_inv->result_id()}); + + // Create the null constant to use in the select. + const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), + std::vector()); + Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + + // Build the select. + inst->SetOpcode(spv::Op::OpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the WriteInvocationAMD extended +// instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// clang-format off +// %result = OpExtInst %type %1 WriteInvocationAMD %input_value %write_value %invocation_index +// clang-format on +// +// with +// +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %cmp = OpIEqual %bool %id %invocation_index +// %result = OpSelect %type %cmp %write_value %input_value +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t var_id = ctx->GetBuiltinInputVarId( + uint32_t(spv::BuiltIn::SubgroupLocalInvocationId)); + ctx->AddCapability(spv::Capability::SubgroupBallotKHR); + ctx->AddExtension("SPV_KHR_shader_ballot"); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* t = + ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id); + analysis::Bool bool_type; + uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type); + Instruction* cmp = + ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(), + inst->GetSingleWordInOperand(4)); + + // Build a select. + inst->SetOpcode(spv::Op::OpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {cmp->result_id()}}); + new_operands.push_back(inst->GetInOperand(3)); + new_operands.push_back(inst->GetInOperand(2)); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the MbcntAMD extended instruction in +// the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %result = OpExtInst %uint %1 MbcntAMD %mask +// +// with +// +// Get SubgroupLtMask and convert the first 64-bits into a uint64_t because +// AMD's shader compiler expects a 64-bit integer mask. +// +// %var = OpLoad %v4uint %SubgroupLtMaskKHR +// %shuffle = OpVectorShuffle %v2uint %var %var 0 1 +// %cast = OpBitcast %ulong %shuffle +// +// Perform the mask and count the bits. +// +// %and = OpBitwiseAnd %ulong %cast %mask +// %result = OpBitCount %uint %and +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceMbcnt(IRContext* context, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = context->get_type_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + uint32_t var_id = + context->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::SubgroupLtMask)); + assert(var_id != 0 && "Could not get SubgroupLtMask variable."); + context->AddCapability(spv::Capability::GroupNonUniformBallot); + Instruction* var_inst = def_use_mgr->GetDef(var_id); + Instruction* var_ptr_type = def_use_mgr->GetDef(var_inst->type_id()); + Instruction* var_type = + def_use_mgr->GetDef(var_ptr_type->GetSingleWordInOperand(1)); + assert(var_type->opcode() == spv::Op::OpTypeVector && + "Variable is suppose to be a vector of 4 ints"); + + // Get the type for the shuffle. + analysis::Vector temp_type(GetUIntType(context), 2); + const analysis::Type* shuffle_type = + context->get_type_mgr()->GetRegisteredType(&temp_type); + uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type); + + uint32_t mask_id = inst->GetSingleWordInOperand(2); + Instruction* mask_inst = def_use_mgr->GetDef(mask_id); + + // Testing with amd's shader compiler shows that a 64-bit mask is expected. + assert(type_mgr->GetType(mask_inst->type_id())->AsInteger() != nullptr); + assert(type_mgr->GetType(mask_inst->type_id())->AsInteger()->width() == 64); + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id); + Instruction* shuffle = ir_builder.AddVectorShuffle( + shuffle_type_id, load->result_id(), load->result_id(), {0, 1}); + Instruction* bitcast = ir_builder.AddUnaryOp( + mask_inst->type_id(), spv::Op::OpBitcast, shuffle->result_id()); + Instruction* t = + ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd, + bitcast->result_id(), mask_id); + + inst->SetOpcode(spv::Op::OpBitCount); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}}); + context->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the CubeFaceCoordAMD extended +// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding is +// successful. +// +// The instruction +// +// %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input +// +// with +// +// %x = OpCompositeExtract %float %input 0 +// %y = OpCompositeExtract %float %input 1 +// %z = OpCompositeExtract %float %input 2 +// %nx = OpFNegate %float %x +// %ny = OpFNegate %float %y +// %nz = OpFNegate %float %z +// %ax = OpExtInst %float %n_1 FAbs %x +// %ay = OpExtInst %float %n_1 FAbs %y +// %az = OpExtInst %float %n_1 FAbs %z +// %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax +// %amax = OpExtInst %float %n_1 FMax %az %amax_x_y +// %cubema = OpFMul %float %float_2 %amax +// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y +// %not_is_z_max = OpLogicalNot %bool %is_z_max +// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax +// %is_y_max = OpLogicalAnd %bool %not_is_z_max %y_gt_x +// %is_z_neg = OpFOrdLessThan %bool %z %float_0 +// %cubesc_case_1 = OpSelect %float %is_z_neg %nx %x +// %is_x_neg = OpFOrdLessThan %bool %x %float_0 +// %cubesc_case_2 = OpSelect %float %is_x_neg %z %nz +// %sel = OpSelect %float %is_y_max %x %cubesc_case_2 +// %cubesc = OpSelect %float %is_z_max %cubesc_case_1 %sel +// %is_y_neg = OpFOrdLessThan %bool %y %float_0 +// %cubetc_case_1 = OpSelect %float %is_y_neg %nz %z +// %cubetc = OpSelect %float %is_y_max %cubetc_case_1 %ny +// %cube = OpCompositeConstruct %v2float %cubesc %cubetc +// %denom = OpCompositeConstruct %v2float %cubema %cubema +// %div = OpFDiv %v2float %cube %denom +// %result = OpFAdd %v2float %div %const +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + uint32_t float_type_id = type_mgr->GetFloatTypeId(); + const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2); + uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type); + uint32_t bool_id = type_mgr->GetBoolTypeId(); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t input_id = inst->GetSingleWordInOperand(2); + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + // Get the constants that will be used. + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5); + const analysis::Constant* vec_const = + const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); + uint32_t vec_const_id = + const_mgr->GetDefiningInstruction(vec_const)->result_id(); + + // Extract the input values. + Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + + // Negate the input values. + Instruction* nx = + ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, x->result_id()); + Instruction* ny = + ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, y->result_id()); + Instruction* nz = + ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, z->result_id()); + + // Get the abolsute values of the inputs. + Instruction* ax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + Instruction* ay = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + Instruction* az = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + + // Find which values are negative. Used in later computations. + Instruction* is_z_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id); + Instruction* is_y_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id); + Instruction* is_x_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id); + + // Compute cubema + Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {ax->result_id(), ay->result_id()}); + Instruction* amax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {az->result_id(), amax_x_y->result_id()}); + Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul, + f2_const_id, amax->result_id()); + + // Do the comparisons needed for computing cubesc and cubetc. + Instruction* is_z_max = + ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, + az->result_id(), amax_x_y->result_id()); + Instruction* not_is_z_max = ir_builder.AddUnaryOp( + bool_id, spv::Op::OpLogicalNot, is_z_max->result_id()); + Instruction* y_gr_x = + ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, + ay->result_id(), ax->result_id()); + Instruction* is_y_max = + ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd, + not_is_z_max->result_id(), y_gr_x->result_id()); + + // Select the correct value for cubesc. + Instruction* cubesc_case_1 = ir_builder.AddSelect( + float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id()); + Instruction* cubesc_case_2 = ir_builder.AddSelect( + float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id()); + Instruction* sel = + ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(), + cubesc_case_2->result_id()); + Instruction* cubesc = + ir_builder.AddSelect(float_type_id, is_z_max->result_id(), + cubesc_case_1->result_id(), sel->result_id()); + + // Select the correct value for cubetc. + Instruction* cubetc_case_1 = ir_builder.AddSelect( + float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id()); + Instruction* cubetc = + ir_builder.AddSelect(float_type_id, is_y_max->result_id(), + cubetc_case_1->result_id(), ny->result_id()); + + // Do the division + Instruction* cube = ir_builder.AddCompositeConstruct( + v2_float_type_id, {cubesc->result_id(), cubetc->result_id()}); + Instruction* denom = ir_builder.AddCompositeConstruct( + v2_float_type_id, {cubema->result_id(), cubema->result_id()}); + Instruction* div = ir_builder.AddBinaryOp( + v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id()); + + // Get the final result by adding 0.5 to |div|. + inst->SetOpcode(spv::Op::OpFAdd); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {div->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {vec_const_id}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the CubeFaceIndexAMD extended +// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding +// is successful. +// +// The instruction +// +// %result = OpExtInst %float %1 CubeFaceIndexAMD %input +// +// with +// +// %x = OpCompositeExtract %float %input 0 +// %y = OpCompositeExtract %float %input 1 +// %z = OpCompositeExtract %float %input 2 +// %ax = OpExtInst %float %n_1 FAbs %x +// %ay = OpExtInst %float %n_1 FAbs %y +// %az = OpExtInst %float %n_1 FAbs %z +// %is_z_neg = OpFOrdLessThan %bool %z %float_0 +// %is_y_neg = OpFOrdLessThan %bool %y %float_0 +// %is_x_neg = OpFOrdLessThan %bool %x %float_0 +// %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay +// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y +// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax +// %case_z = OpSelect %float %is_z_neg %float_5 %float4 +// %case_y = OpSelect %float %is_y_neg %float_3 %float2 +// %case_x = OpSelect %float %is_x_neg %float_1 %float0 +// %sel = OpSelect %float %y_gt_x %case_y %case_x +// %result = OpSelect %float %is_z_max %case_z %sel +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + uint32_t float_type_id = type_mgr->GetFloatTypeId(); + uint32_t bool_id = type_mgr->GetBoolTypeId(); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t input_id = inst->GetSingleWordInOperand(2); + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + // Get the constants that will be used. + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0); + uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0); + uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0); + + // Extract the input values. + Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + + // Get the absolute values of the inputs. + Instruction* ax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + Instruction* ay = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + Instruction* az = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + + // Find which values are negative. Used in later computations. + Instruction* is_z_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id); + Instruction* is_y_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id); + Instruction* is_x_neg = ir_builder.AddBinaryOp( + bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id); + + // Find the max value. + Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {ax->result_id(), ay->result_id()}); + Instruction* is_z_max = + ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, + az->result_id(), amax_x_y->result_id()); + Instruction* y_gr_x = + ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual, + ay->result_id(), ax->result_id()); + + // Get the value for each case. + Instruction* case_z = ir_builder.AddSelect( + float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id); + Instruction* case_y = ir_builder.AddSelect( + float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id); + Instruction* case_x = ir_builder.AddSelect( + float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id); + + // Select the correct case. + Instruction* sel = + ir_builder.AddSelect(float_type_id, y_gr_x->result_id(), + case_y->result_id(), case_x->result_id()); + + // Get the final result by adding 0.5 to |div|. + inst->SetOpcode(spv::Op::OpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_z_max->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {case_z->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {sel->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the TimeAMD extended instruction in the +// SPV_AMD_gcn_shader_ballot. It returns true if the folding is successful. +// It returns False, otherwise. +// +// The instruction +// +// %result = OpExtInst %uint64 %1 TimeAMD +// +// with +// +// %result = OpReadClockKHR %uint64 %uint_3 +// +// NOTE: TimeAMD uses subgroup scope (it is not a real time clock). +bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst, + const std::vector&) { + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + ctx->AddExtension("SPV_KHR_shader_clock"); + ctx->AddCapability(spv::Capability::ShaderClockKHR); + + inst->SetOpcode(spv::Op::OpReadClockKHR); + Instruction::OperandList args; + uint32_t subgroup_scope_id = + ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup)); + args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}}); + inst->SetInOperands(std::move(args)); + ctx->UpdateDefUse(inst); + + return true; +} + +class AmdExtFoldingRules : public FoldingRules { + public: + explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override { + rules_[spv::Op::OpGroupIAddNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupFAddNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupUMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupSMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupFMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupUMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupSMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[spv::Op::OpGroupFMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + + uint32_t extension_id = + context()->module()->GetExtInstImportId("SPV_AMD_shader_ballot"); + + if (extension_id != 0) { + ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsAMD}] + .push_back(ReplaceSwizzleInvocations); + ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsMaskedAMD}] + .push_back(ReplaceSwizzleInvocationsMasked); + ext_rules_[{extension_id, AmdShaderBallotWriteInvocationAMD}].push_back( + ReplaceWriteInvocation); + ext_rules_[{extension_id, AmdShaderBallotMbcntAMD}].push_back( + ReplaceMbcnt); + } + + extension_id = context()->module()->GetExtInstImportId( + "SPV_AMD_shader_trinary_minmax"); + + if (extension_id != 0) { + ext_rules_[{extension_id, FMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, UMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, SMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, FMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, UMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, SMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, FMid3AMD}].push_back( + ReplaceTrinaryMid); + ext_rules_[{extension_id, UMid3AMD}].push_back( + ReplaceTrinaryMid); + ext_rules_[{extension_id, SMid3AMD}].push_back( + ReplaceTrinaryMid); + } + + extension_id = + context()->module()->GetExtInstImportId("SPV_AMD_gcn_shader"); + + if (extension_id != 0) { + ext_rules_[{extension_id, CubeFaceCoordAMD}].push_back( + ReplaceCubeFaceCoord); + ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back( + ReplaceCubeFaceIndex); + ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD); + } + } +}; + +class AmdExtConstFoldingRules : public ConstantFoldingRules { + public: + AmdExtConstFoldingRules(IRContext* ctx) : ConstantFoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override {} +}; + +} // namespace + +Pass::Status AmdExtensionToKhrPass::Process() { + bool changed = false; + + // Traverse the body of the functions to replace instructions that require + // the extensions. + InstructionFolder folder( + context(), + std::unique_ptr(new AmdExtFoldingRules(context())), + MakeUnique(context())); + for (Function& func : *get_module()) { + func.ForEachInst([&changed, &folder](Instruction* inst) { + if (folder.FoldInstruction(inst)) { + changed = true; + } + }); + } + + // Now that instruction that require the extensions have been removed, we can + // remove the extension instructions. + std::set ext_to_remove = {"SPV_AMD_shader_ballot", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader"}; + + std::vector to_be_killed; + for (Instruction& inst : context()->module()->extensions()) { + if (inst.opcode() == spv::Op::OpExtension) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { + to_be_killed.push_back(&inst); + } + } + } + + for (Instruction& inst : context()->ext_inst_imports()) { + if (inst.opcode() == spv::Op::OpExtInstImport) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { + to_be_killed.push_back(&inst); + } + } + } + + for (Instruction* inst : to_be_killed) { + context()->KillInst(inst); + changed = true; + } + + // The replacements that take place use instructions that are missing before + // SPIR-V 1.3. If we changed something, we will have to make sure the version + // is at least SPIR-V 1.3 to make sure those instruction can be used. + if (changed) { + uint32_t version = get_module()->version(); + if (version < 0x00010300 /*1.3*/) { + get_module()->set_version(0x00010300); + } + } + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.h b/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.h new file mode 100644 index 00000000..6a39d953 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/amd_ext_to_khr.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_AMD_EXT_TO_KHR_H_ +#define SOURCE_OPT_AMD_EXT_TO_KHR_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and +// capabilities. +class AmdExtensionToKhrPass : public Pass { + public: + const char* name() const override { return "amd-ext-to-khr"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_AMD_EXT_TO_KHR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.cpp new file mode 100644 index 00000000..529e6846 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/analyze_live_input_pass.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status AnalyzeLiveInputPass::Process() { + // Current functionality assumes shader capability + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return Status::SuccessWithoutChange; + Pass::Status status = DoLiveInputAnalysis(); + return status; +} + +Pass::Status AnalyzeLiveInputPass::DoLiveInputAnalysis() { + // Current functionality only supports frag, tesc, tese or geom shaders. + // Report failure for any other stage. + auto stage = context()->GetStage(); + if (stage != spv::ExecutionModel::Fragment && + stage != spv::ExecutionModel::TessellationControl && + stage != spv::ExecutionModel::TessellationEvaluation && + stage != spv::ExecutionModel::Geometry) + return Status::Failure; + context()->get_liveness_mgr()->GetLiveness(live_locs_, live_builtins_); + return Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.h new file mode 100644 index 00000000..ab292eff --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/analyze_live_input_pass.h @@ -0,0 +1,57 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ANALYZE_LIVE_INPUT_H_ +#define SOURCE_OPT_ANALYZE_LIVE_INPUT_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class AnalyzeLiveInputPass : public Pass { + public: + explicit AnalyzeLiveInputPass(std::unordered_set* live_locs, + std::unordered_set* live_builtins) + : live_locs_(live_locs), live_builtins_(live_builtins) {} + + const char* name() const override { return "analyze-live-input"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Do live input analysis + Status DoLiveInputAnalysis(); + + std::unordered_set* live_locs_; + std::unordered_set* live_builtins_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ANALYZE_LIVE_INPUT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.cpp new file mode 100644 index 00000000..a9fc8e2f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.cpp @@ -0,0 +1,284 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/basic_block.h" + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1; +constexpr uint32_t kLoopMergeMergeBlockIdInIdx = 0; +constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0; +} // namespace + +BasicBlock* BasicBlock::Clone(IRContext* context) const { + BasicBlock* clone = new BasicBlock( + std::unique_ptr(GetLabelInst()->Clone(context))); + for (const auto& inst : insts_) { + // Use the incoming context + clone->AddInstruction(std::unique_ptr(inst.Clone(context))); + } + + if (context->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + for (auto& inst : *clone) { + context->set_instr_block(&inst, clone); + } + } + + return clone; +} + +const Instruction* BasicBlock::GetMergeInst() const { + const Instruction* result = nullptr; + // If it exists, the merge instruction immediately precedes the + // terminator. + auto iter = ctail(); + if (iter != cbegin()) { + --iter; + const auto opcode = iter->opcode(); + if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { + result = &*iter; + } + } + return result; +} + +Instruction* BasicBlock::GetMergeInst() { + Instruction* result = nullptr; + // If it exists, the merge instruction immediately precedes the + // terminator. + auto iter = tail(); + if (iter != begin()) { + --iter; + const auto opcode = iter->opcode(); + if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) { + result = &*iter; + } + } + return result; +} + +const Instruction* BasicBlock::GetLoopMergeInst() const { + if (auto* merge = GetMergeInst()) { + if (merge->opcode() == spv::Op::OpLoopMerge) { + return merge; + } + } + return nullptr; +} + +Instruction* BasicBlock::GetLoopMergeInst() { + if (auto* merge = GetMergeInst()) { + if (merge->opcode() == spv::Op::OpLoopMerge) { + return merge; + } + } + return nullptr; +} + +void BasicBlock::KillAllInsts(bool killLabel) { + ForEachInst([killLabel](Instruction* ip) { + if (killLabel || ip->opcode() != spv::Op::OpLabel) { + ip->context()->KillInst(ip); + } + }); +} + +void BasicBlock::ForEachSuccessorLabel( + const std::function& f) const { + WhileEachSuccessorLabel([f](const uint32_t l) { + f(l); + return true; + }); +} + +bool BasicBlock::WhileEachSuccessorLabel( + const std::function& f) const { + const auto br = &insts_.back(); + switch (br->opcode()) { + case spv::Op::OpBranch: + return f(br->GetOperand(0).words[0]); + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: { + bool is_first = true; + return br->WhileEachInId([&is_first, &f](const uint32_t* idp) { + if (!is_first) return f(*idp); + is_first = false; + return true; + }); + } + default: + return true; + } +} + +void BasicBlock::ForEachSuccessorLabel( + const std::function& f) { + auto br = &insts_.back(); + switch (br->opcode()) { + case spv::Op::OpBranch: { + uint32_t tmp_id = br->GetOperand(0).words[0]; + f(&tmp_id); + if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id}); + } break; + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: { + bool is_first = true; + br->ForEachInId([&is_first, &f](uint32_t* idp) { + if (!is_first) f(idp); + is_first = false; + }); + } break; + default: + break; + } +} + +bool BasicBlock::IsSuccessor(const BasicBlock* block) const { + uint32_t succId = block->id(); + bool isSuccessor = false; + ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) { + if (label == succId) isSuccessor = true; + }); + return isSuccessor; +} + +void BasicBlock::ForMergeAndContinueLabel( + const std::function& f) { + auto ii = insts_.end(); + --ii; + if (ii == insts_.begin()) return; + --ii; + if (ii->opcode() == spv::Op::OpSelectionMerge || + ii->opcode() == spv::Op::OpLoopMerge) { + ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); }); + } +} + +uint32_t BasicBlock::MergeBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t mbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == spv::Op::OpLoopMerge) { + mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); + } else if (merge_ii->opcode() == spv::Op::OpSelectionMerge) { + mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + } + } + + return mbid; +} + +uint32_t BasicBlock::MergeBlockId() const { + uint32_t mbid = MergeBlockIdIfAny(); + assert(mbid && "Expected block to have a corresponding merge block"); + return mbid; +} + +uint32_t BasicBlock::ContinueBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t cbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == spv::Op::OpLoopMerge) { + cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); + } + } + return cbid; +} + +uint32_t BasicBlock::ContinueBlockId() const { + uint32_t cbid = ContinueBlockIdIfAny(); + assert(cbid && "Expected block to have a corresponding continue target"); + return cbid; +} + +std::ostream& operator<<(std::ostream& str, const BasicBlock& block) { + str << block.PrettyPrint(); + return str; +} + +void BasicBlock::Dump() const { + std::cerr << "Basic block #" << id() << "\n" << *this << "\n "; +} + +std::string BasicBlock::PrettyPrint(uint32_t options) const { + std::ostringstream str; + ForEachInst([&str, options](const Instruction* inst) { + str << inst->PrettyPrint(options); + if (!spvOpcodeIsBlockTerminator(inst->opcode())) { + str << std::endl; + } + }); + return str.str(); +} + +BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id, + iterator iter) { + assert(!insts_.empty()); + + std::unique_ptr new_block_temp = MakeUnique( + MakeUnique(context, spv::Op::OpLabel, 0, label_id, + std::initializer_list{})); + BasicBlock* new_block = new_block_temp.get(); + function_->InsertBasicBlockAfter(std::move(new_block_temp), this); + + new_block->insts_.Splice(new_block->end(), &insts_, iter, end()); + assert(new_block->GetParent() == GetParent() && + "The parent should already be set appropriately."); + + context->AnalyzeDefUse(new_block->GetLabelInst()); + + // Update the phi nodes in the successor blocks to reference the new block id. + const_cast(new_block)->ForEachSuccessorLabel( + [new_block, this, context](const uint32_t label) { + BasicBlock* target_bb = context->get_instr_block(label); + target_bb->ForEachPhiInst( + [this, new_block, context](Instruction* phi_inst) { + bool changed = false; + for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) { + if (phi_inst->GetSingleWordInOperand(i) == this->id()) { + changed = true; + phi_inst->SetInOperand(i, {new_block->id()}); + } + } + + if (changed) { + context->UpdateDefUse(phi_inst); + } + }); + }); + + if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + context->set_instr_block(new_block->GetLabelInst(), new_block); + new_block->ForEachInst([new_block, context](Instruction* inst) { + context->set_instr_block(inst, new_block); + }); + } + + return new_block; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.h b/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.h new file mode 100644 index 00000000..24d5fceb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/basic_block.h @@ -0,0 +1,342 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines the language constructs for representing a SPIR-V +// module in memory. + +#ifndef SOURCE_OPT_BASIC_BLOCK_H_ +#define SOURCE_OPT_BASIC_BLOCK_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/instruction_list.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class Function; +class IRContext; + +// A SPIR-V basic block. +class BasicBlock { + public: + using iterator = InstructionList::iterator; + using const_iterator = InstructionList::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + // Creates a basic block with the given starting |label|. + inline explicit BasicBlock(std::unique_ptr label); + + explicit BasicBlock(const BasicBlock& bb) = delete; + + // Creates a clone of the basic block in the given |context| + // + // The parent function will default to null and needs to be explicitly set by + // the user. + // + // If the inst-to-block map in |context| is valid, then the new instructions + // will be inserted into the map. + BasicBlock* Clone(IRContext*) const; + + // Sets the enclosing function for this basic block. + void SetParent(Function* function) { function_ = function; } + + // Return the enclosing function + inline Function* GetParent() const { return function_; } + + // Appends an instruction to this basic block. + inline void AddInstruction(std::unique_ptr i); + + // Appends all of block's instructions (except label) to this block + inline void AddInstructions(BasicBlock* bp); + + // The pointer to the label starting this basic block. + std::unique_ptr& GetLabel() { return label_; } + + // The label starting this basic block. + Instruction* GetLabelInst() { return label_.get(); } + const Instruction* GetLabelInst() const { return label_.get(); } + + // Returns the merge instruction in this basic block, if it exists. + // Otherwise return null. May be used whenever tail() can be used. + const Instruction* GetMergeInst() const; + Instruction* GetMergeInst(); + + // Returns the OpLoopMerge instruction in this basic block, if it exists. + // Otherwise return null. May be used whenever tail() can be used. + const Instruction* GetLoopMergeInst() const; + Instruction* GetLoopMergeInst(); + + // Returns the id of the label at the top of this block + inline uint32_t id() const { return label_->result_id(); } + + iterator begin() { return insts_.begin(); } + iterator end() { return insts_.end(); } + const_iterator begin() const { return insts_.cbegin(); } + const_iterator end() const { return insts_.cend(); } + const_iterator cbegin() const { return insts_.cbegin(); } + const_iterator cend() const { return insts_.cend(); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(cend()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(cbegin()); + } + const_reverse_iterator crbegin() const { + return const_reverse_iterator(cend()); + } + const_reverse_iterator crend() const { + return const_reverse_iterator(cbegin()); + } + + // Returns an iterator pointing to the last instruction. This may only + // be used if this block has an instruction other than the OpLabel + // that defines it. + iterator tail() { + assert(!insts_.empty()); + return --end(); + } + + // Returns a const iterator, but othewrise similar to tail(). + const_iterator ctail() const { + assert(!insts_.empty()); + return --insts_.cend(); + } + + // Returns true if the basic block has at least one successor. + inline bool hasSuccessor() const { return ctail()->IsBranch(); } + + // Runs the given function |f| on each instruction in this basic block, and + // optionally on the debug line instructions that might precede them. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on each instruction in this basic block, and + // optionally on the debug line instructions that might precede them. If |f| + // returns false, iteration is terminated and this function returns false. + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on each Phi instruction in this basic block, + // and optionally on the debug line instructions that might precede them. + inline void ForEachPhiInst(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each Phi instruction in this basic block, + // and optionally on the debug line instructions that might precede them. If + // |f| returns false, iteration is terminated and this function return false. + inline bool WhileEachPhiInst(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each label id of each successor block + void ForEachSuccessorLabel( + const std::function& f) const; + + // Runs the given function |f| on each label id of each successor block. If + // |f| returns false, iteration is terminated and this function returns false. + bool WhileEachSuccessorLabel( + const std::function& f) const; + + // Runs the given function |f| on each label id of each successor block. + // Modifying the pointed value will change the branch taken by the basic + // block. It is the caller responsibility to update or invalidate the CFG. + void ForEachSuccessorLabel(const std::function& f); + + // Returns true if |block| is a direct successor of |this|. + bool IsSuccessor(const BasicBlock* block) const; + + // Runs the given function |f| on the merge and continue label, if any + void ForMergeAndContinueLabel(const std::function& f); + + // Returns true if this basic block has any Phi instructions. + bool HasPhiInstructions() { + return !WhileEachPhiInst([](Instruction*) { return false; }); + } + + // Return true if this block is a loop header block. + bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; } + + // Returns the ID of the merge block declared by a merge instruction in this + // block, if any. If none, returns zero. + uint32_t MergeBlockIdIfAny() const; + + // Returns MergeBlockIdIfAny() and asserts that it is non-zero. + uint32_t MergeBlockId() const; + + // Returns the ID of the continue block declared by a merge instruction in + // this block, if any. If none, returns zero. + uint32_t ContinueBlockIdIfAny() const; + + // Returns ContinueBlockIdIfAny() and asserts that it is non-zero. + uint32_t ContinueBlockId() const; + + // Returns the terminator instruction. Assumes the terminator exists. + Instruction* terminator() { return &*tail(); } + const Instruction* terminator() const { return &*ctail(); } + + // Returns true if this basic block exits this function and returns to its + // caller. + bool IsReturn() const { return ctail()->IsReturn(); } + + // Returns true if this basic block exits this function or aborts execution. + bool IsReturnOrAbort() const { return ctail()->IsReturnOrAbort(); } + + // Kill all instructions in this block. Whether or not to kill the label is + // indicated by |killLabel|. + void KillAllInsts(bool killLabel); + + // Splits this basic block into two. Returns a new basic block with label + // |label_id| containing the instructions from |iter| onwards. Instructions + // prior to |iter| remain in this basic block. The new block will be added + // to the function immediately after the original block. + BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id, + iterator iter); + + // Pretty-prints this basic block into a std::string by printing every + // instruction in it. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Dump this basic block on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + private: + // The enclosing function. + Function* function_; + // The label starting this basic block. + std::unique_ptr label_; + // Instructions inside this basic block, but not the OpLabel. + InstructionList insts_; +}; + +// Pretty-prints |block| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const BasicBlock& block); + +inline BasicBlock::BasicBlock(std::unique_ptr label) + : function_(nullptr), label_(std::move(label)) {} + +inline void BasicBlock::AddInstruction(std::unique_ptr i) { + insts_.push_back(std::move(i)); +} + +inline void BasicBlock::AddInstructions(BasicBlock* bp) { + auto bEnd = end(); + (void)bEnd.MoveBefore(&bp->insts_); +} + +inline bool BasicBlock::WhileEachInst( + const std::function& f, bool run_on_debug_line_insts) { + if (label_) { + if (!label_->WhileEachInst(f, run_on_debug_line_insts)) return false; + } + if (insts_.empty()) { + return true; + } + + Instruction* inst = &insts_.front(); + while (inst != nullptr) { + Instruction* next_instruction = inst->NextNode(); + if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; + inst = next_instruction; + } + return true; +} + +inline bool BasicBlock::WhileEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + if (label_) { + if (!static_cast(label_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) + return false; + } + for (const auto& inst : insts_) { + if (!static_cast(&inst)->WhileEachInst( + f, run_on_debug_line_insts)) + return false; + } + return true; +} + +inline void BasicBlock::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void BasicBlock::ForEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline bool BasicBlock::WhileEachPhiInst( + const std::function& f, bool run_on_debug_line_insts) { + if (insts_.empty()) { + return true; + } + + Instruction* inst = &insts_.front(); + while (inst != nullptr) { + Instruction* next_instruction = inst->NextNode(); + if (inst->opcode() != spv::Op::OpPhi) break; + if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; + inst = next_instruction; + } + return true; +} + +inline void BasicBlock::ForEachPhiInst( + const std::function& f, bool run_on_debug_line_insts) { + WhileEachPhiInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BASIC_BLOCK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.cpp new file mode 100644 index 00000000..d6c33e52 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/block_merge_pass.h" + +#include "source/opt/block_merge_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +bool BlockMergePass::MergeBlocks(Function* func) { + bool modified = false; + for (auto bi = func->begin(); bi != func->end();) { + // Don't bother trying to merge unreachable blocks. + if (context()->IsReachable(*bi) && + blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { + blockmergeutil::MergeWithSuccessor(context(), func, bi); + // Reprocess block. + modified = true; + } else { + ++bi; + } + } + return modified; +} + +Pass::Status BlockMergePass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +BlockMergePass::BlockMergePass() = default; + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.h new file mode 100644 index 00000000..aabf789f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_pass.h @@ -0,0 +1,62 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BLOCK_MERGE_PASS_H_ +#define SOURCE_OPT_BLOCK_MERGE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class BlockMergePass : public Pass { + public: + BlockMergePass(); + const char* name() const override { return "merge-blocks"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + + // Search |func| for blocks which have a single Branch to a block + // with no other predecessors. Merge these blocks into a single block. + bool MergeBlocks(Function* func); + +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BLOCK_MERGE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.cpp new file mode 100644 index 00000000..42f695f2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.cpp @@ -0,0 +1,233 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "block_merge_util.h" + +namespace spvtools { +namespace opt { +namespace blockmergeutil { +namespace { + +// Returns true if |block| contains a merge instruction. +bool IsHeader(BasicBlock* block) { return block->GetMergeInst() != nullptr; } + +// Returns true if |id| contains a merge instruction. +bool IsHeader(IRContext* context, uint32_t id) { + return IsHeader( + context->get_instr_block(context->get_def_use_mgr()->GetDef(id))); +} + +// Returns true if |id| is the merge target of a merge instruction. +bool IsMerge(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse( + id, [](Instruction* user, uint32_t index) { + spv::Op op = user->opcode(); + if ((op == spv::Op::OpLoopMerge || op == spv::Op::OpSelectionMerge) && + index == 0u) { + return false; + } + return true; + }); +} + +// Returns true if |block| is the merge target of a merge instruction. +bool IsMerge(IRContext* context, BasicBlock* block) { + return IsMerge(context, block->id()); +} + +// Returns true if |id| is the continue target of a merge instruction. +bool IsContinue(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse( + id, [](Instruction* user, uint32_t index) { + spv::Op op = user->opcode(); + if (op == spv::Op::OpLoopMerge && index == 1u) { + return false; + } + return true; + }); +} + +// Removes any OpPhi instructions in |block|, which should have exactly one +// predecessor, replacing uses of OpPhi ids with the ids associated with the +// predecessor. +void EliminateOpPhiInstructions(IRContext* context, BasicBlock* block) { + block->ForEachPhiInst([context](Instruction* phi) { + assert(2 == phi->NumInOperands() && + "A block can only have one predecessor for block merging to make " + "sense."); + context->ReplaceAllUsesWith(phi->result_id(), + phi->GetSingleWordInOperand(0)); + context->KillInst(phi); + }); +} + +} // Anonymous namespace + +bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { + // Find block with single successor which has no other predecessors. + auto ii = block->end(); + --ii; + Instruction* br = &*ii; + if (br->opcode() != spv::Op::OpBranch) { + return false; + } + + const uint32_t lab_id = br->GetSingleWordInOperand(0); + if (context->cfg()->preds(lab_id).size() != 1) { + return false; + } + + bool pred_is_merge = IsMerge(context, block); + bool succ_is_merge = IsMerge(context, lab_id); + if (pred_is_merge && succ_is_merge) { + // Cannot merge two merges together. + return false; + } + + // Note: This means that the instructions in a break block will execute as if + // they were still diverged according to the loop iteration. This restricts + // potential transformations an implementation may perform on the IR to match + // shader author expectations. Similarly, instructions in the loop construct + // cannot be moved into the continue construct unless it can be proven that + // invocations are always converged. + if (succ_is_merge && context->get_feature_mgr()->HasExtension( + kSPV_KHR_maximal_reconvergence)) { + return false; + } + + if (pred_is_merge && IsContinue(context, lab_id)) { + // Cannot merge a continue target with a merge block. + return false; + } + + Instruction* merge_inst = block->GetMergeInst(); + const bool pred_is_header = IsHeader(block); + if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { + bool succ_is_header = IsHeader(context, lab_id); + if (pred_is_header && succ_is_header) { + // Cannot merge two headers together when the successor is not the merge + // block of the predecessor. + return false; + } + + // If this is a header block and the successor is not its merge, we must + // be careful about which blocks we are willing to merge together. + // OpLoopMerge must be followed by a conditional or unconditional branch. + // The merge must be a loop merge because a selection merge cannot be + // followed by an unconditional branch. + BasicBlock* succ_block = context->get_instr_block(lab_id); + spv::Op succ_term_op = succ_block->terminator()->opcode(); + assert(merge_inst->opcode() == spv::Op::OpLoopMerge); + if (succ_term_op != spv::Op::OpBranch && + succ_term_op != spv::Op::OpBranchConditional) { + return false; + } + } + + if (succ_is_merge || IsContinue(context, lab_id)) { + auto* struct_cfg = context->GetStructuredCFGAnalysis(); + auto switch_block_id = struct_cfg->ContainingSwitch(block->id()); + if (switch_block_id) { + auto switch_merge_id = struct_cfg->SwitchMergeBlock(switch_block_id); + const auto* switch_inst = + &*block->GetParent()->FindBlock(switch_block_id)->tail(); + for (uint32_t i = 1; i < switch_inst->NumInOperands(); i += 2) { + auto target_id = switch_inst->GetSingleWordInOperand(i); + if (target_id == block->id() && target_id != switch_merge_id) { + // Case constructs must be structurally dominated by the OpSwitch. + // Since the successor is the merge/continue for another construct, + // merging the blocks would break that requirement. + return false; + } + } + } + } + + return true; +} + +void MergeWithSuccessor(IRContext* context, Function* func, + Function::iterator bi) { + assert(CanMergeWithSuccessor(context, &*bi) && + "Precondition failure for MergeWithSuccessor: it must be legal to " + "merge the block and its successor."); + + auto ii = bi->end(); + --ii; + Instruction* br = &*ii; + const uint32_t lab_id = br->GetSingleWordInOperand(0); + Instruction* merge_inst = bi->GetMergeInst(); + bool pred_is_header = IsHeader(&*bi); + + // Merge blocks. + context->KillInst(br); + auto sbi = bi; + for (; sbi != func->end(); ++sbi) + if (sbi->id() == lab_id) break; + // If bi is sbi's only predecessor, it dominates sbi and thus + // sbi must follow bi in func's ordering. + assert(sbi != func->end()); + + if (sbi->tail()->opcode() == spv::Op::OpSwitch && + sbi->MergeBlockIdIfAny() != 0) { + context->InvalidateAnalyses(IRContext::Analysis::kAnalysisStructuredCFG); + } + + // Update the inst-to-block mapping for the instructions in sbi. + for (auto& inst : *sbi) { + context->set_instr_block(&inst, &*bi); + } + + EliminateOpPhiInstructions(context, &*sbi); + + // Now actually move the instructions. + bi->AddInstructions(&*sbi); + + if (merge_inst) { + if (pred_is_header && lab_id == merge_inst->GetSingleWordInOperand(0u)) { + // Merging the header and merge blocks, so remove the structured control + // flow declaration. + context->KillInst(merge_inst); + } else { + // Move OpLine/OpNoLine information to merge_inst. This solves + // the validation error that OpLine is placed between OpLoopMerge + // and OpBranchConditional. + auto terminator = bi->terminator(); + auto& vec = terminator->dbg_line_insts(); + if (vec.size() > 0) { + merge_inst->ClearDbgLineInsts(); + auto& new_vec = merge_inst->dbg_line_insts(); + new_vec.insert(new_vec.end(), vec.begin(), vec.end()); + terminator->ClearDbgLineInsts(); + for (auto& l_inst : new_vec) + context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst); + } + // Clear debug scope of terminator to avoid DebugScope + // emitted between terminator and merge. + terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt)); + // Move the merge instruction to just before the terminator. + merge_inst->InsertBefore(terminator); + } + } + context->ReplaceAllUsesWith(lab_id, bi->id()); + context->KillInst(sbi->GetLabelInst()); + (void)sbi.Erase(); +} + +} // namespace blockmergeutil +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.h b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.h new file mode 100644 index 00000000..e71e3d6a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/block_merge_util.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BLOCK_MERGE_UTIL_H_ +#define SOURCE_OPT_BLOCK_MERGE_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for determining when it is safe to merge blocks, and for +// actually merging blocks, for use by various analyses and passes. +namespace blockmergeutil { + +// Returns true if and only if |block| has exactly one successor and merging +// this successor into |block| has no impact on the semantics or validity of the +// SPIR-V module. +bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block); + +// Requires that |bi| has a successor that can be safely merged into |bi|, and +// performs the merge. +void MergeWithSuccessor(IRContext* context, Function* func, + Function::iterator bi); + +} // namespace blockmergeutil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BLOCK_MERGE_UTIL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.cpp new file mode 100644 index 00000000..3b606dc2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/build_module.h" + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/ir_loader.h" +#include "source/table.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace { + +// Sets the module header for IrLoader. Meets the interface requirement of +// spvBinaryParse(). +spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t reserved) { + reinterpret_cast(builder)->SetModuleHeader( + magic, version, generator, id_bound, reserved); + return SPV_SUCCESS; +} + +// Processes a parsed instruction for IrLoader. Meets the interface requirement +// of spvBinaryParse(). +spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) { + if (reinterpret_cast(builder)->AddInstruction(inst)) { + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_BINARY; +} + +} // namespace + +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, + const size_t size) { + return BuildModule(env, consumer, binary, size, true); +} + +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, + const size_t size, + bool extra_line_tracking) { + auto context = spvContextCreate(env); + SetContextMessageConsumer(context, consumer); + + auto irContext = MakeUnique(env, consumer); + opt::IrLoader loader(consumer, irContext->module()); + loader.SetExtraLineTracking(extra_line_tracking); + + spv_result_t status = spvBinaryParse(context, &loader, binary, size, + SetSpvHeader, SetSpvInst, nullptr); + loader.EndModule(); + + spvContextDestroy(context); + + return status == SPV_SUCCESS ? std::move(irContext) : nullptr; +} + +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const std::string& text, + uint32_t assemble_options) { + SpirvTools t(env); + t.SetMessageConsumer(consumer); + std::vector binary; + if (!t.Assemble(text, &binary, assemble_options)) return nullptr; + return BuildModule(env, consumer, binary.data(), binary.size()); +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.h b/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.h new file mode 100644 index 00000000..0f906c88 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/build_module.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BUILD_MODULE_H_ +#define SOURCE_OPT_BUILD_MODULE_H_ + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +// Builds a Module and returns the owning IRContext from the given SPIR-V +// |binary|. |size| specifies number of words in |binary|. The |binary| will be +// decoded according to the given target |env|. Returns nullptr if errors occur +// and sends the errors to |consumer|. When |extra_line_tracking| is true, +// extra OpLine instructions are injected to better presere line numbers while +// later transforms mutate the module. +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, size_t size, + bool extra_line_tracking); + +// Like above, with extra line tracking turned on. +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, + size_t size); + +// Builds a Module and returns the owning IRContext from the given +// SPIR-V assembly |text|. The |text| will be encoded according to the given +// target |env|. Returns nullptr if errors occur and sends the errors to +// |consumer|. +std::unique_ptr BuildModule( + spv_target_env env, MessageConsumer consumer, const std::string& text, + uint32_t assemble_options = SpirvTools::kDefaultAssembleOption); + +} // namespace spvtools + +#endif // SOURCE_OPT_BUILD_MODULE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.cpp new file mode 100644 index 00000000..46bfc907 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.cpp @@ -0,0 +1,377 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements conditional constant propagation as described in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. + +#include "source/opt/ccp_pass.h" + +#include +#include + +#include "source/opt/fold.h" +#include "source/opt/function.h" +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { +namespace { +// This SSA id is never defined nor referenced in the IR. It is a special ID +// which represents varying values. When an ID is found to have a varying +// value, its entry in the |values_| table maps to kVaryingSSAId. +constexpr uint32_t kVaryingSSAId = std::numeric_limits::max(); +} // namespace + +bool CCPPass::IsVaryingValue(uint32_t id) const { return id == kVaryingSSAId; } + +SSAPropagator::PropStatus CCPPass::MarkInstructionVarying(Instruction* instr) { + assert(instr->result_id() != 0 && + "Instructions with no result cannot be marked varying."); + values_[instr->result_id()] = kVaryingSSAId; + return SSAPropagator::kVarying; +} + +SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) { + uint32_t meet_val_id = 0; + + // Implement the lattice meet operation. The result of this Phi instruction is + // interesting only if the meet operation over arguments coming through + // executable edges yields the same constant value. + for (uint32_t i = 2; i < phi->NumOperands(); i += 2) { + if (!propagator_->IsPhiArgExecutable(phi, i)) { + // Ignore arguments coming through non-executable edges. + continue; + } + uint32_t phi_arg_id = phi->GetSingleWordOperand(i); + auto it = values_.find(phi_arg_id); + if (it != values_.end()) { + // We found an argument with a constant value. Apply the meet operation + // with the previous arguments. + if (it->second == kVaryingSSAId) { + // The "constant" value is actually a placeholder for varying. Return + // varying for this phi. + return MarkInstructionVarying(phi); + } else if (meet_val_id == 0) { + // This is the first argument we find. Initialize the result to its + // constant value id. + meet_val_id = it->second; + } else if (it->second == meet_val_id) { + // The argument is the same constant value already computed. Continue + // looking. + continue; + } else { + // We either found a varying value, or another constant value different + // from the previous computed meet value. This Phi will never be + // constant. + return MarkInstructionVarying(phi); + } + } else { + // The incoming value has no recorded value and is therefore not + // interesting. A not interesting value joined with any other value is the + // other value. + continue; + } + } + + // If there are no incoming executable edges, the meet ID will still be 0. In + // that case, return not interesting to evaluate the Phi node again. + if (meet_val_id == 0) { + return SSAPropagator::kNotInteresting; + } + + // All the operands have the same constant value represented by |meet_val_id|. + // Set the Phi's result to that value and declare it interesting. + values_[phi->result_id()] = meet_val_id; + return SSAPropagator::kInteresting; +} + +uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) { + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + auto val1_it = values_.find(instr->result_id()); + if (val1_it == values_.end()) { + return val2; + } + + uint32_t val1 = val1_it->second; + if (IsVaryingValue(val1)) { + return val1; + } else if (IsVaryingValue(val2)) { + return val2; + } else if (val1 != val2) { + return kVaryingSSAId; + } + return val2; +} + +SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { + assert(instr->result_id() != 0 && + "Expecting an instruction that produces a result"); + + // If this is a copy operation, and the RHS is a known constant, assign its + // value to the LHS. + if (instr->opcode() == spv::Op::OpCopyObject) { + uint32_t rhs_id = instr->GetSingleWordInOperand(0); + auto it = values_.find(rhs_id); + if (it != values_.end()) { + if (IsVaryingValue(it->second)) { + return MarkInstructionVarying(instr); + } else { + uint32_t new_val = ComputeLatticeMeet(instr, it->second); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; + } + } + return SSAPropagator::kNotInteresting; + } + + // Instructions with a RHS that cannot produce a constant are always varying. + if (!instr->IsFoldable()) { + return MarkInstructionVarying(instr); + } + + // See if the RHS of the assignment folds into a constant value. + auto map_func = [this](uint32_t id) { + auto it = values_.find(id); + if (it == values_.end() || IsVaryingValue(it->second)) { + return id; + } + return it->second; + }; + Instruction* folded_inst = + context()->get_instruction_folder().FoldInstructionToConstant(instr, + map_func); + + if (folded_inst != nullptr) { + // We do not want to change the body of the function by adding new + // instructions. When folding we can only generate new constants. + assert((folded_inst->IsConstant() || + IsSpecConstantInst(folded_inst->opcode())) && + "CCP is only interested in constant values."); + uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id()); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; + } + + // Conservatively mark this instruction as varying if any input id is varying. + if (!instr->WhileEachInId([this](uint32_t* op_id) { + auto iter = values_.find(*op_id); + if (iter != values_.end() && IsVaryingValue(iter->second)) return false; + return true; + })) { + return MarkInstructionVarying(instr); + } + + // If not, see if there is a least one unknown operand to the instruction. If + // so, we might be able to fold it later. + if (!instr->WhileEachInId([this](uint32_t* op_id) { + auto it = values_.find(*op_id); + if (it == values_.end()) return false; + return true; + })) { + return SSAPropagator::kNotInteresting; + } + + // Otherwise, we will never be able to fold this instruction, so mark it + // varying. + return MarkInstructionVarying(instr); +} + +SSAPropagator::PropStatus CCPPass::VisitBranch(Instruction* instr, + BasicBlock** dest_bb) const { + assert(instr->IsBranch() && "Expected a branch instruction."); + + *dest_bb = nullptr; + uint32_t dest_label = 0; + if (instr->opcode() == spv::Op::OpBranch) { + // An unconditional jump always goes to its unique destination. + dest_label = instr->GetSingleWordInOperand(0); + } else if (instr->opcode() == spv::Op::OpBranchConditional) { + // For a conditional branch, determine whether the predicate selector has a + // known value in |values_|. If it does, set the destination block + // according to the selector's boolean value. + uint32_t pred_id = instr->GetSingleWordOperand(0); + auto it = values_.find(pred_id); + if (it == values_.end() || IsVaryingValue(it->second)) { + // The predicate has an unknown value, either branch could be taken. + return SSAPropagator::kVarying; + } + + // Get the constant value for the predicate selector from the value table. + // Use it to decide which branch will be taken. + uint32_t pred_val_id = it->second; + const analysis::Constant* c = const_mgr_->FindDeclaredConstant(pred_val_id); + assert(c && "Expected to find a constant declaration for a known value."); + // Undef values should have returned as varying above. + assert(c->AsBoolConstant() || c->AsNullConstant()); + if (c->AsNullConstant()) { + dest_label = instr->GetSingleWordOperand(2u); + } else { + const analysis::BoolConstant* val = c->AsBoolConstant(); + dest_label = val->value() ? instr->GetSingleWordOperand(1) + : instr->GetSingleWordOperand(2); + } + } else { + // For an OpSwitch, extract the value taken by the switch selector and check + // which of the target literals it matches. The branch associated with that + // literal is the taken branch. + assert(instr->opcode() == spv::Op::OpSwitch); + if (instr->GetOperand(0).words.size() != 1) { + // If the selector is wider than 32-bits, return varying. TODO(dnovillo): + // Add support for wider constants. + return SSAPropagator::kVarying; + } + uint32_t select_id = instr->GetSingleWordOperand(0); + auto it = values_.find(select_id); + if (it == values_.end() || IsVaryingValue(it->second)) { + // The selector has an unknown value, any of the branches could be taken. + return SSAPropagator::kVarying; + } + + // Get the constant value for the selector from the value table. Use it to + // decide which branch will be taken. + uint32_t select_val_id = it->second; + const analysis::Constant* c = + const_mgr_->FindDeclaredConstant(select_val_id); + assert(c && "Expected to find a constant declaration for a known value."); + // TODO: support 64-bit integer switches. + uint32_t constant_cond = 0; + if (const analysis::IntConstant* val = c->AsIntConstant()) { + constant_cond = val->words()[0]; + } else { + // Undef values should have returned varying above. + assert(c->AsNullConstant()); + constant_cond = 0; + } + + // Start assuming that the selector will take the default value; + dest_label = instr->GetSingleWordOperand(1); + for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { + if (constant_cond == instr->GetSingleWordOperand(i)) { + dest_label = instr->GetSingleWordOperand(i + 1); + break; + } + } + } + + assert(dest_label && "Destination label should be set at this point."); + *dest_bb = context()->cfg()->block(dest_label); + return SSAPropagator::kInteresting; +} + +SSAPropagator::PropStatus CCPPass::VisitInstruction(Instruction* instr, + BasicBlock** dest_bb) { + *dest_bb = nullptr; + if (instr->opcode() == spv::Op::OpPhi) { + return VisitPhi(instr); + } else if (instr->IsBranch()) { + return VisitBranch(instr, dest_bb); + } else if (instr->result_id()) { + return VisitAssignment(instr); + } + return SSAPropagator::kVarying; +} + +bool CCPPass::ReplaceValues() { + // Even if we make no changes to the function's IR, propagation may have + // created new constants. Even if those constants cannot be replaced in + // the IR, the constant definition itself is a change. To reflect this, + // we check whether the next ID to be given by the module is different than + // the original bound ID. If that happens, new instructions were added to the + // module during propagation. + // + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 and + // https://github.com/KhronosGroup/SPIRV-Tools/issues/3991 for details. + bool changed_ir = (context()->module()->IdBound() > original_id_bound_); + + for (const auto& it : values_) { + uint32_t id = it.first; + uint32_t cst_id = it.second; + if (!IsVaryingValue(cst_id) && id != cst_id) { + context()->KillNamesAndDecorates(id); + changed_ir |= context()->ReplaceAllUsesWith(id, cst_id); + } + } + + return changed_ir; +} + +bool CCPPass::PropagateConstants(Function* fp) { + if (fp->IsDeclaration()) { + return false; + } + + // Mark function parameters as varying. + fp->ForEachParam([this](const Instruction* inst) { + values_[inst->result_id()] = kVaryingSSAId; + }); + + const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) { + return VisitInstruction(instr, dest_bb); + }; + + propagator_ = + std::unique_ptr(new SSAPropagator(context(), visit_fn)); + + if (propagator_->Run(fp)) { + return ReplaceValues(); + } + + return false; +} + +void CCPPass::Initialize() { + const_mgr_ = context()->get_constant_mgr(); + + // Populate the constant table with values from constant declarations in the + // module. The values of each OpConstant declaration is the identity + // assignment (i.e., each constant is its own value). + for (const auto& inst : get_module()->types_values()) { + // Record compile time constant ids. Treat all other global values as + // varying. + if (inst.IsConstant()) { + values_[inst.result_id()] = inst.result_id(); + } else { + values_[inst.result_id()] = kVaryingSSAId; + } + } + + original_id_bound_ = context()->module()->IdBound(); +} + +Pass::Status CCPPass::Process() { + Initialize(); + + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.h new file mode 100644 index 00000000..77ea9f80 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ccp_pass.h @@ -0,0 +1,133 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CCP_PASS_H_ +#define SOURCE_OPT_CCP_PASS_H_ + +#include +#include + +#include "source/opt/constants.h" +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { + +class CCPPass : public MemPass { + public: + CCPPass() = default; + + const char* name() const override { return "ccp"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Initializes the pass. + void Initialize(); + + // Runs constant propagation on the given function |fp|. Returns true if any + // constants were propagated and the IR modified. + bool PropagateConstants(Function* fp); + + // Visits a single instruction |instr|. If the instruction is a conditional + // branch that always jumps to the same basic block, it sets the destination + // block in |dest_bb|. + SSAPropagator::PropStatus VisitInstruction(Instruction* instr, + BasicBlock** dest_bb); + + // Visits an OpPhi instruction |phi|. This applies the meet operator for the + // CCP lattice. Essentially, if all the operands in |phi| have the same + // constant value C, the result for |phi| gets assigned the value C. + SSAPropagator::PropStatus VisitPhi(Instruction* phi); + + // Visits an SSA assignment instruction |instr|. If the RHS of |instr| folds + // into a constant value C, then the LHS of |instr| is assigned the value C in + // |values_|. + SSAPropagator::PropStatus VisitAssignment(Instruction* instr); + + // Visits a branch instruction |instr|. If the branch is conditional + // (OpBranchConditional or OpSwitch), and the value of its selector is known, + // |dest_bb| will be set to the corresponding destination block. Unconditional + // branches always set |dest_bb| to the single destination block. + SSAPropagator::PropStatus VisitBranch(Instruction* instr, + BasicBlock** dest_bb) const; + + // Replaces all operands used in |fp| with the corresponding constant values + // in |values_|. Returns true if any operands were replaced, and false + // otherwise. + bool ReplaceValues(); + + // Marks |instr| as varying by registering a varying value for its result + // into the |values_| table. Returns SSAPropagator::kVarying. + SSAPropagator::PropStatus MarkInstructionVarying(Instruction* instr); + + // Returns true if |id| is the special SSA id that corresponds to a varying + // value. + bool IsVaryingValue(uint32_t id) const; + + // Constant manager for the parent IR context. Used to record new constants + // generated during propagation. + analysis::ConstantManager* const_mgr_; + + // Returns a new value for |instr| by computing the meet operation between + // its existing value and |val2|. + // + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + uint32_t ComputeLatticeMeet(Instruction* instr, uint32_t val2); + + // Constant value table. Each entry in this map + // represents the compile-time constant value for |id| as declared by + // |const_decl_id|. Each |const_decl_id| in this table is an OpConstant + // declaration for the current module. + // + // Additionally, this table keeps track of SSA IDs with varying values. If an + // SSA ID is found to have a varying value, it will have an entry in this + // table that maps to the special SSA id kVaryingSSAId. These values are + // never replaced in the IR, they are used by CCP during propagation. + std::unordered_map values_; + + // Propagator engine used. + std::unique_ptr propagator_; + + // Value for the module's ID bound before running CCP. Used to detect whether + // propagation created new instructions. + uint32_t original_id_bound_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CCP_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.cpp new file mode 100644 index 00000000..4c4bb256 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.cpp @@ -0,0 +1,354 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/cfg.h" + +#include +#include + +#include "source/cfa.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace { + +using cbb_ptr = const opt::BasicBlock*; + +// Universal Limit of ResultID + 1 +constexpr int kMaxResultId = 0x400000; + +} // namespace + +CFG::CFG(Module* module) + : module_(module), + pseudo_entry_block_(std::unique_ptr( + new Instruction(module->context(), spv::Op::OpLabel, 0, 0, {}))), + pseudo_exit_block_(std::unique_ptr(new Instruction( + module->context(), spv::Op::OpLabel, 0, kMaxResultId, {}))) { + for (auto& fn : *module) { + for (auto& blk : fn) { + RegisterBlock(&blk); + } + } +} + +void CFG::AddEdges(BasicBlock* blk) { + uint32_t blk_id = blk->id(); + // Force the creation of an entry, not all basic block have predecessors + // (such as the entry blocks and some unreachables). + label2preds_[blk_id]; + const auto* const_blk = blk; + const_blk->ForEachSuccessorLabel( + [blk_id, this](const uint32_t succ_id) { AddEdge(blk_id, succ_id); }); +} + +void CFG::RemoveNonExistingEdges(uint32_t blk_id) { + std::vector updated_pred_list; + for (uint32_t id : preds(blk_id)) { + const BasicBlock* pred_blk = block(id); + bool has_branch = false; + pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) { + if (succ == blk_id) { + has_branch = true; + } + }); + if (has_branch) updated_pred_list.push_back(id); + } + + label2preds_.at(blk_id) = std::move(updated_pred_list); +} + +void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, + std::list* order) { + ComputeStructuredOrder(func, root, nullptr, order); +} + +void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, + BasicBlock* end, + std::list* order) { + assert(module_->context()->get_feature_mgr()->HasCapability( + spv::Capability::Shader) && + "This only works on structured control flow"); + + // Compute structured successors and do DFS. + ComputeStructuredSuccessors(func); + auto ignore_block = [](cbb_ptr) {}; + auto terminal = [end](cbb_ptr bb) { return bb == end; }; + + auto get_structured_successors = [this](const BasicBlock* b) { + return &(block2structured_succs_[b]); + }; + + // TODO(greg-lunarg): Get rid of const_cast by making moving const + // out of the cfa.h prototypes and into the invoking code. + auto post_order = [&](cbb_ptr b) { + order->push_front(const_cast(b)); + }; + CFA::DepthFirstTraversal(root, get_structured_successors, + ignore_block, post_order, terminal); +} + +void CFG::ForEachBlockInPostOrder(BasicBlock* bb, + const std::function& f) { + std::vector po; + std::unordered_set seen; + ComputePostOrderTraversal(bb, &po, &seen); + + for (BasicBlock* current_bb : po) { + if (!IsPseudoExitBlock(current_bb) && !IsPseudoEntryBlock(current_bb)) { + f(current_bb); + } + } +} + +void CFG::ForEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f) { + WhileEachBlockInReversePostOrder(bb, [f](BasicBlock* b) { + f(b); + return true; + }); +} + +bool CFG::WhileEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f) { + std::vector po; + std::unordered_set seen; + ComputePostOrderTraversal(bb, &po, &seen); + + for (auto current_bb = po.rbegin(); current_bb != po.rend(); ++current_bb) { + if (!IsPseudoExitBlock(*current_bb) && !IsPseudoEntryBlock(*current_bb)) { + if (!f(*current_bb)) { + return false; + } + } + } + return true; +} + +void CFG::ComputeStructuredSuccessors(Function* func) { + block2structured_succs_.clear(); + for (auto& blk : *func) { + // If no predecessors in function, make successor to pseudo entry. + if (label2preds_[blk.id()].size() == 0) + block2structured_succs_[&pseudo_entry_block_].push_back(&blk); + + // If header, make merge block first successor and continue block second + // successor if there is one. + uint32_t mbid = blk.MergeBlockIdIfAny(); + if (mbid != 0) { + block2structured_succs_[&blk].push_back(block(mbid)); + uint32_t cbid = blk.ContinueBlockIdIfAny(); + if (cbid != 0) { + block2structured_succs_[&blk].push_back(block(cbid)); + } + } + + // Add true successors. + const auto& const_blk = blk; + const_blk.ForEachSuccessorLabel([&blk, this](const uint32_t sbid) { + block2structured_succs_[&blk].push_back(block(sbid)); + }); + } +} + +void CFG::ComputePostOrderTraversal(BasicBlock* bb, + std::vector* order, + std::unordered_set* seen) { + std::vector stack; + stack.push_back(bb); + while (!stack.empty()) { + bb = stack.back(); + seen->insert(bb); + static_cast(bb)->WhileEachSuccessorLabel( + [&seen, &stack, this](const uint32_t sbid) { + BasicBlock* succ_bb = id2block_[sbid]; + if (!seen->count(succ_bb)) { + stack.push_back(succ_bb); + return false; + } + return true; + }); + if (stack.back() == bb) { + order->push_back(bb); + stack.pop_back(); + } + } +} + +BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { + assert(bb->GetLoopMergeInst() && "Expecting bb to be the header of a loop."); + + Function* fn = bb->GetParent(); + IRContext* context = module_->context(); + + // Get the new header id up front. If we are out of ids, then we cannot split + // the loop. + uint32_t new_header_id = context->TakeNextId(); + if (new_header_id == 0) { + return nullptr; + } + + // Find the insertion point for the new bb. + Function::iterator header_it = std::find_if( + fn->begin(), fn->end(), + [bb](BasicBlock& block_in_func) { return &block_in_func == bb; }); + assert(header_it != fn->end()); + + const std::vector& pred = preds(bb->id()); + // Find the back edge + BasicBlock* latch_block = nullptr; + Function::iterator latch_block_iter = header_it; + for (; latch_block_iter != fn->end(); ++latch_block_iter) { + // If blocks are in the proper order, then the only branch that appears + // after the header is the latch. + if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) != + pred.end()) { + break; + } + } + assert(latch_block_iter != fn->end() && "Could not find the latch."); + latch_block = &*latch_block_iter; + + RemoveSuccessorEdges(bb); + + // Create the new header bb basic bb. + // Leave the phi instructions behind. + auto iter = bb->begin(); + while (iter->opcode() == spv::Op::OpPhi) { + ++iter; + } + + BasicBlock* new_header = bb->SplitBasicBlock(context, new_header_id, iter); + context->AnalyzeDefUse(new_header->GetLabelInst()); + + // Update cfg + RegisterBlock(new_header); + + // Update bb mappings. + context->set_instr_block(new_header->GetLabelInst(), new_header); + new_header->ForEachInst([new_header, context](Instruction* inst) { + context->set_instr_block(inst, new_header); + }); + + // If |bb| was the latch block, the branch back to the header is not in + // |new_header|. + if (latch_block == bb) { + if (new_header->ContinueBlockId() == bb->id()) { + new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id}); + } + latch_block = new_header; + } + + // Adjust the OpPhi instructions as needed. + bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) { + std::vector preheader_phi_ops; + std::vector header_phi_ops; + + // Identify where the original inputs to original OpPhi belong: header or + // preheader. + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t def_id = phi->GetSingleWordInOperand(i); + uint32_t branch_id = phi->GetSingleWordInOperand(i + 1); + if (branch_id == latch_block->id()) { + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {def_id}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {branch_id}}); + } else { + preheader_phi_ops.push_back(def_id); + preheader_phi_ops.push_back(branch_id); + } + } + + // Create a phi instruction if and only if the preheader_phi_ops has more + // than one pair. + if (preheader_phi_ops.size() > 2) { + InstructionBuilder builder( + context, &*bb->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops); + + // Add the OpPhi to the header bb. + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); + } else { + // An OpPhi with a single entry is just a copy. In this case use the same + // instruction in the new header. + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); + } + + phi->RemoveFromList(); + std::unique_ptr phi_owner(phi); + phi->SetInOperands(std::move(header_phi_ops)); + new_header->begin()->InsertBefore(std::move(phi_owner)); + context->set_instr_block(phi, new_header); + context->AnalyzeUses(phi); + }); + + // Add a branch to the new header. + InstructionBuilder branch_builder( + context, bb, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + bb->AddInstruction( + MakeUnique(context, spv::Op::OpBranch, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {new_header->id()}}})); + context->AnalyzeUses(bb->terminator()); + context->set_instr_block(bb->terminator(), bb); + label2preds_[new_header->id()].push_back(bb->id()); + + // Update the latch to branch to the new header. + latch_block->ForEachSuccessorLabel([bb, new_header_id](uint32_t* id) { + if (*id == bb->id()) { + *id = new_header_id; + } + }); + Instruction* latch_branch = latch_block->terminator(); + context->AnalyzeUses(latch_branch); + label2preds_[new_header->id()].push_back(latch_block->id()); + + auto& block_preds = label2preds_[bb->id()]; + auto latch_pos = + std::find(block_preds.begin(), block_preds.end(), latch_block->id()); + assert(latch_pos != block_preds.end() && "The cfg was invalid."); + block_preds.erase(latch_pos); + + // Update the loop descriptors + if (context->AreAnalysesValid(IRContext::kAnalysisLoopAnalysis)) { + LoopDescriptor* loop_desc = context->GetLoopDescriptor(bb->GetParent()); + Loop* loop = (*loop_desc)[bb->id()]; + + loop->AddBasicBlock(new_header_id); + loop->SetHeaderBlock(new_header); + loop_desc->SetBasicBlockToLoop(new_header_id, loop); + + loop->RemoveBasicBlock(bb->id()); + loop->SetPreHeaderBlock(bb); + + Loop* parent_loop = loop->GetParent(); + if (parent_loop != nullptr) { + parent_loop->AddBasicBlock(bb->id()); + loop_desc->SetBasicBlockToLoop(bb->id(), parent_loop); + } else { + loop_desc->SetBasicBlockToLoop(bb->id(), nullptr); + } + } + return new_header; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.h b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.h new file mode 100644 index 00000000..fa4fef2d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg.h @@ -0,0 +1,189 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CFG_H_ +#define SOURCE_OPT_CFG_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" + +namespace spvtools { +namespace opt { + +class CFG { + public: + explicit CFG(Module* module); + + // Return the list of predecessors for basic block with label |blkid|. + // TODO(dnovillo): Move this to BasicBlock. + const std::vector& preds(uint32_t blk_id) const { + assert(label2preds_.count(blk_id)); + return label2preds_.at(blk_id); + } + + // Return a pointer to the basic block instance corresponding to the label + // |blk_id|. + BasicBlock* block(uint32_t blk_id) const { return id2block_.at(blk_id); } + + // Return the pseudo entry and exit blocks. + const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } + BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } + + const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; } + BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } + + // Return true if |block_ptr| is the pseudo-entry block. + bool IsPseudoEntryBlock(BasicBlock* block_ptr) const { + return block_ptr == &pseudo_entry_block_; + } + + // Return true if |block_ptr| is the pseudo-exit block. + bool IsPseudoExitBlock(BasicBlock* block_ptr) const { + return block_ptr == &pseudo_exit_block_; + } + + // Compute structured block order into |order| for |func| starting at |root|. + // This order has the property that dominators come before all blocks they + // dominate, merge blocks come after all blocks that are in the control + // constructs of their header, and continue blocks come after all of the + // blocks in the body of their loop. + void ComputeStructuredOrder(Function* func, BasicBlock* root, + std::list* order); + + // Compute structured block order into |order| for |func| starting at |root| + // and ending at |end|. This order has the property that dominators come + // before all blocks they dominate, merge blocks come after all blocks that + // are in the control constructs of their header, and continue blocks come + // after all the blocks in the body of their loop. + void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end, + std::list* order); + + // Applies |f| to all blocks that can be reach from |bb| in post order. + void ForEachBlockInPostOrder(BasicBlock* bb, + const std::function& f); + + // Applies |f| to all blocks that can be reach from |bb| in reverse post + // order. + void ForEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f); + + // Applies |f| to all blocks that can be reach from |bb| in reverse post + // order. Return false if |f| return false on any basic block, and stops + // processing. + bool WhileEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f); + + // Registers |blk| as a basic block in the cfg, this also updates the + // predecessor lists of each successor of |blk|. |blk| must have a terminator + // instruction at the end of the block. + void RegisterBlock(BasicBlock* blk) { + assert(blk->begin() != blk->end() && + "Basic blocks must have a terminator before registering."); + assert(blk->tail()->IsBlockTerminator() && + "Basic blocks must have a terminator before registering."); + uint32_t blk_id = blk->id(); + id2block_[blk_id] = blk; + AddEdges(blk); + } + + // Removes from the CFG any mapping for the basic block id |blk_id|. + void ForgetBlock(const BasicBlock* blk) { + id2block_.erase(blk->id()); + label2preds_.erase(blk->id()); + RemoveSuccessorEdges(blk); + } + + void RemoveEdge(uint32_t pred_blk_id, uint32_t succ_blk_id) { + auto pred_it = label2preds_.find(succ_blk_id); + if (pred_it == label2preds_.end()) return; + auto& preds_list = pred_it->second; + auto it = std::find(preds_list.begin(), preds_list.end(), pred_blk_id); + if (it != preds_list.end()) preds_list.erase(it); + } + + // Registers |blk| to all of its successors. + void AddEdges(BasicBlock* blk); + + // Registers the basic block id |pred_blk_id| as being a predecessor of the + // basic block id |succ_blk_id|. + void AddEdge(uint32_t pred_blk_id, uint32_t succ_blk_id) { + label2preds_[succ_blk_id].push_back(pred_blk_id); + } + + // Removes any edges that no longer exist from the predecessor mapping for + // the basic block id |blk_id|. + void RemoveNonExistingEdges(uint32_t blk_id); + + // Remove all edges that leave |bb|. + void RemoveSuccessorEdges(const BasicBlock* bb) { + bb->ForEachSuccessorLabel( + [bb, this](uint32_t succ_id) { RemoveEdge(bb->id(), succ_id); }); + } + + // Divides |block| into two basic blocks. The first block will have the same + // id as |block| and will become a preheader for the loop. The other block + // is a new block that will be the new loop header. + // + // Returns a pointer to the new loop header. Returns |nullptr| if the new + // loop pointer could not be created. + BasicBlock* SplitLoopHeader(BasicBlock* bb); + + private: + // Compute structured successors for function |func|. A block's structured + // successors are the blocks it branches to together with its declared merge + // block and continue block if it has them. When order matters, the merge + // block and continue block always appear first. This assures correct depth + // first search in the presence of early returns and kills. If the successor + // vector contain duplicates of the merge or continue blocks, they are safely + // ignored by DFS. + void ComputeStructuredSuccessors(Function* func); + + // Computes the post-order traversal of the cfg starting at |bb| skipping + // nodes in |seen|. The order of the traversal is appended to |order|, and + // all nodes in the traversal are added to |seen|. + void ComputePostOrderTraversal(BasicBlock* bb, + std::vector* order, + std::unordered_set* seen); + + // Module for this CFG. + Module* module_; + + // Map from block to its structured successor blocks. See + // ComputeStructuredSuccessors() for definition. + std::unordered_map> + block2structured_succs_; + + // Extra block whose successors are all blocks with no predecessors + // in function. + BasicBlock pseudo_entry_block_; + + // Augmented CFG Exit Block. + BasicBlock pseudo_exit_block_; + + // Map from block's label id to its predecessor blocks ids + std::unordered_map> label2preds_; + + // Map from block's label id to block. + std::unordered_map id2block_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CFG_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.cpp new file mode 100644 index 00000000..26fed89f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements a pass to cleanup the CFG to remove superfluous +// constructs (e.g., unreachable basic blocks, empty control flow structures, +// etc) + +#include "source/opt/cfg_cleanup_pass.h" + +#include "source/opt/function.h" + +namespace spvtools { +namespace opt { + +Pass::Status CFGCleanupPass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return CFGCleanup(fp); }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.h new file mode 100644 index 00000000..50954289 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/cfg_cleanup_pass.h @@ -0,0 +1,41 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CFG_CLEANUP_PASS_H_ +#define SOURCE_OPT_CFG_CLEANUP_PASS_H_ + +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +class CFGCleanupPass : public MemPass { + public: + CFGCleanupPass() = default; + + const char* name() const override { return "cfg-cleanup"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CFG_CLEANUP_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.cpp new file mode 100644 index 00000000..90231791 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.cpp @@ -0,0 +1,325 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "code_sink.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +Pass::Status CodeSinkingPass::Process() { + bool modified = false; + for (Function& function : *get_module()) { + cfg()->ForEachBlockInPostOrder(function.entry().get(), + [&modified, this](BasicBlock* bb) { + if (SinkInstructionsInBB(bb)) { + modified = true; + } + }); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool CodeSinkingPass::SinkInstructionsInBB(BasicBlock* bb) { + bool modified = false; + for (auto inst = bb->rbegin(); inst != bb->rend(); ++inst) { + if (SinkInstruction(&*inst)) { + inst = bb->rbegin(); + modified = true; + } + } + return modified; +} + +bool CodeSinkingPass::SinkInstruction(Instruction* inst) { + if (inst->opcode() != spv::Op::OpLoad && + inst->opcode() != spv::Op::OpAccessChain) { + return false; + } + + if (ReferencesMutableMemory(inst)) { + return false; + } + + if (BasicBlock* target_bb = FindNewBasicBlockFor(inst)) { + Instruction* pos = &*target_bb->begin(); + while (pos->opcode() == spv::Op::OpPhi) { + pos = pos->NextNode(); + } + + inst->InsertBefore(pos); + context()->set_instr_block(inst, target_bb); + return true; + } + return false; +} + +BasicBlock* CodeSinkingPass::FindNewBasicBlockFor(Instruction* inst) { + assert(inst->result_id() != 0 && "Instruction should have a result."); + BasicBlock* original_bb = context()->get_instr_block(inst); + BasicBlock* bb = original_bb; + + std::unordered_set bbs_with_uses; + get_def_use_mgr()->ForEachUse( + inst, [&bbs_with_uses, this](Instruction* use, uint32_t idx) { + if (use->opcode() != spv::Op::OpPhi) { + BasicBlock* use_bb = context()->get_instr_block(use); + if (use_bb) { + bbs_with_uses.insert(use_bb->id()); + } + } else { + bbs_with_uses.insert(use->GetSingleWordOperand(idx + 1)); + } + }); + + while (true) { + // If |inst| is used in |bb|, then |inst| cannot be moved any further. + if (bbs_with_uses.count(bb->id())) { + break; + } + + // If |bb| has one successor (succ_bb), and |bb| is the only predecessor + // of succ_bb, then |inst| can be moved to succ_bb. If succ_bb, has move + // then one predecessor, then moving |inst| into succ_bb could cause it to + // be executed more often, so the search has to stop. + if (bb->terminator()->opcode() == spv::Op::OpBranch) { + uint32_t succ_bb_id = bb->terminator()->GetSingleWordInOperand(0); + if (cfg()->preds(succ_bb_id).size() == 1) { + bb = context()->get_instr_block(succ_bb_id); + continue; + } else { + break; + } + } + + // The remaining checks need to know the merge node. If there is no merge + // instruction or an OpLoopMerge, then it is a break or continue. We could + // figure it out, but not worth doing it now. + Instruction* merge_inst = bb->GetMergeInst(); + if (merge_inst == nullptr || + merge_inst->opcode() != spv::Op::OpSelectionMerge) { + break; + } + + // Check all of the successors of |bb| it see which lead to a use of |inst| + // before reaching the merge node. + bool used_in_multiple_blocks = false; + uint32_t bb_used_in = 0; + bb->ForEachSuccessorLabel([this, bb, &bb_used_in, &used_in_multiple_blocks, + &bbs_with_uses](uint32_t* succ_bb_id) { + if (IntersectsPath(*succ_bb_id, bb->MergeBlockIdIfAny(), bbs_with_uses)) { + if (bb_used_in == 0) { + bb_used_in = *succ_bb_id; + } else { + used_in_multiple_blocks = true; + } + } + }); + + // If more than one successor, which is not the merge block, uses |inst| + // then we have to leave |inst| in bb because there is none of the + // successors dominate all uses of |inst|. + if (used_in_multiple_blocks) { + break; + } + + if (bb_used_in == 0) { + // If |inst| is not used before reaching the merge node, then we can move + // |inst| to the merge node. + bb = context()->get_instr_block(bb->MergeBlockIdIfAny()); + } else { + // If the only successor that leads to a used of |inst| has more than 1 + // predecessor, then moving |inst| could cause it to be executed more + // often, so we cannot move it. + if (cfg()->preds(bb_used_in).size() != 1) { + break; + } + + // If |inst| is used after the merge block, then |bb_used_in| does not + // dominate all of the uses. So we cannot move |inst| any further. + if (IntersectsPath(bb->MergeBlockIdIfAny(), original_bb->id(), + bbs_with_uses)) { + break; + } + + // Otherwise, |bb_used_in| dominates all uses, so move |inst| into that + // block. + bb = context()->get_instr_block(bb_used_in); + } + continue; + } + return (bb != original_bb ? bb : nullptr); +} + +bool CodeSinkingPass::ReferencesMutableMemory(Instruction* inst) { + if (!inst->IsLoad()) { + return false; + } + + Instruction* base_ptr = inst->GetBaseAddress(); + if (base_ptr->opcode() != spv::Op::OpVariable) { + return true; + } + + if (base_ptr->IsReadOnlyPointer()) { + return false; + } + + if (HasUniformMemorySync()) { + return true; + } + + if (spv::StorageClass(base_ptr->GetSingleWordInOperand(0)) != + spv::StorageClass::Uniform) { + return true; + } + + return HasPossibleStore(base_ptr); +} + +bool CodeSinkingPass::HasUniformMemorySync() { + if (checked_for_uniform_sync_) { + return has_uniform_sync_; + } + + bool has_sync = false; + get_module()->ForEachInst([this, &has_sync](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpMemoryBarrier: { + uint32_t mem_semantics_id = inst->GetSingleWordInOperand(1); + if (IsSyncOnUniform(mem_semantics_id)) { + has_sync = true; + } + break; + } + case spv::Op::OpControlBarrier: + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: { + uint32_t mem_semantics_id = inst->GetSingleWordInOperand(2); + if (IsSyncOnUniform(mem_semantics_id)) { + has_sync = true; + } + break; + } + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + if (IsSyncOnUniform(inst->GetSingleWordInOperand(2)) || + IsSyncOnUniform(inst->GetSingleWordInOperand(3))) { + has_sync = true; + } + break; + default: + break; + } + }); + has_uniform_sync_ = has_sync; + return has_sync; +} + +bool CodeSinkingPass::IsSyncOnUniform(uint32_t mem_semantics_id) const { + const analysis::Constant* mem_semantics_const = + context()->get_constant_mgr()->FindDeclaredConstant(mem_semantics_id); + assert(mem_semantics_const != nullptr && + "Expecting memory semantics id to be a constant."); + assert(mem_semantics_const->AsIntConstant() && + "Memory semantics should be an integer."); + uint32_t mem_semantics_int = mem_semantics_const->GetU32(); + + // If it does not affect uniform memory, then it is does not apply to uniform + // memory. + if ((mem_semantics_int & uint32_t(spv::MemorySemanticsMask::UniformMemory)) == + 0) { + return false; + } + + // Check if there is an acquire or release. If so not, this it does not add + // any memory constraints. + return (mem_semantics_int & + uint32_t(spv::MemorySemanticsMask::Acquire | + spv::MemorySemanticsMask::AcquireRelease | + spv::MemorySemanticsMask::Release)) != 0; +} + +bool CodeSinkingPass::HasPossibleStore(Instruction* var_inst) { + assert(var_inst->opcode() == spv::Op::OpVariable || + var_inst->opcode() == spv::Op::OpAccessChain || + var_inst->opcode() == spv::Op::OpPtrAccessChain); + + return get_def_use_mgr()->WhileEachUser(var_inst, [this](Instruction* use) { + switch (use->opcode()) { + case spv::Op::OpStore: + return true; + case spv::Op::OpAccessChain: + case spv::Op::OpPtrAccessChain: + return HasPossibleStore(use); + default: + return false; + } + }); +} + +bool CodeSinkingPass::IntersectsPath(uint32_t start, uint32_t end, + const std::unordered_set& set) { + std::vector worklist; + worklist.push_back(start); + std::unordered_set already_done; + already_done.insert(start); + + while (!worklist.empty()) { + BasicBlock* bb = context()->get_instr_block(worklist.back()); + worklist.pop_back(); + + if (bb->id() == end) { + continue; + } + + if (set.count(bb->id())) { + return true; + } + + bb->ForEachSuccessorLabel([&already_done, &worklist](uint32_t* succ_bb_id) { + if (already_done.insert(*succ_bb_id).second) { + worklist.push_back(*succ_bb_id); + } + }); + } + return false; +} + +// namespace opt + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.h b/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.h new file mode 100644 index 00000000..d24df030 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/code_sink.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CODE_SINK_H_ +#define SOURCE_OPT_CODE_SINK_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass does code sinking for OpAccessChain and OpLoad on variables in +// uniform storage or in read only memory. Code sinking is a transformation +// where an instruction is moved into a more deeply nested construct. +// +// The goal is to move these instructions as close as possible to their uses +// without having to execute them more often or to replicate the instruction. +// Moving the instruction in this way can lead to shorter live ranges, which can +// lead to less register pressure. It can also cause instructions to be +// executed less often because they could be moved into one path of a selection +// construct. +// +// This optimization can cause register pressure to rise if the operands of the +// instructions go dead after the instructions being moved. That is why we only +// move certain OpLoad and OpAccessChain instructions. They generally have +// constants, loop induction variables, and global pointers as operands. The +// operands are live for a longer time in most cases. +class CodeSinkingPass : public Pass { + public: + const char* name() const override { return "code-sink"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Sinks the instructions in |bb| as much as possible. Returns true if + // something changes. + bool SinkInstructionsInBB(BasicBlock* bb); + + // Tries the sink |inst| as much as possible. Returns true if the instruction + // is moved. + bool SinkInstruction(Instruction* inst); + + // Returns the basic block in which to move |inst| to move is as close as + // possible to the uses of |inst| without increasing the number of times + // |inst| will be executed. Return |nullptr| if there is no need to move + // |inst|. + BasicBlock* FindNewBasicBlockFor(Instruction* inst); + + // Return true if |inst| reference memory and it is possible that the data in + // the memory changes at some point. + bool ReferencesMutableMemory(Instruction* inst); + + // Returns true if the module contains an instruction that has a memory + // semantics id as an operand, and the memory semantics enforces a + // synchronization of uniform memory. See section 3.25 of the SPIR-V + // specification. + bool HasUniformMemorySync(); + + // Returns true if there may be a store to the variable |var_inst|. + bool HasPossibleStore(Instruction* var_inst); + + // Returns true if one of the basic blocks in |set| exists on a path from the + // basic block |start| to |end|. + bool IntersectsPath(uint32_t start, uint32_t end, + const std::unordered_set& set); + + // Returns true if |mem_semantics_id| is the id of a constant that, when + // interpreted as a memory semantics mask enforces synchronization of uniform + // memory. See section 3.25 of the SPIR-V specification. + bool IsSyncOnUniform(uint32_t mem_semantics_id) const; + + // True if a check has for uniform storage has taken place. + bool checked_for_uniform_sync_; + + // Cache of whether or not the module has a memory sync on uniform storage. + // only valid if |check_for_uniform_sync_| is true. + bool has_uniform_sync_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CODE_SINK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.cpp new file mode 100644 index 00000000..99ec7962 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.cpp @@ -0,0 +1,296 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/combine_access_chains.h" + +#include + +#include "source/opt/constants.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status CombineAccessChains::Process() { + bool modified = false; + + for (auto& function : *get_module()) { + modified |= ProcessFunction(function); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool CombineAccessChains::ProcessFunction(Function& function) { + if (function.IsDeclaration()) { + return false; + } + + bool modified = false; + + cfg()->ForEachBlockInReversePostOrder( + function.entry().get(), [&modified, this](BasicBlock* block) { + block->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + modified |= CombineAccessChain(inst); + break; + default: + break; + } + }); + }); + + return modified; +} + +uint32_t CombineAccessChains::GetConstantValue( + const analysis::Constant* constant_inst) { + if (constant_inst->type()->AsInteger()->width() <= 32) { + if (constant_inst->type()->AsInteger()->IsSigned()) { + return static_cast(constant_inst->GetS32()); + } else { + return constant_inst->GetU32(); + } + } else { + assert(false); + return 0u; + } +} + +uint32_t CombineAccessChains::GetArrayStride(const Instruction* inst) { + uint32_t array_stride = 0; + context()->get_decoration_mgr()->WhileEachDecoration( + inst->type_id(), uint32_t(spv::Decoration::ArrayStride), + [&array_stride](const Instruction& decoration) { + assert(decoration.opcode() != spv::Op::OpDecorateId); + if (decoration.opcode() == spv::Op::OpDecorate) { + array_stride = decoration.GetSingleWordInOperand(1); + } else { + array_stride = decoration.GetSingleWordInOperand(2); + } + return false; + }); + return array_stride; +} + +const analysis::Type* CombineAccessChains::GetIndexedType(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + Instruction* base_ptr = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + const analysis::Type* type = type_mgr->GetType(base_ptr->type_id()); + assert(type->AsPointer()); + type = type->AsPointer()->pointee_type(); + std::vector element_indices; + uint32_t starting_index = 1; + if (IsPtrAccessChain(inst->opcode())) { + // Skip the first index of OpPtrAccessChain as it does not affect type + // resolution. + starting_index = 2; + } + for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) { + Instruction* index_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(i)); + const analysis::Constant* index_constant = + context()->get_constant_mgr()->GetConstantFromInst(index_inst); + if (index_constant) { + uint32_t index_value = GetConstantValue(index_constant); + element_indices.push_back(index_value); + } else { + // This index must not matter to resolve the type in valid SPIR-V. + element_indices.push_back(0); + } + } + type = type_mgr->GetMemberType(type, element_indices); + return type; +} + +bool CombineAccessChains::CombineIndices(Instruction* ptr_input, + Instruction* inst, + std::vector* new_operands) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::ConstantManager* constant_mgr = context()->get_constant_mgr(); + + Instruction* last_index_inst = def_use_mgr->GetDef( + ptr_input->GetSingleWordInOperand(ptr_input->NumInOperands() - 1)); + const analysis::Constant* last_index_constant = + constant_mgr->GetConstantFromInst(last_index_inst); + + Instruction* element_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + const analysis::Constant* element_constant = + constant_mgr->GetConstantFromInst(element_inst); + + // Combine the last index of the AccessChain (|ptr_inst|) with the element + // operand of the PtrAccessChain (|inst|). + const bool combining_element_operands = + IsPtrAccessChain(inst->opcode()) && + IsPtrAccessChain(ptr_input->opcode()) && ptr_input->NumInOperands() == 2; + uint32_t new_value_id = 0; + const analysis::Type* type = GetIndexedType(ptr_input); + if (last_index_constant && element_constant) { + // Combine the constants. + uint32_t new_value = GetConstantValue(last_index_constant) + + GetConstantValue(element_constant); + const analysis::Constant* new_value_constant = + constant_mgr->GetConstant(last_index_constant->type(), {new_value}); + Instruction* new_value_inst = + constant_mgr->GetDefiningInstruction(new_value_constant); + new_value_id = new_value_inst->result_id(); + } else if (!type->AsStruct() || combining_element_operands) { + // Generate an addition of the two indices. + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* addition = builder.AddIAdd(last_index_inst->type_id(), + last_index_inst->result_id(), + element_inst->result_id()); + new_value_id = addition->result_id(); + } else { + // Indexing into structs must be constant, so bail out here. + return false; + } + new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}}); + return true; +} + +bool CombineAccessChains::CreateNewInputOperands( + Instruction* ptr_input, Instruction* inst, + std::vector* new_operands) { + // Start by copying all the input operands of the feeder access chain. + for (uint32_t i = 0; i != ptr_input->NumInOperands() - 1; ++i) { + new_operands->push_back(ptr_input->GetInOperand(i)); + } + + // Deal with the last index of the feeder access chain. + if (IsPtrAccessChain(inst->opcode())) { + // The last index of the feeder should be combined with the element operand + // of |inst|. + if (!CombineIndices(ptr_input, inst, new_operands)) return false; + } else { + // The indices aren't being combined so now add the last index operand of + // |ptr_input|. + new_operands->push_back( + ptr_input->GetInOperand(ptr_input->NumInOperands() - 1)); + } + + // Copy the remaining index operands. + uint32_t starting_index = IsPtrAccessChain(inst->opcode()) ? 2 : 1; + for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) { + new_operands->push_back(inst->GetInOperand(i)); + } + + return true; +} + +bool CombineAccessChains::CombineAccessChain(Instruction* inst) { + assert((inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpAccessChain || + inst->opcode() == spv::Op::OpInBoundsAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) && + "Wrong opcode. Expected an access chain."); + + Instruction* ptr_input = + context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0)); + if (ptr_input->opcode() != spv::Op::OpAccessChain && + ptr_input->opcode() != spv::Op::OpInBoundsAccessChain && + ptr_input->opcode() != spv::Op::OpPtrAccessChain && + ptr_input->opcode() != spv::Op::OpInBoundsPtrAccessChain) { + return false; + } + + if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false; + + // Handles the following cases: + // 1. |ptr_input| is an index-less access chain. Replace the pointer + // in |inst| with |ptr_input|'s pointer. + // 2. |inst| is a index-less access chain. Change |inst| to an + // OpCopyObject. + // 3. |inst| is not a pointer access chain. + // |inst|'s indices are appended to |ptr_input|'s indices. + // 4. |ptr_input| is not pointer access chain. + // |inst| is a pointer access chain. + // |inst|'s element operand is combined with the last index in + // |ptr_input| to form a new operand. + // 5. |ptr_input| is a pointer access chain. + // Like the above scenario, |inst|'s element operand is combined + // with |ptr_input|'s last index. This results is either a + // combined element operand or combined regular index. + + // TODO(alan-baker): Support this properly. Requires analyzing the + // size/alignment of the type and converting the stride into an element + // index. + uint32_t array_stride = GetArrayStride(ptr_input); + if (array_stride != 0) return false; + + if (ptr_input->NumInOperands() == 1) { + // The input is effectively a no-op. + inst->SetInOperand(0, {ptr_input->GetSingleWordInOperand(0)}); + context()->AnalyzeUses(inst); + } else if (inst->NumInOperands() == 1) { + // |inst| is a no-op, change it to a copy. Instruction simplification will + // clean it up. + inst->SetOpcode(spv::Op::OpCopyObject); + } else { + std::vector new_operands; + if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false; + + // Update the instruction. + inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode())); + inst->SetInOperands(std::move(new_operands)); + context()->AnalyzeUses(inst); + } + return true; +} + +spv::Op CombineAccessChains::UpdateOpcode(spv::Op base_opcode, + spv::Op input_opcode) { + auto IsInBounds = [](spv::Op opcode) { + return opcode == spv::Op::OpInBoundsPtrAccessChain || + opcode == spv::Op::OpInBoundsAccessChain; + }; + + if (input_opcode == spv::Op::OpInBoundsPtrAccessChain) { + if (!IsInBounds(base_opcode)) return spv::Op::OpPtrAccessChain; + } else if (input_opcode == spv::Op::OpInBoundsAccessChain) { + if (!IsInBounds(base_opcode)) return spv::Op::OpAccessChain; + } + + return input_opcode; +} + +bool CombineAccessChains::IsPtrAccessChain(spv::Op opcode) { + return opcode == spv::Op::OpPtrAccessChain || + opcode == spv::Op::OpInBoundsPtrAccessChain; +} + +bool CombineAccessChains::Has64BitIndices(Instruction* inst) { + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + Instruction* index_inst = + context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(i)); + const analysis::Type* index_type = + context()->get_type_mgr()->GetType(index_inst->type_id()); + if (!index_type->AsInteger() || index_type->AsInteger()->width() != 32) + return true; + } + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.h b/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.h new file mode 100644 index 00000000..32ee50d3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/combine_access_chains.h @@ -0,0 +1,83 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ +#define SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class CombineAccessChains : public Pass { + public: + const char* name() const override { return "combine-access-chains"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Combine access chains in |function|. Blocks are processed in reverse + // post-order. Returns true if the function is modified. + bool ProcessFunction(Function& function); + + // Combines an access chain (normal, in bounds or pointer) |inst| if its base + // pointer is another access chain. Returns true if the access chain was + // modified. + bool CombineAccessChain(Instruction* inst); + + // Returns the value of |constant_inst| as a uint32_t. + uint32_t GetConstantValue(const analysis::Constant* constant_inst); + + // Returns the array stride of |inst|'s type. + uint32_t GetArrayStride(const Instruction* inst); + + // Returns the type by resolving the index operands |inst|. |inst| must be an + // access chain instruction. + const analysis::Type* GetIndexedType(Instruction* inst); + + // Populates |new_operands| with the operands for the combined access chain. + // Returns false if the access chains cannot be combined. + bool CreateNewInputOperands(Instruction* ptr_input, Instruction* inst, + std::vector* new_operands); + + // Combines the last index of |ptr_input| with the element operand of |inst|. + // Adds the combined operand to |new_operands|. + bool CombineIndices(Instruction* ptr_input, Instruction* inst, + std::vector* new_operands); + + // Returns the opcode to use for the combined access chain. + spv::Op UpdateOpcode(spv::Op base_opcode, spv::Op input_opcode); + + // Returns true if |opcode| is a pointer access chain. + bool IsPtrAccessChain(spv::Op opcode); + + // Returns true if |inst| (an access chain) has 64-bit indices. + bool Has64BitIndices(Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.cpp new file mode 100644 index 00000000..5a2a54b1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/compact_ids_pass.h" + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +// Returns the remapped id of |id| from |result_id_mapping|. If the remapped +// id does not exist, adds a new one to |result_id_mapping| and returns it. +uint32_t GetRemappedId( + std::unordered_map* result_id_mapping, uint32_t id) { + auto it = result_id_mapping->find(id); + if (it == result_id_mapping->end()) { + const uint32_t new_id = + static_cast(result_id_mapping->size()) + 1; + const auto insertion_result = result_id_mapping->emplace(id, new_id); + it = insertion_result.first; + assert(insertion_result.second); + } + return it->second; +} + +} // namespace + +Pass::Status CompactIdsPass::Process() { + bool modified = false; + std::unordered_map result_id_mapping; + + // Disable automatic DebugInfo analysis for the life of the CompactIds pass. + // The DebugInfo manager requires the SPIR-V to be valid to run, but this is + // not true at all times in CompactIds as it remaps all ids. + context()->InvalidateAnalyses(IRContext::kAnalysisDebugInfo); + + context()->module()->ForEachInst( + [&result_id_mapping, &modified](Instruction* inst) { + auto operand = inst->begin(); + while (operand != inst->end()) { + const auto type = operand->type; + if (spvIsIdType(type)) { + assert(operand->words.size() == 1); + uint32_t& id = operand->words[0]; + uint32_t new_id = GetRemappedId(&result_id_mapping, id); + if (id != new_id) { + modified = true; + id = new_id; + // Update data cached in the instruction object. + if (type == SPV_OPERAND_TYPE_RESULT_ID) { + inst->SetResultId(id); + } else if (type == SPV_OPERAND_TYPE_TYPE_ID) { + inst->SetResultType(id); + } + } + } + ++operand; + } + + uint32_t scope_id = inst->GetDebugScope().GetLexicalScope(); + if (scope_id != kNoDebugScope) { + uint32_t new_id = GetRemappedId(&result_id_mapping, scope_id); + if (scope_id != new_id) { + inst->UpdateLexicalScope(new_id); + modified = true; + } + } + uint32_t inlinedat_id = inst->GetDebugInlinedAt(); + if (inlinedat_id != kNoInlinedAt) { + uint32_t new_id = GetRemappedId(&result_id_mapping, inlinedat_id); + if (inlinedat_id != new_id) { + inst->UpdateDebugInlinedAt(new_id); + modified = true; + } + } + }, + true); + + if (context()->module()->id_bound() != result_id_mapping.size() + 1) { + modified = true; + context()->module()->SetIdBound( + static_cast(result_id_mapping.size() + 1)); + // There are ids in the feature manager that could now be invalid + context()->ResetFeatureManager(); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.h new file mode 100644 index 00000000..d97ae0fa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/compact_ids_pass.h @@ -0,0 +1,42 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMPACT_IDS_PASS_H_ +#define SOURCE_OPT_COMPACT_IDS_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class CompactIdsPass : public Pass { + public: + const char* name() const override { return "compact-ids"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMPACT_IDS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/composite.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/composite.cpp new file mode 100644 index 00000000..2b4dca25 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/composite.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/composite.h" + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "spirv/1.2/GLSL.std.450.h" + +namespace spvtools { +namespace opt { + +bool ExtInsMatch(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset) { + uint32_t numIndices = static_cast(extIndices.size()) - extOffset; + if (numIndices != insInst->NumInOperands() - 2) return false; + for (uint32_t i = 0; i < numIndices; ++i) + if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2)) + return false; + return true; +} + +bool ExtInsConflict(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset) { + if (extIndices.size() - extOffset == insInst->NumInOperands() - 2) + return false; + uint32_t extNumIndices = static_cast(extIndices.size()) - extOffset; + uint32_t insNumIndices = insInst->NumInOperands() - 2; + uint32_t numIndices = std::min(extNumIndices, insNumIndices); + for (uint32_t i = 0; i < numIndices; ++i) + if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2)) + return false; + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/composite.h b/engine/engine/3rdlibs/spirv-tools/source/opt/composite.h new file mode 100644 index 00000000..3cc036e4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/composite.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMPOSITE_H_ +#define SOURCE_OPT_COMPOSITE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Return true if the extract indices in |extIndices| starting at |extOffset| +// match indices of insert |insInst|. +bool ExtInsMatch(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset); + +// Return true if indices in |extIndices| starting at |extOffset| and +// indices of insert |insInst| conflict, specifically, if the insert +// changes bits specified by the extract, but changes either more bits +// or less bits than the extract specifies, meaning the exact value being +// inserted cannot be used to replace the extract. +bool ExtInsConflict(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset); + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMPOSITE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.cpp new file mode 100644 index 00000000..a5d4cbe7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.cpp @@ -0,0 +1,1910 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/const_folding_rules.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kExtractCompositeIdInIdx = 0; + +// Returns a constants with the value NaN of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetNan(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::quiet_NaN()); + case 64: + return const_mgr->GetDoubleConst( + std::numeric_limits::quiet_NaN()); + default: + return nullptr; + } +} + +// Returns a constants with the value INF of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetInf(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits::infinity()); + case 64: + return const_mgr->GetDoubleConst(std::numeric_limits::infinity()); + default: + return nullptr; + } +} + +// Returns true if |type| is Float or a vector of Float. +bool HasFloatingPoint(const analysis::Type* type) { + if (type->AsFloat()) { + return true; + } else if (const analysis::Vector* vec_type = type->AsVector()) { + return vec_type->element_type()->AsFloat() != nullptr; + } + + return false; +} + +// Returns a constants with the value |-val| of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* NegateFPConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() == 32) { + float fa = val->GetFloat(); + return const_mgr->GetFloatConst(-fa); + } else if (float_type->width() == 64) { + double da = val->GetDouble(); + return const_mgr->GetDoubleConst(-da); + } + return nullptr; +} + +// Returns a constants with the value |-val| of the given type. +const analysis::Constant* NegateIntConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Integer* int_type = result_type->AsInteger(); + assert(int_type != nullptr); + + if (val->AsNullConstant()) { + return val; + } + + uint64_t new_value = static_cast(-val->GetSignExtendedValue()); + return const_mgr->GetIntConst(new_value, int_type->width(), + int_type->IsSigned()); +} + +// Folds an OpcompositeExtract where input is a composite constant. +ConstantFoldingRule FoldExtractWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + const analysis::Constant* c = constants[kExtractCompositeIdInIdx]; + if (c == nullptr) { + return nullptr; + } + + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + uint32_t element_index = inst->GetSingleWordInOperand(i); + if (c->AsNullConstant()) { + // Return Null for the return type. + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), {}); + } + + auto cc = c->AsCompositeConstant(); + assert(cc != nullptr); + auto components = cc->GetComponents(); + // Protect against invalid IR. Refuse to fold if the index is out + // of bounds. + if (element_index >= components.size()) return nullptr; + c = components[element_index]; + } + return c; + }; +} + +// Folds an OpcompositeInsert where input is a composite constant. +ConstantFoldingRule FoldInsertWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* object = constants[0]; + const analysis::Constant* composite = constants[1]; + if (object == nullptr || composite == nullptr) { + return nullptr; + } + + // If there is more than 1 index, then each additional constant used by the + // index will need to be recreated to use the inserted object. + std::vector chain; + std::vector components; + const analysis::Type* type = nullptr; + const uint32_t final_index = (inst->NumInOperands() - 1); + + // Work down hierarchy of all indexes + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + type = composite->type(); + + if (composite->AsNullConstant()) { + // Make new composite so it can be inserted in the index with the + // non-null value + if (const auto new_composite = + const_mgr->GetNullCompositeConstant(type)) { + // Keep track of any indexes along the way to last index + if (i != final_index) { + chain.push_back(new_composite); + } + components = new_composite->AsCompositeConstant()->GetComponents(); + } else { + // Unsupported input type (such as structs) + return nullptr; + } + } else { + // Keep track of any indexes along the way to last index + if (i != final_index) { + chain.push_back(composite); + } + components = composite->AsCompositeConstant()->GetComponents(); + } + const uint32_t index = inst->GetSingleWordInOperand(i); + composite = components[index]; + } + + // Final index in hierarchy is inserted with new object. + const uint32_t final_operand = inst->GetSingleWordInOperand(final_index); + std::vector ids; + for (size_t i = 0; i < components.size(); i++) { + const analysis::Constant* constant = + (i == final_operand) ? object : components[i]; + Instruction* member_inst = const_mgr->GetDefiningInstruction(constant); + ids.push_back(member_inst->result_id()); + } + const analysis::Constant* new_constant = const_mgr->GetConstant(type, ids); + + // Work backwards up the chain and replace each index with new constant. + for (size_t i = chain.size(); i > 0; i--) { + // Need to insert any previous instruction into the module first. + // Can't just insert in types_values_begin() because it will move above + // where the types are declared. + // Can't compare with location of inst because not all new added + // instructions are added to types_values_ + auto iter = context->types_values_end(); + Module::inst_iterator* pos = &iter; + const_mgr->BuildInstructionAndAddToModule(new_constant, pos); + + composite = chain[i - 1]; + components = composite->AsCompositeConstant()->GetComponents(); + type = composite->type(); + ids.clear(); + for (size_t k = 0; k < components.size(); k++) { + const uint32_t index = + inst->GetSingleWordInOperand(1 + static_cast(i)); + const analysis::Constant* constant = + (k == index) ? new_constant : components[k]; + const uint32_t constant_id = + const_mgr->FindDeclaredConstant(constant, 0); + ids.push_back(constant_id); + } + new_constant = const_mgr->GetConstant(type, ids); + } + + // If multiple constants were created, only need to return the top index. + return new_constant; + }; +} + +ConstantFoldingRule FoldVectorShuffleWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpVectorShuffle); + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* element_type = c1->type()->AsVector()->element_type(); + + std::vector c1_components; + if (const analysis::VectorConstant* vec_const = c1->AsVectorConstant()) { + c1_components = vec_const->GetComponents(); + } else { + assert(c1->AsNullConstant()); + const analysis::Constant* element = + const_mgr->GetConstant(element_type, {}); + c1_components.resize(c1->type()->AsVector()->element_count(), element); + } + std::vector c2_components; + if (const analysis::VectorConstant* vec_const = c2->AsVectorConstant()) { + c2_components = vec_const->GetComponents(); + } else { + assert(c2->AsNullConstant()); + const analysis::Constant* element = + const_mgr->GetConstant(element_type, {}); + c2_components.resize(c2->type()->AsVector()->element_count(), element); + } + + std::vector ids; + const uint32_t undef_literal_value = 0xffffffff; + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + uint32_t index = inst->GetSingleWordInOperand(i); + if (index == undef_literal_value) { + // Don't fold shuffle with undef literal value. + return nullptr; + } else if (index < c1_components.size()) { + Instruction* member_inst = + const_mgr->GetDefiningInstruction(c1_components[index]); + ids.push_back(member_inst->result_id()); + } else { + Instruction* member_inst = const_mgr->GetDefiningInstruction( + c2_components[index - c1_components.size()]); + ids.push_back(member_inst->result_id()); + } + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), ids); + }; +} + +ConstantFoldingRule FoldVectorTimesScalar() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpVectorTimesScalar); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 && c1->IsZero()) { + return c1; + } + + if (c2 && c2->IsZero()) { + // Get or create the NullConstant for this type. + std::vector ids; + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), ids); + } + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsVector() == vector_type); + assert(c1->type()->AsVector()->element_type() == element_type && + c2->type() == element_type); + + // Get a float vector that is the result of vector-times-scalar. + std::vector c1_components = + c1->GetVectorComponents(const_mgr); + std::vector ids; + if (float_type->width() == 32) { + float scalar = c2->GetFloat(); + for (uint32_t i = 0; i < c1_components.size(); ++i) { + utils::FloatProxy result(c1_components[i]->GetFloat() * scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + double scalar = c2->GetDouble(); + for (uint32_t i = 0; i < c1_components.size(); ++i) { + utils::FloatProxy result(c1_components[i]->GetDouble() * + scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +// Returns to the constant that results from tranposing |matrix|. The result +// will have type |result_type|, and |matrix| must exist in |context|. The +// result constant will also exist in |context|. +const analysis::Constant* TransposeMatrix(const analysis::Constant* matrix, + analysis::Matrix* result_type, + IRContext* context) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + if (matrix->AsNullConstant() != nullptr) { + return const_mgr->GetNullCompositeConstant(result_type); + } + + const auto& columns = matrix->AsMatrixConstant()->GetComponents(); + uint32_t number_of_rows = columns[0]->type()->AsVector()->element_count(); + + // Collect the ids of the elements in their new positions. + std::vector> result_elements(number_of_rows); + for (const analysis::Constant* column : columns) { + if (column->AsNullConstant()) { + column = const_mgr->GetNullCompositeConstant(column->type()); + } + const auto& column_components = column->AsVectorConstant()->GetComponents(); + + for (uint32_t row = 0; row < number_of_rows; ++row) { + result_elements[row].push_back( + const_mgr->GetDefiningInstruction(column_components[row]) + ->result_id()); + } + } + + // Create the constant for each row in the result, and collect the ids. + std::vector result_columns(number_of_rows); + for (uint32_t col = 0; col < number_of_rows; ++col) { + auto* element = const_mgr->GetConstant(result_type->element_type(), + result_elements[col]); + result_columns[col] = + const_mgr->GetDefiningInstruction(element)->result_id(); + } + + // Create the matrix constant from the row ids, and return it. + return const_mgr->GetConstant(result_type, result_columns); +} + +const analysis::Constant* FoldTranspose( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpTranspose); + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* matrix = constants[0]; + if (matrix == nullptr) { + return nullptr; + } + + auto* result_type = type_mgr->GetType(inst->type_id()); + return TransposeMatrix(matrix, result_type->AsMatrix(), context); +} + +ConstantFoldingRule FoldVectorTimesMatrix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpVectorTimesMatrix); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsVector() == vector_type); + assert(c1->type()->AsVector()->element_type() == element_type && + c2->type()->AsMatrix()->element_type() == vector_type); + + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + // Get a float vector that is the result of vector-times-matrix. + std::vector c1_components = + c1->GetVectorComponents(const_mgr); + std::vector c2_components = + c2->AsMatrixConstant()->GetComponents(); + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + if (!c2_components[i]->AsNullConstant()) { + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + float c1_scalar = c1_components[j]->GetFloat(); + float c2_scalar = c2_vec->GetComponents()[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < c2_components.size(); ++i) { + double result_scalar = 0.0; + if (!c2_components[i]->AsNullConstant()) { + const analysis::VectorConstant* c2_vec = + c2_components[i]->AsVectorConstant(); + for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) { + double c1_scalar = c1_components[j]->GetDouble(); + double c2_scalar = c2_vec->GetComponents()[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldMatrixTimesVector() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == spv::Op::OpMatrixTimesVector); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsMatrix()->element_type() == vector_type); + assert(c2->type()->AsVector()->element_type() == element_type); + + uint32_t resultVectorSize = result_type->AsVector()->element_count(); + std::vector ids; + + if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) { + std::vector words(float_type->width() / 32, 0); + for (uint32_t i = 0; i < resultVectorSize; ++i) { + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + + // Get a float vector that is the result of matrix-times-vector. + std::vector c1_components = + c1->AsMatrixConstant()->GetComponents(); + std::vector c2_components = + c2->GetVectorComponents(const_mgr); + + if (float_type->width() == 32) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + float result_scalar = 0.0f; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + if (!c1_components[j]->AsNullConstant()) { + float c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetFloat(); + float c2_scalar = c2_components[j]->GetFloat(); + result_scalar += c1_scalar * c2_scalar; + } + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + for (uint32_t i = 0; i < resultVectorSize; ++i) { + double result_scalar = 0.0; + for (uint32_t j = 0; j < c1_components.size(); ++j) { + if (!c1_components[j]->AsNullConstant()) { + double c1_scalar = c1_components[j] + ->AsVectorConstant() + ->GetComponents()[i] + ->GetDouble(); + double c2_scalar = c2_components[j]->GetDouble(); + result_scalar += c1_scalar * c2_scalar; + } + } + utils::FloatProxy result(result_scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldCompositeWithConstants() { + // Folds an OpCompositeConstruct where all of the inputs are constants to a + // constant. A new constant is created if necessary. + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* new_type = type_mgr->GetType(inst->type_id()); + Instruction* type_inst = + context->get_def_use_mgr()->GetDef(inst->type_id()); + + std::vector ids; + for (uint32_t i = 0; i < constants.size(); ++i) { + const analysis::Constant* element_const = constants[i]; + if (element_const == nullptr) { + return nullptr; + } + + uint32_t component_type_id = 0; + if (type_inst->opcode() == spv::Op::OpTypeStruct) { + component_type_id = type_inst->GetSingleWordInOperand(i); + } else if (type_inst->opcode() == spv::Op::OpTypeArray) { + component_type_id = type_inst->GetSingleWordInOperand(0); + } + + uint32_t element_id = + const_mgr->FindDeclaredConstant(element_const, component_type_id); + if (element_id == 0) { + return nullptr; + } + ids.push_back(element_id); + } + return const_mgr->GetConstant(new_type, ids); + }; +} + +// The interface for a function that returns the result of applying a scalar +// floating-point binary operation on |a| and |b|. The type of the return value +// will be |type|. The input constants must also be of type |type|. +using UnaryScalarFoldingRule = std::function; + +// The interface for a function that returns the result of applying a scalar +// floating-point binary operation on |a| and |b|. The type of the return value +// will be |type|. The input constants must also be of type |type|. +using BinaryScalarFoldingRule = std::function; + +// Returns a |ConstantFoldingRule| that folds unary scalar ops +// using |scalar_rule| and unary vectors ops by applying +// |scalar_rule| to the elements of the vector. The |ConstantFoldingRule| +// that is returned assumes that |constants| contains 1 entry. If they are +// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector| +// whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldUnaryOp(UnaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + + const analysis::Constant* arg = + (inst->opcode() == spv::Op::OpExtInst) ? constants[1] : constants[0]; + + if (arg == nullptr) { + return nullptr; + } + + if (vector_type != nullptr) { + std::vector a_components; + std::vector results_components; + + a_components = arg->GetVectorComponents(const_mgr); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // Build the constant object and return it. + std::vector ids; + for (const analysis::Constant* member : results_components) { + ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else { + return scalar_rule(result_type, arg, const_mgr); + } + }; +} + +// Returns a |ConstantFoldingRule| that folds binary scalar ops +// using |scalar_rule| and binary vectors ops by applying +// |scalar_rule| to the elements of the vector. The folding rule assumes that op +// has two inputs. For regular instruction, those are in operands 0 and 1. For +// extended instruction, they are in operands 1 and 2. If an element in +// |constants| is not nullprt, then the constant's type is |Float|, |Integer|, +// or |Vector| whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldBinaryOp(BinaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(constants.size() == inst->NumInOperands()); + assert(constants.size() == (inst->opcode() == spv::Op::OpExtInst ? 3 : 2)); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + + const analysis::Constant* arg1 = + (inst->opcode() == spv::Op::OpExtInst) ? constants[1] : constants[0]; + const analysis::Constant* arg2 = + (inst->opcode() == spv::Op::OpExtInst) ? constants[2] : constants[1]; + + if (arg1 == nullptr || arg2 == nullptr) { + return nullptr; + } + + if (vector_type == nullptr) { + return scalar_rule(result_type, arg1, arg2, const_mgr); + } + + std::vector a_components; + std::vector b_components; + std::vector results_components; + + a_components = arg1->GetVectorComponents(const_mgr); + b_components = arg2->GetVectorComponents(const_mgr); + assert(a_components.size() == b_components.size()); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], b_components[i], + const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // Build the constant object and return it. + std::vector ids; + for (const analysis::Constant* member : results_components) { + ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + }; +} + +// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops +// using |scalar_rule| and unary float point vectors ops by applying +// |scalar_rule| to the elements of the vector. The |ConstantFoldingRule| +// that is returned assumes that |constants| contains 1 entry. If they are +// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector| +// whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { + auto folding_rule = FoldUnaryOp(scalar_rule); + return [folding_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + return folding_rule(context, inst, constants); + }; +} + +// Returns the result of folding the constants in |constants| according the +// |scalar_rule|. If |result_type| is a vector, then |scalar_rule| is applied +// per component. +const analysis::Constant* FoldFPBinaryOp( + BinaryScalarFoldingRule scalar_rule, uint32_t result_type_id, + const std::vector& constants, + IRContext* context) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(result_type_id); + const analysis::Vector* vector_type = result_type->AsVector(); + + if (constants[0] == nullptr || constants[1] == nullptr) { + return nullptr; + } + + if (vector_type != nullptr) { + std::vector a_components; + std::vector b_components; + std::vector results_components; + + a_components = constants[0]->GetVectorComponents(const_mgr); + b_components = constants[1]->GetVectorComponents(const_mgr); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], b_components[i], + const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // Build the constant object and return it. + std::vector ids; + for (const analysis::Constant* member : results_components) { + ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else { + return scalar_rule(result_type, constants[0], constants[1], const_mgr); + } +} + +// Returns a |ConstantFoldingRule| that folds floating point scalars using +// |scalar_rule| and vectors of floating point by applying |scalar_rule| to the +// elements of the vector. The |ConstantFoldingRule| that is returned assumes +// that |constants| contains 2 entries. If they are not |nullptr|, then their +// type is either |Float| or a |Vector| whose element type is |Float|. +ConstantFoldingRule FoldFPBinaryOp(BinaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + if (inst->opcode() == spv::Op::OpExtInst) { + return FoldFPBinaryOp(scalar_rule, inst->type_id(), + {constants[1], constants[2]}, context); + } + return FoldFPBinaryOp(scalar_rule, inst->type_id(), constants, context); + }; +} + +// This macro defines a |UnaryScalarFoldingRule| that performs float to +// integer conversion. +// TODO(greg-lunarg): Support for 64-bit integer types. +UnaryScalarFoldingRule FoldFToIOp() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(integer_type != nullptr); + if (integer_type->width() != 32) return nullptr; + if (float_type->width() == 32) { + float fa = a->GetFloat(); + uint32_t result = integer_type->IsSigned() + ? static_cast(static_cast(fa)) + : static_cast(fa); + std::vector words = {result}; + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + uint32_t result = integer_type->IsSigned() + ? static_cast(static_cast(fa)) + : static_cast(fa); + std::vector words = {result}; + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +// This function defines a |UnaryScalarFoldingRule| that performs integer to +// float conversion. +// TODO(greg-lunarg): Support for 64-bit integer types. +UnaryScalarFoldingRule FoldIToFOp() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Integer* integer_type = a->type()->AsInteger(); + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + assert(integer_type != nullptr); + if (integer_type->width() != 32) return nullptr; + uint32_t ua = a->GetU32(); + if (float_type->width() == 32) { + float result_val = integer_type->IsSigned() + ? static_cast(static_cast(ua)) + : static_cast(ua); + utils::FloatProxy result(result_val); + std::vector words = {result.data()}; + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double result_val = integer_type->IsSigned() + ? static_cast(static_cast(ua)) + : static_cast(ua); + utils::FloatProxy result(result_val); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +// This defines a |UnaryScalarFoldingRule| that performs |OpQuantizeToF16|. +UnaryScalarFoldingRule FoldQuantizeToF16Scalar() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() != 32) { + return nullptr; + } + + float fa = a->GetFloat(); + utils::HexFloat> orignal(fa); + utils::HexFloat> quantized(0); + utils::HexFloat> result(0.0f); + orignal.castTo(quantized, utils::round_direction::kToZero); + quantized.castTo(result, utils::round_direction::kToZero); + std::vector words = {result.getBits()}; + return const_mgr->GetConstant(result_type, words); + }; +} + +// This macro defines a |BinaryScalarFoldingRule| that applies |op|. The +// operator |op| must work for both float and double, and use syntax "f1 op f2". +#define FOLD_FPARITH_OP(op) \ + [](const analysis::Type* result_type_in_macro, const analysis::Constant* a, \ + const analysis::Constant* b, \ + analysis::ConstantManager* const_mgr_in_macro) \ + -> const analysis::Constant* { \ + assert(result_type_in_macro != nullptr && a != nullptr && b != nullptr); \ + assert(result_type_in_macro == a->type() && \ + result_type_in_macro == b->type()); \ + const analysis::Float* float_type_in_macro = \ + result_type_in_macro->AsFloat(); \ + assert(float_type_in_macro != nullptr); \ + if (float_type_in_macro->width() == 32) { \ + float fa = a->GetFloat(); \ + float fb = b->GetFloat(); \ + utils::FloatProxy result_in_macro(fa op fb); \ + std::vector words_in_macro = result_in_macro.GetWords(); \ + return const_mgr_in_macro->GetConstant(result_type_in_macro, \ + words_in_macro); \ + } else if (float_type_in_macro->width() == 64) { \ + double fa = a->GetDouble(); \ + double fb = b->GetDouble(); \ + utils::FloatProxy result_in_macro(fa op fb); \ + std::vector words_in_macro = result_in_macro.GetWords(); \ + return const_mgr_in_macro->GetConstant(result_type_in_macro, \ + words_in_macro); \ + } \ + return nullptr; \ + } + +// Define the folding rule for conversion between floating point and integer +ConstantFoldingRule FoldFToI() { return FoldFPUnaryOp(FoldFToIOp()); } +ConstantFoldingRule FoldIToF() { return FoldFPUnaryOp(FoldIToFOp()); } +ConstantFoldingRule FoldQuantizeToF16() { + return FoldFPUnaryOp(FoldQuantizeToF16Scalar()); +} + +// Define the folding rules for subtraction, addition, multiplication, and +// division for floating point values. +ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); } +ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } +ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } + +// Returns the constant that results from evaluating |numerator| / 0.0. Returns +// |nullptr| if the result could not be evaluated. +const analysis::Constant* FoldFPScalarDivideByZero( + const analysis::Type* result_type, const analysis::Constant* numerator, + analysis::ConstantManager* const_mgr) { + if (numerator == nullptr) { + return nullptr; + } + + if (numerator->IsZero()) { + return GetNan(result_type, const_mgr); + } + + const analysis::Constant* result = GetInf(result_type, const_mgr); + if (result == nullptr) { + return nullptr; + } + + if (numerator->AsFloatConstant()->GetValueAsDouble() < 0.0) { + result = NegateFPConst(result_type, result, const_mgr); + } + return result; +} + +// Returns the result of folding |numerator| / |denominator|. Returns |nullptr| +// if it cannot be folded. +const analysis::Constant* FoldScalarFPDivide( + const analysis::Type* result_type, const analysis::Constant* numerator, + const analysis::Constant* denominator, + analysis::ConstantManager* const_mgr) { + if (denominator == nullptr) { + return nullptr; + } + + if (denominator->IsZero()) { + return FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + } + + uint32_t width = denominator->type()->AsFloat()->width(); + if (width != 32 && width != 64) { + return nullptr; + } + + const analysis::FloatConstant* denominator_float = + denominator->AsFloatConstant(); + if (denominator_float && denominator->GetValueAsDouble() == -0.0) { + const analysis::Constant* result = + FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + if (result != nullptr) + result = NegateFPConst(result_type, result, const_mgr); + return result; + } else { + return FOLD_FPARITH_OP(/)(result_type, numerator, denominator, const_mgr); + } +} + +// Returns the constant folding rule to fold |OpFDiv| with two constants. +ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); } + +bool CompareFloatingPoint(bool op_result, bool op_unordered, + bool need_ordered) { + if (need_ordered) { + // operands are ordered and Operand 1 is |op| Operand 2 + return !op_unordered && op_result; + } else { + // operands are unordered or Operand 1 is |op| Operand 2 + return op_unordered || op_result; + } +} + +// This macro defines a |BinaryScalarFoldingRule| that applies |op|. The +// operator |op| must work for both float and double, and use syntax "f1 op f2". +#define FOLD_FPCMP_OP(op, ord) \ + [](const analysis::Type* result_type, const analysis::Constant* a, \ + const analysis::Constant* b, \ + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { \ + assert(result_type != nullptr && a != nullptr && b != nullptr); \ + assert(result_type->AsBool()); \ + assert(a->type() == b->type()); \ + const analysis::Float* float_type = a->type()->AsFloat(); \ + assert(float_type != nullptr); \ + if (float_type->width() == 32) { \ + float fa = a->GetFloat(); \ + float fb = b->GetFloat(); \ + bool result = CompareFloatingPoint( \ + fa op fb, std::isnan(fa) || std::isnan(fb), ord); \ + std::vector words = {uint32_t(result)}; \ + return const_mgr->GetConstant(result_type, words); \ + } else if (float_type->width() == 64) { \ + double fa = a->GetDouble(); \ + double fb = b->GetDouble(); \ + bool result = CompareFloatingPoint( \ + fa op fb, std::isnan(fa) || std::isnan(fb), ord); \ + std::vector words = {uint32_t(result)}; \ + return const_mgr->GetConstant(result_type, words); \ + } \ + return nullptr; \ + } + +// Define the folding rules for ordered and unordered comparison for floating +// point values. +ConstantFoldingRule FoldFOrdEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(==, true)); +} +ConstantFoldingRule FoldFUnordEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(==, false)); +} +ConstantFoldingRule FoldFOrdNotEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(!=, true)); +} +ConstantFoldingRule FoldFUnordNotEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(!=, false)); +} +ConstantFoldingRule FoldFOrdLessThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<, true)); +} +ConstantFoldingRule FoldFUnordLessThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<, false)); +} +ConstantFoldingRule FoldFOrdGreaterThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>, true)); +} +ConstantFoldingRule FoldFUnordGreaterThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>, false)); +} +ConstantFoldingRule FoldFOrdLessThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<=, true)); +} +ConstantFoldingRule FoldFUnordLessThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<=, false)); +} +ConstantFoldingRule FoldFOrdGreaterThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, true)); +} +ConstantFoldingRule FoldFUnordGreaterThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, false)); +} + +// Folds an OpDot where all of the inputs are constants to a +// constant. A new constant is created if necessary. +ConstantFoldingRule FoldOpDotWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* new_type = type_mgr->GetType(inst->type_id()); + assert(new_type->AsFloat() && "OpDot should have a float return type."); + const analysis::Float* float_type = new_type->AsFloat(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + // If one of the operands is 0, then the result is 0. + bool has_zero_operand = false; + + for (int i = 0; i < 2; ++i) { + if (constants[i]) { + if (constants[i]->AsNullConstant() || + constants[i]->AsVectorConstant()->IsZero()) { + has_zero_operand = true; + break; + } + } + } + + if (has_zero_operand) { + if (float_type->width() == 32) { + utils::FloatProxy result(0.0f); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(float_type, words); + } + if (float_type->width() == 64) { + utils::FloatProxy result(0.0); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(float_type, words); + } + return nullptr; + } + + if (constants[0] == nullptr || constants[1] == nullptr) { + return nullptr; + } + + std::vector a_components; + std::vector b_components; + + a_components = constants[0]->GetVectorComponents(const_mgr); + b_components = constants[1]->GetVectorComponents(const_mgr); + + utils::FloatProxy result(0.0); + std::vector words = result.GetWords(); + const analysis::Constant* result_const = + const_mgr->GetConstant(float_type, words); + for (uint32_t i = 0; i < a_components.size() && result_const != nullptr; + ++i) { + if (a_components[i] == nullptr || b_components[i] == nullptr) { + return nullptr; + } + + const analysis::Constant* component = FOLD_FPARITH_OP(*)( + new_type, a_components[i], b_components[i], const_mgr); + if (component == nullptr) { + return nullptr; + } + result_const = + FOLD_FPARITH_OP(+)(new_type, result_const, component, const_mgr); + } + return result_const; + }; +} + +ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(NegateFPConst); } +ConstantFoldingRule FoldSNegate() { return FoldUnaryOp(NegateIntConst); } + +ConstantFoldingRule FoldFClampFeedingCompare(spv::Op cmp_opcode) { + return [cmp_opcode](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + uint32_t non_const_idx = (constants[0] ? 1 : 0); + uint32_t operand_id = inst->GetSingleWordInOperand(non_const_idx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* operand_type = + type_mgr->GetType(operand_inst->type_id()); + + if (!operand_type->AsFloat()) { + return nullptr; + } + + if (operand_type->AsFloat()->width() != 32 && + operand_type->AsFloat()->width() != 64) { + return nullptr; + } + + if (operand_inst->opcode() != spv::Op::OpExtInst) { + return nullptr; + } + + if (operand_inst->GetSingleWordInOperand(1) != GLSLstd450FClamp) { + return nullptr; + } + + if (constants[1] == nullptr && constants[0] == nullptr) { + return nullptr; + } + + uint32_t max_id = operand_inst->GetSingleWordInOperand(4); + const analysis::Constant* max_const = + const_mgr->FindDeclaredConstant(max_id); + + uint32_t min_id = operand_inst->GetSingleWordInOperand(3); + const analysis::Constant* min_const = + const_mgr->FindDeclaredConstant(min_id); + + bool found_result = false; + bool result = false; + + switch (cmp_opcode) { + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + if (constants[0]) { + if (min_const) { + if (constants[0]->GetValueAsDouble() < + min_const->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == spv::Op::OpFOrdLessThan || + cmp_opcode == spv::Op::OpFUnordLessThan); + } + } + if (max_const) { + if (constants[0]->GetValueAsDouble() >= + max_const->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == spv::Op::OpFOrdLessThan || + cmp_opcode == spv::Op::OpFUnordLessThan); + } + } + } + + if (constants[1]) { + if (max_const) { + if (max_const->GetValueAsDouble() < + constants[1]->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == spv::Op::OpFOrdLessThan || + cmp_opcode == spv::Op::OpFUnordLessThan); + } + } + + if (min_const) { + if (min_const->GetValueAsDouble() >= + constants[1]->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == spv::Op::OpFOrdLessThan || + cmp_opcode == spv::Op::OpFUnordLessThan); + } + } + } + break; + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + if (constants[0]) { + if (min_const) { + if (constants[0]->GetValueAsDouble() <= + min_const->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == spv::Op::OpFOrdLessThanEqual || + cmp_opcode == spv::Op::OpFUnordLessThanEqual); + } + } + if (max_const) { + if (constants[0]->GetValueAsDouble() > + max_const->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == spv::Op::OpFOrdLessThanEqual || + cmp_opcode == spv::Op::OpFUnordLessThanEqual); + } + } + } + + if (constants[1]) { + if (max_const) { + if (max_const->GetValueAsDouble() <= + constants[1]->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == spv::Op::OpFOrdLessThanEqual || + cmp_opcode == spv::Op::OpFUnordLessThanEqual); + } + } + + if (min_const) { + if (min_const->GetValueAsDouble() > + constants[1]->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == spv::Op::OpFOrdLessThanEqual || + cmp_opcode == spv::Op::OpFUnordLessThanEqual); + } + } + } + break; + default: + return nullptr; + } + + if (!found_result) { + return nullptr; + } + + const analysis::Type* bool_type = + context->get_type_mgr()->GetType(inst->type_id()); + const analysis::Constant* result_const = + const_mgr->GetConstant(bool_type, {static_cast(result)}); + assert(result_const); + return result_const; + }; +} + +ConstantFoldingRule FoldFMix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + assert(inst->GetSingleWordInOperand(1) == GLSLstd450FMix && + "Expecting and FMix instruction."); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + // Make sure all FMix operands are constants. + for (uint32_t i = 1; i < 4; i++) { + if (constants[i] == nullptr) { + return nullptr; + } + } + + const analysis::Constant* one; + bool is_vector = false; + const analysis::Type* result_type = constants[1]->type(); + const analysis::Type* base_type = result_type; + if (base_type->AsVector()) { + is_vector = true; + base_type = base_type->AsVector()->element_type(); + } + assert(base_type->AsFloat() != nullptr && + "FMix is suppose to act on floats or vectors of floats."); + + if (base_type->AsFloat()->width() == 32) { + one = const_mgr->GetConstant(base_type, + utils::FloatProxy(1.0f).GetWords()); + } else { + one = const_mgr->GetConstant(base_type, + utils::FloatProxy(1.0).GetWords()); + } + + if (is_vector) { + uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id(); + one = + const_mgr->GetConstant(result_type, std::vector(4, one_id)); + } + + const analysis::Constant* temp1 = FoldFPBinaryOp( + FOLD_FPARITH_OP(-), inst->type_id(), {one, constants[3]}, context); + if (temp1 == nullptr) { + return nullptr; + } + + const analysis::Constant* temp2 = FoldFPBinaryOp( + FOLD_FPARITH_OP(*), inst->type_id(), {constants[1], temp1}, context); + if (temp2 == nullptr) { + return nullptr; + } + const analysis::Constant* temp3 = + FoldFPBinaryOp(FOLD_FPARITH_OP(*), inst->type_id(), + {constants[2], constants[3]}, context); + if (temp3 == nullptr) { + return nullptr; + } + return FoldFPBinaryOp(FOLD_FPARITH_OP(+), inst->type_id(), {temp2, temp3}, + context); + }; +} + +const analysis::Constant* FoldMin(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Integer* int_type = result_type->AsInteger()) { + if (int_type->width() == 32) { + if (int_type->IsSigned()) { + int32_t va = a->GetS32(); + int32_t vb = b->GetS32(); + return (va < vb ? a : b); + } else { + uint32_t va = a->GetU32(); + uint32_t vb = b->GetU32(); + return (va < vb ? a : b); + } + } else if (int_type->width() == 64) { + if (int_type->IsSigned()) { + int64_t va = a->GetS64(); + int64_t vb = b->GetS64(); + return (va < vb ? a : b); + } else { + uint64_t va = a->GetU64(); + uint64_t vb = b->GetU64(); + return (va < vb ? a : b); + } + } + } else if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + return (va < vb ? a : b); + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + return (va < vb ? a : b); + } + } + return nullptr; +} + +const analysis::Constant* FoldMax(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Integer* int_type = result_type->AsInteger()) { + if (int_type->width() == 32) { + if (int_type->IsSigned()) { + int32_t va = a->GetS32(); + int32_t vb = b->GetS32(); + return (va > vb ? a : b); + } else { + uint32_t va = a->GetU32(); + uint32_t vb = b->GetU32(); + return (va > vb ? a : b); + } + } else if (int_type->width() == 64) { + if (int_type->IsSigned()) { + int64_t va = a->GetS64(); + int64_t vb = b->GetS64(); + return (va > vb ? a : b); + } else { + uint64_t va = a->GetU64(); + uint64_t vb = b->GetU64(); + return (va > vb ? a : b); + } + } + } else if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + return (va > vb ? a : b); + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + return (va > vb ? a : b); + } + } + return nullptr; +} + +// Fold an clamp instruction when all three operands are constant. +const analysis::Constant* FoldClamp1( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + // Make sure all Clamp operands are constants. + for (uint32_t i = 1; i < 4; i++) { + if (constants[i] == nullptr) { + return nullptr; + } + } + + const analysis::Constant* temp = FoldFPBinaryOp( + FoldMax, inst->type_id(), {constants[1], constants[2]}, context); + if (temp == nullptr) { + return nullptr; + } + return FoldFPBinaryOp(FoldMin, inst->type_id(), {temp, constants[3]}, + context); +} + +// Fold a clamp instruction when |x <= min_val|. +const analysis::Constant* FoldClamp2( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* min_val = constants[2]; + + if (x == nullptr || min_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldMax, inst->type_id(), {x, min_val}, context); + if (temp == min_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return min_val; + } + return nullptr; +} + +// Fold a clamp instruction when |x >= max_val|. +const analysis::Constant* FoldClamp3( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* max_val = constants[3]; + + if (x == nullptr || max_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldMin, inst->type_id(), {x, max_val}, context); + if (temp == max_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return max_val; + } + return nullptr; +} + +UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float res = static_cast(fp(fa)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double res = fp(fa); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +BinaryScalarFoldingRule FoldFTranscendentalBinary(double (*fp)(double, + double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + assert(float_type == b->type()->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float fb = b->GetFloat(); + float res = static_cast(fp(fa, fb)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double fb = b->GetDouble(); + double res = fp(fa, fb); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +enum Sign { Signed, Unsigned }; + +// Returns a BinaryScalarFoldingRule that applies `op` to the scalars. +// The `signedness` is used to determine if the operands should be interpreted +// as signed or unsigned. If the operands are signed, the value will be sign +// extended before the value is passed to `op`. Otherwise the values will be +// zero extended. +template +BinaryScalarFoldingRule FoldBinaryIntegerOperation(uint64_t (*op)(uint64_t, + uint64_t)) { + return + [op](const analysis::Type* result_type, const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr && b != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type != nullptr); + assert(a->type()->kind() == analysis::Type::kInteger); + assert(b->type()->kind() == analysis::Type::kInteger); + assert(integer_type->width() == a->type()->AsInteger()->width()); + assert(integer_type->width() == b->type()->AsInteger()->width()); + + // In SPIR-V, all operations support unsigned types, but the way they + // are interpreted depends on the opcode. This is why we use the + // template argument to determine how to interpret the operands. + uint64_t ia = (signedness == Signed ? a->GetSignExtendedValue() + : a->GetZeroExtendedValue()); + uint64_t ib = (signedness == Signed ? b->GetSignExtendedValue() + : b->GetZeroExtendedValue()); + uint64_t result = op(ia, ib); + + const analysis::Constant* result_constant = + const_mgr->GenerateIntegerConstant(integer_type, result); + return result_constant; + }; +} + +// A scalar folding rule that folds OpSConvert. +const analysis::Constant* FoldScalarSConvert( + const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) { + assert(result_type != nullptr); + assert(a != nullptr); + assert(const_mgr != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type && "The result type of an SConvert"); + int64_t value = a->GetSignExtendedValue(); + return const_mgr->GenerateIntegerConstant(integer_type, value); +} + +// A scalar folding rule that folds OpUConvert. +const analysis::Constant* FoldScalarUConvert( + const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) { + assert(result_type != nullptr); + assert(a != nullptr); + assert(const_mgr != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + assert(integer_type && "The result type of an UConvert"); + uint64_t value = a->GetZeroExtendedValue(); + + // If the operand was an unsigned value with less than 32-bit, it would have + // been sign extended earlier, and we need to clear those bits. + auto* operand_type = a->type()->AsInteger(); + value = utils::ClearHighBits(value, 64 - operand_type->width()); + return const_mgr->GenerateIntegerConstant(integer_type, value); +} +} // namespace + +void ConstantFoldingRules::AddFoldingRules() { + // Add all folding rules to the list for the opcodes to which they apply. + // Note that the order in which rules are added to the list matters. If a rule + // applies to the instruction, the rest of the rules will not be attempted. + // Take that into consideration. + + rules_[spv::Op::OpCompositeConstruct].push_back(FoldCompositeWithConstants()); + + rules_[spv::Op::OpCompositeExtract].push_back(FoldExtractWithConstants()); + rules_[spv::Op::OpCompositeInsert].push_back(FoldInsertWithConstants()); + + rules_[spv::Op::OpConvertFToS].push_back(FoldFToI()); + rules_[spv::Op::OpConvertFToU].push_back(FoldFToI()); + rules_[spv::Op::OpConvertSToF].push_back(FoldIToF()); + rules_[spv::Op::OpConvertUToF].push_back(FoldIToF()); + rules_[spv::Op::OpSConvert].push_back(FoldUnaryOp(FoldScalarSConvert)); + rules_[spv::Op::OpUConvert].push_back(FoldUnaryOp(FoldScalarUConvert)); + + rules_[spv::Op::OpDot].push_back(FoldOpDotWithConstants()); + rules_[spv::Op::OpFAdd].push_back(FoldFAdd()); + rules_[spv::Op::OpFDiv].push_back(FoldFDiv()); + rules_[spv::Op::OpFMul].push_back(FoldFMul()); + rules_[spv::Op::OpFSub].push_back(FoldFSub()); + + rules_[spv::Op::OpFOrdEqual].push_back(FoldFOrdEqual()); + + rules_[spv::Op::OpFUnordEqual].push_back(FoldFUnordEqual()); + + rules_[spv::Op::OpFOrdNotEqual].push_back(FoldFOrdNotEqual()); + + rules_[spv::Op::OpFUnordNotEqual].push_back(FoldFUnordNotEqual()); + + rules_[spv::Op::OpFOrdLessThan].push_back(FoldFOrdLessThan()); + rules_[spv::Op::OpFOrdLessThan].push_back( + FoldFClampFeedingCompare(spv::Op::OpFOrdLessThan)); + + rules_[spv::Op::OpFUnordLessThan].push_back(FoldFUnordLessThan()); + rules_[spv::Op::OpFUnordLessThan].push_back( + FoldFClampFeedingCompare(spv::Op::OpFUnordLessThan)); + + rules_[spv::Op::OpFOrdGreaterThan].push_back(FoldFOrdGreaterThan()); + rules_[spv::Op::OpFOrdGreaterThan].push_back( + FoldFClampFeedingCompare(spv::Op::OpFOrdGreaterThan)); + + rules_[spv::Op::OpFUnordGreaterThan].push_back(FoldFUnordGreaterThan()); + rules_[spv::Op::OpFUnordGreaterThan].push_back( + FoldFClampFeedingCompare(spv::Op::OpFUnordGreaterThan)); + + rules_[spv::Op::OpFOrdLessThanEqual].push_back(FoldFOrdLessThanEqual()); + rules_[spv::Op::OpFOrdLessThanEqual].push_back( + FoldFClampFeedingCompare(spv::Op::OpFOrdLessThanEqual)); + + rules_[spv::Op::OpFUnordLessThanEqual].push_back(FoldFUnordLessThanEqual()); + rules_[spv::Op::OpFUnordLessThanEqual].push_back( + FoldFClampFeedingCompare(spv::Op::OpFUnordLessThanEqual)); + + rules_[spv::Op::OpFOrdGreaterThanEqual].push_back(FoldFOrdGreaterThanEqual()); + rules_[spv::Op::OpFOrdGreaterThanEqual].push_back( + FoldFClampFeedingCompare(spv::Op::OpFOrdGreaterThanEqual)); + + rules_[spv::Op::OpFUnordGreaterThanEqual].push_back( + FoldFUnordGreaterThanEqual()); + rules_[spv::Op::OpFUnordGreaterThanEqual].push_back( + FoldFClampFeedingCompare(spv::Op::OpFUnordGreaterThanEqual)); + + rules_[spv::Op::OpVectorShuffle].push_back(FoldVectorShuffleWithConstants()); + rules_[spv::Op::OpVectorTimesScalar].push_back(FoldVectorTimesScalar()); + rules_[spv::Op::OpVectorTimesMatrix].push_back(FoldVectorTimesMatrix()); + rules_[spv::Op::OpMatrixTimesVector].push_back(FoldMatrixTimesVector()); + rules_[spv::Op::OpTranspose].push_back(FoldTranspose); + + rules_[spv::Op::OpFNegate].push_back(FoldFNegate()); + rules_[spv::Op::OpSNegate].push_back(FoldSNegate()); + rules_[spv::Op::OpQuantizeToF16].push_back(FoldQuantizeToF16()); + + rules_[spv::Op::OpIAdd].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation( + [](uint64_t a, uint64_t b) { return a + b; }))); + rules_[spv::Op::OpISub].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation( + [](uint64_t a, uint64_t b) { return a - b; }))); + rules_[spv::Op::OpIMul].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation( + [](uint64_t a, uint64_t b) { return a * b; }))); + rules_[spv::Op::OpUDiv].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation( + [](uint64_t a, uint64_t b) { return (b != 0 ? a / b : 0); }))); + rules_[spv::Op::OpSDiv].push_back(FoldBinaryOp( + FoldBinaryIntegerOperation([](uint64_t a, uint64_t b) { + return (b != 0 ? static_cast(static_cast(a) / + static_cast(b)) + : 0); + }))); + rules_[spv::Op::OpUMod].push_back( + FoldBinaryOp(FoldBinaryIntegerOperation( + [](uint64_t a, uint64_t b) { return (b != 0 ? a % b : 0); }))); + + rules_[spv::Op::OpSRem].push_back(FoldBinaryOp( + FoldBinaryIntegerOperation([](uint64_t a, uint64_t b) { + return (b != 0 ? static_cast(static_cast(a) % + static_cast(b)) + : 0); + }))); + + rules_[spv::Op::OpSMod].push_back(FoldBinaryOp( + FoldBinaryIntegerOperation([](uint64_t a, uint64_t b) { + if (b == 0) return static_cast(0ull); + + int64_t signed_a = static_cast(a); + int64_t signed_b = static_cast(b); + int64_t result = signed_a % signed_b; + if ((signed_b < 0) != (result < 0)) result += signed_b; + return static_cast(result); + }))); + + // Add rules for GLSLstd450 + FeatureManager* feature_manager = context_->get_feature_mgr(); + uint32_t ext_inst_glslstd450_id = + feature_manager->GetExtInstImportId_GLSLstd450(); + if (ext_inst_glslstd450_id != 0) { + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMix}].push_back(FoldFMix()); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::cos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Tan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::tan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Asin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::asin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Acos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::acos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::atan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); + +#ifdef __ANDROID__ + // Android NDK r15c targeting ABI 15 doesn't have full support for C++11 + // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't + // available up until ABI 18 so we use a shim + auto log2_shim = [](double v) -> double { return log(v) / log(2.0); }; + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(log2_shim))); +#else + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log2))); +#endif + + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sqrt}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sqrt))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan2}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::atan2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Pow}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::pow))); + } +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.h b/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.h new file mode 100644 index 00000000..fa345321 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/const_folding_rules.h @@ -0,0 +1,136 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONST_FOLDING_RULES_H_ +#define SOURCE_OPT_CONST_FOLDING_RULES_H_ + +#include +#include + +#include "source/opt/constants.h" + +namespace spvtools { +namespace opt { + +// Constant Folding Rules: +// +// The folding mechanism is built around the concept of a |ConstantFoldingRule|. +// A constant folding rule is a function that implements a method of simplifying +// an instruction to a constant. +// +// The inputs to a folding rule are: +// |inst| - the instruction to be simplified. +// |constants| - if an in-operands is an id of a constant, then the +// corresponding value in |constants| contains that +// constant value. Otherwise, the corresponding entry in +// |constants| is |nullptr|. +// +// A constant folding rule returns a pointer to an Constant if |inst| can be +// simplified using this rule. Otherwise, it returns |nullptr|. +// +// See const_folding_rules.cpp for examples on how to write a constant folding +// rule. +// +// Be sure to add new constant folding rules to the table of constant folding +// rules in the constructor for ConstantFoldingRules. The new rule should be +// added to the list for every opcode that it applies to. Note that earlier +// rules in the list are given priority. That is, if an earlier rule is able to +// fold an instruction, the later rules will not be attempted. + +using ConstantFoldingRule = std::function& constants)>; + +class ConstantFoldingRules { + protected: + // The |Key| and |Value| structs are used to by-pass a "decorated name length + // exceeded, name was truncated" warning on VS2013 and VS2015. + struct Key { + uint32_t instruction_set; + uint32_t opcode; + }; + + friend bool operator<(const Key& a, const Key& b) { + if (a.instruction_set < b.instruction_set) { + return true; + } + if (a.instruction_set > b.instruction_set) { + return false; + } + return a.opcode < b.opcode; + } + + struct Value { + std::vector value; + void push_back(ConstantFoldingRule rule) { value.push_back(rule); } + }; + + public: + ConstantFoldingRules(IRContext* ctx) : context_(ctx) {} + virtual ~ConstantFoldingRules() = default; + + // Returns true if there is at least 1 folding rule for |opcode|. + bool HasFoldingRule(const Instruction* inst) const { + return !GetRulesForInstruction(inst).empty(); + } + + // Returns true if there is at least 1 folding rule for |inst|. + const std::vector& GetRulesForInstruction( + const Instruction* inst) const { + if (inst->opcode() != spv::Op::OpExtInst) { + auto it = rules_.find(inst->opcode()); + if (it != rules_.end()) { + return it->second.value; + } + } else { + uint32_t ext_inst_id = inst->GetSingleWordInOperand(0); + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + auto it = ext_rules_.find({ext_inst_id, ext_opcode}); + if (it != ext_rules_.end()) { + return it->second.value; + } + } + return empty_vector_; + } + + // Add the folding rules. + virtual void AddFoldingRules(); + + protected: + struct hasher { + size_t operator()(const spv::Op& op) const noexcept { + return std::hash()(uint32_t(op)); + } + }; + + // |rules[opcode]| is the set of rules that can be applied to instructions + // with |opcode| as the opcode. + std::unordered_map rules_; + + // The folding rules for extended instructions. + std::map ext_rules_; + + private: + // The context that the instruction to be folded will be a part of. + IRContext* context_; + + // The empty set of rules to be used as the default return value in + // |GetRulesForInstruction|. + std::vector empty_vector_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONST_FOLDING_RULES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/constants.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/constants.cpp new file mode 100644 index 00000000..7dc02dea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/constants.cpp @@ -0,0 +1,573 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/constants.h" + +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +float Constant::GetFloat() const { + assert(type()->AsFloat() != nullptr && type()->AsFloat()->width() == 32); + + if (const FloatConstant* fc = AsFloatConstant()) { + return fc->GetFloatValue(); + } else { + assert(AsNullConstant() && "Must be a floating point constant."); + return 0.0f; + } +} + +double Constant::GetDouble() const { + assert(type()->AsFloat() != nullptr && type()->AsFloat()->width() == 64); + + if (const FloatConstant* fc = AsFloatConstant()) { + return fc->GetDoubleValue(); + } else { + assert(AsNullConstant() && "Must be a floating point constant."); + return 0.0; + } +} + +double Constant::GetValueAsDouble() const { + assert(type()->AsFloat() != nullptr); + if (type()->AsFloat()->width() == 32) { + return GetFloat(); + } else { + assert(type()->AsFloat()->width() == 64); + return GetDouble(); + } +} + +uint32_t Constant::GetU32() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 32); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetU32BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0u; + } +} + +uint64_t Constant::GetU64() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 64); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetU64BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0u; + } +} + +int32_t Constant::GetS32() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 32); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetS32BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0; + } +} + +int64_t Constant::GetS64() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 64); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetS64BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0; + } +} + +uint64_t Constant::GetZeroExtendedValue() const { + const auto* int_type = type()->AsInteger(); + assert(int_type != nullptr); + const auto width = int_type->width(); + assert(width <= 64); + + uint64_t value = 0; + if (const IntConstant* ic = AsIntConstant()) { + if (width <= 32) { + value = ic->GetU32BitValue(); + } else { + value = ic->GetU64BitValue(); + } + } else { + assert(AsNullConstant() && "Must be an integer constant."); + } + return value; +} + +int64_t Constant::GetSignExtendedValue() const { + const auto* int_type = type()->AsInteger(); + assert(int_type != nullptr); + const auto width = int_type->width(); + assert(width <= 64); + + int64_t value = 0; + if (const IntConstant* ic = AsIntConstant()) { + if (width <= 32) { + // Let the C++ compiler do the sign extension. + value = int64_t(ic->GetS32BitValue()); + } else { + value = ic->GetS64BitValue(); + } + } else { + assert(AsNullConstant() && "Must be an integer constant."); + } + return value; +} + +ConstantManager::ConstantManager(IRContext* ctx) : ctx_(ctx) { + // Populate the constant table with values from constant declarations in the + // module. The values of each OpConstant declaration is the identity + // assignment (i.e., each constant is its own value). + for (const auto& inst : ctx_->module()->GetConstants()) { + MapInst(inst); + } +} + +Type* ConstantManager::GetType(const Instruction* inst) const { + return context()->get_type_mgr()->GetType(inst->type_id()); +} + +std::vector ConstantManager::GetOperandConstants( + const Instruction* inst) const { + std::vector constants; + constants.reserve(inst->NumInOperands()); + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + constants.push_back(nullptr); + } else { + uint32_t id = operand->words[0]; + const analysis::Constant* constant = FindDeclaredConstant(id); + constants.push_back(constant); + } + } + return constants; +} + +uint32_t ConstantManager::FindDeclaredConstant(const Constant* c, + uint32_t type_id) const { + c = FindConstant(c); + if (c == nullptr) { + return 0; + } + + for (auto range = const_val_to_id_.equal_range(c); + range.first != range.second; ++range.first) { + Instruction* const_def = + context()->get_def_use_mgr()->GetDef(range.first->second); + if (type_id == 0 || const_def->type_id() == type_id) { + return range.first->second; + } + } + return 0; +} + +std::vector ConstantManager::GetConstantsFromIds( + const std::vector& ids) const { + std::vector constants; + for (uint32_t id : ids) { + if (const Constant* c = FindDeclaredConstant(id)) { + constants.push_back(c); + } else { + return {}; + } + } + return constants; +} + +Instruction* ConstantManager::BuildInstructionAndAddToModule( + const Constant* new_const, Module::inst_iterator* pos, uint32_t type_id) { + // TODO(1841): Handle id overflow. + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) { + return nullptr; + } + + auto new_inst = CreateInstruction(new_id, new_const, type_id); + if (!new_inst) { + return nullptr; + } + auto* new_inst_ptr = new_inst.get(); + *pos = pos->InsertBefore(std::move(new_inst)); + ++(*pos); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); + MapConstantToInst(new_const, new_inst_ptr); + return new_inst_ptr; +} + +Instruction* ConstantManager::GetDefiningInstruction( + const Constant* c, uint32_t type_id, Module::inst_iterator* pos) { + uint32_t decl_id = FindDeclaredConstant(c, type_id); + if (decl_id == 0) { + auto iter = context()->types_values_end(); + if (pos == nullptr) pos = &iter; + return BuildInstructionAndAddToModule(c, pos, type_id); + } else { + auto def = context()->get_def_use_mgr()->GetDef(decl_id); + assert(def != nullptr); + assert((type_id == 0 || def->type_id() == type_id) && + "This constant already has an instruction with a different type."); + return def; + } +} + +std::unique_ptr ConstantManager::CreateConstant( + const Type* type, const std::vector& literal_words_or_ids) const { + if (literal_words_or_ids.size() == 0) { + // Constant declared with OpConstantNull + return MakeUnique(type); + } else if (auto* bt = type->AsBool()) { + assert(literal_words_or_ids.size() == 1 && + "Bool constant should be declared with one operand"); + return MakeUnique(bt, literal_words_or_ids.front()); + } else if (auto* it = type->AsInteger()) { + return MakeUnique(it, literal_words_or_ids); + } else if (auto* ft = type->AsFloat()) { + return MakeUnique(ft, literal_words_or_ids); + } else if (auto* vt = type->AsVector()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + // All components of VectorConstant must be of type Bool, Integer or Float. + if (!std::all_of(components.begin(), components.end(), + [](const Constant* c) { + if (c->type()->AsBool() || c->type()->AsInteger() || + c->type()->AsFloat()) { + return true; + } else { + return false; + } + })) + return nullptr; + // All components of VectorConstant must be in the same type. + const auto* component_type = components.front()->type(); + if (!std::all_of(components.begin(), components.end(), + [&component_type](const Constant* c) { + if (c->type() == component_type) return true; + return false; + })) + return nullptr; + return MakeUnique(vt, components); + } else if (auto* mt = type->AsMatrix()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(mt, components); + } else if (auto* st = type->AsStruct()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(st, components); + } else if (auto* at = type->AsArray()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(at, components); + } else { + return nullptr; + } +} + +const Constant* ConstantManager::GetConstantFromInst(const Instruction* inst) { + std::vector literal_words_or_ids; + + // Collect the constant defining literals or component ids. + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + literal_words_or_ids.insert(literal_words_or_ids.end(), + inst->GetInOperand(i).words.begin(), + inst->GetInOperand(i).words.end()); + } + + switch (inst->opcode()) { + // OpConstant{True|False} have the value embedded in the opcode. So they + // are not handled by the for-loop above. Here we add the value explicitly. + case spv::Op::OpConstantTrue: + literal_words_or_ids.push_back(true); + break; + case spv::Op::OpConstantFalse: + literal_words_or_ids.push_back(false); + break; + case spv::Op::OpConstantNull: + case spv::Op::OpConstant: + case spv::Op::OpConstantComposite: + case spv::Op::OpSpecConstantComposite: + break; + default: + return nullptr; + } + + return GetConstant(GetType(inst), literal_words_or_ids); +} + +std::unique_ptr ConstantManager::CreateInstruction( + uint32_t id, const Constant* c, uint32_t type_id) const { + uint32_t type = + (type_id == 0) ? context()->get_type_mgr()->GetId(c->type()) : type_id; + if (c->AsNullConstant()) { + return MakeUnique(context(), spv::Op::OpConstantNull, type, id, + std::initializer_list{}); + } else if (const BoolConstant* bc = c->AsBoolConstant()) { + return MakeUnique( + context(), + bc->value() ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse, type, + id, std::initializer_list{}); + } else if (const IntConstant* ic = c->AsIntConstant()) { + return MakeUnique( + context(), spv::Op::OpConstant, type, id, + std::initializer_list{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + ic->words())}); + } else if (const FloatConstant* fc = c->AsFloatConstant()) { + return MakeUnique( + context(), spv::Op::OpConstant, type, id, + std::initializer_list{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + fc->words())}); + } else if (const CompositeConstant* cc = c->AsCompositeConstant()) { + return CreateCompositeInstruction(id, cc, type_id); + } else { + return nullptr; + } +} + +std::unique_ptr ConstantManager::CreateCompositeInstruction( + uint32_t result_id, const CompositeConstant* cc, uint32_t type_id) const { + std::vector operands; + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + uint32_t component_index = 0; + for (const Constant* component_const : cc->GetComponents()) { + uint32_t component_type_id = 0; + if (type_inst && type_inst->opcode() == spv::Op::OpTypeStruct) { + component_type_id = type_inst->GetSingleWordInOperand(component_index); + } else if (type_inst && type_inst->opcode() == spv::Op::OpTypeArray) { + component_type_id = type_inst->GetSingleWordInOperand(0); + } + uint32_t id = FindDeclaredConstant(component_const, component_type_id); + + if (id == 0) { + // Cannot get the id of the component constant, while all components + // should have been added to the module prior to the composite constant. + // Cannot create OpConstantComposite instruction in this case. + return nullptr; + } + operands.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + std::initializer_list{id}); + component_index++; + } + uint32_t type = + (type_id == 0) ? context()->get_type_mgr()->GetId(cc->type()) : type_id; + return MakeUnique(context(), spv::Op::OpConstantComposite, type, + result_id, std::move(operands)); +} + +const Constant* ConstantManager::GetConstant( + const Type* type, const std::vector& literal_words_or_ids) { + auto cst = CreateConstant(type, literal_words_or_ids); + return cst ? RegisterConstant(std::move(cst)) : nullptr; +} + +const Constant* ConstantManager::GetNullCompositeConstant(const Type* type) { + std::vector literal_words_or_id; + + if (type->AsVector()) { + const Type* element_type = type->AsVector()->element_type(); + const uint32_t null_id = GetNullConstId(element_type); + const uint32_t element_count = type->AsVector()->element_count(); + for (uint32_t i = 0; i < element_count; i++) { + literal_words_or_id.push_back(null_id); + } + } else if (type->AsMatrix()) { + const Type* element_type = type->AsMatrix()->element_type(); + const uint32_t null_id = GetNullConstId(element_type); + const uint32_t element_count = type->AsMatrix()->element_count(); + for (uint32_t i = 0; i < element_count; i++) { + literal_words_or_id.push_back(null_id); + } + } else if (type->AsStruct()) { + // TODO (sfricke-lunarg) add proper struct support + return nullptr; + } else if (type->AsArray()) { + const Type* element_type = type->AsArray()->element_type(); + const uint32_t null_id = GetNullConstId(element_type); + assert(type->AsArray()->length_info().words[0] == + analysis::Array::LengthInfo::kConstant && + "unexpected array length"); + const uint32_t element_count = type->AsArray()->length_info().words[0]; + for (uint32_t i = 0; i < element_count; i++) { + literal_words_or_id.push_back(null_id); + } + } else { + return nullptr; + } + + return GetConstant(type, literal_words_or_id); +} + +const Constant* ConstantManager::GetNumericVectorConstantWithWords( + const Vector* type, const std::vector& literal_words) { + const auto* element_type = type->element_type(); + uint32_t words_per_element = 0; + if (const auto* float_type = element_type->AsFloat()) + words_per_element = float_type->width() / 32; + else if (const auto* int_type = element_type->AsInteger()) + words_per_element = int_type->width() / 32; + else if (element_type->AsBool() != nullptr) + words_per_element = 1; + + if (words_per_element != 1 && words_per_element != 2) return nullptr; + + if (words_per_element * type->element_count() != + static_cast(literal_words.size())) { + return nullptr; + } + + std::vector element_ids; + for (uint32_t i = 0; i < type->element_count(); ++i) { + auto first_word = literal_words.begin() + (words_per_element * i); + std::vector const_data(first_word, + first_word + words_per_element); + const analysis::Constant* element_constant = + GetConstant(element_type, const_data); + auto element_id = GetDefiningInstruction(element_constant)->result_id(); + element_ids.push_back(element_id); + } + + return GetConstant(type, element_ids); +} + +uint32_t ConstantManager::GetFloatConstId(float val) { + const Constant* c = GetFloatConst(val); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetFloatConst(float val) { + Type* float_type = context()->get_type_mgr()->GetFloatType(); + utils::FloatProxy v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + +uint32_t ConstantManager::GetDoubleConstId(double val) { + const Constant* c = GetDoubleConst(val); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetDoubleConst(double val) { + Type* float_type = context()->get_type_mgr()->GetDoubleType(); + utils::FloatProxy v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + +uint32_t ConstantManager::GetSIntConstId(int32_t val) { + Type* sint_type = context()->get_type_mgr()->GetSIntType(); + const Constant* c = GetConstant(sint_type, {static_cast(val)}); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetIntConst(uint64_t val, int32_t bitWidth, + bool isSigned) { + Type* int_type = context()->get_type_mgr()->GetIntType(bitWidth, isSigned); + + if (isSigned) { + // Sign extend the value. + int32_t num_of_bit_to_ignore = 64 - bitWidth; + val = static_cast(val << num_of_bit_to_ignore) >> + num_of_bit_to_ignore; + } else if (bitWidth < 64) { + // Clear the upper bit that are not used. + uint64_t mask = ((1ull << bitWidth) - 1); + val &= mask; + } + + if (bitWidth <= 32) { + return GetConstant(int_type, {static_cast(val)}); + } + + // If the value is more than 32-bit, we need to split the operands into two + // 32-bit integers. + return GetConstant( + int_type, {static_cast(val), static_cast(val >> 32)}); +} + +uint32_t ConstantManager::GetUIntConstId(uint32_t val) { + Type* uint_type = context()->get_type_mgr()->GetUIntType(); + const Constant* c = GetConstant(uint_type, {val}); + return GetDefiningInstruction(c)->result_id(); +} + +uint32_t ConstantManager::GetNullConstId(const Type* type) { + const Constant* c = GetConstant(type, {}); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GenerateIntegerConstant( + const analysis::Integer* integer_type, uint64_t result) { + assert(integer_type != nullptr); + + std::vector words; + if (integer_type->width() == 64) { + // In the 64-bit case, two words are needed to represent the value. + words = {static_cast(result), + static_cast(result >> 32)}; + } else { + // In all other cases, only a single word is needed. + assert(integer_type->width() <= 32); + if (integer_type->IsSigned()) { + result = utils::SignExtendValue(result, integer_type->width()); + } else { + result = utils::ZeroExtendValue(result, integer_type->width()); + } + words = {static_cast(result)}; + } + return GetConstant(integer_type, words); +} + +std::vector Constant::GetVectorComponents( + analysis::ConstantManager* const_mgr) const { + std::vector components; + const analysis::VectorConstant* a = this->AsVectorConstant(); + const analysis::Vector* vector_type = this->type()->AsVector(); + assert(vector_type != nullptr); + if (a != nullptr) { + for (uint32_t i = 0; i < vector_type->element_count(); ++i) { + components.push_back(a->GetComponents()[i]); + } + } else { + const analysis::Type* element_type = vector_type->element_type(); + const analysis::Constant* element_null_const = + const_mgr->GetConstant(element_type, {}); + for (uint32_t i = 0; i < vector_type->element_count(); ++i) { + components.push_back(element_null_const); + } + } + return components; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/constants.h b/engine/engine/3rdlibs/spirv-tools/source/opt/constants.h new file mode 100644 index 00000000..534afa6f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/constants.h @@ -0,0 +1,749 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONSTANTS_H_ +#define SOURCE_OPT_CONSTANTS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/module.h" +#include "source/opt/type_manager.h" +#include "source/opt/types.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +namespace analysis { + +// Class hierarchy to represent the normal constants defined through +// OpConstantTrue, OpConstantFalse, OpConstant, OpConstantNull and +// OpConstantComposite instructions. +// TODO(qining): Add class for constants defined with OpConstantSampler. +class Constant; +class ScalarConstant; +class IntConstant; +class FloatConstant; +class BoolConstant; +class CompositeConstant; +class StructConstant; +class VectorConstant; +class MatrixConstant; +class ArrayConstant; +class NullConstant; +class ConstantManager; + +// Abstract class for a SPIR-V constant. It has a bunch of As methods, +// which is used as a way to probe the actual +class Constant { + public: + Constant() = delete; + virtual ~Constant() = default; + + // Make a deep copy of this constant. + virtual std::unique_ptr Copy() const = 0; + + // reflections + virtual ScalarConstant* AsScalarConstant() { return nullptr; } + virtual IntConstant* AsIntConstant() { return nullptr; } + virtual FloatConstant* AsFloatConstant() { return nullptr; } + virtual BoolConstant* AsBoolConstant() { return nullptr; } + virtual CompositeConstant* AsCompositeConstant() { return nullptr; } + virtual StructConstant* AsStructConstant() { return nullptr; } + virtual VectorConstant* AsVectorConstant() { return nullptr; } + virtual MatrixConstant* AsMatrixConstant() { return nullptr; } + virtual ArrayConstant* AsArrayConstant() { return nullptr; } + virtual NullConstant* AsNullConstant() { return nullptr; } + + virtual const ScalarConstant* AsScalarConstant() const { return nullptr; } + virtual const IntConstant* AsIntConstant() const { return nullptr; } + virtual const FloatConstant* AsFloatConstant() const { return nullptr; } + virtual const BoolConstant* AsBoolConstant() const { return nullptr; } + virtual const CompositeConstant* AsCompositeConstant() const { + return nullptr; + } + virtual const StructConstant* AsStructConstant() const { return nullptr; } + virtual const VectorConstant* AsVectorConstant() const { return nullptr; } + virtual const MatrixConstant* AsMatrixConstant() const { return nullptr; } + virtual const ArrayConstant* AsArrayConstant() const { return nullptr; } + virtual const NullConstant* AsNullConstant() const { return nullptr; } + + // Returns the float representation of the constant. Must be a 32 bit + // Float type. + float GetFloat() const; + + // Returns the double representation of the constant. Must be a 64 bit + // Float type. + double GetDouble() const; + + // Returns the double representation of the constant. Must be a 32-bit or + // 64-bit Float type. + double GetValueAsDouble() const; + + // Returns uint32_t representation of the constant. Must be a 32 bit + // Integer type. + uint32_t GetU32() const; + + // Returns uint64_t representation of the constant. Must be a 64 bit + // Integer type. + uint64_t GetU64() const; + + // Returns int32_t representation of the constant. Must be a 32 bit + // Integer type. + int32_t GetS32() const; + + // Returns int64_t representation of the constant. Must be a 64 bit + // Integer type. + int64_t GetS64() const; + + // Returns the zero-extended representation of an integer constant. Must + // be an integral constant of at most 64 bits. + uint64_t GetZeroExtendedValue() const; + + // Returns the sign-extended representation of an integer constant. Must + // be an integral constant of at most 64 bits. + int64_t GetSignExtendedValue() const; + + // Returns true if the constant is a zero or a composite containing 0s. + virtual bool IsZero() const { return false; } + + const Type* type() const { return type_; } + + // Returns an std::vector containing the elements of |constant|. The type of + // |constant| must be |Vector|. + std::vector GetVectorComponents( + ConstantManager* const_mgr) const; + + protected: + Constant(const Type* ty) : type_(ty) {} + + // The type of this constant. + const Type* type_; +}; + +// Abstract class for scalar type constants. +class ScalarConstant : public Constant { + public: + ScalarConstant() = delete; + ScalarConstant* AsScalarConstant() override { return this; } + const ScalarConstant* AsScalarConstant() const override { return this; } + + // Returns a const reference of the value of this constant in 32-bit words. + virtual const std::vector& words() const { return words_; } + + // Returns true if the value is zero. + bool IsZero() const override { + bool is_zero = true; + for (uint32_t v : words()) { + if (v != 0) { + is_zero = false; + break; + } + } + return is_zero; + } + + uint32_t GetU32BitValue() const { + // Relies on unsigned values smaller than 32-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 1); + return words()[0]; + } + + uint64_t GetU64BitValue() const { + // Relies on unsigned values smaller than 64-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 2); + return static_cast(words()[1]) << 32 | + static_cast(words()[0]); + } + + protected: + ScalarConstant(const Type* ty, const std::vector& w) + : Constant(ty), words_(w) {} + ScalarConstant(const Type* ty, std::vector&& w) + : Constant(ty), words_(std::move(w)) {} + std::vector words_; +}; + +// Integer type constant. +class IntConstant : public ScalarConstant { + public: + IntConstant(const Integer* ty, const std::vector& w) + : ScalarConstant(ty, w) {} + IntConstant(const Integer* ty, std::vector&& w) + : ScalarConstant(ty, std::move(w)) {} + + IntConstant* AsIntConstant() override { return this; } + const IntConstant* AsIntConstant() const override { return this; } + + int32_t GetS32BitValue() const { + // Relies on signed values smaller than 32-bit being sign extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 1); + return words()[0]; + } + + int64_t GetS64BitValue() const { + // Relies on unsigned values smaller than 64-bit being sign extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 2); + return static_cast(words()[1]) << 32 | + static_cast(words()[0]); + } + + // Make a copy of this IntConstant instance. + std::unique_ptr CopyIntConstant() const { + return MakeUnique(type_->AsInteger(), words_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyIntConstant().release()); + } +}; + +// Float type constant. +class FloatConstant : public ScalarConstant { + public: + FloatConstant(const Float* ty, const std::vector& w) + : ScalarConstant(ty, w) {} + FloatConstant(const Float* ty, std::vector&& w) + : ScalarConstant(ty, std::move(w)) {} + + FloatConstant* AsFloatConstant() override { return this; } + const FloatConstant* AsFloatConstant() const override { return this; } + + // Make a copy of this FloatConstant instance. + std::unique_ptr CopyFloatConstant() const { + return MakeUnique(type_->AsFloat(), words_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyFloatConstant().release()); + } + + // Returns the float value of |this|. The type of |this| must be |Float| with + // width of 32. + float GetFloatValue() const { + assert(type()->AsFloat()->width() == 32 && + "Not a 32-bit floating point value."); + utils::FloatProxy a(words()[0]); + return a.getAsFloat(); + } + + // Returns the double value of |this|. The type of |this| must be |Float| + // with width of 64. + double GetDoubleValue() const { + assert(type()->AsFloat()->width() == 64 && + "Not a 32-bit floating point value."); + uint64_t combined_words = words()[1]; + combined_words = combined_words << 32; + combined_words |= words()[0]; + utils::FloatProxy a(combined_words); + return a.getAsFloat(); + } +}; + +// Bool type constant. +class BoolConstant : public ScalarConstant { + public: + BoolConstant(const Bool* ty, bool v) + : ScalarConstant(ty, {static_cast(v)}), value_(v) {} + + BoolConstant* AsBoolConstant() override { return this; } + const BoolConstant* AsBoolConstant() const override { return this; } + + // Make a copy of this BoolConstant instance. + std::unique_ptr CopyBoolConstant() const { + return MakeUnique(type_->AsBool(), value_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyBoolConstant().release()); + } + + bool value() const { return value_; } + + private: + bool value_; +}; + +// Abstract class for composite constants. +class CompositeConstant : public Constant { + public: + CompositeConstant() = delete; + CompositeConstant* AsCompositeConstant() override { return this; } + const CompositeConstant* AsCompositeConstant() const override { return this; } + + // Returns a const reference of the components held in this composite + // constant. + virtual const std::vector& GetComponents() const { + return components_; + } + + bool IsZero() const override { + for (const Constant* c : GetComponents()) { + if (!c->IsZero()) { + return false; + } + } + return true; + } + + protected: + CompositeConstant(const Type* ty) : Constant(ty), components_() {} + CompositeConstant(const Type* ty, + const std::vector& components) + : Constant(ty), components_(components) {} + CompositeConstant(const Type* ty, std::vector&& components) + : Constant(ty), components_(std::move(components)) {} + std::vector components_; +}; + +// Struct type constant. +class StructConstant : public CompositeConstant { + public: + StructConstant(const Struct* ty) : CompositeConstant(ty) {} + StructConstant(const Struct* ty, + const std::vector& components) + : CompositeConstant(ty, components) {} + StructConstant(const Struct* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)) {} + + StructConstant* AsStructConstant() override { return this; } + const StructConstant* AsStructConstant() const override { return this; } + + // Make a copy of this StructConstant instance. + std::unique_ptr CopyStructConstant() const { + return MakeUnique(type_->AsStruct(), components_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyStructConstant().release()); + } +}; + +// Vector type constant. +class VectorConstant : public CompositeConstant { + public: + VectorConstant(const Vector* ty) + : CompositeConstant(ty), component_type_(ty->element_type()) {} + VectorConstant(const Vector* ty, + const std::vector& components) + : CompositeConstant(ty, components), + component_type_(ty->element_type()) {} + VectorConstant(const Vector* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)), + component_type_(ty->element_type()) {} + + VectorConstant* AsVectorConstant() override { return this; } + const VectorConstant* AsVectorConstant() const override { return this; } + + // Make a copy of this VectorConstant instance. + std::unique_ptr CopyVectorConstant() const { + auto another = MakeUnique(type_->AsVector()); + another->components_.insert(another->components_.end(), components_.begin(), + components_.end()); + return another; + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyVectorConstant().release()); + } + + const Type* component_type() const { return component_type_; } + + private: + const Type* component_type_; +}; + +// Matrix type constant. +class MatrixConstant : public CompositeConstant { + public: + MatrixConstant(const Matrix* ty) + : CompositeConstant(ty), component_type_(ty->element_type()) {} + MatrixConstant(const Matrix* ty, + const std::vector& components) + : CompositeConstant(ty, components), + component_type_(ty->element_type()) {} + MatrixConstant(const Vector* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)), + component_type_(ty->element_type()) {} + + MatrixConstant* AsMatrixConstant() override { return this; } + const MatrixConstant* AsMatrixConstant() const override { return this; } + + // Make a copy of this MatrixConstant instance. + std::unique_ptr CopyMatrixConstant() const { + auto another = MakeUnique(type_->AsMatrix()); + another->components_.insert(another->components_.end(), components_.begin(), + components_.end()); + return another; + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyMatrixConstant().release()); + } + + const Type* component_type() { return component_type_; } + + private: + const Type* component_type_; +}; + +// Array type constant. +class ArrayConstant : public CompositeConstant { + public: + ArrayConstant(const Array* ty) : CompositeConstant(ty) {} + ArrayConstant(const Array* ty, const std::vector& components) + : CompositeConstant(ty, components) {} + ArrayConstant(const Array* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)) {} + + ArrayConstant* AsArrayConstant() override { return this; } + const ArrayConstant* AsArrayConstant() const override { return this; } + + // Make a copy of this ArrayConstant instance. + std::unique_ptr CopyArrayConstant() const { + return MakeUnique(type_->AsArray(), components_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyArrayConstant().release()); + } +}; + +// Null type constant. +class NullConstant : public Constant { + public: + NullConstant(const Type* ty) : Constant(ty) {} + NullConstant* AsNullConstant() override { return this; } + const NullConstant* AsNullConstant() const override { return this; } + + // Make a copy of this NullConstant instance. + std::unique_ptr CopyNullConstant() const { + return MakeUnique(type_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyNullConstant().release()); + } + bool IsZero() const override { return true; } +}; + +// Hash function for Constant instances. Use the structure of the constant as +// the key. +struct ConstantHash { + void add_pointer(std::u32string* h, const void* p) const { + uint64_t ptr_val = reinterpret_cast(p); + h->push_back(static_cast(ptr_val >> 32)); + h->push_back(static_cast(ptr_val)); + } + + size_t operator()(const Constant* const_val) const { + std::u32string h; + add_pointer(&h, const_val->type()); + if (const auto scalar = const_val->AsScalarConstant()) { + for (const auto& w : scalar->words()) { + h.push_back(w); + } + } else if (const auto composite = const_val->AsCompositeConstant()) { + for (const auto& c : composite->GetComponents()) { + add_pointer(&h, c); + } + } else if (const_val->AsNullConstant()) { + h.push_back(0); + } else { + assert( + false && + "Tried to compute the hash value of an invalid Constant instance."); + } + + return std::hash()(h); + } +}; + +// Equality comparison structure for two constants. +struct ConstantEqual { + bool operator()(const Constant* c1, const Constant* c2) const { + if (c1->type() != c2->type()) { + return false; + } + + if (const auto& s1 = c1->AsScalarConstant()) { + const auto& s2 = c2->AsScalarConstant(); + return s2 && s1->words() == s2->words(); + } else if (const auto& composite1 = c1->AsCompositeConstant()) { + const auto& composite2 = c2->AsCompositeConstant(); + return composite2 && + composite1->GetComponents() == composite2->GetComponents(); + } else if (c1->AsNullConstant()) { + return c2->AsNullConstant() != nullptr; + } else { + assert(false && "Tried to compare two invalid Constant instances."); + } + return false; + } +}; + +// This class represents a pool of constants. +class ConstantManager { + public: + ConstantManager(IRContext* ctx); + + IRContext* context() const { return ctx_; } + + // Gets or creates a unique Constant instance of type |type| and a vector of + // constant defining words or ids for elements of Vector type + // |literal_words_or_ids|. If a Constant instance existed already in the + // constant pool, it returns a pointer to it. Otherwise, it creates one using + // CreateConstant. If a new Constant instance cannot be created, it returns + // nullptr. + const Constant* GetConstant( + const Type* type, const std::vector& literal_words_or_ids); + + template + const Constant* GetConstant(const Type* type, const C& literal_words_or_ids) { + return GetConstant(type, std::vector(literal_words_or_ids.begin(), + literal_words_or_ids.end())); + } + + // Takes a type and creates a OpConstantComposite + // This allows a + // OpConstantNull %composite_type + // to become a + // OpConstantComposite %composite_type %null %null ... etc + // Assumes type is a Composite already, otherwise returns null + const Constant* GetNullCompositeConstant(const Type* type); + + // Gets or creates a unique Constant instance of Vector type |type| with + // numeric elements and a vector of constant defining words |literal_words|. + // If a Constant instance existed already in the constant pool, it returns a + // pointer to it. Otherwise, it creates one using CreateConstant. If a new + // Constant instance cannot be created, it returns nullptr. + const Constant* GetNumericVectorConstantWithWords( + const Vector* type, const std::vector& literal_words); + + // Gets or creates a Constant instance to hold the constant value of the given + // instruction. It returns a pointer to a Constant instance or nullptr if it + // could not create the constant. + const Constant* GetConstantFromInst(const Instruction* inst); + + // Gets or creates a constant defining instruction for the given Constant |c|. + // If |c| had already been defined, it returns a pointer to the existing + // declaration. Otherwise, it calls BuildInstructionAndAddToModule. If the + // optional |pos| is given, it will insert any newly created instructions at + // the given instruction iterator position. Otherwise, it inserts the new + // instruction at the end of the current module's types section. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, the constant returned will have that type id. + Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0, + Module::inst_iterator* pos = nullptr); + + // Creates a constant defining instruction for the given Constant instance + // and inserts the instruction at the position specified by the given + // instruction iterator. Returns a pointer to the created instruction if + // succeeded, otherwise returns a null pointer. The instruction iterator + // points to the same instruction before and after the insertion. This is the + // only method that actually manages id creation/assignment and instruction + // creation/insertion for a new Constant instance. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + Instruction* BuildInstructionAndAddToModule(const Constant* c, + Module::inst_iterator* pos, + uint32_t type_id = 0); + + // A helper function to get the result type of the given instruction. Returns + // nullptr if the instruction does not have a type id (type id is 0). + Type* GetType(const Instruction* inst) const; + + // A helper function to get the collected normal constant with the given id. + // Returns the pointer to the Constant instance in case it is found. + // Otherwise, it returns a null pointer. + const Constant* FindDeclaredConstant(uint32_t id) const { + auto iter = id_to_const_val_.find(id); + return (iter != id_to_const_val_.end()) ? iter->second : nullptr; + } + + // A helper function to get the id of a collected constant with the pointer + // to the Constant instance. Returns 0 in case the constant is not found. + uint32_t FindDeclaredConstant(const Constant* c, uint32_t type_id) const; + + // Returns the canonical constant that has the same structure and value as the + // given Constant |cst|. If none is found, it returns nullptr. + // + // TODO: Should be able to give a type id to disambiguate types with the same + // structure. + const Constant* FindConstant(const Constant* c) const { + auto it = const_pool_.find(c); + return (it != const_pool_.end()) ? *it : nullptr; + } + + // Registers a new constant |cst| in the constant pool. If the constant + // existed already, it returns a pointer to the previously existing Constant + // in the pool. Otherwise, it returns |cst|. + const Constant* RegisterConstant(std::unique_ptr cst) { + auto ret = const_pool_.insert(cst.get()); + if (ret.second) { + owned_constants_.emplace_back(std::move(cst)); + } + return *ret.first; + } + + // A helper function to get a vector of Constant instances with the specified + // ids. If it can not find the Constant instance for any one of the ids, + // it returns an empty vector. + std::vector GetConstantsFromIds( + const std::vector& ids) const; + + // Returns a vector of constants representing each in operand. If an operand + // is not constant its entry is nullptr. + std::vector GetOperandConstants( + const Instruction* inst) const; + + // Records a mapping between |inst| and the constant value generated by it. + // It returns true if a new Constant was successfully mapped, false if |inst| + // generates no constant values. + bool MapInst(Instruction* inst) { + if (auto cst = GetConstantFromInst(inst)) { + MapConstantToInst(cst, inst); + return true; + } + return false; + } + + void RemoveId(uint32_t id) { + auto it = id_to_const_val_.find(id); + if (it != id_to_const_val_.end()) { + const_val_to_id_.erase(it->second); + id_to_const_val_.erase(it); + } + } + + // Records a new mapping between |inst| and |const_value|. This updates the + // two mappings |id_to_const_val_| and |const_val_to_id_|. + void MapConstantToInst(const Constant* const_value, Instruction* inst) { + if (id_to_const_val_.insert({inst->result_id(), const_value}).second) { + const_val_to_id_.insert({const_value, inst->result_id()}); + } + } + + // Returns the id of a 32-bit floating point constant with value |val|. + uint32_t GetFloatConstId(float val); + + // Returns a 32-bit float constant with the given value. + const Constant* GetFloatConst(float val); + + // Returns the id of a 64-bit floating point constant with value |val|. + uint32_t GetDoubleConstId(double val); + + // Returns a 64-bit float constant with the given value. + const Constant* GetDoubleConst(double val); + + // Returns the id of a 32-bit signed integer constant with value |val|. + uint32_t GetSIntConstId(int32_t val); + + // Returns an integer constant with `bitWidth` and value |val|. If `isSigned` + // is true, the constant will be a signed integer. Otherwise it will be + // unsigned. Only the `bitWidth` lower order bits of |val| will be used. The + // rest will be ignored. + const Constant* GetIntConst(uint64_t val, int32_t bitWidth, bool isSigned); + + // Returns the id of a 32-bit unsigned integer constant with value |val|. + uint32_t GetUIntConstId(uint32_t val); + + // Returns the id of a OpConstantNull with type of |type|. + uint32_t GetNullConstId(const Type* type); + + // Returns a constant whose value is `value` and type is `type`. This constant + // will be generated by `const_mgr`. The type must be a scalar integer type. + const Constant* GenerateIntegerConstant(const analysis::Integer* integer_type, + uint64_t result); + + private: + // Creates a Constant instance with the given type and a vector of constant + // defining words. Returns a unique pointer to the created Constant instance + // if the Constant instance can be created successfully. To create scalar + // type constants, the vector should contain the constant value in 32 bit + // words and the given type must be of type Bool, Integer or Float. To create + // composite type constants, the vector should contain the component ids, and + // those component ids should have been recorded before as Normal Constants. + // And the given type must be of type Struct, Vector or Array. When creating + // VectorType Constant instance, the components must be scalars of the same + // type, either Bool, Integer or Float. If any of the rules above failed, the + // creation will fail and nullptr will be returned. If the vector is empty, + // a NullConstant instance will be created with the given type. + std::unique_ptr CreateConstant( + const Type* type, + const std::vector& literal_words_or_ids) const; + + // Creates an instruction with the given result id to declare a constant + // represented by the given Constant instance. Returns an unique pointer to + // the created instruction if the instruction can be created successfully. + // Otherwise, returns a null pointer. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + std::unique_ptr CreateInstruction(uint32_t result_id, + const Constant* c, + uint32_t type_id = 0) const; + + // Creates an OpConstantComposite instruction with the given result id and + // the CompositeConst instance which represents a composite constant. Returns + // an unique pointer to the created instruction if succeeded. Otherwise + // returns a null pointer. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + std::unique_ptr CreateCompositeInstruction( + uint32_t result_id, const CompositeConstant* cc, + uint32_t type_id = 0) const; + + // IR context that owns this constant manager. + IRContext* ctx_; + + // A mapping from the result ids of Normal Constants to their + // Constant instances. All Normal Constants in the module, either + // existing ones before optimization or the newly generated ones, should have + // their Constant instance stored and their result id registered in this map. + std::unordered_map id_to_const_val_; + + // A mapping from the Constant instance of Normal Constants to their + // result id in the module. This is a mirror map of |id_to_const_val_|. All + // Normal Constants that defining instructions in the module should have + // their Constant and their result id registered here. + std::multimap const_val_to_id_; + + // The constant pool. All created constants are registered here. + std::unordered_set const_pool_; + + // The constant that are owned by the constant manager. Every constant in + // |const_pool_| should be in |owned_constants_| as well. + std::vector> owned_constants_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONSTANTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.cpp new file mode 100644 index 00000000..3d481396 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/control_dependence.h" + +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" + +// Computes the control dependence graph (CDG) using the algorithm in Cytron +// 1991, "Efficiently Computing Static Single Assignment Form and the Control +// Dependence Graph." It relies on the fact that the control dependence sources +// (blocks on which a block is control dependent) are exactly the post-dominance +// frontier for that block. The explanation and proofs are given in Section 6 of +// that paper. +// Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf +// +// The algorithm in Section 4.2 of the same paper is used to construct the +// dominance frontier. It uses the post-dominance tree, which is available in +// the IR context. + +namespace spvtools { +namespace opt { +constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock; + +uint32_t ControlDependence::GetConditionID(const CFG& cfg) const { + if (source_bb_id() == 0) { + // Entry dependence; return 0. + return 0; + } + const BasicBlock* source_bb = cfg.block(source_bb_id()); + const Instruction* branch = source_bb->terminator(); + assert((branch->opcode() == spv::Op::OpBranchConditional || + branch->opcode() == spv::Op::OpSwitch) && + "invalid control dependence; last instruction must be conditional " + "branch or switch"); + return branch->GetSingleWordInOperand(0); +} + +bool ControlDependence::operator<(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) < + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +bool ControlDependence::operator==(const ControlDependence& other) const { + return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) == + std::tie(other.source_bb_id_, other.target_bb_id_, + other.branch_target_bb_id_); +} + +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) { + os << dep.source_bb_id() << "->" << dep.target_bb_id(); + if (dep.branch_target_bb_id() != dep.target_bb_id()) { + os << " through " << dep.branch_target_bb_id(); + } + return os; +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontiers( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + // Compute post-dominance frontiers (reverse graph). + // The dominance frontier for a block X is equal to (Equation 4) + // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) } + // (ipdom(Z) is the immediate post-dominator of Z.) + // where + // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y } + // represents the contribution of X's predecessors to the DF, and + // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y } + // (note: ipdom(Z) = X.) + // represents the contribution of a block to its immediate post- + // dominator's DF. + // This is computed in one pass through a post-order traversal of the + // post-dominator tree. + + // Assert that there is a block other than the pseudo exit in the pdom tree, + // as we need one to get the function entry point (as the pseudo exit is not + // actually part of the function.) + assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_)); + Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent(); + uint32_t function_entry = function->entry()->id(); + // Explicitly initialize pseudo-entry block, as it doesn't depend on anything, + // so it won't be initialized in the following loop. + reverse_nodes_[kPseudoEntryBlock] = {}; + for (auto it = pdom.GetDomTree().post_cbegin(); + it != pdom.GetDomTree().post_cend(); ++it) { + ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it); + } +} + +void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode( + const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry, + const DominatorTreeNode& pdom_node) { + const uint32_t label = pdom_node.id(); + ControlDependenceList& edges = reverse_nodes_[label]; + for (uint32_t pred : cfg.preds(label)) { + if (!pdom.StrictlyDominates(label, pred)) { + edges.push_back(ControlDependence(pred, label)); + } + } + if (label == function_entry) { + // Add edge from pseudo-entry to entry. + // In CDG construction, an edge is added from entry to exit, so only the + // exit node can post-dominate entry. + edges.push_back(ControlDependence(kPseudoEntryBlock, label)); + } + for (DominatorTreeNode* child : pdom_node) { + // Note: iterate dependences by value, as we need a copy. + for (const ControlDependence& dep : reverse_nodes_[child->id()]) { + // Special-case pseudo-entry, as above. + if (dep.source_bb_id() == kPseudoEntryBlock || + !pdom.StrictlyDominates(label, dep.source_bb_id())) { + edges.push_back(ControlDependence(dep.source_bb_id(), label, + dep.branch_target_bb_id())); + } + } + } +} + +void ControlDependenceAnalysis::ComputeControlDependenceGraph( + const CFG& cfg, const PostDominatorAnalysis& pdom) { + ComputePostDominanceFrontiers(cfg, pdom); + ComputeForwardGraphFromReverse(); +} + +void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() { + for (const auto& entry : reverse_nodes_) { + // Ensure an entry is created for each node. + forward_nodes_[entry.first]; + for (const ControlDependence& dep : entry.second) { + forward_nodes_[dep.source_bb_id()].push_back(dep); + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.h b/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.h new file mode 100644 index 00000000..993f3793 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/control_dependence.h @@ -0,0 +1,197 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONTROL_DEPENDENCE_H_ +#define SOURCE_OPT_CONTROL_DEPENDENCE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/dominator_analysis.h" + +namespace spvtools { +namespace opt { + +class ControlDependence { + public: + // The label of the source of this dependence, i.e. the block on which the + // target is dependent on. + // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the + // execution of |target_bb_id| is only dependent on entry to the function. + uint32_t source_bb_id() const { return source_bb_id_; } + // The label of the target of this dependence, i.e. the block which is + // dependent on the source. + uint32_t target_bb_id() const { return target_bb_id_; } + // The label of the target of the *branch* for this dependence. + // Equal to the ID of the entry block for entry dependences. + // + // For example, for the partial CFG pictured below: + // 1 ---> 2 ---> 4 ---> 6 + // \ \ ^ + // \-> 3 \-> 5 -----/ + // Block 6 is control dependent on block 1, but this dependence comes from the + // branch 1 -> 2, so in this case the branch target ID would be 2. + uint32_t branch_target_bb_id() const { return branch_target_bb_id_; } + + // Create a direct control dependence from BB ID |source| to |target|. + ControlDependence(uint32_t source, uint32_t target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(target) {} + // Create a control dependence from BB ID |source| to |target| through the + // branch from |source| to |branch_target|. + ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target) + : source_bb_id_(source), + target_bb_id_(target), + branch_target_bb_id_(branch_target) {} + + // Gets the ID of the conditional value for the branch corresponding to this + // control dependence. This is the first input operand for both + // OpConditionalBranch and OpSwitch. + // Returns 0 for entry dependences. + uint32_t GetConditionID(const CFG& cfg) const; + + bool operator==(const ControlDependence& other) const; + bool operator!=(const ControlDependence& other) const { + return !(*this == other); + } + + // Comparison operators, ordered lexicographically. Total ordering. + bool operator<(const ControlDependence& other) const; + bool operator>(const ControlDependence& other) const { return other < *this; } + bool operator<=(const ControlDependence& other) const { + return !(*this > other); + } + bool operator>=(const ControlDependence& other) const { + return !(*this < other); + } + + private: + uint32_t source_bb_id_; + uint32_t target_bb_id_; + uint32_t branch_target_bb_id_; +}; + +// Prints |dep| to |os| in a human-readable way. For example, +// 1->2 (target_bb_id = branch_target_bb_id = 2) +// 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5) +std::ostream& operator<<(std::ostream& os, const ControlDependence& dep); + +// Represents the control dependence graph. A basic block is control dependent +// on another if the result of that block (e.g. the condition of a conditional +// branch) influences whether it is executed or not. More formally, a block A is +// control dependent on B iff: +// 1. there exists a path from A to the exit node that does *not* go through B +// (i.e., A does not postdominate B), and +// 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates +// all nodes b_i. +class ControlDependenceAnalysis { + public: + // Map basic block labels to control dependencies/dependents. + // Not guaranteed to be in any particular order. + using ControlDependenceList = std::vector; + using ControlDependenceListMap = + std::unordered_map; + + // 0, the label number for the pseudo entry block. + // All control dependences on the pseudo entry block are of type kEntry, and + // vice versa. + static constexpr uint32_t kPseudoEntryBlock = 0; + + // Build the control dependence graph for the given control flow graph |cfg| + // and corresponding post-dominator analysis |pdom|. + void ComputeControlDependenceGraph(const CFG& cfg, + const PostDominatorAnalysis& pdom); + + // Get the list of the nodes that depend on a block. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceTargets(uint32_t block) const { + return forward_nodes_.at(block); + } + + // Get the list of the nodes on which a block depends on. + // Return value is not guaranteed to be in any particular order. + const ControlDependenceList& GetDependenceSources(uint32_t block) const { + return reverse_nodes_.at(block); + } + + // Runs the function |f| on each block label in the CDG. If any iteration + // returns false, immediately stops iteration and returns false. Otherwise + // returns true. Nodes are iterated in some undefined order, including the + // pseudo-entry block. + bool WhileEachBlockLabel(std::function f) const { + for (const auto& entry : forward_nodes_) { + if (!f(entry.first)) { + return false; + } + } + return true; + } + + // Runs the function |f| on each block label in the CDG. Nodes are iterated in + // some undefined order, including the pseudo-entry block. + void ForEachBlockLabel(std::function f) const { + WhileEachBlockLabel([&f](uint32_t label) { + f(label); + return true; + }); + } + + // Returns true if the block |id| exists in the control dependence graph. + // This can be false even if the block exists in the function when it is part + // of an infinite loop, since it is not part of the post-dominator tree. + bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; } + + // Returns true if block |a| is dependent on block |b|. + bool IsDependent(uint32_t a, uint32_t b) const { + if (!HasBlock(a)) return false; + // BBs tend to have more dependents (targets) than they are dependent on + // (sources), so search sources. + const ControlDependenceList& a_sources = GetDependenceSources(a); + return std::find_if(a_sources.begin(), a_sources.end(), + [b](const ControlDependence& dep) { + return dep.source_bb_id() == b; + }) != a_sources.end(); + } + + private: + // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node + // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is + // not modified. + void ComputePostDominanceFrontiers(const CFG& cfg, + const PostDominatorAnalysis& pdom); + // Computes the post-dominance frontier for a specific node |pdom_node| in the + // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()]. + void ComputePostDominanceFrontierForNode(const CFG& cfg, + const PostDominatorAnalysis& pdom, + uint32_t function_entry, + const DominatorTreeNode& pdom_node); + + // Computes the forward graph (forward_nodes_) from the reverse graph + // (reverse_nodes_). + void ComputeForwardGraphFromReverse(); + + ControlDependenceListMap forward_nodes_; + ControlDependenceListMap reverse_nodes_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.cpp new file mode 100644 index 00000000..e243bedf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.cpp @@ -0,0 +1,517 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "convert_to_half_pass.h" + +#include "source/opt/ir_builder.h" + +namespace spvtools { +namespace opt { +namespace { +// Indices of operands in SPIR-V instructions +constexpr int kImageSampleDrefIdInIdx = 2; +} // namespace + +bool ConvertToHalfPass::IsArithmetic(Instruction* inst) { + return target_ops_core_.count(inst->opcode()) != 0 || + (inst->opcode() == spv::Op::OpExtInst && + inst->GetSingleWordInOperand(0) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0); +} + +bool ConvertToHalfPass::IsFloat(Instruction* inst, uint32_t width) { + uint32_t ty_id = inst->type_id(); + if (ty_id == 0) return false; + return Pass::IsFloat(ty_id, width); +} + +bool ConvertToHalfPass::IsStruct(Instruction* inst) { + uint32_t ty_id = inst->type_id(); + if (ty_id == 0) return false; + Instruction* ty_inst = Pass::GetBaseType(ty_id); + return (ty_inst->opcode() == spv::Op::OpTypeStruct); +} + +bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) { + uint32_t r_id = inst->result_id(); + for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false)) + if (r_inst->opcode() == spv::Op::OpDecorate && + spv::Decoration(r_inst->GetSingleWordInOperand(1)) == + spv::Decoration::RelaxedPrecision) { + return true; + } + return false; +} + +bool ConvertToHalfPass::IsRelaxed(uint32_t id) { + return relaxed_ids_set_.count(id) > 0; +} + +void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); } + +bool ConvertToHalfPass::CanRelaxOpOperands(Instruction* inst) { + return image_ops_.count(inst->opcode()) == 0; +} + +analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) { + analysis::Float float_ty(width); + return context()->get_type_mgr()->GetRegisteredType(&float_ty); +} + +analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len, + uint32_t width) { + analysis::Type* reg_float_ty = FloatScalarType(width); + analysis::Vector vec_ty(reg_float_ty, v_len); + return context()->get_type_mgr()->GetRegisteredType(&vec_ty); +} + +analysis::Type* ConvertToHalfPass::FloatMatrixType(uint32_t v_cnt, + uint32_t vty_id, + uint32_t width) { + Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id); + uint32_t v_len = vty_inst->GetSingleWordInOperand(1); + analysis::Type* reg_vec_ty = FloatVectorType(v_len, width); + analysis::Matrix mat_ty(reg_vec_ty, v_cnt); + return context()->get_type_mgr()->GetRegisteredType(&mat_ty); +} + +uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) { + analysis::Type* reg_equiv_ty; + Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id); + if (ty_inst->opcode() == spv::Op::OpTypeMatrix) + reg_equiv_ty = FloatMatrixType(ty_inst->GetSingleWordInOperand(1), + ty_inst->GetSingleWordInOperand(0), width); + else if (ty_inst->opcode() == spv::Op::OpTypeVector) + reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width); + else // spv::Op::OpTypeFloat + reg_equiv_ty = FloatScalarType(width); + return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty); +} + +void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width, + Instruction* inst) { + Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp); + uint32_t ty_id = val_inst->type_id(); + uint32_t nty_id = EquivFloatTypeId(ty_id, width); + if (nty_id == ty_id) return; + Instruction* cvt_inst; + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + if (val_inst->opcode() == spv::Op::OpUndef) + cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef); + else + cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp); + *val_idp = cvt_inst->result_id(); +} + +bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) { + if (inst->opcode() != spv::Op::OpFConvert) return false; + uint32_t mty_id = inst->type_id(); + Instruction* mty_inst = get_def_use_mgr()->GetDef(mty_id); + if (mty_inst->opcode() != spv::Op::OpTypeMatrix) return false; + uint32_t vty_id = mty_inst->GetSingleWordInOperand(0); + uint32_t v_cnt = mty_inst->GetSingleWordInOperand(1); + Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id); + uint32_t cty_id = vty_inst->GetSingleWordInOperand(0); + Instruction* cty_inst = get_def_use_mgr()->GetDef(cty_id); + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Convert each component vector, combine them with OpCompositeConstruct + // and replace original instruction. + uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16; + uint32_t orig_mat_id = inst->GetSingleWordInOperand(0); + uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width); + std::vector opnds = {}; + for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) { + Instruction* ext_inst = builder.AddIdLiteralOp( + orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx); + Instruction* cvt_inst = + builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id()); + opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}}); + } + uint32_t mat_id = TakeNextId(); + std::unique_ptr mat_inst(new Instruction( + context(), spv::Op::OpCompositeConstruct, mty_id, mat_id, opnds)); + (void)builder.AddInstruction(std::move(mat_inst)); + context()->ReplaceAllUsesWith(inst->result_id(), mat_id); + // Turn original instruction into copy so it is valid. + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetResultType(EquivFloatTypeId(mty_id, orig_width)); + get_def_use_mgr()->AnalyzeInstUse(inst); + return true; +} + +bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { + return context()->get_decoration_mgr()->RemoveDecorationsFrom( + id, [](const Instruction& dec) { + if (dec.opcode() == spv::Op::OpDecorate && + spv::Decoration(dec.GetSingleWordInOperand(1u)) == + spv::Decoration::RelaxedPrecision) { + return true; + } else + return false; + }); +} + +bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { + bool modified = false; + // If this is a OpCompositeExtract instruction and has a struct operand, we + // should not relax this instruction. Doing so could cause a mismatch between + // the result type and the struct member type. + bool hasStructOperand = false; + if (inst->opcode() == spv::Op::OpCompositeExtract) { + inst->ForEachInId([&hasStructOperand, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (IsStruct(op_inst)) hasStructOperand = true; + }); + if (hasStructOperand) { + return false; + } + } + // Convert all float32 based operands to float16 equivalent and change + // instruction type to float16 equivalent. + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (!IsFloat(op_inst, 32)) return; + GenConvert(idp, 16, inst); + modified = true; + }); + if (IsFloat(inst, 32)) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; +} + +bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, + uint32_t to_width) { + // Add converts of any float operands to to_width if they are of from_width. + // If converting to 16, change type of phi to float16 equivalent and remember + // result id. Converts need to be added to preceding blocks. + uint32_t ocnt = 0; + uint32_t* prev_idp; + bool modified = false; + inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified, + this](uint32_t* idp) { + if (ocnt % 2 == 0) { + prev_idp = idp; + } else { + Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp); + if (IsFloat(val_inst, from_width)) { + BasicBlock* bp = context()->get_instr_block(*idp); + auto insert_before = bp->tail(); + if (insert_before != bp->begin()) { + --insert_before; + if (insert_before->opcode() != spv::Op::OpSelectionMerge && + insert_before->opcode() != spv::Op::OpLoopMerge) + ++insert_before; + } + GenConvert(prev_idp, to_width, &*insert_before); + modified = true; + } + } + ++ocnt; + }); + if (to_width == 16u) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; +} + +bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { + // If float32 and relaxed, change to float16 convert + if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + get_def_use_mgr()->AnalyzeInstUse(inst); + converted_ids_.insert(inst->result_id()); + } + // If operand and result types are the same, change FConvert to CopyObject to + // keep validator happy; simplification and DCE will clean it up + // One way this can happen is if an FConvert generated during this pass + // (likely by ProcessPhi) is later encountered here and its operand has been + // changed to half. + uint32_t val_id = inst->GetSingleWordInOperand(0); + Instruction* val_inst = get_def_use_mgr()->GetDef(val_id); + if (inst->type_id() == val_inst->type_id()) + inst->SetOpcode(spv::Op::OpCopyObject); + return true; // modified +} + +bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { + bool modified = false; + // If image reference, only need to convert dref args back to float32 + if (dref_image_ops_.count(inst->opcode()) != 0) { + uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx); + if (converted_ids_.count(dref_id) > 0) { + GenConvert(&dref_id, 32, inst); + inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id}); + get_def_use_mgr()->AnalyzeInstUse(inst); + modified = true; + } + } + return modified; +} + +bool ConvertToHalfPass::ProcessDefault(Instruction* inst) { + // If non-relaxed instruction has changed operands, need to convert + // them back to float32 + if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u); + bool modified = false; + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + if (converted_ids_.count(*idp) == 0) return; + uint32_t old_id = *idp; + GenConvert(idp, 32, inst); + if (*idp != old_id) modified = true; + }); + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; +} + +bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { + bool modified = false; + // Remember id for later deletion of RelaxedPrecision decoration + bool inst_relaxed = IsRelaxed(inst->result_id()); + if (IsArithmetic(inst) && inst_relaxed) + modified = GenHalfArith(inst); + else if (inst->opcode() == spv::Op::OpPhi && inst_relaxed) + modified = ProcessPhi(inst, 32u, 16u); + else if (inst->opcode() == spv::Op::OpFConvert) + modified = ProcessConvert(inst); + else if (image_ops_.count(inst->opcode()) != 0) + modified = ProcessImageRef(inst); + else + modified = ProcessDefault(inst); + return modified; +} + +bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) { + if (inst->result_id() == 0) return false; + if (IsRelaxed(inst->result_id())) return false; + if (!IsFloat(inst, 32)) return false; + if (IsDecoratedRelaxed(inst)) { + AddRelaxed(inst->result_id()); + return true; + } + if (closure_ops_.count(inst->opcode()) == 0) return false; + // Can relax if all float operands are relaxed + bool relax = true; + bool hasStructOperand = false; + inst->ForEachInId([&relax, &hasStructOperand, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (IsStruct(op_inst)) hasStructOperand = true; + if (!IsFloat(op_inst, 32)) return; + if (!IsRelaxed(*idp)) relax = false; + }); + // If the instruction has a struct operand, we should not relax it, even if + // all its uses are relaxed. Doing so could cause a mismatch between the + // result type and the struct member type. + if (hasStructOperand) { + return false; + } + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + // Can relax if all uses are relaxed + relax = true; + get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) { + if (uinst->result_id() == 0 || !IsFloat(uinst, 32) || + (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id())) || + !CanRelaxOpOperands(uinst)) { + relax = false; + return; + } + }); + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + return false; +} + +bool ConvertToHalfPass::ProcessFunction(Function* func) { + // Do a closure of Relaxed on composite and phi instructions + bool changed = true; + while (changed) { + changed = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&changed, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + changed |= CloseRelaxInst(&*ii); + }); + } + // Do convert of relaxed instructions to half precision + bool modified = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= GenHalfInst(&*ii); + }); + // Replace invalid converts of matrix into equivalent vector extracts, + // converts and finally a composite construct + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= MatConvertCleanup(&*ii); + }); + return modified; +} + +Pass::Status ConvertToHalfPass::ProcessImpl() { + Pass::ProcessFunction pfn = [this](Function* fp) { + return ProcessFunction(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + // If modified, make sure module has Float16 capability + if (modified) context()->AddCapability(spv::Capability::Float16); + // Remove all RelaxedPrecision decorations from instructions and globals + for (auto c_id : relaxed_ids_set_) { + modified |= RemoveRelaxedDecoration(c_id); + } + for (auto& val : get_module()->types_values()) { + uint32_t v_id = val.result_id(); + if (v_id != 0) { + modified |= RemoveRelaxedDecoration(v_id); + } + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status ConvertToHalfPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void ConvertToHalfPass::Initialize() { + target_ops_core_ = { + spv::Op::OpVectorExtractDynamic, + spv::Op::OpVectorInsertDynamic, + spv::Op::OpVectorShuffle, + spv::Op::OpCompositeConstruct, + spv::Op::OpCompositeInsert, + spv::Op::OpCompositeExtract, + spv::Op::OpCopyObject, + spv::Op::OpTranspose, + spv::Op::OpConvertSToF, + spv::Op::OpConvertUToF, + // spv::Op::OpFConvert, + // spv::Op::OpQuantizeToF16, + spv::Op::OpFNegate, + spv::Op::OpFAdd, + spv::Op::OpFSub, + spv::Op::OpFMul, + spv::Op::OpFDiv, + spv::Op::OpFMod, + spv::Op::OpVectorTimesScalar, + spv::Op::OpMatrixTimesScalar, + spv::Op::OpVectorTimesMatrix, + spv::Op::OpMatrixTimesVector, + spv::Op::OpMatrixTimesMatrix, + spv::Op::OpOuterProduct, + spv::Op::OpDot, + spv::Op::OpSelect, + spv::Op::OpFOrdEqual, + spv::Op::OpFUnordEqual, + spv::Op::OpFOrdNotEqual, + spv::Op::OpFUnordNotEqual, + spv::Op::OpFOrdLessThan, + spv::Op::OpFUnordLessThan, + spv::Op::OpFOrdGreaterThan, + spv::Op::OpFUnordGreaterThan, + spv::Op::OpFOrdLessThanEqual, + spv::Op::OpFUnordLessThanEqual, + spv::Op::OpFOrdGreaterThanEqual, + spv::Op::OpFUnordGreaterThanEqual, + }; + target_ops_450_ = { + GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs, + GLSLstd450FSign, GLSLstd450Floor, GLSLstd450Ceil, GLSLstd450Fract, + GLSLstd450Radians, GLSLstd450Degrees, GLSLstd450Sin, GLSLstd450Cos, + GLSLstd450Tan, GLSLstd450Asin, GLSLstd450Acos, GLSLstd450Atan, + GLSLstd450Sinh, GLSLstd450Cosh, GLSLstd450Tanh, GLSLstd450Asinh, + GLSLstd450Acosh, GLSLstd450Atanh, GLSLstd450Atan2, GLSLstd450Pow, + GLSLstd450Exp, GLSLstd450Log, GLSLstd450Exp2, GLSLstd450Log2, + GLSLstd450Sqrt, GLSLstd450InverseSqrt, GLSLstd450Determinant, + GLSLstd450MatrixInverse, + // TODO(greg-lunarg): GLSLstd450ModfStruct, + GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix, + GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma, + // TODO(greg-lunarg): GLSLstd450FrexpStruct, + GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross, + GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect, + GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp}; + image_ops_ = {spv::Op::OpImageSampleImplicitLod, + spv::Op::OpImageSampleExplicitLod, + spv::Op::OpImageSampleDrefImplicitLod, + spv::Op::OpImageSampleDrefExplicitLod, + spv::Op::OpImageSampleProjImplicitLod, + spv::Op::OpImageSampleProjExplicitLod, + spv::Op::OpImageSampleProjDrefImplicitLod, + spv::Op::OpImageSampleProjDrefExplicitLod, + spv::Op::OpImageFetch, + spv::Op::OpImageGather, + spv::Op::OpImageDrefGather, + spv::Op::OpImageRead, + spv::Op::OpImageSparseSampleImplicitLod, + spv::Op::OpImageSparseSampleExplicitLod, + spv::Op::OpImageSparseSampleDrefImplicitLod, + spv::Op::OpImageSparseSampleDrefExplicitLod, + spv::Op::OpImageSparseSampleProjImplicitLod, + spv::Op::OpImageSparseSampleProjExplicitLod, + spv::Op::OpImageSparseSampleProjDrefImplicitLod, + spv::Op::OpImageSparseSampleProjDrefExplicitLod, + spv::Op::OpImageSparseFetch, + spv::Op::OpImageSparseGather, + spv::Op::OpImageSparseDrefGather, + spv::Op::OpImageSparseTexelsResident, + spv::Op::OpImageSparseRead}; + dref_image_ops_ = { + spv::Op::OpImageSampleDrefImplicitLod, + spv::Op::OpImageSampleDrefExplicitLod, + spv::Op::OpImageSampleProjDrefImplicitLod, + spv::Op::OpImageSampleProjDrefExplicitLod, + spv::Op::OpImageDrefGather, + spv::Op::OpImageSparseSampleDrefImplicitLod, + spv::Op::OpImageSparseSampleDrefExplicitLod, + spv::Op::OpImageSparseSampleProjDrefImplicitLod, + spv::Op::OpImageSparseSampleProjDrefExplicitLod, + spv::Op::OpImageSparseDrefGather, + }; + closure_ops_ = { + spv::Op::OpVectorExtractDynamic, + spv::Op::OpVectorInsertDynamic, + spv::Op::OpVectorShuffle, + spv::Op::OpCompositeConstruct, + spv::Op::OpCompositeInsert, + spv::Op::OpCompositeExtract, + spv::Op::OpCopyObject, + spv::Op::OpTranspose, + spv::Op::OpPhi, + }; + relaxed_ids_set_.clear(); + converted_ids_.clear(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.h new file mode 100644 index 00000000..8e10c4fb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_half_pass.h @@ -0,0 +1,158 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ +#define LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class ConvertToHalfPass : public Pass { + public: + ConvertToHalfPass() : Pass() {} + + ~ConvertToHalfPass() override = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + } + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "convert-to-half-pass"; } + + private: + // Return true if |inst| is an arithmetic, composite or phi op that can be + // of type float16 + bool IsArithmetic(Instruction* inst); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and |width| + bool IsFloat(Instruction* inst, uint32_t width); + bool IsStruct(Instruction* inst); + + // Return true if |inst| is decorated with RelaxedPrecision + bool IsDecoratedRelaxed(Instruction* inst); + + // Return true if |id| has been added to the relaxed id set + bool IsRelaxed(uint32_t id); + + // Add |id| to the relaxed id set + void AddRelaxed(uint32_t id); + + // Return true if the instruction's operands can be relaxed + bool CanRelaxOpOperands(Instruction* inst); + + // Return type id for float with |width| + analysis::Type* FloatScalarType(uint32_t width); + + // Return type id for vector of length |vlen| of float of |width| + analysis::Type* FloatVectorType(uint32_t v_len, uint32_t width); + + // Return type id for matrix of |v_cnt| vectors of length identical to + // |vty_id| of float of |width| + analysis::Type* FloatMatrixType(uint32_t v_cnt, uint32_t vty_id, + uint32_t width); + + // Return equivalent to float type |ty_id| with |width| + uint32_t EquivFloatTypeId(uint32_t ty_id, uint32_t width); + + // Append instructions to builder to convert value |*val_idp| to type + // |ty_id| but with |width|. Set |*val_idp| to the new id. + void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst); + + // Remove RelaxedPrecision decoration of |id|. + bool RemoveRelaxedDecoration(uint32_t id); + + // Add |inst| to relaxed instruction set if warranted. Specifically, if + // it is float32 and either decorated relaxed or a composite or phi + // instruction where all operands are relaxed or all uses are relaxed. + bool CloseRelaxInst(Instruction* inst); + + // If |inst| is an arithmetic, phi, extract or convert instruction of float32 + // base type and decorated with RelaxedPrecision, change it to the equivalent + // float16 based type instruction. Specifically, insert instructions to + // convert all operands to float16 (if needed) and change its type to the + // equivalent float16 type. Otherwise, insert instructions to convert its + // operands back to their original types, if needed. + bool GenHalfInst(Instruction* inst); + + // Gen code for relaxed arithmetic |inst| + bool GenHalfArith(Instruction* inst); + + // Gen code for relaxed phi |inst| + bool ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width); + + // Gen code for relaxed convert |inst| + bool ProcessConvert(Instruction* inst); + + // Gen code for image reference |inst| + bool ProcessImageRef(Instruction* inst); + + // Process default non-relaxed |inst| + bool ProcessDefault(Instruction* inst); + + // If |inst| is an FConvert of a matrix type, decompose it to a series + // of vector extracts, converts and inserts into an Undef. These are + // generated by GenHalfInst because they are easier to manipulate, but are + // invalid so we need to clean them up. + bool MatConvertCleanup(Instruction* inst); + + // Call GenHalfInst on every instruction in |func|. + // If code is generated for an instruction, replace the instruction + // with the new instructions that are generated. + bool ProcessFunction(Function* func); + + Pass::Status ProcessImpl(); + + // Initialize state for converting to half + void Initialize(); + + struct hasher { + size_t operator()(const spv::Op& op) const noexcept { + return std::hash()(uint32_t(op)); + } + }; + + // Set of core operations to be processed + std::unordered_set target_ops_core_; + + // Set of 450 extension operations to be processed + std::unordered_set target_ops_450_; + + // Set of all sample operations, including dref and non-dref operations + std::unordered_set image_ops_; + + // Set of only dref sample operations + std::unordered_set dref_image_ops_; + + // Set of operations that can be marked as relaxed + std::unordered_set closure_ops_; + + // Set of ids of all relaxed instructions + std::unordered_set relaxed_ids_set_; + + // Ids of all converted instructions + std::unordered_set converted_ids_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp new file mode 100644 index 00000000..d2da4d1e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp @@ -0,0 +1,435 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/convert_to_sampled_image_pass.h" + +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" + +namespace spvtools { +namespace opt { + +using VectorOfDescriptorSetAndBindingPairs = + std::vector; +using DescriptorSetBindingToInstruction = + ConvertToSampledImagePass::DescriptorSetBindingToInstruction; + +namespace { + +using utils::ParseNumber; + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +// Reads characters starting from |str| until it meets a separator. Parses a +// number from the characters and stores it into |number|. Returns the pointer +// to the separator if it succeeds. Otherwise, returns nullptr. +const char* ParseNumberUntilSeparator(const char* str, uint32_t* number) { + const char* number_begin = str; + while (!IsSeparator(*str)) str++; + const char* number_end = str; + std::string number_in_str(number_begin, number_end - number_begin); + if (!utils::ParseNumber(number_in_str.c_str(), number)) { + // The descriptor set is not a valid uint32 number. + return nullptr; + } + return str; +} + +// Returns id of the image type used for the sampled image type of +// |sampled_image|. +uint32_t GetImageTypeOfSampledImage(analysis::TypeManager* type_mgr, + Instruction* sampled_image) { + auto* sampled_image_type = + type_mgr->GetType(sampled_image->type_id())->AsSampledImage(); + return type_mgr->GetTypeInstruction(sampled_image_type->image_type()); +} + +// Finds the instruction whose id is |inst_id|. Follows the operand of +// OpCopyObject recursively if the opcode of the instruction is OpCopyObject +// and returns the first instruction that does not have OpCopyObject as opcode. +Instruction* GetNonCopyObjectDef(analysis::DefUseManager* def_use_mgr, + uint32_t inst_id) { + Instruction* inst = def_use_mgr->GetDef(inst_id); + while (inst->opcode() == spv::Op::OpCopyObject) { + inst_id = inst->GetSingleWordInOperand(0u); + inst = def_use_mgr->GetDef(inst_id); + } + return inst; +} + +} // namespace + +bool ConvertToSampledImagePass::GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const { + auto* decoration_manager = context()->get_decoration_mgr(); + bool found_descriptor_set_to_convert = false; + bool found_binding_to_convert = false; + for (auto decorate : + decoration_manager->GetDecorationsFor(inst.result_id(), false)) { + spv::Decoration decoration = + spv::Decoration(decorate->GetSingleWordInOperand(1u)); + if (decoration == spv::Decoration::DescriptorSet) { + if (found_descriptor_set_to_convert) { + assert(false && "A resource has two OpDecorate for the descriptor set"); + return false; + } + descriptor_set_binding->descriptor_set = + decorate->GetSingleWordInOperand(2u); + found_descriptor_set_to_convert = true; + } else if (decoration == spv::Decoration::Binding) { + if (found_binding_to_convert) { + assert(false && "A resource has two OpDecorate for the binding"); + return false; + } + descriptor_set_binding->binding = decorate->GetSingleWordInOperand(2u); + found_binding_to_convert = true; + } + } + return found_descriptor_set_to_convert && found_binding_to_convert; +} + +bool ConvertToSampledImagePass::ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding_pairs_.find(descriptor_set_binding) != + descriptor_set_binding_pairs_.end(); +} + +const analysis::Type* ConvertToSampledImagePass::GetVariableType( + const Instruction& variable) const { + if (variable.opcode() != spv::Op::OpVariable) return nullptr; + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return nullptr; + + return pointer_type->pointee_type(); +} + +spv::StorageClass ConvertToSampledImagePass::GetStorageClass( + const Instruction& variable) const { + assert(variable.opcode() == spv::Op::OpVariable); + auto* type = context()->get_type_mgr()->GetType(variable.type_id()); + auto* pointer_type = type->AsPointer(); + if (!pointer_type) return spv::StorageClass::Max; + + return pointer_type->storage_class(); +} + +bool ConvertToSampledImagePass::CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const { + for (auto& inst : context()->types_values()) { + const auto* variable_type = GetVariableType(inst); + if (variable_type == nullptr) continue; + + DescriptorSetAndBinding descriptor_set_binding; + if (!GetDescriptorSetBinding(inst, &descriptor_set_binding)) continue; + + if (!ShouldResourceBeConverted(descriptor_set_binding)) { + continue; + } + + if (variable_type->AsImage()) { + if (!descriptor_set_binding_pair_to_image + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } else if (variable_type->AsSampler()) { + if (!descriptor_set_binding_pair_to_sampler + ->insert({descriptor_set_binding, &inst}) + .second) { + return false; + } + } + } + return true; +} + +Pass::Status ConvertToSampledImagePass::Process() { + Status status = Status::SuccessWithoutChange; + + DescriptorSetBindingToInstruction descriptor_set_binding_pair_to_sampler, + descriptor_set_binding_pair_to_image; + if (!CollectResourcesToConvert(&descriptor_set_binding_pair_to_sampler, + &descriptor_set_binding_pair_to_image)) { + return Status::Failure; + } + + for (auto& image : descriptor_set_binding_pair_to_image) { + status = CombineStatus( + status, UpdateImageVariableToSampledImage(image.second, image.first)); + if (status == Status::Failure) { + return status; + } + } + + for (const auto& sampler : descriptor_set_binding_pair_to_sampler) { + // Converting only a Sampler to Sampled Image is not allowed. It must have a + // corresponding image to combine the sampler with. + auto image_itr = descriptor_set_binding_pair_to_image.find(sampler.first); + if (image_itr == descriptor_set_binding_pair_to_image.end() || + image_itr->second == nullptr) { + return Status::Failure; + } + + status = CombineStatus( + status, CheckUsesOfSamplerVariable(sampler.second, image_itr->second)); + if (status == Status::Failure) { + return status; + } + } + + return status; +} + +void ConvertToSampledImagePass::FindUses(const Instruction* inst, + std::vector* uses, + spv::Op user_opcode) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(inst, [uses, user_opcode, this](Instruction* user) { + if (user->opcode() == user_opcode) { + uses->push_back(user); + } else if (user->opcode() == spv::Op::OpCopyObject) { + FindUses(user, uses, user_opcode); + } + }); +} + +void ConvertToSampledImagePass::FindUsesOfImage( + const Instruction* image, std::vector* uses) const { + auto* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(image, [uses, this](Instruction* user) { + switch (user->opcode()) { + case spv::Op::OpImageFetch: + case spv::Op::OpImageRead: + case spv::Op::OpImageWrite: + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryOrder: + case spv::Op::OpImageQuerySizeLod: + case spv::Op::OpImageQuerySize: + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQuerySamples: + case spv::Op::OpImageSparseFetch: + uses->push_back(user); + default: + break; + } + if (user->opcode() == spv::Op::OpCopyObject) { + FindUsesOfImage(user, uses); + } + }); +} + +Instruction* ConvertToSampledImagePass::CreateImageExtraction( + Instruction* sampled_image) { + InstructionBuilder builder( + context(), sampled_image->NextNode(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.AddUnaryOp( + GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image), + spv::Op::OpImage, sampled_image->result_id()); +} + +uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage( + Instruction* image_variable) { + const auto* variable_type = GetVariableType(*image_variable); + if (variable_type == nullptr) return 0; + const auto* image_type = variable_type->AsImage(); + if (image_type == nullptr) return 0; + + analysis::Image image_type_for_sampled_image(*image_type); + analysis::SampledImage sampled_image_type(&image_type_for_sampled_image); + return context()->get_type_mgr()->GetTypeInstruction(&sampled_image_type); +} + +Instruction* ConvertToSampledImagePass::UpdateImageUses( + Instruction* sampled_image_load) { + std::vector uses_of_load; + FindUsesOfImage(sampled_image_load, &uses_of_load); + if (uses_of_load.empty()) return nullptr; + + auto* extracted_image = CreateImageExtraction(sampled_image_load); + for (auto* user : uses_of_load) { + user->SetInOperand(0, {extracted_image->result_id()}); + context()->get_def_use_mgr()->AnalyzeInstUse(user); + } + return extracted_image; +} + +bool ConvertToSampledImagePass:: + IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding) { + auto* def_use_mgr = context()->get_def_use_mgr(); + uint32_t sampler_id = sampled_image_inst->GetSingleWordInOperand(1u); + auto* sampler_load = def_use_mgr->GetDef(sampler_id); + if (sampler_load->opcode() != spv::Op::OpLoad) return false; + auto* sampler = def_use_mgr->GetDef(sampler_load->GetSingleWordInOperand(0u)); + DescriptorSetAndBinding sampler_descriptor_set_binding; + return GetDescriptorSetBinding(*sampler, &sampler_descriptor_set_binding) && + sampler_descriptor_set_binding == descriptor_set_binding; +} + +void ConvertToSampledImagePass::UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding) { + std::vector sampled_image_users; + FindUses(image_load, &sampled_image_users, spv::Op::OpSampledImage); + + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto* sampled_image_inst : sampled_image_users) { + if (IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + sampled_image_inst, image_descriptor_set_binding)) { + context()->ReplaceAllUsesWith(sampled_image_inst->result_id(), + image_load->result_id()); + def_use_mgr->AnalyzeInstUse(image_load); + context()->KillInst(sampled_image_inst); + } else { + if (!image_extraction) + image_extraction = CreateImageExtraction(image_load); + sampled_image_inst->SetInOperand(0, {image_extraction->result_id()}); + def_use_mgr->AnalyzeInstUse(sampled_image_inst); + } + } +} + +void ConvertToSampledImagePass::MoveInstructionNextToType(Instruction* inst, + uint32_t type_id) { + auto* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + inst->SetResultType(type_id); + inst->RemoveFromList(); + inst->InsertAfter(type_inst); +} + +bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage( + Instruction* image_variable, uint32_t sampled_image_type_id) { + auto* sampled_image_type = + context()->get_type_mgr()->GetType(sampled_image_type_id); + if (sampled_image_type == nullptr) return false; + auto storage_class = GetStorageClass(*image_variable); + if (storage_class == spv::StorageClass::Max) return false; + // Make sure |image_variable| is behind its type i.e., avoid the forward + // reference. + uint32_t type_id = context()->get_type_mgr()->FindPointerToType( + sampled_image_type_id, storage_class); + MoveInstructionNextToType(image_variable, type_id); + return true; +} + +Pass::Status ConvertToSampledImagePass::UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding) { + std::vector image_variable_loads; + FindUses(image_variable, &image_variable_loads, spv::Op::OpLoad); + if (image_variable_loads.empty()) return Status::SuccessWithoutChange; + + const uint32_t sampled_image_type_id = + GetSampledImageTypeForImage(image_variable); + if (!sampled_image_type_id) return Status::Failure; + + for (auto* load : image_variable_loads) { + load->SetResultType(sampled_image_type_id); + auto* image_extraction = UpdateImageUses(load); + UpdateSampledImageUses(load, image_extraction, descriptor_set_binding); + } + + return ConvertImageVariableToSampledImage(image_variable, + sampled_image_type_id) + ? Status::SuccessWithChange + : Status::Failure; +} + +bool ConvertToSampledImagePass::DoesSampledImageReferenceImage( + Instruction* sampled_image_inst, Instruction* image_variable) { + if (sampled_image_inst->opcode() != spv::Op::OpSampledImage) return false; + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* image_load = GetNonCopyObjectDef( + def_use_mgr, sampled_image_inst->GetSingleWordInOperand(0u)); + if (image_load->opcode() != spv::Op::OpLoad) return false; + auto* image = + GetNonCopyObjectDef(def_use_mgr, image_load->GetSingleWordInOperand(0u)); + return image->opcode() == spv::Op::OpVariable && + image->result_id() == image_variable->result_id(); +} + +Pass::Status ConvertToSampledImagePass::CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with) { + if (image_to_be_combined_with == nullptr) return Status::Failure; + + std::vector sampler_variable_loads; + FindUses(sampler_variable, &sampler_variable_loads, spv::Op::OpLoad); + for (auto* load : sampler_variable_loads) { + std::vector sampled_image_users; + FindUses(load, &sampled_image_users, spv::Op::OpSampledImage); + for (auto* sampled_image_inst : sampled_image_users) { + if (!DoesSampledImageReferenceImage(sampled_image_inst, + image_to_be_combined_with)) { + return Status::Failure; + } + } + } + return Status::SuccessWithoutChange; +} + +std::unique_ptr +ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + const char* str) { + if (!str) return nullptr; + + auto descriptor_set_binding_pairs = + MakeUnique(); + + while (std::isspace(*str)) str++; // skip leading spaces. + + // The parsing loop, break when points to the end. + while (*str) { + // Parse the descriptor set. + uint32_t descriptor_set = 0; + str = ParseNumberUntilSeparator(str, &descriptor_set); + if (str == nullptr) return nullptr; + + // Find the ':', spaces between the descriptor set and the ':' are not + // allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + + // Parse the binding. + uint32_t binding = 0; + str = ParseNumberUntilSeparator(str, &binding); + if (str == nullptr) return nullptr; + + descriptor_set_binding_pairs->push_back({descriptor_set, binding}); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return descriptor_set_binding_pairs; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.h new file mode 100644 index 00000000..a8b1501e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/convert_to_sampled_image_pass.h @@ -0,0 +1,207 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ +#define SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ + +#include +#include +#include + +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// A struct for a pair of descriptor set and binding. +struct DescriptorSetAndBinding { + uint32_t descriptor_set; + uint32_t binding; + + bool operator==(const DescriptorSetAndBinding& descriptor_set_binding) const { + return descriptor_set_binding.descriptor_set == descriptor_set && + descriptor_set_binding.binding == binding; + } +}; + +// See optimizer.hpp for documentation. +class ConvertToSampledImagePass : public Pass { + public: + // Hashing functor for the pair of descriptor set and binding. + struct DescriptorSetAndBindingHash { + size_t operator()( + const DescriptorSetAndBinding& descriptor_set_binding) const { + return std::hash()(descriptor_set_binding.descriptor_set) ^ + std::hash()(descriptor_set_binding.binding); + } + }; + + using SetOfDescriptorSetAndBindingPairs = + std::unordered_set; + using DescriptorSetBindingToInstruction = + std::unordered_map; + + explicit ConvertToSampledImagePass( + const std::vector& descriptor_set_binding_pairs) + : descriptor_set_binding_pairs_(descriptor_set_binding_pairs.begin(), + descriptor_set_binding_pairs.end()) {} + + const char* name() const override { return "convert-to-sampled-image"; } + Status Process() override; + + // Parses the given null-terminated C string to get a vector of descriptor set + // and binding pairs. Returns a unique pointer to the vector of descriptor set + // and binding pairs built from the given |str| on success. Returns a nullptr + // if the given string is not valid for building the vector of pairs. + // A valid string for building the vector of pairs should follow the rule + // below: + // + // ": : ..." + // Example: + // "3:5 2:1 0:4" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a descriptor set and binding pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within a descriptor set or binding. + // + // In each entry, the descriptor set and binding are separated by ':'. + // Missing ':' in any entry is invalid. And it is invalid to have blank + // spaces in between the descriptor set and ':' or ':' and the binding. + // + // : the descriptor set. + // The text must represent a valid uint32_t number. + // + // : the binding. + // The text must represent a valid uint32_t number. + static std::unique_ptr> + ParseDescriptorSetBindingPairsString(const char* str); + + private: + // Collects resources to convert to sampled image and saves them in + // |descriptor_set_binding_pair_to_sampler| if the resource is a sampler and + // saves them in |descriptor_set_binding_pair_to_image| if the resource is an + // image. Returns false if two samplers or two images have the same descriptor + // set and binding. Otherwise, returns true. + bool CollectResourcesToConvert( + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_sampler, + DescriptorSetBindingToInstruction* descriptor_set_binding_pair_to_image) + const; + + // Finds an OpDecorate with DescriptorSet decorating |inst| and another + // OpDecorate with Binding decorating |inst|. Stores the descriptor set and + // binding in |descriptor_set_binding|. Returns whether it successfully finds + // the descriptor set and binding or not. + bool GetDescriptorSetBinding( + const Instruction& inst, + DescriptorSetAndBinding* descriptor_set_binding) const; + + // Returns whether |descriptor_set_binding| is a pair of a descriptor set + // and a binding that we have to convert resources with it to a sampled image + // or not. + bool ShouldResourceBeConverted( + const DescriptorSetAndBinding& descriptor_set_binding) const; + + // Returns the pointee type of the type of variable |variable|. If |variable| + // is not an OpVariable instruction, just returns nullptr. + const analysis::Type* GetVariableType(const Instruction& variable) const; + + // Returns the storage class of |variable|. + spv::StorageClass GetStorageClass(const Instruction& variable) const; + + // Finds |inst|'s users whose opcode is |user_opcode| or users of OpCopyObject + // instructions of |inst| whose opcode is |user_opcode| and puts them in + // |uses|. + void FindUses(const Instruction* inst, std::vector* uses, + spv::Op user_opcode) const; + + // Finds OpImage* instructions using |image| or OpCopyObject instructions that + // copy |image| and puts them in |uses|. + void FindUsesOfImage(const Instruction* image, + std::vector* uses) const; + + // Creates an OpImage instruction that extracts the image from the sampled + // image |sampled_image|. + Instruction* CreateImageExtraction(Instruction* sampled_image); + + // Converts |image_variable| whose type is an image pointer to sampled image + // type. Updates users of |image_variable| accordingly. If some instructions + // e.g., OpImageRead use |image_variable| as an Image operand, creates an + // image extracted from the sampled image using OpImage and replace the Image + // operands of the users with the extracted image. If some OpSampledImage + // instructions use |image_variable| and sampler whose descriptor set and + // binding are the same with |image_variable|, just combines |image_variable| + // and the sampler to a sampled image. + Pass::Status UpdateImageVariableToSampledImage( + Instruction* image_variable, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Returns the id of type sampled image type whose image type is the one of + // |image_variable|. + uint32_t GetSampledImageTypeForImage(Instruction* image_variable); + + // Moves |inst| next to the OpType* instruction with |type_id|. + void MoveInstructionNextToType(Instruction* inst, uint32_t type_id); + + // Converts |image_variable| whose type is an image pointer to sampled image + // with the type id |sampled_image_type_id|. Returns whether it successfully + // converts the type of |image_variable| or not. + bool ConvertImageVariableToSampledImage(Instruction* image_variable, + uint32_t sampled_image_type_id); + + // Replaces |sampled_image_load| instruction used by OpImage* with the image + // extracted from |sampled_image_load|. Returns the extracted image or nullptr + // if it does not have uses. + Instruction* UpdateImageUses(Instruction* sampled_image_load); + + // Returns true if the sampler of |sampled_image_inst| is decorated by a + // descriptor set and a binding |descriptor_set_binding|. + bool IsSamplerOfSampledImageDecoratedByDescriptorSetBinding( + Instruction* sampled_image_inst, + const DescriptorSetAndBinding& descriptor_set_binding); + + // Replaces OpSampledImage instructions using |image_load| with |image_load| + // if the sampler of the OpSampledImage instruction has descriptor set and + // binding |image_descriptor_set_binding|. Otherwise, replaces |image_load| + // with |image_extraction|. + void UpdateSampledImageUses( + Instruction* image_load, Instruction* image_extraction, + const DescriptorSetAndBinding& image_descriptor_set_binding); + + // Checks the uses of |sampler_variable|. When a sampler is used by + // OpSampledImage instruction, the corresponding image must be + // |image_to_be_combined_with| that should be already converted to a sampled + // image by UpdateImageVariableToSampledImage() method. + Pass::Status CheckUsesOfSamplerVariable( + const Instruction* sampler_variable, + Instruction* image_to_be_combined_with); + + // Returns true if Image operand of |sampled_image_inst| is the image of + // |image_variable|. + bool DoesSampledImageReferenceImage(Instruction* sampled_image_inst, + Instruction* image_variable); + + // A set of pairs of descriptor set and binding. If an image and/or a sampler + // have a pair of descriptor set and binding that is an element of + // |descriptor_set_binding_pairs_|, they/it will be converted to a sampled + // image by this pass. + const SetOfDescriptorSetAndBindingPairs descriptor_set_binding_pairs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONVERT_TO_SAMPLED_IMAGE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.cpp new file mode 100644 index 00000000..d66193b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.cpp @@ -0,0 +1,961 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/copy_prop_arrays.h" + +#include + +#include "source/opt/ir_builder.h" + +namespace spvtools { +namespace opt { +namespace { + +constexpr uint32_t kLoadPointerInOperand = 0; +constexpr uint32_t kStorePointerInOperand = 0; +constexpr uint32_t kStoreObjectInOperand = 1; +constexpr uint32_t kCompositeExtractObjectInOperand = 0; +constexpr uint32_t kTypePointerStorageClassInIdx = 0; +constexpr uint32_t kTypePointerPointeeInIdx = 1; +constexpr uint32_t kExtInstSetInIdx = 0; +constexpr uint32_t kExtInstOpInIdx = 1; +constexpr uint32_t kInterpolantInIdx = 2; + +bool IsDebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetCommonDebugOpcode(); + return dbg_opcode == CommonDebugInfoDebugDeclare || + dbg_opcode == CommonDebugInfoDebugValue; +} + +// Returns the number of members in |type|. If |type| is not a composite type +// or the number of components is not known at compile time, the return value +// will be 0. +uint32_t GetNumberOfMembers(const analysis::Type* type, IRContext* context) { + if (const analysis::Struct* struct_type = type->AsStruct()) { + return static_cast(struct_type->element_types().size()); + } else if (const analysis::Array* array_type = type->AsArray()) { + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant( + array_type->LengthId()); + + if (length_const == nullptr) { + // This can happen if the length is an OpSpecConstant. + return 0; + } + assert(length_const->type()->AsInteger()); + return length_const->GetU32(); + } else if (const analysis::Vector* vector_type = type->AsVector()) { + return vector_type->element_count(); + } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } else { + return 0; + } +} + +} // namespace + +Pass::Status CopyPropagateArrays::Process() { + bool modified = false; + for (Function& function : *get_module()) { + if (function.IsDeclaration()) { + continue; + } + + BasicBlock* entry_bb = &*function.begin(); + + for (auto var_inst = entry_bb->begin(); + var_inst->opcode() == spv::Op::OpVariable; ++var_inst) { + worklist_.push(&*var_inst); + } + } + + while (!worklist_.empty()) { + Instruction* var_inst = worklist_.front(); + worklist_.pop(); + + // Find the only store to the entire memory location, if it exists. + Instruction* store_inst = FindStoreInstruction(&*var_inst); + + if (!store_inst) { + continue; + } + + std::unique_ptr source_object = + FindSourceObjectIfPossible(&*var_inst, store_inst); + + if (source_object == nullptr) { + continue; + } + + if (!IsPointerToArrayType(var_inst->type_id()) && + source_object->GetStorageClass() != spv::StorageClass::Input) { + continue; + } + + if (CanUpdateUses(&*var_inst, source_object->GetPointerTypeId(this))) { + modified = true; + + PropagateObject(&*var_inst, source_object.get(), store_inst); + } + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +std::unique_ptr +CopyPropagateArrays::FindSourceObjectIfPossible(Instruction* var_inst, + Instruction* store_inst) { + assert(var_inst->opcode() == spv::Op::OpVariable && "Expecting a variable."); + + // Check that the variable is a composite object where |store_inst| + // dominates all of its loads. + if (!store_inst) { + return nullptr; + } + + // Look at the loads to ensure they are dominated by the store. + if (!HasValidReferencesOnly(var_inst, store_inst)) { + return nullptr; + } + + // If so, look at the store to see if it is the copy of an object. + std::unique_ptr source = GetSourceObjectIfAny( + store_inst->GetSingleWordInOperand(kStoreObjectInOperand)); + + if (!source) { + return nullptr; + } + + // Ensure that |source| does not change between the point at which it is + // loaded, and the position in which |var_inst| is loaded. + // + // For now we will go with the easy to implement approach, and check that the + // entire variable (not just the specific component) is never written to. + + if (!HasNoStores(source->GetVariable())) { + return nullptr; + } + return source; +} + +Instruction* CopyPropagateArrays::FindStoreInstruction( + const Instruction* var_inst) const { + Instruction* store_inst = nullptr; + get_def_use_mgr()->WhileEachUser( + var_inst, [&store_inst, var_inst](Instruction* use) { + if (use->opcode() == spv::Op::OpStore && + use->GetSingleWordInOperand(kStorePointerInOperand) == + var_inst->result_id()) { + if (store_inst == nullptr) { + store_inst = use; + } else { + store_inst = nullptr; + return false; + } + } + return true; + }); + return store_inst; +} + +void CopyPropagateArrays::PropagateObject(Instruction* var_inst, + MemoryObject* source, + Instruction* insertion_point) { + assert(var_inst->opcode() == spv::Op::OpVariable && + "This function propagates variables."); + + Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source); + context()->KillNamesAndDecorates(var_inst); + UpdateUses(var_inst, new_access_chain); +} + +Instruction* CopyPropagateArrays::BuildNewAccessChain( + Instruction* insertion_point, + CopyPropagateArrays::MemoryObject* source) const { + InstructionBuilder builder( + context(), insertion_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + if (source->AccessChain().size() == 0) { + return source->GetVariable(); + } + + source->BuildConstants(); + std::vector access_ids(source->AccessChain().size()); + std::transform( + source->AccessChain().cbegin(), source->AccessChain().cend(), + access_ids.begin(), [](const AccessChainEntry& entry) { + assert(entry.is_result_id && "Constants needs to be built first."); + return entry.result_id; + }); + + return builder.AddAccessChain(source->GetPointerTypeId(this), + source->GetVariable()->result_id(), access_ids); +} + +bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) { + return get_def_use_mgr()->WhileEachUser(ptr_inst, [this](Instruction* use) { + if (use->opcode() == spv::Op::OpLoad) { + return true; + } else if (use->opcode() == spv::Op::OpAccessChain) { + return HasNoStores(use); + } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) { + return true; + } else if (use->opcode() == spv::Op::OpStore) { + return false; + } else if (use->opcode() == spv::Op::OpImageTexelPointer) { + return true; + } else if (use->opcode() == spv::Op::OpEntryPoint) { + return true; + } else if (IsInterpolationInstruction(use)) { + return true; + } else if (use->IsCommonDebugInstr()) { + return true; + } + // Some other instruction. Be conservative. + return false; + }); +} + +bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, + Instruction* store_inst) { + BasicBlock* store_block = context()->get_instr_block(store_inst); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(store_block->GetParent()); + + return get_def_use_mgr()->WhileEachUser( + ptr_inst, + [this, store_inst, dominator_analysis, ptr_inst](Instruction* use) { + if (use->opcode() == spv::Op::OpLoad || + use->opcode() == spv::Op::OpImageTexelPointer) { + // TODO: If there are many load in the same BB as |store_inst| the + // time to do the multiple traverses can add up. Consider collecting + // those loads and doing a single traversal. + return dominator_analysis->Dominates(store_inst, use); + } else if (IsInterpolationInstruction(use)) { + // GLSL InterpolateAt* instructions work similarly to loads + uint32_t interpolant = use->GetSingleWordInOperand(kInterpolantInIdx); + if (interpolant != + store_inst->GetSingleWordInOperand(kStorePointerInOperand)) + return false; + return dominator_analysis->Dominates(store_inst, use); + } else if (use->opcode() == spv::Op::OpAccessChain) { + return HasValidReferencesOnly(use, store_inst); + } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) { + return true; + } else if (use->opcode() == spv::Op::OpStore) { + // If we are storing to part of the object it is not an candidate. + return ptr_inst->opcode() == spv::Op::OpVariable && + store_inst->GetSingleWordInOperand(kStorePointerInOperand) == + ptr_inst->result_id(); + } else if (IsDebugDeclareOrValue(use)) { + return true; + } + // Some other instruction. Be conservative. + return false; + }); +} + +std::unique_ptr +CopyPropagateArrays::GetSourceObjectIfAny(uint32_t result) { + Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result); + + switch (result_inst->opcode()) { + case spv::Op::OpLoad: + return BuildMemoryObjectFromLoad(result_inst); + case spv::Op::OpCompositeExtract: + return BuildMemoryObjectFromExtract(result_inst); + case spv::Op::OpCompositeConstruct: + return BuildMemoryObjectFromCompositeConstruct(result_inst); + case spv::Op::OpCopyObject: + case spv::Op::OpCopyLogical: + return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0)); + case spv::Op::OpCompositeInsert: + return BuildMemoryObjectFromInsert(result_inst); + default: + return nullptr; + } +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromLoad(Instruction* load_inst) { + std::vector components_in_reverse; + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + Instruction* current_inst = def_use_mgr->GetDef( + load_inst->GetSingleWordInOperand(kLoadPointerInOperand)); + + // Build the access chain for the memory object by collecting the indices used + // in the OpAccessChain instructions. If we find a variable index, then + // return |nullptr| because we cannot know for sure which memory location is + // used. + // + // It is built in reverse order because the different |OpAccessChain| + // instructions are visited in reverse order from which they are applied. + while (current_inst->opcode() == spv::Op::OpAccessChain) { + for (uint32_t i = current_inst->NumInOperands() - 1; i >= 1; --i) { + uint32_t element_index_id = current_inst->GetSingleWordInOperand(i); + components_in_reverse.push_back(element_index_id); + } + current_inst = def_use_mgr->GetDef(current_inst->GetSingleWordInOperand(0)); + } + + // If the address in the load is not constructed from an |OpVariable| + // instruction followed by a series of |OpAccessChain| instructions, then + // return |nullptr| because we cannot identify the owner or access chain + // exactly. + if (current_inst->opcode() != spv::Op::OpVariable) { + return nullptr; + } + + // Build the memory object. Use |rbegin| and |rend| to put the access chain + // back in the correct order. + return std::unique_ptr( + new MemoryObject(current_inst, components_in_reverse.rbegin(), + components_in_reverse.rend())); +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) { + assert(extract_inst->opcode() == spv::Op::OpCompositeExtract && + "Expecting an OpCompositeExtract instruction."); + std::unique_ptr result = GetSourceObjectIfAny( + extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand)); + + if (!result) { + return nullptr; + } + + // Copy the indices of the extract instruction to |OpAccessChain| indices. + std::vector components; + for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) { + components.push_back({false, {extract_inst->GetSingleWordInOperand(i)}}); + } + result->PushIndirection(components); + return result; +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct( + Instruction* conststruct_inst) { + assert(conststruct_inst->opcode() == spv::Op::OpCompositeConstruct && + "Expecting an OpCompositeConstruct instruction."); + + // If every operand in the instruction are part of the same memory object, and + // are being combined in the same order, then the result is the same as the + // parent. + + std::unique_ptr memory_object = + GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(0)); + + if (!memory_object) { + return nullptr; + } + + if (!memory_object->IsMember()) { + return nullptr; + } + + AccessChainEntry last_access = memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, 0)) { + return nullptr; + } + + memory_object->PopIndirection(); + if (memory_object->GetNumberOfMembers() != + conststruct_inst->NumInOperands()) { + return nullptr; + } + + for (uint32_t i = 1; i < conststruct_inst->NumInOperands(); ++i) { + std::unique_ptr member_object = + GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(i)); + + if (!member_object) { + return nullptr; + } + + if (!member_object->IsMember()) { + return nullptr; + } + + if (!memory_object->Contains(member_object.get())) { + return nullptr; + } + + last_access = member_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, i)) { + return nullptr; + } + } + return memory_object; +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) { + assert(insert_inst->opcode() == spv::Op::OpCompositeInsert && + "Expecting an OpCompositeInsert instruction."); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(insert_inst->type_id()); + + uint32_t number_of_elements = GetNumberOfMembers(result_type, context()); + + if (number_of_elements == 0) { + return nullptr; + } + + if (insert_inst->NumInOperands() != 3) { + return nullptr; + } + + if (insert_inst->GetSingleWordInOperand(2) != number_of_elements - 1) { + return nullptr; + } + + std::unique_ptr memory_object = + GetSourceObjectIfAny(insert_inst->GetSingleWordInOperand(0)); + + if (!memory_object) { + return nullptr; + } + + if (!memory_object->IsMember()) { + return nullptr; + } + + AccessChainEntry last_access = memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(last_access, number_of_elements - 1)) { + return nullptr; + } + + memory_object->PopIndirection(); + + Instruction* current_insert = + def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1)); + for (uint32_t i = number_of_elements - 1; i > 0; --i) { + if (current_insert->opcode() != spv::Op::OpCompositeInsert) { + return nullptr; + } + + if (current_insert->NumInOperands() != 3) { + return nullptr; + } + + if (current_insert->GetSingleWordInOperand(2) != i - 1) { + return nullptr; + } + + std::unique_ptr current_memory_object = + GetSourceObjectIfAny(current_insert->GetSingleWordInOperand(0)); + + if (!current_memory_object) { + return nullptr; + } + + if (!current_memory_object->IsMember()) { + return nullptr; + } + + if (memory_object->AccessChain().size() + 1 != + current_memory_object->AccessChain().size()) { + return nullptr; + } + + if (!memory_object->Contains(current_memory_object.get())) { + return nullptr; + } + + AccessChainEntry current_last_access = + current_memory_object->AccessChain().back(); + if (!IsAccessChainIndexValidAndEqualTo(current_last_access, i - 1)) { + return nullptr; + } + current_insert = + def_use_mgr->GetDef(current_insert->GetSingleWordInOperand(1)); + } + + return memory_object; +} + +bool CopyPropagateArrays::IsAccessChainIndexValidAndEqualTo( + const AccessChainEntry& entry, uint32_t value) const { + if (!entry.is_result_id) { + return entry.immediate == value; + } + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Constant* constant = + const_mgr->FindDeclaredConstant(entry.result_id); + if (!constant || !constant->type()->AsInteger()) { + return false; + } + return constant->GetU32() == value; +} + +bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Pointer* pointer_type = type_mgr->GetType(type_id)->AsPointer(); + if (pointer_type) { + return pointer_type->pointee_type()->kind() == analysis::Type::kArray || + pointer_type->pointee_type()->kind() == analysis::Type::kImage; + } + return false; +} + +bool CopyPropagateArrays::IsInterpolationInstruction(Instruction* inst) { + if (inst->opcode() == spv::Op::OpExtInst && + inst->GetSingleWordInOperand(kExtInstSetInIdx) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450()) { + uint32_t ext_inst = inst->GetSingleWordInOperand(kExtInstOpInIdx); + switch (ext_inst) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return true; + } + } + return false; +} + +bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, + uint32_t type_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + analysis::Type* type = type_mgr->GetType(type_id); + if (type->AsRuntimeArray()) { + return false; + } + + if (!type->AsStruct() && !type->AsArray() && !type->AsPointer()) { + // If the type is not an aggregate, then the desired type must be the + // same as the current type. No work to do, and we can do that. + return true; + } + + return def_use_mgr->WhileEachUse(original_ptr_inst, [this, type_mgr, + const_mgr, + type](Instruction* use, + uint32_t) { + if (IsDebugDeclareOrValue(use)) return true; + + switch (use->opcode()) { + case spv::Op::OpLoad: { + analysis::Pointer* pointer_type = type->AsPointer(); + uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type()); + + if (new_type_id != use->type_id()) { + return CanUpdateUses(use, new_type_id); + } + return true; + } + case spv::Op::OpExtInst: + if (IsInterpolationInstruction(use)) { + return true; + } + return false; + case spv::Op::OpAccessChain: { + analysis::Pointer* pointer_type = type->AsPointer(); + const analysis::Type* pointee_type = pointer_type->pointee_type(); + + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i)); + if (index_const) { + access_chain.push_back(index_const->GetU32()); + } else { + // Variable index means the type is a type where every element + // is the same type. Use element 0 to get the type. + access_chain.push_back(0); + + // We are trying to access a struct with variable indices. + // This cannot happen. + if (pointee_type->kind() == analysis::Type::kStruct) { + return false; + } + } + } + + const analysis::Type* new_pointee_type = + type_mgr->GetMemberType(pointee_type, access_chain); + analysis::Pointer pointerTy(new_pointee_type, + pointer_type->storage_class()); + uint32_t new_pointer_type_id = + context()->get_type_mgr()->GetTypeInstruction(&pointerTy); + if (new_pointer_type_id == 0) { + return false; + } + + if (new_pointer_type_id != use->type_id()) { + return CanUpdateUses(use, new_pointer_type_id); + } + return true; + } + case spv::Op::OpCompositeExtract: { + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + access_chain.push_back(use->GetSingleWordInOperand(i)); + } + + const analysis::Type* new_type = + type_mgr->GetMemberType(type, access_chain); + uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type); + if (new_type_id == 0) { + return false; + } + + if (new_type_id != use->type_id()) { + return CanUpdateUses(use, new_type_id); + } + return true; + } + case spv::Op::OpStore: + // If needed, we can create an element-by-element copy to change the + // type of the value being stored. This way we can always handled + // stores. + return true; + case spv::Op::OpImageTexelPointer: + case spv::Op::OpName: + return true; + default: + return use->IsDecoration(); + } + }); +} + +void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, + Instruction* new_ptr_inst) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + std::vector > uses; + def_use_mgr->ForEachUse(original_ptr_inst, + [&uses](Instruction* use, uint32_t index) { + uses.push_back({use, index}); + }); + + for (auto pair : uses) { + Instruction* use = pair.first; + uint32_t index = pair.second; + + if (use->IsCommonDebugInstr()) { + switch (use->GetCommonDebugOpcode()) { + case CommonDebugInfoDebugDeclare: { + if (new_ptr_inst->opcode() == spv::Op::OpVariable || + new_ptr_inst->opcode() == spv::Op::OpFunctionParameter) { + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + } else { + // Based on the spec, we cannot use a pointer other than OpVariable + // or OpFunctionParameter for DebugDeclare. We have to use + // DebugValue with Deref. + + context()->ForgetUses(use); + + // Change DebugDeclare to DebugValue. + use->SetOperand(index - 2, + {static_cast(CommonDebugInfoDebugValue)}); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Add Deref operation. + Instruction* dbg_expr = + def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1)); + auto* deref_expr_instr = + context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + use->SetOperand(index + 1, {deref_expr_instr->result_id()}); + + context()->AnalyzeUses(deref_expr_instr); + context()->AnalyzeUses(use); + } + break; + } + case CommonDebugInfoDebugValue: + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + break; + default: + assert(false && "Don't know how to rewrite instruction"); + break; + } + continue; + } + + switch (use->opcode()) { + case spv::Op::OpLoad: { + // Replace the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Update the type. + Instruction* pointer_type_inst = + def_use_mgr->GetDef(new_ptr_inst->type_id()); + uint32_t new_type_id = + pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx); + if (new_type_id != use->type_id()) { + use->SetResultType(new_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + + AddUsesToWorklist(use); + } break; + case spv::Op::OpExtInst: { + if (IsInterpolationInstruction(use)) { + // Replace the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + } else { + assert(false && "Don't know how to rewrite instruction"); + } + } break; + case spv::Op::OpAccessChain: { + // Update the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Convert the ids on the OpAccessChain to indices that can be used to + // get the specific member. + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i)); + if (index_const) { + access_chain.push_back(index_const->GetU32()); + } else { + // Variable index means the type is an type where every element + // is the same type. Use element 0 to get the type. + access_chain.push_back(0); + } + } + + Instruction* pointer_type_inst = + get_def_use_mgr()->GetDef(new_ptr_inst->type_id()); + + uint32_t new_pointee_type_id = GetMemberTypeId( + pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx), + access_chain); + + spv::StorageClass storage_class = static_cast( + pointer_type_inst->GetSingleWordInOperand( + kTypePointerStorageClassInIdx)); + + uint32_t new_pointer_type_id = + type_mgr->FindPointerToType(new_pointee_type_id, storage_class); + + if (new_pointer_type_id != use->type_id()) { + use->SetResultType(new_pointer_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + } break; + case spv::Op::OpCompositeExtract: { + // Update the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + uint32_t new_type_id = new_ptr_inst->type_id(); + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + access_chain.push_back(use->GetSingleWordInOperand(i)); + } + + new_type_id = GetMemberTypeId(new_type_id, access_chain); + + if (new_type_id != use->type_id()) { + use->SetResultType(new_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + } break; + case spv::Op::OpStore: + // If the use is the pointer, then it is the single store to that + // variable. We do not want to replace it. Instead, it will become + // dead after all of the loads are removed, and ADCE will get rid of it. + // + // If the use is the object being stored, we will create a copy of the + // object turning it into the correct type. The copy is done by + // decomposing the object into the base type, which must be the same, + // and then rebuilding them. + if (index == 1) { + Instruction* target_pointer = def_use_mgr->GetDef( + use->GetSingleWordInOperand(kStorePointerInOperand)); + Instruction* pointer_type = + def_use_mgr->GetDef(target_pointer->type_id()); + uint32_t pointee_type_id = + pointer_type->GetSingleWordInOperand(kTypePointerPointeeInIdx); + uint32_t copy = GenerateCopy(original_ptr_inst, pointee_type_id, use); + assert(copy != 0 && + "Should not be updating uses unless we know it can be done."); + + context()->ForgetUses(use); + use->SetInOperand(index, {copy}); + context()->AnalyzeUses(use); + } + break; + case spv::Op::OpDecorate: + // We treat an OpImageTexelPointer as a load. The result type should + // always have the Image storage class, and should not need to be + // updated. + case spv::Op::OpImageTexelPointer: + // Replace the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + break; + default: + assert(false && "Don't know how to rewrite instruction"); + break; + } + } +} + +uint32_t CopyPropagateArrays::GetMemberTypeId( + uint32_t id, const std::vector& access_chain) const { + for (uint32_t element_index : access_chain) { + Instruction* type_inst = get_def_use_mgr()->GetDef(id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + id = type_inst->GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: + id = type_inst->GetSingleWordInOperand(element_index); + break; + default: + break; + } + assert(id != 0 && + "Tried to extract from an object where it cannot be done."); + } + return id; +} + +void CopyPropagateArrays::AddUsesToWorklist(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + def_use_mgr->ForEachUse(inst, [this](Instruction* use, uint32_t) { + if (use->opcode() == spv::Op::OpStore) { + uint32_t var_id; + Instruction* target_pointer = GetPtr(use, &var_id); + if (target_pointer->opcode() != spv::Op::OpVariable) { + return; + } + + worklist_.push(target_pointer); + } + }); +} + +void CopyPropagateArrays::MemoryObject::PushIndirection( + const std::vector& access_chain) { + access_chain_.insert(access_chain_.end(), access_chain.begin(), + access_chain.end()); +} + +uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() { + IRContext* context = variable_inst_->context(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + const analysis::Type* type = type_mgr->GetType(variable_inst_->type_id()); + type = type->AsPointer()->pointee_type(); + + std::vector access_indices = GetAccessIds(); + type = type_mgr->GetMemberType(type, access_indices); + + return opt::GetNumberOfMembers(type, context); +} +template +CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst, + iterator begin, iterator end) + : variable_inst_(var_inst) { + std::transform(begin, end, std::back_inserter(access_chain_), + [](uint32_t id) { + return AccessChainEntry{true, {id}}; + }); +} + +std::vector CopyPropagateArrays::MemoryObject::GetAccessIds() const { + analysis::ConstantManager* const_mgr = + variable_inst_->context()->get_constant_mgr(); + + std::vector indices(AccessChain().size()); + std::transform(AccessChain().cbegin(), AccessChain().cend(), indices.begin(), + [&const_mgr](const AccessChainEntry& entry) { + if (entry.is_result_id) { + const analysis::Constant* constant = + const_mgr->FindDeclaredConstant(entry.result_id); + return constant == nullptr ? 0 : constant->GetU32(); + } + + return entry.immediate; + }); + return indices; +} + +bool CopyPropagateArrays::MemoryObject::Contains( + CopyPropagateArrays::MemoryObject* other) { + if (this->GetVariable() != other->GetVariable()) { + return false; + } + + if (AccessChain().size() > other->AccessChain().size()) { + return false; + } + + for (uint32_t i = 0; i < AccessChain().size(); i++) { + if (AccessChain()[i] != other->AccessChain()[i]) { + return false; + } + } + return true; +} + +void CopyPropagateArrays::MemoryObject::BuildConstants() { + for (auto& entry : access_chain_) { + if (entry.is_result_id) { + continue; + } + + auto context = variable_inst_->context(); + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = + context->get_type_mgr()->GetRegisteredType(&int_type); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Constant* index_const = + const_mgr->GetConstant(uint32_type, {entry.immediate}); + entry.result_id = + const_mgr->GetDefiningInstruction(index_const)->result_id(); + entry.is_result_id = true; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.h b/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.h new file mode 100644 index 00000000..bf4bfb5c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/copy_prop_arrays.h @@ -0,0 +1,274 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COPY_PROP_ARRAYS_H_ +#define SOURCE_OPT_COPY_PROP_ARRAYS_H_ + +#include +#include + +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +// This pass implements a simple array copy propagation. It does not do a full +// array data flow. It looks for simple cases that meet the following +// conditions: +// +// 1) The source must never be stored to. +// 2) The target must be stored to exactly once. +// 3) The store to the target must be a store to the entire array, and be a +// copy of the entire source. +// 4) All loads of the target must be dominated by the store. +// +// The hard part is keeping all of the types correct. We do not want to +// have to do too large a search to update everything, which may not be +// possible, so we give up if we see any instruction that might be hard to +// update. + +class CopyPropagateArrays : public MemPass { + public: + const char* name() const override { return "copy-propagate-arrays"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisCFG | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisDecorations | + IRContext::kAnalysisDominatorAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Represents one index in the OpAccessChain instruction. It can be either + // an instruction's result_id (OpConstant by ex), or a immediate value. + // Immediate values are used to prepare the final access chain without + // creating OpConstant instructions until done. + struct AccessChainEntry { + bool is_result_id; + union { + uint32_t result_id; + uint32_t immediate; + }; + + bool operator!=(const AccessChainEntry& other) const { + return other.is_result_id != is_result_id || other.result_id != result_id; + } + }; + + // The class used to identify a particular memory object. This memory object + // will be owned by a particular variable, meaning that the memory is part of + // that variable. It could be the entire variable or a member of the + // variable. + class MemoryObject { + public: + // Construction a memory object that is owned by |var_inst|. The iterator + // |begin| and |end| traverse a container of integers that identify which + // member of |var_inst| this memory object will represent. These integers + // are interpreted the same way they would be in an |OpAccessChain| + // instruction. + template + MemoryObject(Instruction* var_inst, iterator begin, iterator end); + + // Change |this| to now point to the member identified by |access_chain| + // (starting from the current member). The elements in |access_chain| are + // interpreted the same as the indices in the |OpAccessChain| + // instruction. + void PushIndirection(const std::vector& access_chain); + + // Change |this| to now represent the first enclosing object to which it + // belongs. (Remove the last element off the access_chain). It is invalid + // to call this function if |this| does not represent a member of its owner. + void PopIndirection() { + assert(IsMember()); + access_chain_.pop_back(); + } + + // Returns true if |this| represents a member of its owner, and not the + // entire variable. + bool IsMember() const { return !access_chain_.empty(); } + + // Returns the number of members in the object represented by |this|. If + // |this| does not represent a composite type or the number of components is + // not known at compile time, the return value will be 0. + uint32_t GetNumberOfMembers(); + + // Returns the owning variable that the memory object is contained in. + Instruction* GetVariable() const { return variable_inst_; } + + // Returns a vector of integers that can be used to access the specific + // member that |this| represents starting from the owning variable. These + // values are to be interpreted the same way the indices are in an + // |OpAccessChain| instruction. + const std::vector& AccessChain() const { + return access_chain_; + } + + // Converts all immediate values in the AccessChain their OpConstant + // equivalent. + void BuildConstants(); + + // Returns the type id of the pointer type that can be used to point to this + // memory object. + uint32_t GetPointerTypeId(const CopyPropagateArrays* pass) const { + analysis::DefUseManager* def_use_mgr = + GetVariable()->context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = + GetVariable()->context()->get_type_mgr(); + + Instruction* var_pointer_inst = + def_use_mgr->GetDef(GetVariable()->type_id()); + + uint32_t member_type_id = pass->GetMemberTypeId( + var_pointer_inst->GetSingleWordInOperand(1), GetAccessIds()); + + uint32_t member_pointer_type_id = type_mgr->FindPointerToType( + member_type_id, static_cast( + var_pointer_inst->GetSingleWordInOperand(0))); + return member_pointer_type_id; + } + + // Returns the storage class of the memory object. + spv::StorageClass GetStorageClass() const { + analysis::TypeManager* type_mgr = + GetVariable()->context()->get_type_mgr(); + const analysis::Pointer* pointer_type = + type_mgr->GetType(GetVariable()->type_id())->AsPointer(); + return pointer_type->storage_class(); + } + + // Returns true if |other| represents memory that is contains inside of the + // memory represented by |this|. + bool Contains(MemoryObject* other); + + private: + // The variable that owns this memory object. + Instruction* variable_inst_; + + // The access chain to reach the particular member the memory object + // represents. It should be interpreted the same way the indices in an + // |OpAccessChain| are interpreted. + std::vector access_chain_; + std::vector GetAccessIds() const; + }; + + // Returns the memory object being stored to |var_inst| in the store + // instruction |store_inst|, if one exists, that can be used in place of + // |var_inst| in all of the loads of |var_inst|. This code is conservative + // and only identifies very simple cases. If no such memory object can be + // found, the return value is |nullptr|. + std::unique_ptr FindSourceObjectIfPossible( + Instruction* var_inst, Instruction* store_inst); + + // Replaces all loads of |var_inst| with a load from |source| instead. + // |insertion_pos| is a position where it is possible to construct the + // address of |source| and also dominates all of the loads of |var_inst|. + void PropagateObject(Instruction* var_inst, MemoryObject* source, + Instruction* insertion_pos); + + // Returns true if all of the references to |ptr_inst| can be rewritten and + // are dominated by |store_inst|. + bool HasValidReferencesOnly(Instruction* ptr_inst, Instruction* store_inst); + + // Returns a memory object that at one time was equivalent to the value in + // |result|. If no such memory object exists, the return value is |nullptr|. + std::unique_ptr GetSourceObjectIfAny(uint32_t result); + + // Returns the memory object that is loaded by |load_inst|. If a memory + // object cannot be identified, the return value is |nullptr|. The opcode of + // |load_inst| must be |OpLoad|. + std::unique_ptr BuildMemoryObjectFromLoad( + Instruction* load_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |extract_inst|. If a memory object cannot be identified, the return + // value is |nullptr|. The opcode of |extract_inst| must be + // |OpCompositeExtract|. + std::unique_ptr BuildMemoryObjectFromExtract( + Instruction* extract_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |construct_inst|. If a memory object cannot be identified, the return + // value is |nullptr|. The opcode of |constuct_inst| must be + // |OpCompositeConstruct|. + std::unique_ptr BuildMemoryObjectFromCompositeConstruct( + Instruction* conststruct_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |insert_inst|. If a memory object cannot be identified, the return + // value is |nullptr|. The opcode of |insert_inst| must be + // |OpCompositeInsert|. This function looks for a series of + // |OpCompositeInsert| instructions that insert the elements one at a time in + // order from beginning to end. + std::unique_ptr BuildMemoryObjectFromInsert( + Instruction* insert_inst); + + // Return true if the given entry can represent the given value. + bool IsAccessChainIndexValidAndEqualTo(const AccessChainEntry& entry, + uint32_t value) const; + + // Return true if |type_id| is a pointer type whose pointee type is an array. + bool IsPointerToArrayType(uint32_t type_id); + + // Return true if |inst| is one of the InterpolateAt* GLSL.std.450 extended + // instructions. + bool IsInterpolationInstruction(Instruction* inst); + + // Returns true if there are not stores using |ptr_inst| or something derived + // from it. + bool HasNoStores(Instruction* ptr_inst); + + // Creates an |OpAccessChain| instruction whose result is a pointer the memory + // represented by |source|. The new instruction will be placed before + // |insertion_point|. |insertion_point| must be part of a function. Returns + // the new instruction. + Instruction* BuildNewAccessChain(Instruction* insertion_point, + MemoryObject* source) const; + + // Rewrites all uses of |original_ptr| to use |new_pointer_inst| updating + // types of other instructions as needed. This function should not be called + // if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns + // false. + void UpdateUses(Instruction* original_ptr_inst, + Instruction* new_pointer_inst); + + // Return true if |UpdateUses| is able to change all of the uses of + // |original_ptr_inst| to |type_id| and still have valid code. + bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id); + + // Returns a store to |var_inst| that writes to the entire variable, and is + // the only store that does so. Note it does not look through OpAccessChain + // instruction, so partial stores are not considered. + Instruction* FindStoreInstruction(const Instruction* var_inst) const; + + // Return the type id of the member of the type |id| access using + // |access_chain|. The elements of |access_chain| are to be interpreted the + // same way the indexes are used in an |OpCompositeExtract| instruction. + uint32_t GetMemberTypeId(uint32_t id, + const std::vector& access_chain) const; + + // If the result of inst is stored to a variable, add that variable to the + // worklist. + void AddUsesToWorklist(Instruction* inst); + + // OpVariable worklist. An instruction is added to this list if we would like + // to run copy propagation on it. + std::queue worklist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COPY_PROP_ARRAYS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.cpp new file mode 100644 index 00000000..63737f19 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dataflow.h" + +#include + +namespace spvtools { +namespace opt { + +bool DataFlowAnalysis::Enqueue(Instruction* inst) { + bool& is_enqueued = on_worklist_[inst]; + if (is_enqueued) return false; + is_enqueued = true; + worklist_.push(inst); + return true; +} + +DataFlowAnalysis::VisitResult DataFlowAnalysis::RunOnce( + Function* function, bool is_first_iteration) { + InitializeWorklist(function, is_first_iteration); + VisitResult ret = VisitResult::kResultFixed; + while (!worklist_.empty()) { + Instruction* top = worklist_.front(); + worklist_.pop(); + on_worklist_[top] = false; + VisitResult result = Visit(top); + if (result == VisitResult::kResultChanged) { + EnqueueSuccessors(top); + ret = VisitResult::kResultChanged; + } + } + return ret; +} + +void DataFlowAnalysis::Run(Function* function) { + VisitResult result = RunOnce(function, true); + while (result == VisitResult::kResultChanged) { + result = RunOnce(function, false); + } +} + +void ForwardDataFlowAnalysis::InitializeWorklist(Function* function, + bool /*is_first_iteration*/) { + context().cfg()->ForEachBlockInReversePostOrder( + function->entry().get(), [this](BasicBlock* bb) { + if (label_position_ == LabelPosition::kLabelsOnly) { + Enqueue(bb->GetLabelInst()); + return; + } + if (label_position_ == LabelPosition::kLabelsAtBeginning) { + Enqueue(bb->GetLabelInst()); + } + for (Instruction& inst : *bb) { + Enqueue(&inst); + } + if (label_position_ == LabelPosition::kLabelsAtEnd) { + Enqueue(bb->GetLabelInst()); + } + }); +} + +void ForwardDataFlowAnalysis::EnqueueUsers(Instruction* inst) { + context().get_def_use_mgr()->ForEachUser( + inst, [this](Instruction* user) { Enqueue(user); }); +} + +void ForwardDataFlowAnalysis::EnqueueBlockSuccessors(Instruction* inst) { + if (inst->opcode() != spv::Op::OpLabel) return; + context() + .cfg() + ->block(inst->result_id()) + ->ForEachSuccessorLabel([this](uint32_t* label) { + Enqueue(context().cfg()->block(*label)->GetLabelInst()); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.h new file mode 100644 index 00000000..be07415d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dataflow.h @@ -0,0 +1,148 @@ +// Copyright (c) 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DATAFLOW_H_ +#define SOURCE_OPT_DATAFLOW_H_ + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Generic data-flow analysis. +// Maintains a worklist of instructions to process and processes them in a +// specified order. See also ForwardDataFlowAnalysis, which is specialized for +// forward data-flow analysis. +class DataFlowAnalysis { + public: + // The result of a |Visit| operation on an instruction. + // This is used to determine when analysis has reached a fixpoint. + enum class VisitResult { + // The analysis result for this instruction has changed. + // This means that any instructions that depend on it (its successors) must + // be recomputed. + kResultChanged, + // The analysis result for this instruction has not changed. + // When all visit operations return |kResultFixed|, the analysis has reached + // a fixpoint (converged). + kResultFixed, + }; + + virtual ~DataFlowAnalysis() {} + + // Run this analysis on a given function. + // For analyses which work interprocedurally, |function| may be ignored. + void Run(Function* function); + + protected: + DataFlowAnalysis(IRContext& context) : context_(context) {} + + // Initialize the worklist for a given function. + // |is_first_iteration| is true on the first call to |Run| and false + // afterwards. All subsequent runs are only necessary to check if the analysis + // has converged; if |EnqueueSuccessors| is complete, |InitializeWorklist| + // should do nothing after the first iteration. + virtual void InitializeWorklist(Function* function, + bool is_first_iteration) = 0; + + // Enqueues the successors (instructions which use the analysis result) of + // |inst|. This is not required to be complete, but convergence is faster when + // it is. This is called whenever |Visit| returns |kResultChanged|. + virtual void EnqueueSuccessors(Instruction* inst) = 0; + + // Visits the given instruction, recomputing the analysis result. This is + // called once per instruction queued in |InitializeWorklist| and afterward + // when a predecessor is changed, through |EnqueueSuccessors|. + virtual VisitResult Visit(Instruction* inst) = 0; + + // Enqueues the given instruction to be visited. Ignored if already in the + // worklist. + bool Enqueue(Instruction* inst); + + IRContext& context() { return context_; } + + private: + // Runs one pass, calling |InitializeWorklist| and then iterating through the + // worklist until all fixed. + VisitResult RunOnce(Function* function, bool is_first_iteration); + + IRContext& context_; + std::unordered_map on_worklist_; + // The worklist, which contains the list of instructions to be visited. + // + // The choice of data structure was influenced by the data in "Iterative + // Data-flow Analysis, Revisited" (Cooper et al, 2002). + // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.125.1549&rep=rep1&type=pdf + // The paper shows that the overall performance benefit of a priority queue + // over a regular queue or stack is relatively small (or negative). + // + // A queue has the advantage that nodes are visited in the same order they are + // enqueued, which relieves the analysis from inserting nodes "backwards", for + // example in worklist initialization. Also, as the paper claims that sorting + // successors does not improve runtime, we can use a single queue which is + // modified during iteration. + std::queue worklist_; +}; + +// A generic data flow analysis, specialized for forward analysis. +class ForwardDataFlowAnalysis : public DataFlowAnalysis { + public: + // Indicates where labels should be in the worklist RPO ordering. + enum class LabelPosition { + // Labels should be placed at the beginning of their blocks. + kLabelsAtBeginning, + // Labels should be placed at the end of their blocks. + kLabelsAtEnd, + // Labels should not be in the worklist. + kNoLabels, + // Only labels should be placed in the worklist. + kLabelsOnly, + }; + + ForwardDataFlowAnalysis(IRContext& context, LabelPosition label_position) + : DataFlowAnalysis(context), label_position_(label_position) {} + + protected: + // Initializes the worklist in reverse postorder, regardless of + // |is_first_iteration|. Labels are placed according to the label position + // specified in the constructor. + void InitializeWorklist(Function* function, bool is_first_iteration) override; + + // Enqueues the users and block successors of the given instruction. + // See |EnqueueUsers| and |EnqueueBlockSuccessors|. + void EnqueueSuccessors(Instruction* inst) override { + EnqueueUsers(inst); + EnqueueBlockSuccessors(inst); + } + + // Enqueues the users of the given instruction. + void EnqueueUsers(Instruction* inst); + + // Enqueues the labels of the successors of the block corresponding to the + // given label instruction. Does nothing for other instructions. + void EnqueueBlockSuccessors(Instruction* inst); + + private: + LabelPosition label_position_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DATAFLOW_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.cpp new file mode 100644 index 00000000..1526b9e0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.cpp @@ -0,0 +1,651 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_branch_elim_pass.h" + +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/ir_context.h" +#include "source/opt/struct_cfg_analysis.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kBranchCondTrueLabIdInIdx = 1; +constexpr uint32_t kBranchCondFalseLabIdInIdx = 2; +} // namespace + +bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) { + bool condIsConst; + Instruction* cInst = get_def_use_mgr()->GetDef(condId); + switch (cInst->opcode()) { + case spv::Op::OpConstantNull: + case spv::Op::OpConstantFalse: { + *condVal = false; + condIsConst = true; + } break; + case spv::Op::OpConstantTrue: { + *condVal = true; + condIsConst = true; + } break; + case spv::Op::OpLogicalNot: { + bool negVal; + condIsConst = + GetConstCondition(cInst->GetSingleWordInOperand(0), &negVal); + if (condIsConst) *condVal = !negVal; + } break; + default: { condIsConst = false; } break; + } + return condIsConst; +} + +bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) { + Instruction* sInst = get_def_use_mgr()->GetDef(selId); + uint32_t typeId = sInst->type_id(); + Instruction* typeInst = get_def_use_mgr()->GetDef(typeId); + if (!typeInst || (typeInst->opcode() != spv::Op::OpTypeInt)) return false; + // TODO(greg-lunarg): Support non-32 bit ints + if (typeInst->GetSingleWordInOperand(0) != 32) return false; + if (sInst->opcode() == spv::Op::OpConstant) { + *selVal = sInst->GetSingleWordInOperand(0); + return true; + } else if (sInst->opcode() == spv::Op::OpConstantNull) { + *selVal = 0; + return true; + } + return false; +} + +void DeadBranchElimPass::AddBranch(uint32_t labelId, BasicBlock* bp) { + assert(get_def_use_mgr()->GetDef(labelId) != nullptr); + std::unique_ptr newBranch( + new Instruction(context(), spv::Op::OpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}})); + context()->AnalyzeDefUse(&*newBranch); + context()->set_instr_block(&*newBranch, bp); + bp->AddInstruction(std::move(newBranch)); +} + +BasicBlock* DeadBranchElimPass::GetParentBlock(uint32_t id) { + return context()->get_instr_block(get_def_use_mgr()->GetDef(id)); +} + +bool DeadBranchElimPass::MarkLiveBlocks( + Function* func, std::unordered_set* live_blocks) { + std::vector> conditions_to_simplify; + std::unordered_set blocks_with_backedge; + std::vector stack; + stack.push_back(&*func->begin()); + bool modified = false; + while (!stack.empty()) { + BasicBlock* block = stack.back(); + stack.pop_back(); + + // Live blocks doubles as visited set. + if (!live_blocks->insert(block).second) continue; + + uint32_t cont_id = block->ContinueBlockIdIfAny(); + if (cont_id != 0) { + AddBlocksWithBackEdge(cont_id, block->id(), block->MergeBlockIdIfAny(), + &blocks_with_backedge); + } + + Instruction* terminator = block->terminator(); + uint32_t live_lab_id = 0; + // Check if the terminator has a single valid successor. + if (terminator->opcode() == spv::Op::OpBranchConditional) { + bool condVal; + if (GetConstCondition(terminator->GetSingleWordInOperand(0u), &condVal)) { + live_lab_id = terminator->GetSingleWordInOperand( + condVal ? kBranchCondTrueLabIdInIdx : kBranchCondFalseLabIdInIdx); + } + } else if (terminator->opcode() == spv::Op::OpSwitch) { + uint32_t sel_val; + if (GetConstInteger(terminator->GetSingleWordInOperand(0u), &sel_val)) { + // Search switch operands for selector value, set live_lab_id to + // corresponding label, use default if not found. + uint32_t icnt = 0; + uint32_t case_val; + terminator->WhileEachInOperand( + [&icnt, &case_val, &sel_val, &live_lab_id](const uint32_t* idp) { + if (icnt == 1) { + // Start with default label. + live_lab_id = *idp; + } else if (icnt > 1) { + if (icnt % 2 == 0) { + case_val = *idp; + } else { + if (case_val == sel_val) { + live_lab_id = *idp; + return false; + } + } + } + ++icnt; + return true; + }); + } + } + + // Don't simplify back edges unless it becomes a branch to the header. Every + // loop must have exactly one back edge to the loop header, so we cannot + // remove it. + bool simplify = false; + if (live_lab_id != 0) { + if (!blocks_with_backedge.count(block)) { + // This is not a back edge. + simplify = true; + } else { + const auto& struct_cfg_analysis = context()->GetStructuredCFGAnalysis(); + uint32_t header_id = struct_cfg_analysis->ContainingLoop(block->id()); + if (live_lab_id == header_id) { + // The new branch will be a branch to the header. + simplify = true; + } + } + } + + if (simplify) { + conditions_to_simplify.push_back({block, live_lab_id}); + stack.push_back(GetParentBlock(live_lab_id)); + } else { + // All successors are live. + const auto* const_block = block; + const_block->ForEachSuccessorLabel([&stack, this](const uint32_t label) { + stack.push_back(GetParentBlock(label)); + }); + } + } + + // Traverse |conditions_to_simplify| in reverse order. This is done so that + // we simplify nested constructs before simplifying the constructs that + // contain them. + for (auto b = conditions_to_simplify.rbegin(); + b != conditions_to_simplify.rend(); ++b) { + modified |= SimplifyBranch(b->first, b->second); + } + + return modified; +} + +bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block, + uint32_t live_lab_id) { + Instruction* merge_inst = block->GetMergeInst(); + Instruction* terminator = block->terminator(); + if (merge_inst && merge_inst->opcode() == spv::Op::OpSelectionMerge) { + if (merge_inst->NextNode()->opcode() == spv::Op::OpSwitch && + SwitchHasNestedBreak(block->id())) { + if (terminator->NumInOperands() == 2) { + // We cannot remove the branch, and it already has a single case, so no + // work to do. + return false; + } + // We have to keep the switch because it has a nest break, so we + // remove all cases except for the live one. + Instruction::OperandList new_operands; + new_operands.push_back(terminator->GetInOperand(0)); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}}); + terminator->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(terminator); + } else { + // Check if the merge instruction is still needed because of a + // non-nested break from the construct. Move the merge instruction if + // it is still needed. + StructuredCFGAnalysis* cfg_analysis = + context()->GetStructuredCFGAnalysis(); + Instruction* first_break = FindFirstExitFromSelectionMerge( + live_lab_id, merge_inst->GetSingleWordInOperand(0), + cfg_analysis->LoopMergeBlock(live_lab_id), + cfg_analysis->LoopContinueBlock(live_lab_id), + cfg_analysis->SwitchMergeBlock(live_lab_id)); + + AddBranch(live_lab_id, block); + context()->KillInst(terminator); + if (first_break == nullptr) { + context()->KillInst(merge_inst); + } else { + merge_inst->RemoveFromList(); + first_break->InsertBefore(std::unique_ptr(merge_inst)); + context()->set_instr_block(merge_inst, + context()->get_instr_block(first_break)); + } + } + } else { + AddBranch(live_lab_id, block); + context()->KillInst(terminator); + } + return true; +} + +void DeadBranchElimPass::MarkUnreachableStructuredTargets( + const std::unordered_set& live_blocks, + std::unordered_set* unreachable_merges, + std::unordered_map* unreachable_continues) { + for (auto block : live_blocks) { + if (auto merge_id = block->MergeBlockIdIfAny()) { + BasicBlock* merge_block = GetParentBlock(merge_id); + if (!live_blocks.count(merge_block)) { + unreachable_merges->insert(merge_block); + } + if (auto cont_id = block->ContinueBlockIdIfAny()) { + BasicBlock* cont_block = GetParentBlock(cont_id); + if (!live_blocks.count(cont_block)) { + (*unreachable_continues)[cont_block] = block; + } + } + } + } +} + +bool DeadBranchElimPass::FixPhiNodesInLiveBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_map& unreachable_continues) { + bool modified = false; + for (auto& block : *func) { + if (live_blocks.count(&block)) { + for (auto iter = block.begin(); iter != block.end();) { + if (iter->opcode() != spv::Op::OpPhi) { + break; + } + + bool changed = false; + bool backedge_added = false; + Instruction* inst = &*iter; + std::vector operands; + // Build a complete set of operands (not just input operands). Start + // with type and result id operands. + operands.push_back(inst->GetOperand(0u)); + operands.push_back(inst->GetOperand(1u)); + // Iterate through the incoming labels and determine which to keep + // and/or modify. If there in an unreachable continue block, there will + // be an edge from that block to the header. We need to keep it to + // maintain the structured control flow. If the header has more that 2 + // incoming edges, then the OpPhi must have an entry for that edge. + // However, if there is only one other incoming edge, the OpPhi can be + // eliminated. + for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { + BasicBlock* inc = GetParentBlock(inst->GetSingleWordInOperand(i)); + auto cont_iter = unreachable_continues.find(inc); + if (cont_iter != unreachable_continues.end() && + cont_iter->second == &block && inst->NumInOperands() > 4) { + if (get_def_use_mgr() + ->GetDef(inst->GetSingleWordInOperand(i - 1)) + ->opcode() == spv::Op::OpUndef) { + // Already undef incoming value, no change necessary. + operands.push_back(inst->GetInOperand(i - 1)); + operands.push_back(inst->GetInOperand(i)); + backedge_added = true; + } else { + // Replace incoming value with undef if this phi exists in the + // loop header. Otherwise, this edge is not live since the + // unreachable continue block will be replaced with an + // unconditional branch to the header only. + operands.emplace_back( + SPV_OPERAND_TYPE_ID, + std::initializer_list{Type2Undef(inst->type_id())}); + operands.push_back(inst->GetInOperand(i)); + changed = true; + backedge_added = true; + } + } else if (live_blocks.count(inc) && inc->IsSuccessor(&block)) { + // Keep live incoming edge. + operands.push_back(inst->GetInOperand(i - 1)); + operands.push_back(inst->GetInOperand(i)); + } else { + // Remove incoming edge. + changed = true; + } + } + + if (changed) { + modified = true; + uint32_t continue_id = block.ContinueBlockIdIfAny(); + if (!backedge_added && continue_id != 0 && + unreachable_continues.count(GetParentBlock(continue_id)) && + operands.size() > 4) { + // Changed the backedge to branch from the continue block instead + // of a successor of the continue block. Add an entry to the phi to + // provide an undef for the continue block. Since the successor of + // the continue must also be unreachable (dominated by the continue + // block), any entry for the original backedge has been removed + // from the phi operands. + operands.emplace_back( + SPV_OPERAND_TYPE_ID, + std::initializer_list{Type2Undef(inst->type_id())}); + operands.emplace_back(SPV_OPERAND_TYPE_ID, + std::initializer_list{continue_id}); + } + + // Either replace the phi with a single value or rebuild the phi out + // of |operands|. + // + // We always have type and result id operands. So this phi has a + // single source if there are two more operands beyond those. + if (operands.size() == 4) { + // First input data operands is at index 2. + uint32_t replId = operands[2u].words[0]; + context()->KillNamesAndDecorates(inst->result_id()); + context()->ReplaceAllUsesWith(inst->result_id(), replId); + iter = context()->KillInst(&*inst); + } else { + // We've rewritten the operands, so first instruct the def/use + // manager to forget uses in the phi before we replace them. After + // replacing operands update the def/use manager by re-analyzing + // the used ids in this phi. + get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst); + inst->ReplaceOperands(operands); + get_def_use_mgr()->AnalyzeInstUse(inst); + ++iter; + } + } else { + ++iter; + } + } + } + } + + return modified; +} + +bool DeadBranchElimPass::EraseDeadBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_set& unreachable_merges, + const std::unordered_map& unreachable_continues) { + bool modified = false; + for (auto ebi = func->begin(); ebi != func->end();) { + if (unreachable_continues.count(&*ebi)) { + uint32_t cont_id = unreachable_continues.find(&*ebi)->second->id(); + if (ebi->begin() != ebi->tail() || + ebi->terminator()->opcode() != spv::Op::OpBranch || + ebi->terminator()->GetSingleWordInOperand(0u) != cont_id) { + // Make unreachable, but leave the label. + KillAllInsts(&*ebi, false); + // Add unconditional branch to header. + assert(unreachable_continues.count(&*ebi)); + ebi->AddInstruction(MakeUnique( + context(), spv::Op::OpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cont_id}}})); + get_def_use_mgr()->AnalyzeInstUse(&*ebi->tail()); + context()->set_instr_block(&*ebi->tail(), &*ebi); + modified = true; + } + ++ebi; + } else if (unreachable_merges.count(&*ebi)) { + if (ebi->begin() != ebi->tail() || + ebi->terminator()->opcode() != spv::Op::OpUnreachable) { + // Make unreachable, but leave the label. + KillAllInsts(&*ebi, false); + // Add unreachable terminator. + ebi->AddInstruction( + MakeUnique(context(), spv::Op::OpUnreachable, 0, 0, + std::initializer_list{})); + context()->AnalyzeUses(ebi->terminator()); + context()->set_instr_block(ebi->terminator(), &*ebi); + modified = true; + } + ++ebi; + } else if (!live_blocks.count(&*ebi)) { + // Kill this block. + KillAllInsts(&*ebi); + ebi = ebi.Erase(); + modified = true; + } else { + ++ebi; + } + } + + return modified; +} + +bool DeadBranchElimPass::EliminateDeadBranches(Function* func) { + if (func->IsDeclaration()) { + return false; + } + + bool modified = false; + std::unordered_set live_blocks; + modified |= MarkLiveBlocks(func, &live_blocks); + + std::unordered_set unreachable_merges; + std::unordered_map unreachable_continues; + MarkUnreachableStructuredTargets(live_blocks, &unreachable_merges, + &unreachable_continues); + modified |= FixPhiNodesInLiveBlocks(func, live_blocks, unreachable_continues); + modified |= EraseDeadBlocks(func, live_blocks, unreachable_merges, + unreachable_continues); + + return modified; +} + +void DeadBranchElimPass::FixBlockOrder() { + context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis); + // Reorders blocks according to DFS of dominator tree. + ProcessFunction reorder_dominators = [this](Function* function) { + DominatorAnalysis* dominators = context()->GetDominatorAnalysis(function); + std::vector blocks; + for (auto iter = dominators->GetDomTree().begin(); + iter != dominators->GetDomTree().end(); ++iter) { + if (iter->id() != 0) { + blocks.push_back(iter->bb_); + } + } + for (uint32_t i = 1; i < blocks.size(); ++i) { + function->MoveBasicBlockToAfter(blocks[i]->id(), blocks[i - 1]); + } + return true; + }; + + // Reorders blocks according to structured order. + ProcessFunction reorder_structured = [](Function* function) { + function->ReorderBasicBlocksInStructuredOrder(); + return true; + }; + + // Structured order is more intuitive so use it where possible. + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) { + context()->ProcessReachableCallTree(reorder_structured); + } else { + context()->ProcessReachableCallTree(reorder_dominators); + } +} + +Pass::Status DeadBranchElimPass::Process() { + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == spv::Op::OpGroupDecorate) + return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return EliminateDeadBranches(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + if (modified) FixBlockOrder(); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Instruction* DeadBranchElimPass::FindFirstExitFromSelectionMerge( + uint32_t start_block_id, uint32_t merge_block_id, uint32_t loop_merge_id, + uint32_t loop_continue_id, uint32_t switch_merge_id) { + // To find the "first" exit, we follow branches looking for a conditional + // branch that is not in a nested construct and is not the header of a new + // construct. We follow the control flow from |start_block_id| to find the + // first one. + + while (start_block_id != merge_block_id && start_block_id != loop_merge_id && + start_block_id != loop_continue_id) { + BasicBlock* start_block = context()->get_instr_block(start_block_id); + Instruction* branch = start_block->terminator(); + uint32_t next_block_id = 0; + switch (branch->opcode()) { + case spv::Op::OpBranchConditional: + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + // If a possible target is the |loop_merge_id| or |loop_continue_id|, + // which are not the current merge node, then we continue the search + // with the other target. + for (uint32_t i = 1; i < 3; i++) { + if (branch->GetSingleWordInOperand(i) == loop_merge_id && + loop_merge_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + if (branch->GetSingleWordInOperand(i) == loop_continue_id && + loop_continue_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + if (branch->GetSingleWordInOperand(i) == switch_merge_id && + switch_merge_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + } + + if (next_block_id == 0) { + return branch; + } + } + break; + case spv::Op::OpSwitch: + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + // A switch with no merge instructions can have at most 5 targets: + // a. |merge_block_id| + // b. |loop_merge_id| + // c. |loop_continue_id| + // d. |switch_merge_id| + // e. 1 block inside the current region. + // + // Note that because this is a switch, |merge_block_id| must equal + // |switch_merge_id|. + // + // This leads to a number of cases of what to do. + // + // 1. Does not jump to a block inside of the current construct. In + // this case, there is not conditional break, so we should return + // |nullptr|. + // + // 2. Jumps to |merge_block_id| and a block inside the current + // construct. In this case, this branch conditionally break to the + // end of the current construct, so return the current branch. + // + // 3. Otherwise, this branch may break, but not to the current merge + // block. So we continue with the block that is inside the loop. + bool found_break = false; + for (uint32_t i = 1; i < branch->NumInOperands(); i += 2) { + uint32_t target = branch->GetSingleWordInOperand(i); + if (target == merge_block_id) { + found_break = true; + } else if (target != loop_merge_id && target != loop_continue_id) { + next_block_id = branch->GetSingleWordInOperand(i); + } + } + + if (next_block_id == 0) { + // Case 1. + return nullptr; + } + + if (found_break) { + // Case 2. + return branch; + } + + // The fall through is case 3. + } + break; + case spv::Op::OpBranch: + // Need to check if this is the header of a loop nested in the + // selection construct. + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + next_block_id = branch->GetSingleWordInOperand(0); + } + break; + default: + return nullptr; + } + start_block_id = next_block_id; + } + return nullptr; +} + +void DeadBranchElimPass::AddBlocksWithBackEdge( + uint32_t cont_id, uint32_t header_id, uint32_t merge_id, + std::unordered_set* blocks_with_back_edges) { + std::unordered_set visited; + visited.insert(cont_id); + visited.insert(header_id); + visited.insert(merge_id); + + std::vector work_list; + work_list.push_back(cont_id); + + while (!work_list.empty()) { + uint32_t bb_id = work_list.back(); + work_list.pop_back(); + + BasicBlock* bb = context()->get_instr_block(bb_id); + + bool has_back_edge = false; + bb->ForEachSuccessorLabel([header_id, &visited, &work_list, + &has_back_edge](uint32_t* succ_label_id) { + if (visited.insert(*succ_label_id).second) { + work_list.push_back(*succ_label_id); + } + if (*succ_label_id == header_id) { + has_back_edge = true; + } + }); + + if (has_back_edge) { + blocks_with_back_edges->insert(bb); + } + } +} + +bool DeadBranchElimPass::SwitchHasNestedBreak(uint32_t switch_header_id) { + std::vector block_in_construct; + BasicBlock* start_block = context()->get_instr_block(switch_header_id); + uint32_t merge_block_id = start_block->MergeBlockIdIfAny(); + + StructuredCFGAnalysis* cfg_analysis = context()->GetStructuredCFGAnalysis(); + return !get_def_use_mgr()->WhileEachUser( + merge_block_id, + [this, cfg_analysis, switch_header_id](Instruction* inst) { + if (!inst->IsBranch()) { + return true; + } + + BasicBlock* bb = context()->get_instr_block(inst); + if (bb->id() == switch_header_id) { + return true; + } + return (cfg_analysis->ContainingConstruct(inst) == switch_header_id && + bb->GetMergeInst() == nullptr); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.h new file mode 100644 index 00000000..198bad2d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_branch_elim_pass.h @@ -0,0 +1,176 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ +#define SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class DeadBranchElimPass : public MemPass { + using cbb_ptr = const BasicBlock*; + + public: + DeadBranchElimPass() = default; + + const char* name() const override { return "eliminate-dead-branches"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // If |condId| is boolean constant, return conditional value in |condVal| and + // return true, otherwise return false. + bool GetConstCondition(uint32_t condId, bool* condVal); + + // If |valId| is a 32-bit integer constant, return value via |value| and + // return true, otherwise return false. + bool GetConstInteger(uint32_t valId, uint32_t* value); + + // Add branch to |labelId| to end of block |bp|. + void AddBranch(uint32_t labelId, BasicBlock* bp); + + // For function |func|, look for BranchConditionals with constant condition + // and convert to a Branch to the indicated label. Delete resulting dead + // blocks. Note some such branches and blocks may be left to avoid creating + // invalid control flow. + // TODO(greg-lunarg): Remove remaining constant conditional branches and dead + // blocks. + bool EliminateDeadBranches(Function* func); + + // Returns the basic block containing |id|. + // Note: this pass only requires correct instruction block mappings for the + // input. This pass does not preserve the block mapping, so it is not kept + // up-to-date during processing. + BasicBlock* GetParentBlock(uint32_t id); + + // Marks live blocks reachable from the entry of |func|. Simplifies constant + // branches and switches as it proceeds, to limit the number of live blocks. + // It is careful not to eliminate backedges even if they are dead, but the + // header is live. Likewise, unreachable merge blocks named in live merge + // instruction must be retained (though they may be clobbered). + bool MarkLiveBlocks(Function* func, + std::unordered_set* live_blocks); + + // Checks for unreachable merge and continue blocks with live headers; those + // blocks must be retained. Continues are tracked separately so that a live + // phi can be updated to take an undef value from any of its predecessors + // that are unreachable continues. + // + // |unreachable_continues| maps the id of an unreachable continue target to + // the header block that declares it. + void MarkUnreachableStructuredTargets( + const std::unordered_set& live_blocks, + std::unordered_set* unreachable_merges, + std::unordered_map* unreachable_continues); + + // Fix phis in reachable blocks so that only live (or unremovable) incoming + // edges are present. If the block now only has a single live incoming edge, + // remove the phi and replace its uses with its data input. If the single + // remaining incoming edge is from the phi itself, the phi is in an + // unreachable single block loop. Either the block is dead and will be + // removed, or it's reachable from an unreachable continue target. In the + // latter case that continue target block will be collapsed into a block that + // only branches back to its header and we'll eliminate the block with the + // phi. + // + // |unreachable_continues| maps continue targets that cannot be reached to + // merge instruction that declares them. + bool FixPhiNodesInLiveBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_map& + unreachable_continues); + + // Erases dead blocks. Any block captured in |unreachable_merges| or + // |unreachable_continues| is a dead block that is required to remain due to + // a live merge instruction in the corresponding header. These blocks will + // have their instructions clobbered and will become a label and terminator. + // Unreachable merge blocks are terminated by OpUnreachable, while + // unreachable continue blocks are terminated by an unconditional branch to + // the header. Otherwise, blocks are dead if not explicitly captured in + // |live_blocks| and are totally removed. + // + // |unreachable_continues| maps continue targets that cannot be reached to + // corresponding header block that declares them. + bool EraseDeadBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_set& unreachable_merges, + const std::unordered_map& + unreachable_continues); + + // Reorders blocks in reachable functions so that they satisfy dominator + // block ordering rules. + void FixBlockOrder(); + + // Return the first branch instruction that is a conditional branch to + // |merge_block_id|. Returns |nullptr| if no such branch exists. If there are + // multiple such branches, the first one is the one that would be executed + // first when running the code. That is, the one that dominates all of the + // others. + // + // |start_block_id| must be a block whose innermost containing merge construct + // has |merge_block_id| as the merge block. + // + // |loop_merge_id| and |loop_continue_id| are the merge and continue block ids + // of the innermost loop containing |start_block_id|. + Instruction* FindFirstExitFromSelectionMerge(uint32_t start_block_id, + uint32_t merge_block_id, + uint32_t loop_merge_id, + uint32_t loop_continue_id, + uint32_t switch_merge_id); + + // Adds to |blocks_with_back_edges| all of the blocks on the path from the + // basic block |cont_id| to |header_id| and |merge_id|. The intention is that + // |cond_id| is a the continue target of a loop, |header_id| is the header of + // the loop, and |merge_id| is the merge block of the loop. + void AddBlocksWithBackEdge( + uint32_t cont_id, uint32_t header_id, uint32_t merge_id, + std::unordered_set* blocks_with_back_edges); + + // Returns true if there is a branch to the merge node of the selection + // construct |switch_header_id| that is inside a nested selection construct or + // in the header of the nested selection construct. + bool SwitchHasNestedBreak(uint32_t switch_header_id); + + // Return true of the terminator of |block| is successfully replaced with a + // branch to |live_lab_id|. The merge instruction is deleted or moved as + // needed to maintain structured control flow. Assumes that the + // StructuredCFGAnalysis is valid for the constructs containing |block|. + bool SimplifyBranch(BasicBlock* block, uint32_t live_lab_id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.cpp new file mode 100644 index 00000000..f985e4c2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_insert_elim_pass.h" + +#include "source/opt/composite.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "spirv/1.2/GLSL.std.450.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kTypeVectorCountInIdx = 1; +constexpr uint32_t kTypeMatrixCountInIdx = 1; +constexpr uint32_t kTypeArrayLengthIdInIdx = 1; +constexpr uint32_t kTypeIntWidthInIdx = 0; +constexpr uint32_t kConstantValueInIdx = 0; +constexpr uint32_t kInsertObjectIdInIdx = 0; +constexpr uint32_t kInsertCompositeIdInIdx = 1; +} // namespace + +uint32_t DeadInsertElimPass::NumComponents(Instruction* typeInst) { + switch (typeInst->opcode()) { + case spv::Op::OpTypeVector: { + return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx); + } break; + case spv::Op::OpTypeMatrix: { + return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx); + } break; + case spv::Op::OpTypeArray: { + uint32_t lenId = + typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx); + Instruction* lenInst = get_def_use_mgr()->GetDef(lenId); + if (lenInst->opcode() != spv::Op::OpConstant) return 0; + uint32_t lenTypeId = lenInst->type_id(); + Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId); + // TODO(greg-lunarg): Support non-32-bit array length + if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32) + return 0; + return lenInst->GetSingleWordInOperand(kConstantValueInIdx); + } break; + case spv::Op::OpTypeStruct: { + return typeInst->NumInOperands(); + } break; + default: { return 0; } break; + } +} + +void DeadInsertElimPass::MarkInsertChain( + Instruction* insertChain, std::vector* pExtIndices, + uint32_t extOffset, std::unordered_set* visited_phis) { + // Not currently optimizing array inserts. + Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id()); + if (typeInst->opcode() == spv::Op::OpTypeArray) return; + // Insert chains are only composed of inserts and phis + if (insertChain->opcode() != spv::Op::OpCompositeInsert && + insertChain->opcode() != spv::Op::OpPhi) + return; + // If extract indices are empty, mark all subcomponents if type + // is constant length. + if (pExtIndices == nullptr) { + uint32_t cnum = NumComponents(typeInst); + if (cnum > 0) { + std::vector extIndices; + for (uint32_t i = 0; i < cnum; i++) { + extIndices.clear(); + extIndices.push_back(i); + std::unordered_set sub_visited_phis; + MarkInsertChain(insertChain, &extIndices, 0, &sub_visited_phis); + } + return; + } + } + Instruction* insInst = insertChain; + while (insInst->opcode() == spv::Op::OpCompositeInsert) { + // If no extract indices, mark insert and inserted object (which might + // also be an insert chain) and continue up the chain though the input + // composite. + // + // Note: We mark inserted objects in this function (rather than in + // EliminateDeadInsertsOnePass) because in some cases, we can do it + // more accurately here. + if (pExtIndices == nullptr) { + liveInserts_.insert(insInst->result_id()); + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + // If extract indices match insert, we are done. Mark insert and + // inserted object. + } else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) { + liveInserts_.insert(insInst->result_id()); + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + break; + // If non-matching intersection, mark insert + } else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) { + liveInserts_.insert(insInst->result_id()); + // If more extract indices than insert, we are done. Use remaining + // extract indices to mark inserted object. + uint32_t numInsertIndices = insInst->NumInOperands() - 2; + if (pExtIndices->size() - extOffset > numInsertIndices) { + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices, + extOffset + numInsertIndices, &obj_visited_phis); + break; + // If fewer extract indices than insert, also mark inserted object and + // continue up chain. + } else { + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + } + } + // Get next insert in chain + const uint32_t compId = + insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx); + insInst = get_def_use_mgr()->GetDef(compId); + } + // If insert chain ended with phi, do recursive call on each operand + if (insInst->opcode() != spv::Op::OpPhi) return; + // Mark phi visited to prevent potential infinite loop. If phi is already + // visited, return to avoid infinite loop. + if (visited_phis->count(insInst->result_id()) != 0) return; + visited_phis->insert(insInst->result_id()); + + // Phis may have duplicate inputs values for different edges, prune incoming + // ids lists before recursing. + std::vector ids; + for (uint32_t i = 0; i < insInst->NumInOperands(); i += 2) { + ids.push_back(insInst->GetSingleWordInOperand(i)); + } + std::sort(ids.begin(), ids.end()); + auto new_end = std::unique(ids.begin(), ids.end()); + for (auto id_iter = ids.begin(); id_iter != new_end; ++id_iter) { + Instruction* pi = get_def_use_mgr()->GetDef(*id_iter); + MarkInsertChain(pi, pExtIndices, extOffset, visited_phis); + } +} + +bool DeadInsertElimPass::EliminateDeadInserts(Function* func) { + bool modified = false; + bool lastmodified = true; + // Each pass can delete dead instructions, thus potentially revealing + // new dead insertions ie insertions with no uses. + while (lastmodified) { + lastmodified = EliminateDeadInsertsOnePass(func); + modified |= lastmodified; + } + return modified; +} + +bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) { + bool modified = false; + liveInserts_.clear(); + visitedPhis_.clear(); + // Mark all live inserts + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + // Only process Inserts and composite Phis + spv::Op op = ii->opcode(); + Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id()); + if (op != spv::Op::OpCompositeInsert && + (op != spv::Op::OpPhi || !spvOpcodeIsComposite(typeInst->opcode()))) + continue; + // The marking algorithm can be expensive for large arrays and the + // efficacy of eliminating dead inserts into arrays is questionable. + // Skip optimizing array inserts for now. Just mark them live. + // TODO(greg-lunarg): Eliminate dead array inserts + if (op == spv::Op::OpCompositeInsert) { + if (typeInst->opcode() == spv::Op::OpTypeArray) { + liveInserts_.insert(ii->result_id()); + continue; + } + } + const uint32_t id = ii->result_id(); + get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) { + if (user->IsCommonDebugInstr()) return; + switch (user->opcode()) { + case spv::Op::OpCompositeInsert: + case spv::Op::OpPhi: + // Use by insert or phi does not initiate marking + break; + case spv::Op::OpCompositeExtract: { + // Capture extract indices + std::vector extIndices; + uint32_t icnt = 0; + user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) { + if (icnt > 0) extIndices.push_back(*idp); + ++icnt; + }); + // Mark all inserts in chain that intersect with extract + std::unordered_set visited_phis; + MarkInsertChain(&*ii, &extIndices, 0, &visited_phis); + } break; + default: { + // Mark inserts in chain for all components + std::unordered_set visited_phis; + MarkInsertChain(&*ii, nullptr, 0, &visited_phis); + } break; + } + }); + } + } + // Find and disconnect dead inserts + std::vector dead_instructions; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + if (ii->opcode() != spv::Op::OpCompositeInsert) continue; + const uint32_t id = ii->result_id(); + if (liveInserts_.find(id) != liveInserts_.end()) continue; + const uint32_t replId = + ii->GetSingleWordInOperand(kInsertCompositeIdInIdx); + (void)context()->ReplaceAllUsesWith(id, replId); + dead_instructions.push_back(&*ii); + modified = true; + } + } + // DCE dead inserts + while (!dead_instructions.empty()) { + Instruction* inst = dead_instructions.back(); + dead_instructions.pop_back(); + DCEInst(inst, [&dead_instructions](Instruction* other_inst) { + auto i = std::find(dead_instructions.begin(), dead_instructions.end(), + other_inst); + if (i != dead_instructions.end()) { + dead_instructions.erase(i); + } + }); + } + return modified; +} + +Pass::Status DeadInsertElimPass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { + return EliminateDeadInserts(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.h new file mode 100644 index 00000000..01f12bb0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_insert_elim_pass.h @@ -0,0 +1,90 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ +#define SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class DeadInsertElimPass : public MemPass { + public: + DeadInsertElimPass() = default; + + const char* name() const override { return "eliminate-dead-inserts"; } + Status Process() override; + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Return the number of subcomponents in the composite type |typeId|. + // Return 0 if not a composite type or number of components is not a + // 32-bit constant. + uint32_t NumComponents(Instruction* typeInst); + + // Mark all inserts in instruction chain ending at |insertChain| with + // indices that intersect with extract indices |extIndices| starting with + // index at |extOffset|. Chains are composed solely of Inserts and Phis. + // Mark all inserts in chain if |extIndices| is nullptr. + void MarkInsertChain(Instruction* insertChain, + std::vector* extIndices, uint32_t extOffset, + std::unordered_set* visited_phis); + + // Perform EliminateDeadInsertsOnePass(|func|) until no modification is + // made. Return true if modified. + bool EliminateDeadInserts(Function* func); + + // DCE all dead struct, matrix and vector inserts in |func|. An insert is + // dead if the value it inserts is never used. Replace any reference to the + // insert with its original composite. Return true if modified. Dead inserts + // in dependence cycles are not currently eliminated. Dead inserts into + // arrays are not currently eliminated. + bool EliminateDeadInsertsOnePass(Function* func); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + // Live inserts + std::unordered_set liveInserts_; + + // Visited phis as insert chain is traversed; used to avoid infinite loop + std::unordered_map visitedPhis_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.cpp new file mode 100644 index 00000000..e39132c2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_variable_elimination.h" + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +// This optimization removes global variables that are not needed because they +// are definitely not accessed. +Pass::Status DeadVariableElimination::Process() { + // The algorithm will compute the reference count for every global variable. + // Anything with a reference count of 0 will then be deleted. For variables + // that might have references that are not explicit in this context, we use + // the value kMustKeep as the reference count. + std::vector ids_to_remove; + + // Get the reference count for all of the global OpVariable instructions. + for (auto& inst : context()->types_values()) { + if (inst.opcode() != spv::Op::OpVariable) { + continue; + } + + size_t count = 0; + uint32_t result_id = inst.result_id(); + + // Check the linkage. If it is exported, it could be reference somewhere + // else, so we must keep the variable around. + get_decoration_mgr()->ForEachDecoration( + result_id, uint32_t(spv::Decoration::LinkageAttributes), + [&count](const Instruction& linkage_instruction) { + uint32_t last_operand = linkage_instruction.NumOperands() - 1; + if (spv::LinkageType(linkage_instruction.GetSingleWordOperand( + last_operand)) == spv::LinkageType::Export) { + count = kMustKeep; + } + }); + + if (count != kMustKeep) { + // If we don't have to keep the instruction for other reasons, then look + // at the uses and count the number of real references. + count = 0; + get_def_use_mgr()->ForEachUser(result_id, [&count](Instruction* user) { + if (!IsAnnotationInst(user->opcode()) && + user->opcode() != spv::Op::OpName) { + ++count; + } + }); + } + reference_count_[result_id] = count; + if (count == 0) { + ids_to_remove.push_back(result_id); + } + } + + // Remove all of the variables that have a reference count of 0. + bool modified = false; + if (!ids_to_remove.empty()) { + modified = true; + for (auto result_id : ids_to_remove) { + DeleteVariable(result_id); + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void DeadVariableElimination::DeleteVariable(uint32_t result_id) { + Instruction* inst = get_def_use_mgr()->GetDef(result_id); + assert(inst->opcode() == spv::Op::OpVariable && + "Should not be trying to delete anything other than an OpVariable."); + + // Look for an initializer that references another variable. We need to know + // if that variable can be deleted after the reference is removed. + if (inst->NumOperands() == 4) { + Instruction* initializer = + get_def_use_mgr()->GetDef(inst->GetSingleWordOperand(3)); + + // TODO: Handle OpSpecConstantOP which might be defined in terms of other + // variables. Will probably require a unified dead code pass that does all + // instruction types. (Issue 906) + if (initializer->opcode() == spv::Op::OpVariable) { + uint32_t initializer_id = initializer->result_id(); + size_t& count = reference_count_[initializer_id]; + if (count != kMustKeep) { + --count; + } + + if (count == 0) { + DeleteVariable(initializer_id); + } + } + } + context()->KillDef(result_id); +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.h new file mode 100644 index 00000000..5dde71ba --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dead_variable_elimination.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ +#define SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ + +#include +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +class DeadVariableElimination : public MemPass { + public: + const char* name() const override { return "eliminate-dead-variables"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Deletes the OpVariable instruction who result id is |result_id|. + void DeleteVariable(uint32_t result_id); + + // Keeps track of the number of references of an id. Once that value is 0, it + // is safe to remove the corresponding instruction. + // + // Note that the special value kMustKeep is used to indicate that the + // instruction cannot be deleted for reasons other that is being explicitly + // referenced. + std::unordered_map reference_count_; + + // Special value used to indicate that an id cannot be safely deleted. + enum { kMustKeep = INT_MAX }; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.cpp new file mode 100644 index 00000000..24094b36 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.cpp @@ -0,0 +1,945 @@ +// Copyright (c) 2020-2022 Google LLC +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/debug_info_manager.h" + +#include + +#include "source/opt/ir_context.h" + +// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100 +// extension instructions. + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { +constexpr uint32_t kOpLineOperandLineIndex = 1; +constexpr uint32_t kLineOperandIndexDebugFunction = 7; +constexpr uint32_t kLineOperandIndexDebugLexicalBlock = 5; +constexpr uint32_t kLineOperandIndexDebugLine = 5; +constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13; +constexpr uint32_t kDebugFunctionDefinitionOperandDebugFunctionIndex = 4; +constexpr uint32_t kDebugFunctionDefinitionOperandOpFunctionIndex = 5; +constexpr uint32_t kDebugFunctionOperandParentIndex = 9; +constexpr uint32_t kDebugTypeCompositeOperandParentIndex = 9; +constexpr uint32_t kDebugLexicalBlockOperandParentIndex = 7; +constexpr uint32_t kDebugInlinedAtOperandInlinedIndex = 6; +constexpr uint32_t kDebugExpressOperandOperationIndex = 4; +constexpr uint32_t kDebugDeclareOperandLocalVariableIndex = 4; +constexpr uint32_t kDebugDeclareOperandVariableIndex = 5; +constexpr uint32_t kDebugValueOperandExpressionIndex = 6; +constexpr uint32_t kDebugOperationOperandOperationIndex = 4; +constexpr uint32_t kOpVariableOperandStorageClassIndex = 2; +constexpr uint32_t kDebugLocalVariableOperandParentIndex = 9; +constexpr uint32_t kExtInstInstructionInIdx = 1; +constexpr uint32_t kDebugGlobalVariableOperandFlagsIndex = 12; +constexpr uint32_t kDebugLocalVariableOperandFlagsIndex = 10; + +void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { + assert(dbg_inlined_at); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); + if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { + dbg_inlined_at->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); + } else { + dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex, + {inlined_operand}); + } +} + +uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { + assert(dbg_inlined_at); + assert(dbg_inlined_at->GetCommonDebugOpcode() == + CommonDebugInfoDebugInlinedAt); + if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) + return kNoInlinedAt; + return dbg_inlined_at->GetSingleWordOperand( + kDebugInlinedAtOperandInlinedIndex); +} + +bool IsEmptyDebugExpression(Instruction* instr) { + return (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression) && + instr->NumOperands() == kDebugExpressOperandOperationIndex; +} + +} // namespace + +DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { + AnalyzeDebugInsts(*c->module()); +} + +uint32_t DebugInfoManager::GetDbgSetImportId() { + uint32_t setId = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + if (setId == 0) { + setId = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + } + return setId; +} + +Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { + auto dbg_inst_it = id_to_dbg_inst_.find(id); + return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; +} + +void DebugInfoManager::RegisterDbgInst(Instruction* inst) { + assert(inst->NumInOperands() != 0 && + (GetDbgSetImportId() == inst->GetInOperand(0).words[0]) && + "Given instruction is not a debug instruction"); + id_to_dbg_inst_[inst->result_id()] = inst; +} + +void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + // Do not register function that has been optimized away. + auto fn_inst = GetDbgInst(fn_id); + if (fn_inst != nullptr) { + assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone); + return; + } + assert( + fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunction for a function that already has DebugFunction"); + fn_id_to_dbg_fn_[fn_id] = inst; + } else if (inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + auto fn_id = inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + auto fn_inst = GetDbgInst(inst->GetSingleWordOperand( + kDebugFunctionDefinitionOperandDebugFunctionIndex)); + assert(fn_inst && fn_inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunction); + assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunctionDefinition for a function that already has " + "DebugFunctionDefinition"); + fn_id_to_dbg_fn_[fn_id] = fn_inst; + } else { + assert(false && "inst is not a DebugFunction"); + } +} + +void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, + Instruction* dbg_declare) { + assert(dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + dbg_declare->GetCommonDebugOpcode() == CommonDebugInfoDebugValue); + auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id); + if (dbg_decl_itr == var_id_to_dbg_decl_.end()) { + var_id_to_dbg_decl_[var_id] = {dbg_declare}; + } else { + dbg_decl_itr->second.insert(dbg_declare); + } +} + +// Create new constant directly into global value area, bypassing the +// Constant manager. This is used when the DefUse or Constant managers +// are invalid and cannot be regenerated due to the module being in an +// inconsistent state e.g. in the middle of significant modification +// such as inlining. Invalidate Constant and DefUse managers if used. +uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { + uint32_t id = context->TakeNextId(); + std::unique_ptr new_const(new Instruction( + context, spv::Op::OpConstant, context->get_type_mgr()->GetUIntTypeId(), + id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + {const_value}}, + })); + context->module()->AddGlobalValue(std::move(new_const)); + context->InvalidateAnalyses(IRContext::kAnalysisConstants); + context->InvalidateAnalyses(IRContext::kAnalysisDefUse); + return id; +} + +uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, + const DebugScope& scope) { + uint32_t setId = GetDbgSetImportId(); + + if (setId == 0) return kNoInlinedAt; + + spv_operand_type_t line_number_type = + spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER; + + // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant, + // not literals. + if (setId == + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) + line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID; + + uint32_t line_number = 0; + if (line == nullptr) { + auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); + if (lexical_scope_inst == nullptr) return kNoInlinedAt; + CommonDebugInfoInstructions debug_opcode = + lexical_scope_inst->GetCommonDebugOpcode(); + switch (debug_opcode) { + case CommonDebugInfoDebugFunction: + line_number = lexical_scope_inst->GetSingleWordOperand( + kLineOperandIndexDebugFunction); + break; + case CommonDebugInfoDebugLexicalBlock: + line_number = lexical_scope_inst->GetSingleWordOperand( + kLineOperandIndexDebugLexicalBlock); + break; + case CommonDebugInfoDebugTypeComposite: + case CommonDebugInfoDebugCompilationUnit: + assert(false && + "DebugTypeComposite and DebugCompilationUnit are lexical " + "scopes, but we inline functions into a function or a block " + "of a function, not into a struct/class or a global scope."); + break; + default: + assert(false && + "Unreachable. a debug extension instruction for a " + "lexical scope must be DebugFunction, DebugTypeComposite, " + "DebugLexicalBlock, or DebugCompilationUnit."); + break; + } + } else { + if (line->opcode() == spv::Op::OpLine) { + line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + } else if (line->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugLine) { + line_number = line->GetSingleWordOperand(kLineOperandIndexDebugLine); + } else { + assert(false && + "Unreachable. A line instruction must be OpLine or DebugLine"); + } + + // If we need the line number as an ID, generate that constant now. + // If Constant or DefUse managers are invalid, generate constant + // directly into the global value section of the module; do not + // use Constant manager which may attempt to invoke building of the + // DefUse manager which cannot be done during inlining. The extra + // constants that may be generated here is likely not significant + // and will likely be cleaned up in later passes. + if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID && + line->opcode() == spv::Op::OpLine) { + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) || + !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants)) + line_number = AddNewConstInGlobals(context(), line_number); + else + line_number = + context()->get_constant_mgr()->GetUIntConstId(line_number); + } + } + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr inlined_at(new Instruction( + context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {setId}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(CommonDebugInfoDebugInlinedAt)}}, + {line_number_type, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, + })); + // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt + // into the Inlined operand of this new DebugInlinedAt. + if (scope.GetInlinedAt() != kNoInlinedAt) { + inlined_at->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}}); + } + RegisterDbgInst(inlined_at.get()); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get()); + context()->module()->AddExtInstDebugInfo(std::move(inlined_at)); + return result_id; +} + +DebugScope DebugInfoManager::BuildDebugScope( + const DebugScope& callee_instr_scope, + DebugInlinedAtContext* inlined_at_ctx) { + return DebugScope(callee_instr_scope.GetLexicalScope(), + BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(), + inlined_at_ctx)); +} + +uint32_t DebugInfoManager::BuildDebugInlinedAtChain( + uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) { + if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() == + kNoDebugScope) + return kNoInlinedAt; + + // Reuse the already generated DebugInlinedAt chain if exists. + uint32_t already_generated_chain_head_id = + inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at); + if (already_generated_chain_head_id != kNoInlinedAt) { + return already_generated_chain_head_id; + } + + const uint32_t new_dbg_inlined_at_id = + CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(), + inlined_at_ctx->GetScopeOfCallInstruction()); + if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt; + + if (callee_inlined_at == kNoInlinedAt) { + inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id); + return new_dbg_inlined_at_id; + } + + uint32_t chain_head_id = kNoInlinedAt; + uint32_t chain_iter_id = callee_inlined_at; + Instruction* last_inlined_at_in_chain = nullptr; + do { + Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt( + chain_iter_id, /* insert_before */ last_inlined_at_in_chain); + assert(new_inlined_at_in_chain != nullptr); + + // Set DebugInlinedAt of the new scope as the head of the chain. + if (chain_head_id == kNoInlinedAt) + chain_head_id = new_inlined_at_in_chain->result_id(); + + // Previous DebugInlinedAt of the chain must point to the new + // DebugInlinedAt as its Inlined operand to build a recursive + // chain. + if (last_inlined_at_in_chain != nullptr) { + SetInlinedOperand(last_inlined_at_in_chain, + new_inlined_at_in_chain->result_id()); + } + last_inlined_at_in_chain = new_inlined_at_in_chain; + + chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain); + } while (chain_iter_id != kNoInlinedAt); + + // Put |new_dbg_inlined_at_id| into the end of the chain. + SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id); + + // Keep the new chain information that will be reused it. + inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id); + return chain_head_id; +} + +Instruction* DebugInfoManager::GetDebugOperationWithDeref() { + if (deref_operation_ != nullptr) return deref_operation_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr deref_operation; + + if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + deref_operation = std::unique_ptr(new Instruction( + context(), spv::Op::OpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + } else { + uint32_t deref_id = context()->get_constant_mgr()->GetUIntConstId( + NonSemanticShaderDebugInfo100Deref); + + deref_operation = std::unique_ptr( + new Instruction(context(), spv::Op::OpExtInst, + context()->get_type_mgr()->GetVoidTypeId(), result_id, + { + {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast( + NonSemanticShaderDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_ID, {deref_id}}, + })); + } + + // Add to the front of |ext_inst_debuginfo_|. + deref_operation_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(deref_operation)); + + RegisterDbgInst(deref_operation_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_); + return deref_operation_; +} + +Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { + assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression); + std::unique_ptr deref_expr(dbg_expr->Clone(context())); + deref_expr->SetResultId(context()->TakeNextId()); + deref_expr->InsertOperand( + kDebugExpressOperandOperationIndex, + {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}}); + auto* deref_expr_instr = + context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr)); + AnalyzeDebugInst(deref_expr_instr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr); + return deref_expr_instr; +} + +Instruction* DebugInfoManager::GetDebugInfoNone() { + if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr dbg_info_none_inst(new Instruction( + context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(CommonDebugInfoDebugInfoNone)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + debug_info_none_inst_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(dbg_info_none_inst)); + + RegisterDbgInst(debug_info_none_inst_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_); + return debug_info_none_inst_; +} + +Instruction* DebugInfoManager::GetEmptyDebugExpression() { + if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr empty_debug_expr(new Instruction( + context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(CommonDebugInfoDebugExpression)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + empty_debug_expr_inst_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(empty_debug_expr)); + + RegisterDbgInst(empty_debug_expr_inst_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_); + return empty_debug_expr_inst_; +} + +Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { + auto* inlined_at = GetDbgInst(dbg_inlined_at_id); + if (inlined_at == nullptr) return nullptr; + if (inlined_at->GetCommonDebugOpcode() != CommonDebugInfoDebugInlinedAt) { + return nullptr; + } + return inlined_at; +} + +Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id, + Instruction* insert_before) { + auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id); + if (inlined_at == nullptr) return nullptr; + std::unique_ptr new_inlined_at(inlined_at->Clone(context())); + new_inlined_at->SetResultId(context()->TakeNextId()); + RegisterDbgInst(new_inlined_at.get()); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get()); + if (insert_before != nullptr) + return insert_before->InsertBefore(std::move(new_inlined_at)); + return context()->module()->ext_inst_debuginfo_end()->InsertBefore( + std::move(new_inlined_at)); +} + +bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) { + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + return dbg_decl_itr != var_id_to_dbg_decl_.end(); +} + +bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { + bool modified = false; + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + // We intentionally copy the list of DebugDeclare instructions because + // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we + // directly use |dbg_decl_itr->second|, it accesses a dangling pointer. + auto copy_dbg_decls = dbg_decl_itr->second; + + for (auto* dbg_decl : copy_dbg_decls) { + context()->KillInst(dbg_decl); + modified = true; + } + var_id_to_dbg_decl_.erase(dbg_decl_itr); + } + return modified; +} + +uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { + auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); + assert(dbg_scope_itr != id_to_dbg_inst_.end()); + CommonDebugInfoInstructions debug_opcode = + dbg_scope_itr->second->GetCommonDebugOpcode(); + uint32_t parent_scope = kNoDebugScope; + switch (debug_opcode) { + case CommonDebugInfoDebugFunction: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugFunctionOperandParentIndex); + break; + case CommonDebugInfoDebugLexicalBlock: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugLexicalBlockOperandParentIndex); + break; + case CommonDebugInfoDebugTypeComposite: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugTypeCompositeOperandParentIndex); + break; + case CommonDebugInfoDebugCompilationUnit: + // DebugCompilationUnit does not have a parent scope. + break; + default: + assert(false && + "Unreachable. A debug scope instruction must be " + "DebugFunction, DebugTypeComposite, DebugLexicalBlock, " + "or DebugCompilationUnit."); + break; + } + return parent_scope; +} + +bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) { + uint32_t ancestor_scope_itr = scope; + while (ancestor_scope_itr != kNoDebugScope) { + if (ancestor == ancestor_scope_itr) return true; + ancestor_scope_itr = GetParentScope(ancestor_scope_itr); + } + return false; +} + +bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare, + Instruction* scope) { + assert(dbg_declare != nullptr); + assert(scope != nullptr); + + std::vector scope_ids; + if (scope->opcode() == spv::Op::OpPhi) { + scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); + for (uint32_t i = 0; i < scope->NumInOperands(); i += 2) { + auto* value = context()->get_def_use_mgr()->GetDef( + scope->GetSingleWordInOperand(i)); + if (value != nullptr) + scope_ids.push_back(value->GetDebugScope().GetLexicalScope()); + } + } else { + scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); + } + + uint32_t dbg_local_var_id = + dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex); + auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id); + assert(dbg_local_var_itr != id_to_dbg_inst_.end()); + uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand( + kDebugLocalVariableOperandParentIndex); + + // If the scope of DebugDeclare is an ancestor scope of the instruction's + // scope, the local variable is visible to the instruction. + for (uint32_t scope_id : scope_ids) { + if (scope_id != kNoDebugScope && + IsAncestorOfScope(scope_id, decl_scope_id)) { + return true; + } + } + return false; +} + +bool DebugInfoManager::AddDebugValueForVariable(Instruction* scope_and_line, + uint32_t variable_id, + uint32_t value_id, + Instruction* insert_pos) { + assert(scope_and_line != nullptr); + + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false; + + bool modified = false; + for (auto* dbg_decl_or_val : dbg_decl_itr->second) { + // Avoid inserting the new DebugValue between OpPhi or OpVariable + // instructions. + Instruction* insert_before = insert_pos->NextNode(); + while (insert_before->opcode() == spv::Op::OpPhi || + insert_before->opcode() == spv::Op::OpVariable) { + insert_before = insert_before->NextNode(); + } + modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before, + scope_and_line) != nullptr; + } + return modified; +} + +Instruction* DebugInfoManager::AddDebugValueForDecl( + Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before, + Instruction* scope_and_line) { + if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr; + + std::unique_ptr dbg_val(dbg_decl->Clone(context())); + dbg_val->SetResultId(context()->TakeNextId()); + dbg_val->SetInOperand(kExtInstInstructionInIdx, {CommonDebugInfoDebugValue}); + dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); + dbg_val->SetOperand(kDebugValueOperandExpressionIndex, + {GetEmptyDebugExpression()->result_id()}); + dbg_val->UpdateDebugInfoFrom(scope_and_line); + + auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val)); + AnalyzeDebugInst(added_dbg_val); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_val); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(insert_before); + context()->set_instr_block(added_dbg_val, insert_blk); + } + return added_dbg_val; +} + +uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) { + assert(inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation && + "inst must be Vulkan DebugOperation"); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(context()->get_def_use_mgr()->GetDef( + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex))) + ->GetU32(); +} + +uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( + Instruction* inst) { + if (inst->GetCommonDebugOpcode() != CommonDebugInfoDebugValue) return 0; + + auto* expr = + GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); + if (expr == nullptr) return 0; + if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0; + + auto* operation = GetDbgInst( + expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); + if (operation == nullptr) return 0; + + // OpenCL.DebugInfo.100 contains a literal for the operation, Vulkan uses an + // OpConstant. + if (inst->IsOpenCL100DebugInstr()) { + if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != + OpenCLDebugInfo100Deref) { + return 0; + } + } else { + uint32_t operation_const = GetVulkanDebugOperation(operation); + if (operation_const != NonSemanticShaderDebugInfo100Deref) { + return 0; + } + } + + uint32_t var_id = + inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) { + assert(false && + "Checking a DebugValue can be used for declare needs DefUseManager"); + return 0; + } + + auto* var = context()->get_def_use_mgr()->GetDef(var_id); + if (var->opcode() == spv::Op::OpVariable && + spv::StorageClass( + var->GetSingleWordOperand(kOpVariableOperandStorageClassIndex)) == + spv::StorageClass::Function) { + return var_id; + } + return 0; +} + +bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { + if (!instr->IsCommonDebugInstr()) return false; + return instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; +} + +void DebugInfoManager::ReplaceAllUsesInDebugScopeWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate) { + auto scope_id_to_users_itr = scope_id_to_users_.find(before); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + for (Instruction* inst : scope_id_to_users_itr->second) { + if (predicate(inst)) inst->UpdateLexicalScope(after); + } + scope_id_to_users_[after] = scope_id_to_users_itr->second; + scope_id_to_users_.erase(scope_id_to_users_itr); + } + auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(before); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + for (Instruction* inst : inlinedat_id_to_users_itr->second) { + if (predicate(inst)) inst->UpdateDebugInlinedAt(after); + } + inlinedat_id_to_users_[after] = inlinedat_id_to_users_itr->second; + inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); + } +} + +void DebugInfoManager::ClearDebugScopeAndInlinedAtUses(Instruction* inst) { + auto scope_id_to_users_itr = scope_id_to_users_.find(inst->result_id()); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + scope_id_to_users_.erase(scope_id_to_users_itr); + } + auto inlinedat_id_to_users_itr = + inlinedat_id_to_users_.find(inst->result_id()); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); + } +} + +void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { + if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { + auto& users = scope_id_to_users_[inst->GetDebugScope().GetLexicalScope()]; + users.insert(inst); + } + if (inst->GetDebugInlinedAt() != kNoInlinedAt) { + auto& users = inlinedat_id_to_users_[inst->GetDebugInlinedAt()]; + users.insert(inst); + } + + if (!inst->IsCommonDebugInstr()) return; + + RegisterDbgInst(inst); + + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction || + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + RegisterDbgFunction(inst); + } + + if (deref_operation_ == nullptr && + inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) == + OpenCLDebugInfo100Deref) { + deref_operation_ = inst; + } + + if (deref_operation_ == nullptr && + inst->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(inst); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = inst; + } + } + + if (debug_info_none_inst_ == nullptr && + inst->GetCommonDebugOpcode() == CommonDebugInfoDebugInfoNone) { + debug_info_none_inst_ = inst; + } + + if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(inst)) { + empty_debug_expr_inst_ = inst; + } + + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + uint32_t var_id = + inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + RegisterDbgDeclare(var_id, inst); + } + + if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(inst)) { + RegisterDbgDeclare(var_id, inst); + } +} + +void DebugInfoManager::ConvertDebugGlobalToLocalVariable( + Instruction* dbg_global_var, Instruction* local_var) { + if (dbg_global_var->GetCommonDebugOpcode() != + CommonDebugInfoDebugGlobalVariable) { + return; + } + assert(local_var->opcode() == spv::Op::OpVariable || + local_var->opcode() == spv::Op::OpFunctionParameter); + + // Convert |dbg_global_var| to DebugLocalVariable + // All of the operands up to the scope operand are the same for the type + // instructions. The flag operand needs to move from operand + // kDebugGlobalVariableOperandFlagsIndex to + // kDebugLocalVariableOperandFlagsIndex. No other operands are needed to + // define the DebugLocalVariable. + + // Modify the opcode. + dbg_global_var->SetInOperand(kExtInstInstructionInIdx, + {CommonDebugInfoDebugLocalVariable}); + + // Move the flags operand. + auto flags = dbg_global_var->GetSingleWordOperand( + kDebugGlobalVariableOperandFlagsIndex); + dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags}); + + // Remove the extra operands. Starting at the end to avoid copying too much + // data. + for (uint32_t i = dbg_global_var->NumOperands() - 1; + i > kDebugLocalVariableOperandFlagsIndex; --i) { + dbg_global_var->RemoveOperand(i); + } + + // Update the def-use manager. + context()->ForgetUses(dbg_global_var); + context()->AnalyzeUses(dbg_global_var); + + // Create a DebugDeclare + std::unique_ptr new_dbg_decl(new Instruction( + context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + context()->TakeNextId(), + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(CommonDebugInfoDebugDeclare)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {dbg_global_var->result_id()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {GetEmptyDebugExpression()->result_id()}}, + })); + // Must insert after all OpVariables in block + Instruction* insert_before = local_var; + while (insert_before->opcode() == spv::Op::OpVariable) + insert_before = insert_before->NextNode(); + auto* added_dbg_decl = insert_before->InsertBefore(std::move(new_dbg_decl)); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(local_var); + context()->set_instr_block(added_dbg_decl, insert_blk); + } +} + +void DebugInfoManager::AnalyzeDebugInsts(Module& module) { + deref_operation_ = nullptr; + debug_info_none_inst_ = nullptr; + empty_debug_expr_inst_ = nullptr; + module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); }); + + // Move |empty_debug_expr_inst_| to the beginning of the debug instruction + // list. + if (empty_debug_expr_inst_ != nullptr && + empty_debug_expr_inst_->PreviousNode() != nullptr && + empty_debug_expr_inst_->PreviousNode()->IsCommonDebugInstr()) { + empty_debug_expr_inst_->InsertBefore( + &*context()->module()->ext_inst_debuginfo_begin()); + } + + // Move |debug_info_none_inst_| to the beginning of the debug instruction + // list. + if (debug_info_none_inst_ != nullptr && + debug_info_none_inst_->PreviousNode() != nullptr && + debug_info_none_inst_->PreviousNode()->IsCommonDebugInstr()) { + debug_info_none_inst_->InsertBefore( + &*context()->module()->ext_inst_debuginfo_begin()); + } +} + +void DebugInfoManager::ClearDebugInfo(Instruction* instr) { + auto scope_id_to_users_itr = + scope_id_to_users_.find(instr->GetDebugScope().GetLexicalScope()); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + scope_id_to_users_itr->second.erase(instr); + } + auto inlinedat_id_to_users_itr = + inlinedat_id_to_users_.find(instr->GetDebugInlinedAt()); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + inlinedat_id_to_users_itr->second.erase(instr); + } + + if (instr == nullptr || !instr->IsCommonDebugInstr()) { + return; + } + + id_to_dbg_inst_.erase(instr->result_id()); + + if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = + instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } + if (instr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + auto fn_id = instr->GetSingleWordOperand( + kDebugFunctionDefinitionOperandOpFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } + + if (instr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + instr->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { + auto var_or_value_id = + instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + dbg_decl_itr->second.erase(instr); + } + } + + if (deref_operation_ == instr) { + deref_operation_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + // OpenCL.DebugInfo.100 contains the operation as a literal operand, in + // Vulkan it's referenced as an OpConstant. + if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugOperation && + dbg_instr_itr->GetSingleWordOperand( + kDebugOperationOperandOperationIndex) == + OpenCLDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } else if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugOperation) { + uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr); + if (operation_const == NonSemanticShaderDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } + } + } + } + + if (debug_info_none_inst_ == instr) { + debug_info_none_inst_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && dbg_instr_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugInfoNone) { + debug_info_none_inst_ = &*dbg_instr_itr; + break; + } + } + } + + if (empty_debug_expr_inst_ == instr) { + empty_debug_expr_inst_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) { + empty_debug_expr_inst_ = &*dbg_instr_itr; + break; + } + } + } +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.h new file mode 100644 index 00000000..abb7b9a0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/debug_info_manager.h @@ -0,0 +1,285 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_ +#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_ + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +// When an instruction of a callee function is inlined to its caller function, +// we need the line and the scope information of the function call instruction +// to generate DebugInlinedAt. This class keeps the data. For multiple inlining +// of a single instruction, we have to create multiple DebugInlinedAt +// instructions as a chain. This class keeps the information of the generated +// DebugInlinedAt chains to reduce the number of chains. +class DebugInlinedAtContext { + public: + explicit DebugInlinedAtContext(Instruction* call_inst) + : call_inst_line_(call_inst->dbg_line_inst()), + call_inst_scope_(call_inst->GetDebugScope()) {} + + const Instruction* GetLineOfCallInstruction() { return call_inst_line_; } + const DebugScope& GetScopeOfCallInstruction() { return call_inst_scope_; } + // Puts the DebugInlinedAt chain that is generated for the callee instruction + // whose DebugInlinedAt of DebugScope is |callee_instr_inlined_at| into + // |callee_inlined_at2chain_|. + void SetDebugInlinedAtChain(uint32_t callee_instr_inlined_at, + uint32_t chain_head_id) { + callee_inlined_at2chain_[callee_instr_inlined_at] = chain_head_id; + } + // Gets the DebugInlinedAt chain from |callee_inlined_at2chain_|. + uint32_t GetDebugInlinedAtChain(uint32_t callee_instr_inlined_at) { + auto chain_itr = callee_inlined_at2chain_.find(callee_instr_inlined_at); + if (chain_itr != callee_inlined_at2chain_.end()) return chain_itr->second; + return kNoInlinedAt; + } + + private: + // The line information of the function call instruction that will be + // replaced by the callee function. + const Instruction* call_inst_line_; + + // The scope information of the function call instruction that will be + // replaced by the callee function. + const DebugScope call_inst_scope_; + + // Map from DebugInlinedAt ids of callee to head ids of new generated + // DebugInlinedAt chain. + std::unordered_map callee_inlined_at2chain_; +}; + +// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and +// NonSemantic.Shader.DebugInfo.100 extension instructions. +class DebugInfoManager { + public: + // Constructs a debug information manager from the given |context|. + DebugInfoManager(IRContext* context); + + DebugInfoManager(const DebugInfoManager&) = delete; + DebugInfoManager(DebugInfoManager&&) = delete; + DebugInfoManager& operator=(const DebugInfoManager&) = delete; + DebugInfoManager& operator=(DebugInfoManager&&) = delete; + + friend bool operator==(const DebugInfoManager&, const DebugInfoManager&); + friend bool operator!=(const DebugInfoManager& lhs, + const DebugInfoManager& rhs) { + return !(lhs == rhs); + } + + // Analyzes DebugInfo instruction |dbg_inst|. + void AnalyzeDebugInst(Instruction* dbg_inst); + + // Creates new DebugInlinedAt and returns its id. Its line operand is the + // line number of |line| if |line| is not nullptr. Otherwise, its line operand + // is the line number of lexical scope of |scope|. Its Scope and Inlined + // operands are Scope and Inlined of |scope|. + uint32_t CreateDebugInlinedAt(const Instruction* line, + const DebugScope& scope); + + // Clones DebugExpress instruction |dbg_expr| and add Deref Operation + // in the front of the Operation list of |dbg_expr|. + Instruction* DerefDebugExpression(Instruction* dbg_expr); + + // Returns a DebugInfoNone instruction. + Instruction* GetDebugInfoNone(); + + // Returns DebugInlinedAt whose id is |dbg_inlined_at_id|. If it does not + // exist or it is not a DebugInlinedAt instruction, return nullptr. + Instruction* GetDebugInlinedAt(uint32_t dbg_inlined_at_id); + + // Returns DebugFunction whose Function operand is |fn_id|. If it does not + // exist, return nullptr. + Instruction* GetDebugFunction(uint32_t fn_id) { + auto dbg_fn_it = fn_id_to_dbg_fn_.find(fn_id); + return dbg_fn_it == fn_id_to_dbg_fn_.end() ? nullptr : dbg_fn_it->second; + } + + // Clones DebugInlinedAt whose id is |clone_inlined_at_id|. If + // |clone_inlined_at_id| is not an id of DebugInlinedAt, returns nullptr. + // If |insert_before| is given, inserts the new DebugInlinedAt before it. + // Otherwise, inserts the new DebugInlinedAt into the debug instruction + // section of the module. + Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id, + Instruction* insert_before = nullptr); + + // Returns the debug scope corresponding to an inlining instruction in the + // scope |callee_instr_scope| into |inlined_at_ctx|. Generates all new + // debug instructions needed to represent the scope. + DebugScope BuildDebugScope(const DebugScope& callee_instr_scope, + DebugInlinedAtContext* inlined_at_ctx); + + // Returns DebugInlinedAt corresponding to inlining an instruction, which + // was inlined at |callee_inlined_at|, into |inlined_at_ctx|. Generates all + // new debug instructions needed to represent the DebugInlinedAt. + uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at, + DebugInlinedAtContext* inlined_at_ctx); + + // Returns true if there is a debug declaration instruction whose + // 'Local Variable' operand is |variable_id|. + bool IsVariableDebugDeclared(uint32_t variable_id); + + // Kills all debug declaration instructions with Deref whose 'Local Variable' + // operand is |variable_id|. Returns whether it kills an instruction or not. + bool KillDebugDeclares(uint32_t variable_id); + + // Generates a DebugValue instruction with value |value_id| for every local + // variable that is in the scope of |scope_and_line| and whose memory is + // |variable_id| and inserts it after the instruction |insert_pos|. + // Returns whether a DebugValue is added or not. + bool AddDebugValueForVariable(Instruction* scope_and_line, + uint32_t variable_id, uint32_t value_id, + Instruction* insert_pos); + + // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before + // |insert_before|. The new DebugValue has the same line and scope as + // |scope_and_line|, or no scope and line information if |scope_and_line| + // is nullptr. The new DebugValue has the same operands as DebugDeclare + // but it uses |value_id| for the value. Returns the created DebugValue, + // or nullptr if fails to create one. + Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id, + Instruction* insert_before, + Instruction* scope_and_line); + + // Erases |instr| from data structures of this class. + void ClearDebugInfo(Instruction* instr); + + // Return the opcode for the Vulkan DebugOperation inst + uint32_t GetVulkanDebugOperation(Instruction* inst); + + // Returns the id of Value operand if |inst| is DebugValue who has Deref + // operation and its Value operand is a result id of OpVariable with + // Function storage class. Otherwise, returns 0. + uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst); + + // Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and + // creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|. + void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var, + Instruction* local_var); + + // Returns true if |instr| is a debug declaration instruction. + bool IsDebugDeclare(Instruction* instr); + + // Replace all uses of |before| id that is an operand of a DebugScope with + // |after| id if those uses (instruction) return true for |predicate|. + void ReplaceAllUsesInDebugScopeWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate); + + // Removes uses of DebugScope |inst| from |scope_id_to_users_| or uses of + // DebugInlinedAt |inst| from |inlinedat_id_to_users_|. + void ClearDebugScopeAndInlinedAtUses(Instruction* inst); + + private: + IRContext* context() { return context_; } + + // Analyzes DebugInfo instructions in the given |module| and + // populates data structures in this class. + void AnalyzeDebugInsts(Module& module); + + // Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available. + uint32_t GetDbgSetImportId(); + + // Returns the debug instruction whose id is |id|. Returns |nullptr| if one + // does not exists. + Instruction* GetDbgInst(uint32_t id); + + // Returns a DebugOperation instruction with OpCode Deref. + Instruction* GetDebugOperationWithDeref(); + + // Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of + // |inst| as a key. + void RegisterDbgInst(Instruction* inst); + + // Register the DebugFunction instruction |inst|. The function referenced + // in |inst| must not already be registered. + void RegisterDbgFunction(Instruction* inst); + + // Register the DebugDeclare or DebugValue with Deref operation + // |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id + // |var_id| as a key. + void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare); + + // Returns a DebugExpression instruction without Operation operands. + Instruction* GetEmptyDebugExpression(); + + // Returns true if a scope |ancestor| is |scope| or an ancestor scope + // of |scope|. + bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor); + + // Returns true if the declaration of a local variable |dbg_declare| + // is visible in the scope of an instruction |instr_scope_id|. + bool IsDeclareVisibleToInstr(Instruction* dbg_declare, Instruction* scope); + + // Returns the parent scope of the scope |child_scope|. + uint32_t GetParentScope(uint32_t child_scope); + + IRContext* context_; + + // Mapping from ids of DebugInfo extension instructions. + // to their Instruction instances. + std::unordered_map id_to_dbg_inst_; + + // Mapping from function's ids to DebugFunction instructions whose + // operand is the function. + std::unordered_map fn_id_to_dbg_fn_; + + // Orders Instruction* for use in associative containers (i.e. less than + // ordering). Unique Id is used. + typedef Instruction* InstPtr; + struct InstPtrLess { + bool operator()(const InstPtr& lhs, const InstPtr& rhs) const { + return lhs->unique_id() < rhs->unique_id(); + } + }; + + // Mapping from variable or value ids to DebugDeclare or DebugValue + // instructions whose operand is the variable or value. + std::unordered_map> + var_id_to_dbg_decl_; + + // Mapping from DebugScope ids to users. + std::unordered_map> + scope_id_to_users_; + + // Mapping from DebugInlinedAt ids to users. + std::unordered_map> + inlinedat_id_to_users_; + + // DebugOperation whose OpCode is OpenCLDebugInfo100Deref. + Instruction* deref_operation_; + + // DebugInfoNone instruction. We need only a single DebugInfoNone. + // To reuse the existing one, we keep it using this member variable. + Instruction* debug_info_none_inst_; + + // DebugExpression instruction without Operation operands. We need only + // a single DebugExpression without Operation operands. To reuse the + // existing one, we keep it using this member variable. + Instruction* empty_debug_expr_inst_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEBUG_INFO_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.cpp new file mode 100644 index 00000000..3e95dbc3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.cpp @@ -0,0 +1,644 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/decoration_manager.h" + +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { +using InstructionVector = std::vector; +using DecorationSet = std::set; + +// Returns true if |a| is a subet of |b|. +bool IsSubset(const DecorationSet& a, const DecorationSet& b) { + auto it1 = a.begin(); + auto it2 = b.begin(); + + while (it1 != a.end()) { + if (it2 == b.end() || *it1 < *it2) { + // |*it1| is in |a|, but not in |b|. + return false; + } + if (*it1 == *it2) { + // Found the element move to the next one. + it1++; + it2++; + } else /* *it1 > *it2 */ { + // Did not find |*it1| yet, check the next element in |b|. + it2++; + } + } + return true; +} +} // namespace + +bool DecorationManager::RemoveDecorationsFrom( + uint32_t id, std::function pred) { + bool was_modified = false; + const auto ids_iter = id_to_decoration_insts_.find(id); + if (ids_iter == id_to_decoration_insts_.end()) { + return was_modified; + } + + TargetData& decorations_info = ids_iter->second; + auto context = module_->context(); + std::vector insts_to_kill; + const bool is_group = !decorations_info.decorate_insts.empty(); + + // Schedule all direct decorations for removal if instructed as such by + // |pred|. + for (Instruction* inst : decorations_info.direct_decorations) + if (pred(*inst)) insts_to_kill.push_back(inst); + + // For all groups being directly applied to |id|, remove |id| (and the + // literal if |inst| is an OpGroupMemberDecorate) from the instruction + // applying the group. + std::unordered_set indirect_decorations_to_remove; + for (Instruction* inst : decorations_info.indirect_decorations) { + assert(inst->opcode() == spv::Op::OpGroupDecorate || + inst->opcode() == spv::Op::OpGroupMemberDecorate); + + std::vector group_decorations_to_keep; + const uint32_t group_id = inst->GetSingleWordInOperand(0u); + const auto group_iter = id_to_decoration_insts_.find(group_id); + assert(group_iter != id_to_decoration_insts_.end() && + "Unknown decoration group"); + const auto& group_decorations = group_iter->second.direct_decorations; + for (Instruction* decoration : group_decorations) { + if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration); + } + + // If all decorations should be kept, then we can keep |id| part of the + // group. However, if the group itself has no decorations, we should remove + // the id from the group. This is needed to make |KillNameAndDecorate| work + // correctly when a decoration group has no decorations. + if (group_decorations_to_keep.size() == group_decorations.size() && + group_decorations.size() != 0) { + continue; + } + + // Otherwise, remove |id| from the targets of |group_id| + const uint32_t stride = + inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u; + for (uint32_t i = 1u; i < inst->NumInOperands();) { + if (inst->GetSingleWordInOperand(i) != id) { + i += stride; + continue; + } + + const uint32_t last_operand_index = inst->NumInOperands() - stride; + if (i < last_operand_index) + inst->GetInOperand(i) = inst->GetInOperand(last_operand_index); + // Remove the associated literal, if it exists. + if (stride == 2u) { + if (i < last_operand_index) + inst->GetInOperand(i + 1u) = + inst->GetInOperand(last_operand_index + 1u); + inst->RemoveInOperand(last_operand_index + 1u); + } + inst->RemoveInOperand(last_operand_index); + was_modified = true; + } + + // If the instruction has no targets left, remove the instruction + // altogether. + if (inst->NumInOperands() == 1u) { + indirect_decorations_to_remove.emplace(inst); + insts_to_kill.push_back(inst); + } else if (was_modified) { + context->ForgetUses(inst); + indirect_decorations_to_remove.emplace(inst); + context->AnalyzeUses(inst); + } + + // If only some of the decorations should be kept, clone them and apply + // them directly to |id|. + if (!group_decorations_to_keep.empty()) { + for (Instruction* decoration : group_decorations_to_keep) { + // simply clone decoration and change |group_id| to |id| + std::unique_ptr new_inst( + decoration->Clone(module_->context())); + new_inst->SetInOperand(0, {id}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + } + } + + auto& indirect_decorations = decorations_info.indirect_decorations; + indirect_decorations.erase( + std::remove_if( + indirect_decorations.begin(), indirect_decorations.end(), + [&indirect_decorations_to_remove](const Instruction* inst) { + return indirect_decorations_to_remove.count(inst); + }), + indirect_decorations.end()); + + was_modified |= !insts_to_kill.empty(); + for (Instruction* inst : insts_to_kill) context->KillInst(inst); + insts_to_kill.clear(); + + // Schedule all instructions applying the group for removal if this group no + // longer applies decorations, either directly or indirectly. + if (is_group && decorations_info.direct_decorations.empty() && + decorations_info.indirect_decorations.empty()) { + for (Instruction* inst : decorations_info.decorate_insts) + insts_to_kill.push_back(inst); + } + was_modified |= !insts_to_kill.empty(); + for (Instruction* inst : insts_to_kill) context->KillInst(inst); + + if (decorations_info.direct_decorations.empty() && + decorations_info.indirect_decorations.empty() && + decorations_info.decorate_insts.empty()) { + id_to_decoration_insts_.erase(ids_iter); + } + return was_modified; +} + +std::vector DecorationManager::GetDecorationsFor( + uint32_t id, bool include_linkage) { + return InternalGetDecorationsFor(id, include_linkage); +} + +std::vector DecorationManager::GetDecorationsFor( + uint32_t id, bool include_linkage) const { + return const_cast(this) + ->InternalGetDecorationsFor(id, include_linkage); +} + +bool DecorationManager::HaveTheSameDecorations(uint32_t id1, + uint32_t id2) const { + const InstructionVector decorations_for1 = GetDecorationsFor(id1, false); + const InstructionVector decorations_for2 = GetDecorationsFor(id2, false); + + // This function splits the decoration instructions into different sets, + // based on their opcode; only OpDecorate, OpDecorateId, + // OpDecorateStringGOOGLE, and OpMemberDecorate are considered, the other + // opcodes are ignored. + const auto fillDecorationSets = + [](const InstructionVector& decoration_list, DecorationSet* decorate_set, + DecorationSet* decorate_id_set, DecorationSet* decorate_string_set, + DecorationSet* member_decorate_set) { + for (const Instruction* inst : decoration_list) { + std::u32string decoration_payload; + // Ignore the opcode and the target as we do not want them to be + // compared. + for (uint32_t i = 1u; i < inst->NumInOperands(); ++i) { + for (uint32_t word : inst->GetInOperand(i).words) { + decoration_payload.push_back(word); + } + } + + switch (inst->opcode()) { + case spv::Op::OpDecorate: + decorate_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpMemberDecorate: + member_decorate_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpDecorateId: + decorate_id_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpDecorateStringGOOGLE: + decorate_string_set->emplace(std::move(decoration_payload)); + break; + default: + break; + } + } + }; + + DecorationSet decorate_set_for1; + DecorationSet decorate_id_set_for1; + DecorationSet decorate_string_set_for1; + DecorationSet member_decorate_set_for1; + fillDecorationSets(decorations_for1, &decorate_set_for1, + &decorate_id_set_for1, &decorate_string_set_for1, + &member_decorate_set_for1); + + DecorationSet decorate_set_for2; + DecorationSet decorate_id_set_for2; + DecorationSet decorate_string_set_for2; + DecorationSet member_decorate_set_for2; + fillDecorationSets(decorations_for2, &decorate_set_for2, + &decorate_id_set_for2, &decorate_string_set_for2, + &member_decorate_set_for2); + + const bool result = decorate_set_for1 == decorate_set_for2 && + decorate_id_set_for1 == decorate_id_set_for2 && + member_decorate_set_for1 == member_decorate_set_for2 && + // Compare string sets last in case the strings are long. + decorate_string_set_for1 == decorate_string_set_for2; + return result; +} + +bool DecorationManager::HaveSubsetOfDecorations(uint32_t id1, + uint32_t id2) const { + const InstructionVector decorations_for1 = GetDecorationsFor(id1, false); + const InstructionVector decorations_for2 = GetDecorationsFor(id2, false); + + // This function splits the decoration instructions into different sets, + // based on their opcode; only OpDecorate, OpDecorateId, + // OpDecorateStringGOOGLE, and OpMemberDecorate are considered, the other + // opcodes are ignored. + const auto fillDecorationSets = + [](const InstructionVector& decoration_list, DecorationSet* decorate_set, + DecorationSet* decorate_id_set, DecorationSet* decorate_string_set, + DecorationSet* member_decorate_set) { + for (const Instruction* inst : decoration_list) { + std::u32string decoration_payload; + // Ignore the opcode and the target as we do not want them to be + // compared. + for (uint32_t i = 1u; i < inst->NumInOperands(); ++i) { + for (uint32_t word : inst->GetInOperand(i).words) { + decoration_payload.push_back(word); + } + } + + switch (inst->opcode()) { + case spv::Op::OpDecorate: + decorate_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpMemberDecorate: + member_decorate_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpDecorateId: + decorate_id_set->emplace(std::move(decoration_payload)); + break; + case spv::Op::OpDecorateStringGOOGLE: + decorate_string_set->emplace(std::move(decoration_payload)); + break; + default: + break; + } + } + }; + + DecorationSet decorate_set_for1; + DecorationSet decorate_id_set_for1; + DecorationSet decorate_string_set_for1; + DecorationSet member_decorate_set_for1; + fillDecorationSets(decorations_for1, &decorate_set_for1, + &decorate_id_set_for1, &decorate_string_set_for1, + &member_decorate_set_for1); + + DecorationSet decorate_set_for2; + DecorationSet decorate_id_set_for2; + DecorationSet decorate_string_set_for2; + DecorationSet member_decorate_set_for2; + fillDecorationSets(decorations_for2, &decorate_set_for2, + &decorate_id_set_for2, &decorate_string_set_for2, + &member_decorate_set_for2); + + const bool result = + IsSubset(decorate_set_for1, decorate_set_for2) && + IsSubset(decorate_id_set_for1, decorate_id_set_for2) && + IsSubset(member_decorate_set_for1, member_decorate_set_for2) && + // Compare string sets last in case the strings are long. + IsSubset(decorate_string_set_for1, decorate_string_set_for2); + return result; +} + +// TODO(pierremoreau): If OpDecorateId is referencing an OpConstant, one could +// check that the constants are the same rather than just +// looking at the constant ID. +bool DecorationManager::AreDecorationsTheSame(const Instruction* inst1, + const Instruction* inst2, + bool ignore_target) const { + switch (inst1->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpMemberDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + break; + default: + return false; + } + + if (inst1->opcode() != inst2->opcode() || + inst1->NumInOperands() != inst2->NumInOperands()) + return false; + + for (uint32_t i = ignore_target ? 1u : 0u; i < inst1->NumInOperands(); ++i) + if (inst1->GetInOperand(i) != inst2->GetInOperand(i)) return false; + + return true; +} + +void DecorationManager::AnalyzeDecorations() { + if (!module_) return; + + // For each group and instruction, collect all their decoration instructions. + for (Instruction& inst : module_->annotations()) { + AddDecoration(&inst); + } +} + +void DecorationManager::AddDecoration(Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorate: { + const auto target_id = inst->GetSingleWordInOperand(0u); + id_to_decoration_insts_[target_id].direct_decorations.push_back(inst); + break; + } + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: { + const uint32_t start = + inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u; + const uint32_t stride = start; + for (uint32_t i = start; i < inst->NumInOperands(); i += stride) { + const auto target_id = inst->GetSingleWordInOperand(i); + TargetData& target_data = id_to_decoration_insts_[target_id]; + target_data.indirect_decorations.push_back(inst); + } + const auto target_id = inst->GetSingleWordInOperand(0u); + id_to_decoration_insts_[target_id].decorate_insts.push_back(inst); + break; + } + default: + break; + } +} + +void DecorationManager::AddDecoration(spv::Op opcode, + std::vector opnds) { + IRContext* ctx = module_->context(); + std::unique_ptr newDecoOp( + new Instruction(ctx, opcode, 0, 0, opnds)); + ctx->AddAnnotationInst(std::move(newDecoOp)); +} + +void DecorationManager::AddDecoration(uint32_t inst_id, uint32_t decoration) { + AddDecoration( + spv::Op::OpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}}); +} + +void DecorationManager::AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + spv::Op::OpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + +void DecorationManager::AddMemberDecoration(uint32_t inst_id, uint32_t member, + uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + spv::Op::OpMemberDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + +template +std::vector DecorationManager::InternalGetDecorationsFor( + uint32_t id, bool include_linkage) { + std::vector decorations; + + const auto ids_iter = id_to_decoration_insts_.find(id); + // |id| has no decorations + if (ids_iter == id_to_decoration_insts_.end()) return decorations; + + const TargetData& target_data = ids_iter->second; + + const auto process_direct_decorations = + [include_linkage, + &decorations](const std::vector& direct_decorations) { + for (Instruction* inst : direct_decorations) { + const bool is_linkage = + inst->opcode() == spv::Op::OpDecorate && + spv::Decoration(inst->GetSingleWordInOperand(1u)) == + spv::Decoration::LinkageAttributes; + if (include_linkage || !is_linkage) decorations.push_back(inst); + } + }; + + // Process |id|'s decorations. + process_direct_decorations(ids_iter->second.direct_decorations); + + // Process the decorations of all groups applied to |id|. + for (const Instruction* inst : target_data.indirect_decorations) { + const uint32_t group_id = inst->GetSingleWordInOperand(0u); + const auto group_iter = id_to_decoration_insts_.find(group_id); + assert(group_iter != id_to_decoration_insts_.end() && "Unknown group ID"); + process_direct_decorations(group_iter->second.direct_decorations); + } + + return decorations; +} + +bool DecorationManager::WhileEachDecoration( + uint32_t id, uint32_t decoration, + std::function f) const { + for (const Instruction* inst : GetDecorationsFor(id, true)) { + switch (inst->opcode()) { + case spv::Op::OpMemberDecorate: + if (inst->GetSingleWordInOperand(2) == decoration) { + if (!f(*inst)) return false; + } + break; + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + if (inst->GetSingleWordInOperand(1) == decoration) { + if (!f(*inst)) return false; + } + break; + default: + assert(false && "Unexpected decoration instruction"); + } + } + return true; +} + +void DecorationManager::ForEachDecoration( + uint32_t id, uint32_t decoration, + std::function f) const { + WhileEachDecoration(id, decoration, [&f](const Instruction& inst) { + f(inst); + return true; + }); +} + +bool DecorationManager::HasDecoration(uint32_t id, + spv::Decoration decoration) const { + return HasDecoration(id, static_cast(decoration)); +} + +bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) const { + bool has_decoration = false; + ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) { + has_decoration = true; + }); + return has_decoration; +} + +bool DecorationManager::FindDecoration( + uint32_t id, uint32_t decoration, + std::function f) { + return !WhileEachDecoration( + id, decoration, [&f](const Instruction& inst) { return !f(inst); }); +} + +void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) { + const auto decoration_list = id_to_decoration_insts_.find(from); + if (decoration_list == id_to_decoration_insts_.end()) return; + auto context = module_->context(); + for (Instruction* inst : decoration_list->second.direct_decorations) { + // simply clone decoration and change |target-id| to |to| + std::unique_ptr new_inst(inst->Clone(module_->context())); + new_inst->SetInOperand(0, {to}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + // We need to copy the list of instructions as ForgetUses and AnalyzeUses are + // going to modify it. + std::vector indirect_decorations = + decoration_list->second.indirect_decorations; + for (Instruction* inst : indirect_decorations) { + switch (inst->opcode()) { + case spv::Op::OpGroupDecorate: + context->ForgetUses(inst); + // add |to| to list of decorated id's + inst->AddOperand( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to})); + context->AnalyzeUses(inst); + break; + case spv::Op::OpGroupMemberDecorate: { + context->ForgetUses(inst); + // for each (id == from), add (to, literal) as operands + const uint32_t num_operands = inst->NumOperands(); + for (uint32_t i = 1; i < num_operands; i += 2) { + Operand op = inst->GetOperand(i); + if (op.words[0] == from) { // add new pair of operands: (to, literal) + inst->AddOperand( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to})); + op = inst->GetOperand(i + 1); + inst->AddOperand(std::move(op)); + } + } + context->AnalyzeUses(inst); + break; + } + default: + assert(false && "Unexpected decoration instruction"); + } + } +} + +void DecorationManager::CloneDecorations( + uint32_t from, uint32_t to, + const std::vector& decorations_to_copy) { + const auto decoration_list = id_to_decoration_insts_.find(from); + if (decoration_list == id_to_decoration_insts_.end()) return; + auto context = module_->context(); + for (Instruction* inst : decoration_list->second.direct_decorations) { + if (std::find(decorations_to_copy.begin(), decorations_to_copy.end(), + spv::Decoration(inst->GetSingleWordInOperand(1))) == + decorations_to_copy.end()) { + continue; + } + + // Clone decoration and change |target-id| to |to|. + std::unique_ptr new_inst(inst->Clone(module_->context())); + new_inst->SetInOperand(0, {to}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + + // We need to copy the list of instructions as ForgetUses and AnalyzeUses are + // going to modify it. + std::vector indirect_decorations = + decoration_list->second.indirect_decorations; + for (Instruction* inst : indirect_decorations) { + switch (inst->opcode()) { + case spv::Op::OpGroupDecorate: + CloneDecorations(inst->GetSingleWordInOperand(0), to, + decorations_to_copy); + break; + case spv::Op::OpGroupMemberDecorate: { + assert(false && "The source id is not suppose to be a type."); + break; + } + default: + assert(false && "Unexpected decoration instruction"); + } + } +} + +void DecorationManager::RemoveDecoration(Instruction* inst) { + const auto remove_from_container = [inst](std::vector& v) { + v.erase(std::remove(v.begin(), v.end(), inst), v.end()); + }; + + switch (inst->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorate: { + const auto target_id = inst->GetSingleWordInOperand(0u); + auto const iter = id_to_decoration_insts_.find(target_id); + if (iter == id_to_decoration_insts_.end()) return; + remove_from_container(iter->second.direct_decorations); + } break; + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: { + const uint32_t stride = + inst->opcode() == spv::Op::OpGroupDecorate ? 1u : 2u; + for (uint32_t i = 1u; i < inst->NumInOperands(); i += stride) { + const auto target_id = inst->GetSingleWordInOperand(i); + auto const iter = id_to_decoration_insts_.find(target_id); + if (iter == id_to_decoration_insts_.end()) continue; + remove_from_container(iter->second.indirect_decorations); + } + const auto group_id = inst->GetSingleWordInOperand(0u); + auto const iter = id_to_decoration_insts_.find(group_id); + if (iter == id_to_decoration_insts_.end()) return; + remove_from_container(iter->second.decorate_insts); + } break; + default: + break; + } +} + +bool operator==(const DecorationManager& lhs, const DecorationManager& rhs) { + return lhs.id_to_decoration_insts_ == rhs.id_to_decoration_insts_; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.h new file mode 100644 index 00000000..2be016a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/decoration_manager.h @@ -0,0 +1,212 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DECORATION_MANAGER_H_ +#define SOURCE_OPT_DECORATION_MANAGER_H_ + +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +// A class for analyzing and managing decorations in an Module. +class DecorationManager { + public: + // Constructs a decoration manager from the given |module| + explicit DecorationManager(Module* module) : module_(module) { + AnalyzeDecorations(); + } + DecorationManager() = delete; + + // Removes all decorations (direct and through groups) where |pred| is + // true and that apply to |id| so that they no longer apply to |id|. Returns + // true if something changed. + // + // If |id| is part of a group, it will be removed from the group if it + // does not use all of the group's decorations, or, if there are no + // decorations that apply to the group. + // + // If decoration groups become empty, the |OpGroupDecorate| and + // |OpGroupMemberDecorate| instructions will be killed. + // + // Decoration instructions that apply directly to |id| will be killed. + // + // If |id| is a decoration group and all of the group's decorations are + // removed, then the |OpGroupDecorate| and + // |OpGroupMemberDecorate| for the group will be killed, but not the defining + // |OpDecorationGroup| instruction. + bool RemoveDecorationsFrom( + uint32_t id, std::function pred = + [](const Instruction&) { return true; }); + + // Removes all decorations from the result id of |inst|. + // + // NOTE: This is only meant to be called from ir_context, as only metadata + // will be removed, and no actual instruction. + void RemoveDecoration(Instruction* inst); + + // Returns a vector of all decorations affecting |id|. If a group is applied + // to |id|, the decorations of that group are returned rather than the group + // decoration instruction. If |include_linkage| is not set, linkage + // decorations won't be returned. + std::vector GetDecorationsFor(uint32_t id, + bool include_linkage); + std::vector GetDecorationsFor(uint32_t id, + bool include_linkage) const; + // Returns whether two IDs have the same decorations. Two + // spv::Op::OpGroupDecorate instructions that apply the same decorations but + // to different IDs, still count as being the same. + bool HaveTheSameDecorations(uint32_t id1, uint32_t id2) const; + + // Returns whether two IDs have the same decorations. Two + // spv::Op::OpGroupDecorate instructions that apply the same decorations but + // to different IDs, still count as being the same. + bool HaveSubsetOfDecorations(uint32_t id1, uint32_t id2) const; + + // Returns whether the two decorations instructions are the same and are + // applying the same decorations; unless |ignore_target| is false, the targets + // to which they are applied to does not matter, except for the member part. + // + // This is only valid for OpDecorate, OpMemberDecorate and OpDecorateId; it + // will return false for other opcodes. + bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2, + bool ignore_target) const; + + // Returns whether a decoration instruction for |id| with decoration + // |decoration| exists or not. + bool HasDecoration(uint32_t id, uint32_t decoration) const; + bool HasDecoration(uint32_t id, spv::Decoration decoration) const; + + // |f| is run on each decoration instruction for |id| with decoration + // |decoration|. Processed are all decorations which target |id| either + // directly or indirectly by Decoration Groups. + void ForEachDecoration(uint32_t id, uint32_t decoration, + std::function f) const; + + // |f| is run on each decoration instruction for |id| with decoration + // |decoration|. Processes all decoration which target |id| either directly or + // indirectly through decoration groups. If |f| returns false, iteration is + // terminated and this function returns false. + bool WhileEachDecoration(uint32_t id, uint32_t decoration, + std::function f) const; + + // |f| is run on each decoration instruction for |id| with decoration + // |decoration|. Processes all decoration which target |id| either directly or + // indirectly through decoration groups. If |f| returns true, iteration is + // terminated and this function returns true. Otherwise returns false. + bool FindDecoration(uint32_t id, uint32_t decoration, + std::function f); + + // Clone all decorations from one id |from|. + // The cloned decorations are assigned to the given id |to| and are + // added to the module. The purpose is to decorate cloned instructions. + // This function does not check if the id |to| is already decorated. + void CloneDecorations(uint32_t from, uint32_t to); + + // Same as above, but only clone the decoration if the decoration operand is + // in |decorations_to_copy|. This function has the extra restriction that + // |from| and |to| must not be an object, not a type. + void CloneDecorations( + uint32_t from, uint32_t to, + const std::vector& decorations_to_copy); + + // Informs the decoration manager of a new decoration that it needs to track. + void AddDecoration(Instruction* inst); + + // Add decoration with |opcode| and operands |opnds|. + void AddDecoration(spv::Op opcode, const std::vector opnds); + + // Add |decoration| of |inst_id| to module. + void AddDecoration(uint32_t inst_id, uint32_t decoration); + + // Add |decoration, decoration_value| of |inst_id| to module. + void AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value); + + // Add |decoration, decoration_value| of |inst_id, member| to module. + void AddMemberDecoration(uint32_t inst_id, uint32_t member, + uint32_t decoration, uint32_t decoration_value); + + friend bool operator==(const DecorationManager&, const DecorationManager&); + friend bool operator!=(const DecorationManager& lhs, + const DecorationManager& rhs) { + return !(lhs == rhs); + } + + private: + // Analyzes the defs and uses in the given |module| and populates data + // structures in this class. Does nothing if |module| is nullptr. + void AnalyzeDecorations(); + + template + std::vector InternalGetDecorationsFor(uint32_t id, bool include_linkage); + + // Tracks decoration information of an ID. + struct TargetData { + std::vector direct_decorations; // All decorate + // instructions applied + // to the tracked ID. + std::vector indirect_decorations; // All instructions + // applying a group to + // the tracked ID. + std::vector decorate_insts; // All decorate instructions + // applying the decorations + // of the tracked ID to + // targets. + // It is empty if the + // tracked ID is not a + // group. + }; + + friend bool operator==(const TargetData& lhs, const TargetData& rhs) { + if (!std::is_permutation(lhs.direct_decorations.begin(), + lhs.direct_decorations.end(), + rhs.direct_decorations.begin())) { + return false; + } + if (!std::is_permutation(lhs.indirect_decorations.begin(), + lhs.indirect_decorations.end(), + rhs.indirect_decorations.begin())) { + return false; + } + if (!std::is_permutation(lhs.decorate_insts.begin(), + lhs.decorate_insts.end(), + rhs.decorate_insts.begin())) { + return false; + } + return true; + } + + // Mapping from ids to the instructions applying a decoration to those ids. + // In other words, for each id you get all decoration instructions + // referencing that id, be it directly (spv::Op::OpDecorate, + // spv::Op::OpMemberDecorate and spv::Op::OpDecorateId), or indirectly + // (spv::Op::OpGroupDecorate, spv::Op::OpMemberGroupDecorate). + std::unordered_map id_to_decoration_insts_; + // The enclosing module. + Module* module_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DECORATION_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.cpp new file mode 100644 index 00000000..d54fdb65 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.cpp @@ -0,0 +1,313 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/def_use_manager.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +void DefUseManager::AnalyzeInstDef(Instruction* inst) { + const uint32_t def_id = inst->result_id(); + if (def_id != 0) { + auto iter = id_to_def_.find(def_id); + if (iter != id_to_def_.end()) { + // Clear the original instruction that defining the same result id of the + // new instruction. + ClearInst(iter->second); + } + id_to_def_[def_id] = inst; + } else { + ClearInst(inst); + } +} + +void DefUseManager::AnalyzeInstUse(Instruction* inst) { + // Create entry for the given instruction. Note that the instruction may + // not have any in-operands. In such cases, we still need a entry for those + // instructions so this manager knows it has seen the instruction later. + auto* used_ids = &inst_to_used_ids_[inst]; + if (used_ids->size()) { + EraseUseRecordsOfOperandIds(inst); + used_ids = &inst_to_used_ids_[inst]; + } + used_ids->clear(); // It might have existed before. + + for (uint32_t i = 0; i < inst->NumOperands(); ++i) { + switch (inst->GetOperand(i).type) { + // For any id type but result id type + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: { + uint32_t use_id = inst->GetSingleWordOperand(i); + Instruction* def = GetDef(use_id); + assert(def && "Definition is not registered."); + id_to_users_.insert(UserEntry{def, inst}); + used_ids->push_back(use_id); + } break; + default: + break; + } + } +} + +void DefUseManager::AnalyzeInstDefUse(Instruction* inst) { + AnalyzeInstDef(inst); + AnalyzeInstUse(inst); + // Analyze lines last otherwise they will be cleared when inst is + // cleared by preceding two calls + for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst); +} + +void DefUseManager::UpdateDefUse(Instruction* inst) { + const uint32_t def_id = inst->result_id(); + if (def_id != 0) { + auto iter = id_to_def_.find(def_id); + if (iter == id_to_def_.end()) { + AnalyzeInstDef(inst); + } + } + AnalyzeInstUse(inst); +} + +Instruction* DefUseManager::GetDef(uint32_t id) { + auto iter = id_to_def_.find(id); + if (iter == id_to_def_.end()) return nullptr; + return iter->second; +} + +const Instruction* DefUseManager::GetDef(uint32_t id) const { + const auto iter = id_to_def_.find(id); + if (iter == id_to_def_.end()) return nullptr; + return iter->second; +} + +DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin( + const Instruction* def) const { + return id_to_users_.lower_bound( + UserEntry{const_cast(def), nullptr}); +} + +bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const IdToUsersMap::const_iterator& cached_end, + const Instruction* inst) const { + return (iter != cached_end && iter->def == inst); +} + +bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const Instruction* inst) const { + return UsersNotEnd(iter, id_to_users_.end(), inst); +} + +bool DefUseManager::WhileEachUser( + const Instruction* def, const std::function& f) const { + // Ensure that |def| has been registered. + assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) && + "Definition is not registered."); + if (!def->HasResultId()) return true; + + auto end = id_to_users_.end(); + for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { + if (!f(iter->user)) return false; + } + return true; +} + +bool DefUseManager::WhileEachUser( + uint32_t id, const std::function& f) const { + return WhileEachUser(GetDef(id), f); +} + +void DefUseManager::ForEachUser( + const Instruction* def, const std::function& f) const { + WhileEachUser(def, [&f](Instruction* user) { + f(user); + return true; + }); +} + +void DefUseManager::ForEachUser( + uint32_t id, const std::function& f) const { + ForEachUser(GetDef(id), f); +} + +bool DefUseManager::WhileEachUse( + const Instruction* def, + const std::function& f) const { + // Ensure that |def| has been registered. + assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) && + "Definition is not registered."); + if (!def->HasResultId()) return true; + + auto end = id_to_users_.end(); + for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { + Instruction* user = iter->user; + for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { + const Operand& op = user->GetOperand(idx); + if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { + if (def->result_id() == op.words[0]) { + if (!f(user, idx)) return false; + } + } + } + } + return true; +} + +bool DefUseManager::WhileEachUse( + uint32_t id, const std::function& f) const { + return WhileEachUse(GetDef(id), f); +} + +void DefUseManager::ForEachUse( + const Instruction* def, + const std::function& f) const { + WhileEachUse(def, [&f](Instruction* user, uint32_t index) { + f(user, index); + return true; + }); +} + +void DefUseManager::ForEachUse( + uint32_t id, const std::function& f) const { + ForEachUse(GetDef(id), f); +} + +uint32_t DefUseManager::NumUsers(const Instruction* def) const { + uint32_t count = 0; + ForEachUser(def, [&count](Instruction*) { ++count; }); + return count; +} + +uint32_t DefUseManager::NumUsers(uint32_t id) const { + return NumUsers(GetDef(id)); +} + +uint32_t DefUseManager::NumUses(const Instruction* def) const { + uint32_t count = 0; + ForEachUse(def, [&count](Instruction*, uint32_t) { ++count; }); + return count; +} + +uint32_t DefUseManager::NumUses(uint32_t id) const { + return NumUses(GetDef(id)); +} + +std::vector DefUseManager::GetAnnotations(uint32_t id) const { + std::vector annos; + const Instruction* def = GetDef(id); + if (!def) return annos; + + ForEachUser(def, [&annos](Instruction* user) { + if (IsAnnotationInst(user->opcode())) { + annos.push_back(user); + } + }); + return annos; +} + +void DefUseManager::AnalyzeDefUse(Module* module) { + if (!module) return; + // Analyze all the defs before any uses to catch forward references. + module->ForEachInst( + std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1), + true); + module->ForEachInst( + std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1), + true); +} + +void DefUseManager::ClearInst(Instruction* inst) { + auto iter = inst_to_used_ids_.find(inst); + if (iter != inst_to_used_ids_.end()) { + EraseUseRecordsOfOperandIds(inst); + if (inst->result_id() != 0) { + // Remove all uses of this inst. + auto users_begin = UsersBegin(inst); + auto end = id_to_users_.end(); + auto new_end = users_begin; + for (; UsersNotEnd(new_end, end, inst); ++new_end) { + } + id_to_users_.erase(users_begin, new_end); + id_to_def_.erase(inst->result_id()); + } + } +} + +void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { + // Go through all ids used by this instruction, remove this instruction's + // uses of them. + auto iter = inst_to_used_ids_.find(inst); + if (iter != inst_to_used_ids_.end()) { + for (auto use_id : iter->second) { + id_to_users_.erase( + UserEntry{GetDef(use_id), const_cast(inst)}); + } + inst_to_used_ids_.erase(iter); + } +} + +bool CompareAndPrintDifferences(const DefUseManager& lhs, + const DefUseManager& rhs) { + bool same = true; + + if (lhs.id_to_def_ != rhs.id_to_def_) { + for (auto p : lhs.id_to_def_) { + if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { + printf("Diff in id_to_def: missing value in rhs\n"); + } + } + for (auto p : rhs.id_to_def_) { + if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { + printf("Diff in id_to_def: missing value in lhs\n"); + } + } + same = false; + } + + if (lhs.id_to_users_ != rhs.id_to_users_) { + for (auto p : lhs.id_to_users_) { + if (rhs.id_to_users_.count(p) == 0) { + printf("Diff in id_to_users: missing value in rhs\n"); + } + } + for (auto p : rhs.id_to_users_) { + if (lhs.id_to_users_.count(p) == 0) { + printf("Diff in id_to_users: missing value in lhs\n"); + } + } + same = false; + } + + if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { + for (auto p : lhs.inst_to_used_ids_) { + if (rhs.inst_to_used_ids_.count(p.first) == 0) { + printf("Diff in inst_to_used_ids: missing value in rhs\n"); + } + } + for (auto p : rhs.inst_to_used_ids_) { + if (lhs.inst_to_used_ids_.count(p.first) == 0) { + printf("Diff in inst_to_used_ids: missing value in lhs\n"); + } + } + same = false; + } + + return same; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.h new file mode 100644 index 00000000..13cf9bd3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/def_use_manager.h @@ -0,0 +1,230 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEF_USE_MANAGER_H_ +#define SOURCE_OPT_DEF_USE_MANAGER_H_ + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { +namespace analysis { + +// Definition should never be null. User can be null, however, such an entry +// should be used only for searching (e.g. all users of a particular definition) +// and never stored in a container. +struct UserEntry { + Instruction* def; + Instruction* user; +}; + +inline bool operator==(const UserEntry& lhs, const UserEntry& rhs) { + return lhs.def == rhs.def && lhs.user == rhs.user; +} + +// Orders UserEntry for use in associative containers (i.e. less than ordering). +// +// The definition of an UserEntry is treated as the major key and the users as +// the minor key so that all the users of a particular definition are +// consecutive in a container. +// +// A null user always compares less than a real user. This is done to provide +// easy values to search for the beginning of the users of a particular +// definition (i.e. using {def, nullptr}). +struct UserEntryLess { + bool operator()(const UserEntry& lhs, const UserEntry& rhs) const { + // If lhs.def and rhs.def are both null, fall through to checking the + // second entries. + if (!lhs.def && rhs.def) return true; + if (lhs.def && !rhs.def) return false; + + // If neither definition is null, then compare unique ids. + if (lhs.def && rhs.def) { + if (lhs.def->unique_id() < rhs.def->unique_id()) return true; + if (rhs.def->unique_id() < lhs.def->unique_id()) return false; + } + + // Return false on equality. + if (!lhs.user && !rhs.user) return false; + if (!lhs.user) return true; + if (!rhs.user) return false; + + // If neither user is null then compare unique ids. + return lhs.user->unique_id() < rhs.user->unique_id(); + } +}; + +// A class for analyzing and managing defs and uses in an Module. +class DefUseManager { + public: + using IdToDefMap = std::unordered_map; + + // Constructs a def-use manager from the given |module|. All internal messages + // will be communicated to the outside via the given message |consumer|. This + // instance only keeps a reference to the |consumer|, so the |consumer| should + // outlive this instance. + DefUseManager(Module* module) { AnalyzeDefUse(module); } + + DefUseManager(const DefUseManager&) = delete; + DefUseManager(DefUseManager&&) = delete; + DefUseManager& operator=(const DefUseManager&) = delete; + DefUseManager& operator=(DefUseManager&&) = delete; + + // Analyzes the defs in the given |inst|. + void AnalyzeInstDef(Instruction* inst); + + // Analyzes the uses in the given |inst|. + // + // All operands of |inst| must be analyzed as defs. + void AnalyzeInstUse(Instruction* inst); + + // Analyzes the defs and uses in the given |inst|. + void AnalyzeInstDefUse(Instruction* inst); + + // Returns the def instruction for the given |id|. If there is no instruction + // defining |id|, returns nullptr. + Instruction* GetDef(uint32_t id); + const Instruction* GetDef(uint32_t id) const; + + // Runs the given function |f| on each unique user instruction of |def| (or + // |id|). + // + // If one instruction uses |def| in multiple operands, that instruction will + // only be visited once. + // + // |def| (or |id|) must be registered as a definition. + void ForEachUser(const Instruction* def, + const std::function& f) const; + void ForEachUser(uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique user instruction of |def| (or + // |id|). If |f| returns false, iteration is terminated and this function + // returns false. + // + // If one instruction uses |def| in multiple operands, that instruction will + // be only be visited once. + // + // |def| (or |id|) must be registered as a definition. + bool WhileEachUser(const Instruction* def, + const std::function& f) const; + bool WhileEachUser(uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique use of |def| (or + // |id|). + // + // If one instruction uses |def| in multiple operands, each operand will be + // visited separately. + // + // |def| (or |id|) must be registered as a definition. + void ForEachUse( + const Instruction* def, + const std::function& f) const; + void ForEachUse( + uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique use of |def| (or + // |id|). If |f| returns false, iteration is terminated and this function + // returns false. + // + // If one instruction uses |def| in multiple operands, each operand will be + // visited separately. + // + // |def| (or |id|) must be registered as a definition. + bool WhileEachUse( + const Instruction* def, + const std::function& f) const; + bool WhileEachUse( + uint32_t id, + const std::function& f) const; + + // Returns the number of users of |def| (or |id|). + uint32_t NumUsers(const Instruction* def) const; + uint32_t NumUsers(uint32_t id) const; + + // Returns the number of uses of |def| (or |id|). + uint32_t NumUses(const Instruction* def) const; + uint32_t NumUses(uint32_t id) const; + + // Returns the annotation instrunctions which are a direct use of the given + // |id|. This means when the decorations are applied through decoration + // group(s), this function will just return the OpGroupDecorate + // instruction(s) which refer to the given id as an operand. The OpDecorate + // instructions which decorate the decoration group will not be returned. + std::vector GetAnnotations(uint32_t id) const; + + // Returns the map from ids to their def instructions. + const IdToDefMap& id_to_defs() const { return id_to_def_; } + + // Clear the internal def-use record of the given instruction |inst|. This + // method will update the use information of the operand ids of |inst|. The + // record: |inst| uses an |id|, will be removed from the use records of |id|. + // If |inst| defines an result id, the use record of this result id will also + // be removed. Does nothing if |inst| was not analyzed before. + void ClearInst(Instruction* inst); + + // Erases the records that a given instruction uses its operand ids. + void EraseUseRecordsOfOperandIds(const Instruction* inst); + + friend bool CompareAndPrintDifferences(const DefUseManager&, + const DefUseManager&); + + // If |inst| has not already been analysed, then analyses its definition and + // uses. + void UpdateDefUse(Instruction* inst); + + private: + using IdToUsersMap = std::set; + using InstToUsedIdsMap = + std::unordered_map>; + + // Returns the first location that {|def|, nullptr} could be inserted into the + // users map without violating ordering. + IdToUsersMap::const_iterator UsersBegin(const Instruction* def) const; + + // Returns true if |iter| has not reached the end of |def|'s users. + // + // In the first version |iter| is compared against the end of the map for + // validity before other checks. In the second version, |iter| is compared + // against |cached_end| for validity before other checks. This allows caching + // the map's end which is a performance improvement on some platforms. + bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const Instruction* def) const; + bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const IdToUsersMap::const_iterator& cached_end, + const Instruction* def) const; + + // Analyzes the defs and uses in the given |module| and populates data + // structures in this class. Does nothing if |module| is nullptr. + void AnalyzeDefUse(Module* module); + + IdToDefMap id_to_def_; // Mapping from ids to their definitions + IdToUsersMap id_to_users_; // Mapping from ids to their users + // Mapping from instructions to the ids used in the instruction. + InstToUsedIdsMap inst_to_used_ids_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEF_USE_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.cpp new file mode 100644 index 00000000..124a3d3a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.cpp @@ -0,0 +1,465 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/desc_sroa.h" + +#include "source/opt/desc_sroa_util.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +bool IsDecorationBinding(Instruction* inst) { + if (inst->opcode() != spv::Op::OpDecorate) return false; + return spv::Decoration(inst->GetSingleWordInOperand(1u)) == + spv::Decoration::Binding; +} + +} // namespace + +Pass::Status DescriptorScalarReplacement::Process() { + bool modified = false; + std::vector vars_to_kill; + + for (Instruction& var : context()->types_values()) { + bool is_candidate = + flatten_arrays_ && descsroautil::IsDescriptorArray(context(), &var); + is_candidate |= flatten_composites_ && + descsroautil::IsDescriptorStruct(context(), &var); + if (is_candidate) { + modified = true; + if (!ReplaceCandidate(&var)) { + return Status::Failure; + } + vars_to_kill.push_back(&var); + } + } + + for (Instruction* var : vars_to_kill) { + context()->KillInst(var); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { + std::vector access_chain_work_list; + std::vector load_work_list; + std::vector entry_point_work_list; + bool failed = !get_def_use_mgr()->WhileEachUser( + var->result_id(), [this, &access_chain_work_list, &load_work_list, + &entry_point_work_list](Instruction* use) { + if (use->opcode() == spv::Op::OpName) { + return true; + } + + if (use->IsDecoration()) { + return true; + } + + switch (use->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + access_chain_work_list.push_back(use); + return true; + case spv::Op::OpLoad: + load_work_list.push_back(use); + return true; + case spv::Op::OpEntryPoint: + entry_point_work_list.push_back(use); + return true; + default: + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + return true; + }); + + if (failed) { + return false; + } + + for (Instruction* use : access_chain_work_list) { + if (!ReplaceAccessChain(var, use)) { + return false; + } + } + for (Instruction* use : load_work_list) { + if (!ReplaceLoadedValue(var, use)) { + return false; + } + } + for (Instruction* use : entry_point_work_list) { + if (!ReplaceEntryPoint(var, use)) { + return false; + } + } + return true; +} + +bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, + Instruction* use) { + if (use->NumInOperands() <= 1) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + + const analysis::Constant* const_index = + descsroautil::GetAccessChainIndexAsConst(context(), use); + if (const_index == nullptr) { + context()->EmitErrorMessage("Variable cannot be replaced: invalid index", + use); + return false; + } + + uint32_t idx = const_index->GetU32(); + uint32_t replacement_var = GetReplacementVariable(var, idx); + + if (use->NumInOperands() == 2) { + // We are not indexing into the replacement variable. We can replaces the + // access chain with the replacement variable itself. + context()->ReplaceAllUsesWith(use->result_id(), replacement_var); + context()->KillInst(use); + return true; + } + + // We need to build a new access chain with the replacement variable as the + // base address. + Instruction::OperandList new_operands; + + // Same result id and result type. + new_operands.emplace_back(use->GetOperand(0)); + new_operands.emplace_back(use->GetOperand(1)); + + // Use the replacement variable as the base address. + new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}}); + + // Drop the first index because it is consumed by the replacement, and copy + // the rest. + for (uint32_t i = 4; i < use->NumOperands(); i++) { + new_operands.emplace_back(use->GetOperand(i)); + } + + use->ReplaceOperands(new_operands); + context()->UpdateDefUse(use); + return true; +} + +bool DescriptorScalarReplacement::ReplaceEntryPoint(Instruction* var, + Instruction* use) { + // Build a new |OperandList| for |use| that removes |var| and adds its + // replacement variables. + Instruction::OperandList new_operands; + + // Copy all operands except |var|. + bool found = false; + for (uint32_t idx = 0; idx < use->NumOperands(); idx++) { + Operand& op = use->GetOperand(idx); + if (op.type == SPV_OPERAND_TYPE_ID && op.words[0] == var->result_id()) { + found = true; + } else { + new_operands.emplace_back(op); + } + } + + if (!found) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + + // Add all new replacement variables. + uint32_t num_replacement_vars = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + for (uint32_t i = 0; i < num_replacement_vars; i++) { + new_operands.push_back( + {SPV_OPERAND_TYPE_ID, {GetReplacementVariable(var, i)}}); + } + + use->ReplaceOperands(new_operands); + context()->UpdateDefUse(use); + return true; +} + +uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, + uint32_t idx) { + auto replacement_vars = replacement_variables_.find(var); + if (replacement_vars == replacement_variables_.end()) { + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + replacement_vars = + replacement_variables_ + .insert({var, std::vector(number_of_elements, 0)}) + .first; + } + + if (replacement_vars->second[idx] == 0) { + replacement_vars->second[idx] = CreateReplacementVariable(var, idx); + } + + return replacement_vars->second[idx]; +} + +void DescriptorScalarReplacement::CopyDecorationsForNewVariable( + Instruction* old_var, uint32_t index, uint32_t new_var_id, + uint32_t new_var_ptr_type_id, const bool is_old_var_array, + const bool is_old_var_struct, Instruction* old_var_type) { + // Handle OpDecorate and OpDecorateString instructions. + for (auto old_decoration : + get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { + uint32_t new_binding = 0; + if (IsDecorationBinding(old_decoration)) { + new_binding = GetNewBindingForElement( + old_decoration->GetSingleWordInOperand(2), index, new_var_ptr_type_id, + is_old_var_array, is_old_var_struct, old_var_type); + } + CreateNewDecorationForNewVariable(old_decoration, new_var_id, new_binding); + } + + // Handle OpMemberDecorate instructions. + for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor( + old_var_type->result_id(), true)) { + assert(old_decoration->opcode() == spv::Op::OpMemberDecorate); + if (old_decoration->GetSingleWordInOperand(1u) != index) continue; + CreateNewDecorationForMemberDecorate(old_decoration, new_var_id); + } +} + +uint32_t DescriptorScalarReplacement::GetNewBindingForElement( + uint32_t old_binding, uint32_t index, uint32_t new_var_ptr_type_id, + const bool is_old_var_array, const bool is_old_var_struct, + Instruction* old_var_type) { + if (is_old_var_array) { + return old_binding + index * GetNumBindingsUsedByType(new_var_ptr_type_id); + } + if (is_old_var_struct) { + // The binding offset that should be added is the sum of binding + // numbers used by previous members of the current struct. + uint32_t new_binding = old_binding; + for (uint32_t i = 0; i < index; ++i) { + new_binding += + GetNumBindingsUsedByType(old_var_type->GetSingleWordInOperand(i)); + } + return new_binding; + } + return old_binding; +} + +void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( + Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { + assert(old_decoration->opcode() == spv::Op::OpDecorate || + old_decoration->opcode() == spv::Op::OpDecorateString); + std::unique_ptr new_decoration(old_decoration->Clone(context())); + new_decoration->SetInOperand(0, {new_var_id}); + + if (IsDecorationBinding(new_decoration.get())) { + new_decoration->SetInOperand(2, {new_binding}); + } + context()->AddAnnotationInst(std::move(new_decoration)); +} + +void DescriptorScalarReplacement::CreateNewDecorationForMemberDecorate( + Instruction* old_member_decoration, uint32_t new_var_id) { + std::vector operands( + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {new_var_id}}}); + auto new_decorate_operand_begin = old_member_decoration->begin() + 2u; + auto new_decorate_operand_end = old_member_decoration->end(); + operands.insert(operands.end(), new_decorate_operand_begin, + new_decorate_operand_end); + get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, std::move(operands)); +} + +uint32_t DescriptorScalarReplacement::CreateReplacementVariable( + Instruction* var, uint32_t idx) { + // The storage class for the new variable is the same as the original. + spv::StorageClass storage_class = + static_cast(var->GetSingleWordInOperand(0)); + + // The type for the new variable will be a pointer to type of the elements of + // the array. + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); + const bool is_array = pointee_type_inst->opcode() == spv::Op::OpTypeArray; + const bool is_struct = pointee_type_inst->opcode() == spv::Op::OpTypeStruct; + assert((is_array || is_struct) && + "Variable should be a pointer to an array or structure."); + + uint32_t element_type_id = + is_array ? pointee_type_inst->GetSingleWordInOperand(0) + : pointee_type_inst->GetSingleWordInOperand(idx); + + uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType( + element_type_id, storage_class); + + // Create the variable. + uint32_t id = TakeNextId(); + std::unique_ptr variable( + new Instruction(context(), spv::Op::OpVariable, ptr_element_type_id, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(storage_class)}}})); + context()->AddGlobalValue(std::move(variable)); + + CopyDecorationsForNewVariable(var, idx, id, ptr_element_type_id, is_array, + is_struct, pointee_type_inst); + + // Create a new OpName for the replacement variable. + std::vector> names_to_add; + for (auto p : context()->GetNames(var->result_id())) { + Instruction* name_inst = p.second; + std::string name_str = utils::MakeString(name_inst->GetOperand(1).words); + if (is_array) { + name_str += "[" + utils::ToString(idx) + "]"; + } + if (is_struct) { + Instruction* member_name_inst = + context()->GetMemberName(pointee_type_inst->result_id(), idx); + name_str += "."; + if (member_name_inst) + name_str += utils::MakeString(member_name_inst->GetOperand(2).words); + else + // In case the member does not have a name assigned to it, use the + // member index. + name_str += utils::ToString(idx); + } + + std::unique_ptr new_name(new Instruction( + context(), spv::Op::OpName, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}})); + Instruction* new_name_inst = new_name.get(); + get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst); + names_to_add.push_back(std::move(new_name)); + } + + // We shouldn't add the new names when we are iterating over name ranges + // above. We can add all the new names now. + for (auto& new_name : names_to_add) + context()->AddDebug2Inst(std::move(new_name)); + + return id; +} + +uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( + uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + + // If it's a pointer, look at the underlying type. + if (type_inst->opcode() == spv::Op::OpTypePointer) { + type_id = type_inst->GetSingleWordInOperand(1); + type_inst = get_def_use_mgr()->GetDef(type_id); + } + + // Arrays consume N*M binding numbers where N is the array length, and M is + // the number of bindings used by each array element. + if (type_inst->opcode() == spv::Op::OpTypeArray) { + uint32_t element_type_id = type_inst->GetSingleWordInOperand(0); + uint32_t length_id = type_inst->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context()->get_constant_mgr()->FindDeclaredConstant(length_id); + // OpTypeArray's length must always be a constant + assert(length_const != nullptr); + uint32_t num_elems = length_const->GetU32(); + return num_elems * GetNumBindingsUsedByType(element_type_id); + } + + // The number of bindings consumed by a structure is the sum of the bindings + // used by its members. + if (type_inst->opcode() == spv::Op::OpTypeStruct && + !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) { + uint32_t sum = 0; + for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) + sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); + return sum; + } + + // All other types are considered to take up 1 binding number. + return 1; +} + +bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var, + Instruction* value) { + // |var| is the global variable that has to be eliminated (OpVariable). + // |value| is the OpLoad instruction that has loaded |var|. + // The function expects all users of |value| to be OpCompositeExtract + // instructions. Otherwise the function returns false with an error message. + assert(value->opcode() == spv::Op::OpLoad); + assert(value->GetSingleWordInOperand(0) == var->result_id()); + std::vector work_list; + bool failed = !get_def_use_mgr()->WhileEachUser( + value->result_id(), [this, &work_list](Instruction* use) { + if (use->opcode() != spv::Op::OpCompositeExtract) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + work_list.push_back(use); + return true; + }); + + if (failed) { + return false; + } + + for (Instruction* use : work_list) { + if (!ReplaceCompositeExtract(var, use)) { + return false; + } + } + + // All usages of the loaded value have been killed. We can kill the OpLoad. + context()->KillInst(value); + return true; +} + +bool DescriptorScalarReplacement::ReplaceCompositeExtract( + Instruction* var, Instruction* extract) { + assert(extract->opcode() == spv::Op::OpCompositeExtract); + // We're currently only supporting extractions of one index at a time. If we + // need to, we can handle cases with multiple indexes in the future. + if (extract->NumInOperands() != 2) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", extract); + return false; + } + + uint32_t replacement_var = + GetReplacementVariable(var, extract->GetSingleWordInOperand(1)); + + // The result type of the OpLoad is the same as the result type of the + // OpCompositeExtract. + uint32_t load_id = TakeNextId(); + std::unique_ptr load( + new Instruction(context(), spv::Op::OpLoad, extract->type_id(), load_id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {replacement_var}}})); + Instruction* load_instr = load.get(); + get_def_use_mgr()->AnalyzeInstDefUse(load_instr); + context()->set_instr_block(load_instr, context()->get_instr_block(extract)); + extract->InsertBefore(std::move(load)); + context()->ReplaceAllUsesWith(extract->result_id(), load_id); + context()->KillInst(extract); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.h b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.h new file mode 100644 index 00000000..d6af4df5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa.h @@ -0,0 +1,159 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DESC_SROA_H_ +#define SOURCE_OPT_DESC_SROA_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class DescriptorScalarReplacement : public Pass { + public: + DescriptorScalarReplacement(bool flatten_composites, bool flatten_arrays) + : flatten_composites_(flatten_composites), + flatten_arrays_(flatten_arrays) {} + + const char* name() const override { + if (flatten_composites_ && flatten_arrays_) + return "descriptor-scalar-replacement"; + if (flatten_composites_) return "descriptor-compososite-scalar-replacement"; + return "descriptor-array-scalar-replacement"; + } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces all references to |var| by new variables, one for each element of + // the array |var|. The binding for the new variables corresponding to + // element i will be the binding of |var| plus i. Returns true if successful. + bool ReplaceCandidate(Instruction* var); + + // Replaces the base address |var| in the OpAccessChain or + // OpInBoundsAccessChain instruction |use| by the variable that the access + // chain accesses. The first index in |use| must be an |OpConstant|. Returns + // |true| if successful. + bool ReplaceAccessChain(Instruction* var, Instruction* use); + + // Replaces the given compososite variable |var| loaded by OpLoad |value| with + // replacement variables, one for each component that's accessed in the + // shader. Assumes that |value| is only used by OpCompositeExtract + // instructions, one index at a time. Returns true on success, and false + // otherwise. + bool ReplaceLoadedValue(Instruction* var, Instruction* value); + + // Replaces the given composite variable |var| in the OpEntryPoint with the + // new replacement variables, one for each element of the array |var|. Returns + // |true| if successful, and |false| otherwise. + bool ReplaceEntryPoint(Instruction* var, Instruction* use); + + // Replaces the given OpCompositeExtract |extract| and all of its references + // with an OpLoad of a replacement variable. |var| is the variable with + // composite type whose value is being used by |extract|. Assumes that + // |extract| is extracting one index only. Returns true on success, and false + // otherwise. + bool ReplaceCompositeExtract(Instruction* var, Instruction* extract); + + // Returns the id of the variable that will be used to replace the |idx|th + // element of |var|. The variable is created if it has not already been + // created. + uint32_t GetReplacementVariable(Instruction* var, uint32_t idx); + + // Returns the id of a new variable that can be used to replace the |idx|th + // element of |var|. + uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx); + + // Returns the number of bindings used by the given |type_id|. + // All types are considered to use 1 binding slot, except: + // 1- A pointer type consumes as many binding numbers as its pointee. + // 2- An array of size N consumes N*M binding numbers, where M is the number + // of bindings used by each array element. + // 3- The number of bindings consumed by a structure is the sum of the + // bindings used by its members. + uint32_t GetNumBindingsUsedByType(uint32_t type_id); + + // Copy all of the decorations of variable |old_var| and make them as + // decorations for the new variable whose id is |new_var_id|. The new variable + // is supposed to replace |index|th element of |old_var|. + // |new_var_ptr_type_id| is the id of the pointer to the type of the new + // variable. |is_old_var_array| is true if |old_var| has an array type. + // |is_old_var_struct| is true if |old_var| has a structure type. + // |old_var_type| is the pointee type of |old_var|. + void CopyDecorationsForNewVariable(Instruction* old_var, uint32_t index, + uint32_t new_var_id, + uint32_t new_var_ptr_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Get the new binding number for a new variable that will be replaced with an + // |index|th element of an old variable. The old variable has |old_binding| + // as its binding number. |ptr_elem_type_id| the id of the pointer to the + // element type. |is_old_var_array| is true if the old variable has an array + // type. |is_old_var_struct| is true if the old variable has a structure type. + // |old_var_type| is the pointee type of the old variable. + uint32_t GetNewBindingForElement(uint32_t old_binding, uint32_t index, + uint32_t ptr_elem_type_id, + const bool is_old_var_array, + const bool is_old_var_struct, + Instruction* old_var_type); + + // Create a new OpDecorate(String) instruction by cloning |old_decoration|. + // The new OpDecorate(String) instruction will be used for a variable whose id + // is |new_var_ptr_type_id|. If |old_decoration| is a decoration for a + // binding, the new OpDecorate(String) instruction will have |new_binding| as + // its binding. + void CreateNewDecorationForNewVariable(Instruction* old_decoration, + uint32_t new_var_id, + uint32_t new_binding); + + // Create a new OpDecorate instruction whose operand is the same as an + // OpMemberDecorate instruction |old_member_decoration| except Target operand. + // The Target operand of the new OpDecorate instruction will be |new_var_id|. + void CreateNewDecorationForMemberDecorate(Instruction* old_decoration, + uint32_t new_var_id); + + // A map from an OpVariable instruction to the set of variables that will be + // used to replace it. The entry |replacement_variables_[var][i]| is the id of + // a variable that will be used in the place of the ith element of the + // array |var|. If the entry is |0|, then the variable has not been + // created yet. + std::map> replacement_variables_; + + bool flatten_composites_; + bool flatten_arrays_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.cpp new file mode 100644 index 00000000..62d94764 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/desc_sroa_util.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kOpAccessChainInOperandIndexes = 1; + +// Returns the length of array type |type|. +uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) { + assert(type->opcode() == spv::Op::OpTypeArray && "type must be array"); + uint32_t length_id = type->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant(length_id); + assert(length_const != nullptr); + return length_const->GetU32(); +} + +bool HasDescriptorDecorations(IRContext* context, Instruction* var) { + const auto& decoration_mgr = context->get_decoration_mgr(); + return decoration_mgr->HasDecoration( + var->result_id(), uint32_t(spv::Decoration::DescriptorSet)) && + decoration_mgr->HasDecoration(var->result_id(), + uint32_t(spv::Decoration::Binding)); +} + +Instruction* GetVariableType(IRContext* context, Instruction* var) { + if (var->opcode() != spv::Op::OpVariable) { + return nullptr; + } + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + if (ptr_type_inst->opcode() != spv::Op::OpTypePointer) { + return nullptr; + } + + uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); + return context->get_def_use_mgr()->GetDef(var_type_id); +} + +} // namespace + +namespace descsroautil { + +bool IsDescriptorArray(IRContext* context, Instruction* var) { + Instruction* var_type_inst = GetVariableType(context, var); + if (var_type_inst == nullptr) return false; + return var_type_inst->opcode() == spv::Op::OpTypeArray && + HasDescriptorDecorations(context, var); +} + +bool IsDescriptorStruct(IRContext* context, Instruction* var) { + Instruction* var_type_inst = GetVariableType(context, var); + if (var_type_inst == nullptr) return false; + + while (var_type_inst->opcode() == spv::Op::OpTypeArray) { + var_type_inst = context->get_def_use_mgr()->GetDef( + var_type_inst->GetInOperand(0).AsId()); + } + + if (var_type_inst->opcode() != spv::Op::OpTypeStruct) return false; + + // All structures with descriptor assignments must be replaced by variables, + // one for each of their members - with the exceptions of buffers. + if (IsTypeOfStructuredBuffer(context, var_type_inst)) { + return false; + } + + return HasDescriptorDecorations(context, var); +} + +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) { + if (type->opcode() != spv::Op::OpTypeStruct) { + return false; + } + + // All buffers have offset decorations for members of their structure types. + // This is how we distinguish it from a structure of descriptors. + return context->get_decoration_mgr()->HasDecoration( + type->result_id(), uint32_t(spv::Decoration::Offset)); +} + +const analysis::Constant* GetAccessChainIndexAsConst( + IRContext* context, Instruction* access_chain) { + if (access_chain->NumInOperands() <= 1) { + return nullptr; + } + uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain); + const analysis::Constant* idx_const = + context->get_constant_mgr()->FindDeclaredConstant(idx_id); + return idx_const; +} + +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) { + assert(access_chain->NumInOperands() > 1 && + "OpAccessChain does not have Indexes operand"); + return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes); +} + +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var) { + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context->get_def_use_mgr()->GetDef(pointee_type_id); + if (pointee_type_inst->opcode() == spv::Op::OpTypeArray) { + return GetLengthOfArrayType(context, pointee_type_inst); + } + assert(pointee_type_inst->opcode() == spv::Op::OpTypeStruct && + "Variable should be a pointer to an array or structure."); + return pointee_type_inst->NumInOperands(); +} + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.h b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.h new file mode 100644 index 00000000..04233565 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/desc_sroa_util.h @@ -0,0 +1,58 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_ +#define SOURCE_OPT_DESC_SROA_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for the descriptor array SROA. +namespace descsroautil { + +// Returns true if |var| is an OpVariable instruction that represents a +// descriptor array. +bool IsDescriptorArray(IRContext* context, Instruction* var); + +// Returns true if |var| is an OpVariable instruction that represents a +// struct containing descriptors. +bool IsDescriptorStruct(IRContext* context, Instruction* var); + +// Returns true if |type| is a type that could be used for a structured buffer +// as opposed to a type that would be used for a structure of resource +// descriptors. +bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type); + +// Returns the first index of the OpAccessChain instruction |access_chain| as +// a constant. Returns nullptr if it is not a constant. +const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context, + Instruction* access_chain); + +// Returns the number of elements of an OpVariable instruction |var| whose type +// must be a pointer to an array or a struct. +uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context, + Instruction* var); + +// Returns the first Indexes operand id of the OpAccessChain or +// OpInBoundsAccessChain instruction |access_chain|. The access chain must have +// at least 1 index. +uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain); + +} // namespace descsroautil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_UTIL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.cpp new file mode 100644 index 00000000..eb6dfc9e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dominator_analysis.h" + +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +BasicBlock* DominatorAnalysisBase::CommonDominator(BasicBlock* b1, + BasicBlock* b2) const { + if (!b1 || !b2) return nullptr; + + std::unordered_set seen; + BasicBlock* block = b1; + while (block && seen.insert(block).second) { + block = ImmediateDominator(block); + } + + block = b2; + while (block && !seen.count(block)) { + block = ImmediateDominator(block); + } + + return block; +} + +bool DominatorAnalysisBase::Dominates(Instruction* a, Instruction* b) const { + if (!a || !b) { + return false; + } + + if (a == b) { + return true; + } + + BasicBlock* bb_a = a->context()->get_instr_block(a); + BasicBlock* bb_b = b->context()->get_instr_block(b); + + if (bb_a != bb_b) { + return tree_.Dominates(bb_a, bb_b); + } + + const Instruction* current = a; + const Instruction* other = b; + + if (tree_.IsPostDominator()) { + std::swap(current, other); + } + + // We handle OpLabel instructions explicitly since they are not stored in the + // instruction list. + if (current->opcode() == spv::Op::OpLabel) { + return true; + } + + while ((current = current->NextNode())) { + if (current == other) { + return true; + } + } + + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.h new file mode 100644 index 00000000..a94120a5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_analysis.h @@ -0,0 +1,138 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DOMINATOR_ANALYSIS_H_ +#define SOURCE_OPT_DOMINATOR_ANALYSIS_H_ + +#include +#include + +#include "source/opt/dominator_tree.h" + +namespace spvtools { +namespace opt { + +// Interface to perform dominator or postdominator analysis on a given function. +class DominatorAnalysisBase { + public: + explicit DominatorAnalysisBase(bool is_post_dom) : tree_(is_post_dom) {} + + // Calculates the dominator (or postdominator) tree for given function |f|. + inline void InitializeTree(const CFG& cfg, const Function* f) { + tree_.InitializeTree(cfg, f); + } + + // Returns true if BasicBlock |a| dominates BasicBlock |b|. + inline bool Dominates(const BasicBlock* a, const BasicBlock* b) const { + if (!a || !b) return false; + return Dominates(a->id(), b->id()); + } + + // Returns true if BasicBlock |a| dominates BasicBlock |b|. Same as above only + // using the BasicBlock IDs. + inline bool Dominates(uint32_t a, uint32_t b) const { + return tree_.Dominates(a, b); + } + + // Returns true if instruction |a| dominates instruction |b|. + bool Dominates(Instruction* a, Instruction* b) const; + + // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. + inline bool StrictlyDominates(const BasicBlock* a, + const BasicBlock* b) const { + if (!a || !b) return false; + return StrictlyDominates(a->id(), b->id()); + } + + // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. Same as + // above only using the BasicBlock IDs. + inline bool StrictlyDominates(uint32_t a, uint32_t b) const { + return tree_.StrictlyDominates(a, b); + } + + // Returns the immediate dominator of |node| or returns nullptr if it is has + // no dominator. + inline BasicBlock* ImmediateDominator(const BasicBlock* node) const { + if (!node) return nullptr; + return tree_.ImmediateDominator(node); + } + + // Returns the immediate dominator of |node_id| or returns nullptr if it is + // has no dominator. Same as above but operates on IDs. + inline BasicBlock* ImmediateDominator(uint32_t node_id) const { + return tree_.ImmediateDominator(node_id); + } + + // Returns true if |node| is reachable from the entry. + inline bool IsReachable(const BasicBlock* node) const { + if (!node) return false; + return tree_.ReachableFromRoots(node->id()); + } + + // Returns true if |node_id| is reachable from the entry. + inline bool IsReachable(uint32_t node_id) const { + return tree_.ReachableFromRoots(node_id); + } + + // Dump the tree structure into the given |out| stream in the dot format. + inline void DumpAsDot(std::ostream& out) const { tree_.DumpTreeAsDot(out); } + + // Returns true if this is a postdomiator tree. + inline bool IsPostDominator() const { return tree_.IsPostDominator(); } + + // Returns the tree itself for manual operations, such as traversing the + // roots. + // For normal dominance relationships the methods above should be used. + inline DominatorTree& GetDomTree() { return tree_; } + inline const DominatorTree& GetDomTree() const { return tree_; } + + // Force the dominator tree to be removed + inline void ClearTree() { tree_.ClearTree(); } + + // Applies the std::function |func| to dominator tree nodes in dominator + // order. + void Visit(std::function func) { + tree_.Visit(func); + } + + // Applies the std::function |func| to dominator tree nodes in dominator + // order. + void Visit(std::function func) const { + tree_.Visit(func); + } + + // Returns the most immediate basic block that dominates both |b1| and |b2|. + // If there is no such basic block, nullptr is returned. + BasicBlock* CommonDominator(BasicBlock* b1, BasicBlock* b2) const; + + protected: + DominatorTree tree_; +}; + +// Derived class for normal dominator analysis. +class DominatorAnalysis : public DominatorAnalysisBase { + public: + DominatorAnalysis() : DominatorAnalysisBase(false) {} +}; + +// Derived class for postdominator analysis. +class PostDominatorAnalysis : public DominatorAnalysisBase { + public: + PostDominatorAnalysis() : DominatorAnalysisBase(true) {} +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DOMINATOR_ANALYSIS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.cpp new file mode 100644 index 00000000..3c161a9b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.cpp @@ -0,0 +1,385 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/ir_context.h" + +// Calculates the dominator or postdominator tree for a given function. +// 1 - Compute the successors and predecessors for each BasicBlock. We add a +// placeholder node for the start node or for postdominators the exit. This node +// will point to all entry or all exit nodes. +// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of +// all BasicBlocks. Using the successors (or for postdominator, predecessors) +// calculated in step 1 to traverse the tree. +// 3 - Pass the list calculated in step 2 to the CFA::CalculateDominators using +// the predecessors list (or for postdominator, successors). This will give us a +// vector of BB pairs. Each BB and its immediate dominator. +// 4 - Using the list from 3 use those edges to build a tree of +// DominatorTreeNodes. Each node containing a link to the parent dominator and +// children which are dominated. +// 5 - Using the tree from 4, perform a depth first traversal to calculate the +// preorder and postorder index of each node. We use these indexes to compare +// nodes against each other for domination checks. + +namespace spvtools { +namespace opt { +namespace { + +// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform +// depth first search on generic BasicBlock types. Will call post and pre order +// user defined functions during traversal +// +// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode +// SuccessorLambda - Lamdba matching the signature of 'const +// std::vector*(const BBType *A)'. Will return a vector of the nodes +// succeeding BasicBlock A. +// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed AFTER their children. +// PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed BEFORE their children. +template +void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, + PreLambda pre, PostLambda post) { + auto no_terminal_blocks = [](const BBType*) { return false; }; + CFA::DepthFirstTraversal(bb, successors, pre, post, + no_terminal_blocks); +} + +// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform +// depth first search on generic BasicBlock types. This overload is for only +// performing user defined post order. +// +// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode +// SuccessorLambda - Lamdba matching the signature of 'const +// std::vector*(const BBType *A)'. Will return a vector of the nodes +// succeeding BasicBlock A. +// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed after their children. +template +void DepthFirstSearchPostOrder(const BBType* bb, SuccessorLambda successors, + PostLambda post) { + // Ignore preorder operation. + auto nop_preorder = [](const BBType*) {}; + DepthFirstSearch(bb, successors, nop_preorder, post); +} + +// Small type trait to get the function class type. +template +struct GetFunctionClass { + using FunctionType = Function; +}; + +// Helper class to compute predecessors and successors for each Basic Block in a +// function. Through GetPredFunctor and GetSuccessorFunctor it provides an +// interface to get the successor and predecessor lists for each basic +// block. This is required by the DepthFirstTraversal and ComputeDominator +// functions which take as parameter an std::function returning the successors +// and predecessors respectively. +// +// When computing the post-dominator tree, all edges are inverted. So successors +// returned by this class will be predecessors in the original CFG. +template +class BasicBlockSuccessorHelper { + // This should eventually become const BasicBlock. + using BasicBlock = BBType; + using Function = typename GetFunctionClass::FunctionType; + + using BasicBlockListTy = std::vector; + using BasicBlockMapTy = + std::unordered_map; + + public: + // For compliance with the dominance tree computation, entry nodes are + // connected to a single placeholder node. + BasicBlockSuccessorHelper(Function& func, + const BasicBlock* placeholder_start_node, + bool post); + + // CFA::CalculateDominators requires std::vector. + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + + // Returns the list of predecessor functions. + GetBlocksFunction GetPredFunctor() { + return [this](const BasicBlock* bb) { + BasicBlockListTy* v = &this->predecessors_[bb]; + return v; + }; + } + + // Returns a vector of the list of successor nodes from a given node. + GetBlocksFunction GetSuccessorFunctor() { + return [this](const BasicBlock* bb) { + BasicBlockListTy* v = &this->successors_[bb]; + return v; + }; + } + + private: + bool invert_graph_; + BasicBlockMapTy successors_; + BasicBlockMapTy predecessors_; + + // Build the successors and predecessors map for each basic blocks |f|. + // If |invert_graph_| is true, all edges are reversed (successors becomes + // predecessors and vice versa). + // For convenience, the start of the graph is |placeholder_start_node|. + // The dominator tree construction requires a unique entry node, which cannot + // be guaranteed for the postdominator graph. The |placeholder_start_node| BB + // is here to gather all entry nodes. + void CreateSuccessorMap(Function& f, + const BasicBlock* placeholder_start_node); +}; + +template +BasicBlockSuccessorHelper::BasicBlockSuccessorHelper( + Function& func, const BasicBlock* placeholder_start_node, bool invert) + : invert_graph_(invert) { + CreateSuccessorMap(func, placeholder_start_node); +} + +template +void BasicBlockSuccessorHelper::CreateSuccessorMap( + Function& f, const BasicBlock* placeholder_start_node) { + IRContext* context = f.DefInst().context(); + + if (invert_graph_) { + // For the post dominator tree, we see the inverted graph. + // successors_ in the inverted graph are the predecessors in the CFG. + // The tree construction requires 1 entry point, so we add a placeholder + // node that is connected to all function exiting basic blocks. An exiting + // basic block is a block with an OpKill, OpUnreachable, OpReturn, + // OpReturnValue, or OpTerminateInvocation as terminator instruction. + for (BasicBlock& bb : f) { + if (bb.hasSuccessor()) { + BasicBlockListTy& pred_list = predecessors_[&bb]; + const auto& const_bb = bb; + const_bb.ForEachSuccessorLabel( + [this, &pred_list, &bb, context](const uint32_t successor_id) { + BasicBlock* succ = context->get_instr_block(successor_id); + // Inverted graph: our successors in the CFG + // are our predecessors in the inverted graph. + this->successors_[succ].push_back(&bb); + pred_list.push_back(succ); + }); + } else { + successors_[placeholder_start_node].push_back(&bb); + predecessors_[&bb].push_back( + const_cast(placeholder_start_node)); + } + } + } else { + successors_[placeholder_start_node].push_back(f.entry().get()); + predecessors_[f.entry().get()].push_back( + const_cast(placeholder_start_node)); + for (BasicBlock& bb : f) { + BasicBlockListTy& succ_list = successors_[&bb]; + + const auto& const_bb = bb; + const_bb.ForEachSuccessorLabel([&](const uint32_t successor_id) { + BasicBlock* succ = context->get_instr_block(successor_id); + succ_list.push_back(succ); + predecessors_[succ].push_back(&bb); + }); + } + } +} + +} // namespace + +bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const { + if (a == b) return false; + return Dominates(a, b); +} + +bool DominatorTree::StrictlyDominates(const BasicBlock* a, + const BasicBlock* b) const { + return DominatorTree::StrictlyDominates(a->id(), b->id()); +} + +bool DominatorTree::StrictlyDominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const { + if (a == b) return false; + return Dominates(a, b); +} + +bool DominatorTree::Dominates(uint32_t a, uint32_t b) const { + // Check that both of the inputs are actual nodes. + const DominatorTreeNode* a_node = GetTreeNode(a); + const DominatorTreeNode* b_node = GetTreeNode(b); + if (!a_node || !b_node) return false; + + return Dominates(a_node, b_node); +} + +bool DominatorTree::Dominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const { + if (!a || !b) return false; + // Node A dominates node B if they are the same. + if (a == b) return true; + + return a->dfs_num_pre_ < b->dfs_num_pre_ && + a->dfs_num_post_ > b->dfs_num_post_; +} + +bool DominatorTree::Dominates(const BasicBlock* A, const BasicBlock* B) const { + return Dominates(A->id(), B->id()); +} + +BasicBlock* DominatorTree::ImmediateDominator(const BasicBlock* A) const { + return ImmediateDominator(A->id()); +} + +BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const { + // Check that A is a valid node in the tree. + auto a_itr = nodes_.find(a); + if (a_itr == nodes_.end()) return nullptr; + + const DominatorTreeNode* node = &a_itr->second; + + if (node->parent_ == nullptr) { + return nullptr; + } + + return node->parent_->bb_; +} + +DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) { + DominatorTreeNode* dtn = nullptr; + + std::map::iterator node_iter = + nodes_.find(bb->id()); + if (node_iter == nodes_.end()) { + dtn = &nodes_.emplace(std::make_pair(bb->id(), DominatorTreeNode{bb})) + .first->second; + } else { + dtn = &node_iter->second; + } + + return dtn; +} + +void DominatorTree::GetDominatorEdges( + const Function* f, const BasicBlock* placeholder_start_node, + std::vector>* edges) { + // Each time the depth first traversal calls the postorder callback + // std::function we push that node into the postorder vector to create our + // postorder list. + std::vector postorder; + auto postorder_function = [&](const BasicBlock* b) { + postorder.push_back(b); + }; + + // CFA::CalculateDominators requires std::vector + // BB are derived from F, so we need to const cast it at some point + // no modification is made on F. + BasicBlockSuccessorHelper helper{ + *const_cast(f), placeholder_start_node, postdominator_}; + + // The successor function tells DepthFirstTraversal how to move to successive + // nodes by providing an interface to get a list of successor nodes from any + // given node. + auto successor_functor = helper.GetSuccessorFunctor(); + + // The predecessor functor does the same as the successor functor + // but for all nodes preceding a given node. + auto predecessor_functor = helper.GetPredFunctor(); + + // If we're building a post dominator tree we traverse the tree in reverse + // using the predecessor function in place of the successor function and vice + // versa. + DepthFirstSearchPostOrder(placeholder_start_node, successor_functor, + postorder_function); + *edges = CFA::CalculateDominators(postorder, predecessor_functor); +} + +void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) { + ClearTree(); + + // Skip over empty functions. + if (f->cbegin() == f->cend()) { + return; + } + + const BasicBlock* placeholder_start_node = + postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block(); + + // Get the immediate dominator for each node. + std::vector> edges; + GetDominatorEdges(f, placeholder_start_node, &edges); + + // Transform the vector into the tree structure which we can use to + // efficiently query dominance. + for (auto edge : edges) { + DominatorTreeNode* first = GetOrInsertNode(edge.first); + + if (edge.first == edge.second) { + if (std::find(roots_.begin(), roots_.end(), first) == roots_.end()) + roots_.push_back(first); + continue; + } + + DominatorTreeNode* second = GetOrInsertNode(edge.second); + + first->parent_ = second; + second->children_.push_back(first); + } + ResetDFNumbering(); +} + +void DominatorTree::ResetDFNumbering() { + int index = 0; + auto preFunc = [&index](const DominatorTreeNode* node) { + const_cast(node)->dfs_num_pre_ = ++index; + }; + + auto postFunc = [&index](const DominatorTreeNode* node) { + const_cast(node)->dfs_num_post_ = ++index; + }; + + auto getSucc = [](const DominatorTreeNode* node) { return &node->children_; }; + + for (auto root : roots_) DepthFirstSearch(root, getSucc, preFunc, postFunc); +} + +void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const { + out_stream << "digraph {\n"; + Visit([&out_stream](const DominatorTreeNode* node) { + // Print the node. + if (node->bb_) { + out_stream << node->bb_->id() << "[label=\"" << node->bb_->id() + << "\"];\n"; + } + + // Print the arrow from the parent to this node. Entry nodes will not have + // parents so draw them as children from the placeholder node. + if (node->parent_) { + out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id() + << ";\n"; + } + + // Return true to continue the traversal. + return true; + }); + out_stream << "}\n"; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.h b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.h new file mode 100644 index 00000000..1674b228 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/dominator_tree.h @@ -0,0 +1,305 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DOMINATOR_TREE_H_ +#define SOURCE_OPT_DOMINATOR_TREE_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { +// This helper struct forms the nodes in the tree, with each node containing its +// children. It also contains two values, for the pre and post indexes in the +// tree which are used to compare two nodes. +struct DominatorTreeNode { + explicit DominatorTreeNode(BasicBlock* bb) + : bb_(bb), + parent_(nullptr), + children_({}), + dfs_num_pre_(-1), + dfs_num_post_(-1) {} + + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + // depth first preorder iterator. + using df_iterator = TreeDFIterator; + using const_df_iterator = TreeDFIterator; + // depth first postorder iterator. + using post_iterator = PostOrderTreeDFIterator; + using const_post_iterator = PostOrderTreeDFIterator; + + iterator begin() { return children_.begin(); } + iterator end() { return children_.end(); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { return children_.begin(); } + const_iterator cend() const { return children_.end(); } + + // Depth first preorder iterator using this node as root. + df_iterator df_begin() { return df_iterator(this); } + df_iterator df_end() { return df_iterator(); } + const_df_iterator df_begin() const { return df_cbegin(); } + const_df_iterator df_end() const { return df_cend(); } + const_df_iterator df_cbegin() const { return const_df_iterator(this); } + const_df_iterator df_cend() const { return const_df_iterator(); } + + // Depth first postorder iterator using this node as root. + post_iterator post_begin() { return post_iterator::begin(this); } + post_iterator post_end() { return post_iterator::end(nullptr); } + const_post_iterator post_begin() const { return post_cbegin(); } + const_post_iterator post_end() const { return post_cend(); } + const_post_iterator post_cbegin() const { + return const_post_iterator::begin(this); + } + const_post_iterator post_cend() const { + return const_post_iterator::end(nullptr); + } + + inline uint32_t id() const { return bb_->id(); } + + BasicBlock* bb_; + DominatorTreeNode* parent_; + std::vector children_; + + // These indexes are used to compare two given nodes. A node is a child or + // grandchild of another node if its preorder index is greater than the + // first nodes preorder index AND if its postorder index is less than the + // first nodes postorder index. + int dfs_num_pre_; + int dfs_num_post_; +}; + +// A class representing a tree of BasicBlocks in a given function, where each +// node is dominated by its parent. +class DominatorTree { + public: + // Map OpLabel ids to dominator tree nodes + using DominatorTreeNodeMap = std::map; + using iterator = TreeDFIterator; + using const_iterator = TreeDFIterator; + using post_iterator = PostOrderTreeDFIterator; + using const_post_iterator = PostOrderTreeDFIterator; + + // List of DominatorTreeNode to define the list of roots + using DominatorTreeNodeList = std::vector; + using roots_iterator = DominatorTreeNodeList::iterator; + using roots_const_iterator = DominatorTreeNodeList::const_iterator; + + DominatorTree() : postdominator_(false) {} + explicit DominatorTree(bool post) : postdominator_(post) {} + + // Depth first iterators. + // Traverse the dominator tree in a depth first pre-order. + // The pseudo-block is ignored. + iterator begin() { return ++iterator(GetRoot()); } + iterator end() { return iterator(); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { return ++const_iterator(GetRoot()); } + const_iterator cend() const { return const_iterator(); } + + // Traverse the dominator tree in a depth first post-order. + // The pseudo-block is ignored. + post_iterator post_begin() { return post_iterator::begin(GetRoot()); } + post_iterator post_end() { return post_iterator::end(GetRoot()); } + const_post_iterator post_begin() const { return post_cbegin(); } + const_post_iterator post_end() const { return post_cend(); } + const_post_iterator post_cbegin() const { + return const_post_iterator::begin(GetRoot()); + } + const_post_iterator post_cend() const { + return const_post_iterator::end(GetRoot()); + } + + roots_iterator roots_begin() { return roots_.begin(); } + roots_iterator roots_end() { return roots_.end(); } + roots_const_iterator roots_begin() const { return roots_cbegin(); } + roots_const_iterator roots_end() const { return roots_cend(); } + roots_const_iterator roots_cbegin() const { return roots_.begin(); } + roots_const_iterator roots_cend() const { return roots_.end(); } + + // Get the unique root of the tree. + // It is guaranteed to work on a dominator tree. + // post-dominator might have a list. + DominatorTreeNode* GetRoot() { + assert(roots_.size() == 1); + return *roots_.begin(); + } + + const DominatorTreeNode* GetRoot() const { + assert(roots_.size() == 1); + return *roots_.begin(); + } + + const DominatorTreeNodeList& Roots() const { return roots_; } + + // Dumps the tree in the graphvis dot format into the |out_stream|. + void DumpTreeAsDot(std::ostream& out_stream) const; + + // Build the (post-)dominator tree for the given control flow graph + // |cfg| and the function |f|. |f| must exist in the |cfg|. Any + // existing data in the dominator tree will be overwritten + void InitializeTree(const CFG& cfg, const Function* f); + + // Check if the basic block |a| dominates the basic block |b|. + bool Dominates(const BasicBlock* a, const BasicBlock* b) const; + + // Check if the basic block id |a| dominates the basic block id |b|. + bool Dominates(uint32_t a, uint32_t b) const; + + // Check if the dominator tree node |a| dominates the dominator tree node |b|. + bool Dominates(const DominatorTreeNode* a, const DominatorTreeNode* b) const; + + // Check if the basic block |a| strictly dominates the basic block |b|. + bool StrictlyDominates(const BasicBlock* a, const BasicBlock* b) const; + + // Check if the basic block id |a| strictly dominates the basic block id |b|. + bool StrictlyDominates(uint32_t a, uint32_t b) const; + + // Check if the dominator tree node |a| strictly dominates the dominator tree + // node |b|. + bool StrictlyDominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const; + + // Returns the immediate dominator of basic block |a|. + BasicBlock* ImmediateDominator(const BasicBlock* A) const; + + // Returns the immediate dominator of basic block id |a|. + BasicBlock* ImmediateDominator(uint32_t a) const; + + // Returns true if the basic block |a| is reachable by this tree. A node would + // be unreachable if it cannot be reached by traversal from the start node or + // for a postdominator tree, cannot be reached from the exit nodes. + inline bool ReachableFromRoots(const BasicBlock* a) const { + if (!a) return false; + return ReachableFromRoots(a->id()); + } + + // Returns true if the basic block id |a| is reachable by this tree. + bool ReachableFromRoots(uint32_t a) const { + return GetTreeNode(a) != nullptr; + } + + // Returns true if this tree is a post dominator tree. + bool IsPostDominator() const { return postdominator_; } + + // Clean up the tree. + void ClearTree() { + nodes_.clear(); + roots_.clear(); + } + + // Applies the std::function |func| to all nodes in the dominator tree. + // Tree nodes are visited in a depth first pre-order. + bool Visit(std::function func) { + for (auto n : *this) { + if (!func(&n)) return false; + } + return true; + } + + // Applies the std::function |func| to all nodes in the dominator tree. + // Tree nodes are visited in a depth first pre-order. + bool Visit(std::function func) const { + for (auto n : *this) { + if (!func(&n)) return false; + } + return true; + } + + // Applies the std::function |func| to all nodes in the dominator tree from + // |node| downwards. The boolean return from |func| is used to determine + // whether or not the children should also be traversed. Tree nodes are + // visited in a depth first pre-order. + void VisitChildrenIf(std::function func, + iterator node) { + if (func(&*node)) { + for (auto n : *node) { + VisitChildrenIf(func, n->df_begin()); + } + } + } + + // Returns the DominatorTreeNode associated with the basic block |bb|. + // If the |bb| is unknown to the dominator tree, it returns null. + inline DominatorTreeNode* GetTreeNode(BasicBlock* bb) { + return GetTreeNode(bb->id()); + } + // Returns the DominatorTreeNode associated with the basic block |bb|. + // If the |bb| is unknown to the dominator tree, it returns null. + inline const DominatorTreeNode* GetTreeNode(BasicBlock* bb) const { + return GetTreeNode(bb->id()); + } + + // Returns the DominatorTreeNode associated with the basic block id |id|. + // If the id |id| is unknown to the dominator tree, it returns null. + inline DominatorTreeNode* GetTreeNode(uint32_t id) { + DominatorTreeNodeMap::iterator node_iter = nodes_.find(id); + if (node_iter == nodes_.end()) { + return nullptr; + } + return &node_iter->second; + } + // Returns the DominatorTreeNode associated with the basic block id |id|. + // If the id |id| is unknown to the dominator tree, it returns null. + inline const DominatorTreeNode* GetTreeNode(uint32_t id) const { + DominatorTreeNodeMap::const_iterator node_iter = nodes_.find(id); + if (node_iter == nodes_.end()) { + return nullptr; + } + return &node_iter->second; + } + + // Adds the basic block |bb| to the tree structure if it doesn't already + // exist. + DominatorTreeNode* GetOrInsertNode(BasicBlock* bb); + + // Recomputes the DF numbering of the tree. + void ResetDFNumbering(); + + private: + // Wrapper function which gets the list of pairs of each BasicBlocks to its + // immediately dominating BasicBlock and stores the result in the edges + // parameter. + // + // The |edges| vector will contain the dominator tree as pairs of nodes. + // The first node in the pair is a node in the graph. The second node in the + // pair is its immediate dominator. + // The root of the tree has themself as immediate dominator. + void GetDominatorEdges( + const Function* f, const BasicBlock* dummy_start_node, + std::vector>* edges); + + // The roots of the tree. + std::vector roots_; + + // Pairs each basic block id to the tree node containing that basic block. + DominatorTreeNodeMap nodes_; + + // True if this is a post dominator tree. + bool postdominator_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DOMINATOR_TREE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp new file mode 100644 index 00000000..500fd8af --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_constant_pass.h" + +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadConstantPass::Process() { + std::unordered_set working_list; + // Traverse all the instructions to get the initial set of dead constants as + // working list and count number of real uses for constants. Uses in + // annotation instructions do not count. + std::unordered_map use_counts; + std::vector constants = context()->GetConstants(); + for (auto* c : constants) { + uint32_t const_id = c->result_id(); + size_t count = 0; + context()->get_def_use_mgr()->ForEachUse( + const_id, [&count](Instruction* user, uint32_t index) { + (void)index; + spv::Op op = user->opcode(); + if (!(IsAnnotationInst(op) || IsDebug1Inst(op) || IsDebug2Inst(op) || + IsDebug3Inst(op))) { + ++count; + } + }); + use_counts[c] = count; + if (!count) { + working_list.insert(c); + } + } + + // Start from the constants with 0 uses, back trace through the def-use chain + // to find all dead constants. + std::unordered_set dead_consts; + while (!working_list.empty()) { + Instruction* inst = *working_list.begin(); + // Back propagate if the instruction contains IDs in its operands. + switch (inst->opcode()) { + case spv::Op::OpConstantComposite: + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpSpecConstantOp: + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + // SpecConstantOp instruction contains 'opcode' as its operand. Need + // to exclude such operands when decreasing uses. + if (inst->GetInOperand(i).type != SPV_OPERAND_TYPE_ID) { + continue; + } + uint32_t operand_id = inst->GetSingleWordInOperand(i); + Instruction* def_inst = + context()->get_def_use_mgr()->GetDef(operand_id); + // If the use_count does not have any count for the def_inst, + // def_inst must not be a constant, and should be ignored here. + if (!use_counts.count(def_inst)) { + continue; + } + // The number of uses should never be less then 0, so it can not be + // less than 1 before it decreases. + SPIRV_ASSERT(consumer(), use_counts[def_inst] > 0); + --use_counts[def_inst]; + if (!use_counts[def_inst]) { + working_list.insert(def_inst); + } + } + break; + default: + break; + } + dead_consts.insert(inst); + working_list.erase(inst); + } + + // Turn all dead instructions and uses of them to nop + for (auto* dc : dead_consts) { + context()->KillDef(dc->result_id()); + } + return dead_consts.empty() ? Status::SuccessWithoutChange + : Status::SuccessWithChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.h new file mode 100644 index 00000000..01692dbf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_constant_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadConstantPass : public Pass { + public: + const char* name() const override { return "eliminate-dead-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp new file mode 100644 index 00000000..a4655219 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_functions_util.h" + +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadFunctionsPass::Process() { + // Identify live functions first. Those that are not live + // are dead. + std::unordered_set live_function_set; + ProcessFunction mark_live = [&live_function_set](Function* fp) { + live_function_set.insert(fp); + return false; + }; + context()->ProcessReachableCallTree(mark_live); + + bool modified = false; + for (auto funcIter = get_module()->begin(); + funcIter != get_module()->end();) { + if (live_function_set.count(&*funcIter) == 0) { + modified = true; + funcIter = + eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter); + } else { + ++funcIter; + } + } + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.h new file mode 100644 index 00000000..6ed5c42b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadFunctionsPass : public MemPass { + public: + const char* name() const override { return "eliminate-dead-functions"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + void EliminateFunction(Function* func); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.cpp new file mode 100644 index 00000000..e95b7f6a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "eliminate_dead_functions_util.h" + +namespace spvtools { +namespace opt { + +namespace eliminatedeadfunctionsutil { + +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter) { + bool first_func = *func_iter == context->module()->begin(); + bool seen_func_end = false; + std::unordered_set to_kill; + (*func_iter) + ->ForEachInst( + [context, first_func, func_iter, &seen_func_end, + &to_kill](Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionEnd) { + seen_func_end = true; + } + // Move non-semantic instructions to the previous function or + // global values if this is the first function. + if (seen_func_end && inst->opcode() == spv::Op::OpExtInst) { + assert(inst->IsNonSemanticInstruction()); + if (to_kill.find(inst) != to_kill.end()) return; + std::unique_ptr clone(inst->Clone(context)); + // Clear uses of "inst" to in case this moves a dependent chain of + // instructions. + context->get_def_use_mgr()->ClearInst(inst); + context->AnalyzeDefUse(clone.get()); + if (first_func) { + context->AddGlobalValue(std::move(clone)); + } else { + auto prev_func_iter = *func_iter; + --prev_func_iter; + prev_func_iter->AddNonSemanticInstruction(std::move(clone)); + } + inst->ToNop(); + } else if (to_kill.find(inst) == to_kill.end()) { + context->CollectNonSemanticTree(inst, &to_kill); + context->KillInst(inst); + } + }, + true, true); + + for (auto* dead : to_kill) { + context->KillInst(dead); + } + + return func_iter->Erase(); +} + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.h new file mode 100644 index 00000000..9fcce956 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_functions_util.h @@ -0,0 +1,36 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functionality for eliminating functions that are not needed, for use +// by various analyses and passes. +namespace eliminatedeadfunctionsutil { + +// Removes all of the function's instructions, removes the function from the +// module, and returns the next iterator. +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter); + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.cpp new file mode 100644 index 00000000..5553a336 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.cpp @@ -0,0 +1,256 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_io_components_pass.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kAccessChainBaseInIdx = 0; +constexpr uint32_t kAccessChainIndex0InIdx = 1; +constexpr uint32_t kAccessChainIndex1InIdx = 2; +constexpr uint32_t kConstantValueInIdx = 0; +} // namespace + +Pass::Status EliminateDeadIOComponentsPass::Process() { + // Only process input and output variables + if (elim_sclass_ != spv::StorageClass::Input && + elim_sclass_ != spv::StorageClass::Output) { + if (consumer()) { + std::string message = + "EliminateDeadIOComponentsPass only valid for input and output " + "variables."; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return Status::Failure; + } + // If safe mode, only process Input variables in vertex shader + const auto stage = context()->GetStage(); + if (safe_mode_ && !(stage == spv::ExecutionModel::Vertex && + elim_sclass_ == spv::StorageClass::Input)) + return Status::SuccessWithoutChange; + // Current functionality assumes shader capability. + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return Status::SuccessWithoutChange; + // Current functionality assumes vert, frag, tesc, tese or geom shader. + // TODO(issue #4988): Add GLCompute. + if (stage != spv::ExecutionModel::Vertex && + stage != spv::ExecutionModel::Fragment && + stage != spv::ExecutionModel::TessellationControl && + stage != spv::ExecutionModel::TessellationEvaluation && + stage != spv::ExecutionModel::Geometry) + return Status::SuccessWithoutChange; + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + bool modified = false; + std::vector vars_to_move; + for (auto& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) { + continue; + } + analysis::Type* var_type = type_mgr->GetType(var.type_id()); + analysis::Pointer* ptr_type = var_type->AsPointer(); + if (ptr_type == nullptr) { + continue; + } + const auto sclass = ptr_type->storage_class(); + if (sclass != elim_sclass_) { + continue; + } + // For tesc, or input variables in tese or geom shaders, + // there is a outer per-vertex-array that must be ignored + // for the purposes of this analysis/optimization. Do the + // analysis on the inner type in these cases. + bool skip_first_index = false; + auto core_type = ptr_type->pointee_type(); + if (stage == spv::ExecutionModel::TessellationControl || + (sclass == spv::StorageClass::Input && + (stage == spv::ExecutionModel::TessellationEvaluation || + stage == spv::ExecutionModel::Geometry))) { + auto arr_type = core_type->AsArray(); + if (!arr_type) continue; + core_type = arr_type->element_type(); + skip_first_index = true; + } + const analysis::Array* arr_type = core_type->AsArray(); + if (arr_type != nullptr) { + // Only process array if input of vertex shader, or output of + // fragment shader. Otherwise, if one shader has a runtime index and the + // other does not, interface incompatibility can occur. + if (!((sclass == spv::StorageClass::Input && + stage == spv::ExecutionModel::Vertex) || + (sclass == spv::StorageClass::Output && + stage == spv::ExecutionModel::Fragment))) + continue; + unsigned arr_len_id = arr_type->LengthId(); + Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id); + if (arr_len_inst->opcode() != spv::Op::OpConstant) { + continue; + } + // SPIR-V requires array size is >= 1, so this works for signed or + // unsigned size. + unsigned original_max = + arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1; + unsigned max_idx = FindMaxIndex(var, original_max); + if (max_idx != original_max) { + ChangeArrayLength(var, max_idx + 1); + vars_to_move.push_back(&var); + modified = true; + } + continue; + } + const analysis::Struct* struct_type = core_type->AsStruct(); + if (struct_type == nullptr) continue; + const auto elt_types = struct_type->element_types(); + unsigned original_max = static_cast(elt_types.size()) - 1; + unsigned max_idx = FindMaxIndex(var, original_max, skip_first_index); + if (max_idx != original_max) { + ChangeIOVarStructLength(var, max_idx + 1); + vars_to_move.push_back(&var); + modified = true; + } + } + + // Move changed vars after their new type instruction to preserve backward + // referencing. + for (auto var : vars_to_move) { + auto type_id = var->type_id(); + auto type_inst = def_use_mgr->GetDef(type_id); + var->RemoveFromList(); + var->InsertAfter(type_inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +unsigned EliminateDeadIOComponentsPass::FindMaxIndex( + const Instruction& var, const unsigned original_max, + const bool skip_first_index) { + unsigned max = 0; + bool seen_non_const_ac = false; + assert(var.opcode() == spv::Op::OpVariable && "must be variable"); + context()->get_def_use_mgr()->WhileEachUser( + var.result_id(), [&max, &seen_non_const_ac, var, skip_first_index, + this](Instruction* use) { + auto use_opcode = use->opcode(); + if (use_opcode == spv::Op::OpLoad || use_opcode == spv::Op::OpStore || + use_opcode == spv::Op::OpCopyMemory || + use_opcode == spv::Op::OpCopyMemorySized || + use_opcode == spv::Op::OpCopyObject) { + seen_non_const_ac = true; + return false; + } + if (use->opcode() != spv::Op::OpAccessChain && + use->opcode() != spv::Op::OpInBoundsAccessChain) { + return true; + } + // OpAccessChain with no indices currently not optimized + if (use->NumInOperands() == 1 || + (skip_first_index && use->NumInOperands() == 2)) { + seen_non_const_ac = true; + return false; + } + const unsigned base_id = + use->GetSingleWordInOperand(kAccessChainBaseInIdx); + USE_ASSERT(base_id == var.result_id() && "unexpected base"); + const unsigned in_idx = skip_first_index ? kAccessChainIndex1InIdx + : kAccessChainIndex0InIdx; + const unsigned idx_id = use->GetSingleWordInOperand(in_idx); + Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id); + if (idx_inst->opcode() != spv::Op::OpConstant) { + seen_non_const_ac = true; + return false; + } + unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx); + if (value > max) max = value; + return true; + }); + return seen_non_const_ac ? original_max : max; +} + +void EliminateDeadIOComponentsPass::ChangeArrayLength(Instruction& arr_var, + unsigned length) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::Pointer* ptr_type = + type_mgr->GetType(arr_var.type_id())->AsPointer(); + const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray(); + assert(arr_ty && "expecting array type"); + uint32_t length_id = const_mgr->GetUIntConstId(length); + analysis::Array new_arr_ty(arr_ty->element_type(), + arr_ty->GetConstantLengthInfo(length_id, length)); + analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty); + analysis::Pointer new_ptr_ty(reg_new_arr_ty, ptr_type->storage_class()); + analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty); + uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty); + arr_var.SetResultType(new_ptr_ty_id); + def_use_mgr->AnalyzeInstUse(&arr_var); +} + +void EliminateDeadIOComponentsPass::ChangeIOVarStructLength(Instruction& io_var, + unsigned length) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Pointer* ptr_type = + type_mgr->GetType(io_var.type_id())->AsPointer(); + auto core_type = ptr_type->pointee_type(); + // Check for per-vertex-array of struct from tesc, tese and geom and grab + // embedded struct type. + const auto arr_type = core_type->AsArray(); + if (arr_type) core_type = arr_type->element_type(); + const analysis::Struct* struct_ty = core_type->AsStruct(); + assert(struct_ty && "expecting struct type"); + const auto orig_elt_types = struct_ty->element_types(); + std::vector new_elt_types; + for (unsigned u = 0; u < length; ++u) + new_elt_types.push_back(orig_elt_types[u]); + analysis::Struct new_struct_ty(new_elt_types); + uint32_t old_struct_ty_id = type_mgr->GetTypeInstruction(struct_ty); + std::vector decorations = + context()->get_decoration_mgr()->GetDecorationsFor(old_struct_ty_id, + true); + for (auto dec : decorations) { + if (dec->opcode() == spv::Op::OpMemberDecorate) { + uint32_t midx = dec->GetSingleWordInOperand(1); + if (midx >= length) continue; + } + type_mgr->AttachDecoration(*dec, &new_struct_ty); + } + // Clone name instructions for new struct type + analysis::Type* reg_new_str_ty = type_mgr->GetRegisteredType(&new_struct_ty); + uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_str_ty); + context()->CloneNames(old_struct_ty_id, new_struct_ty_id, length); + // Attach new type to var + analysis::Type* reg_new_var_ty = reg_new_str_ty; + if (arr_type) { + analysis::Array new_arr_ty(reg_new_var_ty, arr_type->length_info()); + reg_new_var_ty = type_mgr->GetRegisteredType(&new_arr_ty); + } + analysis::Pointer new_ptr_ty(reg_new_var_ty, elim_sclass_); + analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty); + uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty); + io_var.SetResultType(new_ptr_ty_id); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->AnalyzeInstUse(&io_var); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.h new file mode 100644 index 00000000..ef4dfb71 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_io_components_pass.h @@ -0,0 +1,75 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadIOComponentsPass : public Pass { + public: + explicit EliminateDeadIOComponentsPass(spv::StorageClass elim_sclass, + bool safe_mode = true) + : elim_sclass_(elim_sclass), safe_mode_(safe_mode) {} + + const char* name() const override { + return "eliminate-dead-input-components"; + } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Find the max constant used to index the variable declared by |var| + // through OpAccessChain or OpInBoundsAccessChain. If any non-constant + // indices or non-Op*AccessChain use of |var|, return |original_max|. + unsigned FindMaxIndex(const Instruction& var, const unsigned original_max, + const bool skip_first_index = false); + + // Change the length of the array |inst| to |length| + void ChangeArrayLength(Instruction& inst, unsigned length); + + // Change the length of the struct in |io_var| to |length|. |io_var| + // is either the struct or a per-vertex-array of the struct. + void ChangeIOVarStructLength(Instruction& io_var, unsigned length); + + // Storage class to be optimized. Must be Input or Output. + spv::StorageClass elim_sclass_; + + // Only make changes that will not cause interface incompatibility if done + // standalone. Currently this is only Input variables in vertex shaders. + bool safe_mode_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.cpp new file mode 100644 index 00000000..170f2706 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.cpp @@ -0,0 +1,711 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_members_pass.h" + +#include "ir_builder.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kRemovedMember = 0xFFFFFFFF; +constexpr uint32_t kSpecConstOpOpcodeIdx = 0; +constexpr uint32_t kArrayElementTypeIdx = 0; +} // namespace + +Pass::Status EliminateDeadMembersPass::Process() { + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return Status::SuccessWithoutChange; + + FindLiveMembers(); + if (RemoveDeadMembers()) { + return Status::SuccessWithChange; + } + return Status::SuccessWithoutChange; +} + +void EliminateDeadMembersPass::FindLiveMembers() { + // Until we have implemented the rewriting of OpSpecConsantOp instructions, + // we have to mark them as fully used just to be safe. + for (auto& inst : get_module()->types_values()) { + if (inst.opcode() == spv::Op::OpSpecConstantOp) { + switch (spv::Op(inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) { + case spv::Op::OpCompositeExtract: + MarkMembersAsLiveForExtract(&inst); + break; + case spv::Op::OpCompositeInsert: + // Nothing specific to do. + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } + } else if (inst.opcode() == spv::Op::OpVariable) { + switch (spv::StorageClass(inst.GetSingleWordInOperand(0))) { + case spv::StorageClass::Input: + case spv::StorageClass::Output: + MarkPointeeTypeAsFullUsed(inst.type_id()); + break; + default: + // Ignore structured buffers as layout(offset) qualifiers cannot be + // applied to structure fields + if (inst.IsVulkanStorageBufferVariable()) + MarkPointeeTypeAsFullUsed(inst.type_id()); + break; + } + } else if (inst.opcode() == spv::Op::OpTypePointer) { + uint32_t storage_class = inst.GetSingleWordInOperand(0); + if (storage_class == uint32_t(spv::StorageClass::PhysicalStorageBuffer)) { + MarkTypeAsFullyUsed(inst.GetSingleWordInOperand(1)); + } + } + } + + for (const Function& func : *get_module()) { + FindLiveMembers(func); + } +} + +void EliminateDeadMembersPass::FindLiveMembers(const Function& function) { + function.ForEachInst( + [this](const Instruction* inst) { FindLiveMembers(inst); }); +} + +void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpStore: + MarkMembersAsLiveForStore(inst); + break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + MarkMembersAsLiveForCopyMemory(inst); + break; + case spv::Op::OpCompositeExtract: + MarkMembersAsLiveForExtract(inst); + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + MarkMembersAsLiveForAccessChain(inst); + break; + case spv::Op::OpReturnValue: + // This should be an issue only if we are returning from the entry point. + // However, for now I will keep it more conservative because functions are + // often inlined leaving only the entry points. + MarkOperandTypeAsFullyUsed(inst, 0); + break; + case spv::Op::OpArrayLength: + MarkMembersAsLiveForArrayLength(inst); + break; + case spv::Op::OpLoad: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCompositeConstruct: + break; + default: + // This path is here for safety. All instructions that can reference + // structs in a function body should be handled above. However, this will + // keep the pass valid, but not optimal, as new instructions get added + // or if something was missed. + MarkStructOperandsAsFullyUsed(inst); + break; + } +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForStore( + const Instruction* inst) { + // We should only have to mark the members as live if the store is to + // memory that is read outside of the shader. Other passes can remove all + // store to memory that is not visible outside of the shader, so we do not + // complicate the code for now. + assert(inst->opcode() == spv::Op::OpStore); + uint32_t object_id = inst->GetSingleWordInOperand(1); + Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id); + uint32_t object_type_id = object_inst->type_id(); + MarkTypeAsFullyUsed(object_type_id); +} + +void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + assert(type_inst != nullptr); + + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + // Mark every member and its type as fully used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + used_members_[type_id].insert(i); + MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + } + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + MarkTypeAsFullyUsed( + type_inst->GetSingleWordInOperand(kArrayElementTypeIdx)); + break; + default: + break; + } +} + +void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) { + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer); + MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1)); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory( + const Instruction* inst) { + uint32_t target_id = inst->GetSingleWordInOperand(0); + Instruction* target_inst = get_def_use_mgr()->GetDef(target_id); + uint32_t pointer_type_id = target_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + MarkTypeAsFullyUsed(type_id); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForExtract( + const Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCompositeExtract || + (inst->opcode() == spv::Op::OpSpecConstantOp && + spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) == + spv::Op::OpCompositeExtract)); + + uint32_t first_operand = + (inst->opcode() == spv::Op::OpSpecConstantOp ? 1 : 0); + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand); + Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); + uint32_t type_id = composite_inst->type_id(); + + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + uint32_t member_idx = inst->GetSingleWordInOperand(i); + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + used_members_[type_id].insert(member_idx); + type_id = type_inst->GetSingleWordInOperand(member_idx); + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain( + const Instruction* inst) { + assert(inst->opcode() == spv::Op::OpAccessChain || + inst->opcode() == spv::Op::OpInBoundsAccessChain || + inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain); + + uint32_t pointer_id = inst->GetSingleWordInOperand(0); + Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); + uint32_t pointer_type_id = pointer_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + // For a pointer access chain, we need to skip the |element| index. It is not + // a reference to the member of a struct, and it does not change the type. + uint32_t i = (inst->opcode() == spv::Op::OpAccessChain || + inst->opcode() == spv::Op::OpInBoundsAccessChain + ? 1 + : 2); + for (; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: { + const analysis::IntConstant* member_idx = + const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) + ->AsIntConstant(); + assert(member_idx); + uint32_t index = + static_cast(member_idx->GetZeroExtendedValue()); + used_members_[type_id].insert(index); + type_id = type_inst->GetSingleWordInOperand(index); + } break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } +} + +void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed( + const Instruction* inst, uint32_t in_idx) { + uint32_t op_id = inst->GetSingleWordInOperand(in_idx); + Instruction* op_inst = get_def_use_mgr()->GetDef(op_id); + MarkTypeAsFullyUsed(op_inst->type_id()); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength( + const Instruction* inst) { + assert(inst->opcode() == spv::Op::OpArrayLength); + uint32_t object_id = inst->GetSingleWordInOperand(0); + Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); + uint32_t pointer_type_id = object_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + used_members_[type_id].insert(inst->GetSingleWordInOperand(1)); +} + +bool EliminateDeadMembersPass::RemoveDeadMembers() { + bool modified = false; + + // First update all of the OpTypeStruct instructions. + get_module()->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpTypeStruct: + modified |= UpdateOpTypeStruct(inst); + break; + default: + break; + } + }); + + // Now update all of the instructions that reference the OpTypeStructs. + get_module()->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpMemberName: + modified |= UpdateOpMemberNameOrDecorate(inst); + break; + case spv::Op::OpMemberDecorate: + modified |= UpdateOpMemberNameOrDecorate(inst); + break; + case spv::Op::OpGroupMemberDecorate: + modified |= UpdateOpGroupMemberDecorate(inst); + break; + case spv::Op::OpSpecConstantComposite: + case spv::Op::OpConstantComposite: + case spv::Op::OpCompositeConstruct: + modified |= UpdateConstantComposite(inst); + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + modified |= UpdateAccessChain(inst); + break; + case spv::Op::OpCompositeExtract: + modified |= UpdateCompsiteExtract(inst); + break; + case spv::Op::OpCompositeInsert: + modified |= UpdateCompositeInsert(inst); + break; + case spv::Op::OpArrayLength: + modified |= UpdateOpArrayLength(inst); + break; + case spv::Op::OpSpecConstantOp: + switch (spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) { + case spv::Op::OpCompositeExtract: + modified |= UpdateCompsiteExtract(inst); + break; + case spv::Op::OpCompositeInsert: + modified |= UpdateCompositeInsert(inst); + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } + break; + default: + break; + } + }); + return modified; +} + +bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpTypeStruct); + + const auto& live_members = used_members_[inst->result_id()]; + if (live_members.size() == inst->NumInOperands()) { + return false; + } + + Instruction::OperandList new_operands; + for (uint32_t idx : live_members) { + new_operands.emplace_back(inst->GetInOperand(idx)); + } + + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpMemberName || + inst->opcode() == spv::Op::OpMemberDecorate); + + uint32_t type_id = inst->GetSingleWordInOperand(0); + auto live_members = used_members_.find(type_id); + if (live_members == used_members_.end()) { + return false; + } + + uint32_t orig_member_idx = inst->GetSingleWordInOperand(1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); + + if (new_member_idx == kRemovedMember) { + context()->KillInst(inst); + return true; + } + + if (new_member_idx == orig_member_idx) { + return false; + } + + inst->SetInOperand(1, {new_member_idx}); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpGroupMemberDecorate); + + bool modified = false; + + Instruction::OperandList new_operands; + new_operands.emplace_back(inst->GetInOperand(0)); + for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { + uint32_t type_id = inst->GetSingleWordInOperand(i); + uint32_t member_idx = inst->GetSingleWordInOperand(i + 1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + + if (new_member_idx == kRemovedMember) { + modified = true; + continue; + } + + new_operands.emplace_back(inst->GetOperand(i)); + if (new_member_idx != member_idx) { + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + modified = true; + } else { + new_operands.emplace_back(inst->GetOperand(i + 1)); + } + } + + if (!modified) { + return false; + } + + if (new_operands.size() == 1) { + context()->KillInst(inst); + return true; + } + + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpSpecConstantComposite || + inst->opcode() == spv::Op::OpConstantComposite || + inst->opcode() == spv::Op::OpCompositeConstruct); + uint32_t type_id = inst->type_id(); + + bool modified = false; + Instruction::OperandList new_operands; + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + uint32_t new_idx = GetNewMemberIndex(type_id, i); + if (new_idx == kRemovedMember) { + modified = true; + } else { + new_operands.emplace_back(inst->GetInOperand(i)); + } + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return modified; +} + +bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpAccessChain || + inst->opcode() == spv::Op::OpInBoundsAccessChain || + inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain); + + uint32_t pointer_id = inst->GetSingleWordInOperand(0); + Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); + uint32_t pointer_type_id = pointer_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + Instruction::OperandList new_operands; + bool modified = false; + new_operands.emplace_back(inst->GetInOperand(0)); + + // For pointer access chains we want to copy the element operand. + if (inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) { + new_operands.emplace_back(inst->GetInOperand(1)); + } + + for (uint32_t i = static_cast(new_operands.size()); + i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: { + const analysis::IntConstant* member_idx = + const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) + ->AsIntConstant(); + assert(member_idx); + uint32_t orig_member_idx = + static_cast(member_idx->GetZeroExtendedValue()); + uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); + assert(new_member_idx != kRemovedMember); + if (orig_member_idx != new_member_idx) { + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping); + uint32_t const_id = + ir_builder.GetUintConstant(new_member_idx)->result_id(); + new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}})); + modified = true; + } else { + new_operands.emplace_back(inst->GetInOperand(i)); + } + // The type will have already been rewritten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + } break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + new_operands.emplace_back(inst->GetInOperand(i)); + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + break; + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id, + uint32_t member_idx) { + auto live_members = used_members_.find(type_id); + if (live_members == used_members_.end()) { + return member_idx; + } + + auto current_member = live_members->second.find(member_idx); + if (current_member == live_members->second.end()) { + return kRemovedMember; + } + + return static_cast( + std::distance(live_members->second.begin(), current_member)); +} + +bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCompositeExtract || + (inst->opcode() == spv::Op::OpSpecConstantOp && + spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) == + spv::Op::OpCompositeExtract)); + + uint32_t first_operand = 0; + if (inst->opcode() == spv::Op::OpSpecConstantOp) { + first_operand = 1; + } + uint32_t object_id = inst->GetSingleWordInOperand(first_operand); + Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); + uint32_t type_id = object_inst->type_id(); + + Instruction::OperandList new_operands; + bool modified = false; + for (uint32_t i = 0; i < first_operand + 1; i++) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { + uint32_t member_idx = inst->GetSingleWordInOperand(i); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + assert(new_member_idx != kRemovedMember); + if (member_idx != new_member_idx) { + modified = true; + } + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + // The type will have already been rewritten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCompositeInsert || + (inst->opcode() == spv::Op::OpSpecConstantOp && + spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) == + spv::Op::OpCompositeInsert)); + + uint32_t first_operand = 0; + if (inst->opcode() == spv::Op::OpSpecConstantOp) { + first_operand = 1; + } + + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1); + Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); + uint32_t type_id = composite_inst->type_id(); + + Instruction::OperandList new_operands; + bool modified = false; + + for (uint32_t i = 0; i < first_operand + 2; ++i) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) { + uint32_t member_idx = inst->GetSingleWordInOperand(i); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + if (new_member_idx == kRemovedMember) { + context()->KillInst(inst); + return true; + } + + if (member_idx != new_member_idx) { + modified = true; + } + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + // The type will have already been rewritten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) { + uint32_t struct_id = inst->GetSingleWordInOperand(0); + Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id); + uint32_t pointer_type_id = struct_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + uint32_t member_idx = inst->GetSingleWordInOperand(1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + assert(new_member_idx != kRemovedMember); + + if (member_idx == new_member_idx) { + return false; + } + + inst->SetInOperand(1, {new_member_idx}); + context()->UpdateDefUse(inst); + return true; +} + +void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed( + const Instruction* inst) { + if (inst->type_id() != 0) { + MarkTypeAsFullyUsed(inst->type_id()); + } + + inst->ForEachInId([this](const uint32_t* id) { + Instruction* instruction = get_def_use_mgr()->GetDef(*id); + if (instruction->type_id() != 0) { + MarkTypeAsFullyUsed(instruction->type_id()); + } + }); +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.h new file mode 100644 index 00000000..4feaa55f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_members_pass.h @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Remove unused members from structures. The remaining members will remain at +// the same offset. +class EliminateDeadMembersPass : public MemPass { + public: + const char* name() const override { return "eliminate-dead-members"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping; + } + + private: + // Populate |used_members_| with the member of structures that are live in the + // current context. + void FindLiveMembers(); + + // Add to |used_members_| the member of structures that are live in + // |function|. + void FindLiveMembers(const Function& function); + // Add to |used_members_| the member of structures that are live in |inst|. + void FindLiveMembers(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |OpStore| + // instruction |inst|. + void MarkMembersAsLiveForStore(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |OpCopyMemory*| + // instruction |inst|. + void MarkMembersAsLiveForCopyMemory(const Instruction* inst); + + // Add to |used_members_| the members that are live in the + // |OpCompositeExtract| instruction |inst|. + void MarkMembersAsLiveForExtract(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |Op*AccessChain| + // instruction |inst|. + void MarkMembersAsLiveForAccessChain(const Instruction* inst); + + // Add the member referenced by the OpArrayLength instruction |inst| to + // |uses_members_|. + void MarkMembersAsLiveForArrayLength(const Instruction* inst); + + // Remove dead members from structs and updates any instructions that need to + // be updated as a consequence. Return true if something changed. + bool RemoveDeadMembers(); + + // Update |inst|, which must be an |OpMemberName| or |OpMemberDecorate| + // instruction, so it references the correct member after the struct is + // updated. Return true if something changed. + bool UpdateOpMemberNameOrDecorate(Instruction* inst); + + // Update |inst|, which must be an |OpGroupMemberDecorate| instruction, so it + // references the correct member after the struct is updated. Return true if + // something changed. + bool UpdateOpGroupMemberDecorate(Instruction* inst); + + // Update the |OpTypeStruct| instruction |inst| my removing the members that + // are not live. Return true if something changed. + bool UpdateOpTypeStruct(Instruction* inst); + + // Update the |OpConstantComposite| instruction |inst| to match the change + // made to the type that was being generated. Return true if something + // changed. + bool UpdateConstantComposite(Instruction* inst); + + // Update the |Op*AccessChain| instruction |inst| to reference the correct + // members. All members referenced in the access chain must be live. This + // function must be called after the |OpTypeStruct| instruction for the type + // has been updated. Return true if something changed. + bool UpdateAccessChain(Instruction* inst); + + // Update the |OpCompositeExtract| instruction |inst| to reference the correct + // members. All members referenced in the instruction must be live. This + // function must be called after the |OpTypeStruct| instruction for the type + // has been updated. Return true if something changed. + bool UpdateCompsiteExtract(Instruction* inst); + + // Update the |OpCompositeInsert| instruction |inst| to reference the correct + // members. If the member being inserted is not live, then |inst| is killed. + // This function must be called after the |OpTypeStruct| instruction for the + // type has been updated. Return true if something changed. + bool UpdateCompositeInsert(Instruction* inst); + + // Update the |OpArrayLength| instruction |inst| to reference the correct + // member. The member referenced in the instruction must be live. Return true + // if something changed. + bool UpdateOpArrayLength(Instruction* inst); + + // Add all of the members of type |type_id| and members of any subtypes to + // |used_members_|. + void MarkTypeAsFullyUsed(uint32_t type_id); + + // Add all of the members of the type of the operand |in_idx| in |inst| and + // members of any subtypes to |uses_members_|. + void MarkOperandTypeAsFullyUsed(const Instruction* inst, uint32_t in_idx); + + // Return the index of the member that use to be the |member_idx|th member of + // |type_id|. If the member has been removed, |kRemovedMember| is returned. + uint32_t GetNewMemberIndex(uint32_t type_id, uint32_t member_idx); + + // A map from a type id to a set of indices representing the members of the + // type that are used, and must be kept. + std::unordered_map> used_members_; + void MarkStructOperandsAsFullyUsed(const Instruction* inst); + void MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.cpp new file mode 100644 index 00000000..e71032d4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.cpp @@ -0,0 +1,240 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_output_stores_pass.h" + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kDecorationLocationInIdx = 2; +constexpr uint32_t kOpDecorateMemberMemberInIdx = 1; +constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2; +constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3; +constexpr uint32_t kOpAccessChainIdx0InIdx = 1; +constexpr uint32_t kOpConstantValueInIdx = 0; +} // namespace + +Pass::Status EliminateDeadOutputStoresPass::Process() { + // Current functionality assumes shader capability + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return Status::SuccessWithoutChange; + Pass::Status status = DoDeadOutputStoreElimination(); + return status; +} + +void EliminateDeadOutputStoresPass::InitializeElimination() { + kill_list_.clear(); +} + +bool EliminateDeadOutputStoresPass::IsLiveBuiltin(uint32_t bi) { + return live_builtins_->find(bi) != live_builtins_->end(); +} + +bool EliminateDeadOutputStoresPass::AnyLocsAreLive(uint32_t start, + uint32_t count) { + auto finish = start + count; + for (uint32_t u = start; u < finish; ++u) { + if (live_locs_->find(u) != live_locs_->end()) return true; + } + return false; +} + +void EliminateDeadOutputStoresPass::KillAllStoresOfRef(Instruction* ref) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + if (ref->opcode() == spv::Op::OpStore) { + kill_list_.push_back(ref); + return; + } + assert((ref->opcode() == spv::Op::OpAccessChain || + ref->opcode() == spv::Op::OpInBoundsAccessChain) && + "unexpected use of output variable"); + def_use_mgr->ForEachUser(ref, [this](Instruction* user) { + if (user->opcode() == spv::Op::OpStore) kill_list_.push_back(user); + }); +} + +void EliminateDeadOutputStoresPass::KillAllDeadStoresOfLocRef( + Instruction* ref, Instruction* var) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr(); + analysis::LivenessManager* live_mgr = context()->get_liveness_mgr(); + // Find variable location if present. + uint32_t start_loc = 0; + auto var_id = var->result_id(); + bool no_loc = deco_mgr->WhileEachDecoration( + var_id, uint32_t(spv::Decoration::Location), + [&start_loc](const Instruction& deco) { + assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration"); + start_loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx); + return false; + }); + // Find patch decoration if present + bool is_patch = !deco_mgr->WhileEachDecoration( + var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) { + if (deco.opcode() != spv::Op::OpDecorate) + assert(false && "unexpected decoration"); + return false; + }); + // Compute offset and final type of reference. If no location found + // or any stored locations are live, return without removing stores. + + Instruction* ptr_type = get_def_use_mgr()->GetDef(var->type_id()); + assert(ptr_type && "unexpected var type"); + const uint32_t kPointerTypePointeeIdx = 1; + uint32_t var_type_id = + ptr_type->GetSingleWordInOperand(kPointerTypePointeeIdx); + uint32_t ref_loc = start_loc; + if (ref->opcode() == spv::Op::OpAccessChain || + ref->opcode() == spv::Op::OpInBoundsAccessChain) { + var_type_id = live_mgr->AnalyzeAccessChainLoc( + ref, var_type_id, &ref_loc, &no_loc, is_patch, /* input */ false); + } + const analysis::Type* curr_type = type_mgr->GetType(var_type_id); + if (no_loc || AnyLocsAreLive(ref_loc, live_mgr->GetLocSize(curr_type))) + return; + // Kill all stores based on this reference + KillAllStoresOfRef(ref); +} + +void EliminateDeadOutputStoresPass::KillAllDeadStoresOfBuiltinRef( + Instruction* ref, Instruction* var) { + auto deco_mgr = context()->get_decoration_mgr(); + auto def_use_mgr = context()->get_def_use_mgr(); + auto type_mgr = context()->get_type_mgr(); + auto live_mgr = context()->get_liveness_mgr(); + // Search for builtin decoration of base variable + uint32_t builtin = uint32_t(spv::BuiltIn::Max); + auto var_id = var->result_id(); + (void)deco_mgr->WhileEachDecoration( + var_id, uint32_t(spv::Decoration::BuiltIn), + [&builtin](const Instruction& deco) { + assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration"); + builtin = deco.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx); + return false; + }); + // If analyzed builtin and not live, kill stores. + if (builtin != uint32_t(spv::BuiltIn::Max)) { + if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin)) + KillAllStoresOfRef(ref); + return; + } + // Search for builtin decoration on indexed member + auto ref_op = ref->opcode(); + if (ref_op != spv::Op::OpAccessChain && + ref_op != spv::Op::OpInBoundsAccessChain) { + return; + } + uint32_t in_idx = kOpAccessChainIdx0InIdx; + analysis::Type* var_type = type_mgr->GetType(var->type_id()); + analysis::Pointer* ptr_type = var_type->AsPointer(); + auto curr_type = ptr_type->pointee_type(); + auto arr_type = curr_type->AsArray(); + if (arr_type) { + curr_type = arr_type->element_type(); + ++in_idx; + } + auto str_type = curr_type->AsStruct(); + auto str_type_id = type_mgr->GetId(str_type); + auto member_idx_id = ref->GetSingleWordInOperand(in_idx); + auto member_idx_inst = def_use_mgr->GetDef(member_idx_id); + assert(member_idx_inst->opcode() == spv::Op::OpConstant && + "unexpected non-constant index"); + auto ac_idx = member_idx_inst->GetSingleWordInOperand(kOpConstantValueInIdx); + (void)deco_mgr->WhileEachDecoration( + str_type_id, uint32_t(spv::Decoration::BuiltIn), + [ac_idx, &builtin](const Instruction& deco) { + assert(deco.opcode() == spv::Op::OpMemberDecorate && + "unexpected decoration"); + auto deco_idx = + deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx); + if (deco_idx == ac_idx) { + builtin = + deco.GetSingleWordInOperand(kOpDecorateMemberBuiltInLiteralInIdx); + return false; + } + return true; + }); + assert(builtin != uint32_t(spv::BuiltIn::Max) && "builtin not found"); + // If analyzed builtin and not live, kill stores. + if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin)) + KillAllStoresOfRef(ref); +} + +Pass::Status EliminateDeadOutputStoresPass::DoDeadOutputStoreElimination() { + // Current implementation only supports vert, tesc, tese, geom shaders + auto stage = context()->GetStage(); + if (stage != spv::ExecutionModel::Vertex && + stage != spv::ExecutionModel::TessellationControl && + stage != spv::ExecutionModel::TessellationEvaluation && + stage != spv::ExecutionModel::Geometry) + return Status::Failure; + InitializeElimination(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr(); + // Process all output variables + for (auto& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) { + continue; + } + analysis::Type* var_type = type_mgr->GetType(var.type_id()); + analysis::Pointer* ptr_type = var_type->AsPointer(); + if (ptr_type->storage_class() != spv::StorageClass::Output) { + continue; + } + // If builtin decoration on variable, process as builtin. + auto var_id = var.result_id(); + bool is_builtin = false; + if (deco_mgr->HasDecoration(var_id, uint32_t(spv::Decoration::BuiltIn))) { + is_builtin = true; + } else { + // If interface block with builtin members, process as builtin. + // Strip off outer array type if present. + auto curr_type = ptr_type->pointee_type(); + auto arr_type = curr_type->AsArray(); + if (arr_type) curr_type = arr_type->element_type(); + auto str_type = curr_type->AsStruct(); + if (str_type) { + auto str_type_id = type_mgr->GetId(str_type); + if (deco_mgr->HasDecoration(str_type_id, + uint32_t(spv::Decoration::BuiltIn))) + is_builtin = true; + } + } + // For each store or access chain using var, if dead builtin or all its + // locations are dead, kill store or all access chain's stores + def_use_mgr->ForEachUser( + var_id, [this, &var, is_builtin](Instruction* user) { + auto op = user->opcode(); + if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName || + op == spv::Op::OpDecorate || user->IsNonSemanticInstruction()) + return; + if (is_builtin) + KillAllDeadStoresOfBuiltinRef(user, &var); + else + KillAllDeadStoresOfLocRef(user, &var); + }); + } + for (auto& kinst : kill_list_) context()->KillInst(kinst); + + return kill_list_.empty() ? Status::SuccessWithoutChange + : Status::SuccessWithChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.h new file mode 100644 index 00000000..676d4f4f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/eliminate_dead_output_stores_pass.h @@ -0,0 +1,81 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadOutputStoresPass : public Pass { + public: + explicit EliminateDeadOutputStoresPass( + std::unordered_set* live_locs, + std::unordered_set* live_builtins) + : live_locs_(live_locs), live_builtins_(live_builtins) {} + + const char* name() const override { return "eliminate-dead-output-stores"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Initialize elimination + void InitializeElimination(); + + // Do dead output store analysis + Status DoDeadOutputStoreElimination(); + + // Kill all stores resulting from |ref|. + void KillAllStoresOfRef(Instruction* ref); + + // Kill all dead stores resulting from |user| of loc-based |var|. + void KillAllDeadStoresOfLocRef(Instruction* user, Instruction* var); + + // Kill all dead stores resulting from |user| of builtin |var|. + void KillAllDeadStoresOfBuiltinRef(Instruction* user, Instruction* var); + + // Return true if any of |count| locations starting at location |start| are + // live. + bool AnyLocsAreLive(uint32_t start, uint32_t count); + + // Return true if builtin |bi| is live. + bool IsLiveBuiltin(uint32_t bi); + + std::unordered_set* live_locs_; + std::unordered_set* live_builtins_; + + std::vector kill_list_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_OUTPUT_STORES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/empty_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/empty_pass.h new file mode 100644 index 00000000..1fd2ae59 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/empty_pass.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_EMPTY_PASS_H_ +#define SOURCE_OPT_EMPTY_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class EmptyPass : public Pass { + public: + EmptyPass() {} + + const char* name() const override { return "empty-pass"; } + + Status Process() override { return Status::SuccessWithoutChange; } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_EMPTY_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.cpp new file mode 100644 index 00000000..51883706 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/feature_manager.h" + +#include + +#include "source/enum_string_mapping.h" + +namespace spvtools { +namespace opt { + +void FeatureManager::Analyze(Module* module) { + AddExtensions(module); + AddCapabilities(module); + AddExtInstImportIds(module); +} + +void FeatureManager::AddExtensions(Module* module) { + for (auto ext : module->extensions()) { + AddExtension(&ext); + } +} + +void FeatureManager::AddExtension(Instruction* ext) { + assert(ext->opcode() == spv::Op::OpExtension && + "Expecting an extension instruction."); + + const std::string name = ext->GetInOperand(0u).AsString(); + Extension extension; + if (GetExtensionFromString(name.c_str(), &extension)) { + extensions_.insert(extension); + } +} + +void FeatureManager::RemoveExtension(Extension ext) { + if (!extensions_.contains(ext)) return; + extensions_.erase(ext); +} + +void FeatureManager::AddCapability(spv::Capability cap) { + if (capabilities_.contains(cap)) return; + + capabilities_.insert(cap); + + spv_operand_desc desc = {}; + if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(cap), &desc)) { + for (auto capability : + CapabilitySet(desc->numCapabilities, desc->capabilities)) { + AddCapability(capability); + } + } +} + +void FeatureManager::RemoveCapability(spv::Capability cap) { + if (!capabilities_.contains(cap)) return; + capabilities_.erase(cap); +} + +void FeatureManager::AddCapabilities(Module* module) { + for (Instruction& inst : module->capabilities()) { + AddCapability(static_cast(inst.GetSingleWordInOperand(0))); + } +} + +void FeatureManager::AddExtInstImportIds(Module* module) { + extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); + extinst_importid_OpenCL100DebugInfo_ = + module->GetExtInstImportId("OpenCL.DebugInfo.100"); + extinst_importid_Shader100DebugInfo_ = + module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100"); +} + +bool operator==(const FeatureManager& a, const FeatureManager& b) { + // We check that the addresses of the grammars are the same because they + // are large objects, and this is faster. It can be changed if needed as a + // later time. + if (&a.grammar_ != &b.grammar_) { + return false; + } + + if (a.capabilities_ != b.capabilities_) { + return false; + } + + if (a.extensions_ != b.extensions_) { + return false; + } + + if (a.extinst_importid_GLSLstd450_ != b.extinst_importid_GLSLstd450_) { + return false; + } + + if (a.extinst_importid_OpenCL100DebugInfo_ != + b.extinst_importid_OpenCL100DebugInfo_) { + return false; + } + + if (a.extinst_importid_Shader100DebugInfo_ != + b.extinst_importid_Shader100DebugInfo_) { + return false; + } + + return true; +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.h new file mode 100644 index 00000000..d150a2fa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/feature_manager.h @@ -0,0 +1,113 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FEATURE_MANAGER_H_ +#define SOURCE_OPT_FEATURE_MANAGER_H_ + +#include "source/assembly_grammar.h" +#include "source/extensions.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Tracks features enabled by a module. The IRContext has a FeatureManager. +class FeatureManager { + public: + // Returns true if |ext| is an enabled extension in the module. + bool HasExtension(Extension ext) const { return extensions_.contains(ext); } + + // Returns true if |cap| is an enabled capability in the module. + bool HasCapability(spv::Capability cap) const { + return capabilities_.contains(cap); + } + + // Returns the capabilities the module declares. + inline const CapabilitySet& GetCapabilities() const { return capabilities_; } + + // Returns the extensions the module imports. + inline const ExtensionSet& GetExtensions() const { return extensions_; } + + uint32_t GetExtInstImportId_GLSLstd450() const { + return extinst_importid_GLSLstd450_; + } + + uint32_t GetExtInstImportId_OpenCL100DebugInfo() const { + return extinst_importid_OpenCL100DebugInfo_; + } + + uint32_t GetExtInstImportId_Shader100DebugInfo() const { + return extinst_importid_Shader100DebugInfo_; + } + + friend bool operator==(const FeatureManager& a, const FeatureManager& b); + friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { + return !(a == b); + } + + private: + explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {} + + // Analyzes |module| and records enabled extensions and capabilities. + void Analyze(Module* module); + + // Add the extension |ext| to the feature manager. + void AddExtension(Instruction* ext); + + // Analyzes |module| and records enabled extensions. + void AddExtensions(Module* module); + + // Removes the given |extension| from the current FeatureManager. + void RemoveExtension(Extension extension); + + // Adds the given |capability| and all implied capabilities into the current + // FeatureManager. + void AddCapability(spv::Capability capability); + + // Analyzes |module| and records enabled capabilities. + void AddCapabilities(Module* module); + + // Removes the given |capability| from the current FeatureManager. + void RemoveCapability(spv::Capability capability); + + // Analyzes |module| and records imported external instruction sets. + void AddExtInstImportIds(Module* module); + + // Auxiliary object for querying SPIR-V grammar facts. + const AssemblyGrammar& grammar_; + + // The enabled extensions. + ExtensionSet extensions_; + + // The enabled capabilities. + CapabilitySet capabilities_; + + // Common external instruction import ids, cached for performance. + uint32_t extinst_importid_GLSLstd450_ = 0; + + // Common OpenCL100DebugInfo external instruction import ids, cached + // for performance. + uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; + + // Common NonSemanticShader100DebugInfo external instruction import ids, + // cached for performance. + uint32_t extinst_importid_Shader100DebugInfo_ = 0; + + friend class IRContext; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FEATURE_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.cpp new file mode 100644 index 00000000..f3486bed --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2022 Advanced Micro Devices, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fix_func_call_arguments.h" + +#include "ir_builder.h" + +using namespace spvtools; +using namespace opt; + +bool FixFuncCallArgumentsPass::ModuleHasASingleFunction() { + auto funcsNum = get_module()->end() - get_module()->begin(); + return funcsNum == 1; +} + +Pass::Status FixFuncCallArgumentsPass::Process() { + bool modified = false; + if (ModuleHasASingleFunction()) return Status::SuccessWithoutChange; + for (auto& func : *get_module()) { + func.ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall) { + modified |= FixFuncCallArguments(inst); + } + }); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FixFuncCallArgumentsPass::FixFuncCallArguments( + Instruction* func_call_inst) { + bool modified = false; + for (uint32_t i = 0; i < func_call_inst->NumInOperands(); ++i) { + Operand& op = func_call_inst->GetInOperand(i); + if (op.type != SPV_OPERAND_TYPE_ID) continue; + Instruction* operand_inst = get_def_use_mgr()->GetDef(op.AsId()); + if (operand_inst->opcode() == spv::Op::OpAccessChain) { + uint32_t var_id = + ReplaceAccessChainFuncCallArguments(func_call_inst, operand_inst); + func_call_inst->SetInOperand(i, {var_id}); + modified = true; + } + } + if (modified) { + context()->UpdateDefUse(func_call_inst); + } + return modified; +} + +uint32_t FixFuncCallArgumentsPass::ReplaceAccessChainFuncCallArguments( + Instruction* func_call_inst, Instruction* operand_inst) { + InstructionBuilder builder( + context(), func_call_inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* next_insert_point = func_call_inst->NextNode(); + // Get Variable insertion point + Function* func = context()->get_instr_block(func_call_inst)->GetParent(); + Instruction* variable_insertion_point = &*(func->begin()->begin()); + Instruction* op_ptr_type = get_def_use_mgr()->GetDef(operand_inst->type_id()); + Instruction* op_type = + get_def_use_mgr()->GetDef(op_ptr_type->GetSingleWordInOperand(1)); + uint32_t varType = context()->get_type_mgr()->FindPointerToType( + op_type->result_id(), spv::StorageClass::Function); + // Create new variable + builder.SetInsertPoint(variable_insertion_point); + Instruction* var = + builder.AddVariable(varType, uint32_t(spv::StorageClass::Function)); + // Load access chain to the new variable before function call + builder.SetInsertPoint(func_call_inst); + + uint32_t operand_id = operand_inst->result_id(); + Instruction* load = builder.AddLoad(op_type->result_id(), operand_id); + builder.AddStore(var->result_id(), load->result_id()); + // Load return value to the acesschain after function call + builder.SetInsertPoint(next_insert_point); + load = builder.AddLoad(op_type->result_id(), var->result_id()); + builder.AddStore(operand_id, load->result_id()); + + return var->result_id(); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.h b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.h new file mode 100644 index 00000000..15781b8c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_func_call_arguments.h @@ -0,0 +1,47 @@ +// Copyright (c) 2022 Advanced Micro Devices, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _VAR_FUNC_CALL_PASS_H +#define _VAR_FUNC_CALL_PASS_H + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { +class FixFuncCallArgumentsPass : public Pass { + public: + FixFuncCallArgumentsPass() {} + const char* name() const override { return "fix-for-funcall-param"; } + Status Process() override; + // Returns true if the module has one one function. + bool ModuleHasASingleFunction(); + // Copies from the memory pointed to by |operand_inst| to a new function scope + // variable created before |func_call_inst|, and + // copies the value of the new variable back to the memory pointed to by + // |operand_inst| after |funct_call_inst| Returns the id of + // the new variable. + uint32_t ReplaceAccessChainFuncCallArguments(Instruction* func_call_inst, + Instruction* operand_inst); + + // Fix function call |func_call_inst| non memory object arguments + bool FixFuncCallArguments(Instruction* func_call_inst); + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisTypes; + } +}; +} // namespace opt +} // namespace spvtools + +#endif // _VAR_FUNC_CALL_PASS_H \ No newline at end of file diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.cpp new file mode 100644 index 00000000..608285e6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.cpp @@ -0,0 +1,370 @@ +// Copyright (c) 2019 Google LLC +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fix_storage_class.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status FixStorageClass::Process() { + bool modified = false; + + get_module()->ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == spv::Op::OpVariable) { + std::set seen; + std::vector> uses; + get_def_use_mgr()->ForEachUse(inst, + [&uses](Instruction* use, uint32_t op_idx) { + uses.push_back({use, op_idx}); + }); + + for (auto& use : uses) { + modified |= PropagateStorageClass( + use.first, + static_cast(inst->GetSingleWordInOperand(0)), + &seen); + assert(seen.empty() && "Seen was not properly reset."); + modified |= + PropagateType(use.first, inst->type_id(), use.second, &seen); + assert(seen.empty() && "Seen was not properly reset."); + } + } + }); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FixStorageClass::PropagateStorageClass(Instruction* inst, + spv::StorageClass storage_class, + std::set* seen) { + if (!IsPointerResultType(inst)) { + return false; + } + + if (IsPointerToStorageClass(inst, storage_class)) { + if (inst->opcode() == spv::Op::OpPhi) { + if (!seen->insert(inst->result_id()).second) { + return false; + } + } + + bool modified = false; + std::vector uses; + get_def_use_mgr()->ForEachUser( + inst, [&uses](Instruction* use) { uses.push_back(use); }); + for (Instruction* use : uses) { + modified |= PropagateStorageClass(use, storage_class, seen); + } + + if (inst->opcode() == spv::Op::OpPhi) { + seen->erase(inst->result_id()); + } + return modified; + } + + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpCopyObject: + case spv::Op::OpPhi: + case spv::Op::OpSelect: + FixInstructionStorageClass(inst, storage_class, seen); + return true; + case spv::Op::OpFunctionCall: + // We cannot be sure of the actual connection between the storage class + // of the parameter and the storage class of the result, so we should not + // do anything. If the result type needs to be fixed, the function call + // should be inlined. + return false; + case spv::Op::OpImageTexelPointer: + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + case spv::Op::OpVariable: + case spv::Op::OpBitcast: + case spv::Op::OpAllocateNodePayloadsAMDX: + // Nothing to change for these opcode. The result type is the same + // regardless of the storage class of the operand. + return false; + default: + assert(false && + "Not expecting instruction to have a pointer result type."); + return false; + } +} + +void FixStorageClass::FixInstructionStorageClass( + Instruction* inst, spv::StorageClass storage_class, + std::set* seen) { + assert(IsPointerResultType(inst) && + "The result type of the instruction must be a pointer."); + + ChangeResultStorageClass(inst, storage_class); + + std::vector uses; + get_def_use_mgr()->ForEachUser( + inst, [&uses](Instruction* use) { uses.push_back(use); }); + for (Instruction* use : uses) { + PropagateStorageClass(use, storage_class, seen); + } +} + +void FixStorageClass::ChangeResultStorageClass( + Instruction* inst, spv::StorageClass storage_class) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + Instruction* result_type_inst = get_def_use_mgr()->GetDef(inst->type_id()); + assert(result_type_inst->opcode() == spv::Op::OpTypePointer); + uint32_t pointee_type_id = result_type_inst->GetSingleWordInOperand(1); + uint32_t new_result_type_id = + type_mgr->FindPointerToType(pointee_type_id, storage_class); + inst->SetResultType(new_result_type_id); + context()->UpdateDefUse(inst); +} + +bool FixStorageClass::IsPointerResultType(Instruction* inst) { + if (inst->type_id() == 0) { + return false; + } + + Instruction* type_def = get_def_use_mgr()->GetDef(inst->type_id()); + return type_def->opcode() == spv::Op::OpTypePointer; +} + +bool FixStorageClass::IsPointerToStorageClass(Instruction* inst, + spv::StorageClass storage_class) { + if (inst->type_id() == 0) { + return false; + } + + Instruction* type_def = get_def_use_mgr()->GetDef(inst->type_id()); + if (type_def->opcode() != spv::Op::OpTypePointer) { + return false; + } + + const uint32_t kPointerTypeStorageClassIndex = 0; + spv::StorageClass pointer_storage_class = static_cast( + type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + return pointer_storage_class == storage_class; +} + +bool FixStorageClass::ChangeResultType(Instruction* inst, + uint32_t new_type_id) { + if (inst->type_id() == new_type_id) { + return false; + } + + context()->ForgetUses(inst); + inst->SetResultType(new_type_id); + context()->AnalyzeUses(inst); + return true; +} + +bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_id, + uint32_t op_idx, std::set* seen) { + assert(type_id != 0 && "Not given a valid type in PropagateType"); + bool modified = false; + + // If the type of operand |op_idx| forces the result type of |inst| to a + // particular type, then we want find that type. + uint32_t new_type_id = 0; + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + if (op_idx == 2) { + new_type_id = WalkAccessChainType(inst, type_id); + } + break; + case spv::Op::OpCopyObject: + new_type_id = type_id; + break; + case spv::Op::OpPhi: + if (seen->insert(inst->result_id()).second) { + new_type_id = type_id; + } + break; + case spv::Op::OpSelect: + if (op_idx > 2) { + new_type_id = type_id; + } + break; + case spv::Op::OpFunctionCall: + // We cannot be sure of the actual connection between the type + // of the parameter and the type of the result, so we should not + // do anything. If the result type needs to be fixed, the function call + // should be inlined. + return false; + case spv::Op::OpLoad: { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + new_type_id = type_inst->GetSingleWordInOperand(1); + break; + } + case spv::Op::OpStore: { + uint32_t obj_id = inst->GetSingleWordInOperand(1); + Instruction* obj_inst = get_def_use_mgr()->GetDef(obj_id); + uint32_t obj_type_id = obj_inst->type_id(); + + uint32_t ptr_id = inst->GetSingleWordInOperand(0); + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id); + uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst); + + if (obj_type_id != pointee_type_id) { + if (context()->get_type_mgr()->GetType(obj_type_id)->AsImage() && + context()->get_type_mgr()->GetType(pointee_type_id)->AsImage()) { + // When storing an image, allow the type mismatch + // and let the later legalization passes eliminate the OpStore. + // This is to support assigning an image to a variable, + // where the assigned image does not have a pre-defined + // image format. + return false; + } + + uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst); + if (copy_id == 0) { + return false; + } + inst->SetInOperand(1, {copy_id}); + context()->UpdateDefUse(inst); + } + } break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + // TODO: May need to expand the copy as we do with the stores. + break; + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + // TODO: DXC does not seem to generate code that will require changes to + // these opcode. The can be implemented when they come up. + break; + case spv::Op::OpImageTexelPointer: + case spv::Op::OpBitcast: + // Nothing to change for these opcode. The result type is the same + // regardless of the type of the operand. + return false; + default: + // I expect the remaining instructions to act on types that are guaranteed + // to be unique, so no change will be necessary. + break; + } + + // If the operand forces the result type, then make sure the result type + // matches, and update the uses of |inst|. We do not have to check the uses + // of |inst| in the result type is not forced because we are only looking for + // issue that come from mismatches between function formal and actual + // parameters after the function has been inlined. These parameters are + // pointers. Once the type no longer depends on the type of the parameter, + // then the types should have be correct. + if (new_type_id != 0) { + modified = ChangeResultType(inst, new_type_id); + + std::vector> uses; + get_def_use_mgr()->ForEachUse(inst, + [&uses](Instruction* use, uint32_t idx) { + uses.push_back({use, idx}); + }); + + for (auto& use : uses) { + PropagateType(use.first, new_type_id, use.second, seen); + } + + if (inst->opcode() == spv::Op::OpPhi) { + seen->erase(inst->result_id()); + } + } + return modified; +} + +uint32_t FixStorageClass::WalkAccessChainType(Instruction* inst, uint32_t id) { + uint32_t start_idx = 0; + switch (inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + start_idx = 1; + break; + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + start_idx = 2; + break; + default: + assert(false); + break; + } + + Instruction* id_type_inst = get_def_use_mgr()->GetDef(id); + assert(id_type_inst->opcode() == spv::Op::OpTypePointer); + id = id_type_inst->GetSingleWordInOperand(1); + spv::StorageClass input_storage_class = + static_cast(id_type_inst->GetSingleWordInOperand(0)); + + for (uint32_t i = start_idx; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(id); + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeCooperativeMatrixKHR: + id = type_inst->GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: { + const analysis::Constant* index_const = + context()->get_constant_mgr()->FindDeclaredConstant( + inst->GetSingleWordInOperand(i)); + // It is highly unlikely that any type would have more fields than could + // be indexed by a 32-bit integer, and GetSingleWordInOperand only takes + // a 32-bit value, so we would not be able to handle it anyway. But the + // specification does allow any scalar integer type, treated as signed, + // so we simply downcast the index to 32-bits. + uint32_t index = + static_cast(index_const->GetSignExtendedValue()); + id = type_inst->GetSingleWordInOperand(index); + break; + } + default: + break; + } + assert(id != 0 && + "Tried to extract from an object where it cannot be done."); + } + + Instruction* orig_type_inst = get_def_use_mgr()->GetDef(inst->type_id()); + spv::StorageClass orig_storage_class = + static_cast(orig_type_inst->GetSingleWordInOperand(0)); + assert(orig_type_inst->opcode() == spv::Op::OpTypePointer); + if (orig_type_inst->GetSingleWordInOperand(1) == id && + input_storage_class == orig_storage_class) { + // The existing type is correct. Avoid the search for the type. Note that if + // there is a duplicate type, the search below could return a different type + // forcing more changes to the code than necessary. + return inst->type_id(); + } + + return context()->get_type_mgr()->FindPointerToType(id, input_storage_class); +} + +// namespace opt + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.h b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.h new file mode 100644 index 00000000..6c67acd3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fix_storage_class.h @@ -0,0 +1,93 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FIX_STORAGE_CLASS_H_ +#define SOURCE_OPT_FIX_STORAGE_CLASS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass tries to fix validation error due to a mismatch of storage classes +// in instructions. There is no guarantee that all such error will be fixed, +// and it is possible that in fixing these errors, it could lead to other +// errors. +class FixStorageClass : public Pass { + public: + const char* name() const override { return "fix-storage-class"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Changes the storage class of the result of |inst| to |storage_class| in + // appropriate, and propagates the change to the users of |inst| as well. + // Returns true of any changes were made. + // |seen| is used to track OpPhi instructions that should not be processed. + bool PropagateStorageClass(Instruction* inst, spv::StorageClass storage_class, + std::set* seen); + + // Changes the storage class of the result of |inst| to |storage_class|. + // Is it assumed that the result type of |inst| is a pointer type. + // Propagates the change to the users of |inst| as well. + // Returns true of any changes were made. + // |seen| is used to track OpPhi instructions that should not be processed by + // |PropagateStorageClass| + void FixInstructionStorageClass(Instruction* inst, + spv::StorageClass storage_class, + std::set* seen); + + // Changes the storage class of the result of |inst| to |storage_class|. The + // result type of |inst| must be a pointer. + void ChangeResultStorageClass(Instruction* inst, + spv::StorageClass storage_class) const; + + // Returns true if the result type of |inst| is a pointer. + bool IsPointerResultType(Instruction* inst); + + // Returns true if the result of |inst| is a pointer to storage class + // |storage_class|. + bool IsPointerToStorageClass(Instruction* inst, + spv::StorageClass storage_class); + + // Change |inst| to match that operand |op_idx| now has type |type_id|, and + // adjust any uses of |inst| accordingly. Returns true if the code changed. + bool PropagateType(Instruction* inst, uint32_t type_id, uint32_t op_idx, + std::set* seen); + + // Changes the result type of |inst| to |new_type_id|. + bool ChangeResultType(Instruction* inst, uint32_t new_type_id); + + // Returns the type id of the member of the type |id| that would be returned + // by following the indices of the access chain instruction |inst|. + uint32_t WalkAccessChainType(Instruction* inst, uint32_t id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FIX_STORAGE_CLASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.cpp new file mode 100644 index 00000000..c878c097 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/flatten_decoration_pass.h" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +using Words = std::vector; +using OrderedUsesMap = std::unordered_map; + +Pass::Status FlattenDecorationPass::Process() { + bool modified = false; + + // The target Id of OpDecorationGroup instructions. + // We have to track this separately from its uses, in case it + // has no uses. + std::unordered_set group_ids; + // Maps a decoration group Id to its GroupDecorate targets, in order + // of appearance. + OrderedUsesMap normal_uses; + // Maps a decoration group Id to its GroupMemberDecorate targets and + // their indices, in of appearance. + OrderedUsesMap member_uses; + + auto annotations = context()->annotations(); + + // On the first pass, record each OpDecorationGroup with its ordered uses. + // Rely on unordered_map::operator[] to create its entries on first access. + for (const auto& inst : annotations) { + switch (inst.opcode()) { + case spv::Op::OpDecorationGroup: + group_ids.insert(inst.result_id()); + break; + case spv::Op::OpGroupDecorate: { + Words& words = normal_uses[inst.GetSingleWordInOperand(0)]; + for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) { + words.push_back(inst.GetSingleWordInOperand(i)); + } + } break; + case spv::Op::OpGroupMemberDecorate: { + Words& words = member_uses[inst.GetSingleWordInOperand(0)]; + for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) { + words.push_back(inst.GetSingleWordInOperand(i)); + } + } break; + default: + break; + } + } + + // On the second pass, replace OpDecorationGroup and its uses with + // equivalent normal and struct member uses. + auto inst_iter = annotations.begin(); + // We have to re-evaluate the end pointer + while (inst_iter != context()->annotations().end()) { + // Should we replace this instruction? + bool replace = false; + switch (inst_iter->opcode()) { + case spv::Op::OpDecorationGroup: + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: + replace = true; + break; + case spv::Op::OpDecorate: { + // If this decoration targets a group, then replace it + // by sets of normal and member decorations. + const uint32_t group = inst_iter->GetSingleWordOperand(0); + const auto normal_uses_iter = normal_uses.find(group); + if (normal_uses_iter != normal_uses.end()) { + for (auto target : normal_uses[group]) { + std::unique_ptr new_inst(inst_iter->Clone(context())); + new_inst->SetInOperand(0, Words{target}); + inst_iter = inst_iter.InsertBefore(std::move(new_inst)); + ++inst_iter; + replace = true; + } + } + const auto member_uses_iter = member_uses.find(group); + if (member_uses_iter != member_uses.end()) { + const Words& member_id_pairs = (*member_uses_iter).second; + // The collection is a sequence of pairs. + assert((member_id_pairs.size() % 2) == 0); + for (size_t i = 0; i < member_id_pairs.size(); i += 2) { + // Make an OpMemberDecorate instruction for each (target, member) + // pair. + const uint32_t target = member_id_pairs[i]; + const uint32_t member = member_id_pairs[i + 1]; + std::vector operands; + operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {target})); + operands.push_back( + Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {member})); + auto decoration_operands_iter = inst_iter->begin(); + decoration_operands_iter++; // Skip the group target. + operands.insert(operands.end(), decoration_operands_iter, + inst_iter->end()); + std::unique_ptr new_inst(new Instruction( + context(), spv::Op::OpMemberDecorate, 0, 0, operands)); + inst_iter = inst_iter.InsertBefore(std::move(new_inst)); + ++inst_iter; + replace = true; + } + } + // If this is an OpDecorate targeting the OpDecorationGroup itself, + // remove it even if that decoration group itself is not the target of + // any OpGroupDecorate or OpGroupMemberDecorate. + if (!replace && group_ids.count(group)) { + replace = true; + } + } break; + default: + break; + } + if (replace) { + inst_iter = inst_iter.Erase(); + modified = true; + } else { + // Handle the case of decorations unrelated to decoration groups. + ++inst_iter; + } + } + + // Remove OpName instructions which reference the removed group decorations. + // An OpDecorationGroup instruction might not have been used by an + // OpGroupDecorate or OpGroupMemberDecorate instruction. + if (!group_ids.empty()) { + for (auto debug_inst_iter = context()->debug2_begin(); + debug_inst_iter != context()->debug2_end();) { + if (debug_inst_iter->opcode() == spv::Op::OpName) { + const uint32_t target = debug_inst_iter->GetSingleWordOperand(0); + if (group_ids.count(target)) { + debug_inst_iter = debug_inst_iter.Erase(); + modified = true; + } else { + ++debug_inst_iter; + } + } + } + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.h new file mode 100644 index 00000000..6a34f5bb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/flatten_decoration_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ +#define SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FlattenDecorationPass : public Pass { + public: + const char* name() const override { return "flatten-decorations"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fold.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/fold.cpp new file mode 100644 index 00000000..cd070c74 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fold.cpp @@ -0,0 +1,701 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/fold.h" + +#include +#include +#include + +#include "source/opt/const_folding_rules.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/folding_rules.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +#ifndef INT32_MIN +#define INT32_MIN (-2147483648) +#endif + +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffff /* 4294967295U */ +#endif + +} // namespace + +uint32_t InstructionFolder::UnaryOperate(spv::Op opcode, + uint32_t operand) const { + switch (opcode) { + // Arthimetics + case spv::Op::OpSNegate: { + int32_t s_operand = static_cast(operand); + if (s_operand == std::numeric_limits::min()) { + return s_operand; + } + return static_cast(-s_operand); + } + case spv::Op::OpNot: + return ~operand; + case spv::Op::OpLogicalNot: + return !static_cast(operand); + case spv::Op::OpUConvert: + return operand; + case spv::Op::OpSConvert: + return operand; + default: + assert(false && + "Unsupported unary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::BinaryOperate(spv::Op opcode, uint32_t a, + uint32_t b) const { + switch (opcode) { + // Shifting + case spv::Op::OpShiftRightLogical: + if (b >= 32) { + // This is undefined behaviour when |b| > 32. Choose 0 for consistency. + // When |b| == 32, doing the shift in C++ in undefined, but the result + // will be 0, so just return that value. + return 0; + } + return a >> b; + case spv::Op::OpShiftRightArithmetic: + if (b > 32) { + // This is undefined behaviour. Choose 0 for consistency. + return 0; + } + if (b == 32) { + // Doing the shift in C++ is undefined, but the result is defined in the + // spir-v spec. Find that value another way. + if (static_cast(a) >= 0) { + return 0; + } else { + return static_cast(-1); + } + } + return (static_cast(a)) >> b; + case spv::Op::OpShiftLeftLogical: + if (b >= 32) { + // This is undefined behaviour when |b| > 32. Choose 0 for consistency. + // When |b| == 32, doing the shift in C++ in undefined, but the result + // will be 0, so just return that value. + return 0; + } + return a << b; + + // Bitwise operations + case spv::Op::OpBitwiseOr: + return a | b; + case spv::Op::OpBitwiseAnd: + return a & b; + case spv::Op::OpBitwiseXor: + return a ^ b; + + // Logical + case spv::Op::OpLogicalEqual: + return (static_cast(a)) == (static_cast(b)); + case spv::Op::OpLogicalNotEqual: + return (static_cast(a)) != (static_cast(b)); + case spv::Op::OpLogicalOr: + return (static_cast(a)) || (static_cast(b)); + case spv::Op::OpLogicalAnd: + return (static_cast(a)) && (static_cast(b)); + + // Comparison + case spv::Op::OpIEqual: + return a == b; + case spv::Op::OpINotEqual: + return a != b; + case spv::Op::OpULessThan: + return a < b; + case spv::Op::OpSLessThan: + return (static_cast(a)) < (static_cast(b)); + case spv::Op::OpUGreaterThan: + return a > b; + case spv::Op::OpSGreaterThan: + return (static_cast(a)) > (static_cast(b)); + case spv::Op::OpULessThanEqual: + return a <= b; + case spv::Op::OpSLessThanEqual: + return (static_cast(a)) <= (static_cast(b)); + case spv::Op::OpUGreaterThanEqual: + return a >= b; + case spv::Op::OpSGreaterThanEqual: + return (static_cast(a)) >= (static_cast(b)); + default: + assert(false && + "Unsupported binary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::TernaryOperate(spv::Op opcode, uint32_t a, + uint32_t b, uint32_t c) const { + switch (opcode) { + case spv::Op::OpSelect: + return (static_cast(a)) ? b : c; + default: + assert(false && + "Unsupported ternary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::OperateWords( + spv::Op opcode, const std::vector& operand_words) const { + switch (operand_words.size()) { + case 1: + return UnaryOperate(opcode, operand_words.front()); + case 2: + return BinaryOperate(opcode, operand_words.front(), operand_words.back()); + case 3: + return TernaryOperate(opcode, operand_words[0], operand_words[1], + operand_words[2]); + default: + assert(false && "Invalid number of operands"); + return 0; + } +} + +bool InstructionFolder::FoldInstructionInternal(Instruction* inst) const { + auto identity_map = [](uint32_t id) { return id; }; + Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map); + if (folded_inst != nullptr) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {folded_inst->result_id()}}}); + return true; + } + + analysis::ConstantManager* const_manager = context_->get_constant_mgr(); + std::vector constants = + const_manager->GetOperandConstants(inst); + + for (const FoldingRule& rule : + GetFoldingRules().GetRulesForInstruction(inst)) { + if (rule(context_, inst, constants)) { + return true; + } + } + return false; +} + +// Returns the result of performing an operation on scalar constant operands. +// This function extracts the operand values as 32 bit words and returns the +// result in 32 bit word. Scalar constants with longer than 32-bit width are +// not accepted in this function. +uint32_t InstructionFolder::FoldScalars( + spv::Op opcode, + const std::vector& operands) const { + assert(IsFoldableOpcode(opcode) && + "Unhandled instruction opcode in FoldScalars"); + std::vector operand_values_in_raw_words; + for (const auto& operand : operands) { + if (const analysis::ScalarConstant* scalar = operand->AsScalarConstant()) { + const auto& scalar_words = scalar->words(); + assert(scalar_words.size() == 1 && + "Scalar constants with longer than 32-bit width are not allowed " + "in FoldScalars()"); + operand_values_in_raw_words.push_back(scalar_words.front()); + } else if (operand->AsNullConstant()) { + operand_values_in_raw_words.push_back(0u); + } else { + assert(false && + "FoldScalars() only accepts ScalarConst or NullConst type of " + "constant"); + } + } + return OperateWords(opcode, operand_values_in_raw_words); +} + +bool InstructionFolder::FoldBinaryIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + spv::Op opcode = inst->opcode(); + analysis::ConstantManager* const_manger = context_->get_constant_mgr(); + + uint32_t ids[2]; + const analysis::IntConstant* constants[2]; + for (uint32_t i = 0; i < 2; i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + return false; + } + ids[i] = id_map(operand->words[0]); + const analysis::Constant* constant = + const_manger->FindDeclaredConstant(ids[i]); + constants[i] = (constant != nullptr ? constant->AsIntConstant() : nullptr); + } + + switch (opcode) { + // Arthimetics + case spv::Op::OpIMul: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr && constants[i]->IsZero()) { + *result = 0; + return true; + } + } + break; + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpUMod: + // This changes undefined behaviour (ie divide by 0) into a 0. + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr && constants[i]->IsZero()) { + *result = 0; + return true; + } + } + break; + + // Shifting + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftLeftLogical: + if (constants[1] != nullptr) { + // When shifting by a value larger than the size of the result, the + // result is undefined. We are setting the undefined behaviour to a + // result of 0. If the shift amount is the same as the size of the + // result, then the result is defined, and it 0. + uint32_t shift_amount = constants[1]->GetU32BitValue(); + if (shift_amount >= 32) { + *result = 0; + return true; + } + } + break; + + // Bitwise operations + case spv::Op::OpBitwiseOr: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + // TODO: Change the mask against a value based on the bit width of the + // instruction result type. This way we can handle say 16-bit values + // as well. + uint32_t mask = constants[i]->GetU32BitValue(); + if (mask == 0xFFFFFFFF) { + *result = 0xFFFFFFFF; + return true; + } + } + } + break; + case spv::Op::OpBitwiseAnd: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (constants[i]->IsZero()) { + *result = 0; + return true; + } + } + } + break; + + // Comparison + case spv::Op::OpULessThan: + if (constants[0] != nullptr && + constants[0]->GetU32BitValue() == UINT32_MAX) { + *result = false; + return true; + } + if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) { + *result = false; + return true; + } + break; + case spv::Op::OpSLessThan: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MAX) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MIN) { + *result = false; + return true; + } + break; + case spv::Op::OpUGreaterThan: + if (constants[0] != nullptr && constants[0]->IsZero()) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetU32BitValue() == UINT32_MAX) { + *result = false; + return true; + } + break; + case spv::Op::OpSGreaterThan: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MIN) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MAX) { + *result = false; + return true; + } + break; + case spv::Op::OpULessThanEqual: + if (constants[0] != nullptr && constants[0]->IsZero()) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetU32BitValue() == UINT32_MAX) { + *result = true; + return true; + } + break; + case spv::Op::OpSLessThanEqual: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MIN) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MAX) { + *result = true; + return true; + } + break; + case spv::Op::OpUGreaterThanEqual: + if (constants[0] != nullptr && + constants[0]->GetU32BitValue() == UINT32_MAX) { + *result = true; + return true; + } + if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) { + *result = true; + return true; + } + break; + case spv::Op::OpSGreaterThanEqual: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MAX) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MIN) { + *result = true; + return true; + } + break; + default: + break; + } + return false; +} + +bool InstructionFolder::FoldBinaryBooleanOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + spv::Op opcode = inst->opcode(); + analysis::ConstantManager* const_manger = context_->get_constant_mgr(); + + uint32_t ids[2]; + const analysis::BoolConstant* constants[2]; + for (uint32_t i = 0; i < 2; i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + return false; + } + ids[i] = id_map(operand->words[0]); + const analysis::Constant* constant = + const_manger->FindDeclaredConstant(ids[i]); + constants[i] = (constant != nullptr ? constant->AsBoolConstant() : nullptr); + } + + switch (opcode) { + // Logical + case spv::Op::OpLogicalOr: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (constants[i]->value()) { + *result = true; + return true; + } + } + } + break; + case spv::Op::OpLogicalAnd: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (!constants[i]->value()) { + *result = false; + return true; + } + } + } + break; + + default: + break; + } + return false; +} + +bool InstructionFolder::FoldIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + assert(IsFoldableOpcode(inst->opcode()) && + "Unhandled instruction opcode in FoldScalars"); + switch (inst->NumInOperands()) { + case 2: + return FoldBinaryIntegerOpToConstant(inst, id_map, result) || + FoldBinaryBooleanOpToConstant(inst, id_map, result); + default: + return false; + } +} + +std::vector InstructionFolder::FoldVectors( + spv::Op opcode, uint32_t num_dims, + const std::vector& operands) const { + assert(IsFoldableOpcode(opcode) && + "Unhandled instruction opcode in FoldVectors"); + std::vector result; + for (uint32_t d = 0; d < num_dims; d++) { + std::vector operand_values_for_one_dimension; + for (const auto& operand : operands) { + if (const analysis::VectorConstant* vector_operand = + operand->AsVectorConstant()) { + // Extract the raw value of the scalar component constants + // in 32-bit words here. The reason of not using FoldScalars() here + // is that we do not create temporary null constants as components + // when the vector operand is a NullConstant because Constant creation + // may need extra checks for the validity and that is not managed in + // here. + if (const analysis::ScalarConstant* scalar_component = + vector_operand->GetComponents().at(d)->AsScalarConstant()) { + const auto& scalar_words = scalar_component->words(); + assert( + scalar_words.size() == 1 && + "Vector components with longer than 32-bit width are not allowed " + "in FoldVectors()"); + operand_values_for_one_dimension.push_back(scalar_words.front()); + } else if (operand->AsNullConstant()) { + operand_values_for_one_dimension.push_back(0u); + } else { + assert(false && + "VectorConst should only has ScalarConst or NullConst as " + "components"); + } + } else if (operand->AsNullConstant()) { + operand_values_for_one_dimension.push_back(0u); + } else { + assert(false && + "FoldVectors() only accepts VectorConst or NullConst type of " + "constant"); + } + } + result.push_back(OperateWords(opcode, operand_values_for_one_dimension)); + } + return result; +} + +bool InstructionFolder::IsFoldableOpcode(spv::Op opcode) const { + // NOTE: Extend to more opcodes as new cases are handled in the folder + // functions. + switch (opcode) { + case spv::Op::OpBitwiseAnd: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpIAdd: + case spv::Op::OpIEqual: + case spv::Op::OpIMul: + case spv::Op::OpINotEqual: + case spv::Op::OpISub: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNot: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpNot: + case spv::Op::OpSDiv: + case spv::Op::OpSelect: + case spv::Op::OpSGreaterThan: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpSLessThan: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpSMod: + case spv::Op::OpSNegate: + case spv::Op::OpSRem: + case spv::Op::OpSConvert: + case spv::Op::OpUConvert: + case spv::Op::OpUDiv: + case spv::Op::OpUGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpUMod: + return true; + default: + return false; + } +} + +bool InstructionFolder::IsFoldableConstant( + const analysis::Constant* cst) const { + // Currently supported constants are 32-bit values or null constants. + if (const analysis::ScalarConstant* scalar = cst->AsScalarConstant()) + return scalar->words().size() == 1; + else + return cst->AsNullConstant() != nullptr; +} + +Instruction* InstructionFolder::FoldInstructionToConstant( + Instruction* inst, std::function id_map) const { + analysis::ConstantManager* const_mgr = context_->get_constant_mgr(); + + if (!inst->IsFoldableByFoldScalar() && !inst->IsFoldableByFoldVector() && + !GetConstantFoldingRules().HasFoldingRule(inst)) { + return nullptr; + } + // Collect the values of the constant parameters. + std::vector constants; + bool missing_constants = false; + inst->ForEachInId([&constants, &missing_constants, const_mgr, + &id_map](uint32_t* op_id) { + uint32_t id = id_map(*op_id); + const analysis::Constant* const_op = const_mgr->FindDeclaredConstant(id); + if (!const_op) { + constants.push_back(nullptr); + missing_constants = true; + } else { + constants.push_back(const_op); + } + }); + + const analysis::Constant* folded_const = nullptr; + for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) { + folded_const = rule(context_, inst, constants); + if (folded_const != nullptr) { + Instruction* const_inst = + const_mgr->GetDefiningInstruction(folded_const, inst->type_id()); + if (const_inst == nullptr) { + return nullptr; + } + assert(const_inst->type_id() == inst->type_id()); + // May be a new instruction that needs to be analysed. + context_->UpdateDefUse(const_inst); + return const_inst; + } + } + + bool successful = false; + + // If all parameters are constant, fold the instruction to a constant. + if (inst->IsFoldableByFoldScalar()) { + uint32_t result_val = 0; + + if (!missing_constants) { + result_val = FoldScalars(inst->opcode(), constants); + successful = true; + } + + if (!successful) { + successful = FoldIntegerOpToConstant(inst, id_map, &result_val); + } + + if (successful) { + const analysis::Constant* result_const = + const_mgr->GetConstant(const_mgr->GetType(inst), {result_val}); + Instruction* folded_inst = + const_mgr->GetDefiningInstruction(result_const, inst->type_id()); + return folded_inst; + } + } else if (inst->IsFoldableByFoldVector()) { + std::vector result_val; + + if (!missing_constants) { + if (Instruction* inst_type = + context_->get_def_use_mgr()->GetDef(inst->type_id())) { + result_val = FoldVectors( + inst->opcode(), inst_type->GetSingleWordInOperand(1), constants); + successful = true; + } + } + + if (successful) { + const analysis::Constant* result_const = + const_mgr->GetNumericVectorConstantWithWords( + const_mgr->GetType(inst)->AsVector(), result_val); + Instruction* folded_inst = + const_mgr->GetDefiningInstruction(result_const, inst->type_id()); + return folded_inst; + } + } + + return nullptr; +} + +bool InstructionFolder::IsFoldableType(Instruction* type_inst) const { + return IsFoldableScalarType(type_inst) || IsFoldableVectorType(type_inst); +} + +bool InstructionFolder::IsFoldableScalarType(Instruction* type_inst) const { + // Support 32-bit integers. + if (type_inst->opcode() == spv::Op::OpTypeInt) { + return type_inst->GetSingleWordInOperand(0) == 32; + } + // Support booleans. + if (type_inst->opcode() == spv::Op::OpTypeBool) { + return true; + } + // Nothing else yet. + return false; +} + +bool InstructionFolder::IsFoldableVectorType(Instruction* type_inst) const { + // Support vectors with foldable components + if (type_inst->opcode() == spv::Op::OpTypeVector) { + uint32_t component_type_id = type_inst->GetSingleWordInOperand(0); + Instruction* def_component_type = + context_->get_def_use_mgr()->GetDef(component_type_id); + return def_component_type != nullptr && + IsFoldableScalarType(def_component_type); + } + // Nothing else yet. + return false; +} + +bool InstructionFolder::FoldInstruction(Instruction* inst) const { + bool modified = false; + Instruction* folded_inst(inst); + while (folded_inst->opcode() != spv::Op::OpCopyObject && + FoldInstructionInternal(&*folded_inst)) { + modified = true; + } + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fold.h b/engine/engine/3rdlibs/spirv-tools/source/opt/fold.h new file mode 100644 index 00000000..42da65e4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fold.h @@ -0,0 +1,195 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLD_H_ +#define SOURCE_OPT_FOLD_H_ + +#include +#include + +#include "source/opt/const_folding_rules.h" +#include "source/opt/constants.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/folding_rules.h" + +namespace spvtools { +namespace opt { + +class InstructionFolder { + public: + explicit InstructionFolder(IRContext* context) + : context_(context), + const_folding_rules_(new ConstantFoldingRules(context)), + folding_rules_(new FoldingRules(context)) { + folding_rules_->AddFoldingRules(); + const_folding_rules_->AddFoldingRules(); + } + + explicit InstructionFolder( + IRContext* context, std::unique_ptr&& folding_rules, + std::unique_ptr&& constant_folding_rules) + : context_(context), + const_folding_rules_(std::move(constant_folding_rules)), + folding_rules_(std::move(folding_rules)) { + folding_rules_->AddFoldingRules(); + const_folding_rules_->AddFoldingRules(); + } + + // Returns the result of folding a scalar instruction with the given |opcode| + // and |operands|. Each entry in |operands| is a pointer to an + // analysis::Constant instance, which should've been created with the constant + // manager (See IRContext::get_constant_mgr). + // + // It is an error to call this function with an opcode that does not pass the + // IsFoldableOpcode test. If any error occurs during folding, the folder will + // fail with a call to assert. + uint32_t FoldScalars( + spv::Op opcode, + const std::vector& operands) const; + + // Returns the result of performing an operation with the given |opcode| over + // constant vectors with |num_dims| dimensions. Each entry in |operands| is a + // pointer to an analysis::Constant instance, which should've been created + // with the constant manager (See IRContext::get_constant_mgr). + // + // This function iterates through the given vector type constant operands and + // calculates the result for each element of the result vector to return. + // Vectors with longer than 32-bit scalar components are not accepted in this + // function. + // + // It is an error to call this function with an opcode that does not pass the + // IsFoldableOpcode test. If any error occurs during folding, the folder will + // fail with a call to assert. + std::vector FoldVectors( + spv::Op opcode, uint32_t num_dims, + const std::vector& operands) const; + + // Returns true if |opcode| represents an operation handled by FoldScalars or + // FoldVectors. + bool IsFoldableOpcode(spv::Op opcode) const; + + // Returns true if |cst| is supported by FoldScalars and FoldVectors. + bool IsFoldableConstant(const analysis::Constant* cst) const; + + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableType(Instruction* type_inst) const; + + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableScalarType(Instruction* type_inst) const; + + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableVectorType(Instruction* type_inst) const; + + // Tries to fold |inst| to a single constant, when the input ids to |inst| + // have been substituted using |id_map|. Returns a pointer to the OpConstant* + // instruction if successful. If necessary, a new constant instruction is + // created and placed in the global values section. + // + // |id_map| is a function that takes one result id and returns another. It + // can be used for things like CCP where it is known that some ids contain a + // constant, but the instruction itself has not been updated yet. This can + // map those ids to the appropriate constants. + Instruction* FoldInstructionToConstant( + Instruction* inst, std::function id_map) const; + // Returns true if |inst| can be folded into a simpler instruction. + // If |inst| can be simplified, |inst| is overwritten with the simplified + // instruction reusing the same result id. + // + // If |inst| is simplified, it is possible that the resulting code in invalid + // because the instruction is in a bad location. Callers of this function + // have to handle the following cases: + // + // 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after + // |inst| in a basic block then this is invalid. The caller must fix this + // up. + bool FoldInstruction(Instruction* inst) const; + + // Return true if this opcode has a const folding rule associtated with it. + bool HasConstFoldingRule(const Instruction* inst) const { + return GetConstantFoldingRules().HasFoldingRule(inst); + } + + private: + // Returns a reference to the ConstnatFoldingRules instance. + const ConstantFoldingRules& GetConstantFoldingRules() const { + return *const_folding_rules_; + } + + // Returns a reference to the FoldingRules instance. + const FoldingRules& GetFoldingRules() const { return *folding_rules_; } + + // Returns the single-word result from performing the given unary operation on + // the operand value which is passed in as a 32-bit word. + uint32_t UnaryOperate(spv::Op opcode, uint32_t operand) const; + + // Returns the single-word result from performing the given binary operation + // on the operand values which are passed in as two 32-bit word. + uint32_t BinaryOperate(spv::Op opcode, uint32_t a, uint32_t b) const; + + // Returns the single-word result from performing the given ternary operation + // on the operand values which are passed in as three 32-bit word. + uint32_t TernaryOperate(spv::Op opcode, uint32_t a, uint32_t b, + uint32_t c) const; + + // Returns the single-word result from performing the given operation on the + // operand words. This only works with 32-bit operations and uses boolean + // convention that 0u is false, and anything else is boolean true. + // TODO(qining): Support operands other than 32-bit wide. + uint32_t OperateWords(spv::Op opcode, + const std::vector& operand_words) const; + + bool FoldInstructionInternal(Instruction* inst) const; + + // Returns true if |inst| is a binary operation that takes two integers as + // parameters and folds to a constant that can be represented as an unsigned + // 32-bit value when the ids have been replaced by |id_map|. If |inst| can be + // folded, the resulting value is returned in |*result|. Valid result types + // for the instruction are any integer (signed or unsigned) with 32-bits or + // less, or a boolean value. + bool FoldBinaryIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const; + + // Returns true if |inst| is a binary operation on two boolean values, and + // folds + // to a constant boolean value when the ids have been replaced using |id_map|. + // If |inst| can be folded, the result value is returned in |*result|. + bool FoldBinaryBooleanOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const; + + // Returns true if |inst| can be folded to an constant when the ids have been + // substituted using id_map. If it can, the value is returned in |result|. If + // not, |result| is unchanged. It is assumed that not all operands are + // constant. Those cases are handled by |FoldScalar|. + bool FoldIntegerOpToConstant(Instruction* inst, + const std::function& id_map, + uint32_t* result) const; + + IRContext* context_; + + // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|. + std::unique_ptr const_folding_rules_; + + // Folding rules used by |FoldInstruction|. + std::unique_ptr folding_rules_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLD_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp new file mode 100644 index 00000000..ddfe59f7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -0,0 +1,330 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/fold_spec_constant_op_and_composite_pass.h" + +#include +#include + +#include "source/opt/constants.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status FoldSpecConstantOpAndCompositePass::Process() { + bool modified = false; + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + // Traverse through all the constant defining instructions. For Normal + // Constants whose values are determined and do not depend on OpUndef + // instructions, records their values in two internal maps: id_to_const_val_ + // and const_val_to_id_ so that we can use them to infer the value of Spec + // Constants later. + // For Spec Constants defined with OpSpecConstantComposite instructions, if + // all of their components are Normal Constants, they will be turned into + // Normal Constants too. For Spec Constants defined with OpSpecConstantOp + // instructions, we check if they only depends on Normal Constants and fold + // them when possible. The two maps for Normal Constants: id_to_const_val_ + // and const_val_to_id_ will be updated along the traversal so that the new + // Normal Constants generated from folding can be used to fold following Spec + // Constants. + // This algorithm depends on the SSA property of SPIR-V when + // defining constants. The dependent constants must be defined before the + // dependee constants. So a dependent Spec Constant must be defined and + // will be processed before its dependee Spec Constant. When we encounter + // the dependee Spec Constants, all its dependent constants must have been + // processed and all its dependent Spec Constants should have been folded if + // possible. + Module::inst_iterator next_inst = context()->types_values_begin(); + for (Module::inst_iterator inst_iter = next_inst; + // Need to re-evaluate the end iterator since we may modify the list of + // instructions in this section of the module as the process goes. + inst_iter != context()->types_values_end(); inst_iter = next_inst) { + ++next_inst; + Instruction* inst = &*inst_iter; + // Collect constant values of normal constants and process the + // OpSpecConstantOp and OpSpecConstantComposite instructions if possible. + // The constant values will be stored in analysis::Constant instances. + // OpConstantSampler instruction is not collected here because it cannot be + // used in OpSpecConstant{Composite|Op} instructions. + // TODO(qining): If the constant or its type has decoration, we may need + // to skip it. + if (const_mgr->GetType(inst) && + !const_mgr->GetType(inst)->decoration_empty()) + continue; + switch (spv::Op opcode = inst->opcode()) { + // Records the values of Normal Constants. + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + case spv::Op::OpConstant: + case spv::Op::OpConstantNull: + case spv::Op::OpConstantComposite: + case spv::Op::OpSpecConstantComposite: { + // A Constant instance will be created if the given instruction is a + // Normal Constant whose value(s) are fixed. Note that for a composite + // Spec Constant defined with OpSpecConstantComposite instruction, if + // all of its components are Normal Constants already, the Spec + // Constant will be turned in to a Normal Constant. In that case, a + // Constant instance should also be created successfully and recorded + // in the id_to_const_val_ and const_val_to_id_ mapps. + if (auto const_value = const_mgr->GetConstantFromInst(inst)) { + // Need to replace the OpSpecConstantComposite instruction with a + // corresponding OpConstantComposite instruction. + if (opcode == spv::Op::OpSpecConstantComposite) { + inst->SetOpcode(spv::Op::OpConstantComposite); + modified = true; + } + const_mgr->MapConstantToInst(const_value, inst); + } + break; + } + // For a Spec Constants defined with OpSpecConstantOp instruction, check + // if it only depends on Normal Constants. If so, the Spec Constant will + // be folded. The original Spec Constant defining instruction will be + // replaced by Normal Constant defining instructions, and the new Normal + // Constants will be added to id_to_const_val_ and const_val_to_id_ so + // that we can use the new Normal Constants when folding following Spec + // Constants. + case spv::Op::OpSpecConstantOp: + modified |= ProcessOpSpecConstantOp(&inst_iter); + break; + default: + break; + } + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( + Module::inst_iterator* pos) { + Instruction* inst = &**pos; + Instruction* folded_inst = nullptr; + assert(inst->GetInOperand(0).type == + SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER && + "The first in-operand of OpSpecConstantOp instruction must be of " + "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); + + folded_inst = FoldWithInstructionFolder(pos); + if (!folded_inst) { + folded_inst = DoComponentWiseOperation(pos); + } + if (!folded_inst) return false; + + // Replace the original constant with the new folded constant, kill the + // original constant. + uint32_t new_id = folded_inst->result_id(); + uint32_t old_id = inst->result_id(); + context()->ReplaceAllUsesWith(old_id, new_id); + context()->KillDef(old_id); + return true; +} + +Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( + Module::inst_iterator* inst_iter_ptr) { + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + // If one of operands to the instruction is not a + // constant, then we cannot fold this spec constant. + for (uint32_t i = 1; i < (*inst_iter_ptr)->NumInOperands(); i++) { + const Operand& operand = (*inst_iter_ptr)->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_ID) { + continue; + } + uint32_t id = operand.words[0]; + if (const_mgr->FindDeclaredConstant(id) == nullptr) { + return nullptr; + } + } + + // All of the operands are constant. Construct a regular version of the + // instruction and pass it to the instruction folder. + std::unique_ptr inst((*inst_iter_ptr)->Clone(context())); + inst->SetOpcode( + static_cast((*inst_iter_ptr)->GetSingleWordInOperand(0))); + inst->RemoveOperand(2); + + // We want the current instruction to be replaced by an |OpConstant*| + // instruction in the same position. We need to keep track of which constants + // the instruction folder creates, so we can move them into the correct place. + auto last_type_value_iter = (context()->types_values_end()); + --last_type_value_iter; + Instruction* last_type_value = &*last_type_value_iter; + + auto identity_map = [](uint32_t id) { return id; }; + Instruction* new_const_inst = + context()->get_instruction_folder().FoldInstructionToConstant( + inst.get(), identity_map); + + // new_const_inst == null indicates we cannot fold this spec constant + if (!new_const_inst) return nullptr; + + // Get the instruction before |pos| to insert after. |pos| cannot be the + // first instruction in the list because its type has to come first. + Instruction* insert_pos = (*inst_iter_ptr)->PreviousNode(); + assert(insert_pos != nullptr && + "pos is the first instruction in the types and values."); + bool need_to_clone = true; + for (Instruction* i = last_type_value->NextNode(); i != nullptr; + i = last_type_value->NextNode()) { + if (i == new_const_inst) { + need_to_clone = false; + } + i->InsertAfter(insert_pos); + insert_pos = insert_pos->NextNode(); + } + + if (need_to_clone) { + new_const_inst = new_const_inst->Clone(context()); + new_const_inst->SetResultId(TakeNextId()); + new_const_inst->InsertAfter(insert_pos); + get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst); + } + const_mgr->MapInst(new_const_inst); + return new_const_inst; +} + +namespace { +// A helper function to check the type for component wise operations. Returns +// true if the type: +// 1) is bool type; +// 2) is 32-bit int type; +// 3) is vector of bool type; +// 4) is vector of 32-bit integer type. +// Otherwise returns false. +bool IsValidTypeForComponentWiseOperation(const analysis::Type* type) { + if (type->AsBool()) { + return true; + } else if (auto* it = type->AsInteger()) { + if (it->width() == 32) return true; + } else if (auto* vt = type->AsVector()) { + if (vt->element_type()->AsBool()) { + return true; + } else if (auto* vit = vt->element_type()->AsInteger()) { + if (vit->width() == 32) return true; + } + } + return false; +} + +// Encodes the integer |value| of in a word vector format appropriate for +// representing this value as a operands for a constant definition. Performs +// zero-extension/sign-extension/truncation when needed, based on the signess of +// the given target type. +// +// Note: type |type| argument must be either Integer or Bool. +utils::SmallVector EncodeIntegerAsWords(const analysis::Type& type, + uint32_t value) { + const uint32_t all_ones = ~0; + uint32_t bit_width = 0; + uint32_t pad_value = 0; + bool result_type_signed = false; + if (auto* int_ty = type.AsInteger()) { + bit_width = int_ty->width(); + result_type_signed = int_ty->IsSigned(); + if (result_type_signed && static_cast(value) < 0) { + pad_value = all_ones; + } + } else if (type.AsBool()) { + bit_width = 1; + } else { + assert(false && "type must be Integer or Bool"); + } + + assert(bit_width > 0); + uint32_t first_word = value; + const uint32_t bits_per_word = 32; + + // Truncate first_word if the |type| has width less than uint32. + if (bit_width < bits_per_word) { + first_word = utils::SignExtendValue(first_word, bit_width); + } + + utils::SmallVector words = {first_word}; + for (uint32_t current_bit = bits_per_word; current_bit < bit_width; + current_bit += bits_per_word) { + words.push_back(pad_value); + } + + return words; +} +} // namespace + +Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( + Module::inst_iterator* pos) { + const Instruction* inst = &**pos; + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Type* result_type = const_mgr->GetType(inst); + spv::Op spec_opcode = static_cast(inst->GetSingleWordInOperand(0)); + // Check and collect operands. + std::vector operands; + + if (!std::all_of( + inst->cbegin(), inst->cend(), [&operands, this](const Operand& o) { + // skip the operands that is not an id. + if (o.type != spv_operand_type_t::SPV_OPERAND_TYPE_ID) return true; + uint32_t id = o.words.front(); + if (auto c = + context()->get_constant_mgr()->FindDeclaredConstant(id)) { + if (IsValidTypeForComponentWiseOperation(c->type())) { + operands.push_back(c); + return true; + } + } + return false; + })) + return nullptr; + + if (result_type->AsInteger() || result_type->AsBool()) { + // Scalar operation + const uint32_t result_val = + context()->get_instruction_folder().FoldScalars(spec_opcode, operands); + auto result_const = const_mgr->GetConstant( + result_type, EncodeIntegerAsWords(*result_type, result_val)); + return const_mgr->BuildInstructionAndAddToModule(result_const, pos); + } else if (result_type->AsVector()) { + // Vector operation + const analysis::Type* element_type = + result_type->AsVector()->element_type(); + uint32_t num_dims = result_type->AsVector()->element_count(); + std::vector result_vec = + context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims, + operands); + std::vector result_vector_components; + for (const uint32_t r : result_vec) { + if (auto rc = const_mgr->GetConstant( + element_type, EncodeIntegerAsWords(*element_type, r))) { + result_vector_components.push_back(rc); + if (!const_mgr->BuildInstructionAndAddToModule(rc, pos)) { + assert(false && + "Failed to build and insert constant declaring instruction " + "for the given vector component constant"); + } + } else { + assert(false && "Failed to create constants with 32-bit word"); + } + } + auto new_vec_const = MakeUnique( + result_type->AsVector(), result_vector_components); + auto reg_vec_const = const_mgr->RegisterConstant(std::move(new_vec_const)); + return const_mgr->BuildInstructionAndAddToModule(reg_vec_const, pos); + } else { + // Cannot process invalid component wise operation. The result of component + // wise operation must be of integer or bool scalar or vector of + // integer/bool type. + return nullptr; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h new file mode 100644 index 00000000..9a8fb403 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h @@ -0,0 +1,71 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ +#define SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ + +#include +#include +#include + +#include "source/opt/constants.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FoldSpecConstantOpAndCompositePass : public Pass { + public: + FoldSpecConstantOpAndCompositePass() = default; + + const char* name() const override { return "fold-spec-const-op-composite"; } + + // Iterates through the types-constants-globals section of the given module, + // finds the Spec Constants defined with OpSpecConstantOp and + // OpSpecConstantComposite instructions. If the result value of those spec + // constants can be folded, fold them to their corresponding normal constants. + Status Process() override; + + private: + // Processes the OpSpecConstantOp instruction pointed by the given + // instruction iterator, folds it to normal constants if possible. Returns + // true if the spec constant is folded to normal constants. New instructions + // will be inserted before the OpSpecConstantOp instruction pointed by the + // instruction iterator. The instruction iterator, which is passed by + // pointer, will still point to the original OpSpecConstantOp instruction. If + // folding is done successfully, the original OpSpecConstantOp instruction + // will be changed to Nop and new folded instruction will be inserted before + // it. + bool ProcessOpSpecConstantOp(Module::inst_iterator* pos); + + // Returns the result of folding the OpSpecConstantOp instruction + // |inst_iter_ptr| using the instruction folder. + Instruction* FoldWithInstructionFolder(Module::inst_iterator* inst_iter_ptr); + + // Try to fold the OpSpecConstantOp instruction + // pointed by the given instruction iterator to a normal constant defining + // instruction. Returns the pointer to the new constant defining instruction + // if succeeded, otherwise return nullptr. + Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.cpp new file mode 100644 index 00000000..3f5f79a2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.cpp @@ -0,0 +1,3135 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/folding_rules.h" + +#include +#include +#include + +#include "ir_builder.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +constexpr uint32_t kExtractCompositeIdInIdx = 0; +constexpr uint32_t kInsertObjectIdInIdx = 0; +constexpr uint32_t kInsertCompositeIdInIdx = 1; +constexpr uint32_t kExtInstSetIdInIdx = 0; +constexpr uint32_t kExtInstInstructionInIdx = 1; +constexpr uint32_t kFMixXIdInIdx = 2; +constexpr uint32_t kFMixYIdInIdx = 3; +constexpr uint32_t kFMixAIdInIdx = 4; +constexpr uint32_t kStoreObjectInIdx = 1; + +// Some image instructions may contain an "image operands" argument. +// Returns the operand index for the "image operands". +// Returns -1 if the instruction does not have image operands. +int32_t ImageOperandsMaskInOperandIndex(Instruction* inst) { + const auto opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageFetch: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseRead: + return inst->NumOperands() > 4 ? 2 : -1; + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + return inst->NumOperands() > 5 ? 3 : -1; + case spv::Op::OpImageWrite: + return inst->NumOperands() > 3 ? 3 : -1; + default: + return -1; + } +} + +// Returns the element width of |type|. +uint32_t ElementWidth(const analysis::Type* type) { + if (const analysis::CooperativeVectorNV* coopvec_type = + type->AsCooperativeVectorNV()) { + return ElementWidth(coopvec_type->component_type()); + } else if (const analysis::Vector* vec_type = type->AsVector()) { + return ElementWidth(vec_type->element_type()); + } else if (const analysis::Float* float_type = type->AsFloat()) { + return float_type->width(); + } else { + assert(type->AsInteger()); + return type->AsInteger()->width(); + } +} + +// Returns true if |type| is Float or a vector of Float. +bool HasFloatingPoint(const analysis::Type* type) { + if (type->AsFloat()) { + return true; + } else if (const analysis::Vector* vec_type = type->AsVector()) { + return vec_type->element_type()->AsFloat() != nullptr; + } + + return false; +} + +// Returns false if |val| is NaN, infinite or subnormal. +template +bool IsValidResult(T val) { + int classified = std::fpclassify(val); + switch (classified) { + case FP_NAN: + case FP_INFINITE: + case FP_SUBNORMAL: + return false; + default: + return true; + } +} + +// Returns true if `type` is a cooperative matrix. +bool IsCooperativeMatrix(const analysis::Type* type) { + return type->kind() == analysis::Type::kCooperativeMatrixKHR || + type->kind() == analysis::Type::kCooperativeMatrixNV; +} + +const analysis::Constant* ConstInput( + const std::vector& constants) { + return constants[0] ? constants[0] : constants[1]; +} + +Instruction* NonConstInput(IRContext* context, const analysis::Constant* c, + Instruction* inst) { + uint32_t in_op = c ? 1u : 0u; + return context->get_def_use_mgr()->GetDef( + inst->GetSingleWordInOperand(in_op)); +} + +std::vector ExtractInts(uint64_t val) { + std::vector words; + words.push_back(static_cast(val)); + words.push_back(static_cast(val >> 32)); + return words; +} + +std::vector GetWordsFromScalarIntConstant( + const analysis::IntConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsInteger()->width(); + assert(width == 8 || width == 16 || width == 32 || width == 64); + if (width == 64) { + uint64_t uval = static_cast(c->GetU64()); + return ExtractInts(uval); + } + // Section 2.2.1 of the SPIR-V spec guarantees that all integer types + // smaller than 32-bits are automatically zero or sign extended to 32-bits. + return {c->GetU32BitValue()}; +} + +std::vector GetWordsFromScalarFloatConstant( + const analysis::FloatConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 16 || width == 32 || width == 64); + if (width == 64) { + utils::FloatProxy result(c->GetDouble()); + return result.GetWords(); + } + // Section 2.2.1 of the SPIR-V spec guarantees that all floating-point types + // smaller than 32-bits are automatically zero extended to 32-bits. + return {c->GetU32BitValue()}; +} + +std::vector GetWordsFromNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const analysis::Constant* c) { + if (const auto* float_constant = c->AsFloatConstant()) { + return GetWordsFromScalarFloatConstant(float_constant); + } else if (const auto* int_constant = c->AsIntConstant()) { + return GetWordsFromScalarIntConstant(int_constant); + } else if (const auto* vec_constant = c->AsVectorConstant()) { + std::vector words; + for (const auto* comp : vec_constant->GetComponents()) { + auto comp_in_words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp); + words.insert(words.end(), comp_in_words.begin(), comp_in_words.end()); + } + return words; + } + return {}; +} + +const analysis::Constant* ConvertWordsToNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const std::vector& words, + const analysis::Type* type) { + const spvtools::opt::analysis::Integer* int_type = type->AsInteger(); + + if (int_type && int_type->width() <= 32) { + assert(words.size() == 1); + return const_mgr->GenerateIntegerConstant(int_type, words[0]); + } + + if (int_type || type->AsFloat()) return const_mgr->GetConstant(type, words); + if (const auto* vec_type = type->AsVector()) + return const_mgr->GetNumericVectorConstantWithWords(vec_type, words); + return nullptr; +} + +// Returns the negation of |c|. |c| must be a 32 or 64 bit floating point +// constant. +uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(c); + assert(c->type()->AsFloat()); + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; + if (width == 64) { + utils::FloatProxy result(c->GetDouble() * -1.0); + words = result.GetWords(); + } else { + utils::FloatProxy result(c->GetFloat() * -1.0f); + words = result.GetWords(); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +// Negates the integer constant |c|. Returns the id of the defining instruction. +uint32_t NegateIntegerConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(c); + assert(c->type()->AsInteger()); + uint32_t width = c->type()->AsInteger()->width(); + assert(width == 32 || width == 64); + std::vector words; + if (width == 64) { + uint64_t uval = static_cast(0 - c->GetU64()); + words = ExtractInts(uval); + } else { + words.push_back(static_cast(0 - c->GetU32())); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +// Negates the vector constant |c|. Returns the id of the defining instruction. +uint32_t NegateVectorConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(const_mgr && c); + assert(c->type()->AsVector()); + if (c->AsNullConstant()) { + // 0.0 vs -0.0 shouldn't matter. + return const_mgr->GetDefiningInstruction(c)->result_id(); + } else { + const analysis::Type* component_type = + c->AsVectorConstant()->component_type(); + std::vector words; + for (auto& comp : c->AsVectorConstant()->GetComponents()) { + if (component_type->AsFloat()) { + words.push_back(NegateFloatingPointConstant(const_mgr, comp)); + } else { + assert(component_type->AsInteger()); + words.push_back(NegateIntegerConstant(const_mgr, comp)); + } + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); + } +} + +// Negates |c|. Returns the id of the defining instruction. +uint32_t NegateConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + if (c->type()->AsVector()) { + return NegateVectorConstant(const_mgr, c); + } else if (c->type()->AsFloat()) { + return NegateFloatingPointConstant(const_mgr, c); + } else { + assert(c->type()->AsInteger()); + return NegateIntegerConstant(const_mgr, c); + } +} + +// Takes the reciprocal of |c|. |c|'s type must be Float or a vector of Float. +// Returns 0 if the reciprocal is NaN, infinite or subnormal. +uint32_t Reciprocal(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(const_mgr && c); + assert(c->type()->AsFloat()); + + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; + + if (c->IsZero()) { + return 0; + } + + if (width == 64) { + spvtools::utils::FloatProxy result(1.0 / c->GetDouble()); + if (!IsValidResult(result.getAsFloat())) return 0; + words = result.GetWords(); + } else { + spvtools::utils::FloatProxy result(1.0f / c->GetFloat()); + if (!IsValidResult(result.getAsFloat())) return 0; + words = result.GetWords(); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +// Replaces fdiv where second operand is constant with fmul. +FoldingRule ReciprocalFDiv() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + if (constants[1] != nullptr) { + uint32_t id = 0; + if (const analysis::VectorConstant* vector_const = + constants[1]->AsVectorConstant()) { + std::vector neg_ids; + for (auto& comp : vector_const->GetComponents()) { + id = Reciprocal(const_mgr, comp); + if (id == 0) return false; + neg_ids.push_back(id); + } + const analysis::Constant* negated_const = + const_mgr->GetConstant(constants[1]->type(), std::move(neg_ids)); + id = const_mgr->GetDefiningInstruction(negated_const)->result_id(); + } else if (constants[1]->AsFloatConstant()) { + id = Reciprocal(const_mgr, constants[1]); + if (id == 0) return false; + } else { + // Don't fold a null constant. + return false; + } + inst->SetOpcode(spv::Op::OpFMul); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {id}}}); + return true; + } + + return false; + }; +} + +// Elides consecutive negate instructions. +FoldingRule MergeNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFNegate || + inst->opcode() == spv::Op::OpSNegate); + (void)constants; + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (op_inst->opcode() == inst->opcode()) { + // Elide negates. + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0u)}}}); + return true; + } + + return false; + }; +} + +// Merges negate into a mul or div operation if that operation contains a +// constant operand. +// Cases: +// -(x * 2) = x * -2 +// -(2 * x) = x * -2 +// -(x / 2) = x / -2 +// -(2 / x) = -2 / x +FoldingRule MergeNegateMulDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFNegate || + inst->opcode() == spv::Op::OpSNegate); + (void)constants; + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + spv::Op opcode = op_inst->opcode(); + if (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFDiv && + opcode != spv::Op::OpIMul && opcode != spv::Op::OpSDiv) { + return false; + } + + std::vector op_constants = + const_mgr->GetOperandConstants(op_inst); + // Merge negate into mul or div if one operand is constant. + if (op_constants[0] == nullptr && op_constants[1] == nullptr) { + return false; + } + + bool zero_is_variable = op_constants[0] == nullptr; + const analysis::Constant* c = ConstInput(op_constants); + uint32_t neg_id = NegateConstant(const_mgr, c); + uint32_t non_const_id = zero_is_variable + ? op_inst->GetSingleWordInOperand(0u) + : op_inst->GetSingleWordInOperand(1u); + // Change this instruction to a mul/div. + inst->SetOpcode(op_inst->opcode()); + if (opcode == spv::Op::OpFDiv || opcode == spv::Op::OpUDiv || + opcode == spv::Op::OpSDiv) { + uint32_t op0 = zero_is_variable ? non_const_id : neg_id; + uint32_t op1 = zero_is_variable ? neg_id : non_const_id; + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op0}}, {SPV_OPERAND_TYPE_ID, {op1}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + } + return true; + }; +} + +// Merges negate into a add or sub operation if that operation contains a +// constant operand. +// Cases: +// -(x + 2) = -2 - x +// -(2 + x) = -2 - x +// -(x - 2) = 2 - x +// -(2 - x) = x - 2 +FoldingRule MergeNegateAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFNegate || + inst->opcode() == spv::Op::OpSNegate); + (void)constants; + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + if (op_inst->opcode() == spv::Op::OpFAdd || + op_inst->opcode() == spv::Op::OpFSub || + op_inst->opcode() == spv::Op::OpIAdd || + op_inst->opcode() == spv::Op::OpISub) { + std::vector op_constants = + const_mgr->GetOperandConstants(op_inst); + if (op_constants[0] || op_constants[1]) { + bool zero_is_variable = op_constants[0] == nullptr; + bool is_add = (op_inst->opcode() == spv::Op::OpFAdd) || + (op_inst->opcode() == spv::Op::OpIAdd); + bool swap_operands = !is_add || zero_is_variable; + bool negate_const = is_add; + const analysis::Constant* c = ConstInput(op_constants); + uint32_t const_id = 0; + if (negate_const) { + const_id = NegateConstant(const_mgr, c); + } else { + const_id = zero_is_variable ? op_inst->GetSingleWordInOperand(1u) + : op_inst->GetSingleWordInOperand(0u); + } + + // Swap operands if necessary and make the instruction a subtraction. + uint32_t op0 = + zero_is_variable ? op_inst->GetSingleWordInOperand(0u) : const_id; + uint32_t op1 = + zero_is_variable ? const_id : op_inst->GetSingleWordInOperand(1u); + if (swap_operands) std::swap(op0, op1); + inst->SetOpcode(HasFloatingPoint(type) ? spv::Op::OpFSub + : spv::Op::OpISub); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op0}}, {SPV_OPERAND_TYPE_ID, {op1}}}); + return true; + } + } + + return false; + }; +} + +// Returns true if |c| has a zero element. +bool HasZero(const analysis::Constant* c) { + if (c->AsNullConstant()) { + return true; + } + if (const analysis::VectorConstant* vec_const = c->AsVectorConstant()) { + for (auto& comp : vec_const->GetComponents()) + if (HasZero(comp)) return true; + } else { + assert(c->AsScalarConstant()); + return c->AsScalarConstant()->IsZero(); + } + + return false; +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Float. +uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, + spv::Op opcode, + const analysis::Constant* input1, + const analysis::Constant* input2) { + const analysis::Type* type = input1->type(); + assert(type->AsFloat()); + uint32_t width = type->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; +#define FOLD_OP(op) \ + if (width == 64) { \ + utils::FloatProxy val = \ + input1->GetDouble() op input2->GetDouble(); \ + double dval = val.getAsFloat(); \ + if (!IsValidResult(dval)) return 0; \ + words = val.GetWords(); \ + } else { \ + utils::FloatProxy val = input1->GetFloat() op input2->GetFloat(); \ + float fval = val.getAsFloat(); \ + if (!IsValidResult(fval)) return 0; \ + words = val.GetWords(); \ + } \ + static_assert(true, "require extra semicolon") + switch (opcode) { + case spv::Op::OpFMul: + FOLD_OP(*); + break; + case spv::Op::OpFDiv: + if (HasZero(input2)) return 0; + FOLD_OP(/); + break; + case spv::Op::OpFAdd: + FOLD_OP(+); + break; + case spv::Op::OpFSub: + FOLD_OP(-); + break; + default: + assert(false && "Unexpected operation"); + break; + } +#undef FOLD_OP + const analysis::Constant* merged_const = const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Integers. +uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, + spv::Op opcode, + const analysis::Constant* input1, + const analysis::Constant* input2) { + assert(input1->type()->AsInteger()); + const analysis::Integer* type = input1->type()->AsInteger(); + uint32_t width = type->AsInteger()->width(); + assert(width == 32 || width == 64); + std::vector words; + // Regardless of the sign of the constant, folding is performed on an unsigned + // interpretation of the constant data. This avoids signed integer overflow + // while folding, and works because sign is irrelevant for the IAdd, ISub and + // IMul instructions. +#define FOLD_OP(op) \ + if (width == 64) { \ + uint64_t val = input1->GetU64() op input2->GetU64(); \ + words = ExtractInts(val); \ + } else { \ + uint32_t val = input1->GetU32() op input2->GetU32(); \ + words.push_back(val); \ + } \ + static_assert(true, "require extra semicolon") + switch (opcode) { + case spv::Op::OpIMul: + FOLD_OP(*); + break; + case spv::Op::OpSDiv: + case spv::Op::OpUDiv: + assert(false && "Should not merge integer division"); + break; + case spv::Op::OpIAdd: + FOLD_OP(+); + break; + case spv::Op::OpISub: + FOLD_OP(-); + break; + default: + assert(false && "Unexpected operation"); + break; + } +#undef FOLD_OP + const analysis::Constant* merged_const = const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Integers, Floats or Vectors of such. +uint32_t PerformOperation(analysis::ConstantManager* const_mgr, spv::Op opcode, + const analysis::Constant* input1, + const analysis::Constant* input2) { + assert(input1 && input2); + const analysis::Type* type = input1->type(); + std::vector words; + if (const analysis::Vector* vector_type = type->AsVector()) { + const analysis::Type* ele_type = vector_type->element_type(); + for (uint32_t i = 0; i != vector_type->element_count(); ++i) { + uint32_t id = 0; + + const analysis::Constant* input1_comp = nullptr; + if (const analysis::VectorConstant* input1_vector = + input1->AsVectorConstant()) { + input1_comp = input1_vector->GetComponents()[i]; + } else { + assert(input1->AsNullConstant()); + input1_comp = const_mgr->GetConstant(ele_type, {}); + } + + const analysis::Constant* input2_comp = nullptr; + if (const analysis::VectorConstant* input2_vector = + input2->AsVectorConstant()) { + input2_comp = input2_vector->GetComponents()[i]; + } else { + assert(input2->AsNullConstant()); + input2_comp = const_mgr->GetConstant(ele_type, {}); + } + + if (ele_type->AsFloat()) { + id = PerformFloatingPointOperation(const_mgr, opcode, input1_comp, + input2_comp); + } else { + assert(ele_type->AsInteger()); + id = PerformIntegerOperation(const_mgr, opcode, input1_comp, + input2_comp); + } + if (id == 0) return 0; + words.push_back(id); + } + const analysis::Constant* merged_const = + const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); + } else if (type->AsFloat()) { + return PerformFloatingPointOperation(const_mgr, opcode, input1, input2); + } else { + assert(type->AsInteger()); + return PerformIntegerOperation(const_mgr, opcode, input1, input2); + } +} + +// Merges consecutive multiplies where each contains one constant operand. +// Cases: +// 2 * (x * 2) = x * 4 +// 2 * (2 * x) = x * 4 +// (x * 2) * 2 = x * 4 +// (2 * x) * 2 = x * 4 +FoldingRule MergeMulMulArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFMul || + inst->opcode() == spv::Op::OpIMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + // Determine the constant input and the variable input in |inst|. + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (HasFloatingPoint(type) && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == inst->opcode()) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + return true; + } + + return false; + }; +} + +// Merges divides into subsequent multiplies if each instruction contains one +// constant operand. Does not support integer operations. +// Cases: +// 2 * (x / 2) = x * 1 +// 2 * (2 / x) = 4 / x +// (x / 2) * 2 = x * 1 +// (2 / x) * 2 = 4 / x +// (y / x) * x = y +// x * (y / x) = y +FoldingRule MergeMulDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + for (uint32_t i = 0; i < 2; i++) { + uint32_t op_id = inst->GetSingleWordInOperand(i); + Instruction* op_inst = def_use_mgr->GetDef(op_id); + if (op_inst->opcode() == spv::Op::OpFDiv) { + if (op_inst->GetSingleWordInOperand(1) == + inst->GetSingleWordInOperand(1 - i)) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0)}}}); + return true; + } + } + } + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + if (other_inst->opcode() == spv::Op::OpFDiv) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2 || HasZero(const_input2)) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + // If the variable value is the second operand of the divide, multiply + // the constants together. Otherwise divide the constants. + uint32_t merged_id = PerformOperation( + const_mgr, + other_first_is_variable ? other_inst->opcode() : inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + // If the variable value is on the second operand of the div, then this + // operation is a div. Otherwise it should be a multiply. + inst->SetOpcode(other_first_is_variable ? inst->opcode() + : other_inst->opcode()); + if (other_first_is_variable) { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_id}}, + {SPV_OPERAND_TYPE_ID, {non_const_id}}}); + } + return true; + } + + return false; + }; +} + +// Merges multiply of constant and negation. +// Cases: +// (-x) * 2 = x * -2 +// 2 * (-x) = x * -2 +FoldingRule MergeMulNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFMul || + inst->opcode() == spv::Op::OpIMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpFNegate || + other_inst->opcode() == spv::Op::OpSNegate) { + uint32_t neg_id = NegateConstant(const_mgr, const_input1); + + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + return true; + } + + return false; + }; +} + +// Merges consecutive divides if each instruction contains one constant operand. +// Does not support integer division. +// Cases: +// 2 / (x / 2) = 4 / x +// 4 / (2 / x) = 2 * x +// (4 / x) / 2 = 2 / x +// (x / 2) / 2 = x / 4 +FoldingRule MergeDivDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1 || HasZero(const_input1)) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == inst->opcode()) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2 || HasZero(const_input2)) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + + spv::Op merge_op = inst->opcode(); + if (other_first_is_variable) { + // Constants magnify. + merge_op = spv::Op::OpFMul; + } + + // This is an x / (*) case. Swap the inputs. Doesn't harm multiply + // because it is commutative. + if (first_is_variable) std::swap(const_input1, const_input2); + uint32_t merged_id = + PerformOperation(const_mgr, merge_op, const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + spv::Op op = inst->opcode(); + if (!first_is_variable && !other_first_is_variable) { + // Effectively div of 1/x, so change to multiply. + op = spv::Op::OpFMul; + } + + uint32_t op1 = merged_id; + uint32_t op2 = non_const_id; + if (first_is_variable && other_first_is_variable) std::swap(op1, op2); + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + + return false; + }; +} + +// Fold multiplies succeeded by divides where each instruction contains a +// constant operand. Does not support integer divide. +// Cases: +// 4 / (x * 2) = 2 / x +// 4 / (2 * x) = 2 / x +// (x * 4) / 2 = x * 2 +// (4 * x) / 2 = x * 2 +// (x * y) / x = y +// (y * x) / x = y +FoldingRule MergeDivMulArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFDiv); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + uint32_t op_id = inst->GetSingleWordInOperand(0); + Instruction* op_inst = def_use_mgr->GetDef(op_id); + + if (op_inst->opcode() == spv::Op::OpFMul) { + for (uint32_t i = 0; i < 2; i++) { + if (op_inst->GetSingleWordInOperand(i) == + inst->GetSingleWordInOperand(1)) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {op_inst->GetSingleWordInOperand(1 - i)}}}); + return true; + } + } + } + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1 || HasZero(const_input1)) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == spv::Op::OpFMul) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + + // This is an x / (*) case. Swap the inputs. + if (first_is_variable) std::swap(const_input1, const_input2); + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + uint32_t op1 = merged_id; + uint32_t op2 = non_const_id; + if (first_is_variable) std::swap(op1, op2); + + // Convert to multiply + if (first_is_variable) inst->SetOpcode(other_inst->opcode()); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + + return false; + }; +} + +// Fold divides of a constant and a negation. +// Cases: +// (-x) / 2 = x / -2 +// 2 / (-x) = -2 / x +FoldingRule MergeDivNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == spv::Op::OpFNegate) { + uint32_t neg_id = NegateConstant(const_mgr, const_input1); + + if (first_is_variable) { + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + } else { + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {neg_id}}, + {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}}); + } + return true; + } + + return false; + }; +} + +// Folds addition of a constant and a negation. +// Cases: +// (-x) + 2 = 2 - x +// 2 + (-x) = 2 - x +FoldingRule MergeAddNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpSNegate || + other_inst->opcode() == spv::Op::OpFNegate) { + inst->SetOpcode(HasFloatingPoint(type) ? spv::Op::OpFSub + : spv::Op::OpISub); + uint32_t const_id = constants[0] ? inst->GetSingleWordInOperand(0u) + : inst->GetSingleWordInOperand(1u); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {const_id}}, + {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of a constant and a negation. +// Cases: +// (-x) - 2 = -2 - x +// 2 - (-x) = x + 2 +FoldingRule MergeSubNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpSNegate || + other_inst->opcode() == spv::Op::OpFNegate) { + uint32_t op1 = 0; + uint32_t op2 = 0; + spv::Op opcode = inst->opcode(); + if (constants[0] != nullptr) { + op1 = other_inst->GetSingleWordInOperand(0u); + op2 = inst->GetSingleWordInOperand(0u); + opcode = HasFloatingPoint(type) ? spv::Op::OpFAdd : spv::Op::OpIAdd; + } else { + op1 = NegateConstant(const_mgr, const_input1); + op2 = other_inst->GetSingleWordInOperand(0u); + } + + inst->SetOpcode(opcode); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds addition of an addition where each operation has a constant operand. +// Cases: +// (x + 2) + 2 = x + 4 +// (2 + x) + 2 = x + 4 +// 2 + (x + 2) = x + 4 +// 2 + (2 + x) = x + 4 +FoldingRule MergeAddAddArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpFAdd || + other_inst->opcode() == spv::Op::OpIAdd) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + return true; + } + return false; + }; +} + +// Folds addition of a subtraction where each operation has a constant operand. +// Cases: +// (x - 2) + 2 = x + 0 +// (2 - x) + 2 = 4 - x +// 2 + (x - 2) = x + 0 +// 2 + (2 - x) = 4 - x +FoldingRule MergeAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpFSub || + other_inst->opcode() == spv::Op::OpISub) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool first_is_variable = other_constants[0] == nullptr; + spv::Op op = inst->opcode(); + uint32_t op1 = 0; + uint32_t op2 = 0; + if (first_is_variable) { + // Subtract constants. Non-constant operand is first. + op1 = other_inst->GetSingleWordInOperand(0u); + op2 = PerformOperation(const_mgr, other_inst->opcode(), const_input1, + const_input2); + } else { + // Add constants. Constant operand is first. Change the opcode. + op1 = PerformOperation(const_mgr, inst->opcode(), const_input1, + const_input2); + op2 = other_inst->GetSingleWordInOperand(1u); + op = other_inst->opcode(); + } + if (op1 == 0 || op2 == 0) return false; + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of an addition where each operand has a constant operand. +// Cases: +// (x + 2) - 2 = x + 0 +// (2 + x) - 2 = x + 0 +// 2 - (x + 2) = 0 - x +// 2 - (2 + x) = 0 - x +FoldingRule MergeSubAddArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpFAdd || + other_inst->opcode() == spv::Op::OpIAdd) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + + // If the first operand of the sub is not a constant, swap the constants + // so the subtraction has the correct operands. + if (constants[0] == nullptr) std::swap(const_input1, const_input2); + // Subtract the constants. + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + spv::Op op = inst->opcode(); + uint32_t op1 = 0; + uint32_t op2 = 0; + if (constants[0] == nullptr) { + // Non-constant operand is first. Change the opcode. + op1 = non_const_input->result_id(); + op2 = merged_id; + op = other_inst->opcode(); + } else { + // Constant operand is first. + op1 = merged_id; + op2 = non_const_input->result_id(); + } + if (op1 == 0 || op2 == 0) return false; + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of a subtraction where each operand has a constant operand. +// Cases: +// (x - 2) - 2 = x - 4 +// (2 - x) - 2 = 0 - x +// 2 - (x - 2) = 4 - x +// 2 - (2 - x) = x + 0 +FoldingRule MergeSubSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFSub || + inst->opcode() == spv::Op::OpISub); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == spv::Op::OpFSub || + other_inst->opcode() == spv::Op::OpISub) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + + // Merge the constants. + uint32_t merged_id = 0; + spv::Op merge_op = inst->opcode(); + if (other_constants[0] == nullptr) { + merge_op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd; + } else if (constants[0] == nullptr) { + std::swap(const_input1, const_input2); + } + merged_id = + PerformOperation(const_mgr, merge_op, const_input1, const_input2); + if (merged_id == 0) return false; + + spv::Op op = inst->opcode(); + if (constants[0] != nullptr && other_constants[0] != nullptr) { + // Change the operation. + op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd; + } + + uint32_t op1 = 0; + uint32_t op2 = 0; + if ((constants[0] == nullptr) ^ (other_constants[0] == nullptr)) { + op1 = merged_id; + op2 = non_const_input->result_id(); + } else { + op1 = non_const_input->result_id(); + op2 = merged_id; + } + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Helper function for MergeGenericAddSubArithmetic. If |addend| and +// subtrahend of |sub| is the same, merge to copy of minuend of |sub|. +bool MergeGenericAddendSub(uint32_t addend, uint32_t sub, Instruction* inst) { + IRContext* context = inst->context(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* sub_inst = def_use_mgr->GetDef(sub); + if (sub_inst->opcode() != spv::Op::OpFSub && + sub_inst->opcode() != spv::Op::OpISub) + return false; + if (sub_inst->opcode() == spv::Op::OpFSub && + !sub_inst->IsFloatingPointFoldingAllowed()) + return false; + if (addend != sub_inst->GetSingleWordInOperand(1)) return false; + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {sub_inst->GetSingleWordInOperand(0)}}}); + context->UpdateDefUse(inst); + return true; +} + +// Folds addition of a subtraction where the subtrahend is equal to the +// other addend. Return a copy of the minuend. Accepts generic (const and +// non-const) operands. +// Cases: +// (a - b) + b = a +// b + (a - b) = a +FoldingRule MergeGenericAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + + if (IsCooperativeMatrix(type)) { + return false; + } + + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + uint32_t add_op0 = inst->GetSingleWordInOperand(0); + uint32_t add_op1 = inst->GetSingleWordInOperand(1); + if (MergeGenericAddendSub(add_op0, add_op1, inst)) return true; + return MergeGenericAddendSub(add_op1, add_op0, inst); + }; +} + +// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|, +// generate |factor0_0| * (|factor0_1| + |factor1_1|). +bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1, + uint32_t factor1_0, uint32_t factor1_1, + Instruction* inst) { + IRContext* context = inst->context(); + if (factor0_0 != factor1_0) return false; + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* new_add_inst = ir_builder.AddBinaryOp( + inst->type_id(), inst->opcode(), factor0_1, factor1_1); + inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? spv::Op::OpFMul + : spv::Op::OpIMul); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}}, + {SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}}); + context->UpdateDefUse(inst); + return true; +} + +// Perform the following factoring identity, handling all operand order +// combinations: (a * b) + (a * c) = a * (b + c) +FoldingRule FactorAddMuls() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpFAdd || + inst->opcode() == spv::Op::OpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t add_op0 = inst->GetSingleWordInOperand(0); + Instruction* add_op0_inst = def_use_mgr->GetDef(add_op0); + if (add_op0_inst->opcode() != spv::Op::OpFMul && + add_op0_inst->opcode() != spv::Op::OpIMul) + return false; + uint32_t add_op1 = inst->GetSingleWordInOperand(1); + Instruction* add_op1_inst = def_use_mgr->GetDef(add_op1); + if (add_op1_inst->opcode() != spv::Op::OpFMul && + add_op1_inst->opcode() != spv::Op::OpIMul) + return false; + + // Only perform this optimization if both of the muls only have one use. + // Otherwise this is a deoptimization in size and performance. + if (def_use_mgr->NumUses(add_op0_inst) > 1) return false; + if (def_use_mgr->NumUses(add_op1_inst) > 1) return false; + + if (add_op0_inst->opcode() == spv::Op::OpFMul && + (!add_op0_inst->IsFloatingPointFoldingAllowed() || + !add_op1_inst->IsFloatingPointFoldingAllowed())) + return false; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + // Check if operand i in add_op0_inst matches operand j in add_op1_inst. + if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i), + add_op0_inst->GetSingleWordInOperand(1 - i), + add_op1_inst->GetSingleWordInOperand(j), + add_op1_inst->GetSingleWordInOperand(1 - j), + inst)) + return true; + } + } + return false; + }; +} + +FoldingRule IntMultipleBy1() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpIMul && + "Wrong opcode. Should be OpIMul."); + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] == nullptr) { + continue; + } + const analysis::IntConstant* int_constant = constants[i]->AsIntConstant(); + if (int_constant) { + uint32_t width = ElementWidth(int_constant->type()); + if (width != 32 && width != 64) return false; + bool is_one = (width == 32) ? int_constant->GetU32BitValue() == 1u + : int_constant->GetU64BitValue() == 1ull; + if (is_one) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1 - i)}}}); + return true; + } + } + } + return false; + }; +} + +// Returns the number of elements that the |index|th in operand in |inst| +// contributes to the result of |inst|. |inst| must be an +// OpCompositeConstructInstruction. +uint32_t GetNumOfElementsContributedByOperand(IRContext* context, + const Instruction* inst, + uint32_t index) { + assert(inst->opcode() == spv::Op::OpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + analysis::Vector* result_type = + type_mgr->GetType(inst->type_id())->AsVector(); + if (result_type == nullptr) { + // If the result of the OpCompositeConstruct is not a vector then every + // operands corresponds to a single element in the result. + return 1; + } + + // If the result type is a vector then the operands are either scalars or + // vectors. If it is a scalar, then it corresponds to a single element. If it + // is a vector, then each element in the vector will be an element in the + // result. + uint32_t id = inst->GetSingleWordInOperand(index); + Instruction* def = def_use_mgr->GetDef(id); + analysis::Vector* type = type_mgr->GetType(def->type_id())->AsVector(); + if (type == nullptr) { + return 1; + } + return type->element_count(); +} + +// Returns the in-operands for an OpCompositeExtract instruction that are needed +// to extract the |result_index|th element in the result of |inst| without using +// the result of |inst|. Returns the empty vector if |result_index| is +// out-of-bounds. |inst| must be an |OpCompositeConstruct| instruction. +std::vector GetExtractOperandsForElementOfCompositeConstruct( + IRContext* context, const Instruction* inst, uint32_t result_index) { + assert(inst->opcode() == spv::Op::OpCompositeConstruct); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + if (result_type->AsVector() == nullptr) { + if (result_index < inst->NumInOperands()) { + uint32_t id = inst->GetSingleWordInOperand(result_index); + return {Operand(SPV_OPERAND_TYPE_ID, {id})}; + } + return {}; + } + + // If the result type is a vector, then vector operands are concatenated. + uint32_t total_element_count = 0; + for (uint32_t idx = 0; idx < inst->NumInOperands(); ++idx) { + uint32_t element_count = + GetNumOfElementsContributedByOperand(context, inst, idx); + total_element_count += element_count; + if (result_index < total_element_count) { + std::vector operands; + uint32_t id = inst->GetSingleWordInOperand(idx); + Instruction* operand_def = def_use_mgr->GetDef(id); + analysis::Type* operand_type = type_mgr->GetType(operand_def->type_id()); + + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + if (operand_type->AsVector()) { + uint32_t start_index_of_id = total_element_count - element_count; + uint32_t index_into_id = result_index - start_index_of_id; + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index_into_id}}); + } + return operands; + } + } + return {}; +} + +bool CompositeConstructFeedingExtract( + IRContext* context, Instruction* inst, + const std::vector&) { + // If the input to an OpCompositeExtract is an OpCompositeConstruct, + // then we can simply use the appropriate element in the construction. + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If there are no index operands, then this rule cannot do anything. + if (inst->NumInOperands() <= 1) { + return false; + } + + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != spv::Op::OpCompositeConstruct) { + return false; + } + + uint32_t index_into_result = inst->GetSingleWordInOperand(1); + std::vector operands = + GetExtractOperandsForElementOfCompositeConstruct(context, cinst, + index_into_result); + + if (operands.empty()) { + return false; + } + + // Add the remaining indices for extraction. + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {inst->GetSingleWordInOperand(i)}}); + } + + if (operands.size() == 1) { + // If there were no extra indices, then we have the final object. No need + // to extract any more. + inst->SetOpcode(spv::Op::OpCopyObject); + } + + inst->SetInOperands(std::move(operands)); + return true; +} + +// Walks the indexes chain from |start| to |end| of an OpCompositeInsert or +// OpCompositeExtract instruction, and returns the type id of the final element +// being accessed. Returns 0 if a valid type could not be found. +uint32_t GetElementType(uint32_t type_id, Instruction::iterator start, + Instruction::iterator end, + const analysis::DefUseManager* def_use_manager) { + for (auto index : make_range(std::move(start), std::move(end))) { + const Instruction* type_inst = def_use_manager->GetDef(type_id); + assert(index.type == SPV_OPERAND_TYPE_LITERAL_INTEGER && + index.words.size() == 1); + if (type_inst->opcode() == spv::Op::OpTypeArray) { + type_id = type_inst->GetSingleWordInOperand(0); + } else if (type_inst->opcode() == spv::Op::OpTypeMatrix) { + type_id = type_inst->GetSingleWordInOperand(0); + } else if (type_inst->opcode() == spv::Op::OpTypeStruct) { + type_id = type_inst->GetSingleWordInOperand(index.words[0]); + } else { + return 0; + } + } + return type_id; +} + +// Returns true of |inst_1| and |inst_2| have the same indexes that will be used +// to index into a composite object, excluding the last index. The two +// instructions must have the same opcode, and be either OpCompositeExtract or +// OpCompositeInsert instructions. +bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) { + assert(inst_1->opcode() == inst_2->opcode() && + "Expecting the opcodes to be the same."); + assert((inst_1->opcode() == spv::Op::OpCompositeInsert || + inst_1->opcode() == spv::Op::OpCompositeExtract) && + "Instructions must be OpCompositeInsert or OpCompositeExtract."); + + if (inst_1->NumInOperands() != inst_2->NumInOperands()) { + return false; + } + + uint32_t first_index_position = + (inst_1->opcode() == spv::Op::OpCompositeInsert ? 2 : 1); + for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1; + i++) { + if (inst_1->GetSingleWordInOperand(i) != + inst_2->GetSingleWordInOperand(i)) { + return false; + } + } + return true; +} + +// If the OpCompositeConstruct is simply putting back together elements that +// where extracted from the same source, we can simply reuse the source. +// +// This is a common code pattern because of the way that scalar replacement +// works. +bool CompositeExtractFeedingConstruct( + IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpCompositeConstruct && + "Wrong opcode. Should be OpCompositeConstruct."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t original_id = 0; + + if (inst->NumInOperands() == 0) { + // The struct being constructed has no members. + return false; + } + + // Check each element to make sure they are: + // - extractions + // - extracting the same position they are inserting + // - all extract from the same id. + Instruction* first_element_inst = nullptr; + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + const uint32_t element_id = inst->GetSingleWordInOperand(i); + Instruction* element_inst = def_use_mgr->GetDef(element_id); + if (first_element_inst == nullptr) { + first_element_inst = element_inst; + } + + if (element_inst->opcode() != spv::Op::OpCompositeExtract) { + return false; + } + + if (!HaveSameIndexesExceptForLast(element_inst, first_element_inst)) { + return false; + } + + if (element_inst->GetSingleWordInOperand(element_inst->NumInOperands() - + 1) != i) { + return false; + } + + if (i == 0) { + original_id = + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + } else if (original_id != + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) { + return false; + } + } + assert(first_element_inst != nullptr); + + // The last check it to see that the object being extracted from is the + // correct type. + Instruction* original_inst = def_use_mgr->GetDef(original_id); + uint32_t original_type_id = + GetElementType(original_inst->type_id(), first_element_inst->begin() + 3, + first_element_inst->end() - 1, def_use_mgr); + + if (inst->type_id() != original_type_id) { + return false; + } + + if (first_element_inst->NumInOperands() == 2) { + // Simplify by using the original object. + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + return true; + } + + // Copies the original id and all indexes except for the last to the new + // extract instruction. + inst->SetOpcode(spv::Op::OpCompositeExtract); + inst->SetInOperands(std::vector(first_element_inst->begin() + 2, + first_element_inst->end() - 1)); + return true; +} + +FoldingRule InsertFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != spv::Op::OpCompositeInsert) { + return false; + } + + // Find the first position where the list of insert and extract indicies + // differ, if at all. + uint32_t i; + for (i = 1; i < inst->NumInOperands(); ++i) { + if (i + 1 >= cinst->NumInOperands()) { + break; + } + + if (inst->GetSingleWordInOperand(i) != + cinst->GetSingleWordInOperand(i + 1)) { + break; + } + } + + // We are extracting the element that was inserted. + if (i == inst->NumInOperands() && i + 1 == cinst->NumInOperands()) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}}}); + return true; + } + + // Extracting the value that was inserted along with values for the base + // composite. Cannot do anything. + if (i == inst->NumInOperands()) { + return false; + } + + // Extracting an element of the value that was inserted. Extract from + // that value directly. + if (i + 1 == cinst->NumInOperands()) { + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}}); + for (; i < inst->NumInOperands(); ++i) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {inst->GetSingleWordInOperand(i)}}); + } + inst->SetInOperands(std::move(operands)); + return true; + } + + // Extracting a value that is disjoint from the element being inserted. + // Rewrite the extract to use the composite input to the insert. + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertCompositeIdInIdx)}}); + for (i = 1; i < inst->NumInOperands(); ++i) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {inst->GetSingleWordInOperand(i)}}); + } + inst->SetInOperands(std::move(operands)); + return true; + }; +} + +// When a VectorShuffle is feeding an Extract, we can extract from one of the +// operands of the VectorShuffle. We just need to adjust the index in the +// extract instruction. +FoldingRule VectorShuffleFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != spv::Op::OpVectorShuffle) { + return false; + } + + // Find the size of the first vector operand of the VectorShuffle + Instruction* first_input = + def_use_mgr->GetDef(cinst->GetSingleWordInOperand(0)); + analysis::Type* first_input_type = + type_mgr->GetType(first_input->type_id()); + assert(first_input_type->AsVector() && + "Input to vector shuffle should be vectors."); + uint32_t first_input_size = first_input_type->AsVector()->element_count(); + + // Get index of the element the vector shuffle is placing in the position + // being extracted. + uint32_t new_index = + cinst->GetSingleWordInOperand(2 + inst->GetSingleWordInOperand(1)); + + // Extracting an undefined value so fold this extract into an undef. + const uint32_t undef_literal_value = 0xffffffff; + if (new_index == undef_literal_value) { + inst->SetOpcode(spv::Op::OpUndef); + inst->SetInOperands({}); + return true; + } + + // Get the id of the of the vector the elemtent comes from, and update the + // index if needed. + uint32_t new_vector = 0; + if (new_index < first_input_size) { + new_vector = cinst->GetSingleWordInOperand(0); + } else { + new_vector = cinst->GetSingleWordInOperand(1); + new_index -= first_input_size; + } + + // Update the extract instruction. + inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); + inst->SetInOperand(1, {new_index}); + return true; + }; +} + +// When an FMix with is feeding an Extract that extracts an element whose +// corresponding |a| in the FMix is 0 or 1, we can extract from one of the +// operands of the FMix. +FoldingRule FMixFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + uint32_t composite_id = + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* composite_inst = def_use_mgr->GetDef(composite_id); + + if (composite_inst->opcode() != spv::Op::OpExtInst) { + return false; + } + + uint32_t inst_set_id = + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (composite_inst->GetSingleWordInOperand(kExtInstSetIdInIdx) != + inst_set_id || + composite_inst->GetSingleWordInOperand(kExtInstInstructionInIdx) != + GLSLstd450FMix) { + return false; + } + + // Get the |a| for the FMix instruction. + uint32_t a_id = composite_inst->GetSingleWordInOperand(kFMixAIdInIdx); + std::unique_ptr a(inst->Clone(context)); + a->SetInOperand(kExtractCompositeIdInIdx, {a_id}); + context->get_instruction_folder().FoldInstruction(a.get()); + + if (a->opcode() != spv::Op::OpCopyObject) { + return false; + } + + const analysis::Constant* a_const = + const_mgr->FindDeclaredConstant(a->GetSingleWordInOperand(0)); + + if (!a_const) { + return false; + } + + bool use_x = false; + + assert(a_const->type()->AsFloat()); + double element_value = a_const->GetValueAsDouble(); + if (element_value == 0.0) { + use_x = true; + } else if (element_value == 1.0) { + use_x = false; + } else { + return false; + } + + // Get the id of the of the vector the element comes from. + uint32_t new_vector = 0; + if (use_x) { + new_vector = composite_inst->GetSingleWordInOperand(kFMixXIdInIdx); + } else { + new_vector = composite_inst->GetSingleWordInOperand(kFMixYIdInIdx); + } + + // Update the extract instruction. + inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); + return true; + }; +} + +// Returns the number of elements in the composite type |type|. Returns 0 if +// |type| is a scalar value. Return UINT32_MAX when the size is unknown at +// compile time. +uint32_t GetNumberOfElements(const analysis::Type* type) { + if (auto* vector_type = type->AsVector()) { + return vector_type->element_count(); + } + if (auto* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } + if (auto* struct_type = type->AsStruct()) { + return static_cast(struct_type->element_types().size()); + } + if (auto* array_type = type->AsArray()) { + if (array_type->length_info().words[0] == + analysis::Array::LengthInfo::kConstant && + array_type->length_info().words.size() == 2) { + return array_type->length_info().words[1]; + } + return UINT32_MAX; + } + return 0; +} + +// Returns a map with the set of values that were inserted into an object by +// the chain of OpCompositeInsertInstruction starting with |inst|. +// The map will map the index to the value inserted at that index. An empty map +// will be returned if the map could not be properly generated. +std::map GetInsertedValues(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr(); + std::map values_inserted; + Instruction* current_inst = inst; + while (current_inst->opcode() == spv::Op::OpCompositeInsert) { + if (current_inst->NumInOperands() > inst->NumInOperands()) { + // This is to catch the case + // %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0 + // %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0 + // %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1 + // In this case we cannot do a single construct to get the matrix. + uint32_t partially_inserted_element_index = + current_inst->GetSingleWordInOperand(inst->NumInOperands() - 1); + if (values_inserted.count(partially_inserted_element_index) == 0) + return {}; + } + if (HaveSameIndexesExceptForLast(inst, current_inst)) { + values_inserted.insert( + {current_inst->GetSingleWordInOperand(current_inst->NumInOperands() - + 1), + current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx)}); + } + current_inst = def_use_mgr->GetDef( + current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx)); + } + return values_inserted; +} + +// Returns true of there is an entry in |values_inserted| for every element of +// |Type|. +bool DoInsertedValuesCoverEntireObject( + const analysis::Type* type, std::map& values_inserted) { + uint32_t container_size = GetNumberOfElements(type); + if (container_size != values_inserted.size()) { + return false; + } + + if (values_inserted.rbegin()->first >= container_size) { + return false; + } + return true; +} + +// Returns id of the type of the element that immediately contains the element +// being inserted by the OpCompositeInsert instruction |inst|. Returns 0 if it +// could not be found. +uint32_t GetContainerTypeId(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCompositeInsert); + analysis::DefUseManager* def_use_manager = inst->context()->get_def_use_mgr(); + uint32_t container_type_id = GetElementType( + inst->type_id(), inst->begin() + 4, inst->end() - 1, def_use_manager); + return container_type_id; +} + +// Returns an OpCompositeConstruct instruction that build an object with +// |type_id| out of the values in |values_inserted|. Each value will be +// placed at the index corresponding to the value. The new instruction will +// be placed before |insert_before|. +Instruction* BuildCompositeConstruct( + uint32_t type_id, const std::map& values_inserted, + Instruction* insert_before) { + InstructionBuilder ir_builder( + insert_before->context(), insert_before, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + std::vector ids_in_order; + for (auto it : values_inserted) { + ids_in_order.push_back(it.second); + } + Instruction* construct = + ir_builder.AddCompositeConstruct(type_id, ids_in_order); + return construct; +} + +// Replaces the OpCompositeInsert |inst| that inserts |construct| into the same +// object as |inst| with final index removed. If the resulting +// OpCompositeInsert instruction would have no remaining indexes, the +// instruction is replaced with an OpCopyObject instead. +void InsertConstructedObject(Instruction* inst, const Instruction* construct) { + if (inst->NumInOperands() == 3) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}}); + } else { + inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()}); + inst->RemoveOperand(inst->NumOperands() - 1); + } +} + +// Replaces a series of |OpCompositeInsert| instruction that cover the entire +// object with an |OpCompositeConstruct|. +bool CompositeInsertToCompositeConstruct( + IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpCompositeInsert && + "Wrong opcode. Should be OpCompositeInsert."); + if (inst->NumInOperands() < 3) return false; + + std::map values_inserted = GetInsertedValues(inst); + uint32_t container_type_id = GetContainerTypeId(inst); + if (container_type_id == 0) { + return false; + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* container_type = type_mgr->GetType(container_type_id); + assert(container_type && "GetContainerTypeId returned a bad id."); + if (!DoInsertedValuesCoverEntireObject(container_type, values_inserted)) { + return false; + } + + Instruction* construct = + BuildCompositeConstruct(container_type_id, values_inserted, inst); + InsertConstructedObject(inst, construct); + return true; +} + +FoldingRule RedundantPhi() { + // An OpPhi instruction where all values are the same or the result of the phi + // itself, can be replaced by the value itself. + return [](IRContext*, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpPhi && + "Wrong opcode. Should be OpPhi."); + + uint32_t incoming_value = 0; + + for (uint32_t i = 0; i < inst->NumInOperands(); i += 2) { + uint32_t op_id = inst->GetSingleWordInOperand(i); + if (op_id == inst->result_id()) { + continue; + } + + if (incoming_value == 0) { + incoming_value = op_id; + } else if (op_id != incoming_value) { + // Found two possible value. Can't simplify. + return false; + } + } + + if (incoming_value == 0) { + // Code looks invalid. Don't do anything. + return false; + } + + // We have a single incoming value. Simplify using that value. + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {incoming_value}}}); + return true; + }; +} + +FoldingRule BitCastScalarOrVector() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpBitcast && constants.size() == 1); + if (constants[0] == nullptr) return false; + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]); + if (words.size() == 0) return false; + + const analysis::Constant* bitcasted_constant = + ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type); + if (!bitcasted_constant) return false; + + auto new_feeder_id = + const_mgr->GetDefiningInstruction(bitcasted_constant, inst->type_id()) + ->result_id(); + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}}); + return true; + }; +} + +FoldingRule RedundantSelect() { + // An OpSelect instruction where both values are the same or the condition is + // constant can be replaced by one of the values + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpSelect && + "Wrong opcode. Should be OpSelect."); + assert(inst->NumInOperands() == 3); + assert(constants.size() == 3); + + uint32_t true_id = inst->GetSingleWordInOperand(1); + uint32_t false_id = inst->GetSingleWordInOperand(2); + + if (true_id == false_id) { + // Both results are the same, condition doesn't matter + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}}); + return true; + } else if (constants[0]) { + const analysis::Type* type = constants[0]->type(); + if (type->AsBool()) { + // Scalar constant value, select the corresponding value. + inst->SetOpcode(spv::Op::OpCopyObject); + if (constants[0]->AsNullConstant() || + !constants[0]->AsBoolConstant()->value()) { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}}); + } + return true; + } else { + assert(type->AsVector()); + if (constants[0]->AsNullConstant()) { + // All values come from false id. + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}}); + return true; + } else { + // Convert to a vector shuffle. + std::vector ops; + ops.push_back({SPV_OPERAND_TYPE_ID, {true_id}}); + ops.push_back({SPV_OPERAND_TYPE_ID, {false_id}}); + const analysis::VectorConstant* vector_const = + constants[0]->AsVectorConstant(); + uint32_t size = + static_cast(vector_const->GetComponents().size()); + for (uint32_t i = 0; i != size; ++i) { + const analysis::Constant* component = + vector_const->GetComponents()[i]; + if (component->AsNullConstant() || + !component->AsBoolConstant()->value()) { + // Selecting from the false vector which is the second input + // vector to the shuffle. Offset the index by |size|. + ops.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i + size}}); + } else { + // Selecting from true vector which is the first input vector to + // the shuffle. + ops.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}); + } + } + + inst->SetOpcode(spv::Op::OpVectorShuffle); + inst->SetInOperands(std::move(ops)); + return true; + } + } + } + + return false; + }; +} + +enum class FloatConstantKind { Unknown, Zero, One }; + +FloatConstantKind getFloatConstantKind(const analysis::Constant* constant) { + if (constant == nullptr) { + return FloatConstantKind::Unknown; + } + + assert(HasFloatingPoint(constant->type()) && "Unexpected constant type"); + + if (constant->AsNullConstant()) { + return FloatConstantKind::Zero; + } else if (const analysis::VectorConstant* vc = + constant->AsVectorConstant()) { + const std::vector& components = + vc->GetComponents(); + assert(!components.empty()); + + FloatConstantKind kind = getFloatConstantKind(components[0]); + + for (size_t i = 1; i < components.size(); ++i) { + if (getFloatConstantKind(components[i]) != kind) { + return FloatConstantKind::Unknown; + } + } + + return kind; + } else if (const analysis::FloatConstant* fc = constant->AsFloatConstant()) { + if (fc->IsZero()) return FloatConstantKind::Zero; + + uint32_t width = fc->type()->AsFloat()->width(); + if (width != 32 && width != 64) return FloatConstantKind::Unknown; + + double value = (width == 64) ? fc->GetDoubleValue() : fc->GetFloatValue(); + + if (value == 0.0) { + return FloatConstantKind::Zero; + } else if (value == 1.0) { + return FloatConstantKind::One; + } else { + return FloatConstantKind::Unknown; + } + } else { + return FloatConstantKind::Unknown; + } +} + +FoldingRule RedundantFAdd() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFAdd && + "Wrong opcode. Should be OpFAdd."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::Zero ? 1 : 0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFSub() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFSub && + "Wrong opcode. Should be OpFSub."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero) { + inst->SetOpcode(spv::Op::OpFNegate); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1)}}}); + return true; + } + + if (kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFMul() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFMul && + "Wrong opcode. Should be OpFMul."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::Zero ? 0 : 1)}}}); + return true; + } + + if (kind0 == FloatConstantKind::One || kind1 == FloatConstantKind::One) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::One ? 1 : 0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFDiv() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFDiv && + "Wrong opcode. Should be OpFDiv."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::One) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFMod() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpFMod && + "Wrong opcode. Should be OpFMod."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + + if (kind0 == FloatConstantKind::Zero) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFMix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpExtInst && + "Wrong opcode. Should be OpExtInst."); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + uint32_t instSetId = + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (inst->GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId && + inst->GetSingleWordInOperand(kExtInstInstructionInIdx) == + GLSLstd450FMix) { + assert(constants.size() == 5); + + FloatConstantKind kind4 = getFloatConstantKind(constants[4]); + + if (kind4 == FloatConstantKind::Zero || kind4 == FloatConstantKind::One) { + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand(kind4 == FloatConstantKind::Zero + ? kFMixXIdInIdx + : kFMixYIdInIdx)}}}); + return true; + } + } + + return false; + }; +} + +// Returns a folding rule that folds the instruction to operand |foldToArg| +// (0 or 1) if operand |arg| (0 or 1) is a zero constant. +FoldingRule RedundantBinaryOpWithZeroOperand(uint32_t arg, uint32_t foldToArg) { + return [arg, foldToArg]( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(constants.size() == 2); + + if (constants[arg] && constants[arg]->IsZero()) { + auto operand = inst->GetSingleWordInOperand(foldToArg); + auto operand_type = constants[arg]->type(); + + const analysis::Type* inst_type = + context->get_type_mgr()->GetType(inst->type_id()); + if (inst_type->IsSame(operand_type)) { + inst->SetOpcode(spv::Op::OpCopyObject); + } else { + inst->SetOpcode(spv::Op::OpBitcast); + } + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}}); + return true; + } + return false; + }; +} + +// This rule handles any of RedundantBinaryRhs0Ops with a 0 or vector 0 on the +// right-hand side (a | 0 => a). +static const constexpr spv::Op RedundantBinaryRhs0Ops[] = { + spv::Op::OpBitwiseOr, + spv::Op::OpBitwiseXor, + spv::Op::OpShiftRightLogical, + spv::Op::OpShiftRightArithmetic, + spv::Op::OpShiftLeftLogical, + spv::Op::OpIAdd, + spv::Op::OpISub}; +FoldingRule RedundantBinaryRhs0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryRhs0Ops), + std::end(RedundantBinaryRhs0Ops), + op) != std::end(RedundantBinaryRhs0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(1, 0); +} + +// This rule handles any of RedundantBinaryLhs0Ops with a 0 or vector 0 on the +// left-hand side (0 | a => a). +static const constexpr spv::Op RedundantBinaryLhs0Ops[] = { + spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpIAdd}; +FoldingRule RedundantBinaryLhs0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryLhs0Ops), + std::end(RedundantBinaryLhs0Ops), + op) != std::end(RedundantBinaryLhs0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(0, 1); +} + +// This rule handles shifts and divisions of 0 or vector 0 by any amount +// (0 >> a => 0). +static const constexpr spv::Op RedundantBinaryLhs0To0Ops[] = { + spv::Op::OpShiftRightLogical, + spv::Op::OpShiftRightArithmetic, + spv::Op::OpShiftLeftLogical, + spv::Op::OpSDiv, + spv::Op::OpUDiv, + spv::Op::OpSMod, + spv::Op::OpUMod}; +FoldingRule RedundantBinaryLhs0To0(spv::Op op) { + assert(std::find(std::begin(RedundantBinaryLhs0To0Ops), + std::end(RedundantBinaryLhs0To0Ops), + op) != std::end(RedundantBinaryLhs0To0Ops) && + "Wrong opcode."); + (void)op; + return RedundantBinaryOpWithZeroOperand(0, 0); +} + +// Returns true if all elements in |c| are 1. +bool IsAllInt1(const analysis::Constant* c) { + if (auto composite = c->AsCompositeConstant()) { + auto& components = composite->GetComponents(); + return std::all_of(std::begin(components), std::end(components), IsAllInt1); + } else if (c->AsIntConstant()) { + return c->GetSignExtendedValue() == 1; + } + + return false; +} + +// This rule handles divisions by 1 or vector 1 (a / 1 => a). +FoldingRule RedundantSUDiv() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(constants.size() == 2); + assert((inst->opcode() == spv::Op::OpUDiv || + inst->opcode() == spv::Op::OpSDiv) && + "Wrong opcode."); + + if (constants[1] && IsAllInt1(constants[1])) { + auto operand = inst->GetSingleWordInOperand(0); + auto operand_type = constants[1]->type(); + + const analysis::Type* inst_type = + context->get_type_mgr()->GetType(inst->type_id()); + if (inst_type->IsSame(operand_type)) { + inst->SetOpcode(spv::Op::OpCopyObject); + } else { + inst->SetOpcode(spv::Op::OpBitcast); + } + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}}); + return true; + } + return false; + }; +} + +// This rule handles modulo from division by 1 or vector 1 (a % 1 => 0). +FoldingRule RedundantSUMod() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(constants.size() == 2); + assert((inst->opcode() == spv::Op::OpUMod || + inst->opcode() == spv::Op::OpSMod) && + "Wrong opcode."); + + if (constants[1] && IsAllInt1(constants[1])) { + auto type = context->get_type_mgr()->GetType(inst->type_id()); + auto zero_id = context->get_constant_mgr()->GetNullConstId(type); + + inst->SetOpcode(spv::Op::OpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}}); + return true; + } + return false; + }; +} + +// This rule look for a dot with a constant vector containing a single 1 and +// the rest 0s. This is the same as doing an extract. +FoldingRule DotProductDoingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == spv::Op::OpDot && + "Wrong opcode. Should be OpDot."); + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + for (int i = 0; i < 2; ++i) { + if (!constants[i]) { + continue; + } + + const analysis::Vector* vector_type = constants[i]->type()->AsVector(); + assert(vector_type && "Inputs to OpDot must be vectors."); + const analysis::Float* element_type = + vector_type->element_type()->AsFloat(); + assert(element_type && "Inputs to OpDot must be vectors of floats."); + uint32_t element_width = element_type->width(); + if (element_width != 32 && element_width != 64) { + return false; + } + + std::vector components; + components = constants[i]->GetVectorComponents(const_mgr); + + constexpr uint32_t kNotFound = std::numeric_limits::max(); + + uint32_t component_with_one = kNotFound; + bool all_others_zero = true; + for (uint32_t j = 0; j < components.size(); ++j) { + const analysis::Constant* element = components[j]; + double value = + (element_width == 32 ? element->GetFloat() : element->GetDouble()); + if (value == 0.0) { + continue; + } else if (value == 1.0) { + if (component_with_one == kNotFound) { + component_with_one = j; + } else { + component_with_one = kNotFound; + break; + } + } else { + all_others_zero = false; + break; + } + } + + if (!all_others_zero || component_with_one == kNotFound) { + continue; + } + + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1u - i)}}); + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {component_with_one}}); + + inst->SetOpcode(spv::Op::OpCompositeExtract); + inst->SetInOperands(std::move(operands)); + return true; + } + return false; + }; +} + +// If we are storing an undef, then we can remove the store. +// +// TODO: We can do something similar for OpImageWrite, but checking for volatile +// is complicated. Waiting to see if it is needed. +FoldingRule StoringUndef() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpStore && + "Wrong opcode. Should be OpStore."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If this is a volatile store, the store cannot be removed. + if (inst->NumInOperands() == 3) { + if (inst->GetSingleWordInOperand(2) & + uint32_t(spv::MemoryAccessMask::Volatile)) { + return false; + } + } + + uint32_t object_id = inst->GetSingleWordInOperand(kStoreObjectInIdx); + Instruction* object_inst = def_use_mgr->GetDef(object_id); + if (object_inst->opcode() == spv::Op::OpUndef) { + inst->ToNop(); + return true; + } + return false; + }; +} + +FoldingRule VectorShuffleFeedingShuffle() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpVectorShuffle && + "Wrong opcode. Should be OpVectorShuffle."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + Instruction* feeding_shuffle_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + analysis::Vector* op0_type = + type_mgr->GetType(feeding_shuffle_inst->type_id())->AsVector(); + uint32_t op0_length = op0_type->element_count(); + + bool feeder_is_op0 = true; + if (feeding_shuffle_inst->opcode() != spv::Op::OpVectorShuffle) { + feeding_shuffle_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + feeder_is_op0 = false; + } + + if (feeding_shuffle_inst->opcode() != spv::Op::OpVectorShuffle) { + return false; + } + + Instruction* feeder2 = + def_use_mgr->GetDef(feeding_shuffle_inst->GetSingleWordInOperand(0)); + analysis::Vector* feeder_op0_type = + type_mgr->GetType(feeder2->type_id())->AsVector(); + uint32_t feeder_op0_length = feeder_op0_type->element_count(); + + uint32_t new_feeder_id = 0; + std::vector new_operands; + new_operands.resize( + 2, {SPV_OPERAND_TYPE_ID, {0}}); // Place holders for vector operands. + const uint32_t undef_literal = 0xffffffff; + for (uint32_t op = 2; op < inst->NumInOperands(); ++op) { + uint32_t component_index = inst->GetSingleWordInOperand(op); + + // Do not interpret the undefined value literal as coming from operand 1. + if (component_index != undef_literal && + feeder_is_op0 == (component_index < op0_length)) { + // This component comes from the feeding_shuffle_inst. Update + // |component_index| to be the index into the operand of the feeder. + + // Adjust component_index to get the index into the operands of the + // feeding_shuffle_inst. + if (component_index >= op0_length) { + component_index -= op0_length; + } + component_index = + feeding_shuffle_inst->GetSingleWordInOperand(component_index + 2); + + // Check if we are using a component from the first or second operand of + // the feeding instruction. + if (component_index < feeder_op0_length) { + if (new_feeder_id == 0) { + // First time through, save the id of the operand the element comes + // from. + new_feeder_id = feeding_shuffle_inst->GetSingleWordInOperand(0); + } else if (new_feeder_id != + feeding_shuffle_inst->GetSingleWordInOperand(0)) { + // We need both elements of the feeding_shuffle_inst, so we cannot + // fold. + return false; + } + } else if (component_index != undef_literal) { + if (new_feeder_id == 0) { + // First time through, save the id of the operand the element comes + // from. + new_feeder_id = feeding_shuffle_inst->GetSingleWordInOperand(1); + } else if (new_feeder_id != + feeding_shuffle_inst->GetSingleWordInOperand(1)) { + // We need both elements of the feeding_shuffle_inst, so we cannot + // fold. + return false; + } + component_index -= feeder_op0_length; + } + + if (!feeder_is_op0 && component_index != undef_literal) { + component_index += op0_length; + } + } + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {component_index}}); + } + + if (new_feeder_id == 0) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + type_mgr->GetType(feeding_shuffle_inst->type_id()); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + new_feeder_id = + const_mgr->GetDefiningInstruction(null_const, 0)->result_id(); + } + + if (feeder_is_op0) { + // If the size of the first vector operand changed then the indices + // referring to the second operand need to be adjusted. + Instruction* new_feeder_inst = def_use_mgr->GetDef(new_feeder_id); + analysis::Type* new_feeder_type = + type_mgr->GetType(new_feeder_inst->type_id()); + uint32_t new_op0_size = new_feeder_type->AsVector()->element_count(); + int32_t adjustment = op0_length - new_op0_size; + + if (adjustment != 0) { + for (uint32_t i = 2; i < new_operands.size(); i++) { + uint32_t operand = inst->GetSingleWordInOperand(i); + if (operand >= op0_length && operand != undef_literal) { + new_operands[i].words[0] -= adjustment; + } + } + } + + new_operands[0].words[0] = new_feeder_id; + new_operands[1] = inst->GetInOperand(1); + } else { + new_operands[1].words[0] = new_feeder_id; + new_operands[0] = inst->GetInOperand(0); + } + + inst->SetInOperands(std::move(new_operands)); + return true; + }; +} + +// Removes duplicate ids from the interface list of an OpEntryPoint +// instruction. +FoldingRule RemoveRedundantOperands() { + return [](IRContext*, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == spv::Op::OpEntryPoint && + "Wrong opcode. Should be OpEntryPoint."); + bool has_redundant_operand = false; + std::unordered_set seen_operands; + std::vector new_operands; + + new_operands.emplace_back(inst->GetOperand(0)); + new_operands.emplace_back(inst->GetOperand(1)); + new_operands.emplace_back(inst->GetOperand(2)); + for (uint32_t i = 3; i < inst->NumOperands(); ++i) { + if (seen_operands.insert(inst->GetSingleWordOperand(i)).second) { + new_operands.emplace_back(inst->GetOperand(i)); + } else { + has_redundant_operand = true; + } + } + + if (!has_redundant_operand) { + return false; + } + + inst->SetInOperands(std::move(new_operands)); + return true; + }; +} + +// If an image instruction's operand is a constant, updates the image operand +// flag from Offset to ConstOffset. +FoldingRule UpdateImageOperands() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + const auto opcode = inst->opcode(); + (void)opcode; + assert((opcode == spv::Op::OpImageSampleImplicitLod || + opcode == spv::Op::OpImageSampleExplicitLod || + opcode == spv::Op::OpImageSampleDrefImplicitLod || + opcode == spv::Op::OpImageSampleDrefExplicitLod || + opcode == spv::Op::OpImageSampleProjImplicitLod || + opcode == spv::Op::OpImageSampleProjExplicitLod || + opcode == spv::Op::OpImageSampleProjDrefImplicitLod || + opcode == spv::Op::OpImageSampleProjDrefExplicitLod || + opcode == spv::Op::OpImageFetch || + opcode == spv::Op::OpImageGather || + opcode == spv::Op::OpImageDrefGather || + opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite || + opcode == spv::Op::OpImageSparseSampleImplicitLod || + opcode == spv::Op::OpImageSparseSampleExplicitLod || + opcode == spv::Op::OpImageSparseSampleDrefImplicitLod || + opcode == spv::Op::OpImageSparseSampleDrefExplicitLod || + opcode == spv::Op::OpImageSparseSampleProjImplicitLod || + opcode == spv::Op::OpImageSparseSampleProjExplicitLod || + opcode == spv::Op::OpImageSparseSampleProjDrefImplicitLod || + opcode == spv::Op::OpImageSparseSampleProjDrefExplicitLod || + opcode == spv::Op::OpImageSparseFetch || + opcode == spv::Op::OpImageSparseGather || + opcode == spv::Op::OpImageSparseDrefGather || + opcode == spv::Op::OpImageSparseRead) && + "Wrong opcode. Should be an image instruction."); + + int32_t operand_index = ImageOperandsMaskInOperandIndex(inst); + if (operand_index >= 0) { + auto image_operands = inst->GetSingleWordInOperand(operand_index); + if (image_operands & uint32_t(spv::ImageOperandsMask::Offset)) { + uint32_t offset_operand_index = operand_index + 1; + if (image_operands & uint32_t(spv::ImageOperandsMask::Bias)) + offset_operand_index++; + if (image_operands & uint32_t(spv::ImageOperandsMask::Lod)) + offset_operand_index++; + if (image_operands & uint32_t(spv::ImageOperandsMask::Grad)) + offset_operand_index += 2; + assert(((image_operands & + uint32_t(spv::ImageOperandsMask::ConstOffset)) == 0) && + "Offset and ConstOffset may not be used together"); + if (offset_operand_index < inst->NumOperands()) { + if (constants[offset_operand_index]) { + if (constants[offset_operand_index]->IsZero()) { + inst->RemoveInOperand(offset_operand_index); + } else { + image_operands = image_operands | + uint32_t(spv::ImageOperandsMask::ConstOffset); + } + image_operands = + image_operands & ~uint32_t(spv::ImageOperandsMask::Offset); + inst->SetInOperand(operand_index, {image_operands}); + return true; + } + } + } + } + + return false; + }; +} + +} // namespace + +void FoldingRules::AddFoldingRules() { + // Add all folding rules to the list for the opcodes to which they apply. + // Note that the order in which rules are added to the list matters. If a rule + // applies to the instruction, the rest of the rules will not be attempted. + // Take that into consideration. + for (auto op : RedundantBinaryRhs0Ops) + rules_[op].push_back(RedundantBinaryRhs0(op)); + for (auto op : RedundantBinaryLhs0Ops) + rules_[op].push_back(RedundantBinaryLhs0(op)); + for (auto op : RedundantBinaryLhs0To0Ops) + rules_[op].push_back(RedundantBinaryLhs0To0(op)); + rules_[spv::Op::OpSDiv].push_back(RedundantSUDiv()); + rules_[spv::Op::OpUDiv].push_back(RedundantSUDiv()); + rules_[spv::Op::OpSMod].push_back(RedundantSUMod()); + rules_[spv::Op::OpUMod].push_back(RedundantSUMod()); + + rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector()); + + rules_[spv::Op::OpCompositeConstruct].push_back( + CompositeExtractFeedingConstruct); + + rules_[spv::Op::OpCompositeExtract].push_back(InsertFeedingExtract()); + rules_[spv::Op::OpCompositeExtract].push_back( + CompositeConstructFeedingExtract); + rules_[spv::Op::OpCompositeExtract].push_back(VectorShuffleFeedingExtract()); + rules_[spv::Op::OpCompositeExtract].push_back(FMixFeedingExtract()); + + rules_[spv::Op::OpCompositeInsert].push_back( + CompositeInsertToCompositeConstruct); + + rules_[spv::Op::OpDot].push_back(DotProductDoingExtract()); + + rules_[spv::Op::OpEntryPoint].push_back(RemoveRedundantOperands()); + + rules_[spv::Op::OpFAdd].push_back(RedundantFAdd()); + rules_[spv::Op::OpFAdd].push_back(MergeAddNegateArithmetic()); + rules_[spv::Op::OpFAdd].push_back(MergeAddAddArithmetic()); + rules_[spv::Op::OpFAdd].push_back(MergeAddSubArithmetic()); + rules_[spv::Op::OpFAdd].push_back(MergeGenericAddSubArithmetic()); + rules_[spv::Op::OpFAdd].push_back(FactorAddMuls()); + + rules_[spv::Op::OpFDiv].push_back(RedundantFDiv()); + rules_[spv::Op::OpFDiv].push_back(ReciprocalFDiv()); + rules_[spv::Op::OpFDiv].push_back(MergeDivDivArithmetic()); + rules_[spv::Op::OpFDiv].push_back(MergeDivMulArithmetic()); + rules_[spv::Op::OpFDiv].push_back(MergeDivNegateArithmetic()); + + rules_[spv::Op::OpFMod].push_back(RedundantFMod()); + + rules_[spv::Op::OpFMul].push_back(RedundantFMul()); + rules_[spv::Op::OpFMul].push_back(MergeMulMulArithmetic()); + rules_[spv::Op::OpFMul].push_back(MergeMulDivArithmetic()); + rules_[spv::Op::OpFMul].push_back(MergeMulNegateArithmetic()); + + rules_[spv::Op::OpFNegate].push_back(MergeNegateArithmetic()); + rules_[spv::Op::OpFNegate].push_back(MergeNegateAddSubArithmetic()); + rules_[spv::Op::OpFNegate].push_back(MergeNegateMulDivArithmetic()); + + rules_[spv::Op::OpFSub].push_back(RedundantFSub()); + rules_[spv::Op::OpFSub].push_back(MergeSubNegateArithmetic()); + rules_[spv::Op::OpFSub].push_back(MergeSubAddArithmetic()); + rules_[spv::Op::OpFSub].push_back(MergeSubSubArithmetic()); + + rules_[spv::Op::OpIAdd].push_back(MergeAddNegateArithmetic()); + rules_[spv::Op::OpIAdd].push_back(MergeAddAddArithmetic()); + rules_[spv::Op::OpIAdd].push_back(MergeAddSubArithmetic()); + rules_[spv::Op::OpIAdd].push_back(MergeGenericAddSubArithmetic()); + rules_[spv::Op::OpIAdd].push_back(FactorAddMuls()); + + rules_[spv::Op::OpIMul].push_back(IntMultipleBy1()); + rules_[spv::Op::OpIMul].push_back(MergeMulMulArithmetic()); + rules_[spv::Op::OpIMul].push_back(MergeMulNegateArithmetic()); + + rules_[spv::Op::OpISub].push_back(MergeSubNegateArithmetic()); + rules_[spv::Op::OpISub].push_back(MergeSubAddArithmetic()); + rules_[spv::Op::OpISub].push_back(MergeSubSubArithmetic()); + + rules_[spv::Op::OpPhi].push_back(RedundantPhi()); + + rules_[spv::Op::OpSNegate].push_back(MergeNegateArithmetic()); + rules_[spv::Op::OpSNegate].push_back(MergeNegateMulDivArithmetic()); + rules_[spv::Op::OpSNegate].push_back(MergeNegateAddSubArithmetic()); + + rules_[spv::Op::OpSelect].push_back(RedundantSelect()); + + rules_[spv::Op::OpStore].push_back(StoringUndef()); + + rules_[spv::Op::OpVectorShuffle].push_back(VectorShuffleFeedingShuffle()); + + rules_[spv::Op::OpImageSampleImplicitLod].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSampleExplicitLod].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSampleDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSampleDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSampleProjImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSampleProjExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSampleProjDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSampleProjDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageFetch].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageGather].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageDrefGather].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageRead].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageWrite].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleProjImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleProjExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleProjDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseSampleProjDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[spv::Op::OpImageSparseFetch].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSparseGather].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSparseDrefGather].push_back(UpdateImageOperands()); + rules_[spv::Op::OpImageSparseRead].push_back(UpdateImageOperands()); + + FeatureManager* feature_manager = context_->get_feature_mgr(); + // Add rules for GLSLstd450 + uint32_t ext_inst_glslstd450_id = + feature_manager->GetExtInstImportId_GLSLstd450(); + if (ext_inst_glslstd450_id != 0) { + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMix}].push_back( + RedundantFMix()); + } +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.h b/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.h new file mode 100644 index 00000000..b51e0ce4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/folding_rules.h @@ -0,0 +1,124 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLDING_RULES_H_ +#define SOURCE_OPT_FOLDING_RULES_H_ + +#include +#include +#include + +#include "source/opt/constants.h" + +namespace spvtools { +namespace opt { + +// Folding Rules: +// +// The folding mechanism is built around the concept of a |FoldingRule|. A +// folding rule is a function that implements a method of simplifying an +// instruction. +// +// The inputs to a folding rule are: +// |inst| - the instruction to be simplified. +// |constants| - if an in-operands is an id of a constant, then the +// corresponding value in |constants| contains that +// constant value. Otherwise, the corresponding entry in +// |constants| is |nullptr|. +// +// A folding rule returns true if |inst| can be simplified using this rule. If +// the instruction can be simplified, then |inst| is changed to the simplified +// instruction. Otherwise, |inst| remains the same. +// +// See folding_rules.cpp for examples on how to write a folding rule. It is +// important to note that if |inst| can be folded to the result of an +// instruction that feed it, then |inst| should be changed to an OpCopyObject +// that copies that id. +// +// Be sure to add new folding rules to the table of folding rules in the +// constructor for FoldingRules. The new rule should be added to the list for +// every opcode that it applies to. Note that earlier rules in the list are +// given priority. That is, if an earlier rule is able to fold an instruction, +// the later rules will not be attempted. + +using FoldingRule = std::function& constants)>; + +class FoldingRules { + public: + using FoldingRuleSet = std::vector; + + explicit FoldingRules(IRContext* ctx) : context_(ctx) {} + virtual ~FoldingRules() = default; + + const FoldingRuleSet& GetRulesForInstruction(Instruction* inst) const { + if (inst->opcode() != spv::Op::OpExtInst) { + auto it = rules_.find(inst->opcode()); + if (it != rules_.end()) { + return it->second; + } + } else { + uint32_t ext_inst_id = inst->GetSingleWordInOperand(0); + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + auto it = ext_rules_.find({ext_inst_id, ext_opcode}); + if (it != ext_rules_.end()) { + return it->second; + } + } + return empty_vector_; + } + + IRContext* context() { return context_; } + + // Adds the folding rules for the object. + virtual void AddFoldingRules(); + + protected: + struct hasher { + size_t operator()(const spv::Op& op) const noexcept { + return std::hash()(uint32_t(op)); + } + }; + + // The folding rules for core instructions. + std::unordered_map rules_; + + // The folding rules for extended instructions. + struct Key { + uint32_t instruction_set; + uint32_t opcode; + }; + + friend bool operator<(const Key& a, const Key& b) { + if (a.instruction_set < b.instruction_set) { + return true; + } + if (a.instruction_set > b.instruction_set) { + return false; + } + return a.opcode < b.opcode; + } + + std::map ext_rules_; + + private: + IRContext* context_; + FoldingRuleSet empty_vector_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLDING_RULES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp new file mode 100644 index 00000000..3f89e56c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/freeze_spec_constant_value_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status FreezeSpecConstantValuePass::Process() { + bool modified = false; + auto ctx = context(); + ctx->module()->ForEachInst([&modified, ctx](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpSpecConstant: + inst->SetOpcode(spv::Op::OpConstant); + modified = true; + break; + case spv::Op::OpSpecConstantTrue: + inst->SetOpcode(spv::Op::OpConstantTrue); + modified = true; + break; + case spv::Op::OpSpecConstantFalse: + inst->SetOpcode(spv::Op::OpConstantFalse); + modified = true; + break; + case spv::Op::OpDecorate: + if (spv::Decoration(inst->GetSingleWordInOperand(1)) == + spv::Decoration::SpecId) { + ctx->KillInst(inst); + modified = true; + } + break; + default: + break; + } + }); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.h new file mode 100644 index 00000000..0663adf4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/freeze_spec_constant_value_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ +#define SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FreezeSpecConstantValuePass : public Pass { + public: + const char* name() const override { return "freeze-spec-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/function.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/function.cpp new file mode 100644 index 00000000..2ee88eca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/function.cpp @@ -0,0 +1,280 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/function.h" + +#include + +#include "ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +Function* Function::Clone(IRContext* ctx) const { + Function* clone = + new Function(std::unique_ptr(DefInst().Clone(ctx))); + clone->params_.reserve(params_.size()); + ForEachParam( + [clone, ctx](const Instruction* inst) { + clone->AddParameter(std::unique_ptr(inst->Clone(ctx))); + }, + true); + + for (const auto& i : debug_insts_in_header_) { + clone->AddDebugInstructionInHeader( + std::unique_ptr(i.Clone(ctx))); + } + + clone->blocks_.reserve(blocks_.size()); + for (const auto& b : blocks_) { + std::unique_ptr bb(b->Clone(ctx)); + clone->AddBasicBlock(std::move(bb)); + } + + clone->SetFunctionEnd(std::unique_ptr(EndInst()->Clone(ctx))); + + clone->non_semantic_.reserve(non_semantic_.size()); + for (auto& non_semantic : non_semantic_) { + clone->AddNonSemanticInstruction( + std::unique_ptr(non_semantic->Clone(ctx))); + } + return clone; +} + +void Function::ForEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts, run_on_non_semantic_insts); +} + +void Function::ForEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts, run_on_non_semantic_insts); +} + +bool Function::WhileEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) { + if (def_inst_) { + if (!def_inst_->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (auto& param : params_) { + if (!param->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (!debug_insts_in_header_.empty()) { + Instruction* di = &debug_insts_in_header_.front(); + while (di != nullptr) { + Instruction* next_instruction = di->NextNode(); + if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false; + di = next_instruction; + } + } + + for (auto& bb : blocks_) { + if (!bb->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (end_inst_) { + if (!end_inst_->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (run_on_non_semantic_insts) { + for (auto& non_semantic : non_semantic_) { + if (!non_semantic->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + } + + return true; +} + +bool Function::WhileEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) const { + if (def_inst_) { + if (!static_cast(def_inst_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (const auto& param : params_) { + if (!static_cast(param.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (const auto& di : debug_insts_in_header_) { + if (!static_cast(&di)->WhileEachInst( + f, run_on_debug_line_insts)) + return false; + } + + for (const auto& bb : blocks_) { + if (!static_cast(bb.get())->WhileEachInst( + f, run_on_debug_line_insts)) { + return false; + } + } + + if (end_inst_) { + if (!static_cast(end_inst_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (run_on_non_semantic_insts) { + for (auto& non_semantic : non_semantic_) { + if (!static_cast(non_semantic.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + } + + return true; +} + +void Function::ForEachParam(const std::function& f, + bool run_on_debug_line_insts) { + for (auto& param : params_) + static_cast(param.get()) + ->ForEachInst(f, run_on_debug_line_insts); +} + +void Function::ForEachParam(const std::function& f, + bool run_on_debug_line_insts) const { + for (const auto& param : params_) + static_cast(param.get()) + ->ForEachInst(f, run_on_debug_line_insts); +} + +void Function::ForEachDebugInstructionsInHeader( + const std::function& f) { + if (debug_insts_in_header_.empty()) return; + + Instruction* di = &debug_insts_in_header_.front(); + while (di != nullptr) { + Instruction* next_instruction = di->NextNode(); + di->ForEachInst(f); + di = next_instruction; + } +} + +BasicBlock* Function::InsertBasicBlockAfter( + std::unique_ptr&& new_block, BasicBlock* position) { + for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { + if (&*bb_iter == position) { + new_block->SetParent(this); + ++bb_iter; + bb_iter = bb_iter.InsertBefore(std::move(new_block)); + return &*bb_iter; + } + } + assert(false && "Could not find insertion point."); + return nullptr; +} + +BasicBlock* Function::InsertBasicBlockBefore( + std::unique_ptr&& new_block, BasicBlock* position) { + for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { + if (&*bb_iter == position) { + new_block->SetParent(this); + bb_iter = bb_iter.InsertBefore(std::move(new_block)); + return &*bb_iter; + } + } + assert(false && "Could not find insertion point."); + return nullptr; +} + +bool Function::HasEarlyReturn() const { + auto post_dominator_analysis = + blocks_.front()->GetLabel()->context()->GetPostDominatorAnalysis(this); + for (auto& block : blocks_) { + if (spvOpcodeIsReturn(block->tail()->opcode()) && + !post_dominator_analysis->Dominates(block.get(), entry().get())) { + return true; + } + } + return false; +} + +bool Function::IsRecursive() const { + IRContext* ctx = blocks_.front()->GetLabel()->context(); + IRContext::ProcessFunction mark_visited = [this](Function* fp) { + return fp == this; + }; + + // Process the call tree from all of the function called by |this|. If it get + // back to |this|, then we have a recursive function. + std::queue roots; + ctx->AddCalls(this, &roots); + return ctx->ProcessCallTreeFromRoots(mark_visited, &roots); +} + +std::ostream& operator<<(std::ostream& str, const Function& func) { + str << func.PrettyPrint(); + return str; +} + +void Function::Dump() const { + std::cerr << "Function #" << result_id() << "\n" << *this << "\n"; +} + +std::string Function::PrettyPrint(uint32_t options) const { + std::ostringstream str; + ForEachInst([&str, options](const Instruction* inst) { + str << inst->PrettyPrint(options); + if (inst->opcode() != spv::Op::OpFunctionEnd) { + str << std::endl; + } + }); + return str.str(); +} + +void Function::ReorderBasicBlocksInStructuredOrder() { + std::list order; + IRContext* context = this->def_inst_->context(); + context->cfg()->ComputeStructuredOrder(this, blocks_[0].get(), &order); + ReorderBasicBlocks(order.begin(), order.end()); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/function.h b/engine/engine/3rdlibs/spirv-tools/source/opt/function.h new file mode 100644 index 00000000..45a0b23b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/function.h @@ -0,0 +1,328 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FUNCTION_H_ +#define SOURCE_OPT_FUNCTION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class CFG; +class IRContext; +class Module; + +// A SPIR-V function. +class Function { + public: + using iterator = UptrVectorIterator; + using const_iterator = UptrVectorIterator; + using ParamList = std::vector>; + + // Creates a function instance declared by the given OpFunction instruction + // |def_inst|. + inline explicit Function(std::unique_ptr def_inst); + + explicit Function(const Function& f) = delete; + + // Creates a clone of the instruction in the given |context| + // + // The parent module will default to null and needs to be explicitly set by + // the user. + Function* Clone(IRContext*) const; + // The OpFunction instruction that begins the definition of this function. + Instruction& DefInst() { return *def_inst_; } + const Instruction& DefInst() const { return *def_inst_; } + + // Appends a parameter to this function. + inline void AddParameter(std::unique_ptr p); + // Appends a debug instruction in function header to this function. + inline void AddDebugInstructionInHeader(std::unique_ptr p); + // Appends a basic block to this function. + inline void AddBasicBlock(std::unique_ptr b); + // Appends a basic block to this function at the position |ip|. + inline void AddBasicBlock(std::unique_ptr b, iterator ip); + template + inline void AddBasicBlocks(T begin, T end, iterator ip); + + // Move basic block with |id| to the position after |ip|. Both have to be + // contained in this function. + inline void MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip); + + // Delete all basic blocks that contain no instructions. + inline void RemoveEmptyBlocks(); + + // Removes a parameter from the function with result id equal to |id|. + // Does nothing if the function doesn't have such a parameter. + inline void RemoveParameter(uint32_t id); + + // Rewrites the function parameters by calling a replacer callback. + // The replacer takes two parameters: an expiring unique pointer to a current + // instruction, and a back-inserter into a new list of unique pointers to + // instructions. The replacer is called for each current parameter, in order. + // Not valid to call while also iterating through the parameter list, e.g. + // within the ForEachParam method. + using RewriteParamFn = std::function&&, std::back_insert_iterator&)>; + void RewriteParams(RewriteParamFn& replacer) { + ParamList new_params; + auto appender = std::back_inserter(new_params); + for (auto& param : params_) { + replacer(std::move(param), appender); + } + params_ = std::move(new_params); + } + + // Saves the given function end instruction. + inline void SetFunctionEnd(std::unique_ptr end_inst); + + // Add a non-semantic instruction that succeeds this function in the module. + // These instructions are maintained in the order they are added. + inline void AddNonSemanticInstruction( + std::unique_ptr non_semantic); + + // Returns the given function end instruction. + inline Instruction* EndInst() { return end_inst_.get(); } + inline const Instruction* EndInst() const { return end_inst_.get(); } + + // Returns function's id + inline uint32_t result_id() const { return def_inst_->result_id(); } + + // Returns function's return type id + inline uint32_t type_id() const { return def_inst_->type_id(); } + + // Returns the function's control mask + inline uint32_t control_mask() const { return def_inst_->GetSingleWordInOperand(0); } + + // Returns the entry basic block for this function. + const std::unique_ptr& entry() const { return blocks_.front(); } + + // Returns the last basic block in this function. + BasicBlock* tail() { return blocks_.back().get(); } + const BasicBlock* tail() const { return blocks_.back().get(); } + + iterator begin() { return iterator(&blocks_, blocks_.begin()); } + iterator end() { return iterator(&blocks_, blocks_.end()); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { + return const_iterator(&blocks_, blocks_.cbegin()); + } + const_iterator cend() const { + return const_iterator(&blocks_, blocks_.cend()); + } + + // Returns an iterator to the basic block |id|. + iterator FindBlock(uint32_t bb_id) { + return std::find_if(begin(), end(), [bb_id](const BasicBlock& it_bb) { + return bb_id == it_bb.id(); + }); + } + + // Runs the given function |f| on instructions in this function, in order, + // and optionally on debug line instructions that might precede them and + // non-semantic instructions that succceed the function. + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false); + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false) const; + // Runs the given function |f| on instructions in this function, in order, + // and optionally on debug line instructions that might precede them and + // non-semantic instructions that succeed the function. If |f| returns + // false, iteration is terminated and this function returns false. + bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false); + bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false) const; + + // Runs the given function |f| on each parameter instruction in this function, + // in order, and optionally on debug line instructions that might precede + // them. + void ForEachParam(const std::function& f, + bool run_on_debug_line_insts = false) const; + void ForEachParam(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each debug instruction in this function's + // header in order. + void ForEachDebugInstructionsInHeader( + const std::function& f); + + BasicBlock* InsertBasicBlockAfter(std::unique_ptr&& new_block, + BasicBlock* position); + + BasicBlock* InsertBasicBlockBefore(std::unique_ptr&& new_block, + BasicBlock* position); + + // Returns true if the function has a return block other than the exit block. + bool HasEarlyReturn() const; + + // Returns true if the function calls itself either directly or indirectly. + bool IsRecursive() const; + + // Pretty-prints all the basic blocks in this function into a std::string. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Dump this function on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + // Returns true is a function declaration and not a function definition. + bool IsDeclaration() { return begin() == end(); } + + // Reorders the basic blocks in the function to match the structured order. + void ReorderBasicBlocksInStructuredOrder(); + + private: + // Reorders the basic blocks in the function to match the order given by the + // range |{begin,end}|. The range must contain every basic block in the + // function, and no extras. + template + void ReorderBasicBlocks(It begin, It end); + + template + bool ContainsAllBlocksInTheFunction(It begin, It end); + + // The OpFunction instruction that begins the definition of this function. + std::unique_ptr def_inst_; + // All parameters to this function. + ParamList params_; + // All debug instructions in this function's header. + InstructionList debug_insts_in_header_; + // All basic blocks inside this function in specification order + std::vector> blocks_; + // The OpFunctionEnd instruction. + std::unique_ptr end_inst_; + // Non-semantic instructions succeeded by this function. + std::vector> non_semantic_; +}; + +// Pretty-prints |func| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const Function& func); + +inline Function::Function(std::unique_ptr def_inst) + : def_inst_(std::move(def_inst)), end_inst_() {} + +inline void Function::AddParameter(std::unique_ptr p) { + params_.emplace_back(std::move(p)); +} + +inline void Function::AddDebugInstructionInHeader( + std::unique_ptr p) { + debug_insts_in_header_.push_back(std::move(p)); +} + +inline void Function::AddBasicBlock(std::unique_ptr b) { + AddBasicBlock(std::move(b), end()); +} + +inline void Function::AddBasicBlock(std::unique_ptr b, + iterator ip) { + b->SetParent(this); + ip.InsertBefore(std::move(b)); +} + +template +inline void Function::AddBasicBlocks(T src_begin, T src_end, iterator ip) { + blocks_.insert(ip.Get(), std::make_move_iterator(src_begin), + std::make_move_iterator(src_end)); +} + +inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) { + std::unique_ptr block_to_move = std::move(*FindBlock(id).Get()); + blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr)); + + assert(block_to_move->GetParent() == ip->GetParent() && + "Both blocks have to be in the same function."); + + InsertBasicBlockAfter(std::move(block_to_move), ip); +} + +inline void Function::RemoveEmptyBlocks() { + auto first_empty = + std::remove_if(std::begin(blocks_), std::end(blocks_), + [](const std::unique_ptr& bb) -> bool { + return bb->GetLabelInst()->opcode() == spv::Op::OpNop; + }); + blocks_.erase(first_empty, std::end(blocks_)); +} + +inline void Function::RemoveParameter(uint32_t id) { + params_.erase(std::remove_if(params_.begin(), params_.end(), + [id](const std::unique_ptr& param) { + return param->result_id() == id; + }), + params_.end()); +} + +inline void Function::SetFunctionEnd(std::unique_ptr end_inst) { + end_inst_ = std::move(end_inst); +} + +inline void Function::AddNonSemanticInstruction( + std::unique_ptr non_semantic) { + non_semantic_.emplace_back(std::move(non_semantic)); +} + +template +void Function::ReorderBasicBlocks(It begin, It end) { + // Asserts to make sure every node in the function is in new_order. + assert(ContainsAllBlocksInTheFunction(begin, end)); + + // We have a pointer to all the elements in order, so we can release all + // pointers in |block_|, and then create the new unique pointers from |{begin, + // end}|. + std::for_each(blocks_.begin(), blocks_.end(), + [](std::unique_ptr& bb) { bb.release(); }); + std::transform(begin, end, blocks_.begin(), [](BasicBlock* bb) { + return std::unique_ptr(bb); + }); +} + +template +bool Function::ContainsAllBlocksInTheFunction(It begin, It end) { + std::unordered_multiset range(begin, end); + if (range.size() != blocks_.size()) { + return false; + } + + for (auto& bb : blocks_) { + if (range.count(bb.get()) == 0) return false; + } + return true; +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.cpp new file mode 100644 index 00000000..e765c397 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.cpp @@ -0,0 +1,1054 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This pass injects code in a graphics shader to implement guarantees +// satisfying Vulkan's robustBufferAccess rules. Robust access rules permit +// an out-of-bounds access to be redirected to an access of the same type +// (load, store, etc.) but within the same root object. +// +// We assume baseline functionality in Vulkan, i.e. the module uses +// logical addressing mode, without VK_KHR_variable_pointers. +// +// - Logical addressing mode implies: +// - Each root pointer (a pointer that exists other than by the +// execution of a shader instruction) is the result of an OpVariable. +// +// - Instructions that result in pointers are: +// OpVariable +// OpAccessChain +// OpInBoundsAccessChain +// OpFunctionParameter +// OpImageTexelPointer +// OpCopyObject +// +// - Instructions that use a pointer are: +// OpLoad +// OpStore +// OpAccessChain +// OpInBoundsAccessChain +// OpFunctionCall +// OpImageTexelPointer +// OpCopyMemory +// OpCopyObject +// all OpAtomic* instructions +// +// We classify pointer-users into: +// - Accesses: +// - OpLoad +// - OpStore +// - OpAtomic* +// - OpCopyMemory +// +// - Address calculations: +// - OpAccessChain +// - OpInBoundsAccessChain +// +// - Pass-through: +// - OpFunctionCall +// - OpFunctionParameter +// - OpCopyObject +// +// The strategy is: +// +// - Handle only logical addressing mode. In particular, don't handle a module +// if it uses one of the variable-pointers capabilities. +// +// - Don't handle modules using capability RuntimeDescriptorArrayEXT. So the +// only runtime arrays are those that are the last member in a +// Block-decorated struct. This allows us to feasibly/easily compute the +// length of the runtime array. See below. +// +// - The memory locations accessed by OpLoad, OpStore, OpCopyMemory, and +// OpAtomic* are determined by their pointer parameter or parameters. +// Pointers are always (correctly) typed and so the address and number of +// consecutive locations are fully determined by the pointer. +// +// - A pointer value originates as one of few cases: +// +// - OpVariable for an interface object or an array of them: image, +// buffer (UBO or SSBO), sampler, sampled-image, push-constant, input +// variable, output variable. The execution environment is responsible for +// allocating the correct amount of storage for these, and for ensuring +// each resource bound to such a variable is big enough to contain the +// SPIR-V pointee type of the variable. +// +// - OpVariable for a non-interface object. These are variables in +// Workgroup, Private, and Function storage classes. The compiler ensures +// the underlying allocation is big enough to store the entire SPIR-V +// pointee type of the variable. +// +// - An OpFunctionParameter. This always maps to a pointer parameter to an +// OpFunctionCall. +// +// - In logical addressing mode, these are severely limited: +// "Any pointer operand to an OpFunctionCall must be: +// - a memory object declaration, or +// - a pointer to an element in an array that is a memory object +// declaration, where the element type is OpTypeSampler or OpTypeImage" +// +// - This has an important simplifying consequence: +// +// - When looking for a pointer to the structure containing a runtime +// array, you begin with a pointer to the runtime array and trace +// backward in the function. You never have to trace back beyond +// your function call boundary. So you can't take a partial access +// chain into an SSBO, then pass that pointer into a function. So +// we don't resort to using fat pointers to compute array length. +// We can trace back to a pointer to the containing structure, +// and use that in an OpArrayLength instruction. (The structure type +// gives us the member index of the runtime array.) +// +// - Otherwise, the pointer type fully encodes the range of valid +// addresses. In particular, the type of a pointer to an aggregate +// value fully encodes the range of indices when indexing into +// that aggregate. +// +// - The pointer is the result of an access chain instruction. We clamp +// indices contributing to address calculations. As noted above, the +// valid ranges are either bound by the length of a runtime array, or +// by the type of the base pointer. The length of a runtime array is +// the result of an OpArrayLength instruction acting on the pointer of +// the containing structure as noted above. +// +// - Access chain indices are always treated as signed, so: +// - Clamp the upper bound at the signed integer maximum. +// - Use SClamp for all clamping. +// +// - TODO(dneto): OpImageTexelPointer: +// - Clamp coordinate to the image size returned by OpImageQuerySize +// - If multi-sampled, clamp the sample index to the count returned by +// OpImageQuerySamples. +// - If not multi-sampled, set the sample index to 0. +// +// - Rely on the external validator to check that pointers are only +// used by the instructions as above. +// +// - Handles OpTypeRuntimeArray +// Track pointer back to original resource (pointer to struct), so we can +// query the runtime array size. +// + +#include "graphics_robust_access_pass.h" + +#include +#include +#include + +#include "function.h" +#include "ir_context.h" +#include "pass.h" +#include "source/diagnostic.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.h" +#include "spirv/unified1/GLSL.std.450.h" +#include "type_manager.h" +#include "types.h" + +namespace spvtools { +namespace opt { + +using opt::Instruction; +using opt::Operand; +using spvtools::MakeUnique; + +GraphicsRobustAccessPass::GraphicsRobustAccessPass() : module_status_() {} + +Pass::Status GraphicsRobustAccessPass::Process() { + module_status_ = PerModuleState(); + + ProcessCurrentModule(); + + auto result = module_status_.failed + ? Status::Failure + : (module_status_.modified ? Status::SuccessWithChange + : Status::SuccessWithoutChange); + + return result; +} + +spvtools::DiagnosticStream GraphicsRobustAccessPass::Fail() { + module_status_.failed = true; + // We don't really have a position, and we'll ignore the result. + return std::move( + spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY) + << name() << ": "); +} + +spv_result_t GraphicsRobustAccessPass::IsCompatibleModule() { + auto* feature_mgr = context()->get_feature_mgr(); + if (!feature_mgr->HasCapability(spv::Capability::Shader)) + return Fail() << "Can only process Shader modules"; + if (feature_mgr->HasCapability(spv::Capability::VariablePointers)) + return Fail() << "Can't process modules with VariablePointers capability"; + if (feature_mgr->HasCapability( + spv::Capability::VariablePointersStorageBuffer)) + return Fail() << "Can't process modules with VariablePointersStorageBuffer " + "capability"; + if (feature_mgr->HasCapability(spv::Capability::RuntimeDescriptorArrayEXT)) { + // These have a RuntimeArray outside of Block-decorated struct. There + // is no way to compute the array length from within SPIR-V. + return Fail() << "Can't process modules with RuntimeDescriptorArrayEXT " + "capability"; + } + + { + auto* inst = context()->module()->GetMemoryModel(); + const auto addressing_model = + spv::AddressingModel(inst->GetSingleWordOperand(0)); + if (addressing_model != spv::AddressingModel::Logical) + return Fail() << "Addressing model must be Logical. Found " + << inst->PrettyPrint(); + } + return SPV_SUCCESS; +} + +spv_result_t GraphicsRobustAccessPass::ProcessCurrentModule() { + auto err = IsCompatibleModule(); + if (err != SPV_SUCCESS) return err; + + ProcessFunction fn = [this](opt::Function* f) { return ProcessAFunction(f); }; + module_status_.modified |= context()->ProcessReachableCallTree(fn); + + // Need something here. It's the price we pay for easier failure paths. + return SPV_SUCCESS; +} + +bool GraphicsRobustAccessPass::ProcessAFunction(opt::Function* function) { + // Ensure that all pointers computed inside a function are within bounds. + // Find the access chains in this block before trying to modify them. + std::vector access_chains; + std::vector image_texel_pointers; + for (auto& block : *function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + access_chains.push_back(&inst); + break; + case spv::Op::OpImageTexelPointer: + image_texel_pointers.push_back(&inst); + break; + default: + break; + } + } + } + for (auto* inst : access_chains) { + ClampIndicesForAccessChain(inst); + if (module_status_.failed) return module_status_.modified; + } + + for (auto* inst : image_texel_pointers) { + if (SPV_SUCCESS != ClampCoordinateForImageTexelPointer(inst)) break; + } + return module_status_.modified; +} + +void GraphicsRobustAccessPass::ClampIndicesForAccessChain( + Instruction* access_chain) { + Instruction& inst = *access_chain; + + auto* constant_mgr = context()->get_constant_mgr(); + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* type_mgr = context()->get_type_mgr(); + const bool have_int64_cap = + context()->get_feature_mgr()->HasCapability(spv::Capability::Int64); + + // Replaces one of the OpAccessChain index operands with a new value. + // Updates def-use analysis. + auto replace_index = [this, &inst, def_use_mgr](uint32_t operand_index, + Instruction* new_value) { + inst.SetOperand(operand_index, {new_value->result_id()}); + def_use_mgr->AnalyzeInstUse(&inst); + module_status_.modified = true; + return SPV_SUCCESS; + }; + + // Replaces one of the OpAccesssChain index operands with a clamped value. + // Replace the operand at |operand_index| with the value computed from + // signed_clamp(%old_value, %min_value, %max_value). It also analyzes + // the new instruction and records that them module is modified. + // Assumes %min_value is signed-less-or-equal than %max_value. (All callees + // use 0 for %min_value). + auto clamp_index = [&inst, type_mgr, this, &replace_index]( + uint32_t operand_index, Instruction* old_value, + Instruction* min_value, Instruction* max_value) { + auto* clamp_inst = + MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst); + return replace_index(operand_index, clamp_inst); + }; + + // Ensures the specified index of access chain |inst| has a value that is + // at most |count| - 1. If the index is already a constant value less than + // |count| then no change is made. + auto clamp_to_literal_count = + [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index, + &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t { + Instruction* index_inst = + this->GetDef(inst.GetSingleWordOperand(operand_index)); + const auto* index_type = + type_mgr->GetType(index_inst->type_id())->AsInteger(); + assert(index_type); + const auto index_width = index_type->width(); + + if (count <= 1) { + // Replace the index with 0. + return replace_index(operand_index, GetValueForType(0, index_type)); + } + + uint64_t maxval = count - 1; + + // Compute the bit width of a viable type to hold |maxval|. + // Look for a bit width, up to 64 bits wide, to fit maxval. + uint32_t maxval_width = index_width; + while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) { + maxval_width *= 2; + } + // Determine the type for |maxval|. + uint32_t next_id = context()->module()->IdBound(); + analysis::Integer signed_type_for_query(maxval_width, true); + auto* maxval_type = + type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger(); + if (next_id != context()->module()->IdBound()) { + module_status_.modified = true; + } + // Access chain indices are treated as signed, so limit the maximum value + // of the index so it will always be positive for a signed clamp operation. + maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1)); + + if (index_width > 64) { + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_width << " bits as index number " + << operand_index << " of access chain " + << inst.PrettyPrint(); + } + + // Split into two cases: the current index is a constant, or not. + + // If the index is a constant then |index_constant| will not be a null + // pointer. (If index is an |OpConstantNull| then it |index_constant| will + // not be a null pointer.) Since access chain indices must be scalar + // integers, this can't be a spec constant. + if (auto* index_constant = constant_mgr->GetConstantFromInst(index_inst)) { + auto* int_index_constant = index_constant->AsIntConstant(); + int64_t value = 0; + // OpAccessChain indices are treated as signed. So get the signed + // constant value here. + if (index_width <= 32) { + value = int64_t(int_index_constant->GetS32BitValue()); + } else if (index_width <= 64) { + value = int_index_constant->GetS64BitValue(); + } + if (value < 0) { + return replace_index(operand_index, GetValueForType(0, index_type)); + } else if (uint64_t(value) <= maxval) { + // Nothing to do. + return SPV_SUCCESS; + } else { + // Replace with maxval. + assert(count > 0); // Already took care of this case above. + return replace_index(operand_index, + GetValueForType(maxval, maxval_type)); + } + } else { + // Generate a clamp instruction. + assert(maxval >= 1); + assert(index_width <= 64); // Otherwise, already returned above. + if (index_width >= 64 && !have_int64_cap) { + // An inconsistent module. + return Fail() << "Access chain index is wider than 64 bits, but Int64 " + "is not declared: " + << index_inst->PrettyPrint(); + } + // Widen the index value if necessary + if (maxval_width > index_width) { + // Find the wider type. We only need this case if a constant array + // bound is too big. + + // From how we calculated maxval_width, widening won't require adding + // the Int64 capability. + assert(have_int64_cap || maxval_width <= 32); + if (!have_int64_cap && maxval_width >= 64) { + // Be defensive, but this shouldn't happen. + return this->Fail() + << "Clamping index would require adding Int64 capability. " + << "Can't clamp 32-bit index " << operand_index + << " of access chain " << inst.PrettyPrint(); + } + index_inst = WidenInteger(index_type->IsSigned(), maxval_width, + index_inst, &inst); + } + + // Finally, clamp the index. + return clamp_index(operand_index, index_inst, + GetValueForType(0, maxval_type), + GetValueForType(maxval, maxval_type)); + } + return SPV_SUCCESS; + }; + + // Ensures the specified index of access chain |inst| has a value that is at + // most the value of |count_inst| minus 1, where |count_inst| is treated as an + // unsigned integer. This can log a failure. + auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count, + &clamp_index, + &type_mgr](uint32_t operand_index, + Instruction* count_inst) -> spv_result_t { + Instruction* index_inst = + this->GetDef(inst.GetSingleWordOperand(operand_index)); + const auto* index_type = + type_mgr->GetType(index_inst->type_id())->AsInteger(); + const auto* count_type = + type_mgr->GetType(count_inst->type_id())->AsInteger(); + assert(index_type); + if (const auto* count_constant = + constant_mgr->GetConstantFromInst(count_inst)) { + uint64_t value = 0; + const auto width = count_constant->type()->AsInteger()->width(); + if (width <= 32) { + value = count_constant->AsIntConstant()->GetU32BitValue(); + } else if (width <= 64) { + value = count_constant->AsIntConstant()->GetU64BitValue(); + } else { + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_type->width() << "bits"; + } + return clamp_to_literal_count(operand_index, value); + } else { + // Widen them to the same width. + const auto index_width = index_type->width(); + const auto count_width = count_type->width(); + const auto target_width = std::max(index_width, count_width); + // UConvert requires the result type to have 0 signedness. So enforce + // that here. + auto* wider_type = index_width < count_width ? count_type : index_type; + if (index_type->width() < target_width) { + // Access chain indices are treated as signed integers. + index_inst = WidenInteger(true, target_width, index_inst, &inst); + } else if (count_type->width() < target_width) { + // Assume type sizes are treated as unsigned. + count_inst = WidenInteger(false, target_width, count_inst, &inst); + } + // Compute count - 1. + // It doesn't matter if 1 is signed or unsigned. + auto* one = GetValueForType(1, wider_type); + auto* count_minus_1 = InsertInst( + &inst, spv::Op::OpISub, type_mgr->GetId(wider_type), TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {one->result_id()}}}); + auto* zero = GetValueForType(0, wider_type); + // Make sure we clamp to an upper bound that is at most the signed max + // for the target type. + const uint64_t max_signed_value = + ((uint64_t(1) << (target_width - 1)) - 1); + // Use unsigned-min to ensure that the result is always non-negative. + // That ensures we satisfy the invariant for SClamp, where the "min" + // argument we give it (zero), is no larger than the third argument. + auto* upper_bound = + MakeUMinInst(*type_mgr, count_minus_1, + GetValueForType(max_signed_value, wider_type), &inst); + // Now clamp the index to this upper bound. + return clamp_index(operand_index, index_inst, zero, upper_bound); + } + return SPV_SUCCESS; + }; + + const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0)); + const Instruction* base_type = GetDef(base_inst->type_id()); + Instruction* pointee_type = GetDef(base_type->GetSingleWordInOperand(1)); + + // Walk the indices from earliest to latest, replacing indices with a + // clamped value, and updating the pointee_type. The order matters for + // the case when we have to compute the length of a runtime array. In + // that the algorithm relies on the fact that that the earlier indices + // have already been clamped. + const uint32_t num_operands = inst.NumOperands(); + for (uint32_t idx = 3; !module_status_.failed && idx < num_operands; ++idx) { + const uint32_t index_id = inst.GetSingleWordOperand(idx); + Instruction* index_inst = GetDef(index_id); + + switch (pointee_type->opcode()) { + case spv::Op::OpTypeMatrix: // Use column count + case spv::Op::OpTypeVector: // Use component count + { + const uint32_t count = pointee_type->GetSingleWordOperand(2); + clamp_to_literal_count(idx, count); + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + case spv::Op::OpTypeArray: { + // The array length can be a spec constant, so go through the general + // case. + Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2)); + clamp_to_count(idx, array_len); + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + case spv::Op::OpTypeStruct: { + // SPIR-V requires the index to be an OpConstant. + // We need to know the index literal value so we can compute the next + // pointee type. + if (index_inst->opcode() != spv::Op::OpConstant || + !constant_mgr->GetConstantFromInst(index_inst) + ->type() + ->AsInteger()) { + Fail() << "Member index into struct is not a constant integer: " + << index_inst->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\nin access chain: " + << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return; + } + const auto num_members = pointee_type->NumInOperands(); + const auto* index_constant = + constant_mgr->GetConstantFromInst(index_inst); + // Get the sign-extended value, since access index is always treated as + // signed. + const auto index_value = index_constant->GetSignExtendedValue(); + if (index_value < 0 || index_value >= num_members) { + Fail() << "Member index " << index_value + << " is out of bounds for struct type: " + << pointee_type->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\nin access chain: " + << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return; + } + pointee_type = GetDef(pointee_type->GetSingleWordInOperand( + static_cast(index_value))); + // No need to clamp this index. We just checked that it's valid. + } break; + + case spv::Op::OpTypeRuntimeArray: { + auto* array_len = MakeRuntimeArrayLengthInst(&inst, idx); + if (!array_len) { // We've already signaled an error. + return; + } + clamp_to_count(idx, array_len); + if (module_status_.failed) return; + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + default: + Fail() << " Unhandled pointee type for access chain " + << pointee_type->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + } +} + +uint32_t GraphicsRobustAccessPass::GetGlslInsts() { + if (module_status_.glsl_insts_id == 0) { + // This string serves double-duty as raw data for a string and for a vector + // of 32-bit words + const char glsl[] = "GLSL.std.450"; + // Use an existing import if we can. + for (auto& inst : context()->module()->ext_inst_imports()) { + if (inst.GetInOperand(0).AsString() == glsl) { + module_status_.glsl_insts_id = inst.result_id(); + } + } + if (module_status_.glsl_insts_id == 0) { + // Make a new import instruction. + module_status_.glsl_insts_id = TakeNextId(); + std::vector words = spvtools::utils::MakeVector(glsl); + auto import_inst = MakeUnique( + context(), spv::Op::OpExtInstImport, 0, module_status_.glsl_insts_id, + std::initializer_list{ + Operand{SPV_OPERAND_TYPE_LITERAL_STRING, std::move(words)}}); + Instruction* inst = import_inst.get(); + context()->module()->AddExtInstImport(std::move(import_inst)); + module_status_.modified = true; + context()->AnalyzeDefUse(inst); + // Invalidates the feature manager, since we added an extended instruction + // set import. + context()->ResetFeatureManager(); + } + } + return module_status_.glsl_insts_id; +} + +opt::Instruction* opt::GraphicsRobustAccessPass::GetValueForType( + uint64_t value, const analysis::Integer* type) { + auto* mgr = context()->get_constant_mgr(); + assert(type->width() <= 64); + std::vector words; + words.push_back(uint32_t(value)); + if (type->width() > 32) { + words.push_back(uint32_t(value >> 32u)); + } + const auto* constant = mgr->GetConstant(type, words); + return mgr->GetDefiningInstruction( + constant, context()->get_type_mgr()->GetTypeInstruction(type)); +} + +opt::Instruction* opt::GraphicsRobustAccessPass::WidenInteger( + bool sign_extend, uint32_t bit_width, Instruction* value, + Instruction* before_inst) { + analysis::Integer unsigned_type_for_query(bit_width, false); + auto* type_mgr = context()->get_type_mgr(); + auto* unsigned_type = type_mgr->GetRegisteredType(&unsigned_type_for_query); + auto type_id = context()->get_type_mgr()->GetId(unsigned_type); + auto conversion_id = TakeNextId(); + auto* conversion = InsertInst( + before_inst, (sign_extend ? spv::Op::OpSConvert : spv::Op::OpUConvert), + type_id, conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}}); + return conversion; +} + +Instruction* GraphicsRobustAccessPass::MakeUMinInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* y, + Instruction* where) { + // Get IDs of instructions we'll be referencing. Evaluate them before calling + // the function so we force a deterministic ordering in case both of them need + // to take a new ID. + const uint32_t glsl_insts_id = GetGlslInsts(); + uint32_t smin_id = TakeNextId(); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width(); + assert(xwidth == ywidth); + (void)xwidth; + (void)ywidth; + auto* smin_inst = InsertInst( + where, spv::Op::OpExtInst, x->type_id(), smin_id, + { + {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}}, + {SPV_OPERAND_TYPE_ID, {x->result_id()}}, + {SPV_OPERAND_TYPE_ID, {y->result_id()}}, + }); + return smin_inst; +} + +Instruction* GraphicsRobustAccessPass::MakeSClampInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* min, + Instruction* max, Instruction* where) { + // Get IDs of instructions we'll be referencing. Evaluate them before calling + // the function so we force a deterministic ordering in case both of them need + // to take a new ID. + const uint32_t glsl_insts_id = GetGlslInsts(); + uint32_t clamp_id = TakeNextId(); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width(); + const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width(); + assert(xwidth == minwidth); + assert(xwidth == maxwidth); + (void)xwidth; + (void)minwidth; + (void)maxwidth; + auto* clamp_inst = InsertInst( + where, spv::Op::OpExtInst, x->type_id(), clamp_id, + { + {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}}, + {SPV_OPERAND_TYPE_ID, {x->result_id()}}, + {SPV_OPERAND_TYPE_ID, {min->result_id()}}, + {SPV_OPERAND_TYPE_ID, {max->result_id()}}, + }); + return clamp_inst; +} + +Instruction* GraphicsRobustAccessPass::MakeRuntimeArrayLengthInst( + Instruction* access_chain, uint32_t operand_index) { + // The Index parameter to the access chain at |operand_index| is indexing + // *into* the runtime-array. To get the number of elements in the runtime + // array we need a pointer to the Block-decorated struct that contains the + // runtime array. So conceptually we have to go 2 steps backward in the + // access chain. The two steps backward might forces us to traverse backward + // across multiple dominating instructions. + auto* type_mgr = context()->get_type_mgr(); + + // How many access chain indices do we have to unwind to find the pointer + // to the struct containing the runtime array? + uint32_t steps_remaining = 2; + // Find or create an instruction computing the pointer to the structure + // containing the runtime array. + // Walk backward through pointer address calculations until we either get + // to exactly the right base pointer, or to an access chain instruction + // that we can replicate but truncate to compute the address of the right + // struct. + Instruction* current_access_chain = access_chain; + Instruction* pointer_to_containing_struct = nullptr; + while (steps_remaining > 0) { + switch (current_access_chain->opcode()) { + case spv::Op::OpCopyObject: + // Whoops. Walk right through this one. + current_access_chain = + GetDef(current_access_chain->GetSingleWordInOperand(0)); + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + const int first_index_operand = 3; + // How many indices in this access chain contribute to getting us + // to an element in the runtime array? + const auto num_contributing_indices = + current_access_chain == access_chain + ? operand_index - (first_index_operand - 1) + : current_access_chain->NumInOperands() - 1 /* skip the base */; + Instruction* base = + GetDef(current_access_chain->GetSingleWordInOperand(0)); + if (num_contributing_indices == steps_remaining) { + // The base pointer points to the structure. + pointer_to_containing_struct = base; + steps_remaining = 0; + break; + } else if (num_contributing_indices < steps_remaining) { + // Peel off the index and keep going backward. + steps_remaining -= num_contributing_indices; + current_access_chain = base; + } else { + // This access chain has more indices than needed. Generate a new + // access chain instruction, but truncating the list of indices. + const int base_operand = 2; + // We'll use the base pointer and the indices up to but not including + // the one indexing into the runtime array. + Instruction::OperandList ops; + // Use the base pointer + ops.push_back(current_access_chain->GetOperand(base_operand)); + const uint32_t num_indices_to_keep = + num_contributing_indices - steps_remaining - 1; + for (uint32_t i = 0; i <= num_indices_to_keep; i++) { + ops.push_back( + current_access_chain->GetOperand(first_index_operand + i)); + } + // Compute the type of the result of the new access chain. Start at + // the base and walk the indices in a forward direction. + auto* constant_mgr = context()->get_constant_mgr(); + std::vector indices_for_type; + for (uint32_t i = 0; i < ops.size() - 1; i++) { + uint32_t index_for_type_calculation = 0; + Instruction* index = + GetDef(current_access_chain->GetSingleWordOperand( + first_index_operand + i)); + if (auto* index_constant = + constant_mgr->GetConstantFromInst(index)) { + // We only need 32 bits. For the type calculation, it's sufficient + // to take the zero-extended value. It only matters for the struct + // case, and struct member indices are unsigned. + index_for_type_calculation = + uint32_t(index_constant->GetZeroExtendedValue()); + } else { + // Indexing into a variably-sized thing like an array. Use 0. + index_for_type_calculation = 0; + } + indices_for_type.push_back(index_for_type_calculation); + } + auto* base_ptr_type = type_mgr->GetType(base->type_id())->AsPointer(); + auto* base_pointee_type = base_ptr_type->pointee_type(); + auto* new_access_chain_result_pointee_type = + type_mgr->GetMemberType(base_pointee_type, indices_for_type); + const uint32_t new_access_chain_type_id = type_mgr->FindPointerToType( + type_mgr->GetId(new_access_chain_result_pointee_type), + base_ptr_type->storage_class()); + + // Create the instruction and insert it. + const auto new_access_chain_id = TakeNextId(); + auto* new_access_chain = + InsertInst(current_access_chain, current_access_chain->opcode(), + new_access_chain_type_id, new_access_chain_id, ops); + pointer_to_containing_struct = new_access_chain; + steps_remaining = 0; + break; + } + } break; + default: + Fail() << "Unhandled access chain in logical addressing mode passes " + "through " + << current_access_chain->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return nullptr; + } + } + assert(pointer_to_containing_struct); + auto* pointee_type = + type_mgr->GetType(pointer_to_containing_struct->type_id()) + ->AsPointer() + ->pointee_type(); + + auto* struct_type = pointee_type->AsStruct(); + const uint32_t member_index_of_runtime_array = + uint32_t(struct_type->element_types().size() - 1); + // Create the length-of-array instruction before the original access chain, + // but after the generation of the pointer to the struct. + const auto array_len_id = TakeNextId(); + analysis::Integer uint_type_for_query(32, false); + auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query); + auto* array_len = InsertInst( + access_chain, spv::Op::OpArrayLength, type_mgr->GetId(uint_type), + array_len_id, + {{SPV_OPERAND_TYPE_ID, {pointer_to_containing_struct->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index_of_runtime_array}}}); + return array_len; +} + +spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( + opt::Instruction* image_texel_pointer) { + // TODO(dneto): Write tests for this code. + // TODO(dneto): Use signed-clamp + (void)(image_texel_pointer); + return SPV_SUCCESS; + + // Do not compile this code until it is ready to be used. +#if 0 + // Example: + // %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord + // %sample + // + // We want to clamp %coord components between vector-0 and the result + // of OpImageQuerySize acting on the underlying image. So insert: + // %image = OpLoad %image_type %image_ptr + // %query_size = OpImageQuerySize %query_size_type %image + // + // For a multi-sampled image, %sample is the sample index, and we need + // to clamp it between zero and the number of samples in the image. + // %sample_count = OpImageQuerySamples %uint %image + // %max_sample_index = OpISub %uint %sample_count %uint_1 + // For non-multi-sampled images, the sample index must be constant zero. + + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* type_mgr = context()->get_type_mgr(); + auto* constant_mgr = context()->get_constant_mgr(); + + auto* image_ptr = GetDef(image_texel_pointer->GetSingleWordInOperand(0)); + auto* image_ptr_type = GetDef(image_ptr->type_id()); + auto image_type_id = image_ptr_type->GetSingleWordInOperand(1); + auto* image_type = GetDef(image_type_id); + auto* coord = GetDef(image_texel_pointer->GetSingleWordInOperand(1)); + auto* samples = GetDef(image_texel_pointer->GetSingleWordInOperand(2)); + + // We will modify the module, at least by adding image query instructions. + module_status_.modified = true; + + // Declare the ImageQuery capability if the module doesn't already have it. + auto* feature_mgr = context()->get_feature_mgr(); + if (!feature_mgr->HasCapability(spv::Capability::ImageQuery)) { + auto cap = MakeUnique( + context(), spv::Op::OpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, {spv::Capability::ImageQuery}}}); + def_use_mgr->AnalyzeInstDefUse(cap.get()); + context()->AddCapability(std::move(cap)); + feature_mgr->Analyze(context()->module()); + } + + // OpImageTexelPointer is used to translate a coordinate and sample index + // into an address for use with an atomic operation. That is, it may only + // used with what Vulkan calls a "storage image" + // (OpTypeImage parameter Sampled=2). + // Note: A storage image never has a level-of-detail associated with it. + + // Constraints on the sample id: + // - Only 2D images can be multi-sampled: OpTypeImage parameter MS=1 + // only if Dim=2D. + // - Non-multi-sampled images (OpTypeImage parameter MS=0) must use + // sample ID to a constant 0. + + // The coordinate is treated as unsigned, and should be clamped against the + // image "size", returned by OpImageQuerySize. (Note: OpImageQuerySizeLod + // is only usable with a sampled image, i.e. its image type has Sampled=1). + + // Determine the result type for the OpImageQuerySize. + // For non-arrayed images: + // non-Cube: + // - Always the same as the coordinate type + // Cube: + // - Use all but the last component of the coordinate (which is the face + // index from 0 to 5). + // For arrayed images (in Vulkan the Dim is 1D, 2D, or Cube): + // non-Cube: + // - A vector with the components in the coordinate, and one more for + // the layer index. + // Cube: + // - The same as the coordinate type: 3-element integer vector. + // - The third component from the size query is the layer count. + // - The third component in the texel pointer calculation is + // 6 * layer + face, where 0 <= face < 6. + // Cube: Use all but the last component of the coordinate (which is the face + // index from 0 to 5). + const auto dim = SpvDim(image_type->GetSingleWordInOperand(1)); + const bool arrayed = image_type->GetSingleWordInOperand(3) == 1; + const bool multisampled = image_type->GetSingleWordInOperand(4) != 0; + const auto query_num_components = [dim, arrayed, this]() -> int { + const int arrayness_bonus = arrayed ? 1 : 0; + int num_coords = 0; + switch (dim) { + case spv::Dim::Buffer: + case SpvDim1D: + num_coords = 1; + break; + case spv::Dim::Cube: + // For cube, we need bounds for x, y, but not face. + case spv::Dim::Rect: + case SpvDim2D: + num_coords = 2; + break; + case SpvDim3D: + num_coords = 3; + break; + case spv::Dim::SubpassData: + case spv::Dim::Max: + return Fail() << "Invalid image dimension for OpImageTexelPointer: " + << int(dim); + break; + } + return num_coords + arrayness_bonus; + }(); + const auto* coord_component_type = [type_mgr, coord]() { + const analysis::Type* coord_type = type_mgr->GetType(coord->type_id()); + if (auto* vector_type = coord_type->AsVector()) { + return vector_type->element_type()->AsInteger(); + } + return coord_type->AsInteger(); + }(); + // For now, only handle 32-bit case for coordinates. + if (!coord_component_type) { + return Fail() << " Coordinates for OpImageTexelPointer are not integral: " + << image_texel_pointer->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + if (coord_component_type->width() != 32) { + return Fail() << " Expected OpImageTexelPointer coordinate components to " + "be 32-bits wide. They are " + << coord_component_type->width() << " bits. " + << image_texel_pointer->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + const auto* query_size_type = + [type_mgr, coord_component_type, + query_num_components]() -> const analysis::Type* { + if (query_num_components == 1) return coord_component_type; + analysis::Vector proposed(coord_component_type, query_num_components); + return type_mgr->GetRegisteredType(&proposed); + }(); + + const uint32_t image_id = TakeNextId(); + auto* image = + InsertInst(image_texel_pointer, spv::Op::OpLoad, image_type_id, image_id, + {{SPV_OPERAND_TYPE_ID, {image_ptr->result_id()}}}); + + const uint32_t query_size_id = TakeNextId(); + auto* query_size = + InsertInst(image_texel_pointer, spv::Op::OpImageQuerySize, + type_mgr->GetTypeInstruction(query_size_type), query_size_id, + {{SPV_OPERAND_TYPE_ID, {image->result_id()}}}); + + auto* component_1 = constant_mgr->GetConstant(coord_component_type, {1}); + const uint32_t component_1_id = + constant_mgr->GetDefiningInstruction(component_1)->result_id(); + auto* component_0 = constant_mgr->GetConstant(coord_component_type, {0}); + const uint32_t component_0_id = + constant_mgr->GetDefiningInstruction(component_0)->result_id(); + + // If the image is a cube array, then the last component of the queried + // size is the layer count. In the query, we have to accommodate folding + // in the face index ranging from 0 through 5. The inclusive upper bound + // on the third coordinate therefore is multiplied by 6. + auto* query_size_including_faces = query_size; + if (arrayed && (dim == spv::Dim::Cube)) { + // Multiply the last coordinate by 6. + auto* component_6 = constant_mgr->GetConstant(coord_component_type, {6}); + const uint32_t component_6_id = + constant_mgr->GetDefiningInstruction(component_6)->result_id(); + assert(query_num_components == 3); + auto* multiplicand = constant_mgr->GetConstant( + query_size_type, {component_1_id, component_1_id, component_6_id}); + auto* multiplicand_inst = + constant_mgr->GetDefiningInstruction(multiplicand); + const auto query_size_including_faces_id = TakeNextId(); + query_size_including_faces = InsertInst( + image_texel_pointer, spv::Op::OpIMul, + type_mgr->GetTypeInstruction(query_size_type), + query_size_including_faces_id, + {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}}, + {SPV_OPERAND_TYPE_ID, {multiplicand_inst->result_id()}}}); + } + + // Make a coordinate-type with all 1 components. + auto* coordinate_1 = + query_num_components == 1 + ? component_1 + : constant_mgr->GetConstant( + query_size_type, + std::vector(query_num_components, component_1_id)); + // Make a coordinate-type with all 1 components. + auto* coordinate_0 = + query_num_components == 0 + ? component_0 + : constant_mgr->GetConstant( + query_size_type, + std::vector(query_num_components, component_0_id)); + + const uint32_t query_max_including_faces_id = TakeNextId(); + auto* query_max_including_faces = InsertInst( + image_texel_pointer, spv::Op::OpISub, + type_mgr->GetTypeInstruction(query_size_type), + query_max_including_faces_id, + {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}}, + {SPV_OPERAND_TYPE_ID, + {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}}); + + // Clamp the coordinate + auto* clamp_coord = MakeSClampInst( + *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0), + query_max_including_faces, image_texel_pointer); + image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()}); + + // Clamp the sample index + if (multisampled) { + // Get the sample count via OpImageQuerySamples + const auto query_samples_id = TakeNextId(); + auto* query_samples = InsertInst( + image_texel_pointer, spv::Op::OpImageQuerySamples, + constant_mgr->GetDefiningInstruction(component_0)->type_id(), + query_samples_id, {{SPV_OPERAND_TYPE_ID, {image->result_id()}}}); + + const auto max_samples_id = TakeNextId(); + auto* max_samples = InsertInst(image_texel_pointer, spv::Op::OpImageQuerySamples, + query_samples->type_id(), max_samples_id, + {{SPV_OPERAND_TYPE_ID, {query_samples_id}}, + {SPV_OPERAND_TYPE_ID, {component_1_id}}}); + + auto* clamp_samples = MakeSClampInst( + *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0), + max_samples, image_texel_pointer); + image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()}); + + } else { + // Just replace it with 0. Don't even check what was there before. + image_texel_pointer->SetInOperand(2, {component_0_id}); + } + + def_use_mgr->AnalyzeInstUse(image_texel_pointer); + + return SPV_SUCCESS; +#endif +} + +opt::Instruction* GraphicsRobustAccessPass::InsertInst( + opt::Instruction* where_inst, spv::Op opcode, uint32_t type_id, + uint32_t result_id, const Instruction::OperandList& operands) { + module_status_.modified = true; + auto* result = where_inst->InsertBefore( + MakeUnique(context(), opcode, type_id, result_id, operands)); + context()->get_def_use_mgr()->AnalyzeInstDefUse(result); + auto* basic_block = context()->get_instr_block(where_inst); + context()->set_instr_block(result, basic_block); + return result; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.h new file mode 100644 index 00000000..a7ffe115 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/graphics_robust_access_pass.h @@ -0,0 +1,156 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ +#define SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ + +#include +#include + +#include "constants.h" +#include "def_use_manager.h" +#include "instruction.h" +#include "module.h" +#include "pass.h" +#include "source/diagnostic.h" +#include "type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class GraphicsRobustAccessPass : public Pass { + public: + GraphicsRobustAccessPass(); + const char* name() const override { return "graphics-robust-access"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes | + IRContext::kAnalysisIdToFuncMapping; + } + + private: + // Records failure for the current module, and returns a stream + // that can be used to provide user error information to the message + // consumer. + spvtools::DiagnosticStream Fail(); + + // Returns SPV_SUCCESS if this pass can correctly process the module, + // as far as we can tell from capabilities and the memory model. + // Otherwise logs a message and returns a failure code. + spv_result_t IsCompatibleModule(); + + // Transform the current module, if possible. Failure and modification + // status is recorded in the |_| member. On failure, error information is + // posted to the message consumer. The return value has no significance. + spv_result_t ProcessCurrentModule(); + + // Process the given function. Updates the state value |_|. Returns true + // if the module was modified. This can log a failure. + bool ProcessAFunction(opt::Function*); + + // Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain|. Inserts instructions before the given instruction. Updates + // analyses and records that the module is modified. This can log a failure. + void ClampIndicesForAccessChain(Instruction* access_chain); + + // Returns the id of the instruction importing the "GLSL.std.450" extended + // instruction set. If it does not yet exist, the import instruction is + // created and inserted into the module, and updates |_.modified| and + // |_.glsl_insts_id|. + uint32_t GetGlslInsts(); + + // Returns an instruction which is constant with the given value of the given + // type. Ignores any value bits beyond the width of the type. + Instruction* GetValueForType(uint64_t value, const analysis::Integer* type); + + // Converts an integer value to an unsigned wider integer type, using either + // sign extension or zero extension. The new instruction is inserted + // immediately before |before_inst|, and is analyzed for definitions and uses. + // Returns the newly inserted instruction. Assumes the |value| is an integer + // scalar of a narrower type than |bit_width| bits. + Instruction* WidenInteger(bool sign_extend, uint32_t bit_width, + Instruction* value, Instruction* before_inst); + + // Returns a new instruction that invokes the UMin GLSL.std.450 extended + // instruction with the two given operands. That is, the result of the + // instruction is: + // - |x| if |x| is unsigned-less than |y| + // - |y| otherwise + // We assume that |x| and |y| are scalar integer types with the same + // width. The instruction is inserted before |where|. + opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* y, + Instruction* where); + + // Returns a new instruction that invokes the SClamp GLSL.std.450 extended + // instruction with the three given operands. That is, the result of the + // instruction is: + // - |min| if |x| is signed-less than |min| + // - |max| if |x| is signed-more than |max| + // - |x| otherwise. + // We assume that |min| is signed-less-or-equal to |max|, and that the + // operands all have the same scalar integer type. The instruction is + // inserted before |where|. + opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* min, + Instruction* max, Instruction* where); + + // Returns a new instruction which evaluates to the length the runtime array + // referenced by the access chain at the specified index. The instruction is + // inserted before the access chain instruction. Returns a null pointer in + // some cases if assumptions are violated (rather than asserting out). + opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain, + uint32_t operand_index); + + // Clamps the coordinate for an OpImageTexelPointer so it stays within + // the bounds of the size of the image. Updates analyses and records that + // the module is modified. Returns a status code to indicate success + // or failure. If assumptions are not met, returns an error status code + // and emits a diagnostic. + spv_result_t ClampCoordinateForImageTexelPointer( + opt::Instruction* image_texel_pointer); + + // Gets the instruction that defines the given id. + opt::Instruction* GetDef(uint32_t id) { + return context()->get_def_use_mgr()->GetDef(id); + } + + // Returns a new instruction inserted before |where_inst|, and created from + // the remaining arguments. Registers the definitions and uses of the new + // instruction and also records its block. + opt::Instruction* InsertInst(opt::Instruction* where_inst, spv::Op opcode, + uint32_t type_id, uint32_t result_id, + const Instruction::OperandList& operands); + + // State required for the current module. + struct PerModuleState { + // This pass modified the module. + bool modified = false; + // True if there is an error processing the current module, e.g. if + // preconditions are not met. + bool failed = false; + // The id of the GLSL.std.450 extended instruction set. Zero if it does + // not exist. + uint32_t glsl_insts_id = 0; + } module_status_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.cpp new file mode 100644 index 00000000..5912cf12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.cpp @@ -0,0 +1,297 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/if_conversion.h" + +#include +#include + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status IfConversion::Process() { + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) { + return Status::SuccessWithoutChange; + } + + const ValueNumberTable& vn_table = *context()->GetValueNumberTable(); + bool modified = false; + std::vector to_kill; + for (auto& func : *get_module()) { + DominatorAnalysis* dominators = context()->GetDominatorAnalysis(&func); + for (auto& block : func) { + // Check if it is possible for |block| to have phis that can be + // transformed. + BasicBlock* common = nullptr; + if (!CheckBlock(&block, dominators, &common)) continue; + + // Get an insertion point. + auto iter = block.begin(); + while (iter != block.end() && iter->opcode() == spv::Op::OpPhi) { + ++iter; + } + + InstructionBuilder builder( + context(), &*iter, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + block.ForEachPhiInst([this, &builder, &modified, &common, &to_kill, + dominators, &block, &vn_table](Instruction* phi) { + // This phi is not compatible, but subsequent phis might be. + if (!CheckType(phi->type_id())) return; + + // We cannot transform cases where the phi is used by another phi in the + // same block due to instruction ordering restrictions. + // TODO(alan-baker): If all inappropriate uses could also be + // transformed, we could still remove this phi. + if (!CheckPhiUsers(phi, &block)) return; + + // Identify the incoming values associated with the true and false + // branches. If |then_block| dominates |inc0| or if the true edge + // branches straight to this block and |common| is |inc0|, then |inc0| + // is on the true branch. Otherwise the |inc1| is on the true branch. + BasicBlock* inc0 = GetIncomingBlock(phi, 0u); + Instruction* branch = common->terminator(); + uint32_t condition = branch->GetSingleWordInOperand(0u); + BasicBlock* then_block = GetBlock(branch->GetSingleWordInOperand(1u)); + Instruction* true_value = nullptr; + Instruction* false_value = nullptr; + if ((then_block == &block && inc0 == common) || + dominators->Dominates(then_block, inc0)) { + true_value = GetIncomingValue(phi, 0u); + false_value = GetIncomingValue(phi, 1u); + } else { + true_value = GetIncomingValue(phi, 1u); + false_value = GetIncomingValue(phi, 0u); + } + + BasicBlock* true_def_block = context()->get_instr_block(true_value); + BasicBlock* false_def_block = context()->get_instr_block(false_value); + + uint32_t true_vn = vn_table.GetValueNumber(true_value); + uint32_t false_vn = vn_table.GetValueNumber(false_value); + if (true_vn != 0 && true_vn == false_vn) { + Instruction* inst_to_use = nullptr; + + // Try to pick an instruction that is not in a side node. If we can't + // pick either the true for false branch as long as they can be + // legally moved. + if (!true_def_block || + dominators->Dominates(true_def_block, &block)) { + inst_to_use = true_value; + } else if (!false_def_block || + dominators->Dominates(false_def_block, &block)) { + inst_to_use = false_value; + } else if (CanHoistInstruction(true_value, common, dominators)) { + inst_to_use = true_value; + } else if (CanHoistInstruction(false_value, common, dominators)) { + inst_to_use = false_value; + } + + if (inst_to_use != nullptr) { + modified = true; + HoistInstruction(inst_to_use, common, dominators); + context()->KillNamesAndDecorates(phi); + context()->ReplaceAllUsesWith(phi->result_id(), + inst_to_use->result_id()); + } + return; + } + + // If either incoming value is defined in a block that does not dominate + // this phi, then we cannot eliminate the phi with a select. + // TODO(alan-baker): Perform code motion where it makes sense to enable + // the transform in this case. + if (true_def_block && !dominators->Dominates(true_def_block, &block)) + return; + + if (false_def_block && !dominators->Dominates(false_def_block, &block)) + return; + + analysis::Type* data_ty = + context()->get_type_mgr()->GetType(true_value->type_id()); + if (analysis::Vector* vec_data_ty = data_ty->AsVector()) { + condition = SplatCondition(vec_data_ty, condition, &builder); + } + + Instruction* select = builder.AddSelect(phi->type_id(), condition, + true_value->result_id(), + false_value->result_id()); + context()->get_def_use_mgr()->AnalyzeInstDefUse(select); + select->UpdateDebugInfoFrom(phi); + context()->ReplaceAllUsesWith(phi->result_id(), select->result_id()); + to_kill.push_back(phi); + modified = true; + + return; + }); + } + } + + for (auto inst : to_kill) { + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, + BasicBlock** common) { + const std::vector& preds = cfg()->preds(block->id()); + + // TODO(alan-baker): Extend to more than two predecessors + if (preds.size() != 2) return false; + + BasicBlock* inc0 = context()->get_instr_block(preds[0]); + if (dominators->Dominates(block, inc0)) return false; + + BasicBlock* inc1 = context()->get_instr_block(preds[1]); + if (dominators->Dominates(block, inc1)) return false; + + if (inc0 == inc1) { + // If the predecessor blocks are the same, then there is only 1 value for + // the OpPhi. Other transformation should be able to simplify that. + return false; + } + // All phis will have the same common dominator, so cache the result + // for this block. If there is no common dominator, then we cannot transform + // any phi in this basic block. + *common = dominators->CommonDominator(inc0, inc1); + if (!*common || cfg()->IsPseudoEntryBlock(*common)) return false; + Instruction* branch = (*common)->terminator(); + if (branch->opcode() != spv::Op::OpBranchConditional) return false; + auto merge = (*common)->GetMergeInst(); + if (!merge || merge->opcode() != spv::Op::OpSelectionMerge) return false; + if (spv::SelectionControlMask(merge->GetSingleWordInOperand(1)) == + spv::SelectionControlMask::DontFlatten) { + return false; + } + if ((*common)->MergeBlockIdIfAny() != block->id()) return false; + + return true; +} + +bool IfConversion::CheckPhiUsers(Instruction* phi, BasicBlock* block) { + return get_def_use_mgr()->WhileEachUser( + phi, [block, this](Instruction* user) { + if (user->opcode() == spv::Op::OpPhi && + context()->get_instr_block(user) == block) + return false; + return true; + }); +} + +uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty, + uint32_t cond, + InstructionBuilder* builder) { + // If the data inputs to OpSelect are vectors, the condition for + // OpSelect must be a boolean vector with the same number of + // components. So splat the condition for the branch into a vector + // type. + analysis::Bool bool_ty; + analysis::Vector bool_vec_ty(&bool_ty, vec_data_ty->element_count()); + uint32_t bool_vec_id = + context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty); + std::vector ids(vec_data_ty->element_count(), cond); + return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id(); +} + +bool IfConversion::CheckType(uint32_t id) { + Instruction* type = get_def_use_mgr()->GetDef(id); + spv::Op op = type->opcode(); + if (spvOpcodeIsScalarType(op) || op == spv::Op::OpTypePointer || + op == spv::Op::OpTypeVector) + return true; + return false; +} + +BasicBlock* IfConversion::GetBlock(uint32_t id) { + return context()->get_instr_block(get_def_use_mgr()->GetDef(id)); +} + +BasicBlock* IfConversion::GetIncomingBlock(Instruction* phi, + uint32_t predecessor) { + uint32_t in_index = 2 * predecessor + 1; + return GetBlock(phi->GetSingleWordInOperand(in_index)); +} + +Instruction* IfConversion::GetIncomingValue(Instruction* phi, + uint32_t predecessor) { + uint32_t in_index = 2 * predecessor; + return get_def_use_mgr()->GetDef(phi->GetSingleWordInOperand(in_index)); +} + +void IfConversion::HoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators) { + BasicBlock* inst_block = context()->get_instr_block(inst); + if (!inst_block) { + // This is in the header, and dominates everything. + return; + } + + if (dominators->Dominates(inst_block, target_block)) { + // Already in position. No work to do. + return; + } + + assert(inst->IsOpcodeCodeMotionSafe() && + "Trying to move an instruction that is not safe to move."); + + // First hoist all instructions it depends on. + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + inst->ForEachInId( + [this, target_block, def_use_mgr, dominators](uint32_t* id) { + Instruction* operand_inst = def_use_mgr->GetDef(*id); + HoistInstruction(operand_inst, target_block, dominators); + }); + + Instruction* insertion_pos = target_block->terminator(); + if ((insertion_pos)->PreviousNode()->opcode() == spv::Op::OpSelectionMerge) { + insertion_pos = insertion_pos->PreviousNode(); + } + inst->RemoveFromList(); + insertion_pos->InsertBefore(std::unique_ptr(inst)); + context()->set_instr_block(inst, target_block); +} + +bool IfConversion::CanHoistInstruction(Instruction* inst, + BasicBlock* target_block, + DominatorAnalysis* dominators) { + BasicBlock* inst_block = context()->get_instr_block(inst); + if (!inst_block) { + // This is in the header, and dominates everything. + return true; + } + + if (dominators->Dominates(inst_block, target_block)) { + // Already in position. No work to do. + return true; + } + + if (!inst->IsOpcodeCodeMotionSafe()) { + return false; + } + + // Check all instruction |inst| depends on. + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return inst->WhileEachInId( + [this, target_block, def_use_mgr, dominators](uint32_t* id) { + Instruction* operand_inst = def_use_mgr->GetDef(*id); + return CanHoistInstruction(operand_inst, target_block, dominators); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.h b/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.h new file mode 100644 index 00000000..db84e703 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/if_conversion.h @@ -0,0 +1,89 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IF_CONVERSION_H_ +#define SOURCE_OPT_IF_CONVERSION_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class IfConversion : public Pass { + public: + const char* name() const override { return "if-conversion"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisCFG | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Returns true if |id| is a valid type for use with OpSelect. OpSelect only + // allows scalars, vectors and pointers as valid inputs. + bool CheckType(uint32_t id); + + // Returns the basic block containing |id|. + BasicBlock* GetBlock(uint32_t id); + + // Returns the basic block for the |predecessor|'th index predecessor of + // |phi|. + BasicBlock* GetIncomingBlock(Instruction* phi, uint32_t predecessor); + + // Returns the instruction defining the |predecessor|'th index of |phi|. + Instruction* GetIncomingValue(Instruction* phi, uint32_t predecessor); + + // Returns the id of a OpCompositeConstruct boolean vector. The composite has + // the same number of elements as |vec_data_ty| and each member is |cond|. + // |where| indicates the location in |block| to insert the composite + // construct. If necessary, this function will also construct the necessary + // type instructions for the boolean vector. + uint32_t SplatCondition(analysis::Vector* vec_data_ty, uint32_t cond, + InstructionBuilder* builder); + + // Returns true if none of |phi|'s users are in |block|. + bool CheckPhiUsers(Instruction* phi, BasicBlock* block); + + // Returns |false| if |block| is not appropriate to transform. Only + // transforms blocks with two predecessors. Neither incoming block can be + // dominated by |block|. Both predecessors must share a common dominator that + // is terminated by a conditional branch. + bool CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, + BasicBlock** common); + + // Moves |inst| to |target_block| if it does not already dominate the block. + // Any instructions that |inst| depends on are move if necessary. It is + // assumed that |inst| can be hoisted to |target_block| as defined by + // |CanHoistInstruction|. |dominators| is the dominator analysis for the + // function that contains |target_block|. + void HoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators); + + // Returns true if it is legal to move |inst| and the instructions it depends + // on to |target_block| if they do not already dominate |target_block|. + bool CanHoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IF_CONVERSION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.cpp new file mode 100644 index 00000000..9cdea43d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_exhaustive_pass.h" + +#include + +namespace spvtools { +namespace opt { + +Pass::Status InlineExhaustivePass::InlineExhaustive(Function* func) { + bool modified = false; + // Using block iterators here because of block erasures and insertions. + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end();) { + if (IsInlinableFunctionCall(&*ii)) { + // Inline call. + std::vector> newBlocks; + std::vector> newVars; + if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) { + return Status::Failure; + } + // If call block is replaced with more than one block, point + // succeeding phis at new last block. + if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); + // Replace old calling block with new block(s). + + bi = bi.Erase(); + + for (auto& bb : newBlocks) { + bb->SetParent(func); + } + bi = bi.InsertBefore(&newBlocks); + // Insert new function variables. + if (newVars.size() > 0) + func->begin()->begin().InsertBefore(std::move(newVars)); + // Restart inlining at beginning of calling block. + ii = bi->begin(); + modified = true; + } else { + ++ii; + } + } + } + + if (modified) { + FixDebugDeclares(func); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +Pass::Status InlineExhaustivePass::ProcessImpl() { + Status status = Status::SuccessWithoutChange; + // Attempt exhaustive inlining on each entry point function in module + ProcessFunction pfn = [&status, this](Function* fp) { + status = CombineStatus(status, InlineExhaustive(fp)); + return false; + }; + context()->ProcessReachableCallTree(pfn); + return status; +} + +InlineExhaustivePass::InlineExhaustivePass() = default; + +Pass::Status InlineExhaustivePass::Process() { + InitializeInline(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.h new file mode 100644 index 00000000..c2e85473 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_exhaustive_pass.h @@ -0,0 +1,53 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ +#define SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/inline_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlineExhaustivePass : public InlinePass { + public: + InlineExhaustivePass(); + Status Process() override; + + const char* name() const override { return "inline-entry-points-exhaustive"; } + + private: + // Exhaustively inline all function calls in func as well as in + // all code that is inlined into func. Returns the status. + Status InlineExhaustive(Function* func); + + void Initialize(); + Pass::Status ProcessImpl(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.cpp new file mode 100644 index 00000000..90a4c224 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_opaque_pass.h" + +#include + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kTypePointerTypeIdInIdx = 1; +} // namespace + +bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) { + const Instruction* typeInst = get_def_use_mgr()->GetDef(typeId); + switch (typeInst->opcode()) { + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampledImage: + return true; + case spv::Op::OpTypePointer: + return IsOpaqueType( + typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx)); + default: + break; + } + // TODO(greg-lunarg): Handle arrays containing opaque type + if (typeInst->opcode() != spv::Op::OpTypeStruct) return false; + // Return true if any member is opaque + return !typeInst->WhileEachInId([this](const uint32_t* tid) { + if (IsOpaqueType(*tid)) return false; + return true; + }); +} + +bool InlineOpaquePass::HasOpaqueArgsOrReturn(const Instruction* callInst) { + // Check return type + if (IsOpaqueType(callInst->type_id())) return true; + // Check args + int icnt = 0; + return !callInst->WhileEachInId([&icnt, this](const uint32_t* iid) { + if (icnt > 0) { + const Instruction* argInst = get_def_use_mgr()->GetDef(*iid); + if (IsOpaqueType(argInst->type_id())) return false; + } + ++icnt; + return true; + }); +} + +Pass::Status InlineOpaquePass::InlineOpaque(Function* func) { + bool modified = false; + // Using block iterators here because of block erasures and insertions. + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end();) { + if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) { + // Inline call. + std::vector> newBlocks; + std::vector> newVars; + if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) { + return Status::Failure; + } + + // If call block is replaced with more than one block, point + // succeeding phis at new last block. + if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); + // Replace old calling block with new block(s). + bi = bi.Erase(); + bi = bi.InsertBefore(&newBlocks); + // Insert new function variables. + if (newVars.size() > 0) + func->begin()->begin().InsertBefore(std::move(newVars)); + // Restart inlining at beginning of calling block. + ii = bi->begin(); + modified = true; + } else { + ++ii; + } + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void InlineOpaquePass::Initialize() { InitializeInline(); } + +Pass::Status InlineOpaquePass::ProcessImpl() { + Status status = Status::SuccessWithoutChange; + // Do opaque inlining on each function in entry point call tree + ProcessFunction pfn = [&status, this](Function* fp) { + status = CombineStatus(status, InlineOpaque(fp)); + return false; + }; + context()->ProcessReachableCallTree(pfn); + return status; +} + +InlineOpaquePass::InlineOpaquePass() = default; + +Pass::Status InlineOpaquePass::Process() { + Initialize(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.h new file mode 100644 index 00000000..1e3081d2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_opaque_pass.h @@ -0,0 +1,60 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_OPAQUE_PASS_H_ +#define SOURCE_OPT_INLINE_OPAQUE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/inline_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlineOpaquePass : public InlinePass { + public: + InlineOpaquePass(); + Status Process() override; + + const char* name() const override { return "inline-entry-points-opaque"; } + + private: + // Return true if |typeId| is or contains opaque type + bool IsOpaqueType(uint32_t typeId); + + // Return true if function call |callInst| has opaque argument or return type + bool HasOpaqueArgsOrReturn(const Instruction* callInst); + + // Inline all function calls in |func| that have opaque params or return + // type. Inline similarly all code that is inlined into func. Return true + // if func is modified. + Status InlineOpaque(Function* func); + + void Initialize(); + Pass::Status ProcessImpl(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_OPAQUE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.cpp new file mode 100644 index 00000000..193e276f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.cpp @@ -0,0 +1,927 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_pass.h" + +#include +#include + +#include "source/cfa.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +// Indices of operands in SPIR-V instructions +constexpr int kSpvFunctionCallFunctionId = 2; +constexpr int kSpvFunctionCallArgumentId = 3; +constexpr int kSpvReturnValueId = 0; +constexpr int kSpvDebugDeclareVarInIdx = 3; +constexpr int kSpvAccessChainBaseInIdx = 0; +} // namespace + +uint32_t InlinePass::AddPointerToType(uint32_t type_id, + spv::StorageClass storage_class) { + uint32_t resultId = context()->TakeNextId(); + if (resultId == 0) { + return resultId; + } + + std::unique_ptr type_inst( + new Instruction(context(), spv::Op::OpTypePointer, 0, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(storage_class)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}})); + context()->AddType(std::move(type_inst)); + analysis::Type* pointeeTy; + std::unique_ptr pointerTy; + std::tie(pointeeTy, pointerTy) = + context()->get_type_mgr()->GetTypeAndPointerType( + type_id, spv::StorageClass::Function); + context()->get_type_mgr()->RegisterType(resultId, *pointerTy); + return resultId; +} + +void InlinePass::AddBranch(uint32_t label_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newBranch( + new Instruction(context(), spv::Op::OpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); + (*block_ptr)->AddInstruction(std::move(newBranch)); +} + +void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id, + uint32_t false_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newBranch( + new Instruction(context(), spv::Op::OpBranchConditional, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); + (*block_ptr)->AddInstruction(std::move(newBranch)); +} + +void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newLoopMerge(new Instruction( + context(), spv::Op::OpLoopMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {0}}})); + (*block_ptr)->AddInstruction(std::move(newLoopMerge)); +} + +void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, + std::unique_ptr* block_ptr, + const Instruction* line_inst, + const DebugScope& dbg_scope) { + std::unique_ptr newStore( + new Instruction(context(), spv::Op::OpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); + if (line_inst != nullptr) { + newStore->AddDebugLine(line_inst); + } + newStore->SetDebugScope(dbg_scope); + (*block_ptr)->AddInstruction(std::move(newStore)); +} + +void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, + std::unique_ptr* block_ptr, + const Instruction* line_inst, + const DebugScope& dbg_scope) { + std::unique_ptr newLoad( + new Instruction(context(), spv::Op::OpLoad, type_id, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); + if (line_inst != nullptr) { + newLoad->AddDebugLine(line_inst); + } + newLoad->SetDebugScope(dbg_scope); + (*block_ptr)->AddInstruction(std::move(newLoad)); +} + +std::unique_ptr InlinePass::NewLabel(uint32_t label_id) { + std::unique_ptr newLabel( + new Instruction(context(), spv::Op::OpLabel, 0, label_id, {})); + return newLabel; +} + +uint32_t InlinePass::GetFalseId() { + if (false_id_ != 0) return false_id_; + false_id_ = get_module()->GetGlobalValue(spv::Op::OpConstantFalse); + if (false_id_ != 0) return false_id_; + uint32_t boolId = get_module()->GetGlobalValue(spv::Op::OpTypeBool); + if (boolId == 0) { + boolId = context()->TakeNextId(); + if (boolId == 0) { + return 0; + } + get_module()->AddGlobalValue(spv::Op::OpTypeBool, boolId, 0); + } + false_id_ = context()->TakeNextId(); + if (false_id_ == 0) { + return 0; + } + get_module()->AddGlobalValue(spv::Op::OpConstantFalse, false_id_, boolId); + return false_id_; +} + +void InlinePass::MapParams( + Function* calleeFn, BasicBlock::iterator call_inst_itr, + std::unordered_map* callee2caller) { + int param_idx = 0; + calleeFn->ForEachParam( + [&call_inst_itr, ¶m_idx, &callee2caller](const Instruction* cpi) { + const uint32_t pid = cpi->result_id(); + (*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand( + kSpvFunctionCallArgumentId + param_idx); + ++param_idx; + }); +} + +bool InlinePass::CloneAndMapLocals( + Function* calleeFn, std::vector>* new_vars, + std::unordered_map* callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx) { + auto callee_block_itr = calleeFn->begin(); + auto callee_var_itr = callee_block_itr->begin(); + while (callee_var_itr->opcode() == spv::Op::OpVariable || + callee_var_itr->GetCommonDebugOpcode() == + CommonDebugInfoDebugDeclare) { + if (callee_var_itr->opcode() != spv::Op::OpVariable) { + ++callee_var_itr; + continue; + } + + std::unique_ptr var_inst(callee_var_itr->Clone(context())); + uint32_t newId = context()->TakeNextId(); + if (newId == 0) { + return false; + } + get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId); + var_inst->SetResultId(newId); + var_inst->UpdateDebugInlinedAt( + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx)); + (*callee2caller)[callee_var_itr->result_id()] = newId; + new_vars->push_back(std::move(var_inst)); + ++callee_var_itr; + } + return true; +} + +uint32_t InlinePass::CreateReturnVar( + Function* calleeFn, std::vector>* new_vars) { + uint32_t returnVarId = 0; + const uint32_t calleeTypeId = calleeFn->type_id(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + assert(type_mgr->GetType(calleeTypeId)->AsVoid() == nullptr && + "Cannot create a return variable of type void."); + // Find or create ptr to callee return type. + uint32_t returnVarTypeId = + type_mgr->FindPointerToType(calleeTypeId, spv::StorageClass::Function); + + if (returnVarTypeId == 0) { + returnVarTypeId = + AddPointerToType(calleeTypeId, spv::StorageClass::Function); + if (returnVarTypeId == 0) { + return 0; + } + } + + // Add return var to new function scope variables. + returnVarId = context()->TakeNextId(); + if (returnVarId == 0) { + return 0; + } + + std::unique_ptr var_inst(new Instruction( + context(), spv::Op::OpVariable, returnVarTypeId, returnVarId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {(uint32_t)spv::StorageClass::Function}}})); + new_vars->push_back(std::move(var_inst)); + get_decoration_mgr()->CloneDecorations(calleeFn->result_id(), returnVarId); + + // Decorate the return var with AliasedPointer if the storage class of the + // pointee type is PhysicalStorageBuffer. + auto const pointee_type = + type_mgr->GetType(returnVarTypeId)->AsPointer()->pointee_type(); + if (pointee_type->AsPointer() != nullptr) { + if (pointee_type->AsPointer()->storage_class() == + spv::StorageClass::PhysicalStorageBuffer) { + get_decoration_mgr()->AddDecoration( + returnVarId, uint32_t(spv::Decoration::AliasedPointer)); + } + } + + return returnVarId; +} + +bool InlinePass::IsSameBlockOp(const Instruction* inst) const { + return inst->opcode() == spv::Op::OpSampledImage || + inst->opcode() == spv::Op::OpImage; +} + +bool InlinePass::CloneSameBlockOps( + std::unique_ptr* inst, + std::unordered_map* postCallSB, + std::unordered_map* preCallSB, + std::unique_ptr* block_ptr) { + return (*inst)->WhileEachInId([&postCallSB, &preCallSB, &block_ptr, + this](uint32_t* iid) { + const auto mapItr = (*postCallSB).find(*iid); + if (mapItr == (*postCallSB).end()) { + const auto mapItr2 = (*preCallSB).find(*iid); + if (mapItr2 != (*preCallSB).end()) { + // Clone pre-call same-block ops, map result id. + const Instruction* inInst = mapItr2->second; + std::unique_ptr sb_inst(inInst->Clone(context())); + if (!CloneSameBlockOps(&sb_inst, postCallSB, preCallSB, block_ptr)) { + return false; + } + + const uint32_t rid = sb_inst->result_id(); + const uint32_t nid = context()->TakeNextId(); + if (nid == 0) { + return false; + } + get_decoration_mgr()->CloneDecorations(rid, nid); + sb_inst->SetResultId(nid); + (*postCallSB)[rid] = nid; + *iid = nid; + (*block_ptr)->AddInstruction(std::move(sb_inst)); + } + } else { + // Reset same-block op operand. + *iid = mapItr->second; + } + return true; + }); +} + +void InlinePass::MoveInstsBeforeEntryBlock( + std::unordered_map* preCallSB, + BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr) { + for (auto cii = call_block_itr->begin(); cii != call_inst_itr; + cii = call_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr cp_inst(inst); + // Remember same-block ops for possible regeneration. + if (IsSameBlockOp(&*cp_inst)) { + auto* sb_inst_ptr = cp_inst.get(); + (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr; + } + new_blk_ptr->AddInstruction(std::move(cp_inst)); + } +} + +std::unique_ptr InlinePass::AddGuardBlock( + std::vector>* new_blocks, + std::unordered_map* callee2caller, + std::unique_ptr new_blk_ptr, uint32_t entry_blk_label_id) { + const auto guard_block_id = context()->TakeNextId(); + if (guard_block_id == 0) { + return nullptr; + } + AddBranch(guard_block_id, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + // Start the next block. + new_blk_ptr = MakeUnique(NewLabel(guard_block_id)); + // Reset the mapping of the callee's entry block to point to + // the guard block. Do this so we can fix up phis later on to + // satisfy dominance. + (*callee2caller)[entry_blk_label_id] = guard_block_id; + return new_blk_ptr; +} + +InstructionList::iterator InlinePass::AddStoresForVariableInitializers( + const std::unordered_map& callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block_itr) { + auto callee_itr = callee_first_block_itr->begin(); + while (callee_itr->opcode() == spv::Op::OpVariable || + callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + if (callee_itr->opcode() == spv::Op::OpVariable && + callee_itr->NumInOperands() == 2) { + assert(callee2caller.count(callee_itr->result_id()) && + "Expected the variable to have already been mapped."); + uint32_t new_var_id = callee2caller.at(callee_itr->result_id()); + + // The initializer must be a constant or global value. No mapped + // should be used. + uint32_t val_id = callee_itr->GetSingleWordInOperand(1); + AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(), + context()->get_debug_info_mgr()->BuildDebugScope( + callee_itr->GetDebugScope(), inlined_at_ctx)); + } + if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + InlineSingleInstruction( + callee2caller, new_blk_ptr->get(), &*callee_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx)); + } + ++callee_itr; + } + return callee_itr; +} + +bool InlinePass::InlineSingleInstruction( + const std::unordered_map& callee2caller, + BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) { + // If we have return, it must be at the end of the callee. We will handle + // it at the end. + if (inst->opcode() == spv::Op::OpReturnValue || + inst->opcode() == spv::Op::OpReturn) + return true; + + // Copy callee instruction and remap all input Ids. + std::unique_ptr cp_inst(inst->Clone(context())); + cp_inst->ForEachInId([&callee2caller](uint32_t* iid) { + const auto mapItr = callee2caller.find(*iid); + if (mapItr != callee2caller.end()) { + *iid = mapItr->second; + } + }); + + // If result id is non-zero, remap it. + const uint32_t rid = cp_inst->result_id(); + if (rid != 0) { + const auto mapItr = callee2caller.find(rid); + if (mapItr == callee2caller.end()) { + return false; + } + uint32_t nid = mapItr->second; + cp_inst->SetResultId(nid); + get_decoration_mgr()->CloneDecorations(rid, nid); + } + + cp_inst->UpdateDebugInlinedAt(dbg_inlined_at); + new_blk_ptr->AddInstruction(std::move(cp_inst)); + return true; +} + +std::unique_ptr InlinePass::InlineReturn( + const std::unordered_map& callee2caller, + std::vector>* new_blocks, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, + const Instruction* inst, uint32_t returnVarId) { + // Store return value to return variable. + if (inst->opcode() == spv::Op::OpReturnValue) { + assert(returnVarId != 0); + uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0]; + const auto mapItr = callee2caller.find(valId); + if (mapItr != callee2caller.end()) { + valId = mapItr->second; + } + AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(), + context()->get_debug_info_mgr()->BuildDebugScope( + inst->GetDebugScope(), inlined_at_ctx)); + } + + uint32_t returnLabelId = 0; + for (auto callee_block_itr = calleeFn->begin(); + callee_block_itr != calleeFn->end(); ++callee_block_itr) { + if (spvOpcodeIsAbort(callee_block_itr->tail()->opcode())) { + returnLabelId = context()->TakeNextId(); + break; + } + } + if (returnLabelId == 0) return new_blk_ptr; + + if (inst->opcode() == spv::Op::OpReturn || + inst->opcode() == spv::Op::OpReturnValue) + AddBranch(returnLabelId, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + return MakeUnique(NewLabel(returnLabelId)); +} + +bool InlinePass::InlineEntryBlock( + const std::unordered_map& callee2caller, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block, + analysis::DebugInlinedAtContext* inlined_at_ctx) { + auto callee_inst_itr = AddStoresForVariableInitializers( + callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); + + while (callee_inst_itr != callee_first_block->end()) { + // Don't inline function definition links, the calling function is not a + // definition. + if (callee_inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + ++callee_inst_itr; + continue; + } + + if (!InlineSingleInstruction( + callee2caller, new_blk_ptr->get(), &*callee_inst_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_inst_itr->GetDebugScope().GetInlinedAt(), + inlined_at_ctx))) { + return false; + } + ++callee_inst_itr; + } + return true; +} + +std::unique_ptr InlinePass::InlineBasicBlocks( + std::vector>* new_blocks, + const std::unordered_map& callee2caller, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) { + auto callee_block_itr = calleeFn->begin(); + ++callee_block_itr; + + while (callee_block_itr != calleeFn->end()) { + new_blocks->push_back(std::move(new_blk_ptr)); + const auto mapItr = + callee2caller.find(callee_block_itr->GetLabelInst()->result_id()); + if (mapItr == callee2caller.end()) return nullptr; + new_blk_ptr = MakeUnique(NewLabel(mapItr->second)); + + auto tail_inst_itr = callee_block_itr->end(); + for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; + ++inst_itr) { + // Don't inline function definition links, the calling function is not a + // definition + if (inst_itr->GetShader100DebugOpcode() == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) + continue; + if (!InlineSingleInstruction( + callee2caller, new_blk_ptr.get(), &*inst_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) { + return nullptr; + } + } + + ++callee_block_itr; + } + return new_blk_ptr; +} + +bool InlinePass::MoveCallerInstsAfterFunctionCall( + std::unordered_map* preCallSB, + std::unordered_map* postCallSB, + std::unique_ptr* new_blk_ptr, + BasicBlock::iterator call_inst_itr, bool multiBlocks) { + // Copy remaining instructions from caller block. + for (Instruction* inst = call_inst_itr->NextNode(); inst; + inst = call_inst_itr->NextNode()) { + inst->RemoveFromList(); + std::unique_ptr cp_inst(inst); + // If multiple blocks generated, regenerate any same-block + // instruction that has not been seen in this last block. + if (multiBlocks) { + if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) { + return false; + } + + // Remember same-block ops in this block. + if (IsSameBlockOp(&*cp_inst)) { + const uint32_t rid = cp_inst->result_id(); + (*postCallSB)[rid] = rid; + } + } + new_blk_ptr->get()->AddInstruction(std::move(cp_inst)); + } + + return true; +} + +void InlinePass::MoveLoopMergeInstToFirstBlock( + std::vector>* new_blocks) { + // Move the OpLoopMerge from the last block back to the first, where + // it belongs. + auto& first = new_blocks->front(); + auto& last = new_blocks->back(); + assert(first != last); + + // Insert a modified copy of the loop merge into the first block. + auto loop_merge_itr = last->tail(); + --loop_merge_itr; + assert(loop_merge_itr->opcode() == spv::Op::OpLoopMerge); + std::unique_ptr cp_inst(loop_merge_itr->Clone(context())); + first->tail().InsertBefore(std::move(cp_inst)); + + // Remove the loop merge from the last block. + loop_merge_itr->RemoveFromList(); + delete &*loop_merge_itr; +} + +void InlinePass::UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks) { + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + + // The back-edge block is split at the branch to create a new back-edge + // block. The old block is modified to branch to the new block. The loop + // merge instruction is updated to declare the new block as the continue + // target. This has the effect of changing the loop from being a large + // continue construct and an empty loop construct to being a loop with a loop + // construct and a trivial continue construct. This change is made to satisfy + // structural dominance. + + // Add the new basic block. + std::unique_ptr new_block = + MakeUnique(NewLabel(new_id)); + auto& old_backedge = new_blocks->back(); + auto old_branch = old_backedge->tail(); + + // Move the old back edge into the new block. + std::unique_ptr br(&*old_branch); + new_block->AddInstruction(std::move(br)); + + // Add a branch to the new block from the old back-edge block. + AddBranch(new_id, &old_backedge); + new_blocks->push_back(std::move(new_block)); + + // Update the loop's continue target to the new block. + merge_inst->SetInOperand(1u, {new_id}); +} + +bool InlinePass::GenInlineCode( + std::vector>* new_blocks, + std::vector>* new_vars, + BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr) { + // Map from all ids in the callee to their equivalent id in the caller + // as callee instructions are copied into caller. + std::unordered_map callee2caller; + // Pre-call same-block insts + std::unordered_map preCallSB; + // Post-call same-block op ids + std::unordered_map postCallSB; + + analysis::DebugInlinedAtContext inlined_at_ctx(&*call_inst_itr); + + // Invalidate the def-use chains. They are not kept up to date while + // inlining. However, certain calls try to keep them up-to-date if they are + // valid. These operations can fail. + context()->InvalidateAnalyses(IRContext::kAnalysisDefUse); + + // If the caller is a loop header and the callee has multiple blocks, then the + // normal inlining logic will place the OpLoopMerge in the last of several + // blocks in the loop. Instead, it should be placed at the end of the first + // block. We'll wait to move the OpLoopMerge until the end of the regular + // inlining logic, and only if necessary. + bool caller_is_loop_header = call_block_itr->GetLoopMergeInst() != nullptr; + + // Single-trip loop continue block + std::unique_ptr single_trip_loop_cont_blk; + + Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand( + kSpvFunctionCallFunctionId)]; + + // Map parameters to actual arguments. + MapParams(calleeFn, call_inst_itr, &callee2caller); + + // Define caller local variables for all callee variables and create map to + // them. + if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) { + return false; + } + + // First block needs to use label of original block + // but map callee label in case of phi reference. + uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id(); + callee2caller[entry_blk_label_id] = call_block_itr->id(); + std::unique_ptr new_blk_ptr = + MakeUnique(NewLabel(call_block_itr->id())); + + // Move instructions of original caller block up to call instruction. + MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr, + call_block_itr); + + if (caller_is_loop_header && + (*(calleeFn->begin())).GetMergeInst() != nullptr) { + // We can't place both the caller's merge instruction and + // another merge instruction in the same block. So split the + // calling block. Insert an unconditional branch to a new guard + // block. Later, once we know the ID of the last block, we + // will move the caller's OpLoopMerge from the last generated + // block into the first block. We also wait to avoid + // invalidating various iterators. + new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller, + std::move(new_blk_ptr), entry_blk_label_id); + if (new_blk_ptr == nullptr) return false; + } + + // Create return var if needed. + const uint32_t calleeTypeId = calleeFn->type_id(); + uint32_t returnVarId = 0; + analysis::Type* calleeType = context()->get_type_mgr()->GetType(calleeTypeId); + if (calleeType->AsVoid() == nullptr) { + returnVarId = CreateReturnVar(calleeFn, new_vars); + if (returnVarId == 0) { + return false; + } + } + + calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) { + // Create set of callee result ids. Used to detect forward references + const uint32_t rid = cpi->result_id(); + if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) { + const uint32_t nid = context()->TakeNextId(); + if (nid == 0) return false; + callee2caller[rid] = nid; + } + return true; + }); + + // Inline DebugClare instructions in the callee's header. + calleeFn->ForEachDebugInstructionsInHeader( + [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) { + InlineSingleInstruction( + callee2caller, new_blk_ptr.get(), inst, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx)); + }); + + // Inline the entry block of the callee function. + if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(), + &inlined_at_ctx)) { + return false; + } + + // Inline blocks of the callee function other than the entry block. + new_blk_ptr = + InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr), + &inlined_at_ctx, calleeFn); + if (new_blk_ptr == nullptr) return false; + + new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr), + &inlined_at_ctx, calleeFn, + &*(calleeFn->tail()->tail()), returnVarId); + + // Load return value into result id of call, if it exists. + if (returnVarId != 0) { + const uint32_t resId = call_inst_itr->result_id(); + assert(resId != 0); + AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr, + call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope()); + } + + // Move instructions of original caller block after call instruction. + if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr, + call_inst_itr, + calleeFn->begin() != calleeFn->end())) + return false; + + // Finalize inline code. + new_blocks->push_back(std::move(new_blk_ptr)); + + if (caller_is_loop_header && (new_blocks->size() > 1)) { + MoveLoopMergeInstToFirstBlock(new_blocks); + + // If the loop was a single basic block previously, update it's structure. + auto& header = new_blocks->front(); + auto* merge_inst = header->GetLoopMergeInst(); + if (merge_inst->GetSingleWordInOperand(1u) == header->id()) { + auto new_id = context()->TakeNextId(); + if (new_id == 0) return false; + UpdateSingleBlockLoopContinueTarget(new_id, new_blocks); + } + } + + // Update block map given replacement blocks. + for (auto& blk : *new_blocks) { + id2block_[blk->id()] = &*blk; + } + + // We need to kill the name and decorations for the call, which will be + // deleted. + context()->KillNamesAndDecorates(&*call_inst_itr); + + return true; +} + +bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) { + if (inst->opcode() != spv::Op::OpFunctionCall) return false; + const uint32_t calleeFnId = + inst->GetSingleWordOperand(kSpvFunctionCallFunctionId); + const auto ci = inlinable_.find(calleeFnId); + if (ci == inlinable_.cend()) return false; + + if (early_return_funcs_.find(calleeFnId) != early_return_funcs_.end()) { + // We rely on the merge-return pass to handle the early return case + // in advance. + std::string message = + "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() + + "' could not be inlined because the return instruction " + "is not at the end of the function. This could be fixed by " + "running merge-return before inlining."; + consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str()); + return false; + } + + return true; +} + +void InlinePass::UpdateSucceedingPhis( + std::vector>& new_blocks) { + const auto firstBlk = new_blocks.begin(); + const auto lastBlk = new_blocks.end() - 1; + const uint32_t firstId = (*firstBlk)->id(); + const uint32_t lastId = (*lastBlk)->id(); + const BasicBlock& const_last_block = *lastBlk->get(); + const_last_block.ForEachSuccessorLabel( + [&firstId, &lastId, this](const uint32_t succ) { + BasicBlock* sbp = this->id2block_[succ]; + sbp->ForEachPhiInst([&firstId, &lastId](Instruction* phi) { + phi->ForEachInId([&firstId, &lastId](uint32_t* id) { + if (*id == firstId) *id = lastId; + }); + }); + }); +} + +bool InlinePass::HasNoReturnInLoop(Function* func) { + // If control not structured, do not do loop/return analysis + // TODO: Analyze returns in non-structured control flow + if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return false; + const auto structured_analysis = context()->GetStructuredCFGAnalysis(); + // Search for returns in structured construct. + bool return_in_loop = false; + for (auto& blk : *func) { + auto terminal_ii = blk.cend(); + --terminal_ii; + if (spvOpcodeIsReturn(terminal_ii->opcode()) && + structured_analysis->ContainingLoop(blk.id()) != 0) { + return_in_loop = true; + break; + } + } + return !return_in_loop; +} + +void InlinePass::AnalyzeReturns(Function* func) { + // Analyze functions without a return in loop. + if (HasNoReturnInLoop(func)) { + no_return_in_loop_.insert(func->result_id()); + } + // Analyze functions with a return before its tail basic block. + for (auto& blk : *func) { + auto terminal_ii = blk.cend(); + --terminal_ii; + if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) { + early_return_funcs_.insert(func->result_id()); + break; + } + } +} + +bool InlinePass::IsInlinableFunction(Function* func) { + // We can only inline a function if it has blocks. + if (func->cbegin() == func->cend()) return false; + + // Do not inline functions with DontInline flag. + if (func->control_mask() & uint32_t(spv::FunctionControlMask::DontInline)) { + return false; + } + + // Do not inline functions with returns in loops. Currently early return + // functions are inlined by wrapping them in a one trip loop and implementing + // the returns as a branch to the loop's merge block. However, this can only + // done validly if the return was not in a loop in the original function. + // Also remember functions with multiple (early) returns. + AnalyzeReturns(func); + if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) { + return false; + } + + if (func->IsRecursive()) { + return false; + } + + // Do not inline functions with an abort instruction if they are called from a + // continue construct. If it is inlined into a continue construct the backedge + // will no longer post-dominate the continue target, which is invalid. An + // `OpUnreachable` is acceptable because it will not change post-dominance if + // it is statically unreachable. + bool func_is_called_from_continue = + funcs_called_from_continue_.count(func->result_id()) != 0; + + if (func_is_called_from_continue && ContainsAbortOtherThanUnreachable(func)) { + return false; + } + + return true; +} + +bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const { + return !func->WhileEachInst([](Instruction* inst) { + return inst->opcode() == spv::Op::OpUnreachable || + !spvOpcodeIsAbort(inst->opcode()); + }); +} + +void InlinePass::InitializeInline() { + false_id_ = 0; + + // clear collections + id2function_.clear(); + id2block_.clear(); + inlinable_.clear(); + no_return_in_loop_.clear(); + early_return_funcs_.clear(); + funcs_called_from_continue_ = + context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue(); + + for (auto& fn : *get_module()) { + // Initialize function and block maps. + id2function_[fn.result_id()] = &fn; + for (auto& blk : fn) { + id2block_[blk.id()] = &blk; + } + // Compute inlinability + if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id()); + } +} + +InlinePass::InlinePass() {} + +void InlinePass::FixDebugDeclares(Function* func) { + std::map access_chains; + std::vector debug_declare_insts; + + func->ForEachInst([&access_chains, &debug_declare_insts](Instruction* inst) { + if (inst->opcode() == spv::Op::OpAccessChain) { + access_chains[inst->result_id()] = inst; + } + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + debug_declare_insts.push_back(inst); + } + }); + + for (auto& inst : debug_declare_insts) { + FixDebugDeclare(inst, access_chains); + } +} + +void InlinePass::FixDebugDeclare( + Instruction* dbg_declare_inst, + const std::map& access_chains) { + do { + uint32_t var_id = + dbg_declare_inst->GetSingleWordInOperand(kSpvDebugDeclareVarInIdx); + + // The def-use chains are not kept up to date while inlining, so we need to + // get the variable by traversing the functions. + auto it = access_chains.find(var_id); + if (it == access_chains.end()) { + return; + } + Instruction* access_chain = it->second; + + // If the variable id in the debug declare is an access chain, it is + // invalid. it needs to be fixed up. The debug declare will be updated so + // that its Var operand becomes the base of the access chain. The indexes of + // the access chain are prepended before the indexes of the debug declare. + + std::vector operands; + for (int i = 0; i < kSpvDebugDeclareVarInIdx; i++) { + operands.push_back(dbg_declare_inst->GetInOperand(i)); + } + + uint32_t access_chain_base = + access_chain->GetSingleWordInOperand(kSpvAccessChainBaseInIdx); + operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {access_chain_base})); + operands.push_back( + dbg_declare_inst->GetInOperand(kSpvDebugDeclareVarInIdx + 1)); + + for (uint32_t i = kSpvAccessChainBaseInIdx + 1; + i < access_chain->NumInOperands(); ++i) { + operands.push_back(access_chain->GetInOperand(i)); + } + + for (uint32_t i = kSpvDebugDeclareVarInIdx + 2; + i < dbg_declare_inst->NumInOperands(); ++i) { + operands.push_back(dbg_declare_inst->GetInOperand(i)); + } + + dbg_declare_inst->SetInOperands(std::move(operands)); + } while (true); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.h new file mode 100644 index 00000000..7bea31d1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/inline_pass.h @@ -0,0 +1,260 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_PASS_H_ +#define SOURCE_OPT_INLINE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/debug_info_manager.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlinePass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + virtual ~InlinePass() override = default; + + protected: + InlinePass(); + + // Add pointer to type to module and return resultId. Returns 0 if the type + // could not be created. + uint32_t AddPointerToType(uint32_t type_id, spv::StorageClass storage_class); + + // Add unconditional branch to labelId to end of block block_ptr. + void AddBranch(uint32_t labelId, std::unique_ptr* block_ptr); + + // Add conditional branch to end of block |block_ptr|. + void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id, + std::unique_ptr* block_ptr); + + // Add unconditional branch to labelId to end of block block_ptr. + void AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + std::unique_ptr* block_ptr); + + // Add store of valId to ptrId to end of block block_ptr. + void AddStore(uint32_t ptrId, uint32_t valId, + std::unique_ptr* block_ptr, + const Instruction* line_inst, const DebugScope& dbg_scope); + + // Add load of ptrId into resultId to end of block block_ptr. + void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId, + std::unique_ptr* block_ptr, + const Instruction* line_inst, const DebugScope& dbg_scope); + + // Return new label. + std::unique_ptr NewLabel(uint32_t label_id); + + // Returns the id for the boolean false value. Looks in the module first + // and creates it if not found. Remembers it for future calls. Returns 0 if + // the value could not be created. + uint32_t GetFalseId(); + + // Map callee params to caller args + void MapParams(Function* calleeFn, BasicBlock::iterator call_inst_itr, + std::unordered_map* callee2caller); + + // Clone and map callee locals. Return true if successful. + bool CloneAndMapLocals(Function* calleeFn, + std::vector>* new_vars, + std::unordered_map* callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx); + + // Create return variable for callee clone code. The return type of + // |calleeFn| must not be void. Returns the id of the return variable if + // created. Returns 0 if the return variable could not be created. + uint32_t CreateReturnVar(Function* calleeFn, + std::vector>* new_vars); + + // Return true if instruction must be in the same block that its result + // is used. + bool IsSameBlockOp(const Instruction* inst) const; + + // Clone operands which must be in same block as consumer instructions. + // Look in preCallSB for instructions that need cloning. Look in + // postCallSB for instructions already cloned. Add cloned instruction + // to postCallSB. + bool CloneSameBlockOps(std::unique_ptr* inst, + std::unordered_map* postCallSB, + std::unordered_map* preCallSB, + std::unique_ptr* block_ptr); + + // Return in new_blocks the result of inlining the call at call_inst_itr + // within its block at call_block_itr. The block at call_block_itr can + // just be replaced with the blocks in new_blocks. Any additional branches + // are avoided. Debug instructions are cloned along with their callee + // instructions. Early returns are replaced by a store to a local return + // variable and a branch to a (created) exit block where the local variable + // is returned. Formal parameters are trivially mapped to their actual + // parameters. Note that the first block in new_blocks retains the label + // of the original calling block. Also note that if an exit block is + // created, it is the last block of new_blocks. + // + // Also return in new_vars additional OpVariable instructions required by + // and to be inserted into the caller function after the block at + // call_block_itr is replaced with new_blocks. + // + // Returns true if successful. + bool GenInlineCode(std::vector>* new_blocks, + std::vector>* new_vars, + BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr); + + // Return true if |inst| is a function call that can be inlined. + bool IsInlinableFunctionCall(const Instruction* inst); + + // Return true if |func| has no return in a loop. The current analysis + // requires structured control flow, so return false if control flow not + // structured ie. module is not a shader. + bool HasNoReturnInLoop(Function* func); + + // Find all functions with multiple returns and no returns in loops + void AnalyzeReturns(Function* func); + + // Return true if |func| is a function that can be inlined. + bool IsInlinableFunction(Function* func); + + // Returns true if |func| contains an abort instruction that is not an + // `OpUnreachable` instruction. + bool ContainsAbortOtherThanUnreachable(Function* func) const; + + // Update phis in succeeding blocks to point to new last block + void UpdateSucceedingPhis( + std::vector>& new_blocks); + + // Initialize state for optimization of |module| + void InitializeInline(); + + // Fixes invalid debug declare functions in `func` that were caused by + // inlining. This function cannot be called while in the middle of inlining + // because it needs to be able to find the instructions that define an + // id. + void FixDebugDeclares(Function* func); + + // Map from function's result id to function. + std::unordered_map id2function_; + + // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt + // CFG. It has functionality not present in CFG. Consolidate. + std::unordered_map id2block_; + + // Set of ids of functions with early return. + std::set early_return_funcs_; + + // Set of ids of functions with no returns in loop + std::set no_return_in_loop_; + + // Set of ids of inlinable functions + std::set inlinable_; + + // result id for OpConstantFalse + uint32_t false_id_; + + // Set of functions that are originally called directly or indirectly from a + // continue construct. + std::unordered_set funcs_called_from_continue_; + + private: + // Moves instructions of the caller function up to the call instruction + // to |new_blk_ptr|. + void MoveInstsBeforeEntryBlock( + std::unordered_map* preCallSB, + BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr); + + // Returns a new guard block after adding a branch to the end of + // |new_blocks|. + std::unique_ptr AddGuardBlock( + std::vector>* new_blocks, + std::unordered_map* callee2caller, + std::unique_ptr new_blk_ptr, uint32_t entry_blk_label_id); + + // Add store instructions for initializers of variables. + InstructionList::iterator AddStoresForVariableInitializers( + const std::unordered_map& callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_block_itr); + + // Inlines a single instruction of the callee function. + bool InlineSingleInstruction( + const std::unordered_map& callee2caller, + BasicBlock* new_blk_ptr, const Instruction* inst, + uint32_t dbg_inlined_at); + + // Inlines the return instruction of the callee function. + std::unique_ptr InlineReturn( + const std::unordered_map& callee2caller, + std::vector>* new_blocks, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, + const Instruction* inst, uint32_t returnVarId); + + // Inlines the entry block of the callee function. + bool InlineEntryBlock( + const std::unordered_map& callee2caller, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block, + analysis::DebugInlinedAtContext* inlined_at_ctx); + + // Inlines basic blocks of the callee function other than the entry basic + // block. + std::unique_ptr InlineBasicBlocks( + std::vector>* new_blocks, + const std::unordered_map& callee2caller, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn); + + // Moves instructions of the caller function after the call instruction + // to |new_blk_ptr|. + bool MoveCallerInstsAfterFunctionCall( + std::unordered_map* preCallSB, + std::unordered_map* postCallSB, + std::unique_ptr* new_blk_ptr, + BasicBlock::iterator call_inst_itr, bool multiBlocks); + + // Move the OpLoopMerge from the last block back to the first. + void MoveLoopMergeInstToFirstBlock( + std::vector>* new_blocks); + + // Update the structure of single block loops so that the inlined code ends + // up in the loop construct and a new continue target is added to satisfy + // structural dominance. + void UpdateSingleBlockLoopContinueTarget( + uint32_t new_id, std::vector>* new_blocks); + + // Replaces the `var` operand of `dbg_declare_inst` and updates the indexes + // accordingly, if it is the id of an access chain in `access_chains`. + void FixDebugDeclare(Instruction* dbg_declare_inst, + const std::map& access_chains); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.cpp new file mode 100644 index 00000000..aa4ae26b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.cpp @@ -0,0 +1,1090 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/instruction.h" + +#include + +#include "OpenCLDebugInfo100.h" +#include "source/disassemble.h" +#include "source/opt/fold.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { +namespace { +// Indices used to get particular operands out of instructions using InOperand. +constexpr uint32_t kTypeImageDimIndex = 1; +constexpr uint32_t kLoadBaseIndex = 0; +constexpr uint32_t kPointerTypeStorageClassIndex = 0; +constexpr uint32_t kVariableStorageClassIndex = 0; +constexpr uint32_t kTypeImageSampledIndex = 5; + +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. +constexpr uint32_t kExtInstSetIdInIdx = 0; +constexpr uint32_t kExtInstInstructionInIdx = 1; +constexpr uint32_t kDebugScopeNumWords = 7; +constexpr uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6; +constexpr uint32_t kDebugNoScopeNumWords = 5; + +// Number of operands of an OpBranchConditional instruction +// with weights. +constexpr uint32_t kOpBranchConditionalWithWeightsNumOperands = 5; +} // namespace + +Instruction::Instruction(IRContext* c) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(spv::Op::OpNop), + has_type_id_(false), + has_result_id_(false), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +Instruction::Instruction(IRContext* c, spv::Op op) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(op), + has_type_id_(false), + has_result_id_(false), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + std::vector&& dbg_line) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(static_cast(inst.opcode)), + has_type_id_(inst.type_id != 0), + has_result_id_(inst.result_id != 0), + unique_id_(c->TakeNextUniqueId()), + dbg_line_insts_(std::move(dbg_line)), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { + operands_.reserve(inst.num_operands); + for (uint32_t i = 0; i < inst.num_operands; ++i) { + const auto& current_payload = inst.operands[i]; + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, + inst.words + current_payload.offset + current_payload.num_words); + } + assert((!IsLineInst() || dbg_line.empty()) && + "Op(No)Line attaching to Op(No)Line found"); +} + +Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(static_cast(inst.opcode)), + has_type_id_(inst.type_id != 0), + has_result_id_(inst.result_id != 0), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(dbg_scope) { + operands_.reserve(inst.num_operands); + for (uint32_t i = 0; i < inst.num_operands; ++i) { + const auto& current_payload = inst.operands[i]; + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, + inst.words + current_payload.offset + current_payload.num_words); + } +} + +Instruction::Instruction(IRContext* c, spv::Op op, uint32_t ty_id, + uint32_t res_id, const OperandList& in_operands) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(op), + has_type_id_(ty_id != 0), + has_result_id_(res_id != 0), + unique_id_(c->TakeNextUniqueId()), + operands_(), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { + size_t operands_size = in_operands.size(); + if (has_type_id_) { + operands_size++; + } + if (has_result_id_) { + operands_size++; + } + operands_.reserve(operands_size); + if (has_type_id_) { + operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID, + std::initializer_list{ty_id}); + } + if (has_result_id_) { + operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID, + std::initializer_list{res_id}); + } + operands_.insert(operands_.end(), in_operands.begin(), in_operands.end()); +} + +Instruction::Instruction(Instruction&& that) + : utils::IntrusiveNodeBase(), + context_(that.context_), + opcode_(that.opcode_), + has_type_id_(that.has_type_id_), + has_result_id_(that.has_result_id_), + unique_id_(that.unique_id_), + operands_(std::move(that.operands_)), + dbg_line_insts_(std::move(that.dbg_line_insts_)), + dbg_scope_(that.dbg_scope_) { + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = that.dbg_scope_; + } +} + +Instruction& Instruction::operator=(Instruction&& that) { + context_ = that.context_; + opcode_ = that.opcode_; + has_type_id_ = that.has_type_id_; + has_result_id_ = that.has_result_id_; + unique_id_ = that.unique_id_; + operands_ = std::move(that.operands_); + dbg_line_insts_ = std::move(that.dbg_line_insts_); + dbg_scope_ = that.dbg_scope_; + return *this; +} + +Instruction* Instruction::Clone(IRContext* c) const { + Instruction* clone = new Instruction(c); + clone->opcode_ = opcode_; + clone->has_type_id_ = has_type_id_; + clone->has_result_id_ = has_result_id_; + clone->unique_id_ = c->TakeNextUniqueId(); + clone->operands_ = operands_; + clone->dbg_line_insts_ = dbg_line_insts_; + for (auto& i : clone->dbg_line_insts_) { + i.unique_id_ = c->TakeNextUniqueId(); + if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId()); + } + clone->dbg_scope_ = dbg_scope_; + return clone; +} + +uint32_t Instruction::GetSingleWordOperand(uint32_t index) const { + const auto& words = GetOperand(index).words; + assert(words.size() == 1 && "expected the operand only taking one word"); + return words.front(); +} + +uint32_t Instruction::NumInOperandWords() const { + uint32_t size = 0; + for (uint32_t i = TypeResultIdCount(); i < operands_.size(); ++i) + size += static_cast(operands_[i].words.size()); + return size; +} + +bool Instruction::HasBranchWeights() const { + if (opcode_ == spv::Op::OpBranchConditional && + NumOperands() == kOpBranchConditionalWithWeightsNumOperands) { + return true; + } + + return false; +} + +void Instruction::ToBinaryWithoutAttachedDebugInsts( + std::vector* binary) const { + const uint32_t num_words = 1 + NumOperandWords(); + binary->push_back((num_words << 16) | static_cast(opcode_)); + for (const auto& operand : operands_) { + binary->insert(binary->end(), operand.words.begin(), operand.words.end()); + } +} + +void Instruction::ReplaceOperands(const OperandList& new_operands) { + operands_.clear(); + operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end()); +} + +bool Instruction::IsReadOnlyLoad() const { + if (IsLoad()) { + Instruction* address_def = GetBaseAddress(); + if (!address_def) { + return false; + } + + if (address_def->opcode() == spv::Op::OpVariable) { + if (address_def->IsReadOnlyPointer()) { + return true; + } + } + + if (address_def->opcode() == spv::Op::OpLoad) { + const analysis::Type* address_type = + context()->get_type_mgr()->GetType(address_def->type_id()); + if (address_type->AsSampledImage() != nullptr) { + const auto* image_type = + address_type->AsSampledImage()->image_type()->AsImage(); + if (image_type->sampled() == 1) { + return true; + } + } + } + } + return false; +} + +Instruction* Instruction::GetBaseAddress() const { + uint32_t base = GetSingleWordInOperand(kLoadBaseIndex); + Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base); + bool done = false; + while (!done) { + switch (base_inst->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpCopyObject: + // All of these instructions have the base pointer use a base pointer + // in in-operand 0. + base = base_inst->GetSingleWordInOperand(0); + base_inst = context()->get_def_use_mgr()->GetDef(base); + break; + default: + done = true; + break; + } + } + return base_inst; +} + +bool Instruction::IsReadOnlyPointer() const { + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) + return IsReadOnlyPointerShaders(); + else + return IsReadOnlyPointerKernel(); +} + +bool Instruction::IsVulkanStorageImage() const { + if (opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + if (storage_class != spv::StorageClass::UniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != spv::Op::OpTypeImage) { + return false; + } + + if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) == + spv::Dim::Buffer) { + return false; + } + + // Check if the image is sampled. If we do not know for sure that it is, + // then assume it is a storage image. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1; +} + +bool Instruction::IsVulkanSampledImage() const { + if (opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + if (storage_class != spv::StorageClass::UniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != spv::Op::OpTypeImage) { + return false; + } + + if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) == + spv::Dim::Buffer) { + return false; + } + + // Check if the image is sampled. If we know for sure that it is, + // then return true. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) == 1; +} + +bool Instruction::IsVulkanStorageTexelBuffer() const { + if (opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + if (storage_class != spv::StorageClass::UniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != spv::Op::OpTypeImage) { + return false; + } + + if (spv::Dim(base_type->GetSingleWordInOperand(kTypeImageDimIndex)) != + spv::Dim::Buffer) { + return false; + } + + // Check if the image is sampled. If we do not know for sure that it is, + // then assume it is a storage texel buffer. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1; +} + +bool Instruction::IsVulkanStorageBuffer() const { + // Is there a difference between a "Storage buffer" and a "dynamic storage + // buffer" in SPIR-V and do we care about the difference? + if (opcode() != spv::Op::OpTypePointer) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != spv::Op::OpTypeStruct) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + if (storage_class == spv::StorageClass::Uniform) { + bool is_buffer_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), uint32_t(spv::Decoration::BufferBlock), + [&is_buffer_block](const Instruction&) { is_buffer_block = true; }); + return is_buffer_block; + } else if (storage_class == spv::StorageClass::StorageBuffer) { + bool is_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), uint32_t(spv::Decoration::Block), + [&is_block](const Instruction&) { is_block = true; }); + return is_block; + } + return false; +} + +bool Instruction::IsVulkanStorageBufferVariable() const { + if (opcode() != spv::Op::OpVariable) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kVariableStorageClassIndex)); + if (storage_class == spv::StorageClass::StorageBuffer || + storage_class == spv::StorageClass::Uniform) { + Instruction* var_type = context()->get_def_use_mgr()->GetDef(type_id()); + return var_type != nullptr && var_type->IsVulkanStorageBuffer(); + } + + return false; +} + +bool Instruction::IsVulkanUniformBuffer() const { + if (opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = + spv::StorageClass(GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + if (storage_class != spv::StorageClass::Uniform) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != spv::Op::OpTypeStruct) { + return false; + } + + bool is_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), uint32_t(spv::Decoration::Block), + [&is_block](const Instruction&) { is_block = true; }); + return is_block; +} + +bool Instruction::IsReadOnlyPointerShaders() const { + if (type_id() == 0) { + return false; + } + + Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id()); + if (type_def->opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = spv::StorageClass( + type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + + switch (storage_class) { + case spv::StorageClass::UniformConstant: + if (!type_def->IsVulkanStorageImage() && + !type_def->IsVulkanStorageTexelBuffer()) { + return true; + } + break; + case spv::StorageClass::Uniform: + if (!type_def->IsVulkanStorageBuffer()) { + return true; + } + break; + case spv::StorageClass::PushConstant: + case spv::StorageClass::Input: + return true; + default: + break; + } + + bool is_nonwritable = false; + context()->get_decoration_mgr()->ForEachDecoration( + result_id(), uint32_t(spv::Decoration::NonWritable), + [&is_nonwritable](const Instruction&) { is_nonwritable = true; }); + return is_nonwritable; +} + +bool Instruction::IsReadOnlyPointerKernel() const { + if (type_id() == 0) { + return false; + } + + Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id()); + if (type_def->opcode() != spv::Op::OpTypePointer) { + return false; + } + + spv::StorageClass storage_class = spv::StorageClass( + type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex)); + + return storage_class == spv::StorageClass::UniformConstant; +} + +void Instruction::UpdateLexicalScope(uint32_t scope) { + dbg_scope_.SetLexicalScope(scope); + for (auto& i : dbg_line_insts_) { + i.dbg_scope_.SetLexicalScope(scope); + } + if (!IsLineInst() && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) { + dbg_scope_.SetInlinedAt(new_inlined_at); + for (auto& i : dbg_line_insts_) { + i.dbg_scope_.SetInlinedAt(new_inlined_at); + } + if (!IsLineInst() && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +void Instruction::ClearDbgLineInsts() { + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + auto def_use_mgr = context()->get_def_use_mgr(); + for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst); + } + clear_dbg_line_insts(); +} + +void Instruction::UpdateDebugInfoFrom(const Instruction* from) { + if (from == nullptr) return; + ClearDbgLineInsts(); + if (!from->dbg_line_insts().empty()) + AddDebugLine(&from->dbg_line_insts().back()); + SetDebugScope(from->GetDebugScope()); + if (!IsLineInst() && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +void Instruction::AddDebugLine(const Instruction* inst) { + dbg_line_insts_.push_back(*inst); + dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId(); + if (inst->IsDebugLineInst()) + dbg_line_insts_.back().SetResultId(context_->TakeNextId()); + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back()); +} + +bool Instruction::IsDebugLineInst() const { + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) || + (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine)); +} + +bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); } + +bool Instruction::IsLine() const { + if (opcode() == spv::Op::OpLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugLine; +} + +bool Instruction::IsNoLine() const { + if (opcode() == spv::Op::OpNoLine) return true; + NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode(); + return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine; +} + +Instruction* Instruction::InsertBefore(std::unique_ptr&& inst) { + inst.get()->InsertBefore(this); + return inst.release(); +} + +Instruction* Instruction::InsertBefore( + std::vector>&& list) { + Instruction* first_node = list.front().get(); + for (auto& inst : list) { + inst.release()->InsertBefore(this); + } + list.clear(); + return first_node; +} + +bool Instruction::IsValidBasePointer() const { + uint32_t tid = type_id(); + if (tid == 0) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(tid); + if (type->opcode() != spv::Op::OpTypePointer) { + return false; + } + + auto feature_mgr = context()->get_feature_mgr(); + if (feature_mgr->HasCapability(spv::Capability::Addresses)) { + // TODO: The rules here could be more restrictive. + return true; + } + + if (opcode() == spv::Op::OpVariable || + opcode() == spv::Op::OpFunctionParameter) { + return true; + } + + // With variable pointers, there are more valid base pointer objects. + // Variable pointers implicitly declares Variable pointers storage buffer. + spv::StorageClass storage_class = + static_cast(type->GetSingleWordInOperand(0)); + if ((feature_mgr->HasCapability( + spv::Capability::VariablePointersStorageBuffer) && + storage_class == spv::StorageClass::StorageBuffer) || + (feature_mgr->HasCapability(spv::Capability::VariablePointers) && + storage_class == spv::StorageClass::Workgroup)) { + switch (opcode()) { + case spv::Op::OpPhi: + case spv::Op::OpSelect: + case spv::Op::OpFunctionCall: + case spv::Op::OpConstantNull: + return true; + default: + break; + } + } + + uint32_t pointee_type_id = type->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context()->get_def_use_mgr()->GetDef(pointee_type_id); + + if (pointee_type_inst->IsOpaqueType()) { + return true; + } + return false; +} + +OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { + if (opcode() != spv::Op::OpExtInst) { + return OpenCLDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + return OpenCLDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + return OpenCLDebugInfo100InstructionsMax; + } + + return OpenCLDebugInfo100Instructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + +NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode() + const { + if (opcode() != spv::Op::OpExtInst) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx); + if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) { + return NonSemanticShaderDebugInfo100InstructionsMax; + } + + return NonSemanticShaderDebugInfo100Instructions(opcode); +} + +CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const { + if (opcode() != spv::Op::OpExtInst) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t opencl_set_id = + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo(); + const uint32_t shader_set_id = + context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo(); + + if (!opencl_set_id && !shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx); + + if (used_set_id != opencl_set_id && used_set_id != shader_set_id) { + return CommonDebugInfoInstructionsMax; + } + + return CommonDebugInfoInstructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + +bool Instruction::IsValidBaseImage() const { + uint32_t tid = type_id(); + if (tid == 0) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(tid); + return (type->opcode() == spv::Op::OpTypeImage || + type->opcode() == spv::Op::OpTypeSampledImage); +} + +bool Instruction::IsOpaqueType() const { + if (opcode() == spv::Op::OpTypeStruct) { + bool is_opaque = false; + ForEachInOperand([&is_opaque, this](const uint32_t* op_id) { + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id); + is_opaque |= type_inst->IsOpaqueType(); + }); + return is_opaque; + } else if (opcode() == spv::Op::OpTypeArray) { + uint32_t sub_type_id = GetSingleWordInOperand(0); + Instruction* sub_type_inst = + context()->get_def_use_mgr()->GetDef(sub_type_id); + return sub_type_inst->IsOpaqueType(); + } else { + return opcode() == spv::Op::OpTypeRuntimeArray || + spvOpcodeIsBaseOpaqueType(opcode()); + } +} + +bool Instruction::IsFoldable() const { + return IsFoldableByFoldScalar() || IsFoldableByFoldVector() || + context()->get_instruction_folder().HasConstFoldingRule(this); +} + +bool Instruction::IsFoldableByFoldScalar() const { + const InstructionFolder& folder = context()->get_instruction_folder(); + if (!folder.IsFoldableOpcode(opcode())) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); + if (!folder.IsFoldableScalarType(type)) { + return false; + } + + // Even if the type of the instruction is foldable, its operands may not be + // foldable (e.g., comparisons of 64bit types). Check that all operand types + // are foldable before accepting the instruction. + return WhileEachInOperand([&folder, this](const uint32_t* op_id) { + Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id); + Instruction* def_inst_type = + context()->get_def_use_mgr()->GetDef(def_inst->type_id()); + return folder.IsFoldableScalarType(def_inst_type); + }); +} + +bool Instruction::IsFoldableByFoldVector() const { + const InstructionFolder& folder = context()->get_instruction_folder(); + if (!folder.IsFoldableOpcode(opcode())) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); + if (!folder.IsFoldableVectorType(type)) { + return false; + } + + // Even if the type of the instruction is foldable, its operands may not be + // foldable (e.g., comparisons of 64bit types). Check that all operand types + // are foldable before accepting the instruction. + return WhileEachInOperand([&folder, this](const uint32_t* op_id) { + Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id); + Instruction* def_inst_type = + context()->get_def_use_mgr()->GetDef(def_inst->type_id()); + return folder.IsFoldableVectorType(def_inst_type); + }); +} + +bool Instruction::IsFloatingPointFoldingAllowed() const { + // TODO: Add the rules for kernels. For now it will be pessimistic. + // For now, do not support capabilities introduced by SPV_KHR_float_controls. + if (!context_->get_feature_mgr()->HasCapability(spv::Capability::Shader) || + context_->get_feature_mgr()->HasCapability( + spv::Capability::DenormPreserve) || + context_->get_feature_mgr()->HasCapability( + spv::Capability::DenormFlushToZero) || + context_->get_feature_mgr()->HasCapability( + spv::Capability::SignedZeroInfNanPreserve) || + context_->get_feature_mgr()->HasCapability( + spv::Capability::RoundingModeRTZ) || + context_->get_feature_mgr()->HasCapability( + spv::Capability::RoundingModeRTE)) { + return false; + } + + bool is_nocontract = false; + context_->get_decoration_mgr()->WhileEachDecoration( + result_id(), uint32_t(spv::Decoration::NoContraction), + [&is_nocontract](const Instruction&) { + is_nocontract = true; + return false; + }); + return !is_nocontract; +} + +std::string Instruction::PrettyPrint(uint32_t options) const { + // Convert the module to binary. + std::vector module_binary; + context()->module()->ToBinary(&module_binary, /* skip_nop = */ false); + + // Convert the instruction to binary. This is used to identify the correct + // stream of words to output from the module. + std::vector inst_binary; + ToBinaryWithoutAttachedDebugInsts(&inst_binary); + + // Do not generate a header. + return spvInstructionBinaryToText( + context()->grammar().target_env(), inst_binary.data(), inst_binary.size(), + module_binary.data(), module_binary.size(), + options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); +} + +std::ostream& operator<<(std::ostream& str, const Instruction& inst) { + str << inst.PrettyPrint(); + return str; +} + +void Instruction::Dump() const { + std::cerr << "Instruction #" << unique_id() << "\n" << *this << "\n"; +} + +bool Instruction::IsOpcodeCodeMotionSafe() const { + switch (opcode_) { + case spv::Op::OpNop: + case spv::Op::OpUndef: + case spv::Op::OpLoad: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpArrayLength: + case spv::Op::OpVectorExtractDynamic: + case spv::Op::OpVectorInsertDynamic: + case spv::Op::OpVectorShuffle: + case spv::Op::OpCompositeConstruct: + case spv::Op::OpCompositeExtract: + case spv::Op::OpCompositeInsert: + case spv::Op::OpCopyObject: + case spv::Op::OpTranspose: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + case spv::Op::OpFConvert: + case spv::Op::OpQuantizeToF16: + case spv::Op::OpBitcast: + case spv::Op::OpSNegate: + case spv::Op::OpFNegate: + case spv::Op::OpIAdd: + case spv::Op::OpFAdd: + case spv::Op::OpISub: + case spv::Op::OpFSub: + case spv::Op::OpIMul: + case spv::Op::OpFMul: + case spv::Op::OpUDiv: + case spv::Op::OpSDiv: + case spv::Op::OpFDiv: + case spv::Op::OpUMod: + case spv::Op::OpSRem: + case spv::Op::OpSMod: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpVectorTimesScalar: + case spv::Op::OpMatrixTimesScalar: + case spv::Op::OpVectorTimesMatrix: + case spv::Op::OpMatrixTimesVector: + case spv::Op::OpMatrixTimesMatrix: + case spv::Op::OpOuterProduct: + case spv::Op::OpDot: + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: + case spv::Op::OpAny: + case spv::Op::OpAll: + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: + case spv::Op::OpLogicalNot: + case spv::Op::OpSelect: + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: + case spv::Op::OpBitFieldInsert: + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: + case spv::Op::OpBitReverse: + case spv::Op::OpBitCount: + case spv::Op::OpSizeOf: + return true; + default: + return false; + } +} + +bool Instruction::IsScalarizable() const { + if (spvOpcodeIsScalarizable(opcode())) { + return true; + } + + if (opcode() == spv::Op::OpExtInst) { + uint32_t instSetId = + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId) { + switch (GetSingleWordInOperand(kExtInstInstructionInIdx)) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450Trunc: + case GLSLstd450FAbs: + case GLSLstd450SAbs: + case GLSLstd450FSign: + case GLSLstd450SSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Atan2: + case GLSLstd450Pow: + case GLSLstd450Exp: + case GLSLstd450Log: + case GLSLstd450Exp2: + case GLSLstd450Log2: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450Modf: + case GLSLstd450FMin: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450FMax: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450FClamp: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450Frexp: + case GLSLstd450Ldexp: + case GLSLstd450FindILsb: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: + return true; + default: + return false; + } + } + } + return false; +} + +bool Instruction::IsOpcodeSafeToDelete() const { + if (context()->IsCombinatorInstruction(this)) { + return true; + } + + switch (opcode()) { + case spv::Op::OpDPdx: + case spv::Op::OpDPdy: + case spv::Op::OpFwidth: + case spv::Op::OpDPdxFine: + case spv::Op::OpDPdyFine: + case spv::Op::OpFwidthFine: + case spv::Op::OpDPdxCoarse: + case spv::Op::OpDPdyCoarse: + case spv::Op::OpFwidthCoarse: + case spv::Op::OpImageQueryLod: + return true; + default: + return false; + } +} + +bool Instruction::IsNonSemanticInstruction() const { + if (!HasResultId()) return false; + if (opcode() != spv::Op::OpExtInst) return false; + + auto import_inst = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0)); + std::string import_name = import_inst->GetInOperand(0).AsString(); + return import_name.find("NonSemantic.") == 0; +} + +void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id, + uint32_t ext_set, + std::vector* binary) const { + uint32_t num_words = kDebugScopeNumWords; + CommonDebugInfoInstructions dbg_opcode = CommonDebugInfoDebugScope; + if (GetLexicalScope() == kNoDebugScope) { + num_words = kDebugNoScopeNumWords; + dbg_opcode = CommonDebugInfoDebugNoScope; + } else if (GetInlinedAt() == kNoInlinedAt) { + num_words = kDebugScopeNumWordsWithoutInlinedAt; + } + std::vector operands = { + (num_words << 16) | static_cast(spv::Op::OpExtInst), + type_id, + result_id, + ext_set, + static_cast(dbg_opcode), + }; + binary->insert(binary->end(), operands.begin(), operands.end()); + if (GetLexicalScope() != kNoDebugScope) { + binary->push_back(GetLexicalScope()); + if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt()); + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.h b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.h new file mode 100644 index 00000000..c2617fba --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction.h @@ -0,0 +1,924 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INSTRUCTION_H_ +#define SOURCE_OPT_INSTRUCTION_H_ + +#include +#include +#include +#include +#include +#include + +#include "NonSemanticShaderDebugInfo100.h" +#include "OpenCLDebugInfo100.h" +#include "source/binary.h" +#include "source/common_debug_info.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_spirv_header.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/opt/reflect.h" +#include "source/util/ilist_node.h" +#include "source/util/small_vector.h" +#include "source/util/string_utils.h" +#include "spirv-tools/libspirv.h" + +constexpr uint32_t kNoDebugScope = 0; +constexpr uint32_t kNoInlinedAt = 0; + +namespace spvtools { +namespace opt { + +class Function; +class IRContext; +class Module; +class InstructionList; + +// Relaxed logical addressing: +// +// In the logical addressing model, pointers cannot be stored or loaded. This +// is a useful assumption because it simplifies the aliasing significantly. +// However, for the purpose of legalizing code generated from HLSL, we will have +// to allow storing and loading of pointers to opaque objects and runtime +// arrays. This relaxation of the rule still implies that function and private +// scope variables do not have any aliasing, so we can treat them as before. +// This will be call the relaxed logical addressing model. +// +// This relaxation of the rule will be allowed by |GetBaseAddress|, but it will +// enforce that no other pointers are stored or loaded. + +// About operand: +// +// In the SPIR-V specification, the term "operand" is used to mean any single +// SPIR-V word following the leading wordcount-opcode word. Here, the term +// "operand" is used to mean a *logical* operand. A logical operand may consist +// of multiple SPIR-V words, which together make up the same component. For +// example, a logical operand of a 64-bit integer needs two words to express. +// +// Further, we categorize logical operands into *in* and *out* operands. +// In operands are operands actually serve as input to operations, while out +// operands are operands that represent ids generated from operations (result +// type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2", +// "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out +// operands. + +// A *logical* operand to a SPIR-V instruction. It can be the type id, result +// id, or other additional operands carried in an instruction. +struct Operand { + using OperandData = utils::SmallVector; + Operand(spv_operand_type_t t, OperandData&& w) + : type(t), words(std::move(w)) {} + + Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {} + + template + Operand(spv_operand_type_t t, InputIt firstOperandData, + InputIt lastOperandData) + : type(t), words(firstOperandData, lastOperandData) {} + + spv_operand_type_t type; // Type of this logical operand. + OperandData words; // Binary segments of this logical operand. + + uint32_t AsId() const { + assert(spvIsIdType(type)); + assert(words.size() == 1); + return words[0]; + } + + // Returns a string operand as a std::string. + std::string AsString() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); + return spvtools::utils::MakeString(words); + } + + // Returns a literal integer operand as a uint64_t + uint64_t AsLiteralUint64() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER || + type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER); + assert(1 <= words.size()); + assert(words.size() <= 2); + uint64_t result = 0; + if (words.size() > 0) { // Needed to avoid maybe-uninitialized GCC warning + uint32_t low = words[0]; + result = uint64_t(low); + } + if (words.size() > 1) { + uint32_t high = words[1]; + result = result | (uint64_t(high) << 32); + } + return result; + } + + friend bool operator==(const Operand& o1, const Operand& o2) { + return o1.type == o2.type && o1.words == o2.words; + } + + // TODO(antiagainst): create fields for literal number kind, width, etc. +}; + +inline bool operator!=(const Operand& o1, const Operand& o2) { + return !(o1 == o2); +} + +// This structure is used to represent a DebugScope instruction from +// the OpenCL.100.DebugInfo extended instruction set. Note that we can +// ignore the result id of DebugScope instruction because it is not +// used for anything. We do not keep it to reduce the size of +// structure. +// TODO: Let validator check that the result id is not used anywhere. +class DebugScope { + public: + DebugScope(uint32_t lexical_scope, uint32_t inlined_at) + : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {} + + inline bool operator!=(const DebugScope& d) const { + return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_; + } + + // Accessor functions for |lexical_scope_|. + uint32_t GetLexicalScope() const { return lexical_scope_; } + void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; } + + // Accessor functions for |inlined_at_|. + uint32_t GetInlinedAt() const { return inlined_at_; } + void SetInlinedAt(uint32_t at) { inlined_at_ = at; } + + // Pushes the binary segments for this DebugScope instruction into + // the back of *|binary|. + void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, + std::vector* binary) const; + + private: + // The result id of the lexical scope in which this debug scope is + // contained. The value is kNoDebugScope if there is no scope. + uint32_t lexical_scope_; + + // The result id of DebugInlinedAt if instruction in this debug scope + // is inlined. The value is kNoInlinedAt if it is not inlined. + uint32_t inlined_at_; +}; + +// A SPIR-V instruction. It contains the opcode and any additional logical +// operand, including the result id (if any) and result type id (if any). It +// may also contain line-related debug instruction (OpLine, OpNoLine) directly +// appearing before this instruction. Note that the result id of an instruction +// should never change after the instruction being built. If the result id +// needs to change, the user should create a new instruction instead. +class Instruction : public utils::IntrusiveNodeBase { + public: + using OperandList = std::vector; + using iterator = OperandList::iterator; + using const_iterator = OperandList::const_iterator; + + // Creates a default OpNop instruction. + // This exists solely for containers that can't do without. Should be removed. + Instruction() + : utils::IntrusiveNodeBase(), + context_(nullptr), + opcode_(spv::Op::OpNop), + has_type_id_(false), + has_result_id_(false), + unique_id_(0), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + + // Creates a default OpNop instruction. + Instruction(IRContext*); + // Creates an instruction with the given opcode |op| and no additional logical + // operands. + Instruction(IRContext*, spv::Op); + // Creates an instruction using the given spv_parsed_instruction_t |inst|. All + // the data inside |inst| will be copied and owned in this instance. And keep + // record of line-related debug instructions |dbg_line| ahead of this + // instruction, if any. + Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + std::vector&& dbg_line = {}); + + Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope); + + // Creates an instruction with the given opcode |op|, type id: |ty_id|, + // result id: |res_id| and input operands: |in_operands|. + Instruction(IRContext* c, spv::Op op, uint32_t ty_id, uint32_t res_id, + const OperandList& in_operands); + + // TODO: I will want to remove these, but will first have to remove the use of + // std::vector. + Instruction(const Instruction&) = default; + Instruction& operator=(const Instruction&) = default; + + Instruction(Instruction&&); + Instruction& operator=(Instruction&&); + + ~Instruction() override = default; + + // Returns a newly allocated instruction that has the same operands, result, + // and type as |this|. The new instruction is not linked into any list. + // It is the responsibility of the caller to make sure that the storage is + // removed. It is the caller's responsibility to make sure that there is only + // one instruction for each result id. + Instruction* Clone(IRContext* c) const; + + IRContext* context() const { return context_; } + + spv::Op opcode() const { return opcode_; } + // Sets the opcode of this instruction to a specific opcode. Note this may + // invalidate the instruction. + // TODO(qining): Remove this function when instruction building and insertion + // is well implemented. + void SetOpcode(spv::Op op) { opcode_ = op; } + uint32_t type_id() const { + return has_type_id_ ? GetSingleWordOperand(0) : 0; + } + uint32_t result_id() const { + return has_result_id_ ? GetSingleWordOperand(has_type_id_ ? 1 : 0) : 0; + } + uint32_t unique_id() const { + assert(unique_id_ != 0); + return unique_id_; + } + // Returns the vector of line-related debug instructions attached to this + // instruction and the caller can directly modify them. + std::vector& dbg_line_insts() { return dbg_line_insts_; } + const std::vector& dbg_line_insts() const { + return dbg_line_insts_; + } + + const Instruction* dbg_line_inst() const { + return dbg_line_insts_.empty() ? nullptr : &dbg_line_insts_[0]; + } + + // Clear line-related debug instructions attached to this instruction. + void clear_dbg_line_insts() { dbg_line_insts_.clear(); } + + // Same semantics as in the base class except the list the InstructionList + // containing |pos| will now assume ownership of |this|. + // inline void MoveBefore(Instruction* pos); + // inline void InsertAfter(Instruction* pos); + + // Begin and end iterators for operands. + iterator begin() { return operands_.begin(); } + iterator end() { return operands_.end(); } + const_iterator begin() const { return operands_.cbegin(); } + const_iterator end() const { return operands_.cend(); } + // Const begin and end iterators for operands. + const_iterator cbegin() const { return operands_.cbegin(); } + const_iterator cend() const { return operands_.cend(); } + + // Gets the number of logical operands. + uint32_t NumOperands() const { + return static_cast(operands_.size()); + } + // Gets the number of SPIR-V words occupied by all logical operands. + uint32_t NumOperandWords() const { + return NumInOperandWords() + TypeResultIdCount(); + } + // Gets the |index|-th logical operand. + inline Operand& GetOperand(uint32_t index); + inline const Operand& GetOperand(uint32_t index) const; + // Adds |operand| to the list of operands of this instruction. + // It is the responsibility of the caller to make sure + // that the instruction remains valid. + inline void AddOperand(Operand&& operand); + // Adds a copy of |operand| to the list of operands of this instruction. + inline void AddOperand(const Operand& operand); + // Gets the |index|-th logical operand as a single SPIR-V word. This method is + // not expected to be used with logical operands consisting of multiple SPIR-V + // words. + uint32_t GetSingleWordOperand(uint32_t index) const; + // Sets the |index|-th in-operand's data to the given |data|. + inline void SetInOperand(uint32_t index, Operand::OperandData&& data); + // Sets the |index|-th operand's data to the given |data|. + // This is for in-operands modification only, but with |index| expressed in + // terms of operand index rather than in-operand index. + inline void SetOperand(uint32_t index, Operand::OperandData&& data); + // Replace all of the in operands with those in |new_operands|. + inline void SetInOperands(OperandList&& new_operands); + // Sets the result type id. + inline void SetResultType(uint32_t ty_id); + inline bool HasResultType() const { return has_type_id_; } + // Sets the result id + inline void SetResultId(uint32_t res_id); + inline bool HasResultId() const { return has_result_id_; } + // Sets DebugScope. + inline void SetDebugScope(const DebugScope& scope); + inline const DebugScope& GetDebugScope() const { return dbg_scope_; } + // Add debug line inst. Renew result id if Debug[No]Line + void AddDebugLine(const Instruction* inst); + // Updates DebugInlinedAt of DebugScope and OpLine. + void UpdateDebugInlinedAt(uint32_t new_inlined_at); + // Clear line-related debug instructions attached to this instruction + // along with def-use entries. + void ClearDbgLineInsts(); + // Return true if Shader100:Debug[No]Line + bool IsDebugLineInst() const; + // Return true if Op[No]Line or Shader100:Debug[No]Line + bool IsLineInst() const; + // Return true if OpLine or Shader100:DebugLine + bool IsLine() const; + // Return true if OpNoLine or Shader100:DebugNoLine + bool IsNoLine() const; + inline uint32_t GetDebugInlinedAt() const { + return dbg_scope_.GetInlinedAt(); + } + // Updates lexical scope of DebugScope and OpLine. + void UpdateLexicalScope(uint32_t scope); + // Updates OpLine and DebugScope based on the information of |from|. + void UpdateDebugInfoFrom(const Instruction* from); + // Remove the |index|-th operand + void RemoveOperand(uint32_t index) { + operands_.erase(operands_.begin() + index); + } + // Insert an operand before the |index|-th operand + void InsertOperand(uint32_t index, Operand&& operand) { + operands_.insert(operands_.begin() + index, operand); + } + + // The following methods are similar to the above, but are for in operands. + uint32_t NumInOperands() const { + return static_cast(operands_.size() - TypeResultIdCount()); + } + uint32_t NumInOperandWords() const; + Operand& GetInOperand(uint32_t index) { + return GetOperand(index + TypeResultIdCount()); + } + const Operand& GetInOperand(uint32_t index) const { + return GetOperand(index + TypeResultIdCount()); + } + uint32_t GetSingleWordInOperand(uint32_t index) const { + return GetSingleWordOperand(index + TypeResultIdCount()); + } + void RemoveInOperand(uint32_t index) { + operands_.erase(operands_.begin() + index + TypeResultIdCount()); + } + + // Returns true if this instruction is OpNop. + inline bool IsNop() const; + // Turns this instruction to OpNop. This does not clear out all preceding + // line-related debug instructions. + inline void ToNop(); + + // Runs the given function |f| on this instruction and optionally on the + // preceding debug line instructions. The function will always be run + // if this is itself a debug line instruction. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on this instruction and optionally on the + // preceding debug line instructions. The function will always be run + // if this is itself a debug line instruction. If |f| returns false, + // iteration is terminated and this function returns false. + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on all operand ids. + // + // |f| should not transform an ID into 0, as 0 is an invalid ID. + inline void ForEachId(const std::function& f); + inline void ForEachId(const std::function& f) const; + + // Runs the given function |f| on all "in" operand ids. + inline void ForEachInId(const std::function& f); + inline void ForEachInId(const std::function& f) const; + + // Runs the given function |f| on all "in" operand ids. If |f| returns false, + // iteration is terminated and this function returns false. + inline bool WhileEachInId(const std::function& f); + inline bool WhileEachInId( + const std::function& f) const; + + // Runs the given function |f| on all "in" operands. + inline void ForEachInOperand(const std::function& f); + inline void ForEachInOperand( + const std::function& f) const; + + // Runs the given function |f| on all "in" operands. If |f| returns false, + // iteration is terminated and this function return false. + inline bool WhileEachInOperand(const std::function& f); + inline bool WhileEachInOperand( + const std::function& f) const; + + // Returns true if it's an OpBranchConditional instruction + // with branch weights. + bool HasBranchWeights() const; + + // Returns true if any operands can be labels + inline bool HasLabels() const; + + // Pushes the binary segments for this instruction into the back of *|binary|. + void ToBinaryWithoutAttachedDebugInsts(std::vector* binary) const; + + // Replaces the operands to the instruction with |new_operands|. The caller + // is responsible for building a complete and valid list of operands for + // this instruction. + void ReplaceOperands(const OperandList& new_operands); + + // Returns true if the instruction annotates an id with a decoration. + inline bool IsDecoration() const; + + // Returns true if the instruction is known to be a load from read-only + // memory. + bool IsReadOnlyLoad() const; + + // Returns the instruction that gives the base address of an address + // calculation. The instruction must be a load, as defined by |IsLoad|, + // store, copy, or access chain instruction. In logical addressing mode, will + // return an OpVariable or OpFunctionParameter instruction. For relaxed + // logical addressing, it would also return a load of a pointer to an opaque + // object. For physical addressing mode, could return other types of + // instructions. + Instruction* GetBaseAddress() const; + + // Returns true if the instruction loads from memory or samples an image, and + // stores the result into an id. It considers only core instructions. + // Memory-to-memory instructions are not considered loads. + inline bool IsLoad() const; + + // Returns true if the instruction generates a pointer that is definitely + // read-only. This is determined by analysing the pointer type's storage + // class and decorations that target the pointer's id. It does not analyse + // other instructions that the pointer may be derived from. Thus if 'true' is + // returned, the pointer is definitely read-only, while if 'false' is returned + // it is possible that the pointer may actually be read-only if it is derived + // from another pointer that is decorated as read-only. + bool IsReadOnlyPointer() const; + + // The following functions check for the various descriptor types defined in + // the Vulkan specification section 13.1. + + // Returns true if the instruction defines a pointer type that points to a + // storage image. + bool IsVulkanStorageImage() const; + + // Returns true if the instruction defines a pointer type that points to a + // sampled image. + bool IsVulkanSampledImage() const; + + // Returns true if the instruction defines a pointer type that points to a + // storage texel buffer. + bool IsVulkanStorageTexelBuffer() const; + + // Returns true if the instruction defines a pointer type that points to a + // storage buffer. + bool IsVulkanStorageBuffer() const; + + // Returns true if the instruction defines a variable in StorageBuffer or + // Uniform storage class with a pointer type that points to a storage buffer. + bool IsVulkanStorageBufferVariable() const; + + // Returns true if the instruction defines a pointer type that points to a + // uniform buffer. + bool IsVulkanUniformBuffer() const; + + // Returns true if the instruction is an atom operation that uses original + // value. + inline bool IsAtomicWithLoad() const; + + // Returns true if the instruction is an atom operation. + inline bool IsAtomicOp() const; + + // Returns true if this instruction is a branch or switch instruction (either + // conditional or not). + bool IsBranch() const { return spvOpcodeIsBranch(opcode()); } + + // Returns true if this instruction causes the function to finish execution + // and return to its caller + bool IsReturn() const { return spvOpcodeIsReturn(opcode()); } + + // Returns true if this instruction exits this function or aborts execution. + bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); } + + // Returns true if this instruction is a basic block terminator. + bool IsBlockTerminator() const { + return spvOpcodeIsBlockTerminator(opcode()); + } + + // Returns true if |this| is an instruction that define an opaque type. Since + // runtime array have similar characteristics they are included as opaque + // types. + bool IsOpaqueType() const; + + // Returns true if |this| is an instruction which could be folded into a + // constant value. + bool IsFoldable() const; + + // Returns true if |this| is an instruction which could be folded into a + // constant value by |FoldScalar|. + bool IsFoldableByFoldScalar() const; + + // Returns true if |this| is an instruction which could be folded into a + // constant value by |FoldVector|. + bool IsFoldableByFoldVector() const; + + // Returns true if we are allowed to fold or otherwise manipulate the + // instruction that defines |id| in the given context. This includes not + // handling NaN values. + bool IsFloatingPointFoldingAllowed() const; + + inline bool operator==(const Instruction&) const; + inline bool operator!=(const Instruction&) const; + inline bool operator<(const Instruction&) const; + + // Takes ownership of the instruction owned by |i| and inserts it immediately + // before |this|. Returns the inserted instruction. + Instruction* InsertBefore(std::unique_ptr&& i); + // Takes ownership of the instructions in |list| and inserts them in order + // immediately before |this|. Returns the first inserted instruction. + // Assumes the list is non-empty. + Instruction* InsertBefore(std::vector>&& list); + using utils::IntrusiveNodeBase::InsertBefore; + + // Returns true if |this| is an instruction defining a constant, but not a + // Spec constant. + inline bool IsConstant() const; + + // Returns true if |this| is an instruction with an opcode safe to move + bool IsOpcodeCodeMotionSafe() const; + + // Pretty-prints |inst|. + // + // Provides the disassembly of a specific instruction. Utilizes |inst|'s + // context to provide the correct interpretation of types, constants, etc. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Returns true if the result can be a vector and the result of each component + // depends on the corresponding component of any vector inputs. + bool IsScalarizable() const; + + // Return true if the only effect of this instructions is the result. + bool IsOpcodeSafeToDelete() const; + + // Returns true if it is valid to use the result of |inst| as the base + // pointer for a load or store. In this case, valid is defined by the relaxed + // logical addressing rules when using logical addressing. Normal validation + // rules for physical addressing. + bool IsValidBasePointer() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo instruction. If + // it is not an OpenCL.100.DebugInfo instruction, just returns + // OpenCLDebugInfo100InstructionsMax. + OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + + // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If + // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return + // NonSemanticShaderDebugInfo100InstructionsMax. + NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo or + // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we + // return the OpenCLDebugInfo code + CommonDebugInfoInstructions GetCommonDebugOpcode() const; + + // Returns true if it is an OpenCL.DebugInfo.100 instruction. + bool IsOpenCL100DebugInstr() const { + return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; + } + + // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction. + bool IsShader100DebugInstr() const { + return GetShader100DebugOpcode() != + NonSemanticShaderDebugInfo100InstructionsMax; + } + bool IsCommonDebugInstr() const { + return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax; + } + + // Returns true if this instructions a non-semantic instruction. + bool IsNonSemanticInstruction() const; + + // Dump this instruction on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + private: + // Returns the total count of result type id and result id. + uint32_t TypeResultIdCount() const { + if (has_type_id_ && has_result_id_) return 2; + if (has_type_id_ || has_result_id_) return 1; + return 0; + } + + // Returns true if the instruction generates a read-only pointer, with the + // same caveats documented in the comment for IsReadOnlyPointer. The first + // version assumes the module is a shader module. The second assumes a + // kernel. + bool IsReadOnlyPointerShaders() const; + bool IsReadOnlyPointerKernel() const; + + // Returns true if the result of |inst| can be used as the base image for an + // instruction that samples a image, reads an image, or writes to an image. + bool IsValidBaseImage() const; + + IRContext* context_; // IR Context + spv::Op opcode_; // Opcode + bool has_type_id_; // True if the instruction has a type id + bool has_result_id_; // True if the instruction has a result id + uint32_t unique_id_; // Unique instruction id + // All logical operands, including result type id and result id. + OperandList operands_; + // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note + // that for Instructions representing Op[No]Line or Debug[No]Line themselves, + // this field should be empty. + std::vector dbg_line_insts_; + + // DebugScope that wraps this instruction. + DebugScope dbg_scope_; + + friend InstructionList; +}; + +// Pretty-prints |inst| to |str| and returns |str|. +// +// Provides the disassembly of a specific instruction. Utilizes |inst|'s context +// to provide the correct interpretation of types, constants, etc. +// +// Disassembly uses raw ids (not pretty printed names). +std::ostream& operator<<(std::ostream& str, const Instruction& inst); + +inline bool Instruction::operator==(const Instruction& other) const { + return unique_id() == other.unique_id(); +} + +inline bool Instruction::operator!=(const Instruction& other) const { + return !(*this == other); +} + +inline bool Instruction::operator<(const Instruction& other) const { + return unique_id() < other.unique_id(); +} + +inline Operand& Instruction::GetOperand(uint32_t index) { + assert(index < operands_.size() && "operand index out of bound"); + return operands_[index]; +} + +inline const Operand& Instruction::GetOperand(uint32_t index) const { + assert(index < operands_.size() && "operand index out of bound"); + return operands_[index]; +} + +inline void Instruction::AddOperand(Operand&& operand) { + operands_.push_back(std::move(operand)); +} + +inline void Instruction::AddOperand(const Operand& operand) { + operands_.push_back(operand); +} + +inline void Instruction::SetInOperand(uint32_t index, + Operand::OperandData&& data) { + SetOperand(index + TypeResultIdCount(), std::move(data)); +} + +inline void Instruction::SetOperand(uint32_t index, + Operand::OperandData&& data) { + assert(index < operands_.size() && "operand index out of bound"); + assert(index >= TypeResultIdCount() && "operand is not a in-operand"); + operands_[index].words = std::move(data); +} + +inline void Instruction::SetInOperands(OperandList&& new_operands) { + // Remove the old in operands. + operands_.erase(operands_.begin() + TypeResultIdCount(), operands_.end()); + // Add the new in operands. + operands_.insert(operands_.end(), new_operands.begin(), new_operands.end()); +} + +inline void Instruction::SetResultId(uint32_t res_id) { + // TODO(dsinclair): Allow setting a result id if there wasn't one + // previously. Need to make room in the operands_ array to place the result, + // and update the has_result_id_ flag. + assert(has_result_id_); + + // TODO(dsinclair): Allow removing the result id. This needs to make sure, + // if there was a result id previously to remove it from the operands_ array + // and reset the has_result_id_ flag. + assert(res_id != 0); + + auto ridx = has_type_id_ ? 1 : 0; + operands_[ridx].words = {res_id}; +} + +inline void Instruction::SetDebugScope(const DebugScope& scope) { + dbg_scope_ = scope; + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = scope; + } +} + +inline void Instruction::SetResultType(uint32_t ty_id) { + // TODO(dsinclair): Allow setting a type id if there wasn't one + // previously. Need to make room in the operands_ array to place the result, + // and update the has_type_id_ flag. + assert(has_type_id_); + + // TODO(dsinclair): Allow removing the type id. This needs to make sure, + // if there was a type id previously to remove it from the operands_ array + // and reset the has_type_id_ flag. + assert(ty_id != 0); + + operands_.front().words = {ty_id}; +} + +inline bool Instruction::IsNop() const { + return opcode_ == spv::Op::OpNop && !has_type_id_ && !has_result_id_ && + operands_.empty(); +} + +inline void Instruction::ToNop() { + opcode_ = spv::Op::OpNop; + has_type_id_ = false; + has_result_id_ = false; + operands_.clear(); +} + +inline bool Instruction::WhileEachInst( + const std::function& f, bool run_on_debug_line_insts) { + if (run_on_debug_line_insts) { + for (auto& dbg_line : dbg_line_insts_) { + if (!f(&dbg_line)) return false; + } + } + return f(this); +} + +inline bool Instruction::WhileEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + if (run_on_debug_line_insts) { + for (auto& dbg_line : dbg_line_insts_) { + if (!f(&dbg_line)) return false; + } + } + return f(this); +} + +inline void Instruction::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void Instruction::ForEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void Instruction::ForEachId(const std::function& f) { + for (auto& operand : operands_) + if (spvIsIdType(operand.type)) f(&operand.words[0]); +} + +inline void Instruction::ForEachId( + const std::function& f) const { + for (const auto& operand : operands_) + if (spvIsIdType(operand.type)) f(&operand.words[0]); +} + +inline bool Instruction::WhileEachInId( + const std::function& f) { + for (auto& operand : operands_) { + if (spvIsInIdType(operand.type) && !f(&operand.words[0])) { + return false; + } + } + return true; +} + +inline bool Instruction::WhileEachInId( + const std::function& f) const { + for (const auto& operand : operands_) { + if (spvIsInIdType(operand.type) && !f(&operand.words[0])) { + return false; + } + } + return true; +} + +inline void Instruction::ForEachInId(const std::function& f) { + WhileEachInId([&f](uint32_t* id) { + f(id); + return true; + }); +} + +inline void Instruction::ForEachInId( + const std::function& f) const { + WhileEachInId([&f](const uint32_t* id) { + f(id); + return true; + }); +} + +inline bool Instruction::WhileEachInOperand( + const std::function& f) { + for (auto& operand : operands_) { + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + break; + default: + if (!f(&operand.words[0])) return false; + break; + } + } + return true; +} + +inline bool Instruction::WhileEachInOperand( + const std::function& f) const { + for (const auto& operand : operands_) { + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + break; + default: + if (!f(&operand.words[0])) return false; + break; + } + } + return true; +} + +inline void Instruction::ForEachInOperand( + const std::function& f) { + WhileEachInOperand([&f](uint32_t* operand) { + f(operand); + return true; + }); +} + +inline void Instruction::ForEachInOperand( + const std::function& f) const { + WhileEachInOperand([&f](const uint32_t* operand) { + f(operand); + return true; + }); +} + +inline bool Instruction::HasLabels() const { + switch (opcode_) { + case spv::Op::OpSelectionMerge: + case spv::Op::OpBranch: + case spv::Op::OpLoopMerge: + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: + case spv::Op::OpPhi: + return true; + break; + default: + break; + } + return false; +} + +bool Instruction::IsDecoration() const { + return spvOpcodeIsDecoration(opcode()); +} + +bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); } + +bool Instruction::IsAtomicWithLoad() const { + return spvOpcodeIsAtomicWithLoad(opcode()); +} + +bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); } + +bool Instruction::IsConstant() const { + return IsConstantInst(opcode()) && !IsSpecConstantInst(opcode()); +} +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.cpp new file mode 100644 index 00000000..385a136e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/instruction_list.h" + +namespace spvtools { +namespace opt { + +InstructionList::iterator InstructionList::iterator::InsertBefore( + std::vector>&& list) { + Instruction* first_node = list.front().get(); + for (auto& i : list) { + i.release()->InsertBefore(node_); + } + list.clear(); + return iterator(first_node); +} + +InstructionList::iterator InstructionList::iterator::InsertBefore( + std::unique_ptr&& i) { + i.get()->InsertBefore(node_); + return iterator(i.release()); +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.h b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.h new file mode 100644 index 00000000..b3e42745 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/instruction_list.h @@ -0,0 +1,140 @@ + +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INSTRUCTION_LIST_H_ +#define SOURCE_OPT_INSTRUCTION_LIST_H_ + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/operand.h" +#include "source/opt/instruction.h" +#include "source/util/ilist.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { + +// This class is intended to be the container for Instructions. This container +// owns the instructions that are in it. When removing an Instruction from the +// list, the caller is assuming responsibility for deleting the storage. +// +// TODO: Because there are a number of other data structures that will want +// pointers to instruction, ownership should probably be moved to the module. +// Because of that I have not made the ownership passing in this class fully +// explicit. For example, RemoveFromList takes ownership from the list, but +// does not return an std::unique_ptr to signal that. When we fully decide on +// ownership, this will have to be fixed up one way or the other. +class InstructionList : public utils::IntrusiveList { + public: + InstructionList() = default; + InstructionList(InstructionList&& that) + : utils::IntrusiveList(std::move(that)) {} + InstructionList& operator=(InstructionList&& that) { + auto p = static_cast*>(this); + *p = std::move(that); + return *this; + } + + // Destroy this list and any instructions in the list. + inline ~InstructionList() override; + + class iterator : public utils::IntrusiveList::iterator { + public: + iterator(const utils::IntrusiveList::iterator& i) + : utils::IntrusiveList::iterator(i) {} + iterator(Instruction* i) : utils::IntrusiveList::iterator(i) {} + + iterator& operator++() { + utils::IntrusiveList::iterator::operator++(); + return *this; + } + + iterator& operator--() { + utils::IntrusiveList::iterator::operator--(); + return *this; + } + + // DEPRECATED: Please use MoveBefore with an InstructionList instead. + // + // Moves the nodes in |list| to the list that |this| points to. The + // positions of the nodes will be immediately before the element pointed to + // by the iterator. The return value will be an iterator pointing to the + // first of the newly inserted elements. Ownership of the elements in + // |list| is now passed on to |*this|. + iterator InsertBefore(std::vector>&& list); + + // The node |i| will be inserted immediately before |this|. The return value + // will be an iterator pointing to the newly inserted node. The owner of + // |*i| becomes |*this| + iterator InsertBefore(std::unique_ptr&& i); + + // Removes the node from the list, and deletes the storage. Returns a valid + // iterator to the next node. + iterator Erase() { + iterator_template next_node = *this; + ++next_node; + node_->RemoveFromList(); + delete node_; + return next_node; + } + }; + + iterator begin() { return utils::IntrusiveList::begin(); } + iterator end() { return utils::IntrusiveList::end(); } + const_iterator begin() const { + return utils::IntrusiveList::begin(); + } + const_iterator end() const { + return utils::IntrusiveList::end(); + } + + void push_back(std::unique_ptr&& inst) { + utils::IntrusiveList::push_back(inst.release()); + } + + // Same as in the base class, except it will delete the data as well. + inline void clear(); + + // Runs the given function |f| on the instructions in the list and optionally + // on the preceding debug line instructions. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + auto next = begin(); + for (auto i = next; i != end(); i = next) { + ++next; + i->ForEachInst(f, run_on_debug_line_insts); + } + } +}; + +InstructionList::~InstructionList() { clear(); } + +void InstructionList::clear() { + while (!empty()) { + Instruction* inst = &front(); + inst->RemoveFromList(); + delete inst; + } +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INSTRUCTION_LIST_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.cpp new file mode 100644 index 00000000..08477cbd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.cpp @@ -0,0 +1,968 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/interface_var_sroa.h" + +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/log.h" +#include "source/opt/type_manager.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kOpDecorateDecorationInOperandIndex = 1; +constexpr uint32_t kOpDecorateLiteralInOperandIndex = 2; +constexpr uint32_t kOpEntryPointInOperandInterface = 3; +constexpr uint32_t kOpVariableStorageClassInOperandIndex = 0; +constexpr uint32_t kOpTypeArrayElemTypeInOperandIndex = 0; +constexpr uint32_t kOpTypeArrayLengthInOperandIndex = 1; +constexpr uint32_t kOpTypeMatrixColCountInOperandIndex = 1; +constexpr uint32_t kOpTypeMatrixColTypeInOperandIndex = 0; +constexpr uint32_t kOpTypePtrTypeInOperandIndex = 1; +constexpr uint32_t kOpConstantValueInOperandIndex = 0; + +// Get the length of the OpTypeArray |array_type|. +uint32_t GetArrayLength(analysis::DefUseManager* def_use_mgr, + Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t const_int_id = + array_type->GetSingleWordInOperand(kOpTypeArrayLengthInOperandIndex); + Instruction* array_length_inst = def_use_mgr->GetDef(const_int_id); + assert(array_length_inst->opcode() == spv::Op::OpConstant); + return array_length_inst->GetSingleWordInOperand( + kOpConstantValueInOperandIndex); +} + +// Get the element type instruction of the OpTypeArray |array_type|. +Instruction* GetArrayElementType(analysis::DefUseManager* def_use_mgr, + Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t elem_type_id = + array_type->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); + return def_use_mgr->GetDef(elem_type_id); +} + +// Get the column type instruction of the OpTypeMatrix |matrix_type|. +Instruction* GetMatrixColumnType(analysis::DefUseManager* def_use_mgr, + Instruction* matrix_type) { + assert(matrix_type->opcode() == spv::Op::OpTypeMatrix); + uint32_t column_type_id = + matrix_type->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); + return def_use_mgr->GetDef(column_type_id); +} + +// Traverses the component type of OpTypeArray or OpTypeMatrix. Repeats it +// |depth_to_component| times recursively and returns the component type. +// |type_id| is the result id of the OpTypeArray or OpTypeMatrix instruction. +uint32_t GetComponentTypeOfArrayMatrix(analysis::DefUseManager* def_use_mgr, + uint32_t type_id, + uint32_t depth_to_component) { + if (depth_to_component == 0) return type_id; + + Instruction* type_inst = def_use_mgr->GetDef(type_id); + if (type_inst->opcode() == spv::Op::OpTypeArray) { + uint32_t elem_type_id = + type_inst->GetSingleWordInOperand(kOpTypeArrayElemTypeInOperandIndex); + return GetComponentTypeOfArrayMatrix(def_use_mgr, elem_type_id, + depth_to_component - 1); + } + + assert(type_inst->opcode() == spv::Op::OpTypeMatrix); + uint32_t column_type_id = + type_inst->GetSingleWordInOperand(kOpTypeMatrixColTypeInOperandIndex); + return GetComponentTypeOfArrayMatrix(def_use_mgr, column_type_id, + depth_to_component - 1); +} + +// Creates an OpDecorate instruction whose Target is |var_id| and Decoration is +// |decoration|. Adds |literal| as an extra operand of the instruction. +void CreateDecoration(analysis::DecorationManager* decoration_mgr, + uint32_t var_id, spv::Decoration decoration, + uint32_t literal) { + std::vector operands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_DECORATION, + {static_cast(decoration)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}, + }); + decoration_mgr->AddDecoration(spv::Op::OpDecorate, std::move(operands)); +} + +// Replaces load instructions with composite construct instructions in all the +// users of the loads. |loads_to_composites| is the mapping from each load to +// its corresponding OpCompositeConstruct. +void ReplaceLoadWithCompositeConstruct( + IRContext* context, + const std::unordered_map& loads_to_composites) { + for (const auto& load_and_composite : loads_to_composites) { + Instruction* load = load_and_composite.first; + Instruction* composite_construct = load_and_composite.second; + + std::vector users; + context->get_def_use_mgr()->ForEachUse( + load, [&users, composite_construct](Instruction* user, uint32_t index) { + user->GetOperand(index).words[0] = composite_construct->result_id(); + users.push_back(user); + }); + + for (Instruction* user : users) + context->get_def_use_mgr()->AnalyzeInstUse(user); + } +} + +// Returns the storage class of the instruction |var|. +spv::StorageClass GetStorageClass(Instruction* var) { + return static_cast( + var->GetSingleWordInOperand(kOpVariableStorageClassInOperandIndex)); +} + +} // namespace + +bool InterfaceVariableScalarReplacement::HasExtraArrayness( + Instruction& entry_point, Instruction* var) { + spv::ExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + if (execution_model != spv::ExecutionModel::TessellationEvaluation && + execution_model != spv::ExecutionModel::TessellationControl) { + return false; + } + if (!context()->get_decoration_mgr()->HasDecoration( + var->result_id(), uint32_t(spv::Decoration::Patch))) { + if (execution_model == spv::ExecutionModel::TessellationControl) + return true; + return GetStorageClass(var) != spv::StorageClass::Output; + } + return false; +} + +bool InterfaceVariableScalarReplacement:: + CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, + bool has_extra_arrayness) { + if (has_extra_arrayness) { + return !ReportErrorIfHasNoExtraArraynessForOtherEntry(interface_var); + } + return !ReportErrorIfHasExtraArraynessForOtherEntry(interface_var); +} + +bool InterfaceVariableScalarReplacement::GetVariableLocation( + Instruction* var, uint32_t* location) { + return !context()->get_decoration_mgr()->WhileEachDecoration( + var->result_id(), uint32_t(spv::Decoration::Location), + [location](const Instruction& inst) { + *location = + inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); + return false; + }); +} + +bool InterfaceVariableScalarReplacement::GetVariableComponent( + Instruction* var, uint32_t* component) { + return !context()->get_decoration_mgr()->WhileEachDecoration( + var->result_id(), uint32_t(spv::Decoration::Component), + [component](const Instruction& inst) { + *component = + inst.GetSingleWordInOperand(kOpDecorateLiteralInOperandIndex); + return false; + }); +} + +std::vector +InterfaceVariableScalarReplacement::CollectInterfaceVariables( + Instruction& entry_point) { + std::vector interface_vars; + for (uint32_t i = kOpEntryPointInOperandInterface; + i < entry_point.NumInOperands(); ++i) { + Instruction* interface_var = context()->get_def_use_mgr()->GetDef( + entry_point.GetSingleWordInOperand(i)); + assert(interface_var->opcode() == spv::Op::OpVariable); + + spv::StorageClass storage_class = GetStorageClass(interface_var); + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + continue; + } + + interface_vars.push_back(interface_var); + } + return interface_vars; +} + +void InterfaceVariableScalarReplacement::KillInstructionAndUsers( + Instruction* inst) { + if (inst->opcode() == spv::Op::OpEntryPoint) { + return; + } + if (inst->opcode() != spv::Op::OpAccessChain) { + context()->KillInst(inst); + return; + } + std::vector users; + context()->get_def_use_mgr()->ForEachUser( + inst, [&users](Instruction* user) { users.push_back(user); }); + for (auto user : users) { + context()->KillInst(user); + } + context()->KillInst(inst); +} + +void InterfaceVariableScalarReplacement::KillInstructionsAndUsers( + const std::vector& insts) { + for (Instruction* inst : insts) { + KillInstructionAndUsers(inst); + } +} + +void InterfaceVariableScalarReplacement::KillLocationAndComponentDecorations( + uint32_t var_id) { + context()->get_decoration_mgr()->RemoveDecorationsFrom( + var_id, [](const Instruction& inst) { + spv::Decoration decoration = spv::Decoration( + inst.GetSingleWordInOperand(kOpDecorateDecorationInOperandIndex)); + return decoration == spv::Decoration::Location || + decoration == spv::Decoration::Component; + }); +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars( + Instruction* interface_var, Instruction* interface_var_type, + uint32_t location, uint32_t component, uint32_t extra_array_length) { + NestedCompositeComponents scalar_interface_vars = + CreateScalarInterfaceVarsForReplacement(interface_var_type, + GetStorageClass(interface_var), + extra_array_length); + + AddLocationAndComponentDecorations(scalar_interface_vars, &location, + component); + KillLocationAndComponentDecorations(interface_var->result_id()); + + if (!ReplaceInterfaceVarWith(interface_var, extra_array_length, + scalar_interface_vars)) { + return false; + } + + context()->KillInst(interface_var); + return true; +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith( + Instruction* interface_var, uint32_t extra_array_length, + const NestedCompositeComponents& scalar_interface_vars) { + std::vector users; + context()->get_def_use_mgr()->ForEachUser( + interface_var, [&users](Instruction* user) { users.push_back(user); }); + + std::vector interface_var_component_indices; + std::unordered_map loads_to_composites; + std::unordered_map + loads_for_access_chain_to_composites; + if (extra_array_length != 0) { + // Note that the extra arrayness is the first dimension of the array + // interface variable. + for (uint32_t index = 0; index < extra_array_length; ++index) { + std::unordered_map loads_to_component_values; + if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, &index, + &loads_to_component_values, + &loads_for_access_chain_to_composites)) { + return false; + } + AddComponentsToCompositesForLoads(loads_to_component_values, + &loads_to_composites, 0); + } + } else if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, users, scalar_interface_vars, + interface_var_component_indices, nullptr, &loads_to_composites, + &loads_for_access_chain_to_composites)) { + return false; + } + + ReplaceLoadWithCompositeConstruct(context(), loads_to_composites); + ReplaceLoadWithCompositeConstruct(context(), + loads_for_access_chain_to_composites); + + KillInstructionsAndUsers(users); + return true; +} + +void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations( + const NestedCompositeComponents& vars, uint32_t* location, + uint32_t component) { + if (!vars.HasMultipleComponents()) { + uint32_t var_id = vars.GetComponentVariable()->result_id(); + CreateDecoration(context()->get_decoration_mgr(), var_id, + spv::Decoration::Location, *location); + CreateDecoration(context()->get_decoration_mgr(), var_id, + spv::Decoration::Component, component); + ++(*location); + return; + } + for (const auto& var : vars.GetComponents()) { + AddLocationAndComponentDecorations(var, location, component); + } +} + +bool InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const NestedCompositeComponents& scalar_interface_vars, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites) { + if (!scalar_interface_vars.HasMultipleComponents()) { + for (Instruction* interface_var_user : interface_var_users) { + if (!ReplaceComponentOfInterfaceVarWith( + interface_var, interface_var_user, + scalar_interface_vars.GetComponentVariable(), + interface_var_component_indices, extra_array_index, + loads_to_composites, loads_for_access_chain_to_composites)) { + return false; + } + } + return true; + } + return ReplaceMultipleComponentsOfInterfaceVarWith( + interface_var, interface_var_users, scalar_interface_vars.GetComponents(), + interface_var_component_indices, extra_array_index, loads_to_composites, + loads_for_access_chain_to_composites); +} + +bool InterfaceVariableScalarReplacement:: + ReplaceMultipleComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const std::vector& components, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites) { + for (uint32_t i = 0; i < components.size(); ++i) { + interface_var_component_indices.push_back(i); + std::unordered_map loads_to_component_values; + std::unordered_map + loads_for_access_chain_to_component_values; + if (!ReplaceComponentsOfInterfaceVarWith( + interface_var, interface_var_users, components[i], + interface_var_component_indices, extra_array_index, + &loads_to_component_values, + &loads_for_access_chain_to_component_values)) { + return false; + } + interface_var_component_indices.pop_back(); + + uint32_t depth_to_component = + static_cast(interface_var_component_indices.size()); + AddComponentsToCompositesForLoads( + loads_for_access_chain_to_component_values, + loads_for_access_chain_to_composites, depth_to_component); + if (extra_array_index) ++depth_to_component; + AddComponentsToCompositesForLoads(loads_to_component_values, + loads_to_composites, depth_to_component); + } + return true; +} + +bool InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith( + Instruction* interface_var, Instruction* interface_var_user, + Instruction* scalar_var, + const std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_component_values, + std::unordered_map* + loads_for_access_chain_to_component_values) { + spv::Op opcode = interface_var_user->opcode(); + if (opcode == spv::Op::OpStore) { + uint32_t value_id = interface_var_user->GetSingleWordInOperand(1); + StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices, + scalar_var, extra_array_index, + interface_var_user); + return true; + } + if (opcode == spv::Op::OpLoad) { + Instruction* scalar_load = + LoadScalarVar(scalar_var, extra_array_index, interface_var_user); + loads_to_component_values->insert({interface_var_user, scalar_load}); + return true; + } + + // Copy OpName and annotation instructions only once. Therefore, we create + // them only for the first element of the extra array. + if (extra_array_index && *extra_array_index != 0) return true; + + if (opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateString || + opcode == spv::Op::OpDecorate) { + CloneAnnotationForVariable(interface_var_user, scalar_var->result_id()); + return true; + } + + if (opcode == spv::Op::OpName) { + std::unique_ptr new_inst(interface_var_user->Clone(context())); + new_inst->SetInOperand(0, {scalar_var->result_id()}); + context()->AddDebug2Inst(std::move(new_inst)); + return true; + } + + if (opcode == spv::Op::OpEntryPoint) { + return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user, + scalar_var->result_id()); + } + + if (opcode == spv::Op::OpAccessChain) { + ReplaceAccessChainWith(interface_var_user, interface_var_component_indices, + scalar_var, + loads_for_access_chain_to_component_values); + return true; + } + + std::string message("Unhandled instruction"); + message += "\n " + interface_var_user->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + message += + "\nfor interface variable scalar replacement\n " + + interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; +} + +void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain( + Instruction* access_chain, Instruction* base_access_chain) { + assert(base_access_chain->opcode() == spv::Op::OpAccessChain && + access_chain->opcode() == spv::Op::OpAccessChain && + access_chain->GetSingleWordInOperand(0) == + base_access_chain->result_id()); + Instruction::OperandList new_operands; + for (uint32_t i = 0; i < base_access_chain->NumInOperands(); ++i) { + new_operands.emplace_back(base_access_chain->GetInOperand(i)); + } + for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { + new_operands.emplace_back(access_chain->GetInOperand(i)); + } + access_chain->SetInOperands(std::move(new_operands)); +} + +Instruction* InterfaceVariableScalarReplacement::CreateAccessChainToVar( + uint32_t var_type_id, Instruction* var, + const std::vector& index_ids, Instruction* insert_before, + uint32_t* component_type_id) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + *component_type_id = GetComponentTypeOfArrayMatrix( + def_use_mgr, var_type_id, static_cast(index_ids.size())); + + uint32_t ptr_type_id = + GetPointerType(*component_type_id, GetStorageClass(var)); + + std::unique_ptr new_access_chain(new Instruction( + context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + for (uint32_t index_id : index_ids) { + new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}}); + } + + Instruction* inst = new_access_chain.get(); + def_use_mgr->AnalyzeInstDefUse(inst); + insert_before->InsertBefore(std::move(new_access_chain)); + return inst; +} + +Instruction* InterfaceVariableScalarReplacement::CreateAccessChainWithIndex( + uint32_t component_type_id, Instruction* var, uint32_t index, + Instruction* insert_before) { + uint32_t ptr_type_id = + GetPointerType(component_type_id, GetStorageClass(var)); + uint32_t index_id = context()->get_constant_mgr()->GetUIntConstId(index); + std::unique_ptr new_access_chain(new Instruction( + context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_ID, {index_id}}, + })); + Instruction* inst = new_access_chain.get(); + context()->get_def_use_mgr()->AnalyzeInstDefUse(inst); + insert_before->InsertBefore(std::move(new_access_chain)); + return inst; +} + +void InterfaceVariableScalarReplacement::ReplaceAccessChainWith( + Instruction* access_chain, + const std::vector& interface_var_component_indices, + Instruction* scalar_var, + std::unordered_map* loads_to_component_values) { + std::vector indexes; + for (uint32_t i = 1; i < access_chain->NumInOperands(); ++i) { + indexes.push_back(access_chain->GetSingleWordInOperand(i)); + } + + // Note that we have a strong assumption that |access_chain| has only a single + // index that is for the extra arrayness. + context()->get_def_use_mgr()->ForEachUser( + access_chain, + [this, access_chain, &indexes, &interface_var_component_indices, + scalar_var, loads_to_component_values](Instruction* user) { + switch (user->opcode()) { + case spv::Op::OpAccessChain: { + UseBaseAccessChainForAccessChain(user, access_chain); + ReplaceAccessChainWith(user, interface_var_component_indices, + scalar_var, loads_to_component_values); + return; + } + case spv::Op::OpStore: { + uint32_t value_id = user->GetSingleWordInOperand(1); + StoreComponentOfValueToAccessChainToScalarVar( + value_id, interface_var_component_indices, scalar_var, indexes, + user); + return; + } + case spv::Op::OpLoad: { + Instruction* value = + LoadAccessChainToVar(scalar_var, indexes, user); + loads_to_component_values->insert({user, value}); + return; + } + default: + break; + } + }); +} + +void InterfaceVariableScalarReplacement::CloneAnnotationForVariable( + Instruction* annotation_inst, uint32_t var_id) { + assert(annotation_inst->opcode() == spv::Op::OpDecorate || + annotation_inst->opcode() == spv::Op::OpDecorateId || + annotation_inst->opcode() == spv::Op::OpDecorateString); + std::unique_ptr new_inst(annotation_inst->Clone(context())); + new_inst->SetInOperand(0, {var_id}); + context()->AddAnnotationInst(std::move(new_inst)); +} + +bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarInEntryPoint( + Instruction* interface_var, Instruction* entry_point, + uint32_t scalar_var_id) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t interface_var_id = interface_var->result_id(); + if (interface_vars_removed_from_entry_point_operands_.find( + interface_var_id) != + interface_vars_removed_from_entry_point_operands_.end()) { + entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {scalar_var_id}}); + def_use_mgr->AnalyzeInstUse(entry_point); + return true; + } + + bool success = !entry_point->WhileEachInId( + [&interface_var_id, &scalar_var_id](uint32_t* id) { + if (*id == interface_var_id) { + *id = scalar_var_id; + return false; + } + return true; + }); + if (!success) { + std::string message( + "interface variable is not an operand of the entry point"); + message += "\n " + interface_var->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + message += "\n " + entry_point->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; + } + + def_use_mgr->AnalyzeInstUse(entry_point); + interface_vars_removed_from_entry_point_operands_.insert(interface_var_id); + return true; +} + +uint32_t InterfaceVariableScalarReplacement::GetPointeeTypeIdOfVar( + Instruction* var) { + assert(var->opcode() == spv::Op::OpVariable); + + uint32_t ptr_type_id = var->type_id(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_type_id); + + assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer && + "Variable must have a pointer type."); + return ptr_type_inst->GetSingleWordInOperand(kOpTypePtrTypeInOperandIndex); +} + +void InterfaceVariableScalarReplacement::StoreComponentOfValueToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (extra_array_index) { + auto* ty_mgr = context()->get_type_mgr(); + analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); + assert(array_type != nullptr); + component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); + ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, + *extra_array_index, insert_before); + } + + StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, + extra_array_index, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::LoadScalarVar( + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (extra_array_index) { + auto* ty_mgr = context()->get_type_mgr(); + analysis::Array* array_type = ty_mgr->GetType(component_type_id)->AsArray(); + assert(array_type != nullptr); + component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type()); + ptr = CreateAccessChainWithIndex(component_type_id, scalar_var, + *extra_array_index, insert_before); + } + + return CreateLoad(component_type_id, ptr, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::CreateLoad( + uint32_t type_id, Instruction* ptr, Instruction* insert_before) { + std::unique_ptr load( + new Instruction(context(), spv::Op::OpLoad, type_id, TakeNextId(), + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}})); + Instruction* load_inst = load.get(); + context()->get_def_use_mgr()->AnalyzeInstDefUse(load_inst); + insert_before->InsertBefore(std::move(load)); + return load_inst; +} + +void InterfaceVariableScalarReplacement::StoreComponentOfValueTo( + uint32_t component_type_id, uint32_t value_id, + const std::vector& component_indices, Instruction* ptr, + const uint32_t* extra_array_index, Instruction* insert_before) { + std::unique_ptr composite_extract(CreateCompositeExtract( + component_type_id, value_id, component_indices, extra_array_index)); + + std::unique_ptr new_store( + new Instruction(context(), spv::Op::OpStore)); + new_store->AddOperand({SPV_OPERAND_TYPE_ID, {ptr->result_id()}}); + new_store->AddOperand( + {SPV_OPERAND_TYPE_ID, {composite_extract->result_id()}}); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->AnalyzeInstDefUse(composite_extract.get()); + def_use_mgr->AnalyzeInstDefUse(new_store.get()); + + insert_before->InsertBefore(std::move(composite_extract)); + insert_before->InsertBefore(std::move(new_store)); +} + +Instruction* InterfaceVariableScalarReplacement::CreateCompositeExtract( + uint32_t type_id, uint32_t composite_id, + const std::vector& indexes, const uint32_t* extra_first_index) { + uint32_t component_id = TakeNextId(); + Instruction* composite_extract = new Instruction( + context(), spv::Op::OpCompositeExtract, type_id, component_id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {composite_id}}}); + if (extra_first_index) { + composite_extract->AddOperand( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {*extra_first_index}}); + } + for (uint32_t index : indexes) { + composite_extract->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); + } + return composite_extract; +} + +void InterfaceVariableScalarReplacement:: + StoreComponentOfValueToAccessChainToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, + const std::vector& access_chain_indices, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(scalar_var); + Instruction* ptr = scalar_var; + if (!access_chain_indices.empty()) { + ptr = CreateAccessChainToVar(component_type_id, scalar_var, + access_chain_indices, insert_before, + &component_type_id); + } + + StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr, + nullptr, insert_before); +} + +Instruction* InterfaceVariableScalarReplacement::LoadAccessChainToVar( + Instruction* var, const std::vector& indexes, + Instruction* insert_before) { + uint32_t component_type_id = GetPointeeTypeIdOfVar(var); + Instruction* ptr = var; + if (!indexes.empty()) { + ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before, + &component_type_id); + } + + return CreateLoad(component_type_id, ptr, insert_before); +} + +Instruction* +InterfaceVariableScalarReplacement::CreateCompositeConstructForComponentOfLoad( + Instruction* load, uint32_t depth_to_component) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t type_id = load->type_id(); + if (depth_to_component != 0) { + type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(), + depth_to_component); + } + uint32_t new_id = context()->TakeNextId(); + std::unique_ptr new_composite_construct(new Instruction( + context(), spv::Op::OpCompositeConstruct, type_id, new_id, {})); + Instruction* composite_construct = new_composite_construct.get(); + def_use_mgr->AnalyzeInstDefUse(composite_construct); + + // Insert |new_composite_construct| after |load|. When there are multiple + // recursive composite construct instructions for a load, we have to place the + // composite construct with a lower depth later because it constructs the + // composite that contains other composites with lower depths. + auto* insert_before = load->NextNode(); + while (true) { + auto itr = + composite_ids_to_component_depths.find(insert_before->result_id()); + if (itr == composite_ids_to_component_depths.end()) break; + if (itr->second <= depth_to_component) break; + insert_before = insert_before->NextNode(); + } + insert_before->InsertBefore(std::move(new_composite_construct)); + composite_ids_to_component_depths.insert({new_id, depth_to_component}); + return composite_construct; +} + +void InterfaceVariableScalarReplacement::AddComponentsToCompositesForLoads( + const std::unordered_map& + loads_to_component_values, + std::unordered_map* loads_to_composites, + uint32_t depth_to_component) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + for (auto& load_and_component_vale : loads_to_component_values) { + Instruction* load = load_and_component_vale.first; + Instruction* component_value = load_and_component_vale.second; + Instruction* composite_construct = nullptr; + auto itr = loads_to_composites->find(load); + if (itr == loads_to_composites->end()) { + composite_construct = + CreateCompositeConstructForComponentOfLoad(load, depth_to_component); + loads_to_composites->insert({load, composite_construct}); + } else { + composite_construct = itr->second; + } + composite_construct->AddOperand( + {SPV_OPERAND_TYPE_ID, {component_value->result_id()}}); + def_use_mgr->AnalyzeInstDefUse(composite_construct); + } +} + +uint32_t InterfaceVariableScalarReplacement::GetArrayType( + uint32_t elem_type_id, uint32_t array_length) { + analysis::Type* elem_type = context()->get_type_mgr()->GetType(elem_type_id); + uint32_t array_length_id = + context()->get_constant_mgr()->GetUIntConstId(array_length); + analysis::Array array_type( + elem_type, + analysis::Array::LengthInfo{array_length_id, {0, array_length}}); + return context()->get_type_mgr()->GetTypeInstruction(&array_type); +} + +uint32_t InterfaceVariableScalarReplacement::GetPointerType( + uint32_t type_id, spv::StorageClass storage_class) { + analysis::Type* type = context()->get_type_mgr()->GetType(type_id); + analysis::Pointer ptr_type(type, storage_class); + return context()->get_type_mgr()->GetTypeInstruction(&ptr_type); +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length) { + assert(interface_var_type->opcode() == spv::Op::OpTypeArray); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t array_length = GetArrayLength(def_use_mgr, interface_var_type); + Instruction* elem_type = GetArrayElementType(def_use_mgr, interface_var_type); + + NestedCompositeComponents scalar_vars; + while (array_length > 0) { + NestedCompositeComponents scalar_vars_for_element = + CreateScalarInterfaceVarsForReplacement(elem_type, storage_class, + extra_array_length); + scalar_vars.AddComponent(scalar_vars_for_element); + --array_length; + } + return scalar_vars; +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length) { + assert(interface_var_type->opcode() == spv::Op::OpTypeMatrix); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t column_count = interface_var_type->GetSingleWordInOperand( + kOpTypeMatrixColCountInOperandIndex); + Instruction* column_type = + GetMatrixColumnType(def_use_mgr, interface_var_type); + + NestedCompositeComponents scalar_vars; + while (column_count > 0) { + NestedCompositeComponents scalar_vars_for_column = + CreateScalarInterfaceVarsForReplacement(column_type, storage_class, + extra_array_length); + scalar_vars.AddComponent(scalar_vars_for_column); + --column_count; + } + return scalar_vars; +} + +InterfaceVariableScalarReplacement::NestedCompositeComponents +InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length) { + // Handle array case. + if (interface_var_type->opcode() == spv::Op::OpTypeArray) { + return CreateScalarInterfaceVarsForArray(interface_var_type, storage_class, + extra_array_length); + } + + // Handle matrix case. + if (interface_var_type->opcode() == spv::Op::OpTypeMatrix) { + return CreateScalarInterfaceVarsForMatrix(interface_var_type, storage_class, + extra_array_length); + } + + // Handle scalar or vector case. + NestedCompositeComponents scalar_var; + uint32_t type_id = interface_var_type->result_id(); + if (extra_array_length != 0) { + type_id = GetArrayType(type_id, extra_array_length); + } + uint32_t ptr_type_id = + context()->get_type_mgr()->FindPointerToType(type_id, storage_class); + uint32_t id = TakeNextId(); + std::unique_ptr variable( + new Instruction(context(), spv::Op::OpVariable, ptr_type_id, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(storage_class)}}})); + scalar_var.SetSingleComponentVariable(variable.get()); + context()->AddGlobalValue(std::move(variable)); + return scalar_var; +} + +Instruction* InterfaceVariableScalarReplacement::GetTypeOfVariable( + Instruction* var) { + uint32_t pointee_type_id = GetPointeeTypeIdOfVar(var); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return def_use_mgr->GetDef(pointee_type_id); +} + +Pass::Status InterfaceVariableScalarReplacement::Process() { + Pass::Status status = Status::SuccessWithoutChange; + for (Instruction& entry_point : get_module()->entry_points()) { + status = + CombineStatus(status, ReplaceInterfaceVarsWithScalars(entry_point)); + } + return status; +} + +bool InterfaceVariableScalarReplacement:: + ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var) { + if (vars_with_extra_arrayness.find(var) == vars_with_extra_arrayness.end()) + return false; + + std::string message( + "A variable is arrayed for an entry point but it is not " + "arrayed for another entry point"); + message += + "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return true; +} + +bool InterfaceVariableScalarReplacement:: + ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var) { + if (vars_without_extra_arrayness.find(var) == + vars_without_extra_arrayness.end()) + return false; + + std::string message( + "A variable is not arrayed for an entry point but it is " + "arrayed for another entry point"); + message += + "\n " + var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return true; +} + +Pass::Status +InterfaceVariableScalarReplacement::ReplaceInterfaceVarsWithScalars( + Instruction& entry_point) { + std::vector interface_vars = + CollectInterfaceVariables(entry_point); + + Pass::Status status = Status::SuccessWithoutChange; + for (Instruction* interface_var : interface_vars) { + uint32_t location, component; + if (!GetVariableLocation(interface_var, &location)) continue; + if (!GetVariableComponent(interface_var, &component)) component = 0; + + Instruction* interface_var_type = GetTypeOfVariable(interface_var); + uint32_t extra_array_length = 0; + if (HasExtraArrayness(entry_point, interface_var)) { + extra_array_length = + GetArrayLength(context()->get_def_use_mgr(), interface_var_type); + interface_var_type = + GetArrayElementType(context()->get_def_use_mgr(), interface_var_type); + vars_with_extra_arrayness.insert(interface_var); + } else { + vars_without_extra_arrayness.insert(interface_var); + } + + if (!CheckExtraArraynessConflictBetweenEntries(interface_var, + extra_array_length != 0)) { + return Pass::Status::Failure; + } + + if (interface_var_type->opcode() != spv::Op::OpTypeArray && + interface_var_type->opcode() != spv::Op::OpTypeMatrix) { + continue; + } + + if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type, + location, component, + extra_array_length)) { + return Pass::Status::Failure; + } + status = Pass::Status::SuccessWithChange; + } + + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.h b/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.h new file mode 100644 index 00000000..45ed3717 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/interface_var_sroa.h @@ -0,0 +1,397 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_ +#define SOURCE_OPT_INTERFACE_VAR_SROA_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +// +// Note that the current implementation of this pass covers only store, load, +// access chain instructions for the interface variables. Supporting other types +// of instructions is a future work. +class InterfaceVariableScalarReplacement : public Pass { + public: + InterfaceVariableScalarReplacement() {} + + const char* name() const override { + return "interface-variable-scalar-replacement"; + } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // A struct containing components of a composite variable. If the composite + // consists of multiple or recursive components, |component_variable| is + // nullptr and |nested_composite_components| keeps the components. If it has a + // single component, |nested_composite_components| is empty and + // |component_variable| is the component. Note that each element of + // |nested_composite_components| has the NestedCompositeComponents struct as + // its type that can recursively keep the components. + struct NestedCompositeComponents { + NestedCompositeComponents() : component_variable(nullptr) {} + + bool HasMultipleComponents() const { + return !nested_composite_components.empty(); + } + + const std::vector& GetComponents() const { + return nested_composite_components; + } + + void AddComponent(const NestedCompositeComponents& component) { + nested_composite_components.push_back(component); + } + + Instruction* GetComponentVariable() const { return component_variable; } + + void SetSingleComponentVariable(Instruction* var) { + component_variable = var; + } + + private: + std::vector nested_composite_components; + Instruction* component_variable; + }; + + // Collects all interface variables used by the |entry_point|. + std::vector CollectInterfaceVariables(Instruction& entry_point); + + // Returns whether |var| has the extra arrayness for the entry point + // |entry_point| or not. + bool HasExtraArrayness(Instruction& entry_point, Instruction* var); + + // Finds a Location BuiltIn decoration of |var| and returns it via + // |location|. Returns true whether the location exists or not. + bool GetVariableLocation(Instruction* var, uint32_t* location); + + // Finds a Component BuiltIn decoration of |var| and returns it via + // |component|. Returns true whether the component exists or not. + bool GetVariableComponent(Instruction* var, uint32_t* component); + + // Returns the type of |var| as an instruction. + Instruction* GetTypeOfVariable(Instruction* var); + + // Replaces an interface variable |interface_var| whose type is + // |interface_var_type| with scalars and returns whether it succeeds or not. + // |location| is the value of Location Decoration for |interface_var|. + // |component| is the value of Component Decoration for |interface_var|. + // If |extra_array_length| is 0, it means |interface_var| has a Patch + // decoration. Otherwise, |extra_array_length| denotes the length of the extra + // array of |interface_var|. + bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var, + Instruction* interface_var_type, + uint32_t location, + uint32_t component, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // an interface variable whose type is |interface_var_type|. If + // |extra_array_length| is not zero, adds the extra arrayness to the created + // scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForReplacement( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // the interface variable whose type is OpTypeArray |interface_var_type| with. + // If |extra_array_length| is not zero, adds the extra arrayness to all the + // scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForArray( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length); + + // Creates scalar variables with the storage classe |storage_class| to replace + // the interface variable whose type is OpTypeMatrix |interface_var_type| + // with. If |extra_array_length| is not zero, adds the extra arrayness to all + // the scalar variables. + NestedCompositeComponents CreateScalarInterfaceVarsForMatrix( + Instruction* interface_var_type, spv::StorageClass storage_class, + uint32_t extra_array_length); + + // Recursively adds Location and Component decorations to variables in + // |vars| with |location| and |component|. Increases |location| by one after + // it actually adds Location and Component decorations for a variable. + void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars, + uint32_t* location, + uint32_t component); + + // Replaces the interface variable |interface_var| with + // |scalar_interface_vars| and returns whether it succeeds or not. + // |extra_arrayness| is the extra arrayness of the interface variable. + // |scalar_interface_vars| contains the nested variables to replace the + // interface variable with. + bool ReplaceInterfaceVarWith( + Instruction* interface_var, uint32_t extra_arrayness, + const NestedCompositeComponents& scalar_interface_vars); + + // Replaces |interface_var| in the operands of instructions + // |interface_var_users| with |scalar_interface_vars|. This is a recursive + // method and |interface_var_component_indices| is used to specify which + // recursive component of |interface_var| is replaced. Returns composite + // construct instructions to be replaced with load instructions of + // |interface_var_users| via |loads_to_composites|. Returns composite + // construct instructions to be replaced with load instructions of access + // chain instructions in |interface_var_users| via + // |loads_for_access_chain_to_composites|. + bool ReplaceComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const NestedCompositeComponents& scalar_interface_vars, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites); + + // Replaces |interface_var| in the operands of instructions + // |interface_var_users| with |components| that is a vector of components for + // the interface variable |interface_var|. This is a recursive method and + // |interface_var_component_indices| is used to specify which recursive + // component of |interface_var| is replaced. Returns composite construct + // instructions to be replaced with load instructions of |interface_var_users| + // via |loads_to_composites|. Returns composite construct instructions to be + // replaced with load instructions of access chain instructions in + // |interface_var_users| via |loads_for_access_chain_to_composites|. + bool ReplaceMultipleComponentsOfInterfaceVarWith( + Instruction* interface_var, + const std::vector& interface_var_users, + const std::vector& components, + std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_composites, + std::unordered_map* + loads_for_access_chain_to_composites); + + // Replaces a component of |interface_var| that is used as an operand of + // instruction |interface_var_user| with |scalar_var|. + // |interface_var_component_indices| is a vector of recursive indices for + // which recursive component of |interface_var| is replaced. If + // |interface_var_user| is a load, returns the component value via + // |loads_to_component_values|. If |interface_var_user| is an access chain, + // returns the component value for loads of |interface_var_user| via + // |loads_for_access_chain_to_component_values|. + bool ReplaceComponentOfInterfaceVarWith( + Instruction* interface_var, Instruction* interface_var_user, + Instruction* scalar_var, + const std::vector& interface_var_component_indices, + const uint32_t* extra_array_index, + std::unordered_map* loads_to_component_values, + std::unordered_map* + loads_for_access_chain_to_component_values); + + // Creates instructions to load |scalar_var| and inserts them before + // |insert_before|. If |extra_array_index| is not null, they load + // |extra_array_index| th component of |scalar_var| instead of |scalar_var| + // itself. + Instruction* LoadScalarVar(Instruction* scalar_var, + const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates instructions to load an access chain to |var| and inserts them + // before |insert_before|. |Indexes| will be Indexes operand of the access + // chain. + Instruction* LoadAccessChainToVar(Instruction* var, + const std::vector& indexes, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to an access chain to |scalar_var| and inserts the created + // instructions before |insert_before|. To get the component, recursively + // traverses the aggregate with |component_indices| as indexes. + // Numbers in |access_chain_indices| are the Indexes operand of the access + // chain to |scalar_var| + void StoreComponentOfValueToAccessChainToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, + const std::vector& access_chain_indices, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to |scalar_var| and inserts the created instructions before + // |insert_before|. To get the component, recursively traverses the aggregate + // using |extra_array_index| and |component_indices| as indexes. + void StoreComponentOfValueToScalarVar( + uint32_t value_id, const std::vector& component_indices, + Instruction* scalar_var, const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates instructions to store a component of an aggregate whose id is + // |value_id| to |ptr| and inserts the created instructions before + // |insert_before|. To get the component, recursively traverses the aggregate + // using |extra_array_index| and |component_indices| as indexes. + // |component_type_id| is the id of the type instruction of the component. + void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id, + const std::vector& component_indices, + Instruction* ptr, + const uint32_t* extra_array_index, + Instruction* insert_before); + + // Creates new OpCompositeExtract with |type_id| for Result Type, + // |composite_id| for Composite operand, and |indexes| for Indexes operands. + // If |extra_first_index| is not nullptr, uses it as the first Indexes + // operand. + Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id, + const std::vector& indexes, + const uint32_t* extra_first_index); + + // Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is + // |ptr|. Inserts the new instruction before |insert_before|. + Instruction* CreateLoad(uint32_t type_id, Instruction* ptr, + Instruction* insert_before); + + // Clones an annotation instruction |annotation_inst| and sets the target + // operand of the new annotation instruction as |var_id|. + void CloneAnnotationForVariable(Instruction* annotation_inst, + uint32_t var_id); + + // Replaces the interface variable |interface_var| in the operands of the + // entry point |entry_point| with |scalar_var_id|. If it cannot find + // |interface_var| from the operands of the entry point |entry_point|, adds + // |scalar_var_id| as an operand of the entry point |entry_point|. + bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var, + Instruction* entry_point, + uint32_t scalar_var_id); + + // Creates an access chain instruction whose Base operand is |var| and Indexes + // operand is |index|. |component_type_id| is the id of the type instruction + // that is the type of component. Inserts the new access chain before + // |insert_before|. + Instruction* CreateAccessChainWithIndex(uint32_t component_type_id, + Instruction* var, uint32_t index, + Instruction* insert_before); + + // Returns the pointee type of the type of variable |var|. + uint32_t GetPointeeTypeIdOfVar(Instruction* var); + + // Replaces the access chain |access_chain| and its users with a new access + // chain that points |scalar_var| as the Base operand having + // |interface_var_component_indices| as Indexes operands and users of the new + // access chain. When some of the users are load instructions, returns the + // original load instruction to the new instruction that loads a component of + // the original load value via |loads_to_component_values|. + void ReplaceAccessChainWith( + Instruction* access_chain, + const std::vector& interface_var_component_indices, + Instruction* scalar_var, + std::unordered_map* + loads_to_component_values); + + // Assuming that |access_chain| is an access chain instruction whose Base + // operand is |base_access_chain|, replaces the operands of |access_chain| + // with operands of |base_access_chain| and Indexes operands of + // |access_chain|. + void UseBaseAccessChainForAccessChain(Instruction* access_chain, + Instruction* base_access_chain); + + // Creates composite construct instructions for load instructions that are the + // keys of |loads_to_component_values| if no such composite construct + // instructions exist. Adds a component of the composite as an operand of the + // created composite construct instruction. Each value of + // |loads_to_component_values| is the component. Returns the created composite + // construct instructions using |loads_to_composites|. |depth_to_component| is + // the number of recursive access steps to get the component from the + // composite. + void AddComponentsToCompositesForLoads( + const std::unordered_map& + loads_to_component_values, + std::unordered_map* loads_to_composites, + uint32_t depth_to_component); + + // Creates a composite construct instruction for a component of the value of + // instruction |load| in |depth_to_component| th recursive depth and inserts + // it after |load|. + Instruction* CreateCompositeConstructForComponentOfLoad( + Instruction* load, uint32_t depth_to_component); + + // Creates a new access chain instruction that points to variable |var| whose + // type is the instruction with |var_type_id| and inserts it before + // |insert_before|. The new access chain will have |index_ids| for Indexes + // operands. Returns the type id of the component that is pointed by the new + // access chain via |component_type_id|. + Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var, + const std::vector& index_ids, + Instruction* insert_before, + uint32_t* component_type_id); + + // Returns the result id of OpTypeArray instrunction whose Element Type + // operand is |elem_type_id| and Length operand is |array_length|. + uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length); + + // Returns the result id of OpTypePointer instrunction whose Type + // operand is |type_id| and Storage Class operand is |storage_class|. + uint32_t GetPointerType(uint32_t type_id, spv::StorageClass storage_class); + + // Kills an instrunction |inst| and its users. + void KillInstructionAndUsers(Instruction* inst); + + // Kills a vector of instrunctions |insts| and their users. + void KillInstructionsAndUsers(const std::vector& insts); + + // Kills all OpDecorate instructions for Location and Component of the + // variable whose id is |var_id|. + void KillLocationAndComponentDecorations(uint32_t var_id); + + // If |var| has the extra arrayness for an entry point, reports an error and + // returns true. Otherwise, returns false. + bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var); + + // If |var| does not have the extra arrayness for an entry point, reports an + // error and returns true. Otherwise, returns false. + bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var); + + // If |interface_var| has the extra arrayness for an entry point but it does + // not have one for another entry point, reports an error and returns false. + // Otherwise, returns true. |has_extra_arrayness| denotes whether it has an + // extra arrayness for an entry point or not. + bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var, + bool has_extra_arrayness); + + // Conducts the scalar replacement for the interface variables used by the + // |entry_point|. + Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point); + + // A set of interface variable ids that were already removed from operands of + // the entry point. + std::unordered_set + interface_vars_removed_from_entry_point_operands_; + + // A mapping from ids of new composite construct instructions that load + // instructions are replaced with to the recursive depth of the component of + // load that the new component construct instruction is used for. + std::unordered_map composite_ids_to_component_depths; + + // A set of interface variables with the extra arrayness for any of the entry + // points. + std::unordered_set vars_with_extra_arrayness; + + // A set of interface variables without the extra arrayness for any of the + // entry points. + std::unordered_set vars_without_extra_arrayness; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.cpp new file mode 100644 index 00000000..2ec2147d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/interp_fixup_pass.h" + +#include +#include + +#include "source/opt/ir_context.h" +#include "type_manager.h" + +namespace spvtools { +namespace opt { +namespace { + +// Input Operand Indices +constexpr int kSpvVariableStorageClassInIdx = 0; + +// Folding rule function which attempts to replace |op(OpLoad(a),...)| +// by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt* +// instructions. Returns true if replaced, false otherwise. +bool ReplaceInternalInterpolate(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t glsl450_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + assert(glsl450_ext_inst_id != 0); + + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + + uint32_t op1_id = inst->GetSingleWordInOperand(2); + + Instruction* load_inst = ctx->get_def_use_mgr()->GetDef(op1_id); + if (load_inst->opcode() != spv::Op::OpLoad) return false; + + Instruction* base_inst = load_inst->GetBaseAddress(); + USE_ASSERT(base_inst->opcode() == spv::Op::OpVariable && + spv::StorageClass(base_inst->GetSingleWordInOperand( + kSpvVariableStorageClassInIdx)) == spv::StorageClass::Input && + "unexpected interpolant in InterpolateAt*"); + + uint32_t ptr_id = load_inst->GetSingleWordInOperand(0); + uint32_t op2_id = (ext_opcode != GLSLstd450InterpolateAtCentroid) + ? inst->GetSingleWordInOperand(3) + : 0; + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl450_ext_inst_id}}); + new_operands.push_back( + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {ext_opcode}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); + if (op2_id != 0) new_operands.push_back({SPV_OPERAND_TYPE_ID, {op2_id}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +class InterpFoldingRules : public FoldingRules { + public: + explicit InterpFoldingRules(IRContext* ctx) : FoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override { + uint32_t extension_id = + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (extension_id != 0) { + ext_rules_[{extension_id, GLSLstd450InterpolateAtCentroid}].push_back( + ReplaceInternalInterpolate); + ext_rules_[{extension_id, GLSLstd450InterpolateAtSample}].push_back( + ReplaceInternalInterpolate); + ext_rules_[{extension_id, GLSLstd450InterpolateAtOffset}].push_back( + ReplaceInternalInterpolate); + } + } +}; + +class InterpConstFoldingRules : public ConstantFoldingRules { + public: + InterpConstFoldingRules(IRContext* ctx) : ConstantFoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override {} +}; + +} // namespace + +Pass::Status InterpFixupPass::Process() { + bool changed = false; + + // Traverse the body of the functions to replace instructions that require + // the extensions. + InstructionFolder folder( + context(), + std::unique_ptr(new InterpFoldingRules(context())), + MakeUnique(context())); + for (Function& func : *get_module()) { + func.ForEachInst([&changed, &folder](Instruction* inst) { + if (folder.FoldInstruction(inst)) { + changed = true; + } + }); + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.h new file mode 100644 index 00000000..e112b651 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/interp_fixup_pass.h @@ -0,0 +1,54 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INTERP_FIXUP_H +#define SOURCE_OPT_INTERP_FIXUP_H + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Replaces overloaded internal form for GLSLstd450Interpolate* instructions +// with external form. Specifically, removes OpLoad from the first argument +// and replaces it with the pointer for the OpLoad. glslang generates the +// internal form. This pass is called as part of glslang HLSL legalization. +class InterpFixupPass : public Pass { + public: + const char* name() const override { return "interp-fixup"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INTERP_FIXUP_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.cpp new file mode 100644 index 00000000..642e2d23 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.cpp @@ -0,0 +1,493 @@ +// Copyright (c) 2023 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/invocation_interlock_placement_pass.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/enum_set.h" +#include "source/enum_string_mapping.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" +#include "source/spirv_target_env.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +constexpr uint32_t kEntryPointExecutionModelInIdx = 0; +constexpr uint32_t kEntryPointFunctionIdInIdx = 1; +constexpr uint32_t kFunctionCallFunctionIdInIdx = 0; +} // namespace + +bool InvocationInterlockPlacementPass::hasSingleNextBlock(uint32_t block_id, + bool reverse_cfg) { + if (reverse_cfg) { + // We are traversing forward, so check whether there is a single successor. + BasicBlock* block = cfg()->block(block_id); + + switch (block->tail()->opcode()) { + case spv::Op::OpBranchConditional: + return false; + case spv::Op::OpSwitch: + return block->tail()->NumInOperandWords() == 1; + default: + return !block->tail()->IsReturnOrAbort(); + } + } else { + // We are traversing backward, so check whether there is a single + // predecessor. + return cfg()->preds(block_id).size() == 1; + } +} + +void InvocationInterlockPlacementPass::forEachNext( + uint32_t block_id, bool reverse_cfg, std::function f) { + if (reverse_cfg) { + BasicBlock* block = cfg()->block(block_id); + + block->ForEachSuccessorLabel([f](uint32_t succ_id) { f(succ_id); }); + } else { + for (uint32_t pred_id : cfg()->preds(block_id)) { + f(pred_id); + } + } +} + +void InvocationInterlockPlacementPass::addInstructionAtBlockBoundary( + BasicBlock* block, spv::Op opcode, bool at_end) { + if (at_end) { + assert(block->begin()->opcode() != spv::Op::OpPhi && + "addInstructionAtBlockBoundary expects to be called with at_end == " + "true only if there is a single successor to block"); + // Insert a begin instruction at the end of the block. + Instruction* begin_inst = new Instruction(context(), opcode); + begin_inst->InsertAfter(&*--block->tail()); + } else { + assert(block->begin()->opcode() != spv::Op::OpPhi && + "addInstructionAtBlockBoundary expects to be called with at_end == " + "false only if there is a single predecessor to block"); + // Insert an end instruction at the beginning of the block. + Instruction* end_inst = new Instruction(context(), opcode); + end_inst->InsertBefore(&*block->begin()); + } +} + +bool InvocationInterlockPlacementPass::killDuplicateBegin(BasicBlock* block) { + bool found = false; + + return context()->KillInstructionIf( + block->begin(), block->end(), [&found](Instruction* inst) { + if (inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT) { + if (found) { + return true; + } + found = true; + } + return false; + }); +} + +bool InvocationInterlockPlacementPass::killDuplicateEnd(BasicBlock* block) { + std::vector to_kill; + block->ForEachInst([&to_kill](Instruction* inst) { + if (inst->opcode() == spv::Op::OpEndInvocationInterlockEXT) { + to_kill.push_back(inst); + } + }); + + if (to_kill.size() <= 1) { + return false; + } + + to_kill.pop_back(); + + for (Instruction* inst : to_kill) { + context()->KillInst(inst); + } + + return true; +} + +void InvocationInterlockPlacementPass::recordBeginOrEndInFunction( + Function* func) { + if (extracted_functions_.count(func)) { + return; + } + + bool had_begin = false; + bool had_end = false; + + func->ForEachInst([this, &had_begin, &had_end](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + had_begin = true; + break; + case spv::Op::OpEndInvocationInterlockEXT: + had_end = true; + break; + case spv::Op::OpFunctionCall: { + uint32_t function_id = + inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx); + Function* inner_func = context()->GetFunction(function_id); + recordBeginOrEndInFunction(inner_func); + ExtractionResult result = extracted_functions_[inner_func]; + had_begin = had_begin || result.had_begin; + had_end = had_end || result.had_end; + break; + } + default: + break; + } + }); + + ExtractionResult result = {had_begin, had_end}; + extracted_functions_[func] = result; +} + +bool InvocationInterlockPlacementPass:: + removeBeginAndEndInstructionsFromFunction(Function* func) { + bool modified = false; + func->ForEachInst([this, &modified](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + context()->KillInst(inst); + modified = true; + break; + case spv::Op::OpEndInvocationInterlockEXT: + context()->KillInst(inst); + modified = true; + break; + default: + break; + } + }); + return modified; +} + +bool InvocationInterlockPlacementPass::extractInstructionsFromCalls( + std::vector blocks) { + bool modified = false; + + for (BasicBlock* block : blocks) { + block->ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == spv::Op::OpFunctionCall) { + uint32_t function_id = + inst->GetSingleWordInOperand(kFunctionCallFunctionIdInIdx); + Function* func = context()->GetFunction(function_id); + ExtractionResult result = extracted_functions_[func]; + + if (result.had_begin) { + Instruction* new_inst = new Instruction( + context(), spv::Op::OpBeginInvocationInterlockEXT); + new_inst->InsertBefore(inst); + modified = true; + } + if (result.had_end) { + Instruction* new_inst = + new Instruction(context(), spv::Op::OpEndInvocationInterlockEXT); + new_inst->InsertAfter(inst); + modified = true; + } + } + }); + } + return modified; +} + +void InvocationInterlockPlacementPass::recordExistingBeginAndEndBlock( + std::vector blocks) { + for (BasicBlock* block : blocks) { + block->ForEachInst([this, block](Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + begin_.insert(block->id()); + break; + case spv::Op::OpEndInvocationInterlockEXT: + end_.insert(block->id()); + break; + default: + break; + } + }); + } +} + +InvocationInterlockPlacementPass::BlockSet +InvocationInterlockPlacementPass::computeReachableBlocks( + BlockSet& previous_inside, const BlockSet& starting_nodes, + bool reverse_cfg) { + BlockSet inside = starting_nodes; + + std::deque worklist; + worklist.insert(worklist.begin(), starting_nodes.begin(), + starting_nodes.end()); + + while (!worklist.empty()) { + uint32_t block_id = worklist.front(); + worklist.pop_front(); + + forEachNext(block_id, reverse_cfg, + [&inside, &previous_inside, &worklist](uint32_t next_id) { + previous_inside.insert(next_id); + if (inside.insert(next_id).second) { + worklist.push_back(next_id); + } + }); + } + + return inside; +} + +bool InvocationInterlockPlacementPass::removeUnneededInstructions( + BasicBlock* block) { + bool modified = false; + if (!predecessors_after_begin_.count(block->id()) && + after_begin_.count(block->id())) { + // None of the previous blocks are in the critical section, but this block + // is. This can only happen if this block already has at least one begin + // instruction. Leave the first begin instruction, and remove any others. + modified |= killDuplicateBegin(block); + } else if (predecessors_after_begin_.count(block->id())) { + // At least one previous block is in the critical section; remove all + // begin instructions in this block. + modified |= context()->KillInstructionIf( + block->begin(), block->end(), [](Instruction* inst) { + return inst->opcode() == spv::Op::OpBeginInvocationInterlockEXT; + }); + } + + if (!successors_before_end_.count(block->id()) && + before_end_.count(block->id())) { + // Same as above + modified |= killDuplicateEnd(block); + } else if (successors_before_end_.count(block->id())) { + modified |= context()->KillInstructionIf( + block->begin(), block->end(), [](Instruction* inst) { + return inst->opcode() == spv::Op::OpEndInvocationInterlockEXT; + }); + } + return modified; +} + +BasicBlock* InvocationInterlockPlacementPass::splitEdge(BasicBlock* block, + uint32_t succ_id) { + // Create a new block to replace the critical edge. + auto new_succ_temp = MakeUnique( + MakeUnique(context(), spv::Op::OpLabel, 0, TakeNextId(), + std::initializer_list{})); + auto* new_succ = new_succ_temp.get(); + + // Insert the new block into the function. + block->GetParent()->InsertBasicBlockAfter(std::move(new_succ_temp), block); + + new_succ->AddInstruction(MakeUnique( + context(), spv::Op::OpBranch, 0, 0, + std::initializer_list{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {succ_id})})); + + assert(block->tail()->opcode() == spv::Op::OpBranchConditional || + block->tail()->opcode() == spv::Op::OpSwitch); + + // Update the first branch to successor to instead branch to + // the new successor. If there are multiple edges, we arbitrarily choose the + // first time it appears in the list. The other edges to `succ_id` will have + // to be split by another call to `splitEdge`. + block->tail()->WhileEachInId([new_succ, succ_id](uint32_t* branch_id) { + if (*branch_id == succ_id) { + *branch_id = new_succ->id(); + return false; + } + return true; + }); + + return new_succ; +} + +bool InvocationInterlockPlacementPass::placeInstructionsForEdge( + BasicBlock* block, uint32_t next_id, BlockSet& inside, + BlockSet& previous_inside, spv::Op opcode, bool reverse_cfg) { + bool modified = false; + + if (previous_inside.count(next_id) && !inside.count(block->id())) { + // This block is not in the critical section but the next has at least one + // other previous block that is, so this block should be enter it as well. + // We need to add begin or end instructions to the edge. + + modified = true; + + if (hasSingleNextBlock(block->id(), reverse_cfg)) { + // This is the only next block. + + // Additionally, because `next_id` is in `previous_inside`, we know that + // `next_id` has at least one previous block in `inside`. And because + // 'block` is not in `inside`, that means the `next_id` has to have at + // least one other previous block in `inside`. + + // This is solely for a debug assertion. It is essentially recomputing the + // value of `previous_inside` to verify that it was computed correctly + // such that the above statement is true. + bool next_has_previous_inside = false; + // By passing !reverse_cfg to forEachNext, we are actually iterating over + // the previous blocks. + forEachNext(next_id, !reverse_cfg, + [&next_has_previous_inside, inside](uint32_t previous_id) { + if (inside.count(previous_id)) { + next_has_previous_inside = true; + } + }); + assert(next_has_previous_inside && + "`previous_inside` must be the set of blocks with at least one " + "previous block in `inside`"); + + addInstructionAtBlockBoundary(block, opcode, reverse_cfg); + } else { + // This block has multiple next blocks. Split the edge and insert the + // instruction in the new next block. + BasicBlock* new_branch; + if (reverse_cfg) { + new_branch = splitEdge(block, next_id); + } else { + new_branch = splitEdge(cfg()->block(next_id), block->id()); + } + + auto inst = new Instruction(context(), opcode); + inst->InsertBefore(&*new_branch->tail()); + } + } + + return modified; +} + +bool InvocationInterlockPlacementPass::placeInstructions(BasicBlock* block) { + bool modified = false; + + block->ForEachSuccessorLabel([this, block, &modified](uint32_t succ_id) { + modified |= placeInstructionsForEdge( + block, succ_id, after_begin_, predecessors_after_begin_, + spv::Op::OpBeginInvocationInterlockEXT, /* reverse_cfg= */ true); + modified |= placeInstructionsForEdge(cfg()->block(succ_id), block->id(), + before_end_, successors_before_end_, + spv::Op::OpEndInvocationInterlockEXT, + /* reverse_cfg= */ false); + }); + + return modified; +} + +bool InvocationInterlockPlacementPass::processFragmentShaderEntry( + Function* entry_func) { + bool modified = false; + + // Save the original order of blocks in the function, so we don't iterate over + // newly-added blocks. + std::vector original_blocks; + for (auto bi = entry_func->begin(); bi != entry_func->end(); ++bi) { + original_blocks.push_back(&*bi); + } + + modified |= extractInstructionsFromCalls(original_blocks); + recordExistingBeginAndEndBlock(original_blocks); + + after_begin_ = computeReachableBlocks(predecessors_after_begin_, begin_, + /* reverse_cfg= */ true); + before_end_ = computeReachableBlocks(successors_before_end_, end_, + /* reverse_cfg= */ false); + + for (BasicBlock* block : original_blocks) { + modified |= removeUnneededInstructions(block); + modified |= placeInstructions(block); + } + return modified; +} + +bool InvocationInterlockPlacementPass::isFragmentShaderInterlockEnabled() { + if (!context()->get_feature_mgr()->HasExtension( + kSPV_EXT_fragment_shader_interlock)) { + return false; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderSampleInterlockEXT)) { + return true; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderPixelInterlockEXT)) { + return true; + } + + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::FragmentShaderShadingRateInterlockEXT)) { + return true; + } + + return false; +} + +Pass::Status InvocationInterlockPlacementPass::Process() { + // Skip this pass if the necessary extension or capability is missing + if (!isFragmentShaderInterlockEnabled()) { + return Status::SuccessWithoutChange; + } + + bool modified = false; + + std::unordered_set entry_points; + for (Instruction& entry_inst : context()->module()->entry_points()) { + uint32_t entry_id = + entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + entry_points.insert(context()->GetFunction(entry_id)); + } + + for (auto fi = context()->module()->begin(); fi != context()->module()->end(); + ++fi) { + Function* func = &*fi; + recordBeginOrEndInFunction(func); + if (!entry_points.count(func) && extracted_functions_.count(func)) { + modified |= removeBeginAndEndInstructionsFromFunction(func); + } + } + + for (Instruction& entry_inst : context()->module()->entry_points()) { + uint32_t entry_id = + entry_inst.GetSingleWordInOperand(kEntryPointFunctionIdInIdx); + Function* entry_func = context()->GetFunction(entry_id); + + auto execution_model = spv::ExecutionModel( + entry_inst.GetSingleWordInOperand(kEntryPointExecutionModelInIdx)); + + if (execution_model != spv::ExecutionModel::Fragment) { + continue; + } + + modified |= processFragmentShaderEntry(entry_func); + } + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.h new file mode 100644 index 00000000..4e85be85 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/invocation_interlock_placement_pass.h @@ -0,0 +1,158 @@ +// Copyright (c) 2023 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_ +#define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/enum_set.h" +#include "source/extensions.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace opt { + +// This pass will ensure that an entry point will only have at most one +// OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that +// order +class InvocationInterlockPlacementPass : public Pass { + public: + InvocationInterlockPlacementPass() {} + InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) = + delete; + InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete; + + const char* name() const override { return "dedupe-interlock-invocation"; } + Status Process() override; + + private: + using BlockSet = std::unordered_set; + + // Specifies whether a function originally had a begin or end instruction. + struct ExtractionResult { + bool had_begin : 1; + bool had_end : 2; + }; + + // Check if a block has only a single next block, depending on the directing + // that we are traversing the CFG. If reverse_cfg is true, we are walking + // forward through the CFG, and will return if the block has only one + // successor. Otherwise, we are walking backward through the CFG, and will + // return if the block has only one predecessor. + bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg); + + // Iterate over each of a block's predecessors or successors, depending on + // direction. If reverse_cfg is true, we are walking forward through the CFG, + // and need to iterate over the successors. Otherwise, we are walking backward + // through the CFG, and need to iterate over the predecessors. + void forEachNext(uint32_t block_id, bool reverse_cfg, + std::function f); + + // Add either a begin or end instruction to the edge of the basic block. If + // at_end is true, add the instruction to the end of the block; otherwise add + // the instruction to the beginning of the basic block. + void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode, + bool at_end); + + // Remove every OpBeginInvocationInterlockEXT instruction in block after the + // first. Returns whether any instructions were removed. + bool killDuplicateBegin(BasicBlock* block); + // Remove every OpBeginInvocationInterlockEXT instruction in block before the + // last. Returns whether any instructions were removed. + bool killDuplicateEnd(BasicBlock* block); + + // Records whether a function will potentially execute a begin or end + // instruction. + void recordBeginOrEndInFunction(Function* func); + + // Recursively removes any begin or end instructions from func and any + // function func calls. Returns whether any instructions were removed. + bool removeBeginAndEndInstructionsFromFunction(Function* func); + + // For every function call in any of the passed blocks, move any begin or end + // instructions outside of the function call. Returns whether any extractions + // occurred. + bool extractInstructionsFromCalls(std::vector blocks); + + // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and + // OpEndInvocationInterlockEXT, storing them in the member variables begin_ + // and end_ respectively. + void recordExistingBeginAndEndBlock(std::vector blocks); + + // Compute the set of blocks including or after the barrier instruction, and + // the set of blocks with any previous blocks inside the barrier instruction. + // If reverse_cfg is true, move forward through the CFG, computing + // after_begin_ and predecessors_after_begin_computing after_begin_ and + // predecessors_after_begin_, otherwise, move backward through the CFG, + // computing before_end_ and successors_before_end_. + BlockSet computeReachableBlocks(BlockSet& in_set, + const BlockSet& starting_nodes, + bool reverse_cfg); + + // Remove unneeded begin and end instructions in block. + bool removeUnneededInstructions(BasicBlock* block); + + // Given a block which branches to multiple successors, and a specific + // successor, creates a new empty block, and update the branch instruction to + // branch to the new block instead. + BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id); + + // For the edge from block to next_id, places a begin or end instruction on + // the edge, based on the direction we are walking the CFG, specified in + // reverse_cfg. + bool placeInstructionsForEdge(BasicBlock* block, uint32_t next_id, + BlockSet& inside, BlockSet& previous_inside, + spv::Op opcode, bool reverse_cfg); + // Calls placeInstructionsForEdge for each edge in block. + bool placeInstructions(BasicBlock* block); + + // Processes a single fragment shader entry function. + bool processFragmentShaderEntry(Function* entry_func); + + // Returns whether the module has the SPV_EXT_fragment_shader_interlock + // extension and one of the FragmentShader*InterlockEXT capabilities. + bool isFragmentShaderInterlockEnabled(); + + // Maps a function to whether that function originally held a begin or end + // instruction. + std::unordered_map extracted_functions_; + + // The set of blocks which have an OpBeginInvocationInterlockEXT instruction. + BlockSet begin_; + // The set of blocks which have an OpEndInvocationInterlockEXT instruction. + BlockSet end_; + // The set of blocks which either have a begin instruction, or have a + // predecessor which has a begin instruction. + BlockSet after_begin_; + // The set of blocks which either have an end instruction, or have a successor + // which have an end instruction. + BlockSet before_end_; + // The set of blocks which have a predecessor in after_begin_. + BlockSet predecessors_after_begin_; + // The set of blocks which have a successor in before_end_. + BlockSet successors_before_end_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ir_builder.h b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_builder.h new file mode 100644 index 00000000..f7ac692a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_builder.h @@ -0,0 +1,735 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_BUILDER_H_ +#define SOURCE_OPT_IR_BUILDER_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/constants.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always +// invalid. +constexpr uint32_t kInvalidId = std::numeric_limits::max(); + +// Helper class to abstract instruction construction and insertion. +// The instruction builder can preserve the following analyses (specified via +// the constructors): +// - Def-use analysis +// - Instruction to block analysis +class InstructionBuilder { + public: + using InsertionPointTy = BasicBlock::iterator; + + // Creates an InstructionBuilder, all new instructions will be inserted before + // the instruction |insert_before|. + InstructionBuilder( + IRContext* context, Instruction* insert_before, + IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) + : InstructionBuilder(context, context->get_instr_block(insert_before), + InsertionPointTy(insert_before), + preserved_analyses) {} + + // Creates an InstructionBuilder, all new instructions will be inserted at the + // end of the basic block |parent_block|. + InstructionBuilder( + IRContext* context, BasicBlock* parent_block, + IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) + : InstructionBuilder(context, parent_block, parent_block->end(), + preserved_analyses) {} + + Instruction* AddNullaryOp(uint32_t type_id, spv::Op opcode) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr new_inst( + new Instruction(GetContext(), opcode, type_id, result_id, {})); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddUnaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newUnOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}})); + return AddInstruction(std::move(newUnOp)); + } + + Instruction* AddBinaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, + uint32_t operand2) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newBinOp(new Instruction( + GetContext(), opcode, type_id, + opcode == spv::Op::OpStore ? 0 : result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}})); + return AddInstruction(std::move(newBinOp)); + } + + Instruction* AddTernaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newTernOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}})); + return AddInstruction(std::move(newTernOp)); + } + + Instruction* AddQuadOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3, + uint32_t operand4) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newQuadOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand4}}})); + return AddInstruction(std::move(newQuadOp)); + } + + Instruction* AddIdLiteralOp(uint32_t type_id, spv::Op opcode, uint32_t id, + uint32_t uliteral) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newBinOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {uliteral}}})); + return AddInstruction(std::move(newBinOp)); + } + + // Creates an N-ary instruction of |opcode|. + // |typid| must be the id of the instruction's type. + // |operands| must be a sequence of operand ids. + // Use |result| for the result id if non-zero. + Instruction* AddNaryOp(uint32_t type_id, spv::Op opcode, + const std::vector& operands, + uint32_t result = 0) { + std::vector ops; + for (size_t i = 0; i < operands.size(); i++) { + ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); + } + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst(new Instruction( + GetContext(), opcode, type_id, + result != 0 ? result : GetContext()->TakeNextId(), ops)); + return AddInstruction(std::move(new_inst)); + } + + // Creates a new selection merge instruction. + // The id |merge_id| is the merge basic block id. + Instruction* AddSelectionMerge( + uint32_t merge_id, uint32_t selection_control = static_cast( + spv::SelectionControlMask::MaskNone)) { + std::unique_ptr new_branch_merge(new Instruction( + GetContext(), spv::Op::OpSelectionMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL, + {selection_control}}})); + return AddInstruction(std::move(new_branch_merge)); + } + + // Creates a new loop merge instruction. + // The id |merge_id| is the basic block id of the merge block. + // |continue_id| is the id of the continue block. + // |loop_control| are the loop control flags to be added to the instruction. + Instruction* AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + uint32_t loop_control = static_cast( + spv::LoopControlMask::MaskNone)) { + std::unique_ptr new_branch_merge(new Instruction( + GetContext(), spv::Op::OpLoopMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {loop_control}}})); + return AddInstruction(std::move(new_branch_merge)); + } + + // Creates a new branch instruction to |label_id|. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddBranch(uint32_t label_id) { + std::unique_ptr new_branch(new Instruction( + GetContext(), spv::Op::OpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); + return AddInstruction(std::move(new_branch)); + } + + // Creates a new conditional instruction and the associated selection merge + // instruction if requested. + // The id |cond_id| is the id of the condition instruction, must be of + // type bool. + // The id |true_id| is the id of the basic block to branch to if the condition + // is true. + // The id |false_id| is the id of the basic block to branch to if the + // condition is false. + // The id |merge_id| is the id of the merge basic block for the selection + // merge instruction. If |merge_id| equals kInvalidId then no selection merge + // instruction will be created. + // The value |selection_control| is the selection control flag for the + // selection merge instruction. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddConditionalBranch( + uint32_t cond_id, uint32_t true_id, uint32_t false_id, + uint32_t merge_id = kInvalidId, + uint32_t selection_control = + static_cast(spv::SelectionControlMask::MaskNone)) { + if (merge_id != kInvalidId) { + AddSelectionMerge(merge_id, selection_control); + } + std::unique_ptr new_branch(new Instruction( + GetContext(), spv::Op::OpBranchConditional, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); + return AddInstruction(std::move(new_branch)); + } + + // Creates a new switch instruction and the associated selection merge + // instruction if requested. + // The id |selector_id| is the id of the selector instruction, must be of + // type int. + // The id |default_id| is the id of the default basic block to branch to. + // The vector |targets| is the pair of literal/branch id. + // The id |merge_id| is the id of the merge basic block for the selection + // merge instruction. If |merge_id| equals kInvalidId then no selection merge + // instruction will be created. + // The value |selection_control| is the selection control flag for the + // selection merge instruction. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddSwitch( + uint32_t selector_id, uint32_t default_id, + const std::vector>& targets, + uint32_t merge_id = kInvalidId, + uint32_t selection_control = + static_cast(spv::SelectionControlMask::MaskNone)) { + if (merge_id != kInvalidId) { + AddSelectionMerge(merge_id, selection_control); + } + std::vector operands; + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}}); + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}}); + for (auto& target : targets) { + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + target.first}); + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}}); + } + std::unique_ptr new_switch( + new Instruction(GetContext(), spv::Op::OpSwitch, 0, 0, operands)); + return AddInstruction(std::move(new_switch)); + } + + // Creates a phi instruction. + // The id |type| must be the id of the phi instruction's type. + // The vector |incomings| must be a sequence of pairs of . + Instruction* AddPhi(uint32_t type, const std::vector& incomings, + uint32_t result = 0) { + assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected"); + return AddNaryOp(type, spv::Op::OpPhi, incomings, result); + } + + // Creates an addition instruction. + // The id |type| must be the id of the instruction's type, must be the same as + // |op1| and |op2| types. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) { + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates a less than instruction for unsigned integer. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + // It is assumed that |op1| and |op2| have the same underlying type. + Instruction* AddULessThan(uint32_t op1, uint32_t op2) { + analysis::Bool bool_type; + uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates a less than instruction for signed integer. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + // It is assumed that |op1| and |op2| have the same underlying type. + Instruction* AddSLessThan(uint32_t op1, uint32_t op2) { + analysis::Bool bool_type; + uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates an OpILessThan or OpULessThen instruction depending on the sign of + // |op1|. The id |op1| is the left hand side of the operation. The id |op2| is + // the right hand side of the operation. It is assumed that |op1| and |op2| + // have the same underlying type. + Instruction* AddLessThan(uint32_t op1, uint32_t op2) { + Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1); + analysis::Type* type = + GetContext()->get_type_mgr()->GetType(op1_insn->type_id()); + analysis::Integer* int_type = type->AsInteger(); + assert(int_type && "Operand is not of int type"); + + if (int_type->IsSigned()) + return AddSLessThan(op1, op2); + else + return AddULessThan(op1, op2); + } + + // Creates a select instruction. + // |type| must match the types of |true_value| and |false_value|. It is up to + // the caller to ensure that |cond| is a correct type (bool or vector of + // bool) for |type|. + Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value, + uint32_t false_value) { + // TODO(1841): Handle id overflow. + std::unique_ptr select(new Instruction( + GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(), + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cond}}, + {SPV_OPERAND_TYPE_ID, {true_value}}, + {SPV_OPERAND_TYPE_ID, {false_value}}})); + return AddInstruction(std::move(select)); + } + + // Returns a pointer to the definition of a signed 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. + Instruction* GetSintConstant(int32_t value) { + return GetIntConstant(value, true); + } + + // Create a composite construct. + // |type| should be a composite type and the number of elements it has should + // match the size od |ids|. + Instruction* AddCompositeConstruct(uint32_t type, + const std::vector& ids) { + std::vector ops; + for (auto id : ids) { + ops.emplace_back(SPV_OPERAND_TYPE_ID, + std::initializer_list{id}); + } + // TODO(1841): Handle id overflow. + std::unique_ptr construct( + new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type, + GetContext()->TakeNextId(), ops)); + return AddInstruction(std::move(construct)); + } + + // Returns a pointer to the definition of an unsigned 32-bit integer constant + // with the given value. Returns |nullptr| if the constant does not exist and + // cannot be created. + Instruction* GetUintConstant(uint32_t value) { + return GetIntConstant(value, false); + } + + uint32_t GetUintConstantId(uint32_t value) { + Instruction* uint_inst = GetUintConstant(value); + return (uint_inst != nullptr ? uint_inst->result_id() : 0); + } + + // Adds either a signed or unsigned 32 bit integer constant to the binary + // depending on the |sign|. If |sign| is true then the value is added as a + // signed constant otherwise as an unsigned constant. If |sign| is false the + // value must not be a negative number. Returns false if the constant does + // not exists and could be be created. + template + Instruction* GetIntConstant(T value, bool sign) { + // Assert that we are not trying to store a negative number in an unsigned + // type. + if (!sign) + assert(value >= 0 && + "Trying to add a signed integer with an unsigned type!"); + + analysis::Integer int_type{32, sign}; + + // Get or create the integer type. This rebuilds the type and manages the + // memory for the rebuilt type. + uint32_t type_id = + GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); + + if (type_id == 0) { + return nullptr; + } + + // Get the memory managed type so that it is safe to be stored by + // GetConstant. + analysis::Type* rebuilt_type = + GetContext()->get_type_mgr()->GetType(type_id); + + // Even if the value is negative we need to pass the bit pattern as a + // uint32_t to GetConstant. + uint32_t word = value; + + // Create the constant value. + const analysis::Constant* constant = + GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); + + // Create the OpConstant instruction using the type and the value. + return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); + } + + Instruction* GetBoolConstant(bool value) { + analysis::Bool type; + uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&type); + analysis::Type* rebuilt_type = + GetContext()->get_type_mgr()->GetType(type_id); + uint32_t word = value; + const analysis::Constant* constant = + GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); + return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); + } + + uint32_t GetBoolConstantId(bool value) { + Instruction* inst = GetBoolConstant(value); + return (inst != nullptr ? inst->result_id() : 0); + } + + Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite, + const std::vector& index_list) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}}); + + for (uint32_t index : index_list) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); + } + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpCompositeExtract, type, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + // Creates an unreachable instruction. + Instruction* AddUnreachable() { + std::unique_ptr select( + new Instruction(GetContext(), spv::Op::OpUnreachable, 0, 0, + std::initializer_list{})); + return AddInstruction(std::move(select)); + } + + Instruction* AddOpcodeAccessChain(spv::Op opcode, uint32_t type_id, + uint32_t base_ptr_id, + const std::vector& ids) { + assert(opcode == spv::Op::OpAccessChain || + opcode == spv::Op::OpInBoundsAccessChain); + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); + + for (uint32_t index_id : ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); + } + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst(new Instruction( + GetContext(), opcode, type_id, GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, + const std::vector& ids) { + return AddOpcodeAccessChain(spv::Op::OpAccessChain, type_id, base_ptr_id, + ids); + } + Instruction* AddInBoundsAccessChain(uint32_t type_id, uint32_t base_ptr_id, + const std::vector& ids) { + return AddOpcodeAccessChain(spv::Op::OpInBoundsAccessChain, type_id, + base_ptr_id, ids); + } + + Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id, + uint32_t alignment = 0) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); + if (alignment != 0) { + operands.push_back( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, + {static_cast(spv::MemoryAccessMask::Aligned)}}); + operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}}); + } + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpLoad, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) { + std::vector operands{{SPV_OPERAND_TYPE_ID, {value_id}}}; + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpCopyObject, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}}); + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpVariable, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}}); + + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpStore, 0, 0, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, + const std::vector& parameters) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); + for (uint32_t id : parameters) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpFunctionCall, result_type, + result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddVectorShuffle(uint32_t result_type, uint32_t vec1, + uint32_t vec2, + const std::vector& components) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {vec1}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {vec2}}); + for (uint32_t id : components) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpVectorShuffle, result_type, + result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddDecoration(uint32_t target_id, spv::Decoration d, + const std::vector& literals) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {target_id}}); + operands.push_back({SPV_OPERAND_TYPE_DECORATION, {uint32_t(d)}}); + for (uint32_t literal : literals) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}); + } + + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpDecorate, 0, 0, operands)); + // Decorations are annotation instructions. Add it via the IR context, + // so the decoration manager will be updated. + // Decorations don't belong to basic blocks, so there is no need + // to update the instruction to block mapping. + Instruction* result = new_inst.get(); + GetContext()->AddAnnotationInst(std::move(new_inst)); + return result; + } + + Instruction* AddNaryExtendedInstruction( + uint32_t result_type, uint32_t set, uint32_t instruction, + const std::vector& ext_operands) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {set}}); + operands.push_back( + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {instruction}}); + for (uint32_t id : ext_operands) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr new_inst(new Instruction( + GetContext(), spv::Op::OpExtInst, result_type, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddSampledImage(uint32_t sampled_image_type_id, + uint32_t image_id, uint32_t sampler_id) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {image_id}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {sampler_id}}); + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr new_inst( + new Instruction(GetContext(), spv::Op::OpSampledImage, + sampled_image_type_id, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + // Inserts the new instruction before the insertion point. + Instruction* AddInstruction(std::unique_ptr&& insn) { + Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); + UpdateInstrToBlockMapping(insn_ptr); + UpdateDefUseMgr(insn_ptr); + return insn_ptr; + } + + // Returns the insertion point iterator. + InsertionPointTy GetInsertPoint() { return insert_before_; } + + // Change the insertion point to insert before the instruction + // |insert_before|. + void SetInsertPoint(Instruction* insert_before) { + parent_ = context_->get_instr_block(insert_before); + insert_before_ = InsertionPointTy(insert_before); + } + + // Change the insertion point to insert at the end of the basic block + // |parent_block|. + void SetInsertPoint(BasicBlock* parent_block) { + parent_ = parent_block; + insert_before_ = parent_block->end(); + } + + // Returns the context which instructions are constructed for. + IRContext* GetContext() const { return context_; } + + // Returns the set of preserved analyses. + inline IRContext::Analysis GetPreservedAnalysis() const { + return preserved_analyses_; + } + + private: + InstructionBuilder(IRContext* context, BasicBlock* parent, + InsertionPointTy insert_before, + IRContext::Analysis preserved_analyses) + : context_(context), + parent_(parent), + insert_before_(insert_before), + preserved_analyses_(preserved_analyses) { + assert(!(preserved_analyses_ & ~(IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping))); + } + + // Returns true if the users requested to update |analysis|. + inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { + if (!GetContext()->AreAnalysesValid(analysis)) { + // Do not try to update something that is not built. + return false; + } + return preserved_analyses_ & analysis; + } + + // Updates the def/use manager if the user requested it. If an update was not + // requested, this function does nothing. + inline void UpdateDefUseMgr(Instruction* insn) { + if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse)) + GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn); + } + + // Updates the instruction to block analysis if the user requested it. If + // an update was not requested, this function does nothing. + inline void UpdateInstrToBlockMapping(Instruction* insn) { + if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) && + parent_) + GetContext()->set_instr_block(insn, parent_); + } + + IRContext* context_; + BasicBlock* parent_; + InsertionPointTy insert_before_; + const IRContext::Analysis preserved_analyses_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_BUILDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.cpp new file mode 100644 index 00000000..3f41965e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.cpp @@ -0,0 +1,1176 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/ir_context.h" + +#include + +#include "OpenCLDebugInfo100.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr int kSpvDecorateTargetIdInIdx = 0; +constexpr int kSpvDecorateDecorationInIdx = 1; +constexpr int kSpvDecorateBuiltinInIdx = 2; +constexpr int kEntryPointInterfaceInIdx = 3; +constexpr int kEntryPointFunctionIdInIdx = 1; +constexpr int kEntryPointExecutionModelInIdx = 0; + +// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100 +// extension instructions. +constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13; +constexpr uint32_t kDebugGlobalVariableOperandVariableIndex = 11; +} // namespace + +void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { + set = Analysis(set & ~valid_analyses_); + + if (set & kAnalysisDefUse) { + BuildDefUseManager(); + } + if (set & kAnalysisInstrToBlockMapping) { + BuildInstrToBlockMapping(); + } + if (set & kAnalysisDecorations) { + BuildDecorationManager(); + } + if (set & kAnalysisCFG) { + BuildCFG(); + } + if (set & kAnalysisDominatorAnalysis) { + ResetDominatorAnalysis(); + } + if (set & kAnalysisLoopAnalysis) { + ResetLoopAnalysis(); + } + if (set & kAnalysisBuiltinVarId) { + ResetBuiltinAnalysis(); + } + if (set & kAnalysisNameMap) { + BuildIdToNameMap(); + } + if (set & kAnalysisScalarEvolution) { + BuildScalarEvolutionAnalysis(); + } + if (set & kAnalysisRegisterPressure) { + BuildRegPressureAnalysis(); + } + if (set & kAnalysisValueNumberTable) { + BuildValueNumberTable(); + } + if (set & kAnalysisStructuredCFG) { + BuildStructuredCFGAnalysis(); + } + if (set & kAnalysisIdToFuncMapping) { + BuildIdToFuncMapping(); + } + if (set & kAnalysisConstants) { + BuildConstantManager(); + } + if (set & kAnalysisTypes) { + BuildTypeManager(); + } + if (set & kAnalysisDebugInfo) { + BuildDebugInfoManager(); + } + if (set & kAnalysisLiveness) { + BuildLivenessManager(); + } +} + +void IRContext::InvalidateAnalysesExceptFor( + IRContext::Analysis preserved_analyses) { + uint32_t analyses_to_invalidate = valid_analyses_ & (~preserved_analyses); + InvalidateAnalyses(static_cast(analyses_to_invalidate)); +} + +void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { + // The ConstantManager and DebugInfoManager contain Type pointers. If the + // TypeManager goes away, the ConstantManager and DebugInfoManager have to + // go away. + if (analyses_to_invalidate & kAnalysisTypes) { + analyses_to_invalidate |= kAnalysisConstants; + analyses_to_invalidate |= kAnalysisDebugInfo; + } + + // The dominator analysis hold the pseudo entry and exit nodes from the CFG. + // Also if the CFG change the dominators many changed as well, so the + // dominator analysis should be invalidated as well. + if (analyses_to_invalidate & kAnalysisCFG) { + analyses_to_invalidate |= kAnalysisDominatorAnalysis; + } + + if (analyses_to_invalidate & kAnalysisDefUse) { + def_use_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisInstrToBlockMapping) { + instr_to_block_.clear(); + } + if (analyses_to_invalidate & kAnalysisDecorations) { + decoration_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisCombinators) { + combinator_ops_.clear(); + } + if (analyses_to_invalidate & kAnalysisBuiltinVarId) { + builtin_var_id_map_.clear(); + } + if (analyses_to_invalidate & kAnalysisCFG) { + cfg_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisDominatorAnalysis) { + dominator_trees_.clear(); + post_dominator_trees_.clear(); + } + if (analyses_to_invalidate & kAnalysisNameMap) { + id_to_name_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisValueNumberTable) { + vn_table_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisStructuredCFG) { + struct_cfg_analysis_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisIdToFuncMapping) { + id_to_func_.clear(); + } + if (analyses_to_invalidate & kAnalysisConstants) { + constant_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisLiveness) { + liveness_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisTypes) { + type_mgr_.reset(nullptr); + } + + if (analyses_to_invalidate & kAnalysisDebugInfo) { + debug_info_mgr_.reset(nullptr); + } + + valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate); +} + +Instruction* IRContext::KillInst(Instruction* inst) { + if (!inst) { + return nullptr; + } + + KillNamesAndDecorates(inst); + + KillOperandFromDebugInstructions(inst); + + if (AreAnalysesValid(kAnalysisDefUse)) { + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + def_use_mgr->ClearInst(inst); + for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst); + } + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + instr_to_block_.erase(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + decoration_mgr_->RemoveDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugScopeAndInlinedAtUses(inst); + get_debug_info_mgr()->ClearDebugInfo(inst); + } + if (type_mgr_ && IsTypeInst(inst->opcode())) { + type_mgr_->RemoveId(inst->result_id()); + } + if (constant_mgr_ && IsConstantInst(inst->opcode())) { + constant_mgr_->RemoveId(inst->result_id()); + } + if (inst->opcode() == spv::Op::OpCapability || + inst->opcode() == spv::Op::OpExtension) { + // We reset the feature manager, instead of updating it, because it is just + // as much work. We would have to remove all capabilities implied by this + // capability that are not also implied by the remaining OpCapability + // instructions. We could update extensions, but we will see if it is + // needed. + ResetFeatureManager(); + } + + RemoveFromIdToName(inst); + + Instruction* next_instruction = nullptr; + if (inst->IsInAList()) { + next_instruction = inst->NextNode(); + inst->RemoveFromList(); + delete inst; + } else { + // Needed for instructions that are not part of a list like OpLabels, + // OpFunction, OpFunctionEnd, etc.. + inst->ToNop(); + } + return next_instruction; +} + +bool IRContext::KillInstructionIf(Module::inst_iterator begin, + Module::inst_iterator end, + std::function condition) { + bool removed = false; + for (auto it = begin; it != end;) { + if (!condition(&*it)) { + ++it; + continue; + } + + removed = true; + // `it` is an iterator on an intrusive list. Next is invalidated on the + // current node when an instruction is killed. The iterator must be moved + // forward before deleting the node. + auto instruction = &*it; + ++it; + KillInst(instruction); + } + + return removed; +} + +void IRContext::CollectNonSemanticTree( + Instruction* inst, std::unordered_set* to_kill) { + if (!inst->HasResultId()) return; + // Debug[No]Line result id is not used, so we are done + if (inst->IsDebugLineInst()) return; + std::vector work_list; + std::unordered_set seen; + work_list.push_back(inst); + + while (!work_list.empty()) { + auto* i = work_list.back(); + work_list.pop_back(); + get_def_use_mgr()->ForEachUser( + i, [&work_list, to_kill, &seen](Instruction* user) { + if (user->IsNonSemanticInstruction() && seen.insert(user).second) { + work_list.push_back(user); + to_kill->insert(user); + } + }); + } +} + +bool IRContext::KillDef(uint32_t id) { + Instruction* def = get_def_use_mgr()->GetDef(id); + if (def != nullptr) { + KillInst(def); + return true; + } + return false; +} + +bool IRContext::RemoveCapability(spv::Capability capability) { + const bool removed = KillInstructionIf( + module()->capability_begin(), module()->capability_end(), + [capability](Instruction* inst) { + return static_cast(inst->GetSingleWordOperand(0)) == + capability; + }); + + if (removed && feature_mgr_ != nullptr) { + feature_mgr_->RemoveCapability(capability); + } + + return removed; +} + +bool IRContext::RemoveExtension(Extension extension) { + const std::string_view extensionName = ExtensionToString(extension); + const bool removed = KillInstructionIf( + module()->extension_begin(), module()->extension_end(), + [&extensionName](Instruction* inst) { + return inst->GetOperand(0).AsString() == extensionName; + }); + + if (removed && feature_mgr_ != nullptr) { + feature_mgr_->RemoveExtension(extension); + } + + return removed; +} + +bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) { + return ReplaceAllUsesWithPredicate(before, after, + [](Instruction*) { return true; }); +} + +bool IRContext::ReplaceAllUsesWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate) { + if (before == after) return false; + + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ReplaceAllUsesInDebugScopeWithPredicate(before, after, + predicate); + } + + // Ensure that |after| has been registered as def. + assert(get_def_use_mgr()->GetDef(after) && + "'after' is not a registered def."); + + std::vector> uses_to_update; + get_def_use_mgr()->ForEachUse( + before, [&predicate, &uses_to_update](Instruction* user, uint32_t index) { + if (predicate(user)) { + uses_to_update.emplace_back(user, index); + } + }); + + Instruction* prev = nullptr; + for (auto p : uses_to_update) { + Instruction* user = p.first; + uint32_t index = p.second; + if (prev == nullptr || prev != user) { + ForgetUses(user); + prev = user; + } + const uint32_t type_result_id_count = + (user->result_id() != 0) + (user->type_id() != 0); + + if (index < type_result_id_count) { + // Update the type_id. Note that result id is immutable so it should + // never be updated. + if (user->type_id() != 0 && index == 0) { + user->SetResultType(after); + } else if (user->type_id() == 0) { + SPIRV_ASSERT(consumer_, false, + "Result type id considered as use while the instruction " + "doesn't have a result type id."); + (void)consumer_; // Makes the compiler happy for release build. + } else { + SPIRV_ASSERT(consumer_, false, + "Trying setting the immutable result id."); + } + } else { + // Update an in-operand. + uint32_t in_operand_pos = index - type_result_id_count; + // Make the modification in the instruction. + user->SetInOperand(in_operand_pos, {after}); + } + AnalyzeUses(user); + } + return true; +} + +bool IRContext::IsConsistent() { +#ifndef SPIRV_CHECK_CONTEXT + return true; +#else + if (AreAnalysesValid(kAnalysisDefUse)) { + analysis::DefUseManager new_def_use(module()); + if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) { + return false; + } + } + + if (AreAnalysesValid(kAnalysisIdToFuncMapping)) { + for (auto& fn : *module_) { + if (id_to_func_[fn.result_id()] != &fn) { + return false; + } + } + } + + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + for (auto& func : *module()) { + for (auto& block : func) { + if (!block.WhileEachInst([this, &block](Instruction* inst) { + if (get_instr_block(inst) != &block) { + return false; + } + return true; + })) + return false; + } + } + } + + if (!CheckCFG()) { + return false; + } + + if (AreAnalysesValid(kAnalysisDecorations)) { + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + analysis::DecorationManager current(module()); + + if (*dec_mgr != current) { + return false; + } + } + + if (feature_mgr_ != nullptr) { + FeatureManager current(grammar_); + current.Analyze(module()); + + if (current != *feature_mgr_) { + return false; + } + } + return true; +#endif +} + +void IRContext::ForgetUses(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + get_decoration_mgr()->RemoveDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugInfo(inst); + } + RemoveFromIdToName(inst); +} + +void IRContext::AnalyzeUses(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstUse(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + get_decoration_mgr()->AddDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->AnalyzeDebugInst(inst); + } + if (id_to_name_ && (inst->opcode() == spv::Op::OpName || + inst->opcode() == spv::Op::OpMemberName)) { + id_to_name_->insert({inst->GetSingleWordInOperand(0), inst}); + } +} + +void IRContext::KillNamesAndDecorates(uint32_t id) { + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + dec_mgr->RemoveDecorationsFrom(id); + + std::vector name_to_kill; + for (auto name : GetNames(id)) { + name_to_kill.push_back(name.second); + } + for (Instruction* name_inst : name_to_kill) { + KillInst(name_inst); + } +} + +void IRContext::KillNamesAndDecorates(Instruction* inst) { + const uint32_t rId = inst->result_id(); + if (rId == 0) return; + KillNamesAndDecorates(rId); +} + +void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { + const auto opcode = inst->opcode(); + const uint32_t id = inst->result_id(); + // Kill id of OpFunction from DebugFunction. + if (opcode == spv::Op::OpFunction) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction) + continue; + auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } + // Kill id of OpVariable for global variable from DebugGlobalVariable. + if (opcode == spv::Op::OpVariable || IsConstantInst(opcode)) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable) + continue; + auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } +} + +void IRContext::AddCombinatorsForCapability(uint32_t capability) { + spv::Capability cap = spv::Capability(capability); + if (cap == spv::Capability::Shader) { + combinator_ops_[0].insert( + {(uint32_t)spv::Op::OpNop, + (uint32_t)spv::Op::OpUndef, + (uint32_t)spv::Op::OpConstant, + (uint32_t)spv::Op::OpConstantTrue, + (uint32_t)spv::Op::OpConstantFalse, + (uint32_t)spv::Op::OpConstantComposite, + (uint32_t)spv::Op::OpConstantSampler, + (uint32_t)spv::Op::OpConstantNull, + (uint32_t)spv::Op::OpTypeVoid, + (uint32_t)spv::Op::OpTypeBool, + (uint32_t)spv::Op::OpTypeInt, + (uint32_t)spv::Op::OpTypeFloat, + (uint32_t)spv::Op::OpTypeVector, + (uint32_t)spv::Op::OpTypeMatrix, + (uint32_t)spv::Op::OpTypeImage, + (uint32_t)spv::Op::OpTypeSampler, + (uint32_t)spv::Op::OpTypeSampledImage, + (uint32_t)spv::Op::OpTypeAccelerationStructureNV, + (uint32_t)spv::Op::OpTypeAccelerationStructureKHR, + (uint32_t)spv::Op::OpTypeRayQueryKHR, + (uint32_t)spv::Op::OpTypeHitObjectNV, + (uint32_t)spv::Op::OpTypeArray, + (uint32_t)spv::Op::OpTypeRuntimeArray, + (uint32_t)spv::Op::OpTypeNodePayloadArrayAMDX, + (uint32_t)spv::Op::OpTypeStruct, + (uint32_t)spv::Op::OpTypeOpaque, + (uint32_t)spv::Op::OpTypePointer, + (uint32_t)spv::Op::OpTypeFunction, + (uint32_t)spv::Op::OpTypeEvent, + (uint32_t)spv::Op::OpTypeDeviceEvent, + (uint32_t)spv::Op::OpTypeReserveId, + (uint32_t)spv::Op::OpTypeQueue, + (uint32_t)spv::Op::OpTypePipe, + (uint32_t)spv::Op::OpTypeForwardPointer, + (uint32_t)spv::Op::OpVariable, + (uint32_t)spv::Op::OpImageTexelPointer, + (uint32_t)spv::Op::OpLoad, + (uint32_t)spv::Op::OpAccessChain, + (uint32_t)spv::Op::OpInBoundsAccessChain, + (uint32_t)spv::Op::OpArrayLength, + (uint32_t)spv::Op::OpVectorExtractDynamic, + (uint32_t)spv::Op::OpVectorInsertDynamic, + (uint32_t)spv::Op::OpVectorShuffle, + (uint32_t)spv::Op::OpCompositeConstruct, + (uint32_t)spv::Op::OpCompositeExtract, + (uint32_t)spv::Op::OpCompositeInsert, + (uint32_t)spv::Op::OpCopyLogical, + (uint32_t)spv::Op::OpCopyObject, + (uint32_t)spv::Op::OpTranspose, + (uint32_t)spv::Op::OpSampledImage, + (uint32_t)spv::Op::OpImageSampleImplicitLod, + (uint32_t)spv::Op::OpImageSampleExplicitLod, + (uint32_t)spv::Op::OpImageSampleDrefImplicitLod, + (uint32_t)spv::Op::OpImageSampleDrefExplicitLod, + (uint32_t)spv::Op::OpImageSampleProjImplicitLod, + (uint32_t)spv::Op::OpImageSampleProjExplicitLod, + (uint32_t)spv::Op::OpImageSampleProjDrefImplicitLod, + (uint32_t)spv::Op::OpImageSampleProjDrefExplicitLod, + (uint32_t)spv::Op::OpImageFetch, + (uint32_t)spv::Op::OpImageGather, + (uint32_t)spv::Op::OpImageDrefGather, + (uint32_t)spv::Op::OpImageRead, + (uint32_t)spv::Op::OpImage, + (uint32_t)spv::Op::OpImageQueryFormat, + (uint32_t)spv::Op::OpImageQueryOrder, + (uint32_t)spv::Op::OpImageQuerySizeLod, + (uint32_t)spv::Op::OpImageQuerySize, + (uint32_t)spv::Op::OpImageQueryLevels, + (uint32_t)spv::Op::OpImageQuerySamples, + (uint32_t)spv::Op::OpConvertFToU, + (uint32_t)spv::Op::OpConvertFToS, + (uint32_t)spv::Op::OpConvertSToF, + (uint32_t)spv::Op::OpConvertUToF, + (uint32_t)spv::Op::OpUConvert, + (uint32_t)spv::Op::OpSConvert, + (uint32_t)spv::Op::OpFConvert, + (uint32_t)spv::Op::OpQuantizeToF16, + (uint32_t)spv::Op::OpBitcast, + (uint32_t)spv::Op::OpSNegate, + (uint32_t)spv::Op::OpFNegate, + (uint32_t)spv::Op::OpIAdd, + (uint32_t)spv::Op::OpFAdd, + (uint32_t)spv::Op::OpISub, + (uint32_t)spv::Op::OpFSub, + (uint32_t)spv::Op::OpIMul, + (uint32_t)spv::Op::OpFMul, + (uint32_t)spv::Op::OpUDiv, + (uint32_t)spv::Op::OpSDiv, + (uint32_t)spv::Op::OpFDiv, + (uint32_t)spv::Op::OpUMod, + (uint32_t)spv::Op::OpSRem, + (uint32_t)spv::Op::OpSMod, + (uint32_t)spv::Op::OpFRem, + (uint32_t)spv::Op::OpFMod, + (uint32_t)spv::Op::OpVectorTimesScalar, + (uint32_t)spv::Op::OpMatrixTimesScalar, + (uint32_t)spv::Op::OpVectorTimesMatrix, + (uint32_t)spv::Op::OpMatrixTimesVector, + (uint32_t)spv::Op::OpMatrixTimesMatrix, + (uint32_t)spv::Op::OpOuterProduct, + (uint32_t)spv::Op::OpDot, + (uint32_t)spv::Op::OpIAddCarry, + (uint32_t)spv::Op::OpISubBorrow, + (uint32_t)spv::Op::OpUMulExtended, + (uint32_t)spv::Op::OpSMulExtended, + (uint32_t)spv::Op::OpAny, + (uint32_t)spv::Op::OpAll, + (uint32_t)spv::Op::OpIsNan, + (uint32_t)spv::Op::OpIsInf, + (uint32_t)spv::Op::OpLogicalEqual, + (uint32_t)spv::Op::OpLogicalNotEqual, + (uint32_t)spv::Op::OpLogicalOr, + (uint32_t)spv::Op::OpLogicalAnd, + (uint32_t)spv::Op::OpLogicalNot, + (uint32_t)spv::Op::OpSelect, + (uint32_t)spv::Op::OpIEqual, + (uint32_t)spv::Op::OpINotEqual, + (uint32_t)spv::Op::OpUGreaterThan, + (uint32_t)spv::Op::OpSGreaterThan, + (uint32_t)spv::Op::OpUGreaterThanEqual, + (uint32_t)spv::Op::OpSGreaterThanEqual, + (uint32_t)spv::Op::OpULessThan, + (uint32_t)spv::Op::OpSLessThan, + (uint32_t)spv::Op::OpULessThanEqual, + (uint32_t)spv::Op::OpSLessThanEqual, + (uint32_t)spv::Op::OpFOrdEqual, + (uint32_t)spv::Op::OpFUnordEqual, + (uint32_t)spv::Op::OpFOrdNotEqual, + (uint32_t)spv::Op::OpFUnordNotEqual, + (uint32_t)spv::Op::OpFOrdLessThan, + (uint32_t)spv::Op::OpFUnordLessThan, + (uint32_t)spv::Op::OpFOrdGreaterThan, + (uint32_t)spv::Op::OpFUnordGreaterThan, + (uint32_t)spv::Op::OpFOrdLessThanEqual, + (uint32_t)spv::Op::OpFUnordLessThanEqual, + (uint32_t)spv::Op::OpFOrdGreaterThanEqual, + (uint32_t)spv::Op::OpFUnordGreaterThanEqual, + (uint32_t)spv::Op::OpShiftRightLogical, + (uint32_t)spv::Op::OpShiftRightArithmetic, + (uint32_t)spv::Op::OpShiftLeftLogical, + (uint32_t)spv::Op::OpBitwiseOr, + (uint32_t)spv::Op::OpBitwiseXor, + (uint32_t)spv::Op::OpBitwiseAnd, + (uint32_t)spv::Op::OpNot, + (uint32_t)spv::Op::OpBitFieldInsert, + (uint32_t)spv::Op::OpBitFieldSExtract, + (uint32_t)spv::Op::OpBitFieldUExtract, + (uint32_t)spv::Op::OpBitReverse, + (uint32_t)spv::Op::OpBitCount, + (uint32_t)spv::Op::OpPhi, + (uint32_t)spv::Op::OpImageSparseSampleImplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleExplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleDrefImplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleDrefExplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleProjImplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleProjExplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleProjDrefImplicitLod, + (uint32_t)spv::Op::OpImageSparseSampleProjDrefExplicitLod, + (uint32_t)spv::Op::OpImageSparseFetch, + (uint32_t)spv::Op::OpImageSparseGather, + (uint32_t)spv::Op::OpImageSparseDrefGather, + (uint32_t)spv::Op::OpImageSparseTexelsResident, + (uint32_t)spv::Op::OpImageSparseRead, + (uint32_t)spv::Op::OpSizeOf}); + } +} + +void IRContext::AddCombinatorsForExtension(Instruction* extension) { + assert(extension->opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = extension->GetInOperand(0).AsString(); + if (extension_name == "GLSL.std.450") { + combinator_ops_[extension->result_id()] = { + (uint32_t)GLSLstd450Round, + (uint32_t)GLSLstd450RoundEven, + (uint32_t)GLSLstd450Trunc, + (uint32_t)GLSLstd450FAbs, + (uint32_t)GLSLstd450SAbs, + (uint32_t)GLSLstd450FSign, + (uint32_t)GLSLstd450SSign, + (uint32_t)GLSLstd450Floor, + (uint32_t)GLSLstd450Ceil, + (uint32_t)GLSLstd450Fract, + (uint32_t)GLSLstd450Radians, + (uint32_t)GLSLstd450Degrees, + (uint32_t)GLSLstd450Sin, + (uint32_t)GLSLstd450Cos, + (uint32_t)GLSLstd450Tan, + (uint32_t)GLSLstd450Asin, + (uint32_t)GLSLstd450Acos, + (uint32_t)GLSLstd450Atan, + (uint32_t)GLSLstd450Sinh, + (uint32_t)GLSLstd450Cosh, + (uint32_t)GLSLstd450Tanh, + (uint32_t)GLSLstd450Asinh, + (uint32_t)GLSLstd450Acosh, + (uint32_t)GLSLstd450Atanh, + (uint32_t)GLSLstd450Atan2, + (uint32_t)GLSLstd450Pow, + (uint32_t)GLSLstd450Exp, + (uint32_t)GLSLstd450Log, + (uint32_t)GLSLstd450Exp2, + (uint32_t)GLSLstd450Log2, + (uint32_t)GLSLstd450Sqrt, + (uint32_t)GLSLstd450InverseSqrt, + (uint32_t)GLSLstd450Determinant, + (uint32_t)GLSLstd450MatrixInverse, + (uint32_t)GLSLstd450ModfStruct, + (uint32_t)GLSLstd450FMin, + (uint32_t)GLSLstd450UMin, + (uint32_t)GLSLstd450SMin, + (uint32_t)GLSLstd450FMax, + (uint32_t)GLSLstd450UMax, + (uint32_t)GLSLstd450SMax, + (uint32_t)GLSLstd450FClamp, + (uint32_t)GLSLstd450UClamp, + (uint32_t)GLSLstd450SClamp, + (uint32_t)GLSLstd450FMix, + (uint32_t)GLSLstd450IMix, + (uint32_t)GLSLstd450Step, + (uint32_t)GLSLstd450SmoothStep, + (uint32_t)GLSLstd450Fma, + (uint32_t)GLSLstd450FrexpStruct, + (uint32_t)GLSLstd450Ldexp, + (uint32_t)GLSLstd450PackSnorm4x8, + (uint32_t)GLSLstd450PackUnorm4x8, + (uint32_t)GLSLstd450PackSnorm2x16, + (uint32_t)GLSLstd450PackUnorm2x16, + (uint32_t)GLSLstd450PackHalf2x16, + (uint32_t)GLSLstd450PackDouble2x32, + (uint32_t)GLSLstd450UnpackSnorm2x16, + (uint32_t)GLSLstd450UnpackUnorm2x16, + (uint32_t)GLSLstd450UnpackHalf2x16, + (uint32_t)GLSLstd450UnpackSnorm4x8, + (uint32_t)GLSLstd450UnpackUnorm4x8, + (uint32_t)GLSLstd450UnpackDouble2x32, + (uint32_t)GLSLstd450Length, + (uint32_t)GLSLstd450Distance, + (uint32_t)GLSLstd450Cross, + (uint32_t)GLSLstd450Normalize, + (uint32_t)GLSLstd450FaceForward, + (uint32_t)GLSLstd450Reflect, + (uint32_t)GLSLstd450Refract, + (uint32_t)GLSLstd450FindILsb, + (uint32_t)GLSLstd450FindSMsb, + (uint32_t)GLSLstd450FindUMsb, + (uint32_t)GLSLstd450InterpolateAtCentroid, + (uint32_t)GLSLstd450InterpolateAtSample, + (uint32_t)GLSLstd450InterpolateAtOffset, + (uint32_t)GLSLstd450NMin, + (uint32_t)GLSLstd450NMax, + (uint32_t)GLSLstd450NClamp}; + } else { + // Map the result id to the empty set. + combinator_ops_[extension->result_id()]; + } +} + +void IRContext::InitializeCombinators() { + for (auto capability : get_feature_mgr()->GetCapabilities()) { + AddCombinatorsForCapability(uint32_t(capability)); + } + + for (auto& extension : module()->ext_inst_imports()) { + AddCombinatorsForExtension(&extension); + } + + valid_analyses_ |= kAnalysisCombinators; +} + +void IRContext::RemoveFromIdToName(const Instruction* inst) { + if (id_to_name_ && (inst->opcode() == spv::Op::OpName || + inst->opcode() == spv::Op::OpMemberName)) { + auto range = id_to_name_->equal_range(inst->GetSingleWordInOperand(0)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == inst) { + id_to_name_->erase(it); + break; + } + } + } +} + +LoopDescriptor* IRContext::GetLoopDescriptor(const Function* f) { + if (!AreAnalysesValid(kAnalysisLoopAnalysis)) { + ResetLoopAnalysis(); + } + + std::unordered_map::iterator it = + loop_descriptors_.find(f); + if (it == loop_descriptors_.end()) { + return &loop_descriptors_ + .emplace(std::make_pair(f, LoopDescriptor(this, f))) + .first->second; + } + + return &it->second; +} + +uint32_t IRContext::FindBuiltinInputVar(uint32_t builtin) { + for (auto& a : module_->annotations()) { + if (spv::Op(a.opcode()) != spv::Op::OpDecorate) continue; + if (spv::Decoration(a.GetSingleWordInOperand( + kSpvDecorateDecorationInIdx)) != spv::Decoration::BuiltIn) + continue; + if (a.GetSingleWordInOperand(kSpvDecorateBuiltinInIdx) != builtin) continue; + uint32_t target_id = a.GetSingleWordInOperand(kSpvDecorateTargetIdInIdx); + Instruction* b_var = get_def_use_mgr()->GetDef(target_id); + if (b_var->opcode() != spv::Op::OpVariable) continue; + if (spv::StorageClass(b_var->GetSingleWordInOperand(0)) != + spv::StorageClass::Input) + continue; + return target_id; + } + return 0; +} + +void IRContext::AddVarToEntryPoints(uint32_t var_id) { + uint32_t ocnt = 0; + for (auto& e : module()->entry_points()) { + bool found = false; + e.ForEachInOperand([&ocnt, &found, &var_id](const uint32_t* idp) { + if (ocnt >= kEntryPointInterfaceInIdx) { + if (*idp == var_id) found = true; + } + ++ocnt; + }); + if (!found) { + e.AddOperand({SPV_OPERAND_TYPE_ID, {var_id}}); + get_def_use_mgr()->AnalyzeInstDefUse(&e); + } + } +} + +uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) { + if (!AreAnalysesValid(kAnalysisBuiltinVarId)) ResetBuiltinAnalysis(); + // If cached, return it. + std::unordered_map::iterator it = + builtin_var_id_map_.find(builtin); + if (it != builtin_var_id_map_.end()) return it->second; + // Look for one in shader + uint32_t var_id = FindBuiltinInputVar(builtin); + if (var_id == 0) { + // If not found, create it + // TODO(greg-lunarg): Add support for all builtins + analysis::TypeManager* type_mgr = get_type_mgr(); + analysis::Type* reg_type; + switch (spv::BuiltIn(builtin)) { + case spv::BuiltIn::FragCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4float_ty); + break; + } + case spv::BuiltIn::VertexIndex: + case spv::BuiltIn::InstanceIndex: + case spv::BuiltIn::PrimitiveId: + case spv::BuiltIn::InvocationId: + case spv::BuiltIn::SubgroupLocalInvocationId: { + analysis::Integer uint_ty(32, false); + reg_type = type_mgr->GetRegisteredType(&uint_ty); + break; + } + case spv::BuiltIn::GlobalInvocationId: + case spv::BuiltIn::LaunchIdNV: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v3uint_ty(reg_uint_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3uint_ty); + break; + } + case spv::BuiltIn::TessCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v3float_ty(reg_float_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3float_ty); + break; + } + case spv::BuiltIn::SubgroupLtMask: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v4uint_ty(reg_uint_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4uint_ty); + break; + } + default: { + assert(false && "unhandled builtin"); + return 0; + } + } + uint32_t type_id = type_mgr->GetTypeInstruction(reg_type); + uint32_t varTyPtrId = + type_mgr->FindPointerToType(type_id, spv::StorageClass::Input); + // TODO(1841): Handle id overflow. + var_id = TakeNextId(); + std::unique_ptr newVarOp( + new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {uint32_t(spv::StorageClass::Input)}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp); + module()->AddGlobalValue(std::move(newVarOp)); + get_decoration_mgr()->AddDecorationVal( + var_id, uint32_t(spv::Decoration::BuiltIn), builtin); + AddVarToEntryPoints(var_id); + } + builtin_var_id_map_[builtin] = var_id; + return var_id; +} + +void IRContext::AddCalls(const Function* func, std::queue* todo) { + for (auto bi = func->begin(); bi != func->end(); ++bi) + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + if (ii->opcode() == spv::Op::OpFunctionCall) + todo->push(ii->GetSingleWordInOperand(0)); + if (ii->opcode() == spv::Op::OpCooperativeMatrixPerElementOpNV) + todo->push(ii->GetSingleWordInOperand(1)); + if (ii->opcode() == spv::Op::OpCooperativeMatrixReduceNV) + todo->push(ii->GetSingleWordInOperand(2)); + if (ii->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) { + const auto memory_operands_index = 3; + auto mask = ii->GetSingleWordInOperand(memory_operands_index); + + uint32_t count = 1; + if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++count; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) + ++count; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) + ++count; + + const auto tensor_operands_index = memory_operands_index + count; + mask = ii->GetSingleWordInOperand(tensor_operands_index); + count = 1; + if (mask & uint32_t(spv::TensorAddressingOperandsMask::TensorView)) + ++count; + + if (mask & uint32_t(spv::TensorAddressingOperandsMask::DecodeFunc)) { + todo->push(ii->GetSingleWordInOperand(tensor_operands_index + count)); + } + } + } +} + +bool IRContext::ProcessEntryPointCallTree(ProcessFunction& pfn) { + // Collect all of the entry points as the roots. + std::queue roots; + for (auto& e : module()->entry_points()) { + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessReachableCallTree(ProcessFunction& pfn) { + std::queue roots; + + // Add all entry points since they can be reached from outside the module. + for (auto& e : module()->entry_points()) + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + + // Add all exported functions since they can be reached from outside the + // module. + for (auto& a : annotations()) { + // TODO: Handle group decorations as well. Currently not generate by any + // front-end, but could be coming. + if (a.opcode() == spv::Op::OpDecorate) { + if (spv::Decoration(a.GetSingleWordOperand(1)) == + spv::Decoration::LinkageAttributes) { + uint32_t lastOperand = a.NumOperands() - 1; + if (spv::LinkageType(a.GetSingleWordOperand(lastOperand)) == + spv::LinkageType::Export) { + uint32_t id = a.GetSingleWordOperand(0); + if (GetFunction(id)) { + roots.push(id); + } + } + } + } + } + + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessCallTreeFromRoots(ProcessFunction& pfn, + std::queue* roots) { + // Process call tree + bool modified = false; + std::unordered_set done; + + while (!roots->empty()) { + const uint32_t fi = roots->front(); + roots->pop(); + if (done.insert(fi).second) { + Function* fn = GetFunction(fi); + assert(fn && "Trying to process a function that does not exist."); + modified = pfn(fn) || modified; + AddCalls(fn, roots); + } + } + return modified; +} + +void IRContext::CollectCallTreeFromRoots(unsigned entryId, + std::unordered_set* funcs) { + std::queue roots; + roots.push(entryId); + while (!roots.empty()) { + const uint32_t fi = roots.front(); + roots.pop(); + funcs->insert(fi); + Function* fn = GetFunction(fi); + AddCalls(fn, &roots); + } +} + +void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { + if (!consumer()) { + return; + } + + Instruction* line_inst = inst; + while (line_inst != nullptr) { // Stop at the beginning of the basic block. + if (!line_inst->dbg_line_insts().empty()) { + line_inst = &line_inst->dbg_line_insts().back(); + if (line_inst->IsNoLine()) { + line_inst = nullptr; + } + break; + } + line_inst = line_inst->PreviousNode(); + } + + uint32_t line_number = 0; + uint32_t col_number = 0; + std::string source; + if (line_inst != nullptr) { + Instruction* file_name = + get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); + source = file_name->GetInOperand(0).AsString(); + + // Get the line number and column number. + line_number = line_inst->GetSingleWordInOperand(1); + col_number = line_inst->GetSingleWordInOperand(2); + } + + message += + "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0}, + message.c_str()); +} + +// Gets the dominator analysis for function |f|. +DominatorAnalysis* IRContext::GetDominatorAnalysis(const Function* f) { + if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) { + ResetDominatorAnalysis(); + } + + if (dominator_trees_.find(f) == dominator_trees_.end()) { + dominator_trees_[f].InitializeTree(*cfg(), f); + } + + return &dominator_trees_[f]; +} + +// Gets the postdominator analysis for function |f|. +PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(const Function* f) { + if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) { + ResetDominatorAnalysis(); + } + + if (post_dominator_trees_.find(f) == post_dominator_trees_.end()) { + post_dominator_trees_[f].InitializeTree(*cfg(), f); + } + + return &post_dominator_trees_[f]; +} + +bool IRContext::CheckCFG() { + std::unordered_map> real_preds; + if (!AreAnalysesValid(kAnalysisCFG)) { + return true; + } + + for (Function& function : *module()) { + for (const auto& bb : function) { + bb.ForEachSuccessorLabel([&bb, &real_preds](const uint32_t lab_id) { + real_preds[lab_id].push_back(bb.id()); + }); + } + + for (auto& bb : function) { + std::vector preds = cfg()->preds(bb.id()); + std::vector real = real_preds[bb.id()]; + std::sort(preds.begin(), preds.end()); + std::sort(real.begin(), real.end()); + + bool same = true; + if (preds.size() != real.size()) { + same = false; + } + + for (size_t i = 0; i < real.size() && same; i++) { + if (preds[i] != real[i]) { + same = false; + } + } + + if (!same) { + std::cerr << "Predecessors for " << bb.id() << " are different:\n"; + + std::cerr << "Real:"; + for (uint32_t i : real) { + std::cerr << ' ' << i; + } + std::cerr << std::endl; + + std::cerr << "Recorded:"; + for (uint32_t i : preds) { + std::cerr << ' ' << i; + } + std::cerr << std::endl; + } + if (!same) return false; + } + } + + return true; +} + +bool IRContext::IsReachable(const opt::BasicBlock& bb) { + auto enclosing_function = bb.GetParent(); + return GetDominatorAnalysis(enclosing_function) + ->Dominates(enclosing_function->entry().get(), &bb); +} + +spv::ExecutionModel IRContext::GetStage() { + const auto& entry_points = module()->entry_points(); + if (entry_points.empty()) { + return spv::ExecutionModel::Max; + } + + uint32_t stage = entry_points.begin()->GetSingleWordInOperand( + kEntryPointExecutionModelInIdx); + auto it = std::find_if( + entry_points.begin(), entry_points.end(), [stage](const Instruction& x) { + return x.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != + stage; + }); + if (it != entry_points.end()) { + EmitErrorMessage("Mixed stage shader module not supported", &(*it)); + } + + return static_cast(stage); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.h b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.h new file mode 100644 index 00000000..38576966 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_context.h @@ -0,0 +1,1302 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_CONTEXT_H_ +#define SOURCE_OPT_IR_CONTEXT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/enum_string_mapping.h" +#include "source/opt/cfg.h" +#include "source/opt/constants.h" +#include "source/opt/debug_info_manager.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/feature_manager.h" +#include "source/opt/fold.h" +#include "source/opt/liveness.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/module.h" +#include "source/opt/register_pressure.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/struct_cfg_analysis.h" +#include "source/opt/type_manager.h" +#include "source/opt/value_number_table.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +class IRContext { + public: + // Available analyses. + // + // When adding a new analysis: + // + // 1. Enum values should be powers of 2. These are cast into uint32_t + // bitmasks, so we can have at most 31 analyses represented. + // + // 2. Make sure it gets invalidated or preserved by IRContext methods that add + // or remove IR elements (e.g., KillDef, KillInst, ReplaceAllUsesWith). + // + // 3. Add handling code in BuildInvalidAnalyses and InvalidateAnalyses + enum Analysis { + kAnalysisNone = 0 << 0, + kAnalysisBegin = 1 << 0, + kAnalysisDefUse = kAnalysisBegin, + kAnalysisInstrToBlockMapping = 1 << 1, + kAnalysisDecorations = 1 << 2, + kAnalysisCombinators = 1 << 3, + kAnalysisCFG = 1 << 4, + kAnalysisDominatorAnalysis = 1 << 5, + kAnalysisLoopAnalysis = 1 << 6, + kAnalysisNameMap = 1 << 7, + kAnalysisScalarEvolution = 1 << 8, + kAnalysisRegisterPressure = 1 << 9, + kAnalysisValueNumberTable = 1 << 10, + kAnalysisStructuredCFG = 1 << 11, + kAnalysisBuiltinVarId = 1 << 12, + kAnalysisIdToFuncMapping = 1 << 13, + kAnalysisConstants = 1 << 14, + kAnalysisTypes = 1 << 15, + kAnalysisDebugInfo = 1 << 16, + kAnalysisLiveness = 1 << 17, + kAnalysisEnd = 1 << 18 + }; + + using ProcessFunction = std::function; + + friend inline Analysis operator|(Analysis lhs, Analysis rhs); + friend inline Analysis& operator|=(Analysis& lhs, Analysis rhs); + friend inline Analysis operator<<(Analysis a, int shift); + friend inline Analysis& operator<<=(Analysis& a, int shift); + + // Creates an |IRContext| that contains an owned |Module| + IRContext(spv_target_env env, MessageConsumer c) + : syntax_context_(spvContextCreate(env)), + grammar_(syntax_context_), + unique_id_(0), + module_(new Module()), + consumer_(std::move(c)), + def_use_mgr_(nullptr), + feature_mgr_(nullptr), + valid_analyses_(kAnalysisNone), + constant_mgr_(nullptr), + type_mgr_(nullptr), + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) { + SetContextMessageConsumer(syntax_context_, consumer_); + module_->SetContext(this); + } + + IRContext(spv_target_env env, std::unique_ptr&& m, MessageConsumer c) + : syntax_context_(spvContextCreate(env)), + grammar_(syntax_context_), + unique_id_(0), + module_(std::move(m)), + consumer_(std::move(c)), + def_use_mgr_(nullptr), + feature_mgr_(nullptr), + valid_analyses_(kAnalysisNone), + type_mgr_(nullptr), + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) { + SetContextMessageConsumer(syntax_context_, consumer_); + module_->SetContext(this); + InitializeCombinators(); + } + + ~IRContext() { spvContextDestroy(syntax_context_); } + + Module* module() const { return module_.get(); } + + // Returns a vector of pointers to constant-creation instructions in this + // context. + inline std::vector GetConstants(); + inline std::vector GetConstants() const; + + // Iterators for annotation instructions contained in this context. + inline Module::inst_iterator annotation_begin(); + inline Module::inst_iterator annotation_end(); + inline IteratorRange annotations(); + inline IteratorRange annotations() const; + + // Iterators for capabilities instructions contained in this module. + inline Module::inst_iterator capability_begin(); + inline Module::inst_iterator capability_end(); + inline IteratorRange capabilities(); + inline IteratorRange capabilities() const; + + // Iterators for extensions instructions contained in this module. + inline Module::inst_iterator extension_begin(); + inline Module::inst_iterator extension_end(); + inline IteratorRange extensions(); + inline IteratorRange extensions() const; + + // Iterators for types, constants and global variables instructions. + inline Module::inst_iterator types_values_begin(); + inline Module::inst_iterator types_values_end(); + inline IteratorRange types_values(); + inline IteratorRange types_values() const; + + // Iterators for ext_inst import instructions contained in this module. + inline Module::inst_iterator ext_inst_import_begin(); + inline Module::inst_iterator ext_inst_import_end(); + inline IteratorRange ext_inst_imports(); + inline IteratorRange ext_inst_imports() const; + + // There are several kinds of debug instructions, according to where they can + // appear in the logical layout of a module: + // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued + // - Section 7b: OpName, OpMemberName + // - Section 7c: OpModuleProcessed + // - Mostly anywhere: OpLine and OpNoLine + // + + // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7a. + inline Module::inst_iterator debug1_begin(); + inline Module::inst_iterator debug1_end(); + inline IteratorRange debugs1(); + inline IteratorRange debugs1() const; + + // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7b. + inline Module::inst_iterator debug2_begin(); + inline Module::inst_iterator debug2_end(); + inline IteratorRange debugs2(); + inline IteratorRange debugs2() const; + + // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7c. + inline Module::inst_iterator debug3_begin(); + inline Module::inst_iterator debug3_end(); + inline IteratorRange debugs3(); + inline IteratorRange debugs3() const; + + // Iterators for debug info instructions (excluding OpLine & OpNoLine) + // contained in this module. These are OpExtInst & + // OpExtInstWithForwardRefsKHR for DebugInfo extension placed between section + // 9 and 10. + inline Module::inst_iterator ext_inst_debuginfo_begin(); + inline Module::inst_iterator ext_inst_debuginfo_end(); + inline IteratorRange ext_inst_debuginfo(); + inline IteratorRange ext_inst_debuginfo() const; + + // Add |capability| to the module, if it is not already enabled. + inline void AddCapability(spv::Capability capability); + // Appends a capability instruction to this module. + inline void AddCapability(std::unique_ptr&& c); + // Removes instruction declaring `capability` from this module. + // Returns true if the capability was removed, false otherwise. + bool RemoveCapability(spv::Capability capability); + + // Appends an extension instruction to this module. + inline void AddExtension(const std::string& ext_name); + inline void AddExtension(std::unique_ptr&& e); + // Removes instruction declaring `extension` from this module. + // Returns true if the extension was removed, false otherwise. + bool RemoveExtension(Extension extension); + + // Appends an extended instruction set instruction to this module. + inline void AddExtInstImport(const std::string& name); + inline void AddExtInstImport(std::unique_ptr&& e); + // Set the memory model for this module. + inline void SetMemoryModel(std::unique_ptr&& m); + // Get the memory model for this module. + inline const Instruction* GetMemoryModel() const; + // Appends an entry point instruction to this module. + inline void AddEntryPoint(std::unique_ptr&& e); + // Appends an execution mode instruction to this module. + inline void AddExecutionMode(std::unique_ptr&& e); + // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 1" instructions are the ones in layout section 7.a), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug1Inst(std::unique_ptr&& d); + // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 2" instructions are the ones in layout section 7.b), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug2Inst(std::unique_ptr&& d); + // Appends a debug 3 instruction (OpModuleProcessed) to this module. + // This is due to decision by the SPIR Working Group, pending publication. + inline void AddDebug3Inst(std::unique_ptr&& d); + // Appends a OpExtInst for DebugInfo to this module. + inline void AddExtInstDebugInfo(std::unique_ptr&& d); + // Appends an annotation instruction to this module. + inline void AddAnnotationInst(std::unique_ptr&& a); + // Appends a type-declaration instruction to this module. + inline void AddType(std::unique_ptr&& t); + // Appends a constant, global variable, or OpUndef instruction to this module. + inline void AddGlobalValue(std::unique_ptr&& v); + // Prepends a function declaration to this module. + inline void AddFunctionDeclaration(std::unique_ptr&& f); + // Appends a function to this module. + inline void AddFunction(std::unique_ptr&& f); + + // Returns a pointer to a def-use manager. If the def-use manager is + // invalid, it is rebuilt first. + analysis::DefUseManager* get_def_use_mgr() { + if (!AreAnalysesValid(kAnalysisDefUse)) { + BuildDefUseManager(); + } + return def_use_mgr_.get(); + } + + // Returns a pointer to a liveness manager. If the liveness manager is + // invalid, it is rebuilt first. + analysis::LivenessManager* get_liveness_mgr() { + if (!AreAnalysesValid(kAnalysisLiveness)) { + BuildLivenessManager(); + } + return liveness_mgr_.get(); + } + + // Returns a pointer to a value number table. If the liveness analysis is + // invalid, it is rebuilt first. + ValueNumberTable* GetValueNumberTable() { + if (!AreAnalysesValid(kAnalysisValueNumberTable)) { + BuildValueNumberTable(); + } + return vn_table_.get(); + } + + // Returns a pointer to a StructuredCFGAnalysis. If the analysis is invalid, + // it is rebuilt first. + StructuredCFGAnalysis* GetStructuredCFGAnalysis() { + if (!AreAnalysesValid(kAnalysisStructuredCFG)) { + BuildStructuredCFGAnalysis(); + } + return struct_cfg_analysis_.get(); + } + + // Returns a pointer to a liveness analysis. If the liveness analysis is + // invalid, it is rebuilt first. + LivenessAnalysis* GetLivenessAnalysis() { + if (!AreAnalysesValid(kAnalysisRegisterPressure)) { + BuildRegPressureAnalysis(); + } + return reg_pressure_.get(); + } + + // Returns the basic block for instruction |instr|. Re-builds the instruction + // block map, if needed. + BasicBlock* get_instr_block(Instruction* instr) { + if (!AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + BuildInstrToBlockMapping(); + } + auto entry = instr_to_block_.find(instr); + return (entry != instr_to_block_.end()) ? entry->second : nullptr; + } + + // Returns the basic block for |id|. Re-builds the instruction block map, if + // needed. + // + // |id| must be a registered definition. + BasicBlock* get_instr_block(uint32_t id) { + Instruction* def = get_def_use_mgr()->GetDef(id); + return get_instr_block(def); + } + + // Sets the basic block for |inst|. Re-builds the mapping if it has become + // invalid. + void set_instr_block(Instruction* inst, BasicBlock* block) { + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + instr_to_block_[inst] = block; + } + } + + // Returns a pointer the decoration manager. If the decoration manager is + // invalid, it is rebuilt first. + analysis::DecorationManager* get_decoration_mgr() { + if (!AreAnalysesValid(kAnalysisDecorations)) { + BuildDecorationManager(); + } + return decoration_mgr_.get(); + } + + // Returns a pointer to the constant manager. If no constant manager has been + // created yet, it creates one. NOTE: Once created, the constant manager + // remains active and it is never re-built. + analysis::ConstantManager* get_constant_mgr() { + if (!AreAnalysesValid(kAnalysisConstants)) { + BuildConstantManager(); + } + return constant_mgr_.get(); + } + + // Returns a pointer to the type manager. If no type manager has been created + // yet, it creates one. NOTE: Once created, the type manager remains active it + // is never re-built. + analysis::TypeManager* get_type_mgr() { + if (!AreAnalysesValid(kAnalysisTypes)) { + BuildTypeManager(); + } + return type_mgr_.get(); + } + + // Returns a pointer to the debug information manager. If no debug + // information manager has been created yet, it creates one. + // NOTE: Once created, the debug information manager remains active + // it is never re-built. + analysis::DebugInfoManager* get_debug_info_mgr() { + if (!AreAnalysesValid(kAnalysisDebugInfo)) { + BuildDebugInfoManager(); + } + return debug_info_mgr_.get(); + } + + // Returns a pointer to the scalar evolution analysis. If it is invalid it + // will be rebuilt first. + ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() { + if (!AreAnalysesValid(kAnalysisScalarEvolution)) { + BuildScalarEvolutionAnalysis(); + } + return scalar_evolution_analysis_.get(); + } + + // Build the map from the ids to the OpName and OpMemberName instruction + // associated with it. + inline void BuildIdToNameMap(); + + // Returns a range of instrucions that contain all of the OpName and + // OpMemberNames associated with the given id. + inline IteratorRange::iterator> + GetNames(uint32_t id); + + // Returns an OpMemberName instruction that targets |struct_type_id| at + // index |index|. Returns nullptr if no such instruction exists. + // While the SPIR-V spec does not prohibit having multiple OpMemberName + // instructions for the same structure member, it is hard to imagine a member + // having more than one name. This method returns the first one it finds. + inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index); + + // Copy names from |old_id| to |new_id|. Only copy member name if index is + // less than |max_member_index|. + inline void CloneNames(const uint32_t old_id, const uint32_t new_id, + const uint32_t max_member_index = UINT32_MAX); + + // Sets the message consumer to the given |consumer|. |consumer| which will be + // invoked every time there is a message to be communicated to the outside. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Returns the reference to the message consumer for this pass. + const MessageConsumer& consumer() const { return consumer_; } + + // Rebuilds the analyses in |set| that are invalid. + void BuildInvalidAnalyses(Analysis set); + + // Invalidates all of the analyses except for those in |preserved_analyses|. + void InvalidateAnalysesExceptFor(Analysis preserved_analyses); + + // Invalidates the analyses marked in |analyses_to_invalidate|. + void InvalidateAnalyses(Analysis analyses_to_invalidate); + + // Deletes the instruction defining the given |id|. Returns true on + // success, false if the given |id| is not defined at all. This method also + // erases the name, decorations, and definition of |id|. + // + // Pointers and iterators pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + bool KillDef(uint32_t id); + + // Deletes the given instruction |inst|. This method erases the + // information of the given instruction's uses of its operands. If |inst| + // defines a result id, its name and decorations will also be deleted. + // + // Pointer and iterator pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + // + // Note that if an instruction is not in an instruction list, the memory may + // not be safe to delete, so the instruction is turned into a OpNop instead. + // This can happen with OpLabel. + // + // Returns a pointer to the instruction after |inst| or |nullptr| if no such + // instruction exists. + Instruction* KillInst(Instruction* inst); + + // Deletes all the instruction in the range [`begin`; `end`[, for which the + // unary predicate `condition` returned true. + // Returns true if at least one instruction was removed, false otherwise. + // + // Pointer and iterator pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + bool KillInstructionIf(Module::inst_iterator begin, Module::inst_iterator end, + std::function condition); + + // Collects the non-semantic instruction tree that uses |inst|'s result id + // to be killed later. + void CollectNonSemanticTree(Instruction* inst, + std::unordered_set* to_kill); + + // Collect function reachable from |entryId|, returns |funcs| + void CollectCallTreeFromRoots(unsigned entryId, + std::unordered_set* funcs); + + // Returns true if all of the given analyses are valid. + bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; } + + // Replaces all uses of |before| id with |after| id. Returns true if any + // replacement happens. This method does not kill the definition of the + // |before| id. If |after| is the same as |before|, does nothing and returns + // false. + // + // |before| and |after| must be registered definitions in the DefUseManager. + bool ReplaceAllUsesWith(uint32_t before, uint32_t after); + + // Replace all uses of |before| id with |after| id if those uses + // (instruction) return true for |predicate|. Returns true if + // any replacement happens. This method does not kill the definition of the + // |before| id. If |after| is the same as |before|, does nothing and return + // false. + bool ReplaceAllUsesWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate); + + // Returns true if all of the analyses that are suppose to be valid are + // actually valid. + bool IsConsistent(); + + // The IRContext will look at the def and uses of |inst| and update any valid + // analyses will be updated accordingly. + inline void AnalyzeDefUse(Instruction* inst); + + // Informs the IRContext that the uses of |inst| are going to change, and that + // is should forget everything it know about the current uses. Any valid + // analyses will be updated accordingly. + void ForgetUses(Instruction* inst); + + // The IRContext will look at the uses of |inst| and update any valid analyses + // will be updated accordingly. + void AnalyzeUses(Instruction* inst); + + // Kill all name and decorate ops targeting |id|. + void KillNamesAndDecorates(uint32_t id); + + // Kill all name and decorate ops targeting the result id of |inst|. + void KillNamesAndDecorates(Instruction* inst); + + // Change operands of debug instruction to DebugInfoNone. + void KillOperandFromDebugInstructions(Instruction* inst); + + // Returns the next unique id for use by an instruction. + inline uint32_t TakeNextUniqueId() { + assert(unique_id_ != std::numeric_limits::max()); + + // Skip zero. + return ++unique_id_; + } + + // Returns true if |inst| is a combinator in the current context. + // |combinator_ops_| is built if it has not been already. + inline bool IsCombinatorInstruction(const Instruction* inst) { + if (!AreAnalysesValid(kAnalysisCombinators)) { + InitializeCombinators(); + } + constexpr uint32_t kExtInstSetIdInIndx = 0; + constexpr uint32_t kExtInstInstructionInIndx = 1; + + if (inst->opcode() != spv::Op::OpExtInst) { + return combinator_ops_[0].count(uint32_t(inst->opcode())) != 0; + } else { + uint32_t set = inst->GetSingleWordInOperand(kExtInstSetIdInIndx); + auto op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx); + return combinator_ops_[set].count(op) != 0; + } + } + + // Returns a pointer to the CFG for all the functions in |module_|. + CFG* cfg() { + if (!AreAnalysesValid(kAnalysisCFG)) { + BuildCFG(); + } + return cfg_.get(); + } + + // Gets the loop descriptor for function |f|. + LoopDescriptor* GetLoopDescriptor(const Function* f); + + // Gets the dominator analysis for function |f|. + DominatorAnalysis* GetDominatorAnalysis(const Function* f); + + // Gets the postdominator analysis for function |f|. + PostDominatorAnalysis* GetPostDominatorAnalysis(const Function* f); + + // Remove the dominator tree of |f| from the cache. + inline void RemoveDominatorAnalysis(const Function* f) { + dominator_trees_.erase(f); + } + + // Remove the postdominator tree of |f| from the cache. + inline void RemovePostDominatorAnalysis(const Function* f) { + post_dominator_trees_.erase(f); + } + + // Return the next available SSA id and increment it. Returns 0 if the + // maximum SSA id has been reached. + inline uint32_t TakeNextId() { + uint32_t next_id = module()->TakeNextIdBound(); + if (next_id == 0) { + if (consumer()) { + std::string message = "ID overflow. Try running compact-ids."; + consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // If TakeNextId returns 0, it is very likely that execution will + // subsequently fail. Such failures are false alarms from a fuzzing point + // of view: they are due to the fact that too many ids were used, rather + // than being due to an actual bug. Thus, during a fuzzing build, it is + // preferable to bail out when ID overflow occurs. + // + // A zero exit code is returned here because a non-zero code would cause + // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious + // crash reports is what this guard aims to avoid. + exit(0); +#endif + } + return next_id; + } + + FeatureManager* get_feature_mgr() { + if (!feature_mgr_.get()) { + AnalyzeFeatures(); + } + return feature_mgr_.get(); + } + + void ResetFeatureManager() { feature_mgr_.reset(nullptr); } + + // Returns the grammar for this context. + const AssemblyGrammar& grammar() const { return grammar_; } + + // If |inst| has not yet been analysed by the def-use manager, then analyse + // its definitions and uses. + inline void UpdateDefUse(Instruction* inst); + + const InstructionFolder& get_instruction_folder() { + if (!inst_folder_) { + inst_folder_ = MakeUnique(this); + } + return *inst_folder_; + } + + uint32_t max_id_bound() const { return max_id_bound_; } + void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; } + + bool preserve_bindings() const { return preserve_bindings_; } + void set_preserve_bindings(bool should_preserve_bindings) { + preserve_bindings_ = should_preserve_bindings; + } + + bool preserve_spec_constants() const { return preserve_spec_constants_; } + void set_preserve_spec_constants(bool should_preserve_spec_constants) { + preserve_spec_constants_ = should_preserve_spec_constants; + } + + // Return id of input variable only decorated with |builtin|, if in module. + // Create variable and return its id otherwise. If builtin not currently + // supported, return 0. + uint32_t GetBuiltinInputVarId(uint32_t builtin); + + // Returns the function whose id is |id|, if one exists. Returns |nullptr| + // otherwise. + Function* GetFunction(uint32_t id) { + if (!AreAnalysesValid(kAnalysisIdToFuncMapping)) { + BuildIdToFuncMapping(); + } + auto entry = id_to_func_.find(id); + return (entry != id_to_func_.end()) ? entry->second : nullptr; + } + + Function* GetFunction(Instruction* inst) { + if (inst->opcode() != spv::Op::OpFunction) { + return nullptr; + } + return GetFunction(inst->result_id()); + } + + // Add to |todo| all ids of functions called directly from |func|. + void AddCalls(const Function* func, std::queue* todo); + + // Applies |pfn| to every function in the call trees that are rooted at the + // entry points. Returns true if any call |pfn| returns true. By convention + // |pfn| should return true if it modified the module. + bool ProcessEntryPointCallTree(ProcessFunction& pfn); + + // Applies |pfn| to every function in the call trees rooted at the entry + // points and exported functions. Returns true if any call |pfn| returns + // true. By convention |pfn| should return true if it modified the module. + bool ProcessReachableCallTree(ProcessFunction& pfn); + + // Applies |pfn| to every function in the call trees rooted at the elements of + // |roots|. Returns true if any call to |pfn| returns true. By convention + // |pfn| should return true if it modified the module. After returning + // |roots| will be empty. + bool ProcessCallTreeFromRoots(ProcessFunction& pfn, + std::queue* roots); + + // Emits a error message to the message consumer indicating the error + // described by |message| occurred in |inst|. + void EmitErrorMessage(std::string message, Instruction* inst); + + // Returns true if and only if there is a path to |bb| from the entry block of + // the function that contains |bb|. + bool IsReachable(const opt::BasicBlock& bb); + + // Return the stage of the module. Will generate error if entry points don't + // all have the same stage. + spv::ExecutionModel GetStage(); + + // Returns true of the current target environment is at least that of the + // given environment. + bool IsTargetEnvAtLeast(spv_target_env env) { + // A bit of a hack. We assume that the target environments are appended to + // the enum, so that there is an appropriate order. + return syntax_context_->target_env >= env; + } + + // Return the target environment for the current context. + spv_target_env GetTargetEnv() const { return syntax_context_->target_env; } + + private: + // Builds the def-use manager from scratch, even if it was already valid. + void BuildDefUseManager() { + def_use_mgr_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisDefUse; + } + + // Builds the liveness manager from scratch, even if it was already valid. + void BuildLivenessManager() { + liveness_mgr_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisLiveness; + } + + // Builds the instruction-block map for the whole module. + void BuildInstrToBlockMapping() { + instr_to_block_.clear(); + for (auto& fn : *module_) { + for (auto& block : fn) { + block.ForEachInst([this, &block](Instruction* inst) { + instr_to_block_[inst] = █ + }); + } + } + valid_analyses_ = valid_analyses_ | kAnalysisInstrToBlockMapping; + } + + // Builds the instruction-function map for the whole module. + void BuildIdToFuncMapping() { + id_to_func_.clear(); + for (auto& fn : *module_) { + id_to_func_[fn.result_id()] = &fn; + } + valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping; + } + + void BuildDecorationManager() { + decoration_mgr_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisDecorations; + } + + void BuildCFG() { + cfg_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisCFG; + } + + void BuildScalarEvolutionAnalysis() { + scalar_evolution_analysis_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisScalarEvolution; + } + + // Builds the liveness analysis from scratch, even if it was already valid. + void BuildRegPressureAnalysis() { + reg_pressure_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisRegisterPressure; + } + + // Builds the value number table analysis from scratch, even if it was already + // valid. + void BuildValueNumberTable() { + vn_table_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisValueNumberTable; + } + + // Builds the structured CFG analysis from scratch, even if it was already + // valid. + void BuildStructuredCFGAnalysis() { + struct_cfg_analysis_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisStructuredCFG; + } + + // Builds the constant manager from scratch, even if it was already + // valid. + void BuildConstantManager() { + constant_mgr_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisConstants; + } + + // Builds the type manager from scratch, even if it was already + // valid. + void BuildTypeManager() { + type_mgr_ = MakeUnique(consumer(), this); + valid_analyses_ = valid_analyses_ | kAnalysisTypes; + } + + // Builds the debug information manager from scratch, even if it was + // already valid. + void BuildDebugInfoManager() { + debug_info_mgr_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisDebugInfo; + } + + // Removes all computed dominator and post-dominator trees. This will force + // the context to rebuild the trees on demand. + void ResetDominatorAnalysis() { + // Clear the cache. + dominator_trees_.clear(); + post_dominator_trees_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisDominatorAnalysis; + } + + // Removes all computed loop descriptors. + void ResetLoopAnalysis() { + // Clear the cache. + loop_descriptors_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis; + } + + // Removes all computed loop descriptors. + void ResetBuiltinAnalysis() { + // Clear the cache. + builtin_var_id_map_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisBuiltinVarId; + } + + // Analyzes the features in the owned module. Builds the manager if required. + void AnalyzeFeatures() { + feature_mgr_ = + std::unique_ptr(new FeatureManager(grammar_)); + feature_mgr_->Analyze(module()); + } + + // Scans a module looking for it capabilities, and initializes combinator_ops_ + // accordingly. + void InitializeCombinators(); + + // Add the combinator opcode for the given capability to combinator_ops_. + void AddCombinatorsForCapability(uint32_t capability); + + // Add the combinator opcode for the given extension to combinator_ops_. + void AddCombinatorsForExtension(Instruction* extension); + + // Remove |inst| from |id_to_name_| if it is in map. + void RemoveFromIdToName(const Instruction* inst); + + // Returns true if it is suppose to be valid but it is incorrect. Returns + // true if the cfg is invalidated. + bool CheckCFG(); + + // Return id of input variable only decorated with |builtin|, if in module. + // Return 0 otherwise. + uint32_t FindBuiltinInputVar(uint32_t builtin); + + // Add |var_id| to all entry points in module. + void AddVarToEntryPoints(uint32_t var_id); + + // The SPIR-V syntax context containing grammar tables for opcodes and + // operands. + spv_context syntax_context_; + + // Auxiliary object for querying SPIR-V grammar facts. + AssemblyGrammar grammar_; + + // An unique identifier for instructions in |module_|. Can be used to order + // instructions in a container. + // + // This member is initialized to 0, but always issues this value plus one. + // Therefore, 0 is not a valid unique id for an instruction. + uint32_t unique_id_; + + // The module being processed within this IR context. + std::unique_ptr module_; + + // A message consumer for diagnostics. + MessageConsumer consumer_; + + // The def-use manager for |module_|. + std::unique_ptr def_use_mgr_; + + // The instruction decoration manager for |module_|. + std::unique_ptr decoration_mgr_; + + // The feature manager for |module_|. + std::unique_ptr feature_mgr_; + + // A map from instructions to the basic block they belong to. This mapping is + // built on-demand when get_instr_block() is called. + // + // NOTE: Do not traverse this map. Ever. Use the function and basic block + // iterators to traverse instructions. + std::unordered_map instr_to_block_; + + // A map from ids to the function they define. This mapping is + // built on-demand when GetFunction() is called. + // + // NOTE: Do not traverse this map. Ever. Use the function and basic block + // iterators to traverse instructions. + std::unordered_map id_to_func_; + + // A bitset indicating which analyzes are currently valid. + Analysis valid_analyses_; + + // Opcodes of shader capability core executable instructions + // without side-effect. + std::unordered_map> combinator_ops_; + + // Opcodes of shader capability core executable instructions + // without side-effect. + std::unordered_map builtin_var_id_map_; + + // The CFG for all the functions in |module_|. + std::unique_ptr cfg_; + + // Each function in the module will create its own dominator tree. We cache + // the result so it doesn't need to be rebuilt each time. + std::map dominator_trees_; + std::map post_dominator_trees_; + + // Cache of loop descriptors for each function. + std::unordered_map loop_descriptors_; + + // Constant manager for |module_|. + std::unique_ptr constant_mgr_; + + // Type manager for |module_|. + std::unique_ptr type_mgr_; + + // Debug information manager for |module_|. + std::unique_ptr debug_info_mgr_; + + // A map from an id to its corresponding OpName and OpMemberName instructions. + std::unique_ptr> id_to_name_; + + // The cache scalar evolution analysis node. + std::unique_ptr scalar_evolution_analysis_; + + // The liveness analysis |module_|. + std::unique_ptr reg_pressure_; + + std::unique_ptr vn_table_; + + std::unique_ptr inst_folder_; + + std::unique_ptr struct_cfg_analysis_; + + // The liveness manager for |module_|. + std::unique_ptr liveness_mgr_; + + // The maximum legal value for the id bound. + uint32_t max_id_bound_; + + // Whether all bindings within |module_| should be preserved. + bool preserve_bindings_; + + // Whether all specialization constants within |module_| + // should be preserved. + bool preserve_spec_constants_; +}; + +inline IRContext::Analysis operator|(IRContext::Analysis lhs, + IRContext::Analysis rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs, + IRContext::Analysis rhs) { + lhs = lhs | rhs; + return lhs; +} + +inline IRContext::Analysis operator<<(IRContext::Analysis a, int shift) { + return static_cast(static_cast(a) << shift); +} + +inline IRContext::Analysis& operator<<=(IRContext::Analysis& a, int shift) { + a = static_cast(static_cast(a) << shift); + return a; +} + +std::vector IRContext::GetConstants() { + return module()->GetConstants(); +} + +std::vector IRContext::GetConstants() const { + return ((const Module*)module())->GetConstants(); +} + +Module::inst_iterator IRContext::annotation_begin() { + return module()->annotation_begin(); +} + +Module::inst_iterator IRContext::annotation_end() { + return module()->annotation_end(); +} + +IteratorRange IRContext::annotations() { + return module_->annotations(); +} + +IteratorRange IRContext::annotations() const { + return ((const Module*)module_.get())->annotations(); +} + +Module::inst_iterator IRContext::capability_begin() { + return module()->capability_begin(); +} + +Module::inst_iterator IRContext::capability_end() { + return module()->capability_end(); +} + +IteratorRange IRContext::capabilities() { + return module()->capabilities(); +} + +IteratorRange IRContext::capabilities() const { + return ((const Module*)module())->capabilities(); +} + +Module::inst_iterator IRContext::extension_begin() { + return module()->extension_begin(); +} + +Module::inst_iterator IRContext::extension_end() { + return module()->extension_end(); +} + +IteratorRange IRContext::extensions() { + return module()->extensions(); +} + +IteratorRange IRContext::extensions() const { + return ((const Module*)module())->extensions(); +} + +Module::inst_iterator IRContext::types_values_begin() { + return module()->types_values_begin(); +} + +Module::inst_iterator IRContext::types_values_end() { + return module()->types_values_end(); +} + +IteratorRange IRContext::types_values() { + return module()->types_values(); +} + +IteratorRange IRContext::types_values() const { + return ((const Module*)module_.get())->types_values(); +} + +Module::inst_iterator IRContext::ext_inst_import_begin() { + return module()->ext_inst_import_begin(); +} + +Module::inst_iterator IRContext::ext_inst_import_end() { + return module()->ext_inst_import_end(); +} + +IteratorRange IRContext::ext_inst_imports() { + return module()->ext_inst_imports(); +} + +IteratorRange IRContext::ext_inst_imports() const { + return ((const Module*)module_.get())->ext_inst_imports(); +} + +Module::inst_iterator IRContext::debug1_begin() { + return module()->debug1_begin(); +} + +Module::inst_iterator IRContext::debug1_end() { return module()->debug1_end(); } + +IteratorRange IRContext::debugs1() { + return module()->debugs1(); +} + +IteratorRange IRContext::debugs1() const { + return ((const Module*)module_.get())->debugs1(); +} + +Module::inst_iterator IRContext::debug2_begin() { + return module()->debug2_begin(); +} +Module::inst_iterator IRContext::debug2_end() { return module()->debug2_end(); } + +IteratorRange IRContext::debugs2() { + return module()->debugs2(); +} + +IteratorRange IRContext::debugs2() const { + return ((const Module*)module_.get())->debugs2(); +} + +Module::inst_iterator IRContext::debug3_begin() { + return module()->debug3_begin(); +} + +Module::inst_iterator IRContext::debug3_end() { return module()->debug3_end(); } + +IteratorRange IRContext::debugs3() { + return module()->debugs3(); +} + +IteratorRange IRContext::debugs3() const { + return ((const Module*)module_.get())->debugs3(); +} + +Module::inst_iterator IRContext::ext_inst_debuginfo_begin() { + return module()->ext_inst_debuginfo_begin(); +} + +Module::inst_iterator IRContext::ext_inst_debuginfo_end() { + return module()->ext_inst_debuginfo_end(); +} + +IteratorRange IRContext::ext_inst_debuginfo() { + return module()->ext_inst_debuginfo(); +} + +IteratorRange IRContext::ext_inst_debuginfo() + const { + return ((const Module*)module_.get())->ext_inst_debuginfo(); +} + +void IRContext::AddCapability(spv::Capability capability) { + if (!get_feature_mgr()->HasCapability(capability)) { + std::unique_ptr capability_inst(new Instruction( + this, spv::Op::OpCapability, 0, 0, + {{SPV_OPERAND_TYPE_CAPABILITY, {static_cast(capability)}}})); + AddCapability(std::move(capability_inst)); + } +} + +void IRContext::AddCapability(std::unique_ptr&& c) { + AddCombinatorsForCapability(c->GetSingleWordInOperand(0)); + if (feature_mgr_ != nullptr) { + feature_mgr_->AddCapability( + static_cast(c->GetSingleWordInOperand(0))); + } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(c.get()); + } + module()->AddCapability(std::move(c)); +} + +void IRContext::AddExtension(const std::string& ext_name) { + std::vector ext_words = spvtools::utils::MakeVector(ext_name); + AddExtension(std::unique_ptr( + new Instruction(this, spv::Op::OpExtension, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); +} + +void IRContext::AddExtension(std::unique_ptr&& e) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(e.get()); + } + if (feature_mgr_ != nullptr) { + feature_mgr_->AddExtension(&*e); + } + module()->AddExtension(std::move(e)); +} + +void IRContext::AddExtInstImport(const std::string& name) { + std::vector ext_words = spvtools::utils::MakeVector(name); + AddExtInstImport(std::unique_ptr( + new Instruction(this, spv::Op::OpExtInstImport, 0u, TakeNextId(), + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); +} + +void IRContext::AddExtInstImport(std::unique_ptr&& e) { + AddCombinatorsForExtension(e.get()); + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(e.get()); + } + module()->AddExtInstImport(std::move(e)); + if (feature_mgr_ != nullptr) { + feature_mgr_->AddExtInstImportIds(module()); + } +} + +void IRContext::SetMemoryModel(std::unique_ptr&& m) { + module()->SetMemoryModel(std::move(m)); +} + +const Instruction* IRContext::GetMemoryModel() const { + return module()->GetMemoryModel(); +} + +void IRContext::AddEntryPoint(std::unique_ptr&& e) { + module()->AddEntryPoint(std::move(e)); +} + +void IRContext::AddExecutionMode(std::unique_ptr&& e) { + module()->AddExecutionMode(std::move(e)); +} + +void IRContext::AddDebug1Inst(std::unique_ptr&& d) { + module()->AddDebug1Inst(std::move(d)); +} + +void IRContext::AddDebug2Inst(std::unique_ptr&& d) { + if (AreAnalysesValid(kAnalysisNameMap)) { + if (d->opcode() == spv::Op::OpName || + d->opcode() == spv::Op::OpMemberName) { + // OpName and OpMemberName do not have result-ids. The target of the + // instruction is at InOperand index 0. + id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()}); + } + } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(d.get()); + } + module()->AddDebug2Inst(std::move(d)); +} + +void IRContext::AddDebug3Inst(std::unique_ptr&& d) { + module()->AddDebug3Inst(std::move(d)); +} + +void IRContext::AddExtInstDebugInfo(std::unique_ptr&& d) { + module()->AddExtInstDebugInfo(std::move(d)); +} + +void IRContext::AddAnnotationInst(std::unique_ptr&& a) { + if (AreAnalysesValid(kAnalysisDecorations)) { + get_decoration_mgr()->AddDecoration(a.get()); + } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(a.get()); + } + module()->AddAnnotationInst(std::move(a)); +} + +void IRContext::AddType(std::unique_ptr&& t) { + module()->AddType(std::move(t)); + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(&*(--types_values_end())); + } +} + +void IRContext::AddGlobalValue(std::unique_ptr&& v) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(&*v); + } + module()->AddGlobalValue(std::move(v)); +} + +void IRContext::AddFunctionDeclaration(std::unique_ptr&& f) { + module()->AddFunctionDeclaration(std::move(f)); +} + +void IRContext::AddFunction(std::unique_ptr&& f) { + module()->AddFunction(std::move(f)); +} + +void IRContext::AnalyzeDefUse(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(inst); + } +} + +void IRContext::UpdateDefUse(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->UpdateDefUse(inst); + } +} + +void IRContext::BuildIdToNameMap() { + id_to_name_ = MakeUnique>(); + for (Instruction& debug_inst : debugs2()) { + if (debug_inst.opcode() == spv::Op::OpMemberName || + debug_inst.opcode() == spv::Op::OpName) { + id_to_name_->insert({debug_inst.GetSingleWordInOperand(0), &debug_inst}); + } + } + valid_analyses_ = valid_analyses_ | kAnalysisNameMap; +} + +IteratorRange::iterator> +IRContext::GetNames(uint32_t id) { + if (!AreAnalysesValid(kAnalysisNameMap)) { + BuildIdToNameMap(); + } + auto result = id_to_name_->equal_range(id); + return make_range(std::move(result.first), std::move(result.second)); +} + +Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) { + if (!AreAnalysesValid(kAnalysisNameMap)) { + BuildIdToNameMap(); + } + auto result = id_to_name_->equal_range(struct_type_id); + for (auto i = result.first; i != result.second; ++i) { + auto* name_instr = i->second; + if (name_instr->opcode() == spv::Op::OpMemberName && + name_instr->GetSingleWordInOperand(1) == index) { + return name_instr; + } + } + return nullptr; +} + +void IRContext::CloneNames(const uint32_t old_id, const uint32_t new_id, + const uint32_t max_member_index) { + std::vector> names_to_add; + auto names = GetNames(old_id); + for (auto n : names) { + Instruction* old_name_inst = n.second; + if (old_name_inst->opcode() == spv::Op::OpMemberName) { + auto midx = old_name_inst->GetSingleWordInOperand(1); + if (midx >= max_member_index) continue; + } + std::unique_ptr new_name_inst(old_name_inst->Clone(this)); + new_name_inst->SetInOperand(0, {new_id}); + names_to_add.push_back(std::move(new_name_inst)); + } + // We can't add the new names when we are iterating over name range above. + // We can add all the new names now. + for (auto& new_name : names_to_add) AddDebug2Inst(std::move(new_name)); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_CONTEXT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.cpp new file mode 100644 index 00000000..567b180b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.cpp @@ -0,0 +1,371 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/ir_loader.h" + +#include + +#include "DebugInfo.h" +#include "OpenCLDebugInfo100.h" +#include "source/ext_inst.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kExtInstSetIndex = 4; +constexpr uint32_t kLexicalScopeIndex = 5; +constexpr uint32_t kInlinedAtIndex = 6; +} // namespace + +IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) + : consumer_(consumer), + module_(m), + source_(""), + inst_index_(0), + last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +bool IsLineInst(const spv_parsed_instruction_t* inst) { + const auto opcode = static_cast(inst->opcode); + if (IsOpLineInst(opcode)) return true; + if (!spvIsExtendedInstruction(opcode)) return false; + if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) + return false; + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine; +} + +bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { + ++inst_index_; + if (IsLineInst(inst)) { + module()->SetContainsDebugInfo(); + last_line_inst_.reset(); + dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_); + return true; + } + + // If it is a DebugScope or DebugNoScope of debug extension, we do not + // create a new instruction, but simply keep the information in + // struct DebugScope. + const auto opcode = static_cast(inst->opcode); + if (spvIsExtendedInstruction(opcode) && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + if (ext_inst_key == CommonDebugInfoDebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugInfo(); + return true; + } + if (ext_inst_key == CommonDebugInfoDebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugInfo(); + return true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugInfo(); + return true; + } + if (ext_inst_key == DebugInfoDebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugInfo(); + return true; + } + } + } + + std::unique_ptr spv_inst( + new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); + if (!spv_inst->dbg_line_insts().empty()) { + if (extra_line_tracking_ && + (!spv_inst->dbg_line_insts().back().IsNoLine())) { + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); + } + dbg_line_info_.clear(); + } else if (last_line_inst_ != nullptr) { + last_line_inst_->SetDebugScope(last_dbg_scope_); + spv_inst->dbg_line_insts().push_back(*last_line_inst_); + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + if (last_line_inst_->IsDebugLineInst()) + last_line_inst_->SetResultId(module()->context()->TakeNextId()); + } + + const char* src = source_.c_str(); + spv_position_t loc = {inst_index_, 0, 0}; + + // Handle function and basic block boundaries first, then normal + // instructions. + if (opcode == spv::Op::OpFunction) { + if (function_ != nullptr) { + Error(consumer_, src, loc, "function inside function"); + return false; + } + function_ = MakeUnique(std::move(spv_inst)); + } else if (opcode == spv::Op::OpFunctionEnd) { + if (function_ == nullptr) { + Error(consumer_, src, loc, + "OpFunctionEnd without corresponding OpFunction"); + return false; + } + if (block_ != nullptr) { + Error(consumer_, src, loc, "OpFunctionEnd inside basic block"); + return false; + } + function_->SetFunctionEnd(std::move(spv_inst)); + module_->AddFunction(std::move(function_)); + function_ = nullptr; + } else if (opcode == spv::Op::OpLabel) { + if (function_ == nullptr) { + Error(consumer_, src, loc, "OpLabel outside function"); + return false; + } + if (block_ != nullptr) { + Error(consumer_, src, loc, "OpLabel inside basic block"); + return false; + } + block_ = MakeUnique(std::move(spv_inst)); + } else if (spvOpcodeIsBlockTerminator(opcode)) { + if (function_ == nullptr) { + Error(consumer_, src, loc, "terminator instruction outside function"); + return false; + } + if (block_ == nullptr) { + Error(consumer_, src, loc, "terminator instruction outside basic block"); + return false; + } + if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) + spv_inst->SetDebugScope(last_dbg_scope_); + block_->AddInstruction(std::move(spv_inst)); + function_->AddBasicBlock(std::move(block_)); + block_ = nullptr; + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + last_line_inst_.reset(); + dbg_line_info_.clear(); + } else { + if (function_ == nullptr) { // Outside function definition + SPIRV_ASSERT(consumer_, block_ == nullptr); + if (opcode == spv::Op::OpCapability) { + module_->AddCapability(std::move(spv_inst)); + } else if (opcode == spv::Op::OpExtension) { + module_->AddExtension(std::move(spv_inst)); + } else if (opcode == spv::Op::OpExtInstImport) { + module_->AddExtInstImport(std::move(spv_inst)); + } else if (opcode == spv::Op::OpMemoryModel) { + module_->SetMemoryModel(std::move(spv_inst)); + } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) { + module_->SetSampledImageAddressMode(std::move(spv_inst)); + } else if (opcode == spv::Op::OpEntryPoint) { + module_->AddEntryPoint(std::move(spv_inst)); + } else if (opcode == spv::Op::OpExecutionMode || + opcode == spv::Op::OpExecutionModeId) { + module_->AddExecutionMode(std::move(spv_inst)); + } else if (IsDebug1Inst(opcode)) { + module_->AddDebug1Inst(std::move(spv_inst)); + } else if (IsDebug2Inst(opcode)) { + module_->AddDebug2Inst(std::move(spv_inst)); + } else if (IsDebug3Inst(opcode)) { + module_->AddDebug3Inst(std::move(spv_inst)); + } else if (IsAnnotationInst(opcode)) { + module_->AddAnnotationInst(std::move(spv_inst)); + } else if (IsTypeInst(opcode)) { + module_->AddType(std::move(spv_inst)); + } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable || + opcode == spv::Op::OpUntypedVariableKHR || + opcode == spv::Op::OpUndef) { + module_->AddGlobalValue(std::move(spv_inst)); + } else if (spvIsExtendedInstruction(opcode) && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + module_->AddExtInstDebugInfo(std::move(spv_inst)); + } else if (spvIsExtendedInstruction(opcode) && + spvExtInstIsNonSemantic(inst->ext_inst_type)) { + // If there are no functions, add the non-semantic instructions to the + // global values. Otherwise append it to the list of the last function. + auto func_begin = module_->begin(); + auto func_end = module_->end(); + if (func_begin == func_end) { + module_->AddGlobalValue(std::move(spv_inst)); + } else { + (--func_end)->AddNonSemanticInstruction(std::move(spv_inst)); + } + } else { + Errorf(consumer_, src, loc, + "Unhandled inst type (opcode: %d) found outside function " + "definition.", + opcode); + return false; + } + } else { + if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) + spv_inst->SetDebugScope(last_dbg_scope_); + if (spvIsExtendedInstruction(opcode) && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case OpenCLDebugInfo100DebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case OpenCLDebugInfo100DebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and " + "DebugValue found inside function", + opcode); + return false; + } + } + } else if (inst->ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { + if (block_ == nullptr) { // Inside function but outside blocks + Errorf(consumer_, src, loc, + "Debug info extension instruction found inside function " + "but outside block", + opcode); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + break; + } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugDeclare, and DebugValue found inside " + "function", + opcode); + return false; + } + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case DebugInfoDebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case DebugInfoDebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugDeclare, and DebugValue found inside " + "function", + opcode); + return false; + } + } + } + } else { + if (block_ == nullptr) { // Inside function but outside blocks + if (opcode != spv::Op::OpFunctionParameter) { + Errorf(consumer_, src, loc, + "Non-OpFunctionParameter (opcode: %d) found inside " + "function but outside basic block", + opcode); + return false; + } + function_->AddParameter(std::move(spv_inst)); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + } + } + } + return true; +} + +// Resolves internal references among the module, functions, basic blocks, etc. +// This function should be called after adding all instructions. +void IrLoader::EndModule() { + if (block_ && function_) { + // We're in the middle of a basic block, but the terminator is missing. + // Register the block anyway. This lets us write tests with less + // boilerplate. + function_->AddBasicBlock(std::move(block_)); + block_ = nullptr; + } + if (function_) { + // We're in the middle of a function, but the OpFunctionEnd is missing. + // Register the function anyway. This lets us write tests with less + // boilerplate. + module_->AddFunction(std::move(function_)); + function_ = nullptr; + } + for (auto& function : *module_) { + for (auto& bb : function) bb.SetParent(&function); + } + + // Copy any trailing Op*Line instruction into the module + module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_)); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.h b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.h new file mode 100644 index 00000000..16bc2c7c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ir_loader.h @@ -0,0 +1,101 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_LOADER_H_ +#define SOURCE_OPT_IR_LOADER_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +// Loader class for constructing SPIR-V in-memory IR representation. Methods in +// this class are designed to work with the interface for spvBinaryParse() in +// libspirv.h so that we can leverage the syntax checks implemented behind it. +// +// The user is expected to call SetModuleHeader() to fill in the module's +// header, and then AddInstruction() for each decoded instruction, and finally +// EndModule() to finalize the module. The instructions processed in sequence +// by AddInstruction() should comprise a valid SPIR-V module. +class IrLoader { + public: + // Instantiates a builder to construct the given |module| gradually. + // All internal messages will be communicated to the outside via the given + // message |consumer|. This instance only keeps a reference to the |consumer|, + // so the |consumer| should outlive this instance. + IrLoader(const MessageConsumer& consumer, Module* m); + + // Sets the source name of the module. + void SetSource(const std::string& src) { source_ = src; } + + Module* module() const { return module_; } + + // Sets the fields in the module's header to the given parameters. + void SetModuleHeader(uint32_t magic, uint32_t version, uint32_t generator, + uint32_t bound, uint32_t reserved) { + module_->SetHeader({magic, version, generator, bound, reserved}); + } + // Adds an instruction to the module. Returns true if no error occurs. This + // method will properly capture and store the data provided in |inst| so that + // |inst| is no longer needed after returning. + bool AddInstruction(const spv_parsed_instruction_t* inst); + // Finalizes the module construction. This must be called after the module + // header has been set and all instructions have been added. This is + // forgiving in the case of a missing terminator instruction on a basic block, + // or a missing OpFunctionEnd. Resolves internal bookkeeping. + void EndModule(); + + // Sets whether extra OpLine instructions should be injected to better + // track line information. + void SetExtraLineTracking(bool flag) { extra_line_tracking_ = flag; } + + private: + // Consumer for communicating messages to outside. + const MessageConsumer& consumer_; + // The module to be built. + Module* module_; + // The source name of the module. + std::string source_; + // The last used instruction index. + uint32_t inst_index_; + // The current Function under construction. + std::unique_ptr function_; + // The current BasicBlock under construction. + std::unique_ptr block_; + // Line related debug instructions accumulated thus far. + std::vector dbg_line_info_; + // If doing extra line tracking, this is the line instruction that should be + // applied to the next instruction. Otherwise it always contains null. + std::unique_ptr last_line_inst_; + + // The last DebugScope information that IrLoader::AddInstruction() handled. + DebugScope last_dbg_scope_; + + // When true, do extra line information tracking: Additional OpLine + // instructions will be injected to help track line info more robustly during + // transformations. + bool extra_line_tracking_ = true; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_LOADER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/iterator.h b/engine/engine/3rdlibs/spirv-tools/source/opt/iterator.h new file mode 100644 index 00000000..847e1bbd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/iterator.h @@ -0,0 +1,350 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ITERATOR_H_ +#define SOURCE_OPT_ITERATOR_H_ + +#include // for ptrdiff_t +#include +#include +#include +#include +#include + +namespace spvtools { +namespace opt { + +// An ad hoc iterator class for std::vector>. The +// purpose of this iterator class is to provide transparent access to those +// std::unique_ptr managed elements in the vector, behaving like we are using +// std::vector<|ValueType|>. +template +class UptrVectorIterator { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = ValueType; + + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; + + // Type aliases. We need to apply constness properly if |IsConst| is true. + using Uptr = std::unique_ptr; + using UptrVector = typename std::conditional, + std::vector>::type; + using UnderlyingIterator = + typename std::conditional::type; + + // Creates a new iterator from the given |container| and its raw iterator + // |it|. + UptrVectorIterator(UptrVector* container, const UnderlyingIterator& it) + : container_(container), iterator_(it) {} + UptrVectorIterator(const UptrVectorIterator&) = default; + UptrVectorIterator& operator=(const UptrVectorIterator&) = default; + + inline UptrVectorIterator& operator++(); + inline UptrVectorIterator operator++(int); + inline UptrVectorIterator& operator--(); + inline UptrVectorIterator operator--(int); + + reference operator*() const { return **iterator_; } + pointer operator->() { return (*iterator_).get(); } + reference operator[](ptrdiff_t index) { return **(iterator_ + index); } + + inline bool operator==(const UptrVectorIterator& that) const; + inline bool operator!=(const UptrVectorIterator& that) const; + + inline ptrdiff_t operator-(const UptrVectorIterator& that) const; + inline bool operator<(const UptrVectorIterator& that) const; + + // Inserts the given |value| to the position pointed to by this iterator + // and returns an iterator to the newly iserted |value|. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // insertion point will be invalidated. + template + inline typename std::enable_if::type + InsertBefore(Uptr value); + + // Inserts the given |valueVector| to the position pointed to by this iterator + // and returns an iterator to the first newly inserted value. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // insertion point will be invalidated. + template + inline typename std::enable_if::type + InsertBefore(UptrVector* valueVector); + + // Erases the value at the position pointed to by this iterator + // and returns an iterator to the following value. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // erasure point will be invalidated. + template + inline typename std::enable_if::type + Erase(); + + // Returns the underlying iterator. + UnderlyingIterator Get() const { return iterator_; } + + // Returns a valid end iterator for the underlying container. + UptrVectorIterator End() const { + return UptrVectorIterator(container_, container_->end()); + } + + private: + UptrVector* container_; // The container we are manipulating. + UnderlyingIterator iterator_; // The raw iterator from the container. +}; + +// Handy class for a (begin, end) iterator pair. +template +class IteratorRange { + public: + IteratorRange(const IteratorType& b, const IteratorType& e) + : begin_(b), end_(e) {} + IteratorRange(IteratorType&& b, IteratorType&& e) + : begin_(std::move(b)), end_(std::move(e)) {} + + IteratorType begin() const { return begin_; } + IteratorType end() const { return end_; } + + bool empty() const { return begin_ == end_; } + size_t size() const { return end_ - begin_; } + + private: + IteratorType begin_; + IteratorType end_; +}; + +// Returns a (begin, end) iterator pair for the given iterators. +// The iterators must belong to the same container. +template +inline IteratorRange make_range(const IteratorType& begin, + const IteratorType& end) { + return {begin, end}; +} + +// Returns a (begin, end) iterator pair for the given iterators. +// The iterators must belong to the same container. +template +inline IteratorRange make_range(IteratorType&& begin, + IteratorType&& end) { + return {std::forward(begin), std::forward(end)}; +} + +// Returns a (begin, end) iterator pair for the given container. +template > +inline IteratorRange make_range( + std::vector>& container) { + return {IteratorType(&container, container.begin()), + IteratorType(&container, container.end())}; +} + +// Returns a const (begin, end) iterator pair for the given container. +template > +inline IteratorRange make_const_range( + const std::vector>& container) { + return {IteratorType(&container, container.cbegin()), + IteratorType(&container, container.cend())}; +} + +// Wrapping iterator class that only consider elements that satisfy the given +// predicate |Predicate|. When moving to the next element of the iterator, the +// FilterIterator will iterate over the range until it finds an element that +// satisfies |Predicate| or reaches the end of the iterator. +// +// Currently this iterator is always an input iterator. +template +class FilterIterator { + public: + // Iterator interface. + using iterator_category = typename SubIterator::iterator_category; + using value_type = typename SubIterator::value_type; + using pointer = typename SubIterator::pointer; + using reference = typename SubIterator::reference; + using difference_type = typename SubIterator::difference_type; + + using Range = IteratorRange; + + FilterIterator(const IteratorRange& iteration_range, + Predicate predicate) + : cur_(iteration_range.begin()), + end_(iteration_range.end()), + predicate_(predicate) { + if (!IsPredicateSatisfied()) { + MoveToNextPosition(); + } + } + + FilterIterator(const SubIterator& end, Predicate predicate) + : FilterIterator({end, end}, predicate) {} + + inline FilterIterator& operator++() { + MoveToNextPosition(); + return *this; + } + inline FilterIterator operator++(int) { + FilterIterator old = *this; + MoveToNextPosition(); + return old; + } + + reference operator*() const { return *cur_; } + pointer operator->() { return &*cur_; } + + inline bool operator==(const FilterIterator& rhs) const { + return cur_ == rhs.cur_ && end_ == rhs.end_; + } + inline bool operator!=(const FilterIterator& rhs) const { + return !(*this == rhs); + } + + // Returns the underlying iterator. + SubIterator Get() const { return cur_; } + + // Returns the sentinel iterator. + FilterIterator GetEnd() const { return FilterIterator(end_, predicate_); } + + private: + // Returns true if the predicate is satisfied or the current iterator reached + // the end. + bool IsPredicateSatisfied() { return cur_ == end_ || predicate_(*cur_); } + + void MoveToNextPosition() { + if (cur_ == end_) return; + + do { + ++cur_; + } while (!IsPredicateSatisfied()); + } + + SubIterator cur_; + SubIterator end_; + Predicate predicate_; +}; + +template +FilterIterator MakeFilterIterator( + const IteratorRange& sub_iterator_range, Predicate predicate) { + return FilterIterator(sub_iterator_range, predicate); +} + +template +FilterIterator MakeFilterIterator( + const SubIterator& begin, const SubIterator& end, Predicate predicate) { + return MakeFilterIterator(make_range(begin, end), predicate); +} + +template +typename FilterIterator::Range MakeFilterIteratorRange( + const SubIterator& begin, const SubIterator& end, Predicate predicate) { + return typename FilterIterator::Range( + MakeFilterIterator(begin, end, predicate), + MakeFilterIterator(end, end, predicate)); +} + +template +inline UptrVectorIterator& UptrVectorIterator::operator++() { + ++iterator_; + return *this; +} + +template +inline UptrVectorIterator UptrVectorIterator::operator++(int) { + auto it = *this; + ++(*this); + return it; +} + +template +inline UptrVectorIterator& UptrVectorIterator::operator--() { + --iterator_; + return *this; +} + +template +inline UptrVectorIterator UptrVectorIterator::operator--(int) { + auto it = *this; + --(*this); + return it; +} + +template +inline bool UptrVectorIterator::operator==( + const UptrVectorIterator& that) const { + return container_ == that.container_ && iterator_ == that.iterator_; +} + +template +inline bool UptrVectorIterator::operator!=( + const UptrVectorIterator& that) const { + return !(*this == that); +} + +template +inline ptrdiff_t UptrVectorIterator::operator-( + const UptrVectorIterator& that) const { + assert(container_ == that.container_); + return iterator_ - that.iterator_; +} + +template +inline bool UptrVectorIterator::operator<( + const UptrVectorIterator& that) const { + assert(container_ == that.container_); + return iterator_ < that.iterator_; +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::InsertBefore(Uptr value) { + auto index = iterator_ - container_->begin(); + container_->insert(iterator_, std::move(value)); + return UptrVectorIterator(container_, container_->begin() + index); +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::InsertBefore(UptrVector* values) { + const auto pos = iterator_ - container_->begin(); + const auto origsz = container_->size(); + container_->resize(origsz + values->size()); + std::move_backward(container_->begin() + pos, container_->begin() + origsz, + container_->end()); + std::move(values->begin(), values->end(), container_->begin() + pos); + return UptrVectorIterator(container_, container_->begin() + pos); +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::Erase() { + auto index = iterator_ - container_->begin(); + (void)container_->erase(iterator_); + return UptrVectorIterator(container_, container_->begin() + index); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ITERATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.cpp new file mode 100644 index 00000000..f2a6e4df --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/licm_pass.h" + +#include + +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +Pass::Status LICMPass::Process() { return ProcessIRContext(); } + +Pass::Status LICMPass::ProcessIRContext() { + Status status = Status::SuccessWithoutChange; + Module* module = get_module(); + + // Process each function in the module + for (auto func = module->begin(); + func != module->end() && status != Status::Failure; ++func) { + status = CombineStatus(status, ProcessFunction(&*func)); + } + return status; +} + +Pass::Status LICMPass::ProcessFunction(Function* f) { + Status status = Status::SuccessWithoutChange; + LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f); + + // Process each loop in the function + for (auto it = loop_descriptor->begin(); + it != loop_descriptor->end() && status != Status::Failure; ++it) { + Loop& loop = *it; + // Ignore nested loops, as we will process them in order in ProcessLoop + if (loop.IsNested()) { + continue; + } + status = CombineStatus(status, ProcessLoop(&loop, f)); + } + return status; +} + +Pass::Status LICMPass::ProcessLoop(Loop* loop, Function* f) { + Status status = Status::SuccessWithoutChange; + + // Process all nested loops first + for (auto nl = loop->begin(); nl != loop->end() && status != Status::Failure; + ++nl) { + Loop* nested_loop = *nl; + status = CombineStatus(status, ProcessLoop(nested_loop, f)); + } + + std::vector loop_bbs{}; + status = CombineStatus( + status, + AnalyseAndHoistFromBB(loop, f, loop->GetHeaderBlock(), &loop_bbs)); + + for (size_t i = 0; i < loop_bbs.size() && status != Status::Failure; ++i) { + BasicBlock* bb = loop_bbs[i]; + // do not delete the element + status = + CombineStatus(status, AnalyseAndHoistFromBB(loop, f, bb, &loop_bbs)); + } + + return status; +} + +Pass::Status LICMPass::AnalyseAndHoistFromBB( + Loop* loop, Function* f, BasicBlock* bb, + std::vector* loop_bbs) { + bool modified = false; + std::function hoist_inst = + [this, &loop, &modified](Instruction* inst) { + if (loop->ShouldHoistInstruction(*inst)) { + if (!HoistInstruction(loop, inst)) { + return false; + } + modified = true; + } + return true; + }; + + if (IsImmediatelyContainedInLoop(loop, f, bb)) { + if (!bb->WhileEachInst(hoist_inst, false)) { + return Status::Failure; + } + } + + DominatorAnalysis* dom_analysis = context()->GetDominatorAnalysis(f); + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + + for (DominatorTreeNode* child_dom_tree_node : *dom_tree.GetTreeNode(bb)) { + if (loop->IsInsideLoop(child_dom_tree_node->bb_)) { + loop_bbs->push_back(child_dom_tree_node->bb_); + } + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool LICMPass::IsImmediatelyContainedInLoop(Loop* loop, Function* f, + BasicBlock* bb) { + LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f); + return loop == (*loop_descriptor)[bb->id()]; +} + +bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) { + // TODO(1841): Handle failure to create pre-header. + BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock(); + if (!pre_header_bb) { + return false; + } + Instruction* insertion_point = &*pre_header_bb->tail(); + Instruction* previous_node = insertion_point->PreviousNode(); + if (previous_node && (previous_node->opcode() == spv::Op::OpLoopMerge || + previous_node->opcode() == spv::Op::OpSelectionMerge)) { + insertion_point = previous_node; + } + + inst->InsertBefore(insertion_point); + context()->set_instr_block(inst, pre_header_bb); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.h new file mode 100644 index 00000000..597fe920 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/licm_pass.h @@ -0,0 +1,72 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LICM_PASS_H_ +#define SOURCE_OPT_LICM_PASS_H_ + +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class LICMPass : public Pass { + public: + LICMPass() {} + + const char* name() const override { return "loop-invariant-code-motion"; } + Status Process() override; + + private: + // Searches the IRContext for functions and processes each, moving invariants + // outside loops within the function where possible. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessIRContext(); + + // Checks the function for loops, calling ProcessLoop on each one found. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessFunction(Function* f); + + // Checks for invariants in the loop and attempts to move them to the loops + // preheader. Works from inner loop to outer when nested loops are found. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessLoop(Loop* loop, Function* f); + + // Analyses each instruction in |bb|, hoisting invariants to |pre_header_bb|. + // Each child of |bb| wrt to |dom_tree| is pushed to |loop_bbs| + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status AnalyseAndHoistFromBB(Loop* loop, Function* f, BasicBlock* bb, + std::vector* loop_bbs); + + // Returns true if |bb| is immediately contained in |loop| + bool IsImmediatelyContainedInLoop(Loop* loop, Function* f, BasicBlock* bb); + + // Move the instruction to the preheader of |loop|. + // This method will update the instruction to block mapping for the context + bool HoistInstruction(Loop* loop, Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LICM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.cpp new file mode 100644 index 00000000..dae705dc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.cpp @@ -0,0 +1,352 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/liveness.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { +constexpr uint32_t kDecorationLocationInIdx = 2; +constexpr uint32_t kOpDecorateMemberMemberInIdx = 1; +constexpr uint32_t kOpDecorateMemberLocationInIdx = 3; +constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2; +constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3; +} // namespace + +LivenessManager::LivenessManager(IRContext* ctx) : ctx_(ctx), computed_(false) { + // Liveness sets computed when queried +} + +void LivenessManager::InitializeAnalysis() { + live_locs_.clear(); + live_builtins_.clear(); + // Mark all builtins live for frag shader. + if (context()->GetStage() == spv::ExecutionModel::Fragment) { + live_builtins_.insert(uint32_t(spv::BuiltIn::PointSize)); + live_builtins_.insert(uint32_t(spv::BuiltIn::ClipDistance)); + live_builtins_.insert(uint32_t(spv::BuiltIn::CullDistance)); + } +} + +bool LivenessManager::IsAnalyzedBuiltin(uint32_t bi) { + // There are only three builtins that can be analyzed and removed between + // two stages: PointSize, ClipDistance and CullDistance. All others are + // always consumed implicitly by the downstream stage. + const auto builtin = spv::BuiltIn(bi); + return builtin == spv::BuiltIn::PointSize || + builtin == spv::BuiltIn::ClipDistance || + builtin == spv::BuiltIn::CullDistance; +} + +bool LivenessManager::AnalyzeBuiltIn(uint32_t id) { + auto deco_mgr = context()->get_decoration_mgr(); + bool saw_builtin = false; + // Analyze all builtin decorations of |id|. + (void)deco_mgr->ForEachDecoration( + id, uint32_t(spv::Decoration::BuiltIn), + [this, &saw_builtin](const Instruction& deco_inst) { + saw_builtin = true; + // No need to process builtins in frag shader. All assumed used. + if (context()->GetStage() == spv::ExecutionModel::Fragment) return; + uint32_t builtin = uint32_t(spv::BuiltIn::Max); + if (deco_inst.opcode() == spv::Op::OpDecorate) + builtin = + deco_inst.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx); + else if (deco_inst.opcode() == spv::Op::OpMemberDecorate) + builtin = deco_inst.GetSingleWordInOperand( + kOpDecorateMemberBuiltInLiteralInIdx); + else + assert(false && "unexpected decoration"); + if (IsAnalyzedBuiltin(builtin)) live_builtins_.insert(builtin); + }); + return saw_builtin; +} + +void LivenessManager::MarkLocsLive(uint32_t start, uint32_t count) { + auto finish = start + count; + for (uint32_t u = start; u < finish; ++u) { + live_locs_.insert(u); + } +} + +uint32_t LivenessManager::GetLocSize(const analysis::Type* type) const { + auto arr_type = type->AsArray(); + if (arr_type) { + auto comp_type = arr_type->element_type(); + auto len_info = arr_type->length_info(); + assert(len_info.words[0] == analysis::Array::LengthInfo::kConstant && + "unexpected array length"); + auto comp_len = len_info.words[1]; + return comp_len * GetLocSize(comp_type); + } + auto struct_type = type->AsStruct(); + if (struct_type) { + uint32_t size = 0u; + for (auto& el_type : struct_type->element_types()) + size += GetLocSize(el_type); + return size; + } + auto mat_type = type->AsMatrix(); + if (mat_type) { + auto cnt = mat_type->element_count(); + auto comp_type = mat_type->element_type(); + return cnt * GetLocSize(comp_type); + } + auto vec_type = type->AsVector(); + if (vec_type) { + auto comp_type = vec_type->element_type(); + if (comp_type->AsInteger()) return 1; + auto float_type = comp_type->AsFloat(); + assert(float_type && "unexpected vector component type"); + auto width = float_type->width(); + if (width == 32 || width == 16) return 1; + assert(width == 64 && "unexpected float type width"); + auto comp_cnt = vec_type->element_count(); + return (comp_cnt > 2) ? 2 : 1; + } + assert((type->AsInteger() || type->AsFloat()) && "unexpected input type"); + return 1; +} + +uint32_t LivenessManager::GetComponentType(uint32_t index, + uint32_t agg_type_id) const { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + Instruction* agg_type_inst = def_use_mgr->GetDef(agg_type_id); + + const uint32_t kArrayElementInIdx = 0; + switch (agg_type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + return agg_type_inst->GetSingleWordInOperand(kArrayElementInIdx); + case spv::Op::OpTypeStruct: + return agg_type_inst->GetSingleWordInOperand(index); + default: + assert(false && "unexpected aggregate type"); + return 0; + } +} + +uint32_t LivenessManager::GetLocOffset(uint32_t index, + uint32_t agg_type_id) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* agg_type = type_mgr->GetType(agg_type_id); + auto arr_type = agg_type->AsArray(); + if (arr_type) return index * GetLocSize(arr_type->element_type()); + auto struct_type = agg_type->AsStruct(); + if (struct_type) { + uint32_t offset = 0u; + uint32_t cnt = 0u; + for (auto& el_type : struct_type->element_types()) { + if (cnt == index) break; + offset += GetLocSize(el_type); + ++cnt; + } + return offset; + } + auto mat_type = agg_type->AsMatrix(); + if (mat_type) return index * GetLocSize(mat_type->element_type()); + auto vec_type = agg_type->AsVector(); + assert(vec_type && "unexpected non-aggregate type"); + auto comp_type = vec_type->element_type(); + auto flt_type = comp_type->AsFloat(); + if (flt_type && flt_type->width() == 64u && index >= 2u) return 1; + return 0; +} + +uint32_t LivenessManager::AnalyzeAccessChainLoc(const Instruction* ac, + uint32_t curr_type_id, + uint32_t* offset, bool* no_loc, + bool is_patch, bool input) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr(); + // For tesc, tese and geom input variables, and tesc output variables, + // first array index does not contribute to offset. + auto stage = context()->GetStage(); + bool skip_first_index = false; + if ((input && (stage == spv::ExecutionModel::TessellationControl || + stage == spv::ExecutionModel::TessellationEvaluation || + stage == spv::ExecutionModel::Geometry)) || + (!input && stage == spv::ExecutionModel::TessellationControl)) + skip_first_index = !is_patch; + uint32_t ocnt = 0; + ac->WhileEachInOperand([this, &ocnt, def_use_mgr, deco_mgr, &curr_type_id, + offset, no_loc, + skip_first_index](const uint32_t* opnd) { + if (ocnt >= 1) { + // Skip first index's contribution to offset if indicated + Instruction* curr_type_inst = def_use_mgr->GetDef(curr_type_id); + if (ocnt == 1 && skip_first_index) { + assert(curr_type_inst->opcode() == spv::Op::OpTypeArray && + "unexpected wrapper type"); + const uint32_t kArrayElementTypeInIdx = 0; + curr_type_id = + curr_type_inst->GetSingleWordInOperand(kArrayElementTypeInIdx); + ocnt++; + return true; + } + // If any non-constant index, mark the entire current object and return. + auto idx_inst = def_use_mgr->GetDef(*opnd); + if (idx_inst->opcode() != spv::Op::OpConstant) return false; + // If current type is struct, look for location decoration on member and + // reset offset if found. + auto index = idx_inst->GetSingleWordInOperand(0); + if (curr_type_inst->opcode() == spv::Op::OpTypeStruct) { + uint32_t loc = 0; + bool no_mem_loc = deco_mgr->WhileEachDecoration( + curr_type_id, uint32_t(spv::Decoration::Location), + [&loc, index, no_loc](const Instruction& deco) { + assert(deco.opcode() == spv::Op::OpMemberDecorate && + "unexpected decoration"); + if (deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx) == + index) { + loc = + deco.GetSingleWordInOperand(kOpDecorateMemberLocationInIdx); + *no_loc = false; + return false; + } + return true; + }); + if (!no_mem_loc) { + *offset = loc; + curr_type_id = curr_type_inst->GetSingleWordInOperand(index); + ocnt++; + return true; + } + } + + // Update offset and current type based on constant index. + *offset += GetLocOffset(index, curr_type_id); + curr_type_id = GetComponentType(index, curr_type_id); + } + ocnt++; + return true; + }); + return curr_type_id; +} + +void LivenessManager::MarkRefLive(const Instruction* ref, Instruction* var) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr(); + // Find variable location if present. + uint32_t loc = 0; + auto var_id = var->result_id(); + bool no_loc = deco_mgr->WhileEachDecoration( + var_id, uint32_t(spv::Decoration::Location), + [&loc](const Instruction& deco) { + assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration"); + loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx); + return false; + }); + // Find patch decoration if present + bool is_patch = !deco_mgr->WhileEachDecoration( + var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) { + if (deco.opcode() != spv::Op::OpDecorate) + assert(false && "unexpected decoration"); + return false; + }); + // If use is a load, mark all locations of var + auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer(); + assert(ptr_type && "unexpected var type"); + auto var_type = ptr_type->pointee_type(); + if (ref->opcode() == spv::Op::OpLoad) { + assert(!no_loc && "missing input variable location"); + MarkLocsLive(loc, GetLocSize(var_type)); + return; + } + // Mark just those locations indicated by access chain + assert((ref->opcode() == spv::Op::OpAccessChain || + ref->opcode() == spv::Op::OpInBoundsAccessChain) && + "unexpected use of input variable"); + // Traverse access chain, compute location offset and type of reference + // through constant indices and mark those locs live. Assert if no location + // found. + uint32_t offset = loc; + Instruction* ptr_type_inst = + context()->get_def_use_mgr()->GetDef(var->type_id()); + assert(ptr_type && "unexpected var type"); + const uint32_t kPointerTypePointeeIdx = 1; + uint32_t var_type_id = + ptr_type_inst->GetSingleWordInOperand(kPointerTypePointeeIdx); + uint32_t curr_type_id = + AnalyzeAccessChainLoc(ref, var_type_id, &offset, &no_loc, is_patch); + auto curr_type = type_mgr->GetType(curr_type_id); + assert(!no_loc && "missing input variable location"); + MarkLocsLive(offset, GetLocSize(curr_type)); +} + +void LivenessManager::ComputeLiveness() { + InitializeAnalysis(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + // Process all input variables + for (auto& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) { + continue; + } + Instruction* var_type_inst = def_use_mgr->GetDef(var.type_id()); + assert(var_type_inst->opcode() == spv::Op::OpTypePointer && + "Expected a pointer type"); + const uint32_t kPointerTypeStorageClassInIdx = 0; + spv::StorageClass sc = static_cast( + var_type_inst->GetSingleWordInOperand(kPointerTypeStorageClassInIdx)); + if (sc != spv::StorageClass::Input) { + continue; + } + // If var is builtin, mark live if analyzed and continue to next variable + auto var_id = var.result_id(); + if (AnalyzeBuiltIn(var_id)) continue; + // If interface block with builtin members, mark live if analyzed and + // continue to next variable. Input interface blocks will only appear + // in tesc, tese and geom shaders. Will need to strip off one level of + // arrayness to get to block type. + const uint32_t kPointerTypePointeeTypeInIdx = 1; + uint32_t pte_type_id = + var_type_inst->GetSingleWordInOperand(kPointerTypePointeeTypeInIdx); + Instruction* pte_type_inst = def_use_mgr->GetDef(pte_type_id); + if (pte_type_inst->opcode() == spv::Op::OpTypeArray) { + uint32_t array_elt_type_id = pte_type_inst->GetSingleWordInOperand(0); + Instruction* arr_elt_type = def_use_mgr->GetDef(array_elt_type_id); + if (arr_elt_type->opcode() == spv::Op::OpTypeStruct) { + if (AnalyzeBuiltIn(array_elt_type_id)) continue; + } + } + // Mark all used locations of var live + def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) { + auto op = user->opcode(); + if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName || + op == spv::Op::OpDecorate || user->IsNonSemanticInstruction()) { + return; + } + MarkRefLive(user, &var); + }); + } +} + +void LivenessManager::GetLiveness(std::unordered_set* live_locs, + std::unordered_set* live_builtins) { + if (!computed_) { + ComputeLiveness(); + computed_ = true; + } + *live_locs = live_locs_; + *live_builtins = live_builtins_; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.h b/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.h new file mode 100644 index 00000000..70500059 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/liveness.h @@ -0,0 +1,98 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LIVENESS_H_ +#define SOURCE_OPT_LIVENESS_H_ + +#include +#include + +namespace spvtools { +namespace opt { + +class IRContext; +class Instruction; + +namespace analysis { + +class Type; + +// This class represents the liveness of the input variables of a module +class LivenessManager { + public: + LivenessManager(IRContext* ctx); + + // Copy liveness info into |live_locs| and |builtin_locs|. + void GetLiveness(std::unordered_set* live_locs, + std::unordered_set* live_builtins); + + // Return true if builtin |bi| is being analyzed. + bool IsAnalyzedBuiltin(uint32_t bi); + + // Return the result type of |ac| when applied to |cur_type_id|. Set + // |no_loc| to true if no loc found. Set |is_patch| indicates if the variable + // is a patch variable. Set |input| if the variable is an input variable. + // Otherwise it is assumed that the variable is an output variable. + uint32_t AnalyzeAccessChainLoc(const Instruction* ac, uint32_t curr_type_id, + uint32_t* offset, bool* no_loc, bool is_patch, + bool input = true); + + // Return size of |type_id| in units of locations + uint32_t GetLocSize(const analysis::Type* type) const; + + private: + IRContext* context() const { return ctx_; } + + // Initialize analysis + void InitializeAnalysis(); + + // Analyze |id| for builtin var and struct members. Return true if builtins + // found. + bool AnalyzeBuiltIn(uint32_t id); + + // Mark all live locations resulting from |user| of |var| at |loc|. + void MarkRefLive(const Instruction* user, Instruction* var); + + // Mark |count| locations starting at location |start|. + void MarkLocsLive(uint32_t start, uint32_t count); + + // Return type of the member |index| in the aggregate type |agg_type_id|. + uint32_t GetComponentType(uint32_t index, uint32_t agg_type_id) const; + + // Return offset of member |index| in the aggregate type |agg_type_id| in + // units of input locations. + uint32_t GetLocOffset(uint32_t index, uint32_t agg_type_id) const; + + // Populate live_locs_ and live_builtins_ + void ComputeLiveness(); + + // IR context that owns this liveness manager. + IRContext* ctx_; + + // True if live_locs_ and live_builtins_ are computed + bool computed_; + + // Live locations + std::unordered_set live_locs_; + + // Live builtins + std::unordered_set live_builtins_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LIVENESS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.cpp new file mode 100644 index 00000000..16147b87 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.cpp @@ -0,0 +1,478 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_access_chain_convert_pass.h" + +#include "ir_context.h" +#include "iterator.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kStoreValIdInIdx = 1; +constexpr uint32_t kAccessChainPtrIdInIdx = 0; +} // namespace + +void LocalAccessChainConvertPass::BuildAndAppendInst( + spv::Op opcode, uint32_t typeId, uint32_t resultId, + const std::vector& in_opnds, + std::vector>* newInsts) { + std::unique_ptr newInst( + new Instruction(context(), opcode, typeId, resultId, in_opnds)); + get_def_use_mgr()->AnalyzeInstDefUse(&*newInst); + newInsts->emplace_back(std::move(newInst)); +} + +uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad( + const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, + std::vector>* newInsts) { + const uint32_t ldResultId = TakeNextId(); + if (ldResultId == 0) { + return 0; + } + + *varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); + const Instruction* varInst = get_def_use_mgr()->GetDef(*varId); + assert(varInst->opcode() == spv::Op::OpVariable); + *varPteTypeId = GetPointeeTypeId(varInst); + BuildAndAppendInst(spv::Op::OpLoad, *varPteTypeId, ldResultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}}, + newInsts); + return ldResultId; +} + +void LocalAccessChainConvertPass::AppendConstantOperands( + const Instruction* ptrInst, std::vector* in_opnds) { + uint32_t iidIdx = 0; + ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) { + if (iidIdx > 0) { + const Instruction* cInst = get_def_use_mgr()->GetDef(*iid); + const auto* constant_value = + context()->get_constant_mgr()->GetConstantFromInst(cInst); + assert(constant_value != nullptr && + "Expecting the index to be a constant."); + + // We take the sign extended value because OpAccessChain interprets the + // index as signed. + int64_t long_value = constant_value->GetSignExtendedValue(); + assert(long_value <= UINT32_MAX && long_value >= 0 && + "The index value is too large for a composite insert or extract " + "instruction."); + + uint32_t val = static_cast(long_value); + in_opnds->push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}}); + } + ++iidIdx; + }); +} + +bool LocalAccessChainConvertPass::ReplaceAccessChainLoad( + const Instruction* address_inst, Instruction* original_load) { + // Build and append load of variable in ptrInst + if (address_inst->NumInOperands() == 1) { + // An access chain with no indices is essentially a copy. All that is + // needed is to propagate the address. + context()->ReplaceAllUsesWith( + address_inst->result_id(), + address_inst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)); + return true; + } + + std::vector> new_inst; + uint32_t varId; + uint32_t varPteTypeId; + const uint32_t ldResultId = + BuildAndAppendVarLoad(address_inst, &varId, &varPteTypeId, &new_inst); + if (ldResultId == 0) { + return false; + } + + new_inst[0]->UpdateDebugInfoFrom(original_load); + context()->get_decoration_mgr()->CloneDecorations( + original_load->result_id(), ldResultId, + {spv::Decoration::RelaxedPrecision}); + original_load->InsertBefore(std::move(new_inst)); + context()->get_debug_info_mgr()->AnalyzeDebugInst( + original_load->PreviousNode()); + + // Rewrite |original_load| into an extract. + Instruction::OperandList new_operands; + + // copy the result id and the type id to the new operand list. + new_operands.emplace_back(original_load->GetOperand(0)); + new_operands.emplace_back(original_load->GetOperand(1)); + + new_operands.emplace_back( + Operand({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}})); + AppendConstantOperands(address_inst, &new_operands); + original_load->SetOpcode(spv::Op::OpCompositeExtract); + original_load->ReplaceOperands(new_operands); + context()->UpdateDefUse(original_load); + return true; +} + +bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement( + const Instruction* ptrInst, uint32_t valId, + std::vector>* newInsts) { + if (ptrInst->NumInOperands() == 1) { + // An access chain with no indices is essentially a copy. However, we still + // have to create a new store because the old ones will be deleted. + BuildAndAppendInst( + spv::Op::OpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}}, + newInsts); + return true; + } + + // Build and append load of variable in ptrInst + uint32_t varId; + uint32_t varPteTypeId; + const uint32_t ldResultId = + BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts); + if (ldResultId == 0) { + return false; + } + + context()->get_decoration_mgr()->CloneDecorations( + varId, ldResultId, {spv::Decoration::RelaxedPrecision}); + + // Build and append Insert + const uint32_t insResultId = TakeNextId(); + if (insResultId == 0) { + return false; + } + std::vector ins_in_opnds = { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}}; + AppendConstantOperands(ptrInst, &ins_in_opnds); + BuildAndAppendInst(spv::Op::OpCompositeInsert, varPteTypeId, insResultId, + ins_in_opnds, newInsts); + + context()->get_decoration_mgr()->CloneDecorations( + varId, insResultId, {spv::Decoration::RelaxedPrecision}); + + // Build and append Store + BuildAndAppendInst(spv::Op::OpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}}, + newInsts); + return true; +} + +bool LocalAccessChainConvertPass::Is32BitConstantIndexAccessChain( + const Instruction* acp) const { + uint32_t inIdx = 0; + return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) { + if (inIdx > 0) { + Instruction* opInst = get_def_use_mgr()->GetDef(*tid); + if (opInst->opcode() != spv::Op::OpConstant) return false; + const auto* index = + context()->get_constant_mgr()->GetConstantFromInst(opInst); + int64_t index_value = index->GetSignExtendedValue(); + if (index_value > UINT32_MAX) return false; + if (index_value < 0) return false; + } + ++inIdx; + return true; + }); +} + +bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) { + if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; + if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + return true; + } + spv::Op op = user->opcode(); + if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) { + if (!HasOnlySupportedRefs(user->result_id())) { + return false; + } + } else if (op != spv::Op::OpStore && op != spv::Op::OpLoad && + op != spv::Op::OpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + })) { + supported_ref_ptrs_.insert(ptrId); + return true; + } + return false; +} + +void LocalAccessChainConvertPass::FindTargetVars(Function* func) { + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + switch (ii->opcode()) { + case spv::Op::OpStore: + case spv::Op::OpLoad: { + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) break; + const spv::Op op = ptrInst->opcode(); + // Rule out variables with non-supported refs eg function calls + if (!HasOnlySupportedRefs(varId)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + // Rule out variables with nested access chains + // TODO(): Convert nested access chains + bool is_non_ptr_access_chain = IsNonPtrAccessChain(op); + if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand( + kAccessChainPtrIdInIdx) != varId) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + // Rule out variables accessed with non-constant indices + if (!Is32BitConstantIndexAccessChain(ptrInst)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + + if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + } break; + default: + break; + } + } + } +} + +Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains( + Function* func) { + FindTargetVars(func); + // Replace access chains of all targeted variables with equivalent + // extract and insert sequences + bool modified = false; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + std::vector dead_instructions; + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + switch (ii->opcode()) { + case spv::Op::OpLoad: { + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsNonPtrAccessChain(ptrInst->opcode())) break; + if (!IsTargetVar(varId)) break; + if (!ReplaceAccessChainLoad(ptrInst, &*ii)) { + return Status::Failure; + } + modified = true; + } break; + case spv::Op::OpStore: { + uint32_t varId; + Instruction* store = &*ii; + Instruction* ptrInst = GetPtr(store, &varId); + if (!IsNonPtrAccessChain(ptrInst->opcode())) break; + if (!IsTargetVar(varId)) break; + std::vector> newInsts; + uint32_t valId = store->GetSingleWordInOperand(kStoreValIdInIdx); + if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) { + return Status::Failure; + } + size_t num_of_instructions_to_skip = newInsts.size() - 1; + dead_instructions.push_back(store); + ++ii; + ii = ii.InsertBefore(std::move(newInsts)); + for (size_t i = 0; i < num_of_instructions_to_skip; ++i) { + ii->UpdateDebugInfoFrom(store); + context()->get_debug_info_mgr()->AnalyzeDebugInst(&*ii); + ++ii; + } + ii->UpdateDebugInfoFrom(store); + context()->get_debug_info_mgr()->AnalyzeDebugInst(&*ii); + modified = true; + } break; + default: + break; + } + } + + while (!dead_instructions.empty()) { + Instruction* inst = dead_instructions.back(); + dead_instructions.pop_back(); + DCEInst(inst, [&dead_instructions](Instruction* other_inst) { + auto i = std::find(dead_instructions.begin(), dead_instructions.end(), + other_inst); + if (i != dead_instructions.end()) { + dead_instructions.erase(i); + } + }); + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void LocalAccessChainConvertPass::Initialize() { + // Initialize Target Variable Caches + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + + // Initialize collections + supported_ref_ptrs_.clear(); + + // Initialize extension allowlist + InitExtensions(); +} + +bool LocalAccessChainConvertPass::AllExtensionsSupported() const { + // This capability can now exist without the extension, so we have to check + // for the capability. This pass is only looking at function scope symbols, + // so we do not care if there are variable pointers on storage buffers. + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointers)) + return false; + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const std::string extName = ei.GetInOperand(0).AsString(); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } + return true; +} + +Pass::Status LocalAccessChainConvertPass::ProcessImpl() { + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == spv::Op::OpGroupDecorate) + return Status::SuccessWithoutChange; + // Do not process if any disallowed extensions are enabled + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + + // Process all functions in the module. + Status status = Status::SuccessWithoutChange; + for (Function& func : *get_module()) { + status = CombineStatus(status, ConvertLocalAccessChains(&func)); + if (status == Status::Failure) { + break; + } + } + return status; +} + +LocalAccessChainConvertPass::LocalAccessChainConvertPass() {} + +Pass::Status LocalAccessChainConvertPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void LocalAccessChainConvertPass::InitExtensions() { + extensions_allowlist_.clear(); + extensions_allowlist_.insert( + {"SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", "SPV_KHR_ray_tracing", "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", "SPV_KHR_ray_tracing_position_fetch", + "SPV_AMDX_shader_enqueue", "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control"}); +} + +bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds( + const Instruction* access_chain_inst) { + assert(IsNonPtrAccessChain(access_chain_inst->opcode())); + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + auto constants = const_mgr->GetOperandConstants(access_chain_inst); + uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0); + Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id); + const analysis::Pointer* base_pointer_type = + type_mgr->GetType(base_pointer->type_id())->AsPointer(); + assert(base_pointer_type != nullptr && + "The base of the access chain is not a pointer."); + const analysis::Type* current_type = base_pointer_type->pointee_type(); + for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) { + if (IsIndexOutOfBounds(constants[i], current_type)) { + return true; + } + + uint32_t index = + (constants[i] + ? static_cast(constants[i]->GetZeroExtendedValue()) + : 0); + current_type = type_mgr->GetMemberType(current_type, {index}); + } + + return false; +} + +bool LocalAccessChainConvertPass::IsIndexOutOfBounds( + const analysis::Constant* index, const analysis::Type* type) const { + if (index == nullptr) { + return false; + } + return index->GetZeroExtendedValue() >= type->NumberOfComponents(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.h new file mode 100644 index 00000000..0cda196f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_access_chain_convert_pass.h @@ -0,0 +1,145 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ +#define SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalAccessChainConvertPass : public MemPass { + public: + LocalAccessChainConvertPass(); + + const char* name() const override { return "convert-local-access-chains"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + using ProcessFunction = std::function; + + private: + // Return true if all refs through |ptrId| are only loads or stores and + // cache ptrId in supported_ref_ptrs_. TODO(dnovillo): This function is + // replicated in other passes and it's slightly different in every pass. Is it + // possible to make one common implementation? + bool HasOnlySupportedRefs(uint32_t ptrId); + + // Search |func| and cache function scope variables of target type that are + // not accessed with non-constant-index access chains. Also cache non-target + // variables. + void FindTargetVars(Function* func); + + // Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|. + // Append to |newInsts|. + void BuildAndAppendInst(spv::Op opcode, uint32_t typeId, uint32_t resultId, + const std::vector& in_opnds, + std::vector>* newInsts); + + // Build load of variable in |ptrInst| and append to |newInsts|. + // Return var in |varId| and its pointee type in |varPteTypeId|. + uint32_t BuildAndAppendVarLoad( + const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, + std::vector>* newInsts); + + // Append literal integer operands to |in_opnds| corresponding to constant + // integer operands from access chain |ptrInst|. Assumes all indices in + // access chains are OpConstant. + void AppendConstantOperands(const Instruction* ptrInst, + std::vector* in_opnds); + + // Create a load/insert/store equivalent to a store of + // |valId| through (constant index) access chain |ptrInst|. + // Append to |newInsts|. Returns true if successful. + bool GenAccessChainStoreReplacement( + const Instruction* ptrInst, uint32_t valId, + std::vector>* newInsts); + + // For the (constant index) access chain |address_inst|, create an + // equivalent load and extract that replaces |original_load|. The result id + // of the extract will be the same as the original result id of + // |original_load|. Returns true if successful. + bool ReplaceAccessChainLoad(const Instruction* address_inst, + Instruction* original_load); + + // Return true if all indices of the access chain |acp| are OpConstant + // integers whose signed values can be represented as unsigned 32-bit values. + bool Is32BitConstantIndexAccessChain(const Instruction* acp) const; + + // Identify all function scope variables of target type which are + // accessed only with loads, stores and access chains with constant + // indices. Convert all loads and stores of such variables into equivalent + // loads, stores, extracts and inserts. This unifies access to these + // variables to a single mode and simplifies analysis and optimization. + // See IsTargetType() for targeted types. + // + // Nested access chains and pointer access chains are not currently + // converted. + // + // Returns a status to indicate success or failure, and change or no change. + Status ConvertLocalAccessChains(Function* func); + + // Returns true one of the indexes in the |access_chain_inst| is definitly out + // of bounds. If the size of the type or the value of the index is unknown, + // then it will be considered in-bounds. + bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst); + + // Returns true if getting element |index| from |type| would be out-of-bounds. + // If |index| is nullptr or the size of the type are unknown, then it will be + // considered in-bounds. + bool IsIndexOutOfBounds(const analysis::Constant* index, + const analysis::Type* type) const; + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + void Initialize(); + Pass::Status ProcessImpl(); + + // Variables with only supported references, ie. loads and stores using + // variable directly or through non-ptr access chains. + std::unordered_set supported_ref_ptrs_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.cpp new file mode 100644 index 00000000..9539e655 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_redundancy_elimination.h" + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status LocalRedundancyEliminationPass::Process() { + bool modified = false; + ValueNumberTable vnTable(context()); + + for (auto& func : *get_module()) { + for (auto& bb : func) { + // Keeps track of all ids that contain a given value number. We keep + // track of multiple values because they could have the same value, but + // different decorations. + std::map value_to_ids; + if (EliminateRedundanciesInBB(&bb, vnTable, &value_to_ids)) + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool LocalRedundancyEliminationPass::EliminateRedundanciesInBB( + BasicBlock* block, const ValueNumberTable& vnTable, + std::map* value_to_ids) { + bool modified = false; + + auto func = [this, &vnTable, &modified, value_to_ids](Instruction* inst) { + if (inst->result_id() == 0) { + return; + } + + uint32_t value = vnTable.GetValueNumber(inst); + + if (value == 0) { + return; + } + + auto candidate = value_to_ids->insert({value, inst->result_id()}); + if (!candidate.second) { + context()->KillNamesAndDecorates(inst); + context()->ReplaceAllUsesWith(inst->result_id(), candidate.first->second); + context()->KillInst(inst); + modified = true; + } + }; + block->ForEachInst(func); + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.h b/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.h new file mode 100644 index 00000000..770457a3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_redundancy_elimination.h @@ -0,0 +1,68 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ +#define SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +// This pass implements local redundancy elimination. Its goal is to reduce the +// number of times the same value is computed. It works on each basic block +// independently, ie local. For each instruction in a basic block, it gets the +// value number for the result id, |id|, of the instruction. If that value +// number has already been computed in the basic block, it tries to replace the +// uses of |id| by the id that already contains the same value. Then the +// current instruction is deleted. +class LocalRedundancyEliminationPass : public Pass { + public: + const char* name() const override { return "local-redundancy-elimination"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + protected: + // Deletes instructions in |block| whose value is in |value_to_ids| or is + // computed earlier in |block|. + // + // |vnTable| must have computed a value number for every result id defined + // in |bb|. + // + // |value_to_ids| is a map from value number to ids. If {vn, id} is in + // |value_to_ids| then vn is the value number of id, and the definition of id + // dominates |bb|. + // + // Returns true if the module is changed. + bool EliminateRedundanciesInBB(BasicBlock* block, + const ValueNumberTable& vnTable, + std::map* value_to_ids); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.cpp new file mode 100644 index 00000000..5115f981 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_single_block_elim_pass.h" + +#include + +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kStoreValIdInIdx = 1; +} // namespace + +bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) { + if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; + if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { + return true; + } + spv::Op op = user->opcode(); + if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) { + if (!HasOnlySupportedRefs(user->result_id())) { + return false; + } + } else if (op != spv::Op::OpStore && op != spv::Op::OpLoad && + op != spv::Op::OpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + })) { + supported_ref_ptrs_.insert(ptrId); + return true; + } + return false; +} + +bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim( + Function* func) { + // Perform local store/load, load/load and store/store elimination + // on each block + bool modified = false; + std::vector instructions_to_kill; + std::unordered_set instructions_to_save; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + var2store_.clear(); + var2load_.clear(); + auto next = bi->begin(); + for (auto ii = next; ii != bi->end(); ii = next) { + ++next; + switch (ii->opcode()) { + case spv::Op::OpStore: { + // Verify store variable is target type + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) continue; + if (!HasOnlySupportedRefs(varId)) continue; + // If a store to the whole variable, remember it for succeeding + // loads and stores. Otherwise forget any previous store to that + // variable. + if (ptrInst->opcode() == spv::Op::OpVariable) { + // If a previous store to same variable, mark the store + // for deletion if not still used. Don't delete store + // if debugging; let ssa-rewrite and DCE handle it + auto prev_store = var2store_.find(varId); + if (prev_store != var2store_.end() && + instructions_to_save.count(prev_store->second) == 0 && + !context()->get_debug_info_mgr()->IsVariableDebugDeclared( + varId)) { + instructions_to_kill.push_back(prev_store->second); + modified = true; + } + + bool kill_store = false; + auto li = var2load_.find(varId); + if (li != var2load_.end()) { + if (ii->GetSingleWordInOperand(kStoreValIdInIdx) == + li->second->result_id()) { + // We are storing the same value that already exists in the + // memory location. The store does nothing. + kill_store = true; + } + } + + if (!kill_store) { + var2store_[varId] = &*ii; + var2load_.erase(varId); + } else { + instructions_to_kill.push_back(&*ii); + modified = true; + } + } else { + assert(IsNonPtrAccessChain(ptrInst->opcode())); + var2store_.erase(varId); + var2load_.erase(varId); + } + } break; + case spv::Op::OpLoad: { + // Verify store variable is target type + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) continue; + if (!HasOnlySupportedRefs(varId)) continue; + uint32_t replId = 0; + if (ptrInst->opcode() == spv::Op::OpVariable) { + // If a load from a variable, look for a previous store or + // load from that variable and use its value. + auto si = var2store_.find(varId); + if (si != var2store_.end()) { + replId = si->second->GetSingleWordInOperand(kStoreValIdInIdx); + } else { + auto li = var2load_.find(varId); + if (li != var2load_.end()) { + replId = li->second->result_id(); + } + } + } else { + // If a partial load of a previously seen store, remember + // not to delete the store. + auto si = var2store_.find(varId); + if (si != var2store_.end()) instructions_to_save.insert(si->second); + } + if (replId != 0) { + // replace load's result id and delete load + context()->KillNamesAndDecorates(&*ii); + context()->ReplaceAllUsesWith(ii->result_id(), replId); + instructions_to_kill.push_back(&*ii); + modified = true; + } else { + if (ptrInst->opcode() == spv::Op::OpVariable) + var2load_[varId] = &*ii; // register load + } + } break; + case spv::Op::OpFunctionCall: { + // Conservatively assume all locals are redefined for now. + // TODO(): Handle more optimally + var2store_.clear(); + var2load_.clear(); + } break; + default: + break; + } + } + } + + for (Instruction* inst : instructions_to_kill) { + context()->KillInst(inst); + } + + return modified; +} + +void LocalSingleBlockLoadStoreElimPass::Initialize() { + // Initialize Target Type Caches + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + + // Clear collections + supported_ref_ptrs_.clear(); + + // Initialize extensions allowlist + InitExtensions(); +} + +bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const std::string extName = ei.GetInOperand(0).AsString(); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } + return true; +} + +Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() { + // Assumes relaxed logical addressing only (see instruction.h). + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses)) + return Status::SuccessWithoutChange; + + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == spv::Op::OpGroupDecorate) + return Status::SuccessWithoutChange; + // If any extensions in the module are not explicitly supported, + // return unmodified. + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return LocalSingleBlockLoadStoreElim(fp); + }; + + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass() = + default; + +Pass::Status LocalSingleBlockLoadStoreElimPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void LocalSingleBlockLoadStoreElimPass::InitExtensions() { + extensions_allowlist_.clear(); + extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMDX_shader_enqueue", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_quad_control"}); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.h new file mode 100644 index 00000000..ea72816a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_block_elim_pass.h @@ -0,0 +1,107 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ +#define SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalSingleBlockLoadStoreElimPass : public MemPass { + public: + LocalSingleBlockLoadStoreElimPass(); + + const char* name() const override { return "eliminate-local-single-block"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Return true if all uses of |varId| are only through supported reference + // operations ie. loads and store. Also cache in supported_ref_ptrs_. + // TODO(dnovillo): This function is replicated in other passes and it's + // slightly different in every pass. Is it possible to make one common + // implementation? + bool HasOnlySupportedRefs(uint32_t varId); + + // On all entry point functions, within each basic block, eliminate + // loads and stores to function variables where possible. For + // loads, if previous load or store to same variable, replace + // load id with previous id and delete load. Finally, check if + // remaining stores are useless, and delete store and variable + // where possible. Assumes logical addressing. + bool LocalSingleBlockLoadStoreElim(Function* func); + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are supported by this pass. + bool AllExtensionsSupported() const; + + void Initialize(); + Pass::Status ProcessImpl(); + + // Map from function scope variable to a store of that variable in the + // current block whose value is currently valid. This map is cleared + // at the start of each block and incrementally updated as the block + // is scanned. The stores are candidates for elimination. The map is + // conservatively cleared when a function call is encountered. + std::unordered_map var2store_; + + // Map from function scope variable to a load of that variable in the + // current block whose value is currently valid. This map is cleared + // at the start of each block and incrementally updated as the block + // is scanned. The stores are candidates for elimination. The map is + // conservatively cleared when a function call is encountered. + std::unordered_map var2load_; + + // Set of variables whose most recent store in the current block cannot be + // deleted, for example, if there is a load of the variable which is + // dependent on the store and is not replaced and deleted by this pass, + // for example, a load through an access chain. A variable is removed + // from this set each time a new store of that variable is encountered. + std::unordered_set pinned_vars_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; + + // Variables that are only referenced by supported operations for this + // pass ie. loads and stores. + std::unordered_set supported_ref_ptrs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.cpp new file mode 100644 index 00000000..7ff9b437 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -0,0 +1,320 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_single_store_elim_pass.h" + +#include "source/cfa.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kStoreValIdInIdx = 1; +constexpr uint32_t kVariableInitIdInIdx = 1; +} // namespace + +bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { + bool modified = false; + + // Check all function scope variables in |func|. + BasicBlock* entry_block = &*func->begin(); + for (Instruction& inst : *entry_block) { + if (inst.opcode() != spv::Op::OpVariable) { + break; + } + + modified |= ProcessVariable(&inst); + } + return modified; +} + +bool LocalSingleStoreElimPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const std::string extName = ei.GetInOperand(0).AsString(); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise + // around unknown extended + // instruction sets even if they are non-semantic + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { + return false; + } + } + return true; +} + +Pass::Status LocalSingleStoreElimPass::ProcessImpl() { + // Assumes relaxed logical addressing only (see instruction.h) + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses)) + return Status::SuccessWithoutChange; + + // Do not process if any disallowed extensions are enabled + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return LocalSingleStoreElim(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default; + +Pass::Status LocalSingleStoreElimPass::Process() { + InitExtensionAllowList(); + return ProcessImpl(); +} + +void LocalSingleStoreElimPass::InitExtensionAllowList() { + extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_EXT_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", + "SPV_EXT_shader_image_int64", + "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", + "SPV_KHR_fragment_shader_barycentric", + "SPV_KHR_vulkan_memory_model", + "SPV_NV_bindless_texture", + "SPV_EXT_shader_atomic_float_add", + "SPV_EXT_fragment_shader_interlock", + "SPV_KHR_compute_shader_derivatives", + "SPV_NV_cooperative_matrix", + "SPV_KHR_cooperative_matrix", + "SPV_KHR_ray_tracing_position_fetch", + "SPV_AMDX_shader_enqueue", + "SPV_KHR_fragment_shading_rate", + "SPV_KHR_ray_tracing", + "SPV_KHR_quad_control", + "SPV_GOOGLE_user_type"}); +} +bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { + std::vector users; + FindUses(var_inst, &users); + + Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users); + + if (store_inst == nullptr) { + return false; + } + + bool all_rewritten; + bool modified = RewriteLoads(store_inst, users, &all_rewritten); + + // If all uses are rewritten and the variable has a DebugDeclare and the + // variable is not an aggregate, add a DebugValue after the store and remove + // the DebugDeclare. + uint32_t var_id = var_inst->result_id(); + if (all_rewritten && + context()->get_debug_info_mgr()->IsVariableDebugDeclared(var_id)) { + const analysis::Type* var_type = + context()->get_type_mgr()->GetType(var_inst->type_id()); + const analysis::Type* store_type = var_type->AsPointer()->pointee_type(); + if (!(store_type->AsStruct() || store_type->AsArray())) { + modified |= RewriteDebugDeclares(store_inst, var_id); + } + } + + return modified; +} + +bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst, + uint32_t var_id) { + uint32_t value_id = store_inst->GetSingleWordInOperand(1); + bool modified = context()->get_debug_info_mgr()->AddDebugValueForVariable( + store_inst, var_id, value_id, store_inst); + modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + return modified; +} + +Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( + Instruction* var_inst, const std::vector& users) const { + // Make sure there is exactly 1 store. + Instruction* store_inst = nullptr; + + // If |var_inst| has an initializer, then that will count as a store. + if (var_inst->NumInOperands() > 1) { + store_inst = var_inst; + } + + for (Instruction* user : users) { + switch (user->opcode()) { + case spv::Op::OpStore: + // Since we are in the relaxed addressing mode, the use has to be the + // base address of the store, and not the value being store. Otherwise, + // we would have a pointer to a pointer to function scope memory, which + // is not allowed. + if (store_inst == nullptr) { + store_inst = user; + } else { + // More than 1 store. + return nullptr; + } + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (FeedsAStore(user)) { + // Has a partial store. Cannot propagate that. + return nullptr; + } + break; + case spv::Op::OpLoad: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpName: + case spv::Op::OpCopyObject: + break; + case spv::Op::OpExtInst: { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { + break; + } + return nullptr; + } + default: + if (!user->IsDecoration()) { + // Don't know if this instruction modifies the variable. + // Conservatively assume it is a store. + return nullptr; + } + break; + } + } + return store_inst; +} + +void LocalSingleStoreElimPass::FindUses( + const Instruction* var_inst, std::vector* users) const { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(var_inst, [users, this](Instruction* user) { + users->push_back(user); + if (user->opcode() == spv::Op::OpCopyObject) { + FindUses(user, users); + } + }); +} + +bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return !def_use_mgr->WhileEachUser(inst, [this](Instruction* user) { + switch (user->opcode()) { + case spv::Op::OpStore: + return false; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpCopyObject: + return !FeedsAStore(user); + case spv::Op::OpLoad: + case spv::Op::OpImageTexelPointer: + case spv::Op::OpName: + return true; + default: + // Don't know if this instruction modifies the variable. + // Conservatively assume it is a store. + return user->IsDecoration(); + } + }); +} + +bool LocalSingleStoreElimPass::RewriteLoads( + Instruction* store_inst, const std::vector& uses, + bool* all_rewritten) { + BasicBlock* store_block = context()->get_instr_block(store_inst); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(store_block->GetParent()); + + uint32_t stored_id; + if (store_inst->opcode() == spv::Op::OpStore) + stored_id = store_inst->GetSingleWordInOperand(kStoreValIdInIdx); + else + stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx); + + *all_rewritten = true; + bool modified = false; + for (Instruction* use : uses) { + if (use->opcode() == spv::Op::OpStore) continue; + auto dbg_op = use->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) + continue; + if (use->opcode() == spv::Op::OpLoad && + dominator_analysis->Dominates(store_inst, use)) { + modified = true; + context()->KillNamesAndDecorates(use->result_id()); + context()->ReplaceAllUsesWith(use->result_id(), stored_id); + context()->KillInst(use); + } else { + *all_rewritten = false; + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.h new file mode 100644 index 00000000..3aa0f02a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/local_single_store_elim_pass.h @@ -0,0 +1,108 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ +#define SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalSingleStoreElimPass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + LocalSingleStoreElimPass(); + + const char* name() const override { return "eliminate-local-single-store"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Do "single-store" optimization of function variables defined only + // with a single non-access-chain store in |func|. Replace all their + // non-access-chain loads with the value that is stored and eliminate + // any resulting dead code. + bool LocalSingleStoreElim(Function* func); + + // Initialize extensions allowlist + void InitExtensionAllowList(); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + Pass::Status ProcessImpl(); + + // If there is a single store to |var_inst|, and it covers the entire + // variable, then replace all of the loads of the entire variable that are + // dominated by the store by the value that was stored. Returns true if the + // module was changed. + bool ProcessVariable(Instruction* var_inst); + + // Collects all of the uses of |var_inst| into |uses|. This looks through + // OpObjectCopy's that copy the address of the variable, and collects those + // uses as well. + void FindUses(const Instruction* var_inst, + std::vector* uses) const; + + // Returns a store to |var_inst| if + // - it is a store to the entire variable, + // - and there are no other instructions that may modify |var_inst|. + Instruction* FindSingleStoreAndCheckUses( + Instruction* var_inst, const std::vector& users) const; + + // Returns true if the address that results from |inst| may be used as a base + // address in a store instruction or may be used to compute the base address + // of a store instruction. + bool FeedsAStore(Instruction* inst) const; + + // Replaces all of the loads in |uses| by the value stored in |store_inst|. + // The load instructions are then killed. |all_rewritten| is true iff all + // uses have been rewritten. + bool RewriteLoads(Instruction* store_inst, + const std::vector& uses, bool* all_rewritten); + + // Replaces DebugDeclares of |var_id| with DebugValues using the value + // assignment of |store_inst|. + bool RewriteDebugDeclares(Instruction* store_inst, uint32_t var_id); + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/log.h b/engine/engine/3rdlibs/spirv-tools/source/opt/log.h new file mode 100644 index 00000000..4fb66fd4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/log.h @@ -0,0 +1,222 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOG_H_ +#define SOURCE_OPT_LOG_H_ + +#include +#include +#include +#include + +#include "spirv-tools/libspirv.hpp" + +// Asserts the given condition is true. Otherwise, sends a message to the +// consumer and exits the program with failure code. Accepts the following +// formats: +// +// SPIRV_ASSERT(, ); +// SPIRV_ASSERT(, , ); +// SPIRV_ASSERT(, , +// , ); +// +// In the third format, the number of cannot exceed (5 - +// 2). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below. +#if !defined(NDEBUG) +#define SPIRV_ASSERT(consumer, ...) SPIRV_ASSERT_IMPL(consumer, __VA_ARGS__) +#else +// Adding a use to avoid errors in the release build related to unused +// consumers. +#define SPIRV_ASSERT(consumer, ...) (void)(consumer) +#endif + +// Logs a debug message to the consumer. Accepts the following formats: +// +// SPIRV_DEBUG(, ); +// SPIRV_DEBUG(, , ); +// +// In the second format, the number of cannot exceed (5 - +// 1). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below. +#if !defined(NDEBUG) && defined(SPIRV_LOG_DEBUG) +#define SPIRV_DEBUG(consumer, ...) SPIRV_DEBUG_IMPL(consumer, __VA_ARGS__) +#else +// Adding a use to avoid errors in the release build related to unused +// consumers. +#define SPIRV_DEBUG(consumer, ...) (void)(consumer) +#endif + +// Helper macros for concatenating arguments. +#define SPIRV_CONCATENATE(a, b) SPIRV_CONCATENATE_(a, b) +#define SPIRV_CONCATENATE_(a, b) a##b + +// Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler. +#define PP_EXPAND(x) x + +namespace spvtools { + +// Calls the given |consumer| by supplying the |message|. The |message| is from +// the given |source| and |location| and of the given severity |level|. +inline void Log(const MessageConsumer& consumer, spv_message_level_t level, + const char* source, const spv_position_t& position, + const char* message) { + if (consumer != nullptr) consumer(level, source, position, message); +} + +// Calls the given |consumer| by supplying the message composed according to the +// given |format|. The |message| is from the given |source| and |location| and +// of the given severity |level|. +template +void Logf(const MessageConsumer& consumer, spv_message_level_t level, + const char* source, const spv_position_t& position, + const char* format, Args&&... args) { +#if defined(_MSC_VER) && _MSC_VER < 1900 +// Sadly, snprintf() is not supported until Visual Studio 2015! +#define snprintf _snprintf +#endif + + enum { kInitBufferSize = 256 }; + + char message[kInitBufferSize]; + const int size = + snprintf(message, kInitBufferSize, format, std::forward(args)...); + + if (size >= 0 && size < kInitBufferSize) { + Log(consumer, level, source, position, message); + return; + } + + if (size >= 0) { + // The initial buffer is insufficient. Allocate a buffer of a larger size, + // and write to it instead. Force the size to be unsigned to avoid a + // warning in GCC 7.1. + std::vector longer_message(size + 1u); + snprintf(longer_message.data(), longer_message.size(), format, + std::forward(args)...); + Log(consumer, level, source, position, longer_message.data()); + return; + } + + Log(consumer, level, source, position, "cannot compose log message"); + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#undef snprintf +#endif +} + +// Calls the given |consumer| by supplying the given error |message|. The +// |message| is from the given |source| and |location|. +inline void Error(const MessageConsumer& consumer, const char* source, + const spv_position_t& position, const char* message) { + Log(consumer, SPV_MSG_ERROR, source, position, message); +} + +// Calls the given |consumer| by supplying the error message composed according +// to the given |format|. The |message| is from the given |source| and +// |location|. +template +inline void Errorf(const MessageConsumer& consumer, const char* source, + const spv_position_t& position, const char* format, + Args&&... args) { + Logf(consumer, SPV_MSG_ERROR, source, position, format, + std::forward(args)...); +} + +} // namespace spvtools + +#define SPIRV_ASSERT_IMPL(consumer, ...) \ + PP_EXPAND(SPIRV_CONCATENATE(SPIRV_ASSERT_, PP_NARGS(__VA_ARGS__))( \ + consumer, __VA_ARGS__)) + +#define SPIRV_DEBUG_IMPL(consumer, ...) \ + PP_EXPAND(SPIRV_CONCATENATE(SPIRV_DEBUG_, PP_NARGS(__VA_ARGS__))( \ + consumer, __VA_ARGS__)) + +#define SPIRV_ASSERT_1(consumer, condition) \ + do { \ + if (!(condition)) { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " #condition); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_2(consumer, condition, message) \ + do { \ + if (!(condition)) { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " message); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_more(consumer, condition, format, ...) \ + do { \ + if (!(condition)) { \ + spvtools::Logf(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " format, __VA_ARGS__); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_3(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_ASSERT_4(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_ASSERT_5(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_DEBUG_1(consumer, message) \ + do { \ + spvtools::Log(consumer, SPV_MSG_DEBUG, __FILE__, \ + {static_cast(__LINE__), 0, 0}, message); \ + } while (0) + +#define SPIRV_DEBUG_more(consumer, format, ...) \ + do { \ + spvtools::Logf(consumer, SPV_MSG_DEBUG, __FILE__, \ + {static_cast(__LINE__), 0, 0}, format, \ + __VA_ARGS__); \ + } while (0) + +#define SPIRV_DEBUG_2(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_3(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_4(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_5(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +// Macros for counting the number of arguments passed in. +#define PP_NARGS(...) PP_EXPAND(PP_ARG_N(__VA_ARGS__, 5, 4, 3, 2, 1, 0)) +#define PP_ARG_N(_1, _2, _3, _4, _5, N, ...) N + +// Tests for making sure that PP_NARGS() behaves as expected. +static_assert(PP_NARGS(0) == 1, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0) == 2, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0) == 3, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0, 0) == 4, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0, 0, 0) == 5, "PP_NARGS macro error"); +static_assert(PP_NARGS(1 + 1, 2, 3 / 3) == 3, "PP_NARGS macro error"); +static_assert(PP_NARGS((1, 1), 2, (3, 3)) == 3, "PP_NARGS macro error"); + +#endif // SOURCE_OPT_LOG_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.cpp new file mode 100644 index 00000000..e41c044a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.cpp @@ -0,0 +1,1674 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_dependence.h" + +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +using SubscriptPair = std::pair; + +namespace { + +// Calculate the greatest common divisor of a & b using Stein's algorithm. +// https://en.wikipedia.org/wiki/Binary_GCD_algorithm +int64_t GreatestCommonDivisor(int64_t a, int64_t b) { + // Simple cases + if (a == b) { + return a; + } else if (a == 0) { + return b; + } else if (b == 0) { + return a; + } + + // Both even + if (a % 2 == 0 && b % 2 == 0) { + return 2 * GreatestCommonDivisor(a / 2, b / 2); + } + + // Even a, odd b + if (a % 2 == 0 && b % 2 == 1) { + return GreatestCommonDivisor(a / 2, b); + } + + // Odd a, even b + if (a % 2 == 1 && b % 2 == 0) { + return GreatestCommonDivisor(a, b / 2); + } + + // Both odd, reduce the larger argument + if (a > b) { + return GreatestCommonDivisor((a - b) / 2, b); + } else { + return GreatestCommonDivisor((b - a) / 2, a); + } +} + +// Check if node is affine, ie in the form: a0*i0 + a1*i1 + ... an*in + c +// and contains only the following types of nodes: SERecurrentNode, SEAddNode +// and SEConstantNode +bool IsInCorrectFormForGCDTest(SENode* node) { + bool children_ok = true; + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + children_ok &= IsInCorrectFormForGCDTest(child); + } + } + + bool this_ok = node->AsSERecurrentNode() || node->AsSEAddNode() || + node->AsSEConstantNode(); + + return children_ok && this_ok; +} + +// If |node| is an SERecurrentNode then returns |node| or if |node| is an +// SEAddNode returns a vector of SERecurrentNode that are its children. +std::vector GetAllTopLevelRecurrences(SENode* node) { + auto nodes = std::vector{}; + if (auto recurrent_node = node->AsSERecurrentNode()) { + nodes.push_back(recurrent_node); + } + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + auto child_nodes = GetAllTopLevelRecurrences(child); + nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); + } + } + + return nodes; +} + +// If |node| is an SEConstantNode then returns |node| or if |node| is an +// SEAddNode returns a vector of SEConstantNode that are its children. +std::vector GetAllTopLevelConstants(SENode* node) { + auto nodes = std::vector{}; + if (auto recurrent_node = node->AsSEConstantNode()) { + nodes.push_back(recurrent_node); + } + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + auto child_nodes = GetAllTopLevelConstants(child); + nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); + } + } + + return nodes; +} + +bool AreOffsetsAndCoefficientsConstant( + const std::vector& nodes) { + for (auto node : nodes) { + if (!node->GetOffset()->AsSEConstantNode() || + !node->GetOffset()->AsSEConstantNode()) { + return false; + } + } + return true; +} + +// Fold all SEConstantNode that appear in |recurrences| and |constants| into a +// single integer value. +int64_t CalculateConstantTerm(const std::vector& recurrences, + const std::vector& constants) { + int64_t constant_term = 0; + for (auto recurrence : recurrences) { + constant_term += + recurrence->GetOffset()->AsSEConstantNode()->FoldToSingleValue(); + } + + for (auto constant : constants) { + constant_term += constant->FoldToSingleValue(); + } + + return constant_term; +} + +int64_t CalculateGCDFromCoefficients( + const std::vector& recurrences, int64_t running_gcd) { + for (SERecurrentNode* recurrence : recurrences) { + auto coefficient = recurrence->GetCoefficient()->AsSEConstantNode(); + + running_gcd = GreatestCommonDivisor( + running_gcd, std::abs(coefficient->FoldToSingleValue())); + } + + return running_gcd; +} + +// Compare 2 fractions while first normalizing them, e.g. 2/4 and 4/8 will both +// be simplified to 1/2 and then determined to be equal. +bool NormalizeAndCompareFractions(int64_t numerator_0, int64_t denominator_0, + int64_t numerator_1, int64_t denominator_1) { + auto gcd_0 = + GreatestCommonDivisor(std::abs(numerator_0), std::abs(denominator_0)); + auto gcd_1 = + GreatestCommonDivisor(std::abs(numerator_1), std::abs(denominator_1)); + + auto normalized_numerator_0 = numerator_0 / gcd_0; + auto normalized_denominator_0 = denominator_0 / gcd_0; + auto normalized_numerator_1 = numerator_1 / gcd_1; + auto normalized_denominator_1 = denominator_1 / gcd_1; + + return normalized_numerator_0 == normalized_numerator_1 && + normalized_denominator_0 == normalized_denominator_1; +} + +} // namespace + +bool LoopDependenceAnalysis::GetDependence(const Instruction* source, + const Instruction* destination, + DistanceVector* distance_vector) { + // Start off by finding and marking all the loops in |loops_| that are + // irrelevant to the dependence analysis. + MarkUnsusedDistanceEntriesAsIrrelevant(source, destination, distance_vector); + + Instruction* source_access_chain = GetOperandDefinition(source, 0); + Instruction* destination_access_chain = GetOperandDefinition(destination, 0); + + auto num_access_chains = + (source_access_chain->opcode() == spv::Op::OpAccessChain) + + (destination_access_chain->opcode() == spv::Op::OpAccessChain); + + // If neither is an access chain, then they are load/store to a variable. + if (num_access_chains == 0) { + if (source_access_chain != destination_access_chain) { + // Not the same location, report independence + return true; + } else { + // Accessing the same variable + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + } + + // If only one is an access chain, it could be accessing a part of a struct + if (num_access_chains == 1) { + auto source_is_chain = + source_access_chain->opcode() == spv::Op::OpAccessChain; + auto access_chain = + source_is_chain ? source_access_chain : destination_access_chain; + auto variable = + source_is_chain ? destination_access_chain : source_access_chain; + + auto location_in_chain = GetOperandDefinition(access_chain, 0); + + if (variable != location_in_chain) { + // Not the same location, report independence + return true; + } else { + // Accessing the same variable + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + } + + // If the access chains aren't collecting from the same structure there is no + // dependence. + Instruction* source_array = GetOperandDefinition(source_access_chain, 0); + Instruction* destination_array = + GetOperandDefinition(destination_access_chain, 0); + + // Nested access chains are not supported yet, bail out. + if (source_array->opcode() == spv::Op::OpAccessChain || + destination_array->opcode() == spv::Op::OpAccessChain) { + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + + if (source_array != destination_array) { + PrintDebug("Proved independence through different arrays."); + return true; + } + + // To handle multiple subscripts we must get every operand in the access + // chains past the first. + std::vector source_subscripts = GetSubscripts(source); + std::vector destination_subscripts = GetSubscripts(destination); + + auto sets_of_subscripts = + PartitionSubscripts(source_subscripts, destination_subscripts); + + auto first_coupled = std::partition( + std::begin(sets_of_subscripts), std::end(sets_of_subscripts), + [](const std::set>& set) { + return set.size() == 1; + }); + + // Go through each subscript testing for independence. + // If any subscript results in independence, we prove independence between the + // load and store. + // If we can't prove independence we store what information we can gather in + // a DistanceVector. + for (auto it = std::begin(sets_of_subscripts); it < first_coupled; ++it) { + auto source_subscript = std::get<0>(*(*it).begin()); + auto destination_subscript = std::get<1>(*(*it).begin()); + + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_subscript)); + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_subscript)); + + // Check the loops are in a form we support. + auto subscript_pair = std::make_pair(source_node, destination_node); + + const Loop* loop = GetLoopForSubscriptPair(subscript_pair); + if (loop) { + if (!IsSupportedLoop(loop)) { + PrintDebug( + "GetDependence found an unsupported loop form. Assuming <=> for " + "loop."); + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (distance_entry) { + distance_entry->direction = DistanceEntry::Directions::ALL; + } + continue; + } + } + + // If either node is simplified to a CanNotCompute we can't perform any + // analysis so must assume <=> dependence and return. + if (source_node->GetType() == SENode::CanNotCompute || + destination_node->GetType() == SENode::CanNotCompute) { + // Record the <=> dependence if we can get a DistanceEntry + PrintDebug( + "GetDependence found source_node || destination_node as " + "CanNotCompute. Abandoning evaluation for this subscript."); + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (distance_entry) { + distance_entry->direction = DistanceEntry::Directions::ALL; + } + continue; + } + + // We have no induction variables so can apply a ZIV test. + if (IsZIV(subscript_pair)) { + PrintDebug("Found a ZIV subscript pair"); + if (ZIVTest(subscript_pair)) { + PrintDebug("Proved independence with ZIVTest."); + return true; + } + } + + // We have only one induction variable so should attempt an SIV test. + if (IsSIV(subscript_pair)) { + PrintDebug("Found a SIV subscript pair."); + if (SIVTest(subscript_pair, distance_vector)) { + PrintDebug("Proved independence with SIVTest."); + return true; + } + } + + // We have multiple induction variables so should attempt an MIV test. + if (IsMIV(subscript_pair)) { + PrintDebug("Found a MIV subscript pair."); + if (GCDMIVTest(subscript_pair)) { + PrintDebug("Proved independence with the GCD test."); + auto current_loops = CollectLoops(source_node, destination_node); + + for (auto current_loop : current_loops) { + auto distance_entry = + GetDistanceEntryForLoop(current_loop, distance_vector); + distance_entry->direction = DistanceEntry::Directions::NONE; + } + return true; + } + } + } + + for (auto it = first_coupled; it < std::end(sets_of_subscripts); ++it) { + auto coupled_instructions = *it; + std::vector coupled_subscripts{}; + + for (const auto& elem : coupled_instructions) { + auto source_subscript = std::get<0>(elem); + auto destination_subscript = std::get<1>(elem); + + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_subscript)); + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_subscript)); + + coupled_subscripts.push_back({source_node, destination_node}); + } + + auto supported = true; + + for (const auto& subscript : coupled_subscripts) { + auto loops = CollectLoops(std::get<0>(subscript), std::get<1>(subscript)); + + auto is_subscript_supported = + std::all_of(std::begin(loops), std::end(loops), + [this](const Loop* l) { return IsSupportedLoop(l); }); + + supported = supported && is_subscript_supported; + } + + if (DeltaTest(coupled_subscripts, distance_vector)) { + return true; + } + } + + // We were unable to prove independence so must gather all of the direction + // information we found. + PrintDebug( + "Couldn't prove independence.\n" + "All possible direction information has been collected in the input " + "DistanceVector."); + + return false; +} + +bool LoopDependenceAnalysis::ZIVTest( + const std::pair& subscript_pair) { + auto source = std::get<0>(subscript_pair); + auto destination = std::get<1>(subscript_pair); + + PrintDebug("Performing ZIVTest"); + // If source == destination, dependence with direction = and distance 0. + if (source == destination) { + PrintDebug("ZIVTest found EQ dependence."); + return false; + } else { + PrintDebug("ZIVTest found independence."); + // Otherwise we prove independence. + return true; + } +} + +bool LoopDependenceAnalysis::SIVTest( + const std::pair& subscript_pair, + DistanceVector* distance_vector) { + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (!distance_entry) { + PrintDebug( + "SIVTest could not find a DistanceEntry for subscript_pair. Exiting"); + } + + SENode* source_node = std::get<0>(subscript_pair); + SENode* destination_node = std::get<1>(subscript_pair); + + int64_t source_induction_count = CountInductionVariables(source_node); + int64_t destination_induction_count = + CountInductionVariables(destination_node); + + // If the source node has no induction variables we can apply a + // WeakZeroSrcTest. + if (source_induction_count == 0) { + PrintDebug("Found source has no induction variable."); + if (WeakZeroSourceSIVTest( + source_node, destination_node->AsSERecurrentNode(), + destination_node->AsSERecurrentNode()->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakZeroSourceSIVTest."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // If the destination has no induction variables we can apply a + // WeakZeroDestTest. + if (destination_induction_count == 0) { + PrintDebug("Found destination has no induction variable."); + if (WeakZeroDestinationSIVTest( + source_node->AsSERecurrentNode(), destination_node, + source_node->AsSERecurrentNode()->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakZeroDestinationSIVTest."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // We now need to collect the SERecurrentExpr nodes from source and + // destination. We do not handle cases where source or destination have + // multiple SERecurrentExpr nodes. + std::vector source_recurrent_nodes = + source_node->CollectRecurrentNodes(); + std::vector destination_recurrent_nodes = + destination_node->CollectRecurrentNodes(); + + if (source_recurrent_nodes.size() == 1 && + destination_recurrent_nodes.size() == 1) { + PrintDebug("Found source and destination have 1 induction variable."); + SERecurrentNode* source_recurrent_expr = *source_recurrent_nodes.begin(); + SERecurrentNode* destination_recurrent_expr = + *destination_recurrent_nodes.begin(); + + // If the coefficients are identical we can apply a StrongSIVTest. + if (source_recurrent_expr->GetCoefficient() == + destination_recurrent_expr->GetCoefficient()) { + PrintDebug("Found source and destination share coefficient."); + if (StrongSIVTest(source_node, destination_node, + source_recurrent_expr->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with StrongSIVTest"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // If the coefficients are of equal magnitude and opposite sign we can + // apply a WeakCrossingSIVTest. + if (source_recurrent_expr->GetCoefficient() == + scalar_evolution_.CreateNegation( + destination_recurrent_expr->GetCoefficient())) { + PrintDebug("Found source coefficient = -destination coefficient."); + if (WeakCrossingSIVTest(source_node, destination_node, + source_recurrent_expr->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakCrossingSIVTest"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + } + + return false; +} + +bool LoopDependenceAnalysis::StrongSIVTest(SENode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing StrongSIVTest."); + // If both source and destination are SERecurrentNodes we can perform tests + // based on distance. + // If either source or destination contain value unknown nodes or if one or + // both are not SERecurrentNodes we must attempt a symbolic test. + std::vector source_value_unknown_nodes = + source->CollectValueUnknownNodes(); + std::vector destination_value_unknown_nodes = + destination->CollectValueUnknownNodes(); + if (source_value_unknown_nodes.size() > 0 || + destination_value_unknown_nodes.size() > 0) { + PrintDebug( + "StrongSIVTest found symbolics. Will attempt SymbolicStrongSIVTest."); + return SymbolicStrongSIVTest(source, destination, coefficient, + distance_entry); + } + + if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { + PrintDebug( + "StrongSIVTest could not simplify source and destination to " + "SERecurrentNodes so will exit."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; + } + + // Build an SENode for distance. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + SENode* source_constant_term = + GetConstantTerm(subscript_loop, source->AsSERecurrentNode()); + SENode* destination_constant_term = + GetConstantTerm(subscript_loop, destination->AsSERecurrentNode()); + if (!source_constant_term || !destination_constant_term) { + PrintDebug( + "StrongSIVTest could not collect the constant terms of either source " + "or destination so will exit."); + return false; + } + SENode* constant_term_delta = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + destination_constant_term, source_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + // We must check the offset delta and coefficient are constants. + int64_t distance = 0; + SEConstantNode* delta_constant = constant_term_delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + PrintDebug( + "StrongSIVTest found delta value and coefficient value as constants " + "with values:\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + "\n"); + // Check if the distance is not integral to try to prove independence. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "StrongSIVTest proved independence through distance not being an " + "integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug("StrongSIV test found distance as " + ToString(distance)); + } + } else { + // If we can't fold delta and coefficient to single values we can't produce + // distance. + // As a result we can't perform the rest of the pass and must assume + // dependence in all directions. + PrintDebug("StrongSIVTest could not produce a distance. Must exit."); + distance_entry->distance = DistanceEntry::Directions::ALL; + return false; + } + + // Next we gather the upper and lower bounds as constants if possible. If + // distance > upper_bound - lower_bound we prove independence. + SENode* lower_bound = GetLowerBound(subscript_loop); + SENode* upper_bound = GetUpperBound(subscript_loop); + if (lower_bound && upper_bound) { + PrintDebug("StrongSIVTest found bounds."); + SENode* bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); + + if (bounds->GetType() == SENode::SENodeType::Constant) { + int64_t bounds_value = bounds->AsSEConstantNode()->FoldToSingleValue(); + PrintDebug( + "StrongSIVTest found upper_bound - lower_bound as a constant with " + "value " + + ToString(bounds_value)); + + // If the absolute value of the distance is > upper bound - lower bound + // then we prove independence. + if (llabs(distance) > llabs(bounds_value)) { + PrintDebug( + "StrongSIVTest proved independence through distance escaping the " + "loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } + } else { + PrintDebug("StrongSIVTest was unable to gather lower and upper bounds."); + } + + // Otherwise we can get a direction as follows + // { < if distance > 0 + // direction = { = if distance == 0 + // { > if distance < 0 + PrintDebug( + "StrongSIVTest could not prove independence. Gathering direction " + "information."); + if (distance > 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::LT; + distance_entry->distance = distance; + return false; + } + if (distance == 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::EQ; + distance_entry->distance = 0; + return false; + } + if (distance < 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::GT; + distance_entry->distance = distance; + return false; + } + + // We were unable to prove independence or discern any additional information + // Must assume <=> direction. + PrintDebug( + "StrongSIVTest was unable to determine any dependence information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::SymbolicStrongSIVTest( + SENode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing SymbolicStrongSIVTest."); + SENode* source_destination_delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(source, destination)); + // By cancelling out the induction variables by subtracting the source and + // destination we can produce an expression of symbolics and constants. This + // expression can be compared to the loop bounds to find if the offset is + // outwith the bounds. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + if (IsProvablyOutsideOfLoopBounds(subscript_loop, source_destination_delta, + coefficient)) { + PrintDebug( + "SymbolicStrongSIVTest proved independence through loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "SymbolicStrongSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakZeroSourceSIVTest( + SENode* source, SERecurrentNode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakZeroSourceSIVTest."); + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + // Build an SENode for distance. + SENode* destination_constant_term = + GetConstantTerm(subscript_loop, destination); + SENode* delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(source, destination_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakZeroSourceSIVTest folding delta and coefficient to constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "WeakZeroSourceSIVTest proved independence through distance not " + "being an integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug( + "WeakZeroSourceSIVTest calculated distance with the following " + "values\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + + "\n\tdistance: " + ToString(distance) + "\n"); + } + } else { + PrintDebug( + "WeakZeroSourceSIVTest was unable to fold delta and coefficient to " + "constants."); + } + + // If we can prove the distance is outside the bounds we prove independence. + SEConstantNode* lower_bound = + GetLowerBound(subscript_loop)->AsSEConstantNode(); + SEConstantNode* upper_bound = + GetUpperBound(subscript_loop)->AsSEConstantNode(); + if (lower_bound && upper_bound) { + PrintDebug("WeakZeroSourceSIVTest found bounds as SEConstantNodes."); + int64_t lower_bound_value = lower_bound->FoldToSingleValue(); + int64_t upper_bound_value = upper_bound->FoldToSingleValue(); + if (!IsWithinBounds(llabs(distance), lower_bound_value, + upper_bound_value)) { + PrintDebug( + "WeakZeroSourceSIVTest proved independence through distance escaping " + "the loop bounds."); + PrintDebug( + "Bound values were as follow\n" + "\tlower bound value: " + + ToString(lower_bound_value) + + "\n\tupper bound value: " + ToString(upper_bound_value) + + "\n\tdistance value: " + ToString(distance) + "\n"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } else { + PrintDebug( + "WeakZeroSourceSIVTest was unable to find lower and upper bound as " + "SEConstantNodes."); + } + + // Now we want to see if we can detect to peel the first or last iterations. + + // We get the FirstTripValue as GetFirstTripInductionNode() + + // GetConstantTerm(destination) + SENode* first_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFirstTripInductionNode(subscript_loop), + GetConstantTerm(subscript_loop, destination))); + + // If source == FirstTripValue, peel_first. + if (first_trip_SENode) { + PrintDebug("WeakZeroSourceSIVTest built first_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroSourceSIVTest has found first_trip_SENode as an " + "SEConstantNode with value: " + + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (source == first_trip_SENode) { + // We have found that peeling the first iteration will break dependency. + PrintDebug( + "WeakZeroSourceSIVTest has found peeling first iteration will break " + "dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_first = true; + return false; + } + } else { + PrintDebug("WeakZeroSourceSIVTest was unable to build first_trip_SENode"); + } + + // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + + // GetConstantTerm(destination) + SENode* final_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFinalTripInductionNode(subscript_loop, coefficient), + GetConstantTerm(subscript_loop, destination))); + + // If source == LastTripValue, peel_last. + if (final_trip_SENode) { + PrintDebug("WeakZeroSourceSIVTest built final_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroSourceSIVTest has found final_trip_SENode as an " + "SEConstantNode with value: " + + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (source == final_trip_SENode) { + // We have found that peeling the last iteration will break dependency. + PrintDebug( + "WeakZeroSourceSIVTest has found peeling final iteration will break " + "dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_last = true; + return false; + } + } else { + PrintDebug("WeakZeroSourceSIVTest was unable to build final_trip_SENode"); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakZeroSourceSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakZeroDestinationSIVTest( + SERecurrentNode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakZeroDestinationSIVTest."); + // Build an SENode for distance. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + SENode* source_constant_term = GetConstantTerm(subscript_loop, source); + SENode* delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(destination, source_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakZeroDestinationSIVTest folding delta and coefficient to " + "constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "WeakZeroDestinationSIVTest proved independence through distance not " + "being an integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug( + "WeakZeroDestinationSIVTest calculated distance with the following " + "values\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + + "\n\tdistance: " + ToString(distance) + "\n"); + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to fold delta and coefficient " + "to constants."); + } + + // If we can prove the distance is outside the bounds we prove independence. + SEConstantNode* lower_bound = + GetLowerBound(subscript_loop)->AsSEConstantNode(); + SEConstantNode* upper_bound = + GetUpperBound(subscript_loop)->AsSEConstantNode(); + if (lower_bound && upper_bound) { + PrintDebug("WeakZeroDestinationSIVTest found bounds as SEConstantNodes."); + int64_t lower_bound_value = lower_bound->FoldToSingleValue(); + int64_t upper_bound_value = upper_bound->FoldToSingleValue(); + if (!IsWithinBounds(llabs(distance), lower_bound_value, + upper_bound_value)) { + PrintDebug( + "WeakZeroDestinationSIVTest proved independence through distance " + "escaping the loop bounds."); + PrintDebug( + "Bound values were as follows\n" + "\tlower bound value: " + + ToString(lower_bound_value) + + "\n\tupper bound value: " + ToString(upper_bound_value) + + "\n\tdistance value: " + ToString(distance)); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to find lower and upper bound " + "as SEConstantNodes."); + } + + // Now we want to see if we can detect to peel the first or last iterations. + + // We get the FirstTripValue as GetFirstTripInductionNode() + + // GetConstantTerm(source) + SENode* first_trip_SENode = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateAddNode(GetFirstTripInductionNode(subscript_loop), + GetConstantTerm(subscript_loop, source))); + + // If destination == FirstTripValue, peel_first. + if (first_trip_SENode) { + PrintDebug("WeakZeroDestinationSIVTest built first_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroDestinationSIVTest has found first_trip_SENode as an " + "SEConstantNode with value: " + + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (destination == first_trip_SENode) { + // We have found that peeling the first iteration will break dependency. + PrintDebug( + "WeakZeroDestinationSIVTest has found peeling first iteration will " + "break dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_first = true; + return false; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to build first_trip_SENode"); + } + + // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + + // GetConstantTerm(source) + SENode* final_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFinalTripInductionNode(subscript_loop, coefficient), + GetConstantTerm(subscript_loop, source))); + + // If destination == LastTripValue, peel_last. + if (final_trip_SENode) { + PrintDebug("WeakZeroDestinationSIVTest built final_trip_SENode."); + if (final_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroDestinationSIVTest has found final_trip_SENode as an " + "SEConstantNode with value: " + + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (destination == final_trip_SENode) { + // We have found that peeling the last iteration will break dependency. + PrintDebug( + "WeakZeroDestinationSIVTest has found peeling final iteration will " + "break dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_last = true; + return false; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to build final_trip_SENode"); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakZeroDestinationSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakCrossingSIVTest( + SENode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakCrossingSIVTest."); + // We currently can't handle symbolic WeakCrossingSIVTests. If either source + // or destination are not SERecurrentNodes we must exit. + if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { + PrintDebug( + "WeakCrossingSIVTest found source or destination != SERecurrentNode. " + "Exiting"); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; + } + + // Build an SENode for distance. + SENode* offset_delta = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + destination->AsSERecurrentNode()->GetOffset(), + source->AsSERecurrentNode()->GetOffset())); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = offset_delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakCrossingSIVTest folding offset_delta and coefficient to " + "constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral or if it has a non-integral part + // equal to 1/2. + if (delta_value % (2 * coefficient_value) != 0 && + static_cast(delta_value % (2 * coefficient_value)) / + static_cast(2 * coefficient_value) != + 0.5) { + PrintDebug( + "WeakCrossingSIVTest proved independence through distance escaping " + "the loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / (2 * coefficient_value); + } + + if (distance == 0) { + PrintDebug("WeakCrossingSIVTest found EQ dependence."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::EQ; + distance_entry->distance = 0; + return false; + } + } else { + PrintDebug( + "WeakCrossingSIVTest was unable to fold offset_delta and coefficient " + "to constants."); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakCrossingSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +// Perform the GCD test if both, the source and the destination nodes, are in +// the form a0*i0 + a1*i1 + ... an*in + c. +bool LoopDependenceAnalysis::GCDMIVTest( + const std::pair& subscript_pair) { + auto source = std::get<0>(subscript_pair); + auto destination = std::get<1>(subscript_pair); + + // Bail out if source/destination is in an unexpected form. + if (!IsInCorrectFormForGCDTest(source) || + !IsInCorrectFormForGCDTest(destination)) { + return false; + } + + auto source_recurrences = GetAllTopLevelRecurrences(source); + auto dest_recurrences = GetAllTopLevelRecurrences(destination); + + // Bail out if all offsets and coefficients aren't constant. + if (!AreOffsetsAndCoefficientsConstant(source_recurrences) || + !AreOffsetsAndCoefficientsConstant(dest_recurrences)) { + return false; + } + + // Calculate the GCD of all coefficients. + auto source_constants = GetAllTopLevelConstants(source); + int64_t source_constant = + CalculateConstantTerm(source_recurrences, source_constants); + + auto dest_constants = GetAllTopLevelConstants(destination); + int64_t destination_constant = + CalculateConstantTerm(dest_recurrences, dest_constants); + + int64_t delta = std::abs(source_constant - destination_constant); + + int64_t running_gcd = 0; + + running_gcd = CalculateGCDFromCoefficients(source_recurrences, running_gcd); + running_gcd = CalculateGCDFromCoefficients(dest_recurrences, running_gcd); + + return delta % running_gcd != 0; +} + +using PartitionedSubscripts = + std::vector>>; +PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts( + const std::vector& source_subscripts, + const std::vector& destination_subscripts) { + PartitionedSubscripts partitions{}; + + auto num_subscripts = source_subscripts.size(); + + // Create initial partitions with one subscript pair per partition. + for (size_t i = 0; i < num_subscripts; ++i) { + partitions.push_back({{source_subscripts[i], destination_subscripts[i]}}); + } + + // Iterate over the loops to create all partitions + for (auto loop : loops_) { + int64_t k = -1; + + for (size_t j = 0; j < partitions.size(); ++j) { + auto& current_partition = partitions[j]; + + // Does |loop| appear in |current_partition| + auto it = std::find_if( + current_partition.begin(), current_partition.end(), + [loop, + this](const std::pair& elem) -> bool { + auto source_recurrences = + scalar_evolution_.AnalyzeInstruction(std::get<0>(elem)) + ->CollectRecurrentNodes(); + auto destination_recurrences = + scalar_evolution_.AnalyzeInstruction(std::get<1>(elem)) + ->CollectRecurrentNodes(); + + source_recurrences.insert(source_recurrences.end(), + destination_recurrences.begin(), + destination_recurrences.end()); + + auto loops_in_pair = CollectLoops(source_recurrences); + auto end_it = loops_in_pair.end(); + + return std::find(loops_in_pair.begin(), end_it, loop) != end_it; + }); + + auto has_loop = it != current_partition.end(); + + if (has_loop) { + if (k == -1) { + k = j; + } else { + // Add |partitions[j]| to |partitions[k]| and discard |partitions[j]| + partitions[static_cast(k)].insert(current_partition.begin(), + current_partition.end()); + current_partition.clear(); + } + } + } + } + + // Remove empty (discarded) partitions + partitions.erase( + std::remove_if( + partitions.begin(), partitions.end(), + [](const std::set>& partition) { + return partition.empty(); + }), + partitions.end()); + + return partitions; +} + +Constraint* LoopDependenceAnalysis::IntersectConstraints( + Constraint* constraint_0, Constraint* constraint_1, + const SENode* lower_bound, const SENode* upper_bound) { + if (constraint_0->AsDependenceNone()) { + return constraint_1; + } else if (constraint_1->AsDependenceNone()) { + return constraint_0; + } + + // Both constraints are distances. Either the same distance or independent. + if (constraint_0->AsDependenceDistance() && + constraint_1->AsDependenceDistance()) { + auto dist_0 = constraint_0->AsDependenceDistance(); + auto dist_1 = constraint_1->AsDependenceDistance(); + + if (*dist_0->GetDistance() == *dist_1->GetDistance()) { + return constraint_0; + } else { + return make_constraint(); + } + } + + // Both constraints are points. Either the same point or independent. + if (constraint_0->AsDependencePoint() && constraint_1->AsDependencePoint()) { + auto point_0 = constraint_0->AsDependencePoint(); + auto point_1 = constraint_1->AsDependencePoint(); + + if (*point_0->GetSource() == *point_1->GetSource() && + *point_0->GetDestination() == *point_1->GetDestination()) { + return constraint_0; + } else { + return make_constraint(); + } + } + + // Both constraints are lines/distances. + if ((constraint_0->AsDependenceDistance() || + constraint_0->AsDependenceLine()) && + (constraint_1->AsDependenceDistance() || + constraint_1->AsDependenceLine())) { + auto is_distance_0 = constraint_0->AsDependenceDistance() != nullptr; + auto is_distance_1 = constraint_1->AsDependenceDistance() != nullptr; + + auto a0 = is_distance_0 ? scalar_evolution_.CreateConstant(1) + : constraint_0->AsDependenceLine()->GetA(); + auto b0 = is_distance_0 ? scalar_evolution_.CreateConstant(-1) + : constraint_0->AsDependenceLine()->GetB(); + auto c0 = + is_distance_0 + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + constraint_0->AsDependenceDistance()->GetDistance())) + : constraint_0->AsDependenceLine()->GetC(); + + auto a1 = is_distance_1 ? scalar_evolution_.CreateConstant(1) + : constraint_1->AsDependenceLine()->GetA(); + auto b1 = is_distance_1 ? scalar_evolution_.CreateConstant(-1) + : constraint_1->AsDependenceLine()->GetB(); + auto c1 = + is_distance_1 + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + constraint_1->AsDependenceDistance()->GetDistance())) + : constraint_1->AsDependenceLine()->GetC(); + + if (a0->AsSEConstantNode() && b0->AsSEConstantNode() && + c0->AsSEConstantNode() && a1->AsSEConstantNode() && + b1->AsSEConstantNode() && c1->AsSEConstantNode()) { + auto constant_a0 = a0->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b0 = b0->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c0 = c0->AsSEConstantNode()->FoldToSingleValue(); + + auto constant_a1 = a1->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b1 = b1->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c1 = c1->AsSEConstantNode()->FoldToSingleValue(); + + // a & b can't both be zero, otherwise it wouldn't be line. + if (NormalizeAndCompareFractions(constant_a0, constant_b0, constant_a1, + constant_b1)) { + // Slopes are equal, either parallel lines or the same line. + + if (constant_b0 == 0 && constant_b1 == 0) { + if (NormalizeAndCompareFractions(constant_c0, constant_a0, + constant_c1, constant_a1)) { + return constraint_0; + } + + return make_constraint(); + } else if (NormalizeAndCompareFractions(constant_c0, constant_b0, + constant_c1, constant_b1)) { + // Same line. + return constraint_0; + } else { + // Parallel lines can't intersect, report independence. + return make_constraint(); + } + + } else { + // Lines are not parallel, therefore, they must intersect. + + // Calculate intersection. + if (upper_bound->AsSEConstantNode() && + lower_bound->AsSEConstantNode()) { + auto constant_lower_bound = + lower_bound->AsSEConstantNode()->FoldToSingleValue(); + auto constant_upper_bound = + upper_bound->AsSEConstantNode()->FoldToSingleValue(); + + auto up = constant_b1 * constant_c0 - constant_b0 * constant_c1; + // Both b or both a can't be 0, so down is never 0 + // otherwise would have entered the parallel line section. + auto down = constant_b1 * constant_a0 - constant_b0 * constant_a1; + + auto x_coord = up / down; + + int64_t y_coord = 0; + int64_t arg1 = 0; + int64_t const_b_to_use = 0; + + if (constant_b1 != 0) { + arg1 = constant_c1 - constant_a1 * x_coord; + y_coord = arg1 / constant_b1; + const_b_to_use = constant_b1; + } else if (constant_b0 != 0) { + arg1 = constant_c0 - constant_a0 * x_coord; + y_coord = arg1 / constant_b0; + const_b_to_use = constant_b0; + } + + if (up % down == 0 && + arg1 % const_b_to_use == 0 && // Coordinates are integers. + constant_lower_bound <= + x_coord && // x_coord is within loop bounds. + x_coord <= constant_upper_bound && + constant_lower_bound <= + y_coord && // y_coord is within loop bounds. + y_coord <= constant_upper_bound) { + // Lines intersect at integer coordinates. + return make_constraint( + scalar_evolution_.CreateConstant(x_coord), + scalar_evolution_.CreateConstant(y_coord), + constraint_0->GetLoop()); + + } else { + return make_constraint(); + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + // One constraint is a line/distance and the other is a point. + if ((constraint_0->AsDependencePoint() && + (constraint_1->AsDependenceLine() || + constraint_1->AsDependenceDistance())) || + (constraint_1->AsDependencePoint() && + (constraint_0->AsDependenceLine() || + constraint_0->AsDependenceDistance()))) { + auto point_0 = constraint_0->AsDependencePoint() != nullptr; + + auto point = point_0 ? constraint_0->AsDependencePoint() + : constraint_1->AsDependencePoint(); + + auto line_or_distance = point_0 ? constraint_1 : constraint_0; + + auto is_distance = line_or_distance->AsDependenceDistance() != nullptr; + + auto a = is_distance ? scalar_evolution_.CreateConstant(1) + : line_or_distance->AsDependenceLine()->GetA(); + auto b = is_distance ? scalar_evolution_.CreateConstant(-1) + : line_or_distance->AsDependenceLine()->GetB(); + auto c = + is_distance + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + line_or_distance->AsDependenceDistance()->GetDistance())) + : line_or_distance->AsDependenceLine()->GetC(); + + auto x = point->GetSource(); + auto y = point->GetDestination(); + + if (a->AsSEConstantNode() && b->AsSEConstantNode() && + c->AsSEConstantNode() && x->AsSEConstantNode() && + y->AsSEConstantNode()) { + auto constant_a = a->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b = b->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c = c->AsSEConstantNode()->FoldToSingleValue(); + + auto constant_x = x->AsSEConstantNode()->FoldToSingleValue(); + auto constant_y = y->AsSEConstantNode()->FoldToSingleValue(); + + auto left_hand_side = constant_a * constant_x + constant_b * constant_y; + + if (left_hand_side == constant_c) { + // Point is on line, return point + return point_0 ? constraint_0 : constraint_1; + } else { + // Point not on line, report independence (empty constraint). + return make_constraint(); + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + return nullptr; +} + +// Propagate constraints function as described in section 5 of Practical +// Dependence Testing, Goff, Kennedy, Tseng, 1991. +SubscriptPair LoopDependenceAnalysis::PropagateConstraints( + const SubscriptPair& subscript_pair, + const std::vector& constraints) { + SENode* new_first = subscript_pair.first; + SENode* new_second = subscript_pair.second; + + for (auto& constraint : constraints) { + // In the paper this is a[k]. We're extracting the coefficient ('a') of a + // recurrent expression with respect to the loop 'k'. + SENode* coefficient_of_recurrent = + scalar_evolution_.GetCoefficientFromRecurrentTerm( + new_first, constraint->GetLoop()); + + // In the paper this is a'[k]. + SENode* coefficient_of_recurrent_prime = + scalar_evolution_.GetCoefficientFromRecurrentTerm( + new_second, constraint->GetLoop()); + + if (constraint->GetType() == Constraint::Distance) { + DependenceDistance* as_distance = constraint->AsDependenceDistance(); + + // In the paper this is a[k]*d + SENode* rhs = scalar_evolution_.CreateMultiplyNode( + coefficient_of_recurrent, as_distance->GetDistance()); + + // In the paper this is a[k] <- 0 + SENode* zeroed_coefficient = + scalar_evolution_.BuildGraphWithoutRecurrentTerm( + new_first, constraint->GetLoop()); + + // In the paper this is e <- e - a[k]*d. + new_first = scalar_evolution_.CreateSubtraction(zeroed_coefficient, rhs); + new_first = scalar_evolution_.SimplifyExpression(new_first); + + // In the paper this is a'[k] - a[k]. + SENode* new_child = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(coefficient_of_recurrent_prime, + coefficient_of_recurrent)); + + // In the paper this is a'[k]'i[k]. + SERecurrentNode* prime_recurrent = + scalar_evolution_.GetRecurrentTerm(new_second, constraint->GetLoop()); + + if (!prime_recurrent) continue; + + // As we hash the nodes we need to create a new node when we update a + // child. + SENode* new_recurrent = scalar_evolution_.CreateRecurrentExpression( + constraint->GetLoop(), prime_recurrent->GetOffset(), new_child); + // In the paper this is a'[k] <- a'[k] - a[k]. + new_second = scalar_evolution_.UpdateChildNode( + new_second, prime_recurrent, new_recurrent); + } + } + + new_second = scalar_evolution_.SimplifyExpression(new_second); + return std::make_pair(new_first, new_second); +} + +bool LoopDependenceAnalysis::DeltaTest( + const std::vector& coupled_subscripts, + DistanceVector* dv_entry) { + std::vector constraints(loops_.size()); + + std::vector loop_appeared(loops_.size()); + + std::generate(std::begin(constraints), std::end(constraints), + [this]() { return make_constraint(); }); + + // Separate SIV and MIV subscripts + std::vector siv_subscripts{}; + std::vector miv_subscripts{}; + + for (const auto& subscript_pair : coupled_subscripts) { + if (IsSIV(subscript_pair)) { + siv_subscripts.push_back(subscript_pair); + } else { + miv_subscripts.push_back(subscript_pair); + } + } + + // Delta Test + while (!siv_subscripts.empty()) { + std::vector results(siv_subscripts.size()); + + std::vector current_distances( + siv_subscripts.size(), DistanceVector(loops_.size())); + + // Apply SIV test to all SIV subscripts, report independence if any of them + // is independent + std::transform( + std::begin(siv_subscripts), std::end(siv_subscripts), + std::begin(current_distances), std::begin(results), + [this](SubscriptPair& p, DistanceVector& d) { return SIVTest(p, &d); }); + + if (std::accumulate(std::begin(results), std::end(results), false, + std::logical_or{})) { + return true; + } + + // Derive new constraint vector. + std::vector> all_new_constrants{}; + + for (size_t i = 0; i < siv_subscripts.size(); ++i) { + auto loop = GetLoopForSubscriptPair(siv_subscripts[i]); + + auto loop_id = + std::distance(std::begin(loops_), + std::find(std::begin(loops_), std::end(loops_), loop)); + + loop_appeared[loop_id] = true; + auto distance_entry = current_distances[i].GetEntries()[loop_id]; + + if (distance_entry.dependence_information == + DistanceEntry::DependenceInformation::DISTANCE) { + // Construct a DependenceDistance. + auto node = scalar_evolution_.CreateConstant(distance_entry.distance); + + all_new_constrants.push_back( + {make_constraint(node, loop), loop_id}); + } else { + // Construct a DependenceLine. + const auto& subscript_pair = siv_subscripts[i]; + SENode* source_node = std::get<0>(subscript_pair); + SENode* destination_node = std::get<1>(subscript_pair); + + int64_t source_induction_count = CountInductionVariables(source_node); + int64_t destination_induction_count = + CountInductionVariables(destination_node); + + SENode* a = nullptr; + SENode* b = nullptr; + SENode* c = nullptr; + + if (destination_induction_count != 0) { + a = destination_node->AsSERecurrentNode()->GetCoefficient(); + c = scalar_evolution_.CreateNegation( + destination_node->AsSERecurrentNode()->GetOffset()); + } else { + a = scalar_evolution_.CreateConstant(0); + c = scalar_evolution_.CreateNegation(destination_node); + } + + if (source_induction_count != 0) { + b = scalar_evolution_.CreateNegation( + source_node->AsSERecurrentNode()->GetCoefficient()); + c = scalar_evolution_.CreateAddNode( + c, source_node->AsSERecurrentNode()->GetOffset()); + } else { + b = scalar_evolution_.CreateConstant(0); + c = scalar_evolution_.CreateAddNode(c, source_node); + } + + a = scalar_evolution_.SimplifyExpression(a); + b = scalar_evolution_.SimplifyExpression(b); + c = scalar_evolution_.SimplifyExpression(c); + + all_new_constrants.push_back( + {make_constraint(a, b, c, loop), loop_id}); + } + } + + // Calculate the intersection between the new and existing constraints. + std::vector intersection = constraints; + for (const auto& constraint_to_intersect : all_new_constrants) { + auto loop_id = std::get<1>(constraint_to_intersect); + auto loop = loops_[loop_id]; + intersection[loop_id] = IntersectConstraints( + intersection[loop_id], std::get<0>(constraint_to_intersect), + GetLowerBound(loop), GetUpperBound(loop)); + } + + // Report independence if an empty constraint (DependenceEmpty) is found. + auto first_empty = + std::find_if(std::begin(intersection), std::end(intersection), + [](Constraint* constraint) { + return constraint->AsDependenceEmpty() != nullptr; + }); + if (first_empty != std::end(intersection)) { + return true; + } + std::vector new_siv_subscripts{}; + std::vector new_miv_subscripts{}; + + auto equal = + std::equal(std::begin(constraints), std::end(constraints), + std::begin(intersection), + [](Constraint* a, Constraint* b) { return *a == *b; }); + + // If any constraints have changed, propagate them into the rest of the + // subscripts possibly creating new ZIV/SIV subscripts. + if (!equal) { + std::vector new_subscripts(miv_subscripts.size()); + + // Propagate constraints into MIV subscripts + std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), + std::begin(new_subscripts), + [this, &intersection](SubscriptPair& subscript_pair) { + return PropagateConstraints(subscript_pair, + intersection); + }); + + // If a ZIV subscript is returned, apply test, otherwise, update untested + // subscripts. + for (auto& subscript : new_subscripts) { + if (IsZIV(subscript) && ZIVTest(subscript)) { + return true; + } else if (IsSIV(subscript)) { + new_siv_subscripts.push_back(subscript); + } else { + new_miv_subscripts.push_back(subscript); + } + } + } + + // Set new constraints and subscripts to test. + std::swap(siv_subscripts, new_siv_subscripts); + std::swap(miv_subscripts, new_miv_subscripts); + std::swap(constraints, intersection); + } + + // Create the dependence vector from the constraints. + for (size_t i = 0; i < loops_.size(); ++i) { + // Don't touch entries for loops that weren't tested. + if (loop_appeared[i]) { + auto current_constraint = constraints[i]; + auto& current_distance_entry = (*dv_entry).GetEntries()[i]; + + if (auto dependence_distance = + current_constraint->AsDependenceDistance()) { + if (auto constant_node = + dependence_distance->GetDistance()->AsSEConstantNode()) { + current_distance_entry.dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + + current_distance_entry.distance = constant_node->FoldToSingleValue(); + if (current_distance_entry.distance == 0) { + current_distance_entry.direction = DistanceEntry::Directions::EQ; + } else if (current_distance_entry.distance < 0) { + current_distance_entry.direction = DistanceEntry::Directions::GT; + } else { + current_distance_entry.direction = DistanceEntry::Directions::LT; + } + } + } else if (auto dependence_point = + current_constraint->AsDependencePoint()) { + auto source = dependence_point->GetSource(); + auto destination = dependence_point->GetDestination(); + + if (source->AsSEConstantNode() && destination->AsSEConstantNode()) { + current_distance_entry = DistanceEntry( + source->AsSEConstantNode()->FoldToSingleValue(), + destination->AsSEConstantNode()->FoldToSingleValue()); + } + } + } + } + + // Test any remaining MIV subscripts and report independence if found. + std::vector results(miv_subscripts.size()); + + std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), + std::begin(results), + [this](const SubscriptPair& p) { return GCDMIVTest(p); }); + + return std::accumulate(std::begin(results), std::end(results), false, + std::logical_or{}); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.h new file mode 100644 index 00000000..03a9075f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence.h @@ -0,0 +1,560 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_DEPENDENCE_H_ +#define SOURCE_OPT_LOOP_DEPENDENCE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +// Stores information about dependence between a load and a store wrt a single +// loop in a loop nest. +// DependenceInformation +// * UNKNOWN if no dependence information can be gathered or is gathered +// for it. +// * DIRECTION if a dependence direction could be found, but not a +// distance. +// * DISTANCE if a dependence distance could be found. +// * PEEL if peeling either the first or last iteration will break +// dependence between the given load and store. +// * IRRELEVANT if it has no effect on the dependence between the given +// load and store. +// +// If peel_first == true, the analysis has found that peeling the first +// iteration of this loop will break dependence. +// +// If peel_last == true, the analysis has found that peeling the last iteration +// of this loop will break dependence. +class DistanceEntry { + public: + enum DependenceInformation { + UNKNOWN = 0, + DIRECTION = 1, + DISTANCE = 2, + PEEL = 3, + IRRELEVANT = 4, + POINT = 5 + }; + enum Directions { + NONE = 0, + LT = 1, + EQ = 2, + LE = 3, + GT = 4, + NE = 5, + GE = 6, + ALL = 7 + }; + DependenceInformation dependence_information; + Directions direction; + int64_t distance; + bool peel_first; + bool peel_last; + int64_t point_x; + int64_t point_y; + + DistanceEntry() + : dependence_information(DependenceInformation::UNKNOWN), + direction(Directions::ALL), + distance(0), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + explicit DistanceEntry(Directions direction_) + : dependence_information(DependenceInformation::DIRECTION), + direction(direction_), + distance(0), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + DistanceEntry(Directions direction_, int64_t distance_) + : dependence_information(DependenceInformation::DISTANCE), + direction(direction_), + distance(distance_), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + DistanceEntry(int64_t x, int64_t y) + : dependence_information(DependenceInformation::POINT), + direction(Directions::ALL), + distance(0), + peel_first(false), + peel_last(false), + point_x(x), + point_y(y) {} + + bool operator==(const DistanceEntry& rhs) const { + return direction == rhs.direction && peel_first == rhs.peel_first && + peel_last == rhs.peel_last && distance == rhs.distance && + point_x == rhs.point_x && point_y == rhs.point_y; + } + + bool operator!=(const DistanceEntry& rhs) const { return !(*this == rhs); } +}; + +// Stores a vector of DistanceEntrys, one per loop in the analysis. +// A DistanceVector holds all of the information gathered in a dependence +// analysis wrt the loops stored in the LoopDependenceAnalysis performing the +// analysis. +class DistanceVector { + public: + explicit DistanceVector(size_t size) : entries(size, DistanceEntry{}) {} + + explicit DistanceVector(std::vector entries_) + : entries(entries_) {} + + DistanceEntry& GetEntry(size_t index) { return entries[index]; } + const DistanceEntry& GetEntry(size_t index) const { return entries[index]; } + + std::vector& GetEntries() { return entries; } + const std::vector& GetEntries() const { return entries; } + + bool operator==(const DistanceVector& rhs) const { + if (entries.size() != rhs.entries.size()) { + return false; + } + for (size_t i = 0; i < entries.size(); ++i) { + if (entries[i] != rhs.entries[i]) { + return false; + } + } + return true; + } + bool operator!=(const DistanceVector& rhs) const { return !(*this == rhs); } + + private: + std::vector entries; +}; + +class DependenceLine; +class DependenceDistance; +class DependencePoint; +class DependenceNone; +class DependenceEmpty; + +class Constraint { + public: + explicit Constraint(const Loop* loop) : loop_(loop) {} + enum ConstraintType { Line, Distance, Point, None, Empty }; + + virtual ConstraintType GetType() const = 0; + + virtual ~Constraint() {} + + // Get the loop this constraint belongs to. + const Loop* GetLoop() const { return loop_; } + + bool operator==(const Constraint& other) const; + + bool operator!=(const Constraint& other) const; + +// clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(DependenceLine) + DeclareCastMethod(DependenceDistance) + DeclareCastMethod(DependencePoint) + DeclareCastMethod(DependenceNone) + DeclareCastMethod(DependenceEmpty) +#undef DeclareCastMethod + + protected: + const Loop* loop_; +}; +// clang-format on + +class DependenceLine : public Constraint { + public: + DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop) + : Constraint(loop), a_(a), b_(b), c_(c) {} + + ConstraintType GetType() const final { return Line; } + + DependenceLine* AsDependenceLine() final { return this; } + const DependenceLine* AsDependenceLine() const final { return this; } + + SENode* GetA() const { return a_; } + SENode* GetB() const { return b_; } + SENode* GetC() const { return c_; } + + private: + SENode* a_; + SENode* b_; + SENode* c_; +}; + +class DependenceDistance : public Constraint { + public: + DependenceDistance(SENode* distance, const Loop* loop) + : Constraint(loop), distance_(distance) {} + + ConstraintType GetType() const final { return Distance; } + + DependenceDistance* AsDependenceDistance() final { return this; } + const DependenceDistance* AsDependenceDistance() const final { return this; } + + SENode* GetDistance() const { return distance_; } + + private: + SENode* distance_; +}; + +class DependencePoint : public Constraint { + public: + DependencePoint(SENode* source, SENode* destination, const Loop* loop) + : Constraint(loop), source_(source), destination_(destination) {} + + ConstraintType GetType() const final { return Point; } + + DependencePoint* AsDependencePoint() final { return this; } + const DependencePoint* AsDependencePoint() const final { return this; } + + SENode* GetSource() const { return source_; } + SENode* GetDestination() const { return destination_; } + + private: + SENode* source_; + SENode* destination_; +}; + +class DependenceNone : public Constraint { + public: + DependenceNone() : Constraint(nullptr) {} + ConstraintType GetType() const final { return None; } + + DependenceNone* AsDependenceNone() final { return this; } + const DependenceNone* AsDependenceNone() const final { return this; } +}; + +class DependenceEmpty : public Constraint { + public: + DependenceEmpty() : Constraint(nullptr) {} + ConstraintType GetType() const final { return Empty; } + + DependenceEmpty* AsDependenceEmpty() final { return this; } + const DependenceEmpty* AsDependenceEmpty() const final { return this; } +}; + +// Provides dependence information between a store instruction and a load +// instruction inside the same loop in a loop nest. +// +// The analysis can only check dependence between stores and loads with regard +// to the loop nest it is created with. +// +// The analysis can output debugging information to a stream. The output +// describes the control flow of the analysis and what information it can deduce +// at each step. +// SetDebugStream and ClearDebugStream are provided for this functionality. +// +// The dependency algorithm is based on the 1990 Paper +// Practical Dependence Testing +// Gina Goff, Ken Kennedy, Chau-Wen Tseng +// +// The algorithm first identifies subscript pairs between the load and store. +// Each pair is tested until all have been tested or independence is found. +// The number of induction variables in a pair determines which test to perform +// on it; +// Zero Index Variable (ZIV) is used when no induction variables are present +// in the pair. +// Single Index Variable (SIV) is used when only one induction variable is +// present, but may occur multiple times in the pair. +// Multiple Index Variable (MIV) is used when more than one induction variable +// is present in the pair. +class LoopDependenceAnalysis { + public: + LoopDependenceAnalysis(IRContext* context, std::vector loops) + : context_(context), + loops_(loops), + scalar_evolution_(context), + debug_stream_(nullptr), + constraints_{} {} + + // Finds the dependence between |source| and |destination|. + // |source| should be an OpLoad. + // |destination| should be an OpStore. + // Any direction and distance information found will be stored in + // |distance_vector|. + // Returns true if independence is found, false otherwise. + bool GetDependence(const Instruction* source, const Instruction* destination, + DistanceVector* distance_vector); + + // Returns true if |subscript_pair| represents a Zero Index Variable pair + // (ZIV) + bool IsZIV(const std::pair& subscript_pair); + + // Returns true if |subscript_pair| represents a Single Index Variable + // (SIV) pair + bool IsSIV(const std::pair& subscript_pair); + + // Returns true if |subscript_pair| represents a Multiple Index Variable + // (MIV) pair + bool IsMIV(const std::pair& subscript_pair); + + // Finds the lower bound of |loop| as an SENode* and returns the result. + // The lower bound is the starting value of the loops induction variable + SENode* GetLowerBound(const Loop* loop); + + // Finds the upper bound of |loop| as an SENode* and returns the result. + // The upper bound is the last value before the loop exit condition is met. + SENode* GetUpperBound(const Loop* loop); + + // Returns true if |value| is between |bound_one| and |bound_two| (inclusive). + bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two); + + // Finds the bounds of |loop| as upper_bound - lower_bound and returns the + // resulting SENode. + // If the operations can not be completed a nullptr is returned. + SENode* GetTripCount(const Loop* loop); + + // Returns the SENode* produced by building an SENode from the result of + // calling GetInductionInitValue on |loop|. + // If the operation can not be completed a nullptr is returned. + SENode* GetFirstTripInductionNode(const Loop* loop); + + // Returns the SENode* produced by building an SENode from the result of + // GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient. + // If the operation can not be completed a nullptr is returned. + SENode* GetFinalTripInductionNode(const Loop* loop, + SENode* induction_coefficient); + + // Returns all the distinct loops that appear in |nodes|. + std::set CollectLoops( + const std::vector& nodes); + + // Returns all the distinct loops that appear in |source| and |destination|. + std::set CollectLoops(SENode* source, SENode* destination); + + // Returns true if |distance| is provably outside the loop bounds. + // |coefficient| must be an SENode representing the coefficient of the + // induction variable of |loop|. + // This method is able to handle some symbolic cases which IsWithinBounds + // can't handle. + bool IsProvablyOutsideOfLoopBounds(const Loop* loop, SENode* distance, + SENode* coefficient); + + // Sets the ostream for debug information for the analysis. + void SetDebugStream(std::ostream& debug_stream) { + debug_stream_ = &debug_stream; + } + + // Clears the stored ostream to stop debug information printing. + void ClearDebugStream() { debug_stream_ = nullptr; } + + // Returns the ScalarEvolutionAnalysis used by this analysis. + ScalarEvolutionAnalysis* GetScalarEvolution() { return &scalar_evolution_; } + + // Creates a new constraint of type |T| and returns the pointer to it. + template + Constraint* make_constraint(Args&&... args) { + constraints_.push_back( + std::unique_ptr(new T(std::forward(args)...))); + + return constraints_.back().get(); + } + + // Subscript partitioning as described in Figure 1 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + // Partitions the subscripts into independent subscripts and minimally coupled + // sets of subscripts. + // Returns the partitioning of subscript pairs. Sets of size 1 indicates an + // independent subscript-pair and others indicate coupled sets. + using PartitionedSubscripts = + std::vector>>; + PartitionedSubscripts PartitionSubscripts( + const std::vector& source_subscripts, + const std::vector& destination_subscripts); + + // Returns the Loop* matching the loop for |subscript_pair|. + // |subscript_pair| must be an SIV pair. + const Loop* GetLoopForSubscriptPair( + const std::pair& subscript_pair); + + // Returns the DistanceEntry matching the loop for |subscript_pair|. + // |subscript_pair| must be an SIV pair. + DistanceEntry* GetDistanceEntryForSubscriptPair( + const std::pair& subscript_pair, + DistanceVector* distance_vector); + + // Returns the DistanceEntry matching |loop|. + DistanceEntry* GetDistanceEntryForLoop(const Loop* loop, + DistanceVector* distance_vector); + + // Returns a vector of Instruction* which form the subscripts of the array + // access defined by the access chain |instruction|. + std::vector GetSubscripts(const Instruction* instruction); + + // Delta test as described in Figure 3 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + bool DeltaTest( + const std::vector>& coupled_subscripts, + DistanceVector* dv_entry); + + // Constraint propagation as described in Figure 5 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + std::pair PropagateConstraints( + const std::pair& subscript_pair, + const std::vector& constraints); + + // Constraint intersection as described in Figure 4 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + Constraint* IntersectConstraints(Constraint* constraint_0, + Constraint* constraint_1, + const SENode* lower_bound, + const SENode* upper_bound); + + // Returns true if each loop in |loops| is in a form supported by this + // analysis. + // A loop is supported if it has a single induction variable and that + // induction variable has a step of +1 or -1 per loop iteration. + bool CheckSupportedLoops(std::vector loops); + + // Returns true if |loop| is in a form supported by this analysis. + // A loop is supported if it has a single induction variable and that + // induction variable has a step of +1 or -1 per loop iteration. + bool IsSupportedLoop(const Loop* loop); + + private: + IRContext* context_; + + // The loop nest we are analysing the dependence of. + std::vector loops_; + + // The ScalarEvolutionAnalysis used by this analysis to store and perform much + // of its logic. + ScalarEvolutionAnalysis scalar_evolution_; + + // The ostream debug information for the analysis to print to. + std::ostream* debug_stream_; + + // Stores all the constraints created by the analysis. + std::list> constraints_; + + // Returns true if independence can be proven and false if it can't be proven. + bool ZIVTest(const std::pair& subscript_pair); + + // Analyzes the subscript pair to find an applicable SIV test. + // Returns true if independence can be proven and false if it can't be proven. + bool SIVTest(const std::pair& subscript_pair, + DistanceVector* distance_vector); + + // Takes the form a*i + c1, a*i + c2 + // When c1 and c2 are loop invariant and a is constant + // distance = (c1 - c2)/a + // < if distance > 0 + // direction = = if distance = 0 + // > if distance < 0 + // Returns true if independence is proven and false if it can't be proven. + bool StrongSIVTest(SENode* source, SENode* destination, SENode* coeff, + DistanceEntry* distance_entry); + + // Takes for form a*i + c1, a*i + c2 + // where c1 and c2 are loop invariant and a is constant. + // c1 and/or c2 contain one or more SEValueUnknown nodes. + bool SymbolicStrongSIVTest(SENode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a1 = 0 + // distance = (c1 - c2) / a2 + // Returns true if independence is proven and false if it can't be proven. + bool WeakZeroSourceSIVTest(SENode* source, SERecurrentNode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a2 = 0 + // distance = (c2 - c1) / a1 + // Returns true if independence is proven and false if it can't be proven. + bool WeakZeroDestinationSIVTest(SERecurrentNode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a1 = -a2 + // distance = (c2 - c1) / 2*a1 + // Returns true if independence is proven and false if it can't be proven. + bool WeakCrossingSIVTest(SENode* source, SENode* destination, + SENode* coefficient, DistanceEntry* distance_entry); + + // Uses the def_use_mgr to get the instruction referenced by + // SingleWordInOperand(|id|) when called on |instruction|. + Instruction* GetOperandDefinition(const Instruction* instruction, int id); + + // Perform the GCD test if both, the source and the destination nodes, are in + // the form a0*i0 + a1*i1 + ... an*in + c. + bool GCDMIVTest(const std::pair& subscript_pair); + + // Finds the number of induction variables in |node|. + // Returns -1 on failure. + int64_t CountInductionVariables(SENode* node); + + // Finds the number of induction variables shared between |source| and + // |destination|. + // Returns -1 on failure. + int64_t CountInductionVariables(SENode* source, SENode* destination); + + // Takes the offset from the induction variable and subtracts the lower bound + // from it to get the constant term added to the induction. + // Returns the resuting constant term, or nullptr if it could not be produced. + SENode* GetConstantTerm(const Loop* loop, SERecurrentNode* induction); + + // Marks all the distance entries in |distance_vector| that were relate to + // loops in |loops_| but were not used in any subscripts as irrelevant to the + // to the dependence test. + void MarkUnsusedDistanceEntriesAsIrrelevant(const Instruction* source, + const Instruction* destination, + DistanceVector* distance_vector); + + // Converts |value| to a std::string and returns the result. + // This is required because Android does not compile std::to_string. + template + std::string ToString(valueT value) { + std::ostringstream string_stream; + string_stream << value; + return string_stream.str(); + } + + // Prints |debug_msg| and "\n" to the ostream pointed to by |debug_stream_|. + // Won't print anything if |debug_stream_| is nullptr. + void PrintDebug(std::string debug_msg); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_DEPENDENCE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence_helpers.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence_helpers.cpp new file mode 100644 index 00000000..5d7d9940 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_dependence_helpers.cpp @@ -0,0 +1,539 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +bool LoopDependenceAnalysis::IsZIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) == + 0; +} + +bool LoopDependenceAnalysis::IsSIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) == + 1; +} + +bool LoopDependenceAnalysis::IsMIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) > + 1; +} + +SENode* LoopDependenceAnalysis::GetLowerBound(const Loop* loop) { + Instruction* cond_inst = loop->GetConditionInst(); + if (!cond_inst) { + return nullptr; + } + Instruction* lower_inst = GetOperandDefinition(cond_inst, 0); + switch (cond_inst->opcode()) { + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: { + // If we have a phi we are looking at the induction variable. We look + // through the phi to the initial value of the phi upon entering the loop. + if (lower_inst->opcode() == spv::Op::OpPhi) { + lower_inst = GetOperandDefinition(lower_inst, 0); + // We don't handle looking through multiple phis. + if (lower_inst->opcode() == spv::Op::OpPhi) { + return nullptr; + } + } + return scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(lower_inst)); + } + default: + return nullptr; + } +} + +SENode* LoopDependenceAnalysis::GetUpperBound(const Loop* loop) { + Instruction* cond_inst = loop->GetConditionInst(); + if (!cond_inst) { + return nullptr; + } + Instruction* upper_inst = GetOperandDefinition(cond_inst, 1); + switch (cond_inst->opcode()) { + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: { + // When we have a < condition we must subtract 1 from the analyzed upper + // instruction. + SENode* upper_bound = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction( + scalar_evolution_.AnalyzeInstruction(upper_inst), + scalar_evolution_.CreateConstant(1))); + return upper_bound; + } + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: { + // When we have a > condition we must add 1 to the analyzed upper + // instruction. + SENode* upper_bound = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + scalar_evolution_.AnalyzeInstruction(upper_inst), + scalar_evolution_.CreateConstant(1))); + return upper_bound; + } + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: { + // We don't need to modify the results of analyzing when we have <= or >=. + SENode* upper_bound = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(upper_inst)); + return upper_bound; + } + default: + return nullptr; + } +} + +bool LoopDependenceAnalysis::IsWithinBounds(int64_t value, int64_t bound_one, + int64_t bound_two) { + if (bound_one < bound_two) { + // If |bound_one| is the lower bound. + return (value >= bound_one && value <= bound_two); + } else if (bound_one > bound_two) { + // If |bound_two| is the lower bound. + return (value >= bound_two && value <= bound_one); + } else { + // Both bounds have the same value. + return value == bound_one; + } +} + +bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds( + const Loop* loop, SENode* distance, SENode* coefficient) { + // We test to see if we can reduce the coefficient to an integral constant. + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (!coefficient_constant) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds could not reduce coefficient to a " + "SEConstantNode so must exit."); + return false; + } + + SENode* lower_bound = GetLowerBound(loop); + SENode* upper_bound = GetUpperBound(loop); + if (!lower_bound || !upper_bound) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds could not get both the lower and upper " + "bounds so must exit."); + return false; + } + // If the coefficient is positive we calculate bounds as upper - lower + // If the coefficient is negative we calculate bounds as lower - upper + SENode* bounds = nullptr; + if (coefficient_constant->FoldToSingleValue() >= 0) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found coefficient >= 0.\n" + "Using bounds as upper - lower."); + bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); + } else { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found coefficient < 0.\n" + "Using bounds as lower - upper."); + bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(lower_bound, upper_bound)); + } + + // We can attempt to deal with symbolic cases by subtracting |distance| and + // the bound nodes. If we can subtract, simplify and produce a SEConstantNode + // we can produce some information. + SEConstantNode* distance_minus_bounds = + scalar_evolution_ + .SimplifyExpression( + scalar_evolution_.CreateSubtraction(distance, bounds)) + ->AsSEConstantNode(); + if (distance_minus_bounds) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found distance - bounds as a " + "SEConstantNode with value " + + ToString(distance_minus_bounds->FoldToSingleValue())); + // If distance - bounds > 0 we prove the distance is outwith the loop + // bounds. + if (distance_minus_bounds->FoldToSingleValue() > 0) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found distance escaped the loop " + "bounds."); + return true; + } + } + + return false; +} + +const Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair( + const std::pair& subscript_pair) { + // Collect all the SERecurrentNodes. + std::vector source_nodes = + std::get<0>(subscript_pair)->CollectRecurrentNodes(); + std::vector destination_nodes = + std::get<1>(subscript_pair)->CollectRecurrentNodes(); + + // Collect all the loops stored by the SERecurrentNodes. + std::unordered_set loops{}; + for (auto source_nodes_it = source_nodes.begin(); + source_nodes_it != source_nodes.end(); ++source_nodes_it) { + loops.insert((*source_nodes_it)->GetLoop()); + } + for (auto destination_nodes_it = destination_nodes.begin(); + destination_nodes_it != destination_nodes.end(); + ++destination_nodes_it) { + loops.insert((*destination_nodes_it)->GetLoop()); + } + + // If we didn't find 1 loop |subscript_pair| is a subscript over multiple or 0 + // loops. We don't handle this so return nullptr. + if (loops.size() != 1) { + PrintDebug("GetLoopForSubscriptPair found loops.size() != 1."); + return nullptr; + } + return *loops.begin(); +} + +DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop( + const Loop* loop, DistanceVector* distance_vector) { + if (!loop) { + return nullptr; + } + + DistanceEntry* distance_entry = nullptr; + for (size_t loop_index = 0; loop_index < loops_.size(); ++loop_index) { + if (loop == loops_[loop_index]) { + distance_entry = &(distance_vector->GetEntries()[loop_index]); + break; + } + } + + return distance_entry; +} + +DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForSubscriptPair( + const std::pair& subscript_pair, + DistanceVector* distance_vector) { + const Loop* loop = GetLoopForSubscriptPair(subscript_pair); + + return GetDistanceEntryForLoop(loop, distance_vector); +} + +SENode* LoopDependenceAnalysis::GetTripCount(const Loop* loop) { + BasicBlock* condition_block = loop->FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* induction_instr = loop->FindConditionVariable(condition_block); + if (!induction_instr) { + return nullptr; + } + Instruction* cond_instr = loop->GetConditionInst(); + if (!cond_instr) { + return nullptr; + } + + size_t iteration_count = 0; + + // We have to check the instruction type here. If the condition instruction + // isn't a supported type we can't calculate the trip count. + if (loop->IsSupportedCondition(cond_instr->opcode())) { + if (loop->FindNumberOfIterations(induction_instr, &*condition_block->tail(), + &iteration_count)) { + return scalar_evolution_.CreateConstant( + static_cast(iteration_count)); + } + } + + return nullptr; +} + +SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(const Loop* loop) { + BasicBlock* condition_block = loop->FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* induction_instr = loop->FindConditionVariable(condition_block); + if (!induction_instr) { + return nullptr; + } + int64_t induction_initial_value = 0; + if (!loop->GetInductionInitValue(induction_instr, &induction_initial_value)) { + return nullptr; + } + + SENode* induction_init_SENode = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateConstant(induction_initial_value)); + return induction_init_SENode; +} + +SENode* LoopDependenceAnalysis::GetFinalTripInductionNode( + const Loop* loop, SENode* induction_coefficient) { + SENode* first_trip_induction_node = GetFirstTripInductionNode(loop); + if (!first_trip_induction_node) { + return nullptr; + } + // Get trip_count as GetTripCount - 1 + // This is because the induction variable is not stepped on the first + // iteration of the loop + SENode* trip_count = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + GetTripCount(loop), scalar_evolution_.CreateConstant(1))); + // Return first_trip_induction_node + trip_count * induction_coefficient + return scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + first_trip_induction_node, + scalar_evolution_.CreateMultiplyNode(trip_count, induction_coefficient))); +} + +std::set LoopDependenceAnalysis::CollectLoops( + const std::vector& recurrent_nodes) { + // We don't handle loops with more than one induction variable. Therefore we + // can identify the number of induction variables by collecting all of the + // loops the collected recurrent nodes belong to. + std::set loops{}; + for (auto recurrent_nodes_it = recurrent_nodes.begin(); + recurrent_nodes_it != recurrent_nodes.end(); ++recurrent_nodes_it) { + loops.insert((*recurrent_nodes_it)->GetLoop()); + } + + return loops; +} + +int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* node) { + if (!node) { + return -1; + } + + std::vector recurrent_nodes = node->CollectRecurrentNodes(); + + // We don't handle loops with more than one induction variable. Therefore we + // can identify the number of induction variables by collecting all of the + // loops the collected recurrent nodes belong to. + std::set loops = CollectLoops(recurrent_nodes); + + return static_cast(loops.size()); +} + +std::set LoopDependenceAnalysis::CollectLoops( + SENode* source, SENode* destination) { + if (!source || !destination) { + return std::set{}; + } + + std::vector source_nodes = source->CollectRecurrentNodes(); + std::vector destination_nodes = + destination->CollectRecurrentNodes(); + + std::set loops = CollectLoops(source_nodes); + std::set destination_loops = CollectLoops(destination_nodes); + + loops.insert(std::begin(destination_loops), std::end(destination_loops)); + + return loops; +} + +int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* source, + SENode* destination) { + if (!source || !destination) { + return -1; + } + + std::set loops = CollectLoops(source, destination); + + return static_cast(loops.size()); +} + +Instruction* LoopDependenceAnalysis::GetOperandDefinition( + const Instruction* instruction, int id) { + return context_->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(id)); +} + +std::vector LoopDependenceAnalysis::GetSubscripts( + const Instruction* instruction) { + Instruction* access_chain = GetOperandDefinition(instruction, 0); + + std::vector subscripts; + + for (auto i = 1u; i < access_chain->NumInOperandWords(); ++i) { + subscripts.push_back(GetOperandDefinition(access_chain, i)); + } + + return subscripts; +} + +SENode* LoopDependenceAnalysis::GetConstantTerm(const Loop* loop, + SERecurrentNode* induction) { + SENode* offset = induction->GetOffset(); + SENode* lower_bound = GetLowerBound(loop); + if (!offset || !lower_bound) { + return nullptr; + } + SENode* constant_term = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(offset, lower_bound)); + return constant_term; +} + +bool LoopDependenceAnalysis::CheckSupportedLoops( + std::vector loops) { + for (auto loop : loops) { + if (!IsSupportedLoop(loop)) { + return false; + } + } + return true; +} + +void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant( + const Instruction* source, const Instruction* destination, + DistanceVector* distance_vector) { + std::vector source_subscripts = GetSubscripts(source); + std::vector destination_subscripts = GetSubscripts(destination); + + std::set used_loops{}; + + for (Instruction* source_inst : source_subscripts) { + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_inst)); + std::vector recurrent_nodes = + source_node->CollectRecurrentNodes(); + for (SERecurrentNode* recurrent_node : recurrent_nodes) { + used_loops.insert(recurrent_node->GetLoop()); + } + } + + for (Instruction* destination_inst : destination_subscripts) { + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_inst)); + std::vector recurrent_nodes = + destination_node->CollectRecurrentNodes(); + for (SERecurrentNode* recurrent_node : recurrent_nodes) { + used_loops.insert(recurrent_node->GetLoop()); + } + } + + for (size_t i = 0; i < loops_.size(); ++i) { + if (used_loops.find(loops_[i]) == used_loops.end()) { + distance_vector->GetEntries()[i].dependence_information = + DistanceEntry::DependenceInformation::IRRELEVANT; + } + } +} + +bool LoopDependenceAnalysis::IsSupportedLoop(const Loop* loop) { + std::vector inductions{}; + loop->GetInductionVariables(inductions); + if (inductions.size() != 1) { + return false; + } + Instruction* induction = inductions[0]; + SENode* induction_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(induction)); + if (!induction_node->AsSERecurrentNode()) { + return false; + } + SENode* induction_step = + induction_node->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step->AsSEConstantNode()) { + return false; + } + if (!(induction_step->AsSEConstantNode()->FoldToSingleValue() == 1 || + induction_step->AsSEConstantNode()->FoldToSingleValue() == -1)) { + return false; + } + return true; +} + +void LoopDependenceAnalysis::PrintDebug(std::string debug_msg) { + if (debug_stream_) { + (*debug_stream_) << debug_msg << "\n"; + } +} + +bool Constraint::operator==(const Constraint& other) const { + // A distance of |d| is equivalent to a line |x - y = -d| + if ((GetType() == ConstraintType::Distance && + other.GetType() == ConstraintType::Line) || + (GetType() == ConstraintType::Line && + other.GetType() == ConstraintType::Distance)) { + auto is_distance = AsDependenceLine() != nullptr; + + auto as_distance = + is_distance ? AsDependenceDistance() : other.AsDependenceDistance(); + auto distance = as_distance->GetDistance(); + + auto line = other.AsDependenceLine(); + + auto scalar_evolution = distance->GetParentAnalysis(); + + auto neg_distance = scalar_evolution->SimplifyExpression( + scalar_evolution->CreateNegation(distance)); + + return *scalar_evolution->CreateConstant(1) == *line->GetA() && + *scalar_evolution->CreateConstant(-1) == *line->GetB() && + *neg_distance == *line->GetC(); + } + + if (GetType() != other.GetType()) { + return false; + } + + if (AsDependenceDistance()) { + return *AsDependenceDistance()->GetDistance() == + *other.AsDependenceDistance()->GetDistance(); + } + + if (AsDependenceLine()) { + auto this_line = AsDependenceLine(); + auto other_line = other.AsDependenceLine(); + return *this_line->GetA() == *other_line->GetA() && + *this_line->GetB() == *other_line->GetB() && + *this_line->GetC() == *other_line->GetC(); + } + + if (AsDependencePoint()) { + auto this_point = AsDependencePoint(); + auto other_point = other.AsDependencePoint(); + + return *this_point->GetSource() == *other_point->GetSource() && + *this_point->GetDestination() == *other_point->GetDestination(); + } + + return true; +} + +bool Constraint::operator!=(const Constraint& other) const { + return !(*this == other); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.cpp new file mode 100644 index 00000000..cbfc2e75 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.cpp @@ -0,0 +1,1024 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_descriptor.h" + +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/constants.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "source/opt/tree_iterator.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +// Takes in a phi instruction |induction| and the loop |header| and returns the +// step operation of the loop. +Instruction* Loop::GetInductionStepOperation( + const Instruction* induction) const { + // Induction must be a phi instruction. + assert(induction->opcode() == spv::Op::OpPhi); + + Instruction* step = nullptr; + + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Traverse the incoming operands of the phi instruction. + for (uint32_t operand_id = 1; operand_id < induction->NumInOperands(); + operand_id += 2) { + // Incoming edge. + BasicBlock* incoming_block = + context_->cfg()->block(induction->GetSingleWordInOperand(operand_id)); + + // Check if the block is dominated by header, and thus coming from within + // the loop. + if (IsInsideLoop(incoming_block)) { + step = def_use_manager->GetDef( + induction->GetSingleWordInOperand(operand_id - 1)); + break; + } + } + + if (!step || !IsSupportedStepOp(step->opcode())) { + return nullptr; + } + + // The induction variable which binds the loop must only be modified once. + uint32_t lhs = step->GetSingleWordInOperand(0); + uint32_t rhs = step->GetSingleWordInOperand(1); + + // One of the left hand side or right hand side of the step instruction must + // be the induction phi and the other must be an OpConstant. + if (lhs != induction->result_id() && rhs != induction->result_id()) { + return nullptr; + } + + if (def_use_manager->GetDef(lhs)->opcode() != spv::Op::OpConstant && + def_use_manager->GetDef(rhs)->opcode() != spv::Op::OpConstant) { + return nullptr; + } + + return step; +} + +// Returns true if the |step| operation is an induction variable step operation +// which is currently handled. +bool Loop::IsSupportedStepOp(spv::Op step) const { + switch (step) { + case spv::Op::OpISub: + case spv::Op::OpIAdd: + return true; + default: + return false; + } +} + +bool Loop::IsSupportedCondition(spv::Op condition) const { + switch (condition) { + // < + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + // > + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + + // >= + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpUGreaterThanEqual: + // <= + case spv::Op::OpSLessThanEqual: + case spv::Op::OpULessThanEqual: + + return true; + default: + return false; + } +} + +int64_t Loop::GetResidualConditionValue(spv::Op condition, + int64_t initial_value, + int64_t step_value, + size_t number_of_iterations, + size_t factor) { + int64_t remainder = + initial_value + (number_of_iterations % factor) * step_value; + + // We subtract or add one as the above formula calculates the remainder if the + // loop where just less than or greater than. Adding or subtracting one should + // give a functionally equivalent value. + switch (condition) { + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpUGreaterThanEqual: { + remainder -= 1; + break; + } + case spv::Op::OpSLessThanEqual: + case spv::Op::OpULessThanEqual: { + remainder += 1; + break; + } + + default: + break; + } + return remainder; +} + +Instruction* Loop::GetConditionInst() const { + BasicBlock* condition_block = FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* branch_conditional = &*condition_block->tail(); + if (!branch_conditional || + branch_conditional->opcode() != spv::Op::OpBranchConditional) { + return nullptr; + } + Instruction* condition_inst = context_->get_def_use_mgr()->GetDef( + branch_conditional->GetSingleWordInOperand(0)); + if (IsSupportedCondition(condition_inst->opcode())) { + return condition_inst; + } + + return nullptr; +} + +// Extract the initial value from the |induction| OpPhi instruction and store it +// in |value|. If the function couldn't find the initial value of |induction| +// return false. +bool Loop::GetInductionInitValue(const Instruction* induction, + int64_t* value) const { + Instruction* constant_instruction = nullptr; + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + for (uint32_t operand_id = 0; operand_id < induction->NumInOperands(); + operand_id += 2) { + BasicBlock* bb = context_->cfg()->block( + induction->GetSingleWordInOperand(operand_id + 1)); + + if (!IsInsideLoop(bb)) { + constant_instruction = def_use_manager->GetDef( + induction->GetSingleWordInOperand(operand_id)); + } + } + + if (!constant_instruction) return false; + + const analysis::Constant* constant = + context_->get_constant_mgr()->FindDeclaredConstant( + constant_instruction->result_id()); + if (!constant) return false; + + if (value) { + const analysis::Integer* type = constant->type()->AsInteger(); + if (!type) { + return false; + } + + *value = type->IsSigned() ? constant->GetSignExtendedValue() + : constant->GetZeroExtendedValue(); + } + + return true; +} + +Loop::Loop(IRContext* context, DominatorAnalysis* dom_analysis, + BasicBlock* header, BasicBlock* continue_target, + BasicBlock* merge_target) + : context_(context), + loop_header_(header), + loop_continue_(continue_target), + loop_merge_(merge_target), + loop_preheader_(nullptr), + parent_(nullptr), + loop_is_marked_for_removal_(false) { + assert(context); + assert(dom_analysis); + loop_preheader_ = FindLoopPreheader(dom_analysis); + loop_latch_ = FindLatchBlock(); +} + +BasicBlock* Loop::FindLoopPreheader(DominatorAnalysis* dom_analysis) { + CFG* cfg = context_->cfg(); + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_); + + // The loop predecessor. + BasicBlock* loop_pred = nullptr; + + auto header_pred = cfg->preds(loop_header_->id()); + for (uint32_t p_id : header_pred) { + DominatorTreeNode* node = dom_tree.GetTreeNode(p_id); + if (node && !dom_tree.Dominates(header_node, node)) { + // The predecessor is not part of the loop, so potential loop preheader. + if (loop_pred && node->bb_ != loop_pred) { + // If we saw 2 distinct predecessors that are outside the loop, we don't + // have a loop preheader. + return nullptr; + } + loop_pred = node->bb_; + } + } + // Safe guard against invalid code, SPIR-V spec forbids loop with the entry + // node as header. + assert(loop_pred && "The header node is the entry block ?"); + + // So we have a unique basic block that can enter this loop. + // If this loop is the unique successor of this block, then it is a loop + // preheader. + bool is_preheader = true; + uint32_t loop_header_id = loop_header_->id(); + const auto* const_loop_pred = loop_pred; + const_loop_pred->ForEachSuccessorLabel( + [&is_preheader, loop_header_id](const uint32_t id) { + if (id != loop_header_id) is_preheader = false; + }); + if (is_preheader) return loop_pred; + return nullptr; +} + +bool Loop::IsInsideLoop(Instruction* inst) const { + const BasicBlock* parent_block = context_->get_instr_block(inst); + if (!parent_block) return false; + return IsInsideLoop(parent_block); +} + +bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) { + assert(bb->GetParent() && "The basic block does not belong to a function"); + DominatorAnalysis* dom_analysis = + context_->GetDominatorAnalysis(bb->GetParent()); + if (dom_analysis->IsReachable(bb) && + !dom_analysis->Dominates(GetHeaderBlock(), bb)) + return false; + + return true; +} + +BasicBlock* Loop::GetOrCreatePreHeaderBlock() { + if (loop_preheader_) return loop_preheader_; + + CFG* cfg = context_->cfg(); + loop_header_ = cfg->SplitLoopHeader(loop_header_); + return loop_preheader_; +} + +void Loop::SetContinueBlock(BasicBlock* continue_block) { + assert(IsInsideLoop(continue_block)); + loop_continue_ = continue_block; +} + +void Loop::SetLatchBlock(BasicBlock* latch) { +#ifndef NDEBUG + assert(latch->GetParent() && "The basic block does not belong to a function"); + + const auto* const_latch = latch; + const_latch->ForEachSuccessorLabel([this](uint32_t id) { + assert((!IsInsideLoop(id) || id == GetHeaderBlock()->id()) && + "A predecessor of the continue block does not belong to the loop"); + }); +#endif // NDEBUG + assert(IsInsideLoop(latch) && "The continue block is not in the loop"); + + SetLatchBlockImpl(latch); +} + +void Loop::SetMergeBlock(BasicBlock* merge) { +#ifndef NDEBUG + assert(merge->GetParent() && "The basic block does not belong to a function"); +#endif // NDEBUG + assert(!IsInsideLoop(merge) && "The merge block is in the loop"); + + SetMergeBlockImpl(merge); + if (GetHeaderBlock()->GetLoopMergeInst()) { + UpdateLoopMergeInst(); + } +} + +void Loop::SetPreHeaderBlock(BasicBlock* preheader) { + if (preheader) { + assert(!IsInsideLoop(preheader) && "The preheader block is in the loop"); + assert(preheader->tail()->opcode() == spv::Op::OpBranch && + "The preheader block does not unconditionally branch to the header " + "block"); + assert(preheader->tail()->GetSingleWordOperand(0) == + GetHeaderBlock()->id() && + "The preheader block does not unconditionally branch to the header " + "block"); + } + loop_preheader_ = preheader; +} + +BasicBlock* Loop::FindLatchBlock() { + CFG* cfg = context_->cfg(); + + DominatorAnalysis* dominator_analysis = + context_->GetDominatorAnalysis(loop_header_->GetParent()); + + // Look at the predecessors of the loop header to find a predecessor block + // which is dominated by the loop continue target. There should only be one + // block which meets this criteria and this is the latch block, as per the + // SPIR-V spec. + for (uint32_t block_id : cfg->preds(loop_header_->id())) { + if (dominator_analysis->Dominates(loop_continue_->id(), block_id)) { + return cfg->block(block_id); + } + } + + assert( + false && + "Every loop should have a latch block dominated by the continue target"); + return nullptr; +} + +void Loop::GetExitBlocks(std::unordered_set* exit_blocks) const { + CFG* cfg = context_->cfg(); + exit_blocks->clear(); + + for (uint32_t bb_id : GetBlocks()) { + const BasicBlock* bb = cfg->block(bb_id); + bb->ForEachSuccessorLabel([exit_blocks, this](uint32_t succ) { + if (!IsInsideLoop(succ)) { + exit_blocks->insert(succ); + } + }); + } +} + +void Loop::GetMergingBlocks( + std::unordered_set* merging_blocks) const { + assert(GetMergeBlock() && "This loop is not structured"); + CFG* cfg = context_->cfg(); + merging_blocks->clear(); + + std::stack to_visit; + to_visit.push(GetMergeBlock()); + while (!to_visit.empty()) { + const BasicBlock* bb = to_visit.top(); + to_visit.pop(); + merging_blocks->insert(bb->id()); + for (uint32_t pred_id : cfg->preds(bb->id())) { + if (!IsInsideLoop(pred_id) && !merging_blocks->count(pred_id)) { + to_visit.push(cfg->block(pred_id)); + } + } + } +} + +namespace { + +inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) { + for (Instruction& inst : *bb) { + if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst)) + return false; + } + + return true; +} + +} // namespace + +bool Loop::IsSafeToClone() const { + CFG& cfg = *context_->cfg(); + + for (uint32_t bb_id : GetBlocks()) { + BasicBlock* bb = cfg.block(bb_id); + assert(bb); + if (!IsBasicBlockSafeToClone(context_, bb)) return false; + } + + // Look at the merge construct. + if (GetHeaderBlock()->GetLoopMergeInst()) { + std::unordered_set blocks; + GetMergingBlocks(&blocks); + blocks.erase(GetMergeBlock()->id()); + for (uint32_t bb_id : blocks) { + BasicBlock* bb = cfg.block(bb_id); + assert(bb); + if (!IsBasicBlockSafeToClone(context_, bb)) return false; + } + } + + return true; +} + +bool Loop::IsLCSSA() const { + CFG* cfg = context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::unordered_set exit_blocks; + GetExitBlocks(&exit_blocks); + + // Declare ir_context so we can capture context_ in the below lambda + IRContext* ir_context = context_; + + for (uint32_t bb_id : GetBlocks()) { + for (Instruction& insn : *cfg->block(bb_id)) { + // All uses must be either: + // - In the loop; + // - In an exit block and in a phi instruction. + if (!def_use_mgr->WhileEachUser( + &insn, + [&exit_blocks, ir_context, this](Instruction* use) -> bool { + BasicBlock* parent = ir_context->get_instr_block(use); + assert(parent && "Invalid analysis"); + if (IsInsideLoop(parent)) return true; + if (use->opcode() != spv::Op::OpPhi) return false; + return exit_blocks.count(parent->id()); + })) + return false; + } + } + return true; +} + +bool Loop::ShouldHoistInstruction(const Instruction& inst) const { + return inst.IsOpcodeCodeMotionSafe() && AreAllOperandsOutsideLoop(inst) && + (!inst.IsLoad() || inst.IsReadOnlyLoad()); +} + +bool Loop::AreAllOperandsOutsideLoop(const Instruction& inst) const { + analysis::DefUseManager* def_use_mgr = GetContext()->get_def_use_mgr(); + + const std::function operand_outside_loop = + [this, &def_use_mgr](const uint32_t* id) { + return !this->IsInsideLoop(def_use_mgr->GetDef(*id)); + }; + + return inst.WhileEachInId(operand_outside_loop); +} + +void Loop::ComputeLoopStructuredOrder( + std::vector* ordered_loop_blocks, bool include_pre_header, + bool include_merge) const { + CFG& cfg = *context_->cfg(); + + // Reserve the memory: all blocks in the loop + extra if needed. + ordered_loop_blocks->reserve(GetBlocks().size() + include_pre_header + + include_merge); + + if (include_pre_header && GetPreHeaderBlock()) + ordered_loop_blocks->push_back(loop_preheader_); + + bool is_shader = + context_->get_feature_mgr()->HasCapability(spv::Capability::Shader); + if (!is_shader) { + cfg.ForEachBlockInReversePostOrder( + loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) { + if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb); + }); + } else { + // If this is a shader, it is possible that there are unreachable merge and + // continue blocks that must be copied to retain the structured order. + // The structured order will include these. + std::list order; + cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, + loop_merge_, &order); + for (BasicBlock* bb : order) { + if (bb == GetMergeBlock()) { + break; + } + ordered_loop_blocks->push_back(bb); + } + } + if (include_merge && GetMergeBlock()) + ordered_loop_blocks->push_back(loop_merge_); +} + +LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f) + : loops_(), placeholder_top_loop_(nullptr) { + PopulateList(context, f); +} + +LoopDescriptor::~LoopDescriptor() { ClearLoops(); } + +void LoopDescriptor::PopulateList(IRContext* context, const Function* f) { + DominatorAnalysis* dom_analysis = context->GetDominatorAnalysis(f); + + ClearLoops(); + + // Post-order traversal of the dominator tree to find all the OpLoopMerge + // instructions. + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + for (DominatorTreeNode& node : + make_range(dom_tree.post_begin(), dom_tree.post_end())) { + Instruction* merge_inst = node.bb_->GetLoopMergeInst(); + if (merge_inst) { + bool all_backedge_unreachable = true; + for (uint32_t pid : context->cfg()->preds(node.bb_->id())) { + if (dom_analysis->IsReachable(pid) && + dom_analysis->Dominates(node.bb_->id(), pid)) { + all_backedge_unreachable = false; + break; + } + } + if (all_backedge_unreachable) + continue; // ignore this one, we actually never branch back. + + // The id of the merge basic block of this loop. + uint32_t merge_bb_id = merge_inst->GetSingleWordOperand(0); + + // The id of the continue basic block of this loop. + uint32_t continue_bb_id = merge_inst->GetSingleWordOperand(1); + + // The merge target of this loop. + BasicBlock* merge_bb = context->cfg()->block(merge_bb_id); + + // The continue target of this loop. + BasicBlock* continue_bb = context->cfg()->block(continue_bb_id); + + // The basic block containing the merge instruction. + BasicBlock* header_bb = context->get_instr_block(merge_inst); + + // Add the loop to the list of all the loops in the function. + Loop* current_loop = + new Loop(context, dom_analysis, header_bb, continue_bb, merge_bb); + loops_.push_back(current_loop); + + // We have a bottom-up construction, so if this loop has nested-loops, + // they are by construction at the tail of the loop list. + for (auto itr = loops_.rbegin() + 1; itr != loops_.rend(); ++itr) { + Loop* previous_loop = *itr; + + // If the loop already has a parent, then it has been processed. + if (previous_loop->HasParent()) continue; + + // If the current loop does not dominates the previous loop then it is + // not nested loop. + if (!dom_analysis->Dominates(header_bb, + previous_loop->GetHeaderBlock())) + continue; + // If the current loop merge dominates the previous loop then it is + // not nested loop. + if (dom_analysis->Dominates(merge_bb, previous_loop->GetHeaderBlock())) + continue; + + current_loop->AddNestedLoop(previous_loop); + } + DominatorTreeNode* dom_merge_node = dom_tree.GetTreeNode(merge_bb); + for (DominatorTreeNode& loop_node : + make_range(node.df_begin(), node.df_end())) { + // Check if we are in the loop. + if (dom_tree.Dominates(dom_merge_node, &loop_node)) continue; + current_loop->AddBasicBlock(loop_node.bb_); + basic_block_to_loop_.insert( + std::make_pair(loop_node.bb_->id(), current_loop)); + } + } + } + for (Loop* loop : loops_) { + if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); + } +} + +std::vector LoopDescriptor::GetLoopsInBinaryLayoutOrder() { + std::vector ids{}; + + for (size_t i = 0; i < NumLoops(); ++i) { + ids.push_back(GetLoopByIndex(i).GetHeaderBlock()->id()); + } + + std::vector loops{}; + if (!ids.empty()) { + auto function = GetLoopByIndex(0).GetHeaderBlock()->GetParent(); + for (const auto& block : *function) { + auto block_id = block.id(); + + auto element = std::find(std::begin(ids), std::end(ids), block_id); + if (element != std::end(ids)) { + loops.push_back(&GetLoopByIndex(element - std::begin(ids))); + } + } + } + + return loops; +} + +BasicBlock* Loop::FindConditionBlock() const { + if (!loop_merge_) { + return nullptr; + } + BasicBlock* condition_block = nullptr; + + uint32_t in_loop_pred = 0; + for (uint32_t p : context_->cfg()->preds(loop_merge_->id())) { + if (IsInsideLoop(p)) { + if (in_loop_pred) { + // 2 in-loop predecessors. + return nullptr; + } + in_loop_pred = p; + } + } + if (!in_loop_pred) { + // Merge block is unreachable. + return nullptr; + } + + BasicBlock* bb = context_->cfg()->block(in_loop_pred); + + if (!bb) return nullptr; + + const Instruction& branch = *bb->ctail(); + + // Make sure the branch is a conditional branch. + if (branch.opcode() != spv::Op::OpBranchConditional) return nullptr; + + // Make sure one of the two possible branches is to the merge block. + if (branch.GetSingleWordInOperand(1) == loop_merge_->id() || + branch.GetSingleWordInOperand(2) == loop_merge_->id()) { + condition_block = bb; + } + + return condition_block; +} + +bool Loop::FindNumberOfIterations(const Instruction* induction, + const Instruction* branch_inst, + size_t* iterations_out, + int64_t* step_value_out, + int64_t* init_value_out) const { + // From the branch instruction find the branch condition. + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Condition instruction from the OpConditionalBranch. + Instruction* condition = + def_use_manager->GetDef(branch_inst->GetSingleWordOperand(0)); + + assert(IsSupportedCondition(condition->opcode())); + + // Get the constant manager from the ir context. + analysis::ConstantManager* const_manager = context_->get_constant_mgr(); + + // Find the constant value used by the condition variable. Exit out if it + // isn't a constant int. + const analysis::Constant* upper_bound = + const_manager->FindDeclaredConstant(condition->GetSingleWordOperand(3)); + if (!upper_bound) return false; + + // Must be integer because of the opcode on the condition. + const analysis::Integer* type = upper_bound->type()->AsInteger(); + + if (!type || type->width() > 64) { + return false; + } + + int64_t condition_value = type->IsSigned() + ? upper_bound->GetSignExtendedValue() + : upper_bound->GetZeroExtendedValue(); + + // Find the instruction which is stepping through the loop. + // + // GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull. + Instruction* step_inst = GetInductionStepOperation(induction); + if (!step_inst) return false; + + // Find the constant value used by the condition variable. + const analysis::Constant* step_constant = + const_manager->FindDeclaredConstant(step_inst->GetSingleWordOperand(3)); + if (!step_constant) return false; + + // Must be integer because of the opcode on the condition. + int64_t step_value = 0; + + const analysis::Integer* step_type = + step_constant->AsIntConstant()->type()->AsInteger(); + + if (step_type->IsSigned()) { + step_value = step_constant->AsIntConstant()->GetS32BitValue(); + } else { + step_value = step_constant->AsIntConstant()->GetU32BitValue(); + } + + // If this is a subtraction step we should negate the step value. + if (step_inst->opcode() == spv::Op::OpISub) { + step_value = -step_value; + } + + // Find the initial value of the loop and make sure it is a constant integer. + int64_t init_value = 0; + if (!GetInductionInitValue(induction, &init_value)) return false; + + // If iterations is non null then store the value in that. + int64_t num_itrs = GetIterations(condition->opcode(), condition_value, + init_value, step_value); + + // If the loop body will not be reached return false. + if (num_itrs <= 0) { + return false; + } + + if (iterations_out) { + assert(static_cast(num_itrs) <= std::numeric_limits::max()); + *iterations_out = static_cast(num_itrs); + } + + if (step_value_out) { + *step_value_out = step_value; + } + + if (init_value_out) { + *init_value_out = init_value; + } + + return true; +} + +// We retrieve the number of iterations using the following formula, diff / +// |step_value| where diff is calculated differently according to the +// |condition| and uses the |condition_value| and |init_value|. If diff / +// |step_value| is NOT cleanly divisible then we add one to the sum. +int64_t Loop::GetIterations(spv::Op condition, int64_t condition_value, + int64_t init_value, int64_t step_value) const { + if (step_value == 0) { + return 0; + } + + int64_t diff = 0; + + switch (condition) { + case spv::Op::OpSLessThan: + case spv::Op::OpULessThan: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value < condition_value)) return 0; + + diff = condition_value - init_value; + + // If the operation is a less then operation then the diff and step must + // have the same sign otherwise the induction will never cross the + // condition (either never true or always true). + if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { + return 0; + } + + break; + } + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThan: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value > condition_value)) return 0; + + diff = init_value - condition_value; + + // If the operation is a greater than operation then the diff and step + // must have opposite signs. Otherwise the condition will always be true + // or will never be true. + if ((diff < 0 && step_value < 0) || (diff > 0 && step_value > 0)) { + return 0; + } + + break; + } + + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpUGreaterThanEqual: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value >= condition_value)) return 0; + + // We subtract one to make it the same as spv::Op::OpGreaterThan as it is + // functionally equivalent. + diff = init_value - (condition_value - 1); + + // If the operation is a greater than operation then the diff and step + // must have opposite signs. Otherwise the condition will always be true + // or will never be true. + if ((diff > 0 && step_value > 0) || (diff < 0 && step_value < 0)) { + return 0; + } + + break; + } + + case spv::Op::OpSLessThanEqual: + case spv::Op::OpULessThanEqual: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value <= condition_value)) return 0; + + // We add one to make it the same as spv::Op::OpLessThan as it is + // functionally equivalent. + diff = (condition_value + 1) - init_value; + + // If the operation is a less than operation then the diff and step must + // have the same sign otherwise the induction will never cross the + // condition (either never true or always true). + if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { + return 0; + } + + break; + } + + default: + assert(false && + "Could not retrieve number of iterations from the loop condition. " + "Condition is not supported."); + } + + // Take the abs of - step values. + step_value = llabs(step_value); + diff = llabs(diff); + int64_t result = diff / step_value; + + if (diff % step_value != 0) { + result += 1; + } + return result; +} + +// Returns the list of induction variables within the loop. +void Loop::GetInductionVariables( + std::vector& induction_variables) const { + for (Instruction& inst : *loop_header_) { + if (inst.opcode() == spv::Op::OpPhi) { + induction_variables.push_back(&inst); + } + } +} + +Instruction* Loop::FindConditionVariable( + const BasicBlock* condition_block) const { + // Find the branch instruction. + const Instruction& branch_inst = *condition_block->ctail(); + + Instruction* induction = nullptr; + // Verify that the branch instruction is a conditional branch. + if (branch_inst.opcode() == spv::Op::OpBranchConditional) { + // From the branch instruction find the branch condition. + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Find the instruction representing the condition used in the conditional + // branch. + Instruction* condition = + def_use_manager->GetDef(branch_inst.GetSingleWordOperand(0)); + + // Ensure that the condition is a less than operation. + if (condition && IsSupportedCondition(condition->opcode())) { + // The left hand side operand of the operation. + Instruction* variable_inst = + def_use_manager->GetDef(condition->GetSingleWordOperand(2)); + + // Make sure the variable instruction used is a phi. + if (!variable_inst || variable_inst->opcode() != spv::Op::OpPhi) + return nullptr; + + // Make sure the phi instruction only has two incoming blocks. Each + // incoming block will be represented by two in operands in the phi + // instruction, the value and the block which that value came from. We + // assume the cannocalised phi will have two incoming values, one from the + // preheader and one from the continue block. + size_t max_supported_operands = 4; + if (variable_inst->NumInOperands() == max_supported_operands) { + // The operand index of the first incoming block label. + uint32_t operand_label_1 = 1; + + // The operand index of the second incoming block label. + uint32_t operand_label_2 = 3; + + // Make sure one of them is the preheader. + if (!IsInsideLoop( + variable_inst->GetSingleWordInOperand(operand_label_1)) && + !IsInsideLoop( + variable_inst->GetSingleWordInOperand(operand_label_2))) { + return nullptr; + } + + // And make sure that the other is the latch block. + if (variable_inst->GetSingleWordInOperand(operand_label_1) != + loop_latch_->id() && + variable_inst->GetSingleWordInOperand(operand_label_2) != + loop_latch_->id()) { + return nullptr; + } + } else { + return nullptr; + } + + if (!FindNumberOfIterations(variable_inst, &branch_inst, nullptr)) + return nullptr; + induction = variable_inst; + } + } + + return induction; +} + +bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() { + auto modified = false; + + for (auto& loop : *this) { + if (!loop.GetPreHeaderBlock()) { + modified = true; + // TODO(1841): Handle failure to create pre-header. + loop.GetOrCreatePreHeaderBlock(); + } + } + + return modified; +} + +// Add and remove loops which have been marked for addition and removal to +// maintain the state of the loop descriptor class. +void LoopDescriptor::PostModificationCleanup() { + LoopContainerType loops_to_remove_; + for (Loop* loop : loops_) { + if (loop->IsMarkedForRemoval()) { + loops_to_remove_.push_back(loop); + if (loop->HasParent()) { + loop->GetParent()->RemoveChildLoop(loop); + } + } + } + + for (Loop* loop : loops_to_remove_) { + loops_.erase(std::find(loops_.begin(), loops_.end(), loop)); + delete loop; + } + + for (auto& pair : loops_to_add_) { + Loop* parent = pair.first; + std::unique_ptr loop = std::move(pair.second); + + if (parent) { + loop->SetParent(nullptr); + parent->AddNestedLoop(loop.get()); + + for (uint32_t block_id : loop->GetBlocks()) { + parent->AddBasicBlock(block_id); + } + } + + loops_.emplace_back(loop.release()); + } + + loops_to_add_.clear(); +} + +void LoopDescriptor::ClearLoops() { + for (Loop* loop : loops_) { + delete loop; + } + loops_.clear(); +} + +// Adds a new loop nest to the descriptor set. +Loop* LoopDescriptor::AddLoopNest(std::unique_ptr new_loop) { + Loop* loop = new_loop.release(); + if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); + // Iterate from inner to outer most loop, adding basic block to loop mapping + // as we go. + for (Loop& current_loop : + make_range(iterator::begin(loop), iterator::end(nullptr))) { + loops_.push_back(¤t_loop); + for (uint32_t bb_id : current_loop.GetBlocks()) + basic_block_to_loop_.insert(std::make_pair(bb_id, ¤t_loop)); + } + + return loop; +} + +void LoopDescriptor::RemoveLoop(Loop* loop) { + Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_; + parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(), + parent->nested_loops_.end(), loop)); + std::for_each( + loop->nested_loops_.begin(), loop->nested_loops_.end(), + [loop](Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); }); + parent->nested_loops_.insert(parent->nested_loops_.end(), + loop->nested_loops_.begin(), + loop->nested_loops_.end()); + for (uint32_t bb_id : loop->GetBlocks()) { + Loop* l = FindLoopForBasicBlock(bb_id); + if (l == loop) { + SetBasicBlockToLoop(bb_id, l->GetParent()); + } else { + ForgetBasicBlock(bb_id); + } + } + + LoopContainerType::iterator it = + std::find(loops_.begin(), loops_.end(), loop); + assert(it != loops_.end()); + delete loop; + loops_.erase(it); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.h new file mode 100644 index 00000000..d451496e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_descriptor.h @@ -0,0 +1,577 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_DESCRIPTOR_H_ +#define SOURCE_OPT_LOOP_DESCRIPTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/module.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class CFG; +class LoopDescriptor; + +// A class to represent and manipulate a loop in structured control flow. +class Loop { + // The type used to represent nested child loops. + using ChildrenList = std::vector; + + public: + using iterator = ChildrenList::iterator; + using const_iterator = ChildrenList::const_iterator; + using BasicBlockListTy = std::unordered_set; + + explicit Loop(IRContext* context) + : context_(context), + loop_header_(nullptr), + loop_continue_(nullptr), + loop_merge_(nullptr), + loop_preheader_(nullptr), + loop_latch_(nullptr), + parent_(nullptr), + loop_is_marked_for_removal_(false) {} + + Loop(IRContext* context, DominatorAnalysis* analysis, BasicBlock* header, + BasicBlock* continue_target, BasicBlock* merge_target); + + // Iterators over the immediate sub-loops. + inline iterator begin() { return nested_loops_.begin(); } + inline iterator end() { return nested_loops_.end(); } + inline const_iterator begin() const { return cbegin(); } + inline const_iterator end() const { return cend(); } + inline const_iterator cbegin() const { return nested_loops_.begin(); } + inline const_iterator cend() const { return nested_loops_.end(); } + + // Returns the header (first basic block of the loop). This block contains the + // OpLoopMerge instruction. + inline BasicBlock* GetHeaderBlock() { return loop_header_; } + inline const BasicBlock* GetHeaderBlock() const { return loop_header_; } + inline void SetHeaderBlock(BasicBlock* header) { loop_header_ = header; } + + // Updates the OpLoopMerge instruction to reflect the current state of the + // loop. + inline void UpdateLoopMergeInst() { + assert(GetHeaderBlock()->GetLoopMergeInst() && + "The loop is not structured"); + Instruction* merge_inst = GetHeaderBlock()->GetLoopMergeInst(); + merge_inst->SetInOperand(0, {GetMergeBlock()->id()}); + } + + // Returns the continue target basic block. This is the block designated as + // the continue target by the OpLoopMerge instruction. + inline BasicBlock* GetContinueBlock() { return loop_continue_; } + inline const BasicBlock* GetContinueBlock() const { return loop_continue_; } + + // Returns the latch basic block (basic block that holds the back-edge). + // These functions return nullptr if the loop is not structured (i.e. if it + // has more than one backedge). + inline BasicBlock* GetLatchBlock() { return loop_latch_; } + inline const BasicBlock* GetLatchBlock() const { return loop_latch_; } + + // Sets |latch| as the loop unique block branching back to the header. + // A latch block must have the following properties: + // - |latch| must be in the loop; + // - must be the only block branching back to the header block. + void SetLatchBlock(BasicBlock* latch); + + // Sets |continue_block| as the continue block of the loop. This should be the + // continue target of the OpLoopMerge and should dominate the latch block. + void SetContinueBlock(BasicBlock* continue_block); + + // Returns the basic block which marks the end of the loop. + // These functions return nullptr if the loop is not structured. + inline BasicBlock* GetMergeBlock() { return loop_merge_; } + inline const BasicBlock* GetMergeBlock() const { return loop_merge_; } + // Sets |merge| as the loop merge block. A merge block must have the following + // properties: + // - |merge| must not be in the loop; + // - all its predecessors must be in the loop. + // - it must not be already used as merge block. + // If the loop has an OpLoopMerge in its header, this instruction is also + // updated. + void SetMergeBlock(BasicBlock* merge); + + // Returns the loop pre-header, nullptr means that the loop predecessor does + // not qualify as a preheader. + // The preheader is the unique predecessor that: + // - Dominates the loop header; + // - Has only the loop header as successor. + inline BasicBlock* GetPreHeaderBlock() { return loop_preheader_; } + + // Returns the loop pre-header. + inline const BasicBlock* GetPreHeaderBlock() const { return loop_preheader_; } + // Sets |preheader| as the loop preheader block. A preheader block must have + // the following properties: + // - |merge| must not be in the loop; + // - have an unconditional branch to the loop header. + void SetPreHeaderBlock(BasicBlock* preheader); + + // Returns the loop pre-header, if there is no suitable preheader it will be + // created. Returns |nullptr| if it fails to create the preheader. + BasicBlock* GetOrCreatePreHeaderBlock(); + + // Returns true if this loop contains any nested loops. + inline bool HasNestedLoops() const { return nested_loops_.size() != 0; } + + // Clears and fills |exit_blocks| with all basic blocks that are not in the + // loop and has at least one predecessor in the loop. + void GetExitBlocks(std::unordered_set* exit_blocks) const; + + // Clears and fills |merging_blocks| with all basic blocks that are + // post-dominated by the merge block. The merge block must exist. + // The set |merging_blocks| will only contain the merge block if it is + // unreachable. + void GetMergingBlocks(std::unordered_set* merging_blocks) const; + + // Returns true if the loop is in a Loop Closed SSA form. + // In LCSSA form, all in-loop definitions are used in the loop or in phi + // instructions in the loop exit blocks. + bool IsLCSSA() const; + + // Returns the depth of this loop in the loop nest. + // The outer-most loop has a depth of 1. + inline size_t GetDepth() const { + size_t lvl = 1; + for (const Loop* loop = GetParent(); loop; loop = loop->GetParent()) lvl++; + return lvl; + } + + inline size_t NumImmediateChildren() const { return nested_loops_.size(); } + + inline bool HasChildren() const { return !nested_loops_.empty(); } + // Adds |nested| as a nested loop of this loop. Automatically register |this| + // as the parent of |nested|. + inline void AddNestedLoop(Loop* nested) { + assert(!nested->GetParent() && "The loop has another parent."); + nested_loops_.push_back(nested); + nested->SetParent(this); + } + + inline Loop* GetParent() { return parent_; } + inline const Loop* GetParent() const { return parent_; } + + inline bool HasParent() const { return parent_; } + + // Returns true if this loop is itself nested within another loop. + inline bool IsNested() const { return parent_ != nullptr; } + + // Returns the set of all basic blocks contained within the loop. Will be all + // BasicBlocks dominated by the header which are not also dominated by the + // loop merge block. + inline const BasicBlockListTy& GetBlocks() const { + return loop_basic_blocks_; + } + + // Returns true if the basic block |bb| is inside this loop. + inline bool IsInsideLoop(const BasicBlock* bb) const { + return IsInsideLoop(bb->id()); + } + + // Returns true if the basic block id |bb_id| is inside this loop. + inline bool IsInsideLoop(uint32_t bb_id) const { + return loop_basic_blocks_.count(bb_id); + } + + // Returns true if the instruction |inst| is inside this loop. + bool IsInsideLoop(Instruction* inst) const; + + // Adds the Basic Block |bb| to this loop and its parents. + void AddBasicBlock(const BasicBlock* bb) { AddBasicBlock(bb->id()); } + + // Adds the Basic Block with |id| to this loop and its parents. + void AddBasicBlock(uint32_t id) { + for (Loop* loop = this; loop != nullptr; loop = loop->parent_) { + loop->loop_basic_blocks_.insert(id); + } + } + + // Removes the Basic Block id |bb_id| from this loop and its parents. + // It the user responsibility to make sure the removed block is not a merge, + // header or continue block. + void RemoveBasicBlock(uint32_t bb_id) { + for (Loop* loop = this; loop != nullptr; loop = loop->parent_) { + loop->loop_basic_blocks_.erase(bb_id); + } + } + + // Removes all the basic blocks from the set of basic blocks within the loop. + // This does not affect any of the stored pointers to the header, preheader, + // merge, or continue blocks. + void ClearBlocks() { loop_basic_blocks_.clear(); } + + // Adds the Basic Block |bb| this loop and its parents. + void AddBasicBlockToLoop(const BasicBlock* bb) { + assert(IsBasicBlockInLoopSlow(bb) && + "Basic block does not belong to the loop"); + + AddBasicBlock(bb); + } + + // Returns the list of induction variables within the loop. + void GetInductionVariables(std::vector& inductions) const; + + // This function uses the |condition| to find the induction variable which is + // used by the loop condition within the loop. This only works if the loop is + // bound by a single condition and single induction variable. + Instruction* FindConditionVariable(const BasicBlock* condition) const; + + // Returns the number of iterations within a loop when given the |induction| + // variable and the loop |condition| check. It stores the found number of + // iterations in the output parameter |iterations| and optionally, the step + // value in |step_value| and the initial value of the induction variable in + // |init_value|. + bool FindNumberOfIterations(const Instruction* induction, + const Instruction* condition, size_t* iterations, + int64_t* step_amount = nullptr, + int64_t* init_value = nullptr) const; + + // Returns the value of the OpLoopMerge control operand as a bool. Loop + // control can be None(0), Unroll(1), or DontUnroll(2). This function returns + // true if it is set to Unroll. + inline bool HasUnrollLoopControl() const { + assert(loop_header_); + if (!loop_header_->GetLoopMergeInst()) return false; + + return loop_header_->GetLoopMergeInst()->GetSingleWordOperand(2) == 1; + } + + // Finds the conditional block with a branch to the merge and continue blocks + // within the loop body. + BasicBlock* FindConditionBlock() const; + + // Remove the child loop form this loop. + inline void RemoveChildLoop(Loop* loop) { + nested_loops_.erase( + std::find(nested_loops_.begin(), nested_loops_.end(), loop)); + loop->SetParent(nullptr); + } + + // Mark this loop to be removed later by a call to + // LoopDescriptor::PostModificationCleanup. + inline void MarkLoopForRemoval() { loop_is_marked_for_removal_ = true; } + + // Returns whether or not this loop has been marked for removal. + inline bool IsMarkedForRemoval() const { return loop_is_marked_for_removal_; } + + // Returns true if all nested loops have been marked for removal. + inline bool AreAllChildrenMarkedForRemoval() const { + for (const Loop* child : nested_loops_) { + if (!child->IsMarkedForRemoval()) { + return false; + } + } + return true; + } + + // Checks if the loop contains any instruction that will prevent it from being + // cloned. If the loop is structured, the merge construct is also considered. + bool IsSafeToClone() const; + + // Sets the parent loop of this loop, that is, a loop which contains this loop + // as a nested child loop. + inline void SetParent(Loop* parent) { parent_ = parent; } + + // Returns true is the instruction is invariant and safe to move wrt loop. + bool ShouldHoistInstruction(const Instruction& inst) const; + + // Returns true if all operands of inst are in basic blocks not contained in + // loop. + bool AreAllOperandsOutsideLoop(const Instruction& inst) const; + + // Extract the initial value from the |induction| variable and store it in + // |value|. If the function couldn't find the initial value of |induction| + // return false. + bool GetInductionInitValue(const Instruction* induction, + int64_t* value) const; + + // Takes in a phi instruction |induction| and the loop |header| and returns + // the step operation of the loop. + Instruction* GetInductionStepOperation(const Instruction* induction) const; + + // Returns true if we can deduce the number of loop iterations in the step + // operation |step|. IsSupportedCondition must also be true for the condition + // instruction. + bool IsSupportedStepOp(spv::Op step) const; + + // Returns true if we can deduce the number of loop iterations in the + // condition operation |condition|. IsSupportedStepOp must also be true for + // the step instruction. + bool IsSupportedCondition(spv::Op condition) const; + + // Creates the list of the loop's basic block in structured order and store + // the result in |ordered_loop_blocks|. If |include_pre_header| is true, the + // pre-header block will also be included at the beginning of the list if it + // exist. If |include_merge| is true, the merge block will also be included at + // the end of the list if it exist. + void ComputeLoopStructuredOrder(std::vector* ordered_loop_blocks, + bool include_pre_header = false, + bool include_merge = false) const; + + // Given the loop |condition|, |initial_value|, |step_value|, the trip count + // |number_of_iterations|, and the |unroll_factor| requested, get the new + // condition value for the residual loop. + static int64_t GetResidualConditionValue(spv::Op condition, + int64_t initial_value, + int64_t step_value, + size_t number_of_iterations, + size_t unroll_factor); + + // Returns the condition instruction for entry into the loop + // Returns nullptr if it can't be found. + Instruction* GetConditionInst() const; + + // Returns the context associated this loop. + IRContext* GetContext() const { return context_; } + + // Looks at all the blocks with a branch to the header block to find one + // which is also dominated by the loop continue block. This block is the latch + // block. The specification mandates that this block should exist, therefore + // this function will assert if it is not found. + BasicBlock* FindLatchBlock(); + + private: + IRContext* context_; + // The block which marks the start of the loop. + BasicBlock* loop_header_; + + // The block which begins the body of the loop. + BasicBlock* loop_continue_; + + // The block which marks the end of the loop. + BasicBlock* loop_merge_; + + // The block immediately before the loop header. + BasicBlock* loop_preheader_; + + // The block containing the backedge to the loop header. + BasicBlock* loop_latch_; + + // A parent of a loop is the loop which contains it as a nested child loop. + Loop* parent_; + + // Nested child loops of this loop. + ChildrenList nested_loops_; + + // A set of all the basic blocks which comprise the loop structure. Will be + // computed only when needed on demand. + BasicBlockListTy loop_basic_blocks_; + + // Check that |bb| is inside the loop using domination property. + // Note: this is for assertion purposes only, IsInsideLoop should be used + // instead. + bool IsBasicBlockInLoopSlow(const BasicBlock* bb); + + // Returns the loop preheader if it exists, returns nullptr otherwise. + BasicBlock* FindLoopPreheader(DominatorAnalysis* dom_analysis); + + // Sets |latch| as the loop unique latch block. No checks are performed + // here. + inline void SetLatchBlockImpl(BasicBlock* latch) { loop_latch_ = latch; } + // Sets |merge| as the loop merge block. No checks are performed here. + inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; } + + // Each different loop |condition| affects how we calculate the number of + // iterations using the |condition_value|, |init_value|, and |step_values| of + // the induction variable. This method will return the number of iterations in + // a loop with those values for a given |condition|. Returns 0 if the number + // of iterations could not be computed. + int64_t GetIterations(spv::Op condition, int64_t condition_value, + int64_t init_value, int64_t step_value) const; + + // This is to allow for loops to be removed mid iteration without invalidating + // the iterators. + bool loop_is_marked_for_removal_; + + // This is only to allow LoopDescriptor::placeholder_top_loop_ to add top + // level loops as child. + friend class LoopDescriptor; + friend class LoopUtils; +}; + +// Loop descriptions class for a given function. +// For a given function, the class builds loop nests information. +// The analysis expects a structured control flow. +class LoopDescriptor { + public: + // Iterator interface (depth first postorder traversal). + using iterator = PostOrderTreeDFIterator; + using const_iterator = PostOrderTreeDFIterator; + + using pre_iterator = TreeDFIterator; + using const_pre_iterator = TreeDFIterator; + + // Creates a loop object for all loops found in |f|. + LoopDescriptor(IRContext* context, const Function* f); + + // Disable copy constructor, to avoid double-free on destruction. + LoopDescriptor(const LoopDescriptor&) = delete; + // Move constructor. + LoopDescriptor(LoopDescriptor&& other) : placeholder_top_loop_(nullptr) { + // We need to take ownership of the Loop objects in the other + // LoopDescriptor, to avoid double-free. + loops_ = std::move(other.loops_); + other.loops_.clear(); + basic_block_to_loop_ = std::move(other.basic_block_to_loop_); + other.basic_block_to_loop_.clear(); + placeholder_top_loop_ = std::move(other.placeholder_top_loop_); + } + + // Destructor + ~LoopDescriptor(); + + // Returns the number of loops found in the function. + inline size_t NumLoops() const { return loops_.size(); } + + // Returns the loop at a particular |index|. The |index| must be in bounds, + // check with NumLoops before calling. + inline Loop& GetLoopByIndex(size_t index) const { + assert(loops_.size() > index && + "Index out of range (larger than loop count)"); + return *loops_[index]; + } + + // Returns the loops in |this| in the order their headers appear in the + // binary. + std::vector GetLoopsInBinaryLayoutOrder(); + + // Returns the inner most loop that contains the basic block id |block_id|. + inline Loop* operator[](uint32_t block_id) const { + return FindLoopForBasicBlock(block_id); + } + + // Returns the inner most loop that contains the basic block |bb|. + inline Loop* operator[](const BasicBlock* bb) const { + return (*this)[bb->id()]; + } + + // Iterators for post order depth first traversal of the loops. + // Inner most loops will be visited first. + inline iterator begin() { return iterator::begin(&placeholder_top_loop_); } + inline iterator end() { return iterator::end(&placeholder_top_loop_); } + inline const_iterator begin() const { return cbegin(); } + inline const_iterator end() const { return cend(); } + inline const_iterator cbegin() const { + return const_iterator::begin(&placeholder_top_loop_); + } + inline const_iterator cend() const { + return const_iterator::end(&placeholder_top_loop_); + } + + // Iterators for pre-order depth first traversal of the loops. + // Inner most loops will be visited first. + inline pre_iterator pre_begin() { + return ++pre_iterator(&placeholder_top_loop_); + } + inline pre_iterator pre_end() { return pre_iterator(); } + inline const_pre_iterator pre_begin() const { return pre_cbegin(); } + inline const_pre_iterator pre_end() const { return pre_cend(); } + inline const_pre_iterator pre_cbegin() const { + return ++const_pre_iterator(&placeholder_top_loop_); + } + inline const_pre_iterator pre_cend() const { return const_pre_iterator(); } + + // Returns the inner most loop that contains the basic block |bb|. + inline void SetBasicBlockToLoop(uint32_t bb_id, Loop* loop) { + basic_block_to_loop_[bb_id] = loop; + } + + // Mark the loop |loop_to_add| as needing to be added when the user calls + // PostModificationCleanup. |parent| may be null. + inline void AddLoop(std::unique_ptr&& loop_to_add, Loop* parent) { + loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add))); + } + + // Checks all loops in |this| and will create pre-headers for all loops + // that don't have one. Returns |true| if any blocks were created. + bool CreatePreHeaderBlocksIfMissing(); + + // Should be called to preserve the LoopAnalysis after loops have been marked + // for addition with AddLoop or MarkLoopForRemoval. + void PostModificationCleanup(); + + // Removes the basic block id |bb_id| from the block to loop mapping. + inline void ForgetBasicBlock(uint32_t bb_id) { + basic_block_to_loop_.erase(bb_id); + } + + // Adds the loop |new_loop| and all its nested loops to the descriptor set. + // The object takes ownership of all the loops. + Loop* AddLoopNest(std::unique_ptr new_loop); + + // Remove the loop |loop|. + void RemoveLoop(Loop* loop); + + void SetAsTopLoop(Loop* loop) { + assert(std::find(placeholder_top_loop_.begin(), placeholder_top_loop_.end(), + loop) == placeholder_top_loop_.end() && + "already registered"); + placeholder_top_loop_.nested_loops_.push_back(loop); + } + + Loop* GetPlaceholderRootLoop() { return &placeholder_top_loop_; } + const Loop* GetPlaceholderRootLoop() const { return &placeholder_top_loop_; } + + private: + // TODO(dneto): This should be a vector of unique_ptr. But VisualStudio 2013 + // is unable to compile it. + using LoopContainerType = std::vector; + + using LoopsToAddContainerType = + std::vector>>; + + // Creates loop descriptors for the function |f|. + void PopulateList(IRContext* context, const Function* f); + + // Returns the inner most loop that contains the basic block id |block_id|. + inline Loop* FindLoopForBasicBlock(uint32_t block_id) const { + std::unordered_map::const_iterator it = + basic_block_to_loop_.find(block_id); + return it != basic_block_to_loop_.end() ? it->second : nullptr; + } + + // Erase all the loop information. + void ClearLoops(); + + // A list of all the loops in the function. This variable owns the Loop + // objects. + LoopContainerType loops_; + + // Placeholder root: this "loop" is only there to help iterators creation. + Loop placeholder_top_loop_; + + std::unordered_map basic_block_to_loop_; + + // List of the loops marked for addition when PostModificationCleanup is + // called. + LoopsToAddContainerType loops_to_add_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_DESCRIPTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.cpp new file mode 100644 index 00000000..1bbe4baa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.cpp @@ -0,0 +1,514 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fission.h" + +#include + +#include "source/opt/register_pressure.h" + +// Implement loop fission with an optional parameter to split only +// if the register pressure in a given loop meets a certain criteria. This is +// controlled via the constructors of LoopFissionPass. +// +// 1 - Build a list of loops to be split, these are top level loops (loops +// without child loops themselves) which meet the register pressure criteria, as +// determined by the ShouldSplitLoop method of LoopFissionPass. +// +// 2 - For each loop in the list, group each instruction into a set of related +// instructions by traversing each instructions users and operands recursively. +// We stop if we encounter an instruction we have seen before or an instruction +// which we don't consider relevant (i.e OpLoopMerge). We then group these +// groups into two different sets, one for the first loop and one for the +// second. +// +// 3 - We then run CanPerformSplit to check that it would be legal to split a +// loop using those two sets. We check that we haven't altered the relative +// order load/stores appear in the binary and that we aren't breaking any +// dependency between load/stores by splitting them into two loops. We also +// check that none of the OpBranch instructions are dependent on a load as we +// leave control flow structure intact and move only instructions in the body so +// we want to avoid any loads with side affects or aliasing. +// +// 4 - We then split the loop by calling SplitLoop. This function clones the +// loop and attaches it to the preheader and connects the new loops merge block +// to the current loop header block. We then use the two sets built in step 2 to +// remove instructions from each loop. If an instruction appears in the first +// set it is removed from the second loop and vice versa. +// +// 5 - If the multiple split passes flag is set we check if each of the loops +// still meet the register pressure criteria. If they do then we add them to the +// list of loops to be split (created in step one) to allow for loops to be +// split multiple times. +// + +namespace spvtools { +namespace opt { + +class LoopFissionImpl { + public: + LoopFissionImpl(IRContext* context, Loop* loop) + : context_(context), loop_(loop), load_used_in_condition_(false) {} + + // Group each instruction in the loop into sets of instructions related by + // their usedef chains. An instruction which uses another will appear in the + // same set. Then merge those sets into just two sets. Returns false if there + // was one or less sets created. + bool GroupInstructionsByUseDef(); + + // Check if the sets built by GroupInstructionsByUseDef violate any data + // dependence rules. + bool CanPerformSplit(); + + // Split the loop and return a pointer to the new loop. + Loop* SplitLoop(); + + // Checks if |inst| is safe to move. We can only move instructions which don't + // have any side effects and OpLoads and OpStores. + bool MovableInstruction(const Instruction& inst) const; + + private: + // Traverse the def use chain of |inst| and add the users and uses of |inst| + // which are in the same loop to the |returned_set|. + void TraverseUseDef(Instruction* inst, std::set* returned_set, + bool ignore_phi_users = false, bool report_loads = false); + + // We group the instructions in the block into two different groups, the + // instructions to be kept in the original loop and the ones to be cloned into + // the new loop. As the cloned loop is attached to the preheader it will be + // the first loop and the second loop will be the original. + std::set cloned_loop_instructions_; + std::set original_loop_instructions_; + + // We need a set of all the instructions to be seen so we can break any + // recursion and also so we can ignore certain instructions by preemptively + // adding them to this set. + std::set seen_instructions_; + + // A map of instructions to their relative position in the function. + std::map instruction_order_; + + IRContext* context_; + + Loop* loop_; + + // This is set to true by TraverseUseDef when traversing the instructions + // related to the loop condition and any if conditions should any of those + // instructions be a load. + bool load_used_in_condition_; +}; + +bool LoopFissionImpl::MovableInstruction(const Instruction& inst) const { + return inst.opcode() == spv::Op::OpLoad || + inst.opcode() == spv::Op::OpStore || + inst.opcode() == spv::Op::OpSelectionMerge || + inst.opcode() == spv::Op::OpPhi || inst.IsOpcodeCodeMotionSafe(); +} + +void LoopFissionImpl::TraverseUseDef(Instruction* inst, + std::set* returned_set, + bool ignore_phi_users, bool report_loads) { + assert(returned_set && "Set to be returned cannot be null."); + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + std::set& inst_set = *returned_set; + + // We create this functor to traverse the use def chain to build the + // grouping of related instructions. The lambda captures the std::function + // to allow it to recurse. + std::function traverser_functor; + traverser_functor = [this, def_use, &inst_set, &traverser_functor, + ignore_phi_users, report_loads](Instruction* user) { + // If we've seen the instruction before or it is not inside the loop end the + // traversal. + if (!user || seen_instructions_.count(user) != 0 || + !context_->get_instr_block(user) || + !loop_->IsInsideLoop(context_->get_instr_block(user))) { + return; + } + + // Don't include labels or loop merge instructions in the instruction sets. + // Including them would mean we group instructions related only by using the + // same labels (i.e phis). We already preempt the inclusion of + // OpSelectionMerge by adding related instructions to the seen_instructions_ + // set. + if (user->opcode() == spv::Op::OpLoopMerge || + user->opcode() == spv::Op::OpLabel) + return; + + // If the |report_loads| flag is set, set the class field + // load_used_in_condition_ to false. This is used to check that none of the + // condition checks in the loop rely on loads. + if (user->opcode() == spv::Op::OpLoad && report_loads) { + load_used_in_condition_ = true; + } + + // Add the instruction to the set of instructions already seen, this breaks + // recursion and allows us to ignore certain instructions. + seen_instructions_.insert(user); + + inst_set.insert(user); + + // Wrapper functor to traverse the operands of each instruction. + auto traverse_operand = [&traverser_functor, def_use](const uint32_t* id) { + traverser_functor(def_use->GetDef(*id)); + }; + user->ForEachInOperand(traverse_operand); + + // For the first traversal we want to ignore the users of the phi. + if (ignore_phi_users && user->opcode() == spv::Op::OpPhi) return; + + // Traverse each user with this lambda. + def_use->ForEachUser(user, traverser_functor); + + // Wrapper functor for the use traversal. + auto traverse_use = [&traverser_functor](Instruction* use, uint32_t) { + traverser_functor(use); + }; + def_use->ForEachUse(user, traverse_use); + + }; + + // We start the traversal of the use def graph by invoking the above + // lambda with the |inst| parameter. + traverser_functor(inst); +} + +bool LoopFissionImpl::GroupInstructionsByUseDef() { + std::vector> sets{}; + + // We want to ignore all the instructions stemming from the loop condition + // instruction. + BasicBlock* condition_block = loop_->FindConditionBlock(); + + if (!condition_block) return false; + Instruction* condition = &*condition_block->tail(); + + // We iterate over the blocks via iterating over all the blocks in the + // function, we do this so we are iterating in the same order which the blocks + // appear in the binary. + Function& function = *loop_->GetHeaderBlock()->GetParent(); + + // Create a temporary set to ignore certain groups of instructions within the + // loop. We don't want any instructions related to control flow to be removed + // from either loop only instructions within the control flow bodies. + std::set instructions_to_ignore{}; + TraverseUseDef(condition, &instructions_to_ignore, true, true); + + // Traverse control flow instructions to ensure they are added to the + // seen_instructions_ set and will be ignored when it it called with actual + // sets. + for (BasicBlock& block : function) { + if (!loop_->IsInsideLoop(block.id())) continue; + + for (Instruction& inst : block) { + // Ignore all instructions related to control flow. + if (inst.opcode() == spv::Op::OpSelectionMerge || inst.IsBranch()) { + TraverseUseDef(&inst, &instructions_to_ignore, true, true); + } + } + } + + // Traverse the instructions and generate the sets, automatically ignoring any + // instructions in instructions_to_ignore. + for (BasicBlock& block : function) { + if (!loop_->IsInsideLoop(block.id()) || + loop_->GetHeaderBlock()->id() == block.id()) + continue; + + for (Instruction& inst : block) { + // Record the order that each load/store is seen. + if (inst.opcode() == spv::Op::OpLoad || + inst.opcode() == spv::Op::OpStore) { + instruction_order_[&inst] = instruction_order_.size(); + } + + // Ignore instructions already seen in a traversal. + if (seen_instructions_.count(&inst) != 0) { + continue; + } + + // Build the set. + std::set inst_set{}; + TraverseUseDef(&inst, &inst_set); + if (!inst_set.empty()) sets.push_back(std::move(inst_set)); + } + } + + // If we have one or zero sets return false to indicate that due to + // insufficient instructions we couldn't split the loop into two groups and + // thus the loop can't be split any further. + if (sets.size() < 2) { + return false; + } + + // Merge the loop sets into two different sets. In CanPerformSplit we will + // validate that we don't break the relative ordering of loads/stores by doing + // this. + for (size_t index = 0; index < sets.size() / 2; ++index) { + cloned_loop_instructions_.insert(sets[index].begin(), sets[index].end()); + } + for (size_t index = sets.size() / 2; index < sets.size(); ++index) { + original_loop_instructions_.insert(sets[index].begin(), sets[index].end()); + } + + return true; +} + +bool LoopFissionImpl::CanPerformSplit() { + // Return false if any of the condition instructions in the loop depend on a + // load. + if (load_used_in_condition_) { + return false; + } + + // Build a list of all parent loops of this loop. Loop dependence analysis + // needs this structure. + std::vector loops; + Loop* parent_loop = loop_; + while (parent_loop) { + loops.push_back(parent_loop); + parent_loop = parent_loop->GetParent(); + } + + LoopDependenceAnalysis analysis{context_, loops}; + + // A list of all the stores in the cloned loop. + std::vector set_one_stores{}; + + // A list of all the loads in the cloned loop. + std::vector set_one_loads{}; + + // Populate the above lists. + for (Instruction* inst : cloned_loop_instructions_) { + if (inst->opcode() == spv::Op::OpStore) { + set_one_stores.push_back(inst); + } else if (inst->opcode() == spv::Op::OpLoad) { + set_one_loads.push_back(inst); + } + + // If we find any instruction which we can't move (such as a barrier), + // return false. + if (!MovableInstruction(*inst)) return false; + } + + // We need to calculate the depth of the loop to create the loop dependency + // distance vectors. + const size_t loop_depth = loop_->GetDepth(); + + // Check the dependencies between loads in the cloned loop and stores in the + // original and vice versa. + for (Instruction* inst : original_loop_instructions_) { + // If we find any instruction which we can't move (such as a barrier), + // return false. + if (!MovableInstruction(*inst)) return false; + + // Look at the dependency between the loads in the original and stores in + // the cloned loops. + if (inst->opcode() == spv::Op::OpLoad) { + for (Instruction* store : set_one_stores) { + DistanceVector vec{loop_depth}; + + // If the store actually should appear after the load, return false. + // This means the store has been placed in the wrong grouping. + if (instruction_order_[store] > instruction_order_[inst]) { + return false; + } + // If not independent check the distance vector. + if (!analysis.GetDependence(store, inst, &vec)) { + for (DistanceEntry& entry : vec.GetEntries()) { + // A distance greater than zero means that the store in the cloned + // loop has a dependency on the load in the original loop. + if (entry.distance > 0) return false; + } + } + } + } else if (inst->opcode() == spv::Op::OpStore) { + for (Instruction* load : set_one_loads) { + DistanceVector vec{loop_depth}; + + // If the load actually should appear after the store, return false. + if (instruction_order_[load] > instruction_order_[inst]) { + return false; + } + + // If not independent check the distance vector. + if (!analysis.GetDependence(inst, load, &vec)) { + for (DistanceEntry& entry : vec.GetEntries()) { + // A distance less than zero means the load in the cloned loop is + // dependent on the store instruction in the original loop. + if (entry.distance < 0) return false; + } + } + } + } + } + return true; +} + +Loop* LoopFissionImpl::SplitLoop() { + // Clone the loop. + LoopUtils util{context_, loop_}; + LoopUtils::LoopCloningResult clone_results; + Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results); + + // Update the OpLoopMerge in the cloned loop. + cloned_loop->UpdateLoopMergeInst(); + + // Add the loop_ to the module. + // TODO(1841): Handle failure to create pre-header. + Function::iterator it = + util.GetFunction()->FindBlock(loop_->GetOrCreatePreHeaderBlock()->id()); + util.GetFunction()->AddBasicBlocks(clone_results.cloned_bb_.begin(), + clone_results.cloned_bb_.end(), ++it); + loop_->SetPreHeaderBlock(cloned_loop->GetMergeBlock()); + + std::vector instructions_to_kill{}; + + // Kill all the instructions which should appear in the cloned loop but not in + // the original loop. + for (uint32_t id : loop_->GetBlocks()) { + BasicBlock* block = context_->cfg()->block(id); + + for (Instruction& inst : *block) { + // If the instruction appears in the cloned loop instruction group, kill + // it. + if (cloned_loop_instructions_.count(&inst) == 1 && + original_loop_instructions_.count(&inst) == 0) { + instructions_to_kill.push_back(&inst); + if (inst.opcode() == spv::Op::OpPhi) { + context_->ReplaceAllUsesWith( + inst.result_id(), clone_results.value_map_[inst.result_id()]); + } + } + } + } + + // Kill all instructions which should appear in the original loop and not in + // the cloned loop. + for (uint32_t id : cloned_loop->GetBlocks()) { + BasicBlock* block = context_->cfg()->block(id); + for (Instruction& inst : *block) { + Instruction* old_inst = clone_results.ptr_map_[&inst]; + // If the instruction belongs to the original loop instruction group, kill + // it. + if (cloned_loop_instructions_.count(old_inst) == 0 && + original_loop_instructions_.count(old_inst) == 1) { + instructions_to_kill.push_back(&inst); + } + } + } + + for (Instruction* i : instructions_to_kill) { + context_->KillInst(i); + } + + return cloned_loop; +} + +LoopFissionPass::LoopFissionPass(const size_t register_threshold_to_split, + bool split_multiple_times) + : split_multiple_times_(split_multiple_times) { + // Split if the number of registers in the loop exceeds + // |register_threshold_to_split|. + split_criteria_ = + [register_threshold_to_split]( + const RegisterLiveness::RegionRegisterLiveness& liveness) { + return liveness.used_registers_ > register_threshold_to_split; + }; +} + +LoopFissionPass::LoopFissionPass() : split_multiple_times_(false) { + // Split by default. + split_criteria_ = [](const RegisterLiveness::RegionRegisterLiveness&) { + return true; + }; +} + +bool LoopFissionPass::ShouldSplitLoop(const Loop& loop, IRContext* c) { + LivenessAnalysis* analysis = c->GetLivenessAnalysis(); + + RegisterLiveness::RegionRegisterLiveness liveness{}; + + Function* function = loop.GetHeaderBlock()->GetParent(); + analysis->Get(function)->ComputeLoopRegisterPressure(loop, &liveness); + + return split_criteria_(liveness); +} + +Pass::Status LoopFissionPass::Process() { + bool changed = false; + + for (Function& f : *context()->module()) { + // We collect all the inner most loops in the function and run the loop + // splitting util on each. The reason we do this is to allow us to iterate + // over each, as creating new loops will invalidate the loop iterator. + std::vector inner_most_loops{}; + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); + for (Loop& loop : loop_descriptor) { + if (!loop.HasChildren() && ShouldSplitLoop(loop, context())) { + inner_most_loops.push_back(&loop); + } + } + + // List of new loops which meet the criteria to be split again. + std::vector new_loops_to_split{}; + + while (!inner_most_loops.empty()) { + for (Loop* loop : inner_most_loops) { + LoopFissionImpl impl{context(), loop}; + + // Group the instructions in the loop into two different sets of related + // instructions. If we can't group the instructions into the two sets + // then we can't split the loop any further. + if (!impl.GroupInstructionsByUseDef()) { + continue; + } + + if (impl.CanPerformSplit()) { + Loop* second_loop = impl.SplitLoop(); + changed = true; + context()->InvalidateAnalysesExceptFor( + IRContext::kAnalysisLoopAnalysis); + + // If the newly created loop meets the criteria to be split, split it + // again. + if (ShouldSplitLoop(*second_loop, context())) + new_loops_to_split.push_back(second_loop); + + // If the original loop (now split) still meets the criteria to be + // split, split it again. + if (ShouldSplitLoop(*loop, context())) + new_loops_to_split.push_back(loop); + } + } + + // If the split multiple times flag has been set add the new loops which + // meet the splitting criteria into the list of loops to be split on the + // next iteration. + if (split_multiple_times_) { + inner_most_loops = std::move(new_loops_to_split); + new_loops_to_split = {}; + } else { + break; + } + } + } + + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.h new file mode 100644 index 00000000..9bc12c0f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fission.h @@ -0,0 +1,78 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FISSION_H_ +#define SOURCE_OPT_LOOP_FISSION_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_utils.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class LoopFissionPass : public Pass { + public: + // Function used to determine if a given loop should be split. Takes register + // pressure region for that loop as a parameter and returns true if the loop + // should be split. + using FissionCriteriaFunction = + std::function; + + // Pass built with this constructor will split all loops regardless of + // register pressure. Will not split loops more than once. + LoopFissionPass(); + + // Split the loop if the number of registers used in the loop exceeds + // |register_threshold_to_split|. |split_multiple_times| flag determines + // whether or not the pass should split loops after already splitting them + // once. + LoopFissionPass(size_t register_threshold_to_split, + bool split_multiple_times = true); + + // Split loops whose register pressure meets the criteria of |functor|. + LoopFissionPass(FissionCriteriaFunction functor, + bool split_multiple_times = true) + : split_criteria_(functor), split_multiple_times_(split_multiple_times) {} + + const char* name() const override { return "loop-fission"; } + + Pass::Status Process() override; + + // Checks if |loop| meets the register pressure criteria to be split. + bool ShouldSplitLoop(const Loop& loop, IRContext* context); + + private: + // Functor to run in ShouldSplitLoop to determine if the register pressure + // criteria is met for splitting the loop. + FissionCriteriaFunction split_criteria_; + + // Flag designating whether or not we should also split the result of + // previously split loops if they meet the register presure criteria. + bool split_multiple_times_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FISSION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.cpp new file mode 100644 index 00000000..dc635530 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.cpp @@ -0,0 +1,733 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fusion.h" + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" + +namespace spvtools { +namespace opt { +namespace { + +// Append all the loops nested in |loop| to |loops|. +void CollectChildren(Loop* loop, std::vector* loops) { + for (auto child : *loop) { + loops->push_back(child); + if (child->NumImmediateChildren() != 0) { + CollectChildren(child, loops); + } + } +} + +// Return the set of locations accessed by |stores| and |loads|. +std::set GetLocationsAccessed( + const std::map>& stores, + const std::map>& loads) { + std::set locations{}; + + for (const auto& kv : stores) { + locations.insert(std::get<0>(kv)); + } + + for (const auto& kv : loads) { + locations.insert(std::get<0>(kv)); + } + + return locations; +} + +// Append all dependences from |sources| to |destinations| to |dependences|. +void GetDependences(std::vector* dependences, + LoopDependenceAnalysis* analysis, + const std::vector& sources, + const std::vector& destinations, + size_t num_entries) { + for (auto source : sources) { + for (auto destination : destinations) { + DistanceVector dist(num_entries); + if (!analysis->GetDependence(source, destination, &dist)) { + dependences->push_back(dist); + } + } + } +} + +// Apped all instructions in |block| to |instructions|. +void AddInstructionsInBlock(std::vector* instructions, + BasicBlock* block) { + for (auto& inst : *block) { + instructions->push_back(&inst); + } + + instructions->push_back(block->GetLabelInst()); +} + +} // namespace + +bool LoopFusion::UsedInContinueOrConditionBlock(Instruction* phi_instruction, + Loop* loop) { + auto condition_block = loop->FindConditionBlock()->id(); + auto continue_block = loop->GetContinueBlock()->id(); + auto not_used = context_->get_def_use_mgr()->WhileEachUser( + phi_instruction, + [this, condition_block, continue_block](Instruction* instruction) { + auto block_id = context_->get_instr_block(instruction)->id(); + return block_id != condition_block && block_id != continue_block; + }); + + return !not_used; +} + +void LoopFusion::RemoveIfNotUsedContinueOrConditionBlock( + std::vector* instructions, Loop* loop) { + instructions->erase( + std::remove_if(std::begin(*instructions), std::end(*instructions), + [this, loop](Instruction* instruction) { + return !UsedInContinueOrConditionBlock(instruction, + loop); + }), + std::end(*instructions)); +} + +bool LoopFusion::AreCompatible() { + // Check that the loops are in the same function. + if (loop_0_->GetHeaderBlock()->GetParent() != + loop_1_->GetHeaderBlock()->GetParent()) { + return false; + } + + // Check that both loops have pre-header blocks. + if (!loop_0_->GetPreHeaderBlock() || !loop_1_->GetPreHeaderBlock()) { + return false; + } + + // Check there are no breaks. + if (context_->cfg()->preds(loop_0_->GetMergeBlock()->id()).size() != 1 || + context_->cfg()->preds(loop_1_->GetMergeBlock()->id()).size() != 1) { + return false; + } + + // Check there are no continues. + if (context_->cfg()->preds(loop_0_->GetContinueBlock()->id()).size() != 1 || + context_->cfg()->preds(loop_1_->GetContinueBlock()->id()).size() != 1) { + return false; + } + + // |GetInductionVariables| returns all OpPhi in the header. Check that both + // loops have exactly one that is used in the continue and condition blocks. + std::vector inductions_0{}, inductions_1{}; + loop_0_->GetInductionVariables(inductions_0); + RemoveIfNotUsedContinueOrConditionBlock(&inductions_0, loop_0_); + + if (inductions_0.size() != 1) { + return false; + } + + induction_0_ = inductions_0.front(); + + loop_1_->GetInductionVariables(inductions_1); + RemoveIfNotUsedContinueOrConditionBlock(&inductions_1, loop_1_); + + if (inductions_1.size() != 1) { + return false; + } + + induction_1_ = inductions_1.front(); + + if (!CheckInit()) { + return false; + } + + if (!CheckCondition()) { + return false; + } + + if (!CheckStep()) { + return false; + } + + // Check adjacency, |loop_0_| should come just before |loop_1_|. + // There is always at least one block between loops, even if it's empty. + // We'll check at most 2 preceding blocks. + + auto pre_header_1 = loop_1_->GetPreHeaderBlock(); + + std::vector block_to_check{}; + block_to_check.push_back(pre_header_1); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + // Follow CFG for one more block. + auto preds = context_->cfg()->preds(pre_header_1->id()); + if (preds.size() == 1) { + auto block = &*containing_function_->FindBlock(preds.front()); + if (block == loop_0_->GetMergeBlock()) { + block_to_check.push_back(block); + } else { + return false; + } + } else { + return false; + } + } + + // Check that the separating blocks are either empty or only contains a store + // to a local variable that is never read (left behind by + // '--eliminate-local-multi-store'). Also allow OpPhi, since the loop could be + // in LCSSA form. + for (auto block : block_to_check) { + for (auto& inst : *block) { + if (inst.opcode() == spv::Op::OpStore) { + // Get the definition of the target to check it's function scope so + // there are no observable side effects. + auto variable = + context_->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0)); + + if (variable->opcode() != spv::Op::OpVariable || + spv::StorageClass(variable->GetSingleWordInOperand(0)) != + spv::StorageClass::Function) { + return false; + } + + // Check the target is never loaded. + auto is_used = false; + context_->get_def_use_mgr()->ForEachUse( + inst.GetSingleWordInOperand(0), + [&is_used](Instruction* use_inst, uint32_t) { + if (use_inst->opcode() == spv::Op::OpLoad) { + is_used = true; + } + }); + + if (is_used) { + return false; + } + } else if (inst.opcode() == spv::Op::OpPhi) { + if (inst.NumInOperands() != 2) { + return false; + } + } else if (inst.opcode() != spv::Op::OpBranch) { + return false; + } + } + } + + return true; +} // namespace opt + +bool LoopFusion::ContainsBarriersOrFunctionCalls(Loop* loop) { + for (const auto& block : loop->GetBlocks()) { + for (const auto& inst : *containing_function_->FindBlock(block)) { + auto opcode = inst.opcode(); + if (opcode == spv::Op::OpFunctionCall || + opcode == spv::Op::OpControlBarrier || + opcode == spv::Op::OpMemoryBarrier || + opcode == spv::Op::OpTypeNamedBarrier || + opcode == spv::Op::OpNamedBarrierInitialize || + opcode == spv::Op::OpMemoryNamedBarrier) { + return true; + } + } + } + + return false; +} + +bool LoopFusion::CheckInit() { + int64_t loop_0_init; + if (!loop_0_->GetInductionInitValue(induction_0_, &loop_0_init)) { + return false; + } + + int64_t loop_1_init; + if (!loop_1_->GetInductionInitValue(induction_1_, &loop_1_init)) { + return false; + } + + if (loop_0_init != loop_1_init) { + return false; + } + + return true; +} + +bool LoopFusion::CheckCondition() { + auto condition_0 = loop_0_->GetConditionInst(); + auto condition_1 = loop_1_->GetConditionInst(); + + if (!loop_0_->IsSupportedCondition(condition_0->opcode()) || + !loop_1_->IsSupportedCondition(condition_1->opcode())) { + return false; + } + + if (condition_0->opcode() != condition_1->opcode()) { + return false; + } + + for (uint32_t i = 0; i < condition_0->NumInOperandWords(); ++i) { + auto arg_0 = context_->get_def_use_mgr()->GetDef( + condition_0->GetSingleWordInOperand(i)); + auto arg_1 = context_->get_def_use_mgr()->GetDef( + condition_1->GetSingleWordInOperand(i)); + + if (arg_0 == induction_0_ && arg_1 == induction_1_) { + continue; + } + + if (arg_0 == induction_0_ && arg_1 != induction_1_) { + return false; + } + + if (arg_1 == induction_1_ && arg_0 != induction_0_) { + return false; + } + + if (arg_0 != arg_1) { + return false; + } + } + + return true; +} + +bool LoopFusion::CheckStep() { + auto scalar_analysis = context_->GetScalarEvolutionAnalysis(); + SENode* induction_node_0 = scalar_analysis->SimplifyExpression( + scalar_analysis->AnalyzeInstruction(induction_0_)); + if (!induction_node_0->AsSERecurrentNode()) { + return false; + } + + SENode* induction_step_0 = + induction_node_0->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step_0->AsSEConstantNode()) { + return false; + } + + SENode* induction_node_1 = scalar_analysis->SimplifyExpression( + scalar_analysis->AnalyzeInstruction(induction_1_)); + if (!induction_node_1->AsSERecurrentNode()) { + return false; + } + + SENode* induction_step_1 = + induction_node_1->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step_1->AsSEConstantNode()) { + return false; + } + + if (*induction_step_0 != *induction_step_1) { + return false; + } + + return true; +} + +std::map> LoopFusion::LocationToMemOps( + const std::vector& mem_ops) { + std::map> location_map{}; + + for (auto instruction : mem_ops) { + auto access_location = context_->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + + while (access_location->opcode() == spv::Op::OpAccessChain) { + access_location = context_->get_def_use_mgr()->GetDef( + access_location->GetSingleWordInOperand(0)); + } + + location_map[access_location].push_back(instruction); + } + + return location_map; +} + +std::pair, std::vector> +LoopFusion::GetLoadsAndStoresInLoop(Loop* loop) { + std::vector loads{}; + std::vector stores{}; + + for (auto block_id : loop->GetBlocks()) { + if (block_id == loop->GetContinueBlock()->id()) { + continue; + } + + for (auto& instruction : *containing_function_->FindBlock(block_id)) { + if (instruction.opcode() == spv::Op::OpLoad) { + loads.push_back(&instruction); + } else if (instruction.opcode() == spv::Op::OpStore) { + stores.push_back(&instruction); + } + } + } + + return std::make_pair(loads, stores); +} + +bool LoopFusion::IsUsedInLoop(Instruction* instruction, Loop* loop) { + auto not_used = context_->get_def_use_mgr()->WhileEachUser( + instruction, [this, loop](Instruction* user) { + auto block_id = context_->get_instr_block(user)->id(); + return !loop->IsInsideLoop(block_id); + }); + + return !not_used; +} + +bool LoopFusion::IsLegal() { + assert(AreCompatible() && "Fusion can't be legal, loops are not compatible."); + + // Bail out if there are function calls as they could have side-effects that + // cause dependencies or if there are any barriers. + if (ContainsBarriersOrFunctionCalls(loop_0_) || + ContainsBarriersOrFunctionCalls(loop_1_)) { + return false; + } + + std::vector phi_instructions{}; + loop_0_->GetInductionVariables(phi_instructions); + + // Check no OpPhi in |loop_0_| is used in |loop_1_|. + for (auto phi_instruction : phi_instructions) { + if (IsUsedInLoop(phi_instruction, loop_1_)) { + return false; + } + } + + // Check no LCSSA OpPhi in merge block of |loop_0_| is used in |loop_1_|. + auto phi_used = false; + loop_0_->GetMergeBlock()->ForEachPhiInst( + [this, &phi_used](Instruction* phi_instruction) { + phi_used |= IsUsedInLoop(phi_instruction, loop_1_); + }); + + if (phi_used) { + return false; + } + + // Grab loads & stores from both loops. + auto loads_stores_0 = GetLoadsAndStoresInLoop(loop_0_); + auto loads_stores_1 = GetLoadsAndStoresInLoop(loop_1_); + + // Build memory location to operation maps. + auto load_locs_0 = LocationToMemOps(std::get<0>(loads_stores_0)); + auto store_locs_0 = LocationToMemOps(std::get<1>(loads_stores_0)); + + auto load_locs_1 = LocationToMemOps(std::get<0>(loads_stores_1)); + auto store_locs_1 = LocationToMemOps(std::get<1>(loads_stores_1)); + + // Get the locations accessed in both loops. + auto locations_0 = GetLocationsAccessed(store_locs_0, load_locs_0); + auto locations_1 = GetLocationsAccessed(store_locs_1, load_locs_1); + + std::vector potential_clashes{}; + + std::set_intersection(std::begin(locations_0), std::end(locations_0), + std::begin(locations_1), std::end(locations_1), + std::back_inserter(potential_clashes)); + + // If the loops don't access the same variables, the fusion is legal. + if (potential_clashes.empty()) { + return true; + } + + // Find variables that have at least one store. + std::vector potential_clashes_with_stores{}; + for (auto location : potential_clashes) { + if (store_locs_0.find(location) != std::end(store_locs_0) || + store_locs_1.find(location) != std::end(store_locs_1)) { + potential_clashes_with_stores.push_back(location); + } + } + + // If there are only loads to the same variables, the fusion is legal. + if (potential_clashes_with_stores.empty()) { + return true; + } + + // Else if loads and at least one store (across loops) to the same variable + // there is a potential dependence and we need to check the dependence + // distance. + + // Find all the loops in this loop nest for the dependency analysis. + std::vector loops{}; + + // Find the parents. + for (auto current_loop = loop_0_; current_loop != nullptr; + current_loop = current_loop->GetParent()) { + loops.push_back(current_loop); + } + + auto this_loop_position = loops.size() - 1; + std::reverse(std::begin(loops), std::end(loops)); + + // Find the children. + CollectChildren(loop_0_, &loops); + CollectChildren(loop_1_, &loops); + + // Check that any dependes created are legal. That means the fused loops do + // not have any dependencies with dependence distance greater than 0 that did + // not exist in the original loops. + + LoopDependenceAnalysis analysis(context_, loops); + + analysis.GetScalarEvolution()->AddLoopsToPretendAreTheSame( + {loop_0_, loop_1_}); + + for (auto location : potential_clashes_with_stores) { + // Analyse dependences from |loop_0_| to |loop_1_|. + std::vector dependences; + // Read-After-Write. + GetDependences(&dependences, &analysis, store_locs_0[location], + load_locs_1[location], loops.size()); + // Write-After-Read. + GetDependences(&dependences, &analysis, load_locs_0[location], + store_locs_1[location], loops.size()); + // Write-After-Write. + GetDependences(&dependences, &analysis, store_locs_0[location], + store_locs_1[location], loops.size()); + + // Check that the induction variables either don't appear in the subscripts + // or the dependence distance is negative. + for (const auto& dependence : dependences) { + const auto& entry = dependence.GetEntries()[this_loop_position]; + if ((entry.dependence_information == + DistanceEntry::DependenceInformation::DISTANCE && + entry.distance < 1) || + (entry.dependence_information == + DistanceEntry::DependenceInformation::IRRELEVANT)) { + continue; + } else { + return false; + } + } + } + + return true; +} + +void ReplacePhiParentWith(Instruction* inst, uint32_t orig_block, + uint32_t new_block) { + if (inst->GetSingleWordInOperand(1) == orig_block) { + inst->SetInOperand(1, {new_block}); + } else { + inst->SetInOperand(3, {new_block}); + } +} + +void LoopFusion::Fuse() { + assert(AreCompatible() && "Can't fuse, loops aren't compatible"); + assert(IsLegal() && "Can't fuse, illegal"); + + // Save the pointers/ids, won't be found in the middle of doing modifications. + auto header_1 = loop_1_->GetHeaderBlock()->id(); + auto condition_1 = loop_1_->FindConditionBlock()->id(); + auto continue_1 = loop_1_->GetContinueBlock()->id(); + auto continue_0 = loop_0_->GetContinueBlock()->id(); + auto condition_block_of_0 = loop_0_->FindConditionBlock(); + + // Find the blocks whose branches need updating. + auto first_block_of_1 = &*(++containing_function_->FindBlock(condition_1)); + auto last_block_of_1 = &*(--containing_function_->FindBlock(continue_1)); + auto last_block_of_0 = &*(--containing_function_->FindBlock(continue_0)); + + // Update the branch for |last_block_of_loop_0| to go to |first_block_of_1|. + last_block_of_0->ForEachSuccessorLabel( + [first_block_of_1](uint32_t* succ) { *succ = first_block_of_1->id(); }); + + // Update the branch for the |last_block_of_loop_1| to go to the continue + // block of |loop_0_|. + last_block_of_1->ForEachSuccessorLabel( + [this](uint32_t* succ) { *succ = loop_0_->GetContinueBlock()->id(); }); + + // Update merge block id in the header of |loop_0_| to the merge block of + // |loop_1_|. + loop_0_->GetHeaderBlock()->ForEachInst([this](Instruction* inst) { + if (inst->opcode() == spv::Op::OpLoopMerge) { + inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()}); + } + }); + + // Update condition branch target in |loop_0_| to the merge block of + // |loop_1_|. + condition_block_of_0->ForEachInst([this](Instruction* inst) { + if (inst->opcode() == spv::Op::OpBranchConditional) { + auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id(); + + if (inst->GetSingleWordInOperand(1) == loop_0_merge_block_id) { + inst->SetInOperand(1, {loop_1_->GetMergeBlock()->id()}); + } else { + inst->SetInOperand(2, {loop_1_->GetMergeBlock()->id()}); + } + } + }); + + // Move OpPhi instructions not corresponding to the induction variable from + // the header of |loop_1_| to the header of |loop_0_|. + std::vector instructions_to_move{}; + for (auto& instruction : *loop_1_->GetHeaderBlock()) { + if (instruction.opcode() == spv::Op::OpPhi && + &instruction != induction_1_) { + instructions_to_move.push_back(&instruction); + } + } + + for (auto& it : instructions_to_move) { + it->RemoveFromList(); + it->InsertBefore(induction_0_); + } + + // Update the OpPhi parents to the correct blocks in |loop_0_|. + loop_0_->GetHeaderBlock()->ForEachPhiInst([this](Instruction* i) { + ReplacePhiParentWith(i, loop_1_->GetPreHeaderBlock()->id(), + loop_0_->GetPreHeaderBlock()->id()); + + ReplacePhiParentWith(i, loop_1_->GetContinueBlock()->id(), + loop_0_->GetContinueBlock()->id()); + }); + + // Update instruction to block mapping & DefUseManager. + for (auto& phi_instruction : instructions_to_move) { + context_->set_instr_block(phi_instruction, loop_0_->GetHeaderBlock()); + context_->get_def_use_mgr()->AnalyzeInstUse(phi_instruction); + } + + // Replace the uses of the induction variable of |loop_1_| with that the + // induction variable of |loop_0_|. + context_->ReplaceAllUsesWith(induction_1_->result_id(), + induction_0_->result_id()); + + // Replace LCSSA OpPhi in merge block of |loop_0_|. + loop_0_->GetMergeBlock()->ForEachPhiInst([this](Instruction* instruction) { + context_->ReplaceAllUsesWith(instruction->result_id(), + instruction->GetSingleWordInOperand(0)); + }); + + // Update LCSSA OpPhi in merge block of |loop_1_|. + loop_1_->GetMergeBlock()->ForEachPhiInst( + [condition_block_of_0](Instruction* instruction) { + instruction->SetInOperand(1, {condition_block_of_0->id()}); + }); + + // Move the continue block of |loop_0_| after the last block of |loop_1_|. + containing_function_->MoveBasicBlockToAfter(continue_0, last_block_of_1); + + // Gather all instructions to be killed from |loop_1_| (induction variable + // initialisation, header, condition and continue blocks). + std::vector instr_to_delete{}; + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetPreHeaderBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetHeaderBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->FindConditionBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetContinueBlock()); + + // There was an additional empty block between the loops, kill that too. + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + AddInstructionsInBlock(&instr_to_delete, loop_0_->GetMergeBlock()); + } + + // Update the CFG, so it wouldn't need invalidating. + auto cfg = context_->cfg(); + + cfg->ForgetBlock(loop_1_->GetPreHeaderBlock()); + cfg->ForgetBlock(loop_1_->GetHeaderBlock()); + cfg->ForgetBlock(loop_1_->FindConditionBlock()); + cfg->ForgetBlock(loop_1_->GetContinueBlock()); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + cfg->ForgetBlock(loop_0_->GetMergeBlock()); + } + + cfg->RemoveEdge(last_block_of_0->id(), loop_0_->GetContinueBlock()->id()); + cfg->AddEdge(last_block_of_0->id(), first_block_of_1->id()); + + cfg->AddEdge(last_block_of_1->id(), loop_0_->GetContinueBlock()->id()); + + cfg->AddEdge(loop_0_->GetContinueBlock()->id(), + loop_1_->GetHeaderBlock()->id()); + + cfg->AddEdge(condition_block_of_0->id(), loop_1_->GetMergeBlock()->id()); + + // Update DefUseManager. + auto def_use_mgr = context_->get_def_use_mgr(); + + // Uses of labels that are in updated branches need analysing. + def_use_mgr->AnalyzeInstUse(last_block_of_0->terminator()); + def_use_mgr->AnalyzeInstUse(last_block_of_1->terminator()); + def_use_mgr->AnalyzeInstUse(loop_0_->GetHeaderBlock()->GetLoopMergeInst()); + def_use_mgr->AnalyzeInstUse(condition_block_of_0->terminator()); + + // Update the LoopDescriptor, so it wouldn't need invalidating. + auto ld = context_->GetLoopDescriptor(containing_function_); + + // Create a copy, so the iterator wouldn't be invalidated. + std::vector loops_to_add_remove{}; + for (auto child_loop : *loop_1_) { + loops_to_add_remove.push_back(child_loop); + } + + for (auto child_loop : loops_to_add_remove) { + loop_1_->RemoveChildLoop(child_loop); + loop_0_->AddNestedLoop(child_loop); + } + + auto loop_1_blocks = loop_1_->GetBlocks(); + + for (auto block : loop_1_blocks) { + loop_1_->RemoveBasicBlock(block); + if (block != header_1 && block != condition_1 && block != continue_1) { + loop_0_->AddBasicBlock(block); + if ((*ld)[block] == loop_1_) { + ld->SetBasicBlockToLoop(block, loop_0_); + } + } + + if ((*ld)[block] == loop_1_) { + ld->ForgetBasicBlock(block); + } + } + + loop_1_->RemoveBasicBlock(loop_1_->GetPreHeaderBlock()->id()); + ld->ForgetBasicBlock(loop_1_->GetPreHeaderBlock()->id()); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + loop_0_->RemoveBasicBlock(loop_0_->GetMergeBlock()->id()); + ld->ForgetBasicBlock(loop_0_->GetMergeBlock()->id()); + } + + loop_0_->SetMergeBlock(loop_1_->GetMergeBlock()); + + loop_1_->ClearBlocks(); + + ld->RemoveLoop(loop_1_); + + // Kill unnecessary instructions and remove all empty blocks. + for (auto inst : instr_to_delete) { + context_->KillInst(inst); + } + + containing_function_->RemoveEmptyBlocks(); + + // Invalidate analyses. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisInstrToBlockMapping | + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse | IRContext::Analysis::kAnalysisCFG); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.h new file mode 100644 index 00000000..769da5f1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion.h @@ -0,0 +1,114 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FUSION_H_ +#define SOURCE_OPT_LOOP_FUSION_H_ + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +class LoopFusion { + public: + LoopFusion(IRContext* context, Loop* loop_0, Loop* loop_1) + : context_(context), + loop_0_(loop_0), + loop_1_(loop_1), + containing_function_(loop_0->GetHeaderBlock()->GetParent()) {} + + // Checks if the |loop_0| and |loop_1| are compatible for fusion. + // That means: + // * they both have one induction variable + // * they have the same upper and lower bounds + // - same initial value + // - same condition + // * they have the same update step + // * they are adjacent, with |loop_0| appearing before |loop_1| + // * there are no break/continue in either of them + // * they both have pre-header blocks (required for ScalarEvolutionAnalysis + // and dependence checking). + bool AreCompatible(); + + // Checks if compatible |loop_0| and |loop_1| are legal to fuse. + // * fused loops do not have any dependencies with dependence distance greater + // than 0 that did not exist in the original loops. + // * there are no function calls in the loops (could have side-effects) + bool IsLegal(); + + // Perform the actual fusion of |loop_0_| and |loop_1_|. The loops have to be + // compatible and the fusion has to be legal. + void Fuse(); + + private: + // Check that the initial values are the same. + bool CheckInit(); + + // Check that the conditions are the same. + bool CheckCondition(); + + // Check that the steps are the same. + bool CheckStep(); + + // Returns |true| if |instruction| is used in the continue or condition block + // of |loop|. + bool UsedInContinueOrConditionBlock(Instruction* instruction, Loop* loop); + + // Remove entries in |instructions| that are not used in the continue or + // condition block of |loop|. + void RemoveIfNotUsedContinueOrConditionBlock( + std::vector* instructions, Loop* loop); + + // Returns |true| if |instruction| is used in |loop|. + bool IsUsedInLoop(Instruction* instruction, Loop* loop); + + // Returns |true| if |loop| has at least one barrier or function call. + bool ContainsBarriersOrFunctionCalls(Loop* loop); + + // Get all instructions in the |loop| (except in the latch block) that have + // the opcode |opcode|. + std::pair, std::vector> + GetLoadsAndStoresInLoop(Loop* loop); + + // Given a vector of memory operations (OpLoad/OpStore), constructs a map from + // variables to the loads/stores that those variables. + std::map> LocationToMemOps( + const std::vector& mem_ops); + + IRContext* context_; + + // The original loops to be fused. + Loop* loop_0_; + Loop* loop_1_; + + // The function that contains |loop_0_| and |loop_1_|. + Function* containing_function_ = nullptr; + + // The induction variables for |loop_0_| and |loop_1_|. + Instruction* induction_0_ = nullptr; + Instruction* induction_1_ = nullptr; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FUSION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.cpp new file mode 100644 index 00000000..097430fc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fusion_pass.h" + +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_fusion.h" +#include "source/opt/register_pressure.h" + +namespace spvtools { +namespace opt { + +Pass::Status LoopFusionPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopFusionPass::ProcessFunction(Function* function) { + LoopDescriptor& ld = *context()->GetLoopDescriptor(function); + + // If a loop doesn't have a preheader needs then it needs to be created. Make + // sure to return Status::SuccessWithChange in that case. + auto modified = ld.CreatePreHeaderBlocksIfMissing(); + + // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be + // picked out so don't have to check every loop + for (auto& loop_0 : ld) { + for (auto& loop_1 : ld) { + LoopFusion fusion(context(), &loop_0, &loop_1); + + if (fusion.AreCompatible() && fusion.IsLegal()) { + RegisterLiveness liveness(context(), function); + RegisterLiveness::RegionRegisterLiveness reg_pressure{}; + liveness.SimulateFusion(loop_0, loop_1, ®_pressure); + + if (reg_pressure.used_registers_ <= max_registers_per_loop_) { + fusion.Fuse(); + // Recurse, as the current iterators will have been invalidated. + ProcessFunction(function); + return true; + } + } + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.h new file mode 100644 index 00000000..9d5b7ccd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_fusion_pass.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FUSION_PASS_H_ +#define SOURCE_OPT_LOOP_FUSION_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Implements a loop fusion pass. +// This pass will look for adjacent loops that are compatible and legal to be +// fused. It will fuse all such loops as long as the register usage for the +// fused loop stays under the threshold defined by |max_registers_per_loop|. +class LoopFusionPass : public Pass { + public: + explicit LoopFusionPass(size_t max_registers_per_loop) + : Pass(), max_registers_per_loop_(max_registers_per_loop) {} + + const char* name() const override { return "loop-fusion"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes have been made to the module. + Status Process() override; + + private: + // Fuse loops in |function| if compatible, legal and the fused loop won't use + // too many registers. + bool ProcessFunction(Function* function); + + // The maximum number of registers a fused loop is allowed to use. + size_t max_registers_per_loop_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FUSION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.cpp new file mode 100644 index 00000000..25c6db12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.cpp @@ -0,0 +1,1086 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_peeling.h" + +#include +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { +namespace { +// Gather the set of blocks for all the path from |entry| to |root|. +void GetBlocksInPath(uint32_t block, uint32_t entry, + std::unordered_set* blocks_in_path, + const CFG& cfg) { + for (uint32_t pid : cfg.preds(block)) { + if (blocks_in_path->insert(pid).second) { + if (pid != entry) { + GetBlocksInPath(pid, entry, blocks_in_path, cfg); + } + } + } +} +} // namespace + +size_t LoopPeelingPass::code_grow_threshold_ = 1000; + +void LoopPeeling::DuplicateAndConnectLoop( + LoopUtils::LoopCloningResult* clone_results) { + CFG& cfg = *context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + assert(CanPeelLoop() && "Cannot peel loop!"); + + std::vector ordered_loop_blocks; + // TODO(1841): Handle failure to create pre-header. + BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock(); + + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks); + + cloned_loop_ = loop_utils_.CloneLoop(clone_results, ordered_loop_blocks); + + // Add the basic block to the function. + Function::iterator it = + loop_utils_.GetFunction()->FindBlock(pre_header->id()); + assert(it != loop_utils_.GetFunction()->end() && + "Pre-header not found in the function."); + loop_utils_.GetFunction()->AddBasicBlocks( + clone_results->cloned_bb_.begin(), clone_results->cloned_bb_.end(), ++it); + + // Make the |loop_|'s preheader the |cloned_loop_| one. + BasicBlock* cloned_header = cloned_loop_->GetHeaderBlock(); + pre_header->ForEachSuccessorLabel( + [cloned_header](uint32_t* succ) { *succ = cloned_header->id(); }); + + // Update cfg. + cfg.RemoveEdge(pre_header->id(), loop_->GetHeaderBlock()->id()); + cloned_loop_->SetPreHeaderBlock(pre_header); + loop_->SetPreHeaderBlock(nullptr); + + // When cloning the loop, we didn't cloned the merge block, so currently + // |cloned_loop_| shares the same block as |loop_|. + // We mutate all branches from |cloned_loop_| block to |loop_|'s merge into a + // branch to |loop_|'s header (so header will also be the merge of + // |cloned_loop_|). + uint32_t cloned_loop_exit = 0; + for (uint32_t pred_id : cfg.preds(loop_->GetMergeBlock()->id())) { + if (loop_->IsInsideLoop(pred_id)) continue; + BasicBlock* bb = cfg.block(pred_id); + assert(cloned_loop_exit == 0 && "The loop has multiple exits."); + cloned_loop_exit = bb->id(); + bb->ForEachSuccessorLabel([this](uint32_t* succ) { + if (*succ == loop_->GetMergeBlock()->id()) + *succ = loop_->GetHeaderBlock()->id(); + }); + } + + // Update cfg. + cfg.RemoveNonExistingEdges(loop_->GetMergeBlock()->id()); + cfg.AddEdge(cloned_loop_exit, loop_->GetHeaderBlock()->id()); + + // Patch the phi of the original loop header: + // - Set the loop entry branch to come from the cloned loop exit block; + // - Set the initial value of the phi using the corresponding cloned loop + // exit values. + // + // We patch the iterating value initializers of the original loop using the + // corresponding cloned loop exit values. Connects the cloned loop iterating + // values to the original loop. This make sure that the initial value of the + // second loop starts with the last value of the first loop. + // + // For example, loops like: + // + // int z = 0; + // for (int i = 0; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + // + // Will become: + // + // int z = 0; + // int i = 0; + // for (; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + // for (; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + loop_->GetHeaderBlock()->ForEachPhiInst([cloned_loop_exit, def_use_mgr, + clone_results, + this](Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (!loop_->IsInsideLoop(phi->GetSingleWordInOperand(i + 1))) { + phi->SetInOperand(i, + {clone_results->value_map_.at( + exit_value_.at(phi->result_id())->result_id())}); + phi->SetInOperand(i + 1, {cloned_loop_exit}); + def_use_mgr->AnalyzeInstUse(phi); + return; + } + } + }); + + // Force the creation of a new preheader for the original loop and set it as + // the merge block for the cloned loop. + // TODO(1841): Handle failure to create pre-header. + cloned_loop_->SetMergeBlock(loop_->GetOrCreatePreHeaderBlock()); +} + +void LoopPeeling::InsertCanonicalInductionVariable( + LoopUtils::LoopCloningResult* clone_results) { + if (original_loop_canonical_induction_variable_) { + canonical_induction_variable_ = + context_->get_def_use_mgr()->GetDef(clone_results->value_map_.at( + original_loop_canonical_induction_variable_->result_id())); + return; + } + + BasicBlock::iterator insert_point = GetClonedLoop()->GetLatchBlock()->tail(); + if (GetClonedLoop()->GetLatchBlock()->GetMergeInst()) { + --insert_point; + } + InstructionBuilder builder( + context_, &*insert_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* uint_1_cst = + builder.GetIntConstant(1, int_type_->IsSigned()); + // Create the increment. + // Note that we do "1 + 1" here, one of the operand should the phi + // value but we don't have it yet. The operand will be set latter. + Instruction* iv_inc = builder.AddIAdd( + uint_1_cst->type_id(), uint_1_cst->result_id(), uint_1_cst->result_id()); + + builder.SetInsertPoint(&*GetClonedLoop()->GetHeaderBlock()->begin()); + + canonical_induction_variable_ = builder.AddPhi( + uint_1_cst->type_id(), + {builder.GetIntConstant(0, int_type_->IsSigned())->result_id(), + GetClonedLoop()->GetPreHeaderBlock()->id(), iv_inc->result_id(), + GetClonedLoop()->GetLatchBlock()->id()}); + // Connect everything. + iv_inc->SetInOperand(0, {canonical_induction_variable_->result_id()}); + + // Update def/use manager. + context_->get_def_use_mgr()->AnalyzeInstUse(iv_inc); + + // If do-while form, use the incremented value. + if (do_while_form_) { + canonical_induction_variable_ = iv_inc; + } +} + +void LoopPeeling::GetIteratorUpdateOperations( + const Loop* loop, Instruction* iterator, + std::unordered_set* operations) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + operations->insert(iterator); + iterator->ForEachInId([def_use_mgr, loop, operations, this](uint32_t* id) { + Instruction* insn = def_use_mgr->GetDef(*id); + if (insn->opcode() == spv::Op::OpLabel) { + return; + } + if (operations->count(insn)) { + return; + } + if (!loop->IsInsideLoop(insn)) { + return; + } + GetIteratorUpdateOperations(loop, insn, operations); + }); +} + +bool LoopPeeling::IsConditionCheckSideEffectFree() const { + CFG& cfg = *context_->cfg(); + + // The "do-while" form does not cause issues, the algorithm takes into account + // the first iteration. + if (!do_while_form_) { + uint32_t condition_block_id = cfg.preds(loop_->GetMergeBlock()->id())[0]; + + std::unordered_set blocks_in_path; + + blocks_in_path.insert(condition_block_id); + GetBlocksInPath(condition_block_id, loop_->GetHeaderBlock()->id(), + &blocks_in_path, cfg); + + for (uint32_t bb_id : blocks_in_path) { + BasicBlock* bb = cfg.block(bb_id); + if (!bb->WhileEachInst([this](Instruction* insn) { + if (insn->IsBranch()) return true; + switch (insn->opcode()) { + case spv::Op::OpLabel: + case spv::Op::OpSelectionMerge: + case spv::Op::OpLoopMerge: + return true; + default: + break; + } + return context_->IsCombinatorInstruction(insn); + })) { + return false; + } + } + } + + return true; +} + +void LoopPeeling::GetIteratingExitValues() { + CFG& cfg = *context_->cfg(); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [this](Instruction* phi) { exit_value_[phi->result_id()] = nullptr; }); + + if (!loop_->GetMergeBlock()) { + return; + } + if (cfg.preds(loop_->GetMergeBlock()->id()).size() != 1) { + return; + } + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + uint32_t condition_block_id = cfg.preds(loop_->GetMergeBlock()->id())[0]; + + auto& header_pred = cfg.preds(loop_->GetHeaderBlock()->id()); + do_while_form_ = std::find(header_pred.begin(), header_pred.end(), + condition_block_id) != header_pred.end(); + if (do_while_form_) { + loop_->GetHeaderBlock()->ForEachPhiInst( + [condition_block_id, def_use_mgr, this](Instruction* phi) { + std::unordered_set operations; + + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (condition_block_id == phi->GetSingleWordInOperand(i + 1)) { + exit_value_[phi->result_id()] = + def_use_mgr->GetDef(phi->GetSingleWordInOperand(i)); + } + } + }); + } else { + DominatorTree* dom_tree = + &context_->GetDominatorAnalysis(loop_utils_.GetFunction()) + ->GetDomTree(); + BasicBlock* condition_block = cfg.block(condition_block_id); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [dom_tree, condition_block, this](Instruction* phi) { + std::unordered_set operations; + + // Not the back-edge value, check if the phi instruction is the only + // possible candidate. + GetIteratorUpdateOperations(loop_, phi, &operations); + + for (Instruction* insn : operations) { + if (insn == phi) { + continue; + } + if (dom_tree->Dominates(context_->get_instr_block(insn), + condition_block)) { + return; + } + } + exit_value_[phi->result_id()] = phi; + }); + } +} + +void LoopPeeling::FixExitCondition( + const std::function& condition_builder) { + CFG& cfg = *context_->cfg(); + + uint32_t condition_block_id = 0; + for (uint32_t id : cfg.preds(GetClonedLoop()->GetMergeBlock()->id())) { + if (GetClonedLoop()->IsInsideLoop(id)) { + condition_block_id = id; + break; + } + } + assert(condition_block_id != 0 && "2nd loop in improperly connected"); + + BasicBlock* condition_block = cfg.block(condition_block_id); + Instruction* exit_condition = condition_block->terminator(); + assert(exit_condition->opcode() == spv::Op::OpBranchConditional); + BasicBlock::iterator insert_point = condition_block->tail(); + if (condition_block->GetMergeInst()) { + --insert_point; + } + + exit_condition->SetInOperand(0, {condition_builder(&*insert_point)}); + + uint32_t to_continue_block_idx = + GetClonedLoop()->IsInsideLoop(exit_condition->GetSingleWordInOperand(1)) + ? 1 + : 2; + exit_condition->SetInOperand( + 1, {exit_condition->GetSingleWordInOperand(to_continue_block_idx)}); + exit_condition->SetInOperand(2, {GetClonedLoop()->GetMergeBlock()->id()}); + + // Update def/use manager. + context_->get_def_use_mgr()->AnalyzeInstUse(exit_condition); +} + +BasicBlock* LoopPeeling::CreateBlockBefore(BasicBlock* bb) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + CFG& cfg = *context_->cfg(); + assert(cfg.preds(bb->id()).size() == 1 && "More than one predecessor"); + + // TODO(1841): Handle id overflow. + std::unique_ptr new_bb = + MakeUnique(std::unique_ptr(new Instruction( + context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))); + // Update the loop descriptor. + Loop* in_loop = (*loop_utils_.GetLoopDescriptor())[bb]; + if (in_loop) { + in_loop->AddBasicBlock(new_bb.get()); + loop_utils_.GetLoopDescriptor()->SetBasicBlockToLoop(new_bb->id(), in_loop); + } + + context_->set_instr_block(new_bb->GetLabelInst(), new_bb.get()); + def_use_mgr->AnalyzeInstDefUse(new_bb->GetLabelInst()); + + BasicBlock* bb_pred = cfg.block(cfg.preds(bb->id())[0]); + bb_pred->tail()->ForEachInId([bb, &new_bb](uint32_t* id) { + if (*id == bb->id()) { + *id = new_bb->id(); + } + }); + cfg.RemoveEdge(bb_pred->id(), bb->id()); + cfg.AddEdge(bb_pred->id(), new_bb->id()); + def_use_mgr->AnalyzeInstUse(&*bb_pred->tail()); + + // Update the incoming branch. + bb->ForEachPhiInst([&new_bb, def_use_mgr](Instruction* phi) { + phi->SetInOperand(1, {new_bb->id()}); + def_use_mgr->AnalyzeInstUse(phi); + }); + InstructionBuilder( + context_, new_bb.get(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .AddBranch(bb->id()); + cfg.RegisterBlock(new_bb.get()); + + // Add the basic block to the function. + Function::iterator it = loop_utils_.GetFunction()->FindBlock(bb->id()); + assert(it != loop_utils_.GetFunction()->end() && + "Basic block not found in the function."); + BasicBlock* ret = new_bb.get(); + loop_utils_.GetFunction()->AddBasicBlock(std::move(new_bb), it); + return ret; +} + +BasicBlock* LoopPeeling::ProtectLoop(Loop* loop, Instruction* condition, + BasicBlock* if_merge) { + // TODO(1841): Handle failure to create pre-header. + BasicBlock* if_block = loop->GetOrCreatePreHeaderBlock(); + // Will no longer be a pre-header because of the if. + loop->SetPreHeaderBlock(nullptr); + // Kill the branch to the header. + context_->KillInst(&*if_block->tail()); + + InstructionBuilder builder( + context_, if_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + builder.AddConditionalBranch(condition->result_id(), + loop->GetHeaderBlock()->id(), if_merge->id(), + if_merge->id()); + + return if_block; +} + +void LoopPeeling::PeelBefore(uint32_t peel_factor) { + assert(CanPeelLoop() && "Cannot peel loop"); + LoopUtils::LoopCloningResult clone_results; + + // Clone the loop and insert the cloned one before the loop. + DuplicateAndConnectLoop(&clone_results); + + // Add a canonical induction variable "canonical_induction_variable_". + InsertCanonicalInductionVariable(&clone_results); + + InstructionBuilder builder( + context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* factor = + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + + Instruction* has_remaining_iteration = builder.AddLessThan( + factor->result_id(), loop_iteration_count_->result_id()); + Instruction* max_iteration = builder.AddSelect( + factor->type_id(), has_remaining_iteration->result_id(), + factor->result_id(), loop_iteration_count_->result_id()); + + // Change the exit condition of the cloned loop to be (exit when become + // false): + // "canonical_induction_variable_" < min("factor", "loop_iteration_count_") + FixExitCondition([max_iteration, this](Instruction* insert_before_point) { + return InstructionBuilder(context_, insert_before_point, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping) + .AddLessThan(canonical_induction_variable_->result_id(), + max_iteration->result_id()) + ->result_id(); + }); + + // "Protect" the second loop: the second loop can only be executed if + // |has_remaining_iteration| is true (i.e. factor < loop_iteration_count_). + BasicBlock* if_merge_block = loop_->GetMergeBlock(); + loop_->SetMergeBlock(CreateBlockBefore(loop_->GetMergeBlock())); + // Prevent the second loop from being executed if we already executed all the + // required iterations. + BasicBlock* if_block = + ProtectLoop(loop_, has_remaining_iteration, if_merge_block); + // Patch the phi of the merge block. + if_merge_block->ForEachPhiInst( + [&clone_results, if_block, this](Instruction* phi) { + // if_merge_block had previously only 1 predecessor. + uint32_t incoming_value = phi->GetSingleWordInOperand(0); + auto def_in_loop = clone_results.value_map_.find(incoming_value); + if (def_in_loop != clone_results.value_map_.end()) + incoming_value = def_in_loop->second; + phi->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {incoming_value}}); + phi->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {if_block->id()}}); + context_->get_def_use_mgr()->AnalyzeInstUse(phi); + }); + + context_->InvalidateAnalysesExceptFor( + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); +} + +void LoopPeeling::PeelAfter(uint32_t peel_factor) { + assert(CanPeelLoop() && "Cannot peel loop"); + LoopUtils::LoopCloningResult clone_results; + + // Clone the loop and insert the cloned one before the loop. + DuplicateAndConnectLoop(&clone_results); + + // Add a canonical induction variable "canonical_induction_variable_". + InsertCanonicalInductionVariable(&clone_results); + + InstructionBuilder builder( + context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* factor = + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + + Instruction* has_remaining_iteration = builder.AddLessThan( + factor->result_id(), loop_iteration_count_->result_id()); + + // Change the exit condition of the cloned loop to be (exit when become + // false): + // "canonical_induction_variable_" + "factor" < "loop_iteration_count_" + FixExitCondition([factor, this](Instruction* insert_before_point) { + InstructionBuilder cond_builder( + context_, insert_before_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Build the following check: canonical_induction_variable_ + factor < + // iteration_count + return cond_builder + .AddLessThan(cond_builder + .AddIAdd(canonical_induction_variable_->type_id(), + canonical_induction_variable_->result_id(), + factor->result_id()) + ->result_id(), + loop_iteration_count_->result_id()) + ->result_id(); + }); + + // "Protect" the first loop: the first loop can only be executed if + // factor < loop_iteration_count_. + + // The original loop's pre-header was the cloned loop merge block. + GetClonedLoop()->SetMergeBlock( + CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock())); + // Use the second loop preheader as if merge block. + + // Prevent the first loop if only the peeled loop needs it. + BasicBlock* if_block = ProtectLoop(cloned_loop_, has_remaining_iteration, + GetOriginalLoop()->GetPreHeaderBlock()); + + // Patch the phi of the header block. + // We added an if to enclose the first loop and because the phi node are + // connected to the exit value of the first loop, the definition no longer + // dominate the preheader. + // We had to the preheader (our if merge block) the required phi instruction + // and patch the header phi. + GetOriginalLoop()->GetHeaderBlock()->ForEachPhiInst( + [&clone_results, if_block, this](Instruction* phi) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + auto find_value_idx = [](Instruction* phi_inst, Loop* loop) { + uint32_t preheader_value_idx = + !loop->IsInsideLoop(phi_inst->GetSingleWordInOperand(1)) ? 0 : 2; + return preheader_value_idx; + }; + + Instruction* cloned_phi = + def_use_mgr->GetDef(clone_results.value_map_.at(phi->result_id())); + uint32_t cloned_preheader_value = cloned_phi->GetSingleWordInOperand( + find_value_idx(cloned_phi, GetClonedLoop())); + + Instruction* new_phi = + InstructionBuilder(context_, + &*GetOriginalLoop()->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping) + .AddPhi(phi->type_id(), + {phi->GetSingleWordInOperand( + find_value_idx(phi, GetOriginalLoop())), + GetClonedLoop()->GetMergeBlock()->id(), + cloned_preheader_value, if_block->id()}); + + phi->SetInOperand(find_value_idx(phi, GetOriginalLoop()), + {new_phi->result_id()}); + def_use_mgr->AnalyzeInstUse(phi); + }); + + context_->InvalidateAnalysesExceptFor( + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); +} + +Pass::Status LoopPeelingPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopPeelingPass::ProcessFunction(Function* f) { + bool modified = false; + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f); + + std::vector to_process_loop; + to_process_loop.reserve(loop_descriptor.NumLoops()); + for (Loop& l : loop_descriptor) { + to_process_loop.push_back(&l); + } + + ScalarEvolutionAnalysis scev_analysis(context()); + + for (Loop* loop : to_process_loop) { + CodeMetrics loop_size; + loop_size.Analyze(*loop); + + auto try_peel = [&loop_size, &modified, this](Loop* loop_to_peel) -> Loop* { + if (!loop_to_peel->IsLCSSA()) { + LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA(); + } + + bool peeled_loop; + Loop* still_peelable_loop; + std::tie(peeled_loop, still_peelable_loop) = + ProcessLoop(loop_to_peel, &loop_size); + + if (peeled_loop) { + modified = true; + } + + return still_peelable_loop; + }; + + Loop* still_peelable_loop = try_peel(loop); + // The pass is working out the maximum factor by which a loop can be peeled. + // If the loop can potentially be peeled again, then there is only one + // possible direction, so only one call is still needed. + if (still_peelable_loop) { + try_peel(loop); + } + } + + return modified; +} + +std::pair LoopPeelingPass::ProcessLoop(Loop* loop, + CodeMetrics* loop_size) { + ScalarEvolutionAnalysis* scev_analysis = + context()->GetScalarEvolutionAnalysis(); + // Default values for bailing out. + std::pair bail_out{false, nullptr}; + + BasicBlock* exit_block = loop->FindConditionBlock(); + if (!exit_block) { + return bail_out; + } + + Instruction* exiting_iv = loop->FindConditionVariable(exit_block); + if (!exiting_iv) { + return bail_out; + } + size_t iterations = 0; + if (!loop->FindNumberOfIterations(exiting_iv, &*exit_block->tail(), + &iterations)) { + return bail_out; + } + if (!iterations) { + return bail_out; + } + + Instruction* canonical_induction_variable = nullptr; + + loop->GetHeaderBlock()->WhileEachPhiInst([&canonical_induction_variable, + scev_analysis, + this](Instruction* insn) { + if (const SERecurrentNode* iv = + scev_analysis->AnalyzeInstruction(insn)->AsSERecurrentNode()) { + const SEConstantNode* offset = iv->GetOffset()->AsSEConstantNode(); + const SEConstantNode* coeff = iv->GetCoefficient()->AsSEConstantNode(); + if (offset && coeff && offset->FoldToSingleValue() == 0 && + coeff->FoldToSingleValue() == 1) { + if (context()->get_type_mgr()->GetType(insn->type_id())->AsInteger()) { + canonical_induction_variable = insn; + return false; + } + } + } + return true; + }); + + bool is_signed = canonical_induction_variable + ? context() + ->get_type_mgr() + ->GetType(canonical_induction_variable->type_id()) + ->AsInteger() + ->IsSigned() + : false; + + LoopPeeling peeler( + loop, + InstructionBuilder( + context(), loop->GetHeaderBlock(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .GetIntConstant(static_cast(iterations), + is_signed), + canonical_induction_variable); + + if (!peeler.CanPeelLoop()) { + return bail_out; + } + + // For each basic block in the loop, check if it can be peeled. If it + // can, get the direction (before/after) and by which factor. + LoopPeelingInfo peel_info(loop, iterations, scev_analysis); + + uint32_t peel_before_factor = 0; + uint32_t peel_after_factor = 0; + + for (uint32_t block : loop->GetBlocks()) { + if (block == exit_block->id()) { + continue; + } + BasicBlock* bb = cfg()->block(block); + PeelDirection direction; + uint32_t factor; + std::tie(direction, factor) = peel_info.GetPeelingInfo(bb); + + if (direction == PeelDirection::kNone) { + continue; + } + if (direction == PeelDirection::kBefore) { + peel_before_factor = std::max(peel_before_factor, factor); + } else { + assert(direction == PeelDirection::kAfter); + peel_after_factor = std::max(peel_after_factor, factor); + } + } + PeelDirection direction = PeelDirection::kNone; + uint32_t factor = 0; + + // Find which direction we should peel. + if (peel_before_factor) { + factor = peel_before_factor; + direction = PeelDirection::kBefore; + } + if (peel_after_factor) { + if (peel_before_factor < peel_after_factor) { + // Favor a peel after here and give the peel before another shot later. + factor = peel_after_factor; + direction = PeelDirection::kAfter; + } + } + + // Do the peel if we can. + if (direction == PeelDirection::kNone) return bail_out; + + // This does not take into account branch elimination opportunities and + // the unrolling. It assumes the peeled loop will be unrolled as well. + if (factor * loop_size->roi_size_ > code_grow_threshold_) { + return bail_out; + } + loop_size->roi_size_ *= factor; + + // Find if a loop should be peeled again. + Loop* extra_opportunity = nullptr; + + if (direction == PeelDirection::kBefore) { + peeler.PeelBefore(factor); + if (stats_) { + stats_->peeled_loops_.emplace_back(loop, PeelDirection::kBefore, factor); + } + if (peel_after_factor) { + // We could have peeled after, give it another try. + extra_opportunity = peeler.GetOriginalLoop(); + } + } else { + peeler.PeelAfter(factor); + if (stats_) { + stats_->peeled_loops_.emplace_back(loop, PeelDirection::kAfter, factor); + } + if (peel_before_factor) { + // We could have peeled before, give it another try. + extra_opportunity = peeler.GetClonedLoop(); + } + } + + return {true, extra_opportunity}; +} + +uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstLoopInvariantOperand( + Instruction* condition) const { + for (uint32_t i = 0; i < condition->NumInOperands(); i++) { + BasicBlock* bb = + context_->get_instr_block(condition->GetSingleWordInOperand(i)); + if (bb && loop_->IsInsideLoop(bb)) { + return condition->GetSingleWordInOperand(i); + } + } + + return 0; +} + +uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstNonLoopInvariantOperand( + Instruction* condition) const { + for (uint32_t i = 0; i < condition->NumInOperands(); i++) { + BasicBlock* bb = + context_->get_instr_block(condition->GetSingleWordInOperand(i)); + if (!bb || !loop_->IsInsideLoop(bb)) { + return condition->GetSingleWordInOperand(i); + } + } + + return 0; +} + +static bool IsHandledCondition(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: + return true; + default: + return false; + } +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::GetPeelingInfo(BasicBlock* bb) const { + if (bb->terminator()->opcode() != spv::Op::OpBranchConditional) { + return GetNoneDirection(); + } + + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + Instruction* condition = + def_use_mgr->GetDef(bb->terminator()->GetSingleWordInOperand(0)); + + if (!IsHandledCondition(condition->opcode())) { + return GetNoneDirection(); + } + + if (!GetFirstLoopInvariantOperand(condition)) { + // No loop invariant, it cannot be peeled by this pass. + return GetNoneDirection(); + } + if (!GetFirstNonLoopInvariantOperand(condition)) { + // Seems to be a job for the unswitch pass. + return GetNoneDirection(); + } + + // Left hand-side. + SExpression lhs = scev_analysis_->AnalyzeInstruction( + def_use_mgr->GetDef(condition->GetSingleWordInOperand(0))); + if (lhs->GetType() == SENode::CanNotCompute) { + // Can't make any conclusion. + return GetNoneDirection(); + } + + // Right hand-side. + SExpression rhs = scev_analysis_->AnalyzeInstruction( + def_use_mgr->GetDef(condition->GetSingleWordInOperand(1))); + if (rhs->GetType() == SENode::CanNotCompute) { + // Can't make any conclusion. + return GetNoneDirection(); + } + + // Only take into account recurrent expression over the current loop. + bool is_lhs_rec = !scev_analysis_->IsLoopInvariant(loop_, lhs); + bool is_rhs_rec = !scev_analysis_->IsLoopInvariant(loop_, rhs); + + if ((is_lhs_rec && is_rhs_rec) || (!is_lhs_rec && !is_rhs_rec)) { + return GetNoneDirection(); + } + + if (is_lhs_rec) { + if (!lhs->AsSERecurrentNode() || + lhs->AsSERecurrentNode()->GetLoop() != loop_) { + return GetNoneDirection(); + } + } + if (is_rhs_rec) { + if (!rhs->AsSERecurrentNode() || + rhs->AsSERecurrentNode()->GetLoop() != loop_) { + return GetNoneDirection(); + } + } + + // If the op code is ==, then we try a peel before or after. + // If opcode is not <, >, <= or >=, we bail out. + // + // For the remaining cases, we canonicalize the expression so that the + // constant expression is on the left hand side and the recurring expression + // is on the right hand side. If we swap hand side, then < becomes >, <= + // becomes >= etc. + // If the opcode is <=, then we add 1 to the right hand side and do the peel + // check on <. + // If the opcode is >=, then we add 1 to the left hand side and do the peel + // check on >. + + CmpOperator cmp_operator; + switch (condition->opcode()) { + default: + return GetNoneDirection(); + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + return HandleEquality(lhs, rhs); + case spv::Op::OpUGreaterThan: + case spv::Op::OpSGreaterThan: { + cmp_operator = CmpOperator::kGT; + break; + } + case spv::Op::OpULessThan: + case spv::Op::OpSLessThan: { + cmp_operator = CmpOperator::kLT; + break; + } + // We add one to transform >= into > and <= into <. + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpSGreaterThanEqual: { + cmp_operator = CmpOperator::kGE; + break; + } + case spv::Op::OpULessThanEqual: + case spv::Op::OpSLessThanEqual: { + cmp_operator = CmpOperator::kLE; + break; + } + } + + // Force the left hand side to be the non recurring expression. + if (is_lhs_rec) { + std::swap(lhs, rhs); + switch (cmp_operator) { + case CmpOperator::kLT: { + cmp_operator = CmpOperator::kGT; + break; + } + case CmpOperator::kGT: { + cmp_operator = CmpOperator::kLT; + break; + } + case CmpOperator::kLE: { + cmp_operator = CmpOperator::kGE; + break; + } + case CmpOperator::kGE: { + cmp_operator = CmpOperator::kLE; + break; + } + } + } + return HandleInequality(cmp_operator, lhs, rhs->AsSERecurrentNode()); +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtFirstIteration( + SERecurrentNode* rec) const { + return rec->GetOffset(); +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtIteration( + SERecurrentNode* rec, int64_t iteration) const { + SExpression coeff = rec->GetCoefficient(); + SExpression offset = rec->GetOffset(); + + return (coeff * iteration) + offset; +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtLastIteration( + SERecurrentNode* rec) const { + return GetValueAtIteration(rec, loop_max_iterations_ - 1); +} + +bool LoopPeelingPass::LoopPeelingInfo::EvalOperator(CmpOperator cmp_op, + SExpression lhs, + SExpression rhs, + bool* result) const { + assert(scev_analysis_->IsLoopInvariant(loop_, lhs)); + assert(scev_analysis_->IsLoopInvariant(loop_, rhs)); + // We perform the test: 0 cmp_op rhs - lhs + // What is left is then to determine the sign of the expression. + switch (cmp_op) { + case CmpOperator::kLT: { + return scev_analysis_->IsAlwaysGreaterThanZero(rhs - lhs, result); + } + case CmpOperator::kGT: { + return scev_analysis_->IsAlwaysGreaterThanZero(lhs - rhs, result); + } + case CmpOperator::kLE: { + return scev_analysis_->IsAlwaysGreaterOrEqualToZero(rhs - lhs, result); + } + case CmpOperator::kGE: { + return scev_analysis_->IsAlwaysGreaterOrEqualToZero(lhs - rhs, result); + } + } + return false; +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::HandleEquality(SExpression lhs, + SExpression rhs) const { + { + // Try peel before opportunity. + SExpression lhs_cst = lhs; + if (SERecurrentNode* rec_node = lhs->AsSERecurrentNode()) { + lhs_cst = rec_node->GetOffset(); + } + SExpression rhs_cst = rhs; + if (SERecurrentNode* rec_node = rhs->AsSERecurrentNode()) { + rhs_cst = rec_node->GetOffset(); + } + + if (lhs_cst == rhs_cst) { + return Direction{LoopPeelingPass::PeelDirection::kBefore, 1}; + } + } + + { + // Try peel after opportunity. + SExpression lhs_cst = lhs; + if (SERecurrentNode* rec_node = lhs->AsSERecurrentNode()) { + // rec_node(x) = a * x + b + // assign to lhs: a * (loop_max_iterations_ - 1) + b + lhs_cst = GetValueAtLastIteration(rec_node); + } + SExpression rhs_cst = rhs; + if (SERecurrentNode* rec_node = rhs->AsSERecurrentNode()) { + // rec_node(x) = a * x + b + // assign to lhs: a * (loop_max_iterations_ - 1) + b + rhs_cst = GetValueAtLastIteration(rec_node); + } + + if (lhs_cst == rhs_cst) { + return Direction{LoopPeelingPass::PeelDirection::kAfter, 1}; + } + } + + return GetNoneDirection(); +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::HandleInequality(CmpOperator cmp_op, + SExpression lhs, + SERecurrentNode* rhs) const { + SExpression offset = rhs->GetOffset(); + SExpression coefficient = rhs->GetCoefficient(); + // Compute (cst - B) / A. + std::pair flip_iteration = (lhs - offset) / coefficient; + if (!flip_iteration.first->AsSEConstantNode()) { + return GetNoneDirection(); + } + // note: !!flip_iteration.second normalize to 0/1 (via bool cast). + int64_t iteration = + flip_iteration.first->AsSEConstantNode()->FoldToSingleValue() + + !!flip_iteration.second; + if (iteration <= 0 || + loop_max_iterations_ <= static_cast(iteration)) { + // Always true or false within the loop bounds. + return GetNoneDirection(); + } + // If this is a <= or >= operator and the iteration, make sure |iteration| is + // the one flipping the condition. + // If (cst - B) and A are not divisible, this equivalent to a < or > check, so + // we skip this test. + if (!flip_iteration.second && + (cmp_op == CmpOperator::kLE || cmp_op == CmpOperator::kGE)) { + bool first_iteration; + bool current_iteration; + if (!EvalOperator(cmp_op, lhs, offset, &first_iteration) || + !EvalOperator(cmp_op, lhs, GetValueAtIteration(rhs, iteration), + ¤t_iteration)) { + return GetNoneDirection(); + } + // If the condition did not flip the next will. + if (first_iteration == current_iteration) { + iteration++; + } + } + + uint32_t cast_iteration = 0; + // Integrity check: can we fit |iteration| in a uint32_t ? + if (static_cast(iteration) < std::numeric_limits::max()) { + cast_iteration = static_cast(iteration); + } + + if (cast_iteration) { + // Peel before if we are closer to the start, after if closer to the end. + if (loop_max_iterations_ / 2 > cast_iteration) { + return Direction{LoopPeelingPass::PeelDirection::kBefore, cast_iteration}; + } else { + return Direction{ + LoopPeelingPass::PeelDirection::kAfter, + static_cast(loop_max_iterations_ - cast_iteration)}; + } + } + + return GetNoneDirection(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.h new file mode 100644 index 00000000..2a55fe44 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_peeling.h @@ -0,0 +1,336 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_PEELING_H_ +#define SOURCE_OPT_LOOP_PEELING_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +// Utility class to perform the peeling of a given loop. +// The loop peeling transformation make a certain amount of a loop iterations to +// be executed either before (peel before) or after (peel after) the transformed +// loop. +// +// For peeling cases the transformation does the following steps: +// - It clones the loop and inserts the cloned loop before the original loop; +// - It connects all iterating values of the cloned loop with the +// corresponding original loop values so that the second loop starts with +// the appropriate values. +// - It inserts a new induction variable "i" is inserted into the cloned that +// starts with the value 0 and increment by step of one. +// +// The last step is specific to each case: +// - Peel before: the transformation is to peel the "N" first iterations. +// The exit condition of the cloned loop is changed so that the loop +// exits when "i < N" becomes false. The original loop is then protected to +// only execute if there is any iteration left to do. +// - Peel after: the transformation is to peel the "N" last iterations, +// then the exit condition of the cloned loop is changed so that the loop +// exits when "i + N < max_iteration" becomes false, where "max_iteration" +// is the upper bound of the loop. The cloned loop is then protected to +// only execute if there is any iteration left to do no covered by the +// second. +// +// To be peelable: +// - The loop must be in LCSSA form; +// - The loop must not contain any breaks; +// - The loop must not have any ambiguous iterators updates (see +// "CanPeelLoop"). +// The method "CanPeelLoop" checks that those constrained are met. +class LoopPeeling { + public: + // LoopPeeling constructor. + // |loop| is the loop to peel. + // |loop_iteration_count| is the instruction holding the |loop| iteration + // count, must be invariant for |loop| and must be of an int 32 type (signed + // or unsigned). + // |canonical_induction_variable| is an induction variable that can be used to + // count the number of iterations, must be of the same type as + // |loop_iteration_count| and start at 0 and increase by step of one at each + // iteration. The value nullptr is interpreted as no suitable variable exists + // and one will be created. + LoopPeeling(Loop* loop, Instruction* loop_iteration_count, + Instruction* canonical_induction_variable = nullptr) + : context_(loop->GetContext()), + loop_utils_(loop->GetContext(), loop), + loop_(loop), + loop_iteration_count_(!loop->IsInsideLoop(loop_iteration_count) + ? loop_iteration_count + : nullptr), + int_type_(nullptr), + original_loop_canonical_induction_variable_( + canonical_induction_variable), + canonical_induction_variable_(nullptr) { + if (loop_iteration_count_) { + int_type_ = context_->get_type_mgr() + ->GetType(loop_iteration_count_->type_id()) + ->AsInteger(); + if (canonical_induction_variable_) { + assert(canonical_induction_variable_->type_id() == + loop_iteration_count_->type_id() && + "loop_iteration_count and canonical_induction_variable do not " + "have the same type"); + } + } + GetIteratingExitValues(); + } + + // Returns true if the loop can be peeled. + // To be peelable, all operation involved in the update of the loop iterators + // must not dominates the exit condition. This restriction is a work around to + // not miss compile code like: + // + // for (int i = 0; i + 1 < N; i++) {} + // for (int i = 0; ++i < N; i++) {} + // + // The increment will happen before the test on the exit condition leading to + // very look-a-like code. + // + // This restriction will not apply if a loop rotate is applied before (i.e. + // becomes a do-while loop). + bool CanPeelLoop() const { + CFG& cfg = *context_->cfg(); + + if (!loop_iteration_count_) { + return false; + } + if (!int_type_) { + return false; + } + if (int_type_->width() != 32) { + return false; + } + if (!loop_->IsLCSSA()) { + return false; + } + if (!loop_->GetMergeBlock()) { + return false; + } + if (cfg.preds(loop_->GetMergeBlock()->id()).size() != 1) { + return false; + } + if (!IsConditionCheckSideEffectFree()) { + return false; + } + + return !std::any_of(exit_value_.cbegin(), exit_value_.cend(), + [](std::pair it) { + return it.second == nullptr; + }); + } + + // Moves the execution of the |factor| first iterations of the loop into a + // dedicated loop. + void PeelBefore(uint32_t factor); + + // Moves the execution of the |factor| last iterations of the loop into a + // dedicated loop. + void PeelAfter(uint32_t factor); + + // Returns the cloned loop. + Loop* GetClonedLoop() { return cloned_loop_; } + // Returns the original loop. + Loop* GetOriginalLoop() { return loop_; } + + private: + IRContext* context_; + LoopUtils loop_utils_; + // The original loop. + Loop* loop_; + // The initial |loop_| upper bound. + Instruction* loop_iteration_count_; + // The int type to use for the canonical_induction_variable_. + analysis::Integer* int_type_; + // The cloned loop. + Loop* cloned_loop_; + // This is set to true when the exit and back-edge branch instruction is the + // same. + bool do_while_form_; + // The canonical induction variable from the original loop if it exists. + Instruction* original_loop_canonical_induction_variable_; + // The canonical induction variable of the cloned loop. The induction variable + // is initialized to 0 and incremented by step of 1. + Instruction* canonical_induction_variable_; + // Map between loop iterators and exit values. Loop iterators + std::unordered_map exit_value_; + + // Duplicate |loop_| and place the new loop before the cloned loop. Iterating + // values from the cloned loop are then connected to the original loop as + // initializer. + void DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results); + + // Insert the canonical induction variable into the first loop as a simplified + // counter. + void InsertCanonicalInductionVariable( + LoopUtils::LoopCloningResult* clone_results); + + // Fixes the exit condition of the before loop. The function calls + // |condition_builder| to get the condition to use in the conditional branch + // of the loop exit. The loop will be exited if the condition evaluate to + // true. |condition_builder| takes an Instruction* that represent the + // insertion point. + void FixExitCondition( + const std::function& condition_builder); + + // Gathers all operations involved in the update of |iterator| into + // |operations|. + void GetIteratorUpdateOperations( + const Loop* loop, Instruction* iterator, + std::unordered_set* operations); + + // Gathers exiting iterator values. The function builds a map between each + // iterating value in the loop (a phi instruction in the loop header) and its + // SSA value when it exit the loop. If no exit value can be accurately found, + // it is map to nullptr (see comment on CanPeelLoop). + void GetIteratingExitValues(); + + // Returns true if a for-loop has no instruction with effects before the + // condition check. + bool IsConditionCheckSideEffectFree() const; + + // Creates a new basic block and insert it between |bb| and the predecessor of + // |bb|. + BasicBlock* CreateBlockBefore(BasicBlock* bb); + + // Inserts code to only execute |loop| only if the given |condition| is true. + // |if_merge| is a suitable basic block to be used by the if condition as + // merge block. + // The function returns the if block protecting the loop. + BasicBlock* ProtectLoop(Loop* loop, Instruction* condition, + BasicBlock* if_merge); +}; + +// Implements a loop peeling optimization. +// For each loop, the pass will try to peel it if there is conditions that +// are true for the "N" first or last iterations of the loop. +// To avoid code size explosion, too large loops will not be peeled. +class LoopPeelingPass : public Pass { + public: + // Describes the peeling direction. + enum class PeelDirection { + kNone, // Cannot peel + kBefore, // Can peel before + kAfter // Can peel last + }; + + // Holds some statistics about peeled function. + struct LoopPeelingStats { + std::vector> peeled_loops_; + }; + + LoopPeelingPass(LoopPeelingStats* stats = nullptr) : stats_(stats) {} + + // Sets the loop peeling growth threshold. If the code size increase is above + // |code_grow_threshold|, the loop will not be peeled. The code size is + // measured in terms of SPIR-V instructions. + static void SetLoopPeelingThreshold(size_t code_grow_threshold) { + code_grow_threshold_ = code_grow_threshold; + } + + // Returns the loop peeling code growth threshold. + static size_t GetLoopPeelingThreshold() { return code_grow_threshold_; } + + const char* name() const override { return "loop-peeling"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes have been made to the module. + Pass::Status Process() override; + + private: + // Describes the peeling direction. + enum class CmpOperator { + kLT, // less than + kGT, // greater than + kLE, // less than or equal + kGE, // greater than or equal + }; + + class LoopPeelingInfo { + public: + using Direction = std::pair; + + LoopPeelingInfo(Loop* loop, size_t loop_max_iterations, + ScalarEvolutionAnalysis* scev_analysis) + : context_(loop->GetContext()), + loop_(loop), + scev_analysis_(scev_analysis), + loop_max_iterations_(loop_max_iterations) {} + + // Returns by how much and to which direction a loop should be peeled to + // make the conditional branch of the basic block |bb| an unconditional + // branch. If |bb|'s terminator is not a conditional branch or the condition + // is not workable then it returns PeelDirection::kNone and a 0 factor. + Direction GetPeelingInfo(BasicBlock* bb) const; + + private: + // Returns the id of the loop invariant operand of the conditional + // expression |condition|. It returns if no operand is invariant. + uint32_t GetFirstLoopInvariantOperand(Instruction* condition) const; + // Returns the id of the non loop invariant operand of the conditional + // expression |condition|. It returns if all operands are invariant. + uint32_t GetFirstNonLoopInvariantOperand(Instruction* condition) const; + + // Returns the value of |rec| at the first loop iteration. + SExpression GetValueAtFirstIteration(SERecurrentNode* rec) const; + // Returns the value of |rec| at the given |iteration|. + SExpression GetValueAtIteration(SERecurrentNode* rec, + int64_t iteration) const; + // Returns the value of |rec| at the last loop iteration. + SExpression GetValueAtLastIteration(SERecurrentNode* rec) const; + + bool EvalOperator(CmpOperator cmp_op, SExpression lhs, SExpression rhs, + bool* result) const; + + Direction HandleEquality(SExpression lhs, SExpression rhs) const; + Direction HandleInequality(CmpOperator cmp_op, SExpression lhs, + SERecurrentNode* rhs) const; + + static Direction GetNoneDirection() { + return Direction{LoopPeelingPass::PeelDirection::kNone, 0}; + } + IRContext* context_; + Loop* loop_; + ScalarEvolutionAnalysis* scev_analysis_; + size_t loop_max_iterations_; + }; + // Peel profitable loops in |f|. + bool ProcessFunction(Function* f); + // Peel |loop| if profitable. + std::pair ProcessLoop(Loop* loop, CodeMetrics* loop_size); + + static size_t code_grow_threshold_; + LoopPeelingStats* stats_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_PEELING_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.cpp new file mode 100644 index 00000000..2e279a78 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.cpp @@ -0,0 +1,1147 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_unroller.h" + +#include +#include +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/loop_utils.h" + +// Implements loop util unrolling functionality for fully and partially +// unrolling loops. Given a factor it will duplicate the loop that many times, +// appending each one to the end of the old loop and removing backedges, to +// create a new unrolled loop. +// +// 1 - User calls LoopUtils::FullyUnroll or LoopUtils::PartiallyUnroll with a +// loop they wish to unroll. LoopUtils::CanPerformUnroll is used to +// validate that a given loop can be unrolled. That method (along with the +// constructor of loop) checks that the IR is in the expected canonicalised +// format. +// +// 2 - The LoopUtils methods create a LoopUnrollerUtilsImpl object to actually +// perform the unrolling. This implements helper methods to copy the loop basic +// blocks and remap the ids of instructions used inside them. +// +// 3 - The core of LoopUnrollerUtilsImpl is the Unroll method, this method +// actually performs the loop duplication. It does this by creating a +// LoopUnrollState object and then copying the loop as given by the factor +// parameter. The LoopUnrollState object retains the state of the unroller +// between the loop body copies as each iteration needs information on the last +// to adjust the phi induction variable, adjust the OpLoopMerge instruction in +// the main loop header, and change the previous continue block to point to the +// new header and the new continue block to the main loop header. +// +// 4 - If the loop is to be fully unrolled then it is simply closed after step +// 3, with the OpLoopMerge being deleted, the backedge removed, and the +// condition blocks folded. +// +// 5 - If it is being partially unrolled: if the unrolling factor leaves the +// loop with an even number of bodies with respect to the number of loop +// iterations then step 3 is all that is needed. If it is uneven then we need to +// duplicate the loop completely and unroll the duplicated loop to cover the +// residual part and adjust the first loop to cover only the "even" part. For +// instance if you request an unroll factor of 3 on a loop with 10 iterations +// then copying the body three times would leave you with three bodies in the +// loop +// where the loop still iterates over each 4 times. So we make two loops one +// iterating once then a second loop of three iterating 3 times. + +namespace spvtools { +namespace opt { +namespace { + +// Loop control constant value for DontUnroll flag. +constexpr uint32_t kLoopControlDontUnrollIndex = 2; + +// Operand index of the loop control parameter of the OpLoopMerge. +constexpr uint32_t kLoopControlIndex = 2; + +// This utility class encapsulates some of the state we need to maintain between +// loop unrolls. Specifically it maintains key blocks and the induction variable +// in the current loop duplication step and the blocks from the previous one. +// This is because each step of the unroll needs to use data from both the +// preceding step and the original loop. +struct LoopUnrollState { + LoopUnrollState() + : previous_phi_(nullptr), + previous_latch_block_(nullptr), + previous_condition_block_(nullptr), + new_phi(nullptr), + new_continue_block(nullptr), + new_condition_block(nullptr), + new_header_block(nullptr) {} + + // Initialize from the loop descriptor class. + LoopUnrollState(Instruction* induction, BasicBlock* latch_block, + BasicBlock* condition, std::vector&& phis) + : previous_phi_(induction), + previous_latch_block_(latch_block), + previous_condition_block_(condition), + new_phi(nullptr), + new_continue_block(nullptr), + new_condition_block(nullptr), + new_header_block(nullptr) { + previous_phis_ = std::move(phis); + } + + // Swap the state so that the new nodes are now the previous nodes. + void NextIterationState() { + previous_phi_ = new_phi; + previous_latch_block_ = new_latch_block; + previous_condition_block_ = new_condition_block; + previous_phis_ = std::move(new_phis_); + + // Clear new nodes. + new_phi = nullptr; + new_continue_block = nullptr; + new_condition_block = nullptr; + new_header_block = nullptr; + new_latch_block = nullptr; + + // Clear new block/instruction maps. + new_blocks.clear(); + new_inst.clear(); + ids_to_new_inst.clear(); + } + + // The induction variable from the immediately preceding loop body. + Instruction* previous_phi_; + + // All the phi nodes from the previous loop iteration. + std::vector previous_phis_; + + std::vector new_phis_; + + // The previous latch block. The backedge will be removed from this and + // added to the new latch block. + BasicBlock* previous_latch_block_; + + // The previous condition block. This may be folded to flatten the loop. + BasicBlock* previous_condition_block_; + + // The new induction variable. + Instruction* new_phi; + + // The new continue block. + BasicBlock* new_continue_block; + + // The new condition block. + BasicBlock* new_condition_block; + + // The new header block. + BasicBlock* new_header_block; + + // The new latch block. + BasicBlock* new_latch_block; + + // A mapping of new block ids to the original blocks which they were copied + // from. + std::unordered_map new_blocks; + + // A mapping of the original instruction ids to the instruction ids to their + // copies. + std::unordered_map new_inst; + + std::unordered_map ids_to_new_inst; +}; + +// This class implements the actual unrolling. It uses a LoopUnrollState to +// maintain the state of the unrolling in between steps. +class LoopUnrollerUtilsImpl { + public: + using BasicBlockListTy = std::vector>; + + LoopUnrollerUtilsImpl(IRContext* c, Function* function) + : context_(c), + function_(*function), + loop_condition_block_(nullptr), + loop_induction_variable_(nullptr), + number_of_loop_iterations_(0), + loop_step_value_(0), + loop_init_value_(0) {} + + // Unroll the |loop| by given |factor| by copying the whole body |factor| + // times. The resulting basicblock structure will remain a loop. + void PartiallyUnroll(Loop*, size_t factor); + + // If partially unrolling the |loop| would leave the loop with too many bodies + // for its number of iterations then this method should be used. This method + // will duplicate the |loop| completely, making the duplicated loop the + // successor of the original's merge block. The original loop will have its + // condition changed to loop over the residual part and the duplicate will be + // partially unrolled. The resulting structure will be two loops. + void PartiallyUnrollResidualFactor(Loop* loop, size_t factor); + + // Fully unroll the |loop| by copying the full body by the total number of + // loop iterations, folding all conditions, and removing the backedge from the + // continue block to the header. + void FullyUnroll(Loop* loop); + + // Get the ID of the variable in the |phi| paired with |label|. + uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const; + + // Close the loop by removing the OpLoopMerge from the |loop| header block and + // making the backedge point to the merge block. + void CloseUnrolledLoop(Loop* loop); + + // Remove the OpConditionalBranch instruction inside |conditional_block| used + // to branch to either exit or continue the loop and replace it with an + // unconditional OpBranch to block |new_target|. + void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target); + + // Add all blocks_to_add_ to function_ at the |insert_point|. + void AddBlocksToFunction(const BasicBlock* insert_point); + + // Duplicates the |old_loop|, cloning each body and remapping the ids without + // removing instructions or changing relative structure. Result will be stored + // in |new_loop|. + void DuplicateLoop(Loop* old_loop, Loop* new_loop); + + inline size_t GetLoopIterationCount() const { + return number_of_loop_iterations_; + } + + // Extracts the initial state information from the |loop|. + void Init(Loop* loop); + + // Replace the uses of each induction variable outside the loop with the final + // value of the induction variable before the loop exit. To reflect the proper + // state of a fully unrolled loop. + void ReplaceInductionUseWithFinalValue(Loop* loop); + + // Remove all the instructions in the invalidated_instructions_ vector. + void RemoveDeadInstructions(); + + // Replace any use of induction variables outwith the loop with the final + // value of the induction variable in the unrolled loop. + void ReplaceOutsideLoopUseWithFinalValue(Loop* loop); + + // Set the LoopControl operand of the OpLoopMerge instruction to be + // DontUnroll. + void MarkLoopControlAsDontUnroll(Loop* loop) const; + + private: + // Remap all the in |basic_block| to new IDs and keep the mapping of new ids + // to old + // ids. |loop| is used to identify special loop blocks (header, continue, + // etc). + void AssignNewResultIds(BasicBlock* basic_block); + + // Using the map built by AssignNewResultIds, replace the uses in |inst| + // by the id that the use maps to. + void RemapOperands(Instruction* inst); + + // Using the map built by AssignNewResultIds, for each instruction in + // |basic_block| use + // that map to substitute the IDs used by instructions (in the operands) with + // the new ids. + void RemapOperands(BasicBlock* basic_block); + + // Copy the whole body of the loop, all blocks dominated by the |loop| header + // and not dominated by the |loop| merge. The copied body will be linked to by + // the old |loop| continue block and the new body will link to the |loop| + // header via the new continue block. |eliminate_conditions| is used to decide + // whether or not to fold all the condition blocks other than the last one. + void CopyBody(Loop* loop, bool eliminate_conditions); + + // Copy a given |block_to_copy| in the |loop| and record the mapping of the + // old/new ids. |preserve_instructions| determines whether or not the method + // will modify (other than result_id) instructions which are copied. + void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy, + bool preserve_instructions); + + // The actual implementation of the unroll step. Unrolls |loop| by given + // |factor| by copying the body by |factor| times. Also propagates the + // induction variable value throughout the copies. + void Unroll(Loop* loop, size_t factor); + + // Fills the loop_blocks_inorder_ field with the ordered list of basic blocks + // as computed by the method ComputeLoopOrderedBlocks. + void ComputeLoopOrderedBlocks(Loop* loop); + + // Adds the blocks_to_add_ to both the |loop| and to the parent of |loop| if + // the parent exists. + void AddBlocksToLoop(Loop* loop) const; + + // After the partially unroll step the phi instructions in the header block + // will be in an illegal format. This function makes the phis legal by making + // the edge from the latch block come from the new latch block and the value + // to be the actual value of the phi at that point. + void LinkLastPhisToStart(Loop* loop) const; + + // Kill all debug declaration instructions from |bb|. + void KillDebugDeclares(BasicBlock* bb); + + // A pointer to the IRContext. Used to add/remove instructions and for usedef + // chains. + IRContext* context_; + + // A reference the function the loop is within. + Function& function_; + + // A list of basic blocks to be added to the loop at the end of an unroll + // step. + BasicBlockListTy blocks_to_add_; + + // List of instructions which are now dead and can be removed. + std::vector invalidated_instructions_; + + // Maintains the current state of the transform between calls to unroll. + LoopUnrollState state_; + + // An ordered list containing the loop basic blocks. + std::vector loop_blocks_inorder_; + + // The block containing the condition check which contains a conditional + // branch to the merge and continue block. + BasicBlock* loop_condition_block_; + + // The induction variable of the loop. + Instruction* loop_induction_variable_; + + // Phis used in the loop need to be remapped to use the actual result values + // and then be remapped at the end. + std::vector loop_phi_instructions_; + + // The number of loop iterations that the loop would perform pre-unroll. + size_t number_of_loop_iterations_; + + // The amount that the loop steps each iteration. + int64_t loop_step_value_; + + // The value the loop starts stepping from. + int64_t loop_init_value_; +}; + +/* + * Static helper functions. + */ + +// Retrieve the index of the OpPhi instruction |phi| which corresponds to the +// incoming |block| id. +uint32_t GetPhiIndexFromLabel(const BasicBlock* block, const Instruction* phi) { + for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) { + if (block->id() == phi->GetSingleWordInOperand(i)) { + return i; + } + } + assert(false && "Could not find operand in instruction."); + return 0; +} + +void LoopUnrollerUtilsImpl::Init(Loop* loop) { + loop_condition_block_ = loop->FindConditionBlock(); + + // When we reinit the second loop during PartiallyUnrollResidualFactor we need + // to use the cached value from the duplicate step as the dominator tree + // basded solution, loop->FindConditionBlock, requires all the nodes to be + // connected up with the correct branches. They won't be at this point. + if (!loop_condition_block_) { + loop_condition_block_ = state_.new_condition_block; + } + assert(loop_condition_block_); + + loop_induction_variable_ = loop->FindConditionVariable(loop_condition_block_); + assert(loop_induction_variable_); + + bool found = loop->FindNumberOfIterations( + loop_induction_variable_, &*loop_condition_block_->ctail(), + &number_of_loop_iterations_, &loop_step_value_, &loop_init_value_); + (void)found; // To silence unused variable warning on release builds. + assert(found); + + // Blocks are stored in an unordered set of ids in the loop class, we need to + // create the dominator ordered list. + ComputeLoopOrderedBlocks(loop); +} + +// This function is used to partially unroll the loop when the factor provided +// would normally lead to an illegal optimization. Instead of just unrolling the +// loop it creates two loops and unrolls one and adjusts the condition on the +// other. The end result being that the new loop pair iterates over the correct +// number of bodies. +void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, + size_t factor) { + // TODO(1841): Handle id overflow. + std::unique_ptr new_label{new Instruction( + context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})}; + std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + new_exit_bb->SetParent(&function_); + + // Save the id of the block before we move it. + uint32_t new_merge_id = new_exit_bb->id(); + + // Add the block the list of blocks to add, we want this merge block to be + // right at the start of the new blocks. + blocks_to_add_.push_back(std::move(new_exit_bb)); + BasicBlock* new_exit_bb_raw = blocks_to_add_[0].get(); + Instruction& original_conditional_branch = *loop_condition_block_->tail(); + // Duplicate the loop, providing access to the blocks of both loops. + // This is a naked new due to the VS2013 requirement of not having unique + // pointers in vectors, as it will be inserted into a vector with + // loop_descriptor.AddLoop. + std::unique_ptr new_loop = MakeUnique(*loop); + + // Clear the basic blocks of the new loop. + new_loop->ClearBlocks(); + + DuplicateLoop(loop, new_loop.get()); + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + blocks_to_add_.clear(); + + // Create a new merge block for the first loop. + InstructionBuilder builder{context_, new_exit_bb_raw}; + // Make the first loop branch to the second. + builder.AddBranch(new_loop->GetHeaderBlock()->id()); + + loop_condition_block_ = state_.new_condition_block; + loop_induction_variable_ = state_.new_phi; + // Unroll the new loop by the factor with the usual -1 to account for the + // existing block iteration. + Unroll(new_loop.get(), factor); + + LinkLastPhisToStart(new_loop.get()); + AddBlocksToLoop(new_loop.get()); + + // Add the new merge block to the back of the list of blocks to be added. It + // needs to be the last block added to maintain dominator order in the binary. + blocks_to_add_.push_back( + std::unique_ptr(new_loop->GetMergeBlock())); + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + + // Reset the usedef analysis. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // The loop condition. + Instruction* condition_check = def_use_manager->GetDef( + original_conditional_branch.GetSingleWordOperand(0)); + + // This should have been checked by the LoopUtils::CanPerformUnroll function + // before entering this. + assert(loop->IsSupportedCondition(condition_check->opcode())); + + // We need to account for the initial body when calculating the remainder. + int64_t remainder = Loop::GetResidualConditionValue( + condition_check->opcode(), loop_init_value_, loop_step_value_, + number_of_loop_iterations_, factor); + + assert(remainder > std::numeric_limits::min() && + remainder < std::numeric_limits::max()); + + Instruction* new_constant = nullptr; + + // If the remainder is negative then we add a signed constant, otherwise just + // add an unsigned constant. + if (remainder < 0) { + new_constant = builder.GetSintConstant(static_cast(remainder)); + } else { + new_constant = builder.GetUintConstant(static_cast(remainder)); + } + + uint32_t constant_id = new_constant->result_id(); + + // Update the condition check. + condition_check->SetInOperand(1, {constant_id}); + + // Update the next phi node. The phi will have a constant value coming in from + // the preheader block. For the duplicated loop we need to update the constant + // to be the amount of iterations covered by the first loop and the incoming + // block to be the first loops new merge block. + std::vector new_inductions; + new_loop->GetInductionVariables(new_inductions); + + std::vector old_inductions; + loop->GetInductionVariables(old_inductions); + for (size_t index = 0; index < new_inductions.size(); ++index) { + Instruction* new_induction = new_inductions[index]; + Instruction* old_induction = old_inductions[index]; + // Get the index of the loop initalizer, the value coming in from the + // preheader. + uint32_t initalizer_index = + GetPhiIndexFromLabel(new_loop->GetPreHeaderBlock(), old_induction); + + // Replace the second loop initalizer with the phi from the first + new_induction->SetInOperand(initalizer_index - 1, + {old_induction->result_id()}); + new_induction->SetInOperand(initalizer_index, {new_merge_id}); + + // If the use of the first loop induction variable is outside of the loop + // then replace that use with the second loop induction variable. + uint32_t second_loop_induction = new_induction->result_id(); + auto replace_use_outside_of_loop = [loop, second_loop_induction]( + Instruction* user, + uint32_t operand_index) { + if (!loop->IsInsideLoop(user)) { + user->SetOperand(operand_index, {second_loop_induction}); + } + }; + + context_->get_def_use_mgr()->ForEachUse(old_induction, + replace_use_outside_of_loop); + } + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + + context_->ReplaceAllUsesWith(loop->GetMergeBlock()->id(), new_merge_id); + + LoopDescriptor& loop_descriptor = *context_->GetLoopDescriptor(&function_); + + loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent()); + + RemoveDeadInstructions(); +} + +// Mark this loop as DontUnroll as it will already be unrolled and it may not +// be safe to unroll a previously partially unrolled loop. +void LoopUnrollerUtilsImpl::MarkLoopControlAsDontUnroll(Loop* loop) const { + Instruction* loop_merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + assert(loop_merge_inst && + "Loop merge instruction could not be found after entering unroller " + "(should have exited before this)"); + loop_merge_inst->SetInOperand(kLoopControlIndex, + {kLoopControlDontUnrollIndex}); +} + +// Duplicate the |loop| body |factor| - 1 number of times while keeping the loop +// backedge intact. This will leave the loop with |factor| number of bodies +// after accounting for the initial body. +void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) { + // If we unroll a loop partially it will not be safe to unroll it further. + // This is due to the current method of calculating the number of loop + // iterations. + MarkLoopControlAsDontUnroll(loop); + + std::vector inductions; + loop->GetInductionVariables(inductions); + state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(), + loop_condition_block_, std::move(inductions)}; + for (size_t i = 0; i < factor - 1; ++i) { + CopyBody(loop, true); + } +} + +void LoopUnrollerUtilsImpl::RemoveDeadInstructions() { + // Remove the dead instructions. + for (Instruction* inst : invalidated_instructions_) { + context_->KillInst(inst); + } +} + +void LoopUnrollerUtilsImpl::ReplaceInductionUseWithFinalValue(Loop* loop) { + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse | + IRContext::Analysis::kAnalysisInstrToBlockMapping); + + std::vector inductions; + loop->GetInductionVariables(inductions); + + for (size_t index = 0; index < inductions.size(); ++index) { + // We don't want the decorations that applied to the induction variable + // to be applied to the value that replace it. + context_->KillNamesAndDecorates(state_.previous_phis_[index]); + + uint32_t trip_step_id = GetPhiDefID(state_.previous_phis_[index], + state_.previous_latch_block_->id()); + context_->ReplaceAllUsesWith(inductions[index]->result_id(), trip_step_id); + invalidated_instructions_.push_back(inductions[index]); + } +} + +// Fully unroll the loop by partially unrolling it by the number of loop +// iterations minus one for the body already accounted for. +void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) { + // We unroll the loop by number of iterations in the loop. + Unroll(loop, number_of_loop_iterations_); + + // The first condition block is preserved until now so it can be copied. + FoldConditionBlock(loop_condition_block_, 1); + + // Delete the OpLoopMerge and remove the backedge to the header. + CloseUnrolledLoop(loop); + + // Mark the loop for later deletion. This allows us to preserve the loop + // iterators but still disregard dead loops. + loop->MarkLoopForRemoval(); + + // If the loop has a parent add the new blocks to the parent. + if (loop->GetParent()) { + AddBlocksToLoop(loop->GetParent()); + } + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + + ReplaceInductionUseWithFinalValue(loop); + + RemoveDeadInstructions(); + // Invalidate all analyses. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse); +} + +void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) { + // We cannot kill an instruction inside BasicBlock::ForEachInst() + // because it will generate dangling pointers. We use |to_be_killed| + // to kill them after the loop. + std::vector to_be_killed; + + bb->ForEachInst([&to_be_killed, this](Instruction* inst) { + if (context_->get_debug_info_mgr()->IsDebugDeclare(inst)) { + to_be_killed.push_back(inst); + } + }); + for (auto* inst : to_be_killed) context_->KillInst(inst); +} + +// Copy a given basic block, give it a new result_id, and store the new block +// and the id mapping in the state. |preserve_instructions| is used to determine +// whether or not this function should edit instructions other than the +// |result_id|. +void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr, + bool preserve_instructions) { + // Clone the block exactly, including the IDs. + BasicBlock* basic_block = itr->Clone(context_); + basic_block->SetParent(itr->GetParent()); + + // We do not want to duplicate DebugDeclare. + KillDebugDeclares(basic_block); + + // Assign each result a new unique ID and keep a mapping of the old ids to + // the new ones. + AssignNewResultIds(basic_block); + + // If this is the continue block we are copying. + if (itr == loop->GetContinueBlock()) { + // Make the OpLoopMerge point to this block for the continue. + if (!preserve_instructions) { + Instruction* merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + merge_inst->SetInOperand(1, {basic_block->id()}); + context_->UpdateDefUse(merge_inst); + } + + state_.new_continue_block = basic_block; + } + + // If this is the header block we are copying. + if (itr == loop->GetHeaderBlock()) { + state_.new_header_block = basic_block; + + if (!preserve_instructions) { + // Remove the loop merge instruction if it exists. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst) invalidated_instructions_.push_back(merge_inst); + } + } + + // If this is the latch block being copied, record it in the state. + if (itr == loop->GetLatchBlock()) state_.new_latch_block = basic_block; + + // If this is the condition block we are copying. + if (itr == loop_condition_block_) { + state_.new_condition_block = basic_block; + } + + // Add this block to the list of blocks to add to the function at the end of + // the unrolling process. + blocks_to_add_.push_back(std::unique_ptr(basic_block)); + + // Keep tracking the old block via a map. + state_.new_blocks[itr->id()] = basic_block; +} + +void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) { + // Copy each basic block in the loop, give them new ids, and save state + // information. + for (const BasicBlock* itr : loop_blocks_inorder_) { + CopyBasicBlock(loop, itr, false); + } + + // Set the previous latch block to point to the new header. + Instruction* latch_branch = state_.previous_latch_block_->terminator(); + latch_branch->SetInOperand(0, {state_.new_header_block->id()}); + context_->UpdateDefUse(latch_branch); + + // As the algorithm copies the original loop blocks exactly, the tail of the + // latch block on iterations after the first one will be a branch to the new + // header and not the actual loop header. The last continue block in the loop + // should always be a backedge to the global header. + Instruction* new_latch_branch = state_.new_latch_block->terminator(); + new_latch_branch->SetInOperand(0, {loop->GetHeaderBlock()->id()}); + context_->AnalyzeUses(new_latch_branch); + + std::vector inductions; + loop->GetInductionVariables(inductions); + for (size_t index = 0; index < inductions.size(); ++index) { + Instruction* primary_copy = inductions[index]; + + assert(primary_copy->result_id() != 0); + Instruction* induction_clone = + state_.ids_to_new_inst[state_.new_inst[primary_copy->result_id()]]; + + state_.new_phis_.push_back(induction_clone); + assert(induction_clone->result_id() != 0); + + if (!state_.previous_phis_.empty()) { + state_.new_inst[primary_copy->result_id()] = GetPhiDefID( + state_.previous_phis_[index], state_.previous_latch_block_->id()); + } else { + // Do not replace the first phi block ids. + state_.new_inst[primary_copy->result_id()] = primary_copy->result_id(); + } + } + + if (eliminate_conditions && + state_.new_condition_block != loop_condition_block_) { + FoldConditionBlock(state_.new_condition_block, 1); + } + + // Only reference to the header block is the backedge in the latch block, + // don't change this. + state_.new_inst[loop->GetHeaderBlock()->id()] = loop->GetHeaderBlock()->id(); + + for (auto& pair : state_.new_blocks) { + RemapOperands(pair.second); + } + + for (Instruction* dead_phi : state_.new_phis_) + invalidated_instructions_.push_back(dead_phi); + + // Swap the state so the new is now the previous. + state_.NextIterationState(); +} + +uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi, + uint32_t label) const { + for (uint32_t operand = 3; operand < phi->NumOperands(); operand += 2) { + if (phi->GetSingleWordOperand(operand) == label) { + return phi->GetSingleWordOperand(operand - 1); + } + } + assert(false && "Could not find a phi index matching the provided label"); + return 0; +} + +void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, + uint32_t operand_label) { + // Remove the old conditional branch to the merge and continue blocks. + Instruction& old_branch = *condition_block->tail(); + uint32_t new_target = old_branch.GetSingleWordOperand(operand_label); + + DebugScope scope = old_branch.GetDebugScope(); + const std::vector lines = old_branch.dbg_line_insts(); + + context_->KillInst(&old_branch); + // Add the new unconditional branch to the merge block. + InstructionBuilder builder( + context_, condition_block, + IRContext::Analysis::kAnalysisDefUse | + IRContext::Analysis::kAnalysisInstrToBlockMapping); + Instruction* new_branch = builder.AddBranch(new_target); + + if (!lines.empty()) new_branch->AddDebugLine(&lines.back()); + new_branch->SetDebugScope(scope); +} + +void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) { + // Remove the OpLoopMerge instruction from the function. + Instruction* merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + invalidated_instructions_.push_back(merge_inst); + + // Remove the final backedge to the header and make it point instead to the + // merge block. + Instruction* latch_instruction = state_.previous_latch_block_->terminator(); + latch_instruction->SetInOperand(0, {loop->GetMergeBlock()->id()}); + context_->UpdateDefUse(latch_instruction); + + // Remove all induction variables as the phis will now be invalid. Replace all + // uses with the constant initializer value (all uses of phis will be in + // the first iteration with the subsequent phis already having been removed). + std::vector inductions; + loop->GetInductionVariables(inductions); + + // We can use the state instruction mechanism to replace all internal loop + // values within the first loop trip (as the subsequent ones will be updated + // by the copy function) with the value coming in from the preheader and then + // use context ReplaceAllUsesWith for the uses outside the loop with the final + // trip phi value. + state_.new_inst.clear(); + for (Instruction* induction : inductions) { + uint32_t initalizer_id = + GetPhiDefID(induction, loop->GetPreHeaderBlock()->id()); + + state_.new_inst[induction->result_id()] = initalizer_id; + } + + for (BasicBlock* block : loop_blocks_inorder_) { + RemapOperands(block); + } + for (auto& block_itr : blocks_to_add_) { + RemapOperands(block_itr.get()); + } + + // Rewrite the last phis, since they may still reference the original phi. + for (Instruction* last_phi : state_.previous_phis_) { + RemapOperands(last_phi); + } +} + +// Uses the first loop to create a copy of the loop with new IDs. +void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { + std::vector new_block_order; + + // Copy every block in the old loop. + for (const BasicBlock* itr : loop_blocks_inorder_) { + CopyBasicBlock(old_loop, itr, true); + new_block_order.push_back(blocks_to_add_.back().get()); + } + + // Clone the merge block, give it a new id and record it in the state. + BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_); + new_merge->SetParent(old_loop->GetMergeBlock()->GetParent()); + AssignNewResultIds(new_merge); + state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge; + + // Remap the operands of every instruction in the loop to point to the new + // copies. + for (auto& pair : state_.new_blocks) { + RemapOperands(pair.second); + } + + loop_blocks_inorder_ = std::move(new_block_order); + + AddBlocksToLoop(new_loop); + + new_loop->SetHeaderBlock(state_.new_header_block); + new_loop->SetContinueBlock(state_.new_continue_block); + new_loop->SetLatchBlock(state_.new_latch_block); + new_loop->SetMergeBlock(new_merge); +} + +// Whenever the utility copies a block it stores it in a temporary buffer, this +// function adds the buffer into the Function. The blocks will be inserted +// after the block |insert_point|. +void LoopUnrollerUtilsImpl::AddBlocksToFunction( + const BasicBlock* insert_point) { + for (auto basic_block_iterator = function_.begin(); + basic_block_iterator != function_.end(); ++basic_block_iterator) { + if (basic_block_iterator->id() == insert_point->id()) { + basic_block_iterator.InsertBefore(&blocks_to_add_); + return; + } + } + + assert( + false && + "Could not add basic blocks to function as insert point was not found."); +} + +// Assign all result_ids in |basic_block| instructions to new IDs and preserve +// the mapping of new ids to old ones. +void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + // Label instructions aren't covered by normal traversal of the + // instructions. + // TODO(1841): Handle id overflow. + uint32_t new_label_id = context_->TakeNextId(); + + // Assign a new id to the label. + state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id; + basic_block->GetLabelInst()->SetResultId(new_label_id); + def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst()); + + for (Instruction& inst : *basic_block) { + // Do def/use analysis on new lines + for (auto& line : inst.dbg_line_insts()) + def_use_mgr->AnalyzeInstDefUse(&line); + + uint32_t old_id = inst.result_id(); + + // Ignore stores etc. + if (old_id == 0) { + continue; + } + + // Give the instruction a new id. + // TODO(1841): Handle id overflow. + inst.SetResultId(context_->TakeNextId()); + def_use_mgr->AnalyzeInstDef(&inst); + + // Save the mapping of old_id -> new_id. + state_.new_inst[old_id] = inst.result_id(); + // Check if this instruction is the induction variable. + if (loop_induction_variable_->result_id() == old_id) { + // Save a pointer to the new copy of it. + state_.new_phi = &inst; + } + state_.ids_to_new_inst[inst.result_id()] = &inst; + } +} + +void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) { + auto remap_operands_to_new_ids = [this](uint32_t* id) { + auto itr = state_.new_inst.find(*id); + + if (itr != state_.new_inst.end()) { + *id = itr->second; + } + }; + + inst->ForEachInId(remap_operands_to_new_ids); + context_->AnalyzeUses(inst); +} + +void LoopUnrollerUtilsImpl::RemapOperands(BasicBlock* basic_block) { + for (Instruction& inst : *basic_block) { + RemapOperands(&inst); + } +} + +// Generate the ordered list of basic blocks in the |loop| and cache it for +// later use. +void LoopUnrollerUtilsImpl::ComputeLoopOrderedBlocks(Loop* loop) { + loop_blocks_inorder_.clear(); + loop->ComputeLoopStructuredOrder(&loop_blocks_inorder_); +} + +// Adds the blocks_to_add_ to both the loop and to the parent. +void LoopUnrollerUtilsImpl::AddBlocksToLoop(Loop* loop) const { + // Add the blocks to this loop. + for (auto& block_itr : blocks_to_add_) { + loop->AddBasicBlock(block_itr.get()); + } + + // Add the blocks to the parent as well. + if (loop->GetParent()) AddBlocksToLoop(loop->GetParent()); +} + +void LoopUnrollerUtilsImpl::LinkLastPhisToStart(Loop* loop) const { + std::vector inductions; + loop->GetInductionVariables(inductions); + + for (size_t i = 0; i < inductions.size(); ++i) { + Instruction* last_phi_in_block = state_.previous_phis_[i]; + + uint32_t phi_index = + GetPhiIndexFromLabel(state_.previous_latch_block_, last_phi_in_block); + uint32_t phi_variable = + last_phi_in_block->GetSingleWordInOperand(phi_index - 1); + uint32_t phi_label = last_phi_in_block->GetSingleWordInOperand(phi_index); + + Instruction* phi = inductions[i]; + phi->SetInOperand(phi_index - 1, {phi_variable}); + phi->SetInOperand(phi_index, {phi_label}); + } +} + +// Duplicate the |loop| body |factor| number of times while keeping the loop +// backedge intact. +void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) { + Unroll(loop, factor); + LinkLastPhisToStart(loop); + AddBlocksToLoop(loop); + AddBlocksToFunction(loop->GetMergeBlock()); + RemoveDeadInstructions(); +} + +/* + * End LoopUtilsImpl. + */ + +} // namespace + +/* + * + * Begin Utils. + * + * */ + +bool LoopUtils::CanPerformUnroll() { + // The loop is expected to be in structured order. + if (!loop_->GetHeaderBlock()->GetMergeInst()) { + return false; + } + + // Find check the loop has a condition we can find and evaluate. + const BasicBlock* condition = loop_->FindConditionBlock(); + if (!condition) return false; + + // Check that we can find and process the induction variable. + const Instruction* induction = loop_->FindConditionVariable(condition); + if (!induction || induction->opcode() != spv::Op::OpPhi) return false; + + // Check that we can find the number of loop iterations. + if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr)) + return false; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop + // iteration counts. This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, loop unrolling is not applied + // to loops with large iteration counts when fuzzing. + constexpr size_t kFuzzerIterationLimit = 100; + size_t num_iterations; + loop_->FindNumberOfIterations(induction, &*condition->ctail(), + &num_iterations); + if (num_iterations > kFuzzerIterationLimit) { + return false; + } +#endif + + // Make sure the latch block is a unconditional branch to the header + // block. + const Instruction& branch = *loop_->GetLatchBlock()->ctail(); + bool branching_assumption = + branch.opcode() == spv::Op::OpBranch && + branch.GetSingleWordInOperand(0) == loop_->GetHeaderBlock()->id(); + if (!branching_assumption) { + return false; + } + + std::vector inductions; + loop_->GetInductionVariables(inductions); + + // Ban breaks within the loop. + const std::vector& merge_block_preds = + context_->cfg()->preds(loop_->GetMergeBlock()->id()); + if (merge_block_preds.size() != 1) { + return false; + } + + // Ban continues within the loop. + const std::vector& continue_block_preds = + context_->cfg()->preds(loop_->GetContinueBlock()->id()); + if (continue_block_preds.size() != 1) { + return false; + } + + // Ban returns in the loop. + // Iterate over all the blocks within the loop and check that none of them + // exit the loop. + for (uint32_t label_id : loop_->GetBlocks()) { + const BasicBlock* block = context_->cfg()->block(label_id); + if (block->ctail()->opcode() == spv::Op::OpKill || + block->ctail()->opcode() == spv::Op::OpReturn || + block->ctail()->opcode() == spv::Op::OpReturnValue || + block->ctail()->opcode() == spv::Op::OpTerminateInvocation) { + return false; + } + } + // Can only unroll inner loops. + if (!loop_->AreAllChildrenMarkedForRemoval()) { + return false; + } + + return true; +} + +bool LoopUtils::PartiallyUnroll(size_t factor) { + if (factor == 1 || !CanPerformUnroll()) return false; + + // Create the unroller utility. + LoopUnrollerUtilsImpl unroller{context_, + loop_->GetHeaderBlock()->GetParent()}; + unroller.Init(loop_); + + // If the unrolling factor is larger than or the same size as the loop just + // fully unroll the loop. + if (factor >= unroller.GetLoopIterationCount()) { + unroller.FullyUnroll(loop_); + return true; + } + + // If the loop unrolling factor is an residual number of iterations we need to + // let run the loop for the residual part then let it branch into the unrolled + // remaining part. We add one when calucating the remainder to take into + // account the one iteration already in the loop. + if (unroller.GetLoopIterationCount() % factor != 0) { + unroller.PartiallyUnrollResidualFactor(loop_, factor); + } else { + unroller.PartiallyUnroll(loop_, factor); + } + + return true; +} + +bool LoopUtils::FullyUnroll() { + if (!CanPerformUnroll()) return false; + + std::vector inductions; + loop_->GetInductionVariables(inductions); + + LoopUnrollerUtilsImpl unroller{context_, + loop_->GetHeaderBlock()->GetParent()}; + + unroller.Init(loop_); + unroller.FullyUnroll(loop_); + + return true; +} + +void LoopUtils::Finalize() { + // Clean up the loop descriptor to preserve the analysis. + + LoopDescriptor* LD = context_->GetLoopDescriptor(&function_); + LD->PostModificationCleanup(); +} + +/* + * + * Begin Pass. + * + */ + +Pass::Status LoopUnroller::Process() { + bool changed = false; + for (Function& f : *context()->module()) { + if (f.IsDeclaration()) { + continue; + } + + LoopDescriptor* LD = context()->GetLoopDescriptor(&f); + for (Loop& loop : *LD) { + LoopUtils loop_utils{context(), &loop}; + if (!loop.HasUnrollLoopControl() || !loop_utils.CanPerformUnroll()) { + continue; + } + + if (fully_unroll_) { + loop_utils.FullyUnroll(); + } else { + loop_utils.PartiallyUnroll(unroll_factor_); + } + changed = true; + } + LD->PostModificationCleanup(); + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.h new file mode 100644 index 00000000..71e7cca3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unroller.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UNROLLER_H_ +#define SOURCE_OPT_LOOP_UNROLLER_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class LoopUnroller : public Pass { + public: + LoopUnroller() : Pass(), fully_unroll_(true), unroll_factor_(0) {} + LoopUnroller(bool fully_unroll, int unroll_factor) + : Pass(), fully_unroll_(fully_unroll), unroll_factor_(unroll_factor) {} + + const char* name() const override { return "loop-unroll"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + bool fully_unroll_; + int unroll_factor_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UNROLLER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.cpp new file mode 100644 index 00000000..41f1a804 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.cpp @@ -0,0 +1,614 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_unswitch_pass.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/fold.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kTypePointerStorageClassInIdx = 0; + +// This class handle the unswitch procedure for a given loop. +// The unswitch will not happen if: +// - The loop has any instruction that will prevent it; +// - The loop invariant condition is not uniform. +class LoopUnswitch { + public: + LoopUnswitch(IRContext* context, Function* function, Loop* loop, + LoopDescriptor* loop_desc) + : function_(function), + loop_(loop), + loop_desc_(*loop_desc), + context_(context), + switch_block_(nullptr) {} + + // Returns true if the loop can be unswitched. + // Can be unswitch if: + // - The loop has no instructions that prevents it (such as barrier); + // - The loop has one conditional branch or switch that do not depends on the + // loop; + // - The loop invariant condition is uniform; + bool CanUnswitchLoop() { + if (switch_block_) return true; + if (loop_->IsSafeToClone()) return false; + + CFG& cfg = *context_->cfg(); + + for (uint32_t bb_id : loop_->GetBlocks()) { + BasicBlock* bb = cfg.block(bb_id); + if (loop_->GetLatchBlock() == bb) { + continue; + } + + if (bb->terminator()->IsBranch() && + bb->terminator()->opcode() != spv::Op::OpBranch) { + if (IsConditionNonConstantLoopInvariant(bb->terminator())) { + switch_block_ = bb; + break; + } + } + } + + return switch_block_; + } + + // Return the iterator to the basic block |bb|. + Function::iterator FindBasicBlockPosition(BasicBlock* bb_to_find) { + Function::iterator it = function_->FindBlock(bb_to_find->id()); + assert(it != function_->end() && "Basic Block not found"); + return it; + } + + // Creates a new basic block and insert it into the function |fn| at the + // position |ip|. This function preserves the def/use and instr to block + // managers. + BasicBlock* CreateBasicBlock(Function::iterator ip) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + // TODO(1841): Handle id overflow. + BasicBlock* bb = &*ip.InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))))); + bb->SetParent(function_); + def_use_mgr->AnalyzeInstDef(bb->GetLabelInst()); + context_->set_instr_block(bb->GetLabelInst(), bb); + + return bb; + } + + Instruction* GetValueForDefaultPathForSwitch(Instruction* switch_inst) { + assert(switch_inst->opcode() == spv::Op::OpSwitch && + "The given instructoin must be an OpSwitch."); + + // Find a value that can be used to select the default path. + // If none are possible, then it will just use 0. The value does not matter + // because this path will never be taken because the new switch outside of + // the loop cannot select this path either. + std::vector existing_values; + for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) { + existing_values.push_back(switch_inst->GetSingleWordInOperand(i)); + } + std::sort(existing_values.begin(), existing_values.end()); + uint32_t value_for_default_path = 0; + if (existing_values.size() < std::numeric_limits::max()) { + for (value_for_default_path = 0; + value_for_default_path < existing_values.size(); + value_for_default_path++) { + if (existing_values[value_for_default_path] != value_for_default_path) { + break; + } + } + } + InstructionBuilder builder( + context_, static_cast(nullptr), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.GetUintConstant(value_for_default_path); + } + + // Unswitches |loop_|. + void PerformUnswitch() { + assert(CanUnswitchLoop() && + "Cannot unswitch if there is not constant condition"); + assert(loop_->GetPreHeaderBlock() && "This loop has no pre-header block"); + assert(loop_->IsLCSSA() && "This loop is not in LCSSA form"); + + CFG& cfg = *context_->cfg(); + DominatorTree* dom_tree = + &context_->GetDominatorAnalysis(function_)->GetDomTree(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + LoopUtils loop_utils(context_, loop_); + + ////////////////////////////////////////////////////////////////////////////// + // Step 1: Create the if merge block for structured modules. + // To do so, the |loop_| merge block will become the if's one and we + // create a merge for the loop. This will limit the amount of duplicated + // code the structured control flow imposes. + // For non structured program, the new loop will be connected to + // the old loop's exit blocks. + ////////////////////////////////////////////////////////////////////////////// + + // Get the merge block if it exists. + BasicBlock* if_merge_block = loop_->GetMergeBlock(); + // The merge block is only created if the loop has a unique exit block. We + // have this guarantee for structured loops, for compute loop it will + // trivially help maintain both a structured-like form and LCSAA. + BasicBlock* loop_merge_block = + if_merge_block + ? CreateBasicBlock(FindBasicBlockPosition(if_merge_block)) + : nullptr; + if (loop_merge_block) { + // Add the instruction and update managers. + InstructionBuilder builder( + context_, loop_merge_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + builder.AddBranch(if_merge_block->id()); + builder.SetInsertPoint(&*loop_merge_block->begin()); + cfg.RegisterBlock(loop_merge_block); + def_use_mgr->AnalyzeInstDef(loop_merge_block->GetLabelInst()); + // Update CFG. + if_merge_block->ForEachPhiInst( + [loop_merge_block, &builder, this](Instruction* phi) { + Instruction* cloned = phi->Clone(context_); + cloned->SetResultId(TakeNextId()); + builder.AddInstruction(std::unique_ptr(cloned)); + phi->SetInOperand(0, {cloned->result_id()}); + phi->SetInOperand(1, {loop_merge_block->id()}); + for (uint32_t j = phi->NumInOperands() - 1; j > 1; j--) + phi->RemoveInOperand(j); + }); + // Copy the predecessor list (will get invalidated otherwise). + std::vector preds = cfg.preds(if_merge_block->id()); + for (uint32_t pid : preds) { + if (pid == loop_merge_block->id()) continue; + BasicBlock* p_bb = cfg.block(pid); + p_bb->ForEachSuccessorLabel( + [if_merge_block, loop_merge_block](uint32_t* id) { + if (*id == if_merge_block->id()) *id = loop_merge_block->id(); + }); + cfg.AddEdge(pid, loop_merge_block->id()); + } + cfg.RemoveNonExistingEdges(if_merge_block->id()); + // Update loop descriptor. + if (Loop* ploop = loop_->GetParent()) { + ploop->AddBasicBlock(loop_merge_block); + loop_desc_.SetBasicBlockToLoop(loop_merge_block->id(), ploop); + } + // Update the dominator tree. + DominatorTreeNode* loop_merge_dtn = + dom_tree->GetOrInsertNode(loop_merge_block); + DominatorTreeNode* if_merge_block_dtn = + dom_tree->GetOrInsertNode(if_merge_block); + loop_merge_dtn->parent_ = if_merge_block_dtn->parent_; + loop_merge_dtn->children_.push_back(if_merge_block_dtn); + loop_merge_dtn->parent_->children_.push_back(loop_merge_dtn); + if_merge_block_dtn->parent_->children_.erase(std::find( + if_merge_block_dtn->parent_->children_.begin(), + if_merge_block_dtn->parent_->children_.end(), if_merge_block_dtn)); + + loop_->SetMergeBlock(loop_merge_block); + } + + //////////////////////////////////////////////////////////////////////////// + // Step 2: Build a new preheader for |loop_|, use the old one + // for the invariant branch. + //////////////////////////////////////////////////////////////////////////// + + BasicBlock* if_block = loop_->GetPreHeaderBlock(); + // If this preheader is the parent loop header, + // we need to create a dedicated block for the if. + BasicBlock* loop_pre_header = + CreateBasicBlock(++FindBasicBlockPosition(if_block)); + InstructionBuilder( + context_, loop_pre_header, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .AddBranch(loop_->GetHeaderBlock()->id()); + + if_block->tail()->SetInOperand(0, {loop_pre_header->id()}); + + // Update loop descriptor. + if (Loop* ploop = loop_desc_[if_block]) { + ploop->AddBasicBlock(loop_pre_header); + loop_desc_.SetBasicBlockToLoop(loop_pre_header->id(), ploop); + } + + // Update the CFG. + cfg.RegisterBlock(loop_pre_header); + def_use_mgr->AnalyzeInstDef(loop_pre_header->GetLabelInst()); + cfg.AddEdge(if_block->id(), loop_pre_header->id()); + cfg.RemoveNonExistingEdges(loop_->GetHeaderBlock()->id()); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [loop_pre_header, if_block](Instruction* phi) { + phi->ForEachInId([loop_pre_header, if_block](uint32_t* id) { + if (*id == if_block->id()) { + *id = loop_pre_header->id(); + } + }); + }); + loop_->SetPreHeaderBlock(loop_pre_header); + + // Update the dominator tree. + DominatorTreeNode* loop_pre_header_dtn = + dom_tree->GetOrInsertNode(loop_pre_header); + DominatorTreeNode* if_block_dtn = dom_tree->GetTreeNode(if_block); + loop_pre_header_dtn->parent_ = if_block_dtn; + assert( + if_block_dtn->children_.size() == 1 && + "A loop preheader should only have the header block as a child in the " + "dominator tree"); + loop_pre_header_dtn->children_.push_back(if_block_dtn->children_[0]); + if_block_dtn->children_.clear(); + if_block_dtn->children_.push_back(loop_pre_header_dtn); + + // Make domination queries valid. + dom_tree->ResetDFNumbering(); + + // Compute an ordered list of basic block to clone: loop blocks + pre-header + // + merge block. + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks_, true, true); + + ///////////////////////////// + // Do the actual unswitch: // + // - Clone the loop // + // - Connect exits // + // - Specialize the loop // + ///////////////////////////// + + Instruction* iv_condition = &*switch_block_->tail(); + spv::Op iv_opcode = iv_condition->opcode(); + Instruction* condition = + def_use_mgr->GetDef(iv_condition->GetOperand(0).words[0]); + + analysis::ConstantManager* cst_mgr = context_->get_constant_mgr(); + const analysis::Type* cond_type = + context_->get_type_mgr()->GetType(condition->type_id()); + + // Build the list of value for which we need to clone and specialize the + // loop. + std::vector> constant_branch; + // Special case for the original loop + Instruction* original_loop_constant_value; + if (iv_opcode == spv::Op::OpBranchConditional) { + constant_branch.emplace_back( + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant(cond_type, {0})), + nullptr); + original_loop_constant_value = + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant(cond_type, {1})); + } else { + // We are looking to take the default branch, so we can't provide a + // specific value. + original_loop_constant_value = + GetValueForDefaultPathForSwitch(iv_condition); + + for (uint32_t i = 2; i < iv_condition->NumInOperands(); i += 2) { + constant_branch.emplace_back( + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant( + cond_type, iv_condition->GetInOperand(i).words)), + nullptr); + } + } + + // Get the loop landing pads. + std::unordered_set if_merging_blocks; + std::function is_from_original_loop; + if (loop_->GetHeaderBlock()->GetLoopMergeInst()) { + if_merging_blocks.insert(if_merge_block->id()); + is_from_original_loop = [this](uint32_t id) { + return loop_->IsInsideLoop(id) || loop_->GetMergeBlock()->id() == id; + }; + } else { + loop_->GetExitBlocks(&if_merging_blocks); + is_from_original_loop = [this](uint32_t id) { + return loop_->IsInsideLoop(id); + }; + } + + for (auto& specialisation_pair : constant_branch) { + Instruction* specialisation_value = specialisation_pair.first; + ////////////////////////////////////////////////////////// + // Step 3: Duplicate |loop_|. + ////////////////////////////////////////////////////////// + LoopUtils::LoopCloningResult clone_result; + + Loop* cloned_loop = + loop_utils.CloneLoop(&clone_result, ordered_loop_blocks_); + specialisation_pair.second = cloned_loop->GetPreHeaderBlock(); + + //////////////////////////////////// + // Step 4: Specialize the loop. // + //////////////////////////////////// + + { + SpecializeLoop(cloned_loop, condition, specialisation_value); + + /////////////////////////////////////////////////////////// + // Step 5: Connect convergent edges to the landing pads. // + /////////////////////////////////////////////////////////// + + for (uint32_t merge_bb_id : if_merging_blocks) { + BasicBlock* merge = context_->cfg()->block(merge_bb_id); + // We are in LCSSA so we only care about phi instructions. + merge->ForEachPhiInst( + [is_from_original_loop, &clone_result](Instruction* phi) { + uint32_t num_in_operands = phi->NumInOperands(); + for (uint32_t i = 0; i < num_in_operands; i += 2) { + uint32_t pred = phi->GetSingleWordInOperand(i + 1); + if (is_from_original_loop(pred)) { + pred = clone_result.value_map_.at(pred); + uint32_t incoming_value_id = phi->GetSingleWordInOperand(i); + // Not all the incoming values are coming from the loop. + ValueMapTy::iterator new_value = + clone_result.value_map_.find(incoming_value_id); + if (new_value != clone_result.value_map_.end()) { + incoming_value_id = new_value->second; + } + phi->AddOperand({SPV_OPERAND_TYPE_ID, {incoming_value_id}}); + phi->AddOperand({SPV_OPERAND_TYPE_ID, {pred}}); + } + } + }); + } + } + function_->AddBasicBlocks(clone_result.cloned_bb_.begin(), + clone_result.cloned_bb_.end(), + ++FindBasicBlockPosition(if_block)); + } + + // Specialize the existing loop. + SpecializeLoop(loop_, condition, original_loop_constant_value); + BasicBlock* original_loop_target = loop_->GetPreHeaderBlock(); + + ///////////////////////////////////// + // Finally: connect the new loops. // + ///////////////////////////////////// + + // Delete the old jump + context_->KillInst(&*if_block->tail()); + InstructionBuilder builder(context_, if_block); + if (iv_opcode == spv::Op::OpBranchConditional) { + assert(constant_branch.size() == 1); + builder.AddConditionalBranch( + condition->result_id(), original_loop_target->id(), + constant_branch[0].second->id(), + if_merge_block ? if_merge_block->id() : kInvalidId); + } else { + std::vector> targets; + for (auto& t : constant_branch) { + targets.emplace_back(t.first->GetInOperand(0).words, t.second->id()); + } + + builder.AddSwitch(condition->result_id(), original_loop_target->id(), + targets, + if_merge_block ? if_merge_block->id() : kInvalidId); + } + + switch_block_ = nullptr; + ordered_loop_blocks_.clear(); + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + } + + private: + using ValueMapTy = std::unordered_map; + using BlockMapTy = std::unordered_map; + + Function* function_; + Loop* loop_; + LoopDescriptor& loop_desc_; + IRContext* context_; + + BasicBlock* switch_block_; + // Map between instructions and if they are dynamically uniform. + std::unordered_map dynamically_uniform_; + // The loop basic blocks in structured order. + std::vector ordered_loop_blocks_; + + // Returns the next usable id for the context. + uint32_t TakeNextId() { + // TODO(1841): Handle id overflow. + return context_->TakeNextId(); + } + + // Simplifies |loop| assuming the instruction |to_version_insn| takes the + // value |cst_value|. |block_range| is an iterator range returning the loop + // basic blocks in a structured order (dominator first). + // The function will ignore basic blocks returned by |block_range| if they + // does not belong to the loop. + // The set |dead_blocks| will contain all the dead basic blocks. + // + // Requirements: + // - |loop| must be in the LCSSA form; + // - |cst_value| must be constant. + void SpecializeLoop(Loop* loop, Instruction* to_version_insn, + Instruction* cst_value) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::function ignore_node; + ignore_node = [loop](uint32_t bb_id) { return !loop->IsInsideLoop(bb_id); }; + + std::vector> use_list; + def_use_mgr->ForEachUse(to_version_insn, + [&use_list, &ignore_node, this]( + Instruction* inst, uint32_t operand_index) { + BasicBlock* bb = context_->get_instr_block(inst); + + if (!bb || ignore_node(bb->id())) { + // Out of the loop, the specialization does not + // apply any more. + return; + } + use_list.emplace_back(inst, operand_index); + }); + + // First pass: inject the specialized value into the loop (and only the + // loop). + for (auto use : use_list) { + Instruction* inst = use.first; + uint32_t operand_index = use.second; + + // To also handle switch, cst_value can be nullptr: this case + // means that we are looking to branch to the default target of + // the switch. We don't actually know its value so we don't touch + // it if it not a switch. + assert(cst_value && "We do not have a value to use."); + inst->SetOperand(operand_index, {cst_value->result_id()}); + def_use_mgr->AnalyzeInstUse(inst); + } + } + + // Returns true if |var| is dynamically uniform. + // Note: this is currently approximated as uniform. + bool IsDynamicallyUniform(Instruction* var, const BasicBlock* entry, + const DominatorTree& post_dom_tree) { + assert(post_dom_tree.IsPostDominator()); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + auto it = dynamically_uniform_.find(var->result_id()); + + if (it != dynamically_uniform_.end()) return it->second; + + analysis::DecorationManager* dec_mgr = context_->get_decoration_mgr(); + + bool& is_uniform = dynamically_uniform_[var->result_id()]; + is_uniform = false; + + dec_mgr->WhileEachDecoration(var->result_id(), + uint32_t(spv::Decoration::Uniform), + [&is_uniform](const Instruction&) { + is_uniform = true; + return false; + }); + if (is_uniform) { + return is_uniform; + } + + BasicBlock* parent = context_->get_instr_block(var); + if (!parent) { + return is_uniform = true; + } + + if (!post_dom_tree.Dominates(parent->id(), entry->id())) { + return is_uniform = false; + } + if (var->opcode() == spv::Op::OpLoad) { + const uint32_t PtrTypeId = + def_use_mgr->GetDef(var->GetSingleWordInOperand(0))->type_id(); + const Instruction* PtrTypeInst = def_use_mgr->GetDef(PtrTypeId); + auto storage_class = spv::StorageClass( + PtrTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx)); + if (storage_class != spv::StorageClass::Uniform && + storage_class != spv::StorageClass::UniformConstant) { + return is_uniform = false; + } + } else { + if (!context_->IsCombinatorInstruction(var)) { + return is_uniform = false; + } + } + + return is_uniform = var->WhileEachInId([entry, &post_dom_tree, + this](const uint32_t* id) { + return IsDynamicallyUniform(context_->get_def_use_mgr()->GetDef(*id), + entry, post_dom_tree); + }); + } + + // Returns true if |insn| is not a constant, but is loop invariant and + // dynamically uniform. + bool IsConditionNonConstantLoopInvariant(Instruction* insn) { + assert(insn->IsBranch()); + assert(insn->opcode() != spv::Op::OpBranch); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + Instruction* condition = def_use_mgr->GetDef(insn->GetOperand(0).words[0]); + if (condition->IsConstant()) { + return false; + } + + if (loop_->IsInsideLoop(condition)) { + return false; + } + + return IsDynamicallyUniform( + condition, function_->entry().get(), + context_->GetPostDominatorAnalysis(function_)->GetDomTree()); + } +}; + +} // namespace + +Pass::Status LoopUnswitchPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopUnswitchPass::ProcessFunction(Function* f) { + bool modified = false; + std::unordered_set processed_loop; + + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f); + + bool loop_changed = true; + while (loop_changed) { + loop_changed = false; + for (Loop& loop : make_range( + ++TreeDFIterator(loop_descriptor.GetPlaceholderRootLoop()), + TreeDFIterator())) { + if (processed_loop.count(&loop)) continue; + processed_loop.insert(&loop); + + LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor); + while (unswitcher.CanUnswitchLoop()) { + if (!loop.IsLCSSA()) { + LoopUtils(context(), &loop).MakeLoopClosedSSA(); + } + modified = true; + loop_changed = true; + unswitcher.PerformUnswitch(); + } + if (loop_changed) break; + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.h new file mode 100644 index 00000000..4f7295d4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_unswitch_pass.h @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ +#define SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ + +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Implements the loop unswitch optimization. +// The loop unswitch hoists invariant "if" statements if the conditions are +// constant within the loop and clones the loop for each branch. +class LoopUnswitchPass : public Pass { + public: + const char* name() const override { return "loop-unswitch"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes have been made to the module. + Pass::Status Process() override; + + private: + bool ProcessFunction(Function* f); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.cpp new file mode 100644 index 00000000..20494e12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.cpp @@ -0,0 +1,693 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/cfg.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" + +namespace spvtools { +namespace opt { +namespace { +// Return true if |bb| is dominated by at least one block in |exits| +inline bool DominatesAnExit(BasicBlock* bb, + const std::unordered_set& exits, + const DominatorTree& dom_tree) { + for (BasicBlock* e_bb : exits) + if (dom_tree.Dominates(bb, e_bb)) return true; + return false; +} + +// Utility class to rewrite out-of-loop uses of an in-loop definition in terms +// of phi instructions to achieve a LCSSA form. +// For a given definition, the class user registers phi instructions using that +// definition in all loop exit blocks by which the definition escapes. +// Then, when rewriting a use of the definition, the rewriter walks the +// paths from the use the loop exits. At each step, it will insert a phi +// instruction to merge the incoming value according to exit blocks definition. +class LCSSARewriter { + public: + LCSSARewriter(IRContext* context, const DominatorTree& dom_tree, + const std::unordered_set& exit_bb, + BasicBlock* merge_block) + : context_(context), + cfg_(context_->cfg()), + dom_tree_(dom_tree), + exit_bb_(exit_bb), + merge_block_id_(merge_block ? merge_block->id() : 0) {} + + struct UseRewriter { + explicit UseRewriter(LCSSARewriter* base, const Instruction& def_insn) + : base_(base), def_insn_(def_insn) {} + // Rewrites the use of |def_insn_| by the instruction |user| at the index + // |operand_index| in terms of phi instruction. This recursively builds new + // phi instructions from |user| to the loop exit blocks' phis. The use of + // |def_insn_| in |user| is replaced by the relevant phi instruction at the + // end of the operation. + // It is assumed that |user| does not dominates any of the loop exit basic + // block. This operation does not update the def/use manager, instead it + // records what needs to be updated. The actual update is performed by + // UpdateManagers. + void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) { + assert( + (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) && + "The root basic block must be the incoming edge if |user| is a phi " + "instruction"); + assert((user->opcode() == spv::Op::OpPhi || bb == GetParent(user)) && + "The root basic block must be the instruction parent if |user| is " + "not " + "phi instruction"); + + Instruction* new_def = GetOrBuildIncoming(bb->id()); + + user->SetOperand(operand_index, {new_def->result_id()}); + rewritten_.insert(user); + } + + // In-place update of some managers (avoid full invalidation). + inline void UpdateManagers() { + analysis::DefUseManager* def_use_mgr = base_->context_->get_def_use_mgr(); + // Register all new definitions. + for (Instruction* insn : rewritten_) { + def_use_mgr->AnalyzeInstDef(insn); + } + // Register all new uses. + for (Instruction* insn : rewritten_) { + def_use_mgr->AnalyzeInstUse(insn); + } + } + + private: + // Return the basic block that |instr| belongs to. + BasicBlock* GetParent(Instruction* instr) { + return base_->context_->get_instr_block(instr); + } + + // Builds a phi instruction for the basic block |bb|. The function assumes + // that |defining_blocks| contains the list of basic block that define the + // usable value for each predecessor of |bb|. + inline Instruction* CreatePhiInstruction( + BasicBlock* bb, const std::vector& defining_blocks) { + std::vector incomings; + const std::vector& bb_preds = base_->cfg_->preds(bb->id()); + assert(bb_preds.size() == defining_blocks.size()); + for (size_t i = 0; i < bb_preds.size(); i++) { + incomings.push_back( + GetOrBuildIncoming(defining_blocks[i])->result_id()); + incomings.push_back(bb_preds[i]); + } + InstructionBuilder builder(base_->context_, &*bb->begin(), + IRContext::kAnalysisInstrToBlockMapping); + Instruction* incoming_phi = + builder.AddPhi(def_insn_.type_id(), incomings); + + rewritten_.insert(incoming_phi); + return incoming_phi; + } + + // Builds a phi instruction for the basic block |bb|, all incoming values + // will be |value|. + inline Instruction* CreatePhiInstruction(BasicBlock* bb, + const Instruction& value) { + std::vector incomings; + const std::vector& bb_preds = base_->cfg_->preds(bb->id()); + for (size_t i = 0; i < bb_preds.size(); i++) { + incomings.push_back(value.result_id()); + incomings.push_back(bb_preds[i]); + } + InstructionBuilder builder(base_->context_, &*bb->begin(), + IRContext::kAnalysisInstrToBlockMapping); + Instruction* incoming_phi = + builder.AddPhi(def_insn_.type_id(), incomings); + + rewritten_.insert(incoming_phi); + return incoming_phi; + } + + // Return the new def to use for the basic block |bb_id|. + // If |bb_id| does not have a suitable def to use then we: + // - return the common def used by all predecessors; + // - if there is no common def, then we build a new phi instr at the + // beginning of |bb_id| and return this new instruction. + Instruction* GetOrBuildIncoming(uint32_t bb_id) { + assert(base_->cfg_->block(bb_id) != nullptr && "Unknown basic block"); + + Instruction*& incoming_phi = bb_to_phi_[bb_id]; + if (incoming_phi) { + return incoming_phi; + } + + BasicBlock* bb = &*base_->cfg_->block(bb_id); + // If this is an exit basic block, look if there already is an eligible + // phi instruction. An eligible phi has |def_insn_| as all incoming + // values. + if (base_->exit_bb_.count(bb)) { + // Look if there is an eligible phi in this block. + if (!bb->WhileEachPhiInst([&incoming_phi, this](Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (phi->GetSingleWordInOperand(i) != def_insn_.result_id()) + return true; + } + incoming_phi = phi; + rewritten_.insert(incoming_phi); + return false; + })) { + return incoming_phi; + } + incoming_phi = CreatePhiInstruction(bb, def_insn_); + return incoming_phi; + } + + // Get the block that defines the value to use for each predecessor. + // If the vector has 1 value, then it means that this block does not need + // to build a phi instruction unless |bb_id| is the loop merge block. + const std::vector& defining_blocks = + base_->GetDefiningBlocks(bb_id); + + // Special case for structured loops: merge block might be different from + // the exit block set. To maintain structured properties it will ease + // transformations if the merge block also holds a phi instruction like + // the exit ones. + if (defining_blocks.size() > 1 || bb_id == base_->merge_block_id_) { + if (defining_blocks.size() > 1) { + incoming_phi = CreatePhiInstruction(bb, defining_blocks); + } else { + assert(bb_id == base_->merge_block_id_); + incoming_phi = + CreatePhiInstruction(bb, *GetOrBuildIncoming(defining_blocks[0])); + } + } else { + incoming_phi = GetOrBuildIncoming(defining_blocks[0]); + } + + return incoming_phi; + } + + LCSSARewriter* base_; + const Instruction& def_insn_; + std::unordered_map bb_to_phi_; + std::unordered_set rewritten_; + }; + + private: + // Return the new def to use for the basic block |bb_id|. + // If |bb_id| does not have a suitable def to use then we: + // - return the common def used by all predecessors; + // - if there is no common def, then we build a new phi instr at the + // beginning of |bb_id| and return this new instruction. + const std::vector& GetDefiningBlocks(uint32_t bb_id) { + assert(cfg_->block(bb_id) != nullptr && "Unknown basic block"); + std::vector& defining_blocks = bb_to_defining_blocks_[bb_id]; + + if (defining_blocks.size()) return defining_blocks; + + // Check if one of the loop exit basic block dominates |bb_id|. + for (const BasicBlock* e_bb : exit_bb_) { + if (dom_tree_.Dominates(e_bb->id(), bb_id)) { + defining_blocks.push_back(e_bb->id()); + return defining_blocks; + } + } + + // Process parents, they will returns their suitable blocks. + // If they are all the same, this means this basic block is dominated by a + // common block, so we won't need to build a phi instruction. + for (uint32_t pred_id : cfg_->preds(bb_id)) { + const std::vector& pred_blocks = GetDefiningBlocks(pred_id); + if (pred_blocks.size() == 1) + defining_blocks.push_back(pred_blocks[0]); + else + defining_blocks.push_back(pred_id); + } + assert(defining_blocks.size()); + if (std::all_of(defining_blocks.begin(), defining_blocks.end(), + [&defining_blocks](uint32_t id) { + return id == defining_blocks[0]; + })) { + // No need for a phi. + defining_blocks.resize(1); + } + + return defining_blocks; + } + + IRContext* context_; + CFG* cfg_; + const DominatorTree& dom_tree_; + const std::unordered_set& exit_bb_; + uint32_t merge_block_id_; + // This map represent the set of known paths. For each key, the vector + // represent the set of blocks holding the definition to be used to build the + // phi instruction. + // If the vector has 0 value, then the path is unknown yet, and must be built. + // If the vector has 1 value, then the value defined by that basic block + // should be used. + // If the vector has more than 1 value, then a phi node must be created, the + // basic block ordering is the same as the predecessor ordering. + std::unordered_map> bb_to_defining_blocks_; +}; + +// Make the set |blocks| closed SSA. The set is closed SSA if all the uses +// outside the set are phi instructions in exiting basic block set (hold by +// |lcssa_rewriter|). +inline void MakeSetClosedSSA(IRContext* context, Function* function, + const std::unordered_set& blocks, + const std::unordered_set& exit_bb, + LCSSARewriter* lcssa_rewriter) { + CFG& cfg = *context->cfg(); + DominatorTree& dom_tree = + context->GetDominatorAnalysis(function)->GetDomTree(); + analysis::DefUseManager* def_use_manager = context->get_def_use_mgr(); + + for (uint32_t bb_id : blocks) { + BasicBlock* bb = cfg.block(bb_id); + // If bb does not dominate an exit block, then it cannot have escaping defs. + if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue; + for (Instruction& inst : *bb) { + LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst); + def_use_manager->ForEachUse( + &inst, [&blocks, &rewriter, &exit_bb, context]( + Instruction* use, uint32_t operand_index) { + BasicBlock* use_parent = context->get_instr_block(use); + assert(use_parent); + if (blocks.count(use_parent->id())) return; + + if (use->opcode() == spv::Op::OpPhi) { + // If the use is a Phi instruction and the incoming block is + // coming from the loop, then that's consistent with LCSSA form. + if (exit_bb.count(use_parent)) { + return; + } else { + // That's not an exit block, but the user is a phi instruction. + // Consider the incoming branch only. + use_parent = context->get_instr_block( + use->GetSingleWordOperand(operand_index + 1)); + } + } + // Rewrite the use. Note that this call does not invalidate the + // def/use manager. So this operation is safe. + rewriter.RewriteUse(use_parent, use, operand_index); + }); + rewriter.UpdateManagers(); + } + } +} + +} // namespace + +void LoopUtils::CreateLoopDedicatedExits() { + Function* function = loop_->GetHeaderBlock()->GetParent(); + LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function); + CFG& cfg = *context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + const IRContext::Analysis PreservedAnalyses = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + + // Gathers the set of basic block that are not in this loop and have at least + // one predecessor in the loop and one not in the loop. + std::unordered_set exit_bb_set; + loop_->GetExitBlocks(&exit_bb_set); + + std::unordered_set new_loop_exits; + bool made_change = false; + // For each block, we create a new one that gathers all branches from + // the loop and fall into the block. + for (uint32_t non_dedicate_id : exit_bb_set) { + BasicBlock* non_dedicate = cfg.block(non_dedicate_id); + const std::vector& bb_pred = cfg.preds(non_dedicate_id); + // Ignore the block if all the predecessors are in the loop. + if (std::all_of(bb_pred.begin(), bb_pred.end(), + [this](uint32_t id) { return loop_->IsInsideLoop(id); })) { + new_loop_exits.insert(non_dedicate); + continue; + } + + made_change = true; + Function::iterator insert_pt = function->begin(); + for (; insert_pt != function->end() && &*insert_pt != non_dedicate; + ++insert_pt) { + } + assert(insert_pt != function->end() && "Basic Block not found"); + + // Create the dedicate exit basic block. + // TODO(1841): Handle id overflow. + BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {}))))); + exit.SetParent(function); + + // Redirect in loop predecessors to |exit| block. + for (uint32_t exit_pred_id : bb_pred) { + if (loop_->IsInsideLoop(exit_pred_id)) { + BasicBlock* pred_block = cfg.block(exit_pred_id); + pred_block->ForEachSuccessorLabel([non_dedicate, &exit](uint32_t* id) { + if (*id == non_dedicate->id()) *id = exit.id(); + }); + // Update the CFG. + // |non_dedicate|'s predecessor list will be updated at the end of the + // loop. + cfg.RegisterBlock(pred_block); + } + } + + // Register the label to the def/use manager, requires for the phi patching. + def_use_mgr->AnalyzeInstDefUse(exit.GetLabelInst()); + context_->set_instr_block(exit.GetLabelInst(), &exit); + + InstructionBuilder builder(context_, &exit, PreservedAnalyses); + // Now jump from our dedicate basic block to the old exit. + // We also reset the insert point so all instructions are inserted before + // the branch. + builder.SetInsertPoint(builder.AddBranch(non_dedicate->id())); + non_dedicate->ForEachPhiInst( + [&builder, &exit, def_use_mgr, this](Instruction* phi) { + // New phi operands for this instruction. + std::vector new_phi_op; + // Phi operands for the dedicated exit block. + std::vector exit_phi_op; + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t def_id = phi->GetSingleWordInOperand(i); + uint32_t incoming_id = phi->GetSingleWordInOperand(i + 1); + if (loop_->IsInsideLoop(incoming_id)) { + exit_phi_op.push_back(def_id); + exit_phi_op.push_back(incoming_id); + } else { + new_phi_op.push_back(def_id); + new_phi_op.push_back(incoming_id); + } + } + + // Build the new phi instruction dedicated exit block. + Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op); + // Build the new incoming branch. + new_phi_op.push_back(exit_phi->result_id()); + new_phi_op.push_back(exit.id()); + // Rewrite operands. + uint32_t idx = 0; + for (; idx < new_phi_op.size(); idx++) + phi->SetInOperand(idx, {new_phi_op[idx]}); + // Remove extra operands, from last to first (more efficient). + for (uint32_t j = phi->NumInOperands() - 1; j >= idx; j--) + phi->RemoveInOperand(j); + // Update the def/use manager for this |phi|. + def_use_mgr->AnalyzeInstUse(phi); + }); + // Update the CFG. + cfg.RegisterBlock(&exit); + cfg.RemoveNonExistingEdges(non_dedicate->id()); + new_loop_exits.insert(&exit); + // If non_dedicate is in a loop, add the new dedicated exit in that loop. + if (Loop* parent_loop = loop_desc[non_dedicate]) + parent_loop->AddBasicBlock(&exit); + } + + if (new_loop_exits.size() == 1) { + loop_->SetMergeBlock(*new_loop_exits.begin()); + } + + if (made_change) { + context_->InvalidateAnalysesExceptFor( + PreservedAnalyses | IRContext::kAnalysisCFG | + IRContext::Analysis::kAnalysisLoopAnalysis); + } +} + +void LoopUtils::MakeLoopClosedSSA() { + CreateLoopDedicatedExits(); + + Function* function = loop_->GetHeaderBlock()->GetParent(); + CFG& cfg = *context_->cfg(); + DominatorTree& dom_tree = + context_->GetDominatorAnalysis(function)->GetDomTree(); + + std::unordered_set exit_bb; + { + std::unordered_set exit_bb_id; + loop_->GetExitBlocks(&exit_bb_id); + for (uint32_t bb_id : exit_bb_id) { + exit_bb.insert(cfg.block(bb_id)); + } + } + + LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb, + loop_->GetMergeBlock()); + MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb, + &lcssa_rewriter); + + // Make sure all defs post-dominated by the merge block have their last use no + // further than the merge block. + if (loop_->GetMergeBlock()) { + std::unordered_set merging_bb_id; + loop_->GetMergingBlocks(&merging_bb_id); + merging_bb_id.erase(loop_->GetMergeBlock()->id()); + // Reset the exit set, now only the merge block is the exit. + exit_bb.clear(); + exit_bb.insert(loop_->GetMergeBlock()); + // LCSSARewriter is reusable here only because it forces the creation of a + // phi instruction in the merge block. + MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb, + &lcssa_rewriter); + } + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisCFG | + IRContext::Analysis::kAnalysisDominatorAnalysis | + IRContext::Analysis::kAnalysisLoopAnalysis); +} + +Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const { + // Compute the structured order of the loop basic blocks and store it in the + // vector ordered_loop_blocks. + std::vector ordered_loop_blocks; + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks); + + // Clone the loop. + return CloneLoop(cloning_result, ordered_loop_blocks); +} + +Loop* LoopUtils::CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result) { + // Clone the loop. + Loop* new_loop = CloneLoop(cloning_result); + + // Create a new exit block/label for the new loop. + // TODO(1841): Handle id overflow. + std::unique_ptr new_label{new Instruction( + context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})}; + std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent()); + + // Create an unconditional branch to the header block. + InstructionBuilder builder{context_, new_exit_bb.get()}; + builder.AddBranch(loop_->GetHeaderBlock()->id()); + + // Save the ids of the new and old merge block. + const uint32_t old_merge_block = loop_->GetMergeBlock()->id(); + const uint32_t new_merge_block = new_exit_bb->id(); + + // Replace the uses of the old merge block in the new loop with the new merge + // block. + for (std::unique_ptr& basic_block : cloning_result->cloned_bb_) { + for (Instruction& inst : *basic_block) { + // For each operand in each instruction check if it is using the old merge + // block and change it to be the new merge block. + auto replace_merge_use = [old_merge_block, + new_merge_block](uint32_t* id) { + if (*id == old_merge_block) *id = new_merge_block; + }; + inst.ForEachInOperand(replace_merge_use); + } + } + + const uint32_t old_header = loop_->GetHeaderBlock()->id(); + const uint32_t new_header = new_loop->GetHeaderBlock()->id(); + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + def_use->ForEachUse(old_header, + [new_header, this](Instruction* inst, uint32_t operand) { + if (!this->loop_->IsInsideLoop(inst)) + inst->SetOperand(operand, {new_header}); + }); + + // TODO(1841): Handle failure to create pre-header. + def_use->ForEachUse( + loop_->GetOrCreatePreHeaderBlock()->id(), + [new_merge_block, this](Instruction* inst, uint32_t operand) { + if (this->loop_->IsInsideLoop(inst)) + inst->SetOperand(operand, {new_merge_block}); + + }); + new_loop->SetMergeBlock(new_exit_bb.get()); + + new_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock()); + + // Add the new block into the cloned instructions. + cloning_result->cloned_bb_.push_back(std::move(new_exit_bb)); + + return new_loop; +} + +Loop* LoopUtils::CloneLoop( + LoopCloningResult* cloning_result, + const std::vector& ordered_loop_blocks) const { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::unique_ptr new_loop = MakeUnique(context_); + + CFG& cfg = *context_->cfg(); + + // Clone and place blocks in a SPIR-V compliant order (dominators first). + for (BasicBlock* old_bb : ordered_loop_blocks) { + // For each basic block in the loop, we clone it and register the mapping + // between old and new ids. + BasicBlock* new_bb = old_bb->Clone(context_); + new_bb->SetParent(&function_); + // TODO(1841): Handle id overflow. + new_bb->GetLabelInst()->SetResultId(context_->TakeNextId()); + def_use_mgr->AnalyzeInstDef(new_bb->GetLabelInst()); + context_->set_instr_block(new_bb->GetLabelInst(), new_bb); + cloning_result->cloned_bb_.emplace_back(new_bb); + + cloning_result->old_to_new_bb_[old_bb->id()] = new_bb; + cloning_result->new_to_old_bb_[new_bb->id()] = old_bb; + cloning_result->value_map_[old_bb->id()] = new_bb->id(); + + if (loop_->IsInsideLoop(old_bb)) new_loop->AddBasicBlock(new_bb); + + for (auto new_inst = new_bb->begin(), old_inst = old_bb->begin(); + new_inst != new_bb->end(); ++new_inst, ++old_inst) { + cloning_result->ptr_map_[&*new_inst] = &*old_inst; + if (new_inst->HasResultId()) { + // TODO(1841): Handle id overflow. + new_inst->SetResultId(context_->TakeNextId()); + cloning_result->value_map_[old_inst->result_id()] = + new_inst->result_id(); + + // Only look at the defs for now, uses are not updated yet. + def_use_mgr->AnalyzeInstDef(&*new_inst); + } + } + } + + // All instructions (including all labels) have been cloned, + // remap instruction operands id with the new ones. + for (std::unique_ptr& bb_ref : cloning_result->cloned_bb_) { + BasicBlock* bb = bb_ref.get(); + + for (Instruction& insn : *bb) { + insn.ForEachInId([cloning_result](uint32_t* old_id) { + // If the operand is defined in the loop, remap the id. + auto id_it = cloning_result->value_map_.find(*old_id); + if (id_it != cloning_result->value_map_.end()) { + *old_id = id_it->second; + } + }); + // Only look at what the instruction uses. All defs are register, so all + // should be fine now. + def_use_mgr->AnalyzeInstUse(&insn); + context_->set_instr_block(&insn, bb); + } + cfg.RegisterBlock(bb); + } + + PopulateLoopNest(new_loop.get(), *cloning_result); + + return new_loop.release(); +} + +void LoopUtils::PopulateLoopNest( + Loop* new_loop, const LoopCloningResult& cloning_result) const { + std::unordered_map loop_mapping; + loop_mapping[loop_] = new_loop; + + if (loop_->HasParent()) loop_->GetParent()->AddNestedLoop(new_loop); + PopulateLoopDesc(new_loop, loop_, cloning_result); + + for (Loop& sub_loop : + make_range(++TreeDFIterator(loop_), TreeDFIterator())) { + Loop* cloned = new Loop(context_); + if (Loop* parent = loop_mapping[sub_loop.GetParent()]) + parent->AddNestedLoop(cloned); + loop_mapping[&sub_loop] = cloned; + PopulateLoopDesc(cloned, &sub_loop, cloning_result); + } + + loop_desc_->AddLoopNest(std::unique_ptr(new_loop)); +} + +// Populates |new_loop| descriptor according to |old_loop|'s one. +void LoopUtils::PopulateLoopDesc( + Loop* new_loop, Loop* old_loop, + const LoopCloningResult& cloning_result) const { + for (uint32_t bb_id : old_loop->GetBlocks()) { + BasicBlock* bb = cloning_result.old_to_new_bb_.at(bb_id); + new_loop->AddBasicBlock(bb); + } + new_loop->SetHeaderBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetHeaderBlock()->id())); + if (old_loop->GetLatchBlock()) + new_loop->SetLatchBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetLatchBlock()->id())); + if (old_loop->GetContinueBlock()) + new_loop->SetContinueBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetContinueBlock()->id())); + if (old_loop->GetMergeBlock()) { + auto it = + cloning_result.old_to_new_bb_.find(old_loop->GetMergeBlock()->id()); + BasicBlock* bb = it != cloning_result.old_to_new_bb_.end() + ? it->second + : old_loop->GetMergeBlock(); + new_loop->SetMergeBlock(bb); + } + if (old_loop->GetPreHeaderBlock()) { + auto it = + cloning_result.old_to_new_bb_.find(old_loop->GetPreHeaderBlock()->id()); + if (it != cloning_result.old_to_new_bb_.end()) { + new_loop->SetPreHeaderBlock(it->second); + } + } +} + +// Class to gather some metrics about a region of interest. +void CodeMetrics::Analyze(const Loop& loop) { + CFG& cfg = *loop.GetContext()->cfg(); + + roi_size_ = 0; + block_sizes_.clear(); + + for (uint32_t id : loop.GetBlocks()) { + const BasicBlock* bb = cfg.block(id); + size_t bb_size = 0; + bb->ForEachInst([&bb_size](const Instruction* insn) { + if (insn->opcode() == spv::Op::OpLabel) return; + if (insn->IsNop()) return; + if (insn->opcode() == spv::Op::OpPhi) return; + bb_size++; + }); + block_sizes_[bb->id()] = bb_size; + roi_size_ += bb_size; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.h b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.h new file mode 100644 index 00000000..70060fc4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/loop_utils.h @@ -0,0 +1,182 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UTILS_H_ +#define SOURCE_OPT_LOOP_UTILS_H_ + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" + +namespace spvtools { + +namespace opt { + +// Class to gather some metrics about a Region Of Interest (ROI). +// So far it counts the number of instructions in a ROI (excluding debug +// and label instructions) per basic block and in total. +struct CodeMetrics { + void Analyze(const Loop& loop); + + // The number of instructions per basic block in the ROI. + std::unordered_map block_sizes_; + + // Number of instruction in the ROI. + size_t roi_size_; +}; + +// LoopUtils is used to encapsulte loop optimizations and from the passes which +// use them. Any pass which needs a loop optimization should do it through this +// or through a pass which is using this. +class LoopUtils { + public: + // Holds a auxiliary results of the loop cloning procedure. + struct LoopCloningResult { + using ValueMapTy = std::unordered_map; + using BlockMapTy = std::unordered_map; + using PtrMap = std::unordered_map; + + PtrMap ptr_map_; + + // Mapping between the original loop ids and the new one. + ValueMapTy value_map_; + // Mapping between original loop blocks to the cloned one. + BlockMapTy old_to_new_bb_; + // Mapping between the cloned loop blocks to original one. + BlockMapTy new_to_old_bb_; + // List of cloned basic block. + std::vector> cloned_bb_; + }; + + LoopUtils(IRContext* context, Loop* loop) + : context_(context), + loop_desc_( + context->GetLoopDescriptor(loop->GetHeaderBlock()->GetParent())), + loop_(loop), + function_(*loop_->GetHeaderBlock()->GetParent()) {} + + // The converts the current loop to loop closed SSA form. + // In the loop closed SSA, all loop exiting values go through a dedicated Phi + // instruction. For instance: + // + // for (...) { + // A1 = ... + // if (...) + // A2 = ... + // A = phi A1, A2 + // } + // ... = op A ... + // + // Becomes + // + // for (...) { + // A1 = ... + // if (...) + // A2 = ... + // A = phi A1, A2 + // } + // C = phi A + // ... = op C ... + // + // This makes some loop transformations (such as loop unswitch) simpler + // (removes the needs to take care of exiting variables). + void MakeLoopClosedSSA(); + + // Create dedicate exit basic block. This ensure all exit basic blocks has the + // loop as sole predecessors. + // By construction, structured control flow already has a dedicated exit + // block. + // Preserves: CFG, def/use and instruction to block mapping. + void CreateLoopDedicatedExits(); + + // Clone |loop_| and remap its instructions. Newly created blocks + // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to + // be inserted into a function. + // It is assumed that |ordered_loop_blocks| is compatible with the result of + // |Loop::ComputeLoopStructuredOrder|. If the preheader and merge block are in + // the list they will also be cloned. If not, the resulting loop will share + // them with the original loop. + // The function preserves the def/use, cfg and instr to block analyses. + // The cloned loop nest will be added to the loop descriptor and will have + // ownership. + Loop* CloneLoop(LoopCloningResult* cloning_result, + const std::vector& ordered_loop_blocks) const; + // Clone |loop_| and remap its instructions, as above. Overload to compute + // loop block ordering within method rather than taking in as parameter. + Loop* CloneLoop(LoopCloningResult* cloning_result) const; + + // Clone the |loop_| and make the new loop branch to the second loop on exit. + Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); + + // Perform a partial unroll of |loop| by given |factor|. This will copy the + // body of the loop |factor| times. So a |factor| of one would give a new loop + // with the original body plus one unrolled copy body. + bool PartiallyUnroll(size_t factor); + + // Fully unroll |loop|. + bool FullyUnroll(); + + // This function validates that |loop| meets the assumptions made by the + // implementation of the loop unroller. As the implementation accommodates + // more types of loops this function can reduce its checks. + // + // The conditions checked to ensure the loop can be unrolled are as follows: + // 1. That the loop is in structured order. + // 2. That the continue block is a branch to the header. + // 3. That the only phi used in the loop is the induction variable. + // TODO(stephen@codeplay.com): This is a temporary measure, after the loop is + // converted into LCSAA form and has a single entry and exit we can rewrite + // the other phis. + // 4. That this is an inner most loop, or that loops contained within this + // loop have already been fully unrolled. + // 5. That each instruction in the loop is only used within the loop. + // (Related to the above phi condition). + bool CanPerformUnroll(); + + // Maintains the loop descriptor object after the unroll functions have been + // called, otherwise the analysis should be invalidated. + void Finalize(); + + // Returns the context associate to |loop_|. + IRContext* GetContext() { return context_; } + // Returns the loop descriptor owning |loop_|. + LoopDescriptor* GetLoopDescriptor() { return loop_desc_; } + // Returns the loop on which the object operates on. + Loop* GetLoop() const { return loop_; } + // Returns the function that |loop_| belong to. + Function* GetFunction() const { return &function_; } + + private: + IRContext* context_; + LoopDescriptor* loop_desc_; + Loop* loop_; + Function& function_; + + // Populates the loop nest of |new_loop| according to |loop_| nest. + void PopulateLoopNest(Loop* new_loop, + const LoopCloningResult& cloning_result) const; + + // Populates |new_loop| descriptor according to |old_loop|'s one. + void PopulateLoopDesc(Loop* new_loop, Loop* old_loop, + const LoopCloningResult& cloning_result) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UTILS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.cpp new file mode 100644 index 00000000..65f45ec3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.cpp @@ -0,0 +1,515 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/mem_pass.h" + +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kCopyObjectOperandInIdx = 0; +constexpr uint32_t kTypePointerStorageClassInIdx = 0; +constexpr uint32_t kTypePointerTypeIdInIdx = 1; +} // namespace + +bool MemPass::IsBaseTargetType(const Instruction* typeInst) const { + switch (typeInst->opcode()) { + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeBool: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypePointer: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + return true; + default: + break; + } + return false; +} + +bool MemPass::IsTargetType(const Instruction* typeInst) const { + if (IsBaseTargetType(typeInst)) return true; + if (typeInst->opcode() == spv::Op::OpTypeArray) { + if (!IsTargetType( + get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)))) { + return false; + } + return true; + } + if (typeInst->opcode() != spv::Op::OpTypeStruct) return false; + // All struct members must be math type + return typeInst->WhileEachInId([this](const uint32_t* tid) { + Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid); + if (!IsTargetType(compTypeInst)) return false; + return true; + }); +} + +bool MemPass::IsNonPtrAccessChain(const spv::Op opcode) const { + return opcode == spv::Op::OpAccessChain || + opcode == spv::Op::OpInBoundsAccessChain; +} + +bool MemPass::IsPtr(uint32_t ptrId) { + uint32_t varId = ptrId; + Instruction* ptrInst = get_def_use_mgr()->GetDef(varId); + if (ptrInst->opcode() == spv::Op::OpFunction) { + // A function is not a pointer, but it's return type could be, which will + // erroneously lead to this function returning true later on + return false; + } + while (ptrInst->opcode() == spv::Op::OpCopyObject) { + varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx); + ptrInst = get_def_use_mgr()->GetDef(varId); + } + const spv::Op op = ptrInst->opcode(); + if (op == spv::Op::OpVariable || IsNonPtrAccessChain(op)) return true; + const uint32_t varTypeId = ptrInst->type_id(); + if (varTypeId == 0) return false; + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + return varTypeInst->opcode() == spv::Op::OpTypePointer; +} + +Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) { + *varId = ptrId; + Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId); + Instruction* varInst; + + if (ptrInst->opcode() == spv::Op::OpConstantNull) { + *varId = 0; + return ptrInst; + } + + if (ptrInst->opcode() != spv::Op::OpVariable && + ptrInst->opcode() != spv::Op::OpFunctionParameter) { + varInst = ptrInst->GetBaseAddress(); + } else { + varInst = ptrInst; + } + if (varInst->opcode() == spv::Op::OpVariable) { + *varId = varInst->result_id(); + } else { + *varId = 0; + } + + while (ptrInst->opcode() == spv::Op::OpCopyObject) { + uint32_t temp = ptrInst->GetSingleWordInOperand(0); + ptrInst = get_def_use_mgr()->GetDef(temp); + } + + return ptrInst; +} + +Instruction* MemPass::GetPtr(Instruction* ip, uint32_t* varId) { + assert(ip->opcode() == spv::Op::OpStore || ip->opcode() == spv::Op::OpLoad || + ip->opcode() == spv::Op::OpImageTexelPointer || + ip->IsAtomicWithLoad()); + + // All of these opcode place the pointer in position 0. + const uint32_t ptrId = ip->GetSingleWordInOperand(0); + return GetPtr(ptrId, varId); +} + +bool MemPass::HasOnlyNamesAndDecorates(uint32_t id) const { + return get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) { + spv::Op op = user->opcode(); + if (op != spv::Op::OpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +void MemPass::KillAllInsts(BasicBlock* bp, bool killLabel) { + bp->KillAllInsts(killLabel); +} + +bool MemPass::HasLoads(uint32_t varId) const { + return !get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { + spv::Op op = user->opcode(); + // TODO(): The following is slightly conservative. Could be + // better handling of non-store/name. + if (IsNonPtrAccessChain(op) || op == spv::Op::OpCopyObject) { + if (HasLoads(user->result_id())) { + return false; + } + } else if (op != spv::Op::OpStore && op != spv::Op::OpName && + !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +bool MemPass::IsLiveVar(uint32_t varId) const { + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + // assume live if not a variable eg. function parameter + if (varInst->opcode() != spv::Op::OpVariable) return true; + // non-function scope vars are live + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (spv::StorageClass(varTypeInst->GetSingleWordInOperand( + kTypePointerStorageClassInIdx)) != spv::StorageClass::Function) + return true; + // test if variable is loaded from + return HasLoads(varId); +} + +void MemPass::AddStores(uint32_t ptr_id, std::queue* insts) { + get_def_use_mgr()->ForEachUser(ptr_id, [this, insts](Instruction* user) { + spv::Op op = user->opcode(); + if (IsNonPtrAccessChain(op)) { + AddStores(user->result_id(), insts); + } else if (op == spv::Op::OpStore) { + insts->push(user); + } + }); +} + +void MemPass::DCEInst(Instruction* inst, + const std::function& call_back) { + std::queue deadInsts; + deadInsts.push(inst); + while (!deadInsts.empty()) { + Instruction* di = deadInsts.front(); + // Don't delete labels + if (di->opcode() == spv::Op::OpLabel) { + deadInsts.pop(); + continue; + } + // Remember operands + std::set ids; + di->ForEachInId([&ids](uint32_t* iid) { ids.insert(*iid); }); + uint32_t varId = 0; + // Remember variable if dead load + if (di->opcode() == spv::Op::OpLoad) (void)GetPtr(di, &varId); + if (call_back) { + call_back(di); + } + context()->KillInst(di); + // For all operands with no remaining uses, add their instruction + // to the dead instruction queue. + for (auto id : ids) + if (HasOnlyNamesAndDecorates(id)) { + Instruction* odi = get_def_use_mgr()->GetDef(id); + if (context()->IsCombinatorInstruction(odi)) deadInsts.push(odi); + } + // if a load was deleted and it was the variable's + // last load, add all its stores to dead queue + if (varId != 0 && !IsLiveVar(varId)) AddStores(varId, &deadInsts); + deadInsts.pop(); + } +} + +MemPass::MemPass() {} + +bool MemPass::HasOnlySupportedRefs(uint32_t varId) { + return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { + auto dbg_op = user->GetCommonDebugOpcode(); + if (dbg_op == CommonDebugInfoDebugDeclare || + dbg_op == CommonDebugInfoDebugValue) { + return true; + } + spv::Op op = user->opcode(); + if (op != spv::Op::OpStore && op != spv::Op::OpLoad && + op != spv::Op::OpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +uint32_t MemPass::Type2Undef(uint32_t type_id) { + const auto uitr = type2undefs_.find(type_id); + if (uitr != type2undefs_.end()) return uitr->second; + const uint32_t undefId = TakeNextId(); + if (undefId == 0) { + return 0; + } + + std::unique_ptr undef_inst( + new Instruction(context(), spv::Op::OpUndef, type_id, undefId, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*undef_inst); + get_module()->AddGlobalValue(std::move(undef_inst)); + type2undefs_[type_id] = undefId; + return undefId; +} + +bool MemPass::IsTargetVar(uint32_t varId) { + if (varId == 0) { + return false; + } + + if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end()) + return false; + if (seen_target_vars_.find(varId) != seen_target_vars_.end()) return true; + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + if (varInst->opcode() != spv::Op::OpVariable) return false; + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (spv::StorageClass(varTypeInst->GetSingleWordInOperand( + kTypePointerStorageClassInIdx)) != spv::StorageClass::Function) { + seen_non_target_vars_.insert(varId); + return false; + } + const uint32_t varPteTypeId = + varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); + Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId); + if (!IsTargetType(varPteTypeInst)) { + seen_non_target_vars_.insert(varId); + return false; + } + seen_target_vars_.insert(varId); + return true; +} + +// Remove all |phi| operands coming from unreachable blocks (i.e., blocks not in +// |reachable_blocks|). There are two types of removal that this function can +// perform: +// +// 1- Any operand that comes directly from an unreachable block is completely +// removed. Since the block is unreachable, the edge between the unreachable +// block and the block holding |phi| has been removed. +// +// 2- Any operand that comes via a live block and was defined at an unreachable +// block gets its value replaced with an OpUndef value. Since the argument +// was generated in an unreachable block, it no longer exists, so it cannot +// be referenced. However, since the value does not reach |phi| directly +// from the unreachable block, the operand cannot be removed from |phi|. +// Therefore, we replace the argument value with OpUndef. +// +// For example, in the switch() below, assume that we want to remove the +// argument with value %11 coming from block %41. +// +// [ ... ] +// %41 = OpLabel <--- Unreachable block +// %11 = OpLoad %int %y +// [ ... ] +// OpSelectionMerge %16 None +// OpSwitch %12 %16 10 %13 13 %14 18 %15 +// %13 = OpLabel +// OpBranch %16 +// %14 = OpLabel +// OpStore %outparm %int_14 +// OpBranch %16 +// %15 = OpLabel +// OpStore %outparm %int_15 +// OpBranch %16 +// %16 = OpLabel +// %30 = OpPhi %int %11 %41 %int_42 %13 %11 %14 %11 %15 +// +// Since %41 is now an unreachable block, the first operand of |phi| needs to +// be removed completely. But the operands (%11 %14) and (%11 %15) cannot be +// removed because %14 and %15 are reachable blocks. Since %11 no longer exist, +// in those arguments, we replace all references to %11 with an OpUndef value. +// This results in |phi| looking like: +// +// %50 = OpUndef %int +// [ ... ] +// %30 = OpPhi %int %int_42 %13 %50 %14 %50 %15 +void MemPass::RemovePhiOperands( + Instruction* phi, const std::unordered_set& reachable_blocks) { + std::vector keep_operands; + uint32_t type_id = 0; + // The id of an undefined value we've generated. + uint32_t undef_id = 0; + + // Traverse all the operands in |phi|. Build the new operand vector by adding + // all the original operands from |phi| except the unwanted ones. + for (uint32_t i = 0; i < phi->NumOperands();) { + if (i < 2) { + // The first two arguments are always preserved. + keep_operands.push_back(phi->GetOperand(i)); + ++i; + continue; + } + + // The remaining Phi arguments come in pairs. Index 'i' contains the + // variable id, index 'i + 1' is the originating block id. + assert(i % 2 == 0 && i < phi->NumOperands() - 1 && + "malformed Phi arguments"); + + BasicBlock* in_block = cfg()->block(phi->GetSingleWordOperand(i + 1)); + if (reachable_blocks.find(in_block) == reachable_blocks.end()) { + // If the incoming block is unreachable, remove both operands as this + // means that the |phi| has lost an incoming edge. + i += 2; + continue; + } + + // In all other cases, the operand must be kept but may need to be changed. + uint32_t arg_id = phi->GetSingleWordOperand(i); + Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id); + BasicBlock* def_block = context()->get_instr_block(arg_def_instr); + if (def_block && + reachable_blocks.find(def_block) == reachable_blocks.end()) { + // If the current |phi| argument was defined in an unreachable block, it + // means that this |phi| argument is no longer defined. Replace it with + // |undef_id|. + if (!undef_id) { + type_id = arg_def_instr->type_id(); + undef_id = Type2Undef(type_id); + } + keep_operands.push_back( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {undef_id})); + } else { + // Otherwise, the argument comes from a reachable block or from no block + // at all (meaning that it was defined in the global section of the + // program). In both cases, keep the argument intact. + keep_operands.push_back(phi->GetOperand(i)); + } + + keep_operands.push_back(phi->GetOperand(i + 1)); + + i += 2; + } + + context()->ForgetUses(phi); + phi->ReplaceOperands(keep_operands); + context()->AnalyzeUses(phi); +} + +void MemPass::RemoveBlock(Function::iterator* bi) { + auto& rm_block = **bi; + + // Remove instructions from the block. + rm_block.ForEachInst([&rm_block, this](Instruction* inst) { + // Note that we do not kill the block label instruction here. The label + // instruction is needed to identify the block, which is needed by the + // removal of phi operands. + if (inst != rm_block.GetLabelInst()) { + context()->KillInst(inst); + } + }); + + // Remove the label instruction last. + auto label = rm_block.GetLabelInst(); + context()->KillInst(label); + + *bi = bi->Erase(); +} + +bool MemPass::RemoveUnreachableBlocks(Function* func) { + if (func->IsDeclaration()) return false; + bool modified = false; + + // Mark reachable all blocks reachable from the function's entry block. + std::unordered_set reachable_blocks; + std::unordered_set visited_blocks; + std::queue worklist; + reachable_blocks.insert(func->entry().get()); + + // Initially mark the function entry point as reachable. + worklist.push(func->entry().get()); + + auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist, + this](uint32_t label_id) { + auto successor = cfg()->block(label_id); + if (visited_blocks.count(successor) == 0) { + reachable_blocks.insert(successor); + worklist.push(successor); + visited_blocks.insert(successor); + } + }; + + // Transitively mark all blocks reachable from the entry as reachable. + while (!worklist.empty()) { + BasicBlock* block = worklist.front(); + worklist.pop(); + + // All the successors of a live block are also live. + static_cast(block)->ForEachSuccessorLabel( + mark_reachable); + + // All the Merge and ContinueTarget blocks of a live block are also live. + block->ForMergeAndContinueLabel(mark_reachable); + } + + // Update operands of Phi nodes that reference unreachable blocks. + for (auto& block : *func) { + // If the block is about to be removed, don't bother updating its + // Phi instructions. + if (reachable_blocks.count(&block) == 0) { + continue; + } + + // If the block is reachable and has Phi instructions, remove all + // operands from its Phi instructions that reference unreachable blocks. + // If the block has no Phi instructions, this is a no-op. + block.ForEachPhiInst([&reachable_blocks, this](Instruction* phi) { + RemovePhiOperands(phi, reachable_blocks); + }); + } + + // Erase unreachable blocks. + for (auto ebi = func->begin(); ebi != func->end();) { + if (reachable_blocks.count(&*ebi) == 0) { + RemoveBlock(&ebi); + modified = true; + } else { + ++ebi; + } + } + + return modified; +} + +bool MemPass::CFGCleanup(Function* func) { + bool modified = false; + modified |= RemoveUnreachableBlocks(func); + return modified; +} + +void MemPass::CollectTargetVars(Function* func) { + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + type2undefs_.clear(); + + // Collect target (and non-) variable sets. Remove variables with + // non-load/store refs from target variable set + for (auto& blk : *func) { + for (auto& inst : blk) { + switch (inst.opcode()) { + case spv::Op::OpStore: + case spv::Op::OpLoad: { + uint32_t varId; + (void)GetPtr(&inst, &varId); + if (!IsTargetVar(varId)) break; + if (HasOnlySupportedRefs(varId)) break; + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + } break; + default: + break; + } + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.h new file mode 100644 index 00000000..aef9e5ff --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/mem_pass.h @@ -0,0 +1,164 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MEM_PASS_H_ +#define SOURCE_OPT_MEM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// A common base class for mem2reg-type passes. Provides common +// utility functions and supporting state. +class MemPass : public Pass { + public: + virtual ~MemPass() override = default; + + // Returns an undef value for the given |var_id|'s type. + uint32_t GetUndefVal(uint32_t var_id) { + return Type2Undef(GetPointeeTypeId(get_def_use_mgr()->GetDef(var_id))); + } + + // Given a load or store |ip|, return the pointer instruction. + // Also return the base variable's id in |varId|. If no base variable is + // found, |varId| will be 0. + Instruction* GetPtr(Instruction* ip, uint32_t* varId); + + // Return true if |varId| is a previously identified target variable. + // Return false if |varId| is a previously identified non-target variable. + // + // Non-target variables are variable of function scope of a target type that + // are accessed with constant-index access chains. not accessed with + // non-constant-index access chains. Also cache non-target variables. + // + // If variable is not cached, return true if variable is a function scope + // variable of target type, false otherwise. Updates caches of target and + // non-target variables. + bool IsTargetVar(uint32_t varId); + + // Collect target SSA variables. This traverses all the loads and stores in + // function |func| looking for variables that can be replaced with SSA IDs. It + // populates the sets |seen_target_vars_| and |seen_non_target_vars_|. + void CollectTargetVars(Function* func); + + protected: + MemPass(); + + // Returns true if |typeInst| is a scalar type + // or a vector or matrix + bool IsBaseTargetType(const Instruction* typeInst) const; + + // Returns true if |typeInst| is a math type or a struct or array + // of a math type. + // TODO(): Add more complex types to convert + bool IsTargetType(const Instruction* typeInst) const; + + // Returns true if |opcode| is a non-ptr access chain op + bool IsNonPtrAccessChain(const spv::Op opcode) const; + + // Given the id |ptrId|, return true if the top-most non-CopyObj is + // a variable, a non-ptr access chain or a parameter of pointer type. + bool IsPtr(uint32_t ptrId); + + // Given the id of a pointer |ptrId|, return the top-most non-CopyObj. + // Also return the base variable's id in |varId|. If no base variable is + // found, |varId| will be 0. + Instruction* GetPtr(uint32_t ptrId, uint32_t* varId); + + // Return true if all uses of |id| are only name or decorate ops. + bool HasOnlyNamesAndDecorates(uint32_t id) const; + + // Kill all instructions in block |bp|. Whether or not to kill the label is + // indicated by |killLabel|. + void KillAllInsts(BasicBlock* bp, bool killLabel = true); + + // Return true if any instruction loads from |varId| + bool HasLoads(uint32_t varId) const; + + // Return true if |varId| is not a function variable or if it has + // a load + bool IsLiveVar(uint32_t varId) const; + + // Add stores using |ptr_id| to |insts| + void AddStores(uint32_t ptr_id, std::queue* insts); + + // Delete |inst| and iterate DCE on all its operands if they are now + // useless. If a load is deleted and its variable has no other loads, + // delete all its variable's stores. + void DCEInst(Instruction* inst, const std::function&); + + // Call all the cleanup helper functions on |func|. + bool CFGCleanup(Function* func); + + // Return true if |op| is supported decorate. + inline bool IsNonTypeDecorate(spv::Op op) const { + return (op == spv::Op::OpDecorate || op == spv::Op::OpDecorateId); + } + + // Return the id of an undef value with type |type_id|. Create and insert an + // undef after the first non-variable in the function if it doesn't already + // exist. Add undef to function undef map. Returns 0 of the value does not + // exist, and cannot be created. + uint32_t Type2Undef(uint32_t type_id); + + // Cache of verified target vars + std::unordered_set seen_target_vars_; + + // Cache of verified non-target vars + std::unordered_set seen_non_target_vars_; + + private: + // Return true if all uses of |varId| are only through supported reference + // operations ie. loads and store. Also cache in supported_ref_vars_. + // TODO(dnovillo): This function is replicated in other passes and it's + // slightly different in every pass. Is it possible to make one common + // implementation? + bool HasOnlySupportedRefs(uint32_t varId); + + // Remove all the unreachable basic blocks in |func|. + bool RemoveUnreachableBlocks(Function* func); + + // Remove the block pointed by the iterator |*bi|. This also removes + // all the instructions in the pointed-to block. + void RemoveBlock(Function::iterator* bi); + + // Remove Phi operands in |phi| that are coming from blocks not in + // |reachable_blocks|. + void RemovePhiOperands( + Instruction* phi, + const std::unordered_set& reachable_blocks); + + // Map from type to undef + std::unordered_map type2undefs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MEM_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.cpp new file mode 100644 index 00000000..c262ea07 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.cpp @@ -0,0 +1,896 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/merge_return_pass.h" + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" +#include "source/util/bit_vector.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status MergeReturnPass::Process() { + bool is_shader = + context()->get_feature_mgr()->HasCapability(spv::Capability::Shader); + + bool failed = false; + ProcessFunction pfn = [&failed, is_shader, this](Function* function) { + std::vector return_blocks = CollectReturnBlocks(function); + if (return_blocks.size() <= 1) { + if (!is_shader || return_blocks.size() == 0) { + return false; + } + bool isInConstruct = + context()->GetStructuredCFGAnalysis()->ContainingConstruct( + return_blocks[0]->id()) != 0; + bool endsWithReturn = return_blocks[0] == function->tail(); + if (!isInConstruct && endsWithReturn) { + return false; + } + } + + function_ = function; + return_flag_ = nullptr; + return_value_ = nullptr; + final_return_block_ = nullptr; + + if (is_shader) { + if (!ProcessStructured(function, return_blocks)) { + failed = true; + } + } else { + MergeReturnBlocks(function, return_blocks); + } + return true; + }; + + bool modified = context()->ProcessReachableCallTree(pfn); + + if (failed) { + return Status::Failure; + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +void MergeReturnPass::GenerateState(BasicBlock* block) { + if (Instruction* mergeInst = block->GetMergeInst()) { + if (mergeInst->opcode() == spv::Op::OpLoopMerge) { + // If new loop, break to this loop merge block + state_.emplace_back(mergeInst, mergeInst); + } else { + auto branchInst = mergeInst->NextNode(); + if (branchInst->opcode() == spv::Op::OpSwitch) { + // If switch inside of loop, break to innermost loop merge block. + // Otherwise need to break to this switch merge block. + auto lastMergeInst = state_.back().BreakMergeInst(); + if (lastMergeInst && lastMergeInst->opcode() == spv::Op::OpLoopMerge) + state_.emplace_back(lastMergeInst, mergeInst); + else + state_.emplace_back(mergeInst, mergeInst); + } else { + // If branch conditional inside loop, always break to innermost + // loop merge block. If branch conditional inside switch, break to + // innermost switch merge block. + auto lastMergeInst = state_.back().BreakMergeInst(); + state_.emplace_back(lastMergeInst, mergeInst); + } + } + } +} + +bool MergeReturnPass::ProcessStructured( + Function* function, const std::vector& return_blocks) { + if (HasNontrivialUnreachableBlocks(function)) { + if (consumer()) { + std::string message = + "Module contains unreachable blocks during merge return. Run dead " + "branch elimination before merge return."; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; + } + + RecordImmediateDominators(function); + if (!AddSingleCaseSwitchAroundFunction()) { + return false; + } + + std::list order; + cfg()->ComputeStructuredOrder(function, &*function->begin(), &order); + + state_.clear(); + state_.emplace_back(nullptr, nullptr); + for (auto block : order) { + if (cfg()->IsPseudoEntryBlock(block) || cfg()->IsPseudoExitBlock(block) || + block == final_return_block_) { + continue; + } + + auto blockId = block->GetLabelInst()->result_id(); + if (blockId == CurrentState().CurrentMergeId()) { + // Pop the current state as we've hit the merge + state_.pop_back(); + } + + ProcessStructuredBlock(block); + + // Generate state for next block if warranted + GenerateState(block); + } + + state_.clear(); + state_.emplace_back(nullptr, nullptr); + std::unordered_set predicated; + for (auto block : order) { + if (cfg()->IsPseudoEntryBlock(block) || cfg()->IsPseudoExitBlock(block)) { + continue; + } + + auto blockId = block->id(); + if (blockId == CurrentState().CurrentMergeId()) { + // Pop the current state as we've hit the merge + state_.pop_back(); + } + + // Predicate successors of the original return blocks as necessary. + if (std::find(return_blocks.begin(), return_blocks.end(), block) != + return_blocks.end()) { + if (!PredicateBlocks(block, &predicated, &order)) { + return false; + } + } + + // Generate state for next block if warranted + GenerateState(block); + } + + // We have not kept the dominator tree up-to-date. + // Invalidate it at this point to make sure it will be rebuilt. + context()->RemoveDominatorAnalysis(function); + AddNewPhiNodes(); + return true; +} + +void MergeReturnPass::CreateReturnBlock() { + // Create a label for the new return block + std::unique_ptr return_label( + new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {})); + + // Create the new basic block + std::unique_ptr return_block( + new BasicBlock(std::move(return_label))); + function_->AddBasicBlock(std::move(return_block)); + final_return_block_ = &*(--function_->end()); + context()->AnalyzeDefUse(final_return_block_->GetLabelInst()); + context()->set_instr_block(final_return_block_->GetLabelInst(), + final_return_block_); + assert(final_return_block_->GetParent() == function_ && + "The function should have been set when the block was created."); +} + +void MergeReturnPass::CreateReturn(BasicBlock* block) { + AddReturnValue(); + + if (return_value_) { + // Load and return the final return value + uint32_t loadId = TakeNextId(); + block->AddInstruction(MakeUnique( + context(), spv::Op::OpLoad, function_->type_id(), loadId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}}})); + Instruction* var_inst = block->terminator(); + context()->AnalyzeDefUse(var_inst); + context()->set_instr_block(var_inst, block); + context()->get_decoration_mgr()->CloneDecorations( + return_value_->result_id(), loadId, + {spv::Decoration::RelaxedPrecision}); + + block->AddInstruction(MakeUnique( + context(), spv::Op::OpReturnValue, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {loadId}}})); + context()->AnalyzeDefUse(block->terminator()); + context()->set_instr_block(block->terminator(), block); + } else { + block->AddInstruction( + MakeUnique(context(), spv::Op::OpReturn)); + context()->AnalyzeDefUse(block->terminator()); + context()->set_instr_block(block->terminator(), block); + } +} + +void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) { + spv::Op tail_opcode = block->tail()->opcode(); + if (tail_opcode == spv::Op::OpReturn || + tail_opcode == spv::Op::OpReturnValue) { + if (!return_flag_) { + AddReturnFlag(); + } + } + + if (tail_opcode == spv::Op::OpReturn || + tail_opcode == spv::Op::OpReturnValue || + tail_opcode == spv::Op::OpUnreachable) { + assert(CurrentState().InBreakable() && + "Should be in the placeholder construct."); + BranchToBlock(block, CurrentState().BreakMergeId()); + return_blocks_.insert(block->id()); + } +} + +void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) { + if (block->tail()->opcode() == spv::Op::OpReturn || + block->tail()->opcode() == spv::Op::OpReturnValue) { + RecordReturned(block); + RecordReturnValue(block); + } + + BasicBlock* target_block = context()->get_instr_block(target); + if (target_block->GetLoopMergeInst()) { + cfg()->SplitLoopHeader(target_block); + } + UpdatePhiNodes(block, target_block); + + Instruction* return_inst = block->terminator(); + return_inst->SetOpcode(spv::Op::OpBranch); + return_inst->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}}); + context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst); + new_edges_[target_block].insert(block->id()); + cfg()->AddEdge(block->id(), target); +} + +void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source, + BasicBlock* target) { + target->ForEachPhiInst([this, new_source](Instruction* inst) { + uint32_t undefId = Type2Undef(inst->type_id()); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}}); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}}); + context()->UpdateDefUse(inst); + }); +} + +void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, + Instruction& inst) { + DominatorAnalysis* dom_tree = + context()->GetDominatorAnalysis(merge_block->GetParent()); + + if (inst.result_id() != 0) { + BasicBlock* inst_bb = context()->get_instr_block(&inst); + std::vector users_to_update; + context()->get_def_use_mgr()->ForEachUser( + &inst, + [&users_to_update, &dom_tree, &inst, inst_bb, this](Instruction* user) { + BasicBlock* user_bb = nullptr; + if (user->opcode() != spv::Op::OpPhi) { + user_bb = context()->get_instr_block(user); + } else { + // For OpPhi, the use should be considered to be in the predecessor. + for (uint32_t i = 0; i < user->NumInOperands(); i += 2) { + if (user->GetSingleWordInOperand(i) == inst.result_id()) { + uint32_t user_bb_id = user->GetSingleWordInOperand(i + 1); + user_bb = context()->get_instr_block(user_bb_id); + break; + } + } + } + + // If |user_bb| is nullptr, then |user| is not in the function. It is + // something like an OpName or decoration, which should not be + // replaced with the result of the OpPhi. + if (user_bb && !dom_tree->Dominates(inst_bb, user_bb)) { + users_to_update.push_back(user); + } + }); + + if (users_to_update.empty()) { + return; + } + + // There is at least one values that needs to be replaced. + // First create the OpPhi instruction. + uint32_t undef_id = Type2Undef(inst.type_id()); + std::vector phi_operands; + const std::set& new_edges = new_edges_[merge_block]; + + // Add the OpPhi operands. If the predecessor is a return block use undef, + // otherwise use |inst|'s id. + std::vector preds = cfg()->preds(merge_block->id()); + for (uint32_t pred_id : preds) { + if (new_edges.count(pred_id)) { + phi_operands.push_back(undef_id); + } else { + phi_operands.push_back(inst.result_id()); + } + phi_operands.push_back(pred_id); + } + + Instruction* new_phi = nullptr; + // If the instruction is a pointer and variable pointers are not an option, + // then we have to regenerate the instruction instead of creating an OpPhi + // instruction. If not, the Spir-V will be invalid. + Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id()); + bool regenerateInstruction = false; + if (inst_type->opcode() == spv::Op::OpTypePointer) { + if (!context()->get_feature_mgr()->HasCapability( + spv::Capability::VariablePointers)) { + regenerateInstruction = true; + } + + auto storage_class = + spv::StorageClass(inst_type->GetSingleWordInOperand(0)); + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer) { + regenerateInstruction = true; + } + } + + if (regenerateInstruction) { + std::unique_ptr regen_inst(inst.Clone(context())); + uint32_t new_id = TakeNextId(); + regen_inst->SetResultId(new_id); + Instruction* insert_pos = &*merge_block->begin(); + while (insert_pos->opcode() == spv::Op::OpPhi) { + insert_pos = insert_pos->NextNode(); + } + new_phi = insert_pos->InsertBefore(std::move(regen_inst)); + get_def_use_mgr()->AnalyzeInstDefUse(new_phi); + context()->set_instr_block(new_phi, merge_block); + + new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) { + Instruction* use = get_def_use_mgr()->GetDef(*use_id); + BasicBlock* use_bb = context()->get_instr_block(use); + if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) { + CreatePhiNodesForInst(merge_block, *use); + } + }); + } else { + InstructionBuilder builder( + context(), &*merge_block->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_phi = builder.AddPhi(inst.type_id(), phi_operands); + } + uint32_t result_of_phi = new_phi->result_id(); + + // Update all of the users to use the result of the new OpPhi. + for (Instruction* user : users_to_update) { + user->ForEachInId([&inst, result_of_phi](uint32_t* id) { + if (*id == inst.result_id()) { + *id = result_of_phi; + } + }); + context()->AnalyzeUses(user); + } + } +} + +bool MergeReturnPass::PredicateBlocks( + BasicBlock* return_block, std::unordered_set* predicated, + std::list* order) { + // The CFG is being modified as the function proceeds so avoid caching + // successors. + + if (predicated->count(return_block)) { + return true; + } + + BasicBlock* block = nullptr; + const BasicBlock* const_block = const_cast(return_block); + const_block->ForEachSuccessorLabel([this, &block](const uint32_t idx) { + BasicBlock* succ_block = context()->get_instr_block(idx); + assert(block == nullptr); + block = succ_block; + }); + assert(block && + "Return blocks should have returns already replaced by a single " + "unconditional branch."); + + auto state = state_.rbegin(); + std::unordered_set seen; + if (block->id() == state->CurrentMergeId()) { + state++; + } else if (block->id() == state->BreakMergeId()) { + while (state->BreakMergeId() == block->id()) { + state++; + } + } + + while (block != nullptr && block != final_return_block_) { + if (!predicated->insert(block).second) break; + // Skip structured subgraphs. + assert(state->InBreakable() && + "Should be in the placeholder construct at the very least."); + Instruction* break_merge_inst = state->BreakMergeInst(); + uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0); + while (state->BreakMergeId() == merge_block_id) { + state++; + } + if (!BreakFromConstruct(block, predicated, order, break_merge_inst)) { + return false; + } + block = context()->get_instr_block(merge_block_id); + } + return true; +} + +bool MergeReturnPass::BreakFromConstruct( + BasicBlock* block, std::unordered_set* predicated, + std::list* order, Instruction* break_merge_inst) { + // Make sure the CFG is build here. If we don't then it becomes very hard + // to know which new blocks need to be updated. + context()->InvalidateAnalyses(IRContext::kAnalysisCFG); + context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG); + + // When predicating, be aware of whether this block is a header block, a + // merge block or both. + // + // If this block is a merge block, ensure the appropriate header stays + // up-to-date with any changes (i.e. points to the pre-header). + // + // If this block is a header block, predicate the entire structured + // subgraph. This can act recursively. + + // If |block| is a loop header, then the back edge must jump to the original + // code, not the new header. + if (block->GetLoopMergeInst()) { + if (cfg()->SplitLoopHeader(block) == nullptr) { + return false; + } + } + + uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0); + BasicBlock* merge_block = context()->get_instr_block(merge_block_id); + if (merge_block->GetLoopMergeInst()) { + cfg()->SplitLoopHeader(merge_block); + } + + // Leave the phi instructions behind. + auto iter = block->begin(); + while (iter->opcode() == spv::Op::OpPhi) { + ++iter; + } + + // Forget about the edges leaving block. They will be removed. + cfg()->RemoveSuccessorEdges(block); + + auto old_body_id = TakeNextId(); + BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter); + predicated->insert(old_body); + + // If a return block is being split, mark the new body block also as a return + // block. + if (return_blocks_.count(block->id())) { + return_blocks_.insert(old_body_id); + } + + // If |block| was a continue target for a loop |old_body| is now the correct + // continue target. + if (break_merge_inst->opcode() == spv::Op::OpLoopMerge && + break_merge_inst->GetSingleWordInOperand(1) == block->id()) { + break_merge_inst->SetInOperand(1, {old_body->id()}); + context()->UpdateDefUse(break_merge_inst); + } + + // Update |order| so old_block will be traversed. + InsertAfterElement(block, old_body, order); + + // Within the new header we need the following: + // 1. Load of the return status flag + // 2. Branch to |merge_block| (true) or old body (false) + // 3. Update OpPhi instructions in |merge_block|. + // 4. Update the CFG. + // + // Since we are branching to the merge block of the current construct, there + // is no need for an OpSelectionMerge. + + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // 1. Load of the return status flag + analysis::Bool bool_type; + uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type); + assert(bool_id != 0); + uint32_t load_id = + builder.AddLoad(bool_id, return_flag_->result_id())->result_id(); + + // 2. Branch to |merge_block| (true) or |old_body| (false) + builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(), + old_body->id()); + + if (!new_edges_[merge_block].insert(block->id()).second) { + // It is possible that we already inserted a new edge to the merge block. + // If so, that edge now goes from |old_body| to |merge_block|. + new_edges_[merge_block].insert(old_body->id()); + } + + // 3. Update OpPhi instructions in |merge_block|. + UpdatePhiNodes(block, merge_block); + + // 4. Update the CFG. We do this after updating the OpPhi instructions + // because |UpdatePhiNodes| assumes the edge from |block| has not been added + // to the CFG yet. + cfg()->AddEdges(block); + cfg()->RegisterBlock(old_body); + + assert(old_body->begin() != old_body->end()); + assert(block->begin() != block->end()); + return true; +} + +void MergeReturnPass::RecordReturned(BasicBlock* block) { + if (block->tail()->opcode() != spv::Op::OpReturn && + block->tail()->opcode() != spv::Op::OpReturnValue) + return; + + assert(return_flag_ && "Did not generate the return flag variable."); + + if (!constant_true_) { + analysis::Bool temp; + const analysis::Bool* bool_type = + context()->get_type_mgr()->GetRegisteredType(&temp)->AsBool(); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Constant* true_const = + const_mgr->GetConstant(bool_type, {true}); + constant_true_ = const_mgr->GetDefiningInstruction(true_const); + context()->UpdateDefUse(constant_true_); + } + + std::unique_ptr return_store(new Instruction( + context(), spv::Op::OpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_flag_->result_id()}}, + {SPV_OPERAND_TYPE_ID, {constant_true_->result_id()}}})); + + Instruction* store_inst = + &*block->tail().InsertBefore(std::move(return_store)); + context()->set_instr_block(store_inst, block); + context()->AnalyzeDefUse(store_inst); +} + +void MergeReturnPass::RecordReturnValue(BasicBlock* block) { + auto terminator = *block->tail(); + if (terminator.opcode() != spv::Op::OpReturnValue) { + return; + } + + assert(return_value_ && + "Did not generate the variable to hold the return value."); + + std::unique_ptr value_store(new Instruction( + context(), spv::Op::OpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}}, + {SPV_OPERAND_TYPE_ID, {terminator.GetSingleWordInOperand(0u)}}})); + + Instruction* store_inst = + &*block->tail().InsertBefore(std::move(value_store)); + context()->set_instr_block(store_inst, block); + context()->AnalyzeDefUse(store_inst); +} + +void MergeReturnPass::AddReturnValue() { + if (return_value_) return; + + uint32_t return_type_id = function_->type_id(); + if (get_def_use_mgr()->GetDef(return_type_id)->opcode() == + spv::Op::OpTypeVoid) + return; + + uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType( + return_type_id, spv::StorageClass::Function); + + uint32_t var_id = TakeNextId(); + std::unique_ptr returnValue( + new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}})); + + auto insert_iter = function_->begin()->begin(); + insert_iter.InsertBefore(std::move(returnValue)); + BasicBlock* entry_block = &*function_->begin(); + return_value_ = &*entry_block->begin(); + context()->AnalyzeDefUse(return_value_); + context()->set_instr_block(return_value_, entry_block); + + context()->get_decoration_mgr()->CloneDecorations( + function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision}); +} + +void MergeReturnPass::AddReturnFlag() { + if (return_flag_) return; + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + analysis::Bool temp; + uint32_t bool_id = type_mgr->GetTypeInstruction(&temp); + analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool(); + + const analysis::Constant* false_const = + const_mgr->GetConstant(bool_type, {false}); + uint32_t const_false_id = + const_mgr->GetDefiningInstruction(false_const)->result_id(); + + uint32_t bool_ptr_id = + type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function); + + uint32_t var_id = TakeNextId(); + std::unique_ptr returnFlag(new Instruction( + context(), spv::Op::OpVariable, bool_ptr_id, var_id, + std::initializer_list{{SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}, + {SPV_OPERAND_TYPE_ID, {const_false_id}}})); + + auto insert_iter = function_->begin()->begin(); + + insert_iter.InsertBefore(std::move(returnFlag)); + BasicBlock* entry_block = &*function_->begin(); + return_flag_ = &*entry_block->begin(); + context()->AnalyzeDefUse(return_flag_); + context()->set_instr_block(return_flag_, entry_block); +} + +std::vector MergeReturnPass::CollectReturnBlocks( + Function* function) { + std::vector return_blocks; + for (auto& block : *function) { + Instruction& terminator = *block.tail(); + if (terminator.opcode() == spv::Op::OpReturn || + terminator.opcode() == spv::Op::OpReturnValue) { + return_blocks.push_back(&block); + } + } + return return_blocks; +} + +void MergeReturnPass::MergeReturnBlocks( + Function* function, const std::vector& return_blocks) { + if (return_blocks.size() <= 1) { + // No work to do. + return; + } + + CreateReturnBlock(); + uint32_t return_id = final_return_block_->id(); + auto ret_block_iter = --function->end(); + // Create the PHI for the merged block (if necessary). + // Create new return. + std::vector phi_ops; + for (auto block : return_blocks) { + if (block->tail()->opcode() == spv::Op::OpReturnValue) { + phi_ops.push_back( + {SPV_OPERAND_TYPE_ID, {block->tail()->GetSingleWordInOperand(0u)}}); + phi_ops.push_back({SPV_OPERAND_TYPE_ID, {block->id()}}); + } + } + + if (!phi_ops.empty()) { + // Need a PHI node to select the correct return value. + uint32_t phi_result_id = TakeNextId(); + uint32_t phi_type_id = function->type_id(); + std::unique_ptr phi_inst(new Instruction( + context(), spv::Op::OpPhi, phi_type_id, phi_result_id, phi_ops)); + ret_block_iter->AddInstruction(std::move(phi_inst)); + BasicBlock::iterator phiIter = ret_block_iter->tail(); + + std::unique_ptr return_inst( + new Instruction(context(), spv::Op::OpReturnValue, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {phi_result_id}}})); + ret_block_iter->AddInstruction(std::move(return_inst)); + BasicBlock::iterator ret = ret_block_iter->tail(); + + // Register the phi def and mark instructions for use updates. + get_def_use_mgr()->AnalyzeInstDefUse(&*phiIter); + get_def_use_mgr()->AnalyzeInstDef(&*ret); + } else { + std::unique_ptr return_inst( + new Instruction(context(), spv::Op::OpReturn)); + ret_block_iter->AddInstruction(std::move(return_inst)); + } + + // Replace returns with branches + for (auto block : return_blocks) { + context()->ForgetUses(block->terminator()); + block->tail()->SetOpcode(spv::Op::OpBranch); + block->tail()->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {return_id}}}); + get_def_use_mgr()->AnalyzeInstUse(block->terminator()); + get_def_use_mgr()->AnalyzeInstUse(block->GetLabelInst()); + } + + get_def_use_mgr()->AnalyzeInstDefUse(ret_block_iter->GetLabelInst()); +} + +void MergeReturnPass::AddNewPhiNodes() { + std::list order; + cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order); + + for (BasicBlock* bb : order) { + AddNewPhiNodes(bb); + } +} + +void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) { + // New phi nodes are needed for any id whose definition used to dominate |bb|, + // but no longer dominates |bb|. These are found by walking the dominator + // tree starting at the original immediate dominator of |bb| and ending at its + // current dominator. + + // Because we are walking the updated dominator tree it is important that the + // new phi nodes for the original dominators of |bb| have already been added. + // Otherwise some ids might be missed. Consider the case where bb1 dominates + // bb2, and bb2 dominates bb3. Suppose there are changes such that bb1 no + // longer dominates bb2 and the same for bb2 and bb3. This algorithm will not + // look at the ids defined in bb1. However, calling |AddNewPhiNodes(bb2)| + // first will add a phi node in bb2 for that value. Then a call to + // |AddNewPhiNodes(bb3)| will process that value by processing the phi in bb2. + DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_); + + BasicBlock* dominator = dom_tree->ImmediateDominator(bb); + if (dominator == nullptr) { + return; + } + + BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]); + while (current_bb != nullptr && current_bb != dominator) { + for (Instruction& inst : *current_bb) { + CreatePhiNodesForInst(bb, inst); + } + current_bb = dom_tree->ImmediateDominator(current_bb); + } +} + +void MergeReturnPass::RecordImmediateDominators(Function* function) { + DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function); + for (BasicBlock& bb : *function) { + BasicBlock* dominator_bb = dom_tree->ImmediateDominator(&bb); + if (dominator_bb && dominator_bb != cfg()->pseudo_entry_block()) { + original_dominator_[&bb] = dominator_bb->terminator(); + } else { + original_dominator_[&bb] = nullptr; + } + } +} + +void MergeReturnPass::InsertAfterElement(BasicBlock* element, + BasicBlock* new_element, + std::list* list) { + auto pos = std::find(list->begin(), list->end(), element); + assert(pos != list->end()); + ++pos; + list->insert(pos, new_element); +} + +bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() { + CreateReturnBlock(); + CreateReturn(final_return_block_); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(final_return_block_); + } + + if (!CreateSingleCaseSwitch(final_return_block_)) { + return false; + } + return true; +} + +BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { + std::unique_ptr label( + new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {})); + + // Create the new basic block + std::unique_ptr block(new BasicBlock(std::move(label))); + + // Insert the new block just before the return block + auto pos = function_->end(); + assert(pos != function_->begin()); + pos--; + assert(pos != function_->begin()); + assert(&*pos == final_return_block_); + auto new_block = &*pos.InsertBefore(std::move(block)); + new_block->SetParent(function_); + + context()->AnalyzeDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + + InstructionBuilder builder( + context(), new_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + builder.AddBranch(header_label_id); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(new_block); + } + + return new_block; +} + +bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { + // Insert the switch before any code is run. We have to split the entry + // block to make sure the OpVariable instructions remain in the entry block. + BasicBlock* start_block = &*function_->begin(); + auto split_pos = start_block->begin(); + while (split_pos->opcode() == spv::Op::OpVariable) { + ++split_pos; + } + + BasicBlock* old_block = + start_block->SplitBasicBlock(context(), TakeNextId(), split_pos); + + // Add the switch to the end of the entry block. + InstructionBuilder builder( + context(), start_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t const_zero_id = builder.GetUintConstantId(0u); + if (const_zero_id == 0) { + return false; + } + builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id()); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(old_block); + cfg()->AddEdges(start_block); + } + return true; +} + +bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) { + utils::BitVector reachable_blocks; + cfg()->ForEachBlockInPostOrder( + function->entry().get(), + [&reachable_blocks](BasicBlock* bb) { reachable_blocks.Set(bb->id()); }); + + for (auto& bb : *function) { + if (reachable_blocks.Get(bb.id())) { + continue; + } + + StructuredCFGAnalysis* struct_cfg_analysis = + context()->GetStructuredCFGAnalysis(); + if (struct_cfg_analysis->IsContinueBlock(bb.id())) { + // |bb| must be an empty block ending with a branch to the header. + Instruction* inst = &*bb.begin(); + if (inst->opcode() != spv::Op::OpBranch) { + return true; + } + + if (inst->GetSingleWordInOperand(0) != + struct_cfg_analysis->ContainingLoop(bb.id())) { + return true; + } + } else if (struct_cfg_analysis->IsMergeBlock(bb.id())) { + // |bb| must be an empty block ending with OpUnreachable. + if (bb.begin()->opcode() != spv::Op::OpUnreachable) { + return true; + } + } else { + return true; + } + } + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.h new file mode 100644 index 00000000..d15db2f6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/merge_return_pass.h @@ -0,0 +1,335 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MERGE_RETURN_PASS_H_ +#define SOURCE_OPT_MERGE_RETURN_PASS_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +/******************************************************************************* + * + * Handling Structured Control Flow: + * + * Structured control flow guarantees that the CFG will converge at a given + * point (the merge block). Within structured control flow, all blocks must be + * post-dominated by the merge block, except return blocks and break blocks. + * A break block is a block that branches to a containing construct's merge + * block. + * + * Beyond this, we further assume that all unreachable blocks have been + * cleaned up. This means that the only unreachable blocks are those necessary + * for valid structured control flow. + * + * Algorithm: + * + * If a return is encountered, it should record that: i) the function has + * "returned" and ii) the value of the return. The return should be replaced + * with a branch. If current block is not within structured control flow, this + * is the final return. This block should branch to the new return block (its + * direct successor). If the current block is within structured control flow, + * the branch destination should be the innermost construct's merge. This + * merge will always exist because a single case switch is added around the + * entire function. If the merge block produces any live values it will need to + * be predicated. While the merge is nested in structured control flow, the + * predication path should branch to the merge block of the inner-most loop + * (or switch if no loop) it is contained in. Once structured control flow has + * been exited, it will be at the merge of the single case switch, which will + * simply return. + * + * In the final return block, the return value should be loaded and returned. + * Memory promotion passes should be able to promote the newly introduced + * variables ("has returned" and "return value"). + * + * Predicating the Final Merge: + * + * At each merge block predication needs to be introduced (optimization: only if + * that block produces value live beyond it). This needs to be done carefully. + * The merge block should be split into multiple blocks. + * + * 1 (loop header) + * / \ + * (ret) 2 3 (merge) + * + * || + * \/ + * + * 0 (single case switch header) + * | + * 1 (loop header) + * / \ + * 2 | (merge) + * \ / + * 3' (merge) + * / \ + * | 3 (original code in 3) + * \ / + * (ret) 4 (single case switch merge) + * + * In the above (simple) example, the return originally in |2| is passed through + * the loop merge. That merge is predicated such that the old body of the block + * is the else branch. The branch condition is based on the value of the "has + * returned" variable. + * + ******************************************************************************/ + +// Documented in optimizer.hpp +class MergeReturnPass : public MemPass { + public: + MergeReturnPass() + : function_(nullptr), + return_flag_(nullptr), + return_value_(nullptr), + constant_true_(nullptr), + final_return_block_(nullptr) {} + + const char* name() const override { return "merge-return"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // This class is used to store the a break merge instruction and a current + // merge instruction. The intended use is to keep track of the block to + // break to and the current innermost control flow construct merge block. + class StructuredControlState { + public: + StructuredControlState(Instruction* break_merge, Instruction* merge) + : break_merge_(break_merge), current_merge_(merge) {} + + bool InBreakable() const { return break_merge_; } + bool InStructuredFlow() const { return CurrentMergeId() != 0; } + + uint32_t CurrentMergeId() const { + return current_merge_ ? current_merge_->GetSingleWordInOperand(0u) : 0u; + } + + uint32_t CurrentMergeHeader() const { + return current_merge_ ? current_merge_->context() + ->get_instr_block(current_merge_) + ->id() + : 0; + } + + uint32_t BreakMergeId() const { + return break_merge_ ? break_merge_->GetSingleWordInOperand(0u) : 0u; + } + + Instruction* BreakMergeInst() const { return break_merge_; } + + private: + Instruction* break_merge_; + Instruction* current_merge_; + }; + + // Returns all BasicBlocks terminated by OpReturn or OpReturnValue in + // |function|. + std::vector CollectReturnBlocks(Function* function); + + // Creates a new basic block with a single return. If |function| returns a + // value, a phi node is created to select the correct value to return. + // Replaces old returns with an unconditional branch to the new block. + void MergeReturnBlocks(Function* function, + const std::vector& returnBlocks); + + // Generate and push new control flow state if |block| contains a merge. + void GenerateState(BasicBlock* block); + + // Merges the return instruction in |function| so that it has a single return + // statement. It is assumed that |function| has structured control flow, and + // that |return_blocks| is a list of all of the basic blocks in |function| + // that have a return. + bool ProcessStructured(Function* function, + const std::vector& return_blocks); + + // Changes an OpReturn* or OpUnreachable instruction at the end of |block| + // into a store to |return_flag_|, a store to |return_value_| (if necessary), + // and a branch to the appropriate merge block. + // + // Is is assumed that |AddReturnValue| have already been called to created the + // variable to store a return value if there is one. + // + // Note this will break the semantics. To fix this, PredicateBlock will have + // to be called on the merge block the branch targets. + void ProcessStructuredBlock(BasicBlock* block); + + // Creates a variable used to store whether or not the control flow has + // traversed a block that used to have a return. A pointer to the instruction + // declaring the variable is stored in |return_flag_|. + void AddReturnFlag(); + + // Creates the variable used to store the return value when passing through + // a block that use to contain an OpReturnValue. + void AddReturnValue(); + + // Adds a store that stores true to |return_flag_| immediately before the + // terminator of |block|. It is assumed that |AddReturnFlag| has already been + // called. + void RecordReturned(BasicBlock* block); + + // Adds an instruction that stores the value being returned in the + // OpReturnValue in |block|. The value is stored to |return_value_|, and the + // store is placed before the OpReturnValue. + // + // If |block| does not contain an OpReturnValue, then this function has no + // effect. If |block| contains an OpReturnValue, then |AddReturnValue| must + // have already been called to create the variable to store to. + void RecordReturnValue(BasicBlock* block); + + // Adds an unconditional branch in |block| that branches to |target|. It also + // adds stores to |return_flag_| and |return_value_| as needed. + // |AddReturnFlag| and |AddReturnValue| must have already been called. + void BranchToBlock(BasicBlock* block, uint32_t target); + + // For every basic block that is reachable from |return_block|, extra code is + // added to jump around any code that should not be executed because the + // original code would have already returned. This involves adding new + // selections constructs to jump around these instructions. + // + // If new blocks that are created will be added to |order|. This way a call + // can traverse these new block in structured order. + // + // Returns true if successful. + bool PredicateBlocks(BasicBlock* return_block, + std::unordered_set* pSet, + std::list* order); + + // Add a conditional branch at the start of |block| that either jumps to + // the merge block of |break_merge_inst| or the original code in |block| + // depending on the value in |return_flag_|. The continue target in + // |break_merge_inst| will be updated if needed. + // + // If new blocks that are created will be added to |order|. This way a call + // can traverse these new block in structured order. + // + // Returns true if successful. + bool BreakFromConstruct(BasicBlock* block, + std::unordered_set* predicated, + std::list* order, + Instruction* break_merge_inst); + + // Add an |OpReturn| or |OpReturnValue| to the end of |block|. If an + // |OpReturnValue| is needed, the return value is loaded from |return_value_|. + void CreateReturn(BasicBlock* block); + + // Creates a block at the end of the function that will become the single + // return block at the end of the pass. + void CreateReturnBlock(); + + // Creates a Phi node in |merge_block| for the result of |inst|. + // Any uses of the result of |inst| that are no longer + // dominated by |inst|, are replaced with the result of the new |OpPhi| + // instruction. + void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst); + + // Add new phi nodes for any id that no longer dominate all of it uses. A phi + // node is added to a block |bb| for an id if the id is defined between the + // original immediate dominator of |bb| and its new immediate dominator. It + // is assumed that at this point there are no unreachable blocks in the + // control flow graph. + void AddNewPhiNodes(); + + // Creates any new phi nodes that are needed in |bb|. |AddNewPhiNodes| must + // have already been called on the original dominators of |bb|. + void AddNewPhiNodes(BasicBlock* bb); + + // Records the terminator of immediate dominator for every basic block in + // |function|. + void RecordImmediateDominators(Function* function); + + // Modifies existing OpPhi instruction in |target| block to account for the + // new edge from |new_source|. The value for that edge will be an Undef. + // + // The CFG must not include the edge from |new_source| to |target| yet. + void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target); + + StructuredControlState& CurrentState() { return state_.back(); } + + // Inserts |new_element| into |list| after the first occurrence of |element|. + // |element| must be in |list| at least once. + void InsertAfterElement(BasicBlock* element, BasicBlock* new_element, + std::list* list); + + // Creates a single case switch around all of the executable code of the + // current function where the switch and case value are both zero and the + // default is the merge block. Returns after the switch is executed. Sets + // |final_return_block_|. + bool AddSingleCaseSwitchAroundFunction(); + + // Creates a new basic block that branches to |header_label_id|. Returns the + // new basic block. The block will be the second last basic block in the + // function. + BasicBlock* CreateContinueTarget(uint32_t header_label_id); + + // Creates a one case switch around the executable code of the function with + // |merge_target| as the merge node. + bool CreateSingleCaseSwitch(BasicBlock* merge_target); + + // Returns true if |function| has an unreachable block that is not a continue + // target that simply branches back to the header, or a merge block containing + // 1 instruction which is OpUnreachable. + bool HasNontrivialUnreachableBlocks(Function* function); + + // A stack used to keep track of the break and current control flow construct + // merge blocks. + std::vector state_; + + // The current function being transformed. + Function* function_; + + // The |OpVariable| instruction defining a boolean variable used to keep track + // of whether or not the function is trying to return. + Instruction* return_flag_; + + // The |OpVariable| instruction defining a variabled to used to keep track of + // the value that was returned when passing through a block that use to + // contain an |OpReturnValue|. + Instruction* return_value_; + + // The instruction defining the boolean constant true. + Instruction* constant_true_; + + // The basic block that is suppose to become the contain the only return value + // after processing the current function. + BasicBlock* final_return_block_; + + // This is a map from a node to its original immediate dominator identified by + // the terminator if that block. We use the terminator because the block we + // want may change if the block is split. + std::unordered_map original_dominator_; + + // A map from a basic block, bb, to the set of basic blocks which represent + // the new edges that reach |bb|. + std::unordered_map> new_edges_; + + // Contains all return blocks that are merged. This is set is populated while + // processing structured blocks and used to properly construct OpPhi + // instructions. + std::unordered_set return_blocks_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MERGE_RETURN_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.cpp new file mode 100644 index 00000000..dd79b628 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "modify_maximal_reconvergence.h" + +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status ModifyMaximalReconvergence::Process() { + bool changed = false; + if (add_) { + changed = AddMaximalReconvergence(); + } else { + changed = RemoveMaximalReconvergence(); + } + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +bool ModifyMaximalReconvergence::AddMaximalReconvergence() { + bool changed = false; + bool has_extension = false; + bool has_shader = + context()->get_feature_mgr()->HasCapability(spv::Capability::Shader); + for (auto extension : context()->extensions()) { + if (extension.GetOperand(0).AsString() == "SPV_KHR_maximal_reconvergence") { + has_extension = true; + break; + } + } + + std::unordered_set entry_points_with_mode; + for (auto mode : get_module()->execution_modes()) { + if (spv::ExecutionMode(mode.GetSingleWordInOperand(1)) == + spv::ExecutionMode::MaximallyReconvergesKHR) { + entry_points_with_mode.insert(mode.GetSingleWordInOperand(0)); + } + } + + for (auto entry_point : get_module()->entry_points()) { + const uint32_t id = entry_point.GetSingleWordInOperand(1); + if (!entry_points_with_mode.count(id)) { + changed = true; + if (!has_extension) { + context()->AddExtension("SPV_KHR_maximal_reconvergence"); + has_extension = true; + } + if (!has_shader) { + context()->AddCapability(spv::Capability::Shader); + has_shader = true; + } + context()->AddExecutionMode(MakeUnique( + context(), spv::Op::OpExecutionMode, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_EXECUTION_MODE, + {static_cast( + spv::ExecutionMode::MaximallyReconvergesKHR)}}})); + entry_points_with_mode.insert(id); + } + } + + return changed; +} + +bool ModifyMaximalReconvergence::RemoveMaximalReconvergence() { + bool changed = false; + std::vector to_remove; + Instruction* mode = &*get_module()->execution_mode_begin(); + while (mode) { + if (mode->opcode() != spv::Op::OpExecutionMode && + mode->opcode() != spv::Op::OpExecutionModeId) { + break; + } + if (spv::ExecutionMode(mode->GetSingleWordInOperand(1)) == + spv::ExecutionMode::MaximallyReconvergesKHR) { + mode = context()->KillInst(mode); + changed = true; + } else { + mode = mode->NextNode(); + } + } + + changed |= + context()->RemoveExtension(Extension::kSPV_KHR_maximal_reconvergence); + return changed; +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.h b/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.h new file mode 100644 index 00000000..8d9a698e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/modify_maximal_reconvergence.h @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_MODIFY_MAXIMAL_RECONVERGENCE_H_ +#define LIBSPIRV_OPT_MODIFY_MAXIMAL_RECONVERGENCE_H_ + +#include "pass.h" + +namespace spvtools { +namespace opt { + +// Modifies entry points to either add or remove MaximallyReconvergesKHR +// +// This pass will either add or remove MaximallyReconvergesKHR to all entry +// points in the module. When adding the execution mode, it does not attempt to +// determine whether any ray tracing invocation repack instructions might be +// executed because it is a runtime restriction. That is left to the user. +class ModifyMaximalReconvergence : public Pass { + public: + const char* name() const override { return "modify-maximal-reconvergence"; } + Status Process() override; + + explicit ModifyMaximalReconvergence(bool add = true) : Pass(), add_(add) {} + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + bool AddMaximalReconvergence(); + bool RemoveMaximalReconvergence(); + + bool add_; +}; +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_MODIFY_MAXIMAL_RECONVERGENCE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/module.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/module.cpp new file mode 100644 index 00000000..a9710c6a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/module.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/module.h" + +#include +#include +#include + +#include "source/operand.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +uint32_t Module::TakeNextIdBound() { + if (context()) { + if (id_bound() >= context()->max_id_bound()) { + return 0; + } + } else if (id_bound() >= kDefaultMaxIdBound) { + return 0; + } + + return header_.bound++; +} + +std::vector Module::GetTypes() { + std::vector type_insts; + for (auto& inst : types_values_) { + if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst); + } + return type_insts; +} + +std::vector Module::GetTypes() const { + std::vector type_insts; + for (auto& inst : types_values_) { + if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst); + } + return type_insts; +} + +std::vector Module::GetConstants() { + std::vector const_insts; + for (auto& inst : types_values_) { + if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst); + } + return const_insts; +} + +std::vector Module::GetConstants() const { + std::vector const_insts; + for (auto& inst : types_values_) { + if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst); + } + return const_insts; +} + +uint32_t Module::GetGlobalValue(spv::Op opcode) const { + for (auto& inst : types_values_) { + if (inst.opcode() == opcode) return inst.result_id(); + } + return 0; +} + +void Module::AddGlobalValue(spv::Op opcode, uint32_t result_id, + uint32_t type_id) { + std::unique_ptr newGlobal( + new Instruction(context(), opcode, type_id, result_id, {})); + AddGlobalValue(std::move(newGlobal)); +} + +void Module::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { +#define DELEGATE(list) list.ForEachInst(f, run_on_debug_line_insts) + DELEGATE(capabilities_); + DELEGATE(extensions_); + DELEGATE(ext_inst_imports_); + if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts); + if (sampled_image_address_mode_) + sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts); + DELEGATE(entry_points_); + DELEGATE(execution_modes_); + DELEGATE(debugs1_); + DELEGATE(debugs2_); + DELEGATE(debugs3_); + DELEGATE(ext_inst_debuginfo_); + DELEGATE(annotations_); + DELEGATE(types_values_); + for (auto& i : functions_) { + i->ForEachInst(f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } +#undef DELEGATE +} + +void Module::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) const { +#define DELEGATE(i) i.ForEachInst(f, run_on_debug_line_insts) + for (auto& i : capabilities_) DELEGATE(i); + for (auto& i : extensions_) DELEGATE(i); + for (auto& i : ext_inst_imports_) DELEGATE(i); + if (memory_model_) + static_cast(memory_model_.get()) + ->ForEachInst(f, run_on_debug_line_insts); + if (sampled_image_address_mode_) + static_cast(sampled_image_address_mode_.get()) + ->ForEachInst(f, run_on_debug_line_insts); + for (auto& i : entry_points_) DELEGATE(i); + for (auto& i : execution_modes_) DELEGATE(i); + for (auto& i : debugs1_) DELEGATE(i); + for (auto& i : debugs2_) DELEGATE(i); + for (auto& i : debugs3_) DELEGATE(i); + for (auto& i : annotations_) DELEGATE(i); + for (auto& i : types_values_) DELEGATE(i); + for (auto& i : ext_inst_debuginfo_) DELEGATE(i); + for (auto& i : functions_) { + static_cast(i.get())->ForEachInst( + f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } + if (run_on_debug_line_insts) { + for (auto& i : trailing_dbg_line_info_) DELEGATE(i); + } +#undef DELEGATE +} + +void Module::ToBinary(std::vector* binary, bool skip_nop) const { + binary->push_back(header_.magic_number); + binary->push_back(header_.version); + // TODO(antiagainst): should we change the generator number? + binary->push_back(header_.generator); + binary->push_back(header_.bound); + binary->push_back(header_.schema); + + size_t bound_idx = binary->size() - 2; + DebugScope last_scope(kNoDebugScope, kNoInlinedAt); + const Instruction* last_line_inst = nullptr; + bool between_merge_and_branch = false; + bool between_label_and_phi_var = false; + auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst, + &between_merge_and_branch, &between_label_and_phi_var, + this](const Instruction* i) { + // Skip emitting line instructions between merge and branch instructions. + auto opcode = i->opcode(); + if (between_merge_and_branch && i->IsLineInst()) { + return; + } + if (last_line_inst != nullptr) { + // If the current instruction is OpLine or DebugLine and it is the same + // as the last line instruction that is still effective (can be applied + // to the next instruction), we skip writing the current instruction. + if (i->IsLine()) { + uint32_t operand_index = 0; + if (last_line_inst->WhileEachInOperand( + [&operand_index, i](const uint32_t* word) { + assert(i->NumInOperandWords() > operand_index); + return *word == i->GetSingleWordInOperand(operand_index++); + })) { + return; + } + } else if (!i->IsNoLine() && i->dbg_line_insts().empty()) { + // If the current instruction does not have the line information, + // the last line information is not effective any more. Emit OpNoLine + // or DebugNoLine to specify it. + uint32_t shader_set_id = context() + ->get_feature_mgr() + ->GetExtInstImportId_Shader100DebugInfo(); + if (shader_set_id != 0) { + binary->push_back((5 << 16) | + static_cast(spv::Op::OpExtInst)); + binary->push_back(context()->get_type_mgr()->GetVoidTypeId()); + binary->push_back(context()->TakeNextId()); + binary->push_back(shader_set_id); + binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine); + } else { + binary->push_back((1 << 16) | + static_cast(spv::Op::OpNoLine)); + } + last_line_inst = nullptr; + } + } + + if (opcode == spv::Op::OpLabel) { + between_label_and_phi_var = true; + } else if (opcode != spv::Op::OpVariable && opcode != spv::Op::OpPhi && + !spvtools::opt::IsOpLineInst(opcode)) { + between_label_and_phi_var = false; + } + + if (!(skip_nop && i->IsNop())) { + const auto& scope = i->GetDebugScope(); + if (scope != last_scope && !between_merge_and_branch) { + // Can only emit nonsemantic instructions after all phi instructions + // in a block so don't emit scope instructions before phi instructions + // for NonSemantic.Shader.DebugInfo.100. + if (!between_label_and_phi_var || + context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()) { + // Emit DebugScope |scope| to |binary|. + auto dbg_inst = ext_inst_debuginfo_.begin(); + scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), + dbg_inst->GetSingleWordOperand(2), binary); + } + last_scope = scope; + } + + i->ToBinaryWithoutAttachedDebugInsts(binary); + } + // Update the last line instruction. + between_merge_and_branch = false; + if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) { + last_line_inst = nullptr; + } else if (opcode == spv::Op::OpLoopMerge || + opcode == spv::Op::OpSelectionMerge) { + between_merge_and_branch = true; + last_line_inst = nullptr; + } else if (i->IsLine()) { + last_line_inst = i; + } + }; + ForEachInst(write_inst, true); + + // We create new instructions for DebugScope and DebugNoLine. The bound must + // be updated. + binary->data()[bound_idx] = header_.bound; +} + +uint32_t Module::ComputeIdBound() const { + uint32_t highest = 0; + + ForEachInst( + [&highest](const Instruction* inst) { + for (const auto& operand : *inst) { + if (spvIsIdType(operand.type)) { + highest = std::max(highest, operand.words[0]); + } + } + }, + true /* scan debug line insts as well */); + + return highest + 1; +} + +bool Module::HasExplicitCapability(uint32_t cap) { + for (auto& ci : capabilities_) { + uint32_t tcap = ci.GetSingleWordOperand(0); + if (tcap == cap) { + return true; + } + } + return false; +} + +uint32_t Module::GetExtInstImportId(const char* extstr) { + for (auto& ei : ext_inst_imports_) + if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id(); + return 0; +} + +std::ostream& operator<<(std::ostream& str, const Module& module) { + module.ForEachInst([&str](const Instruction* inst) { + str << *inst; + if (inst->opcode() != spv::Op::OpFunctionEnd) { + str << std::endl; + } + }); + return str; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/module.h b/engine/engine/3rdlibs/spirv-tools/source/opt/module.h new file mode 100644 index 00000000..98c16dc4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/module.h @@ -0,0 +1,558 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MODULE_H_ +#define SOURCE_OPT_MODULE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// A struct for containing the module header information. +struct ModuleHeader { + uint32_t magic_number; + uint32_t version; + uint32_t generator; + uint32_t bound; + uint32_t schema; +}; + +// A SPIR-V module. It contains all the information for a SPIR-V module and +// serves as the backbone of optimization transformations. +class Module { + public: + using iterator = UptrVectorIterator; + using const_iterator = UptrVectorIterator; + using inst_iterator = InstructionList::iterator; + using const_inst_iterator = InstructionList::const_iterator; + + // Creates an empty module with zero'd header. + Module() : header_({}), contains_debug_info_(false) {} + + // Sets the header to the given |header|. + void SetHeader(const ModuleHeader& header) { header_ = header; } + + // Sets the Id bound. The Id bound cannot be set to 0. + void SetIdBound(uint32_t bound) { + assert(bound != 0); + header_.bound = bound; + } + + // Returns the Id bound. + uint32_t IdBound() const { return header_.bound; } + + // Returns the current Id bound and increases it to the next available value. + // If the id bound has already reached its maximum value, then 0 is returned. + // The maximum value for the id bound is obtained from the context. If there + // is none, then the minimum that limit can be according to the spir-v + // specification. + // TODO(1841): Update the uses to check for a 0 return value. + uint32_t TakeNextIdBound(); + + // Appends a capability instruction to this module. + inline void AddCapability(std::unique_ptr c); + + // Appends an extension instruction to this module. + inline void AddExtension(std::unique_ptr e); + + // Appends an extended instruction set instruction to this module. + inline void AddExtInstImport(std::unique_ptr e); + + // Set the memory model for this module. + inline void SetMemoryModel(std::unique_ptr m); + + // Set the sampled image addressing mode for this module. + inline void SetSampledImageAddressMode(std::unique_ptr m); + + // Appends an entry point instruction to this module. + inline void AddEntryPoint(std::unique_ptr e); + + // Appends an execution mode instruction to this module. + inline void AddExecutionMode(std::unique_ptr e); + + // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 1" instructions are the ones in layout section 7.a), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug1Inst(std::unique_ptr d); + + // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 2" instructions are the ones in layout section 7.b), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug2Inst(std::unique_ptr d); + + // Appends a debug 3 instruction (OpModuleProcessed) to this module. + // This is due to decision by the SPIR Working Group, pending publication. + inline void AddDebug3Inst(std::unique_ptr d); + + // Appends a debug info extension (OpenCL.DebugInfo.100, + // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module. + inline void AddExtInstDebugInfo(std::unique_ptr d); + + // Appends an annotation instruction to this module. + inline void AddAnnotationInst(std::unique_ptr a); + + // Appends a type-declaration instruction to this module. + inline void AddType(std::unique_ptr t); + + // Appends a constant, global variable, or OpUndef instruction to this module. + inline void AddGlobalValue(std::unique_ptr v); + + // Prepends a function declaration to this module. + inline void AddFunctionDeclaration(std::unique_ptr f); + + // Appends a function to this module. + inline void AddFunction(std::unique_ptr f); + + // Sets |contains_debug_info_| as true. + inline void SetContainsDebugInfo(); + inline bool ContainsDebugInfo() { return contains_debug_info_; } + + // Returns a vector of pointers to type-declaration instructions in this + // module. + std::vector GetTypes(); + std::vector GetTypes() const; + // Returns a vector of pointers to constant-creation instructions in this + // module. + std::vector GetConstants(); + std::vector GetConstants() const; + + // Return result id of global value with |opcode|, 0 if not present. + uint32_t GetGlobalValue(spv::Op opcode) const; + + // Add global value with |opcode|, |result_id| and |type_id| + void AddGlobalValue(spv::Op opcode, uint32_t result_id, uint32_t type_id); + + inline uint32_t id_bound() const { return header_.bound; } + + inline uint32_t version() const { return header_.version; } + inline uint32_t generator() const { return header_.generator; } + inline uint32_t schema() const { return header_.schema; } + + inline void set_version(uint32_t v) { header_.version = v; } + + // Iterators for capabilities instructions contained in this module. + inline inst_iterator capability_begin(); + inline inst_iterator capability_end(); + inline IteratorRange capabilities(); + inline IteratorRange capabilities() const; + + // Iterators for ext_inst_imports instructions contained in this module. + inline inst_iterator ext_inst_import_begin(); + inline inst_iterator ext_inst_import_end(); + inline IteratorRange ext_inst_imports(); + inline IteratorRange ext_inst_imports() const; + + // Return the memory model instruction contained in this module. + inline Instruction* GetMemoryModel() { return memory_model_.get(); } + inline const Instruction* GetMemoryModel() const { + return memory_model_.get(); + } + + // Return the sampled image address mode instruction contained in this module. + inline Instruction* GetSampledImageAddressMode() { + return sampled_image_address_mode_.get(); + } + inline const Instruction* GetSampledImageAddressMode() const { + return sampled_image_address_mode_.get(); + } + + // There are several kinds of debug instructions, according to where they can + // appear in the logical layout of a module: + // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued + // - Section 7b: OpName, OpMemberName + // - Section 7c: OpModuleProcessed + // - Mostly anywhere: OpLine and OpNoLine + // + + // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7a. + inline inst_iterator debug1_begin(); + inline inst_iterator debug1_end(); + inline IteratorRange debugs1(); + inline IteratorRange debugs1() const; + + // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7b. + inline inst_iterator debug2_begin(); + inline inst_iterator debug2_end(); + inline IteratorRange debugs2(); + inline IteratorRange debugs2() const; + + // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7c. + inline inst_iterator debug3_begin(); + inline inst_iterator debug3_end(); + inline IteratorRange debugs3(); + inline IteratorRange debugs3() const; + + // Iterators for debug info instructions (excluding OpLine & OpNoLine) + // contained in this module. These are OpExtInst for DebugInfo extension + // placed between section 9 and 10. + inline inst_iterator ext_inst_debuginfo_begin(); + inline inst_iterator ext_inst_debuginfo_end(); + inline IteratorRange ext_inst_debuginfo(); + inline IteratorRange ext_inst_debuginfo() const; + + // Iterators for entry point instructions contained in this module + inline IteratorRange entry_points(); + inline IteratorRange entry_points() const; + + // Iterators for execution_modes instructions contained in this module. + inline inst_iterator execution_mode_begin(); + inline inst_iterator execution_mode_end(); + inline IteratorRange execution_modes(); + inline IteratorRange execution_modes() const; + + // Iterators for annotation instructions contained in this module. + inline inst_iterator annotation_begin(); + inline inst_iterator annotation_end(); + IteratorRange annotations(); + IteratorRange annotations() const; + + // Iterators for extension instructions contained in this module. + inline inst_iterator extension_begin(); + inline inst_iterator extension_end(); + IteratorRange extensions(); + IteratorRange extensions() const; + + // Iterators for types, constants and global variables instructions. + inline inst_iterator types_values_begin(); + inline inst_iterator types_values_end(); + inline IteratorRange types_values(); + inline IteratorRange types_values() const; + + // Iterators for functions contained in this module. + iterator begin() { return iterator(&functions_, functions_.begin()); } + iterator end() { return iterator(&functions_, functions_.end()); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + inline const_iterator cbegin() const; + inline const_iterator cend() const; + + // Invokes function |f| on all instructions in this module, and optionally on + // the debug line instructions that precede them. + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Pushes the binary segments for this instruction into the back of *|binary|. + // If |skip_nop| is true and this is a OpNop, do nothing. + void ToBinary(std::vector* binary, bool skip_nop) const; + + // Returns 1 more than the maximum Id value mentioned in the module. + uint32_t ComputeIdBound() const; + + // Returns true if module has capability |cap| + bool HasExplicitCapability(uint32_t cap); + + // Returns id for OpExtInst instruction for extension |extstr|. + // Returns 0 if not found. + uint32_t GetExtInstImportId(const char* extstr); + + // Sets the associated context for this module + void SetContext(IRContext* c) { context_ = c; } + + // Gets the associated context for this module + IRContext* context() const { return context_; } + + // Sets the trailing debug line info to |dbg_line_info|. + void SetTrailingDbgLineInfo(std::vector&& dbg_line_info) { + trailing_dbg_line_info_ = std::move(dbg_line_info); + } + + std::vector& trailing_dbg_line_info() { + return trailing_dbg_line_info_; + } + + const std::vector& trailing_dbg_line_info() const { + return trailing_dbg_line_info_; + } + + private: + ModuleHeader header_; // Module header + + // The following fields respect the "Logical Layout of a Module" in + // Section 2.4 of the SPIR-V specification. + IRContext* context_; + InstructionList capabilities_; + InstructionList extensions_; + InstructionList ext_inst_imports_; + // A module only has one memory model instruction. + std::unique_ptr memory_model_; + // A module can only have one optional sampled image addressing mode + std::unique_ptr sampled_image_address_mode_; + InstructionList entry_points_; + InstructionList execution_modes_; + InstructionList debugs1_; + InstructionList debugs2_; + InstructionList debugs3_; + InstructionList ext_inst_debuginfo_; + InstructionList annotations_; + // Type declarations, constants, and global variable declarations. + InstructionList types_values_; + std::vector> functions_; + + // If the module ends with Op*Line instruction, they will not be attached to + // any instruction. We record them here, so they will not be lost. + std::vector trailing_dbg_line_info_; + + // This module contains DebugScope/DebugNoScope or OpLine/OpNoLine. + bool contains_debug_info_; +}; + +// Pretty-prints |module| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const Module& module); + +inline void Module::AddCapability(std::unique_ptr c) { + capabilities_.push_back(std::move(c)); +} + +inline void Module::AddExtension(std::unique_ptr e) { + extensions_.push_back(std::move(e)); +} + +inline void Module::AddExtInstImport(std::unique_ptr e) { + ext_inst_imports_.push_back(std::move(e)); +} + +inline void Module::SetMemoryModel(std::unique_ptr m) { + memory_model_ = std::move(m); +} + +inline void Module::SetSampledImageAddressMode(std::unique_ptr m) { + sampled_image_address_mode_ = std::move(m); +} + +inline void Module::AddEntryPoint(std::unique_ptr e) { + entry_points_.push_back(std::move(e)); +} + +inline void Module::AddExecutionMode(std::unique_ptr e) { + execution_modes_.push_back(std::move(e)); +} + +inline void Module::AddDebug1Inst(std::unique_ptr d) { + debugs1_.push_back(std::move(d)); +} + +inline void Module::AddDebug2Inst(std::unique_ptr d) { + debugs2_.push_back(std::move(d)); +} + +inline void Module::AddDebug3Inst(std::unique_ptr d) { + debugs3_.push_back(std::move(d)); +} + +inline void Module::AddExtInstDebugInfo(std::unique_ptr d) { + ext_inst_debuginfo_.push_back(std::move(d)); +} + +inline void Module::AddAnnotationInst(std::unique_ptr a) { + annotations_.push_back(std::move(a)); +} + +inline void Module::AddType(std::unique_ptr t) { + types_values_.push_back(std::move(t)); +} + +inline void Module::AddGlobalValue(std::unique_ptr v) { + types_values_.push_back(std::move(v)); +} + +inline void Module::AddFunctionDeclaration(std::unique_ptr f) { + // function declarations must come before function definitions. + functions_.emplace(functions_.begin(), std::move(f)); +} + +inline void Module::AddFunction(std::unique_ptr f) { + functions_.emplace_back(std::move(f)); +} + +inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; } + +inline Module::inst_iterator Module::capability_begin() { + return capabilities_.begin(); +} +inline Module::inst_iterator Module::capability_end() { + return capabilities_.end(); +} + +inline IteratorRange Module::capabilities() { + return make_range(capabilities_.begin(), capabilities_.end()); +} + +inline IteratorRange Module::capabilities() const { + return make_range(capabilities_.begin(), capabilities_.end()); +} + +inline Module::inst_iterator Module::ext_inst_import_begin() { + return ext_inst_imports_.begin(); +} +inline Module::inst_iterator Module::ext_inst_import_end() { + return ext_inst_imports_.end(); +} + +inline IteratorRange Module::ext_inst_imports() { + return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); +} + +inline IteratorRange Module::ext_inst_imports() + const { + return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); +} + +inline Module::inst_iterator Module::debug1_begin() { return debugs1_.begin(); } +inline Module::inst_iterator Module::debug1_end() { return debugs1_.end(); } + +inline IteratorRange Module::debugs1() { + return make_range(debugs1_.begin(), debugs1_.end()); +} + +inline IteratorRange Module::debugs1() const { + return make_range(debugs1_.begin(), debugs1_.end()); +} + +inline Module::inst_iterator Module::debug2_begin() { return debugs2_.begin(); } +inline Module::inst_iterator Module::debug2_end() { return debugs2_.end(); } + +inline IteratorRange Module::debugs2() { + return make_range(debugs2_.begin(), debugs2_.end()); +} + +inline IteratorRange Module::debugs2() const { + return make_range(debugs2_.begin(), debugs2_.end()); +} + +inline Module::inst_iterator Module::debug3_begin() { return debugs3_.begin(); } +inline Module::inst_iterator Module::debug3_end() { return debugs3_.end(); } + +inline IteratorRange Module::debugs3() { + return make_range(debugs3_.begin(), debugs3_.end()); +} + +inline IteratorRange Module::debugs3() const { + return make_range(debugs3_.begin(), debugs3_.end()); +} + +inline Module::inst_iterator Module::ext_inst_debuginfo_begin() { + return ext_inst_debuginfo_.begin(); +} +inline Module::inst_iterator Module::ext_inst_debuginfo_end() { + return ext_inst_debuginfo_.end(); +} + +inline IteratorRange Module::ext_inst_debuginfo() { + return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); +} + +inline IteratorRange Module::ext_inst_debuginfo() + const { + return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); +} + +inline IteratorRange Module::entry_points() { + return make_range(entry_points_.begin(), entry_points_.end()); +} + +inline IteratorRange Module::entry_points() const { + return make_range(entry_points_.begin(), entry_points_.end()); +} + +inline Module::inst_iterator Module::execution_mode_begin() { + return execution_modes_.begin(); +} +inline Module::inst_iterator Module::execution_mode_end() { + return execution_modes_.end(); +} + +inline IteratorRange Module::execution_modes() { + return make_range(execution_modes_.begin(), execution_modes_.end()); +} + +inline IteratorRange Module::execution_modes() + const { + return make_range(execution_modes_.begin(), execution_modes_.end()); +} + +inline Module::inst_iterator Module::annotation_begin() { + return annotations_.begin(); +} +inline Module::inst_iterator Module::annotation_end() { + return annotations_.end(); +} + +inline IteratorRange Module::annotations() { + return make_range(annotations_.begin(), annotations_.end()); +} + +inline IteratorRange Module::annotations() const { + return make_range(annotations_.begin(), annotations_.end()); +} + +inline Module::inst_iterator Module::extension_begin() { + return extensions_.begin(); +} +inline Module::inst_iterator Module::extension_end() { + return extensions_.end(); +} + +inline IteratorRange Module::extensions() { + return make_range(extensions_.begin(), extensions_.end()); +} + +inline IteratorRange Module::extensions() const { + return make_range(extensions_.begin(), extensions_.end()); +} + +inline Module::inst_iterator Module::types_values_begin() { + return types_values_.begin(); +} + +inline Module::inst_iterator Module::types_values_end() { + return types_values_.end(); +} + +inline IteratorRange Module::types_values() { + return make_range(types_values_.begin(), types_values_.end()); +} + +inline IteratorRange Module::types_values() const { + return make_range(types_values_.begin(), types_values_.end()); +} + +inline Module::const_iterator Module::cbegin() const { + return const_iterator(&functions_, functions_.cbegin()); +} + +inline Module::const_iterator Module::cend() const { + return const_iterator(&functions_, functions_.cend()); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MODULE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/null_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/null_pass.h new file mode 100644 index 00000000..2b5974fb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/null_pass.h @@ -0,0 +1,34 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_NULL_PASS_H_ +#define SOURCE_OPT_NULL_PASS_H_ + +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class NullPass : public Pass { + public: + const char* name() const override { return "null"; } + Status Process() override { return Status::SuccessWithoutChange; } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_NULL_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.cpp new file mode 100644 index 00000000..8684feb4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/opextinst_forward_ref_fixup_pass.h" + +#include +#include + +#include "source/extensions.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "type_manager.h" + +namespace spvtools { +namespace opt { +namespace { + +// Returns true if the instruction |inst| has a forward reference to another +// debug instruction. +// |debug_ids| contains the list of IDs belonging to debug instructions. +// |seen_ids| contains the list of IDs already seen. +bool HasForwardReference(const Instruction& inst, + const std::unordered_set& debug_ids, + const std::unordered_set& seen_ids) { + const uint32_t num_in_operands = inst.NumInOperands(); + for (uint32_t i = 0; i < num_in_operands; ++i) { + const Operand& op = inst.GetInOperand(i); + if (!spvIsIdType(op.type)) continue; + + if (debug_ids.count(op.AsId()) == 0) continue; + + if (seen_ids.count(op.AsId()) == 0) return true; + } + + return false; +} + +// Replace |inst| opcode with OpExtInstWithForwardRefsKHR or OpExtInst +// if required to comply with forward references. +bool ReplaceOpcodeIfRequired(Instruction& inst, bool hasForwardReferences) { + if (hasForwardReferences && + inst.opcode() != spv::Op::OpExtInstWithForwardRefsKHR) + inst.SetOpcode(spv::Op::OpExtInstWithForwardRefsKHR); + else if (!hasForwardReferences && inst.opcode() != spv::Op::OpExtInst) + inst.SetOpcode(spv::Op::OpExtInst); + else + return false; + return true; +} + +// Returns all the result IDs of the instructions in |range|. +std::unordered_set gatherResultIds( + const IteratorRange& range) { + std::unordered_set output; + for (const auto& it : range) output.insert(it.result_id()); + return output; +} + +} // namespace + +Pass::Status OpExtInstWithForwardReferenceFixupPass::Process() { + std::unordered_set seen_ids = + gatherResultIds(get_module()->ext_inst_imports()); + std::unordered_set debug_ids = + gatherResultIds(get_module()->ext_inst_debuginfo()); + for (uint32_t id : seen_ids) debug_ids.insert(id); + + bool moduleChanged = false; + bool hasAtLeastOneForwardReference = false; + IRContext* ctx = context(); + for (Instruction& inst : get_module()->ext_inst_debuginfo()) { + if (inst.opcode() != spv::Op::OpExtInst && + inst.opcode() != spv::Op::OpExtInstWithForwardRefsKHR) + continue; + + seen_ids.insert(inst.result_id()); + bool hasForwardReferences = HasForwardReference(inst, debug_ids, seen_ids); + hasAtLeastOneForwardReference |= hasForwardReferences; + + if (ReplaceOpcodeIfRequired(inst, hasForwardReferences)) { + moduleChanged = true; + ctx->AnalyzeUses(&inst); + } + } + + if (hasAtLeastOneForwardReference != + ctx->get_feature_mgr()->HasExtension( + kSPV_KHR_relaxed_extended_instruction)) { + if (hasAtLeastOneForwardReference) + ctx->AddExtension("SPV_KHR_relaxed_extended_instruction"); + else + ctx->RemoveExtension(Extension::kSPV_KHR_relaxed_extended_instruction); + moduleChanged = true; + } + + return moduleChanged ? Status::SuccessWithChange + : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.h new file mode 100644 index 00000000..26e5b81c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/opextinst_forward_ref_fixup_pass.h @@ -0,0 +1,48 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_OPEXTINST_FORWARD_REF_FIXUP_H +#define SOURCE_OPT_OPEXTINST_FORWARD_REF_FIXUP_H + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class OpExtInstWithForwardReferenceFixupPass : public Pass { + public: + const char* name() const override { return "fix-opextinst-opcodes"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_OPEXTINST_FORWARD_REF_FIXUP_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/optimizer.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/optimizer.cpp new file mode 100644 index 00000000..aedca998 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/optimizer.cpp @@ -0,0 +1,1303 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/optimizer.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/build_module.h" +#include "source/opt/graphics_robust_access_pass.h" +#include "source/opt/log.h" +#include "source/opt/pass_manager.h" +#include "source/opt/passes.h" +#include "source/spirv_optimizer_options.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" + +namespace spvtools { + +std::vector GetVectorOfStrings(const char** strings, + const size_t string_count) { + std::vector result; + for (uint32_t i = 0; i < string_count; i++) { + result.emplace_back(strings[i]); + } + return result; +} + +struct Optimizer::PassToken::Impl { + Impl(std::unique_ptr p) : pass(std::move(p)) {} + + std::unique_ptr pass; // Internal implementation pass. +}; + +Optimizer::PassToken::PassToken( + std::unique_ptr impl) + : impl_(std::move(impl)) {} + +Optimizer::PassToken::PassToken(std::unique_ptr&& pass) + : impl_(MakeUnique(std::move(pass))) {} + +Optimizer::PassToken::PassToken(PassToken&& that) + : impl_(std::move(that.impl_)) {} + +Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) { + impl_ = std::move(that.impl_); + return *this; +} + +Optimizer::PassToken::~PassToken() {} + +struct Optimizer::Impl { + explicit Impl(spv_target_env env) : target_env(env), pass_manager() {} + + spv_target_env target_env; // Target environment. + opt::PassManager pass_manager; // Internal implementation pass manager. + std::unordered_set live_locs; // Arg to debug dead output passes +}; + +Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) { + assert(env != SPV_ENV_WEBGPU_0); +} + +Optimizer::~Optimizer() {} + +void Optimizer::SetMessageConsumer(MessageConsumer c) { + // All passes' message consumer needs to be updated. + for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) { + impl_->pass_manager.GetPass(i)->SetMessageConsumer(c); + } + impl_->pass_manager.SetMessageConsumer(std::move(c)); +} + +const MessageConsumer& Optimizer::consumer() const { + return impl_->pass_manager.consumer(); +} + +Optimizer& Optimizer::RegisterPass(PassToken&& p) { + // Change to use the pass manager's consumer. + p.impl_->pass->SetMessageConsumer(consumer()); + impl_->pass_manager.AddPass(std::move(p.impl_->pass)); + return *this; +} + +// The legalization passes take a spir-v shader generated by an HLSL front-end +// and turn it into a valid vulkan spir-v shader. There are two ways in which +// the code will be invalid at the start: +// +// 1) There will be opaque objects, like images, which will be passed around +// in intermediate objects. Valid spir-v will have to replace the use of +// the opaque object with an intermediate object that is the result of the +// load of the global opaque object. +// +// 2) There will be variables that contain pointers to structured or uniform +// buffers. It be legal, the variables must be eliminated, and the +// references to the structured buffers must use the result of OpVariable +// in the Uniform storage class. +// +// Optimization in this list must accept shaders with these relaxation of the +// rules. There is not guarantee that this list of optimizations is able to +// legalize all inputs, but it is on a best effort basis. +// +// The legalization problem is essentially a very general copy propagation +// problem. The optimization we use are all used to either do copy propagation +// or enable more copy propagation. +Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) { + return + // Wrap OpKill instructions so all other code can be inlined. + RegisterPass(CreateWrapOpKillPass()) + // Remove unreachable block so that merge return works. + .RegisterPass(CreateDeadBranchElimPass()) + // Merge the returns so we can inline. + .RegisterPass(CreateMergeReturnPass()) + // Make sure uses and definitions are in the same function. + .RegisterPass(CreateInlineExhaustivePass()) + // Make private variable function scope + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreatePrivateToLocalPass()) + // Fix up the storage classes that DXC may have purposely generated + // incorrectly. All functions are inlined, and a lot of dead code has + // been removed. + .RegisterPass(CreateFixStorageClassPass()) + // Propagate the value stored to the loads in very simple cases. + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + // Split up aggregates so they are easier to deal with. + .RegisterPass(CreateScalarReplacementPass(0)) + // Remove loads and stores so everything is in intermediate values. + // Takes care of copy propagation of non-members. + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + // Propagate constants to get as many constant conditions on branches + // as possible. + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + // Copy propagate members. Cleans up code sequences generated by + // scalar replacement. Also important for removing OpPhi nodes. + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateCopyPropagateArraysPass()) + // May need loop unrolling here see + // https://github.com/Microsoft/DirectXShaderCompiler/pull/930 + // Get rid of unused code that contain traces of illegal code + // or unused references to unbound external objects + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateReduceLoadSizePass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()) + .RegisterPass(CreateInterpolateFixupPass()) + .RegisterPass(CreateInvocationInterlockPlacementPass()) + .RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass()); +} + +Optimizer& Optimizer::RegisterLegalizationPasses() { + return RegisterLegalizationPasses(false); +} + +Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) { + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateMergeReturnPass()) + .RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreatePrivateToLocalPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateCombineAccessChainsPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateSSARewritePass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateReduceLoadSizePass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateSimplificationPass()); +} + +Optimizer& Optimizer::RegisterPerformancePasses() { + return RegisterPerformancePasses(false); +} + +Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) { + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateMergeReturnPass()) + .RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreatePrivateToLocalPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateEliminateDeadMembersPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass(preserve_interface)) + .RegisterPass(CreateCFGCleanupPass()); +} + +Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); } + +bool Optimizer::RegisterPassesFromFlags(const std::vector& flags) { + return RegisterPassesFromFlags(flags, false); +} + +bool Optimizer::RegisterPassesFromFlags(const std::vector& flags, + bool preserve_interface) { + for (const auto& flag : flags) { + if (!RegisterPassFromFlag(flag, preserve_interface)) { + return false; + } + } + + return true; +} + +bool Optimizer::FlagHasValidForm(const std::string& flag) const { + if (flag == "-O" || flag == "-Os") { + return true; + } else if (flag.size() > 2 && flag.substr(0, 2) == "--") { + return true; + } + + Errorf(consumer(), nullptr, {}, + "%s is not a valid flag. Flag passes should have the form " + "'--pass_name[=pass_args]'. Special flag names also accepted: -O " + "and -Os.", + flag.c_str()); + return false; +} + +bool Optimizer::RegisterPassFromFlag(const std::string& flag) { + return RegisterPassFromFlag(flag, false); +} + +bool Optimizer::RegisterPassFromFlag(const std::string& flag, + bool preserve_interface) { + if (!FlagHasValidForm(flag)) { + return false; + } + + // Split flags of the form --pass_name=pass_args. + auto p = utils::SplitFlagArgs(flag); + std::string pass_name = p.first; + std::string pass_args = p.second; + + // FIXME(dnovillo): This should be re-factored so that pass names can be + // automatically checked against Pass::name() and PassToken instances created + // via a template function. Additionally, class Pass should have a desc() + // method that describes the pass (so it can be used in --help). + // + // Both Pass::name() and Pass::desc() should be static class members so they + // can be invoked without creating a pass instance. + if (pass_name == "strip-debug") { + RegisterPass(CreateStripDebugInfoPass()); + } else if (pass_name == "strip-reflect") { + RegisterPass(CreateStripReflectInfoPass()); + } else if (pass_name == "strip-nonsemantic") { + RegisterPass(CreateStripNonSemanticInfoPass()); + } else if (pass_name == "fix-opextinst-opcodes") { + RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass()); + } else if (pass_name == "set-spec-const-default-value") { + if (pass_args.size() > 0) { + auto spec_ids_vals = + opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString( + pass_args.c_str()); + if (!spec_ids_vals) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --set-spec-const-default-value: %s", + pass_args.c_str()); + return false; + } + RegisterPass( + CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid spec constant value string '%s'. Expected a string of " + ": pairs.", + pass_args.c_str()); + return false; + } + } else if (pass_name == "if-conversion") { + RegisterPass(CreateIfConversionPass()); + } else if (pass_name == "freeze-spec-const") { + RegisterPass(CreateFreezeSpecConstantValuePass()); + } else if (pass_name == "inline-entry-points-exhaustive") { + RegisterPass(CreateInlineExhaustivePass()); + } else if (pass_name == "inline-entry-points-opaque") { + RegisterPass(CreateInlineOpaquePass()); + } else if (pass_name == "combine-access-chains") { + RegisterPass(CreateCombineAccessChainsPass()); + } else if (pass_name == "convert-local-access-chains") { + RegisterPass(CreateLocalAccessChainConvertPass()); + } else if (pass_name == "replace-desc-array-access-using-var-index") { + RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); + } else if (pass_name == "spread-volatile-semantics") { + RegisterPass(CreateSpreadVolatileSemanticsPass()); + } else if (pass_name == "descriptor-scalar-replacement") { + RegisterPass(CreateDescriptorScalarReplacementPass()); + } else if (pass_name == "descriptor-composite-scalar-replacement") { + RegisterPass(CreateDescriptorCompositeScalarReplacementPass()); + } else if (pass_name == "descriptor-array-scalar-replacement") { + RegisterPass(CreateDescriptorArrayScalarReplacementPass()); + } else if (pass_name == "eliminate-dead-code-aggressive") { + RegisterPass(CreateAggressiveDCEPass(preserve_interface)); + } else if (pass_name == "eliminate-insert-extract") { + RegisterPass(CreateInsertExtractElimPass()); + } else if (pass_name == "eliminate-local-single-block") { + RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()); + } else if (pass_name == "eliminate-local-single-store") { + RegisterPass(CreateLocalSingleStoreElimPass()); + } else if (pass_name == "merge-blocks") { + RegisterPass(CreateBlockMergePass()); + } else if (pass_name == "merge-return") { + RegisterPass(CreateMergeReturnPass()); + } else if (pass_name == "eliminate-dead-branches") { + RegisterPass(CreateDeadBranchElimPass()); + } else if (pass_name == "eliminate-dead-functions") { + RegisterPass(CreateEliminateDeadFunctionsPass()); + } else if (pass_name == "eliminate-local-multi-store") { + RegisterPass(CreateLocalMultiStoreElimPass()); + } else if (pass_name == "eliminate-dead-const") { + RegisterPass(CreateEliminateDeadConstantPass()); + } else if (pass_name == "eliminate-dead-inserts") { + RegisterPass(CreateDeadInsertElimPass()); + } else if (pass_name == "eliminate-dead-variables") { + RegisterPass(CreateDeadVariableEliminationPass()); + } else if (pass_name == "eliminate-dead-members") { + RegisterPass(CreateEliminateDeadMembersPass()); + } else if (pass_name == "fold-spec-const-op-composite") { + RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); + } else if (pass_name == "loop-unswitch") { + RegisterPass(CreateLoopUnswitchPass()); + } else if (pass_name == "scalar-replacement") { + if (pass_args.size() == 0) { + RegisterPass(CreateScalarReplacementPass(0)); + } else { + int limit = -1; + if (pass_args.find_first_not_of("0123456789") == std::string::npos) { + limit = atoi(pass_args.c_str()); + } + + if (limit >= 0) { + RegisterPass(CreateScalarReplacementPass(limit)); + } else { + Error(consumer(), nullptr, {}, + "--scalar-replacement must have no arguments or a non-negative " + "integer argument"); + return false; + } + } + } else if (pass_name == "strength-reduction") { + RegisterPass(CreateStrengthReductionPass()); + } else if (pass_name == "unify-const") { + RegisterPass(CreateUnifyConstantPass()); + } else if (pass_name == "flatten-decorations") { + RegisterPass(CreateFlattenDecorationPass()); + } else if (pass_name == "compact-ids") { + RegisterPass(CreateCompactIdsPass()); + } else if (pass_name == "cfg-cleanup") { + RegisterPass(CreateCFGCleanupPass()); + } else if (pass_name == "local-redundancy-elimination") { + RegisterPass(CreateLocalRedundancyEliminationPass()); + } else if (pass_name == "loop-invariant-code-motion") { + RegisterPass(CreateLoopInvariantCodeMotionPass()); + } else if (pass_name == "reduce-load-size") { + if (pass_args.size() == 0) { + RegisterPass(CreateReduceLoadSizePass()); + } else { + double load_replacement_threshold = 0.9; + if (pass_args.find_first_not_of(".0123456789") == std::string::npos) { + load_replacement_threshold = atof(pass_args.c_str()); + } + + if (load_replacement_threshold >= 0) { + RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold)); + } else { + Error(consumer(), nullptr, {}, + "--reduce-load-size must have no arguments or a non-negative " + "double argument"); + return false; + } + } + } else if (pass_name == "redundancy-elimination") { + RegisterPass(CreateRedundancyEliminationPass()); + } else if (pass_name == "private-to-local") { + RegisterPass(CreatePrivateToLocalPass()); + } else if (pass_name == "remove-duplicates") { + RegisterPass(CreateRemoveDuplicatesPass()); + } else if (pass_name == "workaround-1209") { + RegisterPass(CreateWorkaround1209Pass()); + } else if (pass_name == "replace-invalid-opcode") { + RegisterPass(CreateReplaceInvalidOpcodePass()); + } else if (pass_name == "convert-relaxed-to-half") { + RegisterPass(CreateConvertRelaxedToHalfPass()); + } else if (pass_name == "relax-float-ops") { + RegisterPass(CreateRelaxFloatOpsPass()); + } else if (pass_name == "simplify-instructions") { + RegisterPass(CreateSimplificationPass()); + } else if (pass_name == "ssa-rewrite") { + RegisterPass(CreateSSARewritePass()); + } else if (pass_name == "copy-propagate-arrays") { + RegisterPass(CreateCopyPropagateArraysPass()); + } else if (pass_name == "loop-fission") { + int register_threshold_to_split = + (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; + if (register_threshold_to_split > 0) { + RegisterPass(CreateLoopFissionPass( + static_cast(register_threshold_to_split))); + } else { + Error(consumer(), nullptr, {}, + "--loop-fission must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-fusion") { + int max_registers_per_loop = + (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; + if (max_registers_per_loop > 0) { + RegisterPass( + CreateLoopFusionPass(static_cast(max_registers_per_loop))); + } else { + Error(consumer(), nullptr, {}, + "--loop-fusion must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-unroll") { + RegisterPass(CreateLoopUnrollPass(true)); + } else if (pass_name == "upgrade-memory-model") { + RegisterPass(CreateUpgradeMemoryModelPass()); + } else if (pass_name == "vector-dce") { + RegisterPass(CreateVectorDCEPass()); + } else if (pass_name == "loop-unroll-partial") { + int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; + if (factor > 0) { + RegisterPass(CreateLoopUnrollPass(false, factor)); + } else { + Error(consumer(), nullptr, {}, + "--loop-unroll-partial must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-peeling") { + RegisterPass(CreateLoopPeelingPass()); + } else if (pass_name == "loop-peeling-threshold") { + int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; + if (factor > 0) { + opt::LoopPeelingPass::SetLoopPeelingThreshold(factor); + } else { + Error(consumer(), nullptr, {}, + "--loop-peeling-threshold must have a positive integer argument"); + return false; + } + } else if (pass_name == "ccp") { + RegisterPass(CreateCCPPass()); + } else if (pass_name == "code-sink") { + RegisterPass(CreateCodeSinkingPass()); + } else if (pass_name == "fix-storage-class") { + RegisterPass(CreateFixStorageClassPass()); + } else if (pass_name == "O") { + RegisterPerformancePasses(preserve_interface); + } else if (pass_name == "Os") { + RegisterSizePasses(preserve_interface); + } else if (pass_name == "legalize-hlsl") { + RegisterLegalizationPasses(preserve_interface); + } else if (pass_name == "remove-unused-interface-variables") { + RegisterPass(CreateRemoveUnusedInterfaceVariablesPass()); + } else if (pass_name == "graphics-robust-access") { + RegisterPass(CreateGraphicsRobustAccessPass()); + } else if (pass_name == "wrap-opkill") { + RegisterPass(CreateWrapOpKillPass()); + } else if (pass_name == "amd-ext-to-khr") { + RegisterPass(CreateAmdExtToKhrPass()); + } else if (pass_name == "interpolate-fixup") { + RegisterPass(CreateInterpolateFixupPass()); + } else if (pass_name == "remove-dont-inline") { + RegisterPass(CreateRemoveDontInlinePass()); + } else if (pass_name == "eliminate-dead-input-components") { + RegisterPass(CreateEliminateDeadInputComponentsSafePass()); + } else if (pass_name == "fix-func-call-param") { + RegisterPass(CreateFixFuncCallArgumentsPass()); + } else if (pass_name == "convert-to-sampled-image") { + if (pass_args.size() > 0) { + auto descriptor_set_binding_pairs = + opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString( + pass_args.c_str()); + if (!descriptor_set_binding_pairs) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --convert-to-sampled-image: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateConvertToSampledImagePass( + std::move(*descriptor_set_binding_pairs))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid pairs of descriptor set and binding '%s'. Expected a " + "string of : pairs.", + pass_args.c_str()); + return false; + } + } else if (pass_name == "struct-packing") { + if (pass_args.size() == 0) { + Error(consumer(), nullptr, {}, + "--struct-packing requires a name:rule argument."); + return false; + } + + auto separator_pos = pass_args.find(':'); + if (separator_pos == std::string::npos || separator_pos == 0 || + separator_pos + 1 == pass_args.size()) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --struct-packing: %s", pass_args.c_str()); + return false; + } + + const std::string struct_name = pass_args.substr(0, separator_pos); + const std::string rule_name = pass_args.substr(separator_pos + 1); + + RegisterPass( + CreateStructPackingPass(struct_name.c_str(), rule_name.c_str())); + } else if (pass_name == "switch-descriptorset") { + if (pass_args.size() == 0) { + Error(consumer(), nullptr, {}, + "--switch-descriptorset requires a from:to argument."); + return false; + } + uint32_t from_set = 0, to_set = 0; + const char* start = pass_args.data(); + const char* end = pass_args.data() + pass_args.size(); + + auto result = std::from_chars(start, end, from_set); + if (result.ec != std::errc()) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + start = result.ptr; + if (start[0] != ':') { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + start++; + result = std::from_chars(start, end, to_set); + if (result.ec != std::errc() || result.ptr != end) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --switch-descriptorset: %s", + pass_args.c_str()); + return false; + } + RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set)); + } else if (pass_name == "modify-maximal-reconvergence") { + if (pass_args.size() == 0) { + Error(consumer(), nullptr, {}, + "--modify-maximal-reconvergence requires an argument"); + return false; + } + if (pass_args == "add") { + RegisterPass(CreateModifyMaximalReconvergencePass(true)); + } else if (pass_args == "remove") { + RegisterPass(CreateModifyMaximalReconvergencePass(false)); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --modify-maximal-reconvergence: %s (must be " + "'add' or 'remove')", + pass_args.c_str()); + return false; + } + } else if (pass_name == "trim-capabilities") { + RegisterPass(CreateTrimCapabilitiesPass()); + } else if (pass_name == "split-combined-image-sampler") { + RegisterPass(CreateSplitCombinedImageSamplerPass()); + } else if (pass_name == "resolve-binding-conflicts") { + RegisterPass(CreateResolveBindingConflictsPass()); + } else { + Errorf(consumer(), nullptr, {}, + "Unknown flag '--%s'. Use --help for a list of valid flags", + pass_name.c_str()); + return false; + } + + return true; +} + +void Optimizer::SetTargetEnv(const spv_target_env env) { + impl_->target_env = env; +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary) const { + return Run(original_binary, original_binary_size, optimized_binary, + OptimizerOptions()); +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary, + const ValidatorOptions& validator_options, + bool skip_validation) const { + OptimizerOptions opt_options; + opt_options.set_run_validator(!skip_validation); + opt_options.set_validator_options(validator_options); + return Run(original_binary, original_binary_size, optimized_binary, + opt_options); +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const { + spvtools::SpirvTools tools(impl_->target_env); + tools.SetMessageConsumer(impl_->pass_manager.consumer()); + if (opt_options->run_validator_ && + !tools.Validate(original_binary, original_binary_size, + &opt_options->val_options_)) { + return false; + } + + std::unique_ptr context = BuildModule( + impl_->target_env, consumer(), original_binary, original_binary_size); + if (context == nullptr) return false; + + context->set_max_id_bound(opt_options->max_id_bound_); + context->set_preserve_bindings(opt_options->preserve_bindings_); + context->set_preserve_spec_constants(opt_options->preserve_spec_constants_); + + impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_); + impl_->pass_manager.SetTargetEnv(impl_->target_env); + auto status = impl_->pass_manager.Run(context.get()); + + if (status == opt::Pass::Status::Failure) { + return false; + } + +#ifndef NDEBUG + // We do not keep the result id of DebugScope in struct DebugScope. + // Instead, we assign random ids for them, which results in integrity + // check failures. In addition, propagating the OpLine/OpNoLine to preserve + // the debug information through transformations results in integrity + // check failures. We want to skip the integrity check when the module + // contains DebugScope or OpLine/OpNoLine instructions. + if (status == opt::Pass::Status::SuccessWithoutChange && + !context->module()->ContainsDebugInfo()) { + std::vector optimized_binary_with_nop; + context->module()->ToBinary(&optimized_binary_with_nop, + /* skip_nop = */ false); + assert(optimized_binary_with_nop.size() == original_binary_size && + "Binary size unexpectedly changed despite the optimizer saying " + "there was no change"); + + // Compare the magic number to make sure the binaries were encoded in the + // endianness. If not, the contents of the binaries will be different, so + // do not check the contents. + if (optimized_binary_with_nop[0] == original_binary[0]) { + assert(memcmp(optimized_binary_with_nop.data(), original_binary, + original_binary_size) == 0 && + "Binary content unexpectedly changed despite the optimizer saying " + "there was no change"); + } + } +#endif // !NDEBUG + + // Note that |original_binary| and |optimized_binary| may share the same + // buffer and the below will invalidate |original_binary|. + optimized_binary->clear(); + context->module()->ToBinary(optimized_binary, /* skip_nop = */ true); + + return true; +} + +Optimizer& Optimizer::SetPrintAll(std::ostream* out) { + impl_->pass_manager.SetPrintAll(out); + return *this; +} + +Optimizer& Optimizer::SetTimeReport(std::ostream* out) { + impl_->pass_manager.SetTimeReport(out); + return *this; +} + +Optimizer& Optimizer::SetValidateAfterAll(bool validate) { + impl_->pass_manager.SetValidateAfterAll(validate); + return *this; +} + +Optimizer::PassToken CreateNullPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateStripDebugInfoPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStripReflectInfoPass() { + return CreateStripNonSemanticInfoPass(); +} + +Optimizer::PassToken CreateStripNonSemanticInfoPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadFunctionsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadMembersPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map& id_value_map) { + return MakeUnique( + MakeUnique(id_value_map)); +} + +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map>& id_value_map) { + return MakeUnique( + MakeUnique(id_value_map)); +} + +Optimizer::PassToken CreateFlattenDecorationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFreezeSpecConstantValuePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateUnifyConstantPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadConstantPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadVariableEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStrengthReductionPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateBlockMergePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInlineExhaustivePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInlineOpaquePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalAccessChainConvertPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalSingleStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInsertExtractElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadInsertElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadBranchElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalMultiStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateAggressiveDCEPass() { + return MakeUnique( + MakeUnique(false, false)); +} + +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { + return MakeUnique( + MakeUnique(preserve_interface, false)); +} + +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface, + bool remove_outputs) { + return MakeUnique( + MakeUnique(preserve_interface, remove_outputs)); +} + +Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreatePropagateLineInfoPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateRedundantLineInfoElimPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateCompactIdsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateMergeReturnPass() { + return MakeUnique( + MakeUnique()); +} + +std::vector Optimizer::GetPassNames() const { + std::vector v; + for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) { + v.push_back(impl_->pass_manager.GetPass(i)->name()); + } + return v; +} + +Optimizer::PassToken CreateCFGCleanupPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalRedundancyEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopFissionPass(size_t threshold) { + return MakeUnique( + MakeUnique(threshold)); +} + +Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) { + return MakeUnique( + MakeUnique(max_registers_per_loop)); +} + +Optimizer::PassToken CreateLoopInvariantCodeMotionPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateLoopPeelingPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopUnswitchPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRedundancyEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRemoveDuplicatesPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) { + return MakeUnique( + MakeUnique(size_limit)); +} + +Optimizer::PassToken CreatePrivateToLocalPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCCPPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateWorkaround1209Pass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateIfConversionPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateReplaceInvalidOpcodePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSimplificationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) { + return MakeUnique( + MakeUnique(fully_unroll, factor)); +} + +Optimizer::PassToken CreateSSARewritePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCopyPropagateArraysPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateVectorDCEPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateReduceLoadSizePass( + double load_replacement_threshold) { + return MakeUnique( + MakeUnique(load_replacement_threshold)); +} + +Optimizer::PassToken CreateCombineAccessChainsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateUpgradeMemoryModelPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateConvertRelaxedToHalfPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRelaxFloatOpsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCodeSinkingPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFixStorageClassPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateGraphicsRobustAccessPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSpreadVolatileSemanticsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDescriptorScalarReplacementPass() { + return MakeUnique( + MakeUnique( + /* flatten_composites= */ true, /* flatten_arrays= */ true)); +} + +Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() { + return MakeUnique( + MakeUnique( + /* flatten_composites= */ true, /* flatten_arrays= */ false)); +} + +Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() { + return MakeUnique( + MakeUnique( + /* flatten_composites= */ false, /* flatten_arrays= */ true)); +} + +Optimizer::PassToken CreateWrapOpKillPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateAmdExtToKhrPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInterpolateFixupPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadInputComponentsPass() { + return MakeUnique( + MakeUnique(spv::StorageClass::Input, + /* safe_mode */ false)); +} + +Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() { + return MakeUnique( + MakeUnique(spv::StorageClass::Output, + /* safe_mode */ false)); +} + +Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() { + return MakeUnique( + MakeUnique(spv::StorageClass::Input, + /* safe_mode */ true)); +} + +Optimizer::PassToken CreateAnalyzeLiveInputPass( + std::unordered_set* live_locs, + std::unordered_set* live_builtins) { + return MakeUnique( + MakeUnique(live_locs, live_builtins)); +} + +Optimizer::PassToken CreateEliminateDeadOutputStoresPass( + std::unordered_set* live_locs, + std::unordered_set* live_builtins) { + return MakeUnique( + MakeUnique(live_locs, live_builtins)); +} + +Optimizer::PassToken CreateConvertToSampledImagePass( + const std::vector& + descriptor_set_binding_pairs) { + return MakeUnique( + MakeUnique(descriptor_set_binding_pairs)); +} + +Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRemoveDontInlinePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFixFuncCallArgumentsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateTrimCapabilitiesPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStructPackingPass(const char* structToPack, + const char* packingRule) { + return MakeUnique( + MakeUnique( + structToPack, + opt::StructPackingPass::ParsePackingRuleFromString(packingRule))); +} + +Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) { + return MakeUnique( + MakeUnique(from, to)); +} + +Optimizer::PassToken CreateInvocationInterlockPlacementPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) { + return MakeUnique( + MakeUnique(add)); +} + +Optimizer::PassToken CreateOpExtInstWithForwardReferenceFixupPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSplitCombinedImageSamplerPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateResolveBindingConflictsPass() { + return MakeUnique( + MakeUnique()); +} + +} // namespace spvtools + +extern "C" { + +SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) { + return reinterpret_cast(new spvtools::Optimizer(env)); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) { + delete reinterpret_cast(optimizer); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer( + spv_optimizer_t* optimizer, spv_message_consumer consumer) { + reinterpret_cast(optimizer)-> + SetMessageConsumer( + [consumer](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + return consumer(level, source, &position, message); + }); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses( + spv_optimizer_t* optimizer) { + reinterpret_cast(optimizer)-> + RegisterLegalizationPasses(); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses( + spv_optimizer_t* optimizer) { + reinterpret_cast(optimizer)-> + RegisterPerformancePasses(); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses( + spv_optimizer_t* optimizer) { + reinterpret_cast(optimizer)->RegisterSizePasses(); +} + +SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag( + spv_optimizer_t* optimizer, const char* flag) +{ + return reinterpret_cast(optimizer)-> + RegisterPassFromFlag(flag); +} + +SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { + std::vector opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast(optimizer) + ->RegisterPassesFromFlags(opt_flags, false); +} + +SPIRV_TOOLS_EXPORT bool +spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface( + spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) { + std::vector opt_flags = + spvtools::GetVectorOfStrings(flags, flag_count); + return reinterpret_cast(optimizer) + ->RegisterPassesFromFlags(opt_flags, true); +} + +SPIRV_TOOLS_EXPORT +spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer, + const uint32_t* binary, + const size_t word_count, + spv_binary* optimized_binary, + const spv_optimizer_options options) { + std::vector optimized; + + if (!reinterpret_cast(optimizer)-> + Run(binary, word_count, &optimized, options)) { + return SPV_ERROR_INTERNAL; + } + + auto result_binary = new spv_binary_t(); + if (!result_binary) { + *optimized_binary = nullptr; + return SPV_ERROR_OUT_OF_MEMORY; + } + + result_binary->code = new uint32_t[optimized.size()]; + if (!result_binary->code) { + delete result_binary; + *optimized_binary = nullptr; + return SPV_ERROR_OUT_OF_MEMORY; + } + result_binary->wordCount = optimized.size(); + + memcpy(result_binary->code, optimized.data(), + optimized.size() * sizeof(uint32_t)); + + *optimized_binary = result_binary; + + return SPV_SUCCESS; +} + +} // extern "C" diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/pass.cpp new file mode 100644 index 00000000..0f260e2e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pass.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/pass.h" + +#include "source/opt/ir_builder.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kTypePointerTypeIdInIdx = 1; +} // namespace + +Pass::Pass() : consumer_(nullptr), context_(nullptr), already_run_(false) {} + +Pass::Status Pass::Run(IRContext* ctx) { + if (already_run_) { + return Status::Failure; + } + already_run_ = true; + + context_ = ctx; + Pass::Status status = Process(); + context_ = nullptr; + + if (status == Status::SuccessWithChange) { + ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); + } + if (!(status == Status::Failure || ctx->IsConsistent())) + assert(false && "An analysis in the context is out of date."); + return status; +} + +uint32_t Pass::GetPointeeTypeId(const Instruction* ptrInst) const { + const uint32_t ptrTypeId = ptrInst->type_id(); + const Instruction* ptrTypeInst = get_def_use_mgr()->GetDef(ptrTypeId); + return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); +} + +Instruction* Pass::GetBaseType(uint32_t ty_id) { + Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id); + if (ty_inst->opcode() == spv::Op::OpTypeMatrix) { + uint32_t vty_id = ty_inst->GetSingleWordInOperand(0); + ty_inst = get_def_use_mgr()->GetDef(vty_id); + } + if (ty_inst->opcode() == spv::Op::OpTypeVector) { + uint32_t cty_id = ty_inst->GetSingleWordInOperand(0); + ty_inst = get_def_use_mgr()->GetDef(cty_id); + } + return ty_inst; +} + +bool Pass::IsFloat(uint32_t ty_id, uint32_t width) { + Instruction* ty_inst = GetBaseType(ty_id); + if (ty_inst->opcode() != spv::Op::OpTypeFloat) return false; + return ty_inst->GetSingleWordInOperand(0) == width; +} + +uint32_t Pass::GetNullId(uint32_t type_id) { + if (IsFloat(type_id, 16)) context()->AddCapability(spv::Capability::Float16); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + Instruction* null_inst = + const_mgr->GetDefiningInstruction(null_const, type_id); + return null_inst->result_id(); +} + +uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id, + Instruction* insertion_position) { + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + uint32_t original_type_id = object_to_copy->type_id(); + if (original_type_id == new_type_id) { + return object_to_copy->result_id(); + } + + InstructionBuilder ir_builder( + context(), insertion_position, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + + Instruction* original_type = get_def_use_mgr()->GetDef(original_type_id); + Instruction* new_type = get_def_use_mgr()->GetDef(new_type_id); + + if (new_type->opcode() != original_type->opcode()) { + return 0; + } + + switch (original_type->opcode()) { + case spv::Op::OpTypeArray: { + uint32_t original_element_type_id = + original_type->GetSingleWordInOperand(0); + uint32_t new_element_type_id = new_type->GetSingleWordInOperand(0); + + std::vector element_ids; + uint32_t length_id = original_type->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + const_mgr->FindDeclaredConstant(length_id); + assert(length_const->AsIntConstant()); + uint32_t array_length = length_const->AsIntConstant()->GetU32(); + for (uint32_t i = 0; i < array_length; i++) { + Instruction* extract = ir_builder.AddCompositeExtract( + original_element_type_id, object_to_copy->result_id(), {i}); + uint32_t new_id = + GenerateCopy(extract, new_element_type_id, insertion_position); + if (new_id == 0) { + return 0; + } + element_ids.push_back(new_id); + } + + return ir_builder.AddCompositeConstruct(new_type_id, element_ids) + ->result_id(); + } + case spv::Op::OpTypeStruct: { + std::vector element_ids; + for (uint32_t i = 0; i < original_type->NumInOperands(); i++) { + uint32_t orig_member_type_id = original_type->GetSingleWordInOperand(i); + uint32_t new_member_type_id = new_type->GetSingleWordInOperand(i); + Instruction* extract = ir_builder.AddCompositeExtract( + orig_member_type_id, object_to_copy->result_id(), {i}); + uint32_t new_id = + GenerateCopy(extract, new_member_type_id, insertion_position); + if (new_id == 0) { + return 0; + } + element_ids.push_back(new_id); + } + return ir_builder.AddCompositeConstruct(new_type_id, element_ids) + ->result_id(); + } + default: + // If we do not have an aggregate type, then we have a problem. Either we + // found multiple instances of the same type, or we are copying to an + // incompatible type. Either way the code is illegal. Leave the code as + // is and let the caller deal with it. + return 0; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/pass.h new file mode 100644 index 00000000..3e6c4d07 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pass.h @@ -0,0 +1,172 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASS_H_ +#define SOURCE_OPT_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" +#include "types.h" + +// Avoid unused variable warning/error on Linux +#ifndef NDEBUG +#define USE_ASSERT(x) assert(x) +#else +#define USE_ASSERT(x) ((void)(x)) +#endif + +namespace spvtools { +namespace opt { + +// Abstract class of a pass. All passes should implement this abstract class +// and all analysis and transformation is done via the Process() method. +class Pass { + public: + // The status of processing a module using a pass. + // + // The numbers for the cases are assigned to make sure that Failure & anything + // is Failure, SuccessWithChange & any success is SuccessWithChange. + enum class Status { + Failure = 0x00, + SuccessWithChange = 0x10, + SuccessWithoutChange = 0x11, + }; + + using ProcessFunction = std::function; + + // Destructs the pass. + virtual ~Pass() = default; + + // Returns a descriptive name for this pass. + // + // NOTE: When deriving a new pass class, make sure you make the name + // compatible with the corresponding spirv-opt command-line flag. For example, + // if you add the flag --my-pass to spirv-opt, make this function return + // "my-pass" (no leading hyphens). + virtual const char* name() const = 0; + + // Sets the message consumer to the given |consumer|. |consumer| which will be + // invoked every time there is a message to be communicated to the outside. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Returns the reference to the message consumer for this pass. + const MessageConsumer& consumer() const { return consumer_; } + + // Returns the def-use manager used for this pass. TODO(dnovillo): This should + // be handled by the pass manager. + analysis::DefUseManager* get_def_use_mgr() const { + return context()->get_def_use_mgr(); + } + + analysis::DecorationManager* get_decoration_mgr() const { + return context()->get_decoration_mgr(); + } + + FeatureManager* get_feature_mgr() const { + return context()->get_feature_mgr(); + } + + // Returns a pointer to the current module for this pass. + Module* get_module() const { return context_->module(); } + + // Sets the pointer to the current context for this pass. + void SetContextForTesting(IRContext* ctx) { context_ = ctx; } + + // Returns a pointer to the current context for this pass. + IRContext* context() const { return context_; } + + // Returns a pointer to the CFG for current module. + CFG* cfg() const { return context()->cfg(); } + + // Run the pass on the given |module|. Returns Status::Failure if errors occur + // when processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes are made to the module. If there + // were any changes it will also invalidate the analyses in the IRContext + // that are not preserved. + // + // It is an error if |Run| is called twice with the same instance of the pass. + // If this happens the return value will be |Failure|. + Status Run(IRContext* ctx); + + // Returns the set of analyses that the pass is guaranteed to preserve. + virtual IRContext::Analysis GetPreservedAnalyses() { + return IRContext::kAnalysisNone; + } + + // Return type id for |ptrInst|'s pointee + uint32_t GetPointeeTypeId(const Instruction* ptrInst) const; + + // Return base type of |ty_id| type + Instruction* GetBaseType(uint32_t ty_id); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and |width| + bool IsFloat(uint32_t ty_id, uint32_t width); + + // Return the id of OpConstantNull of type |type_id|. Create if necessary. + uint32_t GetNullId(uint32_t type_id); + + protected: + // Constructs a new pass. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + Pass(); + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes are made to the module. + virtual Status Process() = 0; + + // Return the next available SSA id and increment it. + // TODO(1841): Handle id overflow. + uint32_t TakeNextId() { return context_->TakeNextId(); } + + // Returns the id whose value is the same as |object_to_copy| except its type + // is |new_type_id|. Any instructions needed to generate this value will be + // inserted before |insertion_position|. Returns 0 if a copy could not be + // done. + uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id, + Instruction* insertion_position); + + private: + MessageConsumer consumer_; // Message consumer. + + // The context that this pass belongs to. + IRContext* context_; + + // An instance of a pass can only be run once because it is too hard to + // enforce proper resetting of internal state for each instance. This member + // is used to check that we do not run the same instance twice. + bool already_run_; +}; + +inline Pass::Status CombineStatus(Pass::Status a, Pass::Status b) { + return std::min(a, b); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.cpp new file mode 100644 index 00000000..d3c47e7f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/pass_manager.h" + +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/util/timer.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +namespace opt { + +Pass::Status PassManager::Run(IRContext* context) { + auto status = Pass::Status::SuccessWithoutChange; + + // If print_all_stream_ is not null, prints the disassembly of the module + // to that stream, with the given preamble and optionally the pass name. + auto print_disassembly = [&context, this](const char* preamble, Pass* pass) { + if (print_all_stream_) { + std::vector binary; + context->module()->ToBinary(&binary, false); + SpirvTools t(target_env_); + t.SetMessageConsumer(consumer()); + std::string disassembly; + std::string pass_name = (pass ? pass->name() : ""); + if (!t.Disassemble(binary, &disassembly)) { + std::string msg = "Disassembly failed before pass "; + msg += pass_name + "\n"; + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_WARNING, "", null_pos, msg.c_str()); + return; + } + *print_all_stream_ << preamble << pass_name << "\n" + << disassembly << std::endl; + } + }; + + SPIRV_TIMER_DESCRIPTION(time_report_stream_, /* measure_mem_usage = */ true); + for (auto& pass : passes_) { + print_disassembly("; IR before pass ", pass.get()); + SPIRV_TIMER_SCOPED(time_report_stream_, (pass ? pass->name() : ""), true); + const auto one_status = pass->Run(context); + if (one_status == Pass::Status::Failure) return one_status; + if (one_status == Pass::Status::SuccessWithChange) status = one_status; + + if (validate_after_all_) { + spvtools::SpirvTools tools(target_env_); + tools.SetMessageConsumer(consumer()); + std::vector binary; + context->module()->ToBinary(&binary, true); + if (!tools.Validate(binary.data(), binary.size(), val_options_)) { + std::string msg = "Validation failed after pass "; + msg += pass->name(); + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_INTERNAL_ERROR, "", null_pos, msg.c_str()); + return Pass::Status::Failure; + } + } + + // Reset the pass to free any memory used by the pass. + pass.reset(nullptr); + } + print_disassembly("; IR after last pass", nullptr); + + // Set the Id bound in the header in case a pass forgot to do so. + // + // TODO(dnovillo): This should be unnecessary and automatically maintained by + // the IRContext. + if (status == Pass::Status::SuccessWithChange) { + context->module()->SetIdBound(context->module()->ComputeIdBound()); + } + passes_.clear(); + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.h new file mode 100644 index 00000000..11961a33 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pass_manager.h @@ -0,0 +1,158 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASS_MANAGER_H_ +#define SOURCE_OPT_PASS_MANAGER_H_ + +#include +#include +#include +#include + +#include "source/opt/log.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +// The pass manager, responsible for tracking and running passes. +// Clients should first call AddPass() to add passes and then call Run() +// to run on a module. Passes are executed in the exact order of addition. +class PassManager { + public: + // Constructs a pass manager. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + PassManager() + : consumer_(nullptr), + print_all_stream_(nullptr), + time_report_stream_(nullptr), + target_env_(SPV_ENV_UNIVERSAL_1_2), + val_options_(nullptr), + validate_after_all_(false) {} + + // Sets the message consumer to the given |consumer|. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Adds an externally constructed pass. + void AddPass(std::unique_ptr pass); + // Uses the argument |args| to construct a pass instance of type |T|, and adds + // the pass instance to this pass manager. The pass added will use this pass + // manager's message consumer. + template + void AddPass(Args&&... args); + + // Returns the number of passes added. + uint32_t NumPasses() const; + // Returns a pointer to the |index|th pass added. + inline Pass* GetPass(uint32_t index) const; + + // Returns the message consumer. + inline const MessageConsumer& consumer() const; + + // Runs all passes on the given |module|. Returns Status::Failure if errors + // occur when processing using one of the registered passes. All passes + // registered after the error-reporting pass will be skipped. Returns the + // corresponding Status::Success if processing is successful to indicate + // whether changes are made to the module. + // + // After running all the passes, they are removed from the list. + Pass::Status Run(IRContext* context); + + // Sets the option to print the disassembly before each pass and after the + // last pass. Output is written to |out| if that is not null. No output + // is generated if |out| is null. + PassManager& SetPrintAll(std::ostream* out) { + print_all_stream_ = out; + return *this; + } + + // Sets the option to print the resource utilization of each pass. Output is + // written to |out| if that is not null. No output is generated if |out| is + // null. + PassManager& SetTimeReport(std::ostream* out) { + time_report_stream_ = out; + return *this; + } + + // Sets the target environment for validation. + PassManager& SetTargetEnv(spv_target_env env) { + target_env_ = env; + return *this; + } + + // Sets the validation options. + PassManager& SetValidatorOptions(spv_validator_options options) { + val_options_ = options; + return *this; + } + + // Sets the option to validate after each pass. + PassManager& SetValidateAfterAll(bool validate) { + validate_after_all_ = validate; + return *this; + } + + private: + // Consumer for messages. + MessageConsumer consumer_; + // A vector of passes. Order matters. + std::vector> passes_; + // The output stream to write disassembly to before each pass, and after + // the last pass. If this is null, no output is generated. + std::ostream* print_all_stream_; + // The output stream to write the resource utilization of each pass. If this + // is null, no output is generated. + std::ostream* time_report_stream_; + // The target environment. + spv_target_env target_env_; + // The validator options (used when validating each pass). + spv_validator_options val_options_; + // Controls whether validation occurs after every pass. + bool validate_after_all_; +}; + +inline void PassManager::AddPass(std::unique_ptr pass) { + passes_.push_back(std::move(pass)); +} + +template +inline void PassManager::AddPass(Args&&... args) { + passes_.emplace_back(new T(std::forward(args)...)); + passes_.back()->SetMessageConsumer(consumer_); +} + +inline uint32_t PassManager::NumPasses() const { + return static_cast(passes_.size()); +} + +inline Pass* PassManager::GetPass(uint32_t index) const { + SPIRV_ASSERT(consumer_, index < passes_.size(), "index out of bound"); + return passes_[index].get(); +} + +inline const MessageConsumer& PassManager::consumer() const { + return consumer_; +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PASS_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/passes.h b/engine/engine/3rdlibs/spirv-tools/source/opt/passes.h new file mode 100644 index 00000000..6c329787 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/passes.h @@ -0,0 +1,96 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASSES_H_ +#define SOURCE_OPT_PASSES_H_ + +// A single header to include all passes. + +#include "source/opt/aggressive_dead_code_elim_pass.h" +#include "source/opt/amd_ext_to_khr.h" +#include "source/opt/analyze_live_input_pass.h" +#include "source/opt/block_merge_pass.h" +#include "source/opt/ccp_pass.h" +#include "source/opt/cfg_cleanup_pass.h" +#include "source/opt/code_sink.h" +#include "source/opt/combine_access_chains.h" +#include "source/opt/compact_ids_pass.h" +#include "source/opt/convert_to_half_pass.h" +#include "source/opt/convert_to_sampled_image_pass.h" +#include "source/opt/copy_prop_arrays.h" +#include "source/opt/dead_branch_elim_pass.h" +#include "source/opt/dead_insert_elim_pass.h" +#include "source/opt/dead_variable_elimination.h" +#include "source/opt/desc_sroa.h" +#include "source/opt/eliminate_dead_constant_pass.h" +#include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_io_components_pass.h" +#include "source/opt/eliminate_dead_members_pass.h" +#include "source/opt/eliminate_dead_output_stores_pass.h" +#include "source/opt/empty_pass.h" +#include "source/opt/fix_func_call_arguments.h" +#include "source/opt/fix_storage_class.h" +#include "source/opt/flatten_decoration_pass.h" +#include "source/opt/fold_spec_constant_op_and_composite_pass.h" +#include "source/opt/freeze_spec_constant_value_pass.h" +#include "source/opt/graphics_robust_access_pass.h" +#include "source/opt/if_conversion.h" +#include "source/opt/inline_exhaustive_pass.h" +#include "source/opt/inline_opaque_pass.h" +#include "source/opt/interface_var_sroa.h" +#include "source/opt/interp_fixup_pass.h" +#include "source/opt/invocation_interlock_placement_pass.h" +#include "source/opt/licm_pass.h" +#include "source/opt/local_access_chain_convert_pass.h" +#include "source/opt/local_redundancy_elimination.h" +#include "source/opt/local_single_block_elim_pass.h" +#include "source/opt/local_single_store_elim_pass.h" +#include "source/opt/loop_fission.h" +#include "source/opt/loop_fusion_pass.h" +#include "source/opt/loop_peeling.h" +#include "source/opt/loop_unroller.h" +#include "source/opt/loop_unswitch_pass.h" +#include "source/opt/merge_return_pass.h" +#include "source/opt/modify_maximal_reconvergence.h" +#include "source/opt/null_pass.h" +#include "source/opt/opextinst_forward_ref_fixup_pass.h" +#include "source/opt/private_to_local_pass.h" +#include "source/opt/reduce_load_size.h" +#include "source/opt/redundancy_elimination.h" +#include "source/opt/relax_float_ops_pass.h" +#include "source/opt/remove_dontinline_pass.h" +#include "source/opt/remove_duplicates_pass.h" +#include "source/opt/remove_unused_interface_variables_pass.h" +#include "source/opt/replace_desc_array_access_using_var_index.h" +#include "source/opt/replace_invalid_opc.h" +#include "source/opt/resolve_binding_conflicts_pass.h" +#include "source/opt/scalar_replacement_pass.h" +#include "source/opt/set_spec_constant_default_value_pass.h" +#include "source/opt/simplification_pass.h" +#include "source/opt/split_combined_image_sampler_pass.h" +#include "source/opt/spread_volatile_semantics.h" +#include "source/opt/ssa_rewrite_pass.h" +#include "source/opt/strength_reduction_pass.h" +#include "source/opt/strip_debug_info_pass.h" +#include "source/opt/strip_nonsemantic_info_pass.h" +#include "source/opt/struct_packing_pass.h" +#include "source/opt/switch_descriptorset_pass.h" +#include "source/opt/trim_capabilities_pass.h" +#include "source/opt/unify_const_pass.h" +#include "source/opt/upgrade_memory_model.h" +#include "source/opt/vector_dce.h" +#include "source/opt/workaround1209.h" +#include "source/opt/wrap_opkill.h" + +#endif // SOURCE_OPT_PASSES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.cpp new file mode 100644 index 00000000..f45448dc --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source_opt.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.h b/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.h new file mode 100644 index 00000000..73566510 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/pch_source_opt.h @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "source/opt/basic_block.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.cpp new file mode 100644 index 00000000..4904e058 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.cpp @@ -0,0 +1,236 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/private_to_local_pass.h" + +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kVariableStorageClassInIdx = 0; +constexpr uint32_t kSpvTypePointerTypeIdInIdx = 1; +} // namespace + +Pass::Status PrivateToLocalPass::Process() { + bool modified = false; + + // Private variables require the shader capability. If this is not a shader, + // there is no work to do. + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses)) + return Status::SuccessWithoutChange; + + std::vector> variables_to_move; + std::unordered_set localized_variables; + for (auto& inst : context()->types_values()) { + if (inst.opcode() != spv::Op::OpVariable) { + continue; + } + + if (spv::StorageClass(inst.GetSingleWordInOperand( + kVariableStorageClassInIdx)) != spv::StorageClass::Private) { + continue; + } + + Function* target_function = FindLocalFunction(inst); + if (target_function != nullptr) { + variables_to_move.push_back({&inst, target_function}); + } + } + + modified = !variables_to_move.empty(); + for (auto p : variables_to_move) { + if (!MoveVariable(p.first, p.second)) { + return Status::Failure; + } + localized_variables.insert(p.first->result_id()); + } + + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // In SPIR-V 1.4 and later entry points must list private storage class + // variables that are statically used by the entry point. Go through the + // entry points and remove any references to variables that were localized. + for (auto& entry : get_module()->entry_points()) { + std::vector new_operands; + for (uint32_t i = 0; i < entry.NumInOperands(); ++i) { + // Execution model, function id and name are always kept. + if (i < 3 || + !localized_variables.count(entry.GetSingleWordInOperand(i))) { + new_operands.push_back(entry.GetInOperand(i)); + } + } + if (new_operands.size() != entry.NumInOperands()) { + entry.SetInOperands(std::move(new_operands)); + context()->AnalyzeUses(&entry); + } + } + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const { + bool found_first_use = false; + Function* target_function = nullptr; + context()->get_def_use_mgr()->ForEachUser( + inst.result_id(), + [&target_function, &found_first_use, this](Instruction* use) { + BasicBlock* current_block = context()->get_instr_block(use); + if (current_block == nullptr) { + return; + } + + if (!IsValidUse(use)) { + found_first_use = true; + target_function = nullptr; + return; + } + Function* current_function = current_block->GetParent(); + if (!found_first_use) { + found_first_use = true; + target_function = current_function; + } else if (target_function != current_function) { + target_function = nullptr; + } + }); + return target_function; +} // namespace opt + +bool PrivateToLocalPass::MoveVariable(Instruction* variable, + Function* function) { + // The variable needs to be removed from the global section, and placed in the + // header of the function. First step remove from the global list. + variable->RemoveFromList(); + std::unique_ptr var(variable); // Take ownership. + context()->ForgetUses(variable); + + // Update the storage class of the variable. + variable->SetInOperand(kVariableStorageClassInIdx, + {uint32_t(spv::StorageClass::Function)}); + + // Update the type as well. + uint32_t new_type_id = GetNewType(variable->type_id()); + if (new_type_id == 0) { + return false; + } + variable->SetResultType(new_type_id); + + // Place the variable at the start of the first basic block. + context()->AnalyzeUses(variable); + context()->set_instr_block(variable, &*function->begin()); + function->begin()->begin()->InsertBefore(std::move(var)); + + // Update uses where the type may have changed. + return UpdateUses(variable); +} + +uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { + auto type_mgr = context()->get_type_mgr(); + Instruction* old_type_inst = get_def_use_mgr()->GetDef(old_type_id); + uint32_t pointee_type_id = + old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx); + uint32_t new_type_id = + type_mgr->FindPointerToType(pointee_type_id, spv::StorageClass::Function); + if (new_type_id != 0) { + context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id)); + } + return new_type_id; +} + +bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { + // The cases in this switch have to match the cases in |UpdateUse|. + // If we don't know how to update it, it is not valid. + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { + return true; + } + switch (inst->opcode()) { + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpImageTexelPointer: // Treat like a load + return true; + case spv::Op::OpAccessChain: + return context()->get_def_use_mgr()->WhileEachUser( + inst, [this](const Instruction* user) { + if (!IsValidUse(user)) return false; + return true; + }); + case spv::Op::OpName: + return true; + default: + return spvOpcodeIsDecoration(inst->opcode()); + } +} + +bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) { + // The cases in this switch have to match the cases in |IsValidUse|. If we + // don't think it is valid, the optimization will not view the variable as a + // candidate, and therefore the use will not be updated. + if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) { + context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst, + user); + return true; + } + switch (inst->opcode()) { + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpImageTexelPointer: // Treat like a load + // The type is fine because it is the type pointed to, and that does not + // change. + break; + case spv::Op::OpAccessChain: { + context()->ForgetUses(inst); + uint32_t new_type_id = GetNewType(inst->type_id()); + if (new_type_id == 0) { + return false; + } + inst->SetResultType(new_type_id); + context()->AnalyzeUses(inst); + + // Update uses where the type may have changed. + if (!UpdateUses(inst)) { + return false; + } + } break; + case spv::Op::OpName: + case spv::Op::OpEntryPoint: // entry points will be updated separately. + break; + default: + assert(spvOpcodeIsDecoration(inst->opcode()) && + "Do not know how to update the type for this instruction."); + break; + } + return true; +} + +bool PrivateToLocalPass::UpdateUses(Instruction* inst) { + uint32_t id = inst->result_id(); + std::vector uses; + context()->get_def_use_mgr()->ForEachUser( + id, [&uses](Instruction* use) { uses.push_back(use); }); + + for (Instruction* use : uses) { + if (!UpdateUse(use, inst)) { + return false; + } + } + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.h new file mode 100644 index 00000000..e96a965e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/private_to_local_pass.h @@ -0,0 +1,73 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ +#define SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass implements total redundancy elimination. This is the same as +// local redundancy elimination except it looks across basic block boundaries. +// An instruction, inst, is totally redundant if there is another instruction +// that dominates inst, and also computes the same value. +class PrivateToLocalPass : public Pass { + public: + const char* name() const override { return "private-to-local"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Moves |variable| from the private storage class to the function storage + // class of |function|. Returns false if the variable could not be moved. + bool MoveVariable(Instruction* variable, Function* function); + + // |inst| is an instruction declaring a variable. If that variable is + // referenced in a single function and all of uses are valid as defined by + // |IsValidUse|, then that function is returned. Otherwise, the return + // value is |nullptr|. + Function* FindLocalFunction(const Instruction& inst) const; + + // Returns true is |inst| is a valid use of a pointer. In this case, a + // valid use is one where the transformation is able to rewrite the type to + // match a change in storage class of the original variable. + bool IsValidUse(const Instruction* inst) const; + + // Given the result id of a pointer type, |old_type_id|, this function + // returns the id of a the same pointer type except the storage class has + // been changed to function. If the type does not already exist, it will be + // created. Returns 0 if the new type could not be found or generated. + uint32_t GetNewType(uint32_t old_type_id); + + // Updates |inst|, and any instruction dependent on |inst|, to reflect the + // change of the base pointer now pointing to the function storage class. + bool UpdateUse(Instruction* inst, Instruction* user); + bool UpdateUses(Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.cpp new file mode 100644 index 00000000..9cd6174c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.cpp @@ -0,0 +1,291 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { + +void SSAPropagator::AddControlEdge(const Edge& edge) { + BasicBlock* dest_bb = edge.dest; + + // Refuse to add the exit block to the work list. + if (dest_bb == ctx_->cfg()->pseudo_exit_block()) { + return; + } + + // Try to mark the edge executable. If it was already in the set of + // executable edges, do nothing. + if (!MarkEdgeExecutable(edge)) { + return; + } + + // If the edge had not already been marked executable, add the destination + // basic block to the work list. + blocks_.push(dest_bb); +} + +void SSAPropagator::AddSSAEdges(Instruction* instr) { + // Ignore instructions that produce no result. + if (instr->result_id() == 0) { + return; + } + + get_def_use_mgr()->ForEachUser( + instr->result_id(), [this](Instruction* use_instr) { + // If the basic block for |use_instr| has not been simulated yet, do + // nothing. The instruction |use_instr| will be simulated next time the + // block is scheduled. + if (!BlockHasBeenSimulated(ctx_->get_instr_block(use_instr))) { + return; + } + + if (ShouldSimulateAgain(use_instr)) { + ssa_edge_uses_.push(use_instr); + } + }); +} + +bool SSAPropagator::IsPhiArgExecutable(Instruction* phi, uint32_t i) const { + BasicBlock* phi_bb = ctx_->get_instr_block(phi); + + uint32_t in_label_id = phi->GetSingleWordOperand(i + 1); + Instruction* in_label_instr = get_def_use_mgr()->GetDef(in_label_id); + BasicBlock* in_bb = ctx_->get_instr_block(in_label_instr); + + return IsEdgeExecutable(Edge(in_bb, phi_bb)); +} + +bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) { + bool has_old_status = false; + PropStatus old_status = kVarying; + if (HasStatus(inst)) { + has_old_status = true; + old_status = Status(inst); + } + + assert((!has_old_status || old_status <= status) && + "Invalid lattice transition"); + + bool status_changed = !has_old_status || (old_status != status); + if (status_changed) statuses_[inst] = status; + + return status_changed; +} + +bool SSAPropagator::Simulate(Instruction* instr) { + bool changed = false; + + // Don't bother visiting instructions that should not be simulated again. + if (!ShouldSimulateAgain(instr)) { + return changed; + } + + BasicBlock* dest_bb = nullptr; + PropStatus status = visit_fn_(instr, &dest_bb); + bool status_changed = SetStatus(instr, status); + + if (status == kVarying) { + // The statement produces a varying result, add it to the list of statements + // not to simulate anymore and add its SSA def-use edges for simulation. + DontSimulateAgain(instr); + if (status_changed) { + AddSSAEdges(instr); + } + + // If |instr| is a block terminator, add all the control edges out of its + // block. + if (instr->IsBlockTerminator()) { + BasicBlock* block = ctx_->get_instr_block(instr); + for (const auto& e : bb_succs_.at(block)) { + AddControlEdge(e); + } + } + return false; + } else if (status == kInteresting) { + // Add the SSA edges coming out of this instruction if the propagation + // status has changed. + if (status_changed) { + AddSSAEdges(instr); + } + + // If there are multiple outgoing control flow edges and we know which one + // will be taken, add the destination block to the CFG work list. + if (dest_bb) { + AddControlEdge(Edge(ctx_->get_instr_block(instr), dest_bb)); + } + changed = true; + } + + // At this point, we are dealing with instructions that are in status + // kInteresting or kNotInteresting. To decide whether this instruction should + // be simulated again, we examine its operands. If at least one operand O is + // defined at an instruction D that should be simulated again, then the output + // of D might affect |instr|, so we should simulate |instr| again. + bool has_operands_to_simulate = false; + if (instr->opcode() == spv::Op::OpPhi) { + // For Phi instructions, an operand causes the Phi to be simulated again if + // the operand comes from an edge that has not yet been traversed or if its + // definition should be simulated again. + for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { + // Phi arguments come in pairs. Index 'i' contains the + // variable id, index 'i + 1' is the originating block id. + assert(i % 2 == 0 && i < instr->NumOperands() - 1 && + "malformed Phi arguments"); + + uint32_t arg_id = instr->GetSingleWordOperand(i); + Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id); + if (!IsPhiArgExecutable(instr, i) || ShouldSimulateAgain(arg_def_instr)) { + has_operands_to_simulate = true; + break; + } + } + } else { + // For regular instructions, check if the defining instruction of each + // operand needs to be simulated again. If so, then this instruction should + // also be simulated again. + has_operands_to_simulate = + !instr->WhileEachInId([this](const uint32_t* use) { + Instruction* def_instr = get_def_use_mgr()->GetDef(*use); + if (ShouldSimulateAgain(def_instr)) { + return false; + } + return true; + }); + } + + if (!has_operands_to_simulate) { + DontSimulateAgain(instr); + } + + return changed; +} + +bool SSAPropagator::Simulate(BasicBlock* block) { + if (block == ctx_->cfg()->pseudo_exit_block()) { + return false; + } + + // Always simulate Phi instructions, even if we have simulated this block + // before. We do this because Phi instructions receive their inputs from + // incoming edges. When those edges are marked executable, the corresponding + // operand can be simulated. + bool changed = false; + block->ForEachPhiInst( + [&changed, this](Instruction* instr) { changed |= Simulate(instr); }); + + // If this is the first time this block is being simulated, simulate every + // statement in it. + if (!BlockHasBeenSimulated(block)) { + block->ForEachInst([this, &changed](Instruction* instr) { + if (instr->opcode() != spv::Op::OpPhi) { + changed |= Simulate(instr); + } + }); + + MarkBlockSimulated(block); + + // If this block has exactly one successor, mark the edge to its successor + // as executable. + if (bb_succs_.at(block).size() == 1) { + AddControlEdge(bb_succs_.at(block).at(0)); + } + } + + return changed; +} + +void SSAPropagator::Initialize(Function* fn) { + // Compute predecessor and successor blocks for every block in |fn|'s CFG. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + bb_succs_[ctx_->cfg()->pseudo_entry_block()].push_back( + Edge(ctx_->cfg()->pseudo_entry_block(), fn->entry().get())); + + for (auto& block : *fn) { + const auto& const_block = block; + const_block.ForEachSuccessorLabel([this, &block](const uint32_t label_id) { + BasicBlock* succ_bb = + ctx_->get_instr_block(get_def_use_mgr()->GetDef(label_id)); + bb_succs_[&block].push_back(Edge(&block, succ_bb)); + bb_preds_[succ_bb].push_back(Edge(succ_bb, &block)); + }); + if (block.IsReturnOrAbort()) { + bb_succs_[&block].push_back( + Edge(&block, ctx_->cfg()->pseudo_exit_block())); + bb_preds_[ctx_->cfg()->pseudo_exit_block()].push_back( + Edge(ctx_->cfg()->pseudo_exit_block(), &block)); + } + } + + // Add the edges out of the entry block to seed the propagator. + const auto& entry_succs = bb_succs_[ctx_->cfg()->pseudo_entry_block()]; + for (const auto& e : entry_succs) { + AddControlEdge(e); + } +} + +bool SSAPropagator::Run(Function* fn) { + Initialize(fn); + + bool changed = false; + while (!blocks_.empty() || !ssa_edge_uses_.empty()) { + // Simulate all blocks first. Simulating blocks will add SSA edges to + // follow after all the blocks have been simulated. + if (!blocks_.empty()) { + auto block = blocks_.front(); + changed |= Simulate(block); + blocks_.pop(); + continue; + } + + // Simulate edges from the SSA queue. + if (!ssa_edge_uses_.empty()) { + Instruction* instr = ssa_edge_uses_.front(); + changed |= Simulate(instr); + ssa_edge_uses_.pop(); + } + } + +#ifndef NDEBUG + // Verify all visited values have settled. No value that has been simulated + // should end on not interesting. + fn->ForEachInst([this](Instruction* inst) { + assert( + (!HasStatus(inst) || Status(inst) != SSAPropagator::kNotInteresting) && + "Unsettled value"); + }); +#endif + + return changed; +} + +std::ostream& operator<<(std::ostream& str, + const SSAPropagator::PropStatus& status) { + switch (status) { + case SSAPropagator::kVarying: + str << "Varying"; + break; + case SSAPropagator::kInteresting: + str << "Interesting"; + break; + default: + str << "Not interesting"; + break; + } + return str; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.h b/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.h new file mode 100644 index 00000000..71212c96 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/propagator.h @@ -0,0 +1,317 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PROPAGATOR_H_ +#define SOURCE_OPT_PROPAGATOR_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Represents a CFG control edge. +struct Edge { + Edge(BasicBlock* b1, BasicBlock* b2) : source(b1), dest(b2) { + assert(source && "CFG edges cannot have a null source block."); + assert(dest && "CFG edges cannot have a null destination block."); + } + BasicBlock* source; + BasicBlock* dest; + bool operator<(const Edge& o) const { + return std::make_pair(source->id(), dest->id()) < + std::make_pair(o.source->id(), o.dest->id()); + } +}; + +// This class implements a generic value propagation algorithm based on the +// conditional constant propagation algorithm proposed in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. +// +// A Propagation Engine for GCC +// Diego Novillo, GCC Summit 2005 +// http://ols.fedoraproject.org/GCC/Reprints-2005/novillo-Reprint.pdf +// +// The purpose of this implementation is to act as a common framework for any +// transformation that needs to propagate values from statements producing new +// values to statements using those values. Simulation proceeds as follows: +// +// 1- Initially, all edges of the CFG are marked not executable and the CFG +// worklist is seeded with all the statements in the entry basic block. +// +// 2- Every instruction I is simulated by calling a pass-provided function +// |visit_fn|. This function is responsible for three things: +// +// (a) Keep a value table of interesting values. This table maps SSA IDs to +// their values. For instance, when implementing constant propagation, +// given a store operation 'OpStore %f %int_3', |visit_fn| should assign +// the value 3 to the table slot for %f. +// +// In general, |visit_fn| will need to use the value table to replace its +// operands, fold the result and decide whether a new value needs to be +// stored in the table. |visit_fn| should only create a new mapping in +// the value table if all the operands in the instruction are known and +// present in the value table. +// +// (b) Return a status indicator to direct the propagator logic. Once the +// instruction is simulated, the propagator needs to know whether this +// instruction produced something interesting. This is indicated via +// |visit_fn|'s return value: +// +// SSAPropagator::kNotInteresting: Instruction I produces nothing of +// interest and does not affect any of the work lists. The +// propagator will visit the statement again if any of its operands +// produce an interesting value in the future. +// +// |visit_fn| should always return this value when it is not sure +// whether the instruction will produce an interesting value in the +// future or not. For instance, for constant propagation, an OpIAdd +// instruction may produce a constant if its two operands are +// constant, but the first time we visit the instruction, we still +// may not have its operands in the value table. +// +// SSAPropagator::kVarying: The value produced by I cannot be determined +// at compile time. Further simulation of I is not required. The +// propagator will not visit this instruction again. Additionally, +// the propagator will add all the instructions at the end of SSA +// def-use edges to be simulated again. +// +// If I is a basic block terminator, it will mark all outgoing edges +// as executable so they are traversed one more time. Eventually +// the kVarying attribute will be spread out to all the data and +// control dependents for I. +// +// It is important for propagation to use kVarying as a bottom value +// for the propagation lattice. It should never be possible for an +// instruction to return kVarying once and kInteresting on a second +// visit. Otherwise, propagation would not stabilize. +// +// SSAPropagator::kInteresting: Instruction I produces a value that can +// be computed at compile time. In this case, |visit_fn| should +// create a new mapping between I's result ID and the produced +// value. Much like the kNotInteresting case, the propagator will +// visit this instruction again if any of its operands changes. +// This is useful when the statement changes from one interesting +// state to another. +// +// (c) For conditional branches, |visit_fn| may decide which edge to take out +// of I's basic block. For example, if the operand for an OpSwitch is +// known to take a specific constant value, |visit_fn| should figure out +// the destination basic block and pass it back by setting the second +// argument to |visit_fn|. +// +// At the end of propagation, values in the value table are guaranteed to be +// stable and can be replaced in the IR. +// +// 3- The propagator keeps two work queues. Instructions are only added to +// these queues if they produce an interesting or varying value. None of this +// should be handled by |visit_fn|. The propagator keeps track of this +// automatically (see SSAPropagator::Simulate for implementation). +// +// CFG blocks: contains the queue of blocks to be simulated. +// Blocks are added to this queue if their incoming edges are +// executable. +// +// SSA Edges: An SSA edge is a def-use edge between a value-producing +// instruction and its use instruction. The SSA edges list +// contains the statements at the end of a def-use edge that need +// to be re-visited when an instruction produces a kVarying or +// kInteresting result. +// +// 4- Simulation terminates when all work queues are drained. +// +// +// EXAMPLE: Basic constant store propagator. +// +// Suppose we want to propagate all constant assignments of the form "OpStore +// %id %cst" where "%id" is some variable and "%cst" an OpConstant. The +// following code builds a table |values| where every id that was assigned a +// constant value is mapped to the constant value it was assigned. +// +// auto ctx = BuildModule(...); +// std::map values; +// const auto visit_fn = [&ctx, &values](Instruction* instr, +// BasicBlock** dest_bb) { +// if (instr->opcode() == spv::Op::OpStore) { +// uint32_t rhs_id = instr->GetSingleWordOperand(1); +// Instruction* rhs_def = ctx->get_def_use_mgr()->GetDef(rhs_id); +// if (rhs_def->opcode() == spv::Op::OpConstant) { +// uint32_t val = rhs_def->GetSingleWordOperand(2); +// values[rhs_id] = val; +// return SSAPropagator::kInteresting; +// } +// } +// return SSAPropagator::kVarying; +// }; +// SSAPropagator propagator(ctx.get(), &cfg, visit_fn); +// propagator.Run(&fn); +// +// Given the code: +// +// %int_4 = OpConstant %int 4 +// %int_3 = OpConstant %int 3 +// %int_1 = OpConstant %int 1 +// OpStore %x %int_4 +// OpStore %y %int_3 +// OpStore %z %int_1 +// +// After SSAPropagator::Run returns, the |values| map will contain the entries: +// values[%x] = 4, values[%y] = 3, and, values[%z] = 1. +class SSAPropagator { + public: + // Lattice values used for propagation. See class documentation for + // a description. + enum PropStatus { kNotInteresting, kInteresting, kVarying }; + + using VisitFunction = std::function; + + SSAPropagator(IRContext* context, const VisitFunction& visit_fn) + : ctx_(context), visit_fn_(visit_fn) {} + + // Runs the propagator on function |fn|. Returns true if changes were made to + // the function. Otherwise, it returns false. + bool Run(Function* fn); + + // Returns true if the |i|th argument for |phi| comes through a CFG edge that + // has been marked executable. |i| should be an index value accepted by + // Instruction::GetSingleWordOperand. + bool IsPhiArgExecutable(Instruction* phi, uint32_t i) const; + + // Returns true if |inst| has a recorded status. This will be true once |inst| + // has been simulated once. + bool HasStatus(Instruction* inst) const { return statuses_.count(inst); } + + // Returns the current propagation status of |inst|. Assumes + // |HasStatus(inst)| returns true. + PropStatus Status(Instruction* inst) const { + return statuses_.find(inst)->second; + } + + // Records the propagation status |status| for |inst|. Returns true if the + // status for |inst| has changed or set was set for the first time. + bool SetStatus(Instruction* inst, PropStatus status); + + private: + // Initialize processing. + void Initialize(Function* fn); + + // Simulate the execution |block| by calling |visit_fn_| on every instruction + // in it. + bool Simulate(BasicBlock* block); + + // Simulate the execution of |instr| by replacing all the known values in + // every operand and determining whether the result is interesting for + // propagation. This invokes the callback function |visit_fn_| to determine + // the value computed by |instr|. + bool Simulate(Instruction* instr); + + // Returns true if |instr| should be simulated again. + bool ShouldSimulateAgain(Instruction* instr) const { + return do_not_simulate_.find(instr) == do_not_simulate_.end(); + } + + // Add |instr| to the set of instructions not to simulate again. + void DontSimulateAgain(Instruction* instr) { do_not_simulate_.insert(instr); } + + // Returns true if |block| has been simulated already. + bool BlockHasBeenSimulated(BasicBlock* block) const { + return simulated_blocks_.find(block) != simulated_blocks_.end(); + } + + // Marks block |block| as simulated. + void MarkBlockSimulated(BasicBlock* block) { + simulated_blocks_.insert(block); + } + + // Marks |edge| as executable. Returns false if the edge was already marked + // as executable. + bool MarkEdgeExecutable(const Edge& edge) { + return executable_edges_.insert(edge).second; + } + + // Returns true if |edge| has been marked as executable. + bool IsEdgeExecutable(const Edge& edge) const { + return executable_edges_.find(edge) != executable_edges_.end(); + } + + // Returns a pointer to the def-use manager for |ctx_|. + analysis::DefUseManager* get_def_use_mgr() const { + return ctx_->get_def_use_mgr(); + } + + // If the CFG edge |e| has not been executed, this function adds |e|'s + // destination block to the work list. + void AddControlEdge(const Edge& e); + + // Adds all the instructions that use the result of |instr| to the SSA edges + // work list. If |instr| produces no result id, this does nothing. + void AddSSAEdges(Instruction* instr); + + // IR context to use. + IRContext* ctx_; + + // Function that visits instructions during simulation. The output of this + // function is used to determine if the simulated instruction produced a value + // interesting for propagation. The function is responsible for keeping + // track of interesting values by storing them in some user-provided map. + VisitFunction visit_fn_; + + // SSA def-use edges to traverse. Each entry is a destination statement for an + // SSA def-use edge as returned by |def_use_manager_|. + std::queue ssa_edge_uses_; + + // Blocks to simulate. + std::queue blocks_; + + // Blocks simulated during propagation. + std::unordered_set simulated_blocks_; + + // Set of instructions that should not be simulated again because they have + // been found to be in the kVarying state. + std::unordered_set do_not_simulate_; + + // Map between a basic block and its predecessor edges. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + std::unordered_map> bb_preds_; + + // Map between a basic block and its successor edges. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + std::unordered_map> bb_succs_; + + // Set of executable CFG edges. + std::set executable_edges_; + + // Tracks instruction propagation status. + std::unordered_map statuses_; +}; + +std::ostream& operator<<(std::ostream& str, + const SSAPropagator::PropStatus& status); + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PROPAGATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.cpp new file mode 100644 index 00000000..73a90f06 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/reduce_load_size.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kExtractCompositeIdInIdx = 0; +constexpr uint32_t kVariableStorageClassInIdx = 0; +constexpr uint32_t kLoadPointerInIdx = 0; +} // namespace + +Pass::Status ReduceLoadSize::Process() { + bool modified = false; + + for (auto& func : *get_module()) { + func.ForEachInst([&modified, this](Instruction* inst) { + if (inst->opcode() == spv::Op::OpCompositeExtract) { + if (ShouldReplaceExtract(inst)) { + modified |= ReplaceExtract(inst); + } + } + }); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool ReduceLoadSize::ReplaceExtract(Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + uint32_t composite_id = + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* composite_inst = def_use_mgr->GetDef(composite_id); + + if (composite_inst->opcode() != spv::Op::OpLoad) { + return false; + } + + analysis::Type* composite_type = type_mgr->GetType(composite_inst->type_id()); + if (composite_type->kind() == analysis::Type::kVector || + composite_type->kind() == analysis::Type::kMatrix) { + return false; + } + + Instruction* var = composite_inst->GetBaseAddress(); + if (var == nullptr || var->opcode() != spv::Op::OpVariable) { + return false; + } + + spv::StorageClass storage_class = static_cast( + var->GetSingleWordInOperand(kVariableStorageClassInIdx)); + switch (storage_class) { + case spv::StorageClass::Uniform: + case spv::StorageClass::UniformConstant: + case spv::StorageClass::Input: + break; + default: + return false; + } + + // Create a new access chain and load just after the old load. + // We cannot create the new access chain load in the position of the extract + // because the storage may have been written to in between. + InstructionBuilder ir_builder( + inst->context(), composite_inst, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + + uint32_t pointer_to_result_type_id = + type_mgr->FindPointerToType(inst->type_id(), storage_class); + assert(pointer_to_result_type_id != 0 && + "We did not find the pointer type that we need."); + + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = type_mgr->GetRegisteredType(&int_type); + std::vector ids; + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + uint32_t index = inst->GetSingleWordInOperand(i); + const analysis::Constant* index_const = + const_mgr->GetConstant(uint32_type, {index}); + ids.push_back(const_mgr->GetDefiningInstruction(index_const)->result_id()); + } + + Instruction* new_access_chain = ir_builder.AddAccessChain( + pointer_to_result_type_id, + composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids); + Instruction* new_load = + ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id()); + + context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id()); + context()->KillInst(inst); + return true; +} + +bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + Instruction* op_inst = def_use_mgr->GetDef( + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)); + + if (op_inst->opcode() != spv::Op::OpLoad) { + return false; + } + + auto cached_result = should_replace_cache_.find(op_inst->result_id()); + if (cached_result != should_replace_cache_.end()) { + return cached_result->second; + } + + bool all_elements_used = false; + std::set elements_used; + + all_elements_used = + !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { + if (use->IsCommonDebugInstr()) return true; + if (use->opcode() != spv::Op::OpCompositeExtract || + use->NumInOperands() == 1) { + return false; + } + elements_used.insert(use->GetSingleWordInOperand(1)); + return true; + }); + + bool should_replace = false; + if (all_elements_used) { + should_replace = false; + } else if (1.0 <= replacement_threshold_) { + should_replace = true; + } else { + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* load_type = type_mgr->GetType(op_inst->type_id()); + uint32_t total_size = 1; + switch (load_type->kind()) { + case analysis::Type::kArray: { + const analysis::Constant* size_const = + const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId()); + + if (size_const) { + assert(size_const->AsIntConstant()); + total_size = size_const->GetU32(); + } else { + // The size is spec constant, so it is unknown at this time. Assume + // it is very large. + total_size = UINT32_MAX; + } + } break; + case analysis::Type::kStruct: + total_size = static_cast( + load_type->AsStruct()->element_types().size()); + break; + default: + break; + } + double percent_used = static_cast(elements_used.size()) / + static_cast(total_size); + should_replace = (percent_used < replacement_threshold_); + } + + should_replace_cache_[op_inst->result_id()] = should_replace; + return should_replace; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.h b/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.h new file mode 100644 index 00000000..b3238453 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/reduce_load_size.h @@ -0,0 +1,73 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REDUCE_LOAD_SIZE_H_ +#define SOURCE_OPT_REDUCE_LOAD_SIZE_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReduceLoadSize : public Pass { + public: + explicit ReduceLoadSize(double replacement_threshold) + : replacement_threshold_(replacement_threshold) {} + + const char* name() const override { return "reduce-load-size"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces |inst|, which must be an OpCompositeExtract instruction, with + // an OpAccessChain and a load if possible. This happens only if it is a load + // feeding |inst|. Returns true if the substitution happened. The position + // of the new instructions will be in the same place as the load feeding the + // extract. + bool ReplaceExtract(Instruction* inst); + + // Returns true if the OpCompositeExtract instruction |inst| should be replace + // or not. This is determined by looking at the load that feeds |inst| if + // it is a load. |should_replace_cache_| is used to cache the results based + // on the load feeding |inst|. + bool ShouldReplaceExtract(Instruction* inst); + + // Threshold to determine whether we have to replace the load or not. If the + // ratio of the used components of the load is less than the threshold, we + // replace the load. + double replacement_threshold_; + + // Maps the result id of an OpLoad instruction to the result of whether or + // not the OpCompositeExtract that use the id should be replaced. + std::unordered_map should_replace_cache_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REDUCE_LOAD_SIZE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.cpp new file mode 100644 index 00000000..398225bb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/redundancy_elimination.h" + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status RedundancyEliminationPass::Process() { + bool modified = false; + ValueNumberTable vnTable(context()); + + for (auto& func : *get_module()) { + if (func.IsDeclaration()) { + continue; + } + + // Build the dominator tree for this function. It is how the code is + // traversed. + DominatorTree& dom_tree = + context()->GetDominatorAnalysis(&func)->GetDomTree(); + + // Keeps track of all ids that contain a given value number. We keep + // track of multiple values because they could have the same value, but + // different decorations. + std::map value_to_ids; + + if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable, value_to_ids)) { + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool RedundancyEliminationPass::EliminateRedundanciesFrom( + DominatorTreeNode* bb, const ValueNumberTable& vnTable, + std::map value_to_ids) { + bool modified = EliminateRedundanciesInBB(bb->bb_, vnTable, &value_to_ids); + + for (auto dominated_bb : bb->children_) { + modified |= EliminateRedundanciesFrom(dominated_bb, vnTable, value_to_ids); + } + + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.h b/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.h new file mode 100644 index 00000000..40451f40 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/redundancy_elimination.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ +#define SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/local_redundancy_elimination.h" +#include "source/opt/pass.h" +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +// This pass implements total redundancy elimination. This is the same as +// local redundancy elimination except it looks across basic block boundaries. +// An instruction, inst, is totally redundant if there is another instruction +// that dominates inst, and also computes the same value. +class RedundancyEliminationPass : public LocalRedundancyEliminationPass { + public: + const char* name() const override { return "redundancy-elimination"; } + Status Process() override; + + protected: + // Removes for all total redundancies in the function starting at |bb|. + // + // |vnTable| must have computed a value number for every result id defined + // in the function containing |bb|. + // + // |value_to_ids| is a map from value number to ids. If {vn, id} is in + // |value_to_ids| then vn is the value number of id, and the definition of id + // dominates |bb|. + // + // Returns true if at least one instruction is deleted. + bool EliminateRedundanciesFrom(DominatorTreeNode* bb, + const ValueNumberTable& vnTable, + std::map value_to_ids); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/reflect.h b/engine/engine/3rdlibs/spirv-tools/source/opt/reflect.h new file mode 100644 index 00000000..ec7c2dd0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/reflect.h @@ -0,0 +1,63 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REFLECT_H_ +#define SOURCE_OPT_REFLECT_H_ + +#include "source/latest_version_spirv_header.h" +#include "source/opcode.h" + +namespace spvtools { +namespace opt { + +// Note that as SPIR-V evolves over time, new opcodes may appear. So the +// following functions tend to be outdated and should be updated when SPIR-V +// version bumps. + +inline bool IsDebug1Inst(spv::Op opcode) { + return (opcode >= spv::Op::OpSourceContinued && + opcode <= spv::Op::OpSourceExtension) || + opcode == spv::Op::OpString; +} +inline bool IsDebug2Inst(spv::Op opcode) { + return opcode == spv::Op::OpName || opcode == spv::Op::OpMemberName; +} +inline bool IsDebug3Inst(spv::Op opcode) { + return opcode == spv::Op::OpModuleProcessed; +} +inline bool IsOpLineInst(spv::Op opcode) { + return opcode == spv::Op::OpLine || opcode == spv::Op::OpNoLine; +} +inline bool IsAnnotationInst(spv::Op opcode) { + return (opcode >= spv::Op::OpDecorate && + opcode <= spv::Op::OpGroupMemberDecorate) || + opcode == spv::Op::OpDecorateId || + opcode == spv::Op::OpDecorateStringGOOGLE || + opcode == spv::Op::OpMemberDecorateStringGOOGLE; +} +inline bool IsTypeInst(spv::Op opcode) { + return spvOpcodeGeneratesType(opcode) || + opcode == spv::Op::OpTypeForwardPointer; +} +inline bool IsConstantInst(spv::Op opcode) { + return spvOpcodeIsConstant(opcode); +} +inline bool IsSpecConstantInst(spv::Op opcode) { + return spvOpcodeIsSpecConstant(opcode); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REFLECT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.cpp new file mode 100644 index 00000000..34a8ba3e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.cpp @@ -0,0 +1,582 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/register_pressure.h" + +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { +namespace { +// Predicate for the FilterIterator to only consider instructions that are not +// phi instructions defined in the basic block |bb|. +class ExcludePhiDefinedInBlock { + public: + ExcludePhiDefinedInBlock(IRContext* context, const BasicBlock* bb) + : context_(context), bb_(bb) {} + + bool operator()(Instruction* insn) const { + return !(insn->opcode() == spv::Op::OpPhi && + context_->get_instr_block(insn) == bb_); + } + + private: + IRContext* context_; + const BasicBlock* bb_; +}; + +// Returns true if |insn| generates a SSA register that is likely to require a +// physical register. +bool CreatesRegisterUsage(Instruction* insn) { + if (!insn->HasResultId()) return false; + if (insn->opcode() == spv::Op::OpUndef) return false; + if (IsConstantInst(insn->opcode())) return false; + if (insn->opcode() == spv::Op::OpLabel) return false; + return true; +} + +// Compute the register liveness for each basic block of a function. This also +// fill-up some information about the pick register usage and a break down of +// register usage. This implements: "A non-iterative data-flow algorithm for +// computing liveness sets in strict ssa programs" from Boissinot et al. +class ComputeRegisterLiveness { + public: + ComputeRegisterLiveness(RegisterLiveness* reg_pressure, Function* f) + : reg_pressure_(reg_pressure), + context_(reg_pressure->GetContext()), + function_(f), + cfg_(*reg_pressure->GetContext()->cfg()), + def_use_manager_(*reg_pressure->GetContext()->get_def_use_mgr()), + dom_tree_( + reg_pressure->GetContext()->GetDominatorAnalysis(f)->GetDomTree()), + loop_desc_(*reg_pressure->GetContext()->GetLoopDescriptor(f)) {} + + // Computes the register liveness for |function_| and then estimate the + // register usage. The liveness algorithm works in 2 steps: + // - First, compute the liveness for each basic blocks, but will ignore any + // back-edge; + // - Second, walk loop forest to propagate registers crossing back-edges + // (add iterative values into the liveness set). + void Compute() { + for (BasicBlock& start_bb : *function_) { + if (reg_pressure_->Get(start_bb.id()) != nullptr) { + continue; + } + cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) { + if (reg_pressure_->Get(bb->id()) == nullptr) { + ComputePartialLiveness(bb); + } + }); + } + DoLoopLivenessUnification(); + EvaluateRegisterRequirements(); + } + + private: + // Registers all SSA register used by successors of |bb| in their phi + // instructions. + void ComputePhiUses(const BasicBlock& bb, + RegisterLiveness::RegionRegisterLiveness::LiveSet* live) { + uint32_t bb_id = bb.id(); + bb.ForEachSuccessorLabel([live, bb_id, this](uint32_t sid) { + BasicBlock* succ_bb = cfg_.block(sid); + succ_bb->ForEachPhiInst([live, bb_id, this](const Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (phi->GetSingleWordInOperand(i + 1) == bb_id) { + Instruction* insn_op = + def_use_manager_.GetDef(phi->GetSingleWordInOperand(i)); + if (CreatesRegisterUsage(insn_op)) { + live->insert(insn_op); + break; + } + } + } + }); + }); + } + + // Computes register liveness for each basic blocks but ignores all + // back-edges. + void ComputePartialLiveness(BasicBlock* bb) { + assert(reg_pressure_->Get(bb) == nullptr && + "Basic block already processed"); + + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->GetOrInsert(bb->id()); + ComputePhiUses(*bb, &live_inout->live_out_); + + const BasicBlock* cbb = bb; + cbb->ForEachSuccessorLabel([&live_inout, bb, this](uint32_t sid) { + // Skip back edges. + if (dom_tree_.Dominates(sid, bb->id())) { + return; + } + + BasicBlock* succ_bb = cfg_.block(sid); + RegisterLiveness::RegionRegisterLiveness* succ_live_inout = + reg_pressure_->Get(succ_bb); + assert(succ_live_inout && + "Successor liveness analysis was not performed"); + + ExcludePhiDefinedInBlock predicate(context_, succ_bb); + auto filter = + MakeFilterIteratorRange(succ_live_inout->live_in_.begin(), + succ_live_inout->live_in_.end(), predicate); + live_inout->live_out_.insert(filter.begin(), filter.end()); + }); + + live_inout->live_in_ = live_inout->live_out_; + for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { + if (insn.opcode() == spv::Op::OpPhi) { + live_inout->live_in_.insert(&insn); + break; + } + live_inout->live_in_.erase(&insn); + insn.ForEachInId([live_inout, this](uint32_t* id) { + Instruction* insn_op = def_use_manager_.GetDef(*id); + if (CreatesRegisterUsage(insn_op)) { + live_inout->live_in_.insert(insn_op); + } + }); + } + } + + // Propagates the register liveness information of each loop iterators. + void DoLoopLivenessUnification() { + for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) { + DoLoopLivenessUnification(*loop); + } + } + + // Propagates the register liveness information of loop iterators trough-out + // the loop body. + void DoLoopLivenessUnification(const Loop& loop) { + auto blocks_in_loop = MakeFilterIteratorRange( + loop.GetBlocks().begin(), loop.GetBlocks().end(), + [&loop, this](uint32_t bb_id) { + return bb_id != loop.GetHeaderBlock()->id() && + loop_desc_[bb_id] == &loop; + }); + + RegisterLiveness::RegionRegisterLiveness* header_live_inout = + reg_pressure_->Get(loop.GetHeaderBlock()); + assert(header_live_inout && + "Liveness analysis was not performed for the current block"); + + ExcludePhiDefinedInBlock predicate(context_, loop.GetHeaderBlock()); + auto live_loop = + MakeFilterIteratorRange(header_live_inout->live_in_.begin(), + header_live_inout->live_in_.end(), predicate); + + for (uint32_t bb_id : blocks_in_loop) { + BasicBlock* bb = cfg_.block(bb_id); + + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(bb); + live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); + live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); + } + + for (const Loop* inner_loop : loop) { + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(inner_loop->GetHeaderBlock()); + live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); + live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); + + DoLoopLivenessUnification(*inner_loop); + } + } + + // Get the number of required registers for this each basic block. + void EvaluateRegisterRequirements() { + for (BasicBlock& bb : *function_) { + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(bb.id()); + assert(live_inout != nullptr && "Basic block not processed"); + + size_t reg_count = live_inout->live_out_.size(); + for (Instruction* insn : live_inout->live_out_) { + live_inout->AddRegisterClass(insn); + } + live_inout->used_registers_ = reg_count; + + std::unordered_set die_in_block; + for (Instruction& insn : make_range(bb.rbegin(), bb.rend())) { + // If it is a phi instruction, the register pressure will not change + // anymore. + if (insn.opcode() == spv::Op::OpPhi) { + break; + } + + insn.ForEachInId( + [live_inout, &die_in_block, ®_count, this](uint32_t* id) { + Instruction* op_insn = def_use_manager_.GetDef(*id); + if (!CreatesRegisterUsage(op_insn) || + live_inout->live_out_.count(op_insn)) { + // already taken into account. + return; + } + if (!die_in_block.count(*id)) { + live_inout->AddRegisterClass(def_use_manager_.GetDef(*id)); + reg_count++; + die_in_block.insert(*id); + } + }); + live_inout->used_registers_ = + std::max(live_inout->used_registers_, reg_count); + if (CreatesRegisterUsage(&insn)) { + reg_count--; + } + } + } + } + + RegisterLiveness* reg_pressure_; + IRContext* context_; + Function* function_; + CFG& cfg_; + analysis::DefUseManager& def_use_manager_; + DominatorTree& dom_tree_; + LoopDescriptor& loop_desc_; +}; +} // namespace + +// Get the number of required registers for each basic block. +void RegisterLiveness::RegionRegisterLiveness::AddRegisterClass( + Instruction* insn) { + assert(CreatesRegisterUsage(insn) && "Instruction does not use a register"); + analysis::Type* type = + insn->context()->get_type_mgr()->GetType(insn->type_id()); + + RegisterLiveness::RegisterClass reg_class{type, false}; + + insn->context()->get_decoration_mgr()->WhileEachDecoration( + insn->result_id(), uint32_t(spv::Decoration::Uniform), + [®_class](const Instruction&) { + reg_class.is_uniform_ = true; + return false; + }); + + AddRegisterClass(reg_class); +} + +void RegisterLiveness::Analyze(Function* f) { + block_pressure_.clear(); + ComputeRegisterLiveness(this, f).Compute(); +} + +void RegisterLiveness::ComputeLoopRegisterPressure( + const Loop& loop, RegionRegisterLiveness* loop_reg_pressure) const { + loop_reg_pressure->Clear(); + + const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); + loop_reg_pressure->live_in_ = header_live_inout->live_in_; + + std::unordered_set exit_blocks; + loop.GetExitBlocks(&exit_blocks); + + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + loop_reg_pressure->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + + std::unordered_set seen_insn; + for (Instruction* insn : loop_reg_pressure->live_out_) { + loop_reg_pressure->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + for (Instruction* insn : loop_reg_pressure->live_in_) { + if (!seen_insn.count(insn->result_id())) { + continue; + } + loop_reg_pressure->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + + loop_reg_pressure->used_registers_ = 0; + + for (uint32_t bb_id : loop.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout = Get(bb_id); + assert(live_inout != nullptr && "Basic block not processed"); + loop_reg_pressure->used_registers_ = std::max( + loop_reg_pressure->used_registers_, live_inout->used_registers_); + + for (Instruction& insn : *bb) { + if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + loop_reg_pressure->AddRegisterClass(&insn); + } + } +} + +void RegisterLiveness::SimulateFusion( + const Loop& l1, const Loop& l2, RegionRegisterLiveness* sim_result) const { + sim_result->Clear(); + + // Compute the live-in state: + // sim_result.live_in = l1.live_in U l2.live_in + // This assumes that |l1| does not generated register that is live-out for + // |l1|. + const RegionRegisterLiveness* l1_header_live_inout = Get(l1.GetHeaderBlock()); + sim_result->live_in_ = l1_header_live_inout->live_in_; + + const RegionRegisterLiveness* l2_header_live_inout = Get(l2.GetHeaderBlock()); + sim_result->live_in_.insert(l2_header_live_inout->live_in_.begin(), + l2_header_live_inout->live_in_.end()); + + // The live-out set of the fused loop is the l2 live-out set. + std::unordered_set exit_blocks; + l2.GetExitBlocks(&exit_blocks); + + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + sim_result->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + + // Compute the register usage information. + std::unordered_set seen_insn; + for (Instruction* insn : sim_result->live_out_) { + sim_result->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + for (Instruction* insn : sim_result->live_in_) { + if (!seen_insn.count(insn->result_id())) { + continue; + } + sim_result->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + + sim_result->used_registers_ = 0; + + // The loop fusion is injecting the l1 before the l2, the latch of l1 will be + // connected to the header of l2. + // To compute the register usage, we inject the loop live-in (union of l1 and + // l2 live-in header blocks) into the live in/out of each basic block of + // l1 to get the peak register usage. We then repeat the operation to for l2 + // basic blocks but in this case we inject the live-out of the latch of l1. + auto live_loop = MakeFilterIteratorRange( + sim_result->live_in_.begin(), sim_result->live_in_.end(), + [&l1, &l2](Instruction* insn) { + BasicBlock* bb = insn->context()->get_instr_block(insn); + return insn->HasResultId() && + !(insn->opcode() == spv::Op::OpPhi && + (bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock())); + }); + + for (uint32_t bb_id : l1.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout_info = Get(bb_id); + assert(live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; + live_out.insert(live_loop.begin(), live_loop.end()); + sim_result->used_registers_ = + std::max(sim_result->used_registers_, + live_inout_info->used_registers_ + live_out.size() - + live_inout_info->live_out_.size()); + + for (Instruction& insn : *bb) { + if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + sim_result->AddRegisterClass(&insn); + } + } + + const RegionRegisterLiveness* l1_latch_live_inout_info = + Get(l1.GetLatchBlock()->id()); + assert(l1_latch_live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet l1_latch_live_out = + l1_latch_live_inout_info->live_out_; + l1_latch_live_out.insert(live_loop.begin(), live_loop.end()); + + auto live_loop_l2 = + make_range(l1_latch_live_out.begin(), l1_latch_live_out.end()); + + for (uint32_t bb_id : l2.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout_info = Get(bb_id); + assert(live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; + live_out.insert(live_loop_l2.begin(), live_loop_l2.end()); + sim_result->used_registers_ = + std::max(sim_result->used_registers_, + live_inout_info->used_registers_ + live_out.size() - + live_inout_info->live_out_.size()); + + for (Instruction& insn : *bb) { + if (insn.opcode() == spv::Op::OpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + sim_result->AddRegisterClass(&insn); + } + } +} + +void RegisterLiveness::SimulateFission( + const Loop& loop, const std::unordered_set& moved_inst, + const std::unordered_set& copied_inst, + RegionRegisterLiveness* l1_sim_result, + RegionRegisterLiveness* l2_sim_result) const { + l1_sim_result->Clear(); + l2_sim_result->Clear(); + + // Filter predicates: consider instructions that only belong to the first and + // second loop. + auto belong_to_loop1 = [&moved_inst, &copied_inst, &loop](Instruction* insn) { + return moved_inst.count(insn) || copied_inst.count(insn) || + !loop.IsInsideLoop(insn); + }; + auto belong_to_loop2 = [&moved_inst](Instruction* insn) { + return !moved_inst.count(insn); + }; + + const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); + // l1 live-in + { + auto live_loop = MakeFilterIteratorRange( + header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), + belong_to_loop1); + l1_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); + } + // l2 live-in + { + auto live_loop = MakeFilterIteratorRange( + header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), + belong_to_loop2); + l2_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); + } + + std::unordered_set exit_blocks; + loop.GetExitBlocks(&exit_blocks); + + // l2 live-out. + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + l2_sim_result->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + // l1 live-out. + { + auto live_out = MakeFilterIteratorRange(l2_sim_result->live_out_.begin(), + l2_sim_result->live_out_.end(), + belong_to_loop1); + l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); + } + { + auto live_out = + MakeFilterIteratorRange(l2_sim_result->live_in_.begin(), + l2_sim_result->live_in_.end(), belong_to_loop1); + l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); + } + // Lives out of l1 are live out of l2 so are live in of l2 as well. + l2_sim_result->live_in_.insert(l1_sim_result->live_out_.begin(), + l1_sim_result->live_out_.end()); + + for (Instruction* insn : l1_sim_result->live_in_) { + l1_sim_result->AddRegisterClass(insn); + } + for (Instruction* insn : l2_sim_result->live_in_) { + l2_sim_result->AddRegisterClass(insn); + } + + l1_sim_result->used_registers_ = 0; + l2_sim_result->used_registers_ = 0; + + for (uint32_t bb_id : loop.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegisterLiveness::RegionRegisterLiveness* live_inout = Get(bb_id); + assert(live_inout != nullptr && "Basic block not processed"); + auto l1_block_live_out = + MakeFilterIteratorRange(live_inout->live_out_.begin(), + live_inout->live_out_.end(), belong_to_loop1); + auto l2_block_live_out = + MakeFilterIteratorRange(live_inout->live_out_.begin(), + live_inout->live_out_.end(), belong_to_loop2); + + size_t l1_reg_count = + std::distance(l1_block_live_out.begin(), l1_block_live_out.end()); + size_t l2_reg_count = + std::distance(l2_block_live_out.begin(), l2_block_live_out.end()); + + std::unordered_set die_in_block; + for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { + if (insn.opcode() == spv::Op::OpPhi) { + break; + } + + bool does_belong_to_loop1 = belong_to_loop1(&insn); + bool does_belong_to_loop2 = belong_to_loop2(&insn); + insn.ForEachInId([live_inout, &die_in_block, &l1_reg_count, &l2_reg_count, + does_belong_to_loop1, does_belong_to_loop2, + this](uint32_t* id) { + Instruction* op_insn = context_->get_def_use_mgr()->GetDef(*id); + if (!CreatesRegisterUsage(op_insn) || + live_inout->live_out_.count(op_insn)) { + // already taken into account. + return; + } + if (!die_in_block.count(*id)) { + if (does_belong_to_loop1) { + l1_reg_count++; + } + if (does_belong_to_loop2) { + l2_reg_count++; + } + die_in_block.insert(*id); + } + }); + l1_sim_result->used_registers_ = + std::max(l1_sim_result->used_registers_, l1_reg_count); + l2_sim_result->used_registers_ = + std::max(l2_sim_result->used_registers_, l2_reg_count); + if (CreatesRegisterUsage(&insn)) { + if (does_belong_to_loop1) { + if (!l1_sim_result->live_in_.count(&insn)) { + l1_sim_result->AddRegisterClass(&insn); + } + l1_reg_count--; + } + if (does_belong_to_loop2) { + if (!l2_sim_result->live_in_.count(&insn)) { + l2_sim_result->AddRegisterClass(&insn); + } + l2_reg_count--; + } + } + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.h b/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.h new file mode 100644 index 00000000..cb3d2e27 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/register_pressure.h @@ -0,0 +1,196 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REGISTER_PRESSURE_H_ +#define SOURCE_OPT_REGISTER_PRESSURE_H_ + +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class Loop; +class LoopDescriptor; + +// Handles the register pressure of a function for different regions (function, +// loop, basic block). It also contains some utilities to foresee the register +// pressure following code transformations. +class RegisterLiveness { + public: + // Classification of SSA registers. + struct RegisterClass { + analysis::Type* type_; + bool is_uniform_; + + bool operator==(const RegisterClass& rhs) const { + return std::tie(type_, is_uniform_) == + std::tie(rhs.type_, rhs.is_uniform_); + } + }; + + struct RegionRegisterLiveness { + using LiveSet = std::unordered_set; + using RegClassSetTy = std::vector>; + + // SSA register live when entering the basic block. + LiveSet live_in_; + // SSA register live when exiting the basic block. + LiveSet live_out_; + + // Maximum number of required registers. + size_t used_registers_; + // Break down of the number of required registers per class of register. + RegClassSetTy registers_classes_; + + void Clear() { + live_out_.clear(); + live_in_.clear(); + used_registers_ = 0; + registers_classes_.clear(); + } + + void AddRegisterClass(const RegisterClass& reg_class) { + auto it = std::find_if( + registers_classes_.begin(), registers_classes_.end(), + [®_class](const std::pair& class_count) { + return class_count.first == reg_class; + }); + if (it != registers_classes_.end()) { + it->second++; + } else { + registers_classes_.emplace_back(std::move(reg_class), + static_cast(1)); + } + } + + void AddRegisterClass(Instruction* insn); + }; + + RegisterLiveness(IRContext* context, Function* f) : context_(context) { + Analyze(f); + } + + // Returns liveness and register information for the basic block |bb|. If no + // entry exist for the basic block, the function returns null. + const RegionRegisterLiveness* Get(const BasicBlock* bb) const { + return Get(bb->id()); + } + + // Returns liveness and register information for the basic block id |bb_id|. + // If no entry exist for the basic block, the function returns null. + const RegionRegisterLiveness* Get(uint32_t bb_id) const { + RegionRegisterLivenessMap::const_iterator it = block_pressure_.find(bb_id); + if (it != block_pressure_.end()) { + return &it->second; + } + return nullptr; + } + + IRContext* GetContext() const { return context_; } + + // Returns liveness and register information for the basic block |bb|. If no + // entry exist for the basic block, the function returns null. + RegionRegisterLiveness* Get(const BasicBlock* bb) { return Get(bb->id()); } + + // Returns liveness and register information for the basic block id |bb_id|. + // If no entry exist for the basic block, the function returns null. + RegionRegisterLiveness* Get(uint32_t bb_id) { + RegionRegisterLivenessMap::iterator it = block_pressure_.find(bb_id); + if (it != block_pressure_.end()) { + return &it->second; + } + return nullptr; + } + + // Returns liveness and register information for the basic block id |bb_id| or + // create a new empty entry if no entry already existed. + RegionRegisterLiveness* GetOrInsert(uint32_t bb_id) { + return &block_pressure_[bb_id]; + } + + // Compute the register pressure for the |loop| and store the result into + // |reg_pressure|. The live-in set corresponds to the live-in set of the + // header block, the live-out set of the loop corresponds to the union of the + // live-in sets of each exit basic block. + void ComputeLoopRegisterPressure(const Loop& loop, + RegionRegisterLiveness* reg_pressure) const; + + // Estimate the register pressure for the |l1| and |l2| as if they were making + // one unique loop. The result is stored into |simulation_result|. + void SimulateFusion(const Loop& l1, const Loop& l2, + RegionRegisterLiveness* simulation_result) const; + + // Estimate the register pressure of |loop| after it has been fissioned + // according to |moved_instructions| and |copied_instructions|. The function + // assumes that the fission creates a new loop before |loop|, moves any + // instructions present inside |moved_instructions| and copies any + // instructions present inside |copied_instructions| into this new loop. + // The set |loop1_sim_result| store the simulation result of the loop with the + // moved instructions. The set |loop2_sim_result| store the simulation result + // of the loop with the removed instructions. + void SimulateFission( + const Loop& loop, + const std::unordered_set& moved_instructions, + const std::unordered_set& copied_instructions, + RegionRegisterLiveness* loop1_sim_result, + RegionRegisterLiveness* loop2_sim_result) const; + + private: + using RegionRegisterLivenessMap = + std::unordered_map; + + IRContext* context_; + RegionRegisterLivenessMap block_pressure_; + + void Analyze(Function* f); +}; + +// Handles the register pressure of a function for different regions (function, +// loop, basic block). It also contains some utilities to foresee the register +// pressure following code transformations. +class LivenessAnalysis { + using LivenessAnalysisMap = + std::unordered_map; + + public: + LivenessAnalysis(IRContext* context) : context_(context) {} + + // Computes the liveness analysis for the function |f| and cache the result. + // If the analysis was performed for this function, then the cached analysis + // is returned. + const RegisterLiveness* Get(Function* f) { + LivenessAnalysisMap::iterator it = analysis_cache_.find(f); + if (it != analysis_cache_.end()) { + return &it->second; + } + return &analysis_cache_.emplace(f, RegisterLiveness{context_, f}) + .first->second; + } + + private: + IRContext* context_; + LivenessAnalysisMap analysis_cache_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REGISTER_PRESSURE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.cpp new file mode 100644 index 00000000..df925a25 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "relax_float_ops_pass.h" + +#include "source/opt/ir_builder.h" + +namespace spvtools { +namespace opt { + +bool RelaxFloatOpsPass::IsRelaxable(Instruction* inst) { + return target_ops_core_f_rslt_.count(inst->opcode()) != 0 || + target_ops_core_f_opnd_.count(inst->opcode()) != 0 || + sample_ops_.count(inst->opcode()) != 0 || + (inst->opcode() == spv::Op::OpExtInst && + inst->GetSingleWordInOperand(0) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0); +} + +bool RelaxFloatOpsPass::IsFloat32(Instruction* inst) { + uint32_t ty_id; + if (target_ops_core_f_opnd_.count(inst->opcode()) != 0) { + uint32_t opnd_id = inst->GetSingleWordInOperand(0); + Instruction* opnd_inst = get_def_use_mgr()->GetDef(opnd_id); + ty_id = opnd_inst->type_id(); + } else { + ty_id = inst->type_id(); + if (ty_id == 0) return false; + } + return IsFloat(ty_id, 32); +} + +bool RelaxFloatOpsPass::IsRelaxed(uint32_t r_id) { + for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false)) + if (r_inst->opcode() == spv::Op::OpDecorate && + spv::Decoration(r_inst->GetSingleWordInOperand(1)) == + spv::Decoration::RelaxedPrecision) + return true; + return false; +} + +bool RelaxFloatOpsPass::ProcessInst(Instruction* r_inst) { + uint32_t r_id = r_inst->result_id(); + if (r_id == 0) return false; + if (!IsFloat32(r_inst)) return false; + if (IsRelaxed(r_id)) return false; + if (!IsRelaxable(r_inst)) return false; + get_decoration_mgr()->AddDecoration( + r_id, uint32_t(spv::Decoration::RelaxedPrecision)); + return true; +} + +bool RelaxFloatOpsPass::ProcessFunction(Function* func) { + bool modified = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= ProcessInst(&*ii); + }); + return modified; +} + +Pass::Status RelaxFloatOpsPass::ProcessImpl() { + Pass::ProcessFunction pfn = [this](Function* fp) { + return ProcessFunction(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status RelaxFloatOpsPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void RelaxFloatOpsPass::Initialize() { + target_ops_core_f_rslt_ = { + spv::Op::OpLoad, + spv::Op::OpPhi, + spv::Op::OpVectorExtractDynamic, + spv::Op::OpVectorInsertDynamic, + spv::Op::OpVectorShuffle, + spv::Op::OpCompositeExtract, + spv::Op::OpCompositeConstruct, + spv::Op::OpCompositeInsert, + spv::Op::OpCopyObject, + spv::Op::OpTranspose, + spv::Op::OpConvertSToF, + spv::Op::OpConvertUToF, + spv::Op::OpFConvert, + // spv::Op::OpQuantizeToF16, + spv::Op::OpFNegate, + spv::Op::OpFAdd, + spv::Op::OpFSub, + spv::Op::OpFMul, + spv::Op::OpFDiv, + spv::Op::OpFMod, + spv::Op::OpVectorTimesScalar, + spv::Op::OpMatrixTimesScalar, + spv::Op::OpVectorTimesMatrix, + spv::Op::OpMatrixTimesVector, + spv::Op::OpMatrixTimesMatrix, + spv::Op::OpOuterProduct, + spv::Op::OpDot, + spv::Op::OpSelect, + }; + target_ops_core_f_opnd_ = { + spv::Op::OpFOrdEqual, + spv::Op::OpFUnordEqual, + spv::Op::OpFOrdNotEqual, + spv::Op::OpFUnordNotEqual, + spv::Op::OpFOrdLessThan, + spv::Op::OpFUnordLessThan, + spv::Op::OpFOrdGreaterThan, + spv::Op::OpFUnordGreaterThan, + spv::Op::OpFOrdLessThanEqual, + spv::Op::OpFUnordLessThanEqual, + spv::Op::OpFOrdGreaterThanEqual, + spv::Op::OpFUnordGreaterThanEqual, + }; + target_ops_450_ = { + GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs, + GLSLstd450FSign, GLSLstd450Floor, GLSLstd450Ceil, GLSLstd450Fract, + GLSLstd450Radians, GLSLstd450Degrees, GLSLstd450Sin, GLSLstd450Cos, + GLSLstd450Tan, GLSLstd450Asin, GLSLstd450Acos, GLSLstd450Atan, + GLSLstd450Sinh, GLSLstd450Cosh, GLSLstd450Tanh, GLSLstd450Asinh, + GLSLstd450Acosh, GLSLstd450Atanh, GLSLstd450Atan2, GLSLstd450Pow, + GLSLstd450Exp, GLSLstd450Log, GLSLstd450Exp2, GLSLstd450Log2, + GLSLstd450Sqrt, GLSLstd450InverseSqrt, GLSLstd450Determinant, + GLSLstd450MatrixInverse, + // TODO(greg-lunarg): GLSLstd450ModfStruct, + GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix, + GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma, + // TODO(greg-lunarg): GLSLstd450FrexpStruct, + GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross, + GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect, + GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp}; + sample_ops_ = {spv::Op::OpImageSampleImplicitLod, + spv::Op::OpImageSampleExplicitLod, + spv::Op::OpImageSampleDrefImplicitLod, + spv::Op::OpImageSampleDrefExplicitLod, + spv::Op::OpImageSampleProjImplicitLod, + spv::Op::OpImageSampleProjExplicitLod, + spv::Op::OpImageSampleProjDrefImplicitLod, + spv::Op::OpImageSampleProjDrefExplicitLod, + spv::Op::OpImageFetch, + spv::Op::OpImageGather, + spv::Op::OpImageDrefGather, + spv::Op::OpImageRead, + spv::Op::OpImageSparseSampleImplicitLod, + spv::Op::OpImageSparseSampleExplicitLod, + spv::Op::OpImageSparseSampleDrefImplicitLod, + spv::Op::OpImageSparseSampleDrefExplicitLod, + spv::Op::OpImageSparseSampleProjImplicitLod, + spv::Op::OpImageSparseSampleProjExplicitLod, + spv::Op::OpImageSparseSampleProjDrefImplicitLod, + spv::Op::OpImageSparseSampleProjDrefExplicitLod, + spv::Op::OpImageSparseFetch, + spv::Op::OpImageSparseGather, + spv::Op::OpImageSparseDrefGather, + spv::Op::OpImageSparseTexelsResident, + spv::Op::OpImageSparseRead}; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.h new file mode 100644 index 00000000..9e4606f8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/relax_float_ops_pass.h @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ +#define LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class RelaxFloatOpsPass : public Pass { + public: + RelaxFloatOpsPass() : Pass() {} + + ~RelaxFloatOpsPass() override = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + } + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "convert-to-half-pass"; } + + private: + // Return true if |inst| can have the RelaxedPrecision decoration applied + // to it. + bool IsRelaxable(Instruction* inst); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and width 32 + bool IsFloat32(Instruction* inst); + + // Return true if |r_id| is decorated with RelaxedPrecision + bool IsRelaxed(uint32_t r_id); + + // If |inst| is an instruction of float32-based type and is not decorated + // RelaxedPrecision, add such a decoration to the module. + bool ProcessInst(Instruction* inst); + + // Call ProcessInst on every instruction in |func|. + bool ProcessFunction(Function* func); + + Pass::Status ProcessImpl(); + + // Initialize state for converting to half + void Initialize(); + + struct hasher { + size_t operator()(const spv::Op& op) const noexcept { + return std::hash()(uint32_t(op)); + } + }; + + // Set of float result core operations to be processed + std::unordered_set target_ops_core_f_rslt_; + + // Set of float operand core operations to be processed + std::unordered_set target_ops_core_f_opnd_; + + // Set of 450 extension operations to be processed + std::unordered_set target_ops_450_; + + // Set of sample operations + std::unordered_set sample_ops_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.cpp new file mode 100644 index 00000000..3750bc1f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/remove_dontinline_pass.h" + +namespace spvtools { +namespace opt { + +Pass::Status RemoveDontInline::Process() { + bool modified = false; + modified = ClearDontInlineFunctionControl(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool RemoveDontInline::ClearDontInlineFunctionControl() { + bool modified = false; + for (auto& func : *get_module()) { + ClearDontInlineFunctionControl(&func); + } + return modified; +} + +bool RemoveDontInline::ClearDontInlineFunctionControl(Function* function) { + constexpr uint32_t kFunctionControlInOperandIdx = 0; + Instruction* function_inst = &function->DefInst(); + uint32_t function_control = + function_inst->GetSingleWordInOperand(kFunctionControlInOperandIdx); + + if ((function_control & uint32_t(spv::FunctionControlMask::DontInline)) == + 0) { + return false; + } + function_control &= ~uint32_t(spv::FunctionControlMask::DontInline); + function_inst->SetInOperand(kFunctionControlInOperandIdx, {function_control}); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.h new file mode 100644 index 00000000..16243199 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_dontinline_pass.h @@ -0,0 +1,42 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ +#define SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class RemoveDontInline : public Pass { + public: + const char* name() const override { return "remove-dont-inline"; } + Status Process() override; + + private: + // Clears the DontInline function control from every function in the module. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(); + + // Clears the DontInline function control from |function|. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(Function* function); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.cpp new file mode 100644 index 00000000..0df559b3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/remove_duplicates_pass.h" + +#include +#include +#include +#include +#include + +#include "source/opcode.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status RemoveDuplicatesPass::Process() { + bool modified = RemoveDuplicateCapabilities(); + modified |= RemoveDuplicatesExtInstImports(); + modified |= RemoveDuplicateTypes(); + modified |= RemoveDuplicateDecorations(); + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const { + bool modified = false; + + if (context()->capabilities().empty()) { + return modified; + } + + std::unordered_set capabilities; + for (auto* i = &*context()->capability_begin(); i;) { + auto res = capabilities.insert(i->GetSingleWordOperand(0u)); + + if (res.second) { + // Never seen before, keep it. + i = i->NextNode(); + } else { + // It's a duplicate, remove it. + i = context()->KillInst(i); + modified = true; + } + } + + return modified; +} + +bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { + bool modified = false; + + if (context()->ext_inst_imports().empty()) { + return modified; + } + + std::unordered_map ext_inst_imports; + for (auto* i = &*context()->ext_inst_import_begin(); i;) { + auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(), + i->result_id()); + if (res.second) { + // Never seen before, keep it. + i = i->NextNode(); + } else { + // It's a duplicate, remove it. + context()->ReplaceAllUsesWith(i->result_id(), res.first->second); + i = context()->KillInst(i); + modified = true; + } + } + + return modified; +} + +bool RemoveDuplicatesPass::RemoveDuplicateTypes() const { + bool modified = false; + + if (context()->types_values().empty()) { + return modified; + } + + analysis::TypeManager type_manager(context()->consumer(), context()); + + std::vector visited_types; + std::vector visited_forward_pointers; + std::vector to_delete; + for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) { + const bool is_i_forward_pointer = + i->opcode() == spv::Op::OpTypeForwardPointer; + + // We only care about types. + if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) { + continue; + } + + if (!is_i_forward_pointer) { + // Is the current type equal to one of the types we have already visited? + spv::Id id_to_keep = 0u; + analysis::Type* i_type = type_manager.GetType(i->result_id()); + assert(i_type); + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + for (auto j : visited_types) { + analysis::Type* j_type = type_manager.GetType(j->result_id()); + assert(j_type); + if (*i_type == *j_type) { + id_to_keep = j->result_id(); + break; + } + } + + if (id_to_keep == 0u) { + // This is a never seen before type, keep it around. + visited_types.emplace_back(i); + } else { + // The same type has already been seen before, remove this one. + context()->KillNamesAndDecorates(i->result_id()); + context()->ReplaceAllUsesWith(i->result_id(), id_to_keep); + modified = true; + to_delete.emplace_back(i); + } + } else { + analysis::ForwardPointer i_type( + i->GetSingleWordInOperand(0u), + (spv::StorageClass)i->GetSingleWordInOperand(1u)); + i_type.SetTargetPointer( + type_manager.GetType(i_type.target_id())->AsPointer()); + + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + const bool found_a_match = + std::find(std::begin(visited_forward_pointers), + std::end(visited_forward_pointers), + i_type) != std::end(visited_forward_pointers); + + if (!found_a_match) { + // This is a never seen before type, keep it around. + visited_forward_pointers.emplace_back(i_type); + } else { + // The same type has already been seen before, remove this one. + modified = true; + to_delete.emplace_back(i); + } + } + } + + for (auto i : to_delete) { + context()->KillInst(i); + } + + return modified; +} + +// TODO(pierremoreau): Duplicate decoration groups should be removed. For +// example, in +// OpDecorate %1 Constant +// %1 = OpDecorationGroup +// OpDecorate %2 Constant +// %2 = OpDecorationGroup +// OpGroupDecorate %1 %3 +// OpGroupDecorate %2 %4 +// group %2 could be removed. +bool RemoveDuplicatesPass::RemoveDuplicateDecorations() const { + bool modified = false; + + std::vector visited_decorations; + + analysis::DecorationManager decoration_manager(context()->module()); + for (auto* i = &*context()->annotation_begin(); i;) { + // Is the current decoration equal to one of the decorations we have + // already visited? + bool already_visited = false; + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + for (const Instruction* j : visited_decorations) { + if (decoration_manager.AreDecorationsTheSame(&*i, j, false)) { + already_visited = true; + break; + } + } + + if (!already_visited) { + // This is a never seen before decoration, keep it around. + visited_decorations.emplace_back(&*i); + i = i->NextNode(); + } else { + // The same decoration has already been seen before, remove this one. + modified = true; + i = context()->KillInst(i); + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.h new file mode 100644 index 00000000..038caa8b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_duplicates_pass.h @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ +#define SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ + +#include +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +using IdDecorationsList = + std::unordered_map>; + +// See optimizer.hpp for documentation. +class RemoveDuplicatesPass : public Pass { + public: + const char* name() const override { return "remove-duplicates"; } + Status Process() override; + + private: + // Remove duplicate capabilities from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateCapabilities() const; + // Remove duplicate extended instruction imports from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicatesExtInstImports() const; + // Remove duplicate types from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateTypes() const; + // Remove duplicate decorations from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateDecorations() const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.cpp new file mode 100644 index 00000000..c3a4b775 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2021 ZHOU He +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "remove_unused_interface_variables_pass.h" +#include "source/spirv_constant.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesContext { + RemoveUnusedInterfaceVariablesPass& parent_; + Instruction& entry_; + std::unordered_set used_variables_; + std::vector operands_to_add_; + + IRContext::ProcessFunction pfn_ = + std::bind(&RemoveUnusedInterfaceVariablesContext::processFunction, this, + std::placeholders::_1); + + bool processFunction(Function* func) { + for (const auto& basic_block : *func) + for (const auto& instruction : basic_block) + instruction.ForEachInId([&](const uint32_t* id) { + if (used_variables_.count(*id)) return; + auto* var = parent_.get_def_use_mgr()->GetDef(*id); + if (!var || var->opcode() != spv::Op::OpVariable) return; + auto storage_class = + spv::StorageClass(var->GetSingleWordInOperand(0)); + if (storage_class != spv::StorageClass::Function && + (parent_.get_module()->version() >= + SPV_SPIRV_VERSION_WORD(1, 4) || + storage_class == spv::StorageClass::Input || + storage_class == spv::StorageClass::Output)) { + used_variables_.insert(*id); + operands_to_add_.push_back(*id); + } + }); + return false; + } + + public: + RemoveUnusedInterfaceVariablesContext( + RemoveUnusedInterfaceVariablesPass& parent, Instruction& entry) + : parent_(parent), entry_(entry) {} + + void CollectUsedVariables() { + std::queue roots; + roots.push(entry_.GetSingleWordInOperand(1)); + parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots); + } + + bool ShouldModify() { + std::unordered_set old_variables; + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) { + auto variable = entry_.GetInOperand(i).words[0]; + if (!used_variables_.count(variable)) return true; // It is unused. + if (old_variables.count(variable)) return true; // It is duplicate. + old_variables.insert(variable); + } + if (old_variables.size() != used_variables_.size()) // Missing IDs. + return true; + return false; + } + + void Modify() { + for (int i = entry_.NumInOperands() - 1; i >= 3; --i) + entry_.RemoveInOperand(i); + for (auto id : operands_to_add_) { + entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id})); + } + } +}; + +RemoveUnusedInterfaceVariablesPass::Status +RemoveUnusedInterfaceVariablesPass::Process() { + bool modified = false; + for (auto& entry : get_module()->entry_points()) { + RemoveUnusedInterfaceVariablesContext context(*this, entry); + context.CollectUsedVariables(); + if (context.ShouldModify()) { + context.Modify(); + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.h new file mode 100644 index 00000000..a4cb1085 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/remove_unused_interface_variables_pass.h @@ -0,0 +1,31 @@ +// Copyright (c) 2021 ZHOU He +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_ +#define SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_ + +#include "source/opt/pass.h" +namespace spvtools { +namespace opt { + +class RemoveUnusedInterfaceVariablesPass : public Pass { + const char* name() const override { + return "remove-unused-interface-variables-pass"; + } + Status Process() override; +}; +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_UNUSED_INTERFACE_VARIABLES_PASS_H_ \ No newline at end of file diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp new file mode 100644 index 00000000..59745e12 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp @@ -0,0 +1,428 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/replace_desc_array_access_using_var_index.h" + +#include "source/opt/desc_sroa_util.h" +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kOpAccessChainInOperandIndexes = 1; +constexpr uint32_t kOpTypePointerInOperandType = 1; +constexpr uint32_t kOpTypeArrayInOperandType = 0; +constexpr uint32_t kOpTypeStructInOperandMember = 0; +IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + +uint32_t GetValueWithKeyExistenceCheck( + uint32_t key, const std::unordered_map& map) { + auto itr = map.find(key); + assert(itr != map.end() && "Key does not exist"); + return itr->second; +} + +} // namespace + +Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + if (descsroautil::IsDescriptorArray(context(), &var)) { + if (ReplaceVariableAccessesWithConstantElements(&var)) + status = Status::SuccessWithChange; + } + } + return status; +} + +bool ReplaceDescArrayAccessUsingVarIndex:: + ReplaceVariableAccessesWithConstantElements(Instruction* var) const { + std::vector work_list; + get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) { + switch (use->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + work_list.push_back(use); + break; + default: + break; + } + }); + + bool updated = false; + for (Instruction* access_chain : work_list) { + if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) == + nullptr) { + ReplaceAccessChain(var, access_chain); + updated = true; + } + } + // Note that we do not consider OpLoad and OpCompositeExtract because + // OpCompositeExtract always has constant literals for indices. + return updated; +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain( + Instruction* var, Instruction* access_chain) const { + uint32_t number_of_elements = + descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var); + assert(number_of_elements != 0 && "Number of element is 0"); + if (number_of_elements == 1) { + UseConstIndexForAccessChain(access_chain, 0); + get_def_use_mgr()->AnalyzeInstUse(access_chain); + return; + } + ReplaceUsersOfAccessChain(access_chain, number_of_elements); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain( + Instruction* access_chain, uint32_t number_of_elements) const { + std::vector final_users; + CollectRecursiveUsersWithConcreteType(access_chain, &final_users); + for (auto* inst : final_users) { + std::deque insts_to_be_cloned = + CollectRequiredImageAndAccessInsts(inst); + ReplaceNonUniformAccessWithSwitchCase( + inst, access_chain, number_of_elements, insts_to_be_cloned); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const { + std::queue work_list; + work_list.push(access_chain); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + get_def_use_mgr()->ForEachUser( + inst_from_work_list, [this, final_users, &work_list](Instruction* use) { + // TODO: Support Boolean type as well. + if (!use->HasResultId() || IsConcreteType(use->type_id())) { + final_users->push_back(use); + } else { + work_list.push(use); + } + }); + } +} + +std::deque +ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageAndAccessInsts( + Instruction* user) const { + std::unordered_set seen_inst_ids; + std::queue work_list; + + auto decision_to_include_operand = [this, &seen_inst_ids, + &work_list](uint32_t* idp) { + if (!seen_inst_ids.insert(*idp).second) return; + Instruction* operand = get_def_use_mgr()->GetDef(*idp); + if (context()->get_instr_block(operand) != nullptr && + (HasImageOrImagePtrType(operand) || + operand->opcode() == spv::Op::OpAccessChain || + operand->opcode() == spv::Op::OpInBoundsAccessChain)) { + work_list.push(operand); + } + }; + + std::deque required_insts; + required_insts.push_front(user); + user->ForEachInId(decision_to_include_operand); + while (!work_list.empty()) { + auto* inst_from_work_list = work_list.front(); + work_list.pop(); + required_insts.push_front(inst_from_work_list); + inst_from_work_list->ForEachInId(decision_to_include_operand); + } + return required_insts; +} + +bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType( + const Instruction* inst) const { + assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction"); + return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id())); +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType( + const Instruction* type_inst) const { + if (type_inst->opcode() == spv::Op::OpTypeImage || + type_inst->opcode() == spv::Op::OpTypeSampler || + type_inst->opcode() == spv::Op::OpTypeSampledImage) { + return true; + } + if (type_inst->opcode() == spv::Op::OpTypePointer) { + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType)); + return IsImageOrImagePtrType(pointee_type_inst); + } + if (type_inst->opcode() == spv::Op::OpTypeArray) { + Instruction* element_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType)); + return IsImageOrImagePtrType(element_type_inst); + } + if (type_inst->opcode() != spv::Op::OpTypeStruct) return false; + for (uint32_t in_operand_idx = kOpTypeStructInOperandMember; + in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) { + Instruction* member_type_inst = get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember)); + if (IsImageOrImagePtrType(member_type_inst)) return true; + } + return false; +} + +bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType( + uint32_t type_id) const { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + if (type_inst->opcode() == spv::Op::OpTypeInt || + type_inst->opcode() == spv::Op::OpTypeFloat) { + return true; + } + if (type_inst->opcode() == spv::Op::OpTypeVector || + type_inst->opcode() == spv::Op::OpTypeMatrix || + type_inst->opcode() == spv::Op::OpTypeArray) { + return IsConcreteType(type_inst->GetSingleWordInOperand(0)); + } + if (type_inst->opcode() == spv::Op::OpTypeStruct) { + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false; + } + return true; + } + return false; +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const { + auto* case_block = CreateNewBlock(); + AddConstElementAccessToCaseBlock(case_block, access_chain, element_index, + old_ids_to_new_ids); + CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned, + old_ids_to_new_ids); + AddBranchToBlock(case_block, branch_target_id); + UseNewIdsInBlock(case_block, *old_ids_to_new_ids); + return case_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const { + for (auto* inst_to_be_cloned : insts_to_be_cloned) { + if (inst_to_be_cloned == inst_to_skip_cloning) continue; + std::unique_ptr clone(inst_to_be_cloned->Clone(context())); + if (inst_to_be_cloned->HasResultId()) { + uint32_t new_id = context()->TakeNextId(); + clone->SetResultId(new_id); + (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id; + } + get_def_use_mgr()->AnalyzeInstDefUse(clone.get()); + context()->set_instr_block(clone.get(), block); + block->AddInstruction(std::move(clone)); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const { + for (auto block_itr = block->begin(); block_itr != block->end(); + ++block_itr) { + (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) { + auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp); + if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return; + *idp = old_ids_to_new_ids_itr->second; + }); + get_def_use_mgr()->AnalyzeInstUse(&*block_itr); + } +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& insts_to_be_cloned) const { + auto* block = context()->get_instr_block(access_chain_final_user); + // If the instruction does not belong to a block (i.e. in the case of + // OpDecorate), no replacement is needed. + if (!block) return; + + // Create merge block and add terminator + auto* merge_block = SeparateInstructionsIntoNewBlock( + block, access_chain_final_user->NextNode()); + + auto* function = block->GetParent(); + + // Add case blocks + std::vector phi_operands; + std::vector case_block_ids; + for (uint32_t idx = 0; idx < number_of_elements; ++idx) { + std::unordered_map old_ids_to_new_ids_for_cloned_insts; + std::unique_ptr case_block(CreateCaseBlock( + access_chain, idx, insts_to_be_cloned, merge_block->id(), + &old_ids_to_new_ids_for_cloned_insts)); + case_block_ids.push_back(case_block->id()); + function->InsertBasicBlockBefore(std::move(case_block), merge_block); + + // Keep the operand for OpPhi + if (!access_chain_final_user->HasResultId()) continue; + uint32_t phi_operand = + GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(), + old_ids_to_new_ids_for_cloned_insts); + phi_operands.push_back(phi_operand); + } + + // Create default block + std::unique_ptr default_block( + CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands, + merge_block->id())); + uint32_t default_block_id = default_block->id(); + function->InsertBasicBlockBefore(std::move(default_block), merge_block); + + // Create OpSwitch + uint32_t access_chain_index_var_id = + descsroautil::GetFirstIndexOfAccessChain(access_chain); + AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id, + merge_block->id(), case_block_ids); + + // Create phi instructions + if (!phi_operands.empty()) { + uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands, + case_block_ids, default_block_id); + context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id); + } + + // Replace OpPhi incoming block operand that uses |block| with |merge_block| + ReplacePhiIncomingBlock(block->id(), merge_block->id()); +} + +BasicBlock* +ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const { + auto separation_begin = block->begin(); + while (separation_begin != block->end() && + &*separation_begin != separation_begin_inst) { + ++separation_begin; + } + return block->SplitBasicBlock(context(), context()->TakeNextId(), + separation_begin); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const { + auto* new_block = new BasicBlock(std::unique_ptr(new Instruction( + context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {}))); + get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + return new_block; +} + +void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain( + Instruction* access_chain, uint32_t const_element_idx) const { + uint32_t const_element_idx_id = + context()->get_constant_mgr()->GetUIntConstId(const_element_idx); + access_chain->SetInOperand(kOpAccessChainInOperandIndexes, + {const_element_idx_id}); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const { + std::unique_ptr access_clone(access_chain->Clone(context())); + UseConstIndexForAccessChain(access_clone.get(), const_element_idx); + + uint32_t new_access_id = context()->TakeNextId(); + (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id; + access_clone->SetResultId(new_access_id); + get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get()); + + context()->set_instr_block(access_clone.get(), case_block); + case_block->AddInstruction(std::move(access_clone)); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock( + BasicBlock* parent_block, uint32_t branch_destination) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + builder.AddBranch(branch_destination); +} + +BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock( + bool null_const_for_phi_is_needed, std::vector* phi_operands, + uint32_t merge_block_id) const { + auto* default_block = CreateNewBlock(); + AddBranchToBlock(default_block, merge_block_id); + if (!null_const_for_phi_is_needed) return default_block; + + // Create null value for OpPhi + Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]); + auto* null_const_inst = GetConstNull(inst->type_id()); + phi_operands->push_back(null_const_inst->result_id()); + return default_block; +} + +Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull( + uint32_t type_id) const { + assert(type_id != 0 && "Result type is expected"); + auto* type = context()->get_type_mgr()->GetType(type_id); + auto* null_const = context()->get_constant_mgr()->GetConstant(type, {}); + return context()->get_constant_mgr()->GetDefiningInstruction(null_const); +} + +void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const { + InstructionBuilder builder{context(), parent_block, + kAnalysisDefUseAndInstrToBlockMapping}; + std::vector> cases; + for (uint32_t i = 0; i < static_cast(case_block_ids.size()); ++i) { + cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]); + } + builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id); +} + +uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction( + BasicBlock* parent_block, const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const { + std::vector incomings; + assert(case_block_ids.size() + 1 == phi_operands.size() && + "Number of Phi operands must be exactly 1 bigger than the one of case " + "blocks"); + for (size_t i = 0; i < case_block_ids.size(); ++i) { + incomings.push_back(phi_operands[i]); + incomings.push_back(case_block_ids[i]); + } + incomings.push_back(phi_operands.back()); + incomings.push_back(default_block_id); + + InstructionBuilder builder{context(), &*parent_block->begin(), + kAnalysisDefUseAndInstrToBlockMapping}; + uint32_t phi_result_type_id = + context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id(); + auto* phi = builder.AddPhi(phi_result_type_id, incomings); + return phi->result_id(); +} + +void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock( + uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const { + context()->ReplaceAllUsesWithPredicate( + old_incoming_block_id, new_incoming_block_id, + [](Instruction* use) { return use->opcode() == spv::Op::OpPhi; }); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h new file mode 100644 index 00000000..51817c15 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h @@ -0,0 +1,205 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ +#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReplaceDescArrayAccessUsingVarIndex : public Pass { + public: + ReplaceDescArrayAccessUsingVarIndex() {} + + const char* name() const override { + return "replace-desc-array-access-using-var-index"; + } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces all accesses to |var| using variable indices with constant + // elements of the array |var|. Creates switch-case statements to determine + // the value of the variable index for all the possible cases. Returns + // whether replacement is done or not. + bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const; + + // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that + // uses the descriptor variable |var| with the OpAccessChain or + // OpInBoundsAccessChain instruction with a constant Indexes operand. + void ReplaceAccessChain(Instruction* var, Instruction* use) const; + + // Updates the first Indexes operand of the OpAccessChain or + // OpInBoundsAccessChain instruction |access_chain| to let it use a constant + // index |const_element_idx|. + void UseConstIndexForAccessChain(Instruction* access_chain, + uint32_t const_element_idx) const; + + // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| that accesses an array descriptor variable using variable + // indices with constant elements. |number_of_elements| is the number + // of array elements. + void ReplaceUsersOfAccessChain(Instruction* access_chain, + uint32_t number_of_elements) const; + + // Puts all the recursive users of |access_chain| with concrete result types + // or the ones without result it in |final_users|. + void CollectRecursiveUsersWithConcreteType( + Instruction* access_chain, std::vector* final_users) const; + + // Recursively collects the operands of |user| (and operands of the operands) + // whose result types are images/samplers (or pointers/arrays/ structs of + // them) and access chains instructions and returns them. The returned + // collection includes |user|. + std::deque CollectRequiredImageAndAccessInsts( + Instruction* user) const; + + // Returns whether result type of |inst| is an image/sampler/pointer of image + // or sampler or not. + bool HasImageOrImagePtrType(const Instruction* inst) const; + + // Returns whether |type_inst| is an image/sampler or pointer/array/struct of + // image or sampler or not. + bool IsImageOrImagePtrType(const Instruction* type_inst) const; + + // Returns whether the type with |type_id| is a concrete type or not. + bool IsConcreteType(uint32_t type_id) const; + + // Replaces the non-uniform access to a descriptor variable + // |access_chain_final_user| with OpSwitch instruction and case blocks. Each + // case block will contain a clone of |access_chain| and clones of + // |non_uniform_accesses_to_clone| that are recursively used by + // |access_chain_final_user|. The clone of |access_chain| (or + // OpInBoundsAccessChain) will have a constant index for its first index. The + // OpSwitch instruction will have the cases for the variable index of + // |access_chain| from 0 to |number_of_elements| - 1. + void ReplaceNonUniformAccessWithSwitchCase( + Instruction* access_chain_final_user, Instruction* access_chain, + uint32_t number_of_elements, + const std::deque& non_uniform_accesses_to_clone) const; + + // Creates and returns a new basic block that contains all instructions of + // |block| after |separation_begin_inst|. The new basic block is added to the + // function in this method. + BasicBlock* SeparateInstructionsIntoNewBlock( + BasicBlock* block, Instruction* separation_begin_inst) const; + + // Creates and returns a new block. + BasicBlock* CreateNewBlock() const; + + // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain + // instruction |access_chain|. + uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const; + + // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain| to |case_block|. The clone of |access_chain| will use + // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the + // mapping from the result id of |access_chain| to the result of its clone. + void AddConstElementAccessToCaseBlock( + BasicBlock* case_block, Instruction* access_chain, + uint32_t const_element_idx, + std::unordered_map* old_ids_to_new_ids) const; + + // Clones all instructions in |insts_to_be_cloned| and put them to |block|. + // |old_ids_to_new_ids| keeps the mapping from the result id of each + // instruction of |insts_to_be_cloned| to the result of their clones. + void CloneInstsToBlock( + BasicBlock* block, Instruction* inst_to_skip_cloning, + const std::deque& insts_to_be_cloned, + std::unordered_map* old_ids_to_new_ids) const; + + // Adds OpBranch to |branch_destination| at the end of |parent_block|. + void AddBranchToBlock(BasicBlock* parent_block, + uint32_t branch_destination) const; + + // Replaces in-operands of all instructions in the basic block |block| using + // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand + // id is a key of |old_ids_to_new_ids|. + void UseNewIdsInBlock( + BasicBlock* block, + const std::unordered_map& old_ids_to_new_ids) const; + + // Creates a case block for |element_index| case. It adds clones of + // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as + // its first index. The termination instruction of the created case block will + // be a branch to |branch_target_id|. Puts old ids to new ids map for the + // cloned instructions in |old_ids_to_new_ids|. + BasicBlock* CreateCaseBlock( + Instruction* access_chain, uint32_t element_index, + const std::deque& insts_to_be_cloned, + uint32_t branch_target_id, + std::unordered_map* old_ids_to_new_ids) const; + + // Creates a default block for switch-case statement that has only a single + // instruction OpBranch whose target is a basic block with |merge_block_id|. + // If |null_const_for_phi_is_needed| is true, gets or creates a default null + // constant value for a phi instruction whose operands are |phi_operands| and + // puts it in |phi_operands|. + BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed, + std::vector* phi_operands, + uint32_t merge_block_id) const; + + // Creates and adds an OpSwitch used for the selection of OpAccessChain whose + // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be + // added at the end of |parent_block|. It will jump to |default_id| for the + // default case and jumps to one of case blocks whose ids are |case_block_ids| + // if |access_chain_index_var_id| matches the case number. |merge_id| is the + // merge block id. + void AddSwitchForAccessChain( + BasicBlock* parent_block, uint32_t access_chain_index_var_id, + uint32_t default_id, uint32_t merge_id, + const std::vector& case_block_ids) const; + + // Creates a phi instruction with |phi_operands| as values and + // |case_block_ids| and |default_block_id| as incoming blocks. The size of + // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|. + // The last element of |phi_operands| will be used for |default_block_id|. It + // adds the phi instruction to the beginning of |parent_block|. + uint32_t CreatePhiInstruction(BasicBlock* parent_block, + const std::vector& phi_operands, + const std::vector& case_block_ids, + uint32_t default_block_id) const; + + // Replaces the incoming block operand of OpPhi instructions with + // |new_incoming_block_id| if the incoming block operand is + // |old_incoming_block_id|. + void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id, + uint32_t new_incoming_block_id) const; + + // Create an OpConstantNull instruction whose result type id is |type_id|. + Instruction* GetConstNull(uint32_t type_id) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.cpp new file mode 100644 index 00000000..1b97c0e8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.cpp @@ -0,0 +1,219 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/replace_invalid_opc.h" + +#include +#include + +namespace spvtools { +namespace opt { + +Pass::Status ReplaceInvalidOpcodePass::Process() { + bool modified = false; + + if (context()->get_feature_mgr()->HasCapability(spv::Capability::Linkage)) { + return Status::SuccessWithoutChange; + } + + spv::ExecutionModel execution_model = GetExecutionModel(); + if (execution_model == spv::ExecutionModel::Kernel) { + // We do not handle kernels. + return Status::SuccessWithoutChange; + } + if (execution_model == spv::ExecutionModel::Max) { + // Mixed execution models for the entry points. This case is not currently + // handled. + return Status::SuccessWithoutChange; + } + + for (Function& func : *get_module()) { + modified |= RewriteFunction(&func, execution_model); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +spv::ExecutionModel ReplaceInvalidOpcodePass::GetExecutionModel() { + spv::ExecutionModel result = spv::ExecutionModel::Max; + bool first = true; + for (Instruction& entry_point : get_module()->entry_points()) { + if (first) { + result = static_cast( + entry_point.GetSingleWordInOperand(0)); + first = false; + } else { + spv::ExecutionModel current_model = static_cast( + entry_point.GetSingleWordInOperand(0)); + if (current_model != result) { + result = spv::ExecutionModel::Max; + break; + } + } + } + return result; +} + +bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, + spv::ExecutionModel model) { + bool modified = false; + Instruction* last_line_dbg_inst = nullptr; + function->ForEachInst( + [model, &modified, &last_line_dbg_inst, this](Instruction* inst) { + // Track the debug information so we can have a meaningful message. + if (inst->opcode() == spv::Op::OpLabel || inst->IsNoLine()) { + last_line_dbg_inst = nullptr; + return; + } else if (inst->IsLine()) { + last_line_dbg_inst = inst; + return; + } + + bool replace = false; + if (model != spv::ExecutionModel::Fragment && + IsFragmentShaderOnlyInstruction(inst)) { + replace = true; + } + + if (model != spv::ExecutionModel::TessellationControl && + model != spv::ExecutionModel::GLCompute && + !context()->IsTargetEnvAtLeast(SPV_ENV_UNIVERSAL_1_3)) { + if (inst->opcode() == spv::Op::OpControlBarrier) { + assert(model != spv::ExecutionModel::Kernel && + "Expecting to be working on a shader module."); + replace = true; + } + } + + if (replace) { + modified = true; + if (last_line_dbg_inst == nullptr) { + ReplaceInstruction(inst, nullptr, 0, 0); + } else { + // Get the name of the source file. + uint32_t file_name_id = 0; + if (last_line_dbg_inst->opcode() == spv::Op::OpLine) { + file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0); + } else { // Shader100::DebugLine + uint32_t debug_source_id = + last_line_dbg_inst->GetSingleWordInOperand(2); + Instruction* debug_source_inst = + context()->get_def_use_mgr()->GetDef(debug_source_id); + file_name_id = debug_source_inst->GetSingleWordInOperand(2); + } + Instruction* file_name = + context()->get_def_use_mgr()->GetDef(file_name_id); + const std::string source = file_name->GetInOperand(0).AsString(); + + // Get the line number and column number. + uint32_t line_number = + last_line_dbg_inst->GetSingleWordInOperand(1); + uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2); + + // Replace the instruction. + ReplaceInstruction(inst, source.c_str(), line_number, col_number); + } + } + }, + /* run_on_debug_line_insts = */ true); + return modified; +} + +bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction( + Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpDPdx: + case spv::Op::OpDPdy: + case spv::Op::OpFwidth: + case spv::Op::OpDPdxFine: + case spv::Op::OpDPdyFine: + case spv::Op::OpFwidthFine: + case spv::Op::OpDPdxCoarse: + case spv::Op::OpDPdyCoarse: + case spv::Op::OpFwidthCoarse: + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageQueryLod: + // TODO: Teach |ReplaceInstruction| to handle block terminators. Then + // uncomment the OpKill case. + // case spv::Op::OpKill: + // case spv::Op::OpTerminateInstruction: + return true; + default: + return false; + } +} + +void ReplaceInvalidOpcodePass::ReplaceInstruction(Instruction* inst, + const char* source, + uint32_t line_number, + uint32_t column_number) { + if (inst->result_id() != 0) { + uint32_t const_id = GetSpecialConstant(inst->type_id()); + context()->KillNamesAndDecorates(inst); + context()->ReplaceAllUsesWith(inst->result_id(), const_id); + } + assert(!inst->IsBlockTerminator() && + "We cannot simply delete a block terminator. It must be replaced " + "with something."); + if (consumer()) { + std::string message = BuildWarningMessage(inst->opcode()); + consumer()(SPV_MSG_WARNING, source, {line_number, column_number, 0}, + message.c_str()); + } + context()->KillInst(inst); +} + +uint32_t ReplaceInvalidOpcodePass::GetSpecialConstant(uint32_t type_id) { + const analysis::Constant* special_const = nullptr; + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id); + if (type->opcode() == spv::Op::OpTypeVector) { + uint32_t component_const = + GetSpecialConstant(type->GetSingleWordInOperand(0)); + std::vector ids; + for (uint32_t i = 0; i < type->GetSingleWordInOperand(1); ++i) { + ids.push_back(component_const); + } + special_const = const_mgr->GetConstant(type_mgr->GetType(type_id), ids); + } else { + assert(type->opcode() == spv::Op::OpTypeInt || + type->opcode() == spv::Op::OpTypeFloat); + std::vector literal_words; + for (uint32_t i = 0; i < type->GetSingleWordInOperand(0); i += 32) { + literal_words.push_back(0xDEADBEEF); + } + special_const = + const_mgr->GetConstant(type_mgr->GetType(type_id), literal_words); + } + assert(special_const != nullptr); + return const_mgr->GetDefiningInstruction(special_const)->result_id(); +} + +std::string ReplaceInvalidOpcodePass::BuildWarningMessage(spv::Op opcode) { + spv_opcode_desc opcode_info; + context()->grammar().lookupOpcode(opcode, &opcode_info); + std::string message = "Removing "; + message += opcode_info->name; + message += " instruction because of incompatible execution model."; + return message; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.h b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.h new file mode 100644 index 00000000..3f0d16bb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/replace_invalid_opc.h @@ -0,0 +1,67 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REPLACE_INVALID_OPC_H_ +#define SOURCE_OPT_REPLACE_INVALID_OPC_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass will runs on shader modules only. It will replace the result of +// instructions that are valid for shader modules, but not the current shader +// stage, with a constant value. If the instruction does not have a return +// value, the instruction will simply be deleted. +class ReplaceInvalidOpcodePass : public Pass { + public: + const char* name() const override { return "replace-invalid-opcode"; } + Status Process() override; + + private: + // Returns the execution model that is used by every entry point in the + // module. If more than one execution model is used in the module, then the + // return value is spv::ExecutionModel::Max. + spv::ExecutionModel GetExecutionModel(); + + // Replaces all instructions in |function| that are invalid with execution + // model |mode|, but valid for another shader model, with a special constant + // value. See |GetSpecialConstant|. + bool RewriteFunction(Function* function, spv::ExecutionModel mode); + + // Returns true if |inst| is valid for fragment shaders only. + bool IsFragmentShaderOnlyInstruction(Instruction* inst); + + // Replaces all uses of the result of |inst|, if there is one, with the id of + // a special constant. Then |inst| is killed. |inst| cannot be a block + // terminator because the basic block will then become invalid. |inst| is no + // longer valid after calling this function. + void ReplaceInstruction(Instruction* inst, const char* source, + uint32_t line_number, uint32_t column_number); + + // Returns the id of a constant with type |type_id|. The type must be an + // integer, float, or vector. For scalar types, the hex representation of the + // constant will be the concatenation of 0xDEADBEEF with itself until the + // width of the type has been reached. For a vector, each element of the + // constant will be constructed the same way. + uint32_t GetSpecialConstant(uint32_t type_id); + std::string BuildWarningMessage(spv::Op opcode); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_INVALID_OPC_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.cpp new file mode 100644 index 00000000..18ea2e59 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.cpp @@ -0,0 +1,328 @@ +// Copyright (c) 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/resolve_binding_conflicts_pass.h" + +#include +#include +#include +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace opt { + +// A VarBindingInfo contains the binding information for a single resource +// variable. +// +// Exactly one such object is created per resource variable in the +// module. In particular, when a resource variable is statically used by +// more than one entry point, those entry points share the same VarBindingInfo +// object for that variable. +struct VarBindingInfo { + const Instruction* const var; + const uint32_t descriptor_set; + Instruction* const binding_decoration; + + // Returns the binding number. + uint32_t binding() const { + return binding_decoration->GetSingleWordInOperand(2); + } + // Sets the binding number to 'b'. + void updateBinding(uint32_t b) { binding_decoration->SetOperand(2, {b}); } +}; + +// The bindings in the same descriptor set that are used by an entry point. +using BindingList = std::vector; +// A map from descriptor set number to the list of bindings in that descriptor +// set, as used by a particular entry point. +using DescriptorSets = std::unordered_map; + +IRContext::Analysis ResolveBindingConflictsPass::GetPreservedAnalyses() { + // All analyses are kept up to date. + // At most this modifies the Binding numbers on variables. + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes | IRContext::kAnalysisDebugInfo | + IRContext::kAnalysisLiveness; +} + +// Orders variable binding info objects. +// * The binding number is most signficant; +// * Then a sampler-like object compares greater than non-sampler like object. +// * Otherwise compare based on variable ID. +// This provides a total order among bindings in a descriptor set for a valid +// Vulkan module. +bool Less(const VarBindingInfo* const lhs, const VarBindingInfo* const rhs) { + if (lhs->binding() < rhs->binding()) return true; + if (lhs->binding() > rhs->binding()) return false; + + // Examine types. + // In valid Vulkan the only conflict can occur between + // images and samplers. We only care about a specific + // comparison when one is a image-like thing and the other + // is a sampler-like thing of the same shape. So unwrap + // types until we hit one of those two. + + auto* def_use_mgr = lhs->var->context()->get_def_use_mgr(); + + // Returns the type found by iteratively following pointer pointee type, + // or array element type. + auto unwrap = [&def_use_mgr](Instruction* ty) { + bool keep_going = true; + do { + switch (ty->opcode()) { + case spv::Op::OpTypePointer: + ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(0)); + break; + default: + keep_going = false; + break; + } + } while (keep_going); + return ty; + }; + + auto* lhs_ty = unwrap(def_use_mgr->GetDef(lhs->var->type_id())); + auto* rhs_ty = unwrap(def_use_mgr->GetDef(rhs->var->type_id())); + if (lhs_ty->opcode() == rhs_ty->opcode()) { + // Pick based on variable ID. + return lhs->var->result_id() < rhs->var->result_id(); + } + // A sampler is always greater than an image. + if (lhs_ty->opcode() == spv::Op::OpTypeSampler) { + return false; + } + if (rhs_ty->opcode() == spv::Op::OpTypeSampler) { + return true; + } + // Pick based on variable ID. + return lhs->var->result_id() < rhs->var->result_id(); +} + +// Summarizes the caller-callee relationships between functions in a module. +class CallGraph { + public: + // Returns the list of all functions statically reachable from entry points, + // where callees precede callers. + const std::vector& CalleesBeforeCallers() const { + return visit_order_; + } + // Returns the list functions called from a given function. + const std::unordered_set& Callees(uint32_t caller) { + return calls_[caller]; + } + + CallGraph(IRContext& context) { + // Populate calls_. + std::queue callee_queue; + for (const auto& fn : *context.module()) { + auto& callees = calls_[fn.result_id()]; + context.AddCalls(&fn, &callee_queue); + while (!callee_queue.empty()) { + callees.insert(callee_queue.front()); + callee_queue.pop(); + } + } + + // Perform depth-first search, starting from each entry point. + // Populates visit_order_. + for (const auto& ep : context.module()->entry_points()) { + Visit(ep.GetSingleWordInOperand(1)); + } + } + + private: + // Visits a function, recursively visiting its callees. Adds this ID + // to the visit_order after all callees have been visited. + void Visit(uint32_t func_id) { + if (visited_.count(func_id)) { + return; + } + visited_.insert(func_id); + for (auto callee_id : calls_[func_id]) { + Visit(callee_id); + } + visit_order_.push_back(func_id); + } + + // Maps the ID of a function to the IDs of functions it calls. + std::unordered_map> calls_; + + // IDs of visited functions; + std::unordered_set visited_; + // IDs of functions, where callees precede callers. + std::vector visit_order_; +}; + +// Returns vector binding info for all resource variables in the module. +auto GetVarBindings(IRContext& context) { + std::vector vars; + auto* deco_mgr = context.get_decoration_mgr(); + for (auto& inst : context.module()->types_values()) { + if (inst.opcode() == spv::Op::OpVariable) { + Instruction* descriptor_set_deco = nullptr; + Instruction* binding_deco = nullptr; + for (auto* deco : deco_mgr->GetDecorationsFor(inst.result_id(), false)) { + switch (static_cast(deco->GetSingleWordInOperand(1))) { + case spv::Decoration::DescriptorSet: + assert(!descriptor_set_deco); + descriptor_set_deco = deco; + break; + case spv::Decoration::Binding: + assert(!binding_deco); + binding_deco = deco; + break; + default: + break; + } + } + if (descriptor_set_deco && binding_deco) { + vars.push_back({&inst, descriptor_set_deco->GetSingleWordInOperand(2), + binding_deco}); + } + } + } + return vars; +} + +// Merges the bindings from source into sink. Maintains order and uniqueness +// within a list of bindings. +void Merge(DescriptorSets& sink, const DescriptorSets& source) { + for (auto index_and_bindings : source) { + const uint32_t index = index_and_bindings.first; + const BindingList& src1 = index_and_bindings.second; + const BindingList& src2 = sink[index]; + BindingList merged; + merged.resize(src1.size() + src2.size()); + auto merged_end = std::merge(src1.begin(), src1.end(), src2.begin(), + src2.end(), merged.begin(), Less); + auto unique_end = std::unique(merged.begin(), merged_end); + merged.resize(unique_end - merged.begin()); + sink[index] = std::move(merged); + } +} + +// Resolves conflicts within this binding list, so the binding number on an +// item is at least one more than the binding number on the previous item. +// When this does not yet hold, increase the binding number on the second +// item in the pair. Returns true if any changes were applied. +bool ResolveConflicts(BindingList& bl) { + bool changed = false; + for (size_t i = 1; i < bl.size(); i++) { + const auto prev_num = bl[i - 1]->binding(); + if (prev_num >= bl[i]->binding()) { + bl[i]->updateBinding(prev_num + 1); + changed = true; + } + } + return changed; +} + +Pass::Status ResolveBindingConflictsPass::Process() { + // Assumes the descriptor set and binding decorations are not provided + // via decoration groups. Decoration groups were deprecated in SPIR-V 1.3 + // Revision 6. I have not seen any compiler generate them. --dneto + + auto vars = GetVarBindings(*context()); + + // Maps a function ID to the variables used directly or indirectly by the + // function, organized into descriptor sets. Each descriptor set + // consists of a BindingList of distinct variables. + std::unordered_map used_vars; + + // Determine variables directly used by functions. + auto* def_use_mgr = context()->get_def_use_mgr(); + for (auto& var : vars) { + std::unordered_set visited_functions_for_var; + def_use_mgr->ForEachUser(var.var, [&](Instruction* user) { + if (auto* block = context()->get_instr_block(user)) { + auto* fn = block->GetParent(); + assert(fn); + const auto fn_id = fn->result_id(); + if (visited_functions_for_var.insert(fn_id).second) { + used_vars[fn_id][var.descriptor_set].push_back(&var); + } + } + }); + } + + // Sort within a descriptor set by binding number. + for (auto& sets_for_fn : used_vars) { + for (auto& ds : sets_for_fn.second) { + BindingList& bindings = ds.second; + std::stable_sort(bindings.begin(), bindings.end(), Less); + } + } + + // Propagate from callees to callers. + CallGraph call_graph(*context()); + for (const uint32_t caller : call_graph.CalleesBeforeCallers()) { + DescriptorSets& caller_ds = used_vars[caller]; + for (const uint32_t callee : call_graph.Callees(caller)) { + Merge(caller_ds, used_vars[callee]); + } + } + + // At this point, the descriptor sets associated with each entry point + // capture exactly the set of resource variables statically used + // by the static call tree of that entry point. + + // Resolve conflicts. + // VarBindingInfo objects may be shared between the bindings lists. + // Updating a binding in one list can require updating another list later. + // So repeat updates until settling. + + // The union of BindingLists across all entry points. + std::vector ep_bindings; + + for (auto& ep : context()->module()->entry_points()) { + for (auto& ds : used_vars[ep.GetSingleWordInOperand(1)]) { + BindingList& bindings = ds.second; + ep_bindings.push_back(&bindings); + } + } + bool modified = false; + bool found_conflict; + do { + found_conflict = false; + for (BindingList* bl : ep_bindings) { + found_conflict |= ResolveConflicts(*bl); + } + modified |= found_conflict; + } while (found_conflict); + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.h new file mode 100644 index 00000000..d1ab433d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/resolve_binding_conflicts_pass.h @@ -0,0 +1,38 @@ +// Copyright (c) 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_ +#define LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_ + +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opt/pass.h" +#include "source/util/small_vector.h" + +namespace spvtools { +namespace opt { +class ResolveBindingConflictsPass : public Pass { + public: + virtual ~ResolveBindingConflictsPass() override = default; + const char* name() const override { return "resolve-binding-conflicts"; } + IRContext::Analysis GetPreservedAnalyses() override; + Status Process() override; +}; +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.cpp new file mode 100644 index 00000000..26cc8b30 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.cpp @@ -0,0 +1,987 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/scalar_analysis.h" + +#include +#include +#include + +#include "source/opt/ir_context.h" + +// Transforms a given scalar operation instruction into a DAG representation. +// +// 1. Take an instruction and traverse its operands until we reach a +// constant node or an instruction which we do not know how to compute the +// value, such as a load. +// +// 2. Create a new node for each instruction traversed and build the nodes for +// the in operands of that instruction as well. +// +// 3. Add the operand nodes as children of the first and hash the node. Use the +// hash to see if the node is already in the cache. We ensure the children are +// always in sorted order so that two nodes with the same children but inserted +// in a different order have the same hash and so that the overloaded operator== +// will return true. If the node is already in the cache return the cached +// version instead. +// +// 4. The created DAG can then be simplified by +// ScalarAnalysis::SimplifyExpression, implemented in +// scalar_analysis_simplification.cpp. See that file for further information on +// the simplification process. +// + +namespace spvtools { +namespace opt { + +uint32_t SENode::NumberOfNodes = 0; + +ScalarEvolutionAnalysis::ScalarEvolutionAnalysis(IRContext* context) + : context_(context), pretend_equal_{} { + // Create and cached the CantComputeNode. + cached_cant_compute_ = + GetCachedOrAdd(std::unique_ptr(new SECantCompute(this))); +} + +SENode* ScalarEvolutionAnalysis::CreateNegation(SENode* operand) { + // If operand is can't compute then the whole graph is can't compute. + if (operand->IsCantCompute()) return CreateCantComputeNode(); + + if (operand->GetType() == SENode::Constant) { + return CreateConstant(-operand->AsSEConstantNode()->FoldToSingleValue()); + } + std::unique_ptr negation_node{new SENegative(this)}; + negation_node->AddChild(operand); + return GetCachedOrAdd(std::move(negation_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateConstant(int64_t integer) { + return GetCachedOrAdd( + std::unique_ptr(new SEConstantNode(this, integer))); +} + +SENode* ScalarEvolutionAnalysis::CreateRecurrentExpression( + const Loop* loop, SENode* offset, SENode* coefficient) { + assert(loop && "Recurrent add expressions must have a valid loop."); + + // If operands are can't compute then the whole graph is can't compute. + if (offset->IsCantCompute() || coefficient->IsCantCompute()) + return CreateCantComputeNode(); + + const Loop* loop_to_use = nullptr; + if (pretend_equal_[loop]) { + loop_to_use = pretend_equal_[loop]; + } else { + loop_to_use = loop; + } + + std::unique_ptr phi_node{ + new SERecurrentNode(this, loop_to_use)}; + phi_node->AddOffset(offset); + phi_node->AddCoefficient(coefficient); + + return GetCachedOrAdd(std::move(phi_node)); +} + +SENode* ScalarEvolutionAnalysis::AnalyzeMultiplyOp( + const Instruction* multiply) { + assert(multiply->opcode() == spv::Op::OpIMul && + "Multiply node did not come from a multiply instruction"); + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + SENode* op1 = + AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(0))); + SENode* op2 = + AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(1))); + + return CreateMultiplyNode(op1, op2); +} + +SENode* ScalarEvolutionAnalysis::CreateMultiplyNode(SENode* operand_1, + SENode* operand_2) { + // If operands are can't compute then the whole graph is can't compute. + if (operand_1->IsCantCompute() || operand_2->IsCantCompute()) + return CreateCantComputeNode(); + + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() * + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + std::unique_ptr multiply_node{new SEMultiplyNode(this)}; + + multiply_node->AddChild(operand_1); + multiply_node->AddChild(operand_2); + + return GetCachedOrAdd(std::move(multiply_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateSubtraction(SENode* operand_1, + SENode* operand_2) { + // Fold if both operands are constant. + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() - + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + return CreateAddNode(operand_1, CreateNegation(operand_2)); +} + +SENode* ScalarEvolutionAnalysis::CreateAddNode(SENode* operand_1, + SENode* operand_2) { + // Fold if both operands are constant and the |simplify| flag is true. + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() + + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + // If operands are can't compute then the whole graph is can't compute. + if (operand_1->IsCantCompute() || operand_2->IsCantCompute()) + return CreateCantComputeNode(); + + std::unique_ptr add_node{new SEAddNode(this)}; + + add_node->AddChild(operand_1); + add_node->AddChild(operand_2); + + return GetCachedOrAdd(std::move(add_node)); +} + +SENode* ScalarEvolutionAnalysis::AnalyzeInstruction(const Instruction* inst) { + auto itr = recurrent_node_map_.find(inst); + if (itr != recurrent_node_map_.end()) return itr->second; + + SENode* output = nullptr; + switch (inst->opcode()) { + case spv::Op::OpPhi: { + output = AnalyzePhiInstruction(inst); + break; + } + case spv::Op::OpConstant: + case spv::Op::OpConstantNull: { + output = AnalyzeConstant(inst); + break; + } + case spv::Op::OpISub: + case spv::Op::OpIAdd: { + output = AnalyzeAddOp(inst); + break; + } + case spv::Op::OpIMul: { + output = AnalyzeMultiplyOp(inst); + break; + } + default: { + output = CreateValueUnknownNode(inst); + break; + } + } + + return output; +} + +SENode* ScalarEvolutionAnalysis::AnalyzeConstant(const Instruction* inst) { + if (inst->opcode() == spv::Op::OpConstantNull) return CreateConstant(0); + + assert(inst->opcode() == spv::Op::OpConstant); + assert(inst->NumInOperands() == 1); + int64_t value = 0; + + // Look up the instruction in the constant manager. + const analysis::Constant* constant = + context_->get_constant_mgr()->FindDeclaredConstant(inst->result_id()); + + if (!constant) return CreateCantComputeNode(); + + const analysis::IntConstant* int_constant = constant->AsIntConstant(); + + // Exit out if it is a 64 bit integer. + if (!int_constant || int_constant->words().size() != 1) + return CreateCantComputeNode(); + + if (int_constant->type()->AsInteger()->IsSigned()) { + value = int_constant->GetS32BitValue(); + } else { + value = int_constant->GetU32BitValue(); + } + + return CreateConstant(value); +} + +// Handles both addition and subtraction. If the |sub| flag is set then the +// addition will be op1+(-op2) otherwise op1+op2. +SENode* ScalarEvolutionAnalysis::AnalyzeAddOp(const Instruction* inst) { + assert((inst->opcode() == spv::Op::OpIAdd || + inst->opcode() == spv::Op::OpISub) && + "Add node must be created from a OpIAdd or OpISub instruction"); + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + SENode* op1 = + AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(0))); + + SENode* op2 = + AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(1))); + + // To handle subtraction we wrap the second operand in a unary negation node. + if (inst->opcode() == spv::Op::OpISub) { + op2 = CreateNegation(op2); + } + + return CreateAddNode(op1, op2); +} + +SENode* ScalarEvolutionAnalysis::AnalyzePhiInstruction(const Instruction* phi) { + // The phi should only have two incoming value pairs. + if (phi->NumInOperands() != 4) { + return CreateCantComputeNode(); + } + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + // Get the basic block this instruction belongs to. + BasicBlock* basic_block = + context_->get_instr_block(const_cast(phi)); + + // And then the function that the basic blocks belongs to. + Function* function = basic_block->GetParent(); + + // Use the function to get the loop descriptor. + LoopDescriptor* loop_descriptor = context_->GetLoopDescriptor(function); + + // We only handle phis in loops at the moment. + if (!loop_descriptor) return CreateCantComputeNode(); + + // Get the innermost loop which this block belongs to. + Loop* loop = (*loop_descriptor)[basic_block->id()]; + + // If the loop doesn't exist or doesn't have a preheader or latch block, exit + // out. + if (!loop || !loop->GetLatchBlock() || !loop->GetPreHeaderBlock() || + loop->GetHeaderBlock() != basic_block) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + const Loop* loop_to_use = nullptr; + if (pretend_equal_[loop]) { + loop_to_use = pretend_equal_[loop]; + } else { + loop_to_use = loop; + } + std::unique_ptr phi_node{ + new SERecurrentNode(this, loop_to_use)}; + + // We add the node to this map to allow it to be returned before the node is + // fully built. This is needed as the subsequent call to AnalyzeInstruction + // could lead back to this |phi| instruction so we return the pointer + // immediately in AnalyzeInstruction to break the recursion. + recurrent_node_map_[phi] = phi_node.get(); + + // Traverse the operands of the instruction an create new nodes for each one. + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t value_id = phi->GetSingleWordInOperand(i); + uint32_t incoming_label_id = phi->GetSingleWordInOperand(i + 1); + + Instruction* value_inst = def_use->GetDef(value_id); + SENode* value_node = AnalyzeInstruction(value_inst); + + // If any operand is CantCompute then the whole graph is CantCompute. + if (value_node->IsCantCompute()) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + // If the value is coming from the preheader block then the value is the + // initial value of the phi. + if (incoming_label_id == loop->GetPreHeaderBlock()->id()) { + phi_node->AddOffset(value_node); + } else if (incoming_label_id == loop->GetLatchBlock()->id()) { + // Assumed to be in the form of step + phi. + if (value_node->GetType() != SENode::Add) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + SENode* step_node = nullptr; + SENode* phi_operand = nullptr; + SENode* operand_1 = value_node->GetChild(0); + SENode* operand_2 = value_node->GetChild(1); + + // Find which node is the step term. + if (!operand_1->AsSERecurrentNode()) + step_node = operand_1; + else if (!operand_2->AsSERecurrentNode()) + step_node = operand_2; + + // Find which node is the recurrent expression. + if (operand_1->AsSERecurrentNode()) + phi_operand = operand_1; + else if (operand_2->AsSERecurrentNode()) + phi_operand = operand_2; + + // If it is not in the form step + phi exit out. + if (!(step_node && phi_operand)) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + // If the phi operand is not the same phi node exit out. + if (phi_operand != phi_node.get()) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + if (!IsLoopInvariant(loop, step_node)) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + phi_node->AddCoefficient(step_node); + } + } + + // Once the node is fully built we update the map with the version from the + // cache (if it has already been added to the cache). + return recurrent_node_map_[phi] = GetCachedOrAdd(std::move(phi_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateValueUnknownNode( + const Instruction* inst) { + std::unique_ptr load_node{ + new SEValueUnknown(this, inst->result_id())}; + return GetCachedOrAdd(std::move(load_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateCantComputeNode() { + return cached_cant_compute_; +} + +// Add the created node into the cache of nodes. If it already exists return it. +SENode* ScalarEvolutionAnalysis::GetCachedOrAdd( + std::unique_ptr prospective_node) { + auto itr = node_cache_.find(prospective_node); + if (itr != node_cache_.end()) { + return (*itr).get(); + } + + SENode* raw_ptr_to_node = prospective_node.get(); + node_cache_.insert(std::move(prospective_node)); + return raw_ptr_to_node; +} + +bool ScalarEvolutionAnalysis::IsLoopInvariant(const Loop* loop, + const SENode* node) const { + for (auto itr = node->graph_cbegin(); itr != node->graph_cend(); ++itr) { + if (const SERecurrentNode* rec = itr->AsSERecurrentNode()) { + const BasicBlock* header = rec->GetLoop()->GetHeaderBlock(); + + // If the loop which the recurrent expression belongs to is either |loop + // or a nested loop inside |loop| then we assume it is variant. + if (loop->IsInsideLoop(header)) { + return false; + } + } else if (const SEValueUnknown* unknown = itr->AsSEValueUnknown()) { + // If the instruction is inside the loop we conservatively assume it is + // loop variant. + if (loop->IsInsideLoop(unknown->ResultId())) return false; + } + } + + return true; +} + +SENode* ScalarEvolutionAnalysis::GetCoefficientFromRecurrentTerm( + SENode* node, const Loop* loop) { + // Traverse the DAG to find the recurrent expression belonging to |loop|. + for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) { + SERecurrentNode* rec = itr->AsSERecurrentNode(); + if (rec && rec->GetLoop() == loop) { + return rec->GetCoefficient(); + } + } + return CreateConstant(0); +} + +SENode* ScalarEvolutionAnalysis::UpdateChildNode(SENode* parent, + SENode* old_child, + SENode* new_child) { + // Only handles add. + if (parent->GetType() != SENode::Add) return parent; + + std::vector new_children; + for (SENode* child : *parent) { + if (child == old_child) { + new_children.push_back(new_child); + } else { + new_children.push_back(child); + } + } + + std::unique_ptr add_node{new SEAddNode(this)}; + for (SENode* child : new_children) { + add_node->AddChild(child); + } + + return SimplifyExpression(GetCachedOrAdd(std::move(add_node))); +} + +// Rebuild the |node| eliminating, if it exists, the recurrent term which +// belongs to the |loop|. +SENode* ScalarEvolutionAnalysis::BuildGraphWithoutRecurrentTerm( + SENode* node, const Loop* loop) { + // If the node is already a recurrent expression belonging to loop then just + // return the offset. + SERecurrentNode* recurrent = node->AsSERecurrentNode(); + if (recurrent) { + if (recurrent->GetLoop() == loop) { + return recurrent->GetOffset(); + } else { + return node; + } + } + + std::vector new_children; + // Otherwise find the recurrent node in the children of this node. + for (auto itr : *node) { + recurrent = itr->AsSERecurrentNode(); + if (recurrent && recurrent->GetLoop() == loop) { + new_children.push_back(recurrent->GetOffset()); + } else { + new_children.push_back(itr); + } + } + + std::unique_ptr add_node{new SEAddNode(this)}; + for (SENode* child : new_children) { + add_node->AddChild(child); + } + + return SimplifyExpression(GetCachedOrAdd(std::move(add_node))); +} + +// Return the recurrent term belonging to |loop| if it appears in the graph +// starting at |node| or null if it doesn't. +SERecurrentNode* ScalarEvolutionAnalysis::GetRecurrentTerm(SENode* node, + const Loop* loop) { + for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) { + SERecurrentNode* rec = itr->AsSERecurrentNode(); + if (rec && rec->GetLoop() == loop) { + return rec; + } + } + return nullptr; +} +std::string SENode::AsString() const { + switch (GetType()) { + case Constant: + return "Constant"; + case RecurrentAddExpr: + return "RecurrentAddExpr"; + case Add: + return "Add"; + case Negative: + return "Negative"; + case Multiply: + return "Multiply"; + case ValueUnknown: + return "Value Unknown"; + case CanNotCompute: + return "Can not compute"; + } + return "NULL"; +} + +bool SENode::operator==(const SENode& other) const { + if (GetType() != other.GetType()) return false; + + if (other.GetChildren().size() != children_.size()) return false; + + const SERecurrentNode* this_as_recurrent = AsSERecurrentNode(); + + // Check the children are the same, for SERecurrentNodes we need to check the + // offset and coefficient manually as the child vector is sorted by ids so the + // offset/coefficient information is lost. + if (!this_as_recurrent) { + for (size_t index = 0; index < children_.size(); ++index) { + if (other.GetChildren()[index] != children_[index]) return false; + } + } else { + const SERecurrentNode* other_as_recurrent = other.AsSERecurrentNode(); + + // We've already checked the types are the same, this should not fail if + // this->AsSERecurrentNode() succeeded. + assert(other_as_recurrent); + + if (this_as_recurrent->GetCoefficient() != + other_as_recurrent->GetCoefficient()) + return false; + + if (this_as_recurrent->GetOffset() != other_as_recurrent->GetOffset()) + return false; + + if (this_as_recurrent->GetLoop() != other_as_recurrent->GetLoop()) + return false; + } + + // If we're dealing with a value unknown node check both nodes were created by + // the same instruction. + if (GetType() == SENode::ValueUnknown) { + if (AsSEValueUnknown()->ResultId() != + other.AsSEValueUnknown()->ResultId()) { + return false; + } + } + + if (AsSEConstantNode()) { + if (AsSEConstantNode()->FoldToSingleValue() != + other.AsSEConstantNode()->FoldToSingleValue()) + return false; + } + + return true; +} + +bool SENode::operator!=(const SENode& other) const { return !(*this == other); } + +namespace { +// Helper functions to insert 32/64 bit values into the 32 bit hash string. This +// allows us to add pointers to the string by reinterpreting the pointers as +// uintptr_t. PushToString will deduce the type, call sizeof on it and use +// that size to call into the correct PushToStringImpl functor depending on +// whether it is 32 or 64 bit. + +template +struct PushToStringImpl; + +template +struct PushToStringImpl { + void operator()(T id, std::u32string* str) { + str->push_back(static_cast(id >> 32)); + str->push_back(static_cast(id)); + } +}; + +template +struct PushToStringImpl { + void operator()(T id, std::u32string* str) { + str->push_back(static_cast(id)); + } +}; + +template +void PushToString(T id, std::u32string* str) { + PushToStringImpl{}(id, str); +} + +} // namespace + +// Implements the hashing of SENodes. +size_t SENodeHash::operator()(const SENode* node) const { + // Concatenate the terms into a string which we can hash. + std::u32string hash_string{}; + + // Hashing the type as a string is safer than hashing the enum as the enum is + // very likely to collide with constants. + for (char ch : node->AsString()) { + hash_string.push_back(static_cast(ch)); + } + + // We just ignore the literal value unless it is a constant. + if (node->GetType() == SENode::Constant) + PushToString(node->AsSEConstantNode()->FoldToSingleValue(), &hash_string); + + const SERecurrentNode* recurrent = node->AsSERecurrentNode(); + + // If we're dealing with a recurrent expression hash the loop as well so that + // nested inductions like i=0,i++ and j=0,j++ correspond to different nodes. + if (recurrent) { + PushToString(reinterpret_cast(recurrent->GetLoop()), + &hash_string); + + // Recurrent expressions can't be hashed using the normal method as the + // order of coefficient and offset matters to the hash. + PushToString(reinterpret_cast(recurrent->GetCoefficient()), + &hash_string); + PushToString(reinterpret_cast(recurrent->GetOffset()), + &hash_string); + + return std::hash{}(hash_string); + } + + // Hash the result id of the original instruction which created this node if + // it is a value unknown node. + if (node->GetType() == SENode::ValueUnknown) { + PushToString(node->AsSEValueUnknown()->ResultId(), &hash_string); + } + + // Hash the pointers of the child nodes, each SENode has a unique pointer + // associated with it. + const std::vector& children = node->GetChildren(); + for (const SENode* child : children) { + PushToString(reinterpret_cast(child), &hash_string); + } + + return std::hash{}(hash_string); +} + +// This overload is the actual overload used by the node_cache_ set. +size_t SENodeHash::operator()(const std::unique_ptr& node) const { + return this->operator()(node.get()); +} + +void SENode::DumpDot(std::ostream& out, bool recurse) const { + size_t unique_id = std::hash{}(this); + out << unique_id << " [label=\"" << AsString() << " "; + if (GetType() == SENode::Constant) { + out << "\nwith value: " << this->AsSEConstantNode()->FoldToSingleValue(); + } + out << "\"]\n"; + for (const SENode* child : children_) { + size_t child_unique_id = std::hash{}(child); + out << unique_id << " -> " << child_unique_id << " \n"; + if (recurse) child->DumpDot(out, true); + } +} + +namespace { +class IsGreaterThanZero { + public: + explicit IsGreaterThanZero(IRContext* context) : context_(context) {} + + // Determine if the value of |node| is always strictly greater than zero if + // |or_equal_zero| is false or greater or equal to zero if |or_equal_zero| is + // true. It returns true is the evaluation was able to conclude something, in + // which case the result is stored in |result|. + // The algorithm work by going through all the nodes and determine the + // sign of each of them. + bool Eval(const SENode* node, bool or_equal_zero, bool* result) { + *result = false; + switch (Visit(node)) { + case Signedness::kPositiveOrNegative: { + return false; + } + case Signedness::kStrictlyNegative: { + *result = false; + break; + } + case Signedness::kNegative: { + if (!or_equal_zero) { + return false; + } + *result = false; + break; + } + case Signedness::kStrictlyPositive: { + *result = true; + break; + } + case Signedness::kPositive: { + if (!or_equal_zero) { + return false; + } + *result = true; + break; + } + } + return true; + } + + private: + enum class Signedness { + kPositiveOrNegative, // Yield a value positive or negative. + kStrictlyNegative, // Yield a value strictly less than 0. + kNegative, // Yield a value less or equal to 0. + kStrictlyPositive, // Yield a value strictly greater than 0. + kPositive // Yield a value greater or equal to 0. + }; + + // Combine the signedness according to arithmetic rules of a given operator. + using Combiner = std::function; + + // Returns a functor to interpret the signedness of 2 expressions as if they + // were added. + Combiner GetAddCombiner() const { + return [](Signedness lhs, Signedness rhs) { + switch (lhs) { + case Signedness::kPositiveOrNegative: + break; + case Signedness::kStrictlyNegative: + if (rhs == Signedness::kStrictlyNegative || + rhs == Signedness::kNegative) + return lhs; + break; + case Signedness::kNegative: { + if (rhs == Signedness::kStrictlyNegative) + return Signedness::kStrictlyNegative; + if (rhs == Signedness::kNegative) return Signedness::kNegative; + break; + } + case Signedness::kStrictlyPositive: { + if (rhs == Signedness::kStrictlyPositive || + rhs == Signedness::kPositive) { + return Signedness::kStrictlyPositive; + } + break; + } + case Signedness::kPositive: { + if (rhs == Signedness::kStrictlyPositive) + return Signedness::kStrictlyPositive; + if (rhs == Signedness::kPositive) return Signedness::kPositive; + break; + } + } + return Signedness::kPositiveOrNegative; + }; + } + + // Returns a functor to interpret the signedness of 2 expressions as if they + // were multiplied. + Combiner GetMulCombiner() const { + return [](Signedness lhs, Signedness rhs) { + switch (lhs) { + case Signedness::kPositiveOrNegative: + break; + case Signedness::kStrictlyNegative: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: { + return Signedness::kStrictlyPositive; + } + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: { + return Signedness::kStrictlyNegative; + } + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + break; + } + case Signedness::kNegative: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + break; + } + case Signedness::kStrictlyPositive: { + return rhs; + } + case Signedness::kPositive: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: + case Signedness::kNegative: { + return Signedness::kNegative; + } + case Signedness::kStrictlyPositive: + case Signedness::kPositive: { + return Signedness::kPositive; + } + } + break; + } + } + return Signedness::kPositiveOrNegative; + }; + } + + Signedness Visit(const SENode* node) { + switch (node->GetType()) { + case SENode::Constant: + return Visit(node->AsSEConstantNode()); + break; + case SENode::RecurrentAddExpr: + return Visit(node->AsSERecurrentNode()); + break; + case SENode::Negative: + return Visit(node->AsSENegative()); + break; + case SENode::CanNotCompute: + return Visit(node->AsSECantCompute()); + break; + case SENode::ValueUnknown: + return Visit(node->AsSEValueUnknown()); + break; + case SENode::Add: + return VisitExpr(node, GetAddCombiner()); + break; + case SENode::Multiply: + return VisitExpr(node, GetMulCombiner()); + break; + } + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of a constant |node|. + Signedness Visit(const SEConstantNode* node) { + if (0 == node->FoldToSingleValue()) return Signedness::kPositive; + if (0 < node->FoldToSingleValue()) return Signedness::kStrictlyPositive; + if (0 > node->FoldToSingleValue()) return Signedness::kStrictlyNegative; + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of an unknown |node| based on its type. + Signedness Visit(const SEValueUnknown* node) { + Instruction* insn = context_->get_def_use_mgr()->GetDef(node->ResultId()); + analysis::Type* type = context_->get_type_mgr()->GetType(insn->type_id()); + assert(type && "Can't retrieve a type for the instruction"); + analysis::Integer* int_type = type->AsInteger(); + assert(type && "Can't retrieve an integer type for the instruction"); + return int_type->IsSigned() ? Signedness::kPositiveOrNegative + : Signedness::kPositive; + } + + // Returns the signedness of a recurring expression. + Signedness Visit(const SERecurrentNode* node) { + Signedness coeff_sign = Visit(node->GetCoefficient()); + // SERecurrentNode represent an affine expression in the range [0, + // loop_bound], so the result cannot be strictly positive or negative. + switch (coeff_sign) { + default: + break; + case Signedness::kStrictlyNegative: + coeff_sign = Signedness::kNegative; + break; + case Signedness::kStrictlyPositive: + coeff_sign = Signedness::kPositive; + break; + } + return GetAddCombiner()(coeff_sign, Visit(node->GetOffset())); + } + + // Returns the signedness of a negation |node|. + Signedness Visit(const SENegative* node) { + switch (Visit(*node->begin())) { + case Signedness::kPositiveOrNegative: { + return Signedness::kPositiveOrNegative; + } + case Signedness::kStrictlyNegative: { + return Signedness::kStrictlyPositive; + } + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: { + return Signedness::kStrictlyNegative; + } + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + return Signedness::kPositiveOrNegative; + } + + Signedness Visit(const SECantCompute*) { + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of a binary expression by using the combiner + // |reduce|. + Signedness VisitExpr( + const SENode* node, + std::function reduce) { + Signedness result = Visit(*node->begin()); + for (const SENode* operand : make_range(++node->begin(), node->end())) { + if (result == Signedness::kPositiveOrNegative) { + return Signedness::kPositiveOrNegative; + } + result = reduce(result, Visit(operand)); + } + return result; + } + + IRContext* context_; +}; +} // namespace + +bool ScalarEvolutionAnalysis::IsAlwaysGreaterThanZero(SENode* node, + bool* is_gt_zero) const { + return IsGreaterThanZero(context_).Eval(node, false, is_gt_zero); +} + +bool ScalarEvolutionAnalysis::IsAlwaysGreaterOrEqualToZero( + SENode* node, bool* is_ge_zero) const { + return IsGreaterThanZero(context_).Eval(node, true, is_ge_zero); +} + +namespace { + +// Remove |node| from the |mul| chain (of the form A * ... * |node| * ... * Z), +// if |node| is not in the chain, returns the original chain. +SENode* RemoveOneNodeFromMultiplyChain(SEMultiplyNode* mul, + const SENode* node) { + SENode* lhs = mul->GetChildren()[0]; + SENode* rhs = mul->GetChildren()[1]; + if (lhs == node) { + return rhs; + } + if (rhs == node) { + return lhs; + } + if (lhs->AsSEMultiplyNode()) { + SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), node); + if (res != lhs) + return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs); + } + if (rhs->AsSEMultiplyNode()) { + SENode* res = RemoveOneNodeFromMultiplyChain(rhs->AsSEMultiplyNode(), node); + if (res != rhs) + return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs); + } + + return mul; +} +} // namespace + +std::pair SExpression::operator/( + SExpression rhs_wrapper) const { + SENode* lhs = node_; + SENode* rhs = rhs_wrapper.node_; + // Check for division by 0. + if (rhs->AsSEConstantNode() && + !rhs->AsSEConstantNode()->FoldToSingleValue()) { + return {scev_->CreateCantComputeNode(), 0}; + } + + // Trivial case. + if (lhs->AsSEConstantNode() && rhs->AsSEConstantNode()) { + int64_t lhs_value = lhs->AsSEConstantNode()->FoldToSingleValue(); + int64_t rhs_value = rhs->AsSEConstantNode()->FoldToSingleValue(); + return {scev_->CreateConstant(lhs_value / rhs_value), + lhs_value % rhs_value}; + } + + // look for a "c U / U" pattern. + if (lhs->AsSEMultiplyNode()) { + assert(lhs->GetChildren().size() == 2 && + "More than 2 operand for a multiply node."); + SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), rhs); + if (res != lhs) { + return {res, 0}; + } + } + + return {scev_->CreateCantComputeNode(), 0}; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.h b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.h new file mode 100644 index 00000000..fb6d631f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis.h @@ -0,0 +1,314 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_ANALYSIS_H_ +#define SOURCE_OPT_SCALAR_ANALYSIS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class Loop; + +// Manager for the Scalar Evolution analysis. Creates and maintains a DAG of +// scalar operations generated from analysing the use def graph from incoming +// instructions. Each node is hashed as it is added so like node (for instance, +// two induction variables i=0,i++ and j=0,j++) become the same node. After +// creating a DAG with AnalyzeInstruction it can the be simplified into a more +// usable form with SimplifyExpression. +class ScalarEvolutionAnalysis { + public: + explicit ScalarEvolutionAnalysis(IRContext* context); + + // Create a unary negative node on |operand|. + SENode* CreateNegation(SENode* operand); + + // Creates a subtraction between the two operands by adding |operand_1| to the + // negation of |operand_2|. + SENode* CreateSubtraction(SENode* operand_1, SENode* operand_2); + + // Create an addition node between two operands. The |simplify| when set will + // allow the function to return an SEConstant instead of an addition if the + // two input operands are also constant. + SENode* CreateAddNode(SENode* operand_1, SENode* operand_2); + + // Create a multiply node between two operands. + SENode* CreateMultiplyNode(SENode* operand_1, SENode* operand_2); + + // Create a node representing a constant integer. + SENode* CreateConstant(int64_t integer); + + // Create a value unknown node, such as a load. + SENode* CreateValueUnknownNode(const Instruction* inst); + + // Create a CantComputeNode. Used to exit out of analysis. + SENode* CreateCantComputeNode(); + + // Create a new recurrent node with |offset| and |coefficient|, with respect + // to |loop|. + SENode* CreateRecurrentExpression(const Loop* loop, SENode* offset, + SENode* coefficient); + + // Construct the DAG by traversing use def chain of |inst|. + SENode* AnalyzeInstruction(const Instruction* inst); + + // Simplify the |node| by grouping like terms or if contains a recurrent + // expression, rewrite the graph so the whole DAG (from |node| down) is in + // terms of that recurrent expression. + // + // For example. + // Induction variable i=0, i++ would produce Rec(0,1) so i+1 could be + // transformed into Rec(1,1). + // + // X+X*2+Y-Y+34-17 would be transformed into 3*X + 17, where X and Y are + // ValueUnknown nodes (such as a load instruction). + SENode* SimplifyExpression(SENode* node); + + // Add |prospective_node| into the cache and return a raw pointer to it. If + // |prospective_node| is already in the cache just return the raw pointer. + SENode* GetCachedOrAdd(std::unique_ptr prospective_node); + + // Checks that the graph starting from |node| is invariant to the |loop|. + bool IsLoopInvariant(const Loop* loop, const SENode* node) const; + + // Sets |is_gt_zero| to true if |node| represent a value always strictly + // greater than 0. The result of |is_gt_zero| is valid only if the function + // returns true. + bool IsAlwaysGreaterThanZero(SENode* node, bool* is_gt_zero) const; + + // Sets |is_ge_zero| to true if |node| represent a value greater or equals to + // 0. The result of |is_ge_zero| is valid only if the function returns true. + bool IsAlwaysGreaterOrEqualToZero(SENode* node, bool* is_ge_zero) const; + + // Find the recurrent term belonging to |loop| in the graph starting from + // |node| and return the coefficient of that recurrent term. Constant zero + // will be returned if no recurrent could be found. |node| should be in + // simplest form. + SENode* GetCoefficientFromRecurrentTerm(SENode* node, const Loop* loop); + + // Return a rebuilt graph starting from |node| with the recurrent expression + // belonging to |loop| being zeroed out. Returned node will be simplified. + SENode* BuildGraphWithoutRecurrentTerm(SENode* node, const Loop* loop); + + // Return the recurrent term belonging to |loop| if it appears in the graph + // starting at |node| or null if it doesn't. + SERecurrentNode* GetRecurrentTerm(SENode* node, const Loop* loop); + + SENode* UpdateChildNode(SENode* parent, SENode* child, SENode* new_child); + + // The loops in |loop_pair| will be considered the same when constructing + // SERecurrentNode objects. This enables analysing dependencies that will be + // created during loop fusion. + void AddLoopsToPretendAreTheSame( + const std::pair& loop_pair) { + pretend_equal_[std::get<1>(loop_pair)] = std::get<0>(loop_pair); + } + + private: + SENode* AnalyzeConstant(const Instruction* inst); + + // Handles both addition and subtraction. If the |instruction| is OpISub + // then the resulting node will be op1+(-op2) otherwise if it is OpIAdd then + // the result will be op1+op2. |instruction| must be OpIAdd or OpISub. + SENode* AnalyzeAddOp(const Instruction* instruction); + + SENode* AnalyzeMultiplyOp(const Instruction* multiply); + + SENode* AnalyzePhiInstruction(const Instruction* phi); + + IRContext* context_; + + // A map of instructions to SENodes. This is used to track recurrent + // expressions as they are added when analyzing instructions. Recurrent + // expressions come from phi nodes which by nature can include recursion so we + // check if nodes have already been built when analyzing instructions. + std::map recurrent_node_map_; + + // On creation we create and cache the CantCompute node so we not need to + // perform a needless create step. + SENode* cached_cant_compute_; + + // Helper functor to allow two unique_ptr to nodes to be compare. Only + // needed + // for the unordered_set implementation. + struct NodePointersEquality { + bool operator()(const std::unique_ptr& lhs, + const std::unique_ptr& rhs) const { + return *lhs == *rhs; + } + }; + + // Cache of nodes. All pointers to the nodes are references to the memory + // managed by they set. + std::unordered_set, SENodeHash, NodePointersEquality> + node_cache_; + + // Loops that should be considered the same for performing analysis for loop + // fusion. + std::map pretend_equal_; +}; + +// Wrapping class to manipulate SENode pointer using + - * / operators. +class SExpression { + public: + // Implicit on purpose ! + SExpression(SENode* node) + : node_(node->GetParentAnalysis()->SimplifyExpression(node)), + scev_(node->GetParentAnalysis()) {} + + inline operator SENode*() const { return node_; } + inline SENode* operator->() const { return node_; } + const SENode& operator*() const { return *node_; } + + inline ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() const { + return scev_; + } + + inline SExpression operator+(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator+(T integer) const; + inline SExpression operator+(SExpression rhs) const; + + inline SExpression operator-() const; + inline SExpression operator-(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator-(T integer) const; + inline SExpression operator-(SExpression rhs) const; + + inline SExpression operator*(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator*(T integer) const; + inline SExpression operator*(SExpression rhs) const; + + template ::value, int>::type = 0> + inline std::pair operator/(T integer) const; + // Try to perform a division. Returns the pair . If it fails to simplify it, the function returns a + // CanNotCompute node. + std::pair operator/(SExpression rhs) const; + + private: + SENode* node_; + ScalarEvolutionAnalysis* scev_; +}; + +inline SExpression SExpression::operator+(SENode* rhs) const { + return scev_->CreateAddNode(node_, rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator+(T integer) const { + return *this + scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator+(SExpression rhs) const { + return *this + rhs.node_; +} + +inline SExpression SExpression::operator-() const { + return scev_->CreateNegation(node_); +} + +inline SExpression SExpression::operator-(SENode* rhs) const { + return *this + scev_->CreateNegation(rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator-(T integer) const { + return *this - scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator-(SExpression rhs) const { + return *this - rhs.node_; +} + +inline SExpression SExpression::operator*(SENode* rhs) const { + return scev_->CreateMultiplyNode(node_, rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator*(T integer) const { + return *this * scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator*(SExpression rhs) const { + return *this * rhs.node_; +} + +template ::value, int>::type> +inline std::pair SExpression::operator/(T integer) const { + return *this / scev_->CreateConstant(integer); +} + +template ::value, int>::type> +inline SExpression operator+(T lhs, SExpression rhs) { + return rhs + lhs; +} +inline SExpression operator+(SENode* lhs, SExpression rhs) { return rhs + lhs; } + +template ::value, int>::type> +inline SExpression operator-(T lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} - + rhs; +} +inline SExpression operator-(SENode* lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{lhs} - rhs; +} + +template ::value, int>::type> +inline SExpression operator*(T lhs, SExpression rhs) { + return rhs * lhs; +} +inline SExpression operator*(SENode* lhs, SExpression rhs) { return rhs * lhs; } + +template ::value, int>::type> +inline std::pair operator/(T lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} / + rhs; +} +inline std::pair operator/(SENode* lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{lhs} / rhs; +} + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_SCALAR_ANALYSIS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_nodes.h b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_nodes.h new file mode 100644 index 00000000..91ce446f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_nodes.h @@ -0,0 +1,347 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASI, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ +#define SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ + +#include +#include +#include +#include + +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class Loop; +class ScalarEvolutionAnalysis; +class SEConstantNode; +class SERecurrentNode; +class SEAddNode; +class SEMultiplyNode; +class SENegative; +class SEValueUnknown; +class SECantCompute; + +// Abstract class representing a node in the scalar evolution DAG. Each node +// contains a vector of pointers to its children and each subclass of SENode +// implements GetType and an As method to allow casting. SENodes can be hashed +// using the SENodeHash functor. The vector of children is sorted when a node is +// added. This is important as it allows the hash of X+Y to be the same as Y+X. +class SENode { + public: + enum SENodeType { + Constant, + RecurrentAddExpr, + Add, + Multiply, + Negative, + ValueUnknown, + CanNotCompute + }; + + using ChildContainerType = std::vector; + + explicit SENode(ScalarEvolutionAnalysis* parent_analysis) + : parent_analysis_(parent_analysis), unique_id_(++NumberOfNodes) {} + + virtual SENodeType GetType() const = 0; + + virtual ~SENode() {} + + virtual inline void AddChild(SENode* child) { + // If this is a constant node, assert. + if (AsSEConstantNode()) { + assert(false && "Trying to add a child node to a constant!"); + } + + // Find the first point in the vector where |child| is greater than the node + // currently in the vector. + auto find_first_less_than = [child](const SENode* node) { + return child->unique_id_ <= node->unique_id_; + }; + + auto position = std::find_if_not(children_.begin(), children_.end(), + find_first_less_than); + // Children are sorted so the hashing and equality operator will be the same + // for a node with the same children. X+Y should be the same as Y+X. + children_.insert(position, child); + } + + // Get the type as an std::string. This is used to represent the node in the + // dot output and is used to hash the type as well. + std::string AsString() const; + + // Dump the SENode and its immediate children, if |recurse| is true then it + // will recurse through all children to print the DAG starting from this node + // as a root. + void DumpDot(std::ostream& out, bool recurse = false) const; + + // Checks if two nodes are the same by hashing them. + bool operator==(const SENode& other) const; + + // Checks if two nodes are not the same by comparing the hashes. + bool operator!=(const SENode& other) const; + + // Return the child node at |index|. + inline SENode* GetChild(size_t index) { return children_[index]; } + inline const SENode* GetChild(size_t index) const { return children_[index]; } + + // Iterator to iterate over the child nodes. + using iterator = ChildContainerType::iterator; + using const_iterator = ChildContainerType::const_iterator; + + // Iterate over immediate child nodes. + iterator begin() { return children_.begin(); } + iterator end() { return children_.end(); } + + // Constant overloads for iterating over immediate child nodes. + const_iterator begin() const { return children_.cbegin(); } + const_iterator end() const { return children_.cend(); } + const_iterator cbegin() { return children_.cbegin(); } + const_iterator cend() { return children_.cend(); } + + // Collect all the recurrent nodes in this SENode + std::vector CollectRecurrentNodes() { + std::vector recurrent_nodes{}; + + if (auto recurrent_node = AsSERecurrentNode()) { + recurrent_nodes.push_back(recurrent_node); + } + + for (auto child : GetChildren()) { + auto child_recurrent_nodes = child->CollectRecurrentNodes(); + recurrent_nodes.insert(recurrent_nodes.end(), + child_recurrent_nodes.begin(), + child_recurrent_nodes.end()); + } + + return recurrent_nodes; + } + + // Collect all the value unknown nodes in this SENode + std::vector CollectValueUnknownNodes() { + std::vector value_unknown_nodes{}; + + if (auto value_unknown_node = AsSEValueUnknown()) { + value_unknown_nodes.push_back(value_unknown_node); + } + + for (auto child : GetChildren()) { + auto child_value_unknown_nodes = child->CollectValueUnknownNodes(); + value_unknown_nodes.insert(value_unknown_nodes.end(), + child_value_unknown_nodes.begin(), + child_value_unknown_nodes.end()); + } + + return value_unknown_nodes; + } + + // Iterator to iterate over the entire DAG. Even though we are using the tree + // iterator it should still be safe to iterate over. However, nodes with + // multiple parents will be visited multiple times, unlike in a tree. + using dag_iterator = TreeDFIterator; + using const_dag_iterator = TreeDFIterator; + + // Iterate over all child nodes in the graph. + dag_iterator graph_begin() { return dag_iterator(this); } + dag_iterator graph_end() { return dag_iterator(); } + const_dag_iterator graph_begin() const { return graph_cbegin(); } + const_dag_iterator graph_end() const { return graph_cend(); } + const_dag_iterator graph_cbegin() const { return const_dag_iterator(this); } + const_dag_iterator graph_cend() const { return const_dag_iterator(); } + + // Return the vector of immediate children. + const ChildContainerType& GetChildren() const { return children_; } + ChildContainerType& GetChildren() { return children_; } + + // Return true if this node is a can't compute node. + bool IsCantCompute() const { return GetType() == CanNotCompute; } + +// Implements a casting method for each type. +// clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(SEConstantNode) + DeclareCastMethod(SERecurrentNode) + DeclareCastMethod(SEAddNode) + DeclareCastMethod(SEMultiplyNode) + DeclareCastMethod(SENegative) + DeclareCastMethod(SEValueUnknown) + DeclareCastMethod(SECantCompute) +#undef DeclareCastMethod + + // Get the analysis which has this node in its cache. + inline ScalarEvolutionAnalysis* GetParentAnalysis() const { + return parent_analysis_; + } + + protected: + ChildContainerType children_; + + ScalarEvolutionAnalysis* parent_analysis_; + + // The unique id of this node, assigned on creation by incrementing the static + // node count. + uint32_t unique_id_; + + // The number of nodes created. + static uint32_t NumberOfNodes; +}; +// clang-format on + +// Function object to handle the hashing of SENodes. Hashing algorithm hashes +// the type (as a string), the literal value of any constants, and the child +// pointers which are assumed to be unique. +struct SENodeHash { + size_t operator()(const std::unique_ptr& node) const; + size_t operator()(const SENode* node) const; +}; + +// A node representing a constant integer. +class SEConstantNode : public SENode { + public: + SEConstantNode(ScalarEvolutionAnalysis* parent_analysis, int64_t value) + : SENode(parent_analysis), literal_value_(value) {} + + SENodeType GetType() const final { return Constant; } + + int64_t FoldToSingleValue() const { return literal_value_; } + + SEConstantNode* AsSEConstantNode() override { return this; } + const SEConstantNode* AsSEConstantNode() const override { return this; } + + inline void AddChild(SENode*) final { + assert(false && "Attempting to add a child to a constant node!"); + } + + protected: + int64_t literal_value_; +}; + +// A node representing a recurrent expression in the code. A recurrent +// expression is an expression whose value can be expressed as a linear +// expression of the loop iterations. Such as an induction variable. The actual +// value of a recurrent expression is coefficent_ * iteration + offset_, hence +// an induction variable i=0, i++ becomes a recurrent expression with an offset +// of zero and a coefficient of one. +class SERecurrentNode : public SENode { + public: + SERecurrentNode(ScalarEvolutionAnalysis* parent_analysis, const Loop* loop) + : SENode(parent_analysis), loop_(loop) {} + + SENodeType GetType() const final { return RecurrentAddExpr; } + + inline void AddCoefficient(SENode* child) { + coefficient_ = child; + SENode::AddChild(child); + } + + inline void AddOffset(SENode* child) { + offset_ = child; + SENode::AddChild(child); + } + + inline const SENode* GetCoefficient() const { return coefficient_; } + inline SENode* GetCoefficient() { return coefficient_; } + + inline const SENode* GetOffset() const { return offset_; } + inline SENode* GetOffset() { return offset_; } + + // Return the loop which this recurrent expression is recurring within. + const Loop* GetLoop() const { return loop_; } + + SERecurrentNode* AsSERecurrentNode() override { return this; } + const SERecurrentNode* AsSERecurrentNode() const override { return this; } + + private: + SENode* coefficient_; + SENode* offset_; + const Loop* loop_; +}; + +// A node representing an addition operation between child nodes. +class SEAddNode : public SENode { + public: + explicit SEAddNode(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Add; } + + SEAddNode* AsSEAddNode() override { return this; } + const SEAddNode* AsSEAddNode() const override { return this; } +}; + +// A node representing a multiply operation between child nodes. +class SEMultiplyNode : public SENode { + public: + explicit SEMultiplyNode(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Multiply; } + + SEMultiplyNode* AsSEMultiplyNode() override { return this; } + const SEMultiplyNode* AsSEMultiplyNode() const override { return this; } +}; + +// A node representing a unary negative operation. +class SENegative : public SENode { + public: + explicit SENegative(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Negative; } + + SENegative* AsSENegative() override { return this; } + const SENegative* AsSENegative() const override { return this; } +}; + +// A node representing a value which we do not know the value of, such as a load +// instruction. +class SEValueUnknown : public SENode { + public: + // SEValueUnknowns must come from an instruction |unique_id| is the unique id + // of that instruction. This is so we cancompare value unknowns and have a + // unique value unknown for each instruction. + SEValueUnknown(ScalarEvolutionAnalysis* parent_analysis, uint32_t result_id) + : SENode(parent_analysis), result_id_(result_id) {} + + SENodeType GetType() const final { return ValueUnknown; } + + SEValueUnknown* AsSEValueUnknown() override { return this; } + const SEValueUnknown* AsSEValueUnknown() const override { return this; } + + inline uint32_t ResultId() const { return result_id_; } + + private: + uint32_t result_id_; +}; + +// A node which we cannot reason about at all. +class SECantCompute : public SENode { + public: + explicit SECantCompute(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return CanNotCompute; } + + SECantCompute* AsSECantCompute() override { return this; } + const SECantCompute* AsSECantCompute() const override { return this; } +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_simplification.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_simplification.cpp new file mode 100644 index 00000000..3c0947cd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_analysis_simplification.cpp @@ -0,0 +1,538 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "source/opt/scalar_analysis.h" + +// Simplifies scalar analysis DAGs. +// +// 1. Given a node passed to SimplifyExpression we first simplify the graph by +// calling SimplifyPolynomial. This groups like nodes following basic arithmetic +// rules, so multiple adds of the same load instruction could be grouped into a +// single multiply of that instruction. SimplifyPolynomial will traverse the DAG +// and build up an accumulator buffer for each class of instruction it finds. +// For example take the loop: +// for (i=0, i accumulators_; +}; + +// From a |multiply| build up the accumulator objects. +bool SENodeSimplifyImpl::AccumulatorsFromMultiply(SENode* multiply, + bool negation) { + if (multiply->GetChildren().size() != 2 || + multiply->GetType() != SENode::Multiply) + return false; + + SENode* operand_1 = multiply->GetChild(0); + SENode* operand_2 = multiply->GetChild(1); + + SENode* value_unknown = nullptr; + SENode* constant = nullptr; + + // Work out which operand is the unknown value. + if (operand_1->GetType() == SENode::ValueUnknown || + operand_1->GetType() == SENode::RecurrentAddExpr) + value_unknown = operand_1; + else if (operand_2->GetType() == SENode::ValueUnknown || + operand_2->GetType() == SENode::RecurrentAddExpr) + value_unknown = operand_2; + + // Work out which operand is the constant coefficient. + if (operand_1->GetType() == SENode::Constant) + constant = operand_1; + else if (operand_2->GetType() == SENode::Constant) + constant = operand_2; + + // If the expression is not a variable multiplied by a constant coefficient, + // exit out. + if (!(value_unknown && constant)) { + return false; + } + + int64_t sign = negation ? -1 : 1; + + auto iterator = accumulators_.find(value_unknown); + int64_t new_value = constant->AsSEConstantNode()->FoldToSingleValue() * sign; + // Add the result of the multiplication to the accumulators. + if (iterator != accumulators_.end()) { + (*iterator).second += new_value; + } else { + accumulators_.insert({value_unknown, new_value}); + } + + return true; +} + +SENode* SENodeSimplifyImpl::Simplify() { + // We only handle graphs with an addition, multiplication, or negation, at the + // root. + if (node_->GetType() != SENode::Add && node_->GetType() != SENode::Multiply && + node_->GetType() != SENode::Negative) + return node_; + + SENode* simplified_polynomial = SimplifyPolynomial(); + + SERecurrentNode* recurrent_expr = nullptr; + node_ = simplified_polynomial; + + // Fold recurrent expressions which are with respect to the same loop into a + // single recurrent expression. + simplified_polynomial = FoldRecurrentAddExpressions(simplified_polynomial); + + simplified_polynomial = + EliminateZeroCoefficientRecurrents(simplified_polynomial); + + // Traverse the immediate children of the new node to find the recurrent + // expression. If there is more than one there is nothing further we can do. + for (SENode* child : simplified_polynomial->GetChildren()) { + if (child->GetType() == SENode::RecurrentAddExpr) { + recurrent_expr = child->AsSERecurrentNode(); + } + } + + // We need to count the number of unique recurrent expressions in the DAG to + // ensure there is only one. + for (auto child_iterator = simplified_polynomial->graph_begin(); + child_iterator != simplified_polynomial->graph_end(); ++child_iterator) { + if (child_iterator->GetType() == SENode::RecurrentAddExpr && + recurrent_expr != child_iterator->AsSERecurrentNode()) { + return simplified_polynomial; + } + } + + if (recurrent_expr) { + return SimplifyRecurrentAddExpression(recurrent_expr); + } + + return simplified_polynomial; +} + +// Traverse the graph to build up the accumulator objects. +void SENodeSimplifyImpl::GatherAccumulatorsFromChildNodes(SENode* new_node, + SENode* child, + bool negation) { + int32_t sign = negation ? -1 : 1; + + if (child->GetType() == SENode::Constant) { + // Collect all the constants and add them together. + constant_accumulator_ += + child->AsSEConstantNode()->FoldToSingleValue() * sign; + + } else if (child->GetType() == SENode::ValueUnknown || + child->GetType() == SENode::RecurrentAddExpr) { + // To rebuild the graph of X+X+X*2 into 4*X we count the occurrences of X + // and create a new node of count*X after. X can either be a ValueUnknown or + // a RecurrentAddExpr. The count for each X is stored in the accumulators_ + // map. + + auto iterator = accumulators_.find(child); + // If we've encountered this term before add to the accumulator for it. + if (iterator == accumulators_.end()) + accumulators_.insert({child, sign}); + else + iterator->second += sign; + + } else if (child->GetType() == SENode::Multiply) { + if (!AccumulatorsFromMultiply(child, negation)) { + new_node->AddChild(child); + } + + } else if (child->GetType() == SENode::Add) { + for (SENode* next_child : *child) { + GatherAccumulatorsFromChildNodes(new_node, next_child, negation); + } + + } else if (child->GetType() == SENode::Negative) { + SENode* negated_node = child->GetChild(0); + GatherAccumulatorsFromChildNodes(new_node, negated_node, !negation); + } else { + // If we can't work out how to fold the expression just add it back into + // the graph. + new_node->AddChild(child); + } +} + +SERecurrentNode* SENodeSimplifyImpl::UpdateCoefficient( + SERecurrentNode* recurrent, int64_t coefficient_update) const { + std::unique_ptr new_recurrent_node{new SERecurrentNode( + recurrent->GetParentAnalysis(), recurrent->GetLoop())}; + + SENode* new_coefficient = analysis_.CreateMultiplyNode( + recurrent->GetCoefficient(), + analysis_.CreateConstant(coefficient_update)); + + // See if the node can be simplified. + SENode* simplified = analysis_.SimplifyExpression(new_coefficient); + if (simplified->GetType() != SENode::CanNotCompute) + new_coefficient = simplified; + + if (coefficient_update < 0) { + new_recurrent_node->AddOffset( + analysis_.CreateNegation(recurrent->GetOffset())); + } else { + new_recurrent_node->AddOffset(recurrent->GetOffset()); + } + + new_recurrent_node->AddCoefficient(new_coefficient); + + return analysis_.GetCachedOrAdd(std::move(new_recurrent_node)) + ->AsSERecurrentNode(); +} + +// Simplify all the terms in the polynomial function. +SENode* SENodeSimplifyImpl::SimplifyPolynomial() { + std::unique_ptr new_add{new SEAddNode(node_->GetParentAnalysis())}; + + // Traverse the graph and gather the accumulators from it. + GatherAccumulatorsFromChildNodes(new_add.get(), node_, false); + + // Fold all the constants into a single constant node. + if (constant_accumulator_ != 0) { + new_add->AddChild(analysis_.CreateConstant(constant_accumulator_)); + } + + for (auto& pair : accumulators_) { + SENode* term = pair.first; + int64_t count = pair.second; + + // We can eliminate the term completely. + if (count == 0) continue; + + if (count == 1) { + new_add->AddChild(term); + } else if (count == -1 && term->GetType() != SENode::RecurrentAddExpr) { + // If the count is -1 we can just add a negative version of that node, + // unless it is a recurrent expression as we would rather the negative + // goes on the recurrent expressions children. This makes it easier to + // work with in other places. + new_add->AddChild(analysis_.CreateNegation(term)); + } else { + // Output value unknown terms as count*term and output recurrent + // expression terms as rec(offset, coefficient + count) offset and + // coefficient are the same as in the original expression. + if (term->GetType() == SENode::ValueUnknown) { + SENode* count_as_constant = analysis_.CreateConstant(count); + new_add->AddChild( + analysis_.CreateMultiplyNode(count_as_constant, term)); + } else { + assert(term->GetType() == SENode::RecurrentAddExpr && + "We only handle value unknowns or recurrent expressions"); + + // Create a new recurrent expression by adding the count to the + // coefficient of the old one. + new_add->AddChild(UpdateCoefficient(term->AsSERecurrentNode(), count)); + } + } + } + + // If there is only one term in the addition left just return that term. + if (new_add->GetChildren().size() == 1) { + return new_add->GetChild(0); + } + + // If there are no terms left in the addition just return 0. + if (new_add->GetChildren().size() == 0) { + return analysis_.CreateConstant(0); + } + + return analysis_.GetCachedOrAdd(std::move(new_add)); +} + +SENode* SENodeSimplifyImpl::FoldRecurrentAddExpressions(SENode* root) { + std::unique_ptr new_node{new SEAddNode(&analysis_)}; + + // A mapping of loops to the list of recurrent expressions which are with + // respect to those loops. + std::map>> + loops_to_recurrent{}; + + bool has_multiple_same_loop_recurrent_terms = false; + + for (SENode* child : *root) { + bool negation = false; + + if (child->GetType() == SENode::Negative) { + child = child->GetChild(0); + negation = true; + } + + if (child->GetType() == SENode::RecurrentAddExpr) { + const Loop* loop = child->AsSERecurrentNode()->GetLoop(); + + SERecurrentNode* rec = child->AsSERecurrentNode(); + if (loops_to_recurrent.find(loop) == loops_to_recurrent.end()) { + loops_to_recurrent[loop] = {std::make_pair(rec, negation)}; + } else { + loops_to_recurrent[loop].push_back(std::make_pair(rec, negation)); + has_multiple_same_loop_recurrent_terms = true; + } + } else { + new_node->AddChild(child); + } + } + + if (!has_multiple_same_loop_recurrent_terms) return root; + + for (auto pair : loops_to_recurrent) { + std::vector>& recurrent_expressions = + pair.second; + const Loop* loop = pair.first; + + std::unique_ptr new_coefficient{new SEAddNode(&analysis_)}; + std::unique_ptr new_offset{new SEAddNode(&analysis_)}; + + for (auto node_pair : recurrent_expressions) { + SERecurrentNode* node = node_pair.first; + bool negative = node_pair.second; + + if (!negative) { + new_coefficient->AddChild(node->GetCoefficient()); + new_offset->AddChild(node->GetOffset()); + } else { + new_coefficient->AddChild( + analysis_.CreateNegation(node->GetCoefficient())); + new_offset->AddChild(analysis_.CreateNegation(node->GetOffset())); + } + } + + std::unique_ptr new_recurrent{ + new SERecurrentNode(&analysis_, loop)}; + + SENode* new_coefficient_simplified = + analysis_.SimplifyExpression(new_coefficient.get()); + + SENode* new_offset_simplified = + analysis_.SimplifyExpression(new_offset.get()); + + if (new_coefficient_simplified->GetType() == SENode::Constant && + new_coefficient_simplified->AsSEConstantNode()->FoldToSingleValue() == + 0) { + return new_offset_simplified; + } + + new_recurrent->AddCoefficient(new_coefficient_simplified); + new_recurrent->AddOffset(new_offset_simplified); + + new_node->AddChild(analysis_.GetCachedOrAdd(std::move(new_recurrent))); + } + + // If we only have one child in the add just return that. + if (new_node->GetChildren().size() == 1) { + return new_node->GetChild(0); + } + + return analysis_.GetCachedOrAdd(std::move(new_node)); +} + +SENode* SENodeSimplifyImpl::EliminateZeroCoefficientRecurrents(SENode* node) { + if (node->GetType() != SENode::Add) return node; + + bool has_change = false; + + std::vector new_children{}; + for (SENode* child : *node) { + if (child->GetType() == SENode::RecurrentAddExpr) { + SENode* coefficient = child->AsSERecurrentNode()->GetCoefficient(); + // If coefficient is zero then we can eliminate the recurrent expression + // entirely and just return the offset as the recurrent expression is + // representing the equation coefficient*iterations + offset. + if (coefficient->GetType() == SENode::Constant && + coefficient->AsSEConstantNode()->FoldToSingleValue() == 0) { + new_children.push_back(child->AsSERecurrentNode()->GetOffset()); + has_change = true; + } else { + new_children.push_back(child); + } + } else { + new_children.push_back(child); + } + } + + if (!has_change) return node; + + std::unique_ptr new_add{new SEAddNode(node_->GetParentAnalysis())}; + + for (SENode* child : new_children) { + new_add->AddChild(child); + } + + return analysis_.GetCachedOrAdd(std::move(new_add)); +} + +SENode* SENodeSimplifyImpl::SimplifyRecurrentAddExpression( + SERecurrentNode* recurrent_expr) { + const std::vector& children = node_->GetChildren(); + + std::unique_ptr recurrent_node{new SERecurrentNode( + recurrent_expr->GetParentAnalysis(), recurrent_expr->GetLoop())}; + + // Create and simplify the new offset node. + std::unique_ptr new_offset{ + new SEAddNode(recurrent_expr->GetParentAnalysis())}; + new_offset->AddChild(recurrent_expr->GetOffset()); + + for (SENode* child : children) { + if (child->GetType() != SENode::RecurrentAddExpr) { + new_offset->AddChild(child); + } + } + + // Simplify the new offset. + SENode* simplified_child = analysis_.SimplifyExpression(new_offset.get()); + + // If the child can be simplified, add the simplified form otherwise, add it + // via the usual caching mechanism. + if (simplified_child->GetType() != SENode::CanNotCompute) { + recurrent_node->AddOffset(simplified_child); + } else { + recurrent_expr->AddOffset(analysis_.GetCachedOrAdd(std::move(new_offset))); + } + + recurrent_node->AddCoefficient(recurrent_expr->GetCoefficient()); + + return analysis_.GetCachedOrAdd(std::move(recurrent_node)); +} + +/* + * Scalar Analysis simplification public methods. + */ + +SENode* ScalarEvolutionAnalysis::SimplifyExpression(SENode* node) { + SENodeSimplifyImpl impl{this, node}; + + return impl.Simplify(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.cpp new file mode 100644 index 00000000..ff81fae3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -0,0 +1,1016 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/scalar_replacement_pass.h" + +#include +#include +#include +#include + +#include "source/extensions.h" +#include "source/opt/reflect.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kDebugValueOperandValueIndex = 5; +constexpr uint32_t kDebugValueOperandExpressionIndex = 6; +constexpr uint32_t kDebugDeclareOperandVariableIndex = 5; +} // namespace + +Pass::Status ScalarReplacementPass::Process() { + Status status = Status::SuccessWithoutChange; + for (auto& f : *get_module()) { + if (f.IsDeclaration()) { + continue; + } + + Status functionStatus = ProcessFunction(&f); + if (functionStatus == Status::Failure) + return functionStatus; + else if (functionStatus == Status::SuccessWithChange) + status = functionStatus; + } + + return status; +} + +Pass::Status ScalarReplacementPass::ProcessFunction(Function* function) { + std::queue worklist; + BasicBlock& entry = *function->begin(); + for (auto iter = entry.begin(); iter != entry.end(); ++iter) { + // Function storage class OpVariables must appear as the first instructions + // of the entry block. + if (iter->opcode() != spv::Op::OpVariable) break; + + Instruction* varInst = &*iter; + if (CanReplaceVariable(varInst)) { + worklist.push(varInst); + } + } + + Status status = Status::SuccessWithoutChange; + while (!worklist.empty()) { + Instruction* varInst = worklist.front(); + worklist.pop(); + + Status var_status = ReplaceVariable(varInst, &worklist); + if (var_status == Status::Failure) + return var_status; + else if (var_status == Status::SuccessWithChange) + status = var_status; + } + + return status; +} + +Pass::Status ScalarReplacementPass::ReplaceVariable( + Instruction* inst, std::queue* worklist) { + std::vector replacements; + if (!CreateReplacementVariables(inst, &replacements)) { + return Status::Failure; + } + + std::vector dead; + bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( + inst, [this, &replacements, &dead](Instruction* user) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { + if (ReplaceWholeDebugDeclare(user, replacements)) { + dead.push_back(user); + return true; + } + return false; + } + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { + if (ReplaceWholeDebugValue(user, replacements)) { + dead.push_back(user); + return true; + } + return false; + } + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case spv::Op::OpLoad: + if (ReplaceWholeLoad(user, replacements)) { + dead.push_back(user); + } else { + return false; + } + break; + case spv::Op::OpStore: + if (ReplaceWholeStore(user, replacements)) { + dead.push_back(user); + } else { + return false; + } + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (ReplaceAccessChain(user, replacements)) + dead.push_back(user); + else + return false; + break; + case spv::Op::OpName: + case spv::Op::OpMemberName: + break; + default: + assert(false && "Unexpected opcode"); + break; + } + } + return true; + }); + + if (replaced_all_uses) { + dead.push_back(inst); + } else { + return Status::Failure; + } + + // If there are no dead instructions to clean up, return with no changes. + if (dead.empty()) return Status::SuccessWithoutChange; + + // Clean up some dead code. + while (!dead.empty()) { + Instruction* toKill = dead.back(); + dead.pop_back(); + context()->KillInst(toKill); + } + + // Attempt to further scalarize. + for (auto var : replacements) { + if (var->opcode() == spv::Op::OpVariable) { + if (get_def_use_mgr()->NumUsers(var) == 0) { + context()->KillInst(var); + } else if (CanReplaceVariable(var)) { + worklist->push(var); + } + } + } + + return Status::SuccessWithChange; +} + +bool ScalarReplacementPass::ReplaceWholeDebugDeclare( + Instruction* dbg_decl, const std::vector& replacements) { + // Insert Deref operation to the front of the operation list of |dbg_decl|. + Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef( + dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); + auto* deref_expr = + context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + + // Add DebugValue instruction with Indexes operand and Deref operation. + int32_t idx = 0; + for (const auto* var : replacements) { + Instruction* insert_before = var->NextNode(); + while (insert_before->opcode() == spv::Op::OpVariable) + insert_before = insert_before->NextNode(); + assert(insert_before != nullptr && "unexpected end of list"); + Instruction* added_dbg_value = + context()->get_debug_info_mgr()->AddDebugValueForDecl( + dbg_decl, /*value_id=*/var->result_id(), + /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl); + + if (added_dbg_value == nullptr) return false; + added_dbg_value->AddOperand( + {SPV_OPERAND_TYPE_ID, + {context()->get_constant_mgr()->GetSIntConstId(idx)}}); + added_dbg_value->SetOperand(kDebugValueOperandExpressionIndex, + {deref_expr->result_id()}); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) { + context()->get_def_use_mgr()->AnalyzeInstUse(added_dbg_value); + } + ++idx; + } + return true; +} + +bool ScalarReplacementPass::ReplaceWholeDebugValue( + Instruction* dbg_value, const std::vector& replacements) { + int32_t idx = 0; + BasicBlock* block = context()->get_instr_block(dbg_value); + for (auto var : replacements) { + // Clone the DebugValue. + std::unique_ptr new_dbg_value(dbg_value->Clone(context())); + uint32_t new_id = TakeNextId(); + if (new_id == 0) return false; + new_dbg_value->SetResultId(new_id); + // Update 'Value' operand to the |replacements|. + new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()}); + // Append 'Indexes' operand. + new_dbg_value->AddOperand( + {SPV_OPERAND_TYPE_ID, + {context()->get_constant_mgr()->GetSIntConstId(idx)}}); + // Insert the new DebugValue to the basic block. + auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value)); + get_def_use_mgr()->AnalyzeInstDefUse(added_instr); + context()->set_instr_block(added_instr, block); + ++idx; + } + return true; +} + +bool ScalarReplacementPass::ReplaceWholeLoad( + Instruction* load, const std::vector& replacements) { + // Replaces the load of the entire composite with a load from each replacement + // variable followed by a composite construction. + BasicBlock* block = context()->get_instr_block(load); + std::vector loads; + loads.reserve(replacements.size()); + BasicBlock::iterator where(load); + for (auto var : replacements) { + // Create a load of each replacement variable. + if (var->opcode() != spv::Op::OpVariable) { + loads.push_back(var); + continue; + } + + Instruction* type = GetStorageType(var); + uint32_t loadId = TakeNextId(); + if (loadId == 0) { + return false; + } + std::unique_ptr newLoad( + new Instruction(context(), spv::Op::OpLoad, type->result_id(), loadId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + // Copy memory access attributes which start at index 1. Index 0 is the + // pointer to load. + for (uint32_t i = 1; i < load->NumInOperands(); ++i) { + Operand copy(load->GetInOperand(i)); + newLoad->AddOperand(std::move(copy)); + } + where = where.InsertBefore(std::move(newLoad)); + get_def_use_mgr()->AnalyzeInstDefUse(&*where); + context()->set_instr_block(&*where, block); + where->UpdateDebugInfoFrom(load); + loads.push_back(&*where); + } + + // Construct a new composite. + uint32_t compositeId = TakeNextId(); + if (compositeId == 0) { + return false; + } + where = load; + std::unique_ptr compositeConstruct( + new Instruction(context(), spv::Op::OpCompositeConstruct, load->type_id(), + compositeId, {})); + for (auto l : loads) { + Operand op(SPV_OPERAND_TYPE_ID, + std::initializer_list{l->result_id()}); + compositeConstruct->AddOperand(std::move(op)); + } + where = where.InsertBefore(std::move(compositeConstruct)); + get_def_use_mgr()->AnalyzeInstDefUse(&*where); + where->UpdateDebugInfoFrom(load); + context()->set_instr_block(&*where, block); + context()->ReplaceAllUsesWith(load->result_id(), compositeId); + return true; +} + +bool ScalarReplacementPass::ReplaceWholeStore( + Instruction* store, const std::vector& replacements) { + // Replaces a store to the whole composite with a series of extract and stores + // to each element. + uint32_t storeInput = store->GetSingleWordInOperand(1u); + BasicBlock* block = context()->get_instr_block(store); + BasicBlock::iterator where(store); + uint32_t elementIndex = 0; + for (auto var : replacements) { + // Create the extract. + if (var->opcode() != spv::Op::OpVariable) { + elementIndex++; + continue; + } + + Instruction* type = GetStorageType(var); + uint32_t extractId = TakeNextId(); + if (extractId == 0) { + return false; + } + std::unique_ptr extract(new Instruction( + context(), spv::Op::OpCompositeExtract, type->result_id(), extractId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {storeInput}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}})); + auto iter = where.InsertBefore(std::move(extract)); + iter->UpdateDebugInfoFrom(store); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, block); + + // Create the store. + std::unique_ptr newStore( + new Instruction(context(), spv::Op::OpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_ID, {extractId}}})); + // Copy memory access attributes which start at index 2. Index 0 is the + // pointer and index 1 is the data. + for (uint32_t i = 2; i < store->NumInOperands(); ++i) { + Operand copy(store->GetInOperand(i)); + newStore->AddOperand(std::move(copy)); + } + iter = where.InsertBefore(std::move(newStore)); + iter->UpdateDebugInfoFrom(store); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, block); + } + return true; +} + +bool ScalarReplacementPass::ReplaceAccessChain( + Instruction* chain, const std::vector& replacements) { + // Replaces the access chain with either another access chain (with one fewer + // indexes) or a direct use of the replacement variable. + uint32_t indexId = chain->GetSingleWordInOperand(1u); + const Instruction* index = get_def_use_mgr()->GetDef(indexId); + int64_t indexValue = context() + ->get_constant_mgr() + ->GetConstantFromInst(index) + ->GetSignExtendedValue(); + if (indexValue < 0 || + indexValue >= static_cast(replacements.size())) { + // Out of bounds access, this is illegal IR. Notice that OpAccessChain + // indexing is 0-based, so we should also reject index == size-of-array. + return false; + } else { + const Instruction* var = replacements[static_cast(indexValue)]; + if (chain->NumInOperands() > 2) { + // Replace input access chain with another access chain. + BasicBlock::iterator chainIter(chain); + uint32_t replacementId = TakeNextId(); + if (replacementId == 0) { + return false; + } + std::unique_ptr replacementChain(new Instruction( + context(), chain->opcode(), chain->type_id(), replacementId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + // Add the remaining indexes. + for (uint32_t i = 2; i < chain->NumInOperands(); ++i) { + Operand copy(chain->GetInOperand(i)); + replacementChain->AddOperand(std::move(copy)); + } + replacementChain->UpdateDebugInfoFrom(chain); + auto iter = chainIter.InsertBefore(std::move(replacementChain)); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, context()->get_instr_block(chain)); + context()->ReplaceAllUsesWith(chain->result_id(), replacementId); + } else { + // Replace with a use of the variable. + context()->ReplaceAllUsesWith(chain->result_id(), var->result_id()); + } + } + + return true; +} + +bool ScalarReplacementPass::CreateReplacementVariables( + Instruction* inst, std::vector* replacements) { + Instruction* type = GetStorageType(inst); + + std::unique_ptr> components_used = + GetUsedComponents(inst); + + uint32_t elem = 0; + switch (type->opcode()) { + case spv::Op::OpTypeStruct: + type->ForEachInOperand( + [this, inst, &elem, replacements, &components_used](uint32_t* id) { + if (!components_used || components_used->count(elem)) { + CreateVariable(*id, inst, elem, replacements); + } else { + replacements->push_back(GetUndef(*id)); + } + elem++; + }); + break; + case spv::Op::OpTypeArray: + for (uint32_t i = 0; i != GetArrayLength(type); ++i) { + if (!components_used || components_used->count(i)) { + CreateVariable(type->GetSingleWordInOperand(0u), inst, i, + replacements); + } else { + uint32_t element_type_id = type->GetSingleWordInOperand(0); + replacements->push_back(GetUndef(element_type_id)); + } + } + break; + + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + for (uint32_t i = 0; i != GetNumElements(type); ++i) { + CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements); + } + break; + + default: + assert(false && "Unexpected type."); + break; + } + + TransferAnnotations(inst, replacements); + return std::find(replacements->begin(), replacements->end(), nullptr) == + replacements->end(); +} + +Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) { + return get_def_use_mgr()->GetDef(Type2Undef(type_id)); +} + +void ScalarReplacementPass::TransferAnnotations( + const Instruction* source, std::vector* replacements) { + // Only transfer invariant and restrict decorations on the variable. There are + // no type or member decorations that are necessary to transfer. + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(source->result_id(), false)) { + assert(inst->opcode() == spv::Op::OpDecorate); + auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u)); + if (decoration == spv::Decoration::Invariant || + decoration == spv::Decoration::Restrict) { + for (auto var : *replacements) { + if (var == nullptr) { + continue; + } + + std::unique_ptr annotation(new Instruction( + context(), spv::Op::OpDecorate, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_DECORATION, {uint32_t(decoration)}}})); + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + Operand copy(inst->GetInOperand(i)); + annotation->AddOperand(std::move(copy)); + } + context()->AddAnnotationInst(std::move(annotation)); + get_def_use_mgr()->AnalyzeInstUse(&*--context()->annotation_end()); + } + } + } +} + +void ScalarReplacementPass::CreateVariable( + uint32_t type_id, Instruction* var_inst, uint32_t index, + std::vector* replacements) { + uint32_t ptr_id = GetOrCreatePointerType(type_id); + uint32_t id = TakeNextId(); + + if (id == 0) { + replacements->push_back(nullptr); + } + + std::unique_ptr variable( + new Instruction(context(), spv::Op::OpVariable, ptr_id, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}})); + + BasicBlock* block = context()->get_instr_block(var_inst); + block->begin().InsertBefore(std::move(variable)); + Instruction* inst = &*block->begin(); + + // If varInst was initialized, make sure to initialize its replacement. + GetOrCreateInitialValue(var_inst, index, inst); + get_def_use_mgr()->AnalyzeInstDefUse(inst); + context()->set_instr_block(inst, block); + + CopyDecorationsToVariable(var_inst, inst, index); + inst->UpdateDebugInfoFrom(var_inst); + + replacements->push_back(inst); +} + +uint32_t ScalarReplacementPass::GetOrCreatePointerType(uint32_t id) { + auto iter = pointee_to_pointer_.find(id); + if (iter != pointee_to_pointer_.end()) return iter->second; + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + uint32_t ptr_type_id = + type_mgr->FindPointerToType(id, spv::StorageClass::Function); + pointee_to_pointer_[id] = ptr_type_id; + return ptr_type_id; +} + +void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source, + uint32_t index, + Instruction* newVar) { + assert(source->opcode() == spv::Op::OpVariable); + if (source->NumInOperands() < 2) return; + + uint32_t initId = source->GetSingleWordInOperand(1u); + uint32_t storageId = GetStorageType(newVar)->result_id(); + Instruction* init = get_def_use_mgr()->GetDef(initId); + uint32_t newInitId = 0; + // TODO(dnovillo): Refactor this with constant propagation. + if (init->opcode() == spv::Op::OpConstantNull) { + // Initialize to appropriate NULL. + auto iter = type_to_null_.find(storageId); + if (iter == type_to_null_.end()) { + newInitId = TakeNextId(); + type_to_null_[storageId] = newInitId; + context()->AddGlobalValue( + MakeUnique(context(), spv::Op::OpConstantNull, storageId, + newInitId, std::initializer_list{})); + Instruction* newNull = &*--context()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDefUse(newNull); + } else { + newInitId = iter->second; + } + } else if (IsSpecConstantInst(init->opcode())) { + // Create a new constant extract. + newInitId = TakeNextId(); + context()->AddGlobalValue(MakeUnique( + context(), spv::Op::OpSpecConstantOp, storageId, newInitId, + std::initializer_list{ + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, + {uint32_t(spv::Op::OpCompositeExtract)}}, + {SPV_OPERAND_TYPE_ID, {init->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})); + Instruction* newSpecConst = &*--context()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDefUse(newSpecConst); + } else if (init->opcode() == spv::Op::OpConstantComposite) { + // Get the appropriate index constant. + newInitId = init->GetSingleWordInOperand(index); + Instruction* element = get_def_use_mgr()->GetDef(newInitId); + if (element->opcode() == spv::Op::OpUndef) { + // Undef is not a valid initializer for a variable. + newInitId = 0; + } + } else { + assert(false); + } + + if (newInitId != 0) { + newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}}); + } +} + +uint64_t ScalarReplacementPass::GetArrayLength( + const Instruction* arrayType) const { + assert(arrayType->opcode() == spv::Op::OpTypeArray); + const Instruction* length = + get_def_use_mgr()->GetDef(arrayType->GetSingleWordInOperand(1u)); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(length) + ->GetZeroExtendedValue(); +} + +uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const { + assert(type->opcode() == spv::Op::OpTypeVector || + type->opcode() == spv::Op::OpTypeMatrix); + const Operand& op = type->GetInOperand(1u); + assert(op.words.size() <= 2); + uint64_t len = 0; + for (size_t i = 0; i != op.words.size(); ++i) { + len |= (static_cast(op.words[i]) << (32ull * i)); + } + return len; +} + +bool ScalarReplacementPass::IsSpecConstant(uint32_t id) const { + const Instruction* inst = get_def_use_mgr()->GetDef(id); + assert(inst); + return spvOpcodeIsSpecConstant(inst->opcode()); +} + +Instruction* ScalarReplacementPass::GetStorageType( + const Instruction* inst) const { + assert(inst->opcode() == spv::Op::OpVariable); + + uint32_t ptrTypeId = inst->type_id(); + uint32_t typeId = + get_def_use_mgr()->GetDef(ptrTypeId)->GetSingleWordInOperand(1u); + return get_def_use_mgr()->GetDef(typeId); +} + +bool ScalarReplacementPass::CanReplaceVariable( + const Instruction* varInst) const { + assert(varInst->opcode() == spv::Op::OpVariable); + + // Can only replace function scope variables. + if (spv::StorageClass(varInst->GetSingleWordInOperand(0u)) != + spv::StorageClass::Function) { + return false; + } + + if (!CheckTypeAnnotations(get_def_use_mgr()->GetDef(varInst->type_id()))) { + return false; + } + + const Instruction* typeInst = GetStorageType(varInst); + if (!CheckType(typeInst)) { + return false; + } + + if (!CheckAnnotations(varInst)) { + return false; + } + + if (!CheckUses(varInst)) { + return false; + } + + return true; +} + +bool ScalarReplacementPass::CheckType(const Instruction* typeInst) const { + if (!CheckTypeAnnotations(typeInst)) { + return false; + } + + switch (typeInst->opcode()) { + case spv::Op::OpTypeStruct: + // Don't bother with empty structs or very large structs. + if (typeInst->NumInOperands() == 0 || + IsLargerThanSizeLimit(typeInst->NumInOperands())) { + return false; + } + return true; + case spv::Op::OpTypeArray: + if (IsSpecConstant(typeInst->GetSingleWordInOperand(1u))) { + return false; + } + if (IsLargerThanSizeLimit(GetArrayLength(typeInst))) { + return false; + } + return true; + // TODO(alanbaker): Develop some heuristics for when this should be + // re-enabled. + //// Specifically including matrix and vector in an attempt to reduce the + //// number of vector registers required. + // case spv::Op::OpTypeMatrix: + // case spv::Op::OpTypeVector: + // if (IsLargerThanSizeLimit(GetNumElements(typeInst))) return false; + // return true; + + case spv::Op::OpTypeRuntimeArray: + default: + return false; + } +} + +bool ScalarReplacementPass::CheckTypeAnnotations( + const Instruction* typeInst) const { + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) { + uint32_t decoration; + if (inst->opcode() == spv::Op::OpDecorate || + inst->opcode() == spv::Op::OpDecorateId) { + decoration = inst->GetSingleWordInOperand(1u); + } else { + assert(inst->opcode() == spv::Op::OpMemberDecorate); + decoration = inst->GetSingleWordInOperand(2u); + } + + switch (spv::Decoration(decoration)) { + case spv::Decoration::RowMajor: + case spv::Decoration::ColMajor: + case spv::Decoration::ArrayStride: + case spv::Decoration::MatrixStride: + case spv::Decoration::CPacked: + case spv::Decoration::Invariant: + case spv::Decoration::Restrict: + case spv::Decoration::Offset: + case spv::Decoration::Alignment: + case spv::Decoration::AlignmentId: + case spv::Decoration::MaxByteOffset: + case spv::Decoration::RelaxedPrecision: + case spv::Decoration::AliasedPointer: + case spv::Decoration::RestrictPointer: + break; + default: + return false; + } + } + + return true; +} + +bool ScalarReplacementPass::CheckAnnotations(const Instruction* varInst) const { + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(varInst->result_id(), false)) { + assert(inst->opcode() == spv::Op::OpDecorate); + auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u)); + switch (decoration) { + case spv::Decoration::Invariant: + case spv::Decoration::Restrict: + case spv::Decoration::Alignment: + case spv::Decoration::AlignmentId: + case spv::Decoration::MaxByteOffset: + case spv::Decoration::AliasedPointer: + case spv::Decoration::RestrictPointer: + break; + default: + return false; + } + } + + return true; +} + +bool ScalarReplacementPass::CheckUses(const Instruction* inst) const { + VariableStats stats = {0, 0}; + bool ok = CheckUses(inst, &stats); + + // TODO(alanbaker/greg-lunarg): Add some meaningful heuristics about when + // SRoA is costly, such as when the structure has many (unaccessed?) + // members. + + return ok; +} + +bool ScalarReplacementPass::CheckUses(const Instruction* inst, + VariableStats* stats) const { + uint64_t max_legal_index = GetMaxLegalIndex(inst); + + bool ok = true; + get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( + const Instruction* user, + uint32_t index) { + if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare || + user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) { + // TODO: include num_partial_accesses if it uses Fragment operation or + // DebugValue has Indexes operand. + stats->num_full_accesses++; + return; + } + + // Annotations are check as a group separately. + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (index == 2u && user->NumInOperands() > 1) { + uint32_t id = user->GetSingleWordInOperand(1u); + const Instruction* opInst = get_def_use_mgr()->GetDef(id); + const auto* constant = + context()->get_constant_mgr()->GetConstantFromInst(opInst); + if (!constant) { + ok = false; + } else if (constant->GetZeroExtendedValue() >= max_legal_index) { + ok = false; + } else { + if (!CheckUsesRelaxed(user)) ok = false; + } + stats->num_partial_accesses++; + } else { + ok = false; + } + break; + case spv::Op::OpLoad: + if (!CheckLoad(user, index)) ok = false; + stats->num_full_accesses++; + break; + case spv::Op::OpStore: + if (!CheckStore(user, index)) ok = false; + stats->num_full_accesses++; + break; + case spv::Op::OpName: + case spv::Op::OpMemberName: + break; + default: + ok = false; + break; + } + } + }); + + return ok; +} + +bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { + bool ok = true; + get_def_use_mgr()->ForEachUse( + inst, [this, &ok](const Instruction* user, uint32_t index) { + switch (user->opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + if (index != 2u) { + ok = false; + } else { + if (!CheckUsesRelaxed(user)) ok = false; + } + break; + case spv::Op::OpLoad: + if (!CheckLoad(user, index)) ok = false; + break; + case spv::Op::OpStore: + if (!CheckStore(user, index)) ok = false; + break; + case spv::Op::OpImageTexelPointer: + if (!CheckImageTexelPointer(index)) ok = false; + break; + case spv::Op::OpExtInst: + if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare || + !CheckDebugDeclare(index)) + ok = false; + break; + default: + ok = false; + break; + } + }); + + return ok; +} + +bool ScalarReplacementPass::CheckImageTexelPointer(uint32_t index) const { + return index == 2u; +} + +bool ScalarReplacementPass::CheckLoad(const Instruction* inst, + uint32_t index) const { + if (index != 2u) return false; + if (inst->NumInOperands() >= 2 && + inst->GetSingleWordInOperand(1u) & + uint32_t(spv::MemoryAccessMask::Volatile)) + return false; + return true; +} + +bool ScalarReplacementPass::CheckStore(const Instruction* inst, + uint32_t index) const { + if (index != 0u) return false; + if (inst->NumInOperands() >= 3 && + inst->GetSingleWordInOperand(2u) & + uint32_t(spv::MemoryAccessMask::Volatile)) + return false; + return true; +} + +bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const { + if (index != kDebugDeclareOperandVariableIndex) return false; + return true; +} + +bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { + if (max_num_elements_ == 0) { + return false; + } + return length > max_num_elements_; +} + +std::unique_ptr> +ScalarReplacementPass::GetUsedComponents(Instruction* inst) { + std::unique_ptr> result( + new std::unordered_set()); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + def_use_mgr->WhileEachUser(inst, [&result, def_use_mgr, + this](Instruction* use) { + switch (use->opcode()) { + case spv::Op::OpLoad: { + // Look for extract from the load. + std::vector t; + if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) { + if (use2->opcode() != spv::Op::OpCompositeExtract || + use2->NumInOperands() <= 1) { + return false; + } + t.push_back(use2->GetSingleWordInOperand(1)); + return true; + })) { + result->insert(t.begin(), t.end()); + return true; + } else { + result.reset(nullptr); + return false; + } + } + case spv::Op::OpName: + case spv::Op::OpMemberName: + case spv::Op::OpStore: + // No components are used. + return true; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + // Add the first index it if is a constant. + // TODO: Could be improved by checking if the address is used in a load. + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + uint32_t index_id = use->GetSingleWordInOperand(1); + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(index_id); + if (index_const) { + result->insert(index_const->GetSignExtendedValue()); + return true; + } else { + // Could be any element. Assuming all are used. + result.reset(nullptr); + return false; + } + } + default: + // We do not know what is happening. Have to assume the worst. + result.reset(nullptr); + return false; + } + }); + + return result; +} + +uint64_t ScalarReplacementPass::GetMaxLegalIndex( + const Instruction* var_inst) const { + assert(var_inst->opcode() == spv::Op::OpVariable && + "|var_inst| must be a variable instruction."); + Instruction* type = GetStorageType(var_inst); + switch (type->opcode()) { + case spv::Op::OpTypeStruct: + return type->NumInOperands(); + case spv::Op::OpTypeArray: + return GetArrayLength(type); + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + return GetNumElements(type); + default: + return 0; + } + return 0; +} + +void ScalarReplacementPass::CopyDecorationsToVariable(Instruction* from, + Instruction* to, + uint32_t member_index) { + CopyPointerDecorationsToVariable(from, to); + CopyNecessaryMemberDecorationsToVariable(from, to, member_index); +} + +void ScalarReplacementPass::CopyPointerDecorationsToVariable(Instruction* from, + Instruction* to) { + // The RestrictPointer and AliasedPointer decorations are copied to all + // members even if the new variable does not contain a pointer. It does + // not hurt to do so. + for (auto dec_inst : + get_decoration_mgr()->GetDecorationsFor(from->result_id(), false)) { + uint32_t decoration; + decoration = dec_inst->GetSingleWordInOperand(1u); + switch (spv::Decoration(decoration)) { + case spv::Decoration::AliasedPointer: + case spv::Decoration::RestrictPointer: { + std::unique_ptr new_dec_inst(dec_inst->Clone(context())); + new_dec_inst->SetInOperand(0, {to->result_id()}); + context()->AddAnnotationInst(std::move(new_dec_inst)); + } break; + default: + break; + } + } +} + +void ScalarReplacementPass::CopyNecessaryMemberDecorationsToVariable( + Instruction* from, Instruction* to, uint32_t member_index) { + Instruction* type_inst = GetStorageType(from); + for (auto dec_inst : + get_decoration_mgr()->GetDecorationsFor(type_inst->result_id(), false)) { + uint32_t decoration; + if (dec_inst->opcode() == spv::Op::OpMemberDecorate) { + if (dec_inst->GetSingleWordInOperand(1) != member_index) { + continue; + } + + decoration = dec_inst->GetSingleWordInOperand(2u); + switch (spv::Decoration(decoration)) { + case spv::Decoration::ArrayStride: + case spv::Decoration::Alignment: + case spv::Decoration::AlignmentId: + case spv::Decoration::MaxByteOffset: + case spv::Decoration::MaxByteOffsetId: + case spv::Decoration::RelaxedPrecision: { + std::unique_ptr new_dec_inst( + new Instruction(context(), spv::Op::OpDecorate, 0, 0, {})); + new_dec_inst->AddOperand( + Operand(SPV_OPERAND_TYPE_ID, {to->result_id()})); + for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) { + new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i))); + } + context()->AddAnnotationInst(std::move(new_dec_inst)); + } break; + default: + break; + } + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.h new file mode 100644 index 00000000..61d341e5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/scalar_replacement_pass.h @@ -0,0 +1,294 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ +#define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class ScalarReplacementPass : public MemPass { + private: + static constexpr uint32_t kDefaultLimit = 0; + + public: + ScalarReplacementPass(uint32_t limit = kDefaultLimit) + : max_num_elements_(limit) { + const auto num_to_write = snprintf( + name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_); + assert(size_t(num_to_write) < sizeof(name_)); + (void)num_to_write; // Mark as unused + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays. + // This can cause timeouts and memouts during fuzzing that + // are not classed as bugs. To avoid this noise, we set the + // max_num_elements_ to a smaller value for fuzzing. + max_num_elements_ = + (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_ + : 100); +#endif + } + + const char* name() const override { return name_; } + + // Attempts to scalarize all appropriate function scope variables. Returns + // SuccessWithChange if any change is made. + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Small container for tracking statistics about variables. + // + // TODO(alanbaker): Develop some useful heuristics to tune this pass. + struct VariableStats { + uint32_t num_partial_accesses; + uint32_t num_full_accesses; + }; + + // Attempts to scalarize all appropriate function scope variables in + // |function|. Returns SuccessWithChange if any changes are mode. + Status ProcessFunction(Function* function); + + // Returns true if |varInst| can be scalarized. + // + // Examines the use chain of |varInst| to verify all uses are valid for + // scalarization. + bool CanReplaceVariable(const Instruction* varInst) const; + + // Returns true if |typeInst| is an acceptable type to scalarize. + // + // Allows all aggregate types except runtime arrays. Additionally, checks the + // that the number of elements that would be scalarized is within bounds. + bool CheckType(const Instruction* typeInst) const; + + // Returns true if all the decorations for |varInst| are acceptable for + // scalarization. + bool CheckAnnotations(const Instruction* varInst) const; + + // Returns true if all the decorations for |typeInst| are acceptable for + // scalarization. + bool CheckTypeAnnotations(const Instruction* typeInst) const; + + // Returns true if the uses of |inst| are acceptable for scalarization. + // + // Recursively checks all the uses of |inst|. For |inst| specifically, only + // allows spv::Op::OpAccessChain, spv::Op::OpInBoundsAccessChain, + // spv::Op::OpLoad and spv::Op::OpStore. Access chains must have the first + // index be a compile-time constant. Subsequent uses of access chains + // (including other access chains) are checked in a more relaxed manner. + bool CheckUses(const Instruction* inst) const; + + // Helper function for the above |CheckUses|. + // + // This version tracks some stats about the current OpVariable. These stats + // are used to drive heuristics about when to scalarize. + bool CheckUses(const Instruction* inst, VariableStats* stats) const; + + // Relaxed helper function for |CheckUses|. + bool CheckUsesRelaxed(const Instruction* inst) const; + + // Transfers appropriate decorations from |source| to |replacements|. + void TransferAnnotations(const Instruction* source, + std::vector* replacements); + + // Scalarizes |inst| and updates its uses. + // + // |inst| must be an OpVariable. It is replaced with an OpVariable for each + // for element of the composite type. Uses of |inst| are updated as + // appropriate. If the replacement variables are themselves scalarizable, they + // get added to |worklist| for further processing. If any replacement + // variable ends up with no uses it is erased. Returns + // - Status::SuccessWithoutChange if the variable could not be replaced. + // - Status::SuccessWithChange if it made replacements. + // - Status::Failure if it couldn't create replacement variables. + Pass::Status ReplaceVariable(Instruction* inst, + std::queue* worklist); + + // Returns the underlying storage type for |inst|. + // + // |inst| must be an OpVariable. Returns the type that is pointed to by + // |inst|. + Instruction* GetStorageType(const Instruction* inst) const; + + // Returns true if the load can be scalarized. + // + // |inst| must be an OpLoad. Returns true if |index| is the pointer operand of + // |inst| and the load is not from volatile memory. + bool CheckLoad(const Instruction* inst, uint32_t index) const; + + // Returns true if the store can be scalarized. + // + // |inst| must be an OpStore. Returns true if |index| is the pointer operand + // of |inst| and the store is not to volatile memory. + bool CheckStore(const Instruction* inst, uint32_t index) const; + + // Returns true if the DebugDeclare can be scalarized at |index|. + bool CheckDebugDeclare(uint32_t index) const; + + // Returns true if |index| is the pointer operand of an OpImageTexelPointer + // instruction. + bool CheckImageTexelPointer(uint32_t index) const; + + // Creates a variable of type |typeId| from the |index|'th element of + // |varInst|. The new variable is added to |replacements|. If the variable + // could not be created, then |nullptr| is appended to |replacements|. + void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index, + std::vector* replacements); + + // Populates |replacements| with a new OpVariable for each element of |inst|. + // Returns true if the replacement variables were successfully created. + // + // |inst| must be an OpVariable of a composite type. New variables are + // initialized the same as the corresponding index in |inst|. |replacements| + // will contain a variable for each element of the composite with matching + // indexes (i.e. the 0'th element of |inst| is the 0'th entry of + // |replacements|). + bool CreateReplacementVariables(Instruction* inst, + std::vector* replacements); + + // Returns the array length for |arrayInst|. + uint64_t GetArrayLength(const Instruction* arrayInst) const; + + // Returns the number of elements in |type|. + // + // |type| must be a vector or matrix type. + uint64_t GetNumElements(const Instruction* type) const; + + // Returns true if |id| is a specialization constant. + // + // |id| must be registered definition. + bool IsSpecConstant(uint32_t id) const; + + // Returns an id for a pointer to |id|. + uint32_t GetOrCreatePointerType(uint32_t id); + + // Creates the initial value for the |index| element of |source| in |newVar|. + // + // If there is an initial value for |source| for element |index|, it is + // appended as an operand on |newVar|. If the initial value is OpUndef, no + // initial value is added to |newVar|. + void GetOrCreateInitialValue(Instruction* source, uint32_t index, + Instruction* newVar); + + // Replaces the load to the entire composite. + // + // Generates a load for each replacement variable and then creates a new + // composite by combining all of the loads. + // + // |load| must be a load. Returns true if successful. + bool ReplaceWholeLoad(Instruction* load, + const std::vector& replacements); + + // Replaces the store to the entire composite. + // + // Generates a composite extract and store for each element in the scalarized + // variable from the original store data input. Returns true if successful. + bool ReplaceWholeStore(Instruction* store, + const std::vector& replacements); + + // Replaces the DebugDeclare to the entire composite. + // + // Generates a DebugValue with Deref operation for each element in the + // scalarized variable from the original DebugDeclare. Returns true if + // successful. + bool ReplaceWholeDebugDeclare(Instruction* dbg_decl, + const std::vector& replacements); + + // Replaces the DebugValue to the entire composite. + // + // Generates a DebugValue for each element in the scalarized variable from + // the original DebugValue. Returns true if successful. + bool ReplaceWholeDebugValue(Instruction* dbg_value, + const std::vector& replacements); + + // Replaces an access chain to the composite variable with either a direct use + // of the appropriate replacement variable or another access chain with the + // replacement variable as the base and one fewer indexes. Returns true if + // successful. + bool ReplaceAccessChain(Instruction* chain, + const std::vector& replacements); + + // Returns a set containing the which components of the result of |inst| are + // potentially used. If the return value is |nullptr|, then every components + // is possibly used. + std::unique_ptr> GetUsedComponents( + Instruction* inst); + + // Returns an instruction defining an undefined value type |type_id|. + Instruction* GetUndef(uint32_t type_id); + + // Maps storage type to a pointer type enclosing that type. + std::unordered_map pointee_to_pointer_; + + // Maps type id to OpConstantNull for that type. + std::unordered_map type_to_null_; + + // Returns the number of elements in the variable |var_inst|. + uint64_t GetMaxLegalIndex(const Instruction* var_inst) const; + + // Returns true if |length| is larger than limit on the size of the variable + // that we will be willing to split. + bool IsLargerThanSizeLimit(uint64_t length) const; + + // Copies all relevant decorations from `from` to `to`. This includes + // decorations applied to the variable, and to the members of the type. + // It is assumed that `to` is a variable that is intended to replace the + // `member_index`th member of `from`. + void CopyDecorationsToVariable(Instruction* from, Instruction* to, + uint32_t member_index); + + // Copies pointer related decoration from `from` to `to` if they exist. + void CopyPointerDecorationsToVariable(Instruction* from, Instruction* to); + + // Copies decorations that are needed from the `member_index` of `from` to + // `to, if there was one. + void CopyNecessaryMemberDecorationsToVariable(Instruction* from, + Instruction* to, + uint32_t member_index); + + // Limit on the number of members in an object that will be replaced. + // 0 means there is no limit. + uint32_t max_num_elements_; + + // This has to be big enough to fit "scalar-replacement=" followed by a + // uint32_t number written in decimal (so 10 digits), and then a + // terminating nul. + char name_[30]; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp new file mode 100644 index 00000000..d2aa9b1d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp @@ -0,0 +1,390 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/set_spec_constant_default_value_pass.h" + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { +namespace { +using utils::EncodeNumberStatus; +using utils::NumberType; +using utils::ParseAndEncodeNumber; +using utils::ParseNumber; + +// Given a numeric value in a null-terminated c string and the expected type of +// the value, parses the string and encodes it in a vector of words. If the +// value is a scalar integer or floating point value, encodes the value in +// SPIR-V encoding format. If the value is 'false' or 'true', returns a vector +// with single word with value 0 or 1 respectively. Returns the vector +// containing the encoded value on success. Otherwise returns an empty vector. +std::vector ParseDefaultValueStr(const char* text, + const analysis::Type* type) { + std::vector result; + if (!strcmp(text, "true") && type->AsBool()) { + result.push_back(1u); + } else if (!strcmp(text, "false") && type->AsBool()) { + result.push_back(0u); + } else { + NumberType number_type = {32, SPV_NUMBER_UNSIGNED_INT}; + if (const auto* IT = type->AsInteger()) { + number_type.bitwidth = IT->width(); + number_type.kind = + IT->IsSigned() ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT; + } else if (const auto* FT = type->AsFloat()) { + number_type.bitwidth = FT->width(); + number_type.kind = SPV_NUMBER_FLOATING; + } else { + // Does not handle types other then boolean, integer or float. Returns + // empty vector. + result.clear(); + return result; + } + EncodeNumberStatus rc = ParseAndEncodeNumber( + text, number_type, [&result](uint32_t word) { result.push_back(word); }, + nullptr); + // Clear the result vector on failure. + if (rc != EncodeNumberStatus::kSuccess) { + result.clear(); + } + } + return result; +} + +// Given a bit pattern and a type, checks if the bit pattern is compatible +// with the type. If so, returns the bit pattern, otherwise returns an empty +// bit pattern. If the given bit pattern is empty, returns an empty bit +// pattern. If the given type represents a SPIR-V Boolean type, the bit pattern +// to be returned is determined with the following standard: +// If any words in the input bit pattern are non zero, returns a bit pattern +// with 0x1, which represents a 'true'. +// If all words in the bit pattern are zero, returns a bit pattern with 0x0, +// which represents a 'false'. +// For integer and floating point types narrower than 32 bits, the upper bits +// in the input bit pattern are ignored. Instead the upper bits are set +// according to SPIR-V literal requirements: sign extend a signed integer, and +// otherwise set the upper bits to zero. +std::vector ParseDefaultValueBitPattern( + const std::vector& input_bit_pattern, + const analysis::Type* type) { + std::vector result; + if (type->AsBool()) { + if (std::any_of(input_bit_pattern.begin(), input_bit_pattern.end(), + [](uint32_t i) { return i != 0; })) { + result.push_back(1u); + } else { + result.push_back(0u); + } + return result; + } else if (const auto* IT = type->AsInteger()) { + const auto width = IT->width(); + assert(width > 0); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + const uint32_t high_active_bit = (1u << width) >> 1; + if (IT->IsSigned() && (high_active_bit & result[0])) { + // Sign extend. This overwrites the sign bit again, but that's ok. + result[0] = result[0] | ~(high_active_bit - 1); + } else { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + } + return result; + } + } else if (const auto* FT = type->AsFloat()) { + const auto width = FT->width(); + const auto adjusted_width = std::max(32u, width); + if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + result = std::vector(input_bit_pattern); + if (width < 32) { + // Upper bits must be zero. + result[0] = result[0] & ((1u << width) - 1); + } + return result; + } + } + result.clear(); + return result; +} + +// Returns true if the given instruction's result id could have a SpecId +// decoration. +bool CanHaveSpecIdDecoration(const Instruction& inst) { + switch (inst.opcode()) { + case spv::Op::OpSpecConstant: + case spv::Op::OpSpecConstantFalse: + case spv::Op::OpSpecConstantTrue: + return true; + default: + return false; + } +} + +// Given a decoration group defining instruction that is decorated with SpecId +// decoration, finds the spec constant defining instruction which is the real +// target of the SpecId decoration. Returns the spec constant defining +// instruction if such an instruction is found, otherwise returns a nullptr. +Instruction* GetSpecIdTargetFromDecorationGroup( + const Instruction& decoration_group_defining_inst, + analysis::DefUseManager* def_use_mgr) { + // Find the OpGroupDecorate instruction which consumes the given decoration + // group. Note that the given decoration group has SpecId decoration, which + // is unique for different spec constants. So the decoration group cannot be + // consumed by different OpGroupDecorate instructions. Therefore we only need + // the first OpGroupDecoration instruction that uses the given decoration + // group. + Instruction* group_decorate_inst = nullptr; + if (def_use_mgr->WhileEachUser(&decoration_group_defining_inst, + [&group_decorate_inst](Instruction* user) { + if (user->opcode() == + spv::Op::OpGroupDecorate) { + group_decorate_inst = user; + return false; + } + return true; + })) + return nullptr; + + // Scan through the target ids of the OpGroupDecorate instruction. There + // should be only one spec constant target consumes the SpecId decoration. + // If multiple target ids are presented in the OpGroupDecorate instruction, + // they must be the same one that defined by an eligible spec constant + // instruction. If the OpGroupDecorate instruction has different target ids + // or a target id is not defined by an eligible spec cosntant instruction, + // returns a nullptr. + Instruction* target_inst = nullptr; + for (uint32_t i = 1; i < group_decorate_inst->NumInOperands(); i++) { + // All the operands of a OpGroupDecorate instruction should be of type + // SPV_OPERAND_TYPE_ID. + uint32_t candidate_id = group_decorate_inst->GetSingleWordInOperand(i); + Instruction* candidate_inst = def_use_mgr->GetDef(candidate_id); + + if (!candidate_inst) { + continue; + } + + if (!target_inst) { + // If the spec constant target has not been found yet, check if the + // candidate instruction is the target. + if (CanHaveSpecIdDecoration(*candidate_inst)) { + target_inst = candidate_inst; + } else { + // Spec id decoration should not be applied on other instructions. + // TODO(qining): Emit an error message in the invalid case once the + // error handling is done. + return nullptr; + } + } else { + // If the spec constant target has been found, check if the candidate + // instruction is the same one as the target. The module is invalid if + // the candidate instruction is different with the found target. + // TODO(qining): Emit an error messaage in the invalid case once the + // error handling is done. + if (candidate_inst != target_inst) return nullptr; + } + } + return target_inst; +} +} // namespace + +Pass::Status SetSpecConstantDefaultValuePass::Process() { + // The operand index of decoration target in an OpDecorate instruction. + constexpr uint32_t kTargetIdOperandIndex = 0; + // The operand index of the decoration literal in an OpDecorate instruction. + constexpr uint32_t kDecorationOperandIndex = 1; + // The operand index of Spec id literal value in an OpDecorate SpecId + // instruction. + constexpr uint32_t kSpecIdLiteralOperandIndex = 2; + // The number of operands in an OpDecorate SpecId instruction. + constexpr uint32_t kOpDecorateSpecIdNumOperands = 3; + // The in-operand index of the default value in a OpSpecConstant instruction. + constexpr uint32_t kOpSpecConstantLiteralInOperandIndex = 0; + + bool modified = false; + // Scan through all the annotation instructions to find 'OpDecorate SpecId' + // instructions. Then extract the decoration target of those instructions. + // The decoration targets should be spec constant defining instructions with + // opcode: OpSpecConstant{|True|False}. The spec id of those spec constants + // will be used to look up their new default values in the mapping from + // spec id to new default value strings. Once a new default value string + // is found for a spec id, the string will be parsed according to the target + // spec constant type. The parsed value will be used to replace the original + // default value of the target spec constant. + for (Instruction& inst : context()->annotations()) { + // Only process 'OpDecorate SpecId' instructions + if (inst.opcode() != spv::Op::OpDecorate) continue; + if (inst.NumOperands() != kOpDecorateSpecIdNumOperands) continue; + if (inst.GetSingleWordInOperand(kDecorationOperandIndex) != + uint32_t(spv::Decoration::SpecId)) { + continue; + } + + // 'inst' is an OpDecorate SpecId instruction. + uint32_t spec_id = inst.GetSingleWordOperand(kSpecIdLiteralOperandIndex); + uint32_t target_id = inst.GetSingleWordOperand(kTargetIdOperandIndex); + + // Find the spec constant defining instruction. Note that the + // target_id might be a decoration group id. + Instruction* spec_inst = nullptr; + if (Instruction* target_inst = get_def_use_mgr()->GetDef(target_id)) { + if (target_inst->opcode() == spv::Op::OpDecorationGroup) { + spec_inst = + GetSpecIdTargetFromDecorationGroup(*target_inst, get_def_use_mgr()); + } else { + spec_inst = target_inst; + } + } else { + continue; + } + if (!spec_inst) continue; + + // Get the default value bit pattern for this spec id. + std::vector bit_pattern; + + if (spec_id_to_value_str_.size() != 0) { + // Search for the new string-form default value for this spec id. + auto iter = spec_id_to_value_str_.find(spec_id); + if (iter == spec_id_to_value_str_.end()) { + continue; + } + + // Gets the string of the default value and parses it to bit pattern + // with the type of the spec constant. + const std::string& default_value_str = iter->second; + bit_pattern = ParseDefaultValueStr( + default_value_str.c_str(), + context()->get_type_mgr()->GetType(spec_inst->type_id())); + + } else { + // Search for the new bit-pattern-form default value for this spec id. + auto iter = spec_id_to_value_bit_pattern_.find(spec_id); + if (iter == spec_id_to_value_bit_pattern_.end()) { + continue; + } + + // Gets the bit-pattern of the default value from the map directly. + bit_pattern = ParseDefaultValueBitPattern( + iter->second, + context()->get_type_mgr()->GetType(spec_inst->type_id())); + } + + if (bit_pattern.empty()) continue; + + // Update the operand bit patterns of the spec constant defining + // instruction. + switch (spec_inst->opcode()) { + case spv::Op::OpSpecConstant: + // If the new value is the same with the original value, no + // need to do anything. Otherwise update the operand words. + if (spec_inst->GetInOperand(kOpSpecConstantLiteralInOperandIndex) + .words != bit_pattern) { + spec_inst->SetInOperand(kOpSpecConstantLiteralInOperandIndex, + std::move(bit_pattern)); + modified = true; + } + break; + case spv::Op::OpSpecConstantTrue: + // If the new value is also 'true', no need to change anything. + // Otherwise, set the opcode to OpSpecConstantFalse; + if (!static_cast(bit_pattern.front())) { + spec_inst->SetOpcode(spv::Op::OpSpecConstantFalse); + modified = true; + } + break; + case spv::Op::OpSpecConstantFalse: + // If the new value is also 'false', no need to change anything. + // Otherwise, set the opcode to OpSpecConstantTrue; + if (static_cast(bit_pattern.front())) { + spec_inst->SetOpcode(spv::Op::OpSpecConstantTrue); + modified = true; + } + break; + default: + break; + } + // No need to update the DefUse manager, as this pass does not change any + // ids. + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +std::unique_ptr +SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) { + if (!str) return nullptr; + + auto spec_id_to_value = MakeUnique(); + + // The parsing loop, break when points to the end. + while (*str) { + // Find the spec id. + while (std::isspace(*str)) str++; // skip leading spaces. + const char* entry_begin = str; + while (!IsSeparator(*str)) str++; + const char* entry_end = str; + std::string spec_id_str(entry_begin, entry_end - entry_begin); + uint32_t spec_id = 0; + if (!ParseNumber(spec_id_str.c_str(), &spec_id)) { + // The spec id is not a valid uint32 number. + return nullptr; + } + auto iter = spec_id_to_value->find(spec_id); + if (iter != spec_id_to_value->end()) { + // Same spec id has been defined before + return nullptr; + } + // Find the ':', spaces between the spec id and the ':' are not allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + // Find the value string + const char* val_begin = str; + while (!IsSeparator(*str)) str++; + const char* val_end = str; + if (val_end == val_begin) { + // Value string is empty. + return nullptr; + } + // Update the mapping with spec id and value string. + (*spec_id_to_value)[spec_id] = std::string(val_begin, val_end - val_begin); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return spec_id_to_value; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.h new file mode 100644 index 00000000..8bd1787c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/set_spec_constant_default_value_pass.h @@ -0,0 +1,114 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ +#define SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SetSpecConstantDefaultValuePass : public Pass { + public: + using SpecIdToValueStrMap = std::unordered_map; + using SpecIdToValueBitPatternMap = + std::unordered_map>; + using SpecIdToInstMap = std::unordered_map; + + // Constructs a pass instance with a map from spec ids to default values + // in the form of string. + explicit SetSpecConstantDefaultValuePass( + const SpecIdToValueStrMap& default_values) + : spec_id_to_value_str_(default_values), + spec_id_to_value_bit_pattern_() {} + explicit SetSpecConstantDefaultValuePass(SpecIdToValueStrMap&& default_values) + : spec_id_to_value_str_(std::move(default_values)), + spec_id_to_value_bit_pattern_() {} + + // Constructs a pass instance with a map from spec ids to default values in + // the form of bit pattern. + explicit SetSpecConstantDefaultValuePass( + const SpecIdToValueBitPatternMap& default_values) + : spec_id_to_value_str_(), + spec_id_to_value_bit_pattern_(default_values) {} + explicit SetSpecConstantDefaultValuePass( + SpecIdToValueBitPatternMap&& default_values) + : spec_id_to_value_str_(), + spec_id_to_value_bit_pattern_(std::move(default_values)) {} + + const char* name() const override { return "set-spec-const-default-value"; } + Status Process() override; + + // Parses the given null-terminated C string to get a mapping from Spec Id to + // default value strings. Returns a unique pointer of the mapping from spec + // ids to spec constant default value strings built from the given |str| on + // success. Returns a nullptr if the given string is not valid for building + // the mapping. + // A valid string for building the mapping should follow the rule below: + // + // ": : ..." + // Example: + // "200:0x11 201:3.14 202:1.4728" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a Spec Id and default value pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within spec id or the default value string because + // spaces are always considered as delimiter to separate entries. + // + // In each entry, the spec id and value string is separated by ':'. Missing + // ':' in any entry is invalid. And it is invalid to have blank spaces in + // between the spec id and ':' or the default value and ':'. + // + // : specifies the spec id value. + // The text must represent a valid uint32_t number. + // Hex format with '0x' prefix is allowed. + // Empty is not allowed. + // One spec id value can only be defined once, multiple default values + // defined for the same spec id is not allowed. Spec ids with same value + // but different formats (e.g. 0x100 and 256) are considered the same. + // + // : the default value string. + // Spaces before and after default value text is allowed. + // Spaces within the text is not allowed. + // Empty is not allowed. + static std::unique_ptr ParseDefaultValuesString( + const char* str); + + private: + // The mappings from spec ids to default values. Two maps are defined here, + // each to be used for one specific form of the default values. Only one of + // them will be populated in practice. + + // The mapping from spec ids to their string-form default values to be set. + const SpecIdToValueStrMap spec_id_to_value_str_; + // The mapping from spec ids to their bitpattern-form default values to be + // set. + const SpecIdToValueBitPatternMap spec_id_to_value_bit_pattern_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.cpp new file mode 100644 index 00000000..f8ffc03c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/simplification_pass.h" + +#include +#include + +#include "source/opt/fold.h" + +namespace spvtools { +namespace opt { + +Pass::Status SimplificationPass::Process() { + bool modified = false; + + for (Function& function : *get_module()) { + modified |= SimplifyFunction(&function); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void SimplificationPass::AddNewOperands( + Instruction* folded_inst, std::unordered_set* inst_seen, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + folded_inst->ForEachInId( + [&inst_seen, &def_use_mgr, &work_list](uint32_t* iid) { + Instruction* iid_inst = def_use_mgr->GetDef(*iid); + if (!inst_seen->insert(iid_inst).second) return; + work_list->push_back(iid_inst); + }); +} + +bool SimplificationPass::SimplifyFunction(Function* function) { + if (function->IsDeclaration()) { + return false; + } + + bool modified = false; + // Phase 1: Traverse all instructions in dominance order. + // The second phase will only be on the instructions whose inputs have changed + // after being processed during phase 1. Since OpPhi instructions are the + // only instructions whose inputs do not necessarily dominate the use, we keep + // track of the OpPhi instructions already seen, and add them to the work list + // for phase 2 when needed. + std::vector work_list; + std::unordered_set process_phis; + std::unordered_set inst_to_kill; + std::unordered_set in_work_list; + std::unordered_set inst_seen; + const InstructionFolder& folder = context()->get_instruction_folder(); + + cfg()->ForEachBlockInReversePostOrder( + function->entry().get(), + [&modified, &process_phis, &work_list, &in_work_list, &inst_to_kill, + &folder, &inst_seen, this](BasicBlock* bb) { + for (Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) { + inst_seen.insert(inst); + if (inst->opcode() == spv::Op::OpPhi) { + process_phis.insert(inst); + } + + bool is_foldable_copy = + inst->opcode() == spv::Op::OpCopyObject && + context()->get_decoration_mgr()->HaveSubsetOfDecorations( + inst->result_id(), inst->GetSingleWordInOperand(0)); + + if (is_foldable_copy || folder.FoldInstruction(inst)) { + modified = true; + context()->AnalyzeUses(inst); + get_def_use_mgr()->ForEachUser(inst, [&work_list, &process_phis, + &in_work_list]( + Instruction* use) { + if (process_phis.count(use) && in_work_list.insert(use).second) { + work_list.push_back(use); + } + }); + + AddNewOperands(inst, &inst_seen, &work_list); + + if (inst->opcode() == spv::Op::OpCopyObject) { + context()->ReplaceAllUsesWithPredicate( + inst->result_id(), inst->GetSingleWordInOperand(0), + [](Instruction* user) { + const auto opcode = user->opcode(); + if (!spvOpcodeIsDebug(opcode) && + !spvOpcodeIsDecoration(opcode)) { + return true; + } + return false; + }); + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } else if (inst->opcode() == spv::Op::OpNop) { + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } + } + } + }); + + // Phase 2: process the instructions in the work list until all of the work is + // done. This time we add all users to the work list because phase 1 + // has already finished. + for (size_t i = 0; i < work_list.size(); ++i) { + Instruction* inst = work_list[i]; + in_work_list.erase(inst); + inst_seen.insert(inst); + + bool is_foldable_copy = + inst->opcode() == spv::Op::OpCopyObject && + context()->get_decoration_mgr()->HaveSubsetOfDecorations( + inst->result_id(), inst->GetSingleWordInOperand(0)); + + if (is_foldable_copy || folder.FoldInstruction(inst)) { + modified = true; + context()->AnalyzeUses(inst); + get_def_use_mgr()->ForEachUser( + inst, [&work_list, &in_work_list](Instruction* use) { + if (!use->IsDecoration() && use->opcode() != spv::Op::OpName && + in_work_list.insert(use).second) { + work_list.push_back(use); + } + }); + + AddNewOperands(inst, &inst_seen, &work_list); + + if (inst->opcode() == spv::Op::OpCopyObject) { + context()->ReplaceAllUsesWithPredicate( + inst->result_id(), inst->GetSingleWordInOperand(0), + [](Instruction* user) { + const auto opcode = user->opcode(); + if (!spvOpcodeIsDebug(opcode) && !spvOpcodeIsDecoration(opcode)) { + return true; + } + return false; + }); + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } else if (inst->opcode() == spv::Op::OpNop) { + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } + } + } + + // Phase 3: Kill instructions we know are no longer needed. + for (Instruction* inst : inst_to_kill) { + context()->KillInst(inst); + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.h new file mode 100644 index 00000000..149874b0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/simplification_pass.h @@ -0,0 +1,58 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SIMPLIFICATION_PASS_H_ +#define SOURCE_OPT_SIMPLIFICATION_PASS_H_ + +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SimplificationPass : public Pass { + public: + const char* name() const override { return "simplify-instructions"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Returns true if the module was changed. The simplifier is called on every + // instruction in |function| until nothing else in the function can be + // simplified. + bool SimplifyFunction(Function* function); + + // FactorAddMul can create |folded_inst| Mul of new Add. If Mul, push any Add + // operand not in |seen_inst| into |worklist|. This is heavily restricted to + // improve compile time but can be expanded for future simplifications which + // simiarly create new operations. + void AddNewOperands(Instruction* folded_inst, + std::unordered_set* inst_seen, + std::vector* work_list); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SIMPLIFICATION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp new file mode 100644 index 00000000..0af4dba6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp @@ -0,0 +1,648 @@ +// Copyright (c) 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/split_combined_image_sampler_pass.h" + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace opt { + +#define CHECK(cond) \ + { \ + if ((cond) != SPV_SUCCESS) return Pass::Status::Failure; \ + } + +#define CHECK_STATUS(cond) \ + { \ + if (auto c = (cond); c != SPV_SUCCESS) return c; \ + } + +IRContext::Analysis SplitCombinedImageSamplerPass::GetPreservedAnalyses() { + return + // def use manager is updated + IRContext::kAnalysisDefUse + + // decorations are updated + | IRContext::kAnalysisDecorations + + // control flow is not changed + | IRContext::kAnalysisCFG // + | IRContext::kAnalysisLoopAnalysis // + | IRContext::kAnalysisStructuredCFG + + // type manager is updated + | IRContext::kAnalysisTypes; +} + +Pass::Status SplitCombinedImageSamplerPass::Process() { + def_use_mgr_ = context()->get_def_use_mgr(); + type_mgr_ = context()->get_type_mgr(); + + FindCombinedTextureSamplers(); + if (combined_types_to_remove_.empty() && !sampled_image_used_as_param_) { + return Ok(); + } + + CHECK(RemapFunctions()); + CHECK(RemapVars()); + CHECK(RemoveDeadTypes()); + + def_use_mgr_ = nullptr; + type_mgr_ = nullptr; + + return Ok(); +} + +spvtools::DiagnosticStream SplitCombinedImageSamplerPass::Fail() { + return std::move( + spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY) + << "split-combined-image-sampler: "); +} + +void SplitCombinedImageSamplerPass::FindCombinedTextureSamplers() { + for (auto& inst : context()->types_values()) { + RegisterGlobal(inst.result_id()); + switch (inst.opcode()) { + case spv::Op::OpTypeSampler: + // Modules can't have duplicate sampler types. + assert(!sampler_type_); + sampler_type_ = &inst; + break; + + case spv::Op::OpTypeSampledImage: + if (!first_sampled_image_type_) { + first_sampled_image_type_ = &inst; + } + combined_types_.insert(inst.result_id()); + def_use_mgr_->WhileEachUser(inst.result_id(), [&](Instruction* i) { + sampled_image_used_as_param_ |= + i->opcode() == spv::Op::OpTypeFunction; + return !sampled_image_used_as_param_; + }); + break; + + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: { + auto pointee_id = inst.GetSingleWordInOperand(0); + if (combined_types_.find(pointee_id) != combined_types_.end()) { + combined_types_.insert(inst.result_id()); + combined_types_to_remove_.push_back(inst.result_id()); + } + } break; + + case spv::Op::OpTypePointer: { + auto sc = + static_cast(inst.GetSingleWordInOperand(0)); + if (sc == spv::StorageClass::UniformConstant) { + auto pointee_id = inst.GetSingleWordInOperand(1); + if (combined_types_.find(pointee_id) != combined_types_.end()) { + combined_types_.insert(inst.result_id()); + combined_types_to_remove_.push_back(inst.result_id()); + } + } + } break; + + case spv::Op::OpVariable: + if (combined_types_.find(inst.type_id()) != combined_types_.end()) { + ordered_vars_.push_back(&inst); + } + break; + + default: + break; + } + } +} + +Instruction* SplitCombinedImageSamplerPass::GetSamplerType() { + if (!sampler_type_) { + analysis::Sampler s; + uint32_t sampler_type_id = type_mgr_->GetTypeInstruction(&s); + sampler_type_ = def_use_mgr_->GetDef(sampler_type_id); + assert(first_sampled_image_type_); + sampler_type_->InsertBefore(first_sampled_image_type_); + RegisterNewGlobal(sampler_type_->result_id()); + } + return sampler_type_; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapVars() { + for (Instruction* var : ordered_vars_) { + CHECK_STATUS(RemapVar(var)); + } + return SPV_SUCCESS; +} + +std::pair SplitCombinedImageSamplerPass::SplitType( + Instruction& combined_kind_type) { + if (auto where = type_remap_.find(combined_kind_type.result_id()); + where != type_remap_.end()) { + auto& type_remap = where->second; + return {type_remap.image_kind_type, type_remap.sampler_kind_type}; + } + + switch (combined_kind_type.opcode()) { + case spv::Op::OpTypeSampledImage: { + auto* image_type = + def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(0)); + auto* sampler_type = GetSamplerType(); + type_remap_[combined_kind_type.result_id()] = {&combined_kind_type, + image_type, sampler_type}; + return {image_type, sampler_type}; + break; + } + case spv::Op::OpTypePointer: { + auto sc = static_cast( + combined_kind_type.GetSingleWordInOperand(0)); + if (sc == spv::StorageClass::UniformConstant) { + auto* pointee = + def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(1)); + auto [image_pointee, sampler_pointee] = SplitType(*pointee); + // These would be null if the pointee is an image type or a sampler + // type. Don't decompose them. Currently this method does not check the + // assumption that it is being only called on combined types. So code + // this defensively. + if (image_pointee && sampler_pointee) { + auto* ptr_image = MakeUniformConstantPointer(image_pointee); + auto* ptr_sampler = MakeUniformConstantPointer(sampler_pointee); + type_remap_[combined_kind_type.result_id()] = { + &combined_kind_type, ptr_image, ptr_sampler}; + return {ptr_image, ptr_sampler}; + } + } + break; + } + case spv::Op::OpTypeArray: { + const auto* array_ty = + type_mgr_->GetType(combined_kind_type.result_id())->AsArray(); + assert(array_ty); + const auto* sampled_image_ty = array_ty->element_type()->AsSampledImage(); + assert(sampled_image_ty); + + const analysis::Type* image_ty = sampled_image_ty->image_type(); + assert(image_ty); + analysis::Array array_image_ty(image_ty, array_ty->length_info()); + const uint32_t array_image_ty_id = + type_mgr_->GetTypeInstruction(&array_image_ty); + auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id); + if (!IsKnownGlobal(array_image_ty_id)) { + array_image_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_image_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + + analysis::Array sampler_array_ty( + type_mgr_->GetType(GetSamplerType()->result_id()), + array_ty->length_info()); + const uint32_t array_sampler_ty_id = + type_mgr_->GetTypeInstruction(&sampler_array_ty); + auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id); + if (!IsKnownGlobal(array_sampler_ty_id)) { + array_sampler_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_sampler_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + return {array_image_ty_inst, array_sampler_ty_inst}; + } + case spv::Op::OpTypeRuntimeArray: { + // This is like the sized-array case, but there is no length parameter. + auto* array_ty = + type_mgr_->GetType(combined_kind_type.result_id())->AsRuntimeArray(); + assert(array_ty); + auto* sampled_image_ty = array_ty->element_type()->AsSampledImage(); + assert(sampled_image_ty); + + const analysis::Type* image_ty = sampled_image_ty->image_type(); + assert(image_ty); + analysis::RuntimeArray array_image_ty(image_ty); + const uint32_t array_image_ty_id = + type_mgr_->GetTypeInstruction(&array_image_ty); + auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id); + if (!IsKnownGlobal(array_image_ty_id)) { + array_image_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_image_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + + analysis::RuntimeArray sampler_array_ty( + type_mgr_->GetType(GetSamplerType()->result_id())); + const uint32_t array_sampler_ty_id = + type_mgr_->GetTypeInstruction(&sampler_array_ty); + auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id); + if (!IsKnownGlobal(array_sampler_ty_id)) { + array_sampler_ty_inst->InsertBefore(&combined_kind_type); + RegisterNewGlobal(array_sampler_ty_id); + // GetTypeInstruction also updated the def-use manager. + } + return {array_image_ty_inst, array_sampler_ty_inst}; + } + default: + break; + } + return {nullptr, nullptr}; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapVar( + Instruction* combined_var) { + InstructionBuilder builder(context(), combined_var, + IRContext::kAnalysisDefUse); + + // Create an image variable, and a sampler variable. + auto* combined_var_type = def_use_mgr_->GetDef(combined_var->type_id()); + auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type); + assert(ptr_image_ty); + assert(ptr_sampler_ty); + Instruction* sampler_var = builder.AddVariable( + ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant); + Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(), + SpvStorageClassUniformConstant); + modified_ = true; + return RemapUses(combined_var, image_var, sampler_var); +} + +spv_result_t SplitCombinedImageSamplerPass::RemapUses( + Instruction* combined, Instruction* image_part, Instruction* sampler_part) { + // The instructions to delete. + std::unordered_set dead_insts; + // The insertion point should be updated before using this builder. + // We needed *something* here. + InstructionBuilder builder(context(), combined, IRContext::kAnalysisDefUse); + + // This code must maintain the SPIR-V "Data rule" about sampled image values: + // > All OpSampledImage instructions, or instructions that load an image or + // > sampler reference, must be in the same block in which their Result + // > are consumed. + // + // When the code below inserts OpSampledImage instructions, it is always + // either: + // - in the same block as the previous OpSampledImage instruction it is + // replacing, or + // - in the same block as the instruction using sampled image value it is + // replacing. + // + // Assuming that rule is already honoured by the module, these updates will + // continue to honour the rule. + + // Represents a single use of a value to be remapped. + struct RemapUse { + uint32_t used_id; // The ID that is being used. + Instruction* user; + uint32_t index; + Instruction* image_part; // The image part of the replacement. + Instruction* sampler_part; // The sampler part of the replacement. + }; + // The work list of uses to be remapped. + std::vector uses; + + // Adds remap records for each use of a value to be remapped. + // Also schedules the original value for deletion. + auto add_remap = [this, &dead_insts, &uses](Instruction* combined_arg, + Instruction* image_part_arg, + Instruction* sampler_part_arg) { + const uint32_t used_combined_id = combined_arg->result_id(); + + def_use_mgr_->ForEachUse( + combined_arg, [&](Instruction* user, uint32_t use_index) { + uses.push_back({used_combined_id, user, use_index, image_part_arg, + sampler_part_arg}); + }); + dead_insts.insert(combined_arg); + }; + + add_remap(combined, image_part, sampler_part); + + // Use index-based iteration because we can add to the work list as we go + // along, and reallocation would invalidate ordinary iterators. + for (size_t use_index = 0; use_index < uses.size(); ++use_index) { + auto& use = uses[use_index]; + switch (use.user->opcode()) { + case spv::Op::OpCopyObject: { + // Append the uses of this OpCopyObject to the work list. + add_remap(use.user, image_part, sampler_part); + break; + } + case spv::Op::OpLoad: { + assert(use.index == 2 && "variable used as non-pointer index on load"); + Instruction* load = use.user; + + // Assume the loaded value is a sampled image. + assert(def_use_mgr_->GetDef(load->type_id())->opcode() == + spv::Op::OpTypeSampledImage); + + // Create loads for the image part and sampler part. + builder.SetInsertPoint(load); + auto* image = builder.AddLoad(PointeeTypeId(use.image_part), + use.image_part->result_id()); + auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part), + use.sampler_part->result_id()); + + // Move decorations, such as RelaxedPrecision. + auto* deco_mgr = context()->get_decoration_mgr(); + deco_mgr->CloneDecorations(load->result_id(), image->result_id()); + deco_mgr->CloneDecorations(load->result_id(), sampler->result_id()); + deco_mgr->RemoveDecorationsFrom(load->result_id()); + + // Create a sampled image from the loads of the two parts. + auto* sampled_image = builder.AddSampledImage( + load->type_id(), image->result_id(), sampler->result_id()); + // Replace the original sampled image value with the new one. + std::unordered_set users; + def_use_mgr_->ForEachUse( + load, [&users, sampled_image](Instruction* user, uint32_t index) { + user->SetOperand(index, {sampled_image->result_id()}); + users.insert(user); + }); + for (auto* user : users) { + def_use_mgr_->AnalyzeInstUse(user); + } + dead_insts.insert(load); + break; + } + case spv::Op::OpDecorate: { + assert(use.index == 0 && "variable used as non-target index"); + builder.SetInsertPoint(use.user); + spv::Decoration deco{use.user->GetSingleWordInOperand(1)}; + std::vector literals; + for (uint32_t i = 2; i < use.user->NumInOperands(); i++) { + literals.push_back(use.user->GetSingleWordInOperand(i)); + } + builder.AddDecoration(use.image_part->result_id(), deco, literals); + builder.AddDecoration(use.sampler_part->result_id(), deco, literals); + // KillInst will delete names and decorations, so don't schedule a + // deletion of this instruction. + break; + } + case spv::Op::OpEntryPoint: { + // The entry point lists variables in the shader interface, i.e. + // module-scope variables referenced by the static call tree rooted + // at the entry point. (It can be a proper superset). Before SPIR-V + // 1.4, only Input and Output variables are listed; in 1.4 and later, + // module-scope variables in all storage classes are listed. + // If a combined image+sampler is listed by the entry point, then + // the separated image and sampler variables should be. + assert(use.index >= 3 && + "variable used in OpEntryPoint but not as an interface ID"); + use.user->SetOperand(use.index, {use.image_part->result_id()}); + use.user->InsertOperand( + use.user->NumOperands(), + {SPV_OPERAND_TYPE_ID, {use.sampler_part->result_id()}}); + def_use_mgr_->AnalyzeInstUse(use.user); + break; + } + case spv::Op::OpName: { + // Synthesize new names from the old. + const auto name = use.user->GetOperand(1).AsString(); + AddOpName(use.image_part->result_id(), name + "_image"); + AddOpName(use.sampler_part->result_id(), name + "_sampler"); + + // KillInst will delete names and decorations, so don't schedule a + // deletion of this instruction. + break; + } + case spv::Op::OpFunctionCall: { + // Replace each combined arg with two args: the image part, then the + // sampler part. + // The combined value could have been used twice in the argument list. + // Moving things around now will invalidate the 'use' list above. + // So don't trust the use index value. + auto& call = *use.user; + // The insert API only takes absolute arg IDs, not "in" arg IDs. + const auto first_arg_operand_index = 3; // Skip the callee ID + for (uint32_t i = first_arg_operand_index; i < call.NumOperands(); + ++i) { + if (use.used_id == call.GetSingleWordOperand(i)) { + call.SetOperand(i, {use.sampler_part->result_id()}); + call.InsertOperand( + i, {SPV_OPERAND_TYPE_ID, {use.image_part->result_id()}}); + ++i; + } + } + def_use_mgr_->AnalyzeInstUse(&call); + break; + } + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + auto* original_access_chain = use.user; + builder.SetInsertPoint(original_access_chain); + // It can only be the base pointer + assert(use.index == 2); + + // Replace the original access chain with access chains for the image + // part and the sampler part. + std::vector indices; + for (uint32_t i = 3; i < original_access_chain->NumOperands(); i++) { + indices.push_back(original_access_chain->GetSingleWordOperand(i)); + } + + auto [result_image_part_ty, result_sampler_part_ty] = + SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id())); + auto* result_image_part = builder.AddOpcodeAccessChain( + use.user->opcode(), result_image_part_ty->result_id(), + use.image_part->result_id(), indices); + auto* result_sampler_part = builder.AddOpcodeAccessChain( + use.user->opcode(), result_sampler_part_ty->result_id(), + use.sampler_part->result_id(), indices); + + // Remap uses of the original access chain. + add_remap(original_access_chain, result_image_part, + result_sampler_part); + break; + } + default: { + uint32_t used_type_id = def_use_mgr_->GetDef(use.used_id)->type_id(); + auto* used_type = def_use_mgr_->GetDef(used_type_id); + if (used_type->opcode() == spv::Op::OpTypeSampledImage) { + // This value being used is a sampled image value. But it's + // being replaced, so recreate it here. + // Example: used by OpImage, OpImageSampleExplicitLod, etc. + builder.SetInsertPoint(use.user); + auto* sampled_image = + builder.AddSampledImage(used_type_id, use.image_part->result_id(), + use.sampler_part->result_id()); + use.user->SetOperand(use.index, {sampled_image->result_id()}); + def_use_mgr_->AnalyzeInstUse(use.user); + break; + } + return Fail() << "unhandled user: " << *use.user; + } + } + } + + for (auto* inst : dead_insts) { + KillInst(inst); + } + + return SPV_SUCCESS; +} + +spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() { + // Remap function types. A combined type can appear as a parameter, but not as + // the return type. + { + std::unordered_set dead_insts; + for (auto& inst : context()->types_values()) { + if (inst.opcode() != spv::Op::OpTypeFunction) { + continue; + } + analysis::Function* f_ty = + type_mgr_->GetType(inst.result_id())->AsFunction(); + std::vector new_params; + for (const auto* param_ty : f_ty->param_types()) { + const auto param_ty_id = type_mgr_->GetId(param_ty); + if (combined_types_.find(param_ty_id) != combined_types_.end()) { + auto* param_type = def_use_mgr_->GetDef(param_ty_id); + auto [image_type, sampler_type] = SplitType(*param_type); + assert(image_type); + assert(sampler_type); + // The image and sampler types must already exist, so there is no + // need to move them to the right spot. + new_params.push_back(type_mgr_->GetType(image_type->result_id())); + new_params.push_back(type_mgr_->GetType(sampler_type->result_id())); + } else { + new_params.push_back(param_ty); + } + } + if (new_params.size() != f_ty->param_types().size()) { + // Replace this type. + analysis::Function new_f_ty(f_ty->return_type(), new_params); + const uint32_t new_f_ty_id = type_mgr_->GetTypeInstruction(&new_f_ty); + std::unordered_set users; + def_use_mgr_->ForEachUse( + &inst, + [&users, new_f_ty_id](Instruction* user, uint32_t use_index) { + user->SetOperand(use_index, {new_f_ty_id}); + users.insert(user); + }); + for (auto* user : users) { + def_use_mgr_->AnalyzeInstUse(user); + } + dead_insts.insert(&inst); + } + } + for (auto* inst : dead_insts) { + KillInst(inst); + } + } + + // Rewite OpFunctionParameter in function definitions. + for (Function& fn : *context()->module()) { + // Rewrite the function parameters and record their replacements. + struct Replacement { + Instruction* combined; + Instruction* image; + Instruction* sampler; + }; + std::vector replacements; + + Function::RewriteParamFn rewriter = + [&](std::unique_ptr&& param, + std::back_insert_iterator& appender) { + if (combined_types_.count(param->type_id()) == 0) { + appender = std::move(param); + return; + } + + // Replace this parameter with two new parameters. + auto* combined_inst = param.release(); + auto* combined_type = def_use_mgr_->GetDef(combined_inst->type_id()); + auto [image_type, sampler_type] = SplitType(*combined_type); + auto image_param = MakeUnique( + context(), spv::Op::OpFunctionParameter, image_type->result_id(), + context()->TakeNextId(), Instruction::OperandList{}); + auto sampler_param = MakeUnique( + context(), spv::Op::OpFunctionParameter, + sampler_type->result_id(), context()->TakeNextId(), + Instruction::OperandList{}); + replacements.push_back( + {combined_inst, image_param.get(), sampler_param.get()}); + appender = std::move(image_param); + appender = std::move(sampler_param); + }; + fn.RewriteParams(rewriter); + + for (auto& r : replacements) { + modified_ = true; + def_use_mgr_->AnalyzeInstDefUse(r.image); + def_use_mgr_->AnalyzeInstDefUse(r.sampler); + CHECK_STATUS(RemapUses(r.combined, r.image, r.sampler)); + } + } + return SPV_SUCCESS; +} + +Instruction* SplitCombinedImageSamplerPass::MakeUniformConstantPointer( + Instruction* pointee) { + uint32_t ptr_id = type_mgr_->FindPointerToType( + pointee->result_id(), spv::StorageClass::UniformConstant); + auto* ptr = def_use_mgr_->GetDef(ptr_id); + if (!IsKnownGlobal(ptr_id)) { + // The pointer type was created at the end. Put it right after the + // pointee. + ptr->InsertBefore(pointee); + pointee->InsertBefore(ptr); + RegisterNewGlobal(ptr_id); + // FindPointerToType also updated the def-use manager. + } + return ptr; +} + +void SplitCombinedImageSamplerPass::AddOpName(uint32_t id, + const std::string& name) { + std::unique_ptr opname{new Instruction{ + context(), + spv::Op::OpName, + 0u, + 0u, + {{SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, + utils::MakeVector(name)}}}}; + + context()->AddDebug2Inst(std::move(opname)); +} + +spv_result_t SplitCombinedImageSamplerPass::RemoveDeadTypes() { + for (auto dead_type_id : combined_types_to_remove_) { + if (auto* ty = def_use_mgr_->GetDef(dead_type_id)) { + KillInst(ty); + } + } + return SPV_SUCCESS; +} + +void SplitCombinedImageSamplerPass::KillInst(Instruction* inst) { + // IRContext::KillInst will remove associated debug instructions and + // decorations. It will delete the object only if it is already in a list. + const bool was_in_list = inst->IsInAList(); + context()->KillInst(inst); + if (!was_in_list) { + // Avoid leaking + delete inst; + } + modified_ = true; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.h new file mode 100644 index 00000000..253f34ab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/split_combined_image_sampler_pass.h @@ -0,0 +1,169 @@ +// Copyright (c) 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ +#define LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ + +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Replaces each combined-image sampler variable with an image variable +// and a sampler variable. Similar for function parameters. +// +// Copy the descriptor set and binding number. Vulkan allows this, surprisingly. +class SplitCombinedImageSamplerPass : public Pass { + public: + virtual ~SplitCombinedImageSamplerPass() override = default; + const char* name() const override { return "split-combined-image-sampler"; } + IRContext::Analysis GetPreservedAnalyses() override; + Status Process() override; + + private: + // Records failure for the current module, and returns a stream + // that can be used to provide user error information to the message + // consumer. + spvtools::DiagnosticStream Fail(); + + // Find variables that contain combined texture-samplers, or arrays of them. + // Also populate known_globals_. + void FindCombinedTextureSamplers(); + + // Returns the sampler type. If it does not yet exist, then it is created + // and placed before the first sampled image type. + Instruction* GetSamplerType(); + + // Remaps function types and function declarations. Each + // pointer-to-sampled-image-type operand is replaced with a pair of + // pointer-to-image-type and pointer-to-sampler-type pair. + // Updates the def-use manager and type manager. + spv_result_t RemapFunctions(); + // Remap resource variables. + // Updates the def-use manager. + spv_result_t RemapVars(); + // Remap a single resource variable for combined var. + // Updates the def-use manager and the decorations manager. + spv_result_t RemapVar(Instruction* combined_var); + // Transitively remaps uses of the combined object with uses of the + // decomposed image and sampler parts. The combined object can be sampled + // image value, a pointer to one, an array of one, or a pointer to an array + // of one. The image and sampler parts have corresponding shapes. + // Updates the def-use manager and the decorations manager. + spv_result_t RemapUses(Instruction* combined, Instruction* image_part, + Instruction* sampler_part); + // Removes types that are no longer referenced. + spv_result_t RemoveDeadTypes(); + + // Returns the type instruction for a UniformConstant pointer to the given + // pointee type. If it does not yet exist, the new type instruction is created + // and placed immediately after the pointee type instruction. Updates def-use + // and type managers, and the set of known globals. + Instruction* MakeUniformConstantPointer(Instruction* pointee); + + // Returns the ID of the pointee type for a pointer value instruction. + uint32_t PointeeTypeId(Instruction* ptr_value) { + auto* ptr_ty = def_use_mgr_->GetDef(ptr_value->type_id()); + assert(ptr_ty->opcode() == spv::Op::OpTypePointer); + return ptr_ty->GetSingleWordInOperand(1); + } + + // Creates a new OpName instruction mapping the given name to the given + // string, and adds it to the module at the end of the OpName and OpMemberName + // section. + void AddOpName(uint32_t id, const std::string& name); + + // Cached from the IRContext. Valid while Process() is running. + analysis::DefUseManager* def_use_mgr_ = nullptr; + // Cached from the IRContext. Valid while Process() is running. + analysis::TypeManager* type_mgr_ = nullptr; + + // Did processing modify the module? + bool modified_ = false; + Pass::Status Ok() { + return modified_ ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; + } + + // The first OpTypeSampledImage instruction in the module, if one exists. + Instruction* first_sampled_image_type_ = nullptr; + // An OpTypeSampler instruction, if one existed already, or if we created one. + Instruction* sampler_type_ = nullptr; + + // The known types and module-scope values. + // We use this to know when a new such value was created. + std::unordered_set known_globals_; + bool IsKnownGlobal(uint32_t id) const { + return known_globals_.find(id) != known_globals_.end(); + } + void RegisterGlobal(uint32_t id) { known_globals_.insert(id); } + void RegisterNewGlobal(uint32_t id) { + modified_ = true; + RegisterGlobal(id); + } + + // Deletes an instruction and associated debug and decoration instructions. + // Updates the def-use manager. + void KillInst(Instruction* inst); + + // Combined types. The known combined sampled-image type, + // and recursively pointers or arrays of them. + std::unordered_set combined_types_; + // The pre-existing types this pass should remove: pointer to + // combined type, array of combined type, pointer to array of combined type. + std::vector combined_types_to_remove_; + // Is an OpTypeSampledImage used as a function parameter? Those should be + // transformed. + bool sampled_image_used_as_param_ = false; + + // Remaps a combined-kind type to corresponding sampler-kind and image-kind + // of type. + struct TypeRemapInfo { + // The instruction for the combined type, pointer to combined type, + // or point to array of combined type. + Instruction* combined_kind_type; + // The corresponding image type, with the same shape of indirection as the + // combined_kind_type. + Instruction* image_kind_type; + // The corresponding sampler type, with the same shape of indirection as the + // combined_kind_type. + Instruction* sampler_kind_type; + }; + // Maps the ID of a combined-image-sampler type kind to its corresponding + // split parts. + std::unordered_map type_remap_; + + // Returns the image-like and sampler-like types of the same indirection shape + // as the given combined-like type. If combined_kind_type is not a combined + // type or a pointer to one, or an array of one or a pointer to an array of + // one, then returns a pair of null pointer. Either both components are + // non-null, or both components are null. Updates the def-use manager and the + // type manager if new instructions are created. + std::pair SplitType( + Instruction& combined_kind_type); + + // The combined-image-sampler variables to be replaced. + std::vector ordered_vars_; +}; +} // namespace opt +} // namespace spvtools +#endif // LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.cpp new file mode 100644 index 00000000..e552ba5e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.cpp @@ -0,0 +1,300 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/spread_volatile_semantics.h" + +#include "source/opt/decoration_manager.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kOpDecorateInOperandBuiltinDecoration = 2u; +constexpr uint32_t kOpLoadInOperandMemoryOperands = 1u; +constexpr uint32_t kOpEntryPointInOperandEntryPoint = 1u; +constexpr uint32_t kOpEntryPointInOperandInterface = 3u; + +bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id, uint32_t built_in) { + return decoration_manager->FindDecoration( + var_id, uint32_t(spv::Decoration::BuiltIn), + [built_in](const Instruction& inst) { + return built_in == inst.GetSingleWordInOperand( + kOpDecorateInOperandBuiltinDecoration); + }); +} + +bool IsBuiltInForRayTracingVolatileSemantics(spv::BuiltIn built_in) { + switch (built_in) { + case spv::BuiltIn::SMIDNV: + case spv::BuiltIn::WarpIDNV: + case spv::BuiltIn::SubgroupSize: + case spv::BuiltIn::SubgroupLocalInvocationId: + case spv::BuiltIn::SubgroupEqMask: + case spv::BuiltIn::SubgroupGeMask: + case spv::BuiltIn::SubgroupGtMask: + case spv::BuiltIn::SubgroupLeMask: + case spv::BuiltIn::SubgroupLtMask: + return true; + default: + return false; + } +} + +bool HasBuiltinForRayTracingVolatileSemantics( + analysis::DecorationManager* decoration_manager, uint32_t var_id) { + return decoration_manager->FindDecoration( + var_id, uint32_t(spv::Decoration::BuiltIn), [](const Instruction& inst) { + spv::BuiltIn built_in = spv::BuiltIn( + inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration)); + return IsBuiltInForRayTracingVolatileSemantics(built_in); + }); +} + +bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id) { + return decoration_manager->HasDecoration(var_id, + uint32_t(spv::Decoration::Volatile)); +} + +} // namespace + +Pass::Status SpreadVolatileSemantics::Process() { + if (HasNoExecutionModel()) { + return Status::SuccessWithoutChange; + } + const bool is_vk_memory_model_enabled = + context()->get_feature_mgr()->HasCapability( + spv::Capability::VulkanMemoryModel); + CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled); + + // If VulkanMemoryModel capability is not enabled, we have to set Volatile + // decoration for interface variables instead of setting Volatile for load + // instructions. If an interface (or pointers to it) is used by two load + // instructions in two entry points and one must be volatile while another + // is not, we have to report an error for the conflict. + if (!is_vk_memory_model_enabled && + HasInterfaceInConflictOfVolatileSemantics()) { + return Status::Failure; + } + + return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled); +} + +Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled) { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + auto entry_function_ids = + EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id()); + if (entry_function_ids.empty()) { + continue; + } + + if (is_vk_memory_model_enabled) { + SetVolatileForLoadsInEntries(&var, entry_function_ids); + } else { + DecorateVarWithVolatile(&var); + } + status = Status::SuccessWithChange; + } + return status; +} + +bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + std::unordered_set funcs; + context()->CollectCallTreeFromRoots(entry_function_id, &funcs); + return !VisitLoadsOfPointersToVariableInEntries( + var_id, + [](Instruction* load) { + // If it has a load without volatile memory operand, finish traversal + // and return false. + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + return false; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + return (memory_operands & uint32_t(spv::MemoryAccessMask::Volatile)) != + 0; + }, + funcs); +} + +bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() { + for (Instruction& entry_point : get_module()->entry_points()) { + spv::ExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() && + !IsTargetForVolatileSemantics(var_id, execution_model) && + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id); + context()->EmitErrorMessage( + "Variable is a target for Volatile semantics for an entry point, " + "but it is not for another entry point", + inst); + return true; + } + } + } + return false; +} + +void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) { + var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id}; + return; + } + itr->second.insert(entry_function_id); +} + +void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled) { + for (Instruction& entry_point : get_module()->entry_points()) { + spv::ExecutionModel execution_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!IsTargetForVolatileSemantics(var_id, execution_model)) { + continue; + } + if (is_vk_memory_model_enabled || + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + MarkVolatileSemanticsForVariable(var_id, &entry_point); + } + } + } +} + +void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + uint32_t var_id = var->result_id(); + if (HasVolatileDecoration(decoration_manager, var_id)) { + return; + } + get_decoration_mgr()->AddDecoration( + spv::Op::OpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {uint32_t(spv::Decoration::Volatile)}}}); +} + +bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& function_ids) { + std::vector worklist({var_id}); + auto* def_use_mgr = context()->get_def_use_mgr(); + while (!worklist.empty()) { + uint32_t ptr_id = worklist.back(); + worklist.pop_back(); + bool finish_traversal = !def_use_mgr->WhileEachUser( + ptr_id, [this, &worklist, &ptr_id, handle_load, + &function_ids](Instruction* user) { + BasicBlock* block = context()->get_instr_block(user); + if (block == nullptr || + function_ids.find(block->GetParent()->result_id()) == + function_ids.end()) { + return true; + } + + if (user->opcode() == spv::Op::OpAccessChain || + user->opcode() == spv::Op::OpInBoundsAccessChain || + user->opcode() == spv::Op::OpPtrAccessChain || + user->opcode() == spv::Op::OpInBoundsPtrAccessChain || + user->opcode() == spv::Op::OpCopyObject) { + if (ptr_id == user->GetSingleWordInOperand(0)) + worklist.push_back(user->result_id()); + return true; + } + + if (user->opcode() != spv::Op::OpLoad) { + return true; + } + + return handle_load(user); + }); + if (finish_traversal) return false; + } + return true; +} + +void SpreadVolatileSemantics::SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids) { + // Set Volatile memory operand for all load instructions if they do not have + // it. + for (auto entry_id : entry_function_ids) { + std::unordered_set funcs; + context()->CollectCallTreeFromRoots(entry_id, &funcs); + VisitLoadsOfPointersToVariableInEntries( + var->result_id(), + [](Instruction* load) { + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + load->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS, + {uint32_t(spv::MemoryAccessMask::Volatile)}}); + return true; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + memory_operands |= uint32_t(spv::MemoryAccessMask::Volatile); + load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands}); + return true; + }, + funcs); + } +} + +bool SpreadVolatileSemantics::IsTargetForVolatileSemantics( + uint32_t var_id, spv::ExecutionModel execution_model) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + if (execution_model == spv::ExecutionModel::Fragment) { + return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) && + HasBuiltinDecoration(decoration_manager, var_id, + uint32_t(spv::BuiltIn::HelperInvocation)); + } + + if (execution_model == spv::ExecutionModel::IntersectionKHR || + execution_model == spv::ExecutionModel::IntersectionNV) { + if (HasBuiltinDecoration(decoration_manager, var_id, + uint32_t(spv::BuiltIn::RayTmaxKHR))) { + return true; + } + } + + switch (execution_model) { + case spv::ExecutionModel::RayGenerationKHR: + case spv::ExecutionModel::ClosestHitKHR: + case spv::ExecutionModel::MissKHR: + case spv::ExecutionModel::CallableKHR: + case spv::ExecutionModel::IntersectionKHR: + return HasBuiltinForRayTracingVolatileSemantics(decoration_manager, + var_id); + default: + return false; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.h b/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.h new file mode 100644 index 00000000..4cbb526f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/spread_volatile_semantics.h @@ -0,0 +1,117 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ +#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SpreadVolatileSemantics : public Pass { + public: + SpreadVolatileSemantics() {} + + const char* name() const override { return "spread-volatile-semantics"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | + IRContext::kAnalysisInstrToBlockMapping; + } + + private: + // Returns true if it does not have an execution model. Linkage shaders do not + // have an execution model. + bool HasNoExecutionModel() { + return get_module()->entry_points().empty() && + context()->get_feature_mgr()->HasCapability( + spv::Capability::Linkage); + } + + // Iterates interface variables and spreads the Volatile semantics if it has + // load instructions for the Volatile semantics. + Pass::Status SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled); + + // Returns whether |var_id| is the result id of a target builtin variable for + // the volatile semantics for |execution_model| based on the Vulkan spec + // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or + // VUID-StandaloneSpirv-VulkanMemoryModel-04679. + bool IsTargetForVolatileSemantics(uint32_t var_id, + spv::ExecutionModel execution_model); + + // Collects interface variables that need the volatile semantics. + // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is + // enabled. + void CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled); + + // Reports an error if an interface variable is used by two entry points and + // it needs the Volatile decoration for one but not for another. Returns true + // if the error must be reported. + bool HasInterfaceInConflictOfVolatileSemantics(); + + // Returns whether the variable whose result is |var_id| is used by a + // non-volatile load or a pointer to it is used by a non-volatile load in + // |entry_point| or not. + bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id, + Instruction* entry_point); + + // Visits load instructions of pointers to variable whose result id is + // |var_id| if the load instructions are in reachable functions from entry + // points. |handle_load| is a function to do some actions for the load + // instructions. Finishes the traversal and returns false if |handle_load| + // returns false for a load instruction. Otherwise, returns true after running + // |handle_load| for all the load instructions. + bool VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function& handle_load, + const std::unordered_set& function_ids); + + // Sets Memory Operands of OpLoad instructions that load |var| or pointers + // of |var| as Volatile if the function id of the OpLoad instruction is + // included in |entry_function_ids|. + void SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set& entry_function_ids); + + // Adds OpDecorate Volatile for |var| if it does not exist. + void DecorateVarWithVolatile(Instruction* var); + + // Returns a set of entry function ids to spread the volatile semantics for + // the variable with the result id |var_id|. + std::unordered_set EntryFunctionsToSpreadVolatileSemanticsForVar( + uint32_t var_id) { + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {}; + return itr->second; + } + + // Specifies that we have to spread the volatile semantics for the + // variable with the result id |var_id| for the entry point |entry_point|. + void MarkVolatileSemanticsForVariable(uint32_t var_id, + Instruction* entry_point); + + // Result ids of variables to entry function ids for the volatile semantics + // spread. + std::unordered_map> + var_ids_to_entry_fn_for_volatile_semantics_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.cpp new file mode 100644 index 00000000..3eb4ec3f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -0,0 +1,708 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements the SSA rewriting algorithm proposed in +// +// Simple and Efficient Construction of Static Single Assignment Form. +// Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013) +// In: Jhala R., De Bosschere K. (eds) +// Compiler Construction. CC 2013. +// Lecture Notes in Computer Science, vol 7791. +// Springer, Berlin, Heidelberg +// +// https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 +// +// In contrast to common eager algorithms based on dominance and dominance +// frontier information, this algorithm works backwards from load operations. +// +// When a target variable is loaded, it queries the variable's reaching +// definition. If the reaching definition is unknown at the current location, +// it searches backwards in the CFG, inserting Phi instructions at join points +// in the CFG along the way until it finds the desired store instruction. +// +// The algorithm avoids repeated lookups using memoization. +// +// For reducible CFGs, which are a superset of the structured CFGs in SPIRV, +// this algorithm is proven to produce minimal SSA. That is, it inserts the +// minimal number of Phi instructions required to ensure the SSA property, but +// some Phi instructions may be dead +// (https://en.wikipedia.org/wiki/Static_single_assignment_form). + +#include "source/opt/ssa_rewrite_pass.h" + +#include +#include + +#include "source/opcode.h" +#include "source/opt/cfg.h" +#include "source/opt/mem_pass.h" +#include "source/opt/types.h" + +// Debug logging (0: Off, 1-N: Verbosity level). Replace this with the +// implementation done for +// https://github.com/KhronosGroup/SPIRV-Tools/issues/1351 +// #define SSA_REWRITE_DEBUGGING_LEVEL 3 + +#ifdef SSA_REWRITE_DEBUGGING_LEVEL +#include +#else +#define SSA_REWRITE_DEBUGGING_LEVEL 0 +#endif + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kStoreValIdInIdx = 1; +constexpr uint32_t kVariableInitIdInIdx = 1; +} // namespace + +std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const { + std::ostringstream str; + str << "%" << result_id_ << " = Phi[%" << var_id_ << ", BB %" << bb_->id() + << "]("; + if (phi_args_.size() > 0) { + uint32_t arg_ix = 0; + for (uint32_t pred_label : cfg->preds(bb_->id())) { + uint32_t arg_id = phi_args_[arg_ix++]; + str << "[%" << arg_id << ", bb(%" << pred_label << ")] "; + } + } + str << ")"; + if (copy_of_ != 0) { + str << " [COPY OF " << copy_of_ << "]"; + } + str << ((is_complete_) ? " [COMPLETE]" : " [INCOMPLETE]"); + + return str.str(); +} + +SSARewriter::PhiCandidate& SSARewriter::CreatePhiCandidate(uint32_t var_id, + BasicBlock* bb) { + // TODO(1841): Handle id overflow. + uint32_t phi_result_id = pass_->context()->TakeNextId(); + auto result = phi_candidates_.emplace( + phi_result_id, PhiCandidate(var_id, phi_result_id, bb)); + PhiCandidate& phi_candidate = result.first->second; + return phi_candidate; +} + +void SSARewriter::ReplacePhiUsersWith(const PhiCandidate& phi_to_remove, + uint32_t repl_id) { + for (uint32_t user_id : phi_to_remove.users()) { + PhiCandidate* user_phi = GetPhiCandidate(user_id); + BasicBlock* bb = pass_->context()->get_instr_block(user_id); + if (user_phi) { + // If the user is a Phi candidate, replace all arguments that refer to + // |phi_to_remove.result_id()| with |repl_id|. + for (uint32_t& arg : user_phi->phi_args()) { + if (arg == phi_to_remove.result_id()) { + arg = repl_id; + } + } + } else if (bb->id() == user_id) { + // The phi candidate is the definition of the variable at basic block + // |bb|. We must change this to the replacement. + WriteVariable(phi_to_remove.var_id(), bb, repl_id); + } else { + // For regular loads, traverse the |load_replacement_| table looking for + // instances of |phi_to_remove|. + for (auto& it : load_replacement_) { + if (it.second == phi_to_remove.result_id()) { + it.second = repl_id; + } + } + } + } +} + +uint32_t SSARewriter::TryRemoveTrivialPhi(PhiCandidate* phi_candidate) { + uint32_t same_id = 0; + for (uint32_t arg_id : phi_candidate->phi_args()) { + if (arg_id == same_id || arg_id == phi_candidate->result_id()) { + // This is a self-reference operand or a reference to the same value ID. + continue; + } + if (same_id != 0) { + // This Phi candidate merges at least two values. Therefore, it is not + // trivial. + assert(phi_candidate->copy_of() == 0 && + "Phi candidate transitioning from copy to non-copy."); + return phi_candidate->result_id(); + } + same_id = arg_id; + } + + // The previous logic has determined that this Phi candidate |phi_candidate| + // is trivial. It is essentially the copy operation phi_candidate->phi_result + // = Phi(same, same, same, ...). Since it is not necessary, we can re-route + // all the users of |phi_candidate->phi_result| to all its users, and remove + // |phi_candidate|. + + // Mark the Phi candidate as a trivial copy of |same_id|, so it won't be + // generated. + phi_candidate->MarkCopyOf(same_id); + + assert(same_id != 0 && "Completed Phis cannot have %0 in their arguments"); + + // Since |phi_candidate| always produces |same_id|, replace all the users of + // |phi_candidate| with |same_id|. + ReplacePhiUsersWith(*phi_candidate, same_id); + + return same_id; +} + +uint32_t SSARewriter::AddPhiOperands(PhiCandidate* phi_candidate) { + assert(phi_candidate->phi_args().size() == 0 && + "Phi candidate already has arguments"); + + bool found_0_arg = false; + for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) { + BasicBlock* pred_bb = pass_->cfg()->block(pred); + + // If |pred_bb| is not sealed, use %0 to indicate that + // |phi_candidate| needs to be completed after the whole CFG has + // been processed. + // + // Note that we cannot call GetReachingDef() in these cases + // because this would generate an empty Phi candidate in + // |pred_bb|. When |pred_bb| is later processed, a new definition + // for |phi_candidate->var_id_| will be lost because + // |phi_candidate| will still be reached by the empty Phi. + // + // Consider: + // + // BB %23: + // %38 = Phi[%i](%int_0[%1], %39[%25]) + // + // ... + // + // BB %25: [Starts unsealed] + // %39 = Phi[%i]() + // %34 = ... + // OpStore %i %34 -> Currdef(%i) at %25 is %34 + // OpBranch %23 + // + // When we first create the Phi in %38, we add an operandless Phi in + // %39 to hold the unknown reaching def for %i. + // + // But then, when we go to complete %39 at the end. The reaching def + // for %i in %25's predecessor is %38 itself. So we miss the fact + // that %25 has a def for %i that should be used. + // + // By making the argument %0, we make |phi_candidate| incomplete, + // which will cause it to be completed after the whole CFG has + // been scanned. + uint32_t arg_id = IsBlockSealed(pred_bb) + ? GetReachingDef(phi_candidate->var_id(), pred_bb) + : 0; + phi_candidate->phi_args().push_back(arg_id); + + if (arg_id == 0) { + found_0_arg = true; + } else { + // If this argument is another Phi candidate, add |phi_candidate| to the + // list of users for the defining Phi. + PhiCandidate* defining_phi = GetPhiCandidate(arg_id); + if (defining_phi && defining_phi != phi_candidate) { + defining_phi->AddUser(phi_candidate->result_id()); + } + } + } + + // If we could not fill-in all the arguments of this Phi, mark it incomplete + // so it gets completed after the whole CFG has been processed. + if (found_0_arg) { + phi_candidate->MarkIncomplete(); + incomplete_phis_.push(phi_candidate); + return phi_candidate->result_id(); + } + + // Try to remove |phi_candidate|, if it's trivial. + uint32_t repl_id = TryRemoveTrivialPhi(phi_candidate); + if (repl_id == phi_candidate->result_id()) { + // |phi_candidate| is complete and not trivial. Add it to the + // list of Phi candidates to generate. + phi_candidate->MarkComplete(); + phis_to_generate_.push_back(phi_candidate); + } + + return repl_id; +} + +uint32_t SSARewriter::GetValueAtBlock(uint32_t var_id, BasicBlock* bb) { + assert(bb != nullptr); + const auto& bb_it = defs_at_block_.find(bb); + if (bb_it != defs_at_block_.end()) { + const auto& current_defs = bb_it->second; + const auto& var_it = current_defs.find(var_id); + if (var_it != current_defs.end()) { + return var_it->second; + } + } + return 0; +} + +uint32_t SSARewriter::GetReachingDef(uint32_t var_id, BasicBlock* bb) { + // If |var_id| has a definition in |bb|, return it. + uint32_t val_id = GetValueAtBlock(var_id, bb); + if (val_id != 0) return val_id; + + // Otherwise, look up the value for |var_id| in |bb|'s predecessors. + auto& predecessors = pass_->cfg()->preds(bb->id()); + if (predecessors.size() == 1) { + // If |bb| has exactly one predecessor, we look for |var_id|'s definition + // there. + val_id = GetReachingDef(var_id, pass_->cfg()->block(predecessors[0])); + } else if (predecessors.size() > 1) { + // If there is more than one predecessor, this is a join block which may + // require a Phi instruction. This will act as |var_id|'s current + // definition to break potential cycles. + PhiCandidate& phi_candidate = CreatePhiCandidate(var_id, bb); + + // Set the value for |bb| to avoid an infinite recursion. + WriteVariable(var_id, bb, phi_candidate.result_id()); + val_id = AddPhiOperands(&phi_candidate); + } + + // If we could not find a store for this variable in the path from the root + // of the CFG, the variable is not defined, so we use undef. + if (val_id == 0) { + val_id = pass_->GetUndefVal(var_id); + if (val_id == 0) { + return 0; + } + } + + WriteVariable(var_id, bb, val_id); + + return val_id; +} + +void SSARewriter::SealBlock(BasicBlock* bb) { + auto result = sealed_blocks_.insert(bb); + (void)result; + assert(result.second == true && + "Tried to seal the same basic block more than once."); +} + +void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) { + auto opcode = inst->opcode(); + assert((opcode == spv::Op::OpStore || opcode == spv::Op::OpVariable) && + "Expecting a store or a variable definition instruction."); + + uint32_t var_id = 0; + uint32_t val_id = 0; + if (opcode == spv::Op::OpStore) { + (void)pass_->GetPtr(inst, &var_id); + val_id = inst->GetSingleWordInOperand(kStoreValIdInIdx); + } else if (inst->NumInOperands() >= 2) { + var_id = inst->result_id(); + val_id = inst->GetSingleWordInOperand(kVariableInitIdInIdx); + } + if (pass_->IsTargetVar(var_id)) { + WriteVariable(var_id, bb, val_id); + pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable( + inst, var_id, val_id, inst); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': " + << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\n"; +#endif + } +} + +bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) { + // Get the pointer that we are using to load from. + uint32_t var_id = 0; + (void)pass_->GetPtr(inst, &var_id); + + // Get the immediate reaching definition for |var_id|. + // + // In the presence of variable pointers, the reaching definition may be + // another pointer. For example, the following fragment: + // + // %2 = OpVariable %_ptr_Input_float Input + // %11 = OpVariable %_ptr_Function__ptr_Input_float Function + // OpStore %11 %2 + // %12 = OpLoad %_ptr_Input_float %11 + // %13 = OpLoad %float %12 + // + // corresponds to the pseudo-code: + // + // layout(location = 0) in flat float *%2 + // float %13; + // float *%12; + // float **%11; + // *%11 = %2; + // %12 = *%11; + // %13 = *%12; + // + // which ultimately, should correspond to: + // + // %13 = *%2; + // + // During rewriting, the pointer %12 is found to be replaceable by %2 (i.e., + // load_replacement_[12] is 2). However, when processing the load + // %13 = *%12, the type of %12's reaching definition is another float + // pointer (%2), instead of a float value. + // + // When this happens, we need to continue looking up the reaching definition + // chain until we get to a float value or a non-target var (i.e. a variable + // that cannot be SSA replaced, like %2 in this case since it is a function + // argument). + analysis::DefUseManager* def_use_mgr = pass_->context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = pass_->context()->get_type_mgr(); + analysis::Type* load_type = type_mgr->GetType(inst->type_id()); + uint32_t val_id = 0; + bool found_reaching_def = false; + while (!found_reaching_def) { + if (!pass_->IsTargetVar(var_id)) { + // If the variable we are loading from is not an SSA target (globals, + // function parameters), do nothing. + return true; + } + + val_id = GetReachingDef(var_id, bb); + if (val_id == 0) { + return false; + } + + // If the reaching definition is a pointer type different than the type of + // the instruction we are analyzing, then it must be a reference to another + // pointer (otherwise, this would be invalid SPIRV). We continue + // de-referencing it by making |val_id| be |var_id|. + // + // NOTE: if there is no reaching definition instruction, it means |val_id| + // is an undef. + Instruction* reaching_def_inst = def_use_mgr->GetDef(val_id); + if (reaching_def_inst && + !type_mgr->GetType(reaching_def_inst->type_id())->IsSame(load_type)) { + var_id = val_id; + } else { + found_reaching_def = true; + } + } + + // Schedule a replacement for the result of this load instruction with + // |val_id|. After all the rewriting decisions are made, every use of + // this load will be replaced with |val_id|. + uint32_t load_id = inst->result_id(); + assert(load_replacement_.count(load_id) == 0); + load_replacement_[load_id] = val_id; + PhiCandidate* defining_phi = GetPhiCandidate(val_id); + if (defining_phi) { + defining_phi->AddUser(load_id); + } + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\tFound load: " + << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << " (replacement for %" << load_id << " is %" << val_id << ")\n"; +#endif + + return true; +} + +void SSARewriter::PrintPhiCandidates() const { + std::cerr << "\nPhi candidates:\n"; + for (const auto& phi_it : phi_candidates_) { + std::cerr << "\tBB %" << phi_it.second.bb()->id() << ": " + << phi_it.second.PrettyPrint(pass_->cfg()) << "\n"; + } + std::cerr << "\n"; +} + +void SSARewriter::PrintReplacementTable() const { + std::cerr << "\nLoad replacement table\n"; + for (const auto& it : load_replacement_) { + std::cerr << "\t%" << it.first << " -> %" << it.second << "\n"; + } + std::cerr << "\n"; +} + +bool SSARewriter::GenerateSSAReplacements(BasicBlock* bb) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "Generating SSA replacements for block: " << bb->id() << "\n"; + std::cerr << bb->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\n"; +#endif + + for (auto& inst : *bb) { + auto opcode = inst.opcode(); + if (opcode == spv::Op::OpStore || opcode == spv::Op::OpVariable) { + ProcessStore(&inst, bb); + } else if (inst.opcode() == spv::Op::OpLoad) { + if (!ProcessLoad(&inst, bb)) { + return false; + } + } + } + + // Seal |bb|. This means that all the stores in it have been scanned and + // it's ready to feed them into its successors. + SealBlock(bb); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + PrintPhiCandidates(); + PrintReplacementTable(); + std::cerr << "\n\n"; +#endif + return true; +} + +uint32_t SSARewriter::GetReplacement(std::pair repl) { + uint32_t val_id = repl.second; + auto it = load_replacement_.find(val_id); + while (it != load_replacement_.end()) { + val_id = it->second; + it = load_replacement_.find(val_id); + } + return val_id; +} + +uint32_t SSARewriter::GetPhiArgument(const PhiCandidate* phi_candidate, + uint32_t ix) { + assert(phi_candidate->IsReady() && + "Tried to get the final argument from an incomplete/trivial Phi"); + + uint32_t arg_id = phi_candidate->phi_args()[ix]; + while (arg_id != 0) { + PhiCandidate* phi_user = GetPhiCandidate(arg_id); + if (phi_user == nullptr || phi_user->IsReady()) { + // If the argument is not a Phi or it's a Phi candidate ready to be + // emitted, return it. + return arg_id; + } + arg_id = phi_user->copy_of(); + } + + assert(false && + "No Phi candidates in the copy-of chain are ready to be generated"); + + return 0; +} + +bool SSARewriter::ApplyReplacements() { + bool modified = false; + +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "\n\nApplying replacement decisions to IR\n\n"; + PrintPhiCandidates(); + PrintReplacementTable(); + std::cerr << "\n\n"; +#endif + + // Add Phi instructions from completed Phi candidates. + std::vector generated_phis; + for (const PhiCandidate* phi_candidate : phis_to_generate_) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg()) + << "\n"; +#endif + + assert(phi_candidate->is_complete() && + "Tried to instantiate a Phi instruction from an incomplete Phi " + "candidate"); + + auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()); + + // Build the vector of operands for the new OpPhi instruction. + uint32_t type_id = pass_->GetPointeeTypeId(local_var); + std::vector phi_operands; + uint32_t arg_ix = 0; + std::unordered_map already_seen; + for (uint32_t pred_label : pass_->cfg()->preds(phi_candidate->bb()->id())) { + uint32_t op_val_id = GetPhiArgument(phi_candidate, arg_ix++); + if (already_seen.count(pred_label) == 0) { + phi_operands.push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {op_val_id}}); + phi_operands.push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {pred_label}}); + already_seen[pred_label] = op_val_id; + } else { + // It is possible that there are two edges from the same parent block. + // Since the OpPhi can have only one entry for each parent, we have to + // make sure the two edges are consistent with each other. + assert(already_seen[pred_label] == op_val_id && + "Inconsistent value for duplicate edges."); + } + } + + // Generate a new OpPhi instruction and insert it in its basic + // block. + std::unique_ptr phi_inst( + new Instruction(pass_->context(), spv::Op::OpPhi, type_id, + phi_candidate->result_id(), phi_operands)); + generated_phis.push_back(phi_inst.get()); + pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst); + pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb()); + auto insert_it = phi_candidate->bb()->begin(); + insert_it = insert_it.InsertBefore(std::move(phi_inst)); + pass_->context()->get_decoration_mgr()->CloneDecorations( + phi_candidate->var_id(), phi_candidate->result_id(), + {spv::Decoration::RelaxedPrecision}); + + // Add DebugValue for the new OpPhi instruction. + insert_it->SetDebugScope(local_var->GetDebugScope()); + pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable( + &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(), + &*insert_it); + + modified = true; + } + + // Scan uses for all inserted Phi instructions. Do this separately from the + // registration of the Phi instruction itself to avoid trying to analyze + // uses of Phi instructions that have not been registered yet. + for (Instruction* phi_inst : generated_phis) { + pass_->get_def_use_mgr()->AnalyzeInstUse(&*phi_inst); + } + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\n\nReplacing the result of load instructions with the " + "corresponding SSA id\n\n"; +#endif + + // Apply replacements from the load replacement table. + for (auto& repl : load_replacement_) { + uint32_t load_id = repl.first; + uint32_t val_id = GetReplacement(repl); + Instruction* load_inst = + pass_->context()->get_def_use_mgr()->GetDef(load_id); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "\t" + << load_inst->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << " (%" << load_id << " -> %" << val_id << ")\n"; +#endif + + // Remove the load instruction and replace all the uses of this load's + // result with |val_id|. Kill any names or decorates using the load's + // result before replacing to prevent incorrect replacement in those + // instructions. + pass_->context()->KillNamesAndDecorates(load_id); + pass_->context()->ReplaceAllUsesWith(load_id, val_id); + pass_->context()->KillInst(load_inst); + modified = true; + } + + return modified; +} + +void SSARewriter::FinalizePhiCandidate(PhiCandidate* phi_candidate) { + assert(phi_candidate->phi_args().size() > 0 && + "Phi candidate should have arguments"); + + uint32_t ix = 0; + for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) { + BasicBlock* pred_bb = pass_->cfg()->block(pred); + uint32_t& arg_id = phi_candidate->phi_args()[ix++]; + if (arg_id == 0) { + // If |pred_bb| is still not sealed, it means it's unreachable. In this + // case, we just use Undef as an argument. + arg_id = IsBlockSealed(pred_bb) + ? GetReachingDef(phi_candidate->var_id(), pred_bb) + : pass_->GetUndefVal(phi_candidate->var_id()); + } + } + + // This candidate is now completed. + phi_candidate->MarkComplete(); + + // If |phi_candidate| is not trivial, add it to the list of Phis to + // generate. + if (TryRemoveTrivialPhi(phi_candidate) == phi_candidate->result_id()) { + // If we could not remove |phi_candidate|, it means that it is complete + // and not trivial. Add it to the list of Phis to generate. + assert(!phi_candidate->copy_of() && "A completed Phi cannot be trivial."); + phis_to_generate_.push_back(phi_candidate); + } +} + +void SSARewriter::FinalizePhiCandidates() { +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "Finalizing Phi candidates:\n\n"; + PrintPhiCandidates(); + std::cerr << "\n"; +#endif + + // Now, complete the collected candidates. + while (incomplete_phis_.size() > 0) { + PhiCandidate* phi_candidate = incomplete_phis_.front(); + incomplete_phis_.pop(); + FinalizePhiCandidate(phi_candidate); + } +} + +Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 0 + std::cerr << "Function before SSA rewrite:\n" + << fp->PrettyPrint(0) << "\n\n\n"; +#endif + + // Collect variables that can be converted into SSA IDs. + pass_->CollectTargetVars(fp); + + // Generate all the SSA replacements and Phi candidates. This will + // generate incomplete and trivial Phis. + bool succeeded = pass_->cfg()->WhileEachBlockInReversePostOrder( + fp->entry().get(), [this](BasicBlock* bb) { + if (!GenerateSSAReplacements(bb)) { + return false; + } + return true; + }); + + if (!succeeded) { + return Pass::Status::Failure; + } + + // Remove trivial Phis and add arguments to incomplete Phis. + FinalizePhiCandidates(); + + // Finally, apply all the replacements in the IR. + bool modified = ApplyReplacements(); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 0 + std::cerr << "\n\n\nFunction after SSA rewrite:\n" + << fp->PrettyPrint(0) << "\n"; +#endif + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +Pass::Status SSARewritePass::Process() { + Status status = Status::SuccessWithoutChange; + for (auto& fn : *get_module()) { + if (fn.IsDeclaration()) { + continue; + } + status = + CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn)); + // Kill DebugDeclares for target variables. + for (auto var_id : seen_target_vars_) { + context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + } + if (status == Status::Failure) { + break; + } + } + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.h new file mode 100644 index 00000000..2470f85f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/ssa_rewrite_pass.h @@ -0,0 +1,306 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SSA_REWRITE_PASS_H_ +#define SOURCE_OPT_SSA_REWRITE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +// Utility class for passes that need to rewrite a function into SSA. This +// converts load/store operations on function-local variables into SSA IDs, +// which allows them to be the target of optimizing transformations. +// +// Store and load operations to these variables are converted into +// operations on SSA IDs. Phi instructions are added when needed. See the +// SSA construction paper for algorithmic details +// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6) +class SSARewriter { + public: + SSARewriter(MemPass* pass) : pass_(pass) {} + + // Rewrites SSA-target variables in function |fp| into SSA. This is the + // entry point for the SSA rewrite algorithm. SSA-target variables are + // locally defined variables that meet the criteria set by IsSSATargetVar. + // + // Returns whether the function was modified or not, and whether or not the + // rewrite was successful. + Pass::Status RewriteFunctionIntoSSA(Function* fp); + + private: + class PhiCandidate { + public: + explicit PhiCandidate(uint32_t var, uint32_t result, BasicBlock* block) + : var_id_(var), + result_id_(result), + bb_(block), + phi_args_(), + copy_of_(0), + is_complete_(false), + users_() {} + + uint32_t var_id() const { return var_id_; } + uint32_t result_id() const { return result_id_; } + BasicBlock* bb() const { return bb_; } + std::vector& phi_args() { return phi_args_; } + const std::vector& phi_args() const { return phi_args_; } + uint32_t copy_of() const { return copy_of_; } + bool is_complete() const { return is_complete_; } + std::vector& users() { return users_; } + const std::vector& users() const { return users_; } + + // Marks this phi candidate as a trivial copy of |orig_id|. + void MarkCopyOf(uint32_t orig_id) { copy_of_ = orig_id; } + + // Marks this phi candidate as incomplete. + void MarkIncomplete() { is_complete_ = false; } + + // Marks this phi candidate as complete. + void MarkComplete() { is_complete_ = true; } + + // Returns true if this Phi candidate is ready to be emitted. + bool IsReady() const { return is_complete() && copy_of() == 0; } + + // Pretty prints this Phi candidate into a string and returns it. |cfg| is + // needed to lookup basic block predecessors. + std::string PrettyPrint(const CFG* cfg) const; + + // Registers |operand_id| as a user of this Phi candidate. + void AddUser(uint32_t operand_id) { users_.push_back(operand_id); } + + private: + // Variable ID that this Phi is merging. + uint32_t var_id_; + + // SSA ID generated by this Phi (i.e., this is the result ID of the eventual + // Phi instruction). + uint32_t result_id_; + + // Basic block to hold this Phi. + BasicBlock* bb_; + + // Vector of operands for every predecessor block of |bb|. This vector is + // organized so that the Ith slot contains the argument coming from the Ith + // predecessor of |bb|. + std::vector phi_args_; + + // If this Phi is a trivial copy of another Phi, this is the ID of the + // original. If this is 0, it means that this is not a trivial Phi. + uint32_t copy_of_; + + // False, if this Phi candidate has no arguments or at least one argument is + // %0. + bool is_complete_; + + // List of all users for this Phi instruction. Each element is the result ID + // of the load instruction replaced by this Phi, or the result ID of a Phi + // candidate that has this Phi in its list of operands. + std::vector users_; + }; + + // Type used to keep track of store operations in each basic block. + typedef std::unordered_map> + BlockDefsMap; + + // Generates all the SSA rewriting decisions for basic block |bb|. This + // populates the Phi candidate table (|phi_candidate_|) and the load + // replacement table (|load_replacement_). Returns true if successful. + bool GenerateSSAReplacements(BasicBlock* bb); + + // Seals block |bb|. Sealing a basic block means |bb| and all its + // predecessors of |bb| have been scanned for loads/stores. + void SealBlock(BasicBlock* bb); + + // Returns true if |bb| has been sealed. + bool IsBlockSealed(BasicBlock* bb) { return sealed_blocks_.count(bb) != 0; } + + // Returns the Phi candidate with result ID |id| if it exists in the table + // |phi_candidates_|. If no such Phi candidate exists, it returns nullptr. + PhiCandidate* GetPhiCandidate(uint32_t id) { + auto it = phi_candidates_.find(id); + return (it != phi_candidates_.end()) ? &it->second : nullptr; + } + + // Replaces all the users of Phi candidate |phi_cand| to be users of + // |repl_id|. + void ReplacePhiUsersWith(const PhiCandidate& phi_cand, uint32_t repl_id); + + // Returns the value ID that should replace the load ID in the given + // replacement pair |repl|. The replacement is a pair (|load_id|, |val_id|). + // If |val_id| is itself replaced by another value in the table, this function + // will look the replacement for |val_id| until it finds one that is not + // itself replaced. For instance, given: + // + // %34 = OpLoad %float %f1 + // OpStore %t %34 + // %36 = OpLoad %float %t + // + // Assume that %f1 is reached by a Phi candidate %42, the load + // replacement table will have the following entries: + // + // %34 -> %42 + // %36 -> %34 + // + // So, when looking for the replacement for %36, we should not use + // %34. Rather, we should use %42. To do this, the chain of + // replacements must be followed until we reach an element that has + // no replacement. + uint32_t GetReplacement(std::pair repl); + + // Returns the argument at index |ix| from |phi_candidate|. If argument |ix| + // comes from a trivial Phi, it follows the copy-of chain from that trivial + // Phi until it finds the original Phi candidate. + // + // This is only valid after all Phi candidates have been completed. It can + // only be called when generating the IR for these Phis. + uint32_t GetPhiArgument(const PhiCandidate* phi_candidate, uint32_t ix); + + // Applies all the SSA replacement decisions. This replaces loads/stores to + // SSA target variables with their corresponding SSA IDs, and inserts Phi + // instructions for them. + bool ApplyReplacements(); + + // Registers a definition for variable |var_id| in basic block |bb| with + // value |val_id|. + void WriteVariable(uint32_t var_id, BasicBlock* bb, uint32_t val_id) { + defs_at_block_[bb][var_id] = val_id; + if (auto* pc = GetPhiCandidate(val_id)) { + pc->AddUser(bb->id()); + } + } + + // Returns the value of |var_id| at |bb| if |defs_at_block_| contains it. + // Otherwise, returns 0. + uint32_t GetValueAtBlock(uint32_t var_id, BasicBlock* bb); + + // Processes the store operation |inst| in basic block |bb|. This extracts + // the variable ID being stored into, determines whether the variable is an + // SSA-target variable, and, if it is, it stores its value in the + // |defs_at_block_| map. + void ProcessStore(Instruction* inst, BasicBlock* bb); + + // Processes the load operation |inst| in basic block |bb|. This extracts + // the variable ID being stored into, determines whether the variable is an + // SSA-target variable, and, if it is, it reads its reaching definition by + // calling |GetReachingDef|. Returns true if successful. + bool ProcessLoad(Instruction* inst, BasicBlock* bb); + + // Reads the current definition for variable |var_id| in basic block |bb|. + // If |var_id| is not defined in block |bb| it walks up the predecessors of + // |bb|, creating new Phi candidates along the way, if needed. + // + // It returns the value for |var_id| from the RHS of the current reaching + // definition for |var_id|. + uint32_t GetReachingDef(uint32_t var_id, BasicBlock* bb); + + // Adds arguments to |phi_candidate| by getting the reaching definition of + // |phi_candidate|'s variable on each of the predecessors of its basic + // block. After populating the argument list, it determines whether all its + // arguments are the same. If so, it returns the ID of the argument that + // this Phi copies. + uint32_t AddPhiOperands(PhiCandidate* phi_candidate); + + // Creates a Phi candidate instruction for variable |var_id| in basic block + // |bb|. + // + // Since the rewriting algorithm may remove Phi candidates when it finds + // them to be trivial, we avoid the expense of creating actual Phi + // instructions by keeping a pool of Phi candidates (|phi_candidates_|) + // during rewriting. + // + // Once the candidate Phi is created, it returns its ID. + PhiCandidate& CreatePhiCandidate(uint32_t var_id, BasicBlock* bb); + + // Attempts to remove a trivial Phi candidate |phi_cand|. Trivial Phis are + // those that only reference themselves and one other value |val| any number + // of times. This will try to remove any other Phis that become trivial + // after |phi_cand| is removed. + // + // If |phi_cand| is trivial, it returns the SSA ID for the value that should + // replace it. Otherwise, it returns the SSA ID for |phi_cand|. + uint32_t TryRemoveTrivialPhi(PhiCandidate* phi_cand); + + // Finalizes |phi_candidate| by replacing every argument that is still %0 + // with its reaching definition. + void FinalizePhiCandidate(PhiCandidate* phi_candidate); + + // Finalizes processing of Phi candidates. Once the whole function has been + // scanned for loads and stores, the CFG will still have some incomplete and + // trivial Phis. This will add missing arguments and remove trivial Phi + // candidates. + void FinalizePhiCandidates(); + + // Prints the table of Phi candidates to std::cerr. + void PrintPhiCandidates() const; + + // Prints the load replacement table to std::cerr. + void PrintReplacementTable() const; + + // Map holding the value of every SSA-target variable at every basic block + // where the variable is stored. defs_at_block_[block][var_id] = val_id + // means that there is a store or Phi instruction for variable |var_id| at + // basic block |block| with value |val_id|. + BlockDefsMap defs_at_block_; + + // Map, indexed by Phi ID, holding all the Phi candidates created during SSA + // rewriting. |phi_candidates_[id]| returns the Phi candidate whose result + // is |id|. + std::unordered_map phi_candidates_; + + // Queue of incomplete Phi candidates. These are Phi candidates created at + // unsealed blocks. They need to be completed before they are instantiated + // in ApplyReplacements. + std::queue incomplete_phis_; + + // List of completed Phi candidates. These are the only candidates that + // will become real Phi instructions. + std::vector phis_to_generate_; + + // SSA replacement table. This maps variable IDs, resulting from a load + // operation, to the value IDs that will replace them after SSA rewriting. + // After all the rewriting decisions are made, a final scan through the IR + // is done to replace all uses of the original load ID with the value ID. + std::unordered_map load_replacement_; + + // Set of blocks that have been sealed already. + std::unordered_set sealed_blocks_; + + // Memory pass requesting the SSA rewriter. + MemPass* pass_; +}; + +class SSARewritePass : public MemPass { + public: + SSARewritePass() = default; + + const char* name() const override { return "ssa-rewrite"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SSA_REWRITE_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.cpp new file mode 100644 index 00000000..16a7869e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.cpp @@ -0,0 +1,194 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strength_reduction_pass.h" + +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { +namespace { +// Count the number of trailing zeros in the binary representation of +// |constVal|. +uint32_t CountTrailingZeros(uint32_t constVal) { + // Faster if we use the hardware count trailing zeros instruction. + // If not available, we could create a table. + uint32_t shiftAmount = 0; + while ((constVal & 1) == 0) { + ++shiftAmount; + constVal = (constVal >> 1); + } + return shiftAmount; +} + +// Return true if |val| is a power of 2. +bool IsPowerOf2(uint32_t val) { + // The idea is that the & will clear out the least + // significant 1 bit. If it is a power of 2, then + // there is exactly 1 bit set, and the value becomes 0. + if (val == 0) return false; + return ((val - 1) & val) == 0; +} + +} // namespace + +Pass::Status StrengthReductionPass::Process() { + // Initialize the member variables on a per module basis. + bool modified = false; + int32_type_id_ = 0; + uint32_type_id_ = 0; + std::memset(constant_ids_, 0, sizeof(constant_ids_)); + + FindIntTypesAndConstants(); + modified = ScanFunctions(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool StrengthReductionPass::ReplaceMultiplyByPowerOf2( + BasicBlock::iterator* inst) { + assert((*inst)->opcode() == spv::Op::OpIMul && + "Only works for multiplication of integers."); + bool modified = false; + + // Currently only works on 32-bit integers. + if ((*inst)->type_id() != int32_type_id_ && + (*inst)->type_id() != uint32_type_id_) { + return modified; + } + + // Check the operands for a constant that is a power of 2. + for (int i = 0; i < 2; i++) { + uint32_t opId = (*inst)->GetSingleWordInOperand(i); + Instruction* opInst = get_def_use_mgr()->GetDef(opId); + if (opInst->opcode() == spv::Op::OpConstant) { + // We found a constant operand. + uint32_t constVal = opInst->GetSingleWordOperand(2); + + if (IsPowerOf2(constVal)) { + modified = true; + uint32_t shiftAmount = CountTrailingZeros(constVal); + uint32_t shiftConstResultId = GetConstantId(shiftAmount); + + // Create the new instruction. + uint32_t newResultId = TakeNextId(); + std::vector newOperands; + newOperands.push_back((*inst)->GetInOperand(1 - i)); + Operand shiftOperand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {shiftConstResultId}); + newOperands.push_back(shiftOperand); + std::unique_ptr newInstruction( + new Instruction(context(), spv::Op::OpShiftLeftLogical, + (*inst)->type_id(), newResultId, newOperands)); + + // Insert the new instruction and update the data structures. + (*inst) = (*inst).InsertBefore(std::move(newInstruction)); + get_def_use_mgr()->AnalyzeInstDefUse(&*(*inst)); + ++(*inst); + context()->ReplaceAllUsesWith((*inst)->result_id(), newResultId); + + // Remove the old instruction. + Instruction* inst_to_delete = &*(*inst); + --(*inst); + context()->KillInst(inst_to_delete); + + // We do not want to replace the instruction twice if both operands + // are constants that are a power of 2. So we break here. + break; + } + } + } + + return modified; +} + +void StrengthReductionPass::FindIntTypesAndConstants() { + analysis::Integer int32(32, true); + int32_type_id_ = context()->get_type_mgr()->GetId(&int32); + analysis::Integer uint32(32, false); + uint32_type_id_ = context()->get_type_mgr()->GetId(&uint32); + for (auto iter = get_module()->types_values_begin(); + iter != get_module()->types_values_end(); ++iter) { + switch (iter->opcode()) { + case spv::Op::OpConstant: + if (iter->type_id() == uint32_type_id_) { + uint32_t value = iter->GetSingleWordOperand(2); + if (value <= 32) constant_ids_[value] = iter->result_id(); + } + break; + default: + break; + } + } +} + +uint32_t StrengthReductionPass::GetConstantId(uint32_t val) { + assert(val <= 32 && + "This function does not handle constants larger than 32."); + + if (constant_ids_[val] == 0) { + if (uint32_type_id_ == 0) { + analysis::Integer uint(32, false); + uint32_type_id_ = context()->get_type_mgr()->GetTypeInstruction(&uint); + } + + // Construct the constant. + uint32_t resultId = TakeNextId(); + Operand constant(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {val}); + std::unique_ptr newConstant(new Instruction( + context(), spv::Op::OpConstant, uint32_type_id_, resultId, {constant})); + get_module()->AddGlobalValue(std::move(newConstant)); + + // Notify the DefUseManager about this constant. + auto constantIter = --get_module()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDef(&*constantIter); + + // Store the result id for next time. + constant_ids_[val] = resultId; + } + + return constant_ids_[val]; +} + +bool StrengthReductionPass::ScanFunctions() { + // I did not use |ForEachInst| in the module because the function that acts on + // the instruction gets a pointer to the instruction. We cannot use that to + // insert a new instruction. I want an iterator. + bool modified = false; + for (auto& func : *get_module()) { + for (auto& bb : func) { + for (auto inst = bb.begin(); inst != bb.end(); ++inst) { + switch (inst->opcode()) { + case spv::Op::OpIMul: + if (ReplaceMultiplyByPowerOf2(&inst)) modified = true; + break; + default: + break; + } + } + } + } + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.h new file mode 100644 index 00000000..1cbbbcc6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strength_reduction_pass.h @@ -0,0 +1,65 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ +#define SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StrengthReductionPass : public Pass { + public: + const char* name() const override { return "strength-reduction"; } + Status Process() override; + + private: + // Replaces multiple by power of 2 with an equivalent bit shift. + // Returns true if something changed. + bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); + + // Scan the types and constants in the module looking for the integer + // types that we are + // interested in. The shift operation needs a small unsigned integer. We + // need to find + // them or create them. We do not want duplicates. + void FindIntTypesAndConstants(); + + // Get the id for the given constant. If it does not exist, it will be + // created. The parameter must be between 0 and 32 inclusive. + uint32_t GetConstantId(uint32_t); + + // Replaces certain instructions in function bodies with presumably cheaper + // ones. Returns true if something changed. + bool ScanFunctions(); + + // Type ids for the types of interest, or 0 if they do not exist. + uint32_t int32_type_id_; + uint32_t uint32_type_id_; + + // constant_ids[i] is the id for unsigned integer constant i. + // We set the limit at 32 because a bit shift of a 32-bit integer does not + // need a value larger than 32. + uint32_t constant_ids_[33]; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.cpp new file mode 100644 index 00000000..118d8465 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_debug_info_pass.h" +#include "source/opt/ir_context.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripDebugInfoPass::Process() { + bool uses_non_semantic_info = false; + for (auto& inst : context()->module()->extensions()) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { + uses_non_semantic_info = true; + } + } + + std::vector to_kill; + + // if we use non-semantic info, it may reference OpString. Do a more + // expensive pass checking the uses of the OpString to see if any are + // OpExtInst on a non-semantic instruction set. If we're not using the + // extension then we can do a simpler pass and kill all debug1 instructions + if (uses_non_semantic_info) { + for (auto& inst : context()->module()->debugs1()) { + switch (inst.opcode()) { + case spv::Op::OpString: { + analysis::DefUseManager* def_use = context()->get_def_use_mgr(); + + // see if this string is used anywhere by a non-semantic instruction + bool no_nonsemantic_use = + def_use->WhileEachUser(&inst, [def_use](Instruction* use) { + if (spvIsExtendedInstruction(use->opcode())) { + auto ext_inst_set = + def_use->GetDef(use->GetSingleWordInOperand(0u)); + const std::string extension_name = + ext_inst_set->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, + "NonSemantic.")) { + // found a non-semantic use, return false as we cannot + // remove this OpString + return false; + } + } + + // other instructions can't be a non-semantic use + return true; + }); + + if (no_nonsemantic_use) to_kill.push_back(&inst); + + break; + } + + default: + to_kill.push_back(&inst); + break; + } + } + } else { + for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg); + } + + for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg); + for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg); + for (auto& dbg : context()->ext_inst_debuginfo()) to_kill.push_back(&dbg); + + // OpName must come first, since they may refer to other debug instructions. + // If they are after the instructions that refer to, then they will be killed + // when that instruction is killed, which will lead to a double kill. + std::sort(to_kill.begin(), to_kill.end(), + [](Instruction* lhs, Instruction* rhs) -> bool { + if (lhs->opcode() == spv::Op::OpName && + rhs->opcode() != spv::Op::OpName) + return true; + return false; + }); + + bool modified = !to_kill.empty(); + + for (auto* inst : to_kill) context()->KillInst(inst); + + // clear OpLine information + context()->module()->ForEachInst([&modified](Instruction* inst) { + modified |= !inst->dbg_line_insts().empty(); + inst->dbg_line_insts().clear(); + }); + + if (!get_module()->trailing_dbg_line_info().empty()) { + modified = true; + get_module()->trailing_dbg_line_info().clear(); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.h new file mode 100644 index 00000000..47a2cd40 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_debug_info_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripDebugInfoPass : public Pass { + public: + const char* name() const override { return "strip-debug"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.cpp new file mode 100644 index 00000000..659849ef --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_nonsemantic_info_pass.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripNonSemanticInfoPass::Process() { + bool modified = false; + + std::vector to_remove; + + bool other_uses_for_decorate_string = false; + for (auto& inst : context()->module()->annotations()) { + switch (inst.opcode()) { + case spv::Op::OpDecorateStringGOOGLE: + if (spv::Decoration(inst.GetSingleWordInOperand(1)) == + spv::Decoration::HlslSemanticGOOGLE || + spv::Decoration(inst.GetSingleWordInOperand(1)) == + spv::Decoration::UserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case spv::Op::OpMemberDecorateStringGOOGLE: + if (spv::Decoration(inst.GetSingleWordInOperand(2)) == + spv::Decoration::HlslSemanticGOOGLE || + spv::Decoration(inst.GetSingleWordInOperand(2)) == + spv::Decoration::UserTypeGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case spv::Op::OpDecorateId: + if (spv::Decoration(inst.GetSingleWordInOperand(1)) == + spv::Decoration::HlslCounterBufferGOOGLE) { + to_remove.push_back(&inst); + } + break; + + default: + break; + } + } + + for (auto& inst : context()->module()->extensions()) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_GOOGLE_user_type") { + to_remove.push_back(&inst); + } else if (!other_uses_for_decorate_string && + ext_name == "SPV_GOOGLE_decorate_string") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_KHR_non_semantic_info") { + to_remove.push_back(&inst); + } + } + + // remove any extended inst imports that are non semantic + std::unordered_set non_semantic_sets; + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == spv::Op::OpExtInstImport && + "Expecting an import of an extension's instruction set."); + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { + non_semantic_sets.insert(inst.result_id()); + to_remove.push_back(&inst); + } + } + + // if we removed some non-semantic sets, then iterate over the instructions in + // the module to remove any OpExtInst that referenced those sets + if (!non_semantic_sets.empty()) { + context()->module()->ForEachInst( + [&non_semantic_sets, &to_remove](Instruction* inst) { + if (spvIsExtendedInstruction(inst->opcode())) { + if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != + non_semantic_sets.end()) { + to_remove.push_back(inst); + } + } + }, + true); + } + + for (auto* inst : to_remove) { + modified = true; + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.h new file mode 100644 index 00000000..ff4e2e1d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/strip_nonsemantic_info_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripNonSemanticInfoPass : public Pass { + public: + const char* name() const override { return "strip-nonsemantic"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.cpp new file mode 100644 index 00000000..290b4bf4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.cpp @@ -0,0 +1,249 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/struct_cfg_analysis.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kMergeNodeIndex = 0; +constexpr uint32_t kContinueNodeIndex = 1; +} // namespace + +StructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) { + // If this is not a shader, there are no merge instructions, and not + // structured CFG to analyze. + if (!context_->get_feature_mgr()->HasCapability(spv::Capability::Shader)) { + return; + } + + for (auto& func : *context_->module()) { + AddBlocksInFunction(&func); + } +} + +void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) { + if (func->begin() == func->end()) return; + + std::list order; + context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order); + + struct TraversalInfo { + ConstructInfo cinfo; + uint32_t merge_node; + uint32_t continue_node; + }; + + // Set up a stack to keep track of currently active constructs. + std::vector state; + state.emplace_back(); + state[0].cinfo.containing_construct = 0; + state[0].cinfo.containing_loop = 0; + state[0].cinfo.containing_switch = 0; + state[0].cinfo.in_continue = false; + state[0].merge_node = 0; + state[0].continue_node = 0; + + for (BasicBlock* block : order) { + if (context_->cfg()->IsPseudoEntryBlock(block) || + context_->cfg()->IsPseudoExitBlock(block)) { + continue; + } + + if (block->id() == state.back().merge_node) { + state.pop_back(); + } + + // This works because the structured order is designed to keep the blocks in + // the continue construct between the continue header and the merge node. + if (block->id() == state.back().continue_node) { + state.back().cinfo.in_continue = true; + } + + bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo)); + + if (Instruction* merge_inst = block->GetMergeInst()) { + TraversalInfo new_state; + new_state.merge_node = + merge_inst->GetSingleWordInOperand(kMergeNodeIndex); + new_state.cinfo.containing_construct = block->id(); + + if (merge_inst->opcode() == spv::Op::OpLoopMerge) { + new_state.cinfo.containing_loop = block->id(); + new_state.cinfo.containing_switch = 0; + new_state.continue_node = + merge_inst->GetSingleWordInOperand(kContinueNodeIndex); + if (block->id() == new_state.continue_node) { + new_state.cinfo.in_continue = true; + bb_to_construct_[block->id()].in_continue = true; + } else { + new_state.cinfo.in_continue = false; + } + } else { + new_state.cinfo.containing_loop = state.back().cinfo.containing_loop; + new_state.cinfo.in_continue = state.back().cinfo.in_continue; + new_state.continue_node = state.back().continue_node; + + if (merge_inst->NextNode()->opcode() == spv::Op::OpSwitch) { + new_state.cinfo.containing_switch = block->id(); + } else { + new_state.cinfo.containing_switch = + state.back().cinfo.containing_switch; + } + } + + state.emplace_back(new_state); + merge_blocks_.Set(new_state.merge_node); + } + } +} + +uint32_t StructuredCFGAnalysis::ContainingConstruct(Instruction* inst) { + uint32_t bb = context_->get_instr_block(inst)->id(); + return ContainingConstruct(bb); +} + +uint32_t StructuredCFGAnalysis::MergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingConstruct(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +uint32_t StructuredCFGAnalysis::NestingDepth(uint32_t bb_id) { + uint32_t result = 0; + + // Find the merge block of the current merge construct as long as the block is + // inside a merge construct, exiting one for each iteration. + for (uint32_t merge_block_id = MergeBlock(bb_id); merge_block_id != 0; + merge_block_id = MergeBlock(merge_block_id)) { + result++; + } + + return result; +} + +uint32_t StructuredCFGAnalysis::LoopMergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingLoop(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +uint32_t StructuredCFGAnalysis::LoopContinueBlock(uint32_t bb_id) { + uint32_t header_id = ContainingLoop(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kContinueNodeIndex); +} + +uint32_t StructuredCFGAnalysis::LoopNestingDepth(uint32_t bb_id) { + uint32_t result = 0; + + // Find the merge block of the current loop as long as the block is inside a + // loop, exiting a loop for each iteration. + for (uint32_t merge_block_id = LoopMergeBlock(bb_id); merge_block_id != 0; + merge_block_id = LoopMergeBlock(merge_block_id)) { + result++; + } + + return result; +} + +uint32_t StructuredCFGAnalysis::SwitchMergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingSwitch(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +bool StructuredCFGAnalysis::IsContinueBlock(uint32_t bb_id) { + assert(bb_id != 0); + return LoopContinueBlock(bb_id) == bb_id; +} + +bool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct( + uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return false; + } + return it->second.in_continue; +} + +bool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) { + while (bb_id != 0) { + if (IsInContainingLoopsContinueConstruct(bb_id)) { + return true; + } + bb_id = ContainingLoop(bb_id); + } + return false; +} + +bool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) { + return merge_blocks_.Get(bb_id); +} + +std::unordered_set +StructuredCFGAnalysis::FindFuncsCalledFromContinue() { + std::unordered_set called_from_continue; + std::queue funcs_to_process; + + // First collect the functions that are called directly from a continue + // construct. + for (Function& func : *context_->module()) { + for (auto& bb : func) { + if (IsInContainingLoopsContinueConstruct(bb.id())) { + for (const Instruction& inst : bb) { + if (inst.opcode() == spv::Op::OpFunctionCall) { + funcs_to_process.push(inst.GetSingleWordInOperand(0)); + } + } + } + } + } + + // Now collect all of the functions that are indirectly called as well. + while (!funcs_to_process.empty()) { + uint32_t func_id = funcs_to_process.front(); + funcs_to_process.pop(); + Function* func = context_->GetFunction(func_id); + if (called_from_continue.insert(func_id).second) { + context_->AddCalls(func, &funcs_to_process); + } + } + return called_from_continue; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.h b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.h new file mode 100644 index 00000000..9436b4fb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_cfg_analysis.h @@ -0,0 +1,160 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ +#define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ + +#include +#include + +#include "source/opt/function.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// An analysis that, for each basic block, finds the constructs in which it is +// contained, so we can easily get headers and merge nodes. +class StructuredCFGAnalysis { + public: + explicit StructuredCFGAnalysis(IRContext* ctx); + + // Returns the id of the header of the innermost merge construct + // that contains |bb_id|. Returns |0| if |bb_id| is not contained in any + // merge construct. + uint32_t ContainingConstruct(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_construct; + } + + // Returns the id of the header of the innermost merge construct + // that contains |inst|. Returns |0| if |inst| is not contained in any + // merge construct. + uint32_t ContainingConstruct(Instruction* inst); + + // Returns the id of the merge block of the innermost merge construct + // that contains |bb_id|. Returns |0| if |bb_id| is not contained in any + // merge construct. + uint32_t MergeBlock(uint32_t bb_id); + + // Returns the nesting depth of the given block, i.e. the number of merge + // constructs containing it. Headers and merge blocks are not considered part + // of the corresponding merge constructs. + uint32_t NestingDepth(uint32_t block_id); + + // Returns the id of the header of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t ContainingLoop(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_loop; + } + + // Returns the id of the merge block of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t LoopMergeBlock(uint32_t bb_id); + + // Returns the id of the continue block of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t LoopContinueBlock(uint32_t bb_id); + + // Returns the loop nesting depth of |bb_id| within its function, i.e. the + // number of loop constructs in which |bb_id| is contained. As per other + // functions in StructuredCFGAnalysis, a loop header is not regarded as being + // part of the loop that it heads, so that e.g. the nesting depth of an + // outer-most loop header is 0. + uint32_t LoopNestingDepth(uint32_t bb_id); + + // Returns the id of the header of the innermost switch construct + // that contains |bb_id| as long as there is no intervening loop. Returns |0| + // if no such construct exists. + uint32_t ContainingSwitch(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_switch; + } + // Returns the id of the merge block of the innermost switch construct + // that contains |bb_id| as long as there is no intervening loop. Return |0| + // if no such block exists. + uint32_t SwitchMergeBlock(uint32_t bb_id); + + // Returns true if |bb_id| is the continue block for a loop. + bool IsContinueBlock(uint32_t bb_id); + + // Returns true if |bb_id| is in the continue construct for its inner most + // containing loop. + bool IsInContainingLoopsContinueConstruct(uint32_t bb_id); + + // Returns true if |bb_id| is in the continue construct for any loop in its + // function. + bool IsInContinueConstruct(uint32_t bb_id); + + // Return true if |bb_id| is the merge block for a construct. + bool IsMergeBlock(uint32_t bb_id); + + // Returns the set of function ids that are called directly or indirectly from + // a continue construct. + std::unordered_set FindFuncsCalledFromContinue(); + + private: + // Struct used to hold the information for a basic block. + // |containing_construct| is the header for the innermost containing + // construct, or 0 if no such construct exists. It could be a selection + // construct or a loop construct. + // + // |containing_loop| is the innermost containing loop construct, or 0 if the + // basic bloc is not in a loop. If the basic block is in a selection + // construct that is contained in a loop construct, then these two values will + // not be the same. + // + // |containing_switch| is the innermost contain selection construct with an + // |OpSwitch| for the branch, as long as there is not intervening loop. This + // is used to identify the selection construct from which it can break. + // + // |in_continue| is true of the block is in the continue construct for its + // innermost containing loop. + struct ConstructInfo { + uint32_t containing_construct; + uint32_t containing_loop; + uint32_t containing_switch; + bool in_continue; + }; + + // Populates |bb_to_construct_| with the innermost containing merge and loop + // constructs for each basic block in |func|. + void AddBlocksInFunction(Function* func); + + IRContext* context_; + + // A map from a basic block to the headers of its inner most containing + // constructs. + std::unordered_map bb_to_construct_; + utils::BitVector merge_blocks_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.cpp new file mode 100644 index 00000000..3bf2b2ab --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.cpp @@ -0,0 +1,482 @@ +// Copyright (c) 2024 Epic Games, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "struct_packing_pass.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +/* +Std140 packing rules from the original GLSL 140 specification (see +https://registry.khronos.org/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt) + +When using the "std140" storage layout, structures will be laid out in +buffer storage with its members stored in monotonically increasing order +based on their location in the declaration. A structure and each +structure member have a base offset and a base alignment, from which an +aligned offset is computed by rounding the base offset up to a multiple of +the base alignment. The base offset of the first member of a structure is +taken from the aligned offset of the structure itself. The base offset of +all other structure members is derived by taking the offset of the last +basic machine unit consumed by the previous member and adding one. Each +structure member is stored in memory at its aligned offset. The members +of a top-level uniform block are laid out in buffer storage by treating +the uniform block as a structure with a base offset of zero. + +(1) If the member is a scalar consuming basic machine units, the + base alignment is . + +(2) If the member is a two- or four-component vector with components + consuming basic machine units, the base alignment is 2 or + 4, respectively. + +(3) If the member is a three-component vector with components consuming + basic machine units, the base alignment is 4. + +(4) If the member is an array of scalars or vectors, the base alignment + and array stride are set to match the base alignment of a single + array element, according to rules (1), (2), and (3), and rounded up + to the base alignment of a vec4. The array may have padding at the + end; the base offset of the member following the array is rounded up + to the next multiple of the base alignment. + +(5) If the member is a column-major matrix with columns and + rows, the matrix is stored identically to an array of column + vectors with components each, according to rule (4). + +(6) If the member is an array of column-major matrices with + columns and rows, the matrix is stored identically to a row of + * column vectors with components each, according to rule + (4). + +(7) If the member is a row-major matrix with columns and rows, + the matrix is stored identically to an array of row vectors + with components each, according to rule (4). + +(8) If the member is an array of row-major matrices with columns + and rows, the matrix is stored identically to a row of * + row vectors with components each, according to rule (4). + +(9) If the member is a structure, the base alignment of the structure is + , where is the largest base alignment value of any of its + members, and rounded up to the base alignment of a vec4. The + individual members of this sub-structure are then assigned offsets + by applying this set of rules recursively, where the base offset of + the first member of the sub-structure is equal to the aligned offset + of the structure. The structure may have padding at the end; the + base offset of the member following the sub-structure is rounded up + to the next multiple of the base alignment of the structure. + +(10) If the member is an array of structures, the elements of + the array are laid out in order, according to rule (9). +*/ + +static bool isPackingVec4Padded(StructPackingPass::PackingRules rules) { + switch (rules) { + case StructPackingPass::PackingRules::Std140: + case StructPackingPass::PackingRules::Std140EnhancedLayout: + case StructPackingPass::PackingRules::HlslCbuffer: + case StructPackingPass::PackingRules::HlslCbufferPackOffset: + return true; + default: + return false; + } +} + +static bool isPackingScalar(StructPackingPass::PackingRules rules) { + switch (rules) { + case StructPackingPass::PackingRules::Scalar: + case StructPackingPass::PackingRules::ScalarEnhancedLayout: + return true; + default: + return false; + } +} + +static bool isPackingHlsl(StructPackingPass::PackingRules rules) { + switch (rules) { + case StructPackingPass::PackingRules::HlslCbuffer: + case StructPackingPass::PackingRules::HlslCbufferPackOffset: + return true; + default: + return false; + } +} + +static uint32_t getPackedBaseSize(const analysis::Type& type) { + switch (type.kind()) { + case analysis::Type::kBool: + return 1; + case analysis::Type::kInteger: + return type.AsInteger()->width() / 8; + case analysis::Type::kFloat: + return type.AsFloat()->width() / 8; + case analysis::Type::kVector: + return getPackedBaseSize(*type.AsVector()->element_type()); + case analysis::Type::kMatrix: + return getPackedBaseSize(*type.AsMatrix()->element_type()); + default: + break; // we only expect bool, int, float, vec, and mat here + } + assert(0 && "Unrecognized type to get base size"); + return 0; +} + +static uint32_t getScalarElementCount(const analysis::Type& type) { + switch (type.kind()) { + case analysis::Type::kVector: + return type.AsVector()->element_count(); + case analysis::Type::kMatrix: + return getScalarElementCount(*type.AsMatrix()->element_type()); + case analysis::Type::kStruct: + assert(0 && "getScalarElementCount() does not recognized struct types"); + return 0; + default: + return 1; + } +} + +// Aligns the specified value to a multiple of alignment, whereas the +// alignment must be a power-of-two. +static uint32_t alignPow2(uint32_t value, uint32_t alignment) { + return (value + alignment - 1) & ~(alignment - 1); +} + +void StructPackingPass::buildConstantsMap() { + constantsMap_.clear(); + for (Instruction* instr : context()->module()->GetConstants()) { + constantsMap_[instr->result_id()] = instr; + } +} + +uint32_t StructPackingPass::getPackedAlignment( + const analysis::Type& type) const { + switch (type.kind()) { + case analysis::Type::kArray: { + // Get alignment of base type and round up to minimum alignment + const uint32_t minAlignment = isPackingVec4Padded(packingRules_) ? 16 : 1; + return std::max( + minAlignment, getPackedAlignment(*type.AsArray()->element_type())); + } + case analysis::Type::kStruct: { + // Rule 9. Struct alignment is maximum alignmnet of its members + uint32_t alignment = 1; + + for (const analysis::Type* elementType : + type.AsStruct()->element_types()) { + alignment = + std::max(alignment, getPackedAlignment(*elementType)); + } + + if (isPackingVec4Padded(packingRules_)) + alignment = std::max(alignment, 16u); + + return alignment; + } + default: { + const uint32_t baseAlignment = getPackedBaseSize(type); + + // Scalar block layout always uses alignment for the most basic component + if (isPackingScalar(packingRules_)) return baseAlignment; + + if (const analysis::Matrix* matrixType = type.AsMatrix()) { + // Rule 5/7 + if (isPackingVec4Padded(packingRules_) || + matrixType->element_count() == 3) + return baseAlignment * 4; + else + return baseAlignment * matrixType->element_count(); + } else if (const analysis::Vector* vectorType = type.AsVector()) { + // Rule 1 + if (vectorType->element_count() == 1) return baseAlignment; + + // Rule 2 + if (vectorType->element_count() == 2 || + vectorType->element_count() == 4) + return baseAlignment * vectorType->element_count(); + + // Rule 3 + if (vectorType->element_count() == 3) return baseAlignment * 4; + } else { + // Rule 1 + return baseAlignment; + } + } + } + assert(0 && "Unrecognized type to get packed alignment"); + return 0; +} + +static uint32_t getPadAlignment(const analysis::Type& type, + uint32_t packedAlignment) { + // The next member following a struct member is aligned to the base alignment + // of a previous struct member. + return type.kind() == analysis::Type::kStruct ? packedAlignment : 1; +} + +uint32_t StructPackingPass::getPackedSize(const analysis::Type& type) const { + switch (type.kind()) { + case analysis::Type::kArray: { + if (const analysis::Array* arrayType = type.AsArray()) { + uint32_t size = + getPackedArrayStride(*arrayType) * getArrayLength(*arrayType); + + // For arrays of vector and matrices in HLSL, the last element has a + // size depending on its vector/matrix size to allow packing other + // vectors in the last element. + const analysis::Type* arraySubType = arrayType->element_type(); + if (isPackingHlsl(packingRules_) && + arraySubType->kind() != analysis::Type::kStruct) { + size -= (4 - getScalarElementCount(*arraySubType)) * + getPackedBaseSize(*arraySubType); + } + return size; + } + break; + } + case analysis::Type::kStruct: { + uint32_t size = 0; + uint32_t padAlignment = 1; + for (const analysis::Type* memberType : + type.AsStruct()->element_types()) { + const uint32_t packedAlignment = getPackedAlignment(*memberType); + const uint32_t alignment = + std::max(packedAlignment, padAlignment); + padAlignment = getPadAlignment(*memberType, packedAlignment); + size = alignPow2(size, alignment); + size += getPackedSize(*memberType); + } + return size; + } + default: { + const uint32_t baseAlignment = getPackedBaseSize(type); + if (isPackingScalar(packingRules_)) { + return getScalarElementCount(type) * baseAlignment; + } else { + uint32_t size = 0; + if (const analysis::Matrix* matrixType = type.AsMatrix()) { + const analysis::Vector* matrixSubType = + matrixType->element_type()->AsVector(); + assert(matrixSubType != nullptr && + "Matrix sub-type is expected to be a vector type"); + if (isPackingVec4Padded(packingRules_) || + matrixType->element_count() == 3) + size = matrixSubType->element_count() * baseAlignment * 4; + else + size = matrixSubType->element_count() * baseAlignment * + matrixType->element_count(); + + // For matrices in HLSL, the last element has a size depending on its + // vector size to allow packing other vectors in the last element. + if (isPackingHlsl(packingRules_)) { + size -= (4 - matrixSubType->element_count()) * + getPackedBaseSize(*matrixSubType); + } + } else if (const analysis::Vector* vectorType = type.AsVector()) { + size = vectorType->element_count() * baseAlignment; + } else { + size = baseAlignment; + } + return size; + } + } + } + assert(0 && "Unrecognized type to get packed size"); + return 0; +} + +uint32_t StructPackingPass::getPackedArrayStride( + const analysis::Array& arrayType) const { + // Array stride is equal to aligned size of element type + const uint32_t elementSize = getPackedSize(*arrayType.element_type()); + const uint32_t alignment = getPackedAlignment(arrayType); + return alignPow2(elementSize, alignment); +} + +uint32_t StructPackingPass::getArrayLength( + const analysis::Array& arrayType) const { + return getConstantInt(arrayType.LengthId()); +} + +uint32_t StructPackingPass::getConstantInt(spv::Id id) const { + auto it = constantsMap_.find(id); + assert(it != constantsMap_.end() && + "Failed to map SPIR-V instruction ID to constant value"); + [[maybe_unused]] const analysis::Type* constType = + context()->get_type_mgr()->GetType(it->second->type_id()); + assert(constType != nullptr && + "Failed to map SPIR-V instruction result type to definition"); + assert(constType->kind() == analysis::Type::kInteger && + "Failed to map SPIR-V instruction result type to integer type"); + return it->second->GetOperand(2).words[0]; +} + +StructPackingPass::PackingRules StructPackingPass::ParsePackingRuleFromString( + const std::string& s) { + if (s == "std140") return PackingRules::Std140; + if (s == "std140EnhancedLayout") return PackingRules::Std140EnhancedLayout; + if (s == "std430") return PackingRules::Std430; + if (s == "std430EnhancedLayout") return PackingRules::Std430EnhancedLayout; + if (s == "hlslCbuffer") return PackingRules::HlslCbuffer; + if (s == "hlslCbufferPackOffset") return PackingRules::HlslCbufferPackOffset; + if (s == "scalar") return PackingRules::Scalar; + if (s == "scalarEnhancedLayout") return PackingRules::ScalarEnhancedLayout; + return PackingRules::Undefined; +} + +StructPackingPass::StructPackingPass(const char* structToPack, + PackingRules rules) + : structToPack_{structToPack != nullptr ? structToPack : ""}, + packingRules_{rules} {} + +Pass::Status StructPackingPass::Process() { + if (packingRules_ == PackingRules::Undefined) { + if (consumer()) { + consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, + "Cannot pack struct with undefined rule"); + } + return Status::Failure; + } + + // Build Id-to-instruction map for easier access + buildConstantsMap(); + + // Find structure of interest + const uint32_t structIdToPack = findStructIdByName(structToPack_.c_str()); + + const Instruction* structDef = + context()->get_def_use_mgr()->GetDef(structIdToPack); + if (structDef == nullptr || structDef->opcode() != spv::Op::OpTypeStruct) { + if (consumer()) { + const std::string message = + "Failed to find struct with name " + structToPack_; + consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + } + return Status::Failure; + } + + // Find all struct member types + std::vector structMemberTypes = + findStructMemberTypes(*structDef); + + return assignStructMemberOffsets(structIdToPack, structMemberTypes); +} + +uint32_t StructPackingPass::findStructIdByName(const char* structName) const { + for (Instruction& instr : context()->module()->debugs2()) { + if (instr.opcode() == spv::Op::OpName && + instr.GetOperand(1).AsString() == structName) { + return instr.GetOperand(0).AsId(); + } + } + return 0; +} + +std::vector StructPackingPass::findStructMemberTypes( + const Instruction& structDef) const { + // Found struct type to pack, now collect all types of its members + assert(structDef.NumOperands() > 0 && + "Number of operands in OpTypeStruct instruction must not be zero"); + const uint32_t numMembers = structDef.NumOperands() - 1; + std::vector structMemberTypes; + structMemberTypes.resize(numMembers); + for (uint32_t i = 0; i < numMembers; ++i) { + const spv::Id memberTypeId = structDef.GetOperand(1 + i).AsId(); + if (const analysis::Type* memberType = + context()->get_type_mgr()->GetType(memberTypeId)) { + structMemberTypes[i] = memberType; + } + } + return structMemberTypes; +} + +Pass::Status StructPackingPass::assignStructMemberOffsets( + uint32_t structIdToPack, + const std::vector& structMemberTypes) { + // Returns true if the specified instruction is a OpMemberDecorate for the + // struct we're looking for with an offset decoration + auto isMemberOffsetDecoration = + [structIdToPack](const Instruction& instr) -> bool { + return instr.opcode() == spv::Op::OpMemberDecorate && + instr.GetOperand(0).AsId() == structIdToPack && + static_cast(instr.GetOperand(2).words[0]) == + spv::Decoration::Offset; + }; + + bool modified = false; + + // Find and re-assign all member offset decorations + for (auto it = context()->module()->annotation_begin(), + itEnd = context()->module()->annotation_end(); + it != itEnd; ++it) { + if (isMemberOffsetDecoration(*it)) { + // Found first member decoration with offset, we expect all other + // offsets right after the first one + uint32_t prevMemberIndex = 0; + uint32_t currentOffset = 0; + uint32_t padAlignment = 1; + do { + const uint32_t memberIndex = it->GetOperand(1).words[0]; + if (memberIndex < prevMemberIndex) { + // Failure: we expect all members to appear in consecutive order + return Status::Failure; + } + + // Apply alignment rules to current offset + const analysis::Type& memberType = *structMemberTypes[memberIndex]; + uint32_t packedAlignment = getPackedAlignment(memberType); + uint32_t packedSize = getPackedSize(memberType); + + if (isPackingHlsl(packingRules_)) { + // If a member crosses vec4 boundaries, alignment is size of vec4 + if (currentOffset / 16 != (currentOffset + packedSize - 1) / 16) + packedAlignment = std::max(packedAlignment, 16u); + } + + const uint32_t alignment = + std::max(packedAlignment, padAlignment); + currentOffset = alignPow2(currentOffset, alignment); + padAlignment = getPadAlignment(memberType, packedAlignment); + + // Override packed offset in instruction + if (it->GetOperand(3).words[0] < currentOffset) { + // Failure: packing resulted in higher offset for member than + // previously generated + return Status::Failure; + } + + it->GetOperand(3).words[0] = currentOffset; + modified = true; + + // Move to next member + ++it; + prevMemberIndex = memberIndex; + currentOffset += packedSize; + } while (it != itEnd && isMemberOffsetDecoration(*it)); + + // We're done with all decorations for the struct of interest + break; + } + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.h new file mode 100644 index 00000000..3f30f98a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/struct_packing_pass.h @@ -0,0 +1,81 @@ +// Copyright (c) 2024 Epic Games, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRUCT_PACKING_PASS_ +#define SOURCE_OPT_STRUCT_PACKING_PASS_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass re-assigns all field offsets under the specified packing rules. +class StructPackingPass final : public Pass { + public: + enum class PackingRules { + Undefined, + Std140, + Std140EnhancedLayout, + Std430, + Std430EnhancedLayout, + HlslCbuffer, + HlslCbufferPackOffset, + Scalar, + ScalarEnhancedLayout, + }; + + static PackingRules ParsePackingRuleFromString(const std::string& s); + + StructPackingPass(const char* structToPack, PackingRules rules); + const char* name() const override { return "struct-packing"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisConstants | + IRContext::kAnalysisDebugInfo | IRContext::kAnalysisLiveness; + } + + private: + void buildConstantsMap(); + uint32_t findStructIdByName(const char* structName) const; + std::vector findStructMemberTypes( + const Instruction& structDef) const; + Status assignStructMemberOffsets( + uint32_t structIdToPack, + const std::vector& structMemberTypes); + + uint32_t getPackedAlignment(const analysis::Type& type) const; + uint32_t getPackedSize(const analysis::Type& type) const; + uint32_t getPackedArrayStride(const analysis::Array& arrayType) const; + uint32_t getArrayLength(const analysis::Array& arrayType) const; + uint32_t getConstantInt(spv::Id id) const; + + private: + std::string structToPack_; + PackingRules packingRules_ = PackingRules::Undefined; + std::unordered_map constantsMap_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRUCT_PACKING_PASS_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.cpp new file mode 100644 index 00000000..f07c9175 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2023 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/switch_descriptorset_pass.h" + +#include "source/opt/ir_builder.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status SwitchDescriptorSetPass::Process() { + Status status = Status::SuccessWithoutChange; + auto* deco_mgr = context()->get_decoration_mgr(); + + for (Instruction& var : context()->types_values()) { + if (var.opcode() != spv::Op::OpVariable) { + continue; + } + auto decos = deco_mgr->GetDecorationsFor(var.result_id(), false); + for (const auto& deco : decos) { + spv::Decoration d = spv::Decoration(deco->GetSingleWordInOperand(1u)); + if (d == spv::Decoration::DescriptorSet && + deco->GetSingleWordInOperand(2u) == ds_from_) { + deco->SetInOperand(2u, {ds_to_}); + status = Status::SuccessWithChange; + break; + } + } + } + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.h new file mode 100644 index 00000000..2084e9cd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/switch_descriptorset_pass.h @@ -0,0 +1,52 @@ +// Copyright (c) 2023 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SwitchDescriptorSetPass : public Pass { + public: + SwitchDescriptorSetPass(uint32_t ds_from, uint32_t ds_to) + : ds_from_(ds_from), ds_to_(ds_to) {} + + const char* name() const override { return "switch-descriptorset"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + // this pass preserves everything except decorations + uint32_t mask = ((IRContext::kAnalysisEnd << 1) - 1); + mask &= ~static_cast(IRContext::kAnalysisDecorations); + return static_cast(mask); + } + + private: + uint32_t ds_from_; + uint32_t ds_to_; +}; + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/tree_iterator.h b/engine/engine/3rdlibs/spirv-tools/source/opt/tree_iterator.h new file mode 100644 index 00000000..05f42bc5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/tree_iterator.h @@ -0,0 +1,246 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_TREE_ITERATOR_H_ +#define SOURCE_OPT_TREE_ITERATOR_H_ + +#include +#include +#include + +namespace spvtools { +namespace opt { + +// Helper class to iterate over a tree in a depth first order. +// The class assumes the data structure is a tree, tree node type implements a +// forward iterator. +// At each step, the iterator holds the pointer to the current node and state of +// the walk. +// The state is recorded by stacking the iteration position of the node +// children. To move to the next node, the iterator: +// - Looks at the top of the stack; +// - Sets the node behind the iterator as the current node; +// - Increments the iterator if it has more children to visit, pops otherwise; +// - If the current node has children, the children iterator is pushed into the +// stack. +template +class TreeDFIterator { + static_assert(!std::is_pointer::value && + !std::is_reference::value, + "NodeTy should be a class"); + // Type alias to keep track of the const qualifier. + using NodeIterator = + typename std::conditional::value, + typename NodeTy::const_iterator, + typename NodeTy::iterator>::type; + + // Type alias to keep track of the const qualifier. + using NodePtr = NodeTy*; + + public: + // Standard iterator interface. + using reference = NodeTy&; + using value_type = NodeTy; + + explicit inline TreeDFIterator(NodePtr top_node) : current_(top_node) { + if (current_ && current_->begin() != current_->end()) + parent_iterators_.emplace(make_pair(current_, current_->begin())); + } + + // end() iterator. + inline TreeDFIterator() : TreeDFIterator(nullptr) {} + + bool operator==(const TreeDFIterator& x) const { + return current_ == x.current_; + } + + bool operator!=(const TreeDFIterator& x) const { return !(*this == x); } + + reference operator*() const { return *current_; } + + NodePtr operator->() const { return current_; } + + TreeDFIterator& operator++() { + MoveToNextNode(); + return *this; + } + + TreeDFIterator operator++(int) { + TreeDFIterator tmp = *this; + ++*this; + return tmp; + } + + private: + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void MoveToNextNode() { + if (!current_) return; + if (parent_iterators_.empty()) { + current_ = nullptr; + return; + } + std::pair& next_it = parent_iterators_.top(); + // Set the new node. + current_ = *next_it.second; + // Update the iterator for the next child. + ++next_it.second; + // If we finished with node, pop it. + if (next_it.first->end() == next_it.second) parent_iterators_.pop(); + // If our current node is not a leaf, store the iteration state for later. + if (current_->begin() != current_->end()) + parent_iterators_.emplace(make_pair(current_, current_->begin())); + } + + // The current node of the tree. + NodePtr current_; + // State of the tree walk: each pair contains the parent node (which has been + // already visited) and the iterator of the next children to visit. + // When all the children has been visited, we pop the entry, get the next + // child and push back the pair if the children iterator is not end(). + std::stack> parent_iterators_; +}; + +// Helper class to iterate over a tree in a depth first post-order. +// The class assumes the data structure is a tree, tree node type implements a +// forward iterator. +// At each step, the iterator holds the pointer to the current node and state of +// the walk. +// The state is recorded by stacking the iteration position of the node +// children. To move to the next node, the iterator: +// - Looks at the top of the stack; +// - If the children iterator has reach the end, then the node become the +// current one and we pop the stack; +// - Otherwise, we save the child and increment the iterator; +// - We walk the child sub-tree until we find a leaf, stacking all non-leaves +// states (pair of node pointer and child iterator) as we walk it. +template +class PostOrderTreeDFIterator { + static_assert(!std::is_pointer::value && + !std::is_reference::value, + "NodeTy should be a class"); + // Type alias to keep track of the const qualifier. + using NodeIterator = + typename std::conditional::value, + typename NodeTy::const_iterator, + typename NodeTy::iterator>::type; + + // Type alias to keep track of the const qualifier. + using NodePtr = NodeTy*; + + public: + // Standard iterator interface. + using reference = NodeTy&; + using value_type = NodeTy; + + static inline PostOrderTreeDFIterator begin(NodePtr top_node) { + return PostOrderTreeDFIterator(top_node); + } + + static inline PostOrderTreeDFIterator end(NodePtr sentinel_node) { + return PostOrderTreeDFIterator(sentinel_node, false); + } + + bool operator==(const PostOrderTreeDFIterator& x) const { + return current_ == x.current_; + } + + bool operator!=(const PostOrderTreeDFIterator& x) const { + return !(*this == x); + } + + reference operator*() const { return *current_; } + + NodePtr operator->() const { return current_; } + + PostOrderTreeDFIterator& operator++() { + MoveToNextNode(); + return *this; + } + + PostOrderTreeDFIterator operator++(int) { + PostOrderTreeDFIterator tmp = *this; + ++*this; + return tmp; + } + + private: + explicit inline PostOrderTreeDFIterator(NodePtr top_node) + : current_(top_node) { + if (current_) WalkToLeaf(); + } + + // Constructor for the "end()" iterator. + // |end_sentinel| is the value that acts as end value (can be null). The bool + // parameters is to distinguish from the start() Ctor. + inline PostOrderTreeDFIterator(NodePtr sentinel_node, bool) + : current_(sentinel_node) {} + + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void MoveToNextNode() { + if (!current_) return; + if (parent_iterators_.empty()) { + current_ = nullptr; + return; + } + std::pair& next_it = parent_iterators_.top(); + // If we visited all children, the current node is the top of the stack. + if (next_it.second == next_it.first->end()) { + // Set the new node. + current_ = next_it.first; + parent_iterators_.pop(); + return; + } + // We have more children to visit, set the current node to the first child + // and dive to leaf. + current_ = *next_it.second; + // Update the iterator for the next child (avoid unneeded pop). + ++next_it.second; + WalkToLeaf(); + } + + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void WalkToLeaf() { + while (current_->begin() != current_->end()) { + NodeIterator next = ++current_->begin(); + parent_iterators_.emplace(make_pair(current_, next)); + // Set the first child as the new node. + current_ = *current_->begin(); + } + } + + // The current node of the tree. + NodePtr current_; + // State of the tree walk: each pair contains the parent node and the iterator + // of the next children to visit. + // When all the children has been visited, we pop the first entry and the + // parent node become the current node. + std::stack> parent_iterators_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TREE_ITERATOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.cpp new file mode 100644 index 00000000..ca5c6257 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.cpp @@ -0,0 +1,772 @@ +// Copyright (c) 2023 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/trim_capabilities_pass.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/enum_set.h" +#include "source/enum_string_mapping.h" +#include "source/ext_inst.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" +#include "source/spirv_target_env.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +constexpr uint32_t kOpTypeFloatSizeIndex = 0; +constexpr uint32_t kOpTypePointerStorageClassIndex = 0; +constexpr uint32_t kTypeArrayTypeIndex = 0; +constexpr uint32_t kOpTypeScalarBitWidthIndex = 0; +constexpr uint32_t kTypePointerTypeIdInIndex = 1; +constexpr uint32_t kOpTypeIntSizeIndex = 0; +constexpr uint32_t kOpTypeImageDimIndex = 1; +constexpr uint32_t kOpTypeImageArrayedIndex = kOpTypeImageDimIndex + 2; +constexpr uint32_t kOpTypeImageMSIndex = kOpTypeImageArrayedIndex + 1; +constexpr uint32_t kOpTypeImageSampledIndex = kOpTypeImageMSIndex + 1; +constexpr uint32_t kOpTypeImageFormatIndex = kOpTypeImageSampledIndex + 1; +constexpr uint32_t kOpImageReadImageIndex = 0; +constexpr uint32_t kOpImageWriteImageIndex = 0; +constexpr uint32_t kOpImageSparseReadImageIndex = 0; +constexpr uint32_t kOpExtInstSetInIndex = 0; +constexpr uint32_t kOpExtInstInstructionInIndex = 1; +constexpr uint32_t kOpExtInstImportNameInIndex = 0; + +// DFS visit of the type defined by `instruction`. +// If `condition` is true, children of the current node are visited. +// If `condition` is false, the children of the current node are ignored. +template +static void DFSWhile(const Instruction* instruction, UnaryPredicate condition) { + std::stack instructions_to_visit; + std::unordered_set visited_instructions; + instructions_to_visit.push(instruction->result_id()); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + while (!instructions_to_visit.empty()) { + const Instruction* item = def_use_mgr->GetDef(instructions_to_visit.top()); + instructions_to_visit.pop(); + + // Forward references can be allowed, meaning we can have cycles + // between ID uses. Need to keep track of this. + if (visited_instructions.count(item->result_id())) continue; + visited_instructions.insert(item->result_id()); + + if (!condition(item)) { + continue; + } + + if (item->opcode() == spv::Op::OpTypePointer) { + instructions_to_visit.push( + item->GetSingleWordInOperand(kTypePointerTypeIdInIndex)); + continue; + } + + if (item->opcode() == spv::Op::OpTypeMatrix || + item->opcode() == spv::Op::OpTypeVector || + item->opcode() == spv::Op::OpTypeArray || + item->opcode() == spv::Op::OpTypeRuntimeArray) { + instructions_to_visit.push( + item->GetSingleWordInOperand(kTypeArrayTypeIndex)); + continue; + } + + if (item->opcode() == spv::Op::OpTypeStruct) { + item->ForEachInOperand([&instructions_to_visit](const uint32_t* op_id) { + instructions_to_visit.push(*op_id); + }); + continue; + } + } +} + +// Walks the type defined by `instruction` (OpType* only). +// Returns `true` if any call to `predicate` with the type/subtype returns true. +template +static bool AnyTypeOf(const Instruction* instruction, + UnaryPredicate predicate) { + assert(IsTypeInst(instruction->opcode()) && + "AnyTypeOf called with a non-type instruction."); + + bool found_one = false; + DFSWhile(instruction, [&found_one, predicate](const Instruction* node) { + if (found_one || predicate(node)) { + found_one = true; + return false; + } + + return true; + }); + return found_one; +} + +static bool is16bitType(const Instruction* instruction) { + if (instruction->opcode() != spv::Op::OpTypeInt && + instruction->opcode() != spv::Op::OpTypeFloat) { + return false; + } + + return instruction->GetSingleWordInOperand(kOpTypeScalarBitWidthIndex) == 16; +} + +static bool Has16BitCapability(const FeatureManager* feature_manager) { + const CapabilitySet& capabilities = feature_manager->GetCapabilities(); + return capabilities.contains(spv::Capability::Float16) || + capabilities.contains(spv::Capability::Int16); +} + +} // namespace + +// ============== Begin opcode handler implementations. ======================= +// +// Adding support for a new capability should only require adding a new handler, +// and updating the +// kSupportedCapabilities/kUntouchableCapabilities/kFordiddenCapabilities lists. +// +// Handler names follow the following convention: +// Handler__() + +static std::optional Handler_OpTypeFloat_Float16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeFloat && + "This handler only support OpTypeFloat opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeFloatSizeIndex); + return size == 16 ? std::optional(spv::Capability::Float16) : std::nullopt; +} + +static std::optional Handler_OpTypeFloat_Float64( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeFloat && + "This handler only support OpTypeFloat opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeFloatSizeIndex); + return size == 64 ? std::optional(spv::Capability::Float64) : std::nullopt; +} + +static std::optional +Handler_OpTypePointer_StorageInputOutput16(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has an Input/Output + // storage class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + return std::nullopt; + } + + return AnyTypeOf(instruction, is16bitType) + ? std::optional(spv::Capability::StorageInputOutput16) + : std::nullopt; +} + +static std::optional +Handler_OpTypePointer_StoragePushConstant16(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a PushConstant storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::PushConstant) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + return std::nullopt; + } + + return AnyTypeOf(instruction, is16bitType) + ? std::optional(spv::Capability::StoragePushConstant16) + : std::nullopt; +} + +static std::optional +Handler_OpTypePointer_StorageUniformBufferBlock16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a Uniform storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Uniform) { + return std::nullopt; + } + + if (!Has16BitCapability(instruction->context()->get_feature_mgr())) { + return std::nullopt; + } + + const auto* decoration_mgr = instruction->context()->get_decoration_mgr(); + const bool matchesCondition = + AnyTypeOf(instruction, [decoration_mgr](const Instruction* item) { + if (!decoration_mgr->HasDecoration(item->result_id(), + spv::Decoration::BufferBlock)) { + return false; + } + + return AnyTypeOf(item, is16bitType); + }); + + return matchesCondition + ? std::optional(spv::Capability::StorageUniformBufferBlock16) + : std::nullopt; +} + +static std::optional +Handler_OpTypePointer_StorageBuffer16BitAccess(const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // Requires StorageBuffer, ShaderRecordBufferKHR or PhysicalStorageBuffer + // storage classes. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::ShaderRecordBufferKHR && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return std::nullopt; + } + + const auto* decoration_mgr = instruction->context()->get_decoration_mgr(); + const bool matchesCondition = + AnyTypeOf(instruction, [decoration_mgr](const Instruction* item) { + if (!decoration_mgr->HasDecoration(item->result_id(), + spv::Decoration::Block)) { + return false; + } + + return AnyTypeOf(item, is16bitType); + }); + + return matchesCondition + ? std::optional(spv::Capability::StorageBuffer16BitAccess) + : std::nullopt; +} + +static std::optional Handler_OpTypePointer_StorageUniform16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypePointer && + "This handler only support OpTypePointer opcodes."); + + // This capability is only required if the variable has a Uniform storage + // class. + spv::StorageClass storage_class = spv::StorageClass( + instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex)); + if (storage_class != spv::StorageClass::Uniform) { + return std::nullopt; + } + + const auto* feature_manager = instruction->context()->get_feature_mgr(); + if (!Has16BitCapability(feature_manager)) { + return std::nullopt; + } + + const bool hasBufferBlockCapability = + feature_manager->GetCapabilities().contains( + spv::Capability::StorageUniformBufferBlock16); + const auto* decoration_mgr = instruction->context()->get_decoration_mgr(); + bool found16bitType = false; + + DFSWhile(instruction, [decoration_mgr, hasBufferBlockCapability, + &found16bitType](const Instruction* item) { + if (found16bitType) { + return false; + } + + if (hasBufferBlockCapability && + decoration_mgr->HasDecoration(item->result_id(), + spv::Decoration::BufferBlock)) { + return false; + } + + if (is16bitType(item)) { + found16bitType = true; + return false; + } + + return true; + }); + + return found16bitType ? std::optional(spv::Capability::StorageUniform16) + : std::nullopt; +} + +static std::optional Handler_OpTypeInt_Int16( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeInt && + "This handler only support OpTypeInt opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex); + return size == 16 ? std::optional(spv::Capability::Int16) : std::nullopt; +} + +static std::optional Handler_OpTypeInt_Int64( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeInt && + "This handler only support OpTypeInt opcodes."); + + const uint32_t size = + instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex); + return size == 64 ? std::optional(spv::Capability::Int64) : std::nullopt; +} + +static std::optional Handler_OpTypeImage_ImageMSArray( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpTypeImage && + "This handler only support OpTypeImage opcodes."); + + const uint32_t arrayed = + instruction->GetSingleWordInOperand(kOpTypeImageArrayedIndex); + const uint32_t ms = instruction->GetSingleWordInOperand(kOpTypeImageMSIndex); + const uint32_t sampled = + instruction->GetSingleWordInOperand(kOpTypeImageSampledIndex); + + return arrayed == 1 && sampled == 2 && ms == 1 + ? std::optional(spv::Capability::ImageMSArray) + : std::nullopt; +} + +static std::optional +Handler_OpImageRead_StorageImageReadWithoutFormat( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpImageRead && + "This handler only support OpImageRead opcodes."); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + const uint32_t image_index = + instruction->GetSingleWordInOperand(kOpImageReadImageIndex); + const uint32_t type_index = def_use_mgr->GetDef(image_index)->type_id(); + const Instruction* type = def_use_mgr->GetDef(type_index); + const uint32_t dim = type->GetSingleWordInOperand(kOpTypeImageDimIndex); + const uint32_t format = type->GetSingleWordInOperand(kOpTypeImageFormatIndex); + + // If the Image Format is Unknown and Dim is SubpassData, + // StorageImageReadWithoutFormat is required. + const bool is_unknown = spv::ImageFormat(format) == spv::ImageFormat::Unknown; + const bool requires_capability_for_unknown = + spv::Dim(dim) != spv::Dim::SubpassData; + return is_unknown && requires_capability_for_unknown + ? std::optional(spv::Capability::StorageImageReadWithoutFormat) + : std::nullopt; +} + +static std::optional +Handler_OpImageWrite_StorageImageWriteWithoutFormat( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpImageWrite && + "This handler only support OpImageWrite opcodes."); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + const uint32_t image_index = + instruction->GetSingleWordInOperand(kOpImageWriteImageIndex); + const uint32_t type_index = def_use_mgr->GetDef(image_index)->type_id(); + + // If the Image Format is Unknown, StorageImageWriteWithoutFormat is required. + const Instruction* type = def_use_mgr->GetDef(type_index); + const uint32_t format = type->GetSingleWordInOperand(kOpTypeImageFormatIndex); + const bool is_unknown = spv::ImageFormat(format) == spv::ImageFormat::Unknown; + return is_unknown + ? std::optional(spv::Capability::StorageImageWriteWithoutFormat) + : std::nullopt; +} + +static std::optional +Handler_OpImageSparseRead_StorageImageReadWithoutFormat( + const Instruction* instruction) { + assert(instruction->opcode() == spv::Op::OpImageSparseRead && + "This handler only support OpImageSparseRead opcodes."); + const auto* def_use_mgr = instruction->context()->get_def_use_mgr(); + + const uint32_t image_index = + instruction->GetSingleWordInOperand(kOpImageSparseReadImageIndex); + const uint32_t type_index = def_use_mgr->GetDef(image_index)->type_id(); + const Instruction* type = def_use_mgr->GetDef(type_index); + const uint32_t format = type->GetSingleWordInOperand(kOpTypeImageFormatIndex); + + return spv::ImageFormat(format) == spv::ImageFormat::Unknown + ? std::optional(spv::Capability::StorageImageReadWithoutFormat) + : std::nullopt; +} + +// Opcode of interest to determine capabilities requirements. +constexpr std::array, 14> kOpcodeHandlers{{ + // clang-format off + {spv::Op::OpImageRead, Handler_OpImageRead_StorageImageReadWithoutFormat}, + {spv::Op::OpImageWrite, Handler_OpImageWrite_StorageImageWriteWithoutFormat}, + {spv::Op::OpImageSparseRead, Handler_OpImageSparseRead_StorageImageReadWithoutFormat}, + {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float16 }, + {spv::Op::OpTypeFloat, Handler_OpTypeFloat_Float64 }, + {spv::Op::OpTypeImage, Handler_OpTypeImage_ImageMSArray}, + {spv::Op::OpTypeInt, Handler_OpTypeInt_Int16 }, + {spv::Op::OpTypeInt, Handler_OpTypeInt_Int64 }, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageInputOutput16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StoragePushConstant16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniformBufferBlock16}, + {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageBuffer16BitAccess}, + // clang-format on +}}; + +// ============== End opcode handler implementations. ======================= + +namespace { +ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities, + const AssemblyGrammar& grammar) { + ExtensionSet output; + const spv_operand_desc_t* desc = nullptr; + for (auto capability : capabilities) { + if (SPV_SUCCESS != grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast(capability), + &desc)) { + continue; + } + + for (uint32_t i = 0; i < desc->numExtensions; ++i) { + output.insert(desc->extensions[i]); + } + } + + return output; +} + +bool hasOpcodeConflictingCapabilities(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpBeginInvocationInterlockEXT: + case spv::Op::OpEndInvocationInterlockEXT: + case spv::Op::OpGroupNonUniformIAdd: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformIMul: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformSMin: + case spv::Op::OpGroupNonUniformUMin: + case spv::Op::OpGroupNonUniformFMin: + case spv::Op::OpGroupNonUniformSMax: + case spv::Op::OpGroupNonUniformUMax: + case spv::Op::OpGroupNonUniformFMax: + case spv::Op::OpGroupNonUniformBitwiseAnd: + case spv::Op::OpGroupNonUniformBitwiseOr: + case spv::Op::OpGroupNonUniformBitwiseXor: + case spv::Op::OpGroupNonUniformLogicalAnd: + case spv::Op::OpGroupNonUniformLogicalOr: + case spv::Op::OpGroupNonUniformLogicalXor: + return true; + default: + return false; + } +} + +} // namespace + +TrimCapabilitiesPass::TrimCapabilitiesPass() + : supportedCapabilities_( + TrimCapabilitiesPass::kSupportedCapabilities.cbegin(), + TrimCapabilitiesPass::kSupportedCapabilities.cend()), + forbiddenCapabilities_( + TrimCapabilitiesPass::kForbiddenCapabilities.cbegin(), + TrimCapabilitiesPass::kForbiddenCapabilities.cend()), + untouchableCapabilities_( + TrimCapabilitiesPass::kUntouchableCapabilities.cbegin(), + TrimCapabilitiesPass::kUntouchableCapabilities.cend()), + opcodeHandlers_(kOpcodeHandlers.cbegin(), kOpcodeHandlers.cend()) {} + +void TrimCapabilitiesPass::addInstructionRequirementsForOpcode( + spv::Op opcode, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + if (hasOpcodeConflictingCapabilities(opcode)) { + return; + } + + const spv_opcode_desc_t* desc = {}; + auto result = context()->grammar().lookupOpcode(opcode, &desc); + if (result != SPV_SUCCESS) { + return; + } + + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); +} + +void TrimCapabilitiesPass::addInstructionRequirementsForOperand( + const Operand& operand, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + // No supported capability relies on a 2+-word operand. + if (operand.words.size() != 1) { + return; + } + + // No supported capability relies on a literal string operand or an ID. + if (operand.type == SPV_OPERAND_TYPE_LITERAL_STRING || + operand.type == SPV_OPERAND_TYPE_ID || + operand.type == SPV_OPERAND_TYPE_RESULT_ID) { + return; + } + + // If the Vulkan memory model is declared and any instruction uses Device + // scope, the VulkanMemoryModelDeviceScope capability must be declared. This + // rule cannot be covered by the grammar, so must be checked explicitly. + if (operand.type == SPV_OPERAND_TYPE_SCOPE_ID) { + const Instruction* memory_model = context()->GetMemoryModel(); + if (memory_model && memory_model->GetSingleWordInOperand(1u) == + uint32_t(spv::MemoryModel::Vulkan)) { + capabilities->insert(spv::Capability::VulkanMemoryModelDeviceScope); + } + } + + // case 1: Operand is a single value, can directly lookup. + if (!spvOperandIsConcreteMask(operand.type)) { + const spv_operand_desc_t* desc = {}; + auto result = context()->grammar().lookupOperand(operand.type, + operand.words[0], &desc); + if (result != SPV_SUCCESS) { + return; + } + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); + return; + } + + // case 2: operand can be a bitmask, we need to decompose the lookup. + for (uint32_t i = 0; i < 32; i++) { + const uint32_t mask = (1 << i) & operand.words[0]; + if (!mask) { + continue; + } + + const spv_operand_desc_t* desc = {}; + auto result = context()->grammar().lookupOperand(operand.type, mask, &desc); + if (result != SPV_SUCCESS) { + continue; + } + + addSupportedCapabilitiesToSet(desc, capabilities); + addSupportedExtensionsToSet(desc, extensions); + } +} + +void TrimCapabilitiesPass::addInstructionRequirementsForExtInst( + Instruction* instruction, CapabilitySet* capabilities) const { + assert(instruction->opcode() == spv::Op::OpExtInst && + "addInstructionRequirementsForExtInst must be passed an OpExtInst " + "instruction"); + + const auto* def_use_mgr = context()->get_def_use_mgr(); + + const Instruction* extInstImport = def_use_mgr->GetDef( + instruction->GetSingleWordInOperand(kOpExtInstSetInIndex)); + uint32_t extInstruction = + instruction->GetSingleWordInOperand(kOpExtInstInstructionInIndex); + + const Operand& extInstSet = + extInstImport->GetInOperand(kOpExtInstImportNameInIndex); + + spv_ext_inst_type_t instructionSet = + spvExtInstImportTypeGet(extInstSet.AsString().c_str()); + + spv_ext_inst_desc desc = {}; + auto result = + context()->grammar().lookupExtInst(instructionSet, extInstruction, &desc); + if (result != SPV_SUCCESS) { + return; + } + + addSupportedCapabilitiesToSet(desc, capabilities); +} + +void TrimCapabilitiesPass::addInstructionRequirements( + Instruction* instruction, CapabilitySet* capabilities, + ExtensionSet* extensions) const { + // Ignoring OpCapability and OpExtension instructions. + if (instruction->opcode() == spv::Op::OpCapability || + instruction->opcode() == spv::Op::OpExtension) { + return; + } + + if (instruction->opcode() == spv::Op::OpExtInst) { + addInstructionRequirementsForExtInst(instruction, capabilities); + } else { + addInstructionRequirementsForOpcode(instruction->opcode(), capabilities, + extensions); + } + + // Second case: one of the opcode operand is gated by a capability. + const uint32_t operandCount = instruction->NumOperands(); + for (uint32_t i = 0; i < operandCount; i++) { + addInstructionRequirementsForOperand(instruction->GetOperand(i), + capabilities, extensions); + } + + // Last case: some complex logic needs to be run to determine capabilities. + auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode()); + for (auto it = begin; it != end; it++) { + const OpcodeHandler handler = it->second; + auto result = handler(instruction); + if (!result.has_value()) { + continue; + } + + capabilities->insert(*result); + } +} + +void TrimCapabilitiesPass::AddExtensionsForOperand( + const spv_operand_type_t type, const uint32_t value, + ExtensionSet* extensions) const { + const spv_operand_desc_t* desc = nullptr; + spv_result_t result = context()->grammar().lookupOperand(type, value, &desc); + if (result != SPV_SUCCESS) { + return; + } + addSupportedExtensionsToSet(desc, extensions); +} + +std::pair +TrimCapabilitiesPass::DetermineRequiredCapabilitiesAndExtensions() const { + CapabilitySet required_capabilities; + ExtensionSet required_extensions; + + get_module()->ForEachInst([&](Instruction* instruction) { + addInstructionRequirements(instruction, &required_capabilities, + &required_extensions); + }); + + for (auto capability : required_capabilities) { + AddExtensionsForOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast(capability), + &required_extensions); + } + +#if !defined(NDEBUG) + // Debug only. We check the outputted required capabilities against the + // supported capabilities list. The supported capabilities list is useful for + // API users to quickly determine if they can use the pass or not. But this + // list has to remain up-to-date with the pass code. If we can detect a + // capability as required, but it's not listed, it means the list is + // out-of-sync. This method is not ideal, but should cover most cases. + { + for (auto capability : required_capabilities) { + assert(supportedCapabilities_.contains(capability) && + "Module is using a capability that is not listed as supported."); + } + } +#endif + + return std::make_pair(std::move(required_capabilities), + std::move(required_extensions)); +} + +Pass::Status TrimCapabilitiesPass::TrimUnrequiredCapabilities( + const CapabilitySet& required_capabilities) const { + const FeatureManager* feature_manager = context()->get_feature_mgr(); + CapabilitySet capabilities_to_trim; + for (auto capability : feature_manager->GetCapabilities()) { + // Some capabilities cannot be safely removed. Leaving them untouched. + if (untouchableCapabilities_.contains(capability)) { + continue; + } + + // If the capability is unsupported, don't trim it. + if (!supportedCapabilities_.contains(capability)) { + continue; + } + + if (required_capabilities.contains(capability)) { + continue; + } + + capabilities_to_trim.insert(capability); + } + + for (auto capability : capabilities_to_trim) { + context()->RemoveCapability(capability); + } + + return capabilities_to_trim.size() == 0 ? Pass::Status::SuccessWithoutChange + : Pass::Status::SuccessWithChange; +} + +Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions( + const ExtensionSet& required_extensions) const { + const auto supported_extensions = + getExtensionsRelatedTo(supportedCapabilities_, context()->grammar()); + + bool modified_module = false; + for (auto extension : supported_extensions) { + if (required_extensions.contains(extension)) { + continue; + } + + if (context()->RemoveExtension(extension)) { + modified_module = true; + } + } + + return modified_module ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +bool TrimCapabilitiesPass::HasForbiddenCapabilities() const { + // EnumSet.HasAnyOf returns `true` if the given set is empty. + if (forbiddenCapabilities_.size() == 0) { + return false; + } + + const auto& capabilities = context()->get_feature_mgr()->GetCapabilities(); + return capabilities.HasAnyOf(forbiddenCapabilities_); +} + +Pass::Status TrimCapabilitiesPass::Process() { + if (HasForbiddenCapabilities()) { + return Status::SuccessWithoutChange; + } + + auto[required_capabilities, required_extensions] = + DetermineRequiredCapabilitiesAndExtensions(); + + Pass::Status capStatus = TrimUnrequiredCapabilities(required_capabilities); + Pass::Status extStatus = TrimUnrequiredExtensions(required_extensions); + + return capStatus == Pass::Status::SuccessWithChange || + extStatus == Pass::Status::SuccessWithChange + ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.h new file mode 100644 index 00000000..3ec06d7f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/trim_capabilities_pass.h @@ -0,0 +1,216 @@ +// Copyright (c) 2023 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_ +#define SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/enum_set.h" +#include "source/extensions.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/spirv_target_env.h" + +namespace spvtools { +namespace opt { + +// This is required for NDK build. The unordered_set/unordered_map +// implementation don't work with class enums. +struct ClassEnumHash { + std::size_t operator()(spv::Capability value) const { + using StoringType = typename std::underlying_type_t; + return std::hash{}(static_cast(value)); + } + + std::size_t operator()(spv::Op value) const { + using StoringType = typename std::underlying_type_t; + return std::hash{}(static_cast(value)); + } +}; + +// An opcode handler is a function which, given an instruction, returns either +// the required capability, or nothing. +// Each handler checks one case for a capability requirement. +// +// Example: +// - `OpTypeImage` can have operand `A` operand which requires capability 1 +// - `OpTypeImage` can also have operand `B` which requires capability 2. +// -> We have 2 handlers: `Handler_OpTypeImage_1` and +// `Handler_OpTypeImage_2`. +using OpcodeHandler = + std::optional (*)(const Instruction* instruction); + +// This pass tried to remove superfluous capabilities declared in the module. +// - If all the capabilities listed by an extension are removed, the extension +// is also trimmed. +// - If the module countains any capability listed in `kForbiddenCapabilities`, +// the module is left untouched. +// - No capabilities listed in `kUntouchableCapabilities` are trimmed, even when +// not used. +// - Only capabilitied listed in `kSupportedCapabilities` are supported. +// - If the module contains unsupported capabilities, results might be +// incorrect. +class TrimCapabilitiesPass : public Pass { + private: + // All the capabilities supported by this optimization pass. If your module + // contains unsupported instruction, the pass could yield bad results. + static constexpr std::array kSupportedCapabilities{ + // clang-format off + spv::Capability::ComputeDerivativeGroupLinearKHR, + spv::Capability::ComputeDerivativeGroupQuadsKHR, + spv::Capability::Float16, + spv::Capability::Float64, + spv::Capability::FragmentShaderPixelInterlockEXT, + spv::Capability::FragmentShaderSampleInterlockEXT, + spv::Capability::FragmentShaderShadingRateInterlockEXT, + spv::Capability::GroupNonUniform, + spv::Capability::GroupNonUniformArithmetic, + spv::Capability::GroupNonUniformClustered, + spv::Capability::GroupNonUniformPartitionedNV, + spv::Capability::GroupNonUniformVote, + spv::Capability::Groups, + spv::Capability::ImageMSArray, + spv::Capability::Int16, + spv::Capability::Int64, + spv::Capability::InterpolationFunction, + spv::Capability::Linkage, + spv::Capability::MinLod, + spv::Capability::PhysicalStorageBufferAddresses, + spv::Capability::RayQueryKHR, + spv::Capability::RayTracingKHR, + spv::Capability::RayTraversalPrimitiveCullingKHR, + spv::Capability::Shader, + spv::Capability::ShaderClockKHR, + spv::Capability::StorageBuffer16BitAccess, + spv::Capability::StorageImageReadWithoutFormat, + spv::Capability::StorageImageWriteWithoutFormat, + spv::Capability::StorageInputOutput16, + spv::Capability::StoragePushConstant16, + spv::Capability::StorageUniform16, + spv::Capability::StorageUniformBufferBlock16, + spv::Capability::VulkanMemoryModelDeviceScope, + spv::Capability::QuadControlKHR, + // clang-format on + }; + + // Those capabilities disable all transformation of the module. + static constexpr std::array kForbiddenCapabilities{ + spv::Capability::Linkage, + }; + + // Those capabilities are never removed from a module because we cannot + // guess from the SPIR-V only if they are required or not. + static constexpr std::array kUntouchableCapabilities{ + spv::Capability::Shader, + }; + + public: + TrimCapabilitiesPass(); + TrimCapabilitiesPass(const TrimCapabilitiesPass&) = delete; + TrimCapabilitiesPass(TrimCapabilitiesPass&&) = delete; + + private: + // Inserts every capability listed by `descriptor` this pass supports into + // `output`. Expects a Descriptor like `spv_opcode_desc_t` or + // `spv_operand_desc_t`. + template + inline void addSupportedCapabilitiesToSet(const Descriptor* const descriptor, + CapabilitySet* output) const { + const uint32_t capabilityCount = descriptor->numCapabilities; + for (uint32_t i = 0; i < capabilityCount; ++i) { + const auto capability = descriptor->capabilities[i]; + if (supportedCapabilities_.contains(capability)) { + output->insert(capability); + } + } + } + + // Inserts every extension listed by `descriptor` required by the module into + // `output`. Expects a Descriptor like `spv_opcode_desc_t` or + // `spv_operand_desc_t`. + template + inline void addSupportedExtensionsToSet(const Descriptor* const descriptor, + ExtensionSet* output) const { + if (descriptor->minVersion <= + spvVersionForTargetEnv(context()->GetTargetEnv())) { + return; + } + output->insert(descriptor->extensions, + descriptor->extensions + descriptor->numExtensions); + } + + void addInstructionRequirementsForOpcode(spv::Op opcode, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + void addInstructionRequirementsForOperand(const Operand& operand, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + + void addInstructionRequirementsForExtInst(Instruction* instruction, + CapabilitySet* capabilities) const; + + // Given an `instruction`, determines the capabilities it requires, and output + // them in `capabilities`. The returned capabilities form a subset of + // kSupportedCapabilities. + void addInstructionRequirements(Instruction* instruction, + CapabilitySet* capabilities, + ExtensionSet* extensions) const; + + // Given an operand `type` and `value`, adds the extensions it would require + // to `extensions`. + void AddExtensionsForOperand(const spv_operand_type_t type, + const uint32_t value, + ExtensionSet* extensions) const; + + // Returns the list of required capabilities and extensions for the module. + // The returned capabilities form a subset of kSupportedCapabilities. + std::pair + DetermineRequiredCapabilitiesAndExtensions() const; + + // Trims capabilities not listed in `required_capabilities` if possible. + // Returns whether or not the module was modified. + Pass::Status TrimUnrequiredCapabilities( + const CapabilitySet& required_capabilities) const; + + // Trims extensions not listed in `required_extensions` if supported by this + // pass. An extensions is considered supported as soon as one capability this + // pass support requires it. + Pass::Status TrimUnrequiredExtensions( + const ExtensionSet& required_extensions) const; + + // Returns if the analyzed module contains any forbidden capability. + bool HasForbiddenCapabilities() const; + + public: + const char* name() const override { return "trim-capabilities"; } + Status Process() override; + + private: + const CapabilitySet supportedCapabilities_; + const CapabilitySet forbiddenCapabilities_; + const CapabilitySet untouchableCapabilities_; + const std::unordered_multimap + opcodeHandlers_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_TRIM_CAPABILITIES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.cpp new file mode 100644 index 00000000..be7dbc8f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.cpp @@ -0,0 +1,1223 @@ +// Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/type_manager.h" + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { +constexpr int kSpvTypePointerStorageClass = 1; +constexpr int kSpvTypePointerTypeIdInIdx = 2; +} // namespace + +TypeManager::TypeManager(const MessageConsumer& consumer, IRContext* c) + : consumer_(consumer), context_(c) { + AnalyzeTypes(*c->module()); +} + +Type* TypeManager::GetType(uint32_t id) const { + auto iter = id_to_type_.find(id); + if (iter != id_to_type_.end()) return (*iter).second; + iter = id_to_incomplete_type_.find(id); + if (iter != id_to_incomplete_type_.end()) return (*iter).second; + return nullptr; +} + +std::pair> TypeManager::GetTypeAndPointerType( + uint32_t id, spv::StorageClass sc) const { + Type* type = GetType(id); + if (type) { + return std::make_pair(type, MakeUnique(type, sc)); + } else { + return std::make_pair(type, std::unique_ptr()); + } +} + +uint32_t TypeManager::GetId(const Type* type) const { + auto iter = type_to_id_.find(type); + if (iter != type_to_id_.end()) { + return (*iter).second; + } + return 0; +} + +void TypeManager::AnalyzeTypes(const Module& module) { + // First pass through the constants, as some will be needed when traversing + // the types in the next pass. + for (const auto* inst : module.GetConstants()) { + id_to_constant_inst_[inst->result_id()] = inst; + } + + // Then pass through the types. Any types that reference a forward pointer + // (directly or indirectly) are incomplete, and are added to incomplete types. + for (const auto* inst : module.GetTypes()) { + RecordIfTypeDefinition(*inst); + } + + if (incomplete_types_.empty()) { + return; + } + + // Get the real pointer definition for all of the forward pointers. + for (auto& type : incomplete_types_) { + if (type.type()->kind() == Type::kForwardPointer) { + auto* t = GetType(type.id()); + assert(t); + auto* p = t->AsPointer(); + assert(p); + type.type()->AsForwardPointer()->SetTargetPointer(p); + } + } + + // Replaces the references to the forward pointers in the incomplete types. + for (auto& type : incomplete_types_) { + ReplaceForwardPointers(type.type()); + } + + // Delete the forward pointers now that they are not referenced anymore. + for (auto& type : incomplete_types_) { + if (type.type()->kind() == Type::kForwardPointer) { + type.ResetType(nullptr); + } + } + + // Compare the complete types looking for types that are the same. If there + // are two types that are the same, then replace one with the other. + // Continue until we reach a fixed point. + bool restart = true; + while (restart) { + restart = false; + for (auto it1 = incomplete_types_.begin(); it1 != incomplete_types_.end(); + ++it1) { + uint32_t id1 = it1->id(); + Type* type1 = it1->type(); + if (!type1) { + continue; + } + + for (auto it2 = it1 + 1; it2 != incomplete_types_.end(); ++it2) { + uint32_t id2 = it2->id(); + (void)(id2 + id1); + Type* type2 = it2->type(); + if (!type2) { + continue; + } + + if (type1->IsSame(type2)) { + ReplaceType(type1, type2); + it2->ResetType(nullptr); + id_to_incomplete_type_[it2->id()] = type1; + restart = true; + } + } + } + } + + // Add the remaining incomplete types to the type pool. + for (auto& type : incomplete_types_) { + if (type.type() && !type.type()->AsForwardPointer()) { + std::vector decorations = + context()->get_decoration_mgr()->GetDecorationsFor(type.id(), true); + for (auto dec : decorations) { + AttachDecoration(*dec, type.type()); + } + auto pair = type_pool_.insert(type.ReleaseType()); + id_to_type_[type.id()] = pair.first->get(); + type_to_id_[pair.first->get()] = type.id(); + id_to_incomplete_type_.erase(type.id()); + } + } + + // Add a mapping for any ids that whose original type was replaced by an + // equivalent type. + for (auto& type : id_to_incomplete_type_) { + id_to_type_[type.first] = type.second; + } + +#ifndef NDEBUG + // Check if the type pool contains two types that are the same. This + // is an indication that the hashing and comparison are wrong. It + // will cause a problem if the type pool gets resized and everything + // is rehashed. + for (auto& i : type_pool_) { + for (auto& j : type_pool_) { + Type* ti = i.get(); + Type* tj = j.get(); + assert((ti == tj || !ti->IsSame(tj)) && + "Type pool contains two types that are the same."); + } + } +#endif +} + +void TypeManager::RemoveId(uint32_t id) { + auto iter = id_to_type_.find(id); + if (iter == id_to_type_.end()) return; + + auto& type = iter->second; + if (!type->IsUniqueType()) { + auto tIter = type_to_id_.find(type); + if (tIter != type_to_id_.end() && tIter->second == id) { + // |type| currently maps to |id|. + // Search for an equivalent type to re-map. + bool found = false; + for (auto& pair : id_to_type_) { + if (pair.first != id && *pair.second == *type) { + // Equivalent ambiguous type, re-map type. + type_to_id_.erase(type); + type_to_id_[pair.second] = pair.first; + found = true; + break; + } + } + // No equivalent ambiguous type, remove mapping. + if (!found) type_to_id_.erase(tIter); + } + } else { + // Unique type, so just erase the entry. + type_to_id_.erase(type); + } + + // Erase the entry for |id|. + id_to_type_.erase(iter); +} + +uint32_t TypeManager::GetTypeInstruction(const Type* type) { + uint32_t id = GetId(type); + if (id != 0) return id; + + std::unique_ptr typeInst; + // TODO(1841): Handle id overflow. + id = context()->TakeNextId(); + if (id == 0) { + return 0; + } + + RegisterType(id, *type); + switch (type->kind()) { +#define DefineParameterlessCase(kind) \ + case Type::k##kind: \ + typeInst = MakeUnique(context(), spv::Op::OpType##kind, 0, \ + id, std::initializer_list{}); \ + break + DefineParameterlessCase(Void); + DefineParameterlessCase(Bool); + DefineParameterlessCase(Sampler); + DefineParameterlessCase(Event); + DefineParameterlessCase(DeviceEvent); + DefineParameterlessCase(ReserveId); + DefineParameterlessCase(Queue); + DefineParameterlessCase(PipeStorage); + DefineParameterlessCase(NamedBarrier); + DefineParameterlessCase(AccelerationStructureNV); + DefineParameterlessCase(RayQueryKHR); + DefineParameterlessCase(HitObjectNV); +#undef DefineParameterlessCase + case Type::kInteger: + typeInst = MakeUnique( + context(), spv::Op::OpTypeInt, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsInteger()->width()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(type->AsInteger()->IsSigned() ? 1u : 0u)}}}); + break; + case Type::kFloat: + // TODO: Handle FP encoding enums once actually used. + typeInst = MakeUnique( + context(), spv::Op::OpTypeFloat, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsFloat()->width()}}}); + break; + case Type::kVector: { + uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = + MakeUnique(context(), spv::Op::OpTypeVector, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {type->AsVector()->element_count()}}}); + break; + } + case Type::kMatrix: { + uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = + MakeUnique(context(), spv::Op::OpTypeMatrix, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {type->AsMatrix()->element_count()}}}); + break; + } + case Type::kImage: { + const Image* image = type->AsImage(); + uint32_t subtype = GetTypeInstruction(image->sampled_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeImage, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_DIMENSIONALITY, + {static_cast(image->dim())}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {image->depth()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(image->is_arrayed() ? 1u : 0u)}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(image->is_multisampled() ? 1u : 0u)}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {image->sampled()}}, + {SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT, + {static_cast(image->format())}}, + {SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + {static_cast(image->access_qualifier())}}}); + break; + } + case Type::kSampledImage: { + uint32_t subtype = + GetTypeInstruction(type->AsSampledImage()->image_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeSampledImage, 0, id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kArray: { + uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeArray, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_ID, {type->AsArray()->LengthId()}}}); + break; + } + case Type::kRuntimeArray: { + uint32_t subtype = + GetTypeInstruction(type->AsRuntimeArray()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeRuntimeArray, 0, id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kNodePayloadArrayAMDX: { + uint32_t subtype = + GetTypeInstruction(type->AsNodePayloadArrayAMDX()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeNodePayloadArrayAMDX, 0, id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kStruct: { + std::vector ops; + const Struct* structTy = type->AsStruct(); + for (auto ty : structTy->element_types()) { + uint32_t member_type_id = GetTypeInstruction(ty); + if (member_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id})); + } + typeInst = + MakeUnique(context(), spv::Op::OpTypeStruct, 0, id, ops); + break; + } + case Type::kOpaque: { + const Opaque* opaque = type->AsOpaque(); + // Convert to null-terminated packed UTF-8 string. + std::vector words = spvtools::utils::MakeVector(opaque->name()); + typeInst = MakeUnique( + context(), spv::Op::OpTypeOpaque, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_STRING, words}}); + break; + } + case Type::kPointer: { + const Pointer* pointer = type->AsPointer(); + if (pointer->is_untyped()) { + typeInst = MakeUnique( + context(), spv::Op::OpTypeUntypedPointerKHR, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(pointer->storage_class())}}}); + } else { + uint32_t subtype = GetTypeInstruction(pointer->pointee_type()); + typeInst = MakeUnique( + context(), spv::Op::OpTypePointer, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(pointer->storage_class())}}, + {SPV_OPERAND_TYPE_ID, {subtype}}}); + } + break; + } + case Type::kFunction: { + std::vector ops; + const Function* function = type->AsFunction(); + uint32_t return_type_id = GetTypeInstruction(function->return_type()); + if (return_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id})); + for (auto ty : function->param_types()) { + uint32_t paramater_type_id = GetTypeInstruction(ty); + if (paramater_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id})); + } + typeInst = MakeUnique(context(), spv::Op::OpTypeFunction, 0, + id, ops); + break; + } + case Type::kPipe: + typeInst = MakeUnique( + context(), spv::Op::OpTypePipe, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + {static_cast(type->AsPipe()->access_qualifier())}}}); + break; + case Type::kForwardPointer: + typeInst = MakeUnique( + context(), spv::Op::OpTypeForwardPointer, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {type->AsForwardPointer()->target_id()}}, + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast( + type->AsForwardPointer()->storage_class())}}}); + break; + case Type::kCooperativeMatrixNV: { + auto coop_mat = type->AsCooperativeMatrixNV(); + uint32_t const component_type = + GetTypeInstruction(coop_mat->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeCooperativeMatrixNV, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}}); + break; + } + case Type::kCooperativeMatrixKHR: { + auto coop_mat = type->AsCooperativeMatrixKHR(); + uint32_t const component_type = + GetTypeInstruction(coop_mat->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeCooperativeMatrixKHR, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->use_id()}}}); + break; + } + case Type::kTensorLayoutNV: { + auto tensor_layout = type->AsTensorLayoutNV(); + typeInst = MakeUnique( + context(), spv::Op::OpTypeTensorLayoutNV, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {tensor_layout->dim_id()}}, + {SPV_OPERAND_TYPE_ID, {tensor_layout->clamp_mode_id()}}}); + break; + } + case Type::kTensorViewNV: { + auto tensor_view = type->AsTensorViewNV(); + std::vector operands; + operands.push_back(Operand{SPV_OPERAND_TYPE_ID, {tensor_view->dim_id()}}); + operands.push_back( + Operand{SPV_OPERAND_TYPE_ID, {tensor_view->has_dimensions_id()}}); + for (auto p : tensor_view->perm()) { + operands.push_back(Operand{SPV_OPERAND_TYPE_ID, {p}}); + } + typeInst = MakeUnique(context(), spv::Op::OpTypeTensorViewNV, + 0, id, operands); + break; + } + case Type::kCooperativeVectorNV: { + auto coop_vec = type->AsCooperativeVectorNV(); + uint32_t const component_type = + GetTypeInstruction(coop_vec->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique( + context(), spv::Op::OpTypeCooperativeVectorNV, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_ID, {coop_vec->components()}}}); + break; + } + default: + assert(false && "Unexpected type"); + break; + } + context()->AddType(std::move(typeInst)); + // TODO(dneto): This next call to AnalyzeDefUse is redundant becaues + // IRContext::AddType already does it. + context()->AnalyzeDefUse(&*--context()->types_values_end()); + AttachDecorations(id, type); + return id; +} + +uint32_t TypeManager::FindPointerToType(uint32_t type_id, + spv::StorageClass storage_class) { + Type* pointeeTy = GetType(type_id); + Pointer pointerTy(pointeeTy, storage_class); + + Module::inst_iterator type_itr = context()->module()->types_values_begin(); + for (; type_itr != context()->module()->types_values_end(); ++type_itr) { + const Instruction* type_inst = &*type_itr; + if (type_inst->opcode() == spv::Op::OpTypePointer && + type_inst->GetSingleWordOperand(kSpvTypePointerTypeIdInIdx) == + type_id && + spv::StorageClass(type_inst->GetSingleWordOperand( + kSpvTypePointerStorageClass)) == storage_class) + return type_inst->result_id(); + } + + // Must create the pointer type. + uint32_t resultId = context()->TakeNextId(); + if (resultId == 0) { + return 0; + } + std::unique_ptr type_inst( + new Instruction(context(), spv::Op::OpTypePointer, 0, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(storage_class)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}})); + context()->AddType(std::move(type_inst)); + context()->get_type_mgr()->RegisterType(resultId, pointerTy); + return resultId; +} + +void TypeManager::AttachDecorations(uint32_t id, const Type* type) { + for (auto vec : type->decorations()) { + CreateDecoration(id, vec); + } + if (const Struct* structTy = type->AsStruct()) { + for (auto pair : structTy->element_decorations()) { + uint32_t element = pair.first; + for (auto vec : pair.second) { + CreateDecoration(id, vec, /* is_member */ true, element); + } + } + } +} + +void TypeManager::CreateDecoration(uint32_t target, + const std::vector& decoration, + bool is_member, uint32_t element) { + std::vector ops; + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {target})); + if (is_member) { + ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {element})); + } + ops.push_back(Operand(SPV_OPERAND_TYPE_DECORATION, {decoration[0]})); + for (size_t i = 1; i < decoration.size(); ++i) { + ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration[i]})); + } + context()->AddAnnotationInst(MakeUnique( + context(), (is_member ? spv::Op::OpMemberDecorate : spv::Op::OpDecorate), + 0, 0, ops)); + Instruction* inst = &*--context()->annotation_end(); + context()->get_def_use_mgr()->AnalyzeInstUse(inst); +} + +Type* TypeManager::RebuildType(uint32_t type_id, const Type& type) { + assert(type_id != 0); + + // The comparison and hash on the type pool will avoid inserting the rebuilt + // type if an equivalent type already exists. The rebuilt type will be deleted + // when it goes out of scope at the end of the function in that case. Repeated + // insertions of the same Type will, at most, keep one corresponding object in + // the type pool. + std::unique_ptr rebuilt_ty; + + // If |type_id| is already present in the type pool, return the existing type. + // This saves extra work in the type builder and prevents running into + // circular issues (https://github.com/KhronosGroup/SPIRV-Tools/issues/5623). + Type* pool_ty = GetType(type_id); + if (pool_ty != nullptr) { + return pool_ty; + } + + switch (type.kind()) { +#define DefineNoSubtypeCase(kind) \ + case Type::k##kind: \ + rebuilt_ty.reset(type.Clone().release()); \ + return type_pool_.insert(std::move(rebuilt_ty)).first->get() + + DefineNoSubtypeCase(Void); + DefineNoSubtypeCase(Bool); + DefineNoSubtypeCase(Integer); + DefineNoSubtypeCase(Float); + DefineNoSubtypeCase(Sampler); + DefineNoSubtypeCase(Opaque); + DefineNoSubtypeCase(Event); + DefineNoSubtypeCase(DeviceEvent); + DefineNoSubtypeCase(ReserveId); + DefineNoSubtypeCase(Queue); + DefineNoSubtypeCase(Pipe); + DefineNoSubtypeCase(PipeStorage); + DefineNoSubtypeCase(NamedBarrier); + DefineNoSubtypeCase(AccelerationStructureNV); + DefineNoSubtypeCase(RayQueryKHR); + DefineNoSubtypeCase(HitObjectNV); +#undef DefineNoSubtypeCase + case Type::kVector: { + const Vector* vec_ty = type.AsVector(); + const Type* ele_ty = vec_ty->element_type(); + rebuilt_ty = MakeUnique(RebuildType(GetId(ele_ty), *ele_ty), + vec_ty->element_count()); + break; + } + case Type::kMatrix: { + const Matrix* mat_ty = type.AsMatrix(); + const Type* ele_ty = mat_ty->element_type(); + rebuilt_ty = MakeUnique(RebuildType(GetId(ele_ty), *ele_ty), + mat_ty->element_count()); + break; + } + case Type::kImage: { + const Image* image_ty = type.AsImage(); + const Type* ele_ty = image_ty->sampled_type(); + rebuilt_ty = MakeUnique( + RebuildType(GetId(ele_ty), *ele_ty), image_ty->dim(), + image_ty->depth(), image_ty->is_arrayed(), + image_ty->is_multisampled(), image_ty->sampled(), image_ty->format(), + image_ty->access_qualifier()); + break; + } + case Type::kSampledImage: { + const SampledImage* image_ty = type.AsSampledImage(); + const Type* ele_ty = image_ty->image_type(); + rebuilt_ty = + MakeUnique(RebuildType(GetId(ele_ty), *ele_ty)); + break; + } + case Type::kArray: { + const Array* array_ty = type.AsArray(); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = MakeUnique(RebuildType(GetId(ele_ty), *ele_ty), + array_ty->length_info()); + break; + } + case Type::kRuntimeArray: { + const RuntimeArray* array_ty = type.AsRuntimeArray(); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = + MakeUnique(RebuildType(GetId(ele_ty), *ele_ty)); + break; + } + case Type::kNodePayloadArrayAMDX: { + const NodePayloadArrayAMDX* array_ty = type.AsNodePayloadArrayAMDX(); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = + MakeUnique(RebuildType(GetId(ele_ty), *ele_ty)); + break; + } + case Type::kStruct: { + const Struct* struct_ty = type.AsStruct(); + std::vector subtypes; + subtypes.reserve(struct_ty->element_types().size()); + for (const auto* ele_ty : struct_ty->element_types()) { + subtypes.push_back(RebuildType(GetId(ele_ty), *ele_ty)); + } + rebuilt_ty = MakeUnique(subtypes); + Struct* rebuilt_struct = rebuilt_ty->AsStruct(); + for (auto pair : struct_ty->element_decorations()) { + uint32_t index = pair.first; + for (const auto& dec : pair.second) { + // Explicit copy intended. + std::vector copy(dec); + rebuilt_struct->AddMemberDecoration(index, std::move(copy)); + } + } + break; + } + case Type::kPointer: { + const Pointer* pointer_ty = type.AsPointer(); + if (pointer_ty->pointee_type()) { + const Type* ele_ty = pointer_ty->pointee_type(); + rebuilt_ty = MakeUnique(RebuildType(GetId(ele_ty), *ele_ty), + pointer_ty->storage_class()); + } else { + rebuilt_ty = MakeUnique(nullptr, pointer_ty->storage_class()); + } + break; + } + case Type::kFunction: { + const Function* function_ty = type.AsFunction(); + const Type* ret_ty = function_ty->return_type(); + std::vector param_types; + param_types.reserve(function_ty->param_types().size()); + for (const auto* param_ty : function_ty->param_types()) { + param_types.push_back(RebuildType(GetId(param_ty), *param_ty)); + } + rebuilt_ty = MakeUnique(RebuildType(GetId(ret_ty), *ret_ty), + param_types); + break; + } + case Type::kForwardPointer: { + const ForwardPointer* forward_ptr_ty = type.AsForwardPointer(); + rebuilt_ty = MakeUnique(forward_ptr_ty->target_id(), + forward_ptr_ty->storage_class()); + const Pointer* target_ptr = forward_ptr_ty->target_pointer(); + if (target_ptr) { + rebuilt_ty->AsForwardPointer()->SetTargetPointer( + RebuildType(GetId(target_ptr), *target_ptr)->AsPointer()); + } + break; + } + case Type::kCooperativeMatrixNV: { + const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV(); + const Type* component_type = cm_type->component_type(); + rebuilt_ty = MakeUnique( + RebuildType(GetId(component_type), *component_type), + cm_type->scope_id(), cm_type->rows_id(), cm_type->columns_id()); + break; + } + case Type::kCooperativeMatrixKHR: { + const CooperativeMatrixKHR* cm_type = type.AsCooperativeMatrixKHR(); + const Type* component_type = cm_type->component_type(); + rebuilt_ty = MakeUnique( + RebuildType(GetId(component_type), *component_type), + cm_type->scope_id(), cm_type->rows_id(), cm_type->columns_id(), + cm_type->use_id()); + break; + } + case Type::kTensorLayoutNV: { + const TensorLayoutNV* tl_type = type.AsTensorLayoutNV(); + rebuilt_ty = MakeUnique(tl_type->dim_id(), + tl_type->clamp_mode_id()); + break; + } + case Type::kTensorViewNV: { + const TensorViewNV* tv_type = type.AsTensorViewNV(); + rebuilt_ty = MakeUnique( + tv_type->dim_id(), tv_type->has_dimensions_id(), tv_type->perm()); + break; + } + case Type::kCooperativeVectorNV: { + const CooperativeVectorNV* cv_type = type.AsCooperativeVectorNV(); + const Type* component_type = cv_type->component_type(); + rebuilt_ty = MakeUnique( + RebuildType(GetId(component_type), *component_type), + cv_type->components()); + break; + } + default: + assert(false && "Unhandled type"); + return nullptr; + } + for (const auto& dec : type.decorations()) { + // Explicit copy intended. + std::vector copy(dec); + rebuilt_ty->AddDecoration(std::move(copy)); + } + + return type_pool_.insert(std::move(rebuilt_ty)).first->get(); +} + +void TypeManager::RegisterType(uint32_t id, const Type& type) { + // Rebuild |type| so it and all its constituent types are owned by the type + // pool. + Type* rebuilt = RebuildType(id, type); + assert(rebuilt->IsSame(&type)); + id_to_type_[id] = rebuilt; + if (GetId(rebuilt) == 0) { + type_to_id_[rebuilt] = id; + } +} + +Type* TypeManager::GetRegisteredType(const Type* type) { + uint32_t id = GetTypeInstruction(type); + if (id == 0) { + return nullptr; + } + return GetType(id); +} + +Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { + if (!IsTypeInst(inst.opcode())) return nullptr; + + Type* type = nullptr; + switch (inst.opcode()) { + case spv::Op::OpTypeVoid: + type = new Void(); + break; + case spv::Op::OpTypeBool: + type = new Bool(); + break; + case spv::Op::OpTypeInt: + type = new Integer(inst.GetSingleWordInOperand(0), + inst.GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeFloat: { + const spv::FPEncoding encoding = + inst.NumInOperands() > 1 + ? static_cast(inst.GetSingleWordInOperand(1)) + : spv::FPEncoding::Max; + type = new Float(inst.GetSingleWordInOperand(0), encoding); + } break; + case spv::Op::OpTypeVector: + type = new Vector(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeMatrix: + type = new Matrix(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeImage: { + const spv::AccessQualifier access = + inst.NumInOperands() < 8 ? spv::AccessQualifier::ReadOnly + : static_cast( + inst.GetSingleWordInOperand(7)); + type = new Image( + GetType(inst.GetSingleWordInOperand(0)), + static_cast(inst.GetSingleWordInOperand(1)), + inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3) == 1, + inst.GetSingleWordInOperand(4) == 1, inst.GetSingleWordInOperand(5), + static_cast(inst.GetSingleWordInOperand(6)), + access); + } break; + case spv::Op::OpTypeSampler: + type = new Sampler(); + break; + case spv::Op::OpTypeSampledImage: + type = new SampledImage(GetType(inst.GetSingleWordInOperand(0))); + break; + case spv::Op::OpTypeArray: { + const uint32_t length_id = inst.GetSingleWordInOperand(1); + const Instruction* length_constant_inst = id_to_constant_inst_[length_id]; + assert(length_constant_inst); + + // How will we distinguish one length value from another? + // Determine extra words required to distinguish this array length + // from another. + std::vector extra_words{Array::LengthInfo::kDefiningId}; + // If it is a specialised constant, retrieve its SpecId. + // Only OpSpecConstant has a SpecId. + uint32_t spec_id = 0u; + bool has_spec_id = false; + if (length_constant_inst->opcode() == spv::Op::OpSpecConstant) { + context()->get_decoration_mgr()->ForEachDecoration( + length_id, uint32_t(spv::Decoration::SpecId), + [&spec_id, &has_spec_id](const Instruction& decoration) { + assert(decoration.opcode() == spv::Op::OpDecorate); + spec_id = decoration.GetSingleWordOperand(2u); + has_spec_id = true; + }); + } + const auto opcode = length_constant_inst->opcode(); + if (has_spec_id) { + extra_words.push_back(spec_id); + } + if ((opcode == spv::Op::OpConstant) || + (opcode == spv::Op::OpSpecConstant)) { + // Always include the literal constant words. In the spec constant + // case, the constant might not be overridden, so it's still + // significant. + extra_words.insert(extra_words.end(), + length_constant_inst->GetOperand(2).words.begin(), + length_constant_inst->GetOperand(2).words.end()); + extra_words[0] = has_spec_id ? Array::LengthInfo::kConstantWithSpecId + : Array::LengthInfo::kConstant; + } else { + assert(extra_words[0] == Array::LengthInfo::kDefiningId); + extra_words.push_back(length_id); + } + assert(extra_words.size() >= 2); + Array::LengthInfo length_info{length_id, extra_words}; + + type = new Array(GetType(inst.GetSingleWordInOperand(0)), length_info); + + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case spv::Op::OpTypeRuntimeArray: + type = new RuntimeArray(GetType(inst.GetSingleWordInOperand(0))); + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + break; + case spv::Op::OpTypeNodePayloadArrayAMDX: + type = new NodePayloadArrayAMDX(GetType(inst.GetSingleWordInOperand(0))); + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + break; + case spv::Op::OpTypeStruct: { + std::vector element_types; + bool incomplete_type = false; + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + uint32_t type_id = inst.GetSingleWordInOperand(i); + element_types.push_back(GetType(type_id)); + if (id_to_incomplete_type_.count(type_id)) { + incomplete_type = true; + } + } + type = new Struct(element_types); + + if (incomplete_type) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case spv::Op::OpTypeOpaque: { + type = new Opaque(inst.GetInOperand(0).AsString()); + } break; + case spv::Op::OpTypePointer: { + uint32_t pointee_type_id = inst.GetSingleWordInOperand(1); + type = new Pointer( + GetType(pointee_type_id), + static_cast(inst.GetSingleWordInOperand(0))); + + if (id_to_incomplete_type_.count(pointee_type_id)) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + id_to_incomplete_type_.erase(inst.result_id()); + + } break; + case spv::Op::OpTypeUntypedPointerKHR: { + type = new Pointer(nullptr, static_cast( + inst.GetSingleWordInOperand(0))); + id_to_incomplete_type_.erase(inst.result_id()); + } break; + case spv::Op::OpTypeFunction: { + bool incomplete_type = false; + uint32_t return_type_id = inst.GetSingleWordInOperand(0); + if (id_to_incomplete_type_.count(return_type_id)) { + incomplete_type = true; + } + Type* return_type = GetType(return_type_id); + std::vector param_types; + for (uint32_t i = 1; i < inst.NumInOperands(); ++i) { + uint32_t param_type_id = inst.GetSingleWordInOperand(i); + param_types.push_back(GetType(param_type_id)); + if (id_to_incomplete_type_.count(param_type_id)) { + incomplete_type = true; + } + } + + type = new Function(return_type, param_types); + + if (incomplete_type) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case spv::Op::OpTypeEvent: + type = new Event(); + break; + case spv::Op::OpTypeDeviceEvent: + type = new DeviceEvent(); + break; + case spv::Op::OpTypeReserveId: + type = new ReserveId(); + break; + case spv::Op::OpTypeQueue: + type = new Queue(); + break; + case spv::Op::OpTypePipe: + type = new Pipe( + static_cast(inst.GetSingleWordInOperand(0))); + break; + case spv::Op::OpTypeForwardPointer: { + // Handling of forward pointers is different from the other types. + uint32_t target_id = inst.GetSingleWordInOperand(0); + type = new ForwardPointer(target_id, static_cast( + inst.GetSingleWordInOperand(1))); + incomplete_types_.emplace_back(target_id, type); + id_to_incomplete_type_[target_id] = type; + return type; + } + case spv::Op::OpTypePipeStorage: + type = new PipeStorage(); + break; + case spv::Op::OpTypeNamedBarrier: + type = new NamedBarrier(); + break; + case spv::Op::OpTypeAccelerationStructureNV: + type = new AccelerationStructureNV(); + break; + case spv::Op::OpTypeCooperativeMatrixNV: + type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), + inst.GetSingleWordInOperand(2), + inst.GetSingleWordInOperand(3)); + break; + case spv::Op::OpTypeCooperativeMatrixKHR: + type = new CooperativeMatrixKHR( + GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), inst.GetSingleWordInOperand(2), + inst.GetSingleWordInOperand(3), inst.GetSingleWordInOperand(4)); + break; + case spv::Op::OpTypeCooperativeVectorNV: + type = new CooperativeVectorNV(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeRayQueryKHR: + type = new RayQueryKHR(); + break; + case spv::Op::OpTypeHitObjectNV: + type = new HitObjectNV(); + break; + case spv::Op::OpTypeTensorLayoutNV: + type = new TensorLayoutNV(inst.GetSingleWordInOperand(0), + inst.GetSingleWordInOperand(1)); + break; + case spv::Op::OpTypeTensorViewNV: { + const auto count = inst.NumOperands(); + std::vector perm; + for (uint32_t i = 2; i < count; ++i) { + perm.push_back(inst.GetSingleWordOperand(i)); + } + type = new TensorViewNV(inst.GetSingleWordInOperand(0), + inst.GetSingleWordInOperand(1), perm); + break; + } + default: + assert(false && "Type not handled by the type manager."); + break; + } + + uint32_t id = inst.result_id(); + SPIRV_ASSERT(consumer_, id != 0, "instruction without result id found"); + SPIRV_ASSERT(consumer_, type != nullptr, + "type should not be nullptr at this point"); + std::vector decorations = + context()->get_decoration_mgr()->GetDecorationsFor(id, true); + for (auto dec : decorations) { + AttachDecoration(*dec, type); + } + std::unique_ptr unique(type); + auto pair = type_pool_.insert(std::move(unique)); + id_to_type_[id] = pair.first->get(); + type_to_id_[pair.first->get()] = id; + return type; +} + +void TypeManager::AttachDecoration(const Instruction& inst, Type* type) { + const spv::Op opcode = inst.opcode(); + if (!IsAnnotationInst(opcode)) return; + + switch (opcode) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: { + const auto count = inst.NumOperands(); + std::vector data; + for (uint32_t i = 1; i < count; ++i) { + data.push_back(inst.GetSingleWordOperand(i)); + } + type->AddDecoration(std::move(data)); + } break; + case spv::Op::OpMemberDecorate: { + const auto count = inst.NumOperands(); + const uint32_t index = inst.GetSingleWordOperand(1); + std::vector data; + for (uint32_t i = 2; i < count; ++i) { + data.push_back(inst.GetSingleWordOperand(i)); + } + if (Struct* st = type->AsStruct()) { + st->AddMemberDecoration(index, std::move(data)); + } + } break; + default: + assert(false && "Unexpected opcode for a decoration instruction."); + break; + } +} + +const Type* TypeManager::GetMemberType( + const Type* parent_type, const std::vector& access_chain) { + for (uint32_t element_index : access_chain) { + if (const Struct* struct_type = parent_type->AsStruct()) { + parent_type = struct_type->element_types()[element_index]; + } else if (const Array* array_type = parent_type->AsArray()) { + parent_type = array_type->element_type(); + } else if (const RuntimeArray* runtime_array_type = + parent_type->AsRuntimeArray()) { + parent_type = runtime_array_type->element_type(); + } else if (const Vector* vector_type = parent_type->AsVector()) { + parent_type = vector_type->element_type(); + } else if (const Matrix* matrix_type = parent_type->AsMatrix()) { + parent_type = matrix_type->element_type(); + } else { + assert(false && "Trying to get a member of a type without members."); + } + } + return parent_type; +} + +void TypeManager::ReplaceForwardPointers(Type* type) { + switch (type->kind()) { + case Type::kArray: { + const ForwardPointer* element_type = + type->AsArray()->element_type()->AsForwardPointer(); + if (element_type) { + type->AsArray()->ReplaceElementType(element_type->target_pointer()); + } + } break; + case Type::kRuntimeArray: { + const ForwardPointer* element_type = + type->AsRuntimeArray()->element_type()->AsForwardPointer(); + if (element_type) { + type->AsRuntimeArray()->ReplaceElementType( + element_type->target_pointer()); + } + } break; + case Type::kStruct: { + auto& member_types = type->AsStruct()->element_types(); + for (auto& member_type : member_types) { + if (member_type->AsForwardPointer()) { + member_type = member_type->AsForwardPointer()->target_pointer(); + assert(member_type); + } + } + } break; + case Type::kPointer: { + const ForwardPointer* pointee_type = + type->AsPointer()->pointee_type()->AsForwardPointer(); + if (pointee_type) { + type->AsPointer()->SetPointeeType(pointee_type->target_pointer()); + } + } break; + case Type::kFunction: { + Function* func_type = type->AsFunction(); + const ForwardPointer* return_type = + func_type->return_type()->AsForwardPointer(); + if (return_type) { + func_type->SetReturnType(return_type->target_pointer()); + } + + auto& param_types = func_type->param_types(); + for (auto& param_type : param_types) { + if (param_type->AsForwardPointer()) { + param_type = param_type->AsForwardPointer()->target_pointer(); + } + } + } break; + default: + break; + } +} + +void TypeManager::ReplaceType(Type* new_type, Type* original_type) { + assert(original_type->kind() == new_type->kind() && + "Types must be the same for replacement.\n"); + for (auto& p : incomplete_types_) { + Type* type = p.type(); + if (!type) { + continue; + } + + switch (type->kind()) { + case Type::kArray: { + const Type* element_type = type->AsArray()->element_type(); + if (element_type == original_type) { + type->AsArray()->ReplaceElementType(new_type); + } + } break; + case Type::kRuntimeArray: { + const Type* element_type = type->AsRuntimeArray()->element_type(); + if (element_type == original_type) { + type->AsRuntimeArray()->ReplaceElementType(new_type); + } + } break; + case Type::kStruct: { + auto& member_types = type->AsStruct()->element_types(); + for (auto& member_type : member_types) { + if (member_type == original_type) { + member_type = new_type; + } + } + } break; + case Type::kPointer: { + const Type* pointee_type = type->AsPointer()->pointee_type(); + if (pointee_type == original_type) { + type->AsPointer()->SetPointeeType(new_type); + } + } break; + case Type::kFunction: { + Function* func_type = type->AsFunction(); + const Type* return_type = func_type->return_type(); + if (return_type == original_type) { + func_type->SetReturnType(new_type); + } + + auto& param_types = func_type->param_types(); + for (auto& param_type : param_types) { + if (param_type == original_type) { + param_type = new_type; + } + } + } break; + default: + break; + } + } +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.h b/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.h new file mode 100644 index 00000000..d1e6fc15 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/type_manager.h @@ -0,0 +1,294 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_TYPE_MANAGER_H_ +#define SOURCE_OPT_TYPE_MANAGER_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/module.h" +#include "source/opt/types.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +class IRContext; + +namespace analysis { + +// Hashing functor. +// +// All type pointers must be non-null to reach here. +struct HashTypePointer { + size_t operator()(const Type* type) const { + assert(type); + return type->HashValue(); + } +}; +struct HashTypeUniquePointer { + size_t operator()(const std::unique_ptr& type) const { + assert(type); + return type->HashValue(); + } +}; + +// Equality functor. +// +// Checks if two types pointers are the same type. +// +// All type pointers must be non-null. +struct CompareTypePointers { + bool operator()(const Type* lhs, const Type* rhs) const { + assert(lhs && rhs); + return lhs->IsSame(rhs); + } +}; +struct CompareTypeUniquePointers { + bool operator()(const std::unique_ptr& lhs, + const std::unique_ptr& rhs) const { + assert(lhs && rhs); + return lhs->IsSame(rhs.get()); + } +}; + +// A class for managing the SPIR-V type hierarchy. +class TypeManager { + public: + using IdToTypeMap = std::unordered_map; + + // Constructs a type manager from the given |module|. All internal messages + // will be communicated to the outside via the given message |consumer|. + // This instance only keeps a reference to the |consumer|, so the |consumer| + // should outlive this instance. + TypeManager(const MessageConsumer& consumer, IRContext* c); + + TypeManager(const TypeManager&) = delete; + TypeManager(TypeManager&&) = delete; + TypeManager& operator=(const TypeManager&) = delete; + TypeManager& operator=(TypeManager&&) = delete; + + // Returns the type for the given type |id|. Returns nullptr if the given |id| + // does not define a type. + Type* GetType(uint32_t id) const; + // Returns the id for the given |type|. Returns 0 if can not find the given + // |type|. + uint32_t GetId(const Type* type) const; + // Returns the number of types hold in this manager. + size_t NumTypes() const { return id_to_type_.size(); } + // Iterators for all types contained in this manager. + IdToTypeMap::const_iterator begin() const { return id_to_type_.cbegin(); } + IdToTypeMap::const_iterator end() const { return id_to_type_.cend(); } + + // Returns a pair of the type and pointer to the type in |sc|. + // + // |id| must be a registered type. + std::pair> GetTypeAndPointerType( + uint32_t id, spv::StorageClass sc) const; + + // Returns an id for a declaration representing |type|. Returns 0 if the type + // does not exists, and could not be generated. + // + // If |type| is registered, then the registered id is returned. Otherwise, + // this function recursively adds type and annotation instructions as + // necessary to fully define |type|. + uint32_t GetTypeInstruction(const Type* type); + + // Find pointer to type and storage in module, return its resultId. If it is + // not found, a new type is created, and its id is returned. Returns 0 if the + // type could not be created. + uint32_t FindPointerToType(uint32_t type_id, spv::StorageClass storage_class); + + // Registers |id| to |type|. + // + // If GetId(|type|) already returns a non-zero id, that mapping will be + // unchanged. + void RegisterType(uint32_t id, const Type& type); + + // Return the registered type object that is the same as |type|. + Type* GetRegisteredType(const Type* type); + + // Removes knowledge of |id| from the manager. + // + // If |id| is an ambiguous type the multiple ids may be registered to |id|'s + // type (e.g. %struct1 and %struct1 might hash to the same type). In that + // case, calling GetId() with |id|'s type will return another suitable id + // defining that type. + void RemoveId(uint32_t id); + + // Returns the type of the member of |parent_type| that is identified by + // |access_chain|. The vector |access_chain| is a series of integers that are + // used to pick members as in the |OpCompositeExtract| instructions. If you + // want a member of an array, vector, or matrix that does not have a constant + // index, you can use 0 in that position. All elements have the same type. + const Type* GetMemberType(const Type* parent_type, + const std::vector& access_chain); + + // Attaches the decoration encoded in |inst| to |type|. Does nothing if the + // given instruction is not a decoration instruction. Assumes the target is + // |type| (e.g. should be called in loop of |type|'s decorations). + void AttachDecoration(const Instruction& inst, Type* type); + + Type* GetUIntType() { return GetIntType(32, false); } + + uint32_t GetUIntTypeId() { return GetTypeInstruction(GetUIntType()); } + + Type* GetIntType(int32_t bitWidth, bool isSigned) { + Integer int_type(bitWidth, isSigned); + return GetRegisteredType(&int_type); + } + + Type* GetSIntType() { return GetIntType(32, true); } + + uint32_t GetSIntTypeId() { return GetTypeInstruction(GetSIntType()); } + + Type* GetFloatType() { + Float float_type(32); + return GetRegisteredType(&float_type); + } + + uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } + + Type* GetDoubleType() { + Float float_type(64); + return GetRegisteredType(&float_type); + } + + uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); } + + Type* GetUIntVectorType(uint32_t size) { + Vector vec_type(GetUIntType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetUIntVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetUIntVectorType(size)); + } + + Type* GetSIntVectorType(uint32_t size) { + Vector vec_type(GetSIntType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetSIntVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetSIntVectorType(size)); + } + + Type* GetFloatVectorType(uint32_t size) { + Vector vec_type(GetFloatType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetFloatVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetFloatVectorType(size)); + } + + Type* GetBoolType() { + Bool bool_type; + return GetRegisteredType(&bool_type); + } + + uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); } + + Type* GetVoidType() { + Void void_type; + return GetRegisteredType(&void_type); + } + + uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); } + + private: + using TypeToIdMap = std::unordered_map; + using TypePool = + std::unordered_set, HashTypeUniquePointer, + CompareTypeUniquePointers>; + + class UnresolvedType { + public: + UnresolvedType(uint32_t i, Type* t) : id_(i), type_(t) {} + UnresolvedType(const UnresolvedType&) = delete; + UnresolvedType(UnresolvedType&& that) + : id_(that.id_), type_(std::move(that.type_)) {} + + uint32_t id() { return id_; } + Type* type() { return type_.get(); } + std::unique_ptr&& ReleaseType() { return std::move(type_); } + void ResetType(Type* t) { type_.reset(t); } + + private: + uint32_t id_; + std::unique_ptr type_; + }; + using IdToUnresolvedType = std::vector; + + // Analyzes the types and decorations on types in the given |module|. + void AnalyzeTypes(const Module& module); + + IRContext* context() { return context_; } + + // Attaches the decorations on |type| to |id|. + void AttachDecorations(uint32_t id, const Type* type); + + // Create the annotation instruction. + // + // If |is_member| is false, an OpDecorate of |decoration| on |id| is created, + // otherwise an OpMemberDecorate is created at |element|. The annotation is + // registered with the DefUseManager and the DecorationManager. + void CreateDecoration(uint32_t id, const std::vector& decoration, + bool is_member = false, uint32_t element = 0); + + // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if + // the given instruction is not for defining a type. + Type* RecordIfTypeDefinition(const Instruction& inst); + + // Returns an equivalent pointer to |type| built in terms of pointers owned by + // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt + // replacing the bool subtype with one owned by |type_pool_|. + // + // The re-built type will have ID |type_id|. + Type* RebuildType(uint32_t type_id, const Type& type); + + // Completes the incomplete type |type|, by replaces all references to + // ForwardPointer by the defining Pointer. + void ReplaceForwardPointers(Type* type); + + // Replaces all references to |original_type| in |incomplete_types_| by + // |new_type|. + void ReplaceType(Type* new_type, Type* original_type); + + const MessageConsumer& consumer_; // Message consumer. + IRContext* context_; + IdToTypeMap id_to_type_; // Mapping from ids to their type representations. + TypeToIdMap type_to_id_; // Mapping from types to their defining ids. + TypePool type_pool_; // Memory owner of type pointers. + IdToUnresolvedType incomplete_types_; // All incomplete types. Stored in an + // std::vector to make traversals + // deterministic. + + IdToTypeMap id_to_incomplete_type_; // Maps ids to their type representations + // for incomplete types. + + std::unordered_map id_to_constant_inst_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TYPE_MANAGER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/types.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/types.cpp new file mode 100644 index 00000000..aeee82bf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/types.cpp @@ -0,0 +1,896 @@ +// Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/types.h" + +#include +#include +#include +#include +#include +#include + +#include "source/util/hash_combine.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +using spvtools::utils::hash_combine; +using U32VecVec = std::vector>; + +namespace { + +// Returns true if the two vector of vectors are identical. +bool CompareTwoVectors(const U32VecVec a, const U32VecVec b) { + const auto size = a.size(); + if (size != b.size()) return false; + + if (size == 0) return true; + if (size == 1) return a.front() == b.front(); + + std::vector*> a_ptrs, b_ptrs; + a_ptrs.reserve(size); + a_ptrs.reserve(size); + for (uint32_t i = 0; i < size; ++i) { + a_ptrs.push_back(&a[i]); + b_ptrs.push_back(&b[i]); + } + + const auto cmp = [](const std::vector* m, + const std::vector* n) { + return m->front() < n->front(); + }; + + std::sort(a_ptrs.begin(), a_ptrs.end(), cmp); + std::sort(b_ptrs.begin(), b_ptrs.end(), cmp); + + for (uint32_t i = 0; i < size; ++i) { + if (*a_ptrs[i] != *b_ptrs[i]) return false; + } + return true; +} + +} // namespace + +std::string Type::GetDecorationStr() const { + std::ostringstream oss; + oss << "[["; + for (const auto& decoration : decorations_) { + oss << "("; + for (size_t i = 0; i < decoration.size(); ++i) { + oss << (i > 0 ? ", " : ""); + oss << decoration.at(i); + } + oss << ")"; + } + oss << "]]"; + return oss.str(); +} + +bool Type::HasSameDecorations(const Type* that) const { + return CompareTwoVectors(decorations_, that->decorations_); +} + +bool Type::IsUniqueType() const { + switch (kind_) { + case kPointer: + case kStruct: + case kArray: + case kRuntimeArray: + case kNodePayloadArrayAMDX: + return false; + default: + return true; + } +} + +std::unique_ptr Type::Clone() const { + std::unique_ptr type; + switch (kind_) { +#define DeclareKindCase(kind) \ + case k##kind: \ + type = MakeUnique(*this->As##kind()); \ + break + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); + DeclareKindCase(RayQueryKHR); + DeclareKindCase(HitObjectNV); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + } + return type; +} + +std::unique_ptr Type::RemoveDecorations() const { + std::unique_ptr type(Clone()); + type->ClearDecorations(); + return type; +} + +bool Type::operator==(const Type& other) const { + if (kind_ != other.kind_) return false; + + switch (kind_) { +#define DeclareKindCase(kind) \ + case k##kind: \ + return As##kind()->IsSame(&other) + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(NodePayloadArrayAMDX); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); + DeclareKindCase(RayQueryKHR); + DeclareKindCase(HitObjectNV); + DeclareKindCase(TensorLayoutNV); + DeclareKindCase(TensorViewNV); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + return false; + } +} + +size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const { + // Linear search through a dense, cache coherent vector is faster than O(log + // n) search in a complex data structure (eg std::set) for the generally small + // number of nodes. It also skips the overhead of an new/delete per Type + // (when inserting/removing from a set). + if (std::find(seen->begin(), seen->end(), this) != seen->end()) { + return hash; + } + + seen->push_back(this); + + hash = hash_combine(hash, uint32_t(kind_)); + for (const auto& d : decorations_) { + hash = hash_combine(hash, d); + } + + switch (kind_) { +#define DeclareKindCase(type) \ + case k##type: \ + hash = As##type()->ComputeExtraStateHash(hash, seen); \ + break + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(NodePayloadArrayAMDX); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(CooperativeMatrixKHR); + DeclareKindCase(CooperativeVectorNV); + DeclareKindCase(RayQueryKHR); + DeclareKindCase(HitObjectNV); + DeclareKindCase(TensorLayoutNV); + DeclareKindCase(TensorViewNV); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + break; + } + + seen->pop_back(); + return hash; +} + +size_t Type::HashValue() const { + SeenTypes seen; + return ComputeHashValue(0, &seen); +} + +uint64_t Type::NumberOfComponents() const { + switch (kind()) { + case kVector: + return AsVector()->element_count(); + case kMatrix: + return AsMatrix()->element_count(); + case kArray: { + Array::LengthInfo length_info = AsArray()->length_info(); + if (length_info.words[0] != Array::LengthInfo::kConstant) { + return UINT64_MAX; + } + assert(length_info.words.size() <= 3 && + "The size of the array could not fit size_t."); + uint64_t length = 0; + length |= length_info.words[1]; + if (length_info.words.size() > 2) { + length |= static_cast(length_info.words[2]) << 32; + } + return length; + } + case kRuntimeArray: + return UINT64_MAX; + case kStruct: + return AsStruct()->element_types().size(); + default: + return 0; + } +} + +bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { + const Integer* it = that->AsInteger(); + return it && width_ == it->width_ && signed_ == it->signed_ && + HasSameDecorations(that); +} + +std::string Integer::str() const { + std::ostringstream oss; + oss << (signed_ ? "s" : "u") << "int" << width_; + return oss.str(); +} + +size_t Integer::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_, signed_); +} + +bool Float::IsSameImpl(const Type* that, IsSameCache*) const { + const Float* ft = that->AsFloat(); + return ft && width_ == ft->width_ && encoding_ == ft->encoding_ && + HasSameDecorations(that); +} + +std::string Float::str() const { + std::ostringstream oss; + switch (encoding_) { + case spv::FPEncoding::BFloat16KHR: + assert(width_ == 16); + oss << "bfloat16"; + break; + default: + oss << "float" << width_; + break; + } + return oss.str(); +} + +size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_, encoding_); +} + +Vector::Vector(const Type* type, uint32_t count) + : Type(kVector), element_type_(type), count_(count) { + assert(type->AsBool() || type->AsInteger() || type->AsFloat()); +} + +bool Vector::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Vector* vt = that->AsVector(); + if (!vt) return false; + return count_ == vt->count_ && + element_type_->IsSameImpl(vt->element_type_, seen) && + HasSameDecorations(that); +} + +std::string Vector::str() const { + std::ostringstream oss; + oss << "<" << element_type_->str() << ", " << count_ << ">"; + return oss.str(); +} + +size_t Vector::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + // prefer form that doesn't require push/pop from stack: add state and + // make tail call. + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); +} + +Matrix::Matrix(const Type* type, uint32_t count) + : Type(kMatrix), element_type_(type), count_(count) { + assert(type->AsVector()); +} + +bool Matrix::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Matrix* mt = that->AsMatrix(); + if (!mt) return false; + return count_ == mt->count_ && + element_type_->IsSameImpl(mt->element_type_, seen) && + HasSameDecorations(that); +} + +std::string Matrix::str() const { + std::ostringstream oss; + oss << "<" << element_type_->str() << ", " << count_ << ">"; + return oss.str(); +} + +size_t Matrix::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); +} + +Image::Image(Type* type, spv::Dim dimen, uint32_t d, bool array, + bool multisample, uint32_t sampling, spv::ImageFormat f, + spv::AccessQualifier qualifier) + : Type(kImage), + sampled_type_(type), + dim_(dimen), + depth_(d), + arrayed_(array), + ms_(multisample), + sampled_(sampling), + format_(f), + access_qualifier_(qualifier) { + // TODO(antiagainst): check sampled_type +} + +bool Image::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Image* it = that->AsImage(); + if (!it) return false; + return dim_ == it->dim_ && depth_ == it->depth_ && arrayed_ == it->arrayed_ && + ms_ == it->ms_ && sampled_ == it->sampled_ && format_ == it->format_ && + access_qualifier_ == it->access_qualifier_ && + sampled_type_->IsSameImpl(it->sampled_type_, seen) && + HasSameDecorations(that); +} + +std::string Image::str() const { + std::ostringstream oss; + oss << "image(" << sampled_type_->str() << ", " << uint32_t(dim_) << ", " + << depth_ << ", " << arrayed_ << ", " << ms_ << ", " << sampled_ << ", " + << uint32_t(format_) << ", " << uint32_t(access_qualifier_) << ")"; + return oss.str(); +} + +size_t Image::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(dim_), depth_, arrayed_, ms_, sampled_, + uint32_t(format_), uint32_t(access_qualifier_)); + return sampled_type_->ComputeHashValue(hash, seen); +} + +bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const { + const SampledImage* sit = that->AsSampledImage(); + if (!sit) return false; + return image_type_->IsSameImpl(sit->image_type_, seen) && + HasSameDecorations(that); +} + +std::string SampledImage::str() const { + std::ostringstream oss; + oss << "sampled_image(" << image_type_->str() << ")"; + return oss.str(); +} + +size_t SampledImage::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return image_type_->ComputeHashValue(hash, seen); +} + +Array::Array(const Type* type, const Array::LengthInfo& length_info_arg) + : Type(kArray), element_type_(type), length_info_(length_info_arg) { + assert(type != nullptr); + assert(!type->AsVoid()); + // We always have a word to say which case we're in, followed + // by at least one more word. + assert(length_info_arg.words.size() >= 2); +} + +bool Array::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Array* at = that->AsArray(); + if (!at) return false; + bool is_same = element_type_->IsSameImpl(at->element_type_, seen); + is_same = is_same && HasSameDecorations(that); + is_same = is_same && (length_info_.words == at->length_info_.words); + return is_same; +} + +std::string Array::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << ", id(" << LengthId() << "), words("; + const char* spacer = ""; + for (auto w : length_info_.words) { + oss << spacer << w; + spacer = ","; + } + oss << ")]"; + return oss.str(); +} + +size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, length_info_.words); + return element_type_->ComputeHashValue(hash, seen); +} + +void Array::ReplaceElementType(const Type* type) { element_type_ = type; } + +Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id, + uint32_t length) const { + std::vector extra_words{LengthInfo::Case::kConstant, length}; + return {const_id, extra_words}; +} + +RuntimeArray::RuntimeArray(const Type* type) + : Type(kRuntimeArray), element_type_(type) { + assert(!type->AsVoid()); +} + +bool RuntimeArray::IsSameImpl(const Type* that, IsSameCache* seen) const { + const RuntimeArray* rat = that->AsRuntimeArray(); + if (!rat) return false; + return element_type_->IsSameImpl(rat->element_type_, seen) && + HasSameDecorations(that); +} + +std::string RuntimeArray::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << "]"; + return oss.str(); +} + +size_t RuntimeArray::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return element_type_->ComputeHashValue(hash, seen); +} + +void RuntimeArray::ReplaceElementType(const Type* type) { + element_type_ = type; +} + +NodePayloadArrayAMDX::NodePayloadArrayAMDX(const Type* type) + : Type(kNodePayloadArrayAMDX), element_type_(type) { + assert(!type->AsVoid()); +} + +bool NodePayloadArrayAMDX::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const NodePayloadArrayAMDX* rat = that->AsNodePayloadArrayAMDX(); + if (!rat) return false; + return element_type_->IsSameImpl(rat->element_type_, seen) && + HasSameDecorations(that); +} + +std::string NodePayloadArrayAMDX::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << "]"; + return oss.str(); +} + +size_t NodePayloadArrayAMDX::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + return element_type_->ComputeHashValue(hash, seen); +} + +void NodePayloadArrayAMDX::ReplaceElementType(const Type* type) { + element_type_ = type; +} + +Struct::Struct(const std::vector& types) + : Type(kStruct), element_types_(types) { + for (const auto* t : types) { + (void)t; + assert(!t->AsVoid()); + } +} + +void Struct::AddMemberDecoration(uint32_t index, + std::vector&& decoration) { + if (index >= element_types_.size()) { + assert(0 && "index out of bound"); + return; + } + + element_decorations_[index].push_back(std::move(decoration)); +} + +bool Struct::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Struct* st = that->AsStruct(); + if (!st) return false; + if (element_types_.size() != st->element_types_.size()) return false; + const auto size = element_decorations_.size(); + if (size != st->element_decorations_.size()) return false; + if (!HasSameDecorations(that)) return false; + + for (size_t i = 0; i < element_types_.size(); ++i) { + if (!element_types_[i]->IsSameImpl(st->element_types_[i], seen)) + return false; + } + for (const auto& p : element_decorations_) { + if (st->element_decorations_.count(p.first) == 0) return false; + if (!CompareTwoVectors(p.second, st->element_decorations_.at(p.first))) + return false; + } + return true; +} + +std::string Struct::str() const { + std::ostringstream oss; + oss << "{"; + const size_t count = element_types_.size(); + for (size_t i = 0; i < count; ++i) { + oss << element_types_[i]->str(); + if (i + 1 != count) oss << ", "; + } + oss << "}"; + return oss.str(); +} + +size_t Struct::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + for (auto* t : element_types_) { + hash = t->ComputeHashValue(hash, seen); + } + for (const auto& pair : element_decorations_) { + hash = hash_combine(hash, pair.first, pair.second); + } + return hash; +} + +bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const { + const Opaque* ot = that->AsOpaque(); + if (!ot) return false; + return name_ == ot->name_ && HasSameDecorations(that); +} + +std::string Opaque::str() const { + std::ostringstream oss; + oss << "opaque('" << name_ << "')"; + return oss.str(); +} + +size_t Opaque::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, name_); +} + +Pointer::Pointer(const Type* type, spv::StorageClass sc) + : Type(kPointer), pointee_type_(type), storage_class_(sc) {} + +bool Pointer::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Pointer* pt = that->AsPointer(); + if (!pt) return false; + if (storage_class_ != pt->storage_class_) return false; + auto p = seen->insert(std::make_pair(this, that->AsPointer())); + if (!p.second) { + return true; + } + if (pointee_type_ != nullptr && pt->pointee_type_ != nullptr) { + bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen); + seen->erase(p.first); + if (!same_pointee) { + return false; + } + } else { + seen->erase(p.first); + // Either both are untyped or it is mixed typed and untyped. + if (pointee_type_ != pt->pointee_type_) { + return false; + } + } + return HasSameDecorations(that); +} + +std::string Pointer::str() const { + std::ostringstream os; + if (pointee_type_) { + os << pointee_type_->str(); + } else { + os << "untyped_ptr"; + } + os << " " << static_cast(storage_class_) << "*"; + return os.str(); +} + +size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(storage_class_)); + if (pointee_type_) { + hash = pointee_type_->ComputeHashValue(hash, seen); + } + return hash; +} + +void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } + +Function::Function(const Type* ret_type, const std::vector& params) + : Type(kFunction), return_type_(ret_type), param_types_(params) {} + +Function::Function(const Type* ret_type, std::vector& params) + : Type(kFunction), return_type_(ret_type), param_types_(params) {} + +bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Function* ft = that->AsFunction(); + if (!ft) return false; + if (!return_type_->IsSameImpl(ft->return_type_, seen)) return false; + if (param_types_.size() != ft->param_types_.size()) return false; + for (size_t i = 0; i < param_types_.size(); ++i) { + if (!param_types_[i]->IsSameImpl(ft->param_types_[i], seen)) return false; + } + return HasSameDecorations(that); +} + +std::string Function::str() const { + std::ostringstream oss; + const size_t count = param_types_.size(); + oss << "("; + for (size_t i = 0; i < count; ++i) { + oss << param_types_[i]->str(); + if (i + 1 != count) oss << ", "; + } + oss << ") -> " << return_type_->str(); + return oss.str(); +} + +size_t Function::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + for (const auto* t : param_types_) { + hash = t->ComputeHashValue(hash, seen); + } + return return_type_->ComputeHashValue(hash, seen); +} + +void Function::SetReturnType(const Type* type) { return_type_ = type; } + +bool Pipe::IsSameImpl(const Type* that, IsSameCache*) const { + const Pipe* pt = that->AsPipe(); + if (!pt) return false; + return access_qualifier_ == pt->access_qualifier_ && HasSameDecorations(that); +} + +std::string Pipe::str() const { + std::ostringstream oss; + oss << "pipe(" << uint32_t(access_qualifier_) << ")"; + return oss.str(); +} + +size_t Pipe::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, uint32_t(access_qualifier_)); +} + +bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const { + const ForwardPointer* fpt = that->AsForwardPointer(); + if (!fpt) return false; + return (pointer_ && fpt->pointer_ ? *pointer_ == *fpt->pointer_ + : target_id_ == fpt->target_id_) && + storage_class_ == fpt->storage_class_ && HasSameDecorations(that); +} + +std::string ForwardPointer::str() const { + std::ostringstream oss; + oss << "forward_pointer("; + if (pointer_ != nullptr) { + oss << pointer_->str(); + } else { + oss << target_id_; + } + oss << ")"; + return oss.str(); +} + +size_t ForwardPointer::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, target_id_, uint32_t(storage_class_)); + if (pointer_) hash = pointer_->ComputeHashValue(hash, seen); + return hash; +} + +CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, + const uint32_t columns) + : Type(kCooperativeMatrixNV), + component_type_(type), + scope_id_(scope), + rows_id_(rows), + columns_id_(columns) { + assert(type != nullptr); + assert(scope != 0); + assert(rows != 0); + assert(columns != 0); +} + +std::string CooperativeMatrixNV::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_ + << ", " << columns_id_ << ">"; + return oss.str(); +} + +size_t CooperativeMatrixNV::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, scope_id_, rows_id_, columns_id_); + return component_type_->ComputeHashValue(hash, seen); +} + +bool CooperativeMatrixNV::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ && + columns_id_ == mt->columns_id_ && HasSameDecorations(that); +} + +CooperativeMatrixKHR::CooperativeMatrixKHR(const Type* type, + const uint32_t scope, + const uint32_t rows, + const uint32_t columns, + const uint32_t use) + : Type(kCooperativeMatrixKHR), + component_type_(type), + scope_id_(scope), + rows_id_(rows), + columns_id_(columns), + use_id_(use) { + assert(type != nullptr); + assert(scope != 0); + assert(rows != 0); + assert(columns != 0); +} + +std::string CooperativeMatrixKHR::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_ + << ", " << columns_id_ << ", " << use_id_ << ">"; + return oss.str(); +} + +size_t CooperativeMatrixKHR::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, scope_id_, rows_id_, columns_id_, use_id_); + return component_type_->ComputeHashValue(hash, seen); +} + +bool CooperativeMatrixKHR::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeMatrixKHR* mt = that->AsCooperativeMatrixKHR(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ && + columns_id_ == mt->columns_id_ && use_id_ == mt->use_id_ && + HasSameDecorations(that); +} + +TensorLayoutNV::TensorLayoutNV(const uint32_t dim, const uint32_t clamp_mode) + : Type(kTensorLayoutNV), dim_id_(dim), clamp_mode_id_(clamp_mode) {} + +std::string TensorLayoutNV::str() const { + std::ostringstream oss; + oss << "<" << dim_id_ << ", " << clamp_mode_id_ << ">"; + return oss.str(); +} + +size_t TensorLayoutNV::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, dim_id_, clamp_mode_id_); +} + +bool TensorLayoutNV::IsSameImpl(const Type* that, IsSameCache*) const { + const TensorLayoutNV* tl = that->AsTensorLayoutNV(); + if (!tl) return false; + return dim_id_ == tl->dim_id_ && clamp_mode_id_ == tl->clamp_mode_id_; +} + +TensorViewNV::TensorViewNV(const uint32_t dim, const uint32_t clamp_mode, + const std::vector& perm) + : Type(kTensorViewNV), + dim_id_(dim), + has_dimensions_id_(clamp_mode), + perm_(perm) {} + +std::string TensorViewNV::str() const { + std::ostringstream oss; + oss << "<" << dim_id_ << ", " << has_dimensions_id_; + for (auto p : perm_) { + oss << ", " << p; + } + oss << ">"; + return oss.str(); +} + +size_t TensorViewNV::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, dim_id_, has_dimensions_id_, perm_); +} + +bool TensorViewNV::IsSameImpl(const Type* that, IsSameCache*) const { + const TensorViewNV* tv = that->AsTensorViewNV(); + if (!tv) return false; + return dim_id_ == tv->dim_id_ && + has_dimensions_id_ == tv->has_dimensions_id_ && perm_ == tv->perm_; +} + +CooperativeVectorNV::CooperativeVectorNV(const Type* type, + const uint32_t components) + : Type(kCooperativeVectorNV), + component_type_(type), + components_(components) { + assert(type != nullptr); + assert(components != 0); +} + +std::string CooperativeVectorNV::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << components_ << ">"; + return oss.str(); +} + +size_t CooperativeVectorNV::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, components_); + return component_type_->ComputeHashValue(hash, seen); +} + +bool CooperativeVectorNV::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeVectorNV* mt = that->AsCooperativeVectorNV(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + components_ == mt->components_ && HasSameDecorations(that); +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/types.h b/engine/engine/3rdlibs/spirv-tools/source/opt/types.h new file mode 100644 index 00000000..9b12d5f9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/types.h @@ -0,0 +1,815 @@ +// Copyright (c) 2016 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file provides a class hierarchy for representing SPIR-V types. + +#ifndef SOURCE_OPT_TYPES_H_ +#define SOURCE_OPT_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/opt/instruction.h" +#include "source/util/small_vector.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +class Void; +class Bool; +class Integer; +class Float; +class Vector; +class Matrix; +class Image; +class Sampler; +class SampledImage; +class Array; +class RuntimeArray; +class NodePayloadArrayAMDX; +class Struct; +class Opaque; +class Pointer; +class Function; +class Event; +class DeviceEvent; +class ReserveId; +class Queue; +class Pipe; +class ForwardPointer; +class PipeStorage; +class NamedBarrier; +class AccelerationStructureNV; +class CooperativeMatrixNV; +class CooperativeMatrixKHR; +class CooperativeVectorNV; +class RayQueryKHR; +class HitObjectNV; +class TensorLayoutNV; +class TensorViewNV; + +// Abstract class for a SPIR-V type. It has a bunch of As() methods, +// which is used as a way to probe the actual . +class Type { + public: + typedef std::set> IsSameCache; + + using SeenTypes = spvtools::utils::SmallVector; + + // Available subtypes. + // + // When adding a new derived class of Type, please add an entry to the enum. + enum Kind { + kVoid, + kBool, + kInteger, + kFloat, + kVector, + kMatrix, + kImage, + kSampler, + kSampledImage, + kArray, + kRuntimeArray, + kNodePayloadArrayAMDX, + kStruct, + kOpaque, + kPointer, + kFunction, + kEvent, + kDeviceEvent, + kReserveId, + kQueue, + kPipe, + kForwardPointer, + kPipeStorage, + kNamedBarrier, + kAccelerationStructureNV, + kCooperativeMatrixNV, + kCooperativeMatrixKHR, + kCooperativeVectorNV, + kRayQueryKHR, + kHitObjectNV, + kTensorLayoutNV, + kTensorViewNV, + kLast + }; + + Type(Kind k) : kind_(k) {} + + virtual ~Type() = default; + + // Attaches a decoration directly on this type. + void AddDecoration(std::vector&& d) { + decorations_.push_back(std::move(d)); + } + // Returns the decorations on this type as a string. + std::string GetDecorationStr() const; + // Returns true if this type has exactly the same decorations as |that| type. + bool HasSameDecorations(const Type* that) const; + // Returns true if this type is exactly the same as |that| type, including + // decorations. + bool IsSame(const Type* that) const { + IsSameCache seen; + return IsSameImpl(that, &seen); + } + + // Returns true if this type is exactly the same as |that| type, including + // decorations. |seen| is the set of |Pointer*| pair that are currently being + // compared in a parent call to |IsSameImpl|. + virtual bool IsSameImpl(const Type* that, IsSameCache* seen) const = 0; + + // Returns a human-readable string to represent this type. + virtual std::string str() const = 0; + + Kind kind() const { return kind_; } + const std::vector>& decorations() const { + return decorations_; + } + + // Returns true if there is no decoration on this type. For struct types, + // returns true only when there is no decoration for both the struct type + // and the struct members. + virtual bool decoration_empty() const { return decorations_.empty(); } + + // Creates a clone of |this|. + std::unique_ptr Clone() const; + + // Returns a clone of |this| minus any decorations. + std::unique_ptr RemoveDecorations() const; + + // Returns true if this cannot hash to the same value as another type in the + // module. For example, structs are not unique types because the module could + // have two types + // + // %1 = OpTypeStruct %int + // %2 = OpTypeStruct %int + // + // The only way to distinguish these types is the result id. The type manager + // will hash them to the same value. + bool IsUniqueType() const; + + bool operator==(const Type& other) const; + + // Returns the hash value of this type. + size_t HashValue() const; + + size_t ComputeHashValue(size_t hash, SeenTypes* seen) const; + + // Returns the number of components in a composite type. Returns 0 for a + // non-composite type. + uint64_t NumberOfComponents() const; + + // A bunch of methods for casting this type to a given type. Returns this if + // the cast can be done, nullptr otherwise. + // clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(Void) + DeclareCastMethod(Bool) + DeclareCastMethod(Integer) + DeclareCastMethod(Float) + DeclareCastMethod(Vector) + DeclareCastMethod(Matrix) + DeclareCastMethod(Image) + DeclareCastMethod(Sampler) + DeclareCastMethod(SampledImage) + DeclareCastMethod(Array) + DeclareCastMethod(RuntimeArray) + DeclareCastMethod(NodePayloadArrayAMDX) + DeclareCastMethod(Struct) + DeclareCastMethod(Opaque) + DeclareCastMethod(Pointer) + DeclareCastMethod(Function) + DeclareCastMethod(Event) + DeclareCastMethod(DeviceEvent) + DeclareCastMethod(ReserveId) + DeclareCastMethod(Queue) + DeclareCastMethod(Pipe) + DeclareCastMethod(ForwardPointer) + DeclareCastMethod(PipeStorage) + DeclareCastMethod(NamedBarrier) + DeclareCastMethod(AccelerationStructureNV) + DeclareCastMethod(CooperativeMatrixNV) + DeclareCastMethod(CooperativeMatrixKHR) + DeclareCastMethod(CooperativeVectorNV) + DeclareCastMethod(RayQueryKHR) + DeclareCastMethod(HitObjectNV) + DeclareCastMethod(TensorLayoutNV) + DeclareCastMethod(TensorViewNV) +#undef DeclareCastMethod + +protected: + // Add any type-specific state to |hash| and returns new hash. + virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0; + + protected: + // Decorations attached to this type. Each decoration is encoded as a vector + // of uint32_t numbers. The first uint32_t number is the decoration value, + // and the rest are the parameters to the decoration (if any exist). + // The parameters can be either all literals or all ids depending on the + // decoration value. + std::vector> decorations_; + + private: + // Removes decorations on this type. For struct types, also removes element + // decorations. + virtual void ClearDecorations() { decorations_.clear(); } + + Kind kind_; +}; +// clang-format on + +class Integer : public Type { + public: + Integer(uint32_t w, bool is_signed) + : Type(kInteger), width_(w), signed_(is_signed) {} + Integer(const Integer&) = default; + + std::string str() const override; + + Integer* AsInteger() override { return this; } + const Integer* AsInteger() const override { return this; } + uint32_t width() const { return width_; } + bool IsSigned() const { return signed_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t width_; // bit width + bool signed_; // true if this integer is signed +}; + +class Float : public Type { + public: + Float(uint32_t w, spv::FPEncoding encoding = spv::FPEncoding::Max) + : Type(kFloat), width_(w), encoding_(encoding) {} + Float(const Float&) = default; + + std::string str() const override; + + Float* AsFloat() override { return this; } + const Float* AsFloat() const override { return this; } + uint32_t width() const { return width_; } + spv::FPEncoding encoding() const { return encoding_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t width_; // bit width + spv::FPEncoding encoding_; // FPEncoding +}; + +class Vector : public Type { + public: + Vector(const Type* element_type, uint32_t count); + Vector(const Vector&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t element_count() const { return count_; } + + Vector* AsVector() override { return this; } + const Vector* AsVector() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + uint32_t count_; +}; + +class Matrix : public Type { + public: + Matrix(const Type* element_type, uint32_t count); + Matrix(const Matrix&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t element_count() const { return count_; } + + Matrix* AsMatrix() override { return this; } + const Matrix* AsMatrix() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + uint32_t count_; +}; + +class Image : public Type { + public: + Image(Type* type, spv::Dim dimen, uint32_t d, bool array, bool multisample, + uint32_t sampling, spv::ImageFormat f, + spv::AccessQualifier qualifier = spv::AccessQualifier::ReadOnly); + Image(const Image&) = default; + + std::string str() const override; + + Image* AsImage() override { return this; } + const Image* AsImage() const override { return this; } + + const Type* sampled_type() const { return sampled_type_; } + spv::Dim dim() const { return dim_; } + uint32_t depth() const { return depth_; } + bool is_arrayed() const { return arrayed_; } + bool is_multisampled() const { return ms_; } + uint32_t sampled() const { return sampled_; } + spv::ImageFormat format() const { return format_; } + spv::AccessQualifier access_qualifier() const { return access_qualifier_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + Type* sampled_type_; + spv::Dim dim_; + uint32_t depth_; + bool arrayed_; + bool ms_; + uint32_t sampled_; + spv::ImageFormat format_; + spv::AccessQualifier access_qualifier_; +}; + +class SampledImage : public Type { + public: + SampledImage(Type* image) : Type(kSampledImage), image_type_(image) {} + SampledImage(const SampledImage&) = default; + + std::string str() const override; + + SampledImage* AsSampledImage() override { return this; } + const SampledImage* AsSampledImage() const override { return this; } + + const Type* image_type() const { return image_type_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + Type* image_type_; +}; + +class Array : public Type { + public: + // Data about the length operand, that helps us distinguish between one + // array length and another. + struct LengthInfo { + // The result id of the instruction defining the length. + const uint32_t id; + enum Case : uint32_t { + kConstant = 0, + kConstantWithSpecId = 1, + kDefiningId = 2 + }; + // Extra words used to distinshish one array length and another. + // - if OpConstant, then it's 0, then the words in the literal constant + // value. + // - if OpSpecConstant, then it's 1, then the SpecID decoration if there + // is one, followed by the words in the literal constant value. + // The spec might not be overridden, in which case we'll end up using + // the literal value. + // - Otherwise, it's an OpSpecConsant, and this 2, then the ID (again). + const std::vector words; + }; + + // Constructs an array type with given element and length. If the length + // is an OpSpecConstant, then |spec_id| should be its SpecId decoration. + Array(const Type* element_type, const LengthInfo& length_info_arg); + Array(const Array&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t LengthId() const { return length_info_.id; } + const LengthInfo& length_info() const { return length_info_; } + + Array* AsArray() override { return this; } + const Array* AsArray() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void ReplaceElementType(const Type* element_type); + LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + const LengthInfo length_info_; +}; + +class RuntimeArray : public Type { + public: + RuntimeArray(const Type* element_type); + RuntimeArray(const RuntimeArray&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + + RuntimeArray* AsRuntimeArray() override { return this; } + const RuntimeArray* AsRuntimeArray() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void ReplaceElementType(const Type* element_type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; +}; + +class NodePayloadArrayAMDX : public Type { + public: + NodePayloadArrayAMDX(const Type* element_type); + NodePayloadArrayAMDX(const NodePayloadArrayAMDX&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + + NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() override { return this; } + const NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void ReplaceElementType(const Type* element_type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; +}; + +class Struct : public Type { + public: + Struct(const std::vector& element_types); + Struct(const Struct&) = default; + + // Adds a decoration to the member at the given index. The first word is the + // decoration enum, and the remaining words, if any, are its operands. + void AddMemberDecoration(uint32_t index, std::vector&& decoration); + + std::string str() const override; + const std::vector& element_types() const { + return element_types_; + } + std::vector& element_types() { return element_types_; } + bool decoration_empty() const override { + return decorations_.empty() && element_decorations_.empty(); + } + + const std::map>>& + element_decorations() const { + return element_decorations_; + } + + Struct* AsStruct() override { return this; } + const Struct* AsStruct() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + void ClearDecorations() override { + decorations_.clear(); + element_decorations_.clear(); + } + + std::vector element_types_; + // We can attach decorations to struct members and that should not affect the + // underlying element type. So we need an extra data structure here to keep + // track of element type decorations. They must be stored in an ordered map + // because |GetExtraHashWords| will traverse the structure. It must have a + // fixed order in order to hash to the same value every time. + std::map>> element_decorations_; +}; + +class Opaque : public Type { + public: + Opaque(std::string n) : Type(kOpaque), name_(std::move(n)) {} + Opaque(const Opaque&) = default; + + std::string str() const override; + + Opaque* AsOpaque() override { return this; } + const Opaque* AsOpaque() const override { return this; } + + const std::string& name() const { return name_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + std::string name_; +}; + +class Pointer : public Type { + public: + Pointer(const Type* pointee, spv::StorageClass sc); + Pointer(const Pointer&) = default; + + std::string str() const override; + const Type* pointee_type() const { return pointee_type_; } + spv::StorageClass storage_class() const { return storage_class_; } + + bool is_untyped() const { return pointee_type_ == nullptr; } + + Pointer* AsPointer() override { return this; } + const Pointer* AsPointer() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void SetPointeeType(const Type* type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* pointee_type_; + spv::StorageClass storage_class_; +}; + +class Function : public Type { + public: + Function(const Type* ret_type, const std::vector& params); + Function(const Type* ret_type, std::vector& params); + Function(const Function&) = default; + + std::string str() const override; + + Function* AsFunction() override { return this; } + const Function* AsFunction() const override { return this; } + + const Type* return_type() const { return return_type_; } + const std::vector& param_types() const { return param_types_; } + std::vector& param_types() { return param_types_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + void SetReturnType(const Type* type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* return_type_; + std::vector param_types_; +}; + +class Pipe : public Type { + public: + Pipe(spv::AccessQualifier qualifier) + : Type(kPipe), access_qualifier_(qualifier) {} + Pipe(const Pipe&) = default; + + std::string str() const override; + + Pipe* AsPipe() override { return this; } + const Pipe* AsPipe() const override { return this; } + + spv::AccessQualifier access_qualifier() const { return access_qualifier_; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + spv::AccessQualifier access_qualifier_; +}; + +class ForwardPointer : public Type { + public: + ForwardPointer(uint32_t id, spv::StorageClass sc) + : Type(kForwardPointer), + target_id_(id), + storage_class_(sc), + pointer_(nullptr) {} + ForwardPointer(const ForwardPointer&) = default; + + uint32_t target_id() const { return target_id_; } + void SetTargetPointer(const Pointer* pointer) { pointer_ = pointer; } + spv::StorageClass storage_class() const { return storage_class_; } + const Pointer* target_pointer() const { return pointer_; } + + std::string str() const override; + + ForwardPointer* AsForwardPointer() override { return this; } + const ForwardPointer* AsForwardPointer() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t target_id_; + spv::StorageClass storage_class_; + const Pointer* pointer_; +}; + +class CooperativeMatrixNV : public Type { + public: + CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, const uint32_t columns); + CooperativeMatrixNV(const CooperativeMatrixNV&) = default; + + std::string str() const override; + + CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; } + const CooperativeMatrixNV* AsCooperativeMatrixNV() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* component_type() const { return component_type_; } + uint32_t scope_id() const { return scope_id_; } + uint32_t rows_id() const { return rows_id_; } + uint32_t columns_id() const { return columns_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t scope_id_; + const uint32_t rows_id_; + const uint32_t columns_id_; +}; + +class CooperativeMatrixKHR : public Type { + public: + CooperativeMatrixKHR(const Type* type, const uint32_t scope, + const uint32_t rows, const uint32_t columns, + const uint32_t use); + CooperativeMatrixKHR(const CooperativeMatrixKHR&) = default; + + std::string str() const override; + + CooperativeMatrixKHR* AsCooperativeMatrixKHR() override { return this; } + const CooperativeMatrixKHR* AsCooperativeMatrixKHR() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* component_type() const { return component_type_; } + uint32_t scope_id() const { return scope_id_; } + uint32_t rows_id() const { return rows_id_; } + uint32_t columns_id() const { return columns_id_; } + uint32_t use_id() const { return use_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t scope_id_; + const uint32_t rows_id_; + const uint32_t columns_id_; + const uint32_t use_id_; +}; + +class TensorLayoutNV : public Type { + public: + TensorLayoutNV(const uint32_t dim, const uint32_t clamp_mode); + TensorLayoutNV(const TensorLayoutNV&) = default; + + std::string str() const override; + + TensorLayoutNV* AsTensorLayoutNV() override { return this; } + const TensorLayoutNV* AsTensorLayoutNV() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + uint32_t dim_id() const { return dim_id_; } + uint32_t clamp_mode_id() const { return clamp_mode_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const uint32_t dim_id_; + const uint32_t clamp_mode_id_; +}; + +class TensorViewNV : public Type { + public: + TensorViewNV(const uint32_t dim, const uint32_t clamp_mode, + const std::vector& perm); + TensorViewNV(const TensorViewNV&) = default; + + std::string str() const override; + + TensorViewNV* AsTensorViewNV() override { return this; } + const TensorViewNV* AsTensorViewNV() const override { return this; } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + uint32_t dim_id() const { return dim_id_; } + uint32_t has_dimensions_id() const { return has_dimensions_id_; } + const std::vector& perm() const { return perm_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const uint32_t dim_id_; + const uint32_t has_dimensions_id_; + std::vector perm_; +}; + +class CooperativeVectorNV : public Type { + public: + CooperativeVectorNV(const Type* type, const uint32_t components); + CooperativeVectorNV(const CooperativeVectorNV&) = default; + + std::string str() const override; + + CooperativeVectorNV* AsCooperativeVectorNV() override { return this; } + const CooperativeVectorNV* AsCooperativeVectorNV() const override { + return this; + } + + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; + + const Type* component_type() const { return component_type_; } + uint32_t components() const { return components_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t components_; +}; + +#define DefineParameterlessType(type, name) \ + class type : public Type { \ + public: \ + type() : Type(k##type) {} \ + type(const type&) = default; \ + \ + std::string str() const override { return #name; } \ + \ + type* As##type() override { return this; } \ + const type* As##type() const override { return this; } \ + \ + size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \ + return hash; \ + } \ + \ + private: \ + bool IsSameImpl(const Type* that, IsSameCache*) const override { \ + return that->As##type() && HasSameDecorations(that); \ + } \ + } +DefineParameterlessType(Void, void); +DefineParameterlessType(Bool, bool); +DefineParameterlessType(Sampler, sampler); +DefineParameterlessType(Event, event); +DefineParameterlessType(DeviceEvent, device_event); +DefineParameterlessType(ReserveId, reserve_id); +DefineParameterlessType(Queue, queue); +DefineParameterlessType(PipeStorage, pipe_storage); +DefineParameterlessType(NamedBarrier, named_barrier); +DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV); +DefineParameterlessType(RayQueryKHR, rayQueryKHR); +DefineParameterlessType(HitObjectNV, hitObjectNV); +#undef DefineParameterlessType + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TYPES_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.cpp new file mode 100644 index 00000000..83dd438b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/unify_const_pass.h" + +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { + +// The trie that stores a bunch of result ids and, for a given instruction, +// searches the result id that has been defined with the same opcode, type and +// operands. +class ResultIdTrie { + public: + ResultIdTrie() : root_(new Node) {} + + // For a given instruction, extracts its opcode, type id and operand words + // as an array of keys, looks up the trie to find a result id which is stored + // with the same opcode, type id and operand words. If none of such result id + // is found, creates a trie node with those keys, stores the instruction's + // result id and returns that result id. If an existing result id is found, + // returns the existing result id. + uint32_t LookupEquivalentResultFor(const Instruction& inst) { + auto keys = GetLookUpKeys(inst); + auto* node = root_.get(); + for (uint32_t key : keys) { + node = node->GetOrCreateTrieNodeFor(key); + } + if (node->result_id() == 0) { + node->SetResultId(inst.result_id()); + } + return node->result_id(); + } + + private: + // The trie node to store result ids. + class Node { + public: + using TrieNodeMap = std::unordered_map>; + + Node() : result_id_(0), next_() {} + uint32_t result_id() const { return result_id_; } + + // Sets the result id stored in this node. + void SetResultId(uint32_t id) { result_id_ = id; } + + // Searches for the child trie node with the given key. If the node is + // found, returns that node. Otherwise creates an empty child node with + // that key and returns that newly created node. + Node* GetOrCreateTrieNodeFor(uint32_t key) { + auto iter = next_.find(key); + if (iter == next_.end()) { + // insert a new node and return the node. + return next_.insert(std::make_pair(key, MakeUnique())) + .first->second.get(); + } + return iter->second.get(); + } + + private: + // The result id stored in this node. 0 means this node is empty. + uint32_t result_id_; + // The mapping from the keys to the child nodes of this node. + TrieNodeMap next_; + }; + + // Returns a vector of the opcode followed by the words in the raw SPIR-V + // instruction encoding but without the result id. + std::vector GetLookUpKeys(const Instruction& inst) { + std::vector keys; + // Need to use the opcode, otherwise there might be a conflict with the + // following case when 's binary value equals xx's id: + // OpSpecConstantOp tt yy zz + // OpSpecConstantComposite tt xx yy zz; + keys.push_back(static_cast(inst.opcode())); + for (const auto& operand : inst) { + if (operand.type == SPV_OPERAND_TYPE_RESULT_ID) continue; + keys.insert(keys.end(), operand.words.cbegin(), operand.words.cend()); + } + return keys; + } + + std::unique_ptr root_; // The root node of the trie. +}; +} // namespace + +Pass::Status UnifyConstantPass::Process() { + bool modified = false; + ResultIdTrie defined_constants; + + for (Instruction *next_instruction, + *inst = &*(context()->types_values_begin()); + inst; inst = next_instruction) { + next_instruction = inst->NextNode(); + + // Do not handle the instruction when there are decorations upon the result + // id. + if (get_def_use_mgr()->GetAnnotations(inst->result_id()).size() != 0) { + continue; + } + + // The overall algorithm is to store the result ids of all the eligible + // constants encountered so far in a trie. For a constant defining + // instruction under consideration, use its opcode, result type id and + // words in operands as an array of keys to lookup the trie. If a result id + // can be found for that array of keys, a constant with exactly the same + // value must has been defined before, the constant under processing + // should be replaced by the constant previously defined. If no such result + // id can be found for that array of keys, this must be the first time a + // constant with its value be defined, we then create a new trie node to + // store the result id with the keys. When replacing a duplicated constant + // with a previously defined constant, all the uses of the duplicated + // constant, which must be placed after the duplicated constant defining + // instruction, will be updated. This way, the descendants of the + // previously defined constant and the duplicated constant will both refer + // to the previously defined constant. So that the operand ids which are + // used in key arrays will be the ids of the unified constants, when + // processing is up to a descendant. This makes comparing the key array + // always valid for judging duplication. + switch (inst->opcode()) { + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + case spv::Op::OpConstant: + case spv::Op::OpConstantNull: + case spv::Op::OpConstantSampler: + case spv::Op::OpConstantComposite: + // Only spec constants defined with OpSpecConstantOp and + // OpSpecConstantComposite should be processed in this pass. Spec + // constants defined with OpSpecConstant{|True|False} are decorated with + // 'SpecId' decoration and all of them should be treated as unique. + // 'SpecId' is not applicable to SpecConstants defined with + // OpSpecConstant{Op|Composite}, their values are not necessary to be + // unique. When all the operands/components are the same between two + // OpSpecConstant{Op|Composite} results, their result values must be the + // same so are unifiable. + case spv::Op::OpSpecConstantOp: + case spv::Op::OpSpecConstantComposite: { + uint32_t id = defined_constants.LookupEquivalentResultFor(*inst); + if (id != inst->result_id()) { + // The constant is a duplicated one, use the cached constant to + // replace the uses of this duplicated one, then turn it to nop. + context()->ReplaceAllUsesWith(inst->result_id(), id); + context()->KillInst(inst); + modified = true; + } + break; + } + default: + break; + } + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.h b/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.h new file mode 100644 index 00000000..f2b7897c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/unify_const_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_UNIFY_CONST_PASS_H_ +#define SOURCE_OPT_UNIFY_CONST_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class UnifyConstantPass : public Pass { + public: + const char* name() const override { return "unify-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_UNIFY_CONST_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.cpp new file mode 100644 index 00000000..1b439a6e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.cpp @@ -0,0 +1,780 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "upgrade_memory_model.h" + +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status UpgradeMemoryModel::Process() { + // TODO: This pass needs changes to support cooperative matrices. + if (context()->get_feature_mgr()->HasCapability( + spv::Capability::CooperativeMatrixNV)) { + return Pass::Status::SuccessWithoutChange; + } + + // Only update Logical GLSL450 to Logical VulkanKHR. + Instruction* memory_model = get_module()->GetMemoryModel(); + if (memory_model->GetSingleWordInOperand(0u) != + uint32_t(spv::AddressingModel::Logical) || + memory_model->GetSingleWordInOperand(1u) != + uint32_t(spv::MemoryModel::GLSL450)) { + return Pass::Status::SuccessWithoutChange; + } + + UpgradeMemoryModelInstruction(); + UpgradeInstructions(); + CleanupDecorations(); + UpgradeBarriers(); + UpgradeMemoryScope(); + + return Pass::Status::SuccessWithChange; +} + +void UpgradeMemoryModel::UpgradeMemoryModelInstruction() { + // Overall changes necessary: + // 1. Add the OpExtension. + // 2. Add the OpCapability. + // 3. Modify the memory model. + Instruction* memory_model = get_module()->GetMemoryModel(); + context()->AddCapability(MakeUnique( + context(), spv::Op::OpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, + {uint32_t(spv::Capability::VulkanMemoryModelKHR)}}})); + const std::string extension = "SPV_KHR_vulkan_memory_model"; + std::vector words = spvtools::utils::MakeVector(extension); + context()->AddExtension( + MakeUnique(context(), spv::Op::OpExtension, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_STRING, words}})); + memory_model->SetInOperand(1u, {uint32_t(spv::MemoryModel::VulkanKHR)}); +} + +void UpgradeMemoryModel::UpgradeInstructions() { + // Coherent and Volatile decorations are deprecated. Remove them and replace + // with flags on the memory/image operations. The decorations can occur on + // OpVariable, OpFunctionParameter (of pointer type) and OpStructType (member + // decoration). Trace from the decoration target(s) to the final memory/image + // instructions. Additionally, Workgroup storage class variables and function + // parameters are implicitly coherent in GLSL450. + + // Upgrade modf and frexp first since they generate new stores. + // In SPIR-V 1.4 or later, normalize OpCopyMemory* access operands. + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + if (inst->opcode() == spv::Op::OpExtInst) { + auto ext_inst = inst->GetSingleWordInOperand(1u); + if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) { + auto import = + get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (import->GetInOperand(0u).AsString() == "GLSL.std.450") { + UpgradeExtInst(inst); + } + } + } else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (inst->opcode() == spv::Op::OpCopyMemory || + inst->opcode() == spv::Op::OpCopyMemorySized) { + uint32_t start_operand = + inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u; + if (inst->NumInOperands() > start_operand) { + auto num_access_words = MemoryAccessNumWords( + inst->GetSingleWordInOperand(start_operand)); + if ((num_access_words + start_operand) == inst->NumInOperands()) { + // There is a single memory access operand. Duplicate it to have a + // separate operand for both source and target. + for (uint32_t i = 0; i < num_access_words; ++i) { + auto operand = inst->GetInOperand(start_operand + i); + inst->AddOperand(std::move(operand)); + } + } + } else { + // Add two memory access operands. + inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS, + {uint32_t(spv::MemoryAccessMask::MaskNone)}}); + inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS, + {uint32_t(spv::MemoryAccessMask::MaskNone)}}); + } + } + } + }); + } + + UpgradeMemoryAndImages(); + UpgradeAtomics(); +} + +void UpgradeMemoryModel::UpgradeMemoryAndImages() { + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + bool is_coherent = false; + bool is_volatile = false; + bool src_coherent = false; + bool src_volatile = false; + bool dst_coherent = false; + bool dst_volatile = false; + uint32_t start_operand = 0u; + spv::Scope scope = spv::Scope::QueueFamilyKHR; + spv::Scope src_scope = spv::Scope::QueueFamilyKHR; + spv::Scope dst_scope = spv::Scope::QueueFamilyKHR; + switch (inst->opcode()) { + case spv::Op::OpLoad: + case spv::Op::OpStore: + std::tie(is_coherent, is_volatile, scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + break; + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseRead: + case spv::Op::OpImageWrite: + std::tie(is_coherent, is_volatile, scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + std::tie(dst_coherent, dst_volatile, dst_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + std::tie(src_coherent, src_volatile, src_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(1u)); + break; + default: + break; + } + + switch (inst->opcode()) { + case spv::Op::OpLoad: + UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility, + kMemory); + break; + case spv::Op::OpStore: + UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability, + kMemory); + break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u; + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // There are guaranteed to be two memory access operands at this + // point so treat source and target separately. + uint32_t num_access_words = MemoryAccessNumWords( + inst->GetSingleWordInOperand(start_operand)); + UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile, + kAvailability, kMemory); + UpgradeFlags(inst, start_operand + num_access_words, src_coherent, + src_volatile, kVisibility, kMemory); + } else { + UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile, + kAvailability, kMemory); + UpgradeFlags(inst, start_operand, src_coherent, src_volatile, + kVisibility, kMemory); + } + break; + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseRead: + UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage); + break; + case spv::Op::OpImageWrite: + UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability, + kImage); + break; + default: + break; + } + + // |is_coherent| is never used for the same instructions as + // |src_coherent| and |dst_coherent|. + if (is_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}}); + } + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // There are two memory access operands. The first is for the target and + // the second is for the source. + if (dst_coherent || src_coherent) { + start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u; + std::vector new_operands; + uint32_t num_access_words = + MemoryAccessNumWords(inst->GetSingleWordInOperand(start_operand)); + // The flags were already updated so subtract if we're adding a + // scope. + if (dst_coherent) --num_access_words; + for (uint32_t i = 0; i < start_operand + num_access_words; ++i) { + new_operands.push_back(inst->GetInOperand(i)); + } + // Add the target scope if necessary. + if (dst_coherent) { + new_operands.push_back( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}}); + } + // Copy the remaining current operands. + for (uint32_t i = start_operand + num_access_words; + i < inst->NumInOperands(); ++i) { + new_operands.push_back(inst->GetInOperand(i)); + } + // Add the source scope if necessary. + if (src_coherent) { + new_operands.push_back( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}}); + } + inst->SetInOperands(std::move(new_operands)); + } + } else { + // According to SPV_KHR_vulkan_memory_model, if both available and + // visible flags are used the first scope operand is for availability + // (writes) and the second is for visibility (reads). + if (dst_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}}); + } + if (src_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}}); + } + } + }); + } +} + +void UpgradeMemoryModel::UpgradeAtomics() { + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + if (spvOpcodeIsAtomicOp(inst->opcode())) { + bool unused_coherent = false; + bool is_volatile = false; + spv::Scope unused_scope = spv::Scope::QueueFamilyKHR; + std::tie(unused_coherent, is_volatile, unused_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0)); + + UpgradeSemantics(inst, 2u, is_volatile); + if (inst->opcode() == spv::Op::OpAtomicCompareExchange || + inst->opcode() == spv::Op::OpAtomicCompareExchangeWeak) { + UpgradeSemantics(inst, 3u, is_volatile); + } + } + }); + } +} + +void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst, + uint32_t in_operand, + bool is_volatile) { + if (!is_volatile) return; + + uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand); + const analysis::Constant* constant = + context()->get_constant_mgr()->FindDeclaredConstant(semantics_id); + const analysis::Integer* type = constant->type()->AsInteger(); + assert(type && type->width() == 32); + uint32_t value = 0; + if (type->IsSigned()) { + value = static_cast(constant->GetS32()); + } else { + value = constant->GetU32(); + } + + value |= uint32_t(spv::MemorySemanticsMask::Volatile); + auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value}); + auto new_semantics = + context()->get_constant_mgr()->GetDefiningInstruction(new_constant); + inst->SetInOperand(in_operand, {new_semantics->result_id()}); +} + +std::tuple UpgradeMemoryModel::GetInstructionAttributes( + uint32_t id) { + // |id| is a pointer used in a memory/image instruction. Need to determine if + // that pointer points to volatile or coherent memory. Workgroup storage + // class is implicitly coherent and cannot be decorated with volatile, so + // short circuit that case. + Instruction* inst = context()->get_def_use_mgr()->GetDef(id); + analysis::Type* type = context()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsPointer() && + type->AsPointer()->storage_class() == spv::StorageClass::Workgroup) { + return std::make_tuple(true, false, spv::Scope::Workgroup); + } + + bool is_coherent = false; + bool is_volatile = false; + std::unordered_set visited; + std::tie(is_coherent, is_volatile) = + TraceInstruction(context()->get_def_use_mgr()->GetDef(id), + std::vector(), &visited); + + return std::make_tuple(is_coherent, is_volatile, spv::Scope::QueueFamilyKHR); +} + +std::pair UpgradeMemoryModel::TraceInstruction( + Instruction* inst, std::vector indices, + std::unordered_set* visited) { + auto iter = cache_.find(std::make_pair(inst->result_id(), indices)); + if (iter != cache_.end()) { + return iter->second; + } + + if (!visited->insert(inst->result_id()).second) { + return std::make_pair(false, false); + } + + // Initialize the cache before |indices| is (potentially) modified. + auto& cached_result = cache_[std::make_pair(inst->result_id(), indices)]; + cached_result.first = false; + cached_result.second = false; + + bool is_coherent = false; + bool is_volatile = false; + switch (inst->opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpFunctionParameter: + is_coherent |= HasDecoration(inst, 0, spv::Decoration::Coherent); + is_volatile |= HasDecoration(inst, 0, spv::Decoration::Volatile); + if (!is_coherent || !is_volatile) { + bool type_coherent = false; + bool type_volatile = false; + std::tie(type_coherent, type_volatile) = + CheckType(inst->type_id(), indices); + is_coherent |= type_coherent; + is_volatile |= type_volatile; + } + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + // Store indices in reverse order. + for (uint32_t i = inst->NumInOperands() - 1; i > 0; --i) { + indices.push_back(inst->GetSingleWordInOperand(i)); + } + break; + case spv::Op::OpPtrAccessChain: + // Store indices in reverse order. Skip the |Element| operand. + for (uint32_t i = inst->NumInOperands() - 1; i > 1; --i) { + indices.push_back(inst->GetSingleWordInOperand(i)); + } + break; + default: + break; + } + + // No point searching further. + if (is_coherent && is_volatile) { + cached_result.first = true; + cached_result.second = true; + return std::make_pair(true, true); + } + + // Variables and function parameters are sources. Continue searching until we + // reach them. + if (inst->opcode() != spv::Op::OpVariable && + inst->opcode() != spv::Op::OpFunctionParameter) { + inst->ForEachInId([this, &is_coherent, &is_volatile, &indices, + &visited](const uint32_t* id_ptr) { + Instruction* op_inst = context()->get_def_use_mgr()->GetDef(*id_ptr); + const analysis::Type* type = + context()->get_type_mgr()->GetType(op_inst->type_id()); + if (type && + (type->AsPointer() || type->AsImage() || type->AsSampledImage())) { + bool operand_coherent = false; + bool operand_volatile = false; + std::tie(operand_coherent, operand_volatile) = + TraceInstruction(op_inst, indices, visited); + is_coherent |= operand_coherent; + is_volatile |= operand_volatile; + } + }); + } + + cached_result.first = is_coherent; + cached_result.second = is_volatile; + return std::make_pair(is_coherent, is_volatile); +} + +std::pair UpgradeMemoryModel::CheckType( + uint32_t type_id, const std::vector& indices) { + bool is_coherent = false; + bool is_volatile = false; + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + assert(type_inst->opcode() == spv::Op::OpTypePointer); + Instruction* element_inst = context()->get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(1u)); + for (int i = (int)indices.size() - 1; i >= 0; --i) { + if (is_coherent && is_volatile) break; + + if (element_inst->opcode() == spv::Op::OpTypePointer) { + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(1u)); + } else if (element_inst->opcode() == spv::Op::OpTypeStruct) { + uint32_t index = indices.at(i); + Instruction* index_inst = context()->get_def_use_mgr()->GetDef(index); + assert(index_inst->opcode() == spv::Op::OpConstant); + uint64_t value = GetIndexValue(index_inst); + is_coherent |= HasDecoration(element_inst, static_cast(value), + spv::Decoration::Coherent); + is_volatile |= HasDecoration(element_inst, static_cast(value), + spv::Decoration::Volatile); + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(static_cast(value))); + } else { + assert(spvOpcodeIsComposite(element_inst->opcode())); + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(0u)); + } + } + + if (!is_coherent || !is_volatile) { + bool remaining_coherent = false; + bool remaining_volatile = false; + std::tie(remaining_coherent, remaining_volatile) = + CheckAllTypes(element_inst); + is_coherent |= remaining_coherent; + is_volatile |= remaining_volatile; + } + + return std::make_pair(is_coherent, is_volatile); +} + +std::pair UpgradeMemoryModel::CheckAllTypes( + const Instruction* inst) { + std::unordered_set visited; + std::vector stack; + stack.push_back(inst); + + bool is_coherent = false; + bool is_volatile = false; + while (!stack.empty()) { + const Instruction* def = stack.back(); + stack.pop_back(); + + if (!visited.insert(def).second) continue; + + if (def->opcode() == spv::Op::OpTypeStruct) { + // Any member decorated with coherent and/or volatile is enough to have + // the related operation be flagged as coherent and/or volatile. + is_coherent |= HasDecoration(def, std::numeric_limits::max(), + spv::Decoration::Coherent); + is_volatile |= HasDecoration(def, std::numeric_limits::max(), + spv::Decoration::Volatile); + if (is_coherent && is_volatile) + return std::make_pair(is_coherent, is_volatile); + + // Check the subtypes. + for (uint32_t i = 0; i < def->NumInOperands(); ++i) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(i))); + } + } else if (spvOpcodeIsComposite(def->opcode())) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(0u))); + } else if (def->opcode() == spv::Op::OpTypePointer) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(1u))); + } + } + + return std::make_pair(is_coherent, is_volatile); +} + +uint64_t UpgradeMemoryModel::GetIndexValue(Instruction* index_inst) { + const analysis::Constant* index_constant = + context()->get_constant_mgr()->GetConstantFromInst(index_inst); + assert(index_constant->AsIntConstant()); + if (index_constant->type()->AsInteger()->IsSigned()) { + if (index_constant->type()->AsInteger()->width() == 32) { + return index_constant->GetS32(); + } else { + return index_constant->GetS64(); + } + } else { + if (index_constant->type()->AsInteger()->width() == 32) { + return index_constant->GetU32(); + } else { + return index_constant->GetU64(); + } + } +} + +bool UpgradeMemoryModel::HasDecoration(const Instruction* inst, uint32_t value, + spv::Decoration decoration) { + // If the iteration was terminated early then an appropriate decoration was + // found. + return !context()->get_decoration_mgr()->WhileEachDecoration( + inst->result_id(), (uint32_t)decoration, [value](const Instruction& i) { + if (i.opcode() == spv::Op::OpDecorate || + i.opcode() == spv::Op::OpDecorateId) { + return false; + } else if (i.opcode() == spv::Op::OpMemberDecorate) { + if (value == i.GetSingleWordInOperand(1u) || + value == std::numeric_limits::max()) + return false; + } + + return true; + }); +} + +void UpgradeMemoryModel::UpgradeFlags(Instruction* inst, uint32_t in_operand, + bool is_coherent, bool is_volatile, + OperationType operation_type, + InstructionType inst_type) { + if (!is_coherent && !is_volatile) return; + + uint32_t flags = 0; + if (inst->NumInOperands() > in_operand) { + flags |= inst->GetSingleWordInOperand(in_operand); + } + if (is_coherent) { + if (inst_type == kMemory) { + flags |= uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR); + if (operation_type == kVisibility) { + flags |= uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR); + } else { + flags |= uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR); + } + } else { + flags |= uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR); + if (operation_type == kVisibility) { + flags |= uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR); + } else { + flags |= uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR); + } + } + } + + if (is_volatile) { + if (inst_type == kMemory) { + flags |= uint32_t(spv::MemoryAccessMask::Volatile); + } else { + flags |= uint32_t(spv::ImageOperandsMask::VolatileTexelKHR); + } + } + + if (inst->NumInOperands() > in_operand) { + inst->SetInOperand(in_operand, {flags}); + } else if (inst_type == kMemory) { + inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, {flags}}); + } else { + inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_IMAGE, {flags}}); + } +} + +uint32_t UpgradeMemoryModel::GetScopeConstant(spv::Scope scope) { + analysis::Integer int_ty(32, false); + uint32_t int_id = context()->get_type_mgr()->GetTypeInstruction(&int_ty); + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + context()->get_type_mgr()->GetType(int_id), + {static_cast(scope)}); + return context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id(); +} + +void UpgradeMemoryModel::CleanupDecorations() { + // All of the volatile and coherent decorations have been dealt with, so now + // we can just remove them. + get_module()->ForEachInst([this](Instruction* inst) { + if (inst->result_id() != 0) { + context()->get_decoration_mgr()->RemoveDecorationsFrom( + inst->result_id(), [](const Instruction& dec) { + switch (dec.opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + if (spv::Decoration(dec.GetSingleWordInOperand(1u)) == + spv::Decoration::Coherent || + spv::Decoration(dec.GetSingleWordInOperand(1u)) == + spv::Decoration::Volatile) + return true; + break; + case spv::Op::OpMemberDecorate: + if (spv::Decoration(dec.GetSingleWordInOperand(2u)) == + spv::Decoration::Coherent || + spv::Decoration(dec.GetSingleWordInOperand(2u)) == + spv::Decoration::Volatile) + return true; + break; + default: + break; + } + return false; + }); + } + }); +} + +void UpgradeMemoryModel::UpgradeBarriers() { + std::vector barriers; + // Collects all the control barriers in |function|. Returns true if the + // function operates on the Output storage class. + ProcessFunction CollectBarriers = [this, &barriers](Function* function) { + bool operates_on_output = false; + for (auto& block : *function) { + block.ForEachInst([this, &barriers, + &operates_on_output](Instruction* inst) { + if (inst->opcode() == spv::Op::OpControlBarrier) { + barriers.push_back(inst); + } else if (!operates_on_output) { + // This instruction operates on output storage class if it is a + // pointer to output type or any input operand is a pointer to output + // type. + analysis::Type* type = + context()->get_type_mgr()->GetType(inst->type_id()); + if (type && type->AsPointer() && + type->AsPointer()->storage_class() == spv::StorageClass::Output) { + operates_on_output = true; + return; + } + inst->ForEachInId([this, &operates_on_output](uint32_t* id_ptr) { + Instruction* op_inst = + context()->get_def_use_mgr()->GetDef(*id_ptr); + analysis::Type* op_type = + context()->get_type_mgr()->GetType(op_inst->type_id()); + if (op_type && op_type->AsPointer() && + op_type->AsPointer()->storage_class() == + spv::StorageClass::Output) + operates_on_output = true; + }); + } + }); + } + return operates_on_output; + }; + + std::queue roots; + for (auto& e : get_module()->entry_points()) + if (spv::ExecutionModel(e.GetSingleWordInOperand(0u)) == + spv::ExecutionModel::TessellationControl) { + roots.push(e.GetSingleWordInOperand(1u)); + if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) { + for (auto barrier : barriers) { + // Add OutputMemoryKHR to the semantics of the barriers. + uint32_t semantics_id = barrier->GetSingleWordInOperand(2u); + Instruction* semantics_inst = + context()->get_def_use_mgr()->GetDef(semantics_id); + analysis::Type* semantics_type = + context()->get_type_mgr()->GetType(semantics_inst->type_id()); + uint64_t semantics_value = GetIndexValue(semantics_inst); + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + semantics_type, + {static_cast(semantics_value) | + uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)}); + barrier->SetInOperand(2u, {context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id()}); + } + } + barriers.clear(); + } +} + +void UpgradeMemoryModel::UpgradeMemoryScope() { + get_module()->ForEachInst([this](Instruction* inst) { + // Don't need to handle all the operations that take a scope. + // * Group operations can only be subgroup + // * Non-uniform can only be workgroup or subgroup + // * Named barriers are not supported by Vulkan + // * Workgroup ops (e.g. async_copy) have at most workgroup scope. + if (spvOpcodeIsAtomicOp(inst->opcode())) { + if (IsDeviceScope(inst->GetSingleWordInOperand(1))) { + inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)}); + } + } else if (inst->opcode() == spv::Op::OpControlBarrier) { + if (IsDeviceScope(inst->GetSingleWordInOperand(1))) { + inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)}); + } + } else if (inst->opcode() == spv::Op::OpMemoryBarrier) { + if (IsDeviceScope(inst->GetSingleWordInOperand(0))) { + inst->SetInOperand(0, {GetScopeConstant(spv::Scope::QueueFamilyKHR)}); + } + } + }); +} + +bool UpgradeMemoryModel::IsDeviceScope(uint32_t scope_id) { + const analysis::Constant* constant = + context()->get_constant_mgr()->FindDeclaredConstant(scope_id); + assert(constant && "Memory scope must be a constant"); + + const analysis::Integer* type = constant->type()->AsInteger(); + assert(type); + assert(type->width() == 32 || type->width() == 64); + if (type->width() == 32) { + if (type->IsSigned()) + return static_cast(constant->GetS32()) == spv::Scope::Device; + else + return static_cast(constant->GetU32()) == spv::Scope::Device; + } else { + if (type->IsSigned()) + return static_cast(constant->GetS64()) == spv::Scope::Device; + else + return static_cast(constant->GetU64()) == spv::Scope::Device; + } + + assert(false); + return false; +} + +void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) { + const bool is_modf = ext_inst->GetSingleWordInOperand(1u) == GLSLstd450Modf; + auto ptr_id = ext_inst->GetSingleWordInOperand(3u); + auto ptr_type_id = get_def_use_mgr()->GetDef(ptr_id)->type_id(); + auto pointee_type_id = + get_def_use_mgr()->GetDef(ptr_type_id)->GetSingleWordInOperand(1u); + auto element_type_id = ext_inst->type_id(); + std::vector element_types(2); + element_types[0] = context()->get_type_mgr()->GetType(element_type_id); + element_types[1] = context()->get_type_mgr()->GetType(pointee_type_id); + analysis::Struct struct_type(element_types); + uint32_t struct_id = + context()->get_type_mgr()->GetTypeInstruction(&struct_type); + // Change the operation + GLSLstd450 new_op = is_modf ? GLSLstd450ModfStruct : GLSLstd450FrexpStruct; + ext_inst->SetOperand(3u, {static_cast(new_op)}); + // Remove the pointer argument + ext_inst->RemoveOperand(5u); + // Set the type id to the new struct. + ext_inst->SetResultType(struct_id); + + // The result is now a struct of the original result. The zero'th element is + // old result and should replace the old result. The one'th element needs to + // be stored via a new instruction. + auto where = ext_inst->NextNode(); + InstructionBuilder builder( + context(), where, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + auto extract_0 = + builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0}); + context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id()); + // The extract's input was just changed to itself, so fix that. + extract_0->SetInOperand(0u, {ext_inst->result_id()}); + auto extract_1 = + builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1}); + builder.AddStore(ptr_id, extract_1->result_id()); +} + +uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) { + uint32_t result = 1; + if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result; + return result; +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.h b/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.h new file mode 100644 index 00000000..489436b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/upgrade_memory_model.h @@ -0,0 +1,150 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ +#define LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ + +#include +#include + +#include "pass.h" + +namespace spvtools { +namespace opt { + +// Hashing functor for the memoized result store. +struct CacheHash { + size_t operator()( + const std::pair>& item) const { + std::u32string to_hash; + to_hash.push_back(item.first); + for (auto i : item.second) to_hash.push_back(i); + return std::hash()(to_hash); + } +}; + +// Upgrades the memory model from Logical GLSL450 to Logical VulkanKHR. +// +// This pass remove deprecated decorations (Volatile and Coherent) and replaces +// them with new flags on individual instructions. It adds the Output storage +// class semantic to control barriers in tessellation control shaders that have +// an access to Output memory. +class UpgradeMemoryModel : public Pass { + public: + const char* name() const override { return "upgrade-memory-model"; } + Status Process() override; + + private: + // Used to indicate whether the operation performs an availability or + // visibility operation. + enum OperationType { kVisibility, kAvailability }; + + // Used to indicate whether the instruction is a memory or image instruction. + enum InstructionType { kMemory, kImage }; + + // Modifies the OpMemoryModel to use VulkanKHR. Adds the Vulkan memory model + // capability and extension. + void UpgradeMemoryModelInstruction(); + + // Upgrades memory, image and atomic instructions. + // Memory and image instructions convert coherent and volatile decorations + // into flags on the instruction. + // Atomic memory semantics convert volatile decoration into flags on the + // instruction. + void UpgradeInstructions(); + + // Upgrades memory and image operands for instructions that have them. + void UpgradeMemoryAndImages(); + + // Adds the volatile memory semantic if necessary. + void UpgradeAtomics(); + + // Returns whether |id| is coherent and/or volatile. + std::tuple GetInstructionAttributes(uint32_t id); + + // Traces |inst| to determine if it is coherent and/or volatile. + // |indices| tracks the access chain indices seen so far. + std::pair TraceInstruction(Instruction* inst, + std::vector indices, + std::unordered_set* visited); + + // Return true if |inst| is decorated with |decoration|. + // If |inst| is decorated by member decorations then either |value| must + // match the index or |value| must be a maximum allowable value. The max + // value allows any element to match. + bool HasDecoration(const Instruction* inst, uint32_t value, + spv::Decoration decoration); + + // Returns whether |type_id| indexed via |indices| is coherent and/or + // volatile. + std::pair CheckType(uint32_t type_id, + const std::vector& indices); + + // Returns whether any type/element under |inst| is coherent and/or volatile. + std::pair CheckAllTypes(const Instruction* inst); + + // Modifies the flags of |inst| to include the new flags for the Vulkan + // memory model. |operation_type| indicates whether flags should use + // MakeVisible or MakeAvailable variants. |inst_type| indicates whether the + // Pointer or Texel variants of flags should be used. + void UpgradeFlags(Instruction* inst, uint32_t in_operand, bool is_coherent, + bool is_volatile, OperationType operation_type, + InstructionType inst_type); + + // Modifies the semantics at |in_operand| of |inst| to include the volatile + // bit if |is_volatile| is true. + void UpgradeSemantics(Instruction* inst, uint32_t in_operand, + bool is_volatile); + + // Returns the result id for a constant for |scope|. + uint32_t GetScopeConstant(spv::Scope scope); + + // Returns the value of |index_inst|. |index_inst| must be an OpConstant of + // integer type.g + uint64_t GetIndexValue(Instruction* index_inst); + + // Removes coherent and volatile decorations. + void CleanupDecorations(); + + // For all tessellation control entry points, if there is an operation on + // Output storage class, then all barriers are modified to include the + // OutputMemoryKHR semantic. + void UpgradeBarriers(); + + // If the Vulkan memory model is specified, device scope actually means + // device scope. The memory scope must be modified to be QueueFamilyKHR + // scope. + void UpgradeMemoryScope(); + + // Returns true if |scope_id| is spv::Scope::Device. + bool IsDeviceScope(uint32_t scope_id); + + // Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with + // their struct versions. New extracts and a store are added in order to + // facilitate adding memory model flags. + void UpgradeExtInst(Instruction* modf); + + // Returns the number of words taken up by a memory access argument and its + // implied operands. + uint32_t MemoryAccessNumWords(uint32_t mask); + + // Caches the result of TraceInstruction. For a given result id and set of + // indices, stores whether that combination is coherent and/or volatile. + std::unordered_map>, + std::pair, CacheHash> + cache_; +}; +} // namespace opt +} // namespace spvtools +#endif // LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.cpp new file mode 100644 index 00000000..0c7e575e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.cpp @@ -0,0 +1,253 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/value_number_table.h" + +#include + +#include "source/opt/cfg.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +uint32_t ValueNumberTable::GetValueNumber(Instruction* inst) const { + assert(inst->result_id() != 0 && + "inst must have a result id to get a value number."); + + // Check if this instruction already has a value. + auto result_id_to_val = id_to_value_.find(inst->result_id()); + if (result_id_to_val != id_to_value_.end()) { + return result_id_to_val->second; + } + return 0; +} + +uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const { + return GetValueNumber(context()->get_def_use_mgr()->GetDef(id)); +} + +uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { + // If it already has a value return that. + uint32_t value = GetValueNumber(inst); + if (value != 0) { + return value; + } + + auto assign_new_number = [this](Instruction* i) { + const auto new_value = TakeNextValueNumber(); + id_to_value_[i->result_id()] = new_value; + return new_value; + }; + + // If the instruction has other side effects, then it must + // have its own value number. + if (!context()->IsCombinatorInstruction(inst) && + !inst->IsCommonDebugInstr()) { + return assign_new_number(inst); + } + + // OpSampledImage and OpImage must remain in the same basic block in which + // they are used, because of this we will assign each one it own value number. + switch (inst->opcode()) { + case spv::Op::OpSampledImage: + case spv::Op::OpImage: + case spv::Op::OpVariable: + return assign_new_number(inst); + default: + break; + } + + // A load that yields an image, sampler, or sampled image must remain in + // the same basic block. So assign it its own value number. + if (inst->IsLoad()) { + switch (context()->get_def_use_mgr()->GetDef(inst->type_id())->opcode()) { + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: + return assign_new_number(inst); + default: + break; + } + } + + // If it is a load from memory that can be modified, we have to assume the + // memory has been modified, so we give it a new value number. + // + // Note that this test will also handle volatile loads because they are not + // read only. However, if this is ever relaxed because we analyze stores, we + // will have to add a new case for volatile loads. + if (inst->IsLoad() && !inst->IsReadOnlyLoad()) { + return assign_new_number(inst); + } + + analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr(); + + // When we copy an object, the value numbers should be the same. + if (inst->opcode() == spv::Op::OpCopyObject && + dec_mgr->HaveTheSameDecorations(inst->result_id(), + inst->GetSingleWordInOperand(0))) { + value = GetValueNumber(inst->GetSingleWordInOperand(0)); + if (value != 0) { + id_to_value_[inst->result_id()] = value; + return value; + } + } + + // Phi nodes are a type of copy. If all of the inputs have the same value + // number, then we can assign the result of the phi the same value number. + if (inst->opcode() == spv::Op::OpPhi && inst->NumInOperands() > 0 && + dec_mgr->HaveTheSameDecorations(inst->result_id(), + inst->GetSingleWordInOperand(0))) { + value = GetValueNumber(inst->GetSingleWordInOperand(0)); + if (value != 0) { + for (uint32_t op = 2; op < inst->NumInOperands(); op += 2) { + if (value != GetValueNumber(inst->GetSingleWordInOperand(op))) { + value = 0; + break; + } + } + if (value != 0) { + id_to_value_[inst->result_id()] = value; + return value; + } + } + } + + // Replace all of the operands by their value number. The sign bit will be + // set to distinguish between an id and a value number. + Instruction value_ins(context(), inst->opcode(), inst->type_id(), + inst->result_id(), {}); + for (uint32_t o = 0; o < inst->NumInOperands(); ++o) { + const Operand& op = inst->GetInOperand(o); + if (spvIsIdType(op.type)) { + uint32_t id_value = op.words[0]; + auto use_id_to_val = id_to_value_.find(id_value); + if (use_id_to_val != id_to_value_.end()) { + id_value = (1 << 31) | use_id_to_val->second; + } + value_ins.AddOperand(Operand(op.type, {id_value})); + } else { + value_ins.AddOperand(Operand(op.type, op.words)); + } + } + + // TODO: Implement a normal form for opcodes that commute like integer + // addition. This will let us know that a+b is the same value as b+a. + + // Otherwise, we check if this value has been computed before. + auto value_iterator = instruction_to_value_.find(value_ins); + if (value_iterator != instruction_to_value_.end()) { + value = id_to_value_[value_iterator->first.result_id()]; + id_to_value_[inst->result_id()] = value; + return value; + } + + // If not, assign it a new value number. + value = TakeNextValueNumber(); + id_to_value_[inst->result_id()] = value; + instruction_to_value_[value_ins] = value; + return value; +} + +void ValueNumberTable::BuildDominatorTreeValueNumberTable() { + // First value number the headers. + for (auto& inst : context()->annotations()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->capabilities()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->types_values()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->module()->ext_inst_imports()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->module()->ext_inst_debuginfo()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (Function& func : *context()->module()) { + // For best results we want to traverse the code in reverse post order. + // This happens naturally because of the forward referencing rules. + for (BasicBlock& block : func) { + for (Instruction& inst : block) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + } + } +} + +bool ComputeSameValue::operator()(const Instruction& lhs, + const Instruction& rhs) const { + if (lhs.result_id() == 0 || rhs.result_id() == 0) { + return false; + } + + if (lhs.opcode() != rhs.opcode()) { + return false; + } + + if (lhs.type_id() != rhs.type_id()) { + return false; + } + + if (lhs.NumInOperands() != rhs.NumInOperands()) { + return false; + } + + for (uint32_t i = 0; i < lhs.NumInOperands(); ++i) { + if (lhs.GetInOperand(i) != rhs.GetInOperand(i)) { + return false; + } + } + + return lhs.context()->get_decoration_mgr()->HaveTheSameDecorations( + lhs.result_id(), rhs.result_id()); +} + +std::size_t ValueTableHash::operator()(const Instruction& inst) const { + // We hash the opcode and in-operands, not the result, because we want + // instructions that are the same except for the result to hash to the + // same value. + std::u32string h; + h.push_back(uint32_t(inst.opcode())); + h.push_back(inst.type_id()); + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + const auto& opnd = inst.GetInOperand(i); + for (uint32_t word : opnd.words) { + h.push_back(word); + } + } + return std::hash()(h); +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.h b/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.h new file mode 100644 index 00000000..39129ffa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/value_number_table.h @@ -0,0 +1,91 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_VALUE_NUMBER_TABLE_H_ +#define SOURCE_OPT_VALUE_NUMBER_TABLE_H_ + +#include +#include + +#include "source/opt/instruction.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// Returns true if the two instructions compute the same value. Used by the +// value number table to compare two instructions. +class ComputeSameValue { + public: + bool operator()(const Instruction& lhs, const Instruction& rhs) const; +}; + +// The hash function used in the value number table. +class ValueTableHash { + public: + std::size_t operator()(const Instruction& inst) const; +}; + +// This class implements the value number analysis. It is using a hash-based +// approach to value numbering. It is essentially doing dominator-tree value +// numbering described in +// +// Preston Briggs, Keith D. Cooper, and L. Taylor Simpson. 1997. Value +// numbering. Softw. Pract. Exper. 27, 6 (June 1997), 701-724. +// https://www.cs.rice.edu/~keith/Promo/CRPC-TR94517.pdf.gz +// +// The main difference is that because we do not perform redundancy elimination +// as we build the value number table, we do not have to deal with cleaning up +// the scope. +class ValueNumberTable { + public: + ValueNumberTable(IRContext* ctx) : context_(ctx), next_value_number_(1) { + BuildDominatorTreeValueNumberTable(); + } + + // Returns the value number of the value computed by |inst|. |inst| must have + // a result id that will hold the computed value. If no value number has been + // assigned to the result id, then the return value is 0. + uint32_t GetValueNumber(Instruction* inst) const; + + // Returns the value number of the value contain in |id|. Returns 0 if it + // has not been assigned a value number. + uint32_t GetValueNumber(uint32_t id) const; + + IRContext* context() const { return context_; } + + private: + // Assigns a value number to every result id in the module. + void BuildDominatorTreeValueNumberTable(); + + // Returns the new value number. + uint32_t TakeNextValueNumber() { return next_value_number_++; } + + // Assigns a new value number to the result of |inst| if it does not already + // have one. Return the value number for |inst|. |inst| must have a result + // id. + uint32_t AssignValueNumber(Instruction* inst); + + std::unordered_map + instruction_to_value_; + std::unordered_map id_to_value_; + IRContext* context_; + uint32_t next_value_number_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_VALUE_NUMBER_TABLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.cpp new file mode 100644 index 00000000..1e8d255d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.cpp @@ -0,0 +1,431 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/vector_dce.h" + +#include + +namespace spvtools { +namespace opt { +namespace { +constexpr uint32_t kExtractCompositeIdInIdx = 0; +constexpr uint32_t kInsertObjectIdInIdx = 0; +constexpr uint32_t kInsertCompositeIdInIdx = 1; +} // namespace + +Pass::Status VectorDCE::Process() { + bool modified = false; + for (Function& function : *get_module()) { + modified |= VectorDCEFunction(&function); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool VectorDCE::VectorDCEFunction(Function* function) { + LiveComponentMap live_components; + FindLiveComponents(function, &live_components); + return RewriteInstructions(function, live_components); +} + +void VectorDCE::FindLiveComponents(Function* function, + LiveComponentMap* live_components) { + std::vector work_list; + + // Prime the work list. We will assume that any instruction that does + // not result in a vector is live. + // + // Extending to structures and matrices is not as straight forward because of + // the nesting. We cannot simply us a bit vector to keep track of which + // components are live because of arbitrary nesting of structs. + function->ForEachInst( + [&work_list, this, live_components](Instruction* current_inst) { + if (current_inst->IsCommonDebugInstr()) { + return; + } + if (!HasVectorOrScalarResult(current_inst) || + !context()->IsCombinatorInstruction(current_inst)) { + MarkUsesAsLive(current_inst, all_components_live_, live_components, + &work_list); + } + }); + + // Process the work list propagating liveness. + for (uint32_t i = 0; i < work_list.size(); i++) { + WorkListItem current_item = work_list[i]; + Instruction* current_inst = current_item.instruction; + + switch (current_inst->opcode()) { + case spv::Op::OpCompositeExtract: + MarkExtractUseAsLive(current_inst, current_item.components, + live_components, &work_list); + break; + case spv::Op::OpCompositeInsert: + MarkInsertUsesAsLive(current_item, live_components, &work_list); + break; + case spv::Op::OpVectorShuffle: + MarkVectorShuffleUsesAsLive(current_item, live_components, &work_list); + break; + case spv::Op::OpCompositeConstruct: + MarkCompositeContructUsesAsLive(current_item, live_components, + &work_list); + break; + default: + if (current_inst->IsScalarizable()) { + MarkUsesAsLive(current_inst, current_item.components, live_components, + &work_list); + } else { + MarkUsesAsLive(current_inst, all_components_live_, live_components, + &work_list); + } + break; + } + } +} + +void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t operand_id = + current_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + if (HasVectorOrScalarResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + if (current_inst->NumInOperands() < 2) { + new_item.components = live_elements; + } else { + uint32_t element_index = current_inst->GetSingleWordInOperand(1); + uint32_t item_size = GetVectorComponentCount(operand_inst->type_id()); + if (element_index < item_size) { + new_item.components.Set(element_index); + } + } + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } +} + +void VectorDCE::MarkInsertUsesAsLive( + const VectorDCE::WorkListItem& current_item, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + if (current_item.instruction->NumInOperands() > 2) { + uint32_t insert_position = + current_item.instruction->GetSingleWordInOperand(2); + + // Add the elements of the composite object that are used. + uint32_t operand_id = current_item.instruction->GetSingleWordInOperand( + kInsertCompositeIdInIdx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components = current_item.components; + new_item.components.Clear(insert_position); + + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + + // Add the element being inserted if it is used. + if (current_item.components.Get(insert_position)) { + uint32_t obj_operand_id = + current_item.instruction->GetSingleWordInOperand( + kInsertObjectIdInIdx); + Instruction* obj_operand_inst = def_use_mgr->GetDef(obj_operand_id); + WorkListItem new_item_for_obj; + new_item_for_obj.instruction = obj_operand_inst; + new_item_for_obj.components.Set(0); + AddItemToWorkListIfNeeded(new_item_for_obj, live_components, work_list); + } + } else { + // If there are no indices, then this is a copy of the object being + // inserted. + uint32_t object_id = + current_item.instruction->GetSingleWordInOperand(kInsertObjectIdInIdx); + Instruction* object_inst = def_use_mgr->GetDef(object_id); + + WorkListItem new_item; + new_item.instruction = object_inst; + new_item.components = current_item.components; + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } +} + +void VectorDCE::MarkVectorShuffleUsesAsLive( + const WorkListItem& current_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + WorkListItem first_operand; + first_operand.instruction = + def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(0)); + WorkListItem second_operand; + second_operand.instruction = + def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1)); + + uint32_t size_of_first_operand = + GetVectorComponentCount(first_operand.instruction->type_id()); + uint32_t size_of_second_operand = + GetVectorComponentCount(second_operand.instruction->type_id()); + + for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands(); + ++in_op) { + uint32_t index = current_item.instruction->GetSingleWordInOperand(in_op); + if (current_item.components.Get(in_op - 2)) { + if (index < size_of_first_operand) { + first_operand.components.Set(index); + } else if (index - size_of_first_operand < size_of_second_operand) { + second_operand.components.Set(index - size_of_first_operand); + } + } + } + + AddItemToWorkListIfNeeded(first_operand, live_components, work_list); + AddItemToWorkListIfNeeded(second_operand, live_components, work_list); +} + +void VectorDCE::MarkCompositeContructUsesAsLive( + VectorDCE::WorkListItem work_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + uint32_t current_component = 0; + Instruction* current_inst = work_item.instruction; + uint32_t num_in_operands = current_inst->NumInOperands(); + for (uint32_t i = 0; i < num_in_operands; ++i) { + uint32_t id = current_inst->GetSingleWordInOperand(i); + Instruction* op_inst = def_use_mgr->GetDef(id); + + if (HasScalarResult(op_inst)) { + WorkListItem new_work_item; + new_work_item.instruction = op_inst; + if (work_item.components.Get(current_component)) { + new_work_item.components.Set(0); + } + AddItemToWorkListIfNeeded(new_work_item, live_components, work_list); + current_component++; + } else { + assert(HasVectorResult(op_inst)); + WorkListItem new_work_item; + new_work_item.instruction = op_inst; + uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id()); + + for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size; + op_vector_idx++, current_component++) { + if (work_item.components.Get(current_component)) { + new_work_item.components.Set(op_vector_idx); + } + } + AddItemToWorkListIfNeeded(new_work_item, live_components, work_list); + } + } +} + +void VectorDCE::MarkUsesAsLive( + Instruction* current_inst, const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + current_inst->ForEachInId([&work_list, &live_elements, this, live_components, + def_use_mgr](uint32_t* operand_id) { + Instruction* operand_inst = def_use_mgr->GetDef(*operand_id); + + if (HasVectorResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components = live_elements; + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } else if (HasScalarResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components.Set(0); + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } + }); +} + +bool VectorDCE::HasVectorOrScalarResult(const Instruction* inst) const { + return HasScalarResult(inst) || HasVectorResult(inst); +} + +bool VectorDCE::HasVectorResult(const Instruction* inst) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + if (inst->type_id() == 0) { + return false; + } + + const analysis::Type* current_type = type_mgr->GetType(inst->type_id()); + switch (current_type->kind()) { + case analysis::Type::kVector: + return true; + default: + return false; + } +} + +bool VectorDCE::HasScalarResult(const Instruction* inst) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + if (inst->type_id() == 0) { + return false; + } + + const analysis::Type* current_type = type_mgr->GetType(inst->type_id()); + switch (current_type->kind()) { + case analysis::Type::kBool: + case analysis::Type::kInteger: + case analysis::Type::kFloat: + return true; + default: + return false; + } +} + +uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) { + assert(type_id != 0 && + "Trying to get the vector element count, but the type id is 0"); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Vector* vector_type = type->AsVector(); + assert( + vector_type && + "Trying to get the vector element count, but the type is not a vector"); + return vector_type->element_count(); +} + +bool VectorDCE::RewriteInstructions( + Function* function, const VectorDCE::LiveComponentMap& live_components) { + bool modified = false; + + // Kill DebugValue in the middle of the instruction iteration will result + // in accessing a dangling pointer. We keep dead DebugValue instructions + // in |dead_dbg_value| to kill them once after the iteration. + std::vector dead_dbg_value; + + function->ForEachInst([&modified, this, live_components, + &dead_dbg_value](Instruction* current_inst) { + if (!context()->IsCombinatorInstruction(current_inst)) { + return; + } + + auto live_component = live_components.find(current_inst->result_id()); + if (live_component == live_components.end()) { + // If this instruction is not in live_components then it does not + // produce a vector, or it is never referenced and ADCE will remove + // it. No point in trying to differentiate. + return; + } + + // If no element in the current instruction is used replace it with an + // OpUndef. + if (live_component->second.Empty()) { + modified = true; + MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value); + uint32_t undef_id = this->Type2Undef(current_inst->type_id()); + context()->KillNamesAndDecorates(current_inst); + context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id); + context()->KillInst(current_inst); + return; + } + + switch (current_inst->opcode()) { + case spv::Op::OpCompositeInsert: + modified |= RewriteInsertInstruction( + current_inst, live_component->second, &dead_dbg_value); + break; + case spv::Op::OpCompositeConstruct: + // TODO: The members that are not live can be replaced by an undef + // or constant. This will remove uses of those values, and possibly + // create opportunities for ADCE. + break; + default: + // Do nothing. + break; + } + }); + for (auto* i : dead_dbg_value) context()->KillInst(i); + return modified; +} + +bool VectorDCE::RewriteInsertInstruction( + Instruction* current_inst, const utils::BitVector& live_components, + std::vector* dead_dbg_value) { + // If the value being inserted is not live, then we can skip the insert. + + if (current_inst->NumInOperands() == 2) { + // If there are no indices, then this is the same as a copy. + context()->KillNamesAndDecorates(current_inst->result_id()); + uint32_t object_id = + current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx); + context()->ReplaceAllUsesWith(current_inst->result_id(), object_id); + return true; + } + + uint32_t insert_index = current_inst->GetSingleWordInOperand(2); + if (!live_components.Get(insert_index)) { + MarkDebugValueUsesAsDead(current_inst, dead_dbg_value); + context()->KillNamesAndDecorates(current_inst->result_id()); + uint32_t composite_id = + current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx); + context()->ReplaceAllUsesWith(current_inst->result_id(), composite_id); + return true; + } + + // If the values already in the composite are not used, then replace it with + // an undef. + utils::BitVector temp = live_components; + temp.Clear(insert_index); + if (temp.Empty()) { + context()->ForgetUses(current_inst); + uint32_t undef_id = Type2Undef(current_inst->type_id()); + current_inst->SetInOperand(kInsertCompositeIdInIdx, {undef_id}); + context()->AnalyzeUses(current_inst); + return true; + } + + return false; +} + +void VectorDCE::MarkDebugValueUsesAsDead( + Instruction* composite, std::vector* dead_dbg_value) { + context()->get_def_use_mgr()->ForEachUser( + composite, [&dead_dbg_value](Instruction* use) { + if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) + dead_dbg_value->push_back(use); + }); +} + +void VectorDCE::AddItemToWorkListIfNeeded( + WorkListItem work_item, VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + Instruction* current_inst = work_item.instruction; + auto it = live_components->find(current_inst->result_id()); + if (it == live_components->end()) { + live_components->emplace( + std::make_pair(current_inst->result_id(), work_item.components)); + work_list->emplace_back(work_item); + } else { + if (it->second.Or(work_item.components)) { + work_list->emplace_back(work_item); + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.h b/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.h new file mode 100644 index 00000000..a55bda69 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/vector_dce.h @@ -0,0 +1,160 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_VECTOR_DCE_H_ +#define SOURCE_OPT_VECTOR_DCE_H_ + +#include +#include + +#include "source/opt/mem_pass.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +class VectorDCE : public MemPass { + private: + using LiveComponentMap = std::unordered_map; + + // According to the SPEC the maximum size for a vector is 16. See the data + // rules in the universal validation rules (section 2.16.1). + enum { kMaxVectorSize = 16 }; + + struct WorkListItem { + WorkListItem() : instruction(nullptr), components(kMaxVectorSize) {} + + Instruction* instruction; + utils::BitVector components; + }; + + public: + VectorDCE() : all_components_live_(kMaxVectorSize) { + for (uint32_t i = 0; i < kMaxVectorSize; i++) { + all_components_live_.Set(i); + } + } + + const char* name() const override { return "vector-dce"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisCFG | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisDecorations | + IRContext::kAnalysisDominatorAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Runs the vector dce pass on |function|. Returns true if |function| was + // modified. + bool VectorDCEFunction(Function* function); + + // Identifies the live components of the vectors that are results of + // instructions in |function|. The results are stored in |live_components|. + void FindLiveComponents(Function* function, + LiveComponentMap* live_components); + + // Rewrites instructions in |function| that are dead or partially dead. If an + // instruction does not have an entry in |live_components|, then it is not + // changed. Returns true if |function| was modified. + bool RewriteInstructions(Function* function, + const LiveComponentMap& live_components); + + // Makes all DebugValue instructions that use |composite| for their values as + // dead instructions by putting them into |dead_dbg_value|. + void MarkDebugValueUsesAsDead(Instruction* composite, + std::vector* dead_dbg_value); + + // Rewrites the OpCompositeInsert instruction |current_inst| to avoid + // unnecessary computes given that the only components of the result that are + // live are |live_components|. + // + // If the value being inserted is not live, then the result of |current_inst| + // is replaced by the composite input to |current_inst|. + // + // If the composite input to |current_inst| is not live, then it is replaced + // by and OpUndef in |current_inst|. + bool RewriteInsertInstruction(Instruction* current_inst, + const utils::BitVector& live_components, + std::vector* dead_dbg_value); + + // Returns true if the result of |inst| is a vector or a scalar. + bool HasVectorOrScalarResult(const Instruction* inst) const; + + // Returns true if the result of |inst| is a vector. + bool HasVectorResult(const Instruction* inst) const; + + // Returns true if the result of |inst| is a scalar. + bool HasScalarResult(const Instruction* inst) const; + + // Returns the number of elements in the vector type with id |type_id|. + uint32_t GetVectorComponentCount(uint32_t type_id); + + // Adds |work_item| to |work_list| if it is not already live according to + // |live_components|. |live_components| is updated to indicate that + // |work_item| is now live. + void AddItemToWorkListIfNeeded(WorkListItem work_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the components |live_elements| of the uses in |current_inst| as live + // according to |live_components|. If they were not live before, then they are + // added to |work_list|. + void MarkUsesAsLive(Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpVectorShuffle instruction in |current_item| as live + // based on the live components in |current_item|. If anything becomes live + // they are added to |work_list| and |live_components| is updated + // accordingly. + void MarkVectorShuffleUsesAsLive(const WorkListItem& current_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeInsert instruction in |current_item| as + // live based on the live components in |current_item|. If anything becomes + // live they are added to |work_list| and |live_components| is updated + // accordingly. + void MarkInsertUsesAsLive(const WorkListItem& current_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeExtract instruction |current_inst| as + // live. If anything becomes live they are added to |work_list| and + // |live_components| is updated accordingly. + void MarkExtractUseAsLive(const Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeConstruct instruction |current_inst| as + // live. If anything becomes live they are added to |work_list| and + // |live_components| is updated accordingly. + void MarkCompositeContructUsesAsLive(WorkListItem work_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // A BitVector that can always be used to say that all components of a vector + // are live. + utils::BitVector all_components_live_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_VECTOR_DCE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.cpp new file mode 100644 index 00000000..0cf954af --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/workaround1209.h" + +#include +#include +#include +#include + +namespace spvtools { +namespace opt { + +Pass::Status Workaround1209::Process() { + bool modified = false; + modified = RemoveOpUnreachableInLoops(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool Workaround1209::RemoveOpUnreachableInLoops() { + bool modified = false; + for (auto& func : *get_module()) { + std::list structured_order; + cfg()->ComputeStructuredOrder(&func, &*func.begin(), &structured_order); + + // Keep track of the loop merges. The top of the stack will always be the + // loop merge for the loop that immediately contains the basic block being + // processed. + std::stack loop_merges; + for (BasicBlock* bb : structured_order) { + if (!loop_merges.empty() && bb->id() == loop_merges.top()) { + loop_merges.pop(); + } + + if (bb->tail()->opcode() == spv::Op::OpUnreachable) { + if (!loop_merges.empty()) { + // We found an OpUnreachable inside a loop. + // Replace it with an unconditional branch to the loop merge. + context()->KillInst(&*bb->tail()); + std::unique_ptr new_branch( + new Instruction(context(), spv::Op::OpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {loop_merges.top()}}})); + context()->AnalyzeDefUse(&*new_branch); + bb->AddInstruction(std::move(new_branch)); + modified = true; + } + } else { + if (bb->GetLoopMergeInst()) { + loop_merges.push(bb->MergeBlockIdIfAny()); + } + } + } + } + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.h b/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.h new file mode 100644 index 00000000..9a1f88d9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/workaround1209.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_WORKAROUND1209_H_ +#define SOURCE_OPT_WORKAROUND1209_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class Workaround1209 : public Pass { + public: + const char* name() const override { return "workaround-1209"; } + Status Process() override; + + private: + // There is at least one driver where an OpUnreachable found in a loop is not + // handled correctly. Workaround that by changing the OpUnreachable into a + // branch to the loop merge. + // + // Returns true if the code changed. + bool RemoveOpUnreachableInLoops(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WORKAROUND1209_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.cpp b/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.cpp new file mode 100644 index 00000000..c0c6d622 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/wrap_opkill.h" + +#include "ir_builder.h" + +namespace spvtools { +namespace opt { + +Pass::Status WrapOpKill::Process() { + bool modified = false; + + auto func_to_process = + context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue(); + for (uint32_t func_id : func_to_process) { + Function* func = context()->GetFunction(func_id); + bool successful = func->WhileEachInst([this, &modified](Instruction* inst) { + const auto opcode = inst->opcode(); + if ((opcode == spv::Op::OpKill) || + (opcode == spv::Op::OpTerminateInvocation)) { + modified = true; + if (!ReplaceWithFunctionCall(inst)) { + return false; + } + } + return true; + }); + + if (!successful) { + return Status::Failure; + } + } + + if (opkill_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opkill_function_)); + } + if (opterminateinvocation_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opterminateinvocation_function_)); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) { + assert((inst->opcode() == spv::Op::OpKill || + inst->opcode() == spv::Op::OpTerminateInvocation) && + "|inst| must be an OpKill or OpTerminateInvocation instruction."); + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t func_id = GetKillingFuncId(inst->opcode()); + if (func_id == 0) { + return false; + } + Instruction* call_inst = + ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}); + if (call_inst == nullptr) { + return false; + } + call_inst->UpdateDebugInfoFrom(inst); + + Instruction* return_inst = nullptr; + uint32_t return_type_id = GetOwningFunctionsReturnType(inst); + if (return_type_id != GetVoidTypeId()) { + Instruction* undef = + ir_builder.AddNullaryOp(return_type_id, spv::Op::OpUndef); + if (undef == nullptr) { + return false; + } + return_inst = + ir_builder.AddUnaryOp(0, spv::Op::OpReturnValue, undef->result_id()); + } else { + return_inst = ir_builder.AddNullaryOp(0, spv::Op::OpReturn); + } + + if (return_inst == nullptr) { + return false; + } + + context()->KillInst(inst); + return true; +} + +uint32_t WrapOpKill::GetVoidTypeId() { + if (void_type_id_ != 0) { + return void_type_id_; + } + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + void_type_id_ = type_mgr->GetTypeInstruction(&void_type); + return void_type_id_; +} + +uint32_t WrapOpKill::GetVoidFunctionTypeId() { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + const analysis::Type* registered_void_type = + type_mgr->GetRegisteredType(&void_type); + + analysis::Function func_type(registered_void_type, {}); + return type_mgr->GetTypeInstruction(&func_type); +} + +uint32_t WrapOpKill::GetKillingFuncId(spv::Op opcode) { + // Parameterize by opcode + assert(opcode == spv::Op::OpKill || opcode == spv::Op::OpTerminateInvocation); + + std::unique_ptr* const killing_func = + (opcode == spv::Op::OpKill) ? &opkill_function_ + : &opterminateinvocation_function_; + + if (*killing_func != nullptr) { + return (*killing_func)->result_id(); + } + + uint32_t killing_func_id = TakeNextId(); + if (killing_func_id == 0) { + return 0; + } + + uint32_t void_type_id = GetVoidTypeId(); + if (void_type_id == 0) { + return 0; + } + + // Generate the function start instruction + std::unique_ptr func_start(new Instruction( + context(), spv::Op::OpFunction, void_type_id, killing_func_id, {})); + func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}}); + func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}}); + (*killing_func).reset(new Function(std::move(func_start))); + + // Generate the function end instruction + std::unique_ptr func_end( + new Instruction(context(), spv::Op::OpFunctionEnd, 0, 0, {})); + (*killing_func)->SetFunctionEnd(std::move(func_end)); + + // Create the one basic block for the function. + uint32_t lab_id = TakeNextId(); + if (lab_id == 0) { + return 0; + } + std::unique_ptr label_inst( + new Instruction(context(), spv::Op::OpLabel, 0, lab_id, {})); + std::unique_ptr bb(new BasicBlock(std::move(label_inst))); + + // Add the OpKill to the basic block + std::unique_ptr kill_inst( + new Instruction(context(), opcode, 0, 0, {})); + bb->AddInstruction(std::move(kill_inst)); + + // Add the bb to the function + (*killing_func)->AddBasicBlock(std::move(bb)); + + // Add the function to the module. + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + (*killing_func)->ForEachInst([this](Instruction* inst) { + context()->AnalyzeDefUse(inst); + }); + } + + if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + for (BasicBlock& basic_block : *(*killing_func)) { + context()->set_instr_block(basic_block.GetLabelInst(), &basic_block); + for (Instruction& inst : basic_block) { + context()->set_instr_block(&inst, &basic_block); + } + } + } + + return (*killing_func)->result_id(); +} + +uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return 0; + } + + Function* func = bb->GetParent(); + return func->type_id(); +} + +} // namespace opt +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.h b/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.h new file mode 100644 index 00000000..c9eb8887 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/opt/wrap_opkill.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_WRAP_OPKILL_H_ +#define SOURCE_OPT_WRAP_OPKILL_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class WrapOpKill : public Pass { + public: + WrapOpKill() : void_type_id_(0) {} + + const char* name() const override { return "wrap-opkill"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces the OpKill or OpTerminateInvocation instruction |inst| with a + // function call to a function that contains a single instruction, a clone of + // |inst|. An OpUnreachable instruction will be placed after the function + // call. Return true if successful. + bool ReplaceWithFunctionCall(Instruction* inst); + + // Returns the id of the void type. + uint32_t GetVoidTypeId(); + + // Returns the id of the function type for a void function with no parameters. + uint32_t GetVoidFunctionTypeId(); + + // Return the id of a function that has return type void, has no parameters, + // and contains a single instruction, which is |opcode|, either OpKill or + // OpTerminateInvocation. Returns 0 if the function could not be generated. + uint32_t GetKillingFuncId(spv::Op opcode); + + // Returns the id of the return type for the function that contains |inst|. + // Returns 0 if |inst| is not in a function. + uint32_t GetOwningFunctionsReturnType(Instruction* inst); + + // The id of the void type. If its value is 0, then the void type has not + // been found or created yet. + uint32_t void_type_id_; + + // The function that is a single instruction, which is an OpKill. The + // function has a void return type and takes no parameters. If the function is + // |nullptr|, then the function has not been generated. + std::unique_ptr opkill_function_; + // The function that is a single instruction, which is an + // OpTerminateInvocation. The function has a void return type and takes no + // parameters. If the function is |nullptr|, then the function has not been + // generated. + std::unique_ptr opterminateinvocation_function_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WRAP_OPKILL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.cpp b/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.cpp new file mode 100644 index 00000000..cc33f8ba --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains utility functions for spv_parsed_operand_t. + +#include "source/parsed_operand.h" + +#include +#include "source/util/hex_float.h" + +namespace spvtools { + +void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, + const spv_parsed_operand_t& operand) { + if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_LITERAL_FLOAT && + operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER) + return; + if (operand.num_words < 1) return; + // TODO(dneto): Support more than 64-bits at a time. + if (operand.num_words > 2) return; + + const uint32_t word = inst.words[operand.offset]; + if (operand.num_words == 1) { + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + *out << int32_t(word); + break; + case SPV_NUMBER_UNSIGNED_INT: + *out << word; + break; + case SPV_NUMBER_FLOATING: + if (operand.number_bit_width == 16) { + *out << spvtools::utils::FloatProxy( + uint16_t(word & 0xFFFF)); + } else { + // Assume 32-bit floats. + *out << spvtools::utils::FloatProxy(word); + } + break; + default: + break; + } + } else if (operand.num_words == 2) { + // Multi-word numbers are presented with lower order words first. + uint64_t bits = + uint64_t(word) | (uint64_t(inst.words[operand.offset + 1]) << 32); + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + *out << int64_t(bits); + break; + case SPV_NUMBER_UNSIGNED_INT: + *out << bits; + break; + case SPV_NUMBER_FLOATING: + // Assume only 64-bit floats. + *out << spvtools::utils::FloatProxy(bits); + break; + default: + break; + } + } +} +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.h b/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.h new file mode 100644 index 00000000..bab86110 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/parsed_operand.h @@ -0,0 +1,33 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_PARSED_OPERAND_H_ +#define SOURCE_PARSED_OPERAND_H_ + +#include + +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Emits the numeric literal representation of the given instruction operand +// to the stream. The operand must be of numeric type. If integral it may +// be up to 64 bits wide. If floating point, then it must be 16, 32, or 64 +// bits wide. +void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, + const spv_parsed_operand_t& operand); + +} // namespace spvtools + +#endif // SOURCE_PARSED_OPERAND_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/pch_source.cpp b/engine/engine/3rdlibs/spirv-tools/source/pch_source.cpp new file mode 100644 index 00000000..032e29ec --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/pch_source.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/pch_source.h b/engine/engine/3rdlibs/spirv-tools/source/pch_source.h new file mode 100644 index 00000000..6695ba26 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/pch_source.h @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validation_state.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/print.cpp b/engine/engine/3rdlibs/spirv-tools/source/print.cpp new file mode 100644 index 00000000..3143db17 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/print.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/print.h" + +#if defined(SPIRV_WINDOWS) +#include + +namespace spvtools { + +static void SetConsoleForegroundColorPrimary(HANDLE hConsole, WORD color) { + // Get screen buffer information from console handle + CONSOLE_SCREEN_BUFFER_INFO bufInfo; + GetConsoleScreenBufferInfo(hConsole, &bufInfo); + + // Get background color + color = WORD(color | (bufInfo.wAttributes & 0xfff0)); + + // Set foreground color + SetConsoleTextAttribute(hConsole, color); +} + +static void SetConsoleForegroundColor(WORD color) { + SetConsoleForegroundColorPrimary(GetStdHandle(STD_OUTPUT_HANDLE), color); + SetConsoleForegroundColorPrimary(GetStdHandle(STD_ERROR_HANDLE), color); +} + +clr::reset::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(0xf); + return ""; + } + return "\x1b[0m"; +} + +clr::grey::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_INTENSITY); + return ""; + } + return "\x1b[1;30m"; +} + +clr::red::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_RED); + return ""; + } + return "\x1b[31m"; +} + +clr::green::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_GREEN); + return ""; + } + return "\x1b[32m"; +} + +clr::yellow::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_RED | FOREGROUND_GREEN); + return ""; + } + return "\x1b[33m"; +} + +clr::blue::operator const char*() { + // Blue all by itself is hard to see against a black background (the + // default on command shell), or a medium blue background (the default + // on PowerShell). So increase its intensity. + + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY); + return ""; + } + return "\x1b[94m"; +} + +} // namespace spvtools +#else +namespace spvtools { + +clr::reset::operator const char*() { return "\x1b[0m"; } + +clr::grey::operator const char*() { return "\x1b[1;30m"; } + +clr::red::operator const char*() { return "\x1b[31m"; } + +clr::green::operator const char*() { return "\x1b[32m"; } + +clr::yellow::operator const char*() { return "\x1b[33m"; } + +clr::blue::operator const char*() { return "\x1b[34m"; } + +} // namespace spvtools +#endif diff --git a/engine/engine/3rdlibs/spirv-tools/source/print.h b/engine/engine/3rdlibs/spirv-tools/source/print.h new file mode 100644 index 00000000..f31ba38e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/print.h @@ -0,0 +1,75 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_PRINT_H_ +#define SOURCE_PRINT_H_ + +#include +#include + +namespace spvtools { + +// Wrapper for out stream selection. +class out_stream { + public: + out_stream() : pStream(nullptr) {} + explicit out_stream(std::stringstream& stream) : pStream(&stream) {} + + std::ostream& get() { + if (pStream) { + return *pStream; + } + return std::cout; + } + + private: + std::stringstream* pStream; +}; + +namespace clr { +// Resets console color. +struct reset { + operator const char*(); + bool isPrint; +}; +// Sets console color to grey. +struct grey { + operator const char*(); + bool isPrint; +}; +// Sets console color to red. +struct red { + operator const char*(); + bool isPrint; +}; +// Sets console color to green. +struct green { + operator const char*(); + bool isPrint; +}; +// Sets console color to yellow. +struct yellow { + operator const char*(); + bool isPrint; +}; +// Sets console color to blue. +struct blue { + operator const char*(); + bool isPrint; +}; +} // namespace clr + +} // namespace spvtools + +#endif // SOURCE_PRINT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/CMakeLists.txt b/engine/engine/3rdlibs/spirv-tools/source/reduce/CMakeLists.txt new file mode 100644 index 00000000..9ebe4183 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/CMakeLists.txt @@ -0,0 +1,113 @@ +# Copyright (c) 2018 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_REDUCE_SOURCES + change_operand_reduction_opportunity.h + change_operand_to_undef_reduction_opportunity.h + conditional_branch_to_simple_conditional_branch_opportunity_finder.h + conditional_branch_to_simple_conditional_branch_reduction_opportunity.h + merge_blocks_reduction_opportunity.h + merge_blocks_reduction_opportunity_finder.h + operand_to_const_reduction_opportunity_finder.h + operand_to_undef_reduction_opportunity_finder.h + operand_to_dominating_id_reduction_opportunity_finder.h + reducer.h + reduction_opportunity.h + reduction_opportunity_finder.h + reduction_pass.h + reduction_util.h + remove_block_reduction_opportunity.h + remove_block_reduction_opportunity_finder.h + remove_function_reduction_opportunity.h + remove_function_reduction_opportunity_finder.h + remove_instruction_reduction_opportunity.h + remove_selection_reduction_opportunity.h + remove_selection_reduction_opportunity_finder.h + remove_struct_member_reduction_opportunity.h + remove_unused_instruction_reduction_opportunity_finder.h + remove_unused_struct_member_reduction_opportunity_finder.h + simple_conditional_branch_to_branch_opportunity_finder.h + simple_conditional_branch_to_branch_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity.h + structured_construct_to_block_reduction_opportunity_finder.h + structured_loop_to_selection_reduction_opportunity.h + structured_loop_to_selection_reduction_opportunity_finder.h + + change_operand_reduction_opportunity.cpp + change_operand_to_undef_reduction_opportunity.cpp + conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp + conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp + merge_blocks_reduction_opportunity.cpp + merge_blocks_reduction_opportunity_finder.cpp + operand_to_const_reduction_opportunity_finder.cpp + operand_to_undef_reduction_opportunity_finder.cpp + operand_to_dominating_id_reduction_opportunity_finder.cpp + reducer.cpp + reduction_opportunity.cpp + reduction_opportunity_finder.cpp + reduction_pass.cpp + reduction_util.cpp + remove_block_reduction_opportunity.cpp + remove_block_reduction_opportunity_finder.cpp + remove_function_reduction_opportunity.cpp + remove_function_reduction_opportunity_finder.cpp + remove_instruction_reduction_opportunity.cpp + remove_selection_reduction_opportunity.cpp + remove_selection_reduction_opportunity_finder.cpp + remove_struct_member_reduction_opportunity.cpp + remove_unused_instruction_reduction_opportunity_finder.cpp + remove_unused_struct_member_reduction_opportunity_finder.cpp + simple_conditional_branch_to_branch_opportunity_finder.cpp + simple_conditional_branch_to_branch_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity.cpp + structured_construct_to_block_reduction_opportunity_finder.cpp + structured_loop_to_selection_reduction_opportunity.cpp + structured_loop_to_selection_reduction_opportunity_finder.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() + +spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce) + +add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_REDUCE_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-reduce) +target_include_directories(SPIRV-Tools-reduce + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# The reducer reuses a lot of functionality from the SPIRV-Tools library. +target_link_libraries(SPIRV-Tools-reduce + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY} + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-reduce) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-reduce EXPORT SPIRV-Tools-reduceTargets) + export(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake) + + spvtools_config_package_dir(SPIRV-Tools-reduce PACKAGE_DIR) + install(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-reduce) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-reduceConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp new file mode 100644 index 00000000..18e340bf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/change_operand_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool ChangeOperandReductionOpportunity::PreconditionHolds() { + // Check that the instruction still has the original operand. + return inst_->NumOperands() > operand_index_ && + inst_->GetOperand(operand_index_).words[0] == original_id_ && + inst_->GetOperand(operand_index_).type == original_type_; +} + +void ChangeOperandReductionOpportunity::Apply() { + inst_->SetOperand(operand_index_, {new_id_}); + inst_->context()->get_def_use_mgr()->UpdateDefUse(inst_); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.h new file mode 100644 index 00000000..18e6ca1d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_reduction_opportunity.h @@ -0,0 +1,54 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace an id operand of an instruction with some other id. +class ChangeOperandReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to replace operand |operand_index| of |inst| + // with |new_id|. + ChangeOperandReductionOpportunity(opt::Instruction* inst, + uint32_t operand_index, uint32_t new_id) + : inst_(inst), + operand_index_(operand_index), + original_id_(inst->GetOperand(operand_index).words[0]), + original_type_(inst->GetOperand(operand_index).type), + new_id_(new_id) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* const inst_; + const uint32_t operand_index_; + const uint32_t original_id_; + const spv_operand_type_t original_type_; + const uint32_t new_id_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp new file mode 100644 index 00000000..7cc06a03 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +bool ChangeOperandToUndefReductionOpportunity::PreconditionHolds() { + // Check that the instruction still has the original operand. + return inst_->NumOperands() > operand_index_ && + inst_->GetOperand(operand_index_).words[0] == original_id_; +} + +void ChangeOperandToUndefReductionOpportunity::Apply() { + auto operand = inst_->GetOperand(operand_index_); + auto operand_id = operand.words[0]; + auto operand_id_def = context_->get_def_use_mgr()->GetDef(operand_id); + auto operand_type_id = operand_id_def->type_id(); + // The opportunity should not exist unless this holds. + assert(operand_type_id); + auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id); + inst_->SetOperand(operand_index_, {undef_id}); + context_->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h new file mode 100644 index 00000000..ffd3155b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace an id operand of an instruction with undef. +class ChangeOperandToUndefReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to replace operand |operand_index| of |inst| + // with undef. + ChangeOperandToUndefReductionOpportunity(opt::IRContext* context, + opt::Instruction* inst, + uint32_t operand_index) + : context_(context), + inst_(inst), + operand_index_(operand_index), + original_id_(inst->GetOperand(operand_index).words[0]) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Instruction* const inst_; + const uint32_t operand_index_; + const uint32_t original_id_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp new file mode 100644 index 00000000..93b51a11 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +std::vector> +ConditionalBranchToSimpleConditionalBranchOpportunityFinder:: + GetAvailableOpportunities(opt::IRContext* context, + uint32_t target_function) const { + std::vector> result; + + // Find the opportunities for redirecting all false targets before the + // opportunities for redirecting all true targets because the former + // opportunities disable the latter, and vice versa, and the efficiency of the + // reducer is improved by avoiding contiguous opportunities that disable one + // another. + for (bool redirect_to_true : {true, false}) { + // Consider every relevant function. + for (auto* function : GetTargetFunctions(context, target_function)) { + // Consider every block in the function. + for (auto& block : *function) { + // The terminator must be spv::Op::OpBranchConditional. + opt::Instruction* terminator = block.terminator(); + if (terminator->opcode() != spv::Op::OpBranchConditional) { + continue; + } + + uint32_t true_block_id = + terminator->GetSingleWordInOperand(kTrueBranchOperandIndex); + uint32_t false_block_id = + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex); + + // The conditional branch must not already be simplified. + if (true_block_id == false_block_id) { + continue; + } + + // The redirected target must not be a back-edge to a structured loop + // header. + uint32_t redirected_block_id = + redirect_to_true ? false_block_id : true_block_id; + uint32_t containing_loop_header = + context->GetStructuredCFGAnalysis()->ContainingLoop(block.id()); + // The structured CFG analysis does not include a loop header as part + // of the loop construct, but we want to include it, so handle this + // special case: + if (block.GetLoopMergeInst() != nullptr) { + containing_loop_header = block.id(); + } + if (redirected_block_id == containing_loop_header) { + continue; + } + + result.push_back( + MakeUnique< + ConditionalBranchToSimpleConditionalBranchReductionOpportunity>( + context, block.terminator(), redirect_to_true)); + } + } + } + return result; +} + +std::string +ConditionalBranchToSimpleConditionalBranchOpportunityFinder::GetName() const { + return "ConditionalBranchToSimpleConditionalBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h new file mode 100644 index 00000000..17af9b00 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to simplify conditional branches into simple +// conditional branches (conditional branches with one target). +class ConditionalBranchToSimpleConditionalBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp new file mode 100644 index 00000000..8304c30c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + opt::IRContext* context, + opt::Instruction* conditional_branch_instruction, bool redirect_to_true) + : context_(context), + conditional_branch_instruction_(conditional_branch_instruction), + redirect_to_true_(redirect_to_true) {} + +bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + PreconditionHolds() { + // Another opportunity may have already simplified this conditional branch, + // which should disable this opportunity. + return conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) != + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex); +} + +void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() { + uint32_t operand_to_modify = + redirect_to_true_ ? kFalseBranchOperandIndex : kTrueBranchOperandIndex; + uint32_t operand_to_copy = + redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex; + + auto old_successor_block_id = + conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_modify); + + // Do the branch redirection. + conditional_branch_instruction_->SetInOperand( + operand_to_modify, + {conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_copy)}); + + // The old successor block may have phi instructions; these will need to + // respect the change in edges. + AdaptPhiInstructionsForRemovedEdge( + context_->get_instr_block(conditional_branch_instruction_)->id(), + context_->cfg()->block(old_successor_block_id)); + + // We have changed the CFG. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h new file mode 100644 index 00000000..1f9cb6d1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to simplify a conditional branch to a simple conditional +// branch (a conditional branch with one target). +class ConditionalBranchToSimpleConditionalBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. If + // |redirect_to_true| is true, the false target will be changed to also point + // to the true target; otherwise, the true target will be changed to also + // point to the false target. + explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + opt::IRContext* context, opt::Instruction* conditional_branch_instruction, + bool redirect_to_true); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Instruction* conditional_branch_instruction_; + + // If true, the false target will be changed to point to the true target; + // otherwise, the true target will be changed to point to the false target. + bool redirect_to_true_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp new file mode 100644 index 00000000..e626d60b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/merge_blocks_reduction_opportunity.h" + +#include "source/opt/block_merge_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +MergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity( + opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) { + // Precondition: the terminator has to be OpBranch. + assert(block->terminator()->opcode() == spv::Op::OpBranch); + context_ = context; + function_ = function; + // Get the successor block associated with the OpBranch. + successor_block_ = + context->cfg()->block(block->terminator()->GetSingleWordInOperand(0)); +} + +bool MergeBlocksReductionOpportunity::PreconditionHolds() { + // Merge block opportunities can disable each other. + // Example: Given blocks: A->B->C. + // A is a loop header; B and C are blocks in the loop; C ends with OpReturn. + // There are two opportunities: B and C can be merged with their predecessors. + // Merge C. B now ends with OpReturn. We now just have: A->B. + // Merge B is now disabled, as this would lead to A, a loop header, ending + // with an OpReturn, which is invalid. + + const auto predecessors = context_->cfg()->preds(successor_block_->id()); + assert(1 == predecessors.size() && + "For a successor to be merged into its predecessor, exactly one " + "predecessor must be present."); + const uint32_t predecessor_id = predecessors[0]; + opt::BasicBlock* predecessor_block = + context_->get_instr_block(predecessor_id); + return opt::blockmergeutil::CanMergeWithSuccessor(context_, + predecessor_block); +} + +void MergeBlocksReductionOpportunity::Apply() { + // While the original block that targeted the successor may not exist anymore + // (it might have been merged with another block), some block must exist that + // targets the successor. Find it. + + const auto predecessors = context_->cfg()->preds(successor_block_->id()); + assert(1 == predecessors.size() && + "For a successor to be merged into its predecessor, exactly one " + "predecessor must be present."); + const uint32_t predecessor_id = predecessors[0]; + + // We need an iterator pointing to the predecessor, hence the loop. + for (auto bi = function_->begin(); bi != function_->end(); ++bi) { + if (bi->id() == predecessor_id) { + opt::blockmergeutil::MergeWithSuccessor(context_, function_, bi); + // Block merging changes the control flow graph, so invalidate it. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + return; + } + } + + assert(false && + "Unreachable: we should have found a block with the desired id."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h new file mode 100644 index 00000000..5c9180b9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to merge two blocks into one. +class MergeBlocksReductionOpportunity : public ReductionOpportunity { + public: + // Creates the opportunity to merge |block| with its successor, where |block| + // is inside |function|, and |context| is the enclosing IR context. + MergeBlocksReductionOpportunity(opt::IRContext* context, + opt::Function* function, + opt::BasicBlock* block); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Function* function_; + + // Rather than holding on to the block that can be merged with its successor, + // we hold on to its successor. This is because the predecessor block might + // get merged with *its* predecessor, and so will no longer exist, while the + // successor will continue to exist until this opportunity gets applied. + opt::BasicBlock* successor_block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..ea5e9dac --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/opt/block_merge_util.h" +#include "source/reduce/merge_blocks_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::string MergeBlocksReductionOpportunityFinder::GetName() const { + return "MergeBlocksReductionOpportunityFinder"; +} + +std::vector> +MergeBlocksReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every block in every function. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + // See whether it is possible to merge this block with its successor. + if (opt::blockmergeutil::CanMergeWithSuccessor(context, &block)) { + // It is, so record an opportunity to do this. + result.push_back(spvtools::MakeUnique( + context, function, &block)); + } + } + } + return result; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h new file mode 100644 index 00000000..df7a8bf1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to merge blocks together. +class MergeBlocksReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + MergeBlocksReductionOpportunityFinder() = default; + + ~MergeBlocksReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..c6196f36 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToConstReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + assert(result.empty()); + + // We first loop over all constants. This means that all the reduction + // opportunities to replace an operand with a particular constant will be + // contiguous, and in particular it means that multiple, incompatible + // reduction opportunities that try to replace the same operand with distinct + // constants are likely to be discontiguous. This is good because the + // reducer works in the spirit of delta debugging and tries applying large + // contiguous blocks of opportunities early on, and we want to avoid having a + // large block of incompatible opportunities if possible. + for (const auto& constant : context->GetConstants()) { + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + if (spvIsInIdType(operand.type)) { + const auto id = operand.words[0]; + auto def = context->get_def_use_mgr()->GetDef(id); + if (spvOpcodeIsConstant(def->opcode())) { + // The argument is already a constant. + continue; + } + if (def->opcode() == spv::Op::OpFunction) { + // The argument refers to a function, e.g. the function called + // by OpFunctionCall; avoid replacing this with a constant of + // the function's return type. + continue; + } + auto type_id = def->type_id(); + if (type_id) { + if (constant->type_id() == type_id) { + result.push_back( + MakeUnique( + &inst, index, constant->result_id())); + } + } + } + } + } + } + } + } + return result; +} + +std::string OperandToConstReductionOpportunityFinder::GetName() const { + return "OperandToConstReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h new file mode 100644 index 00000000..67267468 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to replace id operands of instructions with ids of +// constants. This reduces the extent to which ids of non-constants are used, +// paving the way for instructions that generate them to be eliminated. +class OperandToConstReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToConstReductionOpportunityFinder() = default; + + ~OperandToConstReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..ca3a99e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToDominatingIdReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Go through every instruction in every block, considering it as a potential + // dominator of other instructions. We choose this order for two reasons: + // + // (1) it is profitable for multiple opportunities to replace the same id x by + // different dominating ids y and z to be discontiguous, as they are + // incompatible. + // + // (2) We want to prioritise opportunities to replace an id with a more + // distant dominator. Intuitively, in a human-readable programming language + // if we have a complex expression e with many sub-expressions, we would like + // to prioritise replacing e with its smallest sub-expressions; generalising + // this idea to dominating ids this roughly corresponds to more distant + // dominators. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto dominating_block = function->begin(); + dominating_block != function->end(); ++dominating_block) { + for (auto& dominating_inst : *dominating_block) { + if (dominating_inst.HasResultId() && dominating_inst.type_id()) { + // Consider replacing any operand with matching type in a dominated + // instruction with the id generated by this instruction. + GetOpportunitiesForDominatingInst( + &result, &dominating_inst, dominating_block, function, context); + } + } + } + } + return result; +} + +void OperandToDominatingIdReductionOpportunityFinder:: + GetOpportunitiesForDominatingInst( + std::vector>* opportunities, + opt::Instruction* candidate_dominator, + opt::Function::iterator candidate_dominator_block, + opt::Function* function, opt::IRContext* context) const { + assert(candidate_dominator->HasResultId()); + assert(candidate_dominator->type_id()); + auto dominator_analysis = context->GetDominatorAnalysis(function); + // SPIR-V requires a block to precede all blocks it dominates, so it suffices + // to search from the candidate dominator block onwards. + for (auto block = candidate_dominator_block; block != function->end(); + ++block) { + if (!dominator_analysis->Dominates(&*candidate_dominator_block, &*block)) { + // If the candidate dominator block doesn't dominate this block then there + // cannot be any of the desired reduction opportunities in this block. + continue; + } + for (auto& inst : *block) { + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + if (spvIsInIdType(operand.type)) { + const auto id = operand.words[0]; + auto def = context->get_def_use_mgr()->GetDef(id); + assert(def); + if (!context->get_instr_block(def)) { + // The definition does not come from a block; e.g. it might be a + // constant. It is thus not relevant to this pass. + continue; + } + assert(!context->get_constant_mgr()->GetConstantFromInst(def) && + "We should not get here if the argument is a constant."); + if (def->type_id() != candidate_dominator->type_id()) { + // The types need to match. + continue; + } + if (candidate_dominator != def && + dominator_analysis->Dominates(candidate_dominator, def)) { + // A hit: the candidate dominator strictly dominates the definition. + opportunities->push_back( + MakeUnique( + &inst, index, candidate_dominator->result_id())); + } + } + } + } + } +} + +std::string OperandToDominatingIdReductionOpportunityFinder::GetName() const { + return "OperandToDominatingIdReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h new file mode 100644 index 00000000..5f333705 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h @@ -0,0 +1,56 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder that aims to bring to SPIR-V (and generalize) the idea from +// human-readable languages of e.g. finding opportunities to replace an +// expression with one of its arguments, (x + y) -> x, or with a reference to an +// identifier that was assigned to higher up in the program. The generalization +// of this is to replace an id with a different id of the same type defined in +// some dominating instruction. +// +// If id x is defined and then used several times, changing each use of x to +// some dominating definition may eventually allow the statement defining x +// to be eliminated by another pass. +class OperandToDominatingIdReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToDominatingIdReductionOpportunityFinder() = default; + + ~OperandToDominatingIdReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + void GetOpportunitiesForDominatingInst( + std::vector>* opportunities, + opt::Instruction* dominating_instruction, + opt::Function::iterator candidate_dominator_block, + opt::Function* function, opt::IRContext* context) const; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..c7bc1213 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToUndefReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + // Skip instructions that result in a pointer type. + auto type_id = inst.type_id(); + if (type_id) { + auto type_id_def = context->get_def_use_mgr()->GetDef(type_id); + if (type_id_def->opcode() == spv::Op::OpTypePointer) { + continue; + } + } + + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandToUndefReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + + if (spvIsInIdType(operand.type)) { + const auto operand_id = operand.words[0]; + auto operand_id_def = + context->get_def_use_mgr()->GetDef(operand_id); + + // Skip constant and undef operands. + // We always want the reducer to make the module "smaller", which + // ensures termination. + // Therefore, we assume: id > undef id > constant id. + if (spvOpcodeIsConstantOrUndef(operand_id_def->opcode())) { + continue; + } + + // Don't replace function operands with undef. + if (operand_id_def->opcode() == spv::Op::OpFunction) { + continue; + } + + // Only consider operands that have a type. + auto operand_type_id = operand_id_def->type_id(); + if (operand_type_id) { + auto operand_type_id_def = + context->get_def_use_mgr()->GetDef(operand_type_id); + + // Skip pointer operands. + if (operand_type_id_def->opcode() == spv::Op::OpTypePointer) { + continue; + } + + result.push_back( + MakeUnique( + context, &inst, index)); + } + } + } + } + } + } + return result; +} + +std::string OperandToUndefReductionOpportunityFinder::GetName() const { + return "OperandToUndefReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h new file mode 100644 index 00000000..a5c759e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to replace id operands of instructions with ids of +// undef. +class OperandToUndefReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToUndefReductionOpportunityFinder() = default; + + ~OperandToUndefReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.cpp new file mode 100644 index 00000000..61e74364 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source_reduce.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.h new file mode 100644 index 00000000..81bed208 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/pch_source_reduce.h @@ -0,0 +1,23 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "source/reduce/change_operand_reduction_opportunity.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "source/reduce/remove_instruction_reduction_opportunity.h" +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.cpp new file mode 100644 index 00000000..b752f415 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.cpp @@ -0,0 +1,249 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reducer.h" + +#include +#include + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" +#include "source/reduce/remove_block_reduction_opportunity_finder.h" +#include "source/reduce/remove_function_reduction_opportunity_finder.h" +#include "source/reduce/remove_selection_reduction_opportunity_finder.h" +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" +#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" +#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" +#include "source/spirv_reducer_options.h" + +namespace spvtools { +namespace reduce { + +Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {} + +Reducer::~Reducer() = default; + +void Reducer::SetMessageConsumer(MessageConsumer c) { + for (auto& pass : passes_) { + pass->SetMessageConsumer(c); + } + for (auto& pass : cleanup_passes_) { + pass->SetMessageConsumer(c); + } + consumer_ = std::move(c); +} + +void Reducer::SetInterestingnessFunction( + Reducer::InterestingnessFunction interestingness_function) { + interestingness_function_ = std::move(interestingness_function); +} + +Reducer::ReductionResultStatus Reducer::Run( + const std::vector& binary_in, std::vector* binary_out, + spv_const_reducer_options options, + spv_validator_options validator_options) { + std::vector current_binary(binary_in); + + spvtools::SpirvTools tools(target_env_); + assert(tools.IsValid() && "Failed to create SPIRV-Tools interface"); + + // Keeps track of how many reduction attempts have been tried. Reduction + // bails out if this reaches a given limit. + uint32_t reductions_applied = 0; + + // Initial state should be valid. + if (!tools.Validate(¤t_binary[0], current_binary.size(), + validator_options)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return Reducer::ReductionResultStatus::kInitialStateInvalid; + } + + // Initial state should be interesting. + if (!interestingness_function_(current_binary, reductions_applied)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial state was not interesting; stopping."); + return Reducer::ReductionResultStatus::kInitialStateNotInteresting; + } + + Reducer::ReductionResultStatus result = + RunPasses(&passes_, options, validator_options, tools, ¤t_binary, + &reductions_applied); + + if (result == Reducer::ReductionResultStatus::kComplete) { + // Cleanup passes. + result = RunPasses(&cleanup_passes_, options, validator_options, tools, + ¤t_binary, &reductions_applied); + } + + if (result == Reducer::ReductionResultStatus::kComplete) { + consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping."); + } + + // Even if the reduction has failed by this point (e.g. due to producing an + // invalid binary), we still update the output binary for better debugging. + *binary_out = std::move(current_binary); + + return result; +} + +void Reducer::AddDefaultReductionPasses() { + AddReductionPass( + spvtools::MakeUnique( + false)); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + StructuredConstructToBlockReductionOpportunityFinder>()); + AddReductionPass(spvtools::MakeUnique< + StructuredLoopToSelectionReductionOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique< + ConditionalBranchToSimpleConditionalBranchOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + RemoveUnusedStructMemberReductionOpportunityFinder>()); + + // Cleanup passes. + + AddCleanupReductionPass( + spvtools::MakeUnique( + true)); +} + +void Reducer::AddReductionPass( + std::unique_ptr finder) { + passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); +} + +void Reducer::AddCleanupReductionPass( + std::unique_ptr finder) { + cleanup_passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); +} + +bool Reducer::ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options) { + return current_step >= options->step_limit; +} + +Reducer::ReductionResultStatus Reducer::RunPasses( + std::vector>* passes, + spv_const_reducer_options options, spv_validator_options validator_options, + const SpirvTools& tools, std::vector* current_binary, + uint32_t* const reductions_applied) { + // Determines whether, on completing one round of reduction passes, it is + // worthwhile trying a further round. + bool another_round_worthwhile = true; + + // Apply round after round of reduction passes until we hit the reduction + // step limit, or deem that another round is not going to be worthwhile. + while (!ReachedStepLimit(*reductions_applied, options) && + another_round_worthwhile) { + // At the start of a round of reduction passes, assume another round will + // not be worthwhile unless we find evidence to the contrary. + another_round_worthwhile = false; + + // Iterate through the available passes. + for (auto& pass : *passes) { + // If this pass hasn't reached its minimum granularity then it's + // worth eventually doing another round of reductions, in order to + // try this pass at a finer granularity. + another_round_worthwhile |= !pass->ReachedMinimumGranularity(); + + // Keep applying this pass at its current granularity until it stops + // working or we hit the reduction step limit. + consumer_(SPV_MSG_INFO, nullptr, {}, + ("Trying pass " + pass->GetName() + ".").c_str()); + do { + auto maybe_result = + pass->TryApplyReduction(*current_binary, options->target_function); + if (maybe_result.empty()) { + // For this round, the pass has no more opportunities (chunks) to + // apply, so move on to the next pass. + consumer_( + SPV_MSG_INFO, nullptr, {}, + ("Pass " + pass->GetName() + " did not make a reduction step.") + .c_str()); + break; + } + bool interesting = false; + std::stringstream stringstream; + (*reductions_applied)++; + stringstream << "Pass " << pass->GetName() << " made reduction step " + << *reductions_applied << "."; + consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str())); + if (!tools.Validate(&maybe_result[0], maybe_result.size(), + validator_options)) { + // The reduction step went wrong and an invalid binary was produced. + // By design, this shouldn't happen; this is a safeguard to stop an + // invalid binary from being regarded as interesting. + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reduction step produced an invalid binary."); + if (options->fail_on_validation_error) { + // In this mode, we fail, so we update the current binary so it is + // output for debugging. + *current_binary = std::move(maybe_result); + return Reducer::ReductionResultStatus::kStateInvalid; + } + } else if (interestingness_function_(maybe_result, + *reductions_applied)) { + // Success! The binary produced by this reduction step is + // interesting, so make it the binary of interest henceforth, and + // note that it's worth doing another round of reduction passes. + consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded."); + *current_binary = std::move(maybe_result); + interesting = true; + another_round_worthwhile = true; + } + // We must call this before the next call to TryApplyReduction. + pass->NotifyInteresting(interesting); + // Bail out if the reduction step limit has been reached. + } while (!ReachedStepLimit(*reductions_applied, options)); + } + } + + // Report whether reduction completed, or bailed out early due to reaching + // the step limit. + if (ReachedStepLimit(*reductions_applied, options)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reached reduction step limit; stopping."); + return Reducer::ReductionResultStatus::kReachedStepLimit; + } + + // The passes completed successfully, although we may still run more passes. + return Reducer::ReductionResultStatus::kComplete; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.h new file mode 100644 index 00000000..f3ba1806 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reducer.h @@ -0,0 +1,122 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCER_H_ +#define SOURCE_REDUCE_REDUCER_H_ + +#include +#include + +#include "source/reduce/reduction_pass.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// This class manages the process of applying a reduction -- parameterized by a +// number of reduction passes and an interestingness test, to a SPIR-V binary. +class Reducer { + public: + // Possible statuses that can result from running a reduction. + enum ReductionResultStatus { + kInitialStateNotInteresting, + kReachedStepLimit, + kComplete, + kInitialStateInvalid, + + // Returned when the fail-on-validation-error option is set and a + // reduction step yields a state that fails validation. + kStateInvalid, + }; + + // The type for a function that will take a binary and return true if and + // only if the binary is deemed interesting. (The function also takes an + // integer argument that will be incremented each time the function is + // called; this is for debugging purposes). + // + // The notion of "interesting" depends on what properties of the binary or + // tools that process the binary we are trying to maintain during reduction. + using InterestingnessFunction = + std::function&, uint32_t)>; + + // Constructs an instance with the given target |target_env|, which is used to + // decode the binary to be reduced later. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + // + // The constructed instance also needs to have an interestingness function + // set and some reduction passes added to it in order to be useful. + explicit Reducer(spv_target_env target_env); + + // Disables copy/move constructor/assignment operations. + Reducer(const Reducer&) = delete; + Reducer(Reducer&&) = delete; + Reducer& operator=(const Reducer&) = delete; + Reducer& operator=(Reducer&&) = delete; + + // Destructs this instance. + ~Reducer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Sets the function that will be used to decide whether a reduced binary + // turned out to be interesting. + void SetInterestingnessFunction( + InterestingnessFunction interestingness_function); + + // Adds all default reduction passes. + void AddDefaultReductionPasses(); + + // Adds a reduction pass based on the given finder to the sequence of passes + // that will be iterated over. + void AddReductionPass(std::unique_ptr finder); + + // Adds a cleanup reduction pass based on the given finder to the sequence of + // passes that will run after other passes. + void AddCleanupReductionPass( + std::unique_ptr finder); + + // Reduces the given SPIR-V module |binary_out|. + // The reduced binary ends up in |binary_out|. + // A status is returned. + ReductionResultStatus Run(const std::vector& binary_in, + std::vector* binary_out, + spv_const_reducer_options options, + spv_validator_options validator_options); + + private: + static bool ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options); + + ReductionResultStatus RunPasses( + std::vector>* passes, + spv_const_reducer_options options, + spv_validator_options validator_options, const SpirvTools& tools, + std::vector* current_binary, uint32_t* reductions_applied); + + const spv_target_env target_env_; + MessageConsumer consumer_; + InterestingnessFunction interestingness_function_; + std::vector> passes_; + std::vector> cleanup_passes_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.cpp new file mode 100644 index 00000000..77be7848 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +void ReductionOpportunity::TryToApply() { + if (PreconditionHolds()) { + Apply(); + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.h new file mode 100644 index 00000000..703a50a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity.h @@ -0,0 +1,47 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ + +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// Abstract class: an opportunity to apply a reducing transformation. +class ReductionOpportunity { + public: + ReductionOpportunity() = default; + virtual ~ReductionOpportunity() = default; + + // Returns true if this opportunity has not been disabled by the application + // of another conflicting opportunity. + virtual bool PreconditionHolds() = 0; + + // Applies the opportunity, mutating the module from which the opportunity was + // created. It is a no-op if PreconditionHolds() returns false. + void TryToApply(); + + protected: + // Applies the opportunity, mutating the module from which the opportunity was + // created. + // Precondition: PreconditionHolds() must return true. + virtual void Apply() = 0; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.cpp new file mode 100644 index 00000000..0bd253b8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +std::vector ReductionOpportunityFinder::GetTargetFunctions( + opt::IRContext* ir_context, uint32_t target_function) { + std::vector result; + for (auto& function : *ir_context->module()) { + if (!target_function || function.result_id() == target_function) { + result.push_back(&function); + } + } + assert((!target_function || !result.empty()) && + "Requested target function must exist."); + return result; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.h new file mode 100644 index 00000000..d95c832b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_opportunity_finder.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// Abstract class for finding opportunities for reducing a SPIR-V module. +class ReductionOpportunityFinder { + public: + ReductionOpportunityFinder() = default; + + virtual ~ReductionOpportunityFinder() = default; + + // Finds and returns the reduction opportunities relevant to this pass that + // could be applied to SPIR-V module |context|. + // + // If |target_function| is non-zero then the available opportunities will be + // restricted to only those opportunities that modify the function with result + // id |target_function|. + virtual std::vector> + GetAvailableOpportunities(opt::IRContext* context, + uint32_t target_function) const = 0; + + // Provides a name for the finder. + virtual std::string GetName() const = 0; + + protected: + // Requires that |target_function| is zero or the id of a function in + // |ir_context|. If |target_function| is zero, returns all the functions in + // |ir_context|. Otherwise, returns the function with id |target_function|. + // This allows fuzzer passes to restrict attention to a single function. + static std::vector GetTargetFunctions( + opt::IRContext* ir_context, uint32_t target_function); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.cpp new file mode 100644 index 00000000..c6d1ebfd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_pass.h" + +#include + +#include "source/opt/build_module.h" + +namespace spvtools { +namespace reduce { + +std::vector ReductionPass::TryApplyReduction( + const std::vector& binary, uint32_t target_function) { + // We represent modules as binaries because (a) attempts at reduction need to + // end up in binary form to be passed on to SPIR-V-consuming tools, and (b) + // when we apply a reduction step we need to do it on a fresh version of the + // module as if the reduction step proves to be uninteresting we need to + // backtrack; re-parsing from binary provides a very clean way of cloning the + // module. + std::unique_ptr context = + BuildModule(target_env_, consumer_, binary.data(), binary.size()); + assert(context); + + std::vector> opportunities = + finder_->GetAvailableOpportunities(context.get(), target_function); + + // There is no point in having a granularity larger than the number of + // opportunities, so reduce the granularity in this case. + if (granularity_ > opportunities.size()) { + granularity_ = std::max((uint32_t)1, (uint32_t)opportunities.size()); + } + + assert(granularity_ > 0); + + if (index_ >= opportunities.size()) { + // We have reached the end of the available opportunities and, therefore, + // the end of the round for this pass, so reset the index and decrease the + // granularity for the next round. Return an empty vector to signal the end + // of the round. + index_ = 0; + granularity_ = std::max((uint32_t)1, granularity_ / 2); + return std::vector(); + } + + for (uint32_t i = index_; + i < std::min(index_ + granularity_, (uint32_t)opportunities.size()); + ++i) { + opportunities[i]->TryToApply(); + } + + std::vector result; + context->module()->ToBinary(&result, false); + return result; +} + +void ReductionPass::SetMessageConsumer(MessageConsumer consumer) { + consumer_ = std::move(consumer); +} + +bool ReductionPass::ReachedMinimumGranularity() const { + assert(granularity_ != 0); + return granularity_ == 1; +} + +std::string ReductionPass::GetName() const { return finder_->GetName(); } + +void ReductionPass::NotifyInteresting(bool interesting) { + if (!interesting) { + index_ += granularity_; + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.h new file mode 100644 index 00000000..18361824 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_pass.h @@ -0,0 +1,86 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_PASS_H_ +#define SOURCE_REDUCE_REDUCTION_PASS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity_finder.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// Abstract class representing a reduction pass, which can be repeatedly +// invoked to find and apply particular reduction opportunities to a SPIR-V +// binary. In the spirit of delta debugging, a pass initially tries to apply +// large chunks of reduction opportunities, iterating through available +// opportunities at a given granularity. When an iteration over available +// opportunities completes, the granularity is reduced and iteration starts +// again, until the minimum granularity is reached. +class ReductionPass { + public: + // Constructs a reduction pass with a given target environment, |target_env|, + // and a given finder of reduction opportunities, |finder|. + explicit ReductionPass(const spv_target_env target_env, + std::unique_ptr finder) + : target_env_(target_env), + finder_(std::move(finder)), + index_(0), + granularity_(std::numeric_limits::max()) {} + + // Applies the reduction pass to the given binary by applying a "chunk" of + // reduction opportunities. Returns the new binary if a chunk was applied; in + // this case, before the next call the caller must invoke + // NotifyInteresting(...) to indicate whether the new binary is interesting. + // Returns an empty vector if there are no more chunks left to apply; in this + // case, the index will be reset and the granularity lowered for the next + // round. + // + // If |target_function| is non-zero, only reduction opportunities that + // simplify the internals of the function with result id |target_function| + // will be applied. + std::vector TryApplyReduction(const std::vector& binary, + uint32_t target_function); + + // Notifies the reduction pass whether the binary returned from + // TryApplyReduction is interesting, so that the next call to + // TryApplyReduction will avoid applying the same chunk of opportunities. + void NotifyInteresting(bool interesting); + + // Sets a consumer to which relevant messages will be directed. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns true if the granularity with which reduction opportunities are + // applied has reached a minimum. + bool ReachedMinimumGranularity() const; + + // Returns the name associated with this reduction pass (based on its + // associated finder). + std::string GetName() const; + + private: + const spv_target_env target_env_; + const std::unique_ptr finder_; + MessageConsumer consumer_; + uint32_t index_; + uint32_t granularity_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_PASS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.cpp new file mode 100644 index 00000000..c9882d5e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_util.h" + +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace reduce { + +const uint32_t kTrueBranchOperandIndex = 1; +const uint32_t kFalseBranchOperandIndex = 2; + +uint32_t FindOrCreateGlobalVariable(opt::IRContext* context, + uint32_t pointer_type_id) { + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != spv::Op::OpVariable) { + continue; + } + if (inst.type_id() == pointer_type_id) { + return inst.result_id(); + } + } + const uint32_t variable_id = context->TakeNextId(); + auto variable_inst = MakeUnique( + context, spv::Op::OpVariable, pointer_type_id, variable_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(context->get_type_mgr() + ->GetType(pointer_type_id) + ->AsPointer() + ->storage_class())}}})); + context->module()->AddGlobalValue(std::move(variable_inst)); + return variable_id; +} + +uint32_t FindOrCreateFunctionVariable(opt::IRContext* context, + opt::Function* function, + uint32_t pointer_type_id) { + // The pointer type of a function variable must have Function storage class. + assert(context->get_type_mgr() + ->GetType(pointer_type_id) + ->AsPointer() + ->storage_class() == spv::StorageClass::Function); + + // Go through the instructions in the function's first block until we find a + // suitable variable, or go past all the variables. + opt::BasicBlock::iterator iter = function->begin()->begin(); + for (;; ++iter) { + // We will either find a suitable variable, or find a non-variable + // instruction; we won't exhaust all instructions. + assert(iter != function->begin()->end()); + if (iter->opcode() != spv::Op::OpVariable) { + // If we see a non-variable, we have gone through all the variables. + break; + } + if (iter->type_id() == pointer_type_id) { + return iter->result_id(); + } + } + // At this point, iter refers to the first non-function instruction of the + // function's entry block. + const uint32_t variable_id = context->TakeNextId(); + auto variable_inst = MakeUnique( + context, spv::Op::OpVariable, pointer_type_id, variable_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(spv::StorageClass::Function)}}})); + iter->InsertBefore(std::move(variable_inst)); + return variable_id; +} + +uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id) { + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != spv::Op::OpUndef) { + continue; + } + if (inst.type_id() == type_id) { + return inst.result_id(); + } + } + const uint32_t undef_id = context->TakeNextId(); + auto undef_inst = + MakeUnique(context, spv::Op::OpUndef, type_id, undef_id, + opt::Instruction::OperandList()); + assert(undef_id == undef_inst->result_id()); + context->module()->AddGlobalValue(std::move(undef_inst)); + return undef_id; +} + +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block) { + to_block->ForEachPhiInst([&from_id](opt::Instruction* phi_inst) { + opt::Instruction::OperandList new_in_operands; + // Go through the OpPhi's input operands in (variable, parent) pairs. + for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) { + // Keep all pairs where the parent is not the block from which the edge + // is being removed. + if (phi_inst->GetInOperand(index + 1).words[0] != from_id) { + new_in_operands.push_back(phi_inst->GetInOperand(index)); + new_in_operands.push_back(phi_inst->GetInOperand(index + 1)); + } + } + phi_inst->SetInOperands(std::move(new_in_operands)); + }); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.h new file mode 100644 index 00000000..bcdb77cd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/reduction_util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_UTIL_H_ +#define SOURCE_REDUCE_REDUCTION_UTIL_H_ + +#include "spirv-tools/libspirv.hpp" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +extern const uint32_t kTrueBranchOperandIndex; +extern const uint32_t kFalseBranchOperandIndex; + +// Returns a global OpVariable of type |pointer_type_id|, adding one if none +// exist. +uint32_t FindOrCreateGlobalVariable(opt::IRContext* context, + uint32_t pointer_type_id); + +// Returns an OpVariable of type |pointer_type_id| declared in |function|, +// adding one if none exist. +uint32_t FindOrCreateFunctionVariable(opt::IRContext* context, opt::Function*, + uint32_t pointer_type_id); + +// Returns an OpUndef id from the global value list that is of the given type, +// adding one if it does not exist. +uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id); + +// Removes any components of |to_block|'s phi instructions relating to +// |from_id|. +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block); + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_UTIL_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp new file mode 100644 index 00000000..55e95769 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_block_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity( + opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) + : context_(context), function_(function), block_(block) { + // precondition: + assert(block_->begin() != block_->end() && + context_->get_def_use_mgr()->NumUsers(block_->id()) == 0 && + "RemoveBlockReductionOpportunity block must have 0 references"); +} + +bool RemoveBlockReductionOpportunity::PreconditionHolds() { + // Removing other blocks cannot disable this opportunity. + return true; +} + +void RemoveBlockReductionOpportunity::Apply() { + // We need an iterator pointing to the block, hence the loop. + for (auto bi = function_->begin(); bi != function_->end(); ++bi) { + if (bi->id() == block_->id()) { + bi.Erase(); + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return; + } + } + + assert(false && + "Unreachable: we should have found a block with the desired id."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.h new file mode 100644 index 00000000..03fede52 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an unreferenced block. +// See RemoveBlockReductionOpportunityFinder. +class RemoveBlockReductionOpportunity : public ReductionOpportunity { + public: + // Creates the opportunity to remove |block| in |function| in |context|. + RemoveBlockReductionOpportunity(opt::IRContext* context, + opt::Function* function, + opt::BasicBlock* block); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Function* function_; + opt::BasicBlock* block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..3b13728a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_block_reduction_opportunity_finder.h" + +#include "source/reduce/remove_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::string RemoveBlockReductionOpportunityFinder::GetName() const { + return "RemoveBlockReductionOpportunityFinder"; +} + +std::vector> +RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every block in every relevant function. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto bi = function->begin(); bi != function->end(); ++bi) { + if (IsBlockValidOpportunity(context, function, &bi)) { + result.push_back(MakeUnique( + context, function, &*bi)); + } + } + } + return result; +} + +bool RemoveBlockReductionOpportunityFinder::IsBlockValidOpportunity( + opt::IRContext* context, opt::Function* function, + opt::Function::iterator* bi) { + assert(*bi != function->end() && "Block iterator was out of bounds"); + + // Don't remove first block; we don't want to end up with no blocks. + if (*bi == function->begin()) { + return false; + } + + // Don't remove blocks with references. + if (context->get_def_use_mgr()->NumUsers((*bi)->id()) > 0) { + return false; + } + + // Don't remove blocks whose instructions have outside references. + if (!BlockInstructionsHaveNoOutsideReferences(context, *bi)) { + return false; + } + + return true; +} + +bool RemoveBlockReductionOpportunityFinder:: + BlockInstructionsHaveNoOutsideReferences( + opt::IRContext* context, const opt::Function::iterator& bi) { + // Get all instructions in block. + std::unordered_set instructions_in_block; + for (const opt::Instruction& instruction : *bi) { + instructions_in_block.insert(instruction.unique_id()); + } + + // For each instruction... + for (const opt::Instruction& instruction : *bi) { + // For each use of the instruction... + bool no_uses_outside_block = context->get_def_use_mgr()->WhileEachUser( + &instruction, [&instructions_in_block](opt::Instruction* user) -> bool { + // If the use is in this block, continue (return true). Otherwise, we + // found an outside use; return false (and stop). + return instructions_in_block.find(user->unique_id()) != + instructions_in_block.end(); + }); + + if (!no_uses_outside_block) { + return false; + } + } + + return true; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h new file mode 100644 index 00000000..d347bf91 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to remove a block. The optimizer can remove dead +// code. However, the reducer needs to be able to remove at a fine-grained +// level. +class RemoveBlockReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveBlockReductionOpportunityFinder() = default; + + ~RemoveBlockReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if the block |bi| in function |function| is a valid + // opportunity according to various restrictions. + static bool IsBlockValidOpportunity(opt::IRContext* context, + opt::Function* function, + opt::Function::iterator* bi); + + // Returns true if the instructions (definitions) in block |bi| have no + // references, except for references from inside the block itself. + static bool BlockInstructionsHaveNoOutsideReferences( + opt::IRContext* context, const opt::Function::iterator& bi); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp new file mode 100644 index 00000000..4b85058b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_function_reduction_opportunity.h" + +#include "source/opt/eliminate_dead_functions_util.h" + +namespace spvtools { +namespace reduce { + +bool RemoveFunctionReductionOpportunity::PreconditionHolds() { + // Removing one function cannot influence whether another function can be + // removed. + return true; +} + +void RemoveFunctionReductionOpportunity::Apply() { + for (opt::Module::iterator function_it = context_->module()->begin(); + function_it != context_->module()->end(); ++function_it) { + if (&*function_it == function_) { + function_it.Erase(); + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return; + } + } + assert(0 && "Function to be removed was not found."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.h new file mode 100644 index 00000000..d8c57db1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an unreferenced function. +class RemoveFunctionReductionOpportunity : public ReductionOpportunity { + public: + // Creates an opportunity to remove |function| from the module represented by + // |context|. + RemoveFunctionReductionOpportunity(opt::IRContext* context, + opt::Function* function) + : context_(context), function_(function) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // The IR context for the module under analysis. + opt::IRContext* context_; + + // The function that can be removed. + opt::Function* function_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..1d8d9726 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_function_reduction_opportunity_finder.h" + +#include "source/reduce/remove_function_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveFunctionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + if (target_function) { + // If we are targeting a specific function then we are only interested in + // opportunities that simplify the internals of that function; removing + // whole functions does not fit the bill. + return {}; + } + + std::vector> result; + // Consider each function. + for (auto& function : *context->module()) { + if (context->get_def_use_mgr()->NumUses(function.result_id()) > 0) { + // If the function is referenced, ignore it. + continue; + } + result.push_back( + MakeUnique(context, &function)); + } + return result; +} + +std::string RemoveFunctionReductionOpportunityFinder::GetName() const { + return "RemoveFunctionReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h new file mode 100644 index 00000000..6fcfb779 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to remove unreferenced functions. +class RemoveFunctionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveFunctionReductionOpportunityFinder() = default; + + ~RemoveFunctionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp new file mode 100644 index 00000000..8026204f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_instruction_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveInstructionReductionOpportunity::PreconditionHolds() { return true; } + +void RemoveInstructionReductionOpportunity::Apply() { + const uint32_t kNumEntryPointInOperandsBeforeInterfaceIds = 3; + for (auto& entry_point : inst_->context()->module()->entry_points()) { + opt::Instruction::OperandList new_entry_point_in_operands; + for (uint32_t index = 0; index < entry_point.NumInOperands(); index++) { + if (index >= kNumEntryPointInOperandsBeforeInterfaceIds && + entry_point.GetSingleWordInOperand(index) == inst_->result_id()) { + continue; + } + new_entry_point_in_operands.push_back(entry_point.GetInOperand(index)); + } + entry_point.SetInOperands(std::move(new_entry_point_in_operands)); + } + inst_->context()->KillInst(inst_); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h new file mode 100644 index 00000000..07bef50a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an instruction from the SPIR-V module. +class RemoveInstructionReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to remove |inst|. + explicit RemoveInstructionReductionOpportunity(opt::Instruction* inst) + : inst_(inst) {} + + // Always returns true, as this opportunity can always be applied. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* inst_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp new file mode 100644 index 00000000..96f01475 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_selection_reduction_opportunity.h" + +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveSelectionReductionOpportunity::PreconditionHolds() { return true; } + +void RemoveSelectionReductionOpportunity::Apply() { + auto merge_instruction = header_block_->GetMergeInst(); + merge_instruction->context()->KillInst(merge_instruction); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h new file mode 100644 index 00000000..892618e5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity for removing a selection construct by simply removing the +// OpSelectionMerge instruction; thus, the selection must have already been +// simplified to a point where the instruction can be trivially removed. +class RemoveSelectionReductionOpportunity : public ReductionOpportunity { + public: + // Constructs a reduction opportunity from the selection header |block| in + // |function|. + RemoveSelectionReductionOpportunity(opt::BasicBlock* header_block) + : header_block_(header_block) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // The header block of the selection. + opt::BasicBlock* header_block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..6abadf2a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_selection_reduction_opportunity_finder.h" + +#include "source/reduce/remove_selection_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +const uint32_t kContinueNodeIndex = 1; +} // namespace + +std::string RemoveSelectionReductionOpportunityFinder::GetName() const { + return "RemoveSelectionReductionOpportunityFinder"; +} + +std::vector> +RemoveSelectionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + // Get all loop merge and continue blocks so we can check for these later. + std::unordered_set merge_and_continue_blocks_from_loops; + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + if (auto merge_instruction = block.GetMergeInst()) { + if (merge_instruction->opcode() == spv::Op::OpLoopMerge) { + uint32_t merge_block_id = + merge_instruction->GetSingleWordOperand(kMergeNodeIndex); + uint32_t continue_block_id = + merge_instruction->GetSingleWordOperand(kContinueNodeIndex); + merge_and_continue_blocks_from_loops.insert(merge_block_id); + merge_and_continue_blocks_from_loops.insert(continue_block_id); + } + } + } + } + + // Return all selection headers where the OpSelectionMergeInstruction can be + // removed. + std::vector> result; + for (auto& function : *context->module()) { + for (auto& block : function) { + if (auto merge_instruction = block.GetMergeInst()) { + if (merge_instruction->opcode() == spv::Op::OpSelectionMerge) { + if (CanOpSelectionMergeBeRemoved( + context, block, merge_instruction, + merge_and_continue_blocks_from_loops)) { + result.push_back( + MakeUnique(&block)); + } + } + } + } + } + return result; +} + +bool RemoveSelectionReductionOpportunityFinder::CanOpSelectionMergeBeRemoved( + opt::IRContext* context, const opt::BasicBlock& header_block, + opt::Instruction* merge_instruction, + std::unordered_set merge_and_continue_blocks_from_loops) { + assert(header_block.GetMergeInst() == merge_instruction && + "CanOpSelectionMergeBeRemoved(...): header block and merge " + "instruction mismatch"); + + // The OpSelectionMerge instruction is needed if either of the following are + // true. + // + // 1. The header block has at least two (unique) successors that are not + // merge or continue blocks of a loop. + // + // 2. The predecessors of the merge block are "using" the merge block to avoid + // divergence. In other words, there exists a predecessor of the merge block + // that has a successor that is not the merge block of this construct and not + // a merge or continue block of a loop. + + // 1. + { + uint32_t divergent_successor_count = 0; + + std::unordered_set seen_successors; + + header_block.ForEachSuccessorLabel( + [&seen_successors, &merge_and_continue_blocks_from_loops, + &divergent_successor_count](uint32_t successor) { + // Not already seen. + if (seen_successors.find(successor) == seen_successors.end()) { + seen_successors.insert(successor); + // Not a loop continue or merge. + if (merge_and_continue_blocks_from_loops.find(successor) == + merge_and_continue_blocks_from_loops.end()) { + ++divergent_successor_count; + } + } + }); + + if (divergent_successor_count > 1) { + return false; + } + } + + // 2. + { + uint32_t merge_block_id = + merge_instruction->GetSingleWordOperand(kMergeNodeIndex); + for (uint32_t predecessor_block_id : + context->cfg()->preds(merge_block_id)) { + const opt::BasicBlock* predecessor_block = + context->cfg()->block(predecessor_block_id); + assert(predecessor_block); + bool found_divergent_successor = false; + predecessor_block->ForEachSuccessorLabel( + [&found_divergent_successor, merge_block_id, + &merge_and_continue_blocks_from_loops](uint32_t successor_id) { + // The successor is not the merge block, nor a loop merge or + // continue. + if (successor_id != merge_block_id && + merge_and_continue_blocks_from_loops.find(successor_id) == + merge_and_continue_blocks_from_loops.end()) { + found_divergent_successor = true; + } + }); + if (found_divergent_successor) { + return false; + } + } + } + + return true; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h new file mode 100644 index 00000000..1a174933 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities for removing a selection construct by simply +// removing the OpSelectionMerge instruction; thus, the selections must have +// already been simplified to a point where they can be trivially removed. +class RemoveSelectionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveSelectionReductionOpportunityFinder() = default; + + ~RemoveSelectionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + // Returns true if the OpSelectionMerge instruction |merge_instruction| in + // block |header_block| can be removed. + static bool CanOpSelectionMergeBeRemoved( + opt::IRContext* context, const opt::BasicBlock& header_block, + opt::Instruction* merge_instruction, + std::unordered_set merge_and_continue_blocks_from_loops); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp new file mode 100644 index 00000000..3309fd09 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_struct_member_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveStructMemberReductionOpportunity::PreconditionHolds() { + return struct_type_->NumInOperands() == original_number_of_members_; +} + +void RemoveStructMemberReductionOpportunity::Apply() { + std::set decorations_to_kill; + + // We need to remove decorations that target the removed struct member, and + // adapt decorations that target later struct members by decrementing the + // member identifier. We also need to adapt composite construction + // instructions so that no id is provided for the member being removed. + // + // To do this, we consider every use of the struct type. + struct_type_->context()->get_def_use_mgr()->ForEachUse( + struct_type_, [this, &decorations_to_kill](opt::Instruction* user, + uint32_t /*operand_index*/) { + switch (user->opcode()) { + case spv::Op::OpCompositeConstruct: + case spv::Op::OpConstantComposite: + // This use is constructing a composite of the struct type, so we + // must remove the id that was provided for the member we are + // removing. + user->RemoveInOperand(member_index_); + break; + case spv::Op::OpMemberDecorate: + // This use is decorating a member of the struct. + if (user->GetSingleWordInOperand(1) == member_index_) { + // The member we are removing is being decorated, so we record + // that we need to get rid of the decoration. + decorations_to_kill.insert(user); + } else if (user->GetSingleWordInOperand(1) > member_index_) { + // A member beyond the one we are removing is being decorated, so + // we adjust the index that identifies the member. + user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1}); + } + break; + default: + break; + } + }); + + // Get rid of all the decorations that were found to target the member being + // removed. + for (auto decoration_to_kill : decorations_to_kill) { + decoration_to_kill->context()->KillInst(decoration_to_kill); + } + + // We now look through all instructions that access composites via sequences + // of indices. Every time we find an index into the struct whose member is + // being removed, and if the member being accessed comes after the member + // being removed, we need to adjust the index accordingly. + // + // We go through every relevant instruction in every block of every function, + // and invoke a helper to adjust it. + auto context = struct_type_->context(); + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + // These access chain instructions take sequences of ids for + // indexing, starting from input operand 1. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id()) + ->GetSingleWordInOperand(1); + AdjustAccessedIndices(composite_type_id, 1, false, context, &inst); + } break; + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: { + // These access chain instructions take sequences of ids for + // indexing, starting from input operand 2. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id()) + ->GetSingleWordInOperand(1); + AdjustAccessedIndices(composite_type_id, 2, false, context, &inst); + } break; + case spv::Op::OpCompositeExtract: { + // OpCompositeExtract uses literals for indexing, starting at input + // operand 1. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id(); + AdjustAccessedIndices(composite_type_id, 1, true, context, &inst); + } break; + case spv::Op::OpCompositeInsert: { + // OpCompositeInsert uses literals for indexing, starting at input + // operand 2. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id(); + AdjustAccessedIndices(composite_type_id, 2, true, context, &inst); + } break; + default: + break; + } + } + } + } + + // Remove the member from the struct type. + struct_type_->RemoveInOperand(member_index_); + + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( + uint32_t composite_type_id, uint32_t first_index_input_operand, + bool literal_indices, opt::IRContext* context, + opt::Instruction* composite_access_instruction) const { + // Walk the series of types that are encountered by following the + // instruction's sequence of indices. For all types except structs, this is + // routine: the type of the composite dictates what the next type will be + // regardless of the specific index value. + uint32_t next_type = composite_type_id; + for (uint32_t i = first_index_input_operand; + i < composite_access_instruction->NumInOperands(); i++) { + auto type_inst = context->get_def_use_mgr()->GetDef(next_type); + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + next_type = type_inst->GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: { + // Struct types are special because (a) we may need to adjust the index + // being used, if the struct type is the one from which we are removing + // a member, and (b) the type encountered by following the current index + // is dependent on the value of the index. + + // Work out the member being accessed. If literal indexing is used this + // is simple; otherwise we need to look up the id of the constant + // instruction being used as an index and get the value of the constant. + uint32_t index_operand = + composite_access_instruction->GetSingleWordInOperand(i); + uint32_t member = literal_indices ? index_operand + : context->get_def_use_mgr() + ->GetDef(index_operand) + ->GetSingleWordInOperand(0); + + // The next type we will consider is obtained by looking up the struct + // type at |member|. + next_type = type_inst->GetSingleWordInOperand(member); + + if (type_inst == struct_type_ && member > member_index_) { + // The struct type is the struct from which we are removing a member, + // and the member being accessed is beyond the member we are removing. + // We thus need to decrement the index by 1. + uint32_t new_in_operand; + if (literal_indices) { + // With literal indexing this is straightforward. + new_in_operand = member - 1; + } else { + // With id-based indexing this is more tricky: we need to find or + // create a constant instruction whose value is one less than + // |member|, and use the id of this constant as the replacement + // input operand. + auto constant_inst = + context->get_def_use_mgr()->GetDef(index_operand); + auto int_type = context->get_type_mgr() + ->GetType(constant_inst->type_id()) + ->AsInteger(); + auto new_index_constant = + opt::analysis::IntConstant(int_type, {member - 1}); + new_in_operand = context->get_constant_mgr() + ->GetDefiningInstruction(&new_index_constant) + ->result_id(); + } + composite_access_instruction->SetInOperand(i, {new_in_operand}); + } + } break; + default: + assert(0 && "Unknown composite type."); + break; + } + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h new file mode 100644 index 00000000..899e5ea7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ + +#include "source/reduce/reduction_opportunity.h" + +#include "source/opt/instruction.h" + +namespace spvtools { +namespace reduce { + +// An opportunity for removing a member from a struct type, adjusting all uses +// of the struct accordingly. +class RemoveStructMemberReductionOpportunity : public ReductionOpportunity { + public: + // Constructs a reduction opportunity from the struct type |struct_type|, for + // removal of member |member_index|. + RemoveStructMemberReductionOpportunity(opt::Instruction* struct_type, + uint32_t member_index) + : struct_type_(struct_type), + member_index_(member_index), + original_number_of_members_(struct_type->NumInOperands()) {} + + // Opportunities to remove fields from a common struct type mutually + // invalidate each other. We guard against this by requiring that the struct + // still has the number of members it had when the opportunity was created. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // |composite_access_instruction| is an instruction that accesses a composite + // id using either a series of literal indices (e.g. in the case of + // OpCompositeInsert) or a series of index ids (e.g. in the case of + // OpAccessChain). + // + // This function adjusts the indices that are used by + // |composite_access_instruction| to that whenever an index is accessing a + // member of |struct_type_|, it is decremented if the member is beyond + // |member_index_|, to account for the removal of the |member_index_|-th + // member. + // + // |composite_type_id| is the id of the composite type that the series of + // indices is to be applied to. + // + // |first_index_input_operand| specifies the first input operand that is an + // index. + // + // |literal_indices| specifies whether indices are given as literals (true), + // or as ids (false). + // + // If id-based indexing is used, this function will add a constant for + // |member_index_| - 1 to the module if needed. + void AdjustAccessedIndices( + uint32_t composite_type_id, uint32_t first_index_input_operand, + bool literal_indices, opt::IRContext* context, + opt::Instruction* composite_access_instruction) const; + + // The struct type from which a member is to be removed. + opt::Instruction* struct_type_; + + uint32_t member_index_; + + uint32_t original_number_of_members_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..fbbeb346 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" + +#include "source/opcode.h" +#include "source/opt/instruction.h" +#include "source/reduce/remove_instruction_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +RemoveUnusedInstructionReductionOpportunityFinder:: + RemoveUnusedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs) + : remove_constants_and_undefs_(remove_constants_and_undefs) {} + +std::vector> +RemoveUnusedInstructionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + if (!target_function) { + // We are not restricting reduction to a specific function, so we consider + // unused instructions defined outside functions. + + for (auto& inst : context->module()->debugs1()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs2()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs3()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->ext_inst_debuginfo()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->types_values()) { + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } + if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context, + inst)) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->annotations()) { + if (context->get_def_use_mgr()->NumUsers(&inst) > 0) { + continue; + } + if (!IsIndependentlyRemovableDecoration(inst)) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + } + + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } + if (spvOpcodeIsBlockTerminator(inst.opcode()) || + inst.opcode() == spv::Op::OpSelectionMerge || + inst.opcode() == spv::Op::OpLoopMerge) { + // In this reduction pass we do not want to affect static + // control flow. + continue; + } + // Given that we're in a block, we should only get here if + // the instruction is not directly related to control flow; + // i.e., it's some straightforward instruction with an + // unused result, like an arithmetic operation or function + // call. + result.push_back( + MakeUnique(&inst)); + } + } + } + return result; +} + +std::string RemoveUnusedInstructionReductionOpportunityFinder::GetName() const { + return "RemoveUnusedInstructionReductionOpportunityFinder"; +} + +bool RemoveUnusedInstructionReductionOpportunityFinder:: + OnlyReferencedByIntimateDecorationOrEntryPointInterface( + opt::IRContext* context, const opt::Instruction& inst) const { + return context->get_def_use_mgr()->WhileEachUse( + &inst, [this](opt::Instruction* user, uint32_t use_index) -> bool { + return (user->IsDecoration() && + !IsIndependentlyRemovableDecoration(*user)) || + (user->opcode() == spv::Op::OpEntryPoint && use_index > 2); + }); +} + +bool RemoveUnusedInstructionReductionOpportunityFinder:: + IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const { + uint32_t decoration; + switch (inst.opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateString: + decoration = inst.GetSingleWordInOperand(1u); + break; + case spv::Op::OpMemberDecorate: + case spv::Op::OpMemberDecorateString: + decoration = inst.GetSingleWordInOperand(2u); + break; + default: + // The instruction is not a decoration. It is legitimate for this to be + // reached: it allows the method to be invoked on arbitrary instructions. + return false; + } + + // We conservatively only remove specific decorations that we believe will + // not change the shader interface, will not make the shader invalid, will + // actually be found in practice, etc. + + switch (spv::Decoration(decoration)) { + case spv::Decoration::RelaxedPrecision: + case spv::Decoration::NoSignedWrap: + case spv::Decoration::NoContraction: + case spv::Decoration::NoUnsignedWrap: + case spv::Decoration::UserSemantic: + return true; + default: + return false; + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h new file mode 100644 index 00000000..03236400 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to remove non-control-flow instructions in blocks +// in cases where the instruction's id is either not referenced at all, or +// referenced only in a trivial manner (for example, we regard a struct type as +// unused if it is referenced only by struct layout decorations). As well as +// making the module smaller, removing an instruction that references particular +// ids may create opportunities for subsequently removing the instructions that +// generated those ids. +class RemoveUnusedInstructionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + explicit RemoveUnusedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs); + + ~RemoveUnusedInstructionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if and only if the only uses of |inst| are by decorations that + // relate intimately to the instruction (as opposed to decorations that could + // be removed independently), or by interface ids in OpEntryPoint. + bool OnlyReferencedByIntimateDecorationOrEntryPointInterface( + opt::IRContext* context, const opt::Instruction& inst) const; + + // Returns true if and only if |inst| is a decoration instruction that can + // legitimately be removed on its own (rather than one that has to be removed + // simultaneously with other instructions). + bool IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const; + + bool remove_constants_and_undefs_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..db381e0f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" + +#include +#include + +#include "source/reduce/remove_struct_member_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + if (target_function) { + // Removing an unused struct member is a global change, as struct types are + // global. We thus do not consider such opportunities if we are targeting + // a specific function. + return {}; + } + + std::vector> result; + + // We track those struct members that are never accessed. We do this by + // associating a member index to all the structs that have this member index + // but do not use it. This representation is designed to allow reduction + // opportunities to be provided in a useful manner, so that opportunities + // associated with the same struct are unlikely to be adjacent. + std::map> unused_member_to_structs; + + // Consider every struct type in the module. + for (auto& type_or_value : context->types_values()) { + if (type_or_value.opcode() != spv::Op::OpTypeStruct) { + continue; + } + + // Initially, we assume that *every* member of the struct is unused. We + // then refine this based on observed uses. + std::set unused_members; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + unused_members.insert(i); + } + + // A separate reduction pass deals with removal of names. If a struct + // member is still named, we treat it as being used. + context->get_def_use_mgr()->ForEachUse( + &type_or_value, + [&unused_members](opt::Instruction* user, uint32_t /*operand_index*/) { + switch (user->opcode()) { + case spv::Op::OpMemberName: + unused_members.erase(user->GetSingleWordInOperand(1)); + break; + default: + break; + } + }); + + for (uint32_t member : unused_members) { + if (!unused_member_to_structs.count(member)) { + unused_member_to_structs.insert( + {member, std::set()}); + } + unused_member_to_structs.at(member).insert(&type_or_value); + } + } + + // We now go through every instruction that might index into a struct, and + // refine our tracking of which struct members are used based on the struct + // indexing we observe. We cannot just go through all uses of a struct type + // because the type is not necessarily even referenced, e.g. when walking + // arrays of structs. + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + switch (inst.opcode()) { + // For each indexing operation we observe, we invoke a helper to + // remove from our map those struct indices that are found to be used. + // The way the helper is invoked depends on whether the instruction + // uses literal or id indices, and the offset into the instruction's + // input operands from which index operands are provided. + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id()) + ->GetSingleWordInOperand(1); + MarkAccessedMembersAsUsed(context, composite_type_id, 1, false, + inst, &unused_member_to_structs); + } break; + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id()) + ->GetSingleWordInOperand(1); + MarkAccessedMembersAsUsed(context, composite_type_id, 2, false, + inst, &unused_member_to_structs); + } break; + case spv::Op::OpCompositeExtract: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id(); + MarkAccessedMembersAsUsed(context, composite_type_id, 1, true, inst, + &unused_member_to_structs); + } break; + case spv::Op::OpCompositeInsert: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id(); + MarkAccessedMembersAsUsed(context, composite_type_id, 2, true, inst, + &unused_member_to_structs); + } break; + default: + break; + } + } + } + } + + // We now know those struct indices that are unused, and we make a reduction + // opportunity for each of them. By mapping each relevant member index to the + // structs in which it is unused, we will group all opportunities to remove + // member k of a struct (for some k) together. This reduces the likelihood + // that opportunities to remove members from the same struct will be adjacent, + // which is good because such opportunities mutually disable one another. + for (auto& entry : unused_member_to_structs) { + for (auto struct_type : entry.second) { + result.push_back(MakeUnique( + struct_type, entry.first)); + } + } + return result; +} + +void RemoveUnusedStructMemberReductionOpportunityFinder:: + MarkAccessedMembersAsUsed( + opt::IRContext* context, uint32_t composite_type_id, + uint32_t first_index_in_operand, bool literal_indices, + const opt::Instruction& composite_access_instruction, + std::map>* + unused_member_to_structs) const { + uint32_t next_type = composite_type_id; + for (uint32_t i = first_index_in_operand; + i < composite_access_instruction.NumInOperands(); i++) { + auto type_inst = context->get_def_use_mgr()->GetDef(next_type); + switch (type_inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + next_type = type_inst->GetSingleWordInOperand(0); + break; + case spv::Op::OpTypeStruct: { + uint32_t index_operand = + composite_access_instruction.GetSingleWordInOperand(i); + uint32_t member = literal_indices ? index_operand + : context->get_def_use_mgr() + ->GetDef(index_operand) + ->GetSingleWordInOperand(0); + // Remove the struct type from the struct types associated with this + // member index, but only if a set of struct types is known to be + // associated with this member index. + if (unused_member_to_structs->count(member)) { + unused_member_to_structs->at(member).erase(type_inst); + } + next_type = type_inst->GetSingleWordInOperand(member); + } break; + default: + assert(0 && "Unknown composite type."); + break; + } + } +} + +std::string RemoveUnusedStructMemberReductionOpportunityFinder::GetName() + const { + return "RemoveUnusedStructMemberReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h new file mode 100644 index 00000000..98f9c019 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to remove struct members that are not explicitly +// used by extract, insert or access chain instructions. +class RemoveUnusedStructMemberReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveUnusedStructMemberReductionOpportunityFinder() = default; + + ~RemoveUnusedStructMemberReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // A helper method to update |unused_members_to_structs| by removing from it + // all struct member accesses that take place in + // |composite_access_instruction|. + // + // |composite_type_id| is the type of the root object indexed into by the + // instruction. + // + // |first_index_in_operand| provides indicates where in the input operands the + // sequence of indices begins. + // + // |literal_indices| indicates whether indices are literals (true) or ids + // (false). + void MarkAccessedMembersAsUsed( + opt::IRContext* context, uint32_t composite_type_id, + uint32_t first_index_in_operand, bool literal_indices, + const opt::Instruction& composite_access_instruction, + std::map>* unused_member_to_structs) + const; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp new file mode 100644 index 00000000..9637f0f1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" + +#include "source/reduce/reduction_util.h" +#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every function. + for (auto* function : GetTargetFunctions(context, target_function)) { + // Consider every block in the function. + for (auto& block : *function) { + // The terminator must be spv::Op::OpBranchConditional. + opt::Instruction* terminator = block.terminator(); + if (terminator->opcode() != spv::Op::OpBranchConditional) { + continue; + } + // It must not be a selection header, as these cannot be followed by + // OpBranch. + if (block.GetMergeInst() && + block.GetMergeInst()->opcode() == spv::Op::OpSelectionMerge) { + continue; + } + // The conditional branch must be simplified. + if (terminator->GetSingleWordInOperand(kTrueBranchOperandIndex) != + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex)) { + continue; + } + + result.push_back( + MakeUnique( + block.terminator())); + } + } + return result; +} + +std::string SimpleConditionalBranchToBranchOpportunityFinder::GetName() const { + return "SimpleConditionalBranchToBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h new file mode 100644 index 00000000..8869908b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to change simple conditional branches (conditional +// branches with one target) to an OpBranch. +class SimpleConditionalBranchToBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp new file mode 100644 index 00000000..6d772b59 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +SimpleConditionalBranchToBranchReductionOpportunity:: + SimpleConditionalBranchToBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction) + : conditional_branch_instruction_(conditional_branch_instruction) {} + +bool SimpleConditionalBranchToBranchReductionOpportunity::PreconditionHolds() { + // We find at most one opportunity per conditional branch and simplifying + // another branch cannot disable this opportunity. + return true; +} + +void SimpleConditionalBranchToBranchReductionOpportunity::Apply() { + assert(conditional_branch_instruction_->opcode() == + spv::Op::OpBranchConditional && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "a conditional branch"); + + assert(conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) == + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex) && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "simple"); + + // OpBranchConditional %condition %block_id %block_id ... + // -> + // OpBranch %block_id + + conditional_branch_instruction_->SetOpcode(spv::Op::OpBranch); + conditional_branch_instruction_->ReplaceOperands( + {{SPV_OPERAND_TYPE_ID, + {conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex)}}}); + conditional_branch_instruction_->context()->InvalidateAnalysesExceptFor( + opt::IRContext::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h new file mode 100644 index 00000000..eddb4647 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to change simple conditional branches (conditional branches +// with one target) to an OpBranch. +class SimpleConditionalBranchToBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. + explicit SimpleConditionalBranchToBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* conditional_branch_instruction_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.cpp new file mode 100644 index 00000000..cc5ffe3c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +bool StructuredConstructToBlockReductionOpportunity::PreconditionHolds() { + return context_->get_def_use_mgr()->GetDef(construct_header_) != nullptr; +} + +void StructuredConstructToBlockReductionOpportunity::Apply() { + auto header_block = context_->cfg()->block(construct_header_); + auto merge_block = context_->cfg()->block(header_block->MergeBlockId()); + + auto* enclosing_function = header_block->GetParent(); + + // A region of blocks is defined in terms of dominators and post-dominators, + // so we compute these for the enclosing function. + auto* dominators = context_->GetDominatorAnalysis(enclosing_function); + auto* postdominators = context_->GetPostDominatorAnalysis(enclosing_function); + + // For each block in the function, determine whether it is inside the region. + // If it is, delete it. + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (header_block != &*block_it && merge_block != &*block_it && + dominators->Dominates(header_block, &*block_it) && + postdominators->Dominates(merge_block, &*block_it)) { + block_it = block_it.Erase(); + } else { + ++block_it; + } + } + // Having removed some blocks from the module it is necessary to invalidate + // analyses, since the remaining patch-up work depends on various analyses + // which will otherwise reference blocks that have been deleted. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We demote the header of the region to a regular block by deleting its merge + // instruction. + context_->KillInst(header_block->GetMergeInst()); + + // The terminator for the header block is changed to be an unconditional + // branch to the merge block. + header_block->terminator()->SetOpcode(spv::Op::OpBranch); + header_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {merge_block->id()}}}); + + // This is an intrusive change, so we invalidate all analyses. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.h new file mode 100644 index 00000000..f461a2f0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity.h @@ -0,0 +1,49 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace a skeletal structured control flow construct with a +// single block. +class StructuredConstructToBlockReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity from a header block id. + StructuredConstructToBlockReductionOpportunity(opt::IRContext* context, + uint32_t construct_header) + : context_(context), construct_header_(construct_header) {} + + // Returns true if and only if |construct_header_| exists in the module - + // another opportunity may have removed it. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + uint32_t construct_header_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..29fbe551 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_construct_to_block_reduction_opportunity_finder.h" + +#include + +#include "source/reduce/structured_construct_to_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every function in the module. + for (auto* function : GetTargetFunctions(context, target_function)) { + // For every header block in the function, there is potentially a region of + // blocks that could be collapsed. + std::unordered_map> + regions; + + // Regions are identified using dominators and postdominators, so we compute + // those for the function. + auto* dominators = context->GetDominatorAnalysis(function); + auto* postdominators = context->GetPostDominatorAnalysis(function); + + // Consider every block in the function. + for (auto& block : *function) { + // If a block has an unreachable predecessor then folding away a region in + // which that block is contained gets complicated, so we ignore regions + // that contain such blocks. We note whether this block suffers from this + // problem. + bool has_unreachable_predecessor = + HasUnreachablePredecessor(block, context); + + // Look through all the regions we have identified so far to see whether + // this block is part of a region, or spoils a region (by having an + // unreachable predecessor). + for (auto entry = regions.begin(); entry != regions.end();) { + // |block| is in this region if it is dominated by the header, + // post-dominated by the merge, and different from the merge. + assert(&block != entry->first && + "The block should not be the region's header because we only " + "make a region when we encounter its header."); + if (entry->first->MergeBlockId() != block.id() && + dominators->Dominates(entry->first, &block) && + postdominators->Dominates( + entry->first->GetMergeInst()->GetSingleWordInOperand(0), + block.id())) { + if (has_unreachable_predecessor) { + // The block would be in this region, but it has an unreachable + // predecessor. This spoils the region, so we remove it. + entry = regions.erase(entry); + continue; + } else { + // Add the block to the region. + entry->second.insert(&block); + } + } + ++entry; + } + if (block.MergeBlockIdIfAny() == 0) { + // The block isn't a header, so it doesn't constitute a new region. + continue; + } + if (!context->IsReachable(block)) { + // The block isn't reachable, so it doesn't constitute a new region. + continue; + } + auto* merge_block = context->cfg()->block( + block.GetMergeInst()->GetSingleWordInOperand(0)); + if (!context->IsReachable(*merge_block)) { + // The block's merge is unreachable, so it doesn't constitute a new + // region. + continue; + } + assert(dominators->Dominates(&block, merge_block) && + "The merge block is reachable, so the header must dominate it"); + if (!postdominators->Dominates(merge_block, &block)) { + // The block is not post-dominated by its merge. This happens for + // instance when there is a break from a conditional, or an early exit. + // This also means that we don't add a region. + continue; + } + // We have a reachable header block with a reachable merge that + // postdominates the header: this means we have a new region. + regions.emplace(&block, std::unordered_set()); + } + + // Now that we have found all the regions and blocks within them, we check + // whether any region defines an id that is used outside the region. If this + // is *not* the case, then we have an opportunity to collapse the region + // down to its header block and merge block. + for (auto& entry : regions) { + if (DefinitionsRestrictedToRegion(*entry.first, entry.second, context)) { + result.emplace_back( + MakeUnique( + context, entry.first->id())); + } + } + } + return result; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context) { + // Consider every block in the region. + for (auto& block : region) { + // Consider every instruction in the block - this includes the label + // instruction + if (!block->WhileEachInst( + [context, &header, ®ion](opt::Instruction* inst) -> bool { + if (inst->result_id() == 0) { + // The instruction does not generate a result id, thus it cannot + // be referred to outside the region - this is fine. + return true; + } + // Consider every use of the instruction's result id. + if (!context->get_def_use_mgr()->WhileEachUse( + inst->result_id(), + [context, &header, ®ion](opt::Instruction* user, + uint32_t) -> bool { + auto user_block = context->get_instr_block(user); + if (user == header.GetMergeInst() || + user == header.terminator()) { + // We are going to delete the header's merge + // instruction and rewrite its terminator, so it does + // not matter if the user is one of these + // instructions. + return true; + } + if (user_block == nullptr || + region.count(user_block) == 0) { + // The user is either a global instruction, or an + // instruction in a block outside the region. Removing + // the region would invalidate this user. + return false; + } + return true; + })) { + return false; + } + return true; + })) { + return false; + } + } + return true; +} + +bool StructuredConstructToBlockReductionOpportunityFinder:: + HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context) { + for (auto pred : context->cfg()->preds(block.id())) { + if (!context->IsReachable(*context->cfg()->block(pred))) { + return true; + } + } + return false; +} + +std::string StructuredConstructToBlockReductionOpportunityFinder::GetName() + const { + return "StructuredConstructToBlockReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h new file mode 100644 index 00000000..28bbc17c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_construct_to_block_reduction_opportunity_finder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H +#define SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to replace a skeletal structured control flow +// construct - that is, a construct that does not define anything that's used +// outside the construct - into its header block. +class StructuredConstructToBlockReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + StructuredConstructToBlockReductionOpportunityFinder() = default; + + ~StructuredConstructToBlockReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if and only if all instructions defined in |region| are used + // only inside |region|, with the exception that they may be used by the merge + // or terminator instruction of |header|, which must be the header block for + // the region. + static bool DefinitionsRestrictedToRegion( + const opt::BasicBlock& header, + const std::unordered_set& region, + opt::IRContext* context); + + // Returns true if and only if |block| has at least one predecessor that is + // unreachable in the control flow graph of its function. + static bool HasUnreachablePredecessor(const opt::BasicBlock& block, + opt::IRContext* context); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_CONSTRUCT_TO_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp new file mode 100644 index 00000000..45b95285 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity.h" + +#include "source/opt/aggressive_dead_code_elim_pass.h" +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +} // namespace + +bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { + // Is the loop header reachable? + return loop_construct_header_->GetLabel()->context()->IsReachable( + *loop_construct_header_); +} + +void StructuredLoopToSelectionReductionOpportunity::Apply() { + // Force computation of dominator analysis, CFG and structured CFG analysis + // before we start to mess with edges in the function. + context_->GetDominatorAnalysis(loop_construct_header_->GetParent()); + context_->cfg(); + context_->GetStructuredCFGAnalysis(); + + // (1) Redirect edges that point to the loop's continue target to their + // closest merge block. + RedirectToClosestMergeBlock(loop_construct_header_->ContinueBlockId()); + + // (2) Redirect edges that point to the loop's merge block to their closest + // merge block (which might be that of an enclosing selection, for instance). + RedirectToClosestMergeBlock(loop_construct_header_->MergeBlockId()); + + // (3) Turn the loop construct header into a selection. + ChangeLoopToSelection(); + + // We have made control flow changes that do not preserve the analyses that + // were performed. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // (4) By changing CFG edges we may have created scenarios where ids are used + // without being dominated; we fix instances of this. + FixNonDominatedIdUses(); + + // Invalidate the analyses we just used. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock( + uint32_t original_target_id) { + // Consider every predecessor of the node with respect to which edges should + // be redirected. + std::set already_seen; + for (auto pred : context_->cfg()->preds(original_target_id)) { + if (already_seen.find(pred) != already_seen.end()) { + // We have already handled this predecessor (this scenario can arise if + // there are multiple edges from a block b to original_target_id). + continue; + } + already_seen.insert(pred); + + if (!context_->IsReachable(*context_->cfg()->block(pred))) { + // We do not care about unreachable predecessors (and dominance + // information, and thus the notion of structured control flow, makes + // little sense for unreachable blocks). + continue; + } + // Find the merge block of the structured control construct that most + // tightly encloses the predecessor. + uint32_t new_merge_target; + // The structured CFG analysis deliberately does not regard a header as + // belonging to the structure that it heads. We want it to, so handle this + // case specially. + if (context_->cfg()->block(pred)->MergeBlockIdIfAny()) { + new_merge_target = context_->cfg()->block(pred)->MergeBlockIdIfAny(); + } else { + new_merge_target = context_->GetStructuredCFGAnalysis()->MergeBlock(pred); + } + assert(new_merge_target != pred); + + if (!new_merge_target) { + // If the loop being transformed is outermost, and the predecessor is + // part of that loop's continue construct, there will be no such + // enclosing control construct. In this case, the continue construct + // will become unreachable anyway, so it is fine not to redirect the + // edge. + continue; + } + + if (new_merge_target != original_target_id) { + // Redirect the edge if it doesn't already point to the desired block. + RedirectEdge(pred, original_target_id, new_merge_target); + } + } +} + +void StructuredLoopToSelectionReductionOpportunity::RedirectEdge( + uint32_t source_id, uint32_t original_target_id, uint32_t new_target_id) { + // Redirect edge source_id->original_target_id to edge + // source_id->new_target_id, where the blocks involved are all different. + assert(source_id != original_target_id); + assert(source_id != new_target_id); + assert(original_target_id != new_target_id); + + // original_target_id must either be the merge target or continue construct + // for the loop being operated on. + assert(original_target_id == loop_construct_header_->MergeBlockId() || + original_target_id == loop_construct_header_->ContinueBlockId()); + + auto terminator = context_->cfg()->block(source_id)->terminator(); + + // Figure out which operands of the terminator need to be considered for + // redirection. + std::vector operand_indices; + if (terminator->opcode() == spv::Op::OpBranch) { + operand_indices = {0}; + } else if (terminator->opcode() == spv::Op::OpBranchConditional) { + operand_indices = {1, 2}; + } else { + assert(terminator->opcode() == spv::Op::OpSwitch); + for (uint32_t label_index = 1; label_index < terminator->NumOperands(); + label_index += 2) { + operand_indices.push_back(label_index); + } + } + + // Redirect the relevant operands, asserting that at least one redirection is + // made. + bool redirected = false; + for (auto operand_index : operand_indices) { + if (terminator->GetSingleWordOperand(operand_index) == original_target_id) { + terminator->SetOperand(operand_index, {new_target_id}); + redirected = true; + } + } + (void)(redirected); + assert(redirected); + + // The old and new targets may have phi instructions; these will need to + // respect the change in edges. + AdaptPhiInstructionsForRemovedEdge( + source_id, context_->cfg()->block(original_target_id)); + AdaptPhiInstructionsForAddedEdge(source_id, + context_->cfg()->block(new_target_id)); +} + +void StructuredLoopToSelectionReductionOpportunity:: + AdaptPhiInstructionsForAddedEdge(uint32_t from_id, + opt::BasicBlock* to_block) { + to_block->ForEachPhiInst([this, &from_id](opt::Instruction* phi_inst) { + // Add to the phi operand an (undef, from_id) pair to reflect the added + // edge. + auto undef_id = FindOrCreateGlobalUndef(context_, phi_inst->type_id()); + phi_inst->AddOperand(opt::Operand(SPV_OPERAND_TYPE_ID, {undef_id})); + phi_inst->AddOperand(opt::Operand(SPV_OPERAND_TYPE_ID, {from_id})); + }); +} + +void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() { + // Change the merge instruction from OpLoopMerge to OpSelectionMerge, with + // the same merge block. + auto loop_merge_inst = loop_construct_header_->GetLoopMergeInst(); + auto const loop_merge_block_id = + loop_merge_inst->GetSingleWordOperand(kMergeNodeIndex); + loop_merge_inst->SetOpcode(spv::Op::OpSelectionMerge); + loop_merge_inst->ReplaceOperands( + {{loop_merge_inst->GetOperand(kMergeNodeIndex).type, + {loop_merge_block_id}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {uint32_t(spv::SelectionControlMask::MaskNone)}}}); + + // The loop header either finishes with OpBranch or OpBranchConditional. + // The latter is fine for a selection. In the former case we need to turn + // it into OpBranchConditional. We use "true" as the condition, and make + // the "else" branch be the merge block. + auto terminator = loop_construct_header_->terminator(); + if (terminator->opcode() == spv::Op::OpBranch) { + opt::analysis::Bool temp; + const opt::analysis::Bool* bool_type = + context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool(); + auto const_mgr = context_->get_constant_mgr(); + auto true_const = const_mgr->GetConstant(bool_type, {1}); + auto true_const_result_id = + const_mgr->GetDefiningInstruction(true_const)->result_id(); + auto original_branch_id = terminator->GetSingleWordOperand(0); + terminator->SetOpcode(spv::Op::OpBranchConditional); + terminator->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {true_const_result_id}}, + {SPV_OPERAND_TYPE_ID, {original_branch_id}}, + {SPV_OPERAND_TYPE_ID, {loop_merge_block_id}}}); + if (original_branch_id != loop_merge_block_id) { + AdaptPhiInstructionsForAddedEdge( + loop_construct_header_->id(), + context_->cfg()->block(loop_merge_block_id)); + } + } +} + +void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { + // Consider each instruction in the function. + for (auto& block : *loop_construct_header_->GetParent()) { + for (auto& def : block) { + if (def.opcode() == spv::Op::OpVariable) { + // Variables are defined at the start of the function, and can be + // accessed by all blocks, even by unreachable blocks that have no + // dominators, so we do not need to worry about them. + continue; + } + context_->get_def_use_mgr()->ForEachUse(&def, [this, &block, &def]( + opt::Instruction* use, + uint32_t index) { + // Ignore uses outside of blocks, such as in OpDecorate. + if (context_->get_instr_block(use) == nullptr) { + return; + } + // If a use is not appropriately dominated by its definition, + // replace the use with an OpUndef, unless the definition is an + // access chain, in which case replace it with some (possibly fresh) + // variable (as we cannot load from / store to OpUndef). + if (!DefinitionSufficientlyDominatesUse(&def, use, index, block)) { + if (def.opcode() == spv::Op::OpAccessChain) { + auto pointer_type = + context_->get_type_mgr()->GetType(def.type_id())->AsPointer(); + switch (pointer_type->storage_class()) { + case spv::StorageClass::Function: + use->SetOperand( + index, {FindOrCreateFunctionVariable( + context_, loop_construct_header_->GetParent(), + context_->get_type_mgr()->GetId(pointer_type))}); + break; + default: + // TODO(2183) Need to think carefully about whether it makes + // sense to add new variables for all storage classes; it's + // fine for Private but might not be OK for input/output + // storage classes for example. + use->SetOperand( + index, {FindOrCreateGlobalVariable( + context_, + context_->get_type_mgr()->GetId(pointer_type))}); + break; + break; + } + } else { + use->SetOperand(index, + {FindOrCreateGlobalUndef(context_, def.type_id())}); + } + } + }); + } + } +} + +bool StructuredLoopToSelectionReductionOpportunity:: + DefinitionSufficientlyDominatesUse(opt::Instruction* def, + opt::Instruction* use, + uint32_t use_index, + opt::BasicBlock& def_block) { + if (use->opcode() == spv::Op::OpPhi) { + // A use in a phi doesn't need to be dominated by its definition, but the + // associated parent block does need to be dominated by the definition. + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) + ->Dominates(def_block.id(), use->GetSingleWordOperand(use_index + 1)); + } + // In non-phi cases, a use needs to be dominated by its definition. + return context_->GetDominatorAnalysis(loop_construct_header_->GetParent()) + ->Dominates(def, use); +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h new file mode 100644 index 00000000..0e3c840a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -0,0 +1,93 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace a structured loop with a selection. +class StructuredLoopToSelectionReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity from a loop header block and the function that + // encloses it. + explicit StructuredLoopToSelectionReductionOpportunity( + opt::IRContext* context, opt::BasicBlock* loop_construct_header) + : context_(context), loop_construct_header_(loop_construct_header) {} + + // Returns true if the loop header is reachable. A structured loop might + // become unreachable as a result of turning another structured loop into + // a selection. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // Parameter |original_target_id| is the id of the loop's merge block or + // continue target. This method considers each edge of the form + // b->original_target_id and transforms it into an edge of the form b->c, + // where c is the merge block of the structured control flow construct that + // most tightly contains b. + void RedirectToClosestMergeBlock(uint32_t original_target_id); + + // |source_id|, |original_target_id| and |new_target_id| are required to all + // be distinct, with a CFG edge existing from |source_id| to + // |original_target_id|, and |original_target_id| being either the merge block + // or continue target for the loop being operated on. + // The method removes this edge and adds an edge from + // |source_id| to |new_target_id|. It takes care of fixing up any OpPhi + // instructions associated with |original_target_id| and |new_target_id|. + void RedirectEdge(uint32_t source_id, uint32_t original_target_id, + uint32_t new_target_id); + + // Adds components to |to_block|'s phi instructions to account for a new + // incoming edge from |from_id|. + void AdaptPhiInstructionsForAddedEdge(uint32_t from_id, + opt::BasicBlock* to_block); + + // Turns the OpLoopMerge for the loop into OpSelectionMerge, and adapts the + // following branch instruction accordingly. + void ChangeLoopToSelection(); + + // Fixes any scenarios where, due to CFG changes, ids have uses not dominated + // by their definitions, by changing such uses to uses of OpUndef or of + // placeholder variables. + void FixNonDominatedIdUses(); + + // Returns true if and only if at least one of the following holds: + // 1) |def| dominates |use| + // 2) |def| is an OpVariable + // 3) |use| is part of an OpPhi, with associated incoming block b, and |def| + // dominates b. + bool DefinitionSufficientlyDominatesUse(opt::Instruction* def, + opt::Instruction* use, + uint32_t use_index, + opt::BasicBlock& def_block); + + opt::IRContext* context_; + opt::BasicBlock* loop_construct_header_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..3fe61280 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +const uint32_t kContinueNodeIndex = 1; +} // namespace + +std::vector> +StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + std::set merge_block_ids; + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + auto merge_block_id = block.MergeBlockIdIfAny(); + if (merge_block_id) { + merge_block_ids.insert(merge_block_id); + } + } + } + + // Consider each loop construct header in the module. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + auto loop_merge_inst = block.GetLoopMergeInst(); + if (!loop_merge_inst) { + // This is not a loop construct header. + continue; + } + + uint32_t continue_block_id = + loop_merge_inst->GetSingleWordOperand(kContinueNodeIndex); + + // Check whether the loop construct's continue target is the merge block + // of some structured control flow construct. If it is, we cautiously do + // not consider applying a transformation. + if (merge_block_ids.find(continue_block_id) != merge_block_ids.end()) { + continue; + } + + // Check whether the loop header block is also the continue target. If it + // is, we cautiously do not consider applying a transformation. + if (block.id() == continue_block_id) { + continue; + } + + // Check whether the loop construct header dominates its merge block. + // If not, the merge block must be unreachable in the control flow graph + // so we cautiously do not consider applying a transformation. + auto merge_block_id = + loop_merge_inst->GetSingleWordInOperand(kMergeNodeIndex); + if (!context->GetDominatorAnalysis(function)->Dominates(block.id(), + merge_block_id)) { + continue; + } + + // Check whether the loop construct merge block postdominates the loop + // construct header. If not (e.g. because the loop contains OpReturn, + // OpKill or OpUnreachable), we cautiously do not consider applying + // a transformation. + if (!context->GetPostDominatorAnalysis(function)->Dominates( + merge_block_id, block.id())) { + continue; + } + + // We can turn this structured loop into a selection, so add the + // opportunity to do so. + result.push_back( + MakeUnique(context, + &block)); + } + } + return result; +} + +std::string StructuredLoopToSelectionReductionOpportunityFinder::GetName() + const { + return "StructuredLoopToSelectionReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h new file mode 100644 index 00000000..6166af38 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H +#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to turn structured loops into selections, +// generalizing from a human-writable language the idea of turning a loop: +// +// while (c) { +// body; +// } +// +// into: +// +// if (c) { +// body; +// } +// +// Applying such opportunities results in continue constructs of transformed +// loops becoming unreachable, so that it may be possible to remove them +// subsequently. +class StructuredLoopToSelectionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + StructuredLoopToSelectionReductionOpportunityFinder() = default; + + ~StructuredLoopToSelectionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H diff --git a/engine/engine/3rdlibs/spirv-tools/source/software_version.cpp b/engine/engine/3rdlibs/spirv-tools/source/software_version.cpp new file mode 100644 index 00000000..b258ebe9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/software_version.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2015-2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.h" + +namespace { + +const char* kBuildVersions[] = { +#include "build-version.inc" +}; + +} // anonymous namespace + +const char* spvSoftwareVersionString(void) { return kBuildVersions[0]; } + +const char* spvSoftwareVersionDetailsString(void) { return kBuildVersions[1]; } diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_constant.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_constant.h new file mode 100644 index 00000000..8636806c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_constant.h @@ -0,0 +1,101 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_CONSTANT_H_ +#define SOURCE_SPIRV_CONSTANT_H_ + +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.h" + +// Version number macros. + +// Evaluates to a well-formed version header word, given valid +// SPIR-V version major and minor version numbers. +#define SPV_SPIRV_VERSION_WORD(MAJOR, MINOR) \ + ((uint32_t(uint8_t(MAJOR)) << 16) | (uint32_t(uint8_t(MINOR)) << 8)) +// Returns the major version extracted from a version header word. +#define SPV_SPIRV_VERSION_MAJOR_PART(WORD) ((uint32_t(WORD) >> 16) & 0xff) +// Returns the minor version extracted from a version header word. +#define SPV_SPIRV_VERSION_MINOR_PART(WORD) ((uint32_t(WORD) >> 8) & 0xff) + +// Header indices + +#define SPV_INDEX_MAGIC_NUMBER 0u +#define SPV_INDEX_VERSION_NUMBER 1u +#define SPV_INDEX_GENERATOR_NUMBER 2u +#define SPV_INDEX_BOUND 3u +#define SPV_INDEX_SCHEMA 4u +#define SPV_INDEX_INSTRUCTION 5u + +// Universal limits + +// SPIR-V 1.0 limits +#define SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX 0xffff +#define SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX 0xffff + +// A single Unicode character in UTF-8 encoding can take +// up 4 bytes. +#define SPV_LIMIT_LITERAL_STRING_BYTES_MAX \ + (SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX * 4) + +// NOTE: These are set to the minimum maximum values +// TODO(dneto): Check these. + +// libspirv limits. +#define SPV_LIMIT_RESULT_ID_BOUND 0x00400000 +#define SPV_LIMIT_CONTROL_FLOW_NEST_DEPTH 0x00000400 +#define SPV_LIMIT_GLOBAL_VARIABLES_MAX 0x00010000 +#define SPV_LIMIT_LOCAL_VARIABLES_MAX 0x00080000 +// TODO: Decorations per target ID max, depends on decoration table size +#define SPV_LIMIT_EXECUTION_MODE_PER_ENTRY_POINT_MAX 0x00000100 +#define SPV_LIMIT_INDICIES_MAX_ACCESS_CHAIN_COMPOSITE_MAX 0x00000100 +#define SPV_LIMIT_FUNCTION_PARAMETERS_PER_FUNCTION_DECL 0x00000100 +#define SPV_LIMIT_FUNCTION_CALL_ARGUMENTS_MAX 0x00000100 +#define SPV_LIMIT_EXT_FUNCTION_CALL_ARGUMENTS_MAX 0x00000100 +#define SPV_LIMIT_SWITCH_LITERAL_LABEL_PAIRS_MAX 0x00004000 +#define SPV_LIMIT_STRUCT_MEMBERS_MAX 0x0000400 +#define SPV_LIMIT_STRUCT_NESTING_DEPTH_MAX 0x00000100 + +// Enumerations + +// Values mapping to registered tools. See the registry at +// https://www.khronos.org/registry/spir-v/api/spir-v.xml +// These values occupy the higher order 16 bits of the generator magic word. +typedef enum spv_generator_t { + // TODO(dneto) Values 0 through 5 were registered only as vendor. + SPV_GENERATOR_KHRONOS = 0, + SPV_GENERATOR_LUNARG = 1, + SPV_GENERATOR_VALVE = 2, + SPV_GENERATOR_CODEPLAY = 3, + SPV_GENERATOR_NVIDIA = 4, + SPV_GENERATOR_ARM = 5, + // These are vendor and tool. + SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6, + SPV_GENERATOR_KHRONOS_ASSEMBLER = 7, + SPV_GENERATOR_KHRONOS_GLSLANG = 8, + SPV_GENERATOR_KHRONOS_LINKER = 17, + SPV_GENERATOR_NUM_ENTRIES, + SPV_FORCE_16_BIT_ENUM(spv_generator_t) +} spv_generator_t; + +// Evaluates to a well-formed generator magic word from a tool value and +// miscellaneous 16-bit value. +#define SPV_GENERATOR_WORD(TOOL, MISC) \ + ((uint32_t(uint16_t(TOOL)) << 16) | uint16_t(MISC)) +// Returns the tool component of the generator word. +#define SPV_GENERATOR_TOOL_PART(WORD) (uint32_t(WORD) >> 16) +// Returns the misc part of the generator word. +#define SPV_GENERATOR_MISC_PART(WORD) (uint32_t(WORD) & 0xFFFF) + +#endif // SOURCE_SPIRV_CONSTANT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_definition.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_definition.h new file mode 100644 index 00000000..5dbd6ab2 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_definition.h @@ -0,0 +1,33 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_DEFINITION_H_ +#define SOURCE_SPIRV_DEFINITION_H_ + +#include + +#include "source/latest_version_spirv_header.h" + +#define spvIsInBitfield(value, bitfield) ((value) == ((value)&bitfield)) + +typedef struct spv_header_t { + uint32_t magic; + uint32_t version; + uint32_t generator; + uint32_t bound; + uint32_t schema; // NOTE: Reserved + const uint32_t* instructions; // NOTE: Unfixed pointer to instruction stream +} spv_header_t; + +#endif // SOURCE_SPIRV_DEFINITION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.cpp new file mode 100644 index 00000000..1d770917 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_endian.h" + +#include + +enum { + I32_ENDIAN_LITTLE = 0x03020100ul, + I32_ENDIAN_BIG = 0x00010203ul, +}; + +// This constant value allows the detection of the host machine's endianness. +// Accessing it through the "value" member is valid due to C++11 section 3.10 +// paragraph 10. +static const union { + unsigned char bytes[4]; + uint32_t value; +} o32_host_order = {{0, 1, 2, 3}}; + +#define I32_ENDIAN_HOST (o32_host_order.value) + +uint32_t spvFixWord(const uint32_t word, const spv_endianness_t endian) { + if ((SPV_ENDIANNESS_LITTLE == endian && I32_ENDIAN_HOST == I32_ENDIAN_BIG) || + (SPV_ENDIANNESS_BIG == endian && I32_ENDIAN_HOST == I32_ENDIAN_LITTLE)) { + return (word & 0x000000ff) << 24 | (word & 0x0000ff00) << 8 | + (word & 0x00ff0000) >> 8 | (word & 0xff000000) >> 24; + } + + return word; +} + +uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, + const spv_endianness_t endian) { + return (uint64_t(spvFixWord(high, endian)) << 32) | spvFixWord(low, endian); +} + +spv_result_t spvBinaryEndianness(spv_const_binary binary, + spv_endianness_t* pEndian) { + if (!binary->code || !binary->wordCount) return SPV_ERROR_INVALID_BINARY; + if (!pEndian) return SPV_ERROR_INVALID_POINTER; + + uint8_t bytes[4]; + memcpy(bytes, binary->code, sizeof(uint32_t)); + + if (0x03 == bytes[0] && 0x02 == bytes[1] && 0x23 == bytes[2] && + 0x07 == bytes[3]) { + *pEndian = SPV_ENDIANNESS_LITTLE; + return SPV_SUCCESS; + } + + if (0x07 == bytes[0] && 0x23 == bytes[1] && 0x02 == bytes[2] && + 0x03 == bytes[3]) { + *pEndian = SPV_ENDIANNESS_BIG; + return SPV_SUCCESS; + } + + return SPV_ERROR_INVALID_BINARY; +} + +bool spvIsHostEndian(spv_endianness_t endian) { + return ((SPV_ENDIANNESS_LITTLE == endian) && + (I32_ENDIAN_LITTLE == I32_ENDIAN_HOST)) || + ((SPV_ENDIANNESS_BIG == endian) && + (I32_ENDIAN_BIG == I32_ENDIAN_HOST)); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.h new file mode 100644 index 00000000..b4927f31 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_endian.h @@ -0,0 +1,37 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_ENDIAN_H_ +#define SOURCE_SPIRV_ENDIAN_H_ + +#include "spirv-tools/libspirv.h" + +// Converts a word in the specified endianness to the host native endianness. +uint32_t spvFixWord(const uint32_t word, const spv_endianness_t endianness); + +// Converts a pair of words in the specified endianness to the host native +// endianness. +uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, + const spv_endianness_t endianness); + +// Gets the endianness of the SPIR-V module given in the binary parameter. +// Returns SPV_ENDIANNESS_UNKNOWN if the SPIR-V magic number is invalid, +// otherwise writes the determined endianness into *endian. +spv_result_t spvBinaryEndianness(const spv_const_binary binary, + spv_endianness_t* endian); + +// Returns true if the given endianness matches the host's native endianness. +bool spvIsHostEndian(spv_endianness_t endian); + +#endif // SOURCE_SPIRV_ENDIAN_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.cpp new file mode 100644 index 00000000..3f62e0e0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_fuzzer_options.h" + +namespace { +// The default maximum number of steps for the reducer to run before giving up. +const uint32_t kDefaultStepLimit = 250; +} // namespace + +spv_fuzzer_options_t::spv_fuzzer_options_t() + : has_random_seed(false), + random_seed(0), + replay_range(0), + replay_validation_enabled(false), + shrinker_step_limit(kDefaultStepLimit), + fuzzer_pass_validation_enabled(false), + all_passes_enabled(false) {} + +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() { + return new spv_fuzzer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation( + spv_fuzzer_options options) { + options->replay_validation_enabled = true; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed) { + options->has_random_seed = true; + options->random_seed = seed; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range) { + options->replay_range = replay_range; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( + spv_fuzzer_options options, uint32_t shrinker_step_limit) { + options->shrinker_step_limit = shrinker_step_limit; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options) { + options->fuzzer_pass_validation_enabled = true; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses( + spv_fuzzer_options options) { + options->all_passes_enabled = true; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.h new file mode 100644 index 00000000..bb8d9103 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_fuzzer_options.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_FUZZER_OPTIONS_H_ +#define SOURCE_SPIRV_FUZZER_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +#include +#include + +// Manages command line options passed to the SPIR-V Fuzzer. New struct +// members may be added for any new option. +struct spv_fuzzer_options_t { + spv_fuzzer_options_t(); + + // See spvFuzzerOptionsSetRandomSeed. + bool has_random_seed; + uint32_t random_seed; + + // See spvFuzzerOptionsSetReplayRange. + int32_t replay_range; + + // See spvFuzzerOptionsEnableReplayValidation. + bool replay_validation_enabled; + + // See spvFuzzerOptionsSetShrinkerStepLimit. + uint32_t shrinker_step_limit; + + // See spvFuzzerOptionsValidateAfterEveryPass. + bool fuzzer_pass_validation_enabled; + + // See spvFuzzerOptionsEnableAllPasses. + bool all_passes_enabled; +}; + +#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.cpp new file mode 100644 index 00000000..e92ffc0f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_optimizer_options.h" + +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate(void) { + return new spv_optimizer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val) { + options->run_validator_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val) { + options->val_options_ = *val; +} +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val) { + options->max_id_bound_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveBindings( + spv_optimizer_options options, bool val) { + options->preserve_bindings_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveSpecConstants( + spv_optimizer_options options, bool val) { + options->preserve_spec_constants_ = val; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.h new file mode 100644 index 00000000..aa76d20b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_optimizer_options.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ +#define SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ + +#include "source/spirv_validator_options.h" +#include "spirv-tools/libspirv.h" + +// Manages command line options passed to the SPIR-V Validator. New struct +// members may be added for any new option. +struct spv_optimizer_options_t { + spv_optimizer_options_t() + : run_validator_(true), + val_options_(), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) {} + + // When true the validator will be run before optimizations are run. + bool run_validator_; + + // Options to pass to the validator if it is run. + spv_validator_options_t val_options_; + + // The maximum value the id bound for a module can have. The Spir-V spec says + // this value must be at least 0x3FFFFF, but implementations can allow for a + // higher value. + uint32_t max_id_bound_; + + // When true, all binding declarations within the module should be preserved. + bool preserve_bindings_; + + // When true, all specialization constants within the module should be + // preserved. + bool preserve_spec_constants_; +}; +#endif // SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.cpp new file mode 100644 index 00000000..9086433e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_reducer_options.h" + +namespace { +// The default maximum number of steps the reducer will take before giving up. +const uint32_t kDefaultStepLimit = 2500; +} // namespace + +spv_reducer_options_t::spv_reducer_options_t() + : step_limit(kDefaultStepLimit), + fail_on_validation_error(false), + target_function(0) {} + +SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate() { + return new spv_reducer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( + spv_reducer_options options, uint32_t step_limit) { + options->step_limit = step_limit; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error) { + options->fail_on_validation_error = fail_on_validation_error; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction( + spv_reducer_options options, uint32_t target_function) { + options->target_function = target_function; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.h new file mode 100644 index 00000000..911747dd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_reducer_options.h @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_REDUCER_OPTIONS_H_ +#define SOURCE_SPIRV_REDUCER_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +#include +#include + +// Manages command line options passed to the SPIR-V Reducer. New struct +// members may be added for any new option. +struct spv_reducer_options_t { + spv_reducer_options_t(); + + // See spvReducerOptionsSetStepLimit. + uint32_t step_limit; + + // See spvReducerOptionsSetFailOnValidationError. + bool fail_on_validation_error; + + // See spvReducerOptionsSetTargetFunction. + uint32_t target_function; +}; + +#endif // SOURCE_SPIRV_REDUCER_OPTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.cpp new file mode 100644 index 00000000..fe23fa64 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.cpp @@ -0,0 +1,510 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_target_env.h" + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.h" + +const char* spvTargetEnvDescription(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + return "SPIR-V 1.0"; + case SPV_ENV_VULKAN_1_0: + return "SPIR-V 1.0 (under Vulkan 1.0 semantics)"; + case SPV_ENV_UNIVERSAL_1_1: + return "SPIR-V 1.1"; + case SPV_ENV_OPENCL_1_2: + return "SPIR-V 1.0 (under OpenCL 1.2 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_1_2: + return "SPIR-V 1.0 (under OpenCL 1.2 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_0: + return "SPIR-V 1.0 (under OpenCL 2.0 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_0: + return "SPIR-V 1.0 (under OpenCL 2.0 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_1: + return "SPIR-V 1.0 (under OpenCL 2.1 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_1: + return "SPIR-V 1.0 (under OpenCL 2.1 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_2: + return "SPIR-V 1.2 (under OpenCL 2.2 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_2: + return "SPIR-V 1.2 (under OpenCL 2.2 Embedded Profile semantics)"; + case SPV_ENV_OPENGL_4_0: + return "SPIR-V 1.0 (under OpenGL 4.0 semantics)"; + case SPV_ENV_OPENGL_4_1: + return "SPIR-V 1.0 (under OpenGL 4.1 semantics)"; + case SPV_ENV_OPENGL_4_2: + return "SPIR-V 1.0 (under OpenGL 4.2 semantics)"; + case SPV_ENV_OPENGL_4_3: + return "SPIR-V 1.0 (under OpenGL 4.3 semantics)"; + case SPV_ENV_OPENGL_4_5: + return "SPIR-V 1.0 (under OpenGL 4.5 semantics)"; + case SPV_ENV_UNIVERSAL_1_2: + return "SPIR-V 1.2"; + case SPV_ENV_UNIVERSAL_1_3: + return "SPIR-V 1.3"; + case SPV_ENV_VULKAN_1_1: + return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_UNIVERSAL_1_4: + return "SPIR-V 1.4"; + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + return "SPIR-V 1.4 (under Vulkan 1.1 semantics)"; + case SPV_ENV_UNIVERSAL_1_5: + return "SPIR-V 1.5"; + case SPV_ENV_VULKAN_1_2: + return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_UNIVERSAL_1_6: + return "SPIR-V 1.6"; + case SPV_ENV_VULKAN_1_3: + return "SPIR-V 1.6 (under Vulkan 1.3 semantics)"; + case SPV_ENV_VULKAN_1_4: + return "SPIR-V 1.6 (under Vulkan 1.4 semantics)"; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return ""; +} + +uint32_t spvVersionForTargetEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return SPV_SPIRV_VERSION_WORD(1, 0); + case SPV_ENV_UNIVERSAL_1_1: + return SPV_SPIRV_VERSION_WORD(1, 1); + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + return SPV_SPIRV_VERSION_WORD(1, 2); + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + return SPV_SPIRV_VERSION_WORD(1, 3); + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + return SPV_SPIRV_VERSION_WORD(1, 4); + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + return SPV_SPIRV_VERSION_WORD(1, 6); + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return SPV_SPIRV_VERSION_WORD(0, 0); +} + +// When a new SPIR-V version is released, update this table. +static_assert(spv::Version == 0x10600); +constexpr auto ordered_universal_envs = std::array{ + SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, +}; + +// When a new SPIR-V version is released, update this table. +// Users see this ordered list when running 'spirv-val --help'. Order +// matters for readability. +static_assert(spv::Version == 0x10600); +inline constexpr std::pair spvTargetEnvNameMap[] = + { + // Do not reorder blindly. The algorithm to find the target looks for + // the first entry where the key is a prefix of the string provided by + // the user. For example, if the user provides `vulkan1.2spv1.5`, it + // will match `vulkan1.2`. If this feature is to work correctly, the + // keys must be ordered so that a string is before its prefix. For + // example, `vulkan1.1spv1.4` must be before `vulkan1.1`. Otherwise, + // `vulkan1.1` will be returned when looking for `vulkan1.1spv1.4`. + {"vulkan1.0", SPV_ENV_VULKAN_1_0}, + {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {"vulkan1.1", SPV_ENV_VULKAN_1_1}, + {"vulkan1.2", SPV_ENV_VULKAN_1_2}, + {"vulkan1.3", SPV_ENV_VULKAN_1_3}, + {"vulkan1.4", SPV_ENV_VULKAN_1_4}, + {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, + {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, + {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, + {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, + {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, + {"spv1.5", SPV_ENV_UNIVERSAL_1_5}, + {"spv1.6", SPV_ENV_UNIVERSAL_1_6}, + {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, + {"opencl1.2", SPV_ENV_OPENCL_1_2}, + {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, + {"opencl2.0", SPV_ENV_OPENCL_2_0}, + {"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1}, + {"opencl2.1", SPV_ENV_OPENCL_2_1}, + {"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2}, + {"opencl2.2", SPV_ENV_OPENCL_2_2}, + {"opengl4.0", SPV_ENV_OPENGL_4_0}, + {"opengl4.1", SPV_ENV_OPENGL_4_1}, + {"opengl4.2", SPV_ENV_OPENGL_4_2}, + {"opengl4.3", SPV_ENV_OPENGL_4_3}, + {"opengl4.5", SPV_ENV_OPENGL_4_5}, +}; + +bool spvParseTargetEnv(const char* s, spv_target_env* env) { + auto match = [s](const char* b) { + return s && (0 == strncmp(s, b, strlen(b))); + }; + for (auto& name_env : spvTargetEnvNameMap) { + if (match(name_env.first)) { + if (env) { + *env = name_env.second; + } + return true; + } + } + if (env) *env = SPV_ENV_UNIVERSAL_1_0; + return false; +} + +bool spvReadEnvironmentFromText(const std::vector& text, + spv_target_env* env) { + // Version is expected to match "; Version: 1.X" + // Version string must occur in header, that is, initial lines of comments + // Once a non-comment line occurs, the header has ended + for (std::size_t i = 0; i < text.size(); ++i) { + char c = text[i]; + + if (c == ';') { + // Try to match against the expected version string + constexpr const char* kVersionPrefix = "; Version: 1."; + constexpr const auto kPrefixLength = 13; + // 'minor_digit_pos' is the expected position of the version digit. + const auto minor_digit_pos = i + kPrefixLength; + if (minor_digit_pos >= text.size()) return false; + + // Match the prefix. + auto j = 1; + for (; j < kPrefixLength; ++j) { + if (kVersionPrefix[j] != text[i + j]) break; + } + // j will match the prefix length if all characters before matched + if (j == kPrefixLength) { + // This expects only one digit in the minor number. + static_assert(((spv::Version >> 8) & 0xff) < 10); + char minor = text[minor_digit_pos]; + char next_char = + minor_digit_pos + 1 < text.size() ? text[minor_digit_pos + 1] : 0; + if (std::isdigit(minor) && !std::isdigit(next_char)) { + const auto index = minor - '0'; + assert(index >= 0); + if (static_cast(index) < ordered_universal_envs.size()) { + *env = ordered_universal_envs[index]; + return true; + } + } + } + + // If no match, determine whether the header has ended (in which case, + // assumption has failed.) + // Skip until the next line. + i += j; + for (; i < text.size(); ++i) { + if (text[i] == '\n') break; + } + } else if (!std::isspace(c)) { + // Allow blanks, but end the search if we find something else. + break; + } + } + return false; +} + +#define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12)) +#define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8)) + +struct VulkanEnv { + spv_target_env vulkan_env; + uint32_t vulkan_ver; + uint32_t spirv_ver; +}; +// Maps each Vulkan target environment enum to the Vulkan version, and the +// maximum supported SPIR-V version for that Vulkan environment. +// Keep this ordered from least capable to most capable. +static const VulkanEnv ordered_vulkan_envs[] = { + {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, + {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, + {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}, + {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}, + {SPV_ENV_VULKAN_1_4, VULKAN_VER(1, 4), SPIRV_VER(1, 6)}}; + +bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, + spv_target_env* env) { + for (auto triple : ordered_vulkan_envs) { + if (triple.vulkan_ver >= vulkan_ver && triple.spirv_ver >= spirv_ver) { + *env = triple.vulkan_env; + return true; + } + } + return false; +} + +bool spvIsVulkanEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: + return false; + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + return true; + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return false; +} + +bool spvIsOpenCLEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + return false; + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + return true; + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return false; +} + +bool spvIsOpenGLEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + return false; + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return false; +} + +bool spvIsValidEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + case SPV_ENV_WEBGPU_0: + case SPV_ENV_MAX: + break; + } + return false; +} + +std::string spvLogStringForEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: { + return "OpenCL"; + } + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: { + return "OpenGL"; + } + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: { + return "Vulkan"; + } + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: { + return "Universal"; + } + case SPV_ENV_WEBGPU_0: + assert(false && "Deprecated target environment value."); + break; + case SPV_ENV_MAX: + assert(false && "Invalid target environment value."); + break; + } + return "Unknown"; +} + +std::string spvTargetEnvList(const int pad, const int wrap) { + std::string ret; + size_t max_line_len = wrap - pad; // The first line isn't padded + std::string line; + std::string sep = ""; + + for (auto& name_env : spvTargetEnvNameMap) { + std::string word = sep + name_env.first; + if (line.length() + word.length() > max_line_len) { + // Adding one word wouldn't fit, commit the line in progress and + // start a new one. + ret += line + "\n"; + line.assign(pad, ' '); + // The first line is done. The max length now comprises the + // padding. + max_line_len = wrap; + } + line += word; + sep = "|"; + } + + ret += line; + + return ret; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.h new file mode 100644 index 00000000..4378f06f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_target_env.h @@ -0,0 +1,56 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_TARGET_ENV_H_ +#define SOURCE_SPIRV_TARGET_ENV_H_ + +#include +#include +#include + +#include "spirv-tools/libspirv.h" + +// Returns true if |env| is a VULKAN environment, false otherwise. +bool spvIsVulkanEnv(spv_target_env env); + +// Returns true if |env| is an OPENCL environment, false otherwise. +bool spvIsOpenCLEnv(spv_target_env env); + +// Returns true if |env| is an OPENGL environment, false otherwise. +bool spvIsOpenGLEnv(spv_target_env env); + +// Returns true if |env| is an implemented/valid environment, false otherwise. +bool spvIsValidEnv(spv_target_env env); + +// Returns the version number for the given SPIR-V target environment. +uint32_t spvVersionForTargetEnv(spv_target_env env); + +// Returns a string to use in logging messages that indicates the class of +// environment, i.e. "Vulkan", "OpenCL", etc. +std::string spvLogStringForEnv(spv_target_env env); + +// Returns a formatted list of all SPIR-V target environment names that +// can be parsed by spvParseTargetEnv. +// |pad| is the number of space characters that the beginning of each line +// except the first one will be padded with. +// |wrap| is the max length of lines the user desires. Word-wrapping will +// occur to satisfy this limit. +std::string spvTargetEnvList(const int pad, const int wrap); + +// Reads the target environment from the header comments of disassembly. Returns +// true if valid name found, false otherwise. +bool spvReadEnvironmentFromText(const std::vector& text, + spv_target_env* env); + +#endif // SOURCE_SPIRV_TARGET_ENV_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.cpp b/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.cpp new file mode 100644 index 00000000..a9591f6b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_validator_options.h" + +#include +#include + +bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* type) { + auto match = [s](const char* b) { + return s && (0 == strncmp(s, b, strlen(b))); + }; + if (match("--max-struct-members")) { + *type = spv_validator_limit_max_struct_members; + } else if (match("--max-struct_depth")) { + *type = spv_validator_limit_max_struct_depth; + } else if (match("--max-local-variables")) { + *type = spv_validator_limit_max_local_variables; + } else if (match("--max-global-variables")) { + *type = spv_validator_limit_max_global_variables; + } else if (match("--max-switch-branches")) { + *type = spv_validator_limit_max_global_variables; + } else if (match("--max-function-args")) { + *type = spv_validator_limit_max_function_args; + } else if (match("--max-control-flow-nesting-depth")) { + *type = spv_validator_limit_max_control_flow_nesting_depth; + } else if (match("--max-access-chain-indexes")) { + *type = spv_validator_limit_max_access_chain_indexes; + } else if (match("--max-id-bound")) { + *type = spv_validator_limit_max_id_bound; + } else { + // The command line option for this validator limit has not been added. + // Therefore we return false. + return false; + } + + return true; +} + +spv_validator_options spvValidatorOptionsCreate(void) { + return new spv_validator_options_t; +} + +void spvValidatorOptionsDestroy(spv_validator_options options) { + delete options; +} + +void spvValidatorOptionsSetUniversalLimit(spv_validator_options options, + spv_validator_limit limit_type, + uint32_t limit) { + assert(options && "Validator options object may not be Null"); + switch (limit_type) { +#define LIMIT(TYPE, FIELD) \ + case TYPE: \ + options->universal_limits_.FIELD = limit; \ + break; + LIMIT(spv_validator_limit_max_struct_members, max_struct_members) + LIMIT(spv_validator_limit_max_struct_depth, max_struct_depth) + LIMIT(spv_validator_limit_max_local_variables, max_local_variables) + LIMIT(spv_validator_limit_max_global_variables, max_global_variables) + LIMIT(spv_validator_limit_max_switch_branches, max_switch_branches) + LIMIT(spv_validator_limit_max_function_args, max_function_args) + LIMIT(spv_validator_limit_max_control_flow_nesting_depth, + max_control_flow_nesting_depth) + LIMIT(spv_validator_limit_max_access_chain_indexes, + max_access_chain_indexes) + LIMIT(spv_validator_limit_max_id_bound, max_id_bound) +#undef LIMIT + } +} + +void spvValidatorOptionsSetRelaxStoreStruct(spv_validator_options options, + bool val) { + options->relax_struct_store = val; +} + +void spvValidatorOptionsSetRelaxLogicalPointer(spv_validator_options options, + bool val) { + options->relax_logical_pointer = val; +} + +void spvValidatorOptionsSetBeforeHlslLegalization(spv_validator_options options, + bool val) { + options->before_hlsl_legalization = val; + options->relax_logical_pointer = val; +} + +void spvValidatorOptionsSetRelaxBlockLayout(spv_validator_options options, + bool val) { + options->relax_block_layout = val; +} + +void spvValidatorOptionsSetUniformBufferStandardLayout( + spv_validator_options options, bool val) { + options->uniform_buffer_standard_layout = val; +} + +void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options, + bool val) { + options->scalar_block_layout = val; +} + +void spvValidatorOptionsSetWorkgroupScalarBlockLayout(spv_validator_options options, + bool val) { + options->workgroup_scalar_block_layout = val; +} + +void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options, + bool val) { + options->skip_block_layout = val; +} + +void spvValidatorOptionsSetAllowLocalSizeId(spv_validator_options options, + bool val) { + options->allow_localsizeid = val; +} + +void spvValidatorOptionsSetAllowOffsetTextureOperand( + spv_validator_options options, bool val) { + options->allow_offset_texture_operand = val; +} + +void spvValidatorOptionsSetAllowVulkan32BitBitwise( + spv_validator_options options, bool val) { + options->allow_vulkan_32_bit_bitwise = val; +} + +void spvValidatorOptionsSetFriendlyNames(spv_validator_options options, + bool val) { + options->use_friendly_names = val; +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.h b/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.h new file mode 100644 index 00000000..9f0c1254 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/spirv_validator_options.h @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ +#define SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +// Return true if the command line option for the validator limit is valid (Also +// returns the Enum for option in this case). Returns false otherwise. +bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* limit); + +// Default initialization of this structure is to the default Universal Limits +// described in the SPIR-V Spec. +struct validator_universal_limits_t { + uint32_t max_struct_members{16383}; + uint32_t max_struct_depth{255}; + uint32_t max_local_variables{524287}; + uint32_t max_global_variables{65535}; + uint32_t max_switch_branches{16383}; + uint32_t max_function_args{255}; + uint32_t max_control_flow_nesting_depth{1023}; + uint32_t max_access_chain_indexes{255}; + uint32_t max_id_bound{0x3FFFFF}; +}; + +// Manages command line options passed to the SPIR-V Validator. New struct +// members may be added for any new option. +struct spv_validator_options_t { + spv_validator_options_t() + : universal_limits_(), + relax_struct_store(false), + relax_logical_pointer(false), + relax_block_layout(false), + uniform_buffer_standard_layout(false), + scalar_block_layout(false), + workgroup_scalar_block_layout(false), + skip_block_layout(false), + allow_localsizeid(false), + allow_offset_texture_operand(false), + allow_vulkan_32_bit_bitwise(false), + before_hlsl_legalization(false), + use_friendly_names(true) {} + + validator_universal_limits_t universal_limits_; + bool relax_struct_store; + bool relax_logical_pointer; + bool relax_block_layout; + bool uniform_buffer_standard_layout; + bool scalar_block_layout; + bool workgroup_scalar_block_layout; + bool skip_block_layout; + bool allow_localsizeid; + bool allow_offset_texture_operand; + bool allow_vulkan_32_bit_bitwise; + bool before_hlsl_legalization; + bool use_friendly_names; +}; + +#endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/table.cpp b/engine/engine/3rdlibs/spirv-tools/source/table.cpp new file mode 100644 index 00000000..fb2726bd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/table.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/table.h" + +#include + +spv_context spvContextCreate(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + case SPV_ENV_VULKAN_1_4: + break; + default: + return nullptr; + } + + spv_opcode_table opcode_table = nullptr; + spv_operand_table operand_table = nullptr; + spv_ext_inst_table ext_inst_table = nullptr; + + spvOpcodeTableGet(&opcode_table, env); + spvOperandTableGet(&operand_table, env); + spvExtInstTableGet(&ext_inst_table, env); + + return new spv_context_t{env, opcode_table, operand_table, ext_inst_table, + nullptr /* a null default consumer */}; +} + +void spvContextDestroy(spv_context context) { delete context; } + +void spvtools::SetContextMessageConsumer(spv_context context, + spvtools::MessageConsumer consumer) { + context->consumer = std::move(consumer); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/table.h b/engine/engine/3rdlibs/spirv-tools/source/table.h new file mode 100644 index 00000000..47625c5f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/table.h @@ -0,0 +1,137 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TABLE_H_ +#define SOURCE_TABLE_H_ + +#include "source/extensions.h" +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.hpp" + +typedef struct spv_opcode_desc_t { + const char* name; + const spv::Op opcode; + const uint32_t numAliases; + const char** aliases; + const uint32_t numCapabilities; + const spv::Capability* capabilities; + // operandTypes[0..numTypes-1] describe logical operands for the instruction. + // The operand types include result id and result-type id, followed by + // the types of arguments. + const uint16_t numTypes; + spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + const bool hasResult; // Does the instruction have a result ID operand? + const bool hasType; // Does the instruction have a type ID operand? + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const uint32_t numExtensions; + const spvtools::Extension* extensions; + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty extension + // lists means only available in extensions. + const uint32_t minVersion; + const uint32_t lastVersion; +} spv_opcode_desc_t; + +typedef struct spv_operand_desc_t { + const char* name; + const uint32_t value; + const uint32_t numAliases; + const char** aliases; + const uint32_t numCapabilities; + const spv::Capability* capabilities; + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const uint32_t numExtensions; + const spvtools::Extension* extensions; + const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty extension + // lists means only available in extensions. + const uint32_t minVersion; + const uint32_t lastVersion; +} spv_operand_desc_t; + +typedef struct spv_operand_desc_group_t { + const spv_operand_type_t type; + const uint32_t count; + const spv_operand_desc_t* entries; +} spv_operand_desc_group_t; + +typedef struct spv_ext_inst_desc_t { + const char* name; + const uint32_t ext_inst; + const uint32_t numCapabilities; + const spv::Capability* capabilities; + const spv_operand_type_t operandTypes[40]; // vksp needs at least 40 +} spv_ext_inst_desc_t; + +typedef struct spv_ext_inst_group_t { + const spv_ext_inst_type_t type; + const uint32_t count; + const spv_ext_inst_desc_t* entries; +} spv_ext_inst_group_t; + +typedef struct spv_opcode_table_t { + const uint32_t count; + const spv_opcode_desc_t* entries; +} spv_opcode_table_t; + +typedef struct spv_operand_table_t { + const uint32_t count; + const spv_operand_desc_group_t* types; +} spv_operand_table_t; + +typedef struct spv_ext_inst_table_t { + const uint32_t count; + const spv_ext_inst_group_t* groups; +} spv_ext_inst_table_t; + +typedef const spv_opcode_desc_t* spv_opcode_desc; +typedef const spv_operand_desc_t* spv_operand_desc; +typedef const spv_ext_inst_desc_t* spv_ext_inst_desc; + +typedef const spv_opcode_table_t* spv_opcode_table; +typedef const spv_operand_table_t* spv_operand_table; +typedef const spv_ext_inst_table_t* spv_ext_inst_table; + +struct spv_context_t { + const spv_target_env target_env; + const spv_opcode_table opcode_table; + const spv_operand_table operand_table; + const spv_ext_inst_table ext_inst_table; + spvtools::MessageConsumer consumer; +}; + +namespace spvtools { + +// Sets the message consumer to |consumer| in the given |context|. The original +// message consumer will be overwritten. +void SetContextMessageConsumer(spv_context context, MessageConsumer consumer); +} // namespace spvtools + +// Populates *table with entries for env. +spv_result_t spvOpcodeTableGet(spv_opcode_table* table, spv_target_env env); + +// Populates *table with entries for env. +spv_result_t spvOperandTableGet(spv_operand_table* table, spv_target_env env); + +// Populates *table with entries for env. +spv_result_t spvExtInstTableGet(spv_ext_inst_table* table, spv_target_env env); + +#endif // SOURCE_TABLE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/text.cpp b/engine/engine/3rdlibs/spirv-tools/source/text.cpp new file mode 100644 index 00000000..156d9627 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/text.cpp @@ -0,0 +1,995 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/text.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/ext_inst.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/table.h" +#include "source/text_handler.h" +#include "source/util/bitutils.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +bool spvIsValidIDCharacter(const char value) { + return value == '_' || 0 != ::isalnum(value); +} + +// Returns true if the given string represents a valid ID name. +bool spvIsValidID(const char* textValue) { + const char* c = textValue; + for (; *c != '\0'; ++c) { + if (!spvIsValidIDCharacter(*c)) { + return false; + } + } + // If the string was empty, then the ID also is not valid. + return c != textValue; +} + +// Text API + +spv_result_t spvTextToLiteral(const char* textValue, spv_literal_t* pLiteral) { + bool isSigned = false; + int numPeriods = 0; + bool isString = false; + + const size_t len = strlen(textValue); + if (len == 0) return SPV_FAILED_MATCH; + + for (uint64_t index = 0; index < len; ++index) { + switch (textValue[index]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case '.': + numPeriods++; + break; + case '-': + if (index == 0) { + isSigned = true; + } else { + isString = true; + } + break; + default: + isString = true; + index = len; // break out of the loop too. + break; + } + } + + pLiteral->type = spv_literal_type_t(99); + + if (isString || numPeriods > 1 || (isSigned && len == 1)) { + if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"') + return SPV_FAILED_MATCH; + bool escaping = false; + for (const char* val = textValue + 1; val != textValue + len - 1; ++val) { + if ((*val == '\\') && (!escaping)) { + escaping = true; + } else { + // Have to save space for the null-terminator + if (pLiteral->str.size() >= SPV_LIMIT_LITERAL_STRING_BYTES_MAX) + return SPV_ERROR_OUT_OF_MEMORY; + pLiteral->str.push_back(*val); + escaping = false; + } + } + + pLiteral->type = SPV_LITERAL_TYPE_STRING; + } else if (numPeriods == 1) { + double d = std::strtod(textValue, nullptr); + float f = (float)d; + if (d == (double)f) { + pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32; + pLiteral->value.f = f; + } else { + pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64; + pLiteral->value.d = d; + } + } else if (isSigned) { + int64_t i64 = strtoll(textValue, nullptr, 10); + int32_t i32 = (int32_t)i64; + if (i64 == (int64_t)i32) { + pLiteral->type = SPV_LITERAL_TYPE_INT_32; + pLiteral->value.i32 = i32; + } else { + pLiteral->type = SPV_LITERAL_TYPE_INT_64; + pLiteral->value.i64 = i64; + } + } else { + uint64_t u64 = strtoull(textValue, nullptr, 10); + uint32_t u32 = (uint32_t)u64; + if (u64 == (uint64_t)u32) { + pLiteral->type = SPV_LITERAL_TYPE_UINT_32; + pLiteral->value.u32 = u32; + } else { + pLiteral->type = SPV_LITERAL_TYPE_UINT_64; + pLiteral->value.u64 = u64; + } + } + + return SPV_SUCCESS; +} + +namespace { + +/// Parses an immediate integer from text, guarding against overflow. If +/// successful, adds the parsed value to pInst, advances the context past it, +/// and returns SPV_SUCCESS. Otherwise, leaves pInst alone, emits diagnostics, +/// and returns SPV_ERROR_INVALID_TEXT. +spv_result_t encodeImmediate(spvtools::AssemblyContext* context, + const char* text, spv_instruction_t* pInst) { + assert(*text == '!'); + uint32_t parse_result; + if (!spvtools::utils::ParseNumber(text + 1, &parse_result)) { + return context->diagnostic(SPV_ERROR_INVALID_TEXT) + << "Invalid immediate integer: !" << text + 1; + } + context->binaryEncodeU32(parse_result, pInst); + context->seekForward(static_cast(strlen(text))); + return SPV_SUCCESS; +} + +} // anonymous namespace + +/// @brief Translate an Opcode operand to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[out] pInst return binary Opcode +/// @param[in,out] pExpectedOperands the operand types expected +/// +/// @return result code +spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, + const spv_operand_type_t type, + const char* textValue, + spv_instruction_t* pInst, + spv_operand_pattern_t* pExpectedOperands) { + // NOTE: Handle immediate int in the stream + if ('!' == textValue[0]) { + if (auto error = encodeImmediate(context, textValue, pInst)) { + return error; + } + *pExpectedOperands = + spvAlternatePatternFollowingImmediate(*pExpectedOperands); + return SPV_SUCCESS; + } + + // Optional literal operands can fail to parse. In that case use + // SPV_FAILED_MATCH to avoid emitting a diagnostic. Use the following + // for those situations. + spv_result_t error_code_for_literals = + spvOperandIsOptional(type) ? SPV_FAILED_MATCH : SPV_ERROR_INVALID_TEXT; + + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: { + if ('%' == textValue[0]) { + textValue++; + } else { + return context->diagnostic() << "Expected id to start with %."; + } + if (!spvIsValidID(textValue)) { + return context->diagnostic() << "Invalid ID " << textValue; + } + const uint32_t id = context->spvNamedIdAssignOrGet(textValue); + if (type == SPV_OPERAND_TYPE_TYPE_ID) pInst->resultTypeId = id; + spvInstructionAddWord(pInst, id); + + // Set the extended instruction type. + // The import set id is the 3rd operand of OpExtInst. + if (spvIsExtendedInstruction(pInst->opcode) && pInst->words.size() == 4) { + auto ext_inst_type = context->getExtInstTypeForId(pInst->words[3]); + if (ext_inst_type == SPV_EXT_INST_TYPE_NONE) { + return context->diagnostic() + << "Invalid extended instruction import Id " + << pInst->words[2]; + } + pInst->extInstType = ext_inst_type; + } + } break; + + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + // The assembler accepts the symbolic name for an extended instruction, + // and emits its corresponding number. + spv_ext_inst_desc extInst; + if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) == + SPV_SUCCESS) { + // if we know about this extended instruction, push the numeric value + spvInstructionAddWord(pInst, extInst->ext_inst); + + // Prepare to parse the operands for the extended instructions. + spvPushOperandTypes(extInst->operandTypes, pExpectedOperands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(pInst->extInstType)) { + return context->diagnostic() + << "Invalid extended instruction name '" << textValue << "'."; + } else { + // for non-semantic instruction sets, as long as the text name is an + // integer value we can encode it since we know the form of all such + // extended instructions + spv_literal_t extInstValue; + if (spvTextToLiteral(textValue, &extInstValue) || + extInstValue.type != SPV_LITERAL_TYPE_UINT_32) { + return context->diagnostic() + << "Couldn't translate unknown extended instruction name '" + << textValue << "' to unsigned integer."; + } + + spvInstructionAddWord(pInst, extInstValue.value.u32); + + // opcode contains an unknown number of IDs. + pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } + } break; + + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + // The assembler accepts the symbolic name for the opcode, but without + // the "Op" prefix. For example, "IAdd" is accepted. The number + // of the opcode is emitted. + spv::Op opcode; + if (grammar.lookupSpecConstantOpcode(textValue, &opcode)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + spv_opcode_desc opcodeEntry = nullptr; + if (grammar.lookupOpcode(opcode, &opcodeEntry)) { + return context->diagnostic(SPV_ERROR_INTERNAL) + << "OpSpecConstant opcode table out of sync"; + } + spvInstructionAddWord(pInst, uint32_t(opcodeEntry->opcode)); + + // Prepare to parse the operands for the opcode. Except skip the + // type Id and result Id, since they've already been processed. + assert(opcodeEntry->hasType); + assert(opcodeEntry->hasResult); + assert(opcodeEntry->numTypes >= 2); + spvPushOperandTypes(opcodeEntry->operandTypes + 2, pExpectedOperands); + } break; + + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: { + // The current operand is an *unsigned* 32-bit integer. + // That's just how the grammar works. + spvtools::IdType expected_type = { + 32, false, spvtools::IdTypeClass::kScalarIntegerType}; + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + + case SPV_OPERAND_TYPE_LITERAL_FLOAT: { + // The current operand is a 32-bit float. + // That's just how the grammar works. + spvtools::IdType expected_type = { + 32, false, spvtools::IdTypeClass::kScalarFloatType}; + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + // This is a context-independent literal number which can be a 32-bit + // number of floating point value. + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, spvtools::kUnknownType, + pInst)) { + return error; + } + break; + + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { + spvtools::IdType expected_type = spvtools::kUnknownType; + // The encoding for OpConstant, OpSpecConstant and OpSwitch all + // depend on either their own result-id or the result-id of + // one of their parameters. + if (spv::Op::OpConstant == pInst->opcode || + spv::Op::OpSpecConstant == pInst->opcode) { + // The type of the literal is determined by the type Id of the + // instruction. + expected_type = + context->getTypeOfTypeGeneratingValue(pInst->resultTypeId); + if (!spvtools::isScalarFloating(expected_type) && + !spvtools::isScalarIntegral(expected_type)) { + spv_opcode_desc d; + const char* opcode_name = "opcode"; + if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) { + opcode_name = d->name; + } + return context->diagnostic() + << "Type for " << opcode_name + << " must be a scalar floating point or integer type"; + } + } else if (pInst->opcode == spv::Op::OpSwitch) { + // The type of the literal is the same as the type of the selector. + expected_type = context->getTypeOfValueInstruction(pInst->words[1]); + if (!spvtools::isScalarIntegral(expected_type)) { + return context->diagnostic() + << "The selector operand for OpSwitch must be the result" + " of an instruction that generates an integer scalar"; + } + } + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { + spv_literal_t literal = {}; + spv_result_t error = spvTextToLiteral(textValue, &literal); + if (error != SPV_SUCCESS) { + if (error == SPV_ERROR_OUT_OF_MEMORY) return error; + return context->diagnostic(error_code_for_literals) + << "Invalid literal string '" << textValue << "'."; + } + if (literal.type != SPV_LITERAL_TYPE_STRING) { + return context->diagnostic() + << "Expected literal string, found literal number '" << textValue + << "'."; + } + + // NOTE: Special case for extended instruction library import + if (spv::Op::OpExtInstImport == pInst->opcode) { + const spv_ext_inst_type_t ext_inst_type = + spvExtInstImportTypeGet(literal.str.c_str()); + if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { + return context->diagnostic() + << "Invalid extended instruction import '" << literal.str + << "'"; + } + if ((error = context->recordIdAsExtInstImport(pInst->words[1], + ext_inst_type))) + return error; + } + + if (context->binaryEncodeString(literal.str.c_str(), pInst)) + return SPV_ERROR_INVALID_TEXT; + } break; + + // Masks. + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: + case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: + case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: + case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: { + uint32_t value; + if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { + return context->diagnostic(error) + << "Invalid " << spvOperandTypeStr(type) << " operand '" + << textValue << "'."; + } + if (auto error = context->binaryEncodeU32(value, pInst)) return error; + // Prepare to parse the operands for this logical operand. + grammar.pushOperandTypesForMask(type, value, pExpectedOperands); + } break; + case SPV_OPERAND_TYPE_OPTIONAL_CIV: { + auto error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, textValue, + pInst, pExpectedOperands); + if (error == SPV_FAILED_MATCH) { + // It's not a literal number -- is it a literal string? + error = spvTextEncodeOperand(grammar, context, + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, + textValue, pInst, pExpectedOperands); + } + if (error == SPV_FAILED_MATCH) { + // It's not a literal -- is it an ID? + error = + spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_OPTIONAL_ID, + textValue, pInst, pExpectedOperands); + } + if (error) { + return context->diagnostic(error) + << "Invalid word following !: " << textValue; + } + if (pExpectedOperands->empty()) { + pExpectedOperands->push_back(SPV_OPERAND_TYPE_OPTIONAL_CIV); + } + } break; + default: { + // NOTE: All non literal operands are handled here using the operand + // table. + spv_operand_desc entry; + if (grammar.lookupOperand(type, textValue, strlen(textValue), &entry)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + if (context->binaryEncodeU32(entry->value, pInst)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + + // Prepare to parse the operands for this logical operand. + spvPushOperandTypes(entry->operandTypes, pExpectedOperands); + } break; + } + return SPV_SUCCESS; +} + +namespace { + +/// Encodes an instruction started by ! at the given position in text. +/// +/// Puts the encoded words into *pInst. If successful, moves position past the +/// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and +/// leaves position pointing to the error in text. +spv_result_t encodeInstructionStartingWithImmediate( + const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, spv_instruction_t* pInst) { + std::string firstWord; + spv_position_t nextPosition = {}; + auto error = context->getWord(&firstWord, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if ((error = encodeImmediate(context, firstWord.c_str(), pInst))) { + return error; + } + while (context->advance() != SPV_END_OF_STREAM) { + // A beginning of a new instruction means we're done. + if (context->isStartOfNewInst()) return SPV_SUCCESS; + + // Otherwise, there must be an operand that's either a literal, an ID, or + // an immediate. + std::string operandValue; + if ((error = context->getWord(&operandValue, &nextPosition))) + return context->diagnostic(error) << "Internal Error"; + + if (operandValue == "=") + return context->diagnostic() << firstWord << " not allowed before =."; + + // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be + // expanded. + spv_operand_pattern_t dummyExpectedOperands; + error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), + pInst, &dummyExpectedOperands); + if (error) return error; + context->setPosition(nextPosition); + } + return SPV_SUCCESS; +} + +/// @brief Translate an instruction started by OpUnknown and the following +/// operands to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[out] pInst returned binary Opcode +/// +/// @return result code +spv_result_t encodeInstructionStartingWithOpUnknown( + const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, spv_instruction_t* pInst) { + spv_position_t nextPosition = {}; + + uint16_t opcode; + uint16_t wordCount; + + // The '(' character. + if (context->advance()) + return context->diagnostic() << "Expected '(', found end of stream."; + if ('(' != context->peek()) { + return context->diagnostic() << "'(' expected after OpUnknown but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + // The opcode enumerant. + if (context->advance()) + return context->diagnostic() + << "Expected opcode enumerant, found end of stream."; + std::string opcodeString; + spv_result_t error = context->getWord(&opcodeString, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if (!spvtools::utils::ParseNumber(opcodeString.c_str(), &opcode)) { + return context->diagnostic() + << "Invalid opcode enumerant: \"" << opcodeString << "\"."; + } + + context->setPosition(nextPosition); + + // The ',' character. + if (context->advance()) + return context->diagnostic() << "Expected ',', found end of stream."; + if (',' != context->peek()) { + return context->diagnostic() + << "',' expected after opcode enumerant but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + // The number of words. + if (context->advance()) + return context->diagnostic() + << "Expected number of words, found end of stream."; + std::string wordCountString; + error = context->getWord(&wordCountString, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if (!spvtools::utils::ParseNumber(wordCountString.c_str(), &wordCount)) { + return context->diagnostic() + << "Invalid number of words: \"" << wordCountString << "\"."; + } + + if (wordCount == 0) { + return context->diagnostic() << "Number of words (which includes the " + "opcode) must be greater than zero."; + } + + context->setPosition(nextPosition); + + // The ')' character. + if (context->advance()) + return context->diagnostic() << "Expected ')', found end of stream."; + if (')' != context->peek()) { + return context->diagnostic() + << "')' expected after number of words but found '" + << context->peek() << "'."; + } + context->seekForward(1); + + pInst->opcode = static_cast(opcode); + context->binaryEncodeU32(spvOpcodeMake(wordCount, pInst->opcode), pInst); + + wordCount--; // Subtract the opcode from the number of words left to read. + + while (wordCount-- > 0) { + if (context->advance() == SPV_END_OF_STREAM) { + return context->diagnostic() << "Expected " << wordCount + 1 + << " more operands, found end of stream."; + } + if (context->isStartOfNewInst()) { + std::string invalid; + context->getWord(&invalid, &nextPosition); + return context->diagnostic() + << "Unexpected start of new instruction: \"" << invalid + << "\". Expected " << wordCount + 1 << " more operands"; + } + + std::string operandValue; + if ((error = context->getWord(&operandValue, &nextPosition))) + return context->diagnostic(error) << "Internal Error"; + + if (operandValue == "=") + return context->diagnostic() << "OpUnknown not allowed before =."; + + // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be + // expanded. + spv_operand_pattern_t dummyExpectedOperands; + error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), + pInst, &dummyExpectedOperands); + if (error) return error; + context->setPosition(nextPosition); + } + + return SPV_SUCCESS; +} + +/// @brief Translate single Opcode and operands to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[in] text stream to translate +/// @param[out] pInst returned binary Opcode +/// @param[in,out] pPosition in the text stream +/// +/// @return result code +spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, + spv_instruction_t* pInst) { + // Check for ! first. + if ('!' == context->peek()) { + return encodeInstructionStartingWithImmediate(grammar, context, pInst); + } + + std::string firstWord; + spv_position_t nextPosition = {}; + spv_result_t error = context->getWord(&firstWord, &nextPosition); + if (error) return context->diagnostic() << "Internal Error"; + + std::string opcodeName; + std::string result_id; + spv_position_t result_id_position = {}; + if (context->startsWithOp()) { + opcodeName = firstWord; + } else { + result_id = firstWord; + if ('%' != result_id.front()) { + return context->diagnostic() + << "Expected or at the beginning " + "of an instruction, found '" + << result_id << "'."; + } + result_id_position = context->position(); + + // The '=' sign. + context->setPosition(nextPosition); + if (context->advance()) + return context->diagnostic() << "Expected '=', found end of stream."; + std::string equal_sign; + error = context->getWord(&equal_sign, &nextPosition); + if ("=" != equal_sign) + return context->diagnostic() << "'=' expected after result id but found '" + << equal_sign << "'."; + + // The after the '=' sign. + context->setPosition(nextPosition); + if (context->advance()) + return context->diagnostic() << "Expected opcode, found end of stream."; + error = context->getWord(&opcodeName, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + if (!context->startsWithOp()) { + return context->diagnostic() + << "Invalid Opcode prefix '" << opcodeName << "'."; + } + } + + if (opcodeName == "OpUnknown") { + if (!result_id.empty()) { + return context->diagnostic() + << "OpUnknown not allowed in assignment. Use an explicit result " + "id operand instead."; + } + context->setPosition(nextPosition); + return encodeInstructionStartingWithOpUnknown(grammar, context, pInst); + } + + // NOTE: The table contains Opcode names without the "Op" prefix. + const char* pInstName = opcodeName.data() + 2; + + spv_opcode_desc opcodeEntry; + error = grammar.lookupOpcode(pInstName, &opcodeEntry); + if (error) { + return context->diagnostic(error) + << "Invalid Opcode name '" << opcodeName << "'"; + } + if (opcodeEntry->hasResult && result_id.empty()) { + return context->diagnostic() + << "Expected at the beginning of an instruction, found '" + << firstWord << "'."; + } + if (!opcodeEntry->hasResult && !result_id.empty()) { + return context->diagnostic() + << "Cannot set ID " << result_id << " because " << opcodeName + << " does not produce a result ID."; + } + pInst->opcode = opcodeEntry->opcode; + context->setPosition(nextPosition); + // Reserve the first word for the instruction. + spvInstructionAddWord(pInst, 0); + + // Maintains the ordered list of expected operand types. + // For many instructions we only need the {numTypes, operandTypes} + // entries in opcodeEntry. However, sometimes we need to modify + // the list as we parse the operands. This occurs when an operand + // has its own logical operands (such as the LocalSize operand for + // ExecutionMode), or for extended instructions that may have their + // own operands depending on the selected extended instruction. + spv_operand_pattern_t expectedOperands; + expectedOperands.reserve(opcodeEntry->numTypes); + for (auto i = 0; i < opcodeEntry->numTypes; i++) + expectedOperands.push_back( + opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]); + + while (!expectedOperands.empty()) { + const spv_operand_type_t type = expectedOperands.back(); + expectedOperands.pop_back(); + + // Expand optional tuples lazily. + if (spvExpandOperandSequenceOnce(type, &expectedOperands)) continue; + + if (type == SPV_OPERAND_TYPE_RESULT_ID && !result_id.empty()) { + // Handle the for value generating instructions. + // We've already consumed it from the text stream. Here + // we inject its words into the instruction. + spv_position_t temp_pos = context->position(); + error = spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_RESULT_ID, + result_id.c_str(), pInst, nullptr); + result_id_position = context->position(); + // Because we are injecting we have to reset the position afterwards. + context->setPosition(temp_pos); + if (error) return error; + } else { + // Find the next word. + error = context->advance(); + if (error == SPV_END_OF_STREAM) { + if (spvOperandIsOptional(type)) { + // This would have been the last potential operand for the + // instruction, + // and we didn't find one. We're finished parsing this instruction. + break; + } else { + return context->diagnostic() + << "Expected operand for " << opcodeName + << " instruction, but found the end of the stream."; + } + } + assert(error == SPV_SUCCESS && "Somebody added another way to fail"); + + if (context->isStartOfNewInst()) { + if (spvOperandIsOptional(type)) { + break; + } else { + return context->diagnostic() + << "Expected operand for " << opcodeName + << " instruction, but found the next instruction instead."; + } + } + + std::string operandValue; + error = context->getWord(&operandValue, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + error = spvTextEncodeOperand(grammar, context, type, operandValue.c_str(), + pInst, &expectedOperands); + + if (error == SPV_FAILED_MATCH && spvOperandIsOptional(type)) + return SPV_SUCCESS; + + if (error) return error; + + context->setPosition(nextPosition); + } + } + + if (spvOpcodeGeneratesType(pInst->opcode)) { + if (context->recordTypeDefinition(pInst) != SPV_SUCCESS) { + return SPV_ERROR_INVALID_TEXT; + } + } else if (opcodeEntry->hasType) { + // SPIR-V dictates that if an instruction has both a return value and a + // type ID then the type id is first, and the return value is second. + assert(opcodeEntry->hasResult && + "Unknown opcode: has a type but no result."); + context->recordTypeIdForValue(pInst->words[2], pInst->words[1]); + } + + if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { + return context->diagnostic() + << opcodeName << " Instruction too long: " << pInst->words.size() + << " words, but the limit is " + << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; + } + + pInst->words[0] = + spvOpcodeMake(uint16_t(pInst->words.size()), opcodeEntry->opcode); + + return SPV_SUCCESS; +} + +enum { kAssemblerVersion = 0 }; + +// Populates a binary stream's |header|. The target environment is specified via +// |env| and Id bound is via |bound|. +spv_result_t SetHeader(spv_target_env env, const uint32_t bound, + uint32_t* header) { + if (!header) return SPV_ERROR_INVALID_BINARY; + + header[SPV_INDEX_MAGIC_NUMBER] = spv::MagicNumber; + header[SPV_INDEX_VERSION_NUMBER] = spvVersionForTargetEnv(env); + header[SPV_INDEX_GENERATOR_NUMBER] = + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, kAssemblerVersion); + header[SPV_INDEX_BOUND] = bound; + header[SPV_INDEX_SCHEMA] = 0; // NOTE: Reserved + + return SPV_SUCCESS; +} + +// Collects all numeric ids in the module source into |numeric_ids|. +// This function is essentially a dry-run of spvTextToBinary. +spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, + const spvtools::MessageConsumer& consumer, + const spv_text text, + std::set* numeric_ids) { + spvtools::AssemblyContext context(text, consumer); + + if (!text->str) return context.diagnostic() << "Missing assembly text."; + + if (!grammar.isValid()) { + return SPV_ERROR_INVALID_TABLE; + } + + // Skip past whitespace and comments. + context.advance(); + + while (context.hasText()) { + spv_instruction_t inst; + + // Operand parsing sometimes involves knowing the opcode of the instruction + // being parsed. A malformed input might feature such an operand *before* + // the opcode is known. To guard against accessing an uninitialized opcode, + // the instruction's opcode is initialized to a default value. + inst.opcode = spv::Op::Max; + + if (spvTextEncodeOpcode(grammar, &context, &inst)) { + return SPV_ERROR_INVALID_TEXT; + } + + if (context.advance()) break; + } + + *numeric_ids = context.GetNumericIds(); + return SPV_SUCCESS; +} + +// Translates a given assembly language module into binary form. +// If a diagnostic is generated, it is not yet marked as being +// for a text-based input. +spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar, + const spvtools::MessageConsumer& consumer, + const spv_text text, + const uint32_t options, + spv_binary* pBinary) { + // The ids in this set will have the same values both in source and binary. + // All other ids will be generated by filling in the gaps. + std::set ids_to_preserve; + + if (options & SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS) { + // Collect all numeric ids from the source into ids_to_preserve. + const spv_result_t result = + GetNumericIds(grammar, consumer, text, &ids_to_preserve); + if (result != SPV_SUCCESS) return result; + } + + spvtools::AssemblyContext context(text, consumer, std::move(ids_to_preserve)); + + if (!text->str) return context.diagnostic() << "Missing assembly text."; + + if (!grammar.isValid()) { + return SPV_ERROR_INVALID_TABLE; + } + if (!pBinary) return SPV_ERROR_INVALID_POINTER; + + std::vector instructions; + + // Skip past whitespace and comments. + context.advance(); + + while (context.hasText()) { + instructions.push_back({}); + spv_instruction_t& inst = instructions.back(); + + if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) { + return error; + } + + if (context.advance()) break; + } + + size_t totalSize = SPV_INDEX_INSTRUCTION; + for (auto& inst : instructions) { + totalSize += inst.words.size(); + } + + uint32_t* data = new uint32_t[totalSize]; + if (!data) return SPV_ERROR_OUT_OF_MEMORY; + uint64_t currentIndex = SPV_INDEX_INSTRUCTION; + for (auto& inst : instructions) { + memcpy(data + currentIndex, inst.words.data(), + sizeof(uint32_t) * inst.words.size()); + currentIndex += inst.words.size(); + } + + if (auto error = SetHeader(grammar.target_env(), context.getBound(), data)) + return error; + + spv_binary binary = new spv_binary_t(); + if (!binary) { + delete[] data; + return SPV_ERROR_OUT_OF_MEMORY; + } + binary->code = data; + binary->wordCount = totalSize; + + *pBinary = binary; + + return SPV_SUCCESS; +} + +} // anonymous namespace + +spv_result_t spvTextToBinary(const spv_const_context context, + const char* input_text, + const size_t input_text_size, spv_binary* pBinary, + spv_diagnostic* pDiagnostic) { + return spvTextToBinaryWithOptions(context, input_text, input_text_size, + SPV_TEXT_TO_BINARY_OPTION_NONE, pBinary, + pDiagnostic); +} + +spv_result_t spvTextToBinaryWithOptions(const spv_const_context context, + const char* input_text, + const size_t input_text_size, + const uint32_t options, + spv_binary* pBinary, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + spv_text_t text = {input_text, input_text_size}; + spvtools::AssemblyGrammar grammar(&hijack_context); + + spv_result_t result = spvTextToBinaryInternal( + grammar, hijack_context.consumer, &text, options, pBinary); + if (pDiagnostic && *pDiagnostic) (*pDiagnostic)->isTextSource = true; + + return result; +} + +void spvTextDestroy(spv_text text) { + if (text) { + if (text->str) delete[] text->str; + delete text; + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/text.h b/engine/engine/3rdlibs/spirv-tools/source/text.h new file mode 100644 index 00000000..fa34ee16 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/text.h @@ -0,0 +1,53 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TEXT_H_ +#define SOURCE_TEXT_H_ + +#include + +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.h" + +typedef enum spv_literal_type_t { + SPV_LITERAL_TYPE_INT_32, + SPV_LITERAL_TYPE_INT_64, + SPV_LITERAL_TYPE_UINT_32, + SPV_LITERAL_TYPE_UINT_64, + SPV_LITERAL_TYPE_FLOAT_32, + SPV_LITERAL_TYPE_FLOAT_64, + SPV_LITERAL_TYPE_STRING, + SPV_FORCE_32_BIT_ENUM(spv_literal_type_t) +} spv_literal_type_t; + +typedef struct spv_literal_t { + spv_literal_type_t type; + union value_t { + int32_t i32; + int64_t i64; + uint32_t u32; + uint64_t u64; + float f; + double d; + } value; + std::string str; // Special field for literal string. +} spv_literal_t; + +// Converts the given text string to a number/string literal and writes the +// result to *literal. String literals must be surrounded by double-quotes ("), +// which are then stripped. +spv_result_t spvTextToLiteral(const char* text, spv_literal_t* literal); + +#endif // SOURCE_TEXT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/text_handler.cpp b/engine/engine/3rdlibs/spirv-tools/source/text_handler.cpp new file mode 100644 index 00000000..ca8859ec --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/text_handler.cpp @@ -0,0 +1,398 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/text_handler.h" + +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/ext_inst.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/text.h" +#include "source/util/bitutils.h" +#include "source/util/hex_float.h" +#include "source/util/parse_number.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace { + +// Advances |text| to the start of the next line and writes the new position to +// |position|. +spv_result_t advanceLine(spv_text text, spv_position position) { + while (true) { + if (position->index >= text->length) return SPV_END_OF_STREAM; + switch (text->str[position->index]) { + case '\0': + return SPV_END_OF_STREAM; + case '\n': + position->column = 0; + position->line++; + position->index++; + return SPV_SUCCESS; + default: + position->column++; + position->index++; + break; + } + } +} + +// Advances |text| to first non white space character and writes the new +// position to |position|. +// If a null terminator is found during the text advance, SPV_END_OF_STREAM is +// returned, SPV_SUCCESS otherwise. No error checking is performed on the +// parameters, its the users responsibility to ensure these are non null. +spv_result_t advance(spv_text text, spv_position position) { + // NOTE: Consume white space, otherwise don't advance. + while (true) { + if (position->index >= text->length) return SPV_END_OF_STREAM; + switch (text->str[position->index]) { + case '\0': + return SPV_END_OF_STREAM; + case ';': + if (spv_result_t error = advanceLine(text, position)) return error; + continue; + case ' ': + case '\t': + case '\r': + position->column++; + position->index++; + continue; + case '\n': + position->column = 0; + position->line++; + position->index++; + continue; + default: + return SPV_SUCCESS; + } + } +} + +// Fetches the next word from the given text stream starting from the given +// *position. On success, writes the decoded word into *word and updates +// *position to the location past the returned word. +// +// A word ends at the next comment or whitespace. However, double-quoted +// strings remain intact, and a backslash always escapes the next character. +spv_result_t getWord(spv_text text, spv_position position, std::string* word) { + if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT; + if (!position) return SPV_ERROR_INVALID_POINTER; + + const size_t start_index = position->index; + + bool quoting = false; + bool escaping = false; + + // NOTE: Assumes first character is not white space! + while (true) { + if (position->index >= text->length) { + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; + } + const char ch = text->str[position->index]; + if (ch == '\\') { + escaping = !escaping; + } else { + switch (ch) { + case '"': + if (!escaping) quoting = !quoting; + break; + case ' ': + case ';': + case ',': + case '(': + case ')': + case '\t': + case '\n': + case '\r': + if (escaping || quoting) break; + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; + case '\0': { // NOTE: End of word found! + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; + } + default: + break; + } + escaping = false; + } + + position->column++; + position->index++; + } +} + +// Returns true if the characters in the text as position represent +// the start of an Opcode. +bool startsWithOp(spv_text text, spv_position position) { + if (text->length < position->index + 3) return false; + char ch0 = text->str[position->index]; + char ch1 = text->str[position->index + 1]; + char ch2 = text->str[position->index + 2]; + return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z')); +} + +} // namespace + +const IdType kUnknownType = {0, false, IdTypeClass::kBottom}; + +// TODO(dneto): Reorder AssemblyContext definitions to match declaration order. + +// This represents all of the data that is only valid for the duration of +// a single compilation. +uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char* textValue) { + if (!ids_to_preserve_.empty()) { + uint32_t id = 0; + if (spvtools::utils::ParseNumber(textValue, &id)) { + if (ids_to_preserve_.find(id) != ids_to_preserve_.end()) { + bound_ = std::max(bound_, id + 1); + return id; + } + } + } + + const auto it = named_ids_.find(textValue); + if (it == named_ids_.end()) { + uint32_t id = next_id_++; + if (!ids_to_preserve_.empty()) { + while (ids_to_preserve_.find(id) != ids_to_preserve_.end()) { + id = next_id_++; + } + } + + named_ids_.emplace(textValue, id); + bound_ = std::max(bound_, id + 1); + return id; + } + + return it->second; +} + +uint32_t AssemblyContext::getBound() const { return bound_; } + +spv_result_t AssemblyContext::advance() { + return spvtools::advance(text_, ¤t_position_); +} + +spv_result_t AssemblyContext::getWord(std::string* word, + spv_position next_position) { + *next_position = current_position_; + return spvtools::getWord(text_, next_position, word); +} + +bool AssemblyContext::startsWithOp() { + return spvtools::startsWithOp(text_, ¤t_position_); +} + +bool AssemblyContext::isStartOfNewInst() { + spv_position_t pos = current_position_; + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::startsWithOp(text_, &pos)) return true; + + std::string word; + pos = current_position_; + if (spvtools::getWord(text_, &pos, &word)) return false; + if ('%' != word.front()) return false; + + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::getWord(text_, &pos, &word)) return false; + if ("=" != word) return false; + + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::startsWithOp(text_, &pos)) return true; + return false; +} + +char AssemblyContext::peek() const { + return text_->str[current_position_.index]; +} + +bool AssemblyContext::hasText() const { + return text_->length > current_position_.index; +} + +void AssemblyContext::seekForward(uint32_t size) { + current_position_.index += size; + current_position_.column += size; +} + +spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value, + spv_instruction_t* pInst) { + pInst->words.insert(pInst->words.end(), value); + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::binaryEncodeNumericLiteral( + const char* val, spv_result_t error_code, const IdType& type, + spv_instruction_t* pInst) { + using spvtools::utils::EncodeNumberStatus; + // Populate the NumberType from the IdType for parsing. + spvtools::utils::NumberType number_type; + switch (type.type_class) { + case IdTypeClass::kOtherType: + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected numeric literal type"; + case IdTypeClass::kScalarIntegerType: + if (type.isSigned) { + number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; + case IdTypeClass::kScalarFloatType: + number_type = {type.bitwidth, SPV_NUMBER_FLOATING}; + break; + case IdTypeClass::kBottom: + // kBottom means the type is unknown and we need to infer the type before + // parsing the number. The rule is: If there is a decimal point, treat + // the value as a floating point value, otherwise a integer value, then + // if the first char of the integer text is '-', treat the integer as a + // signed integer, otherwise an unsigned integer. + uint32_t bitwidth = static_cast(assumedBitWidth(type)); + if (strchr(val, '.')) { + number_type = {bitwidth, SPV_NUMBER_FLOATING}; + } else if (type.isSigned || val[0] == '-') { + number_type = {bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; + } + + std::string error_msg; + EncodeNumberStatus parse_status = ParseAndEncodeNumber( + val, number_type, + [this, pInst](uint32_t d) { this->binaryEncodeU32(d, pInst); }, + &error_msg); + switch (parse_status) { + case EncodeNumberStatus::kSuccess: + return SPV_SUCCESS; + case EncodeNumberStatus::kInvalidText: + return diagnostic(error_code) << error_msg; + case EncodeNumberStatus::kUnsupported: + return diagnostic(SPV_ERROR_INTERNAL) << error_msg; + case EncodeNumberStatus::kInvalidUsage: + return diagnostic(SPV_ERROR_INVALID_TEXT) << error_msg; + } + // This line is not reachable, only added to satisfy the compiler. + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected result code from ParseAndEncodeNumber()"; +} + +spv_result_t AssemblyContext::binaryEncodeString(const char* value, + spv_instruction_t* pInst) { + const size_t length = strlen(value); + const size_t wordCount = (length / 4) + 1; + const size_t oldWordCount = pInst->words.size(); + const size_t newWordCount = oldWordCount + wordCount; + + // TODO(dneto): We can just defer this check until later. + if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { + return diagnostic() << "Instruction too long: more than " + << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; + } + + pInst->words.reserve(newWordCount); + spvtools::utils::AppendToVector(value, &pInst->words); + + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::recordTypeDefinition( + const spv_instruction_t* pInst) { + uint32_t value = pInst->words[1]; + if (types_.find(value) != types_.end()) { + return diagnostic() << "Value " << value + << " has already been used to generate a type"; + } + + if (pInst->opcode == spv::Op::OpTypeInt) { + if (pInst->words.size() != 4) + return diagnostic() << "Invalid OpTypeInt instruction"; + types_[value] = {pInst->words[2], pInst->words[3] != 0, + IdTypeClass::kScalarIntegerType}; + } else if (pInst->opcode == spv::Op::OpTypeFloat) { + if ((pInst->words.size() != 3) && (pInst->words.size() != 4)) + return diagnostic() << "Invalid OpTypeFloat instruction"; + // TODO(kpet) Do we need to record the FP Encoding here? + types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType}; + } else { + types_[value] = {0, false, IdTypeClass::kOtherType}; + } + return SPV_SUCCESS; +} + +IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const { + auto type = types_.find(value); + if (type == types_.end()) { + return kUnknownType; + } + return std::get<1>(*type); +} + +IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const { + auto type_value = value_types_.find(value); + if (type_value == value_types_.end()) { + return {0, false, IdTypeClass::kBottom}; + } + return getTypeOfTypeGeneratingValue(std::get<1>(*type_value)); +} + +spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value, + uint32_t type) { + bool successfully_inserted = false; + std::tie(std::ignore, successfully_inserted) = + value_types_.insert(std::make_pair(value, type)); + if (!successfully_inserted) + return diagnostic() << "Value is being defined a second time"; + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::recordIdAsExtInstImport( + uint32_t id, spv_ext_inst_type_t type) { + bool successfully_inserted = false; + std::tie(std::ignore, successfully_inserted) = + import_id_to_ext_inst_type_.insert(std::make_pair(id, type)); + if (!successfully_inserted) + return diagnostic() << "Import Id is being defined a second time"; + return SPV_SUCCESS; +} + +spv_ext_inst_type_t AssemblyContext::getExtInstTypeForId(uint32_t id) const { + auto type = import_id_to_ext_inst_type_.find(id); + if (type == import_id_to_ext_inst_type_.end()) { + return SPV_EXT_INST_TYPE_NONE; + } + return std::get<1>(*type); +} + +std::set AssemblyContext::GetNumericIds() const { + std::set ids; + for (const auto& kv : named_ids_) { + uint32_t id; + if (spvtools::utils::ParseNumber(kv.first.c_str(), &id)) ids.insert(id); + } + return ids; +} + +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/text_handler.h b/engine/engine/3rdlibs/spirv-tools/source/text_handler.h new file mode 100644 index 00000000..19972e95 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/text_handler.h @@ -0,0 +1,264 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TEXT_HANDLER_H_ +#define SOURCE_TEXT_HANDLER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/instruction.h" +#include "source/text.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Structures + +// This is a lattice for tracking types. +enum class IdTypeClass { + kBottom = 0, // We have no information yet. + kScalarIntegerType, + kScalarFloatType, + kOtherType +}; + +// Contains ID type information that needs to be tracked across all Ids. +// Bitwidth is only valid when type_class is kScalarIntegerType or +// kScalarFloatType. +struct IdType { + uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits. + bool isSigned; // This is only significant if type_class is integral. + IdTypeClass type_class; +}; + +// Default equality operator for IdType. Tests if all members are the same. +inline bool operator==(const IdType& first, const IdType& second) { + return (first.bitwidth == second.bitwidth) && + (first.isSigned == second.isSigned) && + (first.type_class == second.type_class); +} + +// Tests whether any member of the IdTypes do not match. +inline bool operator!=(const IdType& first, const IdType& second) { + return !(first == second); +} + +// A value representing an unknown type. +extern const IdType kUnknownType; + +// Returns true if the type is a scalar integer type. +inline bool isScalarIntegral(const IdType& type) { + return type.type_class == IdTypeClass::kScalarIntegerType; +} + +// Returns true if the type is a scalar floating point type. +inline bool isScalarFloating(const IdType& type) { + return type.type_class == IdTypeClass::kScalarFloatType; +} + +// Returns the number of bits in the type. +// This is only valid for bottom, scalar integer, and scalar floating +// classes. For bottom, assume 32 bits. +inline int assumedBitWidth(const IdType& type) { + switch (type.type_class) { + case IdTypeClass::kBottom: + return 32; + case IdTypeClass::kScalarIntegerType: + case IdTypeClass::kScalarFloatType: + return type.bitwidth; + default: + break; + } + // We don't care about this case. + return 0; +} + +// A templated class with a static member function Clamp, where Clamp +// sets a referenced value of type T to 0 if T is an unsigned +// integer type, and returns true if it modified the referenced +// value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer +// types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + +// Encapsulates the data used during the assembly of a SPIR-V module. +class AssemblyContext { + public: + AssemblyContext(spv_text text, const MessageConsumer& consumer, + std::set&& ids_to_preserve = std::set()) + : current_position_({}), + consumer_(consumer), + text_(text), + bound_(1), + next_id_(1), + ids_to_preserve_(std::move(ids_to_preserve)) {} + + // Assigns a new integer value to the given text ID, or returns the previously + // assigned integer value if the ID has been seen before. + uint32_t spvNamedIdAssignOrGet(const char* textValue); + + // Returns the largest largest numeric ID that has been assigned. + uint32_t getBound() const; + + // Advances position to point to the next word in the input stream. + // Returns SPV_SUCCESS on success. + spv_result_t advance(); + + // Sets word to the next word in the input text. Fills next_position with + // the next location past the end of the word. + spv_result_t getWord(std::string* word, spv_position next_position); + + // Returns true if the next word in the input is the start of a new Opcode. + bool startsWithOp(); + + // Returns true if the next word in the input is the start of a new + // instruction. + bool isStartOfNewInst(); + + // Returns a diagnostic object initialized with current position in the input + // stream, and for the given error code. Any data written to this object will + // show up in pDiagnsotic on destruction. + DiagnosticStream diagnostic(spv_result_t error) { + return DiagnosticStream(current_position_, consumer_, "", error); + } + + // Returns a diagnostic object with the default assembly error code. + DiagnosticStream diagnostic() { + // The default failure for assembly is invalid text. + return diagnostic(SPV_ERROR_INVALID_TEXT); + } + + // Returns then next character in the input stream. + char peek() const; + + // Returns true if there is more text in the input stream. + bool hasText() const; + + // Seeks the input stream forward by 'size' characters. + void seekForward(uint32_t size); + + // Sets the current position in the input stream to the given position. + void setPosition(const spv_position_t& newPosition) { + current_position_ = newPosition; + } + + // Returns the current position in the input stream. + const spv_position_t& position() const { return current_position_; } + + // Appends the given 32-bit value to the given instruction. + // Returns SPV_SUCCESS if the value could be correctly inserted in the + // instruction. + spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst); + + // Appends the given string to the given instruction. + // Returns SPV_SUCCESS if the value could be correctly inserted in the + // instruction. + spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst); + + // Appends the given numeric literal to the given instruction. + // Validates and respects the bitwidth supplied in the IdType argument. + // If the type is of class kBottom the value will be encoded as a + // 32-bit integer. + // Returns SPV_SUCCESS if the value could be correctly added to the + // instruction. Returns the given error code on failure, and emits + // a diagnostic if that error code is not SPV_FAILED_MATCH. + spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal, + spv_result_t error_code, + const IdType& type, + spv_instruction_t* pInst); + + // Returns the IdType associated with this type-generating value. + // If the type has not been previously recorded with recordTypeDefinition, + // kUnknownType will be returned. + IdType getTypeOfTypeGeneratingValue(uint32_t value) const; + + // Returns the IdType that represents the return value of this Value + // generating instruction. + // If the value has not been recorded with recordTypeIdForValue, or the type + // could not be determined kUnknownType will be returned. + IdType getTypeOfValueInstruction(uint32_t value) const; + + // Tracks the type-defining instruction. The result of the tracking can + // later be queried using getValueType. + // pInst is expected to be completely filled in by the time this instruction + // is called. + // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error. + spv_result_t recordTypeDefinition(const spv_instruction_t* pInst); + + // Tracks the relationship between the value and its type. + spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type); + + // Records the given Id as being the import of the given extended instruction + // type. + spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type); + + // Returns the extended instruction type corresponding to the import with + // the given Id, if it exists. Returns SPV_EXT_INST_TYPE_NONE if the + // id is not the id for an extended instruction type. + spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const; + + // Returns a set consisting of each ID generated by spvNamedIdAssignOrGet from + // a numeric ID text representation. For example, generated from "%12" but not + // from "%foo". + std::set GetNumericIds() const; + + private: + // Maps ID names to their corresponding numerical ids. + using spv_named_id_table = std::unordered_map; + // Maps type-defining IDs to their IdType. + using spv_id_to_type_map = std::unordered_map; + // Maps Ids to the id of their type. + using spv_id_to_type_id = std::unordered_map; + + spv_named_id_table named_ids_; + spv_id_to_type_map types_; + spv_id_to_type_id value_types_; + // Maps an extended instruction import Id to the extended instruction type. + std::unordered_map import_id_to_ext_inst_type_; + spv_position_t current_position_; + MessageConsumer consumer_; + spv_text text_; + uint32_t bound_; + uint32_t next_id_; + std::set ids_to_preserve_; +}; + +} // namespace spvtools + +#endif // SOURCE_TEXT_HANDLER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/to_string.cpp b/engine/engine/3rdlibs/spirv-tools/source/to_string.cpp new file mode 100644 index 00000000..b707070b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/to_string.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/to_string.h" + +#include + +namespace spvtools { + +std::string to_string(uint32_t n) { + // This implementation avoids using standard library features that access + // the locale. Using the locale requires taking a mutex which causes + // annoying serialization. + + constexpr int max_digits = 10; // max uint has 10 digits + // Contains the resulting digits, with least significant digit in the last + // entry. + char buf[max_digits]; + int write_index = max_digits - 1; + if (n == 0) { + buf[write_index] = '0'; + } else { + while (n > 0) { + int units = n % 10; + buf[write_index--] = "0123456789"[units]; + n = (n - units) / 10; + } + write_index++; + } + assert(write_index >= 0); + return std::string(buf + write_index, max_digits - write_index); +} +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/to_string.h b/engine/engine/3rdlibs/spirv-tools/source/to_string.h new file mode 100644 index 00000000..83702f92 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/to_string.h @@ -0,0 +1,29 @@ +// Copyright (c) 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TO_STRING_H_ +#define SOURCE_TO_STRING_H_ + +#include +#include + +namespace spvtools { + +// Returns the decimal representation of a number as a string, +// without using the locale. +std::string to_string(uint32_t n); + +} // namespace spvtools + +#endif // SOURCE_TO_STRING_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.cpp b/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.cpp new file mode 100644 index 00000000..47e275bf --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/util/bit_vector.h" + +#include +#include + +namespace spvtools { +namespace utils { + +void BitVector::ReportDensity(std::ostream& out) { + uint32_t count = 0; + + for (BitContainer e : bits_) { + while (e != 0) { + if ((e & 1) != 0) { + ++count; + } + e = e >> 1; + } + } + + out << "count=" << count + << ", total size (bytes)=" << bits_.size() * sizeof(BitContainer) + << ", bytes per element=" + << (double)(bits_.size() * sizeof(BitContainer)) / (double)(count); +} + +bool BitVector::Or(const BitVector& other) { + auto this_it = this->bits_.begin(); + auto other_it = other.bits_.begin(); + bool modified = false; + + while (this_it != this->bits_.end() && other_it != other.bits_.end()) { + auto temp = *this_it | *other_it; + if (temp != *this_it) { + modified = true; + *this_it = temp; + } + ++this_it; + ++other_it; + } + + if (other_it != other.bits_.end()) { + modified = true; + this->bits_.insert(this->bits_.end(), other_it, other.bits_.end()); + } + + return modified; +} + +std::ostream& operator<<(std::ostream& out, const BitVector& bv) { + out << "{"; + for (uint32_t i = 0; i < bv.bits_.size(); ++i) { + BitVector::BitContainer b = bv.bits_[i]; + uint32_t j = 0; + while (b != 0) { + if (b & 1) { + out << ' ' << i * BitVector::kBitContainerSize + j; + } + ++j; + b = b >> 1; + } + } + out << "}"; + return out; +} + +} // namespace utils +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.h b/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.h new file mode 100644 index 00000000..826d62f0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/bit_vector.h @@ -0,0 +1,119 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_BIT_VECTOR_H_ +#define SOURCE_UTIL_BIT_VECTOR_H_ + +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Implements a bit vector class. +// +// All bits default to zero, and the upper bound is 2^32-1. +class BitVector { + private: + using BitContainer = uint64_t; + enum { kBitContainerSize = 64 }; + enum { kInitialNumBits = 1024 }; + + public: + // Creates a bit vector containing 0s. + BitVector(uint32_t reserved_size = kInitialNumBits) + : bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {} + + // Sets the |i|th bit to 1. Returns the |i|th bit before it was set. + bool Set(uint32_t i) { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + bits_.resize(element_index + 1, 0); + } + + BitContainer original = bits_[element_index]; + BitContainer ith_bit = static_cast(1) << bit_in_element; + + if ((original & ith_bit) != 0) { + return true; + } else { + bits_[element_index] = original | ith_bit; + return false; + } + } + + // Sets the |i|th bit to 0. Return the |i|th bit before it was cleared. + bool Clear(uint32_t i) { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + return false; + } + + BitContainer original = bits_[element_index]; + BitContainer ith_bit = static_cast(1) << bit_in_element; + + if ((original & ith_bit) == 0) { + return false; + } else { + bits_[element_index] = original & (~ith_bit); + return true; + } + } + + // Returns the |i|th bit. + bool Get(uint32_t i) const { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + return false; + } + + return (bits_[element_index] & + (static_cast(1) << bit_in_element)) != 0; + } + + // Returns true if every bit is 0. + bool Empty() const { + for (BitContainer b : bits_) { + if (b != 0) { + return false; + } + } + return true; + } + + // Print a report on the densicy of the bit vector, number of 1 bits, number + // of bytes, and average bytes for 1 bit, to |out|. + void ReportDensity(std::ostream& out); + + friend std::ostream& operator<<(std::ostream&, const BitVector&); + + // Performs a bitwise-or operation on |this| and |that|, storing the result in + // |this|. Return true if |this| changed. + bool Or(const BitVector& that); + + private: + std::vector bits_; +}; + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_BIT_VECTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/bitutils.h b/engine/engine/3rdlibs/spirv-tools/source/util/bitutils.h new file mode 100644 index 00000000..2763bc27 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/bitutils.h @@ -0,0 +1,212 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_BITUTILS_H_ +#define SOURCE_UTIL_BITUTILS_H_ + +#include +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// Calculates the bit width of the integer type |T|. +template +struct IntegerBitWidth { + static_assert(std::is_integral::value, "Integer type required"); + static const size_t kBitsPerByte = 8; + static const size_t get = sizeof(T) * kBitsPerByte; +}; + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < IntegerBitWidth::get, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(IntegerBitWidth::get == 32, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 32, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 64, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 8, "IntegerBitWidth mismatch"); + +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +// Returns number of '1' bits in a word. +template +size_t CountSetBits(T word) { + static_assert(std::is_integral::value, + "CountSetBits requires integer type"); + uint32_t count = 0; + while (word) { + word &= word - 1; + ++count; + } + return count; +} + +// Checks if the bit at the |position| is set to '1'. +// Bits zero-indexed starting at the least significant bit. +// |position| must be within the bit width of |T|. +template +bool IsBitAtPositionSet(T word, size_t position) { + static_assert(std::is_integral::value, "Integer type required"); + static_assert(std::is_unsigned::value, "Unsigned type required"); + assert(position < IntegerBitWidth::get && + "position must be less than the bit width"); + return word & T(T(1) << position); +} + +// Returns a value obtained by setting a range of adjacent bits of |word| to +// |value|. Affected bits are within the range: +// [first_position, first_position + num_bits_to_mutate), +// assuming zero-based indexing starting at the least +// significant bit. Bits to mutate must be within the bit width of |T|. +template +T MutateBits(T word, size_t first_position, size_t num_bits_to_mutate, + bool value) { + static_assert(std::is_integral::value, "Integer type required"); + static_assert(std::is_unsigned::value, "Unsigned type required"); + static const size_t word_bit_width = IntegerBitWidth::get; + assert(first_position < word_bit_width && + "Mutated bits must be within bit width"); + assert(first_position + num_bits_to_mutate <= word_bit_width && + "Mutated bits must be within bit width"); + if (num_bits_to_mutate == 0) { + return word; + } + + const T all_ones = ~T(0); + const size_t num_unaffected_low_bits = first_position; + const T unaffected_low_mask = + T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits); + + const size_t num_unaffected_high_bits = + word_bit_width - (first_position + num_bits_to_mutate); + const T unaffected_high_mask = + T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits); + + const T mutation_mask = unaffected_low_mask & unaffected_high_mask; + if (value) { + return word | mutation_mask; + } + return word & T(~mutation_mask); +} + +// Returns a value obtained by setting the |num_bits_to_set| highest bits to +// '1'. |num_bits_to_set| must be not be greater than the bit width of |T|. +template +T SetHighBits(T word, size_t num_bits_to_set) { + if (num_bits_to_set == 0) { + return word; + } + const size_t word_bit_width = IntegerBitWidth::get; + assert(num_bits_to_set <= word_bit_width && + "Can't set more bits than bit width"); + return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set, + true); +} + +// Returns a value obtained by setting the |num_bits_to_set| highest bits to +// '0'. |num_bits_to_set| must be not be greater than the bit width of |T|. +template +T ClearHighBits(T word, size_t num_bits_to_set) { + if (num_bits_to_set == 0) { + return word; + } + const size_t word_bit_width = IntegerBitWidth::get; + assert(num_bits_to_set <= word_bit_width && + "Can't clear more bits than bit width"); + return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set, + false); +} + +// Returns the value obtained by extracting the |number_of_bits| least +// significant bits from |value|, and sign-extending it to 64-bits. +template +T SignExtendValue(T value, uint32_t number_of_bits) { + const uint32_t bit_width = sizeof(value) * 8; + if (number_of_bits == bit_width) return value; + + bool is_negative = utils::IsBitAtPositionSet(value, number_of_bits - 1); + if (is_negative) { + value = utils::SetHighBits(value, bit_width - number_of_bits); + } else { + value = utils::ClearHighBits(value, bit_width - number_of_bits); + } + return value; +} + +// Returns the value obtained by extracting the |number_of_bits| least +// significant bits from |value|, and zero-extending it to 64-bits. +template +T ZeroExtendValue(T value, uint32_t number_of_bits) { + const uint32_t bit_width = sizeof(value) * 8; + if (number_of_bits == bit_width) return value; + return utils::ClearHighBits(value, bit_width - number_of_bits); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_BITUTILS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/hash_combine.h b/engine/engine/3rdlibs/spirv-tools/source/util/hash_combine.h new file mode 100644 index 00000000..1a2dbc33 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/hash_combine.h @@ -0,0 +1,53 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_HASH_COMBINE_H_ +#define SOURCE_UTIL_HASH_COMBINE_H_ + +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Helpers for incrementally computing hashes. +// For reference, see +// http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf + +template +inline size_t hash_combine(std::size_t seed, const T& val) { + return seed ^ (std::hash()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + +template +inline size_t hash_combine(std::size_t hash, const std::vector& vals) { + for (const T& val : vals) { + hash = hash_combine(hash, val); + } + return hash; +} + +inline size_t hash_combine(std::size_t hash) { return hash; } + +template +inline size_t hash_combine(std::size_t hash, const T& val, + const Types&... args) { + return hash_combine(hash_combine(hash, val), args...); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_HASH_COMBINE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/hex_float.h b/engine/engine/3rdlibs/spirv-tools/source/util/hex_float.h new file mode 100644 index 00000000..98353a4a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/hex_float.h @@ -0,0 +1,1259 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_HEX_FLOAT_H_ +#define SOURCE_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/util/bitutils.h" + +#ifndef __GNUC__ +#define GCC_VERSION 0 +#else +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +namespace spvtools { +namespace utils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() = default; + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + using uint_type = void; +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint32_t; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } + // Returns the value as the native floating point format. + static float getAsFloat(const uint_type& t) { return BitwiseCast(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const float& t) { + return BitwiseCast(t); + } + // Returns the bitwidth. + static uint32_t width() { return 32u; } +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint64_t; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } + // Returns the value as the native floating point format. + static double getAsFloat(const uint_type& t) { + return BitwiseCast(t); + } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const double& t) { + return BitwiseCast(t); + } + // Returns the bitwidth. + static uint32_t width() { return 64u; } +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint16_t; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } + // Returns the value as the native floating point format. + static Float16 getAsFloat(const uint_type& t) { return Float16(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const Float16& t) { return t.get_value(); } + // Returns the bitwidth. + static uint32_t width() { return 16u; } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + using uint_type = typename FloatProxyTraits::uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() = default; + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = FloatProxyTraits::getBitsFromFloat(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return FloatProxyTraits::getAsFloat(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns a vector of words suitable for use in an Operand. + std::vector GetWords() const { + std::vector words; + if (FloatProxyTraits::width() == 64) { + FloatProxyTraits::uint_type d = data(); + words.push_back(static_cast(d)); + words.push_back(static_cast(d >> 32)); + } else { + words.push_back(static_cast(data())); + } + return words; + } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val = static_cast(0.0); + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store the bit representation of this hex-float. + using uint_type = void; + // Signed integer type that can store the bit representation of this + // hex-float. + using int_type = void; + // The numerical type that this HexFloat represents. + using underlying_type = void; + // The type needed to construct the underlying type. + using native_type = void; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint32_t; + using int_type = int32_t; + using underlying_type = FloatProxy; + using native_type = float; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint64_t; + using int_type = int64_t; + using underlying_type = FloatProxy; + using native_type = double; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint16_t; + using int_type = int16_t; + using underlying_type = uint16_t; + using native_type = uint16_t; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum class round_direction { + kToZero, + kToNearestEven, + kToPositiveInfinity, + kToNegativeInfinity, + max = kToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + using uint_type = typename Traits::uint_type; + using int_type = typename Traits::int_type; + using underlying_type = typename Traits::underlying_type; + using native_type = typename Traits::native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return value_.data(); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(value_.data() & ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = T(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + +#if GCC_VERSION == 40801 + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + template + struct negatable_left_shift { + static uint_type val(uint_type val) { + if (N > 0) { + return static_cast(val << N); + } else { + return static_cast(val >> N); + } + } + }; + + template + struct negatable_right_shift { + static uint_type val(uint_type val) { + if (N > 0) { + return static_cast(val >> N); + } else { + return static_cast(val << N); + } + } + }; + +#else + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + template + struct negatable_left_shift { + static uint_type val(uint_type val) { + return static_cast(val >> -N); + } + }; + + template + struct negatable_left_shift= 0>::type> { + static uint_type val(uint_type val) { + return static_cast(val << N); + } + }; + + template + struct negatable_right_shift { + static uint_type val(uint_type val) { + return static_cast(val << -N); + } + }; + + template + struct negatable_right_shift= 0>::type> { + static uint_type val(uint_type val) { + return static_cast(val >> N); + } + }; +#endif + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + using other_uint_type = typename other_T::uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift::val(1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift::val(1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift::val(significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case round_direction::kToZero: + break; + case round_direction::kToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case round_direction::kToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case round_direction::kToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift::val(incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift::val(significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(typename other_T::underlying_type( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift< + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits)>::val(significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(typename other_T::underlying_type( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == round_direction::kToNegativeInfinity + : round_dir == round_direction::kToPositiveInfinity; + using other_int_type = typename other_T::int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = value.value().data(); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type{0}); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type{0}); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() | negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, round_direction::kToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +namespace detail { + +// Returns a new value formed from 'value' by setting 'bit' that is the +// 'n'th most significant bit (where 0 is the most significant bit). +// If 'bit' is zero or 'n' is more than the number of bits in the integer +// type, then return the original value. +template +UINT_TYPE set_nth_most_significant_bit(UINT_TYPE value, UINT_TYPE bit, + UINT_TYPE n) { + constexpr UINT_TYPE max_position = std::numeric_limits::digits - 1; + if ((bit != 0) && (n <= max_position)) { + return static_cast(value | (bit << (max_position - n))); + } + return value; +} + +// Attempts to increment the argument. +// If it does not overflow, then increments the argument and returns true. +// If it would overflow, returns false. +template +bool saturated_inc(INT_TYPE& value) { + if (value == std::numeric_limits::max()) { + return false; + } + value++; + return true; +} + +// Attempts to decrement the argument. +// If it does not underflow, then decrements the argument and returns true. +// If it would overflow, returns false. +template +bool saturated_dec(INT_TYPE& value) { + if (value == std::numeric_limits::min()) { + return false; + } + value--; + return true; +} +} // namespace detail + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + + // The mantissa bits, without the most significant 1 bit, and with the + // the most recently read bits in the least significant positions. + uint_type fraction = 0; + // The number of mantissa bits that have been read, including the leading 1 + // bit that is not written into 'fraction'. + uint_type fraction_index = 0; + + // TODO(dneto): handle overflow and underflow + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + // Does the mantissa, as written, have non-zero digits to the left of + // the decimal point. Assume no until proven otherwise. + bool has_integer_part = false; + bool bits_written = false; // Stays false until we write a bit. + + // Scan the mantissa hex digits until we see a '.' or the 'p' that + // starts the exponent. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We have stripped all leading zeroes and we have not yet seen a ".". + has_integer_part = true; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = detail::set_nth_most_significant_bit(fraction, write_bit, + fraction_index); + // Increment the fraction index. If the input has bizarrely many + // significant digits, then silently drop them. + detail::saturated_inc(fraction_index); + if (!detail::saturated_inc(exponent)) { + // Overflow failure + is.setstate(std::ios::failbit); + return is; + } + } + // Since this updated after setting fraction bits, this effectively + // drops the leading 1 bit. + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + // Finished reading the part preceding any '.' or 'p'. + + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if ((!has_integer_part) && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + if (!detail::saturated_dec(exponent)) { + // Overflow failure + is.setstate(std::ios::failbit); + return is; + } + } else { + fraction = detail::set_nth_most_significant_bit(fraction, write_bit, + fraction_index); + // Increment the fraction index. If the input has bizarrely many + // significant digits, then silently drop them. + detail::saturated_inc(fraction_index); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + // Finished reading the part preceding 'p'. + // In hex floats syntax, the binary exponent is required. + + bool seen_exponent_sign = false; + int8_t exponent_sign = 1; + bool seen_written_exponent_digits = false; + // The magnitude of the exponent, as written, or the sentinel value to signal + // overflow. + int_type written_exponent = 0; + // A sentinel value signalling overflow of the magnitude of the written + // exponent. We'll assume that -written_exponent_overflow is valid for the + // type. Later we may add 1 or subtract 1 from the adjusted exponent, so leave + // room for an extra 1. + const int_type written_exponent_overflow = + std::numeric_limits::max() - 1; + while (true) { + if (!seen_written_exponent_digits && + (next_char == '-' || next_char == '+')) { + if (seen_exponent_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_exponent_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + seen_written_exponent_digits = true; + // Hex-floats express their exponent as decimal. + int_type digit = + static_cast(static_cast(next_char) - '0'); + if (written_exponent >= (written_exponent_overflow - digit) / 10) { + // The exponent is very big. Saturate rather than overflow the exponent. + // signed integer, which would be undefined behaviour. + written_exponent = written_exponent_overflow; + } else { + written_exponent = static_cast( + static_cast(written_exponent * 10) + digit); + } + } else { + break; + } + is.get(); + next_char = is.peek(); + } + if (!seen_written_exponent_digits) { + // Binary exponent had no digits. + is.setstate(std::ios::failbit); + return is; + } + + written_exponent = static_cast(written_exponent * exponent_sign); + // Now fold in the exponent bias into the written exponent, updating exponent. + // But avoid undefined behaviour that would result from overflowing int_type. + if (written_exponent >= 0 && exponent >= 0) { + // Saturate up to written_exponent_overflow. + if (written_exponent_overflow - exponent > written_exponent) { + exponent = static_cast(written_exponent + exponent); + } else { + exponent = written_exponent_overflow; + } + } else if (written_exponent < 0 && exponent < 0) { + // Saturate down to -written_exponent_overflow. + if (written_exponent_overflow + exponent > -written_exponent) { + exponent = static_cast(written_exponent + exponent); + } else { + exponent = static_cast(-written_exponent_overflow); + } + } else { + // They're of opposing sign, so it's safe to add. + exponent = static_cast(written_exponent + exponent); + } + + bool is_zero = (!has_integer_part) && (fraction == 0); + if ((!has_integer_part) && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::max_digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_HEX_FLOAT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/ilist.h b/engine/engine/3rdlibs/spirv-tools/source/util/ilist.h new file mode 100644 index 00000000..42d5e62b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/ilist.h @@ -0,0 +1,366 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_ILIST_H_ +#define SOURCE_UTIL_ILIST_H_ + +#include +#include +#include +#include + +#include "source/util/ilist_node.h" + +namespace spvtools { +namespace utils { + +// An IntrusiveList is a generic implementation of a doubly-linked list. The +// intended convention for using this container is: +// +// class Node : public IntrusiveNodeBase { +// // Note that "Node", the class being defined is the template. +// // Must have a default constructor accessible to List. +// // Add whatever data is needed in the node +// }; +// +// using List = IntrusiveList; +// +// You can also inherit from IntrusiveList instead of a typedef if you want to +// add more functionality. +// +// The condition on the template for IntrusiveNodeBase is there to add some type +// checking to the container. The compiler will still allow inserting elements +// of type IntrusiveNodeBase, but that would be an error. This assumption +// allows NextNode and PreviousNode to return pointers to Node, and casting will +// not be required by the user. + +template +class IntrusiveList { + public: + static_assert( + std::is_base_of, NodeType>::value, + "The type from the node must be derived from IntrusiveNodeBase, with " + "itself in the template."); + + // Creates an empty list. + inline IntrusiveList(); + + // Moves the contents of the given list to the list being constructed. + IntrusiveList(IntrusiveList&&); + + // Destroys the list. Note that the elements of the list will not be deleted, + // but they will be removed from the list. + virtual ~IntrusiveList(); + + // Moves all of the elements in the list on the RHS to the list on the LHS. + IntrusiveList& operator=(IntrusiveList&&); + + // Basetype for iterators so an IntrusiveList can be traversed like STL + // containers. + template + class iterator_template { + public: + iterator_template(const iterator_template& i) : node_(i.node_) {} + + iterator_template& operator++() { + node_ = node_->next_node_; + return *this; + } + + iterator_template& operator--() { + node_ = node_->previous_node_; + return *this; + } + + iterator_template& operator=(const iterator_template& i) { + node_ = i.node_; + return *this; + } + + T& operator*() const { return *node_; } + T* operator->() const { return node_; } + + friend inline bool operator==(const iterator_template& lhs, + const iterator_template& rhs) { + return lhs.node_ == rhs.node_; + } + friend inline bool operator!=(const iterator_template& lhs, + const iterator_template& rhs) { + return !(lhs == rhs); + } + + // Moves the nodes in |list| to the list that |this| points to. The + // positions of the nodes will be immediately before the element pointed to + // by the iterator. The return value will be an iterator pointing to the + // first of the newly inserted elements. + iterator_template MoveBefore(IntrusiveList* list) { + if (list->empty()) return *this; + + NodeType* first_node = list->sentinel_.next_node_; + NodeType* last_node = list->sentinel_.previous_node_; + + this->node_->previous_node_->next_node_ = first_node; + first_node->previous_node_ = this->node_->previous_node_; + + last_node->next_node_ = this->node_; + this->node_->previous_node_ = last_node; + + list->sentinel_.next_node_ = &list->sentinel_; + list->sentinel_.previous_node_ = &list->sentinel_; + + return iterator(first_node); + } + + // Define standard iterator types needs so this class can be + // used with . + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = size_t; + + protected: + iterator_template() = delete; + inline iterator_template(T* node) { node_ = node; } + T* node_; + + friend IntrusiveList; + }; + + using iterator = iterator_template; + using const_iterator = iterator_template; + + // Various types of iterators for the start (begin) and one past the end (end) + // of the list. + // + // Decrementing |end()| iterator will give and iterator pointing to the last + // element in the list, if one exists. + // + // Incrementing |end()| iterator will give |begin()|. + // + // Decrementing |begin()| will give |end()|. + // + // TODO: Not marking these functions as noexcept because Visual Studio 2013 + // does not support it. When we no longer care about that compiler, we should + // mark these as noexcept. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + // Appends |node| to the end of the list. If |node| is already in a list, it + // will be removed from that list first. + void push_back(NodeType* node); + + // Returns true if the list is empty. + bool empty() const; + + // Makes the current list empty. + inline void clear(); + + // Returns references to the first or last element in the list. It is an + // error to call these functions on an empty list. + NodeType& front(); + NodeType& back(); + const NodeType& front() const; + const NodeType& back() const; + + // Transfers [|first|, |last|) from |other| into the list at |where|. + // + // If |other| is |this|, no change is made. + void Splice(iterator where, IntrusiveList* other, iterator first, + iterator last); + + protected: + // Doing a deep copy of the list does not make sense if the list does not own + // the data. It is not clear who will own the newly created data. Making + // copies illegal for that reason. + IntrusiveList(const IntrusiveList&) = delete; + IntrusiveList& operator=(const IntrusiveList&) = delete; + + // This function will assert if it finds the list containing |node| is not in + // a valid state. + static void Check(NodeType* node); + + // A special node used to represent both the start and end of the list, + // without being part of the list. + NodeType sentinel_; +}; + +// Implementation of IntrusiveList + +template +inline IntrusiveList::IntrusiveList() : sentinel_() { + sentinel_.next_node_ = &sentinel_; + sentinel_.previous_node_ = &sentinel_; + sentinel_.is_sentinel_ = true; +} + +template +IntrusiveList::IntrusiveList(IntrusiveList&& list) : sentinel_() { + sentinel_.next_node_ = &sentinel_; + sentinel_.previous_node_ = &sentinel_; + sentinel_.is_sentinel_ = true; + list.sentinel_.ReplaceWith(&sentinel_); +} + +template +IntrusiveList::~IntrusiveList() { + clear(); +} + +template +IntrusiveList& IntrusiveList::operator=( + IntrusiveList&& list) { + list.sentinel_.ReplaceWith(&sentinel_); + return *this; +} + +template +inline typename IntrusiveList::iterator +IntrusiveList::begin() { + return iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::iterator +IntrusiveList::end() { + return iterator(&sentinel_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::begin() const { + return const_iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::end() const { + return const_iterator(&sentinel_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::cbegin() const { + return const_iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::cend() const { + return const_iterator(&sentinel_); +} + +template +void IntrusiveList::push_back(NodeType* node) { + node->InsertBefore(&sentinel_); +} + +template +bool IntrusiveList::empty() const { + return sentinel_.NextNode() == nullptr; +} + +template +void IntrusiveList::clear() { + while (!empty()) { + front().RemoveFromList(); + } +} + +template +NodeType& IntrusiveList::front() { + NodeType* node = sentinel_.NextNode(); + assert(node != nullptr && "Can't get the front of an empty list."); + return *node; +} + +template +NodeType& IntrusiveList::back() { + NodeType* node = sentinel_.PreviousNode(); + assert(node != nullptr && "Can't get the back of an empty list."); + return *node; +} + +template +const NodeType& IntrusiveList::front() const { + NodeType* node = sentinel_.NextNode(); + assert(node != nullptr && "Can't get the front of an empty list."); + return *node; +} + +template +const NodeType& IntrusiveList::back() const { + NodeType* node = sentinel_.PreviousNode(); + assert(node != nullptr && "Can't get the back of an empty list."); + return *node; +} + +template +void IntrusiveList::Splice(iterator where, + IntrusiveList* other, + iterator first, iterator last) { + if (first == last) return; + if (other == this) return; + + NodeType* first_prev = first.node_->previous_node_; + NodeType* where_next = where.node_->next_node_; + + // Attach first. + where.node_->next_node_ = first.node_; + first.node_->previous_node_ = where.node_; + + // Attach last. + where_next->previous_node_ = last.node_->previous_node_; + last.node_->previous_node_->next_node_ = where_next; + + // Fixup other. + first_prev->next_node_ = last.node_; + last.node_->previous_node_ = first_prev; +} + +template +void IntrusiveList::Check(NodeType* start) { + int sentinel_count = 0; + NodeType* p = start; + do { + assert(p != nullptr); + assert(p->next_node_->previous_node_ == p); + assert(p->previous_node_->next_node_ == p); + if (p->is_sentinel_) sentinel_count++; + p = p->next_node_; + } while (p != start); + assert(sentinel_count == 1 && "List should have exactly 1 sentinel node."); + (void)sentinel_count; + + p = start; + do { + assert(p != nullptr); + assert(p->previous_node_->next_node_ == p); + assert(p->next_node_->previous_node_ == p); + if (p->is_sentinel_) sentinel_count++; + p = p->previous_node_; + } while (p != start); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_ILIST_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/ilist_node.h b/engine/engine/3rdlibs/spirv-tools/source/util/ilist_node.h new file mode 100644 index 00000000..0579534b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/ilist_node.h @@ -0,0 +1,265 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_ILIST_NODE_H_ +#define SOURCE_UTIL_ILIST_NODE_H_ + +#include + +namespace spvtools { +namespace utils { + +template +class IntrusiveList; + +// IntrusiveNodeBase is the base class for nodes in an IntrusiveList. +// See the comments in ilist.h on how to use the class. + +template +class IntrusiveNodeBase { + public: + // Creates a new node that is not in a list. + inline IntrusiveNodeBase(); + inline IntrusiveNodeBase(const IntrusiveNodeBase&); + inline IntrusiveNodeBase& operator=(const IntrusiveNodeBase&); + inline IntrusiveNodeBase(IntrusiveNodeBase&& that); + + // Destroys a node. It is an error to destroy a node that is part of a + // list, unless it is a sentinel. + virtual ~IntrusiveNodeBase(); + + IntrusiveNodeBase& operator=(IntrusiveNodeBase&& that); + + // Returns true if |this| is in a list. + inline bool IsInAList() const; + + // Returns the node that comes after the given node in the list, if one + // exists. If the given node is not in a list or is at the end of the list, + // the return value is nullptr. + inline NodeType* NextNode() const; + + // Returns the node that comes before the given node in the list, if one + // exists. If the given node is not in a list or is at the start of the + // list, the return value is nullptr. + inline NodeType* PreviousNode() const; + + // Inserts the given node immediately before |pos| in the list. + // If the given node is already in a list, it will first be removed + // from that list. + // + // It is assumed that the given node is of type NodeType. It is an error if + // |pos| is not already in a list. + inline void InsertBefore(NodeType* pos); + + // Inserts the given node immediately after |pos| in the list. + // If the given node is already in a list, it will first be removed + // from that list. + // + // It is assumed that the given node is of type NodeType. It is an error if + // |pos| is not already in a list, or if |pos| is equal to |this|. + inline void InsertAfter(NodeType* pos); + + // Removes the given node from the list. It is assumed that the node is + // in a list. Note that this does not free any storage related to the node, + // it becomes the caller's responsibility to free the storage. + inline void RemoveFromList(); + + protected: + // Replaces |this| with |target|. |this| is a sentinel if and only if + // |target| is also a sentinel. + // + // If neither node is a sentinel, |target| takes + // the place of |this|. It is assumed that |target| is not in a list. + // + // If both are sentinels, then it will cause all of the + // nodes in the list containing |this| to be moved to the list containing + // |target|. In this case, it is assumed that |target| is an empty list. + // + // No storage will be deleted. + void ReplaceWith(NodeType* target); + + // Returns true if |this| is the sentinel node of an empty list. + bool IsEmptyList(); + + // The pointers to the next and previous nodes in the list. + // If the current node is not part of a list, then |next_node_| and + // |previous_node_| are equal to |nullptr|. + NodeType* next_node_; + NodeType* previous_node_; + + // Only true for the sentinel node stored in the list itself. + bool is_sentinel_; + + friend IntrusiveList; +}; + +// Implementation of IntrusiveNodeBase + +template +inline IntrusiveNodeBase::IntrusiveNodeBase() + : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {} + +template +inline IntrusiveNodeBase::IntrusiveNodeBase( + const IntrusiveNodeBase&) { + next_node_ = nullptr; + previous_node_ = nullptr; + is_sentinel_ = false; +} + +template +inline IntrusiveNodeBase& IntrusiveNodeBase::operator=( + const IntrusiveNodeBase&) { + assert(!is_sentinel_); + if (IsInAList()) { + RemoveFromList(); + } + return *this; +} + +template +inline IntrusiveNodeBase::IntrusiveNodeBase(IntrusiveNodeBase&& that) + : next_node_(nullptr), + previous_node_(nullptr), + is_sentinel_(that.is_sentinel_) { + if (is_sentinel_) { + next_node_ = this; + previous_node_ = this; + } + that.ReplaceWith(this); +} + +template +IntrusiveNodeBase::~IntrusiveNodeBase() { + assert(is_sentinel_ || !IsInAList()); +} + +template +IntrusiveNodeBase& IntrusiveNodeBase::operator=( + IntrusiveNodeBase&& that) { + that.ReplaceWith(this); + return *this; +} + +template +inline bool IntrusiveNodeBase::IsInAList() const { + return next_node_ != nullptr; +} + +template +inline NodeType* IntrusiveNodeBase::NextNode() const { + if (!next_node_->is_sentinel_) return next_node_; + return nullptr; +} + +template +inline NodeType* IntrusiveNodeBase::PreviousNode() const { + if (!previous_node_->is_sentinel_) return previous_node_; + return nullptr; +} + +template +inline void IntrusiveNodeBase::InsertBefore(NodeType* pos) { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(pos->IsInAList() && "Pos should already be in a list."); + if (this->IsInAList()) this->RemoveFromList(); + + this->next_node_ = pos; + this->previous_node_ = pos->previous_node_; + pos->previous_node_ = static_cast(this); + this->previous_node_->next_node_ = static_cast(this); +} + +template +inline void IntrusiveNodeBase::InsertAfter(NodeType* pos) { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(pos->IsInAList() && "Pos should already be in a list."); + assert(this != pos && "Can't insert a node after itself."); + + if (this->IsInAList()) { + this->RemoveFromList(); + } + + this->previous_node_ = pos; + this->next_node_ = pos->next_node_; + pos->next_node_ = static_cast(this); + this->next_node_->previous_node_ = static_cast(this); +} + +template +inline void IntrusiveNodeBase::RemoveFromList() { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(this->IsInAList() && + "Cannot remove a node from a list if it is not in a list."); + + this->next_node_->previous_node_ = this->previous_node_; + this->previous_node_->next_node_ = this->next_node_; + this->next_node_ = nullptr; + this->previous_node_ = nullptr; +} + +template +void IntrusiveNodeBase::ReplaceWith(NodeType* target) { + if (this->is_sentinel_) { + assert(target->IsEmptyList() && + "If target is not an empty list, the nodes in that list would not " + "be linked to a sentinel."); + } else { + assert(IsInAList() && "The node being replaced must be in a list."); + assert(!target->is_sentinel_ && + "Cannot turn a sentinel node into one that is not."); + } + + if (!this->IsEmptyList()) { + // Link target into the same position that |this| was in. + target->next_node_ = this->next_node_; + target->previous_node_ = this->previous_node_; + target->next_node_->previous_node_ = target; + target->previous_node_->next_node_ = target; + + // Reset |this| to itself default value. + if (!this->is_sentinel_) { + // Reset |this| so that it is not in a list. + this->next_node_ = nullptr; + this->previous_node_ = nullptr; + } else { + // Set |this| so that it is the head of an empty list. + // We cannot treat sentinel nodes like others because it is invalid for + // a sentinel node to not be in a list. + this->next_node_ = static_cast(this); + this->previous_node_ = static_cast(this); + } + } else { + // If |this| points to itself, it must be a sentinel node with an empty + // list. Reset |this| so that it is the head of an empty list. We want + // |target| to be the same. The asserts above guarantee that. + } +} + +template +bool IntrusiveNodeBase::IsEmptyList() { + if (next_node_ == this) { + assert(is_sentinel_ && + "None sentinel nodes should never point to themselves."); + assert(previous_node_ == this && + "Inconsistency with the previous and next nodes."); + return true; + } + return false; +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_ILIST_NODE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/make_unique.h b/engine/engine/3rdlibs/spirv-tools/source/util/make_unique.h new file mode 100644 index 00000000..ad7976c3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/make_unique.h @@ -0,0 +1,30 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_MAKE_UNIQUE_H_ +#define SOURCE_UTIL_MAKE_UNIQUE_H_ + +#include +#include + +namespace spvtools { + +template +std::unique_ptr MakeUnique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +} // namespace spvtools + +#endif // SOURCE_UTIL_MAKE_UNIQUE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.cpp b/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.cpp new file mode 100644 index 00000000..c3351c23 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.cpp @@ -0,0 +1,217 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/util/parse_number.h" + +#include +#include +#include +#include +#include +#include + +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace utils { +namespace { + +// A helper class that temporarily stores error messages and dump the messages +// to a string which given as as pointer when it is destructed. If the given +// pointer is a nullptr, this class does not store error message. +class ErrorMsgStream { + public: + explicit ErrorMsgStream(std::string* error_msg_sink) + : error_msg_sink_(error_msg_sink) { + if (error_msg_sink_) stream_ = MakeUnique(); + } + ~ErrorMsgStream() { + if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str(); + } + template + ErrorMsgStream& operator<<(T val) { + if (stream_) *stream_ << val; + return *this; + } + + private: + std::unique_ptr stream_; + // The destination string to which this class dump the error message when + // destructor is called. + std::string* error_msg_sink_; +}; +} // namespace + +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (!IsIntegral(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a integer type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const uint32_t bit_width = AssumedBitWidth(type); + + if (bit_width > 64) { + ErrorMsgStream(error_msg) + << "Unsupported " << bit_width << "-bit integer literals"; + return EncodeNumberStatus::kUnsupported; + } + + // Either we are expecting anything or integer. + bool is_negative = text[0] == '-'; + bool can_be_signed = IsSigned(type); + + if (is_negative && !can_be_signed) { + ErrorMsgStream(error_msg) + << "Cannot put a negative number in an unsigned literal"; + return EncodeNumberStatus::kInvalidUsage; + } + + const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X'); + + uint64_t decoded_bits; + if (is_negative) { + int64_t decoded_signed = 0; + + if (!ParseNumber(text, &decoded_signed)) { + ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + + if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex, + &decoded_signed)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_signed << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + decoded_bits = decoded_signed; + } else { + // There's no leading minus sign, so parse it as an unsigned integer. + if (!ParseNumber(text, &decoded_bits)) { + ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex, + &decoded_bits)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_bits << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + } + if (bit_width > 32) { + uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32); + emit(low); + emit(high); + } else { + emit(uint32_t(decoded_bits)); + } + return EncodeNumberStatus::kSuccess; +} + +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (!IsFloating(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const auto bit_width = AssumedBitWidth(type); + switch (bit_width) { + case 16: { + HexFloat> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + // getAsFloat will return the Float16 value, and get_value + // will return a uint16_t representing the bits of the float. + // The encoding is therefore correct from the perspective of the SPIR-V + // spec since the top 16 bits will be 0. + emit(static_cast(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case 32: { + HexFloat> fVal(0.0f); + if (!ParseNumber(text, &fVal)) { + ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + emit(BitwiseCast(fVal)); + return EncodeNumberStatus::kSuccess; + } break; + case 64: { + HexFloat> dVal(0.0); + if (!ParseNumber(text, &dVal)) { + ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + uint64_t decoded_val = BitwiseCast(dVal); + uint32_t low = uint32_t(0x00000000ffffffff & decoded_val); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32); + emit(low); + emit(high); + return EncodeNumberStatus::kSuccess; + } break; + default: + break; + } + ErrorMsgStream(error_msg) + << "Unsupported " << bit_width << "-bit float literals"; + return EncodeNumberStatus::kUnsupported; +} + +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (IsUnknown(type)) { + ErrorMsgStream(error_msg) + << "The expected type is not a integer or float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + // If we explicitly expect a floating-point number, we should handle that + // first. + if (IsFloating(type)) { + return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg); + } + + return ParseAndEncodeIntegerNumber(text, type, emit, error_msg); +} + +} // namespace utils +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.h b/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.h new file mode 100644 index 00000000..d0f2a09a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/parse_number.h @@ -0,0 +1,252 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_PARSE_NUMBER_H_ +#define SOURCE_UTIL_PARSE_NUMBER_H_ + +#include +#include +#include + +#include "source/util/hex_float.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace utils { + +// A struct to hold the expected type information for the number in text to be +// parsed. +struct NumberType { + uint32_t bitwidth; + // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with + // ParseAndEncode{|Integer|Floating}Number(). + spv_number_kind_t kind; +}; + +// Returns true if the type is a scalar integer type. +inline bool IsIntegral(const NumberType& type) { + return type.kind == SPV_NUMBER_UNSIGNED_INT || + type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is a scalar floating point type. +inline bool IsFloating(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING; +} + +// Returns true if the type is a signed value. +inline bool IsSigned(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is unknown. +inline bool IsUnknown(const NumberType& type) { + return type.kind == SPV_NUMBER_NONE; +} + +// Returns the number of bits in the type. This is only valid for integer and +// floating types. +inline int AssumedBitWidth(const NumberType& type) { + switch (type.kind) { + case SPV_NUMBER_SIGNED_INT: + case SPV_NUMBER_UNSIGNED_INT: + case SPV_NUMBER_FLOATING: + return type.bitwidth; + default: + break; + } + // We don't care about this case. + return 0; +} + +// A templated class with a static member function Clamp, where Clamp sets a +// referenced value of type T to 0 if T is an unsigned integer type, and +// returns true if it modified the referenced value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + +// Returns true if the given value fits within the target scalar integral type. +// The target type may have an unusual bit width. If the value was originally +// specified as a hexadecimal number, then the overflow bits should be zero. +// If it was hex and the target type is signed, then return the sign-extended +// value through the updated_value_for_hex pointer argument. On failure, +// returns false. +template +bool CheckRangeAndIfHexThenSignExtend(T value, const NumberType& type, + bool is_hex, T* updated_value_for_hex) { + // The encoded result has three regions of bits that are of interest, from + // least to most significant: + // - magnitude bits, where the magnitude of the number would be stored if + // we were using a signed-magnitude representation. + // - an optional sign bit + // - overflow bits, up to bit 63 of a 64-bit number + // For example: + // Type Overflow Sign Magnitude + // --------------- -------- ---- --------- + // unsigned 8 bit 8-63 n/a 0-7 + // signed 8 bit 8-63 7 0-6 + // unsigned 16 bit 16-63 n/a 0-15 + // signed 16 bit 16-63 15 0-14 + + // We'll use masks to define the three regions. + // At first we'll assume the number is unsigned. + const uint32_t bit_width = AssumedBitWidth(type); + uint64_t magnitude_mask = + (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); + uint64_t sign_mask = 0; + uint64_t overflow_mask = ~magnitude_mask; + + if (value < 0 || IsSigned(type)) { + // Accommodate the sign bit. + magnitude_mask >>= 1; + sign_mask = magnitude_mask + 1; + } + + bool failed = false; + if (value < 0) { + // The top bits must all be 1 for a negative signed value. + failed = ((value & overflow_mask) != overflow_mask) || + ((value & sign_mask) != sign_mask); + } else { + if (is_hex) { + // Hex values are a bit special. They decode as unsigned values, but may + // represent a negative number. In this case, the overflow bits should + // be zero. + failed = (value & overflow_mask) != 0; + } else { + const uint64_t value_as_u64 = static_cast(value); + // Check overflow in the ordinary case. + failed = (value_as_u64 & magnitude_mask) != value_as_u64; + } + } + + if (failed) { + return false; + } + + // Sign extend hex the number. + if (is_hex && (value & sign_mask)) + *updated_value_for_hex = (value | overflow_mask); + + return true; +} + +// Parses a numeric value of a given type from the given text. The number +// should take up the entire string, and should be within bounds for the target +// type. On success, returns true and populates the object referenced by +// value_pointer. On failure, returns false. +template +bool ParseNumber(const char* text, T* value_pointer) { + // C++11 doesn't define std::istringstream(int8_t&), so calling this method + // with a single-byte type leads to implementation-defined behaviour. + // Similarly for uint8_t. + static_assert(sizeof(T) > 1, + "Single-byte types are not supported in this parse method"); + + if (!text) return false; + std::istringstream text_stream(text); + // Allow both decimal and hex input for integers. + // It also allows octal input, but we don't care about that case. + text_stream >> std::setbase(0); + text_stream >> *value_pointer; + + // We should have read something. + bool ok = (text[0] != 0) && !text_stream.bad(); + // It should have been all the text. + ok = ok && text_stream.eof(); + // It should have been in range. + ok = ok && !text_stream.fail(); + + // Work around a bug in the GNU C++11 library. It will happily parse + // "-1" for uint16_t as 65535. + if (ok && text[0] == '-') + ok = !ClampToZeroIfUnsignedType::Clamp(value_pointer); + + return ok; +} + +// Enum to indicate the parsing and encoding status. +enum class EncodeNumberStatus { + kSuccess = 0, + // Unsupported bit width etc. + kUnsupported, + // Expected type (NumberType) is not a scalar int or float, or putting a + // negative number in an unsigned literal. + kInvalidUsage, + // Number value does not fit the bit width of the expected type etc. + kInvalidText, +}; + +// Parses an integer value of a given |type| from the given |text| and encodes +// the number by the given |emit| function. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits are +// supported. +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses a floating point value of a given |type| from the given |text| and +// encodes the number by the given |emit| function. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Only 16, 32 and 64 bit +// floating point numbers are supported. +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses an integer or floating point number of a given |type| from the given +// |text| and encodes the number by the given |emit| function. On success, +// returns EncodeNumberStatus::kSuccess and the parsed number will be consumed +// by the given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits +// and 16/32/64 bit floating point values are supported. +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg); + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_PARSE_NUMBER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/small_vector.h b/engine/engine/3rdlibs/spirv-tools/source/util/small_vector.h new file mode 100644 index 00000000..18388438 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/small_vector.h @@ -0,0 +1,490 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_SMALL_VECTOR_H_ +#define SOURCE_UTIL_SMALL_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace utils { + +// The |SmallVector| class is intended to be a drop-in replacement for +// |std::vector|. The difference is in the implementation. A |SmallVector| is +// optimized for when the number of elements in the vector are small. Small is +// defined by the template parameter |small_size|. +// +// Note that |SmallVector| is not always faster than an |std::vector|, so you +// should experiment with different values for |small_size| and compare to +// using and |std::vector|. +// +// TODO: I have implemented the public member functions from |std::vector| that +// I needed. If others are needed they should be implemented. Do not implement +// public member functions that are not defined by std::vector. +template +class SmallVector { + public: + using value_type = T; + using iterator = T*; + using const_iterator = const T*; + + SmallVector() + : size_(0), + small_data_(reinterpret_cast(buffer)), + large_data_(nullptr) {} + + SmallVector(const SmallVector& that) : SmallVector() { *this = that; } + + SmallVector(SmallVector&& that) : SmallVector() { *this = std::move(that); } + + SmallVector(const std::vector& vec) : SmallVector() { + if (vec.size() > small_size) { + large_data_ = MakeUnique>(vec); + } else { + size_ = vec.size(); + for (uint32_t i = 0; i < size_; i++) { + new (small_data_ + i) T(vec[i]); + } + } + } + + template + SmallVector(InputIt first, InputIt last) : SmallVector() { + insert(end(), first, last); + } + + SmallVector(std::vector&& vec) : SmallVector() { + if (vec.size() > small_size) { + large_data_ = MakeUnique>(std::move(vec)); + } else { + size_ = vec.size(); + for (uint32_t i = 0; i < size_; i++) { + new (small_data_ + i) T(std::move(vec[i])); + } + } + vec.clear(); + } + + SmallVector(std::initializer_list init_list) : SmallVector() { + if (init_list.size() < small_size) { + for (auto it = init_list.begin(); it != init_list.end(); ++it) { + new (small_data_ + (size_++)) T(std::move(*it)); + } + } else { + large_data_ = MakeUnique>(std::move(init_list)); + } + } + + SmallVector(size_t s, const T& v) : SmallVector() { resize(s, v); } + + virtual ~SmallVector() { + for (T* p = small_data_; p < small_data_ + size_; ++p) { + p->~T(); + } + } + + SmallVector& operator=(const SmallVector& that) { + assert(small_data_); + if (that.large_data_) { + if (large_data_) { + *large_data_ = *that.large_data_; + } else { + large_data_ = MakeUnique>(*that.large_data_); + } + } else { + large_data_.reset(nullptr); + size_t i = 0; + // Do a copy for any element in |this| that is already constructed. + for (; i < size_ && i < that.size_; ++i) { + small_data_[i] = that.small_data_[i]; + } + + if (i >= that.size_) { + // If the size of |this| becomes smaller after the assignment, then + // destroy any extra elements. + for (; i < size_; ++i) { + small_data_[i].~T(); + } + } else { + // If the size of |this| becomes larger after the assignement, copy + // construct the new elements that are needed. + for (; i < that.size_; ++i) { + new (small_data_ + i) T(that.small_data_[i]); + } + } + size_ = that.size_; + } + return *this; + } + + SmallVector& operator=(SmallVector&& that) { + if (that.large_data_) { + large_data_.reset(that.large_data_.release()); + } else { + large_data_.reset(nullptr); + size_t i = 0; + // Do a move for any element in |this| that is already constructed. + for (; i < size_ && i < that.size_; ++i) { + small_data_[i] = std::move(that.small_data_[i]); + } + + if (i >= that.size_) { + // If the size of |this| becomes smaller after the assignment, then + // destroy any extra elements. + for (; i < size_; ++i) { + small_data_[i].~T(); + } + } else { + // If the size of |this| becomes larger after the assignement, move + // construct the new elements that are needed. + for (; i < that.size_; ++i) { + new (small_data_ + i) T(std::move(that.small_data_[i])); + } + } + size_ = that.size_; + } + + // Reset |that| because all of the data has been moved to |this|. + that.DestructSmallData(); + return *this; + } + + template + friend bool operator==(const SmallVector& lhs, const OtherVector& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + auto rit = rhs.begin(); + for (auto lit = lhs.begin(); lit != lhs.end(); ++lit, ++rit) { + if (*lit != *rit) { + return false; + } + } + return true; + } + +// Avoid infinite recursion from rewritten operators in C++20 +#if __cplusplus <= 201703L + friend bool operator==(const std::vector& lhs, const SmallVector& rhs) { + return rhs == lhs; + } +#endif + + friend bool operator!=(const SmallVector& lhs, const std::vector& rhs) { + return !(lhs == rhs); + } + + friend bool operator!=(const std::vector& lhs, const SmallVector& rhs) { + return rhs != lhs; + } + + T& operator[](size_t i) { + if (!large_data_) { + return small_data_[i]; + } else { + return (*large_data_)[i]; + } + } + + const T& operator[](size_t i) const { + if (!large_data_) { + return small_data_[i]; + } else { + return (*large_data_)[i]; + } + } + + size_t size() const { + if (!large_data_) { + return size_; + } else { + return large_data_->size(); + } + } + + iterator begin() { + if (large_data_) { + return large_data_->data(); + } else { + return small_data_; + } + } + + const_iterator begin() const { + if (large_data_) { + return large_data_->data(); + } else { + return small_data_; + } + } + + const_iterator cbegin() const { return begin(); } + + iterator end() { + if (large_data_) { + return large_data_->data() + large_data_->size(); + } else { + return small_data_ + size_; + } + } + + const_iterator end() const { + if (large_data_) { + return large_data_->data() + large_data_->size(); + } else { + return small_data_ + size_; + } + } + + const_iterator cend() const { return end(); } + + T* data() { return begin(); } + + const T* data() const { return cbegin(); } + + T& front() { return (*this)[0]; } + + const T& front() const { return (*this)[0]; } + + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + + iterator erase(const_iterator first, const_iterator last) { + if (large_data_) { + size_t start_index = first - large_data_->data(); + size_t end_index = last - large_data_->data(); + auto r = large_data_->erase(large_data_->begin() + start_index, + large_data_->begin() + end_index); + return large_data_->data() + (r - large_data_->begin()); + } + + // Since C++11, std::vector has |const_iterator| for the parameters, so I + // follow that. However, I need iterators to modify the current container, + // which is not const. This is why I cast away the const. + iterator f = const_cast(first); + iterator l = const_cast(last); + iterator e = end(); + + size_t num_of_del_elements = last - first; + iterator ret = f; + if (first == last) { + return ret; + } + + // Move |last| and any elements after it their earlier position. + while (l != e) { + *f = std::move(*l); + ++f; + ++l; + } + + // Destroy the elements that were supposed to be deleted. + while (f != l) { + f->~T(); + ++f; + } + + // Update the size. + size_ -= num_of_del_elements; + return ret; + } + + void push_back(const T& value) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->push_back(value); + return; + } + + new (small_data_ + size_) T(value); + ++size_; + } + + void push_back(T&& value) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->push_back(std::move(value)); + return; + } + + new (small_data_ + size_) T(std::move(value)); + ++size_; + } + + void pop_back() { + if (large_data_) { + large_data_->pop_back(); + } else { + --size_; + small_data_[size_].~T(); + } + } + + template + iterator insert(iterator pos, InputIt first, InputIt last) { + size_t element_idx = (pos - begin()); + size_t num_of_new_elements = std::distance(first, last); + size_t new_size = size_ + num_of_new_elements; + if (!large_data_ && new_size > small_size) { + MoveToLargeData(); + } + + if (large_data_) { + typename std::vector::iterator new_pos = + large_data_->begin() + element_idx; + large_data_->insert(new_pos, first, last); + return begin() + element_idx; + } + + // Move |pos| and all of the elements after it over |num_of_new_elements| + // places. We start at the end and work backwards, to make sure we do not + // overwrite data that we have not moved yet. + for (iterator i = begin() + new_size - 1, j = end() - 1; j >= pos; + --i, --j) { + if (i >= begin() + size_) { + new (i) T(std::move(*j)); + } else { + *i = std::move(*j); + } + } + + // Copy the new elements into position. + iterator p = pos; + for (; first != last; ++p, ++first) { + if (p >= small_data_ + size_) { + new (p) T(*first); + } else { + *p = *first; + } + } + + // Update the size. + size_ += num_of_new_elements; + return pos; + } + + bool empty() const { + if (large_data_) { + return large_data_->empty(); + } + return size_ == 0; + } + + void clear() { + if (large_data_) { + large_data_->clear(); + } else { + DestructSmallData(); + } + } + + template + void emplace_back(Args&&... args) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->emplace_back(std::forward(args)...); + } else { + new (small_data_ + size_) T(std::forward(args)...); + ++size_; + } + } + + void resize(size_t new_size, const T& v) { + if (!large_data_ && new_size > small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->resize(new_size, v); + return; + } + + // If |new_size| < |size_|, then destroy the extra elements. + for (size_t i = new_size; i < size_; ++i) { + small_data_[i].~T(); + } + + // If |new_size| > |size_|, the copy construct the new elements. + for (size_t i = size_; i < new_size; ++i) { + new (small_data_ + i) T(v); + } + + // Update the size. + size_ = new_size; + } + + private: + // Moves all of the element from |small_data_| into a new std::vector that can + // be access through |large_data|. + void MoveToLargeData() { + assert(!large_data_); + large_data_ = MakeUnique>(); + for (size_t i = 0; i < size_; ++i) { + large_data_->emplace_back(std::move(small_data_[i])); + } + DestructSmallData(); + } + + // Destroys all of the elements in |small_data_| that have been constructed. + void DestructSmallData() { + for (size_t i = 0; i < size_; ++i) { + small_data_[i].~T(); + } + size_ = 0; + } + + // The number of elements in |small_data_| that have been constructed. + size_t size_; + + // A type with the same alignment and size as T, but will is POD. + struct alignas(T) PodType { + std::array data; + }; + + // The actual data used to store the array elements. It must never be used + // directly, but must only be accessed through |small_data_|. + PodType buffer[small_size]; + + // The pointed used to access the array of elements when the number of + // elements is small. + T* small_data_; + + // A pointer to a vector that is used to store the elements of the vector when + // this size exceeds |small_size|. If |large_data_| is nullptr, then the data + // is stored in |small_data_|. Otherwise, the data is stored in + // |large_data_|. + std::unique_ptr> large_data_; +}; // namespace utils + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_SMALL_VECTOR_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.cpp b/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.cpp new file mode 100644 index 00000000..b56c353a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/util/string_utils.h" + +namespace spvtools { +namespace utils { + +std::string CardinalToOrdinal(size_t cardinal) { + const size_t mod10 = cardinal % 10; + const size_t mod100 = cardinal % 100; + std::string suffix; + if (mod10 == 1 && mod100 != 11) + suffix = "st"; + else if (mod10 == 2 && mod100 != 12) + suffix = "nd"; + else if (mod10 == 3 && mod100 != 13) + suffix = "rd"; + else + suffix = "th"; + + return ToString(cardinal) + suffix; +} + +std::pair SplitFlagArgs(const std::string& flag) { + if (flag.size() < 2) return make_pair(flag, std::string()); + + // Detect the last dash before the pass name. Since we have to + // handle single dash options (-O and -Os), count up to two dashes. + size_t dash_ix = 0; + if (flag[0] == '-' && flag[1] == '-') + dash_ix = 2; + else if (flag[0] == '-') + dash_ix = 1; + + size_t ix = flag.find('='); + return (ix != std::string::npos) + ? make_pair(flag.substr(dash_ix, ix - 2), flag.substr(ix + 1)) + : make_pair(flag.substr(dash_ix), std::string()); +} + +} // namespace utils +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.h b/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.h new file mode 100644 index 00000000..590f8cf4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/string_utils.h @@ -0,0 +1,133 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_STRING_UTILS_H_ +#define SOURCE_UTIL_STRING_UTILS_H_ + +#include + +#include +#include +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Converts arithmetic value |val| to its default string representation. +template +std::string ToString(T val) { + static_assert( + std::is_arithmetic::value, + "spvtools::utils::ToString is restricted to only arithmetic values"); + std::stringstream os; + os << val; + return os.str(); +} + +// Converts cardinal number to ordinal number string. +std::string CardinalToOrdinal(size_t cardinal); + +// Splits the string |flag|, of the form '--pass_name[=pass_args]' into two +// strings "pass_name" and "pass_args". If |flag| has no arguments, the second +// string will be empty. +std::pair SplitFlagArgs(const std::string& flag); + +// Encodes a string as a sequence of words, using the SPIR-V encoding, appending +// to an existing vector. +template > +inline void AppendToVector(const std::string& input, VectorType* result) { + static_assert(std::is_same::value); + uint32_t word = 0; + size_t num_bytes = input.size(); + // SPIR-V strings are null-terminated. The byte_index == num_bytes + // case is used to push the terminating null byte. + for (size_t byte_index = 0; byte_index <= num_bytes; byte_index++) { + const auto new_byte = + (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); + word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); + if (3 == (byte_index % sizeof(uint32_t))) { + result->push_back(word); + word = 0; + } + } + // Emit a trailing partial word. + if ((num_bytes + 1) % sizeof(uint32_t)) { + result->push_back(word); + } +} + +// Encodes a string as a sequence of words, using the SPIR-V encoding. +template > +inline VectorType MakeVector(const std::string& input) { + static_assert(std::is_same::value); + VectorType result; + AppendToVector(input, &result); + return result; +} + +// Decode a string from a sequence of words between first and last, using the +// SPIR-V encoding. Assert that a terminating 0-byte was found (unless +// assert_found_terminating_null is passed as false). +template +inline std::string MakeString(InputIt first, InputIt last, + bool assert_found_terminating_null = true) { + std::string result; + constexpr size_t kCharsPerWord = sizeof(*first); + static_assert(kCharsPerWord == 4, "expect 4-byte word"); + + for (InputIt pos = first; pos != last; ++pos) { + uint32_t word = *pos; + for (size_t byte_index = 0; byte_index < kCharsPerWord; byte_index++) { + uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF; + char c = static_cast(extracted_word); + if (c == 0) { + return result; + } + result += c; + } + } + assert(!assert_found_terminating_null && + "Did not find terminating null for the string."); + (void)assert_found_terminating_null; /* No unused parameters in release + builds. */ + return result; +} + +// Decode a string from a sequence of words in a vector, using the SPIR-V +// encoding. +template +inline std::string MakeString(const VectorType& words, + bool assert_found_terminating_null = true) { + return MakeString(words.cbegin(), words.cend(), + assert_found_terminating_null); +} + +// Decode a string from array words, consuming up to count words, using the +// SPIR-V encoding. +inline std::string MakeString(const uint32_t* words, size_t num_words, + bool assert_found_terminating_null = true) { + return MakeString(words, words + num_words, assert_found_terminating_null); +} + +// Check if str starts with prefix (only included since C++20) +inline bool starts_with(const std::string& str, const char* prefix) { + return 0 == str.compare(0, std::strlen(prefix), prefix); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_STRING_UTILS_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/timer.cpp b/engine/engine/3rdlibs/spirv-tools/source/util/timer.cpp new file mode 100644 index 00000000..c8b8d5b6 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/timer.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(SPIRV_TIMER_ENABLED) + +#include "source/util/timer.h" + +#include +#include +#include +#include +#include + +namespace spvtools { +namespace utils { + +void PrintTimerDescription(std::ostream* out, bool measure_mem_usage) { + if (out) { + *out << std::setw(30) << "PASS name" << std::setw(12) << "CPU time" + << std::setw(12) << "WALL time" << std::setw(12) << "USR time" + << std::setw(12) << "SYS time"; + if (measure_mem_usage) { + *out << std::setw(12) << "RSS delta" << std::setw(16) << "PGFault delta"; + } + *out << std::endl; + } +} + +// Do not change the order of invoking system calls. We want to make CPU/Wall +// time correct as much as possible. Calling functions to get CPU/Wall time must +// closely surround the target code of measuring. +void Timer::Start() { + if (report_stream_) { + if (getrusage(RUSAGE_SELF, &usage_before_) == -1) + usage_status_ |= kGetrusageFailed; + if (clock_gettime(CLOCK_MONOTONIC, &wall_before_) == -1) + usage_status_ |= kClockGettimeWalltimeFailed; + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_before_) == -1) + usage_status_ |= kClockGettimeCPUtimeFailed; + } +} + +// The order of invoking system calls is important with the same reason as +// Timer::Start(). +void Timer::Stop() { + if (report_stream_ && usage_status_ == kSucceeded) { + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_after_) == -1) + usage_status_ |= kClockGettimeCPUtimeFailed; + if (clock_gettime(CLOCK_MONOTONIC, &wall_after_) == -1) + usage_status_ |= kClockGettimeWalltimeFailed; + if (getrusage(RUSAGE_SELF, &usage_after_) == -1) + usage_status_ = kGetrusageFailed; + } +} + +void Timer::Report(const char* tag) { + if (!report_stream_) return; + + report_stream_->precision(2); + *report_stream_ << std::fixed << std::setw(30) << tag; + + if (usage_status_ & kClockGettimeCPUtimeFailed) + *report_stream_ << std::setw(12) << "Failed"; + else + *report_stream_ << std::setw(12) << CPUTime(); + + if (usage_status_ & kClockGettimeWalltimeFailed) + *report_stream_ << std::setw(12) << "Failed"; + else + *report_stream_ << std::setw(12) << WallTime(); + + if (usage_status_ & kGetrusageFailed) { + *report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed"; + if (measure_mem_usage_) { + *report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed"; + } + } else { + *report_stream_ << std::setw(12) << UserTime() << std::setw(12) + << SystemTime(); + if (measure_mem_usage_) { + *report_stream_ << std::fixed << std::setw(12) << RSS() << std::setw(16) + << PageFault(); + } + } + *report_stream_ << std::endl; +} + +} // namespace utils +} // namespace spvtools + +#endif // defined(SPIRV_TIMER_ENABLED) diff --git a/engine/engine/3rdlibs/spirv-tools/source/util/timer.h b/engine/engine/3rdlibs/spirv-tools/source/util/timer.h new file mode 100644 index 00000000..08083119 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/util/timer.h @@ -0,0 +1,392 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Contains utils for getting resource utilization + +#ifndef SOURCE_UTIL_TIMER_H_ +#define SOURCE_UTIL_TIMER_H_ + +#if defined(SPIRV_TIMER_ENABLED) + +#include +#include +#include + +// A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool). +// The first argument must be given as std::ostream*. If it is NULL, the +// function does nothing. Otherwise, it prints resource types measured by Timer +// class. The second is optional and if it is true, the function also prints +// resource type fields related to memory. Otherwise, it does not print memory +// related fields. Its default is false. In usual, this must be placed before +// calling Timer::Report() to inform what those fields printed by +// Timer::Report() indicate (or spvtools::utils::PrintTimerDescription() must be +// used instead). +#define SPIRV_TIMER_DESCRIPTION(...) \ + spvtools::utils::PrintTimerDescription(__VA_ARGS__) + +// Creates an object of ScopedTimer to measure the resource utilization for the +// scope surrounding it as the following example: +// +// { // <-- beginning of this scope +// +// /* ... code out of interest ... */ +// +// SPIRV_TIMER_SCOPED(std::cout, tag); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// } // <-- end of this scope. The destructor of ScopedTimer prints tag and +// the resource utilization to std::cout. +#define SPIRV_TIMER_SCOPED(...) \ + spvtools::utils::ScopedTimer timer##__LINE__( \ + __VA_ARGS__) + +namespace spvtools { +namespace utils { + +// Prints the description of resource types measured by Timer class. If |out| is +// NULL, it does nothing. Otherwise, it prints resource types. The second is +// optional and if it is true, the function also prints resource type fields +// related to memory. Its default is false. In usual, this must be placed before +// calling Timer::Report() to inform what those fields printed by +// Timer::Report() indicate. +void PrintTimerDescription(std::ostream*, bool = false); + +// Status of Timer. kGetrusageFailed means it failed in calling getrusage(). +// kClockGettimeWalltimeFailed means it failed in getting wall time when calling +// clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU +// time when calling clock_gettime(). +enum UsageStatus { + kSucceeded = 0, + kGetrusageFailed = 1 << 0, + kClockGettimeWalltimeFailed = 1 << 1, + kClockGettimeCPUtimeFailed = 1 << 2, +}; + +// Timer measures the resource utilization for a range of code. The resource +// utilization consists of CPU time (i.e., process time), WALL time (elapsed +// time), USR time, SYS time, RSS delta, and the delta of the number of page +// faults. RSS delta and the delta of the number of page faults are measured +// only when |measure_mem_usage| given to the constructor is true. This class +// should be used as the following example: +// +// spvtools::utils::Timer timer(std::cout); +// timer.Start(); // <-- set |usage_before_|, |wall_before_|, +// and |cpu_before_| +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// timer.Stop(); // <-- set |cpu_after_|, |wall_after_|, and +// |usage_after_| +// timer.Report(tag); // <-- print tag and the resource utilization to +// std::cout. +class Timer { + public: + Timer(std::ostream* out, bool measure_mem_usage = false) + : report_stream_(out), + usage_status_(kSucceeded), + measure_mem_usage_(measure_mem_usage) {} + + // Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of + // getrusage(), clock_gettime() for the wall time, and clock_gettime() for the + // CPU time respectively. Note that this method erases all previous state of + // |usage_before_|, |wall_before_|, |cpu_before_|. + virtual void Start(); + + // Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of + // clock_gettime() for the wall time, and clock_gettime() for the CPU time, + // getrusage() respectively. Note that this method erases all previous state + // of |cpu_after_|, |wall_after_|, |usage_after_|. + virtual void Stop(); + + // If |report_stream_| is NULL, it does nothing. Otherwise, it prints the + // resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the + // time of calling Timer::Start() and the time of calling Timer::Stop(). If we + // cannot get a resource usage because of failures, it prints "Failed" instead + // for the resource. + void Report(const char* tag); + + // Returns the measured CPU Time (i.e., process time) for a range of code + // execution. If kClockGettimeCPUtimeFailed is set by the failure of calling + // clock_gettime(), it returns -1. + virtual double CPUTime() { + if (usage_status_ & kClockGettimeCPUtimeFailed) return -1; + return TimeDifference(cpu_before_, cpu_after_); + } + + // Returns the measured Wall Time (i.e., elapsed time) for a range of code + // execution. If kClockGettimeWalltimeFailed is set by the failure of + // calling clock_gettime(), it returns -1. + virtual double WallTime() { + if (usage_status_ & kClockGettimeWalltimeFailed) return -1; + return TimeDifference(wall_before_, wall_after_); + } + + // Returns the measured USR Time for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual double UserTime() { + if (usage_status_ & kGetrusageFailed) return -1; + return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime); + } + + // Returns the measured SYS Time for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual double SystemTime() { + if (usage_status_ & kGetrusageFailed) return -1; + return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime); + } + + // Returns the measured RSS delta for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual long RSS() const { + if (usage_status_ & kGetrusageFailed) return -1; + return usage_after_.ru_maxrss - usage_before_.ru_maxrss; + } + + // Returns the measured the delta of the number of page faults for a range of + // code execution. If kGetrusageFailed is set because of the failure of + // calling getrusage(), it returns -1. + virtual long PageFault() const { + if (usage_status_ & kGetrusageFailed) return -1; + return (usage_after_.ru_minflt - usage_before_.ru_minflt) + + (usage_after_.ru_majflt - usage_before_.ru_majflt); + } + + virtual ~Timer() {} + + private: + // Returns the time gap between |from| and |to| in seconds. + static double TimeDifference(const timeval& from, const timeval& to) { + assert((to.tv_sec > from.tv_sec) || + (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec)); + return static_cast(to.tv_sec - from.tv_sec) + + static_cast(to.tv_usec - from.tv_usec) * .000001; + } + + // Returns the time gap between |from| and |to| in seconds. + static double TimeDifference(const timespec& from, const timespec& to) { + assert((to.tv_sec > from.tv_sec) || + (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec)); + return static_cast(to.tv_sec - from.tv_sec) + + static_cast(to.tv_nsec - from.tv_nsec) * .000000001; + } + + // Output stream to print out the resource utilization. If it is NULL, + // Report() does nothing. + std::ostream* report_stream_; + + // Status to stop measurement if a system call returns an error. + unsigned usage_status_; + + // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when + // Timer::Start() is called. It is used as the base status of CPU time. + timespec cpu_before_; + + // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when + // Timer::Start() is called. It is used as the base status of WALL time. + timespec wall_before_; + + // Variable to save the result of getrusage() when Timer::Start() is called. + // It is used as the base status of USR time, SYS time, and RSS. + rusage usage_before_; + + // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when + // Timer::Stop() is called. It is used as the last status of CPU time. The + // resource usage is measured by subtracting |cpu_before_| from it. + timespec cpu_after_; + + // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when + // Timer::Stop() is called. It is used as the last status of WALL time. The + // resource usage is measured by subtracting |wall_before_| from it. + timespec wall_after_; + + // Variable to save the result of getrusage() when Timer::Stop() is called. It + // is used as the last status of USR time, SYS time, and RSS. Those resource + // usages are measured by subtracting |usage_before_| from it. + rusage usage_after_; + + // If true, Timer reports the memory usage information too. Otherwise, Timer + // reports only USR time, WALL time, SYS time. + bool measure_mem_usage_; +}; + +// The purpose of ScopedTimer is to measure the resource utilization for a +// scope. Simply creating a local variable of ScopedTimer will call +// Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of +// the scope by its destructor. When we use this class, we must choose the +// proper Timer class (for class TimerType template) in advance. This class +// should be used as the following example: +// +// { // <-- beginning of this scope +// +// /* ... code out of interest ... */ +// +// spvtools::utils::ScopedTimer +// scopedtimer(std::cout, tag); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// } // <-- end of this scope. The destructor of ScopedTimer prints tag and +// the resource utilization to std::cout. +// +// The template is used to choose a Timer class. Currently, +// only options for the Timer class are Timer and MockTimer in the unit test. +template +class ScopedTimer { + public: + ScopedTimer(std::ostream* out, const char* tag, + bool measure_mem_usage = false) + : timer(new TimerType(out, measure_mem_usage)), tag_(tag) { + timer->Start(); + } + + // At the end of the scope surrounding the instance of this class, this + // destructor saves the last status of resource usage and reports it. + virtual ~ScopedTimer() { + timer->Stop(); + timer->Report(tag_); + delete timer; + } + + private: + // Actual timer that measures the resource utilization. It must be an instance + // of Timer class if there is no special reason to use other class. + TimerType* timer; + + // A tag that will be printed in front of the trace reported by Timer class. + const char* tag_; +}; + +// CumulativeTimer is the same as Timer class, but it supports a cumulative +// measurement as the following example: +// +// CumulativeTimer *ctimer = new CumulativeTimer(std::cout); +// ctimer->Start(); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// ctimer->Stop(); +// +// /* ... code out of interest ... */ +// +// ctimer->Start(); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// ctimer->Stop(); +// ctimer->Report(tag); +// delete ctimer; +// +class CumulativeTimer : public Timer { + public: + CumulativeTimer(std::ostream* out, bool measure_mem_usage = false) + : Timer(out, measure_mem_usage), + cpu_time_(0), + wall_time_(0), + usr_time_(0), + sys_time_(0), + rss_(0), + pgfaults_(0) {} + + // If we cannot get a resource usage because of failures, it sets -1 for the + // resource usage. + void Stop() override { + Timer::Stop(); + + if (cpu_time_ >= 0 && Timer::CPUTime() >= 0) + cpu_time_ += Timer::CPUTime(); + else + cpu_time_ = -1; + + if (wall_time_ >= 0 && Timer::WallTime() >= 0) + wall_time_ += Timer::WallTime(); + else + wall_time_ = -1; + + if (usr_time_ >= 0 && Timer::UserTime() >= 0) + usr_time_ += Timer::UserTime(); + else + usr_time_ = -1; + + if (sys_time_ >= 0 && Timer::SystemTime() >= 0) + sys_time_ += Timer::SystemTime(); + else + sys_time_ = -1; + + if (rss_ >= 0 && Timer::RSS() >= 0) + rss_ += Timer::RSS(); + else + rss_ = -1; + + if (pgfaults_ >= 0 && Timer::PageFault() >= 0) + pgfaults_ += Timer::PageFault(); + else + pgfaults_ = -1; + } + + // Returns the cumulative CPU Time (i.e., process time) for a range of code + // execution. + double CPUTime() override { return cpu_time_; } + + // Returns the cumulative Wall Time (i.e., elapsed time) for a range of code + // execution. + double WallTime() override { return wall_time_; } + + // Returns the cumulative USR Time for a range of code execution. + double UserTime() override { return usr_time_; } + + // Returns the cumulative SYS Time for a range of code execution. + double SystemTime() override { return sys_time_; } + + // Returns the cumulative RSS delta for a range of code execution. + long RSS() const override { return rss_; } + + // Returns the cumulative delta of number of page faults for a range of code + // execution. + long PageFault() const override { return pgfaults_; } + + private: + // Variable to save the cumulative CPU time (i.e., process time). + double cpu_time_; + + // Variable to save the cumulative wall time (i.e., elapsed time). + double wall_time_; + + // Variable to save the cumulative user time. + double usr_time_; + + // Variable to save the cumulative system time. + double sys_time_; + + // Variable to save the cumulative RSS delta. + long rss_; + + // Variable to save the cumulative delta of the number of page faults. + long pgfaults_; +}; + +} // namespace utils +} // namespace spvtools + +#else // defined(SPIRV_TIMER_ENABLED) + +#define SPIRV_TIMER_DESCRIPTION(...) +#define SPIRV_TIMER_SCOPED(...) + +#endif // defined(SPIRV_TIMER_ENABLED) + +#endif // SOURCE_UTIL_TIMER_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.cpp new file mode 100644 index 00000000..9a358fcb --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/basic_block.h" + +#include +#include + +namespace spvtools { +namespace val { + +BasicBlock::BasicBlock(uint32_t label_id) + : id_(label_id), + immediate_dominator_(nullptr), + immediate_structural_dominator_(nullptr), + immediate_structural_post_dominator_(nullptr), + predecessors_(), + successors_(), + type_(0), + reachable_(false), + structurally_reachable_(false), + label_(nullptr), + terminator_(nullptr) {} + +void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { + immediate_dominator_ = dom_block; +} + +void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) { + immediate_structural_dominator_ = dom_block; +} + +void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) { + immediate_structural_post_dominator_ = pdom_block; +} + +const BasicBlock* BasicBlock::immediate_dominator() const { + return immediate_dominator_; +} + +const BasicBlock* BasicBlock::immediate_structural_dominator() const { + return immediate_structural_dominator_; +} + +const BasicBlock* BasicBlock::immediate_structural_post_dominator() const { + return immediate_structural_post_dominator_; +} + +BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; } +BasicBlock* BasicBlock::immediate_structural_dominator() { + return immediate_structural_dominator_; +} +BasicBlock* BasicBlock::immediate_structural_post_dominator() { + return immediate_structural_post_dominator_; +} + +void BasicBlock::RegisterSuccessors( + const std::vector& next_blocks) { + for (auto& block : next_blocks) { + block->predecessors_.push_back(this); + successors_.push_back(block); + + // Register structural successors/predecessors too. + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); + } +} + +bool BasicBlock::dominates(const BasicBlock& other) const { + return (this == &other) || + !(other.dom_end() == + std::find(other.dom_begin(), other.dom_end(), this)); +} + +bool BasicBlock::structurally_dominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_dom_end() == + std::find(other.structural_dom_begin(), + other.structural_dom_end(), this)); +} + +bool BasicBlock::structurally_postdominates(const BasicBlock& other) const { + return (this == &other) || !(other.structural_pdom_end() == + std::find(other.structural_pdom_begin(), + other.structural_pdom_end(), this)); +} + +BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} + +BasicBlock::DominatorIterator::DominatorIterator( + const BasicBlock* block, + std::function dominator_func) + : current_(block), dom_func_(dominator_func) {} + +BasicBlock::DominatorIterator& BasicBlock::DominatorIterator::operator++() { + if (current_ == dom_func_(current_)) { + current_ = nullptr; + } else { + current_ = dom_func_(current_); + } + return *this; +} + +const BasicBlock::DominatorIterator BasicBlock::dom_begin() const { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_dominator(); }); +} + +BasicBlock::DominatorIterator BasicBlock::dom_begin() { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_dominator(); }); +} + +const BasicBlock::DominatorIterator BasicBlock::dom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::dom_end() { + return DominatorIterator(); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); +} + +BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_dominator(); + }); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::structural_dom_end() { + return DominatorIterator(); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() { + return DominatorIterator(this, [](const BasicBlock* b) { + return b->immediate_structural_post_dominator(); + }); +} + +const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() { + return DominatorIterator(); +} + +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return lhs.current_ == rhs.current_; +} + +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return !(lhs == rhs); +} + +const BasicBlock*& BasicBlock::DominatorIterator::operator*() { + return current_; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.h b/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.h new file mode 100644 index 00000000..be5657ea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/basic_block.h @@ -0,0 +1,320 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_BASIC_BLOCK_H_ +#define SOURCE_VAL_BASIC_BLOCK_H_ + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { +namespace val { + +enum BlockType : uint32_t { + kBlockTypeUndefined, + kBlockTypeSelection, + kBlockTypeLoop, + kBlockTypeMerge, + kBlockTypeBreak, + kBlockTypeContinue, + kBlockTypeReturn, + kBlockTypeCOUNT ///< Total number of block types. (must be the last element) +}; + +class Instruction; + +// This class represents a basic block in a SPIR-V module +class BasicBlock { + public: + /// Constructor for a BasicBlock + /// + /// @param[in] id The ID of the basic block + explicit BasicBlock(uint32_t id); + + /// Returns the id of the BasicBlock + uint32_t id() const { return id_; } + + /// Returns the predecessors of the BasicBlock + const std::vector* predecessors() const { + return &predecessors_; + } + + /// Returns the predecessors of the BasicBlock + std::vector* predecessors() { return &predecessors_; } + + /// Returns the successors of the BasicBlock + const std::vector* successors() const { return &successors_; } + + /// Returns the successors of the BasicBlock + std::vector* successors() { return &successors_; } + + /// Returns the structural successors of the BasicBlock + std::vector* structural_predecessors() { + return &structural_predecessors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_predecessors() const { + return &structural_predecessors_; + } + + /// Returns the structural successors of the BasicBlock + std::vector* structural_successors() { + return &structural_successors_; + } + + /// Returns the structural predecessors of the BasicBlock + const std::vector* structural_successors() const { + return &structural_successors_; + } + + /// Returns true if the block is reachable in the CFG. + bool reachable() const { return reachable_; } + + /// Returns true if the block is structurally reachable in the CFG. + bool structurally_reachable() const { return structurally_reachable_; } + + /// Returns true if BasicBlock is of the given type + bool is_type(BlockType type) const { + if (type == kBlockTypeUndefined) return type_.none(); + return type_.test(type); + } + + /// Sets the reachability of the basic block in the CFG + void set_reachable(bool reachability) { reachable_ = reachability; } + + /// Sets the structural reachability of the basic block in the CFG + void set_structurally_reachable(bool reachability) { + structurally_reachable_ = reachability; + } + + /// Sets the type of the BasicBlock + void set_type(BlockType type) { + if (type == kBlockTypeUndefined) + type_.reset(); + else + type_.set(type); + } + + /// Sets the immediate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateDominator(BasicBlock* dom_block); + + /// Sets the immediate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateStructuralDominator(BasicBlock* dom_block); + + /// Sets the immediate post dominator of this basic block + /// + /// @param[in] pdom_block The post dominator block + void SetImmediateStructuralPostDominator(BasicBlock* pdom_block); + + /// Returns the immediate dominator of this basic block + BasicBlock* immediate_dominator(); + + /// Returns the immediate dominator of this basic block + const BasicBlock* immediate_dominator() const; + + /// Returns the immediate dominator of this basic block + BasicBlock* immediate_structural_dominator(); + + /// Returns the immediate dominator of this basic block + const BasicBlock* immediate_structural_dominator() const; + + /// Returns the immediate post dominator of this basic block + BasicBlock* immediate_structural_post_dominator(); + + /// Returns the immediate post dominator of this basic block + const BasicBlock* immediate_structural_post_dominator() const; + + /// Returns the label instruction for the block, or nullptr if not set. + const Instruction* label() const { return label_; } + + //// Registers the label instruction for the block. + void set_label(const Instruction* t) { label_ = t; } + + /// Registers the terminator instruction for the block. + void set_terminator(const Instruction* t) { terminator_ = t; } + + /// Returns the terminator instruction for the block. + const Instruction* terminator() const { return terminator_; } + + /// Adds @p next BasicBlocks as successors of this BasicBlock + void RegisterSuccessors( + const std::vector& next = std::vector()); + + /// Returns true if the id of the BasicBlock matches + bool operator==(const BasicBlock& other) const { return other.id_ == id_; } + + /// Returns true if the id of the BasicBlock matches + bool operator==(const uint32_t& other_id) const { return other_id == id_; } + + /// Returns true if this block dominates the other block. + /// Assumes dominators have been computed. + bool dominates(const BasicBlock& other) const; + + /// Returns true if this block structurally dominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_dominates(const BasicBlock& other) const; + + /// Returns true if this block structurally postdominates the other block. + /// Assumes structural dominators have been computed. + bool structurally_postdominates(const BasicBlock& other) const; + + void RegisterStructuralSuccessor(BasicBlock* block) { + block->structural_predecessors_.push_back(this); + structural_successors_.push_back(block); + } + + /// @brief A BasicBlock dominator iterator class + /// + /// This iterator will iterate over the (post)dominators of the block + class DominatorIterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = BasicBlock*; + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; + + /// @brief Constructs the end of dominator iterator + /// + /// This will create an iterator which will represent the element + /// before the root node of the dominator tree + DominatorIterator(); + + /// @brief Constructs an iterator for the given block which points to + /// @p block + /// + /// @param block The block which is referenced by the iterator + /// @param dominator_func This function will be called to get the immediate + /// (post)dominator of the current block + DominatorIterator( + const BasicBlock* block, + std::function dominator_func); + + /// @brief Advances the iterator + DominatorIterator& operator++(); + + /// @brief Returns the current element + const BasicBlock*& operator*(); + + friend bool operator==(const DominatorIterator& lhs, + const DominatorIterator& rhs); + + private: + const BasicBlock* current_; + std::function dom_func_; + }; + + /// Returns a dominator iterator which points to the current block + const DominatorIterator dom_begin() const; + + /// Returns a dominator iterator which points to the current block + DominatorIterator dom_begin(); + + /// Returns a dominator iterator which points to one element past the first + /// block + const DominatorIterator dom_end() const; + + /// Returns a dominator iterator which points to one element past the first + /// block + DominatorIterator dom_end(); + + /// Returns a dominator iterator which points to the current block + const DominatorIterator structural_dom_begin() const; + + /// Returns a dominator iterator which points to the current block + DominatorIterator structural_dom_begin(); + + /// Returns a dominator iterator which points to one element past the first + /// block + const DominatorIterator structural_dom_end() const; + + /// Returns a dominator iterator which points to one element past the first + /// block + DominatorIterator structural_dom_end(); + + /// Returns a post dominator iterator which points to the current block + const DominatorIterator structural_pdom_begin() const; + /// Returns a post dominator iterator which points to the current block + DominatorIterator structural_pdom_begin(); + + /// Returns a post dominator iterator which points to one element past the + /// last block + const DominatorIterator structural_pdom_end() const; + + /// Returns a post dominator iterator which points to one element past the + /// last block + DominatorIterator structural_pdom_end(); + + private: + /// Id of the BasicBlock + const uint32_t id_; + + /// Pointer to the immediate dominator of the BasicBlock + BasicBlock* immediate_dominator_; + + /// Pointer to the immediate structural dominator of the BasicBlock + BasicBlock* immediate_structural_dominator_; + + /// Pointer to the immediate structural post dominator of the BasicBlock + BasicBlock* immediate_structural_post_dominator_; + + /// The set of predecessors of the BasicBlock + std::vector predecessors_; + + /// The set of successors of the BasicBlock + std::vector successors_; + + /// The type of the block + std::bitset type_; + + /// True if the block is reachable in the CFG + bool reachable_; + + /// True if the block is structurally reachable in the CFG + bool structurally_reachable_; + + /// label of this block, if any. + const Instruction* label_; + + /// Terminator of this block. + const Instruction* terminator_; + + std::vector structural_predecessors_; + std::vector structural_successors_; +}; + +/// @brief Returns true if the iterators point to the same element or if both +/// iterators point to the @p dom_end block +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +/// @brief Returns true if the iterators point to different elements and they +/// do not both point to the @p dom_end block +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_BASIC_BLOCK_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/construct.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/construct.cpp new file mode 100644 index 00000000..10af155d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/construct.cpp @@ -0,0 +1,222 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/construct.h" + +#include +#include + +#include "source/val/function.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +Construct::Construct(ConstructType construct_type, BasicBlock* entry, + BasicBlock* exit, std::vector constructs) + : type_(construct_type), + corresponding_constructs_(constructs), + entry_block_(entry), + exit_block_(exit) {} + +ConstructType Construct::type() const { return type_; } + +const std::vector& Construct::corresponding_constructs() const { + return corresponding_constructs_; +} +std::vector& Construct::corresponding_constructs() { + return corresponding_constructs_; +} + +bool ValidateConstructSize(ConstructType type, size_t size) { + switch (type) { + case ConstructType::kSelection: + return size == 0; + case ConstructType::kContinue: + return size == 1; + case ConstructType::kLoop: + return size == 1; + case ConstructType::kCase: + return size >= 1; + default: + assert(1 == 0 && "Type not defined"); + } + return false; +} + +void Construct::set_corresponding_constructs( + std::vector constructs) { + assert(ValidateConstructSize(type_, constructs.size())); + corresponding_constructs_ = constructs; +} + +const BasicBlock* Construct::entry_block() const { return entry_block_; } +BasicBlock* Construct::entry_block() { return entry_block_; } + +const BasicBlock* Construct::exit_block() const { return exit_block_; } +BasicBlock* Construct::exit_block() { return exit_block_; } + +void Construct::set_exit(BasicBlock* block) { exit_block_ = block; } + +Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const { + const auto header = entry_block(); + const auto exit = exit_block(); + const bool is_continue = type() == ConstructType::kContinue; + const bool is_loop = type() == ConstructType::kLoop; + const BasicBlock* continue_header = nullptr; + if (is_loop) { + // The only corresponding construct for a loop is the continue. + continue_header = (*corresponding_constructs().begin())->entry_block(); + } + std::vector stack; + stack.push_back(const_cast(header)); + ConstructBlockSet construct_blocks; + while (!stack.empty()) { + auto* block = stack.back(); + stack.pop_back(); + + if (header->structurally_dominates(*block)) { + bool include = false; + if (is_continue && exit->structurally_postdominates(*block)) { + // Continue construct include blocks dominated by the continue target + // and post-dominated by the back-edge block. + include = true; + } else if (!exit->structurally_dominates(*block)) { + // Selection and loop constructs include blocks dominated by the header + // and not dominated by the merge. + include = true; + if (is_loop && continue_header->structurally_dominates(*block)) { + // Loop constructs have an additional constraint that they do not + // include blocks dominated by the continue construct. Since all + // blocks in the continue construct are dominated by the continue + // target, we just test for dominance by continue target. + include = false; + } + } + if (include) { + if (!construct_blocks.insert(block).second) continue; + + for (auto succ : *block->structural_successors()) { + stack.push_back(succ); + } + } + } + } + + return construct_blocks; +} + +bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { + // Structured Exits: + // - Selection: + // - branch to its merge + // - branch to nearest enclosing loop merge or continue + // - branch to nearest enclosing switch selection merge + // - Loop: + // - branch to its merge + // - branch to its continue + // - Continue: + // - branch to loop header + // - branch to loop merge + // + // Note: we will never see a case construct here. + assert(type() != ConstructType::kCase); + if (type() == ConstructType::kLoop) { + auto header = entry_block(); + auto terminator = header->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + auto merge_block_id = merge_inst->GetOperandAs(0u); + auto continue_block_id = merge_inst->GetOperandAs(1u); + if (dest->id() == merge_block_id || dest->id() == continue_block_id) { + return true; + } + } else if (type() == ConstructType::kContinue) { + auto loop_construct = corresponding_constructs()[0]; + auto header = loop_construct->entry_block(); + auto terminator = header->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + auto merge_block_id = merge_inst->GetOperandAs(0u); + if (dest == header || dest->id() == merge_block_id) { + return true; + } + } else { + assert(type() == ConstructType::kSelection); + if (dest == exit_block()) { + return true; + } + + // The next block in the traversal is either: + // i. The header block that declares |block| as its merge block. + // ii. The immediate dominator of |block|. + auto NextBlock = [](const BasicBlock* block) -> const BasicBlock* { + for (auto& use : block->label()->uses()) { + if ((use.first->opcode() == spv::Op::OpLoopMerge || + use.first->opcode() == spv::Op::OpSelectionMerge) && + use.second == 1 && + use.first->block()->structurally_dominates(*block) && + // A header likely declared itself as its merge. + use.first->block() != block) { + return use.first->block(); + } + } + return block->immediate_structural_dominator(); + }; + + bool seen_switch = false; + auto header = entry_block(); + auto block = NextBlock(header); + while (block) { + auto terminator = block->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + if (merge_inst->opcode() == spv::Op::OpLoopMerge || + (header->terminator()->opcode() != spv::Op::OpSwitch && + merge_inst->opcode() == spv::Op::OpSelectionMerge && + terminator->opcode() == spv::Op::OpSwitch)) { + auto merge_target = merge_inst->GetOperandAs(0u); + auto merge_block = merge_inst->function()->GetBlock(merge_target).first; + if (merge_block->structurally_dominates(*header)) { + block = NextBlock(block); + continue; + } + + if ((!seen_switch || merge_inst->opcode() == spv::Op::OpLoopMerge) && + dest->id() == merge_target) { + return true; + } else if (merge_inst->opcode() == spv::Op::OpLoopMerge) { + auto continue_target = merge_inst->GetOperandAs(1u); + if (dest->id() == continue_target) { + return true; + } + } + + if (terminator->opcode() == spv::Op::OpSwitch) { + seen_switch = true; + } + + // Hit an enclosing loop and didn't break or continue. + if (merge_inst->opcode() == spv::Op::OpLoopMerge) return false; + } + + block = NextBlock(block); + } + } + + return false; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/construct.h b/engine/engine/3rdlibs/spirv-tools/source/val/construct.h new file mode 100644 index 00000000..9476760a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/construct.h @@ -0,0 +1,170 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_CONSTRUCT_H_ +#define SOURCE_VAL_CONSTRUCT_H_ + +#include +#include +#include + +#include "source/val/basic_block.h" + +namespace spvtools { +namespace val { +class ValidationState_t; + +/// Functor for ordering BasicBlocks. BasicBlock pointers must not be null. +struct less_than_id { + bool operator()(const BasicBlock* lhs, const BasicBlock* rhs) const { + return lhs->id() < rhs->id(); + } +}; + +enum class ConstructType : int { + kNone = 0, + /// The set of blocks dominated by a selection header, minus the set of blocks + /// dominated by the header's merge block + kSelection, + /// The set of blocks dominated by an OpLoopMerge's Continue Target and post + /// dominated by the corresponding back + kContinue, + /// The set of blocks dominated by a loop header, minus the set of blocks + /// dominated by the loop's merge block, minus the loop's corresponding + /// continue construct + kLoop, + /// The set of blocks dominated by an OpSwitch's Target or Default, minus the + /// set of blocks dominated by the OpSwitch's merge block (this construct is + /// only defined for those OpSwitch Target or Default that are not equal to + /// the OpSwitch's corresponding merge block) + kCase +}; + +class Function; + +/// @brief This class tracks the CFG constructs as defined in the SPIR-V spec +class Construct { + public: + Construct(ConstructType type, BasicBlock* dominator, + BasicBlock* exit = nullptr, + std::vector constructs = std::vector()); + + /// Returns the type of the construct + ConstructType type() const; + + const std::vector& corresponding_constructs() const; + std::vector& corresponding_constructs(); + void set_corresponding_constructs(std::vector constructs); + + /// Returns the dominator block of the construct. + /// + /// This is usually the header block or the first block of the construct. + const BasicBlock* entry_block() const; + + /// Returns the dominator block of the construct. + /// + /// This is usually the header block or the first block of the construct. + BasicBlock* entry_block(); + + /// Returns the exit block of the construct. + /// + /// For a continue construct it is the backedge block of the corresponding + /// loop construct. For the case construct it is the block that branches to + /// the OpSwitch merge block or other case blocks. Otherwise it is the merge + /// block of the corresponding header block + const BasicBlock* exit_block() const; + + /// Returns the exit block of the construct. + /// + /// For a continue construct it is the backedge block of the corresponding + /// loop construct. For the case construct it is the block that branches to + /// the OpSwitch merge block or other case blocks. Otherwise it is the merge + /// block of the corresponding header block + BasicBlock* exit_block(); + + /// Sets the exit block for this construct. This is useful for continue + /// constructs which do not know the back-edge block during construction + void set_exit(BasicBlock* exit_block); + + // Returns whether the exit block of this construct is the merge block + // for an OpLoopMerge or OpSelectionMerge + bool ExitBlockIsMergeBlock() const { + return type_ == ConstructType::kLoop || type_ == ConstructType::kSelection; + } + + using ConstructBlockSet = std::set; + + // Returns the basic blocks in this construct. This function should not + // be called before the exit block is set and dominators have been + // calculated. + ConstructBlockSet blocks(Function* function) const; + + // Returns true if |dest| is structured exit from the construct. Structured + // exits depend on the construct type. + // Selection: + // * branch to the associated merge + // * branch to the merge or continue of the innermost loop containing the + // selection + // * branch to the merge block of the innermost switch containing the + // selection + // Loop: + // * branch to the associated merge or continue + // Continue: + // * back-edge to the associated loop header + // * branch to the associated loop merge + // + // Note: the validator does not generate case constructs. Switches are + // checked separately from other constructs. + bool IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const; + + private: + /// The type of the construct + ConstructType type_; + + /// These are the constructs that are related to this construct. These + /// constructs can be the continue construct, for the corresponding loop + /// construct, the case construct that are part of the same OpSwitch + /// instruction + /// + /// Here is a table that describes what constructs are included in + /// @p corresponding_constructs_ + /// | this construct | corresponding construct | + /// |----------------|----------------------------------| + /// | loop | continue | + /// | continue | loop | + /// | case | other cases in the same OpSwitch | + /// + /// kContinue and kLoop constructs will always have corresponding + /// constructs even if they are represented by the same block + std::vector corresponding_constructs_; + + /// @brief Dominator block for the construct + /// + /// The dominator block for the construct. Depending on the construct this may + /// be a selection header, a continue target of a loop, a loop header or a + /// Target or Default block of a switch + BasicBlock* entry_block_; + + /// @brief Exiting block for the construct + /// + /// The exit block for the construct. This can be a merge block for the loop + /// and selection constructs, a back-edge block for a continue construct, or + /// the branching block for the case construct + BasicBlock* exit_block_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_CONSTRUCT_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/decoration.h b/engine/engine/3rdlibs/spirv-tools/source/val/decoration.h new file mode 100644 index 00000000..77e0f615 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/decoration.h @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_DECORATION_H_ +#define SOURCE_VAL_DECORATION_H_ + +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { +namespace val { + +// An object of this class represents a specific decoration including its +// parameters (if any). Decorations are used by OpDecorate and OpMemberDecorate, +// and they describe certain properties that can be assigned to one or several +// s. +// +// A Decoration object contains the decoration type (an enum), associated +// literal parameters, and struct member index. If the decoration does not apply +// to a struct member, then the index is kInvalidIndex. A Decoration object does +// not store the target Id, i.e. the Id to which it applies. It is +// possible for the same decoration to be applied to several s (and they +// might be assigned using separate SPIR-V instructions, possibly using an +// assignment through GroupDecorate). +// +// Example 1: Decoration for an object with no parameters: +// OpDecorate %obj Flat +// dec_type_ = spv::Decoration::Flat +// params_ = empty vector +// struct_member_index_ = kInvalidMember +// +// Example 2: Decoration for an object with two parameters: +// OpDecorate %obj LinkageAttributes "link" Import +// dec_type_ = spv::Decoration::LinkageAttributes +// params_ = vector { link, Import } +// struct_member_index_ = kInvalidMember +// +// Example 3: Decoration for a member of a structure with one parameter: +// OpMemberDecorate %struct 2 Offset 2 +// dec_type_ = spv::Decoration::Offset +// params_ = vector { 2 } +// struct_member_index_ = 2 +// +// Example 4: Decoration for a Builtin: +// OpDecorate %var BuiltIn FragDepth +// dec_type_ = spv::Decoration::BuiltIn +// params_ = vector { FragDepth } +// struct_member_index_ = kInvalidMember +// +class Decoration { + public: + enum { kInvalidMember = -1 }; + Decoration(spv::Decoration t, + const std::vector& parameters = std::vector(), + uint32_t member_index = kInvalidMember) + : dec_type_(t), params_(parameters), struct_member_index_(member_index) {} + + void set_struct_member_index(uint32_t index) { struct_member_index_ = index; } + int struct_member_index() const { return struct_member_index_; } + spv::Decoration dec_type() const { return dec_type_; } + std::vector& params() { return params_; } + const std::vector& params() const { return params_; } + spv::BuiltIn builtin() const { + assert(dec_type_ == spv::Decoration::BuiltIn); + return spv::BuiltIn(params_[0]); + } + + inline bool operator<(const Decoration& rhs) const { + // Note: Sort by struct_member_index_ first, then type, so look up can be + // efficient using lower_bound() and upper_bound(). + if (struct_member_index_ < rhs.struct_member_index_) return true; + if (rhs.struct_member_index_ < struct_member_index_) return false; + if (dec_type_ < rhs.dec_type_) return true; + if (rhs.dec_type_ < dec_type_) return false; + return params_ < rhs.params_; + } + inline bool operator==(const Decoration& rhs) const { + return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ && + struct_member_index_ == rhs.struct_member_index_); + } + + private: + spv::Decoration dec_type_; + std::vector params_; + + // If the decoration applies to a member of a structure type, then the index + // of the member is stored here. Otherwise, this is kInvalidIndex. + int struct_member_index_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_DECORATION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/function.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/function.cpp new file mode 100644 index 00000000..290574b8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/function.cpp @@ -0,0 +1,435 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/function.h" + +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +// Universal Limit of ResultID + 1 +static const uint32_t kInvalidId = 0x400000; + +Function::Function(uint32_t function_id, uint32_t result_type_id, + spv::FunctionControlMask function_control, + uint32_t function_type_id) + : id_(function_id), + function_type_id_(function_type_id), + result_type_id_(result_type_id), + function_control_(function_control), + declaration_type_(FunctionDecl::kFunctionDeclUnknown), + end_has_been_registered_(false), + blocks_(), + current_block_(nullptr), + pseudo_entry_block_(0), + pseudo_exit_block_(kInvalidId), + cfg_constructs_(), + variable_ids_(), + parameter_ids_() {} + +bool Function::IsFirstBlock(uint32_t block_id) const { + return !ordered_blocks_.empty() && *first_block() == block_id; +} + +spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id, + uint32_t type_id) { + assert(current_block_ == nullptr && + "RegisterFunctionParameter can only be called when parsing the binary " + "outside of a block"); + // TODO(umar): Validate function parameter type order and count + // TODO(umar): Use these variables to validate parameter type + (void)parameter_id; + (void)type_id; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, + uint32_t continue_id) { + RegisterBlock(merge_id, false); + RegisterBlock(continue_id, false); + BasicBlock& merge_block = blocks_.at(merge_id); + BasicBlock& continue_target_block = blocks_.at(continue_id); + assert(current_block_ && + "RegisterLoopMerge must be called when called within a block"); + current_block_->RegisterStructuralSuccessor(&merge_block); + current_block_->RegisterStructuralSuccessor(&continue_target_block); + + current_block_->set_type(kBlockTypeLoop); + merge_block.set_type(kBlockTypeMerge); + continue_target_block.set_type(kBlockTypeContinue); + Construct& loop_construct = + AddConstruct({ConstructType::kLoop, current_block_, &merge_block}); + Construct& continue_construct = + AddConstruct({ConstructType::kContinue, &continue_target_block}); + + continue_construct.set_corresponding_constructs({&loop_construct}); + loop_construct.set_corresponding_constructs({&continue_construct}); + merge_block_header_[&merge_block] = current_block_; + if (continue_target_headers_.find(&continue_target_block) == + continue_target_headers_.end()) { + continue_target_headers_[&continue_target_block] = {current_block_}; + } else { + continue_target_headers_[&continue_target_block].push_back(current_block_); + } + + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { + RegisterBlock(merge_id, false); + BasicBlock& merge_block = blocks_.at(merge_id); + current_block_->set_type(kBlockTypeSelection); + merge_block.set_type(kBlockTypeMerge); + merge_block_header_[&merge_block] = current_block_; + current_block_->RegisterStructuralSuccessor(&merge_block); + + AddConstruct({ConstructType::kSelection, current_block(), &merge_block}); + + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) { + assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown); + declaration_type_ = type; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { + assert( + declaration_type_ == FunctionDecl::kFunctionDeclDefinition && + "RegisterBlocks can only be called after declaration_type_ is defined"); + + std::unordered_map::iterator inserted_block; + bool success = false; + tie(inserted_block, success) = + blocks_.insert({block_id, BasicBlock(block_id)}); + if (is_definition) { // new block definition + assert(current_block_ == nullptr && + "Register Block can only be called when parsing a binary outside of " + "a BasicBlock"); + + undefined_blocks_.erase(block_id); + current_block_ = &inserted_block->second; + ordered_blocks_.push_back(current_block_); + } else if (success) { // Block doesn't exist but this is not a definition + undefined_blocks_.insert(block_id); + } + + return SPV_SUCCESS; +} + +void Function::RegisterBlockEnd(std::vector next_list) { + assert( + current_block_ && + "RegisterBlockEnd can only be called when parsing a binary in a block"); + std::vector next_blocks; + next_blocks.reserve(next_list.size()); + + std::unordered_map::iterator inserted_block; + bool success; + for (uint32_t successor_id : next_list) { + tie(inserted_block, success) = + blocks_.insert({successor_id, BasicBlock(successor_id)}); + if (success) { + undefined_blocks_.insert(successor_id); + } + next_blocks.push_back(&inserted_block->second); + } + + if (current_block_->is_type(kBlockTypeLoop)) { + // For each loop header, record the set of its successors, and include + // its continue target if the continue target is not the loop header + // itself. + std::vector& next_blocks_plus_continue_target = + loop_header_successors_plus_continue_target_map_[current_block_]; + next_blocks_plus_continue_target = next_blocks; + auto continue_target = + FindConstructForEntryBlock(current_block_, ConstructType::kLoop) + .corresponding_constructs() + .back() + ->entry_block(); + if (continue_target != current_block_) { + next_blocks_plus_continue_target.push_back(continue_target); + } + } + + current_block_->RegisterSuccessors(next_blocks); + current_block_ = nullptr; + return; +} + +void Function::RegisterFunctionEnd() { + if (!end_has_been_registered_) { + end_has_been_registered_ = true; + + ComputeAugmentedCFG(); + } +} + +size_t Function::block_count() const { return blocks_.size(); } + +size_t Function::undefined_block_count() const { + return undefined_blocks_.size(); +} + +const std::vector& Function::ordered_blocks() const { + return ordered_blocks_; +} +std::vector& Function::ordered_blocks() { return ordered_blocks_; } + +const BasicBlock* Function::current_block() const { return current_block_; } +BasicBlock* Function::current_block() { return current_block_; } + +const std::list& Function::constructs() const { + return cfg_constructs_; +} +std::list& Function::constructs() { return cfg_constructs_; } + +const BasicBlock* Function::first_block() const { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} +BasicBlock* Function::first_block() { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} + +bool Function::IsBlockType(uint32_t merge_block_id, BlockType type) const { + bool ret = false; + const BasicBlock* block; + std::tie(block, std::ignore) = GetBlock(merge_block_id); + if (block) { + ret = block->is_type(type); + } + return ret; +} + +std::pair Function::GetBlock(uint32_t block_id) const { + const auto b = blocks_.find(block_id); + if (b != end(blocks_)) { + const BasicBlock* block = &(b->second); + bool defined = + undefined_blocks_.find(block->id()) == std::end(undefined_blocks_); + return std::make_pair(block, defined); + } else { + return std::make_pair(nullptr, false); + } +} + +std::pair Function::GetBlock(uint32_t block_id) { + const BasicBlock* out; + bool defined; + std::tie(out, defined) = + const_cast(this)->GetBlock(block_id); + return std::make_pair(const_cast(out), defined); +} + +Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_successors_map_.find(block); + return where == augmented_successors_map_.end() ? block->successors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_predecessors_map_.find(block); + return where == augmented_predecessors_map_.end() ? block->predecessors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction() + const { + return [this](const BasicBlock* block) { + auto where = augmented_successors_map_.find(block); + return where == augmented_successors_map_.end() + ? block->structural_successors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction +Function::AugmentedStructuralCFGPredecessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_predecessors_map_.find(block); + return where == augmented_predecessors_map_.end() + ? block->structural_predecessors() + : &(*where).second; + }; +} + +void Function::ComputeAugmentedCFG() { + // Compute the successors of the pseudo-entry block, and + // the predecessors of the pseudo exit block. + auto succ_func = [](const BasicBlock* b) { + return b->structural_successors(); + }; + auto pred_func = [](const BasicBlock* b) { + return b->structural_predecessors(); + }; + CFA::ComputeAugmentedCFG( + ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_, + &augmented_successors_map_, &augmented_predecessors_map_, succ_func, + pred_func); +} + +Construct& Function::AddConstruct(const Construct& new_construct) { + cfg_constructs_.push_back(new_construct); + auto& result = cfg_constructs_.back(); + entry_block_to_construct_[std::make_pair(new_construct.entry_block(), + new_construct.type())] = &result; + return result; +} + +Construct& Function::FindConstructForEntryBlock(const BasicBlock* entry_block, + ConstructType type) { + auto where = + entry_block_to_construct_.find(std::make_pair(entry_block, type)); + assert(where != entry_block_to_construct_.end()); + auto construct_ptr = (*where).second; + assert(construct_ptr); + return *construct_ptr; +} + +int Function::GetBlockDepth(BasicBlock* bb) { + // Guard against nullptr. + if (!bb) { + return 0; + } + // Only calculate the depth if it's not already calculated. + // This function uses memoization to avoid duplicate CFG depth calculations. + if (block_depth_.find(bb) != block_depth_.end()) { + return block_depth_[bb]; + } + // Avoid recursion. Something is wrong if the same block is encountered + // multiple times. + block_depth_[bb] = 0; + + BasicBlock* bb_dom = bb->immediate_dominator(); + if (!bb_dom || bb == bb_dom) { + // This block has no dominator, so it's at depth 0. + block_depth_[bb] = 0; + } else if (bb->is_type(kBlockTypeContinue)) { + // This rule must precede the rule for merge blocks in order to set up + // depths correctly. If a block is both a merge and continue then the merge + // is nested within the continue's loop (or the graph is incorrect). + // The depth of the continue block entry point is 1 + loop header depth. + Construct* continue_construct = + entry_block_to_construct_[std::make_pair(bb, ConstructType::kContinue)]; + assert(continue_construct); + // Continue construct has only 1 corresponding construct (loop header). + Construct* loop_construct = + continue_construct->corresponding_constructs()[0]; + assert(loop_construct); + BasicBlock* loop_header = loop_construct->entry_block(); + // The continue target may be the loop itself (while 1). + // In such cases, the depth of the continue block is: 1 + depth of the + // loop's dominator block. + if (loop_header == bb) { + block_depth_[bb] = 1 + GetBlockDepth(bb_dom); + } else { + block_depth_[bb] = 1 + GetBlockDepth(loop_header); + } + } else if (bb->is_type(kBlockTypeMerge)) { + // If this is a merge block, its depth is equal to the block before + // branching. + BasicBlock* header = merge_block_header_[bb]; + assert(header); + block_depth_[bb] = GetBlockDepth(header); + } else if (bb_dom->is_type(kBlockTypeSelection) || + bb_dom->is_type(kBlockTypeLoop)) { + // The dominator of the given block is a header block. So, the nesting + // depth of this block is: 1 + nesting depth of the header. + block_depth_[bb] = 1 + GetBlockDepth(bb_dom); + } else { + block_depth_[bb] = GetBlockDepth(bb_dom); + } + return block_depth_[bb]; +} + +void Function::RegisterExecutionModelLimitation(spv::ExecutionModel model, + const std::string& message) { + execution_model_limitations_.push_back( + [model, message](spv::ExecutionModel in_model, std::string* out_message) { + if (model != in_model) { + if (out_message) { + *out_message = message; + } + return false; + } + return true; + }); +} + +bool Function::IsCompatibleWithExecutionModel(spv::ExecutionModel model, + std::string* reason) const { + bool return_value = true; + std::stringstream ss_reason; + + for (const auto& is_compatible : execution_model_limitations_) { + std::string message; + if (!is_compatible(model, &message)) { + if (!reason) return false; + return_value = false; + if (!message.empty()) { + ss_reason << message << "\n"; + } + } + } + + if (!return_value && reason) { + *reason = ss_reason.str(); + } + + return return_value; +} + +bool Function::CheckLimitations(const ValidationState_t& _, + const Function* entry_point, + std::string* reason) const { + bool return_value = true; + std::stringstream ss_reason; + + for (const auto& is_compatible : limitations_) { + std::string message; + if (!is_compatible(_, entry_point, &message)) { + if (!reason) return false; + return_value = false; + if (!message.empty()) { + ss_reason << message << "\n"; + } + } + } + + if (!return_value && reason) { + *reason = ss_reason.str(); + } + + return return_value; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/function.h b/engine/engine/3rdlibs/spirv-tools/source/val/function.h new file mode 100644 index 00000000..d3dd6396 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/function.h @@ -0,0 +1,403 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_FUNCTION_H_ +#define SOURCE_VAL_FUNCTION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +struct bb_constr_type_pair_hash { + std::size_t operator()( + const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash::type>{}( + static_cast::type>(p.second)); + return (h1 ^ h2); + } +}; + +enum class FunctionDecl { + kFunctionDeclUnknown, /// < Unknown function declaration + kFunctionDeclDeclaration, /// < Function declaration + kFunctionDeclDefinition /// < Function definition +}; + +/// This class manages all function declaration and definitions in a module. It +/// handles the state and id information while parsing a function in the SPIR-V +/// binary. +class Function { + public: + Function(uint32_t id, uint32_t result_type_id, + spv::FunctionControlMask function_control, + uint32_t function_type_id); + + /// Registers a function parameter in the current function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id); + + /// Sets the declaration type of the current function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterSetFunctionDeclType(FunctionDecl type); + + /// Registers a block in the current function. Subsequent block instructions + /// will target this block + /// @param id The ID of the label of the block + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlock(uint32_t id, bool is_definition = true); + + /// Registers a variable in the current block + /// + /// @param[in] type_id The type ID of the variable + /// @param[in] id The ID of the variable + /// @param[in] storage The storage of the variable + /// @param[in] init_id The initializer ID of the variable + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id, + spv::StorageClass storage, + uint32_t init_id); + + /// Registers a loop merge construct in the function + /// + /// @param[in] merge_id The merge block ID of the loop + /// @param[in] continue_id The continue block ID of the loop + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id); + + /// Registers a selection merge construct in the function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterSelectionMerge(uint32_t merge_id); + + /// Registers the end of the block + /// + /// @param[in] successors_list A list of ids to the block's successors + void RegisterBlockEnd(std::vector successors_list); + + /// Registers the end of the function. This is idempotent. + void RegisterFunctionEnd(); + + /// Returns true if the \p id block is the first block of this function + bool IsFirstBlock(uint32_t id) const; + + /// Returns true if the \p merge_block_id is a BlockType of \p type + bool IsBlockType(uint32_t merge_block_id, BlockType type) const; + + /// Returns a pair consisting of the BasicBlock with \p id and a bool + /// which is true if the block has been defined, and false if it is + /// declared but not defined. This function will return nullptr if the + /// \p id was not declared and not defined at the current point in the binary + std::pair GetBlock(uint32_t id) const; + std::pair GetBlock(uint32_t id); + + /// Returns the first block of the current function + const BasicBlock* first_block() const; + + /// Returns the first block of the current function + BasicBlock* first_block(); + + /// Returns a vector of all the blocks in the function + const std::vector& ordered_blocks() const; + + /// Returns a vector of all the blocks in the function + std::vector& ordered_blocks(); + + /// Returns a list of all the cfg constructs in the function + const std::list& constructs() const; + + /// Returns a list of all the cfg constructs in the function + std::list& constructs(); + + /// Returns the number of blocks in the current function being parsed + size_t block_count() const; + + /// Returns the id of the function + uint32_t id() const { return id_; } + + /// Returns return type id of the function + uint32_t GetResultTypeId() const { return result_type_id_; } + + /// Returns the number of blocks in the current function being parsed + size_t undefined_block_count() const; + const std::unordered_set& undefined_blocks() const { + return undefined_blocks_; + } + + /// Returns the block that is currently being parsed in the binary + BasicBlock* current_block(); + + /// Returns the block that is currently being parsed in the binary + const BasicBlock* current_block() const; + + // For dominance calculations, we want to analyze all the + // blocks in the function, even in degenerate control flow cases + // including unreachable blocks. We therefore make an "augmented CFG" + // which is the same as the ordinary CFG but adds: + // - A pseudo-entry node. + // - A pseudo-exit node. + // - A minimal set of edges so that a forward traversal from the + // pseudo-entry node will visit all nodes. + // - A minimal set of edges so that a backward traversal from the + // pseudo-exit node will visit all nodes. + // In particular, the pseudo-entry node is the unique source of the + // augmented CFG, and the psueo-exit node is the unique sink of the + // augmented CFG. + + /// Returns the pseudo exit block + BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } + + /// Returns the pseudo exit block + const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } + + /// Returns the pseudo exit block + BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } + + /// Returns the pseudo exit block + const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; } + + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + /// Returns the block successors function for the augmented CFG. + GetBlocksFunction AugmentedCFGSuccessorsFunction() const; + /// Returns the block predecessors function for the augmented CFG. + GetBlocksFunction AugmentedCFGPredecessorsFunction() const; + /// Returns the block structural successors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const; + /// Returns the block structural predecessors function for the augmented CFG. + GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const; + + /// Returns the control flow nesting depth of the given basic block. + /// This function only works when you have structured control flow. + /// This function should only be called after the control flow constructs have + /// been identified and dominators have been computed. + int GetBlockDepth(BasicBlock* bb); + + /// Prints a GraphViz digraph of the CFG of the current function + void PrintDotGraph() const; + + /// Prints a directed graph of the CFG of the current function + void PrintBlocks() const; + + /// Registers execution model limitation such as "Feature X is only available + /// with Execution Model Y". + void RegisterExecutionModelLimitation(spv::ExecutionModel model, + const std::string& message); + + /// Registers execution model limitation with an |is_compatible| functor. + void RegisterExecutionModelLimitation( + std::function is_compatible) { + execution_model_limitations_.push_back(is_compatible); + } + + /// Registers limitation with an |is_compatible| functor. + void RegisterLimitation(std::function + is_compatible) { + limitations_.push_back(is_compatible); + } + + bool CheckLimitations(const ValidationState_t& _, const Function* entry_point, + std::string* reason) const; + + /// Returns true if the given execution model passes the limitations stored in + /// execution_model_limitations_. Returns false otherwise and fills optional + /// |reason| parameter. + bool IsCompatibleWithExecutionModel(spv::ExecutionModel model, + std::string* reason = nullptr) const; + + // Inserts id to the set of functions called from this function. + void AddFunctionCallTarget(uint32_t call_target_id) { + function_call_targets_.insert(call_target_id); + } + + // Returns a set with ids of all functions called from this function. + const std::set function_call_targets() const { + return function_call_targets_; + } + + // Returns the block containing the OpSelectionMerge or OpLoopMerge that + // references |merge_block|. + // Values of |merge_block_header_| inserted by CFGPass, so do not call before + // the first iteration of ordered instructions in + // ValidateBinaryUsingContextAndValidationState has completed. + BasicBlock* GetMergeHeader(BasicBlock* merge_block) { + return merge_block_header_[merge_block]; + } + + // Returns vector of the blocks containing a OpLoopMerge that references + // |continue_target|. + // Values of |continue_target_headers_| inserted by CFGPass, so do not call + // before the first iteration of ordered instructions in + // ValidateBinaryUsingContextAndValidationState has completed. + std::vector GetContinueHeaders(BasicBlock* continue_target) { + if (continue_target_headers_.find(continue_target) == + continue_target_headers_.end()) { + return {}; + } + return continue_target_headers_[continue_target]; + } + + private: + // Computes the representation of the augmented CFG. + // Populates augmented_successors_map_ and augmented_predecessors_map_. + void ComputeAugmentedCFG(); + + // Adds a copy of the given Construct, and tracks it by its entry block. + // Returns a reference to the stored construct. + Construct& AddConstruct(const Construct& new_construct); + + // Returns a reference to the construct corresponding to the given entry + // block. + Construct& FindConstructForEntryBlock(const BasicBlock* entry_block, + ConstructType t); + + /// The result id of OpFunction + uint32_t id_; + + /// The type of the function + uint32_t function_type_id_; + + /// The type of the return value + uint32_t result_type_id_; + + /// The control fo the function + spv::FunctionControlMask function_control_; + + /// The type of declaration of each function + FunctionDecl declaration_type_; + + // Have we finished parsing this function? + bool end_has_been_registered_; + + /// The blocks in the function mapped by block ID + std::unordered_map blocks_; + + /// A list of blocks in the order they appeared in the binary + std::vector ordered_blocks_; + + /// Blocks which are forward referenced by blocks but not defined + std::unordered_set undefined_blocks_; + + /// The block that is currently being parsed + BasicBlock* current_block_; + + /// A pseudo entry node used in dominance analysis. + /// After the function end has been registered, the successor list of the + /// pseudo entry node is the minimal set of nodes such that all nodes in the + /// CFG can be reached by following successor lists. That is, the successors + /// will be: + /// - Any basic block without predecessors. This includes the entry + /// block to the function. + /// - A single node from each otherwise unreachable cycle in the CFG, if + /// such cycles exist. + /// The pseudo entry node does not appear in the predecessor or successor + /// list of any ordinary block. + /// It has no predecessors. + /// It has Id 0. + BasicBlock pseudo_entry_block_; + + /// A pseudo exit block used in dominance analysis. + /// After the function end has been registered, the predecessor list of the + /// pseudo exit node is the minimal set of nodes such that all nodes in the + /// CFG can be reached by following predecessor lists. That is, the + /// predecessors will be: + /// - Any basic block without successors. This includes any basic block + /// ending with an OpReturn, OpReturnValue or similar instructions. + /// - A single node from each otherwise unreachable cycle in the CFG, if + /// such cycles exist. + /// The pseudo exit node does not appear in the predecessor or successor + /// list of any ordinary block. + /// It has no successors. + BasicBlock pseudo_exit_block_; + + // Maps a block to its successors in the augmented CFG, if that set is + // different from its successors in the ordinary CFG. + std::unordered_map> + augmented_successors_map_; + // Maps a block to its predecessors in the augmented CFG, if that set is + // different from its predecessors in the ordinary CFG. + std::unordered_map> + augmented_predecessors_map_; + + // Maps a structured loop header to its CFG successors and also its + // continue target if that continue target is not the loop header + // itself. This might have duplicates. + std::unordered_map> + loop_header_successors_plus_continue_target_map_; + + /// The constructs that are available in this function + std::list cfg_constructs_; + + /// The variable IDs of the functions + std::vector variable_ids_; + + /// The function parameter ids of the functions + std::vector parameter_ids_; + + /// Maps a construct's entry block to the construct(s). + /// Since a basic block may be the entry block of different types of + /// constructs, the type of the construct should also be specified in order to + /// get the unique construct. + std::unordered_map, Construct*, + bb_constr_type_pair_hash> + entry_block_to_construct_; + + /// This map provides the header block for a given merge block. + std::unordered_map merge_block_header_; + + /// This map provides the header blocks for a given continue target. + std::unordered_map> + continue_target_headers_; + + /// Stores the control flow nesting depth of a given basic block + std::unordered_map block_depth_; + + /// Stores execution model limitations imposed by instructions used within the + /// function. The functor stored in the list return true if execution model + /// is compatible, false otherwise. If the functor returns false, it can also + /// optionally fill the string parameter with the reason for incompatibility. + std::list> + execution_model_limitations_; + + /// Stores limitations imposed by instructions used within the function. + /// Similar to execution_model_limitations_; + std::list> + limitations_; + + /// Stores ids of all functions called from this function. + std::set function_call_targets_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_FUNCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/instruction.cpp new file mode 100644 index 00000000..f16fcd73 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/instruction.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/instruction.h" + +#include + +#include "source/binary.h" +#include "source/util/string_utils.h" + +namespace spvtools { +namespace val { + +Instruction::Instruction(const spv_parsed_instruction_t* inst) + : words_(inst->words, inst->words + inst->num_words), + operands_(inst->operands, inst->operands + inst->num_operands), + inst_({words_.data(), inst->num_words, inst->opcode, inst->ext_inst_type, + inst->type_id, inst->result_id, operands_.data(), + inst->num_operands}) {} + +void Instruction::RegisterUse(const Instruction* inst, uint32_t index) { + uses_.push_back(std::make_pair(inst, index)); +} + +bool operator<(const Instruction& lhs, const Instruction& rhs) { + return lhs.id() < rhs.id(); +} +bool operator<(const Instruction& lhs, uint32_t rhs) { return lhs.id() < rhs; } +bool operator==(const Instruction& lhs, const Instruction& rhs) { + return lhs.id() == rhs.id(); +} +bool operator==(const Instruction& lhs, uint32_t rhs) { + return lhs.id() == rhs; +} + +template <> +std::string Instruction::GetOperandAs(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.offset + o.num_words <= inst_.num_words); + return spvtools::utils::MakeString(words_.data() + o.offset, o.num_words); +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/instruction.h b/engine/engine/3rdlibs/spirv-tools/source/val/instruction.h new file mode 100644 index 00000000..59e8af13 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/instruction.h @@ -0,0 +1,156 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_INSTRUCTION_H_ +#define SOURCE_VAL_INSTRUCTION_H_ + +#include +#include +#include +#include +#include + +#include "source/ext_inst.h" +#include "source/opcode.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +class BasicBlock; +class Function; + +/// Wraps the spv_parsed_instruction struct along with use and definition of the +/// instruction's result id +class Instruction { + public: + explicit Instruction(const spv_parsed_instruction_t* inst); + + /// Registers the use of the Instruction in instruction \p inst at \p index + void RegisterUse(const Instruction* inst, uint32_t index); + + uint32_t id() const { return inst_.result_id; } + uint32_t type_id() const { return inst_.type_id; } + spv::Op opcode() const { return static_cast(inst_.opcode); } + + /// Returns the Function where the instruction was defined. nullptr if it was + /// defined outside of a Function + const Function* function() const { return function_; } + void set_function(Function* func) { function_ = func; } + + /// Returns the BasicBlock where the instruction was defined. nullptr if it + /// was defined outside of a BasicBlock + const BasicBlock* block() const { return block_; } + void set_block(BasicBlock* b) { block_ = b; } + + /// Returns a vector of pairs of all references to this instruction's result + /// id. The first element is the instruction in which this result id was + /// referenced and the second is the index of the word in that instruction + /// where this result id appeared + const std::vector>& uses() const { + return uses_; + } + + /// The word used to define the Instruction + uint32_t word(size_t index) const { return words_[index]; } + + /// The words used to define the Instruction + const std::vector& words() const { return words_; } + + /// Returns the operand at |idx|. + const spv_parsed_operand_t& operand(size_t idx) const { + return operands_[idx]; + } + + /// The operands of the Instruction + const std::vector& operands() const { + return operands_; + } + + /// Provides direct access to the stored C instruction object. + const spv_parsed_instruction_t& c_inst() const { return inst_; } + + /// Provides direct access to instructions spv_ext_inst_type_t object. + const spv_ext_inst_type_t& ext_inst_type() const { + return inst_.ext_inst_type; + } + + bool IsNonSemantic() const { + return spvIsExtendedInstruction(opcode()) && + spvExtInstIsNonSemantic(inst_.ext_inst_type); + } + + /// True if this is an OpExtInst for debug info extension. + bool IsDebugInfo() const { + return spvIsExtendedInstruction(opcode()) && + spvExtInstIsDebugInfo(inst_.ext_inst_type); + } + + // Casts the words belonging to the operand under |index| to |T| and returns. + template + T GetOperandAs(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.num_words * 4 >= sizeof(T)); + assert(o.offset + o.num_words <= inst_.num_words); + return *reinterpret_cast(&words_[o.offset]); + } + + size_t LineNum() const { return line_num_; } + void SetLineNum(size_t pos) { line_num_ = pos; } + + private: + const std::vector words_; + const std::vector operands_; + spv_parsed_instruction_t inst_; + size_t line_num_ = 0; + + /// The function in which this instruction was declared + Function* function_ = nullptr; + + /// The basic block in which this instruction was declared + BasicBlock* block_ = nullptr; + + /// This is a vector of pairs of all references to this instruction's result + /// id. The first element is the instruction in which this result id was + /// referenced and the second is the index of the word in the referencing + /// instruction where this instruction appeared + std::vector> uses_; +}; + +bool operator<(const Instruction& lhs, const Instruction& rhs); +bool operator<(const Instruction& lhs, uint32_t rhs); +bool operator==(const Instruction& lhs, const Instruction& rhs); +bool operator==(const Instruction& lhs, uint32_t rhs); + +template <> +std::string Instruction::GetOperandAs(size_t index) const; + +} // namespace val +} // namespace spvtools + +// custom specialization of std::hash for Instruction +namespace std { +template <> +struct hash { + typedef spvtools::val::Instruction argument_type; + typedef std::size_t result_type; + result_type operator()(const argument_type& inst) const { + return hash()(inst.id()); + } +}; + +} // namespace std + +#endif // SOURCE_VAL_INSTRUCTION_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate.cpp new file mode 100644 index 00000000..4c46d2b5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate.cpp @@ -0,0 +1,471 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include +#include +#include +#include +#include + +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/spirv_target_env.h" +#include "source/val/construct.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" + +namespace { +// TODO(issue 1950): The validator only returns a single message anyway, so no +// point in generating more than 1 warning. +static uint32_t kDefaultMaxNumOfWarnings = 1; +} // namespace + +namespace spvtools { +namespace val { +namespace { + +// Parses OpExtension instruction and registers extension. +void RegisterExtension(ValidationState_t& _, + const spv_parsed_instruction_t* inst) { + const std::string extension_str = spvtools::GetExtensionString(inst); + Extension extension; + if (!GetExtensionFromString(extension_str.c_str(), &extension)) { + // The error will be logged in the ProcessInstruction pass. + return; + } + + _.RegisterExtension(extension); +} + +// Parses the beginning of the module searching for OpExtension instructions. +// Registers extensions if recognized. Returns SPV_REQUESTED_TERMINATION +// once an instruction which is not spv::Op::OpCapability and +// spv::Op::OpExtension is encountered. According to the SPIR-V spec extensions +// are declared after capabilities and before everything else. +spv_result_t ProcessExtensions(void* user_data, + const spv_parsed_instruction_t* inst) { + const spv::Op opcode = static_cast(inst->opcode); + if (opcode == spv::Op::OpCapability) return SPV_SUCCESS; + + if (opcode == spv::Op::OpExtension) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + RegisterExtension(_, inst); + return SPV_SUCCESS; + } + + // OpExtension block is finished, requesting termination. + return SPV_REQUESTED_TERMINATION; +} + +spv_result_t ProcessInstruction(void* user_data, + const spv_parsed_instruction_t* inst) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + + auto* instruction = _.AddOrderedInstruction(inst); + _.RegisterDebugInstruction(instruction); + + return SPV_SUCCESS; +} + +spv_result_t ValidateForwardDecls(ValidationState_t& _) { + if (_.unresolved_forward_id_count() == 0) return SPV_SUCCESS; + + std::stringstream ss; + std::vector ids = _.UnresolvedForwardIds(); + + std::transform( + std::begin(ids), std::end(ids), + std::ostream_iterator(ss, " "), + bind(&ValidationState_t::getIdName, std::ref(_), std::placeholders::_1)); + + auto id_str = ss.str(); + return _.diag(SPV_ERROR_INVALID_ID, nullptr) + << "The following forward referenced IDs have not been defined:\n" + << id_str.substr(0, id_str.size() - 1); +} + +// Entry point validation. Based on 2.16.1 (Universal Validation Rules) of the +// SPIRV spec: +// * There is at least one OpEntryPoint instruction, unless the Linkage +// capability is being used. +// * No function can be targeted by both an OpEntryPoint instruction and an +// OpFunctionCall instruction. +// +// Additionally enforces that entry points for Vulkan should not have recursion. +spv_result_t ValidateEntryPoints(ValidationState_t& _) { + _.ComputeFunctionToEntryPointMapping(); + _.ComputeRecursiveEntryPoints(); + + if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage)) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "No OpEntryPoint instruction was found. This is only allowed if " + "the Linkage capability is being used."; + } + + for (const auto& entry_point : _.entry_points()) { + if (_.IsFunctionCallTarget(entry_point)) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point)) + << "A function (" << entry_point + << ") may not be targeted by both an OpEntryPoint instruction and " + "an OpFunctionCall instruction."; + } + + // For Vulkan, the static function-call graph for an entry point + // must not contain cycles. + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.recursive_entry_points().find(entry_point) != + _.recursive_entry_points().end()) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point)) + << _.VkErrorID(4634) + << "Entry points may not have a call graph with cycles."; + } + } + } + + if (auto error = ValidateFloatControls2(_)) { + return error; + } + if (auto error = ValidateDuplicateExecutionModes(_)) { + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBinaryUsingContextAndValidationState( + const spv_context_t& context, const uint32_t* words, const size_t num_words, + spv_diagnostic* pDiagnostic, ValidationState_t* vstate) { + auto binary = std::unique_ptr( + new spv_const_binary_t{words, num_words}); + + spv_endianness_t endian; + spv_position_t position = {}; + if (spvBinaryEndianness(binary.get(), &endian)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V magic number."; + } + + spv_header_t header; + if (spvBinaryHeaderGet(binary.get(), endian, &header)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V header."; + } + + if (header.version > spvVersionForTargetEnv(context.target_env)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_WRONG_VERSION) + << "Invalid SPIR-V binary version " + << SPV_SPIRV_VERSION_MAJOR_PART(header.version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(header.version) + << " for target environment " + << spvTargetEnvDescription(context.target_env) << "."; + } + + if (header.bound > vstate->options()->universal_limits_.max_id_bound) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V. The id bound is larger than the max id bound " + << vstate->options()->universal_limits_.max_id_bound << "."; + } + + // Look for OpExtension instructions and register extensions. + // This parse should not produce any error messages. Hijack the context and + // replace the message consumer so that we do not pollute any state in input + // consumer. + spv_context_t hijacked_context = context; + hijacked_context.consumer = [](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}; + spvBinaryParse(&hijacked_context, vstate, words, num_words, + /* parsed_header = */ nullptr, ProcessExtensions, + /* diagnostic = */ nullptr); + + // Parse the module and perform inline validation checks. These checks do + // not require the knowledge of the whole module. + if (auto error = spvBinaryParse(&context, vstate, words, num_words, + /*parsed_header =*/nullptr, + ProcessInstruction, pDiagnostic)) { + return error; + } + + bool has_mask_task_nv = false; + bool has_mask_task_ext = false; + std::vector visited_entry_points; + for (auto& instruction : vstate->ordered_instructions()) { + { + // In order to do this work outside of Process Instruction we need to be + // able to, briefly, de-const the instruction. + Instruction* inst = const_cast(&instruction); + + if (inst->opcode() == spv::Op::OpEntryPoint) { + const auto entry_point = inst->GetOperandAs(1); + const auto execution_model = inst->GetOperandAs(0); + const std::string desc_name = inst->GetOperandAs(2); + + ValidationState_t::EntryPointDescription desc; + desc.name = desc_name; + + std::vector interfaces; + for (size_t j = 3; j < inst->operands().size(); ++j) + desc.interfaces.push_back(inst->word(inst->operand(j).offset)); + + vstate->RegisterEntryPoint(entry_point, execution_model, + std::move(desc)); + + if (visited_entry_points.size() > 0) { + for (const Instruction* check_inst : visited_entry_points) { + const auto check_execution_model = + check_inst->GetOperandAs(0); + const std::string check_name = + check_inst->GetOperandAs(2); + + if (desc_name == check_name && + execution_model == check_execution_model) { + return vstate->diag(SPV_ERROR_INVALID_DATA, inst) + << "2 Entry points cannot share the same name and " + "ExecutionMode."; + } + } + } + visited_entry_points.push_back(inst); + + has_mask_task_nv |= (execution_model == spv::ExecutionModel::TaskNV || + execution_model == spv::ExecutionModel::MeshNV); + has_mask_task_ext |= (execution_model == spv::ExecutionModel::TaskEXT || + execution_model == spv::ExecutionModel::MeshEXT); + } + if (inst->opcode() == spv::Op::OpFunctionCall) { + if (!vstate->in_function_body()) { + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction) + << "A FunctionCall must happen within a function body."; + } + + const auto called_id = inst->GetOperandAs(2); + vstate->AddFunctionCallTarget(called_id); + } + + if (vstate->in_function_body()) { + inst->set_function(&(vstate->current_function())); + inst->set_block(vstate->current_function().current_block()); + + if (vstate->in_block() && spvOpcodeIsBlockTerminator(inst->opcode())) { + vstate->current_function().current_block()->set_terminator(inst); + } + } + + if (auto error = IdPass(*vstate, inst)) return error; + } + + if (auto error = CapabilityPass(*vstate, &instruction)) return error; + if (auto error = ModuleLayoutPass(*vstate, &instruction)) return error; + if (auto error = CfgPass(*vstate, &instruction)) return error; + if (auto error = InstructionPass(*vstate, &instruction)) return error; + + // Now that all of the checks are done, update the state. + { + Instruction* inst = const_cast(&instruction); + vstate->RegisterInstruction(inst); + if (inst->opcode() == spv::Op::OpTypeForwardPointer) { + vstate->RegisterForwardPointer(inst->GetOperandAs(0)); + } + } + } + + if (!vstate->has_memory_model_specified()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing required OpMemoryModel instruction."; + + if (vstate->in_function_body()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing OpFunctionEnd at end of module."; + + if (vstate->HasCapability(spv::Capability::BindlessTextureNV) && + !vstate->has_samplerimage_variable_address_mode_specified()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing required OpSamplerImageAddressingModeNV instruction."; + + if (has_mask_task_ext && has_mask_task_nv) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << vstate->VkErrorID(7102) + << "Module can't mix MeshEXT/TaskEXT with MeshNV/TaskNV Execution " + "Model."; + + // Catch undefined forward references before performing further checks. + if (auto error = ValidateForwardDecls(*vstate)) return error; + + // Calculate reachability after all the blocks are parsed, but early that it + // can be relied on in subsequent pases. + ReachabilityPass(*vstate); + + // ID usage needs be handled in its own iteration of the instructions, + // between the two others. It depends on the first loop to have been + // finished, so that all instructions have been registered. And the following + // loop depends on all of the usage data being populated. Thus it cannot live + // in either of those iterations. + // It should also live after the forward declaration check, since it will + // have problems with missing forward declarations, but give less useful error + // messages. + for (size_t i = 0; i < vstate->ordered_instructions().size(); ++i) { + auto& instruction = vstate->ordered_instructions()[i]; + if (auto error = UpdateIdUse(*vstate, &instruction)) return error; + } + + // Validate individual opcodes. + for (size_t i = 0; i < vstate->ordered_instructions().size(); ++i) { + auto& instruction = vstate->ordered_instructions()[i]; + + // Keep these passes in the order they appear in the SPIR-V specification + // sections to maintain test consistency. + if (auto error = MiscPass(*vstate, &instruction)) return error; + if (auto error = DebugPass(*vstate, &instruction)) return error; + if (auto error = AnnotationPass(*vstate, &instruction)) return error; + if (auto error = ExtensionPass(*vstate, &instruction)) return error; + if (auto error = ModeSettingPass(*vstate, &instruction)) return error; + if (auto error = TypePass(*vstate, &instruction)) return error; + if (auto error = ConstantPass(*vstate, &instruction)) return error; + if (auto error = MemoryPass(*vstate, &instruction)) return error; + if (auto error = FunctionPass(*vstate, &instruction)) return error; + if (auto error = ImagePass(*vstate, &instruction)) return error; + if (auto error = ConversionPass(*vstate, &instruction)) return error; + if (auto error = CompositesPass(*vstate, &instruction)) return error; + if (auto error = ArithmeticsPass(*vstate, &instruction)) return error; + if (auto error = BitwisePass(*vstate, &instruction)) return error; + if (auto error = LogicalsPass(*vstate, &instruction)) return error; + if (auto error = ControlFlowPass(*vstate, &instruction)) return error; + if (auto error = DerivativesPass(*vstate, &instruction)) return error; + if (auto error = AtomicsPass(*vstate, &instruction)) return error; + if (auto error = PrimitivesPass(*vstate, &instruction)) return error; + if (auto error = BarriersPass(*vstate, &instruction)) return error; + // Group + // Device-Side Enqueue + // Pipe + if (auto error = NonUniformPass(*vstate, &instruction)) return error; + + if (auto error = LiteralsPass(*vstate, &instruction)) return error; + if (auto error = RayQueryPass(*vstate, &instruction)) return error; + if (auto error = RayTracingPass(*vstate, &instruction)) return error; + if (auto error = RayReorderNVPass(*vstate, &instruction)) return error; + if (auto error = MeshShadingPass(*vstate, &instruction)) return error; + if (auto error = TensorLayoutPass(*vstate, &instruction)) return error; + if (auto error = InvalidTypePass(*vstate, &instruction)) return error; + } + + // Validate the preconditions involving adjacent instructions. e.g. + // spv::Op::OpPhi must only be preceded by spv::Op::OpLabel, spv::Op::OpPhi, + // or spv::Op::OpLine. + if (auto error = ValidateAdjacency(*vstate)) return error; + + if (auto error = ValidateEntryPoints(*vstate)) return error; + // CFG checks are performed after the binary has been parsed + // and the CFGPass has collected information about the control flow + if (auto error = PerformCfgChecks(*vstate)) return error; + if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error; + if (auto error = ValidateDecorations(*vstate)) return error; + if (auto error = ValidateInterfaces(*vstate)) return error; + // TODO(dsinclair): Restructure ValidateBuiltins so we can move into the + // for() above as it loops over all ordered_instructions internally. + if (auto error = ValidateBuiltIns(*vstate)) return error; + // These checks must be performed after individual opcode checks because + // those checks register the limitation checked here. + for (const auto& inst : vstate->ordered_instructions()) { + if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error; + if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error; + if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst)) + return error; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateBinaryAndKeepValidationState( + const spv_const_context context, spv_const_validator_options options, + const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, + std::unique_ptr* vstate) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + vstate->reset(new ValidationState_t(&hijack_context, options, words, + num_words, kDefaultMaxNumOfWarnings)); + + return ValidateBinaryUsingContextAndValidationState( + hijack_context, words, num_words, pDiagnostic, vstate->get()); +} + +} // namespace val +} // namespace spvtools + +spv_result_t spvValidate(const spv_const_context context, + const spv_const_binary binary, + spv_diagnostic* pDiagnostic) { + return spvValidateBinary(context, binary->code, binary->wordCount, + pDiagnostic); +} + +spv_result_t spvValidateBinary(const spv_const_context context, + const uint32_t* words, const size_t num_words, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + // This interface is used for default command line options. + spv_validator_options default_options = spvValidatorOptionsCreate(); + + // Create the ValidationState using the context and default options. + spvtools::val::ValidationState_t vstate(&hijack_context, default_options, + words, num_words, + kDefaultMaxNumOfWarnings); + + spv_result_t result = + spvtools::val::ValidateBinaryUsingContextAndValidationState( + hijack_context, words, num_words, pDiagnostic, &vstate); + + spvValidatorOptionsDestroy(default_options); + return result; +} + +spv_result_t spvValidateWithOptions(const spv_const_context context, + spv_const_validator_options options, + const spv_const_binary binary, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + // Create the ValidationState using the context. + spvtools::val::ValidationState_t vstate(&hijack_context, options, + binary->code, binary->wordCount, + kDefaultMaxNumOfWarnings); + + return spvtools::val::ValidateBinaryUsingContextAndValidationState( + hijack_context, binary->code, binary->wordCount, pDiagnostic, &vstate); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate.h b/engine/engine/3rdlibs/spirv-tools/source/val/validate.h new file mode 100644 index 00000000..5d13a7be --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate.h @@ -0,0 +1,282 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_VALIDATE_H_ +#define SOURCE_VAL_VALIDATE_H_ + +#include +#include +#include +#include + +#include "source/instruction.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +class ValidationState_t; +class BasicBlock; +class Instruction; + +/// @brief Performs the Control Flow Graph checks +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise +spv_result_t PerformCfgChecks(ValidationState_t& _); + +/// @brief Updates the use vectors of all instructions that can be referenced +/// +/// This function will update the vector which define where an instruction was +/// referenced in the binary. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst); + +/// @brief This function checks all ID definitions dominate their use in the +/// CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_ID otherwise +spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _); + +/// @brief This function checks for preconditions involving the adjacent +/// instructions. +/// +/// This function will iterate over all instructions and check for any required +/// predecessor and/or successor instructions. e.g. spv::Op::OpPhi must only be +/// preceded by spv::Op::OpLabel, spv::Op::OpPhi, or spv::Op::OpLine. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_DATA otherwise +spv_result_t ValidateAdjacency(ValidationState_t& _); + +/// @brief Validates static uses of input and output variables +/// +/// Checks that any entry point that uses a input or output variable lists that +/// variable in its interface. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateInterfaces(ValidationState_t& _); + +/// @brief Validates entry point call tree requirements of +/// SPV_KHR_float_controls2 +/// +/// Checks that no entry point using FPFastMathDefault uses: +/// * FPFastMathMode Fast +/// * NoContraction +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateFloatControls2(ValidationState_t& _); + +/// @brief Validates duplicated execution modes for each entry point. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _); + +/// @brief Validates memory instructions +/// +/// @param[in] _ the validation state of the module +/// @return SPV_SUCCESS if no errors are found. +spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst); + +/// @brief Updates the immediate dominator for each of the block edges +/// +/// Updates the immediate dominator of the blocks for each of the edges +/// provided by the @p dom_edges parameter +/// +/// @param[in,out] dom_edges The edges of the dominator tree +/// @param[in] set_func This function will be called to updated the Immediate +/// dominator +void UpdateImmediateDominators( + const std::vector>& dom_edges, + std::function set_func); + +/// @brief Prints all of the dominators of a BasicBlock +/// +/// @param[in] block The dominators of this block will be printed +void printDominatorList(BasicBlock& block); + +/// Performs logical layout validation as described in section 2.4 of the SPIR-V +/// spec. +spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst); + +/// Performs Control Flow Graph validation and construction. +spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst); + +/// Validates Control Flow Graph instructions. +spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst); + +/// Performs Id and SSA validation of a module +spv_result_t IdPass(ValidationState_t& _, Instruction* inst); + +/// Performs instruction validation. +spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst); + +/// Performs decoration validation. Assumes each decoration on a group +/// has been propagated down to the group members. +spv_result_t ValidateDecorations(ValidationState_t& _); + +/// Performs validation of built-in variables. +spv_result_t ValidateBuiltIns(ValidationState_t& _); + +/// Validates type instructions. +spv_result_t TypePass(ValidationState_t& _, const Instruction* inst); + +/// Validates constant instructions. +spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of arithmetic instructions. +spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of composite instructions. +spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of conversion instructions. +spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of derivative instructions. +spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of bitwise instructions. +spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of image instructions. +spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of atomic instructions. +spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of barrier instructions. +spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of literal numbers. +spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of extension instructions. +spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of annotation instructions. +spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of non-uniform group instructions. +spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of debug instructions. +spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst); + +// Validates that capability declarations use operands allowed in the current +// context. +spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of primitive instructions. +spv_result_t PrimitivesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of mode setting instructions. +spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of function instructions. +spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of miscellaneous instructions. +spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of ray query instructions. +spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of ray tracing instructions. +spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of shader execution reorder instructions. +spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of mesh shading instructions. +spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of certain special type instructions. +spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst); + +/// Calculates the reachability of basic blocks. +void ReachabilityPass(ValidationState_t& _); + +/// Validates tensor layout and view instructions. +spv_result_t TensorLayoutPass(ValidationState_t& _, const Instruction* inst); + +/// Validates execution limitations. +/// +/// Verifies execution models are allowed for all functionality they contain. +spv_result_t ValidateExecutionLimitations(ValidationState_t& _, + const Instruction* inst); + +/// Validates restricted uses of 8- and 16-bit types. +/// +/// Validates shaders that uses 8- or 16-bit storage capabilities, but not full +/// capabilities only have appropriate uses of those types. +spv_result_t ValidateSmallTypeUses(ValidationState_t& _, + const Instruction* inst); + +/// Validates restricted uses of QCOM decorated textures +/// +/// The textures that are decorated with some of QCOM image processing +/// decorations must be used in the specified QCOM image processing built-in +/// functions and not used in any other image functions. +spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _, + const Instruction* inst); + +/// @brief Validate the ID's within a SPIR-V binary +/// +/// @param[in] pInstructions array of instructions +/// @param[in] count number of elements in instruction array +/// @param[in] bound the binary header +/// @param[in,out] position current word in the binary +/// @param[in] consumer message consumer callback +/// +/// @return result code +spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions, + const uint64_t count, const uint32_t bound, + spv_position position, + const MessageConsumer& consumer); + +// Performs validation for the SPIRV-V module binary. +// The main difference between this API and spvValidateBinary is that the +// "Validation State" is not destroyed upon function return; it lives on and is +// pointed to by the vstate unique_ptr. +spv_result_t ValidateBinaryAndKeepValidationState( + const spv_const_context context, spv_const_validator_options options, + const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, + std::unique_ptr* vstate); + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_VALIDATE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_adjacency.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_adjacency.cpp new file mode 100644 index 00000000..52519bfa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_adjacency.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of the intra-block preconditions of SPIR-V +// instructions. + +#include + +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +enum { + // Status right after meeting OpFunction. + IN_NEW_FUNCTION, + // Status right after meeting the entry block. + IN_ENTRY_BLOCK, + // Status right after meeting non-entry blocks. + PHI_VALID, + // Status right after meeting non-OpVariable instructions in the entry block + // or non-OpPhi instructions in non-entry blocks, except OpLine. + PHI_AND_VAR_INVALID, +}; + +spv_result_t ValidateAdjacency(ValidationState_t& _) { + const auto& instructions = _.ordered_instructions(); + int adjacency_status = PHI_AND_VAR_INVALID; + + for (size_t i = 0; i < instructions.size(); ++i) { + const auto& inst = instructions[i]; + switch (inst.opcode()) { + case spv::Op::OpFunction: + case spv::Op::OpFunctionParameter: + adjacency_status = IN_NEW_FUNCTION; + break; + case spv::Op::OpLabel: + adjacency_status = + adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID; + break; + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + // If it is a debug info instruction, we do not change the status to + // allow debug info instructions before OpVariable in a function. + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need + // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, + // and DebugValue. + // NOTE: This does not apply to the non-semantic vulkan debug info. + if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || + inst.ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + adjacency_status = PHI_AND_VAR_INVALID; + } + break; + case spv::Op::OpPhi: + if (adjacency_status != PHI_VALID) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpPhi must appear within a non-entry block before all " + << "non-OpPhi instructions " + << "(except for OpLine, which can be mixed with OpPhi)."; + } + break; + case spv::Op::OpLine: + case spv::Op::OpNoLine: + break; + case spv::Op::OpLoopMerge: + adjacency_status = PHI_AND_VAR_INVALID; + if (i != (instructions.size() - 1)) { + switch (instructions[i + 1].opcode()) { + case spv::Op::OpBranch: + case spv::Op::OpBranchConditional: + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpLoopMerge must immediately precede either an " + << "OpBranch or OpBranchConditional instruction. " + << "OpLoopMerge must be the second-to-last instruction in " + << "its block."; + } + } + break; + case spv::Op::OpSelectionMerge: + adjacency_status = PHI_AND_VAR_INVALID; + if (i != (instructions.size() - 1)) { + switch (instructions[i + 1].opcode()) { + case spv::Op::OpBranchConditional: + case spv::Op::OpSwitch: + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpSelectionMerge must immediately precede either an " + << "OpBranchConditional or OpSwitch instruction. " + << "OpSelectionMerge must be the second-to-last " + << "instruction in its block."; + } + } + break; + case spv::Op::OpVariable: + if (inst.GetOperandAs(2) == + spv::StorageClass::Function && + adjacency_status != IN_ENTRY_BLOCK) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "All OpVariable instructions in a function must be the " + "first instructions in the first block."; + } + break; + case spv::Op::OpUntypedVariableKHR: + if (inst.GetOperandAs(2) == + spv::StorageClass::Function && + adjacency_status != IN_ENTRY_BLOCK) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "All OpUntypedVariableKHR instructions in a function must " + "be the first instructions in the first block."; + } + break; + default: + adjacency_status = PHI_AND_VAR_INVALID; + break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_annotation.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_annotation.cpp new file mode 100644 index 00000000..03122924 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_annotation.cpp @@ -0,0 +1,571 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if the decoration takes ID parameters. +// TODO(dneto): This can be generated from the grammar. +bool DecorationTakesIdParameters(spv::Decoration type) { + switch (type) { + case spv::Decoration::UniformId: + case spv::Decoration::AlignmentId: + case spv::Decoration::MaxByteOffsetId: + case spv::Decoration::HlslCounterBufferGOOGLE: + case spv::Decoration::NodeMaxPayloadsAMDX: + case spv::Decoration::NodeSharesPayloadLimitsWithAMDX: + case spv::Decoration::PayloadNodeArraySizeAMDX: + case spv::Decoration::PayloadNodeNameAMDX: + case spv::Decoration::PayloadNodeBaseIndexAMDX: + return true; + default: + break; + } + return false; +} + +bool IsMemberDecorationOnly(spv::Decoration dec) { + switch (dec) { + case spv::Decoration::RowMajor: + case spv::Decoration::ColMajor: + case spv::Decoration::MatrixStride: + // SPIR-V spec bug? Offset is generated on variables when dealing with + // transform feedback. + // case spv::Decoration::Offset: + return true; + default: + break; + } + return false; +} + +bool IsNotMemberDecoration(spv::Decoration dec) { + switch (dec) { + case spv::Decoration::SpecId: + case spv::Decoration::Block: + case spv::Decoration::BufferBlock: + case spv::Decoration::ArrayStride: + case spv::Decoration::GLSLShared: + case spv::Decoration::GLSLPacked: + case spv::Decoration::CPacked: + // TODO: https://github.com/KhronosGroup/glslang/issues/703: + // glslang applies Restrict to structure members. + // case spv::Decoration::Restrict: + case spv::Decoration::Aliased: + case spv::Decoration::Constant: + case spv::Decoration::Uniform: + case spv::Decoration::UniformId: + case spv::Decoration::SaturatedConversion: + case spv::Decoration::Index: + case spv::Decoration::Binding: + case spv::Decoration::DescriptorSet: + case spv::Decoration::FuncParamAttr: + case spv::Decoration::FPRoundingMode: + case spv::Decoration::FPFastMathMode: + case spv::Decoration::LinkageAttributes: + case spv::Decoration::NoContraction: + case spv::Decoration::InputAttachmentIndex: + case spv::Decoration::Alignment: + case spv::Decoration::MaxByteOffset: + case spv::Decoration::AlignmentId: + case spv::Decoration::MaxByteOffsetId: + case spv::Decoration::NoSignedWrap: + case spv::Decoration::NoUnsignedWrap: + case spv::Decoration::NonUniform: + case spv::Decoration::RestrictPointer: + case spv::Decoration::AliasedPointer: + case spv::Decoration::CounterBuffer: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec, + const Instruction* inst, + const Instruction* target) { + auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream { + DiagnosticStream ds = std::move( + _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(vuid) << _.SpvDecorationString(dec) + << " decoration on target " << _.getIdName(target->id()) << " "); + return ds; + }; + switch (dec) { + case spv::Decoration::SpecId: + if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { + return fail(0) << "must be a scalar specialization constant"; + } + break; + case spv::Decoration::Block: + case spv::Decoration::BufferBlock: + case spv::Decoration::GLSLShared: + case spv::Decoration::GLSLPacked: + case spv::Decoration::CPacked: + if (target->opcode() != spv::Op::OpTypeStruct) { + return fail(0) << "must be a structure type"; + } + break; + case spv::Decoration::ArrayStride: + if (target->opcode() != spv::Op::OpTypeArray && + target->opcode() != spv::Op::OpTypeRuntimeArray && + target->opcode() != spv::Op::OpTypePointer && + target->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + return fail(0) << "must be an array or pointer type"; + } + break; + case spv::Decoration::BuiltIn: + if (target->opcode() != spv::Op::OpVariable && + target->opcode() != spv::Op::OpUntypedVariableKHR && + !spvOpcodeIsConstant(target->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "BuiltIns can only target variables, structure members or " + "constants"; + } + if (_.HasCapability(spv::Capability::Shader) && + inst->GetOperandAs(2) == spv::BuiltIn::WorkgroupSize) { + if (!spvOpcodeIsConstant(target->opcode())) { + return fail(0) << "must be a constant for WorkgroupSize"; + } + } else if (target->opcode() != spv::Op::OpVariable && + target->opcode() != spv::Op::OpUntypedVariableKHR) { + return fail(0) << "must be a variable"; + } + break; + case spv::Decoration::NoPerspective: + case spv::Decoration::Flat: + case spv::Decoration::Patch: + case spv::Decoration::Centroid: + case spv::Decoration::Sample: + case spv::Decoration::Restrict: + case spv::Decoration::Aliased: + case spv::Decoration::Volatile: + case spv::Decoration::Coherent: + case spv::Decoration::NonWritable: + case spv::Decoration::NonReadable: + case spv::Decoration::XfbBuffer: + case spv::Decoration::XfbStride: + case spv::Decoration::Component: + case spv::Decoration::Stream: + case spv::Decoration::RestrictPointer: + case spv::Decoration::AliasedPointer: + case spv::Decoration::PerPrimitiveEXT: + if (target->opcode() != spv::Op::OpVariable && + target->opcode() != spv::Op::OpUntypedVariableKHR && + target->opcode() != spv::Op::OpFunctionParameter && + target->opcode() != spv::Op::OpRawAccessChainNV) { + return fail(0) << "must be a memory object declaration"; + } + if (!_.IsPointerType(target->type_id())) { + return fail(0) << "must be a pointer type"; + } + break; + case spv::Decoration::Invariant: + case spv::Decoration::Constant: + case spv::Decoration::Location: + case spv::Decoration::Index: + case spv::Decoration::Binding: + case spv::Decoration::DescriptorSet: + case spv::Decoration::InputAttachmentIndex: + if (target->opcode() != spv::Op::OpVariable && + target->opcode() != spv::Op::OpUntypedVariableKHR) { + return fail(0) << "must be a variable"; + } + break; + default: + break; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + // The following were all checked as pointer types above. + spv::StorageClass sc = spv::StorageClass::Uniform; + const auto type = _.FindDef(target->type_id()); + if (type && type->operands().size() > 2) { + sc = type->GetOperandAs(1); + } + switch (dec) { + case spv::Decoration::Location: + case spv::Decoration::Component: + // Location is used for input, output, tile image, and ray tracing + // stages. + if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output && + sc != spv::StorageClass::RayPayloadKHR && + sc != spv::StorageClass::IncomingRayPayloadKHR && + sc != spv::StorageClass::HitAttributeKHR && + sc != spv::StorageClass::CallableDataKHR && + sc != spv::StorageClass::IncomingCallableDataKHR && + sc != spv::StorageClass::ShaderRecordBufferKHR && + sc != spv::StorageClass::HitObjectAttributeNV && + sc != spv::StorageClass::TileImageEXT) { + return _.diag(SPV_ERROR_INVALID_ID, target) + << _.VkErrorID(6672) << _.SpvDecorationString(dec) + << " decoration must not be applied to this storage class"; + } + break; + case spv::Decoration::Index: + // Langauge from SPIR-V definition of Index + if (sc != spv::StorageClass::Output) { + return fail(0) << "must be in the Output storage class"; + } + break; + case spv::Decoration::Binding: + case spv::Decoration::DescriptorSet: + if (sc != spv::StorageClass::StorageBuffer && + sc != spv::StorageClass::Uniform && + sc != spv::StorageClass::UniformConstant) { + return fail(6491) << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; + } + break; + case spv::Decoration::InputAttachmentIndex: + if (sc != spv::StorageClass::UniformConstant) { + return fail(6678) << "must be in the UniformConstant storage class"; + } + break; + case spv::Decoration::Flat: + case spv::Decoration::NoPerspective: + case spv::Decoration::Centroid: + case spv::Decoration::Sample: + if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output) { + return fail(4670) << "storage class must be Input or Output"; + } + break; + case spv::Decoration::PerVertexKHR: + if (sc != spv::StorageClass::Input) { + return fail(6777) << "storage class must be Input"; + } + break; + default: + break; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((decoration == spv::Decoration::GLSLShared) || + (decoration == spv::Decoration::GLSLPacked)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4669) << "OpDecorate decoration '" + << _.SpvDecorationString(decoration) + << "' is not valid for the Vulkan execution environment."; + } + } + + if (decoration == spv::Decoration::FPFastMathMode) { + if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "FPFastMathMode and NoContraction cannot decorate the same " + "target"; + } + auto mask = inst->GetOperandAs(2); + if ((mask & spv::FPFastMathModeMask::AllowTransform) != + spv::FPFastMathModeMask::MaskNone && + ((mask & (spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc)) != + (spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "AllowReassoc and AllowContract must be specified when " + "AllowTransform is specified"; + } + } + + // This is checked from both sides since we register decorations as we go. + if (decoration == spv::Decoration::NoContraction) { + if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "FPFastMathMode and NoContraction cannot decorate the same " + "target"; + } + } + + if (DecorationTakesIdParameters(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Decorations taking ID parameters may not be used with " + "OpDecorateId"; + } + + if (target->opcode() != spv::Op::OpDecorationGroup) { + if (IsMemberDecorationOnly(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.SpvDecorationString(decoration) + << " can only be applied to structure members"; + } + + if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) { + return error; + } + } + + // TODO: Add validations for all decorations. + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + if (!DecorationTakesIdParameters(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Decorations that don't take ID parameters may not be used with " + "OpDecorateId"; + } + + // No member decorations take id parameters, so we don't bother checking if + // we are using a member only decoration here. + + // TODO: Add validations for these decorations. + // UniformId is covered elsewhere. + return SPV_SUCCESS; +} + +spv_result_t ValidateMemberDecorate(ValidationState_t& _, + const Instruction* inst) { + const auto struct_type_id = inst->GetOperandAs(0); + const auto struct_type = _.FindDef(struct_type_id); + if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberDecorate Structure type " + << _.getIdName(struct_type_id) << " is not a struct type."; + } + const auto member = inst->GetOperandAs(1); + const auto member_count = + static_cast(struct_type->words().size() - 2); + if (member_count <= member) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << member + << " provided in OpMemberDecorate for struct " + << _.getIdName(struct_type_id) + << " is out of bounds. The structure has " << member_count + << " members. Largest valid index is " << member_count - 1 << "."; + } + + const auto decoration = inst->GetOperandAs(2); + if (IsNotMemberDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.SpvDecorationString(decoration) + << " cannot be applied to structure members"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorationGroup(ValidationState_t& _, + const Instruction* inst) { + const auto decoration_group_id = inst->GetOperandAs(0); + const auto decoration_group = _.FindDef(decoration_group_id); + for (auto pair : decoration_group->uses()) { + auto use = pair.first; + if (use->opcode() != spv::Op::OpDecorate && + use->opcode() != spv::Op::OpGroupDecorate && + use->opcode() != spv::Op::OpGroupMemberDecorate && + use->opcode() != spv::Op::OpName && + use->opcode() != spv::Op::OpDecorateId && !use->IsNonSemantic()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result id of OpDecorationGroup can only " + << "be targeted by OpName, OpGroupDecorate, " + << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupDecorate(ValidationState_t& _, + const Instruction* inst) { + const auto decoration_group_id = inst->GetOperandAs(0); + auto decoration_group = _.FindDef(decoration_group_id); + if (!decoration_group || + spv::Op::OpDecorationGroup != decoration_group->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupDecorate Decoration group " + << _.getIdName(decoration_group_id) << " is not a decoration group."; + } + for (unsigned i = 1; i < inst->operands().size(); ++i) { + auto target_id = inst->GetOperandAs(i); + auto target = _.FindDef(target_id); + if (!target || target->opcode() == spv::Op::OpDecorationGroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupDecorate may not target OpDecorationGroup " + << _.getIdName(target_id); + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, + const Instruction* inst) { + const auto decoration_group_id = inst->GetOperandAs(0); + const auto decoration_group = _.FindDef(decoration_group_id); + if (!decoration_group || + spv::Op::OpDecorationGroup != decoration_group->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupMemberDecorate Decoration group " + << _.getIdName(decoration_group_id) << " is not a decoration group."; + } + // Grammar checks ensures that the number of arguments to this instruction + // is an odd number: 1 decoration group + (id,literal) pairs. + for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) { + const uint32_t struct_id = inst->GetOperandAs(i); + const uint32_t index = inst->GetOperandAs(i + 1); + auto struct_instr = _.FindDef(struct_id); + if (!struct_instr || spv::Op::OpTypeStruct != struct_instr->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupMemberDecorate Structure type " + << _.getIdName(struct_id) << " is not a struct type."; + } + const uint32_t num_struct_members = + static_cast(struct_instr->words().size() - 2); + if (index >= num_struct_members) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << index + << " provided in OpGroupMemberDecorate for struct " + << _.getIdName(struct_id) + << " is out of bounds. The structure has " << num_struct_members + << " members. Largest valid index is " << num_struct_members - 1 + << "."; + } + } + return SPV_SUCCESS; +} + +// Registers necessary decoration(s) for the appropriate IDs based on the +// instruction. +spv_result_t RegisterDecorations(ValidationState_t& _, + const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: { + const uint32_t target_id = inst->word(1); + const spv::Decoration dec_type = + static_cast(inst->word(2)); + std::vector dec_params; + if (inst->words().size() > 3) { + dec_params.insert(dec_params.end(), inst->words().begin() + 3, + inst->words().end()); + } + _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params)); + break; + } + case spv::Op::OpMemberDecorate: { + const uint32_t struct_id = inst->word(1); + const uint32_t index = inst->word(2); + const spv::Decoration dec_type = + static_cast(inst->word(3)); + std::vector dec_params; + if (inst->words().size() > 4) { + dec_params.insert(dec_params.end(), inst->words().begin() + 4, + inst->words().end()); + } + _.RegisterDecorationForId(struct_id, + Decoration(dec_type, dec_params, index)); + break; + } + case spv::Op::OpDecorationGroup: { + // We don't need to do anything right now. Assigning decorations to groups + // will be taken care of via OpGroupDecorate. + break; + } + case spv::Op::OpGroupDecorate: { + // Word 1 is the group . All subsequent words are target s that + // are going to be decorated with the decorations. + const uint32_t decoration_group_id = inst->word(1); + std::set& group_decorations = + _.id_decorations(decoration_group_id); + for (size_t i = 2; i < inst->words().size(); ++i) { + const uint32_t target_id = inst->word(i); + _.RegisterDecorationsForId(target_id, group_decorations.begin(), + group_decorations.end()); + } + break; + } + case spv::Op::OpGroupMemberDecorate: { + // Word 1 is the Decoration Group followed by (struct,literal) + // pairs. All decorations of the group should be applied to all the struct + // members that are specified in the instructions. + const uint32_t decoration_group_id = inst->word(1); + std::set& group_decorations = + _.id_decorations(decoration_group_id); + // Grammar checks ensures that the number of arguments to this instruction + // is an odd number: 1 decoration group + (id,literal) pairs. + for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) { + const uint32_t struct_id = inst->word(i); + const uint32_t index = inst->word(i + 1); + // ID validation phase ensures this is in fact a struct instruction and + // that the index is not out of bound. + _.RegisterDecorationsForStructMember(struct_id, index, + group_decorations.begin(), + group_decorations.end()); + } + break; + } + default: + break; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpDecorate: + if (auto error = ValidateDecorate(_, inst)) return error; + break; + case spv::Op::OpDecorateId: + if (auto error = ValidateDecorateId(_, inst)) return error; + break; + // TODO(dneto): spv::Op::OpDecorateStringGOOGLE + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253 + case spv::Op::OpMemberDecorate: + if (auto error = ValidateMemberDecorate(_, inst)) return error; + break; + case spv::Op::OpDecorationGroup: + if (auto error = ValidateDecorationGroup(_, inst)) return error; + break; + case spv::Op::OpGroupDecorate: + if (auto error = ValidateGroupDecorate(_, inst)) return error; + break; + case spv::Op::OpGroupMemberDecorate: + if (auto error = ValidateGroupMemberDecorate(_, inst)) return error; + break; + default: + break; + } + + // In order to validate decoration rules, we need to know all the decorations + // that are applied to any given . + RegisterDecorations(_, inst); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_arithmetics.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_arithmetics.cpp new file mode 100644 index 00000000..38281be3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_arithmetics.cpp @@ -0,0 +1,860 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Performs validation of arithmetic instructions. + +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of arithmetic instructions. +spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + case spv::Op::OpFDiv: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpFNegate: { + bool supportsCoopMat = + (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem && + opcode != spv::Op::OpFMod); + bool supportsCoopVec = + (opcode != spv::Op::OpFRem && opcode != spv::Op::OpFMod); + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpFMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsFloatCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected floating scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } else if (supportsCoopMat && + _.IsCooperativeMatrixKHRType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsFloatCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } else if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + break; + } + + case spv::Op::OpUDiv: + case spv::Op::OpUMod: { + bool supportsCoopMat = (opcode == spv::Op::OpUDiv); + bool supportsCoopVec = (opcode == spv::Op::OpUDiv); + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !(supportsCoopMat && + _.IsUnsignedIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && + _.IsUnsignedIntCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } else if (supportsCoopMat && + _.IsCooperativeMatrixKHRType(result_type)) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsUnsignedIntCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } else if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + break; + } + + case spv::Op::OpISub: + case spv::Op::OpIAdd: + case spv::Op::OpIMul: + case spv::Op::OpSDiv: + case spv::Op::OpSMod: + case spv::Op::OpSRem: + case spv::Op::OpSNegate: { + bool supportsCoopMat = + (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem && + opcode != spv::Op::OpSMod); + bool supportsCoopVec = + (opcode != spv::Op::OpSRem && opcode != spv::Op::OpSMod); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpIMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t dimension = _.GetDimension(result_type); + const uint32_t bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + + if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) { + if (!_.IsCooperativeVectorNVType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, type_id, result_type); + if (ret != SPV_SUCCESS) return ret; + } + + if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) { + if (!_.IsCooperativeMatrixKHRType(type_id) || + !_.IsIntCooperativeMatrixType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false); + if (ret != SPV_SUCCESS) return ret; + } + + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) && + !(opcode == spv::Op::OpIMul && + _.IsCooperativeMatrixKHRType(result_type) && + _.IsIntCooperativeMatrixType(result_type)) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type)))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + if (_.GetDimension(type_id) != dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + break; + } + + case spv::Op::OpDot: { + if (!_.IsFloatScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar type as Result Type: " + << spvOpcodeString(opcode); + + if (_.IsBfloat16ScalarType(result_type)) { + if (!_.HasCapability(spv::Capability::BFloat16DotProductKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpDot Result Type " << _.getIdName(result_type) + << "requires BFloat16DotProductKHR be declared."; + } + } + + uint32_t first_vector_num_components = 0; + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + + if (!type_id || !_.IsFloatVectorType(type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + const uint32_t component_type = _.GetComponentType(type_id); + if (component_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component type to be equal to Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + const uint32_t num_components = _.GetDimension(type_id); + if (operand_index == 2) { + first_vector_num_components = num_components; + } else if (num_components != first_vector_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same number of components: " + << spvOpcodeString(opcode); + } + } + break; + } + + case spv::Op::OpVectorTimesScalar: { + if (!_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != vector_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(vector_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the vector operand: " << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpMatrixTimesScalar: { + if (!_.IsFloatMatrixType(result_type) && + !(_.IsCooperativeMatrixType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != matrix_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected matrix operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(matrix_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the matrix operand: " << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpVectorTimesMatrix: { + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t res_component_type = _.GetComponentType(result_type); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as left operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and vector to be " + << "equal: " << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, + &matrix_num_cols, &matrix_col_type, + &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != matrix_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to " + << "Result Type vector size: " << spvOpcodeString(opcode); + + if (matrix_num_rows != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of rows of the matrix to be equal to the " + << "vector operand size: " << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpMatrixTimesVector: { + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, + &matrix_num_cols, &matrix_col_type, + &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + if (result_type != matrix_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of the matrix to be equal to Result " + "Type: " + << spvOpcodeString(opcode); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (matrix_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size: " << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpMatrixTimesMatrix: { + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t left_num_rows = 0; + uint32_t left_num_cols = 0; + uint32_t left_col_type = 0; + uint32_t left_component_type = 0; + if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols, + &left_col_type, &left_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + uint32_t right_num_rows = 0; + uint32_t right_num_cols = 0; + uint32_t right_col_type = 0; + uint32_t right_component_type = 0; + if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols, + &right_col_type, &right_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (!_.IsFloatScalarType(res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (res_col_type != left_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column types of Result Type and left matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (res_component_type != right_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and right matrix to " + "be " + << "equal: " << spvOpcodeString(opcode); + + if (res_num_cols != right_num_cols) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of Result Type and right matrix " + "to " + << "be equal: " << spvOpcodeString(opcode); + + if (left_num_cols != right_num_rows) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of left matrix and number of " + "rows " + << "of right matrix to be equal: " << spvOpcodeString(opcode); + + assert(left_num_rows == res_num_rows); + break; + } + + case spv::Op::OpOuterProduct: { + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (left_type_id != res_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of Result Type to be equal to the type " + << "of the left operand: " << spvOpcodeString(opcode); + + if (!right_type_id || !_.IsFloatVectorType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (res_num_cols != _.GetDimension(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size of the right operand: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpIAddCarry: + case spv::Op::OpISubBorrow: + case spv::Op::OpUMulExtended: + case spv::Op::OpSMulExtended: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected a struct as Result Type: " + << spvOpcodeString(opcode); + + if (result_types.size() != 2) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct to have two members: " + << spvOpcodeString(opcode); + + if (opcode == spv::Op::OpSMulExtended) { + if (!_.IsIntScalarType(result_types[0]) && + !_.IsIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be integer " + "scalar " + << "or vector: " << spvOpcodeString(opcode); + } else { + if (!_.IsUnsignedIntScalarType(result_types[0]) && + !_.IsUnsignedIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be unsigned " + << "integer scalar or vector: " << spvOpcodeString(opcode); + } + + if (result_types[0] != result_types[1]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be identical: " + << spvOpcodeString(opcode); + + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + if (left_type_id != result_types[0] || right_type_id != result_types[0]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type member type: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpCooperativeMatrixMulAddNV: { + const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); + const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); + const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); + + if (!_.IsCooperativeMatrixNVType(A_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as A Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(B_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as B Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(C_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as C Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixNVType(D_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + const auto A = _.FindDef(A_type_id); + const auto B = _.FindDef(B_type_id); + const auto C = _.FindDef(C_type_id); + const auto D = _.FindDef(D_type_id); + + std::tuple A_scope, B_scope, C_scope, D_scope, + A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; + + A_scope = _.EvalInt32IfConst(A->GetOperandAs(2)); + B_scope = _.EvalInt32IfConst(B->GetOperandAs(2)); + C_scope = _.EvalInt32IfConst(C->GetOperandAs(2)); + D_scope = _.EvalInt32IfConst(D->GetOperandAs(2)); + + A_rows = _.EvalInt32IfConst(A->GetOperandAs(3)); + B_rows = _.EvalInt32IfConst(B->GetOperandAs(3)); + C_rows = _.EvalInt32IfConst(C->GetOperandAs(3)); + D_rows = _.EvalInt32IfConst(D->GetOperandAs(3)); + + A_cols = _.EvalInt32IfConst(A->GetOperandAs(4)); + B_cols = _.EvalInt32IfConst(B->GetOperandAs(4)); + C_cols = _.EvalInt32IfConst(C->GetOperandAs(4)); + D_cols = _.EvalInt32IfConst(D->GetOperandAs(4)); + + const auto notEqual = [](std::tuple X, + std::tuple Y) { + return (std::get<1>(X) && std::get<1>(Y) && + std::get<2>(X) != std::get<2>(Y)); + }; + + if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || + notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || + notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix scopes must match: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || + notEqual(C_rows, D_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'M' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || + notEqual(C_cols, D_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'N' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_cols, B_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'K' mismatch: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpCooperativeMatrixMulAddKHR: { + const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); + const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); + const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); + + if (!_.IsCooperativeMatrixAType(A_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be A Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixBType(B_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be B Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixAccType(C_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix type must be Accumulator Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixKHRType(D_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + const auto A = _.FindDef(A_type_id); + const auto B = _.FindDef(B_type_id); + const auto C = _.FindDef(C_type_id); + const auto D = _.FindDef(D_type_id); + + std::tuple A_scope, B_scope, C_scope, D_scope, + A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; + + A_scope = _.EvalInt32IfConst(A->GetOperandAs(2)); + B_scope = _.EvalInt32IfConst(B->GetOperandAs(2)); + C_scope = _.EvalInt32IfConst(C->GetOperandAs(2)); + D_scope = _.EvalInt32IfConst(D->GetOperandAs(2)); + + A_rows = _.EvalInt32IfConst(A->GetOperandAs(3)); + B_rows = _.EvalInt32IfConst(B->GetOperandAs(3)); + C_rows = _.EvalInt32IfConst(C->GetOperandAs(3)); + D_rows = _.EvalInt32IfConst(D->GetOperandAs(3)); + + A_cols = _.EvalInt32IfConst(A->GetOperandAs(4)); + B_cols = _.EvalInt32IfConst(B->GetOperandAs(4)); + C_cols = _.EvalInt32IfConst(C->GetOperandAs(4)); + D_cols = _.EvalInt32IfConst(D->GetOperandAs(4)); + + const auto notEqual = [](std::tuple X, + std::tuple Y) { + return (std::get<1>(X) && std::get<1>(Y) && + std::get<2>(X) != std::get<2>(Y)); + }; + + if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || + notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || + notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix scopes must match: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || + notEqual(C_rows, D_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'M' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || + notEqual(C_cols, D_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'N' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_cols, B_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'K' mismatch: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpCooperativeMatrixReduceNV: { + if (!_.IsCooperativeMatrixKHRType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be a cooperative matrix type: " + << spvOpcodeString(opcode); + } + + const auto result_comp_type_id = + _.FindDef(result_type)->GetOperandAs(1); + + const auto matrix_id = inst->GetOperandAs(2); + const auto matrix = _.FindDef(matrix_id); + const auto matrix_type_id = matrix->type_id(); + if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix must have a cooperative matrix type: " + << spvOpcodeString(opcode); + } + const auto matrix_type = _.FindDef(matrix_type_id); + const auto matrix_comp_type_id = matrix_type->GetOperandAs(1); + if (matrix_comp_type_id != result_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix type must have the same component " + "type: " + << spvOpcodeString(opcode); + } + if (_.FindDef(result_type)->GetOperandAs(2) != + matrix_type->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix type must have the same scope: " + << spvOpcodeString(opcode); + } + + if (!_.IsCooperativeMatrixAccType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must have UseAccumulator: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixAccType(matrix_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix type must have UseAccumulator: " + << spvOpcodeString(opcode); + } + + const auto reduce_value = inst->GetOperandAs(3); + + if ((reduce_value & + uint32_t( + spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) && + (reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row | + spv::CooperativeMatrixReduceMask::Column))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reduce 2x2 must not be used with Row/Column: " + << spvOpcodeString(opcode); + } + + std::tuple result_rows, result_cols, matrix_rows, + matrix_cols; + result_rows = + _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs(3)); + result_cols = + _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs(4)); + matrix_rows = _.EvalInt32IfConst(matrix_type->GetOperandAs(3)); + matrix_cols = _.EvalInt32IfConst(matrix_type->GetOperandAs(4)); + + if (reduce_value & + uint32_t( + spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) { + if (std::get<1>(result_rows) && std::get<1>(result_cols) && + std::get<1>(matrix_rows) && std::get<1>(matrix_cols) && + (std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 || + std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For Reduce2x2, result rows/cols must be half of matrix " + "rows/cols: " + << spvOpcodeString(opcode); + } + } + if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Row)) { + if (std::get<1>(result_rows) && std::get<1>(matrix_rows) && + std::get<2>(result_rows) != std::get<2>(matrix_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For ReduceRow, result rows must match matrix rows: " + << spvOpcodeString(opcode); + } + } + if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Column)) { + if (std::get<1>(result_cols) && std::get<1>(matrix_cols) && + std::get<2>(result_cols) != std::get<2>(matrix_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For ReduceColumn, result cols must match matrix cols: " + << spvOpcodeString(opcode); + } + } + + const auto combine_func_id = inst->GetOperandAs(4); + const auto combine_func = _.FindDef(combine_func_id); + if (!combine_func || combine_func->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc must be a function: " << spvOpcodeString(opcode); + } + const auto function_type_id = combine_func->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (function_type->operands().size() != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc must have two parameters: " + << spvOpcodeString(opcode); + } + for (uint32_t i = 0; i < 3; ++i) { + // checks return type and two params + const auto param_type_id = function_type->GetOperandAs(i + 1); + if (param_type_id != matrix_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "CombineFunc return type and parameters must match matrix " + "component type: " + << spvOpcodeString(opcode); + } + } + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_atomics.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_atomics.cpp new file mode 100644 index 00000000..990ed315 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_atomics.cpp @@ -0,0 +1,457 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of atomic SPIR-V instructions. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_memory_semantics.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace { + +bool IsStorageClassAllowedByUniversalRules(spv::StorageClass storage_class) { + switch (storage_class) { + case spv::StorageClass::Uniform: + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::Workgroup: + case spv::StorageClass::CrossWorkgroup: + case spv::StorageClass::Generic: + case spv::StorageClass::AtomicCounter: + case spv::StorageClass::Image: + case spv::StorageClass::Function: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::TaskPayloadWorkgroupEXT: + return true; + break; + default: + return false; + } +} + +bool HasReturnType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicFlagClear: + return false; + break; + default: + return true; + } +} + +bool HasOnlyFloatReturnType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicFMaxEXT: + return true; + break; + default: + return false; + } +} + +bool HasOnlyIntReturnType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + return true; + break; + default: + return false; + } +} + +bool HasIntOrFloatReturnType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + return true; + break; + default: + return false; + } +} + +bool HasOnlyBoolReturnType(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpAtomicFlagTestAndSet: + return true; + break; + default: + return false; + } +} + +} // namespace + +namespace spvtools { +namespace val { + +// Validates correctness of atomic instructions. +spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicStore: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: { + const uint32_t result_type = inst->type_id(); + + // Validate return type first so can just check if pointer type is same + // (if applicable) + if (HasReturnType(opcode)) { + if (HasOnlyFloatReturnType(opcode) && + (!(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(result_type)) && + !_.IsFloatScalarType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be float scalar type"; + } else if (HasOnlyIntReturnType(opcode) && + !_.IsIntScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be integer scalar type"; + } else if (HasIntOrFloatReturnType(opcode) && + !_.IsFloatScalarType(result_type) && + !(opcode == spv::Op::OpAtomicExchange && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(result_type)) && + !_.IsIntScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be integer or float scalar type"; + } else if (HasOnlyBoolReturnType(opcode) && + !_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be bool scalar type"; + } + } + + uint32_t operand_index = HasReturnType(opcode) ? 2 : 0; + const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++); + uint32_t data_type = 0; + spv::StorageClass storage_class; + if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to be a pointer type"; + } + + // If the pointer is an untyped pointer, get the data type elsewhere. + if (data_type == 0) { + switch (opcode) { + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicCompareExchange: + case spv::Op::OpAtomicCompareExchangeWeak: + case spv::Op::OpAtomicIIncrement: + case spv::Op::OpAtomicIDecrement: + case spv::Op::OpAtomicIAdd: + case spv::Op::OpAtomicISub: + case spv::Op::OpAtomicSMin: + case spv::Op::OpAtomicUMin: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicSMax: + case spv::Op::OpAtomicUMax: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicAnd: + case spv::Op::OpAtomicOr: + case spv::Op::OpAtomicXor: + data_type = inst->type_id(); + break; + case spv::Op::OpAtomicFlagTestAndSet: + case spv::Op::OpAtomicFlagClear: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Untyped pointers are not supported by atomic flag " + "instructions"; + break; + case spv::Op::OpAtomicStore: + data_type = _.FindDef(inst->GetOperandAs(3))->type_id(); + break; + default: + break; + } + } + + // Can't use result_type because OpAtomicStore doesn't have a result + if (_.IsIntScalarType(data_type) && _.GetBitWidth(data_type) == 64 && + !_.HasCapability(spv::Capability::Int64Atomics)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": 64-bit atomics require the Int64Atomics capability"; + } + + // Validate storage class against universal rules + if (!IsStorageClassAllowedByUniversalRules(storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": storage class forbidden by universal validation rules."; + } + + // Then Shader rules + if (_.HasCapability(spv::Capability::Shader)) { + // Vulkan environment rule + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((storage_class != spv::StorageClass::Uniform) && + (storage_class != spv::StorageClass::StorageBuffer) && + (storage_class != spv::StorageClass::Workgroup) && + (storage_class != spv::StorageClass::Image) && + (storage_class != spv::StorageClass::PhysicalStorageBuffer) && + (storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4686) << spvOpcodeString(opcode) + << ": Vulkan spec only allows storage classes for atomic to " + "be: Uniform, Workgroup, Image, StorageBuffer, " + "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT."; + } + } else if (storage_class == spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Function storage class forbidden when the Shader " + "capability is declared."; + } + + if (opcode == spv::Op::OpAtomicFAddEXT) { + // result type being float checked already + if (_.GetBitWidth(result_type) == 16) { + if (_.IsFloat16Vector2Or4Type(result_type)) { + if (!_.HasCapability(spv::Capability::AtomicFloat16VectorNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float vector atomics require the " + "AtomicFloat16VectorNV capability"; + } else { + if (!_.HasCapability(spv::Capability::AtomicFloat16AddEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + } + } + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(spv::Capability::AtomicFloat32AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(spv::Capability::AtomicFloat64AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat64AddEXT " + "capability"; + } + } else if (opcode == spv::Op::OpAtomicFMinEXT || + opcode == spv::Op::OpAtomicFMaxEXT) { + if (_.GetBitWidth(result_type) == 16) { + if (_.IsFloat16Vector2Or4Type(result_type)) { + if (!_.HasCapability(spv::Capability::AtomicFloat16VectorNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float vector atomics require the " + "AtomicFloat16VectorNV capability"; + } else { + if (!_.HasCapability(spv::Capability::AtomicFloat16MinMaxEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat16MinMaxEXT capability"; + } + } + } + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(spv::Capability::AtomicFloat32MinMaxEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat32MinMaxEXT capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(spv::Capability::AtomicFloat64MinMaxEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat64MinMaxEXT capability"; + } + } + } + + // And finally OpenCL environment rules + if (spvIsOpenCLEnv(_.context()->target_env)) { + if ((storage_class != spv::StorageClass::Function) && + (storage_class != spv::StorageClass::Workgroup) && + (storage_class != spv::StorageClass::CrossWorkgroup) && + (storage_class != spv::StorageClass::Generic)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": storage class must be Function, Workgroup, " + "CrossWorkGroup or Generic in the OpenCL environment."; + } + + if (_.context()->target_env == SPV_ENV_OPENCL_1_2) { + if (storage_class == spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Storage class cannot be Generic in OpenCL 1.2 " + "environment"; + } + } + } + + // If result and pointer type are different, need to do special check here + if (opcode == spv::Op::OpAtomicFlagTestAndSet || + opcode == spv::Op::OpAtomicFlagClear) { + if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to point to a value of 32-bit integer " + "type"; + } + } else if (opcode == spv::Op::OpAtomicStore) { + if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to be a pointer to integer or float " + << "scalar type"; + } + } else if (data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to point to a value of type Result " + "Type"; + } + + auto memory_scope = inst->GetOperandAs(operand_index++); + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + const auto equal_semantics_index = operand_index++; + if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index, + memory_scope)) + return error; + + if (opcode == spv::Op::OpAtomicCompareExchange || + opcode == spv::Op::OpAtomicCompareExchangeWeak) { + const auto unequal_semantics_index = operand_index++; + if (auto error = ValidateMemorySemantics( + _, inst, unequal_semantics_index, memory_scope)) + return error; + + // Volatile bits must match for equal and unequal semantics. Previous + // checks guarantee they are 32-bit constants, but we need to recheck + // whether they are evaluatable constants. + bool is_int32 = false; + bool is_equal_const = false; + bool is_unequal_const = false; + uint32_t equal_value = 0; + uint32_t unequal_value = 0; + std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst( + inst->GetOperandAs(equal_semantics_index)); + std::tie(is_int32, is_unequal_const, unequal_value) = + _.EvalInt32IfConst( + inst->GetOperandAs(unequal_semantics_index)); + if (is_equal_const && is_unequal_const && + ((equal_value & uint32_t(spv::MemorySemanticsMask::Volatile)) ^ + (unequal_value & uint32_t(spv::MemorySemanticsMask::Volatile)))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Volatile mask setting must match for Equal and Unequal " + "memory semantics"; + } + } + + if (opcode == spv::Op::OpAtomicStore) { + const uint32_t value_type = _.GetOperandTypeId(inst, 3); + if (value_type != data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Value type and the type pointed to by " + "Pointer to be the same"; + } + } else if (opcode != spv::Op::OpAtomicLoad && + opcode != spv::Op::OpAtomicIIncrement && + opcode != spv::Op::OpAtomicIDecrement && + opcode != spv::Op::OpAtomicFlagTestAndSet && + opcode != spv::Op::OpAtomicFlagClear) { + const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++); + if (value_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Value to be of type Result Type"; + } + } + + if (opcode == spv::Op::OpAtomicCompareExchange || + opcode == spv::Op::OpAtomicCompareExchangeWeak) { + const uint32_t comparator_type = + _.GetOperandTypeId(inst, operand_index++); + if (comparator_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Comparator to be of type Result Type"; + } + } + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_barriers.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_barriers.cpp new file mode 100644 index 00000000..0abd5c85 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_barriers.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of barrier SPIR-V instructions. + +#include + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_memory_semantics.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of barrier instructions. +spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpControlBarrier: { + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::TessellationControl && + model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::Kernel && + model != spv::ExecutionModel::TaskNV && + model != spv::ExecutionModel::MeshNV) { + if (message) { + *message = + "OpControlBarrier requires one of the following " + "Execution " + "Models: TessellationControl, GLCompute, Kernel, " + "MeshNV or TaskNV"; + } + return false; + } + return true; + }); + } + + const uint32_t execution_scope = inst->word(1); + const uint32_t memory_scope = inst->word(2); + + if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { + return error; + } + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) { + return error; + } + break; + } + + case spv::Op::OpMemoryBarrier: { + const uint32_t memory_scope = inst->word(1); + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 1, memory_scope)) { + return error; + } + break; + } + + case spv::Op::OpNamedBarrierInitialize: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeNamedBarrier) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be OpTypeNamedBarrier"; + } + + const uint32_t subgroup_count_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(subgroup_count_type) || + _.GetBitWidth(subgroup_count_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Subgroup Count to be a 32-bit int"; + } + break; + } + + case spv::Op::OpMemoryNamedBarrier: { + const uint32_t named_barrier_type = _.GetOperandTypeId(inst, 0); + if (_.GetIdOpcode(named_barrier_type) != spv::Op::OpTypeNamedBarrier) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Named Barrier to be of type OpTypeNamedBarrier"; + } + + const uint32_t memory_scope = inst->word(2); + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) { + return error; + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_bitwise.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_bitwise.cpp new file mode 100644 index 00000000..4acc9982 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_bitwise.cpp @@ -0,0 +1,237 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of bitwise instructions. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates when base and result need to be the same type +spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst, + const uint32_t base_type) { + const spv::Op opcode = inst->opcode(); + + if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type for Base operand: " + << spvOpcodeString(opcode); + } + + // Vulkan has a restriction to 32 bit for base + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(base_type) != 32 && + !_.options()->allow_vulkan_32_bit_bitwise) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4781) + << "Expected 32-bit int type for Base operand: " + << spvOpcodeString(opcode); + } + } + + // OpBitCount just needs same number of components + if (base_type != inst->type_id() && opcode != spv::Op::OpBitCount) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + } + + return SPV_SUCCESS; +} + +// Validates correctness of bitwise instructions. +spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpShiftRightLogical: + case spv::Op::OpShiftRightArithmetic: + case spv::Op::OpShiftLeftLogical: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t shift_type = _.GetOperandTypeId(inst, 3); + + if (!base_type || + (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type) && + !_.IsIntCooperativeVectorNVType(base_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(base_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode); + + if (!shift_type || + (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type) && + !_.IsIntCooperativeVectorNVType(shift_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(shift_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpBitwiseOr: + case spv::Op::OpBitwiseXor: + case spv::Op::OpBitwiseAnd: + case spv::Op::OpNot: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t result_bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && + !_.IsIntCooperativeVectorNVType(type_id))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + if (_.GetDimension(type_id) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != result_bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + break; + } + + case spv::Op::OpBitFieldInsert: { + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t insert_type = _.GetOperandTypeId(inst, 3); + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t count_type = _.GetOperandTypeId(inst, 5); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + if (insert_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Insert Type to be equal to Result Type: " + << spvOpcodeString(opcode); + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpBitFieldSExtract: + case spv::Op::OpBitFieldUExtract: { + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t offset_type = _.GetOperandTypeId(inst, 3); + const uint32_t count_type = _.GetOperandTypeId(inst, 4); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpBitReverse: { + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + break; + } + + case spv::Op::OpBitCount: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + + const uint32_t base_dimension = _.GetDimension(base_type); + const uint32_t result_dimension = _.GetDimension(result_type); + + if (base_dimension != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base dimension to be equal to Result Type " + "dimension: " + << spvOpcodeString(opcode); + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_builtins.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_builtins.cpp new file mode 100644 index 00000000..11d926f5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_builtins.cpp @@ -0,0 +1,4775 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of built-in variables. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns a short textual description of the id defined by the given +// instruction. +std::string GetIdDesc(const Instruction& inst) { + std::ostringstream ss; + ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")"; + return ss.str(); +} + +// Gets underlying data type which is +// - member type if instruction is OpTypeStruct +// (member index is taken from decoration). +// - data type if id creates a pointer. +// - type of the constant if instruction is OpConst or OpSpecConst. +// +// Fails in any other case. The function is based on built-ins allowed by +// the Vulkan spec. +// TODO: If non-Vulkan validation rules are added then it might need +// to be refactored. +spv_result_t GetUnderlyingType(ValidationState_t& _, + const Decoration& decoration, + const Instruction& inst, + uint32_t* underlying_type) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + if (inst.opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + *underlying_type = inst.word(decoration.struct_member_index() + 2); + return SPV_SUCCESS; + } + + if (inst.opcode() == spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << " did not find an member index to get underlying data type for " + "struct type."; + } + + if (spvOpcodeIsConstant(inst.opcode())) { + *underlying_type = inst.type_id(); + return SPV_SUCCESS; + } + + spv::StorageClass storage_class; + if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << " is decorated with BuiltIn. BuiltIn decoration should only be " + "applied to struct types, variables and constants."; + } + return SPV_SUCCESS; +} + +// Returns Storage Class used by the instruction if applicable. +// Returns spv::StorageClass::Max if not. +spv::StorageClass GetStorageClass(const Instruction& inst) { + switch (inst.opcode()) { + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypeForwardPointer: { + return spv::StorageClass(inst.word(2)); + } + case spv::Op::OpVariable: { + return spv::StorageClass(inst.word(3)); + } + case spv::Op::OpUntypedVariableKHR: { + return spv::StorageClass(inst.word(4)); + } + case spv::Op::OpGenericCastToPtrExplicit: { + return spv::StorageClass(inst.word(4)); + } + default: { break; } + } + return spv::StorageClass::Max; +} + +typedef enum VUIDError_ { + VUIDErrorExecutionModel = 0, + VUIDErrorStorageClass = 1, + VUIDErrorType = 2, + VUIDErrorMax, +} VUIDError; + +const static uint32_t NumVUIDBuiltins = 40; + +typedef struct { + spv::BuiltIn builtIn; + uint32_t vuid[VUIDErrorMax]; // execution mode, storage class, type VUIDs +} BuiltinVUIDMapping; + +// Many built-ins have the same checks (Storage Class, Type, etc) +// This table provides a nice LUT for the VUIDs +std::array builtinVUIDInfo = {{ + // clang-format off + {spv::BuiltIn::SubgroupEqMask, {0, 4370, 4371}}, + {spv::BuiltIn::SubgroupGeMask, {0, 4372, 4373}}, + {spv::BuiltIn::SubgroupGtMask, {0, 4374, 4375}}, + {spv::BuiltIn::SubgroupLeMask, {0, 4376, 4377}}, + {spv::BuiltIn::SubgroupLtMask, {0, 4378, 4379}}, + {spv::BuiltIn::SubgroupLocalInvocationId, {0, 4380, 4381}}, + {spv::BuiltIn::SubgroupSize, {0, 4382, 4383}}, + {spv::BuiltIn::GlobalInvocationId, {4236, 4237, 4238}}, + {spv::BuiltIn::LocalInvocationId, {4281, 4282, 4283}}, + {spv::BuiltIn::NumWorkgroups, {4296, 4297, 4298}}, + {spv::BuiltIn::NumSubgroups, {4293, 4294, 4295}}, + {spv::BuiltIn::SubgroupId, {4367, 4368, 4369}}, + {spv::BuiltIn::WorkgroupId, {4422, 4423, 4424}}, + {spv::BuiltIn::HitKindKHR, {4242, 4243, 4244}}, + {spv::BuiltIn::HitTNV, {4245, 4246, 4247}}, + {spv::BuiltIn::InstanceCustomIndexKHR, {4251, 4252, 4253}}, + {spv::BuiltIn::InstanceId, {4254, 4255, 4256}}, + {spv::BuiltIn::RayGeometryIndexKHR, {4345, 4346, 4347}}, + {spv::BuiltIn::ObjectRayDirectionKHR, {4299, 4300, 4301}}, + {spv::BuiltIn::ObjectRayOriginKHR, {4302, 4303, 4304}}, + {spv::BuiltIn::ObjectToWorldKHR, {4305, 4306, 4307}}, + {spv::BuiltIn::WorldToObjectKHR, {4434, 4435, 4436}}, + {spv::BuiltIn::IncomingRayFlagsKHR, {4248, 4249, 4250}}, + {spv::BuiltIn::RayTminKHR, {4351, 4352, 4353}}, + {spv::BuiltIn::RayTmaxKHR, {4348, 4349, 4350}}, + {spv::BuiltIn::WorldRayDirectionKHR, {4428, 4429, 4430}}, + {spv::BuiltIn::WorldRayOriginKHR, {4431, 4432, 4433}}, + {spv::BuiltIn::LaunchIdKHR, {4266, 4267, 4268}}, + {spv::BuiltIn::LaunchSizeKHR, {4269, 4270, 4271}}, + {spv::BuiltIn::FragInvocationCountEXT, {4217, 4218, 4219}}, + {spv::BuiltIn::FragSizeEXT, {4220, 4221, 4222}}, + {spv::BuiltIn::FragStencilRefEXT, {4223, 4224, 4225}}, + {spv::BuiltIn::FullyCoveredEXT, {4232, 4233, 4234}}, + {spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}}, + {spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}}, + {spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}}, + {spv::BuiltIn::PrimitivePointIndicesEXT, {7041, 7043, 7044}}, + {spv::BuiltIn::PrimitiveLineIndicesEXT, {7047, 7049, 7050}}, + {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}}, + {spv::BuiltIn::CullPrimitiveEXT, {7034, 7035, 7036}}, + + // clang-format on +}}; + +uint32_t GetVUIDForBuiltin(spv::BuiltIn builtIn, VUIDError type) { + uint32_t vuid = 0; + for (const auto& iter: builtinVUIDInfo) { + if (iter.builtIn == builtIn) { + assert(type < VUIDErrorMax); + vuid = iter.vuid[type]; + break; + } + } + return vuid; +} + +bool IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin, + spv::ExecutionModel stage) { + switch (builtin) { + case spv::BuiltIn::HitKindKHR: + case spv::BuiltIn::HitTNV: + if (stage == spv::ExecutionModel::AnyHitKHR || + stage == spv::ExecutionModel::ClosestHitKHR) { + return true; + } + break; + case spv::BuiltIn::InstanceCustomIndexKHR: + case spv::BuiltIn::InstanceId: + case spv::BuiltIn::RayGeometryIndexKHR: + case spv::BuiltIn::ObjectRayDirectionKHR: + case spv::BuiltIn::ObjectRayOriginKHR: + case spv::BuiltIn::ObjectToWorldKHR: + case spv::BuiltIn::WorldToObjectKHR: + switch (stage) { + case spv::ExecutionModel::IntersectionKHR: + case spv::ExecutionModel::AnyHitKHR: + case spv::ExecutionModel::ClosestHitKHR: + return true; + default: + return false; + } + break; + case spv::BuiltIn::IncomingRayFlagsKHR: + case spv::BuiltIn::RayTminKHR: + case spv::BuiltIn::RayTmaxKHR: + case spv::BuiltIn::WorldRayDirectionKHR: + case spv::BuiltIn::WorldRayOriginKHR: + case spv::BuiltIn::CullMaskKHR: + switch (stage) { + case spv::ExecutionModel::IntersectionKHR: + case spv::ExecutionModel::AnyHitKHR: + case spv::ExecutionModel::ClosestHitKHR: + case spv::ExecutionModel::MissKHR: + return true; + default: + return false; + } + break; + case spv::BuiltIn::LaunchIdKHR: + case spv::BuiltIn::LaunchSizeKHR: + switch (stage) { + case spv::ExecutionModel::RayGenerationKHR: + case spv::ExecutionModel::IntersectionKHR: + case spv::ExecutionModel::AnyHitKHR: + case spv::ExecutionModel::ClosestHitKHR: + case spv::ExecutionModel::MissKHR: + case spv::ExecutionModel::CallableKHR: + return true; + default: + return false; + } + break; + default: + break; + } + return false; +} + +// Helper class managing validation of built-ins. +// TODO: Generic functionality of this class can be moved into +// ValidationState_t to be made available to other users. +class BuiltInsValidator { + public: + BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {} + + // Run validation. + spv_result_t Run(); + + private: + // Goes through all decorations in the module, if decoration is BuiltIn + // calls ValidateSingleBuiltInAtDefinition(). + spv_result_t ValidateBuiltInsAtDefinition(); + + // Validates the instruction defining an id with built-in decoration. + // Can be called multiple times for the same id, if multiple built-ins are + // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed. + spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSingleBuiltInAtDefinitionVulkan( + const Decoration& decoration, const Instruction& inst, + const spv::BuiltIn label); + + // The following section contains functions which are called when id defined + // by |inst| is decorated with BuiltIn |decoration|. + // Most functions are specific to a single built-in and have naming scheme: + // ValidateXYZAtDefinition. Some functions are common to multiple kinds of + // BuiltIn. + spv_result_t ValidateClipOrCullDistanceAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateHelperInvocationAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateLayerOrViewportIndexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePositionAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateVertexIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateLocalInvocationIndexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateBaseInstanceOrVertexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateDrawIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateViewIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateDeviceIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFragInvocationCountAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFragSizeAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFragStencilRefAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFullyCoveredAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. + spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst); + // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask, + // SubgroupLeMask. + spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for SubgroupLocalInvocationId, SubgroupSize. + spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for SubgroupId, NumSubgroups. + spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + + spv_result_t ValidatePrimitiveShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst); + + spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration, + const Instruction& inst); + + spv_result_t ValidateRayTracingBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst); + + spv_result_t ValidateMeshShadingEXTBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst); + + // The following section contains functions which are called when id defined + // by |referenced_inst| is + // 1. referenced by |referenced_from_inst| + // 2. dependent on |built_in_inst| which is decorated with BuiltIn + // |decoration|. Most functions are specific to a single built-in and have + // naming scheme: ValidateXYZAtReference. Some functions are common to + // multiple kinds of BuiltIn. + spv_result_t ValidateFragCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFragDepthAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFrontFacingAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateHelperInvocationAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateInvocationIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateInstanceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePatchVerticesAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePointCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePointSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePrimitiveIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSampleIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSampleMaskAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSamplePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateTessCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateTessLevelAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateLocalInvocationIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateVertexIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateLayerOrViewportIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateWorkgroupSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateClipOrCullDistanceAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateBaseInstanceOrVertexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateDrawIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateViewIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateDeviceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFragInvocationCountAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFragSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFragStencilRefAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFullyCoveredAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. + spv_result_t ValidateComputeShaderI32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Used for BaryCoord, BaryCoordNoPersp. + spv_result_t ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Used for SubgroupId and NumSubgroups. + spv_result_t ValidateComputeI32InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateNVSMOrARMCoreBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePrimitiveShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateRayTracingBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateMeshShadingEXTBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Validates that |built_in_inst| is not (even indirectly) referenced from + // within a function which can be called with |execution_model|. + // + // |vuid| - Vulkan ID for the error, or a negative value if none. + // |comment| - text explaining why the restriction was imposed. + // |decoration| - BuiltIn decoration which causes the restriction. + // |referenced_inst| - instruction which is dependent on |built_in_inst| and + // defines the id which was referenced. + // |referenced_from_inst| - instruction which references id defined by + // |referenced_inst| from within a function. + spv_result_t ValidateNotCalledWithExecutionModel( + int vuid, const char* comment, spv::ExecutionModel execution_model, + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // The following section contains functions which check that the decorated + // variable has the type specified in the function name. |diag| would be + // called with a corresponding error message, if validation is not successful. + spv_result_t ValidateBool( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateI32Arr( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateArrayedI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateOptionalArrayedI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type); + spv_result_t ValidateF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateF32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type); + spv_result_t ValidateF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateF32VecHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type); + // If |num_components| is zero, the number of components is not checked. + spv_result_t ValidateF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateF32ArrHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type); + spv_result_t ValidateF32Mat( + const Decoration& decoration, const Instruction& inst, + uint32_t req_num_rows, uint32_t req_num_columns, + const std::function& diag); + + // Generates strings like "Member #0 of struct ID <2>". + std::string GetDefinitionDesc(const Decoration& decoration, + const Instruction& inst) const; + + // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2> + // (OpTypeStruct) which is decorated with BuiltIn Position". + std::string GetReferenceDesc( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst, + spv::ExecutionModel execution_model = spv::ExecutionModel::Max) const; + + // Generates strings like "ID <51> (OpTypePointer) uses storage class + // UniformConstant". + std::string GetStorageClassDesc(const Instruction& inst) const; + + // Updates inner working of the class. Is called sequentially for every + // instruction. + void Update(const Instruction& inst); + + // Check if "inst" is an interface variable + // or type of a interface varibale of any mesh entry point + bool isMeshInterfaceVar(const Instruction& inst) { + auto getUnderlyingTypeId = [&](const Instruction* ifxVar) { + auto pointerTypeInst = _.FindDef(ifxVar->type_id()); + auto typeInst = _.FindDef(pointerTypeInst->GetOperandAs(2)); + while (typeInst->opcode() == spv::Op::OpTypeArray) { + typeInst = _.FindDef(typeInst->GetOperandAs(1)); + }; + return typeInst->id(); + }; + + for (const uint32_t entry_point : _.entry_points()) { + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(spv::ExecutionModel::MeshEXT) != models->end() || + models->find(spv::ExecutionModel::MeshNV) != models->end()) { + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + for (auto interface : desc.interfaces) { + if (inst.opcode() == spv::Op::OpTypeStruct) { + auto varInst = _.FindDef(interface); + if (inst.id() == getUnderlyingTypeId(varInst)) return true; + } else if (inst.id() == interface) { + return true; + } + } + } + } + } + return false; + } + + ValidationState_t& _; + + // Mapping id -> list of rules which validate instruction referencing the + // id. Rules can create new rules and add them to this container. + // Using std::map, and not std::unordered_map to avoid iterator invalidation + // during rehashing. + std::map>> + id_to_at_reference_checks_; + + // Id of the function we are currently inside. 0 if not inside a function. + uint32_t function_id_ = 0; + + // Entry points which can (indirectly) call the current function. + // The pointer either points to a vector inside to function_to_entry_points_ + // or to no_entry_points_. The pointer is guaranteed to never be null. + const std::vector no_entry_points; + const std::vector* entry_points_ = &no_entry_points; + + // Execution models with which the current function can be called. + std::set execution_models_; +}; + +void BuiltInsValidator::Update(const Instruction& inst) { + const spv::Op opcode = inst.opcode(); + if (opcode == spv::Op::OpFunction) { + // Entering a function. + assert(function_id_ == 0); + function_id_ = inst.id(); + execution_models_.clear(); + entry_points_ = &_.FunctionEntryPoints(function_id_); + // Collect execution models from all entry points from which the current + // function can be called. + for (const uint32_t entry_point : *entry_points_) { + if (const auto* models = _.GetExecutionModels(entry_point)) { + execution_models_.insert(models->begin(), models->end()); + } + } + } + + if (opcode == spv::Op::OpFunctionEnd) { + // Exiting a function. + assert(function_id_ != 0); + function_id_ = 0; + entry_points_ = &no_entry_points; + execution_models_.clear(); + } +} + +std::string BuiltInsValidator::GetDefinitionDesc( + const Decoration& decoration, const Instruction& inst) const { + std::ostringstream ss; + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + assert(inst.opcode() == spv::Op::OpTypeStruct); + ss << "Member #" << decoration.struct_member_index(); + ss << " of struct ID <" << inst.id() << ">"; + } else { + ss << GetIdDesc(inst); + } + return ss.str(); +} + +std::string BuiltInsValidator::GetReferenceDesc( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, const Instruction& referenced_from_inst, + spv::ExecutionModel execution_model) const { + std::ostringstream ss; + ss << GetIdDesc(referenced_from_inst) << " is referencing " + << GetIdDesc(referenced_inst); + if (built_in_inst.id() != referenced_inst.id()) { + ss << " which is dependent on " << GetIdDesc(built_in_inst); + } + + ss << " which is decorated with BuiltIn "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()); + if (function_id_) { + ss << " in function <" << function_id_ << ">"; + if (execution_model != spv::ExecutionModel::Max) { + ss << " called with execution model "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL, + uint32_t(execution_model)); + } + } + ss << "."; + return ss.str(); +} + +std::string BuiltInsValidator::GetStorageClassDesc( + const Instruction& inst) const { + std::ostringstream ss; + ss << GetIdDesc(inst) << " uses storage class "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS, + uint32_t(GetStorageClass(inst))); + ss << "."; + return ss.str(); +} + +spv_result_t BuiltInsValidator::ValidateBool( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (!_.IsBoolScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar."); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (!_.IsIntScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar."); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateI32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateI32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateI32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsIntScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width + << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateF32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsFloatScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not a float scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width + << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (!_.IsIntVectorType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector."); + } + + const uint32_t actual_num_components = _.GetDimension(underlying_type); + if (_.GetDimension(underlying_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateArrayedI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != spv::Op::OpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntVectorType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector."); + } + + const uint32_t actual_num_components = _.GetDimension(component_type); + if (_.GetDimension(component_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateF32VecHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32VecHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32VecHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsFloatVectorType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not a float vector."); + } + + const uint32_t actual_num_components = _.GetDimension(underlying_type); + if (_.GetDimension(underlying_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Arr( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != spv::Op::OpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntScalarType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " components are not int scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32ArrHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip an extra layer of arraying if present. + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + uint32_t subtype = _.FindDef(underlying_type)->word(2u); + if (_.GetIdOpcode(subtype) == spv::Op::OpTypeArray) { + underlying_type = subtype; + } + } + + return ValidateF32ArrHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32ArrHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type) { + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != spv::Op::OpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsFloatScalarType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " components are not float scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + if (num_components != 0) { + uint64_t actual_num_components = 0; + if (!_.EvalConstantValUint64(type_inst->word(3), &actual_num_components)) { + assert(0 && "Array type definition is corrupt"); + } + if (actual_num_components != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateF32Mat( + const Decoration& decoration, const Instruction& inst, + uint32_t req_num_rows, uint32_t req_num_columns, + const std::function& diag) { + uint32_t underlying_type = 0; + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + if (!_.GetMatrixTypeInfo(underlying_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != req_num_rows || num_cols != req_num_columns) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has columns " << num_cols + << " and rows " << num_rows << " not equal to expected " + << req_num_columns << "x" << req_num_rows << "."; + return diag(ss.str()); + } + + return ValidateF32VecHelper(decoration, inst, req_num_rows, diag, col_type); +} + +spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel( + int vuid, const char* comment, spv::ExecutionModel execution_model, + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (function_id_) { + if (execution_models_.count(execution_model)) { + const char* execution_model_str = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model)); + const char* built_in_str = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)decoration.builtin()); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << (vuid < 0 ? std::string("") : _.VkErrorID(vuid)) << comment + << " " << GetIdDesc(referenced_inst) << " depends on " + << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn " + << built_in_str << "." + << " Id <" << referenced_inst.id() << "> is later referenced by " + << GetIdDesc(referenced_from_inst) << " in function <" + << function_id_ << "> which is called with execution model " + << execution_model_str << "."; + } + } else { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + vuid, comment, execution_model, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) ? 4190 : 4199; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Input) { + assert(function_id_ == 0); + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) ? 4188 : 4197; + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Input storage class if execution model is " + "Vertex.", + spv::ExecutionModel::Vertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Input storage class if execution model is " + "MeshNV.", + spv::ExecutionModel::MeshNV, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Input storage class if execution model is " + "MeshEXT.", + spv::ExecutionModel::MeshEXT, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + if (storage_class == spv::StorageClass::Output) { + assert(function_id_ == 0); + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) ? 4189 : 4198; + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Output storage class if execution model is " + "Fragment.", + spv::ExecutionModel::Fragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Fragment: + case spv::ExecutionModel::Vertex: { + if (spv_result_t error = ValidateF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + break; + } + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The outer level of array is applied on the variable. + if (spv_result_t error = ValidateF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::ClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + } + break; + } + + default: { + uint32_t vuid = (decoration.builtin() == spv::BuiltIn::ClipDistance) + ? 4187 + : 4196; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Fragment, Vertex, " + "TessellationControl, TessellationEvaluation or Geometry " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 4, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4212) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FragCoord " + "variable needs to be a 4-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4211) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4210) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragCoord to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4215) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FragDepth " + "variable needs to be a 32-bit float scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragDepthAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragDepthAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4214) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragDepth to be only used for " + "variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4213) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragDepth to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + + for (const uint32_t entry_point : *entry_points_) { + // Every entry point from which this function is called needs to have + // Execution Mode DepthReplacing. + const auto* modes = _.GetExecutionModes(entry_point); + if (!modes || !modes->count(spv::ExecutionMode::DepthReplacing)) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4216) + << spvLogStringForEnv(_.context()->target_env) + << " spec requires DepthReplacing execution mode to be " + "declared when using BuiltIn FragDepth. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4231) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FrontFacing " + "variable needs to be a bool scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFrontFacingAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4230) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FrontFacing to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4229) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FrontFacing to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4241) + << "According to the Vulkan spec BuiltIn HelperInvocation " + "variable needs to be a bool scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateHelperInvocationAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4240) + << "Vulkan spec allows BuiltIn HelperInvocation to be only used " + "for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4239) + << "Vulkan spec allows BuiltIn HelperInvocation to be used only " + "with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4259) + << "According to the Vulkan spec BuiltIn InvocationId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateInvocationIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4258) + << "Vulkan spec allows BuiltIn InvocationId to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::TessellationControl && + execution_model != spv::ExecutionModel::Geometry) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4257) + << "Vulkan spec allows BuiltIn InvocationId to be used only " + "with TessellationControl or Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4265) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn InstanceIndex " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateInstanceIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4264) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn InstanceIndex to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Vertex) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4263) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn InstanceIndex to be used only " + "with Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4310) + << "According to the Vulkan spec BuiltIn PatchVertices " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidatePatchVerticesAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4309) + << "Vulkan spec allows BuiltIn PatchVertices to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::TessellationControl && + execution_model != spv::ExecutionModel::TessellationEvaluation) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4308) + << "Vulkan spec allows BuiltIn PatchVertices to be used only " + "with TessellationControl or TessellationEvaluation " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4313) + << "According to the Vulkan spec BuiltIn PointCoord " + "variable needs to be a 2-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidatePointCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePointCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4312) + << "Vulkan spec allows BuiltIn PointCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4311) + << "Vulkan spec allows BuiltIn PointCoord to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidatePointSizeAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePointSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4316) + << "Vulkan spec allows BuiltIn PointSize to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Input) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4315, + "Vulkan spec doesn't allow BuiltIn PointSize to be used for " + "variables with Input storage class if execution model is " + "Vertex.", + spv::ExecutionModel::Vertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Vertex: { + if (spv_result_t error = ValidateF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn PointSize " + "variable needs to be a 32-bit float scalar. " + << message; + })) { + return error; + } + break; + } + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: { + // PointSize can be a per-vertex variable for tessellation control, + // tessellation evaluation and geometry shader stages. In such cases + // variables will have an array of 32-bit floats. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The array is on the variable, so this must be a 32-bit float. + if (spv_result_t error = ValidateF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn " + "PointSize variable needs to be a 32-bit " + "float scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn " + "PointSize variable needs to be a 32-bit " + "float scalar. " + << message; + })) { + return error; + } + } + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4314) + << "Vulkan spec allows BuiltIn PointSize to be used only with " + "Vertex, TessellationControl, TessellationEvaluation or " + "Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePositionAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidatePositionAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4320) << "Vulkan spec allows BuiltIn Position to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Input) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319, + "Vulkan spec doesn't allow BuiltIn Position to be used " + "for variables " + "with Input storage class if execution model is Vertex.", + spv::ExecutionModel::Vertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319, + "Vulkan spec doesn't allow BuiltIn Position to be used " + "for variables " + "with Input storage class if execution model is MeshNV.", + spv::ExecutionModel::MeshNV, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319, + "Vulkan spec doesn't allow BuiltIn Position to be used " + "for variables " + "with Input storage class if execution model is MeshEXT.", + spv::ExecutionModel::MeshEXT, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Vertex: { + if (spv_result_t error = ValidateF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + break; + } + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: { + // Position can be a per-vertex variable for tessellation control, + // tessellation evaluation, geometry and mesh shader stages. In such + // cases variables will have an array of 4-component 32-bit float + // vectors. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The array is on the variable, so this must be a 4-component + // 32-bit float vector. + if (spv_result_t error = ValidateF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit " + "float vector. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit " + "float vector. " + << message; + })) { + return error; + } + } + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4318) + << "Vulkan spec allows BuiltIn Position to be used only " + "with Vertex, TessellationControl, TessellationEvaluation" + " or Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePositionAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + // PrimitiveId can be a per-primitive variable for mesh shader stage. + // In such cases variable will have an array of 32-bit integers. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // This must be a 32-bit int scalar. + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4337) + << "According to the Vulkan spec BuiltIn PrimitiveId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4337) + << "According to the Vulkan spec BuiltIn PrimitiveId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + if (_.HasCapability(spv::Capability::MeshShadingEXT)) { + if (isMeshInterfaceVar(inst) && + !_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7040) + << "According to the Vulkan spec the variable decorated with " + "Builtin PrimitiveId within the MeshEXT Execution Model must " + "also be decorated with the PerPrimitiveEXT decoration. "; + } + } + } + + // Seed at reference checks with this built-in. + return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn PrimitiveId to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Output) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "TessellationControl.", + spv::ExecutionModel::TessellationControl, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "TessellationEvaluation.", + spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "Fragment.", + spv::ExecutionModel::Fragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "IntersectionKHR.", + spv::ExecutionModel::IntersectionKHR, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "AnyHitKHR.", + spv::ExecutionModel::AnyHitKHR, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334, + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "ClosestHitKHR.", + spv::ExecutionModel::ClosestHitKHR, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Fragment: + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: + case spv::ExecutionModel::IntersectionKHR: + case spv::ExecutionModel::AnyHitKHR: + case spv::ExecutionModel::ClosestHitKHR: { + // Ok. + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4330) + << "Vulkan spec allows BuiltIn PrimitiveId to be used only " + "with Fragment, TessellationControl, " + "TessellationEvaluation, Geometry, MeshNV, MeshEXT, " + "IntersectionKHR, AnyHitKHR, and ClosestHitKHR execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4356) + << "According to the Vulkan spec BuiltIn SampleId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSampleIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSampleIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4355) + << "Vulkan spec allows BuiltIn SampleId to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4354) + << "Vulkan spec allows BuiltIn SampleId to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32Arr( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4359) + << "According to the Vulkan spec BuiltIn SampleMask " + "variable needs to be a 32-bit int array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSampleMaskAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4358) + << "Vulkan spec allows BuiltIn SampleMask to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4357) + << "Vulkan spec allows BuiltIn SampleMask to be used only " + "with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4362) + << "According to the Vulkan spec BuiltIn SamplePosition " + "variable needs to be a 2-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSamplePositionAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4361) + << "Vulkan spec allows BuiltIn SamplePosition to be only used " + "for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4360) + << "Vulkan spec allows BuiltIn SamplePosition to be used only " + "with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4389) + << "According to the Vulkan spec BuiltIn TessCoord " + "variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4388) + << "Vulkan spec allows BuiltIn TessCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::TessellationEvaluation) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4387) + << "Vulkan spec allows BuiltIn TessCoord to be used only with " + "TessellationEvaluation execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Arr( + decoration, inst, 4, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4393) + << "According to the Vulkan spec BuiltIn TessLevelOuter " + "variable needs to be a 4-component 32-bit float " + "array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessLevelAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Arr( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4397) + << "According to the Vulkan spec BuiltIn TessLevelOuter " + "variable needs to be a 2-component 32-bit float " + "array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessLevelAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Input) { + assert(function_id_ == 0); + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::TessLevelOuter) ? 4391 : 4395; + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used " + "for variables with Input storage class if execution model is " + "TessellationControl.", + spv::ExecutionModel::TessellationControl, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + if (storage_class == spv::StorageClass::Output) { + assert(function_id_ == 0); + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::TessLevelOuter) ? 4392 : 4396; + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used " + "for variables with Output storage class if execution model is " + "TessellationEvaluation.", + spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: { + // Ok. + break; + } + + default: { + uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::TessLevelOuter) ? 4390 : 4394; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with TessellationControl or " + "TessellationEvaluation execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4400) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn VertexIndex variable needs to be a " + "32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateVertexIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + (void)decoration; + if (spvIsVulkanEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Vulkan spec doesn't allow BuiltIn VertexId " + "to be used."; + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction&, + const Instruction& referenced_from_inst) { + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4399) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn VertexIndex to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Vertex) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4398) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn VertexIndex to be used only with " + "Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + // This can be a per-primitive variable for mesh shader stage. + // In such cases variable will have an array of 32-bit integers. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // This must be a 32-bit int scalar. + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + if (isMeshInterfaceVar(inst) && + _.HasCapability(spv::Capability::MeshShadingEXT) && + !_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]); + uint32_t vkerrid = (label == spv::BuiltIn::Layer) ? 7039 : 7060; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vkerrid) + << "According to the Vulkan spec the variable decorated with " + "Builtin " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " within the MeshEXT Execution Model must also be decorated " + "with the PerPrimitiveEXT decoration. "; + } + } + + // Seed at reference checks with this built-in. + return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == spv::StorageClass::Input) { + assert(function_id_ == 0); + for (const auto em : + {spv::ExecutionModel::Vertex, spv::ExecutionModel::TessellationEvaluation, + spv::ExecutionModel::Geometry, spv::ExecutionModel::MeshNV, + spv::ExecutionModel::MeshEXT}) { + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, + this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4274 : 4406), + "Vulkan spec doesn't allow BuiltIn Layer and " + "ViewportIndex to be " + "used for variables with Input storage class if " + "execution model is Vertex, TessellationEvaluation, " + "Geometry, MeshNV or MeshEXT.", + em, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + } + + if (storage_class == spv::StorageClass::Output) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, + this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4275 : 4407), + "Vulkan spec doesn't allow BuiltIn Layer and " + "ViewportIndex to be " + "used for variables with Output storage class if " + "execution model is " + "Fragment.", + spv::ExecutionModel::Fragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::Fragment: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: + // Ok. + break; + case spv::ExecutionModel::Vertex: + case spv::ExecutionModel::TessellationEvaluation: { + if (!_.HasCapability(spv::Capability::ShaderViewportIndexLayerEXT)) { + if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex && + _.HasCapability(spv::Capability::ShaderViewportIndex)) + break; // Ok + if (spv::BuiltIn(operand) == spv::BuiltIn::Layer && + _.HasCapability(spv::Capability::ShaderLayer)) + break; // Ok + + const char* capability = "ShaderViewportIndexLayerEXT"; + + if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex) + capability = "ShaderViewportIndexLayerEXT or ShaderViewportIndex"; + if (spv::BuiltIn(operand) == spv::BuiltIn::Layer) + capability = "ShaderViewportIndexLayerEXT or ShaderLayer"; + + uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4273 : 4405; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Using BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " in Vertex or Tessellation execution model requires the " + << capability << " capability."; + } + break; + } + default: { + uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4272 : 4404; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex, TessellationEvaluation, " + "Geometry, or Fragment execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 3, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a 3-component 32-bit int " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute || + execution_model == spv::ExecutionModel::TaskNV || + execution_model == spv::ExecutionModel::MeshNV || + execution_model == spv::ExecutionModel::TaskEXT || + execution_model == spv::ExecutionModel::MeshEXT; + + if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or" + << " TaskEXT execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 32-bit int " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateComputeI32InputAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute || + execution_model == spv::ExecutionModel::TaskNV || + execution_model == spv::ExecutionModel::MeshNV || + execution_model == spv::ExecutionModel::TaskEXT || + execution_model == spv::ExecutionModel::MeshEXT; + if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or " + << "TaskEXT execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 32-bit int. " << message; + })) { + return error; + } + + const spv::StorageClass storage_class = GetStorageClass(inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, inst, inst, inst) << " " + << GetStorageClassDesc(inst); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 4, + [this, &inst, builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 4-component 32-bit int " + "vector. " + << message; + })) { + return error; + } + + const spv::StorageClass storage_class = GetStorageClass(inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, inst, inst, inst) << " " + << GetStorageClassDesc(inst); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Vulkan requires 32-bit int, but Universal has no restrictions + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 3, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4427) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn WorkgroupSize variable needs to be a " + "3-component 32-bit int vector. " + << message; + })) { + return error; + } + } + + if (!spvOpcodeIsConstant(inst.opcode())) { + if (spvIsVulkanEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4426) + << "Vulkan spec requires BuiltIn WorkgroupSize to be a " + "constant. " + << GetIdDesc(inst) << " is not a constant."; + } + } else if (inst.opcode() == spv::Op::OpConstantComposite) { + // can only validate product if static and not spec constant + if (_.FindDef(inst.word(3))->opcode() == spv::Op::OpConstant && + _.FindDef(inst.word(4))->opcode() == spv::Op::OpConstant && + _.FindDef(inst.word(5))->opcode() == spv::Op::OpConstant) { + uint64_t x_size, y_size, z_size; + // ValidateI32Vec above confirms there will be 3 words to read + bool static_x = _.EvalConstantValUint64(inst.word(3), &x_size); + bool static_y = _.EvalConstantValUint64(inst.word(4), &y_size); + bool static_z = _.EvalConstantValUint64(inst.word(5), &z_size); + if (static_x && static_y && static_z && + ((x_size * y_size * z_size) == 0)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "WorkgroupSize decorations must not have a static " + "product of zero (X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size << ")."; + } + } + } + + // Seed at reference checks with this built-in. + return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::GLCompute && + execution_model != spv::ExecutionModel::TaskNV && + execution_model != spv::ExecutionModel::MeshNV && + execution_model != spv::ExecutionModel::TaskEXT && + execution_model != spv::ExecutionModel::MeshEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4425) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or " + << "TaskEXT execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.builtin() == spv::BuiltIn::BaseInstance) ? 4183 + : 4186; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4182 : 4185; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Vertex) { + uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4181 : 4184; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4209) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateDrawIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Vertex && + execution_model != spv::ExecutionModel::MeshNV && + execution_model != spv::ExecutionModel::TaskNV && + execution_model != spv::ExecutionModel::MeshEXT && + execution_model != spv::ExecutionModel::TaskEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex, MeshNV, TaskNV , MeshEXT or" + << " TaskEXT execution " + "model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4403) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateViewIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateViewIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model == spv::ExecutionModel::GLCompute) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be not be used with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4206) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateDeviceIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = (uint32_t)decoration.builtin(); + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration, + const Instruction& inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, &builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration, + const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 2, + [this, &inst, &builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a 2-component 32-bit int vector. " + << message; + })) { + return error; + } + } + + return ValidateFragSizeAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration, + const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateI( + decoration, inst, + [this, &inst, &builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a int scalar. " + << message; + })) { + return error; + } + } + + return ValidateFragStencilRefAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Output) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration, + const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst, &builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " variable needs to be a bool scalar. " + << message; + })) { + return error; + } + } + + return ValidateFullyCoveredAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " to be used only with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateNVSMOrARMCoreBuiltinsAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4486) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + if (isMeshInterfaceVar(inst) && + _.HasCapability(spv::Capability::MeshShadingEXT) && + !_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7059) + << "The variable decorated with PrimitiveShadingRateKHR " + "within the MeshEXT Execution Model must also be " + "decorated with the PerPrimitiveEXT decoration"; + } + } + + // Seed at reference checks with this built-in. + return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be only used for variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case spv::ExecutionModel::Vertex: + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::MeshNV: + case spv::ExecutionModel::MeshEXT: + break; + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be used only with Vertex, Geometry, MeshNV or MeshEXT " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4492) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateShadingRateAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::Fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be used only with the Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + switch (builtin) { + case spv::BuiltIn::HitTNV: + case spv::BuiltIn::RayTminKHR: + case spv::BuiltIn::RayTmaxKHR: + // f32 scalar + if (spv_result_t error = ValidateF32( + decoration, inst, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 32-bit float scalar. " + << message; + })) { + return error; + } + break; + case spv::BuiltIn::HitKindKHR: + case spv::BuiltIn::InstanceCustomIndexKHR: + case spv::BuiltIn::InstanceId: + case spv::BuiltIn::RayGeometryIndexKHR: + case spv::BuiltIn::IncomingRayFlagsKHR: + case spv::BuiltIn::CullMaskKHR: + // i32 scalar + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + break; + case spv::BuiltIn::ObjectRayDirectionKHR: + case spv::BuiltIn::ObjectRayOriginKHR: + case spv::BuiltIn::WorldRayDirectionKHR: + case spv::BuiltIn::WorldRayOriginKHR: + // f32 vec3 + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + break; + case spv::BuiltIn::LaunchIdKHR: + case spv::BuiltIn::LaunchSizeKHR: + // i32 vec3 + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 3, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a 3-component 32-bit int " + "vector. " + << message; + })) { + return error; + } + break; + case spv::BuiltIn::ObjectToWorldKHR: + case spv::BuiltIn::WorldToObjectKHR: + // f32 mat4x3 + if (spv_result_t error = ValidateF32Mat( + decoration, inst, 3, 4, + [this, &inst, + builtin](const std::string& message) -> spv_result_t { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin)) + << " variable needs to be a matrix with" + << " 4 columns of 3-component vectors of 32-bit " + "floats. " + << message; + })) { + return error; + } + break; + default: + assert(0 && "Unexpected ray tracing builtin"); + break; + } + } + + // Seed at reference checks with this built-in. + return ValidateRayTracingBuiltinsAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Input) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const spv::ExecutionModel execution_model : execution_models_) { + if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec does not allow BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " to be used with the execution model " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_EXECUTION_MODEL, + uint32_t(execution_model)) + << ".\n" + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateRayTracingBuiltinsAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); + switch (builtin) { + case spv::BuiltIn::PrimitivePointIndicesEXT: + if (spv_result_t error = ValidateI32Arr( + decoration, inst, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 32-bit int array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::PrimitiveLineIndicesEXT: + if (spv_result_t error = ValidateArrayedI32Vec( + decoration, inst, 2, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 2-component 32-bit int " + "array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::PrimitiveTriangleIndicesEXT: + if (spv_result_t error = ValidateArrayedI32Vec( + decoration, inst, 3, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a 3-component 32-bit int " + "array." + << message; + })) { + return error; + } + break; + case spv::BuiltIn::CullPrimitiveEXT: + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst, &decoration, + &vuid](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + (uint32_t)decoration.builtin()) + << " variable needs to be a boolean value " + "array." + << message; + })) { + return error; + } + if (!_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7038) + << "The variable decorated with CullPrimitiveEXT within the " + "MeshEXT Execution Model must also be decorated with the " + "PerPrimitiveEXT decoration "; + } + break; + default: + assert(0 && "Unexpected mesh EXT builtin"); + } + for (const uint32_t entry_point : _.entry_points()) { + const auto* modes = _.GetExecutionModes(entry_point); + uint64_t maxOutputPrimitives = _.GetOutputPrimitivesEXT(entry_point); + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + uint64_t primitiveArrayDim = 0; + if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(3u); + if (!_.EvalConstantValUint64(underlying_type, &primitiveArrayDim)) { + assert(0 && "Array type definition is corrupt"); + } + } + switch (builtin) { + case spv::BuiltIn::PrimitivePointIndicesEXT: + if (!modes || !modes->count(spv::ExecutionMode::OutputPoints)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7042) + << "The PrimitivePointIndicesEXT decoration must be used " + "with " + "the OutputPoints Execution Mode. "; + } + if (primitiveArrayDim && primitiveArrayDim != maxOutputPrimitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7046) + << "The size of the array decorated with " + "PrimitivePointIndicesEXT must match the value specified " + "by OutputPrimitivesEXT. "; + } + break; + case spv::BuiltIn::PrimitiveLineIndicesEXT: + if (!modes || !modes->count(spv::ExecutionMode::OutputLinesEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7048) + << "The PrimitiveLineIndicesEXT decoration must be used " + "with " + "the OutputLinesEXT Execution Mode. "; + } + if (primitiveArrayDim && primitiveArrayDim != maxOutputPrimitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7052) + << "The size of the array decorated with " + "PrimitiveLineIndicesEXT must match the value specified " + "by OutputPrimitivesEXT. "; + } + break; + case spv::BuiltIn::PrimitiveTriangleIndicesEXT: + if (!modes || !modes->count(spv::ExecutionMode::OutputTrianglesEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7054) + << "The PrimitiveTriangleIndicesEXT decoration must be used " + "with " + "the OutputTrianglesEXT Execution Mode. "; + } + if (primitiveArrayDim && primitiveArrayDim != maxOutputPrimitives) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7058) + << "The size of the array decorated with " + "PrimitiveTriangleIndicesEXT must match the value " + "specified " + "by OutputPrimitivesEXT. "; + } + break; + default: + break; // no validation rules + } + } + } + // Seed at reference checks with this built-in. + return ValidateMeshShadingEXTBuiltinsAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const spv::BuiltIn builtin = decoration.builtin(); + const spv::StorageClass storage_class = + GetStorageClass(referenced_from_inst); + if (storage_class != spv::StorageClass::Max && + storage_class != spv::StorageClass::Output) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " to be only used for variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + for (const spv::ExecutionModel execution_model : execution_models_) { + if (execution_model != spv::ExecutionModel::MeshEXT) { + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + uint32_t(builtin)) + << " to be used only with MeshEXT execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( + const Decoration& decoration, const Instruction& inst) { + const spv::BuiltIn label = decoration.builtin(); + // Universial checks + if (label == spv::BuiltIn::WorkgroupSize) { + return ValidateWorkgroupSizeAtDefinition(decoration, inst); + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + return ValidateSingleBuiltInAtDefinitionVulkan(decoration, inst, label); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinitionVulkan( + const Decoration& decoration, const Instruction& inst, + const spv::BuiltIn label) { + // If you are adding a new BuiltIn enum, please register it here. + // If the newly added enum has validation rules associated with it + // consider leaving a TODO and/or creating an issue. + switch (label) { + case spv::BuiltIn::ClipDistance: + case spv::BuiltIn::CullDistance: { + return ValidateClipOrCullDistanceAtDefinition(decoration, inst); + } + case spv::BuiltIn::FragCoord: { + return ValidateFragCoordAtDefinition(decoration, inst); + } + case spv::BuiltIn::FragDepth: { + return ValidateFragDepthAtDefinition(decoration, inst); + } + case spv::BuiltIn::FrontFacing: { + return ValidateFrontFacingAtDefinition(decoration, inst); + } + case spv::BuiltIn::GlobalInvocationId: + case spv::BuiltIn::LocalInvocationId: + case spv::BuiltIn::NumWorkgroups: + case spv::BuiltIn::WorkgroupId: { + return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst); + } + case spv::BuiltIn::BaryCoordKHR: + case spv::BuiltIn::BaryCoordNoPerspKHR: { + return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst); + } + case spv::BuiltIn::HelperInvocation: { + return ValidateHelperInvocationAtDefinition(decoration, inst); + } + case spv::BuiltIn::InvocationId: { + return ValidateInvocationIdAtDefinition(decoration, inst); + } + case spv::BuiltIn::InstanceIndex: { + return ValidateInstanceIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::Layer: + case spv::BuiltIn::ViewportIndex: { + return ValidateLayerOrViewportIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::PatchVertices: { + return ValidatePatchVerticesAtDefinition(decoration, inst); + } + case spv::BuiltIn::PointCoord: { + return ValidatePointCoordAtDefinition(decoration, inst); + } + case spv::BuiltIn::PointSize: { + return ValidatePointSizeAtDefinition(decoration, inst); + } + case spv::BuiltIn::Position: { + return ValidatePositionAtDefinition(decoration, inst); + } + case spv::BuiltIn::PrimitiveId: { + return ValidatePrimitiveIdAtDefinition(decoration, inst); + } + case spv::BuiltIn::SampleId: { + return ValidateSampleIdAtDefinition(decoration, inst); + } + case spv::BuiltIn::SampleMask: { + return ValidateSampleMaskAtDefinition(decoration, inst); + } + case spv::BuiltIn::SamplePosition: { + return ValidateSamplePositionAtDefinition(decoration, inst); + } + case spv::BuiltIn::SubgroupId: + case spv::BuiltIn::NumSubgroups: { + return ValidateComputeI32InputAtDefinition(decoration, inst); + } + case spv::BuiltIn::SubgroupLocalInvocationId: + case spv::BuiltIn::SubgroupSize: { + return ValidateI32InputAtDefinition(decoration, inst); + } + case spv::BuiltIn::SubgroupEqMask: + case spv::BuiltIn::SubgroupGeMask: + case spv::BuiltIn::SubgroupGtMask: + case spv::BuiltIn::SubgroupLeMask: + case spv::BuiltIn::SubgroupLtMask: { + return ValidateI32Vec4InputAtDefinition(decoration, inst); + } + case spv::BuiltIn::TessCoord: { + return ValidateTessCoordAtDefinition(decoration, inst); + } + case spv::BuiltIn::TessLevelOuter: { + return ValidateTessLevelOuterAtDefinition(decoration, inst); + } + case spv::BuiltIn::TessLevelInner: { + return ValidateTessLevelInnerAtDefinition(decoration, inst); + } + case spv::BuiltIn::VertexIndex: { + return ValidateVertexIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::VertexId: { + return ValidateVertexIdAtDefinition(decoration, inst); + } + case spv::BuiltIn::LocalInvocationIndex: { + return ValidateLocalInvocationIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::CoreIDARM: + case spv::BuiltIn::CoreCountARM: + case spv::BuiltIn::CoreMaxIDARM: + case spv::BuiltIn::WarpIDARM: + case spv::BuiltIn::WarpMaxIDARM: + case spv::BuiltIn::WarpsPerSMNV: + case spv::BuiltIn::SMCountNV: + case spv::BuiltIn::WarpIDNV: + case spv::BuiltIn::SMIDNV: { + return ValidateNVSMOrARMCoreBuiltinsAtDefinition(decoration, inst); + } + case spv::BuiltIn::BaseInstance: + case spv::BuiltIn::BaseVertex: { + return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst); + } + case spv::BuiltIn::DrawIndex: { + return ValidateDrawIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::ViewIndex: { + return ValidateViewIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::DeviceIndex: { + return ValidateDeviceIndexAtDefinition(decoration, inst); + } + case spv::BuiltIn::FragInvocationCountEXT: { + // alias spv::BuiltIn::InvocationsPerPixelNV + return ValidateFragInvocationCountAtDefinition(decoration, inst); + } + case spv::BuiltIn::FragSizeEXT: { + // alias spv::BuiltIn::FragmentSizeNV + return ValidateFragSizeAtDefinition(decoration, inst); + } + case spv::BuiltIn::FragStencilRefEXT: { + return ValidateFragStencilRefAtDefinition(decoration, inst); + } + case spv::BuiltIn::FullyCoveredEXT:{ + return ValidateFullyCoveredAtDefinition(decoration, inst); + } + // Ray tracing builtins + case spv::BuiltIn::HitKindKHR: // alias spv::BuiltIn::HitKindNV + case spv::BuiltIn::HitTNV: // NOT present in KHR + case spv::BuiltIn::InstanceId: + case spv::BuiltIn::LaunchIdKHR: // alias spv::BuiltIn::LaunchIdNV + case spv::BuiltIn::LaunchSizeKHR: // alias spv::BuiltIn::LaunchSizeNV + case spv::BuiltIn::WorldRayOriginKHR: // alias spv::BuiltIn::WorldRayOriginNV + case spv::BuiltIn::WorldRayDirectionKHR: // alias spv::BuiltIn::WorldRayDirectionNV + case spv::BuiltIn::ObjectRayOriginKHR: // alias spv::BuiltIn::ObjectRayOriginNV + case spv::BuiltIn::ObjectRayDirectionKHR: // alias + // spv::BuiltIn::ObjectRayDirectionNV + case spv::BuiltIn::RayTminKHR: // alias spv::BuiltIn::RayTminNV + case spv::BuiltIn::RayTmaxKHR: // alias spv::BuiltIn::RayTmaxNV + case spv::BuiltIn::InstanceCustomIndexKHR: // alias + // spv::BuiltIn::InstanceCustomIndexNV + case spv::BuiltIn::ObjectToWorldKHR: // alias spv::BuiltIn::ObjectToWorldNV + case spv::BuiltIn::WorldToObjectKHR: // alias spv::BuiltIn::WorldToObjectNV + case spv::BuiltIn::IncomingRayFlagsKHR: // alias spv::BuiltIn::IncomingRayFlagsNV + case spv::BuiltIn::RayGeometryIndexKHR: // NOT present in NV + case spv::BuiltIn::CullMaskKHR: { + return ValidateRayTracingBuiltinsAtDefinition(decoration, inst); + } + case spv::BuiltIn::CullPrimitiveEXT: + case spv::BuiltIn::PrimitivePointIndicesEXT: + case spv::BuiltIn::PrimitiveLineIndicesEXT: + case spv::BuiltIn::PrimitiveTriangleIndicesEXT: { + return ValidateMeshShadingEXTBuiltinsAtDefinition(decoration, inst); + } + case spv::BuiltIn::PrimitiveShadingRateKHR: { + return ValidatePrimitiveShadingRateAtDefinition(decoration, inst); + } + case spv::BuiltIn::ShadingRateKHR: { + return ValidateShadingRateAtDefinition(decoration, inst); + } + default: + // No validation rules (for the moment). + break; + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() { + for (const auto& kv : _.id_decorations()) { + const uint32_t id = kv.first; + const auto& decorations = kv.second; + if (decorations.empty()) { + continue; + } + + const Instruction* inst = _.FindDef(id); + assert(inst); + + for (const auto& decoration : kv.second) { + if (decoration.dec_type() != spv::Decoration::BuiltIn) { + continue; + } + + if (spv_result_t error = + ValidateSingleBuiltInAtDefinition(decoration, *inst)) { + return error; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::Run() { + // First pass: validate all built-ins at definition and seed + // id_to_at_reference_checks_ with built-ins. + if (auto error = ValidateBuiltInsAtDefinition()) { + return error; + } + + if (id_to_at_reference_checks_.empty()) { + // No validation tasks were seeded. Nothing else to do. + return SPV_SUCCESS; + } + + // Second pass: validate every id reference in the module using + // rules in id_to_at_reference_checks_. + for (const Instruction& inst : _.ordered_instructions()) { + Update(inst); + + std::set already_checked; + + for (const auto& operand : inst.operands()) { + if (!spvIsIdType(operand.type)) { + // Not id. + continue; + } + + const uint32_t id = inst.word(operand.offset); + if (id == inst.id()) { + // No need to check result id. + continue; + } + + if (!already_checked.insert(id).second) { + // The instruction has already referenced this id. + continue; + } + + // Instruction references the id. Run all checks associated with the id + // on the instruction. id_to_at_reference_checks_ can be modified in the + // process, iterators are safe because it's a tree-based map. + const auto it = id_to_at_reference_checks_.find(id); + if (it != id_to_at_reference_checks_.end()) { + for (const auto& check : it->second) { + if (spv_result_t error = check(inst)) { + return error; + } + } + } + } + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of built-in variables. +spv_result_t ValidateBuiltIns(ValidationState_t& _) { + BuiltInsValidator validator(_); + return validator.Run(); +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_capability.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_capability.cpp new file mode 100644 index 00000000..75b96c96 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_capability.cpp @@ -0,0 +1,456 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates OpCapability instruction. + +#include +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) { + switch (spv::Capability(capability)) { + case spv::Capability::Matrix: + case spv::Capability::Shader: + case spv::Capability::InputAttachment: + case spv::Capability::Sampled1D: + case spv::Capability::Image1D: + case spv::Capability::SampledBuffer: + case spv::Capability::ImageBuffer: + case spv::Capability::ImageQuery: + case spv::Capability::DerivativeControl: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_0(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::DeviceGroup: + case spv::Capability::MultiView: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_1(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::ShaderNonUniform: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_3(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_2(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::DotProduct: + case spv::Capability::DotProductInputAll: + case spv::Capability::DotProductInput4x8Bit: + case spv::Capability::DotProductInput4x8BitPacked: + case spv::Capability::VulkanMemoryModel: + case spv::Capability::VulkanMemoryModelDeviceScope: + case spv::Capability::PhysicalStorageBufferAddresses: + case spv::Capability::DemoteToHelperInvocation: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_4(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_3(capability)) return true; + switch (spv::Capability(capability)) { + case spv::Capability::UniformBufferArrayDynamicIndexing: + case spv::Capability::SampledImageArrayDynamicIndexing: + case spv::Capability::StorageBufferArrayDynamicIndexing: + case spv::Capability::StorageImageArrayDynamicIndexing: + case spv::Capability::Int16: + case spv::Capability::StorageBuffer16BitAccess: + case spv::Capability::VariablePointers: + case spv::Capability::VariablePointersStorageBuffer: + case spv::Capability::UniformTexelBufferArrayDynamicIndexing: + case spv::Capability::StorageTexelBufferArrayDynamicIndexing: + case spv::Capability::Int8: + case spv::Capability::StorageBuffer8BitAccess: + case spv::Capability::FloatControls2: + case spv::Capability::SampleRateShading: + case spv::Capability::StorageImageExtendedFormats: + case spv::Capability::ImageGatherExtended: + return true; + default: + break; + } + return false; +} + +bool IsSupportOptionalVulkan_1_0(uint32_t capability) { + switch (spv::Capability(capability)) { + case spv::Capability::Geometry: + case spv::Capability::Tessellation: + case spv::Capability::Float64: + case spv::Capability::Int64: + case spv::Capability::Int16: + case spv::Capability::TessellationPointSize: + case spv::Capability::GeometryPointSize: + case spv::Capability::ImageGatherExtended: + case spv::Capability::StorageImageMultisample: + case spv::Capability::UniformBufferArrayDynamicIndexing: + case spv::Capability::SampledImageArrayDynamicIndexing: + case spv::Capability::StorageBufferArrayDynamicIndexing: + case spv::Capability::StorageImageArrayDynamicIndexing: + case spv::Capability::ClipDistance: + case spv::Capability::CullDistance: + case spv::Capability::ImageCubeArray: + case spv::Capability::SampleRateShading: + case spv::Capability::SparseResidency: + case spv::Capability::MinLod: + case spv::Capability::SampledCubeArray: + case spv::Capability::ImageMSArray: + case spv::Capability::StorageImageExtendedFormats: + case spv::Capability::InterpolationFunction: + case spv::Capability::StorageImageReadWithoutFormat: + case spv::Capability::StorageImageWriteWithoutFormat: + case spv::Capability::MultiViewport: + case spv::Capability::Int64Atomics: + case spv::Capability::TransformFeedback: + case spv::Capability::GeometryStreams: + case spv::Capability::Float16: + case spv::Capability::Int8: + case spv::Capability::BFloat16TypeKHR: + return true; + default: + break; + } + return false; +} + +bool IsSupportOptionalVulkan_1_1(uint32_t capability) { + if (IsSupportOptionalVulkan_1_0(capability)) return true; + + switch (spv::Capability(capability)) { + case spv::Capability::GroupNonUniform: + case spv::Capability::GroupNonUniformVote: + case spv::Capability::GroupNonUniformArithmetic: + case spv::Capability::GroupNonUniformBallot: + case spv::Capability::GroupNonUniformShuffle: + case spv::Capability::GroupNonUniformShuffleRelative: + case spv::Capability::GroupNonUniformClustered: + case spv::Capability::GroupNonUniformQuad: + case spv::Capability::DrawParameters: + // Alias spv::Capability::StorageBuffer16BitAccess. + case spv::Capability::StorageUniformBufferBlock16: + // Alias spv::Capability::UniformAndStorageBuffer16BitAccess. + case spv::Capability::StorageUniform16: + case spv::Capability::StoragePushConstant16: + case spv::Capability::StorageInputOutput16: + case spv::Capability::DeviceGroup: + case spv::Capability::MultiView: + case spv::Capability::VariablePointersStorageBuffer: + case spv::Capability::VariablePointers: + return true; + default: + break; + } + return false; +} + +bool IsSupportOptionalVulkan_1_2(uint32_t capability) { + if (IsSupportOptionalVulkan_1_1(capability)) return true; + + switch (spv::Capability(capability)) { + case spv::Capability::DenormPreserve: + case spv::Capability::DenormFlushToZero: + case spv::Capability::SignedZeroInfNanPreserve: + case spv::Capability::RoundingModeRTE: + case spv::Capability::RoundingModeRTZ: + case spv::Capability::VulkanMemoryModel: + case spv::Capability::VulkanMemoryModelDeviceScope: + case spv::Capability::StorageBuffer8BitAccess: + case spv::Capability::UniformAndStorageBuffer8BitAccess: + case spv::Capability::StoragePushConstant8: + case spv::Capability::ShaderViewportIndex: + case spv::Capability::ShaderLayer: + case spv::Capability::PhysicalStorageBufferAddresses: + case spv::Capability::RuntimeDescriptorArray: + case spv::Capability::UniformTexelBufferArrayDynamicIndexing: + case spv::Capability::StorageTexelBufferArrayDynamicIndexing: + case spv::Capability::UniformBufferArrayNonUniformIndexing: + case spv::Capability::SampledImageArrayNonUniformIndexing: + case spv::Capability::StorageBufferArrayNonUniformIndexing: + case spv::Capability::StorageImageArrayNonUniformIndexing: + case spv::Capability::InputAttachmentArrayNonUniformIndexing: + case spv::Capability::UniformTexelBufferArrayNonUniformIndexing: + case spv::Capability::StorageTexelBufferArrayNonUniformIndexing: + return true; + default: + break; + } + return false; +} + +// Vulkan 1.3 only added required features. +bool IsSupportOptionalVulkan_1_3(uint32_t capability) { + return IsSupportOptionalVulkan_1_2(capability); +} + +// Vulkan 1.4 only added required features. +bool IsSupportOptionalVulkan_1_4(uint32_t capability) { + return IsSupportOptionalVulkan_1_3(capability); +} + +bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) { + switch (spv::Capability(capability)) { + case spv::Capability::Addresses: + case spv::Capability::Float16Buffer: + case spv::Capability::Int16: + case spv::Capability::Int8: + case spv::Capability::Kernel: + case spv::Capability::Linkage: + case spv::Capability::Vector16: + return true; + case spv::Capability::Int64: + return !embedded_profile; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) { + if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true; + + switch (spv::Capability(capability)) { + case spv::Capability::DeviceEnqueue: + case spv::Capability::GenericPointer: + case spv::Capability::Groups: + case spv::Capability::Pipes: + return true; + default: + break; + } + return false; +} + +bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) { + if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true; + + switch (spv::Capability(capability)) { + case spv::Capability::SubgroupDispatch: + case spv::Capability::PipeStorage: + return true; + default: + break; + } + return false; +} + +bool IsSupportOptionalOpenCL_1_2(uint32_t capability) { + switch (spv::Capability(capability)) { + case spv::Capability::ImageBasic: + case spv::Capability::Float64: + return true; + default: + break; + } + return false; +} + +// Checks if |capability| was enabled by extension. +bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { + spv_operand_desc operand_desc = nullptr; + _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &operand_desc); + + // operand_desc is expected to be not null, otherwise validator would have + // failed at an earlier stage. This 'assert' is 'just in case'. + assert(operand_desc); + + ExtensionSet operand_exts(operand_desc->numExtensions, + operand_desc->extensions); + if (operand_exts.empty()) return false; + + return _.HasAnyOfExtensions(operand_exts); +} + +bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _, + uint32_t capability) { + if (_.HasCapability(spv::Capability::ImageBasic)) { + switch (spv::Capability(capability)) { + case spv::Capability::LiteralSampler: + case spv::Capability::Sampled1D: + case spv::Capability::Image1D: + case spv::Capability::SampledBuffer: + case spv::Capability::ImageBuffer: + return true; + default: + break; + } + return false; + } + return false; +} + +bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _, + uint32_t capability) { + if (_.HasCapability(spv::Capability::ImageBasic)) { + switch (spv::Capability(capability)) { + case spv::Capability::ImageReadWrite: + case spv::Capability::LiteralSampler: + case spv::Capability::Sampled1D: + case spv::Capability::Image1D: + case spv::Capability::SampledBuffer: + case spv::Capability::ImageBuffer: + return true; + default: + break; + } + return false; + } + return false; +} + +} // namespace + +// Validates that capability declarations use operands allowed in the current +// context. +spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) { + if (inst->opcode() != spv::Op::OpCapability) return SPV_SUCCESS; + + assert(inst->operands().size() == 1); + + const spv_parsed_operand_t& operand = inst->operand(0); + + assert(operand.num_words == 1); + assert(operand.offset < inst->words().size()); + + const uint32_t capability = inst->word(operand.offset); + const auto capability_str = [&_, capability]() { + spv_operand_desc desc = nullptr; + if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &desc) != SPV_SUCCESS || + !desc) { + return std::string("Unknown"); + } + return std::string(desc->name); + }; + + const auto env = _.context()->target_env; + const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 || + env == SPV_ENV_OPENCL_EMBEDDED_2_0 || + env == SPV_ENV_OPENCL_EMBEDDED_2_1 || + env == SPV_ENV_OPENCL_EMBEDDED_2_2; + const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full"; + if (env == SPV_ENV_VULKAN_1_0) { + if (!IsSupportGuaranteedVulkan_1_0(capability) && + !IsSupportOptionalVulkan_1_0(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.0 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_1) { + if (!IsSupportGuaranteedVulkan_1_1(capability) && + !IsSupportOptionalVulkan_1_1(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.1 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_2) { + if (!IsSupportGuaranteedVulkan_1_2(capability) && + !IsSupportOptionalVulkan_1_2(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.2 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_3) { + if (!IsSupportGuaranteedVulkan_1_3(capability) && + !IsSupportOptionalVulkan_1_3(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.3 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_4) { + if (!IsSupportGuaranteedVulkan_1_4(capability) && + !IsSupportOptionalVulkan_1_4(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.4 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) { + if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_1_2(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 1.2 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 || + env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) { + if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) { + if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 2.2 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_cfg.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_cfg.cpp new file mode 100644 index 00000000..b7e570de --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_cfg.cpp @@ -0,0 +1,1279 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_validator_options.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { + auto block = inst->block(); + size_t num_in_ops = inst->words().size() - 3; + if (num_in_ops % 2 != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi does not have an equal number of incoming values and " + "basic blocks."; + } + + if (_.IsVoidType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpPhi must not have void result type"; + } + if (_.IsPointerType(inst->type_id()) && + _.addressing_model() == spv::AddressingModel::Logical) { + if (!_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using pointers with OpPhi requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + } + } + + const Instruction* type_inst = _.FindDef(inst->type_id()); + assert(type_inst); + const spv::Op type_opcode = type_inst->opcode(); + + if (!_.options()->before_hlsl_legalization && + !_.HasCapability(spv::Capability::BindlessTextureNV)) { + if (type_opcode == spv::Op::OpTypeSampledImage || + (_.HasCapability(spv::Capability::Shader) && + (type_opcode == spv::Op::OpTypeImage || + type_opcode == spv::Op::OpTypeSampler))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type cannot be Op" << spvOpcodeString(type_opcode); + } + } + + // Create a uniqued vector of predecessor ids for comparison against + // incoming values. OpBranchConditional %cond %label %label produces two + // predecessors in the CFG. + std::vector pred_ids; + std::transform(block->predecessors()->begin(), block->predecessors()->end(), + std::back_inserter(pred_ids), + [](const BasicBlock* b) { return b->id(); }); + std::sort(pred_ids.begin(), pred_ids.end()); + pred_ids.erase(std::unique(pred_ids.begin(), pred_ids.end()), pred_ids.end()); + + size_t num_edges = num_in_ops / 2; + if (num_edges != pred_ids.size()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's number of incoming blocks (" << num_edges + << ") does not match block's predecessor count (" + << block->predecessors()->size() << ")."; + } + + std::unordered_set observed_predecessors; + + for (size_t i = 3; i < inst->words().size(); ++i) { + auto inc_id = inst->word(i); + if (i % 2 == 1) { + // Incoming value type must match the phi result type. + auto inc_type_id = _.GetTypeId(inc_id); + if (inst->type_id() != inc_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's result type " << _.getIdName(inst->type_id()) + << " does not match incoming value " << _.getIdName(inc_id) + << " type " << _.getIdName(inc_type_id) << "."; + } + } else { + if (_.GetIdOpcode(inc_id) != spv::Op::OpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's incoming basic block " << _.getIdName(inc_id) + << " is not an OpLabel."; + } + + // Incoming basic block must be an immediate predecessor of the phi's + // block. + if (!std::binary_search(pred_ids.begin(), pred_ids.end(), inc_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's incoming basic block " << _.getIdName(inc_id) + << " is not a predecessor of " << _.getIdName(block->id()) + << "."; + } + + // We must not have already seen this predecessor as one of the phi's + // operands. + if (observed_predecessors.count(inc_id) != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi references incoming basic block " + << _.getIdName(inc_id) << " multiple times."; + } + + // Note the fact that we have now observed this predecessor. + observed_predecessors.insert(inc_id); + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBranch(ValidationState_t& _, const Instruction* inst) { + // target operands must be OpLabel + const auto id = inst->GetOperandAs(0); + const auto target = _.FindDef(id); + if (!target || spv::Op::OpLabel != target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "'Target Label' operands for OpBranch must be the ID " + "of an OpLabel instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBranchConditional(ValidationState_t& _, + const Instruction* inst) { + // num_operands is either 3 or 5 --- if 5, the last two need to be literal + // integers + const auto num_operands = inst->operands().size(); + if (num_operands != 3 && num_operands != 5) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpBranchConditional requires either 3 or 5 parameters"; + } + + // grab the condition operand and check that it is a bool + const auto cond_id = inst->GetOperandAs(0); + const auto cond_op = _.FindDef(cond_id); + if (!cond_op || !cond_op->type_id() || + !_.IsBoolScalarType(cond_op->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Condition operand for " + "OpBranchConditional must be " + "of boolean type"; + } + + // target operands must be OpLabel + // note that we don't need to check that the target labels are in the same + // function, + // PerformCfgChecks already checks for that + const auto true_id = inst->GetOperandAs(1); + const auto true_target = _.FindDef(true_id); + if (!true_target || spv::Op::OpLabel != true_target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The 'True Label' operand for OpBranchConditional must be the " + "ID of an OpLabel instruction"; + } + + const auto false_id = inst->GetOperandAs(2); + const auto false_target = _.FindDef(false_id); + if (!false_target || spv::Op::OpLabel != false_target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The 'False Label' operand for OpBranchConditional must be the " + "ID of an OpLabel instruction"; + } + + // A similar requirement for SPV_KHR_maximal_reconvergence is deferred until + // entry point call trees have been reconrded. + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, True Label and False Label must be " + "different labels"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) { + const auto num_operands = inst->operands().size(); + // At least two operands (selector, default), any more than that are + // literal/target. + + const auto sel_type_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(sel_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Selector type must be OpTypeInt"; + } + + const auto default_label = _.FindDef(inst->GetOperandAs(1)); + if (default_label->opcode() != spv::Op::OpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Default must be an OpLabel instruction"; + } + + // target operands must be OpLabel + for (size_t i = 2; i < num_operands; i += 2) { + // literal, id + const auto id = inst->GetOperandAs(i + 1); + const auto target = _.FindDef(id); + if (!target || spv::Op::OpLabel != target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "'Target Label' operands for OpSwitch must be IDs of an " + "OpLabel instruction"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateReturnValue(ValidationState_t& _, + const Instruction* inst) { + const auto value_id = inst->GetOperandAs(0); + const auto value = _.FindDef(value_id); + if (!value || !value->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue Value " << _.getIdName(value_id) + << " does not represent a value."; + } + auto value_type = _.FindDef(value->type_id()); + if (!value_type || spv::Op::OpTypeVoid == value_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue value's type " + << _.getIdName(value->type_id()) << " is missing or void."; + } + + if (_.addressing_model() == spv::AddressingModel::Logical && + (spv::Op::OpTypePointer == value_type->opcode() || + spv::Op::OpTypeUntypedPointerKHR == value_type->opcode()) && + !_.features().variable_pointers && !_.options()->relax_logical_pointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue value's type " + << _.getIdName(value->type_id()) + << " is a pointer, which is invalid in the Logical addressing " + "model."; + } + + const auto function = inst->function(); + const auto return_type = _.FindDef(function->GetResultTypeId()); + if (!return_type || return_type->id() != value_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue Value " << _.getIdName(value_id) + << "s type does not match OpFunction's return type."; + } + + return SPV_SUCCESS; +} + +uint32_t operator>>(const spv::LoopControlShift& lhs, + const spv::LoopControlShift& rhs) { + return uint32_t(lhs) >> uint32_t(rhs); +} + +spv_result_t ValidateLoopMerge(ValidationState_t& _, const Instruction* inst) { + const auto merge_id = inst->GetOperandAs(0); + const auto merge = _.FindDef(merge_id); + if (!merge || merge->opcode() != spv::Op::OpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block " << _.getIdName(merge_id) << " must be an OpLabel"; + } + if (merge_id == inst->block()->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block may not be the block containing the OpLoopMerge\n"; + } + + const auto continue_id = inst->GetOperandAs(1); + const auto continue_target = _.FindDef(continue_id); + if (!continue_target || continue_target->opcode() != spv::Op::OpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Continue Target " << _.getIdName(continue_id) + << " must be an OpLabel"; + } + + if (merge_id == continue_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block and Continue Target must be different ids"; + } + + const auto loop_control = inst->GetOperandAs(2); + if ((loop_control >> spv::LoopControlShift::Unroll) & 0x1 && + (loop_control >> spv::LoopControlShift::DontUnroll) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Unroll and DontUnroll loop controls must not both be specified"; + } + if ((loop_control >> spv::LoopControlShift::DontUnroll) & 0x1 && + (loop_control >> spv::LoopControlShift::PeelCount) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PeelCount and DontUnroll " + "loop controls must not " + "both be specified"; + } + if ((loop_control >> spv::LoopControlShift::DontUnroll) & 0x1 && + (loop_control >> spv::LoopControlShift::PartialCount) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PartialCount and " + "DontUnroll loop controls " + "must not both be specified"; + } + + uint32_t operand = 3; + if ((loop_control >> spv::LoopControlShift::DependencyLength) & 0x1) { + ++operand; + } + if ((loop_control >> spv::LoopControlShift::MinIterations) & 0x1) { + ++operand; + } + if ((loop_control >> spv::LoopControlShift::MaxIterations) & 0x1) { + ++operand; + } + if ((loop_control >> spv::LoopControlShift::IterationMultiple) & 0x1) { + if (inst->operands().size() < operand || + inst->GetOperandAs(operand) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "IterationMultiple loop " + "control operand must be " + "greater than zero"; + } + ++operand; + } + if ((loop_control >> spv::LoopControlShift::PeelCount) & 0x1) { + ++operand; + } + if ((loop_control >> spv::LoopControlShift::PartialCount) & 0x1) { + ++operand; + } + + // That the right number of operands is present is checked by the parser. The + // above code tracks operands for expanded validation checking in the future. + + return SPV_SUCCESS; +} + +} // namespace + +void printDominatorList(const BasicBlock& b) { + std::cout << b.id() << " is dominated by: "; + const BasicBlock* bb = &b; + while (bb->immediate_dominator() != bb) { + bb = bb->immediate_dominator(); + std::cout << bb->id() << " "; + } +} + +#define CFG_ASSERT(ASSERT_FUNC, TARGET) \ + if (spv_result_t rcode = ASSERT_FUNC(_, TARGET)) return rcode + +spv_result_t FirstBlockAssert(ValidationState_t& _, uint32_t target) { + if (_.current_function().IsFirstBlock(target)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(_.current_function().id())) + << "First block " << _.getIdName(target) << " of function " + << _.getIdName(_.current_function().id()) << " is targeted by block " + << _.getIdName(_.current_function().current_block()->id()); + } + return SPV_SUCCESS; +} + +spv_result_t MergeBlockAssert(ValidationState_t& _, uint32_t merge_block) { + if (_.current_function().IsBlockType(merge_block, kBlockTypeMerge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(_.current_function().id())) + << "Block " << _.getIdName(merge_block) + << " is already a merge block for another header"; + } + return SPV_SUCCESS; +} + +/// Update the continue construct's exit blocks once the backedge blocks are +/// identified in the CFG. +void UpdateContinueConstructExitBlocks( + Function& function, + const std::vector>& back_edges) { + auto& constructs = function.constructs(); + // TODO(umar): Think of a faster way to do this + for (auto& edge : back_edges) { + uint32_t back_edge_block_id; + uint32_t loop_header_block_id; + std::tie(back_edge_block_id, loop_header_block_id) = edge; + auto is_this_header = [=](Construct& c) { + return c.type() == ConstructType::kLoop && + c.entry_block()->id() == loop_header_block_id; + }; + + for (auto construct : constructs) { + if (is_this_header(construct)) { + Construct* continue_construct = + construct.corresponding_constructs().back(); + assert(continue_construct->type() == ConstructType::kContinue); + + BasicBlock* back_edge_block; + std::tie(back_edge_block, std::ignore) = + function.GetBlock(back_edge_block_id); + continue_construct->set_exit(back_edge_block); + } + } + } +} + +std::tuple ConstructNames( + ConstructType type) { + std::string construct_name, header_name, exit_name; + + switch (type) { + case ConstructType::kSelection: + construct_name = "selection"; + header_name = "selection header"; + exit_name = "merge block"; + break; + case ConstructType::kLoop: + construct_name = "loop"; + header_name = "loop header"; + exit_name = "merge block"; + break; + case ConstructType::kContinue: + construct_name = "continue"; + header_name = "continue target"; + exit_name = "back-edge block"; + break; + case ConstructType::kCase: + construct_name = "case"; + header_name = "case entry block"; + exit_name = "case exit block"; + break; + default: + assert(1 == 0 && "Not defined type"); + } + + return std::make_tuple(construct_name, header_name, exit_name); +} + +/// Constructs an error message for construct validation errors +std::string ConstructErrorString(const Construct& construct, + const std::string& header_string, + const std::string& exit_string, + const std::string& dominate_text) { + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + + // TODO(umar): Add header block for continue constructs to error message + return "The " + construct_name + " construct with the " + header_name + " " + + header_string + " " + dominate_text + " the " + exit_name + " " + + exit_string; +} + +// Finds the fall through case construct of |target_block| and records it in +// |case_fall_through|. Returns SPV_ERROR_INVALID_CFG if the case construct +// headed by |target_block| branches to multiple case constructs. +spv_result_t FindCaseFallThrough( + ValidationState_t& _, BasicBlock* target_block, uint32_t* case_fall_through, + const Construct& switch_construct, + const std::unordered_set& case_targets) { + const auto* merge = switch_construct.exit_block(); + std::vector stack; + stack.push_back(target_block); + std::unordered_set visited; + bool target_reachable = target_block->structurally_reachable(); + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block == merge) continue; + + if (!visited.insert(block).second) continue; + + if (target_reachable && block->structurally_reachable() && + target_block->structurally_dominates(*block)) { + // Still in the case construct. + for (auto successor : *block->successors()) { + stack.push_back(successor); + } + } else { + // Exiting the case construct to non-merge block. + if (!case_targets.count(block->id())) { + // We have already filtered out the following: + // * The switch's merge + // * Other case targets + // * Blocks in the same case construct + // + // So the only remaining valid branches are the structured exits from + // the overall selection construct of the switch. + if (switch_construct.IsStructuredExit(_, block)) { + continue; + } + + return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) + << "Case construct that targets " + << _.getIdName(target_block->id()) + << " has invalid branch to block " << _.getIdName(block->id()) + << " (not another case construct, corresponding merge, outer " + "loop merge or outer loop continue)"; + } + + if (*case_fall_through == 0u) { + if (target_block != block) { + *case_fall_through = block->id(); + } + } else if (*case_fall_through != block->id()) { + // Case construct has at most one branch to another case construct. + return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) + << "Case construct that targets " + << _.getIdName(target_block->id()) + << " has branches to multiple other case construct targets " + << _.getIdName(*case_fall_through) << " and " + << _.getIdName(block->id()); + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, + const Construct& switch_construct) { + const auto* header = switch_construct.entry_block(); + const auto* merge = switch_construct.exit_block(); + const auto* switch_inst = header->terminator(); + std::unordered_set case_targets; + for (uint32_t i = 1; i < switch_inst->operands().size(); i += 2) { + uint32_t target = switch_inst->GetOperandAs(i); + if (target != merge->id()) case_targets.insert(target); + } + // Tracks how many times each case construct is targeted by another case + // construct. + std::map num_fall_through_targeted; + uint32_t default_case_fall_through = 0u; + uint32_t default_target = switch_inst->GetOperandAs(1u); + bool default_appears_multiple_times = false; + for (uint32_t i = 3; i < switch_inst->operands().size(); i += 2) { + if (default_target == switch_inst->GetOperandAs(i)) { + default_appears_multiple_times = true; + break; + } + } + + std::unordered_map seen_to_fall_through; + for (uint32_t i = 1; i < switch_inst->operands().size(); i += 2) { + uint32_t target = switch_inst->GetOperandAs(i); + if (target == merge->id()) continue; + + uint32_t case_fall_through = 0u; + auto seen_iter = seen_to_fall_through.find(target); + if (seen_iter == seen_to_fall_through.end()) { + const auto target_block = function->GetBlock(target).first; + // OpSwitch must dominate all its case constructs. + if (header->structurally_reachable() && + target_block->structurally_reachable() && + !header->structurally_dominates(*target_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, header->label()) + << "Switch header " << _.getIdName(header->id()) + << " does not structurally dominate its case construct " + << _.getIdName(target); + } + + if (auto error = FindCaseFallThrough(_, target_block, &case_fall_through, + switch_construct, case_targets)) { + return error; + } + + // Track how many time the fall through case has been targeted. + if (case_fall_through != 0u) { + auto where = num_fall_through_targeted.lower_bound(case_fall_through); + if (where == num_fall_through_targeted.end() || + where->first != case_fall_through) { + num_fall_through_targeted.insert( + where, std::make_pair(case_fall_through, 1)); + } else { + where->second++; + } + } + seen_to_fall_through.insert(std::make_pair(target, case_fall_through)); + } else { + case_fall_through = seen_iter->second; + } + + if (case_fall_through == default_target && + !default_appears_multiple_times) { + case_fall_through = default_case_fall_through; + } + if (case_fall_through != 0u) { + bool is_default = i == 1; + if (is_default) { + default_case_fall_through = case_fall_through; + } else { + // Allow code like: + // case x: + // case y: + // ... + // case z: + // + // Where x and y target the same block and fall through to z. + uint32_t j = i; + while ((j + 2 < switch_inst->operands().size()) && + target == switch_inst->GetOperandAs(j + 2)) { + j += 2; + } + // If Target T1 branches to Target T2, or if Target T1 branches to the + // Default target and the Default target branches to Target T2, then T1 + // must immediately precede T2 in the list of OpSwitch Target operands. + if ((switch_inst->operands().size() < j + 2) || + (case_fall_through != switch_inst->GetOperandAs(j + 2))) { + return _.diag(SPV_ERROR_INVALID_CFG, switch_inst) + << "Case construct that targets " << _.getIdName(target) + << " has branches to the case construct that targets " + << _.getIdName(case_fall_through) + << ", but does not immediately precede it in the " + "OpSwitch's target list"; + } + } + } + } + + // Each case construct must be branched to by at most one other case + // construct. + for (const auto& pair : num_fall_through_targeted) { + if (pair.second > 1) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pair.first)) + << "Multiple case constructs have branches to the case construct " + "that targets " + << _.getIdName(pair.first); + } + } + + return SPV_SUCCESS; +} + +// Validates that all CFG divergences (i.e. conditional branch or switch) are +// structured correctly. Either divergence is preceded by a merge instruction +// or the divergence introduces at most one unseen label. +spv_result_t ValidateStructuredSelections( + ValidationState_t& _, const std::vector& postorder) { + std::unordered_set seen; + for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) { + const auto* block = *iter; + const auto* terminator = block->terminator(); + if (!terminator) continue; + const auto index = terminator - &_.ordered_instructions()[0]; + auto* merge = &_.ordered_instructions()[index - 1]; + // Marks merges and continues as seen. + if (merge->opcode() == spv::Op::OpSelectionMerge) { + seen.insert(merge->GetOperandAs(0)); + } else if (merge->opcode() == spv::Op::OpLoopMerge) { + seen.insert(merge->GetOperandAs(0)); + seen.insert(merge->GetOperandAs(1)); + } else { + // Only track the pointer if it is a merge instruction. + merge = nullptr; + } + + // Skip unreachable blocks. + if (!block->structurally_reachable()) continue; + + if (terminator->opcode() == spv::Op::OpBranchConditional) { + const auto true_label = terminator->GetOperandAs(1); + const auto false_label = terminator->GetOperandAs(2); + // Mark the upcoming blocks as seen now, but only error out if this block + // was missing a merge instruction and both labels hadn't been seen + // previously. + const bool true_label_unseen = seen.insert(true_label).second; + const bool false_label_unseen = seen.insert(false_label).second; + if ((!merge || merge->opcode() == spv::Op::OpLoopMerge) && + true_label_unseen && false_label_unseen) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } else if (terminator->opcode() == spv::Op::OpSwitch) { + if (!merge) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "OpSwitch must be preceded by an OpSelectionMerge " + "instruction"; + } + // Mark the targets as seen. + for (uint32_t i = 1; i < terminator->operands().size(); i += 2) { + const auto target = terminator->GetOperandAs(i); + seen.insert(target); + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t StructuredControlFlowChecks( + ValidationState_t& _, Function* function, + const std::vector>& back_edges, + const std::vector& postorder) { + /// Check all backedges target only loop headers and have exactly one + /// back-edge branching to it + + // Map a loop header to blocks with back-edges to the loop header. + std::map> loop_latch_blocks; + for (auto back_edge : back_edges) { + uint32_t back_edge_block; + uint32_t header_block; + std::tie(back_edge_block, header_block) = back_edge; + if (!function->IsBlockType(header_block, kBlockTypeLoop)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(back_edge_block)) + << "Back-edges (" << _.getIdName(back_edge_block) << " -> " + << _.getIdName(header_block) + << ") can only be formed between a block and a loop header."; + } + loop_latch_blocks[header_block].insert(back_edge_block); + } + + // Check the loop headers have exactly one back-edge branching to it + for (BasicBlock* loop_header : function->ordered_blocks()) { + if (!loop_header->structurally_reachable()) continue; + if (!loop_header->is_type(kBlockTypeLoop)) continue; + auto loop_header_id = loop_header->id(); + auto num_latch_blocks = loop_latch_blocks[loop_header_id].size(); + if (num_latch_blocks != 1) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(loop_header_id)) + << "Loop header " << _.getIdName(loop_header_id) + << " is targeted by " << num_latch_blocks + << " back-edge blocks but the standard requires exactly one"; + } + } + + // Check construct rules + for (const Construct& construct : function->constructs()) { + auto header = construct.entry_block(); + if (!header->structurally_reachable()) continue; + auto merge = construct.exit_block(); + + if (!merge) { + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + return _.diag(SPV_ERROR_INTERNAL, _.FindDef(header->id())) + << "Construct " + construct_name + " with " + header_name + " " + + _.getIdName(header->id()) + " does not have a " + + exit_name + ". This may be a bug in the validator."; + } + + // If the header is reachable, the merge is guaranteed to be structurally + // reachable. + if (!header->structurally_dominates(*merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not structurally dominate"); + } + + // If it's really a merge block for a selection or loop, then it must be + // *strictly* structrually dominated by the header. + if (construct.ExitBlockIsMergeBlock() && (header == merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not strictly structurally dominate"); + } + + // Check post-dominance for continue constructs. But dominance and + // post-dominance only make sense when the construct is reachable. + if (construct.type() == ConstructType::kContinue) { + if (!merge->structurally_postdominates(*header)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "is not structurally post dominated by"); + } + } + + Construct::ConstructBlockSet construct_blocks = construct.blocks(function); + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + for (auto block : construct_blocks) { + // Check that all exits from the construct are via structured exits. + for (auto succ : *block->successors()) { + if (!construct_blocks.count(succ) && + !construct.IsStructuredExit(_, succ)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "block " << _.getIdName(block->id()) << " exits the " + << construct_name << " headed by " + << _.getIdName(header->id()) + << ", but not via a structured exit"; + } + } + if (block == header) continue; + // Check that for all non-header blocks, all predecessors are within this + // construct. + for (auto pred : *block->predecessors()) { + if (pred->structurally_reachable() && !construct_blocks.count(pred)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id())) + << "block " << pred->id() << " branches to the " + << construct_name << " construct, but not to the " + << header_name << " " << header->id(); + } + } + + if (block->is_type(BlockType::kBlockTypeSelection) || + block->is_type(BlockType::kBlockTypeLoop)) { + size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1; + const auto& merge_inst = _.ordered_instructions()[index]; + if (merge_inst.opcode() == spv::Op::OpSelectionMerge || + merge_inst.opcode() == spv::Op::OpLoopMerge) { + uint32_t merge_id = merge_inst.GetOperandAs(0); + auto merge_block = function->GetBlock(merge_id).first; + if (merge_block->structurally_reachable() && + !construct_blocks.count(merge_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "Header block " << _.getIdName(block->id()) + << " is contained in the " << construct_name + << " construct headed by " << _.getIdName(header->id()) + << ", but its merge block " << _.getIdName(merge_id) + << " is not"; + } + } + } + } + + if (construct.type() == ConstructType::kLoop) { + // If the continue target differs from the loop header, then check that + // all edges into the continue construct come from within the loop. + const auto index = header->terminator() - &_.ordered_instructions()[0]; + const auto& merge_inst = _.ordered_instructions()[index - 1]; + const auto continue_id = merge_inst.GetOperandAs(1); + const auto* continue_inst = _.FindDef(continue_id); + // OpLabel instructions aren't stored as part of the basic block for + // legacy reaasons. Grab the next instruction and use it's block pointer + // instead. + const auto next_index = + (continue_inst - &_.ordered_instructions()[0]) + 1; + const auto& next_inst = _.ordered_instructions()[next_index]; + const auto* continue_target = next_inst.block(); + if (header->id() != continue_id) { + for (auto pred : *continue_target->predecessors()) { + if (!pred->structurally_reachable()) { + continue; + } + // Ignore back-edges from within the continue construct. + bool is_back_edge = false; + for (auto back_edge : back_edges) { + uint32_t back_edge_block; + uint32_t header_block; + std::tie(back_edge_block, header_block) = back_edge; + if (header_block == continue_id && back_edge_block == pred->id()) + is_back_edge = true; + } + if (!construct_blocks.count(pred) && !is_back_edge) { + return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator()) + << "Block " << _.getIdName(pred->id()) + << " branches to the loop continue target " + << _.getIdName(continue_id) + << ", but is not contained in the associated loop construct " + << _.getIdName(header->id()); + } + } + } + } + + // Checks rules for case constructs. + if (construct.type() == ConstructType::kSelection && + header->terminator()->opcode() == spv::Op::OpSwitch) { + if (auto error = StructuredSwitchChecks(_, function, construct)) { + return error; + } + } + } + + if (auto error = ValidateStructuredSelections(_, postorder)) { + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t MaximalReconvergenceChecks(ValidationState_t& _) { + // Find all the entry points with the MaximallyReconvergencesKHR execution + // mode. + std::unordered_set maximal_funcs; + std::unordered_set maximal_entry_points; + for (auto entry_point : _.entry_points()) { + const auto* exec_modes = _.GetExecutionModes(entry_point); + if (exec_modes && + exec_modes->count(spv::ExecutionMode::MaximallyReconvergesKHR)) { + maximal_entry_points.insert(entry_point); + maximal_funcs.insert(entry_point); + } + } + + if (maximal_entry_points.empty()) { + return SPV_SUCCESS; + } + + // Find all the functions reachable from a maximal reconvergence entry point. + for (const auto& func : _.functions()) { + const auto& entry_points = _.EntryPointReferences(func.id()); + for (auto id : entry_points) { + if (maximal_entry_points.count(id)) { + maximal_funcs.insert(func.id()); + break; + } + } + } + + // Check for conditional branches with the same true and false targets. + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() == spv::Op::OpBranchConditional) { + const auto true_id = inst.GetOperandAs(1); + const auto false_id = inst.GetOperandAs(2); + if (true_id == false_id && maximal_funcs.count(inst.function()->id())) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << "In entry points using the MaximallyReconvergesKHR execution " + "mode, True Label and False Label must be different labels"; + } + } + } + + // Check for invalid multiple predecessors. Only loop headers, continue + // targets, merge targets or switch targets or defaults may have multiple + // unique predecessors. + for (const auto& func : _.functions()) { + if (!maximal_funcs.count(func.id())) continue; + + for (const auto* block : func.ordered_blocks()) { + std::unordered_set unique_preds; + const auto* preds = block->predecessors(); + if (!preds) continue; + + for (const auto* pred : *preds) { + unique_preds.insert(pred->id()); + } + if (unique_preds.size() < 2) continue; + + const auto* terminator = block->terminator(); + const auto index = terminator - &_.ordered_instructions()[0]; + const auto* pre_terminator = &_.ordered_instructions()[index - 1]; + if (pre_terminator->opcode() == spv::Op::OpLoopMerge) continue; + + const auto* label = _.FindDef(block->id()); + bool ok = false; + for (const auto& pair : label->uses()) { + const auto* use_inst = pair.first; + switch (use_inst->opcode()) { + case spv::Op::OpSelectionMerge: + case spv::Op::OpLoopMerge: + case spv::Op::OpSwitch: + ok = true; + break; + default: + break; + } + } + if (!ok) { + return _.diag(SPV_ERROR_INVALID_CFG, label) + << "In entry points using the MaximallyReconvergesKHR " + "execution mode, this basic block must not have multiple " + "unique predecessors"; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t PerformCfgChecks(ValidationState_t& _) { + for (auto& function : _.functions()) { + // Check all referenced blocks are defined within a function + if (function.undefined_block_count() != 0) { + std::string undef_blocks("{"); + bool first = true; + for (auto undefined_block : function.undefined_blocks()) { + undef_blocks += _.getIdName(undefined_block); + if (!first) { + undef_blocks += " "; + } + first = false; + } + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(function.id())) + << "Block(s) " << undef_blocks << "}" + << " are referenced but not defined in function " + << _.getIdName(function.id()); + } + + // Set each block's immediate dominator. + // + // We want to analyze all the blocks in the function, even in degenerate + // control flow cases including unreachable blocks. So use the augmented + // CFG to ensure we cover all the blocks. + std::vector postorder; + auto ignore_block = [](const BasicBlock*) {}; + auto no_terminal_blocks = [](const BasicBlock*) { return false; }; + if (!function.ordered_blocks().empty()) { + /// calculate dominators + CFA::DepthFirstTraversal( + function.first_block(), function.AugmentedCFGSuccessorsFunction(), + ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); }, + no_terminal_blocks); + auto edges = CFA::CalculateDominators( + postorder, function.AugmentedCFGPredecessorsFunction()); + for (auto edge : edges) { + if (edge.first != edge.second) + edge.first->SetImmediateDominator(edge.second); + } + } + + auto& blocks = function.ordered_blocks(); + if (!blocks.empty()) { + // Check if the order of blocks in the binary appear before the blocks + // they dominate + for (auto block = begin(blocks) + 1; block != end(blocks); ++block) { + if (auto idom = (*block)->immediate_dominator()) { + if (idom != function.pseudo_entry_block() && + block == std::find(begin(blocks), block, idom)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(idom->id())) + << "Block " << _.getIdName((*block)->id()) + << " appears in the binary before its dominator " + << _.getIdName(idom->id()); + } + } + } + // If we have structured control flow, check that no block has a control + // flow nesting depth larger than the limit. + if (_.HasCapability(spv::Capability::Shader)) { + const int control_flow_nesting_depth_limit = + _.options()->universal_limits_.max_control_flow_nesting_depth; + for (auto block = begin(blocks); block != end(blocks); ++block) { + if (function.GetBlockDepth(*block) > + control_flow_nesting_depth_limit) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef((*block)->id())) + << "Maximum Control Flow nesting depth exceeded."; + } + } + } + } + + /// Structured control flow checks are only required for shader capabilities + if (_.HasCapability(spv::Capability::Shader)) { + // Calculate structural dominance. + postorder.clear(); + std::vector postdom_postorder; + std::vector> back_edges; + if (!function.ordered_blocks().empty()) { + /// calculate dominators + CFA::DepthFirstTraversal( + function.first_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postorder.push_back(b); }, + no_terminal_blocks); + auto edges = CFA::CalculateDominators( + postorder, function.AugmentedStructuralCFGPredecessorsFunction()); + for (auto edge : edges) { + if (edge.first != edge.second) + edge.first->SetImmediateStructuralDominator(edge.second); + } + + /// calculate post dominators + CFA::DepthFirstTraversal( + function.pseudo_exit_block(), + function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, + no_terminal_blocks); + auto postdom_edges = CFA::CalculateDominators( + postdom_postorder, + function.AugmentedStructuralCFGSuccessorsFunction()); + for (auto edge : postdom_edges) { + edge.first->SetImmediateStructuralPostDominator(edge.second); + } + /// calculate back edges. + CFA::DepthFirstTraversal( + function.pseudo_entry_block(), + function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block, + ignore_block, + [&](const BasicBlock* from, const BasicBlock* to) { + // A back edge must be a real edge. Since the augmented successors + // contain structural edges, filter those from consideration. + for (const auto* succ : *(from->successors())) { + if (succ == to) back_edges.emplace_back(from->id(), to->id()); + } + }, + no_terminal_blocks); + } + UpdateContinueConstructExitBlocks(function, back_edges); + + if (auto error = + StructuredControlFlowChecks(_, &function, back_edges, postorder)) + return error; + } + } + + if (auto error = MaximalReconvergenceChecks(_)) { + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { + spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpLabel: + if (auto error = _.current_function().RegisterBlock(inst->id())) + return error; + + // TODO(github:1661) This should be done in the + // ValidationState::RegisterInstruction method but because of the order of + // passes the OpLabel ends up not being part of the basic block it starts. + _.current_function().current_block()->set_label(inst); + break; + case spv::Op::OpLoopMerge: { + uint32_t merge_block = inst->GetOperandAs(0); + uint32_t continue_block = inst->GetOperandAs(1); + CFG_ASSERT(MergeBlockAssert, merge_block); + + if (auto error = _.current_function().RegisterLoopMerge(merge_block, + continue_block)) + return error; + } break; + case spv::Op::OpSelectionMerge: { + uint32_t merge_block = inst->GetOperandAs(0); + CFG_ASSERT(MergeBlockAssert, merge_block); + + if (auto error = _.current_function().RegisterSelectionMerge(merge_block)) + return error; + } break; + case spv::Op::OpBranch: { + uint32_t target = inst->GetOperandAs(0); + CFG_ASSERT(FirstBlockAssert, target); + + _.current_function().RegisterBlockEnd({target}); + } break; + case spv::Op::OpBranchConditional: { + uint32_t tlabel = inst->GetOperandAs(1); + uint32_t flabel = inst->GetOperandAs(2); + CFG_ASSERT(FirstBlockAssert, tlabel); + CFG_ASSERT(FirstBlockAssert, flabel); + + _.current_function().RegisterBlockEnd({tlabel, flabel}); + } break; + + case spv::Op::OpSwitch: { + std::vector cases; + for (size_t i = 1; i < inst->operands().size(); i += 2) { + uint32_t target = inst->GetOperandAs(i); + CFG_ASSERT(FirstBlockAssert, target); + cases.push_back(target); + } + _.current_function().RegisterBlockEnd({cases}); + } break; + case spv::Op::OpReturn: { + const uint32_t return_type = _.current_function().GetResultTypeId(); + const Instruction* return_type_inst = _.FindDef(return_type); + assert(return_type_inst); + if (return_type_inst->opcode() != spv::Op::OpTypeVoid) + return _.diag(SPV_ERROR_INVALID_CFG, inst) + << "OpReturn can only be called from a function with void " + << "return type."; + _.current_function().RegisterBlockEnd(std::vector()); + break; + } + case spv::Op::OpKill: + case spv::Op::OpReturnValue: + case spv::Op::OpUnreachable: + case spv::Op::OpTerminateInvocation: + case spv::Op::OpIgnoreIntersectionKHR: + case spv::Op::OpTerminateRayKHR: + case spv::Op::OpEmitMeshTasksEXT: + _.current_function().RegisterBlockEnd(std::vector()); + // Ops with dedicated passes check for the Execution Model there + if (opcode == spv::Op::OpKill) { + _.current_function().RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + "OpKill requires Fragment execution model"); + } + if (opcode == spv::Op::OpTerminateInvocation) { + _.current_function().RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + "OpTerminateInvocation requires Fragment execution model"); + } + if (opcode == spv::Op::OpIgnoreIntersectionKHR) { + _.current_function().RegisterExecutionModelLimitation( + spv::ExecutionModel::AnyHitKHR, + "OpIgnoreIntersectionKHR requires AnyHitKHR execution model"); + } + if (opcode == spv::Op::OpTerminateRayKHR) { + _.current_function().RegisterExecutionModelLimitation( + spv::ExecutionModel::AnyHitKHR, + "OpTerminateRayKHR requires AnyHitKHR execution model"); + } + + break; + default: + break; + } + return SPV_SUCCESS; +} + +void ReachabilityPass(ValidationState_t& _) { + for (auto& f : _.functions()) { + std::vector stack; + auto entry = f.first_block(); + // Skip function declarations. + if (entry) stack.push_back(entry); + + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block->reachable()) continue; + + block->set_reachable(true); + for (auto succ : *block->successors()) { + stack.push_back(succ); + } + } + } + + // Repeat for structural reachability. + for (auto& f : _.functions()) { + std::vector stack; + auto entry = f.first_block(); + // Skip function declarations. + if (entry) stack.push_back(entry); + + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block->structurally_reachable()) continue; + + block->set_structurally_reachable(true); + for (auto succ : *block->structural_successors()) { + stack.push_back(succ); + } + } + } +} + +spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpPhi: + if (auto error = ValidatePhi(_, inst)) return error; + break; + case spv::Op::OpBranch: + if (auto error = ValidateBranch(_, inst)) return error; + break; + case spv::Op::OpBranchConditional: + if (auto error = ValidateBranchConditional(_, inst)) return error; + break; + case spv::Op::OpReturnValue: + if (auto error = ValidateReturnValue(_, inst)) return error; + break; + case spv::Op::OpSwitch: + if (auto error = ValidateSwitch(_, inst)) return error; + break; + case spv::Op::OpLoopMerge: + if (auto error = ValidateLoopMerge(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_composites.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_composites.cpp new file mode 100644 index 00000000..2afeae78 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_composites.cpp @@ -0,0 +1,652 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of composite SPIR-V instructions. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns the type of the value accessed by OpCompositeExtract or +// OpCompositeInsert instruction. The function traverses the hierarchy of +// nested data structures (structs, arrays, vectors, matrices) as directed by +// the sequence of indices in the instruction. May return error if traversal +// fails (encountered non-composite, out of bounds, no indices, nesting too +// deep). +spv_result_t GetExtractInsertValueType(ValidationState_t& _, + const Instruction* inst, + uint32_t* member_type) { + const spv::Op opcode = inst->opcode(); + assert(opcode == spv::Op::OpCompositeExtract || + opcode == spv::Op::OpCompositeInsert); + uint32_t word_index = opcode == spv::Op::OpCompositeExtract ? 4 : 5; + const uint32_t num_words = static_cast(inst->words().size()); + const uint32_t composite_id_index = word_index - 1; + const uint32_t num_indices = num_words - word_index; + const uint32_t kCompositeExtractInsertMaxNumIndices = 255; + + if (num_indices == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected at least one index to Op" + << spvOpcodeString(inst->opcode()) << ", zero found"; + + } else if (num_indices > kCompositeExtractInsertMaxNumIndices) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The number of indexes in Op" << spvOpcodeString(opcode) + << " may not exceed " << kCompositeExtractInsertMaxNumIndices + << ". Found " << num_indices << " indexes."; + } + + *member_type = _.GetTypeId(inst->word(composite_id_index)); + if (*member_type == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Composite to be an object of composite type"; + } + + for (; word_index < num_words; ++word_index) { + const uint32_t component_index = inst->word(word_index); + const Instruction* const type_inst = _.FindDef(*member_type); + assert(type_inst); + switch (type_inst->opcode()) { + case spv::Op::OpTypeVector: { + *member_type = type_inst->word(2); + const uint32_t vector_size = type_inst->word(3); + if (component_index >= vector_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vector access is out of bounds, vector size is " + << vector_size << ", but access index is " << component_index; + } + break; + } + case spv::Op::OpTypeMatrix: { + *member_type = type_inst->word(2); + const uint32_t num_cols = type_inst->word(3); + if (component_index >= num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix access is out of bounds, matrix has " << num_cols + << " columns, but access index is " << component_index; + } + break; + } + case spv::Op::OpTypeArray: { + uint64_t array_size = 0; + auto size = _.FindDef(type_inst->word(3)); + *member_type = type_inst->word(2); + if (spvOpcodeIsSpecConstant(size->opcode())) { + // Cannot verify against the size of this array. + break; + } + + if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + if (component_index >= array_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Array access is out of bounds, array size is " + << array_size << ", but access index is " << component_index; + } + break; + } + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: { + *member_type = type_inst->word(2); + // Array size is unknown. + break; + } + case spv::Op::OpTypeStruct: { + const size_t num_struct_members = type_inst->words().size() - 2; + if (component_index >= num_struct_members) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Index is out of bounds, can not find index " + << component_index << " in the structure '" + << type_inst->id() << "'. This structure has " + << num_struct_members << " members. Largest valid index is " + << num_struct_members - 1 << "."; + } + *member_type = type_inst->word(component_index + 2); + break; + } + case spv::Op::OpTypeCooperativeVectorNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeMatrixNV: { + *member_type = type_inst->word(2); + break; + } + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reached non-composite type while indexes still remain to " + "be traversed."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorExtractDynamic(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const spv::Op result_opcode = _.GetIdOpcode(result_type); + if (!spvOpcodeIsScalarType(result_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a scalar type"; + } + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + const spv::Op vector_opcode = _.GetIdOpcode(vector_type); + if (vector_opcode != spv::Op::OpTypeVector && + vector_opcode != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector type to be OpTypeVector"; + } + + if (_.GetComponentType(vector_type) != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector component type to be equal to Result Type"; + } + + const auto index = _.FindDef(inst->GetOperandAs(3)); + if (!index || index->type_id() == 0 || !_.IsIntScalarType(index->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Index to be int scalar"; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot extract from a vector of 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorInsertDyanmic(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const spv::Op result_opcode = _.GetIdOpcode(result_type); + if (result_opcode != spv::Op::OpTypeVector && + result_opcode != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeVector"; + } + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + if (vector_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector type to be equal to Result Type"; + } + + const uint32_t component_type = _.GetOperandTypeId(inst, 3); + if (_.GetComponentType(result_type) != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Component type to be equal to Result Type " + << "component type"; + } + + const uint32_t index_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(index_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Index to be int scalar"; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot insert into a vector of 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeConstruct(ValidationState_t& _, + const Instruction* inst) { + const uint32_t num_operands = static_cast(inst->operands().size()); + const uint32_t result_type = inst->type_id(); + const spv::Op result_opcode = _.GetIdOpcode(result_type); + switch (result_opcode) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeCooperativeVectorNV: { + uint32_t num_result_components = _.GetDimension(result_type); + const uint32_t result_component_type = _.GetComponentType(result_type); + uint32_t given_component_count = 0; + + bool comp_is_int32 = true, comp_is_const_int32 = true; + + if (result_opcode == spv::Op::OpTypeVector) { + if (num_operands <= 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of constituents to be at least 2"; + } + } else { + uint32_t comp_count_id = + _.FindDef(result_type)->GetOperandAs(2); + std::tie(comp_is_int32, comp_is_const_int32, num_result_components) = + _.EvalInt32IfConst(comp_count_id); + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type == result_component_type) { + ++given_component_count; + } else { + if (_.GetIdOpcode(operand_type) != spv::Op::OpTypeVector || + _.GetComponentType(operand_type) != result_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituents to be scalars or vectors of" + << " the same type as Result Type components"; + } + + given_component_count += _.GetDimension(operand_type); + } + } + + if (comp_is_const_int32 && + num_result_components != given_component_count) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of given components to be equal " + << "to the size of Result Type vector"; + } + + break; + } + case spv::Op::OpTypeMatrix: { + uint32_t result_num_rows = 0; + uint32_t result_num_cols = 0; + uint32_t result_col_type = 0; + uint32_t result_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, + &result_col_type, &result_component_type)) { + assert(0); + } + + if (result_num_cols + 2 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of columns of Result Type matrix"; + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type != result_col_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the column " + << "type Result Type matrix"; + } + } + + break; + } + case spv::Op::OpTypeArray: { + const Instruction* const array_inst = _.FindDef(result_type); + assert(array_inst); + assert(array_inst->opcode() == spv::Op::OpTypeArray); + + auto size = _.FindDef(array_inst->word(3)); + if (spvOpcodeIsSpecConstant(size->opcode())) { + // Cannot verify against the size of this array. + break; + } + + uint64_t array_size = 0; + if (!_.EvalConstantValUint64(array_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + + if (array_size + 2 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of elements of Result Type array"; + } + + const uint32_t result_component_type = array_inst->word(2); + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type != result_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the column " + << "type Result Type array"; + } + } + + break; + } + case spv::Op::OpTypeStruct: { + const Instruction* const struct_inst = _.FindDef(result_type); + assert(struct_inst); + assert(struct_inst->opcode() == spv::Op::OpTypeStruct); + + if (struct_inst->operands().size() + 1 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of members of Result Type struct"; + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + const uint32_t member_type = struct_inst->word(operand_index); + if (operand_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the " + << "corresponding member type of Result Type struct"; + } + } + + break; + } + case spv::Op::OpTypeCooperativeMatrixKHR: { + const auto result_type_inst = _.FindDef(result_type); + assert(result_type_inst); + const auto component_type_id = + result_type_inst->GetOperandAs(1); + + if (3 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Must be only one constituent"; + } + + const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2); + + if (operand_type_id != component_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the component type"; + } + break; + } + case spv::Op::OpTypeCooperativeMatrixNV: { + const auto result_type_inst = _.FindDef(result_type); + assert(result_type_inst); + const auto component_type_id = + result_type_inst->GetOperandAs(1); + + if (3 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected single constituent"; + } + + const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2); + + if (operand_type_id != component_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the component type"; + } + + break; + } + default: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a composite type"; + } + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot create a composite containing 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeExtract(ValidationState_t& _, + const Instruction* inst) { + uint32_t member_type = 0; + if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + return error; + } + + const uint32_t result_type = inst->type_id(); + if (result_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result type (Op" << spvOpcodeString(_.GetIdOpcode(result_type)) + << ") does not match the type that results from indexing into " + "the composite (Op" + << spvOpcodeString(_.GetIdOpcode(member_type)) << ")."; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot extract from a composite of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeInsert(ValidationState_t& _, + const Instruction* inst) { + const uint32_t object_type = _.GetOperandTypeId(inst, 2); + const uint32_t composite_type = _.GetOperandTypeId(inst, 3); + const uint32_t result_type = inst->type_id(); + if (result_type != composite_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type must be the same as Composite type in Op" + << spvOpcodeString(inst->opcode()) << " yielding Result Id " + << result_type << "."; + } + + uint32_t member_type = 0; + if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + return error; + } + + if (object_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Object type (Op" + << spvOpcodeString(_.GetIdOpcode(object_type)) + << ") does not match the type that results from indexing into the " + "Composite (Op" + << spvOpcodeString(_.GetIdOpcode(member_type)) << ")."; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot insert into a composite of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyObject(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (operand_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type and Operand type to be the same"; + } + if (_.IsVoidType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpCopyObject cannot have void result type"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateTranspose(ValidationState_t& _, const Instruction* inst) { + uint32_t result_num_rows = 0; + uint32_t result_num_cols = 0; + uint32_t result_col_type = 0; + uint32_t result_component_type = 0; + const uint32_t result_type = inst->type_id(); + if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, + &result_col_type, &result_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a matrix type"; + } + + const uint32_t matrix_type = _.GetOperandTypeId(inst, 2); + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type, &matrix_num_rows, &matrix_num_cols, + &matrix_col_type, &matrix_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Matrix to be of type OpTypeMatrix"; + } + + if (result_component_type != matrix_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Matrix and Result Type to be " + << "identical"; + } + + if (result_num_rows != matrix_num_cols || + result_num_cols != matrix_num_rows) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns and the column size of Matrix " + << "to be the reverse of those of Result Type"; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot transpose matrices of 16-bit floats"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorShuffle(ValidationState_t& _, + const Instruction* inst) { + auto resultType = _.FindDef(inst->type_id()); + if (!resultType || resultType->opcode() != spv::Op::OpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of OpVectorShuffle must be" + << " OpTypeVector. Found Op" + << spvOpcodeString(static_cast(resultType->opcode())) + << "."; + } + + // The number of components in Result Type must be the same as the number of + // Component operands. + auto componentCount = inst->operands().size() - 4; + auto resultVectorDimension = resultType->GetOperandAs(2); + if (componentCount != resultVectorDimension) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVectorShuffle component literals count does not match " + "Result Type " + << _.getIdName(resultType->id()) << "s vector component count."; + } + + // Vector 1 and Vector 2 must both have vector types, with the same Component + // Type as Result Type. + auto vector1Object = _.FindDef(inst->GetOperandAs(2)); + auto vector1Type = _.FindDef(vector1Object->type_id()); + auto vector2Object = _.FindDef(inst->GetOperandAs(3)); + auto vector2Type = _.FindDef(vector2Object->type_id()); + if (!vector1Type || vector1Type->opcode() != spv::Op::OpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The type of Vector 1 must be OpTypeVector."; + } + if (!vector2Type || vector2Type->opcode() != spv::Op::OpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The type of Vector 2 must be OpTypeVector."; + } + + auto resultComponentType = resultType->GetOperandAs(1); + if (vector1Type->GetOperandAs(1) != resultComponentType) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Component Type of Vector 1 must be the same as ResultType."; + } + if (vector2Type->GetOperandAs(1) != resultComponentType) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Component Type of Vector 2 must be the same as ResultType."; + } + + // All Component literals must either be FFFFFFFF or in [0, N - 1]. + auto vector1ComponentCount = vector1Type->GetOperandAs(2); + auto vector2ComponentCount = vector2Type->GetOperandAs(2); + auto N = vector1ComponentCount + vector2ComponentCount; + auto firstLiteralIndex = 4; + for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) { + auto literal = inst->GetOperandAs(i); + if (literal != 0xFFFFFFFF && literal >= N) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Component index " << literal << " is out of bounds for " + << "combined (Vector1 + Vector2) size of " << N << "."; + } + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot shuffle a vector of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyLogical(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + const auto source = _.FindDef(inst->GetOperandAs(2u)); + const auto source_type = _.FindDef(source->type_id()); + if (!source_type || !result_type || source_type == result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must not equal the Operand type"; + } + + if (!_.LogicallyMatch(source_type, result_type, false)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type does not logically match the Operand type"; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot copy composites of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +} // anonymous namespace + +// Validates correctness of composite instructions. +spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpVectorExtractDynamic: + return ValidateVectorExtractDynamic(_, inst); + case spv::Op::OpVectorInsertDynamic: + return ValidateVectorInsertDyanmic(_, inst); + case spv::Op::OpVectorShuffle: + return ValidateVectorShuffle(_, inst); + case spv::Op::OpCompositeConstruct: + return ValidateCompositeConstruct(_, inst); + case spv::Op::OpCompositeExtract: + return ValidateCompositeExtract(_, inst); + case spv::Op::OpCompositeInsert: + return ValidateCompositeInsert(_, inst); + case spv::Op::OpCopyObject: + return ValidateCopyObject(_, inst); + case spv::Op::OpTranspose: + return ValidateTranspose(_, inst); + case spv::Op::OpCopyLogical: + return ValidateCopyLogical(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_constants.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_constants.cpp new file mode 100644 index 00000000..9c689c53 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_constants.cpp @@ -0,0 +1,481 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateConstantBool(ValidationState_t& _, + const Instruction* inst) { + auto type = _.FindDef(inst->type_id()); + if (!type || type->opcode() != spv::Op::OpTypeBool) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Op" << spvOpcodeString(inst->opcode()) << " Result Type " + << _.getIdName(inst->type_id()) << " is not a boolean type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantComposite(ValidationState_t& _, + const Instruction* inst) { + std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); + + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || !spvOpcodeIsComposite(result_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Result Type " + << _.getIdName(inst->type_id()) << " is not a composite type."; + } + + const auto constituent_count = inst->words().size() - 3; + switch (result_type->opcode()) { + case spv::Op::OpTypeVector: + case spv::Op::OpTypeCooperativeVectorNV: { + uint32_t num_result_components = _.GetDimension(result_type->id()); + bool comp_is_int32 = true, comp_is_const_int32 = true; + + if (result_type->opcode() == spv::Op::OpTypeCooperativeVectorNV) { + uint32_t comp_count_id = result_type->GetOperandAs(2); + std::tie(comp_is_int32, comp_is_const_int32, num_result_components) = + _.EvalInt32IfConst(comp_count_id); + } + + if (comp_is_const_int32 && num_result_components != constituent_count) { + // TODO: Output ID's on diagnostic + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type " + << _.getIdName(result_type->id()) << "s vector component count."; + } + const auto component_type = + _.FindDef(result_type->GetOperandAs(1)); + if (!component_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Component type is not defined."; + } + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " is not a constant or undef."; + } + const auto constituent_result_type = _.FindDef(constituent->type_id()); + if (!constituent_result_type || + component_type->id() != constituent_result_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << "s type does not match Result Type " + << _.getIdName(result_type->id()) << "s vector element type."; + } + } + } break; + case spv::Op::OpTypeMatrix: { + const auto column_count = result_type->GetOperandAs(2); + if (column_count != constituent_count) { + // TODO: Output ID's on diagnostic + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type " + << _.getIdName(result_type->id()) << "s matrix column count."; + } + + const auto column_type = _.FindDef(result_type->words()[2]); + if (!column_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Column type is not defined."; + } + const auto component_count = column_type->GetOperandAs(2); + const auto component_type = + _.FindDef(column_type->GetOperandAs(1)); + if (!component_type) { + return _.diag(SPV_ERROR_INVALID_ID, column_type) + << "Component type is not defined."; + } + + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + // The message says "... or undef" because the spec does not say + // undef is a constant. + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " is not a constant or undef."; + } + const auto vector = _.FindDef(constituent->type_id()); + if (!vector) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + if (column_type->opcode() != vector->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " type does not match Result Type " + << _.getIdName(result_type->id()) << "s matrix column type."; + } + const auto vector_component_type = + _.FindDef(vector->GetOperandAs(1)); + if (component_type->id() != vector_component_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " component type does not match Result Type " + << _.getIdName(result_type->id()) + << "s matrix column component type."; + } + if (component_count != vector->words()[3]) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " vector component count does not match Result Type " + << _.getIdName(result_type->id()) + << "s vector component count."; + } + } + } break; + case spv::Op::OpTypeArray: { + auto element_type = _.FindDef(result_type->GetOperandAs(1)); + if (!element_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Element type is not defined."; + } + const auto length = _.FindDef(result_type->GetOperandAs(2)); + if (!length) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Length is not defined."; + } + bool is_int32; + bool is_const; + uint32_t value; + std::tie(is_int32, is_const, value) = _.EvalInt32IfConst(length->id()); + if (is_int32 && is_const && value != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type " + << _.getIdName(result_type->id()) << "s array length."; + } + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + if (element_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << "s type does not match Result Type " + << _.getIdName(result_type->id()) << "s array element type."; + } + } + } break; + case spv::Op::OpTypeStruct: { + const auto member_count = result_type->words().size() - 2; + if (member_count != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(inst->type_id()) + << " count does not match Result Type " + << _.getIdName(result_type->id()) << "s struct member count."; + } + for (uint32_t constituent_index = 2, member_index = 1; + constituent_index < inst->operands().size(); + constituent_index++, member_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + + const auto member_type_id = + result_type->GetOperandAs(member_index); + const auto member_type = _.FindDef(member_type_id); + if (!member_type || member_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " type does not match the Result Type " + << _.getIdName(result_type->id()) << "s member type."; + } + } + } break; + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeMatrixNV: { + if (1 != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(inst->type_id()) << " count must be one."; + } + const auto constituent_id = inst->GetOperandAs(2); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) << " is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + + const auto component_type_id = result_type->GetOperandAs(1); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || component_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent " + << _.getIdName(constituent_id) + << " type does not match the Result Type " + << _.getIdName(result_type->id()) << "s component type."; + } + } break; + default: + break; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantSampler(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || result_type->opcode() != spv::Op::OpTypeSampler) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "OpConstantSampler Result Type " + << _.getIdName(inst->type_id()) << " is not a sampler type."; + } + + return SPV_SUCCESS; +} + +// True if instruction defines a type that can have a null value, as defined by +// the SPIR-V spec. Tracks composite-type components through module to check +// nullability transitively. +bool IsTypeNullable(const std::vector& instruction, + const ValidationState_t& _) { + uint16_t opcode; + uint16_t word_count; + spvOpcodeSplit(instruction[0], &word_count, &opcode); + switch (static_cast(opcode)) { + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeEvent: + case spv::Op::OpTypeDeviceEvent: + case spv::Op::OpTypeReserveId: + case spv::Op::OpTypeQueue: + return true; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + case spv::Op::OpTypeVector: { + auto base_type = _.FindDef(instruction[2]); + return base_type && IsTypeNullable(base_type->words(), _); + } + case spv::Op::OpTypeStruct: { + for (size_t elementIndex = 2; elementIndex < instruction.size(); + ++elementIndex) { + auto element = _.FindDef(instruction[elementIndex]); + if (!element || !IsTypeNullable(element->words(), _)) return false; + } + return true; + } + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypePointer: + if (spv::StorageClass(instruction[2]) == + spv::StorageClass::PhysicalStorageBuffer) { + return false; + } + return true; + default: + return false; + } +} + +spv_result_t ValidateConstantNull(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || !IsTypeNullable(result_type->words(), _)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantNull Result Type " << _.getIdName(inst->type_id()) + << " cannot have a null value."; + } + + return SPV_SUCCESS; +} + +// Validates that OpSpecConstant specializes to either int or float type. +spv_result_t ValidateSpecConstant(ValidationState_t& _, + const Instruction* inst) { + // Operand 0 is the of the type that we're specializing to. + auto type_id = inst->GetOperandAs(0); + auto type_instruction = _.FindDef(type_id); + auto type_opcode = type_instruction->opcode(); + if (type_opcode != spv::Op::OpTypeInt && + type_opcode != spv::Op::OpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant " + "must be an integer or " + "floating-point number."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateSpecConstantOp(ValidationState_t& _, + const Instruction* inst) { + const auto op = inst->GetOperandAs(2); + + // The binary parser already ensures that the op is valid for *some* + // environment. Here we check restrictions. + switch (op) { + case spv::Op::OpQuantizeToF16: + if (!_.HasCapability(spv::Capability::Shader)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Specialization constant operation " << spvOpcodeString(op) + << " requires Shader capability"; + } + break; + + case spv::Op::OpUConvert: + if (!_.features().uconvert_spec_constant_op && + !_.HasCapability(spv::Capability::Kernel)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability or extension " + "SPV_AMD_gpu_shader_int16"; + } + break; + + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertUToF: + case spv::Op::OpConvertPtrToU: + case spv::Op::OpConvertUToPtr: + case spv::Op::OpGenericCastToPtr: + case spv::Op::OpPtrCastToGeneric: + case spv::Op::OpBitcast: + case spv::Op::OpFNegate: + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + case spv::Op::OpFDiv: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + if (!_.HasCapability(spv::Capability::Kernel)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Specialization constant operation " << spvOpcodeString(op) + << " requires Kernel capability"; + } + break; + + default: + break; + } + + // TODO(dneto): Validate result type and arguments to the various operations. + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpConstantTrue: + case spv::Op::OpConstantFalse: + case spv::Op::OpSpecConstantTrue: + case spv::Op::OpSpecConstantFalse: + if (auto error = ValidateConstantBool(_, inst)) return error; + break; + case spv::Op::OpConstantComposite: + case spv::Op::OpSpecConstantComposite: + if (auto error = ValidateConstantComposite(_, inst)) return error; + break; + case spv::Op::OpConstantSampler: + if (auto error = ValidateConstantSampler(_, inst)) return error; + break; + case spv::Op::OpConstantNull: + if (auto error = ValidateConstantNull(_, inst)) return error; + break; + case spv::Op::OpSpecConstant: + if (auto error = ValidateSpecConstant(_, inst)) return error; + break; + case spv::Op::OpSpecConstantOp: + if (auto error = ValidateSpecConstantOp(_, inst)) return error; + break; + default: + break; + } + + // Generally disallow creating 8- or 16-bit constants unless the full + // capabilities are present. + if (spvOpcodeIsConstant(inst->opcode()) && + _.HasCapability(spv::Capability::Shader) && + !_.IsPointerType(inst->type_id()) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot form constants of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_conversion.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_conversion.cpp new file mode 100644 index 00000000..8bf87ad3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_conversion.cpp @@ -0,0 +1,697 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of conversion instructions. + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of conversion instructions. +spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpConvertFToU: { + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type) && + !_.IsUnsignedIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case spv::Op::OpConvertFToS: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: { + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case spv::Op::OpUConvert: { + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type) && + !_.IsUnsignedIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpSConvert: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type) && + !_.IsIntCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type) && + !_.IsIntCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpFConvert: { + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type) && + !_.IsFloatCooperativeVectorNVType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type) && + !_.IsFloatCooperativeVectorNVType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeVectorNVType(result_type) || + _.IsCooperativeVectorNVType(input_type)) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + // Scalar type + const uint32_t resScalarType = _.GetComponentType(result_type); + const uint32_t inputScalartype = _.GetComponentType(input_type); + if (_.GetBitWidth(resScalarType) == _.GetBitWidth(inputScalartype)) + if ((_.IsBfloat16ScalarType(resScalarType) && + _.IsBfloat16ScalarType(inputScalartype)) || + (!_.IsBfloat16ScalarType(inputScalartype) && + !_.IsBfloat16ScalarType(resScalarType))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpQuantizeToF16: { + if ((!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type)) || + _.GetBitWidth(result_type) != 32) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (input_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input type to be equal to Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpConvertPtrToU: { + if (!_.IsUnsignedIntScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!_.IsPointerType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (_.addressing_model() == spv::AddressingModel::Logical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " + << spvOpcodeString(opcode); + + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64) { + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + _.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class); + if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBuffer: " + << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(result_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "result integer type to have a 64-bit width for Vulkan " + "environment."; + } + } + } + break; + } + + case spv::Op::OpSatConvertSToU: + case spv::Op::OpSatConvertUToS: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as input: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpConvertUToPtr: { + if (!_.IsPointerType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || !_.IsIntScalarType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar as input: " << spvOpcodeString(opcode); + + if (_.addressing_model() == spv::AddressingModel::Logical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " + << spvOpcodeString(opcode); + + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64) { + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + _.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class); + if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBuffer: " + << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(input_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "input integer to have a 64-bit width for Vulkan " + "environment."; + } + } + } + break; + } + + case spv::Op::OpPtrCastToGeneric: { + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Generic: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Workgroup && + input_storage_class != spv::StorageClass::CrossWorkgroup && + input_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpGenericCastToPtr: { + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != spv::StorageClass::Workgroup && + result_storage_class != spv::StorageClass::CrossWorkgroup && + result_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpGenericCastToPtrExplicit: { + spv::StorageClass result_storage_class; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + const auto target_storage_class = + inst->GetOperandAs(3); + if (result_storage_class != target_storage_class) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be of target storage class: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + spv::StorageClass input_storage_class; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != spv::StorageClass::Generic) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + + if (target_storage_class != spv::StorageClass::Workgroup && + target_storage_class != spv::StorageClass::CrossWorkgroup && + target_storage_class != spv::StorageClass::Function) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected target storage class to be Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpBitcast: { + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have a type: " << spvOpcodeString(opcode); + + const bool result_is_pointer = _.IsPointerType(result_type); + const bool result_is_int_scalar = _.IsIntScalarType(result_type); + const bool input_is_pointer = _.IsPointerType(input_type); + const bool input_is_int_scalar = _.IsIntScalarType(input_type); + + const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type); + const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type); + const bool result_is_coopvec = _.IsCooperativeVectorNVType(result_type); + const bool input_is_coopvec = _.IsCooperativeVectorNVType(input_type); + + if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat && + !result_is_coopvec && !_.IsIntVectorType(result_type) && + !_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer or int or float vector " + << "or scalar type: " << spvOpcodeString(opcode); + + if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat && + !input_is_coopvec && !_.IsIntVectorType(input_type) && + !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer or int or float vector " + << "or scalar: " << spvOpcodeString(opcode); + + if (result_is_coopvec != input_is_coopvec) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative vector can only be cast to another cooperative " + << "vector: " << spvOpcodeString(opcode); + + if (result_is_coopmat != input_is_coopmat) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix can only be cast to another cooperative " + << "matrix: " << spvOpcodeString(opcode); + + if (result_is_coopvec) { + spv_result_t ret = + _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } + + if (result_is_coopmat) { + spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type, + input_type, false); + if (ret != SPV_SUCCESS) return ret; + } + + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) || + _.HasExtension(kSPV_KHR_physical_storage_buffer)) { + const bool result_is_int_vector = _.IsIntVectorType(result_type); + const bool result_has_int32 = + _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32); + const bool input_is_int_vector = _.IsIntVectorType(input_type); + const bool input_has_int32 = + _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32); + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar && + !(input_is_int_vector && input_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer, int scalar or 32-bit int " + "vector if Result Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar && + !(result_is_int_vector && result_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: " + << spvOpcodeString(opcode); + } else { + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer or int scalar if Result " + "Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer can only be converted to another pointer or int " + "scalar: " + << spvOpcodeString(opcode); + } + + if (!result_is_pointer && !input_is_pointer) { + const uint32_t result_size = + _.GetBitWidth(result_type) * _.GetDimension(result_type); + const uint32_t input_size = + _.GetBitWidth(input_type) * _.GetDimension(input_type); + if (result_size != input_size) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same total bit width as " + << "Result Type: " << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpConvertUToAccelerationStructureKHR: { + if (!_.IsAccelerationStructureType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a Acceleration Structure: " + << spvOpcodeString(opcode); + } + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || !_.IsUnsigned64BitHandle(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 64-bit uint scalar or 2-component 32-bit uint " + "vector as input: " + << spvOpcodeString(opcode); + } + + break; + } + + case spv::Op::OpCooperativeMatrixConvertNV: + case spv::Op::OpCooperativeMatrixTransposeNV: { + if (!_.IsCooperativeMatrixType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix Result Type: " + << spvOpcodeString(opcode); + } + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!_.IsCooperativeMatrixType(input_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type for Matrix input: " + << spvOpcodeString(opcode); + } + + bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV); + if (auto error = _.CooperativeMatrixShapesMatch( + inst, result_type, input_type, true, swap_row_col)) + return error; + + if (opcode == spv::Op::OpCooperativeMatrixConvertNV) { + if (_.FindDef(result_type)->GetOperandAs(1) != + _.FindDef(input_type)->GetOperandAs(1)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type and Matrix component types mismatch: " + << spvOpcodeString(opcode); + } + } + + if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) { + if (!_.IsCooperativeMatrixBType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must have UseB: " << spvOpcodeString(opcode); + } + } + break; + } + + default: + break; + } + + if (_.HasCapability(spv::Capability::Shader)) { + switch (inst->opcode()) { + case spv::Op::OpConvertFToU: + case spv::Op::OpConvertFToS: + case spv::Op::OpConvertSToF: + case spv::Op::OpConvertUToF: + case spv::Op::OpBitcast: + if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) || + _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "8- or 16-bit types can only be used with width-only " + "conversions"; + } + break; + default: + break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_debug.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_debug.cpp new file mode 100644 index 00000000..ef537ea0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_debug.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateMemberName(ValidationState_t& _, const Instruction* inst) { + const auto type_id = inst->GetOperandAs(0); + const auto type = _.FindDef(type_id); + if (!type || spv::Op::OpTypeStruct != type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberName Type " << _.getIdName(type_id) + << " is not a struct type."; + } + const auto member_id = inst->GetOperandAs(1); + const auto member_count = (uint32_t)(type->words().size() - 2); + if (member_count <= member_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberName Member " << _.getIdName(member_id) + << " index is larger than Type " << _.getIdName(type->id()) + << "s member count."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) { + const auto file_id = inst->GetOperandAs(0); + const auto file = _.FindDef(file_id); + if (!file || spv::Op::OpString != file->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLine Target " << _.getIdName(file_id) + << " is not an OpString."; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpMemberName: + if (auto error = ValidateMemberName(_, inst)) return error; + break; + case spv::Op::OpLine: + if (auto error = ValidateLine(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_decorations.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_decorations.cpp new file mode 100644 index 00000000..37a5c68e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_decorations.cpp @@ -0,0 +1,2310 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Distinguish between row and column major matrix layouts. +enum MatrixLayout { kRowMajor, kColumnMajor }; + +// A functor for hashing a pair of integers. +struct PairHash { + std::size_t operator()(const std::pair pair) const { + const uint32_t a = pair.first; + const uint32_t b = pair.second; + const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30); + return a ^ rotated_b; + } +}; + +// Struct member layout attributes that are inherited through arrays. +struct LayoutConstraints { + explicit LayoutConstraints( + MatrixLayout the_majorness = MatrixLayout::kColumnMajor, + uint32_t stride = 0) + : majorness(the_majorness), matrix_stride(stride) {} + MatrixLayout majorness; + uint32_t matrix_stride; +}; + +// A type for mapping (struct id, member id) to layout constraints. +using MemberConstraints = std::unordered_map, + LayoutConstraints, PairHash>; + +// Returns the array stride of the given array type. +uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) { + for (auto& decoration : vstate.id_decorations(array_id)) { + if (spv::Decoration::ArrayStride == decoration.dec_type()) { + return decoration.params()[0]; + } + } + return 0; +} + +// Returns true if the given structure type has a Block decoration. +bool isBlock(uint32_t struct_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(struct_id); + return std::any_of(decorations.begin(), decorations.end(), + [](const Decoration& d) { + return spv::Decoration::Block == d.dec_type(); + }); +} + +// Returns true if the given ID has the Import LinkageAttributes decoration. +bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(id); + return std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return spv::Decoration::LinkageAttributes == d.dec_type() && + d.params().size() >= 2u && + spv::LinkageType(d.params().back()) == spv::LinkageType::Import; + }); +} + +// Returns a vector of all members of a structure. +std::vector getStructMembers(uint32_t struct_id, + ValidationState_t& vstate) { + const auto inst = vstate.FindDef(struct_id); + return std::vector(inst->words().begin() + 2, inst->words().end()); +} + +// Returns a vector of all members of a structure that have specific type. +std::vector getStructMembers(uint32_t struct_id, spv::Op type, + ValidationState_t& vstate) { + std::vector members; + for (auto id : getStructMembers(struct_id, vstate)) { + if (type == vstate.FindDef(id)->opcode()) { + members.push_back(id); + } + } + return members; +} + +// Returns whether the given structure is missing Offset decoration for any +// member. Handles also nested structures. +bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { + const auto* inst = vstate.FindDef(struct_id); + std::vector hasOffset; + std::vector struct_members; + if (inst->opcode() == spv::Op::OpTypeStruct) { + // Check offsets of member decorations. + struct_members = getStructMembers(struct_id, vstate); + hasOffset.resize(struct_members.size(), false); + + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (spv::Decoration::Offset == decoration.dec_type() && + Decoration::kInvalidMember != decoration.struct_member_index()) { + // Offset 0xffffffff is not valid so ignore it for simplicity's sake. + if (decoration.params()[0] == 0xffffffff) return true; + hasOffset[decoration.struct_member_index()] = true; + } + } + } else if (inst->opcode() == spv::Op::OpTypeArray || + inst->opcode() == spv::Op::OpTypeRuntimeArray) { + hasOffset.resize(1, true); + struct_members.push_back(inst->GetOperandAs(1u)); + } + // Look through nested structs (which may be in an array). + bool nestedStructsMissingOffset = false; + for (auto id : struct_members) { + if (isMissingOffsetInStruct(id, vstate)) { + nestedStructsMissingOffset = true; + break; + } + } + return nestedStructsMissingOffset || + !std::all_of(hasOffset.begin(), hasOffset.end(), + [](const bool b) { return b; }); +} + +// Rounds x up to the next alignment. Assumes alignment is a power of two. +uint32_t align(uint32_t x, uint32_t alignment) { + return (x + alignment - 1) & ~(alignment - 1); +} + +// Returns base alignment of struct member. If |roundUp| is true, also +// ensure that structs, arrays, and matrices are aligned at least to a +// multiple of 16 bytes. (That is, when roundUp is true, this function +// returns the *extended* alignment as it's called by the Vulkan spec.) +uint32_t getBaseAlignment(uint32_t member_id, bool roundUp, + const LayoutConstraints& inherited, + MemberConstraints& constraints, + ValidationState_t& vstate) { + const auto inst = vstate.FindDef(member_id); + const auto& words = inst->words(); + // Minimal alignment is byte-aligned. + uint32_t baseAlignment = 1; + switch (inst->opcode()) { + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeImage: + if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) + return vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + baseAlignment = words[2] / 8; + break; + case spv::Op::OpTypeVector: { + const auto componentId = words[2]; + const auto numComponents = words[3]; + const auto componentAlignment = getBaseAlignment( + componentId, roundUp, inherited, constraints, vstate); + baseAlignment = + componentAlignment * (numComponents == 3 ? 4 : numComponents); + break; + } + case spv::Op::OpTypeMatrix: { + const auto column_type = words[2]; + if (inherited.majorness == kColumnMajor) { + baseAlignment = getBaseAlignment(column_type, roundUp, inherited, + constraints, vstate); + } else { + // A row-major matrix of C columns has a base alignment equal to the + // base alignment of a vector of C matrix components. + const auto num_columns = words[3]; + const auto component_inst = vstate.FindDef(column_type); + const auto component_id = component_inst->words()[2]; + const auto componentAlignment = getBaseAlignment( + component_id, roundUp, inherited, constraints, vstate); + baseAlignment = + componentAlignment * (num_columns == 3 ? 4 : num_columns); + } + if (roundUp) baseAlignment = align(baseAlignment, 16u); + } break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + baseAlignment = + getBaseAlignment(words[2], roundUp, inherited, constraints, vstate); + if (roundUp) baseAlignment = align(baseAlignment, 16u); + break; + case spv::Op::OpTypeStruct: { + const auto members = getStructMembers(member_id, vstate); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; ++memberIdx) { + const auto id = members[memberIdx]; + const auto& constraint = + constraints[std::make_pair(member_id, memberIdx)]; + baseAlignment = std::max( + baseAlignment, + getBaseAlignment(id, roundUp, constraint, constraints, vstate)); + } + if (roundUp) baseAlignment = align(baseAlignment, 16u); + break; + } + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: + baseAlignment = vstate.pointer_size_and_alignment(); + break; + default: + assert(0); + break; + } + + return baseAlignment; +} + +// Returns scalar alignment of a type. +uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) { + const auto inst = vstate.FindDef(type_id); + const auto& words = inst->words(); + switch (inst->opcode()) { + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeImage: + if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) + return vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + return words[2] / 8; + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: { + const auto compositeMemberTypeId = words[2]; + return getScalarAlignment(compositeMemberTypeId, vstate); + } + case spv::Op::OpTypeStruct: { + const auto members = getStructMembers(type_id, vstate); + uint32_t max_member_alignment = 1; + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; ++memberIdx) { + const auto id = members[memberIdx]; + uint32_t member_alignment = getScalarAlignment(id, vstate); + if (member_alignment > max_member_alignment) { + max_member_alignment = member_alignment; + } + } + return max_member_alignment; + } break; + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: + return vstate.pointer_size_and_alignment(); + default: + assert(0); + break; + } + + return 1; +} + +// Returns size of a struct member. Doesn't include padding at the end of struct +// or array. Assumes that in the struct case, all members have offsets. +uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited, + MemberConstraints& constraints, ValidationState_t& vstate) { + const auto inst = vstate.FindDef(member_id); + const auto& words = inst->words(); + switch (inst->opcode()) { + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeSampler: + case spv::Op::OpTypeImage: + if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) + return vstate.samplerimage_variable_address_mode() / 8; + assert(0); + return 0; + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + return words[2] / 8; + case spv::Op::OpTypeVector: { + const auto componentId = words[2]; + const auto numComponents = words[3]; + const auto componentSize = + getSize(componentId, inherited, constraints, vstate); + const auto size = componentSize * numComponents; + return size; + } + case spv::Op::OpTypeArray: { + const auto sizeInst = vstate.FindDef(words[3]); + if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0; + assert(spv::Op::OpConstant == sizeInst->opcode()); + const uint32_t num_elem = sizeInst->words()[3]; + const uint32_t elem_type = words[2]; + const uint32_t elem_size = + getSize(elem_type, inherited, constraints, vstate); + // Account for gaps due to alignments in the first N-1 elements, + // then add the size of the last element. + const auto size = + (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size; + return size; + } + case spv::Op::OpTypeRuntimeArray: + return 0; + case spv::Op::OpTypeMatrix: { + const auto num_columns = words[3]; + if (inherited.majorness == kColumnMajor) { + return num_columns * inherited.matrix_stride; + } else { + // Row major case. + const auto column_type = words[2]; + const auto component_inst = vstate.FindDef(column_type); + const auto num_rows = component_inst->words()[3]; + const auto scalar_elem_type = component_inst->words()[2]; + const uint32_t scalar_elem_size = + getSize(scalar_elem_type, inherited, constraints, vstate); + return (num_rows - 1) * inherited.matrix_stride + + num_columns * scalar_elem_size; + } + } + case spv::Op::OpTypeStruct: { + const auto& members = getStructMembers(member_id, vstate); + if (members.empty()) return 0; + const auto lastIdx = uint32_t(members.size() - 1); + const auto& lastMember = members.back(); + uint32_t offset = 0xffffffff; + // Find the offset of the last element and add the size. + auto member_decorations = + vstate.id_member_decorations(member_id, lastIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)lastIdx); + if (spv::Decoration::Offset == decoration->dec_type()) { + offset = decoration->params()[0]; + } + } + // This check depends on the fact that all members have offsets. This + // has been checked earlier in the flow. + assert(offset != 0xffffffff); + const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)]; + return offset + getSize(lastMember, constraint, constraints, vstate); + } + case spv::Op::OpTypePointer: + case spv::Op::OpTypeUntypedPointerKHR: + return vstate.pointer_size_and_alignment(); + default: + assert(0); + return 0; + } +} + +// A member is defined to improperly straddle if either of the following are +// true: +// - It is a vector with total size less than or equal to 16 bytes, and has +// Offset decorations placing its first byte at F and its last byte at L, where +// floor(F / 16) != floor(L / 16). +// - It is a vector with total size greater than 16 bytes and has its Offset +// decorations placing its first byte at a non-integer multiple of 16. +bool hasImproperStraddle(uint32_t id, uint32_t offset, + const LayoutConstraints& inherited, + MemberConstraints& constraints, + ValidationState_t& vstate) { + const auto size = getSize(id, inherited, constraints, vstate); + const auto F = offset; + const auto L = offset + size - 1; + if (size <= 16) { + if ((F >> 4) != (L >> 4)) return true; + } else { + if (F % 16 != 0) return true; + } + return false; +} + +// Returns true if |offset| satsifies an alignment to |alignment|. In the case +// of |alignment| of zero, the |offset| must also be zero. +bool IsAlignedTo(uint32_t offset, uint32_t alignment) { + if (alignment == 0) return offset == 0; + return 0 == (offset % alignment); +} + +// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for +// Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns +// something other than SPV_SUCCESS. Matrices inherit the specified column +// or row major-ness. +spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, + const char* decoration_str, bool blockRules, + bool scalar_block_layout, + uint32_t incoming_offset, + MemberConstraints& constraints, + ValidationState_t& vstate) { + if (vstate.options()->skip_block_layout) return SPV_SUCCESS; + + // blockRules are the same as bufferBlock rules if the uniform buffer + // standard layout extension is being used. + if (vstate.options()->uniform_buffer_standard_layout) blockRules = false; + + // Relaxed layout and scalar layout can both be in effect at the same time. + // For example, relaxed layout is implied by Vulkan 1.1. But scalar layout + // is more permissive than relaxed layout. + const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout(); + + auto fail = [&vstate, struct_id, storage_class_str, decoration_str, + blockRules, relaxed_block_layout, + scalar_block_layout](uint32_t member_idx) -> DiagnosticStream { + DiagnosticStream ds = + std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) + << "Structure id " << struct_id << " decorated as " + << decoration_str << " for variable in " << storage_class_str + << " storage class must follow " + << (scalar_block_layout + ? "scalar " + : (relaxed_block_layout ? "relaxed " : "standard ")) + << (blockRules ? "uniform buffer" : "storage buffer") + << " layout rules: member " << member_idx << " "); + return ds; + }; + + // If we are checking the layout of untyped pointers or physical storage + // buffer pointers, we may not actually have a struct here. Instead, pretend + // we have a struct with a single member at offset 0. + const auto& struct_type = vstate.FindDef(struct_id); + std::vector members; + if (struct_type->opcode() == spv::Op::OpTypeStruct) { + members = getStructMembers(struct_id, vstate); + } else { + members.push_back(struct_id); + } + + // To check for member overlaps, we want to traverse the members in + // offset order. + struct MemberOffsetPair { + uint32_t member; + uint32_t offset; + }; + std::vector member_offsets; + + // With untyped pointers or physical storage buffers, we might be checking + // layouts that do not originate from a structure. + if (struct_type->opcode() == spv::Op::OpTypeStruct) { + member_offsets.reserve(members.size()); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; memberIdx++) { + uint32_t offset = 0xffffffff; + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case spv::Decoration::Offset: + offset = decoration->params()[0]; + break; + default: + break; + } + } + member_offsets.push_back( + MemberOffsetPair{memberIdx, incoming_offset + offset}); + } + std::stable_sort( + member_offsets.begin(), member_offsets.end(), + [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) { + return lhs.offset < rhs.offset; + }); + } else { + member_offsets.push_back({0, 0}); + } + + // Now scan from lowest offset to highest offset. + uint32_t nextValidOffset = 0; + for (size_t ordered_member_idx = 0; + ordered_member_idx < member_offsets.size(); ordered_member_idx++) { + const auto& member_offset = member_offsets[ordered_member_idx]; + const auto memberIdx = member_offset.member; + const auto offset = member_offset.offset; + auto id = members[member_offset.member]; + const LayoutConstraints& constraint = + constraints[std::make_pair(struct_id, uint32_t(memberIdx))]; + // Scalar layout takes precedence because it's more permissive, and implying + // an alignment that divides evenly into the alignment that would otherwise + // be used. + const auto alignment = + scalar_block_layout + ? getScalarAlignment(id, vstate) + : getBaseAlignment(id, blockRules, constraint, constraints, vstate); + const auto inst = vstate.FindDef(id); + const auto opcode = inst->opcode(); + const auto size = getSize(id, constraint, constraints, vstate); + // Check offset. + if (offset == 0xffffffff) + return fail(memberIdx) << "is missing an Offset decoration"; + + if (opcode == spv::Op::OpTypeRuntimeArray && + ordered_member_idx != member_offsets.size() - 1) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) + << vstate.VkErrorID(4680) << "Structure id " << struct_id + << " has a runtime array at offset " << offset + << ", but other members at larger offsets"; + } + + if (!scalar_block_layout && relaxed_block_layout && + opcode == spv::Op::OpTypeVector) { + // In relaxed block layout, the vector offset must be aligned to the + // vector's scalar element type. + const auto componentId = inst->words()[2]; + const auto scalar_alignment = getScalarAlignment(componentId, vstate); + if (!IsAlignedTo(offset, scalar_alignment)) { + return fail(memberIdx) + << "at offset " << offset + << " is not aligned to scalar element size " << scalar_alignment; + } + } else { + // Without relaxed block layout, the offset must be divisible by the + // alignment requirement. + if (!IsAlignedTo(offset, alignment)) { + return fail(memberIdx) + << "at offset " << offset << " is not aligned to " << alignment; + } + } + if (offset < nextValidOffset) + return fail(memberIdx) << "at offset " << offset + << " overlaps previous member ending at offset " + << nextValidOffset - 1; + if (!scalar_block_layout && relaxed_block_layout) { + // Check improper straddle of vectors. + if (spv::Op::OpTypeVector == opcode && + hasImproperStraddle(id, offset, constraint, constraints, vstate)) + return fail(memberIdx) + << "is an improperly straddling vector at offset " << offset; + } + // Check struct members recursively. + spv_result_t recursive_status = SPV_SUCCESS; + if (spv::Op::OpTypeStruct == opcode && + SPV_SUCCESS != (recursive_status = checkLayout( + id, storage_class_str, decoration_str, blockRules, + scalar_block_layout, offset, constraints, vstate))) + return recursive_status; + // Check matrix stride. + if (spv::Op::OpTypeMatrix == opcode) { + const auto stride = constraint.matrix_stride; + if (!IsAlignedTo(stride, alignment)) { + return fail(memberIdx) << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment; + } + } + + // Check arrays and runtime arrays recursively. + auto array_inst = inst; + auto array_alignment = alignment; + while (array_inst->opcode() == spv::Op::OpTypeArray || + array_inst->opcode() == spv::Op::OpTypeRuntimeArray) { + const auto typeId = array_inst->word(2); + const auto element_inst = vstate.FindDef(typeId); + // Check array stride. + uint32_t array_stride = 0; + for (auto& decoration : vstate.id_decorations(array_inst->id())) { + if (spv::Decoration::ArrayStride == decoration.dec_type()) { + array_stride = decoration.params()[0]; + if (array_stride == 0) { + return fail(memberIdx) << "contains an array with stride 0"; + } + if (!IsAlignedTo(array_stride, array_alignment)) + return fail(memberIdx) + << "contains an array with stride " << decoration.params()[0] + << " not satisfying alignment to " << alignment; + } + } + + bool is_int32 = false; + bool is_const = false; + uint32_t num_elements = 0; + if (array_inst->opcode() == spv::Op::OpTypeArray) { + std::tie(is_int32, is_const, num_elements) = + vstate.EvalInt32IfConst(array_inst->word(3)); + } + num_elements = std::max(1u, num_elements); + // Check each element recursively if it is a struct. There is a + // limitation to this check if the array size is a spec constant or is a + // runtime array then we will only check a single element. This means + // some improper straddles might be missed. + if (spv::Op::OpTypeStruct == element_inst->opcode()) { + std::vector seen(16, false); + for (uint32_t i = 0; i < num_elements; ++i) { + uint32_t next_offset = i * array_stride + offset; + // Stop checking if offsets repeat in terms of 16-byte multiples. + if (seen[next_offset % 16]) { + break; + } + + if (SPV_SUCCESS != + (recursive_status = checkLayout( + typeId, storage_class_str, decoration_str, blockRules, + scalar_block_layout, next_offset, constraints, vstate))) + return recursive_status; + + seen[next_offset % 16] = true; + } + } else if (spv::Op::OpTypeMatrix == element_inst->opcode()) { + // Matrix stride would be on the array element in the struct. + const auto stride = constraint.matrix_stride; + if (!IsAlignedTo(stride, alignment)) { + return fail(memberIdx) + << "is a matrix with stride " << stride + << " not satisfying alignment to " << alignment; + } + } + + // Proceed to the element in case it is an array. + array_inst = element_inst; + array_alignment = scalar_block_layout + ? getScalarAlignment(array_inst->id(), vstate) + : getBaseAlignment(array_inst->id(), blockRules, + constraint, constraints, vstate); + + const auto element_size = + getSize(element_inst->id(), constraint, constraints, vstate); + if (element_size > array_stride) { + return fail(memberIdx) + << "contains an array with stride " << array_stride + << ", but with an element size of " << element_size; + } + } + nextValidOffset = offset + size; + if (!scalar_block_layout && + (spv::Op::OpTypeArray == opcode || spv::Op::OpTypeStruct == opcode)) { + // Non-scalar block layout rules don't permit anything in the padding of + // a struct or array. + nextValidOffset = align(nextValidOffset, alignment); + } + } + return SPV_SUCCESS; +} + +// Returns true if variable or structure id has given decoration. Handles also +// nested structures. +bool hasDecoration(uint32_t id, spv::Decoration decoration, + ValidationState_t& vstate) { + for (auto& dec : vstate.id_decorations(id)) { + if (decoration == dec.dec_type()) return true; + } + if (spv::Op::OpTypeStruct != vstate.FindDef(id)->opcode()) { + return false; + } + for (auto member_id : getStructMembers(id, spv::Op::OpTypeStruct, vstate)) { + if (hasDecoration(member_id, decoration, vstate)) { + return true; + } + } + return false; +} + +// Returns true if all ids of given type have a specified decoration. +bool checkForRequiredDecoration(uint32_t struct_id, + std::function checker, + spv::Op type, ValidationState_t& vstate) { + const auto& members = getStructMembers(struct_id, vstate); + for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) { + auto id = members[memberIdx]; + if (type == spv::Op::OpTypeMatrix) { + // Matrix decorations also apply to arrays of matrices. + auto memberInst = vstate.FindDef(id); + while (memberInst->opcode() == spv::Op::OpTypeArray || + memberInst->opcode() == spv::Op::OpTypeRuntimeArray) { + memberInst = vstate.FindDef(memberInst->GetOperandAs(1u)); + } + id = memberInst->id(); + } + if (type != vstate.FindDef(id)->opcode()) continue; + bool found = false; + for (auto& dec : vstate.id_decorations(id)) { + if (checker(dec.dec_type())) found = true; + } + for (auto& dec : vstate.id_decorations(struct_id)) { + if (checker(dec.dec_type()) && + (int)memberIdx == dec.struct_member_index()) { + found = true; + } + } + if (!found) { + return false; + } + } + for (auto id : getStructMembers(struct_id, spv::Op::OpTypeStruct, vstate)) { + if (!checkForRequiredDecoration(id, checker, type, vstate)) { + return false; + } + } + return true; +} + +spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) { + for (const auto& function : vstate.functions()) { + if (function.block_count() == 0u) { + // A function declaration (an OpFunction with no basic blocks), must have + // a Linkage Attributes Decoration with the Import Linkage Type. + if (!hasImportLinkageAttribute(function.id(), vstate)) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(function.id())) + << "Function declaration (id " << function.id() + << ") must have a LinkageAttributes decoration with the Import " + "Linkage type."; + } + } else { + if (hasImportLinkageAttribute(function.id(), vstate)) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(function.id())) + << "Function definition (id " << function.id() + << ") may not be decorated with Import Linkage type."; + } + } + } + return SPV_SUCCESS; +} + +// Checks whether an imported variable is initialized by this module. +spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) { + // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported + // variable. This means that a module-scope OpVariable with initialization + // value cannot be marked with the Import Linkage Type (import type id = 1). + for (auto global_var_id : vstate.global_vars()) { + // Initializer is an optional argument for OpVariable. If initializer + // is present, the instruction will have 5 words. + auto variable_instr = vstate.FindDef(global_var_id); + if (variable_instr->words().size() == 5u && + hasImportLinkageAttribute(global_var_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, variable_instr) + << "A module-scope OpVariable with initialization value " + "cannot be marked with the Import Linkage Type."; + } + } + return SPV_SUCCESS; +} + +// Checks whether a builtin variable is valid. +spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(var_id); + for (const auto& d : decorations) { + if (spvIsVulkanEnv(vstate.context()->target_env)) { + if (d.dec_type() == spv::Decoration::Location || + d.dec_type() == spv::Decoration::Component) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id + << ") cannot have any Location or Component decorations"; + } + } + } + return SPV_SUCCESS; +} + +// Checks whether proper decorations have been applied to the entry points. +spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { + for (uint32_t entry_point : vstate.entry_points()) { + const auto& descs = vstate.entry_point_descriptions(entry_point); + int num_builtin_block_inputs = 0; + int num_builtin_block_outputs = 0; + int num_workgroup_variables = 0; + int num_workgroup_variables_with_block = 0; + int num_workgroup_variables_with_aliased = 0; + bool has_task_payload = false; + for (const auto& desc : descs) { + std::unordered_set seen_vars; + std::unordered_set input_var_builtin; + std::unordered_set output_var_builtin; + for (auto interface : desc.interfaces) { + Instruction* var_instr = vstate.FindDef(interface); + if (!var_instr || + (spv::Op::OpVariable != var_instr->opcode() && + spv::Op::OpUntypedVariableKHR != var_instr->opcode())) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Interfaces passed to OpEntryPoint must be variables. " + "Found Op" + << spvOpcodeString(var_instr->opcode()) << "."; + } + const bool untyped_pointers = + var_instr->opcode() == spv::Op::OpUntypedVariableKHR; + const auto sc_index = 2u; + const spv::StorageClass storage_class = + var_instr->GetOperandAs(sc_index); + if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // SPV_EXT_mesh_shader, at most one task payload is permitted + // per entry point + if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { + if (has_task_payload) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "There can be at most one OpVariable with storage " + "class TaskPayloadWorkgroupEXT associated with " + "an OpEntryPoint"; + } + has_task_payload = true; + } + } + if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Starting in 1.4, OpEntryPoint must list all global variables + // it statically uses and those interfaces must be unique. + if (storage_class == spv::StorageClass::Function) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "OpEntryPoint interfaces should only list global " + "variables"; + } + + if (!seen_vars.insert(var_instr).second) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Non-unique OpEntryPoint interface " + << vstate.getIdName(interface) << " is disallowed"; + } + } else { + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << uint32_t(storage_class) << " for Entry Point id " + << entry_point << "."; + } + } + + // It is guaranteed (by validator ID checks) that ptr_instr is + // OpTypePointer. Word 3 of this instruction is the type being pointed + // to. For untyped variables, the pointee type comes from the data type + // operand. + const uint32_t type_id = + untyped_pointers ? var_instr->word(4) + : vstate.FindDef(var_instr->word(1))->word(3); + Instruction* type_instr = vstate.FindDef(type_id); + const bool is_struct = + type_instr && spv::Op::OpTypeStruct == type_instr->opcode(); + + // Search all Built-in (on the variable or the struct) + bool has_built_in = false; + for (auto& dec : + vstate.id_decorations(is_struct ? type_id : interface)) { + if (dec.dec_type() != spv::Decoration::BuiltIn) continue; + has_built_in = true; + + if (!spvIsVulkanEnv(vstate.context()->target_env)) continue; + + const spv::BuiltIn builtin = dec.builtin(); + if (storage_class == spv::StorageClass::Input) { + if (!input_var_builtin.insert(builtin).second) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(9658) + << "OpEntryPoint contains duplicate input variables " + "with " + << vstate.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin) + << " builtin"; + } + } + if (storage_class == spv::StorageClass::Output) { + if (!output_var_builtin.insert(builtin).second) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(9659) + << "OpEntryPoint contains duplicate output variables " + "with " + << vstate.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin) + << " builtin"; + } + } + } + + if (has_built_in) { + if (auto error = CheckBuiltInVariable(interface, vstate)) + return error; + + if (is_struct) { + if (!isBlock(type_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, + vstate.FindDef(type_id)) + << vstate.VkErrorID(4919) + << "Interface struct has no Block decoration but has " + "BuiltIn members. " + "Location decorations must be used on each member of " + "OpVariable with a structure type that is a block not " + "decorated with Location."; + } + if (storage_class == spv::StorageClass::Input) + ++num_builtin_block_inputs; + if (storage_class == spv::StorageClass::Output) + ++num_builtin_block_outputs; + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) + break; + } + } + + if (storage_class == spv::StorageClass::Workgroup) { + ++num_workgroup_variables; + if (type_instr) { + if (spv::Op::OpTypeStruct == type_instr->opcode()) { + if (hasDecoration(type_id, spv::Decoration::Block, vstate)) { + ++num_workgroup_variables_with_block; + } else if (untyped_pointers && + vstate.HasCapability(spv::Capability::Shader)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Untyped workgroup variables in shaders must be " + "block decorated"; + } + if (hasDecoration(var_instr->id(), spv::Decoration::Aliased, + vstate)) + ++num_workgroup_variables_with_aliased; + } else if (untyped_pointers && + vstate.HasCapability(spv::Capability::Shader)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Untyped workgroup variables in shaders must be block " + "decorated structs"; + } + } + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const auto* models = vstate.GetExecutionModels(entry_point); + const bool has_frag = + models->find(spv::ExecutionModel::Fragment) != models->end(); + const bool has_vert = + models->find(spv::ExecutionModel::Vertex) != models->end(); + for (const auto& decoration : + vstate.id_decorations(var_instr->id())) { + if (decoration == spv::Decoration::Flat || + decoration == spv::Decoration::NoPerspective || + decoration == spv::Decoration::Sample || + decoration == spv::Decoration::Centroid) { + // VUID 04670 already validates these decorations are input/output + if (storage_class == spv::StorageClass::Input && + (models->size() > 1 || has_vert)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6202) + << vstate.SpvDecorationString(decoration.dec_type()) + << " decorated variable must not be used in vertex " + "execution model as an Input storage class for Entry " + "Point id " + << entry_point << "."; + } else if (storage_class == spv::StorageClass::Output && + (models->size() > 1 || has_frag)) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(6201) + << vstate.SpvDecorationString(decoration.dec_type()) + << " decorated variable must not be used in fragment " + "execution model as an Output storage class for " + "Entry Point id " + << entry_point << "."; + } + } + } + + const bool has_flat = + hasDecoration(var_instr->id(), spv::Decoration::Flat, vstate); + if (has_frag && storage_class == spv::StorageClass::Input && + !has_flat && + ((vstate.IsFloatScalarType(type_id) && + vstate.GetBitWidth(type_id) == 64) || + vstate.IsIntScalarOrVectorType(type_id))) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << vstate.VkErrorID(4744) + << "Fragment OpEntryPoint operand " + << interface << " with Input interfaces with integer or " + "float type must have a Flat decoration " + "for Entry Point id " + << entry_point << "."; + } + } + } + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "There must be at most one object per Storage Class that can " + "contain a structure type containing members decorated with " + "BuiltIn, consumed per entry-point. Entry Point id " + << entry_point << " does not meet this requirement."; + } + // The LinkageAttributes Decoration cannot be applied to functions + // targeted by an OpEntryPoint instruction + for (auto& decoration : vstate.id_decorations(entry_point)) { + if (spv::Decoration::LinkageAttributes == decoration.dec_type()) { + const std::string linkage_name = + spvtools::utils::MakeString(decoration.params()); + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "The LinkageAttributes Decoration (Linkage name: " + << linkage_name << ") cannot be applied to function id " + << entry_point + << " because it is targeted by an OpEntryPoint instruction."; + } + } + + const bool workgroup_blocks_allowed = vstate.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + if (workgroup_blocks_allowed && + !vstate.HasCapability(spv::Capability::UntypedPointersKHR) && + num_workgroup_variables > 0 && + num_workgroup_variables_with_block > 0) { + if (num_workgroup_variables != num_workgroup_variables_with_block) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "When declaring WorkgroupMemoryExplicitLayoutKHR, " + "either all or none of the Workgroup Storage Class " + "variables " + "in the entry point interface must point to struct types " + "decorated with Block (unless the " + "UntypedPointersKHR capability is declared). " + "Entry point id " + << entry_point << " does not meet this requirement."; + } + if (num_workgroup_variables_with_block > 1 && + num_workgroup_variables_with_block != + num_workgroup_variables_with_aliased) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "When declaring WorkgroupMemoryExplicitLayoutKHR, " + "if more than one Workgroup Storage Class variable in " + "the entry point interface point to a type decorated " + "with Block, all of them must be decorated with Aliased " + "(unless the UntypedPointerWorkgroupKHR capability is " + "declared). Entry point id " + << entry_point << " does not meet this requirement."; + } + } else if (!workgroup_blocks_allowed && + num_workgroup_variables_with_block > 0) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "Workgroup Storage Class variables can't be decorated with " + "Block unless declaring the WorkgroupMemoryExplicitLayoutKHR " + "capability."; + } + } + } + return SPV_SUCCESS; +} + +// Load |constraints| with all the member constraints for structs contained +// within the given array type. +void ComputeMemberConstraintsForArray(MemberConstraints* constraints, + uint32_t array_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate); + +// Load |constraints| with all the member constraints for the given struct, +// and all its contained structs. +void ComputeMemberConstraintsForStruct(MemberConstraints* constraints, + uint32_t struct_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate) { + assert(constraints); + const auto& members = getStructMembers(struct_id, vstate); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; memberIdx++) { + LayoutConstraints& constraint = + (*constraints)[std::make_pair(struct_id, memberIdx)]; + constraint = inherited; + auto member_decorations = + vstate.id_member_decorations(struct_id, memberIdx); + for (auto decoration = member_decorations.begin; + decoration != member_decorations.end; ++decoration) { + assert(decoration->struct_member_index() == (int)memberIdx); + switch (decoration->dec_type()) { + case spv::Decoration::RowMajor: + constraint.majorness = kRowMajor; + break; + case spv::Decoration::ColMajor: + constraint.majorness = kColumnMajor; + break; + case spv::Decoration::MatrixStride: + constraint.matrix_stride = decoration->params()[0]; + break; + default: + break; + } + } + + // Now recurse + auto member_type_id = members[memberIdx]; + const auto member_type_inst = vstate.FindDef(member_type_id); + const auto opcode = member_type_inst->opcode(); + switch (opcode) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + ComputeMemberConstraintsForArray(constraints, member_type_id, inherited, + vstate); + break; + case spv::Op::OpTypeStruct: + ComputeMemberConstraintsForStruct(constraints, member_type_id, + inherited, vstate); + break; + default: + break; + } + } +} + +void ComputeMemberConstraintsForArray(MemberConstraints* constraints, + uint32_t array_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate) { + assert(constraints); + auto elem_type_id = vstate.FindDef(array_id)->words()[2]; + const auto elem_type_inst = vstate.FindDef(elem_type_id); + const auto opcode = elem_type_inst->opcode(); + switch (opcode) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited, + vstate); + break; + case spv::Op::OpTypeStruct: + ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited, + vstate); + break; + default: + break; + } +} + +spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { + // Set of entry points that are known to use a push constant. + std::unordered_set uses_push_constant; + for (const auto& inst : vstate.ordered_instructions()) { + const auto& words = inst.words(); + auto type_id = inst.type_id(); + const Instruction* type_inst = vstate.FindDef(type_id); + bool scalar_block_layout = false; + MemberConstraints constraints; + if (spv::Op::OpVariable == inst.opcode() || + spv::Op::OpUntypedVariableKHR == inst.opcode()) { + const bool untyped_pointer = + inst.opcode() == spv::Op::OpUntypedVariableKHR; + const auto var_id = inst.id(); + // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset + // and Stride Assignment". + const auto storageClassVal = words[3]; + const auto storageClass = spv::StorageClass(storageClassVal); + const bool uniform = storageClass == spv::StorageClass::Uniform; + const bool uniform_constant = + storageClass == spv::StorageClass::UniformConstant; + const bool push_constant = + storageClass == spv::StorageClass::PushConstant; + const bool storage_buffer = + storageClass == spv::StorageClass::StorageBuffer; + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + // Vulkan: There must be no more than one PushConstant block per entry + // point. + if (push_constant) { + auto entry_points = vstate.EntryPointReferences(var_id); + for (auto ep_id : entry_points) { + const bool already_used = !uses_push_constant.insert(ep_id).second; + if (already_used) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6674) << "Entry point id '" << ep_id + << "' uses more than one PushConstant interface.\n" + << "From Vulkan spec:\n" + << "There must be no more than one push constant block " + << "statically used per shader entry point."; + } + } + } + // Vulkan: Check DescriptorSet and Binding decoration for + // UniformConstant which cannot be a struct. + if (uniform_constant) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id + << "' is missing DescriptorSet decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + if (!entry_points.empty() && + !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id + << "' is missing Binding decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + } + } + + if (spvIsOpenGLEnv(vstate.context()->target_env)) { + bool has_block = hasDecoration(var_id, spv::Decoration::Block, vstate); + bool has_buffer_block = + hasDecoration(var_id, spv::Decoration::BufferBlock, vstate); + if ((uniform && (has_block || has_buffer_block)) || + (storage_buffer && has_block)) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << (uniform ? "Uniform" : "Storage Buffer") << " id '" + << var_id << "' is missing Binding decoration.\n" + << "From ARB_gl_spirv extension:\n" + << "Uniform and shader storage block variables must " + << "also be decorated with a *Binding*."; + } + } + } + + const bool phys_storage_buffer = + storageClass == spv::StorageClass::PhysicalStorageBuffer; + const bool workgroup = + storageClass == spv::StorageClass::Workgroup && + vstate.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + if (uniform || push_constant || storage_buffer || phys_storage_buffer || + workgroup) { + const auto ptrInst = vstate.FindDef(words[1]); + assert(spv::Op::OpTypePointer == ptrInst->opcode() || + spv::Op::OpTypeUntypedPointerKHR == ptrInst->opcode()); + auto id = untyped_pointer ? (words.size() > 4 ? words[4] : 0) + : ptrInst->words()[3]; + if (id != 0) { + auto id_inst = vstate.FindDef(id); + // Jump through one level of arraying. + if (!workgroup && + (id_inst->opcode() == spv::Op::OpTypeArray || + id_inst->opcode() == spv::Op::OpTypeRuntimeArray)) { + id = id_inst->GetOperandAs(1u); + id_inst = vstate.FindDef(id); + } + // Struct requirement is checked on variables so just move on here. + if (spv::Op::OpTypeStruct != id_inst->opcode()) continue; + ComputeMemberConstraintsForStruct(&constraints, id, + LayoutConstraints(), vstate); + } + // Prepare for messages + const char* sc_str = + uniform ? "Uniform" + : (push_constant ? "PushConstant" + : (workgroup ? "Workgroup" + : "StorageBuffer")); + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const bool block = hasDecoration(id, spv::Decoration::Block, vstate); + const bool buffer_block = + hasDecoration(id, spv::Decoration::BufferBlock, vstate); + if (storage_buffer && buffer_block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id + << " In Vulkan, BufferBlock is disallowed on variables in " + "the StorageBuffer storage class"; + } + // Vulkan: Check Block decoration for PushConstant, Uniform + // and StorageBuffer variables. Uniform can also use BufferBlock. + if (push_constant && !block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << vstate.VkErrorID(6675) << "PushConstant id '" << id + << "' is missing Block decoration.\n" + << "From Vulkan spec:\n" + << "Such variables must be identified with a Block " + "decoration"; + } + if (storage_buffer && !block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << vstate.VkErrorID(6675) << "StorageBuffer id '" << id + << "' is missing Block decoration.\n" + << "From Vulkan spec:\n" + << "Such variables must be identified with a Block " + "decoration"; + } + if (uniform && !block && !buffer_block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << vstate.VkErrorID(6676) << "Uniform id '" << id + << "' is missing Block or BufferBlock decoration.\n" + << "From Vulkan spec:\n" + << "Such variables must be identified with a Block or " + "BufferBlock decoration"; + } + // Vulkan: Check DescriptorSet and Binding decoration for + // Uniform and StorageBuffer variables. + if (uniform || storage_buffer) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, spv::Decoration::DescriptorSet, + vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id + << "' is missing DescriptorSet decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + if (!entry_points.empty() && + !hasDecoration(var_id, spv::Decoration::Binding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id + << "' is missing Binding decoration.\n" + << "From Vulkan spec:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + } + } + + if (id != 0) { + for (const auto& dec : vstate.id_decorations(id)) { + const bool blockDeco = spv::Decoration::Block == dec.dec_type(); + const bool bufferDeco = + spv::Decoration::BufferBlock == dec.dec_type(); + const bool blockRules = uniform && blockDeco; + const bool bufferRules = (uniform && bufferDeco) || + ((push_constant || storage_buffer || + phys_storage_buffer || workgroup) && + blockDeco); + if (uniform && blockDeco) { + vstate.RegisterPointerToUniformBlock(ptrInst->id()); + vstate.RegisterStructForUniformBlock(id); + } + if ((uniform && bufferDeco) || + ((storage_buffer || phys_storage_buffer) && blockDeco)) { + vstate.RegisterPointerToStorageBuffer(ptrInst->id()); + vstate.RegisterStructForStorageBuffer(id); + } + + if (blockRules || bufferRules) { + const char* deco_str = blockDeco ? "Block" : "BufferBlock"; + spv_result_t recursive_status = SPV_SUCCESS; + scalar_block_layout = + workgroup ? vstate.options()->workgroup_scalar_block_layout + : vstate.options()->scalar_block_layout; + + if (isMissingOffsetInStruct(id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with Offset " + "decorations."; + } + + if (!checkForRequiredDecoration( + id, + [](spv::Decoration d) { + return d == spv::Decoration::ArrayStride; + }, + spv::Op::OpTypeArray, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with ArrayStride " + "decorations."; + } + + if (!checkForRequiredDecoration( + id, + [](spv::Decoration d) { + return d == spv::Decoration::MatrixStride; + }, + spv::Op::OpTypeMatrix, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with MatrixStride " + "decorations."; + } + + if (!checkForRequiredDecoration( + id, + [](spv::Decoration d) { + return d == spv::Decoration::RowMajor || + d == spv::Decoration::ColMajor; + }, + spv::Op::OpTypeMatrix, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with RowMajor or " + "ColMajor decorations."; + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + if (blockRules && + (SPV_SUCCESS != + (recursive_status = checkLayout(id, sc_str, deco_str, true, + scalar_block_layout, 0, + constraints, vstate)))) { + return recursive_status; + } else if (bufferRules && + (SPV_SUCCESS != (recursive_status = checkLayout( + id, sc_str, deco_str, false, + scalar_block_layout, 0, + constraints, vstate)))) { + return recursive_status; + } + } + } + } + } + } + } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer && + type_inst->GetOperandAs(1u) == + spv::StorageClass::PhysicalStorageBuffer) { + const bool buffer = true; + const auto pointee_type_id = type_inst->GetOperandAs(2u); + const auto* data_type_inst = vstate.FindDef(pointee_type_id); + scalar_block_layout = vstate.options()->scalar_block_layout; + if (data_type_inst->opcode() == spv::Op::OpTypeStruct) { + ComputeMemberConstraintsForStruct(&constraints, pointee_type_id, + LayoutConstraints(), vstate); + } + if (auto res = checkLayout(pointee_type_id, "PhysicalStorageBuffer", + "Block", !buffer, scalar_block_layout, 0, + constraints, vstate)) { + return res; + } + } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) && + spvIsVulkanEnv(vstate.context()->target_env)) { + // Untyped variables are checked above. Here we check that instructions + // using an untyped pointer have a valid layout. + uint32_t ptr_ty_id = 0; + uint32_t data_type_id = 0; + switch (inst.opcode()) { + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: + ptr_ty_id = inst.type_id(); + data_type_id = inst.GetOperandAs(2); + break; + case spv::Op::OpLoad: + if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 2)) == + spv::Op::OpTypeUntypedPointerKHR) { + const auto ptr_id = inst.GetOperandAs(2); + ptr_ty_id = vstate.FindDef(ptr_id)->type_id(); + data_type_id = inst.type_id(); + } + break; + case spv::Op::OpStore: + if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 0)) == + spv::Op::OpTypeUntypedPointerKHR) { + const auto ptr_id = inst.GetOperandAs(0); + ptr_ty_id = vstate.FindDef(ptr_id)->type_id(); + data_type_id = vstate.GetOperandTypeId(&inst, 1); + } + break; + case spv::Op::OpUntypedArrayLengthKHR: + ptr_ty_id = vstate.FindDef(inst.GetOperandAs(3))->type_id(); + data_type_id = inst.GetOperandAs(2); + break; + default: + break; + } + + if (ptr_ty_id == 0 || data_type_id == 0) { + // Not an untyped pointer. + continue; + } + + const auto sc = + vstate.FindDef(ptr_ty_id)->GetOperandAs(1); + + const char* sc_str = + sc == spv::StorageClass::Uniform + ? "Uniform" + : (sc == spv::StorageClass::PushConstant + ? "PushConstant" + : (sc == spv::StorageClass::Workgroup ? "Workgroup" + : "StorageBuffer")); + + auto data_type = vstate.FindDef(data_type_id); + scalar_block_layout = + sc == spv::StorageClass::Workgroup + ? vstate.options()->workgroup_scalar_block_layout + : vstate.options()->scalar_block_layout; + + // If the data type is an array that contains a Block- or + // BufferBlock-decorated struct, then use the struct for layout checks + // instead of the array. In this case, the array represents a descriptor + // array which should not have an explicit layout. + if (data_type->opcode() == spv::Op::OpTypeArray || + data_type->opcode() == spv::Op::OpTypeRuntimeArray) { + const auto ele_type = + vstate.FindDef(data_type->GetOperandAs(1u)); + if (ele_type->opcode() == spv::Op::OpTypeStruct && + (vstate.HasDecoration(ele_type->id(), spv::Decoration::Block) || + vstate.HasDecoration(ele_type->id(), + spv::Decoration::BufferBlock))) { + data_type = ele_type; + data_type_id = ele_type->id(); + } + } + + // Assume uniform storage class uses block rules unless we see a + // BufferBlock decorated struct in the data type. + bool bufferRules = sc == spv::StorageClass::Uniform ? false : true; + if (data_type->opcode() == spv::Op::OpTypeStruct) { + if (sc == spv::StorageClass::Uniform) { + bufferRules = + vstate.HasDecoration(data_type_id, spv::Decoration::BufferBlock); + } + ComputeMemberConstraintsForStruct(&constraints, data_type_id, + LayoutConstraints(), vstate); + } + const char* deco_str = + bufferRules + ? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block") + : "Block"; + if (auto result = + checkLayout(data_type_id, sc_str, deco_str, !bufferRules, + scalar_block_layout, 0, constraints, vstate)) { + return result; + } + } + } + return SPV_SUCCESS; +} + +// Returns true if |decoration| cannot be applied to the same id more than once. +bool AtMostOncePerId(spv::Decoration decoration) { + return decoration != spv::Decoration::UserSemantic && + decoration != spv::Decoration::FuncParamAttr; +} + +// Returns true if |decoration| cannot be applied to the same member more than +// once. +bool AtMostOncePerMember(spv::Decoration decoration) { + return decoration != spv::Decoration::UserSemantic; +} + +spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { + using PerIDKey = std::tuple; + using PerMemberKey = std::tuple; + + // An Array of pairs where the decorations in the pair cannot both be applied + // to the same id. + static const spv::Decoration mutually_exclusive_per_id[][2] = { + {spv::Decoration::Block, spv::Decoration::BufferBlock}, + {spv::Decoration::Restrict, spv::Decoration::Aliased}, + {spv::Decoration::RestrictPointer, spv::Decoration::AliasedPointer}}; + static const auto num_mutually_exclusive_per_id_pairs = + sizeof(mutually_exclusive_per_id) / (2 * sizeof(spv::Decoration)); + + // An Array of pairs where the decorations in the pair cannot both be applied + // to the same member. + static const spv::Decoration mutually_exclusive_per_member[][2] = { + {spv::Decoration::RowMajor, spv::Decoration::ColMajor}}; + static const auto num_mutually_exclusive_per_mem_pairs = + sizeof(mutually_exclusive_per_member) / (2 * sizeof(spv::Decoration)); + + std::set seen_per_id; + std::set seen_per_member; + + for (const auto& inst : vstate.ordered_instructions()) { + const auto& words = inst.words(); + if (spv::Op::OpDecorate == inst.opcode()) { + const auto id = words[1]; + const auto dec_type = static_cast(words[2]); + const auto k = PerIDKey(dec_type, id); + const auto already_used = !seen_per_id.insert(k).second; + if (already_used && AtMostOncePerId(dec_type)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "' decorated with " + << vstate.SpvDecorationString(dec_type) + << " multiple times is not allowed."; + } + // Verify certain mutually exclusive decorations are not both applied on + // an ID. + for (uint32_t pair_idx = 0; + pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) { + spv::Decoration excl_dec_type = spv::Decoration::Max; + if (mutually_exclusive_per_id[pair_idx][0] == dec_type) { + excl_dec_type = mutually_exclusive_per_id[pair_idx][1]; + } else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) { + excl_dec_type = mutually_exclusive_per_id[pair_idx][0]; + } else { + continue; + } + + const auto excl_k = PerIDKey(excl_dec_type, id); + if (seen_per_id.find(excl_k) != seen_per_id.end()) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "' decorated with both " + << vstate.SpvDecorationString(dec_type) << " and " + << vstate.SpvDecorationString(excl_dec_type) + << " is not allowed."; + } + } + } else if (spv::Op::OpMemberDecorate == inst.opcode()) { + const auto id = words[1]; + const auto member_id = words[2]; + const auto dec_type = static_cast(words[3]); + const auto k = PerMemberKey(dec_type, id, member_id); + const auto already_used = !seen_per_member.insert(k).second; + if (already_used && AtMostOncePerMember(dec_type)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "', member '" << member_id + << "' decorated with " << vstate.SpvDecorationString(dec_type) + << " multiple times is not allowed."; + } + // Verify certain mutually exclusive decorations are not both applied on + // a (ID, member) tuple. + for (uint32_t pair_idx = 0; + pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) { + spv::Decoration excl_dec_type = spv::Decoration::Max; + if (mutually_exclusive_per_member[pair_idx][0] == dec_type) { + excl_dec_type = mutually_exclusive_per_member[pair_idx][1]; + } else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) { + excl_dec_type = mutually_exclusive_per_member[pair_idx][0]; + } else { + continue; + } + + const auto excl_k = PerMemberKey(excl_dec_type, id, member_id); + if (seen_per_member.find(excl_k) != seen_per_member.end()) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "', member '" << member_id + << "' decorated with both " + << vstate.SpvDecorationString(dec_type) << " and " + << vstate.SpvDecorationString(excl_dec_type) + << " is not allowed."; + } + } + } + } + return SPV_SUCCESS; +} + +spv_result_t CheckVulkanMemoryModelDeprecatedDecorations( + ValidationState_t& vstate) { + if (vstate.memory_model() != spv::MemoryModel::VulkanKHR) return SPV_SUCCESS; + + std::string msg; + std::ostringstream str(msg); + for (const auto& def : vstate.all_definitions()) { + const auto inst = def.second; + const auto id = inst->id(); + for (const auto& dec : vstate.id_decorations(id)) { + const auto member = dec.struct_member_index(); + if (dec.dec_type() == spv::Decoration::Coherent || + dec.dec_type() == spv::Decoration::Volatile) { + str << (dec.dec_type() == spv::Decoration::Coherent ? "Coherent" + : "Volatile"); + str << " decoration targeting " << vstate.getIdName(id); + if (member != Decoration::kInvalidMember) { + str << " (member index " << member << ")"; + } + str << " is banned when using the Vulkan memory model."; + return vstate.diag(SPV_ERROR_INVALID_ID, inst) << str.str(); + } + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for FPRoundingMode +// decorations. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + // Validates width-only conversion instruction for floating-point object + // i.e., OpFConvert + if (inst.opcode() != spv::Op::OpFConvert) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to a " + "width-only conversion instruction for floating-point " + "object."; + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const auto mode = spv::FPRoundingMode(decoration.params()[0]); + if ((mode != spv::FPRoundingMode::RTE) && + (mode != spv::FPRoundingMode::RTZ)) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(4675) + << "In Vulkan, the FPRoundingMode mode must only by RTE or RTZ."; + } + } + + // Validates Object operand of an OpStore + for (const auto& use : inst.uses()) { + const auto store = use.first; + if (store->opcode() == spv::Op::OpFConvert) continue; + if (spvOpcodeIsDebug(store->opcode())) continue; + if (store->IsNonSemantic()) continue; + if (spvOpcodeIsDecoration(store->opcode())) continue; + if (store->opcode() != spv::Op::OpStore) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore."; + } + + if (use.second != 2) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore."; + } + + const auto ptr_inst = vstate.FindDef(store->GetOperandAs(0)); + const auto ptr_type = vstate.FindDef(ptr_inst->GetOperandAs(0)); + + const auto half_float_id = ptr_type->GetOperandAs(2); + if (!vstate.IsFloatScalarOrVectorType(half_float_id) || + vstate.GetBitWidth(half_float_id) != 16) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore storing through a pointer " + "to " + "a 16-bit floating-point scalar or vector object."; + } + + // Validates storage class of the pointer to the OpStore + const auto storage = ptr_type->GetOperandAs(1); + if (storage != spv::StorageClass::StorageBuffer && + storage != spv::StorageClass::Uniform && + storage != spv::StorageClass::PushConstant && + storage != spv::StorageClass::Input && + storage != spv::StorageClass::Output && + storage != spv::StorageClass::PhysicalStorageBuffer) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore in the StorageBuffer, " + "PhysicalStorageBuffer, Uniform, PushConstant, Input, or " + "Output Storage Classes."; + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. The |inst| parameter is the object being decorated. This must +// be called after TypePass and AnnotateCheckDecorationsOfBuffers are called. +spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + // First, it must be a variable or function parameter. + const auto opcode = inst.opcode(); + const auto type_id = inst.type_id(); + if (opcode != spv::Op::OpVariable && + opcode != spv::Op::OpUntypedVariableKHR && + opcode != spv::Op::OpFunctionParameter && + opcode != spv::Op::OpRawAccessChainNV) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of NonWritable decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + const auto var_storage_class = + opcode == spv::Op::OpVariable + ? inst.GetOperandAs(2) + : opcode == spv::Op::OpUntypedVariableKHR + ? inst.GetOperandAs(3) + : spv::StorageClass::Max; + if ((var_storage_class == spv::StorageClass::Function || + var_storage_class == spv::StorageClass::Private) && + vstate.features().nonwritable_var_in_function_or_private) { + // New permitted feature in SPIR-V 1.4. + } else if ( + // It may point to a UBO, SSBO, storage image, or raw access chain. + vstate.IsPointerToUniformBlock(type_id) || + vstate.IsPointerToStorageBuffer(type_id) || + vstate.IsPointerToStorageImage(type_id) || + opcode == spv::Op::OpRawAccessChainNV) { + } else { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of NonWritable decoration is invalid: must point to a " + "storage image, uniform block, " + << (vstate.features().nonwritable_var_in_function_or_private + ? "storage buffer, or variable in Private or Function " + "storage class" + : "or storage buffer"); + } + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for Uniform or +// UniformId decorations. Otherwise emits a diagnostic and returns something +// other than SPV_SUCCESS. Assumes each decoration on a group has been +// propagated down to the group members. The |inst| parameter is the object +// being decorated. +spv_result_t CheckUniformDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + const char* const dec_name = decoration.dec_type() == spv::Decoration::Uniform + ? "Uniform" + : "UniformId"; + + // Uniform or UniformId must decorate an "object" + // - has a result ID + // - is an instantiation of a non-void type. So it has a type ID, and that + // type is not void. + + // We already know the result ID is non-zero. + + if (inst.type_id() == 0) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to a non-object"; + } + if (Instruction* type_inst = vstate.FindDef(inst.type_id())) { + if (type_inst->opcode() == spv::Op::OpTypeVoid) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to a value with void type"; + } + } else { + // We might never get here because this would have been rejected earlier in + // the flow. + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to an object with invalid type"; + } + + // Use of Uniform with OpDecorate is checked elsewhere. + // Use of UniformId with OpDecorateId is checked elsewhere. + + if (decoration.dec_type() == spv::Decoration::UniformId) { + assert(decoration.params().size() == 1 && + "Grammar ensures UniformId has one parameter"); + + // The scope id is an execution scope. + if (auto error = + ValidateExecutionScope(vstate, &inst, decoration.params()[0])) + return error; + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or +// NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns +// something other than SPV_SUCCESS. Assumes each decoration on a group has been +// propagated down to the group members. +spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + switch (inst.opcode()) { + case spv::Op::OpIAdd: + case spv::Op::OpISub: + case spv::Op::OpIMul: + case spv::Op::OpShiftLeftLogical: + case spv::Op::OpSNegate: + return SPV_SUCCESS; + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + // TODO(dneto): Only certain extended instructions allow these + // decorations. For now allow anything. + return SPV_SUCCESS; + default: + break; + } + + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << (decoration.dec_type() == spv::Decoration::NoSignedWrap + ? "NoSignedWrap" + : "NoUnsignedWrap") + << " decoration may not be applied to " + << spvOpcodeString(inst.opcode()); +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the Component +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckComponentDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + assert(decoration.params().size() == 1 && + "Grammar ensures Component has one parameter"); + + uint32_t type_id; + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + const auto opcode = inst.opcode(); + if (opcode != spv::Op::OpVariable && + opcode != spv::Op::OpFunctionParameter) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + + // Only valid for the Input and Output Storage Classes. + const auto storage_class = opcode == spv::Op::OpVariable + ? inst.GetOperandAs(2) + : spv::StorageClass::Max; + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output && + storage_class != spv::StorageClass::Max) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration is invalid: must point to a " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << uint32_t(storage_class); + } + + type_id = inst.type_id(); + if (vstate.IsPointerType(type_id)) { + const auto pointer = vstate.FindDef(type_id); + type_id = pointer->GetOperandAs(2); + } + } else { + if (inst.opcode() != spv::Op::OpTypeStruct) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + type_id = inst.word(decoration.struct_member_index() + 2); + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + // Strip the array, if present. + while (vstate.GetIdOpcode(type_id) == spv::Op::OpTypeArray) { + type_id = vstate.FindDef(type_id)->word(2u); + } + + if (!vstate.IsIntScalarOrVectorType(type_id) && + !vstate.IsFloatScalarOrVectorType(type_id)) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(10583) + << "Component decoration specified for type " + << vstate.getIdName(type_id) << " that is not a scalar or vector"; + } + + const auto component = decoration.params()[0]; + if (component > 3) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(4920) + << "Component decoration value must not be greater than 3"; + } + + const auto dimension = vstate.GetDimension(type_id); + const auto bit_width = vstate.GetBitWidth(type_id); + if (bit_width == 16 || bit_width == 32) { + const auto sum_component = component + dimension; + if (sum_component > 4) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(4921) + << "Sequence of components starting with " << component + << " and ending with " << (sum_component - 1) + << " gets larger than 3"; + } + } else if (bit_width == 64) { + if (dimension > 2) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(7703) + << "Component decoration only allowed on 64-bit scalar and " + "2-component vector"; + } + if (component == 1 || component == 3) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(4923) + << "Component decoration value must not be 1 or 3 for 64-bit " + "data types"; + } + // 64-bit is double per component dimension + const auto sum_component = component + (2 * dimension); + if (sum_component > 4) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(4922) + << "Sequence of components starting with " << component + << " and ending with " << (sum_component - 1) + << " gets larger than 3"; + } + } + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the Block +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckBlockDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + if (inst.opcode() != spv::Op::OpTypeStruct) { + const char* const dec_name = decoration.dec_type() == spv::Decoration::Block + ? "Block" + : "BufferBlock"; + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration on a non-struct type."; + } + return SPV_SUCCESS; +} + +spv_result_t CheckLocationDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + if (inst.opcode() == spv::Op::OpVariable) return SPV_SUCCESS; + + if (decoration.struct_member_index() != Decoration::kInvalidMember && + inst.opcode() == spv::Op::OpTypeStruct) { + return SPV_SUCCESS; + } + + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Location decoration can only be applied to a variable or member " + "of a structure type"; +} + +spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + // This is not the most precise check, but the rules for RelaxPrecision are + // very general, and it will be difficult to implement precisely. For now, + // I will only check for the cases that cause problems for the optimizer. + if (!spvOpcodeGeneratesType(inst.opcode())) { + return SPV_SUCCESS; + } + + if (decoration.struct_member_index() != Decoration::kInvalidMember && + inst.opcode() == spv::Op::OpTypeStruct) { + return SPV_SUCCESS; + } + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "RelaxPrecision decoration cannot be applied to a type"; +} + +#define PASS_OR_BAIL_AT_LINE(X, LINE) \ + { \ + spv_result_t e##LINE = (X); \ + if (e##LINE != SPV_SUCCESS) return e##LINE; \ + } static_assert(true, "require extra semicolon") +#define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__) + +// Check rules for decorations where we start from the decoration rather +// than the decorated object. Assumes each decoration on a group have been +// propagated down to the group members. +spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { + // Some rules are only checked for shaders. + const bool is_shader = vstate.HasCapability(spv::Capability::Shader); + + for (const auto& kv : vstate.id_decorations()) { + const uint32_t id = kv.first; + const auto& decorations = kv.second; + if (decorations.empty()) continue; + + const Instruction* inst = vstate.FindDef(id); + assert(inst); + + // We assume the decorations applied to a decoration group have already + // been propagated down to the group members. + if (inst->opcode() == spv::Op::OpDecorationGroup) continue; + + for (const auto& decoration : decorations) { + switch (decoration.dec_type()) { + case spv::Decoration::Component: + PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::FPRoundingMode: + if (is_shader) + PASS_OR_BAIL( + CheckFPRoundingModeForShaders(vstate, *inst, decoration)); + break; + case spv::Decoration::NonWritable: + PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::Uniform: + case spv::Decoration::UniformId: + PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::NoSignedWrap: + case spv::Decoration::NoUnsignedWrap: + PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::Block: + case spv::Decoration::BufferBlock: + PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::Location: + PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration)); + break; + case spv::Decoration::RelaxedPrecision: + PASS_OR_BAIL( + CheckRelaxPrecisionDecoration(vstate, *inst, decoration)); + break; + default: + break; + } + } + } + return SPV_SUCCESS; +} + +bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) { + switch (sc) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::Uniform: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::PushConstant: + // Always explicitly laid out. + return true; + case spv::StorageClass::UniformConstant: + return false; + case spv::StorageClass::Workgroup: + return vstate.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR); + case spv::StorageClass::Function: + case spv::StorageClass::Private: + return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4); + case spv::StorageClass::Input: + case spv::StorageClass::Output: + // Block is used generally and mesh shaders use Offset. + return true; + default: + // TODO: Some storage classes in ray tracing use explicit layout + // decorations, but it is not well documented which. For now treat other + // storage classes as allowed to be laid out. See Vulkan internal issue + // 4192. + return true; + } +} + +bool UsesExplicitLayout(ValidationState_t& vstate, uint32_t type_id, + std::unordered_map& cache) { + if (type_id == 0) { + return false; + } + + if (cache.count(type_id)) { + return cache[type_id]; + } + + bool res = false; + const auto type_inst = vstate.FindDef(type_id); + if (type_inst->opcode() == spv::Op::OpTypeStruct || + type_inst->opcode() == spv::Op::OpTypeArray || + type_inst->opcode() == spv::Op::OpTypeRuntimeArray || + type_inst->opcode() == spv::Op::OpTypePointer || + type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + const auto& id_decs = vstate.id_decorations(); + const auto iter = id_decs.find(type_id); + if (iter != id_decs.end()) { + bool allowLayoutDecorations = false; + if (type_inst->opcode() == spv::Op::OpTypePointer) { + const auto sc = type_inst->GetOperandAs(1); + allowLayoutDecorations = AllowsLayout(vstate, sc); + } + if (!allowLayoutDecorations) { + res = std::any_of( + iter->second.begin(), iter->second.end(), [](const Decoration& d) { + return d.dec_type() == spv::Decoration::Block || + d.dec_type() == spv::Decoration::BufferBlock || + d.dec_type() == spv::Decoration::Offset || + d.dec_type() == spv::Decoration::ArrayStride || + d.dec_type() == spv::Decoration::MatrixStride; + }); + } + } + + if (!res) { + switch (type_inst->opcode()) { + case spv::Op::OpTypeStruct: + for (uint32_t i = 1; !res && i < type_inst->operands().size(); i++) { + res = UsesExplicitLayout( + vstate, type_inst->GetOperandAs(i), cache); + } + break; + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + res = UsesExplicitLayout(vstate, type_inst->GetOperandAs(1), + cache); + break; + case spv::Op::OpTypePointer: { + const auto sc = type_inst->GetOperandAs(1); + if (!AllowsLayout(vstate, sc)) { + res = UsesExplicitLayout( + vstate, type_inst->GetOperandAs(2), cache); + } + } + default: + break; + } + } + } + + cache[type_id] = res; + return res; +} + +spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) { + if (!spvIsVulkanEnv(vstate.context()->target_env)) { + return SPV_SUCCESS; + } + + std::unordered_map cache; + for (const auto& inst : vstate.ordered_instructions()) { + const auto type_id = inst.type_id(); + const auto type_inst = vstate.FindDef(type_id); + uint32_t fail_id = 0; + // Variables are the main place to check for improper decorations, but some + // untyped pointer instructions must also be checked since those types may + // never be instantiated by a variable. Unlike verifying a valid layout, + // physical storage buffer does not need checked here since it is always + // explicitly laid out. + switch (inst.opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: { + const auto sc = inst.GetOperandAs(2); + auto check_id = type_id; + if (inst.opcode() == spv::Op::OpUntypedVariableKHR) { + if (inst.operands().size() > 3) { + check_id = inst.GetOperandAs(3); + } + } + if (!AllowsLayout(vstate, sc) && + UsesExplicitLayout(vstate, check_id, cache)) { + fail_id = check_id; + } + break; + } + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: { + // Check both the base type and return type. The return type may have an + // invalid array stride. + const auto sc = type_inst->GetOperandAs(1); + const auto base_type_id = inst.GetOperandAs(2); + if (!AllowsLayout(vstate, sc)) { + if (UsesExplicitLayout(vstate, base_type_id, cache)) { + fail_id = base_type_id; + } else if (UsesExplicitLayout(vstate, type_id, cache)) { + fail_id = type_id; + } + } + break; + } + case spv::Op::OpUntypedArrayLengthKHR: { + // Check the data type. + const auto ptr_ty_id = + vstate.FindDef(inst.GetOperandAs(3))->type_id(); + const auto ptr_ty = vstate.FindDef(ptr_ty_id); + const auto sc = ptr_ty->GetOperandAs(1); + const auto base_type_id = inst.GetOperandAs(2); + if (!AllowsLayout(vstate, sc) && + UsesExplicitLayout(vstate, base_type_id, cache)) { + fail_id = base_type_id; + } + break; + } + case spv::Op::OpLoad: { + const auto ptr_id = inst.GetOperandAs(2); + const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id()); + if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + // For untyped pointers check the return type for an invalid layout. + const auto sc = ptr_type->GetOperandAs(1); + if (!AllowsLayout(vstate, sc) && + UsesExplicitLayout(vstate, type_id, cache)) { + fail_id = type_id; + } + } + break; + } + case spv::Op::OpStore: { + const auto ptr_id = inst.GetOperandAs(1); + const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id()); + if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + // For untyped pointers, check the type of the data operand for an + // invalid layout. + const auto sc = ptr_type->GetOperandAs(1); + const auto data_type_id = vstate.GetOperandTypeId(&inst, 2); + if (!AllowsLayout(vstate, sc) && + UsesExplicitLayout(vstate, data_type_id, cache)) { + fail_id = inst.GetOperandAs(2); + } + } + break; + } + default: + break; + } + if (fail_id != 0) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << vstate.VkErrorID(10684) + << "Invalid explicit layout decorations on type for operand " + << vstate.getIdName(fail_id); + } + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateDecorations(ValidationState_t& vstate) { + if (auto error = CheckImportedVariableInitialization(vstate)) return error; + if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error; + if (auto error = CheckDecorationsOfBuffers(vstate)) return error; + if (auto error = CheckDecorationsCompatibility(vstate)) return error; + if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error; + if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate)) + return error; + if (auto error = CheckDecorationsFromDecoration(vstate)) return error; + if (auto error = CheckInvalidVulkanExplicitLayout(vstate)) return error; + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_derivatives.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_derivatives.cpp new file mode 100644 index 00000000..1a473ba8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_derivatives.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of derivative SPIR-V instructions. + +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of derivative instructions. +spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpDPdx: + case spv::Op::OpDPdy: + case spv::Op::OpFwidth: + case spv::Op::OpDPdxFine: + case spv::Op::OpDPdyFine: + case spv::Op::OpFwidthFine: + case spv::Op::OpDPdxCoarse: + case spv::Op::OpDPdyCoarse: + case spv::Op::OpFwidthCoarse: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be float scalar or vector type: " + << spvOpcodeString(opcode); + } + if (!_.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeFloat, + 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result type component width must be 32 bits"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 2); + if (p_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected P type and Result Type to be the same: " + << spvOpcodeString(opcode); + } + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::Fragment && + model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::MeshEXT && + model != spv::ExecutionModel::TaskEXT) { + if (message) { + *message = + std::string( + "Derivative instructions require Fragment, GLCompute, " + "MeshEXT or TaskEXT execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([opcode](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models && + (models->find(spv::ExecutionModel::GLCompute) != + models->end() || + models->find(spv::ExecutionModel::MeshEXT) != models->end() || + models->find(spv::ExecutionModel::TaskEXT) != models->end()) && + (!modes || + (modes->find(spv::ExecutionMode::DerivativeGroupLinearKHR) == + modes->end() && + modes->find(spv::ExecutionMode::DerivativeGroupQuadsKHR) == + modes->end()))) { + if (message) { + *message = + std::string( + "Derivative instructions require " + "DerivativeGroupQuadsKHR " + "or DerivativeGroupLinearKHR execution mode for " + "GLCompute, MeshEXT or TaskEXT execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_execution_limitations.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_execution_limitations.cpp new file mode 100644 index 00000000..0221d7ef --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_execution_limitations.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateExecutionLimitations(ValidationState_t& _, + const Instruction* inst) { + if (inst->opcode() != spv::Op::OpFunction) { + return SPV_SUCCESS; + } + + const auto func = _.function(inst->id()); + if (!func) { + return _.diag(SPV_ERROR_INTERNAL, inst) + << "Internal error: missing function id " << inst->id() << "."; + } + + for (uint32_t entry_id : _.FunctionEntryPoints(inst->id())) { + const auto* models = _.GetExecutionModels(entry_id); + if (models) { + if (models->empty()) { + return _.diag(SPV_ERROR_INTERNAL, inst) + << "Internal error: empty execution models for function id " + << entry_id << "."; + } + for (const auto model : *models) { + std::string reason; + if (!func->IsCompatibleWithExecutionModel(model, &reason)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point " << _.getIdName(entry_id) + << "s callgraph contains function " + << _.getIdName(inst->id()) + << ", which cannot be used with the current execution " + "model:\n" + << reason; + } + } + } + + std::string reason; + if (!func->CheckLimitations(_, _.function(entry_id), &reason)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point " << _.getIdName(entry_id) + << "s callgraph contains function " << _.getIdName(inst->id()) + << ", which cannot be used with the current execution " + "modes:\n" + << reason; + } + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_extensions.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_extensions.cpp new file mode 100644 index 00000000..912b9ae8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_extensions.cpp @@ -0,0 +1,3851 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of extension SPIR-V instructions. +#include +#include +#include +#include + +#include "NonSemanticShaderDebugInfo100.h" +#include "OpenCLDebugInfo100.h" +#include "source/common_debug_info.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/spirv_constant.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" +#include "spirv/unified1/NonSemanticClspvReflection.h" + +namespace spvtools { +namespace val { +namespace { + +std::string ReflectionInstructionName(ValidationState_t& _, + const Instruction* inst) { + spv_ext_inst_desc desc = nullptr; + if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + inst->word(4), &desc) != SPV_SUCCESS || + !desc) { + return std::string("Unknown ExtInst"); + } + std::ostringstream ss; + ss << desc->name; + + return ss.str(); +} + +uint32_t GetSizeTBitWidth(const ValidationState_t& _) { + if (_.addressing_model() == spv::AddressingModel::Physical32) return 32; + + if (_.addressing_model() == spv::AddressingModel::Physical64) return 64; + + return 0; +} + +bool IsIntScalar(ValidationState_t& _, uint32_t id, bool must_len32, + bool must_unsigned) { + auto type = _.FindDef(id); + if (!type || type->opcode() != spv::Op::OpTypeInt) { + return false; + } + + if (must_len32 && type->GetOperandAs(1) != 32) { + return false; + } + + return !must_unsigned || type->GetOperandAs(2) == 0; +} + +bool IsUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != spv::Op::OpConstant) { + return false; + } + + return IsIntScalar(_, inst->type_id(), true, true); +} + +uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + return inst->word(3); +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an instruction with |expected_opcode|. +spv_result_t ValidateOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + spv::Op expected_opcode, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + auto* operand = _.FindDef(inst->word(word_index)); + if (operand->opcode() != expected_opcode) { + spv_opcode_desc desc = nullptr; + if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " must be a result id of " + << "Op" << desc->name; + } + return SPV_SUCCESS; +} + +// For NonSemantic.Shader.DebugInfo.100 check that the operand of a debug info +// instruction |inst| at |word_index| is a result id of a 32-bit integer +// OpConstant instruction. For OpenCL.DebugInfo.100 the parameter is a literal +// word so cannot be validated. +spv_result_t ValidateUint32ConstantOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + if (!IsUint32Constant(_, inst->word(word_index))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand " << operand_name + << " must be a result id of 32-bit unsigned OpConstant"; + } + return SPV_SUCCESS; +} + +#define CHECK_OPERAND(NAME, opcode, index) \ + do { \ + auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +#define CHECK_CONST_UINT_OPERAND(NAME, index) \ + if (vulkanDebugInfo) { \ + auto result = ValidateUint32ConstantOperandForDebugInfo( \ + _, NAME, inst, index, ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } + +// True if the operand of a debug info instruction |inst| at |word_index| +// satisfies |expectation| that is given as a function. Otherwise, +// returns false. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function& expectation, + const Instruction* inst, uint32_t word_index) { + if (inst->words().size() <= word_index) return false; + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (!spvIsExtendedInstruction(debug_inst->opcode()) || + (debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 && + debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation(CommonDebugInfoInstructions(debug_inst->word(4)))) { + return false; + } + return true; +} + +// Overload for NonSemanticShaderDebugInfo100Instructions. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function& + expectation, + const Instruction* inst, uint32_t word_index) { + if (inst->words().size() <= word_index) return false; + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (!spvIsExtendedInstruction(debug_inst->opcode()) || + (debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation( + NonSemanticShaderDebugInfo100Instructions(debug_inst->word(4)))) { + return false; + } + return true; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction whose debug instruction type +// is |expected_debug_inst|. +spv_result_t ValidateDebugInfoOperand( + ValidationState_t& _, const std::string& debug_inst_name, + CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst, + uint32_t word_index, const std::function& ext_inst_name) { + std::function expectation = + [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == expected_debug_inst; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + spv_ext_inst_desc desc = nullptr; + if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst, + &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " must be a result id of " + << desc->name; +} + +#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \ + do { \ + auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction with DebugTypeBasic. +spv_result_t ValidateOperandBaseType( + ValidationState_t& _, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic, + inst, word_index, ext_inst_name); +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug lexical scope instruction which is one of +// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or +// DebugTypeComposite. +spv_result_t ValidateOperandLexicalScope( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + std::function expectation = + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugCompilationUnit || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugLexicalBlock || + dbg_inst == CommonDebugInfoDebugTypeComposite; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " must be a result id of a lexical scope"; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug type instruction (See DebugTypeXXX in +// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec. +spv_result_t ValidateOperandDebugType( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name, + bool allow_template_param) { + // Check for NonSemanticShaderDebugInfo100 specific types. + if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + std::function expectation = + [](NonSemanticShaderDebugInfo100Instructions dbg_inst) { + return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + } + + // Check for common types. + std::function expectation = + [&allow_template_param](CommonDebugInfoInstructions dbg_inst) { + if (allow_template_param && + (dbg_inst == CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == CommonDebugInfoDebugTypeTemplateTemplateParameter)) { + return true; + } + return CommonDebugInfoDebugTypeBasic <= dbg_inst && + dbg_inst <= CommonDebugInfoDebugTypeTemplate; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " is not a valid debug type"; +} + +spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, + const Instruction* inst, + uint32_t version) { + const auto inst_name = ReflectionInstructionName(_, inst); + const auto kernel_id = inst->GetOperandAs(4); + const auto kernel = _.FindDef(kernel_id); + if (kernel->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << inst_name << " does not reference a function"; + } + + bool found_kernel = false; + for (auto entry_point : _.entry_points()) { + if (entry_point == kernel_id) { + found_kernel = true; + break; + } + } + if (!found_kernel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << inst_name << " does not reference an entry-point"; + } + + const auto* exec_models = _.GetExecutionModels(kernel_id); + if (!exec_models || exec_models->empty()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << inst_name << " does not reference an entry-point"; + } + for (auto exec_model : *exec_models) { + if (exec_model != spv::ExecutionModel::GLCompute) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << inst_name << " must refer only to GLCompute entry-points"; + } + } + + auto name = _.FindDef(inst->GetOperandAs(5)); + if (!name || name->opcode() != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; + } + + const std::string name_str = name->GetOperandAs(1); + bool found = false; + for (auto& desc : _.entry_point_descriptions(kernel_id)) { + if (name_str == desc.name) { + found = true; + break; + } + } + if (!found) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Name must match an entry-point for Kernel"; + } + + const auto num_operands = inst->operands().size(); + if (version < 5 && num_operands > 6) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Version " << version << " of the " << inst_name + << " instruction can only have 2 additional operands"; + } + + if (num_operands > 6) { + const auto num_args_id = inst->GetOperandAs(6); + if (!IsUint32Constant(_, num_args_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NumArguments must be a 32-bit unsigned integer OpConstant"; + } + } + + if (num_operands > 7) { + const auto flags_id = inst->GetOperandAs(7); + if (!IsUint32Constant(_, flags_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Flags must be a 32-bit unsigned integer OpConstant"; + } + } + + if (num_operands > 8) { + const auto atts_id = inst->GetOperandAs(8); + if (_.GetIdOpcode(atts_id) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Attributes must be an OpString"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (_.GetIdOpcode(inst->GetOperandAs(4)) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; + } + if (num_operands > 5) { + if (_.GetIdOpcode(inst->GetOperandAs(5)) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "TypeName must be an OpString"; + } + } + if (num_operands > 6) { + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "AddressQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + if (num_operands > 7) { + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "AccessQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + if (num_operands > 8) { + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "TypeQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) { + const auto decl_id = inst->GetOperandAs(4); + const auto decl = _.FindDef(decl_id); + if (!decl || !spvIsExtendedInstruction(decl->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be a Kernel extended instruction"; + } + + if (decl->GetOperandAs(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be from the same extended instruction import"; + } + + const auto ext_inst = + decl->GetOperandAs(3); + if (ext_inst != NonSemanticClspvReflectionKernel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be a Kernel extended instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst, + uint32_t info_index) { + auto info = _.FindDef(inst->GetOperandAs(info_index)); + if (!info || !spvIsExtendedInstruction(info->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be an ArgumentInfo extended instruction"; + } + + if (info->GetOperandAs(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be from the same extended instruction import"; + } + + auto ext_inst = info->GetOperandAs(3); + if (ext_inst != NonSemanticClspvReflectionArgumentInfo) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be an ArgumentInfo extended instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentOffsetBuffer( + ValidationState_t& _, const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(9))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 11) { + if (auto error = ValidateArgInfo(_, inst, 10)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentPushConstant( + ValidationState_t& _, const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "SpecId must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ElemSize must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSpecConstantTriple( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Z must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSpecConstantWorkDim( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Dim must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionInitializedData(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (_.GetIdOpcode(inst->GetOperandAs(6)) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Mask must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize( + ValidationState_t& _, const Instruction* inst) { + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Z must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSubgroupMaxSize(ValidationState_t& _, + const Instruction* inst) { + const auto size_id = inst->GetOperandAs(4); + if (!IsUint32Constant(_, size_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPointerRelocation(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ObjectOffset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PointerOffset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PointerSize must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionImageMetadataPushConstant( + ValidationState_t& _, const Instruction* inst) { + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionImageMetadataUniform( + ValidationState_t& _, const Instruction* inst) { + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(9))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPushConstantData(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (_.GetIdOpcode(inst->GetOperandAs(6)) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPrintfInfo(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PrintfID must be a 32-bit unsigned integer OpConstant"; + } + + if (_.GetIdOpcode(inst->GetOperandAs(5)) != spv::Op::OpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "FormatString must be an OpString"; + } + + for (size_t i = 6; i < inst->operands().size(); ++i) { + if (!IsUint32Constant(_, inst->GetOperandAs(i))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgumentSizes must be a 32-bit unsigned integer OpConstant"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPrintfStorageBuffer( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPrintfPushConstant( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "BufferSize must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _, + const Instruction* inst, + uint32_t version) { + if (!_.IsVoidType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Return Type must be OpTypeVoid"; + } + + uint32_t required_version = 0; + const auto ext_inst = + inst->GetOperandAs(3); + switch (ext_inst) { + case NonSemanticClspvReflectionKernel: + case NonSemanticClspvReflectionArgumentInfo: + case NonSemanticClspvReflectionArgumentStorageBuffer: + case NonSemanticClspvReflectionArgumentUniform: + case NonSemanticClspvReflectionArgumentPodStorageBuffer: + case NonSemanticClspvReflectionArgumentPodUniform: + case NonSemanticClspvReflectionArgumentPodPushConstant: + case NonSemanticClspvReflectionArgumentSampledImage: + case NonSemanticClspvReflectionArgumentStorageImage: + case NonSemanticClspvReflectionArgumentSampler: + case NonSemanticClspvReflectionArgumentWorkgroup: + case NonSemanticClspvReflectionSpecConstantWorkgroupSize: + case NonSemanticClspvReflectionSpecConstantGlobalOffset: + case NonSemanticClspvReflectionSpecConstantWorkDim: + case NonSemanticClspvReflectionPushConstantGlobalOffset: + case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize: + case NonSemanticClspvReflectionPushConstantGlobalSize: + case NonSemanticClspvReflectionPushConstantRegionOffset: + case NonSemanticClspvReflectionPushConstantNumWorkgroups: + case NonSemanticClspvReflectionPushConstantRegionGroupOffset: + case NonSemanticClspvReflectionConstantDataStorageBuffer: + case NonSemanticClspvReflectionConstantDataUniform: + case NonSemanticClspvReflectionLiteralSampler: + case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize: + required_version = 1; + break; + case NonSemanticClspvReflectionSpecConstantSubgroupMaxSize: + required_version = 2; + break; + case NonSemanticClspvReflectionArgumentPointerPushConstant: + case NonSemanticClspvReflectionArgumentPointerUniform: + case NonSemanticClspvReflectionProgramScopeVariablesStorageBuffer: + case NonSemanticClspvReflectionProgramScopeVariablePointerRelocation: + case NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant: + case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant: + case NonSemanticClspvReflectionImageArgumentInfoChannelOrderUniform: + case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypeUniform: + required_version = 3; + break; + case NonSemanticClspvReflectionArgumentStorageTexelBuffer: + case NonSemanticClspvReflectionArgumentUniformTexelBuffer: + required_version = 4; + break; + case NonSemanticClspvReflectionConstantDataPointerPushConstant: + case NonSemanticClspvReflectionProgramScopeVariablePointerPushConstant: + case NonSemanticClspvReflectionPrintfInfo: + case NonSemanticClspvReflectionPrintfBufferStorageBuffer: + case NonSemanticClspvReflectionPrintfBufferPointerPushConstant: + required_version = 5; + break; + default: + break; + } + if (version < required_version) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << ReflectionInstructionName(_, inst) << " requires version " + << required_version << ", but parsed version is " << version; + } + + switch (ext_inst) { + case NonSemanticClspvReflectionKernel: + return ValidateClspvReflectionKernel(_, inst, version); + case NonSemanticClspvReflectionArgumentInfo: + return ValidateClspvReflectionArgumentInfo(_, inst); + case NonSemanticClspvReflectionArgumentStorageBuffer: + case NonSemanticClspvReflectionArgumentUniform: + case NonSemanticClspvReflectionArgumentSampledImage: + case NonSemanticClspvReflectionArgumentStorageImage: + case NonSemanticClspvReflectionArgumentSampler: + case NonSemanticClspvReflectionArgumentStorageTexelBuffer: + case NonSemanticClspvReflectionArgumentUniformTexelBuffer: + return ValidateClspvReflectionArgumentBuffer(_, inst); + case NonSemanticClspvReflectionArgumentPodStorageBuffer: + case NonSemanticClspvReflectionArgumentPodUniform: + case NonSemanticClspvReflectionArgumentPointerUniform: + return ValidateClspvReflectionArgumentOffsetBuffer(_, inst); + case NonSemanticClspvReflectionArgumentPodPushConstant: + case NonSemanticClspvReflectionArgumentPointerPushConstant: + return ValidateClspvReflectionArgumentPushConstant(_, inst); + case NonSemanticClspvReflectionArgumentWorkgroup: + return ValidateClspvReflectionArgumentWorkgroup(_, inst); + case NonSemanticClspvReflectionSpecConstantWorkgroupSize: + case NonSemanticClspvReflectionSpecConstantGlobalOffset: + return ValidateClspvReflectionSpecConstantTriple(_, inst); + case NonSemanticClspvReflectionSpecConstantWorkDim: + return ValidateClspvReflectionSpecConstantWorkDim(_, inst); + case NonSemanticClspvReflectionPushConstantGlobalOffset: + case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize: + case NonSemanticClspvReflectionPushConstantGlobalSize: + case NonSemanticClspvReflectionPushConstantRegionOffset: + case NonSemanticClspvReflectionPushConstantNumWorkgroups: + case NonSemanticClspvReflectionPushConstantRegionGroupOffset: + return ValidateClspvReflectionPushConstant(_, inst); + case NonSemanticClspvReflectionConstantDataStorageBuffer: + case NonSemanticClspvReflectionConstantDataUniform: + case NonSemanticClspvReflectionProgramScopeVariablesStorageBuffer: + return ValidateClspvReflectionInitializedData(_, inst); + case NonSemanticClspvReflectionLiteralSampler: + return ValidateClspvReflectionSampler(_, inst); + case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize: + return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst); + case NonSemanticClspvReflectionSpecConstantSubgroupMaxSize: + return ValidateClspvReflectionSubgroupMaxSize(_, inst); + case NonSemanticClspvReflectionProgramScopeVariablePointerRelocation: + return ValidateClspvReflectionPointerRelocation(_, inst); + case NonSemanticClspvReflectionImageArgumentInfoChannelOrderPushConstant: + case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypePushConstant: + return ValidateClspvReflectionImageMetadataPushConstant(_, inst); + case NonSemanticClspvReflectionImageArgumentInfoChannelOrderUniform: + case NonSemanticClspvReflectionImageArgumentInfoChannelDataTypeUniform: + return ValidateClspvReflectionImageMetadataUniform(_, inst); + case NonSemanticClspvReflectionConstantDataPointerPushConstant: + case NonSemanticClspvReflectionProgramScopeVariablePointerPushConstant: + return ValidateClspvReflectionPushConstantData(_, inst); + case NonSemanticClspvReflectionPrintfInfo: + return ValidateClspvReflectionPrintfInfo(_, inst); + case NonSemanticClspvReflectionPrintfBufferStorageBuffer: + return ValidateClspvReflectionPrintfStorageBuffer(_, inst); + case NonSemanticClspvReflectionPrintfBufferPointerPushConstant: + return ValidateClspvReflectionPrintfPushConstant(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _, + Instruction* instr) { + if (instr->opcode() != spv::Op::OpConstant) return false; + if (!_.IsIntScalarType(instr->type_id())) return false; + uint32_t size_in_bits = _.GetBitWidth(instr->type_id()); + return size_in_bits == 32 || size_in_bits == 64; +} + +bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst, + uint32_t word_index) { + auto* int_scalar_const = _.FindDef(inst->word(word_index)); + if (int_scalar_const->opcode() == spv::Op::OpConstant && + _.IsIntScalarType(int_scalar_const->type_id())) { + return true; + } + return false; +} + +bool IsDebugVariableWithIntScalarType(ValidationState_t& _, + const Instruction* inst, + uint32_t word_index) { + auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index)); + if (CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(dbg_int_scalar_var->word(4)) == + CommonDebugInfoDebugGlobalVariable) { + auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6)); + if (CommonDebugInfoInstructions(dbg_type->word(4)) == + CommonDebugInfoDebugTypeBasic) { + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + uint32_t encoding = dbg_type->word(7); + if (!vulkanDebugInfo || IsUint32Constant(_, encoding)) { + auto ocl_encoding = OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo ? GetUint32Constant(_, encoding) : encoding); + if (ocl_encoding == OpenCLDebugInfo100Signed || + ocl_encoding == OpenCLDebugInfo100Unsigned) { + return true; + } + } + } + } + return false; +} + +} // anonymous namespace + +spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { + std::string extension = GetExtensionString(&(inst->c_inst())); + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) { + if (extension == ExtensionToString(kSPV_KHR_vulkan_memory_model)) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << extension << " extension requires SPIR-V version 1.3 or later."; + } + } + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) { + if (extension == + ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) || + extension == ExtensionToString(kSPV_EXT_mesh_shader) || + extension == ExtensionToString(kSPV_NV_shader_invocation_reorder) || + extension == + ExtensionToString(kSPV_NV_cluster_acceleration_structure) || + extension == ExtensionToString(kSPV_NV_linear_swept_spheres)) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << extension << " extension requires SPIR-V version 1.4 or later."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInstImport(ValidationState_t& _, + const Instruction* inst) { + const auto name_id = 1; + if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) && + !_.HasExtension(kSPV_KHR_non_semantic_info)) { + const std::string name = inst->GetOperandAs(name_id); + if (name.find("NonSemantic.") == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic extended instruction sets cannot be declared " + "without SPV_KHR_non_semantic_info."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t num_operands = static_cast(inst->operands().size()); + + const uint32_t ext_inst_set = inst->word(3); + const uint32_t ext_inst_index = inst->word(4); + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + + auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() { + spv_ext_inst_desc desc = nullptr; + if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) != + SPV_SUCCESS || + !desc) { + return std::string("Unknown ExtInst"); + } + + auto* import_inst = _.FindDef(ext_inst_set); + assert(import_inst); + + std::ostringstream ss; + ss << import_inst->GetOperandAs(1); + ss << " "; + ss << desc->name; + + return ss.str(); + }; + + if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { + const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index); + switch (ext_inst_key) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450FAbs: + case GLSLstd450Trunc: + case GLSLstd450FSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450FMin: + case GLSLstd450FMax: + case GLSLstd450FClamp: + case GLSLstd450FMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450Normalize: + case GLSLstd450FaceForward: + case GLSLstd450Reflect: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450FMin || ext_inst_key == GLSLstd450FMax || + ext_inst_key == GLSLstd450FClamp || + ext_inst_key == GLSLstd450NMin || ext_inst_key == GLSLstd450NMax || + ext_inst_key == GLSLstd450NClamp || + ext_inst_key == GLSLstd450Step || ext_inst_key == GLSLstd450Fma); + + if (!_.IsFloatScalarOrVectorType(result_type) && + !(supportsCoopVec && + _.IsFloatCooperativeVectorNVType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case GLSLstd450SAbs: + case GLSLstd450SSign: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FindILsb: + case GLSLstd450FindUMsb: + case GLSLstd450FindSMsb: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450UMin || ext_inst_key == GLSLstd450UMax || + ext_inst_key == GLSLstd450UClamp || + ext_inst_key == GLSLstd450SMin || ext_inst_key == GLSLstd450SMax || + ext_inst_key == GLSLstd450SClamp); + + if (!_.IsIntScalarOrVectorType(result_type) && + !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector type"; + } + + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + const uint32_t result_type_dimension = _.GetDimension(result_type); + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (!operand_type || + (!_.IsIntScalarOrVectorType(operand_type) && + !(supportsCoopVec && + _.IsIntCooperativeVectorNVType(operand_type)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to be int scalars or vectors"; + } + + if (result_type_dimension != _.GetDimension(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to have the same dimension as " + << "Result Type"; + } + + if (result_type_bit_width != _.GetBitWidth(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to have the same bit width as " + << "Result Type"; + } + + if (ext_inst_key == GLSLstd450FindUMsb || + ext_inst_key == GLSLstd450FindSMsb) { + if (result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "this instruction is currently limited to 32-bit width " + << "components"; + } + } + } + break; + } + + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Exp: + case GLSLstd450Exp2: + case GLSLstd450Log: + case GLSLstd450Log2: + case GLSLstd450Atan2: + case GLSLstd450Pow: { + bool supportsCoopVec = + (ext_inst_key == GLSLstd450Atan || ext_inst_key == GLSLstd450Tanh || + ext_inst_key == GLSLstd450Exp || ext_inst_key == GLSLstd450Log); + + if (!_.IsFloatScalarOrVectorType(result_type) && + !(supportsCoopVec && + _.IsFloatCooperativeVectorNVType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; + } + + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + if (result_type_bit_width != 16 && result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case GLSLstd450Determinant: { + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be a square matrix"; + } + + if (result_type != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X component type to be equal to " + << "Result Type"; + } + break; + } + + case GLSLstd450MatrixInverse: { + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a square matrix"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + break; + } + + case GLSLstd450Modf: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t i_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + spv::StorageClass i_storage_class; + uint32_t i_data_type = 0; + if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I to be a pointer"; + } + + if (i_data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I data type to be equal to Result Type"; + } + + break; + } + + case GLSLstd450ModfStruct: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + result_types[1] != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a struct with two identical " + << "scalar or vector float type members"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to members of " + << "Result Type struct"; + } + break; + } + + case GLSLstd450Frexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + spv::StorageClass exp_storage_class; + uint32_t exp_data_type = 0; + if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, + &exp_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to be a pointer"; + } + + if (!_.IsIntScalarOrVectorType(exp_data_type) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 16 && + _.GetBitWidth(exp_data_type) != 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp data type to be a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector type"; + } + + if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp data type to have the same component " + << "number as Result Type"; + } + + break; + } + + case GLSLstd450Ldexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (!_.IsIntScalarOrVectorType(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to be a 32-bit int scalar " + << "or vector type"; + } + + if (_.GetDimension(result_type) != _.GetDimension(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to have the same component " + << "number as Result Type"; + } + + break; + } + + case GLSLstd450FrexpStruct: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + !_.IsIntScalarOrVectorType(result_types[1]) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 16 && + _.GetBitWidth(result_types[1]) != 32) || + _.GetDimension(result_types[0]) != + _.GetDimension(result_types[1])) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a struct with two members, " + << "first member a float scalar or vector, second member a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector with the same number of " + << "components as the first member"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to the first member " + << "of Result Type struct"; + } + break; + } + + case GLSLstd450PackSnorm4x8: + case GLSLstd450PackUnorm4x8: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit float vector of size 4"; + } + break; + } + + case GLSLstd450PackSnorm2x16: + case GLSLstd450PackUnorm2x16: + case GLSLstd450PackHalf2x16: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit float vector of size 2"; + } + break; + } + + case GLSLstd450PackDouble2x32: { + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 64-bit float scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit int vector of size 2"; + } + break; + } + + case GLSLstd450UnpackSnorm4x8: + case GLSLstd450UnpackUnorm4x8: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 4 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float vector of size " + "4"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackSnorm2x16: + case GLSLstd450UnpackUnorm2x16: + case GLSLstd450UnpackHalf2x16: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackDouble2x32: { + if (!_.IsIntVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 64-bit float scalar"; + } + break; + } + + case GLSLstd450Length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X component type to be equal to Result " + "Type"; + } + break; + } + + case GLSLstd450Distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarOrVectorType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P1 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P1 component type to be equal to " + << "Result Type"; + } + + if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands P0 and P1 to have the same number of " + << "components"; + } + break; + } + + case GLSLstd450Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + if (_.GetDimension(result_type) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 3 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case GLSLstd450Refract: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t i_type = _.GetOperandTypeId(inst, 4); + const uint32_t n_type = _.GetOperandTypeId(inst, 5); + const uint32_t eta_type = _.GetOperandTypeId(inst, 6); + + if (result_type != i_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I to be of type equal to Result Type"; + } + + if (result_type != n_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand N to be of type equal to Result Type"; + } + + if (!_.IsFloatScalarType(eta_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Eta to be a float scalar"; + } + break; + } + + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: { + if (!_.HasCapability(spv::Capability::InterpolationFunction)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << ext_inst_name() + << " requires capability InterpolationFunction"; + } + + if (!_.IsFloatScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float scalar " + << "or vector type"; + } + + // If HLSL legalization and first operand is an OpLoad, use load + // pointer as the interpolant lvalue. Else use interpolate first + // operand. + uint32_t interp_id = inst->GetOperandAs(4); + auto* interp_inst = _.FindDef(interp_id); + uint32_t interpolant_type = (_.options()->before_hlsl_legalization && + interp_inst->opcode() == spv::Op::OpLoad) + ? _.GetOperandTypeId(interp_inst, 2) + : _.GetOperandTypeId(inst, 4); + + spv::StorageClass interpolant_storage_class; + uint32_t interpolant_data_type = 0; + if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, + &interpolant_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant to be a pointer"; + } + + if (result_type != interpolant_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant data type to be equal to Result Type"; + } + + if (interpolant_storage_class != spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant storage class to be Input"; + } + + if (ext_inst_key == GLSLstd450InterpolateAtSample) { + const uint32_t sample_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(sample_type) || + _.GetBitWidth(sample_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Sample to be 32-bit integer"; + } + } + + if (ext_inst_key == GLSLstd450InterpolateAtOffset) { + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatVectorType(offset_type) || + _.GetDimension(offset_type) != 2 || + _.GetBitWidth(offset_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Offset to be a vector of 2 32-bit floats"; + } + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + ext_inst_name() + + std::string(" requires Fragment execution model")); + break; + } + + case GLSLstd450IMix: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Extended instruction GLSLstd450IMix is not supported"; + } + + case GLSLstd450Bad: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Encountered extended instruction GLSLstd450Bad"; + } + + case GLSLstd450Count: { + assert(0); + break; + } + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { + const OpenCLLIB::Entrypoints ext_inst_key = + OpenCLLIB::Entrypoints(ext_inst_index); + switch (ext_inst_key) { + case OpenCLLIB::Acos: + case OpenCLLIB::Acosh: + case OpenCLLIB::Acospi: + case OpenCLLIB::Asin: + case OpenCLLIB::Asinh: + case OpenCLLIB::Asinpi: + case OpenCLLIB::Atan: + case OpenCLLIB::Atan2: + case OpenCLLIB::Atanh: + case OpenCLLIB::Atanpi: + case OpenCLLIB::Atan2pi: + case OpenCLLIB::Cbrt: + case OpenCLLIB::Ceil: + case OpenCLLIB::Copysign: + case OpenCLLIB::Cos: + case OpenCLLIB::Cosh: + case OpenCLLIB::Cospi: + case OpenCLLIB::Erfc: + case OpenCLLIB::Erf: + case OpenCLLIB::Exp: + case OpenCLLIB::Exp2: + case OpenCLLIB::Exp10: + case OpenCLLIB::Expm1: + case OpenCLLIB::Fabs: + case OpenCLLIB::Fdim: + case OpenCLLIB::Floor: + case OpenCLLIB::Fma: + case OpenCLLIB::Fmax: + case OpenCLLIB::Fmin: + case OpenCLLIB::Fmod: + case OpenCLLIB::Hypot: + case OpenCLLIB::Lgamma: + case OpenCLLIB::Log: + case OpenCLLIB::Log2: + case OpenCLLIB::Log10: + case OpenCLLIB::Log1p: + case OpenCLLIB::Logb: + case OpenCLLIB::Mad: + case OpenCLLIB::Maxmag: + case OpenCLLIB::Minmag: + case OpenCLLIB::Nextafter: + case OpenCLLIB::Pow: + case OpenCLLIB::Powr: + case OpenCLLIB::Remainder: + case OpenCLLIB::Rint: + case OpenCLLIB::Round: + case OpenCLLIB::Rsqrt: + case OpenCLLIB::Sin: + case OpenCLLIB::Sinh: + case OpenCLLIB::Sinpi: + case OpenCLLIB::Sqrt: + case OpenCLLIB::Tan: + case OpenCLLIB::Tanh: + case OpenCLLIB::Tanpi: + case OpenCLLIB::Tgamma: + case OpenCLLIB::Trunc: + case OpenCLLIB::Half_cos: + case OpenCLLIB::Half_divide: + case OpenCLLIB::Half_exp: + case OpenCLLIB::Half_exp2: + case OpenCLLIB::Half_exp10: + case OpenCLLIB::Half_log: + case OpenCLLIB::Half_log2: + case OpenCLLIB::Half_log10: + case OpenCLLIB::Half_powr: + case OpenCLLIB::Half_recip: + case OpenCLLIB::Half_rsqrt: + case OpenCLLIB::Half_sin: + case OpenCLLIB::Half_sqrt: + case OpenCLLIB::Half_tan: + case OpenCLLIB::Native_cos: + case OpenCLLIB::Native_divide: + case OpenCLLIB::Native_exp: + case OpenCLLIB::Native_exp2: + case OpenCLLIB::Native_exp10: + case OpenCLLIB::Native_log: + case OpenCLLIB::Native_log2: + case OpenCLLIB::Native_log10: + case OpenCLLIB::Native_powr: + case OpenCLLIB::Native_recip: + case OpenCLLIB::Native_rsqrt: + case OpenCLLIB::Native_sin: + case OpenCLLIB::Native_sqrt: + case OpenCLLIB::Native_tan: + case OpenCLLIB::FClamp: + case OpenCLLIB::Degrees: + case OpenCLLIB::FMax_common: + case OpenCLLIB::FMin_common: + case OpenCLLIB::Mix: + case OpenCLLIB::Radians: + case OpenCLLIB::Step: + case OpenCLLIB::Smoothstep: + case OpenCLLIB::Sign: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Fract: + case OpenCLLIB::Modf: + case OpenCLLIB::Sincos: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Frexp: + case OpenCLLIB::Lgamma_r: + case OpenCLLIB::Remquo: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + if (ext_inst_key == OpenCLLIB::Remquo) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand Y to be equal to Result Type"; + } + } + + const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++); + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsIntScalarOrVectorType(p_data_type) || + _.GetBitWidth(p_data_type) != 32) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to be a 32-bit int " + "scalar or vector type"; + } + + if (!_.ContainsUntypedPointer(p_type) && + _.GetDimension(p_data_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to have the same number " + "of components as Result Type"; + } + break; + } + + case OpenCLLIB::Ilogb: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be a float scalar or vector"; + } + + if (_.GetDimension(x_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to have the same number of components " + "as Result Type"; + } + break; + } + + case OpenCLLIB::Ldexp: + case OpenCLLIB::Pown: + case OpenCLLIB::Rootn: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarOrVectorType(exp_type) || + _.GetBitWidth(exp_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the exponent to be a 32-bit int scalar or vector"; + } + + if (_.GetDimension(exp_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the exponent to have the same number of " + "components as Result Type"; + } + break; + } + + case OpenCLLIB::Nan: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t nancode_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarOrVectorType(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to be an int scalar or vector type"; + } + + if (_.GetDimension(nancode_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to have the same number of components as " + "Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::SAbs: + case OpenCLLIB::SAbs_diff: + case OpenCLLIB::SAdd_sat: + case OpenCLLIB::UAdd_sat: + case OpenCLLIB::SHadd: + case OpenCLLIB::UHadd: + case OpenCLLIB::SRhadd: + case OpenCLLIB::URhadd: + case OpenCLLIB::SClamp: + case OpenCLLIB::UClamp: + case OpenCLLIB::Clz: + case OpenCLLIB::Ctz: + case OpenCLLIB::SMad_hi: + case OpenCLLIB::UMad_sat: + case OpenCLLIB::SMad_sat: + case OpenCLLIB::SMax: + case OpenCLLIB::UMax: + case OpenCLLIB::SMin: + case OpenCLLIB::UMin: + case OpenCLLIB::SMul_hi: + case OpenCLLIB::Rotate: + case OpenCLLIB::SSub_sat: + case OpenCLLIB::USub_sat: + case OpenCLLIB::Popcount: + case OpenCLLIB::UAbs: + case OpenCLLIB::UAbs_diff: + case OpenCLLIB::UMul_hi: + case OpenCLLIB::UMad_hi: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::U_Upsample: + case OpenCLLIB::S_Upsample: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector " + "type"; + } + + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components > 4 && result_num_components != 8 && + result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t result_bit_width = _.GetBitWidth(result_type); + if (result_bit_width != 16 && result_bit_width != 32 && + result_bit_width != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected bit width of Result Type components to be 16, 32 " + "or 64"; + } + + const uint32_t hi_type = _.GetOperandTypeId(inst, 4); + const uint32_t lo_type = _.GetOperandTypeId(inst, 5); + + if (hi_type != lo_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Hi and Lo operands to have the same type"; + } + + if (result_num_components != _.GetDimension(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Hi and Lo operands to have the same number of " + "components as Result Type"; + } + + if (result_bit_width != 2 * _.GetBitWidth(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected bit width of components of Hi and Lo operands to " + "be half of the bit width of components of Result Type"; + } + break; + } + + case OpenCLLIB::SMad24: + case OpenCLLIB::UMad24: + case OpenCLLIB::SMul24: + case OpenCLLIB::UMul24: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components != 3 && num_components != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 3 or 4 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Distance: + case OpenCLLIB::Fast_distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(p0_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (p0_type != p1_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands P0 and P1 to be of the same type"; + } + break; + } + + case OpenCLLIB::Length: + case OpenCLLIB::Fast_length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a float scalar or vector"; + } + + const uint32_t num_components = _.GetDimension(p_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P component type to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Normalize: + case OpenCLLIB::Fast_normalize: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have no more than 4 components"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (p_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Bitselect: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Select: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t a_type = _.GetOperandTypeId(inst, 4); + const uint32_t b_type = _.GetOperandTypeId(inst, 5); + const uint32_t c_type = _.GetOperandTypeId(inst, 6); + + if (result_type != a_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand A type to be equal to Result Type"; + } + + if (result_type != b_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand B type to be equal to Result Type"; + } + + if (!_.IsIntScalarOrVectorType(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to be an int scalar or vector"; + } + + if (num_components != _.GetDimension(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to have the same number of components " + "as Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::Vloadn: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(result_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be equal to component " + "type of Result Type"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstoren: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + + if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be an int or float vector"; + } + + const uint32_t num_components = _.GetDimension(data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(data_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be equal to the type of " + "operand Data components"; + } + break; + } + + case OpenCLLIB::Vload_half: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + break; + } + + case OpenCLLIB::Vload_halfn: + case OpenCLLIB::Vloada_halfn: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::UniformConstant && + p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstore_half: + case OpenCLLIB::Vstore_half_r: + case OpenCLLIB::Vstore_halfn: + case OpenCLLIB::Vstore_halfn_r: + case OpenCLLIB::Vstorea_halfn: + case OpenCLLIB::Vstorea_halfn_r: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + const uint32_t data_type_bit_width = _.GetBitWidth(data_type); + + if (ext_inst_key == OpenCLLIB::Vstore_half || + ext_inst_key == OpenCLLIB::Vstore_half_r) { + if (!_.IsFloatScalarType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be a 32 or 64-bit float scalar"; + } + } else { + if (!_.IsFloatVectorType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be a 32 or 64-bit float vector"; + } + + const uint32_t num_components = _.GetDimension(data_type); + if (num_components > 4 && num_components != 8 && + num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to have 2, 3, 4, 8 or 16 components"; + } + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::Generic && + p_storage_class != spv::StorageClass::CrossWorkgroup && + p_storage_class != spv::StorageClass::Workgroup && + p_storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if ((!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) && + !_.ContainsUntypedPointer(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + + // Rounding mode enum is checked by assembler. + break; + } + + case OpenCLLIB::Shuffle: + case OpenCLLIB::Shuffle2: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float vector type"; + } + + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != 2 && result_num_components != 4 && + result_num_components != 8 && result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 4, 8 or 16 components"; + } + + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); + + if (ext_inst_key == OpenCLLIB::Shuffle2) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (x_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands X and Y to be of the same type"; + } + } + + const uint32_t shuffle_mask_type = + _.GetOperandTypeId(inst, operand_index++); + + if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be an int or float vector"; + } + + const uint32_t x_num_components = _.GetDimension(x_type); + if (x_num_components != 2 && x_num_components != 4 && + x_num_components != 8 && x_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to have 2, 4, 8 or 16 components"; + } + + const uint32_t result_component_type = _.GetComponentType(result_type); + + if (result_component_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X and Result Type to have equal " + "component types"; + } + + if (!_.IsIntVectorType(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask to be an int vector"; + } + + if (result_num_components != _.GetDimension(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask to have the same number of " + "components as Result Type"; + } + + if (_.GetBitWidth(result_component_type) != + _.GetBitWidth(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask components to have the same " + "bit width as Result Type components"; + } + break; + } + + case OpenCLLIB::Printf: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int type"; + } + + const uint32_t format_type = _.GetOperandTypeId(inst, 4); + spv::StorageClass format_storage_class; + uint32_t format_data_type = 0; + if (!_.GetPointerTypeInfo(format_type, &format_data_type, + &format_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Format to be a pointer"; + } + + if (_.HasExtension( + Extension::kSPV_EXT_relaxed_printf_string_address_space)) { + if (format_storage_class != spv::StorageClass::UniformConstant && + // Extension SPV_EXT_relaxed_printf_string_address_space allows + // format strings in Global, Local, Private and Generic address + // spaces + + // Global + format_storage_class != spv::StorageClass::CrossWorkgroup && + // Local + format_storage_class != spv::StorageClass::Workgroup && + // Private + format_storage_class != spv::StorageClass::Function && + // Generic + format_storage_class != spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Format storage class to be UniformConstant, " + "Crossworkgroup, Workgroup, Function, or Generic"; + } + } else { + if (format_storage_class != spv::StorageClass::UniformConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Format storage class to be UniformConstant"; + } + } + + // If pointer points to an array, get the type of an element + if (_.IsIntArrayType(format_data_type)) + format_data_type = _.GetComponentType(format_data_type); + + if ((!_.IsIntScalarType(format_data_type) || + _.GetBitWidth(format_data_type) != 8) && + !_.ContainsUntypedPointer(format_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Format data type to be 8-bit int"; + } + break; + } + + case OpenCLLIB::Prefetch: { + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5); + + spv::StorageClass p_storage_class; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Ptr to be a pointer"; + } + + if (p_storage_class != spv::StorageClass::CrossWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Ptr storage class to be CrossWorkgroup"; + } + + if (!_.IsFloatScalarOrVectorType(p_data_type) && + !_.IsIntScalarOrVectorType(p_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Ptr data type to be int or float scalar or " + "vector"; + } + + const uint32_t num_components = _.GetDimension(p_data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(num_elements_type) || + _.GetBitWidth(num_elements_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Num Elements to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + break; + } + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + ext_inst_type == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + if (!_.IsVoidType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected result type must be a result id of " + << "OpTypeVoid"; + } + + const bool vulkanDebugInfo = + ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100; + + auto num_words = inst->words().size(); + + // Handle any non-common NonSemanticShaderDebugInfo instructions. + if (vulkanDebugInfo) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + // The following block of instructions will be handled by the common + // validation. + case NonSemanticShaderDebugInfo100DebugInfoNone: + case NonSemanticShaderDebugInfo100DebugCompilationUnit: + case NonSemanticShaderDebugInfo100DebugTypePointer: + case NonSemanticShaderDebugInfo100DebugTypeQualifier: + case NonSemanticShaderDebugInfo100DebugTypeArray: + case NonSemanticShaderDebugInfo100DebugTypeVector: + case NonSemanticShaderDebugInfo100DebugTypedef: + case NonSemanticShaderDebugInfo100DebugTypeFunction: + case NonSemanticShaderDebugInfo100DebugTypeEnum: + case NonSemanticShaderDebugInfo100DebugTypeComposite: + case NonSemanticShaderDebugInfo100DebugTypeMember: + case NonSemanticShaderDebugInfo100DebugTypeInheritance: + case NonSemanticShaderDebugInfo100DebugTypePtrToMember: + case NonSemanticShaderDebugInfo100DebugTypeTemplate: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack: + case NonSemanticShaderDebugInfo100DebugGlobalVariable: + case NonSemanticShaderDebugInfo100DebugFunctionDeclaration: + case NonSemanticShaderDebugInfo100DebugFunction: + case NonSemanticShaderDebugInfo100DebugLexicalBlock: + case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugInlinedAt: + case NonSemanticShaderDebugInfo100DebugLocalVariable: + case NonSemanticShaderDebugInfo100DebugInlinedVariable: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugOperation: + case NonSemanticShaderDebugInfo100DebugExpression: + case NonSemanticShaderDebugInfo100DebugMacroDef: + case NonSemanticShaderDebugInfo100DebugMacroUndef: + case NonSemanticShaderDebugInfo100DebugImportedEntity: + case NonSemanticShaderDebugInfo100DebugSource: + break; + + // These checks are for operands that are differnet in + // ShaderDebugInfo100 + case NonSemanticShaderDebugInfo100DebugTypeBasic: { + CHECK_CONST_UINT_OPERAND("Flags", 8); + break; + } + case NonSemanticShaderDebugInfo100DebugDeclare: { + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + auto index_inst = _.FindDef(inst->word(word_index)); + auto type_id = index_inst != nullptr ? index_inst->type_id() : 0; + if (type_id == 0 || !IsIntScalar(_, type_id, false, false)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected index must be scalar integer"; + } + break; + } + case NonSemanticShaderDebugInfo100DebugTypeMatrix: { + CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5); + + CHECK_CONST_UINT_OPERAND("Vector Count", 6); + + uint32_t vector_count = inst->word(6); + uint64_t const_val; + if (!_.EvalConstantValUint64(vector_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Vector Count must be 32-bit integer OpConstant"; + } + + vector_count = const_val & 0xffffffff; + if (!vector_count || vector_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Vector Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: { + CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5); + CHECK_OPERAND("Definition", spv::Op::OpFunction, 6); + const auto* current_function = inst->function(); + if (current_function->first_block()->id() != inst->block()->id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": must be in the entry basic block of the function"; + } + + const uint32_t definition_id = inst->word(6); + if (definition_id != current_function->id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": operand Definition must point to the OpFunction it is " + "inside"; + } + break; + } + case NonSemanticShaderDebugInfo100DebugLine: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line Start", 6); + CHECK_CONST_UINT_OPERAND("Line End", 7); + CHECK_CONST_UINT_OPERAND("Column Start", 8); + CHECK_CONST_UINT_OPERAND("Column End", 9); + + // above already validates if 32-bit and non-spec constant + // but want to use EvalInt32IfConst to be consistent with other Eval + // locations + bool is_int32 = false, is_const_int32 = false; + uint32_t line_start = 0; + uint32_t line_end = 0; + uint32_t column_start = 0; + uint32_t column_end = 0; + std::tie(is_int32, is_const_int32, line_start) = + _.EvalInt32IfConst(inst->word(6)); + std::tie(is_int32, is_const_int32, line_end) = + _.EvalInt32IfConst(inst->word(7)); + std::tie(is_int32, is_const_int32, column_start) = + _.EvalInt32IfConst(inst->word(8)); + std::tie(is_int32, is_const_int32, column_end) = + _.EvalInt32IfConst(inst->word(9)); + if (line_end < line_start) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": operand Line End (" << line_end + << ") is less than Line Start (" << line_start << ")"; + } else if (line_start == line_end && column_end < column_start) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": operand Column End (" << column_end + << ") is less than Column Start (" << column_start + << ") when Line Start equals Line End"; + } + break; + } + case NonSemanticShaderDebugInfo100DebugSourceContinued: { + CHECK_OPERAND("Text", spv::Op::OpString, 5); + break; + } + case NonSemanticShaderDebugInfo100DebugBuildIdentifier: { + CHECK_OPERAND("Identifier", spv::Op::OpString, 5); + CHECK_CONST_UINT_OPERAND("Flags", 6); + break; + } + case NonSemanticShaderDebugInfo100DebugStoragePath: { + CHECK_OPERAND("Path", spv::Op::OpString, 5); + break; + } + case NonSemanticShaderDebugInfo100DebugEntryPoint: { + CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5); + CHECK_DEBUG_OPERAND("Compilation Unit", + CommonDebugInfoDebugCompilationUnit, 6); + CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7); + CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8); + break; + } + + // Has no additional checks + case NonSemanticShaderDebugInfo100DebugNoLine: + break; + case NonSemanticShaderDebugInfo100InstructionsMax: + assert(0); + break; + } + } + + // Handle any non-common OpenCL insts, then common + if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + OpenCLDebugInfo100Instructions(ext_inst_index) != + OpenCLDebugInfo100DebugModuleINTEL) { + const CommonDebugInfoInstructions ext_inst_key = + CommonDebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case CommonDebugInfoDebugInfoNone: + case CommonDebugInfoDebugNoScope: + break; + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation. We just check the parameters to + // DebugOperation are properly constants for vulkan debug info. + case CommonDebugInfoDebugOperation: { + CHECK_CONST_UINT_OPERAND("Operation", 5); + for (uint32_t i = 6; i < num_words; ++i) { + CHECK_CONST_UINT_OPERAND("Operand", i); + } + break; + } + case CommonDebugInfoDebugCompilationUnit: { + CHECK_CONST_UINT_OPERAND("Version", 5); + CHECK_CONST_UINT_OPERAND("DWARF Version", 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Language", 8); + break; + } + case CommonDebugInfoDebugSource: { + CHECK_OPERAND("File", spv::Op::OpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6); + break; + } + case CommonDebugInfoDebugTypeBasic: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_OPERAND("Size", spv::Op::OpConstant, 6); + CHECK_CONST_UINT_OPERAND("Encoding", 7); + break; + } + case CommonDebugInfoDebugTypePointer: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Storage Class", 6); + CHECK_CONST_UINT_OPERAND("Flags", 7); + break; + } + case CommonDebugInfoDebugTypeQualifier: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_CONST_UINT_OPERAND("Type Qualifier", 6); + break; + } + case CommonDebugInfoDebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + CHECK_CONST_UINT_OPERAND("Component Count", 6); + uint32_t component_count = inst->word(6); + if (vulkanDebugInfo) { + uint64_t const_val; + if (!_.EvalConstantValUint64(component_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Component Count must be 32-bit integer OpConstant"; + } + component_count = const_val & 0xffffffff; + } + + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case CommonDebugInfoDebugTypeArray: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the runtime array for + // OpenCL. + if (!vulkanDebugInfo && !component_count->word(3)) { + invalid = true; + } + } else if (component_count->words().size() > 6 && + (CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugLocalVariable || + CommonDebugInfoInstructions(component_count->word(4)) == + CommonDebugInfoDebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + uint32_t encoding = component_count_type->word(7); + if (CommonDebugInfoInstructions(component_count_type->word( + 4)) != CommonDebugInfoDebugTypeBasic || + (vulkanDebugInfo && !IsUint32Constant(_, encoding)) || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + vulkanDebugInfo + ? GetUint32Constant(_, encoding) + : encoding) != OpenCLDebugInfo100Unsigned) { + invalid = true; + } else { + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in + // bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } + } + } else { + invalid = true; + } + } else { + invalid = true; + } + if (invalid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be " + << "OpConstant with a 32- or 64-bits integer scalar type " + "or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- " + "or " + << "64-bits unsigned integer scalar type"; + } + } + break; + } + case CommonDebugInfoDebugTypedef: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_base_type = + ValidateOperandBaseType(_, inst, 6, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case CommonDebugInfoDebugTypeFunction: { + CHECK_CONST_UINT_OPERAND("Flags", 5); + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != spv::Op::OpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name, true); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, ext_inst_name, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case CommonDebugInfoDebugTypeEnum: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", spv::Op::OpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; + } + CHECK_CONST_UINT_OPERAND("Flags", 12); + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", spv::Op::OpConstant, word_index); + CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1); + } + break; + } + case CommonDebugInfoDebugTypeComposite: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + CHECK_OPERAND("Size", spv::Op::OpConstant, 12); + } + CHECK_CONST_UINT_OPERAND("Flags", 13); + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeMember || + dbg_inst == CommonDebugInfoDebugFunction || + dbg_inst == CommonDebugInfoDebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case CommonDebugInfoDebugTypeMember: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + // NonSemantic.Shader.DebugInfo doesn't have the Parent operand + if (vulkanDebugInfo) { + CHECK_OPERAND("Offset", spv::Op::OpConstant, 10); + CHECK_OPERAND("Size", spv::Op::OpConstant, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + if (num_words == 14) + CHECK_OPERAND("Value", spv::Op::OpConstant, 13); + } else { + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, + 10); + CHECK_OPERAND("Offset", spv::Op::OpConstant, 11); + CHECK_OPERAND("Size", spv::Op::OpConstant, 12); + CHECK_CONST_UINT_OPERAND("Flags", 13); + if (num_words == 15) + CHECK_OPERAND("Value", spv::Op::OpConstant, 14); + } + break; + } + case CommonDebugInfoDebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Child must be class or struct debug " + "type"; + } + CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parent must be class or struct debug " + "type"; + } + CHECK_OPERAND("Offset", spv::Op::OpConstant, 7); + CHECK_OPERAND("Size", spv::Op::OpConstant, 8); + CHECK_CONST_UINT_OPERAND("Flags", 9); + break; + } + case CommonDebugInfoDebugFunction: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + CHECK_CONST_UINT_OPERAND("Scope Line", 13); + // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the + // OpFunction + if (vulkanDebugInfo) { + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 14); + } + } else { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", spv::Op::OpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + CommonDebugInfoDebugFunctionDeclaration, 15); + } + } + break; + } + case CommonDebugInfoDebugFunctionDeclaration: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6); + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + CHECK_CONST_UINT_OPERAND("Flags", 12); + break; + } + case CommonDebugInfoDebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5); + CHECK_CONST_UINT_OPERAND("Line", 6); + CHECK_CONST_UINT_OPERAND("Column", 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9); + break; + } + case CommonDebugInfoDebugScope: { + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 7) { + CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6); + } + break; + } + case CommonDebugInfoDebugLocalVariable: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + // TODO: We need a spec discussion that we have to allow local + // variable types to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_CONST_UINT_OPERAND("Flags", 11); + if (num_words == 13) { + CHECK_CONST_UINT_OPERAND("ArgNumber", 12); + } + break; + } + case CommonDebugInfoDebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + auto* operand = _.FindDef(inst->word(6)); + if (operand->opcode() != spv::Op::OpVariable && + operand->opcode() != spv::Op::OpFunctionParameter) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpFunctionParameter"; + } + + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + break; + } + case CommonDebugInfoDebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation, + word_index); + } + break; + } + case CommonDebugInfoDebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugTypeComposite || + dbg_inst == CommonDebugInfoDebugFunction; + }, + inst, 5)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; + } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == + CommonDebugInfoDebugTypeTemplateParameter || + dbg_inst == + CommonDebugInfoDebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } + } + break; + } + case CommonDebugInfoDebugTypeTemplateParameter: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_actual_type = ValidateOperandDebugType( + _, "Actual Type", inst, 6, ext_inst_name, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", spv::Op::OpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8); + CHECK_CONST_UINT_OPERAND("Line", 9); + CHECK_CONST_UINT_OPERAND("Column", 10); + break; + } + case CommonDebugInfoDebugGlobalVariable: { + CHECK_OPERAND("Name", spv::Op::OpString, 5); + auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, + ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7); + CHECK_CONST_UINT_OPERAND("Line", 8); + CHECK_CONST_UINT_OPERAND("Column", 9); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](CommonDebugInfoInstructions dbg_inst) { + return dbg_inst == CommonDebugInfoDebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + if (operand->opcode() != spv::Op::OpVariable && + operand->opcode() != spv::Op::OpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpConstant or DebugInfoNone"; + } + } + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + CommonDebugInfoDebugTypeMember, 14); + } + break; + } + case CommonDebugInfoDebugInlinedAt: { + CHECK_CONST_UINT_OPERAND("Line", 5); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 8) { + CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7); + } + break; + } + case CommonDebugInfoDebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", + CommonDebugInfoDebugLocalVariable, 5); + CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7); + + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int + // scalar or a DebugLocalVariable or DebugGlobalVariable, but we + // have to check it using the same validation for Indexes of + // OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case CommonDebugInfoDebugTypePtrToMember: + case CommonDebugInfoDebugTypeTemplateTemplateParameter: + case CommonDebugInfoDebugTypeTemplateParameterPack: + case CommonDebugInfoDebugLexicalBlockDiscriminator: + case CommonDebugInfoDebugInlinedVariable: + case CommonDebugInfoDebugMacroDef: + case CommonDebugInfoDebugMacroUndef: + case CommonDebugInfoDebugImportedEntity: + break; + case CommonDebugInfoInstructionsMax: + assert(0); + break; + } + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + auto import_inst = _.FindDef(inst->GetOperandAs(2)); + const std::string name = import_inst->GetOperandAs(1); + const std::string reflection = "NonSemantic.ClspvReflection."; + char* end_ptr; + auto version_string = name.substr(reflection.size()); + if (version_string.empty()) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Missing NonSemantic.ClspvReflection import version"; + } + uint32_t version = static_cast( + std::strtoul(version_string.c_str(), &end_ptr, 10)); + if (end_ptr && *end_ptr != '\0') { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "NonSemantic.ClspvReflection import does not encode the " + "version correctly"; + } + if (version == 0 || version > NonSemanticClspvReflectionRevision) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Unknown NonSemantic.ClspvReflection import version"; + } + + return ValidateClspvReflectionInstruction(_, inst, version); + } + + return SPV_SUCCESS; +} + +spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + if (opcode == spv::Op::OpExtension) return ValidateExtension(_, inst); + if (opcode == spv::Op::OpExtInstImport) return ValidateExtInstImport(_, inst); + if (spvIsExtendedInstruction(opcode)) return ValidateExtInst(_, inst); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_function.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_function.cpp new file mode 100644 index 00000000..50ae0a2d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_function.cpp @@ -0,0 +1,373 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/enum_string_mapping.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if |a| and |b| are instructions defining pointers that point to +// types logically match and the decorations that apply to |b| are a subset +// of the decorations that apply to |a|. +bool DoPointeesLogicallyMatch(val::Instruction* a, val::Instruction* b, + ValidationState_t& _) { + if (a->opcode() != spv::Op::OpTypePointer || + b->opcode() != spv::Op::OpTypePointer) { + return false; + } + + const auto& dec_a = _.id_decorations(a->id()); + const auto& dec_b = _.id_decorations(b->id()); + for (const auto& dec : dec_b) { + if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) { + return false; + } + } + + uint32_t a_type = a->GetOperandAs(2); + uint32_t b_type = b->GetOperandAs(2); + + if (a_type == b_type) { + return true; + } + + Instruction* a_type_inst = _.FindDef(a_type); + Instruction* b_type_inst = _.FindDef(b_type); + + return _.LogicallyMatch(a_type_inst, b_type_inst, true); +} + +spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) { + const auto function_type_id = inst->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type || spv::Op::OpTypeFunction != function_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunction Function Type " << _.getIdName(function_type_id) + << " is not a function type."; + } + + const auto return_id = function_type->GetOperandAs(1); + if (return_id != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunction Result Type " << _.getIdName(inst->type_id()) + << " does not match the Function Type's return type " + << _.getIdName(return_id) << "."; + } + + const std::vector acceptable = { + spv::Op::OpGroupDecorate, + spv::Op::OpDecorate, + spv::Op::OpEnqueueKernel, + spv::Op::OpEntryPoint, + spv::Op::OpExecutionMode, + spv::Op::OpExecutionModeId, + spv::Op::OpFunctionCall, + spv::Op::OpGetKernelNDrangeSubGroupCount, + spv::Op::OpGetKernelNDrangeMaxSubGroupSize, + spv::Op::OpGetKernelWorkGroupSize, + spv::Op::OpGetKernelPreferredWorkGroupSizeMultiple, + spv::Op::OpGetKernelLocalSizeForSubgroupCount, + spv::Op::OpGetKernelMaxNumSubgroups, + spv::Op::OpName, + spv::Op::OpCooperativeMatrixPerElementOpNV, + spv::Op::OpCooperativeMatrixReduceNV, + spv::Op::OpCooperativeMatrixLoadTensorNV}; + for (auto& pair : inst->uses()) { + const auto* use = pair.first; + if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) == + acceptable.end() && + !use->IsNonSemantic() && !use->IsDebugInfo()) { + return _.diag(SPV_ERROR_INVALID_ID, use) + << "Invalid use of function result id " << _.getIdName(inst->id()) + << "."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFunctionParameter(ValidationState_t& _, + const Instruction* inst) { + // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place. + size_t param_index = 0; + size_t inst_num = inst->LineNum() - 1; + if (inst_num == 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter cannot be the first instruction."; + } + + auto func_inst = &_.ordered_instructions()[inst_num]; + while (--inst_num) { + func_inst = &_.ordered_instructions()[inst_num]; + if (func_inst->opcode() == spv::Op::OpFunction) { + break; + } else if (func_inst->opcode() == spv::Op::OpFunctionParameter) { + ++param_index; + } + } + + if (func_inst->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter must be preceded by a function."; + } + + const auto function_type_id = func_inst->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type) { + return _.diag(SPV_ERROR_INVALID_ID, func_inst) + << "Missing function type definition."; + } + if (param_index >= function_type->words().size() - 3) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Too many OpFunctionParameters for " << func_inst->id() + << ": expected " << function_type->words().size() - 3 + << " based on the function's type"; + } + + const auto param_type = + _.FindDef(function_type->GetOperandAs(param_index + 2)); + if (!param_type || inst->type_id() != param_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter Result Type " + << _.getIdName(inst->type_id()) + << " does not match the OpTypeFunction parameter " + "type of the same index."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFunctionCall(ValidationState_t& _, + const Instruction* inst) { + const auto function_id = inst->GetOperandAs(2); + const auto function = _.FindDef(function_id); + if (!function || spv::Op::OpFunction != function->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Function " << _.getIdName(function_id) + << " is not a function."; + } + + auto return_type = _.FindDef(function->type_id()); + if (!return_type || return_type->id() != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Result Type " << _.getIdName(inst->type_id()) + << "s type does not match Function " + << _.getIdName(return_type->id()) << "s return type."; + } + + const auto function_type_id = function->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type || function_type->opcode() != spv::Op::OpTypeFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing function type definition."; + } + + const auto function_call_arg_count = inst->words().size() - 4; + const auto function_param_count = function_type->words().size() - 3; + if (function_param_count != function_call_arg_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Function 's parameter count does not match " + "the argument count."; + } + + for (size_t argument_index = 3, param_index = 2; + argument_index < inst->operands().size(); + argument_index++, param_index++) { + const auto argument_id = inst->GetOperandAs(argument_index); + const auto argument = _.FindDef(argument_id); + if (!argument) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing argument " << argument_index - 3 << " definition."; + } + + const auto argument_type = _.FindDef(argument->type_id()); + if (!argument_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing argument " << argument_index - 3 + << " type definition."; + } + + const auto parameter_type_id = + function_type->GetOperandAs(param_index); + const auto parameter_type = _.FindDef(parameter_type_id); + if (!parameter_type || argument_type->id() != parameter_type->id()) { + if (!parameter_type || !_.options()->before_hlsl_legalization || + !DoPointeesLogicallyMatch(argument_type, parameter_type, _)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Argument " << _.getIdName(argument_id) + << "s type does not match Function " + << _.getIdName(parameter_type_id) << "s parameter type."; + } + } + + if (_.addressing_model() == spv::AddressingModel::Logical) { + if ((parameter_type->opcode() == spv::Op::OpTypePointer || + parameter_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) && + !_.options()->relax_logical_pointer) { + spv::StorageClass sc = + parameter_type->GetOperandAs(1u); + // Validate which storage classes can be pointer operands. + switch (sc) { + case spv::StorageClass::UniformConstant: + case spv::StorageClass::Function: + case spv::StorageClass::Private: + case spv::StorageClass::Workgroup: + case spv::StorageClass::AtomicCounter: + // These are always allowed. + break; + case spv::StorageClass::StorageBuffer: + if (!_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "StorageBuffer pointer operand " + << _.getIdName(argument_id) + << " requires a variable pointers capability"; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid storage class for pointer operand " + << _.getIdName(argument_id); + } + + // Validate memory object declaration requirements. + if (argument->opcode() != spv::Op::OpVariable && + argument->opcode() != spv::Op::OpUntypedVariableKHR && + argument->opcode() != spv::Op::OpFunctionParameter) { + const bool ssbo_vptr = + _.HasCapability(spv::Capability::VariablePointersStorageBuffer) && + sc == spv::StorageClass::StorageBuffer; + const bool wg_vptr = + _.HasCapability(spv::Capability::VariablePointers) && + sc == spv::StorageClass::Workgroup; + const bool uc_ptr = sc == spv::StorageClass::UniformConstant; + if (!_.options()->before_hlsl_legalization && !ssbo_vptr && + !wg_vptr && !uc_ptr) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer operand " << _.getIdName(argument_id) + << " must be a memory object declaration"; + } + } + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixPerElementOp(ValidationState_t& _, + const Instruction* inst) { + const auto function_id = inst->GetOperandAs(3); + const auto function = _.FindDef(function_id); + if (!function || spv::Op::OpFunction != function->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV Function " + << _.getIdName(function_id) << " is not a function."; + } + + const auto matrix_id = inst->GetOperandAs(2); + const auto matrix = _.FindDef(matrix_id); + const auto matrix_type_id = matrix->type_id(); + if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV Matrix " + << _.getIdName(matrix_id) << " is not a cooperative matrix."; + } + + const auto result_type_id = inst->GetOperandAs(0); + if (matrix_type_id != result_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV Result Type " + << _.getIdName(result_type_id) << " must match matrix type " + << _.getIdName(matrix_type_id) << "."; + } + + const auto matrix_comp_type_id = + _.FindDef(matrix_type_id)->GetOperandAs(1); + const auto function_type_id = function->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + auto return_type_id = function_type->GetOperandAs(1); + if (return_type_id != matrix_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV function return type " + << _.getIdName(return_type_id) + << " must match matrix component type " + << _.getIdName(matrix_comp_type_id) << "."; + } + + if (function_type->operands().size() < 5) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV function type " + << _.getIdName(function_type_id) + << " must have a least three parameters."; + } + + const auto param0_id = function_type->GetOperandAs(2); + const auto param1_id = function_type->GetOperandAs(3); + const auto param2_id = function_type->GetOperandAs(4); + if (!_.IsIntScalarType(param0_id) || _.GetBitWidth(param0_id) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV function type first parameter " + "type " + << _.getIdName(param0_id) << " must be a 32-bit integer."; + } + + if (!_.IsIntScalarType(param1_id) || _.GetBitWidth(param1_id) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV function type second " + "parameter type " + << _.getIdName(param1_id) << " must be a 32-bit integer."; + } + + if (param2_id != matrix_comp_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixPerElementOpNV function type third parameter " + "type " + << _.getIdName(param2_id) << " must match matrix component type."; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpFunction: + if (auto error = ValidateFunction(_, inst)) return error; + break; + case spv::Op::OpFunctionParameter: + if (auto error = ValidateFunctionParameter(_, inst)) return error; + break; + case spv::Op::OpFunctionCall: + if (auto error = ValidateFunctionCall(_, inst)) return error; + break; + case spv::Op::OpCooperativeMatrixPerElementOpNV: + if (auto error = ValidateCooperativeMatrixPerElementOp(_, inst)) + return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_id.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_id.cpp new file mode 100644 index 00000000..00be150c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_id.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/val/function.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) { + for (auto& operand : inst->operands()) { + const spv_operand_type_t& type = operand.type; + const uint32_t operand_id = inst->word(operand.offset); + if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) { + if (auto def = _.FindDef(operand_id)) + def->RegisterUse(inst, operand.offset); + } + } + + return SPV_SUCCESS; +} + +/// This function checks all ID definitions dominate their use in the CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// NOTE: This function does NOT check module scoped functions which are +/// checked during the initial binary parse in the IdPass below +spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) { + std::vector phi_instructions; + std::unordered_set phi_ids; + for (const auto& inst : _.ordered_instructions()) { + if (inst.id() == 0) continue; + if (const Function* func = inst.function()) { + if (const BasicBlock* block = inst.block()) { + // If the Id is defined within a block then make sure all references to + // that Id appear in a blocks that are dominated by the defining block + for (auto& use_index_pair : inst.uses()) { + const Instruction* use = use_index_pair.first; + if (const BasicBlock* use_block = use->block()) { + if (use_block->reachable() == false) continue; + if (use->opcode() == spv::Op::OpPhi) { + if (phi_ids.insert(use->id()).second) { + phi_instructions.push_back(use); + } + } else if (!block->dominates(*use->block())) { + return _.diag(SPV_ERROR_INVALID_ID, use_block->label()) + << "ID " << _.getIdName(inst.id()) << " defined in block " + << _.getIdName(block->id()) + << " does not dominate its use in block " + << _.getIdName(use_block->id()); + } + } + } + } else { + // If the Ids defined within a function but not in a block(i.e. function + // parameters, block ids), then make sure all references to that Id + // appear within the same function + for (auto use : inst.uses()) { + const Instruction* user = use.first; + if (user->function() && user->function() != func) { + return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id())) + << "ID " << _.getIdName(inst.id()) << " used in function " + << _.getIdName(user->function()->id()) + << " is used outside of it's defining function " + << _.getIdName(func->id()); + } + } + } + } + // NOTE: Ids defined outside of functions must appear before they are used + // This check is being performed in the IdPass function + } + + // Check all OpPhi parent blocks are dominated by the variable's defining + // blocks + for (const Instruction* phi : phi_instructions) { + if (phi->block()->reachable() == false) continue; + for (size_t i = 3; i < phi->operands().size(); i += 2) { + const Instruction* variable = _.FindDef(phi->word(i)); + const BasicBlock* parent = + phi->function()->GetBlock(phi->word(i + 1)).first; + if (variable->block() && parent->reachable() && + !variable->block()->dominates(*parent)) { + return _.diag(SPV_ERROR_INVALID_ID, phi) + << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID " + << _.getIdName(variable->id()) + << " definition does not dominate its parent " + << _.getIdName(parent->id()); + } + } + } + + return SPV_SUCCESS; +} + +// Performs SSA validation on the IDs of an instruction. The +// can_have_forward_declared_ids functor should return true if the +// instruction operand's ID can be forward referenced. +spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { + auto can_have_forward_declared_ids = + spvIsExtendedInstruction(inst->opcode()) && + spvExtInstIsDebugInfo(inst->ext_inst_type()) + ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + inst->opcode(), inst->ext_inst_type(), inst->word(4)) + : spvOperandCanBeForwardDeclaredFunction(inst->opcode()); + + // Keep track of a result id defined by this instruction. 0 means it + // does not define an id. + uint32_t result_id = 0; + bool has_forward_declared_ids = false; + + for (unsigned i = 0; i < inst->operands().size(); i++) { + const spv_parsed_operand_t& operand = inst->operand(i); + const spv_operand_type_t& type = operand.type; + // We only care about Id operands, which are a single word. + const uint32_t operand_word = inst->word(operand.offset); + + auto ret = SPV_ERROR_INTERNAL; + switch (type) { + case SPV_OPERAND_TYPE_RESULT_ID: + // NOTE: Multiple Id definitions are being checked by the binary parser. + // + // Defer undefined-forward-reference removal until after we've analyzed + // the remaining operands to this instruction. Deferral only matters + // for OpPhi since it's the only case where it defines its own forward + // reference. Other instructions that can have forward references + // either don't define a value or the forward reference is to a function + // Id (and hence defined outside of a function body). + result_id = operand_word; + // NOTE: The result Id is added (in RegisterInstruction) *after* all of + // the other Ids have been checked to avoid premature use in the same + // instruction. + ret = SPV_SUCCESS; + break; + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + if (const auto def = _.FindDef(operand_word)) { + const auto opcode = inst->opcode(); + if (spvOpcodeGeneratesType(def->opcode()) && + !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && + !inst->IsDebugInfo() && !inst->IsNonSemantic() && + !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction && + opcode != spv::Op::OpSizeOf && + opcode != spv::Op::OpCooperativeMatrixLengthNV && + opcode != spv::Op::OpCooperativeMatrixLengthKHR && + !spvOpcodeGeneratesUntypedPointer(opcode) && + opcode != spv::Op::OpUntypedArrayLengthKHR && + !(opcode == spv::Op::OpSpecConstantOp && + (spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthKHR))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " cannot be a type"; + } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && + !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && + !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && + !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi && + opcode != spv::Op::OpExtInst && + opcode != spv::Op::OpExtInstWithForwardRefsKHR && + opcode != spv::Op::OpExtInstImport && + opcode != spv::Op::OpSelectionMerge && + opcode != spv::Op::OpLoopMerge && + opcode != spv::Op::OpFunction && + opcode != spv::Op::OpSizeOf && + opcode != spv::Op::OpCooperativeMatrixLengthNV && + opcode != spv::Op::OpCooperativeMatrixLengthKHR && + !spvOpcodeGeneratesUntypedPointer(opcode) && + opcode != spv::Op::OpUntypedArrayLengthKHR && + !(opcode == spv::Op::OpSpecConstantOp && + (spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthNV || + spv::Op(inst->word(3)) == + spv::Op::OpCooperativeMatrixLengthKHR))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " requires a type"; + } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " in semantic instruction cannot be a non-semantic " + "instruction"; + } else { + ret = SPV_SUCCESS; + } + } else if (can_have_forward_declared_ids(i)) { + has_forward_declared_ids = true; + if (spvOpcodeGeneratesType(inst->opcode()) && + !_.IsForwardPointer(operand_word)) { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " requires a previous definition"; + } else { + ret = _.ForwardDeclareId(operand_word); + } + } else { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) + << " has not been defined"; + } + break; + case SPV_OPERAND_TYPE_TYPE_ID: + if (_.IsDefinedId(operand_word)) { + auto* def = _.FindDef(operand_word); + if (!spvOpcodeGeneratesType(def->opcode())) { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) << " is not a type id"; + } else { + ret = SPV_SUCCESS; + } + } else { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) + << " has not been defined"; + } + break; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: + // Ideally, this check would live in validate_extensions.cpp. But since + // forward references are only allowed on non-semantic instructions, and + // ID validation is done first, we would fail with a "ID had not been + // defined" error before we could give a more helpful message. For this + // reason, this test is done here, so we can be more helpful to the + // user. + if (inst->opcode() == spv::Op::OpExtInstWithForwardRefsKHR && + !inst->IsNonSemantic()) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpExtInstWithForwardRefsKHR is only allowed with " + "non-semantic instructions."; + ret = SPV_SUCCESS; + break; + default: + ret = SPV_SUCCESS; + break; + } + if (SPV_SUCCESS != ret) return ret; + } + const bool must_have_forward_declared_ids = + inst->opcode() == spv::Op::OpExtInstWithForwardRefsKHR; + if (must_have_forward_declared_ids && !has_forward_declared_ids) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Opcode OpExtInstWithForwardRefsKHR must have at least one " + "forward " + "declared ID."; + } + + if (result_id) _.RemoveIfForwardDeclared(result_id); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_image.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_image.cpp new file mode 100644 index 00000000..a5f91f7f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_image.cpp @@ -0,0 +1,2556 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of image instructions. + +#include + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Performs compile time check that all spv::ImageOperandsMask::XXX cases are +// handled in this module. If spv::ImageOperandsMask::XXX list changes, this +// function will fail the build. For all other purposes this is a placeholder +// function. +bool CheckAllImageOperandsHandled() { + spv::ImageOperandsMask enum_val = spv::ImageOperandsMask::Bias; + + // Some improvised code to prevent the compiler from considering enum_val + // constant and optimizing the switch away. + uint32_t stack_var = 0; + if (reinterpret_cast(&stack_var) % 256) + enum_val = spv::ImageOperandsMask::Lod; + + switch (enum_val) { + // Please update the validation rules in this module if you are changing + // the list of image operands, and add new enum values to this switch. + case spv::ImageOperandsMask::MaskNone: + return false; + case spv::ImageOperandsMask::Bias: + case spv::ImageOperandsMask::Lod: + case spv::ImageOperandsMask::Grad: + case spv::ImageOperandsMask::ConstOffset: + case spv::ImageOperandsMask::Offset: + case spv::ImageOperandsMask::ConstOffsets: + case spv::ImageOperandsMask::Sample: + case spv::ImageOperandsMask::MinLod: + + // TODO(dneto): Support image operands related to the Vulkan memory model. + // https://gitlab.khronos.org/spirv/spirv-tools/issues/32 + case spv::ImageOperandsMask::MakeTexelAvailableKHR: + case spv::ImageOperandsMask::MakeTexelVisibleKHR: + case spv::ImageOperandsMask::NonPrivateTexelKHR: + case spv::ImageOperandsMask::VolatileTexelKHR: + case spv::ImageOperandsMask::SignExtend: + case spv::ImageOperandsMask::ZeroExtend: + // TODO(jaebaek): Move this line properly after handling image offsets + // operand. This line temporarily fixes CI failure that + // blocks other PRs. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 + case spv::ImageOperandsMask::Offsets: + case spv::ImageOperandsMask::Nontemporal: + return true; + } + return false; +} + +// Used by GetImageTypeInfo. See OpTypeImage spec for more information. +struct ImageTypeInfo { + uint32_t sampled_type = 0; + spv::Dim dim = spv::Dim::Max; + uint32_t depth = 0; + uint32_t arrayed = 0; + uint32_t multisampled = 0; + uint32_t sampled = 0; + spv::ImageFormat format = spv::ImageFormat::Max; + spv::AccessQualifier access_qualifier = spv::AccessQualifier::Max; +}; + +// Provides information on image type. |id| should be object of either +// OpTypeImage or OpTypeSampledImage type. Returns false in case of failure +// (not a valid id, failed to parse the instruction, etc). +bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id, + ImageTypeInfo* info) { + if (!id || !info) return false; + + const Instruction* inst = _.FindDef(id); + assert(inst); + + if (inst->opcode() == spv::Op::OpTypeSampledImage) { + inst = _.FindDef(inst->word(2)); + assert(inst); + } + + if (inst->opcode() != spv::Op::OpTypeImage) return false; + + const size_t num_words = inst->words().size(); + if (num_words != 9 && num_words != 10) return false; + + info->sampled_type = inst->word(2); + info->dim = static_cast(inst->word(3)); + info->depth = inst->word(4); + info->arrayed = inst->word(5); + info->multisampled = inst->word(6); + info->sampled = inst->word(7); + info->format = static_cast(inst->word(8)); + info->access_qualifier = + num_words < 10 ? spv::AccessQualifier::Max + : static_cast(inst->word(9)); + return true; +} + +bool IsImplicitLod(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + return true; + default: + break; + } + return false; +} + +bool IsExplicitLod(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + return true; + default: + break; + } + return false; +} + +bool IsValidLodOperand(const ValidationState_t& _, spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageRead: + case spv::Op::OpImageWrite: + case spv::Op::OpImageSparseRead: + return _.HasCapability(spv::Capability::ImageReadWriteLodAMD); + default: + return IsExplicitLod(opcode); + } +} + +bool IsValidGatherLodBiasAMD(const ValidationState_t& _, spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageGather: + case spv::Op::OpImageSparseGather: + return _.HasCapability(spv::Capability::ImageGatherBiasLodAMD); + default: + break; + } + return false; +} + +// Returns true if the opcode is a Image instruction which applies +// homogenous projection to the coordinates. +bool IsProj(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + return true; + default: + break; + } + return false; +} + +// Returns the number of components in a coordinate used to access a texel in +// a single plane of an image with the given parameters. +uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) { + uint32_t plane_size = 0; + // If this switch breaks your build, please add new values below. + switch (info.dim) { + case spv::Dim::Dim1D: + case spv::Dim::Buffer: + plane_size = 1; + break; + case spv::Dim::Dim2D: + case spv::Dim::Rect: + case spv::Dim::SubpassData: + case spv::Dim::TileImageDataEXT: + plane_size = 2; + break; + case spv::Dim::Dim3D: + case spv::Dim::Cube: + // For Cube direction vector is used instead of UV. + plane_size = 3; + break; + case spv::Dim::Max: + default: + assert(0); + break; + } + + return plane_size; +} + +// Returns minimal number of coordinates based on image dim, arrayed and whether +// the instruction uses projection coordinates. +uint32_t GetMinCoordSize(spv::Op opcode, const ImageTypeInfo& info) { + if (info.dim == spv::Dim::Cube && + (opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite || + opcode == spv::Op::OpImageSparseRead)) { + // These opcodes use UV for Cube, not direction vector. + return 3; + } + + return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0); +} + +// Checks ImageOperand bitfield and respective operands. +// word_index is the index of the first word after the image-operand mask word. +spv_result_t ValidateImageOperands(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info, + uint32_t word_index) { + static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled(); + (void)kAllImageOperandsHandled; + + const spv::Op opcode = inst->opcode(); + const size_t num_words = inst->words().size(); + + const bool have_explicit_mask = (word_index - 1 < num_words); + const uint32_t mask = have_explicit_mask ? inst->word(word_index - 1) : 0u; + + if (have_explicit_mask) { + // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words. + const uint32_t mask_bits_having_operands = + mask & ~uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR | + spv::ImageOperandsMask::VolatileTexelKHR | + spv::ImageOperandsMask::SignExtend | + spv::ImageOperandsMask::ZeroExtend | + spv::ImageOperandsMask::Nontemporal); + size_t expected_num_image_operand_words = + spvtools::utils::CountSetBits(mask_bits_having_operands); + if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { + // Grad uses two words. + ++expected_num_image_operand_words; + } + + if (expected_num_image_operand_words != num_words - word_index) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Number of image operand ids doesn't correspond to the bit " + "mask"; + } + } else if (num_words != word_index - 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Number of image operand ids doesn't correspond to the bit mask"; + } + + if (info.multisampled & + (0 == (mask & uint32_t(spv::ImageOperandsMask::Sample)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Sample is required for operation on " + "multi-sampled image"; + } + + // After this point, only set bits in the image operands mask can cause + // the module to be invalid. + if (mask == 0) return SPV_SUCCESS; + + if (spvtools::utils::CountSetBits( + mask & uint32_t(spv::ImageOperandsMask::Offset | + spv::ImageOperandsMask::ConstOffset | + spv::ImageOperandsMask::ConstOffsets | + spv::ImageOperandsMask::Offsets)) > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " + "cannot be used together"; + } + + const bool is_implicit_lod = IsImplicitLod(opcode); + const bool is_explicit_lod = IsExplicitLod(opcode); + const bool is_valid_lod_operand = IsValidLodOperand(_, opcode); + const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode); + + // The checks should be done in the order of definition of OperandImage. + + if (mask & uint32_t(spv::ImageOperandsMask::Bias)) { + if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Bias can only be used with ImplicitLod opcodes"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Bias to be float scalar"; + } + + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D " + "or Cube"; + } + + // Multisampled is already checked. + } + + if (mask & uint32_t(spv::ImageOperandsMask::Lod)) { + if (!is_valid_lod_operand && opcode != spv::Op::OpImageFetch && + opcode != spv::Op::OpImageSparseFetch && + !is_valid_gather_lod_bias_amd) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod can only be used with ExplicitLod opcodes " + << "and OpImageFetch"; + } + + if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand bits Lod and Grad cannot be set at the same " + "time"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (is_explicit_lod || is_valid_gather_lod_bias_amd) { + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Lod to be float scalar when used " + << "with ExplicitLod"; + } + } else { + if (!_.IsIntScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Lod to be int scalar when used with " + << "OpImageFetch"; + } + } + + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D " + "or Cube"; + } + + // Multisampled is already checked. + } + + if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { + if (!is_explicit_lod) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Grad can only be used with ExplicitLod opcodes"; + } + + const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++)); + const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarOrVectorType(dx_type_id) || + !_.IsFloatScalarOrVectorType(dy_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both Image Operand Grad ids to be float scalars or " + << "vectors"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t dx_size = _.GetDimension(dx_type_id); + const uint32_t dy_size = _.GetDimension(dy_type_id); + if (plane_size != dx_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Grad dx to have " << plane_size + << " components, but given " << dx_size; + } + + if (plane_size != dy_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Grad dy to have " << plane_size + << " components, but given " << dy_size; + } + + // Multisampled is already checked. + } + + if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { + if (info.dim == spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffset cannot be used with Cube Image " + "'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + if (!_.IsIntScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to be int scalar or " + << "vector"; + } + + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to be a const object"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t offset_size = _.GetDimension(type_id); + if (plane_size != offset_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to have " << plane_size + << " components, but given " << offset_size; + } + } + + if (mask & uint32_t(spv::ImageOperandsMask::Offset)) { + if (info.dim == spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Offset cannot be used with Cube Image 'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + if (!_.IsIntScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Offset to be int scalar or " + << "vector"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t offset_size = _.GetDimension(type_id); + if (plane_size != offset_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Offset to have " << plane_size + << " components, but given " << offset_size; + } + + if (!_.options()->before_hlsl_legalization && + spvIsVulkanEnv(_.context()->target_env) && + !_.options()->allow_offset_texture_operand) { + if (opcode != spv::Op::OpImageGather && + opcode != spv::Op::OpImageDrefGather && + opcode != spv::Op::OpImageSparseGather && + opcode != spv::Op::OpImageSparseDrefGather) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10213) + << "Image Operand Offset can only be used with " + "OpImage*Gather operations"; + } + } + } + + if (mask & uint32_t(spv::ImageOperandsMask::ConstOffsets)) { + if (opcode != spv::Op::OpImageGather && + opcode != spv::Op::OpImageDrefGather && + opcode != spv::Op::OpImageSparseGather && + opcode != spv::Op::OpImageSparseDrefGather) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffsets can only be used with " + "OpImageGather and OpImageDrefGather"; + } + + if (info.dim == spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffsets cannot be used with Cube Image " + "'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + const Instruction* type_inst = _.FindDef(type_id); + assert(type_inst); + + if (type_inst->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be an array of size 4"; + } + + uint64_t array_size = 0; + if (!_.EvalConstantValUint64(type_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + + if (array_size != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be an array of size 4"; + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntVectorType(component_type) || + _.GetDimension(component_type) != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets array components to be " + "int vectors of size 2"; + } + + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be a const object"; + } + } + + if (mask & uint32_t(spv::ImageOperandsMask::Sample)) { + if (opcode != spv::Op::OpImageFetch && opcode != spv::Op::OpImageRead && + opcode != spv::Op::OpImageWrite && + opcode != spv::Op::OpImageSparseFetch && + opcode != spv::Op::OpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Sample can only be used with OpImageFetch, " + << "OpImageRead, OpImageWrite, OpImageSparseFetch and " + << "OpImageSparseRead"; + } + + if (info.multisampled == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Sample requires non-zero 'MS' parameter"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsIntScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Sample to be int scalar"; + } + } + + if (mask & uint32_t(spv::ImageOperandsMask::MinLod)) { + if (!is_implicit_lod && !(mask & uint32_t(spv::ImageOperandsMask::Grad))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod can only be used with ImplicitLod " + << "opcodes or together with Image Operand Grad"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand MinLod to be float scalar"; + } + + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, " + "3D or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod requires 'MS' parameter to be 0"; + } + } + + if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR)) { + // Checked elsewhere: capability and memory model are correct. + if (opcode != spv::Op::OpImageWrite) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelAvailableKHR can only be used with Op" + << spvOpcodeString(spv::Op::OpImageWrite) << ": Op" + << spvOpcodeString(opcode); + } + + if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelAvailableKHR requires " + "NonPrivateTexelKHR is also specified: Op" + << spvOpcodeString(opcode); + } + + const auto available_scope = inst->word(word_index++); + if (auto error = ValidateMemoryScope(_, inst, available_scope)) + return error; + } + + if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR)) { + // Checked elsewhere: capability and memory model are correct. + if (opcode != spv::Op::OpImageRead && + opcode != spv::Op::OpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelVisibleKHR can only be used with Op" + << spvOpcodeString(spv::Op::OpImageRead) << " or Op" + << spvOpcodeString(spv::Op::OpImageSparseRead) << ": Op" + << spvOpcodeString(opcode); + } + + if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR " + "is also specified: Op" + << spvOpcodeString(opcode); + } + + const auto visible_scope = inst->word(word_index++); + if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; + } + + if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) { + // Checked elsewhere: SPIR-V 1.4 version or later. + + // "The texel value is converted to the target value via sign extension. + // Only valid when the texel type is a scalar or vector of integer type." + // + // We don't have enough information to know what the texel type is. + // In OpenCL, knowledge is deferred until runtime: the image SampledType is + // void, and the Format is Unknown. + // In Vulkan, the texel type is only known in all cases by the pipeline + // setup. + } + + if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) { + // Checked elsewhere: SPIR-V 1.4 version or later. + + // "The texel value is converted to the target value via zero extension. + // Only valid when the texel type is a scalar or vector of integer type." + // + // We don't have enough information to know what the texel type is. + // In OpenCL, knowledge is deferred until runtime: the image SampledType is + // void, and the Format is Unknown. + // In Vulkan, the texel type is only known in all cases by the pipeline + // setup. + } + + if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) { + // TODO: add validation + } + + if (mask & uint32_t(spv::ImageOperandsMask::Nontemporal)) { + // Checked elsewhere: SPIR-V 1.6 version or later. + } + + return SPV_SUCCESS; +} + +// Validate OpImage*Proj* instructions +spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Rect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'MS' parameter to be 0"; + } + + if (info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'arrayed' parameter to be 0"; + } + + return SPV_SUCCESS; +} + +// Validate OpImage*Read and OpImage*Write instructions +spv_result_t ValidateImageReadWrite(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info) { + if (info.sampled == 2) { + if (info.dim == spv::Dim::Dim1D && + !_.HasCapability(spv::Capability::Image1D)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability Image1D is required to access storage image"; + } else if (info.dim == spv::Dim::Rect && + !_.HasCapability(spv::Capability::ImageRect)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageRect is required to access storage image"; + } else if (info.dim == spv::Dim::Buffer && + !_.HasCapability(spv::Capability::ImageBuffer)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageBuffer is required to access storage image"; + } else if (info.dim == spv::Dim::Cube && info.arrayed == 1 && + !_.HasCapability(spv::Capability::ImageCubeArray)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageCubeArray is required to access " + << "storage image"; + } + + if (info.multisampled == 1 && info.arrayed == 1 && info.sampled == 2 && + !_.HasCapability(spv::Capability::ImageMSArray)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageMSArray is required to access storage " + << "image"; + } + } else if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 2"; + } + + return SPV_SUCCESS; +} + +// Returns true if opcode is *ImageSparse*, false otherwise. +bool IsSparse(spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageSparseTexelsResident: + case spv::Op::OpImageSparseRead: { + return true; + } + + default: { return false; } + } + + return false; +} + +// Checks sparse image opcode result type and returns the second struct member. +// Returns inst.type_id for non-sparse image opcodes. +// Not valid for sparse image opcodes which do not return a struct. +spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst, + uint32_t* actual_result_type) { + const spv::Op opcode = inst->opcode(); + + if (IsSparse(opcode)) { + const Instruction* const type_inst = _.FindDef(inst->type_id()); + assert(type_inst); + + if (!type_inst || type_inst->opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeStruct"; + } + + if (type_inst->words().size() != 4 || + !_.IsIntScalarType(type_inst->word(2))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a struct containing an int " + "scalar and a texel"; + } + + *actual_result_type = type_inst->word(3); + } else { + *actual_result_type = inst->type_id(); + } + + return SPV_SUCCESS; +} + +// Returns a string describing actual result type of an opcode. +// Not valid for sparse image opcodes which do not return a struct. +const char* GetActualResultTypeStr(spv::Op opcode) { + if (IsSparse(opcode)) return "Result Type's second member"; + return "Result Type"; +} + +spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { + assert(inst->type_id() == 0); + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, inst->word(1), &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (_.IsIntScalarType(info.sampled_type) && + (64 == _.GetBitWidth(info.sampled_type)) && + !_.HasCapability(spv::Capability::Int64ImageEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability Int64ImageEXT is required when using Sampled Type of " + "64-bit int"; + } + + const auto target_env = _.context()->target_env; + if (spvIsVulkanEnv(target_env)) { + if ((!_.IsFloatScalarType(info.sampled_type) && + !_.IsIntScalarType(info.sampled_type)) || + ((32 != _.GetBitWidth(info.sampled_type)) && + (64 != _.GetBitWidth(info.sampled_type))) || + ((64 == _.GetBitWidth(info.sampled_type)) && + _.IsFloatScalarType(info.sampled_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4656) + << "Expected Sampled Type to be a 32-bit int, 64-bit int or " + "32-bit float scalar type for Vulkan environment"; + } + } else if (spvIsOpenCLEnv(target_env)) { + if (!_.IsVoidType(info.sampled_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled Type must be OpTypeVoid in the OpenCL environment."; + } + } else { + const spv::Op sampled_type_opcode = _.GetIdOpcode(info.sampled_type); + if (sampled_type_opcode != spv::Op::OpTypeVoid && + sampled_type_opcode != spv::Op::OpTypeInt && + sampled_type_opcode != spv::Op::OpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Type to be either void or" + << " numerical scalar type"; + } + } + + // Universal checks on image type operands + // Dim and Format and Access Qualifier are checked elsewhere. + + if (info.depth > 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)"; + } + + if (info.arrayed > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; + } + + if (info.multisampled > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; + } + + if (info.sampled > 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; + } + + if (info.dim == spv::Dim::SubpassData) { + if (info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2"; + } + + if (info.format != spv::ImageFormat::Unknown) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim SubpassData requires format Unknown"; + } + } else if (info.dim == spv::Dim::TileImageDataEXT) { + if (_.IsVoidType(info.sampled_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim TileImageDataEXT requires Sampled Type to be not " + "OpTypeVoid"; + } + if (info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim TileImageDataEXT requires Sampled to be 2"; + } + if (info.format != spv::ImageFormat::Unknown) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim TileImageDataEXT requires format Unknown"; + } + if (info.depth != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim TileImageDataEXT requires Depth to be 0"; + } + if (info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim TileImageDataEXT requires Arrayed to be 0"; + } + } else { + if (info.multisampled && (info.sampled == 2) && + !_.HasCapability(spv::Capability::StorageImageMultisample)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageMultisample is required when using " + "multisampled storage image"; + } + } + + if (spvIsOpenCLEnv(target_env)) { + if ((info.arrayed == 1) && (info.dim != spv::Dim::Dim1D) && + (info.dim != spv::Dim::Dim2D)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the OpenCL environment, Arrayed may only be set to 1 " + << "when Dim is either 1D or 2D."; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MS must be 0 in the OpenCL environment."; + } + + if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled must be 0 in the OpenCL environment."; + } + + if (info.access_qualifier == spv::AccessQualifier::Max) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the OpenCL environment, the optional Access Qualifier" + << " must be present."; + } + } + + if (spvIsVulkanEnv(target_env)) { + if (info.sampled == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4657) + << "Sampled must be 1 or 2 in the Vulkan environment."; + } + + if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6214) + << "Dim SubpassData requires Arrayed to be 0 in the Vulkan " + "environment"; + } + + if (info.dim == spv::Dim::Rect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(9638) + << "Dim must not be Rect in the Vulkan environment"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeSampledImage(ValidationState_t& _, + const Instruction* inst) { + const uint32_t image_type = inst->word(2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + // OpenCL requires Sampled=0, checked elsewhere. + // Vulkan uses the Sampled=1 case. + // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated + // elsewhere. + if ((info.sampled != 0) && (info.sampled != 1)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4657) + << "Sampled image type requires an image type with \"Sampled\" " + "operand set to 0 or 1"; + } + + // This covers both OpTypeSampledImage and OpSampledImage. + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && + info.dim == spv::Dim::Buffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, sampled image dimension must not be " + "Buffer"; + } + + return SPV_SUCCESS; +} + +bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) { + switch (opcode) { + case spv::Op::OpSampledImage: + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImage: + case spv::Op::OpImageQueryLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpCopyObject: + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + case spv::Op::OpImageSampleFootprintNV: + return true; + case spv::Op::OpStore: + if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true; + return false; + default: + return false; + } +} + +spv_result_t ValidateSampledImage(ValidationState_t& _, + const Instruction* inst) { + auto type_inst = _.FindDef(inst->type_id()); + if (type_inst->opcode() != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeSampledImage."; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage."; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + // Image operands must match except for depth. + auto sampled_image_id = type_inst->GetOperandAs(1); + if (sampled_image_id != image_type) { + ImageTypeInfo sampled_info; + if (!GetImageTypeInfo(_, sampled_image_id, &sampled_info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + if (info.sampled_type != sampled_info.sampled_type || + info.dim != sampled_info.dim || info.arrayed != sampled_info.arrayed || + info.multisampled != sampled_info.multisampled || + info.sampled != sampled_info.sampled || + info.format != sampled_info.format || + info.access_qualifier != sampled_info.access_qualifier) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image operands must match result image operands except for " + "depth"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6671) + << "Expected Image 'Sampled' parameter to be 1 for Vulkan " + "environment."; + } + } else { + if (info.sampled != 0 && info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 1"; + } + } + + if (info.dim == spv::Dim::SubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be not SubpassData."; + } + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampler to be of type OpTypeSampler"; + } + + // We need to validate 2 things: + // * All OpSampledImage instructions must be in the same block in which their + // Result are consumed. + // * Result from OpSampledImage instructions must not appear as operands + // to OpPhi instructions or OpSelect instructions, or any instructions other + // than the image lookup and query instructions specified to take an operand + // whose type is OpTypeSampledImage. + std::vector consumers = _.getSampledImageConsumers(inst->id()); + if (!consumers.empty()) { + for (auto consumer_instr : consumers) { + const auto consumer_opcode = consumer_instr->opcode(); + if (consumer_instr->block() != inst->block()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "All OpSampledImage instructions must be in the same block " + "in " + "which their Result are consumed. OpSampledImage Result " + "Type " + << _.getIdName(inst->id()) + << " has a consumer in a different basic " + "block. The consumer instruction is " + << _.getIdName(consumer_instr->id()) << "."; + } + + if (consumer_opcode == spv::Op::OpPhi || + consumer_opcode == spv::Op::OpSelect) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result from OpSampledImage instruction must not appear " + "as " + "operands of Op" + << spvOpcodeString(static_cast(consumer_opcode)) << "." + << " Found result " << _.getIdName(inst->id()) + << " as an operand of " << _.getIdName(consumer_instr->id()) + << "."; + } + + if (!IsAllowedSampledImageOperand(consumer_opcode, _)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result from OpSampledImage instruction must not appear " + "as operand for Op" + << spvOpcodeString(static_cast(consumer_opcode)) + << ", since it is not specified as taking an " + << "OpTypeSampledImage." + << " Found result " << _.getIdName(inst->id()) + << " as an operand of " << _.getIdName(consumer_instr->id()) + << "."; + } + } + } + + const Instruction* ld_inst; + { + int t_idx = inst->GetOperandAs(2); + ld_inst = _.FindDef(t_idx); + } + + if (ld_inst->opcode() == spv::Op::OpLoad) { + int texture_id = ld_inst->GetOperandAs(2); // variable to load + _.RegisterQCOMImageProcessingTextureConsumer(texture_id, ld_inst, inst); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageTexelPointer(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypePointer && + result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer"; + } + + const auto storage_class = result_type->GetOperandAs(1); + if (storage_class != spv::StorageClass::Image) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer whose Storage Class " + "operand is Image"; + } + + uint32_t ptr_type = 0; + if (result_type->opcode() == spv::Op::OpTypePointer) { + ptr_type = result_type->GetOperandAs(2); + const auto ptr_opcode = _.GetIdOpcode(ptr_type); + if (ptr_opcode != spv::Op::OpTypeInt && + ptr_opcode != spv::Op::OpTypeFloat && + ptr_opcode != spv::Op::OpTypeVoid && + !(ptr_opcode == spv::Op::OpTypeVector && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(ptr_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer whose Type operand " + "must be a scalar numerical type or OpTypeVoid"; + } + } + + const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2)); + if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be OpTypePointer"; + } + + const auto image_type = image_ptr->GetOperandAs(2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be OpTypePointer with Type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (result_type->opcode() == spv::Op::OpTypePointer && + info.sampled_type != ptr_type && + !(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) && + _.IsFloat16Vector2Or4Type(ptr_type) && + _.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat && + ((_.GetDimension(ptr_type) == 2 && + info.format == spv::ImageFormat::Rg16f) || + (_.GetDimension(ptr_type) == 4 && + info.format == spv::ImageFormat::Rgba16f)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as the Type " + "pointed to by Result Type"; + } + + if (info.dim == spv::Dim::SubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim SubpassData cannot be used with OpImageTexelPointer"; + } + + if (info.dim == spv::Dim::TileImageDataEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim TileImageDataEXT cannot be used with " + "OpImageTexelPointer"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be integer scalar or vector"; + } + + uint32_t expected_coord_size = 0; + if (info.arrayed == 0) { + expected_coord_size = GetPlaneCoordSize(info); + } else if (info.arrayed == 1) { + switch (info.dim) { + case spv::Dim::Dim1D: + expected_coord_size = 2; + break; + case spv::Dim::Cube: + case spv::Dim::Dim2D: + expected_coord_size = 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when " + "Arrayed is 1"; + break; + } + } + + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (expected_coord_size != actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have " << expected_coord_size + << " components, but given " << actual_coord_size; + } + + const uint32_t sample_type = _.GetOperandTypeId(inst, 4); + if (!sample_type || !_.IsIntScalarType(sample_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample to be integer scalar"; + } + + if (info.multisampled == 0) { + uint64_t ms = 0; + if (!_.EvalConstantValUint64(inst->GetOperandAs(4), &ms) || + ms != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample for Image with MS 0 to be a valid for " + "the value 0"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((info.format != spv::ImageFormat::R64i) && + (info.format != spv::ImageFormat::R64ui) && + (info.format != spv::ImageFormat::R32f) && + (info.format != spv::ImageFormat::R32i) && + (info.format != spv::ImageFormat::R32ui) && + !((info.format == spv::ImageFormat::Rg16f || + info.format == spv::ImageFormat::Rgba16f) && + _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4658) + << "Expected the Image Format in Image to be R64i, R64ui, R32f, " + "R32i, or R32ui for Vulkan environment"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } + + if (info.multisampled) { + // When using image operands, the Sample image operand is required if and + // only if the image is multisampled (MS=1). The Sample image operand is + // only allowed for fetch, read, and write. + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampling operation is invalid for multisample image"; + } + + if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t texel_component_type = + _.GetComponentType(actual_result_type); + if (texel_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if ((opcode == spv::Op::OpImageSampleExplicitLod || + opcode == spv::Op::OpImageSparseSampleExplicitLod) && + _.HasCapability(spv::Capability::Kernel)) { + if (!_.IsFloatScalarOrVectorType(coord_type) && + !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int or float scalar or vector"; + } + } else { + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); + + if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (opcode == spv::Op::OpImageSampleExplicitLod) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } + } + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +// Validates anything OpImage*Dref* instruction +spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + const uint32_t dref_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Dref to be of 32-bit float type"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.dim == spv::Dim::Dim3D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4777) + << "In Vulkan, OpImage*Dref* instructions must not use images " + "with a 3D Dim"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageDrefLod(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntScalarType(actual_result_type) && + !_.IsFloatScalarType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float scalar type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } + + if (info.multisampled) { + // When using image operands, the Sample image operand is required if and + // only if the image is multisampled (MS=1). The Sample image operand is + // only allowed for fetch, read, and write. + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dref sampling operation is invalid for multisample image"; + } + + if (actual_result_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode); + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 7)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) { + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + const spv::Op opcode = inst->opcode(); + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (info.dim == spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube"; + } + + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 1"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageGather(ValidationState_t& _, + const Instruction* inst) { + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) + return error; + + const spv::Op opcode = inst->opcode(); + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.multisampled) { + // When using image operands, the Sample image operand is required if and + // only if the image is multisampled (MS=1). The Sample image operand is + // only allowed for fetch, read, and write. + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Gather operation is invalid for multisample image"; + } + + if (opcode == spv::Op::OpImageDrefGather || + opcode == spv::Op::OpImageSparseDrefGather || + _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube && + info.dim != spv::Dim::Rect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4777) + << "Expected Image 'Dim' to be 2D, Cube, or Rect"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (opcode == spv::Op::OpImageGather || + opcode == spv::Op::OpImageSparseGather) { + const uint32_t component = inst->GetOperandAs(4); + const uint32_t component_index_type = _.GetTypeId(component); + if (!_.IsIntScalarType(component_index_type) || + _.GetBitWidth(component_index_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Component to be 32-bit int scalar"; + } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4664) + << "Expected Component Operand to be a const object for Vulkan " + "environment"; + } + } + } else { + assert(opcode == spv::Op::OpImageDrefGather || + opcode == spv::Op::OpImageSparseDrefGather); + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 7)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntScalarOrVectorType(actual_result_type) && + !_.IsFloatScalarOrVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float scalar or vector type"; + } + + const auto target_env = _.context()->target_env; + // Vulkan requires the result to be a 4-element int or float + // vector. + if (spvIsVulkanEnv(target_env)) { + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4780) << "Expected " + << GetActualResultTypeStr(opcode) << " to have 4 components"; + } + } // Check OpenCL below, after we get the image info. + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (spvIsOpenCLEnv(target_env)) { + // In OpenCL, a read from a depth image returns a scalar float. In other + // cases, the result is always a 4-element vector. + // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images + // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions + // The builtins for reading depth images are: + // float read_imagef(aQual image2d_depth_t image, int2 coord) + // float read_imagef(aQual image2d_array_depth_t image, int4 coord) + if (info.depth) { + if (!_.IsFloatScalarType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " from a depth image read to result in a scalar float value"; + } + } else { + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + } + + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); + if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } + } + + if (info.dim == spv::Dim::SubpassData) { + if (opcode == spv::Op::OpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim SubpassData cannot be used with ImageSparseRead"; + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + std::string("Dim SubpassData requires Fragment execution model: ") + + spvOpcodeString(opcode)); + } + + if (info.dim == spv::Dim::TileImageDataEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim TileImageDataEXT cannot be used with " + << spvOpcodeString(opcode); + } + + if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.format == spv::ImageFormat::Unknown && + info.dim != spv::Dim::SubpassData && + !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageReadWithoutFormat is required to " + << "read storage image"; + } + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { + const uint32_t image_type = _.GetOperandTypeId(inst, 0); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim == spv::Dim::SubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' cannot be SubpassData"; + } + + if (info.dim == spv::Dim::TileImageDataEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' cannot be TileImageDataEXT"; + } + + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; + + const uint32_t coord_type = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + // because it needs to match with 'Sampled Type' the Texel can't be a boolean + const uint32_t texel_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarOrVectorType(texel_type) && + !_.IsFloatScalarOrVectorType(texel_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Texel to be int or float vector or scalar"; + } + + if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { + const uint32_t texel_component_type = _.GetComponentType(texel_type); + if (texel_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as Texel " + << "components"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.format == spv::ImageFormat::Unknown && + info.dim != spv::Dim::SubpassData && + !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageWriteWithoutFormat is required to " + "write " + << "to storage image"; + } + } + + if (inst->words().size() > 4) { + if (spvIsOpenCLEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Optional Image Operands are not allowed in the OpenCL " + << "environment."; + } + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, /* word_index = */ 5)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeImage"; + } + + const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2); + const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type); + assert(sampled_image_type_inst); + + if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample Image to be of type OpTypeSampleImage"; + } + + if (sampled_image_type_inst->word(2) != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample Image image type to be equal to Result Type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar or vector type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + uint32_t expected_num_components = info.arrayed; + switch (info.dim) { + case spv::Dim::Dim1D: + expected_num_components += 1; + break; + case spv::Dim::Dim2D: + case spv::Dim::Cube: + expected_num_components += 2; + break; + case spv::Dim::Dim3D: + expected_num_components += 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0"; + } + + const auto target_env = _.context()->target_env; + if (spvIsVulkanEnv(target_env)) { + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4659) + << "OpImageQuerySizeLod must only consume an \"Image\" operand " + "whose type has its \"Sampled\" operand set to 1"; + } + } + + uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != expected_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type has " << result_num_components << " components, " + << "but " << expected_num_components << " expected"; + } + + const uint32_t lod_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(lod_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Level of Detail to be int scalar"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQuerySize(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar or vector type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + uint32_t expected_num_components = info.arrayed; + switch (info.dim) { + case spv::Dim::Dim1D: + case spv::Dim::Buffer: + expected_num_components += 1; + break; + case spv::Dim::Dim2D: + case spv::Dim::Cube: + case spv::Dim::Rect: + expected_num_components += 2; + break; + case spv::Dim::Dim3D: + expected_num_components += 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect"; + } + + if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D || + info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) { + if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2"; + } + } + + uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != expected_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type has " << result_num_components << " components, " + << "but " << expected_num_components << " expected"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim == spv::Dim::TileImageDataEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' cannot be TileImageDataEXT"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQueryLod(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [&](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::Fragment && + model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::MeshEXT && + model != spv::ExecutionModel::TaskEXT) { + if (message) { + *message = std::string( + "OpImageQueryLod requires Fragment, GLCompute, MeshEXT or " + "TaskEXT execution model"); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models && + (models->find(spv::ExecutionModel::GLCompute) != models->end() || + models->find(spv::ExecutionModel::MeshEXT) != models->end() || + models->find(spv::ExecutionModel::TaskEXT) != models->end()) && + (!modes || + (modes->find(spv::ExecutionMode::DerivativeGroupLinearKHR) == + modes->end() && + modes->find(spv::ExecutionMode::DerivativeGroupQuadsKHR) == + modes->end()))) { + if (message) { + *message = std::string( + "OpImageQueryLod requires DerivativeGroupQuadsKHR " + "or DerivativeGroupLinearKHR execution mode for GLCompute, " + "MeshEXT or TaskEXT execution model"); + } + return false; + } + return true; + }); + + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be float vector type"; + } + + if (_.GetDimension(result_type) != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have 2 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image operand to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (_.HasCapability(spv::Capability::Kernel)) { + if (!_.IsFloatScalarOrVectorType(coord_type) && + !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int or float scalar or vector"; + } + } else { + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + } + + const uint32_t min_coord_size = GetPlaneCoordSize(info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + // The operand is a sampled image. + // The sampled image type is already checked to be parameterized by an image + // type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have + // Sampled=1. So the validator already enforces Vulkan VUID 4659: + // OpImageQuerySizeLod must only consume an "Image" operand whose type has + // its "Sampled" operand set to 1 + return SPV_SUCCESS; +} + +spv_result_t ValidateImageSparseLod(ValidationState_t& _, + const Instruction* inst) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction reserved for future use, use of this instruction " + << "is invalid"; +} + +spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + const spv::Op opcode = inst->opcode(); + if (opcode == spv::Op::OpImageQueryLevels) { + if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && + info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + const auto target_env = _.context()->target_env; + if (spvIsVulkanEnv(target_env)) { + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4659) + << "OpImageQueryLevels must only consume an \"Image\" operand " + "whose type has its \"Sampled\" operand set to 1"; + } + } + } else { + assert(opcode == spv::Op::OpImageQuerySamples); + if (info.dim != spv::Dim::Dim2D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D"; + } + + if (info.multisampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be bool scalar type"; + } + + const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(resident_code_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Resident Code to be int scalar"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageProcessingQCOMDecoration(ValidationState_t& _, int id, + spv::Decoration decor) { + const Instruction* si_inst = nullptr; + const Instruction* ld_inst = _.FindDef(id); + bool is_intf_obj = (ld_inst->opcode() == spv::Op::OpSampledImage); + if (is_intf_obj == true) { + si_inst = ld_inst; + int t_idx = si_inst->GetOperandAs(2); // texture + ld_inst = _.FindDef(t_idx); + } + if (ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad"; + } + int texture_id = ld_inst->GetOperandAs(2); // variable to load + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageProcessing2QCOMWindowDecoration(ValidationState_t& _, + int id) { + const Instruction* ld_inst = _.FindDef(id); + bool is_intf_obj = (ld_inst->opcode() != spv::Op::OpSampledImage); + if (is_intf_obj == true) { + if (ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) << "Expect to see OpLoad"; + } + int texture_id = ld_inst->GetOperandAs(2); // variable to load + spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + decor = spv::Decoration::BlockMatchSamplerQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + } else { + const Instruction* si_inst = ld_inst; + int t_idx = si_inst->GetOperandAs(2); // texture + const Instruction* t_ld_inst = _.FindDef(t_idx); + if (t_ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, t_ld_inst) + << "Expect to see OpLoad"; + } + int texture_id = t_ld_inst->GetOperandAs(2); // variable to load + spv::Decoration decor = spv::Decoration::BlockMatchTextureQCOM; + if (!_.HasDecoration(texture_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + int s_idx = si_inst->GetOperandAs(3); // sampler + const Instruction* s_ld_inst = _.FindDef(s_idx); + if (s_ld_inst->opcode() != spv::Op::OpLoad) { + return _.diag(SPV_ERROR_INVALID_DATA, s_ld_inst) + << "Expect to see OpLoad"; + } + int sampler_id = s_ld_inst->GetOperandAs(2); // variable to load + decor = spv::Decoration::BlockMatchSamplerQCOM; + if (!_.HasDecoration(sampler_id, decor)) { + return _.diag(SPV_ERROR_INVALID_DATA, ld_inst) + << "Missing decoration " << _.SpvDecorationString(decor); + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageProcessingQCOM(ValidationState_t& _, + const Instruction* inst) { + spv_result_t res = SPV_SUCCESS; + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpImageSampleWeightedQCOM: { + int wi_idx = inst->GetOperandAs(4); // weight + res = ValidateImageProcessingQCOMDecoration( + _, wi_idx, spv::Decoration::WeightTextureQCOM); + break; + } + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: { + int tgt_idx = inst->GetOperandAs(2); // target + res = ValidateImageProcessingQCOMDecoration( + _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs(4); // reference + res = ValidateImageProcessingQCOMDecoration( + _, ref_idx, spv::Decoration::BlockMatchTextureQCOM); + break; + } + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: { + int tgt_idx = inst->GetOperandAs(2); // target + res = ValidateImageProcessing2QCOMWindowDecoration(_, tgt_idx); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs(4); // reference + res = ValidateImageProcessing2QCOMWindowDecoration(_, ref_idx); + break; + } + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: { + int tgt_idx = inst->GetOperandAs(2); // target + res = ValidateImageProcessingQCOMDecoration( + _, tgt_idx, spv::Decoration::BlockMatchTextureQCOM); + if (res != SPV_SUCCESS) break; + int ref_idx = inst->GetOperandAs(4); // reference + res = ValidateImageProcessingQCOMDecoration( + _, ref_idx, spv::Decoration::BlockMatchTextureQCOM); + break; + } + default: + break; + } + + return res; +} + +} // namespace + +// Validates correctness of image instructions. +spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + if (IsImplicitLod(opcode)) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::Fragment && + model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::MeshEXT && + model != spv::ExecutionModel::TaskEXT) { + if (message) { + *message = + std::string( + "ImplicitLod instructions require Fragment, GLCompute, " + "MeshEXT or TaskEXT execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([opcode](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models && + (models->find(spv::ExecutionModel::GLCompute) != models->end() || + models->find(spv::ExecutionModel::MeshEXT) != models->end() || + models->find(spv::ExecutionModel::TaskEXT) != models->end()) && + (!modes || + (modes->find(spv::ExecutionMode::DerivativeGroupLinearKHR) == + modes->end() && + modes->find(spv::ExecutionMode::DerivativeGroupQuadsKHR) == + modes->end()))) { + if (message) { + *message = std::string( + "ImplicitLod instructions require " + "DerivativeGroupQuadsKHR " + "or DerivativeGroupLinearKHR execution mode for " + "GLCompute, " + "MeshEXT or TaskEXT execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + } + + switch (opcode) { + case spv::Op::OpTypeImage: + return ValidateTypeImage(_, inst); + case spv::Op::OpTypeSampledImage: + return ValidateTypeSampledImage(_, inst); + case spv::Op::OpSampledImage: + return ValidateSampledImage(_, inst); + case spv::Op::OpImageTexelPointer: + return ValidateImageTexelPointer(_, inst); + + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + return ValidateImageLod(_, inst); + + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + return ValidateImageDrefLod(_, inst); + + case spv::Op::OpImageFetch: + case spv::Op::OpImageSparseFetch: + return ValidateImageFetch(_, inst); + + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + return ValidateImageGather(_, inst); + + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseRead: + return ValidateImageRead(_, inst); + + case spv::Op::OpImageWrite: + return ValidateImageWrite(_, inst); + + case spv::Op::OpImage: + return ValidateImage(_, inst); + + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryOrder: + return ValidateImageQueryFormatOrOrder(_, inst); + + case spv::Op::OpImageQuerySizeLod: + return ValidateImageQuerySizeLod(_, inst); + case spv::Op::OpImageQuerySize: + return ValidateImageQuerySize(_, inst); + case spv::Op::OpImageQueryLod: + return ValidateImageQueryLod(_, inst); + + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQuerySamples: + return ValidateImageQueryLevelsOrSamples(_, inst); + + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + return ValidateImageSparseLod(_, inst); + + case spv::Op::OpImageSparseTexelsResident: + return ValidateImageSparseTexelsResident(_, inst); + + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + return ValidateImageProcessingQCOM(_, inst); + + default: + break; + } + + return SPV_SUCCESS; +} + +bool IsImageInstruction(const spv::Op opcode) { + switch (opcode) { + case spv::Op::OpImageSampleImplicitLod: + case spv::Op::OpImageSampleDrefImplicitLod: + case spv::Op::OpImageSampleProjImplicitLod: + case spv::Op::OpImageSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleImplicitLod: + case spv::Op::OpImageSparseSampleDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + + case spv::Op::OpImageSampleExplicitLod: + case spv::Op::OpImageSampleDrefExplicitLod: + case spv::Op::OpImageSampleProjExplicitLod: + case spv::Op::OpImageSampleProjDrefExplicitLod: + case spv::Op::OpImageSparseSampleExplicitLod: + case spv::Op::OpImageSparseSampleDrefExplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: + + case spv::Op::OpImage: + case spv::Op::OpImageFetch: + case spv::Op::OpImageSparseFetch: + case spv::Op::OpImageGather: + case spv::Op::OpImageDrefGather: + case spv::Op::OpImageSparseGather: + case spv::Op::OpImageSparseDrefGather: + case spv::Op::OpImageRead: + case spv::Op::OpImageSparseRead: + case spv::Op::OpImageWrite: + + case spv::Op::OpImageQueryFormat: + case spv::Op::OpImageQueryOrder: + case spv::Op::OpImageQuerySizeLod: + case spv::Op::OpImageQuerySize: + case spv::Op::OpImageQueryLod: + case spv::Op::OpImageQueryLevels: + case spv::Op::OpImageQuerySamples: + + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _, + const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + if (!IsImageInstruction(opcode)) return SPV_SUCCESS; + + switch (opcode) { + case spv::Op::OpImageSampleWeightedQCOM: + case spv::Op::OpImageBoxFilterQCOM: + case spv::Op::OpImageBlockMatchSSDQCOM: + case spv::Op::OpImageBlockMatchSADQCOM: + break; + case spv::Op::OpImageBlockMatchWindowSADQCOM: + case spv::Op::OpImageBlockMatchWindowSSDQCOM: + case spv::Op::OpImageBlockMatchGatherSADQCOM: + case spv::Op::OpImageBlockMatchGatherSSDQCOM: + break; + default: + for (size_t i = 0; i < inst->operands().size(); ++i) { + int id = inst->GetOperandAs(i); + const Instruction* operand_inst = _.FindDef(id); + if (operand_inst == nullptr) continue; + if (operand_inst->opcode() == spv::Op::OpLoad) { + if (_.IsQCOMImageProcessingTextureConsumer(id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal use of QCOM image processing decorated texture"; + } + } + if (operand_inst->opcode() == spv::Op::OpSampledImage) { + if (_.IsQCOMImageProcessingTextureConsumer(id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal use of QCOM image processing decorated texture"; + } + } + } + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_instruction.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_instruction.cpp new file mode 100644 index 00000000..acb0d637 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_instruction.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Performs validation on instructions that appear inside of a SPIR-V block. + +#include +#include +#include +#include + +#include "source/enum_set.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +std::string ToString(const CapabilitySet& capabilities, + const AssemblyGrammar& grammar) { + std::stringstream ss; + for (auto capability : capabilities) { + spv_operand_desc desc; + if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(capability), &desc)) + ss << desc->name << " "; + else + ss << uint32_t(capability) << " "; + } + return ss.str(); +} + +// Returns capabilities that enable an opcode. An empty result is interpreted +// as no prohibition of use of the opcode. If the result is non-empty, then +// the opcode may only be used if at least one of the capabilities is specified +// by the module. +CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state, + spv::Op opcode) { + // Exceptions for SPV_AMD_shader_ballot + switch (opcode) { + // Normally these would require Group capability + case spv::Op::OpGroupIAddNonUniformAMD: + case spv::Op::OpGroupFAddNonUniformAMD: + case spv::Op::OpGroupFMinNonUniformAMD: + case spv::Op::OpGroupUMinNonUniformAMD: + case spv::Op::OpGroupSMinNonUniformAMD: + case spv::Op::OpGroupFMaxNonUniformAMD: + case spv::Op::OpGroupUMaxNonUniformAMD: + case spv::Op::OpGroupSMaxNonUniformAMD: + if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet(); + break; + default: + break; + } + // Look it up in the grammar + spv_opcode_desc opcode_desc = {}; + if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) { + return state.grammar().filterCapsAgainstTargetEnv( + opcode_desc->capabilities, opcode_desc->numCapabilities); + } + return CapabilitySet(); +} + +// Returns SPV_SUCCESS if, for the given operand, the target environment +// satsifies minimum version requirements, or if the module declares an +// enabling extension for the operand. Otherwise emit a diagnostic and +// return an error code. +spv_result_t OperandVersionExtensionCheck( + ValidationState_t& _, const Instruction* inst, size_t which_operand, + const spv_operand_desc_t& operand_desc, uint32_t word) { + const uint32_t module_version = _.version(); + const uint32_t operand_min_version = operand_desc.minVersion; + const uint32_t operand_last_version = operand_desc.lastVersion; + const bool reserved = operand_min_version == 0xffffffffu; + const bool version_satisfied = !reserved && + (operand_min_version <= module_version) && + (module_version <= operand_last_version); + + if (version_satisfied) { + return SPV_SUCCESS; + } + + if (operand_last_version < module_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " + << operand_desc.name << "(" << word << ") requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version) + << " or earlier"; + } + + if (!reserved && operand_desc.numExtensions == 0) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " + << operand_desc.name << "(" << word << ") requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later"; + } else { + ExtensionSet required_extensions(operand_desc.numExtensions, + operand_desc.extensions); + if (!_.HasAnyOfExtensions(required_extensions)) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) + << ": operand " << operand_desc.name << "(" << word + << ") requires one of these extensions: " + << ExtensionSetToString(required_extensions); + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if the given operand is enabled by capabilities declared +// in the module. Otherwise issues an error message and returns +// SPV_ERROR_INVALID_CAPABILITY. +spv_result_t CheckRequiredCapabilities(ValidationState_t& state, + const Instruction* inst, + size_t which_operand, + const spv_parsed_operand_t& operand, + uint32_t word) { + // Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin + // decoration does not require the associated capability. The use of such + // a variable value should trigger the capability requirement, but that's + // not implemented yet. This rule is independent of target environment. + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 + if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) { + switch (spv::BuiltIn(word)) { + case spv::BuiltIn::PointSize: + case spv::BuiltIn::ClipDistance: + case spv::BuiltIn::CullDistance: + return SPV_SUCCESS; + default: + break; + } + } else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) { + // Allow all FP rounding modes if requested + if (state.features().free_fp_rounding_mode) { + return SPV_SUCCESS; + } + } else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION && + state.features().group_ops_reduce_and_scans && + (word <= uint32_t(spv::GroupOperation::ExclusiveScan))) { + // Allow certain group operations if requested. + return SPV_SUCCESS; + } + + CapabilitySet enabling_capabilities; + spv_operand_desc operand_desc = nullptr; + const auto lookup_result = + state.grammar().lookupOperand(operand.type, word, &operand_desc); + if (lookup_result == SPV_SUCCESS) { + // Allow FPRoundingMode decoration if requested. + if (operand.type == SPV_OPERAND_TYPE_DECORATION && + spv::Decoration(operand_desc->value) == + spv::Decoration::FPRoundingMode) { + if (state.features().free_fp_rounding_mode) return SPV_SUCCESS; + + // Vulkan API requires more capabilities on rounding mode. + if (spvIsVulkanEnv(state.context()->target_env)) { + enabling_capabilities.insert( + spv::Capability::StorageUniformBufferBlock16); + enabling_capabilities.insert(spv::Capability::StorageUniform16); + enabling_capabilities.insert(spv::Capability::StoragePushConstant16); + enabling_capabilities.insert(spv::Capability::StorageInputOutput16); + } + } else { + enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv( + operand_desc->capabilities, operand_desc->numCapabilities); + } + + // When encountering an OpCapability instruction, the instruction pass + // registers a capability with the module *before* checking capabilities. + // So in the case of an OpCapability instruction, don't bother checking + // enablement by another capability. + if (inst->opcode() != spv::Op::OpCapability) { + const bool enabled_by_cap = + state.HasAnyOfCapabilities(enabling_capabilities); + if (!enabling_capabilities.empty() && !enabled_by_cap) { + return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Operand " << which_operand << " of " + << spvOpcodeString(inst->opcode()) + << " requires one of these capabilities: " + << ToString(enabling_capabilities, state.grammar()); + } + } + return OperandVersionExtensionCheck(state, inst, which_operand, + *operand_desc, word); + } + return SPV_SUCCESS; +} + +// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction +// is explicitly reserved in the SPIR-V core spec. Otherwise return +// SPV_SUCCESS. +spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + switch (opcode) { + // These instructions are enabled by a capability, but should never + // be used anyway. + case spv::Op::OpImageSparseSampleProjImplicitLod: + case spv::Op::OpImageSparseSampleProjExplicitLod: + case spv::Op::OpImageSparseSampleProjDrefImplicitLod: + case spv::Op::OpImageSparseSampleProjDrefExplicitLod: { + spv_opcode_desc inst_desc; + _.grammar().lookupOpcode(opcode, &inst_desc); + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Invalid Opcode name 'Op" << inst_desc->name << "'"; + } + default: + break; + } + return SPV_SUCCESS; +} + +// Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the +// instruction is invalid because the required capability isn't declared +// in the module. +spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + CapabilitySet opcode_caps = EnablingCapabilitiesForOp(_, opcode); + if (!_.HasAnyOfCapabilities(opcode_caps)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires one of these capabilities: " + << ToString(opcode_caps, _.grammar()); + } + for (size_t i = 0; i < inst->operands().size(); ++i) { + const auto& operand = inst->operand(i); + const auto word = inst->word(operand.offset); + if (spvOperandIsConcreteMask(operand.type)) { + // Check for required capabilities for each bit position of the mask. + for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) { + if (word & mask_bit) { + spv_result_t status = + CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit); + if (status != SPV_SUCCESS) return status; + } + } + } else if (spvIsIdType(operand.type)) { + // TODO(dneto): Check the value referenced by this Id, if we can compute + // it. For now, just punt, to fix issue 248: + // https://github.com/KhronosGroup/SPIRV-Tools/issues/248 + } else { + // Check the operand word as a whole. + spv_result_t status = + CheckRequiredCapabilities(_, inst, i + 1, operand, word); + if (status != SPV_SUCCESS) return status; + } + } + return SPV_SUCCESS; +} + +// Checks that the instruction can be used in this target environment's base +// version. Assumes that CapabilityCheck has checked direct capability +// dependencies for the opcode. +spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { + const auto opcode = inst->opcode(); + spv_opcode_desc inst_desc; + const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc); + assert(r == SPV_SUCCESS); + (void)r; + + const auto min_version = inst_desc->minVersion; + const auto last_version = inst_desc->lastVersion; + const auto module_version = _.version(); + + if (last_version < module_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(last_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier"; + } + + // OpTerminateInvocation is special because it is enabled by Shader + // capability, but also requires an extension and/or version check. + const bool capability_check_is_sufficient = + inst->opcode() != spv::Op::OpTerminateInvocation; + + if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) { + // We already checked that the direct capability dependency has been + // satisfied. We don't need to check any further. + return SPV_SUCCESS; + } + + ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); + if (exts.empty()) { + // If no extensions can enable this instruction, then emit error + // messages only concerning core SPIR-V versions if errors happen. + if (min_version == ~0u) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " is reserved for future use."; + } + + if (module_version < min_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum."; + } + } else if (!_.HasAnyOfExtensions(exts)) { + // Otherwise, we only error out when no enabling extensions are + // registered. + if (min_version == ~0u) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << spvOpcodeString(opcode) + << " requires one of the following extensions: " + << ExtensionSetToString(exts); + } + + if (module_version < min_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) + << " at minimum or one of the following extensions: " + << ExtensionSetToString(exts); + } + } + + return SPV_SUCCESS; +} + +// Checks that the Resuld is within the valid bound. +spv_result_t LimitCheckIdBound(ValidationState_t& _, const Instruction* inst) { + if (inst->id() >= _.getIdBound()) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Result '" << inst->id() + << "' must be less than the ID bound '" << _.getIdBound() << "'."; + } + return SPV_SUCCESS; +} + +// Checks that the number of OpTypeStruct members is within the limit. +spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) { + if (spv::Op::OpTypeStruct != inst->opcode()) { + return SPV_SUCCESS; + } + + // Number of members is the number of operands of the instruction minus 1. + // One operand is the result ID. + const uint16_t limit = + static_cast(_.options()->universal_limits_.max_struct_members); + if (inst->operands().size() - 1 > limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Number of OpTypeStruct members (" << inst->operands().size() - 1 + << ") has exceeded the limit (" << limit << ")."; + } + + // Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth" + // must be less than or equal to 255. + // This is interpreted as structures including other structures as + // members. The code does not follow pointers or look into arrays to see + // if we reach a structure downstream. The nesting depth of a struct is + // 1+(largest depth of any member). Scalars are at depth 0. + uint32_t max_member_depth = 0; + // Struct members start at word 2 of OpTypeStruct instruction. + for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { + auto member = inst->word(word_i); + auto memberTypeInstr = _.FindDef(member); + if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) { + max_member_depth = std::max( + max_member_depth, _.struct_nesting_depth(memberTypeInstr->id())); + } + } + + const uint32_t depth_limit = _.options()->universal_limits_.max_struct_depth; + const uint32_t cur_depth = 1 + max_member_depth; + _.set_struct_nesting_depth(inst->id(), cur_depth); + if (cur_depth > depth_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Structure Nesting Depth may not be larger than " << depth_limit + << ". Found " << cur_depth << "."; + } + return SPV_SUCCESS; +} + +// Checks that the number of (literal, label) pairs in OpSwitch is within +// the limit. +spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { + if (spv::Op::OpSwitch == inst->opcode()) { + // The instruction syntax is as follows: + // OpSwitch literal label literal label ... + // literal,label pairs come after the first 2 operands. + // It is guaranteed at this point that num_operands is an even number. + size_t num_pairs = (inst->operands().size() - 2) / 2; + const unsigned int num_pairs_limit = + _.options()->universal_limits_.max_switch_branches; + if (num_pairs > num_pairs_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Number of (literal, label) pairs in OpSwitch (" << num_pairs + << ") exceeds the limit (" << num_pairs_limit << ")."; + } + } + return SPV_SUCCESS; +} + +// Ensure the number of variables of the given class does not exceed the +// limit. +spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id, + const spv::StorageClass storage_class) { + if (spv::StorageClass::Function == storage_class) { + _.registerLocalVariable(var_id); + const uint32_t num_local_vars_limit = + _.options()->universal_limits_.max_local_variables; + if (_.num_local_vars() > num_local_vars_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "Number of local variables ('Function' Storage Class) " + "exceeded the valid limit (" + << num_local_vars_limit << ")."; + } + } else { + _.registerGlobalVariable(var_id); + const uint32_t num_global_vars_limit = + _.options()->universal_limits_.max_global_variables; + if (_.num_global_vars() > num_global_vars_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "Number of Global Variables (Storage Class other than " + "'Function') exceeded the valid limit (" + << num_global_vars_limit << ")."; + } + } + return SPV_SUCCESS; +} + +// Parses OpExtension instruction and logs warnings if unsuccessful. +spv_result_t CheckIfKnownExtension(ValidationState_t& _, + const Instruction* inst) { + const std::string extension_str = GetExtensionString(&(inst->c_inst())); + Extension extension; + if (!GetExtensionFromString(extension_str.c_str(), &extension)) { + return _.diag(SPV_WARNING, inst) + << "Found unrecognized extension " << extension_str; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + if (opcode == spv::Op::OpExtension) { + CheckIfKnownExtension(_, inst); + } else if (opcode == spv::Op::OpCapability) { + _.RegisterCapability(inst->GetOperandAs(0)); + } else if (opcode == spv::Op::OpMemoryModel) { + if (_.has_memory_model_specified()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "OpMemoryModel should only be provided once."; + } + _.set_addressing_model(inst->GetOperandAs(0)); + _.set_memory_model(inst->GetOperandAs(1)); + } else if (opcode == spv::Op::OpExecutionMode || + opcode == spv::Op::OpExecutionModeId) { + const uint32_t entry_point = inst->word(1); + _.RegisterExecutionModeForEntryPoint(entry_point, + spv::ExecutionMode(inst->word(2))); + if (inst->GetOperandAs(1) == + spv::ExecutionMode::LocalSize || + inst->GetOperandAs(1) == + spv::ExecutionMode::LocalSizeId) { + _.RegisterEntryPointLocalSize(entry_point, inst); + } + if (inst->GetOperandAs(1) == + spv::ExecutionMode::OutputPrimitivesEXT) { + _.RegisterEntryPointOutputPrimitivesEXT(entry_point, inst); + } + } else if (opcode == spv::Op::OpVariable) { + const auto storage_class = inst->GetOperandAs(2); + if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) { + return error; + } + } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) { + if (!_.HasCapability(spv::Capability::BindlessTextureNV)) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << "OpSamplerImageAddressingModeNV supported only with extension " + "SPV_NV_bindless_texture"; + } + uint32_t bitwidth = inst->GetOperandAs(0); + if (_.samplerimage_variable_address_mode() != 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "OpSamplerImageAddressingModeNV should only be provided once"; + } + if (bitwidth != 32 && bitwidth != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32"; + } + _.set_samplerimage_variable_address_mode(bitwidth); + } + + if (auto error = ReservedCheck(_, inst)) return error; + if (auto error = CapabilityCheck(_, inst)) return error; + if (auto error = LimitCheckIdBound(_, inst)) return error; + if (auto error = LimitCheckStruct(_, inst)) return error; + if (auto error = LimitCheckSwitch(_, inst)) return error; + if (auto error = VersionCheck(_, inst)) return error; + + // All instruction checks have passed. + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_interfaces.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_interfaces.cpp new file mode 100644 index 00000000..e01a08e9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_interfaces.cpp @@ -0,0 +1,704 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Limit the number of checked locations to 4096. Multiplied by 4 to represent +// all the components. This limit is set to be well beyond practical use cases. +const uint32_t kMaxLocations = 4096 * 4; + +// Returns true if \c inst is an input or output variable. +bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) { + if (is_spv_1_4) { + // Starting in SPIR-V 1.4, all global variables are interface variables. + return (inst->opcode() == spv::Op::OpVariable || + inst->opcode() == spv::Op::OpUntypedVariableKHR) && + inst->GetOperandAs(2u) != + spv::StorageClass::Function; + } else { + return (inst->opcode() == spv::Op::OpVariable || + inst->opcode() == spv::Op::OpUntypedVariableKHR) && + (inst->GetOperandAs(2u) == + spv::StorageClass::Input || + inst->GetOperandAs(2u) == + spv::StorageClass::Output); + } +} + +// Special validation for varibles that are between shader stages +spv_result_t ValidateInputOutputInterfaceVariables(ValidationState_t& _, + const Instruction* var) { + auto var_pointer = _.FindDef(var->GetOperandAs(0)); + uint32_t pointer_id = var_pointer->GetOperandAs(2); + + const auto isPhysicalStorageBuffer = [](const Instruction* insn) { + return insn->opcode() == spv::Op::OpTypePointer && + insn->GetOperandAs(1) == + spv::StorageClass::PhysicalStorageBuffer; + }; + + if (_.ContainsType(pointer_id, isPhysicalStorageBuffer)) { + return _.diag(SPV_ERROR_INVALID_ID, var) + << _.VkErrorID(9557) << "Input/Output interface variable id <" + << var->id() + << "> contains a PhysicalStorageBuffer pointer, which is not " + "allowed. If you want to interface shader stages with a " + "PhysicalStorageBuffer, cast to a uint64 or uvec2 instead."; + } + return SPV_SUCCESS; +} + +// Checks that \c var is listed as an interface in all the entry points that use +// it. +spv_result_t check_interface_variable(ValidationState_t& _, + const Instruction* var) { + std::vector functions; + std::vector uses; + for (auto use : var->uses()) { + uses.push_back(use.first); + } + for (uint32_t i = 0; i < uses.size(); ++i) { + const auto user = uses[i]; + if (const Function* func = user->function()) { + functions.push_back(func); + } else { + // In the rare case that the variable is used by another instruction in + // the global scope, continue searching for an instruction used in a + // function. + for (auto use : user->uses()) { + uses.push_back(use.first); + } + } + } + + std::sort(functions.begin(), functions.end(), + [](const Function* lhs, const Function* rhs) { + return lhs->id() < rhs->id(); + }); + functions.erase(std::unique(functions.begin(), functions.end()), + functions.end()); + + std::vector entry_points; + for (const auto func : functions) { + for (auto id : _.FunctionEntryPoints(func->id())) { + entry_points.push_back(id); + } + } + + std::sort(entry_points.begin(), entry_points.end()); + entry_points.erase(std::unique(entry_points.begin(), entry_points.end()), + entry_points.end()); + + for (auto id : entry_points) { + for (const auto& desc : _.entry_point_descriptions(id)) { + bool found = false; + for (auto interface : desc.interfaces) { + if (var->id() == interface) { + found = true; + break; + } + } + if (!found) { + return _.diag(SPV_ERROR_INVALID_ID, var) + << "Interface variable id <" << var->id() + << "> is used by entry point '" << desc.name << "' id <" << id + << ">, but is not listed as an interface"; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (var->GetOperandAs(2) == spv::StorageClass::Input || + var->GetOperandAs(2) == spv::StorageClass::Output) { + if (auto error = ValidateInputOutputInterfaceVariables(_, var)) + return error; + } + } + + return SPV_SUCCESS; +} + +// This function assumes a base location has been determined already. As such +// any further location decorations are invalid. +// TODO: if this code turns out to be slow, there is an opportunity to cache +// the result for a given type id. +spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type, + uint32_t* num_locations) { + *num_locations = 0; + switch (type->opcode()) { + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + // Scalars always consume a single location. + *num_locations = 1; + break; + case spv::Op::OpTypeVector: + // 3- and 4-component 64-bit vectors consume two locations. + if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) || + _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat, + 64)) && + (type->GetOperandAs(2) > 2)) { + *num_locations = 2; + } else { + *num_locations = 1; + } + break; + case spv::Op::OpTypeMatrix: + // Matrices consume locations equivalent to arrays. + if (auto error = NumConsumedLocations( + _, _.FindDef(type->GetOperandAs(1)), num_locations)) { + return error; + } + *num_locations *= type->GetOperandAs(2); + break; + case spv::Op::OpTypeArray: { + // Arrays consume locations equal to the underlying type times the number + // of elements in the vector. + if (auto error = NumConsumedLocations( + _, _.FindDef(type->GetOperandAs(1)), num_locations)) { + return error; + } + bool is_int = false; + bool is_const = false; + uint32_t value = 0; + // Attempt to evaluate the number of array elements. + std::tie(is_int, is_const, value) = + _.EvalInt32IfConst(type->GetOperandAs(2)); + if (is_int && is_const) *num_locations *= value; + break; + } + case spv::Op::OpTypeStruct: { + // Members cannot have location decorations at this point. + if (_.HasDecoration(type->id(), spv::Decoration::Location)) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << _.VkErrorID(4918) << "Members cannot be assigned a location"; + } + + // Structs consume locations equal to the sum of the locations consumed + // by the members. + for (uint32_t i = 1; i < type->operands().size(); ++i) { + uint32_t member_locations = 0; + if (auto error = NumConsumedLocations( + _, _.FindDef(type->GetOperandAs(i)), + &member_locations)) { + return error; + } + *num_locations += member_locations; + } + break; + } + case spv::Op::OpTypePointer: { + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64 && + type->GetOperandAs(1) == + spv::StorageClass::PhysicalStorageBuffer) { + *num_locations = 1; + break; + } + [[fallthrough]]; + } + default: + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Invalid type to assign a location"; + } + + return SPV_SUCCESS; +} + +// Returns the number of components consumed by types that support a component +// decoration. +uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { + uint32_t num_components = 0; + switch (type->opcode()) { + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: + // 64-bit types consume two components. + if (type->GetOperandAs(1) == 64) { + num_components = 2; + } else { + num_components = 1; + } + break; + case spv::Op::OpTypeVector: + // Vectors consume components equal to the underlying type's consumption + // times the number of elements in the vector. Note that 3- and 4-element + // vectors cannot have a component decoration (i.e. assumed to be zero). + num_components = + NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); + num_components *= type->GetOperandAs(2); + break; + case spv::Op::OpTypeMatrix: + // Matrices consume all components of the location. + // Round up to next multiple of 4. + num_components = + NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); + num_components *= type->GetOperandAs(2); + num_components = ((num_components + 3) / 4) * 4; + break; + case spv::Op::OpTypeArray: { + // Arrays consume all components of the location. + // Round up to next multiple of 4. + num_components = + NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); + + bool is_int = false; + bool is_const = false; + uint32_t value = 0; + // Attempt to evaluate the number of array elements. + std::tie(is_int, is_const, value) = + _.EvalInt32IfConst(type->GetOperandAs(2)); + if (is_int && is_const) num_components *= value; + + num_components = ((num_components + 3) / 4) * 4; + return num_components; + } + case spv::Op::OpTypePointer: + if (_.addressing_model() == + spv::AddressingModel::PhysicalStorageBuffer64 && + type->GetOperandAs(1) == + spv::StorageClass::PhysicalStorageBuffer) { + return 2; + } + break; + default: + // This is an error that is validated elsewhere. + break; + } + + return num_components; +} + +// Populates |locations| (and/or |output_index1_locations|) with the use +// location and component coordinates for |variable|. Indices are calculated as +// 4 * location + component. +spv_result_t GetLocationsForVariable( + ValidationState_t& _, const Instruction* entry_point, + const Instruction* variable, std::unordered_set* locations, + std::unordered_set* output_index1_locations) { + const bool is_fragment = entry_point->GetOperandAs(0) == + spv::ExecutionModel::Fragment; + const auto sc_index = 2u; + const bool is_output = variable->GetOperandAs(sc_index) == + spv::StorageClass::Output; + auto ptr_type_id = variable->GetOperandAs(0); + auto ptr_type = _.FindDef(ptr_type_id); + auto type_id = ptr_type->GetOperandAs(2); + auto type = _.FindDef(type_id); + + // Check for Location, Component and Index decorations on the variable. The + // validator allows duplicate decorations if the location/component/index are + // equal. Also track Patch and PerTaskNV decorations. + bool has_location = false; + uint32_t location = 0; + uint32_t component = 0; + bool has_index = false; + uint32_t index = 0; + bool has_patch = false; + bool has_per_task_nv = false; + bool has_per_vertex_khr = false; + // Duplicate Location, Component, Index are checked elsewhere. + for (auto& dec : _.id_decorations(variable->id())) { + if (dec.dec_type() == spv::Decoration::Location) { + has_location = true; + location = dec.params()[0]; + } else if (dec.dec_type() == spv::Decoration::Component) { + component = dec.params()[0]; + } else if (dec.dec_type() == spv::Decoration::Index) { + if (!is_output || !is_fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Index can only be applied to Fragment output variables"; + } + has_index = true; + index = dec.params()[0]; + } else if (dec.dec_type() == spv::Decoration::BuiltIn) { + // Don't check built-ins. + return SPV_SUCCESS; + } else if (dec.dec_type() == spv::Decoration::Patch) { + has_patch = true; + } else if (dec.dec_type() == spv::Decoration::PerTaskNV) { + has_per_task_nv = true; + } else if (dec.dec_type() == spv::Decoration::PerVertexKHR) { + if (!is_fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6777) + << "PerVertexKHR can only be applied to Fragment Execution " + "Models"; + } + if (type->opcode() != spv::Op::OpTypeArray && + type->opcode() != spv::Op::OpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(6778) + << "PerVertexKHR must be declared as arrays"; + } + has_per_vertex_khr = true; + } + } + + // Vulkan 15.1.3 (Interface Matching): Tessellation control and mesh + // per-vertex outputs and tessellation control, evaluation and geometry + // per-vertex inputs have a layer of arraying that is not included in + // interface matching. + bool is_arrayed = false; + switch (entry_point->GetOperandAs(0)) { + case spv::ExecutionModel::TessellationControl: + if (!has_patch) { + is_arrayed = true; + } + break; + case spv::ExecutionModel::TessellationEvaluation: + if (!is_output && !has_patch) { + is_arrayed = true; + } + break; + case spv::ExecutionModel::Geometry: + if (!is_output) { + is_arrayed = true; + } + break; + case spv::ExecutionModel::Fragment: + if (!is_output && has_per_vertex_khr) { + is_arrayed = true; + } + break; + case spv::ExecutionModel::MeshNV: + if (is_output && !has_per_task_nv) { + is_arrayed = true; + } + break; + default: + break; + } + + // Unpack arrayness. + if (is_arrayed && (type->opcode() == spv::Op::OpTypeArray || + type->opcode() == spv::Op::OpTypeRuntimeArray)) { + type_id = type->GetOperandAs(1); + type = _.FindDef(type_id); + } + + if (type->opcode() == spv::Op::OpTypeStruct) { + // Don't check built-ins. + if (_.HasDecoration(type_id, spv::Decoration::BuiltIn)) return SPV_SUCCESS; + } + + // Only block-decorated structs don't need a location on the variable. + const bool is_block = _.HasDecoration(type_id, spv::Decoration::Block); + if (!has_location && !is_block) { + const auto vuid = (type->opcode() == spv::Op::OpTypeStruct) ? 4917 : 4916; + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(vuid) << "Variable must be decorated with a location"; + } + + const std::string storage_class = is_output ? "output" : "input"; + if (has_location) { + uint32_t num_locations = 0; + if (auto error = NumConsumedLocations(_, type, &num_locations)) + return error; + uint32_t num_components = NumConsumedComponents(_, type); + + uint32_t start = location * 4; + uint32_t end = (location + num_locations) * 4; + if (num_components % 4 != 0) { + start += component; + end = start + num_components; + } + + if (kMaxLocations <= start) { + // Too many locations, give up. + return SPV_SUCCESS; + } + + auto locs = locations; + if (has_index && index == 1) locs = output_index1_locations; + + for (uint32_t i = start; i < end; ++i) { + if (!locs->insert(i).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721)) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << i / 4 << ", component " + << i % 4; + } + } + } else { + // For Block-decorated structs with no location assigned to the variable, + // each member of the block must be assigned a location. Also record any + // member component assignments. The validator allows duplicate decorations + // if they agree on the location/component. + std::unordered_map member_locations; + std::unordered_map member_components; + for (auto& dec : _.id_decorations(type_id)) { + if (dec.dec_type() == spv::Decoration::Location) { + auto where = member_locations.find(dec.struct_member_index()); + if (where == member_locations.end()) { + member_locations[dec.struct_member_index()] = dec.params()[0]; + } else if (where->second != dec.params()[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Member index " << dec.struct_member_index() + << " has conflicting location assignments"; + } + } else if (dec.dec_type() == spv::Decoration::Component) { + auto where = member_components.find(dec.struct_member_index()); + if (where == member_components.end()) { + member_components[dec.struct_member_index()] = dec.params()[0]; + } else if (where->second != dec.params()[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Member index " << dec.struct_member_index() + << " has conflicting component assignments"; + } + } + } + + for (uint32_t i = 1; i < type->operands().size(); ++i) { + auto where = member_locations.find(i - 1); + if (where == member_locations.end()) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << _.VkErrorID(4919) << "Member index " << i - 1 + << " is missing a location assignment"; + } + + location = where->second; + auto member = _.FindDef(type->GetOperandAs(i)); + uint32_t num_locations = 0; + if (auto error = NumConsumedLocations(_, member, &num_locations)) + return error; + + // If the component is not specified, it is assumed to be zero. + uint32_t num_components = NumConsumedComponents(_, member); + component = 0; + if (member_components.count(i - 1)) { + component = member_components[i - 1]; + } + + uint32_t start = location * 4; + if (kMaxLocations <= start) { + // Too many locations, give up. + continue; + } + + uint32_t end = (location + num_locations) * 4; + if (num_components % 4 != 0) { + start += component; + end = location * 4 + component + num_components; + } + + for (uint32_t l = start; l < end; ++l) { + if (!locations->insert(l).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721)) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l / 4 + << ", component " << l % 4; + } + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLocations(ValidationState_t& _, + const Instruction* entry_point) { + // According to Vulkan 14.1 only the following execution models have + // locations assigned. + // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables, + // in other shader stages. Similarly, the *provisional* version of + // SPV_KHR_ray_tracing did as well, but not the final version. + switch (entry_point->GetOperandAs(0)) { + case spv::ExecutionModel::Vertex: + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::Fragment: + break; + default: + return SPV_SUCCESS; + } + + // Locations are stored as a combined location and component values. + std::unordered_set input_locations; + std::unordered_set output_locations_index0; + std::unordered_set output_locations_index1; + std::unordered_set patch_locations_index0; + std::unordered_set patch_locations_index1; + std::unordered_set seen; + for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { + auto interface_id = entry_point->GetOperandAs(i); + auto interface_var = _.FindDef(interface_id); + const auto sc_index = 2u; + auto storage_class = + interface_var->GetOperandAs(sc_index); + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + continue; + } + if (!seen.insert(interface_id).second) { + // Pre-1.4 an interface variable could be listed multiple times in an + // entry point. Validation for 1.4 or later is done elsewhere. + continue; + } + + // The two Tessellation stages have a "Patch" variable that interface with + // the Location mechanism, but are not suppose to be tied to the "normal" + // input/output Location. + // TODO - SPIR-V allows the Patch decoration to be applied to struct + // members, but is not allowed in GLSL/HLSL + bool has_patch = false; + for (auto& dec : _.id_decorations(interface_var->id())) { + if (dec.dec_type() == spv::Decoration::Patch) { + has_patch = true; + if (auto error = GetLocationsForVariable(_, entry_point, interface_var, + &patch_locations_index0, + &patch_locations_index1)) + return error; + break; + } + } + if (has_patch) { + continue; + } + + auto locations = (storage_class == spv::StorageClass::Input) + ? &input_locations + : &output_locations_index0; + if (auto error = GetLocationsForVariable( + _, entry_point, interface_var, locations, &output_locations_index1)) + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateStorageClass(ValidationState_t& _, + const Instruction* entry_point) { + bool has_push_constant = false; + bool has_ray_payload = false; + bool has_hit_attribute = false; + bool has_callable_data = false; + for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { + auto interface_id = entry_point->GetOperandAs(i); + auto interface_var = _.FindDef(interface_id); + auto storage_class = interface_var->GetOperandAs(2); + switch (storage_class) { + case spv::StorageClass::PushConstant: { + if (has_push_constant) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(6673) + << "Entry-point has more than one variable with the " + "PushConstant storage class in the interface"; + } + has_push_constant = true; + break; + } + case spv::StorageClass::IncomingRayPayloadKHR: { + if (has_ray_payload) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4700) + << "Entry-point has more than one variable with the " + "IncomingRayPayloadKHR storage class in the interface"; + } + has_ray_payload = true; + break; + } + case spv::StorageClass::HitAttributeKHR: { + if (has_hit_attribute) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4702) + << "Entry-point has more than one variable with the " + "HitAttributeKHR storage class in the interface"; + } + has_hit_attribute = true; + break; + } + case spv::StorageClass::IncomingCallableDataKHR: { + if (has_callable_data) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << _.VkErrorID(4706) + << "Entry-point has more than one variable with the " + "IncomingCallableDataKHR storage class in the interface"; + } + has_callable_data = true; + break; + } + case spv::StorageClass::Input: + case spv::StorageClass::Output: { + auto result_type = _.FindDef(interface_var->type_id()); + if (_.ContainsType(result_type->GetOperandAs(2), + [](const Instruction* inst) { + if (inst && + inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + if (inst->GetOperandAs(2) == + spv::FPEncoding::BFloat16KHR) { + return true; + } + } + } + return false; + })) { + return _.diag(SPV_ERROR_INVALID_ID, interface_var) + << _.VkErrorID(10370) << "Bfloat16 OpVariable " + << _.getIdName(interface_var->id()) << " must not be declared " + << "with a Storage Class of Input or Output."; + } + } + default: + break; + } + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateInterfaces(ValidationState_t& _) { + bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4); + for (auto& inst : _.ordered_instructions()) { + if (is_interface_variable(&inst, is_spv_1_4)) { + if (auto error = check_interface_variable(_, &inst)) { + return error; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + for (auto& inst : _.ordered_instructions()) { + if (inst.opcode() == spv::Op::OpEntryPoint) { + if (auto error = ValidateLocations(_, &inst)) { + return error; + } + if (auto error = ValidateStorageClass(_, &inst)) { + return error; + } + } + if (inst.opcode() == spv::Op::OpTypeVoid) break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_invalid_type.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_invalid_type.cpp new file mode 100644 index 00000000..a9dcd29c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_invalid_type.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 2025 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Performs validation of invalid type instructions. + +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of certain special type instructions. +spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (opcode) { + // OpExtInst + case spv::Op::OpExtInst: + // Arithmetic Instructions + case spv::Op::OpFAdd: + case spv::Op::OpFSub: + case spv::Op::OpFMul: + case spv::Op::OpFDiv: + case spv::Op::OpFRem: + case spv::Op::OpFMod: + case spv::Op::OpFNegate: + // Derivative Instructions + case spv::Op::OpDPdx: + case spv::Op::OpDPdy: + case spv::Op::OpFwidth: + case spv::Op::OpDPdxFine: + case spv::Op::OpDPdyFine: + case spv::Op::OpFwidthFine: + case spv::Op::OpDPdxCoarse: + case spv::Op::OpDPdyCoarse: + case spv::Op::OpFwidthCoarse: + // Atomic Instructions + case spv::Op::OpAtomicFAddEXT: + case spv::Op::OpAtomicFMinEXT: + case spv::Op::OpAtomicFMaxEXT: + case spv::Op::OpAtomicLoad: + case spv::Op::OpAtomicExchange: + // Group and Subgroup Instructions + case spv::Op::OpGroupNonUniformRotateKHR: + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformShuffle: + case spv::Op::OpGroupNonUniformShuffleXor: + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + case spv::Op::OpGroupNonUniformQuadBroadcast: + case spv::Op::OpGroupNonUniformQuadSwap: + case spv::Op::OpGroupNonUniformBroadcastFirst: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformFMin: { + const uint32_t result_type = inst->type_id(); + if (_.IsBfloat16ScalarType(result_type) || + _.IsBfloat16VectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + break; + } + + case spv::Op::OpAtomicStore: { + uint32_t data_type = + _.FindDef(inst->GetOperandAs(3))->type_id(); + if (_.IsBfloat16VectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + break; + } + // Relational and Logical Instructions + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: { + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (_.IsBfloat16ScalarType(operand_type) || + _.IsBfloat16VectorType(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + break; + } + + case spv::Op::OpGroupNonUniformAllEqual: { + const auto value_type = _.GetOperandTypeId(inst, 3); + if (_.IsBfloat16ScalarType(value_type) || + _.IsBfloat16VectorType(value_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << " doesn't support BFloat16 type."; + } + break; + } + + case spv::Op::OpMatrixTimesMatrix: { + const uint32_t result_type = inst->type_id(); + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) { + if (_.IsBfloat16ScalarType(res_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << " doesn't support BFloat16 type."; + } + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_layout.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_layout.cpp new file mode 100644 index 00000000..baf36b89 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_layout.cpp @@ -0,0 +1,387 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Source code for logical layout validation as described in section 2.4 + +#include "DebugInfo.h" +#include "NonSemanticShaderDebugInfo100.h" +#include "OpenCLDebugInfo100.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Module scoped instructions are processed by determining if the opcode +// is part of the current layout section. If it is not then the next sections is +// checked. +spv_result_t ModuleScopedInstructions(ValidationState_t& _, + const Instruction* inst, spv::Op opcode) { + switch (opcode) { + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + const uint32_t ext_inst_index = inst->word(4); + bool local_debug_info = false; + if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope || + ext_inst_key == OpenCLDebugInfo100DebugNoScope || + ext_inst_key == OpenCLDebugInfo100DebugDeclare || + ext_inst_key == OpenCLDebugInfo100DebugValue) { + local_debug_info = true; + } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope || + ext_inst_key == DebugInfoDebugNoScope || + ext_inst_key == DebugInfoDebugDeclare || + ext_inst_key == DebugInfoDebugValue) { + local_debug_info = true; + } + } + + if (local_debug_info) { + if (_.in_function_body() == false) { + // TODO - Print the actual name of the instruction as this list is + // not complete (see ext_inst_name in ValidateExtInst() for example) + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "of debug info extension must appear in a function " + << "body"; + } + } else { + // Debug info extinst opcodes other than DebugScope, DebugNoScope, + // DebugDeclare, DebugValue must be placed between section 9 (types, + // constants, global variables) and section 10 (function + // declarations). + if (_.current_layout_section() < kLayoutTypes || + _.current_layout_section() >= kLayoutFunctionDeclarations) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Debug info extension instructions other than " + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "must appear between section 9 (types, constants, " + << "global variables) and section 10 (function " + << "declarations)"; + } + } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but since they must name a return type they cannot be the + // first instruction in the types section. Therefore check that we are + // already in it. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } + } else { + // otherwise they must be used in a block + if (_.current_layout_section() < kLayoutFunctionDefinitions) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + default: + break; + } + + while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { + if (_.IsOpcodeInPreviousLayoutSection(opcode)) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " is in an invalid layout section"; + } + + _.ProgressToNextLayoutSectionOrder(); + + switch (_.current_layout_section()) { + case kLayoutMemoryModel: + if (opcode != spv::Op::OpMemoryModel) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear before the memory model instruction"; + } + break; + case kLayoutFunctionDeclarations: + // All module sections have been processed. Recursively call + // ModuleLayoutPass to process the next section of the module + return ModuleLayoutPass(_, inst); + default: + break; + } + } + return SPV_SUCCESS; +} + +// Function declaration validation is performed by making sure that the +// FunctionParameter and FunctionEnd instructions only appear inside of +// functions. It also ensures that the Function instruction does not appear +// inside of another function. This stage ends when the first label is +// encountered inside of a function. +spv_result_t FunctionScopedInstructions(ValidationState_t& _, + const Instruction* inst, + spv::Op opcode) { + // Make sure we advance into the function definitions when we hit + // non-function declaration instructions. + if (_.current_layout_section() == kLayoutFunctionDeclarations && + !_.IsOpcodeInCurrentLayoutSection(opcode)) { + _.ProgressToNextLayoutSectionOrder(); + + if (_.in_function_body()) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDefinition)) { + return error; + } + } + } + + if (_.IsOpcodeInCurrentLayoutSection(opcode)) { + switch (opcode) { + case spv::Op::OpFunction: { + if (_.in_function_body()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Cannot declare a function in a function body"; + } + auto control_mask = inst->GetOperandAs(2); + if (auto error = + _.RegisterFunction(inst->id(), inst->type_id(), control_mask, + inst->GetOperandAs(3))) + return error; + if (_.current_layout_section() == kLayoutFunctionDefinitions) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDefinition)) + return error; + } + } break; + + case spv::Op::OpFunctionParameter: + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter instructions must be in a " + "function body"; + } + if (_.current_function().block_count() != 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameters must only appear immediately after " + "the function definition"; + } + if (auto error = _.current_function().RegisterFunctionParameter( + inst->id(), inst->type_id())) + return error; + break; + + case spv::Op::OpFunctionEnd: + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function end instructions must be in a function body"; + } + if (_.in_block()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function end cannot be called in blocks"; + } + if (_.current_function().block_count() == 0 && + _.current_layout_section() == kLayoutFunctionDefinitions) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function declarations must appear before " + "function definitions."; + } + if (_.current_layout_section() == kLayoutFunctionDeclarations) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDeclaration)) + return error; + } + if (auto error = _.RegisterFunctionEnd()) return error; + break; + + case spv::Op::OpLine: + case spv::Op::OpNoLine: + break; + case spv::Op::OpLabel: + // If the label is encountered then the current function is a + // definition so set the function to a declaration and update the + // module section + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Label instructions must be in a function body"; + } + if (_.in_block()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "A block must end with a branch instruction."; + } + break; + + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + const uint32_t ext_inst_index = inst->word(4); + bool local_debug_info = false; + if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope || + ext_inst_key == OpenCLDebugInfo100DebugNoScope || + ext_inst_key == OpenCLDebugInfo100DebugDeclare || + ext_inst_key == OpenCLDebugInfo100DebugValue) { + local_debug_info = true; + } + } else if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope || + ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare || + ext_inst_key == NonSemanticShaderDebugInfo100DebugValue || + ext_inst_key == NonSemanticShaderDebugInfo100DebugLine || + ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine || + ext_inst_key == + NonSemanticShaderDebugInfo100DebugFunctionDefinition) { + local_debug_info = true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope || + ext_inst_key == DebugInfoDebugNoScope || + ext_inst_key == DebugInfoDebugDeclare || + ext_inst_key == DebugInfoDebugValue) { + local_debug_info = true; + } + } + + if (local_debug_info) { + if (_.in_function_body() == false) { + // DebugScope, DebugNoScope, DebugDeclare, DebugValue must + // appear in a function body. + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "of debug info extension must appear in a function " + << "body"; + } + } else { + // Debug info extinst opcodes other than DebugScope, DebugNoScope, + // DebugDeclare, DebugValue must be placed between section 9 (types, + // constants, global variables) and section 10 (function + // declarations). + if (_.current_layout_section() < kLayoutTypes || + _.current_layout_section() >= kLayoutFunctionDeclarations) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Debug info extension instructions other than " + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "must appear between section 9 (types, constants, " + << "global variables) and section 10 (function " + << "declarations)"; + } + } + } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but must either be placed outside a function declaration, + // or inside a block. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } else if (_.in_function_body() && _.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst within function definition must " + "appear in a block"; + } + } else { + // otherwise they must be used in a block + if (_.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + + default: + if (_.current_layout_section() == kLayoutFunctionDeclarations && + _.in_function_body()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "A function must begin with a label"; + } else { + if (_.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + } + } else { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear in a function declaration"; + } + return SPV_SUCCESS; +} + +} // namespace + +// TODO(umar): Check linkage capabilities for function declarations +// TODO(umar): Better error messages +// NOTE: This function does not handle CFG related validation +// Performs logical layout validation. See Section 2.4 +spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (_.current_layout_section()) { + case kLayoutCapabilities: + case kLayoutExtensions: + case kLayoutExtInstImport: + case kLayoutMemoryModel: + case kLayoutSamplerImageAddressMode: + case kLayoutEntryPoint: + case kLayoutExecutionMode: + case kLayoutDebug1: + case kLayoutDebug2: + case kLayoutDebug3: + case kLayoutAnnotations: + case kLayoutTypes: + if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; + break; + case kLayoutFunctionDeclarations: + case kLayoutFunctionDefinitions: + if (auto error = FunctionScopedInstructions(_, inst, opcode)) { + return error; + } + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_literals.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_literals.cpp new file mode 100644 index 00000000..15cc27a9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_literals.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates literal numbers. + +#include + +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if the operand holds a literal number +bool IsLiteralNumber(const spv_parsed_operand_t& operand) { + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + case SPV_NUMBER_UNSIGNED_INT: + case SPV_NUMBER_FLOATING: + return true; + default: + return false; + } +} + +// Verifies that the upper bits of the given upper |word| with given +// lower |width| are zero- or sign-extended when |signed_int| is true +bool VerifyUpperBits(uint32_t word, uint32_t width, bool signed_int) { + assert(width < 32); + assert(0 < width); + const uint32_t upper_mask = 0xFFFFFFFFu << width; + const uint32_t upper_bits = word & upper_mask; + + bool result = false; + if (signed_int) { + const uint32_t sign_bit = word & (1u << (width - 1)); + if (sign_bit) { + result = upper_bits == upper_mask; + } else { + result = upper_bits == 0; + } + } else { + result = upper_bits == 0; + } + return result; +} + +} // namespace + +// Validates that literal numbers are represented according to the spec +spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst) { + // For every operand that is a literal number + for (size_t i = 0; i < inst->operands().size(); i++) { + const spv_parsed_operand_t& operand = inst->operand(i); + if (!IsLiteralNumber(operand)) continue; + + // The upper bits are always in the last word (little-endian) + int last_index = operand.offset + operand.num_words - 1; + const uint32_t upper_word = inst->word(last_index); + + // TODO(jcaraban): is the |word size| defined in some header? + const uint32_t word_size = 32; + uint32_t bit_width = operand.number_bit_width; + + // Bit widths that are a multiple of the word size have no upper bits + const auto remaining_value_bits = bit_width % word_size; + if (remaining_value_bits == 0) continue; + + const bool signedness = operand.number_kind == SPV_NUMBER_SIGNED_INT; + + if (!VerifyUpperBits(upper_word, remaining_value_bits, signedness)) { + return _.diag(SPV_ERROR_INVALID_VALUE, inst) + << "The high-order bits of a literal number in instruction " + << inst->id() << " must be 0 for a floating-point type, " + << "or 0 for an integer type with Signedness of 0, " + << "or sign extended when Signedness is 1"; + } + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_logicals.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_logicals.cpp new file mode 100644 index 00000000..8a2e5d8c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_logicals.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of logical SPIR-V instructions. + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpAny: + case spv::Op::OpAll: { + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + if (!vector_type || !_.IsBoolVectorType(vector_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be vector bool: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpIsNan: + case spv::Op::OpIsInf: + case spv::Op::OpIsFinite: + case spv::Op::OpIsNormal: + case spv::Op::OpSignBitSet: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (!operand_type || (!_.IsFloatScalarType(operand_type) && + !_.IsFloatVectorType(operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operand to be " + "equal: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpFOrdEqual: + case spv::Op::OpFUnordEqual: + case spv::Op::OpFOrdNotEqual: + case spv::Op::OpFUnordNotEqual: + case spv::Op::OpFOrdLessThan: + case spv::Op::OpFUnordLessThan: + case spv::Op::OpFOrdGreaterThan: + case spv::Op::OpFUnordGreaterThan: + case spv::Op::OpFOrdLessThanEqual: + case spv::Op::OpFUnordLessThanEqual: + case spv::Op::OpFOrdGreaterThanEqual: + case spv::Op::OpFUnordGreaterThanEqual: + case spv::Op::OpLessOrGreater: + case spv::Op::OpOrdered: + case spv::Op::OpUnordered: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2); + if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && + !_.IsFloatVectorType(left_operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be " + "equal: " + << spvOpcodeString(opcode); + + if (left_operand_type != _.GetOperandTypeId(inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected left and right operands to have the same type: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpLogicalEqual: + case spv::Op::OpLogicalNotEqual: + case spv::Op::OpLogicalOr: + case spv::Op::OpLogicalAnd: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != _.GetOperandTypeId(inst, 2) || + result_type != _.GetOperandTypeId(inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpLogicalNot: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != _.GetOperandTypeId(inst, 2)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case spv::Op::OpSelect: { + uint32_t dimension = 1; + { + const Instruction* type_inst = _.FindDef(result_type); + assert(type_inst); + + const auto composites = _.features().select_between_composites; + auto fail = [&_, composites, inst, opcode]() -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar or " + << (composites ? "composite" : "vector") + << " type as Result Type: " << spvOpcodeString(opcode); + }; + + const spv::Op type_opcode = type_inst->opcode(); + switch (type_opcode) { + case spv::Op::OpTypeUntypedPointerKHR: + case spv::Op::OpTypePointer: { + if (_.addressing_model() == spv::AddressingModel::Logical && + !_.HasCapability( + spv::Capability::VariablePointersStorageBuffer)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using pointers with OpSelect requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + break; + } + + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampler: { + if (!_.HasCapability(spv::Capability::BindlessTextureNV)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using image/sampler with OpSelect requires capability " + << "BindlessTextureNV"; + break; + } + + case spv::Op::OpTypeVector: { + dimension = type_inst->word(3); + break; + } + + case spv::Op::OpTypeBool: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeFloat: { + break; + } + + // Not RuntimeArray because of other rules. + case spv::Op::OpTypeArray: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeStruct: { + if (!composites) return fail(); + break; + } + + default: + return fail(); + } + + const uint32_t condition_type = _.GetOperandTypeId(inst, 2); + const uint32_t left_type = _.GetOperandTypeId(inst, 3); + const uint32_t right_type = _.GetOperandTypeId(inst, 4); + + if (!condition_type || (!_.IsBoolScalarType(condition_type) && + !_.IsBoolVectorType(condition_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as condition: " + << spvOpcodeString(opcode); + + if (_.GetDimension(condition_type) != dimension) { + // If the condition is a vector type, then the result must also be a + // vector with matching dimensions. In SPIR-V 1.4, a scalar condition + // can be used to select between vector types. |composites| is a + // proxy for SPIR-V 1.4 functionality. + if (!composites || _.IsBoolVectorType(condition_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the condition " + "to be equal: " + << spvOpcodeString(opcode); + } + } + + if (result_type != left_type || result_type != right_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both objects to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + } + + case spv::Op::OpIEqual: + case spv::Op::OpINotEqual: + case spv::Op::OpUGreaterThan: + case spv::Op::OpUGreaterThanEqual: + case spv::Op::OpULessThan: + case spv::Op::OpULessThanEqual: + case spv::Op::OpSGreaterThan: + case spv::Op::OpSGreaterThanEqual: + case spv::Op::OpSLessThan: + case spv::Op::OpSLessThanEqual: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_type = _.GetOperandTypeId(inst, 2); + const uint32_t right_type = _.GetOperandTypeId(inst, 3); + + if (!left_type || + (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (!right_type || + (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to have the same component bit " + "width: " + << spvOpcodeString(opcode); + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory.cpp new file mode 100644 index 00000000..86ae759c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory.cpp @@ -0,0 +1,2940 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All +// rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool AreLayoutCompatibleStructs(ValidationState_t&, const Instruction*, + const Instruction*); +bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*, + const Instruction*); +bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*, + const Instruction*); +bool HasConflictingMemberOffsets(const std::set&, + const std::set&); + +bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type, + std::initializer_list allowed) { + if (std::find(allowed.begin(), allowed.end(), type->opcode()) != + allowed.end()) { + return true; + } + if (type->opcode() == spv::Op::OpTypeArray || + type->opcode() == spv::Op::OpTypeRuntimeArray) { + auto elem_type = _.FindDef(type->word(2)); + return std::find(allowed.begin(), allowed.end(), elem_type->opcode()) != + allowed.end(); + } + return false; +} + +// Returns true if the two instructions represent structs that, as far as the +// validator can tell, have the exact same data layout. +bool AreLayoutCompatibleStructs(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + if (type1->opcode() != spv::Op::OpTypeStruct) { + return false; + } + if (type2->opcode() != spv::Op::OpTypeStruct) { + return false; + } + + if (!HaveLayoutCompatibleMembers(_, type1, type2)) return false; + + return HaveSameLayoutDecorations(_, type1, type2); +} + +// Returns true if the operands to the OpTypeStruct instruction defining the +// types are the same or are layout compatible types. |type1| and |type2| must +// be OpTypeStruct instructions. +bool HaveLayoutCompatibleMembers(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + assert(type1->opcode() == spv::Op::OpTypeStruct && + "type1 must be an OpTypeStruct instruction."); + assert(type2->opcode() == spv::Op::OpTypeStruct && + "type2 must be an OpTypeStruct instruction."); + const auto& type1_operands = type1->operands(); + const auto& type2_operands = type2->operands(); + if (type1_operands.size() != type2_operands.size()) { + return false; + } + + for (size_t operand = 2; operand < type1_operands.size(); ++operand) { + if (type1->word(operand) != type2->word(operand)) { + auto def1 = _.FindDef(type1->word(operand)); + auto def2 = _.FindDef(type2->word(operand)); + if (!AreLayoutCompatibleStructs(_, def1, def2)) { + return false; + } + } + } + return true; +} + +// Returns true if all decorations that affect the data layout of the struct +// (like Offset), are the same for the two types. |type1| and |type2| must be +// OpTypeStruct instructions. +bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + assert(type1->opcode() == spv::Op::OpTypeStruct && + "type1 must be an OpTypeStruct instruction."); + assert(type2->opcode() == spv::Op::OpTypeStruct && + "type2 must be an OpTypeStruct instruction."); + const std::set& type1_decorations = _.id_decorations(type1->id()); + const std::set& type2_decorations = _.id_decorations(type2->id()); + + // TODO: Will have to add other check for arrays an matricies if we want to + // handle them. + if (HasConflictingMemberOffsets(type1_decorations, type2_decorations)) { + return false; + } + + return true; +} + +bool HasConflictingMemberOffsets( + const std::set& type1_decorations, + const std::set& type2_decorations) { + { + // We are interested in conflicting decoration. If a decoration is in one + // list but not the other, then we will assume the code is correct. We are + // looking for things we know to be wrong. + // + // We do not have to traverse type2_decoration because, after traversing + // type1_decorations, anything new will not be found in + // type1_decoration. Therefore, it cannot lead to a conflict. + for (const Decoration& decoration : type1_decorations) { + switch (decoration.dec_type()) { + case spv::Decoration::Offset: { + // Since these affect the layout of the struct, they must be present + // in both structs. + auto compare = [&decoration](const Decoration& rhs) { + if (rhs.dec_type() != spv::Decoration::Offset) return false; + return decoration.struct_member_index() == + rhs.struct_member_index(); + }; + auto i = std::find_if(type2_decorations.begin(), + type2_decorations.end(), compare); + if (i != type2_decorations.end() && + decoration.params().front() != i->params().front()) { + return true; + } + } break; + default: + // This decoration does not affect the layout of the structure, so + // just moving on. + break; + } + } + } + return false; +} + +// If |skip_builtin| is true, returns true if |storage| contains bool within +// it and no storage that contains the bool is builtin. +// If |skip_builtin| is false, returns true if |storage| contains bool within +// it. +bool ContainsInvalidBool(ValidationState_t& _, const Instruction* storage, + bool skip_builtin) { + if (skip_builtin) { + for (const Decoration& decoration : _.id_decorations(storage->id())) { + if (decoration.dec_type() == spv::Decoration::BuiltIn) return false; + } + } + + const size_t elem_type_index = 1; + uint32_t elem_type_id; + Instruction* elem_type; + + switch (storage->opcode()) { + case spv::Op::OpTypeBool: + return true; + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + elem_type_id = storage->GetOperandAs(elem_type_index); + elem_type = _.FindDef(elem_type_id); + return ContainsInvalidBool(_, elem_type, skip_builtin); + case spv::Op::OpTypeStruct: + for (size_t member_type_index = 1; + member_type_index < storage->operands().size(); + ++member_type_index) { + auto member_type_id = + storage->GetOperandAs(member_type_index); + auto member_type = _.FindDef(member_type_id); + if (ContainsInvalidBool(_, member_type, skip_builtin)) return true; + } + default: + break; + } + return false; +} + +std::pair GetStorageClass( + ValidationState_t& _, const Instruction* inst) { + spv::StorageClass dst_sc = spv::StorageClass::Max; + spv::StorageClass src_sc = spv::StorageClass::Max; + switch (inst->opcode()) { + case spv::Op::OpCooperativeMatrixLoadNV: + case spv::Op::OpCooperativeMatrixLoadTensorNV: + case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeVectorLoadNV: + case spv::Op::OpLoad: { + auto load_pointer = _.FindDef(inst->GetOperandAs(2)); + auto load_pointer_type = _.FindDef(load_pointer->type_id()); + dst_sc = load_pointer_type->GetOperandAs(1); + break; + } + case spv::Op::OpCooperativeMatrixStoreNV: + case spv::Op::OpCooperativeMatrixStoreTensorNV: + case spv::Op::OpCooperativeMatrixStoreKHR: + case spv::Op::OpCooperativeVectorStoreNV: + case spv::Op::OpStore: { + auto store_pointer = _.FindDef(inst->GetOperandAs(0)); + auto store_pointer_type = _.FindDef(store_pointer->type_id()); + dst_sc = store_pointer_type->GetOperandAs(1); + break; + } + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: { + auto dst = _.FindDef(inst->GetOperandAs(0)); + auto dst_type = _.FindDef(dst->type_id()); + dst_sc = dst_type->GetOperandAs(1); + auto src = _.FindDef(inst->GetOperandAs(1)); + auto src_type = _.FindDef(src->type_id()); + src_sc = src_type->GetOperandAs(1); + break; + } + default: + break; + } + + return std::make_pair(dst_sc, src_sc); +} + +// Returns the number of instruction words taken up by a memory access +// argument and its implied operands. +int MemoryAccessNumWords(uint32_t mask) { + int result = 1; // Count the mask + if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result; + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result; + return result; +} + +// Returns the scope ID operand for MakeAvailable memory access with mask +// at the given operand index. +// This function is only called for OpLoad, OpStore, OpCopyMemory and +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, +// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV, +// OpCooperativeVectorStoreNV. +uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask, + uint32_t mask_index) { + assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)); + uint32_t this_bit = uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR); + uint32_t index = + mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1))); + return inst->GetOperandAs(index); +} + +// This function is only called for OpLoad, OpStore, OpCopyMemory, +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, +// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV, +// OpCooperativeVectorStoreNV. +uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask, + uint32_t mask_index) { + assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)); + uint32_t this_bit = uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR); + uint32_t index = + mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1))); + return inst->GetOperandAs(index); +} + +bool DoesStructContainRTA(const ValidationState_t& _, const Instruction* inst) { + for (size_t member_index = 1; member_index < inst->operands().size(); + ++member_index) { + const auto member_id = inst->GetOperandAs(member_index); + const auto member_type = _.FindDef(member_id); + if (member_type->opcode() == spv::Op::OpTypeRuntimeArray) return true; + } + return false; +} + +spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, + uint32_t index) { + spv::StorageClass dst_sc, src_sc; + std::tie(dst_sc, src_sc) = GetStorageClass(_, inst); + if (inst->operands().size() <= index) { + // Cases where lack of some operand is invalid + if (src_sc == spv::StorageClass::PhysicalStorageBuffer || + dst_sc == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; + } + return SPV_SUCCESS; + } + + const uint32_t mask = inst->GetOperandAs(index); + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) { + if (inst->opcode() == spv::Op::OpLoad || + inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV || + inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV || + inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR || + inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MakePointerAvailableKHR cannot be used with OpLoad."; + } + + if (!(mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified."; + } + + // Check the associated scope for MakeAvailableKHR. + const auto available_scope = GetMakeAvailableScope(inst, mask, index); + if (auto error = ValidateMemoryScope(_, inst, available_scope)) + return error; + } + + if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) { + if (inst->opcode() == spv::Op::OpStore || + inst->opcode() == spv::Op::OpCooperativeMatrixStoreNV || + inst->opcode() == spv::Op::OpCooperativeMatrixStoreKHR || + inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV || + inst->opcode() == spv::Op::OpCooperativeVectorStoreNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MakePointerVisibleKHR cannot be used with OpStore."; + } + + if (!(mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR must be specified if " + << "MakePointerVisibleKHR is specified."; + } + + // Check the associated scope for MakeVisibleKHR. + const auto visible_scope = GetMakeVisibleScope(inst, mask, index); + if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; + } + + if (mask & uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR)) { + if (dst_sc != spv::StorageClass::Uniform && + dst_sc != spv::StorageClass::Workgroup && + dst_sc != spv::StorageClass::CrossWorkgroup && + dst_sc != spv::StorageClass::Generic && + dst_sc != spv::StorageClass::Image && + dst_sc != spv::StorageClass::StorageBuffer && + dst_sc != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR requires a pointer in Uniform, " + << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " + << "storage classes."; + } + if (src_sc != spv::StorageClass::Max && + src_sc != spv::StorageClass::Uniform && + src_sc != spv::StorageClass::Workgroup && + src_sc != spv::StorageClass::CrossWorkgroup && + src_sc != spv::StorageClass::Generic && + src_sc != spv::StorageClass::Image && + src_sc != spv::StorageClass::StorageBuffer && + src_sc != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR requires a pointer in Uniform, " + << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " + << "storage classes."; + } + } + + if (!(mask & uint32_t(spv::MemoryAccessMask::Aligned))) { + if (src_sc == spv::StorageClass::PhysicalStorageBuffer || + dst_sc == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; + } + } else { + // even if there are other masks, the Aligned operand will be next + const uint32_t aligned_value = inst->GetOperandAs(index + 1); + const bool is_power_of_two = + aligned_value && !(aligned_value & (aligned_value - 1)); + if (!is_power_of_two) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Memory accesses Aligned operand value " << aligned_value + << " is not a power of two."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { + const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR; + + auto result_type = _.FindDef(inst->type_id()); + if (untyped_pointer) { + if (!result_type || + result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type must be an untyped pointer"; + } else { + if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable Result Type " << _.getIdName(inst->type_id()) + << " is not a pointer type."; + } + } + + const auto storage_class_index = 2u; + auto storage_class = + inst->GetOperandAs(storage_class_index); + uint32_t value_id = 0; + if (untyped_pointer) { + const auto has_data_type = 3u < inst->operands().size(); + if (has_data_type) { + value_id = inst->GetOperandAs(3u); + auto data_type = _.FindDef(value_id); + if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Data type must be a type instruction"; + } + } else { + if (storage_class == spv::StorageClass::Function || + storage_class == spv::StorageClass::Private || + storage_class == spv::StorageClass::Workgroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Data type must be specified for Function, Private, and " + "Workgroup storage classes"; + } + if (spvIsVulkanEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Vulkan requires that data type be specified"; + } + } + } + + // For OpVariable the data type comes from pointee type of the result type, + // while for OpUntypedVariableKHR the data type comes from the operand. + if (!untyped_pointer) { + value_id = result_type->GetOperandAs(2); + } + auto value_type = value_id == 0 ? nullptr : _.FindDef(value_id); + + const auto initializer_index = untyped_pointer ? 4u : 3u; + if (initializer_index < inst->operands().size()) { + const auto initializer_id = inst->GetOperandAs(initializer_index); + const auto initializer = _.FindDef(initializer_id); + const auto is_module_scope_var = + initializer && + (initializer->opcode() == spv::Op::OpVariable || + initializer->opcode() == spv::Op::OpUntypedVariableKHR) && + (initializer->GetOperandAs(storage_class_index) != + spv::StorageClass::Function); + const auto is_constant = + initializer && spvOpcodeIsConstant(initializer->opcode()); + if (!initializer || !(is_constant || is_module_scope_var)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Variable Initializer " << _.getIdName(initializer_id) + << " is not a constant or module-scope variable."; + } + if (initializer->type_id() != value_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Initializer type must match the data type"; + } + } + + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::CrossWorkgroup && + storage_class != spv::StorageClass::Private && + storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::UniformConstant && + storage_class != spv::StorageClass::RayPayloadKHR && + storage_class != spv::StorageClass::IncomingRayPayloadKHR && + storage_class != spv::StorageClass::HitAttributeKHR && + storage_class != spv::StorageClass::CallableDataKHR && + storage_class != spv::StorageClass::IncomingCallableDataKHR && + storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT && + storage_class != spv::StorageClass::HitObjectAttributeNV && + storage_class != spv::StorageClass::NodePayloadAMDX) { + bool storage_input_or_output = storage_class == spv::StorageClass::Input || + storage_class == spv::StorageClass::Output; + bool builtin = false; + if (storage_input_or_output) { + for (const Decoration& decoration : _.id_decorations(inst->id())) { + if (decoration.dec_type() == spv::Decoration::BuiltIn) { + builtin = true; + break; + } + } + } + if (!builtin && value_type && + ContainsInvalidBool(_, value_type, storage_input_or_output)) { + if (storage_input_or_output) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(7290) + << "If OpTypeBool is stored in conjunction with OpVariable " + "using Input or Output Storage Classes it requires a BuiltIn " + "decoration"; + + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "If OpTypeBool is stored in conjunction with OpVariable, it " + "can only be used with non-externally visible shader Storage " + "Classes: Workgroup, CrossWorkgroup, Private, Function, " + "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, " + "HitAttributeKHR, CallableDataKHR, " + "IncomingCallableDataKHR, NodePayloadAMDX, or " + "UniformConstant"; + } + } + } + + if (!_.IsValidStorageClass(storage_class)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << _.VkErrorID(4643) + << "Invalid storage class for target environment"; + } + + if (storage_class == spv::StorageClass::Generic) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Variable storage class cannot be Generic"; + } + + if (inst->function() && storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables must have a function[7] storage class inside" + " of a function"; + } + + if (!inst->function() && storage_class == spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables can not have a function[7] storage class " + "outside of a function"; + } + + // SPIR-V 3.32.8: Check that pointer type and variable type have the same + // storage class. + const auto result_storage_class_index = 1; + const auto result_storage_class = + result_type->GetOperandAs(result_storage_class_index); + if (storage_class != result_storage_class) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Storage class must match result type storage class"; + } + + // Variable pointer related restrictions. + const auto pointee = untyped_pointer + ? value_id == 0 ? nullptr : _.FindDef(value_id) + : _.FindDef(result_type->word(3)); + if (_.addressing_model() == spv::AddressingModel::Logical && + !_.options()->relax_logical_pointer) { + // VariablePointersStorageBuffer is implied by VariablePointers. + if (pointee && pointee->opcode() == spv::Op::OpTypePointer) { + if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, variables may not allocate a pointer " + << "type"; + } else if (storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing with variable pointers, variables " + << "that allocate pointers must be in Function or Private " + << "storage classes"; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + // Vulkan Push Constant Interface section: Check type of PushConstant + // variables. + if (storage_class == spv::StorageClass::PushConstant) { + if (pointee && pointee->opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6808) << "PushConstant OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec, Push Constant Interface section:\n" + << "Such variables must be typed as OpTypeStruct"; + } + } + + // Vulkan Descriptor Set Interface: Check type of UniformConstant and + // Uniform variables. + if (storage_class == spv::StorageClass::UniformConstant) { + if (pointee && !IsAllowedTypeOrArrayOfSame( + _, pointee, + {spv::Op::OpTypeImage, spv::Op::OpTypeSampler, + spv::Op::OpTypeSampledImage, + spv::Op::OpTypeAccelerationStructureKHR})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4655) << "UniformConstant OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "Variables identified with the UniformConstant storage class " + << "are used only as handles to refer to opaque resources. Such " + << "variables must be typed as OpTypeImage, OpTypeSampler, " + << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " + << "or an array of one of these types."; + } + } + + if (storage_class == spv::StorageClass::Uniform) { + if (pointee && + !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6807) << "Uniform OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" + << "Variables identified with the Uniform storage class are " + << "used to access transparent buffer backed resources. Such " + << "variables must be typed as OpTypeStruct, or an array of " + << "this type"; + } + } + + if (storage_class == spv::StorageClass::StorageBuffer) { + if (pointee && + !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6807) << "StorageBuffer OpVariable " + << _.getIdName(inst->id()) << " has illegal type.\n" + << "From Vulkan spec:\n" + << "Variables identified with the StorageBuffer storage class " + "are used to access transparent buffer backed resources. " + "Such variables must be typed as OpTypeStruct, or an array " + "of this type"; + } + } + + // Check for invalid use of Invariant + if (storage_class != spv::StorageClass::Input && + storage_class != spv::StorageClass::Output) { + if (_.HasDecoration(inst->id(), spv::Decoration::Invariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable decorated with Invariant must only be identified " + "with the Input or Output storage class in Vulkan " + "environment."; + } + // Need to check if only the members in a struct are decorated + if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) { + if (_.HasDecoration(value_id, spv::Decoration::Invariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable struct member decorated with Invariant must only " + "be identified with the Input or Output storage class in " + "Vulkan environment."; + } + } + } + } + + // Vulkan Appendix A: Check that if contains initializer, then + // storage class is Output, Private, or Function. + if (inst->operands().size() > initializer_index && + storage_class != spv::StorageClass::Output && + storage_class != spv::StorageClass::Private && + storage_class != spv::StorageClass::Function) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class == spv::StorageClass::Workgroup) { + auto init_id = inst->GetOperandAs(initializer_index); + auto init = _.FindDef(init_id); + if (init->opcode() != spv::Op::OpConstantNull) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4734) << "OpVariable, " + << _.getIdName(inst->id()) + << ", initializers are limited to OpConstantNull in " + "Workgroup " + "storage class"; + } + } else if (storage_class != spv::StorageClass::Output && + storage_class != spv::StorageClass::Private && + storage_class != spv::StorageClass::Function) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4651) << "OpVariable, " + << _.getIdName(inst->id()) + << ", has a disallowed initializer & storage class " + << "combination.\n" + << "From " << spvLogStringForEnv(_.context()->target_env) + << " spec:\n" + << "Variable declarations that include initializers must have " + << "one of the following storage classes: Output, Private, " + << "Function or Workgroup"; + } + } + } + + if (initializer_index < inst->operands().size()) { + if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, " << _.getIdName(inst->id()) + << ", initializer are not allowed for TaskPayloadWorkgroupEXT"; + } + if (storage_class == spv::StorageClass::Input) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, " << _.getIdName(inst->id()) + << ", initializer are not allowed for Input"; + } + if (storage_class == spv::StorageClass::HitObjectAttributeNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, " << _.getIdName(inst->id()) + << ", initializer are not allowed for HitObjectAttributeNV"; + } + } + + if (storage_class == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PhysicalStorageBuffer must not be used with OpVariable."; + } + + // Vulkan specific validation rules for OpTypeRuntimeArray + if (spvIsVulkanEnv(_.context()->target_env)) { + // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, + // so should never appear as a bare variable. + // Unless the module has the RuntimeDescriptorArrayEXT capability. + if (value_type && value_type->opcode() == spv::Op::OpTypeRuntimeArray) { + if (!_.HasCapability(spv::Capability::RuntimeDescriptorArrayEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "OpVariable, " + << _.getIdName(inst->id()) + << ", is attempting to create memory for an illegal type, " + << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " + << "appear as the final member of an OpTypeStruct, thus cannot " + << "be instantiated via OpVariable"; + } else { + // A bare variable OpTypeRuntimeArray is allowed in this context, but + // still need to check the storage class. + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::Uniform && + storage_class != spv::StorageClass::UniformConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan with RuntimeDescriptorArrayEXT, a variable " + << "containing OpTypeRuntimeArray must have storage class of " + << "StorageBuffer, Uniform, or UniformConstant."; + } + } + } + + // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it + // must either have the storage class StorageBuffer and be decorated + // with Block, or it must be in the Uniform storage class and be decorated + // as BufferBlock. + if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) { + if (DoesStructContainRTA(_, value_type)) { + if (storage_class == spv::StorageClass::StorageBuffer || + storage_class == spv::StorageClass::PhysicalStorageBuffer) { + if (!_.HasDecoration(value_id, spv::Decoration::Block)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with Block if it " + << "has storage class StorageBuffer or " + "PhysicalStorageBuffer."; + } + } else if (storage_class == spv::StorageClass::Uniform) { + if (!_.HasDecoration(value_id, spv::Decoration::BufferBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with BufferBlock " + << "if it has storage class Uniform."; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << "For Vulkan, OpTypeStruct variables containing " + << "OpTypeRuntimeArray must have storage class of " + << "StorageBuffer, PhysicalStorageBuffer, or Uniform."; + } + } + } + } + + // Cooperative matrix types can only be allocated in Function or Private + if ((storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private) && + pointee && + _.ContainsType(pointee->id(), [](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + return opcode == spv::Op::OpTypeCooperativeMatrixNV || + opcode == spv::Op::OpTypeCooperativeMatrixKHR; + })) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cooperative matrix types (or types containing them) can only be " + "allocated " + << "in Function or Private storage classes or as function " + "parameters"; + } + + if ((storage_class != spv::StorageClass::Function && + storage_class != spv::StorageClass::Private) && + pointee && + _.ContainsType(pointee->id(), [](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + return opcode == spv::Op::OpTypeCooperativeVectorNV; + })) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cooperative vector types (or types containing them) can only be " + "allocated " + << "in Function or Private storage classes or as function " + "parameters"; + } + + if (_.HasCapability(spv::Capability::Shader)) { + // Don't allow variables containing 16-bit elements without the appropriate + // capabilities. + if ((!_.HasCapability(spv::Capability::Int16) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 16)) || + (!_.HasCapability(spv::Capability::Float16) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) { + auto underlying_type = value_type; + while (underlying_type && + underlying_type->opcode() == spv::Op::OpTypePointer) { + storage_class = underlying_type->GetOperandAs(1u); + underlying_type = + _.FindDef(underlying_type->GetOperandAs(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Uniform: + if (underlying_type && + !_.HasCapability( + spv::Capability::UniformAndStorageBuffer16BitAccess)) { + if (underlying_type->opcode() == spv::Op::OpTypeArray || + underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs(1u)); + } + if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) || + !_.HasDecoration(underlying_type->id(), + spv::Decoration::BufferBlock)) { + storage_class_ok = false; + } + } + break; + case spv::StorageClass::PushConstant: + if (!_.HasCapability(spv::Capability::StoragePushConstant16)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Input: + case spv::StorageClass::Output: + if (!_.HasCapability(spv::Capability::StorageInputOutput16)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Workgroup: + if (!_.HasCapability( + spv::Capability:: + WorkgroupMemoryExplicitLayout16BitAccessKHR)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 16-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 16-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + // Don't allow variables containing 8-bit elements without the appropriate + // capabilities. + if (!_.HasCapability(spv::Capability::Int8) && + _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) { + auto underlying_type = value_type; + while (underlying_type && + underlying_type->opcode() == spv::Op::OpTypePointer) { + storage_class = underlying_type->GetOperandAs(1u); + underlying_type = + _.FindDef(underlying_type->GetOperandAs(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class)); + switch (storage_class) { + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Uniform: + if (underlying_type && + !_.HasCapability( + spv::Capability::UniformAndStorageBuffer8BitAccess)) { + if (underlying_type->opcode() == spv::Op::OpTypeArray || + underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs(1u)); + } + if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) || + !_.HasDecoration(underlying_type->id(), + spv::Decoration::BufferBlock)) { + storage_class_ok = false; + } + } + break; + case spv::StorageClass::PushConstant: + if (!_.HasCapability(spv::Capability::StoragePushConstant8)) { + storage_class_ok = false; + } + break; + case spv::StorageClass::Workgroup: + if (!_.HasCapability( + spv::Capability:: + WorkgroupMemoryExplicitLayout8BitAccessKHR)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 8-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 8-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Result Type " << _.getIdName(inst->type_id()) + << " is not defined."; + } + + const auto pointer_index = 2; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type = _.FindDef(pointer->type_id()); + if (!pointer_type || + (pointer_type->opcode() != spv::Op::OpTypePointer && + pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + if (pointer_type->opcode() == spv::Op::OpTypePointer) { + const auto pointee_type = + _.FindDef(pointer_type->GetOperandAs(2)); + if (!pointee_type || result_type->id() != pointee_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Result Type " << _.getIdName(inst->type_id()) + << " does not match Pointer " << _.getIdName(pointer->id()) + << "s type."; + } + } + + if (!_.options()->before_hlsl_legalization && + _.ContainsRuntimeArray(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot load a runtime-sized array"; + } + + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + result_type->opcode() != spv::Op::OpTypePointer) { + if (result_type->opcode() != spv::Op::OpTypeInt && + result_type->opcode() != spv::Op::OpTypeFloat && + result_type->opcode() != spv::Op::OpTypeVector && + result_type->opcode() != spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "8- or 16-bit loads must be a scalar, vector or matrix type"; + } + } + + _.RegisterQCOMImageProcessingTextureConsumer(pointer_id, inst, nullptr); + + return SPV_SUCCESS; +} + +spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { + const auto pointer_index = 0; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + (_.addressing_model() == spv::AddressingModel::Logical && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + const auto pointer_type = _.FindDef(pointer->type_id()); + if (!pointer_type || + (pointer_type->opcode() != spv::Op::OpTypePointer && + pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + Instruction* type = nullptr; + if (pointer_type->opcode() == spv::Op::OpTypePointer) { + const auto type_id = pointer_type->GetOperandAs(2); + type = _.FindDef(type_id); + if (!type || spv::Op::OpTypeVoid == type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s type is void."; + } + } + + // validate storage class + { + uint32_t data_type; + spv::StorageClass storage_class; + if (!_.GetPointerTypeInfo(pointer_type->id(), &data_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << " is not pointer type"; + } + + if (storage_class == spv::StorageClass::UniformConstant || + storage_class == spv::StorageClass::Input || + storage_class == spv::StorageClass::PushConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << " storage class is read-only"; + } else if (storage_class == spv::StorageClass::ShaderRecordBufferKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ShaderRecordBufferKHR Storage Class variables are read only"; + } else if (storage_class == spv::StorageClass::HitAttributeKHR) { + std::string errorVUID = _.VkErrorID(4703); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model == spv::ExecutionModel::AnyHitKHR || + model == spv::ExecutionModel::ClosestHitKHR) { + if (message) { + *message = + errorVUID + + "HitAttributeKHR Storage Class variables are read only " + "with AnyHitKHR and ClosestHitKHR"; + } + return false; + } + return true; + }); + } + + if (spvIsVulkanEnv(_.context()->target_env) && + storage_class == spv::StorageClass::Uniform) { + auto base_ptr = _.TracePointer(pointer); + if (base_ptr->opcode() == spv::Op::OpVariable) { + // If it's not a variable a different check should catch the problem. + auto base_type = _.FindDef(base_ptr->GetOperandAs(0)); + // Get the pointed-to type. + base_type = _.FindDef(base_type->GetOperandAs(2u)); + if (base_type->opcode() == spv::Op::OpTypeArray || + base_type->opcode() == spv::Op::OpTypeRuntimeArray) { + base_type = _.FindDef(base_type->GetOperandAs(1u)); + } + if (_.HasDecoration(base_type->id(), spv::Decoration::Block)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6925) + << "In the Vulkan environment, cannot store to Uniform Blocks"; + } + } + } + } + + const auto object_index = 1; + const auto object_id = inst->GetOperandAs(object_index); + const auto object = _.FindDef(object_id); + if (!object || !object->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Object " << _.getIdName(object_id) + << " is not an object."; + } + const auto object_type = _.FindDef(object->type_id()); + if (!object_type || spv::Op::OpTypeVoid == object_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Object " << _.getIdName(object_id) + << "s type is void."; + } + + if (type && (type->id() != object_type->id())) { + if (!_.options()->relax_struct_store || + type->opcode() != spv::Op::OpTypeStruct || + object_type->opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s type does not match Object " + << _.getIdName(object->id()) << "s type."; + } + + // TODO: Check for layout compatible matricies and arrays as well. + if (!AreLayoutCompatibleStructs(_, type, object_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer " << _.getIdName(pointer_id) + << "s layout does not match Object " + << _.getIdName(object->id()) << "s layout."; + } + } + + if (auto error = CheckMemoryAccess(_, inst, 2)) return error; + + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + object_type->opcode() != spv::Op::OpTypePointer) { + if (object_type->opcode() != spv::Op::OpTypeInt && + object_type->opcode() != spv::Op::OpTypeFloat && + object_type->opcode() != spv::Op::OpTypeVector && + object_type->opcode() != spv::Op::OpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "8- or 16-bit stores must be a scalar, vector or matrix type"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env) && + !_.options()->before_hlsl_legalization) { + const auto isForbiddenType = [](const Instruction* type_inst) { + auto opcode = type_inst->opcode(); + return opcode == spv::Op::OpTypeImage || + opcode == spv::Op::OpTypeSampler || + opcode == spv::Op::OpTypeSampledImage || + opcode == spv::Op::OpTypeAccelerationStructureKHR; + }; + if (_.ContainsType(object_type->id(), isForbiddenType)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(6924) + << "Cannot store to OpTypeImage, OpTypeSampler, " + "OpTypeSampledImage, or OpTypeAccelerationStructureKHR objects"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _, + const Instruction* inst) { + assert(inst->opcode() == spv::Op::OpCopyMemory || + inst->opcode() == spv::Op::OpCopyMemorySized); + const uint32_t first_access_index = + inst->opcode() == spv::Op::OpCopyMemory ? 2 : 3; + if (inst->operands().size() > first_access_index) { + if (auto error = CheckMemoryAccess(_, inst, first_access_index)) + return error; + + const auto first_access = inst->GetOperandAs(first_access_index); + const uint32_t second_access_index = + first_access_index + MemoryAccessNumWords(first_access); + if (inst->operands().size() > second_access_index) { + if (_.features().copy_memory_permits_two_memory_accesses) { + if (auto error = CheckMemoryAccess(_, inst, second_access_index)) + return error; + + // In the two-access form in SPIR-V 1.4 and later: + // - the first is the target (write) access and it can't have + // make-visible. + // - the second is the source (read) access and it can't have + // make-available. + if (first_access & + uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Target memory access must not include " + "MakePointerVisibleKHR"; + } + const auto second_access = + inst->GetOperandAs(second_access_index); + if (second_access & + uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Source memory access must not include " + "MakePointerAvailableKHR"; + } + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(static_cast(inst->opcode())) + << " with two memory access operands requires SPIR-V 1.4 or " + "later"; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { + const auto target_index = 0; + const auto target_id = inst->GetOperandAs(target_index); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand " << _.getIdName(target_id) + << " is not defined."; + } + + const auto source_index = 1; + const auto source_id = inst->GetOperandAs(source_index); + const auto source = _.FindDef(source_id); + if (!source) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand " << _.getIdName(source_id) + << " is not defined."; + } + + const auto target_pointer_type = _.FindDef(target->type_id()); + if (!target_pointer_type || + (target_pointer_type->opcode() != spv::Op::OpTypePointer && + target_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand " << _.getIdName(target_id) + << " is not a pointer."; + } + + const auto source_pointer_type = _.FindDef(source->type_id()); + if (!source_pointer_type || + (source_pointer_type->opcode() != spv::Op::OpTypePointer && + source_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand " << _.getIdName(source_id) + << " is not a pointer."; + } + + if (inst->opcode() == spv::Op::OpCopyMemory) { + const bool target_typed = + target_pointer_type->opcode() == spv::Op::OpTypePointer; + const bool source_typed = + source_pointer_type->opcode() == spv::Op::OpTypePointer; + Instruction* target_type = nullptr; + Instruction* source_type = nullptr; + if (target_typed) { + target_type = _.FindDef(target_pointer_type->GetOperandAs(2)); + + if (!target_type || target_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand " << _.getIdName(target_id) + << " cannot be a void pointer."; + } + } + + if (source_typed) { + source_type = _.FindDef(source_pointer_type->GetOperandAs(2)); + if (!source_type || source_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand " << _.getIdName(source_id) + << " cannot be a void pointer."; + } + } + + if (target_type && source_type && target_type->id() != source_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target " << _.getIdName(source_id) + << "s type does not match Source " + << _.getIdName(source_type->id()) << "s type."; + } + + if (!target_type && !source_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "One of Source or Target must be a typed pointer"; + } + + if (auto error = CheckMemoryAccess(_, inst, 2)) return error; + } else { + const auto size_id = inst->GetOperandAs(2); + const auto size = _.FindDef(size_id); + if (!size) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand " << _.getIdName(size_id) + << " is not defined."; + } + + const auto size_type = _.FindDef(size->type_id()); + if (!_.IsIntScalarType(size_type->id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand " << _.getIdName(size_id) + << " must be a scalar integer type."; + } + bool is_zero = true; + switch (size->opcode()) { + case spv::Op::OpConstantNull: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand " << _.getIdName(size_id) + << " cannot be a constant zero."; + case spv::Op::OpConstant: + if (size_type->word(3) == 1 && + size->word(size->words().size() - 1) & 0x80000000) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand " << _.getIdName(size_id) + << " cannot have the sign bit set to 1."; + } + for (size_t i = 3; is_zero && i < size->words().size(); ++i) { + is_zero &= (size->word(i) == 0); + } + if (is_zero) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand " << _.getIdName(size_id) + << " cannot be a constant zero."; + } + break; + default: + // Cannot infer any other opcodes. + break; + } + + if (_.HasCapability(spv::Capability::Shader)) { + bool is_int = false; + bool is_const = false; + uint32_t value = 0; + std::tie(is_int, is_const, value) = _.EvalInt32IfConst(size_id); + if (is_const) { + if (value % 4 != 0) { + const auto source_sc = + source_pointer_type->GetOperandAs(1); + const auto target_sc = + target_pointer_type->GetOperandAs(1); + const bool int8 = _.HasCapability(spv::Capability::Int8); + const bool ubo_int8 = _.HasCapability( + spv::Capability::UniformAndStorageBuffer8BitAccess); + const bool ssbo_int8 = + _.HasCapability(spv::Capability::StorageBuffer8BitAccess) || + ubo_int8; + const bool pc_int8 = + _.HasCapability(spv::Capability::StoragePushConstant8); + const bool wg_int8 = _.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR); + const bool int16 = _.HasCapability(spv::Capability::Int16) || int8; + const bool ubo_int16 = + _.HasCapability( + spv::Capability::UniformAndStorageBuffer16BitAccess) || + ubo_int8; + const bool ssbo_int16 = + _.HasCapability(spv::Capability::StorageBuffer16BitAccess) || + ubo_int16 || ssbo_int8; + const bool pc_int16 = + _.HasCapability(spv::Capability::StoragePushConstant16) || + pc_int8; + const bool io_int16 = + _.HasCapability(spv::Capability::StorageInputOutput16); + const bool wg_int16 = _.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR); + + bool source_int16_match = false; + bool target_int16_match = false; + bool source_int8_match = false; + bool target_int8_match = false; + switch (source_sc) { + case spv::StorageClass::StorageBuffer: + source_int16_match = ssbo_int16; + source_int8_match = ssbo_int8; + break; + case spv::StorageClass::Uniform: + source_int16_match = ubo_int16; + source_int8_match = ubo_int8; + break; + case spv::StorageClass::PushConstant: + source_int16_match = pc_int16; + source_int8_match = pc_int8; + break; + case spv::StorageClass::Input: + case spv::StorageClass::Output: + source_int16_match = io_int16; + break; + case spv::StorageClass::Workgroup: + source_int16_match = wg_int16; + source_int8_match = wg_int8; + break; + default: + break; + } + switch (target_sc) { + case spv::StorageClass::StorageBuffer: + target_int16_match = ssbo_int16; + target_int8_match = ssbo_int8; + break; + case spv::StorageClass::Uniform: + target_int16_match = ubo_int16; + target_int8_match = ubo_int8; + break; + case spv::StorageClass::PushConstant: + target_int16_match = pc_int16; + target_int8_match = pc_int8; + break; + // Input is read-only so it cannot be the target pointer. + case spv::StorageClass::Output: + target_int16_match = io_int16; + break; + case spv::StorageClass::Workgroup: + target_int16_match = wg_int16; + target_int8_match = wg_int8; + break; + default: + break; + } + if (!int8 && !int16 && !(source_int16_match && target_int16_match)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a multiple of 4"; + } + if (value % 2 != 0) { + if (!int8 && !(source_int8_match && target_int8_match)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a multiple of 2"; + } + } + } + } + } + + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; + } + if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error; + + // Get past the pointers to avoid checking a pointer copy. + if (target_pointer_type->opcode() == spv::Op::OpTypePointer) { + auto sub_type = _.FindDef(target_pointer_type->GetOperandAs(2)); + while (sub_type->opcode() == spv::Op::OpTypePointer) { + sub_type = _.FindDef(sub_type->GetOperandAs(2)); + } + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(sub_type->id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot copy memory of objects containing 8- or 16-bit types"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateAccessChain(ValidationState_t& _, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode()); + + // The result type must be OpTypePointer for regular access chains and an + // OpTypeUntypedPointerKHR for untyped access chains. + auto result_type = _.FindDef(inst->type_id()); + if (untyped_pointer) { + if (!result_type || + spv::Op::OpTypeUntypedPointerKHR != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) + << " must be OpTypeUntypedPointerKHR. Found Op" + << spvOpcodeString(static_cast(result_type->opcode())) + << "."; + } + } else { + if (!result_type || spv::Op::OpTypePointer != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" + << spvOpcodeString(static_cast(result_type->opcode())) + << "."; + } + } + + if (untyped_pointer) { + // Base type must be a non-pointer type. + const auto base_type = _.FindDef(inst->GetOperandAs(2)); + if (!base_type || !spvOpcodeGeneratesType(base_type->opcode()) || + base_type->opcode() == spv::Op::OpTypePointer || + base_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Base type must be a non-pointer type"; + } + } + + // Base must be a pointer, pointing to the base of a composite object. + const auto base_index = untyped_pointer ? 3 : 2; + const auto base_id = inst->GetOperandAs(base_index); + const auto base = _.FindDef(base_id); + const auto base_type = _.FindDef(base->type_id()); + if (!base_type || !(spv::Op::OpTypePointer == base_type->opcode() || + (untyped_pointer && spv::Op::OpTypeUntypedPointerKHR == + base_type->opcode()))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Base " << _.getIdName(base_id) << " in " << instr_name + << " instruction must be a pointer."; + } + + // The result pointer storage class and base pointer storage class must match. + // Word 2 of OpTypePointer is the Storage Class. + auto result_type_storage_class = result_type->word(2); + auto base_type_storage_class = base_type->word(2); + if (result_type_storage_class != base_type_storage_class) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The result pointer storage class and base " + "pointer storage class in " + << instr_name << " do not match."; + } + + // The type pointed to by OpTypePointer (word 3) must be a composite type. + auto type_pointee = untyped_pointer + ? _.FindDef(inst->GetOperandAs(2)) + : _.FindDef(base_type->word(3)); + + // Check Universal Limit (SPIR-V Spec. Section 2.17). + // The number of indexes passed to OpAccessChain may not exceed 255 + // The instruction includes 4 words + N words (for N indexes) + size_t num_indexes = inst->words().size() - 4; + if (inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain || + inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR || + inst->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) { + // In pointer access chains, the element operand is required, but not + // counted as an index. + --num_indexes; + } + const size_t num_indexes_limit = + _.options()->universal_limits_.max_access_chain_indexes; + if (num_indexes > num_indexes_limit) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The number of indexes in " << instr_name << " may not exceed " + << num_indexes_limit << ". Found " << num_indexes << " indexes."; + } + // Indexes walk the type hierarchy to the desired depth, potentially down to + // scalar granularity. The first index in Indexes will select the top-level + // member/element/component/element of the base composite. All composite + // constituents use zero-based numbering, as described by their OpType... + // instruction. The second index will apply similarly to that result, and so + // on. Once any non-composite type is reached, there must be no remaining + // (unused) indexes. + auto starting_index = untyped_pointer ? 5 : 4; + if (inst->opcode() == spv::Op::OpPtrAccessChain || + inst->opcode() == spv::Op::OpInBoundsPtrAccessChain || + inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR || + inst->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) { + ++starting_index; + } + for (size_t i = starting_index; i < inst->words().size(); ++i) { + const uint32_t cur_word = inst->words()[i]; + // Earlier ID checks ensure that cur_word definition exists. + auto cur_word_instr = _.FindDef(cur_word); + // The index must be a scalar integer type (See OpAccessChain in the Spec.) + auto index_type = _.FindDef(cur_word_instr->type_id()); + if (!index_type || spv::Op::OpTypeInt != index_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Indexes passed to " << instr_name + << " must be of type integer."; + } + switch (type_pointee->opcode()) { + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeCooperativeVectorNV: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeNodePayloadArrayAMDX: { + // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV, + // OpTypeCooperativeVectorNV, OpTypeArray, and OpTypeRuntimeArray, word + // 2 is the Element Type. + type_pointee = _.FindDef(type_pointee->word(2)); + break; + } + case spv::Op::OpTypeStruct: { + // In case of structures, there is an additional constraint on the + // index: the index must be an OpConstant. + int64_t cur_index; + if (!_.EvalConstantValInt64(cur_word, &cur_index)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The passed to " << instr_name << " to index " + << _.getIdName(cur_word) + << " into a " + "structure must be an OpConstant."; + } + + // The index points to the struct member we want, therefore, the index + // should be less than the number of struct members. + const int64_t num_struct_members = + static_cast(type_pointee->words().size() - 2); + if (cur_index >= num_struct_members || cur_index < 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << _.getIdName(cur_word) + << " is out of bounds: " << instr_name << " cannot find index " + << cur_index << " into the structure " + << _.getIdName(type_pointee->id()) << ". This structure has " + << num_struct_members << " members. Largest valid index is " + << num_struct_members - 1 << "."; + } + // Struct members IDs start at word 2 of OpTypeStruct. + const size_t word_index = static_cast(cur_index) + 2; + auto structMemberId = type_pointee->word(word_index); + type_pointee = _.FindDef(structMemberId); + break; + } + default: { + // Give an error. reached non-composite type while indexes still remain. + return _.diag(SPV_ERROR_INVALID_ID, inst) + << instr_name + << " reached non-composite type while indexes " + "still remain to be traversed."; + } + } + } + + if (!untyped_pointer) { + // Result type is a pointer. Find out what it's pointing to. + // This will be used to make sure the indexing results in the same type. + // OpTypePointer word 3 is the type being pointed to. + const auto result_type_pointee = _.FindDef(result_type->word(3)); + // At this point, we have fully walked down from the base using the indeces. + // The type being pointed to should be the same as the result type. + if (type_pointee->id() != result_type_pointee->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << instr_name << " result type (Op" + << spvOpcodeString( + static_cast(result_type_pointee->opcode())) + << ") does not match the type that results from indexing into the " + "base " + " (Op" + << spvOpcodeString(static_cast(type_pointee->opcode())) + << ")."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateRawAccessChain(ValidationState_t& _, + const Instruction* inst) { + std::string instr_name = "Op" + std::string(spvOpcodeString(inst->opcode())); + + // The result type must be OpTypePointer. + const auto result_type = _.FindDef(inst->type_id()); + if (spv::Op::OpTypePointer != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op" + << spvOpcodeString(result_type->opcode()) << '.'; + } + + // The pointed storage class must be valid. + const auto storage_class = result_type->GetOperandAs(1); + if (storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer && + storage_class != spv::StorageClass::Uniform) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) + << " must point to a storage class of " + "StorageBuffer, PhysicalStorageBuffer, or Uniform."; + } + + // The pointed type must not be one in the list below. + const auto result_type_pointee = + _.FindDef(result_type->GetOperandAs(2)); + if (result_type_pointee->opcode() == spv::Op::OpTypeArray || + result_type_pointee->opcode() == spv::Op::OpTypeMatrix || + result_type_pointee->opcode() == spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type of " << instr_name << " " + << _.getIdName(inst->id()) + << " must not point to " + "OpTypeArray, OpTypeMatrix, or OpTypeStruct."; + } + + // Validate Stride is a OpConstant. + const auto stride = _.FindDef(inst->GetOperandAs(3)); + if (stride->opcode() != spv::Op::OpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Stride of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpConstant. Found Op" + << spvOpcodeString(stride->opcode()) << '.'; + } + // Stride type must be OpTypeInt + const auto stride_type = _.FindDef(stride->type_id()); + if (stride_type->opcode() != spv::Op::OpTypeInt) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Stride of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op" + << spvOpcodeString(stride_type->opcode()) << '.'; + } + + // Index and Offset type must be OpTypeInt with a width of 32 + const auto ValidateType = [&](const char* name, + int operandIndex) -> spv_result_t { + const auto value = _.FindDef(inst->GetOperandAs(operandIndex)); + const auto value_type = _.FindDef(value->type_id()); + if (value_type->opcode() != spv::Op::OpTypeInt) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of " << name << " of " << instr_name << " " + << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op" + << spvOpcodeString(value_type->opcode()) << '.'; + } + const auto width = value_type->GetOperandAs(1); + if (width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The integer width of " << name << " of " << instr_name + << " " << _.getIdName(inst->id()) << " must be 32. Found " + << width << '.'; + } + return SPV_SUCCESS; + }; + spv_result_t result; + result = ValidateType("Index", 4); + if (result != SPV_SUCCESS) { + return result; + } + result = ValidateType("Offset", 5); + if (result != SPV_SUCCESS) { + return result; + } + + uint32_t access_operands = 0; + if (inst->operands().size() >= 7) { + access_operands = inst->GetOperandAs(6); + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + uint64_t stride_value = 0; + if (_.EvalConstantValUint64(stride->id(), &stride_value) && + stride_value == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Stride must not be zero when per-element robustness is used."; + } + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV) || + access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + if (storage_class == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Storage class cannot be PhysicalStorageBuffer when " + "raw access chain robustness is used."; + } + } + if (access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerComponentNV) && + access_operands & + uint32_t(spv::RawAccessChainOperandsMask::RobustnessPerElementNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Per-component robustness and per-element robustness are " + "mutually exclusive."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidatePtrAccessChain(ValidationState_t& _, + const Instruction* inst) { + if (_.addressing_model() == spv::AddressingModel::Logical && + inst->opcode() == spv::Op::OpPtrAccessChain) { + if (!_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Generating variable pointers requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + } + } + + // Need to call first, will make sure Base is a valid ID + if (auto error = ValidateAccessChain(_, inst)) return error; + + const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode()); + + const auto base_id = inst->GetOperandAs(2); + const auto base = _.FindDef(base_id); + const auto base_type = untyped_pointer + ? _.FindDef(inst->GetOperandAs(2)) + : _.FindDef(base->type_id()); + const auto base_type_storage_class = + base_type->GetOperandAs(1); + + if (_.HasCapability(spv::Capability::Shader) && + (base_type_storage_class == spv::StorageClass::Uniform || + base_type_storage_class == spv::StorageClass::StorageBuffer || + base_type_storage_class == spv::StorageClass::PhysicalStorageBuffer || + base_type_storage_class == spv::StorageClass::PushConstant || + (_.HasCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR) && + base_type_storage_class == spv::StorageClass::Workgroup)) && + !_.HasDecoration(base_type->id(), spv::Decoration::ArrayStride)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpPtrAccessChain must have a Base whose type is decorated " + "with ArrayStride"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + const auto untyped_cap = + untyped_pointer && _.HasCapability(spv::Capability::UntypedPointersKHR); + if (base_type_storage_class == spv::StorageClass::Workgroup) { + if (!_.HasCapability(spv::Capability::VariablePointers) && !untyped_cap) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7651) + << "OpPtrAccessChain Base operand pointing to Workgroup " + "storage class must use VariablePointers capability"; + } + } else if (base_type_storage_class == spv::StorageClass::StorageBuffer) { + if (!_.features().variable_pointers && !untyped_cap) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7652) + << "OpPtrAccessChain Base operand pointing to StorageBuffer " + "storage class must use VariablePointers or " + "VariablePointersStorageBuffer capability"; + } + } else if (base_type_storage_class != + spv::StorageClass::PhysicalStorageBuffer && + !untyped_cap) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7650) + << "OpPtrAccessChain Base operand must point to Workgroup, " + "StorageBuffer, or PhysicalStorageBuffer storage class"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateArrayLength(ValidationState_t& state, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + // Result type must be a 32-bit unsigned int. + auto result_type = state.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypeInt || + result_type->GetOperandAs(1) != 32 || + result_type->GetOperandAs(2) != 0) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " " + << state.getIdName(inst->id()) + << " must be OpTypeInt with width 32 and signedness 0."; + } + + const bool untyped = inst->opcode() == spv::Op::OpUntypedArrayLengthKHR; + auto pointer_ty_id = state.GetOperandTypeId(inst, (untyped ? 3 : 2)); + auto pointer_ty = state.FindDef(pointer_ty_id); + if (untyped) { + if (pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer must be an untyped pointer"; + } + } else if (pointer_ty->opcode() != spv::Op::OpTypePointer) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Structure's type in " << instr_name << " " + << state.getIdName(inst->id()) + << " must be a pointer to an OpTypeStruct."; + } + + Instruction* structure_type = nullptr; + if (untyped) { + structure_type = state.FindDef(inst->GetOperandAs(2)); + } else { + structure_type = state.FindDef(pointer_ty->GetOperandAs(2)); + } + + if (structure_type->opcode() != spv::Op::OpTypeStruct) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Structure's type in " << instr_name << " " + << state.getIdName(inst->id()) + << " must be a pointer to an OpTypeStruct."; + } + + auto num_of_members = structure_type->operands().size() - 1; + auto last_member = + state.FindDef(structure_type->GetOperandAs(num_of_members)); + if (last_member->opcode() != spv::Op::OpTypeRuntimeArray) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Structure's last member in " << instr_name << " " + << state.getIdName(inst->id()) << " must be an OpTypeRuntimeArray."; + } + + // The array member must the index of the last element (the run time + // array). + const auto index = untyped ? 4 : 3; + if (inst->GetOperandAs(index) != num_of_members - 1) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The array member in " << instr_name << " " + << state.getIdName(inst->id()) + << " must be the last member of the struct."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + // Result type must be a 32-bit unsigned int. + auto result_type = state.FindDef(inst->type_id()); + if (result_type->opcode() != spv::Op::OpTypeInt || + result_type->GetOperandAs(1) != 32 || + result_type->GetOperandAs(2) != 0) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " " + << state.getIdName(inst->id()) + << " must be OpTypeInt with width 32 and signedness 0."; + } + + bool isKhr = inst->opcode() == spv::Op::OpCooperativeMatrixLengthKHR; + auto type_id = inst->GetOperandAs(2); + auto type = state.FindDef(type_id); + if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The type in " << instr_name << " " + << state.getIdName(type_id) + << " must be OpTypeCooperativeMatrixKHR."; + } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The type in " << instr_name << " " + << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeMatrixLoadNV"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs(1))->type_id(); + opname = "spv::Op::OpCooperativeMatrixStoreNV"; + } + + auto matrix_type = _.FindDef(type_id); + + if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) { + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixLoadNV Result Type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixStoreNV Object type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 2u : 0u; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs(storage_class_index); + + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " storage class for pointer type " + << _.getIdName(pointer_type_id) + << " is not Workgroup or StorageBuffer."; + } + + const auto pointee_id = pointer_type->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) || + _.IsFloatScalarOrVectorType(pointee_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer->id()) + << "s Type must be a scalar or vector type."; + } + + const auto stride_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 3u : 2u; + const auto stride_id = inst->GetOperandAs(stride_index); + const auto stride = _.FindDef(stride_id); + if (!stride || !_.IsIntScalarType(stride->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Stride operand " << _.getIdName(stride_id) + << " must be a scalar integer type."; + } + + const auto colmajor_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 4u : 3u; + const auto colmajor_id = inst->GetOperandAs(colmajor_index); + const auto colmajor = _.FindDef(colmajor_id); + if (!colmajor || !_.IsBoolScalarType(colmajor->type_id()) || + !(spvOpcodeIsConstant(colmajor->opcode()) || + spvOpcodeIsSpecConstant(colmajor->opcode()))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Column Major operand " << _.getIdName(colmajor_id) + << " must be a boolean constant instruction."; + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV) ? 5u : 4u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeMatrixLoadKHR"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs(1))->type_id(); + opname = "spv::Op::OpCooperativeMatrixStoreKHR"; + } + + auto matrix_type = _.FindDef(type_id); + + if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixLoadKHR Result Type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixStoreKHR Object type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 2u : 0u; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || + !(pointer_type->opcode() == spv::Op::OpTypePointer || + pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + const bool untyped = + pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR; + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs(storage_class_index); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(8973) << opname + << " storage class for pointer type " + << _.getIdName(pointer_type_id) + << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer."; + } + } + + if (!untyped) { + const auto pointee_id = pointer_type->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) || + _.IsFloatScalarOrVectorType(pointee_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer->id()) + << "s Type must be a scalar or vector type."; + } + } + + const auto layout_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 3u : 2u; + const auto layout_id = inst->GetOperandAs(layout_index); + const auto layout_inst = _.FindDef(layout_id); + if (!layout_inst || !_.IsIntScalarType(layout_inst->type_id()) || + !spvOpcodeIsConstant(layout_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout operand " << _.getIdName(layout_id) + << " must be a 32-bit integer constant instruction."; + } + + bool stride_required = false; + uint64_t layout; + if (_.EvalConstantValUint64(layout_id, &layout)) { + stride_required = + (layout == (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR) || + (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR); + } + + const auto stride_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 4u : 3u; + if (inst->operands().size() > stride_index) { + const auto stride_id = inst->GetOperandAs(stride_index); + const auto stride = _.FindDef(stride_id); + if (!stride || !_.IsIntScalarType(stride->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Stride operand " << _.getIdName(stride_id) + << " must be a scalar integer type."; + } + } else if (stride_required) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MemoryLayout " << layout << " requires a Stride."; + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 5u : 4u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + +// Returns the number of instruction words taken up by a tensor addressing +// operands argument and its implied operands. +int TensorAddressingOperandsNumWords(spv::TensorAddressingOperandsMask mask) { + int result = 1; // Count the mask + if ((mask & spv::TensorAddressingOperandsMask::TensorView) != + spv::TensorAddressingOperandsMask::MaskNone) + ++result; + if ((mask & spv::TensorAddressingOperandsMask::DecodeFunc) != + spv::TensorAddressingOperandsMask::MaskNone) + ++result; + return result; +} + +spv_result_t ValidateCooperativeMatrixLoadStoreTensorNV( + ValidationState_t& _, const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeMatrixLoadTensorNV"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs(1))->type_id(); + opname = "spv::Op::OpCooperativeMatrixStoreTensorNV"; + } + + auto matrix_type = _.FindDef(type_id); + + if (matrix_type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) { + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixLoadTensorNV Result Type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeMatrixStoreTensorNV Object type " + << _.getIdName(type_id) << " is not a cooperative matrix type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) ? 2u : 0u; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs(storage_class_index); + + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(8973) << opname + << " storage class for pointer type " + << _.getIdName(pointer_type_id) + << " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer."; + } + + if (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) { + const auto object_index = 3; + const auto object_id = inst->GetOperandAs(object_index); + const auto object = _.FindDef(object_id); + if (!object || object->type_id() != type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Object " << _.getIdName(object_id) + << " type does not match Result Type."; + } + } + + const auto tensor_layout_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) ? 4u : 2u; + const auto tensor_layout_id = + inst->GetOperandAs(tensor_layout_index); + const auto tensor_layout = _.FindDef(tensor_layout_id); + if (!tensor_layout || _.FindDef(tensor_layout->type_id())->opcode() != + spv::Op::OpTypeTensorLayoutNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " TensorLayout " << _.getIdName(tensor_layout_id) + << " does not have a tensor layout type."; + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV) ? 5u : 3u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + const auto memory_access_mask = + inst->GetOperandAs(memory_access_index); + const auto tensor_operands_index = + memory_access_index + MemoryAccessNumWords(memory_access_mask); + const auto tensor_operands = + inst->GetOperandAs( + tensor_operands_index); + + if (inst->operands().size() < + tensor_operands_index + + TensorAddressingOperandsNumWords(tensor_operands)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " not enough tensor addressing operands."; + } + + uint32_t tensor_operand_index = tensor_operands_index + 1; + if ((tensor_operands & spv::TensorAddressingOperandsMask::TensorView) != + spv::TensorAddressingOperandsMask::MaskNone) { + const auto tensor_view_id = + inst->GetOperandAs(tensor_operand_index); + const auto tensor_view = _.FindDef(tensor_view_id); + if (!tensor_view || _.FindDef(tensor_view->type_id())->opcode() != + spv::Op::OpTypeTensorViewNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " TensorView " << _.getIdName(tensor_view_id) + << " does not have a tensor view type."; + } + + tensor_operand_index++; + } + + if ((tensor_operands & spv::TensorAddressingOperandsMask::DecodeFunc) != + spv::TensorAddressingOperandsMask::MaskNone) { + if (inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpCooperativeMatrixStoreTensorNV does not support DecodeFunc."; + } + const auto decode_func_id = + inst->GetOperandAs(tensor_operand_index); + const auto decode_func = _.FindDef(decode_func_id); + + if (!decode_func || decode_func->opcode() != spv::Op::OpFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " DecodeFunc " << _.getIdName(decode_func_id) + << " is not a function."; + } + + const auto component_type_index = 1; + const auto component_type_id = + matrix_type->GetOperandAs(component_type_index); + + const auto function_type = + _.FindDef(decode_func->GetOperandAs(3)); + if (function_type->GetOperandAs(1) != component_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " DecodeFunc " << _.getIdName(decode_func_id) + << " return type must match matrix component type."; + } + + const auto decode_ptr_type_id = function_type->GetOperandAs(2); + const auto decode_ptr_type = _.FindDef(decode_ptr_type_id); + auto decode_storage_class = + decode_ptr_type->GetOperandAs(storage_class_index); + + if (decode_storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " DecodeFunc " << _.getIdName(decode_func_id) + << " first parameter must be pointer to PhysicalStorageBuffer."; + } + + const auto tensor_layout_type = _.FindDef(tensor_layout->type_id()); + + for (uint32_t param = 3; param < 5; ++param) { + const auto param_type_id = function_type->GetOperandAs(param); + const auto param_type = _.FindDef(param_type_id); + if (param_type->opcode() != spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " DecodeFunc " << _.getIdName(decode_func_id) + << " second/third parameter must be array of 32-bit integer " + "with " + << " dimension equal to the tensor dimension."; + } + const auto length_index = 2u; + uint64_t array_length; + if (_.EvalConstantValUint64( + param_type->GetOperandAs(length_index), + &array_length)) { + const auto tensor_layout_dim_id = + tensor_layout_type->GetOperandAs(1); + uint64_t dim_value; + if (_.EvalConstantValUint64(tensor_layout_dim_id, &dim_value)) { + if (array_length != dim_value) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " DecodeFunc " + << _.getIdName(decode_func_id) + << " second/third parameter must be array of 32-bit integer " + "with " + << " dimension equal to the tensor dimension."; + } + } + } + } + + tensor_operand_index++; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateInt32Operand(ValidationState_t& _, const Instruction* inst, + uint32_t operand_index, + const char* opcode_name, + const char* operand_name) { + const auto type_id = + _.FindDef(inst->GetOperandAs(operand_index))->type_id(); + if (!_.IsIntScalarType(type_id) || _.GetBitWidth(type_id) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " " << operand_name << " type " + << _.getIdName(type_id) << " is not a 32 bit integer."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorPointer(ValidationState_t& _, + const Instruction* inst, + const char* opname, + uint32_t pointer_index) { + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == spv::AddressingModel::Logical) && + ((!_.features().variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (_.features().variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer_id) + << " is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer " << _.getIdName(pointer_id) + << " is not a pointer type."; + } + + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs(storage_class_index); + + if (storage_class != spv::StorageClass::Workgroup && + storage_class != spv::StorageClass::StorageBuffer && + storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " storage class for pointer type " + << _.getIdName(pointer_type_id) + << " is not Workgroup or StorageBuffer."; + } + + const auto pointee_id = pointer_type->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || + (pointee_type->opcode() != spv::Op::OpTypeArray && + pointee_type->opcode() != spv::Op::OpTypeRuntimeArray)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer->id()) + << "s Type must be an array type."; + } + + const auto array_elem_type_id = pointee_type->GetOperandAs(1); + auto array_elem_type = _.FindDef(array_elem_type_id); + if (!array_elem_type || !(_.IsIntScalarOrVectorType(array_elem_type_id) || + _.IsFloatScalarOrVectorType(array_elem_type_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer " << _.getIdName(pointer->id()) + << "s Type must be an array of scalar or vector type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorLoadStoreNV(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { + type_id = inst->type_id(); + opname = "spv::Op::OpCooperativeVectorLoadNV"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs(2))->type_id(); + opname = "spv::Op::OpCooperativeVectorStoreNV"; + } + + auto vector_type = _.FindDef(type_id); + + if (vector_type->opcode() != spv::Op::OpTypeCooperativeVectorNV) { + if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeVectorLoadNV Result Type " + << _.getIdName(type_id) << " is not a cooperative vector type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "spv::Op::OpCooperativeVectorStoreNV Object type " + << _.getIdName(type_id) << " is not a cooperative vector type."; + } + } + + const auto pointer_index = + (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 2u : 0u; + + if (auto error = + ValidateCooperativeVectorPointer(_, inst, opname, pointer_index)) { + return error; + } + + const auto memory_access_index = + (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 4u : 3u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorOuterProductNV(ValidationState_t& _, + const Instruction* inst) { + const auto pointer_index = 0u; + const auto opcode_name = + "spv::Op::OpCooperativeVectorOuterProductAccumulateNV"; + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + pointer_index)) { + return error; + } + + auto type_id = _.FindDef(inst->GetOperandAs(2))->type_id(); + auto a_type = _.FindDef(type_id); + + if (a_type->opcode() != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " A type " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + type_id = _.FindDef(inst->GetOperandAs(3))->type_id(); + auto b_type = _.FindDef(type_id); + + if (b_type->opcode() != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " B type " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + const auto a_component_type_id = a_type->GetOperandAs(1); + const auto b_component_type_id = b_type->GetOperandAs(1); + + if (a_component_type_id != b_component_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " A and B component types " + << _.getIdName(a_component_type_id) << " and " + << _.getIdName(b_component_type_id) << " do not match."; + } + + if (auto error = ValidateInt32Operand(_, inst, 1, opcode_name, "Offset")) { + return error; + } + + if (auto error = + ValidateInt32Operand(_, inst, 4, opcode_name, "MemoryLayout")) { + return error; + } + + if (auto error = ValidateInt32Operand(_, inst, 5, opcode_name, + "MatrixInterpretation")) { + return error; + } + + if (inst->operands().size() > 6) { + if (auto error = + ValidateInt32Operand(_, inst, 6, opcode_name, "MatrixStride")) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeVectorReduceSumNV(ValidationState_t& _, + const Instruction* inst) { + const auto opcode_name = "spv::Op::OpCooperativeVectorReduceSumAccumulateNV"; + const auto pointer_index = 0u; + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + pointer_index)) { + return error; + } + + auto type_id = _.FindDef(inst->GetOperandAs(2))->type_id(); + auto v_type = _.FindDef(type_id); + + if (v_type->opcode() != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " V type " << _.getIdName(type_id) + << " is not a cooperative vector type."; + } + + if (auto error = ValidateInt32Operand(_, inst, 1, opcode_name, "Offset")) { + return error; + } + + return SPV_SUCCESS; +} + +bool InterpretationIsPacked(spv::ComponentType interp) { + switch (interp) { + case spv::ComponentType::SignedInt8PackedNV: + case spv::ComponentType::UnsignedInt8PackedNV: + return true; + default: + return false; + } +} + +using std::get; + +spv_result_t ValidateCooperativeVectorMatrixMulNV(ValidationState_t& _, + const Instruction* inst) { + const bool has_bias = + inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV; + const auto opcode_name = has_bias + ? "spv::Op::OpCooperativeVectorMatrixMulAddNV" + : "spv::Op::OpCooperativeVectorMatrixMulNV"; + + const auto bias_offset = has_bias ? 3 : 0; + + const auto result_type_index = 0u; + const auto input_index = 2u; + const auto input_interpretation_index = 3u; + const auto matrix_index = 4u; + const auto matrix_interpretation_index = 6u; + const auto bias_index = 7u; + const auto bias_interpretation_index = 9u; + const auto m_index = 7u + bias_offset; + const auto k_index = 8u + bias_offset; + const auto memory_layout_index = 9u + bias_offset; + const auto transpose_index = 10u + bias_offset; + + const auto result_type_id = inst->GetOperandAs(result_type_index); + const auto input_id = inst->GetOperandAs(input_index); + const auto input_interpretation_id = + inst->GetOperandAs(input_interpretation_index); + const auto matrix_interpretation_id = + inst->GetOperandAs(matrix_interpretation_index); + const auto bias_interpretation_id = + inst->GetOperandAs(bias_interpretation_index); + const auto m_id = inst->GetOperandAs(m_index); + const auto k_id = inst->GetOperandAs(k_index); + const auto memory_layout_id = + inst->GetOperandAs(memory_layout_index); + const auto transpose_id = inst->GetOperandAs(transpose_index); + + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + matrix_index)) { + return error; + } + + if (inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV) { + if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name, + bias_index)) { + return error; + } + } + + const auto result_type = _.FindDef(result_type_id); + + if (result_type->opcode() != spv::Op::OpTypeCooperativeVectorNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result type " << _.getIdName(result_type_id) + << " is not a cooperative vector type."; + } + + const auto result_component_type_id = result_type->GetOperandAs(1u); + if (!(_.IsIntScalarType(result_component_type_id) && + _.GetBitWidth(result_component_type_id) == 32) && + !(_.IsFloatScalarType(result_component_type_id) && + (_.GetBitWidth(result_component_type_id) == 32 || + _.GetBitWidth(result_component_type_id) == 16))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result component type " + << _.getIdName(result_component_type_id) + << " is not a 32 bit int or 16/32 bit float."; + } + + const auto m_eval = _.EvalInt32IfConst(m_id); + const auto rc_eval = + _.EvalInt32IfConst(result_type->GetOperandAs(2u)); + if (get<1>(m_eval) && get<1>(rc_eval) && get<2>(m_eval) != get<2>(rc_eval)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " result type number of components " + << get<2>(rc_eval) << " does not match M " << get<2>(m_eval); + } + + const auto k_eval = _.EvalInt32IfConst(k_id); + + const auto input = _.FindDef(input_id); + const auto input_type = _.FindDef(input->type_id()); + const auto input_num_components_id = input_type->GetOperandAs(2u); + + auto input_interp_eval = _.EvalInt32IfConst(input_interpretation_id); + if (get<1>(input_interp_eval) && + !InterpretationIsPacked(spv::ComponentType{get<2>(input_interp_eval)})) { + const auto inc_eval = _.EvalInt32IfConst(input_num_components_id); + if (get<1>(inc_eval) && get<1>(k_eval) && + get<2>(inc_eval) != get<2>(k_eval)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " input number of components " + << get<2>(inc_eval) << " does not match K " << get<2>(k_eval); + } + } + + if (!_.IsBoolScalarType(_.FindDef(transpose_id)->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Transpose " << _.getIdName(transpose_id) + << " is not a scalar boolean."; + } + + const auto check_constant = [&](uint32_t id, + const char* operand_name) -> spv_result_t { + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " " << operand_name << " " + << _.getIdName(id) << " is not a constant instruction."; + } + return SPV_SUCCESS; + }; + + if (auto error = + check_constant(input_interpretation_id, "InputInterpretation")) { + return error; + } + if (auto error = + check_constant(matrix_interpretation_id, "MatrixInterpretation")) { + return error; + } + if (has_bias) { + if (auto error = + check_constant(bias_interpretation_id, "BiasInterpretation")) { + return error; + } + } + if (auto error = check_constant(m_id, "M")) { + return error; + } + if (auto error = check_constant(k_id, "K")) { + return error; + } + if (auto error = check_constant(memory_layout_id, "MemoryLayout")) { + return error; + } + if (auto error = check_constant(transpose_id, "Transpose")) { + return error; + } + + if (auto error = ValidateInt32Operand(_, inst, input_interpretation_index, + opcode_name, "InputInterpretation")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, matrix_interpretation_index, + opcode_name, "MatrixInterpretation")) { + return error; + } + if (has_bias) { + if (auto error = ValidateInt32Operand(_, inst, bias_interpretation_index, + opcode_name, "BiasInterpretation")) { + return error; + } + } + if (auto error = ValidateInt32Operand(_, inst, m_index, opcode_name, "M")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, k_index, opcode_name, "K")) { + return error; + } + if (auto error = ValidateInt32Operand(_, inst, memory_layout_index, + opcode_name, "MemoryLayout")) { + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidatePtrComparison(ValidationState_t& _, + const Instruction* inst) { + if (_.addressing_model() == spv::AddressingModel::Logical && + !_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Instruction cannot for logical addressing model be used without " + "a variable pointers capability"; + } + + const auto result_type = _.FindDef(inst->type_id()); + if (inst->opcode() == spv::Op::OpPtrDiff) { + if (!result_type || result_type->opcode() != spv::Op::OpTypeInt) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must be an integer scalar"; + } + } else { + if (!result_type || result_type->opcode() != spv::Op::OpTypeBool) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must be OpTypeBool"; + } + } + + const auto op1 = _.FindDef(inst->GetOperandAs(2u)); + const auto op2 = _.FindDef(inst->GetOperandAs(3u)); + const auto op1_type = _.FindDef(op1->type_id()); + const auto op2_type = _.FindDef(op2->type_id()); + if (!op1_type || (op1_type->opcode() != spv::Op::OpTypePointer && + op1_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand type must be a pointer"; + } + + if (!op2_type || (op2_type->opcode() != spv::Op::OpTypePointer && + op2_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand type must be a pointer"; + } + + if (inst->opcode() == spv::Op::OpPtrDiff) { + if (op1->type_id() != op2->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The types of Operand 1 and Operand 2 must match"; + } + } else { + const auto either_untyped = + op1_type->opcode() == spv::Op::OpTypeUntypedPointerKHR || + op2_type->opcode() == spv::Op::OpTypeUntypedPointerKHR; + if (either_untyped) { + const auto sc1 = op1_type->GetOperandAs(1); + const auto sc2 = op2_type->GetOperandAs(1); + if (sc1 != sc2) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer storage classes must match"; + } + } else if (op1->type_id() != op2->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The types of Operand 1 and Operand 2 must match"; + } + } + + spv::StorageClass sc = op1_type->GetOperandAs(1u); + if (_.addressing_model() == spv::AddressingModel::Logical) { + if (sc != spv::StorageClass::Workgroup && + sc != spv::StorageClass::StorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid pointer storage class"; + } + + if (sc == spv::StorageClass::Workgroup && + !_.HasCapability(spv::Capability::VariablePointers)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Workgroup storage class pointer requires VariablePointers " + "capability to be specified"; + } + } else if (sc == spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot use a pointer in the PhysicalStorageBuffer storage class"; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + if (auto error = ValidateVariable(_, inst)) return error; + break; + case spv::Op::OpLoad: + if (auto error = ValidateLoad(_, inst)) return error; + break; + case spv::Op::OpStore: + if (auto error = ValidateStore(_, inst)) return error; + break; + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + if (auto error = ValidateCopyMemory(_, inst)) return error; + break; + case spv::Op::OpPtrAccessChain: + case spv::Op::OpUntypedPtrAccessChainKHR: + case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: + if (auto error = ValidatePtrAccessChain(_, inst)) return error; + break; + case spv::Op::OpAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpUntypedAccessChainKHR: + case spv::Op::OpUntypedInBoundsAccessChainKHR: + if (auto error = ValidateAccessChain(_, inst)) return error; + break; + case spv::Op::OpRawAccessChainNV: + if (auto error = ValidateRawAccessChain(_, inst)) return error; + break; + case spv::Op::OpArrayLength: + case spv::Op::OpUntypedArrayLengthKHR: + if (auto error = ValidateArrayLength(_, inst)) return error; + break; + case spv::Op::OpCooperativeMatrixLoadNV: + case spv::Op::OpCooperativeMatrixStoreNV: + if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst)) + return error; + break; + case spv::Op::OpCooperativeMatrixLengthKHR: + case spv::Op::OpCooperativeMatrixLengthNV: + if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error; + break; + case spv::Op::OpCooperativeMatrixLoadKHR: + case spv::Op::OpCooperativeMatrixStoreKHR: + if (auto error = ValidateCooperativeMatrixLoadStoreKHR(_, inst)) + return error; + break; + case spv::Op::OpCooperativeMatrixLoadTensorNV: + case spv::Op::OpCooperativeMatrixStoreTensorNV: + if (auto error = ValidateCooperativeMatrixLoadStoreTensorNV(_, inst)) + return error; + break; + case spv::Op::OpCooperativeVectorLoadNV: + case spv::Op::OpCooperativeVectorStoreNV: + if (auto error = ValidateCooperativeVectorLoadStoreNV(_, inst)) + return error; + break; + case spv::Op::OpCooperativeVectorOuterProductAccumulateNV: + if (auto error = ValidateCooperativeVectorOuterProductNV(_, inst)) + return error; + break; + case spv::Op::OpCooperativeVectorReduceSumAccumulateNV: + if (auto error = ValidateCooperativeVectorReduceSumNV(_, inst)) + return error; + break; + case spv::Op::OpCooperativeVectorMatrixMulNV: + case spv::Op::OpCooperativeVectorMatrixMulAddNV: + if (auto error = ValidateCooperativeVectorMatrixMulNV(_, inst)) + return error; + break; + case spv::Op::OpPtrEqual: + case spv::Op::OpPtrNotEqual: + case spv::Op::OpPtrDiff: + if (auto error = ValidatePtrComparison(_, inst)) return error; + break; + case spv::Op::OpImageTexelPointer: + case spv::Op::OpGenericPtrMemSemantics: + default: + break; + } + + return SPV_SUCCESS; +} +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.cpp new file mode 100644 index 00000000..d94d37db --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.cpp @@ -0,0 +1,267 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate_memory_semantics.h" + +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateMemorySemantics(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index, + uint32_t memory_scope) { + const spv::Op opcode = inst->opcode(); + const auto id = inst->GetOperandAs(operand_index); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id); + + if (!is_int32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to be a 32-bit int"; + } + + if (!is_const_int32) { + if (_.HasCapability(spv::Capability::Shader) && + !_.HasCapability(spv::Capability::CooperativeMatrixNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics ids must be OpConstant when Shader " + "capability is present"; + } + + if (_.HasCapability(spv::Capability::Shader) && + _.HasCapability(spv::Capability::CooperativeMatrixNV) && + !spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present"; + } + return SPV_SUCCESS; + } + + const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits( + value & uint32_t(spv::MemorySemanticsMask::Acquire | + spv::MemorySemanticsMask::Release | + spv::MemorySemanticsMask::AcquireRelease | + spv::MemorySemanticsMask::SequentiallyConsistent)); + + if (num_memory_order_set_bits > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics can have at most one of the following " + "bits set: Acquire, Release, AcquireRelease or " + "SequentiallyConsistent"; + } + + if (_.memory_model() == spv::MemoryModel::VulkanKHR && + value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SequentiallyConsistent memory " + "semantics cannot be used with " + "the VulkanKHR memory model."; + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) && + !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeAvailableKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) && + !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeVisibleKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) && + !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics OutputMemoryKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & uint32_t(spv::MemorySemanticsMask::Volatile)) { + if (!_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics Volatile requires capability " + "VulkanMemoryModelKHR"; + } + + if (!spvOpcodeIsAtomicOp(inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics Volatile can only be used with atomic " + "instructions"; + } + } + + if (value & uint32_t(spv::MemorySemanticsMask::UniformMemory) && + !_.HasCapability(spv::Capability::Shader)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics UniformMemory requires capability Shader"; + } + + // Checking for spv::Capability::AtomicStorage is intentionally not done here. + // See https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning + // why. + + if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR | + spv::MemorySemanticsMask::MakeVisibleKHR)) { + const bool includes_storage_class = + value & uint32_t(spv::MemorySemanticsMask::UniformMemory | + spv::MemorySemanticsMask::SubgroupMemory | + spv::MemorySemanticsMask::WorkgroupMemory | + spv::MemorySemanticsMask::CrossWorkgroupMemory | + spv::MemorySemanticsMask::AtomicCounterMemory | + spv::MemorySemanticsMask::ImageMemory | + spv::MemorySemanticsMask::OutputMemoryKHR); + + if (!includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a storage class"; + } + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) && + !(value & uint32_t(spv::MemorySemanticsMask::Acquire | + spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": MakeVisibleKHR Memory Semantics also requires either Acquire " + "or AcquireRelease Memory Semantics"; + } + + if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) && + !(value & uint32_t(spv::MemorySemanticsMask::Release | + spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": MakeAvailableKHR Memory Semantics also requires either " + "Release or AcquireRelease Memory Semantics"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + const bool includes_storage_class = + value & uint32_t(spv::MemorySemanticsMask::UniformMemory | + spv::MemorySemanticsMask::WorkgroupMemory | + spv::MemorySemanticsMask::ImageMemory | + spv::MemorySemanticsMask::OutputMemoryKHR); + + if (opcode == spv::Op::OpMemoryBarrier && !num_memory_order_set_bits) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4732) << spvOpcodeString(opcode) + << ": Vulkan specification requires Memory Semantics to have " + "one of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent"; + } else if (opcode != spv::Op::OpMemoryBarrier && + num_memory_order_set_bits) { + // should leave only atomics and control barriers for Vulkan env + bool memory_is_int32 = false, memory_is_const_int32 = false; + uint32_t memory_value = 0; + std::tie(memory_is_int32, memory_is_const_int32, memory_value) = + _.EvalInt32IfConst(memory_scope); + if (memory_is_int32 && + spv::Scope(memory_value) == spv::Scope::Invocation) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4641) << spvOpcodeString(opcode) + << ": Vulkan specification requires Memory Semantics to be None " + "if used with Invocation Memory Scope"; + } + } + + if (opcode == spv::Op::OpMemoryBarrier && !includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4733) << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a Vulkan-supported " + "storage class"; + } + + if (opcode == spv::Op::OpControlBarrier && value) { + if (!num_memory_order_set_bits) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(10609) << spvOpcodeString(opcode) + << ": Vulkan specification requires non-zero Memory Semantics " + "to have one of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent"; + } + if (!includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4650) << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a Vulkan-supported " + "storage class if Memory Semantics is not None"; + } + } + } + + if (opcode == spv::Op::OpAtomicFlagClear && + (value & uint32_t(spv::MemorySemanticsMask::Acquire) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics Acquire and AcquireRelease cannot be used " + "with " + << spvOpcodeString(opcode); + } + + if (opcode == spv::Op::OpAtomicCompareExchange && operand_index == 5 && + (value & uint32_t(spv::MemorySemanticsMask::Release) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics Release and AcquireRelease cannot be " + "used " + "for operand Unequal"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (opcode == spv::Op::OpAtomicLoad && + (value & uint32_t(spv::MemorySemanticsMask::Release) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) || + value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4731) + << "Vulkan spec disallows OpAtomicLoad with Memory Semantics " + "Release, AcquireRelease and SequentiallyConsistent"; + } + + if (opcode == spv::Op::OpAtomicStore && + (value & uint32_t(spv::MemorySemanticsMask::Acquire) || + value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) || + value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4730) + << "Vulkan spec disallows OpAtomicStore with Memory Semantics " + "Acquire, AcquireRelease and SequentiallyConsistent"; + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.h b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.h new file mode 100644 index 00000000..9e6f93a3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_memory_semantics.h @@ -0,0 +1,29 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of memory semantics for SPIR-V instructions. + +#include "source/opcode.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateMemorySemantics(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index, + uint32_t memory_scope); + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_mesh_shading.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_mesh_shading.cpp new file mode 100644 index 00000000..3bd1dbd3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_mesh_shading.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray query instructions from SPV_KHR_ray_query + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +bool IsInterfaceVariable(ValidationState_t& _, const Instruction* inst, + spv::ExecutionModel model) { + bool foundInterface = false; + for (auto entry_point : _.entry_points()) { + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(model) == models->end()) return false; + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + for (auto interface : desc.interfaces) { + if (inst->id() == interface) { + foundInterface = true; + break; + } + } + } + } + return foundInterface; +} + +spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + switch (opcode) { + case spv::Op::OpEmitMeshTasksEXT: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::TaskEXT) { + if (message) { + *message = + "OpEmitMeshTasksEXT requires TaskEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t group_count_x = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(group_count_x) || + _.GetBitWidth(group_count_x) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count X must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_y = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(group_count_y) || + _.GetBitWidth(group_count_y) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Y must be a 32-bit unsigned int scalar"; + } + + const uint32_t group_count_z = _.GetOperandTypeId(inst, 2); + if (!_.IsUnsignedIntScalarType(group_count_z) || + _.GetBitWidth(group_count_z) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Group Count Z must be a 32-bit unsigned int scalar"; + } + + if (inst->operands().size() == 4) { + const auto payload = _.FindDef(inst->GetOperandAs(3)); + if (payload->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } + if (payload->GetOperandAs(2) != + spv::StorageClass::TaskPayloadWorkgroupEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload OpVariable must have a storage class of " + "TaskPayloadWorkgroupEXT"; + } + } + break; + } + + case spv::Op::OpSetMeshOutputsEXT: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::MeshEXT) { + if (message) { + *message = + "OpSetMeshOutputsEXT requires MeshEXT execution model"; + } + return false; + } + return true; + }); + + const uint32_t vertex_count = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(vertex_count) || + _.GetBitWidth(vertex_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vertex Count must be a 32-bit unsigned int scalar"; + } + + const uint32_t primitive_count = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(primitive_count) || + _.GetBitWidth(primitive_count) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Primitive Count must be a 32-bit unsigned int scalar"; + } + + break; + } + + case spv::Op::OpWritePackedPrimitiveIndices4x8NV: { + // No validation rules (for the moment). + break; + } + case spv::Op::OpVariable: { + if (_.HasCapability(spv::Capability::MeshShadingEXT)) { + bool meshInterfaceVar = + IsInterfaceVariable(_, inst, spv::ExecutionModel::MeshEXT); + bool fragInterfaceVar = + IsInterfaceVariable(_, inst, spv::ExecutionModel::Fragment); + + const spv::StorageClass storage_class = + inst->GetOperandAs(2); + bool storage_output = (storage_class == spv::StorageClass::Output); + bool storage_input = (storage_class == spv::StorageClass::Input); + + if (_.HasDecoration(inst->id(), spv::Decoration::PerPrimitiveEXT)) { + if (fragInterfaceVar && !storage_input) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Input Storage Class in the Fragment " + "Execution Model."; + } + + if (meshInterfaceVar && !storage_output) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4336) + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Output Storage Class in the " + "Storage Class in the MeshEXT Execution Model."; + } + } + } + break; + } + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_misc.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_misc.cpp new file mode 100644 index 00000000..a404134b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_misc.cpp @@ -0,0 +1,214 @@ +// Copyright (c) 2018 Google LLC. +// Copyright (c) 2019 NVIDIA Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) { + if (_.IsVoidType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot create undefined values with void type"; + } + if (_.HasCapability(spv::Capability::Shader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + !_.IsPointerType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot create undefined values with 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateShaderClock(ValidationState_t& _, + const Instruction* inst) { + const uint32_t scope = inst->GetOperandAs(2); + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + if (is_const_int32) { + spv::Scope scope_val{value}; + if (spvIsVulkanEnv(_.context()->target_env)) { + if (scope_val != spv::Scope::Subgroup && + scope_val != spv::Scope::Device) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4652) << "Scope must be Subgroup or Device"; + } + } else if (spvIsOpenCLEnv(_.context()->target_env)) { + if (scope_val != spv::Scope::Workgroup && + scope_val != spv::Scope::Subgroup && + scope_val != spv::Scope::Device) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope must be Subgroup, Workgroup, or Device"; + } + } + } + + // Result Type must be a 64 - bit unsigned integer type or + // a vector of two - components of 32 - + // bit unsigned integer type + const uint32_t result_type = inst->type_id(); + if (!_.IsUnsigned64BitHandle(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " + "vector of two components" + " of unsigned integer" + " or 64bit unsigned integer"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateAssumeTrue(ValidationState_t& _, const Instruction* inst) { + const auto operand_type_id = _.GetOperandTypeId(inst, 0); + if (!operand_type_id || !_.IsBoolScalarType(operand_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Value operand of OpAssumeTrueKHR must be a boolean scalar"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateExpect(ValidationState_t& _, const Instruction* inst) { + const auto result_type = inst->type_id(); + if (!_.IsBoolScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result of OpExpectKHR must be a scalar or vector of integer " + "type or boolean type"; + } + + if (_.GetOperandTypeId(inst, 2) != result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Type of Value operand of OpExpectKHR does not match the result " + "type "; + } + if (_.GetOperandTypeId(inst, 3) != result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Type of ExpectedValue operand of OpExpectKHR does not match the " + "result type "; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpUndef: + if (auto error = ValidateUndef(_, inst)) return error; + break; + default: + break; + } + switch (inst->opcode()) { + case spv::Op::OpBeginInvocationInterlockEXT: + case spv::Op::OpEndInvocationInterlockEXT: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " + "require Fragment execution model"); + + _.function(inst->function()->id()) + ->RegisterLimitation([](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* execution_modes = + state.GetExecutionModes(entry_point->id()); + + auto find_interlock = [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::PixelInterlockOrderedEXT: + case spv::ExecutionMode::PixelInterlockUnorderedEXT: + case spv::ExecutionMode::SampleInterlockOrderedEXT: + case spv::ExecutionMode::SampleInterlockUnorderedEXT: + case spv::ExecutionMode::ShadingRateInterlockOrderedEXT: + case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT: + return true; + default: + return false; + } + }; + + bool found = false; + if (execution_modes) { + auto i = std::find_if(execution_modes->begin(), + execution_modes->end(), find_interlock); + found = (i != execution_modes->end()); + } + + if (!found) { + *message = + "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " + "require a fragment shader interlock execution mode."; + return false; + } + return true; + }); + break; + case spv::Op::OpDemoteToHelperInvocationEXT: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + "OpDemoteToHelperInvocationEXT requires Fragment execution " + "model"); + break; + case spv::Op::OpIsHelperInvocationEXT: { + const uint32_t result_type = inst->type_id(); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Fragment, + "OpIsHelperInvocationEXT requires Fragment execution model"); + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(inst->opcode()); + break; + } + case spv::Op::OpReadClockKHR: + if (auto error = ValidateShaderClock(_, inst)) { + return error; + } + break; + case spv::Op::OpAssumeTrueKHR: + if (auto error = ValidateAssumeTrue(_, inst)) { + return error; + } + break; + case spv::Op::OpExpectKHR: + if (auto error = ValidateExpect(_, inst)) { + return error; + } + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_mode_setting.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_mode_setting.cpp new file mode 100644 index 00000000..baa98ce5 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_mode_setting.cpp @@ -0,0 +1,970 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { + const auto entry_point_id = inst->GetOperandAs(1); + auto entry_point = _.FindDef(entry_point_id); + if (!entry_point || spv::Op::OpFunction != entry_point->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point " << _.getIdName(entry_point_id) + << " is not a function."; + } + + // Only check the shader execution models + const spv::ExecutionModel execution_model = + inst->GetOperandAs(0); + if (execution_model != spv::ExecutionModel::Kernel) { + const auto entry_point_type_id = entry_point->GetOperandAs(3); + const auto entry_point_type = _.FindDef(entry_point_type_id); + if (!entry_point_type || 3 != entry_point_type->words().size()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4633) << "OpEntryPoint Entry Point " + << _.getIdName(entry_point_id) + << "s function parameter count is not zero."; + } + } + + auto return_type = _.FindDef(entry_point->type_id()); + if (!return_type || spv::Op::OpTypeVoid != return_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4633) << "OpEntryPoint Entry Point " + << _.getIdName(entry_point_id) + << "s function return type is not void."; + } + + const auto* execution_modes = _.GetExecutionModes(entry_point_id); + if (_.HasCapability(spv::Capability::Shader)) { + switch (execution_model) { + case spv::ExecutionModel::Fragment: + if (execution_modes && + execution_modes->count(spv::ExecutionMode::OriginUpperLeft) && + execution_modes->count(spv::ExecutionMode::OriginLowerLeft)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can only specify " + "one of OriginUpperLeft or OriginLowerLeft execution " + "modes."; + } + if (!execution_modes || + (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) && + !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points require either an " + "OriginUpperLeft or OriginLowerLeft execution mode."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::DepthGreater: + case spv::ExecutionMode::DepthLess: + case spv::ExecutionMode::DepthUnchanged: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::PixelInterlockOrderedEXT: + case spv::ExecutionMode::PixelInterlockUnorderedEXT: + case spv::ExecutionMode::SampleInterlockOrderedEXT: + case spv::ExecutionMode::SampleInterlockUnorderedEXT: + case spv::ExecutionMode::ShadingRateInterlockOrderedEXT: + case spv::ExecutionMode:: + ShadingRateInterlockUnorderedEXT: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one fragment shader interlock execution mode."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::StencilRefUnchangedFrontAMD: + case spv::ExecutionMode::StencilRefLessFrontAMD: + case spv::ExecutionMode::StencilRefGreaterFrontAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedFrontAMD, " + "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD " + "execution modes."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::StencilRefUnchangedBackAMD: + case spv::ExecutionMode::StencilRefLessBackAMD: + case spv::ExecutionMode::StencilRefGreaterBackAMD: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of StencilRefUnchangedBackAMD, " + "StencilRefLessBackAMD or StencilRefGreaterBackAMD " + "execution modes."; + } + break; + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::SpacingEqual: + case spv::ExecutionMode::SpacingFractionalEven: + case spv::ExecutionMode::SpacingFractionalOdd: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::Triangles: + case spv::ExecutionMode::Quads: + case spv::ExecutionMode::Isolines: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of Triangles, Quads or Isolines execution modes."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::VertexOrderCw: + case spv::ExecutionMode::VertexOrderCcw: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of VertexOrderCw or VertexOrderCcw execution " + "modes."; + } + break; + case spv::ExecutionModel::Geometry: + if (!execution_modes || + 1 != std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::InputPoints: + case spv::ExecutionMode::InputLines: + case spv::ExecutionMode::InputLinesAdjacency: + case spv::ExecutionMode::Triangles: + case spv::ExecutionMode::InputTrianglesAdjacency: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Geometry execution model entry points must specify " + "exactly one of InputPoints, InputLines, " + "InputLinesAdjacency, Triangles or InputTrianglesAdjacency " + "execution modes."; + } + if (!execution_modes || + 1 != std::count_if(execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::OutputPoints: + case spv::ExecutionMode::OutputLineStrip: + case spv::ExecutionMode::OutputTriangleStrip: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Geometry execution model entry points must specify " + "exactly one of OutputPoints, OutputLineStrip or " + "OutputTriangleStrip execution modes."; + } + break; + case spv::ExecutionModel::MeshEXT: + if (!execution_modes || + 1 != std::count_if(execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::OutputPoints: + case spv::ExecutionMode::OutputLinesEXT: + case spv::ExecutionMode::OutputTrianglesEXT: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MeshEXT execution model entry points must specify exactly " + "one of OutputPoints, OutputLinesEXT, or " + "OutputTrianglesEXT Execution Modes."; + } else if (2 != std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const spv::ExecutionMode& mode) { + switch (mode) { + case spv::ExecutionMode::OutputPrimitivesEXT: + case spv::ExecutionMode::OutputVertices: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MeshEXT execution model entry points must specify both " + "OutputPrimitivesEXT and OutputVertices Execution Modes."; + } + break; + default: + break; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + switch (execution_model) { + case spv::ExecutionModel::GLCompute: + if (!execution_modes || + !execution_modes->count(spv::ExecutionMode::LocalSize)) { + bool ok = false; + for (auto& i : _.ordered_instructions()) { + if (i.opcode() == spv::Op::OpDecorate) { + if (i.operands().size() > 2) { + if (i.GetOperandAs(1) == + spv::Decoration::BuiltIn && + i.GetOperandAs(2) == + spv::BuiltIn::WorkgroupSize) { + ok = true; + break; + } + } + } + if (i.opcode() == spv::Op::OpExecutionModeId) { + const auto mode = i.GetOperandAs(1); + if (mode == spv::ExecutionMode::LocalSizeId) { + ok = true; + break; + } + } + } + if (!ok) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6426) + << "In the Vulkan environment, GLCompute execution model " + "entry points require either the LocalSize or " + "LocalSizeId execution mode or an object decorated with " + "WorkgroupSize must be specified."; + } + } + break; + default: + break; + } + } + + if (_.EntryPointHasLocalSizeOrId(entry_point_id)) { + const Instruction* local_size_inst = + _.EntryPointLocalSizeOrId(entry_point_id); + if (local_size_inst) { + const auto mode = local_size_inst->GetOperandAs(1); + const uint32_t operand_x = local_size_inst->GetOperandAs(2); + const uint32_t operand_y = local_size_inst->GetOperandAs(3); + const uint32_t operand_z = local_size_inst->GetOperandAs(4); + if (mode == spv::ExecutionMode::LocalSize) { + if ((operand_x * operand_y * operand_z) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << "Local Size execution mode must not have a product of zero " + "(X " + "= " + << operand_x << ", Y = " << operand_y << ", Z = " << operand_z + << ")."; + } + } else if (mode == spv::ExecutionMode::LocalSizeId) { + // can only validate product if static and not spec constant + // (This is done for us in EvalConstantValUint64) + uint64_t x_size, y_size, z_size; + bool static_x = _.EvalConstantValUint64(operand_x, &x_size); + bool static_y = _.EvalConstantValUint64(operand_y, &y_size); + bool static_z = _.EvalConstantValUint64(operand_z, &z_size); + if (static_x && static_y && static_z && + ((x_size * y_size * z_size) == 0)) { + return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst) + << "Local Size Id execution mode must not have a product of " + "zero " + "(X = " + << x_size << ", Y = " << y_size << ", Z = " << z_size << ")."; + } + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExecutionMode(ValidationState_t& _, + const Instruction* inst) { + const auto entry_point_id = inst->GetOperandAs(0); + const auto found = std::find(_.entry_points().cbegin(), + _.entry_points().cend(), entry_point_id); + if (found == _.entry_points().cend()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpExecutionMode Entry Point " << _.getIdName(entry_point_id) + << " is not the Entry Point " + "operand of an OpEntryPoint."; + } + + const auto mode = inst->GetOperandAs(1); + if (inst->opcode() == spv::Op::OpExecutionModeId) { + bool valid_mode = false; + switch (mode) { + case spv::ExecutionMode::SubgroupsPerWorkgroupId: + case spv::ExecutionMode::LocalSizeHintId: + case spv::ExecutionMode::LocalSizeId: + case spv::ExecutionMode::FPFastMathDefault: + case spv::ExecutionMode::MaximumRegistersIdINTEL: + case spv::ExecutionMode::IsApiEntryAMDX: + case spv::ExecutionMode::MaxNodeRecursionAMDX: + case spv::ExecutionMode::MaxNumWorkgroupsAMDX: + case spv::ExecutionMode::ShaderIndexAMDX: + case spv::ExecutionMode::SharesInputWithAMDX: + case spv::ExecutionMode::StaticNumWorkgroupsAMDX: + valid_mode = true; + break; + default: + valid_mode = false; + break; + } + if (!valid_mode) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpExecutionModeId is only valid when the Mode operand is an " + "execution mode that takes Extra Operands that are id " + "operands."; + } + + size_t operand_count = inst->operands().size(); + for (size_t i = 2; i < operand_count; ++i) { + const auto operand_id = inst->GetOperandAs(i); + const auto* operand_inst = _.FindDef(operand_id); + switch (mode) { + case spv::ExecutionMode::SubgroupsPerWorkgroupId: + case spv::ExecutionMode::LocalSizeHintId: + case spv::ExecutionMode::LocalSizeId: + case spv::ExecutionMode::IsApiEntryAMDX: + case spv::ExecutionMode::MaxNodeRecursionAMDX: + case spv::ExecutionMode::MaxNumWorkgroupsAMDX: + case spv::ExecutionMode::ShaderIndexAMDX: + case spv::ExecutionMode::SharesInputWithAMDX: + case spv::ExecutionMode::StaticNumWorkgroupsAMDX: + if (!spvOpcodeIsConstant(operand_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For OpExecutionModeId all Extra Operand ids must be " + "constant instructions."; + } + break; + case spv::ExecutionMode::FPFastMathDefault: + if (i == 2) { + if (!_.IsFloatScalarType(operand_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Target Type operand must be a floating-point " + "scalar type"; + } + } else { + bool is_int32 = false; + bool is_const = false; + uint32_t value = 0; + std::tie(is_int32, is_const, value) = + _.EvalInt32IfConst(operand_id); + if (is_int32 && is_const) { + // Valid values include up to 0x00040000 (AllowTransform). + uint32_t invalid_mask = 0xfff80000; + if ((invalid_mask & value) != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand is an invalid bitmask " + "value"; + } + if (value & + static_cast(spv::FPFastMathModeMask::Fast)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must not include Fast"; + } + const auto reassoc_contract = + spv::FPFastMathModeMask::AllowContract | + spv::FPFastMathModeMask::AllowReassoc; + if ((value & static_cast( + spv::FPFastMathModeMask::AllowTransform)) != 0 && + ((value & static_cast(reassoc_contract)) != + static_cast(reassoc_contract))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must include " + "AllowContract and AllowReassoc when AllowTransform " + "is specified"; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Fast Math Default operand must be a " + "non-specialization constant"; + } + } + break; + default: + break; + } + } + } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId || + mode == spv::ExecutionMode::LocalSizeHintId || + mode == spv::ExecutionMode::LocalSizeId || + mode == spv::ExecutionMode::FPFastMathDefault || + mode == spv::ExecutionMode::IsApiEntryAMDX || + mode == spv::ExecutionMode::MaxNodeRecursionAMDX || + mode == spv::ExecutionMode::MaxNumWorkgroupsAMDX || + mode == spv::ExecutionMode::ShaderIndexAMDX || + mode == spv::ExecutionMode::SharesInputWithAMDX || + mode == spv::ExecutionMode::StaticNumWorkgroupsAMDX) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands."; + } + + const auto* models = _.GetExecutionModels(entry_point_id); + switch (mode) { + case spv::ExecutionMode::Invocations: + case spv::ExecutionMode::InputPoints: + case spv::ExecutionMode::InputLines: + case spv::ExecutionMode::InputLinesAdjacency: + case spv::ExecutionMode::InputTrianglesAdjacency: + case spv::ExecutionMode::OutputLineStrip: + case spv::ExecutionMode::OutputTriangleStrip: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return model == spv::ExecutionModel::Geometry; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry execution " + "model."; + } + break; + case spv::ExecutionMode::OutputPoints: + if (!std::all_of( + models->begin(), models->end(), + [&_](const spv::ExecutionModel& model) { + switch (model) { + case spv::ExecutionModel::Geometry: + return true; + case spv::ExecutionModel::MeshNV: + return _.HasCapability(spv::Capability::MeshShadingNV); + case spv::ExecutionModel::MeshEXT: + return _.HasCapability(spv::Capability::MeshShadingEXT); + default: + return false; + } + })) { + if (_.HasCapability(spv::Capability::MeshShadingNV) || + _.HasCapability(spv::Capability::MeshShadingEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry " + "MeshNV or MeshEXT execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry " + "execution " + "model."; + } + } + break; + case spv::ExecutionMode::SpacingEqual: + case spv::ExecutionMode::SpacingFractionalEven: + case spv::ExecutionMode::SpacingFractionalOdd: + case spv::ExecutionMode::VertexOrderCw: + case spv::ExecutionMode::VertexOrderCcw: + case spv::ExecutionMode::PointMode: + case spv::ExecutionMode::Quads: + case spv::ExecutionMode::Isolines: + if (!std::all_of( + models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return (model == spv::ExecutionModel::TessellationControl) || + (model == spv::ExecutionModel::TessellationEvaluation); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a tessellation " + "execution model."; + } + break; + case spv::ExecutionMode::Triangles: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + switch (model) { + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry or " + "tessellation execution model."; + } + break; + case spv::ExecutionMode::OutputVertices: + if (!std::all_of( + models->begin(), models->end(), + [&_](const spv::ExecutionModel& model) { + switch (model) { + case spv::ExecutionModel::Geometry: + case spv::ExecutionModel::TessellationControl: + case spv::ExecutionModel::TessellationEvaluation: + return true; + case spv::ExecutionModel::MeshNV: + return _.HasCapability(spv::Capability::MeshShadingNV); + case spv::ExecutionModel::MeshEXT: + return _.HasCapability(spv::Capability::MeshShadingEXT); + default: + return false; + } + })) { + if (_.HasCapability(spv::Capability::MeshShadingNV) || + _.HasCapability(spv::Capability::MeshShadingEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry, " + "tessellation, MeshNV or MeshEXT execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry or " + "tessellation execution model."; + } + } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7330) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputVertices Execution Mode must be greater than 0"; + } + } + break; + case spv::ExecutionMode::OutputLinesEXT: + case spv::ExecutionMode::OutputTrianglesEXT: + case spv::ExecutionMode::OutputPrimitivesEXT: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return (model == spv::ExecutionModel::MeshEXT || + model == spv::ExecutionModel::MeshNV); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the MeshEXT or MeshNV " + "execution " + "model."; + } + if (mode == spv::ExecutionMode::OutputPrimitivesEXT && + spvIsVulkanEnv(_.context()->target_env)) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7331) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputPrimitivesEXT Execution Mode must be greater than 0"; + } + } + break; + case spv::ExecutionMode::QuadDerivativesKHR: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return (model == spv::ExecutionModel::Fragment || + model == spv::ExecutionModel::GLCompute); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Fragment or " + "GLCompute execution model."; + } + break; + case spv::ExecutionMode::PixelCenterInteger: + case spv::ExecutionMode::OriginUpperLeft: + case spv::ExecutionMode::OriginLowerLeft: + case spv::ExecutionMode::EarlyFragmentTests: + case spv::ExecutionMode::DepthReplacing: + case spv::ExecutionMode::DepthGreater: + case spv::ExecutionMode::DepthLess: + case spv::ExecutionMode::DepthUnchanged: + case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT: + case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT: + case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT: + case spv::ExecutionMode::PixelInterlockOrderedEXT: + case spv::ExecutionMode::PixelInterlockUnorderedEXT: + case spv::ExecutionMode::SampleInterlockOrderedEXT: + case spv::ExecutionMode::SampleInterlockUnorderedEXT: + case spv::ExecutionMode::ShadingRateInterlockOrderedEXT: + case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT: + case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD: + case spv::ExecutionMode::StencilRefUnchangedFrontAMD: + case spv::ExecutionMode::StencilRefGreaterFrontAMD: + case spv::ExecutionMode::StencilRefLessFrontAMD: + case spv::ExecutionMode::StencilRefUnchangedBackAMD: + case spv::ExecutionMode::StencilRefGreaterBackAMD: + case spv::ExecutionMode::StencilRefLessBackAMD: + case spv::ExecutionMode::RequireFullQuadsKHR: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return model == spv::ExecutionModel::Fragment; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Fragment execution " + "model."; + } + break; + case spv::ExecutionMode::LocalSizeHint: + case spv::ExecutionMode::VecTypeHint: + case spv::ExecutionMode::ContractionOff: + case spv::ExecutionMode::LocalSizeHintId: + if (!std::all_of(models->begin(), models->end(), + [](const spv::ExecutionModel& model) { + return model == spv::ExecutionModel::Kernel; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Kernel execution " + "model."; + } + break; + case spv::ExecutionMode::LocalSize: + case spv::ExecutionMode::LocalSizeId: + if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed()) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "LocalSizeId mode is not allowed by the current environment."; + + if (!std::all_of( + models->begin(), models->end(), + [&_](const spv::ExecutionModel& model) { + switch (model) { + case spv::ExecutionModel::Kernel: + case spv::ExecutionModel::GLCompute: + return true; + case spv::ExecutionModel::TaskNV: + case spv::ExecutionModel::MeshNV: + return _.HasCapability(spv::Capability::MeshShadingNV); + case spv::ExecutionModel::TaskEXT: + case spv::ExecutionModel::MeshEXT: + return _.HasCapability(spv::Capability::MeshShadingEXT); + default: + return false; + } + })) { + if (_.HasCapability(spv::Capability::MeshShadingNV) || + _.HasCapability(spv::Capability::MeshShadingEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Kernel, GLCompute, " + "MeshNV, MeshEXT, TaskNV or TaskEXT execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Kernel or " + "GLCompute " + "execution model."; + } + } + default: + break; + } + + if (mode == spv::ExecutionMode::FPFastMathDefault) { + const auto* modes = _.GetExecutionModes(entry_point_id); + if (modes && modes->count(spv::ExecutionMode::ContractionOff)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "FPFastMathDefault and ContractionOff execution modes cannot " + "be applied to the same entry point"; + } + if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "FPFastMathDefault and SignedZeroInfNanPreserve execution " + "modes cannot be applied to the same entry point"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (mode == spv::ExecutionMode::OriginLowerLeft) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4653) + << "In the Vulkan environment, the OriginLowerLeft execution mode " + "must not be used."; + } + if (mode == spv::ExecutionMode::PixelCenterInteger) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4654) + << "In the Vulkan environment, the PixelCenterInteger execution " + "mode must not be used."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateMemoryModel(ValidationState_t& _, + const Instruction* inst) { + // Already produced an error if multiple memory model instructions are + // present. + if (_.memory_model() != spv::MemoryModel::VulkanKHR && + _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "VulkanMemoryModelKHR capability must only be specified if " + "the VulkanKHR memory model is used."; + } + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if ((_.addressing_model() != spv::AddressingModel::Physical32) && + (_.addressing_model() != spv::AddressingModel::Physical64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Addressing model must be Physical32 or Physical64 " + << "in the OpenCL environment."; + } + if (_.memory_model() != spv::MemoryModel::OpenCL) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory model must be OpenCL in the OpenCL environment."; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((_.addressing_model() != spv::AddressingModel::Logical) && + (_.addressing_model() != + spv::AddressingModel::PhysicalStorageBuffer64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4635) + << "Addressing model must be Logical or PhysicalStorageBuffer64 " + << "in the Vulkan environment."; + } + } + return SPV_SUCCESS; +} + +bool PerEntryExecutionMode(spv::ExecutionMode mode) { + switch (mode) { + // These execution modes can be specified multiple times per entry point. + case spv::ExecutionMode::DenormPreserve: + case spv::ExecutionMode::DenormFlushToZero: + case spv::ExecutionMode::SignedZeroInfNanPreserve: + case spv::ExecutionMode::RoundingModeRTE: + case spv::ExecutionMode::RoundingModeRTZ: + case spv::ExecutionMode::FPFastMathDefault: + case spv::ExecutionMode::RoundingModeRTPINTEL: + case spv::ExecutionMode::RoundingModeRTNINTEL: + case spv::ExecutionMode::FloatingPointModeALTINTEL: + case spv::ExecutionMode::FloatingPointModeIEEEINTEL: + return false; + default: + return true; + } +} + +spv_result_t ValidateCapability(ValidationState_t& _, const Instruction* inst) { + auto cap = inst->GetOperandAs(0); + if (cap == spv::Capability::CooperativeMatrixKHR) { + if (_.HasCapability(spv::Capability::Shader) && + !_.HasCapability(spv::Capability::VulkanMemoryModel)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "If the Shader and CooperativeMatrixKHR capabilities are " + "declared, the VulkanMemoryModel capability must also be " + "declared"; + } + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateFloatControls2(ValidationState_t& _) { + std::unordered_set fp_fast_math_default_entry_points; + for (auto entry_point : _.entry_points()) { + const auto* exec_modes = _.GetExecutionModes(entry_point); + if (exec_modes && + exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) { + fp_fast_math_default_entry_points.insert(entry_point); + } + } + + std::vector> worklist; + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() != spv::Op::OpDecorate) { + continue; + } + + const auto decoration = inst.GetOperandAs(1); + const auto target_id = inst.GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (decoration == spv::Decoration::NoContraction) { + worklist.push_back(std::make_pair(target, decoration)); + } else if (decoration == spv::Decoration::FPFastMathMode) { + auto mask = inst.GetOperandAs(2); + if ((mask & spv::FPFastMathModeMask::Fast) != + spv::FPFastMathModeMask::MaskNone) { + worklist.push_back(std::make_pair(target, decoration)); + } + } + } + + std::unordered_set visited; + while (!worklist.empty()) { + const auto inst = worklist.back().first; + const auto decoration = worklist.back().second; + worklist.pop_back(); + + if (!visited.insert(inst).second) { + continue; + } + + const auto function = inst->function(); + if (function) { + const auto& entry_points = _.FunctionEntryPoints(function->id()); + for (auto entry_point : entry_points) { + if (fp_fast_math_default_entry_points.count(entry_point)) { + const std::string dec = decoration == spv::Decoration::NoContraction + ? "NoContraction" + : "FPFastMathMode Fast"; + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << dec + << " cannot be used by an entry point with the " + "FPFastMathDefault execution mode"; + } + } + } else { + for (const auto& pair : inst->uses()) { + worklist.push_back(std::make_pair(pair.first, decoration)); + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpEntryPoint: + if (auto error = ValidateEntryPoint(_, inst)) return error; + break; + case spv::Op::OpExecutionMode: + case spv::Op::OpExecutionModeId: + if (auto error = ValidateExecutionMode(_, inst)) return error; + break; + case spv::Op::OpMemoryModel: + if (auto error = ValidateMemoryModel(_, inst)) return error; + break; + case spv::Op::OpCapability: + if (auto error = ValidateCapability(_, inst)) return error; + break; + default: + break; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) { + using PerEntryKey = std::tuple; + using PerOperandKey = std::tuple; + std::set seen_per_entry; + std::set seen_per_operand; + + const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string { + spv_operand_desc desc = nullptr; + if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE, + static_cast(mode), + &desc) == SPV_SUCCESS) { + return std::string(desc->name); + } + return "Unknown"; + }; + + for (const auto& inst : _.ordered_instructions()) { + if (inst.opcode() != spv::Op::OpExecutionMode && + inst.opcode() != spv::Op::OpExecutionModeId) { + continue; + } + + const auto entry = inst.GetOperandAs(0); + const auto mode = inst.GetOperandAs(1); + if (PerEntryExecutionMode(mode)) { + if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << lookupMode(mode) + << " execution mode must not be specified multiple times per " + "entry point"; + } + } else { + // Execution modes allowed multiple times all take a single operand. + const auto operand = inst.GetOperandAs(2); + if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand)) + .second) { + return _.diag(SPV_ERROR_INVALID_ID, &inst) + << lookupMode(mode) + << " execution mode must not be specified multiple times for " + "the same entry point and operands"; + } + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_non_uniform.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_non_uniform.cpp new file mode 100644 index 00000000..fd422a50 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_non_uniform.cpp @@ -0,0 +1,496 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of barrier SPIR-V instructions. + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateGroupNonUniformElect(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformAnyAll(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + if (!_.IsBoolScalarType(_.GetOperandTypeId(inst, 3))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Predicate must be a boolean scalar type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformAllEqual(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar type"; + } + + const auto value_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(value_type) && + !_.IsIntScalarOrVectorType(value_type) && + !_.IsBoolScalarOrVectorType(value_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a scalar or vector of integer, floating-point, or " + "boolean type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBroadcastShuffle(ValidationState_t& _, + const Instruction* inst) { + const auto type_id = inst->type_id(); + if (!_.IsFloatScalarOrVectorType(type_id) && + !_.IsIntScalarOrVectorType(type_id) && + !_.IsBoolScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a scalar or vector of integer, floating-point, " + "or boolean type"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (value_type_id != type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Value must match the Result type"; + } + + const auto GetOperandName = [](const spv::Op opcode) { + std::string operand; + switch (opcode) { + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformShuffle: + operand = "Id"; + break; + case spv::Op::OpGroupNonUniformShuffleXor: + operand = "Mask"; + break; + case spv::Op::OpGroupNonUniformQuadBroadcast: + operand = "Index"; + break; + case spv::Op::OpGroupNonUniformQuadSwap: + operand = "Direction"; + break; + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + default: + operand = "Delta"; + break; + } + return operand; + }; + + const auto id_type_id = _.GetOperandTypeId(inst, 4); + if (!_.IsUnsignedIntScalarType(id_type_id)) { + std::string operand = GetOperandName(inst->opcode()); + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << operand << " must be an unsigned integer scalar"; + } + + const bool should_be_constant = + inst->opcode() == spv::Op::OpGroupNonUniformQuadSwap || + ((inst->opcode() == spv::Op::OpGroupNonUniformBroadcast || + inst->opcode() == spv::Op::OpGroupNonUniformQuadBroadcast) && + _.version() < SPV_SPIRV_VERSION_WORD(1, 5)); + if (should_be_constant) { + const auto id_id = inst->GetOperandAs(4); + const auto id_op = _.GetIdOpcode(id_id); + if (!spvOpcodeIsConstant(id_op)) { + std::string operand = GetOperandName(inst->opcode()); + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Before SPIR-V 1.5, " << operand + << " must be a constant instruction"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBroadcastFirst(ValidationState_t& _, + const Instruction* inst) { + const auto type_id = inst->type_id(); + if (!_.IsFloatScalarOrVectorType(type_id) && + !_.IsIntScalarOrVectorType(type_id) && + !_.IsBoolScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a scalar or vector of integer, floating-point, " + "or boolean type"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (value_type_id != type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Value must match the Result type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBallot(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsUnsignedIntVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(inst->type_id()) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a 4-component unsigned integer vector"; + } + + const auto pred_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsBoolScalarType(pred_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Predicate must be a boolean scalar"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformInverseBallot(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBallotBitExtract(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + const auto id_type_id = _.GetOperandTypeId(inst, 4); + if (!_.IsUnsignedIntScalarType(id_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Id must be an unsigned integer scalar"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _, + const Instruction* inst) { + // Scope is already checked by ValidateExecutionScope() above. + + const uint32_t result_type = inst->type_id(); + if (!_.IsUnsignedIntScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be an unsigned integer type scalar."; + } + + const auto value = inst->GetOperandAs(4); + const auto value_type = _.FindDef(value)->type_id(); + if (!_.IsUnsignedIntVectorType(value_type) || + _.GetDimension(value_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " + "vector of four components " + "of integer type scalar"; + } + + const auto group = inst->GetOperandAs(3); + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((group != spv::GroupOperation::Reduce) && + (group != spv::GroupOperation::InclusiveScan) && + (group != spv::GroupOperation::ExclusiveScan)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4685) + << "In Vulkan: The OpGroupNonUniformBallotBitCount group " + "operation must be only: Reduce, InclusiveScan, or " + "ExclusiveScan."; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformBallotFind(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsUnsignedIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an unsigned integer scalar"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntVectorType(value_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + if (_.GetDimension(value_type_id) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Value must be a 4-component unsigned integer vector"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformArithmetic(ValidationState_t& _, + const Instruction* inst) { + const bool is_unsigned = inst->opcode() == spv::Op::OpGroupNonUniformUMin || + inst->opcode() == spv::Op::OpGroupNonUniformUMax; + const bool is_float = inst->opcode() == spv::Op::OpGroupNonUniformFAdd || + inst->opcode() == spv::Op::OpGroupNonUniformFMul || + inst->opcode() == spv::Op::OpGroupNonUniformFMin || + inst->opcode() == spv::Op::OpGroupNonUniformFMax; + const bool is_bool = inst->opcode() == spv::Op::OpGroupNonUniformLogicalAnd || + inst->opcode() == spv::Op::OpGroupNonUniformLogicalOr || + inst->opcode() == spv::Op::OpGroupNonUniformLogicalXor; + if (is_float) { + if (!_.IsFloatScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a floating-point scalar or vector"; + } + } else if (is_bool) { + if (!_.IsBoolScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be a boolean scalar or vector"; + } + } else if (is_unsigned) { + if (!_.IsUnsignedIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an unsigned integer scalar or vector"; + } + } else if (!_.IsIntScalarOrVectorType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result must be an integer scalar or vector"; + } + + const auto value_type_id = _.GetOperandTypeId(inst, 4); + if (value_type_id != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The type of Value must match the Result type"; + } + + const auto group_op = inst->GetOperandAs(3); + bool is_clustered_reduce = group_op == spv::GroupOperation::ClusteredReduce; + bool is_partitioned_nv = + group_op == spv::GroupOperation::PartitionedReduceNV || + group_op == spv::GroupOperation::PartitionedInclusiveScanNV || + group_op == spv::GroupOperation::PartitionedExclusiveScanNV; + if (inst->operands().size() <= 5) { + if (is_clustered_reduce) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be present when Operation is ClusteredReduce"; + } else if (is_partitioned_nv) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be present when Operation is PartitionedReduceNV, " + "PartitionedInclusiveScanNV, or PartitionedExclusiveScanNV"; + } + } else { + const auto operand_id = inst->GetOperandAs(5); + const auto* operand = _.FindDef(operand_id); + if (is_partitioned_nv) { + if (!operand || !_.IsIntScalarOrVectorType(operand->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be a 4-component integer vector"; + } + + if (_.GetDimension(operand->type_id()) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ballot must be a 4-component integer vector"; + } + } else { + if (!operand || !_.IsUnsignedIntScalarType(operand->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be an unsigned integer scalar"; + } + + if (!spvOpcodeIsConstant(operand->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be a constant instruction"; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupNonUniformRotateKHR(ValidationState_t& _, + const Instruction* inst) { + // Scope is already checked by ValidateExecutionScope() above. + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type) && + !_.IsFloatScalarOrVectorType(result_type) && + !_.IsBoolScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a scalar or vector of " + "floating-point, integer or boolean type."; + } + + const uint32_t value_type = _.GetTypeId(inst->GetOperandAs(3)); + if (value_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type must be the same as the type of Value."; + } + + const uint32_t delta_type = _.GetTypeId(inst->GetOperandAs(4)); + if (!_.IsUnsignedIntScalarType(delta_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Delta must be a scalar of integer type, whose Signedness " + "operand is 0."; + } + + if (inst->words().size() > 6) { + const uint32_t cluster_size_op_id = inst->GetOperandAs(5); + const Instruction* cluster_size_inst = _.FindDef(cluster_size_op_id); + if (!cluster_size_inst || + !_.IsUnsignedIntScalarType(cluster_size_inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must be a scalar of integer type, whose " + "Signedness operand is 0."; + } + + if (!spvOpcodeIsConstant(cluster_size_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ClusterSize must come from a constant instruction."; + } + + uint64_t cluster_size; + const bool valid_const = + _.EvalConstantValUint64(cluster_size_op_id, &cluster_size); + if (valid_const && + ((cluster_size == 0) || ((cluster_size & (cluster_size - 1)) != 0))) { + return _.diag(SPV_WARNING, inst) + << "Behavior is undefined unless ClusterSize is at least 1 and a " + "power of 2."; + } + + // TODO(kpet) Warn about undefined behavior when ClusterSize is greater than + // the declared SubGroupSize + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of non-uniform group instructions. +spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + if (spvOpcodeIsNonUniformGroupOperation(opcode)) { + // OpGroupNonUniformQuadAllKHR and OpGroupNonUniformQuadAnyKHR don't have + // scope paramter + if ((opcode != spv::Op::OpGroupNonUniformQuadAllKHR) && + (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) { + const uint32_t execution_scope = inst->GetOperandAs(2); + if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { + return error; + } + } + } + + switch (opcode) { + case spv::Op::OpGroupNonUniformElect: + return ValidateGroupNonUniformElect(_, inst); + case spv::Op::OpGroupNonUniformAny: + case spv::Op::OpGroupNonUniformAll: + return ValidateGroupNonUniformAnyAll(_, inst); + case spv::Op::OpGroupNonUniformAllEqual: + return ValidateGroupNonUniformAllEqual(_, inst); + case spv::Op::OpGroupNonUniformBroadcast: + case spv::Op::OpGroupNonUniformShuffle: + case spv::Op::OpGroupNonUniformShuffleXor: + case spv::Op::OpGroupNonUniformShuffleUp: + case spv::Op::OpGroupNonUniformShuffleDown: + case spv::Op::OpGroupNonUniformQuadBroadcast: + case spv::Op::OpGroupNonUniformQuadSwap: + return ValidateGroupNonUniformBroadcastShuffle(_, inst); + case spv::Op::OpGroupNonUniformBroadcastFirst: + return ValidateGroupNonUniformBroadcastFirst(_, inst); + case spv::Op::OpGroupNonUniformBallot: + return ValidateGroupNonUniformBallot(_, inst); + case spv::Op::OpGroupNonUniformInverseBallot: + return ValidateGroupNonUniformInverseBallot(_, inst); + case spv::Op::OpGroupNonUniformBallotBitExtract: + return ValidateGroupNonUniformBallotBitExtract(_, inst); + case spv::Op::OpGroupNonUniformBallotBitCount: + return ValidateGroupNonUniformBallotBitCount(_, inst); + case spv::Op::OpGroupNonUniformBallotFindLSB: + case spv::Op::OpGroupNonUniformBallotFindMSB: + return ValidateGroupNonUniformBallotFind(_, inst); + case spv::Op::OpGroupNonUniformIAdd: + case spv::Op::OpGroupNonUniformFAdd: + case spv::Op::OpGroupNonUniformIMul: + case spv::Op::OpGroupNonUniformFMul: + case spv::Op::OpGroupNonUniformSMin: + case spv::Op::OpGroupNonUniformUMin: + case spv::Op::OpGroupNonUniformFMin: + case spv::Op::OpGroupNonUniformSMax: + case spv::Op::OpGroupNonUniformUMax: + case spv::Op::OpGroupNonUniformFMax: + case spv::Op::OpGroupNonUniformBitwiseAnd: + case spv::Op::OpGroupNonUniformBitwiseOr: + case spv::Op::OpGroupNonUniformBitwiseXor: + case spv::Op::OpGroupNonUniformLogicalAnd: + case spv::Op::OpGroupNonUniformLogicalOr: + case spv::Op::OpGroupNonUniformLogicalXor: + return ValidateGroupNonUniformArithmetic(_, inst); + case spv::Op::OpGroupNonUniformRotateKHR: + return ValidateGroupNonUniformRotateKHR(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_primitives.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_primitives.cpp new file mode 100644 index 00000000..6769090d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_primitives.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of primitive SPIR-V instructions. + +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of primitive instructions. +spv_result_t PrimitivesPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + + switch (opcode) { + case spv::Op::OpEmitVertex: + case spv::Op::OpEndPrimitive: + case spv::Op::OpEmitStreamVertex: + case spv::Op::OpEndStreamPrimitive: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + spv::ExecutionModel::Geometry, + std::string(spvOpcodeString(opcode)) + + " instructions require Geometry execution model"); + break; + default: + break; + } + + switch (opcode) { + case spv::Op::OpEmitStreamVertex: + case spv::Op::OpEndStreamPrimitive: { + const uint32_t stream_id = inst->word(1); + const uint32_t stream_type = _.GetTypeId(stream_id); + if (!_.IsIntScalarType(stream_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Stream to be int scalar"; + } + + const spv::Op stream_opcode = _.GetIdOpcode(stream_id); + if (!spvOpcodeIsConstant(stream_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Stream to be constant instruction"; + } + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_query.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_query.cpp new file mode 100644 index 00000000..bd790ac3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_query.cpp @@ -0,0 +1,368 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray query instructions from SPV_KHR_ray_query + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t const_int_id = array_type->GetOperandAs(2U); + Instruction* array_length_inst = _.FindDef(const_int_id); + uint32_t array_length = 0; + if (array_length_inst->opcode() == spv::Op::OpConstant) { + array_length = array_length_inst->GetOperandAs(2); + } + return array_length; +} + +spv_result_t ValidateRayQueryPointer(ValidationState_t& _, + const Instruction* inst, + uint32_t ray_query_index) { + const uint32_t ray_query_id = inst->GetOperandAs(ray_query_index); + auto variable = _.FindDef(ray_query_id); + const auto var_opcode = variable->opcode(); + if (!variable || (var_opcode != spv::Op::OpVariable && + var_opcode != spv::Op::OpFunctionParameter && + var_opcode != spv::Op::OpAccessChain)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a memory object declaration"; + } + auto pointer = _.FindDef(variable->GetOperandAs(0)); + if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer"; + } + auto type = _.FindDef(pointer->GetOperandAs(2)); + if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Query must be a pointer to OpTypeRayQueryKHR"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateIntersectionId(ValidationState_t& _, + const Instruction* inst, + uint32_t intersection_index) { + const uint32_t intersection_id = + inst->GetOperandAs(intersection_index); + const uint32_t intersection_type = _.GetTypeId(intersection_id); + const spv::Op intersection_opcode = _.GetIdOpcode(intersection_id); + if (!_.IsIntScalarType(intersection_type) || + _.GetBitWidth(intersection_type) != 32 || + !spvOpcodeIsConstant(intersection_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Intersection ID to be a constant 32-bit int scalar"; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpRayQueryInitializeKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) != + spv::Op::OpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + const uint32_t ray_flags = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + const uint32_t cull_mask = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull Mask must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + break; + } + + case spv::Op::OpRayQueryTerminateKHR: + case spv::Op::OpRayQueryConfirmIntersectionKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + break; + } + + case spv::Op::OpRayQueryGenerateIntersectionKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error; + + const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1); + if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit T must be a 32-bit float scalar"; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR: + case spv::Op::OpRayQueryProceedKHR: + case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + if (opcode == spv::Op::OpRayQueryGetIntersectionFrontFaceKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionTKHR: + case spv::Op::OpRayQueryGetRayTMinKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float scalar type"; + } + + if (opcode == spv::Op::OpRayQueryGetIntersectionTKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionTypeKHR: + case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR: + case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR: + case spv::Op:: + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR: + case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR: + case spv::Op::OpRayQueryGetRayFlagsKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit int scalar type"; + } + + if (opcode != spv::Op::OpRayQueryGetRayFlagsKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR: + case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR: + case spv::Op::OpRayQueryGetWorldRayDirectionKHR: + case spv::Op::OpRayQueryGetWorldRayOriginKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 3-component " + "vector type"; + } + + if (opcode == spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR || + opcode == spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR) { + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 2-component " + "vector type"; + } + + break; + } + + case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR: + case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected matrix type as Result Type"; + } + + if (num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Count of 4"; + } + + if (!_.IsFloatScalarType(component_type) || + _.GetBitWidth(result_type) != 32 || num_rows != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Type of " + "3-component 32-bit float vectors"; + } + break; + } + + case spv::Op::OpRayQueryGetClusterIdNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit int scalar type"; + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionSpherePositionNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit float 3-component " + "vector type"; + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 2) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit 3 component float point " + "vector as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatArrayType(result_type) || + (GetArrayLength(_, _.FindDef(result_type)) != 2) || + !_.IsFloatScalarType(_.GetComponentType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV: + case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be 32-bit floating point " + "scalar type"; + } + break; + } + + case spv::Op::OpRayQueryIsSphereHitNV: + case spv::Op::OpRayQueryIsLSSHitNV: { + if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error; + if (auto error = ValidateIntersectionId(_, inst, 3)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be Boolean " + "scalar type"; + } + + break; + } + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing.cpp new file mode 100644 index 00000000..f74e9d4b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray tracing instructions from SPV_KHR_ray_tracing + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case spv::Op::OpTraceRayKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + "OpTraceRayKHR requires RayGenerationKHR, " + "ClosestHitKHR and MissKHR execution models"; + } + return false; + } + return true; + }); + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) != + spv::Op::OpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + const uint32_t ray_flags = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + + const uint32_t cull_mask = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull Mask must be a 32-bit int scalar"; + } + + const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(sbt_offset) || _.GetBitWidth(sbt_offset) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Offset must be a 32-bit int scalar"; + } + + const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(sbt_stride) || _.GetBitWidth(sbt_stride) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Stride must be a 32-bit int scalar"; + } + + const uint32_t miss_index = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(miss_index) || _.GetBitWidth(miss_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 6); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7); + if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 8); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9); + if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + + const Instruction* payload = _.FindDef(inst->GetOperandAs(10)); + if (payload->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must be the result of a OpVariable"; + } else if (payload->GetOperandAs(2) != + spv::StorageClass::RayPayloadKHR && + payload->GetOperandAs(2) != + spv::StorageClass::IncomingRayPayloadKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Payload must have storage class RayPayloadKHR or " + "IncomingRayPayloadKHR"; + } + break; + } + + case spv::Op::OpReportIntersectionKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::IntersectionKHR) { + if (message) { + *message = + "OpReportIntersectionKHR requires IntersectionKHR " + "execution model"; + } + return false; + } + return true; + }); + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + const uint32_t hit = _.GetOperandTypeId(inst, 2); + if (!_.IsFloatScalarType(hit) || _.GetBitWidth(hit) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit must be a 32-bit int scalar"; + } + + const uint32_t hit_kind = _.GetOperandTypeId(inst, 3); + if (!_.IsUnsignedIntScalarType(hit_kind) || + _.GetBitWidth(hit_kind) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Kind must be a 32-bit unsigned int scalar"; + } + break; + } + + case spv::Op::OpExecuteCallableKHR: { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR && + model != spv::ExecutionModel::CallableKHR) { + if (message) { + *message = + "OpExecuteCallableKHR requires RayGenerationKHR, " + "ClosestHitKHR, MissKHR and CallableKHR execution models"; + } + return false; + } + return true; + }); + + const uint32_t sbt_index = _.GetOperandTypeId(inst, 0); + if (!_.IsUnsignedIntScalarType(sbt_index) || + _.GetBitWidth(sbt_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Index must be a 32-bit unsigned int scalar"; + } + + const auto callable_data = _.FindDef(inst->GetOperandAs(1)); + if (callable_data->opcode() != spv::Op::OpVariable) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must be the result of a OpVariable"; + } else if (callable_data->GetOperandAs(2) != + spv::StorageClass::CallableDataKHR && + callable_data->GetOperandAs(2) != + spv::StorageClass::IncomingCallableDataKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Callable Data must have storage class CallableDataKHR or " + "IncomingCallableDataKHR"; + } + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing_reorder.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing_reorder.cpp new file mode 100644 index 00000000..3685a765 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_ray_tracing_reorder.cpp @@ -0,0 +1,732 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates ray tracing instructions from SPV_NV_shader_execution_reorder + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +#include + +namespace spvtools { +namespace val { + +static const uint32_t KRayParamInvalidId = std::numeric_limits::max(); + +uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) { + assert(array_type->opcode() == spv::Op::OpTypeArray); + uint32_t const_int_id = array_type->GetOperandAs(2U); + Instruction* array_length_inst = _.FindDef(const_int_id); + uint32_t array_length = 0; + if (array_length_inst->opcode() == spv::Op::OpConstant) { + array_length = array_length_inst->GetOperandAs(2); + } + return array_length; +} + +spv_result_t ValidateHitObjectPointer(ValidationState_t& _, + const Instruction* inst, + uint32_t hit_object_index) { + const uint32_t hit_object_id = inst->GetOperandAs(hit_object_index); + auto variable = _.FindDef(hit_object_id); + const auto var_opcode = variable->opcode(); + if (!variable || (var_opcode != spv::Op::OpVariable && + var_opcode != spv::Op::OpFunctionParameter && + var_opcode != spv::Op::OpAccessChain)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object must be a memory object declaration"; + } + auto pointer = _.FindDef(variable->GetOperandAs(0)); + if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object must be a pointer"; + } + auto type = _.FindDef(pointer->GetOperandAs(2)); + if (!type || type->opcode() != spv::Op::OpTypeHitObjectNV) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Type must be OpTypeHitObjectNV"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateHitObjectInstructionCommonParameters( + ValidationState_t& _, const Instruction* inst, + uint32_t acceleration_struct_index, uint32_t instance_id_index, + uint32_t primtive_id_index, uint32_t geometry_index, + uint32_t ray_flags_index, uint32_t cull_mask_index, uint32_t hit_kind_index, + uint32_t sbt_index, uint32_t sbt_offset_index, uint32_t sbt_stride_index, + uint32_t sbt_record_offset_index, uint32_t sbt_record_stride_index, + uint32_t miss_index, uint32_t ray_origin_index, uint32_t ray_tmin_index, + uint32_t ray_direction_index, uint32_t ray_tmax_index, + uint32_t payload_index, uint32_t hit_object_attr_index) { + auto isValidId = [](uint32_t spvid) { return spvid < KRayParamInvalidId; }; + if (isValidId(acceleration_struct_index) && + _.GetIdOpcode(_.GetOperandTypeId(inst, acceleration_struct_index)) != + spv::Op::OpTypeAccelerationStructureKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Acceleration Structure to be of type " + "OpTypeAccelerationStructureKHR"; + } + + if (isValidId(instance_id_index)) { + const uint32_t instance_id = _.GetOperandTypeId(inst, instance_id_index); + if (!_.IsIntScalarType(instance_id) || _.GetBitWidth(instance_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instance Id must be a 32-bit int scalar"; + } + } + + if (isValidId(primtive_id_index)) { + const uint32_t primitive_id = _.GetOperandTypeId(inst, primtive_id_index); + if (!_.IsIntScalarType(primitive_id) || _.GetBitWidth(primitive_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Primitive Id must be a 32-bit int scalar"; + } + } + + if (isValidId(geometry_index)) { + const uint32_t geometry_index_id = _.GetOperandTypeId(inst, geometry_index); + if (!_.IsIntScalarType(geometry_index_id) || + _.GetBitWidth(geometry_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Geometry Index must be a 32-bit int scalar"; + } + } + + if (isValidId(miss_index)) { + const uint32_t miss_index_id = _.GetOperandTypeId(inst, miss_index); + if (!_.IsUnsignedIntScalarType(miss_index_id) || + _.GetBitWidth(miss_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit int scalar"; + } + } + + if (isValidId(cull_mask_index)) { + const uint32_t cull_mask_id = _.GetOperandTypeId(inst, cull_mask_index); + if (!_.IsUnsignedIntScalarType(cull_mask_id) || + _.GetBitWidth(cull_mask_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cull mask must be a 32-bit int scalar"; + } + } + + if (isValidId(sbt_index)) { + const uint32_t sbt_index_id = _.GetOperandTypeId(inst, sbt_index); + if (!_.IsUnsignedIntScalarType(sbt_index_id) || + _.GetBitWidth(sbt_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Index must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(sbt_offset_index)) { + const uint32_t sbt_offset_id = _.GetOperandTypeId(inst, sbt_offset_index); + if (!_.IsUnsignedIntScalarType(sbt_offset_id) || + _.GetBitWidth(sbt_offset_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Offset must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(sbt_stride_index)) { + const uint32_t sbt_stride_index_id = + _.GetOperandTypeId(inst, sbt_stride_index); + if (!_.IsUnsignedIntScalarType(sbt_stride_index_id) || + _.GetBitWidth(sbt_stride_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT Stride must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(sbt_record_offset_index)) { + const uint32_t sbt_record_offset_index_id = + _.GetOperandTypeId(inst, sbt_record_offset_index); + if (!_.IsUnsignedIntScalarType(sbt_record_offset_index_id) || + _.GetBitWidth(sbt_record_offset_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT record offset must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(sbt_record_stride_index)) { + const uint32_t sbt_record_stride_index_id = + _.GetOperandTypeId(inst, sbt_record_stride_index); + if (!_.IsUnsignedIntScalarType(sbt_record_stride_index_id) || + _.GetBitWidth(sbt_record_stride_index_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SBT record stride must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(ray_origin_index)) { + const uint32_t ray_origin_id = _.GetOperandTypeId(inst, ray_origin_index); + if (!_.IsFloatVectorType(ray_origin_id) || + _.GetDimension(ray_origin_id) != 3 || + _.GetBitWidth(ray_origin_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + } + + if (isValidId(ray_tmin_index)) { + const uint32_t ray_tmin_id = _.GetOperandTypeId(inst, ray_tmin_index); + if (!_.IsFloatScalarType(ray_tmin_id) || _.GetBitWidth(ray_tmin_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + } + + if (isValidId(ray_direction_index)) { + const uint32_t ray_direction_id = + _.GetOperandTypeId(inst, ray_direction_index); + if (!_.IsFloatVectorType(ray_direction_id) || + _.GetDimension(ray_direction_id) != 3 || + _.GetBitWidth(ray_direction_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + } + + if (isValidId(ray_tmax_index)) { + const uint32_t ray_tmax_id = _.GetOperandTypeId(inst, ray_tmax_index); + if (!_.IsFloatScalarType(ray_tmax_id) || _.GetBitWidth(ray_tmax_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + } + + if (isValidId(ray_flags_index)) { + const uint32_t ray_flags_id = _.GetOperandTypeId(inst, ray_flags_index); + if (!_.IsIntScalarType(ray_flags_id) || _.GetBitWidth(ray_flags_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Flags must be a 32-bit int scalar"; + } + } + + if (isValidId(payload_index)) { + const uint32_t payload_id = inst->GetOperandAs(payload_index); + auto variable = _.FindDef(payload_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + (variable->GetOperandAs(2) != + spv::StorageClass::RayPayloadKHR && + variable->GetOperandAs(2) != + spv::StorageClass::IncomingRayPayloadKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "payload must be a OpVariable of storage " + "class RayPayloadKHR or IncomingRayPayloadKHR"; + } + } + + if (isValidId(hit_kind_index)) { + const uint32_t hit_kind_id = _.GetOperandTypeId(inst, hit_kind_index); + if (!_.IsUnsignedIntScalarType(hit_kind_id) || + _.GetBitWidth(hit_kind_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Kind must be a 32-bit unsigned int scalar"; + } + } + + if (isValidId(hit_object_attr_index)) { + const uint32_t hit_object_attr_id = + inst->GetOperandAs(hit_object_attr_index); + auto variable = _.FindDef(hit_object_attr_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + (variable->GetOperandAs(2)) != + spv::StorageClass::HitObjectAttributeNV) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object Attributes id must be a OpVariable of storage " + "class HitObjectAttributeNV"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst) { + const spv::Op opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + auto RegisterOpcodeForValidModel = [](ValidationState_t& vs, + const Instruction* rtinst) { + std::string opcode_name = spvOpcodeString(rtinst->opcode()); + vs.function(rtinst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR, ClosestHitKHR and " + "MissKHR execution models"; + } + return false; + } + return true; + }); + return; + }; + + switch (opcode) { + case spv::Op::OpHitObjectIsMissNV: + case spv::Op::OpHitObjectIsHitNV: + case spv::Op::OpHitObjectIsEmptyNV: { + RegisterOpcodeForValidModel(_, inst); + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type to be bool scalar type"; + } + + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + break; + } + + case spv::Op::OpHitObjectGetShaderRecordBufferHandleNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsIntVectorType(result_type) || + (_.GetDimension(result_type) != 2) || + (_.GetBitWidth(result_type) != 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type 2-component vector as Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetHitKindNV: + case spv::Op::OpHitObjectGetPrimitiveIndexNV: + case spv::Op::OpHitObjectGetGeometryIndexNV: + case spv::Op::OpHitObjectGetInstanceIdNV: + case spv::Op::OpHitObjectGetInstanceCustomIndexNV: + case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type) || !_.GetBitWidth(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetCurrentTimeNV: + case spv::Op::OpHitObjectGetRayTMaxNV: + case spv::Op::OpHitObjectGetRayTMinNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(result_type) != 32) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating-point type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetObjectToWorldNV: + case spv::Op::OpHitObjectGetWorldToObjectNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + if (num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Count of 4" + << spvOpcodeString(opcode); + } + + if (!_.IsFloatScalarType(component_type) || + _.GetBitWidth(result_type) != 32 || num_rows != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "expected Result Type matrix to have a Column Type of " + "3-component 32-bit float vectors: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetObjectRayOriginNV: + case spv::Op::OpHitObjectGetObjectRayDirectionNV: + case spv::Op::OpHitObjectGetWorldRayDirectionNV: + case spv::Op::OpHitObjectGetWorldRayOriginNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + (_.GetDimension(result_type) != 3) || + (_.GetBitWidth(result_type) != 32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating-point type 3-component vector as " + "Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetAttributesNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + const uint32_t hit_object_attr_id = inst->GetOperandAs(1); + auto variable = _.FindDef(hit_object_attr_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + variable->GetOperandAs(2) != + spv::StorageClass::HitObjectAttributeNV) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object Attributes id must be a OpVariable of storage " + "class HitObjectAttributeNV"; + } + break; + } + + case spv::Op::OpHitObjectExecuteShaderNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + const uint32_t hit_object_attr_id = inst->GetOperandAs(1); + auto variable = _.FindDef(hit_object_attr_id); + const auto var_opcode = variable->opcode(); + if (!variable || var_opcode != spv::Op::OpVariable || + (variable->GetOperandAs(2)) != + spv::StorageClass::RayPayloadKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hit Object Attributes id must be a OpVariable of storage " + "class RayPayloadKHR"; + } + break; + } + + case spv::Op::OpHitObjectRecordEmptyNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + break; + } + + case spv::Op::OpHitObjectRecordMissNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + const uint32_t miss_index = _.GetOperandTypeId(inst, 1); + if (!_.IsUnsignedIntScalarType(miss_index) || + _.GetBitWidth(miss_index) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Miss Index must be a 32-bit int scalar"; + } + + const uint32_t ray_origin = _.GetOperandTypeId(inst, 2); + if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 || + _.GetBitWidth(ray_origin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Origin must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmin = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMin must be a 32-bit float scalar"; + } + + const uint32_t ray_direction = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(ray_direction) || + _.GetDimension(ray_direction) != 3 || + _.GetBitWidth(ray_direction) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray Direction must be a 32-bit float 3-component vector"; + } + + const uint32_t ray_tmax = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Ray TMax must be a 32-bit float scalar"; + } + break; + } + + case spv::Op::OpHitObjectRecordHitWithIndexNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, 2 /* Instance Id */, + 3 /* Primtive Id */, 4 /* Geometry Index */, + KRayParamInvalidId /* Ray Flags */, + KRayParamInvalidId /* Cull Mask */, 5 /* Hit Kind*/, + 6 /* SBT index */, KRayParamInvalidId /* SBT Offset */, + KRayParamInvalidId /* SBT Stride */, + KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, + KRayParamInvalidId /* Miss Index */, 7 /* Ray Origin */, + 8 /* Ray TMin */, 9 /* Ray Direction */, 10 /* Ray TMax */, + KRayParamInvalidId /* Payload */, 11 /* Hit Object Attribute */)) + return error; + + break; + } + + case spv::Op::OpHitObjectRecordHitNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, 2 /* Instance Id */, + 3 /* Primtive Id */, 4 /* Geometry Index */, + KRayParamInvalidId /* Ray Flags */, + KRayParamInvalidId /* Cull Mask */, 5 /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, + KRayParamInvalidId /* SBT Offset */, + KRayParamInvalidId /* SBT Stride */, 6 /* SBT Record Offset */, + 7 /* SBT Record Stride */, KRayParamInvalidId /* Miss Index */, + 8 /* Ray Origin */, 9 /* Ray TMin */, 10 /* Ray Direction */, + 11 /* Ray TMax */, KRayParamInvalidId /* Payload */, + 12 /* Hit Object Attribute */)) + return error; + + break; + } + + case spv::Op::OpHitObjectTraceRayMotionNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primtive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 12 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + // Current Time + const uint32_t current_time_id = _.GetOperandTypeId(inst, 11); + if (!_.IsFloatScalarType(current_time_id) || + _.GetBitWidth(current_time_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Current Times must be a 32-bit float scalar type"; + } + + break; + } + + case spv::Op::OpHitObjectTraceRayNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + if (auto error = ValidateHitObjectInstructionCommonParameters( + _, inst, 1 /* Acceleration Struct */, + KRayParamInvalidId /* Instance Id */, + KRayParamInvalidId /* Primtive Id */, + KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */, + 3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/, + KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */, + 5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */, + KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */, + 7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */, + 10 /* Ray TMax */, 11 /* Payload */, + KRayParamInvalidId /* Hit Object Attribute */)) + return error; + break; + } + + case spv::Op::OpReorderThreadWithHitObjectNV: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + if (auto error = ValidateHitObjectPointer(_, inst, 0)) return error; + + if (inst->operands().size() > 1) { + if (inst->operands().size() != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint and Bits are optional together i.e " + << " Either both Hint and Bits should be provided or neither."; + } + + // Validate the optional opreands Hint and Bits + const uint32_t hint_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + const uint32_t bits_id = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "bits must be a 32-bit int scalar"; + } + } + break; + } + + case spv::Op::OpReorderThreadWithHintNV: { + std::string opcode_name = spvOpcodeString(inst->opcode()); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [opcode_name](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR) { + if (message) { + *message = opcode_name + + " requires RayGenerationKHR execution model"; + } + return false; + } + return true; + }); + + const uint32_t hint_id = _.GetOperandTypeId(inst, 0); + if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Hint must be a 32-bit int scalar"; + } + + const uint32_t bits_id = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "bits must be a 32-bit int scalar"; + } + break; + } + + case spv::Op::OpHitObjectGetClusterIdNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit integer type scalar as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case spv::Op::OpHitObjectGetSpherePositionNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 3 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point 2 component vector type as " + "Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetSphereRadiusNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit floating point scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetLSSPositionsNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + auto result_id = _.FindDef(result_type); + if ((result_id->opcode() != spv::Op::OpTypeArray) || + (GetArrayLength(_, result_id) != 2) || + !_.IsFloatVectorType(_.GetComponentType(result_type)) || + _.GetDimension(_.GetComponentType(result_type)) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit 3 component float point " + "vector as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectGetLSSRadiiNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsFloatArrayType(result_type) || + (GetArrayLength(_, _.FindDef(result_type)) != 2) || + !_.IsFloatScalarType(_.GetComponentType(result_type))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 2 element array of 32-bit floating point scalar as " + "Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectIsSphereHitNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Boolean scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + case spv::Op::OpHitObjectIsLSSHitNV: { + RegisterOpcodeForValidModel(_, inst); + if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error; + + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Boolean scalar as Result Type: " + << spvOpcodeString(opcode); + } + break; + } + + default: + break; + } + return SPV_SUCCESS; +} +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.cpp new file mode 100644 index 00000000..6b493538 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.cpp @@ -0,0 +1,323 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate_scopes.h" + +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +bool IsValidScope(uint32_t scope) { + // Deliberately avoid a default case so we have to update the list when the + // scopes list changes. + switch (static_cast(scope)) { + case spv::Scope::CrossDevice: + case spv::Scope::Device: + case spv::Scope::Workgroup: + case spv::Scope::Subgroup: + case spv::Scope::Invocation: + case spv::Scope::QueueFamilyKHR: + case spv::Scope::ShaderCallKHR: + return true; + case spv::Scope::Max: + break; + } + return false; +} + +spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope) { + spv::Op opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + + if (!is_int32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << ": expected scope to be a 32-bit int"; + } + + if (!is_const_int32) { + if (_.HasCapability(spv::Capability::Shader) && + !_.HasCapability(spv::Capability::CooperativeMatrixNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope ids must be OpConstant when Shader capability is " + << "present"; + } + if (_.HasCapability(spv::Capability::Shader) && + _.HasCapability(spv::Capability::CooperativeMatrixNV) && + !spvOpcodeIsConstant(_.GetIdOpcode(scope))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope ids must be constant or specialization constant when " + << "CooperativeMatrixNV capability is present"; + } + } + + if (is_const_int32 && !IsValidScope(value)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope)); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExecutionScope(ValidationState_t& _, + const Instruction* inst, uint32_t scope) { + spv::Op opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t tmp_value = 0; + std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope); + + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + if (!is_const_int32) { + return SPV_SUCCESS; + } + + spv::Scope value = spv::Scope(tmp_value); + + // Vulkan specific rules + if (spvIsVulkanEnv(_.context()->target_env)) { + // Vulkan 1.1 specific rules + if (_.context()->target_env != SPV_ENV_VULKAN_1_0) { + // Scope for Non Uniform Group Operations must be limited to Subgroup + if ((spvOpcodeIsNonUniformGroupOperation(opcode) && + (opcode != spv::Op::OpGroupNonUniformQuadAllKHR) && + (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) && + (value != spv::Scope::Subgroup)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4642) << spvOpcodeString(opcode) + << ": in Vulkan environment Execution scope is limited to " + << "Subgroup"; + } + } + + // OpControlBarrier must only use Subgroup execution scope for a subset of + // execution models. + if (opcode == spv::Op::OpControlBarrier && value != spv::Scope::Subgroup) { + std::string errorVUID = _.VkErrorID(4682); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + spv::ExecutionModel model, + std::string* message) { + if (model == spv::ExecutionModel::Fragment || + model == spv::ExecutionModel::Vertex || + model == spv::ExecutionModel::Geometry || + model == spv::ExecutionModel::TessellationEvaluation || + model == spv::ExecutionModel::RayGenerationKHR || + model == spv::ExecutionModel::IntersectionKHR || + model == spv::ExecutionModel::AnyHitKHR || + model == spv::ExecutionModel::ClosestHitKHR || + model == spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, OpControlBarrier execution scope " + "must be Subgroup for Fragment, Vertex, Geometry, " + "TessellationEvaluation, RayGeneration, Intersection, " + "AnyHit, ClosestHit, and Miss execution models"; + } + return false; + } + return true; + }); + } + + // Only subset of execution models support Workgroup. + if (value == spv::Scope::Workgroup) { + std::string errorVUID = _.VkErrorID(4637); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::TaskNV && + model != spv::ExecutionModel::MeshNV && + model != spv::ExecutionModel::TaskEXT && + model != spv::ExecutionModel::MeshEXT && + model != spv::ExecutionModel::TessellationControl && + model != spv::ExecutionModel::GLCompute) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Workgroup execution scope is " + "only for TaskNV, MeshNV, TaskEXT, MeshEXT, " + "TessellationControl, and GLCompute execution models"; + } + return false; + } + return true; + }); + } + + // Vulkan generic rules + // Scope for execution must be limited to Workgroup or Subgroup + if (value != spv::Scope::Workgroup && value != spv::Scope::Subgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4636) << spvOpcodeString(opcode) + << ": in Vulkan environment Execution Scope is limited to " + << "Workgroup and Subgroup"; + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + // General SPIRV rules + // Scope for execution must be limited to Workgroup or Subgroup for + // non-uniform operations + if (spvOpcodeIsNonUniformGroupOperation(opcode) && + opcode != spv::Op::OpGroupNonUniformQuadAllKHR && + opcode != spv::Op::OpGroupNonUniformQuadAnyKHR && + value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Execution scope is limited to Subgroup or Workgroup"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope) { + const spv::Op opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t tmp_value = 0; + std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope); + + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + if (!is_const_int32) { + return SPV_SUCCESS; + } + + spv::Scope value = spv::Scope(tmp_value); + + if (value == spv::Scope::QueueFamilyKHR) { + if (_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) { + return SPV_SUCCESS; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Scope QueueFamilyKHR requires capability " + << "VulkanMemoryModelKHR"; + } + } + + if (value == spv::Scope::Device && + _.HasCapability(spv::Capability::VulkanMemoryModelKHR) && + !_.HasCapability(spv::Capability::VulkanMemoryModelDeviceScopeKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Use of device scope with VulkanKHR memory model requires the " + << "VulkanMemoryModelDeviceScopeKHR capability"; + } + + // Vulkan Specific rules + if (spvIsVulkanEnv(_.context()->target_env)) { + if (value != spv::Scope::Device && value != spv::Scope::Workgroup && + value != spv::Scope::Subgroup && value != spv::Scope::Invocation && + value != spv::Scope::ShaderCallKHR && + value != spv::Scope::QueueFamily) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4638) << spvOpcodeString(opcode) + << ": in Vulkan environment Memory Scope is limited to Device, " + "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or " + "Invocation"; + } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && + value == spv::Scope::Subgroup && + !_.HasCapability(spv::Capability::SubgroupBallotKHR) && + !_.HasCapability(spv::Capability::SubgroupVoteKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7951) << spvOpcodeString(opcode) + << ": in Vulkan 1.0 environment Memory Scope is can not be " + "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR " + "declared"; + } + + if (value == spv::Scope::ShaderCallKHR) { + std::string errorVUID = _.VkErrorID(4640); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::IntersectionKHR && + model != spv::ExecutionModel::AnyHitKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR && + model != spv::ExecutionModel::CallableKHR) { + if (message) { + *message = + errorVUID + + "ShaderCallKHR Memory Scope requires a ray tracing " + "execution model"; + } + return false; + } + return true; + }); + } + + if (value == spv::Scope::Workgroup) { + std::string errorVUID = _.VkErrorID(7321); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::TessellationControl && + model != spv::ExecutionModel::TaskNV && + model != spv::ExecutionModel::MeshNV && + model != spv::ExecutionModel::TaskEXT && + model != spv::ExecutionModel::MeshEXT) { + if (message) { + *message = errorVUID + + "Workgroup Memory Scope is limited to MeshNV, " + "TaskNV, MeshEXT, TaskEXT, TessellationControl, " + "and GLCompute execution model"; + } + return false; + } + return true; + }); + + if (_.memory_model() == spv::MemoryModel::GLSL450) { + errorVUID = _.VkErrorID(7320); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model == spv::ExecutionModel::TessellationControl) { + if (message) { + *message = + errorVUID + + "Workgroup Memory Scope can't be used with " + "TessellationControl using GLSL450 Memory Model"; + } + return false; + } + return true; + }); + } + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.h b/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.h new file mode 100644 index 00000000..ba8b301a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_scopes.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of scopes for SPIR-V instructions. + +#include "source/opcode.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope); + +spv_result_t ValidateExecutionScope(ValidationState_t& _, + const Instruction* inst, uint32_t scope); + +spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope); + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_small_type_uses.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_small_type_uses.cpp new file mode 100644 index 00000000..69f61ee4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_small_type_uses.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateSmallTypeUses(ValidationState_t& _, + const Instruction* inst) { + if (!_.HasCapability(spv::Capability::Shader) || inst->type_id() == 0 || + !_.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return SPV_SUCCESS; + } + + if (_.IsPointerType(inst->type_id())) return SPV_SUCCESS; + + // The validator should previously have checked ways to generate 8- or 16-bit + // types. So we only need to considervalid paths from source to sink. + // When restricted, uses of 8- or 16-bit types can only be stores, + // width-only conversions, decorations and copy object. + for (auto use : inst->uses()) { + const auto* user = use.first; + switch (user->opcode()) { + case spv::Op::OpDecorate: + case spv::Op::OpDecorateId: + case spv::Op::OpCopyObject: + case spv::Op::OpStore: + case spv::Op::OpFConvert: + case spv::Op::OpUConvert: + case spv::Op::OpSConvert: + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, user) + << "Invalid use of 8- or 16-bit result"; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_tensor_layout.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_tensor_layout.cpp new file mode 100644 index 00000000..35c766b8 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_tensor_layout.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2024 NVIDIA Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validate instructions that manipulate tensor layout and view objects + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateTensorLayoutResultTypeNV(ValidationState_t& _, + const Instruction* inst) { + const auto result_type_index = 0; + const auto result_type_id = inst->GetOperandAs(result_type_index); + const auto result_type = _.FindDef(result_type_id); + + if (!result_type || spv::Op::OpTypeTensorLayoutNV != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Result Type " + << _.getIdName(result_type_id) << " is not a tensor layout type."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateTensorViewResultTypeNV(ValidationState_t& _, + const Instruction* inst) { + const auto result_type_index = 0; + const auto result_type_id = inst->GetOperandAs(result_type_index); + const auto result_type = _.FindDef(result_type_id); + + if (!result_type || spv::Op::OpTypeTensorViewNV != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Result Type " + << _.getIdName(result_type_id) << " is not a tensor view type."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCreateTensorLayoutNV(ValidationState_t& _, + const Instruction* inst) { + if (auto error = ValidateTensorLayoutResultTypeNV(_, inst)) return error; + + return SPV_SUCCESS; +} + +spv_result_t ValidateCreateTensorViewNV(ValidationState_t& _, + const Instruction* inst) { + if (auto error = ValidateTensorViewResultTypeNV(_, inst)) return error; + + return SPV_SUCCESS; +} + +enum ExpectedNumValues { + DIM, + DIMx2, + ONE, + FOUR, +}; + +spv_result_t ValidateTensorTypeWithDimValuesNV(ValidationState_t& _, + const Instruction* inst, + ExpectedNumValues expected, + bool is_view) { + std::string type_str; + if (is_view) { + if (auto error = ValidateTensorViewResultTypeNV(_, inst)) return error; + type_str = "TensorView"; + } else { + if (auto error = ValidateTensorLayoutResultTypeNV(_, inst)) return error; + type_str = "TensorLayout"; + } + + const auto result_type_id = inst->GetOperandAs(0); + const auto tensor_id = inst->GetOperandAs(2); + const auto tensor = _.FindDef(tensor_id); + if (!tensor || result_type_id != tensor->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Result Type " + << _.getIdName(result_type_id) << " does not match " << type_str + << " type."; + } + + const auto num_values = inst->operands().size() - 3; + + const auto result_type = _.FindDef(result_type_id); + const auto dim_index = 1; + const auto dim_id = result_type->GetOperandAs(dim_index); + uint64_t dim_value; + if (_.EvalConstantValUint64(dim_id, &dim_value)) { + uint64_t expected_num_values = 0; + switch (expected) { + case DIM: + expected_num_values = dim_value; + break; + case DIMx2: + expected_num_values = dim_value * 2; + break; + case ONE: + expected_num_values = 1; + break; + case FOUR: + expected_num_values = 4; + break; + } + + if (num_values != expected_num_values) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) + << " unexpected number of operands."; + } + } + + for (uint32_t i = 0; i < num_values; ++i) { + const auto val_id = inst->GetOperandAs(i + 3); + const auto val = _.FindDef(val_id); + if (!val || !_.IsIntScalarType(val->type_id()) || + _.GetBitWidth(val->type_id()) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " operand " + << _.getIdName(val_id) << " is not a 32-bit integer."; + } + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t TensorLayoutPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpCreateTensorLayoutNV: + if (auto error = ValidateCreateTensorLayoutNV(_, inst)) return error; + break; + case spv::Op::OpCreateTensorViewNV: + if (auto error = ValidateCreateTensorViewNV(_, inst)) return error; + break; + case spv::Op::OpTensorLayoutSetBlockSizeNV: + case spv::Op::OpTensorLayoutSetDimensionNV: + case spv::Op::OpTensorLayoutSetStrideNV: + if (auto error = ValidateTensorTypeWithDimValuesNV(_, inst, DIM, false)) + return error; + break; + case spv::Op::OpTensorLayoutSliceNV: + if (auto error = ValidateTensorTypeWithDimValuesNV(_, inst, DIMx2, false)) + return error; + break; + case spv::Op::OpTensorLayoutSetClampValueNV: + if (auto error = ValidateTensorTypeWithDimValuesNV(_, inst, ONE, false)) + return error; + break; + case spv::Op::OpTensorViewSetDimensionNV: + case spv::Op::OpTensorViewSetStrideNV: + if (auto error = ValidateTensorTypeWithDimValuesNV(_, inst, DIM, true)) + return error; + break; + case spv::Op::OpTensorViewSetClipNV: + if (auto error = ValidateTensorTypeWithDimValuesNV(_, inst, FOUR, true)) + return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validate_type.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validate_type.cpp new file mode 100644 index 00000000..b3914a05 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validate_type.cpp @@ -0,0 +1,943 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// Copyright (c) 2024 NVIDIA Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Ensures type declarations are unique unless allowed by the specification. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Validates that type declarations are unique, unless multiple declarations +// of the same data type are allowed by the specification. +// (see section 2.8 Types and Variables) +// Doesn't do anything if SPV_VAL_ignore_type_decl_unique was declared in the +// module. +spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) { + if (_.HasExtension(Extension::kSPV_VALIDATOR_ignore_type_decl_unique)) + return SPV_SUCCESS; + + const auto opcode = inst->opcode(); + if (opcode != spv::Op::OpTypeArray && opcode != spv::Op::OpTypeRuntimeArray && + opcode != spv::Op::OpTypeNodePayloadArrayAMDX && + opcode != spv::Op::OpTypeStruct && opcode != spv::Op::OpTypePointer && + opcode != spv::Op::OpTypeUntypedPointerKHR && + !_.RegisterUniqueTypeDeclaration(inst)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Duplicate non-aggregate type declarations are not allowed. " + "Opcode: " + << spvOpcodeString(opcode) << " id: " << inst->id(); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) { + // Validates that the number of bits specified for an Int type is valid. + // Scalar integer types can be parameterized only with 32-bits. + // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit + // integers, respectively. + auto num_bits = inst->GetOperandAs(1); + if (num_bits != 32) { + if (num_bits == 8) { + if (_.features().declare_int8_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using an 8-bit integer type requires the Int8 capability," + " or an extension that explicitly enables 8-bit integers."; + } else if (num_bits == 16) { + if (_.features().declare_int16_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 16-bit integer type requires the Int16 capability," + " or an extension that explicitly enables 16-bit integers."; + } else if (num_bits == 64) { + if (_.HasCapability(spv::Capability::Int64)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 64-bit integer type requires the Int64 capability."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid number of bits (" << num_bits + << ") used for OpTypeInt."; + } + } + + const auto signedness_index = 2; + const auto signedness = inst->GetOperandAs(signedness_index); + if (signedness != 0 && signedness != 1) { + return _.diag(SPV_ERROR_INVALID_VALUE, inst) + << "OpTypeInt has invalid signedness:"; + } + + // SPIR-V Spec 2.16.3: Validation Rules for Kernel Capabilities: The + // Signedness in OpTypeInt must always be 0. + if (spv::Op::OpTypeInt == inst->opcode() && + _.HasCapability(spv::Capability::Kernel) && + inst->GetOperandAs(2) != 0u) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "The Signedness in OpTypeInt " + "must always be 0 when Kernel " + "capability is used."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) { + // Validates that the number of bits specified for an Int type is valid. + // Scalar integer types can be parameterized only with 32-bits. + // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit + // integers, respectively. + auto num_bits = inst->GetOperandAs(1); + const bool has_encoding = inst->operands().size() > 2; + if (num_bits == 32) { + return SPV_SUCCESS; + } + auto operands = inst->words(); + if (operands.size() > 3) { + if (operands[3] != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Current FPEncoding only supports BFloat16KHR."; + } + return SPV_SUCCESS; + } + + if (num_bits == 16) { + // An absence of FP encoding implies IEEE 754. The Float16 and Float16Buffer + // capabilities only enable IEEE 754 binary 16 + if (has_encoding || _.features().declare_float16_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 16-bit floating point " + << "type requires the Float16 or Float16Buffer capability," + " or an extension that explicitly enables 16-bit floating point."; + } + if (num_bits == 64) { + if (_.HasCapability(spv::Capability::Float64)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 64-bit floating point " + << "type requires the Float64 capability."; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat."; +} + +spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) { + const auto component_index = 1; + const auto component_id = inst->GetOperandAs(component_index); + const auto component_type = _.FindDef(component_id); + if (component_type) { + bool isPointer = component_type->opcode() == spv::Op::OpTypePointer; + bool isScalar = spvOpcodeIsScalarType(component_type->opcode()); + + if (_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) && + !isPointer && !isScalar) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid OpTypeVector Component Type " + << _.getIdName(component_id) + << ": Expected a scalar or pointer type when using the " + "SPV_INTEL_masked_gather_scatter extension."; + } else if (!_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) && + !isScalar) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVector Component Type " << _.getIdName(component_id) + << " is not a scalar type."; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVector Component Type " << _.getIdName(component_id) + << " is not a scalar type."; + } + + // Validates that the number of components in the vector is valid. + // Vector types can only be parameterized as having 2, 3, or 4 components. + // If the Vector16 capability is added, 8 and 16 components are also allowed. + auto num_components = inst->GetOperandAs(2); + if (num_components == 2 || num_components == 3 || num_components == 4) { + return SPV_SUCCESS; + } else if (num_components == 8 || num_components == 16) { + if (_.HasCapability(spv::Capability::Vector16)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Having " << num_components << " components for " + << spvOpcodeString(inst->opcode()) + << " requires the Vector16 capability"; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal number of components (" << num_components << ") for " + << spvOpcodeString(inst->opcode()); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeCooperativeVectorNV(ValidationState_t& _, + const Instruction* inst) { + const auto component_index = 1; + const auto component_type_id = inst->GetOperandAs(component_index); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || (spv::Op::OpTypeFloat != component_type->opcode() && + spv::Op::OpTypeInt != component_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeVectorNV Component Type " + << _.getIdName(component_type_id) + << " is not a scalar numerical type."; + } + + const auto num_components_index = 2; + const auto num_components_id = + inst->GetOperandAs(num_components_index); + const auto num_components = _.FindDef(num_components_id); + if (!num_components || !spvOpcodeIsConstant(num_components->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeVectorNV component count " + << _.getIdName(num_components_id) + << " is not a scalar constant type."; + } + + // NOTE: Check the initialiser value of the constant + const auto const_inst = num_components->words(); + const auto const_result_type_index = 1; + const auto const_result_type = _.FindDef(const_inst[const_result_type_index]); + if (!const_result_type || spv::Op::OpTypeInt != const_result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeVectorNV component count " + << _.getIdName(num_components_id) + << " is not a constant integer type."; + } + + int64_t num_components_value; + if (_.EvalConstantValInt64(num_components_id, &num_components_value)) { + auto& type_words = const_result_type->words(); + const bool is_signed = type_words[3] > 0; + if (num_components_value == 0 || (num_components_value < 0 && is_signed)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeVectorNV component count " + << _.getIdName(num_components_id) + << " default value must be at least 1: found " + << num_components_value; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeMatrix(ValidationState_t& _, const Instruction* inst) { + const auto column_type_index = 1; + const auto column_type_id = inst->GetOperandAs(column_type_index); + const auto column_type = _.FindDef(column_type_id); + if (!column_type || spv::Op::OpTypeVector != column_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Columns in a matrix must be of type vector."; + } + + // Trace back once more to find out the type of components in the vector. + // Operand 1 is the of the type of data in the vector. + const auto comp_type_id = column_type->GetOperandAs(1); + auto comp_type_instruction = _.FindDef(comp_type_id); + if (comp_type_instruction->opcode() != spv::Op::OpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be " + "parameterized with " + "floating-point types."; + } + + // Validates that the matrix has 2,3, or 4 columns. + auto num_cols = inst->GetOperandAs(2); + if (num_cols != 2 && num_cols != 3 && num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be " + "parameterized as having " + "only 2, 3, or 4 columns."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { + const auto element_type_index = 1; + const auto element_type_id = inst->GetOperandAs(element_type_index); + const auto element_type = _.FindDef(element_type_id); + if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Element Type " << _.getIdName(element_type_id) + << " is not a type."; + } + + if (element_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Element Type " << _.getIdName(element_type_id) + << " is a void type."; + } + + if (_.HasCapability(spv::Capability::Shader)) { + if (element_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(element_type->id(), spv::Decoration::Block) || + _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) { + if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride"; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env) && + element_type->opcode() == spv::Op::OpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "OpTypeArray Element Type " + << _.getIdName(element_type_id) << " is not valid in " + << spvLogStringForEnv(_.context()->target_env) << " environments."; + } + + const auto length_index = 2; + const auto length_id = inst->GetOperandAs(length_index); + const auto length = _.FindDef(length_id); + if (!length || !spvOpcodeIsConstant(length->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length " << _.getIdName(length_id) + << " is not a scalar constant type."; + } + + // NOTE: Check the initialiser value of the constant + const auto const_inst = length->words(); + const auto const_result_type_index = 1; + const auto const_result_type = _.FindDef(const_inst[const_result_type_index]); + if (!const_result_type || spv::Op::OpTypeInt != const_result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length " << _.getIdName(length_id) + << " is not a constant integer type."; + } + + int64_t length_value; + if (_.EvalConstantValInt64(length_id, &length_value)) { + auto& type_words = const_result_type->words(); + const bool is_signed = type_words[3] > 0; + if (length_value == 0 || (length_value < 0 && is_signed)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length " << _.getIdName(length_id) + << " default value must be at least 1: found " << length_value; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _, + const Instruction* inst) { + const auto element_type_index = 1; + const auto element_id = inst->GetOperandAs(element_type_index); + const auto element_type = _.FindDef(element_id); + if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeRuntimeArray Element Type " << _.getIdName(element_id) + << " is not a type."; + } + + if (element_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeRuntimeArray Element Type " << _.getIdName(element_id) + << " is a void type."; + } + + if (_.HasCapability(spv::Capability::Shader)) { + if (element_type->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(element_type->id(), spv::Decoration::Block) || + _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) { + if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Array containing a Block or BufferBlock must not be " + "decorated with ArrayStride"; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env) && + element_type->opcode() == spv::Op::OpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type " + << _.getIdName(element_id) << " is not valid in " + << spvLogStringForEnv(_.context()->target_env) << " environments."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { + const uint32_t struct_id = inst->GetOperandAs(0); + for (size_t member_type_index = 1; + member_type_index < inst->operands().size(); ++member_type_index) { + auto member_type_id = inst->GetOperandAs(member_type_index); + if (member_type_id == inst->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structure members may not be self references"; + } + + auto member_type = _.FindDef(member_type_id); + if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeStruct Member Type " << _.getIdName(member_type_id) + << " is not a type."; + } + if (member_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structures cannot contain a void type."; + } + if (spv::Op::OpTypeStruct == member_type->opcode() && + _.IsStructTypeWithBuiltInMember(member_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structure " << _.getIdName(member_type_id) + << " contains members with BuiltIn decoration. Therefore this " + << "structure may not be contained as a member of another " + << "structure " + << "type. Structure " << _.getIdName(struct_id) + << " contains structure " << _.getIdName(member_type_id) + << "."; + } + + if (spvIsVulkanEnv(_.context()->target_env) && + member_type->opcode() == spv::Op::OpTypeRuntimeArray) { + const bool is_last_member = + member_type_index == inst->operands().size() - 1; + if (!is_last_member) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "In " + << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeRuntimeArray must only be used for the last member " + "of an OpTypeStruct"; + } + + if (!_.HasDecoration(inst->id(), spv::Decoration::Block) && + !_.HasDecoration(inst->id(), spv::Decoration::BufferBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) + << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeStruct containing an OpTypeRuntimeArray " + << "must be decorated with Block or BufferBlock."; + } + } + } + + bool has_nested_blockOrBufferBlock_struct = false; + // Struct members start at word 2 of OpTypeStruct instruction. + for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { + auto member = inst->word(word_i); + if (_.ContainsType( + member, + [&_](const Instruction* type_inst) { + if (type_inst->opcode() == spv::Op::OpTypeStruct && + (_.HasDecoration(type_inst->id(), spv::Decoration::Block) || + _.HasDecoration(type_inst->id(), + spv::Decoration::BufferBlock))) { + return true; + } + return false; + }, + /* traverse_all_types = */ false)) { + has_nested_blockOrBufferBlock_struct = true; + break; + } + } + + _.SetHasNestedBlockOrBufferBlockStruct(inst->id(), + has_nested_blockOrBufferBlock_struct); + if (_.GetHasNestedBlockOrBufferBlockStruct(inst->id()) && + (_.HasDecoration(inst->id(), spv::Decoration::BufferBlock) || + _.HasDecoration(inst->id(), spv::Decoration::Block))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "rules: A Block or BufferBlock cannot be nested within another " + "Block or BufferBlock. "; + } + + std::unordered_set built_in_members; + for (auto decoration : _.id_decorations(struct_id)) { + if (decoration.dec_type() == spv::Decoration::BuiltIn && + decoration.struct_member_index() != Decoration::kInvalidMember) { + built_in_members.insert(decoration.struct_member_index()); + } + } + int num_struct_members = static_cast(inst->operands().size() - 1); + int num_builtin_members = static_cast(built_in_members.size()); + if (num_builtin_members > 0 && num_builtin_members != num_struct_members) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "When BuiltIn decoration is applied to a structure-type member, " + << "all members of that structure type must also be decorated with " + << "BuiltIn (No allowed mixing of built-in variables and " + << "non-built-in variables within a single structure). Structure id " + << struct_id << " does not meet this requirement."; + } + if (num_builtin_members > 0) { + _.RegisterStructTypeWithBuiltInMember(struct_id); + } + + const auto isOpaqueType = [&_](const Instruction* opaque_inst) { + auto opcode = opaque_inst->opcode(); + if (_.HasCapability(spv::Capability::BindlessTextureNV) && + (opcode == spv::Op::OpTypeImage || opcode == spv::Op::OpTypeSampler || + opcode == spv::Op::OpTypeSampledImage)) { + return false; + } else if (spvOpcodeIsBaseOpaqueType(opcode)) { + return true; + } + return false; + }; + + if (spvIsVulkanEnv(_.context()->target_env) && + !_.options()->before_hlsl_legalization && + _.ContainsType(inst->id(), isOpaqueType)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4667) << "In " + << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeStruct must not contain an opaque type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypePointer(ValidationState_t& _, + const Instruction* inst) { + auto type_id = inst->GetOperandAs(2); + auto type = _.FindDef(type_id); + if (!type || !spvOpcodeGeneratesType(type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypePointer Type " << _.getIdName(type_id) + << " is not a type."; + } + // See if this points to a storage image. + const auto storage_class = inst->GetOperandAs(1); + if (storage_class == spv::StorageClass::UniformConstant) { + // Unpack an optional level of arraying. + if (type->opcode() == spv::Op::OpTypeArray || + type->opcode() == spv::Op::OpTypeRuntimeArray) { + type_id = type->GetOperandAs(1); + type = _.FindDef(type_id); + } + if (type->opcode() == spv::Op::OpTypeImage) { + const auto sampled = type->GetOperandAs(6); + // 2 indicates this image is known to be be used without a sampler, i.e. + // a storage image. + if (sampled == 2) _.RegisterPointerToStorageImage(inst->id()); + } + } + + if (!_.IsValidStorageClass(storage_class)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << _.VkErrorID(4643) + << "Invalid storage class for target environment"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeFunction(ValidationState_t& _, + const Instruction* inst) { + const auto return_type_id = inst->GetOperandAs(1); + const auto return_type = _.FindDef(return_type_id); + if (!return_type || !spvOpcodeGeneratesType(return_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Return Type " << _.getIdName(return_type_id) + << " is not a type."; + } + size_t num_args = 0; + for (size_t param_type_index = 2; param_type_index < inst->operands().size(); + ++param_type_index, ++num_args) { + const auto param_id = inst->GetOperandAs(param_type_index); + const auto param_type = _.FindDef(param_id); + if (!param_type || !spvOpcodeGeneratesType(param_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Parameter Type " << _.getIdName(param_id) + << " is not a type."; + } + + if (param_type->opcode() == spv::Op::OpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Parameter Type " << _.getIdName(param_id) + << " cannot be OpTypeVoid."; + } + } + const uint32_t num_function_args_limit = + _.options()->universal_limits_.max_function_args; + if (num_args > num_function_args_limit) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction may not take more than " + << num_function_args_limit << " arguments. OpTypeFunction " + << _.getIdName(inst->GetOperandAs(0)) << " has " + << num_args << " arguments."; + } + + // The only valid uses of OpTypeFunction are in an OpFunction, debugging, or + // decoration instruction. + for (auto& pair : inst->uses()) { + const auto* use = pair.first; + if (use->opcode() != spv::Op::OpFunction && + !spvOpcodeIsDebug(use->opcode()) && !use->IsNonSemantic() && + !spvOpcodeIsDecoration(use->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, use) + << "Invalid use of function type result id " + << _.getIdName(inst->id()) << "."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeForwardPointer(ValidationState_t& _, + const Instruction* inst) { + const auto pointer_type_id = inst->GetOperandAs(0); + const auto pointer_type_inst = _.FindDef(pointer_type_id); + if (pointer_type_inst->opcode() != spv::Op::OpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer type in OpTypeForwardPointer is not a pointer type."; + } + + const auto storage_class = inst->GetOperandAs(1); + if (storage_class != pointer_type_inst->GetOperandAs(1)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Storage class in OpTypeForwardPointer does not match the " + << "pointer definition."; + } + + const auto pointee_type_id = pointer_type_inst->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_type_id); + if (!pointee_type || pointee_type->opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Forward pointers must point to a structure"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class != spv::StorageClass::PhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4711) + << "In Vulkan, OpTypeForwardPointer must have " + << "a storage class of PhysicalStorageBuffer."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeCooperativeMatrix(ValidationState_t& _, + const Instruction* inst) { + const auto component_type_index = 1; + const auto component_type_id = + inst->GetOperandAs(component_type_index); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || (spv::Op::OpTypeFloat != component_type->opcode() && + spv::Op::OpTypeInt != component_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Component Type " + << _.getIdName(component_type_id) + << " is not a scalar numerical type."; + } + + if (_.IsBfloat16ScalarType(component_type_id)) { + if (!_.HasCapability(spv::Capability::BFloat16CooperativeMatrixKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Component Type " + << _.getIdName(component_type_id) + << "require BFloat16CooperativeMatrixKHR be declared."; + } + } + + const auto scope_index = 2; + const auto scope_id = inst->GetOperandAs(scope_index); + const auto scope = _.FindDef(scope_id); + if (!scope || !_.IsIntScalarType(scope->type_id()) || + !spvOpcodeIsConstant(scope->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Scope " << _.getIdName(scope_id) + << " is not a constant instruction with scalar integer type."; + } + + const auto rows_index = 3; + const auto rows_id = inst->GetOperandAs(rows_index); + const auto rows = _.FindDef(rows_id); + if (!rows || !_.IsIntScalarType(rows->type_id()) || + !spvOpcodeIsConstant(rows->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Rows " << _.getIdName(rows_id) + << " is not a constant instruction with scalar integer type."; + } + + const auto cols_index = 4; + const auto cols_id = inst->GetOperandAs(cols_index); + const auto cols = _.FindDef(cols_id); + if (!cols || !_.IsIntScalarType(cols->type_id()) || + !spvOpcodeIsConstant(cols->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrix Cols " << _.getIdName(cols_id) + << " is not a constant instruction with scalar integer type."; + } + + if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + const auto use_index = 5; + const auto use_id = inst->GetOperandAs(use_index); + const auto use = _.FindDef(use_id); + if (!use || !_.IsIntScalarType(use->type_id()) || + !spvOpcodeIsConstant(use->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixKHR Use " << _.getIdName(use_id) + << " is not a constant instruction with scalar integer type."; + } + } + + uint64_t scope_value; + if (_.EvalConstantValUint64(scope_id, &scope_value)) { + if (scope_value == static_cast(spv::Scope::Workgroup)) { + for (auto entry_point_id : _.entry_points()) { + if (!_.EntryPointHasLocalSizeOrId(entry_point_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixKHR with ScopeWorkgroup " + << "used without specifying LocalSize or LocalSizeId " + << "for entry point " << _.getIdName(entry_point_id); + } + const auto local_size = _.EntryPointLocalSizeOrId(entry_point_id); + const auto mode = local_size->GetOperandAs(1); + if (mode == spv::ExecutionMode::LocalSizeId) { + uint32_t local_size_ids[3] = { + local_size->GetOperandAs(2), + local_size->GetOperandAs(3), + local_size->GetOperandAs(4), + }; + for (auto id : local_size_ids) { + if (_.FindDef(id) > inst) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixKHR with ScopeWorkgroup " + << "used before LocalSizeId constant value " + << _.getIdName(id) << " is defined."; + } + } + } + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeUntypedPointerKHR(ValidationState_t& _, + const Instruction* inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const auto sc = inst->GetOperandAs(1); + switch (sc) { + case spv::StorageClass::Workgroup: + if (!_.HasCapability( + spv::Capability::WorkgroupMemoryExplicitLayoutKHR)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Workgroup storage class untyped pointers in Vulkan " + "require WorkgroupMemoryExplicitLayoutKHR be declared"; + } + break; + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::Uniform: + case spv::StorageClass::PushConstant: + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Vulkan, untyped pointers can only be used in an " + "explicitly laid out storage class"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateTensorDim(ValidationState_t& _, const Instruction* inst) { + const auto dim_index = 1; + const auto dim_id = inst->GetOperandAs(dim_index); + const auto dim = _.FindDef(dim_id); + if (!dim || !_.IsIntScalarType(dim->type_id()) || + _.GetBitWidth(dim->type_id()) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Dim " + << _.getIdName(dim_id) << " is not a 32-bit integer."; + } + + constexpr uint32_t max_tensor_dim = 5; + + uint64_t dim_value; + if (_.EvalConstantValUint64(dim_id, &dim_value)) { + if (dim_value == 0 || dim_value > max_tensor_dim) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Dim " + << _.getIdName(dim_id) << " must be between 1 and " + << max_tensor_dim << "."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeTensorLayoutNV(ValidationState_t& _, + const Instruction* inst) { + if (auto error = ValidateTensorDim(_, inst)) return error; + + const auto clamp_index = 2; + const auto clamp_id = inst->GetOperandAs(clamp_index); + const auto clamp = _.FindDef(clamp_id); + if (!clamp || !_.IsIntScalarType(clamp->type_id()) || + _.GetBitWidth(clamp->type_id()) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " ClampMode " + << _.getIdName(clamp_id) << " is not a 32-bit integer."; + } + + uint64_t clamp_value; + if (_.EvalConstantValUint64(clamp_id, &clamp_value)) { + if (clamp_value > + static_cast(spv::TensorClampMode::RepeatMirrored)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " ClampMode " + << _.getIdName(clamp_id) << " must be a valid TensorClampMode."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeTensorViewNV(ValidationState_t& _, + const Instruction* inst) { + if (auto error = ValidateTensorDim(_, inst)) return error; + + const auto has_dim_index = 2; + const auto has_dim_id = inst->GetOperandAs(has_dim_index); + const auto has_dim = _.FindDef(has_dim_id); + if (!has_dim || !_.IsBoolScalarType(has_dim->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " HasDimensions " + << _.getIdName(has_dim_id) << " is not a boolean value."; + } + + uint32_t permutation_mask = 0; + bool all_constant = true; + const auto num_dim = inst->operands().size() - 3; + for (size_t p_index = 3; p_index < inst->operands().size(); ++p_index) { + auto p_id = inst->GetOperandAs(p_index); + const auto p = _.FindDef(p_id); + if (!p || !_.IsIntScalarType(p->type_id()) || + _.GetBitWidth(p->type_id()) != 32) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Permutation " + << _.getIdName(p_id) << " is not a 32-bit integer."; + } + + uint64_t p_value; + if (_.EvalConstantValUint64(p_id, &p_value)) { + if (p_value >= num_dim) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) << " Permutation " + << _.getIdName(p_id) << " must be a valid dimension."; + } + permutation_mask |= 1 << p_value; + } else { + all_constant = false; + } + } + if (all_constant && permutation_mask != (1U << num_dim) - 1U) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) + << " Permutation values don't form a valid permutation."; + } + + uint64_t dim_value; + if (_.EvalConstantValUint64(inst->GetOperandAs(1), &dim_value)) { + if (dim_value != num_dim) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << spvOpcodeString(inst->opcode()) + << " Incorrect number of permutation values."; + } + } + + return SPV_SUCCESS; +} +} // namespace + +spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) { + if (!spvOpcodeGeneratesType(inst->opcode()) && + inst->opcode() != spv::Op::OpTypeForwardPointer) { + return SPV_SUCCESS; + } + + if (auto error = ValidateUniqueness(_, inst)) return error; + + switch (inst->opcode()) { + case spv::Op::OpTypeInt: + if (auto error = ValidateTypeInt(_, inst)) return error; + break; + case spv::Op::OpTypeFloat: + if (auto error = ValidateTypeFloat(_, inst)) return error; + break; + case spv::Op::OpTypeVector: + if (auto error = ValidateTypeVector(_, inst)) return error; + break; + case spv::Op::OpTypeMatrix: + if (auto error = ValidateTypeMatrix(_, inst)) return error; + break; + case spv::Op::OpTypeArray: + if (auto error = ValidateTypeArray(_, inst)) return error; + break; + case spv::Op::OpTypeRuntimeArray: + if (auto error = ValidateTypeRuntimeArray(_, inst)) return error; + break; + case spv::Op::OpTypeStruct: + if (auto error = ValidateTypeStruct(_, inst)) return error; + break; + case spv::Op::OpTypePointer: + if (auto error = ValidateTypePointer(_, inst)) return error; + break; + case spv::Op::OpTypeFunction: + if (auto error = ValidateTypeFunction(_, inst)) return error; + break; + case spv::Op::OpTypeForwardPointer: + if (auto error = ValidateTypeForwardPointer(_, inst)) return error; + break; + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error; + break; + case spv::Op::OpTypeCooperativeVectorNV: + if (auto error = ValidateTypeCooperativeVectorNV(_, inst)) return error; + break; + case spv::Op::OpTypeUntypedPointerKHR: + if (auto error = ValidateTypeUntypedPointerKHR(_, inst)) return error; + break; + case spv::Op::OpTypeTensorLayoutNV: + if (auto error = ValidateTypeTensorLayoutNV(_, inst)) return error; + break; + case spv::Op::OpTypeTensorViewNV: + if (auto error = ValidateTypeTensorViewNV(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.cpp b/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.cpp new file mode 100644 index 00000000..96d80f08 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.cpp @@ -0,0 +1,2582 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validation_state.h" + +#include +#include +#include + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/util/make_unique.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { +namespace { + +ModuleLayoutSection InstructionLayoutSection( + ModuleLayoutSection current_section, spv::Op op) { + // See Section 2.4 + if (spvOpcodeGeneratesType(op) || spvOpcodeIsConstant(op)) + return kLayoutTypes; + + switch (op) { + case spv::Op::OpCapability: + return kLayoutCapabilities; + case spv::Op::OpExtension: + return kLayoutExtensions; + case spv::Op::OpExtInstImport: + return kLayoutExtInstImport; + case spv::Op::OpMemoryModel: + return kLayoutMemoryModel; + case spv::Op::OpEntryPoint: + return kLayoutEntryPoint; + case spv::Op::OpExecutionMode: + case spv::Op::OpExecutionModeId: + return kLayoutExecutionMode; + case spv::Op::OpSourceContinued: + case spv::Op::OpSource: + case spv::Op::OpSourceExtension: + case spv::Op::OpString: + return kLayoutDebug1; + case spv::Op::OpName: + case spv::Op::OpMemberName: + return kLayoutDebug2; + case spv::Op::OpModuleProcessed: + return kLayoutDebug3; + case spv::Op::OpDecorate: + case spv::Op::OpMemberDecorate: + case spv::Op::OpGroupDecorate: + case spv::Op::OpGroupMemberDecorate: + case spv::Op::OpDecorationGroup: + case spv::Op::OpDecorateId: + case spv::Op::OpDecorateStringGOOGLE: + case spv::Op::OpMemberDecorateStringGOOGLE: + return kLayoutAnnotations; + case spv::Op::OpTypeForwardPointer: + return kLayoutTypes; + case spv::Op::OpVariable: + case spv::Op::OpUntypedVariableKHR: + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case spv::Op::OpExtInst: + case spv::Op::OpExtInstWithForwardRefsKHR: + // spv::Op::OpExtInst is only allowed in types section for certain + // extended instruction sets. This will be checked separately. + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case spv::Op::OpLine: + case spv::Op::OpNoLine: + case spv::Op::OpUndef: + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case spv::Op::OpFunction: + case spv::Op::OpFunctionParameter: + case spv::Op::OpFunctionEnd: + if (current_section == kLayoutFunctionDeclarations) + return kLayoutFunctionDeclarations; + return kLayoutFunctionDefinitions; + case spv::Op::OpSamplerImageAddressingModeNV: + return kLayoutSamplerImageAddressMode; + default: + break; + } + return kLayoutFunctionDefinitions; +} + +bool IsInstructionInLayoutSection(ModuleLayoutSection layout, spv::Op op) { + return layout == InstructionLayoutSection(layout, op); +} + +// Counts the number of instructions and functions in the file. +spv_result_t CountInstructions(void* user_data, + const spv_parsed_instruction_t* inst) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + if (spv::Op(inst->opcode) == spv::Op::OpFunction) { + _.increment_total_functions(); + } + _.increment_total_instructions(); + + return SPV_SUCCESS; +} + +spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t, + uint32_t version, uint32_t generator, uint32_t id_bound, + uint32_t) { + ValidationState_t& vstate = + *(reinterpret_cast(user_data)); + vstate.setIdBound(id_bound); + vstate.setGenerator(generator); + vstate.setVersion(version); + + return SPV_SUCCESS; +} + +// Add features based on SPIR-V core version number. +void UpdateFeaturesBasedOnSpirvVersion(ValidationState_t::Feature* features, + uint32_t version) { + assert(features); + if (version >= SPV_SPIRV_VERSION_WORD(1, 4)) { + features->select_between_composites = true; + features->copy_memory_permits_two_memory_accesses = true; + features->uconvert_spec_constant_op = true; + features->nonwritable_var_in_function_or_private = true; + } +} + +} // namespace + +ValidationState_t::ValidationState_t(const spv_const_context ctx, + const spv_const_validator_options opt, + const uint32_t* words, + const size_t num_words, + const uint32_t max_warnings) + : context_(ctx), + options_(opt), + words_(words), + num_words_(num_words), + unresolved_forward_ids_{}, + operand_names_{}, + current_layout_section_(kLayoutCapabilities), + module_functions_(), + module_capabilities_(), + module_extensions_(), + ordered_instructions_(), + all_definitions_(), + global_vars_(), + local_vars_(), + struct_nesting_depth_(), + struct_has_nested_blockorbufferblock_struct_(), + grammar_(ctx), + addressing_model_(spv::AddressingModel::Max), + memory_model_(spv::MemoryModel::Max), + pointer_size_and_alignment_(0), + sampler_image_addressing_mode_(0), + in_function_(false), + num_of_warnings_(0), + max_num_of_warnings_(max_warnings) { + assert(opt && "Validator options may not be Null."); + + const auto env = context_->target_env; + + if (spvIsVulkanEnv(env)) { + // Vulkan 1.1 includes VK_KHR_relaxed_block_layout in core. + if (env != SPV_ENV_VULKAN_1_0) { + features_.env_relaxed_block_layout = true; + } + } + + // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4. + switch (env) { + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + features_.env_allow_localsizeid = false; + break; + default: + features_.env_allow_localsizeid = true; + break; + } + + // Only attempt to count if we have words, otherwise let the other validation + // fail and generate an error. + if (num_words > 0) { + // Count the number of instructions in the binary. + // This parse should not produce any error messages. Hijack the context and + // replace the message consumer so that we do not pollute any state in input + // consumer. + spv_context_t hijacked_context = *ctx; + hijacked_context.consumer = [](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}; + spvBinaryParse(&hijacked_context, this, words, num_words, setHeader, + CountInstructions, + /* diagnostic = */ nullptr); + preallocateStorage(); + } + UpdateFeaturesBasedOnSpirvVersion(&features_, version_); + + name_mapper_ = spvtools::GetTrivialNameMapper(); + if (options_->use_friendly_names) { + friendly_mapper_ = spvtools::MakeUnique( + context_, words_, num_words_); + name_mapper_ = friendly_mapper_->GetNameMapper(); + } +} + +void ValidationState_t::preallocateStorage() { + ordered_instructions_.reserve(total_instructions_); + module_functions_.reserve(total_functions_); +} + +spv_result_t ValidationState_t::ForwardDeclareId(uint32_t id) { + unresolved_forward_ids_.insert(id); + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RemoveIfForwardDeclared(uint32_t id) { + unresolved_forward_ids_.erase(id); + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RegisterForwardPointer(uint32_t id) { + forward_pointer_ids_.insert(id); + return SPV_SUCCESS; +} + +bool ValidationState_t::IsForwardPointer(uint32_t id) const { + return (forward_pointer_ids_.find(id) != forward_pointer_ids_.end()); +} + +void ValidationState_t::AssignNameToId(uint32_t id, std::string name) { + operand_names_[id] = name; +} + +std::string ValidationState_t::getIdName(uint32_t id) const { + const std::string id_name = name_mapper_(id); + + std::stringstream out; + out << "'" << id << "[%" << id_name << "]'"; + return out.str(); +} + +size_t ValidationState_t::unresolved_forward_id_count() const { + return unresolved_forward_ids_.size(); +} + +std::vector ValidationState_t::UnresolvedForwardIds() const { + std::vector out(std::begin(unresolved_forward_ids_), + std::end(unresolved_forward_ids_)); + return out; +} + +bool ValidationState_t::IsDefinedId(uint32_t id) const { + return all_definitions_.find(id) != std::end(all_definitions_); +} + +const Instruction* ValidationState_t::FindDef(uint32_t id) const { + auto it = all_definitions_.find(id); + if (it == all_definitions_.end()) return nullptr; + return it->second; +} + +Instruction* ValidationState_t::FindDef(uint32_t id) { + auto it = all_definitions_.find(id); + if (it == all_definitions_.end()) return nullptr; + return it->second; +} + +ModuleLayoutSection ValidationState_t::current_layout_section() const { + return current_layout_section_; +} + +void ValidationState_t::ProgressToNextLayoutSectionOrder() { + // Guard against going past the last element(kLayoutFunctionDefinitions) + if (current_layout_section_ <= kLayoutFunctionDefinitions) { + current_layout_section_ = + static_cast(current_layout_section_ + 1); + } +} + +bool ValidationState_t::IsOpcodeInPreviousLayoutSection(spv::Op op) { + ModuleLayoutSection section = + InstructionLayoutSection(current_layout_section_, op); + return section < current_layout_section_; +} + +bool ValidationState_t::IsOpcodeInCurrentLayoutSection(spv::Op op) { + return IsInstructionInLayoutSection(current_layout_section_, op); +} + +DiagnosticStream ValidationState_t::diag(spv_result_t error_code, + const Instruction* inst) { + if (error_code == SPV_WARNING) { + if (num_of_warnings_ == max_num_of_warnings_) { + DiagnosticStream({0, 0, 0}, context_->consumer, "", error_code) + << "Other warnings have been suppressed.\n"; + } + if (num_of_warnings_ >= max_num_of_warnings_) { + return DiagnosticStream({0, 0, 0}, nullptr, "", error_code); + } + ++num_of_warnings_; + } + + std::string disassembly; + if (inst) disassembly = Disassemble(*inst); + + return DiagnosticStream({0, 0, inst ? inst->LineNum() : 0}, + context_->consumer, disassembly, error_code); +} + +std::vector& ValidationState_t::functions() { + return module_functions_; +} + +Function& ValidationState_t::current_function() { + assert(in_function_body()); + return module_functions_.back(); +} + +const Function& ValidationState_t::current_function() const { + assert(in_function_body()); + return module_functions_.back(); +} + +const Function* ValidationState_t::function(uint32_t id) const { + const auto it = id_to_function_.find(id); + if (it == id_to_function_.end()) return nullptr; + return it->second; +} + +Function* ValidationState_t::function(uint32_t id) { + auto it = id_to_function_.find(id); + if (it == id_to_function_.end()) return nullptr; + return it->second; +} + +bool ValidationState_t::in_function_body() const { return in_function_; } + +bool ValidationState_t::in_block() const { + return module_functions_.empty() == false && + module_functions_.back().current_block() != nullptr; +} + +void ValidationState_t::RegisterCapability(spv::Capability cap) { + // Avoid redundant work. Otherwise the recursion could induce work + // quadrdatic in the capability dependency depth. (Ok, not much, but + // it's something.) + if (module_capabilities_.contains(cap)) return; + + module_capabilities_.insert(cap); + spv_operand_desc desc; + if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + uint32_t(cap), &desc)) { + for (auto capability : + CapabilitySet(desc->numCapabilities, desc->capabilities)) { + RegisterCapability(capability); + } + } + + switch (cap) { + case spv::Capability::Kernel: + features_.group_ops_reduce_and_scans = true; + break; + case spv::Capability::Int8: + features_.use_int8_type = true; + features_.declare_int8_type = true; + break; + case spv::Capability::StorageBuffer8BitAccess: + case spv::Capability::UniformAndStorageBuffer8BitAccess: + case spv::Capability::StoragePushConstant8: + case spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR: + features_.declare_int8_type = true; + break; + case spv::Capability::Int16: + features_.declare_int16_type = true; + break; + case spv::Capability::Float16: + case spv::Capability::Float16Buffer: + features_.declare_float16_type = true; + break; + case spv::Capability::StorageUniformBufferBlock16: + case spv::Capability::StorageUniform16: + case spv::Capability::StoragePushConstant16: + case spv::Capability::StorageInputOutput16: + case spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR: + features_.declare_int16_type = true; + features_.declare_float16_type = true; + features_.free_fp_rounding_mode = true; + break; + case spv::Capability::VariablePointers: + case spv::Capability::VariablePointersStorageBuffer: + features_.variable_pointers = true; + break; + default: + // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses + // capability spv::Capability::RayTracingNV. + // spv::Capability::RayTracingProvisionalKHR would need the same + // treatment. One of the differences going from SPV_KHR_ray_tracing from + // provisional to final spec was the provisional spec uses Locations + // for variables in certain storage classes, just like the + // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension. + // The final SPV_KHR_ray_tracing uses a different capability token + // number, so it doesn't fall into this case. + break; + } +} + +void ValidationState_t::RegisterExtension(Extension ext) { + if (module_extensions_.contains(ext)) return; + + module_extensions_.insert(ext); + + switch (ext) { + case kSPV_AMD_gpu_shader_half_float: + case kSPV_AMD_gpu_shader_half_float_fetch: + // SPV_AMD_gpu_shader_half_float enables float16 type. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/1375 + features_.declare_float16_type = true; + break; + case kSPV_AMD_gpu_shader_int16: + // This is not yet in the extension, but it's recommended for it. + // See https://github.com/KhronosGroup/glslang/issues/848 + features_.uconvert_spec_constant_op = true; + break; + case kSPV_AMD_shader_ballot: + // The grammar doesn't encode the fact that SPV_AMD_shader_ballot + // enables the use of group operations Reduce, InclusiveScan, + // and ExclusiveScan. Enable it manually. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/991 + features_.group_ops_reduce_and_scans = true; + break; + default: + break; + } +} + +bool ValidationState_t::HasAnyOfCapabilities( + const CapabilitySet& capabilities) const { + return module_capabilities_.HasAnyOf(capabilities); +} + +bool ValidationState_t::HasAnyOfExtensions( + const ExtensionSet& extensions) const { + return module_extensions_.HasAnyOf(extensions); +} + +void ValidationState_t::set_addressing_model(spv::AddressingModel am) { + addressing_model_ = am; + switch (am) { + case spv::AddressingModel::Physical32: + pointer_size_and_alignment_ = 4; + break; + default: + // fall through + case spv::AddressingModel::Physical64: + case spv::AddressingModel::PhysicalStorageBuffer64: + pointer_size_and_alignment_ = 8; + break; + } +} + +spv::AddressingModel ValidationState_t::addressing_model() const { + return addressing_model_; +} + +void ValidationState_t::set_memory_model(spv::MemoryModel mm) { + memory_model_ = mm; +} + +spv::MemoryModel ValidationState_t::memory_model() const { + return memory_model_; +} + +void ValidationState_t::set_samplerimage_variable_address_mode( + uint32_t bit_width) { + sampler_image_addressing_mode_ = bit_width; +} + +uint32_t ValidationState_t::samplerimage_variable_address_mode() const { + return sampler_image_addressing_mode_; +} + +spv_result_t ValidationState_t::RegisterFunction( + uint32_t id, uint32_t ret_type_id, + spv::FunctionControlMask function_control, uint32_t function_type_id) { + assert(in_function_body() == false && + "RegisterFunction can only be called when parsing the binary outside " + "of another function"); + in_function_ = true; + module_functions_.emplace_back(id, ret_type_id, function_control, + function_type_id); + id_to_function_.emplace(id, ¤t_function()); + + // TODO(umar): validate function type and type_id + + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RegisterFunctionEnd() { + assert(in_function_body() == true && + "RegisterFunctionEnd can only be called when parsing the binary " + "inside of another function"); + assert(in_block() == false && + "RegisterFunctionParameter can only be called when parsing the binary " + "outside of a block"); + current_function().RegisterFunctionEnd(); + in_function_ = false; + return SPV_SUCCESS; +} + +Instruction* ValidationState_t::AddOrderedInstruction( + const spv_parsed_instruction_t* inst) { + ordered_instructions_.emplace_back(inst); + ordered_instructions_.back().SetLineNum(ordered_instructions_.size()); + return &ordered_instructions_.back(); +} + +// Improves diagnostic messages by collecting names of IDs +void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { + switch (inst->opcode()) { + case spv::Op::OpName: { + const auto target = inst->GetOperandAs(0); + const std::string str = inst->GetOperandAs(1); + AssignNameToId(target, str); + break; + } + case spv::Op::OpMemberName: { + const auto target = inst->GetOperandAs(0); + const std::string str = inst->GetOperandAs(2); + AssignNameToId(target, str); + break; + } + case spv::Op::OpSourceContinued: + case spv::Op::OpSource: + case spv::Op::OpSourceExtension: + case spv::Op::OpString: + case spv::Op::OpLine: + case spv::Op::OpNoLine: + default: + break; + } +} + +void ValidationState_t::RegisterInstruction(Instruction* inst) { + if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst)); + + // Some validation checks are easier by getting all the consumers + for (size_t i = 0; i < inst->operands().size(); ++i) { + const spv_parsed_operand_t& operand = inst->operand(i); + if ((SPV_OPERAND_TYPE_ID == operand.type) || + (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) { + const uint32_t operand_word = inst->word(operand.offset); + Instruction* operand_inst = FindDef(operand_word); + if (!operand_inst) { + continue; + } + + // If the instruction is using an OpTypeSampledImage as an operand, it + // should be recorded. The validator will ensure that all usages of an + // OpTypeSampledImage and its definition are in the same basic block. + if ((SPV_OPERAND_TYPE_ID == operand.type) && + (spv::Op::OpSampledImage == operand_inst->opcode())) { + RegisterSampledImageConsumer(operand_word, inst); + } + + // In order to track storage classes (not Function) used per execution + // model we can't use RegisterExecutionModelLimitation on instructions + // like OpTypePointer which are going to be in the pre-function section. + // Instead just need to register storage class usage for consumers in a + // function block. + if (inst->function()) { + if (operand_inst->opcode() == spv::Op::OpTypePointer) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs(1), inst); + } else if (operand_inst->opcode() == spv::Op::OpVariable) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs(2), inst); + } + } + } + } +} + +std::vector ValidationState_t::getSampledImageConsumers( + uint32_t sampled_image_id) const { + std::vector result; + auto iter = sampled_image_consumers_.find(sampled_image_id); + if (iter != sampled_image_consumers_.end()) { + result = iter->second; + } + return result; +} + +void ValidationState_t::RegisterSampledImageConsumer(uint32_t sampled_image_id, + Instruction* consumer) { + sampled_image_consumers_[sampled_image_id].push_back(consumer); +} + +void ValidationState_t::RegisterQCOMImageProcessingTextureConsumer( + uint32_t texture_id, const Instruction* consumer0, + const Instruction* consumer1) { + if (HasDecoration(texture_id, spv::Decoration::WeightTextureQCOM) || + HasDecoration(texture_id, spv::Decoration::BlockMatchTextureQCOM) || + HasDecoration(texture_id, spv::Decoration::BlockMatchSamplerQCOM)) { + qcom_image_processing_consumers_.insert(consumer0->id()); + if (consumer1) { + qcom_image_processing_consumers_.insert(consumer1->id()); + } + } +} + +void ValidationState_t::RegisterStorageClassConsumer( + spv::StorageClass storage_class, Instruction* consumer) { + if (spvIsVulkanEnv(context()->target_env)) { + if (storage_class == spv::StorageClass::Output) { + std::string errorVUID = VkErrorID(4644); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + spv::ExecutionModel model, + std::string* message) { + if (model == spv::ExecutionModel::GLCompute || + model == spv::ExecutionModel::RayGenerationKHR || + model == spv::ExecutionModel::IntersectionKHR || + model == spv::ExecutionModel::AnyHitKHR || + model == spv::ExecutionModel::ClosestHitKHR || + model == spv::ExecutionModel::MissKHR || + model == spv::ExecutionModel::CallableKHR) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Output Storage Class must not be " + "used in GLCompute, RayGenerationKHR, IntersectionKHR, " + "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " + "execution models"; + } + return false; + } + return true; + }); + } + + if (storage_class == spv::StorageClass::Workgroup) { + std::string errorVUID = VkErrorID(4645); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::GLCompute && + model != spv::ExecutionModel::TaskNV && + model != spv::ExecutionModel::MeshNV && + model != spv::ExecutionModel::TaskEXT && + model != spv::ExecutionModel::MeshEXT) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Workgroup Storage Class is limited " + "to MeshNV, TaskNV, and GLCompute execution model"; + } + return false; + } + return true; + }); + } + } + + if (storage_class == spv::StorageClass::CallableDataKHR) { + std::string errorVUID = VkErrorID(4704); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::CallableKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "CallableDataKHR Storage Class is limited to " + "RayGenerationKHR, ClosestHitKHR, CallableKHR, and " + "MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::IncomingCallableDataKHR) { + std::string errorVUID = VkErrorID(4705); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::CallableKHR) { + if (message) { + *message = + errorVUID + + "IncomingCallableDataKHR Storage Class is limited to " + "CallableKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::RayPayloadKHR) { + std::string errorVUID = VkErrorID(4698); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "RayPayloadKHR Storage Class is limited to RayGenerationKHR, " + "ClosestHitKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::HitAttributeKHR) { + std::string errorVUID = VkErrorID(4701); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::IntersectionKHR && + model != spv::ExecutionModel::AnyHitKHR && + model != spv::ExecutionModel::ClosestHitKHR) { + if (message) { + *message = errorVUID + + "HitAttributeKHR Storage Class is limited to " + "IntersectionKHR, AnyHitKHR, sand ClosestHitKHR " + "execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::IncomingRayPayloadKHR) { + std::string errorVUID = VkErrorID(4699); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::AnyHitKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "IncomingRayPayloadKHR Storage Class is limited to " + "AnyHitKHR, ClosestHitKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::ShaderRecordBufferKHR) { + std::string errorVUID = VkErrorID(7119); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::IntersectionKHR && + model != spv::ExecutionModel::AnyHitKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::CallableKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + errorVUID + + "ShaderRecordBufferKHR Storage Class is limited to " + "RayGenerationKHR, IntersectionKHR, AnyHitKHR, " + "ClosestHitKHR, CallableKHR, and MissKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation( + [](spv::ExecutionModel model, std::string* message) { + if (model != spv::ExecutionModel::TaskEXT && + model != spv::ExecutionModel::MeshEXT) { + if (message) { + *message = + "TaskPayloadWorkgroupEXT Storage Class is limited to " + "TaskEXT and MeshKHR execution model"; + } + return false; + } + return true; + }); + } else if (storage_class == spv::StorageClass::HitObjectAttributeNV) { + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, + std::string* message) { + if (model != spv::ExecutionModel::RayGenerationKHR && + model != spv::ExecutionModel::ClosestHitKHR && + model != spv::ExecutionModel::MissKHR) { + if (message) { + *message = + "HitObjectAttributeNV Storage Class is limited to " + "RayGenerationKHR, ClosestHitKHR or MissKHR execution model"; + } + return false; + } + return true; + }); + } +} + +uint32_t ValidationState_t::getIdBound() const { return id_bound_; } + +void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; } + +bool ValidationState_t::RegisterUniqueTypeDeclaration(const Instruction* inst) { + std::vector key; + key.push_back(static_cast(inst->opcode())); + for (size_t index = 0; index < inst->operands().size(); ++index) { + const spv_parsed_operand_t& operand = inst->operand(index); + + if (operand.type == SPV_OPERAND_TYPE_RESULT_ID) continue; + + const int words_begin = operand.offset; + const int words_end = words_begin + operand.num_words; + assert(words_end <= static_cast(inst->words().size())); + + key.insert(key.end(), inst->words().begin() + words_begin, + inst->words().begin() + words_end); + } + + return unique_type_declarations_.insert(std::move(key)).second; +} + +uint32_t ValidationState_t::GetTypeId(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst ? inst->type_id() : 0; +} + +spv::Op ValidationState_t::GetIdOpcode(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst ? inst->opcode() : spv::Op::OpNop; +} + +uint32_t ValidationState_t::GetComponentType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + switch (inst->opcode()) { + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeBool: + return id; + + case spv::Op::OpTypeArray: + return inst->word(2); + + case spv::Op::OpTypeVector: + return inst->word(2); + + case spv::Op::OpTypeMatrix: + return GetComponentType(inst->word(2)); + + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + return inst->word(2); + + default: + break; + } + + if (inst->type_id()) return GetComponentType(inst->type_id()); + + assert(0); + return 0; +} + +uint32_t ValidationState_t::GetDimension(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + switch (inst->opcode()) { + case spv::Op::OpTypeFloat: + case spv::Op::OpTypeInt: + case spv::Op::OpTypeBool: + return 1; + + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + return inst->word(3); + + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + // Actual dimension isn't known, return 0 + return 0; + + default: + break; + } + + if (inst->type_id()) return GetDimension(inst->type_id()); + + assert(0); + return 0; +} + +uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { + const uint32_t component_type_id = GetComponentType(id); + const Instruction* inst = FindDef(component_type_id); + assert(inst); + + if (inst->opcode() == spv::Op::OpTypeFloat || + inst->opcode() == spv::Op::OpTypeInt) + return inst->word(2); + + if (inst->opcode() == spv::Op::OpTypeBool) return 1; + + assert(0); + return 0; +} + +bool ValidationState_t::IsVoidType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeVoid; +} + +bool ValidationState_t::IsBfloat16ScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (inst && inst->opcode() == spv::Op::OpTypeFloat) { + if (inst->words().size() > 3) { + if (inst->GetOperandAs(2) == + spv::FPEncoding::BFloat16KHR) { + return true; + } + } + } + return false; +} + +bool ValidationState_t::IsBfloat16VectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsBfloat16ScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloatScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeFloat; +} + +bool ValidationState_t::IsFloatArrayType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeArray) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloatVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloat16Vector2Or4Type(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == spv::Op::OpTypeVector) { + uint32_t vectorDim = GetDimension(id); + return IsFloatScalarType(GetComponentType(id)) && + (vectorDim == 2 || vectorDim == 4) && + (GetBitWidth(GetComponentType(id)) == 16); + } + + return false; +} + +bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeFloat) { + return true; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeInt; +} + +bool ValidationState_t::IsIntArrayType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeArray) { + return IsIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeInt) { + return true; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 0; +} + +bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsUnsignedIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsUnsignedIntScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeInt) { + return inst->GetOperandAs(2) == 0; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsUnsignedIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 1; +} + +bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsSignedIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsBoolScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeBool; +} + +bool ValidationState_t::IsBoolVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsBoolScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeBool) { + return true; + } + + if (inst->opcode() == spv::Op::OpTypeVector) { + return IsBoolScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { + const Instruction* inst = FindDef(id); + if (!inst) { + return false; + } + + if (inst->opcode() == spv::Op::OpTypeMatrix) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, + uint32_t* num_cols, + uint32_t* column_type, + uint32_t* component_type) const { + if (!id) return false; + + const Instruction* mat_inst = FindDef(id); + assert(mat_inst); + if (mat_inst->opcode() != spv::Op::OpTypeMatrix) return false; + + const uint32_t vec_type = mat_inst->word(2); + const Instruction* vec_inst = FindDef(vec_type); + assert(vec_inst); + + if (vec_inst->opcode() != spv::Op::OpTypeVector) { + assert(0); + return false; + } + + *num_cols = mat_inst->word(3); + *num_rows = vec_inst->word(3); + *column_type = mat_inst->word(2); + *component_type = vec_inst->word(2); + + return true; +} + +bool ValidationState_t::GetStructMemberTypes( + uint32_t struct_type_id, std::vector* member_types) const { + member_types->clear(); + if (!struct_type_id) return false; + + const Instruction* inst = FindDef(struct_type_id); + assert(inst); + if (inst->opcode() != spv::Op::OpTypeStruct) return false; + + *member_types = + std::vector(inst->words().cbegin() + 2, inst->words().cend()); + + if (member_types->empty()) return false; + + return true; +} + +bool ValidationState_t::IsPointerType(uint32_t id) const { + if (!id) return false; + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == spv::Op::OpTypePointer || + inst->opcode() == spv::Op::OpTypeUntypedPointerKHR; +} + +bool ValidationState_t::GetPointerTypeInfo( + uint32_t id, uint32_t* data_type, spv::StorageClass* storage_class) const { + *storage_class = spv::StorageClass::Max; + if (!id) return false; + + const Instruction* inst = FindDef(id); + assert(inst); + if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) { + *storage_class = spv::StorageClass(inst->word(2)); + *data_type = 0; + return true; + } + + if (inst->opcode() != spv::Op::OpTypePointer) return false; + + *storage_class = spv::StorageClass(inst->word(2)); + *data_type = inst->word(3); + return true; +} + +bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeAccelerationStructureKHR; +} + +bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && (inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV || + inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR); +} + +bool ValidationState_t::IsCooperativeMatrixNVType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV; +} + +bool ValidationState_t::IsCooperativeMatrixKHRType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR; +} + +bool ValidationState_t::IsCooperativeMatrixAType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == + static_cast(spv::CooperativeMatrixUse::MatrixAKHR); + } + return false; +} + +bool ValidationState_t::IsCooperativeMatrixBType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == + static_cast(spv::CooperativeMatrixUse::MatrixBKHR); + } + return false; +} +bool ValidationState_t::IsCooperativeMatrixAccType(uint32_t id) const { + if (!IsCooperativeMatrixKHRType(id)) return false; + const Instruction* inst = FindDef(id); + uint64_t matrixUse = 0; + if (EvalConstantValUint64(inst->word(6), &matrixUse)) { + return matrixUse == static_cast( + spv::CooperativeMatrixUse::MatrixAccumulatorKHR); + } + return false; +} + +bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; + return IsFloatScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsIntCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; + return IsIntScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) + return false; + return IsUnsignedIntScalarType(FindDef(id)->word(2)); +} + +// Either a 32 bit 2-component uint vector or a 64 bit uint scalar +bool ValidationState_t::IsUnsigned64BitHandle(uint32_t id) const { + return ((IsUnsignedIntScalarType(id) && GetBitWidth(id) == 64) || + (IsUnsignedIntVectorType(id) && GetDimension(id) == 2 && + GetBitWidth(id) == 32)); +} + +bool ValidationState_t::IsCooperativeVectorNVType(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst && inst->opcode() == spv::Op::OpTypeCooperativeVectorNV; +} + +bool ValidationState_t::IsFloatCooperativeVectorNVType(uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsFloatScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsIntCooperativeVectorNVType(uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsIntScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsUnsignedIntCooperativeVectorNVType( + uint32_t id) const { + if (!IsCooperativeVectorNVType(id)) return false; + return IsUnsignedIntScalarType(FindDef(id)->word(2)); +} + +spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( + const Instruction* inst, uint32_t result_type_id, uint32_t m2, + bool is_conversion, bool swap_row_col) { + const auto m1_type = FindDef(result_type_id); + const auto m2_type = FindDef(m2); + + if (m1_type->opcode() != m2_type->opcode()) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix types"; + } + + uint32_t m1_scope_id = m1_type->GetOperandAs(2); + uint32_t m1_rows_id = m1_type->GetOperandAs(3); + uint32_t m1_cols_id = m1_type->GetOperandAs(4); + + uint32_t m2_scope_id = m2_type->GetOperandAs(2); + uint32_t m2_rows_id = m2_type->GetOperandAs(3); + uint32_t m2_cols_id = m2_type->GetOperandAs(4); + + if (swap_row_col) { + std::swap(m1_rows_id, m1_cols_id); + } + + bool m1_is_int32 = false, m1_is_const_int32 = false, m2_is_int32 = false, + m2_is_const_int32 = false; + uint32_t m1_value = 0, m2_value = 0; + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_scope_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_scope_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scopes of Matrix and Result Type to be " + << "identical"; + } + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_rows_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_rows_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected rows of Matrix type and Result Type to be " + << (swap_row_col ? "swapped with columns" : "identical"); + } + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_cols_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_cols_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected columns of Matrix type and Result Type to be " + << (swap_row_col ? "swapped with rows" : "identical"); + } + + if (m1_type->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { + uint32_t m1_use_id = m1_type->GetOperandAs(5); + uint32_t m2_use_id = m2_type->GetOperandAs(5); + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_use_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_use_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value && + // CooperativeMatrixConversionsNV allows conversions from Acc->A/B + !(is_conversion && + HasCapability(spv::Capability::CooperativeMatrixConversionsNV) && + m2_value == + (uint32_t)spv::CooperativeMatrixUse::MatrixAccumulatorKHR)) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Use of Matrix type and Result Type to be " + << "identical"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::CooperativeVectorDimensionsMatch( + const Instruction* inst, uint32_t v1, uint32_t v2) { + const auto v1_type = FindDef(v1); + const auto v2_type = FindDef(v2); + + if (v1_type->opcode() != v2_type->opcode()) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative vector types"; + } + + uint32_t v1_components_id = v1_type->GetOperandAs(2); + uint32_t v2_components_id = v2_type->GetOperandAs(2); + + bool v1_is_int32 = false, v1_is_const_int32 = false, v2_is_int32 = false, + v2_is_const_int32 = false; + uint32_t v1_value = 0, v2_value = 0; + + std::tie(v1_is_int32, v1_is_const_int32, v1_value) = + EvalInt32IfConst(v1_components_id); + std::tie(v2_is_int32, v2_is_const_int32, v2_value) = + EvalInt32IfConst(v2_components_id); + + if (v1_is_const_int32 && v2_is_const_int32 && v1_value != v2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of components to be identical"; + } + + return SPV_SUCCESS; +} + +uint32_t ValidationState_t::GetOperandTypeId(const Instruction* inst, + size_t operand_index) const { + return GetTypeId(inst->GetOperandAs(operand_index)); +} + +bool ValidationState_t::EvalConstantValUint64(uint32_t id, + uint64_t* val) const { + const Instruction* inst = FindDef(id); + if (!inst) { + assert(0 && "Instruction not found"); + return false; + } + + if (!IsIntScalarType(inst->type_id())) return false; + + if (inst->opcode() == spv::Op::OpConstantNull) { + *val = 0; + } else if (inst->opcode() != spv::Op::OpConstant) { + // Spec constant values cannot be evaluated so don't consider constant for + // static validation + return false; + } else if (inst->words().size() == 4) { + *val = inst->word(3); + } else { + assert(inst->words().size() == 5); + *val = inst->word(3); + *val |= uint64_t(inst->word(4)) << 32; + } + return true; +} + +bool ValidationState_t::EvalConstantValInt64(uint32_t id, int64_t* val) const { + const Instruction* inst = FindDef(id); + if (!inst) { + assert(0 && "Instruction not found"); + return false; + } + + if (!IsIntScalarType(inst->type_id())) return false; + + if (inst->opcode() == spv::Op::OpConstantNull) { + *val = 0; + } else if (inst->opcode() != spv::Op::OpConstant) { + // Spec constant values cannot be evaluated so don't consider constant for + // static validation + return false; + } else if (inst->words().size() == 4) { + *val = int32_t(inst->word(3)); + } else { + assert(inst->words().size() == 5); + const uint32_t lo_word = inst->word(3); + const uint32_t hi_word = inst->word(4); + *val = static_cast(uint64_t(lo_word) | uint64_t(hi_word) << 32); + } + return true; +} + +std::tuple ValidationState_t::EvalInt32IfConst( + uint32_t id) const { + const Instruction* const inst = FindDef(id); + assert(inst); + const uint32_t type = inst->type_id(); + + if (type == 0 || !IsIntScalarType(type) || GetBitWidth(type) != 32) { + return std::make_tuple(false, false, 0); + } + + // Spec constant values cannot be evaluated so don't consider constant for + // the purpose of this method. + if (!spvOpcodeIsConstant(inst->opcode()) || + spvOpcodeIsSpecConstant(inst->opcode())) { + return std::make_tuple(true, false, 0); + } + + if (inst->opcode() == spv::Op::OpConstantNull) { + return std::make_tuple(true, true, 0); + } + + assert(inst->words().size() == 4); + return std::make_tuple(true, true, inst->word(3)); +} + +void ValidationState_t::ComputeFunctionToEntryPointMapping() { + for (const uint32_t entry_point : entry_points()) { + std::stack call_stack; + std::set visited; + call_stack.push(entry_point); + while (!call_stack.empty()) { + const uint32_t called_func_id = call_stack.top(); + call_stack.pop(); + if (!visited.insert(called_func_id).second) continue; + + function_to_entry_points_[called_func_id].push_back(entry_point); + + const Function* called_func = function(called_func_id); + if (called_func) { + // Other checks should error out on this invalid SPIR-V. + for (const uint32_t new_call : called_func->function_call_targets()) { + call_stack.push(new_call); + } + } + } + } +} + +void ValidationState_t::ComputeRecursiveEntryPoints() { + for (const Function& func : functions()) { + std::stack call_stack; + std::set visited; + + for (const uint32_t new_call : func.function_call_targets()) { + call_stack.push(new_call); + } + + while (!call_stack.empty()) { + const uint32_t called_func_id = call_stack.top(); + call_stack.pop(); + + if (!visited.insert(called_func_id).second) continue; + + if (called_func_id == func.id()) { + for (const uint32_t entry_point : + function_to_entry_points_[called_func_id]) + recursive_entry_points_.insert(entry_point); + break; + } + + const Function* called_func = function(called_func_id); + if (called_func) { + // Other checks should error out on this invalid SPIR-V. + for (const uint32_t new_call : called_func->function_call_targets()) { + call_stack.push(new_call); + } + } + } + } +} + +const std::vector& ValidationState_t::FunctionEntryPoints( + uint32_t func) const { + auto iter = function_to_entry_points_.find(func); + if (iter == function_to_entry_points_.end()) { + return empty_ids_; + } else { + return iter->second; + } +} + +std::set ValidationState_t::EntryPointReferences(uint32_t id) const { + std::set referenced_entry_points; + const auto inst = FindDef(id); + if (!inst) return referenced_entry_points; + + std::vector stack; + stack.push_back(inst); + while (!stack.empty()) { + const auto current_inst = stack.back(); + stack.pop_back(); + + if (const auto func = current_inst->function()) { + // Instruction lives in a function, we can stop searching. + const auto function_entry_points = FunctionEntryPoints(func->id()); + referenced_entry_points.insert(function_entry_points.begin(), + function_entry_points.end()); + } else { + // Instruction is in the global scope, keep searching its uses. + for (auto pair : current_inst->uses()) { + const auto next_inst = pair.first; + stack.push_back(next_inst); + } + } + } + + return referenced_entry_points; +} + +std::string ValidationState_t::Disassemble(const Instruction& inst) const { + const spv_parsed_instruction_t& c_inst(inst.c_inst()); + return Disassemble(c_inst.words, c_inst.num_words); +} + +std::string ValidationState_t::Disassemble(const uint32_t* words, + uint16_t num_words) const { + uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; + + return spvInstructionBinaryToText(context()->target_env, words, num_words, + words_, num_words_, disassembly_options); +} + +bool ValidationState_t::LogicallyMatch(const Instruction* lhs, + const Instruction* rhs, + bool check_decorations) { + if (lhs->opcode() != rhs->opcode()) { + return false; + } + + if (check_decorations) { + const auto& dec_a = id_decorations(lhs->id()); + const auto& dec_b = id_decorations(rhs->id()); + + for (const auto& dec : dec_b) { + if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) { + return false; + } + } + } + + if (lhs->opcode() == spv::Op::OpTypeArray) { + // Size operands must match. + if (lhs->GetOperandAs(2u) != rhs->GetOperandAs(2u)) { + return false; + } + + // Elements must match or logically match. + const auto lhs_ele_id = lhs->GetOperandAs(1u); + const auto rhs_ele_id = rhs->GetOperandAs(1u); + if (lhs_ele_id == rhs_ele_id) { + return true; + } + + const auto lhs_ele = FindDef(lhs_ele_id); + const auto rhs_ele = FindDef(rhs_ele_id); + if (!lhs_ele || !rhs_ele) { + return false; + } + return LogicallyMatch(lhs_ele, rhs_ele, check_decorations); + } else if (lhs->opcode() == spv::Op::OpTypeStruct) { + // Number of elements must match. + if (lhs->operands().size() != rhs->operands().size()) { + return false; + } + + for (size_t i = 1u; i < lhs->operands().size(); ++i) { + const auto lhs_ele_id = lhs->GetOperandAs(i); + const auto rhs_ele_id = rhs->GetOperandAs(i); + // Elements must match or logically match. + if (lhs_ele_id == rhs_ele_id) { + continue; + } + + const auto lhs_ele = FindDef(lhs_ele_id); + const auto rhs_ele = FindDef(rhs_ele_id); + if (!lhs_ele || !rhs_ele) { + return false; + } + + if (!LogicallyMatch(lhs_ele, rhs_ele, check_decorations)) { + return false; + } + } + + // All checks passed. + return true; + } + + // No other opcodes are acceptable at this point. Arrays and structs are + // caught above and if they're elements are not arrays or structs they are + // required to match exactly. + return false; +} + +const Instruction* ValidationState_t::TracePointer( + const Instruction* inst) const { + auto base_ptr = inst; + while (base_ptr->opcode() == spv::Op::OpAccessChain || + base_ptr->opcode() == spv::Op::OpInBoundsAccessChain || + base_ptr->opcode() == spv::Op::OpPtrAccessChain || + base_ptr->opcode() == spv::Op::OpInBoundsPtrAccessChain || + base_ptr->opcode() == spv::Op::OpCopyObject) { + base_ptr = FindDef(base_ptr->GetOperandAs(2u)); + } + return base_ptr; +} + +bool ValidationState_t::ContainsType( + uint32_t id, const std::function& f, + bool traverse_all_types) const { + const auto inst = FindDef(id); + if (!inst) return false; + + if (f(inst)) return true; + + switch (inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeCooperativeMatrixNV: + case spv::Op::OpTypeCooperativeMatrixKHR: + case spv::Op::OpTypeCooperativeVectorNV: + return ContainsType(inst->GetOperandAs(1u), f, + traverse_all_types); + case spv::Op::OpTypePointer: + if (IsForwardPointer(id)) return false; + if (traverse_all_types) { + return ContainsType(inst->GetOperandAs(2u), f, + traverse_all_types); + } + break; + case spv::Op::OpTypeFunction: + case spv::Op::OpTypeStruct: + if (inst->opcode() == spv::Op::OpTypeFunction && !traverse_all_types) { + return false; + } + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (ContainsType(inst->GetOperandAs(i), f, + traverse_all_types)) { + return true; + } + } + break; + default: + break; + } + + return false; +} + +bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, spv::Op type, + uint32_t width) const { + if (type != spv::Op::OpTypeInt && type != spv::Op::OpTypeFloat) return false; + + const auto f = [type, width](const Instruction* inst) { + if (inst->opcode() == type) { + // Bfloat16 is a special type. + if (type == spv::Op::OpTypeFloat && inst->words().size() > 3) + return false; + + return inst->GetOperandAs(1u) == width; + } + return false; + }; + return ContainsType(id, f); +} + +bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { + if ((!HasCapability(spv::Capability::Int16) && + ContainsSizedIntOrFloatType(id, spv::Op::OpTypeInt, 16)) || + (!HasCapability(spv::Capability::Int8) && + ContainsSizedIntOrFloatType(id, spv::Op::OpTypeInt, 8)) || + (!HasCapability(spv::Capability::Float16) && + ContainsSizedIntOrFloatType(id, spv::Op::OpTypeFloat, 16))) { + return true; + } + return false; +} + +bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const { + const auto f = [](const Instruction* inst) { + return inst->opcode() == spv::Op::OpTypeRuntimeArray; + }; + return ContainsType(id, f, /* traverse_all_types = */ false); +} + +bool ValidationState_t::ContainsUntypedPointer(uint32_t id) const { + const auto inst = FindDef(id); + if (!inst) return false; + if (!spvOpcodeGeneratesType(inst->opcode())) return false; + if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) return true; + + switch (inst->opcode()) { + case spv::Op::OpTypeArray: + case spv::Op::OpTypeRuntimeArray: + case spv::Op::OpTypeVector: + case spv::Op::OpTypeMatrix: + case spv::Op::OpTypeImage: + case spv::Op::OpTypeSampledImage: + case spv::Op::OpTypeCooperativeMatrixNV: + return ContainsUntypedPointer(inst->GetOperandAs(1u)); + case spv::Op::OpTypePointer: + if (IsForwardPointer(id)) return false; + return ContainsUntypedPointer(inst->GetOperandAs(2u)); + case spv::Op::OpTypeFunction: + case spv::Op::OpTypeStruct: { + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (ContainsUntypedPointer(inst->GetOperandAs(i))) + return true; + } + return false; + } + default: + return false; + } + + return false; +} + +bool ValidationState_t::IsValidStorageClass( + spv::StorageClass storage_class) const { + if (spvIsVulkanEnv(context()->target_env)) { + switch (storage_class) { + case spv::StorageClass::UniformConstant: + case spv::StorageClass::Uniform: + case spv::StorageClass::StorageBuffer: + case spv::StorageClass::Input: + case spv::StorageClass::Output: + case spv::StorageClass::Image: + case spv::StorageClass::Workgroup: + case spv::StorageClass::Private: + case spv::StorageClass::Function: + case spv::StorageClass::PushConstant: + case spv::StorageClass::PhysicalStorageBuffer: + case spv::StorageClass::RayPayloadKHR: + case spv::StorageClass::IncomingRayPayloadKHR: + case spv::StorageClass::HitAttributeKHR: + case spv::StorageClass::CallableDataKHR: + case spv::StorageClass::IncomingCallableDataKHR: + case spv::StorageClass::ShaderRecordBufferKHR: + case spv::StorageClass::TaskPayloadWorkgroupEXT: + case spv::StorageClass::HitObjectAttributeNV: + case spv::StorageClass::TileImageEXT: + case spv::StorageClass::NodePayloadAMDX: + return true; + default: + return false; + } + } + + return true; +} + +#define VUID_WRAP(vuid) "[" #vuid "] " + +// Currently no 2 VUID share the same id, so no need for |reference| +std::string ValidationState_t::VkErrorID(uint32_t id, + const char* /*reference*/) const { + if (!spvIsVulkanEnv(context_->target_env)) { + return ""; + } + + // This large switch case is only searched when an error has occurred. + // If an id is changed, the old case must be modified or removed. Each string + // here is interpreted as being "implemented" + + // Clang format adds spaces between hyphens + // clang-format off + switch (id) { + case 4154: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154); + case 4155: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155); + case 4156: + return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156); + case 4160: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160); + case 4161: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161); + case 4162: + return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162); + case 4181: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181); + case 4182: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04182); + case 4183: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04183); + case 4184: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04184); + case 4185: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04185); + case 4186: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04186); + case 4187: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04187); + case 4188: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04188); + case 4189: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04189); + case 4190: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04190); + case 4191: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04191); + case 4196: + return VUID_WRAP(VUID-CullDistance-CullDistance-04196); + case 4197: + return VUID_WRAP(VUID-CullDistance-CullDistance-04197); + case 4198: + return VUID_WRAP(VUID-CullDistance-CullDistance-04198); + case 4199: + return VUID_WRAP(VUID-CullDistance-CullDistance-04199); + case 4200: + return VUID_WRAP(VUID-CullDistance-CullDistance-04200); + case 6735: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06735); // Execution Model + case 6736: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06736); // input storage + case 6737: + return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06737); // 32 int scalar + case 4205: + return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04205); + case 4206: + return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04206); + case 4207: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04207); + case 4208: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04208); + case 4209: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04209); + case 4210: + return VUID_WRAP(VUID-FragCoord-FragCoord-04210); + case 4211: + return VUID_WRAP(VUID-FragCoord-FragCoord-04211); + case 4212: + return VUID_WRAP(VUID-FragCoord-FragCoord-04212); + case 4213: + return VUID_WRAP(VUID-FragDepth-FragDepth-04213); + case 4214: + return VUID_WRAP(VUID-FragDepth-FragDepth-04214); + case 4215: + return VUID_WRAP(VUID-FragDepth-FragDepth-04215); + case 4216: + return VUID_WRAP(VUID-FragDepth-FragDepth-04216); + case 4217: + return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04217); + case 4218: + return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04218); + case 4219: + return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04219); + case 4220: + return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04220); + case 4221: + return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04221); + case 4222: + return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04222); + case 4223: + return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04223); + case 4224: + return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04224); + case 4225: + return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04225); + case 4229: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04229); + case 4230: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04230); + case 4231: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04231); + case 4232: + return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04232); + case 4233: + return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04233); + case 4234: + return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04234); + case 4236: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04236); + case 4237: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04237); + case 4238: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04238); + case 4239: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04239); + case 4240: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04240); + case 4241: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04241); + case 4242: + return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04242); + case 4243: + return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04243); + case 4244: + return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04244); + case 4245: + return VUID_WRAP(VUID-HitTNV-HitTNV-04245); + case 4246: + return VUID_WRAP(VUID-HitTNV-HitTNV-04246); + case 4247: + return VUID_WRAP(VUID-HitTNV-HitTNV-04247); + case 4248: + return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04248); + case 4249: + return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04249); + case 4250: + return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04250); + case 4251: + return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04251); + case 4252: + return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04252); + case 4253: + return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04253); + case 4254: + return VUID_WRAP(VUID-InstanceId-InstanceId-04254); + case 4255: + return VUID_WRAP(VUID-InstanceId-InstanceId-04255); + case 4256: + return VUID_WRAP(VUID-InstanceId-InstanceId-04256); + case 4257: + return VUID_WRAP(VUID-InvocationId-InvocationId-04257); + case 4258: + return VUID_WRAP(VUID-InvocationId-InvocationId-04258); + case 4259: + return VUID_WRAP(VUID-InvocationId-InvocationId-04259); + case 4263: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04263); + case 4264: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04264); + case 4265: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04265); + case 4266: + return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04266); + case 4267: + return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04267); + case 4268: + return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04268); + case 4269: + return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04269); + case 4270: + return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04270); + case 4271: + return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04271); + case 4272: + return VUID_WRAP(VUID-Layer-Layer-04272); + case 4273: + return VUID_WRAP(VUID-Layer-Layer-04273); + case 4274: + return VUID_WRAP(VUID-Layer-Layer-04274); + case 4275: + return VUID_WRAP(VUID-Layer-Layer-04275); + case 4276: + return VUID_WRAP(VUID-Layer-Layer-04276); + case 4281: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04281); + case 4282: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282); + case 4283: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283); + case 4293: + return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293); + case 4294: + return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04294); + case 4295: + return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04295); + case 4296: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04296); + case 4297: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04297); + case 4298: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04298); + case 4299: + return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04299); + case 4300: + return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04300); + case 4301: + return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04301); + case 4302: + return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04302); + case 4303: + return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04303); + case 4304: + return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04304); + case 4305: + return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04305); + case 4306: + return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04306); + case 4307: + return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04307); + case 4308: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04308); + case 4309: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04309); + case 4310: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04310); + case 4311: + return VUID_WRAP(VUID-PointCoord-PointCoord-04311); + case 4312: + return VUID_WRAP(VUID-PointCoord-PointCoord-04312); + case 4313: + return VUID_WRAP(VUID-PointCoord-PointCoord-04313); + case 4314: + return VUID_WRAP(VUID-PointSize-PointSize-04314); + case 4315: + return VUID_WRAP(VUID-PointSize-PointSize-04315); + case 4316: + return VUID_WRAP(VUID-PointSize-PointSize-04316); + case 4317: + return VUID_WRAP(VUID-PointSize-PointSize-04317); + case 4318: + return VUID_WRAP(VUID-Position-Position-04318); + case 4319: + return VUID_WRAP(VUID-Position-Position-04319); + case 4320: + return VUID_WRAP(VUID-Position-Position-04320); + case 4321: + return VUID_WRAP(VUID-Position-Position-04321); + case 4330: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330); + case 4334: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334); + case 4336: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04336); + case 4337: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337); + case 4345: + return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04345); + case 4346: + return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04346); + case 4347: + return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04347); + case 4348: + return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04348); + case 4349: + return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04349); + case 4350: + return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04350); + case 4351: + return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04351); + case 4352: + return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04352); + case 4353: + return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04353); + case 4354: + return VUID_WRAP(VUID-SampleId-SampleId-04354); + case 4355: + return VUID_WRAP(VUID-SampleId-SampleId-04355); + case 4356: + return VUID_WRAP(VUID-SampleId-SampleId-04356); + case 4357: + return VUID_WRAP(VUID-SampleMask-SampleMask-04357); + case 4358: + return VUID_WRAP(VUID-SampleMask-SampleMask-04358); + case 4359: + return VUID_WRAP(VUID-SampleMask-SampleMask-04359); + case 4360: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04360); + case 4361: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04361); + case 4362: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04362); + case 4367: + return VUID_WRAP(VUID-SubgroupId-SubgroupId-04367); + case 4368: + return VUID_WRAP(VUID-SubgroupId-SubgroupId-04368); + case 4369: + return VUID_WRAP(VUID-SubgroupId-SubgroupId-04369); + case 4370: + return VUID_WRAP(VUID-SubgroupEqMask-SubgroupEqMask-04370); + case 4371: + return VUID_WRAP(VUID-SubgroupEqMask-SubgroupEqMask-04371); + case 4372: + return VUID_WRAP(VUID-SubgroupGeMask-SubgroupGeMask-04372); + case 4373: + return VUID_WRAP(VUID-SubgroupGeMask-SubgroupGeMask-04373); + case 4374: + return VUID_WRAP(VUID-SubgroupGtMask-SubgroupGtMask-04374); + case 4375: + return VUID_WRAP(VUID-SubgroupGtMask-SubgroupGtMask-04375); + case 4376: + return VUID_WRAP(VUID-SubgroupLeMask-SubgroupLeMask-04376); + case 4377: + return VUID_WRAP(VUID-SubgroupLeMask-SubgroupLeMask-04377); + case 4378: + return VUID_WRAP(VUID-SubgroupLtMask-SubgroupLtMask-04378); + case 4379: + return VUID_WRAP(VUID-SubgroupLtMask-SubgroupLtMask-04379); + case 4380: + return VUID_WRAP(VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04380); + case 4381: + return VUID_WRAP(VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04381); + case 4382: + return VUID_WRAP(VUID-SubgroupSize-SubgroupSize-04382); + case 4383: + return VUID_WRAP(VUID-SubgroupSize-SubgroupSize-04383); + case 4387: + return VUID_WRAP(VUID-TessCoord-TessCoord-04387); + case 4388: + return VUID_WRAP(VUID-TessCoord-TessCoord-04388); + case 4389: + return VUID_WRAP(VUID-TessCoord-TessCoord-04389); + case 4390: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04390); + case 4391: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04391); + case 4392: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04392); + case 4393: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04393); + case 4394: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04394); + case 4395: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04395); + case 4396: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04396); + case 4397: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04397); + case 4398: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04398); + case 4399: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04399); + case 4400: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04400); + case 4401: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04401); + case 4402: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04402); + case 4403: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04403); + case 4404: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04404); + case 4405: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04405); + case 4406: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04406); + case 4407: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04407); + case 4408: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04408); + case 4422: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04422); + case 4423: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04423); + case 4424: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04424); + case 4425: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04425); + case 4426: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04426); + case 4427: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04427); + case 4428: + return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04428); + case 4429: + return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04429); + case 4430: + return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04430); + case 4431: + return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04431); + case 4432: + return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04432); + case 4433: + return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04433); + case 4434: + return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04434); + case 4435: + return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04435); + case 4436: + return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04436); + case 4484: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484); + case 4485: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485); + case 4486: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486); + case 4490: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04490); + case 4491: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04491); + case 4492: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492); + case 4633: + return VUID_WRAP(VUID-StandaloneSpirv-None-04633); + case 4634: + return VUID_WRAP(VUID-StandaloneSpirv-None-04634); + case 4635: + return VUID_WRAP(VUID-StandaloneSpirv-None-04635); + case 4636: + return VUID_WRAP(VUID-StandaloneSpirv-None-04636); + case 4637: + return VUID_WRAP(VUID-StandaloneSpirv-None-04637); + case 4638: + return VUID_WRAP(VUID-StandaloneSpirv-None-04638); + case 7321: + return VUID_WRAP(VUID-StandaloneSpirv-None-07321); + case 4640: + return VUID_WRAP(VUID-StandaloneSpirv-None-04640); + case 4641: + return VUID_WRAP(VUID-StandaloneSpirv-None-04641); + case 4642: + return VUID_WRAP(VUID-StandaloneSpirv-None-04642); + case 4643: + return VUID_WRAP(VUID-StandaloneSpirv-None-04643); + case 4644: + return VUID_WRAP(VUID-StandaloneSpirv-None-04644); + case 4645: + return VUID_WRAP(VUID-StandaloneSpirv-None-04645); + case 10609: + return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-10609); + case 4650: + return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04650); + case 4651: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651); + case 4652: + return VUID_WRAP(VUID-StandaloneSpirv-OpReadClockKHR-04652); + case 4653: + return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653); + case 4654: + return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654); + case 4655: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655); + case 4656: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656); + case 4657: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04657); + case 4658: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658); + case 4659: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659); + case 4664: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); + case 4667: + return VUID_WRAP(VUID-StandaloneSpirv-None-04667); + case 4669: + return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); + case 4670: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670); + case 4675: + return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); + case 4677: + return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); + case 4680: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); + case 4682: + return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); + case 6426: + return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683 + case 4685: + return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); + case 4686: + return VUID_WRAP(VUID-StandaloneSpirv-None-04686); + case 4698: + return VUID_WRAP(VUID-StandaloneSpirv-RayPayloadKHR-04698); + case 4699: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699); + case 4700: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04700); + case 4701: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04701); + case 4702: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04702); + case 4703: + return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04703); + case 4704: + return VUID_WRAP(VUID-StandaloneSpirv-CallableDataKHR-04704); + case 4705: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04705); + case 4706: + return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04706); + case 7119: + return VUID_WRAP(VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119); + case 4708: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708); + case 4710: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); + case 4711: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711); + case 4730: + return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicStore-04730); + case 4731: + return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicLoad-04731); + case 4732: + return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732); + case 4733: + return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); + case 4734: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); + case 4744: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744); + case 4777: + return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); + case 4780: + return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); + case 4781: + return VUID_WRAP(VUID-StandaloneSpirv-Base-04781); + case 4915: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); + case 4916: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04916); + case 4917: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04917); + case 4918: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); + case 4919: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); + case 4920: + return VUID_WRAP(VUID-StandaloneSpirv-Component-04920); + case 4921: + return VUID_WRAP(VUID-StandaloneSpirv-Component-04921); + case 4922: + return VUID_WRAP(VUID-StandaloneSpirv-Component-04922); + case 4923: + return VUID_WRAP(VUID-StandaloneSpirv-Component-04923); + case 6201: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201); + case 6202: + return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202); + case 6214: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); + case 6491: + return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491); + case 6671: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671); + case 6672: + return VUID_WRAP(VUID-StandaloneSpirv-Location-06672); + case 6673: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-06673); + case 6674: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674); + case 6675: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675); + case 6676: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676); + case 6677: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677); + case 6678: + return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678); + case 6777: + return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777); + case 6778: + return VUID_WRAP(VUID-StandaloneSpirv-Input-06778); + case 6807: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807); + case 6808: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); + case 6924: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06924); + case 6925: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); + case 7034: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034); + case 7035: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035); + case 7036: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036); + case 7038: + return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038); + case 7039: + return VUID_WRAP(VUID-Layer-Layer-07039); + case 7040: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-07040); + case 7041: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041); + case 7042: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042); + case 7043: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043); + case 7044: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044); + case 7046: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046); + case 7047: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047); + case 7048: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048); + case 7049: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049); + case 7050: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050); + case 7052: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052); + case 7053: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053); + case 7054: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054); + case 7055: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055); + case 7056: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056); + case 7058: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07058); + case 7059: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059); + case 7060: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-07060); + case 7102: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); + case 7290: + return VUID_WRAP(VUID-StandaloneSpirv-Input-07290); + case 7320: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); + case 7330: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07330); + case 7331: + return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07331); + case 7650: + return VUID_WRAP(VUID-StandaloneSpirv-Base-07650); + case 7651: + return VUID_WRAP(VUID-StandaloneSpirv-Base-07651); + case 7652: + return VUID_WRAP(VUID-StandaloneSpirv-Base-07652); + case 7703: + return VUID_WRAP(VUID-StandaloneSpirv-Component-07703); + case 7951: + return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951); + case 8721: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721); + case 8722: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722); + case 8973: + return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973); + case 9557: + return VUID_WRAP(VUID-StandaloneSpirv-Input-09557); + case 9638: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-09638); + case 9658: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09658); + case 9659: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09659); + case 10213: + // This use to be a standalone, but maintenance8 will set allow_offset_texture_operand now + return VUID_WRAP(VUID-RuntimeSpirv-Offset-10213); + case 10370: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10370); + case 10583: + return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); + case 10684: + return VUID_WRAP(VUID-StandaloneSpirv-None-10684); + default: + return ""; // unknown id + } + // clang-format on +} + +} // namespace val +} // namespace spvtools diff --git a/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.h b/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.h new file mode 100644 index 00000000..8cc87a4a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/val/validation_state.h @@ -0,0 +1,1040 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_VALIDATION_STATE_H_ +#define SOURCE_VAL_VALIDATION_STATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/disassemble.h" +#include "source/enum_set.h" +#include "source/latest_version_spirv_header.h" +#include "source/name_mapper.h" +#include "source/spirv_definition.h" +#include "source/spirv_validator_options.h" +#include "source/val/decoration.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +/// This enum represents the sections of a SPIRV module. See section 2.4 +/// of the SPIRV spec for additional details of the order. The enumerant values +/// are in the same order as the vector returned by GetModuleOrder +enum ModuleLayoutSection { + kLayoutCapabilities, /// < Section 2.4 #1 + kLayoutExtensions, /// < Section 2.4 #2 + kLayoutExtInstImport, /// < Section 2.4 #3 + kLayoutMemoryModel, /// < Section 2.4 #4 + kLayoutSamplerImageAddressMode, /// < Section 2.4 #5 + kLayoutEntryPoint, /// < Section 2.4 #6 + kLayoutExecutionMode, /// < Section 2.4 #7 + kLayoutDebug1, /// < Section 2.4 #8 > 1 + kLayoutDebug2, /// < Section 2.4 #8 > 2 + kLayoutDebug3, /// < Section 2.4 #8 > 3 + kLayoutAnnotations, /// < Section 2.4 #9 + kLayoutTypes, /// < Section 2.4 #10 + kLayoutFunctionDeclarations, /// < Section 2.4 #11 + kLayoutFunctionDefinitions /// < Section 2.4 #12 +}; + +/// This class manages the state of the SPIR-V validation as it is being parsed. +class ValidationState_t { + public: + // Features that can optionally be turned on by a capability or environment. + struct Feature { + bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? + bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? + bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration + // and its values to be used without + // requiring any capability + + // Allow functionalities enabled by VariablePointers or + // VariablePointersStorageBuffer capability. + bool variable_pointers = false; + + // Permit group oerations Reduce, InclusiveScan, ExclusiveScan + bool group_ops_reduce_and_scans = false; + + // Allow OpTypeInt with 8 bit width? + bool declare_int8_type = false; + + // Target environment uses relaxed block layout. + // This is true for Vulkan 1.1 or later. + bool env_relaxed_block_layout = false; + + // Allow an OpTypeInt with 8 bit width to be used in more than just int + // conversion opcodes + bool use_int8_type = false; + + // SPIR-V 1.4 allows us to select between any two composite values + // of the same type. + bool select_between_composites = false; + + // SPIR-V 1.4 allows two memory access operands for OpCopyMemory and + // OpCopyMemorySized. + bool copy_memory_permits_two_memory_accesses = false; + + // SPIR-V 1.4 allows UConvert as a spec constant op in any environment. + // The Kernel capability already enables it, separately from this flag. + bool uconvert_spec_constant_op = false; + + // SPIR-V 1.4 allows Function and Private variables to be NonWritable + bool nonwritable_var_in_function_or_private = false; + + // Whether LocalSizeId execution mode is allowed by the environment. + bool env_allow_localsizeid = false; + }; + + ValidationState_t(const spv_const_context context, + const spv_const_validator_options opt, + const uint32_t* words, const size_t num_words, + const uint32_t max_warnings); + + /// Returns the context + spv_const_context context() const { return context_; } + + /// Returns the command line options + spv_const_validator_options options() const { return options_; } + + /// Sets the ID of the generator for this module. + void setGenerator(uint32_t gen) { generator_ = gen; } + + /// Returns the ID of the generator for this module. + uint32_t generator() const { return generator_; } + + /// Sets the SPIR-V version of this module. + void setVersion(uint32_t ver) { version_ = ver; } + + /// Gets the SPIR-V version of this module. + uint32_t version() const { return version_; } + + /// Forward declares the id in the module + spv_result_t ForwardDeclareId(uint32_t id); + + /// Removes a forward declared ID if it has been defined + spv_result_t RemoveIfForwardDeclared(uint32_t id); + + /// Registers an ID as a forward pointer + spv_result_t RegisterForwardPointer(uint32_t id); + + /// Returns whether or not an ID is a forward pointer + bool IsForwardPointer(uint32_t id) const; + + /// Assigns a name to an ID + void AssignNameToId(uint32_t id, std::string name); + + /// Returns a string representation of the ID in the format [Name] where + /// the is the numeric valid of the id and the Name is a name assigned by + /// the OpName instruction + std::string getIdName(uint32_t id) const; + + /// Accessor function for ID bound. + uint32_t getIdBound() const; + + /// Mutator function for ID bound. + void setIdBound(uint32_t bound); + + /// Returns the number of ID which have been forward referenced but not + /// defined + size_t unresolved_forward_id_count() const; + + /// Returns a vector of unresolved forward ids. + std::vector UnresolvedForwardIds() const; + + /// Returns true if the id has been defined + bool IsDefinedId(uint32_t id) const; + + /// Increments the total number of instructions in the file. + void increment_total_instructions() { total_instructions_++; } + + /// Increments the total number of functions in the file. + void increment_total_functions() { total_functions_++; } + + /// Allocates internal storage. Note, calling this will invalidate any + /// pointers to |ordered_instructions_| or |module_functions_| and, hence, + /// should only be called at the beginning of validation. + void preallocateStorage(); + + /// Returns the current layout section which is being processed + ModuleLayoutSection current_layout_section() const; + + /// Increments the module_layout_order_section_ + void ProgressToNextLayoutSectionOrder(); + + /// Determines if the op instruction is in a previous layout section + bool IsOpcodeInPreviousLayoutSection(spv::Op op); + + /// Determines if the op instruction is part of the current section + bool IsOpcodeInCurrentLayoutSection(spv::Op op); + + DiagnosticStream diag(spv_result_t error_code, const Instruction* inst); + + /// Returns the function states + std::vector& functions(); + + /// Returns the function states + Function& current_function(); + const Function& current_function() const; + + /// Returns function state with the given id, or nullptr if no such function. + const Function* function(uint32_t id) const; + Function* function(uint32_t id); + + /// Returns true if the called after a function instruction but before the + /// function end instruction + bool in_function_body() const; + + /// Returns true if called after a label instruction but before a branch + /// instruction + bool in_block() const; + + struct EntryPointDescription { + std::string name; + std::vector interfaces; + }; + + /// Registers |id| as an entry point with |execution_model| and |interfaces|. + void RegisterEntryPoint(const uint32_t id, + spv::ExecutionModel execution_model, + EntryPointDescription&& desc) { + entry_points_.push_back(id); + entry_point_to_execution_models_[id].insert(execution_model); + entry_point_descriptions_[id].emplace_back(desc); + } + + /// Returns a list of entry point function ids + const std::vector& entry_points() const { return entry_points_; } + + /// Returns the set of entry points that root call graphs that contain + /// recursion. + const std::set& recursive_entry_points() const { + return recursive_entry_points_; + } + + /// Registers execution mode for the given entry point. + void RegisterExecutionModeForEntryPoint(uint32_t entry_point, + spv::ExecutionMode execution_mode) { + entry_point_to_execution_modes_[entry_point].insert(execution_mode); + } + + /// Registers that the entry point declares its local size + void RegisterEntryPointLocalSize(uint32_t entry_point, + const Instruction* inst) { + entry_point_to_local_size_or_id_[entry_point] = inst; + } + + /// Registers that the entry point maximum number of primitives + /// mesh shader will ever emit + void RegisterEntryPointOutputPrimitivesEXT(uint32_t entry_point, + const Instruction* inst) { + entry_point_to_output_primitives_[entry_point] = inst; + } + + /// Returns the maximum number of primitives mesh shader can emit + uint32_t GetOutputPrimitivesEXT(uint32_t entry_point) { + auto entry = entry_point_to_output_primitives_.find(entry_point); + if (entry != entry_point_to_output_primitives_.end()) { + auto inst = entry->second; + return inst->GetOperandAs(2); + } + return 0; + } + + /// Returns whether the entry point declares its local size + bool EntryPointHasLocalSizeOrId(uint32_t entry_point) const { + return entry_point_to_local_size_or_id_.find(entry_point) != + entry_point_to_local_size_or_id_.end(); + } + /// Returns the id of the local size + const Instruction* EntryPointLocalSizeOrId(uint32_t entry_point) const { + return entry_point_to_local_size_or_id_.find(entry_point)->second; + } + + /// Returns the interface descriptions of a given entry point. + const std::vector& entry_point_descriptions( + uint32_t entry_point) { + return entry_point_descriptions_.at(entry_point); + } + + /// Returns Execution Models for the given Entry Point. + /// Returns nullptr if none found (would trigger assertion). + const std::set* GetExecutionModels( + uint32_t entry_point) const { + const auto it = entry_point_to_execution_models_.find(entry_point); + if (it == entry_point_to_execution_models_.end()) { + assert(0); + return nullptr; + } + return &it->second; + } + + /// Returns Execution Modes for the given Entry Point. + /// Returns nullptr if none found. + const std::set* GetExecutionModes( + uint32_t entry_point) const { + const auto it = entry_point_to_execution_modes_.find(entry_point); + if (it == entry_point_to_execution_modes_.end()) { + return nullptr; + } + return &it->second; + } + + /// Traverses call tree and computes function_to_entry_points_. + /// Note: called after fully parsing the binary. + void ComputeFunctionToEntryPointMapping(); + + /// Traverse call tree and computes recursive_entry_points_. + /// Note: called after fully parsing the binary and calling + /// ComputeFunctionToEntryPointMapping. + void ComputeRecursiveEntryPoints(); + + /// Returns all the entry points that can call |func|. + const std::vector& FunctionEntryPoints(uint32_t func) const; + + /// Returns all the entry points that statically use |id|. + /// + /// Note: requires ComputeFunctionToEntryPointMapping to have been called. + std::set EntryPointReferences(uint32_t id) const; + + /// Inserts an to the set of functions that are target of OpFunctionCall. + void AddFunctionCallTarget(const uint32_t id) { + function_call_targets_.insert(id); + current_function().AddFunctionCallTarget(id); + } + + /// Returns whether or not a function is the target of OpFunctionCall. + bool IsFunctionCallTarget(const uint32_t id) { + return (function_call_targets_.find(id) != function_call_targets_.end()); + } + + bool IsFunctionCallDefined(const uint32_t id) { + return (id_to_function_.find(id) != id_to_function_.end()); + } + /// Registers the capability and its dependent capabilities + void RegisterCapability(spv::Capability cap); + + /// Registers the extension. + void RegisterExtension(Extension ext); + + /// Registers the function in the module. Subsequent instructions will be + /// called against this function + spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id, + spv::FunctionControlMask function_control, + uint32_t function_type_id); + + /// Register a function end instruction + spv_result_t RegisterFunctionEnd(); + + /// Returns true if the capability is enabled in the module. + bool HasCapability(spv::Capability cap) const { + return module_capabilities_.contains(cap); + } + + /// Returns a reference to the set of capabilities in the module. + /// This is provided for debuggability. + const CapabilitySet& module_capabilities() const { + return module_capabilities_; + } + + /// Returns true if the extension is enabled in the module. + bool HasExtension(Extension ext) const { + return module_extensions_.contains(ext); + } + + /// Returns true if any of the capabilities is enabled, or if |capabilities| + /// is an empty set. + bool HasAnyOfCapabilities(const CapabilitySet& capabilities) const; + + /// Returns true if any of the extensions is enabled, or if |extensions| + /// is an empty set. + bool HasAnyOfExtensions(const ExtensionSet& extensions) const; + + /// Sets the addressing model of this module (logical/physical). + void set_addressing_model(spv::AddressingModel am); + + /// Returns true if the OpMemoryModel was found. + bool has_memory_model_specified() const { + return addressing_model_ != spv::AddressingModel::Max && + memory_model_ != spv::MemoryModel::Max; + } + + /// Returns the addressing model of this module, or Logical if uninitialized. + spv::AddressingModel addressing_model() const; + + /// Returns the addressing model of this module, or Logical if uninitialized. + uint32_t pointer_size_and_alignment() const { + return pointer_size_and_alignment_; + } + + /// Sets the memory model of this module. + void set_memory_model(spv::MemoryModel mm); + + /// Returns the memory model of this module, or Simple if uninitialized. + spv::MemoryModel memory_model() const; + + /// Sets the bit width for sampler/image type variables. If not set, they are + /// considered opaque + void set_samplerimage_variable_address_mode(uint32_t bit_width); + + /// Get the addressing mode currently set. If 0, it means addressing mode is + /// invalid Sampler/Image type variables must be considered opaque This mode + /// is only valid after the instruction has been read + uint32_t samplerimage_variable_address_mode() const; + + /// Returns true if the OpSamplerImageAddressingModeNV was found. + bool has_samplerimage_variable_address_mode_specified() const { + return sampler_image_addressing_mode_ != 0; + } + + const AssemblyGrammar& grammar() const { return grammar_; } + + /// Inserts the instruction into the list of ordered instructions in the file. + Instruction* AddOrderedInstruction(const spv_parsed_instruction_t* inst); + + /// Registers the instruction. This will add the instruction to the list of + /// definitions and register sampled image consumers. + void RegisterInstruction(Instruction* inst); + + /// Registers the debug instruction information. + void RegisterDebugInstruction(const Instruction* inst); + + /// Registers the decoration for the given + void RegisterDecorationForId(uint32_t id, const Decoration& dec) { + auto& dec_list = id_decorations_[id]; + dec_list.insert(dec); + } + + /// Registers the list of decorations for the given + template + void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) { + std::set& cur_decs = id_decorations_[id]; + cur_decs.insert(begin, end); + } + + /// Registers the list of decorations for the given member of the given + /// structure. + template + void RegisterDecorationsForStructMember(uint32_t struct_id, + uint32_t member_index, InputIt begin, + InputIt end) { + std::set& cur_decs = id_decorations_[struct_id]; + for (InputIt iter = begin; iter != end; ++iter) { + Decoration dec = *iter; + dec.set_struct_member_index(member_index); + cur_decs.insert(dec); + } + } + + /// Returns all the decorations for the given . If no decorations exist + /// for the , it registers an empty set for it in the map and + /// returns the empty set. + std::set& id_decorations(uint32_t id) { + return id_decorations_[id]; + } + + /// Returns the range of decorations for the given field of the given . + struct FieldDecorationsIter { + std::set::const_iterator begin; + std::set::const_iterator end; + }; + FieldDecorationsIter id_member_decorations(uint32_t id, + uint32_t member_index) { + const auto& decorations = id_decorations_[id]; + + // The decorations are sorted by member_index, so this look up will give the + // exact range of decorations for this member index. + Decoration min_decoration((spv::Decoration)0, {}, member_index); + Decoration max_decoration(spv::Decoration::Max, {}, member_index); + + FieldDecorationsIter result; + result.begin = decorations.lower_bound(min_decoration); + result.end = decorations.upper_bound(max_decoration); + + return result; + } + + // Returns const pointer to the internal decoration container. + const std::map>& id_decorations() const { + return id_decorations_; + } + + /// Returns true if the given id has the given decoration , + /// otherwise returns false. + bool HasDecoration(uint32_t id, spv::Decoration dec) { + const auto& decorations = id_decorations_.find(id); + if (decorations == id_decorations_.end()) return false; + + return std::any_of( + decorations->second.begin(), decorations->second.end(), + [dec](const Decoration& d) { return dec == d.dec_type(); }); + } + + /// Finds id's def, if it exists. If found, returns the definition otherwise + /// nullptr + const Instruction* FindDef(uint32_t id) const; + + /// Finds id's def, if it exists. If found, returns the definition otherwise + /// nullptr + Instruction* FindDef(uint32_t id); + + /// Returns the instructions in the order they appear in the binary + const std::vector& ordered_instructions() const { + return ordered_instructions_; + } + + /// Returns a map of instructions mapped by their result id + const std::unordered_map& all_definitions() const { + return all_definitions_; + } + + /// Returns a vector containing the instructions that consume the given + /// SampledImage id. + std::vector getSampledImageConsumers(uint32_t id) const; + + /// Records cons_id as a consumer of sampled_image_id. + void RegisterSampledImageConsumer(uint32_t sampled_image_id, + Instruction* consumer); + + // Record a cons_id as a consumer of texture_id + // if texture 'texture_id' has a QCOM image processing decoration + // and consumer is a load or a sampled image instruction + void RegisterQCOMImageProcessingTextureConsumer(uint32_t texture_id, + const Instruction* consumer0, + const Instruction* consumer1); + + // Record a function's storage class consumer instruction + void RegisterStorageClassConsumer(spv::StorageClass storage_class, + Instruction* consumer); + + /// Returns the set of Global Variables. + std::unordered_set& global_vars() { return global_vars_; } + + /// Returns the set of Local Variables. + std::unordered_set& local_vars() { return local_vars_; } + + /// Returns the number of Global Variables. + size_t num_global_vars() { return global_vars_.size(); } + + /// Returns the number of Local Variables. + size_t num_local_vars() { return local_vars_.size(); } + + /// Inserts a new to the set of Global Variables. + void registerGlobalVariable(const uint32_t id) { global_vars_.insert(id); } + + /// Inserts a new to the set of Local Variables. + void registerLocalVariable(const uint32_t id) { local_vars_.insert(id); } + + // Returns true if using relaxed block layout, equivalent to + // VK_KHR_relaxed_block_layout. + bool IsRelaxedBlockLayout() const { + return features_.env_relaxed_block_layout || options()->relax_block_layout; + } + + // Returns true if allowing localsizeid, either because the environment always + // allows it, or because it is enabled from the command-line. + bool IsLocalSizeIdAllowed() const { + return features_.env_allow_localsizeid || options()->allow_localsizeid; + } + + /// Sets the struct nesting depth for a given struct ID + void set_struct_nesting_depth(uint32_t id, uint32_t depth) { + struct_nesting_depth_[id] = depth; + } + + /// Returns the nesting depth of a given structure ID + uint32_t struct_nesting_depth(uint32_t id) { + return struct_nesting_depth_[id]; + } + + /// Records the has a nested block/bufferblock decorated struct for a given + /// struct ID + void SetHasNestedBlockOrBufferBlockStruct(uint32_t id, bool has) { + struct_has_nested_blockorbufferblock_struct_[id] = has; + } + + /// For a given struct ID returns true if it has a nested block/bufferblock + /// decorated struct + bool GetHasNestedBlockOrBufferBlockStruct(uint32_t id) { + return struct_has_nested_blockorbufferblock_struct_[id]; + } + + /// Records that the structure type has a member decorated with a built-in. + void RegisterStructTypeWithBuiltInMember(uint32_t id) { + builtin_structs_.insert(id); + } + + /// Returns true if the struct type with the given Id has a BuiltIn member. + bool IsStructTypeWithBuiltInMember(uint32_t id) const { + return (builtin_structs_.find(id) != builtin_structs_.end()); + } + + // Returns the state of optional features. + const Feature& features() const { return features_; } + + /// Adds the instruction data to unique_type_declarations_. + /// Returns false if an identical type declaration already exists. + bool RegisterUniqueTypeDeclaration(const Instruction* inst); + + // Returns type_id of the scalar component of |id|. + // |id| can be either + // - scalar, vector or matrix type + // - object of either scalar, vector or matrix type + uint32_t GetComponentType(uint32_t id) const; + + // Returns + // - 1 for scalar types or objects + // - vector size for vector types or objects + // - num columns for matrix types or objects + // Should not be called with any other arguments (will return zero and invoke + // assertion). + uint32_t GetDimension(uint32_t id) const; + + // Returns bit width of scalar or component. + // |id| can be + // - scalar, vector or matrix type + // - object of either scalar, vector or matrix type + // Will invoke assertion and return 0 if |id| is none of the above. + uint32_t GetBitWidth(uint32_t id) const; + + // Provides detailed information on matrix type. + // Returns false iff |id| is not matrix type. + bool GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, uint32_t* num_cols, + uint32_t* column_type, uint32_t* component_type) const; + + // Collects struct member types into |member_types|. + // Returns false iff not struct type or has no members. + // Deletes prior contents of |member_types|. + bool GetStructMemberTypes(uint32_t struct_type_id, + std::vector* member_types) const; + + // Returns true iff |id| is a type corresponding to the name of the function. + // Only works for types not for objects. + bool IsVoidType(uint32_t id) const; + bool IsBfloat16ScalarType(uint32_t id) const; + bool IsBfloat16VectorType(uint32_t id) const; + bool IsFloatScalarType(uint32_t id) const; + bool IsFloatArrayType(uint32_t id) const; + bool IsFloatVectorType(uint32_t id) const; + bool IsFloat16Vector2Or4Type(uint32_t id) const; + bool IsFloatScalarOrVectorType(uint32_t id) const; + bool IsFloatMatrixType(uint32_t id) const; + bool IsIntScalarType(uint32_t id) const; + bool IsIntArrayType(uint32_t id) const; + bool IsIntVectorType(uint32_t id) const; + bool IsIntScalarOrVectorType(uint32_t id) const; + bool IsUnsignedIntScalarType(uint32_t id) const; + bool IsUnsignedIntVectorType(uint32_t id) const; + bool IsUnsignedIntScalarOrVectorType(uint32_t id) const; + bool IsSignedIntScalarType(uint32_t id) const; + bool IsSignedIntVectorType(uint32_t id) const; + bool IsBoolScalarType(uint32_t id) const; + bool IsBoolVectorType(uint32_t id) const; + bool IsBoolScalarOrVectorType(uint32_t id) const; + bool IsPointerType(uint32_t id) const; + bool IsAccelerationStructureType(uint32_t id) const; + bool IsCooperativeMatrixType(uint32_t id) const; + bool IsCooperativeMatrixNVType(uint32_t id) const; + bool IsCooperativeMatrixKHRType(uint32_t id) const; + bool IsCooperativeMatrixAType(uint32_t id) const; + bool IsCooperativeMatrixBType(uint32_t id) const; + bool IsCooperativeMatrixAccType(uint32_t id) const; + bool IsFloatCooperativeMatrixType(uint32_t id) const; + bool IsIntCooperativeMatrixType(uint32_t id) const; + bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; + bool IsUnsigned64BitHandle(uint32_t id) const; + bool IsCooperativeVectorNVType(uint32_t id) const; + bool IsFloatCooperativeVectorNVType(uint32_t id) const; + bool IsIntCooperativeVectorNVType(uint32_t id) const; + bool IsUnsignedIntCooperativeVectorNVType(uint32_t id) const; + + // Returns true if |id| is a type id that contains |type| (or integer or + // floating point type) of |width| bits. + bool ContainsSizedIntOrFloatType(uint32_t id, spv::Op type, + uint32_t width) const; + // Returns true if |id| is a type id that contains a 8- or 16-bit int or + // 16-bit float that is not generally enabled for use. + bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; + + // Returns true if |id| is a type that contains a runtime-sized array. + // Does not consider a pointers as contains the array. + bool ContainsRuntimeArray(uint32_t id) const; + + // Generic type traversal. + // Only traverse pointers and functions if |traverse_all_types| is true. + // Recursively tests |f| against the type hierarchy headed by |id|. + bool ContainsType(uint32_t id, + const std::function& f, + bool traverse_all_types = true) const; + + // Returns true if |id| is type id that contains an untyped pointer. + bool ContainsUntypedPointer(uint32_t id) const; + + // Returns type_id if id has type or zero otherwise. + uint32_t GetTypeId(uint32_t id) const; + + // Returns opcode of the instruction which issued the id or OpNop if the + // instruction is not registered. + spv::Op GetIdOpcode(uint32_t id) const; + + // Returns type_id for given id operand if it has a type or zero otherwise. + // |operand_index| is expected to be pointing towards an operand which is an + // id. + uint32_t GetOperandTypeId(const Instruction* inst, + size_t operand_index) const; + + // Provides information on pointer type. Returns false iff not pointer type. + bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type, + spv::StorageClass* storage_class) const; + + // Is the ID the type of a pointer to a uniform block: Block-decorated struct + // in uniform storage class? The result is only valid after internal method + // CheckDecorationsOfBuffers has been called. + bool IsPointerToUniformBlock(uint32_t type_id) const { + return pointer_to_uniform_block_.find(type_id) != + pointer_to_uniform_block_.cend(); + } + // Save the ID of a pointer to uniform block. + void RegisterPointerToUniformBlock(uint32_t type_id) { + pointer_to_uniform_block_.insert(type_id); + } + // Is the ID the type of a struct used as a uniform block? + // The result is only valid after internal method CheckDecorationsOfBuffers + // has been called. + bool IsStructForUniformBlock(uint32_t type_id) const { + return struct_for_uniform_block_.find(type_id) != + struct_for_uniform_block_.cend(); + } + // Save the ID of a struct of a uniform block. + void RegisterStructForUniformBlock(uint32_t type_id) { + struct_for_uniform_block_.insert(type_id); + } + // Is the ID the type of a pointer to a storage buffer: BufferBlock-decorated + // struct in uniform storage class, or Block-decorated struct in StorageBuffer + // storage class? The result is only valid after internal method + // CheckDecorationsOfBuffers has been called. + bool IsPointerToStorageBuffer(uint32_t type_id) const { + return pointer_to_storage_buffer_.find(type_id) != + pointer_to_storage_buffer_.cend(); + } + // Save the ID of a pointer to a storage buffer. + void RegisterPointerToStorageBuffer(uint32_t type_id) { + pointer_to_storage_buffer_.insert(type_id); + } + // Is the ID the type of a struct for storage buffer? + // The result is only valid after internal method CheckDecorationsOfBuffers + // has been called. + bool IsStructForStorageBuffer(uint32_t type_id) const { + return struct_for_storage_buffer_.find(type_id) != + struct_for_storage_buffer_.cend(); + } + // Save the ID of a struct of a storage buffer. + void RegisterStructForStorageBuffer(uint32_t type_id) { + struct_for_storage_buffer_.insert(type_id); + } + + // Is the ID the type of a pointer to a storage image? That is, the pointee + // type is an image type which is known to not use a sampler. + bool IsPointerToStorageImage(uint32_t type_id) const { + return pointer_to_storage_image_.find(type_id) != + pointer_to_storage_image_.cend(); + } + // Save the ID of a pointer to a storage image. + void RegisterPointerToStorageImage(uint32_t type_id) { + pointer_to_storage_image_.insert(type_id); + } + + // Tries to evaluate a any scalar integer OpConstant as uint64. + // OpConstantNull is defined as zero for scalar int (will return true) + // OpSpecConstant* return false since their values cannot be relied upon + // during validation. + bool EvalConstantValUint64(uint32_t id, uint64_t* val) const; + // Same as EvalConstantValUint64 but returns a signed int + bool EvalConstantValInt64(uint32_t id, int64_t* val) const; + + // Tries to evaluate a 32-bit signed or unsigned scalar integer constant. + // Returns tuple . + // OpSpecConstant* return |is_const_int32| as false since their values cannot + // be relied upon during validation. + std::tuple EvalInt32IfConst(uint32_t id) const; + + // Returns the disassembly string for the given instruction. + std::string Disassemble(const Instruction& inst) const; + + // Returns the disassembly string for the given instruction. + std::string Disassemble(const uint32_t* words, uint16_t num_words) const; + + // Returns the string name for |decoration|. + std::string SpvDecorationString(uint32_t decoration) { + spv_operand_desc desc = nullptr; + if (grammar_.lookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration, + &desc) != SPV_SUCCESS) { + return std::string("Unknown"); + } + return std::string(desc->name); + } + std::string SpvDecorationString(spv::Decoration decoration) { + return SpvDecorationString(uint32_t(decoration)); + } + + // Returns whether type result_type_id and type m2 are cooperative matrices + // with the same "shape" (matching scope, rows, cols). If any are + // specialization constants, we assume they can match because we can't prove + // they don't. + spv_result_t CooperativeMatrixShapesMatch(const Instruction* inst, + uint32_t result_type_id, + uint32_t m2, bool is_conversion, + bool swap_row_col = false); + + spv_result_t CooperativeVectorDimensionsMatch(const Instruction* inst, + uint32_t v1, uint32_t v2); + + // Returns true if |lhs| and |rhs| logically match and, if the decorations of + // |rhs| are a subset of |lhs|. + // + // 1. Must both be either OpTypeArray or OpTypeStruct + // 2. If OpTypeArray, then + // * Length must be the same + // * Element type must match or logically match + // 3. If OpTypeStruct, then + // * Both have same number of elements + // * Element N for both structs must match or logically match + // + // If |check_decorations| is false, then the decorations are not checked. + bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs, + bool check_decorations); + + // Traces |inst| to find a single base pointer. Returns the base pointer. + // Will trace through the following instructions: + // * OpAccessChain + // * OpInBoundsAccessChain + // * OpPtrAccessChain + // * OpInBoundsPtrAccessChain + // * OpCopyObject + const Instruction* TracePointer(const Instruction* inst) const; + + // Validates the storage class for the target environment. + bool IsValidStorageClass(spv::StorageClass storage_class) const; + + // Takes a Vulkan Valid Usage ID (VUID) as |id| and optional |reference| and + // will return a non-empty string only if ID is known and targeting Vulkan. + // VUIDs are found in the Vulkan-Docs repo in the form "[[VUID-ref-ref-id]]" + // where "id" is always an 5 char long number (with zeros padding) and matches + // to |id|. |reference| is used if there is a "common validity" and the VUID + // shares the same |id| value. + // + // More details about Vulkan validation can be found in Vulkan Guide: + // https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/validation_overview.md + std::string VkErrorID(uint32_t id, const char* reference = nullptr) const; + + // Testing method to allow setting the current layout section. + void SetCurrentLayoutSectionForTesting(ModuleLayoutSection section) { + current_layout_section_ = section; + } + + // Check if instruction 'id' is a consumer of a texture decorated + // with a QCOM image processing decoration + bool IsQCOMImageProcessingTextureConsumer(uint32_t id) { + return qcom_image_processing_consumers_.find(id) != + qcom_image_processing_consumers_.end(); + } + + private: + ValidationState_t(const ValidationState_t&); + + const spv_const_context context_; + + /// Stores the Validator command line options. Must be a valid options object. + const spv_const_validator_options options_; + + /// The SPIR-V binary module we're validating. + const uint32_t* words_; + const size_t num_words_; + + /// The generator of the SPIR-V. + uint32_t generator_ = 0; + + /// The version of the SPIR-V. + uint32_t version_ = 0; + + /// The total number of instructions in the binary. + size_t total_instructions_ = 0; + /// The total number of functions in the binary. + size_t total_functions_ = 0; + + /// IDs which have been forward declared but have not been defined + std::unordered_set unresolved_forward_ids_; + + /// IDs that have been declared as forward pointers. + std::unordered_set forward_pointer_ids_; + + /// Stores a vector of instructions that use the result of a given + /// OpSampledImage instruction. + std::unordered_map> + sampled_image_consumers_; + + /// Stores load instructions that load textures used + // in QCOM image processing functions + std::unordered_set qcom_image_processing_consumers_; + + /// A map of operand IDs and their names defined by the OpName instruction + std::unordered_map operand_names_; + + /// The section of the code being processed + ModuleLayoutSection current_layout_section_; + + /// A list of functions in the module. + /// Pointers to objects in this container are guaranteed to be stable and + /// valid until the end of lifetime of the validation state. + std::vector module_functions_; + + /// Capabilities declared in the module + CapabilitySet module_capabilities_; + + /// Extensions declared in the module + ExtensionSet module_extensions_; + + /// List of all instructions in the order they appear in the binary + std::vector ordered_instructions_; + + /// Instructions that can be referenced by Ids + std::unordered_map all_definitions_; + + /// IDs that are entry points, ie, arguments to OpEntryPoint. + std::vector entry_points_; + + /// Maps an entry point id to its descriptions. + std::unordered_map> + entry_point_descriptions_; + + /// IDs that are entry points, ie, arguments to OpEntryPoint, and root a call + /// graph that recurses. + std::set recursive_entry_points_; + + /// Functions IDs that are target of OpFunctionCall. + std::unordered_set function_call_targets_; + + /// ID Bound from the Header + uint32_t id_bound_; + + /// Set of Global Variable IDs (Storage Class other than 'Function') + std::unordered_set global_vars_; + + /// Set of Local Variable IDs ('Function' Storage Class) + std::unordered_set local_vars_; + + /// Set of struct types that have members with a BuiltIn decoration. + std::unordered_set builtin_structs_; + + /// Structure Nesting Depth + std::unordered_map struct_nesting_depth_; + + /// Structure has nested blockorbufferblock struct + std::unordered_map + struct_has_nested_blockorbufferblock_struct_; + + /// Stores the list of decorations for a given + std::map> id_decorations_; + + /// Stores type declarations which need to be unique (i.e. non-aggregates), + /// in the form [opcode, operand words], result_id is not stored. + /// Using ordered set to avoid the need for a vector hash function. + /// The size of this container is expected not to exceed double-digits. + std::set> unique_type_declarations_; + + AssemblyGrammar grammar_; + + spv::AddressingModel addressing_model_; + spv::MemoryModel memory_model_; + // pointer size derived from addressing model. Assumes all storage classes + // have the same pointer size (for physical pointer types). + uint32_t pointer_size_and_alignment_; + + /// bit width of sampler/image type variables. Valid values are 32 and 64 + uint32_t sampler_image_addressing_mode_; + + /// NOTE: See correspoding getter functions + bool in_function_; + + /// The state of optional features. These are determined by capabilities + /// declared by the module and the environment. + Feature features_; + + /// Maps function ids to function stat objects. + std::unordered_map id_to_function_; + + /// Mapping entry point -> execution models. It is presumed that the same + /// function could theoretically be used as 'main' by multiple OpEntryPoint + /// instructions. + std::unordered_map> + entry_point_to_execution_models_; + + /// Mapping entry point -> execution modes. + std::unordered_map> + entry_point_to_execution_modes_; + + // Mapping entry point -> local size execution mode instruction + std::unordered_map + entry_point_to_local_size_or_id_; + + // Mapping entry point -> OutputPrimitivesEXT execution mode instruction + std::unordered_map + entry_point_to_output_primitives_; + + /// Mapping function -> array of entry points inside this + /// module which can (indirectly) call the function. + std::unordered_map> function_to_entry_points_; + const std::vector empty_ids_; + + // The IDs of types of pointers to Block-decorated structs in Uniform storage + // class. This is populated at the start of ValidateDecorations. + std::unordered_set pointer_to_uniform_block_; + // The IDs of struct types for uniform blocks. + // This is populated at the start of ValidateDecorations. + std::unordered_set struct_for_uniform_block_; + // The IDs of types of pointers to BufferBlock-decorated structs in Uniform + // storage class, or Block-decorated structs in StorageBuffer storage class. + // This is populated at the start of ValidateDecorations. + std::unordered_set pointer_to_storage_buffer_; + // The IDs of struct types for storage buffers. + // This is populated at the start of ValidateDecorations. + std::unordered_set struct_for_storage_buffer_; + // The IDs of types of pointers to storage images. This is populated in the + // TypePass. + std::unordered_set pointer_to_storage_image_; + + /// Maps ids to friendly names. + std::unique_ptr friendly_mapper_; + spvtools::NameMapper name_mapper_; + + /// Variables used to reduce the number of diagnostic messages. + uint32_t num_of_warnings_; + uint32_t max_num_of_warnings_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_VALIDATION_STATE_H_ diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/README.md b/engine/engine/3rdlibs/spirv-tools/source/wasm/README.md new file mode 100644 index 00000000..aca0f70c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/README.md @@ -0,0 +1,43 @@ +# SPIRV-Tools + +Wasm (WebAssembly) build of https://github.com/KhronosGroup/SPIRV-Tools + +## Usage + +```js +const spirvTools = require("spirv-tools"); + +const test = async () => { + // Load the library + const spv = await spirvTools(); + + // assemble + const source = ` + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpSource GLSL 450 + OpDecorate %spec SpecId 1 + %int = OpTypeInt 32 1 + %spec = OpSpecConstant %int 0 + %const = OpConstant %int 42`; + const asResult = spv.as( + source, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_TEXT_TO_BINARY_OPTION_NONE + ); + console.log(`as returned ${asResult.byteLength} bytes`); + + // re-disassemble + const disResult = spv.dis( + asResult, + spv.SPV_ENV_UNIVERSAL_1_3, + spv.SPV_BINARY_TO_TEXT_OPTION_INDENT | + spv.SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | + spv.SPV_BINARY_TO_TEXT_OPTION_COLOR + ); + console.log("dis:\n", disResult); +}; + +test(); +``` diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/build.sh b/engine/engine/3rdlibs/spirv-tools/source/wasm/build.sh new file mode 100644 index 00000000..f4663565 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/build.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Copyright (c) 2020 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# This is required to run any git command in the docker since owner will +# have changed between the clone environment, and the docker container. +# Marking the root of the repo as safe for ownership changes. +git config --global --add safe.directory /app + +NUM_CORES=$(nproc) +echo "Detected $NUM_CORES cores for building" + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +VERSION=$(sed -n '0,/^v20/ s/^v\(20[0-9.]*\).*/\1/p' $DIR/../../CHANGES).${GITHUB_RUN_NUMBER:-0} +echo "Version: $VERSION" + +build() { + type=$1 + shift + args=$@ + mkdir -p build/$type + pushd build/$type + echo $args + emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + $args \ + ../.. + emmake make -j $(( $NUM_CORES )) SPIRV-Tools-static + + echo Building js interface + emcc \ + --bind \ + -I../../include \ + -std=c++17 \ + ../../source/wasm/spirv-tools.cpp \ + source/libSPIRV-Tools.a \ + -o spirv-tools.js \ + -s MODULARIZE \ + -Oz + + popd + mkdir -p out/$type + + # copy other js files + cp source/wasm/spirv-tools.d.ts out/$type/ + sed -e 's/\("version"\s*:\s*\).*/\1"'$VERSION'",/' source/wasm/package.json > out/$type/package.json + cp source/wasm/README.md out/$type/ + cp LICENSE out/$type/ + + cp build/$type/spirv-tools.js out/$type/ + gzip -9 -k -f out/$type/spirv-tools.js + if [ -e build/$type/spirv-tools.wasm ] ; then + cp build/$type/spirv-tools.wasm out/$type/ + gzip -9 -k -f out/$type/spirv-tools.wasm + fi +} + +if [ ! -d external/spirv-headers ] ; then + echo "Fetching deps" + utils/git-sync-deps +fi + +echo Building ${BASH_REMATCH[1]} +build web\ + -DSPIRV_COLOR_TERMINAL=OFF\ + -DSPIRV_SKIP_TESTS=ON\ + -DSPIRV_SKIP_EXECUTABLES=ON + +wc -c out/*/* diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/docker-compose.yml b/engine/engine/3rdlibs/spirv-tools/source/wasm/docker-compose.yml new file mode 100644 index 00000000..2340fdb0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3" +services: + build: + image: emscripten/emsdk:3.1.28 + environment: + GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-} + working_dir: /app + command: ./source/wasm/build.sh + volumes: + - ./:/app diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/package.json b/engine/engine/3rdlibs/spirv-tools/source/wasm/package.json new file mode 100644 index 00000000..78273538 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/package.json @@ -0,0 +1,17 @@ +{ + "name": "spirv-tools", + "version": "VERSION", + "license": "Apache-2.0", + "main": "spirv-tools", + "types": "spirv-tools.d.ts", + "files": [ + "*.wasm", + "*.js", + "*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/KhronosGroup/SPIRV-Tools" + }, + "homepage": "https://github.com/KhronosGroup/SPIRV-Tools" +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.cpp b/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.cpp new file mode 100644 index 00000000..33f2f05f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.hpp" + +#include +#include +#include + +#include +#include +using namespace emscripten; + +void print_msg_to_stderr (spv_message_level_t, const char*, + const spv_position_t&, const char* m) { + std::cerr << "error: " << m << std::endl; +}; + +std::string dis(std::string const& buffer, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + const uint32_t* ptr = reinterpret_cast(buffer.data()); + spirv.assign(ptr, ptr + buffer.size() / 4); + std::string disassembly; + if (!core.Disassemble(spirv, &disassembly, options)) return "Error"; + return disassembly; +} + +emscripten::val as(std::string const& source, uint32_t env, uint32_t options) { + spvtools::SpirvTools core(static_cast(env)); + core.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + if (!core.Assemble(source, &spirv, options)) spirv.clear(); + const uint8_t* ptr = reinterpret_cast(spirv.data()); + return emscripten::val(emscripten::typed_memory_view(spirv.size() * 4, + ptr)); +} + +EMSCRIPTEN_BINDINGS(my_module) { + function("dis", &dis); + function("as", &as); + + constant("SPV_ENV_UNIVERSAL_1_0", static_cast(SPV_ENV_UNIVERSAL_1_0)); + constant("SPV_ENV_VULKAN_1_0", static_cast(SPV_ENV_VULKAN_1_0)); + constant("SPV_ENV_UNIVERSAL_1_1", static_cast(SPV_ENV_UNIVERSAL_1_1)); + constant("SPV_ENV_OPENCL_2_1", static_cast(SPV_ENV_OPENCL_2_1)); + constant("SPV_ENV_OPENCL_2_2", static_cast(SPV_ENV_OPENCL_2_2)); + constant("SPV_ENV_OPENGL_4_0", static_cast(SPV_ENV_OPENGL_4_0)); + constant("SPV_ENV_OPENGL_4_1", static_cast(SPV_ENV_OPENGL_4_1)); + constant("SPV_ENV_OPENGL_4_2", static_cast(SPV_ENV_OPENGL_4_2)); + constant("SPV_ENV_OPENGL_4_3", static_cast(SPV_ENV_OPENGL_4_3)); + constant("SPV_ENV_OPENGL_4_5", static_cast(SPV_ENV_OPENGL_4_5)); + constant("SPV_ENV_UNIVERSAL_1_2", static_cast(SPV_ENV_UNIVERSAL_1_2)); + constant("SPV_ENV_OPENCL_1_2", static_cast(SPV_ENV_OPENCL_1_2)); + constant("SPV_ENV_OPENCL_EMBEDDED_1_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_1_2)); + constant("SPV_ENV_OPENCL_2_0", static_cast(SPV_ENV_OPENCL_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_0", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_0)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_1", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_1)); + constant("SPV_ENV_OPENCL_EMBEDDED_2_2", static_cast(SPV_ENV_OPENCL_EMBEDDED_2_2)); + constant("SPV_ENV_UNIVERSAL_1_3", static_cast(SPV_ENV_UNIVERSAL_1_3)); + constant("SPV_ENV_VULKAN_1_1", static_cast(SPV_ENV_VULKAN_1_1)); + constant("SPV_ENV_WEBGPU_0", static_cast(SPV_ENV_WEBGPU_0)); + constant("SPV_ENV_UNIVERSAL_1_4", static_cast(SPV_ENV_UNIVERSAL_1_4)); + constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + constant("SPV_ENV_UNIVERSAL_1_5", static_cast(SPV_ENV_UNIVERSAL_1_5)); + constant("SPV_ENV_VULKAN_1_2", static_cast(SPV_ENV_VULKAN_1_2)); + constant("SPV_ENV_UNIVERSAL_1_6", + static_cast(SPV_ENV_UNIVERSAL_1_6)); + + constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast(SPV_BINARY_TO_TEXT_OPTION_NONE)); + constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast(SPV_BINARY_TO_TEXT_OPTION_PRINT)); + constant("SPV_BINARY_TO_TEXT_OPTION_COLOR", static_cast(SPV_BINARY_TO_TEXT_OPTION_COLOR)); + constant("SPV_BINARY_TO_TEXT_OPTION_INDENT", static_cast(SPV_BINARY_TO_TEXT_OPTION_INDENT)); + constant("SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET", static_cast(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET)); + constant("SPV_BINARY_TO_TEXT_OPTION_NO_HEADER", static_cast(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + constant("SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES", static_cast(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)); + + constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast(SPV_TEXT_TO_BINARY_OPTION_NONE)); + constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); +} diff --git a/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.d.ts b/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.d.ts new file mode 100644 index 00000000..c06bdf18 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/source/wasm/spirv-tools.d.ts @@ -0,0 +1,57 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +declare interface SpirvTools { + as(input: string, env: number, options: number): Uint8Array; + dis(input: Uint8Array, env: number, options: number): string; + + SPV_ENV_UNIVERSAL_1_0: number; + SPV_ENV_VULKAN_1_0: number; + SPV_ENV_UNIVERSAL_1_1: number; + SPV_ENV_OPENCL_2_1: number; + SPV_ENV_OPENCL_2_2: number; + SPV_ENV_OPENGL_4_0: number; + SPV_ENV_OPENGL_4_1: number; + SPV_ENV_OPENGL_4_2: number; + SPV_ENV_OPENGL_4_3: number; + SPV_ENV_OPENGL_4_5: number; + SPV_ENV_UNIVERSAL_1_2: number; + SPV_ENV_OPENCL_1_2: number; + SPV_ENV_OPENCL_EMBEDDED_1_2: number; + SPV_ENV_OPENCL_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_0: number; + SPV_ENV_OPENCL_EMBEDDED_2_1: number; + SPV_ENV_OPENCL_EMBEDDED_2_2: number; + SPV_ENV_UNIVERSAL_1_3: number; + SPV_ENV_VULKAN_1_1: number; + SPV_ENV_WEBGPU_0: number; + SPV_ENV_UNIVERSAL_1_4: number; + SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; + SPV_ENV_UNIVERSAL_1_5: number; + SPV_ENV_VULKAN_1_2: number; + SPV_ENV_UNIVERSAL_1_6: number; + + SPV_TEXT_TO_BINARY_OPTION_NONE: number; + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; + + SPV_BINARY_TO_TEXT_OPTION_NONE: number; + SPV_BINARY_TO_TEXT_OPTION_PRINT: number; + SPV_BINARY_TO_TEXT_OPTION_COLOR: number; + SPV_BINARY_TO_TEXT_OPTION_INDENT: number; + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET: number; + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER: number; + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES: number; +} + +export default function (): Promise; diff --git a/engine/engine/3rdlibs/spirv-tools/utils/check_code_format.sh b/engine/engine/3rdlibs/spirv-tools/utils/check_code_format.sh new file mode 100644 index 00000000..affd3ab1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/check_code_format.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script determines if the source code in a Pull Request is properly formatted. +# Exits with non 0 exit code if formatting is needed. +# Assumptions: +# - git and python3 are on the path +# - Runs from the project root diretory. +# - 'clang-format' is on the path, or env var CLANG_FORMAT points to it. +# - 'clang-format-diff.py' is in the utils directory, or env var +# points to it.CLANG_FORMAT_DIFF + +BASE_BRANCH=${1:-main} + +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} +if [ ! -f "$CLANG_FORMAT" ]; then + echo missing clang-format: set CLANG_FORMAT or put clang-format in the PATH + exit 1 +fi + +# Find clang-format-diff.py from an environment variable, or use a default +CLANG_FORMAT_DIFF=${CLANG_FORMAT_DIFF:-./utils/clang-format-diff.py} +if [ ! -f "$CLANG_FORMAT_DIFF" ]; then + echo missing clang-format-diffy.py: set CLANG_FORMAT_DIFF or put it in ./utils/clang-format-diff.py + exit 1 +fi + +echo "Comparing "$(git rev-parse HEAD)" against $BASE_BRANCH" +echo Using $("$CLANG_FORMAT" --version) + +FILES_TO_CHECK=$(git diff --name-only ${BASE_BRANCH} | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$") + +if [ -z "${FILES_TO_CHECK}" ]; then + echo "No source code to check for formatting." + exit 0 +fi +echo "Checking Formatting for the following files" +echo "${FILES_TO_CHECK}" +echo + +FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python3 "${CLANG_FORMAT_DIFF}" -p1 -style=file -binary "$CLANG_FORMAT") + +if [ -z "${FORMAT_DIFF}" ]; then + echo "All source code in PR properly formatted." + exit 0 +else + echo "Found formatting errors!" + echo "${FORMAT_DIFF}" + exit 1 +fi diff --git a/engine/engine/3rdlibs/spirv-tools/utils/check_copyright.py b/engine/engine/3rdlibs/spirv-tools/utils/check_copyright.py new file mode 100644 index 00000000..c477ece9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/check_copyright.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +# coding=utf-8 +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Checks for copyright notices in all the files that need them under the + +current directory. Optionally insert them. When inserting, replaces +an MIT or Khronos free use license with Apache 2. +""" + +import argparse +import fileinput +import fnmatch +import inspect +import os +import re +import sys + +# List of designated copyright owners. +AUTHORS = ['The Khronos Group Inc.', + 'LunarG Inc.', + 'Google Inc.', + 'Google LLC', + 'Pierre Moreau', + 'Samsung Inc', + 'André Perez Maselco', + 'Vasyl Teliman', + 'Advanced Micro Devices, Inc.', + 'Stefano Milizia', + 'Alastair F. Donaldson', + 'Mostafa Ashraf', + 'Shiyu Liu', + 'ZHOU He', + 'Nintendo', + 'Epic Games, Inc.', + 'NVIDIA Corporation'] +CURRENT_YEAR = 2023 + +FIRST_YEAR = 2014 +FINAL_YEAR = CURRENT_YEAR + 5 +# A regular expression to match the valid years in the copyright information. +YEAR_REGEX = '(' + '|'.join( + str(year) for year in range(FIRST_YEAR, FINAL_YEAR + 1)) + ')' + +# A regular expression to make a range of years in the form -. +YEAR_RANGE_REGEX = '(' +for year1 in range(FIRST_YEAR, FINAL_YEAR + 1): + for year2 in range(year1 + 1, FINAL_YEAR + 1): + YEAR_RANGE_REGEX += str(year1) + '-' + str(year2) + '|' +YEAR_RANGE_REGEX = YEAR_RANGE_REGEX[:-1] + ')' + +# In the copyright info, the year can be a single year or a range. This is a +# regex to make sure it matches one of them. +YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')' + +# The final regular expression to match a valid copyright line. +COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format( + YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS))) + +MIT_BEGIN_RE = re.compile('Permission is hereby granted, ' + 'free of charge, to any person obtaining a') +MIT_END_RE = re.compile('MATERIALS OR THE USE OR OTHER DEALINGS IN ' + 'THE MATERIALS.') +APACHE2_BEGIN_RE = re.compile('Licensed under the Apache License, ' + 'Version 2.0 \(the "License"\);') +APACHE2_END_RE = re.compile('limitations under the License.') + +LICENSED = """Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" +LICENSED_LEN = 10 # Number of lines in LICENSED + + +def find(top, filename_glob, skip_glob_dir_list, skip_glob_files_list): + """Returns files in the tree rooted at top matching filename_glob but not + in directories matching skip_glob_dir_list nor files matching + skip_glob_dir_list.""" + + file_list = [] + for path, dirs, files in os.walk(top): + for glob in skip_glob_dir_list: + for match in fnmatch.filter(dirs, glob): + dirs.remove(match) + for filename in fnmatch.filter(files, filename_glob): + full_file = os.path.join(path, filename) + if full_file not in skip_glob_files_list: + file_list.append(full_file) + return file_list + + +def filtered_descendants(glob): + """Returns glob-matching filenames under the current directory, but skips + some irrelevant paths.""" + return find('.', glob, ['third_party', 'external', 'CompilerIdCXX', + 'build*', 'out*'], ['./utils/clang-format-diff.py']) + + +def skip(line): + """Returns true if line is all whitespace or shebang.""" + stripped = line.lstrip() + return stripped == '' or stripped.startswith('#!') + + +def comment(text, prefix): + """Returns commented-out text. + + Each line of text will be prefixed by prefix and a space character. Any + trailing whitespace will be trimmed. + """ + accum = ['{} {}'.format(prefix, line).rstrip() for line in text.split('\n')] + return '\n'.join(accum) + + +def insert_copyright(author, glob, comment_prefix): + """Finds all glob-matching files under the current directory and inserts the + copyright message, and license notice. An MIT license or Khronos free + use license (modified MIT) is replaced with an Apache 2 license. + + The copyright message goes into the first non-whitespace, non-shebang line + in a file. The license notice follows it. Both are prefixed on each line + by comment_prefix and a space. + """ + + copyright = comment('Copyright (c) {} {}'.format(CURRENT_YEAR, author), + comment_prefix) + '\n\n' + licensed = comment(LICENSED, comment_prefix) + '\n\n' + for file in filtered_descendants(glob): + # Parsing states are: + # 0 Initial: Have not seen a copyright declaration. + # 1 Seen a copyright line and no other interesting lines + # 2 In the middle of an MIT or Khronos free use license + # 9 Exited any of the above + state = 0 + update_file = False + for line in fileinput.input(file, inplace=1): + emit = True + if state == 0: + if COPYRIGHT_RE.search(line): + state = 1 + elif skip(line): + pass + else: + # Didn't see a copyright. Inject copyright and license. + sys.stdout.write(copyright) + sys.stdout.write(licensed) + # Assume there isn't a previous license notice. + state = 1 + elif state == 1: + if MIT_BEGIN_RE.search(line): + state = 2 + emit = False + elif APACHE2_BEGIN_RE.search(line): + # Assume an Apache license is preceded by a copyright + # notice. So just emit it like the rest of the file. + state = 9 + elif state == 2: + # Replace the MIT license with Apache 2 + emit = False + if MIT_END_RE.search(line): + state = 9 + sys.stdout.write(licensed) + if emit: + sys.stdout.write(line) + + +def alert_if_no_copyright(glob, comment_prefix): + """Prints names of all files missing either a copyright or Apache 2 license. + + Finds all glob-matching files under the current directory and checks if they + contain the copyright message and license notice. Prints the names of all the + files that don't meet both criteria. + + Returns the total number of file names printed. + """ + printed_count = 0 + for file in filtered_descendants(glob): + has_copyright = False + has_apache2 = False + line_num = 0 + apache_expected_end = 0 + with open(file, encoding='utf-8') as contents: + for line in contents: + line_num += 1 + if COPYRIGHT_RE.search(line): + has_copyright = True + if APACHE2_BEGIN_RE.search(line): + apache_expected_end = line_num + LICENSED_LEN + if (line_num is apache_expected_end) and APACHE2_END_RE.search(line): + has_apache2 = True + if not (has_copyright and has_apache2): + message = file + if not has_copyright: + message += ' has no copyright' + if not has_apache2: + message += ' has no Apache 2 license notice' + print(message) + printed_count += 1 + return printed_count + + +class ArgParser(argparse.ArgumentParser): + def __init__(self): + super(ArgParser, self).__init__( + description=inspect.getdoc(sys.modules[__name__])) + self.add_argument('--update', dest='author', action='store', + help='For files missing a copyright notice, insert ' + 'one for the given author, and add a license ' + 'notice. The author must be in the AUTHORS ' + 'list in the script.') + + +def main(): + glob_comment_pairs = [('*.h', '//'), ('*.hpp', '//'), ('*.sh', '#'), + ('*.py', '#'), ('*.cpp', '//'), + ('CMakeLists.txt', '#')] + argparser = ArgParser() + args = argparser.parse_args() + + if args.author: + if args.author not in AUTHORS: + print('error: --update argument must be in the AUTHORS list in ' + 'check_copyright.py: {}'.format(AUTHORS)) + sys.exit(1) + for pair in glob_comment_pairs: + insert_copyright(args.author, *pair) + sys.exit(0) + else: + count = sum([alert_if_no_copyright(*p) for p in glob_comment_pairs]) + sys.exit(count > 0) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/check_symbol_exports.py b/engine/engine/3rdlibs/spirv-tools/utils/check_symbol_exports.py new file mode 100644 index 00000000..e44294fe --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/check_symbol_exports.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Ensures that all externally visible functions in the library have an appropriate name + +Appropriate function names are: + - names starting with spv, + - anything in a namespace, + - functions added by the protobuf compiler, + - and weak definitions of new and delete.""" + +import os.path +import re +import subprocess +import sys + + +PROG = 'check_symbol_exports' + + +def command_output(cmd, directory): + """Runs a command in a directory and returns its standard output stream. + + Captures the standard error stream. + + Raises a RuntimeError if the command fails to launch or otherwise fails. + """ + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + (stdout, _) = p.communicate() + if p.returncode != 0: + raise RuntimeError('Failed to run %s in %s' % (cmd, directory)) + return stdout + + +def check_library(library): + """Scans the given library file for global exports. If all such + exports are namespaced or begin with spv (in either C or C++ styles) + then return 0. Otherwise emit a message and return 1.""" + + # The pattern for an externally visible symbol record + symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)') + + # Ok patterns are as follows, assuming Itanium name mangling: + # spv[A-Z] : extern "C" symbol starting with spv + # _ZN : something in a namespace + # _ZSt : something in the standard namespace + # _ZZN : something in a local scope and namespace + # _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_] + symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])') + + # In addition, the following pattern allowlists global functions that are added + # by the protobuf compiler: + # - AddDescriptors_spvtoolsfuzz_2eproto() + # - InitDefaults_spvtoolsfuzz_2eproto() + symbol_allowlist_pattern = re.compile(r'_Z[0-9]+.*spvtoolsfuzz_2eproto.*') + + symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)') + # Compilaion for Arm has various thunks for constructors, destructors, vtables. + # They are weak. + symbol_is_thunk = re.compile(r'^_ZT') + + # This occurs in NDK builds. + symbol_is_hidden = re.compile(r'^\.hidden ') + + seen = set() + result = 0 + for line in command_output(['objdump', '-t', library], '.').split('\n'): + match = symbol_pattern.search(line) + if match: + linkage = match.group(1) + symbol = match.group(2) + if symbol not in seen: + seen.add(symbol) + #print("look at '{}'".format(symbol)) + if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'): + if not (symbol_is_thunk.match(symbol) and linkage == 'w'): + if not (symbol_allowlist_pattern.match(symbol) or + symbol_ok_pattern.match(symbol) or + symbol_is_hidden.match(symbol)): + print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) + result = 1 + return result + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Check global names exported from a library') + parser.add_argument('library', help='The static library to examine') + args = parser.parse_args() + + if not os.path.isfile(args.library): + print('{}: error: {} does not exist'.format(PROG, args.library)) + sys.exit(1) + + if os.name == 'posix': + status = check_library(args.library) + sys.exit(status) + else: + print('Passing test since not on Posix') + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/fixup_fuzz_result.py b/engine/engine/3rdlibs/spirv-tools/utils/fixup_fuzz_result.py new file mode 100644 index 00000000..5b14a7db --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/fixup_fuzz_result.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +if len(sys.argv) < 1: + print("Need file to chop"); + +with open(sys.argv[1], mode='rb') as file: + file_content = file.read() + content = file_content[:len(file_content) - (len(file_content) % 4)] + sys.stdout.write(content) + diff --git a/engine/engine/3rdlibs/spirv-tools/utils/generate_changelog.py b/engine/engine/3rdlibs/spirv-tools/utils/generate_changelog.py new file mode 100644 index 00000000..348bc50a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/generate_changelog.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Args: +# Updates an output file with changelog from the given CHANGES file and tag. +# - search for first line matching in file +# - search for the next line with a tag +# - writes all the lines in between those 2 tags into + +import errno +import os +import os.path +import re +import subprocess +import logging +import sys + +# Regex to match the SPIR-V version tag. +# Example of matching tags: +# - v2020.1 +# - v2020.1-dev +# - v2020.1.rc1 +VERSION_REGEX = re.compile(r'^(v\d+\.\d+) +[0-9]+-[0-9]+-[0-9]+$') + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist.""" + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + +def main(): + FORMAT = '%(asctime)s %(message)s' + logging.basicConfig(format="[%(asctime)s][%(levelname)-8s] %(message)s", datefmt="%H:%M:%S") + if len(sys.argv) != 4: + logging.error("usage: {} ".format(sys.argv[0])) + sys.exit(1) + + changes_path = sys.argv[1] + start_tag = sys.argv[2] + output_file_path = sys.argv[3] + + changelog = [] + has_found_start = False + with open(changes_path, "r") as file: + for line in file.readlines(): + m = VERSION_REGEX.match(line) + if m: + print(m.groups()[0]) + print(start_tag) + if has_found_start: + break; + if start_tag == m.groups()[0]: + has_found_start = True + continue + + if has_found_start: + changelog.append(line) + + if not has_found_start: + logging.error("No tag matching {} found.".format(start_tag)) + sys.exit(1) + + content = "".join(changelog) + if os.path.isfile(output_file_path): + with open(output_file_path, 'r') as f: + if content == f.read(): + sys.exit(0) + + mkdir_p(os.path.dirname(output_file_path)) + with open(output_file_path, 'w') as f: + f.write(content) + sys.exit(0) + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/generate_grammar_tables.py b/engine/engine/3rdlibs/spirv-tools/utils/generate_grammar_tables.py new file mode 100644 index 00000000..d7b52174 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/generate_grammar_tables.py @@ -0,0 +1,940 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates various info tables from SPIR-V JSON grammar.""" + +import errno +import json +import os.path +import re + +# Prefix for all C variables generated by this script. +PYGEN_VARIABLE_PREFIX = 'pygen_variable' + +# Extensions to recognize, but which don't necessarily come from the SPIR-V +# core or KHR grammar files. Get this list from the SPIR-V registry web page. +# NOTE: Only put things on this list if it is not in those grammar files. +EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ +SPV_AMD_gcn_shader +SPV_AMD_gpu_shader_half_float +SPV_AMD_gpu_shader_int16 +SPV_AMD_shader_trinary_minmax +SPV_KHR_non_semantic_info +SPV_EXT_relaxed_printf_string_address_space +""" + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they don't yet + exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + + +def convert_min_required_version(version): + """Converts the minimal required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return 'SPV_SPIRV_VERSION_WORD(1, 0)' + if version == 'None': + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + + +def convert_max_required_version(version): + """Converts the maximum required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + +def get_alias_array_name(aliases): + """Returns the name of the array containing all the given aliases. + + Arguments: + - aliases: a sequence of alias names + """ + if not aliases: + return 'nullptr'; + return '{}_aliases_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(aliases)) + +def compose_alias_list(aliases): + """Returns a string containing a braced list of aliases. + + Arguments: + - aliases: a sequence of alias names + + Returns: + a string containing the braced list of char* named by aliases. + """ + return '{' + ', '.join([('"{}"').format(a) for a in aliases]) + '}' + +def generate_aliases_arrays(aliases): + """Returns the arrays of aliases + + Arguments: + - aliases: a sequence of sequence of alias names + """ + aliases = sorted(set([tuple(a) for a in aliases if a])) + arrays = [ + 'static const char* {}[] = {};'.format( + get_alias_array_name(a), compose_alias_list(a)) + for a in aliases] + return '\n'.join(arrays) + +def compose_capability_list(caps): + """Returns a string containing a braced list of capabilities as enums. + + Arguments: + - caps: a sequence of capability names + + Returns: + a string containing the braced list of SpvCapability* or spv::Capability:: enums named by caps. + """ + base_string = 'spv::Capability::' + + return '{' + ', '.join([(base_string + '{}').format(c) for c in caps]) + '}' + + +def get_capability_array_name(caps): + """Returns the name of the array containing all the given capabilities. + + Args: + - caps: a sequence of capability names + """ + if not caps: + return 'nullptr' + return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps)) + + +def generate_capability_arrays(caps): + """Returns the arrays of capabilities. + + Arguments: + - caps: a sequence of sequence of capability names + """ + caps = sorted(set([tuple(c) for c in caps if c])) + cap_str = 'spv::Capability' + arrays = [ + 'static const ' + cap_str + ' {}[] = {};'.format( + get_capability_array_name(c), compose_capability_list(c)) + for c in caps] + return '\n'.join(arrays) + + +def compose_extension_list(exts): + """Returns a string containing a braced list of extensions as enums. + + Arguments: + - exts: a sequence of extension names + + Returns: + a string containing the braced list of extensions named by exts. + """ + return '{' + ', '.join( + ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}' + + +def get_extension_array_name(extensions): + """Returns the name of the array containing all the given extensions. + + Args: + - extensions: a sequence of extension names + """ + if not extensions: + return 'nullptr' + else: + return '{}_exts_{}'.format( + PYGEN_VARIABLE_PREFIX, ''.join(extensions)) + + +def generate_extension_arrays(extensions): + """Returns the arrays of extensions. + + Arguments: + - caps: a sequence of sequence of extension names + """ + extensions = sorted(set([tuple(e) for e in extensions if e])) + arrays = [ + 'static const spvtools::Extension {}[] = {};'.format( + get_extension_array_name(e), compose_extension_list(e)) + for e in extensions] + return '\n'.join(arrays) + + +def convert_operand_kind(operand_tuple): + """Returns the corresponding operand type used in spirv-tools for the given + operand kind and quantifier used in the JSON grammar. + + Arguments: + - operand_tuple: a tuple of two elements: + - operand kind: used in the JSON grammar + - quantifier: '', '?', or '*' + + Returns: + a string of the enumerant name in spv_operand_type_t + """ + kind, quantifier = operand_tuple + # The following cases are where we differ between the JSON grammar and + # spirv-tools. + if kind == 'IdResultType': + kind = 'TypeId' + elif kind == 'IdResult': + kind = 'ResultId' + elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': + kind = 'MemorySemanticsId' + elif kind == 'IdScope' or kind == 'Scope': + kind = 'ScopeId' + elif kind == 'IdRef': + kind = 'Id' + + elif kind == 'ImageOperands': + kind = 'Image' + elif kind == 'Dim': + kind = 'Dimensionality' + elif kind == 'ImageFormat': + kind = 'SamplerImageFormat' + elif kind == 'KernelEnqueueFlags': + kind = 'KernelEnqFlags' + + elif kind == 'LiteralExtInstInteger': + kind = 'ExtensionInstructionNumber' + elif kind == 'LiteralSpecConstantOpInteger': + kind = 'SpecConstantOpNumber' + elif kind == 'LiteralContextDependentNumber': + kind = 'TypedLiteralNumber' + + elif kind == 'PairLiteralIntegerIdRef': + kind = 'LiteralIntegerId' + elif kind == 'PairIdRefLiteralInteger': + kind = 'IdLiteralInteger' + elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar + kind = 'Id' + + if kind == 'FPRoundingMode': + kind = 'FpRoundingMode' + elif kind == 'FPFastMathMode': + kind = 'FpFastMathMode' + + if quantifier == '?': + kind = 'Optional{}'.format(kind) + elif quantifier == '*': + kind = 'Variable{}'.format(kind) + + return 'SPV_OPERAND_TYPE_{}'.format( + re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) + + +class InstInitializer(object): + """Instances holds a SPIR-V instruction suitable for printing as the + initializer for spv_opcode_desc_t.""" + + def __init__(self, opname, aliases, caps, exts, operands, version, lastVersion): + """Initialization. + + Arguments: + - opname: opcode name (with the 'Op' prefix) + - aliases: a sequence of aliases for the name of this opcode + - caps: a sequence of capability names required by this opcode + - exts: a sequence of names of extensions enabling this enumerant + - operands: a sequence of (operand-kind, operand-quantifier) tuples + - version: minimal SPIR-V version required for this opcode + - lastVersion: last version of SPIR-V that includes this opcode + """ + + assert opname.startswith('Op') + self.opname = opname[2:] # Remove the "Op" prefix. + self.num_aliases = len(aliases); + self.aliases_mask = get_alias_array_name(aliases) + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps) + self.num_exts = len(exts) + self.exts = get_extension_array_name(exts) + self.operands = [convert_operand_kind(o) for o in operands] + + self.fix_syntax() + + operands = [o[0] for o in operands] + self.ref_type_id = 'IdResultType' in operands + self.def_result_id = 'IdResult' in operands + + self.version = convert_min_required_version(version) + self.lastVersion = convert_max_required_version(lastVersion) + + def fix_syntax(self): + """Fix an instruction's syntax, adjusting for differences between the + officially released grammar and how SPIRV-Tools uses the grammar. + + Fixes: + - ExtInst should not end with SPV_OPERAND_VARIABLE_ID. + https://github.com/KhronosGroup/SPIRV-Tools/issues/233 + """ + if (self.opname == 'ExtInst' + and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'): + self.operands.pop() + + def __str__(self): + base_str = 'spv::Op::Op' + template = ['{{"{opname}"', base_str + '{opname}', + '{num_aliases}', '{aliases_mask}', + '{num_caps}', '{caps_mask}', + '{num_operands}', '{{{operands}}}', + '{def_result_id}', '{ref_type_id}', + '{num_exts}', '{exts}', + '{min_version}', '{max_version}}}'] + return ', '.join(template).format( + opname=self.opname, + num_aliases=self.num_aliases, + aliases_mask=self.aliases_mask, + num_caps=self.num_caps, + caps_mask=self.caps_mask, + num_operands=len(self.operands), + operands=', '.join(self.operands), + def_result_id=(1 if self.def_result_id else 0), + ref_type_id=(1 if self.ref_type_id else 0), + num_exts=self.num_exts, + exts=self.exts, + min_version=self.version, + max_version=self.lastVersion) + + +class ExtInstInitializer(object): + """Instances holds a SPIR-V extended instruction suitable for printing as + the initializer for spv_ext_inst_desc_t.""" + + def __init__(self, opname, opcode, caps, operands): + """Initialization. + + Arguments: + - opname: opcode name + - opcode: enumerant value for this opcode + - caps: a sequence of capability names required by this opcode + - operands: a sequence of (operand-kind, operand-quantifier) tuples + """ + self.opname = opname + self.opcode = opcode + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps) + self.operands = [convert_operand_kind(o) for o in operands] + self.operands.append('SPV_OPERAND_TYPE_NONE') + + def __str__(self): + template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', + '{{{operands}}}}}'] + return ', '.join(template).format( + opname=self.opname, + opcode=self.opcode, + num_caps=self.num_caps, + caps_mask=self.caps_mask, + operands=', '.join(self.operands)) + + +def generate_instruction(inst, is_ext_inst): + """Returns the C initializer for the given SPIR-V instruction. + + Arguments: + - inst: a dict containing information about a SPIR-V instruction + - is_ext_inst: a bool indicating whether |inst| is an extended + instruction. + + Returns: + a string containing the C initializer for spv_opcode_desc_t or + spv_ext_inst_desc_t + """ + opname = inst.get('opname') + opcode = inst.get('opcode') + aliases = inst.get('aliases', []) + caps = inst.get('capabilities', []) + exts = inst.get('extensions', []) + operands = inst.get('operands', {}) + operands = [(o['kind'], o.get('quantifier', '')) for o in operands] + min_version = inst.get('version', None) + max_version = inst.get('lastVersion', None) + + assert opname is not None + + if is_ext_inst: + return str(ExtInstInitializer(opname, opcode, caps, operands)) + else: + return str(InstInitializer(opname, aliases, caps, exts, operands, min_version, max_version)) + + +def generate_instruction_table(inst_table): + """Returns the info table containing all SPIR-V instructions, sorted by + opcode, and prefixed by capability arrays. + + Note: + - the built-in sorted() function is guaranteed to be stable. + https://docs.python.org/3/library/functions.html#sorted + + Arguments: + - inst_table: a list containing all SPIR-V instructions. + """ + inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname'])) + + aliases_arrays = generate_aliases_arrays( + [inst.get('aliases', []) for inst in inst_table]) + caps_arrays = generate_capability_arrays( + [inst.get('capabilities', []) for inst in inst_table]) + exts_arrays = generate_extension_arrays( + [inst.get('extensions', []) for inst in inst_table]) + + insts = [generate_instruction(inst, False) for inst in inst_table] + insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n' + ' {}\n}};'.format(',\n '.join(insts))] + + return '{}\n\n{}\n\n{}\n\n{}'.format(aliases_arrays, caps_arrays, exts_arrays, '\n'.join(insts)) + + +def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): + """Returns the info table containing all SPIR-V extended instructions, + sorted by opcode, and prefixed by capability arrays. + + Arguments: + - inst_table: a list containing all SPIR-V instructions. + - set_name: the name of the extended instruction set. + - operand_kind_prefix: the prefix, if any, to add to the front + of operand kind names. + """ + if operand_kind_prefix: + prefix_operand_kind_names(operand_kind_prefix, json_grammar) + + inst_table = json_grammar["instructions"] + set_name = set_name.replace(".", "_") + + inst_table = sorted(inst_table, key=lambda k: k['opcode']) + caps = [inst.get('capabilities', []) for inst in inst_table] + caps_arrays = generate_capability_arrays(caps) + insts = [generate_instruction(inst, True) for inst in inst_table] + insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' + ' {}\n}};'.format(set_name, ',\n '.join(insts))] + + return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) + + +class EnumerantInitializer(object): + """Prints an enumerant as the initializer for spv_operand_desc_t.""" + + def __init__(self, enumerant, value, aliases, caps, exts, parameters, version, lastVersion): + """Initialization. + + Arguments: + - enumerant: enumerant name + - value: enumerant value + - aliases: a sequence of aliased capability names + - caps: a sequence of capability names required by this enumerant + - exts: a sequence of names of extensions enabling this enumerant + - parameters: a sequence of (operand-kind, operand-quantifier) tuples + - version: minimal SPIR-V version required for this opcode + - lastVersion: last SPIR-V version this opode appears + """ + self.enumerant = enumerant + self.value = value + self.num_aliases = len(aliases) + self.aliases = get_alias_array_name(aliases) + self.num_caps = len(caps) + self.caps = get_capability_array_name(caps) + self.num_exts = len(exts) + self.exts = get_extension_array_name(exts) + self.parameters = [convert_operand_kind(p) for p in parameters] + self.version = convert_min_required_version(version) + self.lastVersion = convert_max_required_version(lastVersion) + + def __str__(self): + template = ['{{"{enumerant}"', '{value}', + '{num_aliases}', '{aliases}', + '{num_caps}', '{caps}', + '{num_exts}', '{exts}', + '{{{parameters}}}', '{min_version}', + '{max_version}}}'] + return ', '.join(template).format( + enumerant=self.enumerant, + value=self.value, + num_aliases=self.num_aliases, + aliases=self.aliases, + num_caps=self.num_caps, + caps=self.caps, + num_exts=self.num_exts, + exts=self.exts, + parameters=', '.join(self.parameters), + min_version=self.version, + max_version=self.lastVersion) + + +def generate_enum_operand_kind_entry(entry, extension_map): + """Returns the C initializer for the given operand enum entry. + + Arguments: + - entry: a dict containing information about an enum entry + - extension_map: a dict mapping enum value to list of extensions + + Returns: + a string containing the C initializer for spv_operand_desc_t + """ + enumerant = entry.get('enumerant') + value = entry.get('value') + aliases = entry.get('aliases', []) + caps = entry.get('capabilities', []) + if value in extension_map: + exts = extension_map[value] + else: + exts = [] + params = entry.get('parameters', []) + params = [p.get('kind') for p in params] + params = zip(params, [''] * len(params)) + version = entry.get('version', None) + max_version = entry.get('lastVersion', None) + + assert enumerant is not None + assert value is not None + + return str(EnumerantInitializer( + enumerant, value, aliases, caps, exts, params, version, max_version)) + + +def generate_enum_operand_kind(enum, synthetic_exts_list): + """Returns the C definition for the given operand kind. + It's a static const named array of spv_operand_desc_t. + + Also appends to |synthetic_exts_list| a list of extension lists + used. + """ + kind = enum.get('kind') + assert kind is not None + + # Sort all enumerants according to their values, but otherwise + # preserve their order so the first name listed in the grammar + # as the preferred name for disassembly. + if enum.get('category') == 'ValueEnum': + def functor(k): return (k['value']) + else: + def functor(k): return (int(k['value'], 16)) + entries = sorted(enum.get('enumerants', []), key=functor) + + # SubgroupEqMask and SubgroupEqMaskKHR are the same number with + # same semantics, but one has no extension list while the other + # does. Both should have the extension list. + # So create a mapping from enum value to the union of the extensions + # across all those grammar entries. Preserve order. + extension_map = {} + for e in entries: + value = e.get('value') + extension_map[value] = [] + for e in entries: + value = e.get('value') + exts = e.get('extensions', []) + for ext in exts: + if ext not in extension_map[value]: + extension_map[value].append(ext) + synthetic_exts_list.extend(extension_map.values()) + + name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind) + entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map)) + for e in entries] + if len(entries) == 0: + # Insert a dummy entry. Otherwise the array is empty and compilation + # will fail in MSVC. + entries = [' {"place holder", 0, 0, nullptr, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(999,0), 0}'] + + template = ['static const spv_operand_desc_t {name}[] = {{', + '{entries}', '}};'] + entries = '\n'.join(template).format( + name=name, + entries=',\n'.join(entries)) + + return kind, name, entries + + +def generate_operand_kind_table(enums): + """Returns the info table containing all SPIR-V operand kinds.""" + # We only need to output info tables for those operand kinds that are enums. + enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] + + aliases = [entry.get('aliases', []) + for enum in enums + for entry in enum.get('enumerants', [])] + aliases_arrays = generate_aliases_arrays(aliases) + + caps = [entry.get('capabilities', []) + for enum in enums + for entry in enum.get('enumerants', [])] + caps_arrays = generate_capability_arrays(caps) + + exts = [entry.get('extensions', []) + for enum in enums + for entry in enum.get('enumerants', [])] + enums = [generate_enum_operand_kind(e, exts) for e in enums] + exts_arrays = generate_extension_arrays(exts) + + # We have a few operand kinds that require their optional counterpart to + # exist in the operand info table. + optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat', 'CooperativeMatrixOperands', 'MatrixMultiplyAccumulateOperands', 'RawAccessChainOperands', 'FPEncoding'] + optional_enums = [e for e in enums if e[0] in optional_enums] + enums.extend(optional_enums) + + enum_kinds, enum_names, enum_entries = zip(*enums) + # Mark the last few as optional ones. + enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums) + # And we don't want redefinition of them. + enum_entries = enum_entries[:-len(optional_enums)] + enum_kinds = [convert_operand_kind(e) + for e in zip(enum_kinds, enum_quantifiers)] + table_entries = zip(enum_kinds, enum_names, enum_names) + table_entries = [' {{{}, ARRAY_SIZE({}), {}}}'.format(*e) + for e in table_entries] + + template = [ + 'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{', + '{enums}', '}};'] + table = '\n'.join(template).format( + p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries)) + + return '\n\n'.join((aliases_arrays,) + (caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) + + +def get_extension_list(instructions, operand_kinds): + """Returns extensions as an alphabetically sorted list of strings.""" + + things_with_an_extensions_field = [item for item in instructions] + + enumerants = sum([item.get('enumerants', []) + for item in operand_kinds], []) + + things_with_an_extensions_field.extend(enumerants) + + extensions = sum([item.get('extensions', []) + for item in things_with_an_extensions_field + if item.get('extensions')], []) + + for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): + # If it's already listed in a grammar, then don't put it in the + # special exceptions list. + assert item not in extensions, 'Extension %s is already in a grammar file' % item + + extensions.extend( + EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) + + # Validator would ignore type declaration unique check. Should only be used + # for legacy autogenerated test files containing multiple instances of the + # same type declaration, if fixing the test by other methods is too + # difficult. Shouldn't be used for any other reasons. + extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') + + return sorted(set(extensions)) + + +def get_capabilities(operand_kinds): + """Returns capabilities as a list of JSON objects, in order of + appearance.""" + enumerants = sum([item.get('enumerants', []) for item in operand_kinds + if item.get('kind') in ['Capability']], []) + return enumerants + + +def generate_extension_enum(extensions): + """Returns enumeration containing extensions declared in the grammar.""" + return ',\n'.join(['k' + extension for extension in extensions]) + + +def generate_extension_to_string_mapping(extensions): + """Returns mapping function from extensions to corresponding strings.""" + function = 'const char* ExtensionToString(Extension extension) {\n' + function += ' switch (extension) {\n' + template = ' case Extension::k{extension}:\n' \ + ' return "{extension}";\n' + function += ''.join([template.format(extension=extension) + for extension in extensions]) + function += ' }\n\n return "";\n}' + return function + + +def generate_string_to_extension_mapping(extensions): + """Returns mapping function from strings to corresponding extensions.""" + + function = ''' + bool GetExtensionFromString(const char* str, Extension* extension) {{ + static const char* known_ext_strs[] = {{ {strs} }}; + static const Extension known_ext_ids[] = {{ {ids} }}; + const auto b = std::begin(known_ext_strs); + const auto e = std::end(known_ext_strs); + const auto found = std::equal_range( + b, e, str, [](const char* str1, const char* str2) {{ + return std::strcmp(str1, str2) < 0; + }}); + if (found.first == e || found.first == found.second) return false; + + *extension = known_ext_ids[found.first - b]; + return true; + }} + '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]), + ids=', '.join(['Extension::k{}'.format(e) for e in extensions])) + + return function + + +def generate_capability_to_string_mapping(operand_kinds): + """Returns mapping function from capabilities to corresponding strings. + + We take care to avoid emitting duplicate values. + """ + cap_str = 'spv::Capability' + cap_join = '::' + + function = 'const char* CapabilityToString(' + cap_str + ' capability) {\n' + function += ' switch (capability) {\n' + template = ' case ' + cap_str + cap_join + '{capability}:\n' \ + ' return "{capability}";\n' + emitted = set() # The values of capabilities we already have emitted + for capability in get_capabilities(operand_kinds): + value = capability.get('value') + if value not in emitted: + emitted.add(value) + function += template.format(capability=capability.get('enumerant')) + function += ' case ' + cap_str + cap_join + 'Max:\n' \ + ' assert(0 && "Attempting to convert ' + cap_str + cap_join + 'Max to string");\n' \ + ' return "";\n' + function += ' }\n\n return "";\n}' + return function + + +def generate_all_string_enum_mappings(extensions, operand_kinds): + """Returns all string-to-enum / enum-to-string mapping tables.""" + tables = [] + tables.append(generate_extension_to_string_mapping(extensions)) + tables.append(generate_string_to_extension_mapping(extensions)) + tables.append(generate_capability_to_string_mapping(operand_kinds)) + return '\n\n'.join(tables) + + +def precondition_operand_kinds(operand_kinds): + """For operand kinds that have the same number, make sure they all have the + same extension list.""" + + # Map operand kind and value to list of the union of extensions + # for same-valued enumerants. + exts = {} + for kind_entry in operand_kinds: + kind = kind_entry.get('kind') + for enum_entry in kind_entry.get('enumerants', []): + value = enum_entry.get('value') + key = kind + '.' + str(value) + if key in exts: + exts[key].extend(enum_entry.get('extensions', [])) + else: + exts[key] = enum_entry.get('extensions', []) + exts[key] = sorted(set(exts[key])) + + # Now make each entry the same list. + for kind_entry in operand_kinds: + kind = kind_entry.get('kind') + for enum_entry in kind_entry.get('enumerants', []): + value = enum_entry.get('value') + key = kind + '.' + str(value) + if len(exts[key]) > 0: + enum_entry['extensions'] = exts[key] + + return operand_kinds + + +def prefix_operand_kind_names(prefix, json_dict): + """Modifies json_dict, by prefixing all the operand kind names + with the given prefix. Also modifies their uses in the instructions + to match. + """ + + old_to_new = {} + for operand_kind in json_dict["operand_kinds"]: + old_name = operand_kind["kind"] + new_name = prefix + old_name + operand_kind["kind"] = new_name + old_to_new[old_name] = new_name + + for instruction in json_dict["instructions"]: + for operand in instruction.get("operands", []): + replacement = old_to_new.get(operand["kind"]) + if replacement is not None: + operand["kind"] = replacement + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + + parser.add_argument('--spirv-core-grammar', metavar='', + type=str, required=False, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst-debuginfo-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for DebugInfo extended ' + 'instruction set') + parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenCL.DebugInfo.100 ' + 'extended instruction set') + parser.add_argument('--extinst-glsl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for GLSL extended ' + 'instruction set') + parser.add_argument('--extinst-opencl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenCL extended ' + 'instruction set') + + parser.add_argument('--core-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for core SPIR-V instructions') + parser.add_argument('--glsl-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for GLSL extended instruction set') + parser.add_argument('--opencl-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for OpenCL extended instruction set') + parser.add_argument('--operand-kinds-output', metavar='', + type=str, required=False, default=None, + help='output file for operand kinds') + parser.add_argument('--extension-enum-output', metavar='', + type=str, required=False, default=None, + help='output file for extension enumeration') + parser.add_argument('--enum-string-mapping-output', metavar='', + type=str, required=False, default=None, + help='output file for enum-string mappings') + parser.add_argument('--extinst-vendor-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for vendor extended ' + 'instruction set'), + parser.add_argument('--vendor-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for vendor extended instruction set') + parser.add_argument('--vendor-operand-kind-prefix', metavar='', + type=str, required=False, default=None, + help='prefix for operand kinds (to disambiguate operand type enums)') + args = parser.parse_args() + + # The GN build system needs this because it doesn't handle quoting + # empty string arguments well. + if args.vendor_operand_kind_prefix == "...nil...": + args.vendor_operand_kind_prefix = "" + + if (args.core_insts_output is None) != \ + (args.operand_kinds_output is None): + print('error: --core-insts-output and --operand-kinds-output ' + 'should be specified together.') + exit(1) + if args.operand_kinds_output and not (args.spirv_core_grammar and + args.extinst_debuginfo_grammar and + args.extinst_cldebuginfo100_grammar): + print('error: --operand-kinds-output requires --spirv-core-grammar ' + 'and --extinst-debuginfo-grammar ' + 'and --extinst-cldebuginfo100-grammar') + exit(1) + if (args.glsl_insts_output is None) != \ + (args.extinst_glsl_grammar is None): + print('error: --glsl-insts-output and --extinst-glsl-grammar ' + 'should be specified together.') + exit(1) + if (args.opencl_insts_output is None) != \ + (args.extinst_opencl_grammar is None): + print('error: --opencl-insts-output and --extinst-opencl-grammar ' + 'should be specified together.') + exit(1) + if (args.vendor_insts_output is None) != \ + (args.extinst_vendor_grammar is None): + print('error: --vendor-insts-output and ' + '--extinst-vendor-grammar should be specified together.') + exit(1) + if all([args.core_insts_output is None, + args.glsl_insts_output is None, + args.opencl_insts_output is None, + args.vendor_insts_output is None, + args.extension_enum_output is None, + args.enum_string_mapping_output is None]): + print('error: at least one output should be specified.') + exit(1) + + if args.spirv_core_grammar is not None: + with open(args.spirv_core_grammar) as json_file: + core_grammar = json.loads(json_file.read()) + with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: + debuginfo_grammar = json.loads(debuginfo_json_file.read()) + with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: + cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) + prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) + instructions = [] + instructions.extend(core_grammar['instructions']) + instructions.extend(debuginfo_grammar['instructions']) + instructions.extend(cldebuginfo100_grammar['instructions']) + operand_kinds = [] + operand_kinds.extend(core_grammar['operand_kinds']) + operand_kinds.extend(debuginfo_grammar['operand_kinds']) + operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) + extensions = get_extension_list(instructions, operand_kinds) + operand_kinds = precondition_operand_kinds(operand_kinds) + if args.core_insts_output is not None: + make_path_to_file(args.core_insts_output) + make_path_to_file(args.operand_kinds_output) + with open(args.core_insts_output, 'w') as f: + f.write(generate_instruction_table( + core_grammar['instructions'])) + with open(args.operand_kinds_output, 'w') as f: + f.write(generate_operand_kind_table(operand_kinds)) + if args.extension_enum_output is not None: + make_path_to_file(args.extension_enum_output) + with open(args.extension_enum_output, 'w') as f: + f.write(generate_extension_enum(extensions)) + if args.enum_string_mapping_output is not None: + make_path_to_file(args.enum_string_mapping_output) + with open(args.enum_string_mapping_output, 'w') as f: + f.write(generate_all_string_enum_mappings( + extensions, operand_kinds)) + + if args.extinst_glsl_grammar is not None: + with open(args.extinst_glsl_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.glsl_insts_output) + with open(args.glsl_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, 'glsl')) + + if args.extinst_opencl_grammar is not None: + with open(args.extinst_opencl_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.opencl_insts_output) + with open(args.opencl_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, 'opencl')) + + if args.extinst_vendor_grammar is not None: + with open(args.extinst_vendor_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.vendor_insts_output) + name = args.extinst_vendor_grammar + start = name.find('extinst.') + len('extinst.') + name = name[start:-len('.grammar.json')].replace('-', '_') + with open(args.vendor_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, name, args.vendor_operand_kind_prefix)) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/generate_language_headers.py b/engine/engine/3rdlibs/spirv-tools/utils/generate_language_headers.py new file mode 100644 index 00000000..18a8d5ea --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/generate_language_headers.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates language headers from a JSON grammar file""" + +import errno +import json +import os.path +import re + + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they + don't yet exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + +class ExtInstGrammar: + """The grammar for an extended instruction set""" + + def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None): + self.name = name + self.copyright = copyright + self.instructions = instructions + self.operand_kinds = operand_kinds + self.version = version + self.revision = revision + + +class LangGenerator: + """A language-specific generator""" + + def __init__(self): + self.upper_case_initial = re.compile('^[A-Z]') + pass + + def comment_prefix(self): + return "" + + def namespace_prefix(self): + return "" + + def uses_guards(self): + return False + + def cpp_guard_preamble(self): + return "" + + def cpp_guard_postamble(self): + return "" + + def enum_value(self, prefix, name, value): + if self.upper_case_initial.match(name): + use_name = name + else: + use_name = '_' + name + + return " {}{} = {},".format(prefix, use_name, value) + + def generate(self, grammar): + """Returns a string that is the language-specific header for the given grammar""" + + parts = [] + if grammar.copyright: + parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright]) + parts.append('') + + guard = 'SPIRV_EXTINST_{}_H_'.format(grammar.name) + if self.uses_guards: + parts.append('#ifndef {}'.format(guard)) + parts.append('#define {}'.format(guard)) + parts.append('') + + parts.append(self.cpp_guard_preamble()) + + if grammar.version: + parts.append(self.const_definition(grammar.name, 'Version', grammar.version)) + + if grammar.revision is not None: + parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision)) + + parts.append('') + + if grammar.instructions: + parts.append(self.enum_prefix(grammar.name, 'Instructions')) + for inst in grammar.instructions: + parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode'])) + parts.append(self.enum_end(grammar.name, 'Instructions')) + parts.append('') + + if grammar.operand_kinds: + for kind in grammar.operand_kinds: + parts.append(self.enum_prefix(grammar.name, kind['kind'])) + for e in kind['enumerants']: + parts.append(self.enum_value(grammar.name, e['enumerant'], e['value'])) + parts.append(self.enum_end(grammar.name, kind['kind'])) + parts.append('') + + parts.append(self.cpp_guard_postamble()) + + if self.uses_guards: + parts.append('#endif // {}'.format(guard)) + + return '\n'.join(parts) + + +class CLikeGenerator(LangGenerator): + def uses_guards(self): + return True + + def comment_prefix(self): + return "// " + + def const_definition(self, prefix, var, value): + # Use an anonymous enum. Don't use a static const int variable because + # that can bloat binary size. + return 'enum {0} {1}{2} = {3}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format( + '{', prefix, var, value, '}') + + def enum_prefix(self, prefix, name): + return 'enum {}{} {}'.format(prefix, name, '{') + + def enum_end(self, prefix, enum): + return ' {}{}Max = 0x7ffffff\n{};\n'.format(prefix, enum, '}') + + def cpp_guard_preamble(self): + return '#ifdef __cplusplus\nextern "C" {\n#endif\n' + + def cpp_guard_postamble(self): + return '#ifdef __cplusplus\n}\n#endif\n' + + +class CGenerator(CLikeGenerator): + pass + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') + + parser.add_argument('--extinst-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for extended instruction set') + parser.add_argument('--extinst-output-path', metavar='', + type=str, required=True, + help='Path of the language-specific output file.') + args = parser.parse_args() + + with open(args.extinst_grammar) as json_file: + grammar_json = json.loads(json_file.read()) + grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0] + grammar = ExtInstGrammar(name = grammar_name, + copyright = grammar_json['copyright'], + instructions = grammar_json['instructions'], + operand_kinds = grammar_json['operand_kinds'], + version = grammar_json['version'], + revision = grammar_json['revision']) + make_path_to_file(args.extinst_output_path) + with open(args.extinst_output_path, 'w') as f: + f.write(CGenerator().generate(grammar)) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/generate_registry_tables.py b/engine/engine/3rdlibs/spirv-tools/utils/generate_registry_tables.py new file mode 100644 index 00000000..2564f357 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/generate_registry_tables.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates the vendor tool table from the SPIR-V XML registry.""" + +import errno +import io +import os.path +import platform +from xml.etree.ElementTree import XML, XMLParser, TreeBuilder + + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist. + This is compatible with Python down to 3.0. + """ + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + +def generate_vendor_table(registry): + """Returns a list of C style initializers for the registered vendors + and their tools. + + Args: + registry: The SPIR-V XMLregistry as an xml.ElementTree + """ + + lines = [] + for ids in registry.iter('ids'): + if 'vendor' == ids.attrib['type']: + for an_id in ids.iter('id'): + value = an_id.attrib['value'] + vendor = an_id.attrib['vendor'] + if 'tool' in an_id.attrib: + tool = an_id.attrib['tool'] + vendor_tool = vendor + ' ' + tool + else: + tool = '' + vendor_tool = vendor + line = '{' + '{}, "{}", "{}", "{}"'.format(value, + vendor, + tool, + vendor_tool) + '},' + lines.append(line) + return '\n'.join(lines) + + +def main(): + import argparse + parser = argparse.ArgumentParser(description= + 'Generate tables from SPIR-V XML registry') + parser.add_argument('--xml', metavar='', + type=str, required=True, + help='SPIR-V XML Registry file') + parser.add_argument('--generator-output', metavar='', + type=str, required=True, + help='output file for SPIR-V generators table') + args = parser.parse_args() + + with io.open(args.xml, encoding='utf-8') as xml_in: + # Python3 default str to UTF-8. But Python2.7 (in case of NDK build, + # don't be fooled by the shebang) is returning a unicode string. + # So depending of the version, we need to make sure the correct + # encoding is used. + content = xml_in.read() + if platform.python_version_tuple()[0] == '2': + content = content.encode('utf-8') + parser = XMLParser(target=TreeBuilder(), encoding='utf-8') + registry = XML(content, parser=parser) + + mkdir_p(os.path.dirname(args.generator_output)) + with open(args.generator_output, 'w') as f: + f.write(generate_vendor_table(registry)) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/git-sync-deps b/engine/engine/3rdlibs/spirv-tools/utils/git-sync-deps new file mode 100644 index 00000000..21bf2bc7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/git-sync-deps @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# Copyright 2014 Google Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Parse a DEPS file and git checkout all of the dependencies. +""" + +EXTRA_HELP = """ +Environment Variables: + GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of + ['git', 'git.exe', 'git.bat'] in your default path. + + GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset, + will use the file ../DEPS relative to this script's directory. + + GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages. + +Git Config: + To disable syncing of a single repository: + cd path/to/repository + git config sync-deps.disable true + + To re-enable sync: + cd path/to/repository + git config --unset sync-deps.disable +""" + + +import argparse +import os +import re +import subprocess +import sys +import threading +from builtins import bytes + +def git_executable(): + """Find the git executable. + + Returns: + A triple: + A string suitable for passing to subprocess functions, or None. + The major version number + The minor version number + """ + envgit = os.environ.get('GIT_EXECUTABLE') + searchlist = ['git', 'git.exe', 'git.bat'] + if envgit: + searchlist.insert(0, envgit) + with open(os.devnull, 'w') as devnull: + for git in searchlist: + major=None + minor=None + try: + version_info = subprocess.check_output([git, '--version']).decode('utf-8') + match = re.search(r"^git version (\d+)\.(\d+)",version_info) + print("Using {}".format(version_info)) + if match: + major = int(match.group(1)) + minor = int(match.group(2)) + else: + continue + except (OSError,): + continue + return (git,major,minor) + return (None,0,0) + + +DEFAULT_DEPS_PATH = os.path.normpath( + os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS')) + +def get_deps_os_str(deps_file): + parsed_deps = parse_file_to_dict(deps_file) + parts = [] + if 'deps_os' in parsed_deps: + for deps_os in parsed_deps['deps_os']: + parts.append(' [{}]]'.format(deps_os)) + return "\n".join(parts) + +def looks_like_raw_commit(commit): + return re.match('^[a-f0-9]{40}$', commit) is not None + +def git_repository_sync_is_disabled(git, directory): + try: + disable = subprocess.check_output( + [git, 'config', 'sync-deps.disable'], cwd=directory) + return disable.lower().strip() in ['true', '1', 'yes', 'on'] + except subprocess.CalledProcessError: + return False + + +def is_git_toplevel(git, directory): + """Return true iff the directory is the top level of a Git repository. + + Args: + git (string) the git executable + + directory (string) the path into which the repository + is expected to be checked out. + """ + try: + toplevel = subprocess.check_output( + [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip() + return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel) + except subprocess.CalledProcessError: + return False + + +def status(directory, checkoutable): + def truncate(s, length): + return s if len(s) <= length else '...' + s[-(length - 3):] + dlen = 36 + directory = truncate(directory, dlen) + checkoutable = truncate(checkoutable, 40) + sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable)) + + +def git_checkout_to_directory(git, repo, checkoutable, directory, verbose, treeless): + """Checkout (and clone if needed) a Git repository. + + Args: + git (string) the git executable + + repo (string) the location of the repository, suitable + for passing to `git clone`. + + checkoutable (string) a tag, branch, or commit, suitable for + passing to `git checkout` + + directory (string) the path into which the repository + should be checked out. + + verbose (boolean): emit status info to stdout + + treeless (boolean): when true, clone without any trees. + + Raises an exception if any calls to git fail. + """ + if not os.path.isdir(directory): + # Use blobless or treeless checkouts for faster downloads. + # This defers some work to checkout time. + # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ + filter = ['--filter=tree:0'] if treeless else ['--filter=blob:none'] + # If the thing to check out looks like a tag (and not like a commit), + # then limit the checkout to that branch. + branch = [] if looks_like_raw_commit(checkoutable) else ['--branch={}'.format(checkoutable)] + subprocess.check_call( + [git, 'clone', '--quiet', '--single-branch'] + filter + branch + [repo, directory]) + + if not is_git_toplevel(git, directory): + # if the directory exists, but isn't a git repo, you will modify + # the parent repostory, which isn't what you want. + sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory) + return + + # Check to see if this repo is disabled. Quick return. + if git_repository_sync_is_disabled(git, directory): + sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory) + return + + with open(os.devnull, 'w') as devnull: + # If this fails, we will fetch before trying again. Don't spam user + # with error information. + if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], + cwd=directory, stderr=devnull): + # if this succeeds, skip slow `git fetch`. + if verbose: + status(directory, checkoutable) # Success. + return + + # If the repo has changed, always force use of the correct repo. + # If origin already points to repo, this is a quick no-op. + subprocess.check_call( + [git, 'remote', 'set-url', 'origin', repo], cwd=directory) + + subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory) + + subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory) + + if verbose: + status(directory, checkoutable) # Success. + + +def parse_file_to_dict(path): + dictionary = {} + contents = open(path).read() + # Need to convert Var() to vars[], so that the DEPS is actually Python. Var() + # comes from Autoroller using gclient which has a slightly different DEPS + # format. + contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents) + exec(contents, dictionary) + return dictionary + + +def git_sync_deps(deps_file_path, command_line_os_requests, verbose, treeless): + """Grab dependencies, with optional platform support. + + Args: + deps_file_path (string) Path to the DEPS file. + + command_line_os_requests (list of strings) Can be empty list. + List of strings that should each be a key in the deps_os + dictionary in the DEPS file. + + verbose (boolean): emit status info to stdout + + treeless (boolean): when true, clone as treeless instead of blobless + + Raises git Exceptions. + """ + (git,git_major,git_minor) = git_executable() + assert git + + # --filter=tree:0 is available in git 2.20 and later + if (git_major,git_minor) < (2,20): + print("disabling --treeless: git is older than v2.20") + treeless = False + + deps_file_directory = os.path.dirname(deps_file_path) + deps_file = parse_file_to_dict(deps_file_path) + dependencies = deps_file['deps'].copy() + os_specific_dependencies = deps_file.get('deps_os', dict()) + if 'all' in command_line_os_requests: + for value in list(os_specific_dependencies.values()): + dependencies.update(value) + else: + for os_name in command_line_os_requests: + # Add OS-specific dependencies + if os_name in os_specific_dependencies: + dependencies.update(os_specific_dependencies[os_name]) + for directory in dependencies: + for other_dir in dependencies: + if directory.startswith(other_dir + '/'): + raise Exception('%r is parent of %r' % (other_dir, directory)) + list_of_arg_lists = [] + for directory in sorted(dependencies): + if '@' in dependencies[directory]: + repo, checkoutable = dependencies[directory].split('@', 1) + else: + raise Exception("please specify commit or tag") + + relative_directory = os.path.join(deps_file_directory, directory) + + list_of_arg_lists.append( + (git, repo, checkoutable, relative_directory, verbose, treeless)) + + multithread(git_checkout_to_directory, list_of_arg_lists) + + for directory in deps_file.get('recursedeps', []): + recursive_path = os.path.join(deps_file_directory, directory, 'DEPS') + git_sync_deps(recursive_path, command_line_os_requests, verbose) + + +def multithread(function, list_of_arg_lists): + # for args in list_of_arg_lists: + # function(*args) + # return + threads = [] + for args in list_of_arg_lists: + thread = threading.Thread(None, function, None, args) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + + +def main(argv): + argparser = argparse.ArgumentParser( + prog = "git-sync-deps", + description = "Checkout git-based dependencies as specified by the DEPS file", + add_help=False # Because we want to print deps_os with -h option + ) + argparser.add_argument("--help", "-h", + action='store_true', + help="show this help message and exit") + argparser.add_argument("--deps", + default = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH), + help="location of the the DEPS file") + argparser.add_argument("--verbose", + default=not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)), + action='store_true', + help="be verbose: print status messages") + argparser.add_argument("--treeless", + default=False, + action='store_true', + help=""" + Clone repos without trees (--filter=tree:0). + This is the fastest option for a build machine, + when you only need a single commit. + Defers getting objects until checking out a commit. + + The default is to clone with trees but without blobs. + + Only takes effect if using git 2.20 or later. + + See https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ + """) + argparser.add_argument("os_requests",nargs="*", + help="OS requests, as keys in the deps_os dictionariy in the DEPS file") + + args = argparser.parse_args() + if args.help: + print(argparser.format_help()) + print(EXTRA_HELP) + print(get_deps_os_str(args.deps)) + return 0 + + git_sync_deps(args.deps, args.os_requests, args.verbose, args.treeless) + return 0 + + +if __name__ == '__main__': + exit(main(sys.argv[1:])) diff --git a/engine/engine/3rdlibs/spirv-tools/utils/roll_deps.sh b/engine/engine/3rdlibs/spirv-tools/utils/roll_deps.sh new file mode 100644 index 00000000..a62ebe9b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/roll_deps.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Copyright (c) 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Attempts to roll all entries in DEPS to tip-of-tree and create a commit. +# +# Depends on roll-dep from depot_tools +# (https://chromium.googlesource.com/chromium/tools/depot_tools) being in PATH. + +set -eo pipefail + +function ExitIfIsInterestingError() { + local return_code=$1 + if [[ ${return_code} -ne 0 && ${return_code} -ne 2 ]]; then + exit ${return_code} + fi + return 0 +} + + +declare -A dependency_to_branch_map +dependency_to_branch_map["external/abseil_cpp"]="origin/master" +dependency_to_branch_map["external/effcee/"]="origin/main" +dependency_to_branch_map["external/googletest/"]="origin/main" +dependency_to_branch_map["external/re2/"]="origin/main" +dependency_to_branch_map["external/spirv-headers/"]="origin/main" + +# This script assumes it's parent directory is the repo root. +repo_path=$(dirname "$0")/.. + +cd "$repo_path" + +if [[ $(git diff --stat) != '' ]]; then + echo "Working tree is dirty, commit changes before attempting to roll DEPS" + exit 1 +fi + +echo "*** Ignore messages about running 'git cl upload' ***" + +old_head=$(git rev-parse HEAD) + +set +e + +for dep in ${!dependency_to_branch_map[@]}; do + branch=${dependency_to_branch_map[$dep]} + echo "Rolling $dep" + roll-dep --ignore-dirty-tree --roll-to="${branch}" "${dep}" + ExitIfIsInterestingError $? +done diff --git a/engine/engine/3rdlibs/spirv-tools/utils/update_build_version.py b/engine/engine/3rdlibs/spirv-tools/utils/update_build_version.py new file mode 100644 index 00000000..9115cab1 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/update_build_version.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Updates an output file with version info unless the new content is the same +# as the existing content. +# +# Args: +# +# The output file will contain a line of text consisting of two C source syntax +# string literals separated by a comma: +# - The software version deduced from the given CHANGES file. +# - A longer string with the project name, the software version number, and +# git commit information for the CHANGES file's directory. The commit +# information is the content of the FORCED_BUILD_VERSION_DESCRIPTION +# environement variable is it exists, else the output of "git describe" if +# that succeeds, or "git rev-parse HEAD" if that succeeds, or otherwise a +# message containing the phrase "unknown hash". +# The string contents are escaped as necessary. + +import datetime +import errno +import os +import os.path +import re +import subprocess +import logging +import sys +import time + +# Format of the output generated by this script. Example: +# "v2023.1", "SPIRV-Tools v2023.1 0fc5526f2b01a0cc89192c10cf8bef77f1007a62, 2023-01-18T14:51:49" +OUTPUT_FORMAT = '"{version_tag}", "SPIRV-Tools {version_tag} {description}"\n' + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist.""" + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + +def command_output(cmd, directory): + """Runs a command in a directory and returns its standard output stream. + + Returns (False, None) if the command fails to launch or otherwise fails. + """ + try: + # Set shell=True on Windows so that Chromium's git.bat can be found when + # 'git' is invoked. + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=os.name == 'nt') + (stdout, _) = p.communicate() + if p.returncode != 0: + return False, None + except Exception as e: + return False, None + return p.returncode == 0, stdout + +def deduce_software_version(changes_file): + """Returns a tuple (success, software version number) parsed from the + given CHANGES file. + + Success is set to True if the software version could be deduced. + Software version is undefined if success if False. + Function expects the CHANGES file to describes most recent versions first. + """ + + # Match the first well-formed version-and-date line + # Allow trailing whitespace in the checked-out source code has + # unexpected carriage returns on a linefeed-only system such as + # Linux. + pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$') + with open(changes_file, mode='r') as f: + for line in f.readlines(): + match = pattern.match(line) + if match: + return True, match.group(1) + return False, None + + +def describe(repo_path): + """Returns a string describing the current Git HEAD version as descriptively + as possible. + + Runs 'git describe', or alternately 'git rev-parse HEAD', in directory. If + successful, returns the output; otherwise returns 'unknown hash, '.""" + + # if we're in a git repository, attempt to extract version info + success, output = command_output(["git", "rev-parse", "--show-toplevel"], repo_path) + if success: + success, output = command_output(["git", "describe", "--tags", "--match=v*", "--long"], repo_path) + if not success: + success, output = command_output(["git", "rev-parse", "HEAD"], repo_path) + + if success: + # decode() is needed here for Python3 compatibility. In Python2, + # str and bytes are the same type, but not in Python3. + # Popen.communicate() returns a bytes instance, which needs to be + # decoded into text data first in Python3. And this decode() won't + # hurt Python2. + return output.rstrip().decode() + + # This is the fallback case where git gives us no information, + # e.g. because the source tree might not be in a git tree or + # git is not available on the system. + # In this case, usually use a timestamp. However, to ensure + # reproducible builds, allow the builder to override the wall + # clock time with environment variable SOURCE_DATE_EPOCH + # containing a (presumably) fixed timestamp. + timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) + iso_date = datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc).isoformat() + return "unknown hash, {}".format(iso_date) + +def main(): + FORMAT = '%(asctime)s %(message)s' + logging.basicConfig(format="[%(asctime)s][%(levelname)-8s] %(message)s", datefmt="%H:%M:%S") + if len(sys.argv) != 3: + logging.error("usage: {} ".format(sys.argv[0])) + sys.exit(1) + + changes_file_path = os.path.realpath(sys.argv[1]) + output_file_path = sys.argv[2] + + success, version = deduce_software_version(changes_file_path) + if not success: + logging.error("Could not deduce latest release version from {}.".format(changes_file_path)) + sys.exit(1) + + repo_path = os.path.dirname(changes_file_path) + description = os.getenv("FORCED_BUILD_VERSION_DESCRIPTION", describe(repo_path)) + content = OUTPUT_FORMAT.format(version_tag=version, description=description) + + # Escape file content. + content.replace('"', '\\"') + + if os.path.isfile(output_file_path): + with open(output_file_path, 'r') as f: + if content == f.read(): + return + + mkdir_p(os.path.dirname(output_file_path)) + with open(output_file_path, 'w') as f: + f.write(content) + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vim/README.md b/engine/engine/3rdlibs/spirv-tools/utils/vim/README.md new file mode 100644 index 00000000..a73977ef --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vim/README.md @@ -0,0 +1,87 @@ +# Neovim configuration guide for SPIR-V disassembly files + +This directory holds instructions to configure Neovim for SPIR-V assembly files (`.spvasm`) + +At the end, Neovim should support: +* Syntax highlighting +* Jump to definition +* Find all references +* Symbol renaming +* Operand hover information +* Formatting +* Completion suggestions for all Opcodes and Ids + +While the instructions here are specifically for Neovim, they should translate easily to vim. + +## Dependencies + +In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies: +* [`golang 1.16+`](https://golang.org/) + +## File type detection + +Neovim's default config location is typically `~/.config/nvim` so the rest of the instructions assume that but it will need to be changed if your system is different. + +Tell neovim that `*.spvasm` files should be treated as `spvasm` filetype +```bash +echo "au BufRead,BufNewFile *.spvasm set filetype=spvasm" > ~/.config/nvim/ftdetect/spvasm.vim +``` + +## Syntax Highlighting + +### Generate the syntax highlighting file +```bash +cd +mkdir -p build && cd build +# Any platform is fine, ninja is used an as example +cmake -G Ninja .. +ninja spirv-tools-vimsyntax +``` + +### Copy the syntax file +```bash +cp spvasm.vim ~/.config/nvim/syntax/spvasm.vim +``` + +## Language Server + +### Building the LSP (masOS / Linux) + +Run `build_lsp.sh` +Copy `spirvls` and `spirv.json` to a location in `$PATH` + +```bash +cd /utils/vscode +./build_lsp.sh +sudo cp spirvls/* /usr/local/bin/ +``` + +### Building the LSP (Windows) + +TODO + +### Configuring Neovim + +Configuration will depend a lot on your installed plugins but assuming you are using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) the following should be sufficient. + +```lua +local lspconfig = require 'lspconfig' +local configs = require 'lspconfig.configs' + +if not configs.spvasm then + configs.spvasm = { + default_config = { + cmd = { 'spirvls' }, + filetypes = { 'spvasm' }, + root_dir = function(fname) + return '.' + end, + settings = {}, + }, + } +end + +lspconfig.spvasm.setup { + capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities()), +} +``` diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vim/generate_syntax.py b/engine/engine/3rdlibs/spirv-tools/utils/vim/generate_syntax.py new file mode 100644 index 00000000..1d156a0c --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vim/generate_syntax.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates Vim syntax rules for SPIR-V assembly (.spvasm) files""" + +import json + +PREAMBLE="""" Vim syntax file +" Language: spvasm +" Generated by SPIRV-Tools + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match +""" + +POSTAMBLE=""" + +syntax keyword spvasmTodo TODO FIXME contained + +syn match spvasmIdNumber /%\d\+\>/ + +" The assembler treats the leading minus sign as part of the number token. +" This applies to integers, and to floats below. +syn match spvasmNumber /-\?\<\d\+\>/ + +" Floating point literals. +" In general, C++ requires at least digit in the mantissa, and the +" floating point is optional. This applies to both the regular decimal float +" case and the hex float case. + +" First case: digits before the optional decimal, no trailing digits. +syn match spvasmFloat /-\?\d\+\.\?\(e[+-]\d\+\)\?/ +" Second case: optional digits before decimal, trailing digits +syn match spvasmFloat /-\?\d*\.\d\+\(e[+-]\d\+\)\?/ + +" First case: hex digits before the optional decimal, no trailing hex digits. +syn match spvasmFloat /-\?0[xX]\\x\+\.\?p[-+]\d\+/ +" Second case: optional hex digits before decimal, trailing hex digits +syn match spvasmFloat /-\?0[xX]\\x*\.\\x\+p[-+]\d\+/ + +syn match spvasmComment /;.*$/ contains=spvasmTodo +syn region spvasmString start=/"/ skip=/\\\\"/ end=/"/ +syn match spvasmId /%[a-zA-Z_][a-zA-Z_0-9]*/ + +" Highlight unknown constants and statements as errors +syn match spvasmError /[a-zA-Z][a-zA-Z_0-9]*/ + + +if version >= 508 || !exists("did_c_syn_inits") + if version < 508 + let did_c_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink spvasmStatement Statement + HiLink spvasmNumber Number + HiLink spvasmComment Comment + HiLink spvasmString String + HiLink spvasmFloat Float + HiLink spvasmConstant Constant + HiLink spvasmIdNumber Identifier + HiLink spvasmId Identifier + HiLink spvasmTodo Todo + + delcommand HiLink +endif + +let b:current_syntax = "spvasm" +""" + +# This list is taken from the description of OpSpecConstantOp in SPIR-V 1.1. +# TODO(dneto): Propose that this information be embedded in the grammar file. +SPEC_CONSTANT_OP_OPCODES = """ + OpSConvert, OpFConvert + OpSNegate, OpNot + OpIAdd, OpISub + OpIMul, OpUDiv, OpSDiv, OpUMod, OpSRem, OpSMod + OpShiftRightLogical, OpShiftRightArithmetic, OpShiftLeftLogical + OpBitwiseOr, OpBitwiseXor, OpBitwiseAnd + OpVectorShuffle, OpCompositeExtract, OpCompositeInsert + OpLogicalOr, OpLogicalAnd, OpLogicalNot, + OpLogicalEqual, OpLogicalNotEqual + OpSelect + OpIEqual, OpINotEqual + OpULessThan, OpSLessThan + OpUGreaterThan, OpSGreaterThan + OpULessThanEqual, OpSLessThanEqual + OpUGreaterThanEqual, OpSGreaterThanEqual + + OpQuantizeToF16 + + OpConvertFToS, OpConvertSToF + OpConvertFToU, OpConvertUToF + OpUConvert + OpConvertPtrToU, OpConvertUToPtr + OpGenericCastToPtr, OpPtrCastToGeneric + OpBitcast + OpFNegate + OpFAdd, OpFSub + OpFMul, OpFDiv + OpFRem, OpFMod + OpAccessChain, OpInBoundsAccessChain + OpPtrAccessChain, OpInBoundsPtrAccessChain""" + + +def EmitAsStatement(name): + """Emits the given name as a statement token""" + print('syn keyword spvasmStatement', name) + + +def EmitAsEnumerant(name): + """Emits the given name as an named operand token""" + print('syn keyword spvasmConstant', name) + + +def main(): + """Parses arguments, then generates the Vim syntax rules for SPIR-V assembly + on stdout.""" + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + parser.add_argument('--spirv-core-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst-glsl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for GLSL extended ' + 'instruction set') + parser.add_argument('--extinst-opencl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenGL extended ' + 'instruction set') + parser.add_argument('--extinst-debuginfo-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for DebugInfo extended ' + 'instruction set') + args = parser.parse_args() + + # Generate the syntax rules. + print(PREAMBLE) + + core = json.loads(open(args.spirv_core_grammar).read()) + print('\n" Core instructions') + for inst in core["instructions"]: + EmitAsStatement(inst['opname']) + aliases = inst.get('aliases', []) + for alias in aliases: + EmitAsStatement(alias) + print('\n" Core operand enums') + for operand_kind in core["operand_kinds"]: + if 'enumerants' in operand_kind: + for e in operand_kind['enumerants']: + EmitAsEnumerant(e['enumerant']) + aliases = e.get('aliases', []) + for a in aliases: + EmitAsEnumerant(a) + + if args.extinst_glsl_grammar is not None: + print('\n" GLSL.std.450 extended instructions') + glsl = json.loads(open(args.extinst_glsl_grammar).read()) + # These opcodes are really enumerant operands for the OpExtInst + # instruction. + for inst in glsl["instructions"]: + EmitAsEnumerant(inst['opname']) + + if args.extinst_opencl_grammar is not None: + print('\n" OpenCL.std extended instructions') + opencl = json.loads(open(args.extinst_opencl_grammar).read()) + for inst in opencl["instructions"]: + EmitAsEnumerant(inst['opname']) + + if args.extinst_debuginfo_grammar is not None: + print('\n" DebugInfo extended instructions') + debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read()) + for inst in debuginfo["instructions"]: + EmitAsEnumerant(inst['opname']) + print('\n" DebugInfo operand enums') + for operand_kind in debuginfo["operand_kinds"]: + if 'enumerants' in operand_kind: + for e in operand_kind['enumerants']: + EmitAsEnumerant(e['enumerant']) + + print('\n" OpSpecConstantOp opcodes') + for word in SPEC_CONSTANT_OP_OPCODES.split(' '): + stripped = word.strip('\n,') + if stripped != "": + # Treat as an enumerant, but without the leading "Op" + EmitAsEnumerant(stripped[2:]) + print(POSTAMBLE) + + +if __name__ == '__main__': + main() diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/.gitignore b/engine/engine/3rdlibs/spirv-tools/utils/vscode/.gitignore new file mode 100644 index 00000000..e934adfd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/.gitignore @@ -0,0 +1 @@ +cache/ diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/README.md b/engine/engine/3rdlibs/spirv-tools/utils/vscode/README.md new file mode 100644 index 00000000..9cf82b16 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/README.md @@ -0,0 +1,26 @@ +# Visual Studio Code extension for SPIR-V disassembly files + +This directory holds a Visual Studio Code language server extension for SPIR-V assembly files (`.spvasm`) + +The extension supports: +* Syntax highlighting +* Jump to definition +* Find all references +* Symbol renaming +* Operand hover information +* Formatting +* Completion suggestions for all Opcodes and Ids + +## Dependencies + +In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies: +* [`npm`](https://www.npmjs.com/) +* [`golang 1.16+`](https://golang.org/) + +## Installing (macOS / Linux) + +Run `install_vscode.sh` + +## Installing (Windows) + +Run `install_vscode.bat` diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/build_lsp.sh b/engine/engine/3rdlibs/spirv-tools/utils/vscode/build_lsp.sh new file mode 100644 index 00000000..dc92cc8b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/build_lsp.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +pushd ${ROOT_PATH} + go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json + go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go + + mkdir -p ./spirvls + cp ./spirv.json ./spirvls + + go build -o ./spirvls/spirvls ./src/langsvr.go +popd diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/extension.js b/engine/engine/3rdlibs/spirv-tools/utils/vscode/extension.js new file mode 100644 index 00000000..e7fec285 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/extension.js @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var path = require('path'); +var vscode = require('vscode'); +var langClient = require('vscode-languageclient'); + +var LanguageClient = langClient.LanguageClient; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +function activate(context) { + let serverModule = path.join(context.extensionPath, 'langsvr'); + let debugOptions = {}; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + let serverOptions = { + run: { command: serverModule, transport: langClient.stdio }, + debug: { command: serverModule, transport: langClient.stdio, options: debugOptions } + } + + // Options to control the language client + let clientOptions = { + documentSelector: ['spirv'], + synchronize: { + // Synchronize the setting section 'spirv' to the server + configurationSection: 'spirv', + // Notify the server about file changes to .spvasm files contained in the workspace + fileEvents: vscode.workspace.createFileSystemWatcher('**/*.spvasm') + } + } + + // Create the language client and start the client. + let disposable = new LanguageClient('spirv', serverOptions, clientOptions).start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + + // Set the language configuration here instead of a language configuration + // file to work around https://github.com/microsoft/vscode/issues/42649. + vscode.languages.setLanguageConfiguration("spirv", { + comments: { "lineComment": ";" }, + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + }); +} +exports.activate = activate; + +// this method is called when your extension is deactivated +function deactivate() { +} +exports.deactivate = deactivate; diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.mod b/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.mod new file mode 100644 index 00000000..ea4901a4 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.mod @@ -0,0 +1,8 @@ +module github.com/KhronosGroup/SPIRV-Tools/utils/vscode + +go 1.16 + +require ( + github.com/pkg/errors v0.9.1 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +) diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.sum b/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.sum new file mode 100644 index 00000000..328c8578 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/go.sum @@ -0,0 +1,4 @@ +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.bat b/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.bat new file mode 100644 index 00000000..aa06fa91 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.bat @@ -0,0 +1,30 @@ +@REM Copyright (c) 2019 Google Inc. +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. + +@set EXT_PATH=%userprofile%\.vscode\extensions\google.spirvls-0.0.1 +@set ROOT_PATH=%~dp0 + +go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\spirv.json.tmpl --out %ROOT_PATH%\spirv.json +go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\src\schema\schema.go.tmpl --out %ROOT_PATH%\src\schema\schema.go + +if not exist %EXT_PATH% mkdir -p %EXT_PATH% +copy %ROOT_PATH%\extension.js %EXT_PATH% +copy %ROOT_PATH%\package.json %EXT_PATH% +copy %ROOT_PATH%\spirv.json %EXT_PATH% + +go build -o %EXT_PATH%\langsvr.exe %ROOT_PATH%\src\langsvr.go + +@pushd %EXT_PATH% +call npm install +@popd diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.sh b/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.sh new file mode 100644 index 00000000..de54c623 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/install_vscode.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1 +ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +pushd ${ROOT_PATH} + go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json + go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go + + mkdir -p ${EXT_PATH} + cp ./extension.js ${EXT_PATH} + cp ./package.json ${EXT_PATH} + cp ./spirv.json ${EXT_PATH} + + go build -o ${EXT_PATH}/langsvr ./src/langsvr.go +popd + +cd ${EXT_PATH} +npm install diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/package.json b/engine/engine/3rdlibs/spirv-tools/utils/vscode/package.json new file mode 100644 index 00000000..b058ed18 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/package.json @@ -0,0 +1,39 @@ +{ + "name": "spirvls", + "description": "Language support for SPIR-V disassembly files", + "author": "Google", + "license": "Apache-2.0", + "version": "0.0.1", + "private": true, + "publisher": "Google", + "engines": { + "vscode": "^0.10.10" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "spirv", + "extensions": [ + "spvasm" + ] + } + ], + "grammars": [ + { + "language": "spirv", + "scopeName": "source.spirv", + "path": "spirv.json" + } + ] + }, + "dependencies": { + "vscode-languageclient": "~4.3.0" + }, + "activationEvents": [ + "*" + ], + "main": "./extension.js" +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json b/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json new file mode 100644 index 00000000..4ab2529d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json @@ -0,0 +1,357 @@ +{ + "scopeName": "source.spirv", + "name": "SPIR-V", + "comment": "Generated by gen-grammar.go --template=./spirv.json.tmpl --out=./spirv.json. Do not modify this file directly.", + "patterns": [ + { "include": "#BitEnum_ImageOperands" }, + { "include": "#BitEnum_FPFastMathMode" }, + { "include": "#BitEnum_SelectionControl" }, + { "include": "#BitEnum_LoopControl" }, + { "include": "#BitEnum_FunctionControl" }, + { "include": "#BitEnum_MemorySemantics" }, + { "include": "#BitEnum_MemoryAccess" }, + { "include": "#BitEnum_KernelProfilingInfo" }, + { "include": "#BitEnum_RayFlags" }, + { "include": "#BitEnum_FragmentShadingRate" }, + { "include": "#BitEnum_RawAccessChainOperands" }, + { "include": "#ValueEnum_SourceLanguage" }, + { "include": "#ValueEnum_ExecutionModel" }, + { "include": "#ValueEnum_AddressingModel" }, + { "include": "#ValueEnum_MemoryModel" }, + { "include": "#ValueEnum_ExecutionMode" }, + { "include": "#ValueEnum_StorageClass" }, + { "include": "#ValueEnum_Dim" }, + { "include": "#ValueEnum_SamplerAddressingMode" }, + { "include": "#ValueEnum_SamplerFilterMode" }, + { "include": "#ValueEnum_ImageFormat" }, + { "include": "#ValueEnum_ImageChannelOrder" }, + { "include": "#ValueEnum_ImageChannelDataType" }, + { "include": "#ValueEnum_FPRoundingMode" }, + { "include": "#ValueEnum_FPDenormMode" }, + { "include": "#ValueEnum_QuantizationModes" }, + { "include": "#ValueEnum_FPOperationMode" }, + { "include": "#ValueEnum_OverflowModes" }, + { "include": "#ValueEnum_LinkageType" }, + { "include": "#ValueEnum_AccessQualifier" }, + { "include": "#ValueEnum_HostAccessQualifier" }, + { "include": "#ValueEnum_FunctionParameterAttribute" }, + { "include": "#ValueEnum_Decoration" }, + { "include": "#ValueEnum_BuiltIn" }, + { "include": "#ValueEnum_Scope" }, + { "include": "#ValueEnum_GroupOperation" }, + { "include": "#ValueEnum_KernelEnqueueFlags" }, + { "include": "#ValueEnum_Capability" }, + { "include": "#ValueEnum_RayQueryIntersection" }, + { "include": "#ValueEnum_RayQueryCommittedIntersectionType" }, + { "include": "#ValueEnum_RayQueryCandidateIntersectionType" }, + { "include": "#ValueEnum_PackedVectorFormat" }, + { "include": "#BitEnum_CooperativeMatrixOperands" }, + { "include": "#ValueEnum_CooperativeMatrixLayout" }, + { "include": "#ValueEnum_CooperativeMatrixUse" }, + { "include": "#BitEnum_CooperativeMatrixReduce" }, + { "include": "#ValueEnum_TensorClampMode" }, + { "include": "#BitEnum_TensorAddressingOperands" }, + { "include": "#ValueEnum_InitializationModeQualifier" }, + { "include": "#ValueEnum_LoadCacheControl" }, + { "include": "#ValueEnum_StoreCacheControl" }, + { "include": "#ValueEnum_NamedMaximumNumberOfRegisters" }, + { "include": "#BitEnum_DebugInfoFlags" }, + { "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" }, + { "include": "#ValueEnum_DebugCompositeType" }, + { "include": "#ValueEnum_DebugTypeQualifier" }, + { "include": "#ValueEnum_DebugOperation" }, + { "include": "#ValueEnum_DebugImportedEntity" }, + { "include": "#opcode" }, + { "include": "#extopcode" }, + { "include": "#identifier" }, + { "include": "#number" }, + { "include": "#string" }, + { "include": "#comment" }, + { "include": "#operator" } + ], + "repository": { + "BitEnum_ImageOperands": { + "match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelVisible|NonPrivateTexel|VolatileTexel|SignExtend|ZeroExtend|Nontemporal|Offsets)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FPFastMathMode": { + "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast|AllowContract|AllowReassoc|AllowTransform)\\b", + "name": "keyword.spirv" + }, + "BitEnum_SelectionControl": { + "match": "\\b(None|Flatten|DontFlatten)\\b", + "name": "keyword.spirv" + }, + "BitEnum_LoopControl": { + "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount|InitiationIntervalINTEL|MaxConcurrencyINTEL|DependencyArrayINTEL|PipelineEnableINTEL|LoopCoalesceINTEL|MaxInterleavingINTEL|SpeculatedIterationsINTEL|NoFusionINTEL|LoopCountINTEL|MaxReinvocationDelayINTEL)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FunctionControl": { + "match": "\\b(None|Inline|DontInline|Pure|Const|OptNoneEXT)\\b", + "name": "keyword.spirv" + }, + "BitEnum_MemorySemantics": { + "match": "\\b(Relaxed|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|MakeAvailable|MakeVisible|Volatile)\\b", + "name": "keyword.spirv" + }, + "BitEnum_MemoryAccess": { + "match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerVisible|NonPrivatePointer|AliasScopeINTELMask|NoAliasINTELMask)\\b", + "name": "keyword.spirv" + }, + "BitEnum_KernelProfilingInfo": { + "match": "\\b(None|CmdExecTime)\\b", + "name": "keyword.spirv" + }, + "BitEnum_RayFlags": { + "match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR|ForceOpacityMicromap2StateEXT)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FragmentShadingRate": { + "match": "\\b(Vertical2Pixels|Vertical4Pixels|Horizontal2Pixels|Horizontal4Pixels)\\b", + "name": "keyword.spirv" + }, + "BitEnum_RawAccessChainOperands": { + "match": "\\b(None|RobustnessPerComponentNV|RobustnessPerElementNV)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SourceLanguage": { + "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL|CPP_for_OpenCL|SYCL|HERO_C|NZSL|WGSL|Slang|Zig)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ExecutionModel": { + "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationKHR|IntersectionKHR|AnyHitKHR|ClosestHitKHR|MissKHR|CallableKHR|TaskEXT|MeshEXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_AddressingModel": { + "match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_MemoryModel": { + "match": "\\b(Simple|GLSL450|OpenCL|Vulkan)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ExecutionMode": { + "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|NonCoherentColorAttachmentReadEXT|NonCoherentDepthAttachmentReadEXT|NonCoherentStencilAttachmentReadEXT|SubgroupUniformControlFlowKHR|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|EarlyAndLateFragmentTestsAMD|StencilRefReplacingEXT|CoalescingAMDX|IsApiEntryAMDX|MaxNodeRecursionAMDX|StaticNumWorkgroupsAMDX|ShaderIndexAMDX|MaxNumWorkgroupsAMDX|StencilRefUnchangedFrontAMD|StencilRefGreaterFrontAMD|StencilRefLessFrontAMD|StencilRefUnchangedBackAMD|StencilRefGreaterBackAMD|StencilRefLessBackAMD|QuadDerivativesKHR|RequireFullQuadsKHR|SharesInputWithAMDX|OutputLinesEXT|OutputPrimitivesEXT|DerivativeGroupQuadsKHR|DerivativeGroupLinearKHR|OutputTrianglesEXT|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT|SharedLocalMemorySizeINTEL|RoundingModeRTPINTEL|RoundingModeRTNINTEL|FloatingPointModeALTINTEL|FloatingPointModeIEEEINTEL|MaxWorkgroupSizeINTEL|MaxWorkDimINTEL|NoGlobalOffsetINTEL|NumSIMDWorkitemsINTEL|SchedulerTargetFmaxMhzINTEL|MaximallyReconvergesKHR|FPFastMathDefault|StreamingInterfaceINTEL|RegisterMapInterfaceINTEL|NamedBarrierCountINTEL|MaximumRegistersINTEL|MaximumRegistersIdINTEL|NamedMaximumRegistersINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_StorageClass": { + "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|TileImageEXT|NodePayloadAMDX|CallableDataKHR|IncomingCallableDataKHR|RayPayloadKHR|HitAttributeKHR|IncomingRayPayloadKHR|ShaderRecordBufferKHR|PhysicalStorageBuffer|HitObjectAttributeNV|TaskPayloadWorkgroupEXT|CodeSectionINTEL|DeviceOnlyINTEL|HostOnlyINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Dim": { + "match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData|TileImageDataEXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SamplerAddressingMode": { + "match": "\\b(None|ClampToEdge|Clamp|Repeat|RepeatMirrored)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SamplerFilterMode": { + "match": "\\b(Nearest|Linear)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageFormat": { + "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui|R64ui|R64i)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageChannelOrder": { + "match": "\\b(R|A|RG|RA|RGB|RGBA|BGRA|ARGB|Intensity|Luminance|Rx|RGx|RGBx|Depth|DepthStencil|sRGB|sRGBx|sRGBA|sBGRA|ABGR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageChannelDataType": { + "match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2|UnsignedIntRaw10EXT|UnsignedIntRaw12EXT|UnormInt2_101010EXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FPRoundingMode": { + "match": "\\b(RTE|RTZ|RTP|RTN)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FPDenormMode": { + "match": "\\b(Preserve|FlushToZero)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_QuantizationModes": { + "match": "\\b(TRN|TRN_ZERO|RND|RND_ZERO|RND_INF|RND_MIN_INF|RND_CONV|RND_CONV_ODD)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FPOperationMode": { + "match": "\\b(IEEE|ALT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_OverflowModes": { + "match": "\\b(WRAP|SAT|SAT_ZERO|SAT_SYM)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_LinkageType": { + "match": "\\b(Export|Import|LinkOnceODR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_AccessQualifier": { + "match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_HostAccessQualifier": { + "match": "\\b(NoneINTEL|ReadINTEL|WriteINTEL|ReadWriteINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FunctionParameterAttribute": { + "match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite|RuntimeAlignedINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Decoration": { + "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|WeightTextureQCOM|BlockMatchTextureQCOM|BlockMatchSamplerQCOM|ExplicitInterpAMD|NodeSharesPayloadLimitsWithAMDX|NodeMaxPayloadsAMDX|TrackFinishWritingAMDX|PayloadNodeNameAMDX|PayloadNodeBaseIndexAMDX|PayloadNodeSparseArrayAMDX|PayloadNodeArraySizeAMDX|PayloadDispatchIndirectAMDX|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveEXT|PerViewNV|PerTaskNV|PerVertexKHR|NonUniform|RestrictPointer|AliasedPointer|HitObjectShaderRecordBufferNV|BindlessSamplerNV|BindlessImageNV|BoundSamplerNV|BoundImageNV|SIMTCallINTEL|ReferencedIndirectlyINTEL|ClobberINTEL|SideEffectsINTEL|VectorComputeVariableINTEL|FuncParamIOKindINTEL|VectorComputeFunctionINTEL|StackCallINTEL|GlobalVariableOffsetINTEL|CounterBuffer|UserSemantic|UserTypeGOOGLE|FunctionRoundingModeINTEL|FunctionDenormModeINTEL|RegisterINTEL|MemoryINTEL|NumbanksINTEL|BankwidthINTEL|MaxPrivateCopiesINTEL|SinglepumpINTEL|DoublepumpINTEL|MaxReplicatesINTEL|SimpleDualPortINTEL|MergeINTEL|BankBitsINTEL|ForcePow2DepthINTEL|StridesizeINTEL|WordsizeINTEL|TrueDualPortINTEL|BurstCoalesceINTEL|CacheSizeINTEL|DontStaticallyCoalesceINTEL|PrefetchINTEL|StallEnableINTEL|FuseLoopsInFunctionINTEL|MathOpDSPModeINTEL|AliasScopeINTEL|NoAliasINTEL|InitiationIntervalINTEL|MaxConcurrencyINTEL|PipelineEnableINTEL|BufferLocationINTEL|IOPipeStorageINTEL|FunctionFloatingPointModeINTEL|SingleElementVectorINTEL|VectorComputeCallableFunctionINTEL|MediaBlockIOINTEL|StallFreeINTEL|FPMaxErrorDecorationINTEL|LatencyControlLabelINTEL|LatencyControlConstraintINTEL|ConduitKernelArgumentINTEL|RegisterMapKernelArgumentINTEL|MMHostInterfaceAddressWidthINTEL|MMHostInterfaceDataWidthINTEL|MMHostInterfaceLatencyINTEL|MMHostInterfaceReadWriteModeINTEL|MMHostInterfaceMaxBurstINTEL|MMHostInterfaceWaitRequestINTEL|StableKernelArgumentINTEL|HostAccessINTEL|InitModeINTEL|ImplementInRegisterMapINTEL|CacheControlLoadINTEL|CacheControlStoreINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_BuiltIn": { + "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|CoreIDARM|CoreCountARM|CoreMaxIDARM|WarpIDARM|WarpMaxIDARM|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|BaseVertex|BaseInstance|DrawIndex|PrimitiveShadingRateKHR|DeviceIndex|ViewIndex|ShadingRateKHR|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|RemainingRecursionLevelsAMDX|ShaderIndexAMDX|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordKHR|BaryCoordNoPerspKHR|FragSizeEXT|FragInvocationCountEXT|PrimitivePointIndicesEXT|PrimitiveLineIndicesEXT|PrimitiveTriangleIndicesEXT|CullPrimitiveEXT|LaunchIdKHR|LaunchSizeKHR|WorldRayOriginKHR|WorldRayDirectionKHR|ObjectRayOriginKHR|ObjectRayDirectionKHR|RayTminKHR|RayTmaxKHR|InstanceCustomIndexKHR|ObjectToWorldKHR|WorldToObjectKHR|HitTNV|HitKindKHR|CurrentRayTimeNV|HitTriangleVertexPositionsKHR|HitMicroTriangleVertexPositionsNV|HitMicroTriangleVertexBarycentricsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV|HitKindFrontFacingMicroTriangleNV|HitKindBackFacingMicroTriangleNV|CullMaskKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Scope": { + "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|ShaderCallKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_GroupOperation": { + "match": "\\b(Reduce|InclusiveScan|ExclusiveScan|ClusteredReduce|PartitionedReduceNV|PartitionedInclusiveScanNV|PartitionedExclusiveScanNV)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_KernelEnqueueFlags": { + "match": "\\b(NoWait|WaitKernel|WaitWorkGroup)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Capability": { + "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|UniformDecoration|CoreBuiltinsARM|TileImageColorReadAccessEXT|TileImageDepthReadAccessEXT|TileImageStencilReadAccessEXT|CooperativeMatrixLayoutsARM|FragmentShadingRateKHR|SubgroupBallotKHR|DrawParameters|WorkgroupMemoryExplicitLayoutKHR|WorkgroupMemoryExplicitLayout8BitAccessKHR|WorkgroupMemoryExplicitLayout16BitAccessKHR|SubgroupVoteKHR|StorageBuffer16BitAccess|UniformAndStorageBuffer16BitAccess|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayQueryKHR|UntypedPointersKHR|RayTraversalPrimitiveCullingKHR|RayTracingKHR|TextureSampleWeightedQCOM|TextureBoxFilterQCOM|TextureBlockMatchQCOM|TextureBlockMatch2QCOM|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|Int64ImageEXT|ShaderClockKHR|ShaderEnqueueAMDX|QuadControlKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|MeshShadingEXT|FragmentBarycentricKHR|ComputeDerivativeGroupQuadsKHR|FragmentDensityEXT|GroupNonUniformPartitionedNV|ShaderNonUniform|RuntimeDescriptorArray|InputAttachmentArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexing|UniformBufferArrayNonUniformIndexing|SampledImageArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexing|StorageImageArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexing|RayTracingPositionFetchKHR|RayTracingNV|RayTracingMotionBlurNV|VulkanMemoryModel|VulkanMemoryModelDeviceScope|PhysicalStorageBufferAddresses|ComputeDerivativeGroupLinearKHR|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocation|DisplacementMicromapNV|RayTracingOpacityMicromapEXT|ShaderInvocationReorderNV|BindlessTextureNV|RayQueryPositionFetchKHR|AtomicFloat16VectorNV|RayTracingDisplacementMicromapNV|RawAccessChainsNV|CooperativeMatrixReductionsNV|CooperativeMatrixConversionsNV|CooperativeMatrixPerElementOperationsNV|CooperativeMatrixTensorAddressingNV|CooperativeMatrixBlockLoadsNV|TensorAddressingNV|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|RoundToInfinityINTEL|FloatingPointModeINTEL|IntegerFunctions2INTEL|FunctionPointersINTEL|IndirectReferencesINTEL|AsmINTEL|AtomicFloat32MinMaxEXT|AtomicFloat64MinMaxEXT|AtomicFloat16MinMaxEXT|VectorComputeINTEL|VectorAnyINTEL|ExpectAssumeKHR|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL|VariableLengthArrayINTEL|FunctionFloatControlINTEL|FPGAMemoryAttributesINTEL|FPFastMathModeINTEL|ArbitraryPrecisionIntegersINTEL|ArbitraryPrecisionFloatingPointINTEL|UnstructuredLoopControlsINTEL|FPGALoopControlsINTEL|KernelAttributesINTEL|FPGAKernelAttributesINTEL|FPGAMemoryAccessesINTEL|FPGAClusterAttributesINTEL|LoopFuseINTEL|FPGADSPControlINTEL|MemoryAccessAliasingINTEL|FPGAInvocationPipeliningAttributesINTEL|FPGABufferLocationINTEL|ArbitraryPrecisionFixedPointINTEL|USMStorageClassesINTEL|RuntimeAlignedAttributeINTEL|IOPipesINTEL|BlockingPipesINTEL|FPGARegINTEL|DotProductInputAll|DotProductInput4x8Bit|DotProductInput4x8BitPacked|DotProduct|RayCullMaskKHR|CooperativeMatrixKHR|ReplicatedCompositesEXT|BitInstructions|GroupNonUniformRotateKHR|FloatControls2|AtomicFloat32AddEXT|AtomicFloat64AddEXT|LongCompositesINTEL|OptNoneEXT|AtomicFloat16AddEXT|DebugInfoModuleINTEL|BFloat16ConversionINTEL|SplitBarrierINTEL|ArithmeticFenceEXT|FPGAClusterAttributesV2INTEL|FPGAKernelAttributesv2INTEL|FPMaxErrorINTEL|FPGALatencyControlINTEL|FPGAArgumentInterfacesINTEL|GlobalVariableHostAccessINTEL|GlobalVariableFPGADecorationsINTEL|SubgroupBufferPrefetchINTEL|GroupUniformArithmeticKHR|MaskedGatherScatterINTEL|CacheControlsINTEL|RegisterLimitsINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryIntersection": { + "match": "\\b(RayQueryCandidateIntersectionKHR|RayQueryCommittedIntersectionKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryCommittedIntersectionType": { + "match": "\\b(RayQueryCommittedIntersectionNoneKHR|RayQueryCommittedIntersectionTriangleKHR|RayQueryCommittedIntersectionGeneratedKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryCandidateIntersectionType": { + "match": "\\b(RayQueryCandidateIntersectionTriangleKHR|RayQueryCandidateIntersectionAABBKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_PackedVectorFormat": { + "match": "\\b(PackedVectorFormat4x8Bit)\\b", + "name": "keyword.spirv" + }, + "BitEnum_CooperativeMatrixOperands": { + "match": "\\b(NoneKHR|MatrixASignedComponentsKHR|MatrixBSignedComponentsKHR|MatrixCSignedComponentsKHR|MatrixResultSignedComponentsKHR|SaturatingAccumulationKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_CooperativeMatrixLayout": { + "match": "\\b(RowMajorKHR|ColumnMajorKHR|RowBlockedInterleavedARM|ColumnBlockedInterleavedARM)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_CooperativeMatrixUse": { + "match": "\\b(MatrixAKHR|MatrixBKHR|MatrixAccumulatorKHR)\\b", + "name": "keyword.spirv" + }, + "BitEnum_CooperativeMatrixReduce": { + "match": "\\b(Row|Column|2x2)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_TensorClampMode": { + "match": "\\b(Undefined|Constant|ClampToEdge|Repeat|RepeatMirrored)\\b", + "name": "keyword.spirv" + }, + "BitEnum_TensorAddressingOperands": { + "match": "\\b(None|TensorView|DecodeFunc)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_InitializationModeQualifier": { + "match": "\\b(InitOnDeviceReprogramINTEL|InitOnDeviceResetINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_LoadCacheControl": { + "match": "\\b(UncachedINTEL|CachedINTEL|StreamingINTEL|InvalidateAfterReadINTEL|ConstCachedINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_StoreCacheControl": { + "match": "\\b(UncachedINTEL|WriteThroughINTEL|WriteBackINTEL|StreamingINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_NamedMaximumNumberOfRegisters": { + "match": "\\b(AutoINTEL)\\b", + "name": "keyword.spirv" + }, + "BitEnum_DebugInfoFlags": { + "match": "\\b(None|FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugBaseTypeAttributeEncoding": { + "match": "\\b(Unspecified|Address|Boolean|Float|Signed|SignedChar|Unsigned|UnsignedChar)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugCompositeType": { + "match": "\\b(Class|Structure|Union)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugTypeQualifier": { + "match": "\\b(ConstType|VolatileType|RestrictType|AtomicType)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugOperation": { + "match": "\\b(Deref|Plus|Minus|PlusUconst|BitPiece|Swap|Xderef|StackValue|Constu|Fragment)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugImportedEntity": { + "match": "\\b(ImportedModule|ImportedDeclaration)\\b", + "name": "keyword.spirv" + }, + "opcode": { + "match": "(Op[a-zA-Z]+)", + "name": "entity.name.function.spirv" + }, + "extopcode": { + "match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource|DebugModuleINTEL)", + "name": "entity.name.function.ext" + }, + "identifier": { + "match": "%[a-zA-Z0-9_]+", + "name": "variable.spirv" + }, + "number": { + "match": "\\b[0-9]+.?[0-9]*\\b", + "name": "constant.numeric.spirv" + }, + "comment": { + "match": ";[^\n]*", + "name": "comment.line.spirv" + }, + "operator": { + "match": "=", + "name": "keyword.operator.spirv" + }, + "string": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.spirv" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.spirv" + } + }, + "name": "string.quoted.double.spirv", + "patterns": [ { "include": "#string_escaped_char" } ] + }, + "string_escaped_char": { + "patterns": [ + { + "match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})", + "name": "constant.character.escape.spirv" + }, { + "match": "\\\\[^0-7xuUabfnrtv\\'\"]", + "name": "invalid.illegal.unknown-escape.spirv" + } + ] + } + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json.tmpl b/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json.tmpl new file mode 100644 index 00000000..f655e521 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/spirv.json.tmpl @@ -0,0 +1,72 @@ +{ + "scopeName": "source.spirv", + "name": "SPIR-V", + "comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.", + "patterns": [ +{{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} { "include": "#{{$o.Category}}_{{$o.Kind}}" }, +{{end}}{{end}} { "include": "#opcode" }, + { "include": "#extopcode" }, + { "include": "#identifier" }, + { "include": "#number" }, + { "include": "#string" }, + { "include": "#comment" }, + { "include": "#operator" } + ], + "repository": { {{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} + "{{$o.Category}}_{{$o.Kind}}": { + "match": "\\b({{OperandKindsMatch $o}})\\b", + "name": "keyword.spirv" + },{{end}}{{end}} + "opcode": { + "match": "(Op[a-zA-Z]+)", + "name": "entity.name.function.spirv" + }, + "extopcode": { + "match": "({{AllExtOpcodes}})", + "name": "entity.name.function.ext" + }, + "identifier": { + "match": "%[a-zA-Z0-9_]+", + "name": "variable.spirv" + }, + "number": { + "match": "\\b[0-9]+.?[0-9]*\\b", + "name": "constant.numeric.spirv" + }, + "comment": { + "match": ";[^\n]*", + "name": "comment.line.spirv" + }, + "operator": { + "match": "=", + "name": "keyword.operator.spirv" + }, + "string": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.spirv" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.spirv" + } + }, + "name": "string.quoted.double.spirv", + "patterns": [ { "include": "#string_escaped_char" } ] + }, + "string_escaped_char": { + "patterns": [ + { + "match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})", + "name": "constant.character.escape.spirv" + }, { + "match": "\\\\[^0-7xuUabfnrtv\\'\"]", + "name": "invalid.illegal.unknown-escape.spirv" + } + ] + } + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/grammar/grammar.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/grammar/grammar.go new file mode 100644 index 00000000..857a193e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/grammar/grammar.go @@ -0,0 +1,81 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package grammar holds the JSON type definitions for the SPIR-V grammar schema. +// +// See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html +// for more information. +package grammar + +// Root is the top-level structure of the JSON grammar. +type Root struct { + MagicNumber string `json:"magic_number"` + MajorVersion int `json:"major_version"` + MinorVersion int `json:"minor_version"` + Revision int `json:"revision"` + Instructions []Instruction `json:"instructions"` + OperandKinds []OperandKind `json:"operand_kinds"` +} + +// Instruction holds information about a specific SPIR-V instruction. +type Instruction struct { + Opname string `json:"opname"` + Class string `json:"class"` + Opcode int `json:"opcode"` + Operands []Operand `json:"operands"` +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind string `json:"kind"` + Name string `json:"name"` + Quantifier Quantifier `json:"quantifier"` +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category string `json:"category"` + Kind string `json:"kind"` + Enumerants []Enumerant `json:"enumerants"` + Bases []string `json:"bases"` +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string `json:"enumerant"` + Value interface{} `json:"value"` + Capabilities []string `json:"capabilities"` + Parameters []Parameter `json:"parameters"` + Version string `json:"version"` +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind string `json:"kind"` + Name string `json:"name"` +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/langsvr.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/langsvr.go new file mode 100644 index 00000000..56b11b2d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/langsvr.go @@ -0,0 +1,584 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// langsvr implements a Language Server for the SPIRV assembly language. +package main + +import ( + "context" + "fmt" + "io" + "log" + "os" + "path" + "sort" + "strings" + "sync" + "unicode/utf8" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/parser" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" + lsp "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/protocol" +) + +const ( + enableDebugLogging = false +) + +// rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes +// through it. +type rSpy struct { + prefix string + r io.Reader +} + +func (s rSpy) Read(p []byte) (n int, err error) { + n, err = s.r.Read(p) + log.Printf("%v %v", s.prefix, string(p[:n])) + return n, err +} + +// wSpy is a reader 'spy' that wraps an io.Writer, and logs all data that passes +// through it. +type wSpy struct { + prefix string + w io.Writer +} + +func (s wSpy) Write(p []byte) (n int, err error) { + n, err = s.w.Write(p) + log.Printf("%v %v", s.prefix, string(p)) + return n, err +} + +// main entry point. +func main() { + log.SetOutput(io.Discard) + if enableDebugLogging { + // create a log file in the executable's directory. + if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil { + defer logfile.Close() + log.SetOutput(logfile) + } + } + + log.Println("language server started") + + stream := jsonrpc2.NewHeaderStream(rSpy{"IDE", os.Stdin}, wSpy{"LS", os.Stdout}) + s := server{ + files: map[string]*file{}, + } + s.ctx, s.conn, s.client = lsp.NewServer(context.Background(), stream, &s) + if err := s.conn.Run(s.ctx); err != nil { + log.Panicln(err) + os.Exit(1) + } + + log.Println("language server stopped") +} + +type server struct { + ctx context.Context + conn *jsonrpc2.Conn + client lsp.Client + + files map[string]*file + filesMutex sync.Mutex +} + +// file represents a source file +type file struct { + fullRange parser.Range + res parser.Results +} + +// tokAt returns the parser token at the given position lp +func (f *file) tokAt(lp lsp.Position) *parser.Token { + toks := f.res.Tokens + p := parser.Position{Line: int(lp.Line) + 1, Column: int(lp.Character) + 1} + i := sort.Search(len(toks), func(i int) bool { return p.LessThan(toks[i].Range.End) }) + if i == len(toks) { + return nil + } + if toks[i].Range.Contains(p) { + return toks[i] + } + return nil +} + +func (s *server) DidChangeWorkspaceFolders(ctx context.Context, p *lsp.DidChangeWorkspaceFoldersParams) error { + log.Println("server.DidChangeWorkspaceFolders()") + return nil +} +func (s *server) Initialized(ctx context.Context, p *lsp.InitializedParams) error { + log.Println("server.Initialized()") + return nil +} +func (s *server) Exit(ctx context.Context) error { + log.Println("server.Exit()") + return nil +} +func (s *server) DidChangeConfiguration(ctx context.Context, p *lsp.DidChangeConfigurationParams) error { + log.Println("server.DidChangeConfiguration()") + return nil +} +func (s *server) DidOpen(ctx context.Context, p *lsp.DidOpenTextDocumentParams) error { + log.Println("server.DidOpen()") + return s.processFile(ctx, p.TextDocument.URI, p.TextDocument.Text) +} +func (s *server) DidChange(ctx context.Context, p *lsp.DidChangeTextDocumentParams) error { + log.Println("server.DidChange()") + return s.processFile(ctx, p.TextDocument.URI, p.ContentChanges[0].Text) +} +func (s *server) DidClose(ctx context.Context, p *lsp.DidCloseTextDocumentParams) error { + log.Println("server.DidClose()") + return nil +} +func (s *server) DidSave(ctx context.Context, p *lsp.DidSaveTextDocumentParams) error { + log.Println("server.DidSave()") + return nil +} +func (s *server) WillSave(ctx context.Context, p *lsp.WillSaveTextDocumentParams) error { + log.Println("server.WillSave()") + return nil +} +func (s *server) DidChangeWatchedFiles(ctx context.Context, p *lsp.DidChangeWatchedFilesParams) error { + log.Println("server.DidChangeWatchedFiles()") + return nil +} +func (s *server) Progress(ctx context.Context, p *lsp.ProgressParams) error { + log.Println("server.Progress()") + return nil +} +func (s *server) SetTraceNotification(ctx context.Context, p *lsp.SetTraceParams) error { + log.Println("server.SetTraceNotification()") + return nil +} +func (s *server) LogTraceNotification(ctx context.Context, p *lsp.LogTraceParams) error { + log.Println("server.LogTraceNotification()") + return nil +} +func (s *server) Implementation(ctx context.Context, p *lsp.ImplementationParams) ([]lsp.Location, error) { + log.Println("server.Implementation()") + return nil, nil +} +func (s *server) TypeDefinition(ctx context.Context, p *lsp.TypeDefinitionParams) ([]lsp.Location, error) { + log.Println("server.TypeDefinition()") + return nil, nil +} +func (s *server) DocumentColor(ctx context.Context, p *lsp.DocumentColorParams) ([]lsp.ColorInformation, error) { + log.Println("server.DocumentColor()") + return nil, nil +} +func (s *server) ColorPresentation(ctx context.Context, p *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, error) { + log.Println("server.ColorPresentation()") + return nil, nil +} +func (s *server) FoldingRange(ctx context.Context, p *lsp.FoldingRangeParams) ([]lsp.FoldingRange, error) { + log.Println("server.FoldingRange()") + return nil, nil +} +func (s *server) Declaration(ctx context.Context, p *lsp.DeclarationParams) ([]lsp.DeclarationLink, error) { + log.Println("server.Declaration()") + return nil, nil +} +func (s *server) SelectionRange(ctx context.Context, p *lsp.SelectionRangeParams) ([]lsp.SelectionRange, error) { + log.Println("server.SelectionRange()") + return nil, nil +} +func (s *server) Initialize(ctx context.Context, p *lsp.ParamInitia) (*lsp.InitializeResult, error) { + log.Println("server.Initialize()") + res := lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: lsp.TextDocumentSyncOptions{ + OpenClose: true, + Change: lsp.Full, // TODO: Implement incremental + }, + HoverProvider: true, + DefinitionProvider: true, + ReferencesProvider: true, + RenameProvider: true, + DocumentFormattingProvider: true, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{"%"}, + ResolveProvider: false, + }, + }, + } + return &res, nil +} +func (s *server) Shutdown(ctx context.Context) error { + log.Println("server.Shutdown()") + return nil +} +func (s *server) WillSaveWaitUntil(ctx context.Context, p *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, error) { + log.Println("server.WillSaveWaitUntil()") + return nil, nil +} + +func markdownOpcode(op *schema.Opcode) string { + sb := strings.Builder{} + sb.WriteString(fmt.Sprintf("**%s** (%s)\n\n", op.Opname, op.Class)) + + for idx, operand := range op.Operands { + sb.WriteString(fmt.Sprintf("Operand %d%s: ", idx, operand.Quantifier)) + sb.WriteString(fmt.Sprintf("%s (%s)\n", operand.Name, operand.Kind.Kind)) + } + + return sb.String() +} + +func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) { + log.Println("server.Completion()") + f := s.getFile(p.TextDocument.URI) + if f == nil { + return nil, fmt.Errorf("Unknown file") + } + + if p.Context.TriggerCharacter == "%" { + idents := []lsp.CompletionItem{} + for name, ident := range f.res.Identifiers { + idents = append(idents, lsp.CompletionItem{ + Label: name, + Kind: 6, + Documentation: ident.Definition.Range.Text(f.res.Lines), + }) + } + res := &lsp.CompletionList{ + IsIncomplete: false, + Items: idents, + } + + return res, nil + } + + opcodes := []lsp.CompletionItem{} + for name, opcode := range schema.Opcodes { + opcodes = append(opcodes, lsp.CompletionItem{ + Label: name, + Kind: 3, + Documentation: markdownOpcode(opcode), + }) + } + res := &lsp.CompletionList{ + IsIncomplete: false, + Items: opcodes, + } + return res, nil +} +func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) { + log.Println("server.Resolve()") + return nil, nil +} +func (s *server) Hover(ctx context.Context, p *lsp.HoverParams) (*lsp.Hover, error) { + log.Println("server.Hover()") + f := s.getFile(p.TextDocument.URI) + if f == nil { + return nil, fmt.Errorf("Unknown file") + } + + if tok := f.tokAt(p.Position); tok != nil { + sb := strings.Builder{} + switch v := f.res.Mappings[tok].(type) { + default: + sb.WriteString(fmt.Sprintf("", v)) + case *parser.Instruction: + sb.WriteString(markdownOpcode(v.Opcode)) + case *parser.Identifier: + sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines))) + case *parser.Operand: + if v.Name != "" { + sb.WriteString(strings.Trim(v.Name, `'`)) + sb.WriteString("\n\n") + } + + switch v.Kind.Category { + case schema.OperandCategoryBitEnum: + case schema.OperandCategoryValueEnum: + sb.WriteString("```\n") + sb.WriteString(strings.Trim(v.Kind.Kind, `'`)) + sb.WriteString("\n```") + case schema.OperandCategoryID: + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok && id.Definition != nil { + sb.WriteString("```\n") + sb.WriteString(id.Definition.Range.Text(f.res.Lines)) + sb.WriteString("\n```") + } + } + case schema.OperandCategoryLiteral: + case schema.OperandCategoryComposite: + } + case nil: + } + + if sb.Len() > 0 { + res := lsp.Hover{ + Contents: lsp.MarkupContent{ + Kind: "markdown", + Value: sb.String(), + }, + } + return &res, nil + } + } + + return nil, nil +} +func (s *server) SignatureHelp(ctx context.Context, p *lsp.SignatureHelpParams) (*lsp.SignatureHelp, error) { + log.Println("server.SignatureHelp()") + return nil, nil +} +func (s *server) Definition(ctx context.Context, p *lsp.DefinitionParams) ([]lsp.Location, error) { + log.Println("server.Definition()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + loc := lsp.Location{ + URI: p.TextDocument.URI, + Range: rangeToLSP(id.Definition.Range), + } + return []lsp.Location{loc}, nil + } + } + } + } + return nil, nil +} +func (s *server) References(ctx context.Context, p *lsp.ReferenceParams) ([]lsp.Location, error) { + log.Println("server.References()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + locs := make([]lsp.Location, len(id.References)) + for i, r := range id.References { + locs[i] = lsp.Location{ + URI: p.TextDocument.URI, + Range: rangeToLSP(r.Range), + } + } + return locs, nil + } + } + } + } + return nil, nil +} +func (s *server) DocumentHighlight(ctx context.Context, p *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, error) { + log.Println("server.DocumentHighlight()") + return nil, nil +} +func (s *server) DocumentSymbol(ctx context.Context, p *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, error) { + log.Println("server.DocumentSymbol()") + return nil, nil +} +func (s *server) CodeAction(ctx context.Context, p *lsp.CodeActionParams) ([]lsp.CodeAction, error) { + log.Println("server.CodeAction()") + return nil, nil +} +func (s *server) Symbol(ctx context.Context, p *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, error) { + log.Println("server.Symbol()") + return nil, nil +} +func (s *server) CodeLens(ctx context.Context, p *lsp.CodeLensParams) ([]lsp.CodeLens, error) { + log.Println("server.CodeLens()") + return nil, nil +} +func (s *server) ResolveCodeLens(ctx context.Context, p *lsp.CodeLens) (*lsp.CodeLens, error) { + log.Println("server.ResolveCodeLens()") + return nil, nil +} +func (s *server) DocumentLink(ctx context.Context, p *lsp.DocumentLinkParams) ([]lsp.DocumentLink, error) { + log.Println("server.DocumentLink()") + return nil, nil +} +func (s *server) ResolveDocumentLink(ctx context.Context, p *lsp.DocumentLink) (*lsp.DocumentLink, error) { + log.Println("server.ResolveDocumentLink()") + return nil, nil +} +func (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.Formatting()") + if f := s.getFile(p.TextDocument.URI); f != nil { + // Start by measuring the distance from the start of each line to the + // first opcode on that line. + lineInstOffsets, maxInstOffset, instOffset, curOffset := []int{}, 0, 0, -1 + for _, t := range f.res.Tokens { + curOffset++ // whitespace between tokens + switch t.Type { + case parser.Ident: + if _, isInst := schema.Opcodes[t.Text(f.res.Lines)]; isInst && instOffset == 0 { + instOffset = curOffset + continue + } + case parser.Newline: + lineInstOffsets = append(lineInstOffsets, instOffset) + if instOffset > maxInstOffset { + maxInstOffset = instOffset + } + curOffset, instOffset = -1, 0 + default: + curOffset += utf8.RuneCountInString(t.Text(f.res.Lines)) + } + } + lineInstOffsets = append(lineInstOffsets, instOffset) + + // Now rewrite each of the lines, adding padding at the start of the + // line for alignment. + sb, newline := strings.Builder{}, true + for _, t := range f.res.Tokens { + if newline { + newline = false + indent := maxInstOffset - lineInstOffsets[0] + lineInstOffsets = lineInstOffsets[1:] + switch t.Type { + case parser.Newline, parser.Comment: + default: + for s := 0; s < indent; s++ { + sb.WriteRune(' ') + } + } + } else if t.Type != parser.Newline { + sb.WriteString(" ") + } + + sb.WriteString(t.Text(f.res.Lines)) + if t.Type == parser.Newline { + newline = true + } + } + + formatted := sb.String() + + // Every good file ends with a single new line. + formatted = strings.TrimRight(formatted, "\n") + "\n" + + return []lsp.TextEdit{ + { + Range: rangeToLSP(f.fullRange), + NewText: formatted, + }, + }, nil + } + return nil, nil +} +func (s *server) RangeFormatting(ctx context.Context, p *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.RangeFormatting()") + return nil, nil +} +func (s *server) OnTypeFormatting(ctx context.Context, p *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.OnTypeFormatting()") + return nil, nil +} +func (s *server) Rename(ctx context.Context, p *lsp.RenameParams) (*lsp.WorkspaceEdit, error) { + log.Println("server.Rename()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + changes := make([]lsp.TextEdit, len(id.References)) + for i, r := range id.References { + changes[i].Range = rangeToLSP(r.Range) + changes[i].NewText = p.NewName + } + m := map[string][]lsp.TextEdit{} + m[p.TextDocument.URI] = changes + return &lsp.WorkspaceEdit{Changes: &m}, nil + } + } + } + } + return nil, nil +} +func (s *server) PrepareRename(ctx context.Context, p *lsp.PrepareRenameParams) (*lsp.Range, error) { + log.Println("server.PrepareRename()") + return nil, nil +} +func (s *server) ExecuteCommand(ctx context.Context, p *lsp.ExecuteCommandParams) (interface{}, error) { + log.Println("server.ExecuteCommand()") + return nil, nil +} + +func (s *server) processFile(ctx context.Context, uri, source string) error { + log.Println("server.DidOpen()") + res, err := parser.Parse(source) + if err != nil { + return err + } + fullRange := parser.Range{ + Start: parser.Position{Line: 1, Column: 1}, + End: parser.Position{Line: len(res.Lines), Column: utf8.RuneCountInString(res.Lines[len(res.Lines)-1]) + 1}, + } + + s.filesMutex.Lock() + s.files[uri] = &file{ + fullRange: fullRange, + res: res, + } + s.filesMutex.Unlock() + + dp := lsp.PublishDiagnosticsParams{URI: uri, Diagnostics: make([]lsp.Diagnostic, len(res.Diagnostics))} + for i, d := range res.Diagnostics { + dp.Diagnostics[i] = diagnosticToLSP(d) + } + s.client.PublishDiagnostics(ctx, &dp) + return nil +} + +func (s *server) getFile(uri string) *file { + s.filesMutex.Lock() + defer s.filesMutex.Unlock() + return s.files[uri] +} + +func diagnosticToLSP(d parser.Diagnostic) lsp.Diagnostic { + return lsp.Diagnostic{ + Range: rangeToLSP(d.Range), + Severity: severityToLSP(d.Severity), + Message: d.Message, + } +} + +func severityToLSP(s parser.Severity) lsp.DiagnosticSeverity { + switch s { + case parser.SeverityError: + return lsp.SeverityError + case parser.SeverityWarning: + return lsp.SeverityWarning + case parser.SeverityInformation: + return lsp.SeverityInformation + case parser.SeverityHint: + return lsp.SeverityHint + default: + log.Panicf("Invalid severity '%d'", int(s)) + return lsp.SeverityError + } +} + +func rangeToLSP(r parser.Range) lsp.Range { + return lsp.Range{ + Start: positionToLSP(r.Start), + End: positionToLSP(r.End), + } +} + +func positionToLSP(r parser.Position) lsp.Position { + return lsp.Position{ + Line: float64(r.Line - 1), + Character: float64(r.Column - 1), + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/LICENSE b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/README.md b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/README.md new file mode 100644 index 00000000..c78f183f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/README.md @@ -0,0 +1,5 @@ +This directory contains code forked from https://github.com/golang/tools/tree/master/internal/lsp. + +This code has been modified to remove unneeded features and dependencies. + +Submitted on behalf of a third-party: The Go Authors diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go new file mode 100644 index 00000000..20d48417 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go @@ -0,0 +1,134 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "context" +) + +// Handler is the interface used to hook into the message handling of an rpc +// connection. +type Handler interface { + // Deliver is invoked to handle incoming requests. + // If the request returns false from IsNotify then the Handler must eventually + // call Reply on the Conn with the supplied request. + // Handlers are called synchronously, they should pass the work off to a go + // routine if they are going to take a long time. + // If Deliver returns true all subsequent handlers will be invoked with + // delivered set to true, and should not attempt to deliver the message. + Deliver(ctx context.Context, r *Request, delivered bool) bool + + // Cancel is invoked for cancelled outgoing requests. + // It is okay to use the connection to send notifications, but the context will + // be in the cancelled state, so you must do it with the background context + // instead. + // If Cancel returns true all subsequent handlers will be invoked with + // cancelled set to true, and should not attempt to cancel the message. + Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool + + // Log is invoked for all messages flowing through a Conn. + // direction indicates if the message being received or sent + // id is the message id, if not set it was a notification + // elapsed is the time between a call being seen and the response, and is + // negative for anything that is not a response. + // method is the method name specified in the message + // payload is the parameters for a call or notification, and the result for a + // response + + // Request is called near the start of processing any request. + Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context + // Response is called near the start of processing any response. + Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context + // Done is called when any request is fully processed. + // For calls, this means the response has also been processed, for notifies + // this is as soon as the message has been written to the stream. + // If err is set, it implies the request failed. + Done(ctx context.Context, err error) + // Read is called with a count each time some data is read from the stream. + // The read calls are delayed until after the data has been interpreted so + // that it can be attributed to a request/response. + Read(ctx context.Context, bytes int64) context.Context + // Wrote is called each time some data is written to the stream. + Wrote(ctx context.Context, bytes int64) context.Context + // Error is called with errors that cannot be delivered through the normal + // mechanisms, for instance a failure to process a notify cannot be delivered + // back to the other party. + Error(ctx context.Context, err error) +} + +// Direction is used to indicate to a logger whether the logged message was being +// sent or received. +type Direction bool + +const ( + // Send indicates the message is outgoing. + Send = Direction(true) + // Receive indicates the message is incoming. + Receive = Direction(false) +) + +func (d Direction) String() string { + switch d { + case Send: + return "send" + case Receive: + return "receive" + default: + panic("unreachable") + } +} + +type EmptyHandler struct{} + +func (EmptyHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + return false +} + +func (EmptyHandler) Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool { + return false +} + +func (EmptyHandler) Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context { + return ctx +} + +func (EmptyHandler) Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context { + return ctx +} + +func (EmptyHandler) Done(ctx context.Context, err error) { +} + +func (EmptyHandler) Read(ctx context.Context, bytes int64) context.Context { + return ctx +} + +func (EmptyHandler) Wrote(ctx context.Context, bytes int64) context.Context { + return ctx +} + +func (EmptyHandler) Error(ctx context.Context, err error) {} + +type defaultHandler struct{ EmptyHandler } + +func (defaultHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + if delivered { + return false + } + if !r.IsNotify() { + r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method)) + } + return true +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go new file mode 100644 index 00000000..44dd2205 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go @@ -0,0 +1,416 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. +// https://www.jsonrpc.org/specification +// It is intended to be compatible with other implementations at the wire level. +package jsonrpc2 + +import ( + "context" + "encoding/json" + "fmt" + "sync" + "sync/atomic" +) + +// Conn is a JSON RPC 2 client server connection. +// Conn is bidirectional; it does not have a designated server or client end. +type Conn struct { + seq int64 // must only be accessed using atomic operations + handlers []Handler + stream Stream + err error + pendingMu sync.Mutex // protects the pending map + pending map[ID]chan *WireResponse + handlingMu sync.Mutex // protects the handling map + handling map[ID]*Request +} + +type requestState int + +const ( + requestWaiting = requestState(iota) + requestSerial + requestParallel + requestReplied + requestDone +) + +// Request is sent to a server to represent a Call or Notify operation. +type Request struct { + conn *Conn + cancel context.CancelFunc + state requestState + nextRequest chan struct{} + + // The Wire values of the request. + WireRequest +} + +// NewErrorf builds a Error struct for the supplied message and code. +// If args is not empty, message and args will be passed to Sprintf. +func NewErrorf(code int64, format string, args ...interface{}) *Error { + return &Error{ + Code: code, + Message: fmt.Sprintf(format, args...), + } +} + +// NewConn creates a new connection object around the supplied stream. +// You must call Run for the connection to be active. +func NewConn(s Stream) *Conn { + conn := &Conn{ + handlers: []Handler{defaultHandler{}}, + stream: s, + pending: make(map[ID]chan *WireResponse), + handling: make(map[ID]*Request), + } + return conn +} + +// AddHandler adds a new handler to the set the connection will invoke. +// Handlers are invoked in the reverse order of how they were added, this +// allows the most recent addition to be the first one to attempt to handle a +// message. +func (c *Conn) AddHandler(handler Handler) { + // prepend the new handlers so we use them first + c.handlers = append([]Handler{handler}, c.handlers...) +} + +// Cancel cancels a pending Call on the server side. +// The call is identified by its id. +// JSON RPC 2 does not specify a cancel message, so cancellation support is not +// directly wired in. This method allows a higher level protocol to choose how +// to propagate the cancel. +func (c *Conn) Cancel(id ID) { + c.handlingMu.Lock() + handling, found := c.handling[id] + c.handlingMu.Unlock() + if found { + handling.cancel() + } +} + +// Notify is called to send a notification request over the connection. +// It will return as soon as the notification has been sent, as no response is +// possible. +func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (err error) { + jsonParams, err := marshalToRaw(params) + if err != nil { + return fmt.Errorf("marshalling notify parameters: %v", err) + } + request := &WireRequest{ + Method: method, + Params: jsonParams, + } + data, err := json.Marshal(request) + if err != nil { + return fmt.Errorf("marshalling notify request: %v", err) + } + for _, h := range c.handlers { + ctx = h.Request(ctx, c, Send, request) + } + defer func() { + for _, h := range c.handlers { + h.Done(ctx, err) + } + }() + n, err := c.stream.Write(ctx, data) + for _, h := range c.handlers { + ctx = h.Wrote(ctx, n) + } + return err +} + +// Call sends a request over the connection and then waits for a response. +// If the response is not an error, it will be decoded into result. +// result must be of a type you an pass to json.Unmarshal. +func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) (err error) { + // generate a new request identifier + id := ID{Number: atomic.AddInt64(&c.seq, 1)} + jsonParams, err := marshalToRaw(params) + if err != nil { + return fmt.Errorf("marshalling call parameters: %v", err) + } + request := &WireRequest{ + ID: &id, + Method: method, + Params: jsonParams, + } + // marshal the request now it is complete + data, err := json.Marshal(request) + if err != nil { + return fmt.Errorf("marshalling call request: %v", err) + } + for _, h := range c.handlers { + ctx = h.Request(ctx, c, Send, request) + } + // we have to add ourselves to the pending map before we send, otherwise we + // are racing the response + rchan := make(chan *WireResponse) + c.pendingMu.Lock() + c.pending[id] = rchan + c.pendingMu.Unlock() + defer func() { + // clean up the pending response handler on the way out + c.pendingMu.Lock() + delete(c.pending, id) + c.pendingMu.Unlock() + for _, h := range c.handlers { + h.Done(ctx, err) + } + }() + // now we are ready to send + n, err := c.stream.Write(ctx, data) + for _, h := range c.handlers { + ctx = h.Wrote(ctx, n) + } + if err != nil { + // sending failed, we will never get a response, so don't leave it pending + return err + } + // now wait for the response + select { + case response := <-rchan: + for _, h := range c.handlers { + ctx = h.Response(ctx, c, Receive, response) + } + // is it an error response? + if response.Error != nil { + return response.Error + } + if result == nil || response.Result == nil { + return nil + } + if err := json.Unmarshal(*response.Result, result); err != nil { + return fmt.Errorf("unmarshalling result: %v", err) + } + return nil + case <-ctx.Done(): + // allow the handler to propagate the cancel + cancelled := false + for _, h := range c.handlers { + if h.Cancel(ctx, c, id, cancelled) { + cancelled = true + } + } + return ctx.Err() + } +} + +// Conn returns the connection that created this request. +func (r *Request) Conn() *Conn { return r.conn } + +// IsNotify returns true if this request is a notification. +func (r *Request) IsNotify() bool { + return r.ID == nil +} + +// Parallel indicates that the system is now allowed to process other requests +// in parallel with this one. +// It is safe to call any number of times, but must only be called from the +// request handling go routine. +// It is implied by both reply and by the handler returning. +func (r *Request) Parallel() { + if r.state >= requestParallel { + return + } + r.state = requestParallel + close(r.nextRequest) +} + +// Reply sends a reply to the given request. +// It is an error to call this if request was not a call. +// You must call this exactly once for any given request. +// It should only be called from the handler go routine. +// If err is set then result will be ignored. +// If the request has not yet dropped into parallel mode +// it will be before this function returns. +func (r *Request) Reply(ctx context.Context, result interface{}, err error) error { + if r.state >= requestReplied { + return fmt.Errorf("reply invoked more than once") + } + if r.IsNotify() { + return fmt.Errorf("reply not invoked with a valid call") + } + // reply ends the handling phase of a call, so if we are not yet + // parallel we should be now. The go routine is allowed to continue + // to do work after replying, which is why it is important to unlock + // the rpc system at this point. + r.Parallel() + r.state = requestReplied + + var raw *json.RawMessage + if err == nil { + raw, err = marshalToRaw(result) + } + response := &WireResponse{ + Result: raw, + ID: r.ID, + } + if err != nil { + if callErr, ok := err.(*Error); ok { + response.Error = callErr + } else { + response.Error = NewErrorf(0, "%s", err) + } + } + data, err := json.Marshal(response) + if err != nil { + return err + } + for _, h := range r.conn.handlers { + ctx = h.Response(ctx, r.conn, Send, response) + } + n, err := r.conn.stream.Write(ctx, data) + for _, h := range r.conn.handlers { + ctx = h.Wrote(ctx, n) + } + + if err != nil { + // TODO(iancottrell): if a stream write fails, we really need to shut down + // the whole stream + return err + } + return nil +} + +func (c *Conn) setHandling(r *Request, active bool) { + if r.ID == nil { + return + } + r.conn.handlingMu.Lock() + defer r.conn.handlingMu.Unlock() + if active { + r.conn.handling[*r.ID] = r + } else { + delete(r.conn.handling, *r.ID) + } +} + +// combined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type combined struct { + VersionTag VersionTag `json:"jsonrpc"` + ID *ID `json:"id,omitempty"` + Method string `json:"method"` + Params *json.RawMessage `json:"params,omitempty"` + Result *json.RawMessage `json:"result,omitempty"` + Error *Error `json:"error,omitempty"` +} + +// Run blocks until the connection is terminated, and returns any error that +// caused the termination. +// It must be called exactly once for each Conn. +// It returns only when the reader is closed or there is an error in the stream. +func (c *Conn) Run(runCtx context.Context) error { + // we need to make the next request "lock" in an unlocked state to allow + // the first incoming request to proceed. All later requests are unlocked + // by the preceding request going to parallel mode. + nextRequest := make(chan struct{}) + close(nextRequest) + for { + // get the data for a message + data, n, err := c.stream.Read(runCtx) + if err != nil { + // the stream failed, we cannot continue + return err + } + // read a combined message + msg := &combined{} + if err := json.Unmarshal(data, msg); err != nil { + // a badly formed message arrived, log it and continue + // we trust the stream to have isolated the error to just this message + for _, h := range c.handlers { + h.Error(runCtx, fmt.Errorf("unmarshal failed: %v", err)) + } + continue + } + // work out which kind of message we have + switch { + case msg.Method != "": + // if method is set it must be a request + reqCtx, cancelReq := context.WithCancel(runCtx) + thisRequest := nextRequest + nextRequest = make(chan struct{}) + req := &Request{ + conn: c, + cancel: cancelReq, + nextRequest: nextRequest, + WireRequest: WireRequest{ + VersionTag: msg.VersionTag, + Method: msg.Method, + Params: msg.Params, + ID: msg.ID, + }, + } + for _, h := range c.handlers { + reqCtx = h.Request(reqCtx, c, Receive, &req.WireRequest) + reqCtx = h.Read(reqCtx, n) + } + c.setHandling(req, true) + go func() { + <-thisRequest + req.state = requestSerial + defer func() { + c.setHandling(req, false) + if !req.IsNotify() && req.state < requestReplied { + req.Reply(reqCtx, nil, NewErrorf(CodeInternalError, "method %q did not reply", req.Method)) + } + req.Parallel() + for _, h := range c.handlers { + h.Done(reqCtx, err) + } + cancelReq() + }() + delivered := false + for _, h := range c.handlers { + if h.Deliver(reqCtx, req, delivered) { + delivered = true + } + } + }() + case msg.ID != nil: + // we have a response, get the pending entry from the map + c.pendingMu.Lock() + rchan := c.pending[*msg.ID] + if rchan != nil { + delete(c.pending, *msg.ID) + } + c.pendingMu.Unlock() + // and send the reply to the channel + response := &WireResponse{ + Result: msg.Result, + Error: msg.Error, + ID: msg.ID, + } + rchan <- response + close(rchan) + default: + for _, h := range c.handlers { + h.Error(runCtx, fmt.Errorf("message not a call, notify or response, ignoring")) + } + } + } +} + +func marshalToRaw(obj interface{}) (*json.RawMessage, error) { + data, err := json.Marshal(obj) + if err != nil { + return nil, err + } + raw := json.RawMessage(data) + return &raw, nil +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go new file mode 100644 index 00000000..2c6ad6df --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go @@ -0,0 +1,160 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "sync" +) + +// Stream abstracts the transport mechanics from the JSON RPC protocol. +// A Conn reads and writes messages using the stream it was provided on +// construction, and assumes that each call to Read or Write fully transfers +// a single message, or returns an error. +type Stream interface { + // Read gets the next message from the stream. + // It is never called concurrently. + Read(context.Context) ([]byte, int64, error) + // Write sends a message to the stream. + // It must be safe for concurrent use. + Write(context.Context, []byte) (int64, error) +} + +// NewStream returns a Stream built on top of an io.Reader and io.Writer +// The messages are sent with no wrapping, and rely on json decode consistency +// to determine message boundaries. +func NewStream(in io.Reader, out io.Writer) Stream { + return &plainStream{ + in: json.NewDecoder(in), + out: out, + } +} + +type plainStream struct { + in *json.Decoder + outMu sync.Mutex + out io.Writer +} + +func (s *plainStream) Read(ctx context.Context) ([]byte, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var raw json.RawMessage + if err := s.in.Decode(&raw); err != nil { + return nil, 0, err + } + return raw, int64(len(raw)), nil +} + +func (s *plainStream) Write(ctx context.Context, data []byte) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + s.outMu.Lock() + n, err := s.out.Write(data) + s.outMu.Unlock() + return int64(n), err +} + +// NewHeaderStream returns a Stream built on top of an io.Reader and io.Writer +// The messages are sent with HTTP content length and MIME type headers. +// This is the format used by LSP and others. +func NewHeaderStream(in io.Reader, out io.Writer) Stream { + return &headerStream{ + in: bufio.NewReader(in), + out: out, + } +} + +type headerStream struct { + in *bufio.Reader + outMu sync.Mutex + out io.Writer +} + +func (s *headerStream) Read(ctx context.Context) ([]byte, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var total, length int64 + // read the header, stop on the first empty line + for { + line, err := s.in.ReadString('\n') + total += int64(len(line)) + if err != nil { + return nil, total, fmt.Errorf("failed reading header line %q", err) + } + line = strings.TrimSpace(line) + // check we have a header line + if line == "" { + break + } + colon := strings.IndexRune(line, ':') + if colon < 0 { + return nil, total, fmt.Errorf("invalid header line %q", line) + } + name, value := line[:colon], strings.TrimSpace(line[colon+1:]) + switch name { + case "Content-Length": + if length, err = strconv.ParseInt(value, 10, 32); err != nil { + return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value) + } + if length <= 0 { + return nil, total, fmt.Errorf("invalid Content-Length: %v", length) + } + default: + // ignoring unknown headers + } + } + if length == 0 { + return nil, total, fmt.Errorf("missing Content-Length header") + } + data := make([]byte, length) + if _, err := io.ReadFull(s.in, data); err != nil { + return nil, total, err + } + total += length + return data, total, nil +} + +func (s *headerStream) Write(ctx context.Context, data []byte) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + s.outMu.Lock() + defer s.outMu.Unlock() + n, err := fmt.Fprintf(s.out, "Content-Length: %v\r\n\r\n", len(data)) + total := int64(n) + if err == nil { + n, err = s.out.Write(data) + total += int64(n) + } + return total, err +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go new file mode 100644 index 00000000..fed9a25b --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go @@ -0,0 +1,148 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// this file contains the go forms of the wire specification +// see http://www.jsonrpc.org/specification for details + +const ( + // CodeUnknownError should be used for all non coded errors. + CodeUnknownError = -32001 + // CodeParseError is used when invalid JSON was received by the server. + CodeParseError = -32700 + //CodeInvalidRequest is used when the JSON sent is not a valid Request object. + CodeInvalidRequest = -32600 + // CodeMethodNotFound should be returned by the handler when the method does + // not exist / is not available. + CodeMethodNotFound = -32601 + // CodeInvalidParams should be returned by the handler when method + // parameter(s) were invalid. + CodeInvalidParams = -32602 + // CodeInternalError is not currently returned but defined for completeness. + CodeInternalError = -32603 + + //CodeServerOverloaded is returned when a message was refused due to a + //server being temporarily unable to accept any new messages. + CodeServerOverloaded = -32000 +) + +// WireRequest is sent to a server to represent a Call or Notify operation. +type WireRequest struct { + // VersionTag is always encoded as the string "2.0" + VersionTag VersionTag `json:"jsonrpc"` + // Method is a string containing the method name to invoke. + Method string `json:"method"` + // Params is either a struct or an array with the parameters of the method. + Params *json.RawMessage `json:"params,omitempty"` + // The id of this request, used to tie the Response back to the request. + // Will be either a string or a number. If not set, the Request is a notify, + // and no response is possible. + ID *ID `json:"id,omitempty"` +} + +// WireResponse is a reply to a Request. +// It will always have the ID field set to tie it back to a request, and will +// have either the Result or Error fields set depending on whether it is a +// success or failure response. +type WireResponse struct { + // VersionTag is always encoded as the string "2.0" + VersionTag VersionTag `json:"jsonrpc"` + // Result is the response value, and is required on success. + Result *json.RawMessage `json:"result,omitempty"` + // Error is a structured error response if the call fails. + Error *Error `json:"error,omitempty"` + // ID must be set and is the identifier of the Request this is a response to. + ID *ID `json:"id,omitempty"` +} + +// Error represents a structured error in a Response. +type Error struct { + // Code is an error code indicating the type of failure. + Code int64 `json:"code"` + // Message is a short description of the error. + Message string `json:"message"` + // Data is optional structured data containing additional information about the error. + Data *json.RawMessage `json:"data"` +} + +// VersionTag is a special 0 sized struct that encodes as the jsonrpc version +// tag. +// It will fail during decode if it is not the correct version tag in the +// stream. +type VersionTag struct{} + +// ID is a Request identifier. +// Only one of either the Name or Number members will be set, using the +// number form if the Name is the empty string. +type ID struct { + Name string + Number int64 +} + +func (err *Error) Error() string { + if err == nil { + return "" + } + return err.Message +} + +func (VersionTag) MarshalJSON() ([]byte, error) { + return json.Marshal("2.0") +} + +func (VersionTag) UnmarshalJSON(data []byte) error { + version := "" + if err := json.Unmarshal(data, &version); err != nil { + return err + } + if version != "2.0" { + return fmt.Errorf("Invalid RPC version %v", version) + } + return nil +} + +// String returns a string representation of the ID. +// The representation is non ambiguous, string forms are quoted, number forms +// are preceded by a # +func (id *ID) String() string { + if id == nil { + return "" + } + if id.Name != "" { + return strconv.Quote(id.Name) + } + return "#" + strconv.FormatInt(id.Number, 10) +} + +func (id *ID) MarshalJSON() ([]byte, error) { + if id.Name != "" { + return json.Marshal(id.Name) + } + return json.Marshal(id.Number) +} + +func (id *ID) UnmarshalJSON(data []byte) error { + *id = ID{} + if err := json.Unmarshal(data, &id.Number); err == nil { + return nil + } + return json.Unmarshal(data, &id.Name) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/context.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/context.go new file mode 100644 index 00000000..7833d40e --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/context.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" +) + +type contextKey int + +const ( + clientKey = contextKey(iota) +) + +func WithClient(ctx context.Context, client Client) context.Context { + return context.WithValue(ctx, clientKey, client) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/doc.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/doc.go new file mode 100644 index 00000000..3c9198d3 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/doc.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocol contains the structs that map directly to the wire format +// of the "Language Server Protocol". +// +// It is a literal transcription, with unmodified comments, and only the changes +// required to make it go code. +// Names are uppercased to export them. +// All fields have JSON tags added to correct the names. +// Fields marked with a ? are also marked as "omitempty" +// Fields that are "|| null" are made pointers +// Fields that are string or number are left as string +// Fields that are type "number" are made float64 +package protocol diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/enums.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/enums.go new file mode 100644 index 00000000..db76ca39 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/enums.go @@ -0,0 +1,256 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "fmt" +) + +var ( + namesTextDocumentSyncKind [int(Incremental) + 1]string + namesInitializeError [int(UnknownProtocolVersion) + 1]string + namesMessageType [int(Log) + 1]string + namesFileChangeType [int(Deleted) + 1]string + namesWatchKind [int(WatchDelete) + 1]string + namesCompletionTriggerKind [int(TriggerForIncompleteCompletions) + 1]string + namesDiagnosticSeverity [int(SeverityHint) + 1]string + namesDiagnosticTag [int(Unnecessary) + 1]string + namesCompletionItemKind [int(TypeParameterCompletion) + 1]string + namesInsertTextFormat [int(SnippetTextFormat) + 1]string + namesDocumentHighlightKind [int(Write) + 1]string + namesSymbolKind [int(TypeParameter) + 1]string + namesTextDocumentSaveReason [int(FocusOut) + 1]string +) + +func init() { + namesTextDocumentSyncKind[int(None)] = "None" + namesTextDocumentSyncKind[int(Full)] = "Full" + namesTextDocumentSyncKind[int(Incremental)] = "Incremental" + + namesInitializeError[int(UnknownProtocolVersion)] = "UnknownProtocolVersion" + + namesMessageType[int(Error)] = "Error" + namesMessageType[int(Warning)] = "Warning" + namesMessageType[int(Info)] = "Info" + namesMessageType[int(Log)] = "Log" + + namesFileChangeType[int(Created)] = "Created" + namesFileChangeType[int(Changed)] = "Changed" + namesFileChangeType[int(Deleted)] = "Deleted" + + namesWatchKind[int(WatchCreate)] = "WatchCreate" + namesWatchKind[int(WatchChange)] = "WatchChange" + namesWatchKind[int(WatchDelete)] = "WatchDelete" + + namesCompletionTriggerKind[int(Invoked)] = "Invoked" + namesCompletionTriggerKind[int(TriggerCharacter)] = "TriggerCharacter" + namesCompletionTriggerKind[int(TriggerForIncompleteCompletions)] = "TriggerForIncompleteCompletions" + + namesDiagnosticSeverity[int(SeverityError)] = "Error" + namesDiagnosticSeverity[int(SeverityWarning)] = "Warning" + namesDiagnosticSeverity[int(SeverityInformation)] = "Information" + namesDiagnosticSeverity[int(SeverityHint)] = "Hint" + + namesDiagnosticTag[int(Unnecessary)] = "Unnecessary" + + namesCompletionItemKind[int(TextCompletion)] = "text" + namesCompletionItemKind[int(MethodCompletion)] = "method" + namesCompletionItemKind[int(FunctionCompletion)] = "func" + namesCompletionItemKind[int(ConstructorCompletion)] = "constructor" + namesCompletionItemKind[int(FieldCompletion)] = "field" + namesCompletionItemKind[int(VariableCompletion)] = "var" + namesCompletionItemKind[int(ClassCompletion)] = "type" + namesCompletionItemKind[int(InterfaceCompletion)] = "interface" + namesCompletionItemKind[int(ModuleCompletion)] = "package" + namesCompletionItemKind[int(PropertyCompletion)] = "property" + namesCompletionItemKind[int(UnitCompletion)] = "unit" + namesCompletionItemKind[int(ValueCompletion)] = "value" + namesCompletionItemKind[int(EnumCompletion)] = "enum" + namesCompletionItemKind[int(KeywordCompletion)] = "keyword" + namesCompletionItemKind[int(SnippetCompletion)] = "snippet" + namesCompletionItemKind[int(ColorCompletion)] = "color" + namesCompletionItemKind[int(FileCompletion)] = "file" + namesCompletionItemKind[int(ReferenceCompletion)] = "reference" + namesCompletionItemKind[int(FolderCompletion)] = "folder" + namesCompletionItemKind[int(EnumMemberCompletion)] = "enumMember" + namesCompletionItemKind[int(ConstantCompletion)] = "const" + namesCompletionItemKind[int(StructCompletion)] = "struct" + namesCompletionItemKind[int(EventCompletion)] = "event" + namesCompletionItemKind[int(OperatorCompletion)] = "operator" + namesCompletionItemKind[int(TypeParameterCompletion)] = "typeParam" + + namesInsertTextFormat[int(PlainTextTextFormat)] = "PlainText" + namesInsertTextFormat[int(SnippetTextFormat)] = "Snippet" + + namesDocumentHighlightKind[int(Text)] = "Text" + namesDocumentHighlightKind[int(Read)] = "Read" + namesDocumentHighlightKind[int(Write)] = "Write" + + namesSymbolKind[int(File)] = "File" + namesSymbolKind[int(Module)] = "Module" + namesSymbolKind[int(Namespace)] = "Namespace" + namesSymbolKind[int(Package)] = "Package" + namesSymbolKind[int(Class)] = "Class" + namesSymbolKind[int(Method)] = "Method" + namesSymbolKind[int(Property)] = "Property" + namesSymbolKind[int(Field)] = "Field" + namesSymbolKind[int(Constructor)] = "Constructor" + namesSymbolKind[int(Enum)] = "Enum" + namesSymbolKind[int(Interface)] = "Interface" + namesSymbolKind[int(Function)] = "Function" + namesSymbolKind[int(Variable)] = "Variable" + namesSymbolKind[int(Constant)] = "Constant" + namesSymbolKind[int(String)] = "String" + namesSymbolKind[int(Number)] = "Number" + namesSymbolKind[int(Boolean)] = "Boolean" + namesSymbolKind[int(Array)] = "Array" + namesSymbolKind[int(Object)] = "Object" + namesSymbolKind[int(Key)] = "Key" + namesSymbolKind[int(Null)] = "Null" + namesSymbolKind[int(EnumMember)] = "EnumMember" + namesSymbolKind[int(Struct)] = "Struct" + namesSymbolKind[int(Event)] = "Event" + namesSymbolKind[int(Operator)] = "Operator" + namesSymbolKind[int(TypeParameter)] = "TypeParameter" + + namesTextDocumentSaveReason[int(Manual)] = "Manual" + namesTextDocumentSaveReason[int(AfterDelay)] = "AfterDelay" + namesTextDocumentSaveReason[int(FocusOut)] = "FocusOut" +} + +func formatEnum(f fmt.State, c rune, i int, names []string, unknown string) { + s := "" + if i >= 0 && i < len(names) { + s = names[i] + } + if s != "" { + fmt.Fprint(f, s) + } else { + fmt.Fprintf(f, "%s(%d)", unknown, i) + } +} + +func parseEnum(s string, names []string) int { + for i, name := range names { + if s == name { + return i + } + } + return 0 +} + +func (e TextDocumentSyncKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSyncKind[:], "TextDocumentSyncKind") +} + +func ParseTextDocumentSyncKind(s string) TextDocumentSyncKind { + return TextDocumentSyncKind(parseEnum(s, namesTextDocumentSyncKind[:])) +} + +func (e InitializeError) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesInitializeError[:], "InitializeError") +} + +func ParseInitializeError(s string) InitializeError { + return InitializeError(parseEnum(s, namesInitializeError[:])) +} + +func (e MessageType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesMessageType[:], "MessageType") +} + +func ParseMessageType(s string) MessageType { + return MessageType(parseEnum(s, namesMessageType[:])) +} + +func (e FileChangeType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesFileChangeType[:], "FileChangeType") +} + +func ParseFileChangeType(s string) FileChangeType { + return FileChangeType(parseEnum(s, namesFileChangeType[:])) +} + +func (e WatchKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesWatchKind[:], "WatchKind") +} + +func ParseWatchKind(s string) WatchKind { + return WatchKind(parseEnum(s, namesWatchKind[:])) +} + +func (e CompletionTriggerKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionTriggerKind[:], "CompletionTriggerKind") +} + +func ParseCompletionTriggerKind(s string) CompletionTriggerKind { + return CompletionTriggerKind(parseEnum(s, namesCompletionTriggerKind[:])) +} + +func (e DiagnosticSeverity) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticSeverity[:], "DiagnosticSeverity") +} + +func ParseDiagnosticSeverity(s string) DiagnosticSeverity { + return DiagnosticSeverity(parseEnum(s, namesDiagnosticSeverity[:])) +} + +func (e DiagnosticTag) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticTag[:], "DiagnosticTag") +} + +func ParseDiagnosticTag(s string) DiagnosticTag { + return DiagnosticTag(parseEnum(s, namesDiagnosticTag[:])) +} + +func (e CompletionItemKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionItemKind[:], "CompletionItemKind") +} + +func ParseCompletionItemKind(s string) CompletionItemKind { + return CompletionItemKind(parseEnum(s, namesCompletionItemKind[:])) +} + +func (e InsertTextFormat) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesInsertTextFormat[:], "InsertTextFormat") +} + +func ParseInsertTextFormat(s string) InsertTextFormat { + return InsertTextFormat(parseEnum(s, namesInsertTextFormat[:])) +} + +func (e DocumentHighlightKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDocumentHighlightKind[:], "DocumentHighlightKind") +} + +func ParseDocumentHighlightKind(s string) DocumentHighlightKind { + return DocumentHighlightKind(parseEnum(s, namesDocumentHighlightKind[:])) +} + +func (e SymbolKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesSymbolKind[:], "SymbolKind") +} + +func ParseSymbolKind(s string) SymbolKind { + return SymbolKind(parseEnum(s, namesSymbolKind[:])) +} + +func (e TextDocumentSaveReason) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSaveReason[:], "TextDocumentSaveReason") +} + +func ParseTextDocumentSaveReason(s string) TextDocumentSaveReason { + return TextDocumentSaveReason(parseEnum(s, namesTextDocumentSaveReason[:])) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/log.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/log.go new file mode 100644 index 00000000..2fd7bbbd --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/log.go @@ -0,0 +1,258 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + "sync" + "time" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" +) + +type loggingStream struct { + stream jsonrpc2.Stream + log io.Writer +} + +// LoggingStream returns a stream that does LSP protocol logging too +func LoggingStream(str jsonrpc2.Stream, w io.Writer) jsonrpc2.Stream { + return &loggingStream{str, w} +} + +func (s *loggingStream) Read(ctx context.Context) ([]byte, int64, error) { + data, count, err := s.stream.Read(ctx) + if err == nil { + logIn(s.log, data) + } + return data, count, err +} + +func (s *loggingStream) Write(ctx context.Context, data []byte) (int64, error) { + logOut(s.log, data) + count, err := s.stream.Write(ctx, data) + return count, err +} + +// Combined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type Combined struct { + VersionTag jsonrpc2.VersionTag `json:"jsonrpc"` + ID *jsonrpc2.ID `json:"id,omitempty"` + Method string `json:"method"` + Params *json.RawMessage `json:"params,omitempty"` + Result *json.RawMessage `json:"result,omitempty"` + Error *jsonrpc2.Error `json:"error,omitempty"` +} + +type req struct { + method string + start time.Time +} + +type mapped struct { + mu sync.Mutex + clientCalls map[string]req + serverCalls map[string]req +} + +var maps = &mapped{ + sync.Mutex{}, + make(map[string]req), + make(map[string]req), +} + +// these 4 methods are each used exactly once, but it seemed +// better to have the encapsulation rather than ad hoc mutex +// code in 4 places +func (m *mapped) client(id string, del bool) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.clientCalls[id] + if del { + delete(m.clientCalls, id) + } + return v +} + +func (m *mapped) server(id string, del bool) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.serverCalls[id] + if del { + delete(m.serverCalls, id) + } + return v +} + +func (m *mapped) setClient(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.clientCalls[id] = r +} + +func (m *mapped) setServer(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.serverCalls[id] = r +} + +const eor = "\r\n\r\n\r\n" + +func strID(x *jsonrpc2.ID) string { + if x == nil { + // should never happen, but we need a number + return "999999999" + } + if x.Name != "" { + return x.Name + } + return fmt.Sprintf("%d", x.Number) +} + +func logCommon(outfd io.Writer, data []byte) (*Combined, time.Time, string) { + if outfd == nil { + return nil, time.Time{}, "" + } + var v Combined + err := json.Unmarshal(data, &v) + if err != nil { + fmt.Fprintf(outfd, "Unmarshal %v\n", err) + panic(err) // do better + } + tm := time.Now() + tmfmt := tm.Format("15:04:05.000 PM") + return &v, tm, tmfmt +} + +// logOut and logIn could be combined. "received"<->"Sending", serverCalls<->clientCalls +// but it wouldn't be a lot shorter or clearer and "shutdown" is a special case + +// Writing a message to the client, log it +func logOut(outfd io.Writer, data []byte) { + v, tm, tmfmt := logCommon(outfd, data) + if v == nil { + return + } + if v.Error != nil { + id := strID(v.ID) + fmt.Fprintf(outfd, "[Error - %s] Received #%s %s%s", tmfmt, id, v.Error, eor) + return + } + buf := strings.Builder{} + id := strID(v.ID) + fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning + if v.ID != nil && v.Method != "" && v.Params != nil { + fmt.Fprintf(&buf, "Received request '%s - (%s)'.\n", v.Method, id) + fmt.Fprintf(&buf, "Params: %s%s", *v.Params, eor) + maps.setServer(id, req{method: v.Method, start: tm}) + } else if v.ID != nil && v.Method == "" && v.Params == nil { + cc := maps.client(id, true) + elapsed := tm.Sub(cc.start) + fmt.Fprintf(&buf, "Received response '%s - (%s)' in %dms.\n", + cc.method, id, elapsed/time.Millisecond) + if v.Result == nil { + fmt.Fprintf(&buf, "Result: {}%s", eor) + } else { + fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) + } + } else if v.ID == nil && v.Method != "" && v.Params != nil { + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + fmt.Fprintf(&buf, "Received notification '%s'.\n", v.Method) + fmt.Fprintf(&buf, "Params: %s%s", p, eor) + } else { // for completeness, as it should never happen + buf = strings.Builder{} // undo common Trace + fmt.Fprintf(&buf, "[Error - %s] on write ID?%v method:%q Params:%v Result:%v Error:%v%s", + tmfmt, v.ID != nil, v.Method, v.Params != nil, + v.Result != nil, v.Error != nil, eor) + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + r := "null" + if v.Result != nil { + r = string(*v.Result) + } + fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor) + } + outfd.Write([]byte(buf.String())) +} + +// Got a message from the client, log it +func logIn(outfd io.Writer, data []byte) { + v, tm, tmfmt := logCommon(outfd, data) + if v == nil { + return + } + // ID Method Params => Sending request + // ID !Method Result(might be null, but !Params) => Sending response (could we get an Error?) + // !ID Method Params => Sending notification + if v.Error != nil { // does this ever happen? + id := strID(v.ID) + fmt.Fprintf(outfd, "[Error - %s] Sent #%s %s%s", tmfmt, id, v.Error, eor) + return + } + buf := strings.Builder{} + id := strID(v.ID) + fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning + if v.ID != nil && v.Method != "" && (v.Params != nil || v.Method == "shutdown") { + fmt.Fprintf(&buf, "Sending request '%s - (%s)'.\n", v.Method, id) + x := "{}" + if v.Params != nil { + x = string(*v.Params) + } + fmt.Fprintf(&buf, "Params: %s%s", x, eor) + maps.setClient(id, req{method: v.Method, start: tm}) + } else if v.ID != nil && v.Method == "" && v.Params == nil { + sc := maps.server(id, true) + elapsed := tm.Sub(sc.start) + fmt.Fprintf(&buf, "Sending response '%s - (%s)' took %dms.\n", + sc.method, id, elapsed/time.Millisecond) + if v.Result == nil { + fmt.Fprintf(&buf, "Result: {}%s", eor) + } else { + fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) + } + } else if v.ID == nil && v.Method != "" { + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + fmt.Fprintf(&buf, "Sending notification '%s'.\n", v.Method) + fmt.Fprintf(&buf, "Params: %s%s", p, eor) + } else { // for completeness, as it should never happen + buf = strings.Builder{} // undo common Trace + fmt.Fprintf(&buf, "[Error - %s] on read ID?%v method:%q Params:%v Result:%v Error:%v%s", + tmfmt, v.ID != nil, v.Method, v.Params != nil, + v.Result != nil, v.Error != nil, eor) + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + r := "null" + if v.Result != nil { + r = string(*v.Result) + } + fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor) + } + outfd.Write([]byte(buf.String())) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go new file mode 100644 index 00000000..886b0aaa --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" +) + +const ( + // RequestCancelledError should be used when a request is cancelled early. + RequestCancelledError = -32800 +) + +type DocumentUri = string + +type canceller struct{ jsonrpc2.EmptyHandler } + +type clientHandler struct { + canceller + client Client +} + +type serverHandler struct { + canceller + server Server +} + +func (canceller) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context { + if direction == jsonrpc2.Receive && r.Method == "$/cancelRequest" { + var params CancelParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + log.Printf("%v", err) + } else { + conn.Cancel(params.ID) + } + } + return ctx +} + +func (canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool { + if cancelled { + return false + } + conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: id}) + return true +} + +func NewClient(ctx context.Context, stream jsonrpc2.Stream, client Client) (context.Context, *jsonrpc2.Conn, Server) { + ctx = WithClient(ctx, client) + conn := jsonrpc2.NewConn(stream) + conn.AddHandler(&clientHandler{client: client}) + return ctx, conn, &serverDispatcher{Conn: conn} +} + +func NewServer(ctx context.Context, stream jsonrpc2.Stream, server Server) (context.Context, *jsonrpc2.Conn, Client) { + conn := jsonrpc2.NewConn(stream) + client := &clientDispatcher{Conn: conn} + ctx = WithClient(ctx, client) + conn.AddHandler(&serverHandler{server: server}) + return ctx, conn, client +} + +func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) { + if _, ok := err.(*jsonrpc2.Error); !ok { + err = jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err) + } + if err := req.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/span.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/span.go new file mode 100644 index 00000000..799c2288 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/span.go @@ -0,0 +1,138 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// this file contains protocol<->span converters + +package protocol + +import ( + "fmt" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/span" + + errors "golang.org/x/xerrors" +) + +type ColumnMapper struct { + URI span.URI + Converter *span.TokenConverter + Content []byte +} + +func NewURI(uri span.URI) string { + return string(uri) +} + +func (m *ColumnMapper) Location(s span.Span) (Location, error) { + rng, err := m.Range(s) + if err != nil { + return Location{}, err + } + return Location{URI: NewURI(s.URI()), Range: rng}, nil +} + +func (m *ColumnMapper) Range(s span.Span) (Range, error) { + if span.CompareURI(m.URI, s.URI()) != 0 { + return Range{}, errors.Errorf("column mapper is for file %q instead of %q", m.URI, s.URI()) + } + s, err := s.WithAll(m.Converter) + if err != nil { + return Range{}, err + } + start, err := m.Position(s.Start()) + if err != nil { + return Range{}, err + } + end, err := m.Position(s.End()) + if err != nil { + return Range{}, err + } + return Range{Start: start, End: end}, nil +} + +func (m *ColumnMapper) Position(p span.Point) (Position, error) { + chr, err := span.ToUTF16Column(p, m.Content) + if err != nil { + return Position{}, err + } + return Position{ + Line: float64(p.Line() - 1), + Character: float64(chr - 1), + }, nil +} + +func (m *ColumnMapper) Span(l Location) (span.Span, error) { + return m.RangeSpan(l.Range) +} + +func (m *ColumnMapper) RangeSpan(r Range) (span.Span, error) { + start, err := m.Point(r.Start) + if err != nil { + return span.Span{}, err + } + end, err := m.Point(r.End) + if err != nil { + return span.Span{}, err + } + return span.New(m.URI, start, end).WithAll(m.Converter) +} + +func (m *ColumnMapper) PointSpan(p Position) (span.Span, error) { + start, err := m.Point(p) + if err != nil { + return span.Span{}, err + } + return span.New(m.URI, start, start).WithAll(m.Converter) +} + +func (m *ColumnMapper) Point(p Position) (span.Point, error) { + line := int(p.Line) + 1 + offset, err := m.Converter.ToOffset(line, 1) + if err != nil { + return span.Point{}, err + } + lineStart := span.NewPoint(line, 1, offset) + return span.FromUTF16Column(lineStart, int(p.Character)+1, m.Content) +} + +func IsPoint(r Range) bool { + return r.Start.Line == r.End.Line && r.Start.Character == r.End.Character +} + +func CompareRange(a, b Range) int { + if r := ComparePosition(a.Start, b.Start); r != 0 { + return r + } + return ComparePosition(a.End, b.End) +} + +func ComparePosition(a, b Position) int { + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Character < b.Character { + return -1 + } + if a.Character > b.Character { + return 1 + } + return 0 +} + +func (r Range) Format(f fmt.State, _ rune) { + fmt.Fprintf(f, "%v:%v-%v:%v", r.Start.Line, r.Start.Character, r.End.Line, r.End.Character) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go new file mode 100644 index 00000000..f68d63d0 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go @@ -0,0 +1,221 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" +) + +type Client interface { + ShowMessage(context.Context, *ShowMessageParams) error + LogMessage(context.Context, *LogMessageParams) error + Event(context.Context, *interface{}) error + PublishDiagnostics(context.Context, *PublishDiagnosticsParams) error + WorkspaceFolders(context.Context) ([]WorkspaceFolder, error) + Configuration(context.Context, *ParamConfig) ([]interface{}, error) + RegisterCapability(context.Context, *RegistrationParams) error + UnregisterCapability(context.Context, *UnregistrationParams) error + ShowMessageRequest(context.Context, *ShowMessageRequestParams) (*MessageActionItem, error) + ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) +} + +func (h clientHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + if ctx.Err() != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, "")) + return true + } + switch r.Method { + case "window/showMessage": // notif + var params ShowMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.ShowMessage(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "window/logMessage": // notif + var params LogMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.LogMessage(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "telemetry/event": // notif + var params interface{} + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.Event(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/publishDiagnostics": // notif + var params PublishDiagnosticsParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.PublishDiagnostics(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/workspaceFolders": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + resp, err := h.client.WorkspaceFolders(ctx) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/configuration": // req + var params ParamConfig + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.Configuration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "client/registerCapability": // req + var params RegistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + err := h.client.RegisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "client/unregisterCapability": // req + var params UnregistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + err := h.client.UnregisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "window/showMessageRequest": // req + var params ShowMessageRequestParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.ShowMessageRequest(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/applyEdit": // req + var params ApplyWorkspaceEditParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.ApplyEdit(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + + default: + return false + } +} + +type clientDispatcher struct { + *jsonrpc2.Conn +} + +func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error { + return s.Conn.Notify(ctx, "window/showMessage", params) +} + +func (s *clientDispatcher) LogMessage(ctx context.Context, params *LogMessageParams) error { + return s.Conn.Notify(ctx, "window/logMessage", params) +} + +func (s *clientDispatcher) Event(ctx context.Context, params *interface{}) error { + return s.Conn.Notify(ctx, "telemetry/event", params) +} + +func (s *clientDispatcher) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) error { + return s.Conn.Notify(ctx, "textDocument/publishDiagnostics", params) +} +func (s *clientDispatcher) WorkspaceFolders(ctx context.Context) ([]WorkspaceFolder, error) { + var result []WorkspaceFolder + if err := s.Conn.Call(ctx, "workspace/workspaceFolders", nil, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfig) ([]interface{}, error) { + var result []interface{} + if err := s.Conn.Call(ctx, "workspace/configuration", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *clientDispatcher) RegisterCapability(ctx context.Context, params *RegistrationParams) error { + return s.Conn.Call(ctx, "client/registerCapability", params, nil) // Call, not Notify +} + +func (s *clientDispatcher) UnregisterCapability(ctx context.Context, params *UnregistrationParams) error { + return s.Conn.Call(ctx, "client/unregisterCapability", params, nil) // Call, not Notify +} + +func (s *clientDispatcher) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (*MessageActionItem, error) { + var result MessageActionItem + if err := s.Conn.Call(ctx, "window/showMessageRequest", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *clientDispatcher) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) { + var result ApplyWorkspaceEditResponse + if err := s.Conn.Call(ctx, "workspace/applyEdit", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Types constructed to avoid structs as formal argument types +type ParamConfig struct { + ConfigurationParams + PartialResultParams +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go new file mode 100644 index 00000000..e0a35946 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -0,0 +1,4630 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocol contains data types and code for LSP jsonrpcs +// generated automatically from vscode-languageserver-node +// commit: 36ac51f057215e6e2e0408384e07ecf564a938da +// last fetched Tue Sep 24 2019 17:44:28 GMT-0400 (Eastern Daylight Time) +package protocol + +// Code generated (see typescript/README.md) DO NOT EDIT. + +/*ImplementationClientCapabilities defined: + * Since 3.6.0 + */ +type ImplementationClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `ImplementationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * Since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// ImplementationOptions is +type ImplementationOptions struct { + WorkDoneProgressOptions +} + +// ImplementationRegistrationOptions is +type ImplementationRegistrationOptions struct { + TextDocumentRegistrationOptions + ImplementationOptions + StaticRegistrationOptions +} + +// ImplementationParams is +type ImplementationParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*TypeDefinitionClientCapabilities defined: + * Since 3.6.0 + */ +type TypeDefinitionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `TypeDefinitionRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * Since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// TypeDefinitionOptions is +type TypeDefinitionOptions struct { + WorkDoneProgressOptions +} + +// TypeDefinitionRegistrationOptions is +type TypeDefinitionRegistrationOptions struct { + TextDocumentRegistrationOptions + TypeDefinitionOptions + StaticRegistrationOptions +} + +// TypeDefinitionParams is +type TypeDefinitionParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// WorkspaceFoldersInitializeParams is +type WorkspaceFoldersInitializeParams struct { + + /*WorkspaceFolders defined: + * The actual configured workspace folders. + */ + WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"` +} + +// WorkspaceFoldersClientCapabilities is +type WorkspaceFoldersClientCapabilities struct { + + /*Workspace defined: + * The workspace client capabilities + */ + Workspace *struct { + + /*WorkspaceFolders defined: + * The client has support for workspace folders + */ + WorkspaceFolders bool `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +// WorkspaceFoldersServerCapabilities is +type WorkspaceFoldersServerCapabilities struct { + + /*Workspace defined: + * The workspace server capabilities + */ + Workspace *struct { + + // WorkspaceFolders is + WorkspaceFolders *struct { + + /*Supported defined: + * The Server has support for workspace folders + */ + Supported bool `json:"supported,omitempty"` + + /*ChangeNotifications defined: + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean + } `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +// WorkspaceFolder is +type WorkspaceFolder struct { + + /*URI defined: + * The associated URI for this workspace folder. + */ + URI string `json:"uri"` + + /*Name defined: + * The name of the workspace folder. Used to refer to this + * workspace folder in the user interface. + */ + Name string `json:"name"` +} + +/*DidChangeWorkspaceFoldersParams defined: + * The parameters of a `workspace/didChangeWorkspaceFolders` notification. + */ +type DidChangeWorkspaceFoldersParams struct { + + /*Event defined: + * The actual workspace folder change event. + */ + Event WorkspaceFoldersChangeEvent `json:"event"` +} + +/*WorkspaceFoldersChangeEvent defined: + * The workspace folder change event. + */ +type WorkspaceFoldersChangeEvent struct { + + /*Added defined: + * The array of added workspace folders + */ + Added []WorkspaceFolder `json:"added"` + + /*Removed defined: + * The array of the removed workspace folders + */ + Removed []WorkspaceFolder `json:"removed"` +} + +// ConfigurationClientCapabilities is +type ConfigurationClientCapabilities struct { + + /*Workspace defined: + * The workspace client capabilities + */ + Workspace *struct { + + /*Configuration defined: + * The client supports `workspace/configuration` requests. + */ + Configuration bool `json:"configuration,omitempty"` + } `json:"workspace,omitempty"` +} + +// ConfigurationItem is +type ConfigurationItem struct { + + /*ScopeURI defined: + * The scope to get the configuration section for. + */ + ScopeURI string `json:"scopeUri,omitempty"` + + /*Section defined: + * The configuration section asked for. + */ + Section string `json:"section,omitempty"` +} + +/*ConfigurationParams defined: + * The parameters of a configuration request. + */ +type ConfigurationParams struct { + + // Items is + Items []ConfigurationItem `json:"items"` +} + +// DocumentColorClientCapabilities is +type DocumentColorClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `DocumentColorRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// DocumentColorOptions is +type DocumentColorOptions struct { + + /*ResolveProvider defined: + * Code lens has a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// DocumentColorRegistrationOptions is +type DocumentColorRegistrationOptions struct { + TextDocumentRegistrationOptions + StaticRegistrationOptions + DocumentColorOptions +} + +/*DocumentColorParams defined: + * Parameters for a [DocumentColorRequest](#DocumentColorRequest). + */ +type DocumentColorParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*ColorPresentationParams defined: + * Parameters for a [ColorPresentationRequest](#ColorPresentationRequest). + */ +type ColorPresentationParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Color defined: + * The color to request presentations for. + */ + Color Color `json:"color"` + + /*Range defined: + * The range where the color would be inserted. Serves as a context. + */ + Range Range `json:"range"` + WorkDoneProgressParams + PartialResultParams +} + +// FoldingRangeClientCapabilities is +type FoldingRangeClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for folding range providers. If this is set to `true` + * the client supports the new `FoldingRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*RangeLimit defined: + * The maximum number of folding ranges that the client prefers to receive per document. The value serves as a + * hint, servers are free to follow the limit. + */ + RangeLimit float64 `json:"rangeLimit,omitempty"` + + /*LineFoldingOnly defined: + * If set, the client signals that it only supports folding complete lines. If set, client will + * ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. + */ + LineFoldingOnly bool `json:"lineFoldingOnly,omitempty"` +} + +// FoldingRangeOptions is +type FoldingRangeOptions struct { + WorkDoneProgressOptions +} + +// FoldingRangeRegistrationOptions is +type FoldingRangeRegistrationOptions struct { + TextDocumentRegistrationOptions + FoldingRangeOptions + StaticRegistrationOptions +} + +/*FoldingRange defined: + * Represents a folding range. + */ +type FoldingRange struct { + + /*StartLine defined: + * The zero-based line number from where the folded range starts. + */ + StartLine float64 `json:"startLine"` + + /*StartCharacter defined: + * The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line. + */ + StartCharacter float64 `json:"startCharacter,omitempty"` + + /*EndLine defined: + * The zero-based line number where the folded range ends. + */ + EndLine float64 `json:"endLine"` + + /*EndCharacter defined: + * The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. + */ + EndCharacter float64 `json:"endCharacter,omitempty"` + + /*Kind defined: + * Describes the kind of the folding range such as `comment' or 'region'. The kind + * is used to categorize folding ranges and used by commands like 'Fold all comments'. See + * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds. + */ + Kind string `json:"kind,omitempty"` +} + +/*FoldingRangeParams defined: + * Parameters for a [FoldingRangeRequest](#FoldingRangeRequest). + */ +type FoldingRangeParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DeclarationClientCapabilities defined: + * Since 3.14.0 + */ +type DeclarationClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether declaration supports dynamic registration. If this is set to `true` + * the client supports the new `DeclarationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of declaration links. + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// DeclarationOptions is +type DeclarationOptions struct { + WorkDoneProgressOptions +} + +// DeclarationRegistrationOptions is +type DeclarationRegistrationOptions struct { + DeclarationOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +// DeclarationParams is +type DeclarationParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// SelectionRangeClientCapabilities is +type SelectionRangeClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for selection range providers. If this is set to `true` + * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// SelectionRangeOptions is +type SelectionRangeOptions struct { + WorkDoneProgressOptions +} + +// SelectionRangeRegistrationOptions is +type SelectionRangeRegistrationOptions struct { + SelectionRangeOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +/*SelectionRangeParams defined: + * A parameter literal used in selection range requests. + */ +type SelectionRangeParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Positions defined: + * The positions inside the text document. + */ + Positions []Position `json:"positions"` + WorkDoneProgressParams + PartialResultParams +} + +/*Registration defined: + * General parameters to to register for an notification or to register a provider. + */ +type Registration struct { + + /*ID defined: + * The id used to register the request. The id can be used to deregister + * the request again. + */ + ID string `json:"id"` + + /*Method defined: + * The method to register for. + */ + Method string `json:"method"` + + /*RegisterOptions defined: + * Options necessary for the registration. + */ + RegisterOptions interface{} `json:"registerOptions,omitempty"` +} + +// RegistrationParams is +type RegistrationParams struct { + + // Registrations is + Registrations []Registration `json:"registrations"` +} + +/*Unregistration defined: + * General parameters to unregister a request or notification. + */ +type Unregistration struct { + + /*ID defined: + * The id used to unregister the request or notification. Usually an id + * provided during the register request. + */ + ID string `json:"id"` + + /*Method defined: + * The method to unregister for. + */ + Method string `json:"method"` +} + +// UnregistrationParams is +type UnregistrationParams struct { + + // Unregisterations is + Unregisterations []Unregistration `json:"unregisterations"` +} + +// WorkDoneProgressParams is +type WorkDoneProgressParams struct { + + /*WorkDoneToken defined: + * An optional token that a server can use to report work done progress. + */ + WorkDoneToken *ProgressToken `json:"workDoneToken,omitempty"` +} + +// PartialResultParams is +type PartialResultParams struct { + + /*PartialResultToken defined: + * An optional token that a server can use to report partial results (e.g. streaming) to + * the client. + */ + PartialResultToken *ProgressToken `json:"partialResultToken,omitempty"` +} + +/*TextDocumentPositionParams defined: + * A parameter literal used in requests to pass a text document and a position inside that + * document. + */ +type TextDocumentPositionParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position inside the text document. + */ + Position Position `json:"position"` +} + +/*WorkspaceClientCapabilities defined: + * Workspace specific client capabilities. + */ +type WorkspaceClientCapabilities struct { + + /*ApplyEdit defined: + * The client supports applying batch edits + * to the workspace by supporting the request + * 'workspace/applyEdit' + */ + ApplyEdit bool `json:"applyEdit,omitempty"` + + /*WorkspaceEdit defined: + * Capabilities specific to `WorkspaceEdit`s + */ + WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"` + + /*DidChangeConfiguration defined: + * Capabilities specific to the `workspace/didChangeConfiguration` notification. + */ + DidChangeConfiguration *DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"` + + /*DidChangeWatchedFiles defined: + * Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + */ + DidChangeWatchedFiles *DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"` + + /*Symbol defined: + * Capabilities specific to the `workspace/symbol` request. + */ + Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"` + + /*ExecuteCommand defined: + * Capabilities specific to the `workspace/executeCommand` request. + */ + ExecuteCommand *ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"` +} + +/*TextDocumentClientCapabilities defined: + * Text document specific client capabilities. + */ +type TextDocumentClientCapabilities struct { + + /*Synchronization defined: + * Defines which synchronization capabilities the client supports. + */ + Synchronization *TextDocumentSyncClientCapabilities `json:"synchronization,omitempty"` + + /*Completion defined: + * Capabilities specific to the `textDocument/completion` + */ + Completion *CompletionClientCapabilities `json:"completion,omitempty"` + + /*Hover defined: + * Capabilities specific to the `textDocument/hover` + */ + Hover *HoverClientCapabilities `json:"hover,omitempty"` + + /*SignatureHelp defined: + * Capabilities specific to the `textDocument/signatureHelp` + */ + SignatureHelp *SignatureHelpClientCapabilities `json:"signatureHelp,omitempty"` + + /*Declaration defined: + * Capabilities specific to the `textDocument/declaration` + * + * @since 3.14.0 + */ + Declaration *DeclarationClientCapabilities `json:"declaration,omitempty"` + + /*Definition defined: + * Capabilities specific to the `textDocument/definition` + */ + Definition *DefinitionClientCapabilities `json:"definition,omitempty"` + + /*TypeDefinition defined: + * Capabilities specific to the `textDocument/typeDefinition` + * + * @since 3.6.0 + */ + TypeDefinition *TypeDefinitionClientCapabilities `json:"typeDefinition,omitempty"` + + /*Implementation defined: + * Capabilities specific to the `textDocument/implementation` + * + * @since 3.6.0 + */ + Implementation *ImplementationClientCapabilities `json:"implementation,omitempty"` + + /*References defined: + * Capabilities specific to the `textDocument/references` + */ + References *ReferenceClientCapabilities `json:"references,omitempty"` + + /*DocumentHighlight defined: + * Capabilities specific to the `textDocument/documentHighlight` + */ + DocumentHighlight *DocumentHighlightClientCapabilities `json:"documentHighlight,omitempty"` + + /*DocumentSymbol defined: + * Capabilities specific to the `textDocument/documentSymbol` + */ + DocumentSymbol *DocumentSymbolClientCapabilities `json:"documentSymbol,omitempty"` + + /*CodeAction defined: + * Capabilities specific to the `textDocument/codeAction` + */ + CodeAction *CodeActionClientCapabilities `json:"codeAction,omitempty"` + + /*CodeLens defined: + * Capabilities specific to the `textDocument/codeLens` + */ + CodeLens *CodeLensClientCapabilities `json:"codeLens,omitempty"` + + /*DocumentLink defined: + * Capabilities specific to the `textDocument/documentLink` + */ + DocumentLink *DocumentLinkClientCapabilities `json:"documentLink,omitempty"` + + /*ColorProvider defined: + * Capabilities specific to the `textDocument/documentColor` + */ + ColorProvider *DocumentColorClientCapabilities `json:"colorProvider,omitempty"` + + /*Formatting defined: + * Capabilities specific to the `textDocument/formatting` + */ + Formatting *DocumentFormattingClientCapabilities `json:"formatting,omitempty"` + + /*RangeFormatting defined: + * Capabilities specific to the `textDocument/rangeFormatting` + */ + RangeFormatting *DocumentRangeFormattingClientCapabilities `json:"rangeFormatting,omitempty"` + + /*OnTypeFormatting defined: + * Capabilities specific to the `textDocument/onTypeFormatting` + */ + OnTypeFormatting *DocumentOnTypeFormattingClientCapabilities `json:"onTypeFormatting,omitempty"` + + /*Rename defined: + * Capabilities specific to the `textDocument/rename` + */ + Rename *RenameClientCapabilities `json:"rename,omitempty"` + + /*FoldingRange defined: + * Capabilities specific to `textDocument/foldingRange` requests. + * + * @since 3.10.0 + */ + FoldingRange *FoldingRangeClientCapabilities `json:"foldingRange,omitempty"` + + /*SelectionRange defined: + * Capabilities specific to `textDocument/selectionRange` requests + * + * @since 3.15.0 + */ + SelectionRange *SelectionRangeClientCapabilities `json:"selectionRange,omitempty"` + + /*PublishDiagnostics defined: + * Capabilities specific to `textDocument/publishDiagnostics`. + */ + PublishDiagnostics *PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"` +} + +/*InnerClientCapabilities defined: + * Defines the capabilities provided by the client. + */ +type InnerClientCapabilities struct { + + /*Workspace defined: + * Workspace specific client capabilities. + */ + Workspace *WorkspaceClientCapabilities `json:"workspace,omitempty"` + + /*TextDocument defined: + * Text document specific client capabilities. + */ + TextDocument *TextDocumentClientCapabilities `json:"textDocument,omitempty"` + + /*Window defined: + * Window specific client capabilities. + */ + Window interface{} `json:"window,omitempty"` + + /*Experimental defined: + * Experimental client capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` +} + +// ClientCapabilities is +type ClientCapabilities struct { + + /*Workspace defined: + * Workspace specific client capabilities. + */ + Workspace struct { + + /*ApplyEdit defined: + * The client supports applying batch edits + * to the workspace by supporting the request + * 'workspace/applyEdit' + */ + ApplyEdit bool `json:"applyEdit,omitempty"` + + /*WorkspaceEdit defined: + * Capabilities specific to `WorkspaceEdit`s + */ + WorkspaceEdit WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"` + + /*DidChangeConfiguration defined: + * Capabilities specific to the `workspace/didChangeConfiguration` notification. + */ + DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"` + + /*DidChangeWatchedFiles defined: + * Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + */ + DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"` + + /*Symbol defined: + * Capabilities specific to the `workspace/symbol` request. + */ + Symbol WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"` + + /*ExecuteCommand defined: + * Capabilities specific to the `workspace/executeCommand` request. + */ + ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"` + + /*WorkspaceFolders defined: + * The client has support for workspace folders + */ + WorkspaceFolders bool `json:"workspaceFolders,omitempty"` + + /*Configuration defined: + * The client supports `workspace/configuration` requests. + */ + Configuration bool `json:"configuration,omitempty"` + } `json:"workspace,omitempty"` + + /*TextDocument defined: + * Text document specific client capabilities. + */ + TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"` + + /*Window defined: + * Window specific client capabilities. + */ + Window interface{} `json:"window,omitempty"` + + /*Experimental defined: + * Experimental client capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for selection range providers. If this is set to `true` + * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*StaticRegistrationOptions defined: + * Static registration options to be returned in the initialize + * request. + */ +type StaticRegistrationOptions struct { + + /*ID defined: + * The id used to register the request. The id can be used to deregister + * the request again. See also Registration#id. + */ + ID string `json:"id,omitempty"` +} + +/*TextDocumentRegistrationOptions defined: + * General text document registration options. + */ +type TextDocumentRegistrationOptions struct { + + /*DocumentSelector defined: + * A document selector to identify the scope of the registration. If set to null + * the document selector provided on the client side will be used. + */ + DocumentSelector DocumentSelector `json:"documentSelector"` +} + +/*SaveOptions defined: + * Save options. + */ +type SaveOptions struct { + + /*IncludeText defined: + * The client is supposed to include the content on save. + */ + IncludeText bool `json:"includeText,omitempty"` +} + +// WorkDoneProgressOptions is +type WorkDoneProgressOptions struct { + + // WorkDoneProgress is + WorkDoneProgress bool `json:"workDoneProgress,omitempty"` +} + +/*InnerServerCapabilities defined: + * Defines the capabilities provided by a language + * server. + */ +type InnerServerCapabilities struct { + + /*TextDocumentSync defined: + * Defines how text documents are synced. Is either a detailed structure defining each notification or + * for backwards compatibility the TextDocumentSyncKind number. + */ + TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind + + /*CompletionProvider defined: + * The server provides completion support. + */ + CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` + + /*HoverProvider defined: + * The server provides hover support. + */ + HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions + + /*SignatureHelpProvider defined: + * The server provides signature help support. + */ + SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` + + /*DeclarationProvider defined: + * The server provides Goto Declaration support. + */ + DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions + + /*DefinitionProvider defined: + * The server provides goto definition support. + */ + DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions + + /*TypeDefinitionProvider defined: + * The server provides Goto Type Definition support. + */ + TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions + + /*ImplementationProvider defined: + * The server provides Goto Implementation support. + */ + ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions + + /*ReferencesProvider defined: + * The server provides find references support. + */ + ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions + + /*DocumentHighlightProvider defined: + * The server provides document highlight support. + */ + DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions + + /*DocumentSymbolProvider defined: + * The server provides document symbol support. + */ + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions + + /*CodeActionProvider defined: + * The server provides code actions. CodeActionOptions may only be + * specified if the client states that it supports + * `codeActionLiteralSupport` in its initial `initialize` request. + */ + CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions + + /*CodeLensProvider defined: + * The server provides code lens. + */ + CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` + + /*DocumentLinkProvider defined: + * The server provides document link support. + */ + DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"` + + /*ColorProvider defined: + * The server provides color provider support. + */ + ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions + + /*WorkspaceSymbolProvider defined: + * The server provides workspace symbol support. + */ + WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions + + /*DocumentFormattingProvider defined: + * The server provides document formatting. + */ + DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions + + /*DocumentRangeFormattingProvider defined: + * The server provides document range formatting. + */ + DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions + + /*DocumentOnTypeFormattingProvider defined: + * The server provides document formatting on typing. + */ + DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` + + /*RenameProvider defined: + * The server provides rename support. RenameOptions may only be + * specified if the client states that it supports + * `prepareSupport` in its initial `initialize` request. + */ + RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions + + /*FoldingRangeProvider defined: + * The server provides folding provider support. + */ + FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions + + /*SelectionRangeProvider defined: + * The server provides selection range support. + */ + SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions + + /*ExecuteCommandProvider defined: + * The server provides execute command support. + */ + ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` + + /*Experimental defined: + * Experimental server capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` +} + +// ServerCapabilities is +type ServerCapabilities struct { + + /*TextDocumentSync defined: + * Defines how text documents are synced. Is either a detailed structure defining each notification or + * for backwards compatibility the TextDocumentSyncKind number. + */ + TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind + + /*CompletionProvider defined: + * The server provides completion support. + */ + CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` + + /*HoverProvider defined: + * The server provides hover support. + */ + HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions + + /*SignatureHelpProvider defined: + * The server provides signature help support. + */ + SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` + + /*DeclarationProvider defined: + * The server provides Goto Declaration support. + */ + DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions + + /*DefinitionProvider defined: + * The server provides goto definition support. + */ + DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions + + /*TypeDefinitionProvider defined: + * The server provides Goto Type Definition support. + */ + TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions + + /*ImplementationProvider defined: + * The server provides Goto Implementation support. + */ + ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions + + /*ReferencesProvider defined: + * The server provides find references support. + */ + ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions + + /*DocumentHighlightProvider defined: + * The server provides document highlight support. + */ + DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions + + /*DocumentSymbolProvider defined: + * The server provides document symbol support. + */ + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions + + /*CodeActionProvider defined: + * The server provides code actions. CodeActionOptions may only be + * specified if the client states that it supports + * `codeActionLiteralSupport` in its initial `initialize` request. + */ + CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions + + /*CodeLensProvider defined: + * The server provides code lens. + */ + CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` + + /*DocumentLinkProvider defined: + * The server provides document link support. + */ + DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"` + + /*ColorProvider defined: + * The server provides color provider support. + */ + ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions + + /*WorkspaceSymbolProvider defined: + * The server provides workspace symbol support. + */ + WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions + + /*DocumentFormattingProvider defined: + * The server provides document formatting. + */ + DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions + + /*DocumentRangeFormattingProvider defined: + * The server provides document range formatting. + */ + DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions + + /*DocumentOnTypeFormattingProvider defined: + * The server provides document formatting on typing. + */ + DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` + + /*RenameProvider defined: + * The server provides rename support. RenameOptions may only be + * specified if the client states that it supports + * `prepareSupport` in its initial `initialize` request. + */ + RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions + + /*FoldingRangeProvider defined: + * The server provides folding provider support. + */ + FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions + + /*SelectionRangeProvider defined: + * The server provides selection range support. + */ + SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions + + /*ExecuteCommandProvider defined: + * The server provides execute command support. + */ + ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` + + /*Experimental defined: + * Experimental server capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` + + /*Workspace defined: + * The workspace server capabilities + */ + Workspace *struct { + + // WorkspaceFolders is + WorkspaceFolders *struct { + + /*Supported defined: + * The Server has support for workspace folders + */ + Supported bool `json:"supported,omitempty"` + + /*ChangeNotifications defined: + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean + } `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +/*InnerInitializeParams defined: + * The initialize parameters + */ +type InnerInitializeParams struct { + + /*ProcessID defined: + * The process Id of the parent process that started + * the server. + */ + ProcessID float64 `json:"processId"` + + /*ClientInfo defined: + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo *struct { + + /*Name defined: + * The name of the client as defined by the client. + */ + Name string `json:"name"` + + /*Version defined: + * The client's version as defined by the client. + */ + Version string `json:"version,omitempty"` + } `json:"clientInfo,omitempty"` + + /*RootPath defined: + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + + /*RootURI defined: + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + + /*Capabilities defined: + * The capabilities provided by the client (editor or tool) + */ + Capabilities ClientCapabilities `json:"capabilities"` + + /*InitializationOptions defined: + * User provided initialization options. + */ + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + + /*Trace defined: + * The initial trace setting. If omitted trace is disabled ('off'). + */ + Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose' + WorkDoneProgressParams +} + +// InitializeParams is +type InitializeParams struct { + + /*ProcessID defined: + * The process Id of the parent process that started + * the server. + */ + ProcessID float64 `json:"processId"` + + /*ClientInfo defined: + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo *struct { + + /*Name defined: + * The name of the client as defined by the client. + */ + Name string `json:"name"` + + /*Version defined: + * The client's version as defined by the client. + */ + Version string `json:"version,omitempty"` + } `json:"clientInfo,omitempty"` + + /*RootPath defined: + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + + /*RootURI defined: + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + + /*Capabilities defined: + * The capabilities provided by the client (editor or tool) + */ + Capabilities ClientCapabilities `json:"capabilities"` + + /*InitializationOptions defined: + * User provided initialization options. + */ + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + + /*Trace defined: + * The initial trace setting. If omitted trace is disabled ('off'). + */ + Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose' + + /*WorkspaceFolders defined: + * The actual configured workspace folders. + */ + WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"` +} + +/*InitializeResult defined: + * The result returned from an initialize request. + */ +type InitializeResult struct { + + /*Capabilities defined: + * The capabilities the language server provides. + */ + Capabilities ServerCapabilities `json:"capabilities"` + + /*ServerInfo defined: + * Information about the server. + * + * @since 3.15.0 + */ + ServerInfo *struct { + + /*Name defined: + * The name of the server as defined by the server. + */ + Name string `json:"name"` + + /*Version defined: + * The servers's version as defined by the server. + */ + Version string `json:"version,omitempty"` + } `json:"serverInfo,omitempty"` + + /*Custom defined: + * Custom initialization results. + */ + Custom map[string]interface{} `json:"custom"` // [custom: string]: any; +} + +// InitializedParams is +type InitializedParams struct { +} + +// DidChangeConfigurationClientCapabilities is +type DidChangeConfigurationClientCapabilities struct { + + /*DynamicRegistration defined: + * Did change configuration notification supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// DidChangeConfigurationRegistrationOptions is +type DidChangeConfigurationRegistrationOptions struct { + + // Section is + Section string `json:"section,omitempty"` // string | string[] +} + +/*DidChangeConfigurationParams defined: + * The parameters of a change configuration notification. + */ +type DidChangeConfigurationParams struct { + + /*Settings defined: + * The actual changed settings + */ + Settings interface{} `json:"settings"` +} + +/*ShowMessageParams defined: + * The parameters of a notification message. + */ +type ShowMessageParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` +} + +// MessageActionItem is +type MessageActionItem struct { + + /*Title defined: + * A short title like 'Retry', 'Open Log' etc. + */ + Title string `json:"title"` +} + +// ShowMessageRequestParams is +type ShowMessageRequestParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` + + /*Actions defined: + * The message action items to present. + */ + Actions []MessageActionItem `json:"actions,omitempty"` +} + +/*LogMessageParams defined: + * The log message parameters. + */ +type LogMessageParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` +} + +// TextDocumentSyncClientCapabilities is +type TextDocumentSyncClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether text document synchronization supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*WillSave defined: + * The client supports sending will save notifications. + */ + WillSave bool `json:"willSave,omitempty"` + + /*WillSaveWaitUntil defined: + * The client supports sending a will save request and + * waits for a response providing text edits which will + * be applied to the document before it is saved. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + + /*DidSave defined: + * The client supports did save notifications. + */ + DidSave bool `json:"didSave,omitempty"` +} + +// TextDocumentSyncOptions is +type TextDocumentSyncOptions struct { + + /*OpenClose defined: + * Open and close notifications are sent to the server. If omitted open close notification should not + * be sent. + */ + OpenClose bool `json:"openClose,omitempty"` + + /*Change defined: + * Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full + * and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None. + */ + Change TextDocumentSyncKind `json:"change,omitempty"` + + /*WillSave defined: + * If present will save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + WillSave bool `json:"willSave,omitempty"` + + /*WillSaveWaitUntil defined: + * If present will save wait until requests are sent to the server. If omitted the request should not be + * sent. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + + /*Save defined: + * If present save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + Save *SaveOptions `json:"save,omitempty"` +} + +/*DidOpenTextDocumentParams defined: + * The parameters send in a open text document notification + */ +type DidOpenTextDocumentParams struct { + + /*TextDocument defined: + * The document that was opened. + */ + TextDocument TextDocumentItem `json:"textDocument"` +} + +/*DidChangeTextDocumentParams defined: + * The change text document notification's parameters. + */ +type DidChangeTextDocumentParams struct { + + /*TextDocument defined: + * The document that did change. The version number points + * to the version after all provided content changes have + * been applied. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*ContentChanges defined: + * The actual content changes. The content changes describe single state changes + * to the document. So if there are two content changes c1 and c2 for a document + * in state S then c1 move the document to S' and c2 to S''. + */ + ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` +} + +/*TextDocumentChangeRegistrationOptions defined: + * Describe options to be used when registered for text document change events. + */ +type TextDocumentChangeRegistrationOptions struct { + + /*SyncKind defined: + * How documents are synced to the server. + */ + SyncKind TextDocumentSyncKind `json:"syncKind"` + TextDocumentRegistrationOptions +} + +/*DidCloseTextDocumentParams defined: + * The parameters send in a close text document notification + */ +type DidCloseTextDocumentParams struct { + + /*TextDocument defined: + * The document that was closed. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` +} + +/*DidSaveTextDocumentParams defined: + * The parameters send in a save text document notification + */ +type DidSaveTextDocumentParams struct { + + /*TextDocument defined: + * The document that was closed. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*Text defined: + * Optional the content when saved. Depends on the includeText value + * when the save notification was requested. + */ + Text string `json:"text,omitempty"` +} + +/*TextDocumentSaveRegistrationOptions defined: + * Save registration options. + */ +type TextDocumentSaveRegistrationOptions struct { + TextDocumentRegistrationOptions + SaveOptions +} + +/*WillSaveTextDocumentParams defined: + * The parameters send in a will save text document notification. + */ +type WillSaveTextDocumentParams struct { + + /*TextDocument defined: + * The document that will be saved. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Reason defined: + * The 'TextDocumentSaveReason'. + */ + Reason TextDocumentSaveReason `json:"reason"` +} + +// DidChangeWatchedFilesClientCapabilities is +type DidChangeWatchedFilesClientCapabilities struct { + + /*DynamicRegistration defined: + * Did change watched files notification supports dynamic registration. Please note + * that the current protocol doesn't support static configuration for file changes + * from the server side. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DidChangeWatchedFilesParams defined: + * The watched files change notification's parameters. + */ +type DidChangeWatchedFilesParams struct { + + /*Changes defined: + * The actual file events. + */ + Changes []FileEvent `json:"changes"` +} + +/*FileEvent defined: + * An event describing a file change. + */ +type FileEvent struct { + + /*URI defined: + * The file's uri. + */ + URI DocumentURI `json:"uri"` + + /*Type defined: + * The change type. + */ + Type FileChangeType `json:"type"` +} + +/*DidChangeWatchedFilesRegistrationOptions defined: + * Describe options to be used when registered for text document change events. + */ +type DidChangeWatchedFilesRegistrationOptions struct { + + /*Watchers defined: + * The watchers to register. + */ + Watchers []FileSystemWatcher `json:"watchers"` +} + +// FileSystemWatcher is +type FileSystemWatcher struct { + + /*GlobPattern defined: + * The glob pattern to watch. Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + */ + GlobPattern string `json:"globPattern"` + + /*Kind defined: + * The kind of events of interest. If omitted it defaults + * to WatchKind.Create | WatchKind.Change | WatchKind.Delete + * which is 7. + */ + Kind float64 `json:"kind,omitempty"` +} + +/*PublishDiagnosticsClientCapabilities defined: + * The publish diagnostic client capabilities. + */ +type PublishDiagnosticsClientCapabilities struct { + + /*RelatedInformation defined: + * Whether the clients accepts diagnostics with related information. + */ + RelatedInformation bool `json:"relatedInformation,omitempty"` + + /*TagSupport defined: + * Client supports the tag property to provide meta data about a diagnostic. + * Clients supporting tags have to handle unknown tags gracefully. + * + * @since 3.15.0 + */ + TagSupport *struct { + + /*ValueSet defined: + * The tags supported by the client. + */ + ValueSet []DiagnosticTag `json:"valueSet"` + } `json:"tagSupport,omitempty"` +} + +/*PublishDiagnosticsParams defined: + * The publish diagnostic notification's parameters. + */ +type PublishDiagnosticsParams struct { + + /*URI defined: + * The URI for which diagnostic information is reported. + */ + URI DocumentURI `json:"uri"` + + /*Version defined: + * Optional the version number of the document the diagnostics are published for. + * + * @since 3.15.0 + */ + Version float64 `json:"version,omitempty"` + + /*Diagnostics defined: + * An array of diagnostic information items. + */ + Diagnostics []Diagnostic `json:"diagnostics"` +} + +/*CompletionClientCapabilities defined: + * Completion client capabilities + */ +type CompletionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether completion supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*CompletionItem defined: + * The client supports the following `CompletionItem` specific + * capabilities. + */ + CompletionItem *struct { + + /*SnippetSupport defined: + * Client supports snippets as insert text. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + */ + SnippetSupport bool `json:"snippetSupport,omitempty"` + + /*CommitCharactersSupport defined: + * Client supports commit characters on a completion item. + */ + CommitCharactersSupport bool `json:"commitCharactersSupport,omitempty"` + + /*DocumentationFormat defined: + * Client supports the follow content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"` + + /*DeprecatedSupport defined: + * Client supports the deprecated property on a completion item. + */ + DeprecatedSupport bool `json:"deprecatedSupport,omitempty"` + + /*PreselectSupport defined: + * Client supports the preselect property on a completion item. + */ + PreselectSupport bool `json:"preselectSupport,omitempty"` + + /*TagSupport defined: + * Client supports the tag property on a completion item. Clients supporting + * tags have to handle unknown tags gracefully. Clients especially need to + * preserve unknown tags when sending a completion item back to the server in + * a resolve call. + * + * @since 3.15.0 + */ + TagSupport *struct { + + /*ValueSet defined: + * The tags supported by the client. + */ + ValueSet []CompletionItemTag `json:"valueSet"` + } `json:"tagSupport,omitempty"` + } `json:"completionItem,omitempty"` + + // CompletionItemKind is + CompletionItemKind *struct { + + /*ValueSet defined: + * The completion item kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the completion items kinds from `Text` to `Reference` as defined in + * the initial version of the protocol. + */ + ValueSet []CompletionItemKind `json:"valueSet,omitempty"` + } `json:"completionItemKind,omitempty"` + + /*ContextSupport defined: + * The client supports to send additional context information for a + * `textDocument/completion` requestion. + */ + ContextSupport bool `json:"contextSupport,omitempty"` +} + +/*CompletionContext defined: + * Contains additional information about the context in which a completion request is triggered. + */ +type CompletionContext struct { + + /*TriggerKind defined: + * How the completion was triggered. + */ + TriggerKind CompletionTriggerKind `json:"triggerKind"` + + /*TriggerCharacter defined: + * The trigger character (a single character) that has trigger code complete. + * Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` +} + +/*CompletionParams defined: + * Completion parameters + */ +type CompletionParams struct { + + /*Context defined: + * The completion context. This is only available it the client specifies + * to send this using the client capability `textDocument.completion.contextSupport === true` + */ + Context *CompletionContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*CompletionOptions defined: + * Completion options. + */ +type CompletionOptions struct { + + /*TriggerCharacters defined: + * Most tools trigger completion request automatically without explicitly requesting + * it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user + * starts to type an identifier. For example if the user types `c` in a JavaScript file + * code complete will automatically pop up present `console` besides others as a + * completion item. Characters that make up identifiers don't need to be listed here. + * + * If code complete should automatically be trigger on characters not being valid inside + * an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. + */ + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + + /*AllCommitCharacters defined: + * The list of all possible characters that commit a completion. This field can be used + * if clients don't support individual commit characters per completion item. See + * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` + * + * @since 3.2.0 + */ + AllCommitCharacters []string `json:"allCommitCharacters,omitempty"` + + /*ResolveProvider defined: + * The server provides support to resolve additional + * information for a completion item. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*CompletionRegistrationOptions defined: + * Registration options for a [CompletionRequest](#CompletionRequest). + */ +type CompletionRegistrationOptions struct { + TextDocumentRegistrationOptions + CompletionOptions +} + +// HoverClientCapabilities is +type HoverClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether hover supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*ContentFormat defined: + * Client supports the follow content formats for the content + * property. The order describes the preferred format of the client. + */ + ContentFormat []MarkupKind `json:"contentFormat,omitempty"` +} + +/*HoverOptions defined: + * Hover options. + */ +type HoverOptions struct { + WorkDoneProgressOptions +} + +/*HoverParams defined: + * Parameters for a [HoverRequest](#HoverRequest). + */ +type HoverParams struct { + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*HoverRegistrationOptions defined: + * Registration options for a [HoverRequest](#HoverRequest). + */ +type HoverRegistrationOptions struct { + TextDocumentRegistrationOptions + HoverOptions +} + +/*SignatureHelpClientCapabilities defined: + * Client Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether signature help supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SignatureInformation defined: + * The client supports the following `SignatureInformation` + * specific properties. + */ + SignatureInformation *struct { + + /*DocumentationFormat defined: + * Client supports the follow content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"` + + /*ParameterInformation defined: + * Client capabilities specific to parameter information. + */ + ParameterInformation *struct { + + /*LabelOffsetSupport defined: + * The client supports processing label offsets instead of a + * simple label string. + * + * @since 3.14.0 + */ + LabelOffsetSupport bool `json:"labelOffsetSupport,omitempty"` + } `json:"parameterInformation,omitempty"` + } `json:"signatureInformation,omitempty"` + + /*ContextSupport defined: + * The client supports to send additional context information for a + * `textDocument/signatureHelp` request. A client that opts into + * contextSupport will also support the `retriggerCharacters` on + * `SignatureHelpOptions`. + * + * @since 3.15.0 + */ + ContextSupport bool `json:"contextSupport,omitempty"` +} + +/*SignatureHelpOptions defined: + * Server Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpOptions struct { + + /*TriggerCharacters defined: + * List of characters that trigger signature help. + */ + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + + /*RetriggerCharacters defined: + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + * + * @since 3.15.0 + */ + RetriggerCharacters []string `json:"retriggerCharacters,omitempty"` + WorkDoneProgressOptions +} + +/*SignatureHelpContext defined: + * Additional information about the context in which a signature help request was triggered. + * + * @since 3.15.0 + */ +type SignatureHelpContext struct { + + /*TriggerKind defined: + * Action that caused signature help to be triggered. + */ + TriggerKind SignatureHelpTriggerKind `json:"triggerKind"` + + /*TriggerCharacter defined: + * Character that caused signature help to be triggered. + * + * This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` + + /*IsRetrigger defined: + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occur when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + IsRetrigger bool `json:"isRetrigger"` + + /*ActiveSignatureHelp defined: + * The currently active `SignatureHelp`. + * + * The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on + * the user navigating through available signatures. + */ + ActiveSignatureHelp *SignatureHelp `json:"activeSignatureHelp,omitempty"` +} + +/*SignatureHelpParams defined: + * Parameters for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpParams struct { + + /*Context defined: + * The signature help context. This is only available if the client specifies + * to send this using the client capability `textDocument.signatureHelp.contextSupport === true` + * + * @since 3.15.0 + */ + Context *SignatureHelpContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*SignatureHelpRegistrationOptions defined: + * Registration options for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpRegistrationOptions struct { + TextDocumentRegistrationOptions + SignatureHelpOptions +} + +/*DefinitionClientCapabilities defined: + * Client Capabilities for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether definition supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * @since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +/*DefinitionOptions defined: + * Server Capabilities for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionOptions struct { + WorkDoneProgressOptions +} + +/*DefinitionParams defined: + * Parameters for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*DefinitionRegistrationOptions defined: + * Registration options for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionRegistrationOptions struct { + TextDocumentRegistrationOptions + DefinitionOptions +} + +/*ReferenceClientCapabilities defined: + * Client Capabilities for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether references supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*ReferenceParams defined: + * Parameters for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceParams struct { + + // Context is + Context ReferenceContext `json:"context"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*ReferenceOptions defined: + * Reference options. + */ +type ReferenceOptions struct { + WorkDoneProgressOptions +} + +/*ReferenceRegistrationOptions defined: + * Registration options for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceRegistrationOptions struct { + TextDocumentRegistrationOptions + ReferenceOptions +} + +/*DocumentHighlightClientCapabilities defined: + * Client Capabilities for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document highlight supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentHighlightParams defined: + * Parameters for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentHighlightOptions defined: + * Provider options for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightOptions struct { + WorkDoneProgressOptions +} + +/*DocumentHighlightRegistrationOptions defined: + * Registration options for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentHighlightOptions +} + +/*DocumentSymbolClientCapabilities defined: + * Client Capabilities for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document symbol supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SymbolKind defined: + * Specific capabilities for the `SymbolKind`. + */ + SymbolKind *struct { + + /*ValueSet defined: + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet,omitempty"` + } `json:"symbolKind,omitempty"` + + /*HierarchicalDocumentSymbolSupport defined: + * The client support hierarchical document symbols. + */ + HierarchicalDocumentSymbolSupport bool `json:"hierarchicalDocumentSymbolSupport,omitempty"` +} + +/*DocumentSymbolParams defined: + * Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentSymbolOptions defined: + * Provider options for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolOptions struct { + WorkDoneProgressOptions +} + +/*DocumentSymbolRegistrationOptions defined: + * Registration options for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentSymbolOptions +} + +/*CodeActionClientCapabilities defined: + * The Client Capabilities of a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether code action supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*CodeActionLiteralSupport defined: + * The client support code action literals as a valid + * response of the `textDocument/codeAction` request. + * + * @since 3.8.0 + */ + CodeActionLiteralSupport *struct { + + /*CodeActionKind defined: + * The code action kind is support with the following value + * set. + */ + CodeActionKind struct { + + /*ValueSet defined: + * The code action kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + */ + ValueSet []CodeActionKind `json:"valueSet"` + } `json:"codeActionKind"` + } `json:"codeActionLiteralSupport,omitempty"` + + /*IsPreferredSupport defined: + * Whether code action supports the `isPreferred` property. + * @since 3.15.0 + */ + IsPreferredSupport bool `json:"isPreferredSupport,omitempty"` +} + +/*CodeActionParams defined: + * The parameters of a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionParams struct { + + /*TextDocument defined: + * The document in which the command was invoked. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Range defined: + * The range for which the command was invoked. + */ + Range Range `json:"range"` + + /*Context defined: + * Context carrying additional information. + */ + Context CodeActionContext `json:"context"` + WorkDoneProgressParams + PartialResultParams +} + +/*CodeActionOptions defined: + * Provider options for a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionOptions struct { + + /*CodeActionKinds defined: + * CodeActionKinds that this server may return. + * + * The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server + * may list out every specific kind they provide. + */ + CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"` + WorkDoneProgressOptions +} + +/*CodeActionRegistrationOptions defined: + * Registration options for a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionRegistrationOptions struct { + TextDocumentRegistrationOptions + CodeActionOptions +} + +/*WorkspaceSymbolClientCapabilities defined: + * Client capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolClientCapabilities struct { + + /*DynamicRegistration defined: + * Symbol request supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SymbolKind defined: + * Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + */ + SymbolKind *struct { + + /*ValueSet defined: + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet,omitempty"` + } `json:"symbolKind,omitempty"` +} + +/*WorkspaceSymbolParams defined: + * The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolParams struct { + + /*Query defined: + * A query string to filter symbols by. Clients may send an empty + * string here to request all symbols. + */ + Query string `json:"query"` + WorkDoneProgressParams + PartialResultParams +} + +/*WorkspaceSymbolOptions defined: + * Server capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolOptions struct { + WorkDoneProgressOptions +} + +/*WorkspaceSymbolRegistrationOptions defined: + * Registration options for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolRegistrationOptions struct { + WorkspaceSymbolOptions +} + +/*CodeLensClientCapabilities defined: + * The client capabilities of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether code lens supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*CodeLensParams defined: + * The parameters of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensParams struct { + + /*TextDocument defined: + * The document to request code lens for. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*CodeLensOptions defined: + * Code Lens provider options of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensOptions struct { + + /*ResolveProvider defined: + * Code lens has a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*CodeLensRegistrationOptions defined: + * Registration options for a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensRegistrationOptions struct { + TextDocumentRegistrationOptions + CodeLensOptions +} + +/*DocumentLinkClientCapabilities defined: + * The client capabilities of a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document link supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*TooltipSupport defined: + * Whether the client support the `tooltip` property on `DocumentLink`. + * + * @since 3.15.0 + */ + TooltipSupport bool `json:"tooltipSupport,omitempty"` +} + +/*DocumentLinkParams defined: + * The parameters of a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkParams struct { + + /*TextDocument defined: + * The document to provide document links for. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentLinkOptions defined: + * Provider options for a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkOptions struct { + + /*ResolveProvider defined: + * Document links have a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*DocumentLinkRegistrationOptions defined: + * Registration options for a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentLinkOptions +} + +/*DocumentFormattingClientCapabilities defined: + * Client capabilities of a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentFormattingParams defined: + * The parameters of a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Options defined: + * The format options + */ + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +/*DocumentFormattingOptions defined: + * Provider options for a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingOptions struct { + WorkDoneProgressOptions +} + +/*DocumentFormattingRegistrationOptions defined: + * Registration options for a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentFormattingOptions +} + +/*DocumentRangeFormattingClientCapabilities defined: + * Client capabilities of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether range formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentRangeFormattingParams defined: + * The parameters of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Range defined: + * The range to format + */ + Range Range `json:"range"` + + /*Options defined: + * The format options + */ + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +/*DocumentRangeFormattingOptions defined: + * Provider options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingOptions struct { + WorkDoneProgressOptions +} + +/*DocumentRangeFormattingRegistrationOptions defined: + * Registration options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentRangeFormattingOptions +} + +/*DocumentOnTypeFormattingClientCapabilities defined: + * Client capabilities of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether on type formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentOnTypeFormattingParams defined: + * The parameters of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position at which this request was send. + */ + Position Position `json:"position"` + + /*Ch defined: + * The character that has been typed. + */ + Ch string `json:"ch"` + + /*Options defined: + * The format options. + */ + Options FormattingOptions `json:"options"` +} + +/*DocumentOnTypeFormattingOptions defined: + * Provider options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingOptions struct { + + /*FirstTriggerCharacter defined: + * A character on which formatting should be triggered, like `}`. + */ + FirstTriggerCharacter string `json:"firstTriggerCharacter"` + + /*MoreTriggerCharacter defined: + * More trigger characters. + */ + MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"` +} + +/*DocumentOnTypeFormattingRegistrationOptions defined: + * Registration options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentOnTypeFormattingOptions +} + +// RenameClientCapabilities is +type RenameClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether rename supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*PrepareSupport defined: + * Client supports testing for validity of rename operations + * before execution. + * + * @since version 3.12.0 + */ + PrepareSupport bool `json:"prepareSupport,omitempty"` +} + +/*RenameParams defined: + * The parameters of a [RenameRequest](#RenameRequest). + */ +type RenameParams struct { + + /*TextDocument defined: + * The document to rename. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position at which this request was sent. + */ + Position Position `json:"position"` + + /*NewName defined: + * The new name of the symbol. If the given name is not valid the + * request must return a [ResponseError](#ResponseError) with an + * appropriate message set. + */ + NewName string `json:"newName"` + WorkDoneProgressParams +} + +/*RenameOptions defined: + * Provider options for a [RenameRequest](#RenameRequest). + */ +type RenameOptions struct { + + /*PrepareProvider defined: + * Renames should be checked and tested before being executed. + * + * @since version 3.12.0 + */ + PrepareProvider bool `json:"prepareProvider,omitempty"` + WorkDoneProgressOptions +} + +/*RenameRegistrationOptions defined: + * Registration options for a [RenameRequest](#RenameRequest). + */ +type RenameRegistrationOptions struct { + TextDocumentRegistrationOptions + RenameOptions +} + +// PrepareRenameParams is +type PrepareRenameParams struct { + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*ExecuteCommandClientCapabilities defined: + * The client capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandClientCapabilities struct { + + /*DynamicRegistration defined: + * Execute command supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*ExecuteCommandParams defined: + * The parameters of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandParams struct { + + /*Command defined: + * The identifier of the actual command handler. + */ + Command string `json:"command"` + + /*Arguments defined: + * Arguments that the command should be invoked with. + */ + Arguments []interface{} `json:"arguments,omitempty"` + WorkDoneProgressParams +} + +/*ExecuteCommandOptions defined: + * The server capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandOptions struct { + + /*Commands defined: + * The commands to be executed on the server + */ + Commands []string `json:"commands"` + WorkDoneProgressOptions +} + +/*ExecuteCommandRegistrationOptions defined: + * Registration options for a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandRegistrationOptions struct { + ExecuteCommandOptions +} + +// WorkspaceEditClientCapabilities is +type WorkspaceEditClientCapabilities struct { + + /*DocumentChanges defined: + * The client supports versioned document changes in `WorkspaceEdit`s + */ + DocumentChanges bool `json:"documentChanges,omitempty"` + + /*ResourceOperations defined: + * The resource operations the client supports. Clients should at least + * support 'create', 'rename' and 'delete' files and folders. + * + * @since 3.13.0 + */ + ResourceOperations []ResourceOperationKind `json:"resourceOperations,omitempty"` + + /*FailureHandling defined: + * The failure handling strategy of a client if applying the workspace edit + * fails. + * + * @since 3.13.0 + */ + FailureHandling FailureHandlingKind `json:"failureHandling,omitempty"` +} + +/*ApplyWorkspaceEditParams defined: + * The parameters passed via a apply workspace edit request. + */ +type ApplyWorkspaceEditParams struct { + + /*Label defined: + * An optional label of the workspace edit. This label is + * presented in the user interface for example on an undo + * stack to undo the workspace edit. + */ + Label string `json:"label,omitempty"` + + /*Edit defined: + * The edits to apply. + */ + Edit WorkspaceEdit `json:"edit"` +} + +/*ApplyWorkspaceEditResponse defined: + * A response returned from the apply workspace edit request. + */ +type ApplyWorkspaceEditResponse struct { + + /*Applied defined: + * Indicates whether the edit was applied or not. + */ + Applied bool `json:"applied"` + + /*FailureReason defined: + * An optional textual description for why the edit was not applied. + * This may be used by the server for diagnostic logging or to provide + * a suitable error for a request that triggered the edit. + */ + FailureReason string `json:"failureReason,omitempty"` + + /*FailedChange defined: + * Depending on the client's failure handling strategy `failedChange` might + * contain the index of the change that failed. This property is only available + * if the client signals a `failureHandlingStrategy` in its client capabilities. + */ + FailedChange float64 `json:"failedChange,omitempty"` +} + +/*Position defined: + * Position in a text document expressed as zero-based line and character offset. + * The offsets are based on a UTF-16 string representation. So a string of the form + * `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀` + * is 1 and the character offset of b is 3 since `𐐀` is represented using two code + * units in UTF-16. + * + * Positions are line end character agnostic. So you can not specify a position that + * denotes `\r|\n` or `\n|` where `|` represents the character offset. + */ +type Position struct { + + /*Line defined: + * Line position in a document (zero-based). + * If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. + * If a line number is negative, it defaults to 0. + */ + Line float64 `json:"line"` + + /*Character defined: + * Character offset on a line in a document (zero-based). Assuming that the line is + * represented as a string, the `character` value represents the gap between the + * `character` and `character + 1`. + * + * If the character value is greater than the line length it defaults back to the + * line length. + * If a line number is negative, it defaults to 0. + */ + Character float64 `json:"character"` +} + +/*Range defined: + * A range in a text document expressed as (zero-based) start and end positions. + * + * If you want to specify a range that contains a line including the line ending + * character(s) then use an end position denoting the start of the next line. + * For example: + * ```ts + * { + * start: { line: 5, character: 23 } + * end : { line 6, character : 0 } + * } + * ``` + */ +type Range struct { + + /*Start defined: + * The range's start position + */ + Start Position `json:"start"` + + /*End defined: + * The range's end position. + */ + End Position `json:"end"` +} + +/*Location defined: + * Represents a location inside a resource, such as a line + * inside a text file. + */ +type Location struct { + + // URI is + URI DocumentURI `json:"uri"` + + // Range is + Range Range `json:"range"` +} + +/*LocationLink defined: + * Represents the connection of two locations. Provides additional metadata over normal [locations](#Location), + * including an origin range. + */ +type LocationLink struct { + + /*OriginSelectionRange defined: + * Span of the origin of this link. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + OriginSelectionRange *Range `json:"originSelectionRange,omitempty"` + + /*TargetURI defined: + * The target resource identifier of this link. + */ + TargetURI DocumentURI `json:"targetUri"` + + /*TargetRange defined: + * The full target range of this link. If the target for example is a symbol then target range is the + * range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to highlight the range in the editor. + */ + TargetRange Range `json:"targetRange"` + + /*TargetSelectionRange defined: + * The range that should be selected and revealed when this link is being followed, e.g the name of a function. + * Must be contained by the `targetRange`. See also `DocumentSymbol#range` + */ + TargetSelectionRange Range `json:"targetSelectionRange"` +} + +/*Color defined: + * Represents a color in RGBA space. + */ +type Color struct { + + /*Red defined: + * The red component of this color in the range [0-1]. + */ + Red float64 `json:"red"` + + /*Green defined: + * The green component of this color in the range [0-1]. + */ + Green float64 `json:"green"` + + /*Blue defined: + * The blue component of this color in the range [0-1]. + */ + Blue float64 `json:"blue"` + + /*Alpha defined: + * The alpha component of this color in the range [0-1]. + */ + Alpha float64 `json:"alpha"` +} + +/*ColorInformation defined: + * Represents a color range from a document. + */ +type ColorInformation struct { + + /*Range defined: + * The range in the document where this color appears. + */ + Range Range `json:"range"` + + /*Color defined: + * The actual color value for this color range. + */ + Color Color `json:"color"` +} + +// ColorPresentation is +type ColorPresentation struct { + + /*Label defined: + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + Label string `json:"label"` + + /*TextEdit defined: + * An [edit](#TextEdit) which is applied to a document when selecting + * this presentation for the color. When `falsy` the [label](#ColorPresentation.label) + * is used. + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + + /*AdditionalTextEdits defined: + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves. + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` +} + +/*DiagnosticRelatedInformation defined: + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g when duplicating + * a symbol in a scope. + */ +type DiagnosticRelatedInformation struct { + + /*Location defined: + * The location of this related diagnostic information. + */ + Location Location `json:"location"` + + /*Message defined: + * The message of this related diagnostic information. + */ + Message string `json:"message"` +} + +/*Diagnostic defined: + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a resource. + */ +type Diagnostic struct { + + /*Range defined: + * The range at which the message applies + */ + Range Range `json:"range"` + + /*Severity defined: + * The diagnostic's severity. Can be omitted. If omitted it is up to the + * client to interpret diagnostics as error, warning, info or hint. + */ + Severity DiagnosticSeverity `json:"severity,omitempty"` + + /*Code defined: + * The diagnostic's code, which usually appear in the user interface. + */ + Code interface{} `json:"code,omitempty"` // number | string + + /*Source defined: + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. It usually + * appears in the user interface. + */ + Source string `json:"source,omitempty"` + + /*Message defined: + * The diagnostic's message. It usually appears in the user interface + */ + Message string `json:"message"` + + /*Tags defined: + * Additional metadata about the diagnostic. + */ + Tags []DiagnosticTag `json:"tags,omitempty"` + + /*RelatedInformation defined: + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"` +} + +/*Command defined: + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ +type Command struct { + + /*Title defined: + * Title of the command, like `save`. + */ + Title string `json:"title"` + + /*Command defined: + * The identifier of the actual command handler. + */ + Command string `json:"command"` + + /*Arguments defined: + * Arguments that the command handler should be + * invoked with. + */ + Arguments []interface{} `json:"arguments,omitempty"` +} + +/*TextEdit defined: + * A text edit applicable to a text document. + */ +type TextEdit struct { + + /*Range defined: + * The range of the text document to be manipulated. To insert + * text into a document create a range where start === end. + */ + Range Range `json:"range"` + + /*NewText defined: + * The string to be inserted. For delete operations use an + * empty string. + */ + NewText string `json:"newText"` +} + +/*TextDocumentEdit defined: + * Describes textual changes on a text document. + */ +type TextDocumentEdit struct { + + /*TextDocument defined: + * The text document to change. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*Edits defined: + * The edits to be applied. + */ + Edits []TextEdit `json:"edits"` +} + +// ResourceOperation is +type ResourceOperation struct { + + // Kind is + Kind string `json:"kind"` +} + +/*CreateFileOptions defined: + * Options to create a file. + */ +type CreateFileOptions struct { + + /*Overwrite defined: + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ + Overwrite bool `json:"overwrite,omitempty"` + + /*IgnoreIfExists defined: + * Ignore if exists. + */ + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/*CreateFile defined: + * Create file operation. + */ +type CreateFile struct { + + /*Kind defined: + * A create + */ + Kind string `json:"kind"` // 'create' + + /*URI defined: + * The resource to create. + */ + URI DocumentURI `json:"uri"` + + /*Options defined: + * Additional options + */ + Options *CreateFileOptions `json:"options,omitempty"` +} + +/*RenameFileOptions defined: + * Rename file options + */ +type RenameFileOptions struct { + + /*Overwrite defined: + * Overwrite target if existing. Overwrite wins over `ignoreIfExists` + */ + Overwrite bool `json:"overwrite,omitempty"` + + /*IgnoreIfExists defined: + * Ignores if target exists. + */ + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/*RenameFile defined: + * Rename file operation + */ +type RenameFile struct { + + /*Kind defined: + * A rename + */ + Kind string `json:"kind"` // 'rename' + + /*OldURI defined: + * The old (existing) location. + */ + OldURI DocumentURI `json:"oldUri"` + + /*NewURI defined: + * The new location. + */ + NewURI DocumentURI `json:"newUri"` + + /*Options defined: + * Rename options. + */ + Options *RenameFileOptions `json:"options,omitempty"` +} + +/*DeleteFileOptions defined: + * Delete file options + */ +type DeleteFileOptions struct { + + /*Recursive defined: + * Delete the content recursively if a folder is denoted. + */ + Recursive bool `json:"recursive,omitempty"` + + /*IgnoreIfNotExists defined: + * Ignore the operation if the file doesn't exist. + */ + IgnoreIfNotExists bool `json:"ignoreIfNotExists,omitempty"` +} + +/*DeleteFile defined: + * Delete file operation + */ +type DeleteFile struct { + + /*Kind defined: + * A delete + */ + Kind string `json:"kind"` // 'delete' + + /*URI defined: + * The file to delete. + */ + URI DocumentURI `json:"uri"` + + /*Options defined: + * Delete options. + */ + Options *DeleteFileOptions `json:"options,omitempty"` +} + +/*WorkspaceEdit defined: + * A workspace edit represents changes to many resources managed in the workspace. The edit + * should either provide `changes` or `documentChanges`. If documentChanges are present + * they are preferred over `changes` if the client can handle versioned document edits. + */ +type WorkspaceEdit struct { + + /*Changes defined: + * Holds changes to existing resources. + */ + Changes *map[string][]TextEdit `json:"changes,omitempty"` // [uri: string]: TextEdit[]; + + /*DocumentChanges defined: + * Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes + * are either an array of `TextDocumentEdit`s to express changes to n different text documents + * where each text document edit addresses a specific version of a text document. Or it can contain + * above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations. + * + * Whether a client supports versioned document edits is expressed via + * `workspace.workspaceEdit.documentChanges` client capability. + * + * If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then + * only plain `TextEdit`s using the `changes` property are supported. + */ + DocumentChanges []TextDocumentEdit `json:"documentChanges,omitempty"` // (TextDocumentEdit | CreateFile | RenameFile | DeleteFile) +} + +/*TextEditChange defined: + * A change to capture text edits for existing resources. + */ +type TextEditChange struct { +} + +/*TextDocumentIdentifier defined: + * A literal to identify a text document in the client. + */ +type TextDocumentIdentifier struct { + + /*URI defined: + * The text document's uri. + */ + URI DocumentURI `json:"uri"` +} + +/*VersionedTextDocumentIdentifier defined: + * An identifier to denote a specific version of a text document. + */ +type VersionedTextDocumentIdentifier struct { + + /*Version defined: + * The version number of this document. If a versioned text document identifier + * is sent from the server to the client and the file is not open in the editor + * (the server has not received an open notification before) the server can send + * `null` to indicate that the version is unknown and the content on disk is the + * truth (as speced with document content ownership). + */ + Version float64 `json:"version"` + TextDocumentIdentifier +} + +/*TextDocumentItem defined: + * An item to transfer a text document from the client to the + * server. + */ +type TextDocumentItem struct { + + /*URI defined: + * The text document's uri. + */ + URI DocumentURI `json:"uri"` + + /*LanguageID defined: + * The text document's language identifier + */ + LanguageID string `json:"languageId"` + + /*Version defined: + * The version number of this document (it will increase after each + * change, including undo/redo). + */ + Version float64 `json:"version"` + + /*Text defined: + * The content of the opened text document. + */ + Text string `json:"text"` +} + +/*MarkupContent defined: + * A `MarkupContent` literal represents a string value which content is interpreted base on its + * kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. + * + * If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. + * See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * Here is an example how such a string can be constructed using JavaScript / TypeScript: + * ```ts + * let markdown: MarkdownContent = { + * kind: MarkupKind.Markdown, + * value: [ + * '# Header', + * 'Some text', + * '```typescript', + * 'someCode();', + * '```' + * ].join('\n') + * }; + * ``` + * + * *Please Note* that clients might sanitize the return markdown. A client could decide to + * remove HTML from the markdown to avoid script execution. + */ +type MarkupContent struct { + + /*Kind defined: + * The type of the Markup + */ + Kind MarkupKind `json:"kind"` + + /*Value defined: + * The content itself + */ + Value string `json:"value"` +} + +/*CompletionItem defined: + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +type CompletionItem struct { + + /*Label defined: + * The label of this completion item. By default + * also the text that is inserted when selecting + * this completion. + */ + Label string `json:"label"` + + /*Kind defined: + * The kind of this completion item. Based of the kind + * an icon is chosen by the editor. + */ + Kind CompletionItemKind `json:"kind,omitempty"` + + /*Tags defined: + * Tags for this completion item. + * + * @since 3.15.0 + */ + Tags []CompletionItemTag `json:"tags,omitempty"` + + /*Detail defined: + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + Detail string `json:"detail,omitempty"` + + /*Documentation defined: + * A human-readable string that represents a doc-comment. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent + + /*Deprecated defined: + * Indicates if this item is deprecated. + * @deprecated Use `tags` instead. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Preselect defined: + * Select this item when showing. + * + * *Note* that only one completion item can be selected and that the + * tool / client decides which item that is. The rule is that the *first* + * item of those that match best is selected. + */ + Preselect bool `json:"preselect,omitempty"` + + /*SortText defined: + * A string that should be used when comparing this item + * with other items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + SortText string `json:"sortText,omitempty"` + + /*FilterText defined: + * A string that should be used when filtering a set of + * completion items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + FilterText string `json:"filterText,omitempty"` + + /*InsertText defined: + * A string that should be inserted into a document when selecting + * this completion. When `falsy` the [label](#CompletionItem.label) + * is used. + * + * The `insertText` is subject to interpretation by the client side. + * Some tools might not take the string literally. For example + * VS Code when code complete is requested in this example `con` + * and a completion item with an `insertText` of `console` is provided it + * will only insert `sole`. Therefore it is recommended to use `textEdit` instead + * since it avoids additional client side interpretation. + */ + InsertText string `json:"insertText,omitempty"` + + /*InsertTextFormat defined: + * The format of the insert text. The format applies to both the `insertText` property + * and the `newText` property of a provided `textEdit`. + */ + InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` + + /*TextEdit defined: + * An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored. + * + * *Note:* The text edit's range must be a [single line] and it must contain the position + * at which completion has been requested. + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + + /*AdditionalTextEdits defined: + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this completion. Edits must not overlap (including the same insert position) + * with the main [edit](#CompletionItem.textEdit) nor with themselves. + * + * Additional text edits should be used to change text unrelated to the current cursor position + * (for example adding an import statement at the top of the file if the completion item will + * insert an unqualified type). + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` + + /*CommitCharacters defined: + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + CommitCharacters []string `json:"commitCharacters,omitempty"` + + /*Command defined: + * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property. + */ + Command *Command `json:"command,omitempty"` + + /*Data defined: + * An data entry field that is preserved on a completion item between + * a [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest] + * (#CompletionResolveRequest) + */ + Data interface{} `json:"data,omitempty"` +} + +/*CompletionList defined: + * Represents a collection of [completion items](#CompletionItem) to be presented + * in the editor. + */ +type CompletionList struct { + + /*IsIncomplete defined: + * This list it not complete. Further typing results in recomputing this list. + */ + IsIncomplete bool `json:"isIncomplete"` + + /*Items defined: + * The completion items. + */ + Items []CompletionItem `json:"items"` +} + +/*Hover defined: + * The result of a hover request. + */ +type Hover struct { + + /*Contents defined: + * The hover's content + */ + Contents MarkupContent `json:"contents"` // MarkupContent | MarkedString | MarkedString[] + + /*Range defined: + * An optional range + */ + Range *Range `json:"range,omitempty"` +} + +/*ParameterInformation defined: + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +type ParameterInformation struct { + + /*Label defined: + * The label of this parameter information. + * + * Either a string or an inclusive start and exclusive end offsets within its containing + * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + * string representation as `Position` and `Range` does. + * + * *Note*: a label of type string should be a substring of its containing signature label. + * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + */ + Label string `json:"label"` // string | [number, number] + + /*Documentation defined: + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent +} + +/*SignatureInformation defined: + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +type SignatureInformation struct { + + /*Label defined: + * The label of this signature. Will be shown in + * the UI. + */ + Label string `json:"label"` + + /*Documentation defined: + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent + + /*Parameters defined: + * The parameters of this signature. + */ + Parameters []ParameterInformation `json:"parameters,omitempty"` +} + +/*SignatureHelp defined: + * Signature help represents the signature of something + * callable. There can be multiple signature but only one + * active and only one active parameter. + */ +type SignatureHelp struct { + + /*Signatures defined: + * One or more signatures. + */ + Signatures []SignatureInformation `json:"signatures"` + + /*ActiveSignature defined: + * The active signature. Set to `null` if no + * signatures exist. + */ + ActiveSignature float64 `json:"activeSignature"` + + /*ActiveParameter defined: + * The active parameter of the active signature. Set to `null` + * if the active signature has no parameters. + */ + ActiveParameter float64 `json:"activeParameter"` +} + +/*ReferenceContext defined: + * Value-object that contains additional information when + * requesting references. + */ +type ReferenceContext struct { + + /*IncludeDeclaration defined: + * Include the declaration of the current symbol. + */ + IncludeDeclaration bool `json:"includeDeclaration"` +} + +/*DocumentHighlight defined: + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +type DocumentHighlight struct { + + /*Range defined: + * The range this highlight applies to. + */ + Range Range `json:"range"` + + /*Kind defined: + * The highlight kind, default is [text](#DocumentHighlightKind.Text). + */ + Kind *DocumentHighlightKind `json:"kind,omitempty"` +} + +/*SymbolInformation defined: + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ +type SymbolInformation struct { + + /*Name defined: + * The name of this symbol. + */ + Name string `json:"name"` + + /*Kind defined: + * The kind of this symbol. + */ + Kind SymbolKind `json:"kind"` + + /*Deprecated defined: + * Indicates if this symbol is deprecated. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Location defined: + * The location of this symbol. The location's range is used by a tool + * to reveal the location in the editor. If the symbol is selected in the + * tool the range's start information is used to position the cursor. So + * the range usually spans more than the actual symbol's name and does + * normally include thinks like visibility modifiers. + * + * The range doesn't have to denote a node range in the sense of a abstract + * syntax tree. It can therefore not be used to re-construct a hierarchy of + * the symbols. + */ + Location Location `json:"location"` + + /*ContainerName defined: + * The name of the symbol containing this symbol. This information is for + * user interface purposes (e.g. to render a qualifier in the user interface + * if necessary). It can't be used to re-infer a hierarchy for the document + * symbols. + */ + ContainerName string `json:"containerName,omitempty"` +} + +/*DocumentSymbol defined: + * Represents programming constructs like variables, classes, interfaces etc. + * that appear in a document. Document symbols can be hierarchical and they + * have two ranges: one that encloses its definition and one that points to + * its most interesting range, e.g. the range of an identifier. + */ +type DocumentSymbol struct { + + /*Name defined: + * The name of this symbol. Will be displayed in the user interface and therefore must not be + * an empty string or a string only consisting of white spaces. + */ + Name string `json:"name"` + + /*Detail defined: + * More detail for this symbol, e.g the signature of a function. + */ + Detail string `json:"detail,omitempty"` + + /*Kind defined: + * The kind of this symbol. + */ + Kind SymbolKind `json:"kind"` + + /*Deprecated defined: + * Indicates if this symbol is deprecated. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Range defined: + * The range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the clients cursor is + * inside the symbol to reveal in the symbol in the UI. + */ + Range Range `json:"range"` + + /*SelectionRange defined: + * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. + * Must be contained by the `range`. + */ + SelectionRange Range `json:"selectionRange"` + + /*Children defined: + * Children of this symbol, e.g. properties of a class. + */ + Children []DocumentSymbol `json:"children,omitempty"` +} + +/*CodeActionContext defined: + * Contains additional diagnostic information about the context in which + * a [code action](#CodeActionProvider.provideCodeActions) is run. + */ +type CodeActionContext struct { + + /*Diagnostics defined: + * An array of diagnostics known on the client side overlapping the range provided to the + * `textDocument/codeAction` request. They are provided so that the server knows which + * errors are currently presented to the user for the given range. There is no guarantee + * that these accurately reflect the error state of the resource. The primary parameter + * to compute code actions is the provided range. + */ + Diagnostics []Diagnostic `json:"diagnostics"` + + /*Only defined: + * Requested kind of actions to return. + * + * Actions not of this kind are filtered out by the client before being shown. So servers + * can omit computing them. + */ + Only []CodeActionKind `json:"only,omitempty"` +} + +/*CodeAction defined: + * A code action represents a change that can be performed in code, e.g. to fix a problem or + * to refactor code. + * + * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. + */ +type CodeAction struct { + + /*Title defined: + * A short, human-readable, title for this code action. + */ + Title string `json:"title"` + + /*Kind defined: + * The kind of the code action. + * + * Used to filter code actions. + */ + Kind CodeActionKind `json:"kind,omitempty"` + + /*Diagnostics defined: + * The diagnostics that this code action resolves. + */ + Diagnostics []Diagnostic `json:"diagnostics,omitempty"` + + /*IsPreferred defined: + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted + * by keybindings. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + * + * @since 3.15.0 + */ + IsPreferred bool `json:"isPreferred,omitempty"` + + /*Edit defined: + * The workspace edit this code action performs. + */ + Edit *WorkspaceEdit `json:"edit,omitempty"` + + /*Command defined: + * A command this code action executes. If a code action + * provides a edit and a command, first the edit is + * executed and then the command. + */ + Command *Command `json:"command,omitempty"` +} + +/*CodeLens defined: + * A code lens represents a [command](#Command) that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done to two stages. + */ +type CodeLens struct { + + /*Range defined: + * The range in which this code lens is valid. Should only span a single line. + */ + Range Range `json:"range"` + + /*Command defined: + * The command this code lens represents. + */ + Command *Command `json:"command,omitempty"` + + /*Data defined: + * An data entry field that is preserved on a code lens item between + * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest] + * (#CodeLensResolveRequest) + */ + Data interface{} `json:"data,omitempty"` +} + +/*FormattingOptions defined: + * Value-object describing what options formatting should use. + */ +type FormattingOptions struct { + + /*TabSize defined: + * Size of a tab in spaces. + */ + TabSize float64 `json:"tabSize"` + + /*InsertSpaces defined: + * Prefer spaces over tabs. + */ + InsertSpaces bool `json:"insertSpaces"` + + /*TrimTrailingWhitespace defined: + * Trim trailing whitespaces on a line. + * + * @since 3.15.0 + */ + TrimTrailingWhitespace bool `json:"trimTrailingWhitespace,omitempty"` + + /*InsertFinalNewline defined: + * Insert a newline character at the end of the file if one does not exist. + * + * @since 3.15.0 + */ + InsertFinalNewline bool `json:"insertFinalNewline,omitempty"` + + /*TrimFinalNewlines defined: + * Trim all newlines after the final newline at the end of the file. + * + * @since 3.15.0 + */ + TrimFinalNewlines bool `json:"trimFinalNewlines,omitempty"` + + /*Key defined: + * Signature for further properties. + */ + Key map[string]bool `json:"key"` // [key: string]: boolean | number | string | undefined; +} + +/*DocumentLink defined: + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ +type DocumentLink struct { + + /*Range defined: + * The range this link applies to. + */ + Range Range `json:"range"` + + /*Target defined: + * The uri this link points to. + */ + Target string `json:"target,omitempty"` + + /*Tooltip defined: + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on how to + * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + * user settings, and localization. + * + * @since 3.15.0 + */ + Tooltip string `json:"tooltip,omitempty"` + + /*Data defined: + * A data entry field that is preserved on a document link between a + * DocumentLinkRequest and a DocumentLinkResolveRequest. + */ + Data interface{} `json:"data,omitempty"` +} + +/*SelectionRange defined: + * A selection range represents a part of a selection hierarchy. A selection range + * may have a parent selection range that contains it. + */ +type SelectionRange struct { + + /*Range defined: + * The [range](#Range) of this selection range. + */ + Range Range `json:"range"` + + /*Parent defined: + * The parent selection range containing this range. Therefore `parent.range` must contain `this.range`. + */ + Parent *SelectionRange `json:"parent,omitempty"` +} + +/*TextDocument defined: + * A simple text document. Not to be implemented. + */ +type TextDocument struct { + + /*URI defined: + * The associated URI for this document. Most documents have the __file__-scheme, indicating that they + * represent files on disk. However, some documents may have other schemes indicating that they are not + * available on disk. + * + * @readonly + */ + URI DocumentURI `json:"uri"` + + /*LanguageID defined: + * The identifier of the language associated with this document. + * + * @readonly + */ + LanguageID string `json:"languageId"` + + /*Version defined: + * The version number of this document (it will increase after each + * change, including undo/redo). + * + * @readonly + */ + Version float64 `json:"version"` + + /*LineCount defined: + * The number of lines in this document. + * + * @readonly + */ + LineCount float64 `json:"lineCount"` +} + +/*TextDocumentChangeEvent defined: + * Event to signal changes to a simple text document. + */ +type TextDocumentChangeEvent struct { + + /*Document defined: + * The document that has changed. + */ + Document TextDocument `json:"document"` +} + +// TextDocumentWillSaveEvent is +type TextDocumentWillSaveEvent struct { + + /*Document defined: + * The document that will be saved + */ + Document TextDocument `json:"document"` + + /*Reason defined: + * The reason why save was triggered. + */ + Reason TextDocumentSaveReason `json:"reason"` +} + +/*TextDocumentContentChangeEvent defined: + * An event describing a change to a text document. If range and rangeLength are omitted + * the new text is considered to be the full content of the document. + */ +type TextDocumentContentChangeEvent struct { + + /*Range defined: + * The range of the document that changed. + */ + Range *Range `json:"range,omitempty"` + + /*RangeLength defined: + * The length of the range that got replaced. + */ + RangeLength float64 `json:"rangeLength,omitempty"` + + /*Text defined: + * The new text of the document. + */ + Text string `json:"text"` +} + +// ProgressParams is +type ProgressParams struct { + + /*Token defined: + * The progress token provided by the client or server. + */ + Token ProgressToken `json:"token"` + + /*Value defined: + * The progress data. + */ + Value interface{} `json:"value"` +} + +// SetTraceParams is +type SetTraceParams struct { + + // Value is + Value TraceValues `json:"value"` +} + +// LogTraceParams is +type LogTraceParams struct { + + // Message is + Message string `json:"message"` + + // Verbose is + Verbose string `json:"verbose,omitempty"` +} + +// Tracer is +type Tracer struct { +} + +// FoldingRangeKind defines constants +type FoldingRangeKind string + +// ResourceOperationKind defines constants +type ResourceOperationKind string + +// FailureHandlingKind defines constants +type FailureHandlingKind string + +// InitializeError defines constants +type InitializeError float64 + +// MessageType defines constants +type MessageType float64 + +// TextDocumentSyncKind defines constants +type TextDocumentSyncKind float64 + +// FileChangeType defines constants +type FileChangeType float64 + +// WatchKind defines constants +type WatchKind float64 + +// CompletionTriggerKind defines constants +type CompletionTriggerKind float64 + +// SignatureHelpTriggerKind defines constants +type SignatureHelpTriggerKind float64 + +// DiagnosticSeverity defines constants +type DiagnosticSeverity float64 + +// DiagnosticTag defines constants +type DiagnosticTag float64 + +// MarkupKind defines constants +type MarkupKind string + +// CompletionItemKind defines constants +type CompletionItemKind float64 + +// InsertTextFormat defines constants +type InsertTextFormat float64 + +// CompletionItemTag defines constants +type CompletionItemTag float64 + +// DocumentHighlightKind defines constants +type DocumentHighlightKind float64 + +// SymbolKind defines constants +type SymbolKind float64 + +// CodeActionKind defines constants +type CodeActionKind string + +// TextDocumentSaveReason defines constants +type TextDocumentSaveReason float64 + +// ErrorCodes defines constants +type ErrorCodes float64 + +// Touch defines constants +type Touch float64 + +// Trace defines constants +type Trace string + +// TraceFormat defines constants +type TraceFormat string + +// ConnectionErrors defines constants +type ConnectionErrors float64 + +// ConnectionState defines constants +type ConnectionState float64 + +const ( + + /*Comment defined: + * Folding range for a comment + */ + Comment FoldingRangeKind = "comment" + + /*Imports defined: + * Folding range for a imports or includes + */ + Imports FoldingRangeKind = "imports" + + /*Region defined: + * Folding range for a region (e.g. `#region`) + */ + Region FoldingRangeKind = "region" + + /*Create defined: + * Supports creating new files and folders. + */ + Create ResourceOperationKind = "create" + + /*Rename defined: + * Supports renaming existing files and folders. + */ + Rename ResourceOperationKind = "rename" + + /*Delete defined: + * Supports deleting existing files and folders. + */ + Delete ResourceOperationKind = "delete" + + /*Abort defined: + * Applying the workspace change is simply aborted if one of the changes provided + * fails. All operations executed before the failing operation stay executed. + */ + Abort FailureHandlingKind = "abort" + + /*Transactional defined: + * All operations are executed transactional. That means they either all + * succeed or no changes at all are applied to the workspace. + */ + Transactional FailureHandlingKind = "transactional" + + /*TextOnlyTransactional defined: + * If the workspace edit contains only textual file changes they are executed transactional. + * If resource changes (create, rename or delete file) are part of the change the failure + * handling strategy is abort. + */ + TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" + + /*Undo defined: + * The client tries to undo the operations already executed. But there is no + * guarantee that this is succeeding. + */ + Undo FailureHandlingKind = "undo" + + /*UnknownProtocolVersion defined: + * If the protocol version provided by the client can't be handled by the server. + * @deprecated This initialize error got replaced by client capabilities. There is + * no version handshake in version 3.0x + */ + UnknownProtocolVersion InitializeError = 1 + + /*Error defined: + * An error message. + */ + Error MessageType = 1 + + /*Warning defined: + * A warning message. + */ + Warning MessageType = 2 + + /*Info defined: + * An information message. + */ + Info MessageType = 3 + + /*Log defined: + * A log message. + */ + Log MessageType = 4 + + /*None defined: + * Documents should not be synced at all. + */ + None TextDocumentSyncKind = 0 + + /*Full defined: + * Documents are synced by always sending the full content + * of the document. + */ + Full TextDocumentSyncKind = 1 + + /*Incremental defined: + * Documents are synced by sending the full content on open. + * After that only incremental updates to the document are + * send. + */ + Incremental TextDocumentSyncKind = 2 + + /*Created defined: + * The file got created. + */ + Created FileChangeType = 1 + + /*Changed defined: + * The file got changed. + */ + Changed FileChangeType = 2 + + /*Deleted defined: + * The file got deleted. + */ + Deleted FileChangeType = 3 + + /*WatchCreate defined: + * Interested in create events. + */ + WatchCreate WatchKind = 1 + + /*WatchChange defined: + * Interested in change events + */ + WatchChange WatchKind = 2 + + /*WatchDelete defined: + * Interested in delete events + */ + WatchDelete WatchKind = 4 + + /*Invoked defined: + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ + Invoked CompletionTriggerKind = 1 + + /*TriggerCharacter defined: + * Completion was triggered by a trigger character specified by + * the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + */ + TriggerCharacter CompletionTriggerKind = 2 + + /*TriggerForIncompleteCompletions defined: + * Completion was re-triggered as current completion list is incomplete + */ + TriggerForIncompleteCompletions CompletionTriggerKind = 3 + + /*ContentChange defined: + * Signature help was triggered by the cursor moving or by the document content changing. + */ + ContentChange SignatureHelpTriggerKind = 3 + + /*SeverityError defined: + * Reports an error. + */ + SeverityError DiagnosticSeverity = 1 + + /*SeverityWarning defined: + * Reports a warning. + */ + SeverityWarning DiagnosticSeverity = 2 + + /*SeverityInformation defined: + * Reports an information. + */ + SeverityInformation DiagnosticSeverity = 3 + + /*SeverityHint defined: + * Reports a hint. + */ + SeverityHint DiagnosticSeverity = 4 + + /*Unnecessary defined: + * Unused or unnecessary code. + * + * Clients are allowed to render diagnostics with this tag faded out instead of having + * an error squiggle. + */ + Unnecessary DiagnosticTag = 1 + + /*Deprecated defined: + * Deprecated or obsolete code. + * + * Clients are allowed to rendered diagnostics with this tag strike through. + */ + Deprecated DiagnosticTag = 2 + + /*PlainText defined: + * Plain text is supported as a content format + */ + PlainText MarkupKind = "plaintext" + + /*Markdown defined: + * Markdown is supported as a content format + */ + Markdown MarkupKind = "markdown" + + // TextCompletion is + TextCompletion CompletionItemKind = 1 + + // MethodCompletion is + MethodCompletion CompletionItemKind = 2 + + // FunctionCompletion is + FunctionCompletion CompletionItemKind = 3 + + // ConstructorCompletion is + ConstructorCompletion CompletionItemKind = 4 + + // FieldCompletion is + FieldCompletion CompletionItemKind = 5 + + // VariableCompletion is + VariableCompletion CompletionItemKind = 6 + + // ClassCompletion is + ClassCompletion CompletionItemKind = 7 + + // InterfaceCompletion is + InterfaceCompletion CompletionItemKind = 8 + + // ModuleCompletion is + ModuleCompletion CompletionItemKind = 9 + + // PropertyCompletion is + PropertyCompletion CompletionItemKind = 10 + + // UnitCompletion is + UnitCompletion CompletionItemKind = 11 + + // ValueCompletion is + ValueCompletion CompletionItemKind = 12 + + // EnumCompletion is + EnumCompletion CompletionItemKind = 13 + + // KeywordCompletion is + KeywordCompletion CompletionItemKind = 14 + + // SnippetCompletion is + SnippetCompletion CompletionItemKind = 15 + + // ColorCompletion is + ColorCompletion CompletionItemKind = 16 + + // FileCompletion is + FileCompletion CompletionItemKind = 17 + + // ReferenceCompletion is + ReferenceCompletion CompletionItemKind = 18 + + // FolderCompletion is + FolderCompletion CompletionItemKind = 19 + + // EnumMemberCompletion is + EnumMemberCompletion CompletionItemKind = 20 + + // ConstantCompletion is + ConstantCompletion CompletionItemKind = 21 + + // StructCompletion is + StructCompletion CompletionItemKind = 22 + + // EventCompletion is + EventCompletion CompletionItemKind = 23 + + // OperatorCompletion is + OperatorCompletion CompletionItemKind = 24 + + // TypeParameterCompletion is + TypeParameterCompletion CompletionItemKind = 25 + + /*PlainTextTextFormat defined: + * The primary text to be inserted is treated as a plain string. + */ + PlainTextTextFormat InsertTextFormat = 1 + + /*SnippetTextFormat defined: + * The primary text to be inserted is treated as a snippet. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + * + * See also: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md + */ + SnippetTextFormat InsertTextFormat = 2 + + /*Text defined: + * A textual occurrence. + */ + Text DocumentHighlightKind = 1 + + /*Read defined: + * Read-access of a symbol, like reading a variable. + */ + Read DocumentHighlightKind = 2 + + /*Write defined: + * Write-access of a symbol, like writing to a variable. + */ + Write DocumentHighlightKind = 3 + + // File is + File SymbolKind = 1 + + // Module is + Module SymbolKind = 2 + + // Namespace is + Namespace SymbolKind = 3 + + // Package is + Package SymbolKind = 4 + + // Class is + Class SymbolKind = 5 + + // Method is + Method SymbolKind = 6 + + // Property is + Property SymbolKind = 7 + + // Field is + Field SymbolKind = 8 + + // Constructor is + Constructor SymbolKind = 9 + + // Enum is + Enum SymbolKind = 10 + + // Interface is + Interface SymbolKind = 11 + + // Function is + Function SymbolKind = 12 + + // Variable is + Variable SymbolKind = 13 + + // Constant is + Constant SymbolKind = 14 + + // String is + String SymbolKind = 15 + + // Number is + Number SymbolKind = 16 + + // Boolean is + Boolean SymbolKind = 17 + + // Array is + Array SymbolKind = 18 + + // Object is + Object SymbolKind = 19 + + // Key is + Key SymbolKind = 20 + + // Null is + Null SymbolKind = 21 + + // EnumMember is + EnumMember SymbolKind = 22 + + // Struct is + Struct SymbolKind = 23 + + // Event is + Event SymbolKind = 24 + + // Operator is + Operator SymbolKind = 25 + + // TypeParameter is + TypeParameter SymbolKind = 26 + + /*Empty defined: + * Empty kind. + */ + Empty CodeActionKind = "" + + /*QuickFix defined: + * Base kind for quickfix actions: 'quickfix' + */ + QuickFix CodeActionKind = "quickfix" + + /*Refactor defined: + * Base kind for refactoring actions: 'refactor' + */ + Refactor CodeActionKind = "refactor" + + /*RefactorExtract defined: + * Base kind for refactoring extraction actions: 'refactor.extract' + * + * Example extract actions: + * + * - Extract method + * - Extract function + * - Extract variable + * - Extract interface from class + * - ... + */ + RefactorExtract CodeActionKind = "refactor.extract" + + /*RefactorInline defined: + * Base kind for refactoring inline actions: 'refactor.inline' + * + * Example inline actions: + * + * - Inline function + * - Inline variable + * - Inline constant + * - ... + */ + RefactorInline CodeActionKind = "refactor.inline" + + /*RefactorRewrite defined: + * Base kind for refactoring rewrite actions: 'refactor.rewrite' + * + * Example rewrite actions: + * + * - Convert JavaScript function to class + * - Add or remove parameter + * - Encapsulate field + * - Make method static + * - Move method to base class + * - ... + */ + RefactorRewrite CodeActionKind = "refactor.rewrite" + + /*Source defined: + * Base kind for source actions: `source` + * + * Source code actions apply to the entire file. + */ + Source CodeActionKind = "source" + + /*SourceOrganizeImports defined: + * Base kind for an organize imports source action: `source.organizeImports` + */ + SourceOrganizeImports CodeActionKind = "source.organizeImports" + + /*Manual defined: + * Manually triggered, e.g. by the user pressing save, by starting debugging, + * or by an API call. + */ + Manual TextDocumentSaveReason = 1 + + /*AfterDelay defined: + * Automatic after a delay. + */ + AfterDelay TextDocumentSaveReason = 2 + + /*FocusOut defined: + * When the editor lost focus. + */ + FocusOut TextDocumentSaveReason = 3 + + // MessageWriteError is + MessageWriteError ErrorCodes = 1 + + // MessageReadError is + MessageReadError ErrorCodes = 2 + + // First is + First Touch = 1 + + // Last is + Last Touch = 2 + + // JSON is + JSON TraceFormat = "json" + + /*Closed defined: + * The connection is closed. + */ + Closed ConnectionErrors = 1 + + /*Disposed defined: + * The connection got disposed. + */ + Disposed ConnectionErrors = 2 + + /*AlreadyListening defined: + * The connection is already in listening mode. + */ + AlreadyListening ConnectionErrors = 3 + + // New is + New ConnectionState = 1 + + // Listening is + Listening ConnectionState = 2 +) + +// DocumentFilter is a type +/** + * A document filter denotes a document by different properties like + * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of + * its resource, or a glob-pattern that is applied to the [path](#TextDocument.fileName). + * + * Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * @sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }` + * @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` + */ +type DocumentFilter = struct { + + /*Language defined: A language id, like `typescript`. */ + Language string `json:"language,omitempty"` + + /*Scheme defined: A Uri [scheme](#Uri.scheme), like `file` or `untitled`. */ + Scheme string `json:"scheme,omitempty"` + + /*Pattern defined: A glob pattern, like `*.{ts,js}`. */ + Pattern string `json:"pattern,omitempty"` +} + +// DocumentSelector is a type +/** + * A document selector is the combination of one or many document filters. + * + * @sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; + */ +type DocumentSelector = []DocumentFilter + +// DocumentURI is a type +/** + * A tagging type for string properties that are actually URIs. + */ +type DocumentURI = string + +// MarkedString is a type +/** + * MarkedString can be used to render human readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. The language identifier + * is semantically equal to the optional language identifier in fenced code blocks in GitHub + * issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * The pair of a language and a value is an equivalent to markdown: + * ```${language} + * ${value} + * ``` + * + * Note that markdown strings will be sanitized - that means html will be escaped. + * @deprecated use MarkupContent instead. + */ +type MarkedString = string + +// DefinitionLink is a type +/** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ +type DefinitionLink = LocationLink + +// DeclarationLink is a type +/** + * Information about where a symbol is declared. + * + * Provides additional metadata over normal [location](#Location) declarations, including the range of + * the declaring symbol. + * + * Servers should prefer returning `DeclarationLink` over `Declaration` if supported + * by the client. + */ +type DeclarationLink = LocationLink + +// LSPMessageType is a type +/** + * A LSP Log Entry. + */ +type LSPMessageType = string + +// ProgressToken is a type +type ProgressToken = interface{} // number | string +// TraceValues is a type +type TraceValues = string diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go new file mode 100644 index 00000000..37e8c6a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go @@ -0,0 +1,842 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" +) + +type Server interface { + DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error + Initialized(context.Context, *InitializedParams) error + Exit(context.Context) error + DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error + DidOpen(context.Context, *DidOpenTextDocumentParams) error + DidChange(context.Context, *DidChangeTextDocumentParams) error + DidClose(context.Context, *DidCloseTextDocumentParams) error + DidSave(context.Context, *DidSaveTextDocumentParams) error + WillSave(context.Context, *WillSaveTextDocumentParams) error + DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error + Progress(context.Context, *ProgressParams) error + SetTraceNotification(context.Context, *SetTraceParams) error + LogTraceNotification(context.Context, *LogTraceParams) error + Implementation(context.Context, *ImplementationParams) ([]Location, error) + TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error) + DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error) + ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error) + FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error) + Declaration(context.Context, *DeclarationParams) ([]DeclarationLink, error) + SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error) + Initialize(context.Context, *ParamInitia) (*InitializeResult, error) + Shutdown(context.Context) error + WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error) + Completion(context.Context, *CompletionParams) (*CompletionList, error) + Resolve(context.Context, *CompletionItem) (*CompletionItem, error) + Hover(context.Context, *HoverParams) (*Hover, error) + SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error) + Definition(context.Context, *DefinitionParams) ([]Location, error) + References(context.Context, *ReferenceParams) ([]Location, error) + DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error) + DocumentSymbol(context.Context, *DocumentSymbolParams) ([]DocumentSymbol, error) + CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error) + Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error) + CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error) + ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error) + DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error) + ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error) + Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error) + RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error) + OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error) + Rename(context.Context, *RenameParams) (*WorkspaceEdit, error) + PrepareRename(context.Context, *PrepareRenameParams) (*Range, error) + ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) +} + +func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + if ctx.Err() != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, "")) + return true + } + switch r.Method { + case "workspace/didChangeWorkspaceFolders": // notif + var params DidChangeWorkspaceFoldersParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeWorkspaceFolders(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "initialized": // notif + var params InitializedParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.Initialized(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "exit": // notif + if err := h.server.Exit(ctx); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/didChangeConfiguration": // notif + var params DidChangeConfigurationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeConfiguration(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didOpen": // notif + var params DidOpenTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidOpen(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didChange": // notif + var params DidChangeTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChange(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didClose": // notif + var params DidCloseTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidClose(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didSave": // notif + var params DidSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidSave(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/willSave": // notif + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.WillSave(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/didChangeWatchedFiles": // notif + var params DidChangeWatchedFilesParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeWatchedFiles(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/progress": // notif + var params ProgressParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.Progress(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/setTraceNotification": // notif + var params SetTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.SetTraceNotification(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/logTraceNotification": // notif + var params LogTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.LogTraceNotification(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/implementation": // req + var params ImplementationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Implementation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/typeDefinition": // req + var params TypeDefinitionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.TypeDefinition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentColor": // req + var params DocumentColorParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentColor(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/colorPresentation": // req + var params ColorPresentationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ColorPresentation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/foldingRange": // req + var params FoldingRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.FoldingRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/declaration": // req + var params DeclarationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Declaration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/selectionRange": // req + var params SelectionRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.SelectionRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "initialize": // req + var params ParamInitia + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Initialize(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "shutdown": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + err := h.server.Shutdown(ctx) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/willSaveWaitUntil": // req + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.WillSaveWaitUntil(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/completion": // req + var params CompletionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Completion(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "completionItem/resolve": // req + var params CompletionItem + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Resolve(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/hover": // req + var params HoverParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Hover(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/signatureHelp": // req + var params SignatureHelpParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.SignatureHelp(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/definition": // req + var params DefinitionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Definition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/references": // req + var params ReferenceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.References(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentHighlight": // req + var params DocumentHighlightParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentHighlight(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentSymbol": // req + var params DocumentSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentSymbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/codeAction": // req + var params CodeActionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.CodeAction(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/symbol": // req + var params WorkspaceSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Symbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/codeLens": // req + var params CodeLensParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.CodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "codeLens/resolve": // req + var params CodeLens + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ResolveCodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentLink": // req + var params DocumentLinkParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "documentLink/resolve": // req + var params DocumentLink + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ResolveDocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/formatting": // req + var params DocumentFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Formatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/rangeFormatting": // req + var params DocumentRangeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.RangeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/onTypeFormatting": // req + var params DocumentOnTypeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.OnTypeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/rename": // req + var params RenameParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Rename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/prepareRename": // req + var params PrepareRenameParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.PrepareRename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/executeCommand": // req + var params ExecuteCommandParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ExecuteCommand(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + + default: + return false + } +} + +type serverDispatcher struct { + *jsonrpc2.Conn +} + +func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params) +} + +func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error { + return s.Conn.Notify(ctx, "initialized", params) +} + +func (s *serverDispatcher) Exit(ctx context.Context) error { + return s.Conn.Notify(ctx, "exit", nil) +} + +func (s *serverDispatcher) DidChangeConfiguration(ctx context.Context, params *DidChangeConfigurationParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeConfiguration", params) +} + +func (s *serverDispatcher) DidOpen(ctx context.Context, params *DidOpenTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didOpen", params) +} + +func (s *serverDispatcher) DidChange(ctx context.Context, params *DidChangeTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didChange", params) +} + +func (s *serverDispatcher) DidClose(ctx context.Context, params *DidCloseTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didClose", params) +} + +func (s *serverDispatcher) DidSave(ctx context.Context, params *DidSaveTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didSave", params) +} + +func (s *serverDispatcher) WillSave(ctx context.Context, params *WillSaveTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/willSave", params) +} + +func (s *serverDispatcher) DidChangeWatchedFiles(ctx context.Context, params *DidChangeWatchedFilesParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeWatchedFiles", params) +} + +func (s *serverDispatcher) Progress(ctx context.Context, params *ProgressParams) error { + return s.Conn.Notify(ctx, "$/progress", params) +} + +func (s *serverDispatcher) SetTraceNotification(ctx context.Context, params *SetTraceParams) error { + return s.Conn.Notify(ctx, "$/setTraceNotification", params) +} + +func (s *serverDispatcher) LogTraceNotification(ctx context.Context, params *LogTraceParams) error { + return s.Conn.Notify(ctx, "$/logTraceNotification", params) +} +func (s *serverDispatcher) Implementation(ctx context.Context, params *ImplementationParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/implementation", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) TypeDefinition(ctx context.Context, params *TypeDefinitionParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/typeDefinition", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentColor(ctx context.Context, params *DocumentColorParams) ([]ColorInformation, error) { + var result []ColorInformation + if err := s.Conn.Call(ctx, "textDocument/documentColor", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ColorPresentation(ctx context.Context, params *ColorPresentationParams) ([]ColorPresentation, error) { + var result []ColorPresentation + if err := s.Conn.Call(ctx, "textDocument/colorPresentation", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) FoldingRange(ctx context.Context, params *FoldingRangeParams) ([]FoldingRange, error) { + var result []FoldingRange + if err := s.Conn.Call(ctx, "textDocument/foldingRange", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Declaration(ctx context.Context, params *DeclarationParams) ([]DeclarationLink, error) { + var result []DeclarationLink + if err := s.Conn.Call(ctx, "textDocument/declaration", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) SelectionRange(ctx context.Context, params *SelectionRangeParams) ([]SelectionRange, error) { + var result []SelectionRange + if err := s.Conn.Call(ctx, "textDocument/selectionRange", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitia) (*InitializeResult, error) { + var result InitializeResult + if err := s.Conn.Call(ctx, "initialize", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Shutdown(ctx context.Context) error { + return s.Conn.Call(ctx, "shutdown", nil, nil) +} + +func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSaveTextDocumentParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/willSaveWaitUntil", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList, error) { + var result CompletionList + if err := s.Conn.Call(ctx, "textDocument/completion", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) (*CompletionItem, error) { + var result CompletionItem + if err := s.Conn.Call(ctx, "completionItem/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover, error) { + var result Hover + if err := s.Conn.Call(ctx, "textDocument/hover", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp, error) { + var result SignatureHelp + if err := s.Conn.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/definition", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/references", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentHighlight(ctx context.Context, params *DocumentHighlightParams) ([]DocumentHighlight, error) { + var result []DocumentHighlight + if err := s.Conn.Call(ctx, "textDocument/documentHighlight", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentSymbol(ctx context.Context, params *DocumentSymbolParams) ([]DocumentSymbol, error) { + var result []DocumentSymbol + if err := s.Conn.Call(ctx, "textDocument/documentSymbol", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) CodeAction(ctx context.Context, params *CodeActionParams) ([]CodeAction, error) { + var result []CodeAction + if err := s.Conn.Call(ctx, "textDocument/codeAction", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation, error) { + var result []SymbolInformation + if err := s.Conn.Call(ctx, "workspace/symbol", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens, error) { + var result []CodeLens + if err := s.Conn.Call(ctx, "textDocument/codeLens", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ResolveCodeLens(ctx context.Context, params *CodeLens) (*CodeLens, error) { + var result CodeLens + if err := s.Conn.Call(ctx, "codeLens/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) DocumentLink(ctx context.Context, params *DocumentLinkParams) ([]DocumentLink, error) { + var result []DocumentLink + if err := s.Conn.Call(ctx, "textDocument/documentLink", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ResolveDocumentLink(ctx context.Context, params *DocumentLink) (*DocumentLink, error) { + var result DocumentLink + if err := s.Conn.Call(ctx, "documentLink/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Formatting(ctx context.Context, params *DocumentFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/formatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) RangeFormatting(ctx context.Context, params *DocumentRangeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/rangeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) OnTypeFormatting(ctx context.Context, params *DocumentOnTypeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/onTypeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*WorkspaceEdit, error) { + var result WorkspaceEdit + if err := s.Conn.Call(ctx, "textDocument/rename", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range, error) { + var result Range + if err := s.Conn.Call(ctx, "textDocument/prepareRename", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{}, error) { + var result interface{} + if err := s.Conn.Call(ctx, "workspace/executeCommand", params, &result); err != nil { + return nil, err + } + return result, nil +} + +type CancelParams struct { + /** + * The request id to cancel. + */ + ID jsonrpc2.ID `json:"id"` +} + +// Types constructed to avoid structs as formal argument types +type ParamInitia struct { + InitializeParams + WorkDoneProgressParams +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/parse.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/parse.go new file mode 100644 index 00000000..ec393c2a --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/parse.go @@ -0,0 +1,110 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "strconv" + "strings" + "unicode/utf8" +) + +// Parse returns the location represented by the input. +// All inputs are valid locations, as they can always be a pure filename. +// The returned span will be normalized, and thus if printed may produce a +// different string. +func Parse(input string) Span { + // :0:0#0-0:0#0 + valid := input + var hold, offset int + hadCol := false + suf := rstripSuffix(input) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep == ":" { + valid = suf.remains + hold = suf.num + hadCol = true + suf = rstripSuffix(suf.remains) + } + switch { + case suf.sep == ":": + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) + case suf.sep == "-": + // we have a span, fall out of the case to continue + default: + // separator not valid, rewind to either the : or the start + return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) + } + // only the span form can get here + // at this point we still don't know what the numbers we have mean + // if have not yet seen a : then we might have either a line or a column depending + // on whether start has a column or not + // we build an end point and will fix it later if needed + end := NewPoint(suf.num, hold, offset) + hold, offset = 0, 0 + suf = rstripSuffix(suf.remains) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep != ":" { + // turns out we don't have a span after all, rewind + return New(NewURI(valid), end, Point{}) + } + valid = suf.remains + hold = suf.num + suf = rstripSuffix(suf.remains) + if suf.sep != ":" { + // line#offset only + return New(NewURI(valid), NewPoint(hold, 0, offset), end) + } + // we have a column, so if end only had one number, it is also the column + if !hadCol { + end = NewPoint(suf.num, end.v.Line, end.v.Offset) + } + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) +} + +type suffix struct { + remains string + sep string + num int +} + +func rstripSuffix(input string) suffix { + if len(input) == 0 { + return suffix{"", "", -1} + } + remains := input + num := -1 + // first see if we have a number at the end + last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) + if last >= 0 && last < len(remains)-1 { + number, err := strconv.ParseInt(remains[last+1:], 10, 64) + if err == nil { + num = int(number) + remains = remains[:last+1] + } + } + // now see if we have a trailing separator + r, w := utf8.DecodeLastRuneInString(remains) + if r != ':' && r != '#' && r == '#' { + return suffix{input, "", -1} + } + remains = remains[:len(remains)-w] + return suffix{remains, string(r), num} +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/span.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/span.go new file mode 100644 index 00000000..aefd4f3f --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/span.go @@ -0,0 +1,295 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package span contains support for representing with positions and ranges in +// text files. +package span + +import ( + "encoding/json" + "fmt" + "path" +) + +// Span represents a source code range in standardized form. +type Span struct { + v span +} + +// Point represents a single point within a file. +// In general this should only be used as part of a Span, as on its own it +// does not carry enough information. +type Point struct { + v point +} + +type span struct { + URI URI `json:"uri"` + Start point `json:"start"` + End point `json:"end"` +} + +type point struct { + Line int `json:"line"` + Column int `json:"column"` + Offset int `json:"offset"` +} + +// Invalid is a span that reports false from IsValid +var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} + +var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} + +// Converter is the interface to an object that can convert between line:column +// and offset forms for a single file. +type Converter interface { + //ToPosition converts from an offset to a line:column pair. + ToPosition(offset int) (int, int, error) + //ToOffset converts from a line:column pair to an offset. + ToOffset(line, col int) (int, error) +} + +func New(uri URI, start Point, end Point) Span { + s := Span{v: span{URI: uri, Start: start.v, End: end.v}} + s.v.clean() + return s +} + +func NewPoint(line, col, offset int) Point { + p := Point{v: point{Line: line, Column: col, Offset: offset}} + p.v.clean() + return p +} + +func Compare(a, b Span) int { + if r := CompareURI(a.URI(), b.URI()); r != 0 { + return r + } + if r := comparePoint(a.v.Start, b.v.Start); r != 0 { + return r + } + return comparePoint(a.v.End, b.v.End) +} + +func ComparePoint(a, b Point) int { + return comparePoint(a.v, b.v) +} + +func comparePoint(a, b point) int { + if !a.hasPosition() { + if a.Offset < b.Offset { + return -1 + } + if a.Offset > b.Offset { + return 1 + } + return 0 + } + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Column < b.Column { + return -1 + } + if a.Column > b.Column { + return 1 + } + return 0 +} + +func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +func (s Span) IsValid() bool { return s.v.Start.isValid() } +func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +func (s Span) URI() URI { return s.v.URI } +func (s Span) Start() Point { return Point{s.v.Start} } +func (s Span) End() Point { return Point{s.v.End} } +func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } + +func (p Point) HasPosition() bool { return p.v.hasPosition() } +func (p Point) HasOffset() bool { return p.v.hasOffset() } +func (p Point) IsValid() bool { return p.v.isValid() } +func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +func (p Point) Line() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Line +} +func (p Point) Column() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Column +} +func (p Point) Offset() int { + if !p.v.hasOffset() { + panic(fmt.Errorf("offset not set in %v", p.v)) + } + return p.v.Offset +} + +func (p point) hasPosition() bool { return p.Line > 0 } +func (p point) hasOffset() bool { return p.Offset >= 0 } +func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +func (p point) isZero() bool { + return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +} + +func (s *span) clean() { + //this presumes the points are already clean + if !s.End.isValid() || (s.End == point{}) { + s.End = s.Start + } +} + +func (p *point) clean() { + if p.Line < 0 { + p.Line = 0 + } + if p.Column <= 0 { + if p.Line > 0 { + p.Column = 1 + } else { + p.Column = 0 + } + } + if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { + p.Offset = -1 + } +} + +// Format implements fmt.Formatter to print the Location in a standard form. +// The format produced is one that can be read back in using Parse. +func (s Span) Format(f fmt.State, c rune) { + fullForm := f.Flag('+') + preferOffset := f.Flag('#') + // we should always have a uri, simplify if it is file format + //TODO: make sure the end of the uri is unambiguous + uri := string(s.v.URI) + if c == 'f' { + uri = path.Base(uri) + } else if !fullForm { + uri = s.v.URI.Filename() + } + fmt.Fprint(f, uri) + if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { + return + } + // see which bits of start to write + printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) + printLine := s.HasPosition() && (fullForm || !printOffset) + printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) + fmt.Fprint(f, ":") + if printLine { + fmt.Fprintf(f, "%d", s.v.Start.Line) + } + if printColumn { + fmt.Fprintf(f, ":%d", s.v.Start.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.Start.Offset) + } + // start is written, do we need end? + if s.IsPoint() { + return + } + // we don't print the line if it did not change + printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) + fmt.Fprint(f, "-") + if printLine { + fmt.Fprintf(f, "%d", s.v.End.Line) + } + if printColumn { + if printLine { + fmt.Fprint(f, ":") + } + fmt.Fprintf(f, "%d", s.v.End.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.End.Offset) + } +} + +func (s Span) WithPosition(c Converter) (Span, error) { + if err := s.update(c, true, false); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithOffset(c Converter) (Span, error) { + if err := s.update(c, false, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithAll(c Converter) (Span, error) { + if err := s.update(c, true, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s *Span) update(c Converter, withPos, withOffset bool) error { + if !s.IsValid() { + return fmt.Errorf("cannot add information to an invalid span") + } + if withPos && !s.HasPosition() { + if err := s.v.Start.updatePosition(c); err != nil { + return err + } + if s.v.End.Offset == s.v.Start.Offset { + s.v.End = s.v.Start + } else if err := s.v.End.updatePosition(c); err != nil { + return err + } + } + if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) { + if err := s.v.Start.updateOffset(c); err != nil { + return err + } + if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column { + s.v.End.Offset = s.v.Start.Offset + } else if err := s.v.End.updateOffset(c); err != nil { + return err + } + } + return nil +} + +func (p *point) updatePosition(c Converter) error { + line, col, err := c.ToPosition(p.Offset) + if err != nil { + return err + } + p.Line = line + p.Column = col + return nil +} + +func (p *point) updateOffset(c Converter) error { + offset, err := c.ToOffset(p.Line, p.Column) + if err != nil { + return err + } + p.Offset = offset + return nil +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token.go new file mode 100644 index 00000000..f5a38708 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token.go @@ -0,0 +1,161 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "go/token" +) + +// Range represents a source code range in token.Pos form. +// It also carries the FileSet that produced the positions, so that it is +// self contained. +type Range struct { + FileSet *token.FileSet + Start token.Pos + End token.Pos +} + +// TokenConverter is a Converter backed by a token file set and file. +// It uses the file set methods to work out the conversions, which +// makes it fast and does not require the file contents. +type TokenConverter struct { + fset *token.FileSet + file *token.File +} + +// NewRange creates a new Range from a FileSet and two positions. +// To represent a point pass a 0 as the end pos. +func NewRange(fset *token.FileSet, start, end token.Pos) Range { + return Range{ + FileSet: fset, + Start: start, + End: end, + } +} + +// NewTokenConverter returns an implementation of Converter backed by a +// token.File. +func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter { + return &TokenConverter{fset: fset, file: f} +} + +// NewContentConverter returns an implementation of Converter for the +// given file content. +func NewContentConverter(filename string, content []byte) *TokenConverter { + fset := token.NewFileSet() + f := fset.AddFile(filename, -1, len(content)) + f.SetLinesForContent(content) + return &TokenConverter{fset: fset, file: f} +} + +// IsPoint returns true if the range represents a single point. +func (r Range) IsPoint() bool { + return r.Start == r.End +} + +// Span converts a Range to a Span that represents the Range. +// It will fill in all the members of the Span, calculating the line and column +// information. +func (r Range) Span() (Span, error) { + f := r.FileSet.File(r.Start) + if f == nil { + return Span{}, fmt.Errorf("file not found in FileSet") + } + s := Span{v: span{URI: FileURI(f.Name())}} + var err error + s.v.Start.Offset, err = offset(f, r.Start) + if err != nil { + return Span{}, err + } + if r.End.IsValid() { + s.v.End.Offset, err = offset(f, r.End) + if err != nil { + return Span{}, err + } + } + s.v.Start.clean() + s.v.End.clean() + s.v.clean() + converter := NewTokenConverter(r.FileSet, f) + return s.WithPosition(converter) +} + +// offset is a copy of the Offset function in go/token, but with the adjustment +// that it does not panic on invalid positions. +func offset(f *token.File, pos token.Pos) (int, error) { + if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() { + return 0, fmt.Errorf("invalid pos") + } + return int(pos) - f.Base(), nil +} + +// Range converts a Span to a Range that represents the Span for the supplied +// File. +func (s Span) Range(converter *TokenConverter) (Range, error) { + s, err := s.WithOffset(converter) + if err != nil { + return Range{}, err + } + // go/token will panic if the offset is larger than the file's size, + // so check here to avoid panicking. + if s.Start().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size()) + } + if s.End().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size()) + } + return Range{ + FileSet: converter.fset, + Start: converter.file.Pos(s.Start().Offset()), + End: converter.file.Pos(s.End().Offset()), + }, nil +} + +func (l *TokenConverter) ToPosition(offset int) (int, int, error) { + if offset > l.file.Size() { + return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size()) + } + pos := l.file.Pos(offset) + p := l.fset.Position(pos) + if offset == l.file.Size() { + return p.Line + 1, 1, nil + } + return p.Line, p.Column, nil +} + +func (l *TokenConverter) ToOffset(line, col int) (int, error) { + if line < 0 { + return -1, fmt.Errorf("line is not valid") + } + lineMax := l.file.LineCount() + 1 + if line > lineMax { + return -1, fmt.Errorf("line is beyond end of file %v", lineMax) + } else if line == lineMax { + if col > 1 { + return -1, fmt.Errorf("column is beyond end of file") + } + // at the end of the file, allowing for a trailing eol + return l.file.Size(), nil + } + pos := lineStart(l.file, line) + if !pos.IsValid() { + return -1, fmt.Errorf("line is not in file") + } + // we assume that column is in bytes here, and that the first byte of a + // line is at column 1 + pos += token.Pos(col - 1) + return offset(l.file, pos) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token111.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token111.go new file mode 100644 index 00000000..768419a7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token111.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !go1.12 + +package span + +import ( + "go/token" +) + +// lineStart is the pre-Go 1.12 version of (*token.File).LineStart. For Go +// versions <= 1.11, we borrow logic from the analysisutil package. +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + // Use binary search to find the start offset of this line. + + min := 0 // inclusive + max := f.Size() // exclusive + for { + offset := (min + max) / 2 + pos := f.Pos(offset) + posn := f.Position(pos) + if posn.Line == line { + return pos - (token.Pos(posn.Column) - 1) + } + + if min+1 >= max { + return token.NoPos + } + + if posn.Line < line { + min = offset + } else { + max = offset + } + } +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token112.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token112.go new file mode 100644 index 00000000..abff500d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/token112.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build go1.12 + +package span + +import ( + "go/token" +) + +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + return f.LineStart(line) +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/uri.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/uri.go new file mode 100644 index 00000000..525518be --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/uri.go @@ -0,0 +1,162 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +const fileScheme = "file" + +// URI represents the full URI for a file. +type URI string + +// Filename returns the file path for the given URI. +// It is an error to call this on a URI that is not a valid filename. +func (uri URI) Filename() string { + filename, err := filename(uri) + if err != nil { + panic(err) + } + return filepath.FromSlash(filename) +} + +func filename(uri URI) (string, error) { + if uri == "" { + return "", nil + } + u, err := url.ParseRequestURI(string(uri)) + if err != nil { + return "", err + } + if u.Scheme != fileScheme { + return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) + } + if isWindowsDriveURI(u.Path) { + u.Path = u.Path[1:] + } + return u.Path, nil +} + +// NewURI returns a span URI for the string. +// It will attempt to detect if the string is a file path or uri. +func NewURI(s string) URI { + if u, err := url.PathUnescape(s); err == nil { + s = u + } + if strings.HasPrefix(s, fileScheme+"://") { + return URI(s) + } + return FileURI(s) +} + +func CompareURI(a, b URI) int { + if equalURI(a, b) { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func equalURI(a, b URI) bool { + if a == b { + return true + } + // If we have the same URI basename, we may still have the same file URIs. + if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) { + return false + } + fa, err := filename(a) + if err != nil { + return false + } + fb, err := filename(b) + if err != nil { + return false + } + // Stat the files to check if they are equal. + infoa, err := os.Stat(filepath.FromSlash(fa)) + if err != nil { + return false + } + infob, err := os.Stat(filepath.FromSlash(fb)) + if err != nil { + return false + } + return os.SameFile(infoa, infob) +} + +// FileURI returns a span URI for the supplied file path. +// It will always have the file scheme. +func FileURI(path string) URI { + if path == "" { + return "" + } + // Handle standard library paths that contain the literal "$GOROOT". + // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. + const prefix = "$GOROOT" + if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { + suffix := path[len(prefix):] + path = runtime.GOROOT() + suffix + } + if !isWindowsDrivePath(path) { + if abs, err := filepath.Abs(path); err == nil { + path = abs + } + } + // Check the file path again, in case it became absolute. + if isWindowsDrivePath(path) { + path = "/" + path + } + path = filepath.ToSlash(path) + u := url.URL{ + Scheme: fileScheme, + Path: path, + } + uri := u.String() + if unescaped, err := url.PathUnescape(uri); err == nil { + uri = unescaped + } + return URI(uri) +} + +// isWindowsDrivePath returns true if the file path is of the form used by +// Windows. We check if the path begins with a drive letter, followed by a ":". +func isWindowsDrivePath(path string) bool { + if len(path) < 4 { + return false + } + return unicode.IsLetter(rune(path[0])) && path[1] == ':' +} + +// isWindowsDriveURI returns true if the file URI is of the format used by +// Windows URIs. The url.Parse package does not specially handle Windows paths +// (see https://golang.org/issue/6027). We check if the URI path has +// a drive prefix (e.g. "/C:"). If so, we trim the leading "/". +func isWindowsDriveURI(uri string) bool { + if len(uri) < 4 { + return false + } + return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/utf16.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/utf16.go new file mode 100644 index 00000000..6821851d --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/lsp/span/utf16.go @@ -0,0 +1,104 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +// ToUTF16Column calculates the utf16 column expressed by the point given the +// supplied file contents. +// This is used to convert from the native (always in bytes) column +// representation and the utf16 counts used by some editors. +func ToUTF16Column(p Point, content []byte) (int, error) { + if content == nil { + return -1, fmt.Errorf("ToUTF16Column: missing content") + } + if !p.HasPosition() { + return -1, fmt.Errorf("ToUTF16Column: point is missing position") + } + if !p.HasOffset() { + return -1, fmt.Errorf("ToUTF16Column: point is missing offset") + } + offset := p.Offset() // 0-based + colZero := p.Column() - 1 // 0-based + if colZero == 0 { + // 0-based column 0, so it must be chr 1 + return 1, nil + } else if colZero < 0 { + return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero) + } + // work out the offset at the start of the line using the column + lineOffset := offset - colZero + if lineOffset < 0 || offset > len(content) { + return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content)) + } + // Use the offset to pick out the line start. + // This cannot panic: offset > len(content) and lineOffset < offset. + start := content[lineOffset:] + + // Now, truncate down to the supplied column. + start = start[:colZero] + + // and count the number of utf16 characters + // in theory we could do this by hand more efficiently... + return len(utf16.Encode([]rune(string(start)))) + 1, nil +} + +// FromUTF16Column advances the point by the utf16 character offset given the +// supplied line contents. +// This is used to convert from the utf16 counts used by some editors to the +// native (always in bytes) column representation. +func FromUTF16Column(p Point, chr int, content []byte) (Point, error) { + if !p.HasOffset() { + return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset") + } + // if chr is 1 then no adjustment needed + if chr <= 1 { + return p, nil + } + if p.Offset() >= len(content) { + return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content)) + } + remains := content[p.Offset():] + // scan forward the specified number of characters + for count := 1; count < chr; count++ { + if len(remains) <= 0 { + return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content") + } + r, w := utf8.DecodeRune(remains) + if r == '\n' { + // Per the LSP spec: + // + // > If the character value is greater than the line length it + // > defaults back to the line length. + break + } + remains = remains[w:] + if r >= 0x10000 { + // a two point rune + count++ + // if we finished in a two point rune, do not advance past the first + if count >= chr { + break + } + } + p.v.Column += w + p.v.Offset += w + } + return p, nil +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/parser/parser.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/parser/parser.go new file mode 100644 index 00000000..4c0fa8f7 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/parser/parser.go @@ -0,0 +1,818 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package parser implements a SPIR-V assembly parser. +package parser + +import ( + "fmt" + "io" + "log" + "strings" + "unicode" + "unicode/utf8" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema" +) + +// Type is an enumerator of token types. +type Type int + +// Type enumerators +const ( + Ident Type = iota // Foo + PIdent // %32, %foo + Integer + Float + String + Operator + Comment + Newline +) + +func (t Type) String() string { + switch t { + case Ident: + return "Ident" + case PIdent: + return "PIdent" + case Integer: + return "Integer" + case Float: + return "Float" + case String: + return "String" + case Operator: + return "Operator" + case Comment: + return "Comment" + default: + return "" + } +} + +// Token represents a single lexed token. +type Token struct { + Type Type + Range Range +} + +func (t Token) String() string { return fmt.Sprintf("{%v %v}", t.Type, t.Range) } + +// Text returns the tokens text from the source. +func (t Token) Text(lines []string) string { return t.Range.Text(lines) } + +// Range represents an interval in a text file. +type Range struct { + Start Position + End Position +} + +func (r Range) String() string { return fmt.Sprintf("[%v %v]", r.Start, r.End) } + +// Text returns the text for the given Range in the provided lines. +func (r Range) Text(lines []string) string { + sl, sc := r.Start.Line-1, r.Start.Column-1 + if sl < 0 || sc < 0 || sl > len(lines) || sc > len(lines[sl]) { + return fmt.Sprintf("", r.Start) + } + el, ec := r.End.Line-1, r.End.Column-1 + if el < 0 || ec < 0 || el > len(lines) || ec > len(lines[sl]) { + return fmt.Sprintf("", r.End) + } + + sb := strings.Builder{} + if sl != el { + sb.WriteString(lines[sl][sc:]) + for l := sl + 1; l < el; l++ { + sb.WriteString(lines[l]) + } + sb.WriteString(lines[el][:ec]) + } else { + sb.WriteString(lines[sl][sc:ec]) + } + return sb.String() +} + +// Contains returns true if p is in r. +func (r Range) Contains(p Position) bool { + return !(p.LessThan(r.Start) || p.GreaterThan(r.End)) +} + +func (r *Range) grow(o Range) { + if !r.Start.IsValid() || o.Start.LessThan(r.Start) { + r.Start = o.Start + } + if !r.End.IsValid() || o.End.GreaterThan(r.End) { + r.End = o.End + } +} + +// Position holds a line and column position in a text file. +type Position struct { + Line, Column int +} + +func (p Position) String() string { return fmt.Sprintf("%v:%v", p.Line, p.Column) } + +// IsValid returns true if the position has a line and column greater than 1. +func (p Position) IsValid() bool { return p.Line > 0 && p.Column > 0 } + +// LessThan returns true iff o is before p. +func (p Position) LessThan(o Position) bool { + switch { + case !p.IsValid() || !o.IsValid(): + return false + case p.Line < o.Line: + return true + case p.Line > o.Line: + return false + case p.Column < o.Column: + return true + default: + return false + } +} + +// GreaterThan returns true iff o is greater than p. +func (p Position) GreaterThan(o Position) bool { + switch { + case !p.IsValid() || !o.IsValid(): + return false + case p.Line > o.Line: + return true + case p.Line < o.Line: + return false + case p.Column > o.Column: + return true + default: + return false + } +} + +type lexer struct { + source string + lexerState + diags []Diagnostic + e error +} + +type lexerState struct { + offset int // byte offset in source + toks []*Token // all the lexed tokens + pos Position // current position +} + +// err appends an fmt.Printf style error into l.diags for the given token. +func (l *lexer) err(tok *Token, msg string, args ...interface{}) { + rng := Range{} + if tok != nil { + rng = tok.Range + } + l.diags = append(l.diags, Diagnostic{ + Range: rng, + Severity: SeverityError, + Message: fmt.Sprintf(msg, args...), + }) +} + +// next returns the next rune, or io.EOF if the last rune has already been +// consumed. +func (l *lexer) next() rune { + if l.offset >= len(l.source) { + l.e = io.EOF + return 0 + } + r, n := utf8.DecodeRuneInString(l.source[l.offset:]) + l.offset += n + if n == 0 { + l.e = io.EOF + return 0 + } + if r == '\n' { + l.pos.Line++ + l.pos.Column = 1 + } else { + l.pos.Column++ + } + return r +} + +// save returns the current lexerState. +func (l *lexer) save() lexerState { + return l.lexerState +} + +// restore restores the current lexer state with s. +func (l *lexer) restore(s lexerState) { + l.lexerState = s +} + +// pident processes the PIdent token at the current position. +// The lexer *must* know the next token is a PIdent before calling. +func (l *lexer) pident() { + tok := &Token{Type: PIdent, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '%' { + log.Fatalf("lexer expected '%%', got '%v'", r) + return + } + for l.e == nil { + s := l.save() + r := l.next() + if !isAlphaNumeric(r) && r != '_' { + l.restore(s) + break + } + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// numberOrIdent processes the Ident, Float or Integer token at the current +// position. +func (l *lexer) numberOrIdent() { + const Unknown Type = -1 + tok := &Token{Type: Unknown, Range: Range{Start: l.pos, End: l.pos}} +loop: + for l.e == nil { + s := l.save() + r := l.next() + switch { + case r == '-', r == '+', isNumeric(r): + continue + case isAlpha(r), r == '_': + switch tok.Type { + case Unknown: + tok.Type = Ident + case Float, Integer: + l.err(tok, "invalid number") + return + } + case r == '.': + switch tok.Type { + case Unknown: + tok.Type = Float + default: + l.restore(s) + break loop + } + default: + if tok.Type == Unknown { + tok.Type = Integer + } + l.restore(s) + break loop + } + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// string processes the String token at the current position. +// The lexer *must* know the next token is a String before calling. +func (l *lexer) string() { + tok := &Token{Type: String, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '"' { + log.Fatalf("lexer expected '\"', got '%v'", r) + return + } + escape := false + for l.e == nil { + switch l.next() { + case '"': + if !escape { + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + case '\\': + escape = !escape + default: + escape = false + } + } +} + +// operator processes the Operator token at the current position. +// The lexer *must* know the next token is a Operator before calling. +func (l *lexer) operator() { + tok := &Token{Type: Operator, Range: Range{Start: l.pos, End: l.pos}} + for l.e == nil { + switch l.next() { + case '=', '|': + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + } +} + +// lineComment processes the Comment token at the current position. +// The lexer *must* know the next token is a Comment before calling. +func (l *lexer) lineComment() { + tok := &Token{Type: Comment, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != ';' { + log.Fatalf("lexer expected ';', got '%v'", r) + return + } + for l.e == nil { + s := l.save() + switch l.next() { + case '\n': + l.restore(s) + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + } +} + +// newline processes the Newline token at the current position. +// The lexer *must* know the next token is a Newline before calling. +func (l *lexer) newline() { + tok := &Token{Type: Newline, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '\n' { + log.Fatalf("lexer expected '\n', got '%v'", r) + return + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// lex returns all the tokens and diagnostics after lexing source. +func lex(source string) ([]*Token, []Diagnostic, error) { + l := lexer{source: source, lexerState: lexerState{pos: Position{1, 1}}} + + lastPos := Position{} + for l.e == nil { + // Integrity check that the parser is making progress + if l.pos == lastPos { + log.Panicf("Parsing stuck at %v", l.pos) + } + lastPos = l.pos + + s := l.save() + r := l.next() + switch { + case r == '%': + l.restore(s) + l.pident() + case r == '+' || r == '-' || r == '_' || isAlphaNumeric(r): + l.restore(s) + l.numberOrIdent() + case r == '"': + l.restore(s) + l.string() + case r == '=', r == '|': + l.restore(s) + l.operator() + case r == ';': + l.restore(s) + l.lineComment() + case r == '\n': + l.restore(s) + l.newline() + } + } + if l.e != nil && l.e != io.EOF { + return nil, nil, l.e + } + return l.toks, l.diags, nil +} + +func isNumeric(r rune) bool { return unicode.IsDigit(r) } +func isAlpha(r rune) bool { return unicode.IsLetter(r) } +func isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) } + +type parser struct { + lines []string // all source lines + toks []*Token // all tokens + diags []Diagnostic // parser emitted diagnostics + idents map[string]*Identifier // identifiers by name + mappings map[*Token]interface{} // tokens to semantic map + extInstImports map[string]schema.OpcodeMap // extension imports by identifier + insts []*Instruction // all instructions +} + +func (p *parser) parse() error { + for i := 0; i < len(p.toks); { + if p.newline(i) || p.comment(i) { + i++ + continue + } + if n := p.instruction(i); n > 0 { + i += n + } else { + p.unexpected(i) + i++ + } + } + return nil +} + +// instruction parses the instruction starting at the i'th token. +func (p *parser) instruction(i int) (n int) { + inst := &Instruction{} + + switch { + case p.opcode(i) != nil: + inst.Opcode = p.opcode(i) + inst.Tokens = []*Token{p.tok(i)} + p.mappings[p.tok(i)] = inst + n++ + case p.opcode(i+2) != nil: // try '%id' '=' + inst.Result, inst.Opcode = p.pident(i), p.opcode(i+2) + if inst.Result == nil || p.operator(i+1) != "=" { + return 0 + } + n += 3 + inst.Tokens = []*Token{p.tok(i), p.tok(i + 1), p.tok(i + 2)} + p.mappings[p.tok(i+2)] = inst + default: + return + } + + expectsResult := len(inst.Opcode.Operands) > 0 && IsResult(inst.Opcode.Operands[0].Kind) + operands := inst.Opcode.Operands + switch { + case inst.Result != nil && !expectsResult: + p.err(inst.Result, "'%s' does not have a result", inst.Opcode.Opname) + return + case inst.Result == nil && expectsResult: + p.err(p.tok(i), "'%s' expects a result", inst.Opcode.Opname) + return + case inst.Result != nil && expectsResult: + // Check the result is of the correct type + o := inst.Opcode.Operands[0] + p.operand(o.Name, o.Kind, i, false) + operands = operands[1:] + p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i)) + } + + processOperand := func(o schema.Operand) bool { + if p.newline(i + n) { + return false + } + + switch o.Quantifier { + case schema.Once: + if op, c := p.operand(o.Name, o.Kind, i+n, false); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } + case schema.ZeroOrOnce: + if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } + case schema.ZeroOrMany: + for !p.newline(i + n) { + if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } else { + return false + } + } + } + return true + } + + for _, o := range operands { + if !processOperand(o) { + break + } + + if inst.Opcode == schema.OpExtInst && n == 4 { + extImportTok, extNameTok := p.tok(i+n), p.tok(i+n+1) + extImport := extImportTok.Text(p.lines) + if extOpcodes, ok := p.extInstImports[extImport]; ok { + extName := extNameTok.Text(p.lines) + if extOpcode, ok := extOpcodes[extName]; ok { + n += 2 // skip ext import, ext name + for _, o := range extOpcode.Operands { + if !processOperand(o) { + break + } + } + } else { + p.err(extNameTok, "Unknown extension opcode '%s'", extName) + } + } else { + p.err(extImportTok, "Expected identifier to OpExtInstImport") + } + } + } + + for _, t := range inst.Tokens { + inst.Range.grow(t.Range) + } + + p.insts = append(p.insts, inst) + + if inst.Opcode == schema.OpExtInstImport && len(inst.Tokens) >= 4 { + // Instruction is a OpExtInstImport. Keep track of this. + extTok := inst.Tokens[3] + extName := strings.Trim(extTok.Text(p.lines), `"`) + extOpcodes, ok := schema.ExtOpcodes[extName] + if !ok { + p.err(extTok, "Unknown extension '%s'", extName) + } + extImport := inst.Result.Text(p.lines) + p.extInstImports[extImport] = extOpcodes + } + + return +} + +// operand parses the operand with the name n, kind k, starting at the i'th +// token. +func (p *parser) operand(n string, k *schema.OperandKind, i int, optional bool) (*Operand, int) { + tok := p.tok(i) + if tok == nil { + return nil, 0 + } + + op := &Operand{ + Name: n, + Kind: k, + Tokens: []*Token{tok}, + } + p.mappings[tok] = op + + switch k.Category { + case schema.OperandCategoryBitEnum, schema.OperandCategoryValueEnum: + s := tok.Text(p.lines) + for _, e := range k.Enumerants { + if e.Enumerant == s { + count := 1 + for _, param := range e.Parameters { + p, c := p.operand(param.Name, param.Kind, i+count, false) + if p != nil { + op.Tokens = append(op.Tokens, p.Tokens...) + op.Parameters = append(op.Parameters, p) + } + count += c + } + + // Handle bitfield '|' chains + if p.tok(i+count).Text(p.lines) == "|" { + count++ // '|' + p, c := p.operand(n, k, i+count, false) + if p != nil { + op.Tokens = append(op.Tokens, p.Tokens...) + op.Parameters = append(op.Parameters, p) + } + count += c + } + + return op, count + } + } + if !optional { + p.err(p.tok(i), "invalid operand value '%s'", s) + } + + return nil, 0 + + case schema.OperandCategoryID: + id := p.pident(i) + if id != nil { + p.addIdentRef(p.tok(i)) + return op, 1 + } + if !optional { + p.err(p.tok(i), "operand requires id, got '%s'", tok.Text(p.lines)) + } + return nil, 0 + + case schema.OperandCategoryLiteral: + switch tok.Type { + case String, Integer, Float, Ident: + return op, 1 + } + if !optional { + p.err(p.tok(i), "operand requires literal, got '%s'", tok.Text(p.lines)) + } + return nil, 0 + + case schema.OperandCategoryComposite: + n := 1 + for _, b := range k.Bases { + o, c := p.operand(b.Kind, b, i+n, optional) + if o != nil { + op.Tokens = append(op.Tokens, o.Tokens...) + } + n += c + } + return op, n + + default: + p.err(p.tok(i), "OperandKind '%s' has unexpected category '%s'", k.Kind, k.Category) + return nil, 0 + } +} + +// tok returns the i'th token, or nil if i is out of bounds. +func (p *parser) tok(i int) *Token { + if i < 0 || i >= len(p.toks) { + return nil + } + return p.toks[i] +} + +// opcode returns the schema.Opcode for the i'th token, or nil if the i'th token +// does not represent an opcode. +func (p *parser) opcode(i int) *schema.Opcode { + if tok := p.ident(i); tok != nil { + name := tok.Text(p.lines) + if inst, found := schema.Opcodes[name]; found { + return inst + } + } + return nil +} + +// operator returns the operator for the i'th token, or and empty string if the +// i'th token is not an operator. +func (p *parser) operator(i int) string { + if tok := p.tok(i); tok != nil && tok.Type == Operator { + return tok.Text(p.lines) + } + return "" +} + +// ident returns the i'th token if it is an Ident, otherwise nil. +func (p *parser) ident(i int) *Token { + if tok := p.tok(i); tok != nil && tok.Type == Ident { + return tok + } + return nil +} + +// pident returns the i'th token if it is an PIdent, otherwise nil. +func (p *parser) pident(i int) *Token { + if tok := p.tok(i); tok != nil && tok.Type == PIdent { + return tok + } + return nil +} + +// comment returns true if the i'th token is a Comment, otherwise false. +func (p *parser) comment(i int) bool { + if tok := p.tok(i); tok != nil && tok.Type == Comment { + return true + } + return false +} + +// newline returns true if the i'th token is a Newline, otherwise false. +func (p *parser) newline(i int) bool { + if tok := p.tok(i); tok != nil && tok.Type == Newline { + return true + } + return false +} + +// unexpected emits an 'unexpected token error' for the i'th token. +func (p *parser) unexpected(i int) { + p.err(p.toks[i], "syntax error: unexpected '%s'", p.toks[i].Text(p.lines)) +} + +// addIdentDef records the token definition for the instruction inst with the +// given id. +func (p *parser) addIdentDef(id string, inst *Instruction, def *Token) { + i, existing := p.idents[id] + if !existing { + i = &Identifier{} + p.idents[id] = i + } + if i.Definition == nil { + i.Definition = inst + } else { + p.err(def, "id '%v' redeclared", id) + } +} + +// addIdentRef adds a identifier reference for the token ref. +func (p *parser) addIdentRef(ref *Token) { + id := ref.Text(p.lines) + i, existing := p.idents[id] + if !existing { + i = &Identifier{} + p.idents[id] = i + } + i.References = append(i.References, ref) +} + +// err appends an fmt.Printf style error into l.diags for the given token. +func (p *parser) err(tok *Token, msg string, args ...interface{}) { + rng := Range{} + if tok != nil { + rng = tok.Range + } + p.diags = append(p.diags, Diagnostic{ + Range: rng, + Severity: SeverityError, + Message: fmt.Sprintf(msg, args...), + }) +} + +// Parse parses the SPIR-V assembly string source, returning the parse results. +func Parse(source string) (Results, error) { + toks, diags, err := lex(source) + if err != nil { + return Results{}, err + } + lines := strings.SplitAfter(source, "\n") + p := parser{ + lines: lines, + toks: toks, + idents: map[string]*Identifier{}, + mappings: map[*Token]interface{}{}, + extInstImports: map[string]schema.OpcodeMap{}, + } + if err := p.parse(); err != nil { + return Results{}, err + } + diags = append(diags, p.diags...) + return Results{ + Lines: lines, + Tokens: toks, + Diagnostics: p.diags, + Identifiers: p.idents, + Mappings: p.mappings, + }, nil +} + +// IsResult returns true if k is used to store the result of an instruction. +func IsResult(k *schema.OperandKind) bool { + switch k { + case schema.OperandKindIdResult, schema.OperandKindIdResultType: + return true + default: + return false + } +} + +// Results holds the output of Parse(). +type Results struct { + Lines []string + Tokens []*Token + Diagnostics []Diagnostic + Identifiers map[string]*Identifier // identifiers by name + Mappings map[*Token]interface{} // tokens to semantic map +} + +// Instruction describes a single instruction instance +type Instruction struct { + Tokens []*Token // all the tokens that make up the instruction + Result *Token // the token that represents the result of the instruction, or nil + Operands []*Operand // the operands of the instruction + Range Range // the textual range of the instruction + Opcode *schema.Opcode // the opcode for the instruction +} + +// Operand describes a single operand instance +type Operand struct { + Name string // name of the operand + Kind *schema.OperandKind // kind of the operand + Tokens []*Token // all the tokens that make up the operand + Parameters []*Operand // all the parameters for the operand +} + +// Identifier describes a single, unique SPIR-V identifier (i.e. %32) +type Identifier struct { + Definition *Instruction // where the identifier was defined + References []*Token // all the places the identifier was referenced +} + +// Severity is an enumerator of diagnostic severities +type Severity int + +// Severity levels +const ( + SeverityError Severity = iota + SeverityWarning + SeverityInformation + SeverityHint +) + +// Diagnostic holds a single diagnostic message that was generated while +// parsing. +type Diagnostic struct { + Range Range + Severity Severity + Message string +} diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go new file mode 100644 index 00000000..4a34feca --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go @@ -0,0 +1,34290 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated by gen-grammar.go --template=./src/schema/schema.go.tmpl --out=./src/schema/schema.go +// Do not modify this file directly. + +package schema + +// Opcode holds information about a specific SPIR-V opcode. +type Opcode struct { + Opname string + Class string + Opcode int + Operands []Operand +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind *OperandKind + Name string + Quantifier Quantifier +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category OperandCategory + Kind string + Enumerants []Enumerant + Bases []*OperandKind +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string + Value interface{} + Capabilities []string + Parameters []Parameter + Version string +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind *OperandKind + Name string +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) + +// OperandCategory is an enumerator that groups operand kinds. +type OperandCategory string + +const ( + // OperandCategoryBitEnum describes an operand kind where its value is a + // mask, which is formed by combining the bits specified as enumerants in an + // enum. + OperandCategoryBitEnum = "BitEnum" + + // OperandCategoryValueEnum describes an operand kind where its value is an + // enumerant from an enum. + OperandCategoryValueEnum = "ValueEnum" + + // OperandCategoryID describes and operand kind where its value is an + // definition or reference. + OperandCategoryID = "Id" + + // OperandCategoryLiteral describes and operand kind where its value is an + // literal number or string. + OperandCategoryLiteral = "Literal" + + // OperandCategoryComposite describes and operand kind where its value is + // composed from operand values from the above categories. + OperandCategoryComposite = "Composite" +) + +// OpcodeMap is a map of opcode name to Opcode type. +type OpcodeMap map[string]*Opcode + +var ( + // Opcodes is a map of opcode name to Opcode description. + Opcodes = OpcodeMap { + "OpNop": OpNop, + "OpUndef": OpUndef, + "OpSourceContinued": OpSourceContinued, + "OpSource": OpSource, + "OpSourceExtension": OpSourceExtension, + "OpName": OpName, + "OpMemberName": OpMemberName, + "OpString": OpString, + "OpLine": OpLine, + "OpExtension": OpExtension, + "OpExtInstImport": OpExtInstImport, + "OpExtInst": OpExtInst, + "OpMemoryModel": OpMemoryModel, + "OpEntryPoint": OpEntryPoint, + "OpExecutionMode": OpExecutionMode, + "OpCapability": OpCapability, + "OpTypeVoid": OpTypeVoid, + "OpTypeBool": OpTypeBool, + "OpTypeInt": OpTypeInt, + "OpTypeFloat": OpTypeFloat, + "OpTypeVector": OpTypeVector, + "OpTypeMatrix": OpTypeMatrix, + "OpTypeImage": OpTypeImage, + "OpTypeSampler": OpTypeSampler, + "OpTypeSampledImage": OpTypeSampledImage, + "OpTypeArray": OpTypeArray, + "OpTypeRuntimeArray": OpTypeRuntimeArray, + "OpTypeStruct": OpTypeStruct, + "OpTypeOpaque": OpTypeOpaque, + "OpTypePointer": OpTypePointer, + "OpTypeFunction": OpTypeFunction, + "OpTypeEvent": OpTypeEvent, + "OpTypeDeviceEvent": OpTypeDeviceEvent, + "OpTypeReserveId": OpTypeReserveId, + "OpTypeQueue": OpTypeQueue, + "OpTypePipe": OpTypePipe, + "OpTypeForwardPointer": OpTypeForwardPointer, + "OpConstantTrue": OpConstantTrue, + "OpConstantFalse": OpConstantFalse, + "OpConstant": OpConstant, + "OpConstantComposite": OpConstantComposite, + "OpConstantSampler": OpConstantSampler, + "OpConstantNull": OpConstantNull, + "OpSpecConstantTrue": OpSpecConstantTrue, + "OpSpecConstantFalse": OpSpecConstantFalse, + "OpSpecConstant": OpSpecConstant, + "OpSpecConstantComposite": OpSpecConstantComposite, + "OpSpecConstantOp": OpSpecConstantOp, + "OpFunction": OpFunction, + "OpFunctionParameter": OpFunctionParameter, + "OpFunctionEnd": OpFunctionEnd, + "OpFunctionCall": OpFunctionCall, + "OpVariable": OpVariable, + "OpImageTexelPointer": OpImageTexelPointer, + "OpLoad": OpLoad, + "OpStore": OpStore, + "OpCopyMemory": OpCopyMemory, + "OpCopyMemorySized": OpCopyMemorySized, + "OpAccessChain": OpAccessChain, + "OpInBoundsAccessChain": OpInBoundsAccessChain, + "OpPtrAccessChain": OpPtrAccessChain, + "OpArrayLength": OpArrayLength, + "OpGenericPtrMemSemantics": OpGenericPtrMemSemantics, + "OpInBoundsPtrAccessChain": OpInBoundsPtrAccessChain, + "OpDecorate": OpDecorate, + "OpMemberDecorate": OpMemberDecorate, + "OpDecorationGroup": OpDecorationGroup, + "OpGroupDecorate": OpGroupDecorate, + "OpGroupMemberDecorate": OpGroupMemberDecorate, + "OpVectorExtractDynamic": OpVectorExtractDynamic, + "OpVectorInsertDynamic": OpVectorInsertDynamic, + "OpVectorShuffle": OpVectorShuffle, + "OpCompositeConstruct": OpCompositeConstruct, + "OpCompositeExtract": OpCompositeExtract, + "OpCompositeInsert": OpCompositeInsert, + "OpCopyObject": OpCopyObject, + "OpTranspose": OpTranspose, + "OpSampledImage": OpSampledImage, + "OpImageSampleImplicitLod": OpImageSampleImplicitLod, + "OpImageSampleExplicitLod": OpImageSampleExplicitLod, + "OpImageSampleDrefImplicitLod": OpImageSampleDrefImplicitLod, + "OpImageSampleDrefExplicitLod": OpImageSampleDrefExplicitLod, + "OpImageSampleProjImplicitLod": OpImageSampleProjImplicitLod, + "OpImageSampleProjExplicitLod": OpImageSampleProjExplicitLod, + "OpImageSampleProjDrefImplicitLod": OpImageSampleProjDrefImplicitLod, + "OpImageSampleProjDrefExplicitLod": OpImageSampleProjDrefExplicitLod, + "OpImageFetch": OpImageFetch, + "OpImageGather": OpImageGather, + "OpImageDrefGather": OpImageDrefGather, + "OpImageRead": OpImageRead, + "OpImageWrite": OpImageWrite, + "OpImage": OpImage, + "OpImageQueryFormat": OpImageQueryFormat, + "OpImageQueryOrder": OpImageQueryOrder, + "OpImageQuerySizeLod": OpImageQuerySizeLod, + "OpImageQuerySize": OpImageQuerySize, + "OpImageQueryLod": OpImageQueryLod, + "OpImageQueryLevels": OpImageQueryLevels, + "OpImageQuerySamples": OpImageQuerySamples, + "OpConvertFToU": OpConvertFToU, + "OpConvertFToS": OpConvertFToS, + "OpConvertSToF": OpConvertSToF, + "OpConvertUToF": OpConvertUToF, + "OpUConvert": OpUConvert, + "OpSConvert": OpSConvert, + "OpFConvert": OpFConvert, + "OpQuantizeToF16": OpQuantizeToF16, + "OpConvertPtrToU": OpConvertPtrToU, + "OpSatConvertSToU": OpSatConvertSToU, + "OpSatConvertUToS": OpSatConvertUToS, + "OpConvertUToPtr": OpConvertUToPtr, + "OpPtrCastToGeneric": OpPtrCastToGeneric, + "OpGenericCastToPtr": OpGenericCastToPtr, + "OpGenericCastToPtrExplicit": OpGenericCastToPtrExplicit, + "OpBitcast": OpBitcast, + "OpSNegate": OpSNegate, + "OpFNegate": OpFNegate, + "OpIAdd": OpIAdd, + "OpFAdd": OpFAdd, + "OpISub": OpISub, + "OpFSub": OpFSub, + "OpIMul": OpIMul, + "OpFMul": OpFMul, + "OpUDiv": OpUDiv, + "OpSDiv": OpSDiv, + "OpFDiv": OpFDiv, + "OpUMod": OpUMod, + "OpSRem": OpSRem, + "OpSMod": OpSMod, + "OpFRem": OpFRem, + "OpFMod": OpFMod, + "OpVectorTimesScalar": OpVectorTimesScalar, + "OpMatrixTimesScalar": OpMatrixTimesScalar, + "OpVectorTimesMatrix": OpVectorTimesMatrix, + "OpMatrixTimesVector": OpMatrixTimesVector, + "OpMatrixTimesMatrix": OpMatrixTimesMatrix, + "OpOuterProduct": OpOuterProduct, + "OpDot": OpDot, + "OpIAddCarry": OpIAddCarry, + "OpISubBorrow": OpISubBorrow, + "OpUMulExtended": OpUMulExtended, + "OpSMulExtended": OpSMulExtended, + "OpAny": OpAny, + "OpAll": OpAll, + "OpIsNan": OpIsNan, + "OpIsInf": OpIsInf, + "OpIsFinite": OpIsFinite, + "OpIsNormal": OpIsNormal, + "OpSignBitSet": OpSignBitSet, + "OpLessOrGreater": OpLessOrGreater, + "OpOrdered": OpOrdered, + "OpUnordered": OpUnordered, + "OpLogicalEqual": OpLogicalEqual, + "OpLogicalNotEqual": OpLogicalNotEqual, + "OpLogicalOr": OpLogicalOr, + "OpLogicalAnd": OpLogicalAnd, + "OpLogicalNot": OpLogicalNot, + "OpSelect": OpSelect, + "OpIEqual": OpIEqual, + "OpINotEqual": OpINotEqual, + "OpUGreaterThan": OpUGreaterThan, + "OpSGreaterThan": OpSGreaterThan, + "OpUGreaterThanEqual": OpUGreaterThanEqual, + "OpSGreaterThanEqual": OpSGreaterThanEqual, + "OpULessThan": OpULessThan, + "OpSLessThan": OpSLessThan, + "OpULessThanEqual": OpULessThanEqual, + "OpSLessThanEqual": OpSLessThanEqual, + "OpFOrdEqual": OpFOrdEqual, + "OpFUnordEqual": OpFUnordEqual, + "OpFOrdNotEqual": OpFOrdNotEqual, + "OpFUnordNotEqual": OpFUnordNotEqual, + "OpFOrdLessThan": OpFOrdLessThan, + "OpFUnordLessThan": OpFUnordLessThan, + "OpFOrdGreaterThan": OpFOrdGreaterThan, + "OpFUnordGreaterThan": OpFUnordGreaterThan, + "OpFOrdLessThanEqual": OpFOrdLessThanEqual, + "OpFUnordLessThanEqual": OpFUnordLessThanEqual, + "OpFOrdGreaterThanEqual": OpFOrdGreaterThanEqual, + "OpFUnordGreaterThanEqual": OpFUnordGreaterThanEqual, + "OpShiftRightLogical": OpShiftRightLogical, + "OpShiftRightArithmetic": OpShiftRightArithmetic, + "OpShiftLeftLogical": OpShiftLeftLogical, + "OpBitwiseOr": OpBitwiseOr, + "OpBitwiseXor": OpBitwiseXor, + "OpBitwiseAnd": OpBitwiseAnd, + "OpNot": OpNot, + "OpBitFieldInsert": OpBitFieldInsert, + "OpBitFieldSExtract": OpBitFieldSExtract, + "OpBitFieldUExtract": OpBitFieldUExtract, + "OpBitReverse": OpBitReverse, + "OpBitCount": OpBitCount, + "OpDPdx": OpDPdx, + "OpDPdy": OpDPdy, + "OpFwidth": OpFwidth, + "OpDPdxFine": OpDPdxFine, + "OpDPdyFine": OpDPdyFine, + "OpFwidthFine": OpFwidthFine, + "OpDPdxCoarse": OpDPdxCoarse, + "OpDPdyCoarse": OpDPdyCoarse, + "OpFwidthCoarse": OpFwidthCoarse, + "OpEmitVertex": OpEmitVertex, + "OpEndPrimitive": OpEndPrimitive, + "OpEmitStreamVertex": OpEmitStreamVertex, + "OpEndStreamPrimitive": OpEndStreamPrimitive, + "OpControlBarrier": OpControlBarrier, + "OpMemoryBarrier": OpMemoryBarrier, + "OpAtomicLoad": OpAtomicLoad, + "OpAtomicStore": OpAtomicStore, + "OpAtomicExchange": OpAtomicExchange, + "OpAtomicCompareExchange": OpAtomicCompareExchange, + "OpAtomicCompareExchangeWeak": OpAtomicCompareExchangeWeak, + "OpAtomicIIncrement": OpAtomicIIncrement, + "OpAtomicIDecrement": OpAtomicIDecrement, + "OpAtomicIAdd": OpAtomicIAdd, + "OpAtomicISub": OpAtomicISub, + "OpAtomicSMin": OpAtomicSMin, + "OpAtomicUMin": OpAtomicUMin, + "OpAtomicSMax": OpAtomicSMax, + "OpAtomicUMax": OpAtomicUMax, + "OpAtomicAnd": OpAtomicAnd, + "OpAtomicOr": OpAtomicOr, + "OpAtomicXor": OpAtomicXor, + "OpPhi": OpPhi, + "OpLoopMerge": OpLoopMerge, + "OpSelectionMerge": OpSelectionMerge, + "OpLabel": OpLabel, + "OpBranch": OpBranch, + "OpBranchConditional": OpBranchConditional, + "OpSwitch": OpSwitch, + "OpKill": OpKill, + "OpReturn": OpReturn, + "OpReturnValue": OpReturnValue, + "OpUnreachable": OpUnreachable, + "OpLifetimeStart": OpLifetimeStart, + "OpLifetimeStop": OpLifetimeStop, + "OpGroupAsyncCopy": OpGroupAsyncCopy, + "OpGroupWaitEvents": OpGroupWaitEvents, + "OpGroupAll": OpGroupAll, + "OpGroupAny": OpGroupAny, + "OpGroupBroadcast": OpGroupBroadcast, + "OpGroupIAdd": OpGroupIAdd, + "OpGroupFAdd": OpGroupFAdd, + "OpGroupFMin": OpGroupFMin, + "OpGroupUMin": OpGroupUMin, + "OpGroupSMin": OpGroupSMin, + "OpGroupFMax": OpGroupFMax, + "OpGroupUMax": OpGroupUMax, + "OpGroupSMax": OpGroupSMax, + "OpReadPipe": OpReadPipe, + "OpWritePipe": OpWritePipe, + "OpReservedReadPipe": OpReservedReadPipe, + "OpReservedWritePipe": OpReservedWritePipe, + "OpReserveReadPipePackets": OpReserveReadPipePackets, + "OpReserveWritePipePackets": OpReserveWritePipePackets, + "OpCommitReadPipe": OpCommitReadPipe, + "OpCommitWritePipe": OpCommitWritePipe, + "OpIsValidReserveId": OpIsValidReserveId, + "OpGetNumPipePackets": OpGetNumPipePackets, + "OpGetMaxPipePackets": OpGetMaxPipePackets, + "OpGroupReserveReadPipePackets": OpGroupReserveReadPipePackets, + "OpGroupReserveWritePipePackets": OpGroupReserveWritePipePackets, + "OpGroupCommitReadPipe": OpGroupCommitReadPipe, + "OpGroupCommitWritePipe": OpGroupCommitWritePipe, + "OpEnqueueMarker": OpEnqueueMarker, + "OpEnqueueKernel": OpEnqueueKernel, + "OpGetKernelNDrangeSubGroupCount": OpGetKernelNDrangeSubGroupCount, + "OpGetKernelNDrangeMaxSubGroupSize": OpGetKernelNDrangeMaxSubGroupSize, + "OpGetKernelWorkGroupSize": OpGetKernelWorkGroupSize, + "OpGetKernelPreferredWorkGroupSizeMultiple": OpGetKernelPreferredWorkGroupSizeMultiple, + "OpRetainEvent": OpRetainEvent, + "OpReleaseEvent": OpReleaseEvent, + "OpCreateUserEvent": OpCreateUserEvent, + "OpIsValidEvent": OpIsValidEvent, + "OpSetUserEventStatus": OpSetUserEventStatus, + "OpCaptureEventProfilingInfo": OpCaptureEventProfilingInfo, + "OpGetDefaultQueue": OpGetDefaultQueue, + "OpBuildNDRange": OpBuildNDRange, + "OpImageSparseSampleImplicitLod": OpImageSparseSampleImplicitLod, + "OpImageSparseSampleExplicitLod": OpImageSparseSampleExplicitLod, + "OpImageSparseSampleDrefImplicitLod": OpImageSparseSampleDrefImplicitLod, + "OpImageSparseSampleDrefExplicitLod": OpImageSparseSampleDrefExplicitLod, + "OpImageSparseSampleProjImplicitLod": OpImageSparseSampleProjImplicitLod, + "OpImageSparseSampleProjExplicitLod": OpImageSparseSampleProjExplicitLod, + "OpImageSparseSampleProjDrefImplicitLod": OpImageSparseSampleProjDrefImplicitLod, + "OpImageSparseSampleProjDrefExplicitLod": OpImageSparseSampleProjDrefExplicitLod, + "OpImageSparseFetch": OpImageSparseFetch, + "OpImageSparseGather": OpImageSparseGather, + "OpImageSparseDrefGather": OpImageSparseDrefGather, + "OpImageSparseTexelsResident": OpImageSparseTexelsResident, + "OpNoLine": OpNoLine, + "OpAtomicFlagTestAndSet": OpAtomicFlagTestAndSet, + "OpAtomicFlagClear": OpAtomicFlagClear, + "OpImageSparseRead": OpImageSparseRead, + "OpSizeOf": OpSizeOf, + "OpTypePipeStorage": OpTypePipeStorage, + "OpConstantPipeStorage": OpConstantPipeStorage, + "OpCreatePipeFromPipeStorage": OpCreatePipeFromPipeStorage, + "OpGetKernelLocalSizeForSubgroupCount": OpGetKernelLocalSizeForSubgroupCount, + "OpGetKernelMaxNumSubgroups": OpGetKernelMaxNumSubgroups, + "OpTypeNamedBarrier": OpTypeNamedBarrier, + "OpNamedBarrierInitialize": OpNamedBarrierInitialize, + "OpMemoryNamedBarrier": OpMemoryNamedBarrier, + "OpModuleProcessed": OpModuleProcessed, + "OpExecutionModeId": OpExecutionModeId, + "OpDecorateId": OpDecorateId, + "OpGroupNonUniformElect": OpGroupNonUniformElect, + "OpGroupNonUniformAll": OpGroupNonUniformAll, + "OpGroupNonUniformAny": OpGroupNonUniformAny, + "OpGroupNonUniformAllEqual": OpGroupNonUniformAllEqual, + "OpGroupNonUniformBroadcast": OpGroupNonUniformBroadcast, + "OpGroupNonUniformBroadcastFirst": OpGroupNonUniformBroadcastFirst, + "OpGroupNonUniformBallot": OpGroupNonUniformBallot, + "OpGroupNonUniformInverseBallot": OpGroupNonUniformInverseBallot, + "OpGroupNonUniformBallotBitExtract": OpGroupNonUniformBallotBitExtract, + "OpGroupNonUniformBallotBitCount": OpGroupNonUniformBallotBitCount, + "OpGroupNonUniformBallotFindLSB": OpGroupNonUniformBallotFindLSB, + "OpGroupNonUniformBallotFindMSB": OpGroupNonUniformBallotFindMSB, + "OpGroupNonUniformShuffle": OpGroupNonUniformShuffle, + "OpGroupNonUniformShuffleXor": OpGroupNonUniformShuffleXor, + "OpGroupNonUniformShuffleUp": OpGroupNonUniformShuffleUp, + "OpGroupNonUniformShuffleDown": OpGroupNonUniformShuffleDown, + "OpGroupNonUniformIAdd": OpGroupNonUniformIAdd, + "OpGroupNonUniformFAdd": OpGroupNonUniformFAdd, + "OpGroupNonUniformIMul": OpGroupNonUniformIMul, + "OpGroupNonUniformFMul": OpGroupNonUniformFMul, + "OpGroupNonUniformSMin": OpGroupNonUniformSMin, + "OpGroupNonUniformUMin": OpGroupNonUniformUMin, + "OpGroupNonUniformFMin": OpGroupNonUniformFMin, + "OpGroupNonUniformSMax": OpGroupNonUniformSMax, + "OpGroupNonUniformUMax": OpGroupNonUniformUMax, + "OpGroupNonUniformFMax": OpGroupNonUniformFMax, + "OpGroupNonUniformBitwiseAnd": OpGroupNonUniformBitwiseAnd, + "OpGroupNonUniformBitwiseOr": OpGroupNonUniformBitwiseOr, + "OpGroupNonUniformBitwiseXor": OpGroupNonUniformBitwiseXor, + "OpGroupNonUniformLogicalAnd": OpGroupNonUniformLogicalAnd, + "OpGroupNonUniformLogicalOr": OpGroupNonUniformLogicalOr, + "OpGroupNonUniformLogicalXor": OpGroupNonUniformLogicalXor, + "OpGroupNonUniformQuadBroadcast": OpGroupNonUniformQuadBroadcast, + "OpGroupNonUniformQuadSwap": OpGroupNonUniformQuadSwap, + "OpCopyLogical": OpCopyLogical, + "OpPtrEqual": OpPtrEqual, + "OpPtrNotEqual": OpPtrNotEqual, + "OpPtrDiff": OpPtrDiff, + "OpColorAttachmentReadEXT": OpColorAttachmentReadEXT, + "OpDepthAttachmentReadEXT": OpDepthAttachmentReadEXT, + "OpStencilAttachmentReadEXT": OpStencilAttachmentReadEXT, + "OpTerminateInvocation": OpTerminateInvocation, + "OpTypeUntypedPointerKHR": OpTypeUntypedPointerKHR, + "OpUntypedVariableKHR": OpUntypedVariableKHR, + "OpUntypedAccessChainKHR": OpUntypedAccessChainKHR, + "OpUntypedInBoundsAccessChainKHR": OpUntypedInBoundsAccessChainKHR, + "OpSubgroupBallotKHR": OpSubgroupBallotKHR, + "OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR, + "OpUntypedPtrAccessChainKHR": OpUntypedPtrAccessChainKHR, + "OpUntypedInBoundsPtrAccessChainKHR": OpUntypedInBoundsPtrAccessChainKHR, + "OpUntypedArrayLengthKHR": OpUntypedArrayLengthKHR, + "OpUntypedPrefetchKHR": OpUntypedPrefetchKHR, + "OpSubgroupAllKHR": OpSubgroupAllKHR, + "OpSubgroupAnyKHR": OpSubgroupAnyKHR, + "OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR, + "OpGroupNonUniformRotateKHR": OpGroupNonUniformRotateKHR, + "OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR, + "OpExtInstWithForwardRefsKHR": OpExtInstWithForwardRefsKHR, + "OpTraceRayKHR": OpTraceRayKHR, + "OpExecuteCallableKHR": OpExecuteCallableKHR, + "OpConvertUToAccelerationStructureKHR": OpConvertUToAccelerationStructureKHR, + "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, + "OpTerminateRayKHR": OpTerminateRayKHR, + "OpSDot": OpSDot, + "OpUDot": OpUDot, + "OpSUDot": OpSUDot, + "OpSDotAccSat": OpSDotAccSat, + "OpUDotAccSat": OpUDotAccSat, + "OpSUDotAccSat": OpSUDotAccSat, + "OpTypeCooperativeMatrixKHR": OpTypeCooperativeMatrixKHR, + "OpCooperativeMatrixLoadKHR": OpCooperativeMatrixLoadKHR, + "OpCooperativeMatrixStoreKHR": OpCooperativeMatrixStoreKHR, + "OpCooperativeMatrixMulAddKHR": OpCooperativeMatrixMulAddKHR, + "OpCooperativeMatrixLengthKHR": OpCooperativeMatrixLengthKHR, + "OpConstantCompositeReplicateEXT": OpConstantCompositeReplicateEXT, + "OpSpecConstantCompositeReplicateEXT": OpSpecConstantCompositeReplicateEXT, + "OpCompositeConstructReplicateEXT": OpCompositeConstructReplicateEXT, + "OpTypeRayQueryKHR": OpTypeRayQueryKHR, + "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, + "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, + "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, + "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, + "OpRayQueryProceedKHR": OpRayQueryProceedKHR, + "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, + "OpImageSampleWeightedQCOM": OpImageSampleWeightedQCOM, + "OpImageBoxFilterQCOM": OpImageBoxFilterQCOM, + "OpImageBlockMatchSSDQCOM": OpImageBlockMatchSSDQCOM, + "OpImageBlockMatchSADQCOM": OpImageBlockMatchSADQCOM, + "OpImageBlockMatchWindowSSDQCOM": OpImageBlockMatchWindowSSDQCOM, + "OpImageBlockMatchWindowSADQCOM": OpImageBlockMatchWindowSADQCOM, + "OpImageBlockMatchGatherSSDQCOM": OpImageBlockMatchGatherSSDQCOM, + "OpImageBlockMatchGatherSADQCOM": OpImageBlockMatchGatherSADQCOM, + "OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD, + "OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD, + "OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD, + "OpGroupUMinNonUniformAMD": OpGroupUMinNonUniformAMD, + "OpGroupSMinNonUniformAMD": OpGroupSMinNonUniformAMD, + "OpGroupFMaxNonUniformAMD": OpGroupFMaxNonUniformAMD, + "OpGroupUMaxNonUniformAMD": OpGroupUMaxNonUniformAMD, + "OpGroupSMaxNonUniformAMD": OpGroupSMaxNonUniformAMD, + "OpFragmentMaskFetchAMD": OpFragmentMaskFetchAMD, + "OpFragmentFetchAMD": OpFragmentFetchAMD, + "OpReadClockKHR": OpReadClockKHR, + "OpAllocateNodePayloadsAMDX": OpAllocateNodePayloadsAMDX, + "OpEnqueueNodePayloadsAMDX": OpEnqueueNodePayloadsAMDX, + "OpTypeNodePayloadArrayAMDX": OpTypeNodePayloadArrayAMDX, + "OpFinishWritingNodePayloadAMDX": OpFinishWritingNodePayloadAMDX, + "OpNodePayloadArrayLengthAMDX": OpNodePayloadArrayLengthAMDX, + "OpIsNodePayloadValidAMDX": OpIsNodePayloadValidAMDX, + "OpConstantStringAMDX": OpConstantStringAMDX, + "OpSpecConstantStringAMDX": OpSpecConstantStringAMDX, + "OpGroupNonUniformQuadAllKHR": OpGroupNonUniformQuadAllKHR, + "OpGroupNonUniformQuadAnyKHR": OpGroupNonUniformQuadAnyKHR, + "OpHitObjectRecordHitMotionNV": OpHitObjectRecordHitMotionNV, + "OpHitObjectRecordHitWithIndexMotionNV": OpHitObjectRecordHitWithIndexMotionNV, + "OpHitObjectRecordMissMotionNV": OpHitObjectRecordMissMotionNV, + "OpHitObjectGetWorldToObjectNV": OpHitObjectGetWorldToObjectNV, + "OpHitObjectGetObjectToWorldNV": OpHitObjectGetObjectToWorldNV, + "OpHitObjectGetObjectRayDirectionNV": OpHitObjectGetObjectRayDirectionNV, + "OpHitObjectGetObjectRayOriginNV": OpHitObjectGetObjectRayOriginNV, + "OpHitObjectTraceRayMotionNV": OpHitObjectTraceRayMotionNV, + "OpHitObjectGetShaderRecordBufferHandleNV": OpHitObjectGetShaderRecordBufferHandleNV, + "OpHitObjectGetShaderBindingTableRecordIndexNV": OpHitObjectGetShaderBindingTableRecordIndexNV, + "OpHitObjectRecordEmptyNV": OpHitObjectRecordEmptyNV, + "OpHitObjectTraceRayNV": OpHitObjectTraceRayNV, + "OpHitObjectRecordHitNV": OpHitObjectRecordHitNV, + "OpHitObjectRecordHitWithIndexNV": OpHitObjectRecordHitWithIndexNV, + "OpHitObjectRecordMissNV": OpHitObjectRecordMissNV, + "OpHitObjectExecuteShaderNV": OpHitObjectExecuteShaderNV, + "OpHitObjectGetCurrentTimeNV": OpHitObjectGetCurrentTimeNV, + "OpHitObjectGetAttributesNV": OpHitObjectGetAttributesNV, + "OpHitObjectGetHitKindNV": OpHitObjectGetHitKindNV, + "OpHitObjectGetPrimitiveIndexNV": OpHitObjectGetPrimitiveIndexNV, + "OpHitObjectGetGeometryIndexNV": OpHitObjectGetGeometryIndexNV, + "OpHitObjectGetInstanceIdNV": OpHitObjectGetInstanceIdNV, + "OpHitObjectGetInstanceCustomIndexNV": OpHitObjectGetInstanceCustomIndexNV, + "OpHitObjectGetWorldRayDirectionNV": OpHitObjectGetWorldRayDirectionNV, + "OpHitObjectGetWorldRayOriginNV": OpHitObjectGetWorldRayOriginNV, + "OpHitObjectGetRayTMaxNV": OpHitObjectGetRayTMaxNV, + "OpHitObjectGetRayTMinNV": OpHitObjectGetRayTMinNV, + "OpHitObjectIsEmptyNV": OpHitObjectIsEmptyNV, + "OpHitObjectIsHitNV": OpHitObjectIsHitNV, + "OpHitObjectIsMissNV": OpHitObjectIsMissNV, + "OpReorderThreadWithHitObjectNV": OpReorderThreadWithHitObjectNV, + "OpReorderThreadWithHintNV": OpReorderThreadWithHintNV, + "OpTypeHitObjectNV": OpTypeHitObjectNV, + "OpImageSampleFootprintNV": OpImageSampleFootprintNV, + "OpCooperativeMatrixConvertNV": OpCooperativeMatrixConvertNV, + "OpEmitMeshTasksEXT": OpEmitMeshTasksEXT, + "OpSetMeshOutputsEXT": OpSetMeshOutputsEXT, + "OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV, + "OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV, + "OpFetchMicroTriangleVertexPositionNV": OpFetchMicroTriangleVertexPositionNV, + "OpFetchMicroTriangleVertexBarycentricNV": OpFetchMicroTriangleVertexBarycentricNV, + "OpReportIntersectionKHR": OpReportIntersectionKHR, + "OpIgnoreIntersectionNV": OpIgnoreIntersectionNV, + "OpTerminateRayNV": OpTerminateRayNV, + "OpTraceNV": OpTraceNV, + "OpTraceMotionNV": OpTraceMotionNV, + "OpTraceRayMotionNV": OpTraceRayMotionNV, + "OpRayQueryGetIntersectionTriangleVertexPositionsKHR": OpRayQueryGetIntersectionTriangleVertexPositionsKHR, + "OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR, + "OpExecuteCallableNV": OpExecuteCallableNV, + "OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV, + "OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV, + "OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV, + "OpCooperativeMatrixMulAddNV": OpCooperativeMatrixMulAddNV, + "OpCooperativeMatrixLengthNV": OpCooperativeMatrixLengthNV, + "OpBeginInvocationInterlockEXT": OpBeginInvocationInterlockEXT, + "OpEndInvocationInterlockEXT": OpEndInvocationInterlockEXT, + "OpCooperativeMatrixReduceNV": OpCooperativeMatrixReduceNV, + "OpCooperativeMatrixLoadTensorNV": OpCooperativeMatrixLoadTensorNV, + "OpCooperativeMatrixStoreTensorNV": OpCooperativeMatrixStoreTensorNV, + "OpCooperativeMatrixPerElementOpNV": OpCooperativeMatrixPerElementOpNV, + "OpTypeTensorLayoutNV": OpTypeTensorLayoutNV, + "OpTypeTensorViewNV": OpTypeTensorViewNV, + "OpCreateTensorLayoutNV": OpCreateTensorLayoutNV, + "OpTensorLayoutSetDimensionNV": OpTensorLayoutSetDimensionNV, + "OpTensorLayoutSetStrideNV": OpTensorLayoutSetStrideNV, + "OpTensorLayoutSliceNV": OpTensorLayoutSliceNV, + "OpTensorLayoutSetClampValueNV": OpTensorLayoutSetClampValueNV, + "OpCreateTensorViewNV": OpCreateTensorViewNV, + "OpTensorViewSetDimensionNV": OpTensorViewSetDimensionNV, + "OpTensorViewSetStrideNV": OpTensorViewSetStrideNV, + "OpDemoteToHelperInvocation": OpDemoteToHelperInvocation, + "OpIsHelperInvocationEXT": OpIsHelperInvocationEXT, + "OpTensorViewSetClipNV": OpTensorViewSetClipNV, + "OpTensorLayoutSetBlockSizeNV": OpTensorLayoutSetBlockSizeNV, + "OpCooperativeMatrixTransposeNV": OpCooperativeMatrixTransposeNV, + "OpConvertUToImageNV": OpConvertUToImageNV, + "OpConvertUToSamplerNV": OpConvertUToSamplerNV, + "OpConvertImageToUNV": OpConvertImageToUNV, + "OpConvertSamplerToUNV": OpConvertSamplerToUNV, + "OpConvertUToSampledImageNV": OpConvertUToSampledImageNV, + "OpConvertSampledImageToUNV": OpConvertSampledImageToUNV, + "OpSamplerImageAddressingModeNV": OpSamplerImageAddressingModeNV, + "OpRawAccessChainNV": OpRawAccessChainNV, + "OpSubgroupShuffleINTEL": OpSubgroupShuffleINTEL, + "OpSubgroupShuffleDownINTEL": OpSubgroupShuffleDownINTEL, + "OpSubgroupShuffleUpINTEL": OpSubgroupShuffleUpINTEL, + "OpSubgroupShuffleXorINTEL": OpSubgroupShuffleXorINTEL, + "OpSubgroupBlockReadINTEL": OpSubgroupBlockReadINTEL, + "OpSubgroupBlockWriteINTEL": OpSubgroupBlockWriteINTEL, + "OpSubgroupImageBlockReadINTEL": OpSubgroupImageBlockReadINTEL, + "OpSubgroupImageBlockWriteINTEL": OpSubgroupImageBlockWriteINTEL, + "OpSubgroupImageMediaBlockReadINTEL": OpSubgroupImageMediaBlockReadINTEL, + "OpSubgroupImageMediaBlockWriteINTEL": OpSubgroupImageMediaBlockWriteINTEL, + "OpUCountLeadingZerosINTEL": OpUCountLeadingZerosINTEL, + "OpUCountTrailingZerosINTEL": OpUCountTrailingZerosINTEL, + "OpAbsISubINTEL": OpAbsISubINTEL, + "OpAbsUSubINTEL": OpAbsUSubINTEL, + "OpIAddSatINTEL": OpIAddSatINTEL, + "OpUAddSatINTEL": OpUAddSatINTEL, + "OpIAverageINTEL": OpIAverageINTEL, + "OpUAverageINTEL": OpUAverageINTEL, + "OpIAverageRoundedINTEL": OpIAverageRoundedINTEL, + "OpUAverageRoundedINTEL": OpUAverageRoundedINTEL, + "OpISubSatINTEL": OpISubSatINTEL, + "OpUSubSatINTEL": OpUSubSatINTEL, + "OpIMul32x16INTEL": OpIMul32x16INTEL, + "OpUMul32x16INTEL": OpUMul32x16INTEL, + "OpConstantFunctionPointerINTEL": OpConstantFunctionPointerINTEL, + "OpFunctionPointerCallINTEL": OpFunctionPointerCallINTEL, + "OpAsmTargetINTEL": OpAsmTargetINTEL, + "OpAsmINTEL": OpAsmINTEL, + "OpAsmCallINTEL": OpAsmCallINTEL, + "OpAtomicFMinEXT": OpAtomicFMinEXT, + "OpAtomicFMaxEXT": OpAtomicFMaxEXT, + "OpAssumeTrueKHR": OpAssumeTrueKHR, + "OpExpectKHR": OpExpectKHR, + "OpDecorateString": OpDecorateString, + "OpMemberDecorateString": OpMemberDecorateString, + "OpVmeImageINTEL": OpVmeImageINTEL, + "OpTypeVmeImageINTEL": OpTypeVmeImageINTEL, + "OpTypeAvcImePayloadINTEL": OpTypeAvcImePayloadINTEL, + "OpTypeAvcRefPayloadINTEL": OpTypeAvcRefPayloadINTEL, + "OpTypeAvcSicPayloadINTEL": OpTypeAvcSicPayloadINTEL, + "OpTypeAvcMcePayloadINTEL": OpTypeAvcMcePayloadINTEL, + "OpTypeAvcMceResultINTEL": OpTypeAvcMceResultINTEL, + "OpTypeAvcImeResultINTEL": OpTypeAvcImeResultINTEL, + "OpTypeAvcImeResultSingleReferenceStreamoutINTEL": OpTypeAvcImeResultSingleReferenceStreamoutINTEL, + "OpTypeAvcImeResultDualReferenceStreamoutINTEL": OpTypeAvcImeResultDualReferenceStreamoutINTEL, + "OpTypeAvcImeSingleReferenceStreaminINTEL": OpTypeAvcImeSingleReferenceStreaminINTEL, + "OpTypeAvcImeDualReferenceStreaminINTEL": OpTypeAvcImeDualReferenceStreaminINTEL, + "OpTypeAvcRefResultINTEL": OpTypeAvcRefResultINTEL, + "OpTypeAvcSicResultINTEL": OpTypeAvcSicResultINTEL, + "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL, + "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL, + "OpSubgroupAvcMceSetInterShapePenaltyINTEL": OpSubgroupAvcMceSetInterShapePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL": OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL, + "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL": OpSubgroupAvcMceSetInterDirectionPenaltyINTEL, + "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL": OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL, + "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL, + "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL, + "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL, + "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL": OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL, + "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL": OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL, + "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL, + "OpSubgroupAvcMceSetAcOnlyHaarINTEL": OpSubgroupAvcMceSetAcOnlyHaarINTEL, + "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL, + "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL, + "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL, + "OpSubgroupAvcMceConvertToImePayloadINTEL": OpSubgroupAvcMceConvertToImePayloadINTEL, + "OpSubgroupAvcMceConvertToImeResultINTEL": OpSubgroupAvcMceConvertToImeResultINTEL, + "OpSubgroupAvcMceConvertToRefPayloadINTEL": OpSubgroupAvcMceConvertToRefPayloadINTEL, + "OpSubgroupAvcMceConvertToRefResultINTEL": OpSubgroupAvcMceConvertToRefResultINTEL, + "OpSubgroupAvcMceConvertToSicPayloadINTEL": OpSubgroupAvcMceConvertToSicPayloadINTEL, + "OpSubgroupAvcMceConvertToSicResultINTEL": OpSubgroupAvcMceConvertToSicResultINTEL, + "OpSubgroupAvcMceGetMotionVectorsINTEL": OpSubgroupAvcMceGetMotionVectorsINTEL, + "OpSubgroupAvcMceGetInterDistortionsINTEL": OpSubgroupAvcMceGetInterDistortionsINTEL, + "OpSubgroupAvcMceGetBestInterDistortionsINTEL": OpSubgroupAvcMceGetBestInterDistortionsINTEL, + "OpSubgroupAvcMceGetInterMajorShapeINTEL": OpSubgroupAvcMceGetInterMajorShapeINTEL, + "OpSubgroupAvcMceGetInterMinorShapeINTEL": OpSubgroupAvcMceGetInterMinorShapeINTEL, + "OpSubgroupAvcMceGetInterDirectionsINTEL": OpSubgroupAvcMceGetInterDirectionsINTEL, + "OpSubgroupAvcMceGetInterMotionVectorCountINTEL": OpSubgroupAvcMceGetInterMotionVectorCountINTEL, + "OpSubgroupAvcMceGetInterReferenceIdsINTEL": OpSubgroupAvcMceGetInterReferenceIdsINTEL, + "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL, + "OpSubgroupAvcImeInitializeINTEL": OpSubgroupAvcImeInitializeINTEL, + "OpSubgroupAvcImeSetSingleReferenceINTEL": OpSubgroupAvcImeSetSingleReferenceINTEL, + "OpSubgroupAvcImeSetDualReferenceINTEL": OpSubgroupAvcImeSetDualReferenceINTEL, + "OpSubgroupAvcImeRefWindowSizeINTEL": OpSubgroupAvcImeRefWindowSizeINTEL, + "OpSubgroupAvcImeAdjustRefOffsetINTEL": OpSubgroupAvcImeAdjustRefOffsetINTEL, + "OpSubgroupAvcImeConvertToMcePayloadINTEL": OpSubgroupAvcImeConvertToMcePayloadINTEL, + "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL": OpSubgroupAvcImeSetMaxMotionVectorCountINTEL, + "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL": OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL, + "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL": OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL, + "OpSubgroupAvcImeSetWeightedSadINTEL": OpSubgroupAvcImeSetWeightedSadINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL, + "OpSubgroupAvcImeConvertToMceResultINTEL": OpSubgroupAvcImeConvertToMceResultINTEL, + "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL": OpSubgroupAvcImeGetSingleReferenceStreaminINTEL, + "OpSubgroupAvcImeGetDualReferenceStreaminINTEL": OpSubgroupAvcImeGetDualReferenceStreaminINTEL, + "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL": OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL, + "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL": OpSubgroupAvcImeStripDualReferenceStreamoutINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL, + "OpSubgroupAvcImeGetBorderReachedINTEL": OpSubgroupAvcImeGetBorderReachedINTEL, + "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL": OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL, + "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL": OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL, + "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL, + "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL, + "OpSubgroupAvcFmeInitializeINTEL": OpSubgroupAvcFmeInitializeINTEL, + "OpSubgroupAvcBmeInitializeINTEL": OpSubgroupAvcBmeInitializeINTEL, + "OpSubgroupAvcRefConvertToMcePayloadINTEL": OpSubgroupAvcRefConvertToMcePayloadINTEL, + "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL": OpSubgroupAvcRefSetBidirectionalMixDisableINTEL, + "OpSubgroupAvcRefSetBilinearFilterEnableINTEL": OpSubgroupAvcRefSetBilinearFilterEnableINTEL, + "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL": OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL": OpSubgroupAvcRefEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL, + "OpSubgroupAvcRefConvertToMceResultINTEL": OpSubgroupAvcRefConvertToMceResultINTEL, + "OpSubgroupAvcSicInitializeINTEL": OpSubgroupAvcSicInitializeINTEL, + "OpSubgroupAvcSicConfigureSkcINTEL": OpSubgroupAvcSicConfigureSkcINTEL, + "OpSubgroupAvcSicConfigureIpeLumaINTEL": OpSubgroupAvcSicConfigureIpeLumaINTEL, + "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL": OpSubgroupAvcSicConfigureIpeLumaChromaINTEL, + "OpSubgroupAvcSicGetMotionVectorMaskINTEL": OpSubgroupAvcSicGetMotionVectorMaskINTEL, + "OpSubgroupAvcSicConvertToMcePayloadINTEL": OpSubgroupAvcSicConvertToMcePayloadINTEL, + "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL": OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL, + "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL, + "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL, + "OpSubgroupAvcSicSetBilinearFilterEnableINTEL": OpSubgroupAvcSicSetBilinearFilterEnableINTEL, + "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL": OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL, + "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL": OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL, + "OpSubgroupAvcSicEvaluateIpeINTEL": OpSubgroupAvcSicEvaluateIpeINTEL, + "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL": OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL": OpSubgroupAvcSicEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL, + "OpSubgroupAvcSicConvertToMceResultINTEL": OpSubgroupAvcSicConvertToMceResultINTEL, + "OpSubgroupAvcSicGetIpeLumaShapeINTEL": OpSubgroupAvcSicGetIpeLumaShapeINTEL, + "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL": OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL, + "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL": OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL, + "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL": OpSubgroupAvcSicGetPackedIpeLumaModesINTEL, + "OpSubgroupAvcSicGetIpeChromaModeINTEL": OpSubgroupAvcSicGetIpeChromaModeINTEL, + "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL, + "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL, + "OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL, + "OpVariableLengthArrayINTEL": OpVariableLengthArrayINTEL, + "OpSaveMemoryINTEL": OpSaveMemoryINTEL, + "OpRestoreMemoryINTEL": OpRestoreMemoryINTEL, + "OpArbitraryFloatSinCosPiINTEL": OpArbitraryFloatSinCosPiINTEL, + "OpArbitraryFloatCastINTEL": OpArbitraryFloatCastINTEL, + "OpArbitraryFloatCastFromIntINTEL": OpArbitraryFloatCastFromIntINTEL, + "OpArbitraryFloatCastToIntINTEL": OpArbitraryFloatCastToIntINTEL, + "OpArbitraryFloatAddINTEL": OpArbitraryFloatAddINTEL, + "OpArbitraryFloatSubINTEL": OpArbitraryFloatSubINTEL, + "OpArbitraryFloatMulINTEL": OpArbitraryFloatMulINTEL, + "OpArbitraryFloatDivINTEL": OpArbitraryFloatDivINTEL, + "OpArbitraryFloatGTINTEL": OpArbitraryFloatGTINTEL, + "OpArbitraryFloatGEINTEL": OpArbitraryFloatGEINTEL, + "OpArbitraryFloatLTINTEL": OpArbitraryFloatLTINTEL, + "OpArbitraryFloatLEINTEL": OpArbitraryFloatLEINTEL, + "OpArbitraryFloatEQINTEL": OpArbitraryFloatEQINTEL, + "OpArbitraryFloatRecipINTEL": OpArbitraryFloatRecipINTEL, + "OpArbitraryFloatRSqrtINTEL": OpArbitraryFloatRSqrtINTEL, + "OpArbitraryFloatCbrtINTEL": OpArbitraryFloatCbrtINTEL, + "OpArbitraryFloatHypotINTEL": OpArbitraryFloatHypotINTEL, + "OpArbitraryFloatSqrtINTEL": OpArbitraryFloatSqrtINTEL, + "OpArbitraryFloatLogINTEL": OpArbitraryFloatLogINTEL, + "OpArbitraryFloatLog2INTEL": OpArbitraryFloatLog2INTEL, + "OpArbitraryFloatLog10INTEL": OpArbitraryFloatLog10INTEL, + "OpArbitraryFloatLog1pINTEL": OpArbitraryFloatLog1pINTEL, + "OpArbitraryFloatExpINTEL": OpArbitraryFloatExpINTEL, + "OpArbitraryFloatExp2INTEL": OpArbitraryFloatExp2INTEL, + "OpArbitraryFloatExp10INTEL": OpArbitraryFloatExp10INTEL, + "OpArbitraryFloatExpm1INTEL": OpArbitraryFloatExpm1INTEL, + "OpArbitraryFloatSinINTEL": OpArbitraryFloatSinINTEL, + "OpArbitraryFloatCosINTEL": OpArbitraryFloatCosINTEL, + "OpArbitraryFloatSinCosINTEL": OpArbitraryFloatSinCosINTEL, + "OpArbitraryFloatSinPiINTEL": OpArbitraryFloatSinPiINTEL, + "OpArbitraryFloatCosPiINTEL": OpArbitraryFloatCosPiINTEL, + "OpArbitraryFloatASinINTEL": OpArbitraryFloatASinINTEL, + "OpArbitraryFloatASinPiINTEL": OpArbitraryFloatASinPiINTEL, + "OpArbitraryFloatACosINTEL": OpArbitraryFloatACosINTEL, + "OpArbitraryFloatACosPiINTEL": OpArbitraryFloatACosPiINTEL, + "OpArbitraryFloatATanINTEL": OpArbitraryFloatATanINTEL, + "OpArbitraryFloatATanPiINTEL": OpArbitraryFloatATanPiINTEL, + "OpArbitraryFloatATan2INTEL": OpArbitraryFloatATan2INTEL, + "OpArbitraryFloatPowINTEL": OpArbitraryFloatPowINTEL, + "OpArbitraryFloatPowRINTEL": OpArbitraryFloatPowRINTEL, + "OpArbitraryFloatPowNINTEL": OpArbitraryFloatPowNINTEL, + "OpLoopControlINTEL": OpLoopControlINTEL, + "OpAliasDomainDeclINTEL": OpAliasDomainDeclINTEL, + "OpAliasScopeDeclINTEL": OpAliasScopeDeclINTEL, + "OpAliasScopeListDeclINTEL": OpAliasScopeListDeclINTEL, + "OpFixedSqrtINTEL": OpFixedSqrtINTEL, + "OpFixedRecipINTEL": OpFixedRecipINTEL, + "OpFixedRsqrtINTEL": OpFixedRsqrtINTEL, + "OpFixedSinINTEL": OpFixedSinINTEL, + "OpFixedCosINTEL": OpFixedCosINTEL, + "OpFixedSinCosINTEL": OpFixedSinCosINTEL, + "OpFixedSinPiINTEL": OpFixedSinPiINTEL, + "OpFixedCosPiINTEL": OpFixedCosPiINTEL, + "OpFixedSinCosPiINTEL": OpFixedSinCosPiINTEL, + "OpFixedLogINTEL": OpFixedLogINTEL, + "OpFixedExpINTEL": OpFixedExpINTEL, + "OpPtrCastToCrossWorkgroupINTEL": OpPtrCastToCrossWorkgroupINTEL, + "OpCrossWorkgroupCastToPtrINTEL": OpCrossWorkgroupCastToPtrINTEL, + "OpReadPipeBlockingINTEL": OpReadPipeBlockingINTEL, + "OpWritePipeBlockingINTEL": OpWritePipeBlockingINTEL, + "OpFPGARegINTEL": OpFPGARegINTEL, + "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, + "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, + "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, + "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, + "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, + "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, + "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, + "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, + "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, + "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, + "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, + "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, + "OpAtomicFAddEXT": OpAtomicFAddEXT, + "OpTypeBufferSurfaceINTEL": OpTypeBufferSurfaceINTEL, + "OpTypeStructContinuedINTEL": OpTypeStructContinuedINTEL, + "OpConstantCompositeContinuedINTEL": OpConstantCompositeContinuedINTEL, + "OpSpecConstantCompositeContinuedINTEL": OpSpecConstantCompositeContinuedINTEL, + "OpCompositeConstructContinuedINTEL": OpCompositeConstructContinuedINTEL, + "OpConvertFToBF16INTEL": OpConvertFToBF16INTEL, + "OpConvertBF16ToFINTEL": OpConvertBF16ToFINTEL, + "OpControlBarrierArriveINTEL": OpControlBarrierArriveINTEL, + "OpControlBarrierWaitINTEL": OpControlBarrierWaitINTEL, + "OpArithmeticFenceEXT": OpArithmeticFenceEXT, + "OpSubgroupBlockPrefetchINTEL": OpSubgroupBlockPrefetchINTEL, + "OpGroupIMulKHR": OpGroupIMulKHR, + "OpGroupFMulKHR": OpGroupFMulKHR, + "OpGroupBitwiseAndKHR": OpGroupBitwiseAndKHR, + "OpGroupBitwiseOrKHR": OpGroupBitwiseOrKHR, + "OpGroupBitwiseXorKHR": OpGroupBitwiseXorKHR, + "OpGroupLogicalAndKHR": OpGroupLogicalAndKHR, + "OpGroupLogicalOrKHR": OpGroupLogicalOrKHR, + "OpGroupLogicalXorKHR": OpGroupLogicalXorKHR, + "OpMaskedGatherINTEL": OpMaskedGatherINTEL, + "OpMaskedScatterINTEL": OpMaskedScatterINTEL, + } + + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap { + "GLSL.std.450": { + "Round": GLSLStd450_Round, + "RoundEven": GLSLStd450_RoundEven, + "Trunc": GLSLStd450_Trunc, + "FAbs": GLSLStd450_FAbs, + "SAbs": GLSLStd450_SAbs, + "FSign": GLSLStd450_FSign, + "SSign": GLSLStd450_SSign, + "Floor": GLSLStd450_Floor, + "Ceil": GLSLStd450_Ceil, + "Fract": GLSLStd450_Fract, + "Radians": GLSLStd450_Radians, + "Degrees": GLSLStd450_Degrees, + "Sin": GLSLStd450_Sin, + "Cos": GLSLStd450_Cos, + "Tan": GLSLStd450_Tan, + "Asin": GLSLStd450_Asin, + "Acos": GLSLStd450_Acos, + "Atan": GLSLStd450_Atan, + "Sinh": GLSLStd450_Sinh, + "Cosh": GLSLStd450_Cosh, + "Tanh": GLSLStd450_Tanh, + "Asinh": GLSLStd450_Asinh, + "Acosh": GLSLStd450_Acosh, + "Atanh": GLSLStd450_Atanh, + "Atan2": GLSLStd450_Atan2, + "Pow": GLSLStd450_Pow, + "Exp": GLSLStd450_Exp, + "Log": GLSLStd450_Log, + "Exp2": GLSLStd450_Exp2, + "Log2": GLSLStd450_Log2, + "Sqrt": GLSLStd450_Sqrt, + "InverseSqrt": GLSLStd450_InverseSqrt, + "Determinant": GLSLStd450_Determinant, + "MatrixInverse": GLSLStd450_MatrixInverse, + "Modf": GLSLStd450_Modf, + "ModfStruct": GLSLStd450_ModfStruct, + "FMin": GLSLStd450_FMin, + "UMin": GLSLStd450_UMin, + "SMin": GLSLStd450_SMin, + "FMax": GLSLStd450_FMax, + "UMax": GLSLStd450_UMax, + "SMax": GLSLStd450_SMax, + "FClamp": GLSLStd450_FClamp, + "UClamp": GLSLStd450_UClamp, + "SClamp": GLSLStd450_SClamp, + "FMix": GLSLStd450_FMix, + "IMix": GLSLStd450_IMix, + "Step": GLSLStd450_Step, + "SmoothStep": GLSLStd450_SmoothStep, + "Fma": GLSLStd450_Fma, + "Frexp": GLSLStd450_Frexp, + "FrexpStruct": GLSLStd450_FrexpStruct, + "Ldexp": GLSLStd450_Ldexp, + "PackSnorm4x8": GLSLStd450_PackSnorm4x8, + "PackUnorm4x8": GLSLStd450_PackUnorm4x8, + "PackSnorm2x16": GLSLStd450_PackSnorm2x16, + "PackUnorm2x16": GLSLStd450_PackUnorm2x16, + "PackHalf2x16": GLSLStd450_PackHalf2x16, + "PackDouble2x32": GLSLStd450_PackDouble2x32, + "UnpackSnorm2x16": GLSLStd450_UnpackSnorm2x16, + "UnpackUnorm2x16": GLSLStd450_UnpackUnorm2x16, + "UnpackHalf2x16": GLSLStd450_UnpackHalf2x16, + "UnpackSnorm4x8": GLSLStd450_UnpackSnorm4x8, + "UnpackUnorm4x8": GLSLStd450_UnpackUnorm4x8, + "UnpackDouble2x32": GLSLStd450_UnpackDouble2x32, + "Length": GLSLStd450_Length, + "Distance": GLSLStd450_Distance, + "Cross": GLSLStd450_Cross, + "Normalize": GLSLStd450_Normalize, + "FaceForward": GLSLStd450_FaceForward, + "Reflect": GLSLStd450_Reflect, + "Refract": GLSLStd450_Refract, + "FindILsb": GLSLStd450_FindILsb, + "FindSMsb": GLSLStd450_FindSMsb, + "FindUMsb": GLSLStd450_FindUMsb, + "InterpolateAtCentroid": GLSLStd450_InterpolateAtCentroid, + "InterpolateAtSample": GLSLStd450_InterpolateAtSample, + "InterpolateAtOffset": GLSLStd450_InterpolateAtOffset, + "NMin": GLSLStd450_NMin, + "NMax": GLSLStd450_NMax, + "NClamp": GLSLStd450_NClamp, + }, + "OpenCL.std": { + "acos": OpenCLStd_acos, + "acosh": OpenCLStd_acosh, + "acospi": OpenCLStd_acospi, + "asin": OpenCLStd_asin, + "asinh": OpenCLStd_asinh, + "asinpi": OpenCLStd_asinpi, + "atan": OpenCLStd_atan, + "atan2": OpenCLStd_atan2, + "atanh": OpenCLStd_atanh, + "atanpi": OpenCLStd_atanpi, + "atan2pi": OpenCLStd_atan2pi, + "cbrt": OpenCLStd_cbrt, + "ceil": OpenCLStd_ceil, + "copysign": OpenCLStd_copysign, + "cos": OpenCLStd_cos, + "cosh": OpenCLStd_cosh, + "cospi": OpenCLStd_cospi, + "erfc": OpenCLStd_erfc, + "erf": OpenCLStd_erf, + "exp": OpenCLStd_exp, + "exp2": OpenCLStd_exp2, + "exp10": OpenCLStd_exp10, + "expm1": OpenCLStd_expm1, + "fabs": OpenCLStd_fabs, + "fdim": OpenCLStd_fdim, + "floor": OpenCLStd_floor, + "fma": OpenCLStd_fma, + "fmax": OpenCLStd_fmax, + "fmin": OpenCLStd_fmin, + "fmod": OpenCLStd_fmod, + "fract": OpenCLStd_fract, + "frexp": OpenCLStd_frexp, + "hypot": OpenCLStd_hypot, + "ilogb": OpenCLStd_ilogb, + "ldexp": OpenCLStd_ldexp, + "lgamma": OpenCLStd_lgamma, + "lgamma_r": OpenCLStd_lgamma_r, + "log": OpenCLStd_log, + "log2": OpenCLStd_log2, + "log10": OpenCLStd_log10, + "log1p": OpenCLStd_log1p, + "logb": OpenCLStd_logb, + "mad": OpenCLStd_mad, + "maxmag": OpenCLStd_maxmag, + "minmag": OpenCLStd_minmag, + "modf": OpenCLStd_modf, + "nan": OpenCLStd_nan, + "nextafter": OpenCLStd_nextafter, + "pow": OpenCLStd_pow, + "pown": OpenCLStd_pown, + "powr": OpenCLStd_powr, + "remainder": OpenCLStd_remainder, + "remquo": OpenCLStd_remquo, + "rint": OpenCLStd_rint, + "rootn": OpenCLStd_rootn, + "round": OpenCLStd_round, + "rsqrt": OpenCLStd_rsqrt, + "sin": OpenCLStd_sin, + "sincos": OpenCLStd_sincos, + "sinh": OpenCLStd_sinh, + "sinpi": OpenCLStd_sinpi, + "sqrt": OpenCLStd_sqrt, + "tan": OpenCLStd_tan, + "tanh": OpenCLStd_tanh, + "tanpi": OpenCLStd_tanpi, + "tgamma": OpenCLStd_tgamma, + "trunc": OpenCLStd_trunc, + "half_cos": OpenCLStd_half_cos, + "half_divide": OpenCLStd_half_divide, + "half_exp": OpenCLStd_half_exp, + "half_exp2": OpenCLStd_half_exp2, + "half_exp10": OpenCLStd_half_exp10, + "half_log": OpenCLStd_half_log, + "half_log2": OpenCLStd_half_log2, + "half_log10": OpenCLStd_half_log10, + "half_powr": OpenCLStd_half_powr, + "half_recip": OpenCLStd_half_recip, + "half_rsqrt": OpenCLStd_half_rsqrt, + "half_sin": OpenCLStd_half_sin, + "half_sqrt": OpenCLStd_half_sqrt, + "half_tan": OpenCLStd_half_tan, + "native_cos": OpenCLStd_native_cos, + "native_divide": OpenCLStd_native_divide, + "native_exp": OpenCLStd_native_exp, + "native_exp2": OpenCLStd_native_exp2, + "native_exp10": OpenCLStd_native_exp10, + "native_log": OpenCLStd_native_log, + "native_log2": OpenCLStd_native_log2, + "native_log10": OpenCLStd_native_log10, + "native_powr": OpenCLStd_native_powr, + "native_recip": OpenCLStd_native_recip, + "native_rsqrt": OpenCLStd_native_rsqrt, + "native_sin": OpenCLStd_native_sin, + "native_sqrt": OpenCLStd_native_sqrt, + "native_tan": OpenCLStd_native_tan, + "s_abs": OpenCLStd_s_abs, + "s_abs_diff": OpenCLStd_s_abs_diff, + "s_add_sat": OpenCLStd_s_add_sat, + "u_add_sat": OpenCLStd_u_add_sat, + "s_hadd": OpenCLStd_s_hadd, + "u_hadd": OpenCLStd_u_hadd, + "s_rhadd": OpenCLStd_s_rhadd, + "u_rhadd": OpenCLStd_u_rhadd, + "s_clamp": OpenCLStd_s_clamp, + "u_clamp": OpenCLStd_u_clamp, + "clz": OpenCLStd_clz, + "ctz": OpenCLStd_ctz, + "s_mad_hi": OpenCLStd_s_mad_hi, + "u_mad_sat": OpenCLStd_u_mad_sat, + "s_mad_sat": OpenCLStd_s_mad_sat, + "s_max": OpenCLStd_s_max, + "u_max": OpenCLStd_u_max, + "s_min": OpenCLStd_s_min, + "u_min": OpenCLStd_u_min, + "s_mul_hi": OpenCLStd_s_mul_hi, + "rotate": OpenCLStd_rotate, + "s_sub_sat": OpenCLStd_s_sub_sat, + "u_sub_sat": OpenCLStd_u_sub_sat, + "u_upsample": OpenCLStd_u_upsample, + "s_upsample": OpenCLStd_s_upsample, + "popcount": OpenCLStd_popcount, + "s_mad24": OpenCLStd_s_mad24, + "u_mad24": OpenCLStd_u_mad24, + "s_mul24": OpenCLStd_s_mul24, + "u_mul24": OpenCLStd_u_mul24, + "u_abs": OpenCLStd_u_abs, + "u_abs_diff": OpenCLStd_u_abs_diff, + "u_mul_hi": OpenCLStd_u_mul_hi, + "u_mad_hi": OpenCLStd_u_mad_hi, + "fclamp": OpenCLStd_fclamp, + "degrees": OpenCLStd_degrees, + "fmax_common": OpenCLStd_fmax_common, + "fmin_common": OpenCLStd_fmin_common, + "mix": OpenCLStd_mix, + "radians": OpenCLStd_radians, + "step": OpenCLStd_step, + "smoothstep": OpenCLStd_smoothstep, + "sign": OpenCLStd_sign, + "cross": OpenCLStd_cross, + "distance": OpenCLStd_distance, + "length": OpenCLStd_length, + "normalize": OpenCLStd_normalize, + "fast_distance": OpenCLStd_fast_distance, + "fast_length": OpenCLStd_fast_length, + "fast_normalize": OpenCLStd_fast_normalize, + "bitselect": OpenCLStd_bitselect, + "select": OpenCLStd_select, + "vloadn": OpenCLStd_vloadn, + "vstoren": OpenCLStd_vstoren, + "vload_half": OpenCLStd_vload_half, + "vload_halfn": OpenCLStd_vload_halfn, + "vstore_half": OpenCLStd_vstore_half, + "vstore_half_r": OpenCLStd_vstore_half_r, + "vstore_halfn": OpenCLStd_vstore_halfn, + "vstore_halfn_r": OpenCLStd_vstore_halfn_r, + "vloada_halfn": OpenCLStd_vloada_halfn, + "vstorea_halfn": OpenCLStd_vstorea_halfn, + "vstorea_halfn_r": OpenCLStd_vstorea_halfn_r, + "shuffle": OpenCLStd_shuffle, + "shuffle2": OpenCLStd_shuffle2, + "printf": OpenCLStd_printf, + "prefetch": OpenCLStd_prefetch, + }, + "OpenCL.DebugInfo.100": { + "DebugInfoNone": OpenCLDebugInfo100_DebugInfoNone, + "DebugCompilationUnit": OpenCLDebugInfo100_DebugCompilationUnit, + "DebugTypeBasic": OpenCLDebugInfo100_DebugTypeBasic, + "DebugTypePointer": OpenCLDebugInfo100_DebugTypePointer, + "DebugTypeQualifier": OpenCLDebugInfo100_DebugTypeQualifier, + "DebugTypeArray": OpenCLDebugInfo100_DebugTypeArray, + "DebugTypeVector": OpenCLDebugInfo100_DebugTypeVector, + "DebugTypedef": OpenCLDebugInfo100_DebugTypedef, + "DebugTypeFunction": OpenCLDebugInfo100_DebugTypeFunction, + "DebugTypeEnum": OpenCLDebugInfo100_DebugTypeEnum, + "DebugTypeComposite": OpenCLDebugInfo100_DebugTypeComposite, + "DebugTypeMember": OpenCLDebugInfo100_DebugTypeMember, + "DebugTypeInheritance": OpenCLDebugInfo100_DebugTypeInheritance, + "DebugTypePtrToMember": OpenCLDebugInfo100_DebugTypePtrToMember, + "DebugTypeTemplate": OpenCLDebugInfo100_DebugTypeTemplate, + "DebugTypeTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateParameter, + "DebugTypeTemplateTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter, + "DebugTypeTemplateParameterPack": OpenCLDebugInfo100_DebugTypeTemplateParameterPack, + "DebugGlobalVariable": OpenCLDebugInfo100_DebugGlobalVariable, + "DebugFunctionDeclaration": OpenCLDebugInfo100_DebugFunctionDeclaration, + "DebugFunction": OpenCLDebugInfo100_DebugFunction, + "DebugLexicalBlock": OpenCLDebugInfo100_DebugLexicalBlock, + "DebugLexicalBlockDiscriminator": OpenCLDebugInfo100_DebugLexicalBlockDiscriminator, + "DebugScope": OpenCLDebugInfo100_DebugScope, + "DebugNoScope": OpenCLDebugInfo100_DebugNoScope, + "DebugInlinedAt": OpenCLDebugInfo100_DebugInlinedAt, + "DebugLocalVariable": OpenCLDebugInfo100_DebugLocalVariable, + "DebugInlinedVariable": OpenCLDebugInfo100_DebugInlinedVariable, + "DebugDeclare": OpenCLDebugInfo100_DebugDeclare, + "DebugValue": OpenCLDebugInfo100_DebugValue, + "DebugOperation": OpenCLDebugInfo100_DebugOperation, + "DebugExpression": OpenCLDebugInfo100_DebugExpression, + "DebugMacroDef": OpenCLDebugInfo100_DebugMacroDef, + "DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef, + "DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity, + "DebugSource": OpenCLDebugInfo100_DebugSource, + "DebugModuleINTEL": OpenCLDebugInfo100_DebugModuleINTEL, + }, + } + + OpNop = &Opcode { + Opname: "OpNop", + Class: "Miscellaneous", + Opcode: 0, + Operands: []Operand { + }, + } + OpUndef = &Opcode { + Opname: "OpUndef", + Class: "Miscellaneous", + Opcode: 1, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSourceContinued = &Opcode { + Opname: "OpSourceContinued", + Class: "Debug", + Opcode: 2, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Continued Source'", + Quantifier: "", + }, + }, + } + OpSource = &Opcode { + Opname: "OpSource", + Class: "Debug", + Opcode: 3, + Operands: []Operand { + Operand { + Kind: OperandKindSourceLanguage, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Source'", + Quantifier: "?", + }, + }, + } + OpSourceExtension = &Opcode { + Opname: "OpSourceExtension", + Class: "Debug", + Opcode: 4, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Extension'", + Quantifier: "", + }, + }, + } + OpName = &Opcode { + Opname: "OpName", + Class: "Debug", + Opcode: 5, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpMemberName = &Opcode { + Opname: "OpMemberName", + Class: "Debug", + Opcode: 6, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpString = &Opcode { + Opname: "OpString", + Class: "Debug", + Opcode: 7, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'String'", + Quantifier: "", + }, + }, + } + OpLine = &Opcode { + Opname: "OpLine", + Class: "Debug", + Opcode: 8, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpExtension = &Opcode { + Opname: "OpExtension", + Class: "Extension", + Opcode: 10, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpExtInstImport = &Opcode { + Opname: "OpExtInstImport", + Class: "Extension", + Opcode: 11, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpExtInst = &Opcode { + Opname: "OpExtInst", + Class: "Extension", + Opcode: 12, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Set'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralExtInstInteger, + Name: "'Instruction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1', + 'Operand 2', + ...", + Quantifier: "*", + }, + }, + } + OpMemoryModel = &Opcode { + Opname: "OpMemoryModel", + Class: "Mode-Setting", + Opcode: 14, + Operands: []Operand { + Operand { + Kind: OperandKindAddressingModel, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryModel, + Name: "", + Quantifier: "", + }, + }, + } + OpEntryPoint = &Opcode { + Opname: "OpEntryPoint", + Class: "Mode-Setting", + Opcode: 15, + Operands: []Operand { + Operand { + Kind: OperandKindExecutionModel, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Interface'", + Quantifier: "*", + }, + }, + } + OpExecutionMode = &Opcode { + Opname: "OpExecutionMode", + Class: "Mode-Setting", + Opcode: 16, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindExecutionMode, + Name: "'Mode'", + Quantifier: "", + }, + }, + } + OpCapability = &Opcode { + Opname: "OpCapability", + Class: "Mode-Setting", + Opcode: 17, + Operands: []Operand { + Operand { + Kind: OperandKindCapability, + Name: "'Capability'", + Quantifier: "", + }, + }, + } + OpTypeVoid = &Opcode { + Opname: "OpTypeVoid", + Class: "Type-Declaration", + Opcode: 19, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeBool = &Opcode { + Opname: "OpTypeBool", + Class: "Type-Declaration", + Opcode: 20, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeInt = &Opcode { + Opname: "OpTypeInt", + Class: "Type-Declaration", + Opcode: 21, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Signedness'", + Quantifier: "", + }, + }, + } + OpTypeFloat = &Opcode { + Opname: "OpTypeFloat", + Class: "Type-Declaration", + Opcode: 22, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPEncoding, + Name: "'Floating Point Encoding'", + Quantifier: "?", + }, + }, + } + OpTypeVector = &Opcode { + Opname: "OpTypeVector", + Class: "Type-Declaration", + Opcode: 23, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Component Count'", + Quantifier: "", + }, + }, + } + OpTypeMatrix = &Opcode { + Opname: "OpTypeMatrix", + Class: "Type-Declaration", + Opcode: 24, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column Count'", + Quantifier: "", + }, + }, + } + OpTypeImage = &Opcode { + Opname: "OpTypeImage", + Class: "Type-Declaration", + Opcode: 25, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDim, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Depth'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Arrayed'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'MS'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Sampled'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageFormat, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "", + Quantifier: "?", + }, + }, + } + OpTypeSampler = &Opcode { + Opname: "OpTypeSampler", + Class: "Type-Declaration", + Opcode: 26, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeSampledImage = &Opcode { + Opname: "OpTypeSampledImage", + Class: "Type-Declaration", + Opcode: 27, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + }, + } + OpTypeArray = &Opcode { + Opname: "OpTypeArray", + Class: "Type-Declaration", + Opcode: 28, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Length'", + Quantifier: "", + }, + }, + } + OpTypeRuntimeArray = &Opcode { + Opname: "OpTypeRuntimeArray", + Class: "Type-Declaration", + Opcode: 29, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element Type'", + Quantifier: "", + }, + }, + } + OpTypeStruct = &Opcode { + Opname: "OpTypeStruct", + Class: "Type-Declaration", + Opcode: 30, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Member 0 type', + 'member 1 type', + ...", + Quantifier: "*", + }, + }, + } + OpTypeOpaque = &Opcode { + Opname: "OpTypeOpaque", + Class: "Type-Declaration", + Opcode: 31, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "The name of the opaque type.", + Quantifier: "", + }, + }, + } + OpTypePointer = &Opcode { + Opname: "OpTypePointer", + Class: "Type-Declaration", + Opcode: 32, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpTypeFunction = &Opcode { + Opname: "OpTypeFunction", + Class: "Type-Declaration", + Opcode: 33, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Return Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameter 0 Type', + 'Parameter 1 Type', + ...", + Quantifier: "*", + }, + }, + } + OpTypeEvent = &Opcode { + Opname: "OpTypeEvent", + Class: "Type-Declaration", + Opcode: 34, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeDeviceEvent = &Opcode { + Opname: "OpTypeDeviceEvent", + Class: "Type-Declaration", + Opcode: 35, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeReserveId = &Opcode { + Opname: "OpTypeReserveId", + Class: "Type-Declaration", + Opcode: 36, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeQueue = &Opcode { + Opname: "OpTypeQueue", + Class: "Type-Declaration", + Opcode: 37, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypePipe = &Opcode { + Opname: "OpTypePipe", + Class: "Type-Declaration", + Opcode: 38, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "'Qualifier'", + Quantifier: "", + }, + }, + } + OpTypeForwardPointer = &Opcode { + Opname: "OpTypeForwardPointer", + Class: "Type-Declaration", + Opcode: 39, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantTrue = &Opcode { + Opname: "OpConstantTrue", + Class: "Constant-Creation", + Opcode: 41, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantFalse = &Opcode { + Opname: "OpConstantFalse", + Class: "Constant-Creation", + Opcode: 42, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstant = &Opcode { + Opname: "OpConstant", + Class: "Constant-Creation", + Opcode: 43, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralContextDependentNumber, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpConstantComposite = &Opcode { + Opname: "OpConstantComposite", + Class: "Constant-Creation", + Opcode: 44, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpConstantSampler = &Opcode { + Opname: "OpConstantSampler", + Class: "Constant-Creation", + Opcode: 45, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindSamplerAddressingMode, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSamplerFilterMode, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantNull = &Opcode { + Opname: "OpConstantNull", + Class: "Constant-Creation", + Opcode: 46, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstantTrue = &Opcode { + Opname: "OpSpecConstantTrue", + Class: "Constant-Creation", + Opcode: 48, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstantFalse = &Opcode { + Opname: "OpSpecConstantFalse", + Class: "Constant-Creation", + Opcode: 49, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstant = &Opcode { + Opname: "OpSpecConstant", + Class: "Constant-Creation", + Opcode: 50, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralContextDependentNumber, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSpecConstantComposite = &Opcode { + Opname: "OpSpecConstantComposite", + Class: "Constant-Creation", + Opcode: 51, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpSpecConstantOp = &Opcode { + Opname: "OpSpecConstantOp", + Class: "Constant-Creation", + Opcode: 52, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralSpecConstantOpInteger, + Name: "'Opcode'", + Quantifier: "", + }, + }, + } + OpFunction = &Opcode { + Opname: "OpFunction", + Class: "Function", + Opcode: 54, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindFunctionControl, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function Type'", + Quantifier: "", + }, + }, + } + OpFunctionParameter = &Opcode { + Opname: "OpFunctionParameter", + Class: "Function", + Opcode: 55, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpFunctionEnd = &Opcode { + Opname: "OpFunctionEnd", + Class: "Function", + Opcode: 56, + Operands: []Operand { + }, + } + OpFunctionCall = &Opcode { + Opname: "OpFunctionCall", + Class: "Function", + Opcode: 57, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Argument 0', + 'Argument 1', + ...", + Quantifier: "*", + }, + }, + } + OpVariable = &Opcode { + Opname: "OpVariable", + Class: "Memory", + Opcode: 59, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Initializer'", + Quantifier: "?", + }, + }, + } + OpImageTexelPointer = &Opcode { + Opname: "OpImageTexelPointer", + Class: "Memory", + Opcode: 60, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "", + }, + }, + } + OpLoad = &Opcode { + Opname: "OpLoad", + Class: "Memory", + Opcode: 61, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpStore = &Opcode { + Opname: "OpStore", + Class: "Memory", + Opcode: 62, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCopyMemory = &Opcode { + Opname: "OpCopyMemory", + Class: "Memory", + Opcode: 63, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCopyMemorySized = &Opcode { + Opname: "OpCopyMemorySized", + Class: "Memory", + Opcode: 64, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpAccessChain = &Opcode { + Opname: "OpAccessChain", + Class: "Memory", + Opcode: 65, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpInBoundsAccessChain = &Opcode { + Opname: "OpInBoundsAccessChain", + Class: "Memory", + Opcode: 66, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpPtrAccessChain = &Opcode { + Opname: "OpPtrAccessChain", + Class: "Memory", + Opcode: 67, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpArrayLength = &Opcode { + Opname: "OpArrayLength", + Class: "Memory", + Opcode: 68, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Array member'", + Quantifier: "", + }, + }, + } + OpGenericPtrMemSemantics = &Opcode { + Opname: "OpGenericPtrMemSemantics", + Class: "Memory", + Opcode: 69, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpInBoundsPtrAccessChain = &Opcode { + Opname: "OpInBoundsPtrAccessChain", + Class: "Memory", + Opcode: 70, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpDecorate = &Opcode { + Opname: "OpDecorate", + Class: "Annotation", + Opcode: 71, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpMemberDecorate = &Opcode { + Opname: "OpMemberDecorate", + Class: "Annotation", + Opcode: 72, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Structure Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpDecorationGroup = &Opcode { + Opname: "OpDecorationGroup", + Class: "Annotation", + Opcode: 73, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpGroupDecorate = &Opcode { + Opname: "OpGroupDecorate", + Class: "Annotation", + Opcode: 74, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Decoration Group'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Targets'", + Quantifier: "*", + }, + }, + } + OpGroupMemberDecorate = &Opcode { + Opname: "OpGroupMemberDecorate", + Class: "Annotation", + Opcode: 75, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Decoration Group'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefLiteralInteger, + Name: "'Targets'", + Quantifier: "*", + }, + }, + } + OpVectorExtractDynamic = &Opcode { + Opname: "OpVectorExtractDynamic", + Class: "Composite", + Opcode: 77, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpVectorInsertDynamic = &Opcode { + Opname: "OpVectorInsertDynamic", + Class: "Composite", + Opcode: 78, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpVectorShuffle = &Opcode { + Opname: "OpVectorShuffle", + Class: "Composite", + Opcode: 79, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Components'", + Quantifier: "*", + }, + }, + } + OpCompositeConstruct = &Opcode { + Opname: "OpCompositeConstruct", + Class: "Composite", + Opcode: 80, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpCompositeExtract = &Opcode { + Opname: "OpCompositeExtract", + Class: "Composite", + Opcode: 81, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Composite'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpCompositeInsert = &Opcode { + Opname: "OpCompositeInsert", + Class: "Composite", + Opcode: 82, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Composite'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpCopyObject = &Opcode { + Opname: "OpCopyObject", + Class: "Composite", + Opcode: 83, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpTranspose = &Opcode { + Opname: "OpTranspose", + Class: "Composite", + Opcode: 84, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpSampledImage = &Opcode { + Opname: "OpSampledImage", + Class: "Image", + Opcode: 86, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampler'", + Quantifier: "", + }, + }, + } + OpImageSampleImplicitLod = &Opcode { + Opname: "OpImageSampleImplicitLod", + Class: "Image", + Opcode: 87, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleExplicitLod = &Opcode { + Opname: "OpImageSampleExplicitLod", + Class: "Image", + Opcode: 88, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleDrefImplicitLod = &Opcode { + Opname: "OpImageSampleDrefImplicitLod", + Class: "Image", + Opcode: 89, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleDrefExplicitLod = &Opcode { + Opname: "OpImageSampleDrefExplicitLod", + Class: "Image", + Opcode: 90, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleProjImplicitLod = &Opcode { + Opname: "OpImageSampleProjImplicitLod", + Class: "Image", + Opcode: 91, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleProjExplicitLod = &Opcode { + Opname: "OpImageSampleProjExplicitLod", + Class: "Image", + Opcode: 92, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleProjDrefImplicitLod = &Opcode { + Opname: "OpImageSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 93, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleProjDrefExplicitLod = &Opcode { + Opname: "OpImageSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 94, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageFetch = &Opcode { + Opname: "OpImageFetch", + Class: "Image", + Opcode: 95, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageGather = &Opcode { + Opname: "OpImageGather", + Class: "Image", + Opcode: 96, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageDrefGather = &Opcode { + Opname: "OpImageDrefGather", + Class: "Image", + Opcode: 97, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageRead = &Opcode { + Opname: "OpImageRead", + Class: "Image", + Opcode: 98, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageWrite = &Opcode { + Opname: "OpImageWrite", + Class: "Image", + Opcode: 99, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImage = &Opcode { + Opname: "OpImage", + Class: "Image", + Opcode: 100, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + }, + } + OpImageQueryFormat = &Opcode { + Opname: "OpImageQueryFormat", + Class: "Image", + Opcode: 101, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQueryOrder = &Opcode { + Opname: "OpImageQueryOrder", + Class: "Image", + Opcode: 102, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQuerySizeLod = &Opcode { + Opname: "OpImageQuerySizeLod", + Class: "Image", + Opcode: 103, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Level of Detail'", + Quantifier: "", + }, + }, + } + OpImageQuerySize = &Opcode { + Opname: "OpImageQuerySize", + Class: "Image", + Opcode: 104, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQueryLod = &Opcode { + Opname: "OpImageQueryLod", + Class: "Image", + Opcode: 105, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpImageQueryLevels = &Opcode { + Opname: "OpImageQueryLevels", + Class: "Image", + Opcode: 106, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQuerySamples = &Opcode { + Opname: "OpImageQuerySamples", + Class: "Image", + Opcode: 107, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpConvertFToU = &Opcode { + Opname: "OpConvertFToU", + Class: "Conversion", + Opcode: 109, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertFToS = &Opcode { + Opname: "OpConvertFToS", + Class: "Conversion", + Opcode: 110, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertSToF = &Opcode { + Opname: "OpConvertSToF", + Class: "Conversion", + Opcode: 111, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpConvertUToF = &Opcode { + Opname: "OpConvertUToF", + Class: "Conversion", + Opcode: 112, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpUConvert = &Opcode { + Opname: "OpUConvert", + Class: "Conversion", + Opcode: 113, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpSConvert = &Opcode { + Opname: "OpSConvert", + Class: "Conversion", + Opcode: 114, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpFConvert = &Opcode { + Opname: "OpFConvert", + Class: "Conversion", + Opcode: 115, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpQuantizeToF16 = &Opcode { + Opname: "OpQuantizeToF16", + Class: "Conversion", + Opcode: 116, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpConvertPtrToU = &Opcode { + Opname: "OpConvertPtrToU", + Class: "Conversion", + Opcode: 117, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpSatConvertSToU = &Opcode { + Opname: "OpSatConvertSToU", + Class: "Conversion", + Opcode: 118, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpSatConvertUToS = &Opcode { + Opname: "OpSatConvertUToS", + Class: "Conversion", + Opcode: 119, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpConvertUToPtr = &Opcode { + Opname: "OpConvertUToPtr", + Class: "Conversion", + Opcode: 120, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Integer Value'", + Quantifier: "", + }, + }, + } + OpPtrCastToGeneric = &Opcode { + Opname: "OpPtrCastToGeneric", + Class: "Conversion", + Opcode: 121, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpGenericCastToPtr = &Opcode { + Opname: "OpGenericCastToPtr", + Class: "Conversion", + Opcode: 122, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpGenericCastToPtrExplicit = &Opcode { + Opname: "OpGenericCastToPtrExplicit", + Class: "Conversion", + Opcode: 123, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "'Storage'", + Quantifier: "", + }, + }, + } + OpBitcast = &Opcode { + Opname: "OpBitcast", + Class: "Conversion", + Opcode: 124, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSNegate = &Opcode { + Opname: "OpSNegate", + Class: "Arithmetic", + Opcode: 126, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpFNegate = &Opcode { + Opname: "OpFNegate", + Class: "Arithmetic", + Opcode: 127, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpIAdd = &Opcode { + Opname: "OpIAdd", + Class: "Arithmetic", + Opcode: 128, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFAdd = &Opcode { + Opname: "OpFAdd", + Class: "Arithmetic", + Opcode: 129, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISub = &Opcode { + Opname: "OpISub", + Class: "Arithmetic", + Opcode: 130, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFSub = &Opcode { + Opname: "OpFSub", + Class: "Arithmetic", + Opcode: 131, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIMul = &Opcode { + Opname: "OpIMul", + Class: "Arithmetic", + Opcode: 132, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFMul = &Opcode { + Opname: "OpFMul", + Class: "Arithmetic", + Opcode: 133, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUDiv = &Opcode { + Opname: "OpUDiv", + Class: "Arithmetic", + Opcode: 134, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSDiv = &Opcode { + Opname: "OpSDiv", + Class: "Arithmetic", + Opcode: 135, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFDiv = &Opcode { + Opname: "OpFDiv", + Class: "Arithmetic", + Opcode: 136, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMod = &Opcode { + Opname: "OpUMod", + Class: "Arithmetic", + Opcode: 137, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSRem = &Opcode { + Opname: "OpSRem", + Class: "Arithmetic", + Opcode: 138, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSMod = &Opcode { + Opname: "OpSMod", + Class: "Arithmetic", + Opcode: 139, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFRem = &Opcode { + Opname: "OpFRem", + Class: "Arithmetic", + Opcode: 140, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFMod = &Opcode { + Opname: "OpFMod", + Class: "Arithmetic", + Opcode: 141, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpVectorTimesScalar = &Opcode { + Opname: "OpVectorTimesScalar", + Class: "Arithmetic", + Opcode: 142, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scalar'", + Quantifier: "", + }, + }, + } + OpMatrixTimesScalar = &Opcode { + Opname: "OpMatrixTimesScalar", + Class: "Arithmetic", + Opcode: 143, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scalar'", + Quantifier: "", + }, + }, + } + OpVectorTimesMatrix = &Opcode { + Opname: "OpVectorTimesMatrix", + Class: "Arithmetic", + Opcode: 144, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpMatrixTimesVector = &Opcode { + Opname: "OpMatrixTimesVector", + Class: "Arithmetic", + Opcode: 145, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpMatrixTimesMatrix = &Opcode { + Opname: "OpMatrixTimesMatrix", + Class: "Arithmetic", + Opcode: 146, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LeftMatrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RightMatrix'", + Quantifier: "", + }, + }, + } + OpOuterProduct = &Opcode { + Opname: "OpOuterProduct", + Class: "Arithmetic", + Opcode: 147, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + }, + } + OpDot = &Opcode { + Opname: "OpDot", + Class: "Arithmetic", + Opcode: 148, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + }, + } + OpIAddCarry = &Opcode { + Opname: "OpIAddCarry", + Class: "Arithmetic", + Opcode: 149, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISubBorrow = &Opcode { + Opname: "OpISubBorrow", + Class: "Arithmetic", + Opcode: 150, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMulExtended = &Opcode { + Opname: "OpUMulExtended", + Class: "Arithmetic", + Opcode: 151, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSMulExtended = &Opcode { + Opname: "OpSMulExtended", + Class: "Arithmetic", + Opcode: 152, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpAny = &Opcode { + Opname: "OpAny", + Class: "Relational_and_Logical", + Opcode: 154, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpAll = &Opcode { + Opname: "OpAll", + Class: "Relational_and_Logical", + Opcode: 155, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpIsNan = &Opcode { + Opname: "OpIsNan", + Class: "Relational_and_Logical", + Opcode: 156, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsInf = &Opcode { + Opname: "OpIsInf", + Class: "Relational_and_Logical", + Opcode: 157, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsFinite = &Opcode { + Opname: "OpIsFinite", + Class: "Relational_and_Logical", + Opcode: 158, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsNormal = &Opcode { + Opname: "OpIsNormal", + Class: "Relational_and_Logical", + Opcode: 159, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpSignBitSet = &Opcode { + Opname: "OpSignBitSet", + Class: "Relational_and_Logical", + Opcode: 160, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpLessOrGreater = &Opcode { + Opname: "OpLessOrGreater", + Class: "Relational_and_Logical", + Opcode: 161, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpOrdered = &Opcode { + Opname: "OpOrdered", + Class: "Relational_and_Logical", + Opcode: 162, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpUnordered = &Opcode { + Opname: "OpUnordered", + Class: "Relational_and_Logical", + Opcode: 163, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpLogicalEqual = &Opcode { + Opname: "OpLogicalEqual", + Class: "Relational_and_Logical", + Opcode: 164, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalNotEqual = &Opcode { + Opname: "OpLogicalNotEqual", + Class: "Relational_and_Logical", + Opcode: 165, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalOr = &Opcode { + Opname: "OpLogicalOr", + Class: "Relational_and_Logical", + Opcode: 166, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalAnd = &Opcode { + Opname: "OpLogicalAnd", + Class: "Relational_and_Logical", + Opcode: 167, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalNot = &Opcode { + Opname: "OpLogicalNot", + Class: "Relational_and_Logical", + Opcode: 168, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSelect = &Opcode { + Opname: "OpSelect", + Class: "Relational_and_Logical", + Opcode: 169, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object 2'", + Quantifier: "", + }, + }, + } + OpIEqual = &Opcode { + Opname: "OpIEqual", + Class: "Relational_and_Logical", + Opcode: 170, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpINotEqual = &Opcode { + Opname: "OpINotEqual", + Class: "Relational_and_Logical", + Opcode: 171, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUGreaterThan = &Opcode { + Opname: "OpUGreaterThan", + Class: "Relational_and_Logical", + Opcode: 172, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSGreaterThan = &Opcode { + Opname: "OpSGreaterThan", + Class: "Relational_and_Logical", + Opcode: 173, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUGreaterThanEqual = &Opcode { + Opname: "OpUGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 174, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSGreaterThanEqual = &Opcode { + Opname: "OpSGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 175, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpULessThan = &Opcode { + Opname: "OpULessThan", + Class: "Relational_and_Logical", + Opcode: 176, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSLessThan = &Opcode { + Opname: "OpSLessThan", + Class: "Relational_and_Logical", + Opcode: 177, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpULessThanEqual = &Opcode { + Opname: "OpULessThanEqual", + Class: "Relational_and_Logical", + Opcode: 178, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSLessThanEqual = &Opcode { + Opname: "OpSLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 179, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdEqual = &Opcode { + Opname: "OpFOrdEqual", + Class: "Relational_and_Logical", + Opcode: 180, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordEqual = &Opcode { + Opname: "OpFUnordEqual", + Class: "Relational_and_Logical", + Opcode: 181, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdNotEqual = &Opcode { + Opname: "OpFOrdNotEqual", + Class: "Relational_and_Logical", + Opcode: 182, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordNotEqual = &Opcode { + Opname: "OpFUnordNotEqual", + Class: "Relational_and_Logical", + Opcode: 183, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdLessThan = &Opcode { + Opname: "OpFOrdLessThan", + Class: "Relational_and_Logical", + Opcode: 184, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordLessThan = &Opcode { + Opname: "OpFUnordLessThan", + Class: "Relational_and_Logical", + Opcode: 185, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdGreaterThan = &Opcode { + Opname: "OpFOrdGreaterThan", + Class: "Relational_and_Logical", + Opcode: 186, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordGreaterThan = &Opcode { + Opname: "OpFUnordGreaterThan", + Class: "Relational_and_Logical", + Opcode: 187, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdLessThanEqual = &Opcode { + Opname: "OpFOrdLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 188, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordLessThanEqual = &Opcode { + Opname: "OpFUnordLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 189, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdGreaterThanEqual = &Opcode { + Opname: "OpFOrdGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 190, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordGreaterThanEqual = &Opcode { + Opname: "OpFUnordGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 191, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpShiftRightLogical = &Opcode { + Opname: "OpShiftRightLogical", + Class: "Bit", + Opcode: 194, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpShiftRightArithmetic = &Opcode { + Opname: "OpShiftRightArithmetic", + Class: "Bit", + Opcode: 195, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpShiftLeftLogical = &Opcode { + Opname: "OpShiftLeftLogical", + Class: "Bit", + Opcode: 196, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpBitwiseOr = &Opcode { + Opname: "OpBitwiseOr", + Class: "Bit", + Opcode: 197, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpBitwiseXor = &Opcode { + Opname: "OpBitwiseXor", + Class: "Bit", + Opcode: 198, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpBitwiseAnd = &Opcode { + Opname: "OpBitwiseAnd", + Class: "Bit", + Opcode: 199, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpNot = &Opcode { + Opname: "OpNot", + Class: "Bit", + Opcode: 200, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpBitFieldInsert = &Opcode { + Opname: "OpBitFieldInsert", + Class: "Bit", + Opcode: 201, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Insert'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitFieldSExtract = &Opcode { + Opname: "OpBitFieldSExtract", + Class: "Bit", + Opcode: 202, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitFieldUExtract = &Opcode { + Opname: "OpBitFieldUExtract", + Class: "Bit", + Opcode: 203, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitReverse = &Opcode { + Opname: "OpBitReverse", + Class: "Bit", + Opcode: 204, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + }, + } + OpBitCount = &Opcode { + Opname: "OpBitCount", + Class: "Bit", + Opcode: 205, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + }, + } + OpDPdx = &Opcode { + Opname: "OpDPdx", + Class: "Derivative", + Opcode: 207, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdy = &Opcode { + Opname: "OpDPdy", + Class: "Derivative", + Opcode: 208, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidth = &Opcode { + Opname: "OpFwidth", + Class: "Derivative", + Opcode: 209, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdxFine = &Opcode { + Opname: "OpDPdxFine", + Class: "Derivative", + Opcode: 210, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdyFine = &Opcode { + Opname: "OpDPdyFine", + Class: "Derivative", + Opcode: 211, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidthFine = &Opcode { + Opname: "OpFwidthFine", + Class: "Derivative", + Opcode: 212, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdxCoarse = &Opcode { + Opname: "OpDPdxCoarse", + Class: "Derivative", + Opcode: 213, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdyCoarse = &Opcode { + Opname: "OpDPdyCoarse", + Class: "Derivative", + Opcode: 214, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidthCoarse = &Opcode { + Opname: "OpFwidthCoarse", + Class: "Derivative", + Opcode: 215, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpEmitVertex = &Opcode { + Opname: "OpEmitVertex", + Class: "Primitive", + Opcode: 218, + Operands: []Operand { + }, + } + OpEndPrimitive = &Opcode { + Opname: "OpEndPrimitive", + Class: "Primitive", + Opcode: 219, + Operands: []Operand { + }, + } + OpEmitStreamVertex = &Opcode { + Opname: "OpEmitStreamVertex", + Class: "Primitive", + Opcode: 220, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Stream'", + Quantifier: "", + }, + }, + } + OpEndStreamPrimitive = &Opcode { + Opname: "OpEndStreamPrimitive", + Class: "Primitive", + Opcode: 221, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Stream'", + Quantifier: "", + }, + }, + } + OpControlBarrier = &Opcode { + Opname: "OpControlBarrier", + Class: "Barrier", + Opcode: 224, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpMemoryBarrier = &Opcode { + Opname: "OpMemoryBarrier", + Class: "Barrier", + Opcode: 225, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicLoad = &Opcode { + Opname: "OpAtomicLoad", + Class: "Atomic", + Opcode: 227, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicStore = &Opcode { + Opname: "OpAtomicStore", + Class: "Atomic", + Opcode: 228, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicExchange = &Opcode { + Opname: "OpAtomicExchange", + Class: "Atomic", + Opcode: 229, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicCompareExchange = &Opcode { + Opname: "OpAtomicCompareExchange", + Class: "Atomic", + Opcode: 230, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Equal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Unequal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Comparator'", + Quantifier: "", + }, + }, + } + OpAtomicCompareExchangeWeak = &Opcode { + Opname: "OpAtomicCompareExchangeWeak", + Class: "Atomic", + Opcode: 231, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Equal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Unequal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Comparator'", + Quantifier: "", + }, + }, + } + OpAtomicIIncrement = &Opcode { + Opname: "OpAtomicIIncrement", + Class: "Atomic", + Opcode: 232, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicIDecrement = &Opcode { + Opname: "OpAtomicIDecrement", + Class: "Atomic", + Opcode: 233, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicIAdd = &Opcode { + Opname: "OpAtomicIAdd", + Class: "Atomic", + Opcode: 234, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicISub = &Opcode { + Opname: "OpAtomicISub", + Class: "Atomic", + Opcode: 235, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicSMin = &Opcode { + Opname: "OpAtomicSMin", + Class: "Atomic", + Opcode: 236, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicUMin = &Opcode { + Opname: "OpAtomicUMin", + Class: "Atomic", + Opcode: 237, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicSMax = &Opcode { + Opname: "OpAtomicSMax", + Class: "Atomic", + Opcode: 238, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicUMax = &Opcode { + Opname: "OpAtomicUMax", + Class: "Atomic", + Opcode: 239, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicAnd = &Opcode { + Opname: "OpAtomicAnd", + Class: "Atomic", + Opcode: 240, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicOr = &Opcode { + Opname: "OpAtomicOr", + Class: "Atomic", + Opcode: 241, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicXor = &Opcode { + Opname: "OpAtomicXor", + Class: "Atomic", + Opcode: 242, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpPhi = &Opcode { + Opname: "OpPhi", + Class: "Control-Flow", + Opcode: 245, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefIdRef, + Name: "'Variable, Parent, ...'", + Quantifier: "*", + }, + }, + } + OpLoopMerge = &Opcode { + Opname: "OpLoopMerge", + Class: "Control-Flow", + Opcode: 246, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Merge Block'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Continue Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLoopControl, + Name: "", + Quantifier: "", + }, + }, + } + OpSelectionMerge = &Opcode { + Opname: "OpSelectionMerge", + Class: "Control-Flow", + Opcode: 247, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Merge Block'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSelectionControl, + Name: "", + Quantifier: "", + }, + }, + } + OpLabel = &Opcode { + Opname: "OpLabel", + Class: "Control-Flow", + Opcode: 248, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpBranch = &Opcode { + Opname: "OpBranch", + Class: "Control-Flow", + Opcode: 249, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target Label'", + Quantifier: "", + }, + }, + } + OpBranchConditional = &Opcode { + Opname: "OpBranchConditional", + Class: "Control-Flow", + Opcode: 250, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'True Label'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'False Label'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Branch weights'", + Quantifier: "*", + }, + }, + } + OpSwitch = &Opcode { + Opname: "OpSwitch", + Class: "Control-Flow", + Opcode: 251, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Selector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Default'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairLiteralIntegerIdRef, + Name: "'Target'", + Quantifier: "*", + }, + }, + } + OpKill = &Opcode { + Opname: "OpKill", + Class: "Control-Flow", + Opcode: 252, + Operands: []Operand { + }, + } + OpReturn = &Opcode { + Opname: "OpReturn", + Class: "Control-Flow", + Opcode: 253, + Operands: []Operand { + }, + } + OpReturnValue = &Opcode { + Opname: "OpReturnValue", + Class: "Control-Flow", + Opcode: 254, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpUnreachable = &Opcode { + Opname: "OpUnreachable", + Class: "Control-Flow", + Opcode: 255, + Operands: []Operand { + }, + } + OpLifetimeStart = &Opcode { + Opname: "OpLifetimeStart", + Class: "Control-Flow", + Opcode: 256, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Size'", + Quantifier: "", + }, + }, + } + OpLifetimeStop = &Opcode { + Opname: "OpLifetimeStop", + Class: "Control-Flow", + Opcode: 257, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Size'", + Quantifier: "", + }, + }, + } + OpGroupAsyncCopy = &Opcode { + Opname: "OpGroupAsyncCopy", + Class: "Group", + Opcode: 259, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Destination'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Elements'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpGroupWaitEvents = &Opcode { + Opname: "OpGroupWaitEvents", + Class: "Group", + Opcode: 260, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Events List'", + Quantifier: "", + }, + }, + } + OpGroupAll = &Opcode { + Opname: "OpGroupAll", + Class: "Group", + Opcode: 261, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupAny = &Opcode { + Opname: "OpGroupAny", + Class: "Group", + Opcode: 262, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupBroadcast = &Opcode { + Opname: "OpGroupBroadcast", + Class: "Group", + Opcode: 263, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LocalId'", + Quantifier: "", + }, + }, + } + OpGroupIAdd = &Opcode { + Opname: "OpGroupIAdd", + Class: "Group", + Opcode: 264, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFAdd = &Opcode { + Opname: "OpGroupFAdd", + Class: "Group", + Opcode: 265, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMin = &Opcode { + Opname: "OpGroupFMin", + Class: "Group", + Opcode: 266, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMin = &Opcode { + Opname: "OpGroupUMin", + Class: "Group", + Opcode: 267, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMin = &Opcode { + Opname: "OpGroupSMin", + Class: "Group", + Opcode: 268, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMax = &Opcode { + Opname: "OpGroupFMax", + Class: "Group", + Opcode: 269, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMax = &Opcode { + Opname: "OpGroupUMax", + Class: "Group", + Opcode: 270, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMax = &Opcode { + Opname: "OpGroupSMax", + Class: "Group", + Opcode: 271, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpReadPipe = &Opcode { + Opname: "OpReadPipe", + Class: "Pipe", + Opcode: 274, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpWritePipe = &Opcode { + Opname: "OpWritePipe", + Class: "Pipe", + Opcode: 275, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReservedReadPipe = &Opcode { + Opname: "OpReservedReadPipe", + Class: "Pipe", + Opcode: 276, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReservedWritePipe = &Opcode { + Opname: "OpReservedWritePipe", + Class: "Pipe", + Opcode: 277, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReserveReadPipePackets = &Opcode { + Opname: "OpReserveReadPipePackets", + Class: "Pipe", + Opcode: 278, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReserveWritePipePackets = &Opcode { + Opname: "OpReserveWritePipePackets", + Class: "Pipe", + Opcode: 279, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpCommitReadPipe = &Opcode { + Opname: "OpCommitReadPipe", + Class: "Pipe", + Opcode: 280, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpCommitWritePipe = &Opcode { + Opname: "OpCommitWritePipe", + Class: "Pipe", + Opcode: 281, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpIsValidReserveId = &Opcode { + Opname: "OpIsValidReserveId", + Class: "Pipe", + Opcode: 282, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + }, + } + OpGetNumPipePackets = &Opcode { + Opname: "OpGetNumPipePackets", + Class: "Pipe", + Opcode: 283, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGetMaxPipePackets = &Opcode { + Opname: "OpGetMaxPipePackets", + Class: "Pipe", + Opcode: 284, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupReserveReadPipePackets = &Opcode { + Opname: "OpGroupReserveReadPipePackets", + Class: "Pipe", + Opcode: 285, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupReserveWritePipePackets = &Opcode { + Opname: "OpGroupReserveWritePipePackets", + Class: "Pipe", + Opcode: 286, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupCommitReadPipe = &Opcode { + Opname: "OpGroupCommitReadPipe", + Class: "Pipe", + Opcode: 287, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupCommitWritePipe = &Opcode { + Opname: "OpGroupCommitWritePipe", + Class: "Pipe", + Opcode: 288, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpEnqueueMarker = &Opcode { + Opname: "OpEnqueueMarker", + Class: "Device-Side_Enqueue", + Opcode: 291, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Queue'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Wait Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ret Event'", + Quantifier: "", + }, + }, + } + OpEnqueueKernel = &Opcode { + Opname: "OpEnqueueKernel", + Class: "Device-Side_Enqueue", + Opcode: 292, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Queue'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Wait Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ret Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Local Size'", + Quantifier: "*", + }, + }, + } + OpGetKernelNDrangeSubGroupCount = &Opcode { + Opname: "OpGetKernelNDrangeSubGroupCount", + Class: "Device-Side_Enqueue", + Opcode: 293, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelNDrangeMaxSubGroupSize = &Opcode { + Opname: "OpGetKernelNDrangeMaxSubGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 294, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelWorkGroupSize = &Opcode { + Opname: "OpGetKernelWorkGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 295, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelPreferredWorkGroupSizeMultiple = &Opcode { + Opname: "OpGetKernelPreferredWorkGroupSizeMultiple", + Class: "Device-Side_Enqueue", + Opcode: 296, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpRetainEvent = &Opcode { + Opname: "OpRetainEvent", + Class: "Device-Side_Enqueue", + Opcode: 297, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpReleaseEvent = &Opcode { + Opname: "OpReleaseEvent", + Class: "Device-Side_Enqueue", + Opcode: 298, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpCreateUserEvent = &Opcode { + Opname: "OpCreateUserEvent", + Class: "Device-Side_Enqueue", + Opcode: 299, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpIsValidEvent = &Opcode { + Opname: "OpIsValidEvent", + Class: "Device-Side_Enqueue", + Opcode: 300, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpSetUserEventStatus = &Opcode { + Opname: "OpSetUserEventStatus", + Class: "Device-Side_Enqueue", + Opcode: 301, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Status'", + Quantifier: "", + }, + }, + } + OpCaptureEventProfilingInfo = &Opcode { + Opname: "OpCaptureEventProfilingInfo", + Class: "Device-Side_Enqueue", + Opcode: 302, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Profiling Info'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGetDefaultQueue = &Opcode { + Opname: "OpGetDefaultQueue", + Class: "Device-Side_Enqueue", + Opcode: 303, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpBuildNDRange = &Opcode { + Opname: "OpBuildNDRange", + Class: "Device-Side_Enqueue", + Opcode: 304, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GlobalWorkSize'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LocalWorkSize'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GlobalWorkOffset'", + Quantifier: "", + }, + }, + } + OpImageSparseSampleImplicitLod = &Opcode { + Opname: "OpImageSparseSampleImplicitLod", + Class: "Image", + Opcode: 305, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleExplicitLod = &Opcode { + Opname: "OpImageSparseSampleExplicitLod", + Class: "Image", + Opcode: 306, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleDrefImplicitLod = &Opcode { + Opname: "OpImageSparseSampleDrefImplicitLod", + Class: "Image", + Opcode: 307, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleDrefExplicitLod = &Opcode { + Opname: "OpImageSparseSampleDrefExplicitLod", + Class: "Image", + Opcode: 308, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleProjImplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjImplicitLod", + Class: "Image", + Opcode: 309, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleProjExplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjExplicitLod", + Class: "Image", + Opcode: 310, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleProjDrefImplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 311, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleProjDrefExplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 312, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseFetch = &Opcode { + Opname: "OpImageSparseFetch", + Class: "Image", + Opcode: 313, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseGather = &Opcode { + Opname: "OpImageSparseGather", + Class: "Image", + Opcode: 314, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseDrefGather = &Opcode { + Opname: "OpImageSparseDrefGather", + Class: "Image", + Opcode: 315, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseTexelsResident = &Opcode { + Opname: "OpImageSparseTexelsResident", + Class: "Image", + Opcode: 316, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Resident Code'", + Quantifier: "", + }, + }, + } + OpNoLine = &Opcode { + Opname: "OpNoLine", + Class: "Debug", + Opcode: 317, + Operands: []Operand { + }, + } + OpAtomicFlagTestAndSet = &Opcode { + Opname: "OpAtomicFlagTestAndSet", + Class: "Atomic", + Opcode: 318, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicFlagClear = &Opcode { + Opname: "OpAtomicFlagClear", + Class: "Atomic", + Opcode: 319, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpImageSparseRead = &Opcode { + Opname: "OpImageSparseRead", + Class: "Image", + Opcode: 320, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpSizeOf = &Opcode { + Opname: "OpSizeOf", + Class: "Miscellaneous", + Opcode: 321, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpTypePipeStorage = &Opcode { + Opname: "OpTypePipeStorage", + Class: "Type-Declaration", + Opcode: 322, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantPipeStorage = &Opcode { + Opname: "OpConstantPipeStorage", + Class: "Pipe", + Opcode: 323, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Packet Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Capacity'", + Quantifier: "", + }, + }, + } + OpCreatePipeFromPipeStorage = &Opcode { + Opname: "OpCreatePipeFromPipeStorage", + Class: "Pipe", + Opcode: 324, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe Storage'", + Quantifier: "", + }, + }, + } + OpGetKernelLocalSizeForSubgroupCount = &Opcode { + Opname: "OpGetKernelLocalSizeForSubgroupCount", + Class: "Device-Side_Enqueue", + Opcode: 325, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Subgroup Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelMaxNumSubgroups = &Opcode { + Opname: "OpGetKernelMaxNumSubgroups", + Class: "Device-Side_Enqueue", + Opcode: 326, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpTypeNamedBarrier = &Opcode { + Opname: "OpTypeNamedBarrier", + Class: "Type-Declaration", + Opcode: 327, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpNamedBarrierInitialize = &Opcode { + Opname: "OpNamedBarrierInitialize", + Class: "Barrier", + Opcode: 328, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Subgroup Count'", + Quantifier: "", + }, + }, + } + OpMemoryNamedBarrier = &Opcode { + Opname: "OpMemoryNamedBarrier", + Class: "Barrier", + Opcode: 329, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Named Barrier'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpModuleProcessed = &Opcode { + Opname: "OpModuleProcessed", + Class: "Debug", + Opcode: 330, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Process'", + Quantifier: "", + }, + }, + } + OpExecutionModeId = &Opcode { + Opname: "OpExecutionModeId", + Class: "Mode-Setting", + Opcode: 331, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindExecutionMode, + Name: "'Mode'", + Quantifier: "", + }, + }, + } + OpDecorateId = &Opcode { + Opname: "OpDecorateId", + Class: "Annotation", + Opcode: 332, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpGroupNonUniformElect = &Opcode { + Opname: "OpGroupNonUniformElect", + Class: "Non-Uniform", + Opcode: 333, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAll = &Opcode { + Opname: "OpGroupNonUniformAll", + Class: "Non-Uniform", + Opcode: 334, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAny = &Opcode { + Opname: "OpGroupNonUniformAny", + Class: "Non-Uniform", + Opcode: 335, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAllEqual = &Opcode { + Opname: "OpGroupNonUniformAllEqual", + Class: "Non-Uniform", + Opcode: 336, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBroadcast = &Opcode { + Opname: "OpGroupNonUniformBroadcast", + Class: "Non-Uniform", + Opcode: 337, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Id'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBroadcastFirst = &Opcode { + Opname: "OpGroupNonUniformBroadcastFirst", + Class: "Non-Uniform", + Opcode: 338, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallot = &Opcode { + Opname: "OpGroupNonUniformBallot", + Class: "Non-Uniform", + Opcode: 339, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformInverseBallot = &Opcode { + Opname: "OpGroupNonUniformInverseBallot", + Class: "Non-Uniform", + Opcode: 340, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotBitExtract = &Opcode { + Opname: "OpGroupNonUniformBallotBitExtract", + Class: "Non-Uniform", + Opcode: 341, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotBitCount = &Opcode { + Opname: "OpGroupNonUniformBallotBitCount", + Class: "Non-Uniform", + Opcode: 342, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotFindLSB = &Opcode { + Opname: "OpGroupNonUniformBallotFindLSB", + Class: "Non-Uniform", + Opcode: 343, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotFindMSB = &Opcode { + Opname: "OpGroupNonUniformBallotFindMSB", + Class: "Non-Uniform", + Opcode: 344, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffle = &Opcode { + Opname: "OpGroupNonUniformShuffle", + Class: "Non-Uniform", + Opcode: 345, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Id'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleXor = &Opcode { + Opname: "OpGroupNonUniformShuffleXor", + Class: "Non-Uniform", + Opcode: 346, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleUp = &Opcode { + Opname: "OpGroupNonUniformShuffleUp", + Class: "Non-Uniform", + Opcode: 347, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleDown = &Opcode { + Opname: "OpGroupNonUniformShuffleDown", + Class: "Non-Uniform", + Opcode: 348, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformIAdd = &Opcode { + Opname: "OpGroupNonUniformIAdd", + Class: "Non-Uniform", + Opcode: 349, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFAdd = &Opcode { + Opname: "OpGroupNonUniformFAdd", + Class: "Non-Uniform", + Opcode: 350, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformIMul = &Opcode { + Opname: "OpGroupNonUniformIMul", + Class: "Non-Uniform", + Opcode: 351, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMul = &Opcode { + Opname: "OpGroupNonUniformFMul", + Class: "Non-Uniform", + Opcode: 352, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformSMin = &Opcode { + Opname: "OpGroupNonUniformSMin", + Class: "Non-Uniform", + Opcode: 353, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformUMin = &Opcode { + Opname: "OpGroupNonUniformUMin", + Class: "Non-Uniform", + Opcode: 354, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMin = &Opcode { + Opname: "OpGroupNonUniformFMin", + Class: "Non-Uniform", + Opcode: 355, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformSMax = &Opcode { + Opname: "OpGroupNonUniformSMax", + Class: "Non-Uniform", + Opcode: 356, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformUMax = &Opcode { + Opname: "OpGroupNonUniformUMax", + Class: "Non-Uniform", + Opcode: 357, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMax = &Opcode { + Opname: "OpGroupNonUniformFMax", + Class: "Non-Uniform", + Opcode: 358, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseAnd = &Opcode { + Opname: "OpGroupNonUniformBitwiseAnd", + Class: "Non-Uniform", + Opcode: 359, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseOr = &Opcode { + Opname: "OpGroupNonUniformBitwiseOr", + Class: "Non-Uniform", + Opcode: 360, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseXor = &Opcode { + Opname: "OpGroupNonUniformBitwiseXor", + Class: "Non-Uniform", + Opcode: 361, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalAnd = &Opcode { + Opname: "OpGroupNonUniformLogicalAnd", + Class: "Non-Uniform", + Opcode: 362, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalOr = &Opcode { + Opname: "OpGroupNonUniformLogicalOr", + Class: "Non-Uniform", + Opcode: 363, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalXor = &Opcode { + Opname: "OpGroupNonUniformLogicalXor", + Class: "Non-Uniform", + Opcode: 364, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformQuadBroadcast = &Opcode { + Opname: "OpGroupNonUniformQuadBroadcast", + Class: "Non-Uniform", + Opcode: 365, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadSwap = &Opcode { + Opname: "OpGroupNonUniformQuadSwap", + Class: "Non-Uniform", + Opcode: 366, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpCopyLogical = &Opcode { + Opname: "OpCopyLogical", + Class: "Composite", + Opcode: 400, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpPtrEqual = &Opcode { + Opname: "OpPtrEqual", + Class: "Memory", + Opcode: 401, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpPtrNotEqual = &Opcode { + Opname: "OpPtrNotEqual", + Class: "Memory", + Opcode: 402, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpPtrDiff = &Opcode { + Opname: "OpPtrDiff", + Class: "Memory", + Opcode: 403, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpColorAttachmentReadEXT = &Opcode { + Opname: "OpColorAttachmentReadEXT", + Class: "Image", + Opcode: 4160, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Attachment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpDepthAttachmentReadEXT = &Opcode { + Opname: "OpDepthAttachmentReadEXT", + Class: "Image", + Opcode: 4161, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpStencilAttachmentReadEXT = &Opcode { + Opname: "OpStencilAttachmentReadEXT", + Class: "Image", + Opcode: 4162, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "?", + }, + }, + } + OpTerminateInvocation = &Opcode { + Opname: "OpTerminateInvocation", + Class: "Control-Flow", + Opcode: 4416, + Operands: []Operand { + }, + } + OpTypeUntypedPointerKHR = &Opcode { + Opname: "OpTypeUntypedPointerKHR", + Class: "Type-Declaration", + Opcode: 4417, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + }, + } + OpUntypedVariableKHR = &Opcode { + Opname: "OpUntypedVariableKHR", + Class: "Memory", + Opcode: 4418, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data Type'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Initializer'", + Quantifier: "?", + }, + }, + } + OpUntypedAccessChainKHR = &Opcode { + Opname: "OpUntypedAccessChainKHR", + Class: "Memory", + Opcode: 4419, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedInBoundsAccessChainKHR = &Opcode { + Opname: "OpUntypedInBoundsAccessChainKHR", + Class: "Memory", + Opcode: 4420, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpSubgroupBallotKHR = &Opcode { + Opname: "OpSubgroupBallotKHR", + Class: "Group", + Opcode: 4421, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupFirstInvocationKHR = &Opcode { + Opname: "OpSubgroupFirstInvocationKHR", + Class: "Group", + Opcode: 4422, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpUntypedPtrAccessChainKHR = &Opcode { + Opname: "OpUntypedPtrAccessChainKHR", + Class: "Memory", + Opcode: 4423, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedInBoundsPtrAccessChainKHR = &Opcode { + Opname: "OpUntypedInBoundsPtrAccessChainKHR", + Class: "Memory", + Opcode: 4424, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpUntypedArrayLengthKHR = &Opcode { + Opname: "OpUntypedArrayLengthKHR", + Class: "Memory", + Opcode: 4425, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Array member'", + Quantifier: "", + }, + }, + } + OpUntypedPrefetchKHR = &Opcode { + Opname: "OpUntypedPrefetchKHR", + Class: "Memory", + Opcode: 4426, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Bytes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RW'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Locality'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cache Type'", + Quantifier: "?", + }, + }, + } + OpSubgroupAllKHR = &Opcode { + Opname: "OpSubgroupAllKHR", + Class: "Group", + Opcode: 4428, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupAnyKHR = &Opcode { + Opname: "OpSubgroupAnyKHR", + Class: "Group", + Opcode: 4429, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupAllEqualKHR = &Opcode { + Opname: "OpSubgroupAllEqualKHR", + Class: "Group", + Opcode: 4430, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformRotateKHR = &Opcode { + Opname: "OpGroupNonUniformRotateKHR", + Class: "Group", + Opcode: 4431, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpSubgroupReadInvocationKHR = &Opcode { + Opname: "OpSubgroupReadInvocationKHR", + Class: "Group", + Opcode: 4432, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpExtInstWithForwardRefsKHR = &Opcode { + Opname: "OpExtInstWithForwardRefsKHR", + Class: "Extension", + Opcode: 4433, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Set'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralExtInstInteger, + Name: "'Instruction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1', + 'Operand 2', + ...", + Quantifier: "*", + }, + }, + } + OpTraceRayKHR = &Opcode { + Opname: "OpTraceRayKHR", + Class: "Reserved", + Opcode: 4445, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpExecuteCallableKHR = &Opcode { + Opname: "OpExecuteCallableKHR", + Class: "Reserved", + Opcode: 4446, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable Data'", + Quantifier: "", + }, + }, + } + OpConvertUToAccelerationStructureKHR = &Opcode { + Opname: "OpConvertUToAccelerationStructureKHR", + Class: "Reserved", + Opcode: 4447, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + }, + } + OpIgnoreIntersectionKHR = &Opcode { + Opname: "OpIgnoreIntersectionKHR", + Class: "Reserved", + Opcode: 4448, + Operands: []Operand { + }, + } + OpTerminateRayKHR = &Opcode { + Opname: "OpTerminateRayKHR", + Class: "Reserved", + Opcode: 4449, + Operands: []Operand { + }, + } + OpSDot = &Opcode { + Opname: "OpSDot", + Class: "Arithmetic", + Opcode: 4450, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpUDot = &Opcode { + Opname: "OpUDot", + Class: "Arithmetic", + Opcode: 4451, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSUDot = &Opcode { + Opname: "OpSUDot", + Class: "Arithmetic", + Opcode: 4452, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSDotAccSat = &Opcode { + Opname: "OpSDotAccSat", + Class: "Arithmetic", + Opcode: 4453, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpUDotAccSat = &Opcode { + Opname: "OpUDotAccSat", + Class: "Arithmetic", + Opcode: 4454, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpSUDotAccSat = &Opcode { + Opname: "OpSUDotAccSat", + Class: "Arithmetic", + Opcode: 4455, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accumulator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPackedVectorFormat, + Name: "'Packed Vector Format'", + Quantifier: "?", + }, + }, + } + OpTypeCooperativeMatrixKHR = &Opcode { + Opname: "OpTypeCooperativeMatrixKHR", + Class: "Type-Declaration", + Opcode: 4456, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Rows'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Columns'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Use'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadKHR = &Opcode { + Opname: "OpCooperativeMatrixLoadKHR", + Class: "Memory", + Opcode: 4457, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'MemoryLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixStoreKHR = &Opcode { + Opname: "OpCooperativeMatrixStoreKHR", + Class: "Memory", + Opcode: 4458, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'MemoryLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixMulAddKHR = &Opcode { + Opname: "OpCooperativeMatrixMulAddKHR", + Class: "Arithmetic", + Opcode: 4459, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'C'", + Quantifier: "", + }, + Operand { + Kind: OperandKindCooperativeMatrixOperands, + Name: "'Cooperative Matrix Operands'", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixLengthKHR = &Opcode { + Opname: "OpCooperativeMatrixLengthKHR", + Class: "Miscellaneous", + Opcode: 4460, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpConstantCompositeReplicateEXT = &Opcode { + Opname: "OpConstantCompositeReplicateEXT", + Class: "Constant-Creation", + Opcode: 4461, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSpecConstantCompositeReplicateEXT = &Opcode { + Opname: "OpSpecConstantCompositeReplicateEXT", + Class: "Constant-Creation", + Opcode: 4462, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpCompositeConstructReplicateEXT = &Opcode { + Opname: "OpCompositeConstructReplicateEXT", + Class: "Composite", + Opcode: 4463, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpTypeRayQueryKHR = &Opcode { + Opname: "OpTypeRayQueryKHR", + Class: "Type-Declaration", + Opcode: 4472, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRayQueryInitializeKHR = &Opcode { + Opname: "OpRayQueryInitializeKHR", + Class: "Reserved", + Opcode: 4473, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CullMask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayOrigin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayDirection'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMax'", + Quantifier: "", + }, + }, + } + OpRayQueryTerminateKHR = &Opcode { + Opname: "OpRayQueryTerminateKHR", + Class: "Reserved", + Opcode: 4474, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGenerateIntersectionKHR = &Opcode { + Opname: "OpRayQueryGenerateIntersectionKHR", + Class: "Reserved", + Opcode: 4475, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitT'", + Quantifier: "", + }, + }, + } + OpRayQueryConfirmIntersectionKHR = &Opcode { + Opname: "OpRayQueryConfirmIntersectionKHR", + Class: "Reserved", + Opcode: 4476, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryProceedKHR = &Opcode { + Opname: "OpRayQueryProceedKHR", + Class: "Reserved", + Opcode: 4477, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTypeKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTypeKHR", + Class: "Reserved", + Opcode: 4479, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpImageSampleWeightedQCOM = &Opcode { + Opname: "OpImageSampleWeightedQCOM", + Class: "Image", + Opcode: 4480, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texture'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Weights'", + Quantifier: "", + }, + }, + } + OpImageBoxFilterQCOM = &Opcode { + Opname: "OpImageBoxFilterQCOM", + Class: "Image", + Opcode: 4481, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texture'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Box Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchSSDQCOM", + Class: "Image", + Opcode: 4482, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchSADQCOM = &Opcode { + Opname: "OpImageBlockMatchSADQCOM", + Class: "Image", + Opcode: 4483, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchWindowSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchWindowSSDQCOM", + Class: "Image", + Opcode: 4500, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchWindowSADQCOM = &Opcode { + Opname: "OpImageBlockMatchWindowSADQCOM", + Class: "Image", + Opcode: 4501, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchGatherSSDQCOM = &Opcode { + Opname: "OpImageBlockMatchGatherSSDQCOM", + Class: "Image", + Opcode: 4502, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpImageBlockMatchGatherSADQCOM = &Opcode { + Opname: "OpImageBlockMatchGatherSADQCOM", + Class: "Image", + Opcode: 4503, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Coordinates'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Size'", + Quantifier: "", + }, + }, + } + OpGroupIAddNonUniformAMD = &Opcode { + Opname: "OpGroupIAddNonUniformAMD", + Class: "Group", + Opcode: 5000, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFAddNonUniformAMD = &Opcode { + Opname: "OpGroupFAddNonUniformAMD", + Class: "Group", + Opcode: 5001, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMinNonUniformAMD = &Opcode { + Opname: "OpGroupFMinNonUniformAMD", + Class: "Group", + Opcode: 5002, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMinNonUniformAMD = &Opcode { + Opname: "OpGroupUMinNonUniformAMD", + Class: "Group", + Opcode: 5003, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMinNonUniformAMD = &Opcode { + Opname: "OpGroupSMinNonUniformAMD", + Class: "Group", + Opcode: 5004, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMaxNonUniformAMD = &Opcode { + Opname: "OpGroupFMaxNonUniformAMD", + Class: "Group", + Opcode: 5005, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMaxNonUniformAMD = &Opcode { + Opname: "OpGroupUMaxNonUniformAMD", + Class: "Group", + Opcode: 5006, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMaxNonUniformAMD = &Opcode { + Opname: "OpGroupSMaxNonUniformAMD", + Class: "Group", + Opcode: 5007, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpFragmentMaskFetchAMD = &Opcode { + Opname: "OpFragmentMaskFetchAMD", + Class: "Reserved", + Opcode: 5011, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpFragmentFetchAMD = &Opcode { + Opname: "OpFragmentFetchAMD", + Class: "Reserved", + Opcode: 5012, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fragment Index'", + Quantifier: "", + }, + }, + } + OpReadClockKHR = &Opcode { + Opname: "OpReadClockKHR", + Class: "Reserved", + Opcode: 5056, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Scope'", + Quantifier: "", + }, + }, + } + OpAllocateNodePayloadsAMDX = &Opcode { + Opname: "OpAllocateNodePayloadsAMDX", + Class: "Reserved", + Opcode: 5074, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Visibility'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Node Index'", + Quantifier: "", + }, + }, + } + OpEnqueueNodePayloadsAMDX = &Opcode { + Opname: "OpEnqueueNodePayloadsAMDX", + Class: "Reserved", + Opcode: 5075, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Array'", + Quantifier: "", + }, + }, + } + OpTypeNodePayloadArrayAMDX = &Opcode { + Opname: "OpTypeNodePayloadArrayAMDX", + Class: "Reserved", + Opcode: 5076, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Type'", + Quantifier: "", + }, + }, + } + OpFinishWritingNodePayloadAMDX = &Opcode { + Opname: "OpFinishWritingNodePayloadAMDX", + Class: "Reserved", + Opcode: 5078, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpNodePayloadArrayLengthAMDX = &Opcode { + Opname: "OpNodePayloadArrayLengthAMDX", + Class: "Reserved", + Opcode: 5090, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Array'", + Quantifier: "", + }, + }, + } + OpIsNodePayloadValidAMDX = &Opcode { + Opname: "OpIsNodePayloadValidAMDX", + Class: "Reserved", + Opcode: 5101, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Node Index'", + Quantifier: "", + }, + }, + } + OpConstantStringAMDX = &Opcode { + Opname: "OpConstantStringAMDX", + Class: "Reserved", + Opcode: 5103, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Literal String'", + Quantifier: "", + }, + }, + } + OpSpecConstantStringAMDX = &Opcode { + Opname: "OpSpecConstantStringAMDX", + Class: "Reserved", + Opcode: 5104, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Literal String'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadAllKHR = &Opcode { + Opname: "OpGroupNonUniformQuadAllKHR", + Class: "Non-Uniform", + Opcode: 5110, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadAnyKHR = &Opcode { + Opname: "OpGroupNonUniformQuadAnyKHR", + Class: "Non-Uniform", + Opcode: 5111, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitMotionNV = &Opcode { + Opname: "OpHitObjectRecordHitMotionNV", + Class: "Reserved", + Opcode: 5249, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitWithIndexMotionNV = &Opcode { + Opname: "OpHitObjectRecordHitWithIndexMotionNV", + Class: "Reserved", + Opcode: 5250, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordMissMotionNV = &Opcode { + Opname: "OpHitObjectRecordMissMotionNV", + Class: "Reserved", + Opcode: 5251, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current Time'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldToObjectNV = &Opcode { + Opname: "OpHitObjectGetWorldToObjectNV", + Class: "Reserved", + Opcode: 5252, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectToWorldNV = &Opcode { + Opname: "OpHitObjectGetObjectToWorldNV", + Class: "Reserved", + Opcode: 5253, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectRayDirectionNV = &Opcode { + Opname: "OpHitObjectGetObjectRayDirectionNV", + Class: "Reserved", + Opcode: 5254, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetObjectRayOriginNV = &Opcode { + Opname: "OpHitObjectGetObjectRayOriginNV", + Class: "Reserved", + Opcode: 5255, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectTraceRayMotionNV = &Opcode { + Opname: "OpHitObjectTraceRayMotionNV", + Class: "Reserved", + Opcode: 5256, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cullmask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectGetShaderRecordBufferHandleNV = &Opcode { + Opname: "OpHitObjectGetShaderRecordBufferHandleNV", + Class: "Reserved", + Opcode: 5257, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetShaderBindingTableRecordIndexNV = &Opcode { + Opname: "OpHitObjectGetShaderBindingTableRecordIndexNV", + Class: "Reserved", + Opcode: 5258, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordEmptyNV = &Opcode { + Opname: "OpHitObjectRecordEmptyNV", + Class: "Reserved", + Opcode: 5259, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectTraceRayNV = &Opcode { + Opname: "OpHitObjectTraceRayNV", + Class: "Reserved", + Opcode: 5260, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cullmask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitNV = &Opcode { + Opname: "OpHitObjectRecordHitNV", + Class: "Reserved", + Opcode: 5261, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordHitWithIndexNV = &Opcode { + Opname: "OpHitObjectRecordHitWithIndexNV", + Class: "Reserved", + Opcode: 5262, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Acceleration Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InstanceId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PrimitiveId'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GeometryIndex'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Kind'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Record Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitObject Attributes'", + Quantifier: "", + }, + }, + } + OpHitObjectRecordMissNV = &Opcode { + Opname: "OpHitObjectRecordMissNV", + Class: "Reserved", + Opcode: 5263, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TMax'", + Quantifier: "", + }, + }, + } + OpHitObjectExecuteShaderNV = &Opcode { + Opname: "OpHitObjectExecuteShaderNV", + Class: "Reserved", + Opcode: 5264, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpHitObjectGetCurrentTimeNV = &Opcode { + Opname: "OpHitObjectGetCurrentTimeNV", + Class: "Reserved", + Opcode: 5265, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetAttributesNV = &Opcode { + Opname: "OpHitObjectGetAttributesNV", + Class: "Reserved", + Opcode: 5266, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object Attribute'", + Quantifier: "", + }, + }, + } + OpHitObjectGetHitKindNV = &Opcode { + Opname: "OpHitObjectGetHitKindNV", + Class: "Reserved", + Opcode: 5267, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetPrimitiveIndexNV = &Opcode { + Opname: "OpHitObjectGetPrimitiveIndexNV", + Class: "Reserved", + Opcode: 5268, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetGeometryIndexNV = &Opcode { + Opname: "OpHitObjectGetGeometryIndexNV", + Class: "Reserved", + Opcode: 5269, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetInstanceIdNV = &Opcode { + Opname: "OpHitObjectGetInstanceIdNV", + Class: "Reserved", + Opcode: 5270, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetInstanceCustomIndexNV = &Opcode { + Opname: "OpHitObjectGetInstanceCustomIndexNV", + Class: "Reserved", + Opcode: 5271, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldRayDirectionNV = &Opcode { + Opname: "OpHitObjectGetWorldRayDirectionNV", + Class: "Reserved", + Opcode: 5272, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetWorldRayOriginNV = &Opcode { + Opname: "OpHitObjectGetWorldRayOriginNV", + Class: "Reserved", + Opcode: 5273, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetRayTMaxNV = &Opcode { + Opname: "OpHitObjectGetRayTMaxNV", + Class: "Reserved", + Opcode: 5274, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectGetRayTMinNV = &Opcode { + Opname: "OpHitObjectGetRayTMinNV", + Class: "Reserved", + Opcode: 5275, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsEmptyNV = &Opcode { + Opname: "OpHitObjectIsEmptyNV", + Class: "Reserved", + Opcode: 5276, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsHitNV = &Opcode { + Opname: "OpHitObjectIsHitNV", + Class: "Reserved", + Opcode: 5277, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpHitObjectIsMissNV = &Opcode { + Opname: "OpHitObjectIsMissNV", + Class: "Reserved", + Opcode: 5278, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + }, + } + OpReorderThreadWithHitObjectNV = &Opcode { + Opname: "OpReorderThreadWithHitObjectNV", + Class: "Reserved", + Opcode: 5279, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hit Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hint'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bits'", + Quantifier: "?", + }, + }, + } + OpReorderThreadWithHintNV = &Opcode { + Opname: "OpReorderThreadWithHintNV", + Class: "Reserved", + Opcode: 5280, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Hint'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bits'", + Quantifier: "", + }, + }, + } + OpTypeHitObjectNV = &Opcode { + Opname: "OpTypeHitObjectNV", + Class: "Type-Declaration", + Opcode: 5281, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleFootprintNV = &Opcode { + Opname: "OpImageSampleFootprintNV", + Class: "Image", + Opcode: 5283, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Granularity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coarse'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixConvertNV = &Opcode { + Opname: "OpCooperativeMatrixConvertNV", + Class: "Conversion", + Opcode: 5293, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpEmitMeshTasksEXT = &Opcode { + Opname: "OpEmitMeshTasksEXT", + Class: "Reserved", + Opcode: 5294, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count X'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count Y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Group Count Z'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "?", + }, + }, + } + OpSetMeshOutputsEXT = &Opcode { + Opname: "OpSetMeshOutputsEXT", + Class: "Reserved", + Opcode: 5295, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Vertex Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Count'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformPartitionNV = &Opcode { + Opname: "OpGroupNonUniformPartitionNV", + Class: "Non-Uniform", + Opcode: 5296, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpWritePackedPrimitiveIndices4x8NV = &Opcode { + Opname: "OpWritePackedPrimitiveIndices4x8NV", + Class: "Reserved", + Opcode: 5299, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Index Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Indices'", + Quantifier: "", + }, + }, + } + OpFetchMicroTriangleVertexPositionNV = &Opcode { + Opname: "OpFetchMicroTriangleVertexPositionNV", + Class: "Reserved", + Opcode: 5300, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Instance Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Geometry Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Barycentric'", + Quantifier: "", + }, + }, + } + OpFetchMicroTriangleVertexBarycentricNV = &Opcode { + Opname: "OpFetchMicroTriangleVertexBarycentricNV", + Class: "Reserved", + Opcode: 5301, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Instance Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Geometry Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Primitive Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Barycentric'", + Quantifier: "", + }, + }, + } + OpReportIntersectionKHR = &Opcode { + Opname: "OpReportIntersectionKHR", + Class: "Reserved", + Opcode: 5334, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitKind'", + Quantifier: "", + }, + }, + } + OpIgnoreIntersectionNV = &Opcode { + Opname: "OpIgnoreIntersectionNV", + Class: "Reserved", + Opcode: 5335, + Operands: []Operand { + }, + } + OpTerminateRayNV = &Opcode { + Opname: "OpTerminateRayNV", + Class: "Reserved", + Opcode: 5336, + Operands: []Operand { + }, + } + OpTraceNV = &Opcode { + Opname: "OpTraceNV", + Class: "Reserved", + Opcode: 5337, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PayloadId'", + Quantifier: "", + }, + }, + } + OpTraceMotionNV = &Opcode { + Opname: "OpTraceMotionNV", + Class: "Reserved", + Opcode: 5338, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PayloadId'", + Quantifier: "", + }, + }, + } + OpTraceRayMotionNV = &Opcode { + Opname: "OpTraceRayMotionNV", + Class: "Reserved", + Opcode: 5339, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Time'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTriangleVertexPositionsKHR", + Class: "Reserved", + Opcode: 5340, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpTypeAccelerationStructureKHR = &Opcode { + Opname: "OpTypeAccelerationStructureKHR", + Class: "Type-Declaration", + Opcode: 5341, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpExecuteCallableNV = &Opcode { + Opname: "OpExecuteCallableNV", + Class: "Reserved", + Opcode: 5344, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable DataId'", + Quantifier: "", + }, + }, + } + OpTypeCooperativeMatrixNV = &Opcode { + Opname: "OpTypeCooperativeMatrixNV", + Class: "Type-Declaration", + Opcode: 5358, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Rows'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Columns'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadNV = &Opcode { + Opname: "OpCooperativeMatrixLoadNV", + Class: "Reserved", + Opcode: 5359, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Major'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixStoreNV = &Opcode { + Opname: "OpCooperativeMatrixStoreNV", + Class: "Reserved", + Opcode: 5360, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Major'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixMulAddNV = &Opcode { + Opname: "OpCooperativeMatrixMulAddNV", + Class: "Reserved", + Opcode: 5361, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'C'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLengthNV = &Opcode { + Opname: "OpCooperativeMatrixLengthNV", + Class: "Reserved", + Opcode: 5362, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpBeginInvocationInterlockEXT = &Opcode { + Opname: "OpBeginInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5364, + Operands: []Operand { + }, + } + OpEndInvocationInterlockEXT = &Opcode { + Opname: "OpEndInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5365, + Operands: []Operand { + }, + } + OpCooperativeMatrixReduceNV = &Opcode { + Opname: "OpCooperativeMatrixReduceNV", + Class: "Arithmetic", + Opcode: 5366, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindCooperativeMatrixReduce, + Name: "'Reduce'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CombineFunc'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadTensorNV = &Opcode { + Opname: "OpCooperativeMatrixLoadTensorNV", + Class: "Memory", + Opcode: 5367, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "", + }, + Operand { + Kind: OperandKindTensorAddressingOperands, + Name: "'Tensor Addressing Operands'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixStoreTensorNV = &Opcode { + Opname: "OpCooperativeMatrixStoreTensorNV", + Class: "Memory", + Opcode: 5368, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "'Memory Operand'", + Quantifier: "", + }, + Operand { + Kind: OperandKindTensorAddressingOperands, + Name: "'Tensor Addressing Operands'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixPerElementOpNV = &Opcode { + Opname: "OpCooperativeMatrixPerElementOpNV", + Class: "Function", + Opcode: 5369, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Func'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operands'", + Quantifier: "*", + }, + }, + } + OpTypeTensorLayoutNV = &Opcode { + Opname: "OpTypeTensorLayoutNV", + Class: "Type-Declaration", + Opcode: 5370, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClampMode'", + Quantifier: "", + }, + }, + } + OpTypeTensorViewNV = &Opcode { + Opname: "OpTypeTensorViewNV", + Class: "Type-Declaration", + Opcode: 5371, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HasDimensions'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "*", + }, + }, + } + OpCreateTensorLayoutNV = &Opcode { + Opname: "OpCreateTensorLayoutNV", + Class: "Reserved", + Opcode: 5372, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTensorLayoutSetDimensionNV = &Opcode { + Opname: "OpTensorLayoutSetDimensionNV", + Class: "Reserved", + Opcode: 5373, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSetStrideNV = &Opcode { + Opname: "OpTensorLayoutSetStrideNV", + Class: "Reserved", + Opcode: 5374, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSliceNV = &Opcode { + Opname: "OpTensorLayoutSliceNV", + Class: "Reserved", + Opcode: 5375, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operands'", + Quantifier: "*", + }, + }, + } + OpTensorLayoutSetClampValueNV = &Opcode { + Opname: "OpTensorLayoutSetClampValueNV", + Class: "Reserved", + Opcode: 5376, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpCreateTensorViewNV = &Opcode { + Opname: "OpCreateTensorViewNV", + Class: "Reserved", + Opcode: 5377, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTensorViewSetDimensionNV = &Opcode { + Opname: "OpTensorViewSetDimensionNV", + Class: "Reserved", + Opcode: 5378, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dim'", + Quantifier: "*", + }, + }, + } + OpTensorViewSetStrideNV = &Opcode { + Opname: "OpTensorViewSetStrideNV", + Class: "Reserved", + Opcode: 5379, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "*", + }, + }, + } + OpDemoteToHelperInvocation = &Opcode { + Opname: "OpDemoteToHelperInvocation", + Class: "Control-Flow", + Opcode: 5380, + Operands: []Operand { + }, + } + OpIsHelperInvocationEXT = &Opcode { + Opname: "OpIsHelperInvocationEXT", + Class: "Reserved", + Opcode: 5381, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTensorViewSetClipNV = &Opcode { + Opname: "OpTensorViewSetClipNV", + Class: "Reserved", + Opcode: 5382, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorView'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipRowOffset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipRowSpan'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipColOffset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClipColSpan'", + Quantifier: "", + }, + }, + } + OpTensorLayoutSetBlockSizeNV = &Opcode { + Opname: "OpTensorLayoutSetBlockSizeNV", + Class: "Reserved", + Opcode: 5384, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'TensorLayout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'BlockSize'", + Quantifier: "*", + }, + }, + } + OpCooperativeMatrixTransposeNV = &Opcode { + Opname: "OpCooperativeMatrixTransposeNV", + Class: "Conversion", + Opcode: 5390, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpConvertUToImageNV = &Opcode { + Opname: "OpConvertUToImageNV", + Class: "Reserved", + Opcode: 5391, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertUToSamplerNV = &Opcode { + Opname: "OpConvertUToSamplerNV", + Class: "Reserved", + Opcode: 5392, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertImageToUNV = &Opcode { + Opname: "OpConvertImageToUNV", + Class: "Reserved", + Opcode: 5393, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertSamplerToUNV = &Opcode { + Opname: "OpConvertSamplerToUNV", + Class: "Reserved", + Opcode: 5394, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertUToSampledImageNV = &Opcode { + Opname: "OpConvertUToSampledImageNV", + Class: "Reserved", + Opcode: 5395, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpConvertSampledImageToUNV = &Opcode { + Opname: "OpConvertSampledImageToUNV", + Class: "Reserved", + Opcode: 5396, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSamplerImageAddressingModeNV = &Opcode { + Opname: "OpSamplerImageAddressingModeNV", + Class: "Reserved", + Opcode: 5397, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Bit Width'", + Quantifier: "", + }, + }, + } + OpRawAccessChainNV = &Opcode { + Opname: "OpRawAccessChainNV", + Class: "Memory", + Opcode: 5398, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Byte stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Byte offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindRawAccessChainOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpSubgroupShuffleINTEL = &Opcode { + Opname: "OpSubgroupShuffleINTEL", + Class: "Group", + Opcode: 5571, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InvocationId'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleDownINTEL = &Opcode { + Opname: "OpSubgroupShuffleDownINTEL", + Class: "Group", + Opcode: 5572, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Next'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleUpINTEL = &Opcode { + Opname: "OpSubgroupShuffleUpINTEL", + Class: "Group", + Opcode: 5573, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Previous'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleXorINTEL = &Opcode { + Opname: "OpSubgroupShuffleXorINTEL", + Class: "Group", + Opcode: 5574, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSubgroupBlockReadINTEL = &Opcode { + Opname: "OpSubgroupBlockReadINTEL", + Class: "Group", + Opcode: 5575, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + }, + } + OpSubgroupBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupBlockWriteINTEL", + Class: "Group", + Opcode: 5576, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpSubgroupImageBlockReadINTEL = &Opcode { + Opname: "OpSubgroupImageBlockReadINTEL", + Class: "Group", + Opcode: 5577, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpSubgroupImageBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupImageBlockWriteINTEL", + Class: "Group", + Opcode: 5578, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpSubgroupImageMediaBlockReadINTEL = &Opcode { + Opname: "OpSubgroupImageMediaBlockReadINTEL", + Class: "Group", + Opcode: 5580, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Height'", + Quantifier: "", + }, + }, + } + OpSubgroupImageMediaBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupImageMediaBlockWriteINTEL", + Class: "Group", + Opcode: 5581, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Height'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpUCountLeadingZerosINTEL = &Opcode { + Opname: "OpUCountLeadingZerosINTEL", + Class: "Reserved", + Opcode: 5585, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpUCountTrailingZerosINTEL = &Opcode { + Opname: "OpUCountTrailingZerosINTEL", + Class: "Reserved", + Opcode: 5586, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpAbsISubINTEL = &Opcode { + Opname: "OpAbsISubINTEL", + Class: "Reserved", + Opcode: 5587, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpAbsUSubINTEL = &Opcode { + Opname: "OpAbsUSubINTEL", + Class: "Reserved", + Opcode: 5588, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAddSatINTEL = &Opcode { + Opname: "OpIAddSatINTEL", + Class: "Reserved", + Opcode: 5589, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAddSatINTEL = &Opcode { + Opname: "OpUAddSatINTEL", + Class: "Reserved", + Opcode: 5590, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAverageINTEL = &Opcode { + Opname: "OpIAverageINTEL", + Class: "Reserved", + Opcode: 5591, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAverageINTEL = &Opcode { + Opname: "OpUAverageINTEL", + Class: "Reserved", + Opcode: 5592, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAverageRoundedINTEL = &Opcode { + Opname: "OpIAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5593, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAverageRoundedINTEL = &Opcode { + Opname: "OpUAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5594, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISubSatINTEL = &Opcode { + Opname: "OpISubSatINTEL", + Class: "Reserved", + Opcode: 5595, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUSubSatINTEL = &Opcode { + Opname: "OpUSubSatINTEL", + Class: "Reserved", + Opcode: 5596, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIMul32x16INTEL = &Opcode { + Opname: "OpIMul32x16INTEL", + Class: "Reserved", + Opcode: 5597, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMul32x16INTEL = &Opcode { + Opname: "OpUMul32x16INTEL", + Class: "Reserved", + Opcode: 5598, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpConstantFunctionPointerINTEL = &Opcode { + Opname: "OpConstantFunctionPointerINTEL", + Class: "@exclude", + Opcode: 5600, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + }, + } + OpFunctionPointerCallINTEL = &Opcode { + Opname: "OpFunctionPointerCallINTEL", + Class: "@exclude", + Opcode: 5601, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "*", + }, + }, + } + OpAsmTargetINTEL = &Opcode { + Opname: "OpAsmTargetINTEL", + Class: "@exclude", + Opcode: 5609, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Asm target'", + Quantifier: "", + }, + }, + } + OpAsmINTEL = &Opcode { + Opname: "OpAsmINTEL", + Class: "@exclude", + Opcode: 5610, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Asm type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Asm instructions'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Constraints'", + Quantifier: "", + }, + }, + } + OpAsmCallINTEL = &Opcode { + Opname: "OpAsmCallINTEL", + Class: "@exclude", + Opcode: 5611, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Asm'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Argument 0'", + Quantifier: "*", + }, + }, + } + OpAtomicFMinEXT = &Opcode { + Opname: "OpAtomicFMinEXT", + Class: "Atomic", + Opcode: 5614, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicFMaxEXT = &Opcode { + Opname: "OpAtomicFMaxEXT", + Class: "Atomic", + Opcode: 5615, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAssumeTrueKHR = &Opcode { + Opname: "OpAssumeTrueKHR", + Class: "Miscellaneous", + Opcode: 5630, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + }, + } + OpExpectKHR = &Opcode { + Opname: "OpExpectKHR", + Class: "Miscellaneous", + Opcode: 5631, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ExpectedValue'", + Quantifier: "", + }, + }, + } + OpDecorateString = &Opcode { + Opname: "OpDecorateString", + Class: "Annotation", + Opcode: 5632, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpMemberDecorateString = &Opcode { + Opname: "OpMemberDecorateString", + Class: "Annotation", + Opcode: 5633, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Struct Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpVmeImageINTEL = &Opcode { + Opname: "OpVmeImageINTEL", + Class: "@exclude", + Opcode: 5699, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampler'", + Quantifier: "", + }, + }, + } + OpTypeVmeImageINTEL = &Opcode { + Opname: "OpTypeVmeImageINTEL", + Class: "@exclude", + Opcode: 5700, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + }, + } + OpTypeAvcImePayloadINTEL = &Opcode { + Opname: "OpTypeAvcImePayloadINTEL", + Class: "@exclude", + Opcode: 5701, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcRefPayloadINTEL = &Opcode { + Opname: "OpTypeAvcRefPayloadINTEL", + Class: "@exclude", + Opcode: 5702, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcSicPayloadINTEL = &Opcode { + Opname: "OpTypeAvcSicPayloadINTEL", + Class: "@exclude", + Opcode: 5703, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcMcePayloadINTEL = &Opcode { + Opname: "OpTypeAvcMcePayloadINTEL", + Class: "@exclude", + Opcode: 5704, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcMceResultINTEL = &Opcode { + Opname: "OpTypeAvcMceResultINTEL", + Class: "@exclude", + Opcode: 5705, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultINTEL = &Opcode { + Opname: "OpTypeAvcImeResultINTEL", + Class: "@exclude", + Opcode: 5706, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpTypeAvcImeResultSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5707, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpTypeAvcImeResultDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5708, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpTypeAvcImeSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5709, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeDualReferenceStreaminINTEL = &Opcode { + Opname: "OpTypeAvcImeDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5710, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcRefResultINTEL = &Opcode { + Opname: "OpTypeAvcRefResultINTEL", + Class: "@exclude", + Opcode: 5711, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcSicResultINTEL = &Opcode { + Opname: "OpTypeAvcSicResultINTEL", + Class: "@exclude", + Opcode: 5712, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5713, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5714, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Base Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5715, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5716, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Shape Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5717, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5718, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction Cost'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5719, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL", + Class: "@exclude", + Opcode: 5720, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5721, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5722, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5723, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL", + Class: "@exclude", + Opcode: 5724, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Cost Center Delta'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Cost Table'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cost Precision'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL", + Class: "@exclude", + Opcode: 5725, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL", + Class: "@exclude", + Opcode: 5726, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL", + Class: "@exclude", + Opcode: 5727, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetAcOnlyHaarINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetAcOnlyHaarINTEL", + Class: "@exclude", + Opcode: 5728, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5729, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5730, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5731, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Forward Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Backward Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToImePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToImePayloadINTEL", + Class: "@exclude", + Opcode: 5732, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToImeResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToImeResultINTEL", + Class: "@exclude", + Opcode: 5733, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToRefPayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToRefPayloadINTEL", + Class: "@exclude", + Opcode: 5734, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToRefResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToRefResultINTEL", + Class: "@exclude", + Opcode: 5735, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToSicPayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToSicPayloadINTEL", + Class: "@exclude", + Opcode: 5736, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToSicResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToSicResultINTEL", + Class: "@exclude", + Opcode: 5737, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5738, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5739, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetBestInterDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetBestInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5740, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMajorShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMajorShapeINTEL", + Class: "@exclude", + Opcode: 5741, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMinorShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMinorShapeINTEL", + Class: "@exclude", + Opcode: 5742, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterDirectionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterDirectionsINTEL", + Class: "@exclude", + Opcode: 5743, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5744, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5745, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5746, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Parameter Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcImeInitializeINTEL", + Class: "@exclude", + Opcode: 5747, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SAD Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5748, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetDualReferenceINTEL", + Class: "@exclude", + Opcode: 5749, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'id> Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeRefWindowSizeINTEL = &Opcode { + Opname: "OpSubgroupAvcImeRefWindowSizeINTEL", + Class: "@exclude", + Opcode: 5750, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dual Ref'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeAdjustRefOffsetINTEL = &Opcode { + Opname: "OpSubgroupAvcImeAdjustRefOffsetINTEL", + Class: "@exclude", + Opcode: 5751, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Window Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Size'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcImeConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5752, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5753, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Max Motion Vector Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5754, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL", + Class: "@exclude", + Opcode: 5755, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Threshold'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetWeightedSadINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetWeightedSadINTEL", + Class: "@exclude", + Opcode: 5756, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Sad Weights'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5757, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5758, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5759, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5760, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5761, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5762, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5763, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5764, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcImeConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5765, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5766, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5767, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5768, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5769, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5770, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5771, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5772, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5773, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5774, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5775, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetBorderReachedINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetBorderReachedINTEL", + Class: "@exclude", + Opcode: 5776, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Select'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL", + Class: "@exclude", + Opcode: 5777, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL", + Class: "@exclude", + Opcode: 5778, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL", + Class: "@exclude", + Opcode: 5779, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL", + Class: "@exclude", + Opcode: 5780, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcFmeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcFmeInitializeINTEL", + Class: "@exclude", + Opcode: 5781, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Minor Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pixel Resolution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcBmeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcBmeInitializeINTEL", + Class: "@exclude", + Opcode: 5782, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Minor Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pixel Resolution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bidirectional Weight'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcRefConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5783, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = &Opcode { + Opname: "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5784, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcRefSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5785, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5786, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5787, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5788, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5789, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcRefConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5790, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicInitializeINTEL", + Class: "@exclude", + Opcode: 5791, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureSkcINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureSkcINTEL", + Class: "@exclude", + Opcode: 5792, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Block Partition Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Motion Vector Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bidirectional Weight'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureIpeLumaINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureIpeLumaINTEL", + Class: "@exclude", + Opcode: 5793, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Intra Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intra Neighbour Availabilty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Luma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Right Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL", + Class: "@exclude", + Opcode: 5794, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Intra Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intra Neighbour Availabilty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Luma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Right Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Chroma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Chroma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Chroma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetMotionVectorMaskINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetMotionVectorMaskINTEL", + Class: "@exclude", + Opcode: 5795, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Block Partition Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5796, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5797, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Shape Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5798, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Mode Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Packed Neighbor Modes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Packed Non Dc Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5799, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Chroma Mode Base Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5800, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL", + Class: "@exclude", + Opcode: 5801, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Sad Coefficients'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL", + Class: "@exclude", + Opcode: 5802, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Based Skip Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateIpeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateIpeINTEL", + Class: "@exclude", + Opcode: 5803, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5804, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5805, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5806, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5807, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5808, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetIpeLumaShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetIpeLumaShapeINTEL", + Class: "@exclude", + Opcode: 5809, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL", + Class: "@exclude", + Opcode: 5810, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL", + Class: "@exclude", + Opcode: 5811, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL", + Class: "@exclude", + Opcode: 5812, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetIpeChromaModeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetIpeChromaModeINTEL", + Class: "@exclude", + Opcode: 5813, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL", + Class: "@exclude", + Opcode: 5814, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL", + Class: "@exclude", + Opcode: 5815, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetInterRawSadsINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetInterRawSadsINTEL", + Class: "@exclude", + Opcode: 5816, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpVariableLengthArrayINTEL = &Opcode { + Opname: "OpVariableLengthArrayINTEL", + Class: "@exclude", + Opcode: 5818, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Lenght'", + Quantifier: "", + }, + }, + } + OpSaveMemoryINTEL = &Opcode { + Opname: "OpSaveMemoryINTEL", + Class: "@exclude", + Opcode: 5819, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRestoreMemoryINTEL = &Opcode { + Opname: "OpRestoreMemoryINTEL", + Class: "@exclude", + Opcode: 5820, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinCosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatSinCosPiINTEL", + Class: "@exclude", + Opcode: 5840, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'FromSign'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastINTEL = &Opcode { + Opname: "OpArbitraryFloatCastINTEL", + Class: "@exclude", + Opcode: 5841, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastFromIntINTEL = &Opcode { + Opname: "OpArbitraryFloatCastFromIntINTEL", + Class: "@exclude", + Opcode: 5842, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'FromSign'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCastToIntINTEL = &Opcode { + Opname: "OpArbitraryFloatCastToIntINTEL", + Class: "@exclude", + Opcode: 5843, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatAddINTEL = &Opcode { + Opname: "OpArbitraryFloatAddINTEL", + Class: "@exclude", + Opcode: 5846, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSubINTEL = &Opcode { + Opname: "OpArbitraryFloatSubINTEL", + Class: "@exclude", + Opcode: 5847, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatMulINTEL = &Opcode { + Opname: "OpArbitraryFloatMulINTEL", + Class: "@exclude", + Opcode: 5848, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatDivINTEL = &Opcode { + Opname: "OpArbitraryFloatDivINTEL", + Class: "@exclude", + Opcode: 5849, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatGTINTEL = &Opcode { + Opname: "OpArbitraryFloatGTINTEL", + Class: "@exclude", + Opcode: 5850, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatGEINTEL = &Opcode { + Opname: "OpArbitraryFloatGEINTEL", + Class: "@exclude", + Opcode: 5851, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLTINTEL = &Opcode { + Opname: "OpArbitraryFloatLTINTEL", + Class: "@exclude", + Opcode: 5852, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLEINTEL = &Opcode { + Opname: "OpArbitraryFloatLEINTEL", + Class: "@exclude", + Opcode: 5853, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatEQINTEL = &Opcode { + Opname: "OpArbitraryFloatEQINTEL", + Class: "@exclude", + Opcode: 5854, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatRecipINTEL = &Opcode { + Opname: "OpArbitraryFloatRecipINTEL", + Class: "@exclude", + Opcode: 5855, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatRSqrtINTEL = &Opcode { + Opname: "OpArbitraryFloatRSqrtINTEL", + Class: "@exclude", + Opcode: 5856, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCbrtINTEL = &Opcode { + Opname: "OpArbitraryFloatCbrtINTEL", + Class: "@exclude", + Opcode: 5857, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatHypotINTEL = &Opcode { + Opname: "OpArbitraryFloatHypotINTEL", + Class: "@exclude", + Opcode: 5858, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSqrtINTEL = &Opcode { + Opname: "OpArbitraryFloatSqrtINTEL", + Class: "@exclude", + Opcode: 5859, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLogINTEL = &Opcode { + Opname: "OpArbitraryFloatLogINTEL", + Class: "@exclude", + Opcode: 5860, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog2INTEL = &Opcode { + Opname: "OpArbitraryFloatLog2INTEL", + Class: "@exclude", + Opcode: 5861, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog10INTEL = &Opcode { + Opname: "OpArbitraryFloatLog10INTEL", + Class: "@exclude", + Opcode: 5862, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatLog1pINTEL = &Opcode { + Opname: "OpArbitraryFloatLog1pINTEL", + Class: "@exclude", + Opcode: 5863, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExpINTEL = &Opcode { + Opname: "OpArbitraryFloatExpINTEL", + Class: "@exclude", + Opcode: 5864, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExp2INTEL = &Opcode { + Opname: "OpArbitraryFloatExp2INTEL", + Class: "@exclude", + Opcode: 5865, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExp10INTEL = &Opcode { + Opname: "OpArbitraryFloatExp10INTEL", + Class: "@exclude", + Opcode: 5866, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatExpm1INTEL = &Opcode { + Opname: "OpArbitraryFloatExpm1INTEL", + Class: "@exclude", + Opcode: 5867, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinINTEL = &Opcode { + Opname: "OpArbitraryFloatSinINTEL", + Class: "@exclude", + Opcode: 5868, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCosINTEL = &Opcode { + Opname: "OpArbitraryFloatCosINTEL", + Class: "@exclude", + Opcode: 5869, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinCosINTEL = &Opcode { + Opname: "OpArbitraryFloatSinCosINTEL", + Class: "@exclude", + Opcode: 5870, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatSinPiINTEL = &Opcode { + Opname: "OpArbitraryFloatSinPiINTEL", + Class: "@exclude", + Opcode: 5871, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatCosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatCosPiINTEL", + Class: "@exclude", + Opcode: 5872, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatASinINTEL = &Opcode { + Opname: "OpArbitraryFloatASinINTEL", + Class: "@exclude", + Opcode: 5873, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatASinPiINTEL = &Opcode { + Opname: "OpArbitraryFloatASinPiINTEL", + Class: "@exclude", + Opcode: 5874, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatACosINTEL = &Opcode { + Opname: "OpArbitraryFloatACosINTEL", + Class: "@exclude", + Opcode: 5875, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatACosPiINTEL = &Opcode { + Opname: "OpArbitraryFloatACosPiINTEL", + Class: "@exclude", + Opcode: 5876, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATanINTEL = &Opcode { + Opname: "OpArbitraryFloatATanINTEL", + Class: "@exclude", + Opcode: 5877, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATanPiINTEL = &Opcode { + Opname: "OpArbitraryFloatATanPiINTEL", + Class: "@exclude", + Opcode: 5878, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatATan2INTEL = &Opcode { + Opname: "OpArbitraryFloatATan2INTEL", + Class: "@exclude", + Opcode: 5879, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowINTEL = &Opcode { + Opname: "OpArbitraryFloatPowINTEL", + Class: "@exclude", + Opcode: 5880, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowRINTEL = &Opcode { + Opname: "OpArbitraryFloatPowRINTEL", + Class: "@exclude", + Opcode: 5881, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpArbitraryFloatPowNINTEL = &Opcode { + Opname: "OpArbitraryFloatPowNINTEL", + Class: "@exclude", + Opcode: 5882, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'M1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Mout'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'EnableSubnormals'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingMode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'RoundingAccuracy'", + Quantifier: "", + }, + }, + } + OpLoopControlINTEL = &Opcode { + Opname: "OpLoopControlINTEL", + Class: "Reserved", + Opcode: 5887, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Loop Control Parameters'", + Quantifier: "*", + }, + }, + } + OpAliasDomainDeclINTEL = &Opcode { + Opname: "OpAliasDomainDeclINTEL", + Class: "@exclude", + Opcode: 5911, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpAliasScopeDeclINTEL = &Opcode { + Opname: "OpAliasScopeDeclINTEL", + Class: "@exclude", + Opcode: 5912, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Alias Domain'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpAliasScopeListDeclINTEL = &Opcode { + Opname: "OpAliasScopeListDeclINTEL", + Class: "@exclude", + Opcode: 5913, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'AliasScope1, AliasScope2, ...'", + Quantifier: "*", + }, + }, + } + OpFixedSqrtINTEL = &Opcode { + Opname: "OpFixedSqrtINTEL", + Class: "@exclude", + Opcode: 5923, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedRecipINTEL = &Opcode { + Opname: "OpFixedRecipINTEL", + Class: "@exclude", + Opcode: 5924, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedRsqrtINTEL = &Opcode { + Opname: "OpFixedRsqrtINTEL", + Class: "@exclude", + Opcode: 5925, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinINTEL = &Opcode { + Opname: "OpFixedSinINTEL", + Class: "@exclude", + Opcode: 5926, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedCosINTEL = &Opcode { + Opname: "OpFixedCosINTEL", + Class: "@exclude", + Opcode: 5927, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinCosINTEL = &Opcode { + Opname: "OpFixedSinCosINTEL", + Class: "@exclude", + Opcode: 5928, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinPiINTEL = &Opcode { + Opname: "OpFixedSinPiINTEL", + Class: "@exclude", + Opcode: 5929, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedCosPiINTEL = &Opcode { + Opname: "OpFixedCosPiINTEL", + Class: "@exclude", + Opcode: 5930, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedSinCosPiINTEL = &Opcode { + Opname: "OpFixedSinCosPiINTEL", + Class: "@exclude", + Opcode: 5931, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedLogINTEL = &Opcode { + Opname: "OpFixedLogINTEL", + Class: "@exclude", + Opcode: 5932, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpFixedExpINTEL = &Opcode { + Opname: "OpFixedExpINTEL", + Class: "@exclude", + Opcode: 5933, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'S'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'rI'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Q'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'O'", + Quantifier: "", + }, + }, + } + OpPtrCastToCrossWorkgroupINTEL = &Opcode { + Opname: "OpPtrCastToCrossWorkgroupINTEL", + Class: "@exclude", + Opcode: 5934, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpCrossWorkgroupCastToPtrINTEL = &Opcode { + Opname: "OpCrossWorkgroupCastToPtrINTEL", + Class: "@exclude", + Opcode: 5938, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpReadPipeBlockingINTEL = &Opcode { + Opname: "OpReadPipeBlockingINTEL", + Class: "Pipe", + Opcode: 5946, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpWritePipeBlockingINTEL = &Opcode { + Opname: "OpWritePipeBlockingINTEL", + Class: "Pipe", + Opcode: 5947, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpFPGARegINTEL = &Opcode { + Opname: "OpFPGARegINTEL", + Class: "Reserved", + Opcode: 5949, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Result'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Input'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayTMinKHR = &Opcode { + Opname: "OpRayQueryGetRayTMinKHR", + Class: "Reserved", + Opcode: 6016, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayFlagsKHR = &Opcode { + Opname: "OpRayQueryGetRayFlagsKHR", + Class: "Reserved", + Opcode: 6017, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTKHR", + Class: "Reserved", + Opcode: 6018, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + Class: "Reserved", + Opcode: 6019, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceIdKHR", + Class: "Reserved", + Opcode: 6020, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + Class: "Reserved", + Opcode: 6021, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", + Class: "Reserved", + Opcode: 6022, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", + Class: "Reserved", + Opcode: 6023, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionBarycentricsKHR", + Class: "Reserved", + Opcode: 6024, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionFrontFaceKHR", + Class: "Reserved", + Opcode: 6025, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + Class: "Reserved", + Opcode: 6026, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", + Class: "Reserved", + Opcode: 6027, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", + Class: "Reserved", + Opcode: 6028, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayDirectionKHR", + Class: "Reserved", + Opcode: 6029, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayOriginKHR", + Class: "Reserved", + Opcode: 6030, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", + Class: "Reserved", + Opcode: 6031, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", + Class: "Reserved", + Opcode: 6032, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpAtomicFAddEXT = &Opcode { + Opname: "OpAtomicFAddEXT", + Class: "Atomic", + Opcode: 6035, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpTypeBufferSurfaceINTEL = &Opcode { + Opname: "OpTypeBufferSurfaceINTEL", + Class: "Type-Declaration", + Opcode: 6086, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "'AccessQualifier'", + Quantifier: "", + }, + }, + } + OpTypeStructContinuedINTEL = &Opcode { + Opname: "OpTypeStructContinuedINTEL", + Class: "Type-Declaration", + Opcode: 6090, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Member 0 type', + 'member 1 type', + ...", + Quantifier: "*", + }, + }, + } + OpConstantCompositeContinuedINTEL = &Opcode { + Opname: "OpConstantCompositeContinuedINTEL", + Class: "Constant-Creation", + Opcode: 6091, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpSpecConstantCompositeContinuedINTEL = &Opcode { + Opname: "OpSpecConstantCompositeContinuedINTEL", + Class: "Constant-Creation", + Opcode: 6092, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpCompositeConstructContinuedINTEL = &Opcode { + Opname: "OpCompositeConstructContinuedINTEL", + Class: "Composite", + Opcode: 6096, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpConvertFToBF16INTEL = &Opcode { + Opname: "OpConvertFToBF16INTEL", + Class: "Conversion", + Opcode: 6116, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertBF16ToFINTEL = &Opcode { + Opname: "OpConvertBF16ToFINTEL", + Class: "Conversion", + Opcode: 6117, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'BFloat16 Value'", + Quantifier: "", + }, + }, + } + OpControlBarrierArriveINTEL = &Opcode { + Opname: "OpControlBarrierArriveINTEL", + Class: "Barrier", + Opcode: 6142, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpControlBarrierWaitINTEL = &Opcode { + Opname: "OpControlBarrierWaitINTEL", + Class: "Barrier", + Opcode: 6143, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpArithmeticFenceEXT = &Opcode { + Opname: "OpArithmeticFenceEXT", + Class: "Miscellaneous", + Opcode: 6145, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Target '", + Quantifier: "", + }, + }, + } + OpSubgroupBlockPrefetchINTEL = &Opcode { + Opname: "OpSubgroupBlockPrefetchINTEL", + Class: "Group", + Opcode: 6221, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'NumBytes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpGroupIMulKHR = &Opcode { + Opname: "OpGroupIMulKHR", + Class: "Group", + Opcode: 6401, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMulKHR = &Opcode { + Opname: "OpGroupFMulKHR", + Class: "Group", + Opcode: 6402, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseAndKHR = &Opcode { + Opname: "OpGroupBitwiseAndKHR", + Class: "Group", + Opcode: 6403, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseOrKHR = &Opcode { + Opname: "OpGroupBitwiseOrKHR", + Class: "Group", + Opcode: 6404, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupBitwiseXorKHR = &Opcode { + Opname: "OpGroupBitwiseXorKHR", + Class: "Group", + Opcode: 6405, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalAndKHR = &Opcode { + Opname: "OpGroupLogicalAndKHR", + Class: "Group", + Opcode: 6406, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalOrKHR = &Opcode { + Opname: "OpGroupLogicalOrKHR", + Class: "Group", + Opcode: 6407, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupLogicalXorKHR = &Opcode { + Opname: "OpGroupLogicalXorKHR", + Class: "Group", + Opcode: 6408, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpMaskedGatherINTEL = &Opcode { + Opname: "OpMaskedGatherINTEL", + Class: "Memory", + Opcode: 6428, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PtrVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'FillEmpty'", + Quantifier: "", + }, + }, + } + OpMaskedScatterINTEL = &Opcode { + Opname: "OpMaskedScatterINTEL", + Class: "Memory", + Opcode: 6429, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'InputVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PtrVector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + }, + } + + GLSLStd450_Round = &Opcode { + Opname: "Round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_RoundEven = &Opcode { + Opname: "RoundEven", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Trunc = &Opcode { + Opname: "Trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FAbs = &Opcode { + Opname: "FAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SAbs = &Opcode { + Opname: "SAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FSign = &Opcode { + Opname: "FSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SSign = &Opcode { + Opname: "SSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Floor = &Opcode { + Opname: "Floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ceil = &Opcode { + Opname: "Ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fract = &Opcode { + Opname: "Fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Radians = &Opcode { + Opname: "Radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + GLSLStd450_Degrees = &Opcode { + Opname: "Degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sin = &Opcode { + Opname: "Sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cos = &Opcode { + Opname: "Cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tan = &Opcode { + Opname: "Tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asin = &Opcode { + Opname: "Asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acos = &Opcode { + Opname: "Acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan = &Opcode { + Opname: "Atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y_over_x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sinh = &Opcode { + Opname: "Sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cosh = &Opcode { + Opname: "Cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tanh = &Opcode { + Opname: "Tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asinh = &Opcode { + Opname: "Asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acosh = &Opcode { + Opname: "Acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atanh = &Opcode { + Opname: "Atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan2 = &Opcode { + Opname: "Atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Pow = &Opcode { + Opname: "Pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp = &Opcode { + Opname: "Exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log = &Opcode { + Opname: "Log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp2 = &Opcode { + Opname: "Exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log2 = &Opcode { + Opname: "Log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sqrt = &Opcode { + Opname: "Sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_InverseSqrt = &Opcode { + Opname: "InverseSqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Determinant = &Opcode { + Opname: "Determinant", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_MatrixInverse = &Opcode { + Opname: "MatrixInverse", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Modf = &Opcode { + Opname: "Modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + GLSLStd450_ModfStruct = &Opcode { + Opname: "ModfStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMin = &Opcode { + Opname: "FMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMin = &Opcode { + Opname: "UMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMin = &Opcode { + Opname: "SMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMax = &Opcode { + Opname: "FMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMax = &Opcode { + Opname: "UMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMax = &Opcode { + Opname: "SMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FClamp = &Opcode { + Opname: "FClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_UClamp = &Opcode { + Opname: "UClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_SClamp = &Opcode { + Opname: "SClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMix = &Opcode { + Opname: "FMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_IMix = &Opcode { + Opname: "IMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_Step = &Opcode { + Opname: "Step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SmoothStep = &Opcode { + Opname: "SmoothStep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fma = &Opcode { + Opname: "Fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + GLSLStd450_Frexp = &Opcode { + Opname: "Frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_FrexpStruct = &Opcode { + Opname: "FrexpStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ldexp = &Opcode { + Opname: "Ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm4x8 = &Opcode { + Opname: "PackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm4x8 = &Opcode { + Opname: "PackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm2x16 = &Opcode { + Opname: "PackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm2x16 = &Opcode { + Opname: "PackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackHalf2x16 = &Opcode { + Opname: "PackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackDouble2x32 = &Opcode { + Opname: "PackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm2x16 = &Opcode { + Opname: "UnpackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm2x16 = &Opcode { + Opname: "UnpackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackHalf2x16 = &Opcode { + Opname: "UnpackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm4x8 = &Opcode { + Opname: "UnpackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm4x8 = &Opcode { + Opname: "UnpackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackDouble2x32 = &Opcode { + Opname: "UnpackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_Length = &Opcode { + Opname: "Length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Distance = &Opcode { + Opname: "Distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cross = &Opcode { + Opname: "Cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Normalize = &Opcode { + Opname: "Normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FaceForward = &Opcode { + Opname: "FaceForward", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Nref'", + Quantifier: "", + }, + }, + } + GLSLStd450_Reflect = &Opcode { + Opname: "Reflect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + }, + } + GLSLStd450_Refract = &Opcode { + Opname: "Refract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'eta'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindILsb = &Opcode { + Opname: "FindILsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindSMsb = &Opcode { + Opname: "FindSMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindUMsb = &Opcode { + Opname: "FindUMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtCentroid = &Opcode { + Opname: "InterpolateAtCentroid", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtSample = &Opcode { + Opname: "InterpolateAtSample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'sample'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtOffset = &Opcode { + Opname: "InterpolateAtOffset", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMin = &Opcode { + Opname: "NMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMax = &Opcode { + Opname: "NMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NClamp = &Opcode { + Opname: "NClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + OpenCLStd_acos = &Opcode { + Opname: "acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acosh = &Opcode { + Opname: "acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acospi = &Opcode { + Opname: "acospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asin = &Opcode { + Opname: "asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinh = &Opcode { + Opname: "asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinpi = &Opcode { + Opname: "asinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan = &Opcode { + Opname: "atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2 = &Opcode { + Opname: "atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanh = &Opcode { + Opname: "atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanpi = &Opcode { + Opname: "atanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2pi = &Opcode { + Opname: "atan2pi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cbrt = &Opcode { + Opname: "cbrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ceil = &Opcode { + Opname: "ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_copysign = &Opcode { + Opname: "copysign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_cos = &Opcode { + Opname: "cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cosh = &Opcode { + Opname: "cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cospi = &Opcode { + Opname: "cospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erfc = &Opcode { + Opname: "erfc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erf = &Opcode { + Opname: "erf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp = &Opcode { + Opname: "exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp2 = &Opcode { + Opname: "exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp10 = &Opcode { + Opname: "exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_expm1 = &Opcode { + Opname: "expm1", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fabs = &Opcode { + Opname: "fabs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fdim = &Opcode { + Opname: "fdim", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_floor = &Opcode { + Opname: "floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fma = &Opcode { + Opname: "fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax = &Opcode { + Opname: "fmax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin = &Opcode { + Opname: "fmin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmod = &Opcode { + Opname: "fmod", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fract = &Opcode { + Opname: "fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_frexp = &Opcode { + Opname: "frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + OpenCLStd_hypot = &Opcode { + Opname: "hypot", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_ilogb = &Opcode { + Opname: "ilogb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ldexp = &Opcode { + Opname: "ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'k'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma = &Opcode { + Opname: "lgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma_r = &Opcode { + Opname: "lgamma_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'signp'", + Quantifier: "", + }, + }, + } + OpenCLStd_log = &Opcode { + Opname: "log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log2 = &Opcode { + Opname: "log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log10 = &Opcode { + Opname: "log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log1p = &Opcode { + Opname: "log1p", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_logb = &Opcode { + Opname: "logb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_mad = &Opcode { + Opname: "mad", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_maxmag = &Opcode { + Opname: "maxmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_minmag = &Opcode { + Opname: "minmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_modf = &Opcode { + Opname: "modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'iptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_nan = &Opcode { + Opname: "nan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'nancode'", + Quantifier: "", + }, + }, + } + OpenCLStd_nextafter = &Opcode { + Opname: "nextafter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_pow = &Opcode { + Opname: "pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y", + Quantifier: "", + }, + }, + } + OpenCLStd_pown = &Opcode { + Opname: "pown", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_powr = &Opcode { + Opname: "powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remainder = &Opcode { + Opname: "remainder", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remquo = &Opcode { + Opname: "remquo", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'quo'", + Quantifier: "", + }, + }, + } + OpenCLStd_rint = &Opcode { + Opname: "rint", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rootn = &Opcode { + Opname: "rootn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_round = &Opcode { + Opname: "round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rsqrt = &Opcode { + Opname: "rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sin = &Opcode { + Opname: "sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sincos = &Opcode { + Opname: "sincos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'cosval'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinh = &Opcode { + Opname: "sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinpi = &Opcode { + Opname: "sinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sqrt = &Opcode { + Opname: "sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tan = &Opcode { + Opname: "tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanh = &Opcode { + Opname: "tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanpi = &Opcode { + Opname: "tanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tgamma = &Opcode { + Opname: "tgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_trunc = &Opcode { + Opname: "trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_cos = &Opcode { + Opname: "half_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_divide = &Opcode { + Opname: "half_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp = &Opcode { + Opname: "half_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp2 = &Opcode { + Opname: "half_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp10 = &Opcode { + Opname: "half_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log = &Opcode { + Opname: "half_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log2 = &Opcode { + Opname: "half_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log10 = &Opcode { + Opname: "half_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_powr = &Opcode { + Opname: "half_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_recip = &Opcode { + Opname: "half_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_rsqrt = &Opcode { + Opname: "half_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sin = &Opcode { + Opname: "half_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sqrt = &Opcode { + Opname: "half_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_tan = &Opcode { + Opname: "half_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_cos = &Opcode { + Opname: "native_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_divide = &Opcode { + Opname: "native_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp = &Opcode { + Opname: "native_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp2 = &Opcode { + Opname: "native_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp10 = &Opcode { + Opname: "native_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log = &Opcode { + Opname: "native_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log2 = &Opcode { + Opname: "native_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log10 = &Opcode { + Opname: "native_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_powr = &Opcode { + Opname: "native_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_recip = &Opcode { + Opname: "native_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_rsqrt = &Opcode { + Opname: "native_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sin = &Opcode { + Opname: "native_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sqrt = &Opcode { + Opname: "native_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_tan = &Opcode { + Opname: "native_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs = &Opcode { + Opname: "s_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs_diff = &Opcode { + Opname: "s_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_add_sat = &Opcode { + Opname: "s_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_add_sat = &Opcode { + Opname: "u_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_hadd = &Opcode { + Opname: "s_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_hadd = &Opcode { + Opname: "u_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_rhadd = &Opcode { + Opname: "s_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_rhadd = &Opcode { + Opname: "u_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_clamp = &Opcode { + Opname: "s_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_clamp = &Opcode { + Opname: "u_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_clz = &Opcode { + Opname: "clz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ctz = &Opcode { + Opname: "ctz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_hi = &Opcode { + Opname: "s_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_sat = &Opcode { + Opname: "u_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_sat = &Opcode { + Opname: "s_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_max = &Opcode { + Opname: "s_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_max = &Opcode { + Opname: "u_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_min = &Opcode { + Opname: "s_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_min = &Opcode { + Opname: "u_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul_hi = &Opcode { + Opname: "s_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_rotate = &Opcode { + Opname: "rotate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_sub_sat = &Opcode { + Opname: "s_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_sub_sat = &Opcode { + Opname: "u_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_upsample = &Opcode { + Opname: "u_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_upsample = &Opcode { + Opname: "s_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_popcount = &Opcode { + Opname: "popcount", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad24 = &Opcode { + Opname: "s_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad24 = &Opcode { + Opname: "u_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul24 = &Opcode { + Opname: "s_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul24 = &Opcode { + Opname: "u_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs = &Opcode { + Opname: "u_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs_diff = &Opcode { + Opname: "u_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul_hi = &Opcode { + Opname: "u_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_hi = &Opcode { + Opname: "u_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fclamp = &Opcode { + Opname: "fclamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_degrees = &Opcode { + Opname: "degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax_common = &Opcode { + Opname: "fmax_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin_common = &Opcode { + Opname: "fmin_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_mix = &Opcode { + Opname: "mix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + OpenCLStd_radians = &Opcode { + Opname: "radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + OpenCLStd_step = &Opcode { + Opname: "step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_smoothstep = &Opcode { + Opname: "smoothstep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sign = &Opcode { + Opname: "sign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cross = &Opcode { + Opname: "cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_distance = &Opcode { + Opname: "distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_length = &Opcode { + Opname: "length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_normalize = &Opcode { + Opname: "normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_distance = &Opcode { + Opname: "fast_distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_length = &Opcode { + Opname: "fast_length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_normalize = &Opcode { + Opname: "fast_normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_bitselect = &Opcode { + Opname: "bitselect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_select = &Opcode { + Opname: "select", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloadn = &Opcode { + Opname: "vloadn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstoren = &Opcode { + Opname: "vstoren", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_half = &Opcode { + Opname: "vload_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_halfn = &Opcode { + Opname: "vload_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half = &Opcode { + Opname: "vstore_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half_r = &Opcode { + Opname: "vstore_half_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn = &Opcode { + Opname: "vstore_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn_r = &Opcode { + Opname: "vstore_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloada_halfn = &Opcode { + Opname: "vloada_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn = &Opcode { + Opname: "vstorea_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn_r = &Opcode { + Opname: "vstorea_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle = &Opcode { + Opname: "shuffle", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle2 = &Opcode { + Opname: "shuffle2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_printf = &Opcode { + Opname: "printf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'format'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'additional arguments'", + Quantifier: "*", + }, + }, + } + OpenCLStd_prefetch = &Opcode { + Opname: "prefetch", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'num elements'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugInfoNone = &Opcode { + Opname: "DebugInfoNone", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugCompilationUnit = &Opcode { + Opname: "DebugCompilationUnit", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'DWARF Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSourceLanguage, + Name: "'Language'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeBasic = &Opcode { + Opname: "DebugTypeBasic", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugBaseTypeAttributeEncoding, + Name: "'Encoding'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePointer = &Opcode { + Opname: "DebugTypePointer", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "'Storage Class'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeQualifier = &Opcode { + Opname: "DebugTypeQualifier", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugTypeQualifier, + Name: "'Type Qualifier'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeArray = &Opcode { + Opname: "DebugTypeArray", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Counts'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeVector = &Opcode { + Opname: "DebugTypeVector", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Component Count'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypedef = &Opcode { + Opname: "DebugTypedef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeFunction = &Opcode { + Opname: "DebugTypeFunction", + Operands: []Operand { + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Return Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameter Types'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeEnum = &Opcode { + Opname: "DebugTypeEnum", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Underlying Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefIdRef, + Name: "'Value, Name, Value, Name, ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeComposite = &Opcode { + Opname: "DebugTypeComposite", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugCompositeType, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Members'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeMember = &Opcode { + Opname: "DebugTypeMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugTypeInheritance = &Opcode { + Opname: "DebugTypeInheritance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Child'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePtrToMember = &Opcode { + Opname: "DebugTypePtrToMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Member Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplate = &Opcode { + Opname: "DebugTypeTemplate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Actual Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameterPack = &Opcode { + Opname: "DebugTypeTemplateParameterPack", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugGlobalVariable = &Opcode { + Opname: "DebugGlobalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Static Member Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugFunctionDeclaration = &Opcode { + Opname: "DebugFunctionDeclaration", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugFunction = &Opcode { + Opname: "DebugFunction", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Scope Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlock = &Opcode { + Opname: "DebugLexicalBlock", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlockDiscriminator = &Opcode { + Opname: "DebugLexicalBlockDiscriminator", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Discriminator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugScope = &Opcode { + Opname: "DebugScope", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined At'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugNoScope = &Opcode { + Opname: "DebugNoScope", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugInlinedAt = &Opcode { + Opname: "DebugInlinedAt", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLocalVariable = &Opcode { + Opname: "DebugLocalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Arg Number'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugInlinedVariable = &Opcode { + Opname: "DebugInlinedVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugDeclare = &Opcode { + Opname: "DebugDeclare", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugValue = &Opcode { + Opname: "DebugValue", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugOperation = &Opcode { + Opname: "DebugOperation", + Operands: []Operand { + Operand { + Kind: OperandKindDebugOperation, + Name: "'OpCode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugExpression = &Opcode { + Opname: "DebugExpression", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugMacroDef = &Opcode { + Opname: "DebugMacroDef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugMacroUndef = &Opcode { + Opname: "DebugMacroUndef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Macro'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugImportedEntity = &Opcode { + Opname: "DebugImportedEntity", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugImportedEntity, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Entity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugSource = &Opcode { + Opname: "DebugSource", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Text'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugModuleINTEL = &Opcode { + Opname: "DebugModuleINTEL", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ConfigurationMacros'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'IncludePath'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'APINotesFile'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'IsDeclaration'", + Quantifier: "", + }, + }, + } + + + OperandKindImageOperands = &OperandKind { + Kind: "ImageOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Bias", + Value: 0x0001, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Lod", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Grad", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ConstOffset", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Offset", + Value: 0x0010, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ConstOffsets", + Value: 0x0020, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Sample", + Value: 0x0040, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MinLod", + Value: 0x0080, + Capabilities: []string{"MinLod",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MakeTexelAvailable", + Value: 0x0100, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelVisible", + Value: 0x0200, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivateTexel", + Value: 0x0400, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VolatileTexel", + Value: 0x0800, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SignExtend", + Value: 0x1000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "ZeroExtend", + Value: 0x2000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "Nontemporal", + Value: 0x4000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "Offsets", + Value: 0x10000, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPFastMathMode = &OperandKind { + Kind: "FPFastMathMode", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NotNaN", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NotInf", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NSZ", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AllowRecip", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Fast", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AllowContract", + Value: 0x10000, + Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AllowReassoc", + Value: 0x20000, + Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AllowTransform", + Value: 0x40000, + Capabilities: []string{"FloatControls2",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSelectionControl = &OperandKind { + Kind: "SelectionControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Flatten", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DontFlatten", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLoopControl = &OperandKind { + Kind: "LoopControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Unroll", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DontUnroll", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DependencyInfinite", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "DependencyLength", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "MinIterations", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "MaxIterations", + Value: 0x0020, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "IterationMultiple", + Value: 0x0040, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "PeelCount", + Value: 0x0080, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "PartialCount", + Value: 0x0100, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "InitiationIntervalINTEL", + Value: 0x10000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxConcurrencyINTEL", + Value: 0x20000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DependencyArrayINTEL", + Value: 0x40000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PipelineEnableINTEL", + Value: 0x80000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopCoalesceINTEL", + Value: 0x100000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxInterleavingINTEL", + Value: 0x200000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SpeculatedIterationsINTEL", + Value: 0x400000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoFusionINTEL", + Value: 0x800000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopCountINTEL", + Value: 0x1000000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxReinvocationDelayINTEL", + Value: 0x2000000, + Capabilities: []string{"FPGALoopControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFunctionControl = &OperandKind { + Kind: "FunctionControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Inline", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DontInline", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Pure", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Const", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OptNoneEXT", + Value: 0x10000, + Capabilities: []string{"OptNoneEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemorySemantics = &OperandKind { + Kind: "MemorySemantics", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Relaxed", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Acquire", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Release", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AcquireRelease", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SequentiallyConsistent", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UniformMemory", + Value: 0x0040, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupMemory", + Value: 0x0080, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WorkgroupMemory", + Value: 0x0100, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CrossWorkgroupMemory", + Value: 0x0200, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AtomicCounterMemory", + Value: 0x0400, + Capabilities: []string{"AtomicStorage",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageMemory", + Value: 0x0800, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OutputMemory", + Value: 0x1000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeAvailable", + Value: 0x2000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeVisible", + Value: 0x4000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 0x8000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemoryAccess = &OperandKind { + Kind: "MemoryAccess", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Aligned", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Nontemporal", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MakePointerAvailable", + Value: 0x0008, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakePointerVisible", + Value: 0x0010, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivatePointer", + Value: 0x0020, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "AliasScopeINTELMask", + Value: 0x10000, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoAliasINTELMask", + Value: 0x20000, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindKernelProfilingInfo = &OperandKind { + Kind: "KernelProfilingInfo", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CmdExecTime", + Value: 0x0001, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayFlags = &OperandKind { + Kind: "RayFlags", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneKHR", + Value: 0x0000, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OpaqueKHR", + Value: 0x0001, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoOpaqueKHR", + Value: 0x0002, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TerminateOnFirstHitKHR", + Value: 0x0004, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SkipClosestHitShaderKHR", + Value: 0x0008, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullBackFacingTrianglesKHR", + Value: 0x0010, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullFrontFacingTrianglesKHR", + Value: 0x0020, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullOpaqueKHR", + Value: 0x0040, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullNoOpaqueKHR", + Value: 0x0080, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SkipTrianglesKHR", + Value: 0x0100, + Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SkipAABBsKHR", + Value: 0x0200, + Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ForceOpacityMicromap2StateEXT", + Value: 0x0400, + Capabilities: []string{"RayTracingOpacityMicromapEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFragmentShadingRate = &OperandKind { + Kind: "FragmentShadingRate", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Vertical2Pixels", + Value: 0x0001, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Vertical4Pixels", + Value: 0x0002, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Horizontal2Pixels", + Value: 0x0004, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Horizontal4Pixels", + Value: 0x0008, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRawAccessChainOperands = &OperandKind { + Kind: "RawAccessChainOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RobustnessPerComponentNV", + Value: 0x0001, + Capabilities: []string{"RawAccessChainsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RobustnessPerElementNV", + Value: 0x0002, + Capabilities: []string{"RawAccessChainsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSourceLanguage = &OperandKind { + Kind: "SourceLanguage", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unknown", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ESSL", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GLSL", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OpenCL_C", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OpenCL_CPP", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "HLSL", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CPP_for_OpenCL", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SYCL", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "HERO_C", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NZSL", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WGSL", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Slang", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Zig", + Value: 12, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindExecutionModel = &OperandKind { + Kind: "ExecutionModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Vertex", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessellationControl", + Value: 1, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessellationEvaluation", + Value: 2, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Geometry", + Value: 3, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Fragment", + Value: 4, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GLCompute", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Kernel", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TaskNV", + Value: 5267, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshNV", + Value: 5268, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayGenerationKHR", + Value: 5313, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IntersectionKHR", + Value: 5314, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AnyHitKHR", + Value: 5315, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClosestHitKHR", + Value: 5316, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MissKHR", + Value: 5317, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CallableKHR", + Value: 5318, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskEXT", + Value: 5364, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshEXT", + Value: 5365, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindAddressingModel = &OperandKind { + Kind: "AddressingModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Logical", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Physical32", + Value: 1, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Physical64", + Value: 2, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PhysicalStorageBuffer64", + Value: 5348, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemoryModel = &OperandKind { + Kind: "MemoryModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Simple", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GLSL450", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OpenCL", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Vulkan", + Value: 3, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindExecutionMode = &OperandKind { + Kind: "ExecutionMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Invocations", + Value: 0, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Number of <>'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SpacingEqual", + Value: 1, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SpacingFractionalEven", + Value: 2, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SpacingFractionalOdd", + Value: 3, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "VertexOrderCw", + Value: 4, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "VertexOrderCcw", + Value: 5, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PixelCenterInteger", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OriginUpperLeft", + Value: 7, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OriginLowerLeft", + Value: 8, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "EarlyFragmentTests", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PointMode", + Value: 10, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Xfb", + Value: 11, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DepthReplacing", + Value: 12, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DepthGreater", + Value: 14, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DepthLess", + Value: 15, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DepthUnchanged", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LocalSize", + Value: 17, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LocalSizeHint", + Value: 18, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputPoints", + Value: 19, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputLines", + Value: 20, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputLinesAdjacency", + Value: 21, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Triangles", + Value: 22, + Capabilities: []string{"Geometry","Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputTrianglesAdjacency", + Value: 23, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Quads", + Value: 24, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Isolines", + Value: 25, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OutputVertices", + Value: 26, + Capabilities: []string{"Geometry","Tessellation","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Vertex count'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OutputPoints", + Value: 27, + Capabilities: []string{"Geometry","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OutputLineStrip", + Value: 28, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "OutputTriangleStrip", + Value: 29, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "VecTypeHint", + Value: 30, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Vector type'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ContractionOff", + Value: 31, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Initializer", + Value: 33, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "Finalizer", + Value: 34, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupSize", + Value: 35, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Subgroup Size'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupsPerWorkgroup", + Value: 36, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Subgroups Per Workgroup'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupsPerWorkgroupId", + Value: 37, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindIdRef, "'Subgroups Per Workgroup'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "LocalSizeId", + Value: 38, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "LocalSizeHintId", + Value: 39, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindIdRef, "'x size hint'"},{OperandKindIdRef, "'y size hint'"},{OperandKindIdRef, "'z size hint'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "NonCoherentColorAttachmentReadEXT", + Value: 4169, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonCoherentDepthAttachmentReadEXT", + Value: 4170, + Capabilities: []string{"TileImageDepthReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonCoherentStencilAttachmentReadEXT", + Value: 4171, + Capabilities: []string{"TileImageStencilReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupUniformControlFlowKHR", + Value: 4421, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PostDepthCoverage", + Value: 4446, + Capabilities: []string{"SampleMaskPostDepthCoverage",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DenormPreserve", + Value: 4459, + Capabilities: []string{"DenormPreserve",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "DenormFlushToZero", + Value: 4460, + Capabilities: []string{"DenormFlushToZero",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SignedZeroInfNanPreserve", + Value: 4461, + Capabilities: []string{"SignedZeroInfNanPreserve",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTE", + Value: 4462, + Capabilities: []string{"RoundingModeRTE",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTZ", + Value: 4463, + Capabilities: []string{"RoundingModeRTZ",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "EarlyAndLateFragmentTestsAMD", + Value: 5017, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefReplacingEXT", + Value: 5027, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CoalescingAMDX", + Value: 5069, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IsApiEntryAMDX", + Value: 5070, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Is Entry'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxNodeRecursionAMDX", + Value: 5071, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Number of recursions'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StaticNumWorkgroupsAMDX", + Value: 5072, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderIndexAMDX", + Value: 5073, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Shader Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxNumWorkgroupsAMDX", + Value: 5077, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefUnchangedFrontAMD", + Value: 5079, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefGreaterFrontAMD", + Value: 5080, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefLessFrontAMD", + Value: 5081, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefUnchangedBackAMD", + Value: 5082, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefGreaterBackAMD", + Value: 5083, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilRefLessBackAMD", + Value: 5084, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "QuadDerivativesKHR", + Value: 5088, + Capabilities: []string{"QuadControlKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RequireFullQuadsKHR", + Value: 5089, + Capabilities: []string{"QuadControlKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SharesInputWithAMDX", + Value: 5102, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Node Name'"},{OperandKindIdRef, "'Shader Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputLinesEXT", + Value: 5269, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputPrimitivesEXT", + Value: 5270, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Primitive count'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DerivativeGroupQuadsKHR", + Value: 5289, + Capabilities: []string{"ComputeDerivativeGroupQuadsNV","ComputeDerivativeGroupQuadsKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DerivativeGroupLinearKHR", + Value: 5290, + Capabilities: []string{"ComputeDerivativeGroupLinearNV","ComputeDerivativeGroupLinearKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputTrianglesEXT", + Value: 5298, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PixelInterlockOrderedEXT", + Value: 5366, + Capabilities: []string{"FragmentShaderPixelInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PixelInterlockUnorderedEXT", + Value: 5367, + Capabilities: []string{"FragmentShaderPixelInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleInterlockOrderedEXT", + Value: 5368, + Capabilities: []string{"FragmentShaderSampleInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleInterlockUnorderedEXT", + Value: 5369, + Capabilities: []string{"FragmentShaderSampleInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShadingRateInterlockOrderedEXT", + Value: 5370, + Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShadingRateInterlockUnorderedEXT", + Value: 5371, + Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SharedLocalMemorySizeINTEL", + Value: 5618, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RoundingModeRTPINTEL", + Value: 5620, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RoundingModeRTNINTEL", + Value: 5621, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeALTINTEL", + Value: 5622, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeIEEEINTEL", + Value: 5623, + Capabilities: []string{"RoundToInfinityINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxWorkgroupSizeINTEL", + Value: 5893, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'max_x_size'"},{OperandKindLiteralInteger, "'max_y_size'"},{OperandKindLiteralInteger, "'max_z_size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxWorkDimINTEL", + Value: 5894, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'max_dimensions'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoGlobalOffsetINTEL", + Value: 5895, + Capabilities: []string{"KernelAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NumSIMDWorkitemsINTEL", + Value: 5896, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'vector_width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SchedulerTargetFmaxMhzINTEL", + Value: 5903, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'target_fmax'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximallyReconvergesKHR", + Value: 6023, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPFastMathDefault", + Value: 6028, + Capabilities: []string{"FloatControls2",}, + Parameters: []Parameter{{OperandKindIdRef, "'Target Type'"},{OperandKindIdRef, "'Fast-Math Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingInterfaceINTEL", + Value: 6154, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'StallFreeReturn'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterMapInterfaceINTEL", + Value: 6160, + Capabilities: []string{"FPGAKernelAttributesv2INTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'WaitForDoneWrite'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NamedBarrierCountINTEL", + Value: 6417, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Barrier Count'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximumRegistersINTEL", + Value: 6461, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Number of Registers'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaximumRegistersIdINTEL", + Value: 6462, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Number of Registers'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NamedMaximumRegistersINTEL", + Value: 6463, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{{OperandKindNamedMaximumNumberOfRegisters, "'Named Maximum Number of Registers'"},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindStorageClass = &OperandKind { + Kind: "StorageClass", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UniformConstant", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Input", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Uniform", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Output", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Workgroup", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CrossWorkgroup", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Private", + Value: 6, + Capabilities: []string{"Shader","VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Function", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Generic", + Value: 8, + Capabilities: []string{"GenericPointer",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PushConstant", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AtomicCounter", + Value: 10, + Capabilities: []string{"AtomicStorage",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Image", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageBuffer", + Value: 12, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "TileImageEXT", + Value: 4172, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NodePayloadAMDX", + Value: 5068, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CallableDataKHR", + Value: 5328, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingCallableDataKHR", + Value: 5329, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayPayloadKHR", + Value: 5338, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitAttributeKHR", + Value: 5339, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayPayloadKHR", + Value: 5342, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderRecordBufferKHR", + Value: 5343, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PhysicalStorageBuffer", + Value: 5349, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "HitObjectAttributeNV", + Value: 5385, + Capabilities: []string{"ShaderInvocationReorderNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskPayloadWorkgroupEXT", + Value: 5402, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "CodeSectionINTEL", + Value: 5605, + Capabilities: []string{"FunctionPointersINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DeviceOnlyINTEL", + Value: 5936, + Capabilities: []string{"USMStorageClassesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HostOnlyINTEL", + Value: 5937, + Capabilities: []string{"USMStorageClassesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDim = &OperandKind { + Kind: "Dim", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "1D", + Value: 0, + Capabilities: []string{"Sampled1D",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "2D", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "3D", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Cube", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rect", + Value: 4, + Capabilities: []string{"SampledRect",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Buffer", + Value: 5, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubpassData", + Value: 6, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TileImageDataEXT", + Value: 4173, + Capabilities: []string{"TileImageColorReadAccessEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSamplerAddressingMode = &OperandKind { + Kind: "SamplerAddressingMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ClampToEdge", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Clamp", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Repeat", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RepeatMirrored", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSamplerFilterMode = &OperandKind { + Kind: "SamplerFilterMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Nearest", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Linear", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageFormat = &OperandKind { + Kind: "ImageFormat", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unknown", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba32f", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba16f", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R32f", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba8", + Value: 4, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba8Snorm", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg32f", + Value: 6, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg16f", + Value: 7, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R11fG11fB10f", + Value: 8, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R16f", + Value: 9, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba16", + Value: 10, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgb10A2", + Value: 11, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg16", + Value: 12, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg8", + Value: 13, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R16", + Value: 14, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R8", + Value: 15, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba16Snorm", + Value: 16, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg16Snorm", + Value: 17, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg8Snorm", + Value: 18, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R16Snorm", + Value: 19, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R8Snorm", + Value: 20, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba32i", + Value: 21, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba16i", + Value: 22, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba8i", + Value: 23, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R32i", + Value: 24, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg32i", + Value: 25, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg16i", + Value: 26, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg8i", + Value: 27, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R16i", + Value: 28, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R8i", + Value: 29, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba32ui", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba16ui", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgba8ui", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R32ui", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rgb10a2ui", + Value: 34, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg32ui", + Value: 35, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg16ui", + Value: 36, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rg8ui", + Value: 37, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R16ui", + Value: 38, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R8ui", + Value: 39, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R64ui", + Value: 40, + Capabilities: []string{"Int64ImageEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "R64i", + Value: 41, + Capabilities: []string{"Int64ImageEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageChannelOrder = &OperandKind { + Kind: "ImageChannelOrder", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "R", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "A", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RG", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RA", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RGB", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RGBA", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "BGRA", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ARGB", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Intensity", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Luminance", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Rx", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RGx", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RGBx", + Value: 12, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Depth", + Value: 13, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DepthStencil", + Value: 14, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "sRGB", + Value: 15, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "sRGBx", + Value: 16, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "sRGBA", + Value: 17, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "sBGRA", + Value: 18, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ABGR", + Value: 19, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageChannelDataType = &OperandKind { + Kind: "ImageChannelDataType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "SnormInt8", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SnormInt16", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt8", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt16", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormShort565", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormShort555", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt101010", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SignedInt8", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SignedInt16", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SignedInt32", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedInt8", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedInt16", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedInt32", + Value: 12, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "HalfFloat", + Value: 13, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Float", + Value: 14, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt24", + Value: 15, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt101010_2", + Value: 16, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedIntRaw10EXT", + Value: 19, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnsignedIntRaw12EXT", + Value: 20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UnormInt2_101010EXT", + Value: 21, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPRoundingMode = &OperandKind { + Kind: "FPRoundingMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RTE", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RTZ", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RTP", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RTN", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPDenormMode = &OperandKind { + Kind: "FPDenormMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Preserve", + Value: 0, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FlushToZero", + Value: 1, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindQuantizationModes = &OperandKind { + Kind: "QuantizationModes", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "TRN", + Value: 0, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TRN_ZERO", + Value: 1, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND", + Value: 2, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_ZERO", + Value: 3, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_INF", + Value: 4, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_MIN_INF", + Value: 5, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_CONV", + Value: 6, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RND_CONV_ODD", + Value: 7, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPOperationMode = &OperandKind { + Kind: "FPOperationMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "IEEE", + Value: 0, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ALT", + Value: 1, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindOverflowModes = &OperandKind { + Kind: "OverflowModes", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "WRAP", + Value: 0, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT", + Value: 1, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT_ZERO", + Value: 2, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SAT_SYM", + Value: 3, + Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLinkageType = &OperandKind { + Kind: "LinkageType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Export", + Value: 0, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Import", + Value: 1, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LinkOnceODR", + Value: 2, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindAccessQualifier = &OperandKind { + Kind: "AccessQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ReadOnly", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WriteOnly", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ReadWrite", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindHostAccessQualifier = &OperandKind { + Kind: "HostAccessQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneINTEL", + Value: 0, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReadINTEL", + Value: 1, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteINTEL", + Value: 2, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReadWriteINTEL", + Value: 3, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFunctionParameterAttribute = &OperandKind { + Kind: "FunctionParameterAttribute", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Zext", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Sext", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ByVal", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Sret", + Value: 3, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoAlias", + Value: 4, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoCapture", + Value: 5, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoWrite", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoReadWrite", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RuntimeAlignedINTEL", + Value: 5940, + Capabilities: []string{"RuntimeAlignedAttributeINTEL",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDecoration = &OperandKind { + Kind: "Decoration", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RelaxedPrecision", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SpecId", + Value: 1, + Capabilities: []string{"Shader","Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Specialization Constant ID'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Block", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "BufferBlock", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "RowMajor", + Value: 4, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ColMajor", + Value: 5, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ArrayStride", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Array Stride'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MatrixStride", + Value: 7, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Matrix Stride'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GLSLShared", + Value: 8, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GLSLPacked", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CPacked", + Value: 10, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "BuiltIn", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindBuiltIn, ""},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoPerspective", + Value: 13, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Flat", + Value: 14, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Patch", + Value: 15, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Centroid", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Sample", + Value: 17, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Invariant", + Value: 18, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Restrict", + Value: 19, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Aliased", + Value: 20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 21, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Constant", + Value: 22, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Coherent", + Value: 23, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NonWritable", + Value: 24, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NonReadable", + Value: 25, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Uniform", + Value: 26, + Capabilities: []string{"Shader","UniformDecoration",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UniformId", + Value: 27, + Capabilities: []string{"Shader","UniformDecoration",}, + Parameters: []Parameter{{OperandKindIdScope, "'Execution'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SaturatedConversion", + Value: 28, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Stream", + Value: 29, + Capabilities: []string{"GeometryStreams",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Stream Number'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Location", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Location'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Component", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Component'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Index", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Index'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Binding", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Binding Point'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DescriptorSet", + Value: 34, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Descriptor Set'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Offset", + Value: 35, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Byte Offset'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "XfbBuffer", + Value: 36, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Buffer Number'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "XfbStride", + Value: 37, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Stride'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FuncParamAttr", + Value: 38, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindFunctionParameterAttribute, "'Function Parameter Attribute'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FPRoundingMode", + Value: 39, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindFPRoundingMode, "'Floating-Point Rounding Mode'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FPFastMathMode", + Value: 40, + Capabilities: []string{"Kernel","FloatControls2",}, + Parameters: []Parameter{{OperandKindFPFastMathMode, "'Fast-Math Mode'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LinkageAttributes", + Value: 41, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Name'"},{OperandKindLinkageType, "'Linkage Type'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NoContraction", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputAttachmentIndex", + Value: 43, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Attachment Index'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Alignment", + Value: 44, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Alignment'"},}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MaxByteOffset", + Value: 45, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Max Byte Offset'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "AlignmentId", + Value: 46, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindIdRef, "'Alignment'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "MaxByteOffsetId", + Value: 47, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{{OperandKindIdRef, "'Max Byte Offset'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "NoSignedWrap", + Value: 4469, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "NoUnsignedWrap", + Value: 4470, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "WeightTextureQCOM", + Value: 4487, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockMatchTextureQCOM", + Value: 4488, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockMatchSamplerQCOM", + Value: 4499, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ExplicitInterpAMD", + Value: 4999, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NodeSharesPayloadLimitsWithAMDX", + Value: 5019, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Payload Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NodeMaxPayloadsAMDX", + Value: 5020, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Max number of payloads'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "TrackFinishWritingAMDX", + Value: 5078, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeNameAMDX", + Value: 5091, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Node Name'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeBaseIndexAMDX", + Value: 5098, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Base Index'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeSparseArrayAMDX", + Value: 5099, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadNodeArraySizeAMDX", + Value: 5100, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{{OperandKindIdRef, "'Array Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PayloadDispatchIndirectAMDX", + Value: 5105, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OverrideCoverageNV", + Value: 5248, + Capabilities: []string{"SampleMaskOverrideCoverageNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PassthroughNV", + Value: 5250, + Capabilities: []string{"GeometryShaderPassthroughNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportRelativeNV", + Value: 5252, + Capabilities: []string{"ShaderViewportMaskNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryViewportRelativeNV", + Value: 5256, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Offset'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerPrimitiveEXT", + Value: 5271, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerViewNV", + Value: 5272, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerTaskNV", + Value: 5273, + Capabilities: []string{"MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerVertexKHR", + Value: 5285, + Capabilities: []string{"FragmentBarycentricKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonUniform", + Value: 5300, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RestrictPointer", + Value: 5355, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "AliasedPointer", + Value: 5356, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "HitObjectShaderRecordBufferNV", + Value: 5386, + Capabilities: []string{"ShaderInvocationReorderNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessSamplerNV", + Value: 5398, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessImageNV", + Value: 5399, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BoundSamplerNV", + Value: 5400, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BoundImageNV", + Value: 5401, + Capabilities: []string{"BindlessTextureNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SIMTCallINTEL", + Value: 5599, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'N'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReferencedIndirectlyINTEL", + Value: 5602, + Capabilities: []string{"IndirectReferencesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClobberINTEL", + Value: 5607, + Capabilities: []string{"AsmINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Register'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SideEffectsINTEL", + Value: 5608, + Capabilities: []string{"AsmINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeVariableINTEL", + Value: 5624, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FuncParamIOKindINTEL", + Value: 5625, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Kind'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeFunctionINTEL", + Value: 5626, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StackCallINTEL", + Value: 5627, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableOffsetINTEL", + Value: 5628, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Offset'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "CounterBuffer", + Value: 5634, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, "'Counter Buffer'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "UserSemantic", + Value: 5635, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralString, "'Semantic'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "UserTypeGOOGLE", + Value: 5636, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralString, "'User Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionRoundingModeINTEL", + Value: 5822, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPRoundingMode, "'FP Rounding Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionDenormModeINTEL", + Value: 5823, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPDenormMode, "'FP Denorm Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterINTEL", + Value: 5825, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MemoryINTEL", + Value: 5826, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Memory Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NumbanksINTEL", + Value: 5827, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Banks'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BankwidthINTEL", + Value: 5828, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Width'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxPrivateCopiesINTEL", + Value: 5829, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Copies'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SinglepumpINTEL", + Value: 5830, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DoublepumpINTEL", + Value: 5831, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxReplicatesINTEL", + Value: 5832, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Maximum Replicates'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SimpleDualPortINTEL", + Value: 5833, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MergeINTEL", + Value: 5834, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Merge Key'"},{OperandKindLiteralString, "'Merge Type'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BankBitsINTEL", + Value: 5835, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Bank Bits'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ForcePow2DepthINTEL", + Value: 5836, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Force Key'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StridesizeINTEL", + Value: 5883, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Stride Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "WordsizeINTEL", + Value: 5884, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Word Size'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "TrueDualPortINTEL", + Value: 5885, + Capabilities: []string{"FPGAMemoryAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BurstCoalesceINTEL", + Value: 5899, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheSizeINTEL", + Value: 5900, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Size in bytes'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DontStaticallyCoalesceINTEL", + Value: 5901, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrefetchINTEL", + Value: 5902, + Capabilities: []string{"FPGAMemoryAccessesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Prefetcher Size in bytes'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StallEnableINTEL", + Value: 5905, + Capabilities: []string{"FPGAClusterAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FuseLoopsInFunctionINTEL", + Value: 5907, + Capabilities: []string{"LoopFuseINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MathOpDSPModeINTEL", + Value: 5909, + Capabilities: []string{"FPGADSPControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Mode'"},{OperandKindLiteralInteger, "'Propagate'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "AliasScopeINTEL", + Value: 5914, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "NoAliasINTEL", + Value: 5915, + Capabilities: []string{"MemoryAccessAliasingINTEL",}, + Parameters: []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitiationIntervalINTEL", + Value: 5917, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cycles'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaxConcurrencyINTEL", + Value: 5918, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Invocations'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PipelineEnableINTEL", + Value: 5919, + Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Enable'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "BufferLocationINTEL", + Value: 5921, + Capabilities: []string{"FPGABufferLocationINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Buffer Location ID'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "IOPipeStorageINTEL", + Value: 5944, + Capabilities: []string{"IOPipesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'IO Pipe ID'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionFloatingPointModeINTEL", + Value: 6080, + Capabilities: []string{"FunctionFloatControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPOperationMode, "'FP Operation Mode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "SingleElementVectorINTEL", + Value: 6085, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeCallableFunctionINTEL", + Value: 6087, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MediaBlockIOINTEL", + Value: 6140, + Capabilities: []string{"VectorComputeINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StallFreeINTEL", + Value: 6151, + Capabilities: []string{"FPGAClusterAttributesV2INTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPMaxErrorDecorationINTEL", + Value: 6170, + Capabilities: []string{"FPMaxErrorINTEL",}, + Parameters: []Parameter{{OperandKindLiteralFloat, "'Max Error'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LatencyControlLabelINTEL", + Value: 6172, + Capabilities: []string{"FPGALatencyControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Latency Label'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "LatencyControlConstraintINTEL", + Value: 6173, + Capabilities: []string{"FPGALatencyControlINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Relative To'"},{OperandKindLiteralInteger, "'Control Type'"},{OperandKindLiteralInteger, "'Relative Cycle'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ConduitKernelArgumentINTEL", + Value: 6175, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterMapKernelArgumentINTEL", + Value: 6176, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceAddressWidthINTEL", + Value: 6177, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'AddressWidth'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceDataWidthINTEL", + Value: 6178, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'DataWidth'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceLatencyINTEL", + Value: 6179, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Latency'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceReadWriteModeINTEL", + Value: 6180, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindAccessQualifier, "'ReadWriteMode'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceMaxBurstINTEL", + Value: 6181, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'MaxBurstCount'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "MMHostInterfaceWaitRequestINTEL", + Value: 6182, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Waitrequest'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "StableKernelArgumentINTEL", + Value: 6183, + Capabilities: []string{"FPGAArgumentInterfacesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HostAccessINTEL", + Value: 6188, + Capabilities: []string{"GlobalVariableHostAccessINTEL",}, + Parameters: []Parameter{{OperandKindHostAccessQualifier, "'Access'"},{OperandKindLiteralString, "'Name'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitModeINTEL", + Value: 6190, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{{OperandKindInitializationModeQualifier, "'Trigger'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImplementInRegisterMapINTEL", + Value: 6191, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "Value"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlLoadINTEL", + Value: 6442, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindLoadCacheControl, "'Cache Control'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlStoreINTEL", + Value: 6443, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindStoreCacheControl, "'Cache Control'"},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindBuiltIn = &OperandKind { + Kind: "BuiltIn", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Position", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PointSize", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ClipDistance", + Value: 3, + Capabilities: []string{"ClipDistance",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CullDistance", + Value: 4, + Capabilities: []string{"CullDistance",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "VertexId", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InstanceId", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PrimitiveId", + Value: 7, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InvocationId", + Value: 8, + Capabilities: []string{"Geometry","Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Layer", + Value: 9, + Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ViewportIndex", + Value: 10, + Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessLevelOuter", + Value: 11, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessLevelInner", + Value: 12, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessCoord", + Value: 13, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PatchVertices", + Value: 14, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FragCoord", + Value: 15, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "PointCoord", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FrontFacing", + Value: 17, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampleId", + Value: 18, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SamplePosition", + Value: 19, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampleMask", + Value: 20, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "FragDepth", + Value: 22, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "HelperInvocation", + Value: 23, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NumWorkgroups", + Value: 24, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WorkgroupSize", + Value: 25, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WorkgroupId", + Value: 26, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LocalInvocationId", + Value: 27, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GlobalInvocationId", + Value: 28, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LocalInvocationIndex", + Value: 29, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WorkDim", + Value: 30, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GlobalSize", + Value: 31, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "EnqueuedWorkgroupSize", + Value: 32, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GlobalOffset", + Value: 33, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GlobalLinearId", + Value: 34, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupSize", + Value: 36, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupMaxSize", + Value: 37, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NumSubgroups", + Value: 38, + Capabilities: []string{"Kernel","GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "NumEnqueuedSubgroups", + Value: 39, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupId", + Value: 40, + Capabilities: []string{"Kernel","GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupLocalInvocationId", + Value: 41, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "VertexIndex", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InstanceIndex", + Value: 43, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreIDARM", + Value: 4160, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreCountARM", + Value: 4161, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CoreMaxIDARM", + Value: 4162, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WarpIDARM", + Value: 4163, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WarpMaxIDARM", + Value: 4164, + Capabilities: []string{"CoreBuiltinsARM",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupEqMask", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGeMask", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGtMask", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLeMask", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLtMask", + Value: 4420, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "BaseVertex", + Value: 4424, + Capabilities: []string{"DrawParameters",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "BaseInstance", + Value: 4425, + Capabilities: []string{"DrawParameters",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "DrawIndex", + Value: 4426, + Capabilities: []string{"DrawParameters","MeshShadingNV","MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "PrimitiveShadingRateKHR", + Value: 4432, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DeviceIndex", + Value: 4438, + Capabilities: []string{"DeviceGroup",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "ViewIndex", + Value: 4440, + Capabilities: []string{"MultiView",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "ShadingRateKHR", + Value: 4444, + Capabilities: []string{"FragmentShadingRateKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspAMD", + Value: 4992, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspCentroidAMD", + Value: 4993, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspSampleAMD", + Value: 4994, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothAMD", + Value: 4995, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothCentroidAMD", + Value: 4996, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothSampleAMD", + Value: 4997, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordPullModelAMD", + Value: 4998, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragStencilRefEXT", + Value: 5014, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RemainingRecursionLevelsAMDX", + Value: 5021, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderIndexAMDX", + Value: 5073, + Capabilities: []string{"ShaderEnqueueAMDX",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportMaskNV", + Value: 5253, + Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryPositionNV", + Value: 5257, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryViewportMaskNV", + Value: 5258, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PositionPerViewNV", + Value: 5261, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportMaskPerViewNV", + Value: 5262, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FullyCoveredEXT", + Value: 5264, + Capabilities: []string{"FragmentFullyCoveredEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskCountNV", + Value: 5274, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveCountNV", + Value: 5275, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveIndicesNV", + Value: 5276, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClipDistancePerViewNV", + Value: 5277, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullDistancePerViewNV", + Value: 5278, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LayerPerViewNV", + Value: 5279, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshViewCountNV", + Value: 5280, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshViewIndicesNV", + Value: 5281, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordKHR", + Value: 5286, + Capabilities: []string{"FragmentBarycentricKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspKHR", + Value: 5287, + Capabilities: []string{"FragmentBarycentricKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragSizeEXT", + Value: 5292, + Capabilities: []string{"FragmentDensityEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragInvocationCountEXT", + Value: 5293, + Capabilities: []string{"FragmentDensityEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitivePointIndicesEXT", + Value: 5294, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveLineIndicesEXT", + Value: 5295, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveTriangleIndicesEXT", + Value: 5296, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullPrimitiveEXT", + Value: 5299, + Capabilities: []string{"MeshShadingEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchIdKHR", + Value: 5319, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchSizeKHR", + Value: 5320, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayOriginKHR", + Value: 5321, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayDirectionKHR", + Value: 5322, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayOriginKHR", + Value: 5323, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayDirectionKHR", + Value: 5324, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTminKHR", + Value: 5325, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTmaxKHR", + Value: 5326, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InstanceCustomIndexKHR", + Value: 5327, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectToWorldKHR", + Value: 5330, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldToObjectKHR", + Value: 5331, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTNV", + Value: 5332, + Capabilities: []string{"RayTracingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindKHR", + Value: 5333, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CurrentRayTimeNV", + Value: 5334, + Capabilities: []string{"RayTracingMotionBlurNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTriangleVertexPositionsKHR", + Value: 5335, + Capabilities: []string{"RayTracingPositionFetchKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitMicroTriangleVertexPositionsNV", + Value: 5337, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitMicroTriangleVertexBarycentricsNV", + Value: 5344, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayFlagsKHR", + Value: 5351, + Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayGeometryIndexKHR", + Value: 5352, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WarpsPerSMNV", + Value: 5374, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SMCountNV", + Value: 5375, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WarpIDNV", + Value: 5376, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SMIDNV", + Value: 5377, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindFrontFacingMicroTriangleNV", + Value: 5405, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindBackFacingMicroTriangleNV", + Value: 5406, + Capabilities: []string{"RayTracingDisplacementMicromapNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullMaskKHR", + Value: 6021, + Capabilities: []string{"RayCullMaskKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindScope = &OperandKind { + Kind: "Scope", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "CrossDevice", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Device", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Workgroup", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Subgroup", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Invocation", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "QueueFamily", + Value: 5, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ShaderCallKHR", + Value: 6, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindGroupOperation = &OperandKind { + Kind: "GroupOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Reduce", + Value: 0, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InclusiveScan", + Value: 1, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ExclusiveScan", + Value: 2, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ClusteredReduce", + Value: 3, + Capabilities: []string{"GroupNonUniformClustered",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "PartitionedReduceNV", + Value: 6, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PartitionedInclusiveScanNV", + Value: 7, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PartitionedExclusiveScanNV", + Value: 8, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindKernelEnqueueFlags = &OperandKind { + Kind: "KernelEnqueueFlags", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoWait", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WaitKernel", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "WaitWorkGroup", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCapability = &OperandKind { + Kind: "Capability", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Matrix", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Shader", + Value: 1, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Geometry", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Tessellation", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Addresses", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Linkage", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Kernel", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Vector16", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Float16Buffer", + Value: 8, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Float16", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Float64", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Int64", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Int64Atomics", + Value: 12, + Capabilities: []string{"Int64",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageBasic", + Value: 13, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageReadWrite", + Value: 14, + Capabilities: []string{"ImageBasic",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageMipmap", + Value: 15, + Capabilities: []string{"ImageBasic",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Pipes", + Value: 17, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Groups", + Value: 18, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DeviceEnqueue", + Value: 19, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "LiteralSampler", + Value: 20, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "AtomicStorage", + Value: 21, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Int16", + Value: 22, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TessellationPointSize", + Value: 23, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GeometryPointSize", + Value: 24, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageGatherExtended", + Value: 25, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageImageMultisample", + Value: 27, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "UniformBufferArrayDynamicIndexing", + Value: 28, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampledImageArrayDynamicIndexing", + Value: 29, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageBufferArrayDynamicIndexing", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageImageArrayDynamicIndexing", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ClipDistance", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "CullDistance", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageCubeArray", + Value: 34, + Capabilities: []string{"SampledCubeArray",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampleRateShading", + Value: 35, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageRect", + Value: 36, + Capabilities: []string{"SampledRect",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampledRect", + Value: 37, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GenericPointer", + Value: 38, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Int8", + Value: 39, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InputAttachment", + Value: 40, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SparseResidency", + Value: 41, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MinLod", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Sampled1D", + Value: 43, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "Image1D", + Value: 44, + Capabilities: []string{"Sampled1D",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampledCubeArray", + Value: 45, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SampledBuffer", + Value: 46, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageBuffer", + Value: 47, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageMSArray", + Value: 48, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageImageExtendedFormats", + Value: 49, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "ImageQuery", + Value: 50, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "DerivativeControl", + Value: 51, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "InterpolationFunction", + Value: 52, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "TransformFeedback", + Value: 53, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "GeometryStreams", + Value: 54, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageImageReadWithoutFormat", + Value: 55, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "StorageImageWriteWithoutFormat", + Value: 56, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "MultiViewport", + Value: 57, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "1.0", + }, + Enumerant{ + Enumerant: "SubgroupDispatch", + Value: 58, + Capabilities: []string{"DeviceEnqueue",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "NamedBarrier", + Value: 59, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "PipeStorage", + Value: 60, + Capabilities: []string{"Pipes",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "GroupNonUniform", + Value: 61, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformVote", + Value: 62, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformArithmetic", + Value: 63, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformBallot", + Value: 64, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformShuffle", + Value: 65, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformShuffleRelative", + Value: 66, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformClustered", + Value: 67, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformQuad", + Value: 68, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "ShaderLayer", + Value: 69, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ShaderViewportIndex", + Value: 70, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformDecoration", + Value: 71, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "CoreBuiltinsARM", + Value: 4165, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageColorReadAccessEXT", + Value: 4166, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageDepthReadAccessEXT", + Value: 4167, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TileImageStencilReadAccessEXT", + Value: 4168, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixLayoutsARM", + Value: 4201, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShadingRateKHR", + Value: 4422, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupBallotKHR", + Value: 4423, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DrawParameters", + Value: 4427, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayoutKHR", + Value: 4428, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayout8BitAccessKHR", + Value: 4429, + Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorkgroupMemoryExplicitLayout16BitAccessKHR", + Value: 4430, + Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupVoteKHR", + Value: 4431, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StorageBuffer16BitAccess", + Value: 4433, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "UniformAndStorageBuffer16BitAccess", + Value: 4434, + Capabilities: []string{"StorageBuffer16BitAccess",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StoragePushConstant16", + Value: 4435, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StorageInputOutput16", + Value: 4436, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "DeviceGroup", + Value: 4437, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "MultiView", + Value: 4439, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "VariablePointersStorageBuffer", + Value: 4441, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "VariablePointers", + Value: 4442, + Capabilities: []string{"VariablePointersStorageBuffer",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "AtomicStorageOps", + Value: 4445, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleMaskPostDepthCoverage", + Value: 4447, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StorageBuffer8BitAccess", + Value: 4448, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformAndStorageBuffer8BitAccess", + Value: 4449, + Capabilities: []string{"StorageBuffer8BitAccess",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StoragePushConstant8", + Value: 4450, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "DenormPreserve", + Value: 4464, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "DenormFlushToZero", + Value: 4465, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SignedZeroInfNanPreserve", + Value: 4466, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTE", + Value: 4467, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTZ", + Value: 4468, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RayQueryProvisionalKHR", + Value: 4471, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryKHR", + Value: 4472, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "UntypedPointersKHR", + Value: 4473, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTraversalPrimitiveCullingKHR", + Value: 4478, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingKHR", + Value: 4479, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureSampleWeightedQCOM", + Value: 4484, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBoxFilterQCOM", + Value: 4485, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBlockMatchQCOM", + Value: 4486, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TextureBlockMatch2QCOM", + Value: 4498, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Float16ImageAMD", + Value: 5008, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageGatherBiasLodAMD", + Value: 5009, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentMaskAMD", + Value: 5010, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilExportEXT", + Value: 5013, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageReadWriteLodAMD", + Value: 5015, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Int64ImageEXT", + Value: 5016, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderClockKHR", + Value: 5055, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderEnqueueAMDX", + Value: 5067, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "QuadControlKHR", + Value: 5087, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleMaskOverrideCoverageNV", + Value: 5249, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GeometryShaderPassthroughNV", + Value: 5251, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderViewportIndexLayerEXT", + Value: 5254, + Capabilities: []string{"MultiViewport",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderViewportMaskNV", + Value: 5255, + Capabilities: []string{"ShaderViewportIndexLayerEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderStereoViewNV", + Value: 5259, + Capabilities: []string{"ShaderViewportMaskNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerViewAttributesNV", + Value: 5260, + Capabilities: []string{"MultiView",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentFullyCoveredEXT", + Value: 5265, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshShadingNV", + Value: 5266, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageFootprintNV", + Value: 5282, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshShadingEXT", + Value: 5283, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentBarycentricKHR", + Value: 5284, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ComputeDerivativeGroupQuadsKHR", + Value: 5288, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentDensityEXT", + Value: 5291, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupNonUniformPartitionedNV", + Value: 5297, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderNonUniform", + Value: 5301, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RuntimeDescriptorArray", + Value: 5302, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayDynamicIndexing", + Value: 5303, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayDynamicIndexing", + Value: 5304, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayDynamicIndexing", + Value: 5305, + Capabilities: []string{"ImageBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformBufferArrayNonUniformIndexing", + Value: 5306, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SampledImageArrayNonUniformIndexing", + Value: 5307, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageBufferArrayNonUniformIndexing", + Value: 5308, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageImageArrayNonUniformIndexing", + Value: 5309, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayNonUniformIndexing", + Value: 5310, + Capabilities: []string{"InputAttachment","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayNonUniformIndexing", + Value: 5311, + Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayNonUniformIndexing", + Value: 5312, + Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RayTracingPositionFetchKHR", + Value: 5336, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingNV", + Value: 5340, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingMotionBlurNV", + Value: 5341, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VulkanMemoryModel", + Value: 5345, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VulkanMemoryModelDeviceScope", + Value: 5346, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "PhysicalStorageBufferAddresses", + Value: 5347, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ComputeDerivativeGroupLinearKHR", + Value: 5350, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingProvisionalKHR", + Value: 5353, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixNV", + Value: 5357, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderSampleInterlockEXT", + Value: 5363, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderShadingRateInterlockEXT", + Value: 5372, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderSMBuiltinsNV", + Value: 5373, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderPixelInterlockEXT", + Value: 5378, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DemoteToHelperInvocation", + Value: 5379, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DisplacementMicromapNV", + Value: 5380, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingOpacityMicromapEXT", + Value: 5381, + Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderInvocationReorderNV", + Value: 5383, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BindlessTextureNV", + Value: 5390, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryPositionFetchKHR", + Value: 5391, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16VectorNV", + Value: 5404, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingDisplacementMicromapNV", + Value: 5409, + Capabilities: []string{"RayTracingKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RawAccessChainsNV", + Value: 5414, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixReductionsNV", + Value: 5430, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixConversionsNV", + Value: 5431, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixPerElementOperationsNV", + Value: 5432, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixTensorAddressingNV", + Value: 5433, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixBlockLoadsNV", + Value: 5434, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TensorAddressingNV", + Value: 5439, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupShuffleINTEL", + Value: 5568, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupBufferBlockIOINTEL", + Value: 5569, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupImageBlockIOINTEL", + Value: 5570, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupImageMediaBlockIOINTEL", + Value: 5579, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RoundToInfinityINTEL", + Value: 5582, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatingPointModeINTEL", + Value: 5583, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IntegerFunctions2INTEL", + Value: 5584, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionPointersINTEL", + Value: 5603, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IndirectReferencesINTEL", + Value: 5604, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AsmINTEL", + Value: 5606, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat32MinMaxEXT", + Value: 5612, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat64MinMaxEXT", + Value: 5613, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16MinMaxEXT", + Value: 5616, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorComputeINTEL", + Value: 5617, + Capabilities: []string{"VectorAnyINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VectorAnyINTEL", + Value: 5619, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ExpectAssumeKHR", + Value: 5629, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationINTEL", + Value: 5696, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationIntraINTEL", + Value: 5697, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationChromaINTEL", + Value: 5698, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VariableLengthArrayINTEL", + Value: 5817, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FunctionFloatControlINTEL", + Value: 5821, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAMemoryAttributesINTEL", + Value: 5824, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPFastMathModeINTEL", + Value: 5837, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionIntegersINTEL", + Value: 5844, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionFloatingPointINTEL", + Value: 5845, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "UnstructuredLoopControlsINTEL", + Value: 5886, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGALoopControlsINTEL", + Value: 5888, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "KernelAttributesINTEL", + Value: 5892, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAKernelAttributesINTEL", + Value: 5897, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAMemoryAccessesINTEL", + Value: 5898, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAClusterAttributesINTEL", + Value: 5904, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LoopFuseINTEL", + Value: 5906, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGADSPControlINTEL", + Value: 5908, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MemoryAccessAliasingINTEL", + Value: 5910, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAInvocationPipeliningAttributesINTEL", + Value: 5916, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGABufferLocationINTEL", + Value: 5920, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArbitraryPrecisionFixedPointINTEL", + Value: 5922, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "USMStorageClassesINTEL", + Value: 5935, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RuntimeAlignedAttributeINTEL", + Value: 5939, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IOPipesINTEL", + Value: 5943, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BlockingPipesINTEL", + Value: 5945, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGARegINTEL", + Value: 5948, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DotProductInputAll", + Value: 6016, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProductInput4x8Bit", + Value: 6017, + Capabilities: []string{"Int8",}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProductInput4x8BitPacked", + Value: 6018, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "DotProduct", + Value: 6019, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + Enumerant{ + Enumerant: "RayCullMaskKHR", + Value: 6020, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixKHR", + Value: 6022, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ReplicatedCompositesEXT", + Value: 6024, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BitInstructions", + Value: 6025, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupNonUniformRotateKHR", + Value: 6026, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FloatControls2", + Value: 6029, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat32AddEXT", + Value: 6033, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat64AddEXT", + Value: 6034, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LongCompositesINTEL", + Value: 6089, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OptNoneEXT", + Value: 6094, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AtomicFloat16AddEXT", + Value: 6095, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DebugInfoModuleINTEL", + Value: 6114, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BFloat16ConversionINTEL", + Value: 6115, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SplitBarrierINTEL", + Value: 6141, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ArithmeticFenceEXT", + Value: 6144, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAClusterAttributesV2INTEL", + Value: 6150, + Capabilities: []string{"FPGAClusterAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAKernelAttributesv2INTEL", + Value: 6161, + Capabilities: []string{"FPGAKernelAttributesINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPMaxErrorINTEL", + Value: 6169, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGALatencyControlINTEL", + Value: 6171, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FPGAArgumentInterfacesINTEL", + Value: 6174, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableHostAccessINTEL", + Value: 6187, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GlobalVariableFPGADecorationsINTEL", + Value: 6189, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupBufferPrefetchINTEL", + Value: 6220, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupUniformArithmeticKHR", + Value: 6400, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MaskedGatherScatterINTEL", + Value: 6427, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CacheControlsINTEL", + Value: 6441, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RegisterLimitsINTEL", + Value: 6460, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryIntersection = &OperandKind { + Kind: "RayQueryIntersection", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionKHR", + Value: 0, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionKHR", + Value: 1, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryCommittedIntersectionType = &OperandKind { + Kind: "RayQueryCommittedIntersectionType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionNoneKHR", + Value: 0, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionTriangleKHR", + Value: 1, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionGeneratedKHR", + Value: 2, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryCandidateIntersectionType = &OperandKind { + Kind: "RayQueryCandidateIntersectionType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionTriangleKHR", + Value: 0, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionAABBKHR", + Value: 1, + Capabilities: []string{"RayQueryKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindPackedVectorFormat = &OperandKind { + Kind: "PackedVectorFormat", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "PackedVectorFormat4x8Bit", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.6", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixOperands = &OperandKind { + Kind: "CooperativeMatrixOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneKHR", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixASignedComponentsKHR", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixBSignedComponentsKHR", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixCSignedComponentsKHR", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixResultSignedComponentsKHR", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SaturatingAccumulationKHR", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixLayout = &OperandKind { + Kind: "CooperativeMatrixLayout", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RowMajorKHR", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ColumnMajorKHR", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RowBlockedInterleavedARM", + Value: 4202, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ColumnBlockedInterleavedARM", + Value: 4203, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixUse = &OperandKind { + Kind: "CooperativeMatrixUse", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "MatrixAKHR", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixBKHR", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MatrixAccumulatorKHR", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCooperativeMatrixReduce = &OperandKind { + Kind: "CooperativeMatrixReduce", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Row", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Column", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "2x2", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindTensorClampMode = &OperandKind { + Kind: "TensorClampMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Undefined", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Constant", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClampToEdge", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Repeat", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RepeatMirrored", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindTensorAddressingOperands = &OperandKind { + Kind: "TensorAddressingOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TensorView", + Value: 0x0001, + Capabilities: []string{"CooperativeMatrixTensorAddressingNV",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DecodeFunc", + Value: 0x0002, + Capabilities: []string{"CooperativeMatrixBlockLoadsNV",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindInitializationModeQualifier = &OperandKind { + Kind: "InitializationModeQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "InitOnDeviceReprogramINTEL", + Value: 0, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InitOnDeviceResetINTEL", + Value: 1, + Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLoadCacheControl = &OperandKind { + Kind: "LoadCacheControl", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UncachedINTEL", + Value: 0, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CachedINTEL", + Value: 1, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingINTEL", + Value: 2, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InvalidateAfterReadINTEL", + Value: 3, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ConstCachedINTEL", + Value: 4, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindStoreCacheControl = &OperandKind { + Kind: "StoreCacheControl", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UncachedINTEL", + Value: 0, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteThroughINTEL", + Value: 1, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WriteBackINTEL", + Value: 2, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StreamingINTEL", + Value: 3, + Capabilities: []string{"CacheControlsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindNamedMaximumNumberOfRegisters = &OperandKind { + Kind: "NamedMaximumNumberOfRegisters", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "AutoINTEL", + Value: 0, + Capabilities: []string{"RegisterLimitsINTEL",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPEncoding = &OperandKind { + Kind: "FPEncoding", + Category: "ValueEnum", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdResultType = &OperandKind { + Kind: "IdResultType", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdResult = &OperandKind { + Kind: "IdResult", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdMemorySemantics = &OperandKind { + Kind: "IdMemorySemantics", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdScope = &OperandKind { + Kind: "IdScope", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdRef = &OperandKind { + Kind: "IdRef", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralInteger = &OperandKind { + Kind: "LiteralInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralString = &OperandKind { + Kind: "LiteralString", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralFloat = &OperandKind { + Kind: "LiteralFloat", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralContextDependentNumber = &OperandKind { + Kind: "LiteralContextDependentNumber", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralExtInstInteger = &OperandKind { + Kind: "LiteralExtInstInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralSpecConstantOpInteger = &OperandKind { + Kind: "LiteralSpecConstantOpInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindPairLiteralIntegerIdRef = &OperandKind { + Kind: "PairLiteralIntegerIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindLiteralInteger,OperandKindIdRef,}, + } + OperandKindPairIdRefLiteralInteger = &OperandKind { + Kind: "PairIdRefLiteralInteger", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindLiteralInteger,}, + } + OperandKindPairIdRefIdRef = &OperandKind { + Kind: "PairIdRefIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindIdRef,}, + } + OperandKindDebugInfoFlags = &OperandKind { + Kind: "DebugInfoFlags", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsProtected", + Value: 0x01, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsPrivate", + Value: 0x02, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsPublic", + Value: 0x03, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsLocal", + Value: 0x04, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsDefinition", + Value: 0x08, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagFwdDecl", + Value: 0x10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagArtificial", + Value: 0x20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagExplicit", + Value: 0x40, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagPrototyped", + Value: 0x80, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagObjectPointer", + Value: 0x100, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagStaticMember", + Value: 0x200, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIndirectVariable", + Value: 0x400, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagLValueReference", + Value: 0x800, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagRValueReference", + Value: 0x1000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsOptimized", + Value: 0x2000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsEnumClass", + Value: 0x4000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagTypePassByValue", + Value: 0x8000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagTypePassByReference", + Value: 0x10000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugBaseTypeAttributeEncoding = &OperandKind { + Kind: "DebugBaseTypeAttributeEncoding", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unspecified", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Address", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Boolean", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Signed", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SignedChar", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Unsigned", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnsignedChar", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugCompositeType = &OperandKind { + Kind: "DebugCompositeType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Class", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Structure", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Union", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugTypeQualifier = &OperandKind { + Kind: "DebugTypeQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ConstType", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VolatileType", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RestrictType", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AtomicType", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugOperation = &OperandKind { + Kind: "DebugOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Deref", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Plus", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Minus", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PlusUconst", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "BitPiece", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Swap", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Xderef", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StackValue", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Constu", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Fragment", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugImportedEntity = &OperandKind { + Kind: "DebugImportedEntity", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ImportedModule", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImportedDeclaration", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + +) diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go.tmpl b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go.tmpl new file mode 100644 index 00000000..987fcd06 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/schema/schema.go.tmpl @@ -0,0 +1,156 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated by {{GenerateArguments}} +// Do not modify this file directly. + +package schema + +// Opcode holds information about a specific SPIR-V opcode. +type Opcode struct { + Opname string + Class string + Opcode int + Operands []Operand +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind *OperandKind + Name string + Quantifier Quantifier +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category OperandCategory + Kind string + Enumerants []Enumerant + Bases []*OperandKind +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string + Value interface{} + Capabilities []string + Parameters []Parameter + Version string +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind *OperandKind + Name string +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) + +// OperandCategory is an enumerator that groups operand kinds. +type OperandCategory string + +const ( + // OperandCategoryBitEnum describes an operand kind where its value is a + // mask, which is formed by combining the bits specified as enumerants in an + // enum. + OperandCategoryBitEnum = "BitEnum" + + // OperandCategoryValueEnum describes an operand kind where its value is an + // enumerant from an enum. + OperandCategoryValueEnum = "ValueEnum" + + // OperandCategoryID describes and operand kind where its value is an + // definition or reference. + OperandCategoryID = "Id" + + // OperandCategoryLiteral describes and operand kind where its value is an + // literal number or string. + OperandCategoryLiteral = "Literal" + + // OperandCategoryComposite describes and operand kind where its value is + // composed from operand values from the above categories. + OperandCategoryComposite = "Composite" +) + +// OpcodeMap is a map of opcode name to Opcode type. +type OpcodeMap map[string]*Opcode + +var ( + // Opcodes is a map of opcode name to Opcode description. + Opcodes = OpcodeMap {•{{range $i := .SPIRV.Instructions}} + "{{$i.Opname}}": {{Title $i.Opname}},{{end}} + } + + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap {•{{range $ext := .Extensions}} + "{{$ext.Name}}": {•{{range $i := $ext.Instructions}} + "{{$i.Opname}}": {{Global $ext.Name}}_{{$i.Opname}},{{end}} + },{{end}} + } + +{{range $i := .SPIRV.Instructions}} {{Title $i.Opname}} = &Opcode { + Opname: "{{$i.Opname}}", + Class: "{{$i.Class}}", + Opcode: {{$i.Opcode}}, + Operands: []Operand {•{{range $i := $i.Operands}} + Operand { + Kind: OperandKind{{$i.Kind}}, + Name: "{{Replace $i.Name "\n" " "}}", + Quantifier: "{{$i.Quantifier}}", + }, {{end}} + }, + } +{{end}} +{{range $ext := .Extensions}}{{range $i := $ext.Instructions}} {{Global $ext.Name}}_{{$i.Opname}} = &Opcode { + Opname: "{{$i.Opname}}", + Operands: []Operand {•{{range $i := $i.Operands}} + Operand { + Kind: OperandKind{{$i.Kind}}, + Name: "{{Replace $i.Name "\n" " "}}", + Quantifier: "{{$i.Quantifier}}", + }, {{end}} + }, + } +{{end}}{{end}} + +{{range $o := .All.OperandKinds}} OperandKind{{$o.Kind}} = &OperandKind { + Kind: "{{$o.Kind}}", + Category: "{{$o.Category}}", + Enumerants: []Enumerant {•{{range $e := $o.Enumerants}} + Enumerant{ + Enumerant: "{{$e.Enumerant}}", + Value: {{$e.Value}}, + Capabilities: []string{•{{range $c := $e.Capabilities}}"{{$c}}",{{end}}•}, + Parameters: []Parameter{•{{range $p := $e.Parameters}}{•OperandKind{{$p.Kind}}, "{{$p.Name}}"•},{{end}}•}, + Version: "{{$e.Version}}", + },{{end}} + }, + Bases: []*OperandKind {•{{range $b := $o.Bases}}OperandKind{{$b}},{{end}}•}, + } +{{end}} +) diff --git a/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/tools/gen-grammar.go b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/tools/gen-grammar.go new file mode 100644 index 00000000..a1289ef9 --- /dev/null +++ b/engine/engine/3rdlibs/spirv-tools/utils/vscode/src/tools/gen-grammar.go @@ -0,0 +1,239 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// gen-grammar generates the spirv.json grammar file from the official SPIR-V +// grammar JSON file. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "text/template" + + "github.com/pkg/errors" + + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/grammar" +) + +type grammarDefinition struct { + name string + url string +} + +var ( + spirvGrammar = grammarDefinition{ + name: "SPIR-V", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json", + } + + extensionGrammars = []grammarDefinition{ + { + name: "GLSL.std.450", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.glsl.std.450.grammar.json", + }, { + name: "OpenCL.std", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json", + }, { + name: "OpenCL.DebugInfo.100", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json", + }, + } + + templatePath = flag.String("template", "", "Path to input template file (required)") + outputPath = flag.String("out", "", "Path to output generated file (required)") + cachePath = flag.String("cache", "", "Cache directory for downloaded files (optional)") + + thisDir = func() string { + _, file, _, _ := runtime.Caller(1) + return filepath.Dir(file) + }() +) + +func main() { + flag.Parse() + if *templatePath == "" || *outputPath == "" { + flag.Usage() + os.Exit(1) + } + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + tf, err := ioutil.ReadFile(*templatePath) + if err != nil { + return errors.Wrap(err, "Could not open template file") + } + + type extension struct { + grammar.Root + Name string + } + + args := struct { + SPIRV grammar.Root + Extensions []extension + All grammar.Root // Combination of SPIRV + Extensions + }{} + + if args.SPIRV, err = parseGrammar(spirvGrammar); err != nil { + return errors.Wrap(err, "Failed to parse SPIR-V grammar file") + } + args.All.Instructions = append(args.All.Instructions, args.SPIRV.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, args.SPIRV.OperandKinds...) + + for _, ext := range extensionGrammars { + root, err := parseGrammar(ext) + if err != nil { + return errors.Wrap(err, "Failed to parse extension grammar file: "+ext.name) + } + args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name}) + args.All.Instructions = append(args.All.Instructions, root.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, root.OperandKinds...) + } + + t, err := template.New("tmpl"). + Funcs(template.FuncMap{ + "GenerateArguments": func() string { + relPath := func(path string) string { + rel, err := filepath.Rel(thisDir, path) + if err != nil { + return path + } + return rel + } + escape := func(str string) string { + return strings.ReplaceAll(str, `\`, `/`) + } + args := []string{ + "--template=" + escape(relPath(*templatePath)), + "--out=" + escape(relPath(*outputPath)), + } + return "gen-grammar.go " + strings.Join(args, " ") + }, + "OperandKindsMatch": func(k grammar.OperandKind) string { + sb := strings.Builder{} + for i, e := range k.Enumerants { + if i > 0 { + sb.WriteString("|") + } + sb.WriteString(e.Enumerant) + } + return sb.String() + }, + "AllExtOpcodes": func() string { + sb := strings.Builder{} + for _, ext := range args.Extensions { + for _, inst := range ext.Root.Instructions { + if sb.Len() > 0 { + sb.WriteString("|") + } + sb.WriteString(inst.Opname) + } + } + return sb.String() + }, + "Title": strings.Title, + "Replace": strings.ReplaceAll, + "Global": func(s string) string { + return strings.ReplaceAll(strings.Title(s), ".", "") + }, + }).Parse(string(tf)) + if err != nil { + return errors.Wrap(err, "Failed to parse template") + } + + buf := bytes.Buffer{} + if err := t.Execute(&buf, args); err != nil { + return errors.Wrap(err, "Failed to execute template") + } + + out := buf.String() + out = strings.ReplaceAll(out, "•", "") + + if err := ioutil.WriteFile(*outputPath, []byte(out), 0777); err != nil { + return errors.Wrap(err, "Failed to write output file") + } + + return nil +} + +// parseGrammar downloads (or loads from the cache) the grammar file and returns +// the parsed grammar.Root. +func parseGrammar(def grammarDefinition) (grammar.Root, error) { + file, err := getOrDownload(def.name, def.url) + if err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to load grammar file") + } + + g := grammar.Root{} + if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to parse grammar file") + } + + return g, nil +} + +// getOrDownload loads the specific file from the cache, or downloads the file +// from the given url. +func getOrDownload(name, url string) ([]byte, error) { + if *cachePath != "" { + if err := os.MkdirAll(*cachePath, 0777); err == nil { + path := filepath.Join(*cachePath, name) + if isFile(path) { + return ioutil.ReadFile(path) + } + } + } + resp, err := http.Get(url) + if err != nil { + return nil, err + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if *cachePath != "" { + ioutil.WriteFile(filepath.Join(*cachePath, name), data, 0777) + } + return data, nil +} + +// isFile returns true if path is a file. +func isFile(path string) bool { + s, err := os.Stat(path) + if err != nil { + return false + } + return !s.IsDir() +} + +// isDir returns true if path is a directory. +func isDir(path string) bool { + s, err := os.Stat(path) + if err != nil { + return false + } + return s.IsDir() +} diff --git a/engine/engine/CMakeLists.txt b/engine/engine/CMakeLists.txt index bcc5e605..27402e35 100644 --- a/engine/engine/CMakeLists.txt +++ b/engine/engine/CMakeLists.txt @@ -34,17 +34,23 @@ file(GLOB_RECURSE ENGINE_SHADER_FILES assets/shaders/*.frag assets/shaders/*.ver file(GLOB_RECURSE ENGINE_MODEL_FILES assets/models/*) set(ENGINE_GENERATE_FILES ${NICKEL_REFL_GENERATE_DIR}/refl_generate.cpp ${NICKEL_BINDING_GENERATE_DIR}/script_binding.cpp) -compile_shader(assets/shaders/shader_pbr.vert assets/shaders/shader_pbr.vert.spv) -compile_shader(assets/shaders/shader_pbr.frag assets/shaders/shader_pbr.frag.spv) -compile_shader(assets/shaders/shader_gridline.vert assets/shaders/shader_gridline.vert.spv) -compile_shader(assets/shaders/shader_gridline.frag assets/shaders/shader_gridline.frag.spv) +set(ENGINE_SHADER_COMPILED_FILES ${ENGINE_SHADER_FILES}) +list(TRANSFORM ENGINE_SHADER_COMPILED_FILES APPEND ".spv") +compile_shader("${ENGINE_SHADER_FILES}") macro(attach_common_config_on_engine target_name) target_sources(${target_name} PRIVATE ${ENGINE_ALL_HEADERS} ${ENGINE_SRC} ${ENGINE_MODEL_FILES} ${ENGINE_SHADER_FILES} ${ENGINE_GENERATE_FILES}) + if (GLSLC_PROG) + target_sources(${target_name} PRIVATE ${ENGINE_SHADER_COMPILED_FILES}) + endif() target_include_directories(${target_name} PUBLIC .) target_include_directories(${target_name} PRIVATE ${NICKEL_GENERATE_INCLUDE_DIR}) target_compile_features(${target_name} PUBLIC cxx_std_20) target_link_libraries(${target_name} PRIVATE SDL3::SDL3 volk::volk stb_image tinygltf tomlplusplus::tomlplusplus qjs + # glslang & spriv + glslang::glslang + glslang::glslang-default-resource-limits + # PhysX PhysXExtensions PhysX @@ -107,6 +113,4 @@ else() message(FATAL_ERROR "must config engine build type(NICKEL_BUILD_SHARED/NICKEL_BUILD_STATIC)") endif() -add_subdirectory(main_entry) - -find_package(Python3 COMPONENTS Interpreter REQUIRED) \ No newline at end of file +add_subdirectory(main_entry) \ No newline at end of file diff --git a/engine/engine/nickel/context.hpp b/engine/engine/nickel/context.hpp index cab694ae..395296db 100644 --- a/engine/engine/nickel/context.hpp +++ b/engine/engine/nickel/context.hpp @@ -6,6 +6,7 @@ #include "nickel/graphics/camera.hpp" #include "nickel/graphics/context.hpp" #include "nickel/graphics/lowlevel/adapter.hpp" +#include "nickel/graphics/lowlevel/shader_compiler.hpp" #include "nickel/graphics/texture_manager.hpp" #include "nickel/input/device/device_manager.hpp" #include "nickel/time/time.hpp" diff --git a/engine/engine/nickel/graphics/lowlevel/internal/shader_compiler_impl.hpp b/engine/engine/nickel/graphics/lowlevel/internal/shader_compiler_impl.hpp new file mode 100644 index 00000000..d62df2b4 --- /dev/null +++ b/engine/engine/nickel/graphics/lowlevel/internal/shader_compiler_impl.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "nickel/graphics/lowlevel/shader_compiler.hpp" + +namespace nickel::graphics { + +class ShaderCompilerImpl { +public: + SpirVBinary Compile(std::span code, ShaderType); +}; + +} // namespace nickel::graphics \ No newline at end of file diff --git a/engine/engine/nickel/graphics/lowlevel/shader_compiler.hpp b/engine/engine/nickel/graphics/lowlevel/shader_compiler.hpp new file mode 100644 index 00000000..9b652255 --- /dev/null +++ b/engine/engine/nickel/graphics/lowlevel/shader_compiler.hpp @@ -0,0 +1,41 @@ +#pragma once +#include + +namespace nickel::graphics { + +class ShaderCompilerImpl; + +/// Store SpirV code +struct SpirVBinary { + std::vector m_data; + + operator bool() const noexcept { return !m_data.empty(); } +}; + +enum class ShaderType { + Vertex, + Fragment, + Geometry, + TesselationController, + TesselationEvaluation, + Compute +}; + +class ShaderCompiler { +public: + /// @warning internal usage, init glslang shader compiler system + static void InitCompilerSystem(); + + /// @warning internal usage, shutdown glslang shader compiler system + static void ShutdownCompilerSystem(); + + ShaderCompiler(); + ~ShaderCompiler(); + + SpirVBinary Compile(std::span code, ShaderType stage); + +private: + std::unique_ptr m_impl; +}; + +} // namespace nickel::graphics \ No newline at end of file diff --git a/engine/engine/src/context.cpp b/engine/engine/src/context.cpp index 85c7bfe1..cf82e6ca 100644 --- a/engine/engine/src/context.cpp +++ b/engine/engine/src/context.cpp @@ -40,9 +40,15 @@ Context::~Context() { LOGI("shutdown window system"); m_window.reset(); + + LOGI("shutdown shader compiler system"); + graphics::ShaderCompiler::ShutdownCompilerSystem(); } void Context::Initialize() { + LOGI("init shader compiler system"); + graphics::ShaderCompiler::InitCompilerSystem(); + LOGI("init reflection system"); refl_generate::RegisterReflectionInfo(); diff --git a/engine/engine/src/graphics/lowlevel/shader_compile_impl.cpp b/engine/engine/src/graphics/lowlevel/shader_compile_impl.cpp new file mode 100644 index 00000000..41daf89d --- /dev/null +++ b/engine/engine/src/graphics/lowlevel/shader_compile_impl.cpp @@ -0,0 +1,81 @@ +#include "SPIRV/GlslangToSpv.h" +#include "glslang/Include/glslang_c_interface.h" +#include "glslang/Public/ResourceLimits.h" +#include "glslang/Public/ShaderLang.h" +#include "nickel/common/assert.hpp" +#include "nickel/common/flags.hpp" +#include "nickel/graphics/lowlevel/internal/shader_compiler_impl.hpp" + +namespace nickel::graphics { + +EShLanguage CvtStage(ShaderType stage) { + switch (stage) { + case ShaderType::Vertex: + return EShLangVertex; + case ShaderType::Fragment: + return EShLangFragment; + case ShaderType::Geometry: + return EShLangGeometry; + case ShaderType::TesselationController: + return EShLangTessControl; + case ShaderType::TesselationEvaluation: + return EShLangTessEvaluation; + case ShaderType::Compute: + return EShLangCompute; + } + + NICKEL_CANT_REACH(); + return {}; +} + +SpirVBinary ShaderCompilerImpl::Compile(std::span code, + ShaderType stage) { + auto language = CvtStage(stage); + const char* c = code.data(); + int size = code.size(); + + // Compile + glslang::TShader shader{language}; + shader.setStringsWithLengths(&c, &size, 1); + shader.setDebugInfo(true); + shader.setEnvClient(glslang::EShClient::EShClientVulkan, + glslang::EShTargetClientVersion::EShTargetVulkan_1_3); + shader.setEnvTarget(glslang::EShTargetLanguage::EShTargetSpv, + glslang::EShTargetLanguageVersion::EShTargetSpv_1_6); + Flags messages = Flags{EShMsgSpvRules}|EShMsgVulkanRules; + const TBuiltInResource* resource = GetDefaultResources(); + bool success = + shader.parse(resource, 450, ECoreProfile, false, false, messages); + if (!success) { + LOGE("compile shader failed:\n\t info log:\n {}\n\t debug log:\n {}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return {}; + } + + // Link + glslang::TProgram program; + program.addShader(&shader); + program.buildReflection(EShReflectionDefault); + if (!program.link(messages)) { + LOGE("link shader failed:\n\t info log:\n {}\n\t debug log:\n {}", + program.getInfoLog(), program.getInfoDebugLog()); + } + glslang::SpvOptions options{}; +#ifdef NICKEL_DEBUG + options.generateDebugInfo = true; + options.stripDebugInfo = false; + options.disableOptimizer = false; + options.validate = true; + options.optimizeSize = true; +#else + options.generateDebugInfo = false; + options.stripDebugInfo = true; + options.disableOptimizer = false; +#endif + std::vector spirv; + glslang::GlslangToSpv(*program.getIntermediate(language), spirv, &options); + + return {spirv}; +} + +} // namespace nickel::graphics \ No newline at end of file diff --git a/engine/engine/src/graphics/shader_compiler.cpp b/engine/engine/src/graphics/shader_compiler.cpp new file mode 100644 index 00000000..8e1be6c8 --- /dev/null +++ b/engine/engine/src/graphics/shader_compiler.cpp @@ -0,0 +1,25 @@ +#include "nickel/graphics/lowlevel/shader_compiler.hpp" +#include "glslang/Public/ShaderLang.h" +#include "nickel/graphics/lowlevel/internal/shader_compiler_impl.hpp" + +namespace nickel::graphics { + +void ShaderCompiler::InitCompilerSystem() { + glslang::InitializeProcess(); +} + +void ShaderCompiler::ShutdownCompilerSystem() { + glslang::FinalizeProcess(); +} + +ShaderCompiler::ShaderCompiler() + : m_impl{std::make_unique()} {} + +SpirVBinary ShaderCompiler::Compile(std::span code, + ShaderType stage) { + return m_impl->Compile(code, stage); +} + +ShaderCompiler::~ShaderCompiler() {} + +} // namespace nickel::graphics \ No newline at end of file diff --git a/engine/tests/render/CMakeLists.txt b/engine/tests/render/CMakeLists.txt index 92f68c7b..d531dea6 100644 --- a/engine/tests/render/CMakeLists.txt +++ b/engine/tests/render/CMakeLists.txt @@ -2,6 +2,22 @@ add_library(CommonRenderTestContext STATIC common.hpp common.cpp) target_link_libraries(CommonRenderTestContext PUBLIC Catch2 ${NICKEL_MAIN_ENTRY_NAME}) set_target_properties(CommonRenderTestContext PROPERTIES FOLDER tests/renderer) +macro(add_render_test name) + aux_source_directory(. SRC) + file(GLOB_RECURSE SHADER_FILES *.vert *.frag *.tesc *.tese *.comp *.geom) + set(SHADER_COMPILED_FILES ${SHADER_FILES}) + list(TRANSFORM SHADER_COMPILED_FILES APPEND ".spv") + + compile_shader("${SHADER_FILES}") + + add_executable(${name}) + target_sources(${name} PRIVATE ${SRC} ${SHADER_FILES}) + if (GLSLC_PROG) + target_sources(${name} PRIVATE ${SHADER_COMPILED_FILES}) + endif() + mark_as_gui_test(${name} renderer) +endmacro() + add_subdirectory(triangle) add_subdirectory(rectangle) add_subdirectory(texture_rect) diff --git a/engine/tests/render/colorful_rectangle/CMakeLists.txt b/engine/tests/render/colorful_rectangle/CMakeLists.txt index af42bdc6..030a55af 100644 --- a/engine/tests/render/colorful_rectangle/CMakeLists.txt +++ b/engine/tests/render/colorful_rectangle/CMakeLists.txt @@ -1,9 +1 @@ -aux_source_directory(. SRC) - -file(GLOB FILES ./*) - -add_executable(colorful_rectangle ${SRC} ${FILES}) -mark_as_gui_test(colorful_rectangle renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(colorful_rectangle) \ No newline at end of file diff --git a/engine/tests/render/colorful_rectangle/main.cpp b/engine/tests/render/colorful_rectangle/main.cpp index 221e1bd4..bc2582d8 100644 --- a/engine/tests/render/colorful_rectangle/main.cpp +++ b/engine/tests/render/colorful_rectangle/main.cpp @@ -233,10 +233,10 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/colorful_rectangle/vert.spv"); + engine_relative_path / "tests/render/colorful_rectangle/shader.vert.spv"); auto frag_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/colorful_rectangle/frag.spv"); + engine_relative_path / "tests/render/colorful_rectangle/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/colorful_rectangle2/CMakeLists.txt b/engine/tests/render/colorful_rectangle2/CMakeLists.txt index fd825072..f01d47e2 100644 --- a/engine/tests/render/colorful_rectangle2/CMakeLists.txt +++ b/engine/tests/render/colorful_rectangle2/CMakeLists.txt @@ -1,9 +1 @@ -aux_source_directory(. SRC) - -file(GLOB FILES ./*) - -add_executable(colorful_rectangle2 ${SRC} ${FILES}) -mark_as_gui_test(colorful_rectangle2 renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(colorful_rectangle2) \ No newline at end of file diff --git a/engine/tests/render/colorful_rectangle2/main.cpp b/engine/tests/render/colorful_rectangle2/main.cpp index c914306a..85d47a83 100644 --- a/engine/tests/render/colorful_rectangle2/main.cpp +++ b/engine/tests/render/colorful_rectangle2/main.cpp @@ -189,10 +189,10 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/colorful_rectangle2/vert.spv"); + engine_relative_path / "tests/render/colorful_rectangle2/shader.vert.spv"); auto frag_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/colorful_rectangle2/frag.spv"); + engine_relative_path / "tests/render/colorful_rectangle2/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/cube3d/CMakeLists.txt b/engine/tests/render/cube3d/CMakeLists.txt index ece835e4..35cdd0d6 100644 --- a/engine/tests/render/cube3d/CMakeLists.txt +++ b/engine/tests/render/cube3d/CMakeLists.txt @@ -1,9 +1 @@ -aux_source_directory(. SRC) - -file(GLOB FILES ./*) - -add_executable(cube3d ${SRC} ${FILES}) -mark_as_gui_test(cube3d renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(cube3d) \ No newline at end of file diff --git a/engine/tests/render/cube3d/main.cpp b/engine/tests/render/cube3d/main.cpp index 1af44eb2..13a06ebf 100644 --- a/engine/tests/render/cube3d/main.cpp +++ b/engine/tests/render/cube3d/main.cpp @@ -420,9 +420,9 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/cube3d/vert.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/cube3d/shader.vert.spv"); auto frag_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/cube3d/frag.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/cube3d/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/gltf/CMakeLists.txt b/engine/tests/render/gltf/CMakeLists.txt index fbbb10f1..336363f4 100644 --- a/engine/tests/render/gltf/CMakeLists.txt +++ b/engine/tests/render/gltf/CMakeLists.txt @@ -1,4 +1 @@ -aux_source_directory(. SRC) - -add_executable(gltf ${SRC}) -mark_as_gui_test(gltf renderer) \ No newline at end of file +add_render_test(gltf) \ No newline at end of file diff --git a/engine/tests/render/rectangle/CMakeLists.txt b/engine/tests/render/rectangle/CMakeLists.txt index 581ea64d..5a7931b4 100644 --- a/engine/tests/render/rectangle/CMakeLists.txt +++ b/engine/tests/render/rectangle/CMakeLists.txt @@ -1,7 +1 @@ -aux_source_directory(. SRC) - -add_executable(rectangle ${SRC} shader.vert shader.frag) -mark_as_gui_test(rectangle renderer) - -compile_shader(shader.vert vert.spv) -compile_shader(shader.frag frag.spv) \ No newline at end of file +add_render_test(rectangle) \ No newline at end of file diff --git a/engine/tests/render/rectangle/main.cpp b/engine/tests/render/rectangle/main.cpp index f061624c..2282edaf 100644 --- a/engine/tests/render/rectangle/main.cpp +++ b/engine/tests/render/rectangle/main.cpp @@ -168,9 +168,9 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/rectangle/vert.spv"); + engine_relative_path / "tests/render/rectangle/shader.vert.spv"); auto frag_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/rectangle/frag.spv"); + engine_relative_path / "tests/render/rectangle/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/skybox/CMakeLists.txt b/engine/tests/render/skybox/CMakeLists.txt index fb20ecdc..998dea18 100644 --- a/engine/tests/render/skybox/CMakeLists.txt +++ b/engine/tests/render/skybox/CMakeLists.txt @@ -1,9 +1 @@ -aux_source_directory(. SRC) - -file(GLOB FILES ./*) - -add_executable(skybox ${SRC} ${FILES}) -mark_as_gui_test(skybox renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(skybox) \ No newline at end of file diff --git a/engine/tests/render/skybox/main.cpp b/engine/tests/render/skybox/main.cpp index 766c94c0..0d65485f 100644 --- a/engine/tests/render/skybox/main.cpp +++ b/engine/tests/render/skybox/main.cpp @@ -436,9 +436,9 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/skybox/vert.spv"); + engine_relative_path / "tests/render/skybox/shader.vert.spv"); auto frag_file_content = nickel::ReadWholeFile( - engine_relative_path / "tests/render/skybox/frag.spv"); + engine_relative_path / "tests/render/skybox/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/texture_rect/CMakeLists.txt b/engine/tests/render/texture_rect/CMakeLists.txt index 129d1348..e782682c 100644 --- a/engine/tests/render/texture_rect/CMakeLists.txt +++ b/engine/tests/render/texture_rect/CMakeLists.txt @@ -1,9 +1 @@ -aux_source_directory(. SRC) - -file(GLOB FILES ./*) - -add_executable(texture_rect ${SRC} ${FILES}) -mark_as_gui_test(texture_rect renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(texture_rect) \ No newline at end of file diff --git a/engine/tests/render/texture_rect/main.cpp b/engine/tests/render/texture_rect/main.cpp index 724cc24a..c2304407 100644 --- a/engine/tests/render/texture_rect/main.cpp +++ b/engine/tests/render/texture_rect/main.cpp @@ -267,9 +267,9 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/texture_rect/vert.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/texture_rect/shader.vert.spv"); auto frag_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/texture_rect/frag.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/texture_rect/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tests/render/triangle/CMakeLists.txt b/engine/tests/render/triangle/CMakeLists.txt index 5076ad33..99690a74 100644 --- a/engine/tests/render/triangle/CMakeLists.txt +++ b/engine/tests/render/triangle/CMakeLists.txt @@ -1,7 +1 @@ -aux_source_directory(. SRC) - -add_executable(triangle ${SRC} shader.vert shader.frag) -mark_as_gui_test(triangle renderer) - -compile_shader(shader.frag frag.spv) -compile_shader(shader.vert vert.spv) \ No newline at end of file +add_render_test(triangle) \ No newline at end of file diff --git a/engine/tests/render/triangle/main.cpp b/engine/tests/render/triangle/main.cpp index 81ef0156..677f9455 100644 --- a/engine/tests/render/triangle/main.cpp +++ b/engine/tests/render/triangle/main.cpp @@ -120,9 +120,9 @@ class Application : public nickel::Application { auto engine_relative_path = nickel::Context::GetInst().GetEngineRelativePath(); auto vert_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/triangle/vert.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/triangle/shader.vert.spv"); auto frag_file_content = - nickel::ReadWholeFile(engine_relative_path / "tests/render/triangle/frag.spv"); + nickel::ReadWholeFile(engine_relative_path / "tests/render/triangle/shader.frag.spv"); ShaderModule vertex_shader = device.CreateShaderModule( (uint32_t*)vert_file_content.data(), vert_file_content.size()); diff --git a/engine/tools/3rdlibs/CMakeLists.txt b/engine/tools/3rdlibs/CMakeLists.txt new file mode 100644 index 00000000..a6e33be2 --- /dev/null +++ b/engine/tools/3rdlibs/CMakeLists.txt @@ -0,0 +1 @@ +add_3rdlib(Lyra) \ No newline at end of file diff --git a/engine/tools/3rdlibs/Lyra/CMakeLists.txt b/engine/tools/3rdlibs/Lyra/CMakeLists.txt new file mode 100644 index 00000000..7cb79fe4 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright René Ferdinand Rivera Morell +# Copyright 2020 Jayesh Vinay Badwaik +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt + +# NOTE: +# CMake support for Lyra is currently experimental at best and the +# interface is likely to change in the future +# +# This file provides minimal cmake support (no unit-tests, +# no installation) for integration into a "host" cmake project +# via the "add_subdirectory( )" command. +# +# Other cmake targets can then use the public target name +# "bfg::lyra" in order to express their dependency +# on this library. I.e: +# +# target_link_libraries( PUBLIC bfg::lyra ) + +# Only need the basic minimum of project, add_library, and +# target_include_directories commands. +cmake_minimum_required( VERSION 3.5 ) + +# Don't set VERSION, as that's a pita to keep up to date with the version +# header. And don't set LANGUAGES as we are multi-language and header +# only, so it's irrelevant. +project( lyra ) + +# Simple INTERFACE, and header only, library target. +add_library( lyra INTERFACE ) + +# The only usage requirement is include dir for consumers. +target_include_directories( + lyra + INTERFACE + $ + $ + ) + +# Add an alias to public name. +add_library( bfg::lyra ALIAS lyra ) \ No newline at end of file diff --git a/engine/tools/3rdlibs/Lyra/LICENSE.txt b/engine/tools/3rdlibs/Lyra/LICENSE.txt new file mode 100644 index 00000000..36b7cd93 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/arg.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/arg.hpp new file mode 100644 index 00000000..a34ae77b --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/arg.hpp @@ -0,0 +1,128 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_ARG_HPP +#define LYRA_ARG_HPP + +#include "lyra/detail/bound.hpp" +#include "lyra/detail/print.hpp" +#include "lyra/detail/tokens.hpp" +#include "lyra/option_style.hpp" +#include "lyra/parser.hpp" +#include "lyra/parser_result.hpp" + +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_arg] += `lyra::arg` + +A parser for regular arguments, i.e. not `--` or `-` prefixed. This is simply +a way to get values of arguments directly specified in the cli. + +Is-a <>. + +*/ // end::reference[] +class arg : public bound_parser +{ + public: + using bound_parser::bound_parser; + + std::string get_usage_text(const option_style &) const override + { + std::string text; + if (!m_hint.empty()) + { + auto c = cardinality(); + if (c.is_required()) + { + for (std::size_t i = 0; i < c.minimum; ++i) + (((text += (i > 0 ? " " : "")) += "<") += m_hint) += ">"; + if (c.is_unbounded()) + (((text += (c.is_required() ? " " : "")) += "[<") += m_hint) + += ">...]"; + } + else if (c.is_unbounded()) + { + ((text += "[<") += m_hint) += ">...]"; + } + else + { + ((text += "<") += m_hint) += ">"; + } + } + return text; + } + + using parser::parse; + + parse_result parse(detail::token_iterator const & tokens, + const option_style & style) const override + { + (void)style; + LYRA_PRINT_SCOPE("arg::parse"); + auto validationResult = validate(); + if (!validationResult) return parse_result(validationResult); + + if (!tokens) + { + // Nothing to match against. + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); + } + + auto const & token = tokens.argument(); + + auto valueRef = static_cast(m_ref.get()); + + if (value_choices) + { + auto choice_result = value_choices->contains_value(token.name); + if (!choice_result) + { + LYRA_PRINT_DEBUG( + "(!)", get_usage_text(style), "!=", token.name); + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); + } + } + + auto set_result = valueRef->setValue(token.name); + if (!set_result) + { + LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", token.name); + return parse_result(set_result); + } + else + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", token.name); + auto remainingTokens = tokens; + remainingTokens.pop(token); + return parse_result::ok(detail::parse_state( + parser_result_type::matched, remainingTokens)); + } + } + + protected: + std::string get_print_order_key(const option_style &) const override + { + return this->hint(); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + p.option(style, get_usage_text(style), m_description); + } +}; + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/args.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/args.hpp new file mode 100644 index 00000000..9c15aa09 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/args.hpp @@ -0,0 +1,70 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_ARGS_HPP +#define LYRA_ARGS_HPP + +#include +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_args] += `lyra::args` + +Transport for raw args (copied from main args, supplied via init list, or from +a pair of iterators). + +*/ // end::reference[] +class args +{ + public: + // Construct from usual main() function arguments. + args(int argc, char const * const * argv) + : m_exeName((argv && (argc >= 1)) ? argv[0] : "") + , m_args((argv && (argc >= 1)) ? argv + 1 : nullptr, argv + argc) + {} + + // Construct directly from an initializer '{}'. + args(std::initializer_list args_list) + : m_exeName(args_list.size() >= 1 ? *args_list.begin() : "") + , m_args( + args_list.size() >= 1 ? args_list.begin() + 1 : args_list.end(), + args_list.end()) + {} + + // Construct from iterators. + template + args(const It & start, const It & end) + : m_exeName(start != end ? *start : "") + , m_args(start != end ? start + 1 : end, end) + {} + + // The executable name taken from argument zero. + std::string exe_name() const { return m_exeName; } + + // Arguments, excluding the exe name. + std::vector::const_iterator begin() const + { + return m_args.begin(); + } + + std::vector::const_iterator end() const + { + return m_args.end(); + } + + private: + std::string m_exeName; + std::vector m_args; +}; + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/arguments.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/arguments.hpp new file mode 100644 index 00000000..950e0f36 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/arguments.hpp @@ -0,0 +1,654 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_ARGUMENTS_HPP +#define LYRA_ARGUMENTS_HPP + +#include "lyra/detail/print.hpp" +#include "lyra/detail/tokens.hpp" +#include "lyra/detail/trait_utils.hpp" +#include "lyra/option_style.hpp" +#include "lyra/parser.hpp" +#include "lyra/parser_result.hpp" +#include "lyra/printer.hpp" + +#include +#include +#include +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_arguments] += `lyra::arguments` + +A Combined parser made up of any number of parsers. Creating and using +one of these as a basis one can incrementally compose other parsers into this +one. For example: + +[source] +---- +auto p = lyra::arguments(); +std::string what; +float when = 0; +std::string where; +p |= lyra::opt(what, "what")["--make-it-so"]("Make it so.").required(); +p |= lyra::opt(when. "when")["--time"]("When to do .").optional(); +p.add_argument(lyra::opt(where, "where").name("--where") + .help("There you are.").optional()); +---- + +*/ // end::reference[] +class arguments : public parser +{ + public: + // How to evaluate the collection of arguments within the limits of the + // cardinality. + enum evaluation + { + // Any of the arguments, in any order, are valid. I.e. an inclusive-or. + eval_any = 0, + // All arguments, in sequence, matched. I.e. conjunctive-and. + eval_sequence = 1, + // Any of the arguments, in any order, are valid and uknown arguments + // do not cause parsing errors. + eval_relaxed = 2, + }; + + arguments() = default; + + arguments(evaluation e) + : eval_mode(e) + {} + + // Copy construction, needs to copy the the composed parsers. + arguments(const arguments & other); + + // Compose a regular parser. + arguments & add_argument(parser const & p); + arguments & operator|=(parser const & p); + + // Compose the parsers from another `arguments`. + arguments & add_argument(arguments const & other); + arguments & operator|=(arguments const & other); + + // Concat composition. + template + friend typename std::enable_if< + std::is_base_of::type>::value, + T &>::type + operator|(T & self, U const & other) + { + return static_cast(self.add_argument(other)); + } + template + friend typename std::enable_if< + std::is_base_of::type>::value, + T &>::type + operator|(T && self, U const & other) + { + return static_cast(self.add_argument(other)); + } + + // Parsing mode. + arguments & sequential(); + arguments & inclusive(); + arguments & relaxed(); + + // Access. + template + T & get(std::size_t i); + + // Internal.. + + std::string get_usage_text(const option_style & style) const override + { + std::string text; + for (auto const & p : parsers) + { + std::string usage_text = p->get_usage_text(style); + if (usage_text.size() > 0) + { + if (!text.empty()) text += " "; + if (p->is_group() && p->is_optional()) + ((text += "[ ") += usage_text) += " ]"; + else if (p->is_group()) + ((text += "{ ") += usage_text) += " }"; + else if (p->is_optional()) + ((text += "[") += usage_text) += "]"; + else + text += usage_text; + } + } + return text; + } + + std::string get_description_text(const option_style & style) const override + { + std::string text; + for (auto const & p : parsers) + { + if (p->is_group()) continue; + auto child_description = p->get_description_text(style); + if (!child_description.empty()) + { + if (!text.empty()) text += "\n"; + text += child_description; + } + } + return text; + } + + detail::parser_cardinality cardinality() const override { return { 0, 0 }; } + + result validate() const override + { + for (auto const & p : parsers) + { + auto parse_valid = p->validate(); + if (!parse_valid) return parse_valid; + } + return result::ok(); + } + + parse_result parse(detail::token_iterator const & tokens, + const option_style & style) const override + { + switch (eval_mode) + { + case eval_any: + case eval_relaxed: return parse_any(tokens, style); + case eval_sequence: return parse_sequence(tokens, style); + } + return parse_result::error( + detail::parse_state(parser_result_type::no_match, tokens), + "Unknown evaluation mode; not one of 'any', 'sequence', or 'relaxed'."); + } + + // Match in any order, any number of times. Returns an error if nothing + // matched. + parse_result parse_any( + detail::token_iterator const & tokens, const option_style & style) const + { + LYRA_PRINT_SCOPE("arguments::parse_any"); + + std::vector parsing_count(parsers.size(), 0); + auto parsing_result = parse_result::ok( + detail::parse_state(parser_result_type::empty_match, tokens)); + auto nomatch_result = parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); + + while (parsing_result.value().remainingTokens()) + { + LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", + parsing_result.value().remainingTokens() + ? parsing_result.value().remainingTokens().argument().name + : "", + ".."); + bool token_parsed = false; + + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) + { + auto parser_cardinality = p->cardinality(); + if (parser_cardinality.is_unbounded() + || *parsing_count_i < parser_cardinality.maximum) + { + auto subparse_result = p->parse( + parsing_result.value().remainingTokens(), style); + if (!subparse_result) + { + LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", + parsing_result.value() + .remainingTokens() + .argument() + .name); + // Is the subparse error bad enough to trigger an + // immediate return? For example for an option syntax + // error. + if (subparse_result.has_value() + && subparse_result.value().type() + == parser_result_type::short_circuit_all) + return subparse_result; + // For not severe errors, we save the error if it's + // the first so that in case no other parsers match + // we can report the earliest problem, as that's + // the likeliest issue. + else if (nomatch_result) + nomatch_result = parse_result(subparse_result); + } + else if (subparse_result + && subparse_result.value().type() + == parser_result_type::empty_match) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==>", subparse_result.value().type()); + parsing_result = parse_result(subparse_result); + } + else if (subparse_result + && subparse_result.value().type() + != parser_result_type::no_match) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==>", subparse_result.value().type()); + parsing_result = parse_result(subparse_result); + token_parsed = true; + *parsing_count_i += 1; + break; + } + } + ++parsing_count_i; + } + + if (parsing_result.value().type() + == parser_result_type::short_circuit_all) + return parsing_result; + if (!token_parsed) + { + // Nothing matched for the current arg and we are doing relaxed + // parsing. Hence we need to skip over that unknown arg to + // continue trying the rest. + if (eval_mode == eval_relaxed) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==> skipped"); + auto remainingTokens + = parsing_result.value().remainingTokens(); + remainingTokens.pop(remainingTokens.argument()); + parsing_result = parse_result::ok(detail::parse_state( + parser_result_type::empty_match, remainingTokens)); + } + // If something signaled and error, and hence we didn't + // match/parse anything, we indicate the error if not in relaxed + // mode. + else if (!nomatch_result) + return nomatch_result; + // Encountered something unrecognized. We stop and report the + // result. + else + break; + } + } + // Check missing required options. For bounded arguments we check + // bound min and max bounds against what we parsed. For the loosest + // required arguments we check for only the minimum. As the upper + // bound could be infinite. + { + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) + { + auto parser_cardinality = p->cardinality(); + if ((parser_cardinality.is_bounded() + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) + || (parser_cardinality.is_required() + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, parsing_result, style); + ++parsing_count_i; + } + } + return parsing_result; + } + + parse_result parse_sequence( + detail::token_iterator const & tokens, const option_style & style) const + { + LYRA_PRINT_SCOPE("arguments::parse_sequence"); + LYRA_PRINT_DEBUG("(?)", get_usage_text(style), + "?=", tokens ? tokens.argument().name : "", ".."); + + std::vector parsing_count(parsers.size(), 0); + auto p_result = parse_result::ok( + detail::parse_state(parser_result_type::empty_match, tokens)); + + // Sequential parsing means we walk through the given parsers in order + // and exhaust the tokens as we match parsers. + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) + { + auto parser_cardinality = p->cardinality(); + // This is a greedy sequential parsing algo. As it parses the + // current argument as much as possible. + do + { + auto subresult + = p->parse(p_result.value().remainingTokens(), style); + if (!subresult) break; + if (parser_result_type::no_match == subresult.value().type()) + { + LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + break; + } + if (parser_result_type::short_circuit_all + == subresult.value().type()) + return subresult; + if (parser_result_type::matched == subresult.value().type()) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + *parsing_count_i += 1; + p_result = subresult; + } + if (parser_result_type::empty_match == subresult.value().type()) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + *parsing_count_i += 1; + } + } + while (p_result.value().have_tokens() + && (parser_cardinality.is_unbounded() + || *parsing_count_i < parser_cardinality.maximum)); + // Check missing required options immediately as for sequential the + // argument is greedy and will fully match here. For bounded + // arguments we check bound min and max bounds against what we + // parsed. For the loosest required arguments we check for only the + // minimum. As the upper bound could be infinite. + if ((parser_cardinality.is_bounded() + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) + || (parser_cardinality.is_required() + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, p_result, style); + ++parsing_count_i; + } + // The return is just the last state as it contains any remaining tokens + // to parse. + return p_result; + } + + template + parse_result make_parse_error(const detail::token_iterator & tokens, + const parser & p, + const R & p_result, + const option_style & style) const + { + if (tokens) + return parse_result::error(p_result.value(), + "Unrecognized argument '" + tokens.argument().name + + "' while parsing: " + p.get_usage_text(style)); + else + return parse_result::error( + p_result.value(), "Expected: " + p.get_usage_text(style)); + } + + std::unique_ptr clone() const override + { + return make_clone(this); + } + + template + T & print_help(T & os) const + { + std::unique_ptr p = make_printer(os); + this->print_help_text(*p, get_option_style()); + return os; + } + + const parser * get_named(const std::string & n) const override + { + for (auto & p : parsers) + { + const parser * p_result = p->get_named(n); + if (p_result) return p_result; + } + return nullptr; + } + + protected: + std::shared_ptr opt_style; + std::vector> parsers; + evaluation eval_mode = eval_any; + + option_style get_option_style() const + { + return opt_style ? *opt_style : option_style::posix(); + } + + option_style & ref_option_style() + { + if (!opt_style) + opt_style = std::make_shared(option_style::posix()); + return *opt_style; + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + for_each_print_ordered_parser(style, parsers.begin(), parsers.end(), + [&](const option_style & s, const parser & q) { + q.print_help_text_details(p, s); + }); + } +}; + +/* tag::reference[] + +[#lyra_arguments_ctor] +== Construction + +end::reference[] */ + +/* tag::reference[] + +[#lyra_arguments_ctor_default] +=== Default + +[source] +---- +arguments() = default; +---- + +Default constructing a `arguments` is the starting point to adding arguments +and options for parsing a arguments line. + +end::reference[] */ + +/* tag::reference[] + +[#lyra_arguments_ctor_copy] +=== Copy + +[source] +---- +arguments::arguments(const arguments& other); +---- + +end::reference[] */ +inline arguments::arguments(const arguments & other) + : parser(other) + , opt_style(other.opt_style) + , eval_mode(other.eval_mode) +{ + for (auto & other_parser : other.parsers) + { + parsers.push_back(other_parser->clone()); + } +} + +/* tag::reference[] + +[#lyra_arguments_specification] +== Specification + +end::reference[] */ + +// == + +/* tag::reference[] +[#lyra_arguments_add_argument] +=== `lyra::arguments::add_argument` + +[source] +---- +arguments& arguments::add_argument(parser const& p); +arguments& arguments::operator|=(parser const& p); +arguments& arguments::add_argument(arguments const& other); +arguments& arguments::operator|=(arguments const& other); +---- + +Adds the given argument parser to the considered arguments for this +`arguments`. Depending on the parser given it will be: directly added as an +argument (for `parser`), or add the parsers from another `arguments` to +this one. + +end::reference[] */ +inline arguments & arguments::add_argument(parser const & p) +{ + parsers.push_back(p.clone()); + return *this; +} +inline arguments & arguments::operator|=(parser const & p) +{ + return this->add_argument(p); +} +inline arguments & arguments::add_argument(arguments const & other) +{ + if (other.is_group()) + { + parsers.push_back(other.clone()); + } + else + { + for (auto & p : other.parsers) + { + parsers.push_back(p->clone()); + } + } + return *this; +} +inline arguments & arguments::operator|=(arguments const & other) +{ + return this->add_argument(other); +} + +/* tag::reference[] +=== `lyra::arguments::sequential` + +[source] +---- +arguments & arguments::sequential(); +---- + +Sets the parsing mode for the arguments to "sequential". When parsing the +arguments they will be, greedily, consumed in the order they where added. +This is useful for sub-commands and structured command lines. + +end::reference[] */ +inline arguments & arguments::sequential() +{ + eval_mode = eval_sequence; + return *this; +} + +/* tag::reference[] +=== `lyra::arguments::inclusive` + +[source] +---- +arguments & arguments::inclusive(); +---- + +Sets the parsing mode for the arguments to "inclusively any". This is the +default that attempts to match each parsed argument with all the available +parsers. This means that there is no ordering enforced. + +end::reference[] */ +inline arguments & arguments::inclusive() +{ + eval_mode = eval_any; + return *this; +} + +/* tag::reference[] +=== `lyra::arguments::relaxed` + +[source] +---- +arguments & arguments::relaxed(); +---- + +Sets the parsing mode for the arguments to "relaxed any". When parsing the +arguments it attempts to match each parsed argument with all the available +parsers. This means that there is no ordering enforced. Unknown, i.e. failed, +parsing are ignored. + +end::reference[] */ +inline arguments & arguments::relaxed() +{ + eval_mode = eval_relaxed; + return *this; +} + +/* tag::reference[] +=== `lyra::arguments::get` + +[source] +---- +template +T & arguments::get(std::size_t i); +---- + +Get a modifiable reference to one of the parsers specified. + +end::reference[] */ +template +T & arguments::get(std::size_t i) +{ + return static_cast(*parsers.at(i)); +} + +/* tag::reference[] +=== `lyra::operator<<` + +[source] +---- +template +T & operator<<(T & os, arguments const & a); +---- + +Prints the help text for the arguments to the given stream `os`. The `os` stream +is not used directly for printing out. Instead a <> object is +created by calling `lyra::make_printer(os)`. This indirection allows one to +customize how the output is generated. + +end::reference[] */ +template +T & operator<<(T & os, arguments const & a) +{ + return a.print_help(os); +} + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/cli.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/cli.hpp new file mode 100644 index 00000000..ab5e7c4b --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/cli.hpp @@ -0,0 +1,440 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_CLI_HPP +#define LYRA_CLI_HPP + +#include "lyra/args.hpp" +#include "lyra/arguments.hpp" +#include "lyra/detail/deprecated_parser_customization.hpp" +#include "lyra/detail/from_string.hpp" +#include "lyra/detail/print.hpp" +#include "lyra/detail/tokens.hpp" +#include "lyra/detail/trait_utils.hpp" +#include "lyra/exe_name.hpp" +#include "lyra/group.hpp" +#include "lyra/option_style.hpp" +#include "lyra/parser.hpp" +#include "lyra/parser_result.hpp" + +#include +#include +#include +#include +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_cli] += `lyra::cli` + +A Combined parser made up of any two or more other parsers. Creating and using +one of these as a basis one can incrementally compose other parsers into this +one. For example: + +[source] +---- +auto cli = lyra::cli(); +std::string what; +float when = 0; +std::string where; +cli |= lyra::opt(what, "what")["--make-it-so"]("Make it so.").required(); +cli |= lyra::opt(when. "when")["--time"]("When to do .").optional(); +cli.add_argument(lyra::opt(where, "where").name("--where") + .help("There you are.").optional()); +---- + +*/ // end::reference[] +class cli : protected arguments +{ + public: + cli() = default; + + // Copy construction, needs to copy the exe name and the composed parsers. + cli(const cli & other); + + // Compose the `exe_name` parser. + cli & add_argument(exe_name const & exe_name); + cli & operator|=(exe_name const & exe_name); + + // Compose a regular parser. + cli & add_argument(parser const & p); + cli & operator|=(parser const & p); + + // Compose a group, by adding it as a single argument. + cli & add_argument(group const & p); + cli & operator|=(group const & p); + + // Compose the parsers from another `cli`. + cli & add_argument(cli const & other); + cli & operator|=(cli const & other); + + // Concat composition. + template + cli operator|(T const & other) const; + + // Result reference wrapper to fetch and convert argument. + struct value_result + { + public: + explicit value_result(const parser * p) + : parser_ref(p) + {} + + template ::type>::value>:: + type * = nullptr> + operator T() const + { + typename detail::remove_cvref::type converted_value {}; + if (parser_ref) + detail::from_string::type>( + parser_ref->get_value(0), converted_value); + return converted_value; + } + + template + operator std::vector() const + { + std::vector converted_value; + if (parser_ref) + { + for (std::size_t i = 0; i < parser_ref->get_value_count(); ++i) + { + T v; + if (detail::from_string(parser_ref->get_value(i), v)) + converted_value.push_back(v); + } + } + return converted_value; + } + + operator std::string() const + { + if (parser_ref) return parser_ref->get_value(0); + return ""; + } + + private: + const parser * parser_ref = nullptr; + }; + + value_result operator[](const std::string & n); + + // Style control. + cli & style(const option_style & style); + cli & style(option_style && style); + cli & style_print_short_first(); + cli & style_print_long_first(); + + // Stream out generates the help output. + template + friend T & operator<<(T & os, cli const & c); + + // Parse from arguments. + parse_result parse(args const & args) const + { + if (opt_style) + return parse(args, *opt_style); + else + return parse(args, option_style::posix()); + } + parse_result parse(args const & args, const option_style & style) const; + + cli & sequential() { return arguments::sequential(), *this; } + cli & inclusive() { return arguments::inclusive(), *this; } + cli & relaxed() { return arguments::relaxed(), *this; } + + // Internal.. + + using arguments::parse; + using arguments::get_named; + + std::unique_ptr clone() const override + { + return std::unique_ptr(new cli(*this)); + } + + protected: + mutable exe_name m_exeName; + + std::string get_usage_text(const option_style & style) const override + { + if (!m_exeName.name().empty()) + return m_exeName.name() + " " + arguments::get_usage_text(style); + else + // We use an empty exe name as an indicator to remove USAGE text. + return ""; + } +}; + +/* tag::reference[] + +[#lyra_cli_ctor] +== Construction + +end::reference[] */ + +/* tag::reference[] + +[#lyra_cli_ctor_default] +=== Default + +[source] +---- +cli() = default; +---- + +Default constructing a `cli` is the starting point to adding arguments +and options for parsing a command line. + +end::reference[] */ + +/* tag::reference[] + +[#lyra_cli_ctor_copy] +=== Copy + +[source] +---- +cli::cli(const cli& other); +---- + +end::reference[] */ +inline cli::cli(const cli & other) + : arguments(other) + , m_exeName(other.m_exeName) +{} + +/* tag::reference[] + +[#lyra_cli_specification] +== Specification + +end::reference[] */ + +// == + +/* tag::reference[] +[#lyra_cli_add_argument] +=== `lyra::cli::add_argument` + +[source] +---- +cli& cli::add_argument(exe_name const& exe_name); +cli& cli::operator|=(exe_name const& exe_name); +cli& cli::add_argument(parser const& p); +cli& cli::operator|=(parser const& p); +cli& cli::add_argument(group const& p); +cli& cli::operator|=(group const& p); +cli& cli::add_argument(cli const& other); +cli& cli::operator|=(cli const& other); +---- + +Adds the given argument parser to the considered arguments for this +`cli`. Depending on the parser given it will be: recorded as the exe +name (for `exe_name` parser), directly added as an argument (for +`parser`), or add the parsers from another `cli` to this one. + +end::reference[] */ +inline cli & cli::add_argument(exe_name const & exe_name) +{ + m_exeName = exe_name; + return *this; +} +inline cli & cli::operator|=(exe_name const & exe_name) +{ + return this->add_argument(exe_name); +} +inline cli & cli::add_argument(parser const & p) +{ + arguments::add_argument(p); + return *this; +} +inline cli & cli::operator|=(parser const & p) +{ + arguments::add_argument(p); + return *this; +} +inline cli & cli::add_argument(group const & other) +{ + arguments::add_argument(static_cast(other)); + return *this; +} +inline cli & cli::operator|=(group const & other) +{ + return this->add_argument(other); +} +inline cli & cli::add_argument(cli const & other) +{ + arguments::add_argument(static_cast(other)); + return *this; +} +inline cli & cli::operator|=(cli const & other) +{ + return this->add_argument(other); +} + +template +inline cli cli::operator|(T const & other) const +{ + return cli(*this).add_argument(other); +} + +template +cli operator|(composable_parser const & thing, T const & other) +{ + return cli() | static_cast(thing) | other; +} + +/* tag::reference[] +[#lyra_cli_array_ref] +=== `lyra::cli::operator[]` + +[source] +---- +cli::value_result cli::operator[](const std::string & n) +---- + +Finds the given argument by either option name or hint name and returns a +convertible reference to the value, either the one provided by the user or the +default. + +end::reference[] */ +inline cli::value_result cli::operator[](const std::string & n) +{ + return value_result(this->get_named(n)); +} + +/* tag::reference[] +[#lyra_cli_parse] +=== `lyra::cli::parse` + +[source] +---- +parse_result cli::parse( + args const& args, const option_style& customize) const; +---- + +Parses given arguments `args` and optional option style. +The result indicates success or failure, and if failure what kind of failure +it was. The state of variables bound to options is unspecified and any bound +callbacks may have been called. + +end::reference[] */ +inline parse_result cli::parse( + args const & args, const option_style & style) const +{ + LYRA_PRINT_SCOPE("cli::parse"); + m_exeName.set(args.exe_name()); + detail::token_iterator args_tokens(args, style); + parse_result p_result = parse(args_tokens, style); + if (p_result + && (p_result.value().type() == parser_result_type::no_match + || p_result.value().type() == parser_result_type::matched + || p_result.value().type() == parser_result_type::empty_match)) + { + if (p_result.value().have_tokens()) + { + return parse_result::error(p_result.value(), + "Unrecognized token: " + + p_result.value().remainingTokens().argument().name); + } + } + return p_result; +} + +/* tag::reference[] +[#lyra_cli_style] +=== `lyra::cli::style` + +[source] +---- +lyra::cli & lyra::cli::style(const lyra::option_style & style) +lyra::cli & lyra::cli::style(lyra::option_style && style) +---- + +Specifies the <> to accept for this instance. + +end::reference[] */ +inline cli & cli::style(const option_style & style) +{ + opt_style = std::make_shared(style); + return *this; +} +inline cli & cli::style(option_style && style) +{ + opt_style = std::make_shared(std::move(style)); + return *this; +} + +/* tag::reference[] +[#lyra_cli_style_print_short_first] +=== `lyra::cli::style_print_short_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_short_first() +---- + +Specifies print options sorted with short options appearing first. + +end::reference[] */ +inline cli & cli::style_print_short_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_short_first; + return *this; +} + +/* tag::reference[] +[#lyra_cli_style_print_long_first] +=== `lyra::cli::style_print_long_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_long_first() +---- + +Specifies print options sorted with long options appearing first. + +end::reference[] */ +inline cli & cli::style_print_long_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_long_first; + return *this; +} + +/* tag::reference[] +=== `lyra::operator<<` + +[source] +---- +template +T & operator<<(T & os, cli const & c); +---- + +Prints the help text for the cli to the given stream `os`. The `os` stream +is not used directly for printing out. Instead a <> object is +created by calling `lyra::make_printer(os)`. This indirection allows one to +customize how the output is generated. + +end::reference[] */ +template +T & operator<<(T & os, cli const & c) +{ + return c.print_help(os); +} + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/cli_parser.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/cli_parser.hpp new file mode 100644 index 00000000..ca5a576d --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/cli_parser.hpp @@ -0,0 +1,17 @@ +// Copyright René Ferdinand Rivera Morell +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_CLI_PARSER_HPP +#define LYRA_CLI_PARSER_HPP + +#include "lyra/cli.hpp" + +namespace lyra { + +using cli_parser = cli; + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/command.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/command.hpp new file mode 100644 index 00000000..56ab9a93 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/command.hpp @@ -0,0 +1,247 @@ +// Copyright René Ferdinand Rivera Morell +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_COMMAND_HPP +#define LYRA_COMMAND_HPP + +#include "lyra/group.hpp" +#include "lyra/literal.hpp" +#include "lyra/option_style.hpp" +#include "lyra/parser.hpp" +#include "lyra/printer.hpp" + +#include +#include +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_command] += `lyra::command` + +A parser that encapsulates the pattern of parsing sub-commands. It provides a +quick wrapper for the equivalent arrangement of `group` and `literal` parsers. +For example: + +[source] +---- +lyra::command c = lyra::command("sub"); +---- + +Is equivalent to: + +[source] +---- +lyra::command c = lyra::group() + .sequential() + .add_argument(literal("sub")) + .add_argument(group()); +lyra::group & g = c.get(1); +---- + +I.e. it's composed of a `literal` followed by the rest of the command arguments. + +Is-a <>. + +*/ // end::reference[] +class command : public group +{ + public: + // Construction, with and without, callback. + explicit command(const std::string & n); + command( + const std::string & n, const std::function & f); + + // Help description. + command & help(const std::string & text); + command & operator()(std::string const & description); + + // Add arguments. + template + command & add_argument(P const & p); + template + command & operator|=(P const & p); + + // Settings. + command & brief_help(bool brief = true); + command & optional() { return static_cast(group::optional()); } + command & required(std::size_t n = 1) + { + return static_cast(group::required(n)); + } + command & cardinality(std::size_t n) + { + return static_cast(group::cardinality(n)); + } + command & cardinality(std::size_t n, std::size_t m) + { + return static_cast(group::cardinality(n, m)); + } + + // Internal. + std::unique_ptr clone() const override + { + return make_clone(this); + } + detail::parser_cardinality cardinality() const override + { + return group::cardinality(); + } + + std::string get_usage_text(const option_style & style) const override + { + auto tail = parsers[1]->get_usage_text(style); + return parsers[0]->get_usage_text(style) + + (tail.empty() ? (tail) : (" " + tail)); + } + + protected: + bool expanded_help_details = true; + + std::string get_print_order_key(const option_style & style) const override + { + return parsers[0]->get_print_order_key(style); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + // This avoids printing out the "internal" group brackets "{}" for the + // command arguments. + if (expanded_help_details) + { + p.option(style, "", ""); + parsers[0]->print_help_text_details(p, style); + p.option(style, "", ""); + p.indent(); + parsers[1]->print_help_text_details(p, style); + p.dedent(); + } + else + { + parsers[0]->print_help_text_details(p, style); + } + } +}; + +/* tag::reference[] + +[#lyra_command_ctor] +== Construction + +[source] +---- +command::command(const std::string & n); +command::command( + const std::string & n, const std::function& f); +---- + +To construct an `command` we need a name (`n`) that matches, and triggers, that +command. + + +end::reference[] */ +inline command::command(const std::string & n) +{ + this->sequential() + .add_argument(literal(n)) + .add_argument(group().required()); +} +inline command::command( + const std::string & n, const std::function & f) + : group(f) +{ + this->sequential() + .add_argument(literal(n)) + .add_argument(group().required()); +} + +/* tag::reference[] + +[#lyra_command_specification] +== Specification + +end::reference[] */ + +/* tag::reference[] + +[#lyra_command_help] +=== `lyra:command::help` + +[source] +---- +command & command::help(const std::string& text) +command & command::operator()(std::string const& description) +---- + +Specify a help description for the command. This sets the help for the +underlying literal of the command. + +end::reference[] */ +inline command & command::help(const std::string & text) +{ + this->get(0).help(text); + return *this; +} +inline command & command::operator()(std::string const & description) +{ + return this->help(description); +} + +/* tag::reference[] +[#lyra_command_add_argument] +=== `lyra::command::add_argument` + +[source] +---- +template +command & command::add_argument(P const & p); +template +command & command::operator|=(P const & p); +---- + +Adds the given argument parser to the considered arguments for this `command`. +The argument is added to the sub-group argument instead of this one. Hence it +has the effect of adding arguments *after* the command name. + +end::reference[] */ +template +command & command::add_argument(P const & p) +{ + this->get(1).add_argument(p); + return *this; +} +template +command & command::operator|=(P const & p) +{ + return this->add_argument(p); +} + +/* tag::reference[] +[#lyra_command_brief_help] +=== `lyra::command::brief_help` + +[source] +---- +command & command::brief_help(bool brief = true); +---- + +Enables, or disables with `false`, brief output of the top level help. Brief +output only prints out the command name and description for the top level +help (i.e. `std::cout << cli`). You can output the full command, options, and +arguments by printing the command (i.e. `std::cout << command`). + +end::reference[] */ +inline command & command::brief_help(bool brief) +{ + expanded_help_details = !brief; + return *this; +} + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/bound.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/bound.hpp new file mode 100644 index 00000000..6509a87c --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/bound.hpp @@ -0,0 +1,199 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_BOUND_HPP +#define LYRA_DETAIL_BOUND_HPP + +#include "lyra/detail/from_string.hpp" +#include "lyra/detail/invoke_lambda.hpp" +#include "lyra/detail/parse.hpp" +#include "lyra/detail/unary_lambda_traits.hpp" +#include "lyra/parser_result.hpp" + +#include +#include +#include +#include +#include + +namespace lyra { namespace detail { + +struct NonCopyable +{ + NonCopyable() = default; + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable & operator=(NonCopyable const &) = delete; + NonCopyable & operator=(NonCopyable &&) = delete; +}; + +struct BoundRef : NonCopyable +{ + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + + virtual size_t get_value_count() const { return 0; } + virtual std::string get_value(size_t) const { return ""; } +}; + +struct BoundValueRefBase : BoundRef +{ + virtual auto setValue(std::string const & arg) -> parser_result = 0; +}; + +struct BoundFlagRefBase : BoundRef +{ + virtual auto setFlag(bool flag) -> parser_result = 0; + virtual auto isFlag() const -> bool { return true; } +}; + +template +struct BoundValueRef : BoundValueRefBase +{ + T & m_ref; + + explicit BoundValueRef(T & ref) + : m_ref(ref) + {} + + auto setValue(std::string const & arg) -> parser_result override + { + return parse_string(arg, m_ref); + } + + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override + { + if (i == 0) + { + std::string text; + detail::to_string(m_ref, text); + return text; + } + return ""; + } +}; + +template +struct BoundValueRef> : BoundValueRefBase +{ + std::vector & m_ref; + + explicit BoundValueRef(std::vector & ref) + : m_ref(ref) + {} + + auto isContainer() const -> bool override { return true; } + + auto setValue(std::string const & arg) -> parser_result override + { + T temp; + auto str_result = parse_string(arg, temp); + if (str_result) m_ref.push_back(temp); + return str_result; + } + + size_t get_value_count() const override { return m_ref.size(); } + std::string get_value(size_t i) const override + { + if (i < m_ref.size()) + { + std::string str_result; + detail::to_string(m_ref[i], str_result); + return str_result; + } + return ""; + } +}; + +struct BoundFlagRef : BoundFlagRefBase +{ + bool & m_ref; + + explicit BoundFlagRef(bool & ref) + : m_ref(ref) + {} + + auto setFlag(bool flag) -> parser_result override + { + m_ref = flag; + return parser_result::ok(parser_result_type::matched); + } + + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override + { + if (i == 0) return m_ref ? "true" : "false"; + return ""; + } +}; + +template +struct BoundLambda : BoundValueRefBase +{ + L m_lambda; + + static_assert(unary_lambda_traits::isValid, + "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const & lambda) + : m_lambda(lambda) + {} + + auto setValue(std::string const & arg) -> parser_result override + { + return invokeLambda::ArgType>( + m_lambda, arg); + } +}; + +template +struct BoundFlagLambda : BoundFlagRefBase +{ + L m_lambda; + + static_assert(unary_lambda_traits::isValid, + "Supplied lambda must take exactly one argument"); + static_assert( + std::is_same::ArgType, bool>::value, + "flags must be boolean"); + + explicit BoundFlagLambda(L const & lambda) + : m_lambda(lambda) + {} + + auto setFlag(bool flag) -> parser_result override + { + return LambdaInvoker< + typename unary_lambda_traits::ReturnType>::invoke(m_lambda, + flag); + } +}; + +template +struct BoundVal : BoundValueRef +{ + T value; + + BoundVal(T && v) + : BoundValueRef(value) + , value(v) + {} + + BoundVal(BoundVal && other) noexcept + : BoundValueRef(value) + , value(std::move(other.value)) + {} + + std::shared_ptr move_to_shared() + { + return std::shared_ptr(new BoundVal(std::move(*this))); + } +}; + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/choices.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/choices.hpp new file mode 100644 index 00000000..6bc28ce2 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/choices.hpp @@ -0,0 +1,143 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_CHOICES_HPP +#define LYRA_DETAIL_CHOICES_HPP + +#include "lyra/detail/from_string.hpp" +#include "lyra/detail/unary_lambda_traits.hpp" +#include "lyra/parser_result.hpp" +#include +#include +#include +#include + +namespace lyra { namespace detail { + +/* +Type erased base for set of choices. I.e. it's an "interface". +*/ +struct choices_base +{ + virtual ~choices_base() = default; + virtual parser_result contains_value(std::string const & val) const = 0; +}; + +/* +Stores a set of choice values and provides checking if a given parsed +string value is one of the choices. +*/ +template +struct choices_set : choices_base +{ + // The allowed values. + std::vector values; + + template + explicit choices_set(Vals... vals) + : choices_set({ vals... }) + {} + + explicit choices_set(const std::vector & vals) + : values(vals) + {} + + // Checks if the given string val exists in the set of + // values. Returns a parsing error if the value is not present. + parser_result contains_value(std::string const & val) const override + { + T value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + for (const T & allowed_value : values) + { + if (allowed_value == value) + return parser_result::ok(parser_result_type::matched); + } + // We consider not finding a choice a parse error. + return parser_result::error(parser_result_type::no_match, + "Value '" + val + + "' not expected. Allowed values are: " + this->to_string()); + } + + // Returns a comma separated list of the allowed values. + std::string to_string() const + { + std::string text; + for (const T & val : values) + { + if (!text.empty()) text += ", "; + std::string val_string; + if (detail::to_string(val, val_string)) + text += val_string; + else + text += ""; + } + return text; + } + + protected: + explicit choices_set(std::initializer_list const & vals) + : values(vals) + {} +}; + +template <> +struct choices_set : choices_set +{ + template + explicit choices_set(Vals... vals) + : choices_set(vals...) + {} +}; + +/* +Calls a designated function to check if the choice is valid. +*/ +template +struct choices_check : choices_base +{ + static_assert(unary_lambda_traits::isValid, + "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ReturnType>::value, + "Supplied lambda must return bool"); + + Lambda checker; + using value_type = typename unary_lambda_traits::ArgType; + + explicit choices_check(Lambda const & checker_function) + : checker(checker_function) + {} + + // Checks if the given string val exists in the set of + // values. Returns a parsing error if the value is not present. + parser_result contains_value(std::string const & val) const override + { + value_type value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + if (checker(value)) + { + return parser_result::ok(parser_result_type::matched); + } + // We consider not finding a choice a parse error. + return parser_result::error( + parser_result_type::no_match, "Value '" + val + "' not expected."); + } +}; + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/deprecated_parser_customization.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/deprecated_parser_customization.hpp new file mode 100644 index 00000000..928a6b6c --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/deprecated_parser_customization.hpp @@ -0,0 +1,64 @@ +// Copyright René Ferdinand Rivera Morell +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP +#define LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP + +#include + +namespace lyra { + +/* tag::reference[] + +[#lyra_parser_customization] += `lyra::parser_customization` + +Customization interface for parsing of options. + +[source] +---- +virtual std::string token_delimiters() const = 0; +---- + +Specifies the characters to use for splitting a cli argument into the option +and its value (if any). + +[source] +---- +virtual std::string option_prefix() const = 0; +---- + +Specifies the characters to use as possible prefix, either single or double, +for all options. + +end::reference[] */ +struct parser_customization +{ + virtual std::string token_delimiters() const = 0; + virtual std::string option_prefix() const = 0; + virtual ~parser_customization() {} +}; + +/* tag::reference[] + +[#lyra_default_parser_customization] += `lyra::default_parser_customization` + +Is-a `lyra::parser_customization` that defines token delimiters as space (" ") +or equal (`=`). And specifies the option prefix character as dash (`-`) +resulting in long options with `--` and short options with `-`. + +This customization is used as the default if none is given. + +end::reference[] */ +struct default_parser_customization : parser_customization +{ + std::string token_delimiters() const override { return " ="; } + std::string option_prefix() const override { return "-"; } +}; + +} // namespace lyra + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/from_string.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/from_string.hpp new file mode 100644 index 00000000..0f07c30b --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/from_string.hpp @@ -0,0 +1,200 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_FROM_STRING_HPP +#define LYRA_DETAIL_FROM_STRING_HPP + +#include "lyra/detail/trait_utils.hpp" + +#include +#include +#include +#include + +#ifndef LYRA_CONFIG_OPTIONAL_TYPE +# if defined(__has_include) && __has_include() +# include +# elif defined(__has_include) && __has_include() +# include +# endif +# if defined(__has_include) && __has_include() \ + && defined(__cpp_lib_optional) && (__cpp_lib_optional >= 201606L) +# include +# define LYRA_CONFIG_OPTIONAL_TYPE std::optional +# endif +#endif + +namespace lyra { namespace detail { + +template +bool to_string(const T & source, std::string & target) +{ + std::stringstream ss; + ss << source; + ss >> target; + return !ss.fail(); +} + +inline bool to_string(const std::string & source, std::string & target) +{ + target = source; + return true; +} + +inline bool to_string(const char * source, std::string & target) +{ + target = source; + return true; +} + +inline bool to_string(bool source, std::string & target) +{ + target = source ? "true" : "false"; + return true; +} + +#ifdef LYRA_CONFIG_OPTIONAL_TYPE +template +inline bool to_string( + LYRA_CONFIG_OPTIONAL_TYPE & source, std::string & target) +{ + if (source) + return to_string(*source, target); + else + target = ""; + return true; +} +#endif // LYRA_CONFIG_OPTIONAL_TYPE + +template +struct is_convertible_from_string : std::false_type +{}; + +template +struct is_convertible_from_string::value>::type> + : std::true_type +{}; + +// Validates format of given value strings before conversion. This default +// template return true always. +template +struct validate_from_string +{ + static bool validate(const std::string &) { return true; } +}; + +// Validates that a to be parsed unsigned integer only contains number +// digits. +template +struct validate_from_string::type>::value>::type> +{ + static bool validate(const std::string & s) + { + return s.find_first_not_of("0123456789") == std::string::npos; + } +}; + +// Validates that a to be parsed signed integer only contains a sign and +// number digits. +template +struct validate_from_string::type>::value + && std::is_signed::type>::value>::type> +{ + static bool validate(const std::string & s) + { + return s.find_first_not_of("-0123456789") == std::string::npos; + } +}; + +template +inline bool from_string(S const & source, T & target) +{ + std::stringstream ss; + // Feed what we want to convert into the stream so that we can convert it + // on extraction to the target type. + ss << source; + // Check that the source string data is valid. This check depends on the + // target type. + if (!validate_from_string::validate(ss.str())) return false; + T temp {}; + ss >> temp; + if (!ss.fail() && ss.eof()) + { + target = temp; + return true; + } + return false; +} + +template +inline bool from_string(S const & source, std::basic_string & target) +{ + to_string(source, target); + return true; +} + +template +struct is_convertible_from_string::value>::type> + : std::true_type +{}; + +template +inline bool from_string(S const & source, bool & target) +{ + std::string srcLC; + to_string(source, srcLC); + for (std::string::value_type & c : srcLC) + c = static_cast(std::tolower(c)); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" + || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" + || srcLC == "off") + target = false; + else + return false; + return true; +} + +#ifdef LYRA_CONFIG_OPTIONAL_TYPE +template +struct is_convertible_from_string::value>::type> + : std::true_type +{}; + +template +inline bool from_string(S const & source, LYRA_CONFIG_OPTIONAL_TYPE & target) +{ + std::string srcLC; + to_string(source, srcLC); + for (std::string::value_type & c : srcLC) + c = static_cast(::tolower(c)); + if (srcLC == "") + { + target.reset(); + return true; + } + else + { + T temp; + auto str_result = from_string(source, temp); + if (str_result) target = std::move(temp); + return str_result; + } +} +#endif // LYRA_CONFIG_OPTIONAL_TYPE + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/invoke_lambda.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/invoke_lambda.hpp new file mode 100644 index 00000000..d90c1021 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/invoke_lambda.hpp @@ -0,0 +1,50 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_INVOKE_LAMBDA_HPP +#define LYRA_DETAIL_INVOKE_LAMBDA_HPP + +#include "lyra/detail/parse.hpp" +#include "lyra/detail/unary_lambda_traits.hpp" +#include "lyra/parser_result.hpp" + +namespace lyra { namespace detail { + +template +struct LambdaInvoker +{ + template + static parser_result invoke(L const & lambda, ArgType const & arg) + { + return lambda(arg); + } +}; + +template <> +struct LambdaInvoker +{ + template + static parser_result invoke(L const & lambda, ArgType const & arg) + { + lambda(arg); + return parser_result::ok(parser_result_type::matched); + } +}; + +template +inline parser_result invokeLambda(L const & lambda, std::string const & arg) +{ + ArgType temp {}; + auto p_result = parse_string(arg, temp); + return !p_result + ? p_result + : LambdaInvoker::ReturnType>::invoke( + lambda, temp); +} + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/parse.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/parse.hpp new file mode 100644 index 00000000..fc5e6e9e --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/parse.hpp @@ -0,0 +1,27 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_PARSE_HPP +#define LYRA_DETAIL_PARSE_HPP + +#include "lyra/detail/from_string.hpp" +#include "lyra/parser_result.hpp" + +namespace lyra { namespace detail { + +template +parser_result parse_string(S const & source, T & target) +{ + if (from_string(source, target)) + return parser_result::ok(parser_result_type::matched); + else + return parser_result::error(parser_result_type::no_match, + "Unable to convert '" + source + "' to destination type"); +} + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/print.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/print.hpp new file mode 100644 index 00000000..e17cbf62 --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/print.hpp @@ -0,0 +1,86 @@ +// Copyright René Ferdinand Rivera Morell +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_PRINT_HPP +#define LYRA_DETAIL_PRINT_HPP + +#if LYRA_DEBUG +# include +#endif + +#include + +#ifndef LYRA_DEBUG +# define LYRA_DEBUG 0 +#endif + +namespace lyra { namespace detail { + +constexpr bool is_debug = LYRA_DEBUG; + +template +std::string to_string(T && t) +{ + return std::string(std::forward(t)); +} + +using std::to_string; + +#if LYRA_DEBUG + +struct print +{ + print(const char * scope_name = nullptr) + : scope(scope_name) + { + if (is_debug) print::depth() += 1; + if (scope) debug(scope, "..."); + } + + ~print() + { + if (scope) debug("...", scope); + if (is_debug) print::depth() -= 1; + } + + template + void debug(A... arg) + { + if (is_debug) + { + std::cerr << "[DEBUG]" + << std::string((print::depth() - 1) * 2, ' '); + std::string args[] = { to_string(arg)... }; + for (auto & arg_string : args) + { + std::cerr << " " << arg_string; + } + std::cerr << "\n"; + } + } + + private: + const char * scope; + + static std::size_t & depth() + { + static std::size_t d = 0; + return d; + } +}; + +#endif + +}} // namespace lyra::detail + +#if LYRA_DEBUG +# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope +# define LYRA_PRINT_DEBUG lyra_print_scope.debug +#else +# define LYRA_PRINT_SCOPE(...) while (false) +# define LYRA_PRINT_DEBUG(...) while (false) +#endif + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/result.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/result.hpp new file mode 100644 index 00000000..0a8a418f --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/result.hpp @@ -0,0 +1,164 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_RESULT_HPP +#define LYRA_DETAIL_RESULT_HPP + +#include +#include + +namespace lyra { namespace detail { + +class result_base +{ + public: + result_base const & base() const { return *this; } + explicit operator bool() const { return is_ok(); } + bool is_ok() const { return kind_ == result_kind::ok; } + std::string message() const { return message_; } + + protected: + enum class result_kind + { + ok, + error + }; + + explicit result_base(result_kind kind, const std::string & message = "") + : kind_(kind) + , message_(message) + {} + + explicit result_base(const result_base & other) + : kind_(other.kind_) + , message_(other.message_) + {} + + virtual ~result_base() = default; + + result_base & operator=(const result_base &) = default; + + private: + result_kind kind_; + std::string message_; +}; + +template +class result_value_base : public result_base +{ + public: + using value_type = T; + + value_type const & value() const { return *value_; } + bool has_value() const { return bool(value_); } + + protected: + std::unique_ptr value_; + + explicit result_value_base( + result_kind kind, const std::string & message = "") + : result_base(kind, message) + {} + + explicit result_value_base(result_kind kind, + const value_type & val, + const std::string & message = "") + : result_base(kind, message) + { + value_.reset(new value_type(val)); + } + + explicit result_value_base(result_value_base const & other) + : result_base(other) + { + if (other.value_) value_.reset(new value_type(*other.value_)); + } + + explicit result_value_base(const result_base & other) + : result_base(other) + {} + + result_value_base & operator=(result_value_base const & other) + { + result_base::operator=(other); + if (other.value_) value_.reset(new T(*other.value_)); + return *this; + } +}; + +template <> +class result_value_base : public result_base +{ + public: + using value_type = void; + + protected: + // using result_base::result_base; + explicit result_value_base(const result_base & other) + : result_base(other) + {} + explicit result_value_base( + result_kind kind, const std::string & message = "") + : result_base(kind, message) + {} +}; + +template +class basic_result : public result_value_base +{ + public: + using value_type = typename result_value_base::value_type; + + explicit basic_result(result_base const & other) + : result_value_base(other) + {} + + // With-value results.. + + static basic_result ok(value_type const & val) + { + return basic_result(result_base::result_kind::ok, val); + } + + static basic_result error( + value_type const & val, std::string const & message) + { + return basic_result(result_base::result_kind::error, val, message); + } + + protected: + using result_value_base::result_value_base; +}; + +template <> +class basic_result : public result_value_base +{ + public: + using value_type = typename result_value_base::value_type; + + explicit basic_result(result_base const & other) + : result_value_base(other) + {} + + // Value-less results.. (only kind as void is a value-less kind) + + static basic_result ok() + { + return basic_result(result_base::result_kind::ok); + } + + static basic_result error(std::string const & message) + { + return basic_result(result_base::result_kind::error, message); + } + + protected: + using result_value_base::result_value_base; +}; + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/tokens.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/tokens.hpp new file mode 100644 index 00000000..518abe6b --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/tokens.hpp @@ -0,0 +1,302 @@ +// Copyright René Ferdinand Rivera Morell +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_TOKENS_HPP +#define LYRA_DETAIL_TOKENS_HPP + +#include "lyra/option_style.hpp" + +#include +#include +#include +#include + +namespace lyra { namespace detail { + +// Wraps a token coming from a token stream. These may not directly +// correspond to strings as a single string may encode an option + its +// argument if the : or = form is used +enum class token_type +{ + unknown, + option, + argument +}; + +template > +class basic_token_name +{ + public: + using traits_type = Traits; + using value_type = Char; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator; + using reverse_iterator = const_reverse_iterator; + using string_type = std::basic_string; + + basic_token_name() noexcept + : str { nullptr } + , len { 0 } + {} + + basic_token_name(const basic_token_name &) noexcept = default; + + basic_token_name(const_pointer s) noexcept + : str { s } + , len { traits_type::length(s) } + {} + + basic_token_name(const_pointer s, size_type count) noexcept + : str { s } + , len { count } + {} + + basic_token_name & operator=(const basic_token_name &) noexcept = default; + + void swap(basic_token_name & other) noexcept + { + auto tmp = *this; + *this = other; + other = tmp; + } + + const_iterator begin() const noexcept { return this->str; } + const_iterator end() const noexcept { return this->str + this->len; } + const_iterator cbegin() const noexcept { return this->str; } + const_iterator cend() const noexcept { return this->str + this->len; } + + size_type size() const noexcept { return this->len; } + size_type length() const noexcept { return this->len; } + bool empty() const noexcept { return this->len == 0; } + + friend string_type to_string(const basic_token_name & t) + { + return { t.str, t.len }; + } + + friend string_type operator+( + const_pointer lhs, const basic_token_name & rhs) + { + return lhs + to_string(rhs); + } + + private: + const_pointer str; + size_type len; +}; + +// using token_name = basic_token_name; +using token_name = std::string; + +struct token +{ + token_type type; + token_name name; + + token() + : type(token_type::unknown) + {} + token(const token & other) = default; + token(token_type t, const token_name & n) + : type(t) + , name(n) + {} + + explicit operator bool() const { return type != token_type::unknown; } +}; + +// Abstracts iterators into args with option arguments uniformly handled +class token_iterator +{ + public: + template + explicit token_iterator(Span const & args, const option_style & opt_style) + : style(opt_style) + , args_i(args.begin()) + , args_e(args.end()) + , args_i_sub(opt_style.short_option_size) + {} + + explicit operator bool() const noexcept { return args_i != args_e; } + + token_iterator & pop(const token & arg_or_opt) + { + if (arg_or_opt.type == token_type::option && has_short_option_prefix()) + { + // Multiple short options argument (-abc). Advance to the next + // short option possible, or the next arg entirely. + if (++args_i_sub >= args_i->size()) + { + ++args_i; + args_i_sub = style.short_option_size; + } + } + else + { + // Regular arg or long option, just advance to the next arg. + ++args_i; + args_i_sub = style.short_option_size; + } + return *this; + } + + token_iterator & pop(const token & /* opt */, const token & /* val */) + { + if (has_short_option_prefix() && args_i->size() > 2) + ++args_i; + else if (!has_value_delimiter()) + args_i += 2; + else + ++args_i; + args_i_sub = style.short_option_size; + return *this; + } + + // Current arg looks like an option, short or long. + bool has_option_prefix() const noexcept + { + return has_long_option_prefix() || has_short_option_prefix(); + } + + // Current arg looks like a short option (-o). + bool has_short_option_prefix() const noexcept + { + return (args_i != args_e) + && is_prefixed( + style.short_option_prefix, style.short_option_size, *args_i); + } + + // Current arg looks like a long option (--option). + bool has_long_option_prefix() const noexcept + { + return (args_i != args_e) + && is_prefixed( + style.long_option_prefix, style.long_option_size, *args_i); + } + + // Current arg looks like a delimited option+value (--option=x, -o=x) + bool has_value_delimiter() const noexcept + { + return (args_i != args_e) + && (args_i->find_first_of(style.value_delimiters) + != std::string::npos); + } + + // Extract the current option token. + token option() const + { + if (has_long_option_prefix()) + { + if (has_value_delimiter()) + // --option=x + return token(token_type::option, + args_i->substr( + 0, args_i->find_first_of(style.value_delimiters))); + else + // --option + return token(token_type::option, *args_i); + } + else if (has_short_option_prefix()) + { + // -o (or possibly -abco) + return { token_type::option, + prefix_value(style.short_option_prefix, style.short_option_size) + + (*args_i)[args_i_sub] }; + } + return token(); + } + + // Extracts the option value if available. This will do any needed + // lookahead through the args for the value. + token value() const + { + if (has_short_option_prefix() + && (args_i->find_first_of(style.value_delimiters) + == (style.short_option_size + 1))) + // -o=x + return token(token_type::argument, args_i->substr(3)); + else if (has_long_option_prefix() && has_value_delimiter()) + // --option=x + return token(token_type::argument, + args_i->substr( + args_i->find_first_of(style.value_delimiters) + 1)); + else if (has_long_option_prefix()) + { + if (args_i + 1 != args_e) + // --option x + return token(token_type::argument, *(args_i + 1)); + } + else if (has_short_option_prefix()) + { + if (args_i_sub + 1 < args_i->size()) + // -ox + return token( + token_type::argument, args_i->substr(args_i_sub + 1)); + else if (args_i + 1 != args_e) + // -o x + return token(token_type::argument, *(args_i + 1)); + } + return token(); + } + + token argument() const { return token(token_type::argument, *args_i); } + + static bool is_prefixed( + const std::string & prefix, std::size_t size, const std::string & s) + { + if (!prefix.empty() && size > 0 && s.size() > size) + { + for (auto c : prefix) + { + // Checks that the option looks like "[]{size}[^]". + if (s[size] != c + && s.find_last_not_of(c, size - 1) == std::string::npos) + return true; + } + } + return false; + } + + private: + const option_style & style; + std::vector::const_iterator args_i; + std::vector::const_iterator args_e; + std::string::size_type args_i_sub; + + inline bool is_opt_prefix(char c) const noexcept + { + return is_prefix_char( + style.long_option_prefix, style.long_option_size, c) + || is_prefix_char( + style.short_option_prefix, style.short_option_size, c); + } + + inline bool is_prefix_char(const std::string & prefix, + std::size_t size, + std::string::value_type c) const noexcept + { + return !prefix.empty() && size > 0 + && prefix.find(c) != std::string::npos; + } + + inline std::string prefix_value( + const std::string & prefix, std::size_t size) const + { + return std::string( + static_cast(size), prefix[0]); + } +}; + +}} // namespace lyra::detail + +#endif diff --git a/engine/tools/3rdlibs/Lyra/include/lyra/detail/trait_utils.hpp b/engine/tools/3rdlibs/Lyra/include/lyra/detail/trait_utils.hpp new file mode 100644 index 00000000..e05b9cbe --- /dev/null +++ b/engine/tools/3rdlibs/Lyra/include/lyra/detail/trait_utils.hpp @@ -0,0 +1,74 @@ +// Copyright René Ferdinand Rivera Morell +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef LYRA_DETAIL_TRAIT_UTILS_HPP +#define LYRA_DETAIL_TRAIT_UTILS_HPP + +#include +#include + +namespace lyra { namespace detail { + +// Checks that F can be called with arguments of type Args. +// Credit for the technique goes to Richard Hodges (in SO post). +template +struct is_callable +{ + template + static auto test(U * p) -> decltype((*p)(std::declval()...), + void(), + std::true_type()); + + template + static auto test(...) -> decltype(std::false_type()); + + static constexpr bool value = decltype(test(nullptr))::value; +}; + +template +struct remove_cvref +{ + using type = + typename std::remove_cv::type>::type; +}; + +// Checks that F can be called, with an unspecified set of arguments. +// +// Currently this only detects function objects, like lambdas. +// Where the operator() is not templated. +template +struct is_invocable +{ + template + static auto test( + U * p) -> decltype((&U::operator()), void(), std::true_type()); + + template + static auto test(...) -> decltype(std::false_type()); + + static constexpr bool value + = decltype(test::type>(nullptr))::value; +}; + +// C++11 compatible void_t equivalent. +template +struct make_void +{ + typedef void type; +}; +template +using valid_t = typename make_void::type; + +// Borrowed from https://wg21.link/p2098r1 +template class Primary> +struct is_specialization_of : std::false_type +{}; +template